#[derive(Bitfields)]
{
    // Attributes available to this derive:
    #[bondrewd]
}
Expand description

Generates an implementation of the bondrewd::Bitfield trait, as well as peek and set functions for direct sized u8 arrays access. This crate is designed so that attributes are only required for fields that are not what you would expect without the attribute. For example if you provide a u8 fields with no attributes, the field would be assumed to be the next 8 bits after the field before it. If a field of bool type without attributes is defined, the field would be assumed to be the next bit after the field before it.

Supported Field Types

  • All primitives other than usize and isize (i believe ambiguous sizing is bad for this type of work).
    • Floats currently must be full sized.
    • Its important to know that there is a small runtime cost for signed numbers.
  • Enums which implement the BitfieldEnum trait in Bondrewd.
  • Structs which implement the Bitfield trait in Bondrewd.

Struct Attributes

  • default_endianness = {"le" or "be"} Describes a default endianness for primitive fields. example
  • read_from = {"msb0" or "lsb0"} Defines bit positioning. which end of the byte array to start at. example
  • enforce_bytes = {BYTES} Adds a check that requires total bytes defined by fields to equal provided BYTES. example
  • enforce_bits = {BITS} Adds a check that requires total bits defined by fields to equal provided BITS. example
  • enforce_full_bytes Adds a check that requires total bits defined by fields to equal a multiple of 8. example
  • fill_bytes = {BYTES} Will force the output/input byte array size to be the provided SIZE amount of bytes. example
  • reverse Defines that the entire byte array should be read backward (first byte index becomes last byte index). This has no runtime cost. example

Field Attributes

  • bit_length = {BITS} Define the total amount of bits to use when condensed. example
  • byte_length = {BYTES} Define the total amount of bytes to use when condensed. example
  • endianness = {"le" or "be"} Define per field endianess. example
  • block_bit_length = {BITS} Describes a bit length for the entire array dropping lower indexes first. example
  • block_byte_length = {BYTES} Describes a byte length for the entire array dropping lower indexes first. example
  • element_bit_length = {BITS} Describes a bit length for each element of an array. (default array type). example
  • element_byte_length = {BYTES} Describes a byte length for each element of an array. (default array type). example
  • enum_primitive = "u8" Defines the size of the enum. the BitfieldEnum currently only supports u8. example
  • struct_size = {SIZE} Defines the field as a struct which implements the Bitfield trait and the BYTE_SIZE const defined in said trait. example
  • reserve Defines that this field should be ignored in from and into bytes functions. example
    • Reserve requires the fields type to impl ‘Default’. due to from_bytes needed to provided a value.

Experimental Field Attributes

if you decide to use these remember that they have not been exhaustively tested. when using experimental attributes please be careful and report unexpected behavior to our github issues.

  • bits = "{RANGE}" - Define the bit indexes yourself rather than let the proc macro figure it out. using a rust range in quotes. the RANGE must provide a inclusively below and exclusively above bounded range (ex. bits = “0..2” means use bits 0 and 1 but NOT 2). example
  • read_only - Bondrewd will not include writing/into_bytes logic for the field.
  • overlapping_bits = {BITS} - Tells bondrewd that the provided BITS amount is shared with at least 1 other field and should not be included in the overall structure size.
  • redundant - Tells bondrewd that this field’s bits are all shared by at least one other field. Bondrewd will not include the bit length in the structures overall bit length (because they are redundant). example
    • Bondrewd will read the assigned bits but will not write.
    • This behaves exactly as combining the attributes:
      • read_only
      • overlapping_bits = {FIELD_BIT_LENGTH} FIELD_BIT_LENGTH being the total amount of bits that the field uses.

Simple Example

This example is on the front page for bondrewd-derive. Here i will be adding some asserts to show what to expect. I will be defining a data structure with 7 total bytes as:

  • A boolean field named one will be the first bit.
  • A floating point field named two will be the next 32 bits. floats must be full sized currently.
  • A signed integer field named three will be the next 14 bits.
  • An unsigned integer field named four will be the next 6 bits.
  • Because these fields do not add up to a number divisible by 8 the last 3 bits will be unused.
use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(default_endianness = "be")]
struct SimpleExample {
    // fields that are as expected do not require attributes.
    one: bool,
    two: f32,
    #[bondrewd(bit_length = 14)]
    three: i16,
    #[bondrewd(bit_length = 6)]
    four: u8,
}

