11个版本

0.1.14 2022年6月29日
0.1.13 2022年6月22日
0.1.12 2022年3月3日
0.1.11 2022年1月12日
0.1.6 2021年12月22日

#289数据结构

Download history 1252/week @ 2024-03-14 1386/week @ 2024-03-21 1762/week @ 2024-03-28 1317/week @ 2024-04-04 1065/week @ 2024-04-11 1286/week @ 2024-04-18 1212/week @ 2024-04-25 1521/week @ 2024-05-02 1135/week @ 2024-05-09 1127/week @ 2024-05-16 1287/week @ 2024-05-23 1208/week @ 2024-05-30 1086/week @ 2024-06-06 1596/week @ 2024-06-13 1137/week @ 2024-06-20 1186/week @ 2024-06-27

5,194 每月下载量
用于 8 个crate(7个直接使用)

MIT/Apache

14KB
182 代码行

Bondrewd

一个proc-macro crate,用于安全高效地为由原始类型和数组组成的结构体实现from_bytes/into_bytes

该crate的主要功能包括

  • 解码/编码大端或小端数据结构
  • 关联函数,用于读取/写入单个字段,而不是解码/编码整个结构,从而节省许多指令
  • 能够从整数类型解码/编码类似C的枚举
  • 使用属性进行纯Rust类型化,包括分配字节序/位长度/位定位/...
  • 默认不生成不可失败的功能。存在两种错误类型,但它们都在crate功能"slice_fns""hex_fns"中。
  • 将结构压缩成少量的位,或者用于将大型数据结构扩展到多个字节
  • 所有生成的代码都是no_std兼容的,并且是100%安全的代码。

快速入门

将以下内容添加到dependencies中的Cargo.toml

[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);
}

使用派生详情

bondrewd为结构体、字段和枚举实现了几个派生属性

  • 结构体
  • 字段
  • 枚举

struct派生功能

  • 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 提取特征

  • 从_primitive和into_primitive派生。
  • 指定一个Invalid变体来捕获没有意义的值,否则将使用最后一个值作为通用的捕获。
    • #[bondrewd_enum(无效)].
  • 为枚举变体的区分符指定自定义 u8 字面量
  • 与原始数据类型无效。类似于上面的 Invalid 捕获所有,但它将值存储为变体字段。

为什么选择 Bondrewd

历史上,创建此软件包的主要原因是为了在不同软件服务和依赖项之间共享用于空间通信协议的复杂数据结构(例如 CCSDS/AOS/TC/TM...),而不需要为解码/编码整个 struct 结构体而付出性能代价。最初,我们使用类似 modular_bitfieldpacked_struct 的软件包编写这些格式的代码,并在所有软件服务之间共享一个通用的软件包。对于我们来说,在传输大量数据时,由于计算能力有限,我们花费了大量时间进行解码/编码数据结构。我们发现,对于通信服务的一些部分,我们不需要从字节中解码整个结构来处理通信数据包。此外,我们发现很多时候我们只需设置结构中的一个字段并将数据包传递到下一个阶段。因此,为了解决这些问题,我们需要一个位字段实现,它能够提供性能和安全,并且能够只解码数据的小部分以确定将数据传递给哪个节点/进程。

modular_bitfields/packed_struct 都是优秀的/稳定的库,这两个库的现有、高性能和正确实现几乎适用于所有用例。然而,这个软件包旨在填补以下两个库的以下内容

  • 通过关联函数读取函数。按字段逐个解包。
  • 自然类型,直接使用 proc-macro 属性与原始数据类型。
    • ModularBitfields 或 PackedStruct 都不为此类字段提供此功能:这些字段的位长度不是 2 的幂。
  • 枚举字段支持,可以捕获无效数字而不会引发恐慌。
    • 注意。PackedStruct 提供此功能,但您需要使用内置的 EnumType 包装器。ModularBitfields 表现出恐慌行为。
  • 无需运行时成本即可反转字节顺序。
  • 位 0 定位。Msb0 或 Lsb0
    • PackedStruct 提供此功能。
  • 使用非 2 的幂位长度的位大小强制。

依赖关系

~220KB