It should fix some layer problem on some binaries like ls-linux64 and in the presence of self-loop. The previous algorithm could assign the same layer to two nodes that are connected with an edge (and it should never happen, because they have to be on different layers). This patch uses the topological sort of a graph to assign the layer.
237 lines
6 KiB
C
237 lines
6 KiB
C
/* radare - LGPL - Copyright 2007-2012 - pancake */
|
|
|
|
#include <r_util.h>
|
|
|
|
enum {
|
|
WHITE_COLOR = 0,
|
|
GRAY_COLOR,
|
|
BLACK_COLOR
|
|
};
|
|
|
|
static RGraphNode *r_graph_node_new (void *data) {
|
|
RGraphNode *p = R_NEW0 (RGraphNode);
|
|
p->data = data;
|
|
p->free = NULL;
|
|
p->out_nodes = r_list_new ();
|
|
p->in_nodes = r_list_new ();
|
|
p->all_neighbours = r_list_new ();
|
|
return p;
|
|
}
|
|
|
|
static void r_graph_node_free (RGraphNode *n) {
|
|
if (!n) return;
|
|
if (n->free)
|
|
n->free (n->data);
|
|
r_list_free (n->out_nodes);
|
|
r_list_free (n->in_nodes);
|
|
r_list_free (n->all_neighbours);
|
|
free (n);
|
|
}
|
|
|
|
static int node_cmp (unsigned int idx, RGraphNode *b) {
|
|
return idx == b->idx ? 0 : -1;
|
|
}
|
|
|
|
static void dfs_node (RGraph *g, RGraphNode *n, RGraphVisitor *vis, int color[]) {
|
|
RStack *s;
|
|
RGraphEdge *edg;
|
|
|
|
s = r_stack_new (2 * g->n_edges + 1);
|
|
|
|
edg = R_NEW (RGraphEdge);
|
|
edg->from = NULL;
|
|
edg->to = n;
|
|
r_stack_push (s, edg);
|
|
while (!r_stack_is_empty (s)) {
|
|
RGraphEdge *cur_edge = (RGraphEdge *)r_stack_pop (s);
|
|
RGraphNode *v, *cur = cur_edge->to, *from = cur_edge->from;
|
|
const RList *neighbours;
|
|
RListIter *it;
|
|
int i;
|
|
|
|
if (from && cur) {
|
|
if (color[cur->idx] == WHITE_COLOR && vis->tree_edge)
|
|
vis->tree_edge (cur_edge, vis);
|
|
else if (color[cur->idx] == GRAY_COLOR && vis->back_edge)
|
|
vis->back_edge (cur_edge, vis);
|
|
else if (color[cur->idx] == BLACK_COLOR && vis->fcross_edge)
|
|
vis->fcross_edge (cur_edge, vis);
|
|
} else if (!cur && from) {
|
|
if (color[from->idx] != BLACK_COLOR && vis->finish_node)
|
|
vis->finish_node (from, vis);
|
|
color[from->idx] = BLACK_COLOR;
|
|
}
|
|
free (cur_edge);
|
|
|
|
if (!cur || color[cur->idx] != WHITE_COLOR)
|
|
continue;
|
|
|
|
if (color[cur->idx] == WHITE_COLOR && vis->discover_node)
|
|
vis->discover_node (cur, vis);
|
|
color[cur->idx] = GRAY_COLOR;
|
|
|
|
edg = R_NEW0 (RGraphEdge);
|
|
edg->from = cur;
|
|
r_stack_push (s, edg);
|
|
|
|
i = 0;
|
|
neighbours = r_graph_get_neighbours (g, cur);
|
|
r_list_foreach (neighbours, it, v) {
|
|
edg = R_NEW (RGraphEdge);
|
|
edg->from = cur;
|
|
edg->to = v;
|
|
edg->nth = i++;
|
|
r_stack_push (s, edg);
|
|
}
|
|
}
|
|
r_stack_free (s);
|
|
}
|
|
|
|
R_API RGraph *r_graph_new () {
|
|
RGraph *t = R_NEW0 (RGraph);
|
|
t->nodes = r_list_new ();
|
|
t->nodes->free = (RListFree)r_graph_node_free;
|
|
t->n_nodes = 0;
|
|
t->last_index = 0;
|
|
return t;
|
|
}
|
|
|
|
R_API void r_graph_free (RGraph* t) {
|
|
r_list_free (t->nodes);
|
|
free (t);
|
|
}
|
|
|
|
R_API RGraphNode *r_graph_get_node (const RGraph *t, unsigned int idx) {
|
|
RListIter *it = r_list_find (t->nodes, (void *)(size_t)idx, (RListComparator)node_cmp);
|
|
if (!it)
|
|
return NULL;
|
|
|
|
return (RGraphNode *)it->data;
|
|
}
|
|
|
|
R_API RListIter *r_graph_node_iter (const RGraph *t, unsigned int idx) {
|
|
return r_list_find (t->nodes, (void *)(size_t)idx, (RListComparator)node_cmp);
|
|
}
|
|
|
|
R_API void r_graph_reset (RGraph *t) {
|
|
r_list_free (t->nodes);
|
|
|
|
t->nodes = r_list_new ();
|
|
t->nodes->free = (RListFree)r_graph_node_free;
|
|
t->n_nodes = 0;
|
|
t->n_edges = 0;
|
|
t->last_index = 0;
|
|
}
|
|
|
|
R_API RGraphNode *r_graph_add_node (RGraph *t, void *data) {
|
|
RGraphNode *n = r_graph_node_new (data);
|
|
|
|
n->idx = t->last_index++;
|
|
r_list_append (t->nodes, n);
|
|
t->n_nodes++;
|
|
return n;
|
|
}
|
|
|
|
/* remove the node from the graph and free the node */
|
|
/* users of this function should be aware they can't access n anymore */
|
|
R_API void r_graph_del_node(RGraph *t, RGraphNode *n) {
|
|
RGraphNode *gn;
|
|
RListIter *it;
|
|
|
|
if (!n) return;
|
|
r_list_foreach (n->in_nodes, it, gn) {
|
|
r_list_delete_data (gn->out_nodes, n);
|
|
r_list_delete_data (gn->all_neighbours, n);
|
|
t->n_edges--;
|
|
}
|
|
|
|
r_list_foreach (n->out_nodes, it, gn) {
|
|
r_list_delete_data (gn->in_nodes, n);
|
|
r_list_delete_data (gn->all_neighbours, n);
|
|
t->n_edges--;
|
|
}
|
|
|
|
r_list_delete_data (t->nodes, n);
|
|
t->n_nodes--;
|
|
}
|
|
|
|
R_API void r_graph_add_edge (RGraph *t, RGraphNode *from, RGraphNode *to) {
|
|
r_graph_add_edge_at (t, from, to, -1);
|
|
}
|
|
|
|
R_API void r_graph_add_edge_at (RGraph *t, RGraphNode *from, RGraphNode *to, int nth) {
|
|
if (!from || !to) return;
|
|
r_list_insert(from->out_nodes, nth, to);
|
|
r_list_append(from->all_neighbours, to);
|
|
r_list_append(to->in_nodes, from);
|
|
r_list_append(to->all_neighbours, from);
|
|
t->n_edges++;
|
|
}
|
|
|
|
R_API void r_graph_del_edge (RGraph *t, RGraphNode *from, RGraphNode *to) {
|
|
if (!from || !to || !r_graph_adjacent (t, from, to)) return;
|
|
r_list_delete_data (from->out_nodes, to);
|
|
r_list_delete_data (from->all_neighbours, to);
|
|
r_list_delete_data (to->in_nodes, from);
|
|
r_list_delete_data (to->all_neighbours, from);
|
|
t->n_edges--;
|
|
}
|
|
|
|
/* returns the list of nodes reachable from `n` */
|
|
R_API const RList *r_graph_get_neighbours (const RGraph *g, const RGraphNode *n) {
|
|
if (!n) return NULL;
|
|
return n->out_nodes;
|
|
}
|
|
|
|
/* returns the n-th nodes reachable from the give node `n`.
|
|
* This, of course, depends on the order of the nodes. */
|
|
R_API RGraphNode *r_graph_nth_neighbour (const RGraph *g, const RGraphNode *n, int nth) {
|
|
if (!n) return NULL;
|
|
return (RGraphNode *)r_list_get_n (n->out_nodes, nth);
|
|
}
|
|
|
|
/* returns the list of nodes that can reach `n` */
|
|
R_API const RList *r_graph_innodes (const RGraph *g, const RGraphNode *n) {
|
|
if (!n) return NULL;
|
|
return n->in_nodes;
|
|
}
|
|
|
|
/* returns the list of nodes reachable from `n` and that can reach `n`. */
|
|
R_API const RList *r_graph_all_neighbours (const RGraph *g, const RGraphNode *n) {
|
|
if (!n) return NULL;
|
|
return n->all_neighbours;
|
|
}
|
|
|
|
R_API const RList *r_graph_get_nodes (const RGraph *g) {
|
|
if (!g) return NULL;
|
|
return g->nodes;
|
|
}
|
|
|
|
/* true if there is an edge from the node `from` to the node `to` */
|
|
R_API int r_graph_adjacent (const RGraph *g, const RGraphNode *from, const RGraphNode *to) {
|
|
if (!g || !from) return R_FALSE;
|
|
return r_list_contains (from->out_nodes, to) ? R_TRUE : R_FALSE;
|
|
}
|
|
|
|
R_API void r_graph_dfs_node (RGraph *g, RGraphNode *n, RGraphVisitor *vis) {
|
|
int *color;
|
|
|
|
if (!g || !n || !vis) return;
|
|
color = R_NEWS0 (int, g->last_index);
|
|
dfs_node (g, n, vis, color);
|
|
free (color);
|
|
}
|
|
|
|
R_API void r_graph_dfs (RGraph *g, RGraphVisitor *vis) {
|
|
RGraphNode *n;
|
|
RListIter *it;
|
|
int *color;
|
|
|
|
if (!g || !vis) return;
|
|
color = R_NEWS0 (int, g->last_index);
|
|
r_list_foreach (g->nodes, it, n) {
|
|
if (color[n->idx] == WHITE_COLOR)
|
|
dfs_node (g, n, vis, color);
|
|
}
|
|
free (color);
|
|
}
|