Packing and unpacking bit-level structures is usually a programming tasks that needlessly reinvents the wheel. This library provides a meta-programming approach, using attributes to define fields and how they should be packed. The resulting trait implementations provide safe packing, unpacking and runtime debugging formatters with per-field documentation generated for each structure.
- Plain Rust structures, decorated with attributes
- MSB or LSB integers of user-defined bit widths
- Primitive enum code generation helper
- MSB0 or LSB0 bit positioning
- Documents the field's packing table
- Runtime packing visualization
- Nested packed types
- Arrays of packed structures as fields
- Reserved fields, their bits are always 0 or 1
[dependencies]
packed_struct = "0.3"
packed_struct_codegen = "0.3"
extern crate packed_struct;
#[macro_use]
extern crate packed_struct_codegen;
extern crate packed_struct;
#[macro_use] extern crate packed_struct_codegen;
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::Bits3>,
#[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() {
let test = TestPack {
tiny_int: 5.into(),
mode: SelfTestMode::DebugMode,
enabled: true
};
let packed = test.pack();
assert_eq!([0b10111001], packed);
let unpacked = TestPack::unpack(&packed).unwrap();
assert_eq!(*unpacked.tiny_int, 5);
assert_eq!(unpacked.mode, SelfTestMode::DebugMode);
assert_eq!(unpacked.enabled, true);
}
extern crate packed_struct;
#[macro_use] extern crate packed_struct_codegen;
#[derive(PackedStruct)]
#[packed_struct(attr1="val", attr2="val")]
pub struct Structure {
#[packed_field(attr1="val", attr2="val")]
field: u8
}
Attribute | Values | Comment |
---|---|---|
size_bytes |
1 ... n |
Size of the packed byte stream |
bit_numbering |
msb0 or lsb0 |
Bit numbering for bit positioning of fields. Required if the bits attribute field is used. |
endian |
msb or lsb |
Default integer endianness |
Attribute | Values | Comment |
---|---|---|
bits |
0 , 0..1 , ... |
Position of the field in the packed structure. Three modes are supported: a single bit, the starting bit, or a range of bits. See details below. |
bytes |
0 , 0..1 , ... |
Same as above, multiplied by 8. |
size_bits |
1 , ... |
Specifies the size of the packed structure. Mandatory for certain types. Specifying a range of bits like bits="0..2" can substite the required usage of size_bits . |
size_bytes |
1 , ... |
Same as above, multiplied by 8. |
element_size_bits |
1 , ... |
For packed arrays, specifies the size of a single element of the array. Explicitly stating the size of the entire array can substite the usage of this attribute. |
element_size_bytes |
1 , ... |
Same as above, multiplied by 8. |
ty |
enum |
Packing helper for primitive enums. |
endian |
msb or lsb |
Integer endianness. Applies to u16/i16 and larger types. |
Used for either bits
or bytes
on fields. The examples are for MSB0 positioning.
Value | Comment |
---|---|
0 |
A single bit or byte |
0.. , 0: |
The field starts at bit zero |
0..2 |
Exclusive range, bits zero and one |
0:1 , 0..=1 |
Inclusive range, bits zero and one |
extern crate packed_struct;
#[macro_use] extern crate packed_struct_codegen;
use packed_struct::prelude::*;
#[derive(PackedStruct)]
pub struct EndianExample {
#[packed_field(endian="lsb")]
int1: u16,
#[packed_field(endian="msb")]
int2: i32
}
fn main() {
let example = EndianExample {
int1: 0xBBAA,
int2: 0x11223344
};
let packed = example.pack();
assert_eq!([0xAA, 0xBB, 0x11, 0x22, 0x33, 0x44], packed);
}
extern crate packed_struct;
#[macro_use] extern crate packed_struct_codegen;
use packed_struct::prelude::*;
#[derive(PackedStruct)]
#[packed_struct(endian="lsb")]
pub struct LsbIntExample {
int1: Integer<u32, packed_bits::Bits24>,
}
fn main() {
let example = LsbIntExample {
int1: 0xCCBBAA.into()
};
let packed = example.pack();
assert_eq!([0xAA, 0xBB, 0xCC], packed);
}
extern crate packed_struct;
#[macro_use] extern crate packed_struct_codegen;
use packed_struct::prelude::*;
#[derive(PackedStruct, Default, Debug, PartialEq)]
#[packed_struct(bit_numbering="msb0")]
pub struct TinyFlags {
_reserved: ReservedZero<packed_bits::Bits4>,
flag1: bool,
val1: Integer<u8, packed_bits::Bits2>,
flag2: bool
}
#[derive(PackedStruct, Debug, PartialEq)]
pub struct Settings {
#[packed_field(element_size_bits="4")]
values: [TinyFlags; 4]
}
fn main() {
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).unwrap();
assert_eq!(example, unpacked);
}
Supported backing integer types: u8
, u16
, u32
, u64
, i8
, i16
, i32
, i64
.
Explicit or implicit backing type:
extern crate packed_struct;
#[macro_use] extern crate packed_struct_codegen;
#[derive(PrimitiveEnum, Clone, Copy)]
pub enum ImplicitType {
VariantMin = 0,
VariantMax = 255
}
#[derive(PrimitiveEnum_i16, Clone, Copy)]
pub enum ExplicitType {
VariantMin = -32768,
VariantMax = 32767
}
License: MIT OR Apache-2.0