Walk through a dead vertex's forward edges to find live successors. In the additive edge model there are no PSEUDO reconnection edges, so the alive-graph traversal must dynamically skip over dead vertices to find live descendants. This walks the dead vertex's BLOCK edges (including deleted ones, since we may need to skip multiple dead vertices in a row) and pushes any live destinations onto the
(
txn: &T,
options: &RetrieveOptions,
owner_parent: GraphNode<NodeId>,
dead_vertex: GraphNode<NodeId>,
stack: &mut Vec<VertexId>,
cache: &mut std::collections::HashMap<GraphNod
| 425 | /// list so they appear as proper graph children of the upstream alive |
| 426 | /// parent. |
| 427 | fn walk_through_dead<T: GraphTxnT>( |
| 428 | txn: &T, |
| 429 | options: &RetrieveOptions, |
| 430 | owner_parent: GraphNode<NodeId>, |
| 431 | dead_vertex: GraphNode<NodeId>, |
| 432 | stack: &mut Vec<VertexId>, |
| 433 | cache: &mut std::collections::HashMap<GraphNode<NodeId>, VertexId>, |
| 434 | result: &mut RetrieveResult, |
| 435 | ) -> Result<Vec<VertexId>, PristineError> { |
| 436 | use std::collections::HashSet; |
| 437 | let mut live_successors: Vec<VertexId> = Vec::new(); |
| 438 | |
| 439 | // BFS through dead vertices, recording live ones we encounter. |
| 440 | // |
| 441 | // We track three sets separately: |
| 442 | // * `dead_visited` — dead vertices we have walked through. This is |
| 443 | // the set that an "alive outsider" check should consult: a dead |
| 444 | // vertex `D` is claimed by some alive parent `P` only when `P` is |
| 445 | // NOT in this set (i.e. `P` is not part of the dead chain). |
| 446 | // * `alive_found` — alive vertices reached during this walk. We |
| 447 | // remember them only to avoid pushing the same one to the graph |
| 448 | // twice, but they must NOT mask the alive-outsider check. |
| 449 | // * `seen` — union, used purely to skip re-visiting the |
| 450 | // same vertex during BFS. |
| 451 | let mut dead_visited: HashSet<GraphNode<NodeId>> = HashSet::new(); |
| 452 | let mut seen: HashSet<GraphNode<NodeId>> = HashSet::new(); |
| 453 | let mut queue: Vec<GraphNode<NodeId>> = vec![dead_vertex]; |
| 454 | seen.insert(dead_vertex); |
| 455 | dead_visited.insert(dead_vertex); |
| 456 | |
| 457 | while let Some(current) = queue.pop() { |
| 458 | // Walk current's forward edges (include deleted so we can chain |
| 459 | // through multiple dead vertices in a row). |
| 460 | let edges = txn.iter_forward(current, true)?; |
| 461 | |
| 462 | for edge in edges { |
| 463 | let next_pos = edge.dest; |
| 464 | let next_vertex = match txn.find_block(next_pos) { |
| 465 | Ok(v) => v, |
| 466 | Err(_) => continue, |
| 467 | }; |
| 468 | |
| 469 | if seen.contains(&next_vertex) { |
| 470 | continue; |
| 471 | } |
| 472 | seen.insert(next_vertex); |
| 473 | |
| 474 | // Check change filter |
| 475 | if !options.passes_filter(next_vertex.change) { |
| 476 | continue; |
| 477 | } |
| 478 | |
| 479 | // Is this vertex alive in our view? |
| 480 | let alive = if options.has_filter() { |
| 481 | options.is_vertex_alive(txn, next_vertex)? |
| 482 | } else { |
| 483 | classify::is_vertex_alive(txn, &next_vertex)? |
| 484 | }; |
no test coverage detected