#bit-field #integer #misaligned #unaligned #bitfields

no-std bitbybit

高效实现位字段,其中多个数字打包在一个较大的数字和位枚举中。适用于驱动程序,因此在no_std环境中也能工作

13个稳定版本

1.3.2 2024年3月2日
1.2.2 2023年7月11日
1.1.3 2023年3月10日
1.1.2 2022年12月6日
1.1.1 2022年9月28日

嵌入式开发类别中排名第209

Download history 173/week @ 2024-03-13 119/week @ 2024-03-20 103/week @ 2024-03-27 108/week @ 2024-04-03 102/week @ 2024-04-10 149/week @ 2024-04-17 100/week @ 2024-04-24 90/week @ 2024-05-01 113/week @ 2024-05-08 80/week @ 2024-05-15 85/week @ 2024-05-22 177/week @ 2024-05-29 161/week @ 2024-06-05 172/week @ 2024-06-12 157/week @ 2024-06-19 985/week @ 2024-06-26

每月下载量1,500
用于5个crate(其中3个直接使用)

MIT许可证

78KB
1.5K SLoC

bitbybit: 位字段和位枚举

此crate提供创建位字段和位枚举的宏,这些宏在位打包代码(例如在驱动程序或网络代码中)中非常有用。

一些亮点

  • 高效且100%安全的代码,其性能与手写的位移和掩码一样好
  • 完全兼容const上下文
  • 可在no-std环境中使用
  • 强大的编译时保证(例如,从一个位字段中取出5位放入另一个字段中甚至不需要编译边界检查)
  • 自动创建位枚举,允许将枚举转换为数字或从数字转换为枚举
  • 在位字段内支持数组以表示重复的位模式

基本声明

位字段创建方式类似于常规Rust结构。注解定义了结构的布局。以下是一个示例定义,它指定了一个位字段

#[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,
}

这是如何工作的

  • #[bitfield(u32)]指定这是一个位字段,其中u32是底层数据类型。这意味着位字段中的所有位都必须适合32位。支持内置Rust类型(u8、u16、u32、u64、u128)以及任意整数(u17、u48等)。
  • 每个字段都注解了字段使用的位范围。数据类型必须匹配位数:与u8匹配的0..=8的范围会导致编译错误,因为u9是与0..=8匹配的数据类型。
  • 单比特字段声明为"bit",其他所有字段声明为"bits"
  • 字段的合法数据类型是基本类型u8、u16、u32、u64、u128、bool以及枚举(见下文)或类似u1、u2、u3的类型的arbitrary-int
  • 位编号是LSB0,这意味着从底部开始计数位:bit(0)的值为0x1,bit(1)是0x2,bit(2)是0x4,bit(15)是0x8000,依此类推。如果需求,可以稍后添加MSB0模式(以匹配某些大端设备的文档)。
  • 字段声明为“r”表示只读,为“w”表示只写,或为“rw”表示读写。在上面的示例中,所有字段都是只读的,因为该特定寄存器仅用于读取值。

枚举

很多时候,字段不仅仅是数字,而是真正的枚举。这可以通过首先定义一个bitenum,然后在位域中使用它来实现。

#[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<NonExhaustiveEnum>,

    #[bits(0..=1, rw)]
    e1: ExhaustiveEnum,
}
  • bitenum宏将枚举转换为可以在位域中使用的枚举。第一个参数是基本数据类型,它指定了枚举占用多少位。这可以是u1到u64之间的任何值。
  • 当枚举在位域中使用时,位数的匹配是必须的 - 如果不匹配,将会有编译器错误。
  • exhaustive参数指定是否包含枚举中所有可能的位组合。上面的示例中有一个既是exhaustive又是non-exhaustive的枚举。注意,non-exhaustive枚举必须被Option包裹以处理e2不是定义的枚举值的情况。

数组

有时,位域内的位会被重复。为此,此crate允许指定位向量数组。例如,以下struct提供了对u64中每个单独的半字节(十六进制字符)的读写访问。

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

数组还可以有步长。这在多个较小值重复的情况下很有用。例如,以下定义提供了对每个半字节中每个位的访问。

#[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],
}

默认值

位域始终可以通过new_with_raw_value()创建。

let a = NibbleBits64::new_with_raw_value(0x43218765);

然而,通常特定的值可以被认为是默认值(例如0)。这可以这样指定。

#[bitfield(u32, default = 0x1234)]
struct Bitfield1 {
  #[bits(0..=3, rw)]
  nibble: [u4; 4],
}

如果指定了默认值,位域可以很容易地使用此特定值创建。

let a = Bitfield1::Default;
const A: Bitfield1 = Bitfield1::DEFAULT;

默认值按原样使用,即使它们影响了位域中未定义的位。

使用构建器语法一次设置所有字段

可以一次设置所有字段,如下所示

const T: Test = Test::builder()
    .with_baudrate(0x12)
    .with_some_other_bits(u4::new(0x2))
    .with_array([1, 2, 3, 4])
    .build();

使用builder(),不可能忘记设置任何字段。这是在编译时检查的:如果任何字段未设置,则不能调用build()

目前,必须按照指定的顺序设置所有字段。随着Rust的const generics变得更加强大,这个限制可能会被取消。

要使builder()可用,以下必须为真

  • 位域必须完全填充可写字段(无间隙)或必须指定默认值。
  • 没有可写字段重叠。

非连续位范围

有时,将位范围设置为非连续可能很有用。例如,RISC-V按某种方式定义了一些立即数,它们必须被重新组装。这可以这样实现。

  #[bitfield(u32)]
  struct SBFormat {
      #[bits([8..=11, 25..=30, 7, 31], rw)]
      imm_half: u12,
  }

依赖关系

在Rust中目前不存在像u5或u67这样的任意位宽。因此,需要以下依赖项。

arbitrary-int = "1.2.7"

用法

尽管位域在某种程度上类似于结构体,但它们在内部作为像u32这样的简单数据类型实现。因此,它们提供了一个不可变接口:而不是改变字段的值,任何更改操作都将返回一个具有修改后字段的新的位域。

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, b.raw_value());

依赖关系

~335–780KB
~18K SLoC