assert_eq!(7, SimpleExample::BYTE_SIZE);
assert_eq!(53, SimpleExample::BIT_SIZE);
let mut bytes = SimpleExample {
    one: false,
    two: -4.25,
    three: -1034,
    four: 63,
}.into_bytes();
// check the output binary is correct. (i did math by hand
// to get the binary). each field is separated by a underscore
// in the binary assert to make it easy to see.
assert_eq!([
    0b0_1100000, // one_two,
    0b01000100,  // two,
    0b00000000,  // two,
    0b00000000,  // two,
    0b0_1110111, // two_three,
    0b1110110_1, // three_four,
    0b11111_000, // four_unused
], bytes);
// use read functions to get the fields value without
// doing a from_bytes call.
assert_eq!(false, SimpleExample::read_one(&bytes));
assert_eq!(-4.25, SimpleExample::read_two(&bytes));
assert_eq!(-1034, SimpleExample::read_three(&bytes));
assert_eq!(63, SimpleExample::read_four(&bytes));
// overwrite the values with new ones in the byte array.
SimpleExample::write_one(&mut bytes, true);
SimpleExample::write_two(&mut bytes, 5.5);
SimpleExample::write_three(&mut bytes, 511);
SimpleExample::write_four(&mut bytes, 0);
// from bytes uses the read function so there is no need to
// assert the read functions again.
let reconstructed = SimpleExample::from_bytes(bytes);
// check the values read by from bytes and check if they are
// what we wrote to the bytes NOT the origanal values.
assert_eq!(true,reconstructed.one);
assert_eq!(5.5,reconstructed.two);
assert_eq!(511,reconstructed.three);
assert_eq!(0,reconstructed.four);

Reverse Example

Reverse simply makes Bondrewd index the bytes in the output/input buffers in the opposite order. First index becomes last index and last index becomes the first.

use bondrewd::*;
#[derive(Bitfields)]
struct Example {
    one: u8,
    two: u8,
    three: u8,
    four: u8,
}

#[derive(Bitfields)]
#[bondrewd(reverse)]
struct ExampleReversed {
    one: u8,
    two: u8,
    three: u8,
    four: u8,
}

let test = Example {
    one: 0,
    two: u8::MAX,
    three: 0,
    four: 0b01010101,
};
let test_reverse = ExampleReversed {
    one: 0,
    two: u8::MAX,
    three: 0,
    four: 0b01010101,
};
assert_eq!(test.into_bytes(), [0b00000000, 0b11111111, 0b000000, 0b01010101]);
assert_eq!(test_reverse.into_bytes(), [0b01010101, 0b000000, 0b11111111, 0b00000000]);

Bit Positioning Examples

Here Bit positioning will control where bit 0 is. for example if you have a field with 2 bits then 2 fields with 3 bits each, bit positioning will define the direction in which it traverses bit indices, so in our example if 0 is the least significant bit the first field would be the least significant bit in the last index in the byte array. msb0 is the default.

use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(read_from = "msb0")]
struct ExampleMSB {
    #[bondrewd(bit_length = 2)]
    one: u8,
    #[bondrewd(bit_length = 3)]
    two: u8,
    #[bondrewd(bit_length = 3)]
    three: u8,
}

#[derive(Bitfields)]
#[bondrewd(read_from = "lsb0")]
struct ExampleLSB {
    #[bondrewd(bit_length = 2)]
    one: u8,
    #[bondrewd(bit_length = 3)]
    two: u8,
    #[bondrewd(bit_length = 3)]
    three: u8,
}

let test_msb = ExampleMSB {
    one: 0,
    two: 5,
    three: 0,
};
let test_lsb = ExampleLSB {
    one: 0,
    two: 5,
    three: 0,
};
// in msb0 field one is the first 2 bits followed by field two
// then field three is the last 3 bits.
assert_eq!(test_msb.into_bytes(), [0b00_101_000]);
// in msb0 field three is the first 3 bits followed by field
// 2 then field one being the last 2 bits
assert_eq!(test_lsb.into_bytes(), [0b000_101_00]);

