1use std::collections::HashMap;
21use std::hash::Hash;
22
23use hashlink::LinkedHashMap;
24
25use crate::{Cache, CacheEntry};
26
27pub struct LfuCache<K, V> {
37 cap: usize,
38 min_freq: u64,
39 key_to_entry: HashMap<K, (u64, CacheEntry<V>), ahash::RandomState>,
41 freq_to_keys: HashMap<u64, LinkedHashMap<K, (), ahash::RandomState>, ahash::RandomState>,
43}
44
45impl<K, V> LfuCache<K, V>
46where
47 K: Eq + Hash + Clone,
48{
49 #[must_use]
54 pub fn new(cap: usize) -> Self {
55 LfuCache {
56 cap,
57 min_freq: 0,
58 key_to_entry: HashMap::with_hasher(ahash::RandomState::default()),
59 freq_to_keys: HashMap::with_hasher(ahash::RandomState::default()),
60 }
61 }
62
63 fn increment_freq(&mut self, key: &K) {
69 let (freq, _entry) = match self.key_to_entry.get_mut(key) {
70 Some(pair) => pair,
71 None => return,
72 };
73 let old_freq = *freq;
74 let new_freq = old_freq + 1;
75 *freq = new_freq;
76
77 if let Some(bucket) = self.freq_to_keys.get_mut(&old_freq) {
79 bucket.remove(key);
80 if bucket.is_empty() {
81 self.freq_to_keys.remove(&old_freq);
82 if old_freq == self.min_freq {
84 self.min_freq = new_freq;
85 }
86 }
87 }
88
89 self.freq_to_keys
91 .entry(new_freq)
92 .or_insert_with(|| LinkedHashMap::with_hasher(ahash::RandomState::default()))
93 .insert(key.clone(), ());
94 }
95
96 fn evict(&mut self) -> Option<V> {
98 let bucket = self.freq_to_keys.get_mut(&self.min_freq)?;
99 let (evict_key, ()) = bucket.pop_front()?;
101 if bucket.is_empty() {
102 self.freq_to_keys.remove(&self.min_freq);
103 }
104 let (_freq, entry) = self.key_to_entry.remove(&evict_key)?;
105 Some(entry.value)
106 }
107
108 fn insert_entry(&mut self, key: K, entry: CacheEntry<V>) -> Option<V> {
110 if self.cap == 0 {
111 return Some(entry.value);
112 }
113
114 if self.key_to_entry.contains_key(&key) {
116 let pair = self.key_to_entry.get_mut(&key).expect("confirmed present");
117 pair.1 = entry;
118 self.increment_freq(&key);
119 return None;
120 }
121
122 let evicted = if self.key_to_entry.len() >= self.cap {
124 self.evict()
125 } else {
126 None
127 };
128
129 self.key_to_entry.insert(key.clone(), (1, entry));
131 self.freq_to_keys
132 .entry(1)
133 .or_insert_with(|| LinkedHashMap::with_hasher(ahash::RandomState::default()))
134 .insert(key, ());
135 self.min_freq = 1;
136
137 evicted
138 }
139
140 pub fn get(&mut self, key: &K) -> Option<&V> {
145 let expired = self
147 .key_to_entry
148 .get(key)
149 .map(|(_, e)| e.is_expired())
150 .unwrap_or(false);
151
152 if expired {
153 if let Some((freq, _entry)) = self.key_to_entry.remove(key) {
155 if let Some(bucket) = self.freq_to_keys.get_mut(&freq) {
156 bucket.remove(key);
157 if bucket.is_empty() {
158 self.freq_to_keys.remove(&freq);
159 }
160 }
161 }
162 return None;
163 }
164
165 if !self.key_to_entry.contains_key(key) {
166 return None;
167 }
168
169 self.increment_freq(key);
170 self.key_to_entry.get(key).map(|(_, e)| &e.value)
171 }
172
173 pub fn put(&mut self, key: K, value: V) -> Option<V> {
175 self.insert_entry(key, CacheEntry::new(value))
176 }
177
178 pub fn put_with_ttl(&mut self, key: K, value: V, ttl: std::time::Duration) -> Option<V> {
180 self.insert_entry(key, CacheEntry::with_ttl(value, ttl))
181 }
182
183 #[must_use]
185 pub fn len(&self) -> usize {
186 self.key_to_entry.len()
187 }
188
189 #[must_use]
191 pub fn is_empty(&self) -> bool {
192 self.key_to_entry.is_empty()
193 }
194
195 #[must_use]
197 pub fn cap(&self) -> usize {
198 self.cap
199 }
200
201 #[must_use]
205 pub fn peek(&self, key: &K) -> Option<&V> {
206 self.key_to_entry.get(key).and_then(
207 |(_, e)| {
208 if e.is_expired() {
209 None
210 } else {
211 Some(&e.value)
212 }
213 },
214 )
215 }
216
217 #[must_use]
219 pub fn contains_key(&self, key: &K) -> bool {
220 self.key_to_entry
221 .get(key)
222 .map(|(_, e)| !e.is_expired())
223 .unwrap_or(false)
224 }
225
226 pub fn remove(&mut self, key: &K) -> Option<V> {
228 if let Some((freq, entry)) = self.key_to_entry.remove(key) {
229 if let Some(bucket) = self.freq_to_keys.get_mut(&freq) {
230 bucket.remove(key);
231 if bucket.is_empty() {
232 self.freq_to_keys.remove(&freq);
233 }
234 }
235 Some(entry.value)
236 } else {
237 None
238 }
239 }
240
241 pub fn clear(&mut self) {
243 self.key_to_entry.clear();
244 self.freq_to_keys.clear();
245 self.min_freq = 0;
246 }
247
248 pub fn resize(&mut self, new_cap: usize) {
253 self.cap = new_cap;
254 while self.cap > 0 && self.key_to_entry.len() > self.cap {
255 self.evict();
256 }
257 }
258}
259
260impl<K, V> Cache<K, V> for LfuCache<K, V>
261where
262 K: Eq + Hash + Clone,
263{
264 fn get(&mut self, key: &K) -> Option<&V> {
265 LfuCache::get(self, key)
266 }
267
268 fn put(&mut self, key: K, value: V) -> Option<V> {
269 LfuCache::put(self, key, value)
270 }
271
272 fn put_with_ttl(&mut self, key: K, value: V, ttl: std::time::Duration) -> Option<V> {
273 LfuCache::put_with_ttl(self, key, value, ttl)
274 }
275
276 fn len(&self) -> usize {
277 self.key_to_entry.len()
278 }
279
280 fn cap(&self) -> usize {
281 self.cap
282 }
283
284 fn remove(&mut self, key: &K) -> Option<V> {
285 LfuCache::remove(self, key)
286 }
287
288 fn clear(&mut self) {
289 LfuCache::clear(self);
290 }
291
292 fn peek(&self, key: &K) -> Option<&V> {
293 LfuCache::peek(self, key)
294 }
295
296 fn contains_key(&self, key: &K) -> bool {
297 LfuCache::contains_key(self, key)
298 }
299
300 fn resize(&mut self, new_cap: usize) {
301 LfuCache::resize(self, new_cap);
302 }
303}