Crate bitpiece

Source
Expand description

A Rust crate for effortlessly defining and manipulating bitfields with procedural macros.

bitpiece takes the complexity out of bit-level data manipulation. It provides a powerful #[bitpiece] macro that lets you define structs and enums as compact, typed bitfields, while automatically generating a safe, high-level API to interact with them. It’s perfect for working with network protocols, hardware interfaces, or any scenario where data compactness is key.

§Features

  • Declarative & Simple: Define complex bitfield layouts using simple Rust structs and enums.
  • Type-Safe API: The macro generates getters and setters for each field, so you work with bool, u8, enum types, etc., not raw bit shifts and masks.
  • Flexible: Supports defining types which have exotic bit lengths, for example a 6-bit struct made of two 3-bit fields.
  • Nestable: Compose complex bitfields by nesting bitpiece types within each other.
  • Arbitrary-Width Integers: Use the built-in B1-B64 types (e.g., B3, B7, B12) for unsigned integers with non-standard bit lengths, or the SB1-SB64 types for signed integers.
  • Enums: Supports using enums as bitfields, with automatic validation of input for non-exhaustive enums.
  • Compile-Time Validation: Optionally specify an expected bit length on your structs (e.g., #[bitpiece(32)]) to get a compile-time error if it doesn’t match the sum of its fields.
  • Safe & Unsafe APIs: Provides both panicking (from_bits) and fallible (try_from_bits) APIs for creating bitpieces from raw integer values.
  • #![no_std] compatible.

§Getting Started

First, add bitpiece to your Cargo.toml:

[dependencies]
bitpiece = "0.1.0" # Use the latest version

Now, let’s define a bitfield for a hypothetical network packet header.

use bitpiece::*;

// Define a 2-bit enum for the packet's priority.
// The macro automatically infers it needs 2 bits.
#[bitpiece]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Priority {
    Low = 0,
    Medium = 1,
    High = 2,
    Critical = 3,
}

// Define the packet header structure.
// The macro calculates the total size (1 + 2 + 5 = 8 bits).
#[bitpiece(8)] // The `(8)` is optional but validates the size at compile time.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct PacketHeader {
    is_fragment: bool,
    priority: Priority,
    payload_len: B5, // A 5-bit integer type
}

fn main() {
    // Create a new header from raw bits (e.g., received from a network).
    // Bits: 0b101_10_1 => is_fragment=1, priority=2 (High), payload_len=5
    let mut header = PacketHeader::from_bits(0b101101);

    // Use the generated getter methods to safely access fields.
    assert_eq!(header.is_fragment(), true);
    assert_eq!(header.priority(), Priority::High);
    assert_eq!(header.payload_len().get(), 5); // Use .get() for B-types

    // Use the generated setter methods to modify the header.
    header.set_priority(Priority::Critical);
    header.set_payload_len(B5::new(31)); // Set to max value (2^5 - 1)

    assert_eq!(header.priority(), Priority::Critical);
    assert_eq!(header.payload_len().get(), 31);

    // The underlying storage is automatically updated.
    // Bits: 0b11111_11_1
    assert_eq!(header.to_bits(), 0b11111111);

    // You can also construct a bitpiece from its fields directly.
    let from_fields = PacketHeader::from_fields(PacketHeaderFields {
        is_fragment: false,
        priority: Priority::Low,
        payload_len: B5::new(10),
    });

    assert_eq!(from_fields.to_bits(), 0b1010000);
}

§More Examples

§Nesting

You can easily build complex structures by nesting bitpiece types.

use bitpiece::*;

#[bitpiece(4)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct MacAddressPart {
    a: B1,
    b: B3,
}

#[bitpiece(16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct ProtocolInfo {
    part1: MacAddressPart,
    part2: MacAddressPart,
    flags: u8, // Standard integer types are also supported
}

