1use crate::Value;
2use crate::collections::share_charge::{ShareCharge, vector_path_bytes};
3
4#[derive(Debug, Clone)]
17pub struct PersistentVector {
18 inner: rpds::VectorSync<Value>,
19 is_map_entry: bool,
20 charge: ShareCharge,
21 scalar_only: bool,
23}
24
25pub(crate) fn scalar(value: &Value) -> bool {
26 matches!(
27 value,
28 Value::Nil | Value::Bool(_) | Value::Long(_) | Value::Double(_) | Value::Char(_)
29 )
30}
31
32impl PersistentVector {
33 pub fn empty() -> Self {
34 Self {
35 inner: rpds::VectorSync::new_sync(),
36 is_map_entry: false,
37 charge: ShareCharge::full(),
38 scalar_only: true,
39 }
40 }
41
42 pub fn from_vector(vector: rpds::VectorSync<Value>) -> Self {
43 let scalar_only = vector.iter().all(scalar);
44 Self {
45 scalar_only,
46 inner: vector,
47 is_map_entry: false,
48 charge: ShareCharge::full(),
49 }
50 }
51
52 pub(crate) fn from_vector_summary(vector: rpds::VectorSync<Value>, scalar_only: bool) -> Self {
53 Self {
54 inner: vector,
55 scalar_only,
56 is_map_entry: false,
57 charge: ShareCharge::full(),
58 }
59 }
60
61 pub fn map_entry(key: Value, val: Value) -> Self {
63 let scalar_only = scalar(&key) && scalar(&val);
64 let mut inner = rpds::VectorSync::new_sync();
65 inner = inner.push_back(key);
66 inner = inner.push_back(val);
67 Self {
68 inner,
69 is_map_entry: true,
70 scalar_only,
71 charge: ShareCharge::full(),
72 }
73 }
74
75 pub fn is_map_entry(&self) -> bool {
77 self.is_map_entry
78 }
79
80 pub fn count(&self) -> usize {
81 self.inner.len()
82 }
83
84 pub fn is_empty(&self) -> bool {
85 self.inner.is_empty()
86 }
87
88 pub fn conj(&self, val: Value) -> Self {
90 let scalar_only = self.scalar_only && scalar(&val);
91 let mut result = self.derived(self.inner.push_back(val));
92 result.scalar_only = scalar_only;
93 result
94 }
95
96 pub fn nth(&self, idx: usize) -> Option<&Value> {
98 self.inner.get(idx)
99 }
100
101 pub fn peek(&self) -> Option<&Value> {
103 self.inner.last()
104 }
105
106 pub fn assoc_nth(&self, idx: usize, val: Value) -> Option<Self> {
108 if idx == self.inner.len() {
109 Some(self.conj(val))
110 } else {
111 let scalar_only = self.scalar_only && scalar(&val);
112 let mut result = self.derived(self.inner.set(idx, val)?);
113 result.scalar_only = scalar_only;
114 Some(result)
115 }
116 }
117
118 pub fn pop(&self) -> Option<Self> {
120 Some(self.derived(self.inner.drop_last()?))
121 }
122
123 fn derived(&self, inner: rpds::VectorSync<Value>) -> Self {
125 let len = inner.len().max(self.inner.len());
126 Self {
127 charge: self.charge.derive(
128 self.inner.len(),
129 inner.len(),
130 vector_path_bytes::<Value>(len),
131 ),
132 inner,
133 is_map_entry: false,
134 scalar_only: self.scalar_only,
135 }
136 }
137
138 fn full_size_extra(&self) -> usize {
140 self.inner.len() * (24 + std::mem::size_of::<Value>())
142 }
143
144 #[cfg(test)]
147 fn charged(&self) -> usize {
148 self.charge.size_extra(self.full_size_extra())
149 }
150
151 pub fn iter(&self) -> impl Iterator<Item = &Value> {
153 self.inner.iter()
154 }
155
156 pub(crate) fn scalar_only(&self) -> bool {
157 self.scalar_only
158 }
159
160 pub fn inner(&self) -> &rpds::VectorSync<Value> {
161 &self.inner
162 }
163}
164
165impl std::iter::FromIterator<Value> for PersistentVector {
166 fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
167 let mut v = rpds::VectorSync::new_sync();
168 let mut scalar_only = true;
169 for item in iter {
170 scalar_only &= scalar(&item);
171 v = v.push_back(item);
172 }
173 Self {
174 inner: v,
175 scalar_only,
176 is_map_entry: false,
177 charge: ShareCharge::full(),
178 }
179 }
180}
181
182impl PartialEq for PersistentVector {
183 fn eq(&self, other: &Self) -> bool {
184 if self.inner.len() != other.inner.len() {
185 return false;
186 }
187 self.iter().zip(other.iter()).all(|(a, b)| a == b)
188 }
189}
190
191impl cljrs_gc::Trace for PersistentVector {
192 #[cfg(feature = "no-gc")]
193 fn immutable_children(&self) -> bool {
194 true
195 }
196 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
197 if self.scalar_only {
198 return;
199 }
200 for v in self.inner.iter() {
201 v.trace(visitor);
202 }
203 }
204
205 fn gc_size_extra(&self) -> usize {
206 self.charge.on_alloc(self.full_size_extra())
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213 use crate::Value;
214
215 fn int(n: i64) -> Value {
216 Value::Long(n)
217 }
218
219 #[test]
220 fn test_empty() {
221 let v = PersistentVector::empty();
222 assert!(v.is_empty());
223 assert_eq!(v.count(), 0);
224 assert!(v.nth(0).is_none());
225 }
226
227 #[test]
228 fn test_conj_small() {
229 let v = PersistentVector::from_iter([int(1), int(2), int(3)]);
230 assert_eq!(v.count(), 3);
231 assert_eq!(v.nth(0), Some(&int(1)));
232 assert_eq!(v.nth(2), Some(&int(3)));
233 }
234
235 #[test]
236 fn test_conj_forces_tail_flush() {
237 let v = PersistentVector::from_iter((0..33).map(int));
238 assert_eq!(v.count(), 33);
239 for i in 0..33 {
240 assert_eq!(v.nth(i), Some(&int(i as i64)), "nth({i}) wrong");
241 }
242 }
243
244 #[test]
245 fn test_large() {
246 let n = 1025;
247 let v = PersistentVector::from_iter((0..n).map(|i| int(i as i64)));
248 assert_eq!(v.count(), n);
249 for i in 0..n {
250 assert_eq!(v.nth(i), Some(&int(i as i64)));
251 }
252 }
253
254 #[test]
255 fn test_peek() {
256 let v = PersistentVector::from_iter([int(1), int(2), int(3)]);
257 assert_eq!(v.peek(), Some(&int(3)));
258 }
259
260 #[test]
261 fn test_assoc_nth() {
262 let v = PersistentVector::from_iter([int(1), int(2), int(3)]);
263 let v2 = v.assoc_nth(1, int(99)).unwrap();
264 assert_eq!(v2.nth(0), Some(&int(1)));
265 assert_eq!(v2.nth(1), Some(&int(99)));
266 assert_eq!(v2.nth(2), Some(&int(3)));
267 assert_eq!(v.nth(1), Some(&int(2)));
269 }
270
271 #[test]
272 fn test_pop() {
273 let v = PersistentVector::from_iter([int(1), int(2), int(3)]);
274 let v2 = v.pop().unwrap();
275 assert_eq!(v2.count(), 2);
276 assert_eq!(v2.nth(0), Some(&int(1)));
277 assert_eq!(v2.nth(1), Some(&int(2)));
278 }
279
280 #[test]
281 fn test_equality() {
282 let a = PersistentVector::from_iter([int(1), int(2)]);
283 let b = PersistentVector::from_iter([int(1), int(2)]);
284 let c = PersistentVector::from_iter([int(1), int(3)]);
285 assert_eq!(a, b);
286 assert_ne!(a, c);
287 }
288
289 #[test]
290 fn test_map_entry_flag() {
291 let e = PersistentVector::map_entry(int(1), int(2));
292 assert!(e.is_map_entry());
293 assert_eq!(e.count(), 2);
294 assert_eq!(e.nth(0), Some(&int(1)));
295 assert_eq!(e.nth(1), Some(&int(2)));
296 assert_eq!(e, PersistentVector::from_iter([int(1), int(2)]));
298 assert!(!PersistentVector::from_iter([int(1), int(2)]).is_map_entry());
300 assert!(!e.conj(int(3)).is_map_entry());
302 assert!(!e.assoc_nth(0, int(9)).unwrap().is_map_entry());
303 assert!(!e.pop().unwrap().is_map_entry());
304 }
305
306 #[test]
307 fn test_iter_order() {
308 let v = PersistentVector::from_iter((0..10).map(|i| int(i as i64)));
309 let items: Vec<_> = v.iter().cloned().collect();
310 assert_eq!(items, (0..10).map(|i| int(i as i64)).collect::<Vec<_>>());
311 }
312
313 #[test]
314 fn conj_on_allocated_vector_charges_log_n() {
315 let _alloc = crate::collections::share_charge::alloc_scope();
316 use cljrs_gc::GcPtr;
317
318 let path = vector_path_bytes::<Value>;
319 let mut charges = Vec::new();
320 for n in [1_000usize, 100_000] {
321 let base = PersistentVector::from_iter((0..n as i64).map(int));
322 let full = base.full_size_extra();
323 let mut cur = GcPtr::new(base);
324 assert_eq!(cur.get().charged(), full);
325 for i in 0..500 {
327 let next = GcPtr::new(cur.get().conj(int(i)));
328 let charge = next.get().charged();
329 assert_eq!(charge, path(n + i as usize + 1));
330 assert!(charge * 50 < full, "n={n}: {charge} vs full {full}");
331 cur = next;
332 }
333 charges.push(cur.get().charged());
334 }
335 assert!(charges[1] <= 2 * charges[0], "{charges:?}");
337 }
338
339 #[test]
340 fn assoc_and_pop_on_allocated_vector_charge_log_n() {
341 let _alloc = crate::collections::share_charge::alloc_scope();
342 use cljrs_gc::GcPtr;
343
344 let v = GcPtr::new(PersistentVector::from_iter((0..10_000).map(int)));
345 let set = v.get().assoc_nth(5, int(-1)).unwrap();
346 assert_eq!(set.charged(), vector_path_bytes::<Value>(10_000));
347 let popped = v.get().pop().unwrap();
348 assert_eq!(popped.charged(), vector_path_bytes::<Value>(10_000));
349 }
350
351 #[test]
352 fn conj_chain_recharges_in_full_on_doubling() {
353 let _alloc = crate::collections::share_charge::alloc_scope();
354 use cljrs_gc::GcPtr;
355
356 let mut cur = GcPtr::new(PersistentVector::from_iter((0..100).map(int)));
357 let mut full_at = Vec::new();
358 for i in 100..1_000 {
359 let next = GcPtr::new(cur.get().conj(int(i)));
360 if next.get().charged() == next.get().full_size_extra() {
361 full_at.push(next.get().count());
362 }
363 cur = next;
364 }
365 assert_eq!(full_at, vec![200, 400, 800]);
366 }
367
368 #[test]
369 fn unallocated_builder_chain_is_charged_in_full() {
370 let _alloc = crate::collections::share_charge::alloc_scope();
371 let mut v = PersistentVector::empty();
374 for i in 0..1_000 {
375 v = v.conj(int(i));
376 }
377 assert_eq!(v.charged(), v.full_size_extra());
378 let shared = cljrs_gc::GcPtr::new(v);
380 let copy = shared.get().clone();
381 assert_eq!(copy.charged(), copy.full_size_extra());
382 }
383}