Procedural macro for bitfields that allows specifying bitfields as structs.
As this library provides a procedural macro, it has no runtime dependencies and works for no-std
.
Add this to your Cargo.toml
:
toml
[dependencies]
bitfield-struct = "0.3"
```rs use bitfield_struct::bitfield; /// A test bitfield with documentation
bitfield
are applied carried overstruct 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
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 addr
are:
```rs // generated struct struct MyBitfield(u64); impl MyBitfield { const fn new() -> Self { Self(0) }
const INT_BITS: usize = 32;
const INT_OFFSET: usize = 0;
const fn with_int(self, value: u32) -> Self { /* ... */ }
const fn int(&self) -> u32 { /* ... */ }
fn set_int(&mut self, value: u32) { /* ... */ }
// other field ...
}
// generated trait implementations
impl From
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.
```rs
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:?}") ```