Crate packed_struct[][src]

Expand description

Bit-level packing and unpacking for Rust

Crates.io Documentation master

Introduction

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.

Features

  • 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

Crate-level feature flags

  • 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.

Sample usage

Cargo.toml

[dependencies]
packed_struct = "0.10"

Importing the library with the the most common traits and the derive macros

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

Example of a single-byte structure, with a 3 bit integer, primitive enum and a bool field.

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(())
}

Packing attributes

Syntax

use packed_struct::prelude::*;

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

Per-structure attributes

AttributeValuesComment
size_bytes1 … nSize of the packed byte stream
bit_numberingmsb0 or lsb0Bit numbering for bit positioning of fields. Required if the bits attribute field is used.
endianmsb or lsbDefault integer endianness

Per-field attributes

AttributeValuesComment
bits0, 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.
bytes0, 0..1, …Same as above, multiplied by 8.
size_bits1, …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_bytes1, …Same as above, multiplied by 8.
element_size_bits1, …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_bytes1, …Same as above, multiplied by 8.
tyenumPacking helper for primitive enums.
endianmsb or lsbInteger endianness. Applies to u16/i16 and larger types.

Bit and byte positioning

Used for either bits or bytes on fields. The examples are for MSB0 positioning.

ValueComment
0A single bit or byte
0.., 0:The field starts at bit zero
0..2Exclusive range, bits zero and one
0:1, 0..=1Inclusive range, bits zero and one

More examples

Mixed endian integers

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 bit LSB integers

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(())
}

Nested packed types

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(())
}

Nested packed types within arrays

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(())
}

Primitive enums with simple discriminants

Supported backing integer types: u8, u16, u32, u64, i8, i16, i32, i64.

Explicit or implicit backing type:

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

Primitive enum packing with support for catch-all unknown values

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>
}
 

Modules

Helper structures for runtime packing visualization.

The derivation macros for packing and enums.

Re-exports the most useful traits and types. Meant to be glob imported.

Implementations and wrappers for various packing types.

Tuples of types that can be packed together. Only byte-sized structures can be chained together.

Enums

A wrapper for primitive enums that supports catching and retaining any values that don’t have defined discriminants.

Packing errors that might occur during packing or unpacking

Traits

A structure that can be packed and unpacked from a byte array.

Infos about a particular type that can be packaged.

A structure that can be packed and unpacked from a slice of bytes.

An enum type that can be packed or unpacked from a simple primitive integer.

Dynamic display formatters.

Static display formatters.

Type Definitions