1个不稳定版本
0.11.2 | 2021年5月22日 |
---|
#37 in #位字段
2,418 每月下载量
在 6 个crate中使用 (通过 modular-bitfield-msb)
90KB
1.5K SLoC
Rust的模块化位字段(MSB优先)
持续集成 | 文档 | Crates.io | LoC |
---|---|---|---|
no_std
: 支持不使用std
库的嵌入式开发。- 此crate使用和生成100%安全的Rust代码。
MSB优先版本
此crate是 modular-bitfield 的分支。使用方式相同,有两个关键区别
- 字段以MSB到LSB的顺序排列。
- 跨越多个字节的字段以大端存储。
描述
允许位字段结构和枚举作为位字段说明符,其工作方式与C和C++位字段非常相似。
优点
- 安全性:宏嵌入的枚举和结构体在编译时检查结构是否有效。
- 速度:生成的代码与手写代码一样快。(见下文基准测试。)
- 模块化:枚举可以在位字段结构中模块化使用。
归属
实现了David Tolnay在其 过程宏研讨会 中引入和指定的 #[bitfield]
宏。
感谢David Tolnay为此crate中实现的宏设计规范。
使用
使用 #[bitfield]
属性注释Rust结构体,以将其转换为位字段。可以使用 B1
、B2
、... B128
预言类型作为原语来声明每个字段比特数。
#[bitfield]
pub struct PackedData {
header: B4,
body: B9,
is_alive: B1,
status: B2,
}
这会生成一个 new
构造函数以及各种获取器和设置器,允许以安全的方式与位字段交互
示例:构造函数
let data = PackedData::new()
.with_header(1)
.with_body(2)
.with_is_alive(0)
.with_status(3);
assert_eq!(data.header(), 1);
assert_eq!(data.body(), 2);
assert_eq!(data.is_alive(), 0);
assert_eq!(data.status(), 3);
示例:原语类型
实现了 Specifier
特性的任何类型都可以用作位字段字段。除了已经提到的 B1
,.. B128
,还可以使用来自预定义的 bool
, u8
, u16
, u32
, u64
或 u128
基本类型。
我们可以利用这个知识,将我们的 is_alive
编码为 bool
类型,而不是 B1
#[bitfield]
pub struct PackedData {
header: B4,
body: B9,
is_alive: bool,
status: B2,
}
let mut data = PackedData::new()
.with_is_alive(true);
assert!(data.is_alive());
data.set_is_alive(false);
assert!(!data.is_alive());
示例:枚举指定符
可以为枚举类型非常容易地推导出 Specifier
特性,使它们也可以作为位字段类型中的字段使用
#[derive(BitfieldSpecifier)]
pub enum Status {
Red, Green, Yellow, None,
}
#[bitfield]
pub struct PackedData {
header: B4,
body: B9,
is_alive: bool,
status: Status,
}
示例:额外的安全性保障
为了确保我们的 Status
枚举正好需要 2 位,我们可以在其字段上添加 #[bits = 2]
#[bitfield]
pub struct PackedData {
header: B4,
body: B9,
is_alive: bool,
#[bits = 2]
status: Status,
}
设置和获取我们的新 status
字段自然如下
let mut data = PackedData::new()
.with_status(Status::Green);
assert_eq!(data.status(), Status::Green);
data.set_status(Status::Red);
assert_eq!(data.status(), Status::Red);
示例:递归位字段
可以将 #[bitfield]
结构体用作 #[bitfield]
结构体的字段。这在多个位字段有一些公共字段时非常有用,可以通过向带有 #[derive(BitfieldSpecifier)]
的结构体属性添加 #[bitfield]
来实现
#[bitfield]
#[derive(BitfieldSpecifier)]
pub struct Header {
is_compact: bool,
is_secure: bool,
pre_status: Status,
}
#[bitfield]
pub struct PackedData {
header: Header,
body: B9,
is_alive: bool,
status: Status,
}
通过在 Header
结构体上的 #[bitfield]
宏和 Status
枚举上的 #[bits: int]
属性中使用 bits: int
参数,我们可以对生成的实体的位宽度有额外的编译时保证
#[derive(BitfieldSpecifier)]
#[bits = 2]
pub enum Status {
Red, Green, Yellow
}
#[bitfield(bits = 4)]
#[derive(BitfieldSpecifier)]
pub struct Header {
is_compact: bool,
is_secure: bool,
#[bits = 2]
pre_status: Status,
}
#[bitfield(bits = 16)]
pub struct PackedData {
#[bits = 4]
header: Header,
body: B9,
is_alive: bool,
#[bits = 2]
status: Status,
}
示例:高级枚举指定符
对于我们的 Status
枚举,我们实际上只需要3个状态变体:Green
、Yellow
和 Red
。我们引入了 None
状态变体,因为默认情况下 Specifier
枚举需要具有2的幂次方的变体数量。我们可以通过指定 #[bits = 2]
在顶部来解决这个问题,并移除我们的占位符 None
变体,同时保持它需要2位的不变性
# use modular_bitfield_msb::prelude::*;
#[derive(BitfieldSpecifier)]
#[bits = 2]
pub enum Status {
Red, Green, Yellow,
}
然而,现在拥有这样的枚举会导致位字段可能包含无效的位模式。我们可以通过受保护的获取器安全地访问这些字段。为了演示,我们将使用生成的 from_bytes
构造函数,我们可以轻松地构建可能包含无效位模式的位字段
let mut data = PackedData::from_bytes([0b0000_0000, 0b1100_0000]);
// The 2 status field bits are invalid -----^^
// as Red = 0x00, Green = 0x01 and Yellow = 0x10
assert_eq!(data.status_or_err(), Err(InvalidBitPattern { invalid_bytes: 0b11 }));
data.set_status(Status::Green);
assert_eq!(data.status_or_err(), Ok(Status::Green));
基准测试
以下是一些基准测试,比较了 手动编写的代码 和为一些示例获取器和设置器生成的宏代码,这些获取器和设置器覆盖了相当广泛的使用案例。
我们可以得出结论,宏生成的代码与手动编写的代码一样快。如果您看到改进任何一方的途径,请提交PR。
cargo bench
以运行基准测试cargo test --benches
以运行基准测试测试
总结
modular_bitfield_msb
包生成的位字段与 ...
- 手动编写的替代方案一样高效。
- 与替代 bitfield 包同样高效或更高效。
展示:生成与手动编写
我们测试了以下 #[bitfield]
struct
#[bitfield]
pub struct Generated {
pub a: B9, // Spans 2 bytes.
pub b: B6, // Within 2nd byte.
pub c: B13, // Spans 3 bytes.
pub d: B1, // Within 4rd byte.
pub e: B3, // Within 4rd byte.
pub f: B32, // Spans rest 4 bytes.
}
注意:所有基准测试时间结果总和为10次运行。
获取器性能
get_a/generated time: [3.0990 ns 3.1119 ns 3.1263 ns]
get_a/handwritten time: [3.1072 ns 3.1189 ns 3.1318 ns]
get_b/generated time: [3.0859 ns 3.0993 ns 3.1140 ns]
get_b/handwritten time: [3.1062 ns 3.1154 ns 3.1244 ns]
get_c/generated time: [3.0892 ns 3.1140 ns 3.1491 ns]
get_c/handwritten time: [3.1031 ns 3.1144 ns 3.1266 ns]
get_d/generated time: [3.0937 ns 3.1055 ns 3.1182 ns]
get_d/handwritten time: [3.1109 ns 3.1258 ns 3.1422 ns]
get_e/generated time: [3.1009 ns 3.1139 ns 3.1293 ns]
get_e/handwritten time: [3.1217 ns 3.1366 ns 3.1534 ns]
get_f/generated time: [3.1064 ns 3.1164 ns 3.1269 ns]
get_f/handwritten time: [3.1067 ns 3.1221 ns 3.1404 ns]
设置器性能
set_a/generated time: [15.784 ns 15.855 ns 15.932 ns]
set_a/handwritten time: [15.841 ns 15.907 ns 15.980 ns]
set_b/generated time: [20.496 ns 20.567 ns 20.643 ns]
set_b/handwritten time: [20.319 ns 20.384 ns 20.454 ns]
set_c/generated time: [19.155 ns 19.362 ns 19.592 ns]
set_c/handwritten time: [19.265 ns 19.383 ns 19.523 ns]
set_d/generated time: [12.325 ns 12.376 ns 12.429 ns]
set_d/handwritten time: [12.416 ns 12.472 ns 12.541 ns]
set_e/generated time: [20.460 ns 20.528 ns 20.601 ns]
set_e/handwritten time: [20.473 ns 20.534 ns 20.601 ns]
set_f/generated time: [6.1466 ns 6.1769 ns 6.2127 ns]
set_f/handwritten time: [6.1467 ns 6.1962 ns 6.2670 ns]
许可证
根据您的选择,许可协议为 Apache License,版本 2.0 或 MIT 许可证。
除非您明确说明,否则您故意提交的、根据 Apache-2.0 许可证定义的任何贡献,均应按上述方式双重许可,不附加任何其他条款或条件。
依赖项
~1.5MB
~36K SLoC