Skip to main content

Crate packed_bits

Crate packed_bits 

Source
Expand description

§packed_bits

Memory-efficient bit packing library. Define a packed_bits struct that stores multiple fields in a single integer, using only as many bits as each field needs.

§Usage

use packed_bits::packed_bits;

// LC-3 ADD instruction (16-bit). Fields map directly to the ISA layout:
//   ADD DR, SR1, SR2   -> 0001 DR SR1 0 000 SR2 00
//   ADD DR, SR1, imm5  -> 0001 DR SR1 1 imm5
packed_bits!(
    Lc3Add: u16 {
        value: 5,   // SR2 (register mode) or imm5 (immediate mode)
        imm: 1,     // 0 = register mode, 1 = immediate mode
        sr1: 3,
        dr: 3,
        opcode: 4,  // 0b0001 for ADD
    }
);

// ADD R2, R1, R3 (register mode) -> 0x144C
let add_reg = Lc3Add::from(0x144C);
assert_eq!((0b01100, 0, 1, 2, 0b0001),
    (add_reg.value(), add_reg.imm(), add_reg.sr1(), add_reg.dr(), add_reg.opcode()));

// ADD R0, R1, #5 (immediate mode) -> 0x1065
let add_imm = Lc3Add::new(0b00101, 1, 1, 0, 0b0001);
assert_eq!(0x1065, add_imm.get_raw());

// read a single field
assert_eq!(0b00101, add_imm.value());

// update a field (chainable, returns &mut Self)
let mut add = add_imm;
add.set_dr(1).set_sr1(2);
assert_eq!(1, add.dr());

// raw bit access
let mut flags = add;
flags.set_bit(5, true).toggle_bit(9);
assert!(flags.get_bit(5));
flags.clear_bit(5);
assert!(!flags.get_bit(5));

// const-compatible creation
const ADD_R0_R1_5: Lc3Add = Lc3Add::new(0b00101, 1, 1, 0, 0b0001);
assert_eq!(0x1065, ADD_R0_R1_5.get_raw());

§Typed fields

Fields can also have a type, backed by the PackedField trait. Fieldless enums get an implementation via the derive feature:

use packed_bits::{packed_bits, PackedField};

#[derive(PackedField, Debug, Clone, Copy, PartialEq, Eq)]
enum Color {
    Red = 0,
    Green = 1,
    Blue = 2,
}

packed_bits!(
    Pixel: u16 {
        color: Color = 2,
        alpha: u8 = 8,
    }
);

pub fn run() {
    let pixel = Pixel::new(Color::Blue, 200);
    assert_eq!(Some(Color::Blue), pixel.color());
    assert_eq!(2, pixel.color_raw());
    assert_eq!(Some(200), pixel.alpha());

    // construction from raw bits fails if any field has no valid value
    assert!(Pixel::try_from(3).is_err());
}

§no_std

packed_bits is no_std by default — no features are required. Opt into std (for an Error impl on FieldError) or derive (for the PackedField derive macro) as needed:

packed_bits = { version = "0.3", features = ["derive"] }

§Example: memory savings

Eight boolean flags fit in a single byte instead of eight:

Without packing: fin + syn + ack + … (1 byte each) = 8 bytes With packing: everything fits in just 1 byte!

§Macro parameters

  • name: The name of the generated struct
  • storage: The underlying data type/size (u8, u16, u32, u64)
  • field: Field name (will also be used as a getter method)
  • bits: Number of bits allocated for this field

§Important notes

  • Make sure your bit counts add up to fit in your storage type
  • u16 can hold 16 bits total, u32 can hold 32 bits, etc.
  • Each field gets a method with the same name to read its value, plus set_<field> to update it
  • Values are stored from lowest bits to highest bits in declaration order
  • Passing an out-of-range value to new/setters panics; use set_bit/get_bit for raw bit access

Re-exports§

pub use static_assertions;

Macros§

packed_bits
Defines a packed struct that stores multiple fields in a single integer.
paste

Structs§

FieldError
Error returned when a value exceeds the capacity of a packed field.

Traits§

PackedField
A type that can be packed into a bit field.