When using reverse and read_from in the same structure:

  • lsb0 would begin at the least significant bit in the first byte.
  • ’msb0` would begin at the most significant bit in the last byte.
use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(read_from = "msb0", reverse)]
struct ExampleMSB {
    #[bondrewd(bit_length = 5)]
    one: u8,
    #[bondrewd(bit_length = 4)]
    two: u8,
    #[bondrewd(bit_length = 7)]
    three: u8,
}

#[derive(Bitfields)]
#[bondrewd(read_from = "lsb0", reverse)]
struct ExampleLSB {
    #[bondrewd(bit_length = 5)]
    one: u8,
    #[bondrewd(bit_length = 4)]
    two: u8,
    #[bondrewd(bit_length = 7)]
    three: u8,
}

let test_msb = ExampleMSB {
    one: 0,
    two: u8::MAX,
    three: 0,
};
let test_lsb = ExampleLSB {
    one: 0,
    two: u8::MAX,
    three: 0,
};
// here the 1's belong to feild two. i hope this is understandable.
assert_eq!(test_msb.into_bytes(), [0b10000000, 0b00000111]);
assert_eq!(test_lsb.into_bytes(), [0b11100000, 0b00000001]);

Endianness Examples

There are 2 ways to define endianess of fields that require endianness (multi-byte numbers, char, …)

  • Default endianness which will give provided endianness to all fields that require endianness but do not have it defined.
  • Per field endianess which defines the endianness of a particular field.

Default endianness and per fields endianness can also be used in the same struct

use bondrewd::*;
#[derive(Bitfields)]
// tell bondrewd to default to Big Endian
#[bondrewd(default_endianness = "be")]
struct SimpleExample {
    // this field will be given the default endianness
    one: u16,
    // here we give the field endianness which means it will not use default endianness.
    #[bondrewd(endianness = "le")]
    two: u16,
}

let test = SimpleExample {
    one: 5,
    two: 5,
};
// check that each field are in the correct endianness
assert_eq!(test.into_bytes(),[0b00000000, 0b00000101, 0b00000101, 0b00000000]);

If you define the endianness of all values that require it default_endianness is not required.

use bondrewd::*;
#[derive(Bitfields)]
struct SimpleExample {
    #[bondrewd(endianness = "be")]
    one: u16,
    #[bondrewd(endianness = "le")]
    two: u16,
    // because this field does not use more than 1 byte, endianness is not required.
    three: bool,
}

let test = SimpleExample {
    one: 5,
    two: 5,
    three: true,
};
// check that each field are in the correct endianness
assert_eq!(test.into_bytes(),[0b00000000, 0b00000101, 0b00000101, 0b00000000, 0b10000000]);

Bitfield Struct as Field Examples

Inner structs must implement the Bitfields trait and be given the `struct_size = {BYTE_SIZE}, the BYTE_SIZE being the number of bytes in the outputs byte array or value in the traits const BYTE_SIZE.

// this struct uses 52 total bits which means the total BYTE_SIZE is 7.
use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(default_endianness = "be")]
struct Simple {
    #[bondrewd(bit_length = 3)]
    one: u8,
    #[bondrewd(bit_length = 27)]
    two: char,
    #[bondrewd(bit_length = 14)]
    three: u16,
    four: i8,
}

#[derive(Bitfields)]
#[bondrewd(default_endianness = "be")]
struct SimpleWithStruct {
    #[bondrewd(struct_size = 7)]
    one: Simple,
    // structs can also be used in arrays.
    #[bondrewd(struct_size = 7)]
    two: [Simple; 2],
}

We can also trim the struct to a bit length, this can be very useful for struct that do not use the full amount of bits available in the byte array. For example if we have a struct that uses 4 bits leaving the remaining 4 bits as unused data, we can make a structure with 2 of the bits structure that still only uses 1 byte.

// this struct uses 4 total bits which means the total BYTE_SIZE is 1.
use bondrewd::*;
#[derive(Bitfields, Clone)]
#[bondrewd(default_endianness = "be")]
struct Simple {
    #[bondrewd(bit_length = 2)]
    one: u8,
    #[bondrewd(bit_length = 2)]
    two: u8,
}

#[derive(Bitfields)]
#[bondrewd(default_endianness = "be")]
struct SimpleWithStruct {
    #[bondrewd(struct_size = 1, bit_length = 4)]
    one: Simple,
    #[bondrewd(struct_size = 1, bit_length = 4)]
    two: Simple,
}

// SimpleWithStruct uses the amount of bits that 2
// Simple structures would use.
assert_eq!(SimpleWithStruct::BIT_SIZE, Simple::BIT_SIZE * 2);
// But both structures use 1 byte.
assert_eq!(SimpleWithStruct::BYTE_SIZE, 1);
assert_eq!(SimpleWithStruct::BYTE_SIZE, Simple::BYTE_SIZE);

Bitfield Array Examples

There are 2 types of arrays in Bondrewd:

  • Block Arrays are “bit chucks” that define a total-used-bits amount and will drop bits starting at the lowest index.
  • Element Arrays treat each element of the array as its own field and requires a per element bit-length.
