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, MappedRwLockReadGuard, RwLock, RwLockReadGuard};
26/**************************** Constants**************************************/
27/// I don't think most scenarios will need more than 10 items worth of memory pre-allocated at a time.
28pub const DEFAULT_CACHE_PAGE_SIZE: usize = 10;
29/**************************** Caches ****************************************/
30
31/**************************** Types *****************************************/
32///
33/// Generic Cache store object. One use case will be to use a search string as the key and store
34/// the search parsing object here.
35///
36pub type RUMCache<K, V> = AHashMap<K, V>;
37pub type LazyRUMCache<K, V> = Lazy<Arc<RwLock<RUMCache<K, V>>>>;
38pub type LazyRUMCacheValue<V> = MappedRwLockReadGuard<'static, V>;
39
40/**************************** Traits ****************************************/
41
42/**************************** Helpers ***************************************/
43pub const fn new_cache<K, V>() -> LazyRUMCache<K, V> {
44 LazyRUMCache::new(|| {
45 Arc::new(RwLock::new(RUMCache::with_capacity(
46 DEFAULT_CACHE_PAGE_SIZE,
47 )))
48 })
49}
50
51pub unsafe fn get_or_set_from_cache<K, V, F>(
52 cache: *mut LazyRUMCache<K, V>,
53 expr: &K,
54 new_fn: F,
55) -> LazyRUMCacheValue<V>
56where
57 K: Hash + Eq + Clone + 'static,
58 V: Clone,
59 F: Fn(&K) -> V,
60{
61 let cache_entity = &mut *cache;
62 if !cache_entity.read().unwrap().contains_key(&expr) {
63 let cache_ref = Arc::get_mut(cache_entity).unwrap();
64 cache_ref
65 .write()
66 .unwrap()
67 .insert(expr.clone(), new_fn(&expr).clone());
68 }
69 RwLockReadGuard::map(cache_entity.read().unwrap(), |d| d.get(expr).unwrap())
70}
71
72pub unsafe fn cache_push<K, V>(
73 cache: *mut LazyRUMCache<K, V>,
74 expr: &K,
75 val: &V,
76) -> LazyRUMCacheValue<V>
77where
78 K: Hash + Eq + Clone + 'static,
79 V: Clone,
80{
81 let cache_entity = &mut *cache;
82 cache_entity
83 .write()
84 .unwrap()
85 .insert(expr.clone(), val.clone());
86 RwLockReadGuard::map(cache_entity.read().unwrap(), |d| d.get(expr).unwrap())
87}
88
89pub unsafe fn cache_get<K, V>(cache: *mut LazyRUMCache<K, V>, expr: &K) -> LazyRUMCacheValue<V>
90where
91 K: Hash + Eq + Clone + 'static,
92 V: Clone,
93{
94 let cache_entity = &mut *cache;
95 let cache_ref = cache_entity.read().unwrap();
96 RwLockReadGuard::map(cache_ref, |d| d.get(expr).unwrap())
97}
98
99pub mod cache_macros {
100 ///
101 /// Searches for item in global cache. If global cache lacks item, create item using factory
102 /// function passed to this macro.
103 ///
104 /// ```
105 /// use rumtk_core::rumtk_cache_fetch;
106 /// use rumtk_core::cache::{new_cache, LazyRUMCache};
107 /// use std::sync::Arc;
108 ///
109 /// type StringCache = LazyRUMCache<String, String>;
110 ///
111 /// fn init_cache(k: &String) -> String {
112 /// String::from(k)
113 /// }
114 ///
115 /// let mut cache: StringCache = new_cache();
116 ///
117 /// let test_key: String = String::from("Hello World");
118 /// let v = rumtk_cache_fetch!(
119 /// &raw mut cache,
120 /// &test_key,
121 /// init_cache
122 /// );
123 ///
124 /// assert_eq!(test_key.as_str(), v.as_str(), "The inserted key is not the same to what was passed as input!");
125 ///
126 ///
127 /// ```
128 ///
129 #[macro_export]
130 macro_rules! rumtk_cache_fetch {
131 ( $cache:expr, $key:expr, $func:expr ) => {{
132 use $crate::cache::get_or_set_from_cache;
133 // Do not remove the clippy disable decorator here since we do intend to expand within
134 // the unsafe block. Expanding elsewhere prevents us from getting access to the cache's
135 // internal references due to compiler error
136 #[allow(clippy::macro_metavars_in_unsafe)]
137 unsafe {
138 get_or_set_from_cache($cache, $key, $func)
139 }
140 }};
141 }
142
143 ///
144 /// Overrides contents in cache at key ```K```. No checks are performed for the existence of the
145 /// key in the cache. Be careful not to override necessary data.
146 ///
147 /// ```
148 /// use rumtk_core::{rumtk_cache_fetch, rumtk_cache_push};
149 /// use rumtk_core::cache::{new_cache, LazyRUMCache, cache_push};
150 /// use std::sync::Arc;
151 ///
152 /// type StringCache = LazyRUMCache<String, Vec<String>>;
153 ///
154 /// fn init_cache(k: &String) -> Vec<String> {
155 /// vec![]
156 /// }
157 ///
158 /// let mut cache: StringCache = new_cache();
159 ///
160 /// let test_key: String = String::from("Hello World");
161 /// let test_value: String = String::from("?????");
162 ///
163 /// rumtk_cache_fetch!(
164 /// &raw mut cache,
165 /// &test_key,
166 /// init_cache
167 /// );
168 ///
169 /// let v = rumtk_cache_push!(
170 /// &raw mut cache,
171 /// &test_key,
172 /// &vec![test_value.clone()]
173 /// );
174 ///
175 /// 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!");
176 ///
177 ///
178 /// ```
179 ///
180 #[macro_export]
181 macro_rules! rumtk_cache_push {
182 ( $cache:expr, $key:expr, $val:expr ) => {{
183 use $crate::cache::cache_push;
184 // Do not remove the clippy disable decorator here since we do intend to expand within
185 // the unsafe block. Expanding elsewhere prevents us from getting access to the cache's
186 // internal references due to compiler error
187 #[allow(clippy::macro_metavars_in_unsafe)]
188 unsafe {
189 cache_push($cache, $key, $val)
190 }
191 }};
192 }
193
194 ///
195 /// Overrides contents in cache at key ```K```. No checks are performed for the existence of the
196 /// key in the cache. Be careful not to override necessary data.
197 ///
198 /// ```
199 /// use rumtk_core::{rumtk_cache_fetch, rumtk_cache_push, rumtk_cache_get};
200 /// use rumtk_core::cache::{new_cache, LazyRUMCache, cache_push, cache_get};
201 /// use std::sync::Arc;
202 ///
203 /// type StringCache = LazyRUMCache<String, Vec<String>>;
204 ///
205 /// fn init_cache(k: &String) -> Vec<String> {
206 /// vec![]
207 /// }
208 ///
209 /// let mut cache: StringCache = new_cache();
210 ///
211 /// let test_key: String = String::from("Hello World");
212 /// let test_value: String = String::from("?????");
213 ///
214 /// rumtk_cache_fetch!(
215 /// &raw mut cache,
216 /// &test_key,
217 /// init_cache
218 /// );
219 ///
220 /// rumtk_cache_push!(
221 /// &raw mut cache,
222 /// &test_key,
223 /// &vec![test_value.clone()]
224 /// );
225 ///
226 /// let v = rumtk_cache_get!(
227 /// &raw mut cache,
228 /// &test_key
229 /// );
230 ///
231 /// 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!");
232 ///
233 ///
234 /// ```
235 ///
236 #[macro_export]
237 macro_rules! rumtk_cache_get {
238 ( $cache:expr, $key:expr ) => {{
239 use $crate::cache::cache_get;
240 // Do not remove the clippy disable decorator here since we do intend to expand within
241 // the unsafe block. Expanding elsewhere prevents us from getting access to the cache's
242 // internal references due to compiler error
243 #[allow(clippy::macro_metavars_in_unsafe)]
244 unsafe {
245 cache_get($cache, $key)
246 }
247 }};
248 }
249}