Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* |
3 | | * Graph data structure. |
4 | | * |
5 | | * -- |
6 | | * Copyright (C) 2016 Cumulus Networks, Inc. |
7 | | */ |
8 | | #include <zebra.h> |
9 | | #include "graph.h" |
10 | | #include "memory.h" |
11 | | #include "buffer.h" |
12 | | |
13 | 8 | DEFINE_MTYPE_STATIC(LIB, GRAPH, "Graph"); |
14 | 8 | DEFINE_MTYPE_STATIC(LIB, GRAPH_NODE, "Graph Node"); |
15 | 8 | struct graph *graph_new(void) |
16 | 75 | { |
17 | 75 | struct graph *graph = XCALLOC(MTYPE_GRAPH, sizeof(struct graph)); |
18 | 75 | graph->nodes = vector_init(VECTOR_MIN_SIZE); |
19 | | |
20 | 75 | return graph; |
21 | 75 | } |
22 | | |
23 | | void graph_delete_graph(struct graph *graph) |
24 | | __attribute__((no_sanitize("unsigned-integer-overflow"))) |
25 | 0 | { |
26 | 0 | for (unsigned int i = vector_active(graph->nodes); i--; /**/) |
27 | 0 | graph_delete_node(graph, vector_slot(graph->nodes, i)); |
28 | |
|
29 | 0 | vector_free(graph->nodes); |
30 | 0 | XFREE(MTYPE_GRAPH, graph); |
31 | 0 | } |
32 | | |
33 | | struct graph_node *graph_new_node(struct graph *graph, void *data, |
34 | | void (*del)(void *)) |
35 | 75 | { |
36 | 75 | struct graph_node *node = |
37 | 75 | XCALLOC(MTYPE_GRAPH_NODE, sizeof(struct graph_node)); |
38 | | |
39 | 75 | node->from = vector_init(VECTOR_MIN_SIZE); |
40 | 75 | node->to = vector_init(VECTOR_MIN_SIZE); |
41 | 75 | node->data = data; |
42 | 75 | node->del = del; |
43 | | |
44 | 75 | vector_set(graph->nodes, node); |
45 | | |
46 | 75 | return node; |
47 | 75 | } |
48 | | |
49 | | static void graph_vector_remove(vector v, unsigned int ix) |
50 | 0 | { |
51 | 0 | if (ix >= v->active) |
52 | 0 | return; |
53 | | |
54 | | /* v->active is guaranteed >= 1 because ix can't be lower than 0 |
55 | | * and v->active is > ix. */ |
56 | 0 | v->active--; |
57 | | /* if ix == v->active--, we set the item to itself, then to NULL... |
58 | | * still correct, no check necessary. */ |
59 | 0 | v->index[ix] = v->index[v->active]; |
60 | 0 | v->index[v->active] = NULL; |
61 | 0 | } |
62 | | |
63 | | void graph_delete_node(struct graph *graph, struct graph_node *node) |
64 | | __attribute__((no_sanitize("unsigned-integer-overflow"))) |
65 | 0 | { |
66 | 0 | if (!node) |
67 | 0 | return; |
68 | | |
69 | | // an adjacent node |
70 | 0 | struct graph_node *adj; |
71 | | |
72 | | // remove all edges from other nodes to us |
73 | 0 | for (unsigned int i = vector_active(node->from); i--; /**/) { |
74 | 0 | adj = vector_slot(node->from, i); |
75 | 0 | graph_remove_edge(adj, node); |
76 | 0 | } |
77 | | |
78 | | // remove all edges from us to other nodes |
79 | 0 | for (unsigned int i = vector_active(node->to); i--; /**/) { |
80 | 0 | adj = vector_slot(node->to, i); |
81 | 0 | graph_remove_edge(node, adj); |
82 | 0 | } |
83 | | |
84 | | // if there is a deletion callback, call it |
85 | 0 | if (node->del && node->data) |
86 | 0 | (*node->del)(node->data); |
87 | | |
88 | | // free adjacency lists |
89 | 0 | vector_free(node->to); |
90 | 0 | vector_free(node->from); |
91 | | |
92 | | // remove node from graph->nodes |
93 | 0 | for (unsigned int i = vector_active(graph->nodes); i--; /**/) |
94 | 0 | if (vector_slot(graph->nodes, i) == node) { |
95 | 0 | graph_vector_remove(graph->nodes, i); |
96 | 0 | break; |
97 | 0 | } |
98 | | |
99 | | // free the node itself |
100 | 0 | XFREE(MTYPE_GRAPH_NODE, node); |
101 | 0 | } |
102 | | |
103 | | struct graph_node *graph_add_edge(struct graph_node *from, |
104 | | struct graph_node *to) |
105 | 0 | { |
106 | 0 | vector_set(from->to, to); |
107 | 0 | vector_set(to->from, from); |
108 | 0 | return to; |
109 | 0 | } |
110 | | |
111 | | void graph_remove_edge(struct graph_node *from, struct graph_node *to) |
112 | | __attribute__((no_sanitize("unsigned-integer-overflow"))) |
113 | 0 | { |
114 | | // remove from from to->from |
115 | 0 | for (unsigned int i = vector_active(to->from); i--; /**/) |
116 | 0 | if (vector_slot(to->from, i) == from) { |
117 | 0 | graph_vector_remove(to->from, i); |
118 | 0 | break; |
119 | 0 | } |
120 | | // remove to from from->to |
121 | 0 | for (unsigned int i = vector_active(from->to); i--; /**/) |
122 | 0 | if (vector_slot(from->to, i) == to) { |
123 | 0 | graph_vector_remove(from->to, i); |
124 | 0 | break; |
125 | 0 | } |
126 | 0 | } |
127 | | |
128 | | struct graph_node *graph_find_node(struct graph *graph, void *data) |
129 | | __attribute__((no_sanitize("unsigned-integer-overflow"))) |
130 | 0 | { |
131 | 0 | struct graph_node *g; |
132 | |
|
133 | 0 | for (unsigned int i = vector_active(graph->nodes); i--; /**/) { |
134 | 0 | g = vector_slot(graph->nodes, i); |
135 | 0 | if (g->data == data) |
136 | 0 | return g; |
137 | 0 | } |
138 | | |
139 | 0 | return NULL; |
140 | 0 | } |
141 | | |
142 | | bool graph_has_edge(struct graph_node *from, struct graph_node *to) |
143 | | __attribute__((no_sanitize("unsigned-integer-overflow"))) |
144 | 0 | { |
145 | 0 | for (unsigned int i = vector_active(from->to); i--; /**/) |
146 | 0 | if (vector_slot(from->to, i) == to) |
147 | 0 | return true; |
148 | | |
149 | 0 | return false; |
150 | 0 | } |
151 | | |
152 | | static void _graph_dfs(struct graph *graph, struct graph_node *start, |
153 | | vector visited, |
154 | | void (*dfs_cb)(struct graph_node *, void *), void *arg) |
155 | | __attribute__((no_sanitize("unsigned-integer-overflow"))) |
156 | 0 | { |
157 | | /* check that we have not visited this node */ |
158 | 0 | for (unsigned int i = 0; i < vector_active(visited); i++) { |
159 | 0 | if (start == vector_slot(visited, i)) |
160 | 0 | return; |
161 | 0 | } |
162 | | |
163 | | /* put this node in visited stack */ |
164 | 0 | vector_ensure(visited, vector_active(visited)); |
165 | 0 | vector_set_index(visited, vector_active(visited), start); |
166 | | |
167 | | /* callback */ |
168 | 0 | dfs_cb(start, arg); |
169 | | |
170 | | /* recurse into children */ |
171 | 0 | for (unsigned int i = vector_active(start->to); i--; /**/) { |
172 | 0 | struct graph_node *c = vector_slot(start->to, i); |
173 | |
|
174 | 0 | _graph_dfs(graph, c, visited, dfs_cb, arg); |
175 | 0 | } |
176 | 0 | } |
177 | | |
178 | | void graph_dfs(struct graph *graph, struct graph_node *start, |
179 | | void (*dfs_cb)(struct graph_node *, void *), void *arg) |
180 | 0 | { |
181 | 0 | vector visited = vector_init(VECTOR_MIN_SIZE); |
182 | |
|
183 | 0 | _graph_dfs(graph, start, visited, dfs_cb, arg); |
184 | 0 | vector_free(visited); |
185 | 0 | } |
186 | | |
187 | | #ifndef BUILDING_CLIPPY |
188 | | |
189 | | void graph_dump_dot_default_print_cb(struct graph_node *gn, struct buffer *buf) |
190 | 0 | { |
191 | 0 | char nbuf[64]; |
192 | |
|
193 | 0 | for (unsigned int i = 0; i < vector_active(gn->to); i++) { |
194 | 0 | struct graph_node *adj = vector_slot(gn->to, i); |
195 | |
|
196 | 0 | snprintf(nbuf, sizeof(nbuf), " n%p -> n%p;\n", gn, adj); |
197 | 0 | buffer_putstr(buf, nbuf); |
198 | 0 | } |
199 | 0 | } |
200 | | |
201 | | char *graph_dump_dot(struct graph *graph, struct graph_node *start, |
202 | | void (*pcb)(struct graph_node *, struct buffer *)) |
203 | 0 | { |
204 | 0 | struct buffer *buf = buffer_new(0); |
205 | 0 | char *ret; |
206 | |
|
207 | 0 | pcb = (pcb) ? pcb : graph_dump_dot_default_print_cb; |
208 | 0 | buffer_putstr(buf, "digraph {\n"); |
209 | |
|
210 | 0 | graph_dfs(graph, start, (void (*)(struct graph_node *, void *))pcb, |
211 | 0 | buf); |
212 | |
|
213 | 0 | buffer_putstr(buf, "}\n"); |
214 | |
|
215 | 0 | ret = buffer_getstr(buf); |
216 | 0 | buffer_free(buf); |
217 | |
|
218 | 0 | return ret; |
219 | 0 | } |
220 | | |
221 | | #endif /* BUILDING_CLIPPY */ |