From ace65b453151845bc361f21f3e5b651c35f9f126 Mon Sep 17 00:00:00 2001 From: Jon Santmyer Date: Wed, 22 May 2024 13:00:41 -0400 Subject: massive refactor for mp and organization --- klib/rbtree.c | 361 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 klib/rbtree.c (limited to 'klib/rbtree.c') diff --git a/klib/rbtree.c b/klib/rbtree.c new file mode 100644 index 0000000..2920384 --- /dev/null +++ b/klib/rbtree.c @@ -0,0 +1,361 @@ +#include "rbtree.h" +#include "assert.h" +#include "string.h" + +#define BLACK 0 +#define RED 1 + +#define LEFT 0 +#define left children[LEFT] + +#define RIGHT 1 +#define right children[RIGHT] + +#define CHILD_DIR(c) (c->parent->children[LEFT] == c ? LEFT : RIGHT) + +/**Given a root and a direction, rotate the subtree such that the order of the + * elements are unchanged. + * The opposite direction child of the given root is treated as the pivot point + * The dir-most child of the pivot becomes the opposite dir child of the root. + * The pivot becomes the root and vice-versa. + * @param tree to do rotation on. + * @param root of rotation. + * @param dir to rotate by. + * @return new root node.*/ +rbnode_t* +s_node_rotate(rbtree_t *tree, rbnode_t *root, bool dir) +{ + assert(root != NULL); + rbnode_t *gparent = root->parent; + rbnode_t *sibling = root->children[1-dir]; + rbnode_t *child = NULL; + + assert(sibling != NULL); + child = sibling->children[dir]; + + /* Opposite child of root is dir-most child of sibling.*/ + root->children[1-dir] = child; + if(child != NULL) child->parent = root; + + /* Child of sibling is the root.*/ + sibling->children[ dir] = root; + root->parent = sibling; + + /* Parent of sibling is the grandparent. */ + sibling->parent = gparent; + if(gparent != NULL) + gparent->children[CHILD_DIR(root)] = sibling; + else + tree->root = sibling; + + return sibling; +} + +/**Search the rbtree for a node that matches the key. + * If no such node is found, returns NULL. + * @param tree to search through. + * @param key to search for. + * @return found node.*/ +rbnode_t* +s_find(rbtree_t *tree, intmax_t key) +{ + rbnode_t *node = tree->root; + while(node != NULL && node->key != key) { + node = node->children[node->key > key]; + } + return node; +} + +rbnode_t* +s_closest(rbtree_t *tree, intmax_t key) +{ + rbnode_t *node = tree->root; + while(node != NULL && node->key != key) { + rbnode_t *child = node->children[node->key > key]; + if(child == NULL) return node; + node = child; + } + return node; +} + +/**Find the left-most node for this subtree. + * @param node root of the subtree to search through. + * @return left-most node.*/ +rbnode_t* +s_node_min(rbnode_t *node) +{ + while(node->left != NULL) node = node->left; + return node; +} + +/**Find the successor for the specified node. + * The successor is defined as the left-most node of the right sub-tree. + * (the smallest node larger than the root) + * @param node to search for. + * @return the successor.*/ +rbnode_t* +s_node_successor(rbnode_t *node) +{ + if(node->right != NULL) + return s_node_min(node->right); + /* If the node has no left child (meaning that the node is larger than it's + * parents), we cycle up through the RHS of the parent tree (such that each + * parent is smaller than the child) until we reach a point by which the + * child node is smaller than the parent.*/ + rbnode_t *parent = node->parent; + while(parent != NULL && node == parent->right) { + node = parent; + parent = node->parent; + } + return parent; +} + +/**Copy the data from one node onto another, keeping parent and child relations. + * @prarm tree for metadata purposes. + * @param dest destination node. + * @param src source node.*/ +void +s_node_copy_value(rbtree_t *tree, rbnode_t *dest, rbnode_t *src) +{ + dest->key = src->key; + memcpy(dest->data, src->data, tree->objw); +} + +/**Perform tree adjustments after insertion to rebalance the rbtree. + * @param tree to rebalance. + * @param node that was inserted. + * @param parent of the node. + * @param dir that the node was inserted in.*/ +void +s_node_insert_fix( + rbtree_t *tree, + rbnode_t *node, + rbnode_t *parent, + bool dir) +{ + rbnode_t *gparent; + rbnode_t *uncle; + while((parent = node->parent) != NULL) { + if(parent->color == BLACK) return; /* Tree is valid. no fixes needed.*/ + /*Parent is known to be red.*/ + if((gparent = parent->parent) == NULL) { + /*Parent is red and the root. We can just fix it and return.*/ + parent->color = BLACK; + return; + } + /* Parent is red and has a parent. + * We now need to fix the parent.*/ + dir = CHILD_DIR(parent); + uncle = gparent->children[1-dir]; + if(uncle == NULL || uncle->color == BLACK) { + /* Parent is red, but uncle (opposite child of gparent) is black. + * We need to rotate the gparent node such that the parent takes + * its place. However, if the current node is an inner child of + * gparent, we need to rotate N into P.*/ + if(node == parent->children[1-dir]) { + /* Node is an inner child. We need to rotate N and P first.*/ + s_node_rotate(tree, parent, dir); + node = parent; + parent = gparent->children[dir]; + } + /* N is not an inner child, we can rotate the tree properly.*/ + s_node_rotate(tree, gparent, 1-dir); + parent->color = BLACK; + gparent->color = RED; + return; + } + } +} + +/**Insert a node into the rbtree on the parent and direction given. + * @param tree to insert into. + * @param node to insert. + * @param parent node to insert onto. + * @param dir direction to insert node.*/ +void +s_node_insert(rbtree_t *tree, rbnode_t *node, rbnode_t *parent, bool dir) +{ + node->color = RED; + node->left = NULL; + node->right = NULL; + node->parent = parent; + if(parent == NULL) { + tree->root = node; + return; + } + parent->children[dir] = node; + s_node_insert_fix(tree, node, parent, dir); +} + +void +s_node_remove_fixup(rbtree_t *tree, rbnode_t *node) +{ + bool dir; + rbnode_t *parent = node->parent; + rbnode_t *sibling; + rbnode_t *cnephew; + rbnode_t *dnephew; + + dir = CHILD_DIR(node); + parent->children[dir] = NULL; + while((parent = node->parent) != NULL) + { + dir = CHILD_DIR(node); + sibling = parent->children[1-dir]; + cnephew = parent->children[ dir]; + dnephew = parent->children[1-dir]; + if(sibling->color == RED) + goto case3; + if(dnephew != NULL && dnephew->color == RED) + goto case6; + if(cnephew != NULL && cnephew->color == RED) + goto case5; + if(parent->color == RED) + goto case4; + + /* The parent, both nephews, and sibling are black. We need to paint + * the sibling black and move up a level.*/ + sibling->color = RED; + node = parent; + } + return; + +case3: + /* The sibling is red, which means both nephews and the parent are black. + * We rotate the subtree so that the parent moves to become the siblings + * left child. The tree is then repainted.*/ + s_node_rotate(tree, parent, dir); + parent->color = RED; + sibling->color = BLACK; + /* Now we are working on the sibling subtree.*/ + sibling = cnephew; + dnephew = sibling->children[1-dir]; + if(dnephew != NULL && dnephew->color == BLACK) + goto case6; + cnephew = sibling->children[dir]; + if(cnephew != NULL && cnephew->color == RED) + goto case5; + +case4: + sibling->color = RED; + parent->color = BLACK; + return; + +case5: + s_node_rotate(tree, sibling, 1-dir); + sibling->color = RED; + cnephew->color = BLACK; + dnephew = sibling; + sibling = cnephew; + +case6: + s_node_rotate(tree, parent, dir); + sibling->color = parent->color; + parent->color = BLACK; + dnephew->color = BLACK; +} + +void +s_node_remove(rbtree_t *tree, rbnode_t *node) +{ + rbnode_t *parent = node->parent; + if(node->left != NULL && node->right != NULL) { + /* Node is a full tree. We can replace the node with it's successor to + * keep the balance. + * Needs to recurse if the successor has a right-most child, which is + * dealt with in the next case.*/ + rbnode_t *successor = s_node_successor(node); + s_node_copy_value(tree, node, successor); + s_node_remove(tree, successor); + return; + } else if(node->left == NULL && node->right == NULL) { + if(parent == NULL) { + tree->root = NULL; + kfree(node); + return; + } + if(node->color == RED) { + parent->children[CHILD_DIR(node)] = NULL; + kfree(node); + return; + } + s_node_remove_fixup(tree, node); + } else { + /* Node has a single child. Because the child must be red as per spec, + * and this node must be black, we can simply replace the node + * with it's child and color it black.*/ + rbnode_t *child = node->left == NULL ? node->right : node->left; + memcpy(node, child, sizeof(rbnode_t) + tree->objw); + node->parent = parent; + node->color = BLACK; + kfree(child); + return; + } +} + +void +__rbtree_new(rbtree_t *tree, size_t objw) +{ + *tree = (rbtree_t) { + .root = NULL, + .objw = objw, + }; +} + +void* +rbtree_find(rbtree_t *tree, intmax_t key) +{ + rbnode_t *found = s_find(tree, key); + if(found == NULL) return NULL; + return found->data; +} + +rbnode_t* +s_node_new(rbtree_t *tree, intmax_t key, void *data) +{ + rbnode_t *node = kmalloc(sizeof(rbnode_t) + tree->objw); + if(data != NULL) memcpy(node->data, data, tree->objw); + node->key = key; + return node; +} + +void* +rbtree_insert(rbtree_t *tree, intmax_t key, void *data) +{ + assert(tree != NULL); + + rbnode_t *node = s_closest(tree, key); + if(node != NULL && node->key == key) { + memcpy(node->data, data, tree->objw); + } else if(node != NULL) { + rbnode_t *child = s_node_new(tree, key, data); + s_node_insert(tree, child, node, node->key > key); + return child->data; + } else { + node = s_node_new(tree, key, data); + node->parent = NULL; + node->children[0] = node->children[1] = NULL; + node->color = BLACK; + tree->root = node; + } + return node->data; +} + +void *rbtree_reserve(rbtree_t *tree, intmax_t key) +{ + assert(tree != NULL); + + rbnode_t *node = s_closest(tree, key); + if(node != NULL && node->key == key) { + return node->data; + } else if(node != NULL) { + rbnode_t *child = s_node_new(tree, key, NULL); + s_node_insert(tree, child, node, node->key > key); + return child->data; + } else { + node = s_node_new(tree, key, NULL); + tree->root = node; + } + return node->data; +} + -- cgit v1.2.1