Memory Representation
As discussed in the Type Parameters chapter, bitvec
allows users to select
the specific ordering of bits within a memory element when constructing a type.
This has consequences for how source code translates to an in-memory
representation.
To review: bitvec
provides two orderings of bits within a single memory
element (Lsb0
and Msb0
) and three or four types of memory elements (u8
,
u16
, u32
, and only on systems where it is 8-byte-aligned, u64
). The
usize
type is also supported, but it is not portable, and behaves exactly as
the named register of its width.
The `Cell` and atomic integer variants are not interesting here, as they only
affect how the memory bus operates, not the processor register.
Let us now examine how each possible combination of register width, bit-ordering, and processor byte endianness affects the placement of bits in memory.
Memory Layout
The BitOrder
and BitStore
traits combine with your target architecture’s
byte-ordering of register elements to create a matrix of memory traversals. This
matrix informs what is the appropriate choice of parameters to use for your
program whenever you are using bitvec
for precise memory control rather than
solely for a compact usize
-to-bool
data collection.
The tables below list bytes of a memory address space with addresses increasing to the right, and the bits within those bytes with numeric significance decreasing to the right. This is the ordering used in most debug-printing of memory, so hopefully the table contents should match up with your prior experience viewing memory bytes.
The L
and M
indicate the Lsb0
and Msb0
ordering parameters,
respectively, and xx
indicates that the row matches all register types.
Within each row, traversal begins at zero and follows the arrows to each
successive step. Boundaries between registers are marked with a column;
boundaries between bytes within the same register are marked with a space.
Little-Endian Byte-Ordered Machines
On little-endian machines, the least-significant byte of a register type is stored at the lowest memory address, and each byte-address higher is one step more numerically significant than the last.
byte ║ 00000000│11111111│22222222│33333333│44444444│55555555│66666666│77777777
bit ║ 76543210│76543210│76543210│76543210│76543210│76543210│76543210│76543210
═════╬═════════╪════════╪════════╪════════╪════════╪════════╪════════╪════════
Lxx ║ 1 <--- 0│3 <--- 2│5 <--- 4│7 <--- 6│9 <--- 8│B <--- A│D <--- C│F <--- E
─────╫─────────┼────────┼────────┼────────┼────────┼────────┼────────┼────────
M8 ║ 0 ---> 1│2 ---> 3│4 ---> 5│6 ---> 7│8 ---> 9│A ---> B│C ---> D│E ---> F
M16 ║ 2 ---> 3 0 ---> 1│6 ---> 7 4 ---> 5│A ---> B 8 ---> 9│E ---> F C ---> D
M32 ║ 6 ---> 7 4 ---> 5 2 ---> 3 0 ---> 1│E ---> F C ---> D A ---> B 8 ---> 9
M64 ║ E ---> F C ---> D A ---> B 8 ---> 9 6 ---> 7 4 ---> 5 2 ---> 3 0 ---> 1
Big-Endian Byte-Ordered Machines
On big-endian machines, the most-significant byte of a register type is stored at the lowest memory address, and each byte-address higher is one step less numerically significant than the last.
byte ║ 00000000│11111111│22222222│33333333│44444444│55555555│66666666│77777777
bit ║ 76543210│76543210│76543210│76543210│76543210│76543210│76543210│76543210
═════╬═════════╪════════╪════════╪════════╪════════╪════════╪════════╪════════
L8 ║ 1 <--- 0│3 <--- 2│5 <--- 4│7 <--- 6│9 <--- 8│B <--- A│D <--- C│F <--- E
L16 ║ 3 <--- 2 1 <--- 0│7 <--- 6 5 <--- 4│B <--- A 9 <--- 8│F <--- E D <--- C
L32 ║ 7 <--- 6 5 <--- 4 3 <--- 2 1 <--- 0│F <--- E D <--- C B <--- A 9 <--- 8
L64 ║ F <--- E D <--- C B <--- A 9 <--- 8 7 <--- 6 5 <--- 4 3 <--- 2 1 <--- 0
─────╫─────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────
Mxx ║ 0 ---> 1│2 ---> 3│4 ---> 5│6 ---> 7│8 ---> 9│A ---> B│C ---> D│E ---> F
If you need to care about the memory representation, then you most likely want
to use the <u8, Msb0>
pair. This provides a consistent ordering on all
machines, and the numeric value of the underlying memory will probably match
your expectations about the semantic contents of a data structure.
This chapter, and the BitField
trait, are the two most common sources of
questions about how bitvec
operates. Their intersection is even more complex,
and the layout of numeric integers stored into a BitSlice
is an extremely
common point of confusion.
Read these chapters and the API documentation thoroughly, and experiment with placing data into memory and changing the type parameters to observe their effects on buffer representation.