Bitfield Struct

Crate API

Procedural macro for declarative defining bitfields similar to structs. As this library provides a procedural macro, it has no runtime dependencies and works for no-std.

Usage

Add this to your Cargo.toml:

toml [dependencies] bitfield-struct = "0.4"

Basics

Let's begin with a simple example. Suppose we want to store multiple data inside a single Byte, as shown below:

7 6 5 4 3 3 1 0
P Level S Kind

This crate generates a nice wrapper type that makes it easy to do this:

```rust /// Define your type like this with the bitfield attribute

[bitfield(u8)]

struct MyByte { /// The first field occupies the least significant bits #[bits(4)] kind: usize, /// Booleans are 1 bit large system: bool, /// The bits attribute specifies the bit size of this field #[bits(2)] level: usize, /// The last field spans over the most significant bits present: bool } // The macro creates three accessor functions for each field: // , with and set let mybyte = MyByte::new() .withkind(15) .withsystem(false) .withlevel(3) .with_present(true);

assert!(my_byte.present()); ```

Features

Additionally, this crate has a few useful features, which are shown here in more detail.

The example below shows how attributes are carried over and how signed integers, padding, and custom types are handled.

```rust /// A test bitfield with documentation

[bitfield(u64)]

[derive(PartialEq, Eq)] // <- Attributes after bitfield are carried over

struct MyBitfield { /// defaults to 16 bits for u16 int: u16, /// interpreted as 1 bit flag flag: bool, /// custom bit size #[bits(1)] tiny: u8, /// sign extend for signed integers #[bits(13)] negative: i16, /// supports any type that implements From<u64> and Into<u64> #[bits(16)] custom: CustomEnum, /// public field -> public accessor functions #[bits(12)] pub public: usize, /// padding #[bits(5)] p: u8, /// zero-sized members are ignored #[bits(0)] _completelyignored: String, } /// A custom enum

[derive(Debug, PartialEq, Eq)]

[repr(u64)]

enum CustomEnum { A = 0, B = 1, C = 2, } // implement From<u64> and Into<u64> for CustomEnum!

// Usage: let mut val = MyBitfield::new() .withint(3 << 15) .withflag(true) .withtiny(1) .withnegative(-3) .withcustom(CustomEnum::B) .withpublic(2);

println!("{val:?}"); let raw: u64 = val.into(); println!("{raw:b}");

asserteq!(val.int(), 3 << 15); asserteq!(val.flag(), true); asserteq!(val.negative(), -3); asserteq!(val.tiny(), 1); asserteq!(val.custom(), CustomEnum::B); asserteq!(val.public(), 2);

// const members asserteq!(MyBitfield::FLAGBITS, 1); asserteq!(MyBitfield::FLAGOFFSET, 16);

val.setnegative(1); asserteq!(val.negative(), 1); ```

The macro generates three accessor functions for each field. Each accessor also inherits the documentation of its field.

The signatures for int are:

```rust // generated struct struct MyBitfield(u64); impl MyBitfield { const fn new() -> Self { Self(0) }

const INT_BITS: usize = 16;
const INT_OFFSET: usize = 0;

const fn with_int(self, value: u16) -> Self { /* ... */ }
const fn int(&self) -> u16 { /* ... */ }
fn set_int(&mut self, value: u16) { /* ... */ }

// other field ...

} // generated trait implementations impl From for MyBitfield { /* ... / } impl From for u64 { / ... / } impl Debug for MyBitfield { / ... */ } ```

Hint: You can use the rust-analyzer "Expand macro recursively" action to view the generated code.

fmt::Debug

This macro automatically creates a suitable fmt::Debug implementation similar to the ones created for normal structs by #[derive(Debug)]. You can disable it with the extra debug argument.

```rust

[bitfield(u64, debug = false)]

struct CustomDebug { data: u64 } impl fmt::Debug for CustomDebug { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "0x{:x}", self.data()) } }

let val = CustomDebug::new().with_data(123); println!("{val:?}") ```