1use crate::Error;
10use crate::Reset;
11use std::vec::Vec;
12
13#[inline]
14fn word_bit(i: usize) -> (usize, usize) {
15 (i / 64, i % 64)
16}
17
18pub struct Vector<V> {
48 data: Vec<V>,
49 dirty: Vec<u64>,
51 valid: Vec<u64>,
53 dirty_count: usize,
56}
57
58impl<V> Default for Vector<V> {
59 fn default() -> Self {
60 Self::new()
61 }
62}
63
64impl<V> Vector<V> {
65 pub fn new() -> Self {
67 Self {
68 data: Vec::new(),
69 dirty: Vec::new(),
70 valid: Vec::new(),
71 dirty_count: 0,
72 }
73 }
74
75 pub fn with_capacity(capacity: usize) -> Self {
77 let words = capacity.div_ceil(64);
78 Self {
79 data: Vec::with_capacity(capacity),
80 dirty: Vec::with_capacity(words),
81 valid: Vec::with_capacity(words),
82 dirty_count: 0,
83 }
84 }
85
86 pub fn from_vec(data: Vec<V>) -> Self {
93 let len = data.len();
94 let words = len.div_ceil(64);
95 let mut valid = vec![0u64; words];
96 let full_words = len / 64;
98 let rem = len % 64;
99 for w in &mut valid[..full_words] {
100 *w = u64::MAX;
101 }
102 if rem != 0 {
103 valid[full_words] = (1u64 << rem) - 1;
104 }
105 Self {
106 data,
107 dirty: vec![0u64; words],
108 valid,
109 dirty_count: 0,
110 }
111 }
112
113 pub fn from_fill(value: V, len: usize) -> Self
117 where
118 V: Clone,
119 {
120 Self::from_vec(vec![value; len])
121 }
122
123 pub fn with_invalid_slots(len: usize) -> Self
135 where
136 V: Default,
137 {
138 let words = len.div_ceil(64);
139 let mut data = Vec::with_capacity(len);
140 data.resize_with(len, V::default);
141 Self {
142 data,
143 dirty: vec![0u64; words],
144 valid: vec![0u64; words],
145 dirty_count: 0,
146 }
147 }
148
149 #[inline]
154 pub fn as_slice(&self) -> &[V] {
155 &self.data
156 }
157
158 #[inline]
160 pub fn is_updated(&self) -> bool {
161 self.dirty_count != 0
162 }
163
164 #[inline]
167 pub fn all_updated(&self) -> bool {
168 !self.data.is_empty() && self.dirty_count == self.data.len()
169 }
170
171 pub fn clear(&mut self) {
173 self.data.clear();
174 self.dirty.clear();
175 self.valid.clear();
176 self.dirty_count = 0;
177 }
178
179 pub fn len(&self) -> usize {
181 self.data.len()
182 }
183
184 pub fn is_empty(&self) -> bool {
186 self.data.is_empty()
187 }
188
189 pub fn iter(&self) -> std::slice::Iter<'_, V> {
191 self.data.iter()
192 }
193
194 pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, V> {
197 let len = self.data.len();
198 if self.dirty_count < len {
199 let full_words = len / 64;
200 let rem = len % 64;
201 for w in 0..full_words {
202 self.dirty[w] = u64::MAX;
203 }
204 if rem != 0 {
205 self.dirty[full_words] = (1u64 << rem) - 1;
206 }
207 self.dirty_count = len;
208 }
209 self.data.iter_mut()
210 }
211
212 #[inline]
216 fn mark_dirty_internal(&mut self, index: usize) {
217 let (w, b) = word_bit(index);
218 let mask = 1u64 << b;
219 if (self.dirty[w] & mask) == 0 {
220 self.dirty[w] |= mask;
221 self.dirty_count += 1;
222 }
223 }
224
225 #[inline]
227 fn set_valid_internal(&mut self, index: usize, value: bool) {
228 let (w, b) = word_bit(index);
229 let mask = 1u64 << b;
230 if value {
231 self.valid[w] |= mask;
232 } else {
233 self.valid[w] &= !mask;
234 }
235 }
236
237 #[inline]
241 fn extend_bitsets_for_last_push(&mut self) {
242 let new_idx = self.data.len() - 1;
243 if new_idx.is_multiple_of(64) {
244 self.dirty.push(0);
245 self.valid.push(0);
246 }
247 }
248
249 #[inline]
256 pub fn get_mut_untracked(&mut self, index: usize) -> Option<&mut V> {
257 self.data.get_mut(index)
258 }
259
260 pub fn push(&mut self, value: V) {
268 self.data.push(value);
269 self.extend_bitsets_for_last_push();
270 let idx = self.data.len() - 1;
271 self.mark_dirty_internal(idx);
273 }
274
275 pub fn push_committed(&mut self, value: V) {
282 self.data.push(value);
283 self.extend_bitsets_for_last_push();
284 let idx = self.data.len() - 1;
285 self.set_valid_internal(idx, true);
286 self.mark_dirty_internal(idx);
287 }
288
289 pub fn pop(&mut self) -> Option<V> {
291 if self.data.is_empty() {
292 return None;
293 }
294 let idx = self.data.len() - 1;
295 let (w, b) = word_bit(idx);
296 let mask = 1u64 << b;
297 if (self.dirty[w] & mask) != 0 {
299 self.dirty_count -= 1;
300 }
301 self.dirty[w] &= !mask;
303 self.valid[w] &= !mask;
304 if idx.is_multiple_of(64) {
306 self.dirty.pop();
307 self.valid.pop();
308 }
309 self.data.pop()
310 }
311
312 #[inline]
320 pub fn is_valid(&self, index: usize) -> bool {
321 if index >= self.data.len() {
322 return false;
323 }
324 let (w, b) = word_bit(index);
325 (self.valid[w] & (1u64 << b)) != 0
326 }
327
328 #[inline]
340 pub fn is_updated_at(&self, index: usize) -> bool {
341 if index >= self.data.len() {
342 return false;
343 }
344 let (w, b) = word_bit(index);
345 (self.dirty[w] & (1u64 << b)) != 0
346 }
347
348 #[inline]
353 pub fn get_valid(&self, index: usize) -> Option<&V> {
354 if self.is_valid(index) {
355 self.data.get(index)
356 } else {
357 None
358 }
359 }
360
361 pub fn commit(&mut self, index: usize, value: V) {
364 assert!(
365 index < self.data.len(),
366 "Vector::commit: index {} out of bounds (len = {})",
367 index,
368 self.data.len()
369 );
370 self.data[index] = value;
371 self.set_valid_internal(index, true);
372 self.mark_dirty_internal(index);
373 }
374
375 pub fn update<F>(&mut self, index: usize, f: F)
393 where
394 F: FnOnce(&mut V),
395 {
396 assert!(
397 index < self.data.len(),
398 "Vector::update: index {} out of bounds (len = {})",
399 index,
400 self.data.len()
401 );
402 f(&mut self.data[index]);
403 self.set_valid_internal(index, true);
404 self.mark_dirty_internal(index);
405 }
406
407 pub fn invalidate(&mut self, index: usize) {
411 assert!(
412 index < self.data.len(),
413 "Vector::invalidate: index {} out of bounds (len = {})",
414 index,
415 self.data.len()
416 );
417 self.set_valid_internal(index, false);
418 self.mark_dirty_internal(index);
419 }
420
421 #[must_use = "the returned SlotWriter must be consumed via commit() or invalidate()"]
429 pub fn slot_writer(&mut self, index: usize) -> SlotWriter<'_, V> {
430 assert!(
431 index < self.data.len(),
432 "Vector::slot_writer: index {} out of bounds (len = {})",
433 index,
434 self.data.len()
435 );
436 SlotWriter {
437 vector: self,
438 index,
439 consumed: false,
440 }
441 }
442}
443
444impl<V> Reset for Vector<V> {
445 type Error = Error;
450 fn reset(&mut self) -> Result<(), Error> {
451 for w in &mut self.dirty {
452 *w = 0;
453 }
454 self.dirty_count = 0;
455 Ok(())
456 }
457}
458
459impl<V> crate::Updated for Vector<V> {
460 fn is_updated(&self) -> bool {
461 self.is_updated() }
463}
464
465#[must_use = "SlotWriter must be consumed via commit() or invalidate()"]
478pub struct SlotWriter<'a, V> {
479 vector: &'a mut Vector<V>,
480 index: usize,
481 consumed: bool,
482}
483
484impl<'a, V> SlotWriter<'a, V> {
485 pub fn commit(mut self, value: V) {
487 self.vector.commit(self.index, value);
488 self.consumed = true;
489 }
490
491 pub fn invalidate(mut self) {
495 self.vector.invalidate(self.index);
496 self.consumed = true;
497 }
498}
499
500impl<'a, V> Drop for SlotWriter<'a, V> {
501 fn drop(&mut self) {
502 if !self.consumed {
503 debug_assert!(
504 false,
505 "SlotWriter at index {} dropped without commit() or invalidate()",
506 self.index
507 );
508 }
509 }
510}
511
512impl<V> Vector<V> {
513 pub fn iter_updated_valid(&self) -> IterUpdatedValidItems<'_, V> {
517 IterUpdatedValidItems::new(self)
518 }
519
520 pub fn iter_updated_indices(&self) -> IterUpdatedIndices<'_> {
526 IterUpdatedIndices::new(&self.dirty, self.data.len())
527 }
528}
529
530pub struct IterUpdatedValidItems<'a, V> {
534 vector: &'a Vector<V>,
535 word_idx: usize,
536 word: u64,
537}
538
539impl<'a, V> IterUpdatedValidItems<'a, V> {
540 fn new(v: &'a Vector<V>) -> Self {
541 let mut it = Self {
542 vector: v,
543 word_idx: 0,
544 word: 0,
545 };
546 it.advance_to_nonzero();
547 it
548 }
549
550 fn advance_to_nonzero(&mut self) {
551 while self.word == 0 && self.word_idx < self.vector.dirty.len() {
552 let mut w = self.vector.dirty[self.word_idx];
553 if self.word_idx + 1 == self.vector.dirty.len() {
556 let rem = self.vector.data.len() % 64;
557 if rem != 0 {
558 w &= (1u64 << rem) - 1;
559 }
560 }
561 self.word = w;
562 if self.word != 0 {
563 break;
564 }
565 self.word_idx += 1;
566 }
567 }
568}
569
570impl<'a, V> Iterator for IterUpdatedValidItems<'a, V> {
571 type Item = (usize, Option<&'a V>);
572
573 fn next(&mut self) -> Option<Self::Item> {
574 if self.word == 0 {
575 self.advance_to_nonzero();
576 if self.word == 0 {
577 return None;
578 }
579 }
580 let tz = self.word.trailing_zeros() as usize;
581 let idx = self.word_idx * 64 + tz;
582 self.word &= self.word - 1;
584 let opt = if self.vector.is_valid(idx) {
585 Some(&self.vector.data[idx])
586 } else {
587 None
588 };
589 if self.word == 0 {
590 self.word_idx += 1;
591 }
592 Some((idx, opt))
593 }
594}
595
596pub struct IterUpdatedIndices<'a> {
600 dirty: &'a [u64],
601 total_len: usize,
602 word_idx: usize,
603 word: u64,
604}
605
606impl<'a> IterUpdatedIndices<'a> {
607 fn new(dirty: &'a [u64], total_len: usize) -> Self {
608 let mut it = Self {
609 dirty,
610 total_len,
611 word_idx: 0,
612 word: 0,
613 };
614 it.advance_to_nonzero();
615 it
616 }
617
618 fn advance_to_nonzero(&mut self) {
619 while self.word == 0 && self.word_idx < self.dirty.len() {
620 let mut w = self.dirty[self.word_idx];
621 if self.word_idx + 1 == self.dirty.len() {
622 let rem = self.total_len % 64;
623 if rem != 0 {
624 w &= (1u64 << rem) - 1;
625 }
626 }
627 self.word = w;
628 if self.word != 0 {
629 break;
630 }
631 self.word_idx += 1;
632 }
633 }
634}
635
636impl<'a> Iterator for IterUpdatedIndices<'a> {
637 type Item = usize;
638
639 fn next(&mut self) -> Option<Self::Item> {
640 if self.word == 0 {
641 self.advance_to_nonzero();
642 if self.word == 0 {
643 return None;
644 }
645 }
646 let tz = self.word.trailing_zeros() as usize;
647 let idx = self.word_idx * 64 + tz;
648 self.word &= self.word - 1;
649 if self.word == 0 {
650 self.word_idx += 1;
651 }
652 Some(idx)
653 }
654}
655
656#[cfg(test)]
657mod tests {
658 use super::*;
659
660 #[test]
661 fn test_new_vector() {
662 let vec: Vector<i32> = Vector::new();
663 assert_eq!(vec.len(), 0);
664 assert!(vec.is_empty());
665 assert!(!vec.is_updated());
666 assert!(!vec.all_updated());
667 }
668
669 #[test]
670 fn test_with_capacity() {
671 let mut vec: Vector<i32> = Vector::with_capacity(10);
672 assert_eq!(vec.len(), 0);
673 assert_eq!(vec.data.capacity(), 10);
674 assert!(vec.dirty.capacity() >= 1);
677 assert!(vec.valid.capacity() >= 1);
678 assert_eq!(vec.dirty.len(), 0);
679 assert_eq!(vec.valid.len(), 0);
680 assert!(!vec.is_valid(0));
681
682 vec.push_committed(42);
683 assert_eq!(vec.get_valid(0), Some(&42));
684
685 assert_eq!(vec.pop(), Some(42));
686 assert_eq!(vec.len(), 0);
687 assert!(!vec.is_valid(0));
688 }
689
690 #[test]
691 fn get_mut_untracked_does_not_mark_updated() -> Result<(), Error> {
692 let mut vec = Vector::new();
693 vec.push_committed(String::from("a"));
694 vec.push_committed(String::from("b"));
695 vec.reset()?;
696
697 if let Some(value) = vec.get_mut_untracked(1) {
698 value.clear();
699 }
700
701 assert_eq!(vec.get_valid(1).map(String::as_str), Some(""));
702 assert!(!vec.is_updated());
703 assert!(!vec.all_updated());
704 assert!(vec.iter_updated_valid().next().is_none());
705 Ok(())
706 }
707
708 #[test]
709 fn reset_clears_dirty_preserves_data() -> Result<(), Error> {
710 let mut vec = Vector::new();
711 vec.push_committed(1);
712 vec.push_committed(2);
713 vec.push_committed(3);
714
715 assert!(vec.is_updated());
716
717 vec.reset()?;
718 assert!(!vec.is_updated());
719 assert!(!vec.all_updated());
720 assert_eq!(vec.iter_updated_valid().count(), 0);
721 assert_eq!(vec.get_valid(0), Some(&1));
723 assert_eq!(vec.get_valid(1), Some(&2));
724 assert_eq!(vec.get_valid(2), Some(&3));
725
726 vec.commit(1, 20);
727 assert!(vec.is_updated());
728 assert_eq!(vec.get_valid(1), Some(&20));
729 Ok(())
730 }
731
732 #[test]
733 fn clear_drops_all_state() {
734 let mut vec = Vector::new();
735 vec.push_committed(1);
736 vec.push_committed(2);
737 vec.push_committed(3);
738
739 vec.clear();
740 assert_eq!(vec.len(), 0);
741 assert!(vec.is_empty());
742 assert!(!vec.is_updated());
743 assert!(!vec.all_updated());
744 }
745
746 #[test]
747 fn pop_drops_last_slot_and_its_bits() {
748 let mut vec = Vector::new();
749 vec.push_committed(10);
750 vec.push_committed(20);
751 vec.push_committed(30);
752
753 let val = vec.pop();
754 assert_eq!(val, Some(30));
755 assert_eq!(vec.len(), 2);
756 assert!(vec.is_updated());
757
758 vec.pop();
759 vec.pop();
760 assert!(vec.is_empty());
761 assert!(!vec.is_updated());
762 }
763
764 #[test]
765 fn out_of_bounds_returns_none() {
766 let mut vec = Vector::new();
767 vec.push_committed(1);
768
769 assert_eq!(vec.get_valid(1), None);
770 assert!(!vec.is_valid(1));
771 }
772
773 #[test]
774 fn iter_and_iter_mut_walk_raw_data() {
775 let mut vec = Vector::new();
776 vec.push_committed(1);
777 vec.push_committed(2);
778 vec.push_committed(3);
779
780 let collected: Vec<&i32> = vec.iter().collect();
781 assert_eq!(collected, vec![&1, &2, &3]);
782
783 for val in vec.iter_mut() {
784 *val *= 2;
785 }
786
787 let collected: Vec<&i32> = vec.iter().collect();
788 assert_eq!(collected, vec![&2, &4, &6]);
789 }
790
791 #[test]
792 fn push_after_all_updated_extends_correctly() {
793 let mut vec = Vector::new();
794 vec.push_committed(1);
795 vec.push_committed(2);
796
797 vec.push(3);
799 assert!(vec.is_updated());
800
801 let updated: Vec<(usize, Option<i32>)> = vec
802 .iter_updated_valid()
803 .map(|(i, v)| (i, v.copied()))
804 .collect();
805 assert_eq!(updated, vec![(0, Some(1)), (1, Some(2)), (2, None)]);
806 }
807
808 #[test]
809 fn commit_marks_all_slots_dirty() {
810 let mut vec = Vector::new();
811 vec.push_committed(1);
812 vec.push_committed(2);
813 vec.push_committed(3);
814
815 vec.commit(0, 10);
816 vec.commit(1, 20);
817 vec.commit(2, 30);
818
819 let updated: Vec<(usize, i32)> = vec
820 .iter_updated_valid()
821 .map(|(i, v)| (i, *v.unwrap()))
822 .collect();
823 assert_eq!(updated, vec![(0, 10), (1, 20), (2, 30)]);
824 }
825
826 #[test]
827 fn reset_after_committed_pushes_keeps_validity() -> Result<(), Error> {
828 let mut vec = Vector::new();
829 vec.push_committed(1);
830 vec.push_committed(2);
831
832 vec.reset()?;
833 assert!(!vec.is_updated());
834 assert_eq!(vec.iter_updated_valid().count(), 0);
835 assert_eq!(vec.get_valid(0), Some(&1));
836 assert_eq!(vec.get_valid(1), Some(&2));
837
838 vec.commit(0, 10);
839 let updated: Vec<(usize, i32)> = vec
840 .iter_updated_valid()
841 .map(|(i, v)| (i, *v.unwrap()))
842 .collect();
843 assert_eq!(updated, vec![(0, 10)]);
844 Ok(())
845 }
846
847 #[test]
848 fn push_committed_reads_back_via_get_valid() {
849 let mut vec = Vector::new();
850 vec.push_committed(1);
851 vec.push_committed(2);
852 vec.push_committed(3);
853
854 assert_eq!(vec.len(), 3);
855 assert_eq!(vec.get_valid(0), Some(&1));
856 assert_eq!(vec.get_valid(1), Some(&2));
857 assert_eq!(vec.get_valid(2), Some(&3));
858 assert_eq!(vec.get_valid(3), None);
859
860 assert!(vec.is_updated());
861 }
862
863 #[test]
868 fn validity_push_is_invalid_by_default() {
869 let mut vec = Vector::new();
870 vec.push(42);
871 assert!(!vec.is_valid(0));
872 assert_eq!(vec.get_valid(0), None);
873 assert!(!vec.is_valid(1));
875 assert_eq!(vec.get_valid(1), None);
876 }
877
878 #[test]
879 fn validity_push_committed_is_valid() {
880 let mut vec = Vector::new();
881 vec.push_committed(42);
882 assert!(vec.is_valid(0));
883 assert_eq!(vec.get_valid(0), Some(&42));
884 assert!(vec.is_updated());
885 }
886
887 #[test]
888 fn validity_commit_roundtrip() -> Result<(), Error> {
889 let mut vec = Vector::new();
890 vec.push(0); assert!(!vec.is_valid(0));
892
893 vec.commit(0, 42);
894 assert!(vec.is_valid(0));
895 assert_eq!(vec.get_valid(0), Some(&42));
896 assert!(vec.is_updated());
897
898 vec.reset()?;
899 assert!(!vec.is_updated());
901 assert!(vec.is_valid(0));
902 assert_eq!(vec.get_valid(0), Some(&42));
903 Ok(())
904 }
905
906 #[test]
907 fn validity_invalidate_marks_dirty_and_clears_valid() -> Result<(), Error> {
908 let mut vec = Vector::new();
909 vec.push_committed(42);
910 vec.reset()?;
911 assert!(!vec.is_updated());
912
913 vec.invalidate(0);
914 assert!(!vec.is_valid(0));
915 assert_eq!(vec.get_valid(0), None);
916 assert!(vec.is_updated());
919 Ok(())
920 }
921
922 #[test]
923 fn validity_reset_preserves_validity_clears_dirty() -> Result<(), Error> {
924 let mut vec = Vector::new();
925 vec.push(0);
926 vec.commit(0, 100);
927 vec.reset()?;
928 assert!(!vec.is_updated());
929 assert!(vec.is_valid(0)); assert_eq!(vec.get_valid(0), Some(&100));
931 Ok(())
932 }
933
934 #[test]
935 fn validity_slot_writer_commit_consumes_without_panic() {
936 let mut vec = Vector::new();
937 vec.push(0);
938 let writer = vec.slot_writer(0);
939 writer.commit(99);
940 assert_eq!(vec.get_valid(0), Some(&99));
941 }
942
943 #[test]
944 fn validity_slot_writer_invalidate_consumes_without_panic() {
945 let mut vec = Vector::new();
946 vec.push_committed(123);
947 let writer = vec.slot_writer(0);
948 writer.invalidate();
949 assert_eq!(vec.get_valid(0), None);
950 assert!(vec.is_updated());
951 }
952
953 #[test]
954 #[cfg(debug_assertions)]
955 #[should_panic(expected = "SlotWriter at index 0 dropped without commit() or invalidate()")]
956 fn validity_slot_writer_drop_without_consume_panics_in_debug() {
957 let mut vec: Vector<i32> = Vector::new();
958 vec.push(0);
959 let _writer = vec.slot_writer(0);
960 }
962
963 #[test]
964 fn validity_iter_updated_valid_yields_option_per_slot() -> Result<(), Error> {
965 let mut vec = Vector::new();
966 vec.push(0); vec.push(0); vec.push(0); vec.commit(0, 10);
970 vec.commit(1, 20);
971 let collected: Vec<(usize, Option<i32>)> = vec
974 .iter_updated_valid()
975 .map(|(i, v)| (i, v.copied()))
976 .collect();
977 assert_eq!(
978 collected,
979 vec![(0, Some(10)), (1, Some(20)), (2, None)],
980 "all three slots are dirty; only the two committed ones expose Some(v)"
981 );
982
983 vec.reset()?;
984 let collected: Vec<(usize, Option<i32>)> = vec
985 .iter_updated_valid()
986 .map(|(i, v)| (i, v.copied()))
987 .collect();
988 assert!(collected.is_empty(), "after reset no slots are dirty");
989
990 vec.invalidate(0);
992 let collected: Vec<(usize, Option<i32>)> = vec
993 .iter_updated_valid()
994 .map(|(i, v)| (i, v.copied()))
995 .collect();
996 assert_eq!(collected, vec![(0, None)]);
997 Ok(())
998 }
999
1000 #[test]
1001 fn update_mutates_in_place_and_marks_valid_dirty() -> Result<(), Error> {
1002 let mut vec = Vector::new();
1006 vec.push_committed(vec![1, 2, 3]);
1007 vec.reset()?;
1008 assert!(!vec.is_updated());
1009
1010 let mut prior = None;
1011 vec.update(0, |v| {
1012 prior = Some(v.clone());
1013 v.push(4);
1014 });
1015
1016 assert_eq!(prior, Some(vec![1, 2, 3]));
1017 assert!(vec.is_valid(0));
1018 assert!(vec.is_updated());
1019 assert_eq!(vec.get_valid(0), Some(&vec![1, 2, 3, 4]));
1020 Ok(())
1021 }
1022
1023 #[test]
1024 fn update_on_invalid_slot_makes_it_valid() {
1025 let mut vec = Vector::new();
1029 vec.push(99); assert!(!vec.is_valid(0));
1031
1032 let mut seen_placeholder = None;
1033 vec.update(0, |v| {
1034 seen_placeholder = Some(*v);
1035 *v = 42;
1036 });
1037
1038 assert_eq!(seen_placeholder, Some(99));
1039 assert!(vec.is_valid(0));
1040 assert_eq!(vec.get_valid(0), Some(&42));
1041 }
1042
1043 #[test]
1044 fn update_redirties_after_reset() -> Result<(), Error> {
1045 let mut vec = Vector::new();
1046 vec.push_committed(10);
1047 vec.reset()?;
1048 assert!(!vec.is_updated());
1049
1050 vec.update(0, |v| *v += 5);
1051 assert!(vec.is_updated());
1052 let collected: Vec<(usize, Option<i32>)> = vec
1053 .iter_updated_valid()
1054 .map(|(i, v)| (i, v.copied()))
1055 .collect();
1056 assert_eq!(collected, vec![(0, Some(15))]);
1057 Ok(())
1058 }
1059
1060 #[test]
1061 #[should_panic(expected = "Vector::update: index 1 out of bounds")]
1062 fn update_out_of_bounds_panics() {
1063 let mut vec: Vector<i32> = Vector::new();
1064 vec.push_committed(7);
1065 vec.update(1, |v| *v += 1);
1066 }
1067
1068 #[test]
1073 fn from_vec_loads_clean_and_valid() {
1074 let vec = Vector::from_vec(vec![10, 20, 30]);
1075 assert_eq!(vec.len(), 3);
1076 assert!(!vec.is_updated());
1077 assert_eq!(vec.get_valid(0), Some(&10));
1078 assert_eq!(vec.get_valid(1), Some(&20));
1079 assert_eq!(vec.get_valid(2), Some(&30));
1080 assert_eq!(vec.iter_updated_valid().count(), 0);
1082 assert_eq!(vec.iter_updated_indices().count(), 0);
1083 }
1084
1085 #[test]
1086 fn from_vec_empty_is_well_formed() {
1087 let vec: Vector<i32> = Vector::from_vec(Vec::new());
1088 assert_eq!(vec.len(), 0);
1089 assert!(vec.is_empty());
1090 assert!(!vec.is_updated());
1091 assert!(!vec.all_updated());
1092 }
1093
1094 #[test]
1095 fn from_vec_straddles_word_boundary() {
1096 let data: Vec<u32> = (0..100).collect();
1098 let mut vec = Vector::from_vec(data);
1099 for i in 0..100 {
1100 assert_eq!(vec.get_valid(i), Some(&(i as u32)));
1101 }
1102 assert!(!vec.is_updated());
1103
1104 vec.commit(75, 9999);
1106 let updated: Vec<usize> = vec.iter_updated_indices().collect();
1107 assert_eq!(updated, vec![75]);
1108 }
1109
1110 #[test]
1111 fn from_fill_clones_value() {
1112 let vec = Vector::from_fill(String::from("hi"), 4);
1113 assert_eq!(vec.len(), 4);
1114 assert!(!vec.is_updated());
1115 for i in 0..4 {
1116 assert_eq!(vec.get_valid(i).map(String::as_str), Some("hi"));
1117 }
1118 }
1119
1120 #[test]
1121 fn with_invalid_slots_allocates_invalid_and_clean() {
1122 let mut vec: Vector<u32> = Vector::with_invalid_slots(3);
1123 assert_eq!(vec.len(), 3);
1125 assert!(!vec.is_empty());
1126 assert!(!vec.is_updated());
1127 for i in 0..3 {
1128 assert!(!vec.is_valid(i));
1129 assert_eq!(vec.get_valid(i), None);
1130 }
1131 vec.commit(1, 42);
1134 assert_eq!(vec.get_valid(1), Some(&42));
1135 assert!(vec.is_updated_at(1));
1136 assert_eq!(vec.get_valid(0), None);
1137 assert_eq!(vec.get_valid(2), None);
1138 }
1139
1140 #[test]
1141 fn with_invalid_slots_zero_len_is_well_formed() {
1142 let vec: Vector<u32> = Vector::with_invalid_slots(0);
1143 assert_eq!(vec.len(), 0);
1144 assert!(vec.is_empty());
1145 assert!(!vec.is_updated());
1146 }
1147
1148 #[test]
1149 fn as_slice_returns_full_data() {
1150 let mut vec = Vector::new();
1151 vec.push_committed(1);
1152 vec.push_committed(2);
1153 vec.push(3); assert_eq!(vec.as_slice(), &[1, 2, 3]);
1155 }
1156
1157 #[test]
1162 fn iter_updated_indices_yields_dirty_in_ascending_order() -> Result<(), Error> {
1163 let mut vec = Vector::new();
1164 for _ in 0..5 {
1165 vec.push_committed(0);
1166 }
1167 vec.reset()?;
1168 assert_eq!(vec.iter_updated_indices().count(), 0);
1169
1170 vec.commit(3, 30);
1172 vec.commit(0, 10);
1173 vec.commit(4, 40);
1174
1175 let got: Vec<usize> = vec.iter_updated_indices().collect();
1176 assert_eq!(got, vec![0, 3, 4]);
1177 Ok(())
1178 }
1179
1180 #[test]
1181 fn iter_updated_indices_handles_word_boundaries() {
1182 let mut vec: Vector<u32> = Vector::new();
1184 for _ in 0..200 {
1185 vec.push(0);
1186 }
1187 vec.reset().unwrap();
1189 for &i in &[0_usize, 63, 64, 127, 128, 199] {
1190 vec.commit(i, i as u32);
1191 }
1192 let got: Vec<usize> = vec.iter_updated_indices().collect();
1193 assert_eq!(got, vec![0, 63, 64, 127, 128, 199]);
1194 }
1195
1196 #[test]
1197 fn iter_updated_indices_empty_when_nothing_dirty() -> Result<(), Error> {
1198 let mut vec: Vector<i32> = Vector::new();
1199 assert_eq!(vec.iter_updated_indices().count(), 0);
1200
1201 vec.push_committed(1);
1202 vec.reset()?;
1203 assert_eq!(vec.iter_updated_indices().count(), 0);
1204 Ok(())
1205 }
1206
1207 #[test]
1208 fn iter_updated_indices_ignores_validity() {
1209 let mut vec: Vector<i32> = Vector::new();
1212 vec.push(0); vec.push_committed(99); let got: Vec<usize> = vec.iter_updated_indices().collect();
1215 assert_eq!(got, vec![0, 1]);
1216 }
1217
1218 #[test]
1219 fn validity_pop_keeps_remaining_bits_aligned() -> Result<(), Error> {
1220 let mut vec = Vector::new();
1221 vec.push_committed(1);
1222 vec.push(0);
1223 vec.push_committed(3);
1224 vec.reset()?;
1225 assert!(vec.is_valid(0));
1226 assert!(!vec.is_valid(1));
1227 assert!(vec.is_valid(2));
1228
1229 let popped = vec.pop();
1230 assert_eq!(popped, Some(3));
1231 assert_eq!(vec.len(), 2);
1232 assert!(vec.is_valid(0));
1233 assert!(!vec.is_valid(1));
1234 Ok(())
1235 }
1236
1237 #[test]
1238 fn validity_clear_resets_everything() {
1239 let mut vec = Vector::new();
1240 vec.push_committed(1);
1241 vec.push_committed(2);
1242 vec.clear();
1243 assert_eq!(vec.len(), 0);
1244 assert!(vec.is_empty());
1245 assert!(!vec.is_updated());
1246 }
1247
1248 #[test]
1253 fn is_updated_at_tracks_per_slot_dirty() -> Result<(), Error> {
1254 let mut vec = Vector::new();
1255 for _ in 0..5 {
1256 vec.push_committed(0_u32);
1257 }
1258 for i in 0..5 {
1260 assert!(vec.is_updated_at(i), "slot {} should be dirty", i);
1261 }
1262 vec.reset()?;
1264 for i in 0..5 {
1265 assert!(!vec.is_updated_at(i), "slot {} should be clean", i);
1266 }
1267 vec.commit(1, 10);
1269 vec.commit(4, 40);
1270 assert!(!vec.is_updated_at(0));
1271 assert!(vec.is_updated_at(1));
1272 assert!(!vec.is_updated_at(2));
1273 assert!(!vec.is_updated_at(3));
1274 assert!(vec.is_updated_at(4));
1275 Ok(())
1276 }
1277
1278 #[test]
1279 fn is_updated_at_out_of_bounds_returns_false() {
1280 let mut vec: Vector<u32> = Vector::new();
1281 vec.push_committed(0);
1282 assert!(vec.is_updated_at(0));
1283 assert!(!vec.is_updated_at(1));
1284 assert!(!vec.is_updated_at(usize::MAX));
1285 }
1286
1287 #[test]
1288 fn is_updated_at_agrees_with_iter_updated_indices() {
1289 let mut vec: Vector<u32> = Vector::new();
1290 for _ in 0..200 {
1291 vec.push(0);
1292 }
1293 vec.reset().unwrap();
1294 for &i in &[0_usize, 63, 64, 127, 128, 199] {
1295 vec.commit(i, i as u32);
1296 }
1297 let dirty: std::collections::HashSet<usize> = vec.iter_updated_indices().collect();
1298 for i in 0..200 {
1299 assert_eq!(
1300 vec.is_updated_at(i),
1301 dirty.contains(&i),
1302 "mismatch at slot {}",
1303 i
1304 );
1305 }
1306 }
1307
1308 #[test]
1309 fn is_updated_at_set_by_invalidate() {
1310 let mut vec = Vector::new();
1311 vec.push_committed(7);
1312 vec.reset().unwrap();
1313 assert!(!vec.is_updated_at(0));
1314 vec.invalidate(0);
1315 assert!(
1316 vec.is_updated_at(0),
1317 "invalidate is a write — should set the dirty bit"
1318 );
1319 }
1320}