use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(default_endianness = "be")]
struct SimpleWithArray {
    // each u8 in the array contains 4 bits of useful information.
    #[bondrewd(element_bit_length = 4)]
    one: [u8; 4],
    // due to no attributes being present for field `two`,
    // no bits are missing and the type of array shouldn't
    // matter, Bondrewd will use element array logic. also boolean
    // values are assumed to be 1 bit so this will produce
    // 5 bits in an output.
    #[bondrewd(element_bit_length = 1)]
    two: [bool; 5],
    // the total amount bits in the array should be 20.
    // [{4 bits},{8 bits},{8 bits}]
    #[bondrewd(block_bit_length = 20)]
    three: [u8; 3],
}

let test = SimpleWithArray {
    // the first 4 bits in index 0 and 2 are 1's to show
    // that they will not be in the final result due to
    // each element being set to 4 bits, meaning the values
    // in those indices will become 0 after into_bytes is called.
    one: [0b11110000, 0b00001111, 0b11110000, 0b00001001],
    two: [false, true, false, true, false],
    // its also worth noting that index 0 here will lose the 4
    // most significant bits.
    three: [u8::MAX, 0, 0b10101010],
};
assert_eq!(test.into_bytes(),
    [0b0000_1111,  // one[0 and 1]
     0b0000_1001,  // one[2 and 3]
     0b01010_111,  // two and three[0]
     0b1_0000000,  // remaining three[0] and three[1]
     0b0_1010101,  // remaining three[1] and three[2]
     0b0_0000000]);// remaining three[2] and 7 unused bits.

Structures and Enums can also be used in arrays but there are some extra things to consider.

  • If bit_length of the structs or enums needs to be smaller than the output of either into_bytes or into_primitive then it is recommended to use element arrays.
  • Block Arrays, in my opinion, shouldn’t be used for Structs or Enums. because in the below example if the compressed_structures field was to use block_bit_length = 104 the array would use 48 bits for index 0 and 56 bits for index 1.
// this struct uses 52 total bits which means the total
// BYTE_SIZE is 7.
use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(default_endianness = "be")]
struct SimpleStruct {
    #[bondrewd(bit_length = 3)]
    one: u8,
    #[bondrewd(bit_length = 27)]
    two: char,
    #[bondrewd(bit_length = 14)]
    three: u16,
    four: i8,
}

// this enum has 4 variants therefore only uses 2 bits
// out of 8 in the primitive type.
#[derive(BitfieldEnum)]
enum SimpleEnum {
    Zero,
    One,
    Two,
    Three,
}

#[derive(Bitfields)]
#[bondrewd(default_endianness = "be")]
struct ArraysWithStructsAndEnums {
    // if the bit size should be the full size of the primitive, only
    // the enum attribute is needed.
    #[bondrewd(enum_primitive = "u8")]
    four_byte_four_values: [SimpleEnum; 4],
    // if we use the element_bit_length we can say to only use 2
    // bits per SimpleEnum, and due to SimpleEnum only needing 2
    // bits, this could be desirable. means instead of using 4
    // bytes to store 4 SimpleEnums, we can use 1 byte.
    #[bondrewd(enum_primitive = "u8", element_bit_length = 2)]
    one_byte_four_values: [SimpleEnum; 4],
    // again if the size doesn't need to change, no array attribute
    // is needed.
    #[bondrewd(struct_size = 7)]
    waste_a_byte: [SimpleStruct; 2],
    // if we want to compress the 2 struct in the array we can
    // take advantage of the fact our struct is only using 52 out
    // of 56 bits in the compressed/byte form by adding
    // element bit length = 52. this will make the total size of
    // the 2 structs in compressed/byte form 104 bits instead of
    // 112.
    #[bondrewd(struct_size = 7, element_bit_length = 52)]
    compressed_structures: [SimpleStruct; 2],
}

Reserve Examples

Reserve fields tell Bondrewd to not include logic for reading or writing the field in the from and into bytes functions. Currently only primitive types are supported.

