Skip to main content

SplitAt

Trait SplitAt 

Source
pub unsafe trait SplitAt: KnownLayout<PointerMetadata = usize> {
    type Elem;

    // Provided methods
    unsafe fn split_at_unchecked(&self, l_len: usize) -> Split<&Self> { ... }
    fn split_at(&self, l_len: usize) -> Option<Split<&Self>> { ... }
    unsafe fn split_at_mut_unchecked(
        &mut self,
        l_len: usize,
    ) -> Split<&mut Self> { ... }
    fn split_at_mut(&mut self, l_len: usize) -> Option<Split<&mut Self>> { ... }
}
Expand description

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”).

§Implementation

Do not implement this trait yourself! Instead, use #[derive(SplitAt)]; e.g.:

#[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.

§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.

Required Associated Types§

Source

type Elem

The element type of the trailing slice.

Provided Methods§

Source

unsafe fn split_at_unchecked(&self, l_len: usize) -> Split<&Self>

Unsafely splits self in two.

§Safety

The caller promises that l_len is not greater than the length of self’s trailing slice.

§ Code Generation

This abstraction is safe and cheap, but does not necessarily have zero runtime cost. The codegen you experience in practice will depend on optimization level, the layout of the destination type, and what the compiler can prove about the source.

The below examples illustrate typical codegen for increasingly complex types:

Unsized
Format
use zerocopy_derive::*;

// The only valid value of this type are the bytes `0xC0C0`.
#[derive(TryFromBytes, KnownLayout, Immutable, IntoBytes)]
#[repr(u16)]
pub enum C0C0 {
   _XC0C0 = 0xC0C0,
}

macro_rules! define_packet {
   ($name: ident, $trait: ident, $leading_field: ty) => {
       #[derive($trait, KnownLayout, Immutable, IntoBytes, SplitAt)]
       #[repr(C, align(2))]
       pub struct $name {
           magic_number: $leading_field,
           mug_size: u8,
           temperature: u8,
           marshmallows: [[u8; 2]],
       }
   };
}

/// Packet begins with bytes 0xC0C0.
define_packet!(CocoPacket, TryFromBytes, C0C0);

/// Packet begins with any two bytes.
define_packet!(LocoPacket, FromBytes, [u8; 2]);
Benchmark
use zerocopy::*;

#[path = "formats/coco_dynamic_size.rs"]
mod format;

#[unsafe(no_mangle)]
unsafe fn bench_split_at_unchecked_dynamic_size(
   source: &format::CocoPacket,
   len: usize,
) -> Split<&format::CocoPacket> {
   unsafe { source.split_at_unchecked(len) }
}
Assembly
bench_split_at_unchecked_dynamic_size:
   mov rax, rdi
   mov qword ptr [rdi], rsi
   mov qword ptr [rdi + 8], rdx
   mov qword ptr [rdi + 16], rcx
   ret
Machine Code Analysis
Iterations:        100
Instructions:      500
Total Cycles:      303
Total uOps:        500

Dispatch Width:    4
uOps Per Cycle:    1.65
IPC:               1.65
Block RThroughput: 3.0


Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)

[1]    [2]    [3]    [4]    [5]    [6]    Instructions:
1      1     0.33                        mov	rax, rdi
1      1     1.00           *            mov	qword ptr [rdi], rsi
1      1     1.00           *            mov	qword ptr [rdi + 8], rdx
1      1     1.00           *            mov	qword ptr [rdi + 16], rcx
1      1     1.00                  U     ret


Resources:
[0]   - SBDivider
[1]   - SBFPDivider
[2]   - SBPort0
[3]   - SBPort1
[4]   - SBPort4
[5]   - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23


Resource pressure per iteration:
[0]    [1]    [2]    [3]    [4]    [5]    [6.0]  [6.1]  
-      -     0.49   0.50   3.00   1.01   1.50   1.50   

