mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 08:28:50 +00:00 
			
		
		
		
	Done with a combination of regex'ing and banging my head against a wall. Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
		
			
				
	
	
		
			216 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			216 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
						|
/*
 | 
						|
 * Graph data structure.
 | 
						|
 *
 | 
						|
 * --
 | 
						|
 * Copyright (C) 2016 Cumulus Networks, Inc.
 | 
						|
 */
 | 
						|
#include <zebra.h>
 | 
						|
#include "graph.h"
 | 
						|
#include "memory.h"
 | 
						|
#include "buffer.h"
 | 
						|
 | 
						|
DEFINE_MTYPE_STATIC(LIB, GRAPH, "Graph");
 | 
						|
DEFINE_MTYPE_STATIC(LIB, GRAPH_NODE, "Graph Node");
 | 
						|
struct graph *graph_new(void)
 | 
						|
{
 | 
						|
	struct graph *graph = XCALLOC(MTYPE_GRAPH, sizeof(struct graph));
 | 
						|
	graph->nodes = vector_init(VECTOR_MIN_SIZE);
 | 
						|
 | 
						|
	return graph;
 | 
						|
}
 | 
						|
 | 
						|
void graph_delete_graph(struct graph *graph)
 | 
						|
{
 | 
						|
	for (unsigned int i = vector_active(graph->nodes); i--; /**/)
 | 
						|
		graph_delete_node(graph, vector_slot(graph->nodes, i));
 | 
						|
 | 
						|
	vector_free(graph->nodes);
 | 
						|
	XFREE(MTYPE_GRAPH, graph);
 | 
						|
}
 | 
						|
 | 
						|
struct graph_node *graph_new_node(struct graph *graph, void *data,
 | 
						|
				  void (*del)(void *))
 | 
						|
{
 | 
						|
	struct graph_node *node =
 | 
						|
		XCALLOC(MTYPE_GRAPH_NODE, sizeof(struct graph_node));
 | 
						|
 | 
						|
	node->from = vector_init(VECTOR_MIN_SIZE);
 | 
						|
	node->to = vector_init(VECTOR_MIN_SIZE);
 | 
						|
	node->data = data;
 | 
						|
	node->del = del;
 | 
						|
 | 
						|
	vector_set(graph->nodes, node);
 | 
						|
 | 
						|
	return node;
 | 
						|
}
 | 
						|
 | 
						|
static void graph_vector_remove(vector v, unsigned int ix)
 | 
						|
{
 | 
						|
	if (ix >= v->active)
 | 
						|
		return;
 | 
						|
 | 
						|
	/* v->active is guaranteed >= 1 because ix can't be lower than 0
 | 
						|
	 * and v->active is > ix. */
 | 
						|
	v->active--;
 | 
						|
	/* if ix == v->active--, we set the item to itself, then to NULL...
 | 
						|
	 * still correct, no check necessary. */
 | 
						|
	v->index[ix] = v->index[v->active];
 | 
						|
	v->index[v->active] = NULL;
 | 
						|
}
 | 
						|
 | 
						|
