BLI: add high level documentation for core data structures #25
@ -1,3 +1,107 @@
|
||||
# Bits
|
||||
|
||||
`BitVector`
|
||||
Sometimes it can be benefitial to work with bits directly instead of boolean values because they are very compact and many bits can be processed at the same time. This document shows some available utilities to work with dynamically sized bitsets.
|
||||
|
||||
Before choosing to work with individual bits instead of bools, keep in mind that there are also downsides which may not be obvious at first.
|
||||
- Writing to separate bits in the same int is not thread-safe. Therefore, an existing vector of
|
||||
bool can't easily be replaced with a bit vector, if it is written to from multiple threads.
|
||||
Read-only access from multiple threads is fine though.
|
||||
- Writing individual elements is more expensive when the array is in cache already. That is
|
||||
because changing a bit is always a read-modify-write operation on the int the bit resides in.
|
||||
- Reading individual elements is more expensive when the array is in cache already. That is
|
||||
because additional bit-wise operations have to be applied after the corresponding int is
|
||||
read.
|
||||
|
||||
## BitVector
|
||||
|
||||
The most common type to use when working with bits is `blender::BitVector` ([source](https://projects.blender.org/blender/blender/src/branch/main/source/blender/blenlib/BLI_bit_vector.hh)). It's a dynamically growing contiguous array of bits. As such it has similarities to `blender::Vector<bool>`, but also `std::vector<bool>`. In contrast to both, `BitVector` has an API that is more optimized for dealing with bits.
|
||||
|
||||
Just like `blender::Vector`, `BitVector` also supports an inline buffer. This is especially benefitial here, because many bits can be stored directly in the `BitVector` without significantly increasing its size.
|
||||
|
||||
```cpp
|
||||
/* Construct bit vector with 500 bits which a false/0 by default. */
|
||||
BitVector<> values(500, false);
|
||||
|
||||
/* Setting a bit. */
|
||||
values[10].set();
|
||||
|
||||
/* Setting a bit to a specific value. */
|
||||
values[10].set(true);
|
||||
|
||||
/* Resetting a bit. */
|
||||
values[10].reset();
|
||||
|
||||
/* Check if a bit is set. */
|
||||
values[10].test();
|
||||
|
||||
/* It's also possible to use implicit conversions instead. */
|
||||
if (values[10]) { /* ... */ }
|
||||
```
|
||||
|
||||
## BitRef
|
||||
|
||||
It's not possible to have a pointer or reference to specific bit with standard C++. Instead, there are the `BitRef` and `MutableBitRef` types ([source](https://projects.blender.org/blender/blender/src/branch/main/source/blender/blenlib/BLI_bit_ref.hh)) which can reference individual bits.
|
||||
|
||||
Those are also the types returned when accessing a specific index in `BitVector`.
|
||||
|
||||
## BitSpan
|
||||
|
||||
Just like it's not possible to reference a single bit with standard C++, it's also not possible to reference a span of bits. To do that, one can use `BitSpan` and `MutableBitSpan` ([source](https://projects.blender.org/blender/blender/src/branch/main/source/blender/blenlib/BLI_bit_span.hh)).
|
||||
|
||||
Additionally, there is also `BoundedBitSpan` and `MutableBoundedBitSpan`. Those are like normal bit spans but enforce specific constraints on the alignment of the span. These additional constraints allow bit spans to be processed more efficiently than in the most general constraint. For more details on the exact constraints, check the `is_bounded_span` function.
|
||||
|
||||
It's generally recommended to work with bit spans that follow these constraints if possible for best performance.
|
||||
|
||||
## BitSpan Operations
|
||||
|
||||
There are three core operations that can be performed on bit spans:
|
||||
1. Mix multiple bit spans together in some way and store the result in another bit span.
|
||||
2. Check if any bit is set.
|
||||
3. Iterate over all set bits.
|
||||
|
||||
`BLI_bit_span_ops.hh` ([source](https://projects.blender.org/blender/blender/src/branch/main/source/blender/blenlib/BLI_bit_span_ops.hh)) offers utilities to these things.
|
||||
|
||||
```cpp
|
||||
BitVector<> vec1(500);
|
||||
BitVector<> vec2(500);
|
||||
BitVector<> result(500);
|
||||
|
||||
/* Or vec1 into result. */
|
||||
result |= vec1;
|
||||
|
||||
/* Invert result bits. */
|
||||
bits::invert(result);
|
||||
|
||||
/* Check if two bit spans have common bits set. */
|
||||
bits::has_common_set_bits(vec1, vec2);
|
||||
|
||||
/* Iterate over set bits. */
|
||||
bits::foreach_1_index(result, [&](const int64_t i) { /* ... */ });
|
||||
|
||||
/* Perform custom function on bits. */
|
||||
bits::mix_into_first_expr([](bits::BitInt result,
|
||||
bits::BitInt vec1,
|
||||
bits::BitInt vec2) { return result ^ (vec1 | vec2); },
|
||||
result,
|
||||
vec1,
|
||||
vec2);
|
||||
```
|
||||
|
||||
## BitGroupVector
|
||||
|
||||
`BitGroupVector` ([source](https://projects.blender.org/blender/blender/src/branch/main/source/blender/blenlib/BLI_bit_group_vector.hh)) allows storing a fixed number of bits for each element. For example, this could be used to store a bit for each attribute for each vertex in a mesh.
|
||||
|
||||
In some sense, this data structure is also 2D bit vector, that can dynamically grow on one axis.
|
||||
|
||||
`BitGroupVector` is designed so that the individual bit groups all fullfill the requirements by bounded bit spans. As such, they can be processed efficiently.
|
||||
|
||||
```cpp
|
||||
/* Store a bit for each attribute for each vertex. */
|
||||
BitGroupVector<> bits(verts_num, attributes_num, false);
|
||||
|
||||
/* Set all bits for on vertex to 1. */
|
||||
bits[vert_index].set_all();
|
||||
|
||||
/* Set bit for a specific vertex and attribute. */
|
||||
bits[vert_index][attribute_index].set();
|
||||
```
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
## Vector
|
||||
|
||||
The `blender::Vector<T>` ([source](https://projects.blender.org/blender/blender/src/branch/main/source/blender/blenlib/BLI_vector.hh)) is the most important data structure. It stores values of the given type in a dynamically growing continuous buffer.
|
||||
The `blender::Vector<T>` ([source](https://projects.blender.org/blender/blender/src/branch/main/source/blender/blenlib/BLI_vector.hh)) is the most important data structure. It stores values of the given type in a dynamically growing contiguous buffer.
|
||||
|
||||
```cpp
|
||||
/* Create an empty vector. */
|
||||
@ -129,7 +129,7 @@ Using `Map` with a custom type as key requires an equality operator and a [hash]
|
||||
|
||||
## Vector Set
|
||||
|
||||
A `blender::VectorSet<T>` ([source](https://projects.blender.org/blender/blender/src/branch/main/source/blender/blenlib/BLI_vector_set.hh)) is a combination of a `Vector` and a `Set`. It can't contain duplicate values like a `Set` but the values stored in it are ordered based on insertion order (until elements are removed). Just like in a `Vector`, the values are also stored in a continuous array which makes it easy to pass them to other functions that expect an array.
|
||||
A `blender::VectorSet<T>` ([source](https://projects.blender.org/blender/blender/src/branch/main/source/blender/blenlib/BLI_vector_set.hh)) is a combination of a `Vector` and a `Set`. It can't contain duplicate values like a `Set` but the values stored in it are ordered based on insertion order (until elements are removed). Just like in a `Vector`, the values are also stored in a contiguous array which makes it easy to pass them to other functions that expect an array.
|
||||
|
||||
```cpp
|
||||
/* Construct empty vector-set. */
|
||||
|
Loading…
Reference in New Issue
Block a user