Resource pressure by instruction:
[0]    [1]    [2]    [3]    [4]    [5]    [6.0]  [6.1]  Instructions:
-      -     0.49   0.50    -     0.01    -      -     mov	rax, rdi
-      -      -      -     1.00    -     0.50   0.50   mov	qword ptr [rdi], rsi
-      -      -      -     1.00    -     0.50   0.50   mov	qword ptr [rdi + 8], rdx
-      -      -      -     1.00    -     0.50   0.50   mov	qword ptr [rdi + 16], rcx
-      -      -      -      -     1.00    -      -     ret
Dynamically Padded
Format
use zerocopy_derive::*;

// The only valid value of this type are the bytes `0xC0C0`.
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(u16)]
pub enum C0C0 {
   _XC0C0 = 0xC0C0,
}

#[derive(FromBytes, KnownLayout, Immutable, SplitAt)]
#[repr(C, align(4))]
pub struct Packet<Magic> {
   magic_number: Magic,
   milk: u8,
   mug_size: u8,
   temperature: [u8; 5],
   marshmallows: [[u8; 3]],
}

/// A packet begining with the magic number `0xC0C0`.
pub type CocoPacket = Packet<C0C0>;

/// A packet beginning with any two initialized bytes.
pub type LocoPacket = Packet<[u8; 2]>;
Benchmark
use zerocopy::*;

#[path = "formats/coco_dynamic_padding.rs"]
mod format;

#[unsafe(no_mangle)]
unsafe fn bench_split_at_unchecked_dynamic_padding(
   source: &format::CocoPacket,
   len: usize,
) -> Split<&format::CocoPacket> {
   unsafe { source.split_at_unchecked(len) }
}
Assembly
bench_split_at_unchecked_dynamic_padding:
   mov rax, rdi
   mov qword ptr [rdi], rsi
   mov qword ptr [rdi + 8], rdx
   mov qword ptr [rdi + 16], rcx
   ret
Machine Code Analysis
Iterations:        100
Instructions:      500
Total Cycles:      303
Total uOps:        500

Dispatch Width:    4
uOps Per Cycle:    1.65
IPC:               1.65
Block RThroughput: 3.0


Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)

[1]    [2]    [3]    [4]    [5]    [6]    Instructions:
1      1     0.33                        mov	rax, rdi
1      1     1.00           *            mov	qword ptr [rdi], rsi
1      1     1.00           *            mov	qword ptr [rdi + 8], rdx
1      1     1.00           *            mov	qword ptr [rdi + 16], rcx
1      1     1.00                  U     ret


Resources:
[0]   - SBDivider
[1]   - SBFPDivider
[2]   - SBPort0
[3]   - SBPort1
[4]   - SBPort4
[5]   - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23


Resource pressure per iteration:
[0]    [1]    [2]    [3]    [4]    [5]    [6.0]  [6.1]  
-      -     0.49   0.50   3.00   1.01   1.50   1.50   

Resource pressure by instruction:
[0]    [1]    [2]    [3]    [4]    [5]    [6.0]  [6.1]  Instructions:
-      -     0.49   0.50    -     0.01    -      -     mov	rax, rdi
-      -      -      -     1.00    -     0.50   0.50   mov	qword ptr [rdi], rsi
-      -      -      -     1.00    -     0.50   0.50   mov	qword ptr [rdi + 8], rdx
-      -      -      -     1.00    -     0.50   0.50   mov	qword ptr [rdi + 16], rcx
-      -      -      -      -     1.00    -      -     ret
Source

fn split_at(&self, l_len: usize) -> Option<Split<&Self>>

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};

#[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]);
§ Code Generation

This abstraction is safe and cheap, but does not necessarily have zero runtime cost. The codegen you experience in practice will depend on optimization level, the layout of the destination type, and what the compiler can prove about the source.

The below examples illustrate typical codegen for increasingly complex types:

Unsized
Format
use zerocopy_derive::*;

// The only valid value of this type are the bytes `0xC0C0`.
#[derive(TryFromBytes, KnownLayout, Immutable, IntoBytes)]
#[repr(u16)]
pub enum C0C0 {
   _XC0C0 = 0xC0C0,
}

