diff --git a/src/util/Makefile.sources b/src/util/Makefile.sources index e8933666726..fe34fc26690 100644 --- a/src/util/Makefile.sources +++ b/src/util/Makefile.sources @@ -32,6 +32,8 @@ MESA_UTIL_FILES := \ ralloc.h \ rand_xor.c \ rand_xor.h \ + rb_tree.c \ + rb_tree.h \ register_allocate.c \ register_allocate.h \ rgtc.c \ diff --git a/src/util/meson.build b/src/util/meson.build index 1713864b4f7..1838719d271 100644 --- a/src/util/meson.build +++ b/src/util/meson.build @@ -56,6 +56,8 @@ files_mesa_util = files( 'ralloc.h', 'rand_xor.c', 'rand_xor.h', + 'rb_tree.c', + 'rb_tree.h', 'register_allocate.c', 'register_allocate.h', 'rgtc.c', diff --git a/src/util/rb_tree.c b/src/util/rb_tree.c new file mode 100644 index 00000000000..a86fa31a809 --- /dev/null +++ b/src/util/rb_tree.c @@ -0,0 +1,421 @@ +/* + * Copyright © 2017 Jason Ekstrand + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "rb_tree.h" + +/** \file rb_tree.c + * + * An implementation of a red-black tree + * + * This file implements the guts of a red-black tree. The implementation + * is mostly based on the one in "Introduction to Algorithms", third + * edition, by Cormen, Leiserson, Rivest, and Stein. The primary + * divergence in our algorithms from those presented in CLRS is that we use + * NULL for the leaves instead of a sentinel. This means we have to do a + * tiny bit more tracking in our implementation of delete but it makes the + * algorithms far more explicit than stashing stuff in the sentinel. + */ + +#include +#include +#include + +static bool +rb_node_is_black(struct rb_node *n) +{ + /* NULL nodes are leaves and therefore black */ + return (n == NULL) || (n->parent & 1); +} + +static bool +rb_node_is_red(struct rb_node *n) +{ + return !rb_node_is_black(n); +} + +static void +rb_node_set_black(struct rb_node *n) +{ + n->parent |= 1; +} + +static void +rb_node_set_red(struct rb_node *n) +{ + n->parent &= ~1ull; +} + +static void +rb_node_copy_color(struct rb_node *dst, struct rb_node *src) +{ + dst->parent = (dst->parent & ~1ull) | (src->parent & 1); +} + +static void +rb_node_set_parent(struct rb_node *n, struct rb_node *p) +{ + n->parent = (n->parent & 1) | (uintptr_t)p; +} + +static struct rb_node * +rb_node_minimum(struct rb_node *node) +{ + while (node->left) + node = node->left; + return node; +} + +static struct rb_node * +rb_node_maximum(struct rb_node *node) +{ + while (node->right) + node = node->right; + return node; +} + +void +rb_tree_init(struct rb_tree *T) +{ + T->root = NULL; +} + +/** + * Replace the subtree of T rooted at u with the subtree rooted at v + * + * This is called RB-transplant in CLRS. + * + * The node to be replaced is assumed to be a non-leaf. + */ +static void +rb_tree_splice(struct rb_tree *T, struct rb_node *u, struct rb_node *v) +{ + assert(u); + struct rb_node *p = rb_node_parent(u); + if (p == NULL) { + assert(T->root == u); + T->root = v; + } else if (u == p->left) { + p->left = v; + } else { + assert(u == p->right); + p->right = v; + } + if (v) + rb_node_set_parent(v, p); +} + +static void +rb_tree_rotate_left(struct rb_tree *T, struct rb_node *x) +{ + assert(x && x->right); + + struct rb_node *y = x->right; + x->right = y->left; + if (y->left) + rb_node_set_parent(y->left, x); + rb_tree_splice(T, x, y); + y->left = x; + rb_node_set_parent(x, y); +} + +static void +rb_tree_rotate_right(struct rb_tree *T, struct rb_node *y) +{ + assert(y && y->left); + + struct rb_node *x = y->left; + y->left = x->right; + if (x->right) + rb_node_set_parent(x->right, y); + rb_tree_splice(T, y, x); + x->right = y; + rb_node_set_parent(y, x); +} + +void +rb_tree_insert_at(struct rb_tree *T, struct rb_node *parent, + struct rb_node *node, bool insert_left) +{ + /* This sets null children, parent, and a color of red */ + memset(node, 0, sizeof(*node)); + + if (parent == NULL) { + assert(T->root == NULL); + T->root = node; + rb_node_set_black(node); + return; + } + + if (insert_left) { + assert(parent->left == NULL); + parent->left = node; + } else { + assert(parent->right == NULL); + parent->right = node; + } + rb_node_set_parent(node, parent); + + /* Now we do the insertion fixup */ + struct rb_node *z = node; + while (rb_node_is_red(rb_node_parent(z))) { + struct rb_node *z_p = rb_node_parent(z); + assert(z == z_p->left || z == z_p->right); + struct rb_node *z_p_p = rb_node_parent(z_p); + assert(z_p_p != NULL); + if (z_p == z_p_p->left) { + struct rb_node *y = z_p_p->right; + if (rb_node_is_red(y)) { + rb_node_set_black(z_p); + rb_node_set_black(y); + rb_node_set_red(z_p_p); + z = z_p_p; + } else { + if (z == z_p->right) { + z = z_p; + rb_tree_rotate_left(T, z); + /* We changed z */ + z_p = rb_node_parent(z); + assert(z == z_p->left || z == z_p->right); + z_p_p = rb_node_parent(z_p); + } + rb_node_set_black(z_p); + rb_node_set_red(z_p_p); + rb_tree_rotate_right(T, z_p_p); + } + } else { + struct rb_node *y = z_p_p->left; + if (rb_node_is_red(y)) { + rb_node_set_black(z_p); + rb_node_set_black(y); + rb_node_set_red(z_p_p); + z = z_p_p; + } else { + if (z == z_p->left) { + z = z_p; + rb_tree_rotate_right(T, z); + /* We changed z */ + z_p = rb_node_parent(z); + assert(z == z_p->left || z == z_p->right); + z_p_p = rb_node_parent(z_p); + } + rb_node_set_black(z_p); + rb_node_set_red(z_p_p); + rb_tree_rotate_left(T, z_p_p); + } + } + } + rb_node_set_black(T->root); +} + +void +rb_tree_remove(struct rb_tree *T, struct rb_node *z) +{ + /* x_p is always the parent node of X. We have to track this + * separately because x may be NULL. + */ + struct rb_node *x, *x_p; + struct rb_node *y = z; + bool y_was_black = rb_node_is_black(y); + if (z->left == NULL) { + x = z->right; + x_p = rb_node_parent(z); + rb_tree_splice(T, z, x); + } else if (z->right == NULL) { + x = z->left; + x_p = rb_node_parent(z); + rb_tree_splice(T, z, x); + } else { + /* Find the minimum sub-node of z->right */ + y = rb_node_minimum(z->right); + y_was_black = rb_node_is_black(y); + + x = y->right; + if (rb_node_parent(y) == z) { + x_p = y; + } else { + x_p = rb_node_parent(y); + rb_tree_splice(T, y, x); + y->right = z->right; + rb_node_set_parent(y->right, y); + } + assert(y->left == NULL); + rb_tree_splice(T, z, y); + y->left = z->left; + rb_node_set_parent(y->left, y); + rb_node_copy_color(y, z); + } + + assert(x_p == NULL || x == x_p->left || x == x_p->right); + + if (!y_was_black) + return; + + /* Fixup RB tree after the delete */ + while (x != T->root && rb_node_is_black(x)) { + if (x == x_p->left) { + struct rb_node *w = x_p->right; + if (rb_node_is_red(w)) { + rb_node_set_black(w); + rb_node_set_red(x_p); + rb_tree_rotate_left(T, x_p); + assert(x == x_p->left); + w = x_p->right; + } + if (rb_node_is_black(w->left) && rb_node_is_black(w->right)) { + rb_node_set_red(w); + x = x_p; + } else { + if (rb_node_is_black(w->right)) { + rb_node_set_black(w->left); + rb_node_set_red(w); + rb_tree_rotate_right(T, w); + w = x_p->right; + } + rb_node_copy_color(w, x_p); + rb_node_set_black(x_p); + rb_node_set_black(w->right); + rb_tree_rotate_left(T, x_p); + x = T->root; + } + } else { + struct rb_node *w = x_p->left; + if (rb_node_is_red(w)) { + rb_node_set_black(w); + rb_node_set_red(x_p); + rb_tree_rotate_right(T, x_p); + assert(x == x_p->right); + w = x_p->left; + } + if (rb_node_is_black(w->right) && rb_node_is_black(w->left)) { + rb_node_set_red(w); + x = x_p; + } else { + if (rb_node_is_black(w->left)) { + rb_node_set_black(w->right); + rb_node_set_red(w); + rb_tree_rotate_left(T, w); + w = x_p->left; + } + rb_node_copy_color(w, x_p); + rb_node_set_black(x_p); + rb_node_set_black(w->left); + rb_tree_rotate_right(T, x_p); + x = T->root; + } + } + x_p = rb_node_parent(x); + } + if (x) + rb_node_set_black(x); +} + +struct rb_node * +rb_tree_first(struct rb_tree *T) +{ + return T->root ? rb_node_minimum(T->root) : NULL; +} + +struct rb_node * +rb_tree_last(struct rb_tree *T) +{ + return T->root ? rb_node_maximum(T->root) : NULL; +} + +struct rb_node * +rb_node_next(struct rb_node *node) +{ + if (node->right) { + /* If we have a right child, then the next thing (compared to this + * node) is the left-most child of our right child. + */ + return rb_node_minimum(node->right); + } else { + /* If node doesn't have a right child, crawl back up the to the + * left until we hit a parent to the right. + */ + struct rb_node *p = rb_node_parent(node); + while (p && node == p->right) { + node = p; + p = rb_node_parent(node); + } + assert(p == NULL || node == p->left); + return p; + } +} + +struct rb_node * +rb_node_prev(struct rb_node *node) +{ + if (node->left) { + /* If we have a left child, then the previous thing (compared to + * this node) is the right-most child of our left child. + */ + return rb_node_maximum(node->left); + } else { + /* If node doesn't have a left child, crawl back up the to the + * right until we hit a parent to the left. + */ + struct rb_node *p = rb_node_parent(node); + while (p && node == p->left) { + node = p; + p = rb_node_parent(node); + } + assert(p == NULL || node == p->right); + return p; + } +} + +static void +validate_rb_node(struct rb_node *n, int black_depth) +{ + if (n == NULL) { + assert(black_depth == 0); + return; + } + + if (rb_node_is_black(n)) { + black_depth--; + } else { + assert(rb_node_is_black(n->left)); + assert(rb_node_is_black(n->right)); + } + + validate_rb_node(n->left, black_depth); + validate_rb_node(n->right, black_depth); +} + +void +rb_tree_validate(struct rb_tree *T) +{ + if (T->root == NULL) + return; + + assert(rb_node_is_black(T->root)); + + unsigned black_depth = 0; + for (struct rb_node *n = T->root; n; n = n->left) { + if (rb_node_is_black(n)) + black_depth++; + } + + validate_rb_node(T->root, black_depth); +} diff --git a/src/util/rb_tree.h b/src/util/rb_tree.h new file mode 100644 index 00000000000..e8750b32d0e --- /dev/null +++ b/src/util/rb_tree.h @@ -0,0 +1,269 @@ +/* + * Copyright © 2017 Jason Ekstrand + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef RB_TREE_H +#define RB_TREE_H + +#include +#include +#include +#include + +/** A red-black tree node + * + * This struct represents a node in the red-black tree. This struct should + * be embedded as a member in whatever structure you wish to put in the + * tree. + */ +struct rb_node { + /** Parent and color of this node + * + * The least significant bit represents the color and is est to 1 for + * black and 0 for red. The other bits are the pointer to the parent + * and that pointer can be retrieved by masking off the bottom bit and + * casting to a pointer. + */ + uintptr_t parent; + + /** Left child of this node, null for a leaf */ + struct rb_node *left; + + /** Right child of this node, null for a leaf */ + struct rb_node *right; +}; + +/** Return the parent node of the given node or NULL if it is the root */ +static inline struct rb_node * +rb_node_parent(struct rb_node *n) +{ + return (struct rb_node *)(n->parent & ~1ull); +} + +/** A red-black tree + * + * This struct represents the red-black tree itself. It is just a pointer + * to the root node with no other metadata. + */ +struct rb_tree { + struct rb_node *root; +}; + +/** Initialize a red-black tree */ +void rb_tree_init(struct rb_tree *T); + +/** Returns true if the red-black tree is empty */ +static inline bool +rb_tree_is_empty(const struct rb_tree *T) +{ + return T->root == NULL; +} + +/** Retrieve the data structure containing a node + * + * \param type The type of the containing data structure + * + * \param node A pointer to a rb_node + * + * \param field The rb_node field in the containing data structure + */ +#define rb_node_data(type, node, field) \ + ((type *)(((char *)(node)) - offsetof(type, field))) + +/** Insert a node into a tree at a particular location + * + * This function should probably not be used directly as it relies on the + * caller to ensure that the parent node is correct. Use rb_tree_insert + * instead. + * + * \param T The red-black tree into which to insert the new node + * + * \param parent The node in the tree that will be the parent of the + * newly inserted node + * + * \param node The node to insert + * + * \param insert_left If true, the new node will be the left child of + * \p parent, otherwise it will be the right child + */ +void rb_tree_insert_at(struct rb_tree *T, struct rb_node *parent, + struct rb_node *node, bool insert_left); + +/** Insert a node into a tree + * + * \param T The red-black tree into which to insert the new node + * + * \param node The node to insert + * + * \param cmp A comparison function to use to order the nodes. + */ +static inline void +rb_tree_insert(struct rb_tree *T, struct rb_node *node, + int (*cmp)(const struct rb_node *, const struct rb_node *)) +{ + /* This function is declared inline in the hopes that the compiler can + * optimize away the comparison function pointer call. + */ + struct rb_node *y = NULL; + struct rb_node *x = T->root; + bool left = false; + while (x != NULL) { + y = x; + left = cmp(node, x) < 0; + if (left) + x = x->left; + else + x = x->right; + } + + rb_tree_insert_at(T, y, node, left); +} + +/** Remove a node from a tree + * + * \param T The red-black tree from which to remove the node + * + * \param node The node to remove + */ +void rb_tree_remove(struct rb_tree *T, struct rb_node *z); + +/** Search the tree for a node + * + * If a node with a matching key exists, the first matching node found will + * be returned. If no matching node exists, NULL is returned. + * + * \param T The red-black tree to search + * + * \param key The key to search for + * + * \param cmp A comparison function to use to order the nodes + */ +static inline struct rb_node * +rb_tree_search(struct rb_tree *T, const void *key, + int (*cmp)(const struct rb_node *, const void *)) +{ + /* This function is declared inline in the hopes that the compiler can + * optimize away the comparison function pointer call. + */ + struct rb_node *x = T->root; + while (x != NULL) { + int c = cmp(x, key); + if (c < 0) + x = x->right; + else if (c > 0) + x = x->left; + else + return x; + } + + return x; +} + +/** Sloppily search the tree for a node + * + * This function searches the tree for a given node. If a node with a + * matching key exists, that first matching node found will be returned. + * If no node with an exactly matching key exists, the node returned will + * be either the right-most node comparing less than \p key or the + * right-most node comparing greater than \p key. If the tree is empty, + * NULL is returned. + * + * \param T The red-black tree to search + * + * \param key The key to search for + * + * \param cmp A comparison function to use to order the nodes + */ +static inline struct rb_node * +rb_tree_search_sloppy(struct rb_tree *T, const void *key, + int (*cmp)(const struct rb_node *, const void *)) +{ + /* This function is declared inline in the hopes that the compiler can + * optimize away the comparison function pointer call. + */ + struct rb_node *y = NULL; + struct rb_node *x = T->root; + while (x != NULL) { + y = x; + int c = cmp(x, key); + if (c < 0) + x = x->right; + else if (c > 0) + x = x->left; + else + return x; + } + + return y; +} + +/** Get the first (left-most) node in the tree or NULL */ +struct rb_node *rb_tree_first(struct rb_tree *T); + +/** Get the last (right-most) node in the tree or NULL */ +struct rb_node *rb_tree_last(struct rb_tree *T); + +/** Get the next node (to the right) in the tree or NULL */ +struct rb_node *rb_node_next(struct rb_node *node); + +/** Get the next previous (to the left) in the tree or NULL */ +struct rb_node *rb_node_prev(struct rb_node *node); + +/** Iterate over the nodes in the tree + * + * \param type The type of the containing data structure + * + * \param node The variable name for current node in the iteration; + * this will be declared as a pointer to \p type + * + * \param T The red-black tree + * + * \param field The rb_node field in containing data structure + */ +#define rb_tree_foreach(type, node, T, field) \ + for (type *node = rb_node_data(type, rb_tree_first(T), field); \ + &node->field != NULL; \ + node = rb_node_data(type, rb_node_next(&node->field), field)) + +/** Iterate over the nodes in the tree in reverse + * + * \param type The type of the containing data structure + * + * \param node The variable name for current node in the iteration; + * this will be declared as a pointer to \p type + * + * \param T The red-black tree + * + * \param field The rb_node field in containing data structure + */ +#define rb_tree_foreach_rev(type, node, T, field) \ + for (type *node = rb_node_data(type, rb_tree_last(T), field); \ + &node->field != NULL; \ + node = rb_node_data(type, rb_node_prev(&node->field), field)) + +/** Validate a red-black tree + * + * This function walks the tree and validates that this is a valid red- + * black tree. If anything is wrong, it will assert-fail. + */ +void rb_tree_validate(struct rb_tree *T); + +#endif /* RB_TREE_H */