Module bitvec::field [−][src]
Batched load/store access to bitfields.
This module provides load/store access to bitfield regions that emulates the
ordinary memory bus. This functionality enables any BitSlice span to be used
as a memory region, and provides the basis of a library-level analogue to the
bitfield language feature found in C and C++. Additionally, orderings that have
contiguous positions can transfer more than one bit in an operation, allowing a
performance acceleration over sequential bit-by-bit traversal.
The BitField trait is open for implementation. Rust’s implementation rules
currently disallow a crate to implement a foreign trait on a foreign type, even
when parameterized over a local type. If you need such a BitField
implementation with a new BitOrder type, please file an issue.
Batched Behavior
The first purpose of BitField is to provide access to BitSlice regions
as if they were an ordinary memory location. However, this can be done through
the BitSlice sequential API. The second purpose of this trait is to accelerate
such access by using the parallel memory bus to transfer more than one bit at a
time when the region permits it. As such, implementors should provide a transfer
behavior based on shift/mask operations wherever possible, for as wide a span in
a memory element as possible.
Register Bit Order Preservation
As a default assumption, each element of the underlying memory region used to
store part of a value should not reörder the bit-pattern of that value. While
the BitOrder argument is used to determine which segments of the memory
register are live for the purposes of this transfer, it should not be used to
map each individual bit of the transferred value to a corresponding bit of the
storage element. As an example, the Lsb0 and Msb0 implementations both
store the value 12u8 in memory as a four-bit span with its two
more-significant bits set and its two less-significant bits cleared; the
difference is only in which bits of an element are used to store the span.
Endianness
The _le and _be methods of BitField refer to the order in which
successive T elements of a storage region are assigned numeric significance
during a transfer. Within any particular T element, the ordering of its memory
is not governed by the BitField trait.
The provided BitOrder implementors Lsb0 and Msb0 use the local
machine’s byte ordering, and do not reörder bytes during transfer.
_le Methods
When storing a value M into a sequence of memory elements T, store_le
breaks M into chunks from the least significant edge. The least significant
chunk is placed in the lowest-addressed element T, then the next more
significant chunk is placed in the successive address, until the most
significant chunk of the value M is placed in the highest address of a
location T.
When loading a value M out of a sequence of memory elements T, load_le
uses the same chunking behavior: the lowest-addressed T contains the least
significant chunk of the returned M, then each successive address contains a
more significant chunk, until the highest address contains the most significant.
The BitOrder implementation governs where in each T location a fragment
of M is stored.
Let us store 8 bits into memory, over an element boundary, using both Lsb0
and Msb0 orderings:
use bitvec::prelude::*; let val: u8 = 0b11010_011; // STUVW XYZ let mut store = [0u8; 2]; store.view_bits_mut::<Lsb0>() [5 .. 13] .store_le(val); assert_eq!( store, [0b011_00000, 0b000_11010], // XYZ STUVW ); store = [0u8; 2]; store.view_bits_mut::<Msb0>() [5 .. 13] .store_le(val); assert_eq!( store, [0b00000_011, 0b11010_000], // XYZ STUVW );
In both cases, the lower three bits of val were placed into the element at the
lower memory address. The choice of Lsb0 vs Msb0 changed which three
bits in the element were considered to be indexed by 5 .. 8, but store_le
always placed the least three bits of val, in ordinary register order, into
element [0]. Similarly, the higher five bits of val were placed into element
[1]; Lsb0 and Msb0 selected which five bits in the element were indexed
by 8 .. 13, and the bits retained their register order.
_be Methods
When storing a value M into a sequence of memory elements T, store_be
breaks M into chunks from the most significant edge. The most significant
chunk is placed in the lowest-addressed element T, then the next less
significant chunk is placed in the successive address, until the least
significant chunk of the value M is placed in the highest address of a
location T.
When loading a value M out of a sequence of memory elements T, load_be
uses the same chunking behavior: the lowest-addressed T contains the most
significant chunk of the returned M, then each successive address contains a
less significant chunk, until the highest address contains the least
significant.
The BitOrder implementation governs where in each T location a fragment
of M is stored.
Let us store 8 bits into memory, over an element boundary, using both Lsb0
and Msb0 orderings:
use bitvec::prelude::*; let val: u8 = 0b110_10011; // STU VWXYZ let mut store = [0u8; 2]; store.view_bits_mut::<Lsb0>() [5 .. 13] .store_be(val); assert_eq!( store, [0b110_00000, 0b000_10011], // STU VWXYZ ); store = [0u8; 2]; store.view_bits_mut::<Msb0>() [5 .. 13] .store_be(val); assert_eq!( store, [0b00000_110, 0b10011_000], // STU VWXYZ );
In both cases, the higher three bits of val were placed into the element at
the lower memory address. The choice of Lsb0 vs Msb0 changed which
three bits in the element were considered to be indexed by 5 .. 8, but
store_be always placed the greatest three bits of val, in ordinary
register order, into element [0]. Similarly, the lower five bits of val
were placed into element [1]; Lsb0 and Msb0 selected which five bits in
the element were indexed by 8 .. 13, and the bits retained their register
order.
M and T Relationships
BitField permits any type of (unsigned) integer M to be stored into or
loaded from a bit-slice region with any storage type T. While the examples
used u8 for both, for brevity of writing out values, BitField will still
operate correctly for any other combination of types.
Bitfield implementations use the processor’s own concept of integer registers
to operate. As such, the byte-wise memory access patterns for types wider than
u8 depends on your processor’s byte-endianness, as well as which BitField
method and which BitOrder implementation 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, stick to
BitSlice<_, u8> as the bit-field driver. BitSlice<Msb0, u8> will fill memory
in a way that matches a debugger or other memory inspections.
Traits
| BitField | Performs C-style bitfield access through a |