macro_rules! bitfield {
    (
        $(#[$($meta:meta)+])*
        $vis:vis struct $Name:ident<$T:ident> {
            $(
                $(#[$field_meta:meta])*
                $field_vis:vis const $Field:ident $(: $F:ty)? $( = $val:tt)?;
            )+
        }
    ) => { ... };
    (@field<$T:ident>, prev: $Prev:ident:
        $(#[$meta:meta])*
        $vis:vis const $Field:ident = ..;
    ) => { ... };
    (@field<$T:ident>, prev: $Prev:ident:
        $(#[$meta:meta])*
        $vis:vis const $Field:ident = $value:literal;
        $($rest:tt)*
    ) => { ... };
    (@field<$T:ident>, prev: $Prev:ident:
        $(#[$meta:meta])*
        $vis:vis const $Field:ident: $Val:ty;
        $($rest:tt)*
    ) => { ... };
    (@field<$T:ident>, prev: $Prev:ident: ) => { ... };
    (@field<$T:ident>:
        $(#[$meta:meta])*
        $vis:vis const $Field:ident = $value:literal;
        $($rest:tt)*
    ) => { ... };
    (@field<$T:ident>:
        $(#[$meta:meta])*
        $vis:vis const $Field:ident: $Val:ty;
        $($rest:tt)*
    ) => { ... };
    (@t usize, $V:ty, $F:ty) => { ... };
    (@t u64, $V:ty, $F:ty) => { ... };
    (@t u32, $V:ty, $F:ty) => { ... };
    (@t u16, $V:ty, $F:ty) => { ... };
    (@t u8, $V:ty, $F:ty) => { ... };
    (@t $T:ty, $V:ty, $F:ty) => { ... };
}
Expand description

Generates a typed bitfield struct.

By default, the fmt::Debug, fmt::Display, fmt::Binary, Copy, and Clone traits are automatically derived for bitfields.

All bitfield types are #[repr(transparent)].

For a complete example of the methods generated by the bitfield! macro, see the example module’s ExampleBitfield type.

Generated Implementations

The bitfield! macro generates a type with the following functions, where T is the integer type that represents the bitfield (one of u8, u16, u32, u64, or usize):

FunctionDescription
fn new() -> SelfReturns a new instance of the bitfield type with all bits zeroed.
fn from_bits(bits: T) -> SelfConverts a T into an instance of the bitfield type.
fn with<U>(self, packer: Self::Packer<U>, value: U) -> SelfGiven one of this type’s generated packing specs for a U-typed value, and a U-typed value, returns a new instance of Self with the bit representation of value packed into the range represented by packer.
fn set<U>(&mut self, packer: Self::Packer<U>, value: U) -> &mut SelfSimilar to with, except self is mutated in place, rather than returning a new instance of Self.
fn get<U>(&self, packer: Self::Packer<U>) -> UGiven one of this type’s generated packing specs for a U-typed value, unpacks the bit range represented by that value as a U and returns it. This method panics if the requested bit range does not contain a valid bit pattern for a U-typed value, as determined by U’s implementation of the FromBits trait.
fn try_get<U>(&self, packer: Self::Packer<U>) -> Result<U, <U as FromBits>::Error>Like get, but returns a Result instead of panicking.
fn assert_valid()Asserts that the generated bitfield type is valid. This is primarily intended to be used in tests; the macro cannot generate tests for a bitfield type on its own, so a test that simply calls assert_valid can be added to check the bitfield type’s validity.

The visibility of these methods depends on the visibility of the bitfield struct — if the struct is defined as pub(crate) struct MyBitfield<u16> { ... }, then these functions will all be pub(crate) as well.

If a bitfield type is defined with one visibility, but particular subfields of that bitfield should not be public, the individual fields may also have visibility specifiers. For example, if the bitfield struct MyBitfield is pub, but the subfield named PRIVATE_SUBFIELD is pub(crate), then my_bitfield.get(MyBitfield::PRIVATE_SUBRANGE) can only be called inside the crate defining the type, because the PRIVATE_SUBRANGE constant is not publicly visible.

In addition to the inherent methods discussed above, the following trait implementations are always generated:

TraitDescription
fmt::DebugThe Debug implementation prints the bitfield as a “struct”, with a “field” for each packing spec in the bitfield. If any of the bitfield’s packing specs pack typed values, that type’s fmt::Debug implementation is used rather than printing the value as an integer.
fmt::BinaryPrints the raw bits of this bitfield as a binary number.
fmt::DisplayPretty-prints the bitfield in a very nice-looking multi-line format which I’m rather proud of. See here for examples of this format.
CopyBehaves identically as the Copy implementation for the underlying integer type.
CloneBehaves identically as the Clone implementation for the underlying integer type.

Additional traits may be derived for the bitfield type, such as PartialEq, Eq, and Default. These traits are not automatically derived, as custom implementations may also be desired, depending on the use-case. For example, the Default value for a bitfield may not be all zeroes.

Examples

Basic usage:

mycelium_bitfield::bitfield! {
    /// Bitfield types can have doc comments.
    #[derive(Eq, PartialEq)] // ...and attributes
    pub struct MyBitfield<u16> {
        // Generates a packing spec named `HELLO` for the first 6
        // least-significant bits.
        pub const HELLO = 6;
        // Fields with names starting with `_` can be used to mark bits as
        // reserved.
        const _RESERVED = 4;
        // Generates a packing spec named `WORLD` for the next 3 bits.
        pub const WORLD = 3;
    }
}

// Bitfield types can be cheaply constructed from a raw numeric
// representation:
let bitfield = MyBitfield::from_bits(0b10100_0011_0101);

// `get` methods can be used to unpack fields from a bitfield type:
assert_eq!(bitfield.get(MyBitfield::HELLO), 0b11_0101);
assert_eq!(bitfield.get(MyBitfield::WORLD), 0b0101);

// `with` methods can be used to pack bits into a bitfield type by
// value:
let bitfield2 = MyBitfield::new()
    .with(MyBitfield::HELLO, 0b11_0101)
    .with(MyBitfield::WORLD, 0b0101);

assert_eq!(bitfield, bitfield2);

// `set` methods can be used to mutate a bitfield type in place:
let mut bitfield3 = MyBitfield::new();

bitfield3
    .set(MyBitfield::HELLO, 0b011_0101)
    .set(MyBitfield::WORLD, 0b0101);

assert_eq!(bitfield, bitfield3);

Bitfields may also contain typed values, as long as those values implement the FromBits trait:

use mycelium_bitfield::{bitfield, FromBits};

// An enum type can implement the `FromBits` trait if it has a
// `#[repr(uN)]` attribute.
#[repr(u8)]
#[derive(Debug, Eq, PartialEq)]
enum MyEnum {
    Foo = 0b00,
    Bar = 0b01,
    Baz = 0b10,
}

impl FromBits<u32> for MyEnum {
    // Two bits can represent all possible `MyEnum` values.
    const BITS: u32 = 2;
    type Error = &'static str;

    fn try_from_bits(bits: u32) -> Result<Self, Self::Error> {
        match bits as u8 {
            bits if bits == Self::Foo as u8 => Ok(Self::Foo),
            bits if bits == Self::Bar as u8 => Ok(Self::Bar),
            bits if bits == Self::Baz as u8 => Ok(Self::Baz),
            _ => Err("expected one of 0b00, 0b01, or 0b10"),
        }
    }

    fn into_bits(self) -> u32 {
        self as u8 as u32
    }
}

bitfield! {
    pub struct TypedBitfield<u32> {
        /// Use the first two bits to represent a typed `MyEnum` value.
        const ENUM_VALUE: MyEnum;

        /// Typed values and untyped raw bit fields can be used in the
        /// same bitfield type.
        pub const SOME_BITS = 6;

        /// The `FromBits` trait is also implemented for `bool`, which
        /// can be used to implement bitflags.
        pub const FLAG_1: bool;
        pub const FLAG_2: bool;

        /// `FromBits` is also implemented by (signed and unsigned) integer
        /// types. This will allow the next 8 bits to be treated as a `u8`.
        pub const A_BYTE: u8;
    }
}

// Unpacking a typed value with `get` will return that value, or panic if
// the bit pattern is invalid:
let my_bitfield = TypedBitfield::from_bits(0b0011_0101_1001_1110);

assert_eq!(my_bitfield.get(TypedBitfield::ENUM_VALUE), MyEnum::Baz);
assert_eq!(my_bitfield.get(TypedBitfield::FLAG_1), true);
assert_eq!(my_bitfield.get(TypedBitfield::FLAG_2), false);

// The `try_get` method will return an error rather than panicking if an
// invalid bit pattern is encountered:

let invalid = TypedBitfield::from_bits(0b0011);

// There is no `MyEnum` variant for 0b11.
assert!(invalid.try_get(TypedBitfield::ENUM_VALUE).is_err());

Packing specs from one bitfield type may not be used with a different bitfield type’s get, set, or with methods. For example, the following is a type error:

use mycelium_bitfield::bitfield;

bitfield! {
    struct Bitfield1<u8> {
        pub const FOO: bool;
        pub const BAR: bool;
        pub const BAZ = 6;
    }
}

bitfield! {
    struct Bitfield2<u8> {
        pub const ALICE = 2;
        pub const BOB = 4;
        pub const CHARLIE = 2;
    }
}


// This is a *type error*, because `Bitfield2`'s field `ALICE` cannot be
// used with a `Bitfield2` value:
let bits = Bitfield1::new().with(Bitfield2::ALICE, 0b11);

Example Display Output

Bitfields will automatically generate a pretty fmt::Display implementation:


let my_bitfield = TypedBitfield::from_bits(0b0011_0101_1001_1110);
let formatted = format!("{my_bitfield}");
let expected = r#"
00000000000000000011010110011110
              └┬─────┘││└┬───┘└┤
               │      ││ │     └ ENUM_VALUE: Baz (10)
               │      ││ └────── SOME_BITS: 39 (100111)
               │      │└─────────── FLAG_1: true (1)
               │      └──────────── FLAG_2: false (0)
               └─────────────────── A_BYTE: 13 (00001101)
"#.trim_start();
assert_eq!(formatted, expected);