1use crate::backend::policy::lru::ResourceCounter;
19use crate::backend::policy::{lru::LruCachePolicy, CachePolicy, CachePolicyPutResult};
20use hashbrown::hash_map::DefaultHashBuilder;
21use hashlink::linked_hash_map::{self, IntoIter, Iter, IterMut, LinkedHashMap};
22use std::any::Any;
23use std::fmt;
24use std::fmt::Debug;
25use std::hash::{BuildHasher, Hash};
26
27pub struct LruCache<K, V, H = DefaultHashBuilder>
28where
29 K: Clone + Eq + Hash + Ord + Debug + Send + 'static,
30 V: Clone + Debug + Send + 'static,
31{
32 map: LinkedHashMap<K, V, H>,
33 resource_counter: Box<dyn ResourceCounter<K = K, V = V>>,
34}
35
36impl<K, V> LruCache<K, V>
37where
38 K: Clone + Eq + Hash + Ord + Debug + Send + 'static,
39 V: Clone + Debug + Send + 'static,
40{
41 pub fn with_resource_counter<R>(resource_counter: R) -> Self
42 where
43 R: ResourceCounter<K = K, V = V>,
44 {
45 LruCache {
46 map: LinkedHashMap::new(),
47 resource_counter: Box::new(resource_counter),
48 }
49 }
50}
51
52impl<K, V, H> LruCache<K, V, H>
53where
54 K: Clone + Eq + Hash + Ord + Debug + Send + 'static,
55 V: Clone + Debug + Send + 'static,
56{
57 pub fn with_resource_counter_and_hasher<R>(
58 resource_counter: R,
59 hash_builder: H,
60 ) -> Self
61 where
62 R: ResourceCounter<K = K, V = V>,
63 {
64 LruCache {
65 map: LinkedHashMap::with_hasher(hash_builder),
66 resource_counter: Box::new(resource_counter),
67 }
68 }
69
70 pub fn len(&self) -> usize {
71 self.map.len()
72 }
73
74 pub fn is_empty(&self) -> bool {
75 self.map.is_empty()
76 }
77
78 pub fn iter(&self) -> Iter<K, V> {
79 self.map.iter()
80 }
81
82 pub fn iter_mut(&mut self) -> IterMut<K, V> {
83 self.map.iter_mut()
84 }
85}
86
87impl<K, V, H> LruCachePolicy for LruCache<K, V, H>
88where
89 K: 'static + Clone + Debug + Eq + Hash + Ord + Send,
90 H: 'static + BuildHasher + Debug + Send,
91 V: 'static + Clone + Debug + Send,
92{
93 fn get_lru(&mut self, k: &Self::K) -> Option<Self::V> {
94 match self.map.raw_entry_mut().from_key(k) {
95 linked_hash_map::RawEntryMut::Occupied(mut occupied) => {
96 occupied.to_back();
97 Some(occupied.into_mut().clone())
98 }
99 linked_hash_map::RawEntryMut::Vacant(_) => None,
100 }
101 }
102
103 fn put_lru(
104 &mut self,
105 k: Self::K,
106 v: Self::V,
107 ) -> CachePolicyPutResult<Self::K, Self::V> {
108 let old_val = self.map.insert(k.clone(), v.clone());
109 self.resource_counter.consume(&k, &v);
111 if let Some(old_val) = &old_val {
113 self.resource_counter.restore(&k, old_val);
114 }
115
116 let mut popped_entries = vec![];
117 while self.resource_counter.exceed_capacity() {
118 if let Some(entry) = self.pop_lru() {
119 popped_entries.push(entry);
120 }
121 }
122 (old_val, popped_entries)
123 }
124
125 fn pop_lru(&mut self) -> Option<(Self::K, Self::V)> {
126 if let Some(entry) = self.map.pop_front() {
127 self.resource_counter.restore(&entry.0, &entry.1);
128 Some(entry)
129 } else {
130 None
131 }
132 }
133}
134
135impl<K, V, H> CachePolicy for LruCache<K, V, H>
136where
137 K: 'static + Clone + Debug + Eq + Hash + Ord + Send,
138 H: 'static + BuildHasher + Debug + Send,
139 V: 'static + Clone + Debug + Send,
140{
141 type K = K;
142 type V = V;
143
144 fn get(&mut self, k: &Self::K) -> Option<Self::V> {
145 self.get_lru(k)
146 }
147
148 fn peek(&mut self, k: &Self::K) -> Option<Self::V> {
149 self.map.get(k).cloned()
150 }
151
152 fn put(&mut self, k: Self::K, v: Self::V) -> CachePolicyPutResult<Self::K, Self::V> {
153 self.put_lru(k, v)
154 }
155
156 fn remove(&mut self, k: &Self::K) -> Option<Self::V> {
157 if let Some(v) = self.map.remove(k) {
158 self.resource_counter.restore(k, &v);
159 Some(v)
160 } else {
161 None
162 }
163 }
164
165 fn pop(&mut self) -> Option<(Self::K, Self::V)> {
166 self.pop_lru()
167 }
168
169 fn as_any(&self) -> &dyn Any {
170 self
171 }
172}
173
174impl<K, V, H> IntoIterator for LruCache<K, V, H>
175where
176 K: 'static + Clone + Debug + Eq + Hash + Ord + Send,
177 V: 'static + Clone + Debug + Send,
178{
179 type Item = (K, V);
180 type IntoIter = IntoIter<K, V>;
181
182 fn into_iter(self) -> IntoIter<K, V> {
183 self.map.into_iter()
184 }
185}
186
187impl<'a, K, V, H> IntoIterator for &'a LruCache<K, V, H>
188where
189 K: 'static + Clone + Debug + Eq + Hash + Ord + Send,
190 V: 'static + Clone + Debug + Send,
191{
192 type Item = (&'a K, &'a V);
193 type IntoIter = Iter<'a, K, V>;
194
195 fn into_iter(self) -> Iter<'a, K, V> {
196 self.iter()
197 }
198}
199
200impl<'a, K, V, H> IntoIterator for &'a mut LruCache<K, V, H>
201where
202 K: 'static + Clone + Debug + Eq + Hash + Ord + Send,
203 V: 'static + Clone + Debug + Send,
204{
205 type Item = (&'a K, &'a mut V);
206 type IntoIter = IterMut<'a, K, V>;
207
208 fn into_iter(self) -> IterMut<'a, K, V> {
209 self.iter_mut()
210 }
211}
212
213impl<K, V, H> Debug for LruCache<K, V, H>
214where
215 K: 'static + Clone + Debug + Eq + Hash + Ord + Send,
216 V: 'static + Clone + Debug + Send,
217{
218 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
219 f.debug_map().entries(self.iter().rev()).finish()
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use crate::backend::policy::lru::lru_cache::LruCache;
226 use crate::backend::policy::lru::{DefaultResourceCounter, ResourceCounter};
227 use crate::backend::policy::CachePolicy;
228 use hashbrown::HashMap;
229
230 #[test]
231 fn test_cache_with_lru_policy() {
232 let mut cache = LruCache::with_resource_counter(DefaultResourceCounter::new(3));
233
234 cache.put("1".to_string(), "file1".to_string());
235 cache.put("2".to_string(), "file2".to_string());
236 cache.put("3".to_string(), "file3".to_string());
237 assert_eq!(3, cache.len());
238
239 cache.put("4".to_string(), "file4".to_string());
240 assert_eq!(3, cache.len());
241 assert!(cache.peek(&"1".to_string()).is_none());
242
243 assert!(cache.peek(&"2".to_string()).is_some());
244 let mut iter = cache.iter();
245 assert_eq!("2", iter.next().unwrap().0);
246 assert_eq!("3", iter.next().unwrap().0);
247 assert_eq!("4", iter.next().unwrap().0);
248
249 assert!(cache.get(&"2".to_string()).is_some());
250 let mut iter = cache.iter();
251 assert_eq!("3", iter.next().unwrap().0);
252 assert_eq!("4", iter.next().unwrap().0);
253 assert_eq!("2", iter.next().unwrap().0);
254
255 assert_eq!(Some("file4".to_string()), cache.remove(&"4".to_string()));
256
257 assert_eq!("3".to_string(), cache.pop().unwrap().0);
258 assert_eq!("2".to_string(), cache.pop().unwrap().0);
259 assert!(cache.pop().is_none());
260 }
261
262 #[test]
263 fn test_cache_with_size_resource_counter() {
264 let mut cache =
265 LruCache::with_resource_counter(get_test_size_resource_counter(50));
266
267 cache.put("1".to_string(), "file1".to_string());
268 cache.put("2".to_string(), "file2".to_string());
269 cache.put("3".to_string(), "file3".to_string());
270 assert_eq!(3, cache.len());
271
272 cache.put("4".to_string(), "file4".to_string());
273 assert_eq!(2, cache.len());
274 assert!(cache.peek(&"1".to_string()).is_none());
275 assert!(cache.peek(&"2".to_string()).is_none());
276
277 assert!(cache.peek(&"3".to_string()).is_some());
278 let mut iter = cache.iter();
279 assert_eq!("3", iter.next().unwrap().0);
280 assert_eq!("4", iter.next().unwrap().0);
281
282 assert!(cache.get(&"3".to_string()).is_some());
283 let mut iter = cache.iter();
284 assert_eq!("4", iter.next().unwrap().0);
285 assert_eq!("3", iter.next().unwrap().0);
286
287 cache.put("5".to_string(), "file5".to_string());
288 cache.put("3".to_string(), "file3-bak".to_string());
289 cache.put("1".to_string(), "file1".to_string());
290 let mut iter = cache.iter();
291 assert_eq!("3", iter.next().unwrap().0);
292 assert_eq!("1", iter.next().unwrap().0);
293 assert!(iter.next().is_none());
294 }
295
296 fn get_test_size_resource_counter(max_size: usize) -> TestSizeResourceCounter {
297 let mut size_map = HashMap::new();
298 size_map.insert(("1".to_string(), "file1".to_string()), 10);
299 size_map.insert(("2".to_string(), "file2".to_string()), 20);
300 size_map.insert(("3".to_string(), "file3".to_string()), 15);
301 size_map.insert(("3".to_string(), "file3-bak".to_string()), 30);
302 size_map.insert(("4".to_string(), "file4".to_string()), 35);
303 size_map.insert(("5".to_string(), "file5".to_string()), 25);
304
305 TestSizeResourceCounter {
306 size_map,
307 max_size,
308 current_size: 0,
309 }
310 }
311
312 #[derive(Debug)]
313 struct TestSizeResourceCounter {
314 size_map: HashMap<(String, String), usize>,
315 max_size: usize,
316 current_size: usize,
317 }
318
319 impl ResourceCounter for TestSizeResourceCounter {
320 type K = String;
321 type V = String;
322
323 fn consume(&mut self, k: &Self::K, v: &Self::V) {
324 let s = self.size_map.get(&(k.clone(), v.clone())).unwrap();
325 self.current_size += s;
326 }
327
328 fn restore(&mut self, k: &Self::K, v: &Self::V) {
329 let s = self.size_map.get(&(k.clone(), v.clone())).unwrap();
330 self.current_size -= s;
331 }
332
333 fn exceed_capacity(&self) -> bool {
334 self.current_size > self.max_size
335 }
336 }
337}