bitbybit: Bit fields and bit enums

This crate provides macros that create bit fields and bit enums, which are useful in bit packing code (e.g. in drivers or networking code).

Some highlights:

Basic declaration

A bit field is created similar to a regular Rust struct. Annotations define the layout of the structure. As an example, consider the following definition, which specifies a bit field:

```rs

[bitfield(u32)]

struct GICD_TYPER { #[bits(11..=15, r)] lspi: u5,

#[bit(10, r)]
security_extn: bool,

#[bits(5..=7, r)]
cpu_number: u3,

#[bits(0..=4, r)]
itlines_number: u5,

} ```

How this works:

Enumerations

Very often, fields aren't just numbers but really enums. This is supported by first defining a bitenum and then using that inside of a bitfield:

```rs

[bitenum(u2, exhaustive: false)]

enum NonExhaustiveEnum { Zero = 0b00, One = 0b01, Two = 0b10, }

[bitenum(u2, exhaustive: true)]

enum ExhaustiveEnum { Zero = 0b00, One = 0b01, Two = 0b10, Three = 0b11, }

[bitfield(u64, default: 0)]

struct BitfieldWithEnum { #[bits(2..=3, rw)] e2: Option,

#[bits(0..=1, rw)]
e1: ExhaustiveEnum,

} ```

Arrays

Sometimes, bits inside of bitfields are repeated. To support this, this crate allows specifying bitwise arrays. For example, the following struct gives read/write access to each individual nibble (hex character) of a u64:

```rs

[bitfield(u64, default: 0)]

struct Nibble64 { #[bits(0..=3, rw)] nibble: [u4; 16], } ```

Arrays can also have a stride. This is useful in the case of multiple smaller values repeating. For example, the following definition provides access to each bit of each nibble:

```rs

[bitfield(u64, default: 0)]

struct NibbleBits64 { #[bit(0, rw, stride: 4)] nibble_bit0: [bool; 16],

#[bit(1, rw, stride: 4)]
nibble_bit1: [bool; 16],

#[bit(2, rw, stride: 4)]
nibble_bit2: [bool; 16],

#[bit(3, rw, stride: 4)]
nibble_bit3: [bool; 16],

} ```

Dependencies

Arbitrary bit widths like u5 or u67 do not exist in Rust at the moment. Therefore, the following dependency is required:

toml arbitrary-int = "1.2.0"

Usage

Eventhough bitfields feel somewhat like structs, they are internally implemented as simple data types like u32. Therefore, they provide an immutable interface: Instead of changing the value of a field, any change operation will return a new bitfield with that field modified.

rs let a = NibbleBits64::new_with_raw_value(0x12345678_ABCDEFFF); // Read a value assert_eq!(u4::new(0xE), a.nibble(3)); // Change a value let b = a.with_nibble(0, u4::new(0x3)) assert_eq!(0x12345678_ABCDEFF3, nibble.raw_value());