keos_project5/lru.rs
1//! A Least Recently Used (LRU) Cache.
2//!
3//! `LRUCache<K, V, MAX_SIZE>` stores up to `MAX_SIZE` key-value pairs,
4//! automatically evicting the least recently used entry when the capacity
5//! is exceeded. This makes it useful for caching expensive computations,
6//! I/O results, or any data where temporal locality is expected.
7//!
8//! # Example
9//! ```
10//! let mut cache: LRUCache<i32, String, 2> = LRUCache::new();
11//!
12//! cache.put(1, "one".to_string());
13//! cache.put(2, "two".to_string());
14//!
15//! // Access key 1, making it most recently used
16//! assert_eq!(cache.get(1).map(|v| v.as_str()), Some("one"));
17//!
18//! // Insert new key, evicting key 2 (least recently used)
19//! cache.put(3, "three".to_string());
20//!
21//! assert!(cache.get(2).is_none()); // evicted
22//! assert!(cache.get(1).is_some());
23//! assert!(cache.get(3).is_some());
24//! ```
25use alloc::{collections::BTreeMap, vec::Vec};
26
27struct Node<K: Clone, V> {
28 v: V,
29 prev: Option<K>,
30 next: Option<K>,
31}
32
33/// An Least Recently Used Cache with capacity `MAX_SIZE`.
34pub struct LRUCache<K: Ord + Clone, V, const MAX_SIZE: usize> {
35 inner: BTreeMap<K, Node<K, V>>,
36
37 // Access information
38 head: Option<K>,
39 tail: Option<K>,
40}
41
42impl<K: Ord + Clone, V, const MAX_SIZE: usize> Default for LRUCache<K, V, MAX_SIZE> {
43 fn default() -> Self {
44 Self::new()
45 }
46}
47
48impl<K: Ord + Clone, V, const MAX_SIZE: usize> LRUCache<K, V, MAX_SIZE> {
49 // Attach the access information about key.
50 fn attach(&mut self, k: K) -> &mut Node<K, V> {
51 if let Some(tail) = self.tail.take() {
52 let last = self.inner.get_mut(&tail).unwrap();
53 last.next = Some(k.clone());
54 } else {
55 self.head = Some(k.clone());
56 }
57 let ptail = self.tail.clone();
58 self.tail = Some(k.clone());
59
60 let node = self.inner.get_mut(&k).unwrap();
61 node.prev = ptail;
62 node
63 }
64
65 // Detach the access information about key.
66 fn detach(&mut self, prev: Option<K>, next: Option<K>) {
67 if let Some(next) = next.as_ref() {
68 self.inner.get_mut(next).unwrap().prev = prev.clone();
69 } else {
70 self.tail = prev.clone();
71 }
72
73 if let Some(prev) = prev {
74 self.inner.get_mut(&prev).unwrap().next = next;
75 } else {
76 self.head = next;
77 }
78 }
79
80 /// Makes a new, empty `LRUCache`.
81 ///
82 /// Does not allocate anything on its own.
83 pub const fn new() -> Self {
84 Self {
85 inner: BTreeMap::new(),
86 head: None,
87 tail: None,
88 }
89 }
90
91 /// Returns a mutable reference to the value corresponding to the key and
92 /// update the last access time.
93 pub fn get(&mut self, k: K) -> Option<&mut V> {
94 let node = self.inner.get_mut(&k)?;
95 let (prev, next) = (node.prev.take(), node.next.take());
96 self.detach(prev, next);
97 Some(&mut self.attach(k).v)
98 }
99
100 /// Inserts the value computed with `f` into the `LRUCache` if it is not
101 /// present, then returns a reference to the value in the `LRUCache`.
102 pub fn get_or_insert_with<E>(
103 &mut self,
104 k: K,
105 f: impl FnOnce() -> Result<V, E>,
106 ) -> Result<&mut V, E> {
107 Ok(if let Some(node) = self.inner.get_mut(&k) {
108 let (prev, next) = (node.prev.take(), node.next.take());
109 self.detach(prev, next);
110 &mut self.attach(k).v
111 } else {
112 &mut self.__put(k, f()?).v
113 })
114 }
115
116 fn __put(&mut self, k: K, v: V) -> &mut Node<K, V> {
117 if let Some(node) = self.inner.get_mut(&k) {
118 node.v = v;
119 let (prev, next) = (node.prev.take(), node.next.take());
120 self.detach(prev, next);
121 } else {
122 if MAX_SIZE <= self.inner.len() {
123 self.remove(&self.head.clone().unwrap());
124 }
125 let node = Node {
126 v,
127 prev: self.tail.clone(),
128 next: None,
129 };
130 self.inner.insert(k.clone(), node);
131 }
132 self.attach(k)
133 }
134
135 /// Inserts a key-value pair into the `LRUCache`.
136 ///
137 /// If the map did have this key present, the value is updated.
138 /// The key is not updated, though; this matters for types that can be ==
139 /// without being identical.
140 ///
141 /// If the cache size is overflowed after insertion, evict the oldest
142 /// accessed entry.
143 pub fn put(&mut self, k: K, v: V) {
144 self.__put(k, v);
145 }
146
147 /// Removes a key from the LRUCache, returning the stored value if the
148 /// key was previously in the LRUCache.
149 ///
150 /// The key may be any borrowed form of the LRUCache’s key type, but the
151 /// ordering on the borrowed form must match the ordering on the key
152 /// type.
153 pub fn remove(&mut self, k: &K) -> Option<V> {
154 let mut node = self.inner.remove(k)?;
155 self.detach(node.prev.take(), node.next.take());
156 Some(node.v)
157 }
158
159 /// Retains only the elements specified by the predicate.
160 /// In other words, remove all pairs (k, v) for which f(&k, &mut v) returns
161 /// false.
162 pub fn retain(&mut self, mut f: impl FnMut(&K, &mut V) -> bool) {
163 let retain_targets = self
164 .inner
165 .iter_mut()
166 .filter_map(|(k, v)| {
167 if !f(k, &mut v.v) {
168 Some(k.clone())
169 } else {
170 None
171 }
172 })
173 .collect::<Vec<_>>();
174 for target in retain_targets.into_iter() {
175 self.remove(&target);
176 }
177 }
178
179 /// Iterates over the key-value pairs in the LRUCache.
180 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
181 self.inner.iter_mut().map(|(k, v)| (k, &mut v.v))
182 }
183}