blob: 95050d73aa0be2738c74374b07d5a24bd6c7af19 [file] [log] [blame]
// 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
}