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}