14个版本
0.3.18 | 2022年9月12日 |
---|---|
0.3.17 | 2022年8月19日 |
0.3.14 |
|
0.3.11 | 2022年4月21日 |
0.2.13 | 2021年12月22日 |
#48 在 #packing
6,973 每月下载量
在 9 个crate中使用(通过 bondrewd)
320KB
4.5K SLoC
Bondrewd
一个proc-macro crate,用于安全高效地实现由原始类型和数组组成的结构的from_bytes
和into_bytes
。
该crate的主要功能包括
- 解码/编码大端或小端数据结构
- 关联函数用于读取/写入单个字段,而不是解码/编码整个结构,从而节省许多指令
- 能够从整数类型解码/编码类似C的枚举
- 使用属性进行纯Rust类型,包括指定端序、位长度、位定位等...
- 默认不生成可失败函数。存在两种错误类型,但它们位于crate功能
"slice_fns"
和"hex_fns"
中。 - 将结构压缩成少量的位,或用于在多个字节中展开大数据结构
- 所有生成的代码都支持
no_std
,并且是100%安全的代码。
快速入门
将以下内容添加到Cargo.toml
中的dependencies
[dependencies]
bondrewd = { version = "^0.1", features = ["derive"] }
bondrewd
可以轻松应用于结构以实现类似位字段的结构
use bondrewd::{BitfieldEnum, Bitfields};
///! Implement a basic CCSDS 133.0-B-2 Primary Header using rust Enums to specify fields
/// Packet Sequence Flags as per 4.1.3.4.2.2
#[derive(BitfieldEnum, Clone, PartialEq, Eq, Debug)]
pub enum CcsdsPacketSequenceFlags {
Continuation,
Start,
End,
Unsegmented,
Invalid(u8),
}
/// CCSDS Packet version as per 4.1.3.2
#[derive(BitfieldEnum, Clone, PartialEq, Eq, Debug)]
#[bondrewd_enum(u8)]
pub enum CcsdsPacketVersion {
One,
Two,
Invalid,
}
/// Primary header object as per 4.1.3
#[derive(Bitfields, Clone, PartialEq, Eq, Debug)]
#[bondrewd(default_endianness = "be", enforce_bytes = 6)]
pub struct CcsdsPacketHeader {
#[bondrewd(enum_primitive = "u8", bit_length = 3)]
pub(crate) packet_version_number: CcsdsPacketVersion,
pub(crate) packet_type: bool,
pub(crate) sec_hdr_flag: bool,
#[bondrewd(bit_length = 11)]
pub(crate) app_process_id: u16,
#[bondrewd(enum_primitive = "u8", bit_length = 2)]
pub(crate) sequence_flags: CcsdsPacketSequenceFlags,
#[bondrewd(bit_length = 14)]
pub(crate) packet_seq_count: u16,
pub(crate) packet_data_length: u16,
}
// Now you're on your way to space :)
// Lets see what this can generate
fn main() {
let packet = CcsdsPacketHeader {
packet_version_number: CcsdsPacketVersion::Invalid,
packet_type: true,
sec_hdr_flag: true,
app_process_id: 55255 & 0b0000011111111111,
sequence_flags: CcsdsPacketSequenceFlags::Unsegmented,
packet_seq_count: 65535 & 0b0011111111111111,
packet_data_length: 65535,
};
// Turn into some bytes (clone used to assert_eq later)
let bytes = packet.clone().into_bytes();
// Play with some of the fields
match CcsdsPacketHeader::read_sequence_flags(&bytes) {
CcsdsPacketSequenceFlags::Unsegmented => println!("Unsegmented!"),
CcsdsPacketSequenceFlags::End => println!("End!"),
_ => println!("Something else")
}
// Set the secondary header flag
CcsdsPacketHeader::write_sec_hdr_flag(&mut bytes, false);
// Get back from bytes, check them
let new_packet = CcsdsPacketHeader::from_bytes(bytes);
assert_eq!(new_packet.sec_hdr_flag, false);
assert_eq!(new_packet.app_process_id, packet.app_process_id);
}
使用Derive的详细信息
bondrewd
为以下内容实现了几个derive属性
- 结构
- 字段
- 枚举
struct
Derive功能
from_bytes
和into_bytes
函数通过bondrewd中的Bitfields trait创建。- 无运行时成本的逆字节序。
#[bondrewd(reverse)]
- 使用
Msb0
或Lsb0
进行位0定位,只需微小的编译时间成本。#[bondrewd(read_from = "ZERO_BIT_LOCATION")]
.ZERO_BIT_LOCATION
可以是mbs0
或lsb0
。
- 按字段逐个解包的读取函数。如果您只需要几个字段,而不想解包整个结构,则非常有用。
read_{field_name}()
和read_slice_{field_name}()
。
- 位大小强制执行。指定输出应有多少位/字节。
#[bondrewd(enforce_bits={位数量})]
#[bondrewd(enforce_bytes={字节数量})]
#[bondrewd(enforce_full_bytes)]
field
提取特征
- 原语的自然类型。无自定义类型包装。
#[bondrewd(bit_length={总位数量})]
#[bondrewd(byte_length={总字节数量})]
#[bondrewd(bits = "FIRST_BIT_INDEX..LAST_BIT_INDEX_PLUS_ONE")]
(待测试)。
- 可以捕获无效变体的枚举字段。
#[bondrewd(enum_primitive = "u8")]
。目前,u8 是唯一支持的类型,未来将支持更多。
- 内部结构。
#[bondrewd(struct_size={总字节数})]
- 每个字段的字节序控制。
#[bondrewd(endianness = "{ENDIANNESS}")]
,ENDIANNESS 可以是:le
、be
、msb
、lsb
、big
、little
。使用您喜欢的。
- 数组。
- 元素数组。定义数组中每个元素的位长度。
#[bondrewd(element_bit_length={每个元素的总位数量})]
#[bondrewd(element_byte_length={每个元素的总字节数量})]
- 块数组。定义整体位长度。例如
[u8;4]
定义为 28 位长度将移除 4 个最高有效位。#[bondrewd(block_bit_length={总位数量})]
#[bondrewd(block_byte_length={总字节数量})]
- 元素数组。定义数组中每个元素的位长度。
- 自动保留字段。如果结构总位数量不是 8 的倍数,则忽略末尾的未使用位。
- 忽略保留字段。read_ 和 read_slice_ 函数仍然生成,但 into_bytes 和 from_bytes 只使用零。
#[bondrewd(reserve)]
enum
提取特征
- 从 from_primitive 和 into_primitive 派生。
- 指定一个
Invalid
变体以捕获无意义的值,否则将使用最后一个值作为通配符。#[bondrewd_enum(invalid)]
.
- 为枚举变体的区分符指定自定义的
u8
文本。 - 与原始数据类型不兼容。类似于上面的 Invalid 通配符,但它将值存储为变体字段。
为什么选择 Bondrewd
从历史角度看,创建这个软件包的主要原因是为了在空间通信协议(例如 CCSDS/AOS/TC/TM...)之间共享不同软件服务和依赖项的复杂数据结构,而不会因为解码/编码整个 struct
而带来性能损失。最初,我们使用诸如 modular_bitfield 和 packed_struct 这样的软件包编写这些格式的代码,并在所有软件服务之间共享一个通用的软件包。对于我们的软件,在传输大量数据时,我们受到了计算能力的严重限制,花费了大量时间解码/编码数据结构。我们发现,对于通信服务中的某些部分,我们不需要从字节中解码整个结构来处理通信数据包。此外,我们经常只设置结构中的一个字段,然后将数据包传递到下一阶段。因此,为了解决这些问题,我们需要一个位字段实现,它既具有性能和安全性,又能够只解码数据的小部分以确定将数据传递给哪个节点/进程。
Both modular_bitfields/packed_struct
都是优秀/稳定的库,并且这两个库中的任何一个现有的、性能良好且正确的实现都足以满足几乎所有用例。然而,这个软件包旨在填补以下两个库的以下部分:
- 通过关联函数读取函数。按字段进行解包。
- 自然类型,直接使用原语与 proc-macro 属性。
- ModularBitfields 或 PackedStruct 都不支持具有非 2 的幂次方位长度的字段。
- 枚举字段支持,可以捕获无效数字而不会崩溃。
- 注意。PackedStruct 提供此功能,但您必须使用内置的 EnumType 包装器。ModularBitfields 表现出崩溃行为。
- 无运行时成本的逆字节序。
- 位 0 定位。Msb0 或 Lsb0
- PackedStruct 提供此功能。
- 使用非 2 的幂次方位长度的位大小强制执行。
依赖关系
~1.5MB
~35K SLoC