1use super::addr::VertexAddr;
2use super::csr_edges::CsrEdges;
3use super::vertex::VertexId;
4use rustc_hash::{FxHashMap, FxHashSet};
5
6#[cfg(test)]
7mod tests {
8 use super::super::addr::GridAddr;
9
10 fn grid(row: u32, col: u32) -> VertexAddr {
11 VertexAddr::grid(GridAddr::new(row, col))
12 }
13
14 use super::*;
15
16 #[test]
17 fn test_delta_slab_add_edge() {
18 let csr = CsrEdges::from_adjacency(
19 vec![(0u32, vec![1u32])],
20 &[grid(0, 0), grid(0, 1), grid(0, 2)],
21 );
22 let mut delta = DeltaEdgeSlab::new();
23
24 delta.add_edge(VertexId(0), VertexId(2));
25
26 let merged = delta.merged_view(&csr, VertexId(0));
27 assert_eq!(merged, vec![VertexId(1), VertexId(2)]);
28 }
29
30 #[test]
31 fn test_delta_slab_remove_edge() {
32 let csr = CsrEdges::from_adjacency(
33 vec![(0u32, vec![1u32, 2u32, 3u32])],
34 &[grid(0, 0), grid(0, 1), grid(0, 2), grid(0, 3)],
35 );
36 let mut delta = DeltaEdgeSlab::new();
37
38 delta.remove_edge(VertexId(0), VertexId(2));
39
40 let merged = delta.merged_view(&csr, VertexId(0));
41 assert_eq!(merged, vec![VertexId(1), VertexId(3)]);
42 }
43
44 #[test]
45 fn test_delta_slab_rebuild_threshold() {
46 let mut edges = CsrMutableEdges::new();
47
48 for i in 0..1000 {
50 edges.add_edge(VertexId(i), VertexId(i + 1));
51 }
52
53 assert!(edges.delta_size() < 100); }
56
57 #[test]
58 fn test_delta_slab_multiple_operations() {
59 let csr = CsrEdges::from_adjacency(
60 vec![(0u32, vec![1u32, 2u32]), (1u32, vec![3u32])],
61 &[grid(0, 0), grid(0, 1), grid(0, 2), grid(1, 0)],
62 );
63 let mut delta = DeltaEdgeSlab::new();
64
65 delta.add_edge(VertexId(0), VertexId(3));
67 delta.remove_edge(VertexId(0), VertexId(1));
68 delta.add_edge(VertexId(0), VertexId(4));
69
70 let merged = delta.merged_view(&csr, VertexId(0));
71 assert_eq!(merged, vec![VertexId(2), VertexId(3), VertexId(4)]);
72 }
73
74 #[test]
75 fn test_mutable_edges_exact_edge_count_includes_delta() {
76 let mut edges = CsrMutableEdges::with_coords(vec![grid(0, 0), grid(0, 1), grid(0, 2)]);
77 edges.add_edge(VertexId(0), VertexId(1));
78 edges.add_edge(VertexId(0), VertexId(2));
79 assert_eq!(edges.num_edges_exact(), 2);
80
81 edges.remove_edge(VertexId(0), VertexId(1));
82 assert_eq!(edges.num_edges_exact(), 1);
83 }
84
85 #[test]
86 fn test_delta_slab_empty_base() {
87 let csr = CsrEdges::empty();
88 let mut delta = DeltaEdgeSlab::new();
89
90 delta.add_edge(VertexId(0), VertexId(1));
91 delta.add_edge(VertexId(0), VertexId(2));
92
93 let merged = delta.merged_view(&csr, VertexId(0));
94 assert_eq!(merged, vec![VertexId(1), VertexId(2)]);
95 }
96
97 #[test]
98 fn test_delta_slab_remove_nonexistent() {
99 let csr = CsrEdges::from_adjacency(vec![(0u32, vec![1u32])], &[grid(0, 0), grid(0, 1)]);
100 let mut delta = DeltaEdgeSlab::new();
101
102 delta.remove_edge(VertexId(0), VertexId(2));
104
105 let merged = delta.merged_view(&csr, VertexId(0));
106 assert_eq!(merged, vec![VertexId(1)]); }
108
109 #[test]
110 fn test_delta_slab_apply_to_csr() {
111 let csr = CsrEdges::from_adjacency(
112 vec![(0u32, vec![1u32]), (1u32, vec![2u32]), (2u32, vec![])],
113 &[grid(0, 0), grid(0, 1), grid(1, 0)],
114 );
115
116 let mut delta = DeltaEdgeSlab::new();
117 delta.add_edge(VertexId(0), VertexId(2));
118 delta.remove_edge(VertexId(1), VertexId(2));
119 delta.add_edge(VertexId(2), VertexId(0));
120
121 let coords = vec![grid(0, 0), grid(0, 1), grid(1, 0)];
123 let vertex_ids = vec![0u32, 1u32, 2u32];
124 let new_csr = delta.apply_to_csr(&csr, &coords, &vertex_ids);
125
126 assert_eq!(new_csr.out_edges(VertexId(0)), &[VertexId(1), VertexId(2)]);
127 assert_eq!(new_csr.out_edges(VertexId(1)), &[]);
128 assert_eq!(new_csr.out_edges(VertexId(2)), &[VertexId(0)]);
129 }
130
131 #[test]
132 fn test_mutable_edges_auto_rebuild() {
133 let mut edges = CsrMutableEdges::with_coords(vec![grid(0, 0), grid(0, 1), grid(1, 0)]);
134
135 edges.add_edge(VertexId(0), VertexId(1));
137 edges.add_edge(VertexId(1), VertexId(2));
138
139 for _ in 0..500 {
141 edges.add_edge(VertexId(2), VertexId(0));
142 edges.remove_edge(VertexId(2), VertexId(0));
143 }
144
145 assert!(edges.delta_size() < 50);
147
148 assert_eq!(edges.out_edges(VertexId(0)), vec![VertexId(1)]);
150 assert_eq!(edges.out_edges(VertexId(1)), vec![VertexId(2)]);
151 }
152
153 #[test]
154 fn test_mutable_edges_with_offset_vertex_ids() {
155 use crate::engine::vertex_store::FIRST_NORMAL_VERTEX;
156
157 let mut edges = CsrMutableEdges::new();
158
159 let base_id = FIRST_NORMAL_VERTEX;
161 edges.add_vertex(grid(0, 0), base_id);
162 edges.add_vertex(grid(0, 1), base_id + 1);
163 edges.add_vertex(grid(1, 0), base_id + 2);
164
165 edges.add_edge(VertexId(base_id), VertexId(base_id + 1));
167 edges.add_edge(VertexId(base_id + 1), VertexId(base_id + 2));
168 edges.add_edge(VertexId(base_id + 2), VertexId(base_id));
169
170 assert_eq!(
172 edges.out_edges(VertexId(base_id)),
173 vec![VertexId(base_id + 1)]
174 );
175 assert_eq!(
176 edges.out_edges(VertexId(base_id + 1)),
177 vec![VertexId(base_id + 2)]
178 );
179 assert_eq!(
180 edges.out_edges(VertexId(base_id + 2)),
181 vec![VertexId(base_id)]
182 );
183
184 edges.rebuild();
186 assert_eq!(
187 edges.out_edges(VertexId(base_id)),
188 vec![VertexId(base_id + 1)]
189 );
190 assert_eq!(
191 edges.out_edges(VertexId(base_id + 1)),
192 vec![VertexId(base_id + 2)]
193 );
194 assert_eq!(
195 edges.out_edges(VertexId(base_id + 2)),
196 vec![VertexId(base_id)]
197 );
198 }
199
200 #[test]
201 fn test_csr_coord_update() {
202 let mut edges = CsrMutableEdges::new();
203
204 edges.add_vertex(grid(1, 1), 1024);
205 edges.add_vertex(grid(2, 2), 1025);
206 edges.add_edge(VertexId(1024), VertexId(1025));
207
208 edges.update_addr(VertexId(1024), grid(5, 5));
210
211 edges.rebuild();
213 let out = edges.out_edges(VertexId(1024));
214 assert_eq!(out, vec![VertexId(1025)]);
215 }
216
217 #[test]
218 fn update_coord_uses_vertex_position_index() {
219 let mut edges = CsrMutableEdges::new();
220 let items: Vec<_> = (0..20_000u32).map(|id| (grid(id, id % 17), id)).collect();
221 edges.add_vertices_batch(&items);
222
223 let started = std::time::Instant::now();
224 for id in 15_000..20_000u32 {
225 edges.update_addr(VertexId(id), grid(id + 1, (id + 2) % 100));
226 }
227 let elapsed = started.elapsed();
228
229 if !cfg!(debug_assertions) {
230 assert!(
231 elapsed < std::time::Duration::from_millis(50),
232 "update_coord took {elapsed:?}"
233 );
234 }
235
236 for id in 15_000..20_000u32 {
237 let pos = edges.vertex_pos[&id];
238 assert_eq!(edges.vertex_ids[pos], id);
239 assert_eq!(edges.coords[pos], grid(id + 1, (id + 2) % 100));
240 }
241 }
242
243 #[test]
244 fn test_last_op_wins_add_then_remove() {
245 let csr = CsrEdges::from_adjacency(vec![(0u32, vec![])], &[grid(0, 0)]);
246 let mut delta = DeltaEdgeSlab::new();
247 delta.add_edge(VertexId(0), VertexId(1));
248 delta.remove_edge(VertexId(0), VertexId(1));
249 let merged = delta.merged_view(&csr, VertexId(0));
250 assert_eq!(merged, vec![]);
251 }
252
253 #[test]
254 fn test_last_op_wins_remove_then_add() {
255 let csr = CsrEdges::from_adjacency(vec![(0u32, vec![])], &[grid(0, 0)]);
256 let mut delta = DeltaEdgeSlab::new();
257 delta.remove_edge(VertexId(0), VertexId(1));
258 delta.add_edge(VertexId(0), VertexId(1));
259 let merged = delta.merged_view(&csr, VertexId(0));
260 assert_eq!(merged, vec![VertexId(1)]);
261 }
262
263 #[test]
264 fn test_dedup_additions_and_sorted() {
265 let csr = CsrEdges::from_adjacency(
266 vec![(0u32, vec![2u32])],
267 &[grid(0, 0), grid(0, 1), grid(0, 2)],
268 );
269 let mut delta = DeltaEdgeSlab::new();
270 delta.add_edge(VertexId(0), VertexId(1));
272 delta.add_edge(VertexId(0), VertexId(3));
273 delta.add_edge(VertexId(0), VertexId(1)); let merged = delta.merged_view(&csr, VertexId(0));
275 assert_eq!(merged, vec![VertexId(1), VertexId(2), VertexId(3)]);
277 }
278
279 #[test]
280 fn test_merged_in_view_add_and_remove() {
281 let csr = CsrEdges::from_adjacency(
282 vec![(0u32, vec![2u32]), (1u32, vec![2u32])],
283 &[grid(0, 0), grid(0, 1), grid(0, 2), grid(0, 3)],
284 );
285 let mut delta = DeltaEdgeSlab::new();
286
287 delta.add_edge(VertexId(3), VertexId(2));
289 delta.remove_edge(VertexId(0), VertexId(2));
290
291 let merged = delta.merged_in_view(&csr, VertexId(2));
292 assert_eq!(merged, vec![VertexId(1), VertexId(3)]);
293 }
294
295 #[test]
296 fn test_merged_in_view_last_op_wins() {
297 let csr = CsrEdges::from_adjacency(vec![(0u32, vec![1u32])], &[grid(0, 0), grid(0, 1)]);
298 let mut delta = DeltaEdgeSlab::new();
299
300 delta.remove_edge(VertexId(0), VertexId(1));
301 delta.add_edge(VertexId(0), VertexId(1));
302 assert_eq!(delta.merged_in_view(&csr, VertexId(1)), vec![VertexId(0)]);
303
304 delta.add_edge(VertexId(2), VertexId(1));
305 delta.remove_edge(VertexId(2), VertexId(1));
306 assert_eq!(delta.merged_in_view(&csr, VertexId(1)), vec![VertexId(0)]);
307 }
308
309 #[test]
310 fn test_end_batch_below_threshold_defers_rebuild() {
311 let mut edges = CsrMutableEdges::with_coords(vec![grid(0, 0), grid(0, 1), grid(0, 2)]);
312 let before = edges.rebuild_count();
313
314 edges.begin_batch();
315 edges.add_edge(VertexId(0), VertexId(1));
316 edges.add_edge(VertexId(0), VertexId(2));
317 edges.end_batch();
318
319 assert_eq!(edges.rebuild_count(), before);
321 assert_eq!(edges.out_edges(VertexId(0)), vec![VertexId(1), VertexId(2)]);
323 assert_eq!(edges.in_edges_merged(VertexId(1)), vec![VertexId(0)]);
324 assert_eq!(edges.in_edges_merged(VertexId(2)), vec![VertexId(0)]);
325 }
326
327 #[test]
328 fn test_end_batch_rebuilds_at_threshold() {
329 let coords: Vec<VertexAddr> = (0..1200u32).map(|i| grid(i, 0)).collect();
330 let mut edges = CsrMutableEdges::with_coords(coords);
331 let before = edges.rebuild_count();
332
333 edges.begin_batch();
334 for i in 0..1100u32 {
335 edges.add_edge(VertexId(i), VertexId(i + 1));
336 }
337 edges.end_batch();
338
339 assert_eq!(edges.rebuild_count(), before + 1);
340 assert_eq!(edges.delta_size(), 0);
341 assert_eq!(edges.out_edges(VertexId(0)), vec![VertexId(1)]);
342 assert_eq!(edges.in_edges(VertexId(1)), &[VertexId(0)]);
343 }
344
345 #[test]
346 fn test_add_vertex_defers_rebuild() {
347 let mut edges = CsrMutableEdges::new();
348 edges.add_vertex(grid(0, 0), 1024);
349 edges.add_vertex(grid(0, 1), 1025);
350 assert_eq!(edges.rebuild_count(), 0);
351
352 edges.add_edge(VertexId(1024), VertexId(1025));
353 assert_eq!(edges.out_edges(VertexId(1024)), vec![VertexId(1025)]);
355 assert_eq!(edges.in_edges_merged(VertexId(1025)), vec![VertexId(1024)]);
356
357 edges.rebuild();
359 assert_eq!(edges.out_edges(VertexId(1024)), vec![VertexId(1025)]);
360 assert_eq!(edges.in_edges(VertexId(1025)), &[VertexId(1024)]);
361 }
362
363 #[test]
364 fn test_end_batch_rebuilds_on_coord_change_only() {
365 let mut edges = CsrMutableEdges::with_coords(vec![grid(0, 0), grid(0, 1)]);
366 edges.begin_batch();
367 edges.update_addr(VertexId(0), grid(0, 2));
368 edges.end_batch();
370 assert_eq!(edges.out_edges(VertexId(0)), Vec::<VertexId>::new());
372 }
373}
374
375#[derive(Debug)]
380pub struct DeltaEdgeSlab {
381 additions: FxHashMap<VertexId, FxHashSet<VertexId>>,
383
384 removals: FxHashMap<VertexId, FxHashSet<VertexId>>,
386
387 additions_in: FxHashMap<VertexId, FxHashSet<VertexId>>,
390
391 removals_in: FxHashMap<VertexId, FxHashSet<VertexId>>,
393
394 op_count: usize,
396
397 coord_changed: bool,
399}
400
401impl DeltaEdgeSlab {
402 pub fn new() -> Self {
404 Self {
405 additions: FxHashMap::default(),
406 removals: FxHashMap::default(),
407 additions_in: FxHashMap::default(),
408 removals_in: FxHashMap::default(),
409 op_count: 0,
410 coord_changed: false,
411 }
412 }
413
414 fn reserve_additions(&mut self, additional: usize) {
415 self.additions.reserve(additional);
416 self.additions_in.reserve(additional);
417 }
418
419 pub fn add_edge(&mut self, from: VertexId, to: VertexId) {
421 if let Some(rem) = self.removals.get_mut(&from) {
423 rem.remove(&to);
424 }
425 if let Some(rem) = self.removals_in.get_mut(&to) {
426 rem.remove(&from);
427 }
428 self.additions.entry(from).or_default().insert(to);
430 self.additions_in.entry(to).or_default().insert(from);
431 self.op_count += 1;
432 }
433
434 pub fn remove_edge(&mut self, from: VertexId, to: VertexId) {
436 if let Some(adds) = self.additions.get_mut(&from) {
438 adds.remove(&to);
439 }
440 if let Some(adds) = self.additions_in.get_mut(&to) {
441 adds.remove(&from);
442 }
443 self.removals.entry(from).or_default().insert(to);
445 self.removals_in.entry(to).or_default().insert(from);
446 self.op_count += 1;
447 }
448
449 pub fn merged_view(&self, csr: &CsrEdges, v: VertexId) -> Vec<VertexId> {
451 let mut result: Vec<_> = csr.out_edges(v).to_vec();
453 if let Some(removes) = self.removals.get(&v) {
455 result.retain(|e| !removes.contains(e));
456 }
457 if let Some(adds) = self.additions.get(&v) {
459 result.extend(adds.iter().copied());
460 }
461 let mut seen: FxHashSet<VertexId> = FxHashSet::default();
463 result.retain(|e| seen.insert(*e));
464 result.sort_by_key(|e| e.0);
465 result
466 }
467
468 pub fn merged_in_view(&self, csr: &CsrEdges, v: VertexId) -> Vec<VertexId> {
471 let mut result: Vec<_> = csr.in_edges(v).to_vec();
472 if let Some(removes) = self.removals_in.get(&v) {
473 result.retain(|e| !removes.contains(e));
474 }
475 if let Some(adds) = self.additions_in.get(&v) {
476 result.extend(adds.iter().copied());
477 }
478 let mut seen: FxHashSet<VertexId> = FxHashSet::default();
479 result.retain(|e| seen.insert(*e));
480 result.sort_by_key(|e| e.0);
481 result
482 }
483
484 pub fn needs_rebuild(&self) -> bool {
486 self.op_count >= 1000 || self.coord_changed
487 }
488
489 pub fn mark_dirty(&mut self) {
491 self.coord_changed = true;
492 }
493
494 pub fn op_count(&self) -> usize {
496 self.op_count
497 }
498
499 pub fn clear(&mut self) {
501 self.additions.clear();
502 self.removals.clear();
503 self.additions_in.clear();
504 self.removals_in.clear();
505 self.op_count = 0;
506 self.coord_changed = false;
507 }
508
509 pub fn additions_iter(&self) -> impl Iterator<Item = (&VertexId, &FxHashSet<VertexId>)> {
513 self.additions.iter()
514 }
515
516 pub fn removals_for(&self, from: VertexId) -> impl Iterator<Item = VertexId> + '_ {
518 self.removals
519 .get(&from)
520 .into_iter()
521 .flat_map(|set| set.iter().copied())
522 }
523
524 pub fn apply_to_csr(
526 &self,
527 base: &CsrEdges,
528 coords: &[VertexAddr],
529 vertex_ids: &[u32],
530 ) -> CsrEdges {
531 let mut adjacency = Vec::with_capacity(vertex_ids.len());
532
533 for &vid in vertex_ids {
535 let v = VertexId(vid);
536 let merged = self.merged_view(base, v);
537
538 let targets: Vec<u32> = merged.into_iter().map(|id| id.0).collect();
540
541 adjacency.push((vid, targets));
542 }
543
544 CsrEdges::from_adjacency(adjacency, coords)
545 }
546}
547
548impl Default for DeltaEdgeSlab {
549 fn default() -> Self {
550 Self::new()
551 }
552}
553
554#[derive(Debug)]
559pub struct CsrMutableEdges {
560 base: CsrEdges,
562
563 delta: DeltaEdgeSlab,
565
566 coords: Vec<VertexAddr>,
568
569 vertex_ids: Vec<u32>,
571
572 vertex_pos: FxHashMap<u32, usize>,
574
575 batch_depth: usize,
577
578 rebuild_count: u64,
580}
581
582impl CsrMutableEdges {
583 pub fn new() -> Self {
585 Self {
586 base: CsrEdges::empty(),
587 delta: DeltaEdgeSlab::new(),
588 coords: Vec::new(),
589 vertex_ids: Vec::new(),
590 vertex_pos: FxHashMap::default(),
591 batch_depth: 0,
592 rebuild_count: 0,
593 }
594 }
595
596 pub fn with_coords(coords: Vec<VertexAddr>) -> Self {
598 let num_vertices = coords.len();
599 let vertex_ids: Vec<u32> = (0..num_vertices as u32).collect();
600 let adjacency: Vec<_> = vertex_ids.iter().map(|&id| (id, Vec::new())).collect();
601 let vertex_pos = vertex_ids
602 .iter()
603 .enumerate()
604 .map(|(idx, &id)| (id, idx))
605 .collect();
606
607 Self {
608 base: CsrEdges::from_adjacency(adjacency, &coords),
609 delta: DeltaEdgeSlab::new(),
610 coords,
611 vertex_ids,
612 vertex_pos,
613 batch_depth: 0,
614 rebuild_count: 0,
615 }
616 }
617
618 pub(crate) fn reserve_prepared_additions(&mut self, vertices: usize, edges: usize) {
619 self.coords.reserve(vertices);
620 self.vertex_ids.reserve(vertices);
621 self.vertex_pos.reserve(vertices);
622 self.delta.reserve_additions(edges);
623 }
624
625 pub fn add_edge(&mut self, from: VertexId, to: VertexId) {
627 self.delta.add_edge(from, to);
628 self.maybe_rebuild();
629 }
630
631 pub fn remove_edge(&mut self, from: VertexId, to: VertexId) {
633 self.delta.remove_edge(from, to);
634 self.maybe_rebuild();
635 }
636
637 pub fn out_edges(&self, v: VertexId) -> Vec<VertexId> {
639 if self.delta.op_count() == 0 {
640 self.base.out_edges(v).to_vec()
641 } else {
642 self.delta.merged_view(&self.base, v)
643 }
644 }
645
646 #[inline]
650 pub fn out_edges_ref(&self, v: VertexId) -> Option<&[VertexId]> {
651 if self.delta.op_count() == 0 {
652 Some(self.base.out_edges(v))
653 } else {
654 None
655 }
656 }
657
658 pub fn in_edges(&self, v: VertexId) -> &[VertexId] {
661 self.base.in_edges(v)
662 }
663
664 pub fn in_edges_merged(&self, v: VertexId) -> Vec<VertexId> {
668 if self.delta.op_count() == 0 {
669 self.base.in_edges(v).to_vec()
670 } else {
671 self.delta.merged_in_view(&self.base, v)
672 }
673 }
674
675 #[inline]
679 pub fn in_edges_ref(&self, v: VertexId) -> Option<&[VertexId]> {
680 if self.delta.op_count() == 0 {
681 Some(self.base.in_edges(v))
682 } else {
683 None
684 }
685 }
686
687 pub(crate) fn visit_in_edges_bounded(
697 &self,
698 v: VertexId,
699 remaining_work: &mut u64,
700 visitor: &mut dyn FnMut(VertexId) -> bool,
701 ) -> bool {
702 let removals = self.delta.removals_in.get(&v);
703 for &source in self.base.in_edges(v) {
704 if *remaining_work == 0 {
705 return false;
706 }
707 *remaining_work -= 1;
708 if removals.is_some_and(|set| set.contains(&source)) {
709 continue;
710 }
711 if !visitor(source) {
712 return false;
713 }
714 }
715 if let Some(additions) = self.delta.additions_in.get(&v) {
716 for &source in additions {
717 if *remaining_work == 0 {
718 return false;
719 }
720 *remaining_work -= 1;
721 if !visitor(source) {
722 return false;
723 }
724 }
725 }
726 true
727 }
728
729 pub fn delta_size(&self) -> usize {
731 self.delta.op_count()
732 }
733
734 pub fn num_edges_exact(&self) -> usize {
741 if self.delta.op_count() == 0 {
742 return self.base.num_edges();
743 }
744
745 self.vertex_ids
746 .iter()
747 .map(|&id| self.out_edges(VertexId(id)).len())
748 .sum()
749 }
750
751 pub fn rebuild(&mut self) {
753 if self.delta.op_count() > 0 || self.delta.needs_rebuild() {
754 self.base = self
755 .delta
756 .apply_to_csr(&self.base, &self.coords, &self.vertex_ids);
757 self.delta.clear();
758 self.rebuild_count += 1;
759 }
760 }
761
762 pub fn rebuild_count(&self) -> u64 {
767 self.rebuild_count
768 }
769
770 fn maybe_rebuild(&mut self) {
772 if self.batch_depth == 0 && self.delta.needs_rebuild() {
773 self.rebuild();
774 }
775 }
776
777 pub fn begin_batch(&mut self) {
779 self.batch_depth = self.batch_depth.saturating_add(1);
780 }
781
782 pub fn end_batch(&mut self) {
790 self.batch_depth = self.batch_depth.saturating_sub(1);
791 if self.batch_depth == 0 && self.delta.needs_rebuild() {
792 self.rebuild();
793 }
794 }
795
796 pub(crate) fn end_batch_deferred(&mut self) {
799 self.batch_depth = self.batch_depth.saturating_sub(1);
800 }
801
802 pub fn add_vertex(&mut self, addr: VertexAddr, vertex_id: u32) -> usize {
809 let idx = self.coords.len();
810 self.coords.push(addr);
811 self.vertex_ids.push(vertex_id);
812 self.vertex_pos.insert(vertex_id, idx);
813 idx
814 }
815
816 pub fn add_vertices_batch(&mut self, items: &[(VertexAddr, u32)]) {
818 if items.is_empty() {
819 return;
820 }
821 let start_len = self.coords.len();
822 self.coords.reserve(items.len());
823 self.vertex_ids.reserve(items.len());
824 for (addr, vid) in items {
825 let idx = self.coords.len();
826 self.coords.push(*addr);
827 self.vertex_ids.push(*vid);
828 self.vertex_pos.insert(*vid, idx);
829 }
830 self.rebuild();
832 debug_assert_eq!(self.coords.len(), start_len + items.len());
833 }
834
835 pub fn update_addr(&mut self, vertex_id: VertexId, new_addr: VertexAddr) {
838 if let Some(&pos) = self.vertex_pos.get(&vertex_id.0) {
839 debug_assert_eq!(
840 self.vertex_ids[pos], vertex_id.0,
841 "vertex_pos out of sync with vertex_ids at position {pos}"
842 );
843 self.coords[pos] = new_addr;
844 self.delta.mark_dirty();
846 }
847 }
848
849 pub fn adjacency_with_carried_forward_edges(
864 &self,
865 mut adjacency: Vec<(u32, Vec<u32>)>,
866 ) -> Vec<(u32, Vec<u32>)> {
867 let covered: FxHashSet<u32> = adjacency.iter().map(|(vid, _)| *vid).collect();
868 for &vid in &self.vertex_ids {
872 if covered.contains(&vid) {
873 continue;
874 }
875 let v = VertexId(vid);
876 let merged = if self.delta.op_count() == 0 {
878 self.base.out_edges(v).to_vec()
879 } else {
880 self.delta.merged_view(&self.base, v)
881 };
882 if !merged.is_empty() {
883 adjacency.push((vid, merged.into_iter().map(|v| v.0).collect()));
884 }
885 }
886 for (&from, adds) in self.delta.additions_iter() {
891 if covered.contains(&from.0) {
892 continue;
893 }
894 if adjacency.iter().any(|(v, _)| *v == from.0) {
895 continue;
896 }
897 let removals: FxHashSet<u32> = self.delta.removals_for(from).map(|v| v.0).collect();
898 let mut base_set: FxHashSet<u32> =
899 self.base.out_edges(from).iter().map(|v| v.0).collect();
900 for r in &removals {
901 base_set.remove(r);
902 }
903 for &add in adds {
904 base_set.insert(add.0);
905 }
906 if !base_set.is_empty() {
907 let mut targets: Vec<u32> = base_set.into_iter().collect();
908 targets.sort_unstable();
909 adjacency.push((from.0, targets));
910 }
911 }
912 adjacency
913 }
914
915 pub fn build_from_adjacency(
923 &mut self,
924 adjacency: Vec<(u32, Vec<u32>)>,
925 coords: Vec<VertexAddr>,
926 vertex_ids: Vec<u32>,
927 ) {
928 self.base = CsrEdges::from_adjacency(adjacency, &coords);
929 self.coords = coords;
930 self.vertex_ids = vertex_ids;
931 self.vertex_pos = self
932 .vertex_ids
933 .iter()
934 .enumerate()
935 .map(|(idx, &id)| (id, idx))
936 .collect();
937 self.delta.clear();
938 }
939}
940
941impl Default for CsrMutableEdges {
942 fn default() -> Self {
943 Self::new()
944 }
945}