blob: 4aeb602f69e086e386c4e8b68f828df3822d8976 [file] [edit]
// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
//
// Copyright 2025 The Fuchsia Authors
//
// Licensed under the 2-Clause BSD License <LICENSE-BSD or
// https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use super::*;
use crate::pointer::invariant::{Aligned, Exclusive, Invariants, Safe, Shared};
/// Types that can be split in two.
///
/// This trait generalizes Rust's existing support for splitting slices to
/// support slices and slice-based dynamically-sized types ("slice DSTs").
///
/// For a `repr(C, packed)` or `repr(C, packed(N))` struct, the derive wraps
/// the trailing field's [`Elem`](Self::Elem) in [`Unalign`]. The returned
/// slice can therefore be accessed even when packing misaligns the original
/// elements. Each packed struct in the trailing-field chain adds one wrapper;
/// unpacked structs and [`ManuallyDrop`] forward their trailing type's `Elem`.
///
/// # Implementation
///
/// **Do not implement this trait yourself!** Instead, use
/// [`#[derive(SplitAt)]`][derive]; e.g.:
///
/// ```
/// # use zerocopy_derive::{SplitAt, KnownLayout};
/// #[derive(SplitAt, KnownLayout)]
/// #[repr(C)]
/// struct MyStruct<T: ?Sized> {
/// # /*
/// ...,
/// # */
/// // `SplitAt` types must have at least one field.
/// field: T,
/// }
/// ```
///
/// This derive performs a sophisticated, compile-time safety analysis to
/// determine whether a type is `SplitAt`.
///
/// A packed struct exposes its trailing elements through `Unalign`:
///
/// ```
/// use zerocopy::{FromBytes, SplitAt, Unalign};
/// # use zerocopy_derive::*;
///
/// #[derive(FromBytes, IntoBytes, KnownLayout, SplitAt)]
/// #[repr(C, packed)]
/// struct Packet {
/// tag: u8,
/// words: [u16],
/// }
///
/// let mut bytes = [0u8; 5];
/// let packet = Packet::mut_from_bytes(&mut bytes[..]).unwrap();
/// let (left, right): (&mut Packet, &mut [Unalign<u16>]) =
/// packet.split_at_mut(1).unwrap().via_into_bytes();
/// left.tag = 1;
/// right[0] = Unalign::new(0x0202);
/// assert_eq!(bytes, [1, 0, 0, 2, 2]);
/// ```
///
/// # Safety
///
/// This trait does not convey any safety guarantees to code outside this crate.
///
/// You must not rely on the `#[doc(hidden)]` internals of `SplitAt`. Future
/// releases of zerocopy may make backwards-breaking changes to these items,
/// including changes that only affect soundness, which may cause code which
/// uses those items to silently become unsound.
///
#[cfg_attr(feature = "derive", doc = "[derive]: zerocopy_derive::SplitAt")]
#[cfg_attr(
not(feature = "derive"),
doc = concat!("[derive]: https://docs.rs/zerocopy/", env!("CARGO_PKG_VERSION"), "/zerocopy/derive.SplitAt.html"),
)]
#[cfg_attr(
not(no_zerocopy_diagnostic_on_unimplemented_1_78_0),
diagnostic::on_unimplemented(note = "Consider adding `#[derive(SplitAt)]` to `{Self}`")
)]
// # Safety
//
// `Self` is a slice, a `repr(C)` (possibly packed) or `repr(transparent)` slice
// DST, or `ManuallyDrop<T>` for some `T: SplitAt`.
//
// `Self::Elem` has the same size, bit validity, and `UnsafeCell` coverage as
// the actual trailing element type. Access through either representation
// preserves the other's validity, including writes through mutable references
// and interior mutation through shared references. For every aligned `Self`,
// its trailing slice's address is aligned for `Self::Elem`.
pub unsafe trait SplitAt: KnownLayout<PointerMetadata = usize> {
/// The element type exposed by the split's returned slice.
///
/// For `[T]`, this is `T`. For a struct, this is the trailing field's `Elem`,
/// wrapped in [`Unalign`] if the struct is packed. Nested packed structs
/// produce nested `Unalign` wrappers.
type Elem;
#[doc(hidden)]
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized;
/// Unsafely splits `self` in two.
///
/// # Safety
///
/// The caller promises that `l_len` is not greater than the length of
/// `self`'s trailing slice.
///
#[doc = codegen_section!(
header = "h5",
bench = "split_at_unchecked",
format = "coco",
arity = 2,
[
open
@index 1
@title "Unsized"
@variant "dynamic_size"
],
[
@index 2
@title "Dynamically Padded"
@variant "dynamic_padding"
]
)]
#[inline]
#[must_use]
unsafe fn split_at_unchecked(&self, l_len: usize) -> Split<&Self> {
// SAFETY: By precondition on the caller, `l_len <= self.len()`.
unsafe { Split::<&Self>::new(self, l_len) }
}
/// Attempts to split `self` in two.
///
/// Returns `None` if `l_len` is greater than the length of `self`'s
/// trailing slice.
///
/// # Examples
///
/// ```
/// use zerocopy::{SplitAt, FromBytes};
/// # use zerocopy_derive::*;
///
/// #[derive(SplitAt, FromBytes, KnownLayout, Immutable)]
/// #[repr(C)]
/// struct Packet {
/// length: u8,
/// body: [u8],
/// }
///
/// // These bytes encode a `Packet`.
/// let bytes = &[4, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
///
/// let packet = Packet::ref_from_bytes(bytes).unwrap();
///
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
///
/// // Attempt to split `packet` at `length`.
/// let split = packet.split_at(packet.length as usize).unwrap();
///
/// // Use the `Immutable` bound on `Packet` to prove that it's okay to
/// // return concurrent references to `packet` and `rest`.
/// let (packet, rest) = split.via_immutable();
///
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4]);
/// assert_eq!(rest, [5, 6, 7, 8, 9]);
/// ```
///
#[doc = codegen_section!(
header = "h5",
bench = "split_at",
format = "coco",
arity = 2,
[
open
@index 1
@title "Unsized"
@variant "dynamic_size"
],
[
@index 2
@title "Dynamically Padded"
@variant "dynamic_padding"
]
)]
#[inline]
#[must_use = "has no side effects"]
fn split_at(&self, l_len: usize) -> Option<Split<&Self>> {
MetadataOf::new_in_bounds(self, l_len).map(
#[inline(always)]
|l_len| {
// SAFETY: We have ensured that `l_len <= self.len()` (by
// post-condition on `MetadataOf::new_in_bounds`)
unsafe { Split::new(self, l_len.get()) }
},
)
}
/// Unsafely splits `self` in two.
///
/// # Safety
///
/// The caller promises that `l_len` is not greater than the length of
/// `self`'s trailing slice.
///
#[doc = codegen_header!("h5", "split_at_mut_unchecked")]
///
/// See [`SplitAt::split_at_unchecked`](#method.split_at_unchecked.codegen).
#[inline]
#[must_use]
unsafe fn split_at_mut_unchecked(&mut self, l_len: usize) -> Split<&mut Self> {
// SAFETY: By precondition on the caller, `l_len <= self.len()`.
unsafe { Split::<&mut Self>::new(self, l_len) }
}
/// Attempts to split `self` in two.
///
/// Returns `None` if `l_len` is greater than the length of `self`'s
/// trailing slice.
///
/// # Examples
///
/// ```
/// use zerocopy::{SplitAt, FromBytes};
/// # use zerocopy_derive::*;
///
/// #[derive(SplitAt, FromBytes, KnownLayout, IntoBytes)]
/// #[repr(C)]
/// struct Packet<B: ?Sized> {
/// length: u8,
/// body: B,
/// }
///
/// // These bytes encode a `Packet`.
/// let mut bytes = &mut [4, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
///
/// let packet = Packet::<[u8]>::mut_from_bytes(bytes).unwrap();
///
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
///
/// {
/// // Attempt to split `packet` at `length`.
/// let split = packet.split_at_mut(packet.length as usize).unwrap();
///
/// // Use the `IntoBytes` bound on `Packet` to prove that it's okay to
/// // return concurrent references to `packet` and `rest`.
/// let (packet, rest) = split.via_into_bytes();
///
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4]);
/// assert_eq!(rest, [5, 6, 7, 8, 9]);
///
/// rest.fill(0);
/// }
///
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4, 0, 0, 0, 0, 0]);
/// ```
///
#[doc = codegen_header!("h5", "split_at_mut")]
///
/// See [`SplitAt::split_at`](#method.split_at.codegen).
#[inline]
fn split_at_mut(&mut self, l_len: usize) -> Option<Split<&mut Self>> {
MetadataOf::new_in_bounds(self, l_len).map(
#[inline(always)]
|l_len| {
// SAFETY: We have ensured that `l_len <= self.len()` (by
// post-condition on `MetadataOf::new_in_bounds`)
unsafe { Split::new(self, l_len.get()) }
},
)
}
}
// SAFETY: `[T]` exposes its own element type, preserving size, validity, and
// `UnsafeCell` coverage. An aligned slice is aligned for its elements.
unsafe impl<T> SplitAt for [T] {
type Elem = T;
#[inline]
#[allow(dead_code)]
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized,
{
}
}
// SAFETY: `ManuallyDrop<T>` has the same layout and bit validity as `T` [1].
// Its `KnownLayout` implementation preserves `T`'s metadata and trailing-slice
// layout. Forwarding `T::Elem` therefore preserves its size, validity, and
// alignment guarantees. Its `UnsafeCell` coverage also matches `T`'s, as
// established for `ManuallyDrop` in `impls.rs`; accesses through the forwarded
// element representation preserve validity in both directions.
//
// [1] Per https://doc.rust-lang.org/1.93.0/std/mem/struct.ManuallyDrop.html:
//
// `ManuallyDrop<T>` is guaranteed to have the same layout and bit validity as
// `T`
unsafe impl<T: ?Sized + SplitAt> SplitAt for ManuallyDrop<T> {
type Elem = T::Elem;
#[inline]
#[allow(dead_code)]
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized,
{
}
}
/// A `T` that has been split into two possibly-overlapping parts.
///
/// For some dynamically sized types, the padding that appears after the
/// trailing slice field [is a dynamic function of the trailing slice
/// length](KnownLayout#slice-dst-layout). If `T` is split at a length that
/// requires trailing padding, the trailing padding of the left part of the
/// split `T` will overlap the right part. If `T` is a mutable reference or
/// permits interior mutation, you must ensure that the left and right parts do
/// not overlap. Use [`Self::via_into_bytes`] or
/// [`Self::via_no_dynamic_padding`] to establish this without a runtime check,
/// or [`Self::via_runtime_check`] to check a particular split. Shared references
/// to an [`Immutable`] type may overlap; use [`Self::via_immutable`] in that
/// case.
#[derive(Debug)]
pub struct Split<T> {
/// A pointer to the source slice DST.
source: T,
/// The length of the future left half of `source`.
///
/// # Safety
///
/// If `source` is a pointer to a slice DST, `l_len` is no greater than
/// `source`'s length.
l_len: usize,
}
impl<T> Split<T> {
/// Produces a `Split` of `source` with `l_len`.
///
/// # Safety
///
/// `l_len` is no greater than `source`'s length.
#[inline(always)]
unsafe fn new(source: T, l_len: usize) -> Self {
Self { source, l_len }
}
}
impl<'a, T> Split<&'a T>
where
T: ?Sized + SplitAt,
{
#[inline(always)]
fn into_ptr(self) -> Split<Ptr<'a, T, (Shared, Aligned, Safe)>> {
let source = Ptr::from_ref(self.source);
// SAFETY: `Ptr::from_ref(self.source)` points to exactly `self.source`
// and thus maintains the invariants of `self` with respect to `l_len`.
unsafe { Split::new(source, self.l_len) }
}
/// Produces the split parts of `self`, using [`Immutable`] to ensure that
/// it is sound to have concurrent references to both parts.
///
/// # Examples
///
/// ```
/// use zerocopy::{SplitAt, FromBytes};
/// # use zerocopy_derive::*;
///
/// #[derive(SplitAt, FromBytes, KnownLayout, Immutable)]
/// #[repr(C)]
/// struct Packet {
/// length: u8,
/// body: [u8],
/// }
///
/// // These bytes encode a `Packet`.
/// let bytes = &[4, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
///
/// let packet = Packet::ref_from_bytes(bytes).unwrap();
///
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
///
/// // Attempt to split `packet` at `length`.
/// let split = packet.split_at(packet.length as usize).unwrap();
///
/// // Use the `Immutable` bound on `Packet` to prove that it's okay to
/// // return concurrent references to `packet` and `rest`.
/// let (packet, rest) = split.via_immutable();
///
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4]);
/// assert_eq!(rest, [5, 6, 7, 8, 9]);
/// ```
///
#[doc = codegen_section!(
header = "h5",
bench = "split_via_immutable",
format = "coco",
arity = 2,
[
open
@index 1
@title "Unsized"
@variant "dynamic_size"
],
[
@index 2
@title "Dynamically Padded"
@variant "dynamic_padding"
]
)]
#[must_use = "has no side effects"]
#[inline(always)]
pub fn via_immutable(self) -> (&'a T, &'a [T::Elem])
where
T: Immutable,
{
let (l, r) = self.into_ptr().via_immutable();
(l.as_ref(), r.as_ref())
}
/// Produces the split parts of `self`, using [`IntoBytes`] to ensure that
/// it is sound to have concurrent references to both parts.
///
/// # Examples
///
/// ```
/// use zerocopy::{SplitAt, FromBytes};
/// # use zerocopy_derive::*;
///
/// #[derive(SplitAt, FromBytes, KnownLayout, Immutable, IntoBytes)]
/// #[repr(C)]
/// struct Packet<B: ?Sized> {
/// length: u8,
/// body: B,
/// }
///
/// // These bytes encode a `Packet`.
/// let bytes = &[4, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
///
/// let packet = Packet::<[u8]>::ref_from_bytes(bytes).unwrap();
///
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
///
/// // Attempt to split `packet` at `length`.
/// let split = packet.split_at(packet.length as usize).unwrap();
///
/// // Use the `IntoBytes` bound on `Packet` to prove that it's okay to
/// // return concurrent references to `packet` and `rest`.
/// let (packet, rest) = split.via_into_bytes();
///
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4]);
/// assert_eq!(rest, [5, 6, 7, 8, 9]);
/// ```
///
#[doc = codegen_header!("h5", "split_via_into_bytes")]
///
/// See [`Split::via_immutable`](#method.split_via_immutable.codegen).
#[must_use = "has no side effects"]
#[inline(always)]
pub fn via_into_bytes(self) -> (&'a T, &'a [T::Elem])
where
T: IntoBytes,
{
let (l, r) = self.into_ptr().via_into_bytes();
(l.as_ref(), r.as_ref())
}
/// Produces the split parts of `self` using [`Self::via_no_dynamic_padding`].
#[deprecated(note = "use `Split::via_no_dynamic_padding` instead")]
#[doc(hidden)]
#[must_use = "has no side effects"]
#[inline(always)]
pub fn via_unaligned(self) -> (&'a T, &'a [T::Elem])
where
T: Unaligned,
{
self.via_no_dynamic_padding()
}
/// Produces the split parts of `self` using a compile-time assertion to
/// ensure that it is unconditionally sound to have concurrent references to
/// both parts.
///
/// When possible, you should prefer using [`Self::via_into_bytes`], which
/// ensures this safety property through regular trait bounds.
///
/// # Examples
///
/// In the below example, a single byte of padding exists (on most
/// platforms) between `Packet<[u16]>`'s `length` and `body` fields,
/// precluding the use of [`Self::via_into_bytes`].
///
/// ```
/// use zerocopy::SplitAt;
/// # use zerocopy_derive::*;
///
/// #[derive(SplitAt, KnownLayout)]
/// #[repr(C)]
/// struct Packet<B: ?Sized> {
/// length: u8,
/// body: B,
/// }
///
/// let packet: &Packet<[u16]> = &Packet { length: 4, body: [1u16, 2, 3, 4, 5, 6, 7, 8, 9] };
///
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
///
/// if let Some(split) = packet.split_at(packet.length as usize) {
/// // Every body length requires no trailing padding.
/// let (packet, rest) = split.via_no_dynamic_padding();
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4]);
/// assert_eq!(rest, [5, 6, 7, 8, 9]);
/// } else {
/// unreachable!("The packet's length field is within its body");
/// }
/// ```
///
/// # Compile-Time Assertions
///
/// This method rejects at compile-time any type that admits dynamic
/// trailing padding. In the below example, some `split_at` indices would
/// produce left parts whose trailing padding overlaps with the right part:
///
/// ```compile_fail,E0080
/// use zerocopy::SplitAt;
/// # use zerocopy_derive::*;
///
/// #[derive(SplitAt, KnownLayout)]
/// #[repr(C, align(2))]
/// struct Packet<B: ?Sized> {
/// length: u16,
/// body: B,
/// }
///
/// let packet: &Packet<[u8]> = &Packet { length: 4, body: [1u8, 2, 3, 4, 5, 6, 7, 8, 9] };
///
/// if let Some(split) = packet.split_at(packet.length as usize) {
/// let _ = split.via_no_dynamic_padding(); // âš  Compile Error!
/// } else {
/// unreachable!("The packet's length field is within its body");
/// }
/// ```
///
/// If you need to split such types, use [`Self::via_runtime_check`].
///
#[doc = codegen_header!("h5", "split_via_no_dynamic_padding")]
///
/// See [`Split::via_immutable`](#method.split_via_immutable.codegen).
#[must_use = "has no side effects"]
#[inline(always)]
pub fn via_no_dynamic_padding(self) -> (&'a T, &'a [T::Elem]) {
let (l, r) = self.into_ptr().via_no_dynamic_padding();
(l.as_ref(), r.as_ref())
}
/// Produces the split parts of `self`, using a dynamic check to ensure that
/// it is sound to have concurrent references to both parts. You should
/// prefer using [`Self::via_immutable`], [`Self::via_into_bytes`], or
/// [`Self::via_no_dynamic_padding`], which perform no runtime check.
///
/// Note that this check is overly conservative if `T` is [`Immutable`]; for
/// some types, this check will reject some splits which
/// [`Self::via_immutable`] will accept.
///
/// # Examples
///
/// ```
/// use zerocopy::SplitAt;
/// # use zerocopy_derive::*;
///
/// #[derive(SplitAt, KnownLayout)]
/// #[repr(C, align(2))]
/// struct Packet<B: ?Sized> {
/// length: u16,
/// body: B,
/// }
///
/// let packet: &Packet<[u8]> = &Packet { length: 4, body: [1u8, 2, 3, 4, 5, 6, 7, 8, 9] };
///
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
///
/// // The two-byte prefix and four-byte body need no trailing padding.
/// if let Some(split) = packet.split_at(packet.length as usize) {
/// if let Ok((packet, rest)) = split.via_runtime_check() {
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4]);
/// assert_eq!(rest, [5, 6, 7, 8, 9]);
/// } else {
/// unreachable!("A four-byte body requires no trailing padding");
/// }
/// } else {
/// unreachable!("The packet's length field is within its body");
/// }
///
/// // A three-byte body needs a padding byte that would overlap `rest`.
/// if let Some(split) = packet.split_at((packet.length - 1) as usize) {
/// assert!(split.via_runtime_check().is_err());
/// } else {
/// unreachable!("One less than the packet's length is within its body");
/// }
/// ```
///
#[doc = codegen_section!(
header = "h5",
bench = "split_via_runtime_check",
format = "coco",
arity = 2,
[
open
@index 1
@title "Unsized"
@variant "dynamic_size"
],
[
@index 2
@title "Dynamically Padded"
@variant "dynamic_padding"
]
)]
#[must_use = "has no side effects"]
#[inline(always)]
pub fn via_runtime_check(self) -> Result<(&'a T, &'a [T::Elem]), Self> {
match self.into_ptr().via_runtime_check() {
Ok((l, r)) => Ok((l.as_ref(), r.as_ref())),
Err(s) => Err(s.into_ref()),
}
}
/// Unsafely produces the split parts of `self`.
///
/// # Safety
///
/// If `T` permits interior mutation, the trailing padding bytes of the left
/// portion must not overlap the right portion. For some dynamically sized
/// types, the padding that appears after the trailing slice field [is a
/// dynamic function of the trailing slice
/// length](KnownLayout#slice-dst-layout). Thus, for some types, this
/// condition is dependent on the length of the left portion.
///
#[doc = codegen_section!(
header = "h5",
bench = "split_via_unchecked",
format = "coco",
arity = 2,
[
open
@index 1
@title "Unsized"
@variant "dynamic_size"
],
[
@index 2
@title "Dynamically Padded"
@variant "dynamic_padding"
]
)]
#[must_use = "has no side effects"]
#[inline(always)]
pub unsafe fn via_unchecked(self) -> (&'a T, &'a [T::Elem]) {
// SAFETY: The aliasing of `self.into_ptr()` is not `Exclusive`, but the
// caller has promised that if `T` permits interior mutation then the
// left and right portions of `self` split at `l_len` do not overlap.
let (l, r) = unsafe { self.into_ptr().via_unchecked() };
(l.as_ref(), r.as_ref())
}
}
impl<'a, T> Split<&'a mut T>
where
T: ?Sized + SplitAt,
{
#[inline(always)]
fn into_ptr(self) -> Split<Ptr<'a, T, (Exclusive, Aligned, Safe)>> {
let source = Ptr::from_mut(self.source);
// SAFETY: `Ptr::from_mut(self.source)` points to exactly `self.source`,
// and thus maintains the invariants of `self` with respect to `l_len`.
unsafe { Split::new(source, self.l_len) }
}
/// Produces the split parts of `self`, using [`IntoBytes`] to ensure that
/// it is sound to have concurrent references to both parts.
///
/// # Examples
///
/// ```
/// use zerocopy::{SplitAt, FromBytes};
/// # use zerocopy_derive::*;
///
/// #[derive(SplitAt, FromBytes, KnownLayout, IntoBytes)]
/// #[repr(C)]
/// struct Packet<B: ?Sized> {
/// length: u8,
/// body: B,
/// }
///
/// // These bytes encode a `Packet`.
/// let mut bytes = &mut [4, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
///
/// let packet = Packet::<[u8]>::mut_from_bytes(bytes).unwrap();
///
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
///
/// {
/// // Attempt to split `packet` at `length`.
/// let split = packet.split_at_mut(packet.length as usize).unwrap();
///
/// // Use the `IntoBytes` bound on `Packet` to prove that it's okay to
/// // return concurrent references to `packet` and `rest`.
/// let (packet, rest) = split.via_into_bytes();
///
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4]);
/// assert_eq!(rest, [5, 6, 7, 8, 9]);
///
/// rest.fill(0);
/// }
///
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4, 0, 0, 0, 0, 0]);
/// ```
///
/// # Code Generation
///
/// See [`Split::via_immutable`](#method.split_via_immutable.codegen).
#[must_use = "has no side effects"]
#[inline(always)]
pub fn via_into_bytes(self) -> (&'a mut T, &'a mut [T::Elem])
where
T: IntoBytes,
{
let (l, r) = self.into_ptr().via_into_bytes();
(l.as_mut(), r.as_mut())
}
/// Produces the split parts of `self` using [`Self::via_no_dynamic_padding`].
#[deprecated(note = "use `Split::via_no_dynamic_padding` instead")]
#[doc(hidden)]
#[must_use = "has no side effects"]
#[inline(always)]
pub fn via_unaligned(self) -> (&'a mut T, &'a mut [T::Elem])
where
T: Unaligned,
{
self.via_no_dynamic_padding()
}
/// Produces the split parts of `self` using a compile-time assertion to
/// ensure that it is unconditionally sound to have concurrent references to
/// both parts.
///
/// When possible, you should prefer using [`Self::via_into_bytes`], which
/// ensures this safety property through regular trait bounds.
///
/// # Examples
///
/// In the below example, a single byte of padding exists (on most
/// platforms) between `Packet<[u16]>`'s `length` and `body` fields,
/// precluding the use of [`Self::via_into_bytes`].
///
/// ```
/// use zerocopy::SplitAt;
/// # use zerocopy_derive::*;
///
/// #[derive(SplitAt, KnownLayout)]
/// #[repr(C)]
/// struct Packet<B: ?Sized> {
/// length: u8,
/// body: B,
/// }
///
/// let packet: &mut Packet<[u16]> = &mut Packet { length: 4, body: [1u16, 2, 3, 4, 5, 6, 7, 8, 9] };
///
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
///
/// // Every body length requires no trailing padding.
/// if let Some(split) = packet.split_at_mut(packet.length as usize) {
/// let (packet, rest) = split.via_no_dynamic_padding();
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4]);
/// assert_eq!(rest, [5, 6, 7, 8, 9]);
/// rest.fill(0);
/// } else {
/// unreachable!("The packet's length field is within its body");
/// }
///
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4, 0, 0, 0, 0, 0]);
/// ```
///
/// # Compile-Time Assertions
///
/// This method rejects at compile-time any type that admits dynamic
/// trailing padding. In the below example, some `split_at` indices would
/// produce left parts whose trailing padding overlaps with the right part:
///
/// ```compile_fail,E0080
/// use zerocopy::SplitAt;
/// # use zerocopy_derive::*;
///
/// #[derive(SplitAt, KnownLayout)]
/// #[repr(C, align(2))]
/// struct Packet<B: ?Sized> {
/// length: u16,
/// body: B,
/// }
///
/// let packet: &mut Packet<[u8]> = &mut Packet { length: 4, body: [1u8, 2, 3, 4, 5, 6, 7, 8, 9] };
///
/// if let Some(split) = packet.split_at_mut(packet.length as usize) {
/// let _ = split.via_no_dynamic_padding(); // âš  Compile Error!
/// } else {
/// unreachable!("The packet's length field is within its body");
/// }
/// ```
///
/// If you need to split such types, use [`Self::via_runtime_check`].
///
/// # Code Generation
///
/// See [`Split::via_immutable`](#method.split_via_immutable.codegen).
#[must_use = "has no side effects"]
#[inline(always)]
pub fn via_no_dynamic_padding(self) -> (&'a mut T, &'a mut [T::Elem]) {
let (l, r) = self.into_ptr().via_no_dynamic_padding();
(l.as_mut(), r.as_mut())
}
/// Produces the split parts of `self`, using a dynamic check to ensure that
/// it is sound to have concurrent references to both parts. You should
/// prefer using [`Self::via_into_bytes`] or
/// [`Self::via_no_dynamic_padding`], which perform no runtime check.
///
/// # Examples
///
/// ```
/// use zerocopy::SplitAt;
/// # use zerocopy_derive::*;
///
/// #[derive(SplitAt, KnownLayout)]
/// #[repr(C, align(2))]
/// struct Packet<B: ?Sized> {
/// length: u16,
/// body: B,
/// }
///
/// let packet: &mut Packet<[u8]> = &mut Packet { length: 4, body: [1u8, 2, 3, 4, 5, 6, 7, 8, 9] };
///
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
///
/// // The two-byte prefix and four-byte body need no trailing padding.
/// if let Some(split) = packet.split_at_mut(packet.length as usize) {
/// if let Ok((packet, rest)) = split.via_runtime_check() {
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4]);
/// assert_eq!(rest, [5, 6, 7, 8, 9]);
/// rest.fill(0);
/// } else {
/// unreachable!("A four-byte body requires no trailing padding");
/// }
/// } else {
/// unreachable!("The packet's length field is within its body");
/// }
///
/// assert_eq!(packet.length, 4);
/// assert_eq!(packet.body, [1, 2, 3, 4, 0, 0, 0, 0, 0]);
/// ```
///
/// # Code Generation
///
/// See [`Split::via_runtime_check`](#method.split_via_runtime_check.codegen).
#[must_use = "has no side effects"]
#[inline(always)]
pub fn via_runtime_check(self) -> Result<(&'a mut T, &'a mut [T::Elem]), Self> {
match self.into_ptr().via_runtime_check() {
Ok((l, r)) => Ok((l.as_mut(), r.as_mut())),
Err(s) => Err(s.into_mut()),
}
}
/// Unsafely produces the split parts of `self`.
///
/// # Safety
///
/// The trailing padding bytes of the left portion must not overlap the
/// right portion. For some dynamically sized types, the padding that
/// appears after the trailing slice field [is a dynamic function of the
/// trailing slice length](KnownLayout#slice-dst-layout). Thus, for some
/// types, this condition is dependent on the length of the left portion.
///
/// # Code Generation
///
/// See [`Split::via_unchecked`](#method.split_via_unchecked.codegen).
#[must_use = "has no side effects"]
#[inline(always)]
pub unsafe fn via_unchecked(self) -> (&'a mut T, &'a mut [T::Elem]) {
// SAFETY: The aliasing of `self.into_ptr()` is `Exclusive`, and the
// caller has promised that the left and right portions of `self` split
// at `l_len` do not overlap.
let (l, r) = unsafe { self.into_ptr().via_unchecked() };
(l.as_mut(), r.as_mut())
}
}
impl<'a, T, I> Split<Ptr<'a, T, I>>
where
T: ?Sized + SplitAt,
I: Invariants<Alignment = Aligned, Validity = Safe>,
{
fn into_ref(self) -> Split<&'a T>
where
I: Invariants<Aliasing = Shared>,
{
// SAFETY: `self.source.as_ref()` points to exactly the same referent as
// `self.source` and thus maintains the invariants of `self` with
// respect to `l_len`.
unsafe { Split::new(self.source.as_ref(), self.l_len) }
}
fn into_mut(self) -> Split<&'a mut T>
where
I: Invariants<Aliasing = Exclusive>,
{
// SAFETY: `self.source.as_mut()` points to exactly the same referent as
// `self.source` and thus maintains the invariants of `self` with
// respect to `l_len`.
unsafe { Split::new(self.source.unify_invariants().as_mut(), self.l_len) }
}
/// Produces the length of `self`'s left part.
#[inline(always)]
fn l_len(&self) -> MetadataOf<T> {
// SAFETY: By invariant on `Split`, `self.l_len` is not greater than the
// length of `self.source`.
unsafe { MetadataOf::<T>::new_unchecked(self.l_len) }
}
/// Produces the split parts of `self`, using [`Immutable`] to ensure that
/// it is sound to have concurrent references to both parts.
#[inline(always)]
fn via_immutable(self) -> (Ptr<'a, T, I>, Ptr<'a, [T::Elem], I>)
where
T: Immutable,
I: Invariants<Aliasing = Shared>,
{
// SAFETY: `Aliasing = Shared` and `T: Immutable`.
unsafe { self.via_unchecked() }
}
/// Produces the split parts of `self`, using [`IntoBytes`] to ensure that
/// it is sound to have concurrent references to both parts.
#[inline(always)]
fn via_into_bytes(self) -> (Ptr<'a, T, I>, Ptr<'a, [T::Elem], I>)
where
T: IntoBytes,
{
// SAFETY: By `T: IntoBytes`, `T` has no padding for any length.
// Consequently, `T` can be split into non-overlapping parts at any
// index.
unsafe { self.via_unchecked() }
}
/// Produces the split parts of `self` after the conservative layout
/// predicate establishes that `T` never requires dynamic trailing padding.
#[inline(always)]
fn via_no_dynamic_padding(self) -> (Ptr<'a, T, I>, Ptr<'a, [T::Elem], I>) {
static_assert!(
T: ?Sized + KnownLayout => !T::LAYOUT.requires_dynamic_padding(),
"`Split::via_no_dynamic_padding` cannot be used with a type whose layout may require dynamic trailing padding; use `Split::via_runtime_check` instead"
);
// SAFETY: The assertion establishes that `requires_dynamic_padding()`
// is false. By that method's guarantee, every valid metadata—including
// `self.l_len()`, which is valid by `Split`'s invariant—describes an
// object with no trailing padding. By the postcondition of
// `PtrInner::split_at_unchecked`, the two returned referents are then
// contiguous and non-overlapping. Since the source already conforms to
// `I::Aliasing`, splitting it into disjoint byte ranges preserves that
// invariant: under `Exclusive`, neither result aliases or accesses the
// other's bytes; under `Shared`, interior mutation through one result
// cannot mutate bytes pointed to by the other. This satisfies Rust's
// reference aliasing rules [1].
//
// [1] Per https://doc.rust-lang.org/1.93.1/std/ptr/index.html#pointer-to-reference-conversion:
//
// When creating a mutable reference, then while this reference
// exists, the memory it points to must not get accessed (read or
// written) through any other pointer or reference not derived from
// this reference.
//
// When creating a shared reference, then while this reference
// exists, the memory it points to must not get mutated (except inside
// `UnsafeCell`).
unsafe { self.via_unchecked() }
}
/// Produces the split parts of `self`, using a dynamic check to ensure that
/// it is sound to have concurrent references to both parts. You should
/// prefer using [`Self::via_immutable`], [`Self::via_into_bytes`], or
/// [`Self::via_no_dynamic_padding`], which perform no runtime check.
#[inline(always)]
fn via_runtime_check(self) -> Result<(Ptr<'a, T, I>, Ptr<'a, [T::Elem], I>), Self> {
let l_len = self.l_len();
// `l_len` witnesses an object size at most `isize::MAX`, and
// `KnownLayout::LAYOUT` guarantees that it contains the trailing
// slice. Thus `padding_for_elems` returns the exact trailing padding.
let trailing_padding = crate::trailing_slice_layout::<T>().padding_for_elems(l_len.get());
// FIXME(#1290): Once we require `KnownLayout` on all fields, add an
// `IS_IMMUTABLE` associated const, and add `T::IS_IMMUTABLE ||` to the
// below check.
if trailing_padding == 0 {
// SAFETY: As established above, `trailing_padding` is the exact
// padding after the left part's trailing slice. If it is zero,
// the left and right parts are strictly non-overlapping.
Ok(unsafe { self.via_unchecked() })
} else {
Err(self)
}
}
/// Unsafely produces the split parts of `self`.
///
/// # Safety
///
/// The caller promises that if `I::Aliasing` is [`Exclusive`] or `T`
/// permits interior mutation, then the left part has no trailing padding.
#[inline(always)]
unsafe fn via_unchecked(self) -> (Ptr<'a, T, I>, Ptr<'a, [T::Elem], I>) {
let l_len = self.l_len();
let inner = self.source.as_inner();
// SAFETY: By invariant on `Self::l_len`, `l_len` is not greater than
// the length of `inner`'s trailing slice.
let (left, right) = unsafe { inner.split_at_unchecked(l_len) };
// Lemma 0: `left` and `right` conform to the aliasing invariant
// `I::Aliasing`. Proof: If `I::Aliasing` is `Exclusive` or `T` permits
// interior mutation, the caller promises that the left part has no
// trailing padding. Consequently, by the postcondition of
// `PtrInner::split_at_unchecked`, `left` and `right` do not overlap.
// If `I::Aliasing` is shared and `T` forbids interior mutation, then
// overlap between their referents is permissible.
// SAFETY:
// 0. `left` conforms to the aliasing invariant of `I::Aliasing`, by Lemma 0.
// 1. `left` conforms to the alignment invariant of `I::Alignment, because
// the referents of `left` and `Self` have the same address and type
// (and, thus, alignment requirement).
// 2. `left` conforms to the validity invariant of `I::Validity`, neither
// the type nor bytes of `left`'s referent have been changed.
let left = unsafe { Ptr::from_inner(left) };
// SAFETY:
// 0. `right` conforms to the aliasing invariant of `I::Aliasing`, by Lemma
// 0.
// 1. `right` conforms to the alignment invariant of `I::Alignment`:
// if the source is aligned, `T: SplitAt` guarantees that its
// trailing slice's address is aligned for `T::Elem`. Advancing by
// whole elements preserves that alignment.
// 2. `right` conforms to the validity invariant of `I::Validity`,
// because `T: SplitAt` guarantees that `T::Elem` has the size and
// bit validity of the original trailing element. It also guarantees
// compatible shared and mutable access, so writes through `right`
// preserve the original elements' validity and interior mutation
// respects the original `UnsafeCell` coverage. The left part
// cannot invalidate `right`: when the references are exclusive or
// permit interior mutation, the caller guarantees no trailing
// padding and hence no overlap.
let right = unsafe { Ptr::from_inner(right) };
(left, right)
}
}
#[cfg(test)]
mod tests {
use core::{cell::Cell, mem::ManuallyDrop};
use crate::{Immutable, KnownLayout, SplitAt, Unalign, Unaligned};
#[derive(KnownLayout, SplitAt, Immutable, Unaligned)]
#[repr(C, packed)]
struct Packed<T: ?Sized> {
prefix: u8,
tail: ManuallyDrop<T>,
}
#[derive(KnownLayout, SplitAt, Immutable)]
#[repr(C, packed(2))]
struct Packed2<T: ?Sized> {
prefix: u8,
tail: ManuallyDrop<T>,
}
#[derive(KnownLayout, SplitAt, Immutable)]
#[repr(C)]
struct Inner<T: ?Sized> {
prefix: u32,
tail: T,
}
#[test]
fn test_split_at_packed() {
use crate::{FromBytes, IntoBytes};
#[derive(FromBytes, IntoBytes, KnownLayout, SplitAt, Immutable, Unaligned)]
#[repr(C, packed)]
struct Packet {
prefix: u8,
tail: [u16],
}
// The tail starts at an odd address, exercising unaligned reads and
// writes on targets where `u16` has alignment 2.
#[repr(align(8))]
struct Bytes([u8; 9]);
for i in 0..=4 {
let mut bytes = Bytes([1; 9]);
let packet = Packet::ref_from_bytes(&bytes.0).unwrap();
let (left, right): (&Packet, &[Unalign<u16>]) =
packet.split_at(i).unwrap().via_immutable();
assert_eq!(core::mem::size_of_val(left), 1 + 2 * i);
assert_eq!(right.len(), 4 - i);
assert!(right.iter().all(|elem| elem.get() == 0x0101));
assert!(left.split_at(i).is_some());
assert!(left.split_at(i + 1).is_none());
assert!(packet.split_at(5).is_none());
let (_, right) = packet.split_at(i).unwrap().via_into_bytes();
assert_eq!(right.len(), 4 - i);
let (_, right) = packet.split_at(i).unwrap().via_no_dynamic_padding();
assert_eq!(right.len(), 4 - i);
let packet = Packet::mut_from_bytes(&mut bytes.0).unwrap();
let (left, right): (&mut Packet, &mut [Unalign<u16>]) =
packet.split_at_mut(i).unwrap().via_runtime_check().ok().unwrap();
left.prefix = 2;
for elem in right {
*elem = Unalign::new(0x0202);
}
assert_eq!(bytes.0[0], 2);
assert!(bytes.0[1..1 + 2 * i].iter().all(|&byte| byte == 1));
assert!(bytes.0[1 + 2 * i..].iter().all(|&byte| byte == 2));
}
}
#[test]
fn test_split_at_nested_packed() {
// A packing factor greater than 1 still exposes `Unalign` elements,
// including when the factor is below the original element alignment.
let mut words = Packed2 { prefix: 0, tail: ManuallyDrop::new([1u32, 2, 3]) };
let dst: &mut Packed2<[u32]> = &mut words;
let (left, right): (&mut _, &mut [Unalign<u32>]) =
dst.split_at_mut(1).unwrap().via_no_dynamic_padding();
left.prefix = 4;
right[0] = Unalign::new(5);
let dst: &Packed2<[u32]> = &words;
let (left, right) = dst.split_at(0).unwrap().via_immutable();
assert_eq!(left.prefix, 4);
assert_eq!(right[0].get(), 1);
assert_eq!(right[1].get(), 5);
assert_eq!(right[2].get(), 3);
let mut packet = Inner {
prefix: 0,
tail: Packed2 {
prefix: 1,
tail: ManuallyDrop::new(Packed {
prefix: 2,
tail: ManuallyDrop::new([3u16, 4, 5]),
}),
},
};
let dst: &Inner<Packed2<Packed<[u16]>>> = &packet;
let (left, right): (&_, &[Unalign<Unalign<u16>>]) =
dst.split_at(1).unwrap().via_immutable();
assert_eq!(left.prefix, 0);
assert_eq!(right.len(), 2);
assert_eq!(right[0].get().get(), 4);
assert_eq!(right[1].get().get(), 5);
// Split the packed field separately: the unpacked outer struct's
// padding does not constrain this split.
let dst: &mut Packed2<Packed<[u16]>> = &mut packet.tail;
let (left, right): (&mut _, &mut [Unalign<Unalign<u16>>]) =
dst.split_at_mut(1).unwrap().via_no_dynamic_padding();
left.prefix = 6;
right[0] = Unalign::new(Unalign::new(7));
let dst: &Inner<Packed2<Packed<[u16]>>> = &packet;
let (_, right) = dst.split_at(0).unwrap().via_immutable();
assert_eq!(right[0].get().get(), 3);
assert_eq!(right[1].get().get(), 7);
assert_eq!(right[2].get().get(), 5);
}
#[test]
fn test_split_at_packed_preserves_inner_padding() {
for i in 0..=4 {
let mut packet = Packed {
prefix: 1,
tail: ManuallyDrop::new(Inner { prefix: 0, tail: [2u8, 3, 4, 5] }),
};
let dst: &Packed<Inner<[u8]>> = &packet;
let (left, right): (&_, &[Unalign<u8>]) = dst.split_at(i).unwrap().via_immutable();
assert_eq!(right.len(), 4 - i);
assert_eq!(right.first().map(Unalign::get), [2, 3, 4, 5].get(i).copied());
// The inner `u32` prefix makes the tail start at byte 5 of the
// packed wrapper. Its size may still include inner padding.
let has_padding = core::mem::size_of_val(left) != 5 + i;
assert_eq!(dst.split_at(i).unwrap().via_runtime_check().is_err(), has_padding);
let dst: &mut Packed<Inner<[u8]>> = &mut packet;
let split = dst.split_at_mut(i).unwrap().via_runtime_check();
assert_eq!(split.is_err(), has_padding);
if let Ok((left, right)) = split {
left.prefix = 6;
for elem in right {
*elem = Unalign::new(7);
}
let dst: &Packed<Inner<[u8]>> = &packet;
let (left, right) = dst.split_at(i).unwrap().via_immutable();
assert_eq!(left.prefix, 6);
assert!(right.iter().all(|elem| elem.get() == 7));
}
}
}
#[test]
fn test_split_at_packed_interior_mutation_and_zsts() {
let packet = Packed {
prefix: 0,
tail: ManuallyDrop::new([Cell::new(1u8), Cell::new(2), Cell::new(3)]),
};
let dst: &Packed<[Cell<u8>]> = &packet;
let (left, right): (&_, &[Unalign<Cell<u8>>]) =
dst.split_at(1).unwrap().via_no_dynamic_padding();
left.tail[0].set(4);
right[0].try_deref().unwrap().set(5);
assert_eq!(dst.tail[0].get(), 4);
assert_eq!(dst.tail[1].get(), 5);
assert_eq!(dst.tail[2].get(), 3);
let mut packet = Packed { prefix: 0, tail: ManuallyDrop::new([(); 3]) };
for i in 0..=3 {
let dst: &mut Packed<[()]> = &mut packet;
let (left, right): (&mut _, &mut [Unalign<()>]) =
dst.split_at_mut(i).unwrap().via_no_dynamic_padding();
assert_eq!(left.tail.len(), i);
assert_eq!(right.len(), 3 - i);
assert_eq!(core::mem::size_of_val(left), 1);
}
}
#[cfg(feature = "derive")]
#[test]
fn test_split_at() {
use crate::{FromBytes, Immutable, IntoBytes, KnownLayout, SplitAt};
#[derive(FromBytes, KnownLayout, SplitAt, IntoBytes, Immutable, Debug)]
#[repr(C)]
struct SliceDst<const OFFSET: usize> {
prefix: [u8; OFFSET],
trailing: [u8],
}
#[allow(clippy::as_conversions)]
fn test_split_at<const OFFSET: usize, const BUFFER_SIZE: usize>() {
// Test `split_at`
let n: usize = BUFFER_SIZE - OFFSET;
let arr = [1; BUFFER_SIZE];
let dst = SliceDst::<OFFSET>::ref_from_bytes(&arr[..]).unwrap();
for i in 0..=n {
let (l, r) = dst.split_at(i).unwrap().via_runtime_check().unwrap();
let l_sum: u8 = l.trailing.iter().sum();
let r_sum: u8 = r.iter().sum();
assert_eq!(l_sum, i as u8);
assert_eq!(r_sum, (n - i) as u8);
assert_eq!(l_sum + r_sum, n as u8);
}
// Test `split_at_mut`
let n: usize = BUFFER_SIZE - OFFSET;
let mut arr = [1; BUFFER_SIZE];
let dst = SliceDst::<OFFSET>::mut_from_bytes(&mut arr[..]).unwrap();
for i in 0..=n {
let (l, r) = dst.split_at_mut(i).unwrap().via_runtime_check().unwrap();
let l_sum: u8 = l.trailing.iter().sum();
let r_sum: u8 = r.iter().sum();
assert_eq!(l_sum, i as u8);
assert_eq!(r_sum, (n - i) as u8);
assert_eq!(l_sum + r_sum, n as u8);
}
}
test_split_at::<0, 16>();
test_split_at::<1, 17>();
test_split_at::<2, 18>();
}
#[cfg(feature = "derive")]
#[test]
#[allow(clippy::as_conversions)]
fn test_split_at_overlapping() {
use crate::{FromBytes, Immutable, IntoBytes, KnownLayout, SplitAt};
#[derive(FromBytes, KnownLayout, SplitAt, Immutable)]
#[repr(C, align(2))]
struct SliceDst {
prefix: u8,
trailing: [u8],
}
assert!(SliceDst::LAYOUT.requires_dynamic_padding());
const N: usize = 16;
let arr = [1u16; N];
let dst = SliceDst::ref_from_bytes(arr.as_bytes()).unwrap();
for i in 0..N {
let split = dst.split_at(i).unwrap().via_runtime_check();
if i % 2 == 1 {
assert!(split.is_ok());
} else {
assert!(split.is_err());
}
}
}
#[test]
fn test_split_at_unchecked() {
use crate::SplitAt;
let mut arr = [1, 2, 3, 4];
let slice = &arr[..];
// SAFETY: 2 <= arr.len() (4)
let split = unsafe { SplitAt::split_at_unchecked(slice, 2) };
// SAFETY: SplitAt::split_at_unchecked guarantees that the split is valid.
let (l, r) = unsafe { split.via_unchecked() };
assert_eq!(l, &[1, 2]);
assert_eq!(r, &[3, 4]);
let slice_mut = &mut arr[..];
// SAFETY: 2 <= arr.len() (4)
let split = unsafe { SplitAt::split_at_mut_unchecked(slice_mut, 2) };
// SAFETY: SplitAt::split_at_mut_unchecked guarantees that the split is valid.
let (l, r) = unsafe { split.via_unchecked() };
assert_eq!(l, &mut [1, 2]);
assert_eq!(r, &mut [3, 4]);
}
#[test]
fn test_split_at_via_methods() {
use crate::{FromBytes, Immutable, IntoBytes, KnownLayout, SplitAt};
#[derive(FromBytes, KnownLayout, SplitAt, IntoBytes, Immutable, Debug)]
#[repr(C)]
struct Packet {
length: u8,
body: [u8],
}
let arr = [1, 2, 3, 4];
let packet = Packet::ref_from_bytes(&arr[..]).unwrap();
let split1 = packet.split_at(2).unwrap();
let (l, r) = split1.via_immutable();
assert_eq!(l.length, 1);
assert_eq!(r, &[4]);
let split2 = packet.split_at(2).unwrap();
let (l, r) = split2.via_into_bytes();
assert_eq!(l.length, 1);
assert_eq!(r, &[4]);
}
#[test]
#[allow(deprecated)]
fn test_split_at_via_unaligned() {
use crate::{Immutable, KnownLayout, Split, SplitAt, Unaligned};
fn via_unaligned<'a, T>(split: Split<&'a T>) -> (&'a T, &'a [T::Elem])
where
T: ?Sized + SplitAt + Unaligned,
{
split.via_unaligned()
}
fn via_unaligned_mut<'a, T>(split: Split<&'a mut T>) -> (&'a mut T, &'a mut [T::Elem])
where
T: ?Sized + SplitAt + Unaligned,
{
split.via_unaligned()
}
#[derive(KnownLayout, SplitAt, Immutable, Unaligned)]
#[repr(C)]
struct Packet<B: ?Sized> {
prefix: [u8; 2],
body: B,
}
// Exercise generic callers using the original `T: Unaligned` bound.
assert!(!Packet::<[[u8; 2]]>::LAYOUT.requires_dynamic_padding());
let packet = Packet { prefix: [0, 1], body: [[2, 3], [4, 5], [6, 7]] };
let packet: &Packet<[[u8; 2]]> = &packet;
let split = packet.split_at(2).unwrap();
let (l, r) = via_unaligned(split);
assert_eq!(l.body, [[2, 3], [4, 5]]);
assert_eq!(r, &[[6, 7]]);
let mut packet = Packet { prefix: [0, 1], body: [[2, 3], [4, 5], [6, 7]] };
{
let packet: &mut Packet<[[u8; 2]]> = &mut packet;
let split = packet.split_at_mut(2).unwrap();
let (l, r) = via_unaligned_mut(split);
l.body[0] = [8, 9];
r[0] = [10, 11];
}
assert_eq!(packet.body, [[8, 9], [4, 5], [10, 11]]);
}
#[test]
fn test_split_at_via_no_dynamic_padding() {
use core::cell::Cell;
use crate::SplitAt;
// `u16` does not implement `Unaligned`, but `[u16]` never requires
// dynamic trailing padding.
let words = [1u16, 2, 3, 4];
let split = SplitAt::split_at(&words[..], 2).unwrap();
let (left, right) = split.via_no_dynamic_padding();
assert_eq!(left, [1, 2]);
assert_eq!(right, [3, 4]);
let mut words = [1u16, 2, 3, 4];
let split = SplitAt::split_at_mut(&mut words[..], 2).unwrap();
let (left, right) = split.via_no_dynamic_padding();
left[0] = 5;
right[0] = 6;
assert_eq!(words, [5, 2, 6, 4]);
// Exercise the safety-sensitive shared route with interior mutation.
let cells = [Cell::new(1u16), Cell::new(2), Cell::new(3)];
let split = SplitAt::split_at(&cells[..], 2).unwrap();
let (left, right) = split.via_no_dynamic_padding();
left[0].set(4);
right[0].set(5);
assert_eq!([cells[0].get(), cells[1].get(), cells[2].get()], [4, 2, 5]);
}
}