Skip to main content

indexset/concurrent/
map.rs

1use ::core::borrow::Borrow;
2use ::core::fmt::{Debug, Display, Formatter};
3use ::core::iter::FusedIterator;
4use ::core::ops::RangeBounds;
5use alloc::vec::Vec;
6
7use super::set::BTreeSet;
8use crate::core::node::NodeLike;
9use crate::{cdc::change::ChangeEvent, core::pair::Pair};
10
11/// Pointer-free node layout used by background checkpointing.
12///
13/// The snapshot preserves node boundaries without retaining locks, pointers,
14/// or allocator state. It is intended for quiescent or externally sequenced
15/// capture; ordinary point reads and mutations do not touch it.
16#[cfg(feature = "cdc")]
17#[derive(Clone, Debug, Eq, PartialEq)]
18pub struct Topology<T> {
19    pub node_capacity: usize,
20    pub nodes: Vec<Vec<T>>,
21}
22
23/// A pointer-free topology cannot be attached without violating B-tree node
24/// ordering or capacity invariants.
25#[cfg(feature = "cdc")]
26#[derive(Clone, Debug, Eq, PartialEq)]
27pub enum TopologyError {
28    ZeroNodeCapacity,
29    EmptyNode { index: usize },
30    OversizedNode { index: usize, len: usize, capacity: usize },
31    UnsortedNode { index: usize },
32    OverlappingNodes { left: usize, right: usize },
33}
34
35#[cfg(feature = "cdc")]
36impl Display for TopologyError {
37    fn fmt(&self, formatter: &mut Formatter<'_>) -> ::core::fmt::Result {
38        match self {
39            Self::ZeroNodeCapacity => formatter.write_str("topology node capacity must be non-zero"),
40            Self::EmptyNode { index } => write!(formatter, "topology node {index} is empty"),
41            Self::OversizedNode { index, len, capacity } => write!(
42                formatter,
43                "topology node {index} has {len} entries but capacity is {capacity}"
44            ),
45            Self::UnsortedNode { index } => write!(formatter, "topology node {index} is not strictly ordered"),
46            Self::OverlappingNodes { left, right } => {
47                write!(
48                    formatter,
49                    "topology nodes {left} and {right} overlap or are out of order"
50                )
51            }
52        }
53    }
54}
55
56#[cfg(feature = "cdc")]
57impl ::core::error::Error for TopologyError {}
58
59#[derive(Debug)]
60pub struct BTreeMap<K, V, Node = Vec<Pair<K, V>>>
61where
62    K: Send + Ord + Clone + 'static,
63    V: Send + Clone + 'static,
64    Node: NodeLike<Pair<K, V>>,
65{
66    pub(crate) set: BTreeSet<Pair<K, V>, Node>,
67}
68
69impl<K, V, Node> Default for BTreeMap<K, V, Node>
70where
71    K: Send + Ord + Clone,
72    V: Send + Clone + 'static,
73    Node: NodeLike<Pair<K, V>> + Send + 'static,
74{
75    fn default() -> Self {
76        Self {
77            set: Default::default(),
78        }
79    }
80}
81
82pub struct Iter<'a, K, V, Node>
83where
84    K: Debug + Send + Ord + Clone + 'static,
85    V: Debug + Send + Clone + 'static,
86    Node: NodeLike<Pair<K, V>> + Send + 'static,
87{
88    inner: super::set::Iter<'a, Pair<K, V>, Node>,
89}
90
91impl<'a, K, V, Node> Iterator for Iter<'a, K, V, Node>
92where
93    K: Debug + Send + Ord + Clone + 'static,
94    V: Debug + Send + Clone + 'static,
95    Node: NodeLike<Pair<K, V>> + Send + 'static,
96{
97    type Item = (K, V);
98
99    fn next(&mut self) -> Option<Self::Item> {
100        if let Some(entry) = self.inner.next() {
101            return Some((entry.key, entry.value));
102        }
103
104        None
105    }
106}
107
108impl<'a, K, V, Node> DoubleEndedIterator for Iter<'a, K, V, Node>
109where
110    K: Debug + Send + Ord + Clone + 'static,
111    V: Debug + Send + Clone + 'static,
112    Node: NodeLike<Pair<K, V>> + Send + 'static,
113{
114    fn next_back(&mut self) -> Option<Self::Item> {
115        if let Some(entry) = self.inner.next_back() {
116            return Some((entry.key, entry.value));
117        }
118
119        None
120    }
121}
122
123impl<'a, K, V, Node> FusedIterator for Iter<'a, K, V, Node>
124where
125    K: Debug + Send + Ord + Clone + 'static,
126    V: Debug + Send + Clone + 'static,
127    Node: NodeLike<Pair<K, V>> + Send + 'static,
128{
129}
130
131pub struct Range<'a, K, V, Node>
132where
133    K: Debug + Send + Ord + Clone + 'static,
134    V: Debug + Send + Clone + 'static,
135    Node: NodeLike<Pair<K, V>> + Send + 'static,
136{
137    inner: super::set::Range<'a, Pair<K, V>, Node>,
138}
139
140impl<'a, K, V, Node> Iterator for Range<'a, K, V, Node>
141where
142    K: Debug + Send + Ord + Clone + 'static,
143    V: Debug + Send + Clone + 'static,
144    Node: NodeLike<Pair<K, V>> + Send + 'static,
145{
146    type Item = (K, V);
147
148    fn next(&mut self) -> Option<Self::Item> {
149        if let Some(entry) = self.inner.next() {
150            return Some((entry.key, entry.value));
151        }
152
153        None
154    }
155}
156
157impl<'a, K, V, Node> DoubleEndedIterator for Range<'a, K, V, Node>
158where
159    K: Debug + Send + Ord + Clone + 'static,
160    V: Debug + Send + Clone + 'static,
161    Node: NodeLike<Pair<K, V>> + Send + 'static,
162{
163    fn next_back(&mut self) -> Option<Self::Item> {
164        if let Some(entry) = self.inner.next_back() {
165            return Some((entry.key, entry.value));
166        }
167
168        None
169    }
170}
171
172impl<'a, K, V, Node> FusedIterator for Range<'a, K, V, Node>
173where
174    K: Debug + Send + Ord + Clone + 'static,
175    V: Debug + Send + Clone + 'static,
176    Node: NodeLike<Pair<K, V>> + Send + 'static,
177{
178}
179
180impl<K, V, Node> BTreeMap<K, V, Node>
181where
182    K: Debug + Send + Ord + Clone + 'static,
183    V: Debug + Send + Clone + 'static,
184    Node: NodeLike<Pair<K, V>> + Send + 'static,
185{
186    /// Makes a new, empty, persistent `BTreeMap`.
187    ///
188    /// # Examples
189    ///
190    /// Basic usage:
191    ///
192    /// ```
193    /// use indexset::concurrent::map::BTreeMap;
194    ///
195    /// let mut map = BTreeMap::<usize, &str>::new();
196    ///
197    /// // entries can now be inserted into the empty map
198    /// map.insert(1, "a");
199    /// ```
200    pub fn new() -> Self {
201        Self {
202            set: Default::default(),
203        }
204    }
205    /// Makes a new, empty `BTreeMap` with the given maximum node size. Allocates one vec with
206    /// the capacity set to be the specified node size.
207    ///
208    /// # Examples
209    ///
210    /// ```
211    /// use indexset::concurrent::map::BTreeMap;
212    ///
213    /// let map = BTreeMap::<i32, i32>::with_maximum_node_size(128);
214    pub fn with_maximum_node_size(node_capacity: usize) -> Self {
215        Self {
216            set: BTreeSet::with_maximum_node_size(node_capacity),
217        }
218    }
219    /// Adds full [`Node`] to this set. [`Node`] should be correct node with
220    /// values sorted.
221    #[cfg(feature = "cdc")]
222    pub fn attach_node(&self, node: Node) {
223        self.set.attach_node(node)
224    }
225    /// Attaches persisted [`Node`]s with one topology publication.
226    #[cfg(feature = "cdc")]
227    pub fn attach_nodes(&self, nodes: impl IntoIterator<Item = Node>) {
228        self.set.attach_nodes(nodes)
229    }
230
231    /// Returns detached, read-only snapshots of this map's [`Node`]s.
232    ///
233    /// Callers requiring one coherent logical generation must prevent
234    /// concurrent mutation while collecting.
235    #[cfg(feature = "cdc")]
236    pub fn snapshot_nodes(&self) -> Vec<Node>
237    where
238        Node: Clone,
239    {
240        self.set
241            .index
242            .read()
243            .values()
244            .map(|node| (*node.read()).clone())
245            .collect()
246    }
247
248    /// Copies the exact node boundaries into a pointer-free checkpoint image.
249    ///
250    /// Callers that need a single logical generation must externally prevent
251    /// mutations for the duration of this method, or snapshot a temporary
252    /// index reconstructed from an ordered redo log.
253    #[cfg(feature = "cdc")]
254    pub fn export_topology(&self) -> Topology<Pair<K, V>> {
255        let (node_capacity, nodes) = self.set.export_topology();
256        Topology { node_capacity, nodes }
257    }
258
259    /// Reconstructs a B-tree from a validated pointer-free topology image.
260    #[cfg(feature = "cdc")]
261    pub fn from_topology(topology: Topology<Pair<K, V>>) -> Result<Self, TopologyError> {
262        if topology.node_capacity == 0 {
263            return Err(TopologyError::ZeroNodeCapacity);
264        }
265
266        for (index, node) in topology.nodes.iter().enumerate() {
267            if node.is_empty() {
268                return Err(TopologyError::EmptyNode { index });
269            }
270            if node.len() > topology.node_capacity {
271                return Err(TopologyError::OversizedNode {
272                    index,
273                    len: node.len(),
274                    capacity: topology.node_capacity,
275                });
276            }
277            if node.windows(2).any(|pair| pair[0] >= pair[1]) {
278                return Err(TopologyError::UnsortedNode { index });
279            }
280            if index > 0
281                && topology.nodes[index - 1]
282                    .last()
283                    .expect("non-empty node validated above")
284                    >= node.first().expect("non-empty node validated above")
285            {
286                return Err(TopologyError::OverlappingNodes {
287                    left: index - 1,
288                    right: index,
289                });
290            }
291        }
292
293        let map = Self::with_maximum_node_size(topology.node_capacity);
294        map.attach_nodes(topology.nodes.into_iter().map(|values| {
295            let mut node = Node::with_capacity(topology.node_capacity);
296            for value in values {
297                let (inserted, _) = NodeLike::insert(&mut node, value);
298                debug_assert!(inserted, "validated topology contains unique values");
299            }
300            node
301        }));
302        Ok(map)
303    }
304    /// Returns `true` if the map contains a value for the specified key.
305    ///
306    /// The key may be any borrowed form of the map's key type, but the ordering
307    /// on the borrowed form *must* match the ordering on the key type.
308    ///
309    /// # Examples
310    ///
311    /// Basic usage:
312    ///
313    /// ```
314    /// use indexset::concurrent::map::BTreeMap;
315    ///
316    /// let mut map = BTreeMap::<usize, &str>::new();
317    /// map.insert(1, "a");
318    /// assert_eq!(map.contains_key(&1), true);
319    /// assert_eq!(map.contains_key(&2), false);
320    /// ```
321    pub fn contains_key<Q>(&self, key: &Q) -> bool
322    where
323        Pair<K, V>: Borrow<Q> + Ord,
324        Q: Ord + ?Sized,
325    {
326        self.set.contains(key)
327    }
328    /// Returns a reference to a pair whose key corresponds to the input.
329    ///
330    /// The key may be any borrowed form of the map's key type, but the ordering
331    /// on the borrowed form *must* match the ordering on the key type.
332    ///
333    /// # Examples
334    ///
335    /// Basic usage:
336    ///
337    /// ```
338    /// use indexset::concurrent::map::BTreeMap;
339    ///
340    /// let mut map = BTreeMap::<usize, &str>::new();
341    /// map.insert(1, "a");
342    /// assert_eq!(map.get(&1).and_then(|e| Some(e.get().value)), Some("a"));
343    /// assert_eq!(map.get(&2).and_then(|e| Some(e.get().value)), None);
344    /// ```
345    pub fn get<Q>(&self, key: &Q) -> Option<super::r#ref::Ref<Pair<K, V>, Node>>
346    where
347        Pair<K, V>: Borrow<Q> + Ord,
348        Q: Ord + ?Sized,
349    {
350        self.set.get(key)
351    }
352
353    /// Returns an owned clone from the definitive point-lookup path.
354    ///
355    /// The structural mapping is pinned until the selected node is locked, so
356    /// both hits and misses are authoritative. Only the value is cloned; the
357    /// key remains borrowed. This API requires `V: Clone`.
358    #[inline(always)]
359    pub fn lookup_for_select<Q>(&self, key: &Q) -> Option<V>
360    where
361        Pair<K, V>: Borrow<Q> + Ord,
362        Q: Ord + ?Sized,
363        V: Clone,
364    {
365        self.set.get_with(key, |pair| pair.value.clone())
366    }
367
368    /// Returns an owned clone from the optimistic one-node lookup only.
369    ///
370    /// This is an explicit latency-first primitive for callers that can accept
371    /// a transient false miss during concurrent structural reindexing. Most
372    /// callers should use [`BTreeMap::lookup_for_select`].
373    #[inline(always)]
374    pub fn lookup_for_select_optimistic<Q>(&self, key: &Q) -> Option<V>
375    where
376        Pair<K, V>: Borrow<Q> + Ord,
377        Q: Ord + ?Sized,
378        V: Clone,
379    {
380        self.set.get_with_optimistic(key, |pair| pair.value.clone())
381    }
382
383    /// Inserts a key-value pair into the map.
384    ///
385    /// If the map did not have this key present, it will be inserted.
386    ///
387    /// Otherwise, the value is updated.
388    ///
389    /// [module-level documentation]: index.html#insert-and-complex-keys
390    ///
391    /// # Examples
392    ///
393    /// Basic usage:
394    ///
395    /// ```
396    /// use indexset::concurrent::map::BTreeMap;
397    ///
398    /// let mut map = BTreeMap::<usize, &str>::new();
399    /// assert_eq!(map.insert(37, "a"), None);
400    /// assert_eq!(map.len() == 0, false);
401    ///
402    /// map.insert(37, "b");
403    /// assert_eq!(map.insert(37, "c"), Some("b"));
404    /// assert_eq!(map.get(&37).and_then(|e| Some(e.get().value)), Some("c"));
405    /// ```
406    pub fn insert(&self, key: K, value: V) -> Option<V> {
407        let new_entry = Pair { key, value };
408
409        self.set.put(new_entry).map(|pair| pair.value)
410    }
411    pub fn checked_insert(&self, key: K, value: V) -> Option<()> {
412        let new_entry = Pair { key, value };
413        self.set.put_checked(new_entry).ok().map(|_| ())
414    }
415    /// Inserts a key-value pair into the map and returns old value (if it was
416    /// already in set) with [`ChangeEvent`]'s that describes this insert
417    /// action.
418    pub fn insert_cdc(&self, key: K, value: V) -> (Option<V>, Vec<ChangeEvent<Pair<K, V>>>) {
419        let new_entry = Pair { key, value };
420
421        let (old_value, cdc) = self.set.put_cdc(new_entry);
422
423        (old_value.map(|pair| pair.value), cdc)
424    }
425    pub fn checked_insert_cdc(&self, key: K, value: V) -> Option<Vec<ChangeEvent<Pair<K, V>>>> {
426        let new_entry = Pair { key, value };
427        self.set.put_cdc_checked(new_entry).ok().map(|(_, evs)| evs)
428    }
429    /// Removes a key from the map, returning the key and the value if the key
430    /// was previously in the map.
431    ///
432    /// The key may be any borrowed form of the map's key type, but the ordering
433    /// on the borrowed form *must* match the ordering on the key type.
434    ///
435    /// # Examples
436    ///
437    /// Basic usage:
438    ///
439    /// ```
440    /// use indexset::concurrent::map::BTreeMap;
441    ///
442    /// let map = BTreeMap::<usize, &str>::new();
443    /// map.insert(1, "a");
444    /// assert_eq!(map.remove(&1), Some((1, "a")));
445    /// assert_eq!(map.remove(&1), None);
446    /// ```
447    pub fn remove<Q>(&self, key: &Q) -> Option<(K, V)>
448    where
449        Pair<K, V>: Borrow<Q> + Ord,
450        Q: Ord + ?Sized,
451    {
452        self.set.remove(key).map(|pair| (pair.key, pair.value))
453    }
454    /// Removes a key from the map, returning the key and the value if the key
455    /// was previously in the map and [`ChangeEvent`]s describing changes caused
456    /// by this action.
457    #[allow(clippy::type_complexity)]
458    pub fn remove_cdc<Q>(&self, key: &Q) -> (Option<(K, V)>, Vec<ChangeEvent<Pair<K, V>>>)
459    where
460        Pair<K, V>: Borrow<Q> + Ord,
461        Q: Ord + ?Sized,
462    {
463        let (old_value, cdc) = self.set.remove_cdc(key);
464
465        (old_value.map(|pair| (pair.key, pair.value)), cdc)
466    }
467    /// Returns the number of elements in the map.
468    ///
469    /// # Examples
470    ///
471    /// Basic usage:
472    ///
473    /// ```
474    /// use indexset::concurrent::map::BTreeMap;
475    ///
476    /// let mut a = BTreeMap::<usize, &str>::new();
477    /// assert_eq!(a.len(), 0);
478    /// a.insert(1, "a");
479    /// assert_eq!(a.len(), 1);
480    /// ```
481    pub fn len(&self) -> usize {
482        self.set.len()
483    }
484    /// Returns `true` if the map contains no elements.
485    ///
486    /// # Examples
487    ///
488    /// Basic usage:
489    ///
490    /// ```
491    /// use indexset::concurrent::map::BTreeMap;
492    ///
493    /// let mut a = BTreeMap::<usize, &str>::new();
494    /// assert!(a.is_empty());
495    /// a.insert(1, "a");
496    /// assert!(!a.is_empty());
497    /// ```
498    pub fn is_empty(&self) -> bool {
499        self.set.is_empty()
500    }
501    /// Returns the total number of allocated slots across all internal nodes.
502    ///
503    /// This represents the number of key-value pairs the map can hold
504    /// without reallocating memory in its internal vectors.
505    ///
506    /// # Examples
507    ///
508    /// Basic usage:
509    ///
510    /// ```
511    /// use indexset::concurrent::map::BTreeMap;
512    ///
513    /// let mut a = BTreeMap::<usize, &str>::with_maximum_node_size(16);
514    ///
515    /// a.insert(1, "a");
516    /// a.insert(2, "b");
517    ///
518    /// // Capacity remains the same until node is split or reallocated
519    /// assert_eq!(a.capacity(), 16);
520    /// ```
521    pub fn capacity(&self) -> usize {
522        self.set.capacity()
523    }
524    /// Returns the total number of nodes.
525    ///
526    ///
527    /// # Examples
528    ///
529    /// Basic usage:
530    ///
531    /// ```
532    /// use indexset::concurrent::map::BTreeMap;
533    ///
534    /// let mut a = BTreeMap::<usize, &str>::with_maximum_node_size(16);
535    ///
536    /// a.insert(1, "a");
537    /// a.insert(2, "b");
538    ///
539    /// assert_eq!(a.node_count(), 1);
540    /// ```
541    pub fn node_count(&self) -> usize {
542        self.set.node_count()
543    }
544    /// Gets an iterator over the entries of the map, sorted by key.
545    ///
546    /// # Examples
547    ///
548    /// Basic usage:
549    ///
550    /// ```
551    /// use indexset::concurrent::map::BTreeMap;
552    ///
553    /// let mut map = BTreeMap::<usize, &str>::new();
554    /// map.insert(3, "c");
555    /// map.insert(2, "b");
556    /// map.insert(1, "a");
557    ///
558    /// for (key, value) in map.iter() {
559    ///     println!("{key}: {value}");
560    /// }
561    ///
562    /// let (first_key, first_value) = map.iter().next().unwrap();
563    /// assert_eq!((first_key, first_value), (1, "a"));
564    /// ```
565    pub fn iter(&self) -> Iter<'_, K, V, Node> {
566        Iter { inner: self.set.iter() }
567    }
568    /// Constructs a double-ended iterator over a sub-range of elements in the map.
569    /// The simplest way is to use the range syntax `min..max`, thus `range(min..max)` will
570    /// yield elements from min (inclusive) to max (exclusive).
571    /// The range may also be entered as `(Bound<T>, Bound<T>)`, so for example
572    /// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive
573    /// range from 4 to 10.
574    ///
575    /// # Panics
576    ///
577    /// Panics if range `start > end`.
578    /// Panics if range `start == end` and both bounds are `Excluded`.
579    ///
580    /// # Examples
581    ///
582    /// Basic usage:
583    ///
584    /// ```
585    /// use indexset::concurrent::map::BTreeMap;
586    /// use std::ops::Bound::Included;
587    ///
588    /// let mut map = BTreeMap::<i32, &str>::new();
589    /// map.insert(3, "a");
590    /// map.insert(5, "b");
591    /// map.insert(8, "c");
592    /// for (key, value) in map.range::<i32, _>((Included(&4), Included(&8))) {
593    ///     println!("{key}: {value}");
594    /// }
595    /// assert_eq!(Some((5, "b")), map.range(4..).next());
596    /// ```
597    pub fn range<Q, R>(&self, range: R) -> Range<'_, K, V, Node>
598    where
599        Pair<K, V>: Borrow<Q>,
600        Q: Ord + ?Sized,
601        R: RangeBounds<Q>,
602    {
603        Range {
604            inner: BTreeSet::range(&self.set, range),
605        }
606    }
607}
608
609#[cfg(test)]
610mod tests {
611    use super::BTreeMap;
612    use super::ChangeEvent;
613    use super::Pair;
614    #[cfg(feature = "cdc")]
615    use super::{Topology, TopologyError};
616    use crate::core::constants::DEFAULT_INNER_SIZE;
617    use crate::BTreeSet;
618    use rand::Rng;
619    use scc::HashMap;
620    use std::fmt::Debug;
621    use std::sync::{Arc, Mutex};
622    use std::thread;
623
624    #[cfg(feature = "cdc")]
625    #[test]
626    fn pointer_free_topology_round_trip_preserves_nodes() {
627        let map = BTreeMap::<u64, u64>::with_maximum_node_size(4);
628        for key in 0..37 {
629            map.insert(key, key * 10);
630        }
631
632        let topology = map.export_topology();
633        assert!(topology.nodes.len() > 1);
634        let restored = BTreeMap::<u64, u64>::from_topology(topology.clone()).unwrap();
635
636        assert_eq!(restored.export_topology(), topology);
637        assert_eq!(
638            restored.iter().collect::<Vec<_>>(),
639            (0..37).map(|k| (k, k * 10)).collect::<Vec<_>>()
640        );
641    }
642
643    #[cfg(feature = "cdc")]
644    #[test]
645    fn pointer_free_topology_rejects_invalid_boundaries() {
646        let error = BTreeMap::<u64, u64>::from_topology(Topology {
647            node_capacity: 4,
648            nodes: vec![
649                vec![Pair { key: 1, value: 10 }, Pair { key: 3, value: 30 }],
650                vec![Pair { key: 2, value: 20 }],
651            ],
652        })
653        .unwrap_err();
654        assert_eq!(error, TopologyError::OverlappingNodes { left: 0, right: 1 });
655    }
656
657    #[test]
658    fn test_range_edge_cast() {
659        let maximum_node_size = 3;
660        let map = BTreeMap::<usize, &str>::with_maximum_node_size(maximum_node_size);
661
662        map.insert(1usize, "a");
663
664        map.insert(2usize, "b");
665        map.insert(3usize, "c");
666
667        map.insert(4usize, "d");
668        map.insert(5usize, "e");
669
670        map.insert(6usize, "f");
671        map.insert(7usize, "g");
672
673        let mid_range = map.range::<usize, _>(3..5).collect::<BTreeSet<_>>();
674        assert_eq!(
675            mid_range,
676            vec![(3usize, "c"), (4usize, "d"),].into_iter().collect::<BTreeSet<_>>()
677        );
678    }
679
680    #[test]
681    fn test_split_insert_replaces_left_split_max() {
682        let maximum_node_size = 4;
683        let map = BTreeMap::<usize, &str>::with_maximum_node_size(maximum_node_size);
684
685        for key in 0..maximum_node_size {
686            assert_eq!(map.insert(key, "old"), None);
687        }
688
689        let split_left_max = maximum_node_size / 2 - 1;
690
691        assert_eq!(map.insert(split_left_max, "new"), Some("old"));
692        assert_eq!(map.len(), maximum_node_size);
693        assert_eq!(map.get(&split_left_max).map(|entry| entry.get().value), Some("new"));
694        assert_eq!(map.iter().filter(|(key, _)| *key == split_left_max).count(), 1);
695    }
696
697    #[derive(Debug, Default)]
698    struct PersistedBTreeMap<K, V>
699    where
700        K: Debug + Ord + Clone,
701        V: Debug + Clone + PartialEq,
702    {
703        nodes: std::collections::BTreeMap<K, Vec<Pair<K, V>>>,
704    }
705
706    impl<K: Debug + Ord + Clone, V: Debug + Clone + PartialEq> PersistedBTreeMap<K, V> {
707        fn persist(&mut self, event: &ChangeEvent<Pair<K, V>>) {
708            match event {
709                ChangeEvent::CreateNode { max_value, event_id: _ } => {
710                    let node = vec![max_value.clone()];
711                    self.nodes.insert(max_value.key.clone(), node);
712                }
713                ChangeEvent::RemoveNode { max_value, event_id: _ } => {
714                    self.nodes.remove(&max_value.key);
715                }
716                ChangeEvent::InsertAt {
717                    max_value,
718                    index,
719                    value,
720                    event_id: _,
721                } => {
722                    if let Some(node) = self.nodes.get_mut(&max_value.key) {
723                        node.insert(*index, value.clone());
724                    }
725                    if max_value.key < value.key {
726                        let node = self.nodes.remove(&max_value.key).unwrap();
727                        self.nodes.insert(value.key.clone(), node);
728                    }
729                }
730                ChangeEvent::RemoveAt {
731                    max_value,
732                    index,
733                    value,
734                    event_id: _,
735                } => {
736                    let mut max_removed = false;
737                    if let Some(node) = self.nodes.get_mut(&max_value.key) {
738                        node.remove(*index);
739                        max_removed = max_value.key == value.key;
740                    }
741                    if max_removed {
742                        let node = self.nodes.remove(&max_value.key).unwrap();
743                        if let Some(new_max) = node.last() {
744                            self.nodes.insert(new_max.key.clone(), node);
745                        }
746                    }
747                }
748                ChangeEvent::SplitNode {
749                    max_value,
750                    split_index,
751                    event_id: _,
752                } => {
753                    if let Some(mut old_node) = self.nodes.remove(&max_value.key) {
754                        let new_node = old_node.split_off(*split_index);
755                        let new_max_value = new_node.last().unwrap();
756                        self.nodes.insert(new_max_value.key.clone(), new_node);
757                        let old_max_value = old_node.last().unwrap();
758                        self.nodes.insert(old_max_value.key.clone(), old_node);
759                    }
760                }
761            }
762        }
763
764        fn contains_pair(&self, key: &K, value: &V) -> bool {
765            for node in self.nodes.values() {
766                if let Ok(pos) = node.binary_search(&Pair {
767                    key: key.clone(),
768                    value: value.clone(),
769                }) {
770                    if node[pos].value == *value {
771                        return true;
772                    }
773                }
774            }
775            false
776        }
777    }
778
779    #[cfg(feature = "cdc")]
780    #[test]
781    fn test_cdc_single_insert() {
782        let map = BTreeMap::<usize, &str>::new();
783        let mut mock_state = PersistedBTreeMap::default();
784
785        let (_, events) = map.insert_cdc(1, "a");
786
787        for event in events {
788            mock_state.persist(&event);
789        }
790
791        assert!(mock_state.contains_pair(&1, &"a"));
792        assert!(map.contains_key(&1));
793        assert_eq!(map.get(&1).unwrap().get().value, "a");
794
795        let expected_state = map
796            .set
797            .index
798            .read()
799            .iter()
800            .map(|(key, node)| (key.clone().key, node.read_arc().clone()))
801            .collect::<_>();
802        assert_eq!(mock_state.nodes, expected_state);
803    }
804
805    #[cfg(feature = "cdc")]
806    #[test]
807    fn test_cdc_multiple_inserts() {
808        let map = BTreeMap::<usize, String>::new();
809        let mut mock_state = PersistedBTreeMap::default();
810
811        for i in 0..1024 {
812            let (_, events) = map.insert_cdc(i, format!("val{}", i));
813
814            for event in events {
815                mock_state.persist(&event);
816            }
817        }
818
819        for i in 0..1024 {
820            assert!(mock_state.contains_pair(&i, &format!("val{}", i)));
821            assert!(map.contains_key(&i));
822            assert_eq!(map.get(&i).unwrap().get().value, format!("val{}", i));
823        }
824
825        let expected_state = map
826            .set
827            .index
828            .read()
829            .iter()
830            .map(|(key, node)| (key.clone().key, node.read_arc().clone()))
831            .collect::<_>();
832        assert_eq!(mock_state.nodes, expected_state);
833    }
834
835    #[cfg(feature = "cdc")]
836    #[test]
837    fn test_cdc_updates() {
838        let map = BTreeMap::<usize, &str>::new();
839        let mut mock_state = PersistedBTreeMap::default();
840
841        let (_, events) = map.insert_cdc(1, "a");
842        for event in events {
843            mock_state.persist(&event);
844        }
845
846        let (_, events) = map.insert_cdc(1, "b");
847        for event in events {
848            mock_state.persist(&event);
849        }
850
851        assert!(mock_state.contains_pair(&1, &"b"));
852        assert!(!mock_state.contains_pair(&1, &"a"));
853        assert!(map.contains_key(&1));
854        assert_eq!(map.get(&1).unwrap().get().value, "b");
855
856        let expected_state = map
857            .set
858            .index
859            .read()
860            .iter()
861            .map(|(key, node)| (key.clone().key, node.read_arc().clone()))
862            .collect::<_>();
863        assert_eq!(mock_state.nodes, expected_state);
864    }
865
866    #[cfg(feature = "cdc")]
867    #[test]
868    fn test_cdc_node_splits() {
869        let map = BTreeMap::<usize, String>::new();
870        let mut mock_state = PersistedBTreeMap::default();
871
872        let n = crate::core::constants::DEFAULT_INNER_SIZE + 10;
873
874        for i in 0..n {
875            let (_, events) = map.insert_cdc(i, format!("val{}", i));
876            for event in events {
877                mock_state.persist(&event);
878            }
879        }
880
881        for i in 0..n {
882            assert!(mock_state.contains_pair(&i, &format!("val{}", i)));
883            assert!(map.contains_key(&i));
884            assert_eq!(map.get(&i).unwrap().get().value, format!("val{}", i));
885        }
886
887        assert!(mock_state.nodes.len() > 1);
888
889        let expected_state = map
890            .set
891            .index
892            .read()
893            .iter()
894            .map(|(key, node)| (key.clone().key, node.read_arc().clone()))
895            .collect::<_>();
896        assert_eq!(mock_state.nodes, expected_state);
897    }
898
899    #[cfg(feature = "cdc")]
900    #[test]
901    fn test_concurrent_insert_cdc() {
902        let map = Arc::new(BTreeMap::<usize, String>::new());
903        let num_threads = 8;
904        let operations_per_thread = 1000;
905        let mut handles = vec![];
906
907        let test_data: Vec<Vec<(i32, (usize, String))>> = (0..num_threads)
908            .map(|_| {
909                let mut rng = rand::rng();
910                (0..operations_per_thread)
911                    .map(|_| {
912                        let value = rng.random_range(0..100000);
913                        let operation = rng.random_range(0..2);
914                        (operation, (value, format!("val{value}")))
915                    })
916                    .collect()
917            })
918            .collect();
919
920        let expected_values = Arc::new(Mutex::new(HashMap::new()));
921
922        for thread_idx in 0..num_threads {
923            let map_clone = Arc::clone(&map);
924            let expected_values = Arc::clone(&expected_values);
925            let thread_data = test_data[thread_idx].clone();
926
927            let handle = thread::spawn(move || {
928                let mut events = Vec::new();
929                for (operation, (k, v)) in thread_data {
930                    if operation == 0 {
931                        let (_, evs) = map_clone.insert_cdc(k, v.clone());
932                        events.extend(evs);
933                        let _ = expected_values.lock().unwrap().insert(k, v);
934                    }
935                }
936                events
937            });
938            handles.push(handle);
939        }
940
941        let mut final_events = Vec::new();
942        for handle in handles {
943            let thread_events = handle.join().unwrap();
944            final_events.extend(thread_events)
945        }
946        final_events.sort_by(|ev1, ev2| ev1.id().cmp(&ev2.id()));
947
948        let mut mock_state = PersistedBTreeMap::default();
949        for ev in final_events {
950            mock_state.persist(&ev);
951        }
952
953        // let expected_values = expected_values.lock().unwrap();
954        // assert_eq!(mock_state.len(), expected_values.len());
955
956        let expected_state = map
957            .set
958            .index
959            .read()
960            .iter()
961            .map(|(key, node)| (key.clone().key, node.read_arc().clone()))
962            .collect::<_>();
963        assert_eq!(mock_state.nodes, expected_state);
964    }
965
966    #[cfg(feature = "cdc")]
967    #[test]
968    fn test_cdc_event_ids_sequential_no_gaps() {
969        let map = BTreeMap::<usize, String>::new();
970        let mut all_events = Vec::new();
971
972        for i in 0..100 {
973            let (_, events) = map.insert_cdc(i, format!("val{}", i));
974            all_events.extend(events);
975        }
976        all_events.sort_by_key(|e| e.id());
977
978        // Verify IDs are consecutive with no gaps
979        assert!(!all_events.is_empty(), "Should have at least one event");
980        for i in 1..all_events.len() {
981            let prev_id = all_events[i - 1].id().inner();
982            let curr_id = all_events[i].id().inner();
983            assert_eq!(
984                curr_id,
985                prev_id + 1,
986                "Event IDs should be consecutive: {} followed by {}, but got gap",
987                prev_id,
988                curr_id
989            );
990        }
991    }
992
993    #[cfg(feature = "cdc")]
994    #[test]
995    fn normal_writes_do_not_consume_cdc_event_ids() {
996        let map = BTreeMap::<usize, String>::new();
997
998        map.insert(1, "first".to_owned());
999        map.insert(1, "second".to_owned());
1000        map.remove(&1);
1001
1002        let (_, events) = map.insert_cdc(2, "recorded".to_owned());
1003        assert!(!events.is_empty());
1004        assert_eq!(events[0].id().inner(), 0);
1005    }
1006
1007    #[cfg(feature = "cdc")]
1008    #[test]
1009    fn test_cdc_remove_monotonicity() {
1010        let map = BTreeMap::<usize, String>::new();
1011        let mut all_events = Vec::new();
1012
1013        for i in 0..50 {
1014            let (_, events) = map.insert_cdc(i, format!("val{}", i));
1015            all_events.extend(events);
1016        }
1017
1018        for i in 0..25 {
1019            let (_, events) = map.remove_cdc(&i);
1020            all_events.extend(events);
1021        }
1022
1023        all_events.sort_by_key(|e| e.id());
1024
1025        // Verify IDs are consecutive with no gaps
1026        assert!(!all_events.is_empty(), "Should have at least one event");
1027        for i in 1..all_events.len() {
1028            let prev_id = all_events[i - 1].id().inner();
1029            let curr_id = all_events[i].id().inner();
1030            assert_eq!(
1031                curr_id,
1032                prev_id + 1,
1033                "Event IDs should be consecutive across inserts and removes"
1034            );
1035        }
1036    }
1037
1038    #[cfg(feature = "cdc")]
1039    #[test]
1040    fn test_cdc_split_no_gaps() {
1041        let map = BTreeMap::<usize, String>::new();
1042        let mut all_events = Vec::new();
1043
1044        let n = DEFAULT_INNER_SIZE + 200;
1045        for i in 0..n {
1046            let (_, events) = map.insert_cdc(i, format!("val{}", i));
1047            all_events.extend(events);
1048        }
1049
1050        all_events.sort_by_key(|e| e.id());
1051
1052        assert!(!all_events.is_empty(), "Should have at least one event");
1053        for i in 1..all_events.len() {
1054            let prev_id = all_events[i - 1].id().inner();
1055            let curr_id = all_events[i].id().inner();
1056            assert_eq!(
1057                curr_id,
1058                prev_id + 1,
1059                "Event IDs should be consecutive even during splits"
1060            );
1061        }
1062
1063        // Verify splits actually occurred
1064        let split_events: Vec<_> = all_events
1065            .iter()
1066            .filter(|e| matches!(e, ChangeEvent::SplitNode { .. }))
1067            .collect();
1068        assert!(!split_events.is_empty(), "Should have at least one split event");
1069    }
1070
1071    #[cfg(feature = "cdc")]
1072    #[test]
1073    fn test_concurrent_cdc_no_gaps() {
1074        let map = Arc::new(BTreeMap::<usize, String>::new());
1075        let num_threads = 16;
1076        let operations_per_thread = 500;
1077        let mut handles = vec![];
1078
1079        for thread_idx in 0..num_threads {
1080            let map_clone = Arc::clone(&map);
1081
1082            let handle = thread::spawn(move || {
1083                let mut events = Vec::new();
1084                let base = thread_idx * 10000;
1085                for i in 0..operations_per_thread {
1086                    let value = base + i;
1087                    let (_, evs) = map_clone.insert_cdc(value, format!("val{}", value));
1088                    events.extend(evs);
1089                }
1090                events
1091            });
1092            handles.push(handle);
1093        }
1094
1095        let mut final_events = Vec::new();
1096        for handle in handles {
1097            let thread_events = handle.join().unwrap();
1098            final_events.extend(thread_events);
1099        }
1100
1101        final_events.sort_by_key(|e| e.id());
1102
1103        // Verify no gaps in event IDs
1104        assert!(!final_events.is_empty(), "Should have at least one event");
1105        for i in 1..final_events.len() {
1106            let prev_id = final_events[i - 1].id().inner();
1107            let curr_id = final_events[i].id().inner();
1108            assert_eq!(
1109                curr_id,
1110                prev_id + 1,
1111                "Concurrent event IDs should be consecutive with no gaps: {} -> {}",
1112                prev_id,
1113                curr_id
1114            );
1115        }
1116    }
1117
1118    #[cfg(feature = "cdc")]
1119    #[test]
1120    fn test_cdc_mixed_operations() {
1121        let map = BTreeMap::<usize, String>::new();
1122        let mut all_events = Vec::new();
1123
1124        for i in 0..100 {
1125            let (_, events) = map.insert_cdc(i, format!("val{}", i));
1126            all_events.extend(events);
1127        }
1128
1129        for i in 0..50 {
1130            let (_, events) = map.remove_cdc(&i);
1131            all_events.extend(events);
1132        }
1133
1134        for i in 100..125 {
1135            let (_, events) = map.insert_cdc(i, format!("val{}", i));
1136            all_events.extend(events);
1137        }
1138
1139        all_events.sort_by_key(|e| e.id());
1140
1141        // Verify IDs are consecutive with no gaps
1142        assert!(!all_events.is_empty(), "Should have at least one event");
1143        for i in 1..all_events.len() {
1144            let prev_id = all_events[i - 1].id().inner();
1145            let curr_id = all_events[i].id().inner();
1146            assert_eq!(curr_id, prev_id + 1, "Mixed operation event IDs should be consecutive");
1147        }
1148    }
1149}