From 17038c60a2783850b812e44ac3a36455759b9a3d Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Fri, 1 Mar 2024 17:43:38 -0800 Subject: [PATCH] Add non-slice Ref constructors w/ slice elem count Add `Ref` constructors which are generic over `T: KnownLayout` - in other words, types whose trailing field is a slice (i.e., slices or slice DSTs). These constructors take an explicit element count for the trailing slice, and replace the previous constructors which only supported slices. Makes progress on #29 --- src/deprecated.rs | 78 ++++++++++ src/lib.rs | 359 +++++++++++++++++----------------------------- 2 files changed, 213 insertions(+), 224 deletions(-) diff --git a/src/deprecated.rs b/src/deprecated.rs index c94d04905c..cc19b85456 100644 --- a/src/deprecated.rs +++ b/src/deprecated.rs @@ -31,6 +31,7 @@ where T: NoCell, { #[deprecated(since = "0.8.0", note = "`Ref::new_zeroed` now supports slices")] + #[must_use] #[doc(hidden)] #[inline(always)] pub fn new_slice_zeroed(bytes: B) -> Option> { @@ -89,3 +90,80 @@ where self.into_mut() } } + +impl Ref +where + B: SplitByteSlice, + T: NoCell, +{ + #[deprecated(since = "0.8.0", note = "replaced by `Ref::with_trailing_elements_from_prefix`")] + #[must_use = "has no side effects"] + #[doc(hidden)] + #[inline] + pub fn new_slice_from_prefix(bytes: B, count: usize) -> Option<(Ref, B)> { + Ref::with_trailing_elements_from_prefix(bytes, count) + } + + #[deprecated(since = "0.8.0", note = "replaced by `Ref::with_trailing_elements_from_suffix`")] + #[must_use = "has no side effects"] + #[doc(hidden)] + #[inline] + pub fn new_slice_from_suffix(bytes: B, count: usize) -> Option<(B, Ref)> { + Ref::with_trailing_elements_from_suffix(bytes, count) + } +} + +impl Ref +where + B: SplitByteSliceMut, + T: NoCell, +{ + #[deprecated( + since = "0.8.0", + note = "replaced by `Ref::with_trailing_elements_from_prefix_zeroed`" + )] + #[doc(hidden)] + #[inline(always)] + pub fn new_slice_from_prefix_zeroed(bytes: B, count: usize) -> Option<(Ref, B)> { + Self::with_trailing_elements_from_prefix_zeroed(bytes, count) + } + + #[deprecated( + since = "0.8.0", + note = "replaced by `Ref::with_trailing_elements_from_suffix_zeroed`" + )] + #[must_use] + #[doc(hidden)] + #[inline(always)] + pub fn new_slice_from_suffix_zeroed(bytes: B, count: usize) -> Option<(B, Ref)> { + Self::with_trailing_elements_from_suffix_zeroed(bytes, count) + } +} + +impl Ref +where + B: SplitByteSlice, + T: Unaligned + NoCell, +{ + #[deprecated( + since = "0.8.0", + note = "replaced by `Ref::with_trailing_elements_unaligned_from_prefix`" + )] + #[doc(hidden)] + #[must_use = "has no side effects"] + #[inline(always)] + pub fn new_slice_unaligned_from_prefix(bytes: B, count: usize) -> Option<(Ref, B)> { + Ref::with_trailing_elements_unaligned_from_prefix(bytes, count) + } + + #[deprecated( + since = "0.8.0", + note = "replaced by `Ref::with_trailing_elements_unaligned_from_suffix`" + )] + #[doc(hidden)] + #[must_use = "has no side effects"] + #[inline(always)] + pub fn new_slice_unaligned_from_suffix(bytes: B, count: usize) -> Option<(B, Ref)> { + Ref::with_trailing_elements_unaligned_from_suffix(bytes, count) + } +} diff --git a/src/lib.rs b/src/lib.rs index 51bb430689..9742e43e44 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1107,7 +1107,7 @@ impl PointerMetadata for () { #[inline] fn size_for_metadata(&self, layout: DstLayout) -> Option { match layout.size_info { - SizeInfo::Sized { _size } => Some(_size), + SizeInfo::Sized { size } => Some(size), SizeInfo::SliceDst(_) => unreachable!(), } } @@ -1122,9 +1122,9 @@ impl PointerMetadata for usize { #[inline] fn size_for_metadata(&self, layout: DstLayout) -> Option { match layout.size_info { - SizeInfo::SliceDst(TrailingSliceLayout { _offset, _elem_size }) => { - let slice_len = _elem_size.checked_mul(*self)?; - let without_padding = _offset.checked_add(slice_len)?; + SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }) => { + let slice_len = elem_size.checked_mul(*self)?; + let without_padding = offset.checked_add(slice_len)?; without_padding.checked_add(util::padding_needed_for(without_padding, layout.align)) } SizeInfo::Sized { .. } => unreachable!(), @@ -2527,11 +2527,25 @@ pub unsafe trait FromBytes: FromZeros { /// ``` #[must_use = "has no side effects"] #[inline] + fn from_prefix_with_trailing_elements(bytes: &[u8], count: usize) -> Option<(&Self, &[u8])> + where + Self: KnownLayout + NoCell, + { + Ref::<_, Self>::with_trailing_elements_from_prefix(bytes, count) + .map(|(r, b)| (r.into_ref(), b)) + } + + #[deprecated( + since = "0.8.0", + note = "renamed to `FromBytes::from_prefix_with_trailing_elements`" + )] + #[doc(hidden)] + #[inline] fn slice_from_prefix(bytes: &[u8], count: usize) -> Option<(&[Self], &[u8])> where Self: Sized + NoCell, { - Ref::<_, [Self]>::new_slice_from_prefix(bytes, count).map(|(r, b)| (r.into_ref(), b)) + <[Self]>::from_prefix_with_trailing_elements(bytes, count) } /// Interprets the suffix of the given `bytes` as a `&[Self]` with length @@ -2578,63 +2592,36 @@ pub unsafe trait FromBytes: FromZeros { /// ``` #[must_use = "has no side effects"] #[inline] + fn from_suffix_with_trailing_elements(bytes: &[u8], count: usize) -> Option<(&[u8], &Self)> + where + Self: KnownLayout + NoCell, + { + Ref::<_, Self>::with_trailing_elements_from_suffix(bytes, count) + .map(|(b, r)| (b, r.into_ref())) + } + + #[deprecated( + since = "0.8.0", + note = "renamed to `FromBytes::from_prefix_with_trailing_elements`" + )] + #[doc(hidden)] + #[inline] fn slice_from_suffix(bytes: &[u8], count: usize) -> Option<(&[u8], &[Self])> where Self: Sized + NoCell, { - Ref::<_, [Self]>::new_slice_from_suffix(bytes, count).map(|(b, r)| (b, r.into_ref())) + <[Self]>::from_suffix_with_trailing_elements(bytes, count) } - /// Interprets the given `bytes` as a `&mut [Self]` without copying. - /// - /// If `bytes.len() % size_of::() != 0` or `bytes` is not aligned to - /// `align_of::()`, this returns `None`. - /// - /// If you need to convert a specific number of slice elements, see - /// [`mut_slice_from_prefix`](FromBytes::mut_slice_from_prefix) or - /// [`mut_slice_from_suffix`](FromBytes::mut_slice_from_suffix). - /// - /// # Panics - /// - /// If `T` is a zero-sized type. - /// - /// # Examples - /// - /// ``` - /// use zerocopy::FromBytes; - /// # use zerocopy_derive::*; - /// - /// # #[derive(Debug, PartialEq, Eq)] - /// #[derive(FromBytes, IntoBytes, NoCell)] - /// #[repr(C)] - /// struct Pixel { - /// r: u8, - /// g: u8, - /// b: u8, - /// a: u8, - /// } - /// - /// // These bytes encode two `Pixel`s. - /// let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7][..]; - /// - /// let pixels = Pixel::mut_slice_from(bytes).unwrap(); - /// - /// assert_eq!(pixels, &[ - /// Pixel { r: 0, g: 1, b: 2, a: 3 }, - /// Pixel { r: 4, g: 5, b: 6, a: 7 }, - /// ]); - /// - /// pixels[1] = Pixel { r: 0, g: 0, b: 0, a: 0 }; - /// - /// assert_eq!(bytes, [0, 1, 2, 3, 0, 0, 0, 0]); - /// ``` + #[deprecated(since = "0.8.0", note = "`FromBytes::mut_from` now supports slices")] #[must_use = "has no side effects"] + #[doc(hidden)] #[inline] fn mut_slice_from(bytes: &mut [u8]) -> Option<&mut [Self]> where Self: Sized + IntoBytes + NoCell, { - Ref::<_, [Self]>::new(bytes).map(|r| r.into_mut()) + <[Self]>::mut_from(bytes) } /// Interprets the prefix of the given `bytes` as a `&mut [Self]` with @@ -2686,11 +2673,28 @@ pub unsafe trait FromBytes: FromZeros { /// ``` #[must_use = "has no side effects"] #[inline] - fn mut_slice_from_prefix(bytes: &mut [u8], count: usize) -> Option<(&mut [Self], &mut [u8])> + fn mut_from_prefix_with_trailing_elements( + bytes: &mut [u8], + count: usize, + ) -> Option<(&mut Self, &mut [u8])> where - Self: Sized + IntoBytes + NoCell, + Self: IntoBytes + KnownLayout + NoCell, + { + Ref::<_, Self>::with_trailing_elements_from_prefix(bytes, count) + .map(|(r, b)| (r.into_mut(), b)) + } + + #[deprecated( + since = "0.8.0", + note = "renamed to `FromBytes::mut_from_prefix_with_trailing_elements`" + )] + #[doc(hidden)] + #[inline] + fn mut_slice_from_prefix(bytes: &[u8], count: usize) -> Option<(&[Self], &[u8])> + where + Self: Sized + NoCell, { - Ref::<_, [Self]>::new_slice_from_prefix(bytes, count).map(|(r, b)| (r.into_mut(), b)) + <[Self]>::from_prefix_with_trailing_elements(bytes, count) } /// Interprets the suffix of the given `bytes` as a `&mut [Self]` with length @@ -2742,11 +2746,28 @@ pub unsafe trait FromBytes: FromZeros { /// ``` #[must_use = "has no side effects"] #[inline] + fn mut_from_suffix_with_trailing_elements( + bytes: &mut [u8], + count: usize, + ) -> Option<(&mut [u8], &mut Self)> + where + Self: IntoBytes + KnownLayout + NoCell, + { + Ref::<_, Self>::with_trailing_elements_from_suffix(bytes, count) + .map(|(b, r)| (b, r.into_mut())) + } + + #[deprecated( + since = "0.8.0", + note = "renamed to `FromBytes::mut_from_suffix_with_trailing_elements`" + )] + #[doc(hidden)] + #[inline] fn mut_slice_from_suffix(bytes: &mut [u8], count: usize) -> Option<(&mut [u8], &mut [Self])> where Self: Sized + IntoBytes + NoCell, { - Ref::<_, [Self]>::new_slice_from_suffix(bytes, count).map(|(b, r)| (b, r.into_mut())) + <[Self]>::mut_from_suffix_with_trailing_elements(bytes, count) } /// Reads a copy of `Self` from `bytes`. @@ -5082,58 +5103,44 @@ where } } -impl Ref +impl Ref where B: SplitByteSlice, - T: NoCell, + T: KnownLayout + NoCell + ?Sized, { - /// Constructs a new `Ref` of a slice type from the prefix of a byte slice. - /// - /// `new_slice_from_prefix` verifies that `bytes.len() >= size_of::() * - /// count` and that `bytes` is aligned to `align_of::()`. It consumes the - /// first `size_of::() * count` bytes from `bytes` to construct a `Ref`, - /// and returns the remaining bytes to the caller. It also ensures that - /// `sizeof::() * count` does not overflow a `usize`. If any of the - /// length, alignment, or overflow checks fail, it returns `None`. - /// - /// # Panics - /// - /// `new_slice_from_prefix` panics if `T` is a zero-sized type. - #[must_use = "has no side effects"] + // TODO(#29), TODO(#871): Pick a name and make this public. Make sure to + // update references to this name in `#[deprecated]` attributes elsewhere. + #[doc(hidden)] #[inline] - pub fn new_slice_from_prefix(bytes: B, count: usize) -> Option<(Ref, B)> { - let expected_len = match mem::size_of::().checked_mul(count) { + pub fn with_trailing_elements_from_prefix(bytes: B, count: usize) -> Option<(Ref, B)> { + let expected_len = match count.size_for_metadata(T::LAYOUT) { Some(len) => len, None => return None, }; if bytes.len() < expected_len { return None; } - let (prefix, bytes) = try_split_at(bytes, expected_len)?; + let (prefix, bytes) = bytes.split_at(expected_len); Self::new(prefix).map(move |l| (l, bytes)) } +} - /// Constructs a new `Ref` of a slice type from the suffix of a byte slice. - /// - /// `new_slice_from_suffix` verifies that `bytes.len() >= size_of::() * - /// count` and that `bytes` is aligned to `align_of::()`. It consumes the - /// last `size_of::() * count` bytes from `bytes` to construct a `Ref`, - /// and returns the preceding bytes to the caller. It also ensures that - /// `sizeof::() * count` does not overflow a `usize`. If any of the - /// length, alignment, or overflow checks fail, it returns `None`. - /// - /// # Panics - /// - /// `new_slice_from_suffix` panics if `T` is a zero-sized type. - #[must_use = "has no side effects"] +impl Ref +where + B: SplitByteSlice, + T: KnownLayout + NoCell + ?Sized, +{ + // TODO(#29), TODO(#871): Pick a name and make this public. Make sure to + // update references to this name in `#[deprecated]` attributes elsewhere. + #[doc(hidden)] #[inline] - pub fn new_slice_from_suffix(bytes: B, count: usize) -> Option<(B, Ref)> { - let expected_len = match mem::size_of::().checked_mul(count) { + pub fn with_trailing_elements_from_suffix(bytes: B, count: usize) -> Option<(B, Ref)> { + let expected_len = match count.size_for_metadata(T::LAYOUT) { Some(len) => len, None => return None, }; let split_at = bytes.len().checked_sub(expected_len)?; - let (bytes, suffix) = try_split_at(bytes, split_at)?; + let (bytes, suffix) = bytes.split_at(split_at); Self::new(suffix).map(move |l| (bytes, l)) } } @@ -5229,55 +5236,27 @@ where } } -impl Ref +impl Ref where B: SplitByteSliceMut, - T: NoCell, + T: KnownLayout + NoCell + ?Sized, { - /// Constructs a new `Ref` of a slice type from the prefix of a byte slice, - /// after zeroing the bytes. - /// - /// `new_slice_from_prefix` verifies that `bytes.len() >= size_of::() * - /// count` and that `bytes` is aligned to `align_of::()`. It consumes the - /// first `size_of::() * count` bytes from `bytes` to construct a `Ref`, - /// and returns the remaining bytes to the caller. It also ensures that - /// `sizeof::() * count` does not overflow a `usize`. If any of the - /// length, alignment, or overflow checks fail, it returns `None`. - /// - /// If the checks succeed, then the suffix which is consumed will be - /// initialized to zero. This can be useful when re-using buffers to ensure - /// that sensitive data previously stored in the buffer is not leaked. - /// - /// # Panics - /// - /// `new_slice_from_prefix_zeroed` panics if `T` is a zero-sized type. - #[must_use] + #[doc(hidden)] #[inline(always)] - pub fn new_slice_from_prefix_zeroed(bytes: B, count: usize) -> Option<(Ref, B)> { - map_prefix_tuple_zeroed(Self::new_slice_from_prefix(bytes, count)) + pub fn with_trailing_elements_from_prefix_zeroed( + bytes: B, + count: usize, + ) -> Option<(Ref, B)> { + map_prefix_tuple_zeroed(Self::with_trailing_elements_from_prefix(bytes, count)) } - /// Constructs a new `Ref` of a slice type from the prefix of a byte slice, - /// after zeroing the bytes. - /// - /// `new_slice_from_suffix` verifies that `bytes.len() >= size_of::() * - /// count` and that `bytes` is aligned to `align_of::()`. It consumes the - /// last `size_of::() * count` bytes from `bytes` to construct a `Ref`, - /// and returns the preceding bytes to the caller. It also ensures that - /// `sizeof::() * count` does not overflow a `usize`. If any of the - /// length, alignment, or overflow checks fail, it returns `None`. - /// - /// If the checks succeed, then the consumed suffix will be initialized to - /// zero. This can be useful when re-using buffers to ensure that sensitive - /// data previously stored in the buffer is not leaked. - /// - /// # Panics - /// - /// `new_slice_from_suffix_zeroed` panics if `T` is a zero-sized type. - #[must_use] + #[doc(hidden)] #[inline(always)] - pub fn new_slice_from_suffix_zeroed(bytes: B, count: usize) -> Option<(B, Ref)> { - map_suffix_tuple_zeroed(Self::new_slice_from_suffix(bytes, count)) + pub fn with_trailing_elements_from_suffix_zeroed( + bytes: B, + count: usize, + ) -> Option<(B, Ref)> { + map_suffix_tuple_zeroed(Self::with_trailing_elements_from_suffix(bytes, count)) } } @@ -5329,46 +5308,37 @@ where } } -impl Ref +impl Ref where B: SplitByteSlice, - T: Unaligned + NoCell, + T: KnownLayout + Unaligned + NoCell + ?Sized, { - /// Constructs a new `Ref` of a slice type with no alignment requirement - /// from the prefix of a byte slice. - /// - /// `new_slice_from_prefix` verifies that `bytes.len() >= size_of::() * - /// count`. It consumes the first `size_of::() * count` bytes from - /// `bytes` to construct a `Ref`, and returns the remaining bytes to the - /// caller. It also ensures that `sizeof::() * count` does not overflow a - /// `usize`. If either the length, or overflow checks fail, it returns - /// `None`. - /// - /// # Panics - /// - /// `new_slice_unaligned_from_prefix` panics if `T` is a zero-sized type. - #[must_use = "has no side effects"] - #[inline(always)] - pub fn new_slice_unaligned_from_prefix(bytes: B, count: usize) -> Option<(Ref, B)> { - Ref::new_slice_from_prefix(bytes, count) + // TODO(#29), TODO(#871): Pick a name and make this public. Make sure to + // update references to this name in `#[deprecated]` attributes elsewhere. + #[doc(hidden)] + #[inline] + pub fn with_trailing_elements_unaligned_from_prefix( + bytes: B, + count: usize, + ) -> Option<(Ref, B)> { + Self::with_trailing_elements_from_prefix(bytes, count) } +} - /// Constructs a new `Ref` of a slice type with no alignment requirement - /// from the suffix of a byte slice. - /// - /// `new_slice_from_suffix` verifies that `bytes.len() >= size_of::() * - /// count`. It consumes the last `size_of::() * count` bytes from `bytes` - /// to construct a `Ref`, and returns the remaining bytes to the caller. It - /// also ensures that `sizeof::() * count` does not overflow a `usize`. - /// If either the length, or overflow checks fail, it returns `None`. - /// - /// # Panics - /// - /// `new_slice_unaligned_from_suffix` panics if `T` is a zero-sized type. - #[must_use = "has no side effects"] - #[inline(always)] - pub fn new_slice_unaligned_from_suffix(bytes: B, count: usize) -> Option<(B, Ref)> { - Ref::new_slice_from_suffix(bytes, count) +impl Ref +where + B: SplitByteSlice, + T: KnownLayout + Unaligned + NoCell + ?Sized, +{ + // TODO(#29), TODO(#871): Pick a name and make this public. Make sure to + // update references to this name in `#[deprecated]` attributes elsewhere. + #[doc(hidden)] + #[inline] + pub fn with_trailing_elements_unaligned_from_suffix( + bytes: B, + count: usize, + ) -> Option<(B, Ref)> { + Self::with_trailing_elements_from_suffix(bytes, count) } } @@ -5433,65 +5403,6 @@ where } } -impl Ref -where - B: SplitByteSliceMut, - T: Unaligned + NoCell, -{ - /// Constructs a new `Ref` of a slice type with no alignment requirement - /// from the prefix of a byte slice, after zeroing the bytes. - /// - /// `new_slice_from_prefix` verifies that `bytes.len() >= size_of::() * - /// count`. It consumes the first `size_of::() * count` bytes from - /// `bytes` to construct a `Ref`, and returns the remaining bytes to the - /// caller. It also ensures that `sizeof::() * count` does not overflow a - /// `usize`. If either the length, or overflow checks fail, it returns - /// `None`. - /// - /// If the checks succeed, then the prefix will be initialized to zero. This - /// can be useful when re-using buffers to ensure that sensitive data - /// previously stored in the buffer is not leaked. - /// - /// # Panics - /// - /// `new_slice_unaligned_from_prefix_zeroed` panics if `T` is a zero-sized - /// type. - #[must_use] - #[inline(always)] - pub fn new_slice_unaligned_from_prefix_zeroed( - bytes: B, - count: usize, - ) -> Option<(Ref, B)> { - map_prefix_tuple_zeroed(Self::new_slice_unaligned_from_prefix(bytes, count)) - } - - /// Constructs a new `Ref` of a slice type with no alignment requirement - /// from the suffix of a byte slice, after zeroing the bytes. - /// - /// `new_slice_from_suffix` verifies that `bytes.len() >= size_of::() * - /// count`. It consumes the last `size_of::() * count` bytes from `bytes` - /// to construct a `Ref`, and returns the remaining bytes to the caller. It - /// also ensures that `sizeof::() * count` does not overflow a `usize`. - /// If either the length, or overflow checks fail, it returns `None`. - /// - /// If the checks succeed, then the suffix will be initialized to zero. This - /// can be useful when re-using buffers to ensure that sensitive data - /// previously stored in the buffer is not leaked. - /// - /// # Panics - /// - /// `new_slice_unaligned_from_suffix_zeroed` panics if `T` is a zero-sized - /// type. - #[must_use] - #[inline(always)] - pub fn new_slice_unaligned_from_suffix_zeroed( - bytes: B, - count: usize, - ) -> Option<(B, Ref)> { - map_suffix_tuple_zeroed(Self::new_slice_unaligned_from_suffix(bytes, count)) - } -} - impl<'a, B, T> Ref where B: 'a + IntoByteSlice<'a>,