Skip to content

Commit e3b2ec1

Browse files
jswrennjoshlf
andauthored
Add FromBytes::read_from_io and IntoBytes::write_to_io (#2016)
Makes progress on #158. Co-authored-by: Joshua Liebow-Feeser <joshlf@users.noreply.github.com>
1 parent af9179e commit e3b2ec1

File tree

1 file changed

+109
-4
lines changed

1 file changed

+109
-4
lines changed

src/lib.rs

Lines changed: 109 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,9 @@ use core::{
359359
slice,
360360
};
361361

362+
#[cfg(feature = "std")]
363+
use std::io;
364+
362365
use crate::pointer::{invariant, BecauseExclusive};
363366

364367
#[cfg(any(feature = "alloc", test))]
@@ -3083,10 +3086,7 @@ pub unsafe trait FromZeros: TryFromBytes {
30833086
// "exposed" provenance, and thus Rust may have to assume that this
30843087
// may consume provenance from any pointer whose provenance has been
30853088
// exposed.
3086-
#[allow(fuzzy_provenance_casts)]
3087-
unsafe {
3088-
NonNull::new_unchecked(dangling)
3089-
}
3089+
unsafe { NonNull::new_unchecked(dangling) }
30903090
};
30913091

30923092
let ptr = Self::raw_from_ptr_len(ptr, count);
@@ -4526,6 +4526,48 @@ pub unsafe trait FromBytes: FromZeros {
45264526
}
45274527
}
45284528

4529+
/// Reads a copy of `self` from an `io::Read`.
4530+
///
4531+
/// This is useful for interfacing with operating system byte sinks (files,
4532+
/// sockets, etc.).
4533+
///
4534+
/// # Examples
4535+
///
4536+
/// ```no_run
4537+
/// use zerocopy::{byteorder::big_endian::*, FromBytes};
4538+
/// use std::fs::File;
4539+
/// # use zerocopy_derive::*;
4540+
///
4541+
/// #[derive(FromBytes)]
4542+
/// #[repr(C)]
4543+
/// struct BitmapFileHeader {
4544+
/// signature: [u8; 2],
4545+
/// size: U32,
4546+
/// reserved: U64,
4547+
/// offset: U64,
4548+
/// }
4549+
///
4550+
/// let mut file = File::open("image.bin").unwrap();
4551+
/// let header = BitmapFileHeader::read_from_io(&mut file).unwrap();
4552+
/// ```
4553+
#[cfg(feature = "std")]
4554+
#[inline(always)]
4555+
fn read_from_io<R>(mut src: R) -> io::Result<Self>
4556+
where
4557+
Self: Sized,
4558+
R: io::Read,
4559+
{
4560+
let mut buf = MaybeUninit::<Self>::zeroed();
4561+
let ptr = Ptr::from_mut(&mut buf);
4562+
// SAFETY: `buf` consists entirely of initialized, zeroed bytes.
4563+
let ptr = unsafe { ptr.assume_validity::<invariant::Initialized>() };
4564+
let ptr = ptr.as_bytes::<BecauseExclusive>();
4565+
src.read_exact(ptr.as_mut())?;
4566+
// SAFETY: `buf` entirely consists of initialized bytes, and `Self` is
4567+
// `FromBytes`.
4568+
Ok(unsafe { buf.assume_init() })
4569+
}
4570+
45294571
#[deprecated(since = "0.8.0", note = "renamed to `FromBytes::ref_from_bytes`")]
45304572
#[doc(hidden)]
45314573
#[must_use = "has no side effects"]
@@ -5189,6 +5231,55 @@ pub unsafe trait IntoBytes {
51895231
Ok(())
51905232
}
51915233

5234+
/// Writes a copy of `self` to an `io::Write`.
5235+
///
5236+
/// This is a shorthand for `dst.write_all(self.as_bytes())`, and is useful
5237+
/// for interfacing with operating system byte sinks (files, sockets, etc.).
5238+
///
5239+
/// # Examples
5240+
///
5241+
/// ```no_run
5242+
/// use zerocopy::{byteorder::big_endian::U16, FromBytes, IntoBytes};
5243+
/// use std::fs::File;
5244+
/// # use zerocopy_derive::*;
5245+
///
5246+
/// #[derive(FromBytes, IntoBytes, Immutable, KnownLayout)]
5247+
/// #[repr(C, packed)]
5248+
/// struct GrayscaleImage {
5249+
/// height: U16,
5250+
/// width: U16,
5251+
/// pixels: [U16],
5252+
/// }
5253+
///
5254+
/// let image = GrayscaleImage::ref_from_bytes(&[0, 0, 0, 0][..]).unwrap();
5255+
/// let mut file = File::create("image.bin").unwrap();
5256+
/// image.write_to_io(&mut file).unwrap();
5257+
/// ```
5258+
///
5259+
/// If the write fails, `write_to_io` returns `Err` and a partial write may
5260+
/// have occured; e.g.:
5261+
///
5262+
/// ```
5263+
/// # use zerocopy::IntoBytes;
5264+
///
5265+
/// let src = u128::MAX;
5266+
/// let mut dst = [0u8; 2];
5267+
///
5268+
/// let write_result = src.write_to_io(&mut dst[..]);
5269+
///
5270+
/// assert!(write_result.is_err());
5271+
/// assert_eq!(dst, [255, 255]);
5272+
/// ```
5273+
#[cfg(feature = "std")]
5274+
#[inline(always)]
5275+
fn write_to_io<W>(&self, mut dst: W) -> io::Result<()>
5276+
where
5277+
Self: Immutable,
5278+
W: io::Write,
5279+
{
5280+
dst.write_all(self.as_bytes())
5281+
}
5282+
51925283
#[deprecated(since = "0.8.0", note = "`IntoBytes::as_bytes_mut` was renamed to `as_mut_bytes`")]
51935284
#[doc(hidden)]
51945285
#[inline]
@@ -5951,6 +6042,20 @@ mod tests {
59516042
assert_eq!(bytes, want);
59526043
}
59536044

6045+
#[test]
6046+
#[cfg(feature = "std")]
6047+
fn test_read_write_io() {
6048+
let mut long_buffer = [0, 0, 0, 0];
6049+
assert!(matches!(u16::MAX.write_to_io(&mut long_buffer[..]), Ok(())));
6050+
assert_eq!(long_buffer, [255, 255, 0, 0]);
6051+
assert!(matches!(u16::read_from_io(&long_buffer[..]), Ok(u16::MAX)));
6052+
6053+
let mut short_buffer = [0, 0];
6054+
assert!(u32::MAX.write_to_io(&mut short_buffer[..]).is_err());
6055+
assert_eq!(short_buffer, [255, 255]);
6056+
assert!(u32::read_from_io(&short_buffer[..]).is_err());
6057+
}
6058+
59546059
#[test]
59556060
fn test_try_from_bytes_try_read_from() {
59566061
assert_eq!(<bool as TryFromBytes>::try_read_from_bytes(&[0]), Ok(false));

0 commit comments

Comments
 (0)