Skip to main content

simple_bitset/
bitset64.rs

1use core::fmt;
2use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Index};
3#[cfg(feature = "serde")]
4use {
5    sequential_storage::map::PostcardValue,
6    serde::{Deserialize, Serialize},
7};
8
9/// A memory-efficient 64-bit set for embedded environments.
10// Note that it data is a singlet: this makes comparison with `BitSet128` duplet clearer.
11#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq)]
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13pub struct BitSet64(u64);
14
15impl BitSet64 {
16    /// Create a new empty bitset.
17    #[must_use]
18    pub const fn new() -> Self {
19        Self(0)
20    }
21}
22
23#[cfg(feature = "serde")]
24impl PostcardValue<'_> for BitSet64 {}
25
26impl Default for BitSet64 {
27    fn default() -> Self {
28        Self::new()
29    }
30}
31
32impl BitSet64 {
33    /// Resets all bits to 0.
34    #[inline]
35    pub fn reset_all(&mut self) {
36        self.0 = 0;
37    }
38
39    /// Resets the bit at `index` to 0. Does nothing if the index is out of bounds.
40    #[inline]
41    pub fn reset(&mut self, index: u8) {
42        if index < 64 {
43            self.0 &= !(1u64 << index);
44        }
45    }
46
47    /// Sets all bits to 1.
48    #[inline]
49    pub fn set_all(&mut self) {
50        self.0 = u64::MAX;
51    }
52
53    /// Sets the bit at `index` to 1. Does nothing if the index is out of bounds.
54    #[inline]
55    pub fn set(&mut self, index: u8) {
56        if index < 64 {
57            self.0 |= 1u64 << index;
58        }
59    }
60
61    /// Flips the bit at `index`. Does nothing if the index is out of bounds.
62    #[inline]
63    pub fn flip(&mut self, index: u8) {
64        if index < 64 {
65            self.0 ^= 1u64 << index;
66        }
67    }
68
69    /// Flips all bits in the bitset (0s become 1s, and 1s become 0s).
70    #[inline]
71    pub fn flip_all(&mut self) {
72        self.0 = !self.0;
73    }
74
75    /// In-place Difference / Mask-Clear. Clears any bits that are set in `other`.
76    /// This represents the mathematical operation: `self = self AND NOT other`.
77    #[inline]
78    pub fn and_not(&mut self, other: Self) {
79        self.0 &= !other.0;
80    }
81
82    /// Tests if the bit at `index` is 1.
83    /// Returns false if the bit is 0 or index is out of bounds.
84    #[inline]
85    #[must_use]
86    pub fn test(&self, index: u8) -> bool {
87        if index < 64 {
88            (self.0 & (1u64 << index)) != 0
89        } else {
90            false
91        }
92    }
93
94    /// Returns bits 0 to 31.
95    #[allow(clippy::cast_possible_truncation)]
96    #[inline]
97    #[must_use]
98    pub fn bits_0_31(&self) -> u32 {
99        self.0 as u32
100    }
101
102    /// Returns bits 32 to 63.
103    #[inline]
104    #[must_use]
105    pub fn bits_32_63(&self) -> u32 {
106        (self.0 >> 32) as u32
107    }
108
109    /// Returns the number of set bits (population count).
110    #[inline]
111    #[must_use]
112    pub const fn count_ones(&self) -> u32 {
113        self.0.count_ones()
114    }
115
116    /// Returns the number of leading zeros in the bitset,
117    /// counting from the most significant bit (index 63).
118    #[inline]
119    #[must_use]
120    pub const fn leading_zeros(&self) -> u32 {
121        self.0.leading_zeros()
122    }
123
124    /// Returns true if no bits are set.
125    #[inline]
126    #[must_use]
127    pub const fn is_empty(&self) -> bool {
128        self.0 == 0
129    }
130
131    /// Returns the highest index set, or None if the bitset is empty.
132    /// Useful for finding the "top" of a priority queue or resource map.
133    #[inline]
134    #[must_use]
135    pub const fn last_set(&self) -> Option<u8> {
136        if self.is_empty() {
137            None
138        } else {
139            // Cast is safe as (63 - leading) is guaranteed to be between 0 and 63.
140            #[allow(clippy::cast_possible_truncation)]
141            Some(63 - self.0.leading_zeros() as u8)
142        }
143    }
144
145    /// Returns true if this bitset contains all the bits set in `other`.
146    #[inline]
147    #[must_use]
148    pub const fn is_superset(&self, other: BitSet64) -> bool {
149        // A bitset is a superset if clearing any bits not present in self
150        // results in exactly the other bitset configuration for both halves.
151        (self.0 & other.0) == other.0
152    }
153
154    /// Returns true if this bitset is a subset of `other`.
155    #[inline]
156    #[must_use]
157    pub const fn is_subset(&self, other: BitSet64) -> bool {
158        other.is_superset(*self)
159    }
160
161    /// Returns true if this bitset shares at least one common set bit with `other`.
162    /// Returns false if there is no overlap or if either bitset is empty.
163    #[inline]
164    #[must_use]
165    pub const fn intersects(&self, other: Self) -> bool {
166        // Run a bitwise AND between bitsets.
167        // If the result is non-zero, an intersection exists.
168        self.0 & other.0 != 0
169    }
170
171    /// Returns an iterator over the indices of the set bits.
172    #[inline]
173    #[must_use]
174    pub fn iter(&self) -> BitSet64Iter {
175        self.into_iter()
176    }
177}
178
179// **** Bit operations ****
180
181impl BitOr for BitSet64 {
182    type Output = Self;
183    fn bitor(self, other: Self) -> Self::Output {
184        Self(self.0 | other.0)
185    }
186}
187
188impl BitAnd for BitSet64 {
189    type Output = Self;
190    fn bitand(self, other: Self) -> Self::Output {
191        Self(self.0 & other.0)
192    }
193}
194
195impl BitXor for BitSet64 {
196    type Output = Self;
197    fn bitxor(self, other: Self) -> Self::Output {
198        Self(self.0 ^ other.0)
199    }
200}
201
202impl BitOrAssign for BitSet64 {
203    fn bitor_assign(&mut self, other: Self) {
204        self.0 |= other.0;
205    }
206}
207
208impl BitAndAssign for BitSet64 {
209    fn bitand_assign(&mut self, other: Self) {
210        self.0 &= other.0;
211    }
212}
213
214impl BitXorAssign for BitSet64 {
215    fn bitxor_assign(&mut self, other: Self) {
216        self.0 ^= other.0;
217    }
218}
219
220impl Index<u8> for BitSet64 {
221    type Output = bool;
222
223    fn index(&self, index: u8) -> &Self::Output {
224        // We use static booleans because we must return a reference
225        if self.test(index) { &true } else { &false }
226    }
227}
228
229impl Index<usize> for BitSet64 {
230    type Output = bool;
231
232    #[allow(clippy::cast_possible_truncation)]
233    fn index(&self, index: usize) -> &Self::Output {
234        // We use static booleans because we must return a reference
235        if self.test(index as u8) { &true } else { &false }
236    }
237}
238
239/// `BitSet64` from `u32`.
240impl From<u32> for BitSet64 {
241    #[inline]
242    fn from(a: u32) -> Self {
243        Self(u64::from(a))
244    }
245}
246
247/// `BitSet64` from `(u32,u32)`.
248impl From<(u32, u32)> for BitSet64 {
249    #[inline]
250    fn from((a, b): (u32, u32)) -> Self {
251        Self(u64::from(a) << 32 | u64::from(b))
252    }
253}
254
255/// `BitSet64` from `u64`.
256impl From<u64> for BitSet64 {
257    #[inline]
258    fn from(a: u64) -> Self {
259        Self(a)
260    }
261}
262
263// **** Iter ****
264
265/// Iterator for `BitSet64`.
266#[derive(Debug, Default, Eq, PartialEq)]
267pub struct BitSet64Iter(u64);
268
269/// Consuming iterator.
270impl Iterator for BitSet64Iter {
271    type Item = u8;
272
273    fn next(&mut self) -> Option<Self::Item> {
274        if self.0 == 0 {
275            None
276        } else {
277            // Find the index of the least significant bit set to 1
278            #[allow(clippy::cast_possible_truncation)]
279            let index = self.0.trailing_zeros() as u8;
280            // Clear the least significant bit to prep for next iteration
281            self.0 &= self.0 - 1;
282            Some(index)
283        }
284    }
285
286    #[inline]
287    fn size_hint(&self) -> (usize, Option<usize>) {
288        // We know exactly how many bits are left to yield at any moment!
289        let len = self.0.count_ones() as usize;
290        (len, Some(len))
291    }
292}
293
294// Implementing ExactSizeIterator unlocks additional optimizations automatically
295impl ExactSizeIterator for BitSet64Iter {}
296impl core::iter::FusedIterator for BitSet64Iter {}
297
298/// Non-consuming iterator for bitset reference.
299impl IntoIterator for &BitSet64 {
300    type Item = u8;
301    type IntoIter = BitSet64Iter;
302
303    fn into_iter(self) -> Self::IntoIter {
304        // Since `BitSet64` is `Copy` and just a `(u64)`,
305        // we just pass the underlying value to the iterator.
306        BitSet64Iter(self.0)
307    }
308}
309
310/// Non-consuming iterator for bitset.
311impl IntoIterator for BitSet64 {
312    type Item = u8;
313    type IntoIter = BitSet64Iter;
314
315    fn into_iter(self) -> Self::IntoIter {
316        // Since `BitSet64` is `Copy` and just a `(u64)`,
317        // we just pass the underlying value to the iterator.
318        BitSet64Iter(self.0)
319    }
320}
321
322/// From iterator for bitset.
323impl FromIterator<u8> for BitSet64 {
324    fn from_iter<I: IntoIterator<Item = u8>>(iter: I) -> Self {
325        let mut bitset = Self::new();
326        for index in iter {
327            bitset.set(index);
328        }
329        bitset
330    }
331}
332
333impl Extend<u8> for BitSet64 {
334    fn extend<I: IntoIterator<Item = u8>>(&mut self, iter: I) {
335        for index in iter {
336            self.set(index);
337        }
338    }
339}
340
341// **** fmt ****
342
343impl fmt::Binary for BitSet64 {
344    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345        // Handle the "0b" prefix if requested via {:#b}
346        if f.alternate() {
347            f.write_str("0b")?;
348        }
349        // Print from high bits to low bits (left-to-right)
350        for i in (0..64).rev() {
351            let val = (self.0 >> i) & 1;
352            write!(f, "{val}")?;
353        }
354
355        Ok(())
356    }
357}
358
359impl fmt::UpperHex for BitSet64 {
360    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
361        if f.alternate() {
362            f.write_str("0x")?;
363        }
364        write!(f, "{:016X}", self.0)
365    }
366}
367
368#[cfg(test)]
369mod tests {
370    use super::*;
371
372    fn is_normal<T: Sized + Send + Sync + Unpin>() {}
373    fn is_full<T: Sized + Send + Sync + Unpin + Copy + Clone + Default + PartialEq>() {}
374    #[cfg(feature = "serde")]
375    fn is_config<T: Serialize + for<'a> Deserialize<'a>>() {}
376
377    #[test]
378    fn normal_types() {
379        is_full::<BitSet64>();
380        #[cfg(feature = "serde")]
381        is_config::<BitSet64>();
382        is_normal::<BitSet64Iter>();
383    }
384
385    #[test]
386    fn new() {
387        let mut bits = BitSet64::new();
388        bits.set(42);
389        assert!(bits[42u8]);
390        assert!(bits.test(42));
391    }
392
393    #[test]
394    fn const_new() {
395        const FLAGS: BitSet64 = BitSet64::new();
396        const EMPTY_CHECK: bool = FLAGS.is_empty(); // Evaluated at compile time
397        assert_eq!(0, FLAGS.0);
398        #[allow(clippy::assertions_on_constants)]
399        {
400            assert!(EMPTY_CHECK);
401        }
402    }
403
404    #[test]
405    fn assign() {
406        let mut bits = BitSet64::new();
407        bits.set(42);
408        assert!(bits[42u8]);
409        assert!(bits.test(42));
410        let mask = bits;
411        assert!(mask.test(42));
412    }
413
414    #[test]
415    fn from() {
416        let _bits = BitSet64::from((0xab_u32, 0x12_u32));
417    }
418
419    #[test]
420    fn flip_all() {
421        let mut bitset = BitSet64::new();
422
423        // Alternating pattern test
424        bitset.set(0);
425        bitset.set(32);
426
427        bitset.flip_all();
428
429        // Bit 0 and 64 should now be 0, others should be 1
430        assert!(!bitset.test(0));
431        assert!(!bitset.test(32));
432        assert!(bitset.test(1));
433        assert!(bitset.test(33));
434
435        // Full cycle test (Empty -> Full -> Empty)
436        let mut empty_set = BitSet64::new();
437        empty_set.flip_all(); // Should become full
438
439        let mut full_set = BitSet64::new();
440        full_set.set_all();
441
442        assert_eq!(empty_set, full_set);
443
444        empty_set.flip_all(); // Should return to completely empty
445        assert!(empty_set.is_empty());
446    }
447
448    #[test]
449    fn leading_zeros() {
450        let mut bitset = BitSet64::new();
451
452        // Completely empty set should return 64 zeros
453        assert_eq!(bitset.leading_zeros(), 64);
454
455        // Setting the absolute most significant bit (index 63) leaves 0 leading zeros
456        bitset.set(63);
457        assert_eq!(bitset.leading_zeros(), 0);
458
459        // Setting index 62 leaves exactly 1 leading zero
460        bitset.reset_all();
461        bitset.set(62);
462        assert_eq!(bitset.leading_zeros(), 1);
463    }
464
465    #[test]
466    fn last_set() {
467        let mut bitset = BitSet64::new();
468
469        // Empty set should return None
470        assert_eq!(bitset.last_set(), None);
471
472        // Single lowest bit set
473        bitset.set(0);
474        assert_eq!(bitset.last_set(), Some(0));
475
476        // Multiple bits set, should return the highest one
477        bitset.set(10);
478        bitset.set(45);
479        assert_eq!(bitset.last_set(), Some(45));
480
481        // Boundary verification at the top of the lower u64
482        bitset.reset_all();
483        bitset.set(63);
484        assert_eq!(bitset.last_set(), Some(63));
485    }
486
487    #[test]
488    fn is_superset() {
489        let mut set_a = BitSet64::new();
490        let mut set_b = BitSet64::new();
491
492        // An empty set is always a superset of another empty set
493        assert!(set_a.is_superset(set_b));
494
495        // Setup indices spanning across the 64-bit boundary
496        set_a.set(10);
497        set_a.set(60);
498
499        set_b.set(10);
500
501        // set_a has [10, 60], set_b has [10] -> Should be true
502        assert!(set_a.is_superset(set_b));
503        // set_b is missing 80 -> Should be false
504        assert!(!set_b.is_superset(set_a));
505
506        // Test exact match
507        set_b.set(60);
508        assert!(set_a.is_superset(set_b));
509
510        // Test failure where lower matches but higher fails (Catches the original bug)
511        let mut set_c = BitSet64::new();
512        let mut set_d = BitSet64::new();
513        set_c.set(15); // Higher element (.1) is 0
514
515        set_d.set(15);
516        set_d.set(55); // Higher element (.1) is non-zero
517
518        // Lower halves match, but set_c is missing bit 95.
519        // Your old function would mistakenly return true here.
520        assert!(!set_c.is_superset(set_d));
521    }
522
523    #[test]
524    fn intersects() {
525        let mut set_a = BitSet64::new();
526        let mut set_b = BitSet64::new();
527
528        // Empty sets should never intersect
529        assert!(!set_a.intersects(set_b));
530
531        // Add an item to set_a only
532        set_a.set(15);
533        assert!(!set_a.intersects(set_b));
534
535        // Match the item in set_b (Intersection in the lower u64 block)
536        set_b.set(15);
537        assert!(set_a.intersects(set_b));
538        assert!(set_b.intersects(set_a));
539
540        // Test disjoint sets across the 64-bit split boundary
541        set_a.reset_all();
542        set_b.reset_all();
543        set_a.set(10);
544        set_b.set(60);
545        assert!(!set_a.intersects(set_b));
546
547        // Test intersection purely inside the higher u64 block (index >= 64)
548        set_a.set(60);
549        assert!(set_a.intersects(set_b));
550    }
551
552    #[test]
553    fn inplace_logical_ops() {
554        let mut set_a = BitSet64::new();
555        let mut set_b = BitSet64::new();
556
557        // Setup overlapping patterns crossing 64-bit boundaries
558        set_a.set(10);
559        set_a.set(50);
560
561        set_b.set(10);
562        set_b.set(60);
563
564        // Test BitAndAssign (&=)
565        let mut result = set_a;
566        result &= set_b;
567        assert!(result.test(10));
568        assert!(!result.test(50));
569        assert!(!result.test(60));
570
571        // Test BitOrAssign (|=)
572        let mut result = set_a;
573        result |= set_b;
574        assert!(result.test(10));
575        assert!(result.test(50));
576        assert!(result.test(60));
577
578        // Test BitXorAssign (^=)
579        let mut result = set_a;
580        result ^= set_b;
581        assert!(!result.test(10)); // Both were 1, turns to 0
582        assert!(result.test(50));
583        assert!(result.test(60));
584
585        // Test and_not
586        let mut result = set_a;
587        result.and_not(set_b);
588        assert!(!result.test(10)); // Cleared by mask
589        assert!(result.test(50)); // Preserved
590        assert!(!result.test(60)); // Never present in set_a
591    }
592
593    #[test]
594    fn exercise() {
595        let mut system_flags = BitSet64::new();
596        let error_mask = BitSet64::new(); // imagine this has error bits set
597
598        // Combine with OR-assign
599        system_flags |= error_mask;
600
601        // Toggle bits with XOR-assign
602        system_flags ^= error_mask;
603
604        // Mask out bits with AND-assign
605        system_flags &= BitSet64(0x0000_FFFF_FFFF_FFFF);
606
607        let mut set_a = BitSet64::new();
608        set_a.set(10);
609        set_a.set(20);
610
611        let mut set_b = BitSet64::new();
612        set_b.set(20);
613        set_b.set(30);
614
615        // Intersection (AND): only bit 20 remains
616        let common = set_a & set_b;
617        assert!(!common.test(10));
618        assert!(common.test(20));
619        assert!(!common.test(30));
620
621        // Union (OR): bits 10, 20, and 30 are set
622        let all = set_a | set_b;
623        assert!(all.test(10));
624        assert!(all.test(20));
625        assert!(all.test(30));
626
627        // Difference (XOR): bits 10 and 30 are set (20 is cancelled out)
628        let diff = set_a ^ set_b;
629        assert!(diff.test(10));
630        assert!(!diff.test(20));
631        assert!(diff.test(30));
632    }
633
634    #[test]
635    fn iterator_consuming() {
636        let mut bits = BitSet64::new();
637        bits.set(2);
638        bits.set(10);
639
640        let mut sum = 0;
641        let mut count = 0;
642        for bit in &bits {
643            sum += bit;
644            count += 1;
645        }
646        assert_eq!(2, count);
647        assert_eq!(12, sum);
648    }
649
650    #[test]
651    fn into_iter_consuming() {
652        let mut bits = BitSet64::new();
653        bits.set(0);
654        bits.set(10);
655        bits.set(63);
656
657        // Consuming iterator
658        let mut iter = bits.into_iter();
659
660        assert_eq!(iter.next(), Some(0));
661        assert_eq!(iter.next(), Some(10));
662        assert_eq!(iter.next(), Some(63));
663        assert_eq!(iter.next(), None);
664
665        // bits is moved here and can no longer be used
666    }
667
668    #[test]
669    fn non_consuming_iterator() {
670        let bits = BitSet64(0b1101); // Bits 0, 2, and 3 are set
671
672        let mut sum = 0;
673        let mut count = 0;
674
675        // Use reference to bits rather than bits.iter()
676        for bit in &bits {
677            sum += bit;
678            count += 1;
679        }
680        assert_eq!(3, count);
681        assert_eq!(5, sum);
682
683        let mut sum = 0;
684        let mut count = 0;
685        // Using a reference in a loop
686        for bit in &bits {
687            sum += bit;
688            count += 1;
689        }
690        assert_eq!(3, count);
691        assert_eq!(5, sum);
692    }
693
694    #[test]
695    fn non_consuming_iterator2() {
696        let mut bits = BitSet64::new();
697        bits.set(5);
698        bits.set(12);
699
700        // Test using the .iter() method
701        let count = bits.iter().count();
702        assert_eq!(count, 2);
703
704        // Test using & reference in a for-loop
705        let mut last_val = 0;
706        for bit in &bits {
707            last_val = bit;
708        }
709        assert_eq!(last_val, 12);
710
711        // test original bits is still valid
712        assert!(bits.test(5));
713    }
714
715    #[test]
716    fn from_iterator() {
717        let indices = [1, 3, 5];
718        // collect() uses FromIterator
719        let bits: BitSet64 = indices.iter().copied().collect();
720
721        assert!(bits.test(1));
722        assert!(bits.test(3));
723        assert!(bits.test(5));
724        assert!(!bits.test(2));
725    }
726
727    #[test]
728    fn empty_and_full() {
729        let empty = BitSet64::new();
730        assert_eq!(empty.iter().count(), 0);
731
732        let mut full = BitSet64::new();
733        full.set_all();
734        assert_eq!(full.iter().count(), 64);
735    }
736}