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