1use crate::Value;
2use crate::collections::array_map::PersistentArrayMap;
3use crate::collections::share_charge::{ShareCharge, hamt_path_bytes, rb_path_bytes};
4use rpds::{HashTrieMapSync, RedBlackTreeMapSync};
5
6#[derive(Debug, Clone)]
25pub struct PersistentHashMap {
26 index: HashTrieMapSync<Value, u64>,
27 order: RedBlackTreeMapSync<u64, (Value, Value)>,
28 next_seq: u64,
29 charge: ShareCharge,
30}
31
32type OrderEntry = (u64, (Value, Value));
34
35impl PersistentHashMap {
36 pub fn empty() -> Self {
37 Self {
38 index: HashTrieMapSync::new_sync(),
39 next_seq: 0,
40 order: RedBlackTreeMapSync::new_sync(),
41 charge: ShareCharge::full(),
42 }
43 }
44
45 pub fn new(map: HashTrieMapSync<Value, Value>) -> Self {
50 Self::from_pairs(map.iter().map(|(k, v)| (k.clone(), v.clone())))
51 }
52
53 pub fn count(&self) -> usize {
54 self.index.size()
55 }
56
57 pub fn is_empty(&self) -> bool {
58 self.index.is_empty()
59 }
60
61 pub fn get(&self, key: &Value) -> Option<&Value> {
63 let seq = self.index.get(key)?;
64 self.order.get(seq).map(|(_, v)| v)
65 }
66
67 pub fn contains_key(&self, key: &Value) -> bool {
68 self.index.contains_key(key)
69 }
70
71 pub fn assoc(&self, key: Value, value: Value) -> Self {
76 if let Some(seq) = self.index.get(&key) {
77 let seq = *seq;
78 let n = self.count();
79 Self {
80 index: self.index.clone(),
81 order: self.order.insert(seq, (key, value)),
82 next_seq: self.next_seq,
83 charge: self.charge.derive(n, n, rb_path_bytes::<OrderEntry>(n)),
84 }
85 } else {
86 let seq = self.next_seq;
87 let n = self.count();
88 Self {
89 index: self.index.insert(key.clone(), seq),
90 order: self.order.insert(seq, (key, value)),
91 next_seq: seq + 1,
92 charge: self.charge.derive(n, n + 1, Self::path_bytes(n + 1)),
93 }
94 }
95 }
96
97 pub(crate) fn assoc_mut(&mut self, key: Value, value: Value) {
99 if let Some(seq) = self.index.get(&key).copied() {
100 self.order.insert_mut(seq, (key, value));
101 } else {
102 let seq = self.next_seq;
103 self.index.insert_mut(key.clone(), seq);
104 self.order.insert_mut(seq, (key, value));
105 self.next_seq += 1;
106 }
107 self.charge = ShareCharge::full();
108 }
109
110 pub(crate) fn dissoc_mut(&mut self, key: &Value) {
111 if let Some(seq) = self.index.get(key).copied() {
112 self.index.remove_mut(key);
113 self.order.remove_mut(&seq);
114 self.charge = ShareCharge::full();
115 }
116 }
117
118 pub fn dissoc(&self, key: &Value) -> Self {
120 match self.index.get(key) {
121 Some(seq) => {
122 let n = self.count();
123 Self {
124 index: self.index.remove(key),
125 order: self.order.remove(seq),
126 next_seq: self.next_seq,
127 charge: self.charge.derive(n, n - 1, Self::path_bytes(n)),
128 }
129 }
130 None => {
131 let n = self.count();
132 Self {
133 index: self.index.clone(),
134 order: self.order.clone(),
135 next_seq: self.next_seq,
136 charge: self.charge.derive(n, n, 0),
137 }
138 }
139 }
140 }
141
142 fn path_bytes(n: usize) -> usize {
144 hamt_path_bytes::<(Value, u64)>(n) + rb_path_bytes::<OrderEntry>(n)
145 }
146
147 pub(crate) fn full_size_extra(&self) -> usize {
149 self.count() * (88 + 2 * std::mem::size_of::<Value>())
152 }
153
154 #[cfg(test)]
157 fn charged(&self) -> usize {
158 self.charge.size_extra(self.full_size_extra())
159 }
160
161 pub fn iter(&self) -> impl Iterator<Item = (&Value, &Value)> {
163 self.order.iter().map(|(_, (k, v))| (k, v))
164 }
165
166 pub fn keys(&self) -> Vec<Value> {
168 self.iter().map(|(k, _)| k.clone()).collect()
169 }
170
171 pub fn vals(&self) -> Vec<Value> {
173 self.iter().map(|(_, v)| v.clone()).collect()
174 }
175
176 pub fn merge(&self, other: &Self) -> Self {
178 let mut result = self.clone();
179 for (k, v) in other.iter() {
180 result = result.assoc(k.clone(), v.clone());
181 }
182 result
183 }
184
185 pub fn from_pairs<I: IntoIterator<Item = (Value, Value)>>(iter: I) -> Self {
187 let mut m = Self::empty();
188 for (k, v) in iter {
189 m = m.assoc(k, v);
190 }
191 m
192 }
193
194 pub fn from_array_map(am: &PersistentArrayMap) -> Self {
196 Self::from_pairs(am.iter().map(|(k, v)| (k.clone(), v.clone())))
197 }
198}
199
200impl PartialEq for PersistentHashMap {
201 fn eq(&self, other: &Self) -> bool {
202 if self.count() != other.count() {
203 return false;
204 }
205 self.iter().all(|(k, v)| other.get(k) == Some(v))
206 }
207}
208
209impl cljrs_gc::Trace for PersistentHashMap {
210 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
211 for (_, (k, v)) in self.order.iter() {
212 k.trace(visitor);
213 v.trace(visitor);
214 }
215 }
216
217 fn gc_size_extra(&self) -> usize {
218 self.charge.on_alloc(self.full_size_extra())
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225 use crate::Value;
226 use crate::collections::array_map::AssocResult;
227 use cljrs_gc::GcPtr;
228
229 fn kw(s: &str) -> Value {
230 Value::Keyword(GcPtr::new(crate::keyword::Keyword::simple(s)))
231 }
232 fn int(n: i64) -> Value {
233 Value::Long(n)
234 }
235
236 #[test]
237 fn test_basic_ops() {
238 let m = PersistentHashMap::empty();
239 let m = m.assoc(kw("a"), int(1));
240 let m = m.assoc(kw("b"), int(2));
241 assert_eq!(m.count(), 2);
242 assert_eq!(m.get(&kw("a")), Some(&int(1)));
243 assert_eq!(m.get(&kw("b")), Some(&int(2)));
244 assert_eq!(m.get(&kw("c")), None);
245 }
246
247 #[test]
248 fn test_update() {
249 let m = PersistentHashMap::empty()
250 .assoc(kw("a"), int(1))
251 .assoc(kw("a"), int(99));
252 assert_eq!(m.count(), 1);
253 assert_eq!(m.get(&kw("a")), Some(&int(99)));
254 }
255
256 #[test]
257 fn test_dissoc() {
258 let m = PersistentHashMap::empty()
259 .assoc(kw("a"), int(1))
260 .assoc(kw("b"), int(2));
261 let m2 = m.dissoc(&kw("a"));
262 assert_eq!(m2.count(), 1);
263 assert_eq!(m2.get(&kw("a")), None);
264 assert_eq!(m2.get(&kw("b")), Some(&int(2)));
265 }
266
267 #[test]
268 fn test_many_entries() {
269 let mut m = PersistentHashMap::empty();
270 for i in 0i64..200 {
271 m = m.assoc(int(i), int(i * 10));
272 }
273 assert_eq!(m.count(), 200);
274 for i in 0i64..200 {
275 assert_eq!(m.get(&int(i)), Some(&int(i * 10)));
276 }
277 }
278
279 #[test]
280 fn test_merge() {
281 let a = PersistentHashMap::empty()
282 .assoc(kw("a"), int(1))
283 .assoc(kw("b"), int(2));
284 let b = PersistentHashMap::empty()
285 .assoc(kw("b"), int(99))
286 .assoc(kw("c"), int(3));
287 let merged = a.merge(&b);
288 assert_eq!(merged.count(), 3);
289 assert_eq!(merged.get(&kw("a")), Some(&int(1)));
290 assert_eq!(merged.get(&kw("b")), Some(&int(99))); assert_eq!(merged.get(&kw("c")), Some(&int(3)));
292 }
293
294 #[test]
295 fn test_equality() {
296 let a = PersistentHashMap::empty()
297 .assoc(kw("a"), int(1))
298 .assoc(kw("b"), int(2));
299 let b = PersistentHashMap::empty()
300 .assoc(kw("b"), int(2))
301 .assoc(kw("a"), int(1));
302 assert_eq!(a, b);
303 }
304
305 #[test]
306 fn test_from_array_map() {
307 let mut am = PersistentArrayMap::empty();
308 for i in 0..3i64 {
309 let AssocResult::Array(next) = am.assoc(int(i), int(i * 2)) else {
310 panic!()
311 };
312 am = next;
313 }
314 let hm = PersistentHashMap::from_array_map(&am);
315 assert_eq!(hm.count(), 3);
316 for i in 0..3i64 {
317 assert_eq!(hm.get(&int(i)), Some(&int(i * 2)));
318 }
319 }
320
321 #[test]
322 fn test_iteration_order_matches_insertion_order() {
323 let mut m = PersistentHashMap::empty();
324 let keys = ["j", "i", "h", "g", "f", "e", "d", "c", "b", "a", "z", "y"];
325 for k in keys {
326 m = m.assoc(kw(k), int(0));
327 }
328 let iterated: Vec<Value> = m.keys();
329 let expected: Vec<Value> = keys.iter().map(|k| kw(k)).collect();
330 assert_eq!(iterated, expected);
331 }
332
333 #[test]
334 fn test_reassoc_keeps_original_position() {
335 let m = PersistentHashMap::empty()
336 .assoc(kw("a"), int(1))
337 .assoc(kw("b"), int(2))
338 .assoc(kw("c"), int(3))
339 .assoc(kw("b"), int(99));
340 let keys = m.keys();
341 assert_eq!(keys, vec![kw("a"), kw("b"), kw("c")]);
342 assert_eq!(m.get(&kw("b")), Some(&int(99)));
343 }
344
345 #[test]
346 fn test_dissoc_preserves_remaining_order() {
347 let m = PersistentHashMap::empty()
348 .assoc(kw("a"), int(1))
349 .assoc(kw("b"), int(2))
350 .assoc(kw("c"), int(3))
351 .dissoc(&kw("b"));
352 assert_eq!(m.keys(), vec![kw("a"), kw("c")]);
353 }
354
355 #[test]
356 fn assoc_on_allocated_map_charges_path_bytes() {
357 let _alloc = crate::collections::share_charge::alloc_scope();
358 let m = GcPtr::new(PersistentHashMap::from_pairs(
359 (0..10_000).map(|i| (int(i), int(i))),
360 ));
361 let added = m.get().assoc(int(-1), int(0));
362 assert_eq!(added.charged(), PersistentHashMap::path_bytes(10_001));
363 let replaced = m.get().assoc(int(5), int(0));
364 assert_eq!(replaced.charged(), rb_path_bytes::<OrderEntry>(10_000));
365 let removed = m.get().dissoc(&int(5));
366 assert_eq!(removed.charged(), PersistentHashMap::path_bytes(10_000));
367 assert!(removed.charged() * 100 < m.get().full_size_extra());
368 }
369}