aba_cache/lru/
mod.rs

1#[cfg(not(feature = "asynchronous"))]
2use std::rc::Rc;
3#[cfg(feature = "asynchronous")]
4use std::sync::Arc;
5use std::{borrow::Borrow, collections::HashMap, hash::Hash};
6
7use storage::{Pointer, Storage};
8
9#[cfg(feature = "asynchronous")]
10pub(crate) mod asynchronous;
11mod storage;
12
13#[cfg(test)]
14mod tests;
15
16#[cfg(not(feature = "asynchronous"))]
17type Ref<T> = Rc<T>;
18
19#[cfg(feature = "asynchronous")]
20type Ref<T> = Arc<T>;
21
22/// Cache with LRU eviction strategy
23pub struct Cache<K, V> {
24    storage: Storage<Ref<K>, V>,
25    map: HashMap<Ref<K>, Pointer>,
26}
27
28impl<K: Hash + Eq, V> Cache<K, V> {
29    /// Create new Cache, which will expiring its entry after `timeout_secs`
30    /// and allocating new slab with capacity `multiply_cap` when no space
31    /// is ready and no entry expires
32    pub fn new(multiply_cap: usize, timeout_secs: u64) -> Self {
33        if multiply_cap == 0 {
34            panic!("Cache defined with 0 capacity")
35        }
36        Cache {
37            storage: Storage::new(multiply_cap, timeout_secs),
38            map: HashMap::with_capacity(multiply_cap),
39        }
40    }
41
42    /// Returns a reference to the value of the key in the cache or `None` if it is not
43    /// present in the cache. Moves the key to the head of the LRU list if it exists.
44    ///
45    /// # Example
46    ///
47    /// ```
48    /// use aba_cache as cache;
49    /// use cache::LruCache;
50    ///
51    /// let mut cache = LruCache::new(2, 60);
52    ///
53    /// cache.put(1, "a");
54    /// cache.put(2, "b");
55    /// cache.put(2, "c");
56    /// cache.put(3, "d");
57    ///
58    /// assert_eq!(cache.get(&1), Some(&"a"));
59    /// assert_eq!(cache.get(&2), Some(&"c"));
60    /// assert_eq!(cache.get(&3), Some(&"d"));
61    /// ```
62    pub fn get<Q: ?Sized>(&mut self, key: &Q) -> Option<&V>
63    where
64        Ref<K>: Borrow<Q>,
65        Q: Hash + Eq,
66    {
67        if self.map.is_empty() {
68            None
69        } else if let Some(&index) = self.map.get(key) {
70            let result = self.storage.get(index);
71            if result.is_none() {
72                self.map.remove(key);
73            }
74            result
75        } else {
76            None
77        }
78    }
79
80    /// Puts a key-value pair into cache. If the key already exists in the cache, then it updates
81    /// the key's value and returns the old value. Otherwise, `None` is returned.
82    ///
83    /// # Example
84    ///
85    /// ```
86    /// use aba_cache as cache;
87    /// use cache::LruCache;
88    ///
89    /// let mut cache = LruCache::new(2, 60);
90    ///
91    /// assert_eq!(None, cache.put(1, "a"));
92    /// assert_eq!(None, cache.put(2, "b"));
93    /// assert_eq!(Some("b"), cache.put(2, "beta"));
94    ///
95    /// assert_eq!(cache.get(&1), Some(&"a"));
96    /// assert_eq!(cache.get(&2), Some(&"beta"));
97    /// ```
98    pub fn put(&mut self, key: K, value: V) -> Option<V> {
99        if let Some(&index) = self.map.get(&key) {
100            Some(self.storage.update(index, value))
101        } else {
102            let key = Ref::new(key);
103            let (idx, old_pair) = self.storage.put(key.clone(), value);
104            let result = if let Some((old_key, old_data)) = old_pair {
105                self.map.remove(&old_key);
106                Some(old_data)
107            } else {
108                None
109            };
110            self.map.insert(key, idx);
111            result
112        }
113    }
114
115    /// Removes expired entry.
116    /// This operation will deallocate empty slab caused by entry removal if any.
117    ///
118    /// # Example
119    ///
120    /// ```
121    /// use aba_cache as cache;
122    /// use cache::LruCache;
123    /// use std::{thread, time::Duration};
124    ///
125    /// let mut cache = LruCache::new(2, 1);
126    ///
127    /// cache.put(String::from("1"), "one");
128    /// cache.put(String::from("2"), "two");
129    /// cache.put(String::from("3"), "three");
130    ///
131    /// assert_eq!(cache.len(), 3);
132    /// assert_eq!(cache.capacity(), 4);
133    ///
134    /// thread::sleep(Duration::from_secs(1));
135    /// cache.evict();
136    ///
137    /// assert_eq!(cache.len(), 0);
138    /// assert_eq!(cache.capacity(), 0);
139    /// ```
140    pub fn evict(&mut self) {
141        if !self.is_empty() {
142            self.storage.evict().drain(..).for_each(|key| {
143                self.map.remove(&key);
144            })
145        }
146    }
147
148    /// Returns the maximum number of key-value pairs the cache can hold.
149    /// Note that on data insertion, when no space is available and no
150    /// entry is timeout, then capacity will be added with `multiply_cap`
151    /// to accomodate.
152    ///
153    /// # Example
154    ///
155    /// ```
156    /// use aba_cache as cache;
157    /// use cache::LruCache;
158    ///
159    /// let mut cache: LruCache<usize, &str> = LruCache::new(2, 60);
160    /// assert_eq!(cache.capacity(), 2);
161    ///
162    /// cache.put(1, "a");
163    /// assert_eq!(cache.capacity(), 2);
164    ///
165    /// cache.put(2, "b");
166    /// assert_eq!(cache.capacity(), 2);
167    ///
168    /// cache.put(3, "c");
169    /// assert_eq!(cache.capacity(), 4);
170    /// ```
171    pub fn capacity(&self) -> usize {
172        self.storage.capacity()
173    }
174
175    /// Returns the number of key-value pairs that are currently in the the cache.
176    /// Note that len should be less than or equal to capacity
177    ///
178    /// # Example
179    ///
180    /// ```
181    /// use aba_cache as cache;
182    /// use cache::LruCache;
183    ///
184    /// let mut cache = LruCache::new(2, 60);
185    /// assert_eq!(cache.len(), 0);
186    ///
187    /// cache.put(1, "a");
188    /// assert_eq!(cache.len(), 1);
189    ///
190    /// cache.put(2, "b");
191    /// assert_eq!(cache.len(), 2);
192    /// assert_eq!(cache.capacity(), 2);
193    ///
194    /// cache.put(3, "c");
195    /// assert_eq!(cache.len(), 3);
196    /// assert_eq!(cache.capacity(), 4);
197    /// ```
198    pub fn len(&self) -> usize {
199        self.map.len()
200    }
201
202    /// Returns a bool indicating whether the cache is empty or not.
203    ///
204    /// # Example
205    ///
206    /// ```
207    /// use aba_cache as cache;
208    /// use cache::LruCache;
209    ///
210    /// let mut cache = LruCache::new(2, 60);
211    /// assert!(cache.is_empty());
212    ///
213    /// cache.put(1, "a");
214    /// assert!(!cache.is_empty());
215    /// ```
216    pub fn is_empty(&self) -> bool {
217        self.map.is_empty()
218    }
219}