@@ -231,20 +231,30 @@ where
231231
232232 let scc_indices = ( 0 ..num_nodes)
233233 . map ( G :: Node :: new)
234- . map ( |node| match this. walk_node ( 0 , node) {
234+ . map ( |node| match this. start_walk_from ( node) {
235235 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+ ) ,
239240 } )
240241 . collect ( ) ;
241242
242243 Sccs { scc_indices, scc_data : this. scc_data }
243244 }
244245
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.
248258 ///
249259 /// If it is already on the DFS stack it will be in the state
250260 /// `BeingVisited`. In that case, we have found a cycle and we
@@ -253,20 +263,19 @@ where
253263 /// Otherwise, we are looking at a node that has already been
254264 /// completely visited. We therefore return `WalkReturn::Complete`
255265 /// 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) {
259268 NodeState :: InCycle { scc_index } => WalkReturn :: Complete { scc_index } ,
260269
261270 NodeState :: BeingVisited { depth : min_depth } => WalkReturn :: Cycle { min_depth } ,
262271
263- NodeState :: NotVisited => self . walk_unvisited_node ( depth , node ) ,
272+ NodeState :: NotVisited => return None ,
264273
265274 NodeState :: InCycleWith { parent } => panic ! (
266275 "`find_state` returned `InCycleWith({:?})`, which ought to be impossible" ,
267276 parent
268277 ) ,
269- }
278+ } )
270279 }
271280
272281 /// Fetches the state of the node `r`. If `r` is recorded as being
@@ -392,74 +401,174 @@ where
392401 }
393402
394403 /// 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.
417506 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
420509 ) ;
421- min_depth = successor_min_depth;
422- min_cycle_root = successor_node;
510+ successors_stack. push ( successor_scc_index) ;
423511 }
424- }
425512
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+ }
434530 }
435531 }
436- }
437532
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+ } ) ;
463566 }
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 ( )
464573 }
465574}
0 commit comments