1use crate::util::bit_util::ceil;
21use std::fmt::Debug;
22
23#[derive(Debug)]
31pub struct UnalignedBitChunk<'a> {
32 lead_padding: usize,
33 trailing_padding: usize,
34
35 prefix: Option<u64>,
36 chunks: &'a [u64],
37 suffix: Option<u64>,
38}
39
40impl<'a> UnalignedBitChunk<'a> {
41 pub fn new(buffer: &'a [u8], offset: usize, len: usize) -> Self {
43 if len == 0 {
44 return Self {
45 lead_padding: 0,
46 trailing_padding: 0,
47 prefix: None,
48 chunks: &[],
49 suffix: None,
50 };
51 }
52
53 let byte_offset = offset / 8;
54 let offset_padding = offset % 8;
55
56 let bytes_len = (len + offset_padding).div_ceil(8);
57 let buffer = &buffer[byte_offset..byte_offset + bytes_len];
58
59 let prefix_mask = compute_prefix_mask(offset_padding);
60
61 if buffer.len() <= 8 {
63 let (suffix_mask, trailing_padding) = compute_suffix_mask(len, offset_padding);
64 let prefix = read_u64(buffer) & suffix_mask & prefix_mask;
65
66 return Self {
67 lead_padding: offset_padding,
68 trailing_padding,
69 prefix: Some(prefix),
70 chunks: &[],
71 suffix: None,
72 };
73 }
74
75 if buffer.len() <= 16 {
77 let (suffix_mask, trailing_padding) = compute_suffix_mask(len, offset_padding);
78 let prefix = read_u64(&buffer[..8]) & prefix_mask;
79 let suffix = read_u64(&buffer[8..]) & suffix_mask;
80
81 return Self {
82 lead_padding: offset_padding,
83 trailing_padding,
84 prefix: Some(prefix),
85 chunks: &[],
86 suffix: Some(suffix),
87 };
88 }
89
90 let (prefix, mut chunks, suffix) = unsafe { buffer.align_to::<u64>() };
92 assert!(
93 prefix.len() < 8 && suffix.len() < 8,
94 "align_to did not return largest possible aligned slice"
95 );
96
97 let (alignment_padding, prefix) = match (offset_padding, prefix.is_empty()) {
98 (0, true) => (0, None),
99 (_, true) => {
100 let prefix = chunks[0] & prefix_mask;
101 chunks = &chunks[1..];
102 (0, Some(prefix))
103 }
104 (_, false) => {
105 let alignment_padding = (8 - prefix.len()) * 8;
106
107 let prefix = (read_u64(prefix) & prefix_mask) << alignment_padding;
108 (alignment_padding, Some(prefix))
109 }
110 };
111
112 let lead_padding = offset_padding + alignment_padding;
113 let (suffix_mask, trailing_padding) = compute_suffix_mask(len, lead_padding);
114
115 let suffix = match (trailing_padding, suffix.is_empty()) {
116 (0, _) => None,
117 (_, true) => {
118 let suffix = chunks[chunks.len() - 1] & suffix_mask;
119 chunks = &chunks[..chunks.len() - 1];
120 Some(suffix)
121 }
122 (_, false) => Some(read_u64(suffix) & suffix_mask),
123 };
124
125 Self {
126 lead_padding,
127 trailing_padding,
128 prefix,
129 chunks,
130 suffix,
131 }
132 }
133
134 pub fn lead_padding(&self) -> usize {
136 self.lead_padding
137 }
138
139 pub fn trailing_padding(&self) -> usize {
141 self.trailing_padding
142 }
143
144 pub fn prefix(&self) -> Option<u64> {
146 self.prefix
147 }
148
149 pub fn suffix(&self) -> Option<u64> {
151 self.suffix
152 }
153
154 pub fn chunks(&self) -> &'a [u64] {
156 self.chunks
157 }
158
159 #[inline]
161 pub fn iter(&self) -> UnalignedBitChunkIterator<'a> {
162 self.prefix
163 .into_iter()
164 .chain(self.chunks.iter().copied())
165 .chain(self.suffix)
166 }
167
168 pub fn count_ones(&self) -> usize {
170 self.iter().map(|x| x.count_ones() as usize).sum()
171 }
172}
173
174impl<'a> IntoIterator for &UnalignedBitChunk<'a> {
175 type Item = u64;
176 type IntoIter = UnalignedBitChunkIterator<'a>;
177
178 fn into_iter(self) -> Self::IntoIter {
179 self.iter()
180 }
181}
182
183pub type UnalignedBitChunkIterator<'a> = std::iter::Chain<
185 std::iter::Chain<std::option::IntoIter<u64>, std::iter::Copied<std::slice::Iter<'a, u64>>>,
186 std::option::IntoIter<u64>,
187>;
188
189#[inline]
190fn read_u64(input: &[u8]) -> u64 {
191 let len = input.len().min(8);
192 let mut buf = [0_u8; 8];
193 buf[..len].copy_from_slice(input);
194 u64::from_le_bytes(buf)
195}
196
197#[inline]
198fn compute_prefix_mask(lead_padding: usize) -> u64 {
199 !((1 << lead_padding) - 1)
200}
201
202#[inline]
203fn compute_suffix_mask(len: usize, lead_padding: usize) -> (u64, usize) {
204 let trailing_bits = (len + lead_padding) % 64;
205
206 if trailing_bits == 0 {
207 return (u64::MAX, 0);
208 }
209
210 let trailing_padding = 64 - trailing_bits;
211 let suffix_mask = (1 << trailing_bits) - 1;
212 (suffix_mask, trailing_padding)
213}
214
215#[derive(Debug)]
220pub struct BitChunks<'a> {
221 buffer: &'a [u8],
222 bit_offset: usize,
224 chunk_len: usize,
226 remainder_len: usize,
228}
229
230impl<'a> BitChunks<'a> {
231 pub fn new(buffer: &'a [u8], offset: usize, len: usize) -> Self {
233 let end = offset.checked_add(len).expect("offset + len out of bounds");
234 assert!(ceil(end, 8) <= buffer.len(), "offset + len out of bounds");
235
236 let byte_offset = offset / 8;
237 let bit_offset = offset % 8;
238
239 let chunk_len = len / 64;
241 let remainder_len = len % 64;
243
244 BitChunks::<'a> {
245 buffer: &buffer[byte_offset..],
246 bit_offset,
247 chunk_len,
248 remainder_len,
249 }
250 }
251}
252
253#[derive(Debug)]
255pub struct BitChunkIterator<'a> {
256 buffer: &'a [u8],
257 bit_offset: usize,
258 chunk_len: usize,
259 index: usize,
260}
261
262impl<'a> BitChunks<'a> {
263 #[inline]
265 pub const fn remainder_len(&self) -> usize {
266 self.remainder_len
267 }
268
269 #[inline]
271 pub const fn chunk_len(&self) -> usize {
272 self.chunk_len
273 }
274
275 #[inline]
277 pub fn remainder_bits(&self) -> u64 {
278 let bit_len = self.remainder_len;
279 if bit_len == 0 {
280 0
281 } else {
282 let bit_offset = self.bit_offset;
283 let byte_len = ceil(bit_len + bit_offset, 8);
286 let base = unsafe {
288 self.buffer
289 .as_ptr()
290 .add(self.chunk_len * std::mem::size_of::<u64>())
291 };
292
293 let mut bits = unsafe { std::ptr::read(base) } as u64 >> bit_offset;
294 for i in 1..byte_len {
295 let byte = unsafe { std::ptr::read(base.add(i)) };
296 bits |= (byte as u64) << (i * 8 - bit_offset);
297 }
298
299 bits & ((1 << bit_len) - 1)
300 }
301 }
302
303 #[inline]
309 pub fn num_u64s(&self) -> usize {
310 if self.remainder_len == 0 {
311 self.chunk_len
312 } else {
313 self.chunk_len + 1
314 }
315 }
316
317 #[inline]
320 pub fn num_bytes(&self) -> usize {
321 ceil(self.chunk_len * 64 + self.remainder_len, 8)
322 }
323
324 #[inline]
326 pub const fn iter(&self) -> BitChunkIterator<'a> {
327 BitChunkIterator::<'a> {
328 buffer: self.buffer,
329 bit_offset: self.bit_offset,
330 chunk_len: self.chunk_len,
331 index: 0,
332 }
333 }
334
335 #[inline]
337 pub fn iter_padded(&self) -> impl Iterator<Item = u64> + 'a {
338 self.iter().chain(std::iter::once(self.remainder_bits()))
339 }
340}
341
342impl<'a> IntoIterator for BitChunks<'a> {
343 type Item = u64;
344 type IntoIter = BitChunkIterator<'a>;
345
346 fn into_iter(self) -> Self::IntoIter {
347 self.iter()
348 }
349}
350
351impl<'a> IntoIterator for &BitChunks<'a> {
352 type Item = u64;
353 type IntoIter = BitChunkIterator<'a>;
354
355 fn into_iter(self) -> Self::IntoIter {
356 self.iter()
357 }
358}
359
360impl Iterator for BitChunkIterator<'_> {
361 type Item = u64;
362
363 #[inline]
364 fn next(&mut self) -> Option<u64> {
365 let index = self.index;
366 if index >= self.chunk_len {
367 return None;
368 }
369
370 #[expect(clippy::cast_ptr_alignment)]
372 let raw_data = self.buffer.as_ptr().cast::<u64>();
373
374 let current = unsafe { std::ptr::read_unaligned(raw_data.add(index)).to_le() };
377
378 let bit_offset = self.bit_offset;
379
380 let combined = if bit_offset == 0 {
381 current
382 } else {
383 let next =
386 unsafe { std::ptr::read_unaligned(raw_data.add(index + 1).cast::<u8>()) as u64 };
387
388 (current >> bit_offset) | (next << (64 - bit_offset))
389 };
390
391 self.index = index + 1;
392
393 Some(combined)
394 }
395
396 #[inline]
397 fn size_hint(&self) -> (usize, Option<usize>) {
398 (
399 self.chunk_len - self.index,
400 Some(self.chunk_len - self.index),
401 )
402 }
403}
404
405impl ExactSizeIterator for BitChunkIterator<'_> {
406 #[inline]
407 fn len(&self) -> usize {
408 self.chunk_len - self.index
409 }
410}
411
412#[cfg(test)]
413mod tests {
414 use rand::distr::uniform::UniformSampler;
415 use rand::distr::uniform::UniformUsize;
416 use rand::prelude::*;
417 use rand::rng;
418
419 use crate::buffer::Buffer;
420 use crate::util::bit_chunk_iterator::UnalignedBitChunk;
421
422 #[test]
423 fn test_iter_aligned() {
424 let input: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7];
425 let buffer: Buffer = Buffer::from(input);
426
427 let bitchunks = buffer.bit_chunks(0, 64);
428 let result = bitchunks.into_iter().collect::<Vec<_>>();
429
430 assert_eq!(vec![0x0706050403020100], result);
431 }
432
433 #[test]
434 fn test_iter_unaligned() {
435 let input: &[u8] = &[
436 0b00000000, 0b00000001, 0b00000010, 0b00000100, 0b00001000, 0b00010000, 0b00100000,
437 0b01000000, 0b11111111,
438 ];
439 let buffer: Buffer = Buffer::from(input);
440
441 let bitchunks = buffer.bit_chunks(4, 64);
442
443 assert_eq!(0, bitchunks.remainder_len());
444 assert_eq!(0, bitchunks.remainder_bits());
445
446 let result = bitchunks.into_iter().collect::<Vec<_>>();
447
448 assert_eq!(
449 vec![0b1111010000000010000000010000000010000000010000000010000000010000],
450 result
451 );
452 }
453
454 #[test]
455 fn test_iter_unaligned_remainder_1_byte() {
456 let input: &[u8] = &[
457 0b00000000, 0b00000001, 0b00000010, 0b00000100, 0b00001000, 0b00010000, 0b00100000,
458 0b01000000, 0b11111111,
459 ];
460 let buffer: Buffer = Buffer::from(input);
461
462 let bitchunks = buffer.bit_chunks(4, 66);
463
464 assert_eq!(2, bitchunks.remainder_len());
465 assert_eq!(0b00000011, bitchunks.remainder_bits());
466
467 let result = bitchunks.into_iter().collect::<Vec<_>>();
468
469 assert_eq!(
470 vec![0b1111010000000010000000010000000010000000010000000010000000010000],
471 result
472 );
473 }
474
475 #[test]
476 fn test_iter_unaligned_remainder_bits_across_bytes() {
477 let input: &[u8] = &[0b00111111, 0b11111100];
478 let buffer: Buffer = Buffer::from(input);
479
480 let bitchunks = buffer.bit_chunks(6, 7);
483
484 assert_eq!(7, bitchunks.remainder_len());
485 assert_eq!(0b1110000, bitchunks.remainder_bits());
486 }
487
488 #[test]
489 fn test_iter_unaligned_remainder_bits_large() {
490 let input: &[u8] = &[
491 0b11111111, 0b00000000, 0b11111111, 0b00000000, 0b11111111, 0b00000000, 0b11111111,
492 0b00000000, 0b11111111,
493 ];
494 let buffer: Buffer = Buffer::from(input);
495
496 let bitchunks = buffer.bit_chunks(2, 63);
497
498 assert_eq!(63, bitchunks.remainder_len());
499 assert_eq!(
500 0b100_0000_0011_1111_1100_0000_0011_1111_1100_0000_0011_1111_1100_0000_0011_1111,
501 bitchunks.remainder_bits()
502 );
503 }
504
505 #[test]
506 fn test_iter_remainder_out_of_bounds() {
507 const ALLOC_SIZE: usize = 4 * 1024;
509 let input = vec![0xFF_u8; ALLOC_SIZE];
510
511 let buffer: Buffer = Buffer::from_vec(input);
512
513 let bitchunks = buffer.bit_chunks(57, ALLOC_SIZE * 8 - 57);
514
515 assert_eq!(u64::MAX, bitchunks.iter().last().unwrap());
516 assert_eq!(0x7F, bitchunks.remainder_bits());
517 }
518
519 #[test]
520 #[should_panic(expected = "offset + len out of bounds")]
521 fn test_out_of_bound_should_panic_length_is_more_than_buffer_length() {
522 const ALLOC_SIZE: usize = 4 * 1024;
523 let input = vec![0xFF_u8; ALLOC_SIZE];
524
525 let buffer: Buffer = Buffer::from_vec(input);
526
527 buffer.bit_chunks(0, (ALLOC_SIZE + 1) * 8);
529 }
530
531 #[test]
532 #[should_panic(expected = "offset + len out of bounds")]
533 fn test_out_of_bound_should_panic_length_is_more_than_buffer_length_but_not_when_not_using_ceil()
534 {
535 const ALLOC_SIZE: usize = 4 * 1024;
536 let input = vec![0xFF_u8; ALLOC_SIZE];
537
538 let buffer: Buffer = Buffer::from_vec(input);
539
540 buffer.bit_chunks(0, (ALLOC_SIZE * 8) + 1);
542 }
543
544 #[test]
545 #[should_panic(expected = "offset + len out of bounds")]
546 fn test_out_of_bound_should_panic_when_offset_is_not_zero_and_length_is_the_entire_buffer_length()
547 {
548 const ALLOC_SIZE: usize = 4 * 1024;
549 let input = vec![0xFF_u8; ALLOC_SIZE];
550
551 let buffer: Buffer = Buffer::from_vec(input);
552
553 buffer.bit_chunks(8, ALLOC_SIZE * 8);
555 }
556
557 #[test]
558 #[should_panic(expected = "offset + len out of bounds")]
559 fn test_out_of_bound_should_panic_when_offset_is_not_zero_and_length_is_the_entire_buffer_length_with_ceil()
560 {
561 const ALLOC_SIZE: usize = 4 * 1024;
562 let input = vec![0xFF_u8; ALLOC_SIZE];
563
564 let buffer: Buffer = Buffer::from_vec(input);
565
566 buffer.bit_chunks(1, ALLOC_SIZE * 8);
568 }
569
570 #[test]
571 #[should_panic(expected = "offset + len out of bounds")]
572 fn test_out_of_bound_should_panic_when_offset_and_length_overflow() {
573 let buffer = Buffer::from(vec![0xFF_u8; 8]);
574 buffer.bit_chunks(1, usize::MAX);
575 }
576
577 #[test]
578 fn test_unaligned_bit_chunk_iterator() {
579 let buffer = Buffer::from(&[0xFF; 5]);
580 let unaligned = UnalignedBitChunk::new(buffer.as_slice(), 0, 40);
581
582 assert!(unaligned.chunks().is_empty()); assert_eq!(unaligned.lead_padding(), 0);
584 assert_eq!(unaligned.trailing_padding(), 24);
585 assert_eq!(
587 unaligned.prefix(),
588 Some(0b0000000000000000000000001111111111111111111111111111111111111111)
589 );
590 assert_eq!(unaligned.suffix(), None);
591
592 let buffer = buffer.slice(1);
593 let unaligned = UnalignedBitChunk::new(buffer.as_slice(), 0, 32);
594
595 assert!(unaligned.chunks().is_empty()); assert_eq!(unaligned.lead_padding(), 0);
597 assert_eq!(unaligned.trailing_padding(), 32);
598 assert_eq!(
600 unaligned.prefix(),
601 Some(0b0000000000000000000000000000000011111111111111111111111111111111)
602 );
603 assert_eq!(unaligned.suffix(), None);
604
605 let unaligned = UnalignedBitChunk::new(buffer.as_slice(), 5, 27);
606
607 assert!(unaligned.chunks().is_empty()); assert_eq!(unaligned.lead_padding(), 5); assert_eq!(unaligned.trailing_padding(), 32);
610 assert_eq!(
612 unaligned.prefix(),
613 Some(0b0000000000000000000000000000000011111111111111111111111111100000)
614 );
615 assert_eq!(unaligned.suffix(), None);
616
617 let unaligned = UnalignedBitChunk::new(buffer.as_slice(), 12, 20);
618
619 assert!(unaligned.chunks().is_empty()); assert_eq!(unaligned.lead_padding(), 4); assert_eq!(unaligned.trailing_padding(), 40);
622 assert_eq!(
624 unaligned.prefix(),
625 Some(0b0000000000000000000000000000000000000000111111111111111111110000)
626 );
627 assert_eq!(unaligned.suffix(), None);
628
629 let buffer = Buffer::from(&[0xFF; 14]);
630
631 let (prefix, aligned, suffix) = unsafe { buffer.as_slice().align_to::<u64>() };
633 assert_eq!(prefix.len(), 0);
634 assert_eq!(aligned.len(), 1);
635 assert_eq!(suffix.len(), 6);
636
637 let unaligned = UnalignedBitChunk::new(buffer.as_slice(), 0, 112);
638
639 assert!(unaligned.chunks().is_empty()); assert_eq!(unaligned.lead_padding(), 0); assert_eq!(unaligned.trailing_padding(), 16);
642 assert_eq!(unaligned.prefix(), Some(u64::MAX));
643 assert_eq!(unaligned.suffix(), Some((1 << 48) - 1));
644
645 let buffer = Buffer::from(&[0xFF; 16]);
646
647 let (prefix, aligned, suffix) = unsafe { buffer.as_slice().align_to::<u64>() };
649 assert_eq!(prefix.len(), 0);
650 assert_eq!(aligned.len(), 2);
651 assert_eq!(suffix.len(), 0);
652
653 let unaligned = UnalignedBitChunk::new(buffer.as_slice(), 0, 128);
654
655 assert_eq!(unaligned.prefix(), Some(u64::MAX));
656 assert_eq!(unaligned.suffix(), Some(u64::MAX));
657 assert!(unaligned.chunks().is_empty()); let buffer = Buffer::from(&[0xFF; 64]);
660
661 let (prefix, aligned, suffix) = unsafe { buffer.as_slice().align_to::<u64>() };
663 assert_eq!(prefix.len(), 0);
664 assert_eq!(aligned.len(), 8);
665 assert_eq!(suffix.len(), 0);
666
667 let unaligned = UnalignedBitChunk::new(buffer.as_slice(), 0, 512);
668
669 assert_eq!(unaligned.suffix(), None);
671 assert_eq!(unaligned.prefix(), None);
672 assert_eq!(unaligned.chunks(), [u64::MAX; 8].as_slice());
673 assert_eq!(unaligned.lead_padding(), 0);
674 assert_eq!(unaligned.trailing_padding(), 0);
675
676 let buffer = buffer.slice(1); let (prefix, aligned, suffix) = unsafe { buffer.as_slice().align_to::<u64>() };
680 assert_eq!(prefix.len(), 7);
681 assert_eq!(aligned.len(), 7);
682 assert_eq!(suffix.len(), 0);
683
684 let unaligned = UnalignedBitChunk::new(buffer.as_slice(), 0, 504);
685
686 assert_eq!(unaligned.prefix(), Some(u64::MAX - 0xFF));
688 assert_eq!(unaligned.suffix(), None);
689 assert_eq!(unaligned.chunks(), [u64::MAX; 7].as_slice());
690 assert_eq!(unaligned.lead_padding(), 8);
691 assert_eq!(unaligned.trailing_padding(), 0);
692
693 let unaligned = UnalignedBitChunk::new(buffer.as_slice(), 17, 300);
694
695 assert_eq!(unaligned.lead_padding(), 25);
702 assert_eq!(unaligned.trailing_padding(), 59);
703 assert_eq!(unaligned.prefix(), Some(u64::MAX - (1 << 25) + 1));
704 assert_eq!(unaligned.suffix(), Some(0b11111));
705 assert_eq!(unaligned.chunks(), [u64::MAX; 4].as_slice());
706
707 let unaligned = UnalignedBitChunk::new(buffer.as_slice(), 17, 0);
708
709 assert_eq!(unaligned.prefix(), None);
710 assert_eq!(unaligned.suffix(), None);
711 assert!(unaligned.chunks().is_empty());
712 assert_eq!(unaligned.lead_padding(), 0);
713 assert_eq!(unaligned.trailing_padding(), 0);
714
715 let unaligned = UnalignedBitChunk::new(buffer.as_slice(), 17, 1);
716
717 assert_eq!(unaligned.prefix(), Some(2));
718 assert_eq!(unaligned.suffix(), None);
719 assert!(unaligned.chunks().is_empty());
720 assert_eq!(unaligned.lead_padding(), 1);
721 assert_eq!(unaligned.trailing_padding(), 62);
722 }
723
724 #[test]
725 #[cfg_attr(miri, ignore)]
726 fn fuzz_unaligned_bit_chunk_iterator() {
727 let mut rng = rng();
728
729 let uusize = UniformUsize::new(usize::MIN, usize::MAX).unwrap();
730 for _ in 0..100 {
731 let mask_len = rng.random_range(0..1024);
732 let bools: Vec<_> = std::iter::from_fn(|| Some(rng.random()))
733 .take(mask_len)
734 .collect();
735
736 let buffer = Buffer::from_iter(bools.iter().copied());
737
738 let max_offset = 64.min(mask_len);
739 let offset = uusize.sample(&mut rng).checked_rem(max_offset).unwrap_or(0);
740
741 let max_truncate = 128.min(mask_len - offset);
742 let truncate = uusize
743 .sample(&mut rng)
744 .checked_rem(max_truncate)
745 .unwrap_or(0);
746
747 let unaligned =
748 UnalignedBitChunk::new(buffer.as_slice(), offset, mask_len - offset - truncate);
749
750 let bool_slice = &bools[offset..mask_len - truncate];
751
752 let count = unaligned.count_ones();
753 let expected_count = bool_slice.iter().filter(|x| **x).count();
754
755 assert_eq!(count, expected_count);
756
757 let collected: Vec<u64> = unaligned.iter().collect();
758
759 let get_bit = |idx: usize| -> bool {
760 let padded_index = idx + unaligned.lead_padding();
761 let byte_idx = padded_index / 64;
762 let bit_idx = padded_index % 64;
763 (collected[byte_idx] & (1 << bit_idx)) != 0
764 };
765
766 for (idx, b) in bool_slice.iter().enumerate() {
767 assert_eq!(*b, get_bit(idx))
768 }
769 }
770 }
771}