1use crate::{
52 cache::{Cache, Eviction},
53 collections::list::{Link, LinkedList, LinkedListArenaEntry, ListError},
54 map::Map,
55 vector::Vector,
56};
57use core::{
58 fmt::{Debug, Display},
59 mem,
60};
61
62use super::Lookup;
63
64extern crate alloc;
65
66#[derive(Clone, Copy)]
68pub struct Block<K, T> {
69 pub key: K,
70 pub value: T,
71}
72
73pub type LRUCacheBlockArenaEntry<K, T> = LinkedListArenaEntry<Block<K, T>>;
75
76pub struct LRUCache<V, K, T, M> {
97 block_list: LinkedList<V, Block<K, T>>,
98 block_refs: M,
99
100 capacity: usize,
101}
102
103impl<V, K, T, M> LRUCache<V, K, T, M>
104where
105 V: Vector<LRUCacheBlockArenaEntry<K, T>>,
106 M: Map<K, Link>,
107{
108 pub fn least_recent(&self) -> Option<(&K, &T)> {
110 let block = self.block_list.peek_front()?;
111 Some((&block.key, &block.value))
112 }
113
114 pub fn most_recent(&self) -> Option<(&K, &T)> {
116 let block = self.block_list.peek_back()?;
117 Some((&block.key, &block.value))
118 }
119}
120
121impl<V, K, T, M> LRUCache<V, K, T, M>
122where
123 V: Vector<LRUCacheBlockArenaEntry<K, T>>,
124 M: Map<K, Link>,
125{
126 pub fn with_backing_vector_and_map(vector: V, map: M) -> Self {
129 let block_list = LinkedList::with_backing_vector(vector);
130 let capacity = block_list.capacity();
131
132 Self {
133 block_list,
134 block_refs: map,
135 capacity,
136 }
137 }
138}
139
140impl<V, K, T, M> LRUCache<V, K, T, M>
141where
142 V: Vector<LRUCacheBlockArenaEntry<K, T>>,
143 M: Map<K, Link> + Default,
144{
145 pub fn with_backing_vector(vector: V) -> Self {
148 Self::with_backing_vector_and_map(vector, M::default())
149 }
150}
151
152impl<V, K, T, M> Default for LRUCache<V, K, T, M>
153where
154 V: Vector<LRUCacheBlockArenaEntry<K, T>> + Default,
155 M: Map<K, Link> + Default,
156{
157 fn default() -> Self {
158 Self::with_backing_vector(V::default())
159 }
160}
161
162#[derive(Debug)]
164pub enum LRUCacheError<VE, ME> {
165 ListError(ListError<VE>),
167
168 ListUnderflow,
170
171 MapListInconsistent,
174
175 MapError(ME),
177}
178
179impl<VE, ME> Display for LRUCacheError<VE, ME>
180where
181 VE: Debug,
182 ME: Debug,
183{
184 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
185 write!(f, "{self:?}")
186 }
187}
188
189#[allow(unused)]
190impl<V, K, T, M> Cache<K, T> for LRUCache<V, K, T, M>
191where
192 V: Vector<LRUCacheBlockArenaEntry<K, T>>,
193 M: Map<K, Link>,
194 K: Copy,
195{
196 type Error = LRUCacheError<V::Error, M::Error>;
197
198 fn insert(&mut self, key: K, value: T) -> Result<Eviction<K, T>, Self::Error> {
199 if let Some(link) = self.block_refs.get(&key) {
200 self.block_list
201 .shift_push_back(link)
202 .ok_or(Self::Error::MapListInconsistent)?;
203
204 let block = self
205 .block_list
206 .get_mut(link)
207 .ok_or(Self::Error::MapListInconsistent)?;
208
209 return Ok(Eviction::Value(mem::replace(&mut block.value, value)));
210 }
211
212 let eviction = if self.is_maxed() {
213 let Block { key, value } = self
214 .block_list
215 .pop_front()
216 .ok_or(Self::Error::ListUnderflow)?;
217
218 self.block_refs.remove(&key);
219
220 Eviction::Block { key, value }
221 } else {
222 Eviction::None
223 };
224
225 let link = self
226 .block_list
227 .push_back(Block { key, value })
228 .map_err(Self::Error::ListError)?;
229
230 self.block_refs
231 .insert(key, link)
232 .map_err(Self::Error::MapError)?;
233
234 Ok(eviction)
235 }
236
237 fn remove(&mut self, key: &K) -> Result<Lookup<T>, Self::Error> {
238 match self.block_refs.remove(key) {
239 Some(link) => self
240 .block_list
241 .remove(&link)
242 .map(|x| Lookup::Hit(x.value))
243 .ok_or(Self::Error::MapListInconsistent),
244 _ => Ok(Lookup::Miss),
245 }
246 }
247
248 fn shrink(&mut self, new_capacity: usize) -> Result<(), Self::Error> {
249 if new_capacity >= self.capacity() {
250 return Ok(());
251 }
252
253 while self.len() > new_capacity {
254 let Block { key, value } = self
255 .block_list
256 .pop_front()
257 .ok_or(Self::Error::ListUnderflow)?;
258
259 self.block_refs.remove(&key);
260 }
261
262 self.capacity = new_capacity;
263
264 Ok(())
265 }
266
267 fn reserve(&mut self, additional: usize) -> Result<(), Self::Error> {
268 self.block_list
269 .reserve(additional)
270 .map_err(Self::Error::ListError)?;
271
272 self.capacity += additional;
273
274 Ok(())
275 }
276
277 fn query(&mut self, key: &K) -> Result<Lookup<&T>, Self::Error> {
278 match self.block_refs.get(key) {
279 Some(link) => {
280 self.block_list
281 .shift_push_back(link)
282 .ok_or(Self::Error::MapListInconsistent)?;
283
284 self.block_list
285 .get(link)
286 .map(|x| Lookup::Hit(&x.value))
287 .ok_or(Self::Error::MapListInconsistent)
288 }
289 _ => Ok(Lookup::Miss),
290 }
291 }
292
293 fn capacity(&self) -> usize {
294 self.capacity
295 }
296
297 fn len(&self) -> usize {
298 self.block_list.len()
299 }
300
301 fn is_empty(&self) -> bool {
302 self.block_list.is_empty()
303 }
304
305 fn clear(&mut self) -> Result<(), Self::Error> {
306 self.block_list.clear().map_err(Self::Error::ListError)?;
307 self.block_refs.clear().map_err(Self::Error::MapError)?;
308
309 Ok(())
310 }
311}
312
313#[doc(hidden)]
314pub mod tests {
315
316 use super::{
317 Cache, Eviction, LRUCache, LRUCacheBlockArenaEntry, LRUCacheError, Link, Lookup, Map,
318 Vector,
319 };
320
321 pub fn _test_cache_correctness<VX, VY, M>(zero_capacity_vec: VX, test_vec: VY)
322 where
323 VX: Vector<LRUCacheBlockArenaEntry<usize, usize>>,
324 VY: Vector<LRUCacheBlockArenaEntry<usize, usize>>,
325 M: Map<usize, Link> + Default,
326 {
327 assert_eq!(
328 zero_capacity_vec.capacity(),
329 0,
330 "Zero capacity vector provider yielded vector of non zero capacity."
331 );
332
333 let mut cache = LRUCache::<_, _, _, M>::with_backing_vector(zero_capacity_vec);
334
335 assert!(cache.is_empty());
336
337 match cache.insert(0, 0) {
338 Err(LRUCacheError::ListUnderflow) => {}
339 _ => unreachable!("Wrong error on list underflow."),
340 };
341
342 let mut cache = LRUCache::<_, _, _, M>::with_backing_vector(test_vec);
343
344 let capacity = cache.capacity();
345
346 assert!(
347 capacity > 3,
348 "Too small capacity: {} to run meaningful tests.",
349 capacity
350 );
351
352 assert!(cache.is_empty());
353
354 for i in 0..cache.capacity() {
355 assert_eq!(cache.insert(i, i).unwrap(), Eviction::None);
356 }
357
358 assert_eq!(cache.least_recent().unwrap(), (&0, &0));
359
360 assert_eq!(
361 cache.insert(capacity, capacity).unwrap(),
362 Eviction::Block { key: 0, value: 0 }
363 );
364
365 assert_eq!(cache.query(&1).unwrap(), Lookup::Hit(&1));
366
367 assert_eq!(cache.least_recent().unwrap(), (&2, &2));
368 assert_eq!(cache.most_recent().unwrap(), (&1, &1));
369
370 assert_eq!(cache.remove(&(capacity + 1)).unwrap(), Lookup::Miss);
371 assert_eq!(cache.query(&(capacity + 1)).unwrap(), Lookup::Miss);
372
373 assert_eq!(
374 cache.insert(capacity + 1, capacity + 1).unwrap(),
375 Eviction::Block { key: 2, value: 2 }
376 );
377
378 assert_eq!(
379 cache.remove(&(capacity + 1)).unwrap(),
380 Lookup::Hit(capacity + 1)
381 );
382
383 assert_eq!(cache.remove(&(capacity + 1)).unwrap(), Lookup::Miss);
384 assert_eq!(cache.query(&(capacity + 1)).unwrap(), Lookup::Miss);
385
386 assert_eq!(
387 cache.insert(capacity, capacity + 2).unwrap(),
388 Eviction::Value(capacity)
389 );
390
391 assert_eq!(cache.most_recent().unwrap(), (&capacity, &(capacity + 2)));
392
393 cache.clear().unwrap();
394
395 assert!(cache.is_empty());
396
397 for i in 0..cache.capacity() {
398 assert_eq!(cache.insert(i, i).unwrap(), Eviction::None);
399 }
400
401 assert_eq!(cache.least_recent().unwrap(), (&0, &0));
402
403 const ADDITIONAL: usize = 5;
404
405 let result = cache.reserve(ADDITIONAL);
406
407 if result.is_ok() {
408 let old_len = cache.len();
409 for i in 0..ADDITIONAL {
410 assert_eq!(cache.insert(i + old_len, i).unwrap(), Eviction::None);
411 }
412 }
413
414 let old_capacity = cache.capacity();
415
416 cache.shrink(0).unwrap();
417
418 assert!(cache.is_maxed());
419
420 match cache.insert(0, 0) {
421 Err(LRUCacheError::ListUnderflow) => {}
422 _ => unreachable!("Wrong error on list underflow."),
423 };
424
425 assert!(cache.is_empty());
426
427 cache.reserve(old_capacity).unwrap();
428 cache.shrink(old_capacity).unwrap();
429
430 assert_eq!(cache.capacity(), old_capacity);
431
432 for i in 0..cache.capacity() {
433 assert_eq!(cache.insert(i, i).unwrap(), Eviction::None);
434 }
435
436 cache.clear().unwrap();
437
438 assert!(cache.is_empty());
439 }
440}