Format
Types which derive Archive
generate an archived version of the type where:
- Member types are replaced with their archived counterparts
- Structs are
#[repr(C)]
. - Enums have
#[repr(N)]
where N isu8
,u16
,u32
,u64
, oru128
, choosing the smallest possible type that can represent all of the variants. - All primitives are replaced with versions which have stable, well-defined layouts and byte orders.
For example, a struct like:
#![allow(unused)] fn main() { struct Example { a: u32, b: String, c: Box<(u32, String)>, } }
Would have the archived counterpart:
#![allow(unused)] fn main() { #[repr(C)] struct ArchivedExample { a: u32_le, b: ArchivedString, c: ArchivedBox<ArchivedTuple2<u32_le, ArchivedString>>, } }
With the little_endian
feature enabled.
rkyv provides Archive
implementations for common standard library types by default. In general,
they follow the same format as derived implementations but may differ in some cases. For example,
ArchivedString
performs a small string optimization which helps reduce memory use.
Format control
rkyv provides sets of feature flags which control the basic properties of archived primitives:
- Endianness:
little_endian
/big_endian
control the endianness of the underlying data - Alignment:
aligned
/unaligned
control whether primitive types have alignment greater than 1. - Pointer width:
pointer_width_16
/pointer_width_32
/pointer_width_64
control the size of relative pointer offsets. This allows trading off space for a larger maximum buffer size.
When left unspecified, rkyv chooses these defaults for format control:
- Little-endian
- Aligned
- 32-bit relative pointers
Object order
rkyv lays out subobjects in depth-first order from the leaves to the root. This means that the root object is stored at the end of the buffer, not the beginning. For example, this tree:
a
/ \
b c
/ \
d e
would be laid out like this in the buffer:
b d e c a
from this serialization order:
a -> b
a -> c -> d
a -> c -> e
a -> c
a
This deterministic layout means that you don't need to store the position of the root object in most
cases. As long as your buffer ends right at the end of your root object, you can use access
with
your buffer.