Skip to main content

disk_core/unix_common/
bitmap.rs

1//! Bitmap utilities for Unix-like filesystems.
2//!
3//! Provides efficient bit-level operations on block and inode bitmaps.
4//! Uses little-endian bit order within each byte (bit 0 = LSB), matching
5//! the standard Linux bitmap convention used by ext2/3/4, xfs AG bitmaps,
6//! UFS cylinder group bitmaps, and Minix zone maps.
7
8/// A read-only view over a bitmap stored as a byte slice.
9///
10/// `bit_count` specifies the number of valid bits, which may be less than
11/// `data.len() * 8` (e.g. when the last byte is only partially used).
12pub struct BitmapReader<'a> {
13    data: &'a [u8],
14    bit_count: u64,
15}
16
17impl<'a> BitmapReader<'a> {
18    /// Create a new bitmap reader.
19    ///
20    /// `bit_count` is the number of valid bits. Bits beyond `bit_count`
21    /// (in the last partial byte) are ignored by counting and iteration methods.
22    pub fn new(data: &'a [u8], bit_count: u64) -> Self {
23        Self { data, bit_count }
24    }
25
26    /// Returns the total number of valid bits in this bitmap.
27    pub fn bit_count(&self) -> u64 {
28        self.bit_count
29    }
30
31    /// Test whether the bit at `index` is set (1).
32    ///
33    /// Returns `false` if `index` is out of range.
34    #[inline]
35    pub fn is_bit_set(&self, index: u64) -> bool {
36        if index >= self.bit_count {
37            return false;
38        }
39        let byte_idx = (index / 8) as usize;
40        let bit_idx = (index % 8) as u32;
41        if byte_idx >= self.data.len() {
42            return false;
43        }
44        self.data[byte_idx] & (1u8 << bit_idx) != 0
45    }
46
47    /// Count the number of set (1) bits in the valid range.
48    pub fn count_set_bits(&self) -> u64 {
49        if self.bit_count == 0 {
50            return 0;
51        }
52
53        let full_bytes = (self.bit_count / 8) as usize;
54        let remaining_bits = (self.bit_count % 8) as u32;
55
56        let mut count: u64 = 0;
57
58        // Count full bytes using popcount
59        for &byte in &self.data[..full_bytes] {
60            count += byte.count_ones() as u64;
61        }
62
63        // Handle the partial last byte
64        if remaining_bits > 0 && full_bytes < self.data.len() {
65            let mask = (1u8 << remaining_bits) - 1;
66            count += (self.data[full_bytes] & mask).count_ones() as u64;
67        }
68
69        count
70    }
71
72    /// Count the number of clear (0) bits in the valid range.
73    pub fn count_clear_bits(&self) -> u64 {
74        self.bit_count - self.count_set_bits()
75    }
76
77    /// Index (0..=7) of the most significant set bit of a **non-zero** byte.
78    ///
79    /// Widened to `u32` before `leading_zeros` deliberately, and it must stay
80    /// that way: mrustc's C backend lowers `leading_zeros` on integers narrower
81    /// than 32 bits as a *32-bit* count, so under it `0x01u8.leading_zeros()`
82    /// is 31 rather than 7 (and `0x80u8` gives 24 rather than 0; `u16` is wrong
83    /// the same way). The obvious `7 - byte.leading_zeros()` therefore wraps to
84    /// about 4.29e9 on any mrustc-built binary - which is how the PowerPC build
85    /// came to report every ext volume's last used block as the end of the
86    /// volume, silently imaging 32 MiB where the desktop imaged 4.5 MiB. It was
87    /// invisible because `count_ones` *is* lowered correctly, so bit *counts*
88    /// agreed everywhere and only positions were wrong.
89    ///
90    /// `u32`/`u64` `leading_zeros` are correct under mrustc, so this form is
91    /// identical on both compilers. Verified directly against mrustc, not
92    /// assumed.
93    #[inline]
94    fn top_set_bit(byte: u8) -> u32 {
95        debug_assert!(byte != 0, "top_set_bit requires a non-zero byte");
96        31 - (byte as u32).leading_zeros()
97    }
98
99    /// Find the index of the highest set bit, scanning from the end.
100    ///
101    /// Returns `None` if no bits are set.
102    pub fn highest_set_bit(&self) -> Option<u64> {
103        if self.bit_count == 0 {
104            return None;
105        }
106
107        let full_bytes = (self.bit_count / 8) as usize;
108        let remaining_bits = (self.bit_count % 8) as u32;
109
110        // Check partial last byte first
111        if remaining_bits > 0 && full_bytes < self.data.len() {
112            let mask = (1u8 << remaining_bits) - 1;
113            let masked = self.data[full_bytes] & mask;
114            if masked != 0 {
115                let top_bit = Self::top_set_bit(masked);
116                return Some(full_bytes as u64 * 8 + top_bit as u64);
117            }
118        }
119
120        // Scan full bytes from end to start
121        for i in (0..full_bytes).rev() {
122            if self.data[i] != 0 {
123                let top_bit = Self::top_set_bit(self.data[i]);
124                return Some(i as u64 * 8 + top_bit as u64);
125            }
126        }
127
128        None
129    }
130
131    /// Find the index of the highest **clear** (0) bit, scanning from the
132    /// end. Useful for "set = free" bitmaps (UFS / JFS / AFFS) where the
133    /// last-allocated position translates to the highest clear bit.
134    ///
135    /// Returns `None` if all bits in the valid range are set.
136    pub fn highest_clear_bit(&self) -> Option<u64> {
137        if self.bit_count == 0 {
138            return None;
139        }
140
141        let full_bytes = (self.bit_count / 8) as usize;
142        let remaining_bits = (self.bit_count % 8) as u32;
143
144        // Partial last byte first — invert before scanning so we can reuse
145        // the same `leading_zeros` trick.
146        if remaining_bits > 0 && full_bytes < self.data.len() {
147            let mask = (1u8 << remaining_bits) - 1;
148            let inv = (!self.data[full_bytes]) & mask;
149            if inv != 0 {
150                let top_bit = Self::top_set_bit(inv);
151                // `mask` guarantees `top_bit < remaining_bits`, so the
152                // result is already inside the valid range.
153                return Some(full_bytes as u64 * 8 + top_bit as u64);
154            }
155        }
156
157        for i in (0..full_bytes).rev() {
158            let inv = !self.data[i];
159            if inv != 0 {
160                let top_bit = Self::top_set_bit(inv);
161                return Some(i as u64 * 8 + top_bit as u64);
162            }
163        }
164
165        None
166    }
167
168    /// Find the index of the lowest clear (0) bit, scanning from the start.
169    ///
170    /// Returns `None` if all bits are set.
171    pub fn lowest_clear_bit(&self) -> Option<u64> {
172        if self.bit_count == 0 {
173            return None;
174        }
175
176        let full_bytes = (self.bit_count / 8) as usize;
177        let remaining_bits = (self.bit_count % 8) as u32;
178
179        // Scan full bytes
180        for i in 0..full_bytes {
181            if self.data[i] != 0xFF {
182                let bit = self.data[i].trailing_ones();
183                return Some(i as u64 * 8 + bit as u64);
184            }
185        }
186
187        // Check partial last byte
188        if remaining_bits > 0 && full_bytes < self.data.len() {
189            let mask = (1u8 << remaining_bits) - 1;
190            let masked = self.data[full_bytes] & mask;
191            if masked != mask {
192                // There's a clear bit in the valid range
193                let bit = masked.trailing_ones();
194                if bit < remaining_bits {
195                    return Some(full_bytes as u64 * 8 + bit as u64);
196                }
197            }
198        }
199
200        None
201    }
202
203    /// Iterate over the indices of all set (1) bits in ascending order.
204    pub fn iter_set_bits(&self) -> SetBitsIter<'_> {
205        SetBitsIter {
206            bitmap: self,
207            index: 0,
208        }
209    }
210
211    /// Iterate over the indices of all clear (0) bits in ascending order.
212    pub fn iter_clear_bits(&self) -> ClearBitsIter<'_> {
213        ClearBitsIter {
214            bitmap: self,
215            index: 0,
216        }
217    }
218}
219
220/// Iterator over set bit indices in a `BitmapReader`.
221pub struct SetBitsIter<'a> {
222    bitmap: &'a BitmapReader<'a>,
223    index: u64,
224}
225
226impl Iterator for SetBitsIter<'_> {
227    type Item = u64;
228
229    fn next(&mut self) -> Option<u64> {
230        while self.index < self.bitmap.bit_count {
231            let i = self.index;
232            self.index += 1;
233            if self.bitmap.is_bit_set(i) {
234                return Some(i);
235            }
236        }
237        None
238    }
239}
240
241/// Iterator over clear bit indices in a `BitmapReader`.
242pub struct ClearBitsIter<'a> {
243    bitmap: &'a BitmapReader<'a>,
244    index: u64,
245}
246
247impl Iterator for ClearBitsIter<'_> {
248    type Item = u64;
249
250    fn next(&mut self) -> Option<u64> {
251        while self.index < self.bitmap.bit_count {
252            let i = self.index;
253            self.index += 1;
254            if !self.bitmap.is_bit_set(i) {
255                return Some(i);
256            }
257        }
258        None
259    }
260}
261
262// --- Bitmap mutation helpers (for filesystem editing) ---
263
264/// Set the bit at `index` in a mutable bitmap (little-endian bit order).
265///
266/// Panics if `index` is out of range for the data slice.
267#[inline]
268pub fn bitmap_set_bit(data: &mut [u8], index: u64) {
269    let byte_idx = (index / 8) as usize;
270    let bit_idx = (index % 8) as u32;
271    data[byte_idx] |= 1u8 << bit_idx;
272}
273
274/// Clear the bit at `index` in a mutable bitmap (little-endian bit order).
275///
276/// Panics if `index` is out of range for the data slice.
277#[inline]
278pub fn bitmap_clear_bit(data: &mut [u8], index: u64) {
279    let byte_idx = (index / 8) as usize;
280    let bit_idx = (index % 8) as u32;
281    data[byte_idx] &= !(1u8 << bit_idx);
282}
283
284/// Find the first clear (0) bit in the bitmap within `bit_count` valid bits.
285///
286/// Returns `None` if all valid bits are set.
287pub fn bitmap_find_clear_bit(data: &[u8], bit_count: u64) -> Option<u64> {
288    let full_bytes = (bit_count / 8) as usize;
289    let remaining_bits = (bit_count % 8) as u32;
290
291    for (i, &byte) in data[..full_bytes].iter().enumerate() {
292        if byte != 0xFF {
293            let bit = byte.trailing_ones();
294            return Some(i as u64 * 8 + bit as u64);
295        }
296    }
297
298    if remaining_bits > 0 && full_bytes < data.len() {
299        let mask = (1u8 << remaining_bits) - 1;
300        let masked = data[full_bytes] & mask;
301        if masked != mask {
302            let bit = masked.trailing_ones();
303            if bit < remaining_bits {
304                return Some(full_bytes as u64 * 8 + bit as u64);
305            }
306        }
307    }
308
309    None
310}
311
312#[cfg(test)]
313mod tests {
314    use super::*;
315
316    /// `top_set_bit` must agree with a from-scratch bit scan for every byte.
317    ///
318    /// This pins the widening in `top_set_bit`. The natural-looking
319    /// `7 - byte.leading_zeros()` is correct on rustc and *wrong* on mrustc,
320    /// which lowers `leading_zeros` for sub-32-bit integers as a 32-bit count -
321    /// so `0x01u8` yields 31, the subtraction wraps to ~4.29e9, and every
322    /// caller silently believes the last used block is off the end of the
323    /// volume. A native `cargo test` cannot see that difference, so this test
324    /// guards the *shape* of the computation: it fails only if someone changes
325    /// the arithmetic itself, and the doc comment on `top_set_bit` explains why
326    /// the obvious simplification must not be applied.
327    #[test]
328    fn top_set_bit_matches_a_plain_scan_for_every_byte() {
329        for byte in 1u8..=255 {
330            let expected = (0..8u32).filter(|b| byte & (1 << b) != 0).max().unwrap();
331            assert_eq!(
332                BitmapReader::top_set_bit(byte),
333                expected,
334                "top_set_bit({byte:#04x})"
335            );
336        }
337    }
338
339    /// The whole-bitmap entry points, over every single-bit pattern: a wrong
340    /// top-bit calculation would show up as an off-by-N index here.
341    #[test]
342    fn highest_set_and_clear_bits_are_positionally_correct() {
343        for bit in 0..8u64 {
344            let data = [1u8 << bit];
345            let bm = BitmapReader::new(&data, 8);
346            assert_eq!(bm.highest_set_bit(), Some(bit), "highest_set_bit for {bit}");
347
348            // Inverse pattern: exactly one clear bit, at the same position.
349            let inv = [!(1u8 << bit)];
350            let bmi = BitmapReader::new(&inv, 8);
351            assert_eq!(
352                bmi.highest_clear_bit(),
353                Some(bit),
354                "highest_clear_bit for {bit}"
355            );
356        }
357
358        // Multi-byte: the highest set bit is in an earlier byte than the last.
359        let data = [0x00u8, 0b0001_0000, 0x00];
360        assert_eq!(
361            BitmapReader::new(&data, 24).highest_set_bit(),
362            Some(8 + 4),
363            "highest set bit should be byte 1, bit 4"
364        );
365    }
366
367    #[test]
368    fn test_single_byte_set_bits() {
369        let data = [0b10100101u8]; // bits 0,2,5,7 set
370        let bm = BitmapReader::new(&data, 8);
371        assert!(bm.is_bit_set(0));
372        assert!(!bm.is_bit_set(1));
373        assert!(bm.is_bit_set(2));
374        assert!(!bm.is_bit_set(3));
375        assert!(!bm.is_bit_set(4));
376        assert!(bm.is_bit_set(5));
377        assert!(!bm.is_bit_set(6));
378        assert!(bm.is_bit_set(7));
379        assert_eq!(bm.count_set_bits(), 4);
380        assert_eq!(bm.count_clear_bits(), 4);
381        assert_eq!(bm.highest_set_bit(), Some(7));
382    }
383
384    #[test]
385    fn test_partial_byte() {
386        let data = [0xFF];
387        let bm = BitmapReader::new(&data, 5); // only 5 bits valid
388        assert_eq!(bm.count_set_bits(), 5); // not 8
389        assert_eq!(bm.count_clear_bits(), 0);
390        assert_eq!(bm.highest_set_bit(), Some(4));
391    }
392
393    #[test]
394    fn test_partial_byte_with_clear_bits() {
395        // 0b11110011 = bits 0,1,4,5,6,7 set; bits 2,3 clear
396        let data = [0b11110011u8];
397        let bm = BitmapReader::new(&data, 6); // only bits 0-5 valid
398                                              // Valid bits: 0=1, 1=1, 2=0, 3=0, 4=1, 5=1 → 4 set
399        assert_eq!(bm.count_set_bits(), 4);
400        assert_eq!(bm.count_clear_bits(), 2);
401        assert_eq!(bm.highest_set_bit(), Some(5));
402    }
403
404    #[test]
405    fn test_iter_set_bits() {
406        let data = [0b00000101u8]; // bits 0, 2
407        let bm = BitmapReader::new(&data, 8);
408        let bits: Vec<u64> = bm.iter_set_bits().collect();
409        assert_eq!(bits, vec![0, 2]);
410    }
411
412    #[test]
413    fn test_iter_clear_bits() {
414        let data = [0b11111010u8]; // bits 1,3,4,5,6,7 set; bits 0,2 clear
415        let bm = BitmapReader::new(&data, 8);
416        let bits: Vec<u64> = bm.iter_clear_bits().collect();
417        assert_eq!(bits, vec![0, 2]);
418    }
419
420    #[test]
421    fn test_iter_set_bits_partial() {
422        let data = [0b11111111u8];
423        let bm = BitmapReader::new(&data, 3); // only bits 0,1,2 valid
424        let bits: Vec<u64> = bm.iter_set_bits().collect();
425        assert_eq!(bits, vec![0, 1, 2]);
426    }
427
428    #[test]
429    fn test_multi_byte() {
430        let data = [0xFF, 0x00, 0b00000011]; // byte 0: all set, byte 1: none, byte 2: bits 0,1
431        let bm = BitmapReader::new(&data, 24);
432        assert_eq!(bm.count_set_bits(), 10); // 8 + 0 + 2
433        assert_eq!(bm.count_clear_bits(), 14);
434        assert_eq!(bm.highest_set_bit(), Some(17)); // byte 2, bit 1 → index 16+1=17
435    }
436
437    #[test]
438    fn test_multi_byte_partial() {
439        let data = [0xFF, 0xFF, 0b00001111];
440        let bm = BitmapReader::new(&data, 20); // 16 full + 4 partial
441        assert_eq!(bm.count_set_bits(), 20); // 8+8+4
442        assert_eq!(bm.highest_set_bit(), Some(19));
443    }
444
445    #[test]
446    fn test_highest_set_bit_middle() {
447        let data = [0x00, 0b10000000, 0x00]; // only bit 15 set (byte 1, bit 7)
448        let bm = BitmapReader::new(&data, 24);
449        assert_eq!(bm.highest_set_bit(), Some(15));
450    }
451
452    #[test]
453    fn test_highest_set_bit_none() {
454        let data = [0x00, 0x00];
455        let bm = BitmapReader::new(&data, 16);
456        assert_eq!(bm.highest_set_bit(), None);
457    }
458
459    #[test]
460    fn test_highest_clear_bit_basic() {
461        // 0b10110101: bits 1, 3, 6 clear; bits 0,2,4,5,7 set.
462        let data = [0b10110101u8];
463        let bm = BitmapReader::new(&data, 8);
464        assert_eq!(bm.highest_clear_bit(), Some(6));
465    }
466
467    #[test]
468    fn test_highest_clear_bit_all_set() {
469        let data = [0xFF, 0xFF];
470        let bm = BitmapReader::new(&data, 16);
471        assert_eq!(bm.highest_clear_bit(), None);
472    }
473
474    #[test]
475    fn test_highest_clear_bit_partial() {
476        // Last byte partial: only bits 0,1,2 valid; byte is 0xFF, all set.
477        let data = [0xFE, 0xFF];
478        let bm = BitmapReader::new(&data, 11);
479        // Byte 0 has bit 0 clear → highest clear bit is 0.
480        assert_eq!(bm.highest_clear_bit(), Some(0));
481    }
482
483    #[test]
484    fn test_highest_clear_bit_partial_with_clear() {
485        // Byte 1 partial (bits 8,9,10 valid). Bit 10 set, bit 9 clear, bit 8 clear.
486        let data = [0xFF, 0b00000101]; // byte 1: bits 0, 2 set; bits 1, 3..7 clear
487        let bm = BitmapReader::new(&data, 11);
488        // Valid bits in byte 1: bit 0 (=set), 1 (=clear), 2 (=set). Highest clear = 1 → index 9.
489        assert_eq!(bm.highest_clear_bit(), Some(9));
490    }
491
492    #[test]
493    fn test_highest_clear_bit_middle_byte() {
494        // byte 0 = 0xFF (no clear), byte 1 = 0b01111111 (bit 7 clear),
495        // byte 2 = 0xFF.
496        let data = [0xFF, 0b01111111, 0xFF];
497        let bm = BitmapReader::new(&data, 24);
498        // Highest clear bit: byte 1 bit 7 = index 15.
499        assert_eq!(bm.highest_clear_bit(), Some(15));
500    }
501
502    #[test]
503    fn test_highest_clear_bit_empty() {
504        let data: [u8; 0] = [];
505        let bm = BitmapReader::new(&data, 0);
506        assert_eq!(bm.highest_clear_bit(), None);
507    }
508
509    #[test]
510    fn test_lowest_clear_bit() {
511        let data = [0xFF, 0b11111110]; // byte 0: all set, byte 1: bit 0 clear
512        let bm = BitmapReader::new(&data, 16);
513        assert_eq!(bm.lowest_clear_bit(), Some(8)); // first clear bit is at index 8
514    }
515
516    #[test]
517    fn test_lowest_clear_bit_first() {
518        let data = [0b11111110]; // bit 0 clear
519        let bm = BitmapReader::new(&data, 8);
520        assert_eq!(bm.lowest_clear_bit(), Some(0));
521    }
522
523    #[test]
524    fn test_lowest_clear_bit_none() {
525        let data = [0xFF, 0xFF];
526        let bm = BitmapReader::new(&data, 16);
527        assert_eq!(bm.lowest_clear_bit(), None);
528    }
529
530    #[test]
531    fn test_lowest_clear_bit_partial() {
532        // All bits set in the full byte, partial byte has a clear bit
533        let data = [0xFF, 0b11111011]; // bit 2 of byte 1 is clear
534        let bm = BitmapReader::new(&data, 12); // 8 full + 4 partial
535                                               // Valid bits in byte 1: 0=1, 1=1, 2=0, 3=1 → bit 10 is clear
536        assert_eq!(bm.lowest_clear_bit(), Some(10));
537    }
538
539    #[test]
540    fn test_empty_bitmap() {
541        let data = [];
542        let bm = BitmapReader::new(&data, 0);
543        assert_eq!(bm.count_set_bits(), 0);
544        assert_eq!(bm.count_clear_bits(), 0);
545        assert_eq!(bm.highest_set_bit(), None);
546        assert_eq!(bm.lowest_clear_bit(), None);
547        assert!(!bm.is_bit_set(0));
548        assert_eq!(bm.iter_set_bits().count(), 0);
549        assert_eq!(bm.iter_clear_bits().count(), 0);
550    }
551
552    #[test]
553    fn test_out_of_range() {
554        let data = [0xFF];
555        let bm = BitmapReader::new(&data, 8);
556        assert!(!bm.is_bit_set(8));
557        assert!(!bm.is_bit_set(100));
558    }
559
560    #[test]
561    fn test_bit_count() {
562        let data = [0xFF; 4];
563        let bm = BitmapReader::new(&data, 30);
564        assert_eq!(bm.bit_count(), 30);
565    }
566
567    #[test]
568    fn test_large_bitmap() {
569        // Simulate a 1024-block bitmap (128 bytes), all allocated
570        let data = vec![0xFF; 128];
571        let bm = BitmapReader::new(&data, 1024);
572        assert_eq!(bm.count_set_bits(), 1024);
573        assert_eq!(bm.count_clear_bits(), 0);
574        assert_eq!(bm.highest_set_bit(), Some(1023));
575        assert_eq!(bm.lowest_clear_bit(), None);
576    }
577
578    #[test]
579    fn test_large_bitmap_sparse() {
580        // 1024-bit bitmap with only every 8th bit set (bit 0 of each byte)
581        let data = vec![0x01; 128];
582        let bm = BitmapReader::new(&data, 1024);
583        assert_eq!(bm.count_set_bits(), 128);
584        assert_eq!(bm.count_clear_bits(), 896);
585        assert_eq!(bm.highest_set_bit(), Some(1016)); // byte 127, bit 0 → 127*8=1016
586        assert_eq!(bm.lowest_clear_bit(), Some(1)); // bit 1 of byte 0
587    }
588
589    #[test]
590    fn test_iter_set_bits_multi_byte() {
591        let data = [0b00000001, 0b10000000]; // bit 0 and bit 15
592        let bm = BitmapReader::new(&data, 16);
593        let bits: Vec<u64> = bm.iter_set_bits().collect();
594        assert_eq!(bits, vec![0, 15]);
595    }
596
597    #[test]
598    fn test_iter_clear_bits_all_set() {
599        let data = [0xFF];
600        let bm = BitmapReader::new(&data, 8);
601        let bits: Vec<u64> = bm.iter_clear_bits().collect();
602        assert!(bits.is_empty());
603    }
604
605    #[test]
606    fn test_iter_set_bits_all_clear() {
607        let data = [0x00];
608        let bm = BitmapReader::new(&data, 8);
609        let bits: Vec<u64> = bm.iter_set_bits().collect();
610        assert!(bits.is_empty());
611    }
612
613    // --- Mutation helper tests ---
614
615    #[test]
616    fn test_bitmap_set_bit() {
617        let mut data = [0u8; 2];
618        bitmap_set_bit(&mut data, 0);
619        assert_eq!(data[0], 0b00000001);
620        bitmap_set_bit(&mut data, 7);
621        assert_eq!(data[0], 0b10000001);
622        bitmap_set_bit(&mut data, 8);
623        assert_eq!(data[1], 0b00000001);
624        bitmap_set_bit(&mut data, 15);
625        assert_eq!(data[1], 0b10000001);
626    }
627
628    #[test]
629    fn test_bitmap_clear_bit() {
630        let mut data = [0xFF, 0xFF];
631        bitmap_clear_bit(&mut data, 0);
632        assert_eq!(data[0], 0b11111110);
633        bitmap_clear_bit(&mut data, 7);
634        assert_eq!(data[0], 0b01111110);
635        bitmap_clear_bit(&mut data, 8);
636        assert_eq!(data[1], 0b11111110);
637    }
638
639    #[test]
640    fn test_bitmap_set_clear_roundtrip() {
641        let mut data = [0u8; 4];
642        for i in 0..32u64 {
643            bitmap_set_bit(&mut data, i);
644        }
645        assert_eq!(data, [0xFF; 4]);
646        for i in 0..32u64 {
647            bitmap_clear_bit(&mut data, i);
648        }
649        assert_eq!(data, [0x00; 4]);
650    }
651
652    #[test]
653    fn test_bitmap_find_clear_bit_basic() {
654        let data = [0xFF, 0b11111110]; // first clear bit is 8
655        assert_eq!(bitmap_find_clear_bit(&data, 16), Some(8));
656    }
657
658    #[test]
659    fn test_bitmap_find_clear_bit_first() {
660        let data = [0b11111110]; // bit 0 clear
661        assert_eq!(bitmap_find_clear_bit(&data, 8), Some(0));
662    }
663
664    #[test]
665    fn test_bitmap_find_clear_bit_none() {
666        let data = [0xFF, 0xFF];
667        assert_eq!(bitmap_find_clear_bit(&data, 16), None);
668    }
669
670    #[test]
671    fn test_bitmap_find_clear_bit_partial() {
672        let data = [0xFF, 0b00001111]; // bits 12-15 are clear, but only 12 valid
673        assert_eq!(bitmap_find_clear_bit(&data, 12), None); // all 12 valid bits are set
674    }
675
676    #[test]
677    fn test_bitmap_find_clear_bit_in_partial() {
678        let data = [0xFF, 0b11111011]; // bit 10 clear
679        assert_eq!(bitmap_find_clear_bit(&data, 12), Some(10));
680    }
681}