engine/store/storage/
cache.rs

1// Copyright 2020-2021 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::store::storage::Value;
5
6use serde::{Deserialize, Serialize};
7
8use std::{
9    collections::{hash_map::Entry, HashMap},
10    fmt::Debug,
11    hash::Hash,
12    time::{Duration, SystemTime},
13};
14
15/// The [`Cache`] struct used to store the data in an ordered format.
16// #[deprecated(note = "use [`stronghold_iota_new::client_new::types::cache::Cache")]
17#[derive(Serialize, Deserialize, Clone, Debug)]
18pub struct Cache<K, V>
19where
20    K: Hash + Eq + Clone,
21    V: Clone + Debug,
22{
23    // hashmap of data.
24    table: HashMap<K, Value<V>>,
25    // the scan frequency for removing data based on the expiration time.
26    scan_freq: Option<Duration>,
27    // a created at timestamp.
28    created_at: SystemTime,
29    // a last scan timestamp.
30    last_scan_at: Option<SystemTime>,
31}
32
33impl<K: Hash + Eq + Clone, V: Clone + Debug> Cache<K, V> {
34    /// creates a new empty [`Cache`]
35    /// # Example
36    /// ```
37    /// use engine::store::Cache;
38    /// use std::time::Duration;
39    ///
40    /// let mut cache = Cache::new();
41    ///
42    /// let key: Vec<u8> = b"key".to_vec();
43    /// let value: Vec<u8> = b"value".to_vec();
44    ///
45    /// cache.insert(key.clone(), value.clone(), None);
46    ///
47    /// assert_eq!(cache.get(&key), Some(&value))
48    /// ```
49    pub fn new() -> Self {
50        Self {
51            table: HashMap::new(),
52            scan_freq: None,
53            created_at: SystemTime::now(),
54            last_scan_at: None,
55        }
56    }
57
58    /// creates an empty [`Cache`] with a periodic scanner which identifies expired entries.
59    ///
60    /// # Example
61    /// ```
62    /// use engine::store::Cache;
63    /// use std::time::Duration;
64    ///
65    /// let scan_freq = Duration::from_secs(60);
66    ///
67    /// let mut cache = Cache::create_with_scanner(scan_freq);
68    ///
69    /// let key: &'static str = "key";
70    /// let value: &'static str = "value";
71    ///
72    /// cache.insert(key, value, None);
73    ///
74    /// assert_eq!(cache.get(&key), Some(&value));
75    /// ```
76    pub fn create_with_scanner(scan_freq: Duration) -> Self {
77        Self {
78            table: HashMap::new(),
79            scan_freq: Some(scan_freq),
80            created_at: SystemTime::now(),
81            last_scan_at: None,
82        }
83    }
84
85    /// Gets the value associated with the specified key.
86    ///
87    /// # Example
88    /// ```
89    /// use engine::store::Cache;
90    /// use std::time::Duration;
91    ///
92    /// let mut cache = Cache::new();
93    ///
94    /// let key: &'static str = "key";
95    /// let value: &'static str = "value";
96    ///
97    /// cache.insert(key, value, None);
98    ///
99    /// assert_eq!(cache.get(&key), Some(&value))
100    /// ```
101    pub fn get(&self, key: &K) -> Option<&V> {
102        let now = SystemTime::now();
103
104        self.table
105            .get(key)
106            .filter(|value| !value.has_expired(now))
107            .map(|value| &value.val)
108    }
109
110    /// Gets the value associated with the specified key.  If the key could not be found in the [`Cache`], creates and
111    /// inserts the value using a specified `func` function. # Example
112    /// ```
113    /// use engine::store::Cache;
114    /// use std::time::Duration;
115    ///
116    /// let mut cache = Cache::new();
117    ///
118    /// let key = "key";
119    /// let value = "value";
120    ///
121    /// assert_eq!(cache.get_or_insert(key, move || value, None), &value);
122    /// assert!(cache.contains_key(&key));
123    /// ```
124    pub fn get_or_insert<F>(&mut self, key: K, func: F, lifetime: Option<Duration>) -> &V
125    where
126        F: Fn() -> V,
127    {
128        let now = SystemTime::now();
129
130        self.try_remove_expired_items(now);
131
132        match self.table.entry(key) {
133            Entry::Occupied(mut occ) => {
134                if occ.get().has_expired(now) {
135                    occ.insert(Value::new(func(), lifetime));
136                }
137
138                &occ.into_mut().val
139            }
140            Entry::Vacant(vac) => &vac.insert(Value::new(func(), lifetime)).val,
141        }
142    }
143
144    /// Insert a key-value pair into the cache.
145    /// If key was not present, a [`None`] is returned, else the value is updated and the old value is returned.  
146    ///
147    /// # Example
148    /// ```
149    /// use engine::store::Cache;
150    /// use std::time::Duration;
151    ///
152    /// let mut cache = Cache::new();
153    ///
154    /// let key: &'static str = "key";
155    /// let value: &'static str = "value";
156    ///
157    /// let insert = cache.insert(key, value, None);
158    ///
159    /// assert_eq!(cache.get(&key), Some(&value));
160    /// assert!(insert.is_none());
161    /// ```
162    pub fn insert(&mut self, key: K, value: V, lifetime: Option<Duration>) -> Option<V> {
163        let now = SystemTime::now();
164
165        self.try_remove_expired_items(now);
166
167        self.table
168            .insert(key, Value::new(value, lifetime))
169            .filter(|value| !value.has_expired(now))
170            .map(|value| value.val)
171    }
172
173    /// Removes a key from the cache.  Returns the value from the key if the key existed in the cache.
174    ///
175    /// # Example
176    ///
177    /// ```
178    /// use engine::store::Cache;
179    /// use std::time::Duration;
180    ///
181    /// let mut cache = Cache::new();
182    ///
183    /// let key: &'static str = "key";
184    /// let value: &'static str = "value";
185    ///
186    /// let insert = cache.insert(key, value, None);
187    /// assert_eq!(cache.remove(&key), Some(value));
188    /// assert!(!cache.contains_key(&key));
189    /// ```
190    pub fn remove(&mut self, key: &K) -> Option<V> {
191        let now = SystemTime::now();
192
193        self.try_remove_expired_items(now);
194
195        self.table
196            .remove(key)
197            .filter(|value| !value.has_expired(now))
198            .map(|value| value.val)
199    }
200
201    // Check if the [`Cache<K, V>`] contains a specific key.
202    pub fn contains_key(&self, key: &K) -> bool {
203        let now = SystemTime::now();
204
205        self.table.get(key).filter(|value| !value.has_expired(now)).is_some()
206    }
207
208    // Get the last scanned at time.
209    pub fn get_last_scanned_at(&self) -> Option<SystemTime> {
210        self.last_scan_at
211    }
212
213    /// Get the cache's scan frequency.
214    ///
215    /// # Example
216    /// ```
217    /// use engine::store::Cache;
218    /// use std::time::Duration;
219    ///
220    /// let scan_freq = Duration::from_secs(60);
221    ///
222    /// let mut cache = Cache::create_with_scanner(scan_freq);
223    ///
224    /// let key: &'static str = "key";
225    /// let value: &'static str = "value";
226    ///
227    /// cache.insert(key, value, None);
228    ///
229    /// assert_eq!(cache.get_scan_freq(), Some(scan_freq));
230    /// ```
231    pub fn get_scan_freq(&self) -> Option<Duration> {
232        self.scan_freq
233    }
234
235    /// Clear the stored cache and reset.
236    pub fn clear(&mut self) {
237        self.table.clear();
238        self.scan_freq = None;
239        self.created_at = SystemTime::now();
240        self.last_scan_at = None;
241    }
242
243    /// Returns a list of all keys inside the [`Cache`] as references
244    pub fn keys(&self) -> Vec<K> {
245        self.table.keys().cloned().collect()
246    }
247
248    /// attempts to remove expired items based on the current system time provided.
249    fn try_remove_expired_items(&mut self, now: SystemTime) {
250        if let Some(frequency) = self.scan_freq {
251            let since = now
252                .duration_since(self.last_scan_at.unwrap_or(self.created_at))
253                .expect("System time is before the scanned time");
254
255            if since >= frequency {
256                self.table.retain(|_, value| !value.has_expired(now));
257
258                self.last_scan_at = Some(now)
259            }
260        }
261    }
262}
263
264/// Default implementation for [`Cache<K, V>`]
265impl<K: Hash + Eq + Clone, V: Clone + Debug> Default for Cache<K, V> {
266    fn default() -> Self {
267        Cache::new()
268    }
269}