rustls/
limited_cache.rs
1use std::borrow::Borrow;
2use std::collections::hash_map::Entry;
3use std::collections::{HashMap, VecDeque};
4use std::hash::Hash;
5
6pub(crate) struct LimitedCache<K: Clone + Hash + Eq, V> {
15 map: HashMap<K, V>,
16
17 oldest: VecDeque<K>,
19}
20
21impl<K, V> LimitedCache<K, V>
22where
23 K: Eq + Hash + Clone + std::fmt::Debug,
24 V: Default,
25{
26 pub(crate) fn new(capacity_order_of_magnitude: usize) -> Self {
28 Self {
29 map: HashMap::with_capacity(capacity_order_of_magnitude),
30 oldest: VecDeque::with_capacity(capacity_order_of_magnitude),
31 }
32 }
33
34 pub(crate) fn get_or_insert_default_and_edit(&mut self, k: K, edit: impl FnOnce(&mut V)) {
35 let inserted_new_item = match self.map.entry(k) {
36 Entry::Occupied(value) => {
37 edit(value.into_mut());
38 false
39 }
40 entry @ Entry::Vacant(_) => {
41 self.oldest
42 .push_back(entry.key().clone());
43 edit(entry.or_insert_with(V::default));
44 true
45 }
46 };
47
48 if inserted_new_item && self.oldest.capacity() == self.oldest.len() {
50 if let Some(oldest_key) = self.oldest.pop_front() {
51 self.map.remove(&oldest_key);
52 }
53 }
54 }
55
56 pub(crate) fn insert(&mut self, k: K, v: V) {
57 let inserted_new_item = match self.map.entry(k) {
58 Entry::Occupied(mut old) => {
59 old.insert(v);
61 false
62 }
63
64 entry @ Entry::Vacant(_) => {
65 self.oldest
66 .push_back(entry.key().clone());
67 entry.or_insert(v);
68 true
69 }
70 };
71
72 if inserted_new_item && self.oldest.capacity() == self.oldest.len() {
74 if let Some(oldest_key) = self.oldest.pop_front() {
75 self.map.remove(&oldest_key);
76 }
77 }
78 }
79
80 pub(crate) fn get<Q: Hash + Eq + ?Sized>(&self, k: &Q) -> Option<&V>
81 where
82 K: Borrow<Q>,
83 {
84 self.map.get(k)
85 }
86
87 pub(crate) fn get_mut<Q: Hash + Eq + ?Sized>(&mut self, k: &Q) -> Option<&mut V>
88 where
89 K: Borrow<Q>,
90 {
91 self.map.get_mut(k)
92 }
93
94 pub(crate) fn remove<Q: Hash + Eq + ?Sized>(&mut self, k: &Q) -> Option<V>
95 where
96 K: Borrow<Q>,
97 {
98 if let Some(value) = self.map.remove(k) {
99 if let Some(index) = self
101 .oldest
102 .iter()
103 .position(|item| item.borrow() == k)
104 {
105 self.oldest.remove(index);
106 }
107 Some(value)
108 } else {
109 None
110 }
111 }
112}
113
114#[cfg(test)]
115mod test {
116 type Test = super::LimitedCache<String, usize>;
117
118 #[test]
119 fn test_updates_existing_item() {
120 let mut t = Test::new(3);
121 t.insert("abc".into(), 1);
122 t.insert("abc".into(), 2);
123 assert_eq!(t.get("abc"), Some(&2));
124 }
125
126 #[test]
127 fn test_evicts_oldest_item() {
128 let mut t = Test::new(3);
129 t.insert("abc".into(), 1);
130 t.insert("def".into(), 2);
131 t.insert("ghi".into(), 3);
132
133 assert_eq!(t.get("abc"), None);
134 assert_eq!(t.get("def"), Some(&2));
135 assert_eq!(t.get("ghi"), Some(&3));
136 }
137
138 #[test]
139 fn test_evicts_second_oldest_item_if_first_removed() {
140 let mut t = Test::new(3);
141 t.insert("abc".into(), 1);
142 t.insert("def".into(), 2);
143
144 assert_eq!(t.remove("abc"), Some(1));
145
146 t.insert("ghi".into(), 3);
147 t.insert("jkl".into(), 4);
148
149 assert_eq!(t.get("abc"), None);
150 assert_eq!(t.get("def"), None);
151 assert_eq!(t.get("ghi"), Some(&3));
152 assert_eq!(t.get("jkl"), Some(&4));
153 }
154
155 #[test]
156 fn test_evicts_after_second_oldest_item_removed() {
157 let mut t = Test::new(3);
158 t.insert("abc".into(), 1);
159 t.insert("def".into(), 2);
160
161 assert_eq!(t.remove("def"), Some(2));
162 assert_eq!(t.get("abc"), Some(&1));
163
164 t.insert("ghi".into(), 3);
165 t.insert("jkl".into(), 4);
166
167 assert_eq!(t.get("abc"), None);
168 assert_eq!(t.get("def"), None);
169 assert_eq!(t.get("ghi"), Some(&3));
170 assert_eq!(t.get("jkl"), Some(&4));
171 }
172
173 #[test]
174 fn test_removes_all_items() {
175 let mut t = Test::new(3);
176 t.insert("abc".into(), 1);
177 t.insert("def".into(), 2);
178
179 assert_eq!(t.remove("def"), Some(2));
180 assert_eq!(t.remove("abc"), Some(1));
181
182 t.insert("ghi".into(), 3);
183 t.insert("jkl".into(), 4);
184 t.insert("mno".into(), 5);
185
186 assert_eq!(t.get("abc"), None);
187 assert_eq!(t.get("def"), None);
188 assert_eq!(t.get("ghi"), None);
189 assert_eq!(t.get("jkl"), Some(&4));
190 assert_eq!(t.get("mno"), Some(&5));
191 }
192
193 #[test]
194 fn test_inserts_many_items() {
195 let mut t = Test::new(3);
196
197 for _ in 0..10000 {
198 t.insert("abc".into(), 1);
199 t.insert("def".into(), 2);
200 t.insert("ghi".into(), 3);
201 }
202 }
203
204 #[test]
205 fn test_get_or_insert_default_and_edit_evicts_old_items_to_meet_capacity() {
206 let mut t = Test::new(3);
207
208 t.get_or_insert_default_and_edit("abc".into(), |v| *v += 1);
209 t.get_or_insert_default_and_edit("def".into(), |v| *v += 2);
210
211 t.get_or_insert_default_and_edit("ghi".into(), |v| *v += 3);
213 assert_eq!(t.get("abc"), None);
214
215 t.get_or_insert_default_and_edit("jkl".into(), |v| *v += 4);
217 assert_eq!(t.get("def"), None);
218
219 t.get_or_insert_default_and_edit("abc".into(), |v| *v += 5);
221 assert_eq!(t.get("ghi"), None);
222
223 t.get_or_insert_default_and_edit("def".into(), |v| *v += 6);
225
226 assert_eq!(t.get("abc"), Some(&5));
227 assert_eq!(t.get("def"), Some(&6));
228 assert_eq!(t.get("ghi"), None);
229 assert_eq!(t.get("jkl"), None);
230 }
231
232 #[test]
233 fn test_get_or_insert_default_and_edit_edits_existing_item() {
234 let mut t = Test::new(3);
235
236 t.get_or_insert_default_and_edit("abc".into(), |v| *v += 1);
237 t.get_or_insert_default_and_edit("abc".into(), |v| *v += 2);
238 t.get_or_insert_default_and_edit("abc".into(), |v| *v += 3);
239
240 assert_eq!(t.get("abc"), Some(&6));
241 }
242}