mycelium-bitfield
🍄 bitfield utilities, courtesy of Mycelium.
what is it?
This library provides utilities for defining structured bitfields in Rust. It
consists of a set of types for defining ranges that can be packed and
unpacked from an integer value, and a bitfield!
macro for
generating bitfield types automatically using the packing types. These
components are modular: it's possible to use the packing spec types to
hand-write all of the code that the bitfield!
macro would generate.
This crate was originally implemented for usage in the Mycelium operating system, although it is usable in other projects and does not depend on any Mycelium-specific libraries.
comparison with other crates
There are several other crates implementing bitfields or bitfield-related
utilities in Rust. These crates offer different, but sometimes overlapping,
functionality relative to mycelium-bitfield
. In particular, the most directly
comparable crates that I'm currently aware of are the modular-bitfield
and
bitflags
libraries.
Note This crate exists primarily because I thought it would be fun to write my own bitfield crate, not because the existing libraries were deficient. It is possible that I have a somewhat perverse conception of "fun"...
The
modular-bitfield
crate, in particular, can do most of the same things asmycelium-bitfield
. However, there are some differences between the two libraries which may be interesting to consider.
-
bitflags
: Thebitflags
crate provides a declarative macro for generating a structured type representing a set of bitflags.The critical difference between
bitflags
'bitflags!
macro andmycelium-bitfield
'sbitfield!
macro is that thebitflags
crate only implements bitflags, not bitfields. It is not possible to define multi-bit structured ranges usingbitflags
; only single-bit flags can be set and unset.However, the
bitflags
crate is widely used, has been around for a long time, and is relatively simple and lightweight. If all you need is a set of boolean, single-bit flags, it's a very solid choice. But, if you needmycelium-bitfield
's additional functionality for working with multi-bit ranges, note that it can also do most most of what thebitflags
crate can do. -
modular-bitfield
: Themodular-bitfield
crate provides a procedural macro for generating typed structured bitfields.The functionality implemented by
modular-bitfield
is broadly very similar tomycelium-bitfield
— the two libraries can do most of the same things.The primary difference is that
modular-bitfield
is implemented using a procedural macro attribute, whilemycelium-bitfield
'sbitfield!
macro is a declarative macro. In my opinion, this isn't a reason to prefermycelium-bitfield
overmodular-bitfield
in most use cases. I decided to try to write the whole thing using a declarative macro because I thought it would be a fun challenge, not because it's better (in fact, it would probably have been much easier to implement the bitfield type generation using a procedural macro). However, users who need to reduce or avoid procedural macros for some reason may want to consider choosingmycelium-bitfield
for that reason.The other primary difference between
mycelium-bitfield
andmodular-bitfield
is thatmycelium-bitfield
also provides thepack
module with packing spec types. These types can be used to build bitfield types "by hand", in cases where different behavior from the macro-generated code is needed.modular-bitfield
only provides a procedural macro, and does not have an equivalent to this lower-level interface.On the other hand,
modular-bitfield
provides nicer validation that a typed value used as part of a bitfield actually fits in that bitfield.mycelium-bitfield
cannot currently do this kind of compile-time checking, and relies on implementations of theFromBits
trait for user-provided types being correct.
usage
This crate's API consists of three primary components, the packing spec
types, the bitfield! macro
, and the
FromBits
trait.
packing spec types
The pack
module defines a set of types that can be used to pack and
unpack ranges from integer values of various sizes, such as Pack64
for
packing and unpacking a range from a u64
value.
These packing spec types have const fn
constructors that allow them to be
defined in relationship with each other. For example:
use Pack64;
// Defines a packing spec for the least-significant 12 bits of a 64-bit value.
const LOW: Pack64 = least_significant;
// Defines a packing spec for the next 8 more-significant bits after `LOW`.
const MID: Pack64 = LOW.next;
// Defines a packing spec for the next 4 more-significant bits after `MID`.
const HIGH: Pack64 = MID.next;
// Wrap an integer value to pack it using method calls.
let coffee = pack_in
// pack the 12 bits of `0xfee` at the range specified by `LOW`.
.pack
// pack the 4 bits `0xc` at the range specified by `HIGH`.
.pack
// pack `0xf` in the 8 bits specified by `MID`.
.pack
// unwrap the packing value back into a `u64`.
.bits;
assert_eq!; // i want c0ffee
A majority of the functions in the pack
module are const fn
s, allowing
the use of packing specs in const contexts.
See the module-level docs for pack
for details.
bitfield!
macro
The bitfield!
macro allows defining a structured bitfield type
declaratively. The macro will generate code that uses the pack
module's
packing spec APIs to represent a bitfield type.
For example:
!
// Bitfield types can be cheaply constructed from a raw numeric
// representation:
let bitfield = from_bits;
// `get` methods can be used to unpack fields from a bitfield type:
assert_eq!;
assert_eq!;
// `with` methods can be used to pack bits into a bitfield type by
// value:
let bitfield2 = new
.with
.with;
assert_eq!;
// `set` methods can be used to mutate a bitfield type in place:
let mut bitfield3 = new;
bitfield3
.set
.set;
assert_eq!;
bitfield
See the bitfield!
macro's documentation for details on the macro's usage
and the code it generates.
FromBits
trait
The FromBits
trait can be implemented for user-defined types which can be
used as subfields of a bitfield!
-generated structured bitfield type.
For example:
use ;
// An enum type can implement the `FromBits` trait if it has a
// `#[repr(uN)]` attribute.
bitfield!
// Unpacking a typed value with `get` will return that value, or panic if
// the bit pattern is invalid:
let my_bitfield = from_bits;
assert_eq!;
assert_eq!;
assert_eq!;
// The `try_get` method will return an error rather than panicking if an
// invalid bit pattern is encountered:
let invalid = from_bits;
// There is no `MyEnum` variant for 0b11.
assert!;
See the FromBits
trait documentation for details on
implementing FromBits
for user-defined types.