void graph_delete_node(struct graph *graph, struct graph_node *node)
 | 
						|
{
 | 
						|
	if (!node)
 | 
						|
		return;
 | 
						|
 | 
						|
	// an adjacent node
 | 
						|
	struct graph_node *adj;
 | 
						|
 | 
						|
	// remove all edges from other nodes to us
 | 
						|
	for (unsigned int i = vector_active(node->from); i--; /**/) {
 | 
						|
		adj = vector_slot(node->from, i);
 | 
						|
		graph_remove_edge(adj, node);
 | 
						|
	}
 | 
						|
 | 
						|
	// remove all edges from us to other nodes
 | 
						|
	for (unsigned int i = vector_active(node->to); i--; /**/) {
 | 
						|
		adj = vector_slot(node->to, i);
 | 
						|
		graph_remove_edge(node, adj);
 | 
						|
	}
 | 
						|
 | 
						|
	// if there is a deletion callback, call it
 | 
						|
	if (node->del && node->data)
 | 
						|
		(*node->del)(node->data);
 | 
						|
 | 
						|
	// free adjacency lists
 | 
						|
	vector_free(node->to);
 | 
						|
	vector_free(node->from);
 | 
						|
 | 
						|
	// remove node from graph->nodes
 | 
						|
	for (unsigned int i = vector_active(graph->nodes); i--; /**/)
 | 
						|
		if (vector_slot(graph->nodes, i) == node) {
 | 
						|
			graph_vector_remove(graph->nodes, i);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
	// free the node itself
 | 
						|
	XFREE(MTYPE_GRAPH_NODE, node);
 | 
						|
}
 | 
						|
 | 
						|
struct graph_node *graph_add_edge(struct graph_node *from,
 | 
						|
				  struct graph_node *to)
 | 
						|
{
 | 
						|
	vector_set(from->to, to);
 | 
						|
	vector_set(to->from, from);
 | 
						|
	return to;
 | 
						|
}
 | 
						|
 | 
						|
void graph_remove_edge(struct graph_node *from, struct graph_node *to)
 | 
						|
{
 | 
						|
	// remove from from to->from
 | 
						|
	for (unsigned int i = vector_active(to->from); i--; /**/)
 | 
						|
		if (vector_slot(to->from, i) == from) {
 | 
						|
			graph_vector_remove(to->from, i);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	// remove to from from->to
 | 
						|
	for (unsigned int i = vector_active(from->to); i--; /**/)
 | 
						|
		if (vector_slot(from->to, i) == to) {
 | 
						|
			graph_vector_remove(from->to, i);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
}
 | 
						|
 | 
						|
struct graph_node *graph_find_node(struct graph *graph, void *data)
 | 
						|
{
 | 
						|
	struct graph_node *g;
 | 
						|
 | 
						|
	for (unsigned int i = vector_active(graph->nodes); i--; /**/) {
 | 
						|
		g = vector_slot(graph->nodes, i);
 | 
						|
		if (g->data == data)
 | 
						|
			return g;
 | 
						|
	}
 | 
						|
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
bool graph_has_edge(struct graph_node *from, struct graph_node *to)
 | 
						|
{
 | 
						|
	for (unsigned int i = vector_active(from->to); i--; /**/)
 | 
						|
		if (vector_slot(from->to, i) == to)
 | 
						|
			return true;
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
static void _graph_dfs(struct graph *graph, struct graph_node *start,
 | 
						|
		       vector visited,
 | 
						|
		       void (*dfs_cb)(struct graph_node *, void *), void *arg)
 | 
						|
{
 | 
						|
	/* check that we have not visited this node */
 | 
						|
	for (unsigned int i = 0; i < vector_active(visited); i++) {
 | 
						|
		if (start == vector_slot(visited, i))
 | 
						|
			return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* put this node in visited stack */
 | 
						|
	vector_ensure(visited, vector_active(visited));
 | 
						|
	vector_set_index(visited, vector_active(visited), start);
 | 
						|
 | 
						|
	/* callback */
 | 
						|
	dfs_cb(start, arg);
 | 
						|
 | 
						|
	/* recurse into children */
 | 
						|
	for (unsigned int i = vector_active(start->to); i--; /**/) {
 | 
						|
		struct graph_node *c = vector_slot(start->to, i);
 | 
						|
 | 
						|
		_graph_dfs(graph, c, visited, dfs_cb, arg);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void graph_dfs(struct graph *graph, struct graph_node *start,
 | 
						|
	       void (*dfs_cb)(struct graph_node *, void *), void *arg)
 | 
						|
{
 | 
						|
	vector visited = vector_init(VECTOR_MIN_SIZE);
 | 
						|
 | 
						|
	_graph_dfs(graph, start, visited, dfs_cb, arg);
 | 
						|
	vector_free(visited);
 | 
						|
}
 | 
						|
 | 
						|
#ifndef BUILDING_CLIPPY
 | 
						|
 | 
						|
void graph_dump_dot_default_print_cb(struct graph_node *gn, struct buffer *buf)
 | 
						|
{
 | 
						|
	char nbuf[64];
 | 
						|
 | 
						|
	for (unsigned int i = 0; i < vector_active(gn->to); i++) {
 | 
						|
		struct graph_node *adj = vector_slot(gn->to, i);
 | 
						|
 | 
						|
		snprintf(nbuf, sizeof(nbuf), "    n%p -> n%p;\n", gn, adj);
 | 
						|
		buffer_putstr(buf, nbuf);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
char *graph_dump_dot(struct graph *graph, struct graph_node *start,
 | 
						|
		     void (*pcb)(struct graph_node *, struct buffer *))
 | 
						|
{
 | 
						|
	struct buffer *buf = buffer_new(0);
 | 
						|
	char *ret;
 | 
						|
 | 
						|
	pcb = (pcb) ? pcb : graph_dump_dot_default_print_cb;
 | 
						|
	buffer_putstr(buf, "digraph {\n");
 | 
						|
 | 
						|
	graph_dfs(graph, start, (void (*)(struct graph_node *, void *))pcb,
 | 
						|
		  buf);
 | 
						|
 | 
						|
	buffer_putstr(buf, "}\n");
 | 
						|
 | 
						|
	ret = buffer_getstr(buf);
 | 
						|
	buffer_free(buf);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
#endif /* BUILDING_CLIPPY */
 |