reifydb_core/util/
lru.rs

1// Copyright (c) reifydb.com 2025
2// This file is licensed under the AGPL-3.0-or-later, see license.md file
3
4use std::{collections::HashMap, hash::Hash};
5
6pub struct LruCache<K, V> {
7	map: HashMap<K, usize>,
8	entries: Vec<Entry<K, V>>,
9	head: Option<usize>,
10	tail: Option<usize>,
11	capacity: usize,
12}
13
14struct Entry<K, V> {
15	key: K,
16	value: V,
17	prev: Option<usize>,
18	next: Option<usize>,
19}
20
21impl<K: Hash + Eq + Clone, V> LruCache<K, V> {
22	pub fn new(capacity: usize) -> Self {
23		assert!(capacity > 0, "LRU cache capacity must be greater than 0");
24		Self {
25			map: HashMap::with_capacity(capacity),
26			entries: Vec::with_capacity(capacity),
27			head: None,
28			tail: None,
29			capacity,
30		}
31	}
32
33	pub fn get(&mut self, key: &K) -> Option<&V> {
34		if let Some(&idx) = self.map.get(key) {
35			self.move_to_front(idx);
36			Some(&self.entries[idx].value)
37		} else {
38			None
39		}
40	}
41
42	pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
43		if let Some(&idx) = self.map.get(key) {
44			self.move_to_front(idx);
45			Some(&mut self.entries[idx].value)
46		} else {
47			None
48		}
49	}
50
51	pub fn put(&mut self, key: K, value: V) -> Option<V> {
52		if let Some(&idx) = self.map.get(&key) {
53			let old_value = std::mem::replace(&mut self.entries[idx].value, value);
54			self.move_to_front(idx);
55			return Some(old_value);
56		}
57
58		let evicted = if self.entries.len() >= self.capacity {
59			self.evict_lru()
60		} else {
61			None
62		};
63
64		let idx = self.entries.len();
65		self.entries.push(Entry {
66			key: key.clone(),
67			value,
68			prev: None,
69			next: self.head,
70		});
71
72		if let Some(old_head) = self.head {
73			self.entries[old_head].prev = Some(idx);
74		}
75
76		self.head = Some(idx);
77
78		if self.tail.is_none() {
79			self.tail = Some(idx);
80		}
81
82		self.map.insert(key, idx);
83		evicted
84	}
85
86	pub fn remove(&mut self, key: &K) -> Option<V> {
87		if let Some(idx) = self.map.remove(key) {
88			self.unlink(idx);
89			let entry = self.entries.swap_remove(idx);
90
91			if idx < self.entries.len() {
92				let moved_key = self.entries[idx].key.clone();
93				self.map.insert(moved_key, idx);
94
95				if let Some(prev) = self.entries[idx].prev {
96					self.entries[prev].next = Some(idx);
97				} else {
98					self.head = Some(idx);
99				}
100
101				if let Some(next) = self.entries[idx].next {
102					self.entries[next].prev = Some(idx);
103				} else {
104					self.tail = Some(idx);
105				}
106			}
107
108			Some(entry.value)
109		} else {
110			None
111		}
112	}
113
114	pub fn contains_key(&self, key: &K) -> bool {
115		self.map.contains_key(key)
116	}
117
118	pub fn clear(&mut self) {
119		self.map.clear();
120		self.entries.clear();
121		self.head = None;
122		self.tail = None;
123	}
124
125	pub fn len(&self) -> usize {
126		self.entries.len()
127	}
128
129	pub fn is_empty(&self) -> bool {
130		self.entries.is_empty()
131	}
132
133	pub fn capacity(&self) -> usize {
134		self.capacity
135	}
136
137	pub fn iter(&self) -> LruIter<'_, K, V> {
138		LruIter {
139			entries: &self.entries,
140			current: self.head,
141		}
142	}
143
144	fn move_to_front(&mut self, idx: usize) {
145		if self.head == Some(idx) {
146			return;
147		}
148
149		self.unlink(idx);
150
151		self.entries[idx].prev = None;
152		self.entries[idx].next = self.head;
153
154		if let Some(old_head) = self.head {
155			self.entries[old_head].prev = Some(idx);
156		}
157
158		self.head = Some(idx);
159
160		if self.tail.is_none() {
161			self.tail = Some(idx);
162		}
163	}
164
165	fn unlink(&mut self, idx: usize) {
166		let entry = &self.entries[idx];
167		let prev = entry.prev;
168		let next = entry.next;
169
170		if let Some(p) = prev {
171			self.entries[p].next = next;
172		} else {
173			self.head = next;
174		}
175
176		if let Some(n) = next {
177			self.entries[n].prev = prev;
178		} else {
179			self.tail = prev;
180		}
181	}
182
183	fn evict_lru(&mut self) -> Option<V> {
184		if let Some(tail_idx) = self.tail {
185			let key = self.entries[tail_idx].key.clone();
186			self.remove(&key)
187		} else {
188			None
189		}
190	}
191}
192
193pub struct LruIter<'a, K, V> {
194	entries: &'a [Entry<K, V>],
195	current: Option<usize>,
196}
197
198impl<'a, K, V> Iterator for LruIter<'a, K, V> {
199	type Item = (&'a K, &'a V);
200
201	fn next(&mut self) -> Option<Self::Item> {
202		if let Some(idx) = self.current {
203			let entry = &self.entries[idx];
204			self.current = entry.next;
205			Some((&entry.key, &entry.value))
206		} else {
207			None
208		}
209	}
210}
211
212#[cfg(test)]
213mod tests {
214	use super::*;
215
216	#[test]
217	fn test_basic_operations() {
218		let mut cache = LruCache::new(2);
219
220		assert_eq!(cache.put(1, "a"), None);
221		assert_eq!(cache.put(2, "b"), None);
222		assert_eq!(cache.get(&1), Some(&"a"));
223		assert_eq!(cache.get(&2), Some(&"b"));
224		assert_eq!(cache.len(), 2);
225	}
226
227	#[test]
228	fn test_eviction() {
229		let mut cache = LruCache::new(2);
230
231		cache.put(1, "a");
232		cache.put(2, "b");
233		let evicted = cache.put(3, "c");
234
235		assert_eq!(evicted, Some("a"));
236		assert_eq!(cache.get(&1), None);
237		assert_eq!(cache.get(&2), Some(&"b"));
238		assert_eq!(cache.get(&3), Some(&"c"));
239	}
240
241	#[test]
242	fn test_lru_order() {
243		let mut cache = LruCache::new(2);
244
245		cache.put(1, "a");
246		cache.put(2, "b");
247		cache.get(&1);
248		cache.put(3, "c");
249
250		assert_eq!(cache.get(&1), Some(&"a"));
251		assert_eq!(cache.get(&2), None);
252		assert_eq!(cache.get(&3), Some(&"c"));
253	}
254
255	#[test]
256	fn test_update_existing() {
257		let mut cache = LruCache::new(2);
258
259		cache.put(1, "a");
260		let old = cache.put(1, "b");
261
262		assert_eq!(old, Some("a"));
263		assert_eq!(cache.get(&1), Some(&"b"));
264		assert_eq!(cache.len(), 1);
265	}
266
267	#[test]
268	fn test_remove() {
269		let mut cache = LruCache::new(2);
270
271		cache.put(1, "a");
272		cache.put(2, "b");
273		let removed = cache.remove(&1);
274
275		assert_eq!(removed, Some("a"));
276		assert_eq!(cache.get(&1), None);
277		assert_eq!(cache.len(), 1);
278	}
279
280	#[test]
281	fn test_clear() {
282		let mut cache = LruCache::new(2);
283
284		cache.put(1, "a");
285		cache.put(2, "b");
286		cache.clear();
287
288		assert_eq!(cache.len(), 0);
289		assert!(cache.is_empty());
290	}
291
292	#[test]
293	fn test_contains_key() {
294		let mut cache = LruCache::new(2);
295
296		cache.put(1, "a");
297		assert!(cache.contains_key(&1));
298		assert!(!cache.contains_key(&2));
299	}
300
301	#[test]
302	fn test_iter() {
303		let mut cache = LruCache::new(3);
304
305		cache.put(1, "a");
306		cache.put(2, "b");
307		cache.put(3, "c");
308		cache.get(&1);
309
310		let items: Vec<_> = cache.iter().collect();
311		assert_eq!(items[0], (&1, &"a"));
312		assert_eq!(items[1], (&3, &"c"));
313		assert_eq!(items[2], (&2, &"b"));
314	}
315}