Skip to main content

zerocopy/
split_at.rs

1// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
2
3// Copyright 2025 The Fuchsia Authors
4//
5// Licensed under the 2-Clause BSD License <LICENSE-BSD or
6// https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0
7// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
8// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
9// This file may not be copied, modified, or distributed except according to
10// those terms.
11
12use super::*;
13use crate::pointer::invariant::{Aligned, Exclusive, Invariants, Shared, Valid};
14
15/// Types that can be split in two.
16///
17/// This trait generalizes Rust's existing support for splitting slices to
18/// support slices and slice-based dynamically-sized types ("slice DSTs").
19///
20/// # Implementation
21///
22/// **Do not implement this trait yourself!** Instead, use
23/// [`#[derive(SplitAt)]`][derive]; e.g.:
24///
25/// ```
26/// # use zerocopy_derive::{SplitAt, KnownLayout};
27/// #[derive(SplitAt, KnownLayout)]
28/// #[repr(C)]
29/// struct MyStruct<T: ?Sized> {
30/// # /*
31///     ...,
32/// # */
33///     // `SplitAt` types must have at least one field.
34///     field: T,
35/// }
36/// ```
37///
38/// This derive performs a sophisticated, compile-time safety analysis to
39/// determine whether a type is `SplitAt`.
40///
41/// # Safety
42///
43/// This trait does not convey any safety guarantees to code outside this crate.
44///
45/// You must not rely on the `#[doc(hidden)]` internals of `SplitAt`. Future
46/// releases of zerocopy may make backwards-breaking changes to these items,
47/// including changes that only affect soundness, which may cause code which
48/// uses those items to silently become unsound.
49///
50#[cfg_attr(feature = "derive", doc = "[derive]: zerocopy_derive::SplitAt")]
51#[cfg_attr(
52    not(feature = "derive"),
53    doc = concat!("[derive]: https://docs.rs/zerocopy/", env!("CARGO_PKG_VERSION"), "/zerocopy/derive.SplitAt.html"),
54)]
55#[cfg_attr(
56    not(no_zerocopy_diagnostic_on_unimplemented_1_78_0),
57    diagnostic::on_unimplemented(note = "Consider adding `#[derive(SplitAt)]` to `{Self}`")
58)]
59// # Safety
60//
61// The trailing slice is well-aligned for its element type. `Self` is `[T]`, or
62// a `repr(C)` or `repr(transparent)` slice DST.
63pub unsafe trait SplitAt: KnownLayout<PointerMetadata = usize> {
64    /// The element type of the trailing slice.
65    type Elem;
66
67    #[doc(hidden)]
68    fn only_derive_is_allowed_to_implement_this_trait()
69    where
70        Self: Sized;
71
72    /// Unsafely splits `self` in two.
73    ///
74    /// # Safety
75    ///
76    /// The caller promises that `l_len` is not greater than the length of
77    /// `self`'s trailing slice.
78    ///
79    #[doc = codegen_section!(
80        header = "h5",
81        bench = "split_at_unchecked",
82        format = "coco",
83        arity = 2,
84        [
85            open
86            @index 1
87            @title "Unsized"
88            @variant "dynamic_size"
89        ],
90        [
91            @index 2
92            @title "Dynamically Padded"
93            @variant "dynamic_padding"
94        ]
95    )]
96    #[inline]
97    #[must_use]
98    unsafe fn split_at_unchecked(&self, l_len: usize) -> Split<&Self> {
99        // SAFETY: By precondition on the caller, `l_len <= self.len()`.
100        unsafe { Split::<&Self>::new(self, l_len) }
101    }
102
103    /// Attempts to split `self` in two.
104    ///
105    /// Returns `None` if `l_len` is greater than the length of `self`'s
106    /// trailing slice.
107    ///
108    /// # Examples
109    ///
110    /// ```
111    /// use zerocopy::{SplitAt, FromBytes};
112    /// # use zerocopy_derive::*;
113    ///
114    /// #[derive(SplitAt, FromBytes, KnownLayout, Immutable)]
115    /// #[repr(C)]
116    /// struct Packet {
117    ///     length: u8,
118    ///     body: [u8],
119    /// }
120    ///
121    /// // These bytes encode a `Packet`.
122    /// let bytes = &[4, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
123    ///
124    /// let packet = Packet::ref_from_bytes(bytes).unwrap();
125    ///
126    /// assert_eq!(packet.length, 4);
127    /// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
128    ///
129    /// // Attempt to split `packet` at `length`.
130    /// let split = packet.split_at(packet.length as usize).unwrap();
131    ///
132    /// // Use the `Immutable` bound on `Packet` to prove that it's okay to
133    /// // return concurrent references to `packet` and `rest`.
134    /// let (packet, rest) = split.via_immutable();
135    ///
136    /// assert_eq!(packet.length, 4);
137    /// assert_eq!(packet.body, [1, 2, 3, 4]);
138    /// assert_eq!(rest, [5, 6, 7, 8, 9]);
139    /// ```
140    ///
141    #[doc = codegen_section!(
142        header = "h5",
143        bench = "split_at",
144        format = "coco",
145        arity = 2,
146        [
147            open
148            @index 1
149            @title "Unsized"
150            @variant "dynamic_size"
151        ],
152        [
153            @index 2
154            @title "Dynamically Padded"
155            @variant "dynamic_padding"
156        ]
157    )]
158    #[inline]
159    #[must_use = "has no side effects"]
160    fn split_at(&self, l_len: usize) -> Option<Split<&Self>> {
161        MetadataOf::new_in_bounds(self, l_len).map(
162            #[inline(always)]
163            |l_len| {
164                // SAFETY: We have ensured that `l_len <= self.len()` (by
165                // post-condition on `MetadataOf::new_in_bounds`)
166                unsafe { Split::new(self, l_len.get()) }
167            },
168        )
169    }
170
171    /// Unsafely splits `self` in two.
172    ///
173    /// # Safety
174    ///
175    /// The caller promises that `l_len` is not greater than the length of
176    /// `self`'s trailing slice.
177    ///
178    #[doc = codegen_header!("h5", "split_at_mut_unchecked")]
179    ///
180    /// See [`SplitAt::split_at_unchecked`](#method.split_at_unchecked.codegen).
181    #[inline]
182    #[must_use]
183    unsafe fn split_at_mut_unchecked(&mut self, l_len: usize) -> Split<&mut Self> {
184        // SAFETY: By precondition on the caller, `l_len <= self.len()`.
185        unsafe { Split::<&mut Self>::new(self, l_len) }
186    }
187
188    /// Attempts to split `self` in two.
189    ///
190    /// Returns `None` if `l_len` is greater than the length of `self`'s
191    /// trailing slice, or if the given `l_len` would result in [the trailing
192    /// padding](KnownLayout#slice-dst-layout) of the left portion overlapping
193    /// the right portion.
194    ///
195    ///
196    /// # Examples
197    ///
198    /// ```
199    /// use zerocopy::{SplitAt, FromBytes};
200    /// # use zerocopy_derive::*;
201    ///
202    /// #[derive(SplitAt, FromBytes, KnownLayout, IntoBytes)]
203    /// #[repr(C)]
204    /// struct Packet<B: ?Sized> {
205    ///     length: u8,
206    ///     body: B,
207    /// }
208    ///
209    /// // These bytes encode a `Packet`.
210    /// let mut bytes = &mut [4, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
211    ///
212    /// let packet = Packet::<[u8]>::mut_from_bytes(bytes).unwrap();
213    ///
214    /// assert_eq!(packet.length, 4);
215    /// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
216    ///
217    /// {
218    ///     // Attempt to split `packet` at `length`.
219    ///     let split = packet.split_at_mut(packet.length as usize).unwrap();
220    ///
221    ///     // Use the `IntoBytes` bound on `Packet` to prove that it's okay to
222    ///     // return concurrent references to `packet` and `rest`.
223    ///     let (packet, rest) = split.via_into_bytes();
224    ///
225    ///     assert_eq!(packet.length, 4);
226    ///     assert_eq!(packet.body, [1, 2, 3, 4]);
227    ///     assert_eq!(rest, [5, 6, 7, 8, 9]);
228    ///
229    ///     rest.fill(0);
230    /// }
231    ///
232    /// assert_eq!(packet.length, 4);
233    /// assert_eq!(packet.body, [1, 2, 3, 4, 0, 0, 0, 0, 0]);
234    /// ```
235    ///
236    #[doc = codegen_header!("h5", "split_at_mut")]
237    ///
238    /// See [`SplitAt::split_at`](#method.split_at.codegen).
239    #[inline]
240    fn split_at_mut(&mut self, l_len: usize) -> Option<Split<&mut Self>> {
241        MetadataOf::new_in_bounds(self, l_len).map(
242            #[inline(always)]
243            |l_len| {
244                // SAFETY: We have ensured that `l_len <= self.len()` (by
245                // post-condition on `MetadataOf::new_in_bounds`)
246                unsafe { Split::new(self, l_len.get()) }
247            },
248        )
249    }
250}
251
252// SAFETY: `[T]`'s trailing slice is `[T]`, which is trivially aligned.
253unsafe impl<T> SplitAt for [T] {
254    type Elem = T;
255
256    #[inline]
257    #[allow(dead_code)]
258    fn only_derive_is_allowed_to_implement_this_trait()
259    where
260        Self: Sized,
261    {
262    }
263}
264
265/// A `T` that has been split into two possibly-overlapping parts.
266///
267/// For some dynamically sized types, the padding that appears after the
268/// trailing slice field [is a dynamic function of the trailing slice
269/// length](KnownLayout#slice-dst-layout). If `T` is split at a length that
270/// requires trailing padding, the trailing padding of the left part of the
271/// split `T` will overlap the right part. If `T` is a mutable reference or
272/// permits interior mutation, you must ensure that the left and right parts do
273/// not overlap. You can do this at zero-cost using using
274/// [`Self::via_immutable`], [`Self::via_into_bytes`], or
275/// [`Self::via_unaligned`], or with a dynamic check by using
276/// [`Self::via_runtime_check`].
277#[derive(Debug)]
278pub struct Split<T> {
279    /// A pointer to the source slice DST.
280    source: T,
281    /// The length of the future left half of `source`.
282    ///
283    /// # Safety
284    ///
285    /// If `source` is a pointer to a slice DST, `l_len` is no greater than
286    /// `source`'s length.
287    l_len: usize,
288}
289
290impl<T> Split<T> {
291    /// Produces a `Split` of `source` with `l_len`.
292    ///
293    /// # Safety
294    ///
295    /// `l_len` is no greater than `source`'s length.
296    #[inline(always)]
297    unsafe fn new(source: T, l_len: usize) -> Self {
298        Self { source, l_len }
299    }
300}
301
302impl<'a, T> Split<&'a T>
303where
304    T: ?Sized + SplitAt,
305{
306    #[inline(always)]
307    fn into_ptr(self) -> Split<Ptr<'a, T, (Shared, Aligned, Valid)>> {
308        let source = Ptr::from_ref(self.source);
309        // SAFETY: `Ptr::from_ref(self.source)` points to exactly `self.source`
310        // and thus maintains the invariants of `self` with respect to `l_len`.
311        unsafe { Split::new(source, self.l_len) }
312    }
313
314    /// Produces the split parts of `self`, using [`Immutable`] to ensure that
315    /// it is sound to have concurrent references to both parts.
316    ///
317    /// # Examples
318    ///
319    /// ```
320    /// use zerocopy::{SplitAt, FromBytes};
321    /// # use zerocopy_derive::*;
322    ///
323    /// #[derive(SplitAt, FromBytes, KnownLayout, Immutable)]
324    /// #[repr(C)]
325    /// struct Packet {
326    ///     length: u8,
327    ///     body: [u8],
328    /// }
329    ///
330    /// // These bytes encode a `Packet`.
331    /// let bytes = &[4, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
332    ///
333    /// let packet = Packet::ref_from_bytes(bytes).unwrap();
334    ///
335    /// assert_eq!(packet.length, 4);
336    /// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
337    ///
338    /// // Attempt to split `packet` at `length`.
339    /// let split = packet.split_at(packet.length as usize).unwrap();
340    ///
341    /// // Use the `Immutable` bound on `Packet` to prove that it's okay to
342    /// // return concurrent references to `packet` and `rest`.
343    /// let (packet, rest) = split.via_immutable();
344    ///
345    /// assert_eq!(packet.length, 4);
346    /// assert_eq!(packet.body, [1, 2, 3, 4]);
347    /// assert_eq!(rest, [5, 6, 7, 8, 9]);
348    /// ```
349    ///
350    #[doc = codegen_section!(
351        header = "h5",
352        bench = "split_via_immutable",
353        format = "coco",
354        arity = 2,
355        [
356            open
357            @index 1
358            @title "Unsized"
359            @variant "dynamic_size"
360        ],
361        [
362            @index 2
363            @title "Dynamically Padded"
364            @variant "dynamic_padding"
365        ]
366    )]
367    #[must_use = "has no side effects"]
368    #[inline(always)]
369    pub fn via_immutable(self) -> (&'a T, &'a [T::Elem])
370    where
371        T: Immutable,
372    {
373        let (l, r) = self.into_ptr().via_immutable();
374        (l.as_ref(), r.as_ref())
375    }
376
377    /// Produces the split parts of `self`, using [`IntoBytes`] to ensure that
378    /// it is sound to have concurrent references to both parts.
379    ///
380    /// # Examples
381    ///
382    /// ```
383    /// use zerocopy::{SplitAt, FromBytes};
384    /// # use zerocopy_derive::*;
385    ///
386    /// #[derive(SplitAt, FromBytes, KnownLayout, Immutable, IntoBytes)]
387    /// #[repr(C)]
388    /// struct Packet<B: ?Sized> {
389    ///     length: u8,
390    ///     body: B,
391    /// }
392    ///
393    /// // These bytes encode a `Packet`.
394    /// let bytes = &[4, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
395    ///
396    /// let packet = Packet::<[u8]>::ref_from_bytes(bytes).unwrap();
397    ///
398    /// assert_eq!(packet.length, 4);
399    /// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
400    ///
401    /// // Attempt to split `packet` at `length`.
402    /// let split = packet.split_at(packet.length as usize).unwrap();
403    ///
404    /// // Use the `IntoBytes` bound on `Packet` to prove that it's okay to
405    /// // return concurrent references to `packet` and `rest`.
406    /// let (packet, rest) = split.via_into_bytes();
407    ///
408    /// assert_eq!(packet.length, 4);
409    /// assert_eq!(packet.body, [1, 2, 3, 4]);
410    /// assert_eq!(rest, [5, 6, 7, 8, 9]);
411    /// ```
412    ///
413    #[doc = codegen_header!("h5", "split_via_into_bytes")]
414    ///
415    /// See [`Split::via_immutable`](#method.split_via_immutable.codegen).
416    #[must_use = "has no side effects"]
417    #[inline(always)]
418    pub fn via_into_bytes(self) -> (&'a T, &'a [T::Elem])
419    where
420        T: IntoBytes,
421    {
422        let (l, r) = self.into_ptr().via_into_bytes();
423        (l.as_ref(), r.as_ref())
424    }
425
426    /// Produces the split parts of `self`, using [`Unaligned`] to ensure that
427    /// it is sound to have concurrent references to both parts.
428    ///
429    /// # Examples
430    ///
431    /// ```
432    /// use zerocopy::{SplitAt, FromBytes};
433    /// # use zerocopy_derive::*;
434    ///
435    /// #[derive(SplitAt, FromBytes, KnownLayout, Immutable, Unaligned)]
436    /// #[repr(C)]
437    /// struct Packet {
438    ///     length: u8,
439    ///     body: [u8],
440    /// }
441    ///
442    /// // These bytes encode a `Packet`.
443    /// let bytes = &[4, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
444    ///
445    /// let packet = Packet::ref_from_bytes(bytes).unwrap();
446    ///
447    /// assert_eq!(packet.length, 4);
448    /// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
449    ///
450    /// // Attempt to split `packet` at `length`.
451    /// let split = packet.split_at(packet.length as usize).unwrap();
452    ///
453    /// // Use the `Unaligned` bound on `Packet` to prove that it's okay to
454    /// // return concurrent references to `packet` and `rest`.
455    /// let (packet, rest) = split.via_unaligned();
456    ///
457    /// assert_eq!(packet.length, 4);
458    /// assert_eq!(packet.body, [1, 2, 3, 4]);
459    /// assert_eq!(rest, [5, 6, 7, 8, 9]);
460    /// ```
461    ///
462    #[doc = codegen_header!("h5", "split_via_unaligned")]
463    ///
464    /// See [`Split::via_immutable`](#method.split_via_immutable.codegen).
465    #[must_use = "has no side effects"]
466    #[inline(always)]
467    pub fn via_unaligned(self) -> (&'a T, &'a [T::Elem])
468    where
469        T: Unaligned,
470    {
471        let (l, r) = self.into_ptr().via_unaligned();
472        (l.as_ref(), r.as_ref())
473    }
474
475    /// Produces the split parts of `self`, using a dynamic check to ensure that
476    /// it is sound to have concurrent references to both parts. You should
477    /// prefer using [`Self::via_immutable`], [`Self::via_into_bytes`], or
478    /// [`Self::via_unaligned`], which have no runtime cost.
479    ///
480    /// Note that this check is overly conservative if `T` is [`Immutable`]; for
481    /// some types, this check will reject some splits which
482    /// [`Self::via_immutable`] will accept.
483    ///
484    /// # Examples
485    ///
486    /// ```
487    /// use zerocopy::{SplitAt, FromBytes, IntoBytes, network_endian::U16};
488    /// # use zerocopy_derive::*;
489    ///
490    /// #[derive(SplitAt, FromBytes, KnownLayout, Immutable, Debug)]
491    /// #[repr(C, align(2))]
492    /// struct Packet {
493    ///     length: U16,
494    ///     body: [u8],
495    /// }
496    ///
497    /// // These bytes encode a `Packet`.
498    /// let bytes = [
499    ///     4u16.to_be(),
500    ///     1u16.to_be(),
501    ///     2u16.to_be(),
502    ///     3u16.to_be(),
503    ///     4u16.to_be()
504    /// ];
505    ///
506    /// let packet = Packet::ref_from_bytes(bytes.as_bytes()).unwrap();
507    ///
508    /// assert_eq!(packet.length, 4);
509    /// assert_eq!(packet.body, [0, 1, 0, 2, 0, 3, 0, 4]);
510    ///
511    /// // Attempt to split `packet` at `length`.
512    /// let split = packet.split_at(packet.length.into()).unwrap();
513    ///
514    /// // Use a dynamic check to prove that it's okay to return concurrent
515    /// // references to `packet` and `rest`.
516    /// let (packet, rest) = split.via_runtime_check().unwrap();
517    ///
518    /// assert_eq!(packet.length, 4);
519    /// assert_eq!(packet.body, [0, 1, 0, 2]);
520    /// assert_eq!(rest, [0, 3, 0, 4]);
521    ///
522    /// // Attempt to split `packet` at `length - 1`.
523    /// let idx = packet.length.get() - 1;
524    /// let split = packet.split_at(idx as usize).unwrap();
525    ///
526    /// // Attempt (and fail) to use a dynamic check to prove that it's okay
527    /// // to return concurrent references to `packet` and `rest`. Note that
528    /// // this is a case of `via_runtime_check` being overly conservative.
529    /// // Although the left and right parts indeed overlap, the `Immutable`
530    /// // bound ensures that concurrently referencing these overlapping
531    /// // parts is sound.
532    /// assert!(split.via_runtime_check().is_err());
533    /// ```
534    ///
535    #[doc = codegen_section!(
536        header = "h5",
537        bench = "split_via_runtime_check",
538        format = "coco",
539        arity = 2,
540        [
541            open
542            @index 1
543            @title "Unsized"
544            @variant "dynamic_size"
545        ],
546        [
547            @index 2
548            @title "Dynamically Padded"
549            @variant "dynamic_padding"
550        ]
551    )]
552    #[must_use = "has no side effects"]
553    #[inline(always)]
554    pub fn via_runtime_check(self) -> Result<(&'a T, &'a [T::Elem]), Self> {
555        match self.into_ptr().via_runtime_check() {
556            Ok((l, r)) => Ok((l.as_ref(), r.as_ref())),
557            Err(s) => Err(s.into_ref()),
558        }
559    }
560
561    /// Unsafely produces the split parts of `self`.
562    ///
563    /// # Safety
564    ///
565    /// If `T` permits interior mutation, the trailing padding bytes of the left
566    /// portion must not overlap the right portion. For some dynamically sized
567    /// types, the padding that appears after the trailing slice field [is a
568    /// dynamic function of the trailing slice
569    /// length](KnownLayout#slice-dst-layout). Thus, for some types, this
570    /// condition is dependent on the length of the left portion.
571    ///
572    #[doc = codegen_section!(
573        header = "h5",
574        bench = "split_via_unchecked",
575        format = "coco",
576        arity = 2,
577        [
578            open
579            @index 1
580            @title "Unsized"
581            @variant "dynamic_size"
582        ],
583        [
584            @index 2
585            @title "Dynamically Padded"
586            @variant "dynamic_padding"
587        ]
588    )]
589    #[must_use = "has no side effects"]
590    #[inline(always)]
591    pub unsafe fn via_unchecked(self) -> (&'a T, &'a [T::Elem]) {
592        // SAFETY: The aliasing of `self.into_ptr()` is not `Exclusive`, but the
593        // caller has promised that if `T` permits interior mutation then the
594        // left and right portions of `self` split at `l_len` do not overlap.
595        let (l, r) = unsafe { self.into_ptr().via_unchecked() };
596        (l.as_ref(), r.as_ref())
597    }
598}
599
600impl<'a, T> Split<&'a mut T>
601where
602    T: ?Sized + SplitAt,
603{
604    #[inline(always)]
605    fn into_ptr(self) -> Split<Ptr<'a, T, (Exclusive, Aligned, Valid)>> {
606        let source = Ptr::from_mut(self.source);
607        // SAFETY: `Ptr::from_mut(self.source)` points to exactly `self.source`,
608        // and thus maintains the invariants of `self` with respect to `l_len`.
609        unsafe { Split::new(source, self.l_len) }
610    }
611
612    /// Produces the split parts of `self`, using [`IntoBytes`] to ensure that
613    /// it is sound to have concurrent references to both parts.
614    ///
615    /// # Examples
616    ///
617    /// ```
618    /// use zerocopy::{SplitAt, FromBytes};
619    /// # use zerocopy_derive::*;
620    ///
621    /// #[derive(SplitAt, FromBytes, KnownLayout, IntoBytes)]
622    /// #[repr(C)]
623    /// struct Packet<B: ?Sized> {
624    ///     length: u8,
625    ///     body: B,
626    /// }
627    ///
628    /// // These bytes encode a `Packet`.
629    /// let mut bytes = &mut [4, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
630    ///
631    /// let packet = Packet::<[u8]>::mut_from_bytes(bytes).unwrap();
632    ///
633    /// assert_eq!(packet.length, 4);
634    /// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
635    ///
636    /// {
637    ///     // Attempt to split `packet` at `length`.
638    ///     let split = packet.split_at_mut(packet.length as usize).unwrap();
639    ///
640    ///     // Use the `IntoBytes` bound on `Packet` to prove that it's okay to
641    ///     // return concurrent references to `packet` and `rest`.
642    ///     let (packet, rest) = split.via_into_bytes();
643    ///
644    ///     assert_eq!(packet.length, 4);
645    ///     assert_eq!(packet.body, [1, 2, 3, 4]);
646    ///     assert_eq!(rest, [5, 6, 7, 8, 9]);
647    ///
648    ///     rest.fill(0);
649    /// }
650    ///
651    /// assert_eq!(packet.length, 4);
652    /// assert_eq!(packet.body, [1, 2, 3, 4, 0, 0, 0, 0, 0]);
653    /// ```
654    ///
655    /// # Code Generation
656    ///
657    /// See [`Split::via_immutable`](#method.split_via_immutable.codegen).
658    #[must_use = "has no side effects"]
659    #[inline(always)]
660    pub fn via_into_bytes(self) -> (&'a mut T, &'a mut [T::Elem])
661    where
662        T: IntoBytes,
663    {
664        let (l, r) = self.into_ptr().via_into_bytes();
665        (l.as_mut(), r.as_mut())
666    }
667
668    /// Produces the split parts of `self`, using [`Unaligned`] to ensure that
669    /// it is sound to have concurrent references to both parts.
670    ///
671    /// # Examples
672    ///
673    /// ```
674    /// use zerocopy::{SplitAt, FromBytes};
675    /// # use zerocopy_derive::*;
676    ///
677    /// #[derive(SplitAt, FromBytes, KnownLayout, IntoBytes, Unaligned)]
678    /// #[repr(C)]
679    /// struct Packet<B: ?Sized> {
680    ///     length: u8,
681    ///     body: B,
682    /// }
683    ///
684    /// // These bytes encode a `Packet`.
685    /// let mut bytes = &mut [4, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
686    ///
687    /// let packet = Packet::<[u8]>::mut_from_bytes(bytes).unwrap();
688    ///
689    /// assert_eq!(packet.length, 4);
690    /// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
691    ///
692    /// {
693    ///     // Attempt to split `packet` at `length`.
694    ///     let split = packet.split_at_mut(packet.length as usize).unwrap();
695    ///
696    ///     // Use the `Unaligned` bound on `Packet` to prove that it's okay to
697    ///     // return concurrent references to `packet` and `rest`.
698    ///     let (packet, rest) = split.via_unaligned();
699    ///
700    ///     assert_eq!(packet.length, 4);
701    ///     assert_eq!(packet.body, [1, 2, 3, 4]);
702    ///     assert_eq!(rest, [5, 6, 7, 8, 9]);
703    ///
704    ///     rest.fill(0);
705    /// }
706    ///
707    /// assert_eq!(packet.length, 4);
708    /// assert_eq!(packet.body, [1, 2, 3, 4, 0, 0, 0, 0, 0]);
709    /// ```
710    ///
711    /// # Code Generation
712    ///
713    /// See [`Split::via_immutable`](#method.split_via_immutable.codegen).
714    #[must_use = "has no side effects"]
715    #[inline(always)]
716    pub fn via_unaligned(self) -> (&'a mut T, &'a mut [T::Elem])
717    where
718        T: Unaligned,
719    {
720        let (l, r) = self.into_ptr().via_unaligned();
721        (l.as_mut(), r.as_mut())
722    }
723
724    /// Produces the split parts of `self`, using a dynamic check to ensure that
725    /// it is sound to have concurrent references to both parts. You should
726    /// prefer using [`Self::via_into_bytes`] or [`Self::via_unaligned`], which
727    /// have no runtime cost.
728    ///
729    /// # Examples
730    ///
731    /// ```
732    /// use zerocopy::{SplitAt, FromBytes};
733    /// # use zerocopy_derive::*;
734    ///
735    /// #[derive(SplitAt, FromBytes, KnownLayout, IntoBytes, Debug)]
736    /// #[repr(C)]
737    /// struct Packet<B: ?Sized> {
738    ///     length: u8,
739    ///     body: B,
740    /// }
741    ///
742    /// // These bytes encode a `Packet`.
743    /// let mut bytes = &mut [4, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
744    ///
745    /// let packet = Packet::<[u8]>::mut_from_bytes(bytes).unwrap();
746    ///
747    /// assert_eq!(packet.length, 4);
748    /// assert_eq!(packet.body, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
749    ///
750    /// {
751    ///     // Attempt to split `packet` at `length`.
752    ///     let split = packet.split_at_mut(packet.length as usize).unwrap();
753    ///
754    ///     // Use a dynamic check to prove that it's okay to return concurrent
755    ///     // references to `packet` and `rest`.
756    ///     let (packet, rest) = split.via_runtime_check().unwrap();
757    ///
758    ///     assert_eq!(packet.length, 4);
759    ///     assert_eq!(packet.body, [1, 2, 3, 4]);
760    ///     assert_eq!(rest, [5, 6, 7, 8, 9]);
761    ///
762    ///     rest.fill(0);
763    /// }
764    ///
765    /// assert_eq!(packet.length, 4);
766    /// assert_eq!(packet.body, [1, 2, 3, 4, 0, 0, 0, 0, 0]);
767    /// ```
768    ///
769    /// # Code Generation
770    ///
771    /// See [`Split::via_runtime_check`](#method.split_via_runtime_check.codegen).
772    #[must_use = "has no side effects"]
773    #[inline(always)]
774    pub fn via_runtime_check(self) -> Result<(&'a mut T, &'a mut [T::Elem]), Self> {
775        match self.into_ptr().via_runtime_check() {
776            Ok((l, r)) => Ok((l.as_mut(), r.as_mut())),
777            Err(s) => Err(s.into_mut()),
778        }
779    }
780
781    /// Unsafely produces the split parts of `self`.
782    ///
783    /// # Safety
784    ///
785    /// The trailing padding bytes of the left portion must not overlap the
786    /// right portion. For some dynamically sized types, the padding that
787    /// appears after the trailing slice field [is a dynamic function of the
788    /// trailing slice length](KnownLayout#slice-dst-layout). Thus, for some
789    /// types, this condition is dependent on the length of the left portion.
790    ///
791    /// # Code Generation
792    ///
793    /// See [`Split::via_unchecked`](#method.split_via_unchecked.codegen).
794    #[must_use = "has no side effects"]
795    #[inline(always)]
796    pub unsafe fn via_unchecked(self) -> (&'a mut T, &'a mut [T::Elem]) {
797        // SAFETY: The aliasing of `self.into_ptr()` is `Exclusive`, and the
798        // caller has promised that the left and right portions of `self` split
799        // at `l_len` do not overlap.
800        let (l, r) = unsafe { self.into_ptr().via_unchecked() };
801        (l.as_mut(), r.as_mut())
802    }
803}
804
805impl<'a, T, I> Split<Ptr<'a, T, I>>
806where
807    T: ?Sized + SplitAt,
808    I: Invariants<Alignment = Aligned, Validity = Valid>,
809{
810    fn into_ref(self) -> Split<&'a T>
811    where
812        I: Invariants<Aliasing = Shared>,
813    {
814        // SAFETY: `self.source.as_ref()` points to exactly the same referent as
815        // `self.source` and thus maintains the invariants of `self` with
816        // respect to `l_len`.
817        unsafe { Split::new(self.source.as_ref(), self.l_len) }
818    }
819
820    fn into_mut(self) -> Split<&'a mut T>
821    where
822        I: Invariants<Aliasing = Exclusive>,
823    {
824        // SAFETY: `self.source.as_mut()` points to exactly the same referent as
825        // `self.source` and thus maintains the invariants of `self` with
826        // respect to `l_len`.
827        unsafe { Split::new(self.source.unify_invariants().as_mut(), self.l_len) }
828    }
829
830    /// Produces the length of `self`'s left part.
831    #[inline(always)]
832    fn l_len(&self) -> MetadataOf<T> {
833        // SAFETY: By invariant on `Split`, `self.l_len` is not greater than the
834        // length of `self.source`.
835        unsafe { MetadataOf::<T>::new_unchecked(self.l_len) }
836    }
837
838    /// Produces the split parts of `self`, using [`Immutable`] to ensure that
839    /// it is sound to have concurrent references to both parts.
840    #[inline(always)]
841    fn via_immutable(self) -> (Ptr<'a, T, I>, Ptr<'a, [T::Elem], I>)
842    where
843        T: Immutable,
844        I: Invariants<Aliasing = Shared>,
845    {
846        // SAFETY: `Aliasing = Shared` and `T: Immutable`.
847        unsafe { self.via_unchecked() }
848    }
849
850    /// Produces the split parts of `self`, using [`IntoBytes`] to ensure that
851    /// it is sound to have concurrent references to both parts.
852    #[inline(always)]
853    fn via_into_bytes(self) -> (Ptr<'a, T, I>, Ptr<'a, [T::Elem], I>)
854    where
855        T: IntoBytes,
856    {
857        // SAFETY: By `T: IntoBytes`, `T` has no padding for any length.
858        // Consequently, `T` can be split into non-overlapping parts at any
859        // index.
860        unsafe { self.via_unchecked() }
861    }
862
863    /// Produces the split parts of `self`, using [`Unaligned`] to ensure that
864    /// it is sound to have concurrent references to both parts.
865    #[inline(always)]
866    fn via_unaligned(self) -> (Ptr<'a, T, I>, Ptr<'a, [T::Elem], I>)
867    where
868        T: Unaligned,
869    {
870        // SAFETY: By `T: SplitAt + Unaligned`, `T` is either a slice or a
871        // `repr(C)` or `repr(transparent)` slice DST that is well-aligned at
872        // any address and length. If `T` is a slice DST with alignment 1,
873        // `repr(C)` or `repr(transparent)` ensures that no padding is placed
874        // after the final element of the trailing slice. Consequently, `T` can
875        // be split into strictly non-overlapping parts any any index.
876        unsafe { self.via_unchecked() }
877    }
878
879    /// Produces the split parts of `self`, using a dynamic check to ensure that
880    /// it is sound to have concurrent references to both parts. You should
881    /// prefer using [`Self::via_immutable`], [`Self::via_into_bytes`], or
882    /// [`Self::via_unaligned`], which have no runtime cost.
883    #[inline(always)]
884    fn via_runtime_check(self) -> Result<(Ptr<'a, T, I>, Ptr<'a, [T::Elem], I>), Self> {
885        let l_len = self.l_len();
886        // FIXME(#1290): Once we require `KnownLayout` on all fields, add an
887        // `IS_IMMUTABLE` associated const, and add `T::IS_IMMUTABLE ||` to the
888        // below check.
889        if l_len.padding_needed_for() == 0 {
890            // SAFETY: By `T: SplitAt`, `T` is either `[T]`, or a `repr(C)` or
891            // `repr(transparent)` slice DST, for which the trailing padding
892            // needed to accommodate `l_len` trailing elements is
893            // `l_len.padding_needed_for()`. If no trailing padding is required,
894            // the left and right parts are strictly non-overlapping.
895            Ok(unsafe { self.via_unchecked() })
896        } else {
897            Err(self)
898        }
899    }
900
901    /// Unsafely produces the split parts of `self`.
902    ///
903    /// # Safety
904    ///
905    /// The caller promises that if `I::Aliasing` is [`Exclusive`] or `T`
906    /// permits interior mutation, then `l_len.padding_needed_for() == 0`.
907    #[inline(always)]
908    unsafe fn via_unchecked(self) -> (Ptr<'a, T, I>, Ptr<'a, [T::Elem], I>) {
909        let l_len = self.l_len();
910        let inner = self.source.as_inner();
911
912        // SAFETY: By invariant on `Self::l_len`, `l_len` is not greater than
913        // the length of `inner`'s trailing slice.
914        let (left, right) = unsafe { inner.split_at_unchecked(l_len) };
915
916        // Lemma 0: `left` and `right` conform to the aliasing invariant
917        // `I::Aliasing`. Proof: If `I::Aliasing` is `Exclusive` or `T` permits
918        // interior mutation, the caller promises that `l_len.padding_needed_for()
919        // == 0`. Consequently, by post-condition on `PtrInner::split_at_unchecked`,
920        // there is no trailing padding after `left`'s final element that would
921        // overlap into `right`. If `I::Aliasing` is shared and `T` forbids interior
922        // mutation, then overlap between their referents is permissible.
923
924        // SAFETY:
925        // 0. `left` conforms to the aliasing invariant of `I::Aliasing`, by Lemma 0.
926        // 1. `left` conforms to the alignment invariant of `I::Alignment, because
927        //    the referents of `left` and `Self` have the same address and type
928        //    (and, thus, alignment requirement).
929        // 2. `left` conforms to the validity invariant of `I::Validity`, neither
930        //    the type nor bytes of `left`'s referent have been changed.
931        let left = unsafe { Ptr::from_inner(left) };
932
933        // SAFETY:
934        // 0. `right` conforms to the aliasing invariant of `I::Aliasing`, by Lemma
935        //    0.
936        // 1. `right` conforms to the alignment invariant of `I::Alignment, because
937        //    if `ptr` with `I::Alignment = Aligned`, then by invariant on `T:
938        //    SplitAt`, the trailing slice of `ptr` (from which `right` is derived)
939        //    will also be well-aligned.
940        // 2. `right` conforms to the validity invariant of `I::Validity`,
941        //    because `right: [T::Elem]` is derived from the trailing slice of
942        //    `ptr`, which, by contract on `T: SplitAt::Elem`, has type
943        //    `[T::Elem]`. The `left` part cannot be used to invalidate `right`,
944        //    because the caller promises that if `I::Aliasing` is `Exclusive`
945        //    or `T` permits interior mutation, then `l_len.padding_needed_for()
946        //    == 0` and thus the parts will be non-overlapping.
947        let right = unsafe { Ptr::from_inner(right) };
948
949        (left, right)
950    }
951}
952
953#[cfg(test)]
954mod tests {
955    #[cfg(feature = "derive")]
956    #[test]
957    fn test_split_at() {
958        use crate::{FromBytes, Immutable, IntoBytes, KnownLayout, SplitAt};
959
960        #[derive(FromBytes, KnownLayout, SplitAt, IntoBytes, Immutable, Debug)]
961        #[repr(C)]
962        struct SliceDst<const OFFSET: usize> {
963            prefix: [u8; OFFSET],
964            trailing: [u8],
965        }
966
967        #[allow(clippy::as_conversions)]
968        fn test_split_at<const OFFSET: usize, const BUFFER_SIZE: usize>() {
969            // Test `split_at`
970            let n: usize = BUFFER_SIZE - OFFSET;
971            let arr = [1; BUFFER_SIZE];
972            let dst = SliceDst::<OFFSET>::ref_from_bytes(&arr[..]).unwrap();
973            for i in 0..=n {
974                let (l, r) = dst.split_at(i).unwrap().via_runtime_check().unwrap();
975                let l_sum: u8 = l.trailing.iter().sum();
976                let r_sum: u8 = r.iter().sum();
977                assert_eq!(l_sum, i as u8);
978                assert_eq!(r_sum, (n - i) as u8);
979                assert_eq!(l_sum + r_sum, n as u8);
980            }
981
982            // Test `split_at_mut`
983            let n: usize = BUFFER_SIZE - OFFSET;
984            let mut arr = [1; BUFFER_SIZE];
985            let dst = SliceDst::<OFFSET>::mut_from_bytes(&mut arr[..]).unwrap();
986            for i in 0..=n {
987                let (l, r) = dst.split_at_mut(i).unwrap().via_runtime_check().unwrap();
988                let l_sum: u8 = l.trailing.iter().sum();
989                let r_sum: u8 = r.iter().sum();
990                assert_eq!(l_sum, i as u8);
991                assert_eq!(r_sum, (n - i) as u8);
992                assert_eq!(l_sum + r_sum, n as u8);
993            }
994        }
995
996        test_split_at::<0, 16>();
997        test_split_at::<1, 17>();
998        test_split_at::<2, 18>();
999    }
1000
1001    #[cfg(feature = "derive")]
1002    #[test]
1003    #[allow(clippy::as_conversions)]
1004    fn test_split_at_overlapping() {
1005        use crate::{FromBytes, Immutable, IntoBytes, KnownLayout, SplitAt};
1006
1007        #[derive(FromBytes, KnownLayout, SplitAt, Immutable)]
1008        #[repr(C, align(2))]
1009        struct SliceDst {
1010            prefix: u8,
1011            trailing: [u8],
1012        }
1013
1014        const N: usize = 16;
1015
1016        let arr = [1u16; N];
1017        let dst = SliceDst::ref_from_bytes(arr.as_bytes()).unwrap();
1018
1019        for i in 0..N {
1020            let split = dst.split_at(i).unwrap().via_runtime_check();
1021            if i % 2 == 1 {
1022                assert!(split.is_ok());
1023            } else {
1024                assert!(split.is_err());
1025            }
1026        }
1027    }
1028    #[test]
1029    fn test_split_at_unchecked() {
1030        use crate::SplitAt;
1031        let mut arr = [1, 2, 3, 4];
1032        let slice = &arr[..];
1033        // SAFETY: 2 <= arr.len() (4)
1034        let split = unsafe { SplitAt::split_at_unchecked(slice, 2) };
1035        // SAFETY: SplitAt::split_at_unchecked guarantees that the split is valid.
1036        let (l, r) = unsafe { split.via_unchecked() };
1037        assert_eq!(l, &[1, 2]);
1038        assert_eq!(r, &[3, 4]);
1039
1040        let slice_mut = &mut arr[..];
1041        // SAFETY: 2 <= arr.len() (4)
1042        let split = unsafe { SplitAt::split_at_mut_unchecked(slice_mut, 2) };
1043        // SAFETY: SplitAt::split_at_mut_unchecked guarantees that the split is valid.
1044        let (l, r) = unsafe { split.via_unchecked() };
1045        assert_eq!(l, &mut [1, 2]);
1046        assert_eq!(r, &mut [3, 4]);
1047    }
1048
1049    #[test]
1050    fn test_split_at_via_methods() {
1051        use crate::{FromBytes, Immutable, IntoBytes, KnownLayout, SplitAt};
1052        #[derive(FromBytes, KnownLayout, SplitAt, IntoBytes, Immutable, Debug)]
1053        #[repr(C)]
1054        struct Packet {
1055            length: u8,
1056            body: [u8],
1057        }
1058
1059        let arr = [1, 2, 3, 4];
1060        let packet = Packet::ref_from_bytes(&arr[..]).unwrap();
1061
1062        let split1 = packet.split_at(2).unwrap();
1063        let (l, r) = split1.via_immutable();
1064        assert_eq!(l.length, 1);
1065        assert_eq!(r, &[4]);
1066
1067        let split2 = packet.split_at(2).unwrap();
1068        let (l, r) = split2.via_into_bytes();
1069        assert_eq!(l.length, 1);
1070        assert_eq!(r, &[4]);
1071    }
1072    #[test]
1073    fn test_split_at_via_unaligned() {
1074        use crate::{FromBytes, Immutable, IntoBytes, KnownLayout, SplitAt, Unaligned};
1075        #[derive(FromBytes, KnownLayout, SplitAt, IntoBytes, Immutable, Unaligned)]
1076        #[repr(C)]
1077        struct Packet {
1078            length: u8,
1079            body: [u8],
1080        }
1081
1082        let arr = [1, 2, 3, 4];
1083        let packet = Packet::ref_from_bytes(&arr[..]).unwrap();
1084
1085        let split = packet.split_at(2).unwrap();
1086        let (l, r) = split.via_unaligned();
1087        assert_eq!(l.length, 1);
1088        assert_eq!(r, &[4]);
1089    }
1090}