fn main() {
    let mut info = ProtocolInfo::zeroes(); // zeroes() is a handy constructor

    info.set_part1(MacAddressPart::from_bits(0b1010));

    assert_eq!(info.part1().b().get(), 0b101);
    assert_eq!(info.to_bits(), 0b00000000_1010);

    // Set a field in a nested bitpiece
    info.part1_mut().set_b(B3::new(0b110));

    assert_eq!(info.part1().b().get(), 0b110);
    assert_eq!(info.to_bits(), 0b00000000_1100);
}

§Non-Exhaustive Enums

By default, an enum’s bit-length is determined by its largest variant. If you try to create an enum from an invalid integer value, it will panic.

Sometimes, however, an enum definition isn’t complete, but you still want to handle known variants. For this, bitpiece generates a try_from_bits method.

use bitpiece::*;

#[bitpiece] // Bit length is inferred as 7 bits (from 120)
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum OpCode {
    Read = 0,
    Write = 1,
    Sync = 80,
    Halt = 120,
}

fn main() {
    // try_from_bits returns an Option, which is great for safe parsing.
    let known_code = OpCode::try_from_bits(80);
    assert_eq!(known_code, Some(OpCode::Sync));

    let unknown_code = OpCode::try_from_bits(55);
    assert_eq!(unknown_code, None);

    // In contrast, from_bits will panic on an unknown variant.
    // let panicked = OpCode::from_bits(55); // This would panic!
}

§Explicit Bit-Length on Enums

You can give an enum a larger bit-width than it needs. This is useful when a protocol reserves a certain number of bits for an enum, even if not all values are currently used.

use bitpiece::*;

// This enum's highest value is 2, which only needs 2 bits.
// But we can force it to occupy a full byte.
#[bitpiece(8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum MessageType {
    Query = 0,
    Ack = 1,
    Nak = 2,
}

fn main() {
    // The underlying storage type will be u8.
    assert_eq!(MessageType::from_bits(1).to_bits(), 1u8);

    assert_eq!(MessageType::try_from_bits(200), None); // Fails, not a valid variant
}

§Generated API

For a struct like MyPiece { field_a: bool, field_b: B3 }, the macro generates:

  • MyPiece::from_bits(u8) -> Self: Creates an instance from raw bits. Panics if any field gets an invalid value (e.g., for a non-exhaustive enum).
  • MyPiece::try_from_bits(u8) -> Option<Self>: Safely creates an instance, returning None if any field would be invalid.
  • my_piece.to_bits() -> u8: Returns the raw bits as the smallest possible integer storage type.
  • MyPiece::from_fields(MyPieceFields) -> Self: Creates an instance from a struct containing all the fields.
  • my_piece.to_fields() -> MyPieceFields: Deconstructs the instance into a struct of its fields.
  • MyPiece::zeroes() -> Self: A constructor where all bits are 0.
  • my_piece.field_a() -> bool: Getter for field_a.
  • my_piece.set_field_a(bool): Setter for field_a.
  • my_piece.field_b() -> B3: Getter for field_b.
  • my_piece.set_field_b(B3): Setter for field_b.
  • my_piece.field_a_mut() -> BitPieceMut: Advanced usage for mutable access, especially for nested pieces.
  • my_piece.field_b_mut() -> BitPieceMut: Same as above, but for field_b

Structs§

