Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
# `bitvec` API Documentation
Rust release `1.54` stabilized the use of `#[doc = include_str!()]`, which
allows documentation to be sourced from external files. This directory contains
the Rustdoc API documentation for items whose text is larger than a comment
block warrants.
The files here use Rustdocs ability to resolve symbol paths as link references,
and so will not render correctly in other Markdown viewers. The target renderer
is Rustdoc, not CommonMark.
Module and type documentation should generally be moved to this directory;
function, struct field, and enum variant documentation should generally stay
in source.
+22
View File
@@ -0,0 +1,22 @@
# Memory Bus Access Management
`bitvec` allows a program to produce handles over memory that do not *logically*
alias their bits, but *may* alias their hardware locations. This module provides
a unified interface for memory accesses that can be specialized to handle such
aliased and unaliased events.
The [`BitAccess`] trait provides capabilities to access individual or clustered
bits in memory elements through shared, maybe-aliased, references. Its
implementations are responsible for coördinating synchronization and contention
as needed.
The [`BitSafe`] trait guards [`Radium`] types in order to forbid writing through
shared-only references, and require access to an `&mut` exclusive reference for
modification. This permits other components in the crate that do *not* have
`BitSafe` reference guards to safely mutate a referent element that a `BitSafe`d
reference can observe, while preventing that reference from emitting mutations
of its own.
[`BitAccess`]: self::BitAccess
[`BitSafe`]: self::BitSafe
[`Radium`]: radium::Radium
+40
View File
@@ -0,0 +1,40 @@
# Bit-Level Access Instructions
This trait extends [`Radium`] in order to manipulate specific bits in an element
according to the crates logic. It drives all memory access instructions and is
responsible for translating the bit-selection logic of the [`index`] module into
real effects.
This is blanket-implemented on all types that permit shared-mutable memory
access via the [`radium`] crate. Its use is constrained in the [`store`] module.
It is required to be a publicly accessible symbol, as it is exported in other
traits, but it is a crate-internal item and is not part of the public API. Its
blanket implementation for `<R: Radium>` prevents any other implementations from
being written.
## Implementation and Safety Notes
This trait is automatically implemented for all types that implement `Radium`,
and relies exclusively on `Radium`s API and implementations for its work. In
particular, `Radium` has no functions which operate on **pointers**: it
exclusively operates on memory through **references**. Since references must
always refer to initialized memory, `BitAccess` and, by extension, all APIs in
`bitvec` that touch memory, cannot be used to operate on uninitialized memory in
any way.
While you may *create* a `bitvec` pointer object that targets uninitialized
memory, you may not *dereference* it until the targeted memory has been wholly
initialized with integer values.
This restriction cannot be loosened without stable access to pointer-based
atomic intrinsics in the Rust standard library and corresponding updates to the
`Radium` trait.
Do not attempt to access uninitialized memory through `bitvec`. Doing so will
cause `bitvec` to produce references to uninitialized memory, which is undefined
behavior.
[`Radium`]: radium::Radium
[`index`]: crate::index
[`radium`]: radium
[`store`]: crate::store
+16
View File
@@ -0,0 +1,16 @@
# Read-Only Semivolatile Handle
This trait describes views of memory that are not permitted to modify the value
they reference, but must tolerate external modification to that value.
Implementors must tolerate shared-mutability behaviors, but are not allowed to
expose shared mutation APIs. They are permitted to modify the referent only
under `&mut` exclusive references.
This behavior enables an important aspect of the `bitvec` memory model when
working with memory elements that multiple [`&mut BitSlice`][0] references
touch: each `BitSlice` needs to be able to give the caller a view of the memory
element, but they also need to prevent modification of bits outside of their
span. This trait enables callers to view raw underlying memory without
improperly modifying memory that *other* `&mut BitSlice`s expect to be stable.
[0]: crate::slice::BitSlice
+12
View File
@@ -0,0 +1,12 @@
# Read-Only Shared-Mutable Handle
This type marks a handle to a shared-mutable type that may be modified through
*other* handles, but cannot be modified through *this* one. It is used when a
[`BitSlice`] region has partial ownership of an element and wishes to expose the
entire underlying raw element to the user without granting them write
permissions.
Under the `feature = "atomic"` build setting, this uses `radium`s best-effort
atomic alias; when this feature is disabled, it reverts to `Cell`.
[`BitSlice`]: crate::slice::BitSlice
+21
View File
@@ -0,0 +1,21 @@
# Statically-Allocated, Fixed-Size, Bit Buffer
This module defines a port of the [array fundamental][0] and its APIs. The
primary export is the [`BitArray`] structure. This is a thin wrapper over
`[T; N]` that provides a [`BitSlice`] view of its contents and is *roughly*
analogous to the C++ type [`std::bitset<N>`].
See the `BitArray` documentation for more details on its usage.
## Submodules
- `api` contains ports of the standard librarys array type and `core::array`
module.
- `iter` contains ports of array iteration.
- `ops` defines operator-sigil traits.
- `traits` defines all the other traits.
[0]: https://doc.rust-lang.org/std/primitive.array.html
[`BitArray`]: self::BitArray
[`BitSlice`]: crate::slice::BitSlice
[`std::bitset<N>`]: https://en.cppreference.com/w/cpp/utility/bitset
+108
View File
@@ -0,0 +1,108 @@
# Bit-Precision Array Immediate
This type is a wrapper over the [array fundamental][0] `[T; N]` that views its
contents as a [`BitSlice`] region. As an array, it can be held directly by value
and does not require an indirection such as the `&BitSlice` reference.
## Original
[`[T; N]`](https://doc.rust-lang.org/std/primitive.array.html)
## Usage
`BitArray` is a Rust analogue of the C++ [`std::bitset<N>`] container. However,
restrictions in the Rust type system do not allow specifying exact bit lengths
in the array type. Instead, it must specify a storage array that can contain all
the bits you want.
Because `BitArray` is a plain-old-data object, its fields are public and it has
no restrictions on its interior value. You can freely access the interior
storage and move data in or out of the `BitArray` type with no cost.
As a convenience, the [`BitArr!`] type-constructor macro can produce correct
type definitions from an exact bit count and your memory-layout type parameters.
Values of that type can then be built from the [`bitarr!`] *value*-constructor
macro:
```rust
use bitvec::prelude::*;
type Example = BitArr!(for 43, in u32, Msb0);
let example: Example = bitarr!(u32, Msb0; 1; 33);
struct HasBitfield {
inner: Example,
}
let ex2 = HasBitfield {
inner: BitArray::new([1, 2]),
};
```
Note that the actual type of the `Example` alias is `BitArray<[u32; 2], Msb0>`,
as that is `ceil(32, 43)`, so the `bitarr!` macro can accept any number of bits
in `33 .. 65` and will produce a value of the correct type.
## Type Parameters
`BitArray` differs from the other data structures in the crate in that it does
not take a `T: BitStore` parameter, but rather takes `A: BitViewSized`. That
trait is implemented by all `T: BitStore` scalars and all `[T; N]` arrays of
them, and provides the logic to translate the aggregate storage into the memory
sequence that the crate expects.
As with all `BitSlice` regions, the `O: BitOrder` parameter specifies the
ordering of bits within a single `A::Store` element.
## Future API Changes
Exact bit lengths cannot be encoded into the `BitArray` type until the
const-generics system in the compiler can allow type-level computation on type
integers. When this stabilizes, `bitvec` will issue a major upgrade that
replaces the `BitArray<A, O>` definition with `BitArray<T, O, const N: usize>`
and match the C++ `std::bitset<N>` definition.
## Large Bit-Arrays
As with ordinary arrays, large arrays can be expensive to move by value, and
should generally be preferred to have static locations such as actual `static`
bindings, a long lifetime in a low stack frame, or a heap allocation. While you
certainly can `Box<[BitArray<A, O>]>` directly, you may instead prefer the
[`BitBox`] or [`BitVec`] heap-allocated regions. These offer the same storage
behavior and are better optimized than `Box<BitArray>` for working with the
contained `BitSlice` region.
## Examples
```rust
use bitvec::prelude::*;
const WELL_KNOWN: BitArr!(for 16, in u8, Lsb0) = BitArray::<[u8; 2], Lsb0> {
data: *b"bv",
..BitArray::ZERO
};
struct HasBitfields {
inner: BitArr!(for 50, in u8, Lsb0),
}
impl HasBitfields {
fn new() -> Self {
Self {
inner: bitarr!(u8, Lsb0; 0; 50),
}
}
fn some_field(&self) -> &BitSlice<u8, Lsb0> {
&self.inner[2 .. 52]
}
}
```
[0]: https://doc.rust-lang.org/std/primitive.array.html
[`BitArr!`]: macro@crate::BitArr
[`BitBox`]: crate::boxed::BitBox
[`BitSlice`]: crate::slice::BitSlice
[`BitVec`]: crate::vec::BitVec
[`bitarr!`]: macro@crate::bitarr
[`std::bitset<N>`]: https://en.cppreference.com/w/cpp/utility/bitset
+8
View File
@@ -0,0 +1,8 @@
# Bit-Array Iteration
This structure wraps a bit-array and provides by-value iteration of the bits it
contains.
## Original
[`array::IntoIter`](core::array::IntoIter)
@@ -0,0 +1,16 @@
# Bit-Slice to Bit-Array Conversion Error
This error is produced when an `&BitSlice` view is unable to be recast as a
`&BitArray` view with the same parameters.
Unlike ordinary scalars and arrays, where arrays are never aligned more
stringently than their components, `BitSlice` is aligned to an individual bit
while `BitArray` is aligned to its `A` storage type.
This is produced whenever a `&BitSlice` view is not exactly as long as the
destination `&BitArray` view is, or does not also begin at the zeroth bit in an
`A::Store` element.
## Original
[`array::TryFromSliceError`](core::array::TryFromSliceError)
+19
View File
@@ -0,0 +1,19 @@
# Port of Array Inherent Methods
This module ports the inherent methods available on the [array] fundamental
type.
As of 1.56, only `.map()` is stable. The `.as_slice()` and `.as_mut_slice()`
methods are ported, as the *behavior* has always been stable, and only the name
is new.
The remaining methods (as of 1.56, `.each_mut()`, `.each_ref()`, `.zip()`) are
not ported. While `BitArray` is capable of implementing their behavior with the
existing crate APIs, the `const`-generic system is not yet able to allow
construction of an array whose length is dependent on an associated `const` in a
type parameter.
These methods will not be available until the `const`-generic system improves
enough for `bitvec 2` to use the proper `BitArray` API.
[array]: https://doc.rust-lang.org/std/primitive.array.html
+5
View File
@@ -0,0 +1,5 @@
# Bit-Array Iteration
This module defines the core iteration logic for `BitArray`. It includes the
`IntoIterator` implementations on bit-arrays and their references, as well as
the `IntoIter` struct that walks bit-arrays by value.
+15
View File
@@ -0,0 +1,15 @@
# Heap-Allocated, Fixed-Size, Bit Buffer
This module defines an analogue to `Box<[bool]>`, as `Box<BitSlice>` cannot be
constructed or used in any way. Like `Box<[T]>`, this is a heap allocation that
can modify its contents, but cannot resize the collection. The `BitBox` value is
an owning [`*BitSlice`] pointer, and can be used to access its contents without
any decoding.
You should generally prefer [`BitVec`] or [`BitArray`]; however, very large
`BitArrays` are likely better served being copied into a `BitBox` rather than
being boxed themselves when moved into the heap.
[`BitArray`]: crate::array::BitArray
[`BitVec`]: crate::vec::BitVec
[`*BitSlice`]: crate::slice::BitSlice
+59
View File
@@ -0,0 +1,59 @@
# Fixed-Size, Heap-Allocated, Bit Slice
`BitBox` is a heap-allocated [`BitSlice`] region. It is a distinct type because
the implementation of bit-slice pointers means that `Box<BitSlice>` cannot
exist. It can be created by cloning a bit-slice into the heap, or by freezing
the allocation of a [`BitVec`]
## Original
[`Box<[T]>`](alloc::boxed::Box)
## API Differences
As with `BitSlice`, this takes a pair of [`BitOrder`] and [`BitStore`] type
parameters to govern the buffers memory representation. Because `BitSlice` is
unsized, `BitBox` has almost none of the `Box` API, and is difficult to use
directly.
## Behavior
`BitBox`, like `&BitSlice`, is an opaque pointer to a bit-addressed slice
region. Unlike `&BitSlice`, it uses the allocator to guarantee that it is the
sole accessor to the referent buffer, and is able to use that uniqueness
guarantee to specialize some `BitSlice` behavior to be faster or more efficient.
## Safety
`BitBox` is, essentially, a `NonNull<BitSlice<T, O>>` pointer. The internal
value is opaque and cannot be inspected or modified by user code.
If you attempt to do so, your program becomes inconsistent. You will likely
break the allocators internal state and cause a crash. No guarantees of crash
*or* recovery are provided. Do not inspect or modify the `BitBox` handle value.
## Construction
The simplest way to construct a `BitBox` is by using the [`bitbox!`] macro. You
can also explicitly clone a `BitSlice` with [`BitBox::from_bitslice`], or freeze
a `BitVec` with [`BitVec::into_boxed_bitslice`].
## Examples
```rust
use bitvec::prelude::*;
let a = BitBox::from_bitslice(bits![1, 0, 1, 1, 0]);
let b = bitbox![0, 1, 0, 0, 1];
let b_raw: *mut BitSlice = BitBox::into_raw(b);
let b_reformed = unsafe { BitBox::from_raw(b_raw) };
```
[`BitBox::from_bitslice`]: self::BitBox::from_bitslice
[`BitOrder`]: crate::order::BitOrder
[`BitSlice`]: crate::slice::BitSlice
[`BitStore`]: crate::store::BitStore
[`BitVec`]: crate::vec::BitVec
[`BitVec::into_boxed_bitslice`]: crate::vec::BitVec::into_boxed_bitslice
[`bitbox!`]: macro@crate::bitbox
+11
View File
@@ -0,0 +1,11 @@
# Boxed Bit-Slice Iteration
This module contains the by-value iterator used by both `BitBox` and `BitVec`.
In the standard library, this iterator is defined under `alloc::vec`, not
`alloc::boxed`, as `Box` already has an iteration implementation that forwards
to its boxed value.
It is moved here for simplicity: both `BitBox` and `BitVec` iterate over a
dynamic bit-slice by value, and must deällocate the region when dropped. As
`BitBox` has a smaller value than `BitVec`, it is used as the owning handle for
the bit-slice being iterated.
+132
View File
@@ -0,0 +1,132 @@
# Memory Region Description
This module bridges the abstract [`BitSlice`] region to real memory by
segmenting any bit-slice along its maybe-aliased and known-unaliased boundaries.
This segmentation applies to both bit-slice and ordinary-element views of
memory, and can be used to selectively remove alias restrictions or to enable
access to the underlying memory with ordinary types.
The four enums in this module all intentionally have the same variants by name
and shape, in order to maintain textual consistency.
## Memory Layout Model
Any bit-slice resident in memory has one of two major kinds, which the enums in
this module refer to as `Enclave` and `Region`
### Enclave
An `Enclave` layout occurs when a bit-slice is contained entirely within a
single memory element, and does not include either the initial or final semantic
index in its span.
```text
[ 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 ]
[ ^^^^^^^^^^^^^^^^^^^^^ ]
```
In an 8-bit element, a bit-slice is considered to be an `Enclave` if it is
contained entirely in the marked interior bits, and touches *neither* bit 7 nor
bit 0. Wider elements may touch interior byte boundaries, and only restrict bits
0 and `width - 1`.
### Region
A `Region` layout occurs when a bit-slice consists of:
- zero or one half-spanned head element (excludes bit 0, includes `width - 1`)
- zero or more fully-spanned elements body (includes both 0 and `width - 1`)
- zero or one half-spanned tail element (includes bit 0, excludes `width - 1`)
Each of these three sections is optionally present independently of the other
two. That is, in the following three bytes, all of the following bit-slices have
the `Region` layout:
```text
[ 7 6 5 4 3 2 1 0 ] [ 7 6 5 4 3 2 1 0 ] [ 7 6 5 4 3 2 1 0 ]
[ ]
[ h h h h ]
[ b b b b b b b b ]
[ t t t t ]
[ h h h h t t t t ]
[ h h h h b b b b b b b b ]
[ b b b b b b b b t t t t ]
[ h h h h b b b b b b b b t t t t ]
```
1. The empty bit-slice is a region with all of its segments blank.
1. A bit-slice with one element that touches `width - 1` but not 0 has a head,
but no body or tail.
1. A bit-slice that touches both `0` and `width - 1` of any number of elements
has a body, but no head or tail.
1. A bit-slice with one element that touches 0 but not `width - 1` has a tail,
but no head or body.
1. A bit-slice with two elements, that touches neither 0 of the first nor
`width - 1` of the second (but by definition `width - 1` of the first and 0
of the second; bit-slices are contiguous) has a head and tail, but no body.
The final three rows show how the individual segments can be composed to
describe all possible bit-slices.
## Aliasing Awareness
The contiguity property of `BitSlice` combines with the `&`/`&mut` exclusion
rules of the Rust language to provide additional information about the state of
the program that allows a given bit-slice to exist.
Specifically, any well-formed Rust program knows that *if* a bit-slice is able
to produce a `Region.body` segment, *then* that body is not aliased by `bitvec`,
and can safely transition to the `T::Unalias` state. Alias-permitting types like
`Cell` and the atomics will never change their types (because `bitvec` cannot
know that there are no views to a region other than what it has been given), but
a tainted `BitSlice<O, u8::Alias>` bit-slice can revert its interior body back
to `u8` and no longer require the alias tainting.
The head and tail segments do not retain their history, and cannot tell whether
they have been created by splitting or by shrinking, so they do not change their
types at all.
## Raw Memory Access
The [`BitDomain`] enum only splits a bit-slice along these boundaries, and
allows a bit-slice view to safely shed aliasing protection added to it by
[`.split_at_mut()`].
The [`Domain`] enum completely sheds its bit-precision views, and reverts to
ordinary element accesses. The body segment is an ordinary Rust slice with no
additional information or restriction; it can be freely used without regard for
any of `bitvec`s constraints.
In order to preserve the rules that any given bit-slice can never be used to
affect bits outside of its own view of memory, the underlying memory of the head
and tail segments is only made accessible through a [`PartialElement`] reference
guard. This guard is an opaque proxy to the memory location, and holds both a
reference and the bit-mask required to prevent reading from or writing to the
bits outside the scope of the originating bit-slice.
## Generics
This module, and the contents of [`ptr`], make extensive use of a trait-level
mutability and reference tracking system in order to reduce code duplication and
provide a more powerful development environment than would be achieved with
macros.
As such, the trait bounds on types in this module are more intense than the
standard `<T, O>` fare in the crates main data structures. However, they are
only ever instantiated with shared or exclusive references, and all of the
bounds are a much more verbose way of saying “a reference, that is maybe-mut and
maybe-slice, of `T`”.
User code does not need to be aware of any of this: the `BitSlice` APIs that
call into this module always result in structures where the complex bounds are
reduced to ordinary slice references.
[`BitDomain`]: BitDomain
[`BitSlice`]: crate::slice::BitSlice
[`Domain`]: Domain
[`PartialElement`]: PartialElement
[`ptr`]: crate::ptr
[`.split_at_mut()`]: crate::slice::BitSlice::split_at_mut
+30
View File
@@ -0,0 +1,30 @@
# Bit-Slice Partitioning
This enum partitions a bit-slice into its head- and tail- edge bit-slices, and
its interior body bit-slice, according to the definitions laid out in the module
documentation.
It fragments a [`BitSlice`] into smaller `BitSlice`s, and allows the interior
bit-slice to become `::Unalias`ed. This is useful when you need to retain a
bit-slice view of memory, but wish to remove synchronization costs imposed by a
prior call to [`.split_at_mut()`] for as much of the bit-slice as possible.
## Why Not `Option`?
The `Enclave` variant always contains as its single field the exact bit-slice
that created the `Enclave`. As such, this type is easily replaceäble with an
`Option` of the `Region` variant, which when `None` is understood to be the
original.
This exists as a dedicated enum, even with a technically useless variant, in
order to mirror the shape of the element-domain enum. This type should be
understood as a shortcut to the end result of splitting by element-domain, then
mapping each `PartialElement` and slice back into `BitSlice`s, rather than
testing whether a bit-slice can be split on alias boundaries.
You can get the alternate behavior, of testing whether or not a bit-slice can be
split into a `Region` or is unsplittable, by calling `.bit_domain().region()`
to produce exactly such an `Option`.
[`BitSlice`]: crate::slice::BitSlice
[`.split_at_mut()`]: crate::slice::BitSlice::split_at_mut
+63
View File
@@ -0,0 +1,63 @@
# Bit-Slice Element Partitioning
This structure provides the bridge between bit-precision memory modeling and
element-precision memory manipulation. It allows a bit-slice to provide a safe
and correct view of the underlying memory elements, without exposing the values,
or permitting mutation, of bits outside a bit-slices control but within the
elements the bit-slice uses.
Nearly all memory access that is not related to single-bit access goes through
this structure, and it is highly likely to be in your hot path. Its code is a
perpetual topic of optimization, and improvements are always welcome.
This is essentially a fully-decoded `BitSpan` handle, in that it addresses
memory elements directly and contains the bit-masks needed to selectively
interact with them. It is therefore by necessity a large structure, and is
usually only alive for a short time. It has a minimal API, as most of its
logical operations are attached to `BitSlice`, and merely route through it.
If your application cannot afford the cost of repeated `Domain` construction,
please [file an issue][0].
## Memory Model and Variants
A given `BitSlice` has essentially two possibilities for where it resides in
real memory:
- it can reside entirely in the interior of a exactly one memory element,
touching neither edge bit, or
- it can touch at least one edge bit of zero or more elements.
These states correspond to the `Enclave` and `Region` variants, respectively.
When a `BitSlice` has only partial control of a given memory element, that
element can only be accessed through the bit-slices provenance by a
[`PartialElement`] handle. This handle is an appropriately-guarded reference to
the underlying element, as well as mask information needed to interact with the
raw bits and to manipulate the numerical contents. Each `PartialElement` guard
carries permissions for *its own bits* within the guarded element, independently
of any other handle that may access the element, and all handles are
appropriately synchronized with each other to prevent race conditions.
The `Enclave` variant is a single `PartialElement`. The `Region` variant is more
complex. It has:
1. an optional `PartialElement` for the case where the bit-slice only partially
occupies the lowest-addressed memory element it governs, starting after
bit-index 0 and extending up to the maximal bit-index,
1. a slice of zero or more fully-occupied memory elements,
1. an optional `PartialElement` for the case where it only partially occupies
the highest-addressed memory element it governs, starting at bit-index 0 and
ending before the maximal.
## Usage
Once created, match upon a `Domain` to access its fields. Each `PartialElement`
has a [`.load_value()`][`PartialElement::load_value`] method that produces its
stored value (with all ungoverned bits cleared to 0), and a `.store_value()`
that writes into its governed bits. If present, the fully-occupied slice can be
used as normal.
[0]: https://github.com/bitvecto-rs/bitvec/issues/new
[`PartialElement`]: crate::domain::PartialElement
[`PartialElement::load_value`]: crate::domain::PartialElement::load_value
@@ -0,0 +1,30 @@
# Partially-Owned Memory Element
This type is a guarded reference to memory that permits interacting with it as
an integer, but only allows views to the section of the integer that the
producing handle has permission to observe. Unlike the `BitSafe` type family in
the [`access`] module, it is not a transparent wrapper that can be used for
reference conversion; it is a “heavy reference” that carries the mask and
## Type Parameters
- `T`: The type, including register width and alias information, of the
bit-slice handle that created it.
- `O`: This propagates the bit-ordering type used by the [`BitSlice`] handle
that created it.
## Lifetime
This carries the lifetime of the bit-slice handle that created it.
## Usage
This structure is only created as part of the [`Domain`] region descriptions,
and refers to partially-occupied edge elements. The underlying referent memory
can be read with `.load_value()` or written with `.store_value()`, and the
appropriate masking will be applied in order to restrict access to only the
permitted bits.
[`access`]: crate::access
[`BitSlice`]: crate::slice::BitSlice
[`Domain`]: Domain
+133
View File
@@ -0,0 +1,133 @@
# 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 processors own concept of integer registers
to operate. As such, the byte-wise memory access patters for types wider than
`u8` depends on your processors 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 processors 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!
[`BitField`]: self::BitField
[`BitOrder`]: crate::order::BitOrder
[`BitSlice`]: crate::slice::BitSlice
[`Domain::Region`]: crate::domain::Domain::Region
+96
View File
@@ -0,0 +1,96 @@
# C-Style Bit-Field Access
This trait describes data transfer between a [`BitSlice`] region and an ordinary
integer. It is not intended for use by any other types than the data structures
in this crate.
The methods in this trait always operate on the `bitslice.len()` least
significant bits of an integer, and ignore any remaining high bits. When
loading, any excess high bits not copied out of a bit-slice are cleared to zero.
## Usage
The trait methods all panic if called on a bit-slice that is wider than the
integer type being transferred. As such, the first step is generally to subslice
a larger data structure into exactly the region used for storage, with
`bits[start .. end]`. Then, call the desired method on the narrowed bit-slice.
## Target-Specific Behavior
If you do not care about the details of the memory layout of stored values, you
can use the [`.load()`] and [`.store()`] unadorned methods. These each forward
to their `_le` variant on little-endian targets, and their `_be` variant on
big-endian. These will provide a reasonable default behavior, but do not
guarantee a stable memory layout, and their buffers are not suitable for
de/serialization.
If you require a stable memory layout, you will need to choose a `BitSlice`
with a fixed `O: BitOrder` type parameter (not `LocalBits`), and use a fixed
method suffix (`_le` or `_be`). You should *probably* also use `u8` as your
`T: BitStore` parameter, in order to avoid any byte-ordering issues. `bitvec`
never interferes with processor concepts of wide-integer layout, and always
relies on the target machines behavior for this work.
## Element- and Bit- Ordering Combinations
Remember: the `_le` and `_be` method suffixes are completely independent of the
`Lsb0` and `Msb0` types! `_le` and `_be` refer to the order in which successive
memory elements are considered to gain numerical significance, while `BitOrder`
refers only to the order of successive bits in *one* memory element.
The `BitField` and `BitOrder` traits are ***not*** related.
When a load or store operation is contained in only one memory element, then the
`_le` and `_be` methods have the same behavior: they exchange an integer value
with the segment of the element that its governing `BitSlice` considers live.
Only when a `BitSlice` covers multiple elements does the distinction come into
play.
The `_le` methods consider numerical significance to start low and increase with
increasing memory address, while the `_be` methods consider numerical
significance to start high and *decrease* with increasing memory address. This
distinction affects the order in which memory elements are used to load or store
segments of the exchanged integer value.
Each trait method has detailed visual diagrams in its documentation.
Additionally, each *implementation*s documentation has diagrams that show what
the governed bit-sections of elements are! Be sure to check each, or to run the
demonstration with `cargo run --example bitfield`.
## Bitfield Value Types
When interacting with a bit-slice as a C-style bitfield, it can *only* store the
signed or unsigned integer types. No other type is permitted, as the
implementation relies on the 2s-complement significance behavior of processor
integers. Record types and floating-point numbers do not have this property, and
thus have no sensible default protocol for truncation and un/marshalling that
`bitvec` can use.
If you have such a protocol, you may implement it yourself by providing a
de/serialization transform between your type and the integers. For instance, a
numerically-correct protocol to store floating-point numbers in bitfields might
look like this:
```rust
use bitvec::mem::bits_of;
use funty::Floating;
fn to_storage<F>(num: F, width: usize) -> F::Raw
where F: Floating {
num.to_bits() >> (bits_of::<F>() - width)
}
fn from_storage<F>(val: F::Raw, width: usize) -> F
where F: Floating {
F::from_bits(val << (bits_of::<F>() - width))
}
```
This implements truncation in the least-significant bits, where floating-point
numbers store disposable bits in the mantissa, rather than in the
most-significant bits which contain the sign, exponent, and most significant
portion of the mantissa.
[`BitSlice`]: crate::slice::BitSlice
[`.load()`]: Self::load
[`.store()`]: Self::store
+15
View File
@@ -0,0 +1,15 @@
# `Lsb0` Bit-Field Behavior
`BitField` has no requirements about the in-memory representation or layout of
stored integers within a bit-slice, only that round-tripping an integer through
a store and a load of the same element suffix on the same bit-slice is
idempotent (with respect to sign truncation).
`Lsb0` provides a contiguous translation from bit-index to real memory: for any
given bit index `n` and its position `P(n)`, `P(n + 1)` is `P(n) + 1`. This
allows it to provide batched behavior: since the section of contiguous indices
used within an element translates to a section of contiguous bits in real
memory, the transaction is always a single shift/mask operation.
Each implemented method contains documentation and examples showing exactly how
the abstract integer space is mapped to real memory.
@@ -0,0 +1,77 @@
# `Lsb0` Big-Endian Integer Loading
This implementation uses the `Lsb0` bit-ordering to determine *which* bits in a
partially-occupied memory element contain the contents of an integer to be
loaded, using big-endian element ordering.
See the [trait method definition][orig] for an overview of what element ordering
means.
## Signed-Integer Loading
As described in the trait definition, when loading as a signed integer, the most
significant bit *loaded* from memory is sign-extended to the full width of the
returned type. In this method, that means that the most-significant bit of the
first element.
## Examples
In each memory element, the `Lsb0` ordering counts indices leftward from the
right edge:
```rust
use bitvec::prelude::*;
let raw = 0b00_10110_0u8;
// 76 54321 0
// ^ sign bit
assert_eq!(
raw.view_bits::<Lsb0>()
[1 .. 6]
.load_be::<u8>(),
0b000_10110,
);
assert_eq!(
raw.view_bits::<Lsb0>()
[1 .. 6]
.load_be::<i8>(),
0b111_10110u8 as i8,
);
```
In bit-slices that span multiple elements, the big-endian element ordering means
that the slice index increases while numeric significance decreases:
```rust
use bitvec::prelude::*;
let raw = [
0b0010_1111u8,
// ^ sign bit
// 7 0
0x0_1u8,
// 15 8
0xF_8u8,
// 23 16
];
assert_eq!(
raw.view_bits::<Lsb0>()
[4 .. 20]
.load_be::<u16>(),
0x2018u16,
);
```
Note that while these examples use `u8` storage for convenience in displaying
the literals, `BitField` operates identically with *any* storage type. As most
machines use little-endian *byte ordering* within wider element types, and
`bitvec` exclusively operates on *elements*, the actual bytes of memory may
rapidly start to behave oddly when translating between numeric literals and
in-memory representation.
The [user guide] has a chapter that translates bit indices into memory positions
for each combination of `<T: BitStore, O: BitOrder>`, and may be of additional
use when choosing a combination of type parameters and load functions.
[orig]: crate::field::BitField::load_le
[user guide]: https://bitvecto-rs.github.io/bitvec/memory-layout
@@ -0,0 +1,77 @@
# `Lsb0` Little-Endian Integer Loading
This implementation uses the `Lsb0` bit-ordering to determine *which* bits in a
partially-occupied memory element contain the contents of an integer to be
loaded, using little-endian element ordering.
See the [trait method definition][orig] for an overview of what element ordering
means.
## Signed-Integer Loading
As described in the trait definition, when loading as a signed integer, the most
significant bit *loaded* from memory is sign-extended to the full width of the
returned type. In this method, that means the most-significant loaded bit of the
final element.
## Examples
In each memory element, the `Lsb0` ordering counts indices leftward from the
right edge:
```rust
use bitvec::prelude::*;
let raw = 0b00_10110_0u8;
// 76 54321 0
// ^ sign bit
assert_eq!(
raw.view_bits::<Lsb0>()
[1 .. 6]
.load_le::<u8>(),
0b000_10110,
);
assert_eq!(
raw.view_bits::<Lsb0>()
[1 .. 6]
.load_le::<i8>(),
0b111_10110u8 as i8,
);
```
In bit-slices that span multiple elements, the little-endian element ordering
means that the slice index increases with numerical significance:
```rust
use bitvec::prelude::*;
let raw = [
0x8_Fu8,
// 7 0
0x0_1u8,
// 15 8
0b1111_0010u8,
// ^ sign bit
// 23 16
];
assert_eq!(
raw.view_bits::<Lsb0>()
[4 .. 20]
.load_le::<u16>(),
0x2018u16,
);
```
Note that while these examples use `u8` storage for convenience in displaying
the literals, `BitField` operates identically with *any* storage type. As most
machines use little-endian *byte ordering* within wider element types, and
`bitvec` exclusively operates on *elements*, the actual bytes of memory may
rapidly start to behave oddly when translating between numeric literals and
in-memory representation.
The [user guide] has a chapter that translates bit indices into memory positions
for each combination of `<T: BitStore, O: BitOrder>`, and may be of additional
use when choosing a combination of type parameters and load functions.
[orig]: crate::field::BitField::load_le
[user guide]: https://bitvecto-rs.github.io/bitvec/memory-layout
@@ -0,0 +1,69 @@
# `Lsb0` Big-Endian Integer Storing
This implementation uses the `Lsb0` bit-ordering to determine *which* bits in a
partially-occupied memory element are used for storage, using big-endian element
ordering.
See the [trait method definition][orig] for an overview of what element ordering
means.
## Narrowing Behavior
Integers are truncated from the high end. When storing into a bit-slice of
length `n`, the `n` least numerically significant bits are stored, and any
remaining high bits are ignored.
Be aware of this behavior if you are storing signed integers! The signed integer
`-14i8` (bit pattern `0b1111_0010u8`) will, when stored into and loaded back
from a 4-bit slice, become the value `2i8`.
## Examples
```rust
use bitvec::prelude::*;
let mut raw = 0u8;
raw.view_bits_mut::<Lsb0>()
[1 .. 6]
.store_be(22u8);
assert_eq!(raw, 0b00_10110_0);
// 76 54321 0
raw.view_bits_mut::<Lsb0>()
[1 .. 6]
.store_be(-10i8);
assert_eq!(raw, 0b00_10110_0);
```
In bit-slices that span multiple elements, the big-endian element ordering means
that the slice index increases while numerical significance decreases:
```rust
use bitvec::prelude::*;
let mut raw = [!0u8; 3];
raw.view_bits_mut::<Lsb0>()
[4 .. 20]
.store_be(0x2018u16);
assert_eq!(raw, [
0x2_F,
// 7 0
0x0_1,
// 15 8
0xF_8,
// 23 16
]);
```
Note that while these examples use `u8` storage for convenience in displaying
the literals, `BitField` operates identically with *any* storage type. As most
machines use little-endian *byte ordering* within wider element types, and
`bitvec` exclusively operates on *elements*, the actual bytes of memory may
rapidly start to behave oddly when translating between numeric literals and
in-memory representation.
The [user guide] has a chapter that translates bit indices into memory positions
for each combination of `<T: BitStore, O: BitOrder>`, and may be of additional
use when choosing a combination of type parameters and store functions.
[orig]: crate::field::BitField::store_be
[user guide]: https://bitvecto-rs.github.io/bitvec/memory-layout
@@ -0,0 +1,69 @@
# `Lsb0` Little-Endian Integer Storing
This implementation uses the `Lsb0` bit-ordering to determine *which* bits in a
partially-occupied memory element are used for storage, using little-endian
element ordering.
See the [trait method definition][orig] for an overview of what element ordering
means.
## Narrowing Behavior
Integers are truncated from the high end. When storing into a bit-slice of
length `n`, the `n` least numerically significant bits are stored, and any
remaining high bits are ignored.
Be aware of this behavior if you are storing signed integers! The signed integer
`-14i8` (bit pattern `0b1111_0010u8`) will, when stored into and loaded back
from a 4-bit slice, become the value `2i8`.
## Examples
```rust
use bitvec::prelude::*;
let mut raw = 0u8;
raw.view_bits_mut::<Lsb0>()
[1 .. 6]
.store_le(22u8);
assert_eq!(raw, 0b00_10110_0);
// 76 54321 0
raw.view_bits_mut::<Lsb0>()
[1 .. 6]
.store_le(-10i8);
assert_eq!(raw, 0b00_10110_0);
```
In bit-slices that span multiple elements, the little-endian element ordering
means that the slice index increases with numerical significance:
```rust
use bitvec::prelude::*;
let mut raw = [!0u8; 3];
raw.view_bits_mut::<Lsb0>()
[4 .. 20]
.store_le(0x2018u16);
assert_eq!(raw, [
0x8_F,
// 7 0
0x0_1,
// 15 8
0xF_2,
// 23 16
]);
```
Note that while these examples use `u8` storage for convenience in displaying
the literals, `BitField` operates identically with *any* storage type. As most
machines use little-endian *byte ordering* within wider element types, and
`bitvec` exclusively operates on *elements*, the actual bytes of memory may
rapidly start to behave oddly when translating between numeric literals and
in-memory representation.
The [user guide] has a chapter that translates bit indices into memory positions
for each combination of `<T: BitStore, O: BitOrder>`, and may be of additional
use when choosing a combination of type parameters and store functions.
[orig]: crate::field::BitField::store_le
[user guide]: https://bitvecto-rs.github.io/bitvec/memory-layout
+23
View File
@@ -0,0 +1,23 @@
# `Msb0` Bit-Field Behavior
`BitField` has no requirements about the in-memory representation or layout of
stored integers within a bit-slice, only that round-tripping an integer through
a store and a load of the same element suffix on the same bit-slice is
idempotent (with respect to sign truncation).
`Msb0` provides a contiguous translation from bit-index to real memory: for any
given bit index `n` and its position `P(n)`, `P(n + 1)` is `P(n) - 1`. This
allows it to provide batched behavior: since the section of contiguous indices
used within an element translates to a section of contiguous bits in real
memory, the transaction is always a single shift-mask operation.
Each implemented method contains documentation and examples showing exactly how
the abstract integer space is mapped to real memory.
## Notes
In particular, note that while `Msb0` indexes bits from the most significant
down to the least, and integers index from the least up to the most, this
**does not** reörder any bits of the integer value! This ordering only finds a
region in real memory; it does *not* affect the partial-integer contents stored
in that region.
@@ -0,0 +1,77 @@
# `Msb0` Big-Endian Integer Loading
This implementation uses the `Msb0` bit-ordering to determine *which* bits in a
partially-occupied memory element contain the contents of an integer to be
loaded, using big-endian element ordering.
See the [trait method definition][orig] for an overview of what element ordering
means.
## Signed-Integer Loading
As described in the trait definition, when loading as a signed integer, the most
significant bit *loaded* from memory is sign-extended to the full width of the
returned type. In this method, that means the most-significant loaded bit of the
first element.
## Examples
In each memory element, the `Msb0` ordering counts indices rightward from the
left edge:
```rust
use bitvec::prelude::*;
let raw = 0b00_10110_0u8;
// 01 23456 7
// ^ sign bit
assert_eq!(
raw.view_bits::<Msb0>()
[2 .. 7]
.load_be::<u8>(),
0b000_10110,
);
assert_eq!(
raw.view_bits::<Msb0>()
[2 .. 7]
.load_be::<i8>(),
0b111_10110u8 as i8,
);
```
In bit-slices that span multiple elements, the big-endian element ordering means
that the slice index increases with numerical significance:
```rust
use bitvec::prelude::*;
let raw = [
0b1111_0010u8,
// ^ sign bit
// 0 7
0x0_1u8,
// 8 15
0x8_Fu8,
// 16 23
];
assert_eq!(
raw.view_bits::<Msb0>()
[4 .. 20]
.load_be::<u16>(),
0x2018u16,
);
```
Note that while these examples use `u8` storage for convenience in displaying
the literals, `BitField` operates identically with *any* storage type. As most
machines use little-endian *byte ordering* within wider element types, and
`bitvec` exclusively operates on *elements*, the actual bytes of memory may
rapidly start to behave oddly when translating between numeric literals and
in-memory representation.
The [user guide] has a chapter that translates bit indices into memory positions
for each combination of `<T: BitStore, O: BitOrder>`, and may be of additional
use when choosing a combination of type parameters and load functions.
[orig]: crate::field::BitField::load_le
[user guide]: https://bitvecto-rs.github.io/bitvec/memory-layout
@@ -0,0 +1,77 @@
# `Msb0` Little-Endian Integer Loading
This implementation uses the `Msb0` bit-ordering to determine *which* bits in a
partially-occupied memory element contain the contents of an integer to be
loaded, using little-endian element ordering.
See the [trait method definition][orig] for an overview of what element ordering
means.
## Signed-Integer Loading
As described in the trait definition, when loading as a signed integer, the most
significant bit *loaded* from memory is sign-extended to the full width of the
returned type. In this method, that means the most-significant loaded bit of the
final element.
## Examples
In each memory element, the `Msb0` ordering counts indices rightward from the
left edge:
```rust
use bitvec::prelude::*;
let raw = 0b00_10110_0u8;
// 01 23456 7
// ^ sign bit
assert_eq!(
raw.view_bits::<Msb0>()
[2 .. 7]
.load_le::<u8>(),
0b000_10110,
);
assert_eq!(
raw.view_bits::<Msb0>()
[2 .. 7]
.load_le::<i8>(),
0b111_10110u8 as i8,
);
```
In bit-slices that span multiple elements, the little-endian element ordering
means that the slice index increases with numerical significance:
```rust
use bitvec::prelude::*;
let raw = [
0xF_8u8,
// 0 7
0x0_1u8,
// 8 15
0b0010_1111u8,
// ^ sign bit
// 16 23
];
assert_eq!(
raw.view_bits::<Msb0>()
[4 .. 20]
.load_le::<u16>(),
0x2018u16,
);
```
Note that while these examples use `u8` storage for convenience in displaying
the literals, `BitField` operates identically with *any* storage type. As most
machines use little-endian *byte ordering* within wider element types, and
`bitvec` exclusively operates on *elements*, the actual bytes of memory may
rapidly start to behave oddly when translating between numeric literals and
in-memory representation.
The [user guide] has a chapter that translates bit indices into memory positions
for each combination of `<T: BitStore, O: BitOrder>`, and may be of additional
use when choosing a combination of type parameters and load functions.
[orig]: crate::field::BitField::load_le
[user guide]: https://bitvecto-rs.github.io/bitvec/memory-layout
@@ -0,0 +1,69 @@
# `Msb0` Big-Endian Integer Storing
This implementation uses the `Msb0` bit-ordering to determine *which* bits in a
partially-occupied memory element are used for storage, using big-endian element
ordering.
See the [trait method definition][orig] for an overview of what element ordering
means.
## Narrowing Behavior
Integers are truncated from the high end. When storing into a bit-slice of
length `n`, the `n` least numerically significant bits are stored, and any
remaining high bits are ignored.
Be aware of this behavior if you are storing signed integers! The signed integer
`-14i8` (bit pattern `0b1111_0010u8`) will, when stored into and loaded back
from a 4-bit slice, become the value `2i8`.
## Examples
```rust
use bitvec::prelude::*;
let mut raw = 0u8;
raw.view_bits_mut::<Msb0>()
[2 .. 7]
.store_be(22u8);
assert_eq!(raw, 0b00_10110_0);
// 01 23456 7
raw.view_bits_mut::<Msb0>()
[2 .. 7]
.store_be(-10i8);
assert_eq!(raw, 0b00_10110_0);
```
In bit-slices that span multiple elements, the big-endian element ordering means
that the slice index increases while numerical significance decreases:
```rust
use bitvec::prelude::*;
let mut raw = [!0u8; 3];
raw.view_bits_mut::<Msb0>()
[4 .. 20]
.store_be(0x2018u16);
assert_eq!(raw, [
0xF_2,
// 0 7
0x0_1,
// 8 15
0x8_F,
// 16 23
]);
```
Note that while these examples use `u8` storage for convenience in displaying
the literals, `BitField` operates identically with *any* storage type. As most
machines use little-endian *byte ordering* within wider element types, and
`bitvec` exclusively operates on *elements*, the actual bytes of memory may
rapidly start to behave oddly when translating between numeric literals and
in-memory representation.
The [user guide] has a chapter that translates bit indices into memory positions
for each combination of `<T: BitStore, O: BitOrder>`, and may be of additional
use when choosing a combination of type parameters and store functions.
[orig]: crate::field::BitField::store_be
[user guide]: https://bitvecto-rs.github.io/bitvec/memory-layout
@@ -0,0 +1,69 @@
# `Msb0` Little-Endian Integer Storing
This implementation uses the `Msb0` bit-ordering to determine *which* bits in a
partially-occupied memory element are used for storage, using little-endian
element ordering.
See the [trait method definition][orig] for an overview of what element ordering
means.
## Narrowing Behavior
Integers are truncated from the high end. When storing into a bit-slice of
length `n`, the `n` least numerically significant bits are stored, and any
remaining high bits are ignored.
Be aware of this behavior if you are storing signed integers! The signed integer
`-14i8` (bit pattern `0b1111_0010u8`) will, when stored into and loaded back
from a 4-bit slice, become the value `2i8`.
## Examples
```rust
use bitvec::prelude::*;
let mut raw = 0u8;
raw.view_bits_mut::<Msb0>()
[2 .. 7]
.store_le(22u8);
assert_eq!(raw, 0b00_10110_0);
// 01 23456 7
raw.view_bits_mut::<Msb0>()
[2 .. 7]
.store_le(-10i8);
assert_eq!(raw, 0b00_10110_0);
```
In bit-slices that span multiple elements, the little-endian element ordering
means that the slice index increases with numerical significance:
```rust
use bitvec::prelude::*;
let mut raw = [!0u8; 3];
raw.view_bits_mut::<Msb0>()
[4 .. 20]
.store_le(0x2018u16);
assert_eq!(raw, [
0xF_8,
// 0 7
0x0_1,
// 8 15
0x2_F,
// 16 23
]);
```
Note that while these examples use `u8` storage for convenience in displaying
the literals, `BitField` operates identically with *any* storage type. As most
machines use little-endian *byte ordering* within wider element types, and
`bitvec` exclusively operates on *elements*, the actual bytes of memory may
rapidly start to behave oddly when translating between numeric literals and
in-memory representation.
The [user guide] has a chapter that translates bit indices into memory positions
for each combination of `<T: BitStore, O: BitOrder>`, and may be of additional
use when choosing a combination of type parameters and store functions.
[orig]: crate::field::BitField::store_le
[user guide]: https://bitvecto-rs.github.io/bitvec/memory-layout
+58
View File
@@ -0,0 +1,58 @@
# Integer Loading
This method reads the contents of a bit-slice region as an integer. The region
may be shorter than the destination integer type, in which case the loaded value
will be zero-extended (when `I: Unsigned`) or sign-extended from the most
significant loaded bit (when `I: Signed`).
The region may not be zero bits, nor wider than the destination type. Attempting
to load a `u32` from a bit-slice of length 33 will panic the program.
## Operation and Endianness Handling
Each element in the bit-slice contains a segment of the value to be loaded. If
the bit-slice contains more than one element, then the numerical significance of
each loaded segment is interpreted according to the targets endianness:
- little-endian targets consider each *`T` element* to have increasing numerical
significance, starting with the least-significant segment at the low address
and ending with the most-significant segment at the high address.
- big-endian targets consider each *`T` element* to have decreasing numerical
significance, starting with the most-significant segment at the high address
and ending with the least-significant segment at the low address.
See the documentation for [`.load_le()`] and [`.load_be()`] for more detail on
what this means for how the in-memory representation of bit-slices translates to
loaded values.
You must always use the loading method that exactly corresponds to the storing
method previously used to insert data into the bit-slice: same suffix on the
method name (none, `_le`, `_be`) and same integer type. `bitvec` is not required
to, and will not, guarantee round-trip consistency if you change any of these
parameters.
## Type Parameters
- `I`: The integer type being loaded. This can be any of the signed or unsigned
integers.
## Parameters
- `&self`: A bit-slice region whose length is in the range `1 ..= I::BITS`.
## Returns
The contents of the bit-slice, interpreted as an integer.
## Panics
This panics if `self.len()` is 0, or greater than `I::BITS`.
## Examples
This method is inherently non-portable, and changes behavior depending on the
target characteristics. If your target is little-endian, see [`.load_le()`]; if
your target is big-endian, see [`.load_be()`].
[`.load_be()`]: Self::load_be
[`.load_le()`]: Self::load_le
@@ -0,0 +1,145 @@
# Big-Endian Integer Loading
This method loads an integer value from a bit-slice, using big-endian
significance ordering when the bit-slice spans more than one `T` element in
memory.
Big-endian significance ordering means that if a bit-slice occupies an array
`[A, B, C]`, then the bits stored in `A` are considered to be the most
significant segment of the loaded integer, then `B` contains the middle segment,
then `C` contains the least significant segment.
The segments are combined in order, that is, as the raw bit-pattern
`0b<padding><A><B><C>`. If the destination type is signed, the loaded value is
sign-extended according to the most-significant bit in the `A` segment.
It is important to note that the `O: BitOrder` parameter of the bit-slice from
which the value is loaded **does not** affect the bit-pattern of the stored
segments. They are always stored exactly as they exist in an ordinary integer.
The ordering parameter only affects *which* bits in an element are available for
storage.
## Type Parameters
- `I`: The integer type being loaded. This can be any of the signed or unsigned
integers.
## Parameters
- `&self`: A bit-slice region whose length is in the range `1 ..= I::BITS`.
## Returns
The contents of the bit-slice, interpreted as an integer.
## Panics
This panics if `self.len()` is 0, or greater than `I::BITS`.
## Examples
Let us consider an `i32` value stored in 24 bits of a `BitSlice<u8, Lsb0>`:
```rust
use bitvec::prelude::*;
let mut raw = [0u8; 4];
let bits = raw.view_bits_mut::<Lsb0>();
let integer = 0x00__B4_96_3Cu32 as i32;
bits[4 .. 28].store_be::<i32>(integer);
let loaded = bits[4 .. 28].load_be::<i32>();
assert_eq!(loaded, 0xFF__B4_96_3Cu32 as i32);
```
Observe that, because the lowest 24 bits began with the pattern `0b1101…`, the
value was considered to be negative when interpreted as an `i24` and was
sign-extended through the highest byte.
Let us now look at the memory representation of this value:
```rust
# use bitvec::prelude::*;
# let mut raw = [0u8; 4];
# let bits = raw.view_bits_mut::<Lsb0>();
# bits[4 .. 28].store_be::<u32>(0x00B4963Cu32);
assert_eq!(raw, [
0b1011_0000,
// 0xB dead
0b0100_1001,
// 0x4 0x9
0b0110_0011,
// 0x6 0x3
0b0000_1100,
// dead 0xC
]);
```
Notice how while the `Lsb0` bit-ordering means that indexing within the
bit-slice proceeds right-to-left in each element, the actual bit-patterns stored
in memory are not affected. Element `[0]` is more numerically significant than
element `[1]`, but bit `[4]` is not more numerically significant than bit `[5]`.
In the sequence `B496`, `B` is the most significant, and so it gets placed
lowest in memory. `49` fits in one byte, and is stored directly as written.
Lastly, `6` is the least significant nibble of the four, and is placed highest
in memory.
Now lets look at the way different `BitOrder` parameters interpret the
placement of bit indices within memory:
```rust
use bitvec::prelude::*;
let raw = [
// Bit index 14 ←
// Lsb0: ─┤
0b0100_0000_0000_0011u16,
// Msb0: ├─
// → 14
// Bit index ← 19 16
// Lsb0: ├──┤
0b0001_0000_0000_1110u16,
// Msb0: ├──┤
// 16 19 →
];
assert_eq!(
raw.view_bits::<Lsb0>()
[14 .. 20]
.load_be::<u8>(),
0b00_01_1110,
);
assert_eq!(
raw.view_bits::<Msb0>()
[14 .. 20]
.load_be::<u8>(),
0b00_11_0001,
);
```
Notice how the bit-orderings change which *parts* of the memory are loaded, but
in both cases the segment in `raw[0]` is more significant than the segment in
`raw[1]`, and the ordering of bits *within* each segment are unaffected by the
bit-ordering.
## Notes
Be sure to see the documentation for
[`<BitSlice<_, Lsb0> as BitField>::load_be`][llb] and
[`<BitSlice<_, Msb0> as Bitfield>::load_be`][mlb] for more detailed information
on the memory views!
You can view the mask of all *storage regions* of a bit-slice by using its
[`.domain()`] method to view the breakdown of its memory region, then print the
[`.mask()`] of any [`PartialElement`] the domain contains. Whole elements are
always used in their entirety. You should use the `domain` modules types
whenever you are uncertain of the exact locations in memory that a particular
bit-slice governs.
[llb]: https://docs.rs/bitvec/latest/bitvec/field/trait.BitField.html#method.load_be-3
[mlb]: https://docs.rs/bitvec/latest/bitvec/field/trait.BitField.html#method.load_be-4
[`PartialElement`]: crate::domain::PartialElement
[`.domain()`]: crate::slice::BitSlice::domain
[`.mask()`]: crate::domain::PartialElement::mask
@@ -0,0 +1,145 @@
# Little-Endian Integer Loading
This method loads an integer value from a bit-slice, using little-endian
significance ordering when the bit-slice spans more than one `T` element in
memory.
Little-endian significance ordering means that if a bit-slice occupies an array
`[A, B, C]`, then the bits stored in `A` are considered to contain the least
significant segment of the loaded integer, then `B` contains the middle segment,
and then `C` contains the most significant segment.
The segments are combined in order, that is, as the raw bit-pattern
`0b<padding><C><B><A>`. If the destination type is signed, the loaded value is
sign-extended according to the most-significant bit in the `C` segment.
It is important to note that the `O: BitOrder` parameter of the bit-slice from
which the value is loaded **does not** affect the bit-pattern of the stored
segments. They are always stored exactly as they exist in an ordinary integer.
The ordering parameter only affects *which* bits in an element are available for
storage.
## Type Parameters
- `I`: The integer type being loaded. This can be any of the signed or unsigned
integers.
## Parameters
- `&self`: A bit-slice region whose length is in the range `1 ..= I::BITS`.
## Returns
The contents of the bit-slice, interpreted as an integer.
## Panics
This panics if `self.len()` is 0, or greater than `I::BITS`.
## Examples
Let us consider an `i32` value stored in 24 bits of a `BitSlice<u8, Msb0>`:
```rust
use bitvec::prelude::*;
let mut raw = [0u8; 4];
let bits = raw.view_bits_mut::<Msb0>();
let integer = 0x00__B4_96_3Cu32 as i32;
bits[4 .. 28].store_le::<i32>(integer);
let loaded = bits[4 .. 28].load_le::<i32>();
assert_eq!(loaded, 0xFF__B4_96_3Cu32 as i32);
```
Observe that, because the lowest 24 bits began with the pattern `0b1101…`, the
value was considered to be negative when interpreted as an `i24` and was
sign-extended through the highest byte.
Let us now look at the memory representation of this value:
```rust
# use bitvec::prelude::*;
# let mut raw = [0u8; 4];
# let bits = raw.view_bits_mut::<Msb0>();
# bits[4 .. 28].store_le::<u32>(0x00B4963Cu32);
assert_eq!(raw, [
0b0000_1100,
// dead 0xC
0b0110_0011,
// 0x6 0x3
0b0100_1001,
// 0x4 0x9
0b1011_0000,
// 0xB dead
]);
```
Notice how while the `Msb0` bit-ordering means that indexing within the
bit-slice proceeds left-to-right in each element, and the bit-patterns in each
element proceed left-to-right in the aggregate and the decomposed literals, the
ordering of the elements is reversed from how the literal was written.
In the sequence `B496`, `B` is the most significant, and so it gets placed
highest in memory. `49` fits in one byte, and is stored directly as written.
Lastly, `6` is the least significant nibble of the four, and is placed lowest
in memory.
Now lets look at the way different `BitOrder` parameters interpret the
placement of bit indices within memory:
```rust
use bitvec::prelude::*;
let raw = [
// Bit index 14 ←
// Lsb0: ─┤
0b0100_0000_0000_0011u16,
// Msb0: ├─
// → 14
// Bit index ← 19 16
// Lsb0: ├──┤
0b0001_0000_0000_1110u16,
// Msb0: ├──┤
// 16 19 →
];
assert_eq!(
raw.view_bits::<Lsb0>()
[14 .. 20]
.load_le::<u8>(),
0b00_1110_01,
);
assert_eq!(
raw.view_bits::<Msb0>()
[14 .. 20]
.load_le::<u8>(),
0b00_0001_11,
);
```
Notice how the bit-orderings change which *parts* of the memory are loaded, but
in both cases the segment in `raw[0]` is less significant than the segment in
`raw[1]`, and the ordering of bits *within* each segment are unaffected by the
bit-ordering.
## Notes
Be sure to see the documentation for
[`<BitSlice<_, Lsb0> as BitField>::load_le`][lll] and
[`<BitSlice<_, Msb0> as Bitfield>::load_le`][mll] for more detailed information
on the memory views!
You can view the mask of all *storage regions* of a bit-slice by using its
[`.domain()`] method to view the breakdown of its memory region, then print the
[`.mask()`] of any [`PartialElement`] the domain contains. Whole elements are
always used in their entirety. You should use the `domain` modules types
whenever you are uncertain of the exact locations in memory that a particular
bit-slice governs.
[lll]: https://docs.rs/bitvec/latest/bitvec/field/trait.BitField.html#method.load_le-3
[mll]: https://docs.rs/bitvec/latest/bitvec/field/trait.BitField.html#method.load_le-4
[`PartialElement`]: crate::domain::PartialElement
[`.domain()`]: crate::slice::BitSlice::domain
[`.mask()`]: crate::domain::PartialElement::mask
+57
View File
@@ -0,0 +1,57 @@
# Integer Storing
This method writes an integer into the contents of a bit-slice region. The
region may be shorter than the source integer type, in which case the stored
value will be truncated. On load, it may be zero-extended (unsigned destination)
or sign-extended from the most significant **stored** bit (signed destination).
The region may not be zero bits, nor wider than the source type. Attempting
to store a `u32` into a bit-slice of length 33 will panic the program.
## Operation and Endianness Handling
The value to be stored is broken into segments according to the elements of the
bit-slice receiving it. If the bit-slice contains more than one element, then
the numerical significance of each segment routes to a storage element according
to the targets endianness:
- little-endian targets consider each *`T` element* to have increasing numerical
significance, starting with the least-significant segment at the low address
and ending with the most-significant segment at the high address.
- big-endian targets consider each *`T` element* to have decreasing numerical
significance, starting with the most-significant segment at the high address
and ending with the least-significant segment at the low address.
See the documentation for [`.store_le()`] and [`.store_be()`] for more detail on
what this means for how the in-memory representation of bit-slices translates to
stored values.
You must always use the loading method that exactly corresponds to the storing
method previously used to insert data into the bit-slice: same suffix on the
method name (none, `_le`, `_be`) and same integer type. `bitvec` is not required
to, and will not, guarantee round-trip consistency if you change any of these
parameters.
## Type Parameters
- `I`: The integer type being stored. This can be any of the signed or unsigned
integers.
## Parameters
- `&self`: A bit-slice region whose length is in the range `1 ..= I::BITS`.
- `value`: An integer value whose `self.len()` least numerically significant
bits will be written into `self`.
## Panics
This panics if `self.len()` is 0, or greater than `I::BITS`.
## Examples
This method is inherently non-portable, and changes behavior depending on the
target characteristics. If your target is little-endian, see [`.store_le()`]; if
your target is big-endian, see [`.store_be()`].
[`.store_be()`]: Self::store_be
[`.store_le()`]: Self::store_le
@@ -0,0 +1,143 @@
# Big-Endian Integer Storing
This method stores an integer value into a bit-slice, using big-endian
significance ordering when the bit-slice spans more than one `T` element in
memory.
Big-endian significance ordering means that if a bit-slice occupies an array
`[A, B, C]`, then the bits stored in `A` are considered to contain the most
significant segment of the stored integer, then `B` contains the middle segment,
and then `C` contains the least significant segment.
An integer is broken into segments in order, that is, the raw bit-pattern is
fractured into `0b<padding><A><B><C>`. High bits beyond the length of the
bit-slice into which the integer is stored are truncated.
It is important to note that the `O: BitOrder` parameter of the bit-slice into
which the value is stored **does not** affect the bit-pattern of the stored
segments. They are always stored exactly as they exist in an ordinary integer.
The ordering parameter only affects *which* bits in an element are available for
storage.
## Type Parameters
- `I`: The integer type being stored. This can be any of the signed or unsigned
integers.
## Parameters
- `&mut self`: A bit-slice region whose length is in the range `1 ..= I::BITS`.
- `value`: An integer value whose `self.len()` least numerically significant
bits will be written into `self`.
## Panics
This panics if `self.len()` is 0, or greater than `I::BITS`.
## Examples
Let us consider an `i32` value stored in 24 bits of a `BitSlice<u8, Lsb0>`:
```rust
use bitvec::prelude::*;
let mut raw = [0u8; 4];
let bits = raw.view_bits_mut::<Lsb0>();
let integer = 0x00__B4_96_3Cu32 as i32;
bits[4 .. 28].store_be::<i32>(integer);
let loaded = bits[4 .. 28].load_be::<i32>();
assert_eq!(loaded, 0xFF__B4_96_3Cu32 as i32);
```
Observe that, because the lowest 24 bits began with the pattern `0b1101…`, the
value was considered to be negative when interpreted as an `i24` and was
sign-extended through the highest byte.
Let us now look at the memory representation of this value:
```rust
# use bitvec::prelude::*;
# let mut raw = [0u8; 4];
# let bits = raw.view_bits_mut::<Lsb0>();
# bits[4 .. 28].store_be::<u32>(0x00B4963Cu32);
assert_eq!(raw, [
0b1011_0000,
// 0xB dead
0b0100_1001,
// 0x4 0x9
0b0110_0011,
// 0x6 0x3
0b0000_1100,
// dead 0xC
]);
```
Notice how while the `Lsb0` bit-ordering means that indexing within the
bit-slice proceeds right-to-left in each element, the actual bit-patterns stored
in memory are not affected. Element `[0]` is more numerically significant than
element `[1]`, but bit `[4]` is not more numerically significant than bit `[5]`.
In the sequence `B496`, `B` is the most significant, and so it gets placed
lowest in memory. `49` fits in one byte, and is stored directly as written.
Lastly, `6` is the least significant nibble of the four, and is placed highest
in memory.
Now lets look at the way different `BitOrder` parameters interpret the
placement of bit indices within memory:
```rust
use bitvec::prelude::*;
let raw = [
// Bit index 14 ←
// Lsb0: ─┤
0b0100_0000_0000_0011u16,
// Msb0: ├─
// → 14
// Bit index ← 19 16
// Lsb0: ├──┤
0b0001_0000_0000_1110u16,
// Msb0: ├──┤
// 16 19 →
];
assert_eq!(
raw.view_bits::<Lsb0>()
[14 .. 20]
.load_be::<u8>(),
0b00_01_1110,
);
assert_eq!(
raw.view_bits::<Msb0>()
[14 .. 20]
.load_be::<u8>(),
0b00_11_0001,
);
```
Notice how the bit-orderings change which *parts* of the memory are loaded, but
in both cases the segment in `raw[0]` is more significant than the segment in
`raw[1]`, and the ordering of bits *within* each segment are unaffected by the
bit-ordering.
## Notes
Be sure to see the documentation for
[`<BitSlice<_, Lsb0> as BitField>::store_be`][lsb] and
[`<BitSlice<_, Msb0> as Bitfield>::store_be`][msb] for more detailed information
on the memory views!
You can view the mask of all *storage regions* of a bit-slice by using its
[`.domain()`] method to view the breakdown of its memory region, then print the
[`.mask()`] of any [`PartialElement`] the domain contains. Whole elements are
always used in their entirety. You should use the `domain` modules types
whenever you are uncertain of the exact locations in memory that a particular
bit-slice governs.
[lsb]: https://docs.rs/bitvec/latest/bitvec/field/trait.BitField.html#method.store_be-3
[msb]: https://docs.rs/bitvec/latest/bitvec/field/trait.BitField.html#method.store_be-4
[`PartialElement`]: crate::domain::PartialElement
[`.domain()`]: crate::slice::BitSlice::domain
[`.mask()`]: crate::domain::PartialElement::mask
@@ -0,0 +1,143 @@
# Little-Endian Integer Storing
This method stores an integer value into a bit-slice, using little-endian
significance ordering when the bit-slice spans more than one `T` element in
memory.
Little-endian significance ordering means that if a bit-slice occupies an array
`[A, B, C]`, then the bits stored in `A` are considered to contain the least
significant segment of the stored integer, then `B` contains the middle segment,
and then `C` contains the most significant segment.
An integer is broken into segments in order, that is, the raw bit-pattern is
fractured into `0b<padding><C><B><A>`. High bits beyond the length of the
bit-slice into which the integer is stored are truncated.
It is important to note that the `O: BitOrder` parameter of the bit-slice into
which the value is stored **does not** affect the bit-pattern of the stored
segments. They are always stored exactly as they exist in an ordinary integer.
The ordering parameter only affects *which* bits in an element are available for
storage.
## Type Parameters
- `I`: The integer type being stored. This can be any of the signed or unsigned
integers.
## Parameters
- `&mut self`: A bit-slice region whose length is in the range `1 ..= I::BITS`.
- `value`: An integer value whose `self.len()` least numerically significant
bits will be written into `self`.
## Panics
This panics if `self.len()` is 0, or greater than `I::BITS`.
## Examples
Let us consider an `i32` value stored in 24 bits of a `BitSlice<u8, Msb0>`:
```rust
use bitvec::prelude::*;
let mut raw = [0u8; 4];
let bits = raw.view_bits_mut::<Msb0>();
let integer = 0x00__B4_96_3Cu32 as i32;
bits[4 .. 28].store_le::<i32>(integer);
let loaded = bits[4 .. 28].load_le::<i32>();
assert_eq!(loaded, 0xFF__B4_96_3Cu32 as i32);
```
Observe that, because the lowest 24 bits began with the pattern `0b1101…`, the
value was considered to be negative when interpreted as an `i24` and was
sign-extended through the highest byte.
Let us now look at the memory representation of this value:
```rust
# use bitvec::prelude::*;
# let mut raw = [0u8; 4];
# let bits = raw.view_bits_mut::<Msb0>();
# bits[4 .. 28].store_le::<u32>(0x00B4963Cu32);
assert_eq!(raw, [
0b0000_1100,
// dead 0xC
0b0110_0011,
// 0x6 0x3
0b0100_1001,
// 0x4 0x9
0b1011_0000,
// 0xB dead
]);
```
Notice how while the `Msb0` bit-ordering means that indexing within the
bit-slice proceeds left-to-right in each element, and the bit-patterns in each
element proceed left-to-right in the aggregate and the decomposed literals, the
ordering of the elements is reversed from how the literal was written.
In the sequence `B496`, `B` is the most significant, and so it gets placed
highest in memory. `49` fits in one byte, and is stored directly as written.
Lastly, `6` is the least significant nibble of the four, and is placed lowest
in memory.
Now lets look at the way different `BitOrder` parameters interpret the
placement of bit indices within memory:
```rust
use bitvec::prelude::*;
let raw = [
// Bit index 14 ←
// Lsb0: ─┤
0b0100_0000_0000_0011u16,
// Msb0: ├─
// → 14
// Bit index ← 19 16
// Lsb0: ├──┤
0b0001_0000_0000_1110u16,
// Msb0: ├──┤
// 16 19 →
];
assert_eq!(
raw.view_bits::<Lsb0>()
[14 .. 20]
.load_le::<u8>(),
0b00_1110_01,
);
assert_eq!(
raw.view_bits::<Msb0>()
[14 .. 20]
.load_le::<u8>(),
0b00_0001_11,
);
```
Notice how the bit-orderings change which *parts* of the memory are loaded, but
in both cases the segment in `raw[0]` is less significant than the segment in
`raw[1]`, and the ordering of bits *within* each segment are unaffected by the
bit-ordering.
## Notes
Be sure to see the documentation for
[`<BitSlice<_, Lsb0> as BitField>::store_le`][lsl] and
[`<BitSlice<_, Msb0> as Bitfield>::store_le`][msl] for more detailed information
on the memory views!
You can view the mask of all *storage regions* of a bit-slice by using its
[`.domain()`] method to view the breakdown of its memory region, then print the
[`.mask()`] of any [`PartialElement`] the domain contains. Whole elements are
always used in their entirety. You should use the `domain` modules types
whenever you are uncertain of the exact locations in memory that a particular
bit-slice governs.
[lsl]: https://docs.rs/bitvec/latest/bitvec/field/trait.BitField.html#method.store_le-3
[msl]: https://docs.rs/bitvec/latest/bitvec/field/trait.BitField.html#method.store_le-4
[`PartialElement`]: crate::domain::PartialElement
[`.domain()`]: crate::slice::BitSlice::domain
[`.mask()`]: crate::domain::PartialElement::mask
+26
View File
@@ -0,0 +1,26 @@
# Partial-Element Getter
This function extracts a portion of an integer value from a [`PartialElement`].
The `BitField` implementations call it as they assemble a complete integer. It
performs the following steps:
1. the `PartialElement` is loaded (and masked to discard unused bits),
1. the loaded value is then shifted to abut the LSedge of the stack local,
1. and then `resize`d into a `U` value.
## Type Parameters
- `O` and `T` are the type parameters of the `PartialElement` argument.
- `U` is the destination integer type.
## Parameters
- `elem`: A `PartialElement` containing a value segment.
- `shamt`: The distance by which to right-shift the value loaded from `elem` so
that it abuts the LSedge.
## Returns
The segment of an integer stored in `elem`.
[`PartialElement`]: crate::domain::PartialElement
@@ -0,0 +1,9 @@
# Bit-Array Implementation of `BitField`
The `BitArray` implementation is only ever called when the entire bit-array is
available for use, which means it can skip the bit-slice memory detection and
instead use the underlying storage elements directly.
The implementation still performs the segmentation for each element contained in
the array, in order to maintain value consistency so that viewing the array as a
bit-slice is still able to correctly interact with data contained in it.
+10
View File
@@ -0,0 +1,10 @@
# Bit-Field I/O Protocols
This module defines the standard-library `io::{Read, Write}` byte-oriented
protocols on `bitvec` structures that are capable of operating on bytes through
the `BitField` trait.
Note that calling [`BitField`] methods in a loop imposes a non-trivial, and
irremovable, performance penalty on each invocation. The `.read()` and
`.write()` methods implemented in this module are going to suffer this cost, and
you should prefer to operate directly on the underlying buffer if possible.
@@ -0,0 +1,20 @@
# Reading From a Bit-Slice
The implementation loads bytes out of the referenced bit-slice until either the
destination buffer is filled or the source has no more bytes to provide. When
`.read()` returns, the provided bit-slice handle will have been updated to no
longer include the leading segment copied out as bytes into `buf`.
Note that the return value of `.read()` is always the number of *bytes* of `buf`
filled!
The implementation uses [`BitField::load_be`] to collect bytes. Note that unlike
the standard library, it is implemented on bit-slices of *any* underlying
element type. However, using a `BitSlice<_, u8>` is still likely to be fastest.
## Original
[`impl Read for [u8]`][orig]
[orig]: https://doc.rust-lang.org/std/primitive.slice.html#impl-Read
[`BitField::load_be`]: crate::field::BitField::load_be
+14
View File
@@ -0,0 +1,14 @@
# Reading From a Bit-Vector
The implementation loads bytes out of the reference bit-vector until either the
destination buffer is filled or the source has no more bytes to provide. When
`.read()` returns, the provided bit-vector will have its contents shifted down
so that it begins at the first bit *after* the last byte copied out into `buf`.
Note that the return value of `.read()` is always the number of *bytes* of `buf`
filled!
## API Differences
The standard library does not `impl Read for Vec<u8>`. It is provided here as a
courtesy.
@@ -0,0 +1,20 @@
# Writing Into a Bit-Slice
The implementation stores bytes into the referenced bit-slice until either the
source buffer is exhausted or the destination has no more slots to fill. When
`.write()` returns, the provided bit-slice handle will have been updated to no
longer include the leading segment filled with bytes from `buf`.
Note that the return value of `.write()` is always the number of *bytes* of
`buf` consumed!
The implementation uses [`BitField::store_be`] to fill bytes. Note that unlike
the standard library, it is implemented on bit-slices of *any* underlying
element type. However, using a `BitSlice<_, u8>` is still likely to be fastest.
## Original
[`impl Write for [u8]`][orig]
[orig]: https://doc.rust-lang.org/std/primitive.slice.html#impl-Write
[`BitField::store_be`]: crate::field::BitField::store_be
@@ -0,0 +1,18 @@
# Writing Into a Bit-Vector
The implementation appends bytes to the referenced bit-vector until the source
buffer is exhausted.
Note that the return value of `.write()` is always the number of *bytes* of
`buf` consumed!
The implementation uses [`BitField::store_be`] to fill bytes. Note that unlike
the standard library, it is implemented on bit-vectors of *any* underlying
element type. However, using a `BitVec<_, u8>` is still likely to be fastest.
## Original
[`impl Write for Vec<u8>`][orig]
[orig]: https://doc.rust-lang.org/std/vec/struct.Vec.html#impl-Write
[`BitField::store_be`]: crate::field::BitField::store_be
+18
View File
@@ -0,0 +1,18 @@
# Value Resizing
This zero-extends or truncates a source value to fit into a target type.
## Type Parameters
- `T`: The initial integer type of the value being resized.
- `U`: The destination type of the value after resizing.
## Parameters
- `value`: Any (unsigned) integer.
## Returns
`value`, either zero-extended in the most-significant bits (if `U` is wider than
`T`) or truncated retaining the least-significant bits (if `U` is narrower than
`T`).
+25
View File
@@ -0,0 +1,25 @@
# Partial-Element Setter
This function inserts a portion of an integer value into a [`PartialElement`].
The `BitField` implementations call it as they disassemble a complete integer.
It performs the following steps:
1. the value is `resize`d into a `T::Mem`,
1. shifted up from LSedge as needed to fit in the governed region of the partial
element,
1. and then stored (after masking away excess bits) through the `PartialElement`
into memory.
## Type Parameters
- `O` and `T` are the type parameters of the `PartialElement` argument.
- `U` is the source integer type.
## Parameters
- `elem`: A `PartialElement` into which a value segment will be written.
- `value`: A value, whose least-significant bits will be written into `elem`.
- `shamt`: The shift distance from the storage locations LSedge to its live
bits.
[`PartialElement`]: crate::domain::PartialElement
+30
View File
@@ -0,0 +1,30 @@
# Sign Extension
When a bit-slice loads a value whose destination type is wider than the
bit-slice itself, and the destination type is a signed integer, the loaded value
must be sign-extended. The load accumulator always begins as the zero pattern,
and the loaders do not attempt to detect a sign bit before they begin.
As such, this function takes a value loaded out of a bit-slice, which has been
zero-extended from the storage length to the destination type, and the length of
the bit-slice that contained it. If the destination type is unsigned, then the
value is returned as-is; if the destination type is signed, then the value is
sign-extended according to the bit at `1 << (width - 1)`.
## Type Parameters
- `I`: The integer type of the loaded element. When this is one of
`u{8,16,32,64,size}`, no sign extension takes place.
## Parameters
- `elem`: The value loaded out of a bit-slice.
- `width`: The width in bits of the source bit-slice. This is always known to be
in the domain `1 ..= I::BITS`.
## Returns
A correctly-signed copy of `elem`. Unsigned integers, and signed integers whose
most significant loaded bit was `0`, are untouched. Signed integers whose most
significant loaded bit was `1` have their remaining high bits set to `1` for
sign extension.
+45
View File
@@ -0,0 +1,45 @@
# Bit Indices
This module provides well-typed counters for working with bit-storage registers.
The session types encode a strict chain of custody for translating semantic
indices within [`BitSlice`] regions into real effects in memory.
The main advantage of types within this module is that they provide
register-dependent range requirements for counter values, making it impossible
to have an index out of bounds for a register. They also create a sequence of
type transformations that assure the library about the continued validity of
each value in its surrounding context.
By eliminating public constructors from arbitrary integers, `bitvec` can
guarantee that only it can produce initial values, and only trusted functions
can transform their numeric values or types, until the program reaches the
property that it requires. This chain of assurance means that memory operations
can be confident in the correctness of their actions and effects.
## Type Sequence
The library produces [`BitIdx`] values from region computation. These types
cannot be publicly constructed, and are only ever the result of pointer
analysis. As such, they rely on the correctness of the memory regions provided
to library entry points, and those entry points can leverage the Rust type
system to ensure safety there.
`BitIdx` is transformed to [`BitPos`] through the [`BitOrder`] trait. The
[`order`] module provides verification functions that implementors can use to
demonstrate correctness. `BitPos` is the basis type that describes memory
operations, and is used to create the selection masks [`BitSel`] and
[`BitMask`].
## Usage
The types in this module should only be used by client crates in their test
suites. They have no other purpose, and conjuring values for them is potentially
memory-unsafe.
[`BitIdx`]: self::BitIdx
[`BitMask`]: self::BitMask
[`BitOrder`]: crate::order::BitOrder
[`BitPos`]: self::BitPos
[`BitSel`]: self::BitSel
[`BitSlice`]: crate::slice::BitSlice
[`order`]: crate::order
+35
View File
@@ -0,0 +1,35 @@
# One-Bit-After Tail Index
This is a semantic bit-index within *or one bit after* an `R` register. It is
the index of the first “dead” bit after a “live” region, and corresponds to the
similar half-open range concept in the Rust `Range` type or the LLVM memory
model, pointer values include the address one object past the end of a region.
It is a counter in the ring `0 ..= R::BITS` (note the inclusive high end). Like
[`BitIdx`], this is a virtual semantic index with no bearing on real memory
effects; unlike `BitIdx`, it can never be translated to real memory because it
does not describe real memory.
This type is necessary in order to preserve the distinction between a dead
memory address that is *not* part of a region and a live memory address that
*is* within a region. Additionally, it makes computation of region extension or
offsets easy. `BitIdx` is insufficient to this task, and produces off-by-one
errors when used in its stead.
## Type Parameters
- `R`: The register element that this dead-bit index governs.
## Validity
Values of this type are **required** to be in the range `0 ..= R::BITS`. Any
value greater than [`R::BITS`] makes the program invalid and will likely cause
either a crash or incorrect memory access.
## Construction
This type cannot be publicly constructed except by using the iterators provided
for testing.
[`BitIdx`]: crate::index::BitIdx
[`R::BITS`]: funty::Integral::BITS
+31
View File
@@ -0,0 +1,31 @@
# Semantic Bit Index
This type is a counter in the ring `0 .. R::BITS` and serves to mark a semantic
index within some register element. It is a virtual index, and is the stored
value used in pointer encodings to track region start information.
It is translated to a real index through the [`BitOrder`] trait. This virtual
index is the only counter that can be used for address computation, and once
lowered to an electrical index through [`BitOrder::at`], the electrical address
can only be used for setting up machine instructions.
## Type Parameters
- `R`: The register element that this index governs.
## Validity
Values of this type are **required** to be in the range `0 .. R::BITS`. Any
value not less than [`R::BITS`] makes the program invalid, and will likely cause
either a crash or incorrect memory access.
## Construction
This type can never be constructed outside of the `bitvec` crate. It is passed
in to [`BitOrder`] implementations, which may use it to construct electrical
position values from it. All values of this type constructed by `bitvec` are
known to be correct in their region; no other construction site can be trusted.
[`BitOrder`]: crate::order::BitOrder
[`BitOrder::at`]: crate::order::BitOrder::at
[`R::BITS`]: funty::Integral::BITS
+6
View File
@@ -0,0 +1,6 @@
# Bit Index Error
This type marks that a value is out of range to be used as an index within an
`R` element. It is likely never produced, as `bitvec` does not construct invalid
indices, but is provided for completeness and to ensure that in the event of
this error occurring, the diagnostic information is useful.
+19
View File
@@ -0,0 +1,19 @@
# Multi-Bit Selection Mask
Unlike [`BitSel`], which enforces a strict one-hot mask encoding, this type
permits any number of bits to be set or cleared. This is used to accumulate
selections for batched operations on a register in real memory.
## Type Parameters
- `R`: The register element that this mask governs.
## Construction
This must only be constructed by combining `BitSel` selection masks produced
through the accepted chains of custody beginning with [`BitIdx`] values.
Bit-masks not constructed in this manner are not guaranteed to be correct in the
callers context and may lead to incorrect memory behaviors.
[`BitIdx`]: crate::index::BitIdx
[`BitSel`]: crate::index::BitSel
+29
View File
@@ -0,0 +1,29 @@
# Bit Position
This is a position counter of a real bit in an `R` memory element.
Like [`BitIdx`], it is a counter in the ring `0 .. R::BITS`. It marks a real bit
in memory, and is the shift distance in the expression `1 << n`. It can only be
produced by applying [`BitOrder::at`] to an existing `BitIdx` produced by
`bitvec`.
## Type Parameters
- `R`: The register element that this position governs.
## Validity
Values of this type are **required** to be in the range `0 .. R::BITS`. Any
value not less than [`R::BITS`] makes the program invalid, and will likely cause
either a crash or incorrect memory access.
## Construction
This type is publicly constructible, but is only correct to do so within an
implementation of `BitOrder::at`. `bitvec` will only request its creation
through that trait implementation, and has no sites that can publicly accept
untrusted values.
[`BitIdx`]: crate::index::BitIdx
[`BitOrder::at`]: crate::order::BitOrder::at
[`R::BITS`]: funty::Integral::BITS
+28
View File
@@ -0,0 +1,28 @@
# One-Hot Bit Selection Mask
This type selects exactly one bit in a register. It is a [`BitPos`] shifted from
a counter to a selector, and is used to apply test and write operations to real
memory.
## Type Parameters
- `R`: The register element this selector governs.
## Validity
Values of this type are **required** to have exactly one bit set and all others
cleared. Any other value makes the program incorrect, and will cause memory
corruption.
## Construction
This type is only constructed from `BitPos`, and is always equivalent to
`1 << BitPos`.
The chain of custody from known-good [`BitIdx`] values, through proven-good
[`BitOrder`] implementations, into `BitPos` and then `BitSel` proves that values
of this type are always correct to apply to real memory.
[`BitIdx`]: crate::index::BitIdx
[`BitOrder`]: crate::order::BitOrder
[`BitPos`]: crate::index::BitPos
+31
View File
@@ -0,0 +1,31 @@
# Constructor Macros
This module provides macros that can be used to create `bitvec` data buffers at
compile time. Each data structure has a corresponding macro:
- `BitSlice` has [`bits!`]
- `BitArray` has [`bitarr!`] (and [`BitArr!`] to produce type expressions)
- `BitBox` has [`bitbox!`]
- `BitVec` has [`bitvec!`]
These macros take a sequence of bit literals, as well as some optional control
prefixes, and expand to code that is generally solvable at compile-time. The
provided bit-orderings `Lsb0` and `Msb0` have implementations that can be used
in `const` contexts, while third-party user-provided orderings cannot be used in
`const` contexts but almost certainly *can* be const-folded by LLVM.
The sequences are encoded into element literals during compilation, and will be
correctly encoded into the target binary. This is even true for targets with
differing byte-endianness than the host compiler.
See each macro for documentation on its invocation syntax. The general pattern
is `[modifier] [T, O;] bits…`. The modifiers influence the nature of the
produced binding, the `[T, O;]` pair provides type parameters when the default
is undesirable, and the `bits…` provides the actual contents of the data
buffer.
[`BitArr!`]: macro@crate::BitArr
[`bitarr!`]: macro@crate::bitarr
[`bitbox!`]: macro@crate::bitbox
[`bits!`]: macro@crate::bits
[`bitvec!`]: macro@crate::bitvec
+35
View File
@@ -0,0 +1,35 @@
# Bit-Array Type Definition
Because `BitArray<T, O, const BITS: usize>` is not expressible in stable Rust,
this macro serves the purpose of creating a type definition that expands to a
suitable `BitArray`. It creates the correct, rounded-up, `BitArray` to hold a
requested number of bits in a requested set of ordering/storage parameters.
The macro takes a minimum number of bits to store, and an optional set of
bit-order and bit-store type names, and creates a `BitArray` that satisfies the
request. As this macro is only usable in type position, it is named with
`PascalCase` rather than `snake_case`.
## Examples
You must provide a bit-count; you may optionally provide a storage type, or a
bit-ordering *and* a storage type, as subsequent arguments. When elided, the
type parameters are set to the crate defaut type parameters of `Lsb0` and
`usize`.
```rust
use bitvec::prelude::*;
use core::cell::Cell;
let a: BitArr!(for 100) = BitArray::ZERO;
let b: BitArr!(for 100, in u32) = BitArray::<_>::ZERO;
let c: BitArr!(for 100, in Cell<u16>, Msb0) = BitArray::<_, _>::ZERO;
```
The length expression must be `const`. It may be a literal, a named `const`
item, or a `const` expression, as long as it evaluates to a `usize`. The type
arguments have no restrictions, as long as they are in-scope at the invocation
site and are implementors of [`BitOrder`] and [`BitStore`].
[`BitOrder`]: crate::order::BitOrder
[`BitStore`]: crate::store::BitStore
+61
View File
@@ -0,0 +1,61 @@
# Bit-Array Value Constructor
This macro provides a bit-initializer syntax for [`BitArray`] values. It takes a
superset of the [`vec!`] arguments, and is capable of producing bit-arrays in
`const` contexts (for known type parameters).
Like `vec!`, it can accept a sequence of comma-separated bit values, or a
semicolon-separated pair of a bit value and a repetition counter. Bit values may
be any integer or name of a `const` integer, but *should* only be `0` or `1`.
## Argument Syntax
It accepts zero, one, or three prefix arguments:
- `const`: If the first argument to the macro is the keyword `const`, separated
from remaining arguments by a space, then the macro expands to a
`const`-expression that can be used in any appropriate context (initializing
a `static`, a `const`, or passed to a `const fn`). This only works when the
bit-ordering argument is either implicit, or one of the three tokens that
`bitvec` can recognize.
- `$order ,`: When this is one of the three literal tokens `LocalBits`, `Lsb0`,
or `Msb0`, then the macro is able to compute the encoded bit-array contents at
compile time, including in `const` contexts. When it is anything else, the
encoding must take place at runtime. The name or path chosen must be in scope
at the macro invocation site.
When not provided, this defaults to `Lsb0`.
- `$store ;`: This must be one of `uTYPE`, `Cell<uTYPE>`, `AtomicUTYPE`, or
`RadiumUTYPE` where `TYPE` is one of `8`, `16`, `32`, `64`, or `size`. The
macro recognizes this token textually, and does not have access to the type
system resolver, so it will not accept aliases or qualified paths.
When not provided, this defaults to `usize`.
The `const` argument can be present or absent independently of the
type-parameter pair. The pair must be either both absent or both present
together.
> Previous versions of `bitvec` supported `$order`-only arguments. This has been
> removed for clarity of use and ease of implementation.
## Examples
```rust
use bitvec::prelude::*;
use core::{cell::Cell, mem};
use radium::types::*;
let a: BitArray = bitarr![0, 1, 0, 0, 1];
let b: BitArray = bitarr![1; 5];
assert_eq!(b.len(), mem::size_of::<usize>() * 8);
let c = bitarr![u16, Lsb0; 0, 1, 0, 0, 1];
let d = bitarr![Cell<u16>, Msb0; 1; 10];
const E: BitArray<[u32; 1], LocalBits> = bitarr![u32, LocalBits; 1; 15];
let f = bitarr![RadiumU32, Msb0; 1; 20];
```
[`BitArray`]: crate::array::BitArray
[`vec!`]: macro@alloc::vec
+10
View File
@@ -0,0 +1,10 @@
# Boxed Bit-Slice Constructor
This macro creates encoded `BitSlice` buffers at compile-time, and at run-time
copies them directly into a new heap allocation.
It forwards all of its arguments to [`bitvec!`], and calls
[`BitVec::into_boxed_bitslice`] on the produced `BitVec`.
[`BitVec::into_boxed_bitslice`]: crate::vec::BitVec::into_boxed_bitslice
[`bitvec!`]: macro@crate::bitvec
+102
View File
@@ -0,0 +1,102 @@
# Bit-Slice Region Constructor
This macro provides a bit-initializer syntax for [`BitSlice`] reference values.
It takes a superset of the [`vec!`] arguments, and is capable of producing
bit-slices in `const` contexts (for known type parameters).
Like `vec!`, it can accept a sequence of comma-separated bit values, or a
semicolon-separated pair of a bit value and a repetition counter. Bit values may
be any integer or name of a `const` integer, but *should* only be `0` or `1`.
## Argument Syntax
It accepts two modifier prefixes, zero or two type parameters, and the bit
expressions described above.
The modifier prefixes are separated from the remaining arguments by clearspace.
- `static`: If the first argument is the keyword `static`, then this produces a
`&'static BitSlice` reference bound into a (hidden, unnameable)
`static BitArray` item. If not, then it produces a stack temporary that the
Rust compiler automatically extends to have the lifetime of the returned
reference. Note that non-`static` invocations rely on the compilers escape
analysis, and you should typically not try to move them up the call stack.
- `mut`: If the first argument is the keyword `mut`, then this produces a `&mut`
writable `BitSlice`.
- `static mut`: These can be combined to create a `&'static mut BitSlice`. It is
always safe to use this reference, because the `static mut BitArray` it
creates is concealed and unreachable by any other codepath, and so the
produced reference is always the sole handle that can reach it.
The next possible arguments are a pair of `BitOrder`/`BitStore` type parameters.
- `$order ,`: When this is one of the three literal tokens `LocalBits`, `Lsb0`,
or `Msb0`, then the macro is able to compute the encoded bit-array contents at
compile time, including in `const` contexts. When it is anything else, the
encoding must take place at runtime. The name or path chosen must be in scope
at the macro invocation site.
When not provided, this defaults to `Lsb0`.
- `$store ;`: This must be one of `uTYPE`, `Cell<uTYPE>`, `AtomicUTYPE`, or
`RadiumUTYPE` where `TYPE` is one of `8`, `16`, `32`, `64`, or `size`. The
macro recognizes this token textually, and does not have access to the type
system resolver, so it will not accept aliases or qualified paths.
When not provided, this defaults to `usize`.
The `static`/`mut` modifiers may be individually present or absent independently
of the type-parameter pair. The pair must be either both absent or both present
together.
> Previous versions of `bitvec` supported $order`-only arguments. This has been
> removed for clarity of use and ease of implementation.
## Safety
Rust considers all `static mut` bindings to be `unsafe` to use. While `bits!`
can prevent *some* of this unsafety by preventing direct access to the created
`static mut` buffer, there are still ways to create multiple names referring to
the same underlying buffer.
```rust,ignore
use bitvec::prelude::*;
fn unsound() -> &'static mut BitSlice<usize, Lsb0> {
unsafe { bits![static mut 0; 64] }
}
let a = unsound();
let b = unsound();
```
The two names `a` and `b` can be used to produce aliasing `&mut [usize]`
references.
**You must not invoke `bits![static mut …]` in a context where it can be used**
**to create multiple escaping names**. This, and only this, argument combination
of the macro produces a value that requires a call-site `unsafe` block to use.
If you do not use this behavior to create multiple names over the same
underlying buffer, then the macros expansion is safe to use, as `bitvec`s
existing alias-protection behavior suffices.
## Examples
```rust
use bitvec::prelude::*;
use core::cell::Cell;
use radium::types::*;
let a: &BitSlice = bits![0, 1, 0, 0, 1];
let b: &BitSlice = bits![1; 5];
assert_eq!(b.len(), 5);
let c = bits![u16, Lsb0; 0, 1, 0, 0, 1];
let d = bits![static Cell<u16>, Msb0; 1; 10];
let e = unsafe { bits![static mut u32, LocalBits; 0; 15] };
let f = bits![RadiumU32, Msb0; 1; 20];
```
[`BitSlice`]: crate::slice::BitSlice
[`vec!`]: macro@alloc::vec
+12
View File
@@ -0,0 +1,12 @@
# Bit-Vector Constructor
This macro creates encoded `BitSlice` buffers at compile-time, and at run-time
copies them directly into a new heap allocation.
It forwards all of its arguments to [`bits!`], and calls
[`BitVec::from_bitslice`] on the produced `&BitSlice` expression. While you can
use the `bits!` modifiers, there is no point, as the produced bit-slice is lost
before the macro exits.
[`BitVec::from_bitslice`]: crate::vec::BitVec::from_bitslice
[`bits!`]: macro@crate::bits
+62
View File
@@ -0,0 +1,62 @@
# Bit-Sequence Buffer Encoding
This macro accepts a sequence of bit expressions from the public macros and
creates encoded `[T; N]` arrays from them. The public macros can then use these
encoded arrays as the basis of the requested data structure.
This is a complex macro that uses recursion to modify and inspect its input
tokens. It is divided into three major sections.
## Entry Points
The first section provides a series of entry points that the public macros
invoke. Each arm matches the syntax provided by public macros, and detects a
specific `BitStore` implementor name: `uN`, `Cell<uN>`, `AtomicUN`, or
`RadiumUN`, for each `N` in `8`, `16`, `32`, `64`, and `size`.
These arms then recurse, adding a token for the raw unsigned integer used as the
basis of the encoding. The `usize` arms take an additional recursion that routes
to the 32-bit or 64-bit encoding, depending on the target.
## Zero Extension
The next two arms handle extending the list of bit-expressions with 64 `0,`s.
The first arm captures initial reëntry and appends the zero-comma tokens, then
recurses to enter the chunking group. The second arm traps when recursion has
chunked all user-provided tokens, and only the literal `0,` tokens appended by
the first arm remain.
The second arm dispatches the chunked bit-expressions into the element encoder,
and is the exit point of the macro. Its output is an array of encoded memory
elements, typed as the initially-requested `BitStore` name.
The `0,` tokens remain matchable as text literals because they never depart
this macro: recursion within the same macro does not change the types in the
AST, while invoking a new macro causes already-known tokens to become opacified
into `:tt` whose contents cannot be matched. This is the reason that the macro
is recursive rather than dispatching.
## Chunking
The stream of user-provided bit-expressions, followed by the appended zero-comma
tokens, is divided into chunks by the width of the storage type.
Each width (8, 16, 32, 64) has an arm that munches from the token stream and
grows an opaque token-list containing munched groups. In syntax, this is
represented by the `[$([$($bit:tt,)+],)*];` cluster:
- it is an array
- of zero or more arrays
- of one or more bit expressions
- each followed by a comma
- each followed by a comma
- followed by a semicolon
By placing this array ahead of the bit-expression stream, we can use the array
as an append-only list (matched as `[$($elem:tt)*]`, emitted as
`[$($elem)* [new]]`) grown by munching from the token stream of unknown length
at the end of the argument set.
On each recursion, the second arm in zero-extension attempts to trap the input.
If it fails, then user-provided tokens remain; if it succeeds, then it discards
any remaining macro-appended zeros and terminates.
+6
View File
@@ -0,0 +1,6 @@
# Internal Macro Implementations
The contents of this module are required to be publicly reachable from external
crates, because that is the context in which the public macros expand; however,
the contents of this module are **not** public API and `bitvec` does not support
any use of it other than within the public macros.
+21
View File
@@ -0,0 +1,21 @@
# Element Encoder Macro
This macro is invoked by `__encode_bits!` with a set of bits that exactly fills
some `BitStore` element type. It is responsible for encoding those bits into the
raw memory bytes and assembling them into a whole integer.
It works by inspecting the `$order` argument. If it is one of `LocalBits`,
`Lsb0`, or `Msb0`, then it can do the construction in-place, and get solved
during `const` evaluation. If it is any other ordering, then it emits runtime
code to do the translation and defers to the optimizer for evaluation.
It divides the input into clusters of eight bit expressions, then uses the
`$order` argument to choose whether the bits are accumulated into a `u8` using
`Lsb0`, `Msb0`, or `LocalBits` ordering. The accumulated byte array is then
converted into an integer using the corresponding `uN::from_{b,l,n}e_bytes`
function in `__ty_from_bytes!`.
Once assembled, the raw integer is changed into the requested final type. This
currently routes through a helper type that unifies `const fn` constructors for
each of the raw integer fundamentals, cells, and atomics in order to avoid
transmutes.
+11
View File
@@ -0,0 +1,11 @@
# Memory Element Descriptions
This module describes the memory integers and processor registers used to hold
and manipulate `bitvec` data buffers.
The [`BitRegister`] trait marks the unsigned integers that correspond to
processor registers, and can therefore be used for buffer control. The integers
that are not `BitRegister` can be composed from register values, but are not
able to be used in buffer type parameters.
[`BitRegister`]: self::BitRegister
+17
View File
@@ -0,0 +1,17 @@
# Unified Element Constructor
This type is a hack around the fact that `Cell` and `AtomicUN` all have
`const fn new(val: Inner) -> Self;` constructors, but the numberic fundamentals
do not. As such, the standard library does not provide a unified construction
syntax to turn an integer fundamental into the final type.
This provides a `const fn BitElement::<_>::new(R) -> Self;` function,
implemented only for the `BitStore` implementors that the crate provides, that
the constructor macros can use to turn integers into final values without using
[`mem::transmute`]. While `transmute` is acceptable in this case (the types are
all `#[repr(transparent)]`), it is still better avoided where possible.
As this is a macro assistant, it is publicly exposed, but is not public API. It
has no purpose outside of the crates macros.
[`mem::transmute`]: core::mem::transmute.
+6
View File
@@ -0,0 +1,6 @@
# Register Descriptions
This trait describes the unsigned integer types that can be manipulated in a
target processors general-purpose registers. It has no bearing on the processor
instructions or registers used to interact with the memory bus, and solely
exists to describe integers that can exist on a system.
+28
View File
@@ -0,0 +1,28 @@
# Bit Storage Calculator
Computes the number of `T` elements required to store some number of bits. `T`
must be an unsigned integer type and cannot have padding bits, but this
restriction cannot be placed on `const fn`s yet.
## Parameters
- `bits`: The number of bits being stored in a `[T]` array.
## Returns
A minimal `N` in `[T; N]` that is not less than `bits`.
As this is a `const` function, when `bits` is also a `const` expression, it can
be used to compute the size of an array type, such as
`[u32; elts::<u32>(BITS)]`.
## Examples
```rust
use bitvec::mem as bv_mem;
assert_eq!(bv_mem::elts::<u8>(10), 2);
assert_eq!(bv_mem::elts::<u8>(16), 2);
let arr: [u16; bv_mem::elts::<u16>(20)] = [0; 2];
```
+24
View File
@@ -0,0 +1,24 @@
# In-Element Bit Ordering
The `bitvec` memory model is designed to separate the semantic ordering of bits
in an abstract memory space from the electrical ordering of latches in real
memory. This module provides the bridge between the two domains with the
[`BitOrder`] trait and implementations of it.
The `BitOrder` trait bridges semantic indices (marked by the [`BitIdx`] type) to
electrical position counters (morked by the [`BitPos`] type) or selection masks
(marked by the [`BitSel`] and [`BitMask`] types).
Because `BitOrder` is open for client crates to implement, this module also
provides verification functions for the test suite that ensure a given
`BitOrder` implementation is correct for all the register types that it will
govern. See the [`verify_for_type`] or [`verify`] functions for more
information.
[`BitIdx`]: crate::index::BitIdx
[`BitMask`]: crate::index::BitMask
[`BitOrder`]: self::BitOrder
[`BitPos`]: crate::index::BitPos
[`BitSel`]: crate::index::BitSel
[`verify`]: self::verify
[`verify_for_type`]: self::verify_for_type
+92
View File
@@ -0,0 +1,92 @@
# In-Element Bit Ordering
This trait manages the translation of semantic bit indices into electrical
positions within storage elements of a memory region.
## Usage
`bitvec` APIs operate on semantic index counters that exist in an abstract
memory space independently of the real memory that underlies them. In order to
affect real memory, `bitvec` must translate these indices into real values. The
[`at`] function maps abstract index values into their corresponding real
positions that can then be used to access memory.
You will likely never call any of the trait functions yourself. They are used by
`bitvec` internals to operate on memory regions; all you need to do is provide
an implementation of this trait as a type parameter to `bitvec` data structures.
## Safety
`BitOrder` is unsafe to implement because its translation of index to position
cannot be forcibly checked by `bitvec` itself, and an improper implementation
will lead to memory unsafety errors and unexpected collisions. The trait has
strict requirements for each function. If these are not upheld, then the
implementation is considered undefined at the library level and its use may
produce incorrect or undefined behavior during compilation.
You are responsible for running [`verify_for_type`] or [`verify`] in your test
suite if you implement `BitOrder`.
## Implementation Rules
Values of this type are never constructed or passed to `bitvec` functions. Your
implementation does not need to be zero-sized, but it will never have access to
an instance to view its state. It *may* refer to other global state, but per the
rules of `at`, that state may not change while any `bitvec` data structures are
alive.
The only function you *need* to provide is `at`. Its requirements are listed in
its trait documentation.
You *may* also choose to provide implementations of `select` and `mask`. These
have a default implementation that is correct, but may be unoptimized for your
implementation. As such, you may replace them with a better version, but your
implementation of these functions must be exactly equal to the default
implementation for all possible inputs.
This requirement is checked by the `verify_for_type` function.
## Verification
The `verify_for_type` function verifies that a `BitOrder` implementation is
correct for a single `BitStore` implementor, and the `verify` function runs
`verify_for_type` on all unsigned integers that implement `BitStore` on a
target. If you run these functions in your test suite, they will provide
detailed information if your implementation is incorrect.
## Examples
Implementations are not required to remain contiguous over a register, and may
have any mapping they wish as long as it is total and bijective. This example
swizzles the high and low halves of each byte.
```rust
use bitvec::{
order::BitOrder,
index::{BitIdx, BitPos},
mem::BitRegister,
};
pub struct HiLo;
unsafe impl BitOrder for HiLo {
fn at<R>(index: BitIdx<R>) -> BitPos<R>
where R: BitRegister {
unsafe { BitPos::new_unchecked(index.into_inner() ^ 4) }
}
}
#[test]
#[cfg(test)]
fn prove_hilo() {
bitvec::order::verify::<HiLo>();
}
```
Once a `BitOrder` implementation passes the test suite, it can be freely used as
a type parameter in `bitvec` data structures. The translation takes place
automatically, and you never need to look at this trait again.
[`at`]: Self::at
[`verify`]: crate::order::verify
[`verify_for_type`]: crate::order::verify_for_type
+23
View File
@@ -0,0 +1,23 @@
# C-Compatible Bit Ordering
This type alias attempts to match the bitfield ordering used by GCC on your
target. The C standard permits ordering of single-bit bitfields in a structure
to be implementation-defined, and GCC has been observed to use Lsb0-ordering on
little-endian processors and Msb0-ordering on big-endian processors.
This has two important caveats:
- ordering of bits in an element is **completely** independent of the ordering
of constituent bytes in memory. These have nothing to do with each other in
any way. See [the user guide][0] for more information on memory
representation.
- GCC wide bitfields on big-endian targets behave as `<T, Lsb0>` bit-slices
using the `_be` variants of `BitField` accessors. They do not match `Msb0`
bit-wise ordering.
This type is provided solely as a convenience for narrow use cases that *may*
match GCCs `std::bitset<N>`. It makes no guarantee about what C compilers for
your target actually do, and you will need to do your own investigation if you
are exchanging a single buffer across FFI in this manner.
[0]: https://bitvecto-rs.github.io/bitvec/memory-representation
+13
View File
@@ -0,0 +1,13 @@
# Least-Significant-First Bit Traversal
This type orders the bits in an element with the least significant bit first and
the most significant bit last, in contiguous order across the element.
The guide has [a chapter][0] with more detailed information on the memory
representation this produces.
This is the default type parameter used throughout the crate. If you do not have
a desired memory representation, you should continue to use it, as it provides
the best codegen for bit manipulation.
[0]: https://bitvecto-rs.github.io/bitvec/memory-representation
+13
View File
@@ -0,0 +1,13 @@
# Most-Significant-First Bit Traversal
This type orders the bits in an element with the most significant bit first and
the least significant bit last, in contiguous order across the element.
The guide has [a chapter][0] with more detailed information on the memory
representation this produces.
This type likely matches the ordering of bits you would expect to see in a
debugger, but has worse codegen than `Lsb0`, and is not encouraged if you are
not doing direct memory inspection.
[0]: https://bitvecto-rs.github.io/bitvec/memory-representation
+23
View File
@@ -0,0 +1,23 @@
# Complete `BitOrder` Verification
This function checks some [`BitOrder`] implementations behavior on each of the
[`BitRegister`] types present on the target, and reports any violation of the
rules that it detects.
## Type Parameters
- `O`: The `BitOrder` implementation being tested.
## Parameters
- `verbose`: Controls whether the test should print diagnostic information to
standard output. If this is false, then the test only prints a message on
failure; if it is true, it emits a message for every test it executes.
## Panics
This panics when it detects a violation of the `BitOrder` rules. If it returns
normally, then the implementation is correct.
[`BitOrder`]: crate::order::BitOrder
[`BitRegister`]: crate::mem::BitRegister
@@ -0,0 +1,29 @@
# Single-Type `BitOrder` Verification
This function checks some [`BitOrder`] implementations behavior on only one
[`BitRegister`] type. It can be used when a program knows that it will only use
a limited set of storage types and does not need to check against all of them.
You should prefer to use [`verify`], as `bitvec` has no means of preventing the
use of a `BitRegister` storage type that your `BitOrder` implementation does not
satisfy.
## Type Parameters
- `O`: The `BitOrder` implementation being tested.
- `R`: The `BitRegister` type for which `O` is being tested.
## Parameters
- `verbose`: Controls whether the test should print diagnostic information to
standard output. If this is false, then the test only prints a message on
failure; if it is true, then it emits a message for every test it executes.
## Panics
This panics when it detects a violation of the `BitOrder` rules. If it returns
normally, then the implementation is correct for the given `R` type.
[`BitOrder`]: crate::order::BitOrder
[`BitRegister`]: crate::mem::BitRegister
[`verify`]: crate::order::verify.
+9
View File
@@ -0,0 +1,9 @@
# Symbol Export
This module collects the general public API into a single place for bulk import,
as `use bitvec::prelude::*;`, without polluting the root namespace of the crate.
This provides all the data structure types and macros, as well as the two traits
needed to operate them as type parameters, by name. It also imports extension
traits without naming them, so that their methods are available but their trait
names are not.
+22
View File
@@ -0,0 +1,22 @@
# Raw Pointer Implementation
This provides `bitvec`-internal pointer types and a mirror of the [`core::ptr`]
module.
It contains the following types:
- [`BitPtr`] is a raw-pointer to exactly one bit.
- [`BitRef`] is a proxy reference to exactly one bit.
- `BitSpan` is the encoded form of the `*BitSlice` pointer and `&BitSlice`
reference. It is not publicly exposed, but it serves as the foundation of
`bitvec`s ability to describe memory regions.
It also provides ports of the free functions available in `core::ptr`, as well
as some utilities for bridging ordinary Rust pointers into `bitvec`.
You should generally not use the contents of this module; `BitSlice` provides
more convenience and has stronger abilities to optimize performance.
[`BitPtr`]: self::BitPtr
[`BitRef`]: self::BitRef
[`core::ptr`]: core::ptr
+61
View File
@@ -0,0 +1,61 @@
# Single-Bit Pointer
This structure defines a pointer to exactly one bit in a memory element. It is a
structure, rather than an encoding of a `*Bit` raw pointer, because it contains
more information than can be packed into such a pointer. Furthermore, it can
uphold the same requirements and guarantees that the rest of the crate demands,
whereäs a raw pointer cannot.
## Original
[`*bool`](https://doc.rust-lang.org/std/primitive.pointer.html) and
[`NonNull<bool>`](core::ptr::NonNull)
## API Differences
Since raw pointers are not sufficient in space or guarantees, and are limited by
not being marked `#[fundamental]`, this is an ordinary `struct`. Because it
cannot use the `*const`/`*mut` distinction that raw pointers and references can,
this encodes mutability in a type parameter instead.
In order to be consistent with the rest of the crate, particularly the
`*BitSlice` encoding, this enforces that all `T` element addresses are
well-aligned to `T` and non-null. While this type is used in the API as an
analogue of raw pointers, it is restricted in value to only contain the values
of valid *references* to memory, not arbitrary pointers.
## ABI Differences
This is aligned to `1`, rather than the processor word, in order to enable some
crate-internal space optimizations.
## Type Parameters
- `M`: Marks whether the pointer has mutability permissions to the referent
memory. Only `Mut` pointers can be used to create `&mut` references.
- `T`: A memory type used to select both the register width and the bus behavior
when performing memory accesses.
- `O`: The ordering of bits within a memory element.
## Usage
This structure is used as the `bitvec` equivalent to `*bool`. It is used in all
raw-pointer APIs and provides behavior to emulate raw pointers. It cannot be
directly dereferenced, as it is not a pointer; it can only be transformed back
into higher referential types, or used in functions that accept it.
These pointers can never be null or misaligned.
## Safety
Rust and LLVM **do not** have a concept of bit-level initialization yet.
Furthermore, the underlying foundational code that this type uses to manipulate
individual bits in memory relies on construction of **shared references** to
memory, which means that unlike standard pointers, the `T` element to which
`BitPtr` values point must always be **already initialized** in your program
context.
`bitvec` is not able to detect or enforce this requirement, and is currently not
able to avoid it. See [`BitAccess`] for more information.
[`BitAccess`]: crate::access::BitAccess
+36
View File
@@ -0,0 +1,36 @@
# Bit-Pointer Range
This type is equivalent in purpose, but superior in functionality, to
`Range<BitPtr<M, T, O>>`. If the standard library stabilizes [`Step`], the trait
used to drive `Range` operations, then this type will likely be destroyed in
favor of an `impl Step for BitPtr` block and use of standard ranges.
Like [`Range`], this is a half-open set where the low bit-pointer selects the
first live bit in a span and the high bit-pointer selects the first dead bit
*after* the span.
This type is not capable of inspecting provenance, and has no requirement of its
own that both bit-pointers be derived from the same provenance region. It is
safe to construct and use with any pair of bit-pointers; however, the
bit-pointers it *produces* are, necessarily, `unsafe` to use.
## Original
[`Range<*bool>`][`Range`]
## Memory Representation
[`BitPtr`] is required to be `repr(packed)` in order to satisfy the [`BitRef`]
size optimizations. In order to stay minimally sized itself, this type has no
alignment requirement, and reading either bit-pointer *may* incur a misalignment
penalty. Reads are always safe and valid; they may merely be slow.
## Type Parameters
This takes the same type parameters as `BitPtr`, as it is simply a pair of
bit-pointers with range semantics.
[`BitPtr`]: crate::ptr::BitPtr
[`BitRef`]: crate::ptr::BitRef
[`Range`]: core::ops::Range
[`Step`]: core::iter::Step
+49
View File
@@ -0,0 +1,49 @@
# Proxy Bit-Reference
This structure simulates `&/mut bool` within `BitSlice` regions. It is analogous
to the C++ type [`std::bitset<N>::reference`][0].
This type wraps a [`BitPtr`] and caches a `bool` in one of the remaining padding
bytes. It is then able to freely give out references to its cached `bool`, and
commits the cached value back to the proxied location when dropped.
## Original
This is semantically equivalent to `&'a bool` or `&'a mut bool`.
## Quirks
Because this type has both a lifetime and a destructor, it can introduce an
uncommon syntax error condition in Rust. When an expression that produces this
type is in the final expression of a block, including if that expression is used
as a condition in a `match`, `if let`, or `if`, then the compiler will attempt
to extend the drop scope of this type to the outside of the block. This causes a
lifetime mismatch error if the source region from which this proxy is produced
begins its lifetime inside the block.
If you get a compiler error that this type causes something to be dropped while
borrowed, you can end the borrow by putting any expression-ending syntax element
after the offending expression that produces this type, including a semicolon or
an item definition.
## Examples
```rust
use bitvec::prelude::*;
let bits = bits![mut 0; 2];
let (left, right) = bits.split_at_mut(1);
let mut first = left.get_mut(0).unwrap();
let second = right.get_mut(0).unwrap();
// Writing through a dereference requires a `mut` binding.
*first = true;
// Writing through the explicit method call does not.
second.commit(true);
drop(first); // Its not a reference, so NLL does not apply!
assert_eq!(bits, bits![1; 2]);
```
[0]: https://en.cppreference.com/w/cpp/utility/bitset/reference
+134
View File
@@ -0,0 +1,134 @@
# Encoded Bit-Span Descriptor
This structure is used as the actual in-memory value of `BitSlice` pointers
(including both `*{const,mut} BitSlice` and `&/mut BitSlice`). It is **not**
public API, and the encoding scheme does not support external modification.
Rust slices encode a base element address and an element count into a single
`&[T]` two-word value. `BitSpan` encodes a third value, the index of the base
bit within the base element, into unused bits of the address and length counter.
The slice reference has the ABI `(*T, usize)`, which is exactly two processor
words in size. `BitSpan` matches this ABI so that it can be cast into
`&/mut BitSlice` and used in reference-demanding APIs.
## Layout
This structure is a more complex version of the `(*const T, usize)` tuple that
Rust uses to represent slices throughout the language. It breaks the pointer and
counter fundamentals into sub-field components. Rust does not have bitfield
syntax, so the below description of the structure layout is in C++.
```cpp
template <typename T>
struct BitSpan {
uintptr_t ptr_head : __builtin_ctzll(alignof(T));
uintptr_t ptr_addr : sizeof(uintptr_T) * 8 - __builtin_ctzll(alignof(T));
size_t len_head : 3;
size_t len_bits : sizeof(size_t) * 8 - 3;
};
```
This means that the `BitSpan<O, T>` has three *logical* fields, stored in four
segments, across the two *structural* fields of the type. The widths and
placements of each segment are functions of the size of `*const T`, `usize`, and
of the alignment of the `T` referent buffer element type.
## Fields
### Base Address
The address of the base element in a memory region is stored in all but the
lowest bits of the `ptr` field. An aligned pointer to `T` will always have its
lowest log<sub>2</sub>(byte width) bits zeroed, so those bits can be used to
store other information, as long as they are erased before dereferencing the
address as a pointer to `T`.
### Head Bit Index
For any referent element type `T`, the selection of a single bit within the
element requires log<sub>2</sub>(byte width) bits to select a byte within the
element `T`, and another three bits to select a bit within the selected byte.
|Type |Alignment|Trailing Zeros|Count Bits|
|:----|--------:|-------------:|---------:|
|`u8` | 1| 0| 3|
|`u16`| 2| 1| 4|
|`u32`| 4| 2| 5|
|`u64`| 8| 3| 6|
The index of the first live bit in the base element is split to have its three
least significant bits stored in the least significant edge of the `len` field,
and its remaining bits stored in the least significant edge of the `ptr` field.
### Length Counter
All but the lowest three bits of the `len` field are used to store a counter of
live bits in the referent region. When this is zero, the region is empty.
Because it is missing three bits, a `BitSpan` has only ⅛ of the index space of
a `usize` value.
## Significant Values
The following values represent significant instances of the `BitSpan` type.
### Null Slice
The fully-zeroed slot is not a valid member of the `BitSpan<O, T>` type; it is
reserved instead as the sentinel value for `Option::<BitSpan<O, T>>::None`.
### Canonical Empty Slice
All pointers with a `bits: 0` logical field are empty. Pointers that are used to
maintain ownership of heap buffers are not permitted to erase their `addr`
field. The canonical form of the empty slice has an `addr` value of
[`NonNull::<T>::dangling()`], but all pointers to an empty region are equivalent
regardless of address.
#### Uninhabited Slices
Any empty pointer with a non-[`dangling()`] base address is considered to be an
uninhabited region. `BitSpan` never discards its address information, even as
operations may alter or erase its head-index or length values.
## Type Parameters
- `T`: The memory type of the referent region. `BitSpan<O, T>` is a specialized
`*[T]` slice pointer, and operates on memory in terms of the `T` type for
access instructions and pointer calculation.
- `O`: The ordering within the register type. The bit-ordering used within a
region colors all pointers to the region, and orderings can never mix.
## Safety
`BitSpan` values may only be constructed from pointers provided by the
surrounding program.
## Undefined Behavior
Values of this type are binary-incompatible with slice pointers. Transmutation
of these values into any other type will result in an incorrect program, and
permit the program to begin illegal or undefined behaviors. This type may never
be manipulated in any way by user code outside of the APIs it offers to this
`bitvec`; it certainly may not be seen or observed by other crates.
## Design Notes
Accessing the `.head` logical field would be faster if it inhabited the least
significant byte of `.len`, and was not partitioned into `.ptr` as well.
This implementation was chosen against in order to minimize the loss of bits in
the length counter; if user studies indicate that bit-slices do not **ever**
require more than 2<sup>24</sup> bits on 32-bit systems, this may be revisited.
The `ptr_metadata` feature, tracked in [Issue #81513], defines a trait `Pointee`
that regions such as `BitSlice` can implement and define a `Metadata` type that
carries all information other than a dereferenceable memory address. For regular
slices, this would be `impl<T> Pointee for [T] { type Metadata = usize; }`. For
`BitSlice`, it would be `(usize, BitIdx<T::Mem>)` and obviate this module
entirely. But until it stabilizes, this remains.
[Issue #81513]: https://github.com/rust-lang/rust/issues/81513
[`NonNull::<T>::dangling()`]: core::ptr::NonNull::dangling
[`dangling()`]: core::ptr::NonNull::dangling
+5
View File
@@ -0,0 +1,5 @@
# Address Value Management
This module provides utilities for working with `T: BitStore` addresses so that
the other `ptr` submodules can rely on the correctness of their values when
doing pointer encoding.
@@ -0,0 +1,30 @@
# Bit-Slice Pointer Construction
This forms a raw [`BitSlice`] pointer from a bit-pointer and a length.
## Original
[`ptr::slice_from_raw_parts`](core::ptr::slice_from_raw_parts)
## Examples
You will need to construct a `BitPtr` first; these are typically produced by
existing `BitSlice` views, or you can do so manually.
```rust
use bitvec::{
prelude::*,
index::BitIdx,
ptr as bv_ptr,
};
let data = 6u16;
let head = BitIdx::new(1).unwrap();
let ptr = BitPtr::<_, _, Lsb0>::new((&data).into(), head).unwrap();
let slice = bv_ptr::bitslice_from_raw_parts(ptr, 10);
let slice_ref = unsafe { &*slice };
assert_eq!(slice_ref.len(), 10);
assert_eq!(slice_ref, bits![1, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
```
[`BitSlice`]: crate::slice::BitSlice
@@ -0,0 +1,31 @@
# Bit-Slice Pointer Construction
This forms a raw [`BitSlice`] pointer from a bit-pointer and a length.
## Original
[`ptr::slice_from_raw_parts`](core::ptr::slice_from_raw_parts)
## Examples
You will need to construct a `BitPtr` first; these are typically produced by
existing `BitSlice` views, or you can do so manually.
```rust
use bitvec::{
prelude::*,
index::BitIdx,
ptr as bv_ptr,
};
let mut data = 6u16;
let head = BitIdx::new(1).unwrap();
let ptr = BitPtr::<_, _, Lsb0>::new((&mut data).into(), head).unwrap();
let slice = bv_ptr::bitslice_from_raw_parts_mut(ptr, 10);
let slice_ref = unsafe { &mut *slice };
assert_eq!(slice_ref.len(), 10);
slice_ref.set(2, true);
assert_eq!(slice_ref, bits![1, 1, 1, 0, 0, 0, 0, 0, 0, 0]);
```
[`BitSlice`]: crate::slice::BitSlice
+87
View File
@@ -0,0 +1,87 @@
# Bit-wise `memcpy`
This copies bits from a region beginning at `src` into a region beginning at
`dst`, each extending upwards in the address space for `count` bits.
The two regions may overlap.
If the two regions are known to *never* overlap, then [`copy_nonoverlapping`][0]
can be used instead.
## Original
[`ptr::copy`](core::ptr::copy)
## Overlap Definition
`bitvec` defines region overlap only when the bit-pointers used to access them
have the same `O: BitOrder` type parameter. When this parameter differs, the
regions are always assumed to not overlap in real memory, because `bitvec` does
not define the effects of different orderings mapping to the same locations.
## Safety
In addition to the bit-ordering constraints, this inherits the restrictions of
the original `ptr::copy`:
- `src` must be valid to read the next `count` bits out of memory.
- `dst` must be valid to write into the next `count` bits.
- Both `src` and `dst` must satisfy [`BitPtr`]s non-null, well-aligned,
requirements.
## Behavior
This reads and writes each bit individually. It is incapable of optimizing its
behavior to perform batched memory accesses that have better awareness of the
underlying memory.
The [`BitSlice::copy_from_bitslice`][1] method *is* able to perform this
optimization. You should always prefer to use `BitSlice` if you are sensitive to
performance.
## Examples
This example performs a simple copy across independent regions. You can see that
it follows the ordering parameter for the source and destination regions as it
walks each bit individually.
```rust
use bitvec::prelude::*;
use bitvec::ptr as bv_ptr;
let start = 0b1011u8;
let mut end = 0u16;
let src = BitPtr::<_, _, Lsb0>::from_ref(&start);
let dst = BitPtr::<_, _, Msb0>::from_mut(&mut end);
unsafe {
bv_ptr::copy(src, dst, 4);
}
assert_eq!(end, 0b1101_0000_0000_0000);
```
This can detect overlapping regions. Note again that overlap only exists when
the ordering parameter is the same! Using bit-pointers that overlap in real
memory with different ordering is not defined, and `bitvec` does not specify any
result.
```rust
use bitvec::prelude::*;
use bitvec::ptr as bv_ptr;
let mut x = 0b1111_0010u8;
let src = BitPtr::<_, _, Lsb0>::from_mut(&mut x);
let dst = unsafe { src.add(2) };
unsafe {
bv_ptr::copy(src.to_const(), dst, 4);
}
assert_eq!(x, 0b1100_1010);
// bottom nibble ^^ ^^ moved here
```
[`BitPtr`]: crate::ptr::BitPtr
[0]: crate::ptr::copy_nonoverlapping
[1]: crate::slice::BitSlice::copy_from_bitslice
@@ -0,0 +1,59 @@
# Bit-wise `memcpy`
This copies bits from a region beginning at `src` into a region beginning at
`dst`, each extending upwards in the address space for `count` bits.
The two regions *may not* overlap.
## Original
[`ptr::copy_nonoverlapping`](core::ptr::copy_nonoverlapping)
## Overlap Definition
The two regions may be in the same provenance as long as they have no common
bits. `bitvec` only defines the possibility of overlap when the `O1` and `O2`
bit-ordering parameters are the same; if they are different, then it considers
the regions to not overlap, and does not attempt to detect real-memory
collisions.
## Safety
In addition to the bit-ordering constraints, this inherits the restrictions of
the original `ptr::copy_nonoverlapping`:
- `src` must be valid to read the next `count` bits out of memory.
- `dst` must be valid to write into the next `count` bits.
- Both `src` and `dst` must satisfy [`BitPtr`]s non-null, well-aligned,
requirements.
## Behavior
This reads and writes each bit individually. It is incapable of optimizing its
behavior to perform batched memory accesses that have better awareness of the
underlying memory.
The [`BitSlice::copy_from_bitslice`][1] method *is* able to perform this
optimization, and tolerates overlap. You should always prefer to use `BitSlice`
if you are sensitive to performance.
## Examples
```rust
use bitvec::prelude::*;
use bitvec::ptr as bv_ptr;
let start = 0b1011u8;
let mut end = 0u16;
let src = BitPtr::<_, _, Lsb0>::from_ref(&start);
let dst = BitPtr::<_, _, Msb0>::from_mut(&mut end);
unsafe {
bv_ptr::copy_nonoverlapping(src, dst, 4);
}
assert_eq!(end, 0b1101_0000_0000_0000);
```
[1]: crate::slice::BitSlice::copy_from_bitslice
[`BitPtr`]: crate::ptr::BitPtr
+9
View File
@@ -0,0 +1,9 @@
# Remote Destructor
`BitPtr` only points to indestructible types. This has no effect, and is only
present for symbol compatibility. You should not have been calling it on your
integers or `bool`s anyway!
## Original
[`ptr::drop_in_place`](core::ptr::drop_in_place)
+38
View File
@@ -0,0 +1,38 @@
# Bit-Pointer Equality
This compares two bit-pointers for equality by their address value, not by the
value of their referent bit. This does not dereference either.
## Original
[`ptr::eq`](core::ptr::eq)
## API Differences
The two bit-pointers can differ in their storage type parameters. `bitvec`
defines pointer equality only between pointers with the same underlying
[`BitStore::Mem`][0] element type. Numerically-equal bit-pointers with different
integer types *will not* compare equal, though this function will compile and
accept them.
This cannot compare encoded span poiters. `*const BitSlice` can be used in the
standard-library `ptr::eq`, and does not need an override.
## Examples
```rust
use bitvec::prelude::*;
use bitvec::ptr as bv_ptr;
use core::cell::Cell;
let data = 0u16;
let bare_ptr = BitPtr::<_, _, Lsb0>::from_ref(&data);
let cell_ptr = bare_ptr.cast::<Cell<u16>>();
assert!(bv_ptr::eq(bare_ptr, cell_ptr));
let byte_ptr = bare_ptr.cast::<u8>();
assert!(!bv_ptr::eq(bare_ptr, byte_ptr));
```
[0]: crate::store::BitStore::Mem
+11
View File
@@ -0,0 +1,11 @@
# Bit-Pointer Hashing
This hashes a bit-pointer by the value of its components, rather than its
referent bit. It does not dereference the pointer.
This can be used to ensure that you are hashing the bit-pointers address value,
though, as always, hashing an address rather than a data value is likely unwise.
## Original
[`ptr::hash`](core::ptr::hash)
+10
View File
@@ -0,0 +1,10 @@
# Bit-Pointer Sentinel Value
`BitPtr` does not permit actual null pointers. Instead, it uses the canonical
dangling address as a sentinel for uninitialized, useless, locations.
You should use `Option<BitPtr>` if you need to track nullability.
## Original
[`ptr::null`](core::ptr::null)
+10
View File
@@ -0,0 +1,10 @@
# Bit-Pointer Sentinel Value
`BitPtr` does not permit actual null pointers. Instead, it uses the canonical
dangling address as a sentinel for uninitialized, useless, locations.
You should use `Option<BitPtr>` if you need to track nullability.
## Original
[`ptr::null_mut`](core::ptr::null_mut)
+9
View File
@@ -0,0 +1,9 @@
# Proxy Bit-References
Rust does not permit the use of custom proxy structures in place of true
reference primitives, so APIs that specify references (like `IndexMut` or
`DerefMut`) cannot be implemented by types that cannot manifest `&mut`
references directly. Since `bitvec` cannot produce an `&mut bool` reference
within a `BitSlice`, it instead uses the `BitRef` proxy type defined in this
module to provide reference-like work generally, and simply does not define
`IndexMut<usize>`.
+20
View File
@@ -0,0 +1,20 @@
# Bit-Pointer Ranges
This module defines ports of the `Range` type family to work with `BitPtr`s.
Rusts own ranges have unstable internal details that make them awkward to use
within the standard library, and essentially impossible outside it, with
anything other than the numeric fundamentals.
In particular, `bitvec` uses a half-open range of `BitPtr`s to represent
C++-style dual-pointer memory regions (such as `BitSlice` iterators). Rusts own
slice iterators also do this, but because `*T` does not implement the [`Step`]
trait, the standard library duplicates some work done by `Range` types in the
slice iterators just to be able to alter the views.
As such, `Range<BitPtr<_, _, _>>` has the same functionality as
`Range<*const _>`: almost none. As this is undesirable, this module defines
equivalent types that implement the full desired behavior of a pointer range.
These are primarily used as crate internals, but may also be of interest to
users.
[`Step`]: core::iter::Step
+28
View File
@@ -0,0 +1,28 @@
# Single-Bit Read
This reads the bit out of `src` directly.
## Original
[`ptr::read`](core::ptr::read)
## Safety
Because this performs a dereference of memory, it inherits the original
`ptr::read`s requirements:
- `src` must be valid to read.
- `src` must be properly aligned. This is an invariant of the `BitPtr` type as
well as of the memory access.
- `src` must point to an initialized value of `T`.
## Examples
```rust
use bitvec::prelude::*;
use bitvec::ptr as bv_ptr;
let data = 128u8;
let ptr = BitPtr::<_, _, Msb0>::from_ref(&data);
assert!(unsafe { bv_ptr::read(ptr) });
```
+30
View File
@@ -0,0 +1,30 @@
# Single-Bit Unaligned Read
This reads the bit out of `src` directly. It uses compiler intrinsics to
tolerate an unaligned `T` address. However, because `BitPtr` has a type
invariant that addresses are always well-aligned (and non-null), this has no
benefit or purpose.
## Original
[`ptr::read_unaligned`](core::ptr::read_unaligned)
## Safety
Because this performs a dereference of memory, it inherits the original
`ptr::read_unaligned`s requirements:
- `src` must be valid to read.
- `src` must point to an initialized value of `T`.
## Examples
```rust
use bitvec::prelude::*;
use bitvec::ptr as bv_ptr;
let data = 128u8;
let ptr = BitPtr::<_, _, Msb0>::from_ref(&data);
assert!(unsafe { bv_ptr::read_unaligned(ptr) });
```
+39
View File
@@ -0,0 +1,39 @@
# Single-Bit Volatile Read
This reads the bit out of `src` directly, using a volatile I/O intrinsic to
prevent compiler reördering or removal.
You should not use `bitvec` to perform any volatile I/O operations. You should
instead do volatile I/O work on integer values directly, or use a crate like
[`voladdress`][0] to perform I/O transactions, and use `bitvec` only on stack
locals that have no additional memory semantics.
## Original
[`ptr::read_volatile`](core::ptr::read_volatile)
## Safety
Because this performs a dereference of memory, it inherits the original
`ptr::read_volatile`s requirements:
- `src` must be valid to read.
- `src` must be properly aligned. This is an invariant of the `BitPtr` type as
well as of the memory access.
- `src` must point to an initialized value of `T`.
Remember that volatile accesses are ordinary loads that the compiler cannot
remove or reörder! They are *not* an atomic synchronizer.
## Examples
```rust
use bitvec::prelude::*;
use bitvec::ptr as bv_ptr;
let data = 128u8;
let ptr = BitPtr::<_, _, Msb0>::from_ref(&data);
assert!(unsafe { bv_ptr::read_volatile(ptr) });
```
[0]: https://docs.rs/voladdress/latest/voladdress
+35
View File
@@ -0,0 +1,35 @@
# Single-Bit Replacement
This writes a new value into a location, and returns the bit-value previously
stored there. It is semantically and behaviorally equivalent to
[`BitRef::replace`][0], except that it works on bit-pointer structures rather
than proxy references. Prefer to use a proxy reference or
[`BitSlice::replace`][1] instead.
## Original
[`ptr::replace`](core::ptr::replace)
## Safety
This has the same safety requirements as [`ptr::read`][2] and [`ptr::write`][3],
as it is required to use them in its implementation.
## Examples
```rust
use bitvec::prelude::*;
use bitvec::ptr as bv_ptr;
let mut data = 4u8;
let ptr = BitPtr::<_, _, Lsb0>::from_mut(&mut data);
assert!(unsafe {
bv_ptr::replace(ptr.add(2), false)
});
assert_eq!(data, 0);
```
[0]: crate::ptr::BitRef::replace
[1]: crate::slice::BitSlice::replace
[2]: crate::ptr::read
[3]: crate::ptr::write
+10
View File
@@ -0,0 +1,10 @@
# Single-Bit Pointers
This module defines single-bit pointers, which are required to be structures in
their own right and do not have an encoded form.
These pointers should generally not be used; [`BitSlice`] is more likely to be
correct and have better performance. They are provided for consistency, not for
hidden optimizations.
[`BitSlice`]: crate::slice::BitSlice
@@ -0,0 +1,10 @@
# Raw Bit-Slice Pointer Construction
This is an alias for [`bitslice_from_raw_parts`][0], renamed for symbol
compatibility. See its documentation instead.
## Original
[`ptr::slice_from_raw_parts`](core::ptr::slice_from_raw_parts)
[0]: crate::ptr::bitslice_from_raw_parts
@@ -0,0 +1,10 @@
# Raw Bit-Slice Pointer Construction
This is an alias for [`bitslice_from_raw_parts_mut`][0], renamed for symbol
compatibility. See its documentation instead.
## Original
[`ptr::slice_from_raw_parts_mut`](core::ptr::slice_from_raw_parts_mut)
[0]: crate::ptr::bitslice_from_raw_parts_mut
+33
View File
@@ -0,0 +1,33 @@
# Encoded Bit-Span Pointer
This module implements the logic used to encode and operate on values of
`*BitSlice`. It is the core operational module of the library.
## Theory
Rust is slowly experimenting with allowing user-provided types to define
metadata structures attached to raw-pointers and references in a structured
manner. However, this is a fairly recent endeavour, much newer than `bitvec`s
work in the same area, so `bitvec` does not attempt to use it.
The problem with bit-addressable memory is that it takes three more bits to
select a *bit* than it does a *byte*. While AMD64 specifies (and AArch64 likely
follows by fiat) that pointers are 64 bits wide but only contain 48 (or more
recently, 57) bits of information, leaving the remainder available to store
userspace information (as long as it is canonicalized before dereferencing),
x86 and Arm32 have no such luxury space in their pointers.
Since `bitvec` supports 32-bit targets, it instead opts to place the three
bit-selector bits outside the pointer address. The only other space available in
Rust pointers is in the length field of slice pointers. As such, `bitvec`
encodes its span description information into `*BitSlice` and, by extension,
`&/mut BitSlice`. The value underlying these language fundamentals is well-known
(though theoretically opaque), and the standard library provides APIs that it
promises will always be valid to manipulate them. Through careful use of these
APIs, and following type-system rules to prevent undefined behavior, `bitvec` is
able to define its span descriptions within the language fundamentals and appear
fully idiomatic and compliant with existing Rust patterns.
See the [`BitSpan`] type documentation for details on the encoding scheme used.
[`BitSpan`]: self::BitSpan
+34
View File
@@ -0,0 +1,34 @@
# Bit Swap
This exchanges the bit-values in two locations. It is semantically and
behaviorally equivalent to [`BitRef::swap`][0], except that it works on
bit-pointer structures rather than proxy references. Prefer to use a proxy
reference or [`BitSlice::swap`][1] instead.
## Original
[`ptr::swap`](core::ptr::swap)
## Safety
This has the same safety requirements as [`ptr::read`][2] and [`ptr::write`][3],
as it is required to use them in its implementation.
## Examples
```rust
use bitvec::prelude::*;
use bitvec::ptr as bv_ptr;
let mut data = 2u8;
let x = BitPtr::<_, _, Lsb0>::from_mut(&mut data);
let y = unsafe { x.add(1) };
unsafe { bv_ptr::swap(x, y); }
assert_eq!(data, 1);
```
[0]: crate::ptr::BitRef::swap
[1]: crate::slice::BitSlice::swap
[2]: crate::ptr::read
[3]: crate::ptr::write

Some files were not shown because too many files have changed in this diff Show More