Skip to main content

linkrs_storage/edge/immutable_csr/
iter.rs

1use super::super::{ColdStamps, HotNbr, Nbr, Timestamp, VertexId};
2use super::ImmutableCsr;
3
4impl ImmutableCsr {
5    /// Iterate timestamp-visible entries across all rows.
6    pub fn iter(&self, ts: Timestamp) -> ImmutableCsrIterator<'_> {
7        ImmutableCsrIterator::new(self, ts)
8    }
9
10    /// Iterate timestamp-visible entries of one row without allocating.
11    ///
12    /// Zero-copy counterpart of `edges_of`: walks the packed hot slice and
13    /// assembles each record with its cold half inline, so per-vertex scans
14    /// over frozen groups never touch the allocator. Out-of-range rows yield
15    /// an empty iterator.
16    pub fn iter_edges_of(&self, src_vid: u32, ts: Timestamp) -> FrozenRowIter<'_> {
17        let (start, end) = self.row_window(src_vid).unwrap_or((0, 0));
18        FrozenRowIter {
19            hot: &self.hot_entries[start..end],
20            cold: &self.cold_entries[start..end],
21            ts,
22            idx: 0,
23        }
24    }
25
26    /// Iterate every physically stored entry, including tombstoned ones.
27    pub fn iter_all(&self) -> ImmutableCsrIterator<'_> {
28        ImmutableCsrIterator::new_all(self)
29    }
30}
31
32/// Iterator over one frozen row, yielding assembled entries.
33///
34/// The packed row is already contiguous, so iteration is a filtered
35/// hot-slice walk with no allocation and no pointer chasing. Records are
36/// assembled by value because the halves live in separate slices.
37pub struct FrozenRowIter<'a> {
38    hot: &'a [HotNbr],
39    cold: &'a [ColdStamps],
40    ts: Timestamp,
41    idx: usize,
42}
43
44impl<'a> Iterator for FrozenRowIter<'a> {
45    type Item = Nbr;
46
47    fn next(&mut self) -> Option<Self::Item> {
48        while self.idx < self.hot.len() {
49            let nbr = Nbr::from_parts(self.hot[self.idx], self.cold[self.idx]);
50            self.idx += 1;
51            if nbr.is_alive_at(self.ts) {
52                return Some(nbr);
53            }
54        }
55        None
56    }
57}
58
59/// Iterator over frozen rows, yielding local vertex ids.
60pub struct ImmutableCsrIterator<'a> {
61    csr: &'a ImmutableCsr,
62    ts: Timestamp,
63    include_deleted: bool,
64    row: usize,
65    idx: usize,
66}
67
68impl<'a> ImmutableCsrIterator<'a> {
69    fn new(csr: &'a ImmutableCsr, ts: Timestamp) -> Self {
70        Self {
71            csr,
72            ts,
73            include_deleted: false,
74            row: 0,
75            idx: 0,
76        }
77    }
78
79    fn new_all(csr: &'a ImmutableCsr) -> Self {
80        Self {
81            csr,
82            ts: 0,
83            include_deleted: true,
84            row: 0,
85            idx: 0,
86        }
87    }
88}
89
90impl<'a> Iterator for ImmutableCsrIterator<'a> {
91    type Item = (VertexId, Nbr);
92
93    fn next(&mut self) -> Option<Self::Item> {
94        while self.row < self.csr.degrees.len() {
95            let end = self.csr.offsets[self.row] as usize + self.csr.degrees[self.row] as usize;
96            while self.idx < end {
97                let nbr = self.csr.slot_at(self.idx).unwrap();
98                self.idx += 1;
99                if self.include_deleted || nbr.is_alive_at(self.ts) {
100                    return Some((VertexId::from_u32(u32::try_from(self.row).ok()?), nbr));
101                }
102            }
103            self.row += 1;
104            if self.row < self.csr.offsets.len() {
105                self.idx = self.csr.offsets[self.row] as usize;
106            }
107        }
108        None
109    }
110}