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§
Provided Methods§
Sourceunsafe fn split_at_unchecked(&self, l_len: usize) -> Split<&Self>
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
Sourcefn split_at(&self, l_len: usize) -> Option<Split<&Self>>
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
Sourceunsafe fn split_at_mut_unchecked(&mut self, l_len: usize) -> Split<&mut Self>
unsafe fn split_at_mut_unchecked(&mut self, l_len: usize) -> Split<&mut Self>
Sourcefn split_at_mut(&mut self, l_len: usize) -> Option<Split<&mut Self>>
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.