Skip to content

Commit 6c2a249

Browse files
joshlfjswrenn
andauthored
Add FromBytes::read_from_io and IntoBytes::write_to_io (#2016) (#2046)
Makes progress on #158. gherrit-pr-id: I9253d6be7407d8d8679ee355e4e71cd9b15b9ff7 Co-authored-by: Jack Wrenn <jswrenn@amazon.com>
1 parent d3157cc commit 6c2a249

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::{self, BecauseExclusive};
363366

364367
#[cfg(any(feature = "alloc", test))]
@@ -3068,10 +3071,7 @@ pub unsafe trait FromZeros: TryFromBytes {
30683071
// "exposed" provenance, and thus Rust may have to assume that this
30693072
// may consume provenance from any pointer whose provenance has been
30703073
// exposed.
3071-
#[allow(fuzzy_provenance_casts)]
3072-
unsafe {
3073-
NonNull::new_unchecked(dangling)
3074-
}
3074+
unsafe { NonNull::new_unchecked(dangling) }
30753075
};
30763076

30773077
let ptr = Self::raw_from_ptr_len(ptr, count);
@@ -4495,6 +4495,48 @@ pub unsafe trait FromBytes: FromZeros {
44954495
Err(CastError::Validity(i)) => match i {},
44964496
}
44974497
}
4498+
4499+
/// Reads a copy of `self` from an `io::Read`.
4500+
///
4501+
/// This is useful for interfacing with operating system byte sinks (files,
4502+
/// sockets, etc.).
4503+
///
4504+
/// # Examples
4505+
///
4506+
/// ```no_run
4507+
/// use zerocopy::{byteorder::big_endian::*, FromBytes};
4508+
/// use std::fs::File;
4509+
/// # use zerocopy_derive::*;
4510+
///
4511+
/// #[derive(FromBytes)]
4512+
/// #[repr(C)]
4513+
/// struct BitmapFileHeader {
4514+
/// signature: [u8; 2],
4515+
/// size: U32,
4516+
/// reserved: U64,
4517+
/// offset: U64,
4518+
/// }
4519+
///
4520+
/// let mut file = File::open("image.bin").unwrap();
4521+
/// let header = BitmapFileHeader::read_from_io(&mut file).unwrap();
4522+
/// ```
4523+
#[cfg(feature = "std")]
4524+
#[inline(always)]
4525+
fn read_from_io<R>(mut src: R) -> io::Result<Self>
4526+
where
4527+
Self: Sized,
4528+
R: io::Read,
4529+
{
4530+
let mut buf = MaybeUninit::<Self>::zeroed();
4531+
let ptr = Ptr::from_mut(&mut buf);
4532+
// SAFETY: `buf` consists entirely of initialized, zeroed bytes.
4533+
let ptr = unsafe { ptr.assume_validity::<invariant::Initialized>() };
4534+
let ptr = ptr.as_bytes::<BecauseExclusive>();
4535+
src.read_exact(ptr.as_mut())?;
4536+
// SAFETY: `buf` entirely consists of initialized bytes, and `Self` is
4537+
// `FromBytes`.
4538+
Ok(unsafe { buf.assume_init() })
4539+
}
44984540
}
44994541

45004542
/// Interprets the given affix of the given bytes as a `&Self`.
@@ -5081,6 +5123,55 @@ pub unsafe trait IntoBytes {
50815123
}
50825124
Ok(())
50835125
}
5126+
5127+
/// Writes a copy of `self` to an `io::Write`.
5128+
///
5129+
/// This is a shorthand for `dst.write_all(self.as_bytes())`, and is useful
5130+
/// for interfacing with operating system byte sinks (files, sockets, etc.).
5131+
///
5132+
/// # Examples
5133+
///
5134+
/// ```no_run
5135+
/// use zerocopy::{byteorder::big_endian::U16, FromBytes, IntoBytes};
5136+
/// use std::fs::File;
5137+
/// # use zerocopy_derive::*;
5138+
///
5139+
/// #[derive(FromBytes, IntoBytes, Immutable, KnownLayout)]
5140+
/// #[repr(C, packed)]
5141+
/// struct GrayscaleImage {
5142+
/// height: U16,
5143+
/// width: U16,
5144+
/// pixels: [U16],
5145+
/// }
5146+
///
5147+
/// let image = GrayscaleImage::ref_from_bytes(&[0, 0, 0, 0][..]).unwrap();
5148+
/// let mut file = File::create("image.bin").unwrap();
5149+
/// image.write_to_io(&mut file).unwrap();
5150+
/// ```
5151+
///
5152+
/// If the write fails, `write_to_io` returns `Err` and a partial write may
5153+
/// have occured; e.g.:
5154+
///
5155+
/// ```
5156+
/// # use zerocopy::IntoBytes;
5157+
///
5158+
/// let src = u128::MAX;
5159+
/// let mut dst = [0u8; 2];
5160+
///
5161+
/// let write_result = src.write_to_io(&mut dst[..]);
5162+
///
5163+
/// assert!(write_result.is_err());
5164+
/// assert_eq!(dst, [255, 255]);
5165+
/// ```
5166+
#[cfg(feature = "std")]
5167+
#[inline(always)]
5168+
fn write_to_io<W>(&self, mut dst: W) -> io::Result<()>
5169+
where
5170+
Self: Immutable,
5171+
W: io::Write,
5172+
{
5173+
dst.write_all(self.as_bytes())
5174+
}
50845175
}
50855176

50865177
/// Analyzes whether a type is [`Unaligned`].
@@ -5795,6 +5886,20 @@ mod tests {
57955886
assert_eq!(bytes, want);
57965887
}
57975888

5889+
#[test]
5890+
#[cfg(feature = "std")]
5891+
fn test_read_write_io() {
5892+
let mut long_buffer = [0, 0, 0, 0];
5893+
assert!(matches!(u16::MAX.write_to_io(&mut long_buffer[..]), Ok(())));
5894+
assert_eq!(long_buffer, [255, 255, 0, 0]);
5895+
assert!(matches!(u16::read_from_io(&long_buffer[..]), Ok(u16::MAX)));
5896+
5897+
let mut short_buffer = [0, 0];
5898+
assert!(u32::MAX.write_to_io(&mut short_buffer[..]).is_err());
5899+
assert_eq!(short_buffer, [255, 255]);
5900+
assert!(u32::read_from_io(&short_buffer[..]).is_err());
5901+
}
5902+
57985903
#[test]
57995904
fn test_try_from_bytes_try_read_from() {
58005905
assert_eq!(<bool as TryFromBytes>::try_read_from_bytes(&[0]), Ok(false));

0 commit comments

Comments
 (0)