14 个版本 (7 个重大变化)

0.10.1 2022年11月16日
0.10.0 2021年9月15日
0.6.1 2021年9月10日
0.6.0 2021年6月27日
0.2.3 2018年3月29日

#79 in 编码

Download history 15366/week @ 2024-03-14 16651/week @ 2024-03-21 17509/week @ 2024-03-28 18288/week @ 2024-04-04 15767/week @ 2024-04-11 16116/week @ 2024-04-18 17844/week @ 2024-04-25 17188/week @ 2024-05-02 18122/week @ 2024-05-09 15377/week @ 2024-05-16 15544/week @ 2024-05-23 15056/week @ 2024-05-30 14405/week @ 2024-06-06 15349/week @ 2024-06-13 15456/week @ 2024-06-20 11460/week @ 2024-06-27

59,582 每月下载量
43 个 crate (26 directly) 中使用

MIT/Apache

90KB
1.5K SLoC

Rust 的位级打包和解包

Crates.io Documentation master

简介

位级结构的打包和解包通常是重新发明轮子的编程任务。此库提供了一种元编程方法,使用属性来定义字段及其打包方式。生成的 trait 实现提供安全的打包、解包和运行时调试格式化程序,并为每个结构体生成字段文档。

功能

  • 带属性的普通 Rust 结构体
  • 用户定义位宽的 MSB 或 LSB 整数
  • 原始枚举代码生成辅助工具
  • MSB0 或 LSB0 位定位
  • 记录字段的打包表
  • 运行时打包可视化
  • 嵌套打包类型
  • 作为字段的打包结构体数组
  • 保留字段,它们的位总是 0 或 1

crate 级功能标志

  • std: 使用 Rust 标准库。默认。
  • alloc: 在 no_std + alloc 场景下使用 alloc crate。需要 nightly Rust。
  • use_serde: 向内置辅助类型添加序列化支持。
  • byte_types_64, byte_types_256: 扩大生成的数组、字节和位宽类型的大小。

示例用法

Cargo.toml

[dependencies]
packed_struct = "0.10"

导入具有最常见特性和 derive 宏的库

// This is only needed for pre Rust 2018
#[macro_use] extern crate packed_struct;
// Prelude import with the common imports
use packed_struct::prelude::*;

单个字节结构的示例,包含 3 位整数、原始枚举和布尔字段。

use packed_struct::prelude::*;

#[derive(PackedStruct)]
#[packed_struct(bit_numbering="msb0")]
pub struct TestPack {
    #[packed_field(bits="0..=2")]
    tiny_int: Integer<u8, packed_bits::Bits::<3>>,
    #[packed_field(bits="3..=4", ty="enum")]
    mode: SelfTestMode,
    #[packed_field(bits="7")]
    enabled: bool
}

#[derive(PrimitiveEnum_u8, Clone, Copy, Debug, PartialEq)]
pub enum SelfTestMode {
    NormalMode = 0,
    PositiveSignSelfTest = 1,
    NegativeSignSelfTest = 2,
    DebugMode = 3,
}

fn main() -> Result<(), PackingError> {
    let test = TestPack {
        tiny_int: 5.into(),
        mode: SelfTestMode::DebugMode,
        enabled: true
    };

    // pack into a byte array
    let packed: [u8; 1] = test.pack()?;
    assert_eq!([0b10111001], packed);

    // unpack from a byte array
    let unpacked = TestPack::unpack(&packed)?;
    assert_eq!(*unpacked.tiny_int, 5);
    assert_eq!(unpacked.mode, SelfTestMode::DebugMode);
    assert_eq!(unpacked.enabled, true);

    // or unpack from a slice
    let unpacked = TestPack::unpack_from_slice(&packed[..])?;

    Ok(())
}

打包属性

语法

use packed_struct::prelude::*;

#[derive(PackedStruct)]
#[packed_struct(attr1="val", attr2="val")]
pub struct Structure {
    #[packed_field(attr1="val", attr2="val")]
    field: u8
}

每个结构体的属性

属性 注释
size_bytes 1 ... n 打包字节流的长度
bit_numbering msb0lsb0 字段位定位的位编号。如果使用 bits 属性字段,则必须指定。
endian msblsb 默认整数字节序

每个字段的属性

属性 注释
bits 0, 0..1, ... 字段在打包结构体中的位置。支持三种模式:单个位、起始位或位范围。详见下文。
bytes 0, 0..1, ... 与上面相同,乘以 8。
size_bits 1, ... 指定打包结构的尺寸。对于某些类型是必需的。指定一个位范围,如 bits="0..2" 可以替代使用 size_bits 的需求。
size_bytes 1, ... 与上面相同,乘以 8。
element_size_bits 1, ... 对于打包数组,指定数组中单个元素的尺寸。明确声明整个数组的尺寸可以替代使用此属性。
element_size_bytes 1, ... 与上面相同,乘以 8。
ty 枚举 原始枚举的打包助手。
endian msblsb 整数字节序。适用于 u16/i16 和更大的类型。

