cljrs_value/collections/
array_map.rs1use std::sync::Arc;
2
3use crate::Value;
4
5pub const THRESHOLD: usize = 8;
7
8#[derive(Debug, Clone)]
14pub struct PersistentArrayMap {
15 entries: Arc<Vec<Value>>,
17}
18
19pub enum AssocResult {
21 Array(PersistentArrayMap),
22 Promote(Vec<(Value, Value)>),
25}
26
27impl PersistentArrayMap {
28 pub fn empty() -> Self {
30 Self {
31 entries: Arc::new(Vec::new()),
32 }
33 }
34
35 pub fn count(&self) -> usize {
36 self.entries.len() / 2
37 }
38
39 pub fn is_empty(&self) -> bool {
40 self.entries.is_empty()
41 }
42
43 pub fn get(&self, key: &Value) -> Option<&Value> {
45 let e = &self.entries;
46 let mut i = 0;
47 while i < e.len() {
48 if &e[i] == key {
49 return Some(&e[i + 1]);
50 }
51 i += 2;
52 }
53 None
54 }
55
56 pub fn contains_key(&self, key: &Value) -> bool {
57 self.get(key).is_some()
58 }
59
60 pub fn assoc(&self, key: Value, value: Value) -> AssocResult {
63 let mut new_entries = (*self.entries).clone();
65 let mut i = 0;
66 while i < new_entries.len() {
67 if new_entries[i] == key {
68 new_entries[i + 1] = value;
69 return AssocResult::Array(Self {
70 entries: Arc::new(new_entries),
71 });
72 }
73 i += 2;
74 }
75
76 new_entries.push(key);
78 new_entries.push(value);
79
80 if new_entries.len() / 2 > THRESHOLD {
81 let mut pairs = Vec::with_capacity(new_entries.len() / 2);
83 let mut j = 0;
84 while j < new_entries.len() {
85 pairs.push((new_entries[j].clone(), new_entries[j + 1].clone()));
86 j += 2;
87 }
88 AssocResult::Promote(pairs)
89 } else {
90 AssocResult::Array(Self {
91 entries: Arc::new(new_entries),
92 })
93 }
94 }
95
96 pub fn dissoc(&self, key: &Value) -> Self {
98 let mut new_entries = (*self.entries).clone();
99 let mut i = 0;
100 while i < new_entries.len() {
101 if &new_entries[i] == key {
102 new_entries.remove(i); new_entries.remove(i); return Self {
105 entries: Arc::new(new_entries),
106 };
107 }
108 i += 2;
109 }
110 self.clone()
112 }
113
114 pub fn iter(&self) -> ArrayMapIter<'_> {
116 ArrayMapIter {
117 entries: &self.entries,
118 pos: 0,
119 }
120 }
121
122 pub fn from_flat_entries(entries: Vec<Value>) -> AssocResult {
128 debug_assert!(entries.len().is_multiple_of(2));
129 if entries.len() / 2 > THRESHOLD {
130 let mut pairs = Vec::with_capacity(entries.len() / 2);
131 let mut i = 0;
132 while i < entries.len() {
133 pairs.push((entries[i].clone(), entries[i + 1].clone()));
134 i += 2;
135 }
136 AssocResult::Promote(pairs)
137 } else {
138 AssocResult::Array(Self {
139 entries: Arc::new(entries),
140 })
141 }
142 }
143
144 pub fn from_pairs<I: IntoIterator<Item = (Value, Value)>>(iter: I) -> AssocResult {
147 let mut map = Self::empty();
148 let mut iter = iter.into_iter();
149 for (k, v) in iter.by_ref() {
150 match map.assoc(k, v) {
151 AssocResult::Array(m) => map = m,
152 AssocResult::Promote(pairs) => {
153 let mut promoted = pairs;
155 for (k2, v2) in iter {
156 if let Some(pos) = promoted.iter().position(|(pk, _)| *pk == k2) {
158 promoted[pos].1 = v2;
159 } else {
160 promoted.push((k2, v2));
161 }
162 }
163 return AssocResult::Promote(promoted);
164 }
165 }
166 }
167 AssocResult::Array(map)
168 }
169}
170
171pub struct ArrayMapIter<'a> {
172 entries: &'a Vec<Value>,
173 pos: usize,
174}
175
176impl<'a> Iterator for ArrayMapIter<'a> {
177 type Item = (&'a Value, &'a Value);
178
179 fn next(&mut self) -> Option<Self::Item> {
180 if self.pos >= self.entries.len() {
181 return None;
182 }
183 let k = &self.entries[self.pos];
184 let v = &self.entries[self.pos + 1];
185 self.pos += 2;
186 Some((k, v))
187 }
188}
189
190impl PartialEq for PersistentArrayMap {
191 fn eq(&self, other: &Self) -> bool {
192 if self.count() != other.count() {
193 return false;
194 }
195 self.iter().all(|(k, v)| other.get(k) == Some(v))
197 }
198}
199
200impl cljrs_gc::Trace for PersistentArrayMap {
201 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
202 for v in self.entries.iter() {
203 v.trace(visitor);
204 }
205 }
206
207 fn gc_size_extra(&self) -> usize {
208 16 + self.entries.capacity() * std::mem::size_of::<Value>()
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216 use crate::Value;
217
218 fn kw(s: &str) -> Value {
219 Value::Keyword(cljrs_gc::GcPtr::new(crate::keyword::Keyword::simple(s)))
220 }
221
222 fn int(n: i64) -> Value {
223 Value::Long(n)
224 }
225
226 #[test]
227 fn test_empty() {
228 let m = PersistentArrayMap::empty();
229 assert!(m.is_empty());
230 assert_eq!(m.count(), 0);
231 }
232
233 #[test]
234 fn test_assoc_and_get() {
235 let m = PersistentArrayMap::empty();
236 let AssocResult::Array(m) = m.assoc(kw("a"), int(1)) else {
237 panic!()
238 };
239 let AssocResult::Array(m) = m.assoc(kw("b"), int(2)) else {
240 panic!()
241 };
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 = PersistentArrayMap::empty();
250 let AssocResult::Array(m) = m.assoc(kw("a"), int(1)) else {
251 panic!()
252 };
253 let AssocResult::Array(m) = m.assoc(kw("a"), int(99)) else {
254 panic!()
255 };
256 assert_eq!(m.get(&kw("a")), Some(&int(99)));
257 assert_eq!(m.count(), 1);
258 }
259
260 #[test]
261 fn test_dissoc() {
262 let m = PersistentArrayMap::empty();
263 let AssocResult::Array(m) = m.assoc(kw("a"), int(1)) else {
264 panic!()
265 };
266 let AssocResult::Array(m) = m.assoc(kw("b"), int(2)) else {
267 panic!()
268 };
269 let m2 = m.dissoc(&kw("a"));
270 assert_eq!(m2.get(&kw("a")), None);
271 assert_eq!(m2.get(&kw("b")), Some(&int(2)));
272 assert_eq!(m2.count(), 1);
273 }
274
275 #[test]
276 fn test_promotes_at_threshold() {
277 let mut m = PersistentArrayMap::empty();
278 for i in 0..THRESHOLD as i64 {
279 let AssocResult::Array(next) = m.assoc(int(i), int(i * 10)) else {
280 panic!()
281 };
282 m = next;
283 }
284 let result = m.assoc(int(THRESHOLD as i64), int(0));
286 assert!(matches!(result, AssocResult::Promote(_)));
287 }
288
289 #[test]
290 fn test_from_pairs_preserves_all_entries_past_threshold() {
291 let pairs: Vec<(Value, Value)> = (0..15i64).map(|i| (int(i), int(i * 10))).collect();
293 let result = PersistentArrayMap::from_pairs(pairs);
294 match result {
295 AssocResult::Promote(pairs) => {
296 assert_eq!(pairs.len(), 15, "all 15 entries must survive promotion");
297 for i in 0..15i64 {
298 let found = pairs.iter().find(|(k, _)| *k == int(i));
299 assert!(found.is_some(), "key {i} missing after promotion");
300 assert_eq!(found.unwrap().1, int(i * 10));
301 }
302 }
303 AssocResult::Array(_) => panic!("expected promotion for 15 entries"),
304 }
305 }
306
307 #[test]
308 fn test_from_pairs_duplicate_keys_after_promotion() {
309 let mut pairs: Vec<(Value, Value)> = (0..10i64).map(|i| (int(i), int(i))).collect();
311 pairs.push((int(0), int(999))); let result = PersistentArrayMap::from_pairs(pairs);
313 match result {
314 AssocResult::Promote(pairs) => {
315 assert_eq!(pairs.len(), 10, "duplicate should not add extra entry");
316 let val = pairs.iter().find(|(k, _)| *k == int(0)).unwrap();
317 assert_eq!(val.1, int(999), "last value should win for duplicate key");
318 }
319 AssocResult::Array(_) => panic!("expected promotion"),
320 }
321 }
322
323 #[test]
324 fn test_equality() {
325 let m1 = PersistentArrayMap::empty();
326 let AssocResult::Array(m1) = m1.assoc(kw("a"), int(1)) else {
327 panic!()
328 };
329 let m2 = PersistentArrayMap::empty();
330 let AssocResult::Array(m2) = m2.assoc(kw("a"), int(1)) else {
331 panic!()
332 };
333 assert_eq!(m1, m2);
334 }
335}