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<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::with_capacity(capacity),
27 nodes: Vec::with_capacity(capacity),
28 free: Vec::new(),
29 head: None,
30 tail: None,
31 capacity,
32 }
33 }
34
35 pub fn get(&mut self, key: &K) -> Option<V> {
36 if let Some(&idx) = self.map.get(key) {
37 self.move_to_front(idx);
38 Some(self.nodes[idx].value.clone())
39 } else {
40 None
41 }
42 }
43
44 pub fn put(&mut self, key: K, value: V) -> Option<V> {
45 if let Some(&idx) = self.map.get(&key) {
46 let old = mem::replace(&mut self.nodes[idx].value, value);
47 self.move_to_front(idx);
48 return Some(old);
49 }
50
51 if self.map.len() >= self.capacity {
52 self.evict_tail();
53 }
54
55 let idx = self.alloc_node(key.clone(), value);
56 self.map.insert(key, idx);
57 self.push_front(idx);
58 None
59 }
60
61 pub fn remove(&mut self, key: &K) -> Option<V> {
62 if let Some(idx) = self.map.remove(key) {
63 self.unlink(idx);
64 self.free.push(idx);
65 Some(self.nodes[idx].value.clone())
66 } else {
67 None
68 }
69 }
70
71 pub fn contains_key(&self, key: &K) -> bool {
72 self.map.contains_key(key)
73 }
74
75 pub fn clear(&mut self) {
76 self.map.clear();
77 self.nodes.clear();
78 self.free.clear();
79 self.head = None;
80 self.tail = None;
81 }
82
83 pub fn len(&self) -> usize {
84 self.map.len()
85 }
86
87 pub fn is_empty(&self) -> bool {
88 self.map.is_empty()
89 }
90
91 pub fn capacity(&self) -> usize {
92 self.capacity
93 }
94
95 fn alloc_node(&mut self, key: K, value: V) -> usize {
96 let node = SlabNode {
97 key,
98 value,
99 prev: None,
100 next: None,
101 };
102 if let Some(idx) = self.free.pop() {
103 self.nodes[idx] = node;
104 idx
105 } else {
106 self.nodes.push(node);
107 self.nodes.len() - 1
108 }
109 }
110
111 fn evict_tail(&mut self) {
112 if let Some(idx) = self.tail {
113 self.unlink(idx);
114 self.map.remove(&self.nodes[idx].key);
115 self.free.push(idx);
116 }
117 }
118
119 fn push_front(&mut self, idx: usize) {
120 self.nodes[idx].prev = None;
121 self.nodes[idx].next = self.head;
122 if let Some(h) = self.head {
123 self.nodes[h].prev = Some(idx);
124 }
125 self.head = Some(idx);
126 if self.tail.is_none() {
127 self.tail = Some(idx);
128 }
129 }
130
131 fn unlink(&mut self, idx: usize) {
132 let prev = self.nodes[idx].prev;
133 let next = self.nodes[idx].next;
134 match prev {
135 Some(p) => self.nodes[p].next = next,
136 None => self.head = next,
137 }
138 match next {
139 Some(n) => self.nodes[n].prev = prev,
140 None => self.tail = prev,
141 }
142 self.nodes[idx].prev = None;
143 self.nodes[idx].next = None;
144 }
145
146 fn move_to_front(&mut self, idx: usize) {
147 if self.head == Some(idx) {
148 return;
149 }
150 self.unlink(idx);
151 self.push_front(idx);
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::SlabLru;
158
159 #[test]
160 fn test_basic_operations() {
161 let mut cache = SlabLru::new(2);
162
163 assert_eq!(cache.put(1, "a"), None);
164 assert_eq!(cache.put(2, "b"), None);
165 assert_eq!(cache.get(&1), Some("a"));
166 assert_eq!(cache.get(&2), Some("b"));
167 assert_eq!(cache.len(), 2);
168 }
169
170 #[test]
171 fn test_eviction_removes_lru() {
172 let mut cache = SlabLru::new(2);
173
174 cache.put(1, "a");
175 cache.put(2, "b");
176 let evicted = cache.put(3, "c");
179
180 assert_eq!(evicted, None);
181 assert_eq!(cache.get(&1), None);
182 assert_eq!(cache.get(&2), Some("b"));
183 assert_eq!(cache.get(&3), Some("c"));
184 assert_eq!(cache.len(), 2);
185 }
186
187 #[test]
188 fn test_get_promotes_recency() {
189 let mut cache = SlabLru::new(2);
190
191 cache.put(1, "a");
192 cache.put(2, "b");
193 cache.get(&1); cache.put(3, "c"); assert_eq!(cache.get(&1), Some("a"));
197 assert_eq!(cache.get(&2), None);
198 assert_eq!(cache.get(&3), Some("c"));
199 }
200
201 #[test]
202 fn test_update_existing_returns_old_and_keeps_len() {
203 let mut cache = SlabLru::new(2);
204
205 cache.put(1, "a");
206 let old = cache.put(1, "b");
207
208 assert_eq!(old, Some("a"));
209 assert_eq!(cache.get(&1), Some("b"));
210 assert_eq!(cache.len(), 1);
211 }
212
213 #[test]
214 fn test_remove() {
215 let mut cache = SlabLru::new(2);
216
217 cache.put(1, "a");
218 cache.put(2, "b");
219
220 assert_eq!(cache.remove(&1), Some("a"));
221 assert_eq!(cache.get(&1), None);
222 assert_eq!(cache.len(), 1);
223 assert_eq!(cache.remove(&999), None);
224 }
225
226 #[test]
227 fn test_clear_then_reuse() {
228 let mut cache = SlabLru::new(2);
229
230 cache.put(1, "a");
231 cache.put(2, "b");
232 cache.clear();
233
234 assert_eq!(cache.len(), 0);
235 assert!(cache.is_empty());
236 assert_eq!(cache.put(5, "e"), None);
238 assert_eq!(cache.get(&5), Some("e"));
239 }
240
241 #[test]
242 fn test_contains_key_does_not_promote() {
243 let mut cache = SlabLru::new(2);
244
245 cache.put(1, "a");
246 cache.put(2, "b");
247 assert!(cache.contains_key(&1));
250 cache.put(3, "c");
251
252 assert_eq!(cache.get(&1), None);
253 assert_eq!(cache.get(&2), Some("b"));
254 }
255
256 #[test]
257 #[should_panic(expected = "capacity must be greater than 0")]
258 fn test_zero_capacity_panics() {
259 let _cache: SlabLru<i32, i32> = SlabLru::new(0);
260 }
261
262 #[test]
263 fn test_slab_recycles_slots_no_unbounded_growth() {
264 let cap = 8usize;
269 let mut cache = SlabLru::new(cap);
270 for k in 0..1000i32 {
271 cache.put(k, k * 10);
272 assert!(cache.len() <= cap);
273 }
274
275 assert_eq!(cache.len(), cap);
276 assert_eq!(cache.nodes.len(), cap);
278 assert!(cache.free.is_empty());
279
280 for k in (1000 - cap as i32)..1000 {
282 assert_eq!(cache.get(&k), Some(k * 10));
283 }
284 assert_eq!(cache.get(&0), None);
285 }
286}