Macro format_struct

Source
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 either little or big keywords if you want fixed endianness or dynamic 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,
    }
}