pub unsafe trait FromBytes: FromZeros {
Show 15 methods
// Provided methods
fn ref_from_bytes(source: &[u8]) -> Result<&Self, CastError<&[u8], Self>>
where Self: KnownLayout + Immutable { ... }
fn ref_from_prefix(
source: &[u8],
) -> Result<(&Self, &[u8]), CastError<&[u8], Self>>
where Self: KnownLayout + Immutable { ... }
fn ref_from_suffix(
source: &[u8],
) -> Result<(&[u8], &Self), CastError<&[u8], Self>>
where Self: Immutable + KnownLayout { ... }
fn mut_from_bytes(
source: &mut [u8],
) -> Result<&mut Self, CastError<&mut [u8], Self>>
where Self: IntoBytes + KnownLayout { ... }
fn mut_from_prefix(
source: &mut [u8],
) -> Result<(&mut Self, &mut [u8]), CastError<&mut [u8], Self>>
where Self: IntoBytes + KnownLayout { ... }
fn mut_from_suffix(
source: &mut [u8],
) -> Result<(&mut [u8], &mut Self), CastError<&mut [u8], Self>>
where Self: IntoBytes + KnownLayout { ... }
fn ref_from_bytes_with_elems(
source: &[u8],
count: usize,
) -> Result<&Self, CastError<&[u8], Self>>
where Self: KnownLayout<PointerMetadata = usize> + Immutable { ... }
fn ref_from_prefix_with_elems(
source: &[u8],
count: usize,
) -> Result<(&Self, &[u8]), CastError<&[u8], Self>>
where Self: KnownLayout<PointerMetadata = usize> + Immutable { ... }
fn ref_from_suffix_with_elems(
source: &[u8],
count: usize,
) -> Result<(&[u8], &Self), CastError<&[u8], Self>>
where Self: KnownLayout<PointerMetadata = usize> + Immutable { ... }
fn mut_from_bytes_with_elems(
source: &mut [u8],
count: usize,
) -> Result<&mut Self, CastError<&mut [u8], Self>>
where Self: IntoBytes + KnownLayout<PointerMetadata = usize> + Immutable { ... }
fn mut_from_prefix_with_elems(
source: &mut [u8],
count: usize,
) -> Result<(&mut Self, &mut [u8]), CastError<&mut [u8], Self>>
where Self: IntoBytes + KnownLayout<PointerMetadata = usize> { ... }
fn mut_from_suffix_with_elems(
source: &mut [u8],
count: usize,
) -> Result<(&mut [u8], &mut Self), CastError<&mut [u8], Self>>
where Self: IntoBytes + KnownLayout<PointerMetadata = usize> { ... }
fn read_from_bytes(source: &[u8]) -> Result<Self, SizeError<&[u8], Self>>
where Self: Sized { ... }
fn read_from_prefix(
source: &[u8],
) -> Result<(Self, &[u8]), SizeError<&[u8], Self>>
where Self: Sized { ... }
fn read_from_suffix(
source: &[u8],
) -> Result<(&[u8], Self), SizeError<&[u8], Self>>
where Self: Sized { ... }
}Expand description
Types for which any bit pattern is valid.
Any memory region of the appropriate length which contains initialized bytes
can be viewed as any FromBytes type with no runtime overhead. This is
useful for efficiently parsing bytes as structured data.
§Warning: Padding bytes
Note that, when a value is moved or copied, only the non-padding bytes of that value are guaranteed to be preserved. It is unsound to assume that values written to padding bytes are preserved after a move or copy. For example, the following is unsound:
use core::mem::{size_of, transmute};
use zerocopy::FromZeros;
// Assume `Foo` is a type with padding bytes.
#[derive(FromZeros, Default)]
struct Foo {
...
}
let mut foo: Foo = Foo::default();
FromZeros::zero(&mut foo);
// UNSOUND: Although `FromZeros::zero` writes zeros to all bytes of `foo`,
// those writes are not guaranteed to be preserved in padding bytes when
// `foo` is moved, so this may expose padding bytes as `u8`s.
let foo_bytes: [u8; size_of::<Foo>()] = unsafe { transmute(foo) };§Implementation
Do not implement this trait yourself! Instead, use
#[derive(FromBytes)]; e.g.:
#[derive(FromBytes)]
struct MyStruct {
...
}
#[derive(FromBytes)]
#[repr(u8)]
enum MyEnum {
...
}
#[derive(FromBytes, Immutable)]
union MyUnion {
...
}This derive performs a sophisticated, compile-time safety analysis to
determine whether a type is FromBytes.
§Safety
This section describes what is required in order for T: FromBytes, and
what unsafe code may assume of such types. If you don’t plan on implementing
FromBytes manually, and you don’t plan on writing unsafe code that
operates on FromBytes types, then you don’t need to read this section.
If T: FromBytes, then unsafe code may assume that it is sound to produce a
T whose bytes are initialized to any sequence of valid u8s (in other
words, any byte value which is not uninitialized). If a type is marked as
FromBytes which violates this contract, it may cause undefined behavior.
#[derive(FromBytes)] only permits types which satisfy these
requirements.
Provided Methods§
Sourcefn ref_from_bytes(source: &[u8]) -> Result<&Self, CastError<&[u8], Self>>where
Self: KnownLayout + Immutable,
fn ref_from_bytes(source: &[u8]) -> Result<&Self, CastError<&[u8], Self>>where
Self: KnownLayout + Immutable,
Interprets the given source as a &Self.
This method attempts to return a reference to source interpreted as a
Self. If the length of source is not a valid size of
Self, or if source is not appropriately aligned, this
returns Err. If Self: Unaligned, you can
infallibly discard the alignment error.
Self may be a sized type, a slice, or a slice DST.
§Compile-Time Assertions
This method cannot yet be used on unsized types whose dynamically-sized component is zero-sized. Attempting to use this method on such types results in a compile-time assertion error; e.g.:
use zerocopy::*;
#[derive(FromBytes, Immutable, KnownLayout)]
#[repr(C)]
struct ZSTy {
leading_sized: u16,
trailing_dst: [()],
}
let _ = ZSTy::ref_from_bytes(0u16.as_bytes()); // ⚠ Compile Error!§Examples
use zerocopy::FromBytes;
#[derive(FromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct PacketHeader {
src_port: [u8; 2],
dst_port: [u8; 2],
length: [u8; 2],
checksum: [u8; 2],
}
#[derive(FromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct Packet {
header: PacketHeader,
body: [u8],
}
// These bytes encode a `Packet`.
let bytes = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11][..];
let packet = Packet::ref_from_bytes(bytes).unwrap();
assert_eq!(packet.header.src_port, [0, 1]);
assert_eq!(packet.header.dst_port, [2, 3]);
assert_eq!(packet.header.length, [4, 5]);
assert_eq!(packet.header.checksum, [6, 7]);
assert_eq!(packet.body, [8, 9, 10, 11]);§ 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:
Sized
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)]
#[repr(C, align(2))]
pub struct Packet<Magic> {
magic_number: Magic,
mug_size: u8,
temperature: u8,
marshmallows: [u8; 2],
}
/// 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
#[path = "formats/coco_static_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_from_bytes_static_size(source: &[u8]) -> Option<&format::LocoPacket> {
zerocopy::FromBytes::ref_from_bytes(source).ok()
}
Assembly
bench_ref_from_bytes_static_size:
mov ecx, edi
and ecx, 1
xor rsi, 6
xor eax, eax
or rsi, rcx
cmove rax, rdi
ret
Machine Code Analysis
Iterations: 100
Instructions: 700
Total Cycles: 240
Total uOps: 800
Dispatch Width: 4
uOps Per Cycle: 3.33
IPC: 2.92
Block RThroughput: 2.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 ecx, edi
1 1 0.33 and ecx, 1
1 1 0.33 xor rsi, 6
1 0 0.25 xor eax, eax
1 1 0.33 or rsi, rcx
2 2 0.67 cmove rax, rdi
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]
- - 2.33 2.33 - 2.34 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.01 0.98 - 0.01 - - mov ecx, edi
- - 0.02 0.66 - 0.32 - - and ecx, 1
- - 0.33 0.66 - 0.01 - - xor rsi, 6
- - - - - - - - xor eax, eax
- - 0.98 0.02 - - - - or rsi, rcx
- - 0.99 0.01 - 1.00 - - cmove rax, rdi
- - - - - 1.00 - - ret
Unsized
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)]
#[repr(C, align(2))]
pub struct Packet<Magic> {
magic_number: Magic,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
/// 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
#[path = "formats/coco_dynamic_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_from_bytes_dynamic_size(source: &[u8]) -> Option<&format::LocoPacket> {
zerocopy::FromBytes::ref_from_bytes(source).ok()
}
Assembly
bench_ref_from_bytes_dynamic_size:
mov rdx, rsi
cmp rsi, 4
setb al
or al, dil
test al, 1
je .LBB5_2
xor eax, eax
ret
.LBB5_2:
lea rcx, [rdx - 4]
mov rsi, rcx
and rsi, -2
add rsi, 4
shr rcx
xor eax, eax
cmp rdx, rsi
cmove rdx, rcx
cmove rax, rdi
ret
Machine Code Analysis
Iterations: 100
Instructions: 1800
Total Cycles: 704
Total uOps: 2000
Dispatch Width: 4
uOps Per Cycle: 2.84
IPC: 2.56
Block RThroughput: 5.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 rdx, rsi
1 1 0.33 cmp rsi, 4
1 1 0.50 setb al
1 1 0.33 or al, dil
1 1 0.33 test al, 1
1 1 1.00 je .LBB5_2
1 0 0.25 xor eax, eax
1 1 1.00 U ret
1 1 0.50 lea rcx, [rdx - 4]
1 1 0.33 mov rsi, rcx
1 1 0.33 and rsi, -2
1 1 0.33 add rsi, 4
1 1 0.50 shr rcx
1 0 0.25 xor eax, eax
1 1 0.33 cmp rdx, rsi
2 2 0.67 cmove rdx, rcx
2 2 0.67 cmove rax, rdi
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]
- - 5.97 5.98 - 6.05 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.97 0.01 - 0.02 - - mov rdx, rsi
- - 0.01 0.02 - 0.97 - - cmp rsi, 4
- - 0.03 - - 0.97 - - setb al
- - 0.01 0.02 - 0.97 - - or al, dil
- - - 0.98 - 0.02 - - test al, 1
- - - - - 1.00 - - je .LBB5_2
- - - - - - - - xor eax, eax
- - - - - 1.00 - - ret
- - 0.98 0.02 - - - - lea rcx, [rdx - 4]
- - 0.01 0.99 - - - - mov rsi, rcx
- - - 0.98 - 0.02 - - and rsi, -2
- - 0.98 0.01 - 0.01 - - add rsi, 4
- - 0.99 - - 0.01 - - shr rcx
- - - - - - - - xor eax, eax
- - 0.02 0.97 - 0.01 - - cmp rdx, rsi
- - 0.99 0.99 - 0.02 - - cmove rdx, rcx
- - 0.98 0.99 - 0.03 - - cmove rax, rdi
- - - - - 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)]
#[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
#[path = "formats/coco_dynamic_padding.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_from_bytes_dynamic_padding(source: &[u8]) -> Option<&format::LocoPacket> {
zerocopy::FromBytes::ref_from_bytes(source).ok()
}
Assembly
bench_ref_from_bytes_dynamic_padding:
test dil, 3
jne .LBB5_3
movabs rax, 9223372036854775804
and rax, rsi
cmp rax, 9
jb .LBB5_3
add rax, -9
movabs rcx, -6148914691236517205
mul rcx
shr rdx
lea rax, [rdx + 2*rdx]
or rax, 3
add rax, 9
cmp rsi, rax
je .LBB5_4
.LBB5_3:
xor edi, edi
mov rdx, rsi
.LBB5_4:
mov rax, rdi
ret
Machine Code Analysis
Iterations: 100
Instructions: 1900
Total Cycles: 645
Total uOps: 2000
Dispatch Width: 4
uOps Per Cycle: 3.10
IPC: 2.95
Block RThroughput: 5.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 test dil, 3
1 1 1.00 jne .LBB5_3
1 1 0.33 movabs rax, 9223372036854775804
1 1 0.33 and rax, rsi
1 1 0.33 cmp rax, 9
1 1 1.00 jb .LBB5_3
1 1 0.33 add rax, -9
1 1 0.33 movabs rcx, -6148914691236517205
2 4 1.00 mul rcx
1 1 0.50 shr rdx
1 1 0.50 lea rax, [rdx + 2*rdx]
1 1 0.33 or rax, 3
1 1 0.33 add rax, 9
1 1 0.33 cmp rsi, rax
1 1 1.00 je .LBB5_4
1 0 0.25 xor edi, edi
1 1 0.33 mov rdx, rsi
1 1 0.33 mov rax, rdi
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]
- - 6.32 6.33 - 6.35 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.64 0.35 - 0.01 - - test dil, 3
- - - - - 1.00 - - jne .LBB5_3
- - 0.34 0.65 - 0.01 - - movabs rax, 9223372036854775804
- - 0.35 0.65 - - - - and rax, rsi
- - 0.33 0.34 - 0.33 - - cmp rax, 9
- - - - - 1.00 - - jb .LBB5_3
- - 0.35 - - 0.65 - - add rax, -9
- - 0.97 0.01 - 0.02 - - movabs rcx, -6148914691236517205
- - 1.00 1.00 - - - - mul rcx
- - 0.99 - - 0.01 - - shr rdx
- - 0.33 0.67 - - - - lea rax, [rdx + 2*rdx]
- - 0.34 0.66 - - - - or rax, 3
- - 0.33 0.66 - 0.01 - - add rax, 9
- - 0.01 0.99 - - - - cmp rsi, rax
- - - - - 1.00 - - je .LBB5_4
- - - - - - - - xor edi, edi
- - 0.32 0.01 - 0.67 - - mov rdx, rsi
- - 0.02 0.34 - 0.64 - - mov rax, rdi
- - - - - 1.00 - - ret
Sourcefn ref_from_prefix(
source: &[u8],
) -> Result<(&Self, &[u8]), CastError<&[u8], Self>>where
Self: KnownLayout + Immutable,
fn ref_from_prefix(
source: &[u8],
) -> Result<(&Self, &[u8]), CastError<&[u8], Self>>where
Self: KnownLayout + Immutable,
Interprets the prefix of the given source as a &Self without
copying.
This method computes the largest possible size of Self
that can fit in the leading bytes of source, then attempts to return
both a reference to those bytes interpreted as a Self, and a reference
to the remaining bytes. If there are insufficient bytes, or if source
is not appropriately aligned, this returns Err. If Self: Unaligned, you can infallibly discard the alignment
error.
Self may be a sized type, a slice, or a slice DST.
§Compile-Time Assertions
This method cannot yet be used on unsized types whose dynamically-sized
component is zero-sized. See ref_from_prefix_with_elems, which does
support such types. Attempting to use this method on such types results
in a compile-time assertion error; e.g.:
use zerocopy::*;
#[derive(FromBytes, Immutable, KnownLayout)]
#[repr(C)]
struct ZSTy {
leading_sized: u16,
trailing_dst: [()],
}
let _ = ZSTy::ref_from_prefix(0u16.as_bytes()); // ⚠ Compile Error!§Examples
use zerocopy::FromBytes;
#[derive(FromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct PacketHeader {
src_port: [u8; 2],
dst_port: [u8; 2],
length: [u8; 2],
checksum: [u8; 2],
}
#[derive(FromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct Packet {
header: PacketHeader,
body: [[u8; 2]],
}
// These are more bytes than are needed to encode a `Packet`.
let bytes = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14][..];
let (packet, suffix) = Packet::ref_from_prefix(bytes).unwrap();
assert_eq!(packet.header.src_port, [0, 1]);
assert_eq!(packet.header.dst_port, [2, 3]);
assert_eq!(packet.header.length, [4, 5]);
assert_eq!(packet.header.checksum, [6, 7]);
assert_eq!(packet.body, [[8, 9], [10, 11], [12, 13]]);
assert_eq!(suffix, &[14u8][..]);§ 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:
Sized
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)]
#[repr(C, align(2))]
pub struct Packet<Magic> {
magic_number: Magic,
mug_size: u8,
temperature: u8,
marshmallows: [u8; 2],
}
/// 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
#[path = "formats/coco_static_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_from_prefix_static_size(source: &[u8]) -> Option<&format::LocoPacket> {
match zerocopy::FromBytes::ref_from_prefix(source) {
Ok((packet, _rest)) => Some(packet),
_ => None,
}
}
Assembly
bench_ref_from_prefix_static_size:
xor eax, eax
cmp rsi, 6
mov rcx, rdi
cmovb rcx, rax
test dil, 1
cmove rax, rcx
ret
Machine Code Analysis
Iterations: 100
Instructions: 700
Total Cycles: 274
Total uOps: 900
Dispatch Width: 4
uOps Per Cycle: 3.28
IPC: 2.55
Block RThroughput: 2.3
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 0 0.25 xor eax, eax
1 1 0.33 cmp rsi, 6
1 1 0.33 mov rcx, rdi
2 2 0.67 cmovb rcx, rax
1 1 0.33 test dil, 1
2 2 0.67 cmove rax, 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]
- - 2.66 2.67 - 2.67 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - - - - - - xor eax, eax
- - - 0.01 - 0.99 - - cmp rsi, 6
- - 0.01 0.67 - 0.32 - - mov rcx, rdi
- - 1.00 0.99 - 0.01 - - cmovb rcx, rax
- - 0.66 0.01 - 0.33 - - test dil, 1
- - 0.99 0.99 - 0.02 - - cmove rax, rcx
- - - - - 1.00 - - ret
Unsized
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)]
#[repr(C, align(2))]
pub struct Packet<Magic> {
magic_number: Magic,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
/// 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
#[path = "formats/coco_dynamic_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_from_prefix_dynamic_size(source: &[u8]) -> Option<&format::LocoPacket> {
match zerocopy::FromBytes::ref_from_prefix(source) {
Ok((packet, _rest)) => Some(packet),
_ => None,
}
}
Assembly
bench_ref_from_prefix_dynamic_size:
xor edx, edx
mov eax, 0
test dil, 1
jne .LBB5_4
cmp rsi, 4
jae .LBB5_3
mov edx, 1
xor eax, eax
ret
.LBB5_3:
add rsi, -4
shr rsi
mov rdx, rsi
mov rax, rdi
.LBB5_4:
ret
Machine Code Analysis
Iterations: 100
Instructions: 1400
Total Cycles: 405
Total uOps: 1400
Dispatch Width: 4
uOps Per Cycle: 3.46
IPC: 3.46
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 0 0.25 xor edx, edx
1 1 0.33 mov eax, 0
1 1 0.33 test dil, 1
1 1 1.00 jne .LBB5_4
1 1 0.33 cmp rsi, 4
1 1 1.00 jae .LBB5_3
1 1 0.33 mov edx, 1
1 0 0.25 xor eax, eax
1 1 1.00 U ret
1 1 0.33 add rsi, -4
1 1 0.50 shr rsi
1 1 0.33 mov rdx, rsi
1 1 0.33 mov rax, rdi
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]
- - 3.99 3.99 - 4.02 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - - - - - - xor edx, edx
- - 0.01 0.98 - 0.01 - - mov eax, 0
- - 0.98 0.02 - - - - test dil, 1
- - - - - 1.00 - - jne .LBB5_4
- - 0.02 0.98 - - - - cmp rsi, 4
- - - - - 1.00 - - jae .LBB5_3
- - 0.98 0.01 - 0.01 - - mov edx, 1
- - - - - - - - xor eax, eax
- - - - - 1.00 - - ret
- - 0.01 0.99 - - - - add rsi, -4
- - 1.00 - - - - - shr rsi
- - - 1.00 - - - - mov rdx, rsi
- - 0.99 0.01 - - - - mov rax, rdi
- - - - - 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)]
#[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
#[path = "formats/coco_dynamic_padding.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_from_prefix_dynamic_padding(source: &[u8]) -> Option<&format::LocoPacket> {
match zerocopy::FromBytes::ref_from_prefix(source) {
Ok((packet, _rest)) => Some(packet),
_ => None,
}
}
Assembly
bench_ref_from_prefix_dynamic_padding:
xor edx, edx
mov eax, 0
test dil, 3
je .LBB5_1
ret
.LBB5_1:
movabs rax, 9223372036854775804
and rsi, rax
cmp rsi, 9
jae .LBB5_3
mov edx, 1
xor eax, eax
ret
.LBB5_3:
add rsi, -9
movabs rcx, -6148914691236517205
mov rax, rsi
mul rcx
shr rdx
mov rax, rdi
ret
Machine Code Analysis
Iterations: 100
Instructions: 1900
Total Cycles: 608
Total uOps: 2000
Dispatch Width: 4
uOps Per Cycle: 3.29
IPC: 3.13
Block RThroughput: 5.0
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 0 0.25 xor edx, edx
1 1 0.33 mov eax, 0
1 1 0.33 test dil, 3
1 1 1.00 je .LBB5_1
1 1 1.00 U ret
1 1 0.33 movabs rax, 9223372036854775804
1 1 0.33 and rsi, rax
1 1 0.33 cmp rsi, 9
1 1 1.00 jae .LBB5_3
1 1 0.33 mov edx, 1
1 0 0.25 xor eax, eax
1 1 1.00 U ret
1 1 0.33 add rsi, -9
1 1 0.33 movabs rcx, -6148914691236517205
1 1 0.33 mov rax, rsi
2 4 1.00 mul rcx
1 1 0.50 shr rdx
1 1 0.33 mov rax, rdi
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]
- - 6.00 6.00 - 6.00 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - - - - - - xor edx, edx
- - 0.01 0.98 - 0.01 - - mov eax, 0
- - 0.98 0.01 - 0.01 - - test dil, 3
- - - - - 1.00 - - je .LBB5_1
- - - - - 1.00 - - ret
- - 0.01 0.99 - - - - movabs rax, 9223372036854775804
- - - 1.00 - - - - and rsi, rax
- - - 1.00 - - - - cmp rsi, 9
- - - - - 1.00 - - jae .LBB5_3
- - 1.00 - - - - - mov edx, 1
- - - - - - - - xor eax, eax
- - - - - 1.00 - - ret
- - 0.02 0.02 - 0.96 - - add rsi, -9
- - 0.99 0.01 - - - - movabs rcx, -6148914691236517205
- - 0.01 0.99 - - - - mov rax, rsi
- - 1.00 1.00 - - - - mul rcx
- - 1.00 - - - - - shr rdx
- - 0.98 - - 0.02 - - mov rax, rdi
- - - - - 1.00 - - ret
Sourcefn ref_from_suffix(
source: &[u8],
) -> Result<(&[u8], &Self), CastError<&[u8], Self>>where
Self: Immutable + KnownLayout,
fn ref_from_suffix(
source: &[u8],
) -> Result<(&[u8], &Self), CastError<&[u8], Self>>where
Self: Immutable + KnownLayout,
Interprets the suffix of the given bytes as a &Self.
This method computes the largest possible size of Self
that can fit in the trailing bytes of source, then attempts to return
both a reference to those bytes interpreted as a Self, and a reference
to the preceding bytes. If there are insufficient bytes, or if that
suffix of source is not appropriately aligned, this returns Err. If
Self: Unaligned, you can infallibly discard the
alignment error.
Self may be a sized type, a slice, or a slice DST.
§Compile-Time Assertions
This method cannot yet be used on unsized types whose dynamically-sized
component is zero-sized. See ref_from_suffix_with_elems, which does
support such types. Attempting to use this method on such types results
in a compile-time assertion error; e.g.:
use zerocopy::*;
#[derive(FromBytes, Immutable, KnownLayout)]
#[repr(C)]
struct ZSTy {
leading_sized: u16,
trailing_dst: [()],
}
let _ = ZSTy::ref_from_suffix(0u16.as_bytes()); // ⚠ Compile Error!§Examples
use zerocopy::FromBytes;
#[derive(FromBytes, Immutable, KnownLayout)]
#[repr(C)]
struct PacketTrailer {
frame_check_sequence: [u8; 4],
}
// These are more bytes than are needed to encode a `PacketTrailer`.
let bytes = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
let (prefix, trailer) = PacketTrailer::ref_from_suffix(bytes).unwrap();
assert_eq!(prefix, &[0, 1, 2, 3, 4, 5][..]);
assert_eq!(trailer.frame_check_sequence, [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:
Sized
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)]
#[repr(C, align(2))]
pub struct Packet<Magic> {
magic_number: Magic,
mug_size: u8,
temperature: u8,
marshmallows: [u8; 2],
}
/// 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
#[path = "formats/coco_static_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_from_suffix_static_size(source: &[u8]) -> Option<&format::LocoPacket> {
match zerocopy::FromBytes::ref_from_suffix(source) {
Ok((_rest, packet)) => Some(packet),
_ => None,
}
}
Assembly
bench_ref_from_suffix_static_size:
lea eax, [rsi + rdi]
cmp rsi, 6
setb cl
or cl, al
test cl, 1
je .LBB5_2
xor eax, eax
ret
.LBB5_2:
lea rax, [rdi + rsi]
add rax, -6
ret
Machine Code Analysis
Iterations: 100
Instructions: 1100
Total Cycles: 338
Total uOps: 1100
Dispatch Width: 4
uOps Per Cycle: 3.25
IPC: 3.25
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.50 lea eax, [rsi + rdi]
1 1 0.33 cmp rsi, 6
1 1 0.50 setb cl
1 1 0.33 or cl, al
1 1 0.33 test cl, 1
1 1 1.00 je .LBB5_2
1 0 0.25 xor eax, eax
1 1 1.00 U ret
1 1 0.50 lea rax, [rdi + rsi]
1 1 0.33 add rax, -6
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]
- - 3.32 3.33 - 3.35 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.97 0.03 - - - - lea eax, [rsi + rdi]
- - 0.33 0.32 - 0.35 - - cmp rsi, 6
- - 1.00 - - - - - setb cl
- - - 1.00 - - - - or cl, al
- - - 1.00 - - - - test cl, 1
- - - - - 1.00 - - je .LBB5_2
- - - - - - - - xor eax, eax
- - - - - 1.00 - - ret
- - 0.34 0.66 - - - - lea rax, [rdi + rsi]
- - 0.68 0.32 - - - - add rax, -6
- - - - - 1.00 - - ret
Unsized
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)]
#[repr(C, align(2))]
pub struct Packet<Magic> {
magic_number: Magic,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
/// 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
#[path = "formats/coco_dynamic_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_from_suffix_dynamic_size(source: &[u8]) -> Option<&format::LocoPacket> {
match zerocopy::FromBytes::ref_from_suffix(source) {
Ok((_rest, packet)) => Some(packet),
_ => None,
}
}
Assembly
bench_ref_from_suffix_dynamic_size:
mov rdx, rsi
lea ecx, [rsi + rdi]
mov eax, edx
and eax, 1
add rax, rdi
xor esi, esi
sub rdx, 4
cmovb rax, rsi
shr rdx
test cl, 1
cmovne rax, rsi
ret
Machine Code Analysis
Iterations: 100
Instructions: 1200
Total Cycles: 439
Total uOps: 1400
Dispatch Width: 4
uOps Per Cycle: 3.19
IPC: 2.73
Block RThroughput: 3.5
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 rdx, rsi
1 1 0.50 lea ecx, [rsi + rdi]
1 1 0.33 mov eax, edx
1 1 0.33 and eax, 1
1 1 0.33 add rax, rdi
1 0 0.25 xor esi, esi
1 1 0.33 sub rdx, 4
2 2 0.67 cmovb rax, rsi
1 1 0.50 shr rdx
1 1 0.33 test cl, 1
2 2 0.67 cmovne 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]
- - 4.33 4.33 - 4.34 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.02 0.32 - 0.66 - - mov rdx, rsi
- - 0.32 0.68 - - - - lea ecx, [rsi + rdi]
- - 0.66 - - 0.34 - - mov eax, edx
- - 0.02 0.33 - 0.65 - - and eax, 1
- - - 0.99 - 0.01 - - add rax, rdi
- - - - - - - - xor esi, esi
- - 0.65 - - 0.35 - - sub rdx, 4
- - 1.00 1.00 - - - - cmovb rax, rsi
- - 0.66 - - 0.34 - - shr rdx
- - - 0.01 - 0.99 - - test cl, 1
- - 1.00 1.00 - - - - cmovne 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)]
#[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
#[path = "formats/coco_dynamic_padding.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_from_suffix_dynamic_padding(source: &[u8]) -> Option<&format::LocoPacket> {
match zerocopy::FromBytes::ref_from_suffix(source) {
Ok((_rest, packet)) => Some(packet),
_ => None,
}
}
Assembly
bench_ref_from_suffix_dynamic_padding:
lea eax, [rsi + rdi]
test al, 3
jne .LBB5_1
movabs rax, 9223372036854775804
and rax, rsi
cmp rax, 9
jae .LBB5_3
.LBB5_1:
xor eax, eax
ret
.LBB5_3:
add rax, -9
movabs rcx, -6148914691236517205
mul rcx
shr rdx
lea rax, [rdx + 2*rdx]
sub rsi, rax
or rax, -4
add rsi, rdi
add rax, rsi
add rax, -8
ret
Machine Code Analysis
Iterations: 100
Instructions: 2000
Total Cycles: 682
Total uOps: 2100
Dispatch Width: 4
uOps Per Cycle: 3.08
IPC: 2.93
Block RThroughput: 5.3
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.50 lea eax, [rsi + rdi]
1 1 0.33 test al, 3
1 1 1.00 jne .LBB5_1
1 1 0.33 movabs rax, 9223372036854775804
1 1 0.33 and rax, rsi
1 1 0.33 cmp rax, 9
1 1 1.00 jae .LBB5_3
1 0 0.25 xor eax, eax
1 1 1.00 U ret
1 1 0.33 add rax, -9
1 1 0.33 movabs rcx, -6148914691236517205
2 4 1.00 mul rcx
1 1 0.50 shr rdx
1 1 0.50 lea rax, [rdx + 2*rdx]
1 1 0.33 sub rsi, rax
1 1 0.33 or rax, -4
1 1 0.33 add rsi, rdi
1 1 0.33 add rax, rsi
1 1 0.33 add rax, -8
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]
- - 6.65 6.67 - 6.68 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.90 0.10 - - - - lea eax, [rsi + rdi]
- - 0.93 - - 0.07 - - test al, 3
- - - - - 1.00 - - jne .LBB5_1
- - 0.51 0.47 - 0.02 - - movabs rax, 9223372036854775804
- - - - - 1.00 - - and rax, rsi
- - - 0.09 - 0.91 - - cmp rax, 9
- - - - - 1.00 - - jae .LBB5_3
- - - - - - - - xor eax, eax
- - - - - 1.00 - - ret
- - 0.43 0.47 - 0.10 - - add rax, -9
- - 0.42 0.39 - 0.19 - - movabs rcx, -6148914691236517205
- - 1.00 1.00 - - - - mul rcx
- - 0.69 - - 0.31 - - shr rdx
- - 0.54 0.46 - - - - lea rax, [rdx + 2*rdx]
- - 0.07 0.91 - 0.02 - - sub rsi, rax
- - 0.91 0.05 - 0.04 - - or rax, -4
- - 0.08 0.90 - 0.02 - - add rsi, rdi
- - 0.09 0.91 - - - - add rax, rsi
- - 0.08 0.92 - - - - add rax, -8
- - - - - 1.00 - - ret
Sourcefn mut_from_bytes(
source: &mut [u8],
) -> Result<&mut Self, CastError<&mut [u8], Self>>where
Self: IntoBytes + KnownLayout,
fn mut_from_bytes(
source: &mut [u8],
) -> Result<&mut Self, CastError<&mut [u8], Self>>where
Self: IntoBytes + KnownLayout,
Interprets the given source as a &mut Self.
This method attempts to return a reference to source interpreted as a
Self. If the length of source is not a valid size of
Self, or if source is not appropriately aligned, this
returns Err. If Self: Unaligned, you can
infallibly discard the alignment error.
Self may be a sized type, a slice, or a slice DST.
§Compile-Time Assertions
This method cannot yet be used on unsized types whose dynamically-sized
component is zero-sized. See mut_from_prefix_with_elems, which does
support such types. Attempting to use this method on such types results
in a compile-time assertion error; e.g.:
use zerocopy::*;
#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
#[repr(C, packed)]
struct ZSTy {
leading_sized: [u8; 2],
trailing_dst: [()],
}
let mut source = [85, 85];
let _ = ZSTy::mut_from_bytes(&mut source[..]); // ⚠ Compile Error!§Examples
use zerocopy::FromBytes;
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
struct PacketHeader {
src_port: [u8; 2],
dst_port: [u8; 2],
length: [u8; 2],
checksum: [u8; 2],
}
// These bytes encode a `PacketHeader`.
let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7][..];
let header = PacketHeader::mut_from_bytes(bytes).unwrap();
assert_eq!(header.src_port, [0, 1]);
assert_eq!(header.dst_port, [2, 3]);
assert_eq!(header.length, [4, 5]);
assert_eq!(header.checksum, [6, 7]);
header.checksum = [0, 0];
assert_eq!(bytes, [0, 1, 2, 3, 4, 5, 0, 0]);
§ Code Generation
Sourcefn mut_from_prefix(
source: &mut [u8],
) -> Result<(&mut Self, &mut [u8]), CastError<&mut [u8], Self>>where
Self: IntoBytes + KnownLayout,
fn mut_from_prefix(
source: &mut [u8],
) -> Result<(&mut Self, &mut [u8]), CastError<&mut [u8], Self>>where
Self: IntoBytes + KnownLayout,
Interprets the prefix of the given source as a &mut Self without
copying.
This method computes the largest possible size of Self
that can fit in the leading bytes of source, then attempts to return
both a reference to those bytes interpreted as a Self, and a reference
to the remaining bytes. If there are insufficient bytes, or if source
is not appropriately aligned, this returns Err. If Self: Unaligned, you can infallibly discard the alignment
error.
Self may be a sized type, a slice, or a slice DST.
§Compile-Time Assertions
This method cannot yet be used on unsized types whose dynamically-sized
component is zero-sized. See mut_from_suffix_with_elems, which does
support such types. Attempting to use this method on such types results
in a compile-time assertion error; e.g.:
use zerocopy::*;
#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
#[repr(C, packed)]
struct ZSTy {
leading_sized: [u8; 2],
trailing_dst: [()],
}
let mut source = [85, 85];
let _ = ZSTy::mut_from_prefix(&mut source[..]); // ⚠ Compile Error!§Examples
use zerocopy::FromBytes;
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
struct PacketHeader {
src_port: [u8; 2],
dst_port: [u8; 2],
length: [u8; 2],
checksum: [u8; 2],
}
// These are more bytes than are needed to encode a `PacketHeader`.
let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
let (header, body) = PacketHeader::mut_from_prefix(bytes).unwrap();
assert_eq!(header.src_port, [0, 1]);
assert_eq!(header.dst_port, [2, 3]);
assert_eq!(header.length, [4, 5]);
assert_eq!(header.checksum, [6, 7]);
assert_eq!(body, &[8, 9][..]);
header.checksum = [0, 0];
body.fill(1);
assert_eq!(bytes, [0, 1, 2, 3, 4, 5, 0, 0, 1, 1]);§ Code Generation
Sourcefn mut_from_suffix(
source: &mut [u8],
) -> Result<(&mut [u8], &mut Self), CastError<&mut [u8], Self>>where
Self: IntoBytes + KnownLayout,
fn mut_from_suffix(
source: &mut [u8],
) -> Result<(&mut [u8], &mut Self), CastError<&mut [u8], Self>>where
Self: IntoBytes + KnownLayout,
Interprets the suffix of the given source as a &mut Self without
copying.
This method computes the largest possible size of Self
that can fit in the trailing bytes of source, then attempts to return
both a reference to those bytes interpreted as a Self, and a reference
to the preceding bytes. If there are insufficient bytes, or if that
suffix of source is not appropriately aligned, this returns Err. If
Self: Unaligned, you can infallibly discard the
alignment error.
Self may be a sized type, a slice, or a slice DST.
§Compile-Time Assertions
This method cannot yet be used on unsized types whose dynamically-sized component is zero-sized. Attempting to use this method on such types results in a compile-time assertion error; e.g.:
use zerocopy::*;
#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
#[repr(C, packed)]
struct ZSTy {
leading_sized: [u8; 2],
trailing_dst: [()],
}
let mut source = [85, 85];
let _ = ZSTy::mut_from_suffix(&mut source[..]); // ⚠ Compile Error!§Examples
use zerocopy::FromBytes;
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
struct PacketTrailer {
frame_check_sequence: [u8; 4],
}
// These are more bytes than are needed to encode a `PacketTrailer`.
let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
let (prefix, trailer) = PacketTrailer::mut_from_suffix(bytes).unwrap();
assert_eq!(prefix, &[0u8, 1, 2, 3, 4, 5][..]);
assert_eq!(trailer.frame_check_sequence, [6, 7, 8, 9]);
prefix.fill(0);
trailer.frame_check_sequence.fill(1);
assert_eq!(bytes, [0, 0, 0, 0, 0, 0, 1, 1, 1, 1]);§ Code Generation
Sourcefn ref_from_bytes_with_elems(
source: &[u8],
count: usize,
) -> Result<&Self, CastError<&[u8], Self>>
fn ref_from_bytes_with_elems( source: &[u8], count: usize, ) -> Result<&Self, CastError<&[u8], Self>>
Interprets the given source as a &Self with a DST length equal to
count.
This method attempts to return a reference to source interpreted as a
Self with count trailing elements. If the length of source is not
equal to the size of Self with count elements, or if source is not
appropriately aligned, this returns Err. If Self: Unaligned, you can infallibly discard the alignment
error.
§Examples
use zerocopy::FromBytes;
#[derive(FromBytes, Immutable)]
#[repr(C)]
struct Pixel {
r: u8,
g: u8,
b: u8,
a: u8,
}
let bytes = &[0, 1, 2, 3, 4, 5, 6, 7][..];
let pixels = <[Pixel]>::ref_from_bytes_with_elems(bytes, 2).unwrap();
assert_eq!(pixels, &[
Pixel { r: 0, g: 1, b: 2, a: 3 },
Pixel { r: 4, g: 5, b: 6, a: 7 },
]);
Since an explicit count is provided, this method supports types with
zero-sized trailing slice elements. Methods such as ref_from_bytes
which do not take an explicit count do not support such types.
use zerocopy::*;
#[derive(FromBytes, Immutable, KnownLayout)]
#[repr(C)]
struct ZSTy {
leading_sized: [u8; 2],
trailing_dst: [()],
}
let src = &[85, 85][..];
let zsty = ZSTy::ref_from_bytes_with_elems(src, 42).unwrap();
assert_eq!(zsty.trailing_dst.len(), 42);§ 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)]
#[repr(u16)]
pub enum C0C0 {
_XC0C0 = 0xC0C0,
}
#[derive(FromBytes, KnownLayout, Immutable)]
#[repr(C, align(2))]
pub struct Packet<Magic> {
magic_number: Magic,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
/// 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
#[path = "formats/coco_dynamic_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_from_bytes_with_elems_dynamic_size(
source: &[u8],
count: usize,
) -> Option<&format::LocoPacket> {
zerocopy::FromBytes::ref_from_bytes_with_elems(source, count).ok()
}
Assembly
bench_ref_from_bytes_with_elems_dynamic_size:
movabs rax, 9223372036854775805
cmp rdx, rax
seta cl
mov rax, rdi
or dil, cl
test dil, 1
jne .LBB5_2
lea rcx, [2*rdx + 4]
cmp rsi, rcx
je .LBB5_3
.LBB5_2:
xor eax, eax
mov rdx, rsi
.LBB5_3:
ret
Machine Code Analysis
Iterations: 100
Instructions: 1300
Total Cycles: 439
Total uOps: 1400
Dispatch Width: 4
uOps Per Cycle: 3.19
IPC: 2.96
Block RThroughput: 3.5
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 movabs rax, 9223372036854775805
1 1 0.33 cmp rdx, rax
2 2 1.00 seta cl
1 1 0.33 mov rax, rdi
1 1 0.33 or dil, cl
1 1 0.33 test dil, 1
1 1 1.00 jne .LBB5_2
1 1 0.50 lea rcx, [2*rdx + 4]
1 1 0.33 cmp rsi, rcx
1 1 1.00 je .LBB5_3
1 0 0.25 xor eax, eax
1 1 0.33 mov rdx, 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]
- - 4.32 4.33 - 4.35 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - 0.99 - 0.01 - - movabs rax, 9223372036854775805
- - 0.33 0.67 - - - - cmp rdx, rax
- - 1.98 - - 0.02 - - seta cl
- - 0.01 0.99 - - - - mov rax, rdi
- - 1.00 - - - - - or dil, cl
- - 0.99 0.01 - - - - test dil, 1
- - - - - 1.00 - - jne .LBB5_2
- - - 1.00 - - - - lea rcx, [2*rdx + 4]
- - 0.01 - - 0.99 - - cmp rsi, rcx
- - - - - 1.00 - - je .LBB5_3
- - - - - - - - xor eax, eax
- - - 0.67 - 0.33 - - mov rdx, 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)]
#[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
#[path = "formats/coco_dynamic_padding.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_from_bytes_with_elems_dynamic_padding(
source: &[u8],
count: usize,
) -> Option<&format::LocoPacket> {
zerocopy::FromBytes::ref_from_bytes_with_elems(source, count).ok()
}
Assembly
bench_ref_from_bytes_with_elems_dynamic_padding:
mov rcx, rdx
mov edx, 3
mov rax, rcx
mul rdx
jo .LBB5_5
cmp rax, -10
ja .LBB5_5
mov edx, eax
not edx
and edx, 3
add rdx, rax
add rdx, 9
cmp rsi, rdx
jne .LBB5_5
mov r8d, edi
and r8d, 3
jne .LBB5_5
add rax, 9
cmp rdx, rax
jb .LBB5_5
mov rax, rdi
mov rdx, rcx
ret
.LBB5_5:
xor edi, edi
mov rcx, rsi
mov rax, rdi
mov rdx, rcx
ret
Machine Code Analysis
Iterations: 100
Instructions: 2800
Total Cycles: 944
Total uOps: 2900
Dispatch Width: 4
uOps Per Cycle: 3.07
IPC: 2.97
Block RThroughput: 7.3
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 rcx, rdx
1 1 0.33 mov edx, 3
1 1 0.33 mov rax, rcx
2 4 1.00 mul rdx
1 1 1.00 jo .LBB5_5
1 1 0.33 cmp rax, -10
1 1 1.00 ja .LBB5_5
1 1 0.33 mov edx, eax
1 1 0.33 not edx
1 1 0.33 and edx, 3
1 1 0.33 add rdx, rax
1 1 0.33 add rdx, 9
1 1 0.33 cmp rsi, rdx
1 1 1.00 jne .LBB5_5
1 1 0.33 mov r8d, edi
1 1 0.33 and r8d, 3
1 1 1.00 jne .LBB5_5
1 1 0.33 add rax, 9
1 1 0.33 cmp rdx, rax
1 1 1.00 jb .LBB5_5
1 1 0.33 mov rax, rdi
1 1 0.33 mov rdx, rcx
1 1 1.00 U ret
1 0 0.25 xor edi, edi
1 1 0.33 mov rcx, rsi
1 1 0.33 mov rax, rdi
1 1 0.33 mov rdx, 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]
- - 9.32 9.32 - 9.36 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.64 0.18 - 0.18 - - mov rcx, rdx
- - 0.17 0.83 - - - - mov edx, 3
- - 0.50 0.49 - 0.01 - - mov rax, rcx
- - 1.00 1.00 - - - - mul rdx
- - - - - 1.00 - - jo .LBB5_5
- - 0.82 0.18 - - - - cmp rax, -10
- - - - - 1.00 - - ja .LBB5_5
- - 0.02 0.98 - - - - mov edx, eax
- - 0.82 0.02 - 0.16 - - not edx
- - 0.82 0.17 - 0.01 - - and edx, 3
- - 0.99 - - 0.01 - - add rdx, rax
- - 0.98 0.01 - 0.01 - - add rdx, 9
- - 1.00 - - - - - cmp rsi, rdx
- - - - - 1.00 - - jne .LBB5_5
- - 0.16 0.83 - 0.01 - - mov r8d, edi
- - 0.17 0.17 - 0.66 - - and r8d, 3
- - - - - 1.00 - - jne .LBB5_5
- - 0.02 0.98 - - - - add rax, 9
- - - 0.17 - 0.83 - - cmp rdx, rax
- - - - - 1.00 - - jb .LBB5_5
- - 0.01 0.67 - 0.32 - - mov rax, rdi
- - 0.02 0.98 - - - - mov rdx, rcx
- - - - - 1.00 - - ret
- - - - - - - - xor edi, edi
- - 0.34 0.66 - - - - mov rcx, rsi
- - 0.34 0.50 - 0.16 - - mov rax, rdi
- - 0.50 0.50 - - - - mov rdx, rcx
- - - - - 1.00 - - ret
Sourcefn ref_from_prefix_with_elems(
source: &[u8],
count: usize,
) -> Result<(&Self, &[u8]), CastError<&[u8], Self>>
fn ref_from_prefix_with_elems( source: &[u8], count: usize, ) -> Result<(&Self, &[u8]), CastError<&[u8], Self>>
Interprets the prefix of the given source as a DST &Self with length
equal to count.
This method attempts to return a reference to the prefix of source
interpreted as a Self with count trailing elements, and a reference
to the remaining bytes. If there are insufficient bytes, or if source
is not appropriately aligned, this returns Err. If Self: Unaligned, you can infallibly discard the alignment
error.
§Examples
use zerocopy::FromBytes;
#[derive(FromBytes, Immutable)]
#[repr(C)]
struct Pixel {
r: u8,
g: u8,
b: u8,
a: u8,
}
// These are more bytes than are needed to encode two `Pixel`s.
let bytes = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
let (pixels, suffix) = <[Pixel]>::ref_from_prefix_with_elems(bytes, 2).unwrap();
assert_eq!(pixels, &[
Pixel { r: 0, g: 1, b: 2, a: 3 },
Pixel { r: 4, g: 5, b: 6, a: 7 },
]);
assert_eq!(suffix, &[8, 9]);Since an explicit count is provided, this method supports types with
zero-sized trailing slice elements. Methods such as ref_from_prefix
which do not take an explicit count do not support such types.
use zerocopy::*;
#[derive(FromBytes, Immutable, KnownLayout)]
#[repr(C)]
struct ZSTy {
leading_sized: [u8; 2],
trailing_dst: [()],
}
let src = &[85, 85][..];
let (zsty, _) = ZSTy::ref_from_prefix_with_elems(src, 42).unwrap();
assert_eq!(zsty.trailing_dst.len(), 42);§ 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)]
#[repr(u16)]
pub enum C0C0 {
_XC0C0 = 0xC0C0,
}
#[derive(FromBytes, KnownLayout, Immutable)]
#[repr(C, align(2))]
pub struct Packet<Magic> {
magic_number: Magic,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
/// 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
#[path = "formats/coco_dynamic_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_from_prefix_with_elems_dynamic_size(
source: &[u8],
count: usize,
) -> Option<&format::LocoPacket> {
match zerocopy::FromBytes::ref_from_prefix_with_elems(source, count) {
Ok((packet, _rest)) => Some(packet),
_ => None,
}
}
Assembly
bench_ref_from_prefix_with_elems_dynamic_size:
movabs rax, 9223372036854775805
cmp rdx, rax
ja .LBB5_1
mov rcx, rdx
xor edx, edx
mov eax, 0
test dil, 1
jne .LBB5_4
lea rax, [2*rcx + 4]
xor r8d, r8d
cmp rax, rsi
mov edx, 1
cmovbe rdx, rcx
cmova rdi, r8
mov rax, rdi
.LBB5_4:
ret
.LBB5_1:
mov edx, 1
xor eax, eax
ret
Machine Code Analysis
Iterations: 100
Instructions: 1900
Total Cycles: 672
Total uOps: 2300
Dispatch Width: 4
uOps Per Cycle: 3.42
IPC: 2.83
Block RThroughput: 5.8
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 movabs rax, 9223372036854775805
1 1 0.33 cmp rdx, rax
1 1 1.00 ja .LBB5_1
1 1 0.33 mov rcx, rdx
1 0 0.25 xor edx, edx
1 1 0.33 mov eax, 0
1 1 0.33 test dil, 1
1 1 1.00 jne .LBB5_4
1 1 0.50 lea rax, [2*rcx + 4]
1 0 0.25 xor r8d, r8d
1 1 0.33 cmp rax, rsi
1 1 0.33 mov edx, 1
3 3 1.00 cmovbe rdx, rcx
3 3 1.00 cmova rdi, r8
1 1 0.33 mov rax, rdi
1 1 1.00 U ret
1 1 0.33 mov edx, 1
1 0 0.25 xor eax, eax
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]
- - 6.66 6.66 - 6.68 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - 0.99 - 0.01 - - movabs rax, 9223372036854775805
- - 0.37 0.63 - - - - cmp rdx, rax
- - - - - 1.00 - - ja .LBB5_1
- - 0.63 0.37 - - - - mov rcx, rdx
- - - - - - - - xor edx, edx
- - 0.01 0.98 - 0.01 - - mov eax, 0
- - 0.98 0.02 - - - - test dil, 1
- - - - - 1.00 - - jne .LBB5_4
- - 0.01 0.99 - - - - lea rax, [2*rcx + 4]
- - - - - - - - xor r8d, r8d
- - 1.00 - - - - - cmp rax, rsi
- - - 0.67 - 0.33 - - mov edx, 1
- - 0.73 0.98 - 1.29 - - cmovbe rdx, rcx
- - 1.60 0.36 - 1.04 - - cmova rdi, r8
- - 0.99 0.01 - - - - mov rax, rdi
- - - - - 1.00 - - ret
- - 0.34 0.66 - - - - mov edx, 1
- - - - - - - - xor eax, eax
- - - - - 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)]
#[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
#[path = "formats/coco_dynamic_padding.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_from_prefix_with_elems_dynamic_padding(
source: &[u8],
count: usize,
) -> Option<&format::LocoPacket> {
match zerocopy::FromBytes::ref_from_prefix_with_elems(source, count) {
Ok((packet, _rest)) => Some(packet),
_ => None,
}
}
Assembly
bench_ref_from_prefix_with_elems_dynamic_padding:
mov rcx, rdx
mov edx, 3
mov rax, rcx
mul rdx
mov edx, 1
jo .LBB5_5
cmp rax, -10
ja .LBB5_5
lea r8, [rax + 9]
not eax
and eax, 3
add rax, r8
jae .LBB5_3
.LBB5_5:
xor r8d, r8d
mov rax, r8
ret
.LBB5_3:
xor edx, edx
mov r8d, 0
test dil, 3
je .LBB5_4
mov rax, r8
ret
.LBB5_4:
xor edx, edx
cmp rax, rsi
mov eax, 1
cmova rcx, rax
cmova rdi, rdx
mov rdx, rcx
mov r8, rdi
mov rax, r8
ret
Machine Code Analysis
Iterations: 100
Instructions: 3100
Total Cycles: 1110
Total uOps: 3600
Dispatch Width: 4
uOps Per Cycle: 3.24
IPC: 2.79
Block RThroughput: 9.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 rcx, rdx
1 1 0.33 mov edx, 3
1 1 0.33 mov rax, rcx
2 4 1.00 mul rdx
1 1 0.33 mov edx, 1
1 1 1.00 jo .LBB5_5
1 1 0.33 cmp rax, -10
1 1 1.00 ja .LBB5_5
1 1 0.50 lea r8, [rax + 9]
1 1 0.33 not eax
1 1 0.33 and eax, 3
1 1 0.33 add rax, r8
1 1 1.00 jae .LBB5_3
1 0 0.25 xor r8d, r8d
1 1 0.33 mov rax, r8
1 1 1.00 U ret
1 0 0.25 xor edx, edx
1 1 0.33 mov r8d, 0
1 1 0.33 test dil, 3
1 1 1.00 je .LBB5_4
1 1 0.33 mov rax, r8
1 1 1.00 U ret
1 0 0.25 xor edx, edx
1 1 0.33 cmp rax, rsi
1 1 0.33 mov eax, 1
3 3 1.00 cmova rcx, rax
3 3 1.00 cmova rdi, rdx
1 1 0.33 mov rdx, rcx
1 1 0.33 mov r8, rdi
1 1 0.33 mov rax, r8
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]
- - 11.01 10.98 - 11.01 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.48 0.50 - 0.02 - - mov rcx, rdx
- - 0.02 0.98 - - - - mov edx, 3
- - 0.51 0.48 - 0.01 - - mov rax, rcx
- - 1.00 1.00 - - - - mul rdx
- - 0.49 0.50 - 0.01 - - mov edx, 1
- - - - - 1.00 - - jo .LBB5_5
- - 0.98 0.02 - - - - cmp rax, -10
- - - - - 1.00 - - ja .LBB5_5
- - 0.02 0.98 - - - - lea r8, [rax + 9]
- - 0.98 0.02 - - - - not eax
- - 0.99 0.01 - - - - and eax, 3
- - 0.98 0.01 - 0.01 - - add rax, r8
- - - - - 1.00 - - jae .LBB5_3
- - - - - - - - xor r8d, r8d
- - 0.01 0.98 - 0.01 - - mov rax, r8
- - - - - 1.00 - - ret
- - - - - - - - xor edx, edx
- - 0.48 0.52 - - - - mov r8d, 0
- - 0.02 0.97 - 0.01 - - test dil, 3
- - - - - 1.00 - - je .LBB5_4
- - 0.49 0.50 - 0.01 - - mov rax, r8
- - - - - 1.00 - - ret
- - - - - - - - xor edx, edx
- - 0.51 0.49 - - - - cmp rax, rsi
- - - 1.00 - - - - mov eax, 1
- - 1.04 0.97 - 0.99 - - cmova rcx, rax
- - 0.98 0.53 - 1.49 - - cmova rdi, rdx
- - 0.50 0.50 - - - - mov rdx, rcx
- - 0.51 0.01 - 0.48 - - mov r8, rdi
- - 0.02 0.01 - 0.97 - - mov rax, r8
- - - - - 1.00 - - ret
Sourcefn ref_from_suffix_with_elems(
source: &[u8],
count: usize,
) -> Result<(&[u8], &Self), CastError<&[u8], Self>>
fn ref_from_suffix_with_elems( source: &[u8], count: usize, ) -> Result<(&[u8], &Self), CastError<&[u8], Self>>
Interprets the suffix of the given source as a DST &Self with length
equal to count.
This method attempts to return a reference to the suffix of source
interpreted as a Self with count trailing elements, and a reference
to the preceding bytes. If there are insufficient bytes, or if that
suffix of source is not appropriately aligned, this returns Err. If
Self: Unaligned, you can infallibly discard the
alignment error.
§Examples
use zerocopy::FromBytes;
#[derive(FromBytes, Immutable)]
#[repr(C)]
struct Pixel {
r: u8,
g: u8,
b: u8,
a: u8,
}
// These are more bytes than are needed to encode two `Pixel`s.
let bytes = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
let (prefix, pixels) = <[Pixel]>::ref_from_suffix_with_elems(bytes, 2).unwrap();
assert_eq!(prefix, &[0, 1]);
assert_eq!(pixels, &[
Pixel { r: 2, g: 3, b: 4, a: 5 },
Pixel { r: 6, g: 7, b: 8, a: 9 },
]);Since an explicit count is provided, this method supports types with
zero-sized trailing slice elements. Methods such as ref_from_suffix
which do not take an explicit count do not support such types.
use zerocopy::*;
#[derive(FromBytes, Immutable, KnownLayout)]
#[repr(C)]
struct ZSTy {
leading_sized: [u8; 2],
trailing_dst: [()],
}
let src = &[85, 85][..];
let (_, zsty) = ZSTy::ref_from_suffix_with_elems(src, 42).unwrap();
assert_eq!(zsty.trailing_dst.len(), 42);§ 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)]
#[repr(u16)]
pub enum C0C0 {
_XC0C0 = 0xC0C0,
}
#[derive(FromBytes, KnownLayout, Immutable)]
#[repr(C, align(2))]
pub struct Packet<Magic> {
magic_number: Magic,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
/// 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
#[path = "formats/coco_dynamic_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_from_suffix_with_elems_dynamic_size(
source: &[u8],
count: usize,
) -> Option<&format::LocoPacket> {
match zerocopy::FromBytes::ref_from_suffix_with_elems(source, count) {
Ok((_rest, packet)) => Some(packet),
_ => None,
}
}
Assembly
bench_ref_from_suffix_with_elems_dynamic_size:
movabs rax, 9223372036854775805
cmp rdx, rax
ja .LBB5_1
lea r8d, [rsi + rdi]
xor ecx, ecx
mov eax, 0
test r8b, 1
jne .LBB5_5
lea rax, [2*rdx + 4]
sub rsi, rax
jae .LBB5_4
.LBB5_1:
xor eax, eax
mov edx, 1
ret
.LBB5_4:
add rdi, rsi
mov rcx, rdx
mov rax, rdi
.LBB5_5:
mov rdx, rcx
ret
Machine Code Analysis
Iterations: 100
Instructions: 1900
Total Cycles: 571
Total uOps: 1900
Dispatch Width: 4
uOps Per Cycle: 3.33
IPC: 3.33
Block RThroughput: 5.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 movabs rax, 9223372036854775805
1 1 0.33 cmp rdx, rax
1 1 1.00 ja .LBB5_1
1 1 0.50 lea r8d, [rsi + rdi]
1 0 0.25 xor ecx, ecx
1 1 0.33 mov eax, 0
1 1 0.33 test r8b, 1
1 1 1.00 jne .LBB5_5
1 1 0.50 lea rax, [2*rdx + 4]
1 1 0.33 sub rsi, rax
1 1 1.00 jae .LBB5_4
1 0 0.25 xor eax, eax
1 1 0.33 mov edx, 1
1 1 1.00 U ret
1 1 0.33 add rdi, rsi
1 1 0.33 mov rcx, rdx
1 1 0.33 mov rax, rdi
1 1 0.33 mov rdx, 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]
- - 5.66 5.66 - 5.68 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.66 0.33 - 0.01 - - movabs rax, 9223372036854775805
- - 0.01 0.99 - - - - cmp rdx, rax
- - - - - 1.00 - - ja .LBB5_1
- - 0.99 0.01 - - - - lea r8d, [rsi + rdi]
- - - - - - - - xor ecx, ecx
- - 0.33 0.33 - 0.34 - - mov eax, 0
- - 0.33 0.34 - 0.33 - - test r8b, 1
- - - - - 1.00 - - jne .LBB5_5
- - 0.34 0.66 - - - - lea rax, [2*rdx + 4]
- - - 1.00 - - - - sub rsi, rax
- - - - - 1.00 - - jae .LBB5_4
- - - - - - - - xor eax, eax
- - 1.00 - - - - - mov edx, 1
- - - - - 1.00 - - ret
- - - 1.00 - - - - add rdi, rsi
- - 1.00 - - - - - mov rcx, rdx
- - 0.32 0.68 - - - - mov rax, rdi
- - 0.68 0.32 - - - - mov rdx, 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)]
#[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
#[path = "formats/coco_dynamic_padding.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_ref_from_suffix_with_elems_dynamic_padding(
source: &[u8],
count: usize,
) -> Option<&format::LocoPacket> {
match zerocopy::FromBytes::ref_from_suffix_with_elems(source, count) {
Ok((_rest, packet)) => Some(packet),
_ => None,
}
}
Assembly
bench_ref_from_suffix_with_elems_dynamic_padding:
mov rcx, rdx
mov edx, 3
mov rax, rcx
mul rdx
jo .LBB5_1
cmp rax, -10
ja .LBB5_1
lea rdx, [rax + 9]
not eax
and eax, 3
add rax, rdx
jae .LBB5_4
.LBB5_1:
xor r8d, r8d
mov edx, 1
mov rax, r8
ret
.LBB5_4:
lea r9d, [rsi + rdi]
xor edx, edx
mov r8d, 0
test r9b, 3
je .LBB5_5
mov rax, r8
ret
.LBB5_5:
sub rsi, rax
jb .LBB5_1
add rdi, rsi
mov rdx, rcx
mov r8, rdi
mov rax, r8
ret
Machine Code Analysis
Iterations: 100
Instructions: 3000
Total Cycles: 973
Total uOps: 3100
Dispatch Width: 4
uOps Per Cycle: 3.19
IPC: 3.08
Block RThroughput: 8.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 rcx, rdx
1 1 0.33 mov edx, 3
1 1 0.33 mov rax, rcx
2 4 1.00 mul rdx
1 1 1.00 jo .LBB5_1
1 1 0.33 cmp rax, -10
1 1 1.00 ja .LBB5_1
1 1 0.50 lea rdx, [rax + 9]
1 1 0.33 not eax
1 1 0.33 and eax, 3
1 1 0.33 add rax, rdx
1 1 1.00 jae .LBB5_4
1 0 0.25 xor r8d, r8d
1 1 0.33 mov edx, 1
1 1 0.33 mov rax, r8
1 1 1.00 U ret
1 1 0.50 lea r9d, [rsi + rdi]
1 0 0.25 xor edx, edx
1 1 0.33 mov r8d, 0
1 1 0.33 test r9b, 3
1 1 1.00 je .LBB5_5
1 1 0.33 mov rax, r8
1 1 1.00 U ret
1 1 0.33 sub rsi, rax
1 1 1.00 jb .LBB5_1
1 1 0.33 add rdi, rsi
1 1 0.33 mov rdx, rcx
1 1 0.33 mov r8, rdi
1 1 0.33 mov rax, r8
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]
- - 9.66 9.66 - 9.68 - -
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - 0.99 - 0.01 - - mov rcx, rdx
- - 0.66 0.34 - - - - mov edx, 3
- - 0.34 0.66 - - - - mov rax, rcx
- - 1.00 1.00 - - - - mul rdx
- - - - - 1.00 - - jo .LBB5_1
- - 1.00 - - - - - cmp rax, -10
- - - - - 1.00 - - ja .LBB5_1
- - - 1.00 - - - - lea rdx, [rax + 9]
- - 1.00 - - - - - not eax
- - 1.00 - - - - - and eax, 3
- - 1.00 - - - - - add rax, rdx
- - - - - 1.00 - - jae .LBB5_4
- - - - - - - - xor r8d, r8d
- - 0.33 0.33 - 0.34 - - mov edx, 1
- - 0.33 - - 0.67 - - mov rax, r8
- - - - - 1.00 - - ret
- - 0.33 0.67 - - - - lea r9d, [rsi + rdi]
- - - - - - - - xor edx, edx
- - 0.67 0.33 - - - - mov r8d, 0
- - 0.33 0.34 - 0.33 - - test r9b, 3
- - - - - 1.00 - - je .LBB5_5
- - 0.66 0.01 - 0.33 - - mov rax, r8
- - - - - 1.00 - - ret
- - 0.33 0.67 - - - - sub rsi, rax
- - - - - 1.00 - - jb .LBB5_1
- - - 1.00 - - - - add rdi, rsi
- - 0.01 0.99 - - - - mov rdx, rcx
- - - 1.00 - - - - mov r8, rdi
- - 0.67 0.33 - - - - mov rax, r8
- - - - - 1.00 - - ret
Sourcefn mut_from_bytes_with_elems(
source: &mut [u8],
count: usize,
) -> Result<&mut Self, CastError<&mut [u8], Self>>
fn mut_from_bytes_with_elems( source: &mut [u8], count: usize, ) -> Result<&mut Self, CastError<&mut [u8], Self>>
Interprets the given source as a &mut Self with a DST length equal
to count.
This method attempts to return a reference to source interpreted as a
Self with count trailing elements. If the length of source is not
equal to the size of Self with count elements, or if source is not
appropriately aligned, this returns Err. If Self: Unaligned, you can infallibly discard the alignment
error.
§Examples
use zerocopy::FromBytes;
#[derive(KnownLayout, FromBytes, IntoBytes, Immutable)]
#[repr(C)]
struct Pixel {
r: u8,
g: u8,
b: u8,
a: u8,
}
let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7][..];
let pixels = <[Pixel]>::mut_from_bytes_with_elems(bytes, 2).unwrap();
assert_eq!(pixels, &[
Pixel { r: 0, g: 1, b: 2, a: 3 },
Pixel { r: 4, g: 5, b: 6, a: 7 },
]);
pixels[1] = Pixel { r: 0, g: 0, b: 0, a: 0 };
assert_eq!(bytes, [0, 1, 2, 3, 0, 0, 0, 0]);Since an explicit count is provided, this method supports types with
zero-sized trailing slice elements. Methods such as mut_from_bytes
which do not take an explicit count do not support such types.
use zerocopy::*;
#[derive(FromBytes, IntoBytes, Immutable, KnownLayout)]
#[repr(C, packed)]
struct ZSTy {
leading_sized: [u8; 2],
trailing_dst: [()],
}
let src = &mut [85, 85][..];
let zsty = ZSTy::mut_from_bytes_with_elems(src, 42).unwrap();
assert_eq!(zsty.trailing_dst.len(), 42);§ Code Generation
Sourcefn mut_from_prefix_with_elems(
source: &mut [u8],
count: usize,
) -> Result<(&mut Self, &mut [u8]), CastError<&mut [u8], Self>>
fn mut_from_prefix_with_elems( source: &mut [u8], count: usize, ) -> Result<(&mut Self, &mut [u8]), CastError<&mut [u8], Self>>
Interprets the prefix of the given source as a &mut Self with DST
length equal to count.
This method attempts to return a reference to the prefix of source
interpreted as a Self with count trailing elements, and a reference
to the preceding bytes. If there are insufficient bytes, or if source
is not appropriately aligned, this returns Err. If Self: Unaligned, you can infallibly discard the alignment
error.
§Examples
use zerocopy::FromBytes;
#[derive(KnownLayout, FromBytes, IntoBytes, Immutable)]
#[repr(C)]
struct Pixel {
r: u8,
g: u8,
b: u8,
a: u8,
}
// These are more bytes than are needed to encode two `Pixel`s.
let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
let (pixels, suffix) = <[Pixel]>::mut_from_prefix_with_elems(bytes, 2).unwrap();
assert_eq!(pixels, &[
Pixel { r: 0, g: 1, b: 2, a: 3 },
Pixel { r: 4, g: 5, b: 6, a: 7 },
]);
assert_eq!(suffix, &[8, 9]);
pixels[1] = Pixel { r: 0, g: 0, b: 0, a: 0 };
suffix.fill(1);
assert_eq!(bytes, [0, 1, 2, 3, 0, 0, 0, 0, 1, 1]);Since an explicit count is provided, this method supports types with
zero-sized trailing slice elements. Methods such as mut_from_prefix
which do not take an explicit count do not support such types.
use zerocopy::*;
#[derive(FromBytes, IntoBytes, Immutable, KnownLayout)]
#[repr(C, packed)]
struct ZSTy {
leading_sized: [u8; 2],
trailing_dst: [()],
}
let src = &mut [85, 85][..];
let (zsty, _) = ZSTy::mut_from_prefix_with_elems(src, 42).unwrap();
assert_eq!(zsty.trailing_dst.len(), 42);§ Code Generation
Sourcefn mut_from_suffix_with_elems(
source: &mut [u8],
count: usize,
) -> Result<(&mut [u8], &mut Self), CastError<&mut [u8], Self>>
fn mut_from_suffix_with_elems( source: &mut [u8], count: usize, ) -> Result<(&mut [u8], &mut Self), CastError<&mut [u8], Self>>
Interprets the suffix of the given source as a &mut Self with DST
length equal to count.
This method attempts to return a reference to the suffix of source
interpreted as a Self with count trailing elements, and a reference
to the remaining bytes. If there are insufficient bytes, or if that
suffix of source is not appropriately aligned, this returns Err. If
Self: Unaligned, you can infallibly discard the
alignment error.
§Examples
use zerocopy::FromBytes;
#[derive(FromBytes, IntoBytes, Immutable)]
#[repr(C)]
struct Pixel {
r: u8,
g: u8,
b: u8,
a: u8,
}
// These are more bytes than are needed to encode two `Pixel`s.
let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
let (prefix, pixels) = <[Pixel]>::mut_from_suffix_with_elems(bytes, 2).unwrap();
assert_eq!(prefix, &[0, 1]);
assert_eq!(pixels, &[
Pixel { r: 2, g: 3, b: 4, a: 5 },
Pixel { r: 6, g: 7, b: 8, a: 9 },
]);
prefix.fill(9);
pixels[1] = Pixel { r: 0, g: 0, b: 0, a: 0 };
assert_eq!(bytes, [9, 9, 2, 3, 4, 5, 0, 0, 0, 0]);Since an explicit count is provided, this method supports types with
zero-sized trailing slice elements. Methods such as mut_from_suffix
which do not take an explicit count do not support such types.
use zerocopy::*;
#[derive(FromBytes, IntoBytes, Immutable, KnownLayout)]
#[repr(C, packed)]
struct ZSTy {
leading_sized: [u8; 2],
trailing_dst: [()],
}
let src = &mut [85, 85][..];
let (_, zsty) = ZSTy::mut_from_suffix_with_elems(src, 42).unwrap();
assert_eq!(zsty.trailing_dst.len(), 42);§ Code Generation
Sourcefn read_from_bytes(source: &[u8]) -> Result<Self, SizeError<&[u8], Self>>where
Self: Sized,
fn read_from_bytes(source: &[u8]) -> Result<Self, SizeError<&[u8], Self>>where
Self: Sized,
Reads a copy of Self from the given source.
If source.len() != size_of::<Self>(), read_from_bytes returns Err.
§Examples
use zerocopy::FromBytes;
#[derive(FromBytes)]
#[repr(C)]
struct PacketHeader {
src_port: [u8; 2],
dst_port: [u8; 2],
length: [u8; 2],
checksum: [u8; 2],
}
// These bytes encode a `PacketHeader`.
let bytes = &[0, 1, 2, 3, 4, 5, 6, 7][..];
let header = PacketHeader::read_from_bytes(bytes).unwrap();
assert_eq!(header.src_port, [0, 1]);
assert_eq!(header.dst_port, [2, 3]);
assert_eq!(header.length, [4, 5]);
assert_eq!(header.checksum, [6, 7]);§ 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.
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)]
#[repr(C, align(2))]
pub struct Packet<Magic> {
magic_number: Magic,
mug_size: u8,
temperature: u8,
marshmallows: [u8; 2],
}
/// 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
#[path = "formats/coco_static_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_read_from_bytes_static_size(source: &[u8]) -> Option<format::LocoPacket> {
zerocopy::FromBytes::read_from_bytes(source).ok()
}
Assembly
bench_read_from_bytes_static_size:
mov rcx, rsi
cmp rsi, 6
jne .LBB5_2
mov eax, dword ptr [rdi]
movzx ecx, word ptr [rdi + 4]
shl rcx, 32
or rcx, rax
.LBB5_2:
shl rcx, 16
inc rcx
xor eax, eax
cmp rsi, 6
cmove rax, rcx
ret
Machine Code Analysis
Iterations: 100
Instructions: 1300
Total Cycles: 377
Total uOps: 1400
Dispatch Width: 4
uOps Per Cycle: 3.71
IPC: 3.45
Block RThroughput: 3.5
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 rcx, rsi
1 1 0.33 cmp rsi, 6
1 1 1.00 jne .LBB5_2
1 5 0.50 * mov eax, dword ptr [rdi]
1 5 0.50 * movzx ecx, word ptr [rdi + 4]
1 1 0.50 shl rcx, 32
1 1 0.33 or rcx, rax
1 1 0.50 shl rcx, 16
1 1 0.33 inc rcx
1 0 0.25 xor eax, eax
1 1 0.33 cmp rsi, 6
2 2 0.67 cmove rax, 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]
- - 3.66 3.67 - 3.67 1.00 1.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.63 0.36 - 0.01 - - mov rcx, rsi
- - 0.05 0.05 - 0.90 - - cmp rsi, 6
- - - - - 1.00 - - jne .LBB5_2
- - - - - - - 1.00 mov eax, dword ptr [rdi]
- - - - - - 1.00 - movzx ecx, word ptr [rdi + 4]
- - 0.97 - - 0.03 - - shl rcx, 32
- - 0.02 0.35 - 0.63 - - or rcx, rax
- - 0.98 - - 0.02 - - shl rcx, 16
- - - 0.98 - 0.02 - - inc rcx
- - - - - - - - xor eax, eax
- - 0.03 0.93 - 0.04 - - cmp rsi, 6
- - 0.98 1.00 - 0.02 - - cmove rax, rcx
- - - - - 1.00 - - ret
Sourcefn read_from_prefix(
source: &[u8],
) -> Result<(Self, &[u8]), SizeError<&[u8], Self>>where
Self: Sized,
fn read_from_prefix(
source: &[u8],
) -> Result<(Self, &[u8]), SizeError<&[u8], Self>>where
Self: Sized,
Reads a copy of Self from the prefix of the given source.
This attempts to read a Self from the first size_of::<Self>() bytes
of source, returning that Self and any remaining bytes. If
source.len() < size_of::<Self>(), it returns Err.
§Examples
use zerocopy::FromBytes;
#[derive(FromBytes)]
#[repr(C)]
struct PacketHeader {
src_port: [u8; 2],
dst_port: [u8; 2],
length: [u8; 2],
checksum: [u8; 2],
}
// These are more bytes than are needed to encode a `PacketHeader`.
let bytes = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
let (header, body) = PacketHeader::read_from_prefix(bytes).unwrap();
assert_eq!(header.src_port, [0, 1]);
assert_eq!(header.dst_port, [2, 3]);
assert_eq!(header.length, [4, 5]);
assert_eq!(header.checksum, [6, 7]);
assert_eq!(body, [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.
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)]
#[repr(C, align(2))]
pub struct Packet<Magic> {
magic_number: Magic,
mug_size: u8,
temperature: u8,
marshmallows: [u8; 2],
}
/// 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
#[path = "formats/coco_static_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_read_from_prefix_static_size(source: &[u8]) -> Option<format::LocoPacket> {
match zerocopy::FromBytes::read_from_prefix(source) {
Ok((packet, _rest)) => Some(packet),
_ => None,
}
}
Assembly
bench_read_from_prefix_static_size:
cmp rsi, 5
jbe .LBB5_2
mov eax, dword ptr [rdi]
movzx edi, word ptr [rdi + 4]
shl rdi, 32
or rdi, rax
.LBB5_2:
shl rdi, 16
inc rdi
xor eax, eax
cmp rsi, 6
cmovae rax, rdi
ret
Machine Code Analysis
Iterations: 100
Instructions: 1200
Total Cycles: 905
Total uOps: 1300
Dispatch Width: 4
uOps Per Cycle: 1.44
IPC: 1.33
Block RThroughput: 3.3
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 cmp rsi, 5
1 1 1.00 jbe .LBB5_2
1 5 0.50 * mov eax, dword ptr [rdi]
1 5 0.50 * movzx edi, word ptr [rdi + 4]
1 1 0.50 shl rdi, 32
1 1 0.33 or rdi, rax
1 1 0.50 shl rdi, 16
1 1 0.33 inc rdi
1 0 0.25 xor eax, eax
1 1 0.33 cmp rsi, 6
2 2 0.67 cmovae rax, rdi
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]
- - 3.32 3.32 - 3.36 1.00 1.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.05 0.94 - 0.01 - - cmp rsi, 5
- - - - - 1.00 - - jbe .LBB5_2
- - - - - - - 1.00 mov eax, dword ptr [rdi]
- - - - - - 1.00 - movzx edi, word ptr [rdi + 4]
- - 0.71 - - 0.29 - - shl rdi, 32
- - - 0.64 - 0.36 - - or rdi, rax
- - 1.00 - - - - - shl rdi, 16
- - 0.31 0.40 - 0.29 - - inc rdi
- - - - - - - - xor eax, eax
- - 0.34 0.35 - 0.31 - - cmp rsi, 6
- - 0.91 0.99 - 0.10 - - cmovae rax, rdi
- - - - - 1.00 - - ret
Sourcefn read_from_suffix(
source: &[u8],
) -> Result<(&[u8], Self), SizeError<&[u8], Self>>where
Self: Sized,
fn read_from_suffix(
source: &[u8],
) -> Result<(&[u8], Self), SizeError<&[u8], Self>>where
Self: Sized,
Reads a copy of Self from the suffix of the given source.
This attempts to read a Self from the last size_of::<Self>() bytes
of source, returning that Self and any preceding bytes. If
source.len() < size_of::<Self>(), it returns Err.
§Examples
use zerocopy::FromBytes;
#[derive(FromBytes)]
#[repr(C)]
struct PacketTrailer {
frame_check_sequence: [u8; 4],
}
// These are more bytes than are needed to encode a `PacketTrailer`.
let bytes = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..];
let (prefix, trailer) = PacketTrailer::read_from_suffix(bytes).unwrap();
assert_eq!(prefix, [0, 1, 2, 3, 4, 5]);
assert_eq!(trailer.frame_check_sequence, [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.
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)]
#[repr(C, align(2))]
pub struct Packet<Magic> {
magic_number: Magic,
mug_size: u8,
temperature: u8,
marshmallows: [u8; 2],
}
/// 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
#[path = "formats/coco_static_size.rs"]
mod format;
#[unsafe(no_mangle)]
fn bench_read_from_suffix_static_size(source: &[u8]) -> Option<format::LocoPacket> {
match zerocopy::FromBytes::read_from_suffix(source) {
Ok((_rest, packet)) => Some(packet),
_ => None,
}
}
Assembly
bench_read_from_suffix_static_size:
mov rcx, rsi
cmp rsi, 6
jb .LBB5_2
mov eax, dword ptr [rdi + rsi - 6]
movzx ecx, word ptr [rdi + rsi - 2]
shl rcx, 32
or rcx, rax
.LBB5_2:
shl rcx, 16
inc rcx
xor eax, eax
cmp rsi, 6
cmovae rax, rcx
ret
Machine Code Analysis
Iterations: 100
Instructions: 1300
Total Cycles: 377
Total uOps: 1400
Dispatch Width: 4
uOps Per Cycle: 3.71
IPC: 3.45
Block RThroughput: 3.5
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 rcx, rsi
1 1 0.33 cmp rsi, 6
1 1 1.00 jb .LBB5_2
1 5 0.50 * mov eax, dword ptr [rdi + rsi - 6]
1 5 0.50 * movzx ecx, word ptr [rdi + rsi - 2]
1 1 0.50 shl rcx, 32
1 1 0.33 or rcx, rax
1 1 0.50 shl rcx, 16
1 1 0.33 inc rcx
1 0 0.25 xor eax, eax
1 1 0.33 cmp rsi, 6
2 2 0.67 cmovae rax, 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]
- - 3.66 3.67 - 3.67 1.00 1.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.63 0.36 - 0.01 - - mov rcx, rsi
- - 0.05 0.05 - 0.90 - - cmp rsi, 6
- - - - - 1.00 - - jb .LBB5_2
- - - - - - - 1.00 mov eax, dword ptr [rdi + rsi - 6]
- - - - - - 1.00 - movzx ecx, word ptr [rdi + rsi - 2]
- - 0.97 - - 0.03 - - shl rcx, 32
- - 0.02 0.35 - 0.63 - - or rcx, rax
- - 0.98 - - 0.02 - - shl rcx, 16
- - - 0.98 - 0.02 - - inc rcx
- - - - - - - - xor eax, eax
- - 0.03 0.93 - 0.04 - - cmp rsi, 6
- - 0.98 1.00 - 0.02 - - cmovae rax, rcx
- - - - - 1.00 - - ret
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.