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}