1use std::convert::Infallible;
5use std::fmt::Display;
6use std::fmt::Formatter;
7use std::fmt::Result as FmtResult;
8use std::ops::BitAnd;
9use std::ops::BitOr;
10use std::ops::BitXor;
11use std::ops::Not;
12use std::ops::RangeBounds;
13
14use crate::Alignment;
15use crate::BitBufferMeta;
16use crate::BitBufferMut;
17use crate::Buffer;
18use crate::BufferMut;
19use crate::ByteBuffer;
20use crate::bit::BitChunks;
21use crate::bit::BitIndexIterator;
22use crate::bit::BitIterator;
23use crate::bit::BitSliceIterator;
24use crate::bit::UnalignedBitChunk;
25use crate::bit::collect_bool_word;
26use crate::bit::count_ones::count_ones;
27use crate::bit::get_bit_unchecked;
28use crate::bit::ops::bitwise_binary_op;
29use crate::bit::ops::bitwise_binary_op_lhs_owned;
30use crate::bit::ops::bitwise_unary_op;
31use crate::bit::ops::bitwise_unary_op_copy;
32use crate::bit::select::bit_select;
33use crate::buffer;
34
35#[derive(Debug, Clone, Eq)]
37#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
38pub struct BitBuffer {
39 buffer: ByteBuffer,
40 offset: usize,
44 len: usize,
45}
46
47const LIMIT_LEN: usize = 16;
48impl Display for BitBuffer {
49 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
50 let limit = f.precision().unwrap_or(LIMIT_LEN);
51 let buf: Vec<bool> = self.into_iter().take(limit).collect();
52 f.debug_struct("BitBuffer")
53 .field("len", &self.len)
54 .field("buffer", &buf)
55 .finish()
56 }
57}
58
59impl PartialEq for BitBuffer {
60 fn eq(&self, other: &Self) -> bool {
61 if self.len != other.len {
62 return false;
63 }
64
65 if self.len == 0 {
66 return true;
67 }
68
69 if self.offset == 0 && other.offset == 0 {
71 let full_bytes = self.len / 8;
72 let self_bytes = &self.buffer.as_slice()[..full_bytes];
73 let other_bytes = &other.buffer.as_slice()[..full_bytes];
74 if self_bytes != other_bytes {
75 return false;
76 }
77 let rem = self.len % 8;
79 if rem != 0 {
80 let mask = (1u8 << rem) - 1;
81 let a = self.buffer.as_slice()[full_bytes] & mask;
82 let b = other.buffer.as_slice()[full_bytes] & mask;
83 return a == b;
84 }
85 return true;
86 }
87
88 self.chunks()
89 .iter_padded()
90 .zip(other.chunks().iter_padded())
91 .all(|(a, b)| a == b)
92 }
93}
94
95impl BitBuffer {
96 #[inline]
100 pub fn new(buffer: ByteBuffer, len: usize) -> Self {
101 assert!(
102 buffer.len() * 8 >= len,
103 "provided ByteBuffer not large enough to back BoolBuffer with len {len}"
104 );
105
106 let buffer = buffer.aligned(Alignment::none());
108
109 Self {
110 buffer,
111 len,
112 offset: 0,
113 }
114 }
115
116 #[inline]
121 pub fn new_with_offset(buffer: ByteBuffer, len: usize, offset: usize) -> Self {
122 assert!(
123 len.saturating_add(offset) <= buffer.len().saturating_mul(8),
124 "provided ByteBuffer (len={}) not large enough to back BoolBuffer with offset {offset} len {len}",
125 buffer.len()
126 );
127
128 let buffer = buffer.aligned(Alignment::none());
130
131 let byte_offset = offset / 8;
133 let offset = offset % 8;
134 let buffer = if byte_offset != 0 {
135 buffer.slice(byte_offset..)
136 } else {
137 buffer
138 };
139
140 Self {
141 buffer,
142 offset,
143 len,
144 }
145 }
146
147 #[inline]
149 pub fn new_set(len: usize) -> Self {
150 let words = len.div_ceil(8);
151 let buffer = buffer![0xFF; words];
152
153 Self {
154 buffer,
155 len,
156 offset: 0,
157 }
158 }
159
160 #[inline]
162 pub fn new_unset(len: usize) -> Self {
163 let words = len.div_ceil(8);
164 let buffer = Buffer::zeroed(words);
165
166 Self {
167 buffer,
168 len,
169 offset: 0,
170 }
171 }
172
173 pub fn from_indices(len: usize, indices: impl IntoIterator<Item = usize>) -> BitBuffer {
175 BitBufferMut::from_indices(len, indices).freeze()
176 }
177
178 #[inline]
180 pub fn empty() -> Self {
181 Self::new_set(0)
182 }
183
184 #[inline]
186 pub fn full(value: bool, len: usize) -> Self {
187 if value {
188 Self::new_set(len)
189 } else {
190 Self::new_unset(len)
191 }
192 }
193
194 #[inline]
220 pub fn collect_bool<F: FnMut(usize) -> bool>(len: usize, f: F) -> Self {
221 BitBufferMut::collect_bool(len, f).freeze()
222 }
223
224 #[inline]
235 pub fn collect_bool_multiversioned<F: FnMut(usize) -> bool>(len: usize, f: F) -> Self {
236 BitBufferMut::collect_bool_multiversioned(len, f).freeze()
237 }
238
239 pub fn map_cmp<F>(&self, mut f: F) -> Self
244 where
245 F: FnMut(usize, bool) -> bool,
246 {
247 let len = self.len;
248 let mut buffer: BufferMut<u64> = BufferMut::with_capacity(len.div_ceil(64));
249
250 let chunks_count = len / 64;
251 let remainder = len % 64;
252 let chunks = self.chunks();
253
254 for (chunk_idx, src_chunk) in chunks.iter().enumerate() {
255 let packed = collect_bool_word(64, |bit_idx| {
256 let i = bit_idx + chunk_idx * 64;
257 let bit_value = (src_chunk >> bit_idx) & 1 == 1;
258 f(i, bit_value)
259 });
260
261 unsafe { buffer.push_unchecked(packed) }
263 }
264
265 if remainder != 0 {
266 let src_chunk = chunks.remainder_bits();
267 let packed = collect_bool_word(remainder, |bit_idx| {
268 let i = bit_idx + chunks_count * 64;
269 let bit_value = (src_chunk >> bit_idx) & 1 == 1;
270 f(i, bit_value)
271 });
272
273 unsafe { buffer.push_unchecked(packed) }
275 }
276
277 let mut bytes = buffer.into_byte_buffer();
278 bytes.truncate(len.div_ceil(8));
279
280 Self {
281 buffer: bytes.freeze(),
282 offset: 0,
283 len,
284 }
285 }
286
287 #[inline]
289 pub fn clear(&mut self) {
290 self.buffer.clear();
291 self.len = 0;
292 self.offset = 0;
293 }
294
295 #[inline]
300 pub fn len(&self) -> usize {
301 self.len
302 }
303
304 #[inline]
306 pub fn is_empty(&self) -> bool {
307 self.len() == 0
308 }
309
310 #[allow(clippy::inline_always)]
312 #[inline(always)]
313 pub fn offset(&self) -> usize {
314 self.offset
315 }
316
317 #[allow(clippy::inline_always)]
319 #[inline(always)]
320 pub fn inner(&self) -> &ByteBuffer {
321 &self.buffer
322 }
323
324 #[inline]
330 pub fn byte_aligned_bytes(&self) -> Option<&[u8]> {
331 if !self.offset.is_multiple_of(8) {
332 return None;
333 }
334
335 let n_bytes = self.len.div_ceil(8);
336 let start = self.offset / 8;
337 let end = start + n_bytes;
338 Some(&self.buffer.as_slice()[start..end])
339 }
340
341 #[inline]
347 pub fn value(&self, index: usize) -> bool {
348 assert!(index < self.len);
349 unsafe { self.value_unchecked(index) }
350 }
351
352 #[inline]
357 pub unsafe fn value_unchecked(&self, index: usize) -> bool {
358 unsafe { get_bit_unchecked(self.buffer.as_ptr(), index + self.offset) }
359 }
360
361 #[inline]
366 pub fn slice(&self, range: impl RangeBounds<usize>) -> Self {
367 let (byte_offset, meta) = BitBufferMeta::new(self.offset, self.len).slice(range);
368
369 let buffer = if byte_offset != 0 {
372 self.buffer.slice_unaligned(byte_offset..)
373 } else {
374 self.buffer.clone().aligned(Alignment::none())
375 };
376
377 Self {
378 buffer,
379 offset: meta.offset(),
380 len: meta.len(),
381 }
382 }
383
384 pub fn shrink_offset(self) -> Self {
386 let word_start = self.offset / 8;
387 let word_end = (self.offset + self.len).div_ceil(8);
388
389 let buffer = self.buffer.slice(word_start..word_end);
390
391 let bit_offset = self.offset % 8;
392 let len = self.len;
393 BitBuffer::new_with_offset(buffer, len, bit_offset)
394 }
395
396 #[inline]
398 pub fn unaligned_chunks(&self) -> UnalignedBitChunk<'_> {
399 UnalignedBitChunk::new(self.buffer.as_slice(), self.offset, self.len)
400 }
401
402 #[inline]
406 pub fn chunks(&self) -> BitChunks<'_> {
407 BitChunks::new(self.buffer.as_slice(), self.offset, self.len)
408 }
409
410 #[inline]
412 pub fn true_count(&self) -> usize {
413 count_ones(self.buffer.as_slice(), self.offset, self.len)
414 }
415
416 #[inline]
424 pub fn count_range(&self, start: usize, end: usize) -> usize {
425 assert!(start <= end, "start {start} exceeds end {end}");
426 assert!(end <= self.len, "end {end} exceeds len {}", self.len);
427 count_ones(self.buffer.as_slice(), self.offset + start, end - start)
428 }
429
430 #[inline]
437 pub fn select(&self, nth: usize) -> Option<usize> {
438 bit_select(self.buffer.as_slice(), self.offset, self.len, nth)
439 }
440
441 #[inline]
446 pub fn last_set_index(&self) -> Option<usize> {
447 let chunks = self.unaligned_chunks();
448 let lead = chunks.lead_padding();
449 let prefix_words = usize::from(chunks.prefix().is_some());
450
451 if let Some(word) = chunks.suffix()
452 && word != 0
453 {
454 let word_index = prefix_words + chunks.chunks().len();
455 return Some(word_index * 64 + 63 - word.leading_zeros() as usize - lead);
456 }
457
458 for (index, &word) in chunks.chunks().iter().enumerate().rev() {
459 if word != 0 {
460 let word_index = prefix_words + index;
461 return Some(word_index * 64 + 63 - word.leading_zeros() as usize - lead);
462 }
463 }
464
465 chunks.prefix().filter(|word| *word != 0).map(|word| {
466 debug_assert!(word.trailing_zeros() as usize >= lead);
467 63 - word.leading_zeros() as usize - lead
468 })
469 }
470
471 #[inline]
473 pub fn false_count(&self) -> usize {
474 self.len - self.true_count()
475 }
476
477 #[inline]
479 pub fn iter(&self) -> BitIterator<'_> {
480 BitIterator::new(self.buffer.as_slice(), self.offset, self.len)
481 }
482
483 #[inline]
485 pub fn set_indices(&self) -> BitIndexIterator<'_> {
486 BitIndexIterator::new(self.buffer.as_slice(), self.offset, self.len)
487 }
488
489 #[inline]
491 pub fn set_slices(&self) -> BitSliceIterator<'_> {
492 BitSliceIterator::new(self.buffer.as_slice(), self.offset, self.len)
493 }
494
495 #[inline]
504 pub fn for_each_set_index<F: FnMut(usize)>(&self, mut f: F) {
505 let Ok(()) = self.try_for_each_set_index(|index| {
506 f(index);
507 Ok::<_, Infallible>(())
508 });
509 }
510
511 #[inline]
515 pub fn try_for_each_set_index<E, F>(&self, mut f: F) -> Result<(), E>
516 where
517 F: FnMut(usize) -> Result<(), E>,
518 {
519 let mut base = 0usize;
520 for word in self.chunks().iter_padded() {
521 if word == u64::MAX {
522 for k in 0..64 {
523 f(base + k)?;
524 }
525 } else {
526 let mut w = word;
527 while w != 0 {
528 f(base + w.trailing_zeros() as usize)?;
529 w &= w - 1;
530 }
531 }
532 base += 64;
533 }
534
535 Ok(())
536 }
537
538 pub fn sliced(&self) -> Self {
540 if self.offset.is_multiple_of(8) {
541 return Self::new(
542 self.buffer
543 .slice(self.offset / 8..(self.offset + self.len).div_ceil(8)),
544 self.len,
545 );
546 }
547
548 bitwise_unary_op_copy(self, |a| a)
550 }
551}
552
553impl BitBuffer {
556 #[inline]
558 pub fn into_inner(self) -> (usize, usize, ByteBuffer) {
559 (self.offset, self.len, self.buffer)
560 }
561
562 #[inline]
564 pub fn try_into_mut(self) -> Result<BitBufferMut, Self> {
565 match self.buffer.try_into_mut() {
566 Ok(buffer) => Ok(BitBufferMut::from_buffer(buffer, self.offset, self.len)),
567 Err(buffer) => Err(BitBuffer::new_with_offset(buffer, self.len, self.offset)),
568 }
569 }
570}
571
572impl From<&[bool]> for BitBuffer {
573 fn from(value: &[bool]) -> Self {
574 BitBufferMut::from(value).freeze()
575 }
576}
577
578impl From<Vec<bool>> for BitBuffer {
579 fn from(value: Vec<bool>) -> Self {
580 BitBufferMut::from(value).freeze()
581 }
582}
583
584impl FromIterator<bool> for BitBuffer {
585 #[inline]
586 fn from_iter<T: IntoIterator<Item = bool>>(iter: T) -> Self {
587 BitBufferMut::from_iter(iter).freeze()
588 }
589}
590
591impl BitOr for BitBuffer {
592 type Output = Self;
593
594 #[inline]
595 fn bitor(self, rhs: Self) -> Self::Output {
596 bitwise_binary_op_lhs_owned(self, &rhs, |a, b| a | b)
597 }
598}
599
600impl BitOr for &BitBuffer {
601 type Output = BitBuffer;
602
603 #[inline]
604 fn bitor(self, rhs: Self) -> Self::Output {
605 bitwise_binary_op(self, rhs, |a, b| a | b)
606 }
607}
608
609impl BitOr<&BitBuffer> for BitBuffer {
610 type Output = BitBuffer;
611
612 #[inline]
613 fn bitor(self, rhs: &BitBuffer) -> Self::Output {
614 bitwise_binary_op_lhs_owned(self, rhs, |a, b| a | b)
615 }
616}
617
618impl BitAnd for &BitBuffer {
619 type Output = BitBuffer;
620
621 #[inline]
622 fn bitand(self, rhs: Self) -> Self::Output {
623 bitwise_binary_op(self, rhs, |a, b| a & b)
624 }
625}
626
627impl BitAnd<BitBuffer> for &BitBuffer {
628 type Output = BitBuffer;
629
630 #[inline]
631 fn bitand(self, rhs: BitBuffer) -> Self::Output {
632 self.bitand(&rhs)
633 }
634}
635
636impl BitAnd<&BitBuffer> for BitBuffer {
637 type Output = BitBuffer;
638
639 #[inline]
640 fn bitand(self, rhs: &BitBuffer) -> Self::Output {
641 bitwise_binary_op_lhs_owned(self, rhs, |a, b| a & b)
642 }
643}
644
645impl BitAnd<BitBuffer> for BitBuffer {
646 type Output = BitBuffer;
647
648 #[inline]
649 fn bitand(self, rhs: BitBuffer) -> Self::Output {
650 bitwise_binary_op_lhs_owned(self, &rhs, |a, b| a & b)
651 }
652}
653
654impl Not for &BitBuffer {
655 type Output = BitBuffer;
656
657 #[inline]
658 fn not(self) -> Self::Output {
659 bitwise_unary_op_copy(self, |a| !a)
662 }
663}
664
665impl Not for BitBuffer {
666 type Output = BitBuffer;
667
668 #[inline]
669 fn not(self) -> Self::Output {
670 bitwise_unary_op(self, |a| !a)
671 }
672}
673
674impl BitXor for &BitBuffer {
675 type Output = BitBuffer;
676
677 #[inline]
678 fn bitxor(self, rhs: Self) -> Self::Output {
679 bitwise_binary_op(self, rhs, |a, b| a ^ b)
680 }
681}
682
683impl BitXor<&BitBuffer> for BitBuffer {
684 type Output = BitBuffer;
685
686 #[inline]
687 fn bitxor(self, rhs: &BitBuffer) -> Self::Output {
688 bitwise_binary_op_lhs_owned(self, rhs, |a, b| a ^ b)
689 }
690}
691
692impl BitBuffer {
693 pub fn bitand_not(&self, rhs: &BitBuffer) -> BitBuffer {
698 bitwise_binary_op(self, rhs, |a, b| a & !b)
699 }
700
701 pub fn into_bitand_not(self, rhs: &BitBuffer) -> BitBuffer {
703 bitwise_binary_op_lhs_owned(self, rhs, |a, b| a & !b)
704 }
705
706 #[inline]
716 pub fn iter_bits<F>(&self, mut f: F)
717 where
718 F: FnMut(usize, bool),
719 {
720 let total_bits = self.len;
721 if total_bits == 0 {
722 return;
723 }
724
725 let chunks = self.chunks();
727 let chunks_count = total_bits / 64;
728 let remainder = total_bits % 64;
729
730 for (chunk_idx, chunk) in chunks.iter().enumerate() {
731 let base = chunk_idx * 64;
732 for bit_idx in 0..64 {
733 f(base + bit_idx, (chunk >> bit_idx) & 1 == 1);
734 }
735 }
736
737 if remainder != 0 {
738 let rem_chunk = chunks.remainder_bits();
739 let base = chunks_count * 64;
740 for bit_idx in 0..remainder {
741 f(base + bit_idx, (rem_chunk >> bit_idx) & 1 == 1);
742 }
743 }
744 }
745}
746
747impl<'a> IntoIterator for &'a BitBuffer {
748 type Item = bool;
749 type IntoIter = BitIterator<'a>;
750
751 fn into_iter(self) -> Self::IntoIter {
752 self.iter()
753 }
754}
755
756#[cfg(test)]
757mod tests {
758 use rstest::rstest;
759
760 use crate::ByteBuffer;
761 use crate::bit::BitBuffer;
762 use crate::buffer;
763
764 #[test]
765 fn test_bool() {
766 let buffer: ByteBuffer = buffer![1 << 7; 1024];
768 let bools = BitBuffer::new(buffer, 1024 * 8);
769
770 assert_eq!(bools.len(), 1024 * 8);
772 assert!(!bools.is_empty());
773 assert_eq!(bools.true_count(), 1024);
774 assert_eq!(bools.false_count(), 1024 * 7);
775
776 for word in 0..1024 {
778 for bit in 0..8 {
779 if bit == 7 {
780 assert!(bools.value(word * 8 + bit));
781 } else {
782 assert!(!bools.value(word * 8 + bit));
783 }
784 }
785 }
786
787 let sliced = bools.slice(64..72);
789
790 assert_eq!(sliced.len(), 8);
792 assert!(!sliced.is_empty());
793 assert_eq!(sliced.true_count(), 1);
794 assert_eq!(sliced.false_count(), 7);
795
796 for bit in 0..8 {
798 if bit == 7 {
799 assert!(sliced.value(bit));
800 } else {
801 assert!(!sliced.value(bit));
802 }
803 }
804 }
805
806 #[test]
807 fn test_padded_equaltiy() {
808 let buf1 = BitBuffer::new_set(64); let buf2 = BitBuffer::collect_bool(64, |x| x < 32); for i in 0..32 {
812 assert_eq!(buf1.value(i), buf2.value(i), "Bit {} should be the same", i);
813 }
814
815 for i in 32..64 {
816 assert_ne!(buf1.value(i), buf2.value(i), "Bit {} should differ", i);
817 }
818
819 assert_eq!(
820 buf1.slice(0..32),
821 buf2.slice(0..32),
822 "Buffer slices with same bits should be equal (`PartialEq` needs `iter_padded()`)"
823 );
824 assert_ne!(
825 buf1.slice(32..64),
826 buf2.slice(32..64),
827 "Buffer slices with different bits should not be equal (`PartialEq` needs `iter_padded()`)"
828 );
829 }
830
831 #[test]
832 fn test_slice_offset_calculation() {
833 let buf = BitBuffer::collect_bool(16, |_| true);
834 let sliced = buf.slice(10..16);
835 assert_eq!(sliced.len(), 6);
836 assert_eq!(sliced.offset(), 2);
838 }
839
840 #[test]
841 fn test_byte_aligned_bytes() {
842 let bytes: ByteBuffer = buffer![0b1010_0101u8, 0b0000_0011];
843 let buf = BitBuffer::new(bytes.clone(), 10);
844 assert_eq!(buf.byte_aligned_bytes(), Some(bytes.as_slice()));
845
846 let byte_sliced = buf.slice(8..10);
847 assert_eq!(byte_sliced.byte_aligned_bytes(), Some(&[0b0000_0011][..]));
848
849 let bit_sliced = buf.slice(1..9);
850 assert!(bit_sliced.byte_aligned_bytes().is_none());
851 }
852
853 #[test]
854 fn test_from_indices_dense_crosses_words() {
855 let len = 130;
856 let indices = (0..len).filter(|idx| idx % 3 != 1);
857 let buf = BitBuffer::from_indices(len, indices);
858
859 assert_eq!(buf.len(), len);
860 for idx in 0..len {
861 assert_eq!(buf.value(idx), idx % 3 != 1, "mismatch at {idx}");
862 }
863 }
864
865 #[test]
866 #[should_panic(expected = "index 5 exceeds len 5")]
867 fn test_from_indices_out_of_bounds() {
868 BitBuffer::from_indices(5, [0, 5]);
869 }
870
871 #[rstest]
872 #[case(0, 0, None)]
873 #[case(3, 7, None)]
874 #[case(0, 1, Some(0))]
875 #[case(8, 64, Some(63))]
876 #[case(13, 65, Some(0))]
877 #[case(13, 65, Some(64))]
878 #[case(67, 151, Some(97))]
879 #[case(67, 151, Some(150))]
880 fn last_set_index_handles_offsets_and_padding(
881 #[case] offset: usize,
882 #[case] len: usize,
883 #[case] expected: Option<usize>,
884 ) {
885 let backing = BitBuffer::from_iter(
886 std::iter::repeat_n(true, offset)
887 .chain((0..len).map(|index| Some(index) == expected))
888 .chain(std::iter::repeat_n(true, 7)),
889 );
890 let buffer = BitBuffer::new_with_offset(backing.inner().clone(), len, offset);
891
892 assert_eq!(buffer.last_set_index(), expected);
893 }
894
895 #[rstest]
896 #[case(5)]
897 #[case(8)]
898 #[case(10)]
899 #[case(13)]
900 #[case(16)]
901 #[case(23)]
902 #[case(100)]
903 fn test_iter_bits(#[case] len: usize) {
904 let buf = BitBuffer::collect_bool(len, |i| i % 2 == 0);
905
906 let mut collected = Vec::new();
907 buf.iter_bits(|idx, is_set| {
908 collected.push((idx, is_set));
909 });
910
911 assert_eq!(collected.len(), len);
912
913 for (idx, is_set) in collected {
914 assert_eq!(is_set, idx % 2 == 0);
915 }
916 }
917
918 #[rstest]
919 #[case(3, 5)]
920 #[case(3, 8)]
921 #[case(5, 10)]
922 #[case(2, 16)]
923 #[case(8, 16)]
924 #[case(9, 16)]
925 #[case(17, 16)]
926 fn test_iter_bits_with_offset(#[case] offset: usize, #[case] len: usize) {
927 let total_bits = offset + len;
928 let buf = BitBuffer::collect_bool(total_bits, |i| i % 2 == 0);
929 let buf_with_offset = BitBuffer::new_with_offset(buf.inner().clone(), len, offset);
930
931 let mut collected = Vec::new();
932 buf_with_offset.iter_bits(|idx, is_set| {
933 collected.push((idx, is_set));
934 });
935
936 assert_eq!(collected.len(), len);
937
938 for (idx, is_set) in collected {
939 assert_eq!(is_set, (offset + idx).is_multiple_of(2));
941 }
942 }
943
944 #[rstest]
945 #[case(8, 10)]
946 #[case(9, 7)]
947 #[case(16, 8)]
948 #[case(17, 10)]
949 fn test_iter_bits_catches_wrong_byte_offset(#[case] offset: usize, #[case] len: usize) {
950 let total_bits = offset + len;
951 let buf = BitBuffer::collect_bool(total_bits, |i| (i / 8) % 2 == 0);
953
954 let buf_with_offset = BitBuffer::new_with_offset(buf.inner().clone(), len, offset);
955
956 let mut collected = Vec::new();
957 buf_with_offset.iter_bits(|idx, is_set| {
958 collected.push((idx, is_set));
959 });
960
961 assert_eq!(collected.len(), len);
962
963 for (idx, is_set) in collected {
964 let bit_position = offset + idx;
965 let byte_index = bit_position / 8;
966 let expected_is_set = byte_index.is_multiple_of(2);
967
968 assert_eq!(
969 is_set, expected_is_set,
970 "Bit mismatch at index {}: expected {} got {}",
971 bit_position, expected_is_set, is_set
972 );
973 }
974 }
975
976 #[rstest]
977 #[case(5)]
978 #[case(8)]
979 #[case(10)]
980 #[case(64)]
981 #[case(65)]
982 #[case(100)]
983 #[case(128)]
984 fn test_map_cmp_identity(#[case] len: usize) {
985 let buf = BitBuffer::collect_bool(len, |i| i % 3 == 0);
987 let mapped = buf.map_cmp(|_idx, bit| bit);
988
989 assert_eq!(buf.len(), mapped.len());
990 for i in 0..len {
991 assert_eq!(buf.value(i), mapped.value(i), "Mismatch at index {}", i);
992 }
993 }
994
995 #[rstest]
996 #[case(5)]
997 #[case(8)]
998 #[case(64)]
999 #[case(65)]
1000 #[case(100)]
1001 fn test_map_cmp_negate(#[case] len: usize) {
1002 let buf = BitBuffer::collect_bool(len, |i| i % 2 == 0);
1004 let mapped = buf.map_cmp(|_idx, bit| !bit);
1005
1006 assert_eq!(buf.len(), mapped.len());
1007 for i in 0..len {
1008 assert_eq!(!buf.value(i), mapped.value(i), "Mismatch at index {}", i);
1009 }
1010 }
1011
1012 #[rstest]
1013 #[case(0, 0)]
1014 #[case(0, 64)]
1015 #[case(5, 70)]
1016 #[case(64, 130)]
1017 #[case(0, 200)]
1018 fn test_count_range(#[case] start: usize, #[case] end: usize) {
1019 let len = 200;
1020 let buf = BitBuffer::collect_bool(len, |i| i % 3 == 0);
1021 let expected = (start..end).filter(|i| i % 3 == 0).count();
1022 assert_eq!(buf.count_range(start, end), expected);
1023 assert_eq!(
1025 buf.count_range(start, end),
1026 buf.slice(start..end).true_count()
1027 );
1028 }
1029
1030 #[rstest]
1031 #[case(3)]
1032 #[case(7)]
1033 fn test_count_range_with_offset(#[case] offset: usize) {
1034 let len = 150;
1035 let buf = BitBuffer::collect_bool(offset + len, |i| i % 2 == 0);
1036 let view = BitBuffer::new_with_offset(buf.inner().clone(), len, offset);
1037 for (start, end) in [(0, len), (10, 100), (1, 2), (63, 129)] {
1038 let expected = (offset + start..offset + end)
1039 .filter(|i| i % 2 == 0)
1040 .count();
1041 assert_eq!(view.count_range(start, end), expected, "[{start}, {end})");
1042 }
1043 }
1044
1045 #[rstest]
1046 #[case(0)]
1047 #[case(1)]
1048 #[case(63)]
1049 #[case(64)]
1050 #[case(65)]
1051 #[case(200)]
1052 #[case(1000)]
1053 fn test_set_index_visitors_match_set_indices(#[case] len: usize) {
1054 let buf = BitBuffer::collect_bool(len, |i| i % 5 == 0 || i % 7 == 0);
1055 let expected: Vec<usize> = buf.set_indices().collect();
1056
1057 let mut got = Vec::new();
1058 buf.for_each_set_index(|i| got.push(i));
1059 assert_eq!(got, expected);
1060
1061 let mut fallible_got = Vec::new();
1062 let result = buf.try_for_each_set_index(|i| {
1063 fallible_got.push(i);
1064 Ok::<(), ()>(())
1065 });
1066 assert_eq!(result, Ok(()));
1067 assert_eq!(fallible_got, expected);
1068 }
1069
1070 #[rstest]
1071 #[case(3, 200)]
1072 #[case(7, 130)]
1073 fn test_for_each_set_index_with_offset(#[case] offset: usize, #[case] len: usize) {
1074 let base = BitBuffer::collect_bool(offset + len, |i| i % 3 == 0);
1075 let view = BitBuffer::new_with_offset(base.inner().clone(), len, offset);
1076 let expected: Vec<usize> = view.set_indices().collect();
1077 let mut got = Vec::new();
1078 view.for_each_set_index(|i| got.push(i));
1079 assert_eq!(got, expected);
1080 }
1081
1082 #[test]
1083 fn test_for_each_set_index_all_set() {
1084 let buf = BitBuffer::new_set(130);
1085 let mut got = Vec::new();
1086 buf.for_each_set_index(|i| got.push(i));
1087 assert_eq!(got, (0..130).collect::<Vec<_>>());
1088 }
1089
1090 #[test]
1091 fn test_try_for_each_set_index_stops_on_error() {
1092 for (buffer, stop) in [
1093 (BitBuffer::new_set(130), 65),
1094 (BitBuffer::collect_bool(130, |i| i % 3 == 0), 66),
1095 ] {
1096 let mut visited = Vec::new();
1097 let result = buffer.try_for_each_set_index(|index| {
1098 visited.push(index);
1099 if index == stop {
1100 return Err(index);
1101 }
1102
1103 Ok(())
1104 });
1105
1106 assert_eq!(result, Err(stop));
1107 assert_eq!(
1108 visited,
1109 buffer
1110 .set_indices()
1111 .take_while(|&i| i <= stop)
1112 .collect::<Vec<_>>()
1113 );
1114 }
1115 }
1116
1117 #[test]
1118 fn test_map_cmp_conditional() {
1119 let len = 100;
1121 let buf = BitBuffer::collect_bool(len, |i| i % 2 == 0);
1122
1123 let mapped = buf.map_cmp(|idx, bit| bit && idx % 4 == 0);
1125
1126 for i in 0..len {
1127 let expected = (i % 2 == 0) && (i % 4 == 0);
1128 assert_eq!(mapped.value(i), expected, "Mismatch at index {}", i);
1129 }
1130 }
1131}