macro_rules! define_packet {
   ($name: ident, $trait: ident, $leading_field: ty) => {
       #[derive($trait, KnownLayout, Immutable, IntoBytes, SplitAt)]
       #[repr(C, align(2))]
       pub struct $name {
           magic_number: $leading_field,
           mug_size: u8,
           temperature: u8,
           marshmallows: [[u8; 2]],
       }
   };
}

/// Packet begins with bytes 0xC0C0.
define_packet!(CocoPacket, TryFromBytes, C0C0);

/// Packet begins with any two bytes.
define_packet!(LocoPacket, FromBytes, [u8; 2]);
Benchmark
use zerocopy::*;

#[path = "formats/coco_dynamic_size.rs"]
mod format;

#[unsafe(no_mangle)]
fn bench_split_at_dynamic_size(
   source: &format::CocoPacket,
   len: usize,
) -> Option<Split<&format::CocoPacket>> {
   source.split_at(len)
}
Assembly
bench_split_at_dynamic_size:
   mov rax, rdi
   cmp rcx, rdx
   jbe .LBB5_2
   xor esi, esi
   mov qword ptr [rax], rsi
   ret
.LBB5_2:
   mov qword ptr [rax + 8], rdx
   mov qword ptr [rax + 16], rcx
   mov qword ptr [rax], rsi
   ret
Machine Code Analysis
Iterations:        100
Instructions:      1000
Total Cycles:      404
Total uOps:        1000

Dispatch Width:    4
uOps Per Cycle:    2.48
IPC:               2.48
Block RThroughput: 4.0


Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)

[1]    [2]    [3]    [4]    [5]    [6]    Instructions:
1      1     0.33                        mov	rax, rdi
1      1     0.33                        cmp	rcx, rdx
1      1     1.00                        jbe	.LBB5_2
1      0     0.25                        xor	esi, esi
1      1     1.00           *            mov	qword ptr [rax], rsi
1      1     1.00                  U     ret
1      1     1.00           *            mov	qword ptr [rax + 8], rdx
1      1     1.00           *            mov	qword ptr [rax + 16], rcx
1      1     1.00           *            mov	qword ptr [rax], rsi
1      1     1.00                  U     ret


Resources:
[0]   - SBDivider
[1]   - SBFPDivider
[2]   - SBPort0
[3]   - SBPort1
[4]   - SBPort4
[5]   - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23


Resource pressure per iteration:
[0]    [1]    [2]    [3]    [4]    [5]    [6.0]  [6.1]  
-      -     0.99   1.00   4.00   3.01   2.00   2.00   

Resource pressure by instruction:
[0]    [1]    [2]    [3]    [4]    [5]    [6.0]  [6.1]  Instructions:
-      -     0.99    -      -     0.01    -      -     mov	rax, rdi
-      -      -     1.00    -      -      -      -     cmp	rcx, rdx
-      -      -      -      -     1.00    -      -     jbe	.LBB5_2
-      -      -      -      -      -      -      -     xor	esi, esi
-      -      -      -     1.00    -      -     1.00   mov	qword ptr [rax], rsi
-      -      -      -      -     1.00    -      -     ret
-      -      -      -     1.00    -     1.00    -     mov	qword ptr [rax + 8], rdx
-      -      -      -     1.00    -      -     1.00   mov	qword ptr [rax + 16], rcx
-      -      -      -     1.00    -     1.00    -     mov	qword ptr [rax], rsi
-      -      -      -      -     1.00    -      -     ret
Dynamically Padded
Format
use zerocopy_derive::*;

// The only valid value of this type are the bytes `0xC0C0`.
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(u16)]
pub enum C0C0 {
   _XC0C0 = 0xC0C0,
}

#[derive(FromBytes, KnownLayout, Immutable, SplitAt)]
#[repr(C, align(4))]
pub struct Packet<Magic> {
   magic_number: Magic,
   milk: u8,
   mug_size: u8,
   temperature: [u8; 5],
   marshmallows: [[u8; 3]],
}

