1use std::io;
24
25use bytes::{Bytes, BytesMut};
26
27fn words_to_bytes(words: &[u64]) -> Bytes {
31 let mut buf = BytesMut::with_capacity(words.len() * 8);
32 for &w in words {
33 buf.extend_from_slice(&w.to_le_bytes());
34 }
35 buf.freeze()
36}
37
38#[derive(Debug, Clone)]
48enum WordStore {
49 Inline(Vec<u64>),
50 Mapped(Bytes),
51}
52
53impl WordStore {
54 #[inline]
55 fn byte_len(&self) -> usize {
56 match self {
57 Self::Inline(v) => v.len() * 8,
58 Self::Mapped(b) => b.len(),
59 }
60 }
61
62 #[inline]
63 fn word_count(&self) -> usize {
64 match self {
65 Self::Inline(v) => v.len(),
66 Self::Mapped(b) => b.len() / 8,
67 }
68 }
69
70 #[inline]
71 fn as_slice(&self) -> Option<&[u64]> {
72 match self {
73 Self::Inline(v) => Some(v.as_slice()),
74 Self::Mapped(_) => None,
75 }
76 }
77
78 #[inline]
79 fn word_at(&self, idx: usize) -> Option<u64> {
80 match self {
81 Self::Inline(v) => v.get(idx).copied(),
82 Self::Mapped(b) => {
83 let start = idx.checked_mul(8)?;
84 let end = start.checked_add(8)?;
85 let chunk: [u8; 8] = b.get(start..end)?.try_into().ok()?;
86 Some(u64::from_le_bytes(chunk))
87 }
88 }
89 }
90
91 fn to_bytes(&self) -> Bytes {
94 match self {
95 Self::Inline(v) => words_to_bytes(v),
96 Self::Mapped(b) => b.clone(),
97 }
98 }
99}
100
101#[derive(Debug, Clone)]
118pub struct BitPackedInts {
119 data: WordStore,
123 bits_per_value: u8,
125 count: usize,
127}
128
129impl BitPackedInts {
130 #[must_use]
135 pub fn from_raw_parts(data: Vec<u64>, bits_per_value: u8, count: usize) -> Self {
136 Self {
137 data: WordStore::Inline(data),
138 bits_per_value,
139 count,
140 }
141 }
142
143 #[must_use]
149 pub fn from_bytes_storage(data: Bytes, bits_per_value: u8, count: usize) -> Self {
150 Self {
151 data: WordStore::Mapped(data),
152 bits_per_value,
153 count,
154 }
155 }
156
157 #[must_use]
159 pub fn pack(values: &[u64]) -> Self {
160 if values.is_empty() {
161 return Self {
162 data: WordStore::Inline(Vec::new()),
163 bits_per_value: 0,
164 count: 0,
165 };
166 }
167
168 let max_value = values.iter().copied().max().unwrap_or(0);
169 let bits = Self::bits_needed(max_value);
170 Self::pack_with_bits(values, bits)
171 }
172
173 #[must_use]
179 pub fn pack_with_bits(values: &[u64], bits_per_value: u8) -> Self {
180 if values.is_empty() {
181 return Self {
182 data: WordStore::Inline(Vec::new()),
183 bits_per_value,
184 count: 0,
185 };
186 }
187
188 if bits_per_value == 0 {
189 debug_assert!(values.iter().all(|&v| v == 0));
191 return Self {
192 data: WordStore::Inline(Vec::new()),
193 bits_per_value: 0,
194 count: values.len(),
195 };
196 }
197
198 let bits = bits_per_value as usize;
199 let values_per_word = 64 / bits;
200 let num_words = values.len().div_ceil(values_per_word);
201
202 let mut words = vec![0u64; num_words];
203 let mask = if bits >= 64 {
204 u64::MAX
205 } else {
206 (1u64 << bits) - 1
207 };
208
209 for (i, &value) in values.iter().enumerate() {
210 debug_assert!(
211 value <= mask,
212 "Value {} doesn't fit in {} bits",
213 value,
214 bits_per_value
215 );
216
217 let word_idx = i / values_per_word;
218 let bit_offset = (i % values_per_word) * bits;
219 words[word_idx] |= (value & mask) << bit_offset;
220 }
221
222 Self {
223 data: WordStore::Inline(words),
224 bits_per_value,
225 count: values.len(),
226 }
227 }
228
229 #[must_use]
231 pub fn unpack(&self) -> Vec<u64> {
232 if self.count == 0 {
233 return Vec::new();
234 }
235
236 if self.bits_per_value == 0 {
237 return vec![0u64; self.count];
238 }
239
240 let bits = self.bits_per_value as usize;
241 let values_per_word = 64 / bits;
242 let mask = if bits >= 64 {
243 u64::MAX
244 } else {
245 (1u64 << bits) - 1
246 };
247
248 let mut result = Vec::with_capacity(self.count);
249
250 for i in 0..self.count {
251 let word_idx = i / values_per_word;
252 let bit_offset = (i % values_per_word) * bits;
253 let word = self.word_at(word_idx).unwrap_or(0);
254 let value = (word >> bit_offset) & mask;
255 result.push(value);
256 }
257
258 result
259 }
260
261 #[must_use]
263 #[inline]
264 pub fn get(&self, index: usize) -> Option<u64> {
265 if index >= self.count {
266 return None;
267 }
268
269 if self.bits_per_value == 0 {
270 return Some(0);
271 }
272
273 let bits = self.bits_per_value as usize;
274 let values_per_word = 64 / bits;
275 let word_idx = index / values_per_word;
276 let bit_offset = (index % values_per_word) * bits;
277 let mask = if bits >= 64 {
278 u64::MAX
279 } else {
280 (1u64 << bits) - 1
281 };
282
283 let word = match &self.data {
284 WordStore::Inline(v) => *v.get(word_idx)?,
285 WordStore::Mapped(b) => {
288 let start = word_idx.checked_mul(8)?;
289 let end = start.checked_add(8)?;
290 let chunk: [u8; 8] = b.get(start..end)?.try_into().ok()?;
291 u64::from_le_bytes(chunk)
292 }
293 };
294 Some((word >> bit_offset) & mask)
295 }
296
297 #[must_use]
306 pub fn scan_eq(&self, target: u64) -> Vec<usize> {
307 if self.count == 0 {
308 return Vec::new();
309 }
310
311 if self.bits_per_value == 0 {
312 return if target == 0 {
313 (0..self.count).collect()
314 } else {
315 Vec::new()
316 };
317 }
318
319 let bits = self.bits_per_value as usize;
320 let values_per_word = 64 / bits;
322 let mask = if bits >= 64 {
323 u64::MAX
324 } else {
325 (1u64 << bits) - 1
326 };
327
328 if mask != u64::MAX && target & !mask != 0 {
331 return Vec::new();
332 }
333
334 match &self.data {
335 WordStore::Inline(words) => {
336 let mut out = Vec::new();
337 for i in 0..self.count {
338 let word_idx = i / values_per_word;
339 let bit_offset = (i % values_per_word) * bits;
340 let Some(&word) = words.get(word_idx) else {
344 break;
345 };
346 if (word >> bit_offset) & mask == target {
347 out.push(i);
348 }
349 }
350 out
351 }
352 WordStore::Mapped(b) => {
353 let mut out = Vec::new();
354 let bytes = b.as_ref();
355 for i in 0..self.count {
356 let word_idx = i / values_per_word;
357 let bit_offset = (i % values_per_word) * bits;
358 let start = word_idx * 8;
359 let Some(slice) = bytes.get(start..start + 8) else {
360 break;
361 };
362 let Ok(chunk) = <[u8; 8]>::try_from(slice) else {
363 break;
364 };
365 let word = u64::from_le_bytes(chunk);
366 if (word >> bit_offset) & mask == target {
367 out.push(i);
368 }
369 }
370 out
371 }
372 }
373 }
374
375 #[must_use]
377 pub fn len(&self) -> usize {
378 self.count
379 }
380
381 #[must_use]
383 pub fn is_empty(&self) -> bool {
384 self.count == 0
385 }
386
387 #[must_use]
389 pub fn bits_per_value(&self) -> u8 {
390 self.bits_per_value
391 }
392
393 #[must_use]
399 pub fn data_bytes(&self) -> Bytes {
400 self.data.to_bytes()
401 }
402
403 #[must_use]
405 pub fn word_count(&self) -> usize {
406 self.data.word_count()
407 }
408
409 #[must_use]
415 #[inline]
416 pub fn as_words_slice(&self) -> Option<&[u64]> {
417 self.data.as_slice()
418 }
419
420 #[must_use]
422 #[inline]
423 pub fn word_at(&self, idx: usize) -> Option<u64> {
424 self.data.word_at(idx)
425 }
426
427 #[must_use]
429 pub fn compression_ratio(&self) -> f64 {
430 if self.count == 0 {
431 return 1.0;
432 }
433
434 let original_size = self.count * 8; let packed_size = self.data.byte_len();
436
437 if packed_size == 0 {
438 return f64::INFINITY; }
440
441 original_size as f64 / packed_size as f64
442 }
443
444 #[must_use]
453 pub fn bits_needed(value: u64) -> u8 {
454 if value == 0 {
455 1 } else {
457 u8::try_from(64u32 - value.leading_zeros()).expect("bits_needed result is in 1..=64")
460 }
461 }
462
463 pub fn to_bytes(&self) -> io::Result<Vec<u8>> {
469 let count_u32 = u32::try_from(self.count).map_err(|_| {
470 io::Error::new(
471 io::ErrorKind::InvalidInput,
472 format!(
473 "BitPackedInts count {} exceeds u32::MAX, cannot serialize",
474 self.count
475 ),
476 )
477 })?;
478 let mut buf = Vec::with_capacity(1 + 4 + self.data.byte_len());
479 buf.push(self.bits_per_value);
480 buf.extend_from_slice(&count_u32.to_le_bytes());
481 let body = self.data.to_bytes();
484 buf.extend_from_slice(&body);
485 Ok(buf)
486 }
487
488 pub fn from_bytes(bytes: &[u8]) -> io::Result<Self> {
494 if bytes.len() < 5 {
495 return Err(io::Error::new(
496 io::ErrorKind::InvalidData,
497 "BitPackedInts too short",
498 ));
499 }
500
501 let bits_per_value = bytes[0];
502 if bits_per_value > 64 {
503 return Err(io::Error::new(
504 io::ErrorKind::InvalidData,
505 format!("BitPackedInts bits_per_value {bits_per_value} exceeds 64"),
506 ));
507 }
508 let count = u32::from_le_bytes(
509 bytes[1..5]
510 .try_into()
511 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?,
512 ) as usize;
513
514 let num_words = if bits_per_value == 0 || count == 0 {
515 0
516 } else {
517 let values_per_word = 64 / bits_per_value as usize;
518 (count + values_per_word - 1) / values_per_word
519 };
520
521 let needed = 5 + num_words * 8;
522 if bytes.len() < needed {
523 return Err(io::Error::new(
524 io::ErrorKind::InvalidData,
525 "BitPackedInts truncated",
526 ));
527 }
528
529 let data = WordStore::Mapped(Bytes::copy_from_slice(&bytes[5..needed]));
533
534 Ok(Self {
535 data,
536 bits_per_value,
537 count,
538 })
539 }
540}
541
542#[derive(Debug, Clone)]
548pub struct DeltaBitPacked {
549 base: u64,
551 deltas: BitPackedInts,
553}
554
555impl DeltaBitPacked {
556 #[must_use]
558 pub fn encode(values: &[u64]) -> Self {
559 if values.is_empty() {
560 return Self {
561 base: 0,
562 deltas: BitPackedInts::pack(&[]),
563 };
564 }
565
566 let base = values[0];
567 let delta_values: Vec<u64> = values
568 .windows(2)
569 .map(|w| w[1].saturating_sub(w[0]))
570 .collect();
571
572 let deltas = BitPackedInts::pack(&delta_values);
573
574 Self { base, deltas }
575 }
576
577 #[must_use]
579 pub fn decode(&self) -> Vec<u64> {
580 if self.deltas.is_empty() && self.base == 0 {
581 return Vec::new();
582 }
583
584 let delta_values = self.deltas.unpack();
585 let mut result = Vec::with_capacity(delta_values.len() + 1);
586 let mut current = self.base;
587 result.push(current);
588
589 for delta in delta_values {
590 current = current.wrapping_add(delta);
591 result.push(current);
592 }
593
594 result
595 }
596
597 #[must_use]
599 pub fn len(&self) -> usize {
600 if self.deltas.is_empty() && self.base == 0 {
601 0
602 } else {
603 self.deltas.len() + 1
604 }
605 }
606
607 #[must_use]
609 pub fn is_empty(&self) -> bool {
610 self.deltas.is_empty() && self.base == 0
611 }
612
613 #[must_use]
615 pub fn base(&self) -> u64 {
616 self.base
617 }
618
619 #[must_use]
621 pub fn bits_per_delta(&self) -> u8 {
622 self.deltas.bits_per_value()
623 }
624
625 #[must_use]
627 pub fn compression_ratio(&self) -> f64 {
628 let count = self.len();
629 if count == 0 {
630 return 1.0;
631 }
632
633 let original_size = count * 8;
634 let packed_size = 8 + self.deltas.data_bytes().len(); original_size as f64 / packed_size as f64
637 }
638
639 pub fn to_bytes(&self) -> io::Result<Vec<u8>> {
645 let delta_bytes = self.deltas.to_bytes()?;
646 let mut buf = Vec::with_capacity(8 + delta_bytes.len());
647 buf.extend_from_slice(&self.base.to_le_bytes());
648 buf.extend_from_slice(&delta_bytes);
649 Ok(buf)
650 }
651
652 pub fn from_bytes(bytes: &[u8]) -> io::Result<Self> {
658 if bytes.len() < 8 {
659 return Err(io::Error::new(
660 io::ErrorKind::InvalidData,
661 "DeltaBitPacked too short",
662 ));
663 }
664
665 let base = u64::from_le_bytes(
666 bytes[0..8]
667 .try_into()
668 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?,
669 );
670 let deltas = BitPackedInts::from_bytes(&bytes[8..])?;
671
672 Ok(Self { base, deltas })
673 }
674}
675
676#[cfg(test)]
677mod tests {
678 use super::*;
679
680 #[test]
681 fn test_bitpack_basic() {
682 let values = vec![5u64, 2, 3, 5, 5, 8, 2];
683 let packed = BitPackedInts::pack(&values);
684 let unpacked = packed.unpack();
685 assert_eq!(values, unpacked);
686 }
687
688 #[test]
689 fn test_bitpack_empty() {
690 let values: Vec<u64> = vec![];
691 let packed = BitPackedInts::pack(&values);
692 assert!(packed.is_empty());
693 assert_eq!(packed.unpack(), values);
694 }
695
696 #[test]
697 fn test_bitpack_single() {
698 let values = vec![42u64];
699 let packed = BitPackedInts::pack(&values);
700 assert_eq!(packed.len(), 1);
701 assert_eq!(packed.unpack(), values);
702 }
703
704 #[test]
705 fn test_bitpack_all_zeros() {
706 let values = vec![0u64; 100];
707 let packed = BitPackedInts::pack(&values);
708 assert_eq!(packed.bits_per_value(), 1);
709 assert_eq!(packed.unpack(), values);
710 }
711
712 #[test]
713 fn test_bitpack_powers_of_two() {
714 for bits in 1..=64u8 {
715 let max_val = if bits == 64 {
716 u64::MAX
717 } else {
718 (1u64 << bits) - 1
719 };
720 let values = vec![0, max_val / 2, max_val];
721 let packed = BitPackedInts::pack(&values);
722 assert_eq!(packed.bits_per_value(), bits);
723 assert_eq!(packed.unpack(), values);
724 }
725 }
726
727 #[test]
728 fn test_bitpack_get() {
729 let values = vec![1u64, 2, 3, 4, 5];
730 let packed = BitPackedInts::pack(&values);
731
732 for (i, &expected) in values.iter().enumerate() {
733 assert_eq!(packed.get(i), Some(expected));
734 }
735 assert_eq!(packed.get(100), None);
736 }
737
738 #[test]
739 fn test_bitpack_compression() {
740 let values: Vec<u64> = (0..100).map(|i| i % 16).collect();
742 let packed = BitPackedInts::pack(&values);
743 assert_eq!(packed.bits_per_value(), 4);
744 let ratio = packed.compression_ratio();
746 assert!(ratio > 10.0, "Expected ratio > 10, got {}", ratio);
747 }
748
749 #[test]
750 fn test_bitpack_serialization() {
751 let values = vec![1u64, 3, 7, 15, 31];
752 let packed = BitPackedInts::pack(&values);
753 let bytes = packed.to_bytes().unwrap();
754 let restored = BitPackedInts::from_bytes(&bytes).unwrap();
755 assert_eq!(packed.unpack(), restored.unpack());
756 }
757
758 #[test]
759 fn test_delta_bitpacked_basic() {
760 let values = vec![100u64, 105, 107, 110, 115, 120, 128, 130];
761 let encoded = DeltaBitPacked::encode(&values);
762 let decoded = encoded.decode();
763 assert_eq!(values, decoded);
764 }
765
766 #[test]
767 fn test_delta_bitpacked_sequential() {
768 let values: Vec<u64> = (1000..1100).collect();
770 let encoded = DeltaBitPacked::encode(&values);
771 assert_eq!(encoded.bits_per_delta(), 1);
772 assert_eq!(encoded.decode(), values);
773
774 let ratio = encoded.compression_ratio();
776 assert!(ratio > 5.0, "Expected ratio > 5, got {}", ratio);
777 }
778
779 #[test]
780 fn test_delta_bitpacked_empty() {
781 let values: Vec<u64> = vec![];
782 let encoded = DeltaBitPacked::encode(&values);
783 assert!(encoded.is_empty());
784 assert_eq!(encoded.decode(), values);
785 }
786
787 #[test]
788 fn test_delta_bitpacked_single() {
789 let values = vec![42u64];
790 let encoded = DeltaBitPacked::encode(&values);
791 assert_eq!(encoded.len(), 1);
792 assert_eq!(encoded.decode(), values);
793 }
794
795 #[test]
796 fn test_delta_bitpacked_serialization() {
797 let values = vec![100u64, 105, 107, 110, 115];
798 let encoded = DeltaBitPacked::encode(&values);
799 let bytes = encoded.to_bytes().unwrap();
800 let restored = DeltaBitPacked::from_bytes(&bytes).unwrap();
801 assert_eq!(encoded.decode(), restored.decode());
802 }
803
804 #[test]
805 fn test_bits_needed() {
806 assert_eq!(BitPackedInts::bits_needed(0), 1);
807 assert_eq!(BitPackedInts::bits_needed(1), 1);
808 assert_eq!(BitPackedInts::bits_needed(2), 2);
809 assert_eq!(BitPackedInts::bits_needed(3), 2);
810 assert_eq!(BitPackedInts::bits_needed(4), 3);
811 assert_eq!(BitPackedInts::bits_needed(7), 3);
812 assert_eq!(BitPackedInts::bits_needed(8), 4);
813 assert_eq!(BitPackedInts::bits_needed(255), 8);
814 assert_eq!(BitPackedInts::bits_needed(256), 9);
815 assert_eq!(BitPackedInts::bits_needed(u64::MAX), 64);
816 }
817
818 #[test]
821 fn test_bitpack_word_at_returns_words_from_bytes() {
822 let packed = BitPackedInts::pack(&[1u64, 3, 7, 15, 4]);
824 assert_eq!(packed.word_count(), 1);
825 let word = packed.word_at(0).unwrap();
826 assert_eq!(word & 0xF, 1);
828 assert_eq!((word >> 4) & 0xF, 3);
829 assert_eq!((word >> 8) & 0xF, 7);
830 assert_eq!((word >> 12) & 0xF, 15);
831 assert_eq!((word >> 16) & 0xF, 4);
832 }
833
834 #[test]
835 fn test_bitpack_word_at_out_of_range_returns_none() {
836 let packed = BitPackedInts::pack(&[1u64, 2, 3]);
837 assert!(packed.word_at(packed.word_count()).is_none());
838 assert!(packed.word_at(usize::MAX).is_none());
839 }
840
841 #[test]
842 fn test_bitpack_data_bytes_length_matches_word_count() {
843 let packed = BitPackedInts::pack_with_bits(
844 &(0u64..200).collect::<Vec<_>>(),
845 8, );
847 assert_eq!(packed.data_bytes().len(), packed.word_count() * 8);
848 let unpacked = packed.unpack();
850 assert_eq!(unpacked, (0u64..200).collect::<Vec<_>>());
851 }
852
853 #[test]
854 fn test_bitpack_round_trip_through_to_bytes_and_from_bytes() {
855 let values: Vec<u64> = (0u64..50).map(|i| i * 7 % 1024).collect();
856 let packed = BitPackedInts::pack(&values);
857 let serialized = packed.to_bytes().unwrap();
858 let restored = BitPackedInts::from_bytes(&serialized).unwrap();
859 assert_eq!(packed.unpack(), restored.unpack());
860 assert_eq!(packed.bits_per_value(), restored.bits_per_value());
861 assert_eq!(packed.len(), restored.len());
862 }
863
864 #[test]
867 fn test_bitpack_inline_storage_word_slice_via_pack() {
868 let values: Vec<u64> = (0..1000).map(|i| i % 16).collect();
869 let packed = BitPackedInts::pack(&values);
870
871 let slice = packed.as_words_slice();
872 assert!(
873 slice.is_some(),
874 "BitPackedInts::pack must produce Inline storage so scans skip per-element decode"
875 );
876 }
877
878 #[test]
879 fn test_bitpack_inline_storage_word_slice_matches_input() {
880 let words = vec![0xDEAD_BEEFu64, 0xCAFE_BABE];
881 let packed = BitPackedInts::from_raw_parts(words.clone(), 8, 16);
882 let slice = packed.as_words_slice().expect("Vec input is Inline");
883 assert_eq!(slice, words.as_slice());
884 }
885
886 #[test]
887 fn test_bitpack_mapped_storage_no_word_slice() {
888 let words: Vec<u64> = vec![1, 2, 3];
889 let mut buf = bytes::BytesMut::with_capacity(words.len() * 8);
890 for &w in &words {
891 buf.extend_from_slice(&w.to_le_bytes());
892 }
893 let packed = BitPackedInts::from_bytes_storage(buf.freeze(), 8, 24);
894
895 assert!(
896 packed.as_words_slice().is_none(),
897 "Bytes-backed (mmap) storage cannot expose a u64 slice"
898 );
899 assert_eq!(packed.word_at(0), Some(1));
900 assert_eq!(packed.word_at(2), Some(3));
901 }
902
903 #[test]
904 fn test_bitpack_get_matches_between_inline_and_mapped_storage() {
905 let values: Vec<u64> = (0..2_000).map(|i| (i * 7) % 31).collect();
906 let inline = BitPackedInts::pack(&values);
907
908 let bytes = inline.data_bytes();
910 let mapped =
911 BitPackedInts::from_bytes_storage(bytes, inline.bits_per_value(), values.len());
912
913 for i in 0..values.len() {
914 assert_eq!(inline.get(i), Some(values[i]), "inline mismatch at {i}");
915 assert_eq!(mapped.get(i), Some(values[i]), "mapped mismatch at {i}");
916 }
917 assert_eq!(inline.get(values.len()), None);
918 assert_eq!(mapped.get(values.len()), None);
919 }
920
921 #[test]
922 fn test_scan_eq_matches_get_filter_inline() {
923 let values: Vec<u64> = (0..5_000).map(|i| (i * 13) % 97).collect();
924 let packed = BitPackedInts::pack(&values);
925
926 for target in [0u64, 1, 50, 96, 97, u64::MAX] {
927 let scan: Vec<usize> = packed.scan_eq(target);
928 let getf: Vec<usize> = (0..values.len())
929 .filter(|&i| packed.get(i) == Some(target))
930 .collect();
931 assert_eq!(scan, getf, "scan_eq vs get-filter for target {target}");
932 }
933 }
934
935 #[test]
936 fn test_scan_eq_matches_get_filter_mapped() {
937 let values: Vec<u64> = (0..5_000).map(|i| (i * 13) % 97).collect();
938 let inline = BitPackedInts::pack(&values);
939 let bytes = inline.data_bytes();
940 let mapped =
941 BitPackedInts::from_bytes_storage(bytes, inline.bits_per_value(), values.len());
942
943 for target in [0u64, 1, 50, 96, 97, u64::MAX] {
944 let scan: Vec<usize> = mapped.scan_eq(target);
945 let getf: Vec<usize> = (0..values.len())
946 .filter(|&i| mapped.get(i) == Some(target))
947 .collect();
948 assert_eq!(
949 scan, getf,
950 "mapped scan_eq vs get-filter for target {target}"
951 );
952 }
953 }
954
955 #[test]
956 fn test_scan_eq_all_zeros_short_circuit() {
957 let packed = BitPackedInts::pack(&vec![0u64; 64]);
959 assert_eq!(packed.bits_per_value(), 1);
960 let zero_bits = BitPackedInts::from_raw_parts(Vec::new(), 0, 64);
962 assert_eq!(zero_bits.scan_eq(0), (0..64).collect::<Vec<_>>());
963 assert!(zero_bits.scan_eq(1).is_empty());
964 }
965
966 #[test]
967 fn test_scan_eq_target_exceeds_mask_returns_empty() {
968 let values: Vec<u64> = (0..10).map(|i| i % 16).collect();
971 let packed = BitPackedInts::pack(&values);
972 assert!(packed.bits_per_value() <= 4);
973 assert!(packed.scan_eq(100).is_empty());
974 }
975
976 #[test]
977 fn test_scan_eq_empty_column() {
978 let packed = BitPackedInts::pack(&[]);
979 assert!(packed.scan_eq(0).is_empty());
980 assert!(packed.scan_eq(42).is_empty());
981 }
982}