macro_rules! format_struct {
($($(#[$m:meta])* $vis:vis struct $endian:tt $name:ident {
$($(#[doc = $field_doc:literal])* $field_vis:vis $field_name:ident: $ty:tt),*,
})+) => { ... };
(@impl_conv $name:tt$(<$gen:ident$(: $trait:ident)?$(, const $const_gen:ident: usize)?>)? size $size_expr:expr) => { ... };
(@endian_type little) => { ... };
(@endian_type big) => { ... };
(@endian_type dynamic) => { ... };
(@wrapper_type $endian:tt [$ty:ident; $($n:tt)+]) => { ... };
(@wrapper_type $endian:tt u8) => { ... };
(@wrapper_type $endian:tt i8) => { ... };
(@wrapper_type $endian:tt u16) => { ... };
(@wrapper_type $endian:tt i16) => { ... };
(@wrapper_type $endian:tt u32) => { ... };
(@wrapper_type $endian:tt i32) => { ... };
(@wrapper_type $endian:tt u64) => { ... };
(@wrapper_type $endian:tt i64) => { ... };
(@wrapper_type $endian:tt u128) => { ... };
(@wrapper_type $endian:tt i128) => { ... };
(@wrapper_type $endian:tt $ty:ty) => { ... };
}
Expand description
Defines a structure that can be transmuted from/into a byte slice for parsing/constructing binary formats in a zero-copy way.
The macro achieves this by replacing all multibyte integers with wrapper types that are byte arrays internally and only allowing integer and fixed size array fields in a structure.
Accepted syntax is similar to a standard structure definition in Rust with some differences:
- The
struct
keyword is followed by eitherlittle
orbig
keywords if you want fixed endianness ordynamic
keyword if you want dynamic endianness. - Fields of the generated structure may only have documentation meta, other meta types are disallowed.
§Examples
format_struct! {
/// A little-endian test structure.
#[derive(Default, Clone)]
pub struct little Test {
/// this byte is public
pub byte: u8,
short: u16,
word: i32,
dword: i64,
qword: u128,
byte_arr: [u8; 16],
}
}
It is also possible to define multiple structures in one macro invocation:
format_struct! {
struct little Foo {
byte: u8,
}
struct big Bar {
a: u64,
}
pub struct little Baz {
z: [u8; 33],
}
}
§Allowed field types
Integer types (u8
, u16
, u32
, u64
, u128
and their signed counterparts) are allowed. Multi-byte integer
types are converted to wrapper types with alignment 1 (u16 to U16
, u32 to U32
, …).
Statically-sized arrays of types implementing ReprByteSlice
are allowed as well making it possible to nest
structures defined by the format_struct
macros.
Example:
format_struct! {
#[derive(Clone, Debug)]
struct little TopLevel {
foo: u32,
nested: Nested,
}
#[derive(Clone, Debug)]
struct big Nested {
bar: u32,
}
}
Note that nested structures are only affected by their endian specifiers. In the example above field bar
of
the Nested
structure will be big-endian even when wrapped into the TopLevel
structure.
§Layout
The fields in the structure are laid out in declaration order without any padding. That means that the following structure will take 7 bytes instead of 16 you might expect:
format_struct! {
struct little SmallStruct {
byte: u8,
dword: u64,
}
}