FastLED 3.9.15
Loading...
Searching...
No Matches

◆ FL_DISABLE_WARNING()

FL_DISABLE_WARNING_PUSH FL_DISABLE_WARNING ( double- promotion)

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.

Bitwise AND

Bitwise OR

Bitwise XOR

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.

Parameters
test_valueThe value to search for (true or false)
offsetStarting position to search from (default: 0)

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.

Parameters
test_valueThe value to search for (true or false)
min_lengthMinimum length of the run (default: 1)
offsetStarting position to search from (default: 0)

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.

Resizes the Bitset if needed

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).

Convert bitset to string representation

Finds the first bit that matches the test value. Returns the index of the first matching bit, or -1 if none found.

Parameters
test_valueThe value to search for (true or false)
offsetStarting position to search from (default: 0)

Bitwise operators

Definition at line 12 of file bitset.h.

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>; // 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
uint8_t pos
Definition Blur.ino:11
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

References FL_DISABLE_WARNING_FLOAT_CONVERSION, FL_DISABLE_WARNING_IMPLICIT_INT_CONVERSION, fl::move(), offset(), operator&(), operator|(), pos, fl::detail::to_string(), and fl::to_string().

+ Here is the call graph for this function: