14个版本

0.3.18 2022年9月12日
0.3.17 2022年8月19日
0.3.14 2022年6月30日
0.3.11 2022年4月21日
0.2.13 2021年12月22日

#48#packing

Download history • Rust 包仓库 1219/week @ 2024-04-25 • Rust 包仓库 1528/week @ 2024-05-02 • Rust 包仓库 1136/week @ 2024-05-09 • Rust 包仓库 1134/week @ 2024-05-16 • Rust 包仓库 1292/week @ 2024-05-23 • Rust 包仓库 1211/week @ 2024-05-30 • Rust 包仓库 1098/week @ 2024-06-06 • Rust 包仓库 1591/week @ 2024-06-13 • Rust 包仓库 1135/week @ 2024-06-20 • Rust 包仓库 1396/week @ 2024-06-27 • Rust 包仓库 1150/week @ 2024-07-04 • Rust 包仓库 1407/week @ 2024-07-11 • Rust 包仓库 1651/week @ 2024-07-18 • Rust 包仓库 1878/week @ 2024-07-25 • Rust 包仓库 1281/week @ 2024-08-01 • Rust 包仓库 1961/week @ 2024-08-08 • Rust 包仓库

6,973 每月下载量
9 个crate中使用(通过 bondrewd

MIT/Apache

320KB
4.5K SLoC

Bondrewd

一个proc-macro crate,用于安全高效地实现由原始类型和数组组成的结构的from_bytesinto_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_bytesinto_bytes函数通过bondrewd中的Bitfields trait创建。
  • 无运行时成本的逆字节序。
    • #[bondrewd(reverse)]
  • 使用Msb0Lsb0进行位0定位,只需微小的编译时间成本。
    • #[bondrewd(read_from = "ZERO_BIT_LOCATION")]. ZERO_BIT_LOCATION 可以是 mbs0lsb0
  • 按字段逐个解包的读取函数。如果您只需要几个字段,而不想解包整个结构,则非常有用。
    • 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 可以是: lebemsblsbbiglittle。使用您喜欢的。
  • 数组。
    • 元素数组。定义数组中每个元素的位长度。
      • #[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_bitfieldpacked_struct 这样的软件包编写这些格式的代码,并在所有软件服务之间共享一个通用的软件包。对于我们的软件,在传输大量数据时,我们受到了计算能力的严重限制,花费了大量时间解码/编码数据结构。我们发现,对于通信服务中的某些部分,我们不需要从字节中解码整个结构来处理通信数据包。此外,我们经常只设置结构中的一个字段,然后将数据包传递到下一阶段。因此,为了解决这些问题,我们需要一个位字段实现,它既具有性能和安全性,又能够只解码数据的小部分以确定将数据传递给哪个节点/进程。

Both modular_bitfields/packed_struct 都是优秀/稳定的库,并且这两个库中的任何一个现有的、性能良好且正确的实现都足以满足几乎所有用例。然而,这个软件包旨在填补以下两个库的以下部分:

  • 通过关联函数读取函数。按字段进行解包。
  • 自然类型,直接使用原语与 proc-macro 属性。
    • ModularBitfields 或 PackedStruct 都不支持具有非 2 的幂次方位长度的字段。
  • 枚举字段支持,可以捕获无效数字而不会崩溃。
    • 注意。PackedStruct 提供此功能,但您必须使用内置的 EnumType 包装器。ModularBitfields 表现出崩溃行为。
  • 无运行时成本的逆字节序。
  • 位 0 定位。Msb0 或 Lsb0
    • PackedStruct 提供此功能。
  • 使用非 2 的幂次方位长度的位大小强制执行。

依赖关系

~1.5MB
~35K SLoC