hydrate_base/
lru_cache.rs

1use crate::hashing::HashMap;
2use std::hash::Hash;
3
4#[derive(Copy, Clone)]
5struct LruCacheNode {
6    next: u32,
7    previous: u32,
8}
9
10pub struct LruCache<K, V> {
11    // Doubly linked list with u32::MAX for "null" and using indices instead of pointers
12    lru_list_head: u32,
13    lru_list_tail: u32,
14    lru_list: Vec<LruCacheNode>,
15
16    // Slots that line up with the doubly linked list
17    lru_list_pairs: Vec<Option<(K, V)>>,
18
19    // Lookup for the index a key is stored at
20    lookup: HashMap<K, u32>,
21}
22
23impl<K: Clone + PartialEq + Eq + Hash, V> LruCache<K, V> {
24    pub fn new(size: u32) -> LruCache<K, V> {
25        assert!(size > 2);
26        let mut lru_list = vec![
27            LruCacheNode {
28                next: 0,
29                previous: 0
30            };
31            size as usize
32        ];
33        lru_list[0].previous = u32::MAX;
34        lru_list[0].next = 1;
35        for i in 1..(size - 1) {
36            lru_list[i as usize].previous = i - 1;
37            lru_list[i as usize].next = i + 1;
38        }
39        lru_list[size as usize - 1].previous = size - 2;
40        lru_list[size as usize - 1].next = u32::MAX;
41
42        let mut lru_list_pairs = Vec::with_capacity(size as usize);
43        for _ in 0..size {
44            lru_list_pairs.push(None);
45        }
46
47        let lookup = HashMap::default();
48
49        LruCache {
50            lru_list_head: 0,
51            lru_list_tail: size - 1,
52            lru_list,
53            lru_list_pairs,
54            lookup,
55        }
56    }
57
58    // For debug, can throw in to try and find when state is invalid
59    /*
60    fn check_list(&self) {
61        let mut iter = self.lru_list_head;
62        let mut count = 0;
63        while self.lru_list[iter as usize].next != u32::MAX {
64            let next_index = self.lru_list[iter as usize].next;
65            assert_eq!(self.lru_list[next_index as usize].previous, iter);
66            iter = self.lru_list[iter as usize].next;
67            count += 1;
68        }
69
70        assert_eq!(count, self.lru_list.len() - 1);
71    }
72    */
73
74    pub fn pairs(&self) -> &Vec<Option<(K, V)>> {
75        &self.lru_list_pairs
76    }
77
78    pub fn pairs_mut(&mut self) -> &mut Vec<Option<(K, V)>> {
79        &mut self.lru_list_pairs
80    }
81
82    fn move_to_front(
83        &mut self,
84        node_index: u32,
85    ) {
86        //self.check_list();
87        let node = self.lru_list[node_index as usize];
88
89        if node_index == self.lru_list_head {
90            // Do nothing if already at head
91            assert_eq!(node.previous, u32::MAX);
92            assert_ne!(node.next, u32::MAX);
93            return;
94        }
95
96        if node_index == self.lru_list_tail {
97            // If we are the tail, make the node previous to us the new tail
98            assert_eq!(node.next, u32::MAX);
99            assert_ne!(node.previous, u32::MAX);
100            self.lru_list_tail = node.previous;
101        }
102
103        // splice this node out of the list.
104        assert_ne!(node.previous, u32::MAX);
105        self.lru_list[node.previous as usize].next = node.next;
106        if node.next != u32::MAX {
107            self.lru_list[node.next as usize].previous = node.previous;
108        }
109
110        // Make this node the new head
111        assert_eq!(
112            self.lru_list[self.lru_list_head as usize].previous,
113            u32::MAX
114        );
115        self.lru_list[self.lru_list_head as usize].previous = node_index;
116        self.lru_list[node_index as usize].previous = u32::MAX;
117        self.lru_list[node_index as usize].next = self.lru_list_head;
118        self.lru_list_head = node_index;
119
120        //self.check_list();
121    }
122
123    fn move_to_back(
124        &mut self,
125        node_index: u32,
126    ) {
127        //self.check_list();
128        let node = self.lru_list[node_index as usize];
129
130        if node_index == self.lru_list_tail {
131            // Do nothing if we are already the tail
132            assert_eq!(node.next, u32::MAX);
133            assert_ne!(node.previous, u32::MAX);
134            return;
135        }
136
137        if node_index == self.lru_list_head {
138            // If we are the head, make the node next/after us the new head
139            assert_eq!(node.previous, u32::MAX);
140            assert_ne!(node.next, u32::MAX);
141            self.lru_list_head = node.next;
142        }
143
144        // splice this node out of the list.
145        if node.previous != u32::MAX {
146            self.lru_list[node.previous as usize].next = node.next;
147        }
148        assert_ne!(node.next, u32::MAX);
149        self.lru_list[node.next as usize].previous = node.previous;
150
151        // Make this node the new tail
152        assert_eq!(self.lru_list[self.lru_list_tail as usize].next, u32::MAX);
153        self.lru_list[self.lru_list_tail as usize].next = node_index;
154        self.lru_list[node_index as usize].previous = self.lru_list_tail;
155        self.lru_list[node_index as usize].next = u32::MAX;
156        self.lru_list_tail = node_index;
157
158        //self.check_list();
159    }
160
161    pub fn get(
162        &mut self,
163        k: &K,
164        mark_as_recently_used: bool,
165    ) -> Option<&V> {
166        if let Some(&node_index) = self.lookup.get(k) {
167            if mark_as_recently_used {
168                // move node to head
169                self.move_to_front(node_index);
170            }
171            // return the value
172            self.lru_list_pairs[node_index as usize]
173                .as_ref()
174                .map(|(_, v)| v)
175        } else {
176            None
177        }
178    }
179
180    pub fn get_mut(
181        &mut self,
182        k: &K,
183        mark_as_recently_used: bool,
184    ) -> Option<&mut V> {
185        if let Some(&node_index) = self.lookup.get(k) {
186            if mark_as_recently_used {
187                // move node to head
188                self.move_to_front(node_index);
189            }
190            // return the value
191            self.lru_list_pairs[node_index as usize]
192                .as_mut()
193                .map(|(_, v)| v)
194        } else {
195            None
196        }
197    }
198
199    pub fn insert(
200        &mut self,
201        k: K,
202        v: V,
203    ) {
204        if let Some(key_to_remove) = self.lru_list_pairs[self.lru_list_tail as usize]
205            .as_ref()
206            .map(|(k, _)| k)
207            .cloned()
208        {
209            self.remove(&key_to_remove);
210        }
211
212        // remove tail element if it exists
213        let node_index = self.lru_list_tail;
214        if let Some((k, _)) = &self.lru_list_pairs[node_index as usize] {
215            self.lookup.remove(k);
216        }
217
218        self.move_to_front(self.lru_list_tail);
219        self.lookup.insert(k.clone(), node_index);
220        self.lru_list_pairs[node_index as usize] = Some((k, v));
221    }
222
223    pub fn remove(
224        &mut self,
225        k: &K,
226    ) -> Option<V> {
227        if let Some(&node_index) = self.lookup.get(k) {
228            // move node to tail
229            self.move_to_back(node_index);
230            // return the value
231            let v = self.lru_list_pairs[node_index as usize]
232                .take()
233                .map(|(_, v)| v);
234            self.lookup.remove(k);
235            v
236        } else {
237            None
238        }
239    }
240}
241
242#[cfg(test)]
243mod test {
244    use super::*;
245
246    #[test]
247    fn check_lru_gets_full() {
248        let mut lru_cache = LruCache::new(3);
249        lru_cache.insert(0, 0);
250        lru_cache.insert(1, 1);
251        lru_cache.insert(2, 2);
252
253        // All should be present
254        assert!(lru_cache.get(&0, false).is_some());
255        assert!(lru_cache.get(&1, false).is_some());
256        assert!(lru_cache.get(&2, false).is_some());
257
258        // The oldest one should be bumped, and the new one should be present
259        lru_cache.insert(3, 3);
260        assert!(lru_cache.get(&0, false).is_none());
261        assert!(lru_cache.get(&1, false).is_some());
262        assert!(lru_cache.get(&2, false).is_some());
263        assert!(lru_cache.get(&3, false).is_some());
264    }
265
266    #[test]
267    fn check_lru_deletes_least_recently_used() {
268        let mut lru_cache = LruCache::new(3);
269        lru_cache.insert(0, 0);
270        lru_cache.insert(1, 1);
271        lru_cache.insert(2, 2);
272
273        // All should be present
274        assert!(lru_cache.get(&0, false).is_some());
275        assert!(lru_cache.get(&1, false).is_some());
276        assert!(lru_cache.get(&2, false).is_some());
277
278        // Touch the oldest, preventing it from being removed
279        lru_cache.get(&0, true);
280
281        lru_cache.insert(3, 3);
282        assert!(lru_cache.get(&0, false).is_some());
283        assert!(lru_cache.get(&1, false).is_none());
284        assert!(lru_cache.get(&2, false).is_some());
285        assert!(lru_cache.get(&3, false).is_some());
286    }
287
288    #[test]
289    fn check_remove() {
290        let mut lru_cache = LruCache::new(3);
291        lru_cache.insert(0, 0);
292        lru_cache.insert(1, 1);
293        lru_cache.insert(2, 2);
294
295        // All should be present
296        assert!(lru_cache.get(&0, false).is_some());
297        assert!(lru_cache.get(&1, false).is_some());
298        assert!(lru_cache.get(&2, false).is_some());
299
300        lru_cache.remove(&0);
301        lru_cache.remove(&2);
302        lru_cache.remove(&1);
303
304        lru_cache.insert(3, 3);
305        assert!(lru_cache.get(&0, false).is_none());
306        assert!(lru_cache.get(&1, false).is_none());
307        assert!(lru_cache.get(&2, false).is_none());
308        assert!(lru_cache.get(&3, false).is_some());
309    }
310}