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