Skip to main content

reifydb_runtime/cache/sync/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::hash::Hash;
5
6use cfg_if::cfg_if;
7
8#[cfg(not(reifydb_single_threaded))]
9pub(crate) mod native;
10#[cfg(reifydb_single_threaded)]
11pub(crate) mod wasm;
12
13cfg_if! {
14	if #[cfg(not(reifydb_single_threaded))] {
15		type LruImpl<K, V> = native::NativeLru<K, V>;
16	} else {
17		type LruImpl<K, V> = wasm::WasmLru<K, V>;
18	}
19}
20
21pub struct SyncLru<K, V>
22where
23	K: Hash + Eq + Clone + Send + Sync + 'static,
24	V: Clone + Send + Sync + 'static,
25{
26	inner: LruImpl<K, V>,
27}
28
29impl<K, V> SyncLru<K, V>
30where
31	K: Hash + Eq + Clone + Send + Sync + 'static,
32	V: Clone + Send + Sync + 'static,
33{
34	pub fn new(capacity: usize) -> Self {
35		assert!(capacity > 0, "LRU cache capacity must be greater than 0");
36		Self {
37			inner: LruImpl::new(capacity),
38		}
39	}
40
41	pub fn get(&self, key: &K) -> Option<V> {
42		self.inner.get(key)
43	}
44
45	pub fn put(&self, key: K, value: V) -> Option<V> {
46		self.inner.put(key, value)
47	}
48
49	pub fn remove(&self, key: &K) -> Option<V> {
50		self.inner.remove(key)
51	}
52
53	pub fn contains_key(&self, key: &K) -> bool {
54		self.inner.contains_key(key)
55	}
56
57	pub fn clear(&self) {
58		self.inner.clear();
59	}
60
61	pub fn len(&self) -> usize {
62		self.inner.len()
63	}
64
65	pub fn is_empty(&self) -> bool {
66		self.len() == 0
67	}
68
69	pub fn capacity(&self) -> usize {
70		self.inner.capacity()
71	}
72
73	pub fn run_pending_tasks(&self) {
74		self.inner.run_pending_tasks();
75	}
76}
77
78#[cfg(test)]
79mod tests {
80	use super::SyncLru;
81
82	#[test]
83	fn test_basic_operations() {
84		let cache = SyncLru::new(2);
85
86		assert_eq!(cache.put(1, "a"), None);
87		assert_eq!(cache.put(2, "b"), None);
88		assert_eq!(cache.get(&1), Some("a"));
89		assert_eq!(cache.get(&2), Some("b"));
90		cache.run_pending_tasks();
91		assert_eq!(cache.len(), 2);
92	}
93
94	#[test]
95	fn test_eviction() {
96		let cache = SyncLru::new(2);
97
98		cache.put(1, "a");
99		cache.put(2, "b");
100		let evicted = cache.put(3, "c");
101		cache.run_pending_tasks();
102
103		assert_eq!(evicted, None);
104		assert_eq!(cache.get(&1), None);
105		assert_eq!(cache.get(&2), Some("b"));
106		assert_eq!(cache.get(&3), Some("c"));
107	}
108
109	#[test]
110	fn test_lru_order() {
111		let cache = SyncLru::new(2);
112
113		cache.put(1, "a");
114		cache.put(2, "b");
115		cache.run_pending_tasks();
116		cache.get(&1); // Access 1, making it more recent than 2
117		cache.run_pending_tasks();
118		cache.put(3, "c"); // Should evict 2 (least recently used)
119		cache.run_pending_tasks();
120
121		assert_eq!(cache.get(&1), Some("a"));
122		assert_eq!(cache.get(&2), None);
123		assert_eq!(cache.get(&3), Some("c"));
124	}
125
126	#[test]
127	fn test_update_existing() {
128		let cache = SyncLru::new(2);
129
130		cache.put(1, "a");
131		let old = cache.put(1, "b");
132
133		assert_eq!(old, Some("a"));
134		assert_eq!(cache.get(&1), Some("b"));
135		cache.run_pending_tasks();
136		assert_eq!(cache.len(), 1);
137	}
138
139	#[test]
140	fn test_remove() {
141		let cache = SyncLru::new(2);
142
143		cache.put(1, "a");
144		cache.put(2, "b");
145		let removed = cache.remove(&1);
146
147		assert_eq!(removed, Some("a"));
148		assert_eq!(cache.get(&1), None);
149		cache.run_pending_tasks();
150		assert_eq!(cache.len(), 1);
151	}
152
153	#[test]
154	fn test_clear() {
155		let cache = SyncLru::new(2);
156
157		cache.put(1, "a");
158		cache.put(2, "b");
159		cache.clear();
160		cache.run_pending_tasks();
161
162		assert_eq!(cache.len(), 0);
163		assert!(cache.is_empty());
164	}
165
166	#[test]
167	fn test_contains_key() {
168		let cache = SyncLru::new(2);
169
170		cache.put(1, "a");
171		assert!(cache.contains_key(&1));
172		assert!(!cache.contains_key(&2));
173	}
174}