Skip to main content

commonware_utils/bitmap/roaring/
mod.rs

1//! 64-bit roaring-style compressed bitmap.
2//!
3//! Provides a memory-efficient representation for sets of 64-bit integers using
4//! run-length encoding and adaptive container types. This implementation partitions
5//! integers into chunks of 2^16 values using the high 48 bits as the chunk key,
6//! with each chunk stored in one of three container types optimized for different
7//! data densities.
8//!
9//! # Architecture
10//!
11//! Each 64-bit value is split into a high 48-bit key (selecting a container) and
12//! a low 16-bit index (stored within that container):
13//!
14//! ```text
15//! 64-bit value
16//! +-----------------------------------------------+---------------+
17//! |            high 48 bits (key)                 | low 16 bits   |
18//! +-----------------------------------------------+---------------+
19//!                      |                                 |
20//!                      v                                 v
21//!               BTreeMap key                     Container index
22//! ```
23//!
24//! The bitmap stores containers in a sorted map, where each container holds
25//! values in the range `[key * 2^16, (key + 1) * 2^16)`:
26//!
27//! ```text
28//! Bitmap
29//! +------------------------------------------------------------------+
30//! |                     BTreeMap<u64, Container>                     |
31//! +------------------------------------------------------------------+
32//! |  Key 0          |  Key 1          |  Key 2          |    ...     |
33//! |  [0, 65535]     |  [65536, 131071]|  [131072, ...]  |            |
34//! +-----------------+-----------------+-----------------+------------+
35//!         |                 |                 |
36//!         v                 v                 v
37//! +--------------+  +--------------+  +--------------+
38//! |    Array     |  |    Bitmap    |  |     Run      |
39//! | [3, 7, 42,   |  | 1011010...   |  | [(0, 4095),  |
40//! |  100, 8000]  |  | (8KB bits)   |  |  (8200, ..)] |
41//! +--------------+  +--------------+  +--------------+
42//!   Sparse data       Dense data      Few runs
43//!   (<= 4096 vals)    (many runs)     (any density)
44//! ```
45//!
46//! # Container Types
47//!
48//! | Type   | Use Case                                 | Storage                  |
49//! |--------|------------------------------------------|--------------------------|
50//! | Array  | Sparse data                              | Sorted `Vec<u16>`        |
51//! | Bitmap | Dense data with many disjoint runs       | `[u64; 1024]` (8 KB)     |
52//! | Run    | Data with few maximal runs (any density) | Sorted `Vec<(u16, u16)>` |
53//!
54//! Containers automatically convert between variants on each `insert` /
55//! `insert_range` to maintain a compact representation. The Bitmap→Run
56//! transition uses a hysteresis band on the bitmap's run count, so a container
57//! that hovers near break-even doesn't thrash between variants. See the container module
58//! for the full transition table and threshold values.
59//!
60//! ```text
61//! Array --[> 4096 values]--> Bitmap <--[run-count threshold]--> Run
62//! ```
63//!
64//! # References
65//!
66//! * <https://arxiv.org/pdf/1402.6407>: Better bitmap performance with Roaring bitmaps
67//! * <https://github.com/RoaringBitmap/RoaringFormatSpec>: Roaring Bitmap Format Specification
68//! * <https://github.com/RoaringBitmap/roaring-rs>: roaring-rs Crate
69
70mod container;
71#[cfg(any(test, feature = "fuzz"))]
72commonware_macros::stability_mod!(ALPHA, pub mod fuzz);
73mod ops;
74mod prunable;
75
76#[cfg(not(feature = "std"))]
77use alloc::collections::BTreeMap;
78use bytes::{Buf, BufMut};
79use commonware_codec::{EncodeSize, Error as CodecError, RangeCfg, Read, Write};
80use container::Container;
81use core::ops::Range;
82pub use prunable::Prunable;
83#[cfg(feature = "std")]
84use std::collections::BTreeMap;
85
86/// Maximum container key (high 48 bits of a u64).
87const MAX_KEY: u64 = (1u64 << 48) - 1;
88
89/// Extracts the high 48 bits (container key) from a 64-bit value.
90const fn high_bits(value: u64) -> u64 {
91    value >> 16
92}
93
94/// Extracts the low 16 bits (container index) from a 64-bit value.
95const fn low_bits(value: u64) -> u16 {
96    value as u16
97}
98
99/// Combines a container key and index into a 64-bit value.
100const fn combine(key: u64, index: u16) -> u64 {
101    (key << 16) | (index as u64)
102}
103
104/// A 64-bit roaring-style compressed bitmap.
105///
106/// This is an append-only data structure optimized for memory efficiency and
107/// fast set operations. Values can be inserted but not removed.
108///
109/// # Example
110///
111/// ```
112/// use commonware_utils::bitmap::roaring::Bitmap;
113///
114/// let mut bitmap = Bitmap::new();
115/// bitmap.insert(42);
116/// bitmap.insert(100);
117/// bitmap.insert_range(1000..2000);
118///
119/// assert!(bitmap.contains(42));
120/// assert!(bitmap.contains(1500));
121/// assert!(!bitmap.contains(500));
122///
123/// // Iterate over values
124/// for value in bitmap.iter().take(10) {
125///     println!("{}", value);
126/// }
127/// ```
128#[derive(Clone, Debug, Default, PartialEq, Eq)]
129pub struct Bitmap {
130    /// Map from high 48 bits to container storing low 16 bits.
131    containers: BTreeMap<u64, Container>,
132}
133
134impl Bitmap {
135    fn from_containers(containers: BTreeMap<u64, Container>) -> Result<Self, CodecError> {
136        if let Some((&max_key, _)) = containers.last_key_value() {
137            if max_key > MAX_KEY {
138                return Err(CodecError::Invalid(
139                    "Bitmap",
140                    "container key exceeds 48-bit range",
141                ));
142            }
143        }
144
145        if containers.values().any(|c| c.is_empty()) {
146            return Err(CodecError::Invalid("Bitmap", "empty container"));
147        }
148
149        Ok(Self { containers })
150    }
151
152    /// Creates an empty roaring bitmap.
153    pub const fn new() -> Self {
154        Self {
155            containers: BTreeMap::new(),
156        }
157    }
158
159    /// Returns the number of values in the bitmap.
160    pub fn len(&self) -> u64 {
161        self.containers.values().map(|c| c.len() as u64).sum()
162    }
163
164    /// Returns whether the bitmap is empty.
165    pub fn is_empty(&self) -> bool {
166        self.containers.is_empty()
167    }
168
169    /// Returns the number of containers in the bitmap.
170    pub fn container_count(&self) -> usize {
171        self.containers.len()
172    }
173
174    /// Clears all values from the bitmap.
175    pub fn clear(&mut self) {
176        self.containers.clear();
177    }
178
179    /// Checks if the bitmap contains the given value.
180    pub fn contains(&self, value: u64) -> bool {
181        let key = high_bits(value);
182        let index = low_bits(value);
183        self.containers.get(&key).is_some_and(|c| c.contains(index))
184    }
185
186    /// Inserts a value into the bitmap.
187    ///
188    /// Returns `true` if the value was newly inserted, `false` if it already existed.
189    pub fn insert(&mut self, value: u64) -> bool {
190        let key = high_bits(value);
191        let index = low_bits(value);
192        self.containers.entry(key).or_default().insert(index)
193    }
194
195    /// Inserts a range of values into the bitmap.
196    ///
197    /// Returns the number of values newly inserted.
198    pub fn insert_range(&mut self, range: Range<u64>) -> u64 {
199        let Range { start, end } = range;
200        if start >= end {
201            return 0;
202        }
203
204        let start_key = high_bits(start);
205        let end_key = high_bits(end - 1);
206        let mut inserted = 0u64;
207
208        for key in start_key..=end_key {
209            let container_start = if key == start_key { low_bits(start) } else { 0 };
210            let container_end = if key == end_key {
211                let last_value = low_bits(end - 1);
212                if last_value == u16::MAX {
213                    None
214                } else {
215                    Some(last_value + 1)
216                }
217            } else {
218                None
219            };
220
221            let container = self.containers.entry(key).or_default();
222            if container_start == 0 && container_end.is_none() {
223                inserted += container::bitmap::BITS as u64 - container.len() as u64;
224                *container = Container::Run(container::Run::full());
225                continue;
226            }
227            match container_end {
228                Some(container_end) => {
229                    inserted += container.insert_range(container_start..container_end) as u64;
230                }
231                None => {
232                    inserted += container.insert_range(container_start..u16::MAX) as u64;
233                    if container.insert(u16::MAX) {
234                        inserted += 1;
235                    }
236                }
237            }
238        }
239
240        inserted
241    }
242
243    /// Returns an iterator over the values in sorted order.
244    pub fn iter(&self) -> impl Iterator<Item = u64> + '_ {
245        self.containers
246            .iter()
247            .flat_map(|(&key, container)| container.iter().map(move |index| combine(key, index)))
248    }
249
250    /// Returns an iterator over the values in the range in sorted order.
251    pub fn iter_range(&self, range: Range<u64>) -> impl Iterator<Item = u64> + '_ {
252        let Range { start, end } = range;
253        let start_key = high_bits(start);
254        let end_key_exclusive = if start >= end {
255            start_key
256        } else {
257            high_bits(end - 1) + 1
258        };
259        let end_key = end_key_exclusive.saturating_sub(1);
260
261        self.containers
262            .range(start_key..end_key_exclusive)
263            .flat_map(move |(&key, container)| {
264                let container_start = if key == start_key { low_bits(start) } else { 0 };
265                let container_end = if key == end_key {
266                    low_bits(end - 1) as u32 + 1
267                } else {
268                    container::bitmap::BITS
269                };
270                container
271                    .iter_range(container_start as u32..container_end)
272                    .map(move |index| combine(key, index))
273            })
274    }
275
276    /// Returns the minimum value in the bitmap, if any.
277    pub fn min(&self) -> Option<u64> {
278        self.containers
279            .first_key_value()
280            .and_then(|(&key, container)| container.min().map(|index| combine(key, index)))
281    }
282
283    /// Returns the maximum value in the bitmap, if any.
284    pub fn max(&self) -> Option<u64> {
285        self.containers
286            .last_key_value()
287            .and_then(|(&key, container)| container.max().map(|index| combine(key, index)))
288    }
289
290    #[cfg(test)]
291    const fn containers(&self) -> &BTreeMap<u64, Container> {
292        &self.containers
293    }
294
295    /// Drops all containers whose key is strictly less than `target_key`.
296    fn truncate_containers_below(&mut self, target_key: u64) {
297        // BTreeMap::split_off returns the half with keys >= target_key.
298        let kept = self.containers.split_off(&target_key);
299        self.containers = kept;
300    }
301
302    /// Returns counts of Array, Bitmap, and Run containers.
303    ///
304    /// Available only for tests and analysis benchmarks.
305    #[cfg(any(test, feature = "analysis"))]
306    pub fn container_variant_counts(&self) -> (usize, usize, usize) {
307        let mut counts = (0, 0, 0);
308        for container in self.containers.values() {
309            match container {
310                Container::Array(_) => counts.0 += 1,
311                Container::Bitmap(_) => counts.1 += 1,
312                Container::Run(_) => counts.2 += 1,
313            }
314        }
315        counts
316    }
317}
318
319impl Extend<u64> for Bitmap {
320    fn extend<I: IntoIterator<Item = u64>>(&mut self, iter: I) {
321        for value in iter {
322            self.insert(value);
323        }
324    }
325}
326
327impl FromIterator<u64> for Bitmap {
328    fn from_iter<I: IntoIterator<Item = u64>>(iter: I) -> Self {
329        let mut bitmap = Self::new();
330        bitmap.extend(iter);
331        bitmap
332    }
333}
334
335impl Write for Bitmap {
336    fn write(&self, buf: &mut impl BufMut) {
337        self.containers.write(buf);
338    }
339}
340
341impl EncodeSize for Bitmap {
342    fn encode_size(&self) -> usize {
343        self.containers.encode_size()
344    }
345}
346
347impl Read for Bitmap {
348    /// Configuration for decoding: range limit on number of containers.
349    ///
350    /// Use `RangeCfg::new(..=max_containers)` to limit memory allocation.
351    type Cfg = RangeCfg<usize>;
352
353    fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, CodecError> {
354        // Use BTreeMap's codec which validates sorted/unique keys and bounds count.
355        let containers = BTreeMap::<u64, Container>::read_cfg(buf, &(*cfg, ((), ())))?;
356        Self::from_containers(containers)
357    }
358}
359
360#[cfg(feature = "arbitrary")]
361impl arbitrary::Arbitrary<'_> for Bitmap {
362    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
363        let num_containers = u.int_in_range(0..=1000)?;
364        let mut containers = BTreeMap::new();
365        let mut prev_key = 0u64;
366
367        for _ in 0..num_containers {
368            // Generate increasing keys
369            let key = if containers.is_empty() {
370                u.int_in_range(0..=MAX_KEY)?
371            } else {
372                let remaining = MAX_KEY - prev_key;
373                if remaining == 0 {
374                    break;
375                }
376                prev_key.saturating_add(u.int_in_range(1..=remaining)?)
377            };
378            if key > MAX_KEY {
379                break;
380            }
381            // `Container::arbitrary` can produce an empty container (zero-length
382            // Array/Run, all-zero Bitmap). Bitmap rejects empty containers via
383            // `try_from` and the decoder, so skip them here to keep generated
384            // values round-trippable through encode/decode.
385            let container = Container::arbitrary(u)?;
386            if container.is_empty() {
387                continue;
388            }
389            containers.insert(key, container);
390            prev_key = key;
391        }
392
393        Ok(Self { containers })
394    }
395}
396
397#[cfg(test)]
398mod tests {
399    use super::*;
400    use commonware_codec::Encode;
401
402    #[test]
403    fn test_new_and_empty() {
404        let bitmap = Bitmap::new();
405        assert!(bitmap.is_empty());
406        assert_eq!(bitmap.len(), 0);
407        assert_eq!(bitmap.container_count(), 0);
408    }
409
410    #[test]
411    fn test_insert_and_contains() {
412        let mut bitmap = Bitmap::new();
413
414        assert!(bitmap.insert(42));
415        assert!(bitmap.insert(100));
416        assert!(bitmap.insert(1000000));
417        assert!(!bitmap.insert(42)); // Duplicate
418
419        assert_eq!(bitmap.len(), 3);
420        assert!(bitmap.contains(42));
421        assert!(bitmap.contains(100));
422        assert!(bitmap.contains(1000000));
423        assert!(!bitmap.contains(50));
424    }
425
426    #[test]
427    fn test_insert_range() {
428        let mut bitmap = Bitmap::new();
429
430        let inserted = bitmap.insert_range(100..200);
431        assert_eq!(inserted, 100);
432        assert_eq!(bitmap.len(), 100);
433
434        for i in 100..200 {
435            assert!(bitmap.contains(i), "missing value {}", i);
436        }
437        assert!(!bitmap.contains(99));
438        assert!(!bitmap.contains(200));
439    }
440
441    #[test]
442    fn test_insert_range_spanning_containers() {
443        let mut bitmap = Bitmap::new();
444
445        // Insert range that spans multiple containers
446        let start = 65530; // Near end of first container
447        let end = 65550; // Into second container
448        let inserted = bitmap.insert_range(start..end);
449        assert_eq!(inserted, 20);
450
451        for i in start..end {
452            assert!(bitmap.contains(i), "missing value {}", i);
453        }
454    }
455
456    #[test]
457    fn test_iterator() {
458        let mut bitmap = Bitmap::new();
459        bitmap.insert(100);
460        bitmap.insert(10);
461        bitmap.insert(1000);
462        bitmap.insert(5);
463
464        let values: Vec<_> = bitmap.iter().collect();
465        assert_eq!(values, vec![5, 10, 100, 1000]);
466    }
467
468    #[test]
469    fn test_iter_range() {
470        let mut bitmap = Bitmap::new();
471        for i in 0..100 {
472            bitmap.insert(i);
473        }
474
475        let values: Vec<_> = bitmap.iter_range(25..75).collect();
476        assert_eq!(values.len(), 50);
477        assert_eq!(values[0], 25);
478        assert_eq!(values[49], 74);
479    }
480
481    #[test]
482    fn test_iter_range_reversed_cross_container_empty() {
483        let mut bitmap = Bitmap::new();
484        bitmap.insert(1);
485        bitmap.insert(70_000);
486
487        let start = 70_000;
488        let end = 10;
489        let values: Vec<_> = bitmap.iter_range(start..end).collect();
490        assert!(values.is_empty());
491    }
492
493    #[test]
494    fn test_min_max() {
495        let mut bitmap = Bitmap::new();
496        assert_eq!(bitmap.min(), None);
497        assert_eq!(bitmap.max(), None);
498
499        bitmap.insert(50);
500        bitmap.insert(10);
501        bitmap.insert(100);
502
503        assert_eq!(bitmap.min(), Some(10));
504        assert_eq!(bitmap.max(), Some(100));
505    }
506
507    #[test]
508    fn test_clear() {
509        let mut bitmap = Bitmap::new();
510        bitmap.insert(1);
511        bitmap.insert(2);
512        bitmap.insert(3);
513
514        bitmap.clear();
515        assert!(bitmap.is_empty());
516        assert_eq!(bitmap.len(), 0);
517    }
518
519    #[test]
520    fn test_from_iter_empty() {
521        let bitmap: Bitmap = core::iter::empty::<u64>().collect();
522        assert!(bitmap.is_empty());
523    }
524
525    #[test]
526    fn test_from_iter_basic() {
527        let bitmap: Bitmap = [5u64, 10, 15, 100].into_iter().collect();
528        assert_eq!(bitmap.len(), 4);
529        let values: Vec<_> = bitmap.iter().collect();
530        assert_eq!(values, vec![5, 10, 15, 100]);
531    }
532
533    #[test]
534    fn test_from_iter_unsorted_with_duplicates() {
535        // Verifies the iterator order doesn't matter and duplicates dedup.
536        let bitmap: Bitmap = [100u64, 5, 100, 50, 5, 10].into_iter().collect();
537        assert_eq!(bitmap.len(), 4);
538        let values: Vec<_> = bitmap.iter().collect();
539        assert_eq!(values, vec![5, 10, 50, 100]);
540    }
541
542    #[test]
543    fn test_from_iter_range_expression() {
544        // The motivating ergonomic case: collect a Range directly.
545        let bitmap: Bitmap = (0u64..1000).collect();
546        assert_eq!(bitmap.len(), 1000);
547        assert!(bitmap.contains(0));
548        assert!(bitmap.contains(999));
549        assert!(!bitmap.contains(1000));
550    }
551
552    #[test]
553    fn test_from_iter_multi_container() {
554        // Spans multiple 16-bit container shelves.
555        let bitmap: Bitmap = [1u64, 65_537, 131_073, 1_000_000].into_iter().collect();
556        assert_eq!(bitmap.len(), 4);
557        assert_eq!(bitmap.container_count(), 4);
558    }
559
560    #[test]
561    fn test_extend_into_existing() {
562        let mut bitmap: Bitmap = [1u64, 2, 3].into_iter().collect();
563        bitmap.extend([3u64, 4, 5]);
564        let values: Vec<_> = bitmap.iter().collect();
565        assert_eq!(values, vec![1, 2, 3, 4, 5]);
566    }
567
568    #[test]
569    fn test_high_low_bits() {
570        // Test boundary values
571        assert_eq!(high_bits(0), 0);
572        assert_eq!(low_bits(0), 0);
573
574        assert_eq!(high_bits(65535), 0);
575        assert_eq!(low_bits(65535), 65535);
576
577        assert_eq!(high_bits(65536), 1);
578        assert_eq!(low_bits(65536), 0);
579
580        assert_eq!(high_bits(u64::MAX), (1u64 << 48) - 1);
581        assert_eq!(low_bits(u64::MAX), u16::MAX);
582    }
583
584    #[test]
585    fn test_combine() {
586        assert_eq!(combine(0, 0), 0);
587        assert_eq!(combine(0, 65535), 65535);
588        assert_eq!(combine(1, 0), 65536);
589        assert_eq!(combine(1, 1), 65537);
590    }
591
592    #[test]
593    fn test_large_values() {
594        let mut bitmap = Bitmap::new();
595
596        let large_value = 1u64 << 40;
597        bitmap.insert(large_value);
598        bitmap.insert(large_value + 1);
599        bitmap.insert(large_value + 1000);
600
601        assert!(bitmap.contains(large_value));
602        assert!(bitmap.contains(large_value + 1));
603        assert!(bitmap.contains(large_value + 1000));
604        assert!(!bitmap.contains(large_value + 2));
605    }
606
607    #[test]
608    fn test_codec_roundtrip_empty() {
609        use commonware_codec::{Decode, Encode};
610
611        let bitmap = Bitmap::new();
612        let encoded = bitmap.encode();
613        let decoded = Bitmap::decode_cfg(encoded, &(..).into()).unwrap();
614        assert_eq!(bitmap, decoded);
615    }
616
617    #[test]
618    fn test_codec_roundtrip_sparse() {
619        use commonware_codec::{Decode, Encode};
620
621        let mut bitmap = Bitmap::new();
622        bitmap.insert(42);
623        bitmap.insert(100);
624        bitmap.insert(1000000);
625
626        let encoded = bitmap.encode();
627        let decoded = Bitmap::decode_cfg(encoded, &(..).into()).unwrap();
628        assert_eq!(bitmap, decoded);
629    }
630
631    #[test]
632    fn test_codec_roundtrip_dense() {
633        use commonware_codec::{Decode, Encode};
634
635        let mut bitmap = Bitmap::new();
636        bitmap.insert_range(0..5000);
637
638        let encoded = bitmap.encode();
639        let decoded = Bitmap::decode_cfg(encoded, &(..).into()).unwrap();
640        assert_eq!(bitmap, decoded);
641    }
642
643    #[test]
644    fn test_codec_roundtrip_multiple_containers() {
645        use commonware_codec::{Decode, Encode};
646
647        let mut bitmap = Bitmap::new();
648        bitmap.insert_range(0..100);
649        bitmap.insert_range(65536..65636);
650        bitmap.insert(1u64 << 40);
651
652        let encoded = bitmap.encode();
653        let decoded = Bitmap::decode_cfg(encoded, &(..).into()).unwrap();
654        assert_eq!(bitmap, decoded);
655    }
656
657    #[test]
658    fn test_codec_roundtrip_large_mixed_variants() {
659        // Exercises BTreeMap-codec scaling and confirms all three Container variants
660        // roundtrip correctly when packed into one bitmap. 600 containers across
661        // disjoint shelves: 200 sparse (Array), 200 dense alternating (Bitmap),
662        // 200 contiguous ranges (Run).
663        use commonware_codec::{Decode, Encode};
664
665        let mut bitmap = Bitmap::new();
666
667        // Sparse shelves: 100 values each, well below MAX_CARDINALITY.
668        for shelf in 0..200u64 {
669            let base = shelf * 65_536;
670            for i in 0..100u64 {
671                bitmap.insert(base + i * 500);
672            }
673        }
674
675        // Dense shelves: 5000 alternating values. Above MAX_CARDINALITY (Array→Bitmap)
676        // with run count above the Run-conversion threshold so they stay Bitmap.
677        for shelf in 200..400u64 {
678            let base = shelf * 65_536;
679            for i in 0..5_000u64 {
680                bitmap.insert(base + i * 2);
681            }
682        }
683
684        // Run shelves: one contiguous range. After Array→Bitmap conversion at
685        // MAX_CARDINALITY, the single-run state triggers Bitmap→Run.
686        for shelf in 400..600u64 {
687            let base = shelf * 65_536;
688            bitmap.insert_range(base..base + 50_000);
689        }
690
691        assert_eq!(bitmap.container_count(), 600);
692
693        let (arrays, bitmaps, runs) = bitmap.container_variant_counts();
694        assert!(
695            arrays > 0 && bitmaps > 0 && runs > 0,
696            "expected all three variants, got A={arrays} B={bitmaps} R={runs}"
697        );
698
699        let encoded = bitmap.encode();
700        let decoded = Bitmap::decode_cfg(encoded, &(..=1000usize).into()).unwrap();
701        assert_eq!(decoded, bitmap);
702    }
703
704    #[test]
705    fn test_codec_container_limit() {
706        use commonware_codec::{Decode, Encode, Error};
707
708        let mut bitmap = Bitmap::new();
709        // Create 3 containers
710        bitmap.insert(0);
711        bitmap.insert(65536);
712        bitmap.insert(131072);
713        assert_eq!(bitmap.container_count(), 3);
714
715        let encoded = bitmap.encode();
716
717        // Should succeed with limit >= 3
718        let decoded = Bitmap::decode_cfg(encoded.clone(), &(..=3).into()).unwrap();
719        assert_eq!(bitmap, decoded);
720
721        // Should fail with limit < 3
722        let result = Bitmap::decode_cfg(encoded, &(..=2).into());
723        assert!(matches!(result, Err(Error::InvalidLength(3))));
724    }
725
726    #[test]
727    fn test_from_containers_rejects_out_of_range_key() {
728        use commonware_codec::Error;
729
730        let mut malformed: BTreeMap<u64, Container> = BTreeMap::new();
731        let mut container = Container::new();
732        container.insert(0);
733        malformed.insert(1u64 << 48, container);
734
735        let result = Bitmap::from_containers(malformed);
736        assert!(
737            matches!(
738                result,
739                Err(Error::Invalid("Bitmap", msg)) if msg.contains("48-bit")
740            ),
741            "expected Invalid(\"Bitmap\", ...), got {result:?}"
742        );
743    }
744
745    #[test]
746    fn test_from_containers_accepts_in_range_keys() {
747        let mut map: BTreeMap<u64, Container> = BTreeMap::new();
748        let mut container = Container::new();
749        container.insert(42);
750        map.insert(MAX_KEY, container);
751
752        let bm = Bitmap::from_containers(map).unwrap();
753        assert!(bm.contains(combine(MAX_KEY, 42)));
754    }
755
756    #[test]
757    fn test_from_containers_rejects_empty_container() {
758        // An empty container has no values to find via iteration, but the key still
759        // shows up in `container_count()` and confuses `min`/`max`. Reject at
760        // construction before decode can accept it.
761        use commonware_codec::Error;
762
763        let mut map: BTreeMap<u64, Container> = BTreeMap::new();
764        map.insert(0, Container::new());
765
766        let result = Bitmap::from_containers(map);
767        assert!(
768            matches!(result, Err(Error::Invalid("Bitmap", "empty container"))),
769            "expected Invalid(\"Bitmap\", \"empty container\"), got {result:?}"
770        );
771    }
772
773    #[test]
774    fn test_decode_rejects_out_of_range_key() {
775        use commonware_codec::{Decode, Encode, Error};
776
777        let mut malformed: BTreeMap<u64, Container> = BTreeMap::new();
778        let mut container = Container::new();
779        container.insert(0);
780        malformed.insert(1u64 << 48, container);
781
782        // BTreeMap shares its codec format with `Bitmap`, so we can encode directly.
783        let bytes = malformed.encode();
784
785        let result = Bitmap::decode_cfg(bytes, &(..=10usize).into());
786        assert!(
787            matches!(
788                result,
789                Err(Error::Invalid("Bitmap", msg)) if msg.contains("48-bit")
790            ),
791            "expected Invalid(\"Bitmap\", ...), got {result:?}"
792        );
793    }
794
795    #[test]
796    fn test_insert_range_at_container_boundary_regression() {
797        // Regression test for bug where insert_range failed when the range
798        // ended exactly at a container boundary (low_bits(end-1) == u16::MAX).
799        // The bug was that saturating_add(1) on u16::MAX saturates to u16::MAX
800        // instead of wrapping to 0, causing incorrect range insertion.
801        //
802        // Original crash input: InsertRange { start: 18446744071497449473, len: 65535 }
803
804        // Test case 1: Range ending at first container boundary (65535)
805        let mut bitmap = Bitmap::new();
806        let inserted = bitmap.insert_range(0..65536);
807        assert_eq!(inserted, 65536);
808        assert_eq!(bitmap.len(), 65536);
809        for i in 0u64..65536 {
810            assert!(bitmap.contains(i), "missing value {}", i);
811        }
812        assert!(!bitmap.contains(65536));
813
814        // Test case 2: Range ending at container boundary within a single container
815        let mut bitmap = Bitmap::new();
816        let inserted = bitmap.insert_range(65000..65536);
817        assert_eq!(inserted, 536);
818        assert_eq!(bitmap.len(), 536);
819        for i in 65000u64..65536 {
820            assert!(bitmap.contains(i), "missing value {}", i);
821        }
822
823        // Test case 3: Range spanning containers ending at boundary
824        let mut bitmap = Bitmap::new();
825        let inserted = bitmap.insert_range(65530..131072);
826        assert_eq!(inserted, 65542);
827        assert_eq!(bitmap.len(), 65542);
828        for i in 65530u64..131072 {
829            assert!(bitmap.contains(i), "missing value {}", i);
830        }
831
832        // Test case 4: The actual fuzzer crash input (large values near u64::MAX)
833        let start: u64 = 18446744071497449473;
834        let len: u16 = 65535;
835        let end = start.saturating_add(len as u64);
836        let expected_len = end - start;
837
838        let mut bitmap = Bitmap::new();
839        let inserted = bitmap.insert_range(start..end);
840        assert_eq!(inserted, expected_len);
841        assert_eq!(bitmap.len(), expected_len);
842    }
843
844    #[test]
845    fn test_encode_size_empty() {
846        let bm = Bitmap::new();
847        assert_eq!(bm.encode_size(), bm.encode().len());
848    }
849
850    #[test]
851    fn test_encode_size_grows_with_containers() {
852        let mut bm = Bitmap::new();
853        let s0 = bm.encode_size();
854        // Three values in three different high-48-bit shelves => three containers.
855        bm.insert(0);
856        bm.insert(65_536);
857        bm.insert(131_072);
858        let s3 = bm.encode_size();
859        assert_eq!(bm.container_count(), 3);
860        assert!(s3 > s0);
861        assert_eq!(s3, bm.encode().len());
862    }
863
864    #[test]
865    fn test_encode_size_dense_uses_bitmap_container() {
866        // Force a single container past the Array→Bitmap threshold AND keep it there by
867        // using alternating values that produce many runs, defeating the Bitmap→Run
868        // auto-conversion (which fires only when run_count is below the threshold).
869        let mut bm = Bitmap::new();
870        for i in 0u64..5000 {
871            bm.insert(i * 2);
872        }
873        // Bitmap container alone is ~8 KB.
874        assert!(bm.encode_size() >= 8192);
875        assert_eq!(bm.encode_size(), bm.encode().len());
876    }
877
878    #[cfg(feature = "arbitrary")]
879    mod conformance {
880        use commonware_codec::conformance::CodecConformance;
881
882        commonware_conformance::conformance_tests! {
883            CodecConformance<super::Bitmap>,
884        }
885    }
886}