@@ -231,20 +231,30 @@ where
231
231
232
232
let scc_indices = ( 0 ..num_nodes)
233
233
. map ( G :: Node :: new)
234
- . map ( |node| match this. walk_node ( 0 , node) {
234
+ . map ( |node| match this. start_walk_from ( node) {
235
235
WalkReturn :: Complete { scc_index } => scc_index,
236
- WalkReturn :: Cycle { min_depth } => {
237
- panic ! ( "`walk_node(0, {:?})` returned cycle with depth {:?}" , node, min_depth)
238
- }
236
+ WalkReturn :: Cycle { min_depth } => panic ! (
237
+ "`start_walk_node({:?})` returned cycle with depth {:?}" ,
238
+ node, min_depth
239
+ ) ,
239
240
} )
240
241
. collect ( ) ;
241
242
242
243
Sccs { scc_indices, scc_data : this. scc_data }
243
244
}
244
245
245
- /// Visits a node during the DFS. We first examine its current
246
- /// state -- if it is not yet visited (`NotVisited`), we can push
247
- /// it onto the stack and start walking its successors.
246
+ fn start_walk_from ( & mut self , node : G :: Node ) -> WalkReturn < S > {
247
+ if let Some ( result) = self . inspect_node ( node) {
248
+ result
249
+ } else {
250
+ self . walk_unvisited_node ( node)
251
+ }
252
+ }
253
+
254
+ /// Inspect a node during the DFS. We first examine its current
255
+ /// state -- if it is not yet visited (`NotVisited`), return `None` so
256
+ /// that the caller might push it onto the stack and start walking its
257
+ /// successors.
248
258
///
249
259
/// If it is already on the DFS stack it will be in the state
250
260
/// `BeingVisited`. In that case, we have found a cycle and we
@@ -253,20 +263,19 @@ where
253
263
/// Otherwise, we are looking at a node that has already been
254
264
/// completely visited. We therefore return `WalkReturn::Complete`
255
265
/// with its associated SCC index.
256
- fn walk_node ( & mut self , depth : usize , node : G :: Node ) -> WalkReturn < S > {
257
- debug ! ( "walk_node(depth = {:?}, node = {:?})" , depth, node) ;
258
- match self . find_state ( node) {
266
+ fn inspect_node ( & mut self , node : G :: Node ) -> Option < WalkReturn < S > > {
267
+ Some ( match self . find_state ( node) {
259
268
NodeState :: InCycle { scc_index } => WalkReturn :: Complete { scc_index } ,
260
269
261
270
NodeState :: BeingVisited { depth : min_depth } => WalkReturn :: Cycle { min_depth } ,
262
271
263
- NodeState :: NotVisited => self . walk_unvisited_node ( depth , node ) ,
272
+ NodeState :: NotVisited => return None ,
264
273
265
274
NodeState :: InCycleWith { parent } => panic ! (
266
275
"`find_state` returned `InCycleWith({:?})`, which ought to be impossible" ,
267
276
parent
268
277
) ,
269
- }
278
+ } )
270
279
}
271
280
272
281
/// Fetches the state of the node `r`. If `r` is recorded as being
@@ -392,74 +401,174 @@ where
392
401
}
393
402
394
403
/// Walks a node that has never been visited before.
395
- fn walk_unvisited_node ( & mut self , depth : usize , node : G :: Node ) -> WalkReturn < S > {
396
- debug ! ( "walk_unvisited_node(depth = {:?}, node = {:?})" , depth, node) ;
397
-
398
- debug_assert ! ( matches!( self . node_states[ node] , NodeState :: NotVisited ) ) ;
399
-
400
- // Push `node` onto the stack.
401
- self . node_states [ node] = NodeState :: BeingVisited { depth } ;
402
- self . node_stack . push ( node) ;
403
-
404
- // Walk each successor of the node, looking to see if any of
405
- // them can reach a node that is presently on the stack. If
406
- // so, that means they can also reach us.
407
- let mut min_depth = depth;
408
- let mut min_cycle_root = node;
409
- let successors_len = self . successors_stack . len ( ) ;
410
- for successor_node in self . graph . successors ( node) {
411
- debug ! ( "walk_unvisited_node: node = {:?} successor_ode = {:?}" , node, successor_node) ;
412
- match self . walk_node ( depth + 1 , successor_node) {
413
- WalkReturn :: Cycle { min_depth : successor_min_depth } => {
414
- // Track the minimum depth we can reach.
415
- assert ! ( successor_min_depth <= depth) ;
416
- if successor_min_depth < min_depth {
404
+ ///
405
+ /// Call this method when `inspect_node` has returned `None`. Having the
406
+ /// caller decide avoids mutual recursion between the two methods and allows
407
+ /// us to maintain an allocated stack for nodes on the path between calls.
408
+ fn walk_unvisited_node ( & mut self , initial : G :: Node ) -> WalkReturn < S > {
409
+ struct VisitingNodeFrame < G : DirectedGraph , Successors > {
410
+ node : G :: Node ,
411
+ iter : Option < Successors > ,
412
+ depth : usize ,
413
+ min_depth : usize ,
414
+ successors_len : usize ,
415
+ min_cycle_root : G :: Node ,
416
+ successor_node : G :: Node ,
417
+ }
418
+
419
+ // Move the stack to a local variable. We want to utilize the existing allocation and
420
+ // mutably borrow it without borrowing self at the same time.
421
+ let mut successors_stack = core:: mem:: take ( & mut self . successors_stack ) ;
422
+ debug_assert_eq ! ( successors_stack. len( ) , 0 ) ;
423
+
424
+ let mut stack: Vec < VisitingNodeFrame < G , _ > > = vec ! [ VisitingNodeFrame {
425
+ node: initial,
426
+ depth: 0 ,
427
+ min_depth: 0 ,
428
+ iter: None ,
429
+ successors_len: 0 ,
430
+ min_cycle_root: initial,
431
+ successor_node: initial,
432
+ } ] ;
433
+
434
+ let mut return_value = None ;
435
+
436
+ ' recurse: while let Some ( frame) = stack. last_mut ( ) {
437
+ let VisitingNodeFrame {
438
+ node,
439
+ depth,
440
+ iter,
441
+ successors_len,
442
+ min_depth,
443
+ min_cycle_root,
444
+ successor_node,
445
+ } = frame;
446
+
447
+ let node = * node;
448
+ let depth = * depth;
449
+
450
+ let successors = match iter {
451
+ Some ( iter) => iter,
452
+ None => {
453
+ // This None marks that we still have the initialize this node's frame.
454
+ debug ! ( "walk_unvisited_node(depth = {:?}, node = {:?})" , depth, node) ;
455
+
456
+ debug_assert ! ( matches!( self . node_states[ node] , NodeState :: NotVisited ) ) ;
457
+
458
+ // Push `node` onto the stack.
459
+ self . node_states [ node] = NodeState :: BeingVisited { depth } ;
460
+ self . node_stack . push ( node) ;
461
+
462
+ // Walk each successor of the node, looking to see if any of
463
+ // them can reach a node that is presently on the stack. If
464
+ // so, that means they can also reach us.
465
+ * successors_len = successors_stack. len ( ) ;
466
+ // Set and return a reference, this is currently empty.
467
+ iter. get_or_insert ( self . graph . successors ( node) )
468
+ }
469
+ } ;
470
+
471
+ // Now that iter is initialized, this is a constant for this frame.
472
+ let successors_len = * successors_len;
473
+
474
+ // Construct iterators for the nodes and walk results. There are two cases:
475
+ // * The walk of a successor node returned.
476
+ // * The remaining successor nodes.
477
+ let returned_walk =
478
+ return_value. take ( ) . into_iter ( ) . map ( |walk| ( * successor_node, Some ( walk) ) ) ;
479
+
480
+ let successor_walk = successors. by_ref ( ) . map ( |successor_node| {
481
+ debug ! (
482
+ "walk_unvisited_node: node = {:?} successor_ode = {:?}" ,
483
+ node, successor_node
484
+ ) ;
485
+ ( successor_node, self . inspect_node ( successor_node) )
486
+ } ) ;
487
+
488
+ for ( successor_node, walk) in returned_walk. chain ( successor_walk) {
489
+ match walk {
490
+ Some ( WalkReturn :: Cycle { min_depth : successor_min_depth } ) => {
491
+ // Track the minimum depth we can reach.
492
+ assert ! ( successor_min_depth <= depth) ;
493
+ if successor_min_depth < * min_depth {
494
+ debug ! (
495
+ "walk_unvisited_node: node = {:?} successor_min_depth = {:?}" ,
496
+ node, successor_min_depth
497
+ ) ;
498
+ * min_depth = successor_min_depth;
499
+ * min_cycle_root = successor_node;
500
+ }
501
+ }
502
+
503
+ Some ( WalkReturn :: Complete { scc_index : successor_scc_index } ) => {
504
+ // Push the completed SCC indices onto
505
+ // the `successors_stack` for later.
417
506
debug ! (
418
- "walk_unvisited_node: node = {:?} successor_min_depth = {:?}" ,
419
- node, successor_min_depth
507
+ "walk_unvisited_node: node = {:?} successor_scc_index = {:?}" ,
508
+ node, successor_scc_index
420
509
) ;
421
- min_depth = successor_min_depth;
422
- min_cycle_root = successor_node;
510
+ successors_stack. push ( successor_scc_index) ;
423
511
}
424
- }
425
512
426
- WalkReturn :: Complete { scc_index : successor_scc_index } => {
427
- // Push the completed SCC indices onto
428
- // the `successors_stack` for later.
429
- debug ! (
430
- "walk_unvisited_node: node = {:?} successor_scc_index = {:?}" ,
431
- node, successor_scc_index
432
- ) ;
433
- self . successors_stack . push ( successor_scc_index) ;
513
+ None => {
514
+ let depth = depth + 1 ;
515
+ debug ! ( "walk_node(depth = {:?}, node = {:?})" , depth, successor_node) ;
516
+ // Remember which node the return value will come from.
517
+ frame. successor_node = successor_node;
518
+ // Start a new stack frame the step into it.
519
+ stack. push ( VisitingNodeFrame {
520
+ node : successor_node,
521
+ depth,
522
+ iter : None ,
523
+ successors_len : 0 ,
524
+ min_depth : depth,
525
+ min_cycle_root : successor_node,
526
+ successor_node : successor_node,
527
+ } ) ;
528
+ continue ' recurse;
529
+ }
434
530
}
435
531
}
436
- }
437
532
438
- // Completed walk, remove `node` from the stack.
439
- let r = self . node_stack . pop ( ) ;
440
- debug_assert_eq ! ( r, Some ( node) ) ;
441
-
442
- // If `min_depth == depth`, then we are the root of the
443
- // cycle: we can't reach anyone further down the stack.
444
- if min_depth == depth {
445
- // Note that successor stack may have duplicates, so we
446
- // want to remove those:
447
- let deduplicated_successors = {
448
- let duplicate_set = & mut self . duplicate_set ;
449
- duplicate_set. clear ( ) ;
450
- self . successors_stack
451
- . drain ( successors_len..)
452
- . filter ( move |& i| duplicate_set. insert ( i) )
453
- } ;
454
- let scc_index = self . scc_data . create_scc ( deduplicated_successors) ;
455
- self . node_states [ node] = NodeState :: InCycle { scc_index } ;
456
- WalkReturn :: Complete { scc_index }
457
- } else {
458
- // We are not the head of the cycle. Return back to our
459
- // caller. They will take ownership of the
460
- // `self.successors` data that we pushed.
461
- self . node_states [ node] = NodeState :: InCycleWith { parent : min_cycle_root } ;
462
- WalkReturn :: Cycle { min_depth }
533
+ // Completed walk, remove `node` from the stack.
534
+ let r = self . node_stack . pop ( ) ;
535
+ debug_assert_eq ! ( r, Some ( node) ) ;
536
+
537
+ // Remove the frame, it's done.
538
+ let frame = stack. pop ( ) . unwrap ( ) ;
539
+
540
+ // If `min_depth == depth`, then we are the root of the
541
+ // cycle: we can't reach anyone further down the stack.
542
+
543
+ // Pass the 'return value' down the stack.
544
+ // We return one frame at a time so there can't be another return value.
545
+ debug_assert ! ( return_value. is_none( ) ) ;
546
+ return_value = Some ( if frame. min_depth == depth {
547
+ // Note that successor stack may have duplicates, so we
548
+ // want to remove those:
549
+ let deduplicated_successors = {
550
+ let duplicate_set = & mut self . duplicate_set ;
551
+ duplicate_set. clear ( ) ;
552
+ successors_stack
553
+ . drain ( successors_len..)
554
+ . filter ( move |& i| duplicate_set. insert ( i) )
555
+ } ;
556
+ let scc_index = self . scc_data . create_scc ( deduplicated_successors) ;
557
+ self . node_states [ node] = NodeState :: InCycle { scc_index } ;
558
+ WalkReturn :: Complete { scc_index }
559
+ } else {
560
+ // We are not the head of the cycle. Return back to our
561
+ // caller. They will take ownership of the
562
+ // `self.successors` data that we pushed.
563
+ self . node_states [ node] = NodeState :: InCycleWith { parent : frame. min_cycle_root } ;
564
+ WalkReturn :: Cycle { min_depth : frame. min_depth }
565
+ } ) ;
463
566
}
567
+
568
+ // Keep the allocation we used for successors_stack.
569
+ self . successors_stack = successors_stack;
570
+ debug_assert_eq ! ( self . successors_stack. len( ) , 0 ) ;
571
+
572
+ return_value. unwrap ( )
464
573
}
465
574
}
0 commit comments