Skip to content

Commit af48300

Browse files
joshlfjswrenn
authored andcommitted
Add TryFromBytes::try_read_from_io
Makes progress on #158 gherrit-pr-id: Ia15467145b06dd57e4dfd9e127fdd6d7a313ecba
1 parent da442b4 commit af48300

File tree

1 file changed

+78
-5
lines changed

1 file changed

+78
-5
lines changed

src/lib.rs

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2696,7 +2696,7 @@ pub unsafe trait TryFromBytes {
26962696
};
26972697
// SAFETY: `candidate` was copied from from `source: &[u8]`, so all of
26982698
// its bytes are initialized.
2699-
unsafe { try_read_from(source, candidate) }
2699+
unsafe { try_read_from(source, candidate) }.map_err(TryReadError::Validity)
27002700
}
27012701

27022702
/// Attempts to read a `Self` from the prefix of the given `source`.
@@ -2757,7 +2757,10 @@ pub unsafe trait TryFromBytes {
27572757
};
27582758
// SAFETY: `candidate` was copied from from `source: &[u8]`, so all of
27592759
// its bytes are initialized.
2760-
unsafe { try_read_from(source, candidate).map(|slf| (slf, suffix)) }
2760+
match unsafe { try_read_from(source, candidate) } {
2761+
Ok(slf) => Ok((slf, suffix)),
2762+
Err(err) => Err(TryReadError::Validity(err)),
2763+
}
27612764
}
27622765

27632766
/// Attempts to read a `Self` from the suffix of the given `source`.
@@ -2819,7 +2822,77 @@ pub unsafe trait TryFromBytes {
28192822
};
28202823
// SAFETY: `candidate` was copied from from `source: &[u8]`, so all of
28212824
// its bytes are initialized.
2822-
unsafe { try_read_from(source, candidate).map(|slf| (prefix, slf)) }
2825+
match unsafe { try_read_from(source, candidate) } {
2826+
Ok(slf) => Ok((prefix, slf)),
2827+
Err(err) => Err(TryReadError::Validity(err)),
2828+
}
2829+
}
2830+
2831+
/// Attempts to read a copy of `self` from an `io::Read`.
2832+
///
2833+
/// This is useful for interfacing with operating system byte sinks (files,
2834+
/// sockets, etc.).
2835+
///
2836+
/// # Examples
2837+
///
2838+
/// ```no_run
2839+
/// use zerocopy::{byteorder::big_endian::*, TryFromBytes, Unalign};
2840+
/// use std::fs::File;
2841+
/// # use zerocopy_derive::*;
2842+
///
2843+
/// #[derive(TryFromBytes)]
2844+
/// #[repr(C)]
2845+
/// struct BitmapFileHeader {
2846+
/// signature: Unalign<Signature>,
2847+
/// size: U32,
2848+
/// reserved: U64,
2849+
/// offset: U64,
2850+
/// }
2851+
///
2852+
/// #[derive(TryFromBytes)]
2853+
/// #[repr(u16)]
2854+
/// enum Signature {
2855+
/// BM = u16::from_ne_bytes(*b"BM")
2856+
/// }
2857+
///
2858+
/// let mut file = File::open("image.bin").unwrap();
2859+
/// let header = BitmapFileHeader::try_read_from_io(&mut file)
2860+
/// .expect("read failed")
2861+
/// .expect("invalid header");
2862+
/// ```
2863+
// TODO: Write tests for this method
2864+
#[cfg(feature = "std")]
2865+
#[inline(always)]
2866+
fn try_read_from_io<R>(mut src: R) -> io::Result<Result<Self, ValidityError<&'static (), Self>>>
2867+
where
2868+
Self: Sized,
2869+
R: io::Read,
2870+
{
2871+
// NOTE(#2319, #2320): We do `buf.zero()` separately rather than
2872+
// constructing `let buf = CoreMaybeUninit::zeroed()` because, if `Self`
2873+
// contains padding bytes, then a typed copy of `CoreMaybeUninit<Self>`
2874+
// will not necessarily preserve zeros written to those padding byte
2875+
// locations, and so `buf` could contain uninitialized bytes.
2876+
let mut buf = CoreMaybeUninit::<Self>::uninit();
2877+
buf.zero();
2878+
2879+
let ptr = Ptr::from_mut(&mut buf);
2880+
// SAFETY: After `buf.zero()`, `buf` consists entirely of initialized,
2881+
// zeroed bytes. Since `MaybeUninit` has no validity requirements, `ptr`
2882+
// cannot be used to write values which will violate `buf`'s bit
2883+
// validity. Since `ptr` has `Exclusive` aliasing, nothing other than
2884+
// `ptr` may be used to mutate `ptr`'s referent, and so its bit validity
2885+
// cannot be violated even though `buf` may have more permissive bit
2886+
// validity than `ptr`.
2887+
let ptr = unsafe { ptr.assume_validity::<invariant::Initialized>() };
2888+
let ptr = ptr.as_bytes::<BecauseExclusive>();
2889+
src.read_exact(ptr.as_mut())?;
2890+
2891+
// SAFETY: `buf` entirely consists of initialized bytes.
2892+
Ok(match unsafe { try_read_from((), buf) } {
2893+
Ok(slf) => Ok(slf),
2894+
Err(err) => Err(err.map_src(|_| [])),
2895+
})
28232896
}
28242897
}
28252898

@@ -2885,7 +2958,7 @@ fn swap<T, U>((t, u): (T, U)) -> (U, T) {
28852958
unsafe fn try_read_from<S, T: TryFromBytes>(
28862959
source: S,
28872960
mut candidate: CoreMaybeUninit<T>,
2888-
) -> Result<T, TryReadError<S, T>> {
2961+
) -> Result<T, ValidityError<S, T>> {
28892962
// We use `from_mut` despite not mutating via `c_ptr` so that we don't need
28902963
// to add a `T: Immutable` bound.
28912964
let c_ptr = Ptr::from_mut(&mut candidate);
@@ -2911,7 +2984,7 @@ unsafe fn try_read_from<S, T: TryFromBytes>(
29112984
// `Self: !Immutable`. Since `Self: Immutable`, this panic condition will
29122985
// not happen.
29132986
if !Wrapping::<T>::is_bit_valid(c_ptr.forget_aligned()) {
2914-
return Err(ValidityError::new(source).into());
2987+
return Err(ValidityError::new(source));
29152988
}
29162989

29172990
fn _assert_same_size_and_validity<T>()

0 commit comments

Comments
 (0)