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 mod cache_macros {
75 ///
76 /// Searches for item in global cache. If global cache lacks item, create item using factory
77 /// function passed to this macro.
78 ///
79 /// ```
80 /// use rumtk_core::rumtk_cache_fetch;
81 /// use rumtk_core::cache::{new_cache, LazyRUMCache};
82 /// use std::sync::Arc;
83 ///
84 /// type StringCache = LazyRUMCache<String, String>;
85 ///
86 /// fn init_cache(k: &String) -> String {
87 /// String::from(k)
88 /// }
89 ///
90 /// let mut cache: StringCache = new_cache();
91 ///
92 /// let test_key: String = String::from("Hello World");
93 /// let v = rumtk_cache_fetch!(
94 /// &mut cache,
95 /// &test_key,
96 /// init_cache
97 /// );
98 ///
99 /// assert_eq!(test_key.as_str(), v.as_str(), "The inserted key is not the same to what was passed as input!");
100 ///
101 ///
102 /// ```
103 ///
104 #[macro_export]
105 macro_rules! rumtk_cache_fetch {
106 ( $cache:expr, $key:expr, $func:expr ) => {{
107 use $crate::cache::get_or_set_from_cache;
108 // Do not remove the clippy disable decorator here since we do intend to expand within
109 // the unsafe block. Expanding elsewhere prevents us from getting access to the cache's
110 // internal references due to compiler error
111 #[allow(clippy::macro_metavars_in_unsafe)]
112 unsafe {
113 get_or_set_from_cache($cache, $key, $func)
114 }
115 }};
116 }
117
118 ///
119 /// Overrides contents in cache at key ```K```. No checks are performed for the existence of the
120 /// key in the cache. Be careful not to override necessary data.
121 ///
122 /// ```
123 /// use rumtk_core::{rumtk_cache_fetch, rumtk_cache_push};
124 /// use rumtk_core::cache::{new_cache, LazyRUMCache, cache_push};
125 /// use std::sync::Arc;
126 ///
127 /// type StringCache = LazyRUMCache<String, Vec<String>>;
128 ///
129 /// fn init_cache(k: &String) -> Vec<String> {
130 /// vec![]
131 /// }
132 ///
133 /// let mut cache: StringCache = new_cache();
134 ///
135 /// let test_key: String = String::from("Hello World");
136 /// let test_value: String = String::from("?????");
137 ///
138 /// rumtk_cache_fetch!(
139 /// &mut cache,
140 /// &test_key,
141 /// init_cache
142 /// );
143 ///
144 /// let v = rumtk_cache_push!(
145 /// &mut cache,
146 /// &test_key,
147 /// &vec![test_value.clone()]
148 /// );
149 ///
150 /// 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!");
151 ///
152 ///
153 /// ```
154 ///
155 #[macro_export]
156 macro_rules! rumtk_cache_push {
157 ( $cache:expr, $key:expr, $val:expr ) => {{
158 use $crate::cache::cache_push;
159 // Do not remove the clippy disable decorator here since we do intend to expand within
160 // the unsafe block. Expanding elsewhere prevents us from getting access to the cache's
161 // internal references due to compiler error
162 #[allow(clippy::macro_metavars_in_unsafe)]
163 unsafe {
164 cache_push($cache, $key, $val)
165 }
166 }};
167 }
168}