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.
std
: use the Rust standard library. Default.alloc
: use the alloc
crate for no_std
+ alloc
scenarios. Requires nightly Rust.use_serde
: add serialization support to the built-in helper types.byte_types_64
, byte_types_256
: enlarge the size of the generated array, byte and bit width types.toml
[dependencies]
packed_struct = "0.10"
```rust // This is only needed for pre Rust 2018
// Prelude import with the common imports use packed_struct::prelude::*; ```
```rust use packed_struct::prelude::*;
pub struct TestPack {
#[packedfield(bits="0..=2")]
tinyint: Integer
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(())
} ```
```rust use packed_struct::prelude::*;
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
```rust use packed_struct::prelude::*;
pub struct EndianExample { #[packedfield(endian="lsb")] int1: u16, #[packedfield(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(())
} ```
```rust use packed_struct::prelude::*;
pub struct LsbIntExample {
int1: Integer
fn main() -> Result<(), PackingError> { let example = LsbIntExample { int1: 0xCCBBAA.into() };
let packed = example.pack()?;
assert_eq!([0xAA, 0xBB, 0xCC], packed);
Ok(())
} ```
```rust use packed_struct::prelude::*;
pub struct Duration { minutes: u8, seconds: u8, }
pub struct Record { #[packedfield(elementsizebytes="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)?; asserteq!(example, unpacked); Ok(()) } ```
```rust use packed_struct::prelude::*;
pub struct TinyFlags {
_reserved: ReservedZero
pub struct Settings { #[packedfield(elementsize_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(())
} ```
Supported backing integer types: u8
, u16
, u32
, u64
, i8
, i16
, i32
, i64
.
Explicit or implicit backing type:
```rust use packed_struct::prelude::*;
pub enum ImplicitType { VariantMin = 0, VariantMax = 255 }
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);
} ```
```rust use packed_struct::prelude::*;
pub enum Field { A = 1, B = 2, C = 3 }
pub struct Register {
#[packed_field(bits="0..4", ty="enum")]
field: EnumCatchAll
``` License: MIT OR Apache-2.0