Skip to main content

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