Skip to main content

reifydb_core/util/
multi.rs

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