pub unsafe trait TryFromBytes {
Show 15 methods
// Provided methods
fn try_ref_from_bytes(
source: &[u8],
) -> Result<&Self, TryCastError<&[u8], Self>>
where Self: KnownLayout + Immutable { ... }
fn try_ref_from_prefix(
source: &[u8],
) -> Result<(&Self, &[u8]), TryCastError<&[u8], Self>>
where Self: KnownLayout + Immutable { ... }
fn try_ref_from_suffix(
source: &[u8],
) -> Result<(&[u8], &Self), TryCastError<&[u8], Self>>
where Self: KnownLayout + Immutable { ... }
fn try_mut_from_bytes(
bytes: &mut [u8],
) -> Result<&mut Self, TryCastError<&mut [u8], Self>>
where Self: KnownLayout + IntoBytes { ... }
fn try_mut_from_prefix(
source: &mut [u8],
) -> Result<(&mut Self, &mut [u8]), TryCastError<&mut [u8], Self>>
where Self: KnownLayout + IntoBytes { ... }
fn try_mut_from_suffix(
source: &mut [u8],
) -> Result<(&mut [u8], &mut Self), TryCastError<&mut [u8], Self>>
where Self: KnownLayout + IntoBytes { ... }
fn try_ref_from_bytes_with_elems(
source: &[u8],
count: usize,
) -> Result<&Self, TryCastError<&[u8], Self>>
where Self: KnownLayout<PointerMetadata = usize> + Immutable { ... }
fn try_ref_from_prefix_with_elems(
source: &[u8],
count: usize,
) -> Result<(&Self, &[u8]), TryCastError<&[u8], Self>>
where Self: KnownLayout<PointerMetadata = usize> + Immutable { ... }
fn try_ref_from_suffix_with_elems(
source: &[u8],
count: usize,
) -> Result<(&[u8], &Self), TryCastError<&[u8], Self>>
where Self: KnownLayout<PointerMetadata = usize> + Immutable { ... }
fn try_mut_from_bytes_with_elems(
source: &mut [u8],
count: usize,
) -> Result<&mut Self, TryCastError<&mut [u8], Self>>
where Self: KnownLayout<PointerMetadata = usize> + IntoBytes { ... }
fn try_mut_from_prefix_with_elems(
source: &mut [u8],
count: usize,
) -> Result<(&mut Self, &mut [u8]), TryCastError<&mut [u8], Self>>
where Self: KnownLayout<PointerMetadata = usize> + IntoBytes { ... }
fn try_mut_from_suffix_with_elems(
source: &mut [u8],
count: usize,
) -> Result<(&mut [u8], &mut Self), TryCastError<&mut [u8], Self>>
where Self: KnownLayout<PointerMetadata = usize> + IntoBytes { ... }
fn try_read_from_bytes(
source: &[u8],
) -> Result<Self, TryReadError<&[u8], Self>>
where Self: Sized { ... }
fn try_read_from_prefix(
source: &[u8],
) -> Result<(Self, &[u8]), TryReadError<&[u8], Self>>
where Self: Sized { ... }
fn try_read_from_suffix(
source: &[u8],
) -> Result<(&[u8], Self), TryReadError<&[u8], Self>>
where Self: Sized { ... }
}Expand description
Types for which some bit patterns are valid.
A memory region of the appropriate length which contains initialized bytes
can be viewed as a TryFromBytes type so long as the runtime value of those
bytes corresponds to a valid instance of that type. For example,
bool is TryFromBytes, so zerocopy can transmute a u8 into a
bool so long as it first checks that the value of the u8 is 0 or
1.
§Implementation
Do not implement this trait yourself! Instead, use
#[derive(TryFromBytes)]; e.g.:
#[derive(TryFromBytes)]
struct MyStruct {
...
}
#[derive(TryFromBytes)]
#[repr(u8)]
enum MyEnum {
...
}
#[derive(TryFromBytes, Immutable)]
union MyUnion {
...
}This derive ensures that the runtime check of whether bytes correspond to a valid instance is sound. You must implement this trait via the derive.
§What is a “valid instance”?
In Rust, each type has bit validity, which refers to the set of bit
patterns which may appear in an instance of that type. It is impossible for
safe Rust code to produce values which violate bit validity (ie, values
outside of the “valid” set of bit patterns). If unsafe code produces an
invalid value, this is considered undefined behavior.
Rust’s bit validity rules are currently being decided, which means that some types have three classes of bit patterns: those which are definitely valid, and whose validity is documented in the language; those which may or may not be considered valid at some point in the future; and those which are definitely invalid.
Zerocopy takes a conservative approach, and only considers a bit pattern to be valid if its validity is a documented guarantee provided by the language.
For most use cases, Rust’s current guarantees align with programmers’ intuitions about what ought to be valid. As a result, zerocopy’s conservatism should not affect most users.
If you are negatively affected by lack of support for a particular type, we encourage you to let us know by filing an issue.
§TryFromBytes is not symmetrical with IntoBytes
There are some types which implement both TryFromBytes and IntoBytes,
but for which TryFromBytes is not guaranteed to accept all byte sequences
produced by IntoBytes. In other words, for some T: TryFromBytes + IntoBytes, there exist values of t: T such that
TryFromBytes::try_ref_from_bytes(t.as_bytes()) == None. Code should not
generally assume that values produced by IntoBytes will necessarily be
accepted as valid by TryFromBytes.
§Safety
On its own, T: TryFromBytes does not make any guarantees about the layout
or representation of T. It merely provides the ability to perform a
validity check at runtime via methods like try_ref_from_bytes.
You must not rely on the #[doc(hidden)] internals of TryFromBytes.
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.
Provided Methods§
Sourcefn try_ref_from_bytes(source: &[u8]) -> Result<&Self, TryCastError<&[u8], Self>>where
Self: KnownLayout + Immutable,
fn try_ref_from_bytes(source: &[u8]) -> Result<&Self, TryCastError<&[u8], Self>>where
Self: KnownLayout + Immutable,
Attempts to interpret the given source as a &Self.
If the bytes of source are a valid instance of Self, this method
returns a reference to those bytes interpreted as a Self. If the
length of source is not a valid size of Self, or if
source is not appropriately aligned, or if source is not a valid
instance of Self, 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(TryFromBytes, Immutable, KnownLayout)]
#[repr(C)]
struct ZSTy {
leading_sized: u16,
trailing_dst: [()],
}
let _ = ZSTy::try_ref_from_bytes(0u16.as_bytes()); // ⚠ Compile Error!§Examples
use zerocopy::TryFromBytes;
// The only valid value of this type is the byte `0xC0`
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(u8)]
enum C0 { xC0 = 0xC0 }
// The only valid value of this type is the byte sequence `0xC0C0`.
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct C0C0(C0, C0);
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct Packet {
magic_number: C0C0,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
let bytes = &[0xC0, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5][..];
let packet = Packet::try_ref_from_bytes(bytes).unwrap();
assert_eq!(packet.mug_size, 240);
assert_eq!(packet.temperature, 77);
assert_eq!(packet.marshmallows, [[0, 1], [2, 3], [4, 5]]);
// These bytes are not valid instance of `Packet`.
let bytes = &[0x10, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5][..];
assert!(Packet::try_ref_from_bytes(bytes).is_err());§ 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_try_ref_from_bytes_static_size(source: &[u8]) -> Option<&format::CocoPacket> {
zerocopy::TryFromBytes::try_ref_from_bytes(source).ok()
}
Assembly
bench_try_ref_from_bytes_static_size:
mov rax, rdi
cmp rsi, 6
setne cl
or cl, al
test cl, 1
jne .LBB5_2
cmp word ptr [rax], -16192
je .LBB5_3
.LBB5_2:
xor eax, eax
.LBB5_3:
ret
Machine Code Analysis
Iterations: 100
Instructions: 1000
Total Cycles: 308
Total uOps: 1100
Dispatch Width: 4
uOps Per Cycle: 3.57
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.33 mov rax, rdi
1 1 0.33 cmp rsi, 6
1 1 0.50 setne cl
1 1 0.33 or cl, al
1 1 0.33 test cl, 1
1 1 1.00 jne .LBB5_2
2 6 0.50 * cmp word ptr [rax], -16192
1 1 1.00 je .LBB5_3
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]
- - 2.98 2.98 - 3.04 0.50 0.50
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.02 0.97 - 0.01 - - mov rax, rdi
- - 0.02 0.98 - - - - cmp rsi, 6
- - 1.00 - - - - - setne cl
- - 0.97 0.02 - 0.01 - - or cl, al
- - 0.96 0.03 - 0.01 - - test cl, 1
- - - - - 1.00 - - jne .LBB5_2
- - 0.01 0.98 - 0.01 0.50 0.50 cmp word ptr [rax], -16192
- - - - - 1.00 - - je .LBB5_3
- - - - - - - - xor eax, eax
- - - - - 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_try_ref_from_bytes_dynamic_size(source: &[u8]) -> Option<&format::CocoPacket> {
zerocopy::TryFromBytes::try_ref_from_bytes(source).ok()
}
Assembly
bench_try_ref_from_bytes_dynamic_size:
mov rdx, rsi
mov rax, rdi
cmp rsi, 4
setb cl
or cl, al
test cl, 1
jne .LBB5_4
lea rcx, [rdx - 4]
mov rsi, rcx
and rsi, -2
add rsi, 4
cmp rdx, rsi
jne .LBB5_4
cmp word ptr [rax], -16192
jne .LBB5_4
shr rcx
mov rdx, rcx
ret
.LBB5_4:
xor eax, eax
ret
Machine Code Analysis
Iterations: 100
Instructions: 2000
Total Cycles: 639
Total uOps: 2100
Dispatch Width: 4
uOps Per Cycle: 3.29
IPC: 3.13
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.33 mov rdx, rsi
1 1 0.33 mov rax, rdi
1 1 0.33 cmp rsi, 4
1 1 0.50 setb cl
1 1 0.33 or cl, al
1 1 0.33 test cl, 1
1 1 1.00 jne .LBB5_4
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.33 cmp rdx, rsi
1 1 1.00 jne .LBB5_4
2 6 0.50 * cmp word ptr [rax], -16192
1 1 1.00 jne .LBB5_4
1 1 0.50 shr rcx
1 1 0.33 mov rdx, rcx
1 1 1.00 U ret
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.32 6.32 - 6.36 0.50 0.50
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.33 0.66 - 0.01 - - mov rdx, rsi
- - 0.66 0.34 - - - - mov rax, rdi
- - 0.34 0.66 - - - - cmp rsi, 4
- - 0.99 - - 0.01 - - setb cl
- - 0.01 0.99 - - - - or cl, al
- - - 1.00 - - - - test cl, 1
- - - - - 1.00 - - jne .LBB5_4
- - 0.66 0.34 - - - - lea rcx, [rdx - 4]
- - 0.33 0.66 - 0.01 - - mov rsi, rcx
- - 1.00 - - - - - and rsi, -2
- - 0.66 0.34 - - - - add rsi, 4
- - - 1.00 - - - - cmp rdx, rsi
- - - - - 1.00 - - jne .LBB5_4
- - - - - 1.00 0.50 0.50 cmp word ptr [rax], -16192
- - - - - 1.00 - - jne .LBB5_4
- - 0.67 - - 0.33 - - shr rcx
- - 0.67 0.33 - - - - mov rdx, rcx
- - - - - 1.00 - - ret
- - - - - - - - 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_try_ref_from_bytes_dynamic_padding(source: &[u8]) -> Option<&format::CocoPacket> {
zerocopy::TryFromBytes::try_ref_from_bytes(source).ok()
}
Assembly
bench_try_ref_from_bytes_dynamic_padding:
test dil, 3
jne .LBB5_4
movabs rax, 9223372036854775804
and rax, rsi
cmp rax, 9
jb .LBB5_4
add rax, -9
movabs rcx, -6148914691236517205
mul rcx
shr rdx
lea rax, [rdx + 2*rdx]
or rax, 3
add rax, 9
cmp rsi, rax
jne .LBB5_4
cmp word ptr [rdi], -16192
je .LBB5_5
.LBB5_4:
xor edi, edi
mov rdx, rsi
.LBB5_5:
mov rax, rdi
ret
Machine Code Analysis
Iterations: 100
Instructions: 2100
Total Cycles: 709
Total uOps: 2300
Dispatch Width: 4
uOps Per Cycle: 3.24
IPC: 2.96
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 test dil, 3
1 1 1.00 jne .LBB5_4
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_4
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 jne .LBB5_4
2 6 0.50 * cmp word ptr [rdi], -16192
1 1 1.00 je .LBB5_5
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.98 6.99 - 7.03 0.50 0.50
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.48 0.51 - 0.01 - - test dil, 3
- - - - - 1.00 - - jne .LBB5_4
- - 0.51 0.49 - - - - movabs rax, 9223372036854775804
- - 0.01 0.99 - - - - and rax, rsi
- - 0.51 0.49 - - - - cmp rax, 9
- - - - - 1.00 - - jb .LBB5_4
- - 0.98 - - 0.02 - - add rax, -9
- - 0.98 0.02 - - - - movabs rcx, -6148914691236517205
- - 1.00 1.00 - - - - mul rcx
- - 0.99 - - 0.01 - - shr rdx
- - - 1.00 - - - - lea rax, [rdx + 2*rdx]
- - - 0.51 - 0.49 - - or rax, 3
- - 0.01 0.49 - 0.50 - - add rax, 9
- - - 0.02 - 0.98 - - cmp rsi, rax
- - - - - 1.00 - - jne .LBB5_4
- - 0.51 0.49 - - 0.50 0.50 cmp word ptr [rdi], -16192
- - - - - 1.00 - - je .LBB5_5
- - - - - - - - xor edi, edi
- - 0.50 0.50 - - - - mov rdx, rsi
- - 0.50 0.48 - 0.02 - - mov rax, rdi
- - - - - 1.00 - - ret
Sourcefn try_ref_from_prefix(
source: &[u8],
) -> Result<(&Self, &[u8]), TryCastError<&[u8], Self>>where
Self: KnownLayout + Immutable,
fn try_ref_from_prefix(
source: &[u8],
) -> Result<(&Self, &[u8]), TryCastError<&[u8], Self>>where
Self: KnownLayout + Immutable,
Attempts to interpret the prefix of the given source as a &Self.
This method computes the largest possible size of Self
that can fit in the leading bytes of source. If that prefix is a valid
instance of Self, this method returns a reference to those bytes
interpreted as Self, and a reference to the remaining bytes. If there
are insufficient bytes, or if source is not appropriately aligned, or
if those bytes are not a valid instance of Self, 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(TryFromBytes, Immutable, KnownLayout)]
#[repr(C)]
struct ZSTy {
leading_sized: u16,
trailing_dst: [()],
}
let _ = ZSTy::try_ref_from_prefix(0u16.as_bytes()); // ⚠ Compile Error!§Examples
use zerocopy::TryFromBytes;
// The only valid value of this type is the byte `0xC0`
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(u8)]
enum C0 { xC0 = 0xC0 }
// The only valid value of this type is the bytes `0xC0C0`.
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct C0C0(C0, C0);
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct Packet {
magic_number: C0C0,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
// These are more bytes than are needed to encode a `Packet`.
let bytes = &[0xC0, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5, 6][..];
let (packet, suffix) = Packet::try_ref_from_prefix(bytes).unwrap();
assert_eq!(packet.mug_size, 240);
assert_eq!(packet.temperature, 77);
assert_eq!(packet.marshmallows, [[0, 1], [2, 3], [4, 5]]);
assert_eq!(suffix, &[6u8][..]);
// These bytes are not valid instance of `Packet`.
let bytes = &[0x10, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5, 6][..];
assert!(Packet::try_ref_from_prefix(bytes).is_err());§ 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_try_ref_from_prefix_static_size(source: &[u8]) -> Option<&format::CocoPacket> {
match zerocopy::TryFromBytes::try_ref_from_prefix(source) {
Ok((packet, _rest)) => Some(packet),
_ => None,
}
}
Assembly
bench_try_ref_from_prefix_static_size:
cmp rsi, 6
setb al
or al, dil
test al, 1
jne .LBB5_2
movzx eax, word ptr [rdi]
cmp eax, 49344
mov eax, 2
cmove rax, rdi
je .LBB5_3
.LBB5_2:
xor eax, eax
.LBB5_3:
ret
Machine Code Analysis
Iterations: 100
Instructions: 1200
Total Cycles: 374
Total uOps: 1300
Dispatch Width: 4
uOps Per Cycle: 3.48
IPC: 3.21
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, 6
1 1 0.50 setb al
1 1 0.33 or al, dil
1 1 0.33 test al, 1
1 1 1.00 jne .LBB5_2
1 5 0.50 * movzx eax, word ptr [rdi]
1 1 0.33 cmp eax, 49344
1 1 0.33 mov eax, 2
2 2 0.67 cmove rax, rdi
1 1 1.00 je .LBB5_3
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]
- - 3.66 3.65 - 3.69 0.50 0.50
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.35 0.64 - 0.01 - - cmp rsi, 6
- - 1.00 - - - - - setb al
- - 0.02 0.66 - 0.32 - - or al, dil
- - 0.03 0.65 - 0.32 - - test al, 1
- - - - - 1.00 - - jne .LBB5_2
- - - - - - 0.50 0.50 movzx eax, word ptr [rdi]
- - 0.92 0.07 - 0.01 - - cmp eax, 49344
- - 0.37 0.63 - - - - mov eax, 2
- - 0.97 1.00 - 0.03 - - cmove rax, rdi
- - - - - 1.00 - - je .LBB5_3
- - - - - - - - xor eax, eax
- - - - - 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_try_ref_from_prefix_dynamic_size(source: &[u8]) -> Option<&format::CocoPacket> {
match zerocopy::TryFromBytes::try_ref_from_prefix(source) {
Ok((packet, _rest)) => Some(packet),
_ => None,
}
}
Assembly
bench_try_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
movzx ecx, word ptr [rdi]
cmp ecx, 49344
mov edx, 2
cmove rdx, rsi
xor eax, eax
cmp cx, -16192
cmove rax, rdi
.LBB5_4:
ret
Machine Code Analysis
Iterations: 100
Instructions: 1900
Total Cycles: 573
Total uOps: 2100
Dispatch Width: 4
uOps Per Cycle: 3.66
IPC: 3.32
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 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 5 0.50 * movzx ecx, word ptr [rdi]
1 1 0.33 cmp ecx, 49344
1 1 0.33 mov edx, 2
2 2 0.67 cmove rdx, rsi
1 0 0.25 xor eax, eax
1 1 0.33 cmp cx, -16192
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.66 5.67 - 5.67 0.50 0.50
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - - - - - - xor edx, edx
- - 0.30 0.37 - 0.33 - - mov eax, 0
- - 0.35 0.32 - 0.33 - - test dil, 1
- - - - - 1.00 - - jne .LBB5_4
- - 0.32 0.33 - 0.35 - - cmp rsi, 4
- - - - - 1.00 - - jae .LBB5_3
- - 0.33 0.35 - 0.32 - - mov edx, 1
- - - - - - - - xor eax, eax
- - - - - 1.00 - - ret
- - 0.34 0.64 - 0.02 - - add rsi, -4
- - 1.00 - - - - - shr rsi
- - - - - - 0.50 0.50 movzx ecx, word ptr [rdi]
- - 0.60 0.40 - - - - cmp ecx, 49344
- - 0.05 0.95 - - - - mov edx, 2
- - 1.00 1.00 - - - - cmove rdx, rsi
- - - - - - - - xor eax, eax
- - 0.37 0.31 - 0.32 - - cmp cx, -16192
- - 1.00 1.00 - - - - 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_try_ref_from_prefix_dynamic_padding(source: &[u8]) -> Option<&format::CocoPacket> {
match zerocopy::TryFromBytes::try_ref_from_prefix(source) {
Ok((packet, _rest)) => Some(packet),
_ => None,
}
}
Assembly
bench_try_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
mov rax, rdx
shr rax
movzx ecx, word ptr [rdi]
cmp cx, -16192
mov edx, 2
cmove rdx, rax
xor eax, eax
cmp ecx, 49344
cmove rax, rdi
ret
Machine Code Analysis
Iterations: 100
Instructions: 2600
Total Cycles: 843
Total uOps: 2900
Dispatch Width: 4
uOps Per Cycle: 3.44
IPC: 3.08
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 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.33 mov rax, rdx
1 1 0.50 shr rax
1 5 0.50 * movzx ecx, word ptr [rdi]
1 1 0.33 cmp cx, -16192
1 1 0.33 mov edx, 2
2 2 0.67 cmove rdx, rax
1 0 0.25 xor eax, eax
1 1 0.33 cmp ecx, 49344
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]
- - 8.33 8.33 - 8.34 0.50 0.50
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - - - - - - xor edx, edx
- - 0.32 0.34 - 0.34 - - mov eax, 0
- - 0.34 0.33 - 0.33 - - test dil, 3
- - - - - 1.00 - - je .LBB5_1
- - - - - 1.00 - - ret
- - 0.35 0.65 - - - - movabs rax, 9223372036854775804
- - 0.96 0.03 - 0.01 - - and rsi, rax
- - 0.01 0.97 - 0.02 - - cmp rsi, 9
- - - - - 1.00 - - jae .LBB5_3
- - 0.67 0.01 - 0.32 - - mov edx, 1
- - - - - - - - xor eax, eax
- - - - - 1.00 - - ret
- - 0.02 0.34 - 0.64 - - add rsi, -9
- - 0.33 0.66 - 0.01 - - movabs rcx, -6148914691236517205
- - 0.66 0.34 - - - - mov rax, rsi
- - 1.00 1.00 - - - - mul rcx
- - 0.01 0.99 - - - - mov rax, rdx
- - 0.99 - - 0.01 - - shr rax
- - - - - - 0.50 0.50 movzx ecx, word ptr [rdi]
- - 0.33 0.03 - 0.64 - - cmp cx, -16192
- - 0.01 0.31 - 0.68 - - mov edx, 2
- - 1.00 1.00 - - - - cmove rdx, rax
- - - - - - - - xor eax, eax
- - 0.33 0.33 - 0.34 - - cmp ecx, 49344
- - 1.00 1.00 - - - - cmove rax, rdi
- - - - - 1.00 - - ret
Sourcefn try_ref_from_suffix(
source: &[u8],
) -> Result<(&[u8], &Self), TryCastError<&[u8], Self>>where
Self: KnownLayout + Immutable,
fn try_ref_from_suffix(
source: &[u8],
) -> Result<(&[u8], &Self), TryCastError<&[u8], Self>>where
Self: KnownLayout + Immutable,
Attempts to interpret the suffix of the given source as a &Self.
This method computes the largest possible size of Self
that can fit in the trailing bytes of source. If that suffix is a
valid instance of Self, this method returns a reference to those bytes
interpreted as Self, and a reference to the preceding bytes. If there
are insufficient bytes, or if the suffix of source would not be
appropriately aligned, or if the suffix is not a valid instance of
Self, 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(TryFromBytes, Immutable, KnownLayout)]
#[repr(C)]
struct ZSTy {
leading_sized: u16,
trailing_dst: [()],
}
let _ = ZSTy::try_ref_from_suffix(0u16.as_bytes()); // ⚠ Compile Error!§Examples
use zerocopy::TryFromBytes;
// The only valid value of this type is the byte `0xC0`
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(u8)]
enum C0 { xC0 = 0xC0 }
// The only valid value of this type is the bytes `0xC0C0`.
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct C0C0(C0, C0);
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct Packet {
magic_number: C0C0,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
// These are more bytes than are needed to encode a `Packet`.
let bytes = &[0, 0xC0, 0xC0, 240, 77, 2, 3, 4, 5, 6, 7][..];
let (prefix, packet) = Packet::try_ref_from_suffix(bytes).unwrap();
assert_eq!(packet.mug_size, 240);
assert_eq!(packet.temperature, 77);
assert_eq!(packet.marshmallows, [[2, 3], [4, 5], [6, 7]]);
assert_eq!(prefix, &[0u8][..]);
// These bytes are not valid instance of `Packet`.
let bytes = &[0, 1, 2, 3, 4, 5, 6, 77, 240, 0xC0, 0x10][..];
assert!(Packet::try_ref_from_suffix(bytes).is_err());§ 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_try_ref_from_suffix_static_size(source: &[u8]) -> Option<&format::CocoPacket> {
match zerocopy::TryFromBytes::try_ref_from_suffix(source) {
Ok((_rest, packet)) => Some(packet),
_ => None,
}
}
Assembly
bench_try_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 rcx, [rdi + rsi]
add rcx, -6
xor eax, eax
cmp word ptr [rdi + rsi - 6], -16192
cmove rax, rcx
ret
Machine Code Analysis
Iterations: 100
Instructions: 1400
Total Cycles: 443
Total uOps: 1600
Dispatch Width: 4
uOps Per Cycle: 3.61
IPC: 3.16
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.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 rcx, [rdi + rsi]
1 1 0.33 add rcx, -6
1 0 0.25 xor eax, eax
2 6 0.50 * cmp word ptr [rdi + rsi - 6], -16192
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]
- - 4.33 4.33 - 4.34 0.50 0.50
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.32 0.68 - - - - lea eax, [rsi + rdi]
- - 0.05 0.94 - 0.01 - - cmp rsi, 6
- - 1.00 - - - - - setb cl
- - 0.95 0.05 - - - - or cl, al
- - 0.95 0.02 - 0.03 - - test cl, 1
- - - - - 1.00 - - je .LBB5_2
- - - - - - - - xor eax, eax
- - - - - 1.00 - - ret
- - 0.04 0.96 - - - - lea rcx, [rdi + rsi]
- - 0.02 0.97 - 0.01 - - add rcx, -6
- - - - - - - - xor eax, eax
- - 0.03 0.66 - 0.31 0.50 0.50 cmp word ptr [rdi + rsi - 6], -16192
- - 0.97 0.05 - 0.98 - - 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_try_ref_from_suffix_dynamic_size(source: &[u8]) -> Option<&format::CocoPacket> {
match zerocopy::TryFromBytes::try_ref_from_suffix(source) {
Ok((_rest, packet)) => Some(packet),
_ => None,
}
}
Assembly
bench_try_ref_from_suffix_dynamic_size:
lea eax, [rsi + rdi]
cmp rsi, 4
setb cl
or cl, al
test cl, 1
je .LBB5_2
xor eax, eax
ret
.LBB5_2:
lea rdx, [rsi - 4]
shr rdx
and esi, 1
lea rcx, [rdi + rsi]
xor eax, eax
cmp word ptr [rdi + rsi], -16192
cmove rax, rcx
ret
Machine Code Analysis
Iterations: 100
Instructions: 1600
Total Cycles: 510
Total uOps: 1800
Dispatch Width: 4
uOps Per Cycle: 3.53
IPC: 3.14
Block RThroughput: 4.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.50 lea eax, [rsi + rdi]
1 1 0.33 cmp rsi, 4
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 rdx, [rsi - 4]
1 1 0.50 shr rdx
1 1 0.33 and esi, 1
1 1 0.50 lea rcx, [rdi + rsi]
1 0 0.25 xor eax, eax
2 6 0.50 * cmp word ptr [rdi + rsi], -16192
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]
- - 4.99 5.00 - 5.01 0.50 0.50
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.98 0.02 - - - - lea eax, [rsi + rdi]
- - - 0.98 - 0.02 - - cmp rsi, 4
- - 1.00 - - - - - setb cl
- - 0.01 0.99 - - - - or cl, al
- - 0.01 0.07 - 0.92 - - test cl, 1
- - - - - 1.00 - - je .LBB5_2
- - - - - - - - xor eax, eax
- - - - - 1.00 - - ret
- - 0.93 0.07 - - - - lea rdx, [rsi - 4]
- - 0.93 - - 0.07 - - shr rdx
- - 0.06 0.93 - 0.01 - - and esi, 1
- - 0.07 0.93 - - - - lea rcx, [rdi + rsi]
- - - - - - - - xor eax, eax
- - - 0.01 - 0.99 0.50 0.50 cmp word ptr [rdi + rsi], -16192
- - 1.00 1.00 - - - - cmove rax, 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_try_ref_from_suffix_dynamic_padding(source: &[u8]) -> Option<&format::CocoPacket> {
match zerocopy::TryFromBytes::try_ref_from_suffix(source) {
Ok((_rest, packet)) => Some(packet),
_ => None,
}
}
Assembly
bench_try_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 rcx, [rdx + 2*rdx]
sub rsi, rcx
or rcx, -4
add rsi, rdi
lea rdi, [rcx + rsi]
add rdi, -8
xor eax, eax
cmp word ptr [rcx + rsi - 8], -16192
cmove rax, rdi
ret
Machine Code Analysis
Iterations: 100
Instructions: 2300
Total Cycles: 791
Total uOps: 2600
Dispatch Width: 4
uOps Per Cycle: 3.29
IPC: 2.91
Block RThroughput: 6.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.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 rcx, [rdx + 2*rdx]
1 1 0.33 sub rsi, rcx
1 1 0.33 or rcx, -4
1 1 0.33 add rsi, rdi
1 1 0.50 lea rdi, [rcx + rsi]
1 1 0.33 add rdi, -8
1 0 0.25 xor eax, eax
2 6 0.50 * cmp word ptr [rcx + rsi - 8], -16192
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]
- - 7.70 7.58 - 7.72 0.50 0.50
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.26 0.74 - - - - lea eax, [rsi + rdi]
- - 0.19 0.28 - 0.53 - - test al, 3
- - - - - 1.00 - - jne .LBB5_1
- - 0.93 0.06 - 0.01 - - movabs rax, 9223372036854775804
- - 0.81 0.14 - 0.05 - - and rax, rsi
- - 0.55 0.43 - 0.02 - - cmp rax, 9
- - - - - 1.00 - - jae .LBB5_3
- - - - - - - - xor eax, eax
- - - - - 1.00 - - ret
- - 0.42 0.56 - 0.02 - - add rax, -9
- - 0.67 0.30 - 0.03 - - movabs rcx, -6148914691236517205
- - 1.00 1.00 - - - - mul rcx
- - 0.71 - - 0.29 - - shr rdx
- - 0.32 0.68 - - - - lea rcx, [rdx + 2*rdx]
- - 0.57 0.04 - 0.39 - - sub rsi, rcx
- - 0.28 0.67 - 0.05 - - or rcx, -4
- - 0.29 0.29 - 0.42 - - add rsi, rdi
- - 0.02 0.98 - - - - lea rdi, [rcx + rsi]
- - 0.02 0.41 - 0.57 - - add rdi, -8
- - - - - - - - xor eax, eax
- - 0.57 0.01 - 0.42 0.50 0.50 cmp word ptr [rcx + rsi - 8], -16192
- - 0.09 0.99 - 0.92 - - cmove rax, rdi
- - - - - 1.00 - - ret
Sourcefn try_mut_from_bytes(
bytes: &mut [u8],
) -> Result<&mut Self, TryCastError<&mut [u8], Self>>where
Self: KnownLayout + IntoBytes,
fn try_mut_from_bytes(
bytes: &mut [u8],
) -> Result<&mut Self, TryCastError<&mut [u8], Self>>where
Self: KnownLayout + IntoBytes,
Attempts to interpret the given source as a &mut Self without
copying.
If the bytes of source are a valid instance of Self, this method
returns a reference to those bytes interpreted as a Self. If the
length of source is not a valid size of Self, or if
source is not appropriately aligned, or if source is not a valid
instance of Self, 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(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(C, packed)]
struct ZSTy {
leading_sized: [u8; 2],
trailing_dst: [()],
}
let mut source = [85, 85];
let _ = ZSTy::try_mut_from_bytes(&mut source[..]); // ⚠ Compile Error!§Examples
use zerocopy::TryFromBytes;
// The only valid value of this type is the byte `0xC0`
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(u8)]
enum C0 { xC0 = 0xC0 }
// The only valid value of this type is the bytes `0xC0C0`.
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(C)]
struct C0C0(C0, C0);
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(C, packed)]
struct Packet {
magic_number: C0C0,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
let bytes = &mut [0xC0, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5][..];
let packet = Packet::try_mut_from_bytes(bytes).unwrap();
assert_eq!(packet.mug_size, 240);
assert_eq!(packet.temperature, 77);
assert_eq!(packet.marshmallows, [[0, 1], [2, 3], [4, 5]]);
packet.temperature = 111;
assert_eq!(bytes, [0xC0, 0xC0, 240, 111, 0, 1, 2, 3, 4, 5]);
// These bytes are not valid instance of `Packet`.
let bytes = &mut [0x10, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5, 6][..];
assert!(Packet::try_mut_from_bytes(bytes).is_err());§ Code Generation
Sourcefn try_mut_from_prefix(
source: &mut [u8],
) -> Result<(&mut Self, &mut [u8]), TryCastError<&mut [u8], Self>>where
Self: KnownLayout + IntoBytes,
fn try_mut_from_prefix(
source: &mut [u8],
) -> Result<(&mut Self, &mut [u8]), TryCastError<&mut [u8], Self>>where
Self: KnownLayout + IntoBytes,
Attempts to interpret the prefix of the given source as a &mut Self.
This method computes the largest possible size of Self
that can fit in the leading bytes of source. If that prefix is a valid
instance of Self, this method returns a reference to those bytes
interpreted as Self, and a reference to the remaining bytes. If there
are insufficient bytes, or if source is not appropriately aligned, or
if the bytes are not a valid instance of Self, 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(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(C, packed)]
struct ZSTy {
leading_sized: [u8; 2],
trailing_dst: [()],
}
let mut source = [85, 85];
let _ = ZSTy::try_mut_from_prefix(&mut source[..]); // ⚠ Compile Error!§Examples
use zerocopy::TryFromBytes;
// The only valid value of this type is the byte `0xC0`
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(u8)]
enum C0 { xC0 = 0xC0 }
// The only valid value of this type is the bytes `0xC0C0`.
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(C)]
struct C0C0(C0, C0);
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(C, packed)]
struct Packet {
magic_number: C0C0,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
// These are more bytes than are needed to encode a `Packet`.
let bytes = &mut [0xC0, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5, 6][..];
let (packet, suffix) = Packet::try_mut_from_prefix(bytes).unwrap();
assert_eq!(packet.mug_size, 240);
assert_eq!(packet.temperature, 77);
assert_eq!(packet.marshmallows, [[0, 1], [2, 3], [4, 5]]);
assert_eq!(suffix, &[6u8][..]);
packet.temperature = 111;
suffix[0] = 222;
assert_eq!(bytes, [0xC0, 0xC0, 240, 111, 0, 1, 2, 3, 4, 5, 222]);
// These bytes are not valid instance of `Packet`.
let bytes = &mut [0x10, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5, 6][..];
assert!(Packet::try_mut_from_prefix(bytes).is_err());§ Code Generation
Sourcefn try_mut_from_suffix(
source: &mut [u8],
) -> Result<(&mut [u8], &mut Self), TryCastError<&mut [u8], Self>>where
Self: KnownLayout + IntoBytes,
fn try_mut_from_suffix(
source: &mut [u8],
) -> Result<(&mut [u8], &mut Self), TryCastError<&mut [u8], Self>>where
Self: KnownLayout + IntoBytes,
Attempts to interpret the suffix of the given source as a &mut Self.
This method computes the largest possible size of Self
that can fit in the trailing bytes of source. If that suffix is a
valid instance of Self, this method returns a reference to those bytes
interpreted as Self, and a reference to the preceding bytes. If there
are insufficient bytes, or if the suffix of source would not be
appropriately aligned, or if the suffix is not a valid instance of
Self, 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(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(C, packed)]
struct ZSTy {
leading_sized: u16,
trailing_dst: [()],
}
let mut source = [85, 85];
let _ = ZSTy::try_mut_from_suffix(&mut source[..]); // ⚠ Compile Error!§Examples
use zerocopy::TryFromBytes;
// The only valid value of this type is the byte `0xC0`
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(u8)]
enum C0 { xC0 = 0xC0 }
// The only valid value of this type is the bytes `0xC0C0`.
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(C)]
struct C0C0(C0, C0);
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(C, packed)]
struct Packet {
magic_number: C0C0,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
// These are more bytes than are needed to encode a `Packet`.
let bytes = &mut [0, 0xC0, 0xC0, 240, 77, 2, 3, 4, 5, 6, 7][..];
let (prefix, packet) = Packet::try_mut_from_suffix(bytes).unwrap();
assert_eq!(packet.mug_size, 240);
assert_eq!(packet.temperature, 77);
assert_eq!(packet.marshmallows, [[2, 3], [4, 5], [6, 7]]);
assert_eq!(prefix, &[0u8][..]);
prefix[0] = 111;
packet.temperature = 222;
assert_eq!(bytes, [111, 0xC0, 0xC0, 240, 222, 2, 3, 4, 5, 6, 7]);
// These bytes are not valid instance of `Packet`.
let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 77, 240, 0xC0, 0x10][..];
assert!(Packet::try_mut_from_suffix(bytes).is_err());§ Code Generation
Sourcefn try_ref_from_bytes_with_elems(
source: &[u8],
count: usize,
) -> Result<&Self, TryCastError<&[u8], Self>>
fn try_ref_from_bytes_with_elems( source: &[u8], count: usize, ) -> Result<&Self, TryCastError<&[u8], Self>>
Attempts to interpret 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, if source is not
appropriately aligned, or if source does not contain a valid instance
of Self, this returns Err. If Self: Unaligned,
you can infallibly discard the alignment error.
§Examples
use zerocopy::TryFromBytes;
// The only valid value of this type is the byte `0xC0`
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(u8)]
enum C0 { xC0 = 0xC0 }
// The only valid value of this type is the bytes `0xC0C0`.
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct C0C0(C0, C0);
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct Packet {
magic_number: C0C0,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
let bytes = &[0xC0, 0xC0, 240, 77, 2, 3, 4, 5, 6, 7][..];
let packet = Packet::try_ref_from_bytes_with_elems(bytes, 3).unwrap();
assert_eq!(packet.mug_size, 240);
assert_eq!(packet.temperature, 77);
assert_eq!(packet.marshmallows, [[2, 3], [4, 5], [6, 7]]);
// These bytes are not valid instance of `Packet`.
let bytes = &[0, 1, 2, 3, 4, 5, 6, 77, 240, 0xC0, 0xC0][..];
assert!(Packet::try_ref_from_bytes_with_elems(bytes, 3).is_err());Since an explicit count is provided, this method supports types with
zero-sized trailing slice elements. Methods such as try_ref_from_bytes
which do not take an explicit count do not support such types.
use core::num::NonZeroU16;
use zerocopy::*;
#[derive(TryFromBytes, Immutable, KnownLayout)]
#[repr(C)]
struct ZSTy {
leading_sized: NonZeroU16,
trailing_dst: [()],
}
let src = 0xCAFEu16.as_bytes();
let zsty = ZSTy::try_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_try_ref_from_bytes_with_elems_dynamic_size(
source: &[u8],
count: usize,
) -> Option<&format::CocoPacket> {
zerocopy::TryFromBytes::try_ref_from_bytes_with_elems(source, count).ok()
}
Assembly
bench_try_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_3
lea rcx, [2*rdx + 4]
cmp rsi, rcx
jne .LBB5_3
cmp word ptr [rax], -16192
je .LBB5_4
.LBB5_3:
xor eax, eax
mov rdx, rsi
.LBB5_4:
ret
Machine Code Analysis
Iterations: 100
Instructions: 1500
Total Cycles: 507
Total uOps: 1700
Dispatch Width: 4
uOps Per Cycle: 3.35
IPC: 2.96
Block RThroughput: 4.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 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_3
1 1 0.50 lea rcx, [2*rdx + 4]
1 1 0.33 cmp rsi, rcx
1 1 1.00 jne .LBB5_3
2 6 0.50 * cmp word ptr [rax], -16192
1 1 1.00 je .LBB5_4
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.98 4.99 - 5.03 0.50 0.50
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - 0.99 - 0.01 - - movabs rax, 9223372036854775805
- - 0.50 0.50 - - - - cmp rdx, rax
- - 1.96 - - 0.04 - - 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_3
- - 0.01 0.99 - - - - lea rcx, [2*rdx + 4]
- - 0.02 0.49 - 0.49 - - cmp rsi, rcx
- - - - - 1.00 - - jne .LBB5_3
- - - 0.51 - 0.49 0.50 0.50 cmp word ptr [rax], -16192
- - - - - 1.00 - - je .LBB5_4
- - - - - - - - xor eax, eax
- - 0.49 0.51 - - - - 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_try_ref_from_bytes_with_elems_dynamic_padding(
source: &[u8],
count: usize,
) -> Option<&format::CocoPacket> {
zerocopy::TryFromBytes::try_ref_from_bytes_with_elems(source, count).ok()
}
Assembly
bench_try_ref_from_bytes_with_elems_dynamic_padding:
mov rcx, rdx
mov edx, 3
mov rax, rcx
mul rdx
jo .LBB5_8
mov rdx, rax
cmp rax, -10
ja .LBB5_8
mov eax, edx
not eax
and eax, 3
lea r8, [rax + rdx]
add r8, 9
xor eax, eax
cmp rsi, r8
jne .LBB5_6
mov r9d, edi
and r9d, 3
jne .LBB5_6
add rdx, 9
cmp r8, rdx
jb .LBB5_6
movzx edx, word ptr [rdi]
cmp dx, -16192
cmove rsi, rcx
xor eax, eax
cmp edx, 49344
cmove rax, rdi
.LBB5_6:
mov rdx, rsi
ret
.LBB5_8:
xor eax, eax
mov rdx, rsi
ret
Machine Code Analysis
Iterations: 100
Instructions: 3300
Total Cycles: 1082
Total uOps: 3600
Dispatch Width: 4
uOps Per Cycle: 3.33
IPC: 3.05
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 1.00 jo .LBB5_8
1 1 0.33 mov rdx, rax
1 1 0.33 cmp rax, -10
1 1 1.00 ja .LBB5_8
1 1 0.33 mov eax, edx
1 1 0.33 not eax
1 1 0.33 and eax, 3
1 1 0.50 lea r8, [rax + rdx]
1 1 0.33 add r8, 9
1 0 0.25 xor eax, eax
1 1 0.33 cmp rsi, r8
1 1 1.00 jne .LBB5_6
1 1 0.33 mov r9d, edi
1 1 0.33 and r9d, 3
1 1 1.00 jne .LBB5_6
1 1 0.33 add rdx, 9
1 1 0.33 cmp r8, rdx
1 1 1.00 jb .LBB5_6
1 5 0.50 * movzx edx, word ptr [rdi]
1 1 0.33 cmp dx, -16192
2 2 0.67 cmove rsi, rcx
1 0 0.25 xor eax, eax
1 1 0.33 cmp edx, 49344
2 2 0.67 cmove rax, rdi
1 1 0.33 mov rdx, rsi
1 1 1.00 U ret
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]
- - 10.64 10.65 - 10.71 0.50 0.50
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.25 0.73 - 0.02 - - mov rcx, rdx
- - 0.74 0.03 - 0.23 - - mov edx, 3
- - 0.60 0.40 - - - - mov rax, rcx
- - 1.00 1.00 - - - - mul rdx
- - - - - 1.00 - - jo .LBB5_8
- - 0.11 0.89 - - - - mov rdx, rax
- - 0.99 0.01 - - - - cmp rax, -10
- - - - - 1.00 - - ja .LBB5_8
- - 0.01 0.89 - 0.10 - - mov eax, edx
- - 0.11 0.89 - - - - not eax
- - 0.01 0.88 - 0.11 - - and eax, 3
- - 0.01 0.99 - - - - lea r8, [rax + rdx]
- - 0.01 0.99 - - - - add r8, 9
- - - - - - - - xor eax, eax
- - - 0.99 - 0.01 - - cmp rsi, r8
- - - - - 1.00 - - jne .LBB5_6
- - 0.42 - - 0.58 - - mov r9d, edi
- - 0.53 0.01 - 0.46 - - and r9d, 3
- - - - - 1.00 - - jne .LBB5_6
- - 0.99 0.01 - - - - add rdx, 9
- - 0.99 0.01 - - - - cmp r8, rdx
- - - - - 1.00 - - jb .LBB5_6
- - - - - - 0.50 0.50 movzx edx, word ptr [rdi]
- - 0.45 0.01 - 0.54 - - cmp dx, -16192
- - 1.00 0.35 - 0.65 - - cmove rsi, rcx
- - - - - - - - xor eax, eax
- - 0.75 0.02 - 0.23 - - cmp edx, 49344
- - 1.00 0.68 - 0.32 - - cmove rax, rdi
- - 0.12 0.54 - 0.34 - - mov rdx, rsi
- - - - - 1.00 - - ret
- - - - - - - - xor eax, eax
- - 0.55 0.33 - 0.12 - - mov rdx, rsi
- - - - - 1.00 - - ret
Sourcefn try_ref_from_prefix_with_elems(
source: &[u8],
count: usize,
) -> Result<(&Self, &[u8]), TryCastError<&[u8], Self>>
fn try_ref_from_prefix_with_elems( source: &[u8], count: usize, ) -> Result<(&Self, &[u8]), TryCastError<&[u8], Self>>
Attempts to interpret the prefix of the given source as a &Self with
a 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 remaining bytes. If the length of source is less than the size
of Self with count elements, if source is not appropriately
aligned, or if the prefix of source does not contain a valid instance
of Self, this returns Err. If Self: Unaligned,
you can infallibly discard the alignment error.
§Examples
use zerocopy::TryFromBytes;
// The only valid value of this type is the byte `0xC0`
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(u8)]
enum C0 { xC0 = 0xC0 }
// The only valid value of this type is the bytes `0xC0C0`.
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct C0C0(C0, C0);
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct Packet {
magic_number: C0C0,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
let bytes = &[0xC0, 0xC0, 240, 77, 2, 3, 4, 5, 6, 7, 8][..];
let (packet, suffix) = Packet::try_ref_from_prefix_with_elems(bytes, 3).unwrap();
assert_eq!(packet.mug_size, 240);
assert_eq!(packet.temperature, 77);
assert_eq!(packet.marshmallows, [[2, 3], [4, 5], [6, 7]]);
assert_eq!(suffix, &[8u8][..]);
// These bytes are not valid instance of `Packet`.
let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 77, 240, 0xC0, 0xC0][..];
assert!(Packet::try_ref_from_prefix_with_elems(bytes, 3).is_err());Since an explicit count is provided, this method supports types with
zero-sized trailing slice elements. Methods such as try_ref_from_prefix
which do not take an explicit count do not support such types.
use core::num::NonZeroU16;
use zerocopy::*;
#[derive(TryFromBytes, Immutable, KnownLayout)]
#[repr(C)]
struct ZSTy {
leading_sized: NonZeroU16,
trailing_dst: [()],
}
let src = 0xCAFEu16.as_bytes();
let (zsty, _) = ZSTy::try_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_try_ref_from_prefix_with_elems_dynamic_size(
source: &[u8],
count: usize,
) -> Option<&format::CocoPacket> {
match zerocopy::TryFromBytes::try_ref_from_prefix_with_elems(source, count) {
Ok((packet, _rest)) => Some(packet),
_ => None,
}
}
Assembly
bench_try_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_5
lea rax, [2*rcx + 4]
cmp rax, rsi
jbe .LBB5_4
.LBB5_1:
xor eax, eax
mov edx, 1
ret
.LBB5_4:
movzx esi, word ptr [rdi]
cmp si, -16192
mov edx, 2
cmove rdx, rcx
xor eax, eax
cmp esi, 49344
cmove rax, rdi
.LBB5_5:
ret
Machine Code Analysis
Iterations: 100
Instructions: 2200
Total Cycles: 674
Total uOps: 2400
Dispatch Width: 4
uOps Per Cycle: 3.56
IPC: 3.26
Block RThroughput: 6.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.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_5
1 1 0.50 lea rax, [2*rcx + 4]
1 1 0.33 cmp rax, rsi
1 1 1.00 jbe .LBB5_4
1 0 0.25 xor eax, eax
1 1 0.33 mov edx, 1
1 1 1.00 U ret
1 5 0.50 * movzx esi, word ptr [rdi]
1 1 0.33 cmp si, -16192
1 1 0.33 mov edx, 2
2 2 0.67 cmove rdx, rcx
1 0 0.25 xor eax, eax
1 1 0.33 cmp esi, 49344
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]
- - 6.65 6.66 - 6.69 0.50 0.50
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.02 0.66 - 0.32 - - cmp rdx, rax
- - - - - 1.00 - - ja .LBB5_1
- - 0.66 0.33 - 0.01 - - mov rcx, rdx
- - - - - - - - xor edx, edx
- - 0.33 0.01 - 0.66 - - mov eax, 0
- - 0.34 0.65 - 0.01 - - test dil, 1
- - - - - 1.00 - - jne .LBB5_5
- - 0.65 0.35 - - - - lea rax, [2*rcx + 4]
- - - 1.00 - - - - cmp rax, rsi
- - - - - 1.00 - - jbe .LBB5_4
- - - - - - - - xor eax, eax
- - 0.34 0.01 - 0.65 - - mov edx, 1
- - - - - 1.00 - - ret
- - - - - - 0.50 0.50 movzx esi, word ptr [rdi]
- - 0.65 0.34 - 0.01 - - cmp si, -16192
- - 0.66 0.34 - - - - mov edx, 2
- - 1.00 0.99 - 0.01 - - cmove rdx, rcx
- - - - - - - - xor eax, eax
- - 0.34 0.66 - - - - cmp esi, 49344
- - 1.00 0.99 - 0.01 - - 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_try_ref_from_prefix_with_elems_dynamic_padding(
source: &[u8],
count: usize,
) -> Option<&format::CocoPacket> {
match zerocopy::TryFromBytes::try_ref_from_prefix_with_elems(source, count) {
Ok((packet, _rest)) => Some(packet),
_ => None,
}
}
Assembly
bench_try_ref_from_prefix_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 eax, eax
mov edx, 1
ret
.LBB5_4:
mov r8, rax
xor edx, edx
mov eax, 0
test dil, 3
je .LBB5_5
ret
.LBB5_5:
cmp r8, rsi
ja .LBB5_1
movzx esi, word ptr [rdi]
cmp si, -16192
mov edx, 2
cmove rdx, rcx
xor eax, eax
cmp esi, 49344
cmove rax, rdi
ret
Machine Code Analysis
Iterations: 100
Instructions: 3100
Total Cycles: 1008
Total uOps: 3400
Dispatch Width: 4
uOps Per Cycle: 3.37
IPC: 3.08
Block RThroughput: 8.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, 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 eax, eax
1 1 0.33 mov edx, 1
1 1 1.00 U ret
1 1 0.33 mov r8, rax
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_5
1 1 1.00 U ret
1 1 0.33 cmp r8, rsi
1 1 1.00 ja .LBB5_1
1 5 0.50 * movzx esi, word ptr [rdi]
1 1 0.33 cmp si, -16192
1 1 0.33 mov edx, 2
2 2 0.67 cmove rdx, rcx
1 0 0.25 xor eax, eax
1 1 0.33 cmp esi, 49344
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]
- - 9.98 9.99 - 10.03 0.50 0.50
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.49 0.50 - 0.01 - - mov rcx, rdx
- - 0.01 0.99 - - - - mov edx, 3
- - 0.99 0.01 - - - - 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
- - 0.99 0.01 - - - - and eax, 3
- - 0.99 0.01 - - - - add rax, rdx
- - - - - 1.00 - - jae .LBB5_4
- - - - - - - - xor eax, eax
- - - 0.98 - 0.02 - - mov edx, 1
- - - - - 1.00 - - ret
- - 0.50 0.50 - - - - mov r8, rax
- - - - - - - - xor edx, edx
- - 0.02 0.49 - 0.49 - - mov eax, 0
- - - 0.49 - 0.51 - - test dil, 3
- - - - - 1.00 - - je .LBB5_5
- - - - - 1.00 - - ret
- - 0.98 0.02 - - - - cmp r8, rsi
- - - - - 1.00 - - ja .LBB5_1
- - - - - - 0.50 0.50 movzx esi, word ptr [rdi]
- - 0.02 0.98 - - - - cmp si, -16192
- - 0.98 0.02 - - - - mov edx, 2
- - 0.50 1.00 - 0.50 - - cmove rdx, rcx
- - - - - - - - xor eax, eax
- - 0.01 0.99 - - - - cmp esi, 49344
- - 0.50 1.00 - 0.50 - - cmove rax, rdi
- - - - - 1.00 - - ret
Sourcefn try_ref_from_suffix_with_elems(
source: &[u8],
count: usize,
) -> Result<(&[u8], &Self), TryCastError<&[u8], Self>>
fn try_ref_from_suffix_with_elems( source: &[u8], count: usize, ) -> Result<(&[u8], &Self), TryCastError<&[u8], Self>>
Attempts to interpret the suffix of the given source as a &Self with
a 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 preceding bytes. If the length of source is less than the size
of Self with count elements, if the suffix of source is not
appropriately aligned, or if the suffix of source does not contain a
valid instance of Self, this returns Err. If Self: Unaligned, you can infallibly discard the alignment
error.
§Examples
use zerocopy::TryFromBytes;
// The only valid value of this type is the byte `0xC0`
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(u8)]
enum C0 { xC0 = 0xC0 }
// The only valid value of this type is the bytes `0xC0C0`.
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct C0C0(C0, C0);
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(C)]
struct Packet {
magic_number: C0C0,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
let bytes = &[123, 0xC0, 0xC0, 240, 77, 2, 3, 4, 5, 6, 7][..];
let (prefix, packet) = Packet::try_ref_from_suffix_with_elems(bytes, 3).unwrap();
assert_eq!(packet.mug_size, 240);
assert_eq!(packet.temperature, 77);
assert_eq!(packet.marshmallows, [[2, 3], [4, 5], [6, 7]]);
assert_eq!(prefix, &[123u8][..]);
// These bytes are not valid instance of `Packet`.
let bytes = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 77, 240, 0xC0, 0xC0][..];
assert!(Packet::try_ref_from_suffix_with_elems(bytes, 3).is_err());Since an explicit count is provided, this method supports types with
zero-sized trailing slice elements. Methods such as try_ref_from_prefix
which do not take an explicit count do not support such types.
use core::num::NonZeroU16;
use zerocopy::*;
#[derive(TryFromBytes, Immutable, KnownLayout)]
#[repr(C)]
struct ZSTy {
leading_sized: NonZeroU16,
trailing_dst: [()],
}
let src = 0xCAFEu16.as_bytes();
let (_, zsty) = ZSTy::try_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_try_ref_from_suffix_with_elems_dynamic_size(
source: &[u8],
count: usize,
) -> Option<&format::CocoPacket> {
match zerocopy::TryFromBytes::try_ref_from_suffix_with_elems(source, count) {
Ok((_rest, packet)) => Some(packet),
_ => None,
}
}
Assembly
bench_try_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:
lea r8, [rdi + rsi]
movzx esi, word ptr [rdi + rsi]
cmp si, -16192
mov ecx, 2
cmove rcx, rdx
xor eax, eax
cmp esi, 49344
cmove rax, r8
.LBB5_5:
mov rdx, rcx
ret
Machine Code Analysis
Iterations: 100
Instructions: 2400
Total Cycles: 1107
Total uOps: 2600
Dispatch Width: 4
uOps Per Cycle: 2.35
IPC: 2.17
Block RThroughput: 6.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
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.50 lea r8, [rdi + rsi]
1 5 0.50 * movzx esi, word ptr [rdi + rsi]
1 1 0.33 cmp si, -16192
1 1 0.33 mov ecx, 2
2 2 0.67 cmove rcx, rdx
1 0 0.25 xor eax, eax
1 1 0.33 cmp esi, 49344
2 2 0.67 cmove rax, r8
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]
- - 6.99 7.00 - 8.01 0.50 0.50
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.02 0.95 - 0.03 - - movabs rax, 9223372036854775805
- - 0.93 0.04 - 0.03 - - cmp rdx, rax
- - - - - 1.00 - - ja .LBB5_1
- - 0.96 0.04 - - - - lea r8d, [rsi + rdi]
- - - - - - - - xor ecx, ecx
- - 0.95 0.02 - 0.03 - - mov eax, 0
- - 0.95 0.05 - - - - test r8b, 1
- - - - - 1.00 - - jne .LBB5_5
- - 0.06 0.94 - - - - lea rax, [2*rdx + 4]
- - 0.93 0.07 - - - - sub rsi, rax
- - - - - 1.00 - - jae .LBB5_4
- - - - - - - - xor eax, eax
- - 0.03 0.95 - 0.02 - - mov edx, 1
- - - - - 1.00 - - ret
- - 0.97 0.03 - - - - lea r8, [rdi + rsi]
- - - - - - 0.50 0.50 movzx esi, word ptr [rdi + rsi]
- - 0.03 0.97 - - - - cmp si, -16192
- - 0.05 0.94 - 0.01 - - mov ecx, 2
- - 0.06 0.98 - 0.96 - - cmove rcx, rdx
- - - - - - - - xor eax, eax
- - 0.97 0.03 - - - - cmp esi, 49344
- - 0.06 0.96 - 0.98 - - cmove rax, r8
- - 0.02 0.03 - 0.95 - - 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_try_ref_from_suffix_with_elems_dynamic_padding(
source: &[u8],
count: usize,
) -> Option<&format::CocoPacket> {
match zerocopy::TryFromBytes::try_ref_from_suffix_with_elems(source, count) {
Ok((_rest, packet)) => Some(packet),
_ => None,
}
}
Assembly
bench_try_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
lea rax, [rdi + rsi]
movzx esi, word ptr [rdi + rsi]
cmp si, -16192
mov edx, 2
cmove rdx, rcx
xor r8d, r8d
cmp esi, 49344
cmove r8, rax
mov rax, r8
ret
Machine Code Analysis
Iterations: 100
Instructions: 3500
Total Cycles: 1144
Total uOps: 3800
Dispatch Width: 4
uOps Per Cycle: 3.32
IPC: 3.06
Block RThroughput: 9.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, 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.50 lea rax, [rdi + rsi]
1 5 0.50 * movzx esi, word ptr [rdi + rsi]
1 1 0.33 cmp si, -16192
1 1 0.33 mov edx, 2
2 2 0.67 cmove rdx, rcx
1 0 0.25 xor r8d, r8d
1 1 0.33 cmp esi, 49344
2 2 0.67 cmove r8, rax
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.32 11.32 - 11.36 0.50 0.50
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.32 0.67 - 0.01 - - mov rcx, rdx
- - 0.66 0.18 - 0.16 - - mov edx, 3
- - 1.00 - - - - - mov rax, rcx
- - 1.00 1.00 - - - - mul rdx
- - - - - 1.00 - - jo .LBB5_1
- - 0.01 0.99 - - - - cmp rax, -10
- - - - - 1.00 - - ja .LBB5_1
- - 0.99 0.01 - - - - lea rdx, [rax + 9]
- - 0.01 0.99 - - - - not eax
- - 0.02 0.98 - - - - and eax, 3
- - 0.02 0.98 - - - - add rax, rdx
- - - - - 1.00 - - jae .LBB5_4
- - - - - - - - xor r8d, r8d
- - 0.66 0.01 - 0.33 - - mov edx, 1
- - 0.50 - - 0.50 - - mov rax, r8
- - - - - 1.00 - - ret
- - 0.99 0.01 - - - - lea r9d, [rsi + rdi]
- - - - - - - - xor edx, edx
- - 0.50 0.32 - 0.18 - - mov r8d, 0
- - 0.16 0.17 - 0.67 - - test r9b, 3
- - - - - 1.00 - - je .LBB5_5
- - 0.33 0.33 - 0.34 - - mov rax, r8
- - - - - 1.00 - - ret
- - - 0.51 - 0.49 - - sub rsi, rax
- - - - - 1.00 - - jb .LBB5_1
- - 0.16 0.84 - - - - lea rax, [rdi + rsi]
- - - - - - 0.50 0.50 movzx esi, word ptr [rdi + rsi]
- - 0.02 0.98 - - - - cmp si, -16192
- - 1.00 - - - - - mov edx, 2
- - 0.99 0.84 - 0.17 - - cmove rdx, rcx
- - - - - - - - xor r8d, r8d
- - 0.98 - - 0.02 - - cmp esi, 49344
- - 0.99 0.52 - 0.49 - - cmove r8, rax
- - 0.01 0.99 - - - - mov rax, r8
- - - - - 1.00 - - ret
Sourcefn try_mut_from_bytes_with_elems(
source: &mut [u8],
count: usize,
) -> Result<&mut Self, TryCastError<&mut [u8], Self>>
fn try_mut_from_bytes_with_elems( source: &mut [u8], count: usize, ) -> Result<&mut Self, TryCastError<&mut [u8], Self>>
Attempts to interpret 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, if source is not
appropriately aligned, or if source does not contain a valid instance
of Self, this returns Err. If Self: Unaligned,
you can infallibly discard the alignment error.
§Examples
use zerocopy::TryFromBytes;
// The only valid value of this type is the byte `0xC0`
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(u8)]
enum C0 { xC0 = 0xC0 }
// The only valid value of this type is the bytes `0xC0C0`.
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(C)]
struct C0C0(C0, C0);
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(C, packed)]
struct Packet {
magic_number: C0C0,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
let bytes = &mut [0xC0, 0xC0, 240, 77, 2, 3, 4, 5, 6, 7][..];
let packet = Packet::try_mut_from_bytes_with_elems(bytes, 3).unwrap();
assert_eq!(packet.mug_size, 240);
assert_eq!(packet.temperature, 77);
assert_eq!(packet.marshmallows, [[2, 3], [4, 5], [6, 7]]);
packet.temperature = 111;
assert_eq!(bytes, [0xC0, 0xC0, 240, 111, 2, 3, 4, 5, 6, 7]);
// These bytes are not valid instance of `Packet`.
let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 77, 240, 0xC0, 0xC0][..];
assert!(Packet::try_mut_from_bytes_with_elems(bytes, 3).is_err());Since an explicit count is provided, this method supports types with
zero-sized trailing slice elements. Methods such as try_mut_from_bytes
which do not take an explicit count do not support such types.
use core::num::NonZeroU16;
use zerocopy::*;
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(C, packed)]
struct ZSTy {
leading_sized: NonZeroU16,
trailing_dst: [()],
}
let mut src = 0xCAFEu16;
let src = src.as_mut_bytes();
let zsty = ZSTy::try_mut_from_bytes_with_elems(src, 42).unwrap();
assert_eq!(zsty.trailing_dst.len(), 42);§ Code Generation
Sourcefn try_mut_from_prefix_with_elems(
source: &mut [u8],
count: usize,
) -> Result<(&mut Self, &mut [u8]), TryCastError<&mut [u8], Self>>
fn try_mut_from_prefix_with_elems( source: &mut [u8], count: usize, ) -> Result<(&mut Self, &mut [u8]), TryCastError<&mut [u8], Self>>
Attempts to interpret the prefix of the given source as a &mut Self
with a 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 remaining bytes. If the length of source is less than the size
of Self with count elements, if source is not appropriately
aligned, or if the prefix of source does not contain a valid instance
of Self, this returns Err. If Self: Unaligned,
you can infallibly discard the alignment error.
§Examples
use zerocopy::TryFromBytes;
// The only valid value of this type is the byte `0xC0`
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(u8)]
enum C0 { xC0 = 0xC0 }
// The only valid value of this type is the bytes `0xC0C0`.
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(C)]
struct C0C0(C0, C0);
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(C, packed)]
struct Packet {
magic_number: C0C0,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
let bytes = &mut [0xC0, 0xC0, 240, 77, 2, 3, 4, 5, 6, 7, 8][..];
let (packet, suffix) = Packet::try_mut_from_prefix_with_elems(bytes, 3).unwrap();
assert_eq!(packet.mug_size, 240);
assert_eq!(packet.temperature, 77);
assert_eq!(packet.marshmallows, [[2, 3], [4, 5], [6, 7]]);
assert_eq!(suffix, &[8u8][..]);
packet.temperature = 111;
suffix[0] = 222;
assert_eq!(bytes, [0xC0, 0xC0, 240, 111, 2, 3, 4, 5, 6, 7, 222]);
// These bytes are not valid instance of `Packet`.
let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 77, 240, 0xC0, 0xC0][..];
assert!(Packet::try_mut_from_prefix_with_elems(bytes, 3).is_err());Since an explicit count is provided, this method supports types with
zero-sized trailing slice elements. Methods such as try_mut_from_prefix
which do not take an explicit count do not support such types.
use core::num::NonZeroU16;
use zerocopy::*;
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(C, packed)]
struct ZSTy {
leading_sized: NonZeroU16,
trailing_dst: [()],
}
let mut src = 0xCAFEu16;
let src = src.as_mut_bytes();
let (zsty, _) = ZSTy::try_mut_from_prefix_with_elems(src, 42).unwrap();
assert_eq!(zsty.trailing_dst.len(), 42);§ Code Generation
Sourcefn try_mut_from_suffix_with_elems(
source: &mut [u8],
count: usize,
) -> Result<(&mut [u8], &mut Self), TryCastError<&mut [u8], Self>>
fn try_mut_from_suffix_with_elems( source: &mut [u8], count: usize, ) -> Result<(&mut [u8], &mut Self), TryCastError<&mut [u8], Self>>
Attempts to interpret the suffix of the given source as a &mut Self
with a 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 preceding bytes. If the length of source is less than the size
of Self with count elements, if the suffix of source is not
appropriately aligned, or if the suffix of source does not contain a
valid instance of Self, this returns Err. If Self: Unaligned, you can infallibly discard the alignment
error.
§Examples
use zerocopy::TryFromBytes;
// The only valid value of this type is the byte `0xC0`
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(u8)]
enum C0 { xC0 = 0xC0 }
// The only valid value of this type is the bytes `0xC0C0`.
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(C)]
struct C0C0(C0, C0);
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(C, packed)]
struct Packet {
magic_number: C0C0,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
let bytes = &mut [123, 0xC0, 0xC0, 240, 77, 2, 3, 4, 5, 6, 7][..];
let (prefix, packet) = Packet::try_mut_from_suffix_with_elems(bytes, 3).unwrap();
assert_eq!(packet.mug_size, 240);
assert_eq!(packet.temperature, 77);
assert_eq!(packet.marshmallows, [[2, 3], [4, 5], [6, 7]]);
assert_eq!(prefix, &[123u8][..]);
prefix[0] = 111;
packet.temperature = 222;
assert_eq!(bytes, [111, 0xC0, 0xC0, 240, 222, 2, 3, 4, 5, 6, 7]);
// These bytes are not valid instance of `Packet`.
let bytes = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 77, 240, 0xC0, 0xC0][..];
assert!(Packet::try_mut_from_suffix_with_elems(bytes, 3).is_err());Since an explicit count is provided, this method supports types with
zero-sized trailing slice elements. Methods such as try_mut_from_prefix
which do not take an explicit count do not support such types.
use core::num::NonZeroU16;
use zerocopy::*;
#[derive(TryFromBytes, IntoBytes, KnownLayout)]
#[repr(C, packed)]
struct ZSTy {
leading_sized: NonZeroU16,
trailing_dst: [()],
}
let mut src = 0xCAFEu16;
let src = src.as_mut_bytes();
let (_, zsty) = ZSTy::try_mut_from_suffix_with_elems(src, 42).unwrap();
assert_eq!(zsty.trailing_dst.len(), 42);§ Code Generation
Sourcefn try_read_from_bytes(source: &[u8]) -> Result<Self, TryReadError<&[u8], Self>>where
Self: Sized,
fn try_read_from_bytes(source: &[u8]) -> Result<Self, TryReadError<&[u8], Self>>where
Self: Sized,
Attempts to read the given source as a Self.
If source.len() != size_of::<Self>() or the bytes are not a valid
instance of Self, this returns Err.
§Examples
use zerocopy::TryFromBytes;
// The only valid value of this type is the byte `0xC0`
#[derive(TryFromBytes)]
#[repr(u8)]
enum C0 { xC0 = 0xC0 }
// The only valid value of this type is the bytes `0xC0C0`.
#[derive(TryFromBytes)]
#[repr(C)]
struct C0C0(C0, C0);
#[derive(TryFromBytes)]
#[repr(C)]
struct Packet {
magic_number: C0C0,
mug_size: u8,
temperature: u8,
}
let bytes = &[0xC0, 0xC0, 240, 77][..];
let packet = Packet::try_read_from_bytes(bytes).unwrap();
assert_eq!(packet.mug_size, 240);
assert_eq!(packet.temperature, 77);
// These bytes are not valid instance of `Packet`.
let bytes = &mut [0x10, 0xC0, 240, 77][..];
assert!(Packet::try_read_from_bytes(bytes).is_err());§Performance Considerations
In this version of zerocopy, this method reads the source into a
well-aligned stack allocation and then validates that the allocation
is a valid Self. This ensures that validation can be performed using
aligned reads (which carry a performance advantage over unaligned reads
on many platforms) at the cost of an unconditional copy.
§ 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_try_read_from_bytes_static_size(source: &[u8]) -> Option<format::CocoPacket> {
zerocopy::TryFromBytes::try_read_from_bytes(source).ok()
}
Assembly
bench_try_read_from_bytes_static_size:
mov ax, -16191
cmp rsi, 6
jne .LBB5_1
mov ecx, dword ptr [rdi]
movzx edx, cx
cmp edx, 49344
jne .LBB5_4
movzx eax, word ptr [rdi + 4]
shl rax, 32
or rcx, rax
shr rcx, 16
mov ax, -16192
.LBB5_4:
shl rcx, 16
movzx eax, ax
or rax, rcx
ret
.LBB5_1:
shl rcx, 16
movzx eax, ax
or rax, rcx
ret
Machine Code Analysis
Iterations: 100
Instructions: 2000
Total Cycles: 608
Total uOps: 2000
Dispatch Width: 4
uOps Per Cycle: 3.29
IPC: 3.29
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 ax, -16191
1 1 0.33 cmp rsi, 6
1 1 1.00 jne .LBB5_1
1 5 0.50 * mov ecx, dword ptr [rdi]
1 1 0.33 movzx edx, cx
1 1 0.33 cmp edx, 49344
1 1 1.00 jne .LBB5_4
1 5 0.50 * movzx eax, word ptr [rdi + 4]
1 1 0.50 shl rax, 32
1 1 0.33 or rcx, rax
1 1 0.50 shr rcx, 16
1 1 0.33 mov ax, -16192
1 1 0.50 shl rcx, 16
1 1 0.33 movzx eax, ax
1 1 0.33 or rax, rcx
1 1 1.00 U ret
1 1 0.50 shl rcx, 16
1 1 0.33 movzx eax, ax
1 1 0.33 or 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]
- - 5.99 5.99 - 6.02 1.00 1.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - 0.99 - 0.01 - - mov ax, -16191
- - - 0.01 - 0.99 - - cmp rsi, 6
- - - - - 1.00 - - jne .LBB5_1
- - - - - - - 1.00 mov ecx, dword ptr [rdi]
- - 0.98 - - 0.02 - - movzx edx, cx
- - 0.99 0.01 - - - - cmp edx, 49344
- - - - - 1.00 - - jne .LBB5_4
- - - - - - 1.00 - movzx eax, word ptr [rdi + 4]
- - 0.01 - - 0.99 - - shl rax, 32
- - 0.02 0.98 - - - - or rcx, rax
- - 1.00 - - - - - shr rcx, 16
- - 0.99 0.01 - - - - mov ax, -16192
- - 1.00 - - - - - shl rcx, 16
- - - 1.00 - - - - movzx eax, ax
- - - 1.00 - - - - or rax, rcx
- - - - - 1.00 - - ret
- - 1.00 - - - - - shl rcx, 16
- - - 1.00 - - - - movzx eax, ax
- - - 0.99 - 0.01 - - or rax, rcx
- - - - - 1.00 - - ret
Sourcefn try_read_from_prefix(
source: &[u8],
) -> Result<(Self, &[u8]), TryReadError<&[u8], Self>>where
Self: Sized,
fn try_read_from_prefix(
source: &[u8],
) -> Result<(Self, &[u8]), TryReadError<&[u8], Self>>where
Self: Sized,
Attempts to read a 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>() or the bytes are not a valid instance
of Self, it returns Err.
§Examples
use zerocopy::TryFromBytes;
// The only valid value of this type is the byte `0xC0`
#[derive(TryFromBytes)]
#[repr(u8)]
enum C0 { xC0 = 0xC0 }
// The only valid value of this type is the bytes `0xC0C0`.
#[derive(TryFromBytes)]
#[repr(C)]
struct C0C0(C0, C0);
#[derive(TryFromBytes)]
#[repr(C)]
struct Packet {
magic_number: C0C0,
mug_size: u8,
temperature: u8,
}
// These are more bytes than are needed to encode a `Packet`.
let bytes = &[0xC0, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5, 6][..];
let (packet, suffix) = Packet::try_read_from_prefix(bytes).unwrap();
assert_eq!(packet.mug_size, 240);
assert_eq!(packet.temperature, 77);
assert_eq!(suffix, &[0u8, 1, 2, 3, 4, 5, 6][..]);
// These bytes are not valid instance of `Packet`.
let bytes = &[0x10, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5, 6][..];
assert!(Packet::try_read_from_prefix(bytes).is_err());§Performance Considerations
In this version of zerocopy, this method reads the source into a
well-aligned stack allocation and then validates that the allocation
is a valid Self. This ensures that validation can be performed using
aligned reads (which carry a performance advantage over unaligned reads
on many platforms) at the cost of an unconditional copy.
§ 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_try_read_from_prefix_static_size(source: &[u8]) -> Option<format::CocoPacket> {
match zerocopy::TryFromBytes::try_read_from_prefix(source) {
Ok((packet, _rest)) => Some(packet),
_ => None,
}
}
Assembly
bench_try_read_from_prefix_static_size:
mov eax, 49345
cmp rsi, 6
jb .LBB5_2
mov eax, dword ptr [rdi]
movzx ecx, word ptr [rdi + 4]
shl rcx, 32
or rcx, rax
movzx eax, cx
and rcx, -65536
or rcx, 49344
cmp eax, 49344
mov eax, 49345
cmove rax, rcx
.LBB5_2:
ret
Machine Code Analysis
Iterations: 100
Instructions: 1400
Total Cycles: 442
Total uOps: 1500
Dispatch Width: 4
uOps Per Cycle: 3.39
IPC: 3.17
Block RThroughput: 3.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 mov eax, 49345
1 1 0.33 cmp rsi, 6
1 1 1.00 jb .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.33 movzx eax, cx
1 1 0.33 and rcx, -65536
1 1 0.33 or rcx, 49344
1 1 0.33 cmp eax, 49344
1 1 0.33 mov eax, 49345
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]
- - 4.33 4.33 - 4.34 1.00 1.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.65 0.01 - 0.34 - - mov eax, 49345
- - 0.01 0.33 - 0.66 - - cmp rsi, 6
- - - - - 1.00 - - jb .LBB5_2
- - - - - - - 1.00 mov eax, dword ptr [rdi]
- - - - - - 1.00 - movzx ecx, word ptr [rdi + 4]
- - 0.65 - - 0.35 - - shl rcx, 32
- - - 0.67 - 0.33 - - or rcx, rax
- - 0.01 0.99 - - - - movzx eax, cx
- - 0.99 0.01 - - - - and rcx, -65536
- - 0.01 0.99 - - - - or rcx, 49344
- - 0.99 0.01 - - - - cmp eax, 49344
- - 0.02 0.33 - 0.65 - - mov eax, 49345
- - 1.00 0.99 - 0.01 - - cmove rax, rcx
- - - - - 1.00 - - ret
Sourcefn try_read_from_suffix(
source: &[u8],
) -> Result<(&[u8], Self), TryReadError<&[u8], Self>>where
Self: Sized,
fn try_read_from_suffix(
source: &[u8],
) -> Result<(&[u8], Self), TryReadError<&[u8], Self>>where
Self: Sized,
Attempts to read a 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>() or the bytes are not a valid instance
of Self, it returns Err.
§Examples
use zerocopy::TryFromBytes;
// The only valid value of this type is the byte `0xC0`
#[derive(TryFromBytes)]
#[repr(u8)]
enum C0 { xC0 = 0xC0 }
// The only valid value of this type is the bytes `0xC0C0`.
#[derive(TryFromBytes)]
#[repr(C)]
struct C0C0(C0, C0);
#[derive(TryFromBytes)]
#[repr(C)]
struct Packet {
magic_number: C0C0,
mug_size: u8,
temperature: u8,
}
// These are more bytes than are needed to encode a `Packet`.
let bytes = &[0, 1, 2, 3, 4, 5, 0xC0, 0xC0, 240, 77][..];
let (prefix, packet) = Packet::try_read_from_suffix(bytes).unwrap();
assert_eq!(packet.mug_size, 240);
assert_eq!(packet.temperature, 77);
assert_eq!(prefix, &[0u8, 1, 2, 3, 4, 5][..]);
// These bytes are not valid instance of `Packet`.
let bytes = &[0, 1, 2, 3, 4, 5, 0x10, 0xC0, 240, 77][..];
assert!(Packet::try_read_from_suffix(bytes).is_err());§Performance Considerations
In this version of zerocopy, this method reads the source into a
well-aligned stack allocation and then validates that the allocation
is a valid Self. This ensures that validation can be performed using
aligned reads (which carry a performance advantage over unaligned reads
on many platforms) at the cost of an unconditional copy.
§ 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_try_read_from_suffix_static_size(source: &[u8]) -> Option<format::CocoPacket> {
match zerocopy::TryFromBytes::try_read_from_suffix(source) {
Ok((_rest, packet)) => Some(packet),
_ => None,
}
}
Assembly
bench_try_read_from_suffix_static_size:
mov eax, 49345
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
movzx edx, cx
xor eax, eax
cmp edx, 49344
cmovne rcx, rsi
sete al
and rcx, -65536
xor rax, 49345
or rax, rcx
.LBB5_2:
ret
Machine Code Analysis
Iterations: 100
Instructions: 1600
Total Cycles: 478
Total uOps: 1700
Dispatch Width: 4
uOps Per Cycle: 3.56
IPC: 3.35
Block RThroughput: 4.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 eax, 49345
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.33 movzx edx, cx
1 0 0.25 xor eax, eax
1 1 0.33 cmp edx, 49344
2 2 0.67 cmovne rcx, rsi
1 1 0.50 sete al
1 1 0.33 and rcx, -65536
1 1 0.33 xor rax, 49345
1 1 0.33 or 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]
- - 4.66 4.66 - 4.68 1.00 1.00
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.32 0.01 - 0.67 - - mov eax, 49345
- - 0.62 0.02 - 0.36 - - 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.37 - - 0.63 - - shl rcx, 32
- - 0.99 0.01 - - - - or rcx, rax
- - 1.00 - - - - - movzx edx, cx
- - - - - - - - xor eax, eax
- - 0.35 0.64 - 0.01 - - cmp edx, 49344
- - 1.00 1.00 - - - - cmovne rcx, rsi
- - - - - 1.00 - - sete al
- - 0.01 0.99 - - - - and rcx, -65536
- - - 1.00 - - - - xor rax, 49345
- - - 0.99 - 0.01 - - or 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.