Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/pkg/flow/internal/dag/dag.go
4095 views
1
// Package dag defines a Directed Acyclic Graph.
2
package dag
3
4
import "fmt"
5
6
// Node is an individual Vertex in the DAG.
7
type Node interface {
8
// NodeID returns the display name of the Node.
9
NodeID() string
10
}
11
12
// Edge is a directed connection between two Nodes.
13
type Edge struct{ From, To Node }
14
15
// Graph is a Directed Acyclic Graph. The zero value is ready for use. Graph
16
// cannot be modified concurrently.
17
type Graph struct {
18
nodeByID map[string]Node
19
nodes nodeSet
20
outEdges map[Node]nodeSet // Outgoing edges for a given Node
21
inEdges map[Node]nodeSet // Incoming edges for a given Node
22
}
23
24
type nodeSet map[Node]struct{}
25
26
// Add adds n into ns if it doesn't already exist.
27
func (ns nodeSet) Add(n Node) { ns[n] = struct{}{} }
28
29
// Remove removes n from ns if it exists.
30
func (ns nodeSet) Remove(n Node) { delete(ns, n) }
31
32
// Has returns true if ns includes n.
33
func (ns nodeSet) Has(n Node) bool {
34
_, ok := ns[n]
35
return ok
36
}
37
38
// Clone returns a copy of ns.
39
func (ns nodeSet) Clone() nodeSet {
40
newSet := make(nodeSet, len(ns))
41
for node := range ns {
42
newSet[node] = struct{}{}
43
}
44
return newSet
45
}
46
47
// init prepares g for writing.
48
func (g *Graph) init() {
49
if g.nodeByID == nil {
50
g.nodeByID = make(map[string]Node)
51
}
52
if g.nodes == nil {
53
g.nodes = make(nodeSet)
54
}
55
if g.outEdges == nil {
56
g.outEdges = make(map[Node]nodeSet)
57
}
58
if g.inEdges == nil {
59
g.inEdges = make(map[Node]nodeSet)
60
}
61
}
62
63
// Add adds a new Node into g. Add is a no-op if n already exists in g.
64
//
65
// Add will panic if there is another node in g with the same NodeID as n.
66
func (g *Graph) Add(n Node) {
67
g.init()
68
69
if other, ok := g.nodeByID[n.NodeID()]; ok && other != n {
70
panic(fmt.Sprintf("Graph.Add: Node ID %q is already in use by another Node", n.NodeID()))
71
}
72
g.nodes.Add(n)
73
g.nodeByID[n.NodeID()] = n
74
}
75
76
// GetByID returns a node from an ID. Returns nil if the ID does not exist in
77
// the graph.
78
func (g *Graph) GetByID(id string) Node { return g.nodeByID[id] }
79
80
// Remove removes a Node from g. Remove is a no-op if n does not exist in g.
81
//
82
// Remove also removes any edge to or from n.
83
func (g *Graph) Remove(n Node) {
84
if !g.nodes.Has(n) {
85
return
86
}
87
88
delete(g.nodeByID, n.NodeID())
89
g.nodes.Remove(n)
90
91
// Remove all the outgoing edges from n.
92
delete(g.outEdges, n)
93
94
// Remove n from any edge where it is the target.
95
for _, ns := range g.inEdges {
96
ns.Remove(n)
97
}
98
}
99
100
// AddEdge adds a new Edge into g. AddEdge does not prevent cycles from being
101
// introduced; cycles must be detected separately.
102
//
103
// AddEdge will panic if either node in the edge doesn't exist in g.
104
func (g *Graph) AddEdge(e Edge) {
105
g.init()
106
107
if !g.nodes.Has(e.From) || !g.nodes.Has(e.To) {
108
panic("AddEdge called with a node that doesn't exist in graph")
109
}
110
111
inSet, ok := g.inEdges[e.To]
112
if !ok {
113
inSet = make(nodeSet)
114
g.inEdges[e.To] = inSet
115
}
116
inSet.Add(e.From)
117
118
outSet, ok := g.outEdges[e.From]
119
if !ok {
120
outSet = make(nodeSet)
121
g.outEdges[e.From] = outSet
122
}
123
outSet.Add(e.To)
124
}
125
126
// RemoveEdge removes an edge e from g. RemoveEdge is a no-op if e doesn't
127
// exist in g.
128
func (g *Graph) RemoveEdge(e Edge) {
129
inSet, ok := g.inEdges[e.To]
130
if ok {
131
delete(inSet, e.From)
132
}
133
134
outSet, ok := g.outEdges[e.From]
135
if ok {
136
delete(outSet, e.To)
137
}
138
}
139
140
// Nodes returns the set of Nodes in g.
141
func (g *Graph) Nodes() []Node {
142
nodes := make([]Node, 0, len(g.nodes))
143
for n := range g.nodes {
144
nodes = append(nodes, n)
145
}
146
return nodes
147
}
148
149
// Edges returns the set of all edges in g.
150
func (g *Graph) Edges() []Edge {
151
var edges []Edge
152
for from, tos := range g.outEdges {
153
for to := range tos {
154
edges = append(edges, Edge{From: from, To: to})
155
}
156
}
157
return edges
158
}
159
160
// Dependants returns the list of Nodes that depend on n: all Nodes for which
161
// an edge to n is defined.
162
func (g *Graph) Dependants(n Node) []Node {
163
sourceDependants := g.inEdges[n]
164
dependants := make([]Node, 0, len(sourceDependants))
165
for dep := range sourceDependants {
166
dependants = append(dependants, dep)
167
}
168
return dependants
169
}
170
171
// Dependencies returns the list of Nodes that n depends on: all Nodes for
172
// which an edge from n is defined.
173
func (g *Graph) Dependencies(n Node) []Node {
174
sourceDependencies := g.outEdges[n]
175
dependencies := make([]Node, 0, len(sourceDependencies))
176
for dep := range sourceDependencies {
177
dependencies = append(dependencies, dep)
178
}
179
return dependencies
180
}
181
182
// Roots returns the set of Nodes in g that have no dependants. This is useful
183
// for walking g.
184
func (g *Graph) Roots() []Node {
185
var res []Node
186
187
for n := range g.nodes {
188
if len(g.inEdges[n]) == 0 {
189
res = append(res, n)
190
}
191
}
192
193
return res
194
}
195
196
// Leaves returns the set of Nodes in g that have no dependencies. This is
197
// useful for walking g in reverse.
198
func (g *Graph) Leaves() []Node {
199
var res []Node
200
201
for n := range g.nodes {
202
if len(g.outEdges[n]) == 0 {
203
res = append(res, n)
204
}
205
}
206
207
return res
208
}
209
210
// Clone returns a copy of g.
211
func (g *Graph) Clone() *Graph {
212
newGraph := &Graph{
213
nodes: g.nodes.Clone(),
214
215
nodeByID: make(map[string]Node, len(g.nodeByID)),
216
outEdges: make(map[Node]nodeSet, len(g.outEdges)),
217
inEdges: make(map[Node]nodeSet, len(g.outEdges)),
218
}
219
220
for key, value := range g.nodeByID {
221
newGraph.nodeByID[key] = value
222
}
223
for node, set := range g.outEdges {
224
newGraph.outEdges[node] = set.Clone()
225
}
226
for node, set := range g.inEdges {
227
newGraph.inEdges[node] = set.Clone()
228
}
229
return newGraph
230
}
231
232