diff options
author | Teodor Sigaev <teodor@sigaev.ru> | 2010-02-11 14:29:50 +0000 |
---|---|---|
committer | Teodor Sigaev <teodor@sigaev.ru> | 2010-02-11 14:29:50 +0000 |
commit | 5209c084a646018bf429e4a1800e76e7b8b548a7 (patch) | |
tree | c065623638186055ecac27ec34bf61842b9f66bd /src/backend/utils/misc/rbtree.c | |
parent | 161d9d51b321f021d4231001ccc32988edfccda0 (diff) | |
download | postgresql-5209c084a646018bf429e4a1800e76e7b8b548a7.tar.gz postgresql-5209c084a646018bf429e4a1800e76e7b8b548a7.zip |
Generic implementation of red-black binary tree. It's planned to use in
several places, but for now only GIN uses it during index creation.
Using self-balanced tree greatly speeds up index creation in corner cases
with preordered data.
Diffstat (limited to 'src/backend/utils/misc/rbtree.c')
-rw-r--r-- | src/backend/utils/misc/rbtree.c | 790 |
1 files changed, 790 insertions, 0 deletions
diff --git a/src/backend/utils/misc/rbtree.c b/src/backend/utils/misc/rbtree.c new file mode 100644 index 00000000000..d7b129fcb24 --- /dev/null +++ b/src/backend/utils/misc/rbtree.c @@ -0,0 +1,790 @@ +/*------------------------------------------------------------------------- + * + * 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) 1996-2009, PostgreSQL Global Development Group + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/utils/misc/rbtree.c,v 1.1 2010/02/11 14:29:50 teodor Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "utils/rbtree.h" + +/********************************************************************** + * Declarations * + **********************************************************************/ + +/* + * Values for RBNode->iteratorState + */ +#define InitialState (0) +#define FirstStepDone (1) +#define SecondStepDone (2) +#define ThirdStepDone (3) + +/* + * Colors of node + */ +#define RBBLACK (0) +#define RBRED (1) + +typedef struct RBNode +{ + uint32 iteratorState:2, + color: 1 , + unused: 29; + struct RBNode *left; + struct RBNode *right; + struct RBNode *parent; + void *data; +} RBNode; + +struct RBTree +{ + RBNode *root; + rb_comparator comparator; + rb_appendator appendator; + rb_freefunc freefunc; + void *arg; +}; + +struct RBTreeIterator +{ + RBNode *node; + void *(*iterate) (RBTreeIterator *iterator); +}; + +/* + * all leafs are sentinels, use customized NIL name to prevent + * collision with sytem-wide NIL which is actually NULL + */ +#define RBNIL &sentinel + +RBNode sentinel = {InitialState, RBBLACK, 0, RBNIL, RBNIL, NULL, NULL}; + +/********************************************************************** + * Create * + **********************************************************************/ + +RBTree * +rb_create(rb_comparator comparator, rb_appendator appendator, + rb_freefunc freefunc, void *arg) +{ + RBTree *tree = palloc(sizeof(RBTree)); + + tree->root = RBNIL; + tree->comparator = comparator; + tree->appendator = appendator; + tree->freefunc = freefunc; + tree->arg = arg; + + return tree; +} + +/********************************************************************** + * Search * + **********************************************************************/ + +void * +rb_find(RBTree *rb, void *data) +{ + RBNode *node = rb->root; + int cmp; + + while (node != RBNIL) + { + cmp = rb->comparator(data, node->data, rb->arg); + + if (cmp == 0) + return node->data; + else if (cmp < 0) + node = node->left; + else + node = node->right; + } + + 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 +rb_rotate_left(RBTree *rb, RBNode *x) +{ + RBNode *y = x->right; + + /* establish x->right link */ + x->right = y->left; + if (y->left != RBNIL) + y->left->parent = x; + + /* establish y->parent link */ + if (y != RBNIL) + y->parent = x->parent; + if (x->parent) + { + if (x == x->parent->left) + x->parent->left = y; + else + x->parent->right = y; + } + else + { + rb->root = y; + } + + /* link x and y */ + y->left = x; + if (x != RBNIL) + 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 +rb_rotate_right(RBTree *rb, RBNode *x) +{ + RBNode *y = x->left; + + /* establish x->left link */ + x->left = y->right; + if (y->right != RBNIL) + y->right->parent = x; + + /* establish y->parent link */ + if (y != RBNIL) + y->parent = x->parent; + if (x->parent) + { + if (x == x->parent->right) + x->parent->right = y; + else + x->parent->left = y; + } + else + { + rb->root = y; + } + + /* link x and y */ + y->right = x; + if (x != RBNIL) + 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 +rb_insert_fixup(RBTree *rb, RBNode *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 != rb->root && x->parent->color == RBRED) + { + /* + * 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) + { + RBNode *y = x->parent->parent->right; + + if (y->color == RBRED) + { + /* uncle is RBRED */ + x->parent->color = RBBLACK; + y->color = RBBLACK; + x->parent->parent->color = RBRED; + x = x->parent->parent; + } + else + { + /* uncle is RBBLACK */ + if (x == x->parent->right) + { + /* make x a left child */ + x = x->parent; + rb_rotate_left(rb, x); + } + + /* recolor and rotate */ + x->parent->color = RBBLACK; + x->parent->parent->color = RBRED; + rb_rotate_right(rb, x->parent->parent); + } + } + else + { + /* mirror image of above code */ + RBNode *y = x->parent->parent->left; + + if (y->color == RBRED) + { + /* uncle is RBRED */ + x->parent->color = RBBLACK; + y->color = RBBLACK; + x->parent->parent->color = RBRED; + x = x->parent->parent; + } + else + { + /* uncle is RBBLACK */ + if (x == x->parent->left) + { + x = x->parent; + rb_rotate_right(rb, x); + } + x->parent->color = RBBLACK; + x->parent->parent->color = RBRED; + rb_rotate_left(rb, x->parent->parent); + } + } + } + + /* + * The root may already have been black; if not, the black-height of every + * node in the tree increases by one. + */ + rb->root->color = RBBLACK; +} + +/* + * Allocate node for data and insert in tree. + * + * Return old data (or result of appendator method) if it exists and NULL + * otherwise. + */ +void * +rb_insert(RBTree *rb, void *data) +{ + RBNode *current, + *parent, + *x; + int cmp; + + /* find where node belongs */ + current = rb->root; + parent = NULL; + while (current != RBNIL) + { + cmp = rb->comparator(data, current->data, rb->arg); + if (cmp == 0) + { + /* + * Found node with given key. If appendator method is provided, + * call it to join old and new data; else, new data replaces old + * data. + */ + if (rb->appendator) + { + current->data = rb->appendator(current->data, data, rb->arg); + return current->data; + } + else + { + void *old = current->data; + + current->data = data; + return old; + } + } + parent = current; + current = (cmp < 0) ? current->left : current->right; + } + + /* setup new node in tree */ + x = palloc(sizeof(RBNode)); + x->data = data; + x->parent = parent; + x->left = RBNIL; + x->right = RBNIL; + x->color = RBRED; + x->iteratorState = InitialState; + + /* insert node in tree */ + if (parent) + { + if (cmp < 0) + parent->left = x; + else + parent->right = x; + } + else + { + rb->root = x; + } + + rb_insert_fixup(rb, x); + return NULL; +} + +/********************************************************************** + * Deletion * + **********************************************************************/ + +/* + * Maintain Red-Black tree balance after deleting a black node. + */ +static void +rb_delete_fixup(RBTree *rb, RBNode *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 != rb->root && x->color == RBBLACK) + { + /* + * 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) + { + RBNode *w = x->parent->right; + + if (w->color == RBRED) + { + w->color = RBBLACK; + x->parent->color = RBRED; + rb_rotate_left(rb, x->parent); + w = x->parent->right; + } + + if (w->left->color == RBBLACK && w->right->color == RBBLACK) + { + w->color = RBRED; + x = x->parent; + } + else + { + if (w->right->color == RBBLACK) + { + w->left->color = RBBLACK; + w->color = RBRED; + rb_rotate_right(rb, w); + w = x->parent->right; + } + w->color = x->parent->color; + x->parent->color = RBBLACK; + w->right->color = RBBLACK; + rb_rotate_left(rb, x->parent); + x = rb->root; /* Arrange for loop to terminate. */ + } + } + else + { + RBNode *w = x->parent->left; + + if (w->color == RBRED) + { + w->color = RBBLACK; + x->parent->color = RBRED; + rb_rotate_right(rb, x->parent); + w = x->parent->left; + } + + if (w->right->color == RBBLACK && w->left->color == RBBLACK) + { + w->color = RBRED; + x = x->parent; + } + else + { + if (w->left->color == RBBLACK) + { + w->right->color = RBBLACK; + w->color = RBRED; + rb_rotate_left(rb, w); + w = x->parent->left; + } + w->color = x->parent->color; + x->parent->color = RBBLACK; + w->left->color = RBBLACK; + rb_rotate_right(rb, x->parent); + x = rb->root; /* Arrange for loop to terminate. */ + } + } + } + x->color = RBBLACK; +} + +/* + * Delete node z from tree. + */ +static void +rb_delete_node(RBTree *rb, RBNode *z) +{ + RBNode *x, + *y; + + if (!z || z == RBNIL) + 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 == RBNIL || z->right == RBNIL) + { + /* y has a RBNIL node as a child */ + y = z; + } + else + { + /* find tree successor */ + y = z->right; + while (y->left != RBNIL) + y = y->left; + } + + /* x is y's only child */ + if (y->left != RBNIL) + 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 + { + rb->root = x; + } + + /* + * If we removed the tree successor of z rather than z itself, then + * attach the data for the removed node to the one we were supposed to + * remove. + */ + if (y != z) + z->data = y->data; + + /* + * 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 == RBBLACK) + rb_delete_fixup(rb, x); + + pfree(y); +} + +extern void +rb_delete(RBTree *rb, void *data) +{ + RBNode *node = rb->root; + int cmp; + + while (node != RBNIL) + { + cmp = rb->comparator(data, node->data, rb->arg); + + if (cmp == 0) + { + /* found node to delete */ + if (rb->freefunc) + rb->freefunc(node->data); + node->data = NULL; + rb_delete_node(rb, node); + return; + } + else if (cmp < 0) + node = node->left; + else + node = node->right; + } +} + +/* + * Return data on left most node and delete + * that node + */ +extern void * +rb_leftmost(RBTree *rb) +{ + RBNode *node = rb->root; + RBNode *leftmost = rb->root; + void *res = NULL; + + while (node != RBNIL) + { + leftmost = node; + node = node->left; + } + + if (leftmost != RBNIL) + { + res = leftmost->data; + leftmost->data = NULL; + rb_delete_node(rb, leftmost); + } + + return res; +} + +/********************************************************************** + * Traverse * + **********************************************************************/ + +static void * +rb_next_node(RBTreeIterator *iterator, RBNode *node) +{ + node->iteratorState = InitialState; + iterator->node = node; + return iterator->iterate(iterator); +} + +static void * +rb_left_right_iterator(RBTreeIterator *iterator) +{ + RBNode *node = iterator->node; + + switch (node->iteratorState) + { + case InitialState: + if (node->left != RBNIL) + { + node->iteratorState = FirstStepDone; + return rb_next_node(iterator, node->left); + } + case FirstStepDone: + node->iteratorState = SecondStepDone; + return node->data; + case SecondStepDone: + if (node->right != RBNIL) + { + node->iteratorState = ThirdStepDone; + return rb_next_node(iterator, node->right); + } + case ThirdStepDone: + if (node->parent) + { + iterator->node = node->parent; + return iterator->iterate(iterator); + } + break; + default: + elog(ERROR, "Unknow node state: %d", node->iteratorState); + } + + return NULL; +} + +static void * +rb_right_left_iterator(RBTreeIterator *iterator) +{ + RBNode *node = iterator->node; + + switch (node->iteratorState) + { + case InitialState: + if (node->right != RBNIL) + { + node->iteratorState = FirstStepDone; + return rb_next_node(iterator, node->right); + } + case FirstStepDone: + node->iteratorState = SecondStepDone; + return node->data; + case SecondStepDone: + if (node->left != RBNIL) + { + node->iteratorState = ThirdStepDone; + return rb_next_node(iterator, node->left); + } + case ThirdStepDone: + if (node->parent) + { + iterator->node = node->parent; + return iterator->iterate(iterator); + } + break; + default: + elog(ERROR, "Unknow node state: %d", node->iteratorState); + } + + return NULL; +} + +static void * +rb_direct_iterator(RBTreeIterator *iterator) +{ + RBNode *node = iterator->node; + + switch (node->iteratorState) + { + case InitialState: + node->iteratorState = FirstStepDone; + return node->data; + case FirstStepDone: + if (node->left != RBNIL) + { + node->iteratorState = SecondStepDone; + return rb_next_node(iterator, node->left); + } + case SecondStepDone: + if (node->right != RBNIL) + { + node->iteratorState = ThirdStepDone; + return rb_next_node(iterator, node->right); + } + case ThirdStepDone: + if (node->parent) + { + iterator->node = node->parent; + return iterator->iterate(iterator); + } + break; + default: + elog(ERROR, "Unknow node state: %d", node->iteratorState); + } + + return NULL; +} + +static void * +rb_inverted_iterator(RBTreeIterator *iterator) +{ + RBNode *node = iterator->node; + + switch (node->iteratorState) + { + case InitialState: + if (node->left != RBNIL) + { + node->iteratorState = FirstStepDone; + return rb_next_node(iterator, node->left); + } + case FirstStepDone: + if (node->right != RBNIL) + { + node->iteratorState = SecondStepDone; + return rb_next_node(iterator, node->right); + } + case SecondStepDone: + node->iteratorState = ThirdStepDone; + return node->data; + case ThirdStepDone: + if (node->parent) + { + iterator->node = node->parent; + return iterator->iterate(iterator); + } + break; + default: + elog(ERROR, "Unknow node state: %d", node->iteratorState); + } + + return NULL; +} + +RBTreeIterator * +rb_begin_iterate(RBTree *rb, RBOrderControl ctrl) +{ + RBTreeIterator *iterator = palloc(sizeof(RBTreeIterator)); + + iterator->node = rb->root; + if (iterator->node != RBNIL) + iterator->node->iteratorState = InitialState; + + switch (ctrl) + { + case LeftRightWalk: /* visit left, then self, then right */ + iterator->iterate = rb_left_right_iterator; + break; + case RightLeftWalk: /* visit right, then self, then left */ + iterator->iterate = rb_right_left_iterator; + break; + case DirectWalk: /* visit self, then left, then right */ + iterator->iterate = rb_direct_iterator; + break; + case InvertedWalk: /* visit left, then right, then self */ + iterator->iterate = rb_inverted_iterator; + break; + default: + elog(ERROR, "Unknown iterator order: %d", ctrl); + } + + return iterator; +} + +void * +rb_iterate(RBTreeIterator *iterator) +{ + if (iterator->node == RBNIL) + return NULL; + + return iterator->iterate(iterator); +} + +void +rb_free_iterator(RBTreeIterator *iterator) +{ + pfree(iterator); +} |