use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(default_endianness = "be")]
struct ReserveExample {
    #[bondrewd(bit_length = 7)]
    one: u8,
    #[bondrewd(bit_length = 7)]
    two: u8,
    #[bondrewd(bit_length = 10, reserve)]
    reserve: u16
}
assert_eq!(3, ReserveExample::BYTE_SIZE);
assert_eq!(24, ReserveExample::BIT_SIZE);
let mut bytes = ReserveExample {
    one: 127,
    two: 127,
    reserve: 1023,
}.into_bytes();
assert_eq!([0b11111111, 0b11111100, 0b00000000], bytes);
assert_eq!(127,ReserveExample::read_one(&bytes));
assert_eq!(127,ReserveExample::read_two(&bytes));
assert_eq!(0,ReserveExample::read_reserve(&bytes));
// quick note write_reserve will actually change the bytes in the byte array.
ReserveExample::write_reserve(&mut bytes, 42);
assert_eq!(42,ReserveExample::read_reserve(&bytes));
// but again from/into bytes doesn't care.
let reconstructed = ReserveExample::from_bytes(bytes);
assert_eq!(127,reconstructed.one);
assert_eq!(127,reconstructed.two);
assert_eq!(0,reconstructed.reserve);

Reserves do not need to be at the end.

use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(default_endianness = "be", fill_bytes = 3)]
struct ReserveExample {
    #[bondrewd(bit_length = 7)]
    one: u8,
    #[bondrewd(bit_length = 10, reserve)]
    reserve: u16,
    #[bondrewd(bit_length = 7)]
    two: u8,
}
assert_eq!(3, ReserveExample::BYTE_SIZE);
assert_eq!(24, ReserveExample::BIT_SIZE);
let mut bytes = ReserveExample {
    one: 127,
    two: 127,
    reserve: 1023,
}.into_bytes();
assert_eq!(127, ReserveExample::read_one(&bytes));
assert_eq!(127, ReserveExample::read_two(&bytes));
assert_eq!(0, ReserveExample::read_reserve(&bytes));
ReserveExample::write_reserve(&mut bytes, 42);
assert_eq!(42, ReserveExample::read_reserve(&bytes));
let reconstructed = ReserveExample::from_bytes(bytes);
assert_eq!(127,reconstructed.one);
assert_eq!(127,reconstructed.two);
assert_eq!(0,reconstructed.reserve);

Fill Bytes Examples

Fill bytes is used here to make the total output byte size 3 bytes. If fill bytes attribute was not present the total output byte size would be 2.

use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(default_endianness = "be", fill_bytes = 3)]
struct FilledBytes {
    #[bondrewd(bit_length = 7)]
    one: u8,
    #[bondrewd(bit_length = 7)]
    two: u8,
}
assert_eq!(3, FilledBytes::BYTE_SIZE);
assert_eq!(24, FilledBytes::BIT_SIZE);

Here im going to compare the example above to the closest alternative using a reserve field:

  • FilledBytes only has 2 field, so only 2 fields are required for instantiation, where as ReservedBytes still needs a value for the reserve field despite from/into bytes not using the value anyway.
  • ReservedBytes has 2 extra functions that FilledBytes does not, write_reserve and read_reserve.
  • One more thing to consider is reserve fields are currently confined to primitives, if more than 128 reserve bits are required at the end, fill_bytes is the only supported way of doing this.
use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(default_endianness = "be")]
struct ReservedBytes {
    #[bondrewd(bit_length = 7)]
    one: u8,
    #[bondrewd(bit_length = 7)]
    two: u8,
    #[bondrewd(bit_length = 10, reserve)]
    reserve: u16
}
assert_eq!(3, ReservedBytes::BYTE_SIZE);
assert_eq!(24, ReservedBytes::BIT_SIZE);

Enforce Bits Examples

Enforce Bits/Bytes Main purpose is to act as a compile time check to ensure how many bit you think are being use is the actual amount of bits being used.
Here i have 2 fields with a total defined bit-length of 6, and then an undecorated boolean field. I also have trust issues so i want to verify that the bool is only using 1 bit making the total bit length of the struct 7 bits. Adding enforce_bits = 7 will force a compiler error if the calculated total bit length is not 7.

use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(default_endianness = "be", enforce_bits = 7)]
struct FilledBytesEnforced {
    #[bondrewd(bit_length = 4)]
    one: u8,
    #[bondrewd(bit_length = 2)]
    two: u8,
    three: bool
}
assert_eq!(1, FilledBytesEnforced::BYTE_SIZE);
assert_eq!(7, FilledBytesEnforced::BIT_SIZE);

Here is the same example where but i messed up the bit_length of the first field making the total 8 instead of 7.

use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(default_endianness = "be", enforce_bits = 7)]
struct FilledBytesEnforced {
    #[bondrewd(bit_length = 5)]
    one: u8,
    #[bondrewd(bit_length = 2)]
    two: u8,
    three: bool
}
assert_eq!(1, FilledBytesEnforced::BYTE_SIZE);
assert_eq!(7, FilledBytesEnforced::BIT_SIZE);

