Crate mvbitfield
source ·Expand description
mvbitfield
generates types to work with bit-aligned fields.
Bitfield structs serve roughly the same use cases as C/C++ structs with bit-field members and are:
- Endian-insensitive, packing fields within an integer rather than across bytes or array elements.
- Flexible and type-safe with optional user-defined field accessor types.
- Suitable for FFI and memory-mapped I/O with care, as always.
Bitfield enums are unit-only Rust enums with a declared bit width that provide safe zero-cost conversions to and from an integer type and can be used as accessors in a bitfield struct.
Demo
// Recommended, but not required. The mvbitfield prelude includes the bitint
// prelude.
use mvbitfield::prelude::*;
bitfield! {
#[lsb_first] // Field packing order.
#[derive(PartialOrd, Ord)] // Other attributes are passed through.
pub struct MyBitfieldStruct: 32 {
// The lowest three bits with public bitint::U3 accessors.
pub some_number: 3,
// The next eight bits with public bitint::U8 accessors.
pub another_number: 8,
// No accessors for field names starting with _.
_padding: 2,
// Private bitint::U11 accessors.
internal_number: 11,
// Skip unused bits, in this case five bits.
..,
// The two next-to-most significant bits with public MyBitfieldEnum
// accessors.
pub an_enum: 2 as MyBitfieldEnum,
// Private bool accessors.
high_bit_flag: 1 as bool,
}
pub enum MyBitfieldEnum: 2 {
// Declare up to 2^width unit variants with optional explicit
// discriminants.
Three = 3,
Zero = 0,
One,
// Generates `Unused2` to complete the enum.
..
}
}
#[bitint_literals]
fn main() {
// Use generated with_* methods to build bitfield structs.
let x = MyBitfieldStruct::zero()
.with_some_number(6_U3)
.with_another_number(0xa5_U8)
.with_internal_number(1025_U11)
.with_an_enum(MyBitfieldEnum::One)
.with_high_bit_flag(true);
// Default accessors return bitints.
assert_eq!(x.some_number(), 6_U3);
assert_eq!(x.some_number().to_primitive(), 6);
assert_eq!(x.another_number(), 0xa5_U8);
assert_eq!(x.another_number().to_primitive(), 0xa5);
assert_eq!(x.internal_number(), 1025_U11);
assert_eq!(x.internal_number().to_primitive(), 1025);
// Custom accessors return the chosen type, which must have Into
// conversions to and from the default accessor bitint.
assert_eq!(x.an_enum(), MyBitfieldEnum::One);
assert_eq!(x.high_bit_flag(), true);
// Zero-cost conversions to and from bitints and to primitive.
// For bitfield structs:
assert_eq!(x.to_bitint(), 0b1_01_00000_10000000001_00_10100101_110_U32);
assert_eq!(x.to_primitive(), 0b1_01_00000_10000000001_00_10100101_110);
assert_eq!(x, MyBitfieldStruct::from_bitint(0xa080252e_U32));
// For bitfield enums:
assert_eq!(MyBitfieldEnum::One.to_bitint(), 1_U2);
assert_eq!(MyBitfieldEnum::One.to_primitive(), 1);
assert_eq!(MyBitfieldEnum::One, MyBitfieldEnum::from_bitint(1_U2));
// Zero-cost conversion from primitive, only for primitive-sized
// bitfield structs and enums.
assert_eq!(x, MyBitfieldStruct::from_primitive(0xa080252e));
bitfield! { enum MyEightBitEnum: 8 { X = 192, .. } }
assert_eq!(MyEightBitEnum::X, MyEightBitEnum::from_primitive(192));
// Bitfield enums optionally generate placeholder variants for unused
// discriminants with `..`. The name is always "Unused" followed by the
// discriminant value in base 10.
assert_eq!(MyBitfieldEnum::Unused2.to_bitint(), 2_U2);
assert_eq!(MyBitfieldEnum::Unused2, MyBitfieldEnum::from_bitint(2_U2));
}
Associated types
Bitfield types have two associated types: a bitint
type and a primitive
type. The bitint
type is the bitfield type’s canonical integer
representation and is one of the 128 unsigned types from the bitint
crate. The primitive type is the bitint
type’s primitive type.
The Bitfield::Bitint
, Bitfield::Primitive
, and
UBitint::Primitive
associated types model these relationships.
Bitfield structs
Bitfield structs are declared with a sequence of fields, but unlike regular Rust structs those fields are not directly exposed. Instead, they are packed into an integer and are only available by value through accessor methods that perform the necessary shifting and masking operations.
Examples
See BitfieldStruct24
and
BitfieldStruct32
for bitfield!
invocations and the resulting generated types.
Bitfield struct packing
Fields occupy contiguous ranges of bits and are tightly packed in
declaration order. Each bit must be covered by precisely one field. The ..
shorthand for a flexible field may be convenient to cover unused bits at
either end or in the middle.
Packing begins with the first declared field at either the least or most significant bit, depending on the packing order attribute. If there is only one field, it must cover every bit and the packing order attribute is optional.
Bitfield struct layout
A bitfield struct has the same layout as its bitint
type. Bitfield structs
of widths 8, 16, 32, 64, or 128 are particularly well suited for
memory-mapped I/O and foreign function interface bindings because their
bitint
types have no forbidden bit patterns. Bitfield structs of other
widths require more care in unsafe contexts because their bitint
types
have unused upper bits that must remain clear.
Bitfield struct trait implementations
Bitfield structs implement the Bitfield
trait and its requirements:
Copy
(andClone
)Debug
Eq
(andPartialEq
)Hash
From<Self::Bitint>
TryFrom<Self::Primitive>
Into<Self::Bitint>
Into<Self::Primitive>
You are free to provide more trait impls alongside the bitfield!
invocation, as with any other type. The bitfield!
macro preserves
attributes it doesn’t recognize and applies them to the generated type, so
you can request additional derives as well.
bitfield! {
#[derive(PartialOrd, Ord)]
#[msb_first]
pub struct MyStruct: 12 {
pub high_bit: 1 as bool,
..
}
}
trait MyOtherTrait {
fn get_five() -> i32;
}
impl MyOtherTrait for MyStruct {
fn get_five() -> i32 { 5 }
}
assert_eq!(MyStruct::get_five(), 5);
assert!(MyStruct::zero() < MyStruct::zero().with_high_bit(true));
Bitfield struct constructors and conversions
Bitfield structs provide all of the Bitfield
trait methods and
conversions to and from the bitint
and primitive type as const
inherent
methods.
impl MyBitfieldStruct {
pub const ZERO: Self;
pub const fn zero() -> Self;
pub const fn new(value: Self::Primitive) -> Option<Self>;
pub const fn new_masked(value: Self::Primitive) -> Self;
pub const unsafe fn new_unchecked(value: Self::Primitive) -> Self;
pub const fn from_bitint(value: Self::Bitint) -> Self;
// Only for primitive widths.
pub const fn from_primitive(value: Self::Primitive) -> Self;
pub const fn to_bitint(self) -> Self::Bitint;
pub const fn to_primitive(self) -> Self::Primitive;
}
See the rustdoc on any generated bitfield struct type for details on behavior, invariants, cost, and safety.
Field accessors
impl MyBitfieldStruct {
pub fn my_field(self) -> T;
pub fn with_my_field(self, value: T) -> Self;
pub fn map_my_field(self, f: impl FnOnce(T) -> T) -> Self;
pub fn set_my_field(&mut self, value: T);
pub fn replace_my_field(&mut self, value: T) -> T;
pub fn update_my_field(&mut self, f: impl FnOnce(T) -> T) -> T;
}
where my_field
is the field name and T
is the field accessor type.
Note that field accessor methods are not const
because they rely on
Into
conversions (plus FnOnce
invocations for map
and update
),
which cannot be const
as of Rust 1.69.
Bitfield enums
Bitfield enums are unit-only/fieldless Rust enums that have a declared bit
width and corresponding bitint
type. A bitfield enum with width n has
precisely 2ⁿ variants, one for each of the bitint
type’s valid primitive
values. This allows for sound zero-cost conversions to and from the bitint
type.
A maximum width is currently enforced at 10 bits to keep compile times and memory usage reasonable.
Examples
See BitfieldEnum1
and
BitfieldEnum3
for practical bitfield!
invocations and the resulting generated types. See
BitfieldEnum8
for a perhaps impractically large
bitfield enum that has primitive width, allowing an additional zero-cost
from_primitive
method and
From<u8>
impl in place of TryFrom<u8>
.
Bitfield enum layout
A bitfield enum has the same layout as its bitint
type.
Bitfield enum trait implementations
Like bitfield structs, bitfield
enums implement the Bitfield
trait and its requirements. Attributes are
passed through to the generated type, permitting doc comments and additional
derives.
Bitfield enum constructors and conversions
Bitfield enums provide all of the Bitfield
trait methods and conversions
to and from the bitint
and primitive type as const
inherent methods.
impl MyBitfieldEnum {
pub const ZERO: Self;
pub const fn zero() -> Self;
pub const fn new(value: Self::Primitive) -> Option<Self>;
pub const fn new_masked(value: Self::Primitive) -> Self;
pub const unsafe fn new_unchecked(value: Self::Primitive) -> Self;
pub const fn from_bitint(value: Self::Bitint) -> Self;
// Only for primitive widths.
pub const fn from_primitive(value: u8) -> Self;
pub const fn to_bitint(self) -> Self::Bitint;
pub const fn to_primitive(self) -> Self::Primitive;
}
See the rustdoc on any generated bitfield enum type for details on behavior, invariants, cost, and safety.
Declaration syntax
A detailed reference is provided with the bitfield!
macro.
Re-exports
pub use ::bitint;
Modules
Macros
- Generates bitfield types.
Traits
- Bitfield struct and enum types.