diff --git a/src/lib.rs b/src/lib.rs index a54d9c42f2..595e3bd9e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2696,7 +2696,7 @@ pub unsafe trait TryFromBytes { }; // SAFETY: `candidate` was copied from from `source: &[u8]`, so all of // its bytes are initialized. - unsafe { try_read_from(source, candidate) } + unsafe { try_read_from(source, candidate) }.map_err(TryReadError::Validity) } /// Attempts to read a `Self` from the prefix of the given `source`. @@ -2757,7 +2757,10 @@ pub unsafe trait TryFromBytes { }; // SAFETY: `candidate` was copied from from `source: &[u8]`, so all of // its bytes are initialized. - unsafe { try_read_from(source, candidate).map(|slf| (slf, suffix)) } + match unsafe { try_read_from(source, candidate) } { + Ok(slf) => Ok((slf, suffix)), + Err(err) => Err(TryReadError::Validity(err)), + } } /// Attempts to read a `Self` from the suffix of the given `source`. @@ -2819,7 +2822,77 @@ pub unsafe trait TryFromBytes { }; // SAFETY: `candidate` was copied from from `source: &[u8]`, so all of // its bytes are initialized. - unsafe { try_read_from(source, candidate).map(|slf| (prefix, slf)) } + match unsafe { try_read_from(source, candidate) } { + Ok(slf) => Ok((prefix, slf)), + Err(err) => Err(TryReadError::Validity(err)), + } + } + + /// Attempts to read a copy of `self` from an `io::Read`. + /// + /// This is useful for interfacing with operating system byte sinks (files, + /// sockets, etc.). + /// + /// # Examples + /// + /// ```no_run + /// use zerocopy::{byteorder::big_endian::*, TryFromBytes, Unalign}; + /// use std::fs::File; + /// # use zerocopy_derive::*; + /// + /// #[derive(TryFromBytes)] + /// #[repr(C)] + /// struct BitmapFileHeader { + /// signature: Unalign, + /// size: U32, + /// reserved: U64, + /// offset: U64, + /// } + /// + /// #[derive(TryFromBytes)] + /// #[repr(u16)] + /// enum Signature { + /// BM = u16::from_ne_bytes(*b"BM") + /// } + /// + /// let mut file = File::open("image.bin").unwrap(); + /// let header = BitmapFileHeader::try_read_from_io(&mut file) + /// .expect("read failed") + /// .expect("invalid header"); + /// ``` + // TODO: Write tests for this method + #[cfg(feature = "std")] + #[inline(always)] + fn try_read_from_io(mut src: R) -> io::Result>> + where + Self: Sized, + R: io::Read, + { + // NOTE(#2319, #2320): We do `buf.zero()` separately rather than + // constructing `let buf = CoreMaybeUninit::zeroed()` because, if `Self` + // contains padding bytes, then a typed copy of `CoreMaybeUninit` + // will not necessarily preserve zeros written to those padding byte + // locations, and so `buf` could contain uninitialized bytes. + let mut buf = CoreMaybeUninit::::uninit(); + buf.zero(); + + let ptr = Ptr::from_mut(&mut buf); + // SAFETY: After `buf.zero()`, `buf` consists entirely of initialized, + // zeroed bytes. Since `MaybeUninit` has no validity requirements, `ptr` + // cannot be used to write values which will violate `buf`'s bit + // validity. Since `ptr` has `Exclusive` aliasing, nothing other than + // `ptr` may be used to mutate `ptr`'s referent, and so its bit validity + // cannot be violated even though `buf` may have more permissive bit + // validity than `ptr`. + let ptr = unsafe { ptr.assume_validity::() }; + let ptr = ptr.as_bytes::(); + src.read_exact(ptr.as_mut())?; + + // SAFETY: `buf` entirely consists of initialized bytes. + Ok(match unsafe { try_read_from((), buf) } { + Ok(slf) => Ok(slf), + Err(err) => Err(err.map_src(|_| &())), + }) } } @@ -2885,7 +2958,7 @@ fn swap((t, u): (T, U)) -> (U, T) { unsafe fn try_read_from( source: S, mut candidate: CoreMaybeUninit, -) -> Result> { +) -> Result> { // We use `from_mut` despite not mutating via `c_ptr` so that we don't need // to add a `T: Immutable` bound. let c_ptr = Ptr::from_mut(&mut candidate); @@ -2911,7 +2984,7 @@ unsafe fn try_read_from( // `Self: !Immutable`. Since `Self: Immutable`, this panic condition will // not happen. if !Wrapping::::is_bit_valid(c_ptr.forget_aligned()) { - return Err(ValidityError::new(source).into()); + return Err(ValidityError::new(source)); } fn _assert_same_size_and_validity()