These next 3 examples all attempt to have near the same end results. A total output of 3 bytes, but the last 10 of them will be reserved/unused (should be ignored and assumed to be 0).

In this first example i will be showing what a struct might look like without fill bytes, then in the second example i will show the the same end result but without a reserve field. First will be defining all 24 total bits as 3 fields marking the last field of 10 bits with the reserve attribute because we don’t want from/into bytes functions to process those bytes.

use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(default_endianness = "be", enforce_bytes = 3)]
struct FilledBytesEnforced {
    #[bondrewd(bit_length = 7)]
    one: u8,
    #[bondrewd(bit_length = 7)]
    two: u8,
    #[bondrewd(bit_length = 10, reserve)]
    reserve: u16
}
assert_eq!(3, FilledBytesEnforced::BYTE_SIZE);
assert_eq!(24, FilledBytesEnforced::BIT_SIZE);

Also note that fill_bytes does NOT effect how enforce_bytes works. enforce_bytes will check the total bit length before the bits are filled.

Here i am telling Bondrewd to make the total bit length 3 bytes using fill_bytes. This Example fails to build because only 14 bits are being defined by fields and enforce_bytes is telling Bondrewd to expect 24 bits to be used by defined fields.

use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(default_endianness = "be", fill_bytes = 3, enforce_bytes = 3)]
struct FilledBytesEnforced {
    #[bondrewd(bit_length = 7)]
    one: u8,
    #[bondrewd(bit_length = 7)]
    two: u8,
}

To fix this we need to make sure our enforcement value is the amount fo bits defined by the fields NOT the expected FilledBytesEnforced::BYTE_SIZE.

Here is the Correct usage of these two attributes working together.

use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(default_endianness = "be", fill_bytes = 3, enforce_bits = 14)]
struct FilledBytesEnforced {
    #[bondrewd(bit_length = 7)]
    one: u8,
    #[bondrewd(bit_length = 7)]
    two: u8,
}
assert_eq!(3, FilledBytesEnforced::BYTE_SIZE);
// we are enforcing 14 bits but fill_bytes is creating
// an imaginary reserve field from bit index 14 to
// index 23
assert_eq!(24, FilledBytesEnforced::BIT_SIZE);

Enforce Full Bytes Example

enforce_full_bytes adds a check during parsing phase of Bondrewd which will throw an error if the total bits determined from the defined fields is not a multiple of 8. This was included for those like me that get paranoid they entered something in wrong.

use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(default_endianness = "be", enforce_full_bytes)]
struct FilledBytesEnforced {
    #[bondrewd(bit_length = 7)]
    one: u8,
    #[bondrewd(bit_length = 7)]
    two: u8,
}

In this case if we still wanted fields one and two to remain 7 bits we need to add another field to use the remaining 2 bits.

use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(default_endianness = "be", enforce_full_bytes)]
struct FilledBytesEnforced {
    #[bondrewd(bit_length = 7)]
    one: u8,
    #[bondrewd(bit_length = 7)]
    two: u8,
    #[bondrewd(bit_length = 2, reserve)]
    reserve: u16
}
assert_eq!(2, FilledBytesEnforced::BYTE_SIZE);
assert_eq!(16, FilledBytesEnforced::BIT_SIZE);

Enum Examples

For enum derive examples goto BitfieldEnum Derive.

use bondrewd::*;
#[derive(BitfieldEnum)]
enum SimpleEnum {
    Zero,
    One,
    Two,
    Three,
}
#[derive(Bitfields)]
#[bondrewd(default_endianness = "le")]
struct StructWithEnumExample {
    #[bondrewd(bit_length = 3)]
    one: u8,
    #[bondrewd(enum_primitive = "u8", bit_length = 2)]
    two: SimpleEnum,
    #[bondrewd(bit_length = 3)]
    three: u8,
}

Enums can also be used in arrays

use bondrewd::*;
#[derive(BitfieldEnum)]
enum Simple {
    One,
    Two,
    Three,
    Four,
}

#[derive(Bitfields)]
#[bondrewd(default_endianness = "be")]
struct SimpleWithStruct {
    // bit length is not required for enums but in this case where only 4 possible variants are in
    // our enums 2 bits is all that is needed. also note using more bits than possible variants is
    // not a problem because the catch all system will protect you from bad inputs.
    #[bondrewd(bit_length = 2, enum_primitive = "u8")]
    one: Simple,
    #[bondrewd(element_bit_length = 2, enum_primitive = "u8")]
    two: [Simple; 3],
}

