diff options
author | Heikki Linnakangas <heikki.linnakangas@iki.fi> | 2014-12-22 17:52:08 +0200 |
---|---|---|
committer | Heikki Linnakangas <heikki.linnakangas@iki.fi> | 2014-12-22 17:52:08 +0200 |
commit | 955557ddccb4065831af80b0966cbd02937dfb72 (patch) | |
tree | e853e7bc8217fd9bacbe61852fc12f4621788b18 /src/backend/utils/misc | |
parent | 7f0dccaed64a8ed6f5db8ad43e7612202fbeeeaf (diff) | |
download | postgresql-955557ddccb4065831af80b0966cbd02937dfb72.tar.gz postgresql-955557ddccb4065831af80b0966cbd02937dfb72.zip |
Move rbtree.c from src/backend/utils/misc to src/backend/lib.
We have other general-purpose data structures in src/backend/lib, so it
seems like a better home for the red-black tree as well.
Diffstat (limited to 'src/backend/utils/misc')
-rw-r--r-- | src/backend/utils/misc/Makefile | 2 | ||||
-rw-r--r-- | src/backend/utils/misc/rbtree.c | 873 |
2 files changed, 1 insertions, 874 deletions
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile index c7b745e5131..449d5b47ea2 100644 --- a/src/backend/utils/misc/Makefile +++ b/src/backend/utils/misc/Makefile @@ -14,7 +14,7 @@ include $(top_builddir)/src/Makefile.global override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS) -OBJS = guc.o help_config.o pg_rusage.o ps_status.o rbtree.o \ +OBJS = guc.o help_config.o pg_rusage.o ps_status.o \ superuser.o timeout.o tzparser.o # This location might depend on the installation directories. Therefore diff --git a/src/backend/utils/misc/rbtree.c b/src/backend/utils/misc/rbtree.c deleted file mode 100644 index e3efd4c08bd..00000000000 --- a/src/backend/utils/misc/rbtree.c +++ /dev/null @@ -1,873 +0,0 @@ -/*------------------------------------------------------------------------- - * - * 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-2014, PostgreSQL Global Development Group - * - * IDENTIFICATION - * src/backend/utils/misc/rbtree.c - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include "utils/rbtree.h" - - -/* - * Values of RBNode.iteratorState - * - * Note that iteratorState has an undefined value except in nodes that are - * currently being visited by an active iteration. - */ -#define InitialState (0) -#define FirstStepDone (1) -#define SecondStepDone (2) -#define ThirdStepDone (3) - -/* - * Colors of nodes (values of RBNode.color) - */ -#define RBBLACK (0) -#define RBRED (1) - -/* - * RBTree control structure - */ -struct RBTree -{ - RBNode *root; /* root node, or RBNIL if tree is empty */ - - /* Iteration state */ - RBNode *cur; /* current iteration node */ - RBNode *(*iterate) (RBTree *rb); - - /* Remaining fields are constant after rb_create */ - - Size node_size; /* actual size of tree nodes */ - /* The caller-supplied manipulation functions */ - rb_comparator comparator; - rb_combiner combiner; - rb_allocfunc allocfunc; - rb_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 RBNIL (&sentinel) - -static RBNode sentinel = {InitialState, RBBLACK, RBNIL, RBNIL, NULL}; - - -/* - * rb_create: create an empty RBTree - * - * Arguments are: - * node_size: actual size of tree nodes (> sizeof(RBNode)) - * The manipulation functions: - * comparator: compare two RBNodes for less/equal/greater - * combiner: merge an existing tree entry with a new one - * allocfunc: allocate a new RBNode - * freefunc: free an old RBNode - * 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 rb_insert, in which the RBNode 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 - * RBNode fields, only the extra fields of the struct the RBNode 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 * -rb_create(Size node_size, - rb_comparator comparator, - rb_combiner combiner, - rb_allocfunc allocfunc, - rb_freefunc freefunc, - void *arg) -{ - RBTree *tree = (RBTree *) palloc(sizeof(RBTree)); - - Assert(node_size > sizeof(RBNode)); - - tree->root = RBNIL; - tree->cur = RBNIL; - tree->iterate = NULL; - 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 RBNode to another */ -static inline void -rb_copy_data(RBTree *rb, RBNode *dest, const RBNode *src) -{ - memcpy(dest + 1, src + 1, rb->node_size - sizeof(RBNode)); -} - -/********************************************************************** - * Search * - **********************************************************************/ - -/* - * rb_find: search for a value in an RBTree - * - * data represents the value to try to find. Its RBNode 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. - */ -RBNode * -rb_find(RBTree *rb, const RBNode *data) -{ - RBNode *node = rb->root; - - while (node != RBNIL) - { - int cmp = rb->comparator(data, node, rb->arg); - - if (cmp == 0) - return node; - else if (cmp < 0) - node = node->left; - else - node = node->right; - } - - return NULL; -} - -/* - * rb_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 rb_delete on the result if that's what - * you want. - */ -RBNode * -rb_leftmost(RBTree *rb) -{ - RBNode *node = rb->root; - RBNode *leftmost = rb->root; - - while (node != RBNIL) - { - leftmost = node; - node = node->left; - } - - if (leftmost != RBNIL) - 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 -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; -} - -/* - * rb_insert: insert a new value into the tree. - * - * data represents the value to insert. Its RBNode 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. - */ -RBNode * -rb_insert(RBTree *rb, const RBNode *data, bool *isNew) -{ - RBNode *current, - *parent, - *x; - int cmp; - - /* find where node belongs */ - current = rb->root; - parent = NULL; - cmp = 0; /* just to prevent compiler warning */ - - while (current != RBNIL) - { - cmp = rb->comparator(data, current, rb->arg); - if (cmp == 0) - { - /* - * Found node with given key. Apply combiner. - */ - rb->combiner(current, data, rb->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 = rb->allocfunc (rb->arg); - - x->iteratorState = InitialState; - x->color = RBRED; - - x->left = RBNIL; - x->right = RBNIL; - x->parent = parent; - rb_copy_data(rb, x, data); - - /* 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 x; -} - -/********************************************************************** - * 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 move - * the data for the removed node to the one we were supposed to remove. - */ - if (y != z) - rb_copy_data(rb, 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 == RBBLACK) - rb_delete_fixup(rb, x); - - /* Now we can recycle the y node */ - if (rb->freefunc) - rb->freefunc (y, rb->arg); -} - -/* - * rb_delete: remove the given tree entry - * - * "node" must have previously been found via rb_find or rb_leftmost. - * It is caller's responsibility to free any subsidiary data attached - * to the node before calling rb_delete. (Do *not* try to push that - * responsibility off to the freefunc, as some other physical node - * may be the one actually freed!) - */ -void -rb_delete(RBTree *rb, RBNode *node) -{ - rb_delete_node(rb, node); -} - -/********************************************************************** - * Traverse * - **********************************************************************/ - -/* - * The iterator routines were originally coded in tail-recursion style, - * which is nice to look at, but is trouble if your compiler isn't smart - * enough to optimize it. Now we just use looping. - */ -#define descend(next_node) \ - do { \ - (next_node)->iteratorState = InitialState; \ - node = rb->cur = (next_node); \ - goto restart; \ - } while (0) - -#define ascend(next_node) \ - do { \ - node = rb->cur = (next_node); \ - goto restart; \ - } while (0) - - -static RBNode * -rb_left_right_iterator(RBTree *rb) -{ - RBNode *node = rb->cur; - -restart: - switch (node->iteratorState) - { - case InitialState: - if (node->left != RBNIL) - { - node->iteratorState = FirstStepDone; - descend(node->left); - } - /* FALL THROUGH */ - case FirstStepDone: - node->iteratorState = SecondStepDone; - return node; - case SecondStepDone: - if (node->right != RBNIL) - { - node->iteratorState = ThirdStepDone; - descend(node->right); - } - /* FALL THROUGH */ - case ThirdStepDone: - if (node->parent) - ascend(node->parent); - break; - default: - elog(ERROR, "unrecognized rbtree node state: %d", - node->iteratorState); - } - - return NULL; -} - -static RBNode * -rb_right_left_iterator(RBTree *rb) -{ - RBNode *node = rb->cur; - -restart: - switch (node->iteratorState) - { - case InitialState: - if (node->right != RBNIL) - { - node->iteratorState = FirstStepDone; - descend(node->right); - } - /* FALL THROUGH */ - case FirstStepDone: - node->iteratorState = SecondStepDone; - return node; - case SecondStepDone: - if (node->left != RBNIL) - { - node->iteratorState = ThirdStepDone; - descend(node->left); - } - /* FALL THROUGH */ - case ThirdStepDone: - if (node->parent) - ascend(node->parent); - break; - default: - elog(ERROR, "unrecognized rbtree node state: %d", - node->iteratorState); - } - - return NULL; -} - -static RBNode * -rb_direct_iterator(RBTree *rb) -{ - RBNode *node = rb->cur; - -restart: - switch (node->iteratorState) - { - case InitialState: - node->iteratorState = FirstStepDone; - return node; - case FirstStepDone: - if (node->left != RBNIL) - { - node->iteratorState = SecondStepDone; - descend(node->left); - } - /* FALL THROUGH */ - case SecondStepDone: - if (node->right != RBNIL) - { - node->iteratorState = ThirdStepDone; - descend(node->right); - } - /* FALL THROUGH */ - case ThirdStepDone: - if (node->parent) - ascend(node->parent); - break; - default: - elog(ERROR, "unrecognized rbtree node state: %d", - node->iteratorState); - } - - return NULL; -} - -static RBNode * -rb_inverted_iterator(RBTree *rb) -{ - RBNode *node = rb->cur; - -restart: - switch (node->iteratorState) - { - case InitialState: - if (node->left != RBNIL) - { - node->iteratorState = FirstStepDone; - descend(node->left); - } - /* FALL THROUGH */ - case FirstStepDone: - if (node->right != RBNIL) - { - node->iteratorState = SecondStepDone; - descend(node->right); - } - /* FALL THROUGH */ - case SecondStepDone: - node->iteratorState = ThirdStepDone; - return node; - case ThirdStepDone: - if (node->parent) - ascend(node->parent); - break; - default: - elog(ERROR, "unrecognized rbtree node state: %d", - node->iteratorState); - } - - return NULL; -} - -/* - * rb_begin_iterate: prepare to traverse the tree in any of several orders - * - * After calling rb_begin_iterate, call rb_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 - * rb_iterate are unspecified. - * - * Note: this used to return a separately palloc'd iterator control struct, - * but that's a bit pointless since the data structure is incapable of - * supporting multiple concurrent traversals. Now we just keep the state - * in RBTree. - */ -void -rb_begin_iterate(RBTree *rb, RBOrderControl ctrl) -{ - rb->cur = rb->root; - if (rb->cur != RBNIL) - rb->cur->iteratorState = InitialState; - - switch (ctrl) - { - case LeftRightWalk: /* visit left, then self, then right */ - rb->iterate = rb_left_right_iterator; - break; - case RightLeftWalk: /* visit right, then self, then left */ - rb->iterate = rb_right_left_iterator; - break; - case DirectWalk: /* visit self, then left, then right */ - rb->iterate = rb_direct_iterator; - break; - case InvertedWalk: /* visit left, then right, then self */ - rb->iterate = rb_inverted_iterator; - break; - default: - elog(ERROR, "unrecognized rbtree iteration order: %d", ctrl); - } -} - -/* - * rb_iterate: return the next node in traversal order, or NULL if no more - */ -RBNode * -rb_iterate(RBTree *rb) -{ - if (rb->cur == RBNIL) - return NULL; - - return rb->iterate(rb); -} |