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