diff --git a/src/lib.rs b/src/lib.rs index c9008621c3..3749507c96 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -359,6 +359,9 @@ use core::{ slice, }; +#[cfg(feature = "std")] +use std::io; + use crate::pointer::{invariant, BecauseExclusive}; #[cfg(any(feature = "alloc", test))] @@ -4525,6 +4528,48 @@ pub unsafe trait FromBytes: FromZeros { } } + /// Reads a copy of `self` from the IO byte source `src`. + /// + /// This is useful for interfacing with operating system byte sinks (files, + /// sockets, etc.). + /// + /// # Examples + /// + /// ```no_run + /// use zerocopy::{byteorder::big_endian::*, FromBytes}; + /// use std::fs::File; + /// # use zerocopy_derive::*; + /// + /// #[derive(FromBytes)] + /// #[repr(C)] + /// struct BitmapFileHeader { + /// signature: [u8; 2], + /// size: U32, + /// reserved: U64, + /// offset: U64, + /// } + /// + /// let mut file = File::open("image.bin").unwrap(); + /// let header = BitmapFileHeader::read_from_io(&mut file).unwrap(); + /// ``` + #[cfg(feature = "std")] + #[inline(always)] + fn read_from_io(mut src: R) -> io::Result + where + Self: Sized, + R: io::Read, + { + let mut buf = MaybeUninit::::zeroed(); + let ptr = Ptr::from_mut(&mut buf); + // SAFETY: `buf` consists entirely of initialized, zeroed bytes. + let ptr = unsafe { ptr.assume_validity::() }; + let ptr = ptr.as_bytes::(); + src.read_exact(ptr.as_mut())?; + // SAFETY: `buf` entirely consists of initialized bytes, and `Self` is + // `FromBytes`. + Ok(unsafe { buf.assume_init() }) + } + #[deprecated(since = "0.8.0", note = "renamed to `FromBytes::ref_from_bytes`")] #[doc(hidden)] #[must_use = "has no side effects"] @@ -5188,6 +5233,55 @@ pub unsafe trait IntoBytes { Ok(()) } + /// Writes a copy of `self` to the IO byte sink `dst`. + /// + /// This is a shorthand for `dst.write_all(self.as_bytes())`, and is useful + /// for interfacing with operating system byte sinks (files, sockets, etc.). + /// + /// # Examples + /// + /// ```no_run + /// use zerocopy::{byteorder::big_endian::U16, FromBytes, IntoBytes}; + /// use std::fs::File; + /// # use zerocopy_derive::*; + /// + /// #[derive(FromBytes, IntoBytes, Immutable, KnownLayout)] + /// #[repr(C, packed)] + /// struct GrayscaleImage { + /// height: U16, + /// width: U16, + /// pixels: [U16], + /// } + /// + /// let image = GrayscaleImage::ref_from_bytes(&[0, 0, 0, 0][..]).unwrap(); + /// let mut file = File::create("image.bin").unwrap(); + /// image.write_to_io(&mut file).unwrap(); + /// ``` + /// + /// If the write fails, `write_to_io` returns `Err` and a partial write may + /// have occured; e.g.: + /// + /// ``` + /// # use zerocopy::IntoBytes; + /// + /// let src = u128::MAX; + /// let mut dst = [0u8, 0]; + /// + /// let write_result = src.write_to_io(dst.as_mut_slice()); + /// + /// assert!(write_result.is_err()); + /// assert_eq!(dst, [255, 255]); + /// ``` + #[cfg(feature = "std")] + #[inline(always)] + fn write_to_io(&self, mut dst: W) -> io::Result<()> + where + Self: Immutable, + W: io::Write, + { + dst.write_all(self.as_bytes()) + } + #[deprecated(since = "0.8.0", note = "`IntoBytes::as_bytes_mut` was renamed to `as_mut_bytes`")] #[doc(hidden)] #[inline] @@ -5950,6 +6044,20 @@ mod tests { assert_eq!(bytes, want); } + #[test] + #[cfg(feature = "std")] + fn test_read_write_io() { + let mut long_buffer = [0, 0, 0, 0]; + assert!(matches!(u16::MAX.write_to_io(long_buffer.as_mut_slice()), Ok(()))); + assert_eq!(long_buffer, [255, 255, 0, 0]); + assert!(matches!(u16::read_from_io(long_buffer.as_slice()), Ok(u16::MAX))); + + let mut short_buffer = [0, 0]; + assert!(u32::MAX.write_to_io(short_buffer.as_mut_slice()).is_err()); + assert_eq!(short_buffer, [255, 255]); + assert!(u32::read_from_io(short_buffer.as_slice()).is_err()); + } + #[test] fn test_try_from_bytes_try_read_from() { assert_eq!(::try_read_from_bytes(&[0]), Ok(false));