Coverage Report

Created: 2025-12-05 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/lib/graph.c
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 */