Expand description
Bit-Field Memory Slots
This module implements a load/store protocol for BitSlice
regions that
enables them to act as if they were a storage slot for integers. Implementations
of the BitField
trait provide behavior similar to C and C++ language
bit-fields. While any BitSlice<T, O>
instantiation is able to provide this
behavior, the lack of specialization in the language means that it is instead
only implemented for BitSlice<T, Lsb0>
and BitSlice<T, Msb0>
in order to
gain a performance advantage.
Batched Behavior
Bit-field behavior can be simulated using BitSlice
’s existing APIs; however,
the inherent methods are all required to operate on each bit individually in
sequence. In addition to the semantic load/store behavior this module describes,
it also implements it in a way that takes advantage of the contiguity properties
of the Lsb0
and Msb0
orderings in order to maximize how many bits are
transferred in each cycle of the overall operation.
This is most efficient when using BitSlice<usize, O>
as the storage bit-slice,
or using .load::<usize>()
or .store::<usize>()
as the transfer type.
Bit-Slice Storage and Integer Value Relationships
BitField
permits any type of integer, including signed integers, to be
stored into or loaded out of a BitSlice<T, _>
with any storage type T
. While
the examples in this module will largely use u8
, just to keep the text
concise, BitField
is tested, and will work correctly, for any combination of
types.
BitField
implementations use the processor’s own concept of integer registers
to operate. As such, the byte-wise memory access patters for types wider than
u8
depends on your processor’s byte endianness, as well as which BitField
method, and which BitOrder
type parameter, you are using.
BitField
only operates within processor registers; traffic of T
elements
between the memory bank and the processor register is controlled entirely by the
processor.
If you do not want to introduce the processor’s byte endianness as a variable
that affects the in-memory representation of stored integers, use
BitSlice<u8, _>
as the bit-field storage type. In particular,
BitSlice<u8, Msb0>
will fill memory in a way that intuitively matches what
most debuggers show when inspecting memory.
On the other hand, if you do not care about memory representation and just need
fast storage of less than an entire integer, BitSlice<Lsb0, usize>
is likely
your best bet. As always, the choice of type parameters is a trade-off with
different advantages for each combination, which is why bitvec
refuses to make
the choice for you.
Signed Behavior
The length of the BitSlice
that stores a value is considered to be the width
of that value when it is loaded back out. As such, storing an i16
into a
bit-slice of length 12
means that the stored value has type i12
.
When calling .load::<i16>()
on a 12-bit slice, the load will detect the sign
bit of the i12
value and sign-extend it to i16
. This means that storing
2048i16
into a 12-bit slice and then loading it back out into an i16
will
produce -2048i16
(negative), not 2048i16
(positive), because 1 << 11
is
the sign bit.
BitField
does not record the true sign bit of an integer being stored, and
will not attempt to set the sign bit of the narrowed value in storage. Storing
-127i8
(0b1000_0001
) into a 7-bit slice will load 1i8
.
Register Bit Order Preservation
The implementations in this module assume that the bits within a value being transferred into or out of a bit-slice should not be re-ordered. While the implementations will segment a value in order to make it fit into bit-slice storage, and will order those segments in memory according to their type parameter and specific trait method called, each segment will remain individually unmodified.
If we consider the value 0b100_1011
, segmented at the underscore, then the
segments 0b100
and 0b1011
will be present somewhere in the bit-slice that
stores them. They may be shifted within an element or re-ordered across
elements, but each segment will not be changed.
Endianness
bitvec
uses the BitOrder
trait to describe the order of bits within a single
memory element. This ordering is independent of, and does not consider, the
ordering of memory elements in a sequence; bitvec
is always “little-endian” in
this regard: lower indices are in lower memory addresses, higher indices are in
higher memory addresses.
However, BitField
is explicitly aware of multiple storage elements in
sequence. It is by design able to allow combinations such as
<BitSlice<u8, Lsb0> as BitField>::store_be::<u32>
. Even where the storage and
value types are the same, or the value is narrower, the bit-slice may be spread
across multiple elements and must segment the value across them.
The _be
and _le
orderings on BitField
method names refer to the numeric
significance of bit-slice storage elements.
In _be
methods, lower-address storage elements will hold more-significant
segments of the value, and higher-address storage will hold less-significant.
In _le
methods, lower-address storage elements will hold less-significant
segments of the value, and higher-address storage will hold more-significant.
Consider again the value 0b100_1011
, segmented at the underscore. When used
with .store_be()
, it will be placed into memory as [0b…100…, 0b…1011…]
; when
used with .store_le()
, it will be placed into memory as [0b…1011…, 0b…100…]
.
Bit-Ordering Behaviors
The _be
and _le
suffices select the ordering of storage elements in memory.
The other critical aspect of the BitField
memory behavior is selecting
which bits in a storage element are used when a bit-slice has partial
elements.
When BitSlice<_, Lsb0>
produces a Domain::Region
, its head
is in the
most-significant bits of its element and its tail
is in the least-significant
bits. When BitSlice<_, Msb0>
produces a Region
, its head
is in the
least-significant bits, and its tail
is in the most-significant bits.
You can therefore use these combinations of BitOrder
type parameter and
BitField
method suffix to select exactly the memory behavior you want for a
storage region.
Each implementation of BitField
has documentation showing exactly what its
memory layout looks like, with code examples and visual inspections of memory.
This documentation is likely collapsed by default when viewing the trait docs;
be sure to use the [+]
button to expand it!
Traits
C-Style Bit-Field Access