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}