Skip to content

Commit

Permalink
Add non-slice Ref constructors w/ slice elem count
Browse files Browse the repository at this point in the history
Add `Ref` constructors which are generic over `T:
KnownLayout<PointerMetadata = usize>` - 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
  • Loading branch information
joshlf committed Apr 23, 2024
1 parent fe63e38 commit a6e01bd
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 121 deletions.
50 changes: 50 additions & 0 deletions src/deprecated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,53 @@ where
self.into_mut()
}
}

impl<B, T> Ref<B, [T]>
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, [T]>, 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<B, [T]>)> {
Ref::with_trailing_elements_from_suffix(bytes, count)
}
}

impl<B, T> Ref<B, [T]>
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, [T]>, 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<B, [T]>)> {
Ref::with_trailing_elements_unaligned_from_suffix(bytes, count)
}
}
241 changes: 120 additions & 121 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1065,8 +1065,9 @@ pub unsafe trait KnownLayout {
where
Self: Sized;

/// `()` for sized types and `usize` for slice DSTs.
#[doc(hidden)]
/// The type of metadata stored in a pointer to `Self`.
///
/// This is `()` for sized types and `usize` for slice DSTs.
type PointerMetadata: PointerMetadata;

#[doc(hidden)]
Expand Down Expand Up @@ -2527,11 +2528,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<PointerMetadata = usize> + 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
Expand Down Expand Up @@ -2578,63 +2593,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<PointerMetadata = usize> + 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::<T>() != 0` or `bytes` is not aligned to
/// `align_of::<T>()`, 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
Expand Down Expand Up @@ -2686,11 +2674,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<PointerMetadata = usize> + 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
Expand Down Expand Up @@ -2742,11 +2747,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<PointerMetadata = usize> + 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`.
Expand Down Expand Up @@ -5082,58 +5104,44 @@ where
}
}

impl<B, T> Ref<B, [T]>
impl<B, T> Ref<B, T>
where
B: SplitByteSlice,
T: NoCell,
T: KnownLayout<PointerMetadata = usize> + 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::<T>() *
/// count` and that `bytes` is aligned to `align_of::<T>()`. It consumes the
/// first `size_of::<T>() * count` bytes from `bytes` to construct a `Ref`,
/// and returns the remaining bytes to the caller. It also ensures that
/// `sizeof::<T>() * 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, [T]>, B)> {
let expected_len = match mem::size_of::<T>().checked_mul(count) {
pub fn with_trailing_elements_from_prefix(bytes: B, count: usize) -> Option<(Ref<B, T>, 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::<T>() *
/// count` and that `bytes` is aligned to `align_of::<T>()`. It consumes the
/// last `size_of::<T>() * count` bytes from `bytes` to construct a `Ref`,
/// and returns the preceding bytes to the caller. It also ensures that
/// `sizeof::<T>() * 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<B, T> Ref<B, T>
where
B: SplitByteSlice,
T: KnownLayout<PointerMetadata = usize> + 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<B, [T]>)> {
let expected_len = match mem::size_of::<T>().checked_mul(count) {
pub fn with_trailing_elements_from_suffix(bytes: B, count: usize) -> Option<(B, Ref<B, T>)> {
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))
}
}
Expand Down Expand Up @@ -5186,46 +5194,37 @@ where
}
}

impl<B, T> Ref<B, [T]>
impl<B, T> Ref<B, T>
where
B: SplitByteSlice,
T: Unaligned + NoCell,
T: KnownLayout<PointerMetadata = usize> + 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::<T>() *
/// count`. It consumes the first `size_of::<T>() * count` bytes from
/// `bytes` to construct a `Ref`, and returns the remaining bytes to the
/// caller. It also ensures that `sizeof::<T>() * 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, [T]>, 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, T>, 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::<T>() *
/// count`. It consumes the last `size_of::<T>() * count` bytes from `bytes`
/// to construct a `Ref`, and returns the remaining bytes to the caller. It
/// also ensures that `sizeof::<T>() * 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<B, [T]>)> {
Ref::new_slice_from_suffix(bytes, count)
impl<B, T> Ref<B, T>
where
B: SplitByteSlice,
T: KnownLayout<PointerMetadata = usize> + 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<B, T>)> {
Self::with_trailing_elements_from_suffix(bytes, count)
}
}

Expand Down

0 comments on commit a6e01bd

Please sign in to comment.