linkrs_storage/edge/immutable_csr/
read.rs1use super::super::csr_shared::{decode_endpoint_pair, is_reclaimable_cold};
2use super::super::{EdgeId, EdgePosition, HotNbr, Nbr, Timestamp, VertexId, INVALID_EDGE_ID};
3use super::pack::frozen_key_range;
4use super::ImmutableCsr;
5
6impl ImmutableCsr {
7 pub(crate) fn row_window(&self, src_vid: u32) -> Option<(usize, usize)> {
8 let idx = src_vid as usize;
9 if idx >= self.degrees.len() {
10 return None;
11 }
12 let start = self.offsets[idx] as usize;
13 let degree = self.degrees[idx] as usize;
14 if start.saturating_add(degree) > self.hot_entries.len() {
15 return None;
16 }
17 Some((start, start + degree))
18 }
19
20 #[inline]
22 pub(crate) fn slot_at(&self, idx: usize) -> Option<Nbr> {
23 Some(Nbr::from_parts(
24 *self.hot_entries.get(idx)?,
25 *self.cold_entries.get(idx)?,
26 ))
27 }
28
29 pub fn row_degree(&self, src_vid: u32) -> usize {
31 let idx = src_vid as usize;
32 if idx >= self.degrees.len() {
33 0
34 } else {
35 self.degrees[idx] as usize
36 }
37 }
38
39 pub fn edges_of(&self, src_vid: u32, ts: Timestamp) -> Vec<Nbr> {
46 let Some((start, end)) = self.row_window(src_vid) else {
47 return Vec::new();
48 };
49 self.hot_entries[start..end]
50 .iter()
51 .zip(&self.cold_entries[start..end])
52 .filter_map(|(hot, cold)| {
53 let nbr = Nbr::from_parts(*hot, *cold);
54 nbr.is_alive_at(ts).then_some(nbr)
55 })
56 .collect()
57 }
58
59 pub fn get_edge(&self, src_vid: u32, dst: VertexId, ts: Timestamp) -> Option<Nbr> {
65 let (decoded_endpoint, decoded_rank) = decode_endpoint_pair(dst)?;
66 let (start, end) = self.row_window(src_vid)?;
67 let hot = &self.hot_entries[start..end];
68 let cold = &self.cold_entries[start..end];
69 let (lo, hi) = frozen_key_range(hot, decoded_endpoint, decoded_rank);
70 hot[lo..hi]
71 .iter()
72 .zip(&cold[lo..hi])
73 .find_map(|(hot, cold)| {
74 let nbr = Nbr::from_parts(*hot, *cold);
75 nbr.is_alive_at(ts).then_some(nbr)
76 })
77 }
78
79 pub fn get_edge_physical(&self, src_vid: u32, dst: VertexId) -> Option<Nbr> {
84 let (decoded_endpoint, decoded_rank) = decode_endpoint_pair(dst)?;
85 let (start, end) = self.row_window(src_vid)?;
86 let hot = &self.hot_entries[start..end];
87 let cold = &self.cold_entries[start..end];
88 let (lo, hi) = frozen_key_range(hot, decoded_endpoint, decoded_rank);
89 hot[lo..hi]
90 .iter()
91 .zip(&cold[lo..hi])
92 .find_map(|(hot, cold)| {
93 if hot.edge_id != INVALID_EDGE_ID && cold.is_live() {
94 Some(Nbr::from_parts(*hot, *cold))
95 } else {
96 None
97 }
98 })
99 }
100
101 pub fn physical_edges_of(&self, src_vid: u32) -> Vec<Nbr> {
106 let mut out = Vec::new();
107 self.fill_physical_into(src_vid, &mut out);
108 out
109 }
110
111 pub fn fill_physical_into(&self, src_vid: u32, out: &mut Vec<Nbr>) {
113 out.clear();
114 let Some((start, end)) = self.row_window(src_vid) else {
115 return;
116 };
117 out.reserve(end - start);
118 for (hot, cold) in self.hot_entries[start..end]
119 .iter()
120 .zip(&self.cold_entries[start..end])
121 {
122 out.push(Nbr::from_parts(*hot, *cold));
123 }
124 }
125
126 pub fn visit_physical<F>(&self, src_vid: u32, mut f: F)
128 where
129 F: FnMut(Nbr) -> bool,
130 {
131 let Some((start, end)) = self.row_window(src_vid) else {
132 return;
133 };
134 for (hot, cold) in self.hot_entries[start..end]
135 .iter()
136 .zip(&self.cold_entries[start..end])
137 {
138 if !f(Nbr::from_parts(*hot, *cold)) {
139 return;
140 }
141 }
142 }
143
144 pub fn visit_hot<F>(&self, src_vid: u32, mut f: F)
150 where
151 F: FnMut(HotNbr) -> bool,
152 {
153 let Some((start, end)) = self.row_window(src_vid) else {
154 return;
155 };
156 for hot in &self.hot_entries[start..end] {
157 if !f(*hot) {
158 return;
159 }
160 }
161 }
162
163 pub fn is_row_sorted(&self, _src_vid: u32) -> bool {
165 true
166 }
167
168 pub fn visit_threshold<F>(
172 &self,
173 src_vid: u32,
174 lower: Option<(u32, i64)>,
175 upper: Option<(u32, i64)>,
176 mut f: F,
177 ) where
178 F: FnMut(Nbr) -> bool,
179 {
180 let Some((start, end)) = self.row_window(src_vid) else {
181 return;
182 };
183 let hot = &self.hot_entries[start..end];
184 let cold = &self.cold_entries[start..end];
185 let lo = match lower {
186 Some((lo_ep, lo_rank)) => {
187 hot.partition_point(|h| (h.endpoint, h.rank) < (lo_ep, lo_rank))
188 }
189 None => 0,
190 };
191 let hi = match upper {
192 Some((hi_ep, hi_rank)) => {
193 hot.partition_point(|h| (h.endpoint, h.rank) <= (hi_ep, hi_rank))
194 }
195 None => hot.len(),
196 };
197 for (h, c) in hot[lo..hi].iter().zip(&cold[lo..hi]) {
198 if c.is_live() && !f(Nbr::from_parts(*h, *c)) {
199 return;
200 }
201 }
202 }
203
204 pub fn fill_threshold_into(
206 &self,
207 src_vid: u32,
208 lower: Option<(u32, i64)>,
209 upper: Option<(u32, i64)>,
210 out: &mut Vec<Nbr>,
211 ) {
212 out.clear();
213 self.visit_threshold(src_vid, lower, upper, |nbr| {
214 out.push(nbr);
215 true
216 });
217 }
218
219 pub fn nbr_at_offset(&self, src_vid: u32, offset: i32) -> Option<Nbr> {
221 if offset < 0 {
222 return None;
223 }
224 let (start, end) = self.row_window(src_vid)?;
225 if start.saturating_add(offset as usize) >= end {
226 return None;
227 }
228 self.slot_at(start + offset as usize)
229 }
230
231 pub fn has_physical_entries(&self, vid: u32) -> bool {
233 self.row_degree(vid) > 0
234 }
235
236 pub fn primary_contains(&self, src_vid: u32, edge_id: EdgeId) -> bool {
238 let Some((start, end)) = self.row_window(src_vid) else {
239 return false;
240 };
241 self.hot_entries[start..end]
242 .iter()
243 .any(|hot| hot.edge_id == edge_id)
244 }
245
246 pub fn locate_edge(&self, src_vid: u32, edge_id: EdgeId) -> Option<(EdgePosition, Nbr)> {
248 let (start, end) = self.row_window(src_vid)?;
249 for (slot, (hot, cold)) in self.hot_entries[start..end]
250 .iter()
251 .zip(&self.cold_entries[start..end])
252 .enumerate()
253 {
254 if hot.edge_id == edge_id {
255 return Some((
256 EdgePosition::Primary { slot: slot as u32 },
257 Nbr::from_parts(*hot, *cold),
258 ));
259 }
260 }
261 None
262 }
263
264 pub fn has_valued_entries(&self) -> bool {
269 !self.values.is_empty()
270 }
271
272 pub(crate) fn value_at_index(&self, idx: usize) -> (u64, bool) {
277 if idx >= self.values.len() {
278 return (0, false);
279 }
280 let raw = self.values[idx];
281 let valid = self.valid.get(idx).map(|b| *b).unwrap_or(false);
282 (raw, valid)
283 }
284
285 pub fn value_by_edge_id(&self, src_vid: u32, edge_id: EdgeId) -> Option<(u64, bool)> {
290 let (start, end) = self.row_window(src_vid)?;
291 for idx in start..end {
292 if self.hot_entries[idx].edge_id == edge_id {
293 return Some(self.value_at_index(idx));
294 }
295 }
296 None
297 }
298
299 pub fn value_by_endpoint(&self, src_vid: u32, endpoint: u32) -> Option<(u64, bool)> {
305 let (start, end) = self.row_window(src_vid)?;
306 for idx in start..end {
307 let hot = self.hot_entries[idx];
308 if hot.endpoint == endpoint && hot.edge_id != INVALID_EDGE_ID {
309 return Some(self.value_at_index(idx));
310 }
311 }
312 None
313 }
314
315 pub fn visit_physical_with_values<F>(&self, src_vid: u32, mut f: F)
322 where
323 F: FnMut(Nbr, Option<u64>) -> bool,
324 {
325 let Some((start, end)) = self.row_window(src_vid) else {
326 return;
327 };
328 for idx in start..end {
329 let nbr = Nbr::from_parts(self.hot_entries[idx], self.cold_entries[idx]);
330 let (raw, valid) = self.value_at_index(idx);
331 if !f(nbr, valid.then_some(raw)) {
332 return;
333 }
334 }
335 }
336
337 pub fn any_valid_values(&self) -> bool {
339 self.valid.count_ones() > 0
340 }
341
342 pub(crate) fn packed_values(&self) -> &[u64] {
344 &self.values
345 }
346
347 pub(crate) fn packed_valid_bytes(&self) -> &[u8] {
352 let want = self.values.len().div_ceil(8);
353 self.valid.as_raw_slice().get(..want).unwrap_or(&[])
354 }
355
356 pub fn vertex_census(&self, vid: u32) -> (usize, usize, usize) {
361 let Some((start, end)) = self.row_window(vid) else {
362 return (0, 0, 0);
363 };
364 let mut live = 0usize;
365 let mut dead = 0usize;
366 for cold in &self.cold_entries[start..end] {
367 if cold.is_live() {
368 live += 1;
369 } else {
370 dead += 1;
371 }
372 }
373 (live, dead, end - start)
374 }
375
376 pub fn reclaimable_count(&self, vid: u32, cutoff: Timestamp) -> usize {
378 if cutoff == Timestamp::MAX {
379 return 0;
380 }
381 let Some((start, end)) = self.row_window(vid) else {
382 return 0;
383 };
384 self.cold_entries[start..end]
385 .iter()
386 .filter(|cold| is_reclaimable_cold(cold, cutoff))
387 .count()
388 }
389}