位和字节定位

用于字段上的 bitsbytes。以下示例为 MSB0 定位。

注释
0 单个位或字节
0.., 0: 字段从零位开始
0..2 排他性范围,位零和一
0:1, 0..=1 包含性范围,位零和一

更多示例

混合字节序的整数

use packed_struct::prelude::*;

#[derive(PackedStruct)]
pub struct EndianExample {
    #[packed_field(endian="lsb")]
    int1: u16,
    #[packed_field(endian="msb")]
    int2: i32
}

fn main() -> Result<(), PackingError> {
    let example = EndianExample {
        int1: 0xBBAA,
        int2: 0x11223344
    };

    let packed = example.pack()?;
    assert_eq!([0xAA, 0xBB, 0x11, 0x22, 0x33, 0x44], packed);
    Ok(())
}

24位LSB整数

use packed_struct::prelude::*;

#[derive(PackedStruct)]
#[packed_struct(endian="lsb")]
pub struct LsbIntExample {
    int1: Integer<u32, packed_bits::Bits::<24>>,
}

fn main() -> Result<(), PackingError> {
    let example = LsbIntExample {
        int1: 0xCCBBAA.into()
    };

    let packed = example.pack()?;
    assert_eq!([0xAA, 0xBB, 0xCC], packed);
    Ok(())
}

嵌套打包类型

use packed_struct::prelude::*;
#[derive(PackedStruct, Debug, PartialEq)]
#[packed_struct(endian="lsb")]
pub struct Duration {
    minutes: u8,
    seconds: u8,
}
#[derive(PackedStruct, Debug, PartialEq)]
pub struct Record {
    #[packed_field(element_size_bytes="2")]
    span: Duration,
    events: u8,
}
fn main() -> Result<(), PackingError> {
    let example = Record {
        span: Duration {
            minutes: 10,
            seconds: 34,
        },
        events: 3,
    };
    let packed = example.pack()?;
    let unpacked = Record::unpack(&packed)?;
    assert_eq!(example, unpacked);
    Ok(())
}

数组中的嵌套打包类型

use packed_struct::prelude::*;

#[derive(PackedStruct, Default, Debug, PartialEq)]
#[packed_struct(bit_numbering="msb0")]
pub struct TinyFlags {
    _reserved: ReservedZero<packed_bits::Bits::<4>>,
    flag1: bool,
    val1: Integer<u8, packed_bits::Bits::<2>>,
    flag2: bool
}

#[derive(PackedStruct, Debug, PartialEq)]
pub struct Settings {
    #[packed_field(element_size_bits="4")]
    values: [TinyFlags; 4]
}

fn main() -> Result<(), PackingError> {
    let example = Settings {
        values: [
            TinyFlags { flag1: true,  val1: 1.into(), flag2: false, .. TinyFlags::default() },
            TinyFlags { flag1: true,  val1: 2.into(), flag2: true,  .. TinyFlags::default() },
            TinyFlags { flag1: false, val1: 3.into(), flag2: false, .. TinyFlags::default() },
            TinyFlags { flag1: true,  val1: 0.into(), flag2: false, .. TinyFlags::default() },
        ]
    };

    let packed = example.pack()?;
    let unpacked = Settings::unpack(&packed)?;

    assert_eq!(example, unpacked);
    Ok(())
}

具有简单区分符的原始枚举

支持的底层整数类型:u8u16u32u64i8i16i32i64

显式或隐式底层类型

use packed_struct::prelude::*;

#[derive(PrimitiveEnum, Clone, Copy, PartialEq, Debug)]
pub enum ImplicitType {
    VariantMin = 0,
    VariantMax = 255
}

#[derive(PrimitiveEnum_i16, Clone, Copy)]
pub enum ExplicitType {
    VariantMin = -32768,
    VariantMax = 32767
}

fn main() {
    use packed_struct::PrimitiveEnum;

    let t = ImplicitType::VariantMin;
    let tn: u8 = t.to_primitive();
    assert_eq!(0, tn);

    let t = ImplicitType::from_primitive(255).unwrap();
    assert_eq!(ImplicitType::VariantMax, t);
}

支持捕获所有未知值的原始枚举打包

use packed_struct::prelude::*;

#[derive(PrimitiveEnum_u8, Debug, Clone, Copy)]
pub enum Field {
    A = 1,
    B = 2,
    C = 3
}

#[derive(PackedStruct, Debug, PartialEq)]
#[packed_struct(bit_numbering="msb0")]
pub struct Register {
    #[packed_field(bits="0..4", ty="enum")]
    field: EnumCatchAll<Field>
}

许可:MIT OR Apache-2.0

依赖项

~2.5MB
~60K SLoC