1use super::BitMap;
4use bytes::{Buf, BufMut};
5use commonware_codec::{EncodeSize, Error as CodecError, Read, ReadExt, Write};
6use thiserror::Error;
7
8#[derive(Debug, Error, Clone, PartialEq, Eq)]
10pub enum Error {
11 #[error("pruned_chunks * CHUNK_SIZE_BITS overflows u64")]
13 PrunedChunksOverflow,
14}
15
16#[derive(Clone, Debug)]
23pub struct Prunable<const N: usize> {
24 bitmap: BitMap<N>,
26
27 pruned_chunks: usize,
33}
34
35impl<const N: usize> Prunable<N> {
36 pub const CHUNK_SIZE_BITS: u64 = BitMap::<N>::CHUNK_SIZE_BITS;
38
39 pub const fn new() -> Self {
43 Self {
44 bitmap: BitMap::new(),
45 pruned_chunks: 0,
46 }
47 }
48
49 pub fn new_with_pruned_chunks(pruned_chunks: usize) -> Result<Self, Error> {
56 let pruned_chunks_u64 = pruned_chunks as u64;
58 pruned_chunks_u64
59 .checked_mul(Self::CHUNK_SIZE_BITS)
60 .ok_or(Error::PrunedChunksOverflow)?;
61
62 Ok(Self {
63 bitmap: BitMap::new(),
64 pruned_chunks,
65 })
66 }
67
68 #[inline]
72 pub const fn len(&self) -> u64 {
73 let pruned_bits = (self.pruned_chunks as u64)
74 .checked_mul(Self::CHUNK_SIZE_BITS)
75 .expect("invariant violated: pruned_chunks * CHUNK_SIZE_BITS overflows u64");
76
77 pruned_bits
78 .checked_add(self.bitmap.len())
79 .expect("invariant violated: pruned_bits + bitmap.len() overflows u64")
80 }
81
82 #[inline]
84 pub const fn is_empty(&self) -> bool {
85 self.len() == 0
86 }
87
88 #[inline]
90 pub const fn is_chunk_aligned(&self) -> bool {
91 self.len().is_multiple_of(Self::CHUNK_SIZE_BITS)
92 }
93
94 #[inline]
96 pub fn chunks_len(&self) -> usize {
97 self.pruned_chunks + self.bitmap.chunks_len()
98 }
99
100 #[inline]
102 pub const fn pruned_chunks(&self) -> usize {
103 self.pruned_chunks
104 }
105
106 #[inline]
109 pub fn complete_chunks(&self) -> usize {
110 self.pruned_chunks
111 + self
112 .bitmap
113 .chunks_len()
114 .saturating_sub(if self.is_chunk_aligned() { 0 } else { 1 })
115 }
116
117 #[inline]
119 pub const fn pruned_bits(&self) -> u64 {
120 (self.pruned_chunks as u64)
121 .checked_mul(Self::CHUNK_SIZE_BITS)
122 .expect("invariant violated: pruned_chunks * CHUNK_SIZE_BITS overflows u64")
123 }
124
125 #[inline]
133 pub fn get_bit(&self, bit: u64) -> bool {
134 let chunk_num = Self::to_chunk_index(bit);
135 assert!(chunk_num >= self.pruned_chunks, "bit pruned: {bit}");
136
137 self.bitmap.get(bit - self.pruned_bits())
139 }
140
141 #[inline]
147 pub fn get_chunk_containing(&self, bit: u64) -> &[u8; N] {
148 let chunk_num = Self::to_chunk_index(bit);
149 assert!(chunk_num >= self.pruned_chunks, "bit pruned: {bit}");
150
151 self.bitmap.get_chunk_containing(bit - self.pruned_bits())
153 }
154
155 #[inline]
158 pub const fn get_bit_from_chunk(chunk: &[u8; N], bit: u64) -> bool {
159 BitMap::<N>::get_bit_from_chunk(chunk, bit)
160 }
161
162 #[inline]
164 pub fn last_chunk(&self) -> (&[u8; N], u64) {
165 self.bitmap.last_chunk()
166 }
167
168 pub fn set_bit(&mut self, bit: u64, value: bool) {
176 let chunk_num = Self::to_chunk_index(bit);
177 assert!(chunk_num >= self.pruned_chunks, "bit pruned: {bit}");
178
179 self.bitmap.set(bit - self.pruned_bits(), value);
181 }
182
183 pub fn push(&mut self, bit: bool) {
185 self.bitmap.push(bit);
186 }
187
188 pub fn extend_to(&mut self, new_len: u64) {
191 let current = self.len();
192 if new_len > current {
193 self.bitmap.extend_to(new_len - self.pruned_bits());
194 }
195 }
196
197 pub fn pop(&mut self) -> bool {
203 self.bitmap.pop()
204 }
205
206 pub fn truncate(&mut self, new_len: u64) {
212 assert!(
213 new_len >= self.pruned_bits(),
214 "cannot truncate into pruned region"
215 );
216 self.bitmap.truncate(new_len - self.pruned_bits());
217 }
218
219 pub fn push_byte(&mut self, byte: u8) {
225 self.bitmap.push_byte(byte);
226 }
227
228 pub fn push_chunk(&mut self, chunk: &[u8; N]) {
234 self.bitmap.push_chunk(chunk);
235 }
236
237 pub fn pop_chunk(&mut self) -> [u8; N] {
243 self.bitmap.pop_chunk()
244 }
245
246 pub fn prune_to_bit(&mut self, bit: u64) {
260 assert!(
261 bit <= self.len(),
262 "bit {} out of bounds (len: {})",
263 bit,
264 self.len()
265 );
266
267 let chunk = Self::to_chunk_index(bit);
268 if chunk < self.pruned_chunks {
269 return;
270 }
271
272 let chunks_to_prune = chunk - self.pruned_chunks;
273 self.bitmap.prune_chunks(chunks_to_prune);
274 self.pruned_chunks = chunk;
275 }
276
277 #[inline]
281 pub const fn chunk_byte_bitmask(bit: u64) -> u8 {
282 BitMap::<N>::chunk_byte_bitmask(bit)
283 }
284
285 #[inline]
287 pub const fn chunk_byte_offset(bit: u64) -> usize {
288 BitMap::<N>::chunk_byte_offset(bit)
289 }
290
291 #[inline]
297 pub fn to_chunk_index(bit: u64) -> usize {
298 BitMap::<N>::to_chunk_index(bit)
299 }
300
301 #[inline]
307 pub fn get_chunk(&self, chunk: usize) -> &[u8; N] {
308 assert!(
309 chunk >= self.pruned_chunks,
310 "chunk {chunk} is pruned (pruned_chunks: {})",
311 self.pruned_chunks
312 );
313 self.bitmap.get_chunk(chunk - self.pruned_chunks)
314 }
315
316 pub fn set_chunk_by_index(&mut self, chunk_index: usize, chunk_data: &[u8; N]) {
323 assert!(
324 chunk_index >= self.pruned_chunks,
325 "cannot set pruned chunk {chunk_index} (pruned_chunks: {})",
326 self.pruned_chunks
327 );
328 let bitmap_chunk_idx = chunk_index - self.pruned_chunks;
329
330 if bitmap_chunk_idx + 1 == self.bitmap.chunks_len() {
333 let trailing = self.bitmap.len() % Self::CHUNK_SIZE_BITS;
334 if trailing != 0 {
335 let last_byte = ((trailing - 1) / 8) as usize;
336 let bits_in_last_byte = (trailing % 8) as u32;
337 let byte_mask = if bits_in_last_byte != 0 {
338 !((1u8 << bits_in_last_byte) - 1)
339 } else {
340 0
341 };
342 assert!(
343 chunk_data[last_byte] & byte_mask == 0
344 && chunk_data[last_byte + 1..] == [0u8; N][last_byte + 1..],
345 "non-zero bits beyond bitmap length"
346 );
347 }
348 }
349
350 self.bitmap.set_chunk_by_index(bitmap_chunk_idx, chunk_data);
351 }
352
353 pub(super) fn unprune_chunks(&mut self, chunks: &[[u8; N]]) {
364 assert!(
365 chunks.len() <= self.pruned_chunks,
366 "cannot unprune {} chunks (only {} pruned)",
367 chunks.len(),
368 self.pruned_chunks
369 );
370
371 for chunk in chunks.iter() {
372 self.bitmap.prepend_chunk(chunk);
373 }
374
375 self.pruned_chunks -= chunks.len();
376 }
377}
378
379impl<const N: usize> super::Readable<N> for Prunable<N> {
380 fn complete_chunks(&self) -> usize {
381 Self::complete_chunks(self)
382 }
383 fn get_chunk(&self, chunk: usize) -> [u8; N] {
384 *Self::get_chunk(self, chunk)
385 }
386 fn last_chunk(&self) -> ([u8; N], u64) {
387 let (c, n) = Self::last_chunk(self);
388 (*c, n)
389 }
390 fn pruned_chunks(&self) -> usize {
391 self.pruned_chunks
392 }
393 fn len(&self) -> u64 {
394 Self::len(self)
395 }
396}
397
398impl<const N: usize> Default for Prunable<N> {
399 fn default() -> Self {
400 Self::new()
401 }
402}
403
404impl<const N: usize> Write for Prunable<N> {
405 fn write(&self, buf: &mut impl BufMut) {
406 (self.pruned_chunks as u64).write(buf);
407 self.bitmap.write(buf);
408 }
409}
410
411impl<const N: usize> Read for Prunable<N> {
412 type Cfg = u64;
414
415 fn read_cfg(buf: &mut impl Buf, max_len: &Self::Cfg) -> Result<Self, CodecError> {
416 let pruned_chunks_u64 = u64::read(buf)?;
417
418 let pruned_bits =
420 pruned_chunks_u64
421 .checked_mul(Self::CHUNK_SIZE_BITS)
422 .ok_or(CodecError::Invalid(
423 "Prunable",
424 "pruned_chunks would overflow when computing pruned_bits",
425 ))?;
426
427 let pruned_chunks = usize::try_from(pruned_chunks_u64)
428 .map_err(|_| CodecError::Invalid("Prunable", "pruned_chunks doesn't fit in usize"))?;
429
430 let bitmap = BitMap::<N>::read_cfg(buf, max_len)?;
431
432 pruned_bits
434 .checked_add(bitmap.len())
435 .ok_or(CodecError::Invalid(
436 "Prunable",
437 "total bitmap length (pruned + unpruned) would overflow u64",
438 ))?;
439
440 Ok(Self {
441 bitmap,
442 pruned_chunks,
443 })
444 }
445}
446
447impl<const N: usize> EncodeSize for Prunable<N> {
448 fn encode_size(&self) -> usize {
449 (self.pruned_chunks as u64).encode_size() + self.bitmap.encode_size()
450 }
451}
452
453#[cfg(feature = "arbitrary")]
454impl<const N: usize> arbitrary::Arbitrary<'_> for Prunable<N> {
455 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
456 let mut bitmap = Self {
457 bitmap: BitMap::<N>::arbitrary(u)?,
458 pruned_chunks: 0,
459 };
460 let prune_to = u.int_in_range(0..=bitmap.len())?;
461 bitmap.prune_to_bit(prune_to);
462 Ok(bitmap)
463 }
464}
465
466#[cfg(test)]
467mod tests {
468 use super::{super::Readable, *};
469 use crate::hex;
470 use bytes::BytesMut;
471 use commonware_codec::Encode;
472
473 #[test]
474 fn test_new() {
475 let prunable: Prunable<32> = Prunable::new();
476 assert_eq!(prunable.len(), 0);
477 assert_eq!(prunable.pruned_bits(), 0);
478 assert_eq!(prunable.pruned_chunks(), 0);
479 assert!(prunable.is_empty());
480 assert_eq!(prunable.chunks_len(), 0); }
482
483 #[test]
484 fn test_new_with_pruned_chunks() {
485 let prunable: Prunable<2> = Prunable::new_with_pruned_chunks(1).unwrap();
486 assert_eq!(prunable.len(), 16);
487 assert_eq!(prunable.pruned_bits(), 16);
488 assert_eq!(prunable.pruned_chunks(), 1);
489 assert_eq!(prunable.chunks_len(), 1);
490 }
491
492 #[test]
493 fn test_new_with_pruned_chunks_overflow() {
494 let overflowing_pruned_chunks = (u64::MAX / Prunable::<4>::CHUNK_SIZE_BITS) as usize + 1;
496 let result = Prunable::<4>::new_with_pruned_chunks(overflowing_pruned_chunks);
497
498 assert!(matches!(result, Err(Error::PrunedChunksOverflow)));
499 }
500
501 #[test]
502 fn test_push_and_get_bits() {
503 let mut prunable: Prunable<4> = Prunable::new();
504
505 prunable.push(true);
507 prunable.push(false);
508 prunable.push(true);
509
510 assert_eq!(prunable.len(), 3);
511 assert!(!prunable.is_empty());
512 assert!(prunable.get_bit(0));
513 assert!(!prunable.get_bit(1));
514 assert!(prunable.get_bit(2));
515 }
516
517 #[test]
518 fn test_push_byte() {
519 let mut prunable: Prunable<4> = Prunable::new();
520
521 prunable.push_byte(0xFF);
523 assert_eq!(prunable.len(), 8);
524
525 for i in 0..8 {
527 assert!(prunable.get_bit(i as u64));
528 }
529
530 prunable.push_byte(0x00);
531 assert_eq!(prunable.len(), 16);
532
533 for i in 8..16 {
535 assert!(!prunable.get_bit(i as u64));
536 }
537 }
538
539 #[test]
540 fn test_push_chunk() {
541 let mut prunable: Prunable<4> = Prunable::new();
542 let chunk = hex!("0xAABBCCDD");
543
544 prunable.push_chunk(&chunk);
545 assert_eq!(prunable.len(), 32); let retrieved_chunk = prunable.get_chunk_containing(0);
548 assert_eq!(retrieved_chunk, &chunk);
549 }
550
551 #[test]
552 fn test_set_bit() {
553 let mut prunable: Prunable<4> = Prunable::new();
554
555 prunable.push(false);
557 prunable.push(false);
558 prunable.push(false);
559
560 assert!(!prunable.get_bit(1));
561
562 prunable.set_bit(1, true);
564 assert!(prunable.get_bit(1));
565
566 prunable.set_bit(1, false);
568 assert!(!prunable.get_bit(1));
569 }
570
571 #[test]
572 fn test_pruning_basic() {
573 let mut prunable: Prunable<4> = Prunable::new();
574
575 let chunk1 = hex!("0x01020304");
577 let chunk2 = hex!("0x05060708");
578 let chunk3 = hex!("0x090A0B0C");
579
580 prunable.push_chunk(&chunk1);
581 prunable.push_chunk(&chunk2);
582 prunable.push_chunk(&chunk3);
583
584 assert_eq!(prunable.len(), 96); assert_eq!(prunable.pruned_chunks(), 0);
586
587 prunable.prune_to_bit(32);
589 assert_eq!(prunable.pruned_chunks(), 1);
590 assert_eq!(prunable.pruned_bits(), 32);
591 assert_eq!(prunable.len(), 96); assert_eq!(prunable.get_chunk_containing(32), &chunk2);
595 assert_eq!(prunable.get_chunk_containing(64), &chunk3);
596
597 prunable.prune_to_bit(64);
599 assert_eq!(prunable.pruned_chunks(), 2);
600 assert_eq!(prunable.pruned_bits(), 64);
601 assert_eq!(prunable.len(), 96);
602
603 assert_eq!(prunable.get_chunk_containing(64), &chunk3);
605 }
606
607 #[test]
608 #[should_panic(expected = "bit pruned")]
609 fn test_get_pruned_bit_panics() {
610 let mut prunable: Prunable<4> = Prunable::new();
611
612 prunable.push_chunk(&[1, 2, 3, 4]);
614 prunable.push_chunk(&[5, 6, 7, 8]);
615
616 prunable.prune_to_bit(32);
618
619 prunable.get_bit(0);
621 }
622
623 #[test]
624 #[should_panic(expected = "bit pruned")]
625 fn test_get_pruned_chunk_panics() {
626 let mut prunable: Prunable<4> = Prunable::new();
627
628 prunable.push_chunk(&[1, 2, 3, 4]);
630 prunable.push_chunk(&[5, 6, 7, 8]);
631
632 prunable.prune_to_bit(32);
634
635 prunable.get_chunk_containing(0);
637 }
638
639 #[test]
640 #[should_panic(expected = "bit pruned")]
641 fn test_set_pruned_bit_panics() {
642 let mut prunable: Prunable<4> = Prunable::new();
643
644 prunable.push_chunk(&[1, 2, 3, 4]);
646 prunable.push_chunk(&[5, 6, 7, 8]);
647
648 prunable.prune_to_bit(32);
650
651 prunable.set_bit(0, true);
653 }
654
655 #[test]
656 #[should_panic(expected = "bit 25 out of bounds (len: 24)")]
657 fn test_prune_to_bit_out_of_bounds() {
658 let mut prunable: Prunable<1> = Prunable::new();
659
660 prunable.push_byte(1);
662 prunable.push_byte(2);
663 prunable.push_byte(3);
664
665 prunable.prune_to_bit(25);
667 }
668
669 #[test]
670 fn test_pruning_with_partial_chunk() {
671 let mut prunable: Prunable<4> = Prunable::new();
672
673 prunable.push_chunk(&[0xFF; 4]);
675 prunable.push_chunk(&[0xAA; 4]);
676 prunable.push(true);
677 prunable.push(false);
678 prunable.push(true);
679
680 assert_eq!(prunable.len(), 67); prunable.prune_to_bit(32);
684 assert_eq!(prunable.pruned_chunks(), 1);
685 assert_eq!(prunable.len(), 67);
686
687 assert!(prunable.get_bit(64));
689 assert!(!prunable.get_bit(65));
690 assert!(prunable.get_bit(66));
691 }
692
693 #[test]
694 fn test_prune_idempotent() {
695 let mut prunable: Prunable<4> = Prunable::new();
696
697 prunable.push_chunk(&[1, 2, 3, 4]);
699 prunable.push_chunk(&[5, 6, 7, 8]);
700
701 prunable.prune_to_bit(32);
703 assert_eq!(prunable.pruned_chunks(), 1);
704
705 prunable.prune_to_bit(32);
707 assert_eq!(prunable.pruned_chunks(), 1);
708
709 prunable.prune_to_bit(16);
710 assert_eq!(prunable.pruned_chunks(), 1);
711 }
712
713 #[test]
714 fn test_push_after_pruning() {
715 let mut prunable: Prunable<4> = Prunable::new();
716
717 prunable.push_chunk(&[1, 2, 3, 4]);
719 prunable.push_chunk(&[5, 6, 7, 8]);
720
721 prunable.prune_to_bit(32);
723 assert_eq!(prunable.len(), 64);
724 assert_eq!(prunable.pruned_chunks(), 1);
725
726 prunable.push_chunk(&[9, 10, 11, 12]);
728 assert_eq!(prunable.len(), 96); assert_eq!(prunable.get_chunk_containing(64), &[9, 10, 11, 12]);
732 }
733
734 #[test]
735 fn test_chunk_calculations() {
736 assert_eq!(Prunable::<4>::to_chunk_index(0), 0);
738 assert_eq!(Prunable::<4>::to_chunk_index(31), 0);
739 assert_eq!(Prunable::<4>::to_chunk_index(32), 1);
740 assert_eq!(Prunable::<4>::to_chunk_index(63), 1);
741 assert_eq!(Prunable::<4>::to_chunk_index(64), 2);
742
743 assert_eq!(Prunable::<4>::chunk_byte_offset(0), 0);
745 assert_eq!(Prunable::<4>::chunk_byte_offset(8), 1);
746 assert_eq!(Prunable::<4>::chunk_byte_offset(16), 2);
747 assert_eq!(Prunable::<4>::chunk_byte_offset(24), 3);
748 assert_eq!(Prunable::<4>::chunk_byte_offset(32), 0); assert_eq!(Prunable::<4>::chunk_byte_bitmask(0), 0b00000001);
752 assert_eq!(Prunable::<4>::chunk_byte_bitmask(1), 0b00000010);
753 assert_eq!(Prunable::<4>::chunk_byte_bitmask(7), 0b10000000);
754 assert_eq!(Prunable::<4>::chunk_byte_bitmask(8), 0b00000001); }
756
757 #[test]
758 fn test_last_chunk_with_pruning() {
759 let mut prunable: Prunable<4> = Prunable::new();
760
761 prunable.push_chunk(&[1, 2, 3, 4]);
763 prunable.push_chunk(&[5, 6, 7, 8]);
764 prunable.push(true);
765 prunable.push(false);
766
767 let (_, next_bit) = prunable.last_chunk();
768 assert_eq!(next_bit, 2);
769
770 let chunk_data = *prunable.last_chunk().0;
772
773 prunable.prune_to_bit(32);
775 let (chunk2, next_bit2) = prunable.last_chunk();
776 assert_eq!(next_bit2, 2);
777 assert_eq!(&chunk_data, chunk2);
778 }
779
780 #[test]
781 fn test_different_chunk_sizes() {
782 let mut p8: Prunable<8> = Prunable::new();
784 let mut p16: Prunable<16> = Prunable::new();
785 let mut p32: Prunable<32> = Prunable::new();
786
787 for i in 0..10 {
789 p8.push(i % 2 == 0);
790 p16.push(i % 2 == 0);
791 p32.push(i % 2 == 0);
792 }
793
794 assert_eq!(p8.len(), 10);
796 assert_eq!(p16.len(), 10);
797 assert_eq!(p32.len(), 10);
798
799 for i in 0..10 {
801 let expected = i % 2 == 0;
802 if expected {
803 assert!(p8.get_bit(i));
804 assert!(p16.get_bit(i));
805 assert!(p32.get_bit(i));
806 } else {
807 assert!(!p8.get_bit(i));
808 assert!(!p16.get_bit(i));
809 assert!(!p32.get_bit(i));
810 }
811 }
812 }
813
814 #[test]
815 fn test_get_bit_from_chunk() {
816 let chunk: [u8; 4] = [0b10101010, 0b11001100, 0b11110000, 0b00001111];
817
818 assert!(!Prunable::<4>::get_bit_from_chunk(&chunk, 0));
820 assert!(Prunable::<4>::get_bit_from_chunk(&chunk, 1));
821 assert!(!Prunable::<4>::get_bit_from_chunk(&chunk, 2));
822 assert!(Prunable::<4>::get_bit_from_chunk(&chunk, 3));
823
824 assert!(!Prunable::<4>::get_bit_from_chunk(&chunk, 8));
826 assert!(!Prunable::<4>::get_bit_from_chunk(&chunk, 9));
827 assert!(Prunable::<4>::get_bit_from_chunk(&chunk, 10));
828 assert!(Prunable::<4>::get_bit_from_chunk(&chunk, 11));
829 }
830
831 #[test]
832 fn test_get_chunk() {
833 let mut prunable: Prunable<4> = Prunable::new();
834 let chunk1 = hex!("0x11223344");
835 let chunk2 = hex!("0x55667788");
836 let chunk3 = hex!("0x99AABBCC");
837
838 prunable.push_chunk(&chunk1);
839 prunable.push_chunk(&chunk2);
840 prunable.push_chunk(&chunk3);
841
842 assert_eq!(prunable.get_chunk(0), &chunk1);
844 assert_eq!(prunable.get_chunk(1), &chunk2);
845 assert_eq!(prunable.get_chunk(2), &chunk3);
846
847 prunable.prune_to_bit(32);
849 assert_eq!(prunable.get_chunk(1), &chunk2);
850 assert_eq!(prunable.get_chunk(2), &chunk3);
851 }
852
853 #[test]
854 fn test_pop() {
855 let mut prunable: Prunable<4> = Prunable::new();
856
857 prunable.push(true);
858 prunable.push(false);
859 prunable.push(true);
860 assert_eq!(prunable.len(), 3);
861
862 assert!(prunable.pop());
863 assert_eq!(prunable.len(), 2);
864
865 assert!(!prunable.pop());
866 assert_eq!(prunable.len(), 1);
867
868 assert!(prunable.pop());
869 assert_eq!(prunable.len(), 0);
870 assert!(prunable.is_empty());
871
872 for i in 0..100 {
873 prunable.push(i % 3 == 0);
874 }
875 assert_eq!(prunable.len(), 100);
876
877 for i in (0..100).rev() {
878 let expected = i % 3 == 0;
879 assert_eq!(prunable.pop(), expected);
880 assert_eq!(prunable.len(), i);
881 }
882
883 assert!(prunable.is_empty());
884 }
885
886 #[test]
887 fn test_truncate() {
888 let mut prunable: Prunable<4> = Prunable::new();
889 let expected: Vec<bool> = (0..96).map(|i| i % 3 == 0).collect();
890 for &bit in &expected {
891 prunable.push(bit);
892 }
893
894 prunable.truncate(65);
895 assert_eq!(prunable.len(), 65);
896 for i in 0..65 {
897 assert_eq!(prunable.get_bit(i), expected[i as usize]);
898 }
899
900 prunable.prune_to_bit(32);
901 assert_eq!(prunable.pruned_bits(), 32);
902
903 prunable.truncate(64);
904 assert_eq!(prunable.len(), 64);
905 for i in 32..64 {
906 assert_eq!(prunable.get_bit(i), expected[i as usize]);
907 }
908 }
909
910 #[test]
911 #[should_panic(expected = "cannot truncate into pruned region")]
912 fn test_truncate_into_pruned_region_panics() {
913 let mut prunable: Prunable<4> = Prunable::new();
914 prunable.push_chunk(&[0xFF; 4]);
915 prunable.push_chunk(&[0xFF; 4]);
916 prunable.prune_to_bit(32);
917 prunable.truncate(31);
918 }
919
920 #[test]
921 fn test_pop_chunk() {
922 let mut prunable: Prunable<4> = Prunable::new();
923 const CHUNK_SIZE: u64 = Prunable::<4>::CHUNK_SIZE_BITS;
924
925 let chunk1 = hex!("0xAABBCCDD");
927 prunable.push_chunk(&chunk1);
928 assert_eq!(prunable.len(), CHUNK_SIZE);
929 let popped = prunable.pop_chunk();
930 assert_eq!(popped, chunk1);
931 assert_eq!(prunable.len(), 0);
932 assert!(prunable.is_empty());
933
934 let chunk2 = hex!("0x11223344");
936 let chunk3 = hex!("0x55667788");
937 let chunk4 = hex!("0x99AABBCC");
938
939 prunable.push_chunk(&chunk2);
940 prunable.push_chunk(&chunk3);
941 prunable.push_chunk(&chunk4);
942 assert_eq!(prunable.len(), CHUNK_SIZE * 3);
943
944 assert_eq!(prunable.pop_chunk(), chunk4);
945 assert_eq!(prunable.len(), CHUNK_SIZE * 2);
946
947 assert_eq!(prunable.pop_chunk(), chunk3);
948 assert_eq!(prunable.len(), CHUNK_SIZE);
949
950 assert_eq!(prunable.pop_chunk(), chunk2);
951 assert_eq!(prunable.len(), 0);
952
953 prunable = Prunable::new();
955 let first_chunk = hex!("0xAABBCCDD");
956 let second_chunk = hex!("0x11223344");
957 prunable.push_chunk(&first_chunk);
958 prunable.push_chunk(&second_chunk);
959
960 assert_eq!(prunable.pop_chunk(), second_chunk);
962 assert_eq!(prunable.len(), CHUNK_SIZE);
963
964 for i in 0..CHUNK_SIZE {
965 let byte_idx = (i / 8) as usize;
966 let bit_idx = i % 8;
967 let expected = (first_chunk[byte_idx] >> bit_idx) & 1 == 1;
968 assert_eq!(prunable.get_bit(i), expected);
969 }
970
971 assert_eq!(prunable.pop_chunk(), first_chunk);
972 assert_eq!(prunable.len(), 0);
973 }
974
975 #[test]
976 #[should_panic(expected = "cannot pop chunk when not chunk aligned")]
977 fn test_pop_chunk_not_aligned() {
978 let mut prunable: Prunable<4> = Prunable::new();
979
980 prunable.push_chunk(&[0xFF; 4]);
982 prunable.push(true);
983
984 prunable.pop_chunk();
986 }
987
988 #[test]
989 #[should_panic(expected = "cannot pop chunk: bitmap has fewer than CHUNK_SIZE_BITS bits")]
990 fn test_pop_chunk_insufficient_bits() {
991 let mut prunable: Prunable<4> = Prunable::new();
992
993 prunable.push(true);
995 prunable.push(false);
996
997 prunable.pop_chunk();
999 }
1000
1001 #[test]
1002 fn test_write_read_empty() {
1003 let original: Prunable<4> = Prunable::new();
1004 let encoded = original.encode();
1005
1006 let decoded = Prunable::<4>::read_cfg(&mut encoded.as_ref(), &u64::MAX).unwrap();
1007 assert_eq!(decoded.len(), original.len());
1008 assert_eq!(decoded.pruned_chunks(), original.pruned_chunks());
1009 assert!(decoded.is_empty());
1010 }
1011
1012 #[test]
1013 fn test_write_read_non_empty() {
1014 let mut original: Prunable<4> = Prunable::new();
1015 original.push_chunk(&hex!("0xAABBCCDD"));
1016 original.push_chunk(&hex!("0x11223344"));
1017 original.push(true);
1018 original.push(false);
1019 original.push(true);
1020
1021 let encoded = original.encode();
1022 let decoded = Prunable::<4>::read_cfg(&mut encoded.as_ref(), &u64::MAX).unwrap();
1023
1024 assert_eq!(decoded.len(), original.len());
1025 assert_eq!(decoded.pruned_chunks(), original.pruned_chunks());
1026 assert_eq!(decoded.len(), 67);
1027
1028 for i in 0..original.len() {
1030 assert_eq!(decoded.get_bit(i), original.get_bit(i));
1031 }
1032 }
1033
1034 #[test]
1035 fn test_write_read_with_pruning() {
1036 let mut original: Prunable<4> = Prunable::new();
1037 original.push_chunk(&hex!("0x01020304"));
1038 original.push_chunk(&hex!("0x05060708"));
1039 original.push_chunk(&hex!("0x090A0B0C"));
1040
1041 original.prune_to_bit(32);
1043 assert_eq!(original.pruned_chunks(), 1);
1044 assert_eq!(original.len(), 96);
1045
1046 let encoded = original.encode();
1047 let decoded = Prunable::<4>::read_cfg(&mut encoded.as_ref(), &u64::MAX).unwrap();
1048
1049 assert_eq!(decoded.len(), original.len());
1050 assert_eq!(decoded.pruned_chunks(), original.pruned_chunks());
1051 assert_eq!(decoded.pruned_chunks(), 1);
1052 assert_eq!(decoded.len(), 96);
1053
1054 assert_eq!(decoded.get_chunk_containing(32), &hex!("0x05060708"));
1056 assert_eq!(decoded.get_chunk_containing(64), &hex!("0x090A0B0C"));
1057 }
1058
1059 #[test]
1060 fn test_write_read_with_pruning_2() {
1061 let mut original: Prunable<4> = Prunable::new();
1062
1063 for i in 0..5 {
1065 let chunk = [
1066 (i * 4) as u8,
1067 (i * 4 + 1) as u8,
1068 (i * 4 + 2) as u8,
1069 (i * 4 + 3) as u8,
1070 ];
1071 original.push_chunk(&chunk);
1072 }
1073
1074 original.prune_to_bit(96); assert_eq!(original.pruned_chunks(), 3);
1077 assert_eq!(original.len(), 160);
1078
1079 let encoded = original.encode();
1080 let decoded = Prunable::<4>::read_cfg(&mut encoded.as_ref(), &u64::MAX).unwrap();
1081
1082 assert_eq!(decoded.len(), original.len());
1083 assert_eq!(decoded.pruned_chunks(), 3);
1084
1085 for i in 96..original.len() {
1087 assert_eq!(decoded.get_bit(i), original.get_bit(i));
1088 }
1089 }
1090
1091 #[test]
1092 fn test_encode_size_matches() {
1093 let mut prunable: Prunable<4> = Prunable::new();
1094 prunable.push_chunk(&[1, 2, 3, 4]);
1095 prunable.push_chunk(&[5, 6, 7, 8]);
1096 prunable.push(true);
1097
1098 let size = prunable.encode_size();
1099 let encoded = prunable.encode();
1100
1101 assert_eq!(size, encoded.len());
1102 }
1103
1104 #[test]
1105 fn test_encode_size_with_pruning() {
1106 let mut prunable: Prunable<4> = Prunable::new();
1107 prunable.push_chunk(&[1, 2, 3, 4]);
1108 prunable.push_chunk(&[5, 6, 7, 8]);
1109 prunable.push_chunk(&[9, 10, 11, 12]);
1110
1111 prunable.prune_to_bit(32);
1112
1113 let size = prunable.encode_size();
1114 let encoded = prunable.encode();
1115
1116 assert_eq!(size, encoded.len());
1117 }
1118
1119 #[test]
1120 fn test_read_max_len_validation() {
1121 let mut original: Prunable<4> = Prunable::new();
1122 for _ in 0..10 {
1123 original.push(true);
1124 }
1125
1126 let encoded = original.encode();
1127
1128 assert!(Prunable::<4>::read_cfg(&mut encoded.as_ref(), &100).is_ok());
1130
1131 let result = Prunable::<4>::read_cfg(&mut encoded.as_ref(), &5);
1133 assert!(result.is_err());
1134 }
1135
1136 #[test]
1137 fn test_codec_roundtrip_different_chunk_sizes() {
1138 let mut p8: Prunable<8> = Prunable::new();
1140 let mut p16: Prunable<16> = Prunable::new();
1141 let mut p32: Prunable<32> = Prunable::new();
1142
1143 for i in 0..100 {
1144 let bit = i % 3 == 0;
1145 p8.push(bit);
1146 p16.push(bit);
1147 p32.push(bit);
1148 }
1149
1150 let encoded8 = p8.encode();
1152 let decoded8 = Prunable::<8>::read_cfg(&mut encoded8.as_ref(), &u64::MAX).unwrap();
1153 assert_eq!(decoded8.len(), p8.len());
1154
1155 let encoded16 = p16.encode();
1156 let decoded16 = Prunable::<16>::read_cfg(&mut encoded16.as_ref(), &u64::MAX).unwrap();
1157 assert_eq!(decoded16.len(), p16.len());
1158
1159 let encoded32 = p32.encode();
1160 let decoded32 = Prunable::<32>::read_cfg(&mut encoded32.as_ref(), &u64::MAX).unwrap();
1161 assert_eq!(decoded32.len(), p32.len());
1162 }
1163
1164 #[test]
1165 fn test_read_pruned_chunks_overflow() {
1166 let mut buf = BytesMut::new();
1167
1168 let overflowing_pruned_chunks = (u64::MAX / Prunable::<4>::CHUNK_SIZE_BITS) + 1;
1170 overflowing_pruned_chunks.write(&mut buf);
1171
1172 0u64.write(&mut buf); let result = Prunable::<4>::read_cfg(&mut buf.as_ref(), &u64::MAX);
1177 match result {
1178 Err(CodecError::Invalid(type_name, msg)) => {
1179 assert_eq!(type_name, "Prunable");
1180 assert_eq!(
1181 msg,
1182 "pruned_chunks would overflow when computing pruned_bits"
1183 );
1184 }
1185 Ok(_) => panic!("Expected error but got Ok"),
1186 Err(e) => panic!("Expected Invalid error for pruned_bits overflow, got: {e:?}"),
1187 }
1188 }
1189
1190 #[test]
1191 fn test_read_total_length_overflow() {
1192 let mut buf = BytesMut::new();
1193
1194 let max_safe_pruned_chunks = u64::MAX / Prunable::<4>::CHUNK_SIZE_BITS;
1196 let pruned_bits = max_safe_pruned_chunks * Prunable::<4>::CHUNK_SIZE_BITS;
1197
1198 let remaining_space = u64::MAX - pruned_bits;
1200 let bitmap_len = remaining_space + 1; max_safe_pruned_chunks.write(&mut buf);
1204 bitmap_len.write(&mut buf);
1205
1206 let num_chunks = bitmap_len.div_ceil(Prunable::<4>::CHUNK_SIZE_BITS);
1208 for _ in 0..(num_chunks * 4) {
1209 0u8.write(&mut buf);
1210 }
1211
1212 let result = Prunable::<4>::read_cfg(&mut buf.as_ref(), &u64::MAX);
1214 match result {
1215 Err(CodecError::Invalid(type_name, msg)) => {
1216 assert_eq!(type_name, "Prunable");
1217 assert_eq!(
1218 msg,
1219 "total bitmap length (pruned + unpruned) would overflow u64"
1220 );
1221 }
1222 Ok(_) => panic!("Expected error but got Ok"),
1223 Err(e) => panic!("Expected Invalid error for total length overflow, got: {e:?}"),
1224 }
1225 }
1226
1227 #[test]
1228 fn test_is_chunk_aligned() {
1229 let prunable: Prunable<4> = Prunable::new();
1231 assert!(prunable.is_chunk_aligned());
1232
1233 let mut prunable: Prunable<4> = Prunable::new();
1235 for i in 1..=32 {
1236 prunable.push(i % 2 == 0);
1237 if i == 32 {
1238 assert!(prunable.is_chunk_aligned()); } else {
1240 assert!(!prunable.is_chunk_aligned()); }
1242 }
1243
1244 for i in 33..=64 {
1246 prunable.push(i % 2 == 0);
1247 if i == 64 {
1248 assert!(prunable.is_chunk_aligned()); } else {
1250 assert!(!prunable.is_chunk_aligned()); }
1252 }
1253
1254 let mut prunable: Prunable<4> = Prunable::new();
1256 assert!(prunable.is_chunk_aligned());
1257 prunable.push_chunk(&[1, 2, 3, 4]);
1258 assert!(prunable.is_chunk_aligned()); prunable.push_chunk(&[5, 6, 7, 8]);
1260 assert!(prunable.is_chunk_aligned()); prunable.push(true);
1262 assert!(!prunable.is_chunk_aligned()); let mut prunable: Prunable<4> = Prunable::new();
1266 prunable.push_chunk(&[1, 2, 3, 4]);
1267 prunable.push_chunk(&[5, 6, 7, 8]);
1268 prunable.push_chunk(&[9, 10, 11, 12]);
1269 assert!(prunable.is_chunk_aligned()); prunable.prune_to_bit(32);
1273 assert!(prunable.is_chunk_aligned());
1274 assert_eq!(prunable.len(), 96);
1275
1276 prunable.push(true);
1278 prunable.push(false);
1279 assert!(!prunable.is_chunk_aligned()); prunable.prune_to_bit(64);
1283 assert!(!prunable.is_chunk_aligned()); let prunable: Prunable<4> = Prunable::new_with_pruned_chunks(2).unwrap();
1287 assert!(prunable.is_chunk_aligned()); let mut prunable: Prunable<4> = Prunable::new_with_pruned_chunks(1).unwrap();
1290 assert!(prunable.is_chunk_aligned()); prunable.push(true);
1292 assert!(!prunable.is_chunk_aligned()); let mut prunable: Prunable<4> = Prunable::new();
1296 for _ in 0..4 {
1297 prunable.push_byte(0xFF);
1298 }
1299 assert!(prunable.is_chunk_aligned()); prunable.pop();
1303 assert!(!prunable.is_chunk_aligned()); for _ in 0..31 {
1307 prunable.pop();
1308 }
1309 assert!(prunable.is_chunk_aligned()); }
1311
1312 #[test]
1313 fn test_ones_iter_with_pruning() {
1314 let mut p = Prunable::<4>::new();
1316 for _ in 0..96 {
1317 p.push(false);
1318 }
1319 p.set_bit(40, true);
1321 p.set_bit(55, true);
1322 p.set_bit(64, true);
1323 p.set_bit(90, true);
1324
1325 p.prune_to_bit(32);
1327 assert_eq!(p.pruned_chunks(), 1);
1328 assert_eq!(p.pruned_bits(), 32);
1329
1330 let ones: Vec<u64> = Readable::ones_iter_from(&p, p.pruned_bits()).collect();
1331 assert_eq!(ones, vec![40, 55, 64, 90]);
1332 }
1333
1334 #[test]
1335 fn test_ones_iter_from_midway_with_pruning() {
1336 let mut p = Prunable::<4>::new();
1337 for _ in 0..96 {
1338 p.push(false);
1339 }
1340 p.set_bit(40, true);
1341 p.set_bit(55, true);
1342 p.set_bit(64, true);
1343 p.set_bit(90, true);
1344
1345 p.prune_to_bit(32);
1346
1347 let ones: Vec<u64> = Readable::ones_iter_from(&p, 50).collect();
1349 assert_eq!(ones, vec![55, 64, 90]);
1350
1351 let ones: Vec<u64> = Readable::ones_iter_from(&p, 91).collect();
1353 assert!(ones.is_empty());
1354 }
1355
1356 #[test]
1357 fn test_ones_iter_all_ones_with_pruning() {
1358 let mut p = Prunable::<4>::new();
1359 for _ in 0..96 {
1360 p.push(true);
1361 }
1362
1363 p.prune_to_bit(64);
1365 assert_eq!(p.pruned_chunks(), 2);
1366
1367 let ones: Vec<u64> = Readable::ones_iter_from(&p, p.pruned_bits()).collect();
1368 let expected: Vec<u64> = (64..96).collect();
1369 assert_eq!(ones, expected);
1370 }
1371
1372 #[test]
1373 fn test_ones_iter_empty_after_pruning() {
1374 let mut p = Prunable::<4>::new();
1375 for _ in 0..64 {
1376 p.push(false);
1377 }
1378
1379 p.prune_to_bit(32);
1381 assert_eq!(p.pruned_chunks(), 1);
1382
1383 let ones: Vec<u64> = Readable::ones_iter_from(&p, p.pruned_bits()).collect();
1384 assert!(ones.is_empty());
1385 }
1386
1387 #[test]
1388 fn test_ones_iter_from_pruned_region_fast_forwards() {
1389 let mut p = Prunable::<4>::new();
1390 for _ in 0..64 {
1391 p.push(true);
1392 }
1393 p.prune_to_bit(32);
1394
1395 let ones: Vec<u64> = Readable::ones_iter_from(&p, 0).collect();
1397 let expected: Vec<u64> = (32..64).collect();
1398 assert_eq!(ones, expected);
1399 }
1400
1401 #[test]
1402 fn test_extend_to() {
1403 let mut p = Prunable::<4>::new();
1404 for i in 0..30u64 {
1405 p.push(i % 2 == 0);
1406 }
1407
1408 p.extend_to(5);
1410 assert_eq!(p.len(), 30);
1411
1412 p.extend_to(65);
1414 assert_eq!(p.len(), 65);
1415 for i in 0..30 {
1416 assert_eq!(p.get_bit(i), i % 2 == 0, "original bit {i}");
1417 }
1418 for i in 30..65 {
1419 assert!(!p.get_bit(i), "extended bit {i} should be false");
1420 }
1421
1422 p.prune_to_bit(32);
1424 p.extend_to(100);
1425 assert_eq!(p.len(), 100);
1426 assert_eq!(p.pruned_bits(), 32);
1427 for i in 65..100 {
1428 assert!(!p.get_bit(i), "extended bit {i} should be false");
1429 }
1430 }
1431
1432 #[test]
1435 fn test_set_chunk_by_index_accepts_valid_partial_chunk() {
1436 let mut p = Prunable::<4>::new();
1437 p.extend_to(35); p.set_chunk_by_index(1, &[0b0000_0111, 0, 0, 0]);
1440 assert_eq!(p.get_chunk(1), &[0b0000_0111, 0, 0, 0]);
1441 }
1442
1443 #[test]
1444 fn test_set_chunk_by_index_accepts_full_chunk() {
1445 let mut p = Prunable::<4>::new();
1446 p.extend_to(64); p.set_chunk_by_index(1, &[0xFF; 4]);
1449 assert_eq!(p.get_chunk(1), &[0xFF; 4]);
1450 }
1451
1452 #[test]
1453 #[should_panic(expected = "non-zero bits beyond bitmap length")]
1454 fn test_set_chunk_by_index_rejects_high_bits_in_last_byte() {
1455 let mut p = Prunable::<4>::new();
1456 p.extend_to(35); p.set_chunk_by_index(1, &[0b0000_1000, 0, 0, 0]);
1459 }
1460
1461 #[test]
1462 #[should_panic(expected = "non-zero bits beyond bitmap length")]
1463 fn test_set_chunk_by_index_rejects_nonzero_trailing_bytes() {
1464 let mut p = Prunable::<4>::new();
1465 p.extend_to(35); p.set_chunk_by_index(1, &[0, 0xFF, 0, 0]);
1468 }
1469
1470 #[test]
1471 #[should_panic(expected = "non-zero bits beyond bitmap length")]
1472 fn test_set_chunk_by_index_rejects_nonzero_final_byte() {
1473 let mut p = Prunable::<4>::new();
1474 p.extend_to(35); p.set_chunk_by_index(1, &[0, 0, 0, 1]);
1476 }
1477
1478 #[test]
1479 fn test_set_chunk_by_index_roundtrips_codec() {
1480 let mut p = Prunable::<4>::new();
1481 p.extend_to(35);
1482 p.set_chunk_by_index(1, &[0b0000_0101, 0, 0, 0]);
1483
1484 let encoded = p.encode();
1485 let decoded = Prunable::<4>::read_cfg(&mut encoded.as_ref(), &u64::MAX)
1486 .expect("valid chunk should round-trip");
1487 assert_eq!(decoded.len(), 35);
1488 assert_eq!(decoded.get_chunk(1), &[0b0000_0101, 0, 0, 0]);
1489 }
1490
1491 #[cfg(feature = "arbitrary")]
1492 mod conformance {
1493 use super::*;
1494 use commonware_codec::conformance::CodecConformance;
1495
1496 commonware_conformance::conformance_tests! {
1497 CodecConformance<Prunable<16>>,
1498 }
1499 }
1500}