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 eprintln!(
78 "WARNING: LrMap::insert failed due to poisoned mutex: {e}. Write dropped."
79 );
80 }
81 }
82 }
83
84 pub fn clear(&self) {
85 match self.writer.lock() {
86 Ok(mut w) => {
87 w.append(MapOp::Clear);
88 w.publish();
89 }
90 Err(e) => {
91 eprintln!(
93 "WARNING: LrMap::clear failed due to poisoned mutex: {e}. Operation dropped."
94 );
95 }
96 }
97 }
98
99 pub fn len(&self) -> usize {
100 let reader = self.reader_factory.handle();
101 reader.enter().map(|m| m.len()).unwrap_or(0)
102 }
103
104 pub fn is_empty(&self) -> bool {
105 self.len() == 0
106 }
107}
108
109impl<K, V> Default for LrMap<K, V>
110where
111 K: Eq + Hash + Clone + Send + Sync,
112 V: Clone + Send + Sync,
113{
114 fn default() -> Self {
115 Self::new()
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use std::sync::Arc;
123
124 #[test]
125 fn insert_and_get() {
126 let map: LrMap<String, i32> = LrMap::new();
127 map.insert("a".into(), 1);
128 assert_eq!(map.get("a"), Some(1));
129 assert_eq!(map.get("b"), None);
130 }
131
132 #[test]
133 fn overwrite_key() {
134 let map: LrMap<String, i32> = LrMap::new();
135 map.insert("a".into(), 1);
136 map.insert("a".into(), 2);
137 assert_eq!(map.get("a"), Some(2));
138 }
139
140 #[test]
141 fn clear_removes_all() {
142 let map: LrMap<String, i32> = LrMap::new();
143 map.insert("a".into(), 1);
144 map.insert("b".into(), 2);
145 map.clear();
146 assert!(map.is_empty());
147 }
148
149 #[test]
150 fn concurrent_reads() {
151 let map: Arc<LrMap<String, i32>> = Arc::new(LrMap::new());
152 map.insert("key".into(), 42);
153
154 let handles: Vec<_> = (0..4)
155 .map(|_| {
156 let m = Arc::clone(&map);
157 std::thread::spawn(move || {
158 for _ in 0..100 {
159 assert_eq!(m.get("key"), Some(42));
160 }
161 })
162 })
163 .collect();
164
165 for h in handles {
166 h.join().expect("reader thread panicked");
167 }
168 }
169}