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 }