FastLED 3.9.15
Loading...
Searching...
No Matches
bitset.h
Go to the documentation of this file.
1#pragma once
2
3#include "fl/bitset_dynamic.h"
4#include "fl/type_traits.h"
5#include "fl/variant.h"
6#include <stdint.h>
7
8namespace fl {
9
10template <uint32_t N> class BitsetInlined;
11
12template <uint32_t N> class BitsetFixed;
13
14template <uint32_t N = 256>
15using bitset = BitsetInlined<N>; // inlined but can go bigger.
16
17template <uint32_t N>
18using bitset_fixed = BitsetFixed<N>; // fixed size, no dynamic allocation.
19
21template <uint32_t N> class BitsetFixed {
22 private:
23 static constexpr uint32_t bits_per_block = 8 * sizeof(uint64_t);
24 static constexpr uint32_t block_count =
26 using block_type = uint64_t;
27
28 // Underlying blocks storing bits
30
31 public:
32 struct Proxy {
34 uint32_t _pos;
35
37
38 Proxy &operator=(bool value) {
39 _bitset.set(_pos, value);
40 return *this;
41 }
42
43 operator bool() const { return _bitset.test(_pos); }
44 };
45
46 Proxy operator[](uint32_t pos) { return Proxy(*this, pos); }
47
49 constexpr BitsetFixed() noexcept : _blocks{} {}
50
52 void reset() noexcept {
53 for (uint32_t i = 0; i < block_count; ++i) {
54 _blocks[i] = 0;
55 }
56 }
57
59 BitsetFixed &set(uint32_t pos, bool value = true) {
60 if (pos < N) {
61 const uint32_t idx = pos / bits_per_block;
62 const uint32_t off = pos % bits_per_block;
63 if (value) {
64 _blocks[idx] |= (block_type(1) << off);
65 } else {
66 _blocks[idx] &= ~(block_type(1) << off);
67 }
68 }
69 return *this;
70 }
71
72 void assign(size_t n, bool value) {
73 if (n > N) {
74 n = N;
75 }
76 for (size_t i = 0; i < n; ++i) {
77 set(i, value);
78 }
79 }
80
82 BitsetFixed &reset(uint32_t pos) { return set(pos, false); }
83
85 BitsetFixed &flip(uint32_t pos) {
86 if (pos < N) {
87 const uint32_t idx = pos / bits_per_block;
88 const uint32_t off = pos % bits_per_block;
89 _blocks[idx] ^= (block_type(1) << off);
90 }
91 return *this;
92 }
93
95 BitsetFixed &flip() noexcept {
96 for (uint32_t i = 0; i < block_count; ++i) {
97 _blocks[i] = ~_blocks[i];
98 }
99 // Mask out unused high bits in the last block
100 if (N % bits_per_block != 0) {
101 const uint32_t extra = bits_per_block - (N % bits_per_block);
102 _blocks[block_count - 1] &= (~block_type(0) >> extra);
103 }
104 return *this;
105 }
106
108 bool test(uint32_t pos) const noexcept {
109 if (pos < N) {
110 const uint32_t idx = pos / bits_per_block;
111 const uint32_t off = pos % bits_per_block;
112 return (_blocks[idx] >> off) & 1;
113 }
114 return false;
115 }
116
118 bool operator[](uint32_t pos) const noexcept { return test(pos); }
119
121 uint32_t count() const noexcept {
122 uint32_t cnt = 0;
123 // Count bits in all complete blocks
124 for (uint32_t i = 0; i < block_count - 1; ++i) {
125 cnt += __builtin_popcountll(_blocks[i]);
126 }
127
128 // For the last block, we need to be careful about counting only valid
129 // bits
130 if (block_count > 0) {
131 block_type last_block = _blocks[block_count - 1];
132 // If N is not a multiple of bits_per_block, mask out the unused
133 // bits
134 if (N % bits_per_block != 0) {
135 const uint32_t valid_bits = N % bits_per_block;
136 // Create a mask with only the valid bits set to 1
137 block_type mask = (valid_bits == bits_per_block)
138 ? ~block_type(0)
139 : ((block_type(1) << valid_bits) - 1);
140 last_block &= mask;
141 }
142 cnt += __builtin_popcountll(last_block);
143 }
144
145 return cnt;
146 }
147
149 bool any() const noexcept { return count() > 0; }
150 bool none() const noexcept { return count() == 0; }
151 bool all() const noexcept {
152 if (N == 0)
153 return true;
154
155 // Check all complete blocks
156 for (uint32_t i = 0; i < block_count - 1; ++i) {
157 if (_blocks[i] != ~block_type(0)) {
158 return false;
159 }
160 }
161
162 // Check the last block
163 if (block_count > 0) {
164 block_type mask;
165 if (N % bits_per_block != 0) {
166 // Create a mask for the valid bits in the last block
167 mask = (block_type(1) << (N % bits_per_block)) - 1;
168 } else {
169 mask = ~block_type(0);
170 }
171
172 if ((_blocks[block_count - 1] & mask) != mask) {
173 return false;
174 }
175 }
176
177 return true;
178 }
179
181 BitsetFixed &operator&=(const BitsetFixed &other) noexcept {
182 for (uint32_t i = 0; i < block_count; ++i) {
183 _blocks[i] &= other._blocks[i];
184 }
185 return *this;
186 }
187
188 BitsetFixed &operator|=(const BitsetFixed &other) noexcept {
189 for (uint32_t i = 0; i < block_count; ++i) {
190 _blocks[i] |= other._blocks[i];
191 }
192 return *this;
193 }
194
195 BitsetFixed &operator^=(const BitsetFixed &other) noexcept {
196 for (uint32_t i = 0; i < block_count; ++i) {
197 _blocks[i] ^= other._blocks[i];
198 }
199 return *this;
200 }
201
203 constexpr uint32_t size() const noexcept { return N; }
204
207 const BitsetFixed &rhs) noexcept {
208 return lhs &= rhs;
209 }
211 const BitsetFixed &rhs) noexcept {
212 return lhs |= rhs;
213 }
215 const BitsetFixed &rhs) noexcept {
216 return lhs ^= rhs;
217 }
218 friend BitsetFixed operator~(BitsetFixed bs) noexcept { return bs.flip(); }
219};
220
224template <uint32_t N = 256> // Default size is 256 bits, or 32 bytes
226 private:
227 // Either store a fixed Bitset<N> or a dynamic Bitset
230
231 public:
232 struct Proxy {
234 uint32_t _pos;
235
238
239 Proxy &operator=(bool value) {
240 _bitset.set(_pos, value);
241 return *this;
242 }
243
244 operator bool() const { return _bitset.test(_pos); }
245 };
246
247 Proxy operator[](uint32_t pos) { return Proxy(*this, pos); }
248
252 if (size > N) {
254 }
255 }
257 BitsetInlined(BitsetInlined &&other) noexcept
258 : _storage(fl::move(other._storage)) {}
260 if (this != &other) {
261 _storage = other._storage;
262 }
263 return *this;
264 }
266 if (this != &other) {
267 _storage = fl::move(other._storage);
268 }
269 return *this;
270 }
271
273 void reset() noexcept {
274 if (_storage.template is<fixed_bitset>()) {
275 _storage.template ptr<fixed_bitset>()->reset();
276 } else {
277 _storage.template ptr<bitset_dynamic>()->reset();
278 }
279 }
280
281 void assign(size_t n, bool value) {
282 resize(n);
283 if (_storage.template is<fixed_bitset>()) {
284 _storage.template ptr<fixed_bitset>()->assign(n, value);
285 } else {
286 _storage.template ptr<bitset_dynamic>()->assign(n, value);
287 }
288 }
289
291 void resize(uint32_t new_size) {
292 if (new_size <= N) {
293 // If we're already using the fixed Bitset, nothing to do
294 if (_storage.template is<bitset_dynamic>()) {
295 // Convert back to fixed Bitset
296 fixed_bitset fixed;
297 bitset_dynamic *dynamic =
298 _storage.template ptr<bitset_dynamic>();
299
300 // Copy bits from dynamic to fixed
301 for (uint32_t i = 0; i < N && i < dynamic->size(); ++i) {
302 if (dynamic->test(i)) {
303 fixed.set(i);
304 }
305 }
306
307 _storage = fixed;
308 }
309 } else {
310 // Need to use dynamic Bitset
311 if (_storage.template is<fixed_bitset>()) {
312 // Convert from fixed to dynamic
313 bitset_dynamic dynamic(new_size);
314 fixed_bitset *fixed = _storage.template ptr<fixed_bitset>();
315
316 // Copy bits from fixed to dynamic
317 for (uint32_t i = 0; i < N; ++i) {
318 if (fixed->test(i)) {
319 dynamic.set(i);
320 }
321 }
322
323 _storage = dynamic;
324 } else {
325 // Already using dynamic, just resize
326 _storage.template ptr<bitset_dynamic>()->resize(new_size);
327 }
328 }
329 }
330
332 BitsetInlined &set(uint32_t pos, bool value = true) {
333 if (pos >= N && _storage.template is<fixed_bitset>()) {
334 resize(pos + 1);
335 }
336
337 if (_storage.template is<fixed_bitset>()) {
338 if (pos < N) {
339 _storage.template ptr<fixed_bitset>()->set(pos, value);
340 }
341 } else {
342 if (pos >= _storage.template ptr<bitset_dynamic>()->size()) {
343 _storage.template ptr<bitset_dynamic>()->resize(pos + 1);
344 }
345 _storage.template ptr<bitset_dynamic>()->set(pos, value);
346 }
347 return *this;
348 }
349
351 BitsetInlined &reset(uint32_t pos) { return set(pos, false); }
352
354 BitsetInlined &flip(uint32_t pos) {
355 if (pos >= N && _storage.template is<fixed_bitset>()) {
356 resize(pos + 1);
357 }
358
359 if (_storage.template is<fixed_bitset>()) {
360 if (pos < N) {
361 _storage.template ptr<fixed_bitset>()->flip(pos);
362 }
363 } else {
364 if (pos >= _storage.template ptr<bitset_dynamic>()->size()) {
365 _storage.template ptr<bitset_dynamic>()->resize(pos + 1);
366 }
367 _storage.template ptr<bitset_dynamic>()->flip(pos);
368 }
369 return *this;
370 }
371
373 BitsetInlined &flip() noexcept {
374 if (_storage.template is<fixed_bitset>()) {
375 _storage.template ptr<fixed_bitset>()->flip();
376 } else {
377 _storage.template ptr<bitset_dynamic>()->flip();
378 }
379 return *this;
380 }
381
383 bool test(uint32_t pos) const noexcept {
384 if (_storage.template is<fixed_bitset>()) {
385 return pos < N ? _storage.template ptr<fixed_bitset>()->test(pos)
386 : false;
387 } else {
388 return _storage.template ptr<bitset_dynamic>()->test(pos);
389 }
390 }
391
393 bool operator[](uint32_t pos) const noexcept { return test(pos); }
394
396 uint32_t count() const noexcept {
397 if (_storage.template is<fixed_bitset>()) {
398 return _storage.template ptr<fixed_bitset>()->count();
399 } else {
400 return _storage.template ptr<bitset_dynamic>()->count();
401 }
402 }
403
405 bool any() const noexcept {
406 if (_storage.template is<fixed_bitset>()) {
407 return _storage.template ptr<fixed_bitset>()->any();
408 } else {
409 return _storage.template ptr<bitset_dynamic>()->any();
410 }
411 }
412
413 bool none() const noexcept {
414 if (_storage.template is<fixed_bitset>()) {
415 return _storage.template ptr<fixed_bitset>()->none();
416 } else {
417 return _storage.template ptr<bitset_dynamic>()->none();
418 }
419 }
420
421 bool all() const noexcept {
422 if (_storage.template is<fixed_bitset>()) {
423 return _storage.template ptr<fixed_bitset>()->all();
424 } else {
425 return _storage.template ptr<bitset_dynamic>()->all();
426 }
427 }
428
430 uint32_t size() const noexcept {
431 if (_storage.template is<fixed_bitset>()) {
432 return N;
433 } else {
434 return _storage.template ptr<bitset_dynamic>()->size();
435 }
436 }
437
439 friend BitsetInlined operator~(const BitsetInlined &bs) noexcept {
440 BitsetInlined result = bs;
441 result.flip();
442 return result;
443 }
444
446 const BitsetInlined &rhs) noexcept {
447 BitsetInlined result = lhs;
448
449 if (result._storage.template is<fixed_bitset>() &&
450 rhs._storage.template is<fixed_bitset>()) {
451 // Both are fixed, use the fixed implementation
452 *result._storage.template ptr<fixed_bitset>() &=
453 *rhs._storage.template ptr<fixed_bitset>();
454 } else {
455 // At least one is dynamic, handle bit by bit
456 uint32_t min_size =
457 result.size() < rhs.size() ? result.size() : rhs.size();
458 for (uint32_t i = 0; i < min_size; ++i) {
459 result.set(i, result.test(i) && rhs.test(i));
460 }
461 // Clear any bits beyond the size of rhs
462 for (uint32_t i = min_size; i < result.size(); ++i) {
463 result.reset(i);
464 }
465 }
466
467 return result;
468 }
469
471 const BitsetInlined &rhs) noexcept {
472 BitsetInlined result = lhs;
473
474 if (result._storage.template is<fixed_bitset>() &&
475 rhs._storage.template is<fixed_bitset>()) {
476 // Both are fixed, use the fixed implementation
477 *result._storage.template ptr<fixed_bitset>() |=
478 *rhs._storage.template ptr<fixed_bitset>();
479 } else {
480 // At least one is dynamic, handle bit by bit
481 uint32_t max_size =
482 result.size() > rhs.size() ? result.size() : rhs.size();
483
484 // Resize if needed
485 if (result.size() < max_size) {
486 result.resize(max_size);
487 }
488
489 // Set bits from rhs
490 for (uint32_t i = 0; i < rhs.size(); ++i) {
491 if (rhs.test(i)) {
492 result.set(i);
493 }
494 }
495 }
496
497 return result;
498 }
499
501 const BitsetInlined &rhs) noexcept {
502 BitsetInlined result = lhs;
503
504 if (result._storage.template is<fixed_bitset>() &&
505 rhs._storage.template is<fixed_bitset>()) {
506 // Both are fixed, use the fixed implementation
507 *result._storage.template ptr<fixed_bitset>() ^=
508 *rhs._storage.template ptr<fixed_bitset>();
509 } else {
510 // At least one is dynamic, handle bit by bit
511 uint32_t max_size =
512 result.size() > rhs.size() ? result.size() : rhs.size();
513
514 // Resize if needed
515 if (result.size() < max_size) {
516 result.resize(max_size);
517 }
518
519 // XOR bits from rhs
520 for (uint32_t i = 0; i < rhs.size(); ++i) {
521 result.set(i, result.test(i) != rhs.test(i));
522 }
523 }
524
525 return result;
526 }
527};
528
529} // namespace fl
uint8_t pos
Definition Blur.ino:11
static constexpr uint32_t block_count
Definition bitset.h:24
constexpr BitsetFixed() noexcept
Constructs a BitsetFixed with all bits reset.
Definition bitset.h:49
friend BitsetFixed operator~(BitsetFixed bs) noexcept
Definition bitset.h:218
BitsetFixed & operator|=(const BitsetFixed &other) noexcept
Bitwise OR.
Definition bitset.h:188
bool any() const noexcept
Queries.
Definition bitset.h:149
block_type _blocks[block_count]
Definition bitset.h:29
BitsetFixed & flip(uint32_t pos)
Flips (toggles) the bit at position pos.
Definition bitset.h:85
bool operator[](uint32_t pos) const noexcept
Returns the value of the bit at position pos.
Definition bitset.h:118
BitsetFixed & operator&=(const BitsetFixed &other) noexcept
Bitwise AND.
Definition bitset.h:181
bool none() const noexcept
Definition bitset.h:150
void reset() noexcept
Resets all bits to zero.
Definition bitset.h:52
BitsetFixed & reset(uint32_t pos)
Clears the bit at position pos.
Definition bitset.h:82
friend BitsetFixed operator&(BitsetFixed lhs, const BitsetFixed &rhs) noexcept
Friend operators for convenience.
Definition bitset.h:206
constexpr uint32_t size() const noexcept
Size of the BitsetFixed (number of bits).
Definition bitset.h:203
BitsetFixed & set(uint32_t pos, bool value=true)
Sets or clears the bit at position pos.
Definition bitset.h:59
uint32_t count() const noexcept
Returns the number of set bits.
Definition bitset.h:121
BitsetFixed & operator^=(const BitsetFixed &other) noexcept
Bitwise XOR.
Definition bitset.h:195
void assign(size_t n, bool value)
Definition bitset.h:72
uint64_t block_type
Definition bitset.h:26
Proxy operator[](uint32_t pos)
Definition bitset.h:46
friend BitsetFixed operator|(BitsetFixed lhs, const BitsetFixed &rhs) noexcept
Definition bitset.h:210
bool test(uint32_t pos) const noexcept
Tests whether the bit at position pos is set.
Definition bitset.h:108
static constexpr uint32_t bits_per_block
Definition bitset.h:23
bool all() const noexcept
Definition bitset.h:151
friend BitsetFixed operator^(BitsetFixed lhs, const BitsetFixed &rhs) noexcept
Definition bitset.h:214
BitsetFixed & flip() noexcept
Flips all bits.
Definition bitset.h:95
A simple fixed-size Bitset implementation similar to std::Bitset.
Definition bitset.h:21
void reset() noexcept
Resets all bits to zero.
Definition bitset.h:273
friend BitsetInlined operator&(const BitsetInlined &lhs, const BitsetInlined &rhs) noexcept
Definition bitset.h:445
BitsetInlined(const BitsetInlined &other)
Definition bitset.h:256
bool test(uint32_t pos) const noexcept
Tests whether the bit at position pos is set.
Definition bitset.h:383
bool operator[](uint32_t pos) const noexcept
Returns the value of the bit at position pos.
Definition bitset.h:393
BitsetInlined & set(uint32_t pos, bool value=true)
Sets or clears the bit at position pos.
Definition bitset.h:332
bool none() const noexcept
Definition bitset.h:413
BitsetInlined(BitsetInlined &&other) noexcept
Definition bitset.h:257
BitsetInlined()
Constructs a Bitset with all bits reset.
Definition bitset.h:250
BitsetInlined & operator=(BitsetInlined &&other) noexcept
Definition bitset.h:265
Proxy operator[](uint32_t pos)
Definition bitset.h:247
uint32_t count() const noexcept
Returns the number of set bits.
Definition bitset.h:396
BitsetInlined(size_t size)
Definition bitset.h:251
bool any() const noexcept
Queries.
Definition bitset.h:405
BitsetFixed< N > fixed_bitset
Definition bitset.h:228
friend BitsetInlined operator^(const BitsetInlined &lhs, const BitsetInlined &rhs) noexcept
Definition bitset.h:500
uint32_t size() const noexcept
Definition bitset.h:430
BitsetInlined & operator=(const BitsetInlined &other)
Definition bitset.h:259
void resize(uint32_t new_size)
Resizes the Bitset if needed.
Definition bitset.h:291
bool all() const noexcept
Definition bitset.h:421
void assign(size_t n, bool value)
Definition bitset.h:281
BitsetInlined & reset(uint32_t pos)
Clears the bit at position pos.
Definition bitset.h:351
friend BitsetInlined operator|(const BitsetInlined &lhs, const BitsetInlined &rhs) noexcept
Definition bitset.h:470
Variant< fixed_bitset, bitset_dynamic > _storage
Definition bitset.h:229
friend BitsetInlined operator~(const BitsetInlined &bs) noexcept
Bitwise operators.
Definition bitset.h:439
BitsetInlined & flip() noexcept
Flips all bits.
Definition bitset.h:373
BitsetInlined & flip(uint32_t pos)
Flips (toggles) the bit at position pos.
Definition bitset.h:354
A Bitset implementation with inline storage that can grow if needed.
Definition bitset.h:225
uint32_t size() const noexcept
bool test(uint32_t pos) const noexcept
void set(uint32_t pos) noexcept
A dynamic bitset implementation that can be resized at runtime.
BitsetInlined< N > bitset
Definition bitset.h:15
constexpr remove_reference< T >::type && move(T &&t) noexcept
BitsetFixed< N > bitset_fixed
Definition bitset.h:18
Implements a simple red square effect for 2D LED grids.
Definition crgb.h:16
Proxy & operator=(bool value)
Definition bitset.h:38
Proxy(BitsetFixed &bitset, uint32_t pos)
Definition bitset.h:36
BitsetFixed & _bitset
Definition bitset.h:33
BitsetInlined & _bitset
Definition bitset.h:233
Proxy(BitsetInlined &bitset, uint32_t pos)
Definition bitset.h:236
Proxy & operator=(bool value)
Definition bitset.h:239