Skip to content

Commit

Permalink
Split ByteSlice and SplitByteSlice
Browse files Browse the repository at this point in the history
TODO:
- Consider making these traits un-sealed
- Implement ByteSlice and ByteSliceMut for Vec<u8>

Closes #1
  • Loading branch information
joshlf committed Mar 1, 2024
1 parent 6ab3c5a commit 8502377
Showing 1 changed file with 96 additions and 42 deletions.
138 changes: 96 additions & 42 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4785,7 +4785,13 @@ where
// INVARIANTS: `try_cast_into_no_leftover` validates size and alignment.
Some(Ref(bytes, PhantomData))
}
}

impl<B, T> Ref<B, T>
where
B: SplitByteSlice,
T: KnownLayout + NoCell + ?Sized,
{
fn bikeshed_new_from_prefix_known_layout(bytes: B) -> Option<(Ref<B, T>, B)> {
let (_, split_at) = Ptr::from_ref(bytes.deref()).try_cast_into::<T>(CastType::Prefix)?;
let (bytes, suffix) = bytes.split_at(split_at);
Expand Down Expand Up @@ -4816,7 +4822,12 @@ where
// INVARIANTS: We just validated size and alignment.
Some(Ref(bytes, PhantomData))
}
}

impl<B, T> Ref<B, T>
where
B: SplitByteSlice,
{
fn new_sized_from_prefix(bytes: B) -> Option<(Ref<B, T>, B)> {
if bytes.len() < mem::size_of::<T>() || !util::aligned_to::<_, T>(bytes.deref()) {
return None;
Expand Down Expand Up @@ -4859,7 +4870,13 @@ where
// INVARIANTS: `try_cast_into_no_leftover` validates size and alignment.
Some(Ref(bytes, PhantomData))
}
}

impl<B, T> Ref<B, T>
where
B: SplitByteSlice,
T: KnownLayout + NoCell + ?Sized,
{
/// Constructs a new `Ref` from the prefix of a byte slice.
///
/// `new_from_prefix` verifies that `bytes.len() >= size_of::<T>()` and that
Expand Down Expand Up @@ -4907,7 +4924,13 @@ where
pub fn new_slice(bytes: B) -> Option<Ref<B, [T]>> {
Self::new(bytes)
}
}

impl<B, T> Ref<B, [T]>
where
B: SplitByteSlice,
T: NoCell,
{
/// 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>() *
Expand Down Expand Up @@ -5003,7 +5026,13 @@ where
pub fn new_zeroed(bytes: B) -> Option<Ref<B, T>> {
map_zeroed(Self::new(bytes))
}
}

impl<B, T> Ref<B, T>
where
B: SplitByteSliceMut,
T: KnownLayout + NoCell + ?Sized,
{
/// Constructs a new `Ref` from the prefix of a byte slice, zeroing the
/// prefix.
///
Expand Down Expand Up @@ -5063,7 +5092,13 @@ where
pub fn new_slice_zeroed(bytes: B) -> Option<Ref<B, [T]>> {
map_zeroed(Self::new(bytes))
}
}

impl<B, T> Ref<B, [T]>
where
B: SplitByteSliceMut,
T: NoCell,
{
/// Constructs a new `Ref` of a slice type from the prefix of a byte slice,
/// after zeroing the bytes.
///
Expand Down Expand Up @@ -5122,7 +5157,13 @@ where
pub fn new_unaligned(bytes: B) -> Option<Ref<B, T>> {
Ref::new(bytes)
}
}

impl<B, T> Ref<B, T>
where
B: SplitByteSlice,
T: Unaligned + KnownLayout + NoCell + ?Sized,
{
/// Constructs a new `Ref` from the prefix of a byte slice for a type with
/// no alignment requirement.
///
Expand Down Expand Up @@ -5166,7 +5207,13 @@ where
pub fn new_slice_unaligned(bytes: B) -> Option<Ref<B, [T]>> {
Ref::new(bytes)
}
}

impl<B, T> Ref<B, [T]>
where
B: SplitByteSlice,
T: Unaligned + NoCell,
{
/// Constructs a new `Ref` of a slice type with no alignment requirement
/// from the prefix of a byte slice.
///
Expand Down Expand Up @@ -5221,7 +5268,13 @@ where
pub fn new_unaligned_zeroed(bytes: B) -> Option<Ref<B, T>> {
map_zeroed(Self::new_unaligned(bytes))
}
}

impl<B, T> Ref<B, T>
where
B: SplitByteSliceMut,
T: Unaligned + KnownLayout + NoCell + ?Sized,
{
/// Constructs a new `Ref` from the prefix of a byte slice for a type with
/// no alignment requirement, zeroing the prefix.
///
Expand Down Expand Up @@ -5278,7 +5331,13 @@ where
pub fn new_slice_unaligned_zeroed(bytes: B) -> Option<Ref<B, [T]>> {
map_zeroed(Self::new_slice_unaligned(bytes))
}
}

impl<B, T> Ref<B, [T]>
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.
///
Expand Down Expand Up @@ -5561,16 +5620,6 @@ mod sealed {
pub trait ByteSliceSealed {}
}

// ByteSlice and ByteSliceMut abstract over [u8] references (&[u8], &mut [u8],
// Ref<[u8]>, RefMut<[u8]>, etc). We rely on various behaviors of these
// references such as that a given reference will never changes its length
// between calls to deref() or deref_mut(), and that split_at() works as
// expected. If ByteSlice or ByteSliceMut were not sealed, consumers could
// implement them in a way that violated these behaviors, and would break our
// unsafe code. Thus, we seal them and implement it only for known-good
// reference types. For the same reason, they're unsafe traits.

#[allow(clippy::missing_safety_doc)] // TODO(fxbug.dev/99068)
/// A mutable or immutable reference to a byte slice.
///
/// `ByteSlice` abstracts over the mutability of a byte slice reference, and is
Expand All @@ -5586,16 +5635,13 @@ mod sealed {
///
/// # Safety
///
/// If `Self: Clone` or `Self: Copy`, these implementations must be "stable" in
/// the sense that, given `bytes: Self`, if `bytes.deref()` produces a byte
/// slice of a given address and length, any copy or clone of `bytes`,
/// `bytes_new`, must also produce a byte slice of the same address and length
/// from `bytes_new.deref()`.
///
// It may seem overkill to go to this length to ensure that this doc link never
// breaks. We do this because it simplifies CI - it means that generating docs
// always succeeds, so we don't need special logic to only generate docs under
// certain features.
/// Implementations of `ByteSlice` must promise that their implementations of
/// [`Deref`] and [`DerefMut`] are "stable". In particular, given `B: ByteSlice`
/// and `b: B`, `b` must always dereference to a byte slice with the same
/// address and length. This is true for both `b.deref()` and `b.deref_mut()`.
/// If `B: Copy` or `B: Clone`, then the same is also true of copies or clones
/// of `b`. For example, `b.deref_mut()` must return a byte slice with the same
/// address and length as `b.clone().deref()`.
#[cfg_attr(feature = "alloc", doc = "[`Vec<u8>`]: alloc::vec::Vec")]
#[cfg_attr(
not(feature = "alloc"),
Expand All @@ -5604,12 +5650,22 @@ mod sealed {
pub unsafe trait ByteSlice:
Deref<Target = [u8]> + Sized + self::sealed::ByteSliceSealed
{
/// Gets a raw pointer to the first byte in the slice.
#[inline]
fn as_ptr(&self) -> *const u8 {
<[u8]>::as_ptr(self)
}
}

#[allow(clippy::missing_safety_doc)] // TODO(fxbug.dev/99068)
/// A mutable reference to a byte slice.
///
/// `ByteSliceMut` abstracts over various ways of storing a mutable reference to
/// a byte slice, and is implemented for various special reference types such as
/// `RefMut<[u8]>`.
pub unsafe trait ByteSliceMut: ByteSlice + DerefMut {}

/// TODO
///
/// # Safety
///
/// TODO
pub unsafe trait SplitByteSlice: ByteSlice {
/// Splits the slice at the midpoint.
///
/// `x.split_at(mid)` returns `x[..mid]` and `x[mid..]`.
Expand All @@ -5620,19 +5676,9 @@ pub unsafe trait ByteSlice:
fn split_at(self, mid: usize) -> (Self, Self);
}

#[allow(clippy::missing_safety_doc)] // TODO(fxbug.dev/99068)
/// A mutable reference to a byte slice.
///
/// `ByteSliceMut` abstracts over various ways of storing a mutable reference to
/// a byte slice, and is implemented for various special reference types such as
/// `RefMut<[u8]>`.
pub unsafe trait ByteSliceMut: ByteSlice + DerefMut {
/// Gets a mutable raw pointer to the first byte in the slice.
#[inline]
fn as_mut_ptr(&mut self) -> *mut u8 {
<[u8]>::as_mut_ptr(self)
}
}
/// A shorthand for [`SplitByteSlice`] and [`ByteSliceMut`].
pub trait SplitByteSliceMut: SplitByteSlice + ByteSliceMut {}
impl<B: SplitByteSlice + ByteSliceMut> SplitByteSliceMut for B {}

/// A [`ByteSlice`] that conveys no ownership, and so can be converted into a
/// byte slice.
Expand Down Expand Up @@ -5661,7 +5707,9 @@ pub trait IntoByteSliceMut<'a>: ByteSliceMut + Into<&'a mut [u8]> {}
impl<'a> sealed::ByteSliceSealed for &'a [u8] {}
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
#[allow(clippy::undocumented_unsafe_blocks)]
unsafe impl<'a> ByteSlice for &'a [u8] {
unsafe impl<'a> ByteSlice for &'a [u8] {}

unsafe impl<'a> SplitByteSlice for &'a [u8] {
#[inline]
fn split_at(self, mid: usize) -> (Self, Self) {
<[u8]>::split_at(self, mid)
Expand All @@ -5673,7 +5721,9 @@ impl<'a> IntoByteSlice<'a> for &'a [u8] {}
impl<'a> sealed::ByteSliceSealed for &'a mut [u8] {}
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
#[allow(clippy::undocumented_unsafe_blocks)]
unsafe impl<'a> ByteSlice for &'a mut [u8] {
unsafe impl<'a> ByteSlice for &'a mut [u8] {}

unsafe impl<'a> SplitByteSlice for &'a mut [u8] {
#[inline]
fn split_at(self, mid: usize) -> (Self, Self) {
<[u8]>::split_at_mut(self, mid)
Expand All @@ -5685,7 +5735,9 @@ impl<'a> IntoByteSliceMut<'a> for &'a mut [u8] {}
impl<'a> sealed::ByteSliceSealed for cell::Ref<'a, [u8]> {}
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
#[allow(clippy::undocumented_unsafe_blocks)]
unsafe impl<'a> ByteSlice for cell::Ref<'a, [u8]> {
unsafe impl<'a> ByteSlice for cell::Ref<'a, [u8]> {}

unsafe impl<'a> SplitByteSlice for cell::Ref<'a, [u8]> {
#[inline]
fn split_at(self, mid: usize) -> (Self, Self) {
cell::Ref::map_split(self, |slice| <[u8]>::split_at(slice, mid))
Expand All @@ -5695,7 +5747,9 @@ unsafe impl<'a> ByteSlice for cell::Ref<'a, [u8]> {
impl<'a> sealed::ByteSliceSealed for RefMut<'a, [u8]> {}
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
#[allow(clippy::undocumented_unsafe_blocks)]
unsafe impl<'a> ByteSlice for RefMut<'a, [u8]> {
unsafe impl<'a> ByteSlice for RefMut<'a, [u8]> {}

unsafe impl<'a> SplitByteSlice for RefMut<'a, [u8]> {
#[inline]
fn split_at(self, mid: usize) -> (Self, Self) {
RefMut::map_split(self, |slice| <[u8]>::split_at_mut(slice, mid))
Expand Down

0 comments on commit 8502377

Please sign in to comment.