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