/* vim: tabstop=4 shiftwidth=4 noexpandtab * This file is part of ToaruOS and is released under the terms * of the NCSA / University of Illinois License - see LICENSE.md * Copyright (C) 2011-2018 K. Lange * * General-purpose tree implementation */ #ifdef _KERNEL_ # include #else # include # include #endif #include tree_t * tree_create(void) { /* Create a new tree */ tree_t * out = malloc(sizeof(tree_t)); out->nodes = 0; out->root = NULL; return out; } void tree_set_root(tree_t * tree, void * value) { /* Set the root node for a new tree. */ tree_node_t * root = tree_node_create(value); tree->root = root; tree->nodes = 1; } void tree_node_destroy(tree_node_t * node) { /* Free the contents of a node and its children, but not the nodes themselves */ foreach(child, node->children) { tree_node_destroy((tree_node_t *)child->value); } free(node->value); } void tree_destroy(tree_t * tree) { /* Free the contents of a tree, but not the nodes */ if (tree->root) { tree_node_destroy(tree->root); } } void tree_node_free(tree_node_t * node) { /* Free a node and its children, but not their contents */ if (!node) return; foreach(child, node->children) { tree_node_free(child->value); } free(node); } void tree_free(tree_t * tree) { /* Free all of the nodes in a tree, but not their contents */ tree_node_free(tree->root); } tree_node_t * tree_node_create(void * value) { /* Create a new tree node pointing to the given value */ tree_node_t * out = malloc(sizeof(tree_node_t)); out->value = value; out->children = list_create(); out->parent = NULL; return out; } void tree_node_insert_child_node(tree_t * tree, tree_node_t * parent, tree_node_t * node) { /* Insert a node as a child of parent */ list_insert(parent->children, node); node->parent = parent; tree->nodes++; } tree_node_t * tree_node_insert_child(tree_t * tree, tree_node_t * parent, void * value) { /* Insert a (fresh) node as a child of parent */ tree_node_t * out = tree_node_create(value); tree_node_insert_child_node(tree, parent, out); return out; } tree_node_t * tree_node_find_parent(tree_node_t * haystack, tree_node_t * needle) { /* Recursive node part of tree_find_parent */ tree_node_t * found = NULL; foreach(child, haystack->children) { if (child->value == needle) { return haystack; } found = tree_node_find_parent((tree_node_t *)child->value, needle); if (found) { break; } } return found; } tree_node_t * tree_find_parent(tree_t * tree, tree_node_t * node) { /* Return the parent of a node, inefficiently. */ if (!tree->root) return NULL; return tree_node_find_parent(tree->root, node); } size_t tree_count_children(tree_node_t * node) { /* return the number of children this node has */ if (!node) return 0; if (!node->children) return 0; size_t out = node->children->length; foreach(child, node->children) { out += tree_count_children((tree_node_t *)child->value); } return out; } void tree_node_parent_remove(tree_t * tree, tree_node_t * parent, tree_node_t * node) { /* remove a node when we know its parent; update node counts for the tree */ tree->nodes -= tree_count_children(node) + 1; list_delete(parent->children, list_find(parent->children, node)); tree_node_free(node); } void tree_node_remove(tree_t * tree, tree_node_t * node) { /* remove an entire branch given its root */ tree_node_t * parent = node->parent; if (!parent) { if (node == tree->root) { tree->nodes = 0; tree->root = NULL; tree_node_free(node); } } tree_node_parent_remove(tree, parent, node); } void tree_remove(tree_t * tree, tree_node_t * node) { /* Remove this node and move its children into its parent's list of children */ tree_node_t * parent = node->parent; /* This is something we just can't do. We don't know how to merge our * children into our "parent" because then we'd have more than one root node. * A good way to think about this is actually what this tree struct * primarily exists for: processes. Trying to remove the root is equivalent * to trying to kill init! Which is bad. We immediately fault on such * a case anyway ("Tried to kill init, shutting down!"). */ if (!parent) return; tree->nodes--; list_delete(parent->children, list_find(parent->children, node)); foreach(child, node->children) { /* Reassign the parents */ ((tree_node_t *)child->value)->parent = parent; } list_merge(parent->children, node->children); free(node); } void tree_remove_reparent_root(tree_t * tree, tree_node_t * node) { /* Remove this node and move its children into the root children */ tree_node_t * parent = node->parent; if (!parent) return; tree->nodes--; list_delete(parent->children, list_find(parent->children, node)); foreach(child, node->children) { /* Reassign the parents */ ((tree_node_t *)child->value)->parent = tree->root; } list_merge(tree->root->children, node->children); free(node); } void tree_break_off(tree_t * tree, tree_node_t * node) { tree_node_t * parent = node->parent; if (!parent) return; list_delete(parent->children, list_find(parent->children, node)); } tree_node_t * tree_node_find(tree_node_t * node, void * search, tree_comparator_t comparator) { if (comparator(node->value,search)) { return node; } tree_node_t * found; foreach(child, node->children) { found = tree_node_find((tree_node_t *)child->value, search, comparator); if (found) return found; } return NULL; } tree_node_t * tree_find(tree_t * tree, void * value, tree_comparator_t comparator) { return tree_node_find(tree->root, value, comparator); }