1use core::hash::Hash;
4use std::collections::HashMap;
5use std::sync::Mutex;
6
7use crate::cache::Cache;
8use crate::error::CacheError;
9use crate::util::MutexExt;
10
11pub struct SizedCache<K, V> {
58 max_weight: usize,
59 weigher: fn(&V) -> usize,
60 inner: Mutex<Inner<K, V>>,
61}
62
63struct Node<K, V> {
64 key: K,
65 value: V,
66 weight: usize,
67 prev: Option<usize>,
68 next: Option<usize>,
69}
70
71struct Inner<K, V> {
72 nodes: Vec<Option<Node<K, V>>>,
73 free: Vec<usize>,
74 head: Option<usize>,
75 tail: Option<usize>,
76 map: HashMap<K, usize>,
77 total_weight: usize,
78}
79
80impl<K, V> Inner<K, V>
81where
82 K: Eq + Hash + Clone,
83{
84 fn new() -> Self {
85 Self {
86 nodes: Vec::new(),
87 free: Vec::new(),
88 head: None,
89 tail: None,
90 map: HashMap::new(),
91 total_weight: 0,
92 }
93 }
94
95 fn alloc(&mut self, node: Node<K, V>) -> usize {
96 if let Some(idx) = self.free.pop() {
97 self.nodes[idx] = Some(node);
98 idx
99 } else {
100 self.nodes.push(Some(node));
101 self.nodes.len() - 1
102 }
103 }
104
105 fn dealloc(&mut self, idx: usize) -> Node<K, V> {
106 let node = self.nodes[idx]
107 .take()
108 .unwrap_or_else(|| unreachable!("arena slot must be occupied"));
109 self.free.push(idx);
110 node
111 }
112
113 fn unlink(&mut self, idx: usize) {
114 let (prev, next) = {
115 let n = self.nodes[idx]
116 .as_ref()
117 .unwrap_or_else(|| unreachable!("unlink target must be occupied"));
118 (n.prev, n.next)
119 };
120 match prev {
121 Some(p) => {
122 self.nodes[p]
123 .as_mut()
124 .unwrap_or_else(|| unreachable!())
125 .next = next
126 }
127 None => self.head = next,
128 }
129 match next {
130 Some(n) => {
131 self.nodes[n]
132 .as_mut()
133 .unwrap_or_else(|| unreachable!())
134 .prev = prev
135 }
136 None => self.tail = prev,
137 }
138 if let Some(n) = self.nodes[idx].as_mut() {
139 n.prev = None;
140 n.next = None;
141 }
142 }
143
144 fn push_front(&mut self, idx: usize) {
145 let old_head = self.head;
146 if let Some(n) = self.nodes[idx].as_mut() {
147 n.prev = None;
148 n.next = old_head;
149 }
150 if let Some(h) = old_head {
151 if let Some(n) = self.nodes[h].as_mut() {
152 n.prev = Some(idx);
153 }
154 } else {
155 self.tail = Some(idx);
156 }
157 self.head = Some(idx);
158 }
159
160 fn promote(&mut self, idx: usize) {
161 if self.head == Some(idx) {
162 return;
163 }
164 self.unlink(idx);
165 self.push_front(idx);
166 }
167
168 fn evict_tail(&mut self) -> Option<(K, usize)> {
170 let tail_idx = self.tail?;
171 self.unlink(tail_idx);
172 let node = self.dealloc(tail_idx);
173 self.total_weight = self.total_weight.saturating_sub(node.weight);
174 Some((node.key, node.weight))
175 }
176}
177
178impl<K, V> SizedCache<K, V>
179where
180 K: Eq + Hash + Clone,
181 V: Clone,
182{
183 pub fn new(max_weight: usize, weigher: fn(&V) -> usize) -> Result<Self, CacheError> {
198 if max_weight == 0 {
199 return Err(CacheError::InvalidCapacity);
200 }
201 Ok(Self {
202 max_weight,
203 weigher,
204 inner: Mutex::new(Inner::new()),
205 })
206 }
207
208 pub fn max_weight(&self) -> usize {
210 self.max_weight
211 }
212
213 pub fn total_weight(&self) -> usize {
215 self.inner.lock_recover().total_weight
216 }
217}
218
219impl<K, V> Cache<K, V> for SizedCache<K, V>
220where
221 K: Eq + Hash + Clone,
222 V: Clone,
223{
224 fn get(&self, key: &K) -> Option<V> {
225 let mut inner = self.inner.lock_recover();
226 let idx = *inner.map.get(key)?;
227 inner.promote(idx);
228 inner.nodes[idx].as_ref().map(|n| n.value.clone())
229 }
230
231 fn insert(&self, key: K, value: V) -> Option<V> {
232 let new_weight = (self.weigher)(&value);
233 if new_weight > self.max_weight {
235 return None;
236 }
237
238 let mut inner = self.inner.lock_recover();
239
240 if let Some(&idx) = inner.map.get(&key) {
242 let (old_value, old_weight) = inner.nodes[idx]
243 .as_mut()
244 .map(|n| {
245 let ov = core::mem::replace(&mut n.value, value);
246 let ow = n.weight;
247 n.weight = new_weight;
248 (ov, ow)
249 })
250 .unwrap_or_else(|| unreachable!("mapped index must be occupied"));
251 inner.total_weight = inner
252 .total_weight
253 .saturating_add(new_weight)
254 .saturating_sub(old_weight);
255 inner.promote(idx);
256 while inner.total_weight > self.max_weight {
258 if inner.evict_tail().is_none() {
259 break;
260 }
261 }
262 return Some(old_value);
266 }
267
268 while inner.total_weight.saturating_add(new_weight) > self.max_weight {
270 match inner.evict_tail() {
271 Some((evicted_key, _)) => {
272 let _ = inner.map.remove(&evicted_key);
273 }
274 None => break,
275 }
276 }
277
278 let idx = inner.alloc(Node {
279 key: key.clone(),
280 value,
281 weight: new_weight,
282 prev: None,
283 next: None,
284 });
285 inner.push_front(idx);
286 let _ = inner.map.insert(key, idx);
287 inner.total_weight = inner.total_weight.saturating_add(new_weight);
288 None
289 }
290
291 fn remove(&self, key: &K) -> Option<V> {
292 let mut inner = self.inner.lock_recover();
293 let idx = inner.map.remove(key)?;
294 inner.unlink(idx);
295 let node = inner.dealloc(idx);
296 inner.total_weight = inner.total_weight.saturating_sub(node.weight);
297 Some(node.value)
298 }
299
300 fn contains_key(&self, key: &K) -> bool {
301 self.inner.lock_recover().map.contains_key(key)
302 }
303
304 fn len(&self) -> usize {
305 self.inner.lock_recover().map.len()
306 }
307
308 fn clear(&self) {
309 let mut inner = self.inner.lock_recover();
310 inner.nodes.clear();
311 inner.free.clear();
312 inner.head = None;
313 inner.tail = None;
314 inner.map.clear();
315 inner.total_weight = 0;
316 }
317
318 fn capacity(&self) -> usize {
321 self.max_weight
322 }
323}