diff options
Diffstat (limited to 'src/utils/rbtree.c')
-rw-r--r-- | src/utils/rbtree.c | 742 |
1 files changed, 0 insertions, 742 deletions
diff --git a/src/utils/rbtree.c b/src/utils/rbtree.c deleted file mode 100644 index 4fd3c23..0000000 --- a/src/utils/rbtree.c +++ /dev/null @@ -1,742 +0,0 @@ -/* - * This file is part of LibParserUtils. - * Licensed under the MIT License, - * http://www.opensource.org/licenses/mit-license.php - * Copyright 2008 John-Mark Bell <jmb@netsurf-browser.org> - */ - -#include <assert.h> -#ifndef NDEBUG -#include <stdio.h> -#endif - -#include <parserutils/errors.h> -#include <parserutils/functypes.h> - -#include "utils/rbtree.h" - -/* Compile-time configuration */ - -/* Define this to validate the tree structure after every insertion/deletion - * Note: this performs a complete tree traversal, so has a large performance - * impact */ -#undef RBTREE_VALIDATE - -/* Define this to use deleteMax() to destroy a tree. - * Uses deleteMin() if undefined. */ -#undef USE_DELETEMAX - -/** - * Node in an RB tree - */ -typedef struct rbnode -{ - struct rbnode *left; /**< Left subtree */ - struct rbnode *right; /**< Right subtree */ - enum { RED = true, BLACK = false } colour; /**< Colour of node */ - - void *key; /**< Node key */ - void *value; /**< Node value */ -} rbnode; - -/** - * RB tree object - */ -struct parserutils_rbtree -{ - rbnode *root; /**< Root node of tree */ - - parserutils_rbtree_cmp cmp; /**< Key comparator */ - - parserutils_alloc alloc; /**< Memory (de)allocation function */ - void *pw; /**< Pointer to client data */ -}; - -static rbnode *rbtree_insert_internal(parserutils_rbtree *tree, - rbnode *current, void *key, void *value, void **oldvalue); -static rbnode *rbtree_delete_internal(parserutils_rbtree *tree, - rbnode *current, const void *key, void **intkey, void **value); -static inline rbnode *rbnode_create(parserutils_alloc alloc, void *pw, - void *key, void *value); -static inline void rbnode_destroy(parserutils_alloc alloc, void *pw, - rbnode *node); -static inline bool isRed(rbnode *node); -static inline void colourFlip(rbnode *node); -static inline rbnode *rotateLeft(rbnode *node); -static inline rbnode *rotateRight(rbnode *node); -static inline rbnode *moveRedLeft(rbnode *node); -static inline rbnode *moveRedRight(rbnode *node); -static inline rbnode *fixUp(rbnode *node); -#ifdef USE_DELETEMAX -static inline rbnode *deleteMax(parserutils_rbtree *tree, rbnode *node, - parserutils_rbtree_del destructor, void *pw); -#else -static inline rbnode *deleteMin(parserutils_rbtree *tree, rbnode *node, - parserutils_rbtree_del destructor, void *pw); -#endif -static inline rbnode *findMin(rbnode *node); - -#ifndef NDEBUG -#ifdef RBTREE_VALIDATE -static inline void verify_tree(rbnode *node); -static inline rbnode *verify_tree_internal(rbnode *node); -static inline void dump_tree_raw(rbnode *node, uint32_t depth); -#endif -static inline void dump_tree(rbnode *node, parserutils_rbtree_print print, - uint32_t depth); -#endif - -/** - * Create a RB tree - * - * \param cmp Comparator routine for keys - * \param alloc Memory (de)allocation function - * \param pw Pointer to client-specific private data - * \param tree Pointer to location to receive tree instance - * \return PARSERUTILS_OK on success, - * PARSERUTILS_BADPARM on bad parameters, - * PARSERUTILS_NOMEM on memory exhaustion - */ -parserutils_error parserutils_rbtree_create(parserutils_rbtree_cmp cmp, - parserutils_alloc alloc, void *pw, parserutils_rbtree **tree) -{ - parserutils_rbtree *t; - - if (cmp == NULL || alloc == NULL || tree == NULL) - return PARSERUTILS_BADPARM; - - t = alloc(NULL, sizeof(parserutils_rbtree), pw); - if (t == NULL) - return PARSERUTILS_NOMEM; - - t->root = NULL; - t->cmp = cmp; - t->alloc = alloc; - t->pw = pw; - - *tree = t; - - return PARSERUTILS_OK; -} - -/** - * Destroy an RB tree - * - * \param tree The tree to destroy - * \param destructor Routine to be called with key/value pairs to destroy - * \param pw Pointer to client-specific private data - * \return PARSERUTILS_OK on success, appropriate error otherwise - */ -parserutils_error parserutils_rbtree_destroy(parserutils_rbtree *tree, - parserutils_rbtree_del destructor, void *pw) -{ - if (tree == NULL) - return PARSERUTILS_BADPARM; - - while (tree->root != NULL) { -#ifndef USE_DELETEMAX - tree->root = deleteMin(tree, tree->root, destructor, pw); -#else - tree->root = deleteMax(tree, tree->root, destructor, pw); -#endif - if (tree->root != NULL) - tree->root->colour = BLACK; - -#if !defined(NDEBUG) && defined(RBTREE_VALIDATE) - verify_tree(tree->root); -#endif - } - - tree->alloc(tree, 0, tree->pw); - - return PARSERUTILS_OK; -} - -/** - * Insert into a RB tree - * - * \param tree The tree to insert into - * \param key The key to insert (not copied) - * \param value The value to insert (not copied) - * \param oldvalue Pointer to location to receive previous value for key - * \return PARSERUTILS_OK on success, appropriate error otherwise. - */ -parserutils_error parserutils_rbtree_insert(parserutils_rbtree *tree, - void *key, void *value, void **oldvalue) -{ - rbnode *temp; - - if (tree == NULL || key == NULL || oldvalue == NULL) - return PARSERUTILS_BADPARM; - - temp = rbtree_insert_internal(tree, tree->root, key, - value, oldvalue); - if (temp == NULL) - return PARSERUTILS_NOMEM; - - temp->colour = BLACK; - tree->root = temp; - -#if !defined(NDEBUG) && defined(RBTREE_VALIDATE) - verify_tree(tree->root); -#endif - - return PARSERUTILS_OK; -} - -/** - * Find an entry in a RB tree - * - * \param tree The tree to search in - * \param key The key to search for - * \param value Pointer to location to receive value for key - * \return PARSERUTILS_OK on success, appropriate error otherwise - */ -parserutils_error parserutils_rbtree_find(parserutils_rbtree *tree, - const void *key, void **value) -{ - rbnode *node; - - if (tree == NULL || key == NULL || value == NULL) - return PARSERUTILS_BADPARM; - - node = tree->root; - *value = NULL; - - while (node != NULL) { - int cmp = tree->cmp(key, node->key); - - if (cmp == 0) { - *value = node->value; - break; - } else if (cmp < 0) { - node = node->left; - } else { - node = node->right; - } - } - - return PARSERUTILS_OK; -} - -/** - * Delete an entry from a RB tree - * - * \param tree The tree to delete from - * \param key The key to delete - * \param intkey Pointer to location to receive key - * \param value Pointer to location to receive value - * \return PARSERUTILS_OK on success, appropriate error otherwise - */ -parserutils_error parserutils_rbtree_delete(parserutils_rbtree *tree, - const void *key, void **intkey, void **value) -{ - rbnode *temp; - - if (tree == NULL || key == NULL || intkey == NULL || value == NULL) - return PARSERUTILS_BADPARM; - - if (tree->root == NULL) { - *intkey = NULL; - *value = NULL; - return PARSERUTILS_OK; - } - - temp = rbtree_delete_internal(tree, tree->root, key, intkey, value); - if (temp != NULL) - temp->colour = BLACK; - tree->root = temp; - -#if !defined(NDEBUG) && defined(RBTREE_VALIDATE) - verify_tree(tree->root); -#endif - - return PARSERUTILS_OK; -} - -/** - * Helper function for tree insertion - * - * \param tree Tree to insert into - * \param current The root of the current subtree - * \param key Key to insert (not copied) - * \param value Value to insert (not copied) - * \param oldvalue Pointer to location to receive old value for key - * \return Pointer to new root of current subtree - */ -rbnode *rbtree_insert_internal(parserutils_rbtree *tree, rbnode *current, - void *key, void *value, void **oldvalue) -{ - int cmp; - if (current == NULL) { - rbnode *node = rbnode_create(tree->alloc, tree->pw, key, value); - if (node == NULL) - return NULL; - - *oldvalue = NULL; - - return node; - } - - /* Split 4-nodes */ - if (isRed(current->left) && isRed(current->right)) - colourFlip(current); - - cmp = tree->cmp(key, current->key); - - if (cmp == 0) { - *oldvalue = current->value; - current->value = value; - } else if (cmp < 0) { - rbnode *temp = rbtree_insert_internal(tree, current->left, - key, value, oldvalue); - if (temp == NULL) - return NULL; - current->left = temp; - } else { - rbnode *temp = rbtree_insert_internal(tree, current->right, - key, value, oldvalue); - if (temp == NULL) - return NULL; - current->right = temp; - } - - /* Eliminate right-leaning 3-nodes by rotating left */ - if (isRed(current->right)) - current = rotateLeft(current); - - /* Balance 4-nodes by rotating right */ - if (isRed(current->left) && isRed(current->left->left)) - current = rotateRight(current); - - return current; -} - -/** - * Helper function for tree deletion - * - * \param tree Tree to delete from - * \param current Root node of current subtree - * \param key Key to delete - * \param intkey Pointer to location to receive key - * \param value Pointer to location to receive value - * \return Pointer to new root node for current subtree - */ -rbnode *rbtree_delete_internal(parserutils_rbtree *tree, rbnode *current, - const void *key, void **intkey, void **value) -{ - int cmp; - - if (current == NULL) - return NULL; - - cmp = tree->cmp(key, current->key); - - if (cmp < 0) { - if (current->left != NULL && !isRed(current->left) && - !isRed(current->left->left)) - current = moveRedLeft(current); - current->left = rbtree_delete_internal(tree, current->left, - key, intkey, value); - } else { - if (isRed(current->left)) - current = rotateRight(current); - - if (cmp == 0 && current->right == NULL) { - *intkey = current->key; - *value = current->value; - - rbnode_destroy(tree->alloc, tree->pw, current); - return NULL; - } - - if (current->right != NULL && !isRed(current->right) && - !isRed(current->right->left)) - current = moveRedRight(current); - - /* Must recalculate here, as current may have changed */ - if (tree->cmp(key, current->key) == 0) { - rbnode *successor; - *intkey = current->key; - *value = current->value; - - successor = findMin(current->right); - - current->value = successor->value; - current->key = successor->key; - - current->right = deleteMin(tree, current->right, - NULL, NULL); - } else { - current->right = rbtree_delete_internal(tree, - current->right, key, intkey, value); - } - } - - return fixUp(current); -} - -/** - * Create a RB node - * - * \param alloc Memory (de)allocation function - * \param pw Pointer to client-specific private data - * \param key Key for node (not copied) - * \param value Value for node (not copied) - * \return Pointer to node, or NULL on memory exhaustion - */ -rbnode *rbnode_create(parserutils_alloc alloc, void *pw, void *key, void *value) -{ - rbnode *node = alloc(NULL, sizeof(rbnode), pw); - if (node == NULL) - return NULL; - - node->left = NULL; - node->right = NULL; - node->colour = RED; - node->key = key; - node->value = value; - - return node; -} - -/** - * Destroy a RB node - * - * \param alloc Memory (de)allocation function - * \param pw Pointer to client-specific private data - * \param node The node to destroy - */ -void rbnode_destroy(parserutils_alloc alloc, void *pw, rbnode *node) -{ - alloc(node, 0, pw); -} - -/** - * Determine if a given node is red - * - * \param node The node to consider - * \return True if node is red, false otherwise - */ -bool isRed(rbnode *node) -{ - if (node == NULL) - return false; - - return node->colour == RED; -} - -/** - * Flip the colours for a node - * - * \param node The node to consider - */ -void colourFlip(rbnode *node) -{ - node->colour = !node->colour; - if (node->left != NULL) - node->left->colour = !node->left->colour; - if (node->right != NULL) - node->right->colour = !node->right->colour; -} - -/** - * Rotate a node left - * - * \param node The node to rotate - * \return The new root of the subtree - */ -rbnode *rotateLeft(rbnode *node) -{ - rbnode *r = node->right; - - node->right = r->left; - r->left = node; - r->colour = node->colour; - node->colour = RED; - - return r; -} - -/** - * Rotate a node right - * - * \param node The node to rotate - * \return The new root of the subtree - */ -rbnode *rotateRight(rbnode *node) -{ - rbnode *l = node->left; - - node->left = l->right; - l->right = node; - l->colour = node->colour; - node->colour = RED; - - return l; -} - -/** - * Move a red node left - * - * \param node Root of subtree to manipulate - * \return New root of subtree - */ -rbnode *moveRedLeft(rbnode *node) -{ - colourFlip(node); - - if (node->right != NULL && isRed(node->right->left)) { - node->right = rotateRight(node->right); - node = rotateLeft(node); - colourFlip(node); - - /* Maintain left-leaning requirement for 3-nodes after - * we've just split a 4-node */ - if (node->right != NULL && isRed(node->right->right)) { - node->right = rotateLeft(node->right); - } - } - - return node; -} - -/** - * Move a red node right - * - * \param node Root of subtree to manipulate - * \return New root of subtree - */ -rbnode *moveRedRight(rbnode *node) -{ - colourFlip(node); - - if (node->left != NULL && isRed(node->left->left)) { - node = rotateRight(node); - colourFlip(node); - } - - return node; -} - -/** - * Fix right-leaning red links - * - * \param node Root of subtree to manipulate - * \return New root of subtree - */ -rbnode *fixUp(rbnode *node) -{ - if (node->right != NULL && isRed(node->right)) - node = rotateLeft(node); - - if (node->left != NULL && isRed(node->left)) { - /* Two red links in a row aren't permitted */ - if (node->left->left != NULL && isRed(node->left->left)) { - node = rotateRight(node); - } else if (node->left->right != NULL && - isRed(node->left->right)) { - node->left = rotateLeft(node->left); - /* Force left child colour to black (it can only have - * been red before). We can't use colourFlip() here, as - * it will potentially break the right-hand subtree. */ - node->left->colour = BLACK; - } - } - - if (isRed(node->left) && isRed(node->right)) - colourFlip(node); - - return node; -} - -#ifdef USE_DELETEMAX -/** - * Delete the right-most node in a tree - * - * \param tree The tree instance - * \param node Root of subtree to manipulate - * \param destructor Destructor routine, or NULL - * \param pw Pointer to client-specific private data - * \return New root of subtree - */ -rbnode *deleteMax(parserutils_rbtree *tree, rbnode *node, - parserutils_rbtree_del destructor, void *pw) -{ - if (isRed(node->left)) - node = rotateRight(node); - - if (node->right == NULL) { - if (destructor != NULL) - destructor(node->key, node->value, pw); - rbnode_destroy(tree->alloc, tree->pw, node); - return NULL; - } - - if (!isRed(node->right) && !isRed(node->right->left)) - node = moveRedRight(node); - - assert(isRed(node->right) || isRed(node->right->left) || - isRed(node->right->right)); - - node->right = deleteMax(tree, node->right, destructor, pw); - - return fixUp(node); -} -#else -/** - * Delete the left-most node in a tree - * - * \param tree The tree instance - * \param node Root of subtree to manipulate - * \param destructor Destructor routine, or NULL - * \param pw Pointer to client-specific private data - * \return New root of subtree - */ -rbnode *deleteMin(parserutils_rbtree *tree, rbnode *node, - parserutils_rbtree_del destructor, void *pw) -{ - if (node->left == NULL) { - if (destructor != NULL) - destructor(node->key, node->value, pw); - rbnode_destroy(tree->alloc, tree->pw, node); - return NULL; - } - - if (!isRed(node->left) && !isRed(node->left->left)) - node = moveRedLeft(node); - - assert(isRed(node->left) || isRed(node->left->left)); - - node->left = deleteMin(tree, node->left, destructor, pw); - - return fixUp(node); -} -#endif - -/** - * Find the left-most node in a tree - * - * \param node Root of the subtree to consider - * \return Left-most node - */ -rbnode *findMin(rbnode *node) -{ - while (node->left != NULL) - node = node->left; - - return node; -} - -#ifndef NDEBUG -#ifdef RBTREE_VALIDATE -/** - * Verify the correctness of a tree - * - * \param node Root of tree to verify - */ -void verify_tree(rbnode *node) -{ - rbnode *invalid; - - if ((invalid = verify_tree_internal(node)) != NULL) { - dump_tree_raw(node, 0); - printf("Invalid node: %p\n", (void *) invalid); - assert(0 && "RB invariant violated"); - } -} - -/** - * Verify the structure of an RB tree - * - * \param node Root node of tree to validate - * \return Pointer to invalid node, or NULL - */ -rbnode *verify_tree_internal(rbnode *node) -{ - rbnode *invalid; - - if (node == NULL) - return NULL; - - if (isRed(node) && (isRed(node->left) || isRed(node->right))) { - return node; - } - - if (isRed(node->right) && !isRed(node->left)) { - return node; - } - - if (isRed(node->left) && isRed(node->left->left)) { - return node; - } - - if (isRed(node->right) && isRed(node->right->right)) { - return node; - } - - if ((invalid = verify_tree_internal(node->left)) != NULL) - return invalid; - - if ((invalid = verify_tree_internal(node->right)) != NULL) - return invalid; - - return NULL; -} - -/** - * Dump a tree to stdout in raw format - * - * \param node Root of tree to dump - * \param depth Current node depth - */ -void dump_tree_raw(rbnode *node, uint32_t depth) -{ - if (node->right) - dump_tree_raw(node->right, depth + 1); - - for (uint32_t i = 0; i < depth; i++) - putchar(' '); - - printf("%p %p %p [%c]\n", (void *) node, node->key, node->value, - isRed(node) ? 'r' : 'b'); - - if (node->left) - dump_tree_raw(node->left, depth + 1); -} -#endif - -/** - * Recursively print out a pre-order traversal of a RB tree - * - * \param node Root node of subtree - * \param print Print routine - * \param depth Current depth in tree - */ -void dump_tree(rbnode *node, parserutils_rbtree_print print, uint32_t depth) -{ - if (node->right) - dump_tree(node->right, print, depth + 1); - - print(node->key, node->value, depth); - - if (node->left) - dump_tree(node->left, print, depth + 1); -} - -/** - * Print out a RB tree - * - * \param tree The tree to print - * \param print Print routine - */ -void parserutils_rbtree_dump(parserutils_rbtree *tree, - parserutils_rbtree_print print) -{ - if (tree == NULL || tree->root == NULL) - return; - - dump_tree(tree->root, print, 0); -} -#endif - |