Macro binary_layout::prelude::binary_layout

source ·
macro_rules! binary_layout {
    ($name: ident, $endianness: ident, {$($field_name: ident : $field_type: ty $(as $underlying_type: ty)?),* $(,)?}) => { ... };
    (@impl_fields $endianness: ty, $offset_accumulator: expr, {}) => { ... };
    (@impl_fields $endianness: ty, $offset_accumulator: expr, {$name: ident : $type: ty as $underlying_type: ty $(, $($tail:tt)*)?}) => { ... };
    (@impl_fields $endianness: ty, $offset_accumulator: expr, {$name: ident : $type: ty $(, $($tail:tt)*)?}) => { ... };
    (@impl_view_asref {}) => { ... };
    (@impl_view_asref {$name: ident $(, $name_tail: ident)*}) => { ... };
    (@impl_view_asmut {}) => { ... };
    (@impl_view_asmut {$name: ident $(, $name_tail: ident)*}) => { ... };
    (@impl_view_into {}) => { ... };
    (@impl_view_into {$name: ident $(, $name_tail: ident)*}) => { ... };
}
Expand description

This macro defines a data layout. Given such a layout, the Field or FieldView APIs can be used to access data based on it.

Data layouts define

  • a name for the layout
  • and endianness for its fields (BigEndian or LittleEndian)
  • and an ordered collection of typed fields.

See supported field types for a list of supported field types.

§API

binary_layout!(<<Name>>, <<Endianness>>, {
  <<FieldName>>: <<FieldType>>,
  <<FieldName>>: <<FieldType>>,
  ...
});

§Field names

Field names can be any valid Rust identifiers, but it is recommended to avoid names that contain storage, into_ or _mut. This is because the binary_layout! macro creates a View class with several accessors for each field that contain those identifier parts.

§Example

use binary_layout::prelude::*;

binary_layout!(icmp_packet, BigEndian, {
  packet_type: u8,
  code: u8,
  checksum: u16,
  rest_of_header: [u8; 4],
  data_section: [u8], // open ended byte array, matches until the end of the packet
});

§Generated code

See icmp_packet for an example.

This macro will define a module for you with several members:

  • For each field, there will be a struct containing
    • metadata like OFFSET and SIZE as rust consts
    • data accessors for the Field API
  • The module will also contain a View struct that offers the FieldView API.

This macro will also generate rustdoc documentation for everything it generates. One of the best ways to figure out how to use the generated layouts is to read the rustdoc documentation that was generated for them.

§Metadata Example

use binary_layout::prelude::*;

binary_layout!(my_layout, LittleEndian, {
  field1: u16,
  field2: u32,
});
assert_eq!(2, my_layout::field2::OFFSET);
assert_eq!(Some(4), my_layout::field2::SIZE);

§struct View

See icmp_packet::View for an example.

You can create views over a storage by calling View::new. Views can be created based on

  • Immutable borrowed storage: &[u8]
  • Mutable borrowed storage: &mut [u8]
  • Owning storage: impl AsRef<u8> (for example: Vec<u8>)

The generated View struct will offer

  • View::new(storage) to create a View
  • View::into_storage(self) to destroy a View and return the storage held

and it will offer the following accessors for each field

  • ${field_name}(): Read access. This returns a FieldView instance with read access.
  • ${field_name}_mut(): Read access. This returns a FieldView instance with write access.
  • into_${field_name}: Extract access. This destroys the View and returns a FieldView instance owning the storage. Mostly useful for slice fields when you want to return an owning slice.