/*1* Copyright © 2019 Broadcom2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING19* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS20* IN THE SOFTWARE.21*/2223#include "util/set.h"24#include "util/dag.h"2526/**27* Adds a directed edge from the parent node to the child.28*29* Both nodes should have been initialized with dag_init_node(). The edge30* list may contain multiple edges to the same child with different data.31*/32void33dag_add_edge(struct dag_node *parent, struct dag_node *child, void *data)34{35util_dynarray_foreach(&parent->edges, struct dag_edge, edge) {36if (edge->child == child && edge->data == data)37return;38}39/* Remove the child as a DAG head. */40list_delinit(&child->link);4142struct dag_edge edge = {43.child = child,44.data = data,45};4647util_dynarray_append(&parent->edges, struct dag_edge, edge);48child->parent_count++;49}5051/* Removes a single edge from the graph, promoting the child to a DAG head.52*53* Note that calling this other than through dag_prune_head() means that you54* need to be careful when iterating the edges of remaining nodes for NULL55* children.56*/57void58dag_remove_edge(struct dag *dag, struct dag_edge *edge)59{60if (!edge->child)61return;6263struct dag_node *child = edge->child;64child->parent_count--;65if (child->parent_count == 0)66list_addtail(&child->link, &dag->heads);6768edge->child = NULL;69edge->data = NULL;70}7172/**73* Removes a DAG head from the graph, and moves any new dag heads into the74* heads list.75*/76void77dag_prune_head(struct dag *dag, struct dag_node *node)78{79assert(!node->parent_count);8081list_delinit(&node->link);8283util_dynarray_foreach(&node->edges, struct dag_edge, edge) {84dag_remove_edge(dag, edge);85}86}8788/**89* Initializes DAG node (probably embedded in some other datastructure in the90* user).91*/92void93dag_init_node(struct dag *dag, struct dag_node *node)94{95util_dynarray_init(&node->edges, dag);96list_addtail(&node->link, &dag->heads);97}9899struct dag_traverse_bottom_up_state {100struct set *seen;101void *data;102};103104static void105dag_traverse_bottom_up_node(struct dag_node *node,106void (*cb)(struct dag_node *node,107void *data),108struct dag_traverse_bottom_up_state *state)109{110if (_mesa_set_search(state->seen, node))111return;112113util_dynarray_foreach(&node->edges, struct dag_edge, edge) {114dag_traverse_bottom_up_node(edge->child, cb, state);115}116117cb(node, state->data);118_mesa_set_add(state->seen, node);119}120121/**122* Walks the DAG from leaves to the root, ensuring that each node is only seen123* once its children have been, and each node is only traversed once.124*/125void126dag_traverse_bottom_up(struct dag *dag, void (*cb)(struct dag_node *node,127void *data), void *data)128{129struct dag_traverse_bottom_up_state state = {130.seen = _mesa_pointer_set_create(NULL),131.data = data,132};133134list_for_each_entry(struct dag_node, node, &dag->heads, link) {135dag_traverse_bottom_up_node(node, cb, &state);136}137138ralloc_free(state.seen);139}140141/**142* Creates an empty DAG datastructure.143*/144struct dag *145dag_create(void *mem_ctx)146{147struct dag *dag = rzalloc(mem_ctx, struct dag);148149list_inithead(&dag->heads);150151return dag;152}153154155156