B1
a type used to represent a field with a specific amount of bits.
B2
a type used to represent a field with a specific amount of bits.
B3
a type used to represent a field with a specific amount of bits.
B4
a type used to represent a field with a specific amount of bits.
B5
a type used to represent a field with a specific amount of bits.
B6
a type used to represent a field with a specific amount of bits.
B7
a type used to represent a field with a specific amount of bits.
B8
a type used to represent a field with a specific amount of bits.
B9
a type used to represent a field with a specific amount of bits.
B10
a type used to represent a field with a specific amount of bits.
B11
a type used to represent a field with a specific amount of bits.
B12
a type used to represent a field with a specific amount of bits.
B13
a type used to represent a field with a specific amount of bits.
B14
a type used to represent a field with a specific amount of bits.
B15
a type used to represent a field with a specific amount of bits.
B16
a type used to represent a field with a specific amount of bits.
B17
a type used to represent a field with a specific amount of bits.
B18
a type used to represent a field with a specific amount of bits.
B19
a type used to represent a field with a specific amount of bits.
B20
a type used to represent a field with a specific amount of bits.
B21
a type used to represent a field with a specific amount of bits.
B22
a type used to represent a field with a specific amount of bits.
B23
a type used to represent a field with a specific amount of bits.
B24
a type used to represent a field with a specific amount of bits.
B25
a type used to represent a field with a specific amount of bits.
B26
a type used to represent a field with a specific amount of bits.
B27
a type used to represent a field with a specific amount of bits.
B28
a type used to represent a field with a specific amount of bits.
B29
a type used to represent a field with a specific amount of bits.
B30
a type used to represent a field with a specific amount of bits.
B31
a type used to represent a field with a specific amount of bits.
B32
a type used to represent a field with a specific amount of bits.
B33
a type used to represent a field with a specific amount of bits.
B34
a type used to represent a field with a specific amount of bits.
B35
a type used to represent a field with a specific amount of bits.
B36
a type used to represent a field with a specific amount of bits.
B37
a type used to represent a field with a specific amount of bits.
B38
a type used to represent a field with a specific amount of bits.
B39
a type used to represent a field with a specific amount of bits.
B40
a type used to represent a field with a specific amount of bits.
B41
a type used to represent a field with a specific amount of bits.
B42
a type used to represent a field with a specific amount of bits.
B43
a type used to represent a field with a specific amount of bits.
B44
a type used to represent a field with a specific amount of bits.
B45
a type used to represent a field with a specific amount of bits.
B46
a type used to represent a field with a specific amount of bits.
B47
a type used to represent a field with a specific amount of bits.
B48
a type used to represent a field with a specific amount of bits.
B49
a type used to represent a field with a specific amount of bits.
B50
a type used to represent a field with a specific amount of bits.
B51
a type used to represent a field with a specific amount of bits.
B52
a type used to represent a field with a specific amount of bits.
B53
a type used to represent a field with a specific amount of bits.
B54
a type used to represent a field with a specific amount of bits.
B55
a type used to represent a field with a specific amount of bits.
B56
a type used to represent a field with a specific amount of bits.
B57
a type used to represent a field with a specific amount of bits.
B58
a type used to represent a field with a specific amount of bits.
B59
a type used to represent a field with a specific amount of bits.
B60
a type used to represent a field with a specific amount of bits.
B61
a type used to represent a field with a specific amount of bits.
B62
a type used to represent a field with a specific amount of bits.
B63
a type used to represent a field with a specific amount of bits.
B64
a type used to represent a field with a specific amount of bits.
BitLength
an empty struct used to represent a specific bit length. this is then combined with some traits (ExactAssociatedStorage, AssociatedStorage) to perform operations on the specified bit length.
BitsMut
a convenience type for interacting with the bits of an underlying storage type, starting at a specific bit index. this is useful for implementing mutable references.
GenericBitPieceMut
a generic implementation of the BitPieceMut trait used for convenience.
SB1
a type used to represent a field with a specific amount of bits.
SB2
a type used to represent a field with a specific amount of bits.
SB3
a type used to represent a field with a specific amount of bits.
SB4
a type used to represent a field with a specific amount of bits.
SB5
a type used to represent a field with a specific amount of bits.
SB6
a type used to represent a field with a specific amount of bits.
SB7
a type used to represent a field with a specific amount of bits.
SB8
a type used to represent a field with a specific amount of bits.
SB9
a type used to represent a field with a specific amount of bits.
SB10
a type used to represent a field with a specific amount of bits.
SB11
a type used to represent a field with a specific amount of bits.
SB12
a type used to represent a field with a specific amount of bits.
SB13
a type used to represent a field with a specific amount of bits.
SB14
a type used to represent a field with a specific amount of bits.
SB15
a type used to represent a field with a specific amount of bits.
SB16
a type used to represent a field with a specific amount of bits.
SB17
a type used to represent a field with a specific amount of bits.
SB18
a type used to represent a field with a specific amount of bits.
SB19
a type used to represent a field with a specific amount of bits.
SB20
a type used to represent a field with a specific amount of bits.
SB21
a type used to represent a field with a specific amount of bits.
SB22
a type used to represent a field with a specific amount of bits.
SB23
a type used to represent a field with a specific amount of bits.
SB24
a type used to represent a field with a specific amount of bits.
SB25
a type used to represent a field with a specific amount of bits.
SB26
a type used to represent a field with a specific amount of bits.
SB27
a type used to represent a field with a specific amount of bits.
SB28
a type used to represent a field with a specific amount of bits.
SB29
a type used to represent a field with a specific amount of bits.
SB30
a type used to represent a field with a specific amount of bits.
SB31
a type used to represent a field with a specific amount of bits.
SB32
a type used to represent a field with a specific amount of bits.
SB33
a type used to represent a field with a specific amount of bits.
SB34
a type used to represent a field with a specific amount of bits.
SB35
a type used to represent a field with a specific amount of bits.
SB36
a type used to represent a field with a specific amount of bits.
SB37
a type used to represent a field with a specific amount of bits.
SB38
a type used to represent a field with a specific amount of bits.
SB39
a type used to represent a field with a specific amount of bits.
SB40
a type used to represent a field with a specific amount of bits.
SB41
a type used to represent a field with a specific amount of bits.
SB42
a type used to represent a field with a specific amount of bits.
SB43
a type used to represent a field with a specific amount of bits.
SB44
a type used to represent a field with a specific amount of bits.
SB45
a type used to represent a field with a specific amount of bits.
SB46
a type used to represent a field with a specific amount of bits.
SB47
a type used to represent a field with a specific amount of bits.
SB48
a type used to represent a field with a specific amount of bits.
SB49
a type used to represent a field with a specific amount of bits.
SB50
a type used to represent a field with a specific amount of bits.
SB51
a type used to represent a field with a specific amount of bits.
SB52
a type used to represent a field with a specific amount of bits.
SB53
a type used to represent a field with a specific amount of bits.
SB54
a type used to represent a field with a specific amount of bits.
SB55
a type used to represent a field with a specific amount of bits.
SB56
a type used to represent a field with a specific amount of bits.
SB57
a type used to represent a field with a specific amount of bits.
SB58
a type used to represent a field with a specific amount of bits.
SB59
a type used to represent a field with a specific amount of bits.
SB60
a type used to represent a field with a specific amount of bits.
SB61
a type used to represent a field with a specific amount of bits.
SB62
a type used to represent a field with a specific amount of bits.
SB63
a type used to represent a field with a specific amount of bits.
SB64
a type used to represent a field with a specific amount of bits.

Traits§

AssociatedStorage
a trait implemented for all BitLength types that are small enough and provides the minimal storage type required for storing that amount of bits. for example for bit lengths 0..8 this will be u8.
BitPiece
a bitpiece. this is the core trait of this crate and represents a type with a specified bit length which can be used in a standalone way or inside another bitpiece.
BitPieceMut
a mutable reference to a bitpiece inside another bitpiece.
BitStorage
a type which can be used as the internal storage of a bitpiece.
ExactAssociatedStorage
a trait implemented for BitLength types that have an exact associated storage type, for example u8 or u16.

Functions§

extract_bits
extracts some bits from a value
extract_bits_noshift
extracts some bits (mask only, no shift) from a value
modify_bits
returns a new value with the specified bit range modified to the new value

Attribute Macros§

bitpiece
an attribute for defining bitfields.