diff options
Diffstat (limited to 'src/backend/lib/rbtree.c')
-rw-r--r-- | src/backend/lib/rbtree.c | 770 |
1 files changed, 770 insertions, 0 deletions
diff --git a/src/backend/lib/rbtree.c b/src/backend/lib/rbtree.c new file mode 100644 index 0000000..536df1f --- /dev/null +++ b/src/backend/lib/rbtree.c @@ -0,0 +1,770 @@ +/*------------------------------------------------------------------------- + * + * rbtree.c + * implementation for PostgreSQL generic Red-Black binary tree package + * Adopted from http://algolist.manual.ru/ds/rbtree.php + * + * This code comes from Thomas Niemann's "Sorting and Searching Algorithms: + * a Cookbook". + * + * See http://www.cs.auckland.ac.nz/software/AlgAnim/niemann/s_man.htm for + * license terms: "Source code, when part of a software project, may be used + * freely without reference to the author." + * + * Red-black trees are a type of balanced binary tree wherein (1) any child of + * a red node is always black, and (2) every path from root to leaf traverses + * an equal number of black nodes. From these properties, it follows that the + * longest path from root to leaf is only about twice as long as the shortest, + * so lookups are guaranteed to run in O(lg n) time. + * + * Copyright (c) 2009-2021, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/lib/rbtree.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "lib/rbtree.h" + + +/* + * Colors of nodes (values of RBTNode.color) + */ +#define RBTBLACK (0) +#define RBTRED (1) + +/* + * RBTree control structure + */ +struct RBTree +{ + RBTNode *root; /* root node, or RBTNIL if tree is empty */ + + /* Remaining fields are constant after rbt_create */ + + Size node_size; /* actual size of tree nodes */ + /* The caller-supplied manipulation functions */ + rbt_comparator comparator; + rbt_combiner combiner; + rbt_allocfunc allocfunc; + rbt_freefunc freefunc; + /* Passthrough arg passed to all manipulation functions */ + void *arg; +}; + +/* + * all leafs are sentinels, use customized NIL name to prevent + * collision with system-wide constant NIL which is actually NULL + */ +#define RBTNIL (&sentinel) + +static RBTNode sentinel = +{ + RBTBLACK, RBTNIL, RBTNIL, NULL +}; + + +/* + * rbt_create: create an empty RBTree + * + * Arguments are: + * node_size: actual size of tree nodes (> sizeof(RBTNode)) + * The manipulation functions: + * comparator: compare two RBTNodes for less/equal/greater + * combiner: merge an existing tree entry with a new one + * allocfunc: allocate a new RBTNode + * freefunc: free an old RBTNode + * arg: passthrough pointer that will be passed to the manipulation functions + * + * Note that the combiner's righthand argument will be a "proposed" tree node, + * ie the input to rbt_insert, in which the RBTNode fields themselves aren't + * valid. Similarly, either input to the comparator may be a "proposed" node. + * This shouldn't matter since the functions aren't supposed to look at the + * RBTNode fields, only the extra fields of the struct the RBTNode is embedded + * in. + * + * The freefunc should just be pfree or equivalent; it should NOT attempt + * to free any subsidiary data, because the node passed to it may not contain + * valid data! freefunc can be NULL if caller doesn't require retail + * space reclamation. + * + * The RBTree node is palloc'd in the caller's memory context. Note that + * all contents of the tree are actually allocated by the caller, not here. + * + * Since tree contents are managed by the caller, there is currently not + * an explicit "destroy" operation; typically a tree would be freed by + * resetting or deleting the memory context it's stored in. You can pfree + * the RBTree node if you feel the urge. + */ +RBTree * +rbt_create(Size node_size, + rbt_comparator comparator, + rbt_combiner combiner, + rbt_allocfunc allocfunc, + rbt_freefunc freefunc, + void *arg) +{ + RBTree *tree = (RBTree *) palloc(sizeof(RBTree)); + + Assert(node_size > sizeof(RBTNode)); + + tree->root = RBTNIL; + tree->node_size = node_size; + tree->comparator = comparator; + tree->combiner = combiner; + tree->allocfunc = allocfunc; + tree->freefunc = freefunc; + + tree->arg = arg; + + return tree; +} + +/* Copy the additional data fields from one RBTNode to another */ +static inline void +rbt_copy_data(RBTree *rbt, RBTNode *dest, const RBTNode *src) +{ + memcpy(dest + 1, src + 1, rbt->node_size - sizeof(RBTNode)); +} + +/********************************************************************** + * Search * + **********************************************************************/ + +/* + * rbt_find: search for a value in an RBTree + * + * data represents the value to try to find. Its RBTNode fields need not + * be valid, it's the extra data in the larger struct that is of interest. + * + * Returns the matching tree entry, or NULL if no match is found. + */ +RBTNode * +rbt_find(RBTree *rbt, const RBTNode *data) +{ + RBTNode *node = rbt->root; + + while (node != RBTNIL) + { + int cmp = rbt->comparator(data, node, rbt->arg); + + if (cmp == 0) + return node; + else if (cmp < 0) + node = node->left; + else + node = node->right; + } + + return NULL; +} + +/* + * rbt_leftmost: fetch the leftmost (smallest-valued) tree node. + * Returns NULL if tree is empty. + * + * Note: in the original implementation this included an unlink step, but + * that's a bit awkward. Just call rbt_delete on the result if that's what + * you want. + */ +RBTNode * +rbt_leftmost(RBTree *rbt) +{ + RBTNode *node = rbt->root; + RBTNode *leftmost = rbt->root; + + while (node != RBTNIL) + { + leftmost = node; + node = node->left; + } + + if (leftmost != RBTNIL) + return leftmost; + + return NULL; +} + +/********************************************************************** + * Insertion * + **********************************************************************/ + +/* + * Rotate node x to left. + * + * x's right child takes its place in the tree, and x becomes the left + * child of that node. + */ +static void +rbt_rotate_left(RBTree *rbt, RBTNode *x) +{ + RBTNode *y = x->right; + + /* establish x->right link */ + x->right = y->left; + if (y->left != RBTNIL) + y->left->parent = x; + + /* establish y->parent link */ + if (y != RBTNIL) + y->parent = x->parent; + if (x->parent) + { + if (x == x->parent->left) + x->parent->left = y; + else + x->parent->right = y; + } + else + { + rbt->root = y; + } + + /* link x and y */ + y->left = x; + if (x != RBTNIL) + x->parent = y; +} + +/* + * Rotate node x to right. + * + * x's left right child takes its place in the tree, and x becomes the right + * child of that node. + */ +static void +rbt_rotate_right(RBTree *rbt, RBTNode *x) +{ + RBTNode *y = x->left; + + /* establish x->left link */ + x->left = y->right; + if (y->right != RBTNIL) + y->right->parent = x; + + /* establish y->parent link */ + if (y != RBTNIL) + y->parent = x->parent; + if (x->parent) + { + if (x == x->parent->right) + x->parent->right = y; + else + x->parent->left = y; + } + else + { + rbt->root = y; + } + + /* link x and y */ + y->right = x; + if (x != RBTNIL) + x->parent = y; +} + +/* + * Maintain Red-Black tree balance after inserting node x. + * + * The newly inserted node is always initially marked red. That may lead to + * a situation where a red node has a red child, which is prohibited. We can + * always fix the problem by a series of color changes and/or "rotations", + * which move the problem progressively higher up in the tree. If one of the + * two red nodes is the root, we can always fix the problem by changing the + * root from red to black. + * + * (This does not work lower down in the tree because we must also maintain + * the invariant that every leaf has equal black-height.) + */ +static void +rbt_insert_fixup(RBTree *rbt, RBTNode *x) +{ + /* + * x is always a red node. Initially, it is the newly inserted node. Each + * iteration of this loop moves it higher up in the tree. + */ + while (x != rbt->root && x->parent->color == RBTRED) + { + /* + * x and x->parent are both red. Fix depends on whether x->parent is + * a left or right child. In either case, we define y to be the + * "uncle" of x, that is, the other child of x's grandparent. + * + * If the uncle is red, we flip the grandparent to red and its two + * children to black. Then we loop around again to check whether the + * grandparent still has a problem. + * + * If the uncle is black, we will perform one or two "rotations" to + * balance the tree. Either x or x->parent will take the + * grandparent's position in the tree and recolored black, and the + * original grandparent will be recolored red and become a child of + * that node. This always leaves us with a valid red-black tree, so + * the loop will terminate. + */ + if (x->parent == x->parent->parent->left) + { + RBTNode *y = x->parent->parent->right; + + if (y->color == RBTRED) + { + /* uncle is RBTRED */ + x->parent->color = RBTBLACK; + y->color = RBTBLACK; + x->parent->parent->color = RBTRED; + + x = x->parent->parent; + } + else + { + /* uncle is RBTBLACK */ + if (x == x->parent->right) + { + /* make x a left child */ + x = x->parent; + rbt_rotate_left(rbt, x); + } + + /* recolor and rotate */ + x->parent->color = RBTBLACK; + x->parent->parent->color = RBTRED; + + rbt_rotate_right(rbt, x->parent->parent); + } + } + else + { + /* mirror image of above code */ + RBTNode *y = x->parent->parent->left; + + if (y->color == RBTRED) + { + /* uncle is RBTRED */ + x->parent->color = RBTBLACK; + y->color = RBTBLACK; + x->parent->parent->color = RBTRED; + + x = x->parent->parent; + } + else + { + /* uncle is RBTBLACK */ + if (x == x->parent->left) + { + x = x->parent; + rbt_rotate_right(rbt, x); + } + x->parent->color = RBTBLACK; + x->parent->parent->color = RBTRED; + + rbt_rotate_left(rbt, x->parent->parent); + } + } + } + + /* + * The root may already have been black; if not, the black-height of every + * node in the tree increases by one. + */ + rbt->root->color = RBTBLACK; +} + +/* + * rbt_insert: insert a new value into the tree. + * + * data represents the value to insert. Its RBTNode fields need not + * be valid, it's the extra data in the larger struct that is of interest. + * + * If the value represented by "data" is not present in the tree, then + * we copy "data" into a new tree entry and return that node, setting *isNew + * to true. + * + * If the value represented by "data" is already present, then we call the + * combiner function to merge data into the existing node, and return the + * existing node, setting *isNew to false. + * + * "data" is unmodified in either case; it's typically just a local + * variable in the caller. + */ +RBTNode * +rbt_insert(RBTree *rbt, const RBTNode *data, bool *isNew) +{ + RBTNode *current, + *parent, + *x; + int cmp; + + /* find where node belongs */ + current = rbt->root; + parent = NULL; + cmp = 0; /* just to prevent compiler warning */ + + while (current != RBTNIL) + { + cmp = rbt->comparator(data, current, rbt->arg); + if (cmp == 0) + { + /* + * Found node with given key. Apply combiner. + */ + rbt->combiner(current, data, rbt->arg); + *isNew = false; + return current; + } + parent = current; + current = (cmp < 0) ? current->left : current->right; + } + + /* + * Value is not present, so create a new node containing data. + */ + *isNew = true; + + x = rbt->allocfunc(rbt->arg); + + x->color = RBTRED; + + x->left = RBTNIL; + x->right = RBTNIL; + x->parent = parent; + rbt_copy_data(rbt, x, data); + + /* insert node in tree */ + if (parent) + { + if (cmp < 0) + parent->left = x; + else + parent->right = x; + } + else + { + rbt->root = x; + } + + rbt_insert_fixup(rbt, x); + + return x; +} + +/********************************************************************** + * Deletion * + **********************************************************************/ + +/* + * Maintain Red-Black tree balance after deleting a black node. + */ +static void +rbt_delete_fixup(RBTree *rbt, RBTNode *x) +{ + /* + * x is always a black node. Initially, it is the former child of the + * deleted node. Each iteration of this loop moves it higher up in the + * tree. + */ + while (x != rbt->root && x->color == RBTBLACK) + { + /* + * Left and right cases are symmetric. Any nodes that are children of + * x have a black-height one less than the remainder of the nodes in + * the tree. We rotate and recolor nodes to move the problem up the + * tree: at some stage we'll either fix the problem, or reach the root + * (where the black-height is allowed to decrease). + */ + if (x == x->parent->left) + { + RBTNode *w = x->parent->right; + + if (w->color == RBTRED) + { + w->color = RBTBLACK; + x->parent->color = RBTRED; + + rbt_rotate_left(rbt, x->parent); + w = x->parent->right; + } + + if (w->left->color == RBTBLACK && w->right->color == RBTBLACK) + { + w->color = RBTRED; + + x = x->parent; + } + else + { + if (w->right->color == RBTBLACK) + { + w->left->color = RBTBLACK; + w->color = RBTRED; + + rbt_rotate_right(rbt, w); + w = x->parent->right; + } + w->color = x->parent->color; + x->parent->color = RBTBLACK; + w->right->color = RBTBLACK; + + rbt_rotate_left(rbt, x->parent); + x = rbt->root; /* Arrange for loop to terminate. */ + } + } + else + { + RBTNode *w = x->parent->left; + + if (w->color == RBTRED) + { + w->color = RBTBLACK; + x->parent->color = RBTRED; + + rbt_rotate_right(rbt, x->parent); + w = x->parent->left; + } + + if (w->right->color == RBTBLACK && w->left->color == RBTBLACK) + { + w->color = RBTRED; + + x = x->parent; + } + else + { + if (w->left->color == RBTBLACK) + { + w->right->color = RBTBLACK; + w->color = RBTRED; + + rbt_rotate_left(rbt, w); + w = x->parent->left; + } + w->color = x->parent->color; + x->parent->color = RBTBLACK; + w->left->color = RBTBLACK; + + rbt_rotate_right(rbt, x->parent); + x = rbt->root; /* Arrange for loop to terminate. */ + } + } + } + x->color = RBTBLACK; +} + +/* + * Delete node z from tree. + */ +static void +rbt_delete_node(RBTree *rbt, RBTNode *z) +{ + RBTNode *x, + *y; + + /* This is just paranoia: we should only get called on a valid node */ + if (!z || z == RBTNIL) + return; + + /* + * y is the node that will actually be removed from the tree. This will + * be z if z has fewer than two children, or the tree successor of z + * otherwise. + */ + if (z->left == RBTNIL || z->right == RBTNIL) + { + /* y has a RBTNIL node as a child */ + y = z; + } + else + { + /* find tree successor */ + y = z->right; + while (y->left != RBTNIL) + y = y->left; + } + + /* x is y's only child */ + if (y->left != RBTNIL) + x = y->left; + else + x = y->right; + + /* Remove y from the tree. */ + x->parent = y->parent; + if (y->parent) + { + if (y == y->parent->left) + y->parent->left = x; + else + y->parent->right = x; + } + else + { + rbt->root = x; + } + + /* + * If we removed the tree successor of z rather than z itself, then move + * the data for the removed node to the one we were supposed to remove. + */ + if (y != z) + rbt_copy_data(rbt, z, y); + + /* + * Removing a black node might make some paths from root to leaf contain + * fewer black nodes than others, or it might make two red nodes adjacent. + */ + if (y->color == RBTBLACK) + rbt_delete_fixup(rbt, x); + + /* Now we can recycle the y node */ + if (rbt->freefunc) + rbt->freefunc(y, rbt->arg); +} + +/* + * rbt_delete: remove the given tree entry + * + * "node" must have previously been found via rbt_find or rbt_leftmost. + * It is caller's responsibility to free any subsidiary data attached + * to the node before calling rbt_delete. (Do *not* try to push that + * responsibility off to the freefunc, as some other physical node + * may be the one actually freed!) + */ +void +rbt_delete(RBTree *rbt, RBTNode *node) +{ + rbt_delete_node(rbt, node); +} + +/********************************************************************** + * Traverse * + **********************************************************************/ + +static RBTNode * +rbt_left_right_iterator(RBTreeIterator *iter) +{ + if (iter->last_visited == NULL) + { + iter->last_visited = iter->rbt->root; + while (iter->last_visited->left != RBTNIL) + iter->last_visited = iter->last_visited->left; + + return iter->last_visited; + } + + if (iter->last_visited->right != RBTNIL) + { + iter->last_visited = iter->last_visited->right; + while (iter->last_visited->left != RBTNIL) + iter->last_visited = iter->last_visited->left; + + return iter->last_visited; + } + + for (;;) + { + RBTNode *came_from = iter->last_visited; + + iter->last_visited = iter->last_visited->parent; + if (iter->last_visited == NULL) + { + iter->is_over = true; + break; + } + + if (iter->last_visited->left == came_from) + break; /* came from left sub-tree, return current + * node */ + + /* else - came from right sub-tree, continue to move up */ + } + + return iter->last_visited; +} + +static RBTNode * +rbt_right_left_iterator(RBTreeIterator *iter) +{ + if (iter->last_visited == NULL) + { + iter->last_visited = iter->rbt->root; + while (iter->last_visited->right != RBTNIL) + iter->last_visited = iter->last_visited->right; + + return iter->last_visited; + } + + if (iter->last_visited->left != RBTNIL) + { + iter->last_visited = iter->last_visited->left; + while (iter->last_visited->right != RBTNIL) + iter->last_visited = iter->last_visited->right; + + return iter->last_visited; + } + + for (;;) + { + RBTNode *came_from = iter->last_visited; + + iter->last_visited = iter->last_visited->parent; + if (iter->last_visited == NULL) + { + iter->is_over = true; + break; + } + + if (iter->last_visited->right == came_from) + break; /* came from right sub-tree, return current + * node */ + + /* else - came from left sub-tree, continue to move up */ + } + + return iter->last_visited; +} + +/* + * rbt_begin_iterate: prepare to traverse the tree in any of several orders + * + * After calling rbt_begin_iterate, call rbt_iterate repeatedly until it + * returns NULL or the traversal stops being of interest. + * + * If the tree is changed during traversal, results of further calls to + * rbt_iterate are unspecified. Multiple concurrent iterators on the same + * tree are allowed. + * + * The iterator state is stored in the 'iter' struct. The caller should + * treat it as an opaque struct. + */ +void +rbt_begin_iterate(RBTree *rbt, RBTOrderControl ctrl, RBTreeIterator *iter) +{ + /* Common initialization for all traversal orders */ + iter->rbt = rbt; + iter->last_visited = NULL; + iter->is_over = (rbt->root == RBTNIL); + + switch (ctrl) + { + case LeftRightWalk: /* visit left, then self, then right */ + iter->iterate = rbt_left_right_iterator; + break; + case RightLeftWalk: /* visit right, then self, then left */ + iter->iterate = rbt_right_left_iterator; + break; + default: + elog(ERROR, "unrecognized rbtree iteration order: %d", ctrl); + } +} + +/* + * rbt_iterate: return the next node in traversal order, or NULL if no more + */ +RBTNode * +rbt_iterate(RBTreeIterator *iter) +{ + if (iter->is_over) + return NULL; + + return iter->iterate(iter); +} |