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