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)DebugEq(andPartialEq)HashFrom<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§
- bitfield
- Generates bitfield types.
Traits§
- Bitfield
- Bitfield struct and enum types.