unc_cache/
sync.rs

1use lru::LruCache;
2use std::convert::Infallible;
3use std::hash::Hash;
4use std::sync::Mutex;
5
6/// A wrapper around `LruCache`. This struct is thread safe, doesn't return any references to any
7/// elements inside.
8pub struct SyncLruCache<K, V> {
9    inner: Mutex<LruCache<K, V>>,
10}
11
12impl<K, V> SyncLruCache<K, V>
13where
14    K: Hash + Eq,
15    V: Clone,
16{
17    /// Creats a new `LRU` cache that holds at most `cap` items.
18    pub fn new(cap: usize) -> Self {
19        Self { inner: Mutex::new(LruCache::<K, V>::new(cap)) }
20    }
21
22    /// Returns the number of key-value pairs that are currently in the the cache.
23    pub fn len(&self) -> usize {
24        self.inner.lock().unwrap().len()
25    }
26
27    /// Returns true if the cache is empty and false otherwise.
28    pub fn is_empty(&self) -> bool {
29        self.inner.lock().unwrap().is_empty()
30    }
31
32    /// Return the value of they key in the cache otherwise computes the value and inserts it into
33    /// the cache. If the key is already in the cache, they gets gets moved to the head of
34    /// the LRU list.
35    pub fn get_or_put<F>(&self, key: K, f: F) -> V
36    where
37        V: Clone,
38        F: FnOnce(&K) -> V,
39    {
40        Result::<_, Infallible>::unwrap(self.get_or_try_put(key, |k| Ok(f(k))))
41    }
42
43    /// Returns the value of they key in the cache if present, otherwise
44    /// computes the value using the provided closure.
45    ///
46    /// If the key is already in the cache, it gets moved to the head of the LRU
47    /// list.
48    ///
49    /// If the provided closure fails, the error is returned and the cache is
50    /// not updated.
51    pub fn get_or_try_put<F, E>(&self, key: K, f: F) -> Result<V, E>
52    where
53        V: Clone,
54        F: FnOnce(&K) -> Result<V, E>,
55    {
56        if let Some(result) = self.get(&key) {
57            return Ok(result);
58        }
59        let val = f(&key)?;
60        let val_clone = val.clone();
61        self.inner.lock().unwrap().put(key, val_clone);
62        Ok(val)
63    }
64
65    /// Puts a key-value pair into cache. If the key already exists in the cache,
66    /// then it updates the key's value.
67    pub fn put(&self, key: K, value: V) {
68        self.inner.lock().unwrap().put(key, value);
69    }
70
71    /// Returns the value of the key in the cache or None if it is not present in the cache.
72    /// Moves the key to the head of the LRU list if it exists.
73    pub fn get(&self, key: &K) -> Option<V> {
74        self.inner.lock().unwrap().get(key).cloned()
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn test_cache() {
84        let cache = SyncLruCache::<u64, Vec<u64>>::new(100);
85
86        assert_eq!(cache.get(&0u64), None);
87        assert_eq!(cache.get_or_put(123u64, |key| vec![*key, 123]), vec![123u64, 123]);
88        assert_eq!(cache.get(&123u64), Some(vec![123u64, 123]));
89        assert_eq!(cache.get(&0u64), None);
90    }
91}