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 "fl/stdint.h"
7#include "fl/int.h"
8
10
12FL_DISABLE_WARNING(double-promotion)
13FL_DISABLE_WARNING(float-conversion)
14FL_DISABLE_WARNING(sign-conversion)
17
18
19namespace fl {
20
21template <fl::u32 N> class BitsetInlined;
22
23template <fl::u32 N> class BitsetFixed;
24
25class string;
26template <fl::u32 N = 16>
27using bitset = BitsetInlined<N>; // inlined but can go bigger.
28
29template <fl::u32 N>
30using bitset_fixed = BitsetFixed<N>; // fixed size, no dynamic allocation.
31
32
33// TODO: move this to fl/math.h
34template<typename IntType>
35inline fl::u8 popcount(IntType value) {
36 return static_cast<fl::u8>(__builtin_popcount(value));
37}
38
39template<typename IntType>
40inline fl::u8 countr_zero(IntType value) {
41 return static_cast<fl::u8>(__builtin_ctz(value));
42}
43
45template <fl::u32 N> class BitsetFixed {
46 private:
47 static_assert(sizeof(fl::u16) == 2, "u16 should be 2 bytes");
48 static constexpr fl::u32 bits_per_block = 8 * sizeof(fl::u16);
49 static constexpr fl::u32 block_count =
50 (N + bits_per_block - 1) / bits_per_block;
51 using block_type = fl::u16;
52
53 // Underlying blocks storing bits
54 block_type _blocks[block_count];
55
56 public:
57 struct Proxy {
58 BitsetFixed &_bitset;
59 fl::u32 _pos;
60
61 Proxy(BitsetFixed &bitset, fl::u32 pos) : _bitset(bitset), _pos(pos) {}
62
63 Proxy &operator=(bool value) {
64 _bitset.set(_pos, value);
65 return *this;
66 }
67
68 operator bool() const { return _bitset.test(_pos); }
69 };
70
71 Proxy operator[](fl::u32 pos) { return Proxy(*this, pos); }
72
74 constexpr BitsetFixed() noexcept : _blocks{} {}
75
76 void to_string(string* dst) const {
77 detail::to_string(_blocks, N, dst);
78 }
79
81 void reset() noexcept {
82 for (fl::u32 i = 0; i < block_count; ++i) {
83 _blocks[i] = 0;
84 }
85 }
86
88 BitsetFixed &set(fl::u32 pos, bool value = true) {
89 if (pos < N) {
90 const fl::u32 idx = pos / bits_per_block;
91 const fl::u32 off = pos % bits_per_block;
92 if (value) {
93 _blocks[idx] |= (block_type(1) << off);
94 } else {
95 _blocks[idx] &= ~(block_type(1) << off);
96 }
97 }
98 return *this;
99 }
100
101 void assign(fl::size n, bool value) {
102 if (n > N) {
103 n = N;
104 }
105 for (fl::size i = 0; i < n; ++i) {
106 set(i, value);
107 }
108 }
109
111 BitsetFixed &reset(fl::u32 pos) { return set(pos, false); }
112
114 BitsetFixed &flip(fl::u32 pos) {
115 if (pos < N) {
116 const fl::u32 idx = pos / bits_per_block;
117 const fl::u32 off = pos % bits_per_block;
118 _blocks[idx] ^= (block_type(1) << off);
119 }
120 return *this;
121 }
122
124 BitsetFixed &flip() noexcept {
125 for (fl::u32 i = 0; i < block_count; ++i) {
126 _blocks[i] = ~_blocks[i];
127 }
128 // Mask out unused high bits in the last block
129 if (N % bits_per_block != 0) {
130 const fl::u32 extra = bits_per_block - (N % bits_per_block);
131 _blocks[block_count - 1] &= (~block_type(0) >> extra);
132 }
133 return *this;
134 }
135
137 bool test(fl::u32 pos) const noexcept {
138 if (pos < N) {
139 const fl::u32 idx = pos / bits_per_block;
140 const fl::u32 off = pos % bits_per_block;
141 return (_blocks[idx] >> off) & 1;
142 }
143 return false;
144 }
145
147 bool operator[](fl::u32 pos) const noexcept { return test(pos); }
148
150 fl::u32 count() const noexcept {
151 fl::u32 cnt = 0;
152 // Count bits in all complete blocks
153 for (fl::u32 i = 0; i < block_count - 1; ++i) {
154 cnt += fl::popcount(_blocks[i]);
155 }
156
157 // For the last block, we need to be careful about counting only valid
158 // bits
159 if (block_count > 0) {
160 block_type last_block = _blocks[block_count - 1];
161 // If N is not a multiple of bits_per_block, mask out the unused
162 // bits
163 if (N % bits_per_block != 0) {
164 const fl::u32 valid_bits = N % bits_per_block;
165 // Create a mask with only the valid bits set to 1
166 block_type mask = (valid_bits == bits_per_block)
167 ? static_cast<block_type>(~block_type(0))
168 : ((block_type(1) << valid_bits) - 1);
169 last_block &= mask;
170 }
171 cnt += fl::popcount(last_block);
172 }
173
174 return cnt;
175 }
176
178 bool any() const noexcept { return count() > 0; }
179 bool none() const noexcept { return count() == 0; }
180 bool all() const noexcept {
181 if (N == 0)
182 return true;
183
184 // Check all complete blocks
185 for (fl::u32 i = 0; i < block_count - 1; ++i) {
186 if (_blocks[i] != ~block_type(0)) {
187 return false;
188 }
189 }
190
191 // Check the last block
192 if (block_count > 0) {
193 block_type mask;
194 if (N % bits_per_block != 0) {
195 // Create a mask for the valid bits in the last block
196 mask = (block_type(1) << (N % bits_per_block)) - 1;
197 } else {
198 mask = static_cast<block_type>(~block_type(0));
199 }
200
201 if ((_blocks[block_count - 1] & mask) != mask) {
202 return false;
203 }
204 }
205
206 return true;
207 }
208
210 BitsetFixed &operator&=(const BitsetFixed &other) noexcept {
211 for (fl::u32 i = 0; i < block_count; ++i) {
212 _blocks[i] &= other._blocks[i];
213 }
214 return *this;
215 }
217 BitsetFixed &operator|=(const BitsetFixed &other) noexcept {
218 for (fl::u32 i = 0; i < block_count; ++i) {
219 _blocks[i] |= other._blocks[i];
220 }
221 return *this;
222 }
224 BitsetFixed &operator^=(const BitsetFixed &other) noexcept {
225 for (fl::u32 i = 0; i < block_count; ++i) {
226 _blocks[i] ^= other._blocks[i];
227 }
228 return *this;
229 }
230
232 constexpr fl::u32 size() const noexcept { return N; }
233
238 fl::i32 find_first(bool test_value, fl::u32 offset = 0) const noexcept {
239 // If offset is beyond our size, no match possible
240 if (offset >= N) {
241 return -1;
242 }
243
244 // Calculate which block to start from
245 fl::u32 start_block = offset / bits_per_block;
246 fl::u32 start_bit = offset % bits_per_block;
247
248 for (fl::u32 block_idx = start_block; block_idx < block_count; ++block_idx) {
249 block_type current_block = _blocks[block_idx];
250
251 // For the last block, we need to mask out unused bits
252 if (block_idx == block_count - 1 && N % bits_per_block != 0) {
253 const fl::u32 valid_bits = N % bits_per_block;
254 block_type mask = (valid_bits == bits_per_block)
255 ? static_cast<block_type>(~block_type(0))
256 : ((block_type(1) << valid_bits) - 1);
257 current_block &= mask;
258 }
259
260 // If looking for false bits, invert the block
261 if (!test_value) {
262 current_block = ~current_block;
263 }
264
265 // For the first block, mask out bits before the offset
266 if (block_idx == start_block && start_bit > 0) {
267 current_block &= ~((block_type(1) << start_bit) - 1);
268 }
269
270 // If there are any matching bits in this block
271 if (current_block != 0) {
272 // Find the first set bit
273 fl::u32 bit_pos = fl::countr_zero(current_block);
274 fl::u32 absolute_pos = block_idx * bits_per_block + bit_pos;
275
276 // Make sure we haven't gone past the end of the bitset
277 if (absolute_pos < N) {
278 return static_cast<fl::i32>(absolute_pos);
279 }
280 }
281 }
282
283 return -1; // No matching bit found
284 }
285
291 fl::i32 find_run(bool test_value, fl::u32 min_length, fl::u32 offset = 0) const noexcept {
292 fl::u32 run_start = offset;
293 fl::u32 run_length = 0;
294
295 for (fl::u32 i = offset; i < N && run_length < min_length; ++i) {
296 bool current_bit = test(i);
297 if (current_bit != test_value) {
298 run_length = 0;
299 if (i + 1 < N) {
300 run_start = i + 1;
301 }
302 } else {
303 ++run_length;
304 }
305 }
306
307 if (run_length >= min_length) {
308 return static_cast<fl::i32>(run_start);
309 }
310
311 return -1; // No run found
312 }
313
315 friend BitsetFixed operator&(BitsetFixed lhs,
316 const BitsetFixed &rhs) noexcept {
317 return lhs &= rhs;
318 }
319 friend BitsetFixed operator|(BitsetFixed lhs,
320 const BitsetFixed &rhs) noexcept {
321 return lhs |= rhs;
322 }
323 friend BitsetFixed operator^(BitsetFixed lhs,
324 const BitsetFixed &rhs) noexcept {
325 return lhs ^= rhs;
326 }
327 friend BitsetFixed operator~(BitsetFixed bs) noexcept { return bs.flip(); }
328};
329
333template <fl::u32 N = 16> // Default size is 16 bits, or 2 bytes
334class BitsetInlined {
335 private:
336 // Either store a fixed Bitset<N> or a dynamic Bitset
337 using fixed_bitset = BitsetFixed<N>;
338 Variant<fixed_bitset, bitset_dynamic> _storage;
339
340 public:
341 struct Proxy {
342 BitsetInlined &_bitset;
343 fl::u32 _pos;
344
345 Proxy(BitsetInlined &bitset, fl::u32 pos)
346 : _bitset(bitset), _pos(pos) {}
347
348 Proxy &operator=(bool value) {
349 _bitset.set(_pos, value);
350 return *this;
351 }
352
353 operator bool() const { return _bitset.test(_pos); }
354 };
355
356 Proxy operator[](fl::u32 pos) { return Proxy(*this, pos); }
357
359 BitsetInlined() : _storage(fixed_bitset()) {}
360 BitsetInlined(fl::size size) : _storage(fixed_bitset()) {
361 if (size > N) {
362 _storage = bitset_dynamic(size);
363 }
364 }
365 BitsetInlined(const BitsetInlined &other) : _storage(other._storage) {}
366 BitsetInlined(BitsetInlined &&other) noexcept
367 : _storage(fl::move(other._storage)) {}
368 BitsetInlined &operator=(const BitsetInlined &other) {
369 if (this != &other) {
370 _storage = other._storage;
371 }
372 return *this;
373 }
374 BitsetInlined &operator=(BitsetInlined &&other) noexcept {
375 if (this != &other) {
376 _storage = fl::move(other._storage);
377 }
378 return *this;
379 }
380
382 void reset() noexcept {
383 if (_storage.template is<fixed_bitset>()) {
384 _storage.template ptr<fixed_bitset>()->reset();
385 } else {
386 _storage.template ptr<bitset_dynamic>()->reset();
387 }
388 }
389
390 void assign(fl::size n, bool value) {
391 resize(n);
392 if (_storage.template is<fixed_bitset>()) {
393 _storage.template ptr<fixed_bitset>()->assign(n, value);
394 } else {
395 _storage.template ptr<bitset_dynamic>()->assign(n, value);
396 }
397 }
398
399
400
402 void resize(fl::u32 new_size) {
403 if (new_size <= N) {
404 // If we're already using the fixed Bitset, nothing to do
405 if (_storage.template is<bitset_dynamic>()) {
406 // Convert back to fixed Bitset
407 fixed_bitset fixed;
408 bitset_dynamic *dynamic =
409 _storage.template ptr<bitset_dynamic>();
410
411 // Copy bits from dynamic to fixed
412 for (fl::u32 i = 0; i < N && i < dynamic->size(); ++i) {
413 if (dynamic->test(i)) {
414 fixed.set(i);
415 }
416 }
417
418 _storage = fixed;
419 }
420 } else {
421 // Need to use dynamic Bitset
422 if (_storage.template is<fixed_bitset>()) {
423 // Convert from fixed to dynamic
424 bitset_dynamic dynamic(new_size);
425 fixed_bitset *fixed = _storage.template ptr<fixed_bitset>();
426
427 // Copy bits from fixed to dynamic
428 for (fl::u32 i = 0; i < N; ++i) {
429 if (fixed->test(i)) {
430 dynamic.set(i);
431 }
432 }
433
434 _storage = dynamic;
435 } else {
436 // Already using dynamic, just resize
437 _storage.template ptr<bitset_dynamic>()->resize(new_size);
438 }
439 }
440 }
441
443 BitsetInlined &set(fl::u32 pos, bool value = true) {
444 if (pos >= N && _storage.template is<fixed_bitset>()) {
445 resize(pos + 1);
446 }
447
448 if (_storage.template is<fixed_bitset>()) {
449 if (pos < N) {
450 _storage.template ptr<fixed_bitset>()->set(pos, value);
451 }
452 } else {
453 if (pos >= _storage.template ptr<bitset_dynamic>()->size()) {
454 _storage.template ptr<bitset_dynamic>()->resize(pos + 1);
455 }
456 _storage.template ptr<bitset_dynamic>()->set(pos, value);
457 }
458 return *this;
459 }
460
462 BitsetInlined &reset(fl::u32 pos) { return set(pos, false); }
463
465 BitsetInlined &flip(fl::u32 pos) {
466 if (pos >= N && _storage.template is<fixed_bitset>()) {
467 resize(pos + 1);
468 }
469
470 if (_storage.template is<fixed_bitset>()) {
471 if (pos < N) {
472 _storage.template ptr<fixed_bitset>()->flip(pos);
473 }
474 } else {
475 if (pos >= _storage.template ptr<bitset_dynamic>()->size()) {
476 _storage.template ptr<bitset_dynamic>()->resize(pos + 1);
477 }
478 _storage.template ptr<bitset_dynamic>()->flip(pos);
479 }
480 return *this;
481 }
482
484 BitsetInlined &flip() noexcept {
485 if (_storage.template is<fixed_bitset>()) {
486 _storage.template ptr<fixed_bitset>()->flip();
487 } else {
488 _storage.template ptr<bitset_dynamic>()->flip();
489 }
490 return *this;
491 }
492
494 bool test(fl::u32 pos) const noexcept {
495 if (_storage.template is<fixed_bitset>()) {
496 return pos < N ? _storage.template ptr<fixed_bitset>()->test(pos)
497 : false;
498 } else {
499 return _storage.template ptr<bitset_dynamic>()->test(pos);
500 }
501 }
502
504 bool operator[](fl::u32 pos) const noexcept { return test(pos); }
505
507 fl::u32 count() const noexcept {
508 if (_storage.template is<fixed_bitset>()) {
509 return _storage.template ptr<fixed_bitset>()->count();
510 } else {
511 return _storage.template ptr<bitset_dynamic>()->count();
512 }
513 }
514
516 bool any() const noexcept {
517 if (_storage.template is<fixed_bitset>()) {
518 return _storage.template ptr<fixed_bitset>()->any();
519 } else {
520 return _storage.template ptr<bitset_dynamic>()->any();
521 }
522 }
523
524 bool none() const noexcept {
525 if (_storage.template is<fixed_bitset>()) {
526 return _storage.template ptr<fixed_bitset>()->none();
527 } else {
528 return _storage.template ptr<bitset_dynamic>()->none();
529 }
530 }
531
532 bool all() const noexcept {
533 if (_storage.template is<fixed_bitset>()) {
534 return _storage.template ptr<fixed_bitset>()->all();
535 } else {
536 return _storage.template ptr<bitset_dynamic>()->all();
537 }
538 }
539
541 fl::u32 size() const noexcept {
542 if (_storage.template is<fixed_bitset>()) {
543 return N;
544 } else {
545 return _storage.template ptr<bitset_dynamic>()->size();
546 }
547 }
548
550 void to_string(string* dst) const {
551 if (_storage.template is<fixed_bitset>()) {
552 _storage.template ptr<fixed_bitset>()->to_string(dst);
553 } else {
554 _storage.template ptr<bitset_dynamic>()->to_string(dst);
555 }
556 }
557
562 fl::i32 find_first(bool test_value, fl::u32 offset = 0) const noexcept {
563 if (_storage.template is<fixed_bitset>()) {
564 return _storage.template ptr<fixed_bitset>()->find_first(test_value, offset);
565 } else {
566 return _storage.template ptr<bitset_dynamic>()->find_first(test_value, offset);
567 }
568 }
569
571 friend BitsetInlined operator~(const BitsetInlined &bs) noexcept {
572 BitsetInlined result = bs;
573 result.flip();
574 return result;
575 }
576
577 friend BitsetInlined operator&(const BitsetInlined &lhs,
578 const BitsetInlined &rhs) noexcept {
579 BitsetInlined result = lhs;
580
581 if (result._storage.template is<fixed_bitset>() &&
582 rhs._storage.template is<fixed_bitset>()) {
583 // Both are fixed, use the fixed implementation
584 *result._storage.template ptr<fixed_bitset>() &=
585 *rhs._storage.template ptr<fixed_bitset>();
586 } else {
587 // At least one is dynamic, handle bit by bit
588 fl::u32 min_size =
589 result.size() < rhs.size() ? result.size() : rhs.size();
590 for (fl::u32 i = 0; i < min_size; ++i) {
591 result.set(i, result.test(i) && rhs.test(i));
592 }
593 // Clear any bits beyond the size of rhs
594 for (fl::u32 i = min_size; i < result.size(); ++i) {
595 result.reset(i);
596 }
597 }
598
599 return result;
600 }
601
602 friend BitsetInlined operator|(const BitsetInlined &lhs,
603 const BitsetInlined &rhs) noexcept {
604 BitsetInlined result = lhs;
605
606 if (result._storage.template is<fixed_bitset>() &&
607 rhs._storage.template is<fixed_bitset>()) {
608 // Both are fixed, use the fixed implementation
609 *result._storage.template ptr<fixed_bitset>() |=
610 *rhs._storage.template ptr<fixed_bitset>();
611 } else {
612 // At least one is dynamic, handle bit by bit
613 fl::u32 max_size =
614 result.size() > rhs.size() ? result.size() : rhs.size();
615
616 // Resize if needed
617 if (result.size() < max_size) {
618 result.resize(max_size);
619 }
620
621 // Set bits from rhs
622 for (fl::u32 i = 0; i < rhs.size(); ++i) {
623 if (rhs.test(i)) {
624 result.set(i);
625 }
626 }
627 }
628
629 return result;
630 }
631
632 friend BitsetInlined operator^(const BitsetInlined &lhs,
633 const BitsetInlined &rhs) noexcept {
634 BitsetInlined result = lhs;
635
636 if (result._storage.template is<fixed_bitset>() &&
637 rhs._storage.template is<fixed_bitset>()) {
638 // Both are fixed, use the fixed implementation
639 *result._storage.template ptr<fixed_bitset>() ^=
640 *rhs._storage.template ptr<fixed_bitset>();
641 } else {
642 // At least one is dynamic, handle bit by bit
643 fl::u32 max_size =
644 result.size() > rhs.size() ? result.size() : rhs.size();
645
646 // Resize if needed
647 if (result.size() < max_size) {
648 result.resize(max_size);
649 }
650
651 // XOR bits from rhs
652 for (fl::u32 i = 0; i < rhs.size(); ++i) {
653 result.set(i, result.test(i) != rhs.test(i));
654 }
655 }
656
657 return result;
658 }
659};
660
661} // namespace fl
662
uint8_t pos
Definition Blur.ino:11
#define FL_DISABLE_WARNING(warning)
#define FL_DISABLE_WARNING_IMPLICIT_INT_CONVERSION
#define FL_DISABLE_WARNING_PUSH
#define FL_DISABLE_WARNING_POP
#define FL_DISABLE_WARNING_FLOAT_CONVERSION
FASTLED_FORCE_INLINE CRGB operator|(const CRGB &p1, const CRGB &p2)
Combine two CRGB objects, taking the largest value of each channel.
Definition crgb.h:868
FASTLED_FORCE_INLINE CRGB operator&(const CRGB &p1, const CRGB &p2)
Combine two CRGB objects, taking the smallest value of each channel.
Definition crgb.h:860
UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
void to_string(const fl::u16 *bit_data, fl::u32 bit_count, string *dst)
Definition bitset.cpp:8
constexpr remove_reference< T >::type && move(T &&t) noexcept
Definition move.h:27
unsigned char u8
Definition int.h:17
string to_string(T value)
Definition str.h:957
IMPORTANT!
Definition crgb.h:20