subms_block_cache/features/
weighted.rs1use std::collections::HashMap;
17use std::hash::Hash;
18
19const NIL: u32 = u32::MAX;
20
21struct Slot<K, V> {
22 key: K,
23 value: V,
24 size: usize,
25 referenced: bool,
26}
27
28pub struct WeightedCache<K, V> {
29 capacity_bytes: usize,
30 used_bytes: usize,
31 slots: Vec<Option<Slot<K, V>>>,
32 index: HashMap<K, u32>,
33 hand: u32,
34 size_of: Box<dyn Fn(&V) -> usize>,
35 free_slots: Vec<u32>,
39}
40
41impl<K: Hash + Eq + Clone, V> WeightedCache<K, V> {
42 pub fn with_capacity_bytes<F>(capacity_bytes: usize, size_of: F) -> Self
43 where
44 F: Fn(&V) -> usize + 'static,
45 {
46 Self {
47 capacity_bytes: capacity_bytes.max(1),
48 used_bytes: 0,
49 slots: Vec::new(),
50 index: HashMap::new(),
51 hand: 0,
52 size_of: Box::new(size_of),
53 free_slots: Vec::new(),
54 }
55 }
56
57 pub fn capacity_bytes(&self) -> usize {
58 self.capacity_bytes
59 }
60 pub fn used_bytes(&self) -> usize {
61 self.used_bytes
62 }
63 pub fn len(&self) -> usize {
64 self.index.len()
65 }
66 pub fn is_empty(&self) -> bool {
67 self.index.is_empty()
68 }
69
70 pub fn get(&mut self, key: &K) -> Option<&V> {
71 let id = *self.index.get(key)?;
72 let s = self.slots[id as usize].as_mut().unwrap();
73 s.referenced = true;
74 Some(&self.slots[id as usize].as_ref().unwrap().value)
75 }
76
77 pub fn put(&mut self, key: K, value: V) -> Vec<(K, V)> {
81 let new_size = (self.size_of)(&value);
82
83 if new_size > self.capacity_bytes {
85 return vec![(key, value)];
86 }
87
88 if let Some(&id) = self.index.get(&key) {
90 let old_size = self.slots[id as usize].as_ref().unwrap().size;
91 let s = self.slots[id as usize].as_mut().unwrap();
92 s.value = value;
93 s.referenced = true;
94 s.size = new_size;
95 self.used_bytes = self.used_bytes + new_size - old_size;
96 let mut evicted = Vec::new();
98 while self.used_bytes > self.capacity_bytes {
99 if let Some(ev) = self.sweep_evict_excluding(id) {
100 evicted.push(ev);
101 } else {
102 break;
103 }
104 }
105 return evicted;
106 }
107
108 let mut evicted = Vec::new();
110 while self.used_bytes + new_size > self.capacity_bytes {
111 if let Some(ev) = self.sweep_evict_excluding(NIL) {
112 evicted.push(ev);
113 } else {
114 break;
115 }
116 }
117
118 let new_id = if let Some(i) = self.free_slots.pop() {
121 self.slots[i as usize] = Some(Slot {
122 key: key.clone(),
123 value,
124 size: new_size,
125 referenced: true,
126 });
127 i
128 } else {
129 let id = self.slots.len() as u32;
130 self.slots.push(Some(Slot {
131 key: key.clone(),
132 value,
133 size: new_size,
134 referenced: true,
135 }));
136 id
137 };
138 self.index.insert(key, new_id);
139 self.used_bytes += new_size;
140 evicted
141 }
142
143 fn sweep_evict_excluding(&mut self, skip: u32) -> Option<(K, V)> {
148 if self.index.is_empty() {
149 return None;
150 }
151 let n = self.slots.len();
152 if n == 0 {
153 return None;
154 }
155 for _ in 0..(2 * n + 1) {
156 let i = (self.hand as usize) % n;
157 self.hand = ((self.hand as usize + 1) % n.max(1)) as u32;
158 let want_evict = {
159 let s = match self.slots[i].as_mut() {
160 Some(s) => s,
161 None => continue,
162 };
163 if (i as u32) == skip {
164 false
165 } else if s.referenced {
166 s.referenced = false;
167 false
168 } else {
169 true
170 }
171 };
172 if want_evict {
173 let s = self.slots[i].take().unwrap();
174 self.index.remove(&s.key);
175 self.used_bytes -= s.size;
176 self.free_slots.push(i as u32);
177 return Some((s.key, s.value));
178 }
179 }
180 None
181 }
182}
183
184#[cfg(test)]
185#[path = "weighted_tests.rs"]
186mod tests;