From c862641ee9b677b106a05ec7a5458e062596186f Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 30 Mar 2022 09:29:51 -0400 Subject: [PATCH] cmd/digraph: only print non-trivial sccs In 'digraph sccs', do not print trivial single-node components. This is more useful because it distinguishes nodes with self-loops (printed) from nodes without (not printed). Also remove an unnecessary TODO about map[string]bool vs map[string]struct{}. The savings is at most 5% and if we really cared about storage we would probably not use a map at all. Change-Id: I6049b3c0f99a913c65f08c6c40e77ae99d1ba8c4 Reviewed-on: https://go-review.googlesource.com/c/tools/+/396834 Reviewed-by: Russ Cox TryBot-Result: Gopher Robot Auto-Submit: Dmitri Shuralyov gopls-CI: kokoro Reviewed-by: Dmitri Shuralyov Run-TryBot: Dmitri Shuralyov Reviewed-by: Roland Shoemaker --- cmd/digraph/digraph.go | 17 ++++++++++++++--- cmd/digraph/digraph_test.go | 3 ++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/cmd/digraph/digraph.go b/cmd/digraph/digraph.go index 1e9e095d04..62cb08d23a 100644 --- a/cmd/digraph/digraph.go +++ b/cmd/digraph/digraph.go @@ -122,7 +122,8 @@ The support commands are: allpaths the set of nodes on all paths from the first node to the second sccs - all strongly connected components (one per line) + all non-trivial strongly connected components, one per line + (single-node components are only printed for nodes with self-loops) scc the set of nodes nodes strongly connected to the specified one focus @@ -158,7 +159,7 @@ func (l nodelist) println(sep string) { fmt.Fprintln(stdout) } -type nodeset map[string]bool // TODO(deklerk): change bool to struct to reduce memory footprint +type nodeset map[string]bool func (s nodeset) sort() nodelist { nodes := make(nodelist, len(s)) @@ -266,6 +267,9 @@ func (g graph) sccs() []nodeset { if !seen[top] { scc = make(nodeset) rvisit(top) + if len(scc) == 1 && !g[top][top] { + continue + } sccs = append(sccs, scc) } } @@ -365,7 +369,7 @@ func parse(rd io.Reader) (graph, error) { return g, nil } -// Overridable for testing purposes. +// Overridable for redirection. var stdin io.Reader = os.Stdin var stdout io.Writer = os.Stdout @@ -485,9 +489,16 @@ func digraph(cmd string, args []string) error { if len(args) != 0 { return fmt.Errorf("usage: digraph sccs") } + buf := new(bytes.Buffer) + oldStdout := stdout + stdout = buf for _, scc := range g.sccs() { scc.sort().println(" ") } + lines := strings.SplitAfter(buf.String(), "\n") + sort.Strings(lines) + stdout = oldStdout + io.WriteString(stdout, strings.Join(lines, "")) case "scc": if len(args) != 1 { diff --git a/cmd/digraph/digraph_test.go b/cmd/digraph/digraph_test.go index 1746fcaa69..cff46735b2 100644 --- a/cmd/digraph/digraph_test.go +++ b/cmd/digraph/digraph_test.go @@ -27,6 +27,7 @@ a b c b d c d d c +e e ` for _, test := range []struct { @@ -41,7 +42,7 @@ d c {"transpose", g1, "transpose", nil, "belt pants\njacket sweater\npants shorts\nshoes pants\nshoes socks\nsweater shirt\ntie shirt\n"}, {"forward", g1, "forward", []string{"socks"}, "shoes\nsocks\n"}, {"forward multiple args", g1, "forward", []string{"socks", "sweater"}, "jacket\nshoes\nsocks\nsweater\n"}, - {"scss", g2, "sccs", nil, "a\nb\nc d\n"}, + {"scss", g2, "sccs", nil, "c d\ne\n"}, {"scc", g2, "scc", []string{"d"}, "c\nd\n"}, {"succs", g2, "succs", []string{"a"}, "b\nc\n"}, {"preds", g2, "preds", []string{"c"}, "a\nd\n"},