blob: 5b01b0a838859b41f1caec4a3c3764c950846638 [file] [log] [blame] [view]
# Bit-Addressable Memory
A slice of individual bits, anywhere in memory.
`BitSlice<T, O>` is an unsized region type; you interact with it through
`&BitSlice<T, O>` and `&mut BitSlice<T, O>` references, which work exactly like
all other Rust references. As with the standard slices relationship to arrays
and vectors, this is `bitvec`s primary working type, but you will probably
hold it through one of the provided [`BitArray`], [`BitBox`], or [`BitVec`]
containers.
`BitSlice` is conceptually a `[bool]` slice, and provides a nearly complete
mirror of `[bool]`s API.
Every bit-vector crate can give you an opaque type that hides shift/mask
calculations from you. `BitSlice` does far more than this: it offers you the
full Rust guarantees about reference behavior, including lifetime tracking,
mutability and aliasing awareness, and explicit memory control, *as well as* the
full set of tools and APIs available to the standard `[bool]` slice type.
`BitSlice` can arbitrarily split and subslice, just like `[bool]`. You can write
a linear consuming function and keep the patterns you already know.
For example, to trim all the bits off either edge that match a condition, you
could write
```rust
use bitvec::prelude::*;
fn trim<T: BitStore, O: BitOrder>(
bits: &BitSlice<T, O>,
to_trim: bool,
) -> &BitSlice<T, O> {
let stop = |b: bool| b != to_trim;
let front = bits.iter()
.by_vals()
.position(stop)
.unwrap_or(0);
let back = bits.iter()
.by_vals()
.rposition(stop)
.map_or(0, |p| p + 1);
&bits[front .. back]
}
# assert_eq!(trim(bits![0, 0, 1, 1, 0, 1, 0], false), bits![1, 1, 0, 1]);
```
to get behavior something like
`trim(&BitSlice[0, 0, 1, 1, 0, 1, 0], false) == &BitSlice[1, 1, 0, 1]`.
## Documentation
All APIs that mirror something in the standard library will have an `Original`
section linking to the corresponding item. All APIs that have a different
signature or behavior than the original will have an `API Differences` section
explaining what has changed, and how to adapt your existing code to the change.
These sections look like this:
## Original
[`[bool]`](https://doc.rust-lang.org/stable/std/primitive.slice.html)
## API Differences
The slice type `[bool]` has no type parameters. `BitSlice<T, O>` has two: one
for the integer type used as backing storage, and one for the order of bits
within that integer type.
`&BitSlice<T, O>` is capable of producing `&bool` references to read bits out
of its memory, but is not capable of producing `&mut bool` references to write
bits *into* its memory. Any `[bool]` API that would produce a `&mut bool` will
instead produce a [`BitRef<Mut, T, O>`] proxy reference.
## Behavior
`BitSlice` is a wrapper over `[T]`. It describes a region of memory, and must be
handled indirectly. This is most commonly done through the reference types
`&BitSlice` and `&mut BitSlice`, which borrow memory owned by some other value
in the program. These buffers can be directly owned by the sibling types
[`BitBox`], which behaves like [`Box<[T]>`](alloc::boxed::Box), and [`BitVec`],
which behaves like [`Vec<T>`]. It cannot be used as the type parameter to a
pointer type such as `Box`, `Rc`, `Arc`, or any other indirection.
The `BitSlice` region provides access to each individual bit in the region, as
if each bit had a memory address that you could use to dereference it. It packs
each logical bit into exactly one bit of storage memory, just like
[`std::bitset`] and [`std::vector<bool>`] in C++.
## Type Parameters
`BitSlice` has two type parameters which propagate through nearly every public
API in the crate. These are very important to its operation, and your choice
of type arguments informs nearly every part of this librarys behavior.
### `T: BitStore`
[`BitStore`] is the simpler of the two parameters. It refers to the integer type
used to hold bits. It must be one of the Rust unsigned integer fundamentals:
`u8`, `u16`, `u32`, `usize`, and on 64-bit systems only, `u64`. In addition, it
can also be an alias-safe wrapper over them (see the [`access`] module) in
order to permit bit-slices to share underlying memory without interfering with
each other.
`BitSlice` references can only be constructed over the integers, not over their
aliasing wrappers. `BitSlice` will only use aliasing types in its `T` slots when
you invoke APIs that produce them, such as [`.split_at_mut()`].
The default type argument is `usize`.
The argument you choose is used as the basis of a `[T]` slice, over which the
`BitSlice` view is produced. `BitSlice<T, _>` is subject to all of the rules
about alignment that `[T]` is. If you are working with in-memory representation
formats, chances are that you already have a `T` type with which youve been
working, and should use it here.
If you are only using this crate to discard the seven wasted bits per `bool`
in a collection of `bool`s, and are not too concerned about the in-memory
representation, then you should use the default type argument of `usize`. This
is because most processors work best when moving an entire `usize` between
memory and the processor itself, and using a smaller type may cause it to slow
down. Additionally, processor instructions are typically optimized for the whole
register, and the processor might need to do additional clearing work for
narrower types.
### `O: BitOrder`
[`BitOrder`] is the more complex parameter. It has a default argument which,
like `usize`, is a good baseline choice when you do not explicitly need to
control the representation of bits in memory.
This parameter determines how `bitvec` indexes the bits within a single `T`
memory element. Computers all agree that in a slice of `T` elements, the element
with the lower index has a lower memory address than the element with the higher
index. But the individual bits within an element do not have addresses, and so
there is no uniform standard of which bit is the zeroth, which is the first,
which is the penultimate, and which is the last.
To make matters even more confusing, there are two predominant ideas of
in-element ordering that often *correlate* with the in-element *byte* ordering
of integer types, but are in fact wholly unrelated! `bitvec` provides these two
main orderings as types for you, and if you need a different one, it also
provides the tools you need to write your own.
#### Least Significant Bit Comes First
This ordering, named the [`Lsb0`] type, indexes bits within an element by
placing the `0` index at the least significant bit (numeric value `1`) and the
final index at the most significant bit (numeric value [`T::MIN`][minval] for
signed integers on most machines).
For example, this is the ordering used by most C compilers to lay out bit-field
struct members on little-endian **byte**-ordered machines.
#### Most Significant Bit Comes First
This ordering, named the [`Msb0`] type, indexes bits within an element by
placing the `0` index at the most significant bit (numeric value
[`T::MIN`][minval] for most signed integers) and the final index at the least
significant bit (numeric value `1`).
For example, this is the ordering used by the [TCP wire format][tcp], and by
most C compilers to lay out bit-field struct members on big-endian
**byte**-ordered machines.
#### Default Ordering
The default ordering is [`Lsb0`], as it typically produces shorter object code
than [`Msb0`] does. If you are implementing a collection, then `Lsb0` will
likely give you better performance; if you are implementing a buffer protocol,
then your choice of ordering is dictated by the protocol definition.
## Safety
`BitSlice` is designed to never introduce new memory unsafety that you did not
provide yourself, either before or during the use of this crate. However, safety
bugs have been identified before, and you are welcome to submit any discovered
flaws as a defect report.
The `&BitSlice` reference type uses a private encoding scheme to hold all of the
information needed in its stack value. This encoding is **not** part of the
public API of the library, and is not binary-compatible with `&[T]`.
Furthermore, in order to satisfy Rusts requirements about alias conditions,
`BitSlice` performs type transformations on the `T` parameter to ensure that it
never creates the potential for undefined behavior or data races.
You must never attempt to type-cast a reference to `BitSlice` in any way. You
must not use [`mem::transmute`] with `BitSlice` anywhere in its type arguments.
You must not use `as`-casting to convert between `*BitSlice` and any other type.
You must not attempt to modify the binary representation of a `&BitSlice`
reference value. These actions will all lead to runtime memory unsafety, are
(hopefully) likely to induce a program crash, and may possibly cause undefined
behavior at compile-time.
Everything in the `BitSlice` public API, even the `unsafe` parts, are guaranteed
to have no more unsafety than their equivalent items in the standard library.
All `unsafe` APIs will have documentation explicitly detailing what the API
requires you to uphold in order for it to function safely and correctly. All
safe APIs will do so themselves.
## Performance
Like the standard librarys `[T]` slice, `BitSlice` is designed to be very easy
to use safely, while supporting `unsafe` usage when necessary. Rust has a
powerful optimizing engine, and `BitSlice` will frequently be compiled to have
zero runtime cost. Where it is slower, it will not be significantly slower than
a manual replacement.
As the machine instructions operate on registers rather than bits, your choice
of [`T: BitStore`] type parameter can influence your bits-slices performance.
Using larger register types means that bit-slices can gallop over
completely-used interior elements faster, while narrower register types permit
more graceful handling of subslicing and aliased splits.
## Construction
`BitSlice` views of memory can be constructed over borrowed data in a number of
ways. As this is a reference-only type, it can only ever be built by borrowing
an existing memory buffer and taking temporary control of your programs view of
the region.
### Macro Constructor
`BitSlice` buffers can be constructed at compile-time through the [`bits!`]
macro. This macro accepts a superset of the [`vec!`] arguments, and creates an
appropriate buffer in the local scope. The macro expands to a borrowed
[`BitArray`] temporary, which will live for the duration of the bound name.
```rust
use bitvec::prelude::*;
let immut = bits![u8, Lsb0; 0, 1, 0, 0, 1, 0, 0, 1];
let mutable: &mut BitSlice<_, _> = bits![mut u8, Msb0; 0; 8];
assert_ne!(immut, mutable);
mutable.clone_from_bitslice(immut);
assert_eq!(immut, mutable);
```
### Borrowing Constructors
You may borrow existing elements or slices with the following functions:
- [`from_element`] and [`from_element_mut`],
- [`from_slice`] and [`from_slice_mut`],
- [`try_from_slice`] and [`try_from_slice_mut`]
These take references to existing memory and construct `BitSlice` references
from them. These are the most basic ways to borrow memory and view it as bits;
however, you should prefer the [`BitView`] trait methods instead.
```rust
use bitvec::prelude::*;
let data = [0u16; 3];
let local_borrow = BitSlice::<_, Lsb0>::from_slice(&data);
let mut data = [0u8; 5];
let local_mut = BitSlice::<_, Lsb0>::from_slice_mut(&mut data);
```
### Trait Method Constructors
The [`BitView`] trait implements [`.view_bits::<O>()`] and
[`.view_bits_mut::<O>()`] methods on elements, arrays, and slices. This trait,
imported in the crate prelude, is *probably* the easiest way for you to borrow
memory as bits.
```rust
use bitvec::prelude::*;
let data = [0u32; 5];
let trait_view = data.view_bits::<Lsb0>();
let mut data = 0usize;
let trait_mut = data.view_bits_mut::<Msb0>();
```
### Owned Bit Slices
If you wish to take ownership of a memory region and enforce that it is always
viewed as a `BitSlice` by default, you can use one of the [`BitArray`],
[`BitBox`], or [`BitVec`] types, rather than pairing ordinary buffer types with
the borrowing constructors.
```rust
use bitvec::prelude::*;
let slice = bits![0; 27];
let array = bitarr![u8, LocalBits; 0; 10];
# #[cfg(feature = "alloc")] fn allocates() {
let boxed = bitbox![0; 10];
let vec = bitvec![0; 20];
# } #[cfg(feature = "alloc")] allocates();
// arrays always round up
assert_eq!(array.as_bitslice(), slice[.. 16]);
# #[cfg(feature = "alloc")] fn allocates2() {
# let slice = bits![0; 27];
# let boxed = bitbox![0; 10];
# let vec = bitvec![0; 20];
assert_eq!(boxed.as_bitslice(), slice[.. 10]);
assert_eq!(vec.as_bitslice(), slice[.. 20]);
# } #[cfg(feature = "alloc")] allocates2();
```
## Usage
`BitSlice` implements the full standard-library `[bool]` API. The documentation
for these API surfaces is intentionally sparse, and forwards to the standard
library rather than try to replicate it.
`BitSlice` also has a great deal of novel API surfaces. These are broken into
separate `impl` blocks below. A short summary:
- Since there is no `BitSlice` literal, the constructor functions `::empty()`,
`::from_element()`, `::from_slice()`, and `::try_from_slice()`, and their
`_mut` counterparts, create bit-slices as needed.
- Since `bits[idx] = value` does not exist, you can use `.set()` or `.replace()`
(as well as their `_unchecked` and `_aliased` counterparts) to write into a
bit-slice.
- Raw memory can be inspected with `.domain()` and `.domain_mut()`, and a
bit-slice can be split on aliasing lines with `.bit_domain()` and
`.bit_domain_mut()`.
- The population can be queried for which indices have `0` or `1` bits by
iterating across all such indices, counting them, or counting leading or
trailing blocks. Additionally, `.any()`, `.all()`, `.not_any()`, `.not_all()`,
and `.some()` test whether bit-slices satisfy aggregate Boolean qualities.
- Buffer contents can be relocated internally by shifting or rotating to the
left or right.
## Trait Implementations
`BitSlice` adds trait implementations that `[bool]` and `[T]` do not necessarily
have, including numeric formatting and Boolean arithmetic operators.
Additionally, the [`BitField`] trait allows bit-slices to act as a buffer for
wide-value storage.
[minval]: https://doc.rust-lang.org/stable/std/primitive.usize.html#associatedconstant.MIN
[tcp]: https://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_segment_structure
[`BitArray`]: crate::array::BitArray
[`BitBox`]: crate::boxed::BitBox
[`BitField`]: crate::field::BitField
[`BitRef<Mut, T, O>`]: crate::ptr::BitRef
[`BitOrder`]: crate::order::BitOrder
[`BitStore`]: crate::store::BitStore
[`BitVec`]: crate::vec::BitVec
[`BitView`]: crate::view::BitView
[`Cell<T>`]: core::cell::Cell
[`Lsb0`]: crate::order::Lsb0
[`Msb0`]: crate::order::Msb0
[`T: BitStore`]: crate::store::BitStore
[`Vec<T>`]: alloc::vec::Vec
[`access`]: crate::access
[`bits!`]: macro@crate::bits
[`bitvec::prelude::LocalBits`]: crate::order::LocalBits
[`from_element`]: Self::from_element
[`from_element_mut`]: Self::from_element_mut
[`from_slice`]: Self::from_slice
[`from_slice_mut`]: Self::from_slice_mut
[`mem::transmute`]: core::mem::transmute
[`std::bitset`]: https://en.cppreference.com/w/cpp/utility/bitset
[`std::vector<bool>`]: https://en.cppreference.com/w/cpp/container/vector_bool
[`try_from_slice`]: Self::try_from_slice
[`try_from_slice_mut`]: Self::try_from_slice_mut
[`vec!`]: macro@alloc::vec
[`.split_at_mut()`]: Self::split_at_mut
[`.view_bits::<O>()`]: crate::view::BitView::view_bits
[`.view_bits_mut::<O>()`]: crate::view::BitView::view_bits_mut