From dfce956162cf9e5c91ef69ff35deb65eb1303bc3 Mon Sep 17 00:00:00 2001 From: Eric Coissac Date: Wed, 13 May 2026 10:25:14 +0800 Subject: [PATCH] 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. --- src/obicompactvec/src/reader.rs | 45 +++++++++++++++++++++++++++++ src/obicompactvec/src/tests/mod.rs | 46 ++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/src/obicompactvec/src/reader.rs b/src/obicompactvec/src/reader.rs index 3a8bd56..f4773cf 100644 --- a/src/obicompactvec/src/reader.rs +++ b/src/obicompactvec/src/reader.rs @@ -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 { + 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) { + let remaining = self.pciv.n - self.slot; + (remaining, Some(remaining)) + } } diff --git a/src/obicompactvec/src/tests/mod.rs b/src/obicompactvec/src/tests/mod.rs index ec9c42d..9f2ba94 100644 --- a/src/obicompactvec/src/tests/mod.rs +++ b/src/obicompactvec/src/tests/mod.rs @@ -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 = r.iter().collect(); + let via_get: Vec = (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 = (&r).into_iter().collect(); + assert_eq!(collected, vec![10, 500, 30]); +} + #[test] fn mixed_large_dataset() { let n = 1000usize;