#!=

Bit-level packing and unpacking

Packing and unpacking bit-level structures is usually a programming tasks that needlessly reinvents the wheel. This library provides a meta-programming aproach, using attributes to document 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.
Rust crate packed_struct
Crates.io Documentation Travis CI Star

Features

  • Plain Rust structures, decorated with attributes
  • MSB or LSB integers of user-defined bit widths
  • Primitive enum code generation helper
  • MSB or LSB bit positioning
  • Documents the field’s packing table
  • Runtime packing visualization
  • Nested packed types
  • Arrays
  • Reserved fields

Sample packed structure

/// Control register, address 0xA0.
#[derive(PackedStruct, Debug, Copy, Clone, PartialEq)]
#[packed_struct(bit_numbering="msb0")]
pub struct ControlRegister {
    /// Sensor's power mode
    #[packed_field(bits="0:1", ty="enum")]
    pub power_mode: PowerMode,
    /// Voltage on the input. [mV]
    #[packed_field(bits="3:7")]
    pub voltage_milli_volts: Integer<u8, packed_bits::Bits5>,
    /// Is the standby LED enabled?
    #[packed_field(bits="8")]
    pub standby_led_enabled: bool,
    /// Which of the four gain stages are active
    #[packed_field(bits="9:12")]
    pub gain_stages: [bool; 4],
    /// Reserved bits, always 1
    #[packed_field(bits="13:15")]
    pub _reserved: ReservedOnes<packed_bits::Bits3>,
    /// Sensor's reading
    #[packed_field(bits="16:31", endian="lsb")]
    pub sensor_value: i16
}

#[derive(PrimitiveEnum, Debug, Copy, Clone, PartialEq)]
pub enum PowerMode {
    /// The sensor is turned off
    Off = 0,
    /// The sensor can be triggered by an external source
    Standby = 1,
    /// Digital logic is on and waiting
    LowPower = 2,
    /// The sensor is enabled and turned on
    On = 3
}

Usage

let reg = ControlRegister {
    power_mode: PowerMode::LowPower,
    voltage_milli_volts: 11.into(),
    standby_led_enabled: true,
    gain_stages: [true, true, false, false],
    _reserved: Default::default(),
    sensor_value: -1503
};

let packed: [u8; 4] = reg.pack();
let unpacked = ControlRegister::unpack(&[0x8B, 0xE7, 0x21, 0xFA]).unwrap();

Packing table in the documentation

Runtime display formatter

ControlRegister (4 bytes)

Decimal
[139, 231, 33, 250]

Hex
[0x8B, 0xE7, 0x21, 0xFA]

Binary
[0b10001011, 0b11100111, 0b00100001, 0b11111010]

          power_mode | bits   0:1   | 0b10               | "LowPower"
 voltage_milli_volts | bits   3:7   | 0b01011            | "11"
 standby_led_enabled | bits   8:8   | 0b1                | "true"
      gain_stages[0] | bits   9:9   | 0b1                | "true"
      gain_stages[1] | bits  10:10  | 0b1                | "true"
      gain_stages[2] | bits  11:11  | 0b0                | "false"
      gain_stages[3] | bits  12:12  | 0b0                | "false"
           _reserved | bits  13:15  | 0b111              | "Reserved - always 1"
        sensor_value | bits  16:31  | 0b0010000111111010 | "-1503"