A simple fixed-size Bitset implementation similar to std::Bitset.
Constructs a BitsetFixed with all bits reset.
Resets all bits to zero.
Sets or clears the bit at position pos.
Clears the bit at position pos.
Flips (toggles) the bit at position pos.
Flips all bits.
Tests whether the bit at position pos is set.
Returns the value of the bit at position pos.
Returns the number of set bits.
Queries.
Size of the BitsetFixed (number of bits).
Finds the first bit that matches the test value. Returns the index of the first matching bit, or -1 if none found.
Finds the first run of consecutive bits that match the test value. Returns the index of the first bit in the run, or -1 if no run found.
Friend operators for convenience.
A Bitset implementation with inline storage that can grow if needed. T is the storage type (u8, u16, u32, uint64_t) N is the initial number of bits to store inline
Constructs a Bitset with all bits reset.
Resets all bits to zero.
Sets or clears the bit at position pos.
Clears the bit at position pos.
Flips (toggles) the bit at position pos.
Flips all bits.
Tests whether the bit at position pos is set.
Returns the value of the bit at position pos.
Returns the number of set bits.
Queries.
Size of the Bitset (number of bits).
Finds the first bit that matches the test value. Returns the index of the first matching bit, or -1 if none found.
19 {
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>;
28
29template <fl::u32 N>
30using bitset_fixed = BitsetFixed<N>;
31
32
33
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
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
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) {
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) {
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
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 {
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
153 for (fl::u32 i = 0; i < block_count - 1; ++i) {
154 cnt += fl::popcount(_blocks[i]);
155 }
156
157
158
159 if (block_count > 0) {
160 block_type last_block = _blocks[block_count - 1];
161
162
163 if (N % bits_per_block != 0) {
164 const fl::u32 valid_bits = N % bits_per_block;
165
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
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
192 if (block_count > 0) {
193 block_type mask;
194 if (N % bits_per_block != 0) {
195
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
241 return -1;
242 }
243
244
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
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
261 if (!test_value) {
262 current_block = ~current_block;
263 }
264
265
266 if (block_idx == start_block && start_bit > 0) {
267 current_block &= ~((block_type(1) << start_bit) - 1);
268 }
269
270
271 if (current_block != 0) {
272
273 fl::u32 bit_pos = fl::countr_zero(current_block);
274 fl::u32 absolute_pos = block_idx * bits_per_block + bit_pos;
275
276
277 if (absolute_pos < N) {
278 return static_cast<fl::i32>(absolute_pos);
279 }
280 }
281 }
282
283 return -1;
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;
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>
334class BitsetInlined {
335 private:
336
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
405 if (_storage.template is<bitset_dynamic>()) {
406
407 fixed_bitset fixed;
408 bitset_dynamic *dynamic =
409 _storage.template ptr<bitset_dynamic>();
410
411
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
422 if (_storage.template is<fixed_bitset>()) {
423
424 bitset_dynamic dynamic(new_size);
425 fixed_bitset *fixed = _storage.template ptr<fixed_bitset>();
426
427
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
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>()) {
446 }
447
448 if (_storage.template is<fixed_bitset>()) {
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>()) {
468 }
469
470 if (_storage.template is<fixed_bitset>()) {
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
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
584 *result._storage.template ptr<fixed_bitset>() &=
585 *rhs._storage.template ptr<fixed_bitset>();
586 } else {
587
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
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
609 *result._storage.template ptr<fixed_bitset>() |=
610 *rhs._storage.template ptr<fixed_bitset>();
611 } else {
612
613 fl::u32 max_size =
614 result.size() > rhs.size() ? result.size() : rhs.size();
615
616
617 if (result.size() < max_size) {
618 result.resize(max_size);
619 }
620
621
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
639 *result._storage.template ptr<fixed_bitset>() ^=
640 *rhs._storage.template ptr<fixed_bitset>();
641 } else {
642
643 fl::u32 max_size =
644 result.size() > rhs.size() ? result.size() : rhs.size();
645
646
647 if (result.size() < max_size) {
648 result.resize(max_size);
649 }
650
651
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}
FASTLED_FORCE_INLINE CRGB operator|(const CRGB &p1, const CRGB &p2)
Combine two CRGB objects, taking the largest value of each channel.
FASTLED_FORCE_INLINE CRGB operator&(const CRGB &p1, const CRGB &p2)
Combine two CRGB objects, taking the smallest value of each channel.
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)
constexpr remove_reference< T >::type && move(T &&t) noexcept