feat(obicompactvec): implement Iterator for PersistentCompactIntVec

Add an `Iter` struct that implements `Iterator` and `ExactSizeIterator` to enable idiomatic traversal of `&PersistentCompactIntVec`. The iterator maintains `slot` and `overflow_pos` state to correctly yield `u32` values from both primary and overflow memory regions. Includes three unit tests validating iteration correctness against direct indexing, accurate `len()` tracking, and proper reference-based iteration.
This commit is contained in:
Eric Coissac
2026-05-13 10:25:14 +08:00
parent 4d5fcd4340
commit dfce956162
2 changed files with 91 additions and 0 deletions
+45
View File
@@ -104,4 +104,49 @@ impl PersistentCompactIntVec {
let off = self.data_offset + i * 8 + 4;
u32::from_le_bytes(self.mmap[off..off + 4].try_into().unwrap())
}
pub fn iter(&self) -> Iter<'_> {
Iter { pciv: self, slot: 0, overflow_pos: 0 }
}
}
impl<'a> IntoIterator for &'a PersistentCompactIntVec {
type Item = u32;
type IntoIter = Iter<'a>;
fn into_iter(self) -> Iter<'a> {
self.iter()
}
}
pub struct Iter<'a> {
pciv: &'a PersistentCompactIntVec,
slot: usize,
overflow_pos: usize,
}
impl ExactSizeIterator for Iter<'_> {}
impl Iterator for Iter<'_> {
type Item = u32;
fn next(&mut self) -> Option<u32> {
if self.slot >= self.pciv.n {
return None;
}
let v = self.pciv.mmap[self.pciv.primary_offset + self.slot];
self.slot += 1;
if v < 255 {
Some(v as u32)
} else {
let val = self.pciv.data_value(self.overflow_pos);
self.overflow_pos += 1;
Some(val)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.pciv.n - self.slot;
(remaining, Some(remaining))
}
}
+46
View File
@@ -89,6 +89,52 @@ fn sparse_index_built_for_many_overflows() {
}
}
#[test]
fn iter_matches_get() {
let values = [(0u64, 255u32), (1, 1000), (2, 50), (3, 1_313_691), (4, 7), (5, 42)];
let n = 6;
let dir = tempdir().unwrap();
let path = dir.path().join("test.pciv");
let mut b = PersistentCompactIntVecBuilder::new(n);
for &(slot, v) in &values {
b.set(slot, v);
}
b.close(&path).unwrap();
let r = PersistentCompactIntVec::open(&path).unwrap();
let via_iter: Vec<u32> = r.iter().collect();
let via_get: Vec<u32> = (0..n as u64).map(|s| r.get(s)).collect();
assert_eq!(via_iter, via_get);
}
#[test]
fn iter_size_hint_exact() {
let dir = tempdir().unwrap();
let path = dir.path().join("test.pciv");
let mut b = PersistentCompactIntVecBuilder::new(5);
b.set(2, 1000);
b.close(&path).unwrap();
let r = PersistentCompactIntVec::open(&path).unwrap();
let mut it = r.iter();
assert_eq!(it.len(), 5);
it.next();
assert_eq!(it.len(), 4);
}
#[test]
fn into_iter_for_ref() {
let dir = tempdir().unwrap();
let path = dir.path().join("test.pciv");
let mut b = PersistentCompactIntVecBuilder::new(3);
b.set(0, 10);
b.set(1, 500);
b.set(2, 30);
b.close(&path).unwrap();
let r = PersistentCompactIntVec::open(&path).unwrap();
let collected: Vec<u32> = (&r).into_iter().collect();
assert_eq!(collected, vec![10, 500, 30]);
}
#[test]
fn mixed_large_dataset() {
let n = 1000usize;