1use hashbrown::HashMap;
10use left_right::{Absorb, ReadHandleFactory, WriteHandle};
11use std::hash::Hash;
12use std::sync::Mutex;
13
14enum MapOp<K, V> {
15 Insert(K, V),
16 Clear,
17}
18
19impl<K: Eq + Hash + Clone, V: Clone> Absorb<MapOp<K, V>> for HashMap<K, V> {
20 fn absorb_first(&mut self, operation: &mut MapOp<K, V>, _other: &Self) {
21 match operation {
22 MapOp::Insert(k, v) => {
23 self.insert(k.clone(), v.clone());
24 }
25 MapOp::Clear => self.clear(),
26 }
27 }
28
29 fn sync_with(&mut self, first: &Self) {
30 self.clone_from(first);
31 }
32}
33
34pub struct LrMap<K: Eq + Hash + Clone, V: Clone> {
41 reader_factory: ReadHandleFactory<HashMap<K, V>>,
42 writer: Mutex<WriteHandle<HashMap<K, V>, MapOp<K, V>>>,
43}
44
45impl<K, V> LrMap<K, V>
46where
47 K: Eq + Hash + Clone + Send + Sync,
48 V: Clone + Send + Sync,
49{
50 pub fn new() -> Self {
51 let (writer, reader) = left_right::new_from_empty(HashMap::new());
52 let factory = reader.factory();
53 Self {
54 reader_factory: factory,
55 writer: Mutex::new(writer),
56 }
57 }
58
59 pub fn get<Q>(&self, key: &Q) -> Option<V>
61 where
62 K: std::borrow::Borrow<Q>,
63 Q: Hash + Eq + ?Sized,
64 {
65 let reader = self.reader_factory.handle();
66 reader.enter().and_then(|map| map.get(key).cloned())
67 }
68
69 pub fn insert(&self, key: K, value: V) {
70 match self.writer.lock() {
71 Ok(mut w) => {
72 w.append(MapOp::Insert(key, value));
73 w.publish();
74 }
75 Err(e) => {
76 tracing::warn!("LrMap::insert failed due to poisoned mutex: {e}. Write dropped.");
77 }
78 }
79 }
80
81 pub fn clear(&self) {
82 match self.writer.lock() {
83 Ok(mut w) => {
84 w.append(MapOp::Clear);
85 w.publish();
86 }
87 Err(e) => {
88 tracing::warn!(
89 "LrMap::clear failed due to poisoned mutex: {e}. Operation dropped."
90 );
91 }
92 }
93 }
94
95 pub fn len(&self) -> usize {
96 let reader = self.reader_factory.handle();
97 reader.enter().map(|m| m.len()).unwrap_or(0)
98 }
99
100 pub fn is_empty(&self) -> bool {
101 self.len() == 0
102 }
103}
104
105impl<K, V> Default for LrMap<K, V>
106where
107 K: Eq + Hash + Clone + Send + Sync,
108 V: Clone + Send + Sync,
109{
110 fn default() -> Self {
111 Self::new()
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use std::sync::Arc;
119
120 #[test]
121 fn insert_and_get() {
122 let map: LrMap<String, i32> = LrMap::new();
123 map.insert("a".into(), 1);
124 assert_eq!(map.get("a"), Some(1));
125 assert_eq!(map.get("b"), None);
126 }
127
128 #[test]
129 fn overwrite_key() {
130 let map: LrMap<String, i32> = LrMap::new();
131 map.insert("a".into(), 1);
132 map.insert("a".into(), 2);
133 assert_eq!(map.get("a"), Some(2));
134 }
135
136 #[test]
137 fn clear_removes_all() {
138 let map: LrMap<String, i32> = LrMap::new();
139 map.insert("a".into(), 1);
140 map.insert("b".into(), 2);
141 map.clear();
142 assert!(map.is_empty());
143 }
144
145 #[test]
146 fn concurrent_reads() {
147 let map: Arc<LrMap<String, i32>> = Arc::new(LrMap::new());
148 map.insert("key".into(), 42);
149
150 let handles: Vec<_> = (0..4)
151 .map(|_| {
152 let m = Arc::clone(&map);
153 std::thread::spawn(move || {
154 for _ in 0..100 {
155 assert_eq!(m.get("key"), Some(42));
156 }
157 })
158 })
159 .collect();
160
161 for h in handles {
162 h.join().expect("reader thread panicked");
163 }
164 }
165}