diff options
author | Jason Ekstrand <[email protected]> | 2018-04-05 08:21:49 -0700 |
---|---|---|
committer | Lionel Landwerlin <[email protected]> | 2018-07-05 11:57:45 +0100 |
commit | 2602ea89d55cd1004bbec46c8ff1ac1ad1510940 (patch) | |
tree | f97c0dace92b5693ffc2cc4814fb26941d0b6fa8 /src/util/rb_tree.h | |
parent | 144b40db541183ba2ee18efa4e1531aabcf9c6e8 (diff) |
util: rb-tree: A simple, invasive, red-black tree
This is a simple, invasive, liberally licensed red-black tree
implementation. It's an invasive data structure similar to the
Linux kernel linked-list where the intention is that you embed a
rb_node struct the data structure you intend to put into the
tree.
The implementation is mostly based on the one in "Introduction to
Algorithms", third edition, by Cormen, Leiserson, Rivest, and
Stein. There were a few other key design points:
* It's an invasive data structure similar to the [Linux kernel
linked list].
* It uses NULL for leaves instead of a sentinel. This means a few
algorithms differ a small bit from the ones in "Introduction to
Algorithms".
* All search operations are inlined so that the compiler can
optimize away the function pointer call.
Reviewed-by: Lionel Landwerlin <[email protected]>
Diffstat (limited to 'src/util/rb_tree.h')
-rw-r--r-- | src/util/rb_tree.h | 269 |
1 files changed, 269 insertions, 0 deletions
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 <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> + +/** 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 */ |