Skip to main content

linkrs_storage/edge/csr_variant/
read.rs

1use super::super::{HotNbr, MutableCsrTrait, Nbr, Timestamp};
2use super::{CsrIterator, CsrRowIter, CsrVariant};
3
4impl CsrVariant {
5    /// Iterate edges of a vertex without allocating, for every strategy.
6    ///
7    /// Test-only row-stamp filtered iterator; production scans go through
8    /// the version authority. Missing groups and the `None` strategy yield
9    /// no iterator. Bundled rows yield topology only: pair each item with
10    /// the value accessors instead of reading values from this walk.
11    pub fn iter_edges_of(&self, src_vid: u32, ts: Timestamp) -> Option<CsrRowIter<'_>> {
12        // Pure rows store no timestamps, so the timestamp carries no
13        // information there; the borrowed walk yields live entries directly.
14        let _ = ts;
15        match self {
16            CsrVariant::Multiple(csr) => Some(CsrRowIter::Multiple(csr.iter_edges_of(src_vid, ts))),
17            CsrVariant::Single(csr) => Some(CsrRowIter::Single(
18                csr.iter_edges_of(src_vid, ts).into_iter(),
19            )),
20            CsrVariant::Pure(csr) => Some(CsrRowIter::Pure(csr.iter_row(src_vid))),
21            CsrVariant::Bundled(csr) => Some(CsrRowIter::Bundled(csr.iter_row(src_vid))),
22            CsrVariant::Frozen(csr) => Some(CsrRowIter::Frozen(csr.iter_edges_of(src_vid, ts))),
23            CsrVariant::None { .. } => None,
24        }
25    }
26
27    /// Iterate over all physically present entries, including tombstoned ones.
28    ///
29    /// Used when rebuilding the CSR (e.g. vertex ID remapping) so entries
30    /// marked as deleted survive the rebuild. Pure and bundled holes carry
31    /// the unassignable sentinel instead of an edge, so they are skipped:
32    /// no rebuild may resurrect a hole as a live edge. Bundled values are
33    /// not yielded here; migration paths resolve them through the paired
34    /// value entry.
35    pub fn iter_all(&self) -> CsrIterator<'_> {
36        match self {
37            CsrVariant::Multiple(csr) => CsrIterator::Multiple(csr.iter_all()),
38            CsrVariant::Single(csr) => CsrIterator::Single(csr.iter_all()),
39            CsrVariant::Pure(csr) => CsrIterator::Pure(csr.iter_all()),
40            CsrVariant::Bundled(csr) => CsrIterator::Bundled(csr.iter_all()),
41            CsrVariant::Frozen(csr) => CsrIterator::Frozen(csr.iter_all()),
42            CsrVariant::None { .. } => CsrIterator::None,
43        }
44    }
45
46    /// Visit every physically stored entry of one vertex without allocating.
47    ///
48    /// Bundled rows visit topology only; use `visit_physical_with_values`
49    /// when the inline value is needed.
50    pub fn visit_physical<F>(&self, src_vid: u32, f: F)
51    where
52        F: FnMut(Nbr) -> bool,
53    {
54        match self {
55            CsrVariant::Multiple(csr) => csr.visit_physical(src_vid, f),
56            CsrVariant::Single(csr) => csr.visit_physical(src_vid, f),
57            CsrVariant::Pure(csr) => csr.visit_physical(src_vid, f),
58            CsrVariant::Bundled(csr) => csr.visit_physical(src_vid, f),
59            CsrVariant::Frozen(csr) => csr.visit_physical(src_vid, f),
60            CsrVariant::None { .. } => {}
61        }
62    }
63
64    /// Visit every physically stored hot half of one vertex without
65    /// allocating and without touching the stamp lines.
66    ///
67    /// Hot-only counterpart of [`Self::visit_physical`] for traversals that
68    /// resolve visibility through the version authority by `edge_id`.
69    pub fn visit_hot<F>(&self, src_vid: u32, f: F)
70    where
71        F: FnMut(HotNbr) -> bool,
72    {
73        match self {
74            CsrVariant::Multiple(csr) => csr.visit_hot(src_vid, f),
75            CsrVariant::Single(csr) => csr.visit_hot(src_vid, f),
76            CsrVariant::Pure(csr) => csr.visit_hot(src_vid, f),
77            CsrVariant::Bundled(csr) => csr.visit_hot(src_vid, f),
78            CsrVariant::Frozen(csr) => csr.visit_hot(src_vid, f),
79            CsrVariant::None { .. } => {}
80        }
81    }
82
83    /// Fill a caller buffer with every physically stored entry of one vertex.
84    ///
85    /// Same content as the allocating trait accessor, without the per-vertex
86    /// allocation. Batch scans reuse one buffer across vertices. Behavior is
87    /// uniform across variants: the buffer is cleared first, then the row is
88    /// appended in the variant's promised order (insertion order for mutable
89    /// forms, sorted order for frozen forms; see the enum-level contract).
90    /// Bundled rows fill topology only; pair with the value accessors.
91    pub fn fill_physical_into(&self, src_vid: u32, out: &mut Vec<Nbr>) {
92        match self {
93            CsrVariant::Multiple(csr) => csr.fill_physical_into(src_vid, out),
94            CsrVariant::Single(csr) => csr.fill_physical_into(src_vid, out),
95            CsrVariant::Pure(csr) => csr.fill_physical_into(src_vid, out),
96            CsrVariant::Bundled(csr) => csr.fill_physical_into(src_vid, out),
97            CsrVariant::Frozen(csr) => csr.fill_physical_into(src_vid, out),
98            CsrVariant::None { .. } => out.clear(),
99        }
100    }
101
102    /// Whether the live entries of one row arrive in key order.
103    ///
104    /// Only frozen and single-slot rows promise order and may use bisection.
105    /// All other forms report an observation that callers must not cache
106    /// across restarts: the flag is memory-only, rebuilt on load, and query
107    /// planning falls back to a linear walk unless the promise holds for the
108    /// row's current variant.
109    pub fn is_row_sorted(&self, src_vid: u32) -> bool {
110        match self {
111            CsrVariant::Multiple(csr) => csr.is_row_sorted(src_vid),
112            CsrVariant::Single(csr) => csr.is_row_sorted(src_vid),
113            CsrVariant::Pure(csr) => csr.is_row_sorted(src_vid),
114            CsrVariant::Bundled(csr) => csr.is_row_sorted(src_vid),
115            CsrVariant::Frozen(csr) => csr.is_row_sorted(src_vid),
116            CsrVariant::None { .. } => true,
117        }
118    }
119
120    /// Visit live entries whose `(endpoint, rank)` key falls in the inclusive
121    /// `[lower, upper]` range. Pure and bundled rows carry no rank, so the
122    /// rank halves of the bounds are explicitly ignored there: callers pass
123    /// endpoint intervals, and reusing one binary interval across forms
124    /// scans the wider endpoint range on these forms by contract rather than
125    /// by silent widening.
126    ///
127    /// Bisection planning stays with [`Self::should_use_bisection`]: promised
128    /// forms bisect whole rows, other forms bisect only a sorted primary
129    /// prefix through their memory-only flag and scan the rest linearly.
130    /// This entry only forwards; it never re-decides the plan.
131    pub fn visit_threshold<F>(
132        &self,
133        src_vid: u32,
134        lower: Option<(u32, i64)>,
135        upper: Option<(u32, i64)>,
136        f: F,
137    ) where
138        F: FnMut(Nbr) -> bool,
139    {
140        match self {
141            CsrVariant::Multiple(csr) => csr.visit_threshold(src_vid, lower, upper, f),
142            CsrVariant::Single(csr) => csr.visit_threshold(src_vid, lower, upper, f),
143            CsrVariant::Pure(csr) => {
144                csr.visit_threshold(src_vid, lower.map(|(ep, _)| ep), upper.map(|(ep, _)| ep), f)
145            }
146            CsrVariant::Bundled(csr) => {
147                csr.visit_threshold(src_vid, lower.map(|(ep, _)| ep), upper.map(|(ep, _)| ep), f)
148            }
149            CsrVariant::Frozen(csr) => csr.visit_threshold(src_vid, lower, upper, f),
150            CsrVariant::None { .. } => {}
151        }
152    }
153
154    /// Fill a caller buffer with the same range content as `visit_threshold`.
155    ///
156    /// Same explicit rank-half rule as `visit_threshold` on pure and bundled
157    /// rows.
158    pub fn fill_threshold_into(
159        &self,
160        src_vid: u32,
161        lower: Option<(u32, i64)>,
162        upper: Option<(u32, i64)>,
163        out: &mut Vec<Nbr>,
164    ) {
165        out.clear();
166        self.visit_threshold(src_vid, lower, upper, |nbr| {
167            out.push(nbr);
168            true
169        });
170    }
171
172    /// Visit entries whose endpoint falls in the inclusive endpoint range.
173    ///
174    /// Endpoint-only counterpart of `visit_threshold` for callers sharing one
175    /// interval across forms. Pure and bundled rows ignore rank halves by
176    /// contract; other forms widen the rank halves to the full range so the
177    /// same endpoint interval scans the same endpoints everywhere.
178    pub fn visit_threshold_endpoint_only<F>(
179        &self,
180        src_vid: u32,
181        lower: Option<u32>,
182        upper: Option<u32>,
183        f: F,
184    ) where
185        F: FnMut(Nbr) -> bool,
186    {
187        let lower_key = lower.map(|endpoint| (endpoint, i64::MIN));
188        let upper_key = upper.map(|endpoint| (endpoint, i64::MAX));
189        self.visit_threshold(src_vid, lower_key, upper_key, f)
190    }
191
192    /// Fill a caller buffer with the same endpoint-range content.
193    pub fn fill_threshold_endpoint_only_into(
194        &self,
195        src_vid: u32,
196        lower: Option<u32>,
197        upper: Option<u32>,
198        out: &mut Vec<Nbr>,
199    ) {
200        out.clear();
201        self.visit_threshold_endpoint_only(src_vid, lower, upper, |nbr| {
202            out.push(nbr);
203            true
204        });
205    }
206}