Attribute Macro modular_bitfield::bitfield
[−]#[bitfield]
Expand description
Applicable to structs to turn their fields into compact bitfields.
Generated API
By default this generates the following API:
-
Constructors:
new()
: Initializes all bits to 0 even if 0 bits may be invalid. Note that invalid bit patterns are supported in that getters and setters will be protecting accesses.
-
Getters:
For every field
f
we generate the following getters:f()
: Returns the value off
and might panic if the value contains an invalid bit pattern.f_or_err()
: Returns the value off
or an error if the value contains an invalid bit pattern.
-
Setters:
For every field
f
we generate the following setters:set_f(new_value)
: Sets the value off
tonew_value
and might panic ifnew_value
is out of bounds for the bit width off
.set_f_checked(new_value)
: Sets the value off
tonew
or returns an error ifnew_value
if out of bounds for the bit width off
.with_f(new_value)
: Similar toset_f
but consumes and returnsSelf
. Primarily useful for method chaining.with_f_checked(new_value)
: Similar toset_f_checked
but consumes and returnsSelf
. Primarily useful for method chaining.
-
Conversions:
from_bytes(bytes)
: Allows to constructor the bitfield type from a fixed array of bytes.into_bytes()
: Allows to convert the bitfield into its underlying byte representation.
Parameters
The following parameters for the #[bitfield]
macro are supported:
Parameter: bytes = N
This ensures at compilation time that the resulting #[bitfield]
struct consists of
exactly N
bytes. Yield a compilation error if this does not hold true.
Example
#[bitfield(bytes = 2)]
pub struct SingedInt {
sign: bool, // 1 bit
value: B15, // 15 bits
}
Parameter: filled: bool
If filled
is true
ensures that the #[bitfield]
struct defines all bits and
therefore has a bitwidth that is divisible by 8. If filled
is false
ensures the
exact opposite.
The default value is: true
Example
#[bitfield(filled = false)]
pub struct Package {
is_received: bool, // 1 bit
is_alive: bool, // 1 bit
status: B2, // 2 bits
}
Parameter: bits = N
With the bits: int
parameter it is possible to control the targeted bit width of
a #[bitfield]
annoated struct. Using bits = N
guarantees that the resulting bitfield
struct will have a bit width of exactly N
.
Example 1
#[bitfield(bits = 16)]
pub struct Package {
is_received: bool, // 1 bit
is_alive: bool, // 1 bit
status: B14, // 14 bits
}
Example 2
The bits: int
parameter is especially useful when using this in conjunction with
#[derive(BitfieldSpecifier)] and
filled = false` as shown in the below example.
#[bitfield(bits = 5)]
#[derive(BitfieldSpecifier)]
pub struct Package {
is_received: bool, // 1 bit
is_alive: bool, // 1 bit
status: B3, // 3 bits
}
Field Parameter: #[bits = N]
To ensure at compile time that a field of a #[bitfield]
struct has a bit width of exactly
N
a user may add #[bits = N]
to the field in question.
Example
#[bitfield]
pub struct Base {
#[bits = 4]
header: Header, // 4 bits
content: B28, // 28 bits
}
Field Parameter: #[skip(..)]
It is possible to skip the entire code generation for getters or setters with the #[skip]
field attribute.
This is useful if a field just needs to be read or written exclusively. Skipping both
setters and getters is useful if you want to have undefined blocks within your bitfields.
Example
#[bitfield]
pub struct Sparse {
#[skip(getters)]
no_getters: B4,
#[skip(setters)]
no_setters: B4,
#[skip]
skipped_entirely: B4,
#[skip(getters, setters)]
skipped_entirely_2: B2,
#[skip(getters)] #[skip(setters)]
skipped_entirely_2: B2,
}
Trick: Wildcards
If you are completely uninterested in a field of a bitfield, for example when specifying some undefined bits in your bitfield you can use double wildcards as their names:
#[bitfield]
pub struct Sparse {
#[skip] __: B10,
a: bool,
#[skip] __: B10,
b: bool,
#[skip] __: B10,
}
Features
Support: #[derive(BitfieldSpecifier)]
If a #[bitfield]
struct is annotated with a #[derive(BitfieldSpecifier)]
attribute
an implementation of the Specifier
trait will be generated for it. This has the effect
that the bitfield struct itself can be used as the type of a field of another bitfield type.
This feature is limited to bitfield types that have a total bit width of 128 bit or fewer. This restriction is ensured at compile time.
Example
#[bitfield(filled = false)]
#[derive(BitfieldSpecifier)]
pub struct Header {
is_received: bool, // 1 bit
is_alive: bool, // 1 bit
status: B2, // 2 bits
}
Now the above Header
bitfield type can be used in yet another #[bitfield]
annotated type:
#[bitfield]
pub struct Base {
header: Header, // 4 bits
content: B28, // 28 bits
}
Support: #[derive(Debug)]
If a #[derive(Debug)]
is found by the #[bitfield]
a naturally formatting implementation
is going to be generated that clearly displays all the fields and their values as the user
would expect.
Also invalid bit patterns for fields are clearly displayed under this implementation.
Example
#[bitfield]
#[derive(Debug)]
pub struct Package {
is_received: bool, // 1 bit
is_alive: bool, // 1 bit
status: B6, // 6 bits
}
let package = Package::new()
.with_is_received(false)
.with_is_alive(true)
.with_status(3);
println!("{:?}", package);
assert_eq!(
format!("{:?}", package),
"Package { is_received: false, is_alive: true, status: 3 }",
);
Support: #[repr(uN)]
It is possible to additionally annotate a #[bitfield]
annotated struct with #[repr(uN)]
where uN
is one of u8
, u16
, u32
, u64
or u128
in order to make it conveniently
interchangeable with such an unsigned integer value.
As an effect to the user this implements From
implementations between the chosen primitive
and the bitfield as well as ensuring at compile time that the bit width of the bitfield struct
matches the bit width of the primitive.
Example
#[bitfield]
#[repr(u16)]
pub struct SignedU16 {
sign: bool, // 1 bit
abs_value: B15, // 15 bits
}
let sint = SignedU16::from(0b0111_0001);
assert_eq!(sint.sign(), true);
assert_eq!(sint.abs_value(), 0b0011_1000);
assert_eq!(u16::from(sint), 0b0111_0001_u16);