github.com/hashicorp/packer@v1.14.3/internal/dag/tarjan.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package dag
     5  
     6  // StronglyConnected returns the list of strongly connected components
     7  // within the Graph g. This information is primarily used by this package
     8  // for cycle detection, but strongly connected components have widespread
     9  // use.
    10  func StronglyConnected(g *Graph) [][]Vertex {
    11  	vs := g.Vertices()
    12  	acct := sccAcct{
    13  		NextIndex:   1,
    14  		VertexIndex: make(map[Vertex]int, len(vs)),
    15  	}
    16  	for _, v := range vs {
    17  		// Recurse on any non-visited nodes
    18  		if acct.VertexIndex[v] == 0 {
    19  			stronglyConnected(&acct, g, v)
    20  		}
    21  	}
    22  	return acct.SCC
    23  }
    24  
    25  func stronglyConnected(acct *sccAcct, g *Graph, v Vertex) int {
    26  	// Initial vertex visit
    27  	index := acct.visit(v)
    28  	minIdx := index
    29  
    30  	for _, raw := range g.downEdgesNoCopy(v) {
    31  		target := raw.(Vertex)
    32  		targetIdx := acct.VertexIndex[target]
    33  
    34  		// Recurse on successor if not yet visited
    35  		if targetIdx == 0 {
    36  			minIdx = min(minIdx, stronglyConnected(acct, g, target))
    37  		} else if acct.inStack(target) {
    38  			// Check if the vertex is in the stack
    39  			minIdx = min(minIdx, targetIdx)
    40  		}
    41  	}
    42  
    43  	// Pop the strongly connected components off the stack if
    44  	// this is a root vertex
    45  	if index == minIdx {
    46  		var scc []Vertex
    47  		for {
    48  			v2 := acct.pop()
    49  			scc = append(scc, v2)
    50  			if v2 == v {
    51  				break
    52  			}
    53  		}
    54  
    55  		acct.SCC = append(acct.SCC, scc)
    56  	}
    57  
    58  	return minIdx
    59  }
    60  
    61  // sccAcct is used ot pass around accounting information for
    62  // the StronglyConnectedComponents algorithm
    63  type sccAcct struct {
    64  	NextIndex   int
    65  	VertexIndex map[Vertex]int
    66  	Stack       []Vertex
    67  	SCC         [][]Vertex
    68  }
    69  
    70  // visit assigns an index and pushes a vertex onto the stack
    71  func (s *sccAcct) visit(v Vertex) int {
    72  	idx := s.NextIndex
    73  	s.VertexIndex[v] = idx
    74  	s.NextIndex++
    75  	s.push(v)
    76  	return idx
    77  }
    78  
    79  // push adds a vertex to the stack
    80  func (s *sccAcct) push(n Vertex) {
    81  	s.Stack = append(s.Stack, n)
    82  }
    83  
    84  // pop removes a vertex from the stack
    85  func (s *sccAcct) pop() Vertex {
    86  	n := len(s.Stack)
    87  	if n == 0 {
    88  		return nil
    89  	}
    90  	vertex := s.Stack[n-1]
    91  	s.Stack = s.Stack[:n-1]
    92  	return vertex
    93  }
    94  
    95  // inStack checks if a vertex is in the stack
    96  func (s *sccAcct) inStack(needle Vertex) bool {
    97  	for _, n := range s.Stack {
    98  		if n == needle {
    99  			return true
   100  		}
   101  	}
   102  	return false
   103  }