Crate fray

Crate fray 

Source
Expand description

A type-safe and ergonomic library for working with bitfields.

A bitfield is defined using the #[bitfield] attribute macro and must specify either repr(primitive), where primitive is an unsigned integer type, or container(T), where T implements the BitContainer trait.

The generated bitfield struct provides access to its fields through the methods of the BitField trait.

Each field type must implement FieldType, which defines its default size and the type used for storage. The default size can be overridden with the #[bits(N)] attribute on individual fields.

The macro also allows customizing the generated struct: any trait can be forwarded via derives, while only certain common traits can be requested via impls.

See the #[bitfield] documentation for all available macro arguments and options.

§Example: basic usage

use fray::{bitfield, BitField};

#[bitfield(repr(u8), impls(debug), derives(Clone, Copy))]
pub struct DeviceFlags {
    powered_on: bool,
    error: bool,
    tx_enabled: bool,
    rx_enabled: bool,
    #[bits(3)]
    priority: u8,
    #[bits(1)]
    reserved: (),
}

fn main() {
    let mut flags = DeviceFlags::new();

    flags
        .with::<powered_on>(true)
        .with::<tx_enabled>(true)
        .with::<priority>(5);

    assert!(flags.get::<powered_on>());
    assert!(flags.get::<tx_enabled>());
    assert_eq!(flags.get::<priority>(), 5);

    assert!(!flags.get::<error>());
    flags.set::<error>(true);
    assert!(flags.get::<error>());

    let debug_fmt = format!("{:?}", flags);
    let expected = "DeviceFlags { powered_on: true, error: true, tx_enabled: true, rx_enabled: false, priority: 5, reserved: () }";
    assert_eq!(debug_fmt, expected);

    let flags_copy = flags;

    // DeviceFlags use LSB0 ordering, so the literal is written
    // with the least-significant bit on the right.
    assert_eq!(flags.into_inner(), 0b0_101_0_1_1_1);
    //                               | |   | | | |
    //                               | |   | | | powered_on
    //                               | |   | | error
    //                               | |   | tx_enabled
    //                               | |   rx_enabled
    //                               | priority
    //                               reserved
}

§Example: custom field type

use fray::{bitfield, BitField, FieldType};

#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PriorityLevel {
    Low = 0,
    Medium = 1,
    High = 2,
    Critical = 3,
}

// Requiered for try_get
impl TryFrom<u8> for PriorityLevel {
    type Error = u8;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        Ok(match value {
            0 => Self::Low,
            1 => Self::Medium,
            2 => Self::High,
            3 => Self::Critical,
            value => return Err(value)
        })
    }
}

// Requiered for set/with
impl From<PriorityLevel> for u8 {
    fn from(value: PriorityLevel) -> Self {
        value as u8
    }
}

// Requiered for being accepted as field type in struct declaration
impl FieldType for PriorityLevel {
    const SIZE: usize = 3;
    type BitsType = u8;
}

#[bitfield(repr(u8))]
pub struct DeviceFlags {
    powered_on: bool,
    error: bool,
    tx_enabled: bool,
    rx_enabled: bool,
    priority: PriorityLevel,
    #[bits(1)]
    reserved: (),
}

fn main() {
    let mut flags = DeviceFlags::new();

    flags.with::<powered_on>(true)
        .with::<priority>(PriorityLevel::High)
        .with::<error>(true);

    assert!(flags.get::<powered_on>());
    assert!(flags.get::<error>());
    assert_eq!(flags.try_get::<priority>(), Ok(PriorityLevel::High));
}

§Example: nested

use fray::{BitFieldImpl, BitField, FieldType, bitfield};

// https://datatracker.ietf.org/doc/html/rfc9293#name-header-format
#[bitfield(repr(u16), bitorder(msb0))]
pub struct TcpHeader {
    #[bits(4)]
    DOffset: u8,
    #[bits(4)]
    Rsrvd: (),
    flags: ControlBits,
}

#[bitfield(repr(u8), bitorder(msb0))]
pub struct ControlBits {
    CWR: bool,
    ECE: bool,
    URG: bool,
    ACK: bool,
    PSH: bool,
    RST: bool,
    SYN: bool,
    FIN: bool,
}

impl FieldType for ControlBits {
    const SIZE: usize = 8;

    type BitsType = u8;
}

impl From<ControlBits> for u8 {
    fn from(value: ControlBits) -> Self {
        value.into_inner()
    }
}

impl From<u8> for ControlBits {
    fn from(value: u8) -> Self {
        <Self as BitFieldImpl>::Container::from(value).into()
    }
}

fn main() {
    let mut control_bits = ControlBits::new();
    control_bits.with::<FIN>(true).with::<ACK>(true);
    let mut tcp_header = TcpHeader::new();
    tcp_header.set::<flags>(control_bits);
    tcp_header.set::<DOffset>(8);

    let flags = tcp_header.get::<flags>();
    assert!(flags.get::<FIN>());
    assert!(flags.get::<ACK>());

    assert_eq!(tcp_header.get::<DOffset>(), 8);

    assert_eq!(tcp_header.into_inner(), 0x8011)
}

§How It Works

A BitField is essentially a wrapper around a BitContainer. The BitField trait provides high-level methods for interacting with the bitfield, while the underlying BitContainer defines how values are stored and retrieved at a low level.

To interact with individual fields, each field must also be defined and implement the Field<T> trait (where T is the type of the bitfield).

The BitField trait is sealed and is automatically implemented for all types that implement BitFieldImpl.

The #[bitfield] attribute macro generates a struct that implements BitField, as well as structs implementing Field for each of its fields.

Modules§

bitorder
Module defining bit numbering orders used by bitfields.
debug
Utilities for customizing debug output.
iterable
Contain the default BitContainer implementation.

Traits§

BitContainer
A bit container type used by a BitField, abstracting how bits are stored and retrieved.
BitContainerFor
Provides typed storage and retrieval for a specific type T within a BitContainer.
BitField
Provides all the convenience methods for interacting with a bitfield structure.
BitFieldImpl
Internal trait required by BitField.
Field
Describe a single field within a BitField.
FieldType
Describes a type that can be stored in a BitField via a Field.

Attribute Macros§

bitfield
Attribute macro for defining bitfield structures.