1use std::collections::VecDeque;
2
3use rustc_hash::FxHashMap;
4
5pub struct FifoCache<K, V> {
24 map: FxHashMap<K, V>,
25 order: VecDeque<K>,
26 capacity: usize,
27}
28
29impl<K, V> FifoCache<K, V> {
30 pub fn new(capacity: usize) -> Self {
31 Self {
32 map: FxHashMap::default(),
33 order: VecDeque::new(),
34 capacity,
35 }
36 }
37}
38
39impl<K, V> FifoCache<K, V>
40where
41 K: Clone + Eq + std::hash::Hash,
42{
43 pub fn set_capacity(&mut self, capacity: usize) {
45 self.capacity = capacity;
46 while self.order.len() > capacity {
47 if let Some(old_key) = self.order.pop_front() {
48 self.map.remove(&old_key);
49 }
50 }
51 }
52
53 pub fn insert(&mut self, key: K, value: V) {
56 if self.capacity == 0 {
57 return;
58 }
59
60 if self.map.contains_key(&key) {
61 self.map.insert(key, value);
62 } else {
63 if self.order.len() >= self.capacity
64 && let Some(old_key) = self.order.pop_front()
65 {
66 self.map.remove(&old_key);
67 }
68 self.order.push_back(key.clone());
69 self.map.insert(key, value);
70 }
71 }
72
73 pub fn get(&self, key: &K) -> Option<&V> {
74 self.map.get(key)
75 }
76
77 pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
79 self.map.get_mut(key)
80 }
81
82 pub fn capacity(&self) -> usize {
83 self.capacity
84 }
85
86 pub fn len(&self) -> usize {
87 self.map.len()
88 }
89
90 pub fn is_empty(&self) -> bool {
91 self.map.is_empty()
92 }
93
94 pub fn clear(&mut self) {
95 self.map.clear();
96 self.order.clear();
97 }
98}
99
100impl<K, V> std::fmt::Debug for FifoCache<K, V>
101where
102 K: std::fmt::Debug,
103 V: std::fmt::Debug,
104{
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 f.debug_struct("FifoCache")
107 .field("map", &self.map)
108 .field("order", &self.order)
109 .field("capacity", &self.capacity)
110 .finish()
111 }
112}
113
114#[cfg(test)]
115mod test {
116 use crate::FifoCache;
117
118 #[test]
119 fn custom_capacity() {
120 let mut cache = FifoCache::new(2);
121 cache.insert(1, "first");
122 cache.insert(2, "second");
123 cache.insert(1, "updated");
124 assert_eq!(cache.get(&1), Some(&"updated"));
125 assert_eq!(cache.len(), 2);
126
127 cache.insert(3, "third");
128 assert_eq!(cache.get(&1), None);
129 assert_eq!(cache.get(&2), Some(&"second"));
130 assert_eq!(cache.get(&3), Some(&"third"));
131 assert_eq!(cache.len(), 2);
132 }
133
134 #[test]
135 fn zero_capacity_keeps_nothing() {
136 let mut cache = FifoCache::new(0);
137 for key in 0..3 {
138 cache.insert(key, key);
139 }
140 assert!(cache.is_empty());
141 assert!(cache.order.is_empty());
142 assert_eq!(cache.get(&0), None);
143 }
144
145 #[test]
146 fn clear_preserves_custom_capacity() {
147 let mut cache = FifoCache::new(1);
148 cache.insert(1, "first");
149 cache.clear();
150 assert!(cache.is_empty());
151
152 cache.insert(2, "second");
153 cache.insert(3, "third");
154 assert_eq!(cache.get(&1), None);
155 assert_eq!(cache.get(&2), None);
156 assert_eq!(cache.get(&3), Some(&"third"));
157 assert_eq!(cache.len(), 1);
158 }
159
160 #[test]
161 fn shrinking_removes_oldest_results() {
162 let mut cache = FifoCache::new(4);
163 for key in 0..4 {
164 cache.insert(key, key * 10);
165 }
166 cache.insert(0, 100);
167
168 cache.set_capacity(2);
169 assert_eq!(cache.capacity(), 2);
170 assert_eq!(cache.len(), 2);
171 assert_eq!(cache.get(&0), None);
172 assert_eq!(cache.get(&1), None);
173 assert_eq!(cache.get(&2), Some(&20));
174 assert_eq!(cache.get(&3), Some(&30));
175
176 cache.insert(4, 40);
177 assert_eq!(cache.get(&2), None);
178 assert_eq!(cache.get(&3), Some(&30));
179 assert_eq!(cache.get(&4), Some(&40));
180 assert_eq!(cache.len(), 2);
181 }
182
183 #[test]
184 fn growing_preserves_saved_results() {
185 let mut cache = FifoCache::new(1);
186 cache.insert(1, "first");
187 cache.set_capacity(3);
188 cache.insert(2, "second");
189 cache.insert(3, "third");
190
191 assert_eq!(cache.capacity(), 3);
192 assert_eq!(cache.len(), 3);
193 assert_eq!(cache.get(&1), Some(&"first"));
194 assert_eq!(cache.get(&2), Some(&"second"));
195 assert_eq!(cache.get(&3), Some(&"third"));
196 }
197
198 #[test]
199 fn shrinking_to_zero_then_growing() {
200 let mut cache = FifoCache::new(2);
201 cache.insert(1, "first");
202 cache.insert(2, "second");
203 cache.set_capacity(0);
204 cache.insert(3, "third");
205
206 assert_eq!(cache.capacity(), 0);
207 assert!(cache.is_empty());
208 assert!(cache.order.is_empty());
209
210 cache.set_capacity(1);
211 cache.insert(4, "fourth");
212 assert_eq!(cache.get(&1), None);
213 assert_eq!(cache.get(&4), Some(&"fourth"));
214 assert_eq!(cache.len(), 1);
215 }
216
217 #[test]
218 fn unchanged_capacity_preserves_saved_results() {
219 let mut cache = FifoCache::new(2);
220 cache.insert(1, "first");
221 cache.insert(2, "second");
222 cache.set_capacity(2);
223
224 assert_eq!(cache.len(), 2);
225 assert_eq!(cache.get(&1), Some(&"first"));
226 assert_eq!(cache.get(&2), Some(&"second"));
227 }
228}