Expand description
Support for defining bitfields as Rust structures.
The bitfield! macro declares integer types that are split into distinct
bit fields of arbitrary length. Each field is typed using Bounded to
ensure values are properly validated and to avoid implicit data loss.
§Example
use kernel::bitfield;
use kernel::num::Bounded;
bitfield! {
pub struct Rgb(u16) {
15:11 blue;
10:5 green;
4:0 red;
}
}
// Valid value for the `blue` field.
let blue = Bounded::<u16, 5>::new::<0x18>();
// Setters can be chained. Values ranges are checked at compile-time.
let color = Rgb::zeroed()
// Compile-time bounds check of constant value.
.with_const_red::<0x10>()
.with_const_green::<0x1f>()
// A `Bounded` can also be passed.
.with_blue(blue);
assert_eq!(color.red(), 0x10);
assert_eq!(color.green(), 0x1f);
assert_eq!(color.blue(), 0x18);
assert_eq!(
color.into_raw(),
(0x18 << Rgb::BLUE_SHIFT) + (0x1f << Rgb::GREEN_SHIFT) + 0x10,
);
// Convert to/from the backing storage type.
let raw: u16 = color.into();
assert_eq!(Rgb::from(raw), color);§Syntax
bitfield! {
#[attributes]
// Documentation for `Name`.
pub struct Name(storage_type) {
// `field_1` documentation.
hi:lo field_1;
// `field_2` documentation.
hi:lo field_2 => ConvertedType;
// `field_3` documentation.
hi:lo field_3 ?=> ConvertedType;
...
}
}storage_type: The underlying unsigned integer type (u8,u16,u32,u64). Signed integer storage types are not supported.hi:lo: Bit range (inclusive), wherehi >= lo.=> Type: Optional infallible conversion (see below).?=> Type: Optional fallible conversion (see below).- Documentation strings and attributes are optional.
§Generated code
Each field is internally represented as a Bounded parameterized by its bit width. Field
values can either be set/retrieved directly, or converted from/to another type.
The use of Bounded for each field enforces bounds-checking (at build time or runtime) of
every value assigned to a field. This ensures that data is never accidentally truncated.
The macro generates the bitfield type, From and Into implementations for its storage
type, as well as Debug and Zeroable implementations.
For each field, it also generates:
field(): Getter method for the field value.with_field(value): Infallible setter; the argument type must fit within the field’s width.with_const_field::<VALUE>():constsetter; the value is validated at compile time. Usually shorter to use thanwith_fieldfor constant values as it doesn’t require constructing aBounded.try_with_field(value): Fallible setter. Returns an error if the value is out of range.FIELD_MASK,FIELD_SHIFT,FIELD_RANGE: Constants for manual bit manipulation.
§Reserved names for field identifiers
Field identifiers are used to generate methods and associated constants on the bitfield type.
For a field named field, the macro may generate methods named field, with_field,
with_const_field, try_with_field, __field and __with_field, as well as constants named
FIELD_MASK, FIELD_SHIFT and FIELD_RANGE.
Therefore, field identifiers must not use names that would collide with generated items for any field in the same bitfield. The following prefixes are thus reserved for field identifiers:
with_const_try_with___
The field identifiers from_raw, into_raw, and into are also reserved.
In addition, field identifiers should follow Rust snake_case conventions, since the associated
constants are generated by uppercasing the field name.
§Implicit conversions
Types that fit entirely within a field’s bit width can be used directly with setters. For
example, bool works with single-bit fields, and u8 works with 8-bit fields:
use kernel::bitfield;
bitfield! {
pub struct Flags(u32) {
15:8 byte_field;
0:0 flag;
}
}
let flags = Flags::zeroed()
.with_byte_field(0x42_u8)
.with_flag(true);
assert_eq!(flags.into_raw(), (0x42 << Flags::BYTE_FIELD_SHIFT) | 1);§Runtime bounds checking
When a value is not known at compile time, use try_with_field() to check bounds at runtime:
use kernel::bitfield;
bitfield! {
pub struct Config(u8) {
3:0 nibble;
}
}
fn set_nibble(config: Config, value: u8) -> Result<Config, Error> {
// Returns `EOVERFLOW` if `value > 0xf`.
config.try_with_nibble(value)
}§Type conversion
Fields can be automatically converted to/from a custom type using => (infallible) or ?=>
(fallible). The custom type must implement the appropriate From or TryFrom traits with
Bounded.
§Infallible conversion (=>)
Use this when all possible bit patterns of a field map to valid values:
use kernel::bitfield;
use kernel::num::Bounded;
#[derive(Debug, Clone, Copy, PartialEq)]
enum Power {
Off,
On,
}
impl From<Bounded<u32, 1>> for Power {
fn from(v: Bounded<u32, 1>) -> Self {
match *v {
0 => Power::Off,
_ => Power::On,
}
}
}
impl From<Power> for Bounded<u32, 1> {
fn from(p: Power) -> Self {
(p as u32 != 0).into()
}
}
bitfield! {
pub struct Control(u32) {
0:0 power => Power;
}
}
let ctrl = Control::zeroed().with_power(Power::On);
assert_eq!(ctrl.power(), Power::On);§Fallible conversion (?=>)
Use this when some bit patterns of a field are invalid. The getter returns a Result:
use kernel::bitfield;
use kernel::num::Bounded;
#[derive(Debug, Clone, Copy, PartialEq)]
enum Mode {
Low = 0,
High = 1,
Auto = 2,
// 3 is invalid
}
impl TryFrom<Bounded<u32, 2>> for Mode {
type Error = u32;
fn try_from(v: Bounded<u32, 2>) -> Result<Self, u32> {
match *v {
0 => Ok(Mode::Low),
1 => Ok(Mode::High),
2 => Ok(Mode::Auto),
n => Err(n),
}
}
}
impl From<Mode> for Bounded<u32, 2> {
fn from(m: Mode) -> Self {
match m {
Mode::Low => Bounded::<u32, _>::new::<0>(),
Mode::High => Bounded::<u32, _>::new::<1>(),
Mode::Auto => Bounded::<u32, _>::new::<2>(),
}
}
}
bitfield! {
pub struct Config(u32) {
1:0 mode ?=> Mode;
}
}
let cfg = Config::zeroed().with_mode(Mode::Auto);
assert_eq!(cfg.mode(), Ok(Mode::Auto));
// Invalid bit pattern returns an error.
assert_eq!(Config::from(0b11).mode(), Err(3));§Bits outside of declared fields
Bits of the storage type that are not part of any declared field are preserved by the setter
methods, and can only be modified through from_raw or the From implementation from the
storage type.
use kernel::bitfield;
bitfield! {
pub struct Sparse(u8) {
7:6 high;
// Bits 5:1 are not covered by any field.
0:0 low;
}
}
// Set the gap bits via `from_raw`, then mutate the declared fields.
let val = Sparse::from_raw(0b0010_1010)
.with_const_high::<0b11>()
.with_low(true);
// Bits 5:1 are unchanged.
assert_eq!(val.into_raw(), 0b1110_1011);§Signed field values
Bitfield storage types are unsigned. Since field getter methods return a Bounded of the
storage type, fields are also unsigned by default.
If a field needs to encode a signed value, use a custom conversion type with => or ?=> to
perform the sign interpretation explicitly.