reifydb_core/util/
multi.rs1use std::{fmt::Debug, sync::Arc};
5
6use crossbeam_skiplist::SkipMap;
7use reifydb_runtime::sync::rwlock::RwLock;
8
9use crate::common::CommitVersion;
10
11#[derive(Debug)]
12pub struct MultiVersionContainer<T: Debug + Clone + Send + Sync + 'static> {
13 inner: Arc<RwLock<MultiVersionDefInner<T>>>,
14}
15
16#[derive(Debug)]
17struct MultiVersionDefInner<T: Debug + Clone + Send + Sync + 'static> {
18 versions: SkipMap<CommitVersion, Option<T>>,
19}
20
21impl<T: Debug + Clone + Send + Sync + 'static> MultiVersionContainer<T> {
22 pub fn new() -> Self {
23 Self {
24 inner: Arc::new(RwLock::new(MultiVersionDefInner {
25 versions: SkipMap::new(),
26 })),
27 }
28 }
29
30 pub fn insert(&self, version: impl Into<CommitVersion>, value: T) -> Option<Option<T>> {
31 let version = version.into();
32 let inner = self.inner.write();
33 if let Some(entry) = inner.versions.get(&version) {
34 let old_value = entry.value().clone();
35 inner.versions.insert(version, Some(value));
36 Some(old_value)
37 } else {
38 inner.versions.insert(version, Some(value));
39 None
40 }
41 }
42
43 pub fn get(&self, version: impl Into<CommitVersion>) -> Option<T> {
44 let version = version.into();
45 let inner = self.inner.read();
46
47 inner.versions.range(..=version).next_back().and_then(|entry| entry.value().clone())
48 }
49
50 pub fn get_or_tombstone(&self, version: impl Into<CommitVersion>) -> Option<Option<T>> {
51 let version = version.into();
52 let inner = self.inner.read();
53
54 inner.versions.range(..=version).next_back().map(|entry| entry.value().clone())
55 }
56
57 pub fn get_latest(&self) -> Option<T> {
58 let inner = self.inner.read();
59 inner.versions.back().and_then(|entry| entry.value().clone())
60 }
61
62 pub fn versions(&self) -> Vec<CommitVersion> {
63 let inner = self.inner.read();
64 inner.versions.iter().map(|entry| *entry.key()).collect()
65 }
66
67 pub fn remove(&self, version: impl Into<CommitVersion>) -> Option<Option<T>> {
68 let version = version.into();
69 let inner = self.inner.write();
70
71 if let Some(entry) = inner.versions.get(&version) {
72 let old_value = entry.value().clone();
73 inner.versions.insert(version, None);
74 Some(old_value)
75 } else {
76 inner.versions.insert(version, None);
77 None
78 }
79 }
80
81 pub fn len(&self) -> usize {
82 let inner = self.inner.read();
83 inner.versions.len()
84 }
85
86 pub fn is_empty(&self) -> bool {
87 let inner = self.inner.read();
88 inner.versions.is_empty()
89 }
90
91 pub fn clear(&self) {
92 let inner = self.inner.write();
93 inner.versions.clear();
94 }
95}
96
97impl<T: Debug + Clone + Send + Sync + 'static> Clone for MultiVersionContainer<T> {
98 fn clone(&self) -> Self {
99 Self {
100 inner: Arc::clone(&self.inner),
101 }
102 }
103}
104
105impl<T: Debug + Clone + Send + Sync + 'static> Default for MultiVersionContainer<T> {
106 fn default() -> Self {
107 Self::new()
108 }
109}
110
111#[cfg(test)]
112pub mod tests {
113 use super::*;
114
115 #[derive(Debug, Clone, PartialEq)]
116 struct Test {
117 name: String,
118 }
119
120 #[test]
121 fn test_basic_operations() {
122 let multi = MultiVersionContainer::<Test>::new();
123
124 assert!(multi.is_empty());
126 assert_eq!(multi.len(), 0);
127 assert!(multi.get_latest().is_none());
128
129 let def1 = Test {
131 name: "v1".to_string(),
132 };
133 multi.insert(1, def1.clone());
134 assert!(!multi.is_empty());
135 assert_eq!(multi.len(), 1);
136
137 assert_eq!(multi.get(1), Some(def1.clone()));
139 assert_eq!(multi.get(2), Some(def1.clone())); assert_eq!(multi.get_latest(), Some(def1.clone()));
141
142 let def2 = Test {
144 name: "v2".to_string(),
145 };
146 multi.insert(5, def2.clone());
147 assert_eq!(multi.len(), 2);
148 assert_eq!(multi.get(1), Some(def1.clone()));
149 assert_eq!(multi.get(3), Some(def1.clone()));
150 assert_eq!(multi.get(5), Some(def2.clone()));
151 assert_eq!(multi.get(10), Some(def2.clone()));
152 assert_eq!(multi.get_latest(), Some(def2.clone()));
153
154 multi.remove(7);
155 assert_eq!(multi.get(7), None);
156 assert_eq!(multi.get(10), None);
157
158 assert_eq!(multi.get_latest(), None);
159 }
160}