Bits Attribute Example

First i will replicate the Simple Example to show an equivalent use.

use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(default_endianness = "be")]
struct SimpleExample {
    // fields that are as expected do not require attributes.
    // #[bondrewd(bits = "0..1")] this could be used but is not needed.
    one: bool,
    // #[bondrewd(bits = "1..33")] this could be used but is not needed.
    two: f32,
    #[bondrewd(bits = "33..47")]
    three: i16,
    #[bondrewd(bits = "47..53")]
    four: u8,
}

assert_eq!(7, SimpleExample::BYTE_SIZE);
assert_eq!(53, SimpleExample::BIT_SIZE);
let mut bytes = SimpleExample {
    one: false,
    two: -4.25,
    three: -1034,
    four: 63,
}.into_bytes();
// check the output binary is correct. (i did math by hand
// to get the binary). each field is separated by a underscore
// in the binary assert to make it easy to see.
assert_eq!([
    0b0_1100000, // one_two,
    0b01000100,  // two,
    0b00000000,  // two,
    0b00000000,  // two,
    0b0_1110111, // two_three,
    0b1110110_1, // three_four,
    0b11111_000, // four_unused
], bytes);
// use read functions to get the fields value without
// doing a from_bytes call.
assert_eq!(false, SimpleExample::read_one(&bytes));
assert_eq!(-4.25, SimpleExample::read_two(&bytes));
assert_eq!(-1034, SimpleExample::read_three(&bytes));
assert_eq!(63, SimpleExample::read_four(&bytes));
// overwrite the values with new ones in the byte array.
SimpleExample::write_one(&mut bytes, true);
SimpleExample::write_two(&mut bytes, 5.5);
SimpleExample::write_three(&mut bytes, 511);
SimpleExample::write_four(&mut bytes, 0);
// from bytes uses the read function so there is no need to
// assert the read functions again.
let reconstructed = SimpleExample::from_bytes(bytes);
// check the values read by from bytes and check if they are
// what we wrote to the bytes NOT the origanal values.
assert_eq!(true,reconstructed.one);
assert_eq!(5.5,reconstructed.two);
assert_eq!(511,reconstructed.three);
assert_eq!(0,reconstructed.four);

Redundant Examples

In this example we will has fields share data. flags in the example will represent a u8 storing multiple boolean flags, but all of the flags within are also fields in the struct. if we mark flags as redundant above the boolean flag fields then flags will be “read_only”(effects nothing during an into_bytes() call).

use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(default_endianness = "be")]
struct SimpleExample {
    // fields that are as expected do not require attributes.
    one: bool,
    two: f32,
    #[bondrewd(bit_length = 14)]
    three: i16,
    // the field is above the bits it shares because bondrewd
    // will get the last non-shared set of bits to base its start from.
    #[bondrewd(redundant, bit_length = 6)]
    flags: u8,
    flag_one: bool,
    flag_two: bool,
    flag_three: bool,
    flag_four: bool,
    flag_five: bool,
    flag_six: bool,
}

assert_eq!(7, SimpleExample::BYTE_SIZE);
assert_eq!(53, SimpleExample::BIT_SIZE);
let mut bytes = SimpleExample {
    one: false,
    two: -4.25,
    three: -1034,
    flags: 0,
    flag_one: true,
    flag_two: true,
    flag_three: true,
    flag_four: true,
    flag_five: true,
    flag_six: true,
}.into_bytes();
// check the output binary is correct. (i did math by hand
// to get the binary). each field is separated by a underscore
// in the binary assert to make it easy to see.
assert_eq!([
    0b0_1100000, // one_two,
    0b01000100,  // two,
    0b00000000,  // two,
    0b00000000,  // two,
    0b0_1110111, // two_three,
    0b1110110_1, // three_four,
    0b11111_000, // four_unused
], bytes);
// use read functions to get the fields value without
// doing a from_bytes call.
assert_eq!(false, SimpleExample::read_one(&bytes));
assert_eq!(-4.25, SimpleExample::read_two(&bytes));
assert_eq!(-1034, SimpleExample::read_three(&bytes));
// notice i can still use the read calls for the redundant field.
assert_eq!(63, SimpleExample::read_flags(&bytes));
assert_eq!(true,SimpleExample::read_flag_one(&bytes));
assert_eq!(true,SimpleExample::read_flag_two(&bytes));
assert_eq!(true,SimpleExample::read_flag_three(&bytes));
assert_eq!(true,SimpleExample::read_flag_four(&bytes));
assert_eq!(true,SimpleExample::read_flag_five(&bytes));
assert_eq!(true,SimpleExample::read_flag_six(&bytes));
// overwrite the values with new ones in the byte array.
SimpleExample::write_one(&mut bytes, true);
SimpleExample::write_two(&mut bytes, 5.5);
SimpleExample::write_three(&mut bytes, 511);
// notice i can still use the write calls for the redundant field.
SimpleExample::write_flags(&mut bytes, 0);
// from bytes uses the read function so there is no need to
// assert the read functions again.
let reconstructed = SimpleExample::from_bytes(bytes);
// check the values read by from bytes and check if they are
// what we wrote to the bytes NOT the origanal values.
assert_eq!(true,reconstructed.one);
assert_eq!(5.5,reconstructed.two);
assert_eq!(511,reconstructed.three);
assert_eq!(0,reconstructed.flags);
assert_eq!(false,reconstructed.flag_one);
assert_eq!(false,reconstructed.flag_two);
assert_eq!(false,reconstructed.flag_three);
assert_eq!(false,reconstructed.flag_four);
assert_eq!(false,reconstructed.flag_five);
assert_eq!(false,reconstructed.flag_six);