/// A packet begining with the magic number `0xC0C0`.
pub type CocoPacket = Packet<C0C0>;

/// A packet beginning with any two initialized bytes.
pub type LocoPacket = Packet<[u8; 2]>;
Benchmark
use zerocopy::*;

#[path = "formats/coco_dynamic_padding.rs"]
mod format;

#[unsafe(no_mangle)]
fn bench_split_at_dynamic_padding(
   source: &format::CocoPacket,
   len: usize,
) -> Option<Split<&format::CocoPacket>> {
   source.split_at(len)
}
Assembly
bench_split_at_dynamic_padding:
   mov rax, rdi
   cmp rcx, rdx
   jbe .LBB5_2
   xor esi, esi
   mov qword ptr [rax], rsi
   ret
.LBB5_2:
   mov qword ptr [rax + 8], rdx
   mov qword ptr [rax + 16], rcx
   mov qword ptr [rax], rsi
   ret
Machine Code Analysis
Iterations:        100
Instructions:      1000
Total Cycles:      404
Total uOps:        1000

Dispatch Width:    4
uOps Per Cycle:    2.48
IPC:               2.48
Block RThroughput: 4.0


Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)

[1]    [2]    [3]    [4]    [5]    [6]    Instructions:
1      1     0.33                        mov	rax, rdi
1      1     0.33                        cmp	rcx, rdx
1      1     1.00                        jbe	.LBB5_2
1      0     0.25                        xor	esi, esi
1      1     1.00           *            mov	qword ptr [rax], rsi
1      1     1.00                  U     ret
1      1     1.00           *            mov	qword ptr [rax + 8], rdx
1      1     1.00           *            mov	qword ptr [rax + 16], rcx
1      1     1.00           *            mov	qword ptr [rax], rsi
1      1     1.00                  U     ret


Resources:
[0]   - SBDivider
[1]   - SBFPDivider
[2]   - SBPort0
[3]   - SBPort1
[4]   - SBPort4
[5]   - SBPort5
[6.0] - SBPort23
[6.1] - SBPort23


Resource pressure per iteration:
[0]    [1]    [2]    [3]    [4]    [5]    [6.0]  [6.1]  
-      -     0.99   1.00   4.00   3.01   2.00   2.00   

Resource pressure by instruction:
[0]    [1]    [2]    [3]    [4]    [5]    [6.0]  [6.1]  Instructions:
-      -     0.99    -      -     0.01    -      -     mov	rax, rdi
-      -      -     1.00    -      -      -      -     cmp	rcx, rdx
-      -      -      -      -     1.00    -      -     jbe	.LBB5_2
-      -      -      -      -      -      -      -     xor	esi, esi
-      -      -      -     1.00    -      -     1.00   mov	qword ptr [rax], rsi
-      -      -      -      -     1.00    -      -     ret
-      -      -      -     1.00    -     1.00    -     mov	qword ptr [rax + 8], rdx
-      -      -      -     1.00    -      -     1.00   mov	qword ptr [rax + 16], rcx
-      -      -      -     1.00    -     1.00    -     mov	qword ptr [rax], rsi
-      -      -      -      -     1.00    -      -     ret
Source

unsafe fn split_at_mut_unchecked(&mut self, l_len: usize) -> Split<&mut Self>

Unsafely splits self in two.

§Safety

The caller promises that l_len is not greater than the length of self’s trailing slice.

§ Code Generation

See SplitAt::split_at_unchecked.

Source

fn split_at_mut(&mut self, l_len: usize) -> Option<Split<&mut Self>>

Attempts to split self in two.

Returns None if l_len is greater than the length of self’s trailing slice, or if the given l_len would result in the trailing padding of the left portion overlapping the right portion.

§Examples
use zerocopy::{SplitAt, FromBytes};

#[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 SplitAt::split_at.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementations on Foreign Types§

Source§

impl<T> SplitAt for [T]

Source§

type Elem = T

Implementors§