Trait SplitAt
pub unsafe trait SplitAt: KnownLayout<PointerMetadata = usize>
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.:
# use ;
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.
Associated Types
type Elem;The element type of the trailing slice.
Provided Methods
unsafe fn split_at_unchecked(&self, l_len: usize) -> Split<&Self>Unsafely splits
selfin two.Safety
The caller promises that
l_lenis not greater than the length ofself'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 retMachine 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 - - retDynamically 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 retMachine 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 - - retfn split_at(&self, l_len: usize) -> Option<Split<&Self>>Attempts to split
selfin two.Returns
Noneifl_lenis greater than the length ofself's trailing slice.Examples
use ; # use *; // These bytes encode a `Packet`. let bytes = &; let packet = ref_from_bytes.unwrap; assert_eq!; assert_eq!; // Attempt to split `packet` at `length`. let split = packet.split_at.unwrap; // Use the `Immutable` bound on `Packet` to prove that it's okay to // return concurrent references to `packet` and `rest`. let = split.via_immutable; assert_eq!; assert_eq!; assert_eq!;§ 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 retMachine 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 - - retDynamically 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 retMachine 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 - - retunsafe fn split_at_mut_unchecked(&mut self, l_len: usize) -> Split<&mut Self>Unsafely splits
selfin two.Safety
The caller promises that
l_lenis not greater than the length ofself's trailing slice.§ Code Generation
fn split_at_mut(&mut self, l_len: usize) -> Option<Split<&mut Self>>Attempts to split
selfin two.Returns
Noneifl_lenis greater than the length ofself's trailing slice, or if the givenl_lenwould result in the trailing padding of the left portion overlapping the right portion.Examples
use ; # use *; // These bytes encode a `Packet`. let mut bytes = &mut ; let packet = mut_from_bytes.unwrap; assert_eq!; assert_eq!; assert_eq!; assert_eq!;§ Code Generation
See
SplitAt::split_at.
Implementors
impl<T> SplitAt for [T]