14个版本

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日

过程宏中排名197

Download history 15383/week @ 2024-03-14 15543/week @ 2024-03-21 16300/week @ 2024-03-28 18271/week @ 2024-04-04 15756/week @ 2024-04-11 16112/week @ 2024-04-18 17833/week @ 2024-04-25 17165/week @ 2024-05-02 18142/week @ 2024-05-09 15366/week @ 2024-05-16 15528/week @ 2024-05-23 15028/week @ 2024-05-30 14368/week @ 2024-06-06 15353/week @ 2024-06-13 15426/week @ 2024-06-20 11459/week @ 2024-06-27

每月下载量59,514
44个crate中使用(直接使用10个)

MIT/Apache

61KB
1.5K SLoC

Rust的位级打包和解包

Crates.io Documentation master

介绍

位级结构的打包和解包通常是一个需要重新发明轮子的编程任务。这个库提供了一种元编程方法,使用属性来定义字段以及它们应该如何打包。生成的特质实现提供了安全的打包、解包和运行时调试格式化器,并为每个结构生成字段级别的文档。

特性

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

crate级别的功能标志

  • std:使用Rust标准库。默认。
  • alloc:在no_std + alloc场景中使用alloc crate。需要nightly Rust。
  • use_serde:为内置辅助类型添加序列化支持。
  • byte_types_64byte_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属性字段,则为必需。
端序 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 enum 原始枚举的打包助手
端序 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(())
}

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

支持的底层整数类型: u8, u16, u32, u64, i8, i16, i32, i64.

显式或隐式底层类型

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

依赖关系

~1.5MB
~36K SLoC