File: BitSlice.md

package info (click to toggle)
rust-bitvec 1.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,780 kB
  • sloc: makefile: 2
file content (371 lines) | stat: -rw-r--r-- 15,634 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# 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 slice’s 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 library’s 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 you’ve 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 Rust’s 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 library’s `[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-slice’s 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 program’s 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