1use std::{collections::HashMap, hash::Hash, mem};
5
6struct SlabNode<K, V> {
7 key: K,
8 value: V,
9 prev: Option<usize>,
10 next: Option<usize>,
11}
12
13pub struct SlabLru<K, V> {
14 map: HashMap<K, usize>,
15 nodes: Vec<Option<SlabNode<K, V>>>,
16 free: Vec<usize>,
17 head: Option<usize>,
18 tail: Option<usize>,
19 capacity: usize,
20}
21
22impl<K: Hash + Eq + Clone, V: Clone> SlabLru<K, V> {
23 pub fn new(capacity: usize) -> Self {
24 assert!(capacity > 0, "LRU cache capacity must be greater than 0");
25 Self {
26 map: HashMap::new(),
27 nodes: Vec::new(),
28 free: Vec::new(),
29 head: None,
30 tail: None,
31 capacity,
32 }
33 }
34
35 pub fn unbounded() -> Self {
36 Self::new(usize::MAX)
37 }
38
39 pub fn get(&mut self, key: &K) -> Option<V> {
40 if let Some(&idx) = self.map.get(key) {
41 self.move_to_front(idx);
42 Some(self.node(idx).value.clone())
43 } else {
44 None
45 }
46 }
47
48 pub fn put(&mut self, key: K, value: V) -> Option<V> {
49 if let Some(&idx) = self.map.get(&key) {
50 let old = mem::replace(&mut self.node_mut(idx).value, value);
51 self.move_to_front(idx);
52 return Some(old);
53 }
54
55 if self.map.len() >= self.capacity {
56 self.evict_tail();
57 }
58
59 let idx = self.alloc_node(key.clone(), value);
60 self.map.insert(key, idx);
61 self.push_front(idx);
62 None
63 }
64
65 pub fn remove(&mut self, key: &K) -> Option<V> {
66 if let Some(idx) = self.map.remove(key) {
67 self.unlink(idx);
68 self.free.push(idx);
69 self.nodes[idx].take().map(|node| node.value)
70 } else {
71 None
72 }
73 }
74
75 pub fn pop_tail(&mut self) -> Option<(K, V)> {
76 let idx = self.tail?;
77 self.unlink(idx);
78 let node = self.nodes[idx].take()?;
79 self.map.remove(&node.key);
80 self.free.push(idx);
81 Some((node.key, node.value))
82 }
83
84 pub fn contains_key(&self, key: &K) -> bool {
85 self.map.contains_key(key)
86 }
87
88 pub fn clear(&mut self) {
89 self.map.clear();
90 self.nodes.clear();
91 self.free.clear();
92 self.head = None;
93 self.tail = None;
94 }
95
96 pub fn len(&self) -> usize {
97 self.map.len()
98 }
99
100 pub fn is_empty(&self) -> bool {
101 self.map.is_empty()
102 }
103
104 pub fn capacity(&self) -> usize {
105 self.capacity
106 }
107
108 pub fn values(&self) -> impl Iterator<Item = &V> {
109 self.nodes.iter().filter_map(|slot| slot.as_ref().map(|node| &node.value))
110 }
111
112 pub fn keys(&self) -> impl Iterator<Item = &K> {
113 self.nodes.iter().filter_map(|slot| slot.as_ref().map(|node| &node.key))
114 }
115
116 pub const fn entry_struct_bytes() -> usize {
117 mem::size_of::<Option<SlabNode<K, V>>>() + mem::size_of::<K>() + mem::size_of::<usize>() * 2
118 }
119
120 pub fn struct_bytes(&self) -> usize {
121 self.nodes.capacity() * Self::entry_struct_bytes() + self.free.capacity() * mem::size_of::<usize>()
122 }
123
124 fn node(&self, idx: usize) -> &SlabNode<K, V> {
125 self.nodes[idx].as_ref().expect("occupied slab slot")
126 }
127
128 fn node_mut(&mut self, idx: usize) -> &mut SlabNode<K, V> {
129 self.nodes[idx].as_mut().expect("occupied slab slot")
130 }
131
132 fn alloc_node(&mut self, key: K, value: V) -> usize {
133 let node = SlabNode {
134 key,
135 value,
136 prev: None,
137 next: None,
138 };
139 if let Some(idx) = self.free.pop() {
140 self.nodes[idx] = Some(node);
141 idx
142 } else {
143 self.nodes.push(Some(node));
144 self.nodes.len() - 1
145 }
146 }
147
148 fn evict_tail(&mut self) {
149 if let Some(idx) = self.tail {
150 self.unlink(idx);
151 if let Some(node) = self.nodes[idx].take() {
152 self.map.remove(&node.key);
153 }
154 self.free.push(idx);
155 }
156 }
157
158 fn push_front(&mut self, idx: usize) {
159 let head = self.head;
160 {
161 let node = self.node_mut(idx);
162 node.prev = None;
163 node.next = head;
164 }
165 if let Some(h) = head {
166 self.node_mut(h).prev = Some(idx);
167 }
168 self.head = Some(idx);
169 if self.tail.is_none() {
170 self.tail = Some(idx);
171 }
172 }
173
174 fn unlink(&mut self, idx: usize) {
175 let (prev, next) = {
176 let node = self.node(idx);
177 (node.prev, node.next)
178 };
179 match prev {
180 Some(p) => self.node_mut(p).next = next,
181 None => self.head = next,
182 }
183 match next {
184 Some(n) => self.node_mut(n).prev = prev,
185 None => self.tail = prev,
186 }
187 let node = self.node_mut(idx);
188 node.prev = None;
189 node.next = None;
190 }
191
192 fn move_to_front(&mut self, idx: usize) {
193 if self.head == Some(idx) {
194 return;
195 }
196 self.unlink(idx);
197 self.push_front(idx);
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::SlabLru;
204
205 #[test]
206 fn test_basic_operations() {
207 let mut cache = SlabLru::new(2);
208
209 assert_eq!(cache.put(1, "a"), None);
210 assert_eq!(cache.put(2, "b"), None);
211 assert_eq!(cache.get(&1), Some("a"));
212 assert_eq!(cache.get(&2), Some("b"));
213 assert_eq!(cache.len(), 2);
214 }
215
216 #[test]
217 fn test_eviction_removes_lru() {
218 let mut cache = SlabLru::new(2);
219
220 cache.put(1, "a");
221 cache.put(2, "b");
222 let evicted = cache.put(3, "c");
224
225 assert_eq!(evicted, None);
226 assert_eq!(cache.get(&1), None);
227 assert_eq!(cache.get(&2), Some("b"));
228 assert_eq!(cache.get(&3), Some("c"));
229 assert_eq!(cache.len(), 2);
230 }
231
232 #[test]
233 fn test_get_promotes_recency() {
234 let mut cache = SlabLru::new(2);
235
236 cache.put(1, "a");
237 cache.put(2, "b");
238 cache.get(&1); cache.put(3, "c"); assert_eq!(cache.get(&1), Some("a"));
242 assert_eq!(cache.get(&2), None);
243 assert_eq!(cache.get(&3), Some("c"));
244 }
245
246 #[test]
247 fn test_update_existing_returns_old_and_keeps_len() {
248 let mut cache = SlabLru::new(2);
249
250 cache.put(1, "a");
251 let old = cache.put(1, "b");
252
253 assert_eq!(old, Some("a"));
254 assert_eq!(cache.get(&1), Some("b"));
255 assert_eq!(cache.len(), 1);
256 }
257
258 #[test]
259 fn test_remove() {
260 let mut cache = SlabLru::new(2);
261
262 cache.put(1, "a");
263 cache.put(2, "b");
264
265 assert_eq!(cache.remove(&1), Some("a"));
266 assert_eq!(cache.get(&1), None);
267 assert_eq!(cache.len(), 1);
268 assert_eq!(cache.remove(&999), None);
269 }
270
271 #[test]
272 fn test_clear_then_reuse() {
273 let mut cache = SlabLru::new(2);
274
275 cache.put(1, "a");
276 cache.put(2, "b");
277 cache.clear();
278
279 assert_eq!(cache.len(), 0);
280 assert!(cache.is_empty());
281 assert_eq!(cache.put(5, "e"), None);
283 assert_eq!(cache.get(&5), Some("e"));
284 }
285
286 #[test]
287 fn test_contains_key_does_not_promote() {
288 let mut cache = SlabLru::new(2);
289
290 cache.put(1, "a");
291 cache.put(2, "b");
292 assert!(cache.contains_key(&1));
294 cache.put(3, "c");
295
296 assert_eq!(cache.get(&1), None);
297 assert_eq!(cache.get(&2), Some("b"));
298 }
299
300 #[test]
301 fn test_keys_yields_every_resident_key() {
302 let mut cache = SlabLru::new(2);
305 cache.put(1, "a");
306 cache.put(2, "b");
307 cache.put(3, "c"); let mut keys: Vec<i32> = cache.keys().copied().collect();
310 keys.sort_unstable();
311 assert_eq!(keys, vec![2, 3], "keys() must reflect exactly the resident set after eviction");
312 }
313
314 #[test]
315 #[should_panic(expected = "capacity must be greater than 0")]
316 fn test_zero_capacity_panics() {
317 let _cache: SlabLru<i32, i32> = SlabLru::new(0);
318 }
319
320 #[test]
321 fn test_pop_tail_removes_lru_first() {
322 let mut cache = SlabLru::unbounded();
325 cache.put(1, "a");
326 cache.put(2, "b");
327 cache.put(3, "c");
328 cache.get(&1); assert_eq!(cache.pop_tail(), Some((2, "b")));
331 assert_eq!(cache.pop_tail(), Some((3, "c")));
332 assert_eq!(cache.len(), 1);
333 assert_eq!(cache.get(&2), None);
334 assert_eq!(cache.pop_tail(), Some((1, "a")));
335 assert_eq!(cache.pop_tail(), None);
336 assert!(cache.is_empty());
337
338 cache.put(9, "z");
340 assert_eq!(cache.get(&9), Some("z"));
341 }
342
343 #[test]
344 fn test_unbounded_never_evicts_on_put() {
345 let mut cache = SlabLru::unbounded();
348 for k in 0..10_000i32 {
349 cache.put(k, k);
350 }
351 assert_eq!(cache.len(), 10_000);
352 assert_eq!(cache.get(&0), Some(0));
353 }
354
355 #[test]
356 fn test_slab_recycles_slots_no_unbounded_growth() {
357 let cap = 8usize;
360 let mut cache = SlabLru::new(cap);
361 for k in 0..1000i32 {
362 cache.put(k, k * 10);
363 assert!(cache.len() <= cap);
364 }
365
366 assert_eq!(cache.len(), cap);
367 assert_eq!(cache.nodes.len(), cap);
368 assert!(cache.free.is_empty());
369
370 for k in (1000 - cap as i32)..1000 {
372 assert_eq!(cache.get(&k), Some(k * 10));
373 }
374 assert_eq!(cache.get(&0), None);
375 }
376}