macro_rules! try_transmute_ref {
($e:expr) => { ... };
}Expand description
Conditionally transmutes a mutable or immutable reference of one type to an immutable reference of another type of the same size and compatible alignment.
Note that while the value of the referent is checked for validity at
runtime, the size and alignment are checked at compile time. For
conversions which are fallible with respect to size and alignment, see the
methods on TryFromBytes.
This macro behaves like an invocation of this function:
fn try_transmute_ref<Src, Dst>(src: &Src) -> Result<&Dst, ValidityError<&Src, Dst>>
where
Src: IntoBytes + Immutable + ?Sized,
Dst: TryFromBytes + Immutable + ?Sized,
align_of::<Src>() >= align_of::<Dst>(),
size_compatible::<Src, Dst>(),
{
...
}The types Src and Dst are inferred from the calling context; they cannot
be explicitly specified in the macro invocation.
§Size compatibility
try_transmute_ref! supports transmuting between Sized types, between
unsized (i.e., ?Sized) types, and from a Sized type to an unsized type.
It supports any transmutation that preserves the number of bytes of the
referent, even if doing so requires updating the metadata stored in an
unsized “fat” reference:
let src: &[[u8; 2]] = &[[0, 1], [2, 3]][..];
let dst: &[u8] = try_transmute_ref!(src).unwrap();
assert_eq!(src.len(), 2);
assert_eq!(dst.len(), 4);
assert_eq!(dst, [0, 1, 2, 3]);
assert_eq!(size_of_val(src), size_of_val(dst));§Examples
Transmuting between Sized types:
// 0u8 → bool = false
assert_eq!(try_transmute_ref!(&0u8), Ok(&false));
// 1u8 → bool = true
assert_eq!(try_transmute_ref!(&1u8), Ok(&true));
// 2u8 → bool = error
assert!(matches!(
try_transmute_ref!(&2u8),
Result::<&bool, _>::Err(ValidityError { .. })
));Transmuting between unsized types:
#[derive(KnownLayout, FromBytes, IntoBytes, Immutable)]
#[repr(C)]
struct SliceDst<T, U> {
t: T,
u: [U],
}
type Src = SliceDst<u32, u16>;
type Dst = SliceDst<u16, bool>;
let src = Src::ref_from_bytes(&[0, 1, 0, 1, 0, 1, 0, 1]).unwrap();
let dst: &Dst = try_transmute_ref!(src).unwrap();
assert_eq!(src.t.as_bytes(), [0, 1, 0, 1]);
assert_eq!(src.u.len(), 2);
assert_eq!(src.u.as_bytes(), [0, 1, 0, 1]);
assert_eq!(dst.t.as_bytes(), [0, 1]);
assert_eq!(dst.u, [false, true, false, true, false, true]);§ 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
use zerocopy_derive::*;
#[path = "formats/coco_static_size.rs"]
mod format;
#[derive(IntoBytes, KnownLayout, Immutable)]
#[repr(C, align(2))]
struct MinimalViableSource {
bytes: [u8; 6],
}
#[unsafe(no_mangle)]
fn bench_try_transmute_ref_static_size(
source: &MinimalViableSource,
) -> Option<&format::CocoPacket> {
zerocopy::try_transmute_ref!(source).ok()
}
Assembly
bench_try_transmute_ref_static_size:
xor eax, eax
cmp word ptr [rdi], -16192
cmove rax, rdi
ret
Machine Code Analysis
Iterations: 100
Instructions: 400
Total Cycles: 160
Total uOps: 600
Dispatch Width: 4
uOps Per Cycle: 3.75
IPC: 2.50
Block RThroughput: 1.5
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1] [2] [3] [4] [5] [6] Instructions:
1 0 0.25 xor eax, eax
2 6 0.50 * cmp word ptr [rdi], -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]
- - 1.02 1.48 - 1.50 0.50 0.50
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - - - - - - - xor eax, eax
- - 0.02 0.49 - 0.49 0.50 0.50 cmp word ptr [rdi], -16192
- - 1.00 0.99 - 0.01 - - cmove rax, rdi
- - - - - 1.00 - - ret
Unsized
Format
use zerocopy_derive::*;
// The only valid value of this type are the bytes `0xC0C0`.
#[derive(TryFromBytes, KnownLayout, Immutable)]
#[repr(u16)]
pub enum C0C0 {
_XC0C0 = 0xC0C0,
}
#[derive(FromBytes, KnownLayout, Immutable)]
#[repr(C, align(2))]
pub struct Packet<Magic> {
magic_number: Magic,
mug_size: u8,
temperature: u8,
marshmallows: [[u8; 2]],
}
/// A packet begining with the magic number `0xC0C0`.
pub type CocoPacket = Packet<C0C0>;
/// A packet beginning with any two initialized bytes.
pub type LocoPacket = Packet<[u8; 2]>;
Benchmark
use zerocopy_derive::*;
#[path = "formats/coco_dynamic_size.rs"]
mod format;
#[derive(IntoBytes, KnownLayout, Immutable)]
#[repr(C, align(2))]
struct MinimalViableSource {
header: [u8; 6],
trailer: [[u8; 2]],
}
#[unsafe(no_mangle)]
fn bench_try_transmute_ref_dynamic_size(
source: &MinimalViableSource,
) -> Option<&format::CocoPacket> {
zerocopy::try_transmute_ref!(source).ok()
}
Assembly
bench_try_transmute_ref_dynamic_size:
lea rdx, [rsi + 1]
xor eax, eax
cmp word ptr [rdi], -16192
cmove rax, rdi
ret
Machine Code Analysis
Iterations: 100
Instructions: 500
Total Cycles: 209
Total uOps: 700
Dispatch Width: 4
uOps Per Cycle: 3.35
IPC: 2.39
Block RThroughput: 1.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.50 lea rdx, [rsi + 1]
1 0 0.25 xor eax, eax
2 6 0.50 * cmp word ptr [rdi], -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]
- - 1.50 1.51 - 1.99 0.50 0.50
Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
- - 0.51 0.49 - - - - lea rdx, [rsi + 1]
- - - - - - - - xor eax, eax
- - - 0.02 - 0.98 0.50 0.50 cmp word ptr [rdi], -16192
- - 0.99 1.00 - 0.01 - - cmove rax, rdi
- - - - - 1.00 - - ret