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
208#[cfg(test)]
209mod tests {
210 use super::*;
211 use crate::Value;
212
213 fn kw(s: &str) -> Value {
214 Value::Keyword(cljrs_gc::GcPtr::new(crate::keyword::Keyword::simple(s)))
215 }
216
217 fn int(n: i64) -> Value {
218 Value::Long(n)
219 }
220
221 #[test]
222 fn test_empty() {
223 let m = PersistentArrayMap::empty();
224 assert!(m.is_empty());
225 assert_eq!(m.count(), 0);
226 }
227
228 #[test]
229 fn test_assoc_and_get() {
230 let m = PersistentArrayMap::empty();
231 let AssocResult::Array(m) = m.assoc(kw("a"), int(1)) else {
232 panic!()
233 };
234 let AssocResult::Array(m) = m.assoc(kw("b"), int(2)) else {
235 panic!()
236 };
237 assert_eq!(m.get(&kw("a")), Some(&int(1)));
238 assert_eq!(m.get(&kw("b")), Some(&int(2)));
239 assert_eq!(m.get(&kw("c")), None);
240 }
241
242 #[test]
243 fn test_update() {
244 let m = PersistentArrayMap::empty();
245 let AssocResult::Array(m) = m.assoc(kw("a"), int(1)) else {
246 panic!()
247 };
248 let AssocResult::Array(m) = m.assoc(kw("a"), int(99)) else {
249 panic!()
250 };
251 assert_eq!(m.get(&kw("a")), Some(&int(99)));
252 assert_eq!(m.count(), 1);
253 }
254
255 #[test]
256 fn test_dissoc() {
257 let m = PersistentArrayMap::empty();
258 let AssocResult::Array(m) = m.assoc(kw("a"), int(1)) else {
259 panic!()
260 };
261 let AssocResult::Array(m) = m.assoc(kw("b"), int(2)) else {
262 panic!()
263 };
264 let m2 = m.dissoc(&kw("a"));
265 assert_eq!(m2.get(&kw("a")), None);
266 assert_eq!(m2.get(&kw("b")), Some(&int(2)));
267 assert_eq!(m2.count(), 1);
268 }
269
270 #[test]
271 fn test_promotes_at_threshold() {
272 let mut m = PersistentArrayMap::empty();
273 for i in 0..THRESHOLD as i64 {
274 let AssocResult::Array(next) = m.assoc(int(i), int(i * 10)) else {
275 panic!()
276 };
277 m = next;
278 }
279 let result = m.assoc(int(THRESHOLD as i64), int(0));
281 assert!(matches!(result, AssocResult::Promote(_)));
282 }
283
284 #[test]
285 fn test_from_pairs_preserves_all_entries_past_threshold() {
286 let pairs: Vec<(Value, Value)> = (0..15i64).map(|i| (int(i), int(i * 10))).collect();
288 let result = PersistentArrayMap::from_pairs(pairs);
289 match result {
290 AssocResult::Promote(pairs) => {
291 assert_eq!(pairs.len(), 15, "all 15 entries must survive promotion");
292 for i in 0..15i64 {
293 let found = pairs.iter().find(|(k, _)| *k == int(i));
294 assert!(found.is_some(), "key {i} missing after promotion");
295 assert_eq!(found.unwrap().1, int(i * 10));
296 }
297 }
298 AssocResult::Array(_) => panic!("expected promotion for 15 entries"),
299 }
300 }
301
302 #[test]
303 fn test_from_pairs_duplicate_keys_after_promotion() {
304 let mut pairs: Vec<(Value, Value)> = (0..10i64).map(|i| (int(i), int(i))).collect();
306 pairs.push((int(0), int(999))); let result = PersistentArrayMap::from_pairs(pairs);
308 match result {
309 AssocResult::Promote(pairs) => {
310 assert_eq!(pairs.len(), 10, "duplicate should not add extra entry");
311 let val = pairs.iter().find(|(k, _)| *k == int(0)).unwrap();
312 assert_eq!(val.1, int(999), "last value should win for duplicate key");
313 }
314 AssocResult::Array(_) => panic!("expected promotion"),
315 }
316 }
317
318 #[test]
319 fn test_equality() {
320 let m1 = PersistentArrayMap::empty();
321 let AssocResult::Array(m1) = m1.assoc(kw("a"), int(1)) else {
322 panic!()
323 };
324 let m2 = PersistentArrayMap::empty();
325 let AssocResult::Array(m2) = m2.assoc(kw("a"), int(1)) else {
326 panic!()
327 };
328 assert_eq!(m1, m2);
329 }
330}