| // Copyright 2025 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package splitpkg |
| |
| // SCC algorithm stolen from cmd/digraph. |
| |
| type ( |
| graph = map[int]map[int]bool |
| nodeList = []int |
| nodeSet = map[int]bool |
| ) |
| |
| // addNode ensures a node exists in the graph with an initialized edge set. |
| func addNode(g graph, node int) map[int]bool { |
| edges := g[node] |
| if edges == nil { |
| edges = make(map[int]bool) |
| g[node] = edges |
| } |
| return edges |
| } |
| |
| // addEdges adds one or more edges from a 'from' node. |
| func addEdges(g graph, from int, to ...int) { |
| edges := addNode(g, from) |
| for _, toNode := range to { |
| addNode(g, toNode) |
| edges[toNode] = true |
| } |
| } |
| |
| // transpose creates the transpose (reverse) of the graph. |
| func transpose(g graph) graph { |
| rev := make(graph) |
| for node, edges := range g { |
| addNode(rev, node) // Ensure all nodes exist in the transposed graph |
| for succ := range edges { |
| addEdges(rev, succ, node) |
| } |
| } |
| return rev |
| } |
| |
| // sccs returns the non-trivial strongly connected components of the graph. |
| func sccs(g graph) []nodeSet { |
| // Kosaraju's algorithm---Tarjan is overkill here. |
| // |
| // TODO(adonovan): factor with Tarjan's algorithms from |
| // go/ssa/dom.go, |
| // go/callgraph/vta/propagation.go, |
| // ../../cache/typerefs/refs.go, |
| // ../../cache/metadata/graph.go. |
| |
| // Forward pass. |
| S := make(nodeList, 0, len(g)) // postorder stack |
| seen := make(nodeSet) |
| var visit func(node int) |
| visit = func(node int) { |
| if !seen[node] { |
| seen[node] = true |
| for e := range g[node] { |
| visit(e) |
| } |
| S = append(S, node) |
| } |
| } |
| for node := range g { |
| visit(node) |
| } |
| |
| // Reverse pass. |
| rev := transpose(g) |
| var scc nodeSet |
| seen = make(nodeSet) |
| var rvisit func(node int) |
| rvisit = func(node int) { |
| if !seen[node] { |
| seen[node] = true |
| scc[node] = true |
| for e := range rev[node] { |
| rvisit(e) |
| } |
| } |
| } |
| var sccs []nodeSet |
| for len(S) > 0 { |
| top := S[len(S)-1] |
| S = S[:len(S)-1] // pop |
| if !seen[top] { |
| scc = make(nodeSet) |
| rvisit(top) |
| if len(scc) == 1 && !g[top][top] { |
| continue |
| } |
| sccs = append(sccs, scc) |
| } |
| } |
| return sccs |
| } |