diff --git a/src/util/dag.c b/src/util/dag.c index 97fd7386a49..8625647f887 100644 --- a/src/util/dag.c +++ b/src/util/dag.c @@ -24,6 +24,21 @@ #include "util/set.h" #include "util/dag.h" +static void +append_edge(struct dag_node *parent, struct dag_node *child, uintptr_t data) +{ + /* Remove the child as a DAG head. */ + list_delinit(&child->link); + + struct dag_edge edge = { + .child = child, + .data = data, + }; + + util_dynarray_append(&parent->edges, struct dag_edge, edge); + child->parent_count++; +} + /** * Adds a directed edge from the parent node to the child. * @@ -37,16 +52,30 @@ dag_add_edge(struct dag_node *parent, struct dag_node *child, uintptr_t data) if (edge->child == child && edge->data == data) return; } - /* Remove the child as a DAG head. */ - list_delinit(&child->link); - struct dag_edge edge = { - .child = child, - .data = data, - }; + append_edge(parent, child, data); +} - util_dynarray_append(&parent->edges, struct dag_edge, edge); - child->parent_count++; +/** + * Adds a directed edge from the parent node to the child. + * + * Both nodes should have been initialized with dag_init_node(). If there is + * already an existing edge, the data is updated to the maximum of the + * previous data and the new data. This is useful if the data represents a + * delay. + */ +void +dag_add_edge_max_data(struct dag_node *parent, struct dag_node *child, + uintptr_t data) +{ + util_dynarray_foreach(&parent->edges, struct dag_edge, edge) { + if (edge->child == child) { + edge->data = MAX2(edge->data, data); + return; + } + } + + append_edge(parent, child, data); } /* Removes a single edge from the graph, promoting the child to a DAG head. diff --git a/src/util/dag.h b/src/util/dag.h index fbc3ed93d48..3ede13d7548 100644 --- a/src/util/dag.h +++ b/src/util/dag.h @@ -53,6 +53,7 @@ struct dag { struct dag *dag_create(void *mem_ctx); void dag_init_node(struct dag *dag, struct dag_node *node); void dag_add_edge(struct dag_node *parent, struct dag_node *child, uintptr_t data); +void dag_add_edge_max_data(struct dag_node *parent, struct dag_node *child, uintptr_t data); void dag_remove_edge(struct dag *dag, struct dag_edge *edge); void dag_traverse_bottom_up(struct dag *dag, void (*cb)(struct dag_node *node, void *data), void *data); diff --git a/src/util/tests/dag_test.cpp b/src/util/tests/dag_test.cpp index d8c2ae9483c..6cace736972 100644 --- a/src/util/tests/dag_test.cpp +++ b/src/util/tests/dag_test.cpp @@ -56,6 +56,16 @@ struct node: public dag_node { static_cast(&child), 0); return child; } + + void add_edge(struct node &child, uintptr_t data) { + dag_add_edge(static_cast(this), + static_cast(&child), data); + } + + void add_edge_max_data(struct node &child, uintptr_t data) { + dag_add_edge_max_data(static_cast(this), + static_cast(&child), data); + } }; static void output_cb(struct dag_node *dag_node, void *data) @@ -151,6 +161,49 @@ TEST_F(dag_test, simple) TEST_CHECK(); } +TEST_F(dag_test, duplicate_edge) +{ + INIT_NODES(3); + + node[0].add_edge(node[1], 0); + node[0].add_edge(node[1], 1); + node[0].add_edge(node[2], 0); + + EXPECT_EQ(util_dynarray_num_elements(&node[0].edges, struct dag_edge), 3); + + SET_EXPECTED(1, 2, 0); + + dag_traverse_bottom_up(dag, output_cb, &actual); + + TEST_CHECK(); +} + +TEST_F(dag_test, duplicate_edge_max_data) +{ + INIT_NODES(3); + + node[0].add_edge_max_data(node[1], 0); + node[0].add_edge_max_data(node[1], 1); + node[0].add_edge_max_data(node[2], 0); + + EXPECT_EQ(util_dynarray_num_elements(&node[0].edges, struct dag_edge), 2); + + util_dynarray_foreach (&node[0].edges, struct dag_edge, edge) { + if (edge->child == &node[1]) { + EXPECT_EQ(edge->data, 1); + } else { + EXPECT_EQ(edge->child, &node[2]); + EXPECT_EQ(edge->data, 0); + } + } + + SET_EXPECTED(1, 2, 0); + + dag_traverse_bottom_up(dag, output_cb, &actual); + + TEST_CHECK(); +} + TEST_F(dag_test, simple_many_children) { INIT_NODES(6);