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, rhs: Self) -> Self::Output {
190        Self(self.0 | rhs.0, self.1 | rhs.1)
191    }
192}
193
194impl BitAnd for BitSet128 {
195    type Output = Self;
196    fn bitand(self, rhs: Self) -> Self::Output {
197        Self(self.0 & rhs.0, self.1 & rhs.1)
198    }
199}
200
201impl BitXor for BitSet128 {
202    type Output = Self;
203    fn bitxor(self, rhs: Self) -> Self::Output {
204        Self(self.0 ^ rhs.0, self.1 ^ rhs.1)
205    }
206}
207
208impl BitOrAssign for BitSet128 {
209    fn bitor_assign(&mut self, rhs: Self) {
210        self.0 |= rhs.0;
211        self.1 |= rhs.1;
212    }
213}
214
215impl BitAndAssign for BitSet128 {
216    fn bitand_assign(&mut self, rhs: Self) {
217        self.0 &= rhs.0;
218        self.1 &= rhs.1;
219    }
220}
221
222impl BitXorAssign for BitSet128 {
223    fn bitxor_assign(&mut self, rhs: Self) {
224        self.0 ^= rhs.0;
225        self.1 ^= rhs.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#[derive(Debug, Default, Eq, PartialEq)]
275pub struct BitSet128Iter(u64, u64);
276
277/// Consuming iterator.
278impl Iterator for BitSet128Iter {
279    type Item = u8;
280
281    fn next(&mut self) -> Option<Self::Item> {
282        if self.0 == 0 {
283            if self.1 == 0 {
284                None
285            } else {
286                // Find the index of the least significant bit set to 1
287                #[allow(clippy::cast_possible_truncation)]
288                let index = self.1.trailing_zeros() as u8;
289                // Clear the least significant bit to prep for next iteration
290                self.1 &= self.1 - 1;
291                Some(index + 64)
292            }
293        } else {
294            // Find the index of the least significant bit set to 1
295            #[allow(clippy::cast_possible_truncation)]
296            let index = self.0.trailing_zeros() as u8;
297            // Clear the least significant bit to prep for next iteration
298            self.0 &= self.0 - 1;
299            Some(index)
300        }
301    }
302
303    #[inline]
304    fn size_hint(&self) -> (usize, Option<usize>) {
305        // We know exactly how many bits are left to yield at any moment!
306        let len = (self.0.count_ones() + self.1.count_ones()) as usize;
307        (len, Some(len))
308    }
309}
310
311// Implementing ExactSizeIterator unlocks additional optimizations automatically
312impl ExactSizeIterator for BitSet128Iter {}
313impl core::iter::FusedIterator for BitSet128Iter {}
314
315/// Non-consuming iterator for bitset reference.
316impl IntoIterator for &BitSet128 {
317    type Item = u8;
318    type IntoIter = BitSet128Iter;
319
320    fn into_iter(self) -> Self::IntoIter {
321        // Since `BitSet128` is `Copy` and just a `(u64, u64)`,
322        // we just pass the underlying value to the iterator.
323        BitSet128Iter(self.0, self.1)
324    }
325}
326
327/// Non-consuming iterator for bitset.
328impl IntoIterator for BitSet128 {
329    type Item = u8;
330    type IntoIter = BitSet128Iter;
331
332    fn into_iter(self) -> Self::IntoIter {
333        // Since `BitSet128` is `Copy` and just a `(u64, u64)`,
334        // we just pass the underlying value to the iterator.
335        BitSet128Iter(self.0, self.1)
336    }
337}
338
339/// From iterator for bitset.
340impl FromIterator<u8> for BitSet128 {
341    fn from_iter<I: IntoIterator<Item = u8>>(iter: I) -> Self {
342        let mut bitset = Self::new();
343        for index in iter {
344            bitset.set(index);
345        }
346        bitset
347    }
348}
349
350impl Extend<u8> for BitSet128 {
351    fn extend<I: IntoIterator<Item = u8>>(&mut self, iter: I) {
352        for index in iter {
353            self.set(index);
354        }
355    }
356}
357
358// **** fmt ****
359
360impl fmt::Binary for BitSet128 {
361    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362        // Handle the "0b" prefix if requested via {:#b}
363        if f.alternate() {
364            f.write_str("0b")?;
365        }
366        // Print from high bits to low bits (left-to-right)
367        // High bits (127 down to 64)
368        for i in (0..64).rev() {
369            let val = (self.1 >> i) & 1;
370            write!(f, "{val}")?;
371        }
372        // Low bits (63 down to 0)
373        for i in (0..64).rev() {
374            let val = (self.0 >> i) & 1;
375            write!(f, "{val}")?;
376        }
377
378        Ok(())
379    }
380}
381
382impl fmt::UpperHex for BitSet128 {
383    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
384        if f.alternate() {
385            f.write_str("0x")?;
386        }
387        write!(f, "{:016X}{:016X}", self.1, self.0)
388    }
389}
390
391#[cfg(test)]
392mod tests {
393    use super::*;
394
395    fn is_normal<T: Sized + Send + Sync + Unpin>() {}
396    fn is_full<T: Sized + Send + Sync + Unpin + Copy + Clone + Default + PartialEq>() {}
397    #[cfg(feature = "serde")]
398    fn is_config<T: Serialize + for<'a> Deserialize<'a>>() {}
399
400    #[test]
401    fn normal_types() {
402        is_full::<BitSet128>();
403        #[cfg(feature = "serde")]
404        is_config::<BitSet128>();
405        is_normal::<BitSet128Iter>();
406    }
407    #[test]
408    fn new() {
409        let mut bits = BitSet128::new();
410        bits.set(42);
411        assert!(bits[42u8]);
412        assert!(bits.test(42));
413    }
414    #[allow(unused)]
415    #[test]
416    fn const_new() {
417        const FLAGS: BitSet128 = BitSet128::new();
418        const EMPTY_CHECK: bool = FLAGS.is_empty(); // Evaluated at compile time
419    }
420    #[test]
421    fn assign() {
422        let mut bits = BitSet128::new();
423        bits.set(42);
424        assert!(bits[42u8]);
425        assert!(bits.test(42));
426        let mask = bits;
427        assert!(mask.test(42));
428    }
429    #[test]
430    fn from() {
431        let _bits = BitSet128::from((0xab_u32, 0x12_u32));
432    }
433    #[test]
434    fn flip_all() {
435        let mut bitset = BitSet128::new();
436
437        // Alternating pattern test
438        bitset.set(0);
439        bitset.set(64);
440
441        bitset.flip_all();
442
443        // Bit 0 and 64 should now be 0, others should be 1
444        assert!(!bitset.test(0));
445        assert!(!bitset.test(64));
446        assert!(bitset.test(1));
447        assert!(bitset.test(65));
448
449        // Full cycle test (Empty -> Full -> Empty)
450        let mut empty_set = BitSet128::new();
451        empty_set.flip_all(); // Should become full
452
453        let mut full_set = BitSet128::new();
454        full_set.set_all();
455
456        assert_eq!(empty_set, full_set);
457
458        empty_set.flip_all(); // Should return to completely empty
459        assert!(empty_set.is_empty());
460    }
461    #[test]
462    fn leading_zeros() {
463        let mut bitset = BitSet128::new();
464
465        // Completely empty set should return 128 zeros
466        assert_eq!(bitset.leading_zeros(), 128);
467
468        // Setting the absolute most significant bit (index 127) leaves 0 leading zeros
469        bitset.set(127);
470        assert_eq!(bitset.leading_zeros(), 0);
471
472        // Setting index 126 leaves exactly 1 leading zero
473        bitset.reset_all();
474        bitset.set(126);
475        assert_eq!(bitset.leading_zeros(), 1);
476
477        // Test boundary exactly at the edge of the higher u64 (index 64)
478        bitset.reset_all();
479        bitset.set(64);
480        assert_eq!(bitset.leading_zeros(), 63);
481
482        // Test boundary exactly at the edge of the lower u64 (index 63)
483        bitset.reset_all();
484        bitset.set(63);
485        assert_eq!(bitset.leading_zeros(), 64);
486
487        // Setting the absolute lowest bit (index 0) leaves 127 leading zeros
488        bitset.reset_all();
489        bitset.set(0);
490        assert_eq!(bitset.leading_zeros(), 127);
491    }
492    #[test]
493    fn last_set() {
494        let mut bitset = BitSet128::new();
495
496        // Empty set should return None
497        assert_eq!(bitset.last_set(), None);
498
499        // Single lowest bit set
500        bitset.set(0);
501        assert_eq!(bitset.last_set(), Some(0));
502
503        // Multiple bits set, should return the highest one
504        bitset.set(10);
505        bitset.set(45);
506        assert_eq!(bitset.last_set(), Some(45));
507
508        // Boundary verification at the top of the lower u64
509        bitset.reset_all();
510        bitset.set(63);
511        assert_eq!(bitset.last_set(), Some(63));
512
513        // Boundary verification at the bottom of the higher u64
514        bitset.set(64);
515        assert_eq!(bitset.last_set(), Some(64));
516
517        // Multiple bits stretching into the high byte
518        bitset.set(100);
519        bitset.set(127);
520        assert_eq!(bitset.last_set(), Some(127));
521    }
522    #[test]
523    fn is_superset() {
524        let mut set_a = BitSet128::new();
525        let mut set_b = BitSet128::new();
526
527        // An empty set is always a superset of another empty set
528        assert!(set_a.is_superset(&set_b));
529
530        // Setup indices spanning across the 64-bit boundary
531        set_a.set(10);
532        set_a.set(80);
533
534        set_b.set(10);
535
536        // set_a has [10, 80], set_b has [10] -> Should be true
537        assert!(set_a.is_superset(&set_b));
538        // set_b is missing 80 -> Should be false
539        assert!(!set_b.is_superset(&set_a));
540
541        // Test exact match
542        set_b.set(80);
543        assert!(set_a.is_superset(&set_b));
544
545        // Test failure where lower matches but higher fails (Catches the original bug)
546        let mut set_c = BitSet128::new();
547        let mut set_d = BitSet128::new();
548        set_c.set(15); // Higher element (.1) is 0
549
550        set_d.set(15);
551        set_d.set(95); // Higher element (.1) is non-zero
552
553        // Lower halves match, but set_c is missing bit 95.
554        // Your old function would mistakenly return true here.
555        assert!(!set_c.is_superset(&set_d));
556    }
557    #[test]
558    fn intersects() {
559        let mut set_a = BitSet128::new();
560        let mut set_b = BitSet128::new();
561
562        // Empty sets should never intersect
563        assert!(!set_a.intersects(&set_b));
564
565        // Add an item to set_a only
566        set_a.set(15);
567        assert!(!set_a.intersects(&set_b));
568
569        // Match the item in set_b (Intersection in the lower u64 block)
570        set_b.set(15);
571        assert!(set_a.intersects(&set_b));
572        assert!(set_b.intersects(&set_a));
573
574        // Test disjoint sets across the 64-bit split boundary
575        set_a.reset_all();
576        set_b.reset_all();
577        set_a.set(10); // Lower u64 block
578        set_b.set(80); // Higher u64 block
579        assert!(!set_a.intersects(&set_b));
580
581        // Test intersection purely inside the higher u64 block (index >= 64)
582        set_a.set(80);
583        assert!(set_a.intersects(&set_b));
584    }
585    #[test]
586    fn inplace_logical_ops() {
587        let mut set_a = BitSet128::new();
588        let mut set_b = BitSet128::new();
589
590        // Setup overlapping patterns crossing 64-bit boundaries
591        set_a.set(10);
592        set_a.set(70);
593
594        set_b.set(10);
595        set_b.set(80);
596
597        // Test BitAndAssign (&=)
598        let mut result = set_a;
599        result &= set_b;
600        assert!(result.test(10));
601        assert!(!result.test(70));
602        assert!(!result.test(80));
603
604        // Test BitOrAssign (|=)
605        let mut result = set_a;
606        result |= set_b;
607        assert!(result.test(10));
608        assert!(result.test(70));
609        assert!(result.test(80));
610
611        // Test BitXorAssign (^=)
612        let mut result = set_a;
613        result ^= set_b;
614        assert!(!result.test(10)); // Both were 1, turns to 0
615        assert!(result.test(70));
616        assert!(result.test(80));
617
618        // Test and_not
619        let mut result = set_a;
620        result.and_not(set_b);
621        assert!(!result.test(10)); // Cleared by mask
622        assert!(result.test(70)); // Preserved
623        assert!(!result.test(80)); // Never present in set_a
624    }
625    #[test]
626    fn exercise() {
627        let mut system_flags = BitSet128::new();
628        let error_mask = BitSet128::new(); // imagine this has error bits set
629
630        // Combine with OR-assign
631        system_flags |= error_mask;
632
633        // Toggle bits with XOR-assign
634        system_flags ^= error_mask;
635
636        // Mask out bits with AND-assign
637        //system_flags &= BitSet128(0x0000_FFFF_FFFF_FFFF);
638
639        let mut set_a = BitSet128::new();
640        set_a.set(10);
641        set_a.set(20);
642
643        let mut set_b = BitSet128::new();
644        set_b.set(20);
645        set_b.set(30);
646
647        // Intersection (AND): only bit 20 remains
648        let common = set_a & set_b;
649        assert!(!common.test(10));
650        assert!(common.test(20));
651        assert!(!common.test(30));
652
653        // Union (OR): bits 10, 20, and 30 are set
654        let all = set_a | set_b;
655        assert!(all.test(10));
656        assert!(all.test(20));
657        assert!(all.test(30));
658
659        // Difference (XOR): bits 10 and 30 are set (20 is cancelled out)
660        let diff = set_a ^ set_b;
661        assert!(diff.test(10));
662        assert!(!diff.test(20));
663        assert!(diff.test(30));
664    }
665
666    #[test]
667    fn test_iterator_empty() {
668        let bitset = BitSet128::new();
669        let mut iter = bitset.iter();
670
671        assert_eq!(iter.size_hint(), (0, Some(0)));
672        assert_eq!(iter.next(), None);
673    }
674
675    #[test]
676    fn test_iterator_single_bits() {
677        // Test lower bound (index 0)
678        let mut bitset = BitSet128::new();
679        bitset.set(0);
680        let mut iter = bitset.iter();
681        assert_eq!(iter.next(), Some(0));
682        assert_eq!(iter.next(), None);
683
684        // Test boundary transition index (63)
685        bitset.reset_all();
686        bitset.set(63);
687        let mut iter = bitset.iter();
688        assert_eq!(iter.next(), Some(63));
689        assert_eq!(iter.next(), None);
690
691        // Test boundary transition index (64)
692        bitset.reset_all();
693        bitset.set(64);
694        let mut iter = bitset.iter();
695        assert_eq!(iter.next(), Some(64));
696        assert_eq!(iter.next(), None);
697
698        // Test upper bound (index 127)
699        bitset.reset_all();
700        bitset.set(127);
701        let mut iter = bitset.iter();
702        assert_eq!(iter.next(), Some(127));
703        assert_eq!(iter.next(), None);
704    }
705
706    #[test]
707    fn test_iterator_multiple_scattered_bits() {
708        let mut bitset = BitSet128::new();
709        // A stack-allocated expected sequence
710        let expected_indices = [5, 12, 63, 64, 99, 127];
711
712        for &idx in &expected_indices {
713            bitset.set(idx);
714        }
715
716        // Check size_hint tracks perfectly before iteration begins
717        let mut iter = bitset.iter();
718        assert_eq!(iter.size_hint(), (expected_indices.len(), Some(expected_indices.len())));
719
720        // Verify elements using a zero-allocation loop matching indices
721        for &expected in &expected_indices {
722            assert_eq!(iter.next(), Some(expected));
723        }
724        assert_eq!(iter.next(), None);
725    }
726
727    #[test]
728    fn test_iterator_size_hint_drainage() {
729        let mut bitset = BitSet128::new();
730        bitset.set(10);
731        bitset.set(90);
732
733        let mut iter = bitset.iter();
734        assert_eq!(iter.size_hint(), (2, Some(2)));
735        assert_eq!(iter.len(), 2); // Enabled by ExactSizeIterator
736
737        assert_eq!(iter.next(), Some(10));
738        assert_eq!(iter.size_hint(), (1, Some(1)));
739        assert_eq!(iter.len(), 1);
740
741        assert_eq!(iter.next(), Some(90));
742        assert_eq!(iter.size_hint(), (0, Some(0)));
743        assert_eq!(iter.len(), 0);
744
745        assert_eq!(iter.next(), None);
746    }
747
748    #[test]
749    fn test_iterator_all_bits_set() {
750        let mut bitset = BitSet128::new();
751        bitset.set_all();
752
753        let mut count = 0;
754        #[allow(clippy::cast_possible_truncation)]
755        for (expected_idx, actual_idx) in bitset.iter().enumerate() {
756            assert_eq!(expected_idx as u8, actual_idx);
757            count += 1;
758        }
759        assert_eq!(count, 128);
760    }
761
762    #[test]
763    fn test_consuming_into_iter() {
764        let mut bitset = BitSet128::new();
765        bitset.set(42);
766
767        let mut count = 0;
768        for idx in bitset {
769            assert_eq!(idx, 42);
770            count += 1;
771        }
772        assert_eq!(count, 1);
773    }
774
775    #[test]
776    fn from_iterator() {
777        // Collect from a concrete array slice
778        let indices = [5, 63, 64, 120];
779        let bitset: BitSet128 = indices.into_iter().collect();
780
781        assert!(bitset.test(5));
782        assert!(bitset.test(63));
783        assert!(bitset.test(64));
784        assert!(bitset.test(120));
785        assert_eq!(bitset.count_ones(), 4);
786
787        // Verify out-of-bounds values are ignored safely without panics
788        let invalid_indices = [20, 200, 255];
789        let safe_bitset: BitSet128 = invalid_indices.into_iter().collect();
790
791        assert!(safe_bitset.test(20));
792        assert_eq!(safe_bitset.count_ones(), 1); // Only index 20 should be set
793    }
794    #[test]
795    fn empty_and_full() {
796        let empty = BitSet128::new();
797        assert_eq!(empty.iter().count(), 0);
798
799        let mut full = BitSet128::new();
800        full.set_all();
801        assert_eq!(full.iter().count(), 128);
802    }
803}