Skip to content

Commit

Permalink
Add FromBytes::read_from_io and IntoBytes::write_to_io
Browse files Browse the repository at this point in the history
Makes progress on #158.
  • Loading branch information
jswrenn committed Nov 5, 2024
1 parent 1fb6365 commit e4949fb
Showing 1 changed file with 109 additions and 0 deletions.
109 changes: 109 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,12 +359,16 @@ use core::{
slice,
};

#[cfg(feature = "std")]
use std::io;

use crate::pointer::{invariant, BecauseExclusive};

#[cfg(any(feature = "alloc", test))]
extern crate alloc;
#[cfg(any(feature = "alloc", test))]
use alloc::{boxed::Box, vec::Vec};
use pointer::invariant::Initialized;

#[cfg(any(feature = "alloc", test, kani))]
use core::alloc::Layout;
Expand Down Expand Up @@ -4525,6 +4529,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<R>(mut src: R) -> io::Result<Self>
where
Self: Sized,
R: io::Read,
{
let mut buf = MaybeUninit::<Self>::zeroed();
let ptr = Ptr::from_mut(&mut buf);
// SAFETY: `buf` consists entirely of initialized, zeroed bytes.
let ptr = unsafe { ptr.assume_validity::<Initialized>() };
let ptr = ptr.as_bytes::<BecauseExclusive>();
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"]
Expand Down Expand Up @@ -5188,6 +5234,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<W>(&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]
Expand Down Expand Up @@ -5950,6 +6045,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!(<bool as TryFromBytes>::try_read_from_bytes(&[0]), Ok(false));
Expand Down

0 comments on commit e4949fb

Please sign in to comment.