we can also have the flags below if we use the bits attribute.

use bondrewd::*;
#[derive(Bitfields)]
#[bondrewd(default_endianness = "be")]
struct SimpleExample {
    // fields that are as expected do not require attributes.
    one: bool,
    two: f32,
    #[bondrewd(bit_length = 14)]
    three: i16,
    // the field is above the bits it shares because bondrewd
    // will get the last non-shared set of bits to base its start from.
    flag_one: bool,
    flag_two: bool,
    flag_three: bool,
    flag_four: bool,
    flag_five: bool,
    flag_six: bool,
    #[bondrewd(redundant, bits = "47..53")]
    flags: u8,
}

assert_eq!(7, SimpleExample::BYTE_SIZE);
assert_eq!(53, SimpleExample::BIT_SIZE);
let mut bytes = SimpleExample {
    one: false,
    two: -4.25,
    three: -1034,
    flags: 0,
    flag_one: true,
    flag_two: true,
    flag_three: true,
    flag_four: true,
    flag_five: true,
    flag_six: true,
}.into_bytes();
// check the output binary is correct. (i did math by hand
// to get the binary). each field is separated by a underscore
// in the binary assert to make it easy to see.
assert_eq!([
    0b0_1100000, // one_two,
    0b01000100,  // two,
    0b00000000,  // two,
    0b00000000,  // two,
    0b0_1110111, // two_three,
    0b1110110_1, // three_four,
    0b11111_000, // four_unused
], bytes);
// use read functions to get the fields value without
// doing a from_bytes call.
assert_eq!(false, SimpleExample::read_one(&bytes));
assert_eq!(-4.25, SimpleExample::read_two(&bytes));
assert_eq!(-1034, SimpleExample::read_three(&bytes));
// notice i can still use the read calls for the redundant field.
assert_eq!(63, SimpleExample::read_flags(&bytes));
assert_eq!(true,SimpleExample::read_flag_one(&bytes));
assert_eq!(true,SimpleExample::read_flag_two(&bytes));
assert_eq!(true,SimpleExample::read_flag_three(&bytes));
assert_eq!(true,SimpleExample::read_flag_four(&bytes));
assert_eq!(true,SimpleExample::read_flag_five(&bytes));
assert_eq!(true,SimpleExample::read_flag_six(&bytes));
// overwrite the values with new ones in the byte array.
SimpleExample::write_one(&mut bytes, true);
SimpleExample::write_two(&mut bytes, 5.5);
SimpleExample::write_three(&mut bytes, 511);
// notice i can still use the write calls for the redundant field.
SimpleExample::write_flags(&mut bytes, 0);
// from bytes uses the read function so there is no need to
// assert the read functions again.
let reconstructed = SimpleExample::from_bytes(bytes);
// check the values read by from bytes and check if they are
// what we wrote to the bytes NOT the origanal values.
assert_eq!(true,reconstructed.one);
assert_eq!(5.5,reconstructed.two);
assert_eq!(511,reconstructed.three);
assert_eq!(0,reconstructed.flags);
assert_eq!(false,reconstructed.flag_one);
assert_eq!(false,reconstructed.flag_two);
assert_eq!(false,reconstructed.flag_three);
assert_eq!(false,reconstructed.flag_four);
assert_eq!(false,reconstructed.flag_five);
assert_eq!(false,reconstructed.flag_six);