rumtk_core/
cache.rs

1/*
2 * rumtk attempts to implement HL7 and medical protocols for interoperability in medicine.
3 * This toolkit aims to be reliable, simple, performant, and standards compliant.
4 * Copyright (C) 2024  Luis M. Santos, M.D.
5 * Copyright (C) 2025  MedicalMasses L.L.C.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22pub use ahash::AHashMap;
23use core::hash::Hash;
24pub use once_cell::unsync::Lazy;
25use std::sync::Arc;
26pub use std::sync::Mutex;
27/**************************** Constants**************************************/
28pub const DEFAULT_CACHE_PAGE_SIZE: usize = 10;
29/// I don't think most scenarios will need more than 10 items worth of memory pre-allocated at a time.
30/**************************** Caches ****************************************/
31
32/**************************** Types *****************************************/
33///
34/// Generic Cache store object. One use case will be to use a search string as the key and store
35/// the search parsing object here.
36///
37pub type RUMCache<K, V> = AHashMap<K, V>;
38pub type LazyRUMCache<K, V> = Lazy<Arc<RUMCache<K, V>>>;
39
40/**************************** Traits ****************************************/
41
42/**************************** Helpers ***************************************/
43pub const fn new_cache<K, V>() -> LazyRUMCache<K, V> {
44    LazyRUMCache::new(|| Arc::new(RUMCache::with_capacity(DEFAULT_CACHE_PAGE_SIZE)))
45}
46
47pub fn get_or_set_from_cache<'a, K, V, F>(
48    cache: &'a mut LazyRUMCache<K, V>,
49    expr: &K,
50    new_fn: F,
51) -> &'a V
52where
53    K: Hash + Eq + Clone,
54    V: Clone,
55    F: Fn(&K) -> V,
56{
57    if !cache.contains_key(expr) {
58        let cache_ref = Arc::get_mut(cache).unwrap();
59        cache_ref.insert(expr.clone(), new_fn(expr).clone());
60    }
61    cache.get(expr).unwrap()
62}
63
64pub fn cache_push<'a, K, V>(cache: &'a mut LazyRUMCache<K, V>, expr: &K, val: &V) -> &'a V
65where
66    K: Hash + Eq + Clone,
67    V: Clone,
68{
69    let cache_ref = Arc::get_mut(cache).unwrap();
70    cache_ref.insert(expr.clone(), val.clone());
71    cache.get(expr).unwrap()
72}
73
74pub fn cache_get<'a, K, V>(cache: &'a mut LazyRUMCache<K, V>, expr: &K) -> Option<&'a V>
75where
76    K: Hash + Eq + Clone,
77    V: Clone,
78{
79    cache.get(expr)
80}
81
82pub mod cache_macros {
83    ///
84    /// Searches for item in global cache. If global cache lacks item, create item using factory
85    /// function passed to this macro.
86    ///
87    /// ```
88    /// use rumtk_core::rumtk_cache_fetch;
89    /// use rumtk_core::cache::{new_cache, LazyRUMCache};
90    /// use std::sync::Arc;
91    ///
92    /// type StringCache = LazyRUMCache<String, String>;
93    ///
94    /// fn init_cache(k: &String) -> String {
95    ///    String::from(k)
96    /// }
97    ///
98    /// let mut cache: StringCache = new_cache();
99    ///
100    /// let test_key: String = String::from("Hello World");
101    /// let v = rumtk_cache_fetch!(
102    ///     &mut cache,
103    ///     &test_key,
104    ///     init_cache
105    /// );
106    ///
107    /// assert_eq!(test_key.as_str(), v.as_str(), "The inserted key is not the same to what was passed as input!");
108    ///
109    ///
110    /// ```
111    ///
112    #[macro_export]
113    macro_rules! rumtk_cache_fetch {
114        ( $cache:expr, $key:expr, $func:expr ) => {{
115            use $crate::cache::get_or_set_from_cache;
116            // Do not remove the clippy disable decorator here since we do intend to expand within
117            // the unsafe block. Expanding elsewhere prevents us from getting access to the cache's
118            // internal references due to compiler error
119            #[allow(clippy::macro_metavars_in_unsafe)]
120            unsafe {
121                get_or_set_from_cache($cache, $key, $func)
122            }
123        }};
124    }
125
126    ///
127    /// Overrides contents in cache at key ```K```. No checks are performed for the existence of the
128    /// key in the cache. Be careful not to override necessary data.
129    ///
130    /// ```
131    /// use rumtk_core::{rumtk_cache_fetch, rumtk_cache_push};
132    /// use rumtk_core::cache::{new_cache, LazyRUMCache, cache_push};
133    /// use std::sync::Arc;
134    ///
135    /// type StringCache = LazyRUMCache<String, Vec<String>>;
136    ///
137    /// fn init_cache(k: &String) -> Vec<String> {
138    ///    vec![]
139    /// }
140    ///
141    /// let mut cache: StringCache = new_cache();
142    ///
143    /// let test_key: String = String::from("Hello World");
144    /// let test_value: String = String::from("?????");
145    ///
146    /// rumtk_cache_fetch!(
147    ///     &mut cache,
148    ///     &test_key,
149    ///     init_cache
150    /// );
151    ///
152    /// let v = rumtk_cache_push!(
153    ///     &mut cache,
154    ///     &test_key,
155    ///     &vec![test_value.clone()]
156    /// );
157    ///
158    /// assert_eq!(test_value.as_str(), v.get(0).unwrap().as_str(), "The inserted key is not the same to what was passed as input!");
159    ///
160    ///
161    /// ```
162    ///
163    #[macro_export]
164    macro_rules! rumtk_cache_push {
165        ( $cache:expr, $key:expr, $val:expr ) => {{
166            use $crate::cache::cache_push;
167            // Do not remove the clippy disable decorator here since we do intend to expand within
168            // the unsafe block. Expanding elsewhere prevents us from getting access to the cache's
169            // internal references due to compiler error
170            #[allow(clippy::macro_metavars_in_unsafe)]
171            unsafe {
172                cache_push($cache, $key, $val)
173            }
174        }};
175    }
176
177    ///
178    /// Overrides contents in cache at key ```K```. No checks are performed for the existence of the
179    /// key in the cache. Be careful not to override necessary data.
180    ///
181    /// ```
182    /// use rumtk_core::{rumtk_cache_fetch, rumtk_cache_push, rumtk_cache_get};
183    /// use rumtk_core::cache::{new_cache, LazyRUMCache, cache_push, cache_get};
184    /// use std::sync::Arc;
185    ///
186    /// type StringCache = LazyRUMCache<String, Vec<String>>;
187    ///
188    /// fn init_cache(k: &String) -> Vec<String> {
189    ///    vec![]
190    /// }
191    ///
192    /// let mut cache: StringCache = new_cache();
193    ///
194    /// let test_key: String = String::from("Hello World");
195    /// let test_value: String = String::from("?????");
196    ///
197    /// rumtk_cache_fetch!(
198    ///     &mut cache,
199    ///     &test_key,
200    ///     init_cache
201    /// );
202    ///
203    /// rumtk_cache_push!(
204    ///     &mut cache,
205    ///     &test_key,
206    ///     &vec![test_value.clone()]
207    /// );
208    ///
209    /// let v = rumtk_cache_get!(
210    ///     &mut cache,
211    ///     &test_key
212    /// ).unwrap();
213    ///
214    /// assert_eq!(test_value.as_str(), v.get(0).unwrap().as_str(), "The inserted key is not the same to what was passed as input!");
215    ///
216    ///
217    /// ```
218    ///
219    #[macro_export]
220    macro_rules! rumtk_cache_get {
221        ( $cache:expr, $key:expr ) => {{
222            use $crate::cache::cache_get;
223            // Do not remove the clippy disable decorator here since we do intend to expand within
224            // the unsafe block. Expanding elsewhere prevents us from getting access to the cache's
225            // internal references due to compiler error
226            #[allow(clippy::macro_metavars_in_unsafe)]
227            unsafe {
228                cache_get($cache, $key)
229            }
230        }};
231    }
232}