#layout #bit-fields #bitfields

no-std bitfld

Ergonomic, no-std specification of bitfield layouts

6 releases

new 0.1.7 Feb 17, 2025
0.1.6 Feb 17, 2025

#510 in Encoding

Download history 541/week @ 2025-02-12

541 downloads per month

MIT license

27KB
112 lines

bitfld is a no-std crate for ergonomically specifying layouts of bitfields over integral types. While the aim is to be general-purpose, the imagined user is a systems programmer uncomfortably hunched over an architectural manual or hardware spec, looking to transcribe register layouts into Rust with minimal fuss.

The heavy lifting is done by the layout! procedural macro. Care is taken to generate readable and efficient code. The zerocopy crate is further leveraged for safe and efficient transmutation between integral values and custom bitfield representations.

Features

  • Generation of a simple, extensible wrapper type around the given integral base type;
  • Simple, const-friendly builder pattern for constructing layout instances;
  • Automatic implementations of the basic, convenient traits one would expect out of a thin integral wrapper type:
    • Copy, Clone
    • Eq, PartialEq
    • Default
    • From over the base type
    • Deref and DerefMut with a target of the underlying base type
    • Debug, Display, Binary, LowerHex, UpperHex, Octal
  • Specification of default and "reserved as" values, with new() respecting reserved-as values and default() respecting both;
  • Custom bitfield representation types without any boilerplate;
  • Iteration over individual bitfield values and metadata;
  • Associated constants around masks and shifts for use in inline assembly.

For more detail, see layout!.

Example

use bitfld::{bitfield_repr, layout};

#[bitfield_repr(u8)]
pub enum CustomFieldRepr {
    Option1 = 0xa,
    Option2 = 0xf,
}

layout!({
    pub struct Example(u32);
    {
        let foo: Bits<21, 14>;
        let custom: Bits<13, 10, CustomFieldRepr>;
        let bar: Bits<9, 8> = 0b11;
        let baz: Bit<7>;
        let frob: Bits<6, 4>;
        let _: Bits<3, 2> = 1;
        let _: Bits<1, 0>;
    }
});

fn main() {
    let example = *Example::default()
        .set_custom(CustomFieldRepr::Option2)
        .set_frob(0x7);
    assert_eq!(example.foo(), 0);
    assert_eq!(example.custom().unwrap(), CustomFieldRepr::Option2);
    assert_eq!(example.bar(), 0b11);
    assert_eq!(example.baz(), false);
    assert_eq!(example.frob(), 0x7);
    assert_eq!(*example & 0b1100, 0b0100);

    // Will print: `Example { custom: Option2, foo: 0x0, bar: 0x3, baz: false, frob: 0xa }`
    println!("{example}");

    // Or iterate over all fields and and print them individually.
    for (value, metadata) in example {
      println!("{}: {:x}", metadata.name, value);
    }
}

bitfield_repr is an attribute that is syntactic sugar for deriving repr(X) and the handful of traits expected of a custom field representation.

Why another crate for bitfields?

There are already a handful out there, so why this one too? It is the author's opinion that none of those at the time of writing this offer all of the above features (e.g., around reserved semantics or boilerplate-free, custom field representations) or the author's desired ergonomics around register modeling. For example, some constrain field specification by bit width instead of by an explicit bit range, which is not how registers are commonly described in official references (plus, the author surely can't trust himself to do mental math like that).

Dependencies

~200–630KB
~15K SLoC