UNIXworkcode

/******************************************************************************* * * * rbTree.c -- Nirvana editor red-black balanced binary tree * * * * Copyright (C) 2001 Mark Edel * * * * This is free software; you can redistribute it and/or modify it under the * * terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This software is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * * for more details. * * * * You should have received a copy of the GNU General Public License along with * * software; if not, write to the Free Software Foundation, Inc., 59 Temple * * Place, Suite 330, Boston, MA 02111-1307 USA * * * * Nirvana Text Editor * * July, 1993 * * * * Written by Mark Edel * * * *******************************************************************************/ /* ** rbTree is a red-black balanced binary tree ** the base node holds the leftmost, rightmost and root pointers ** and a node count */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include "rbTree.h" #include "../util/nedit_malloc.h" #include <stdlib.h> #include <string.h> /*#define RBTREE_TEST_CODE*/ #ifdef RBTREE_TEST_CODE #include <stdio.h> #endif #ifdef HAVE_DEBUG_H #include "../debug.h" #endif #define rbTreeNodeRed 0 #define rbTreeNodeBlack 1 /* ** rotate a node left */ static void rotateLeft(rbTreeNode *x, rbTreeNode **root) { rbTreeNode *y = x->right; x->right = y->left; if (y->left != NULL) { y->left->parent = x; } y->parent = x->parent; if (x == *root) { *root = y; } else if (x == x->parent->left) { x->parent->left = y; } else { x->parent->right = y; } y->left = x; x->parent = y; } /* ** rotate a node right */ static void rotateRight(rbTreeNode *x, rbTreeNode **root) { rbTreeNode *y = x->left; x->left = y->right; if (y->right != NULL) { y->right->parent = x; } y->parent = x->parent; if (x == *root) { *root = y; } else if (x == x->parent->right) { x->parent->right = y; } else { x->parent->left = y; } y->right = x; x->parent = y; } /* ** balance tree after an insert of node x */ static void insertBalance(rbTreeNode *x, rbTreeNode **root) { x->color = rbTreeNodeRed; while (x != *root && x->parent->color == rbTreeNodeRed) { if (x->parent == x->parent->parent->left) { rbTreeNode *y = x->parent->parent->right; if (y && y->color == rbTreeNodeRed) { x->parent->color = rbTreeNodeBlack; y->color = rbTreeNodeBlack; x->parent->parent->color = rbTreeNodeRed; x = x->parent->parent; } else { if (x == x->parent->right) { x = x->parent; rotateLeft(x, root); } x->parent->color = rbTreeNodeBlack; x->parent->parent->color = rbTreeNodeRed; rotateRight(x->parent->parent, root); } } else { rbTreeNode *y = x->parent->parent->left; if (y && y->color == rbTreeNodeRed) { x->parent->color = rbTreeNodeBlack; y->color = rbTreeNodeBlack; x->parent->parent->color = rbTreeNodeRed; x = x->parent->parent; } else { if (x == x->parent->left) { x = x->parent; rotateRight(x, root); } x->parent->color = rbTreeNodeBlack; x->parent->parent->color = rbTreeNodeRed; rotateLeft(x->parent->parent, root); } } } (*root)->color = rbTreeNodeBlack; } /* ** returns the leftmost node (the beginning of the sorted list) */ rbTreeNode *rbTreeBegin(rbTreeNode *base) { return(base->left); } /* ** returns the rightmost node (the end of the sorted list) */ rbTreeNode *rbTreeReverseBegin(rbTreeNode *base) { return(base->right); } /* ** search for a node and return it's pointer, NULL if not found */ rbTreeNode *rbTreeFind(rbTreeNode *base, rbTreeNode *searchNode, rbTreeCompareNodeCB compareRecords) { rbTreeNode *foundNode = NULL; rbTreeNode *current = base->parent; while(current != NULL) { int compareResult = compareRecords(searchNode, current); if (compareResult < 0) { current = current->left; } else if (compareResult > 0) { current = current->right; } else { foundNode = current; current = NULL; } } return(foundNode); } /* ** insert a node into the tree and rebalance it ** if a duplicate is found copy the new data over it ** returns the new node */ rbTreeNode *rbTreeInsert(rbTreeNode *base, rbTreeNode *searchNode, rbTreeCompareNodeCB compareRecords, rbTreeAllocateNodeCB allocateNode, rbTreeCopyToNodeCB copyToNode) { rbTreeNode *current, *parent, *x; int fromLeft = 0, foundMatch = 0; current = base->parent; parent = NULL; x = NULL; while(current != NULL) { int compareResult = compareRecords(searchNode, current); if (compareResult < 0) { parent = current; current = current->left; fromLeft = 1; } else if (compareResult > 0) { parent = current; current = current->right; fromLeft = 0; } else { x = current; if (!copyToNode(x, searchNode)) { x = NULL; } current = NULL; foundMatch = 1; } } if (!foundMatch) { x = allocateNode(searchNode); if (x) { x->parent = parent; x->left = NULL; x->right = NULL; x->color = rbTreeNodeRed; if (parent) { if (fromLeft) { parent->left = x; } else { parent->right = x; } } else { base->parent = x; } ++(base->color); if (x->parent == base->left && (x->parent == NULL || x->parent->left == x)) { base->left = x; } if (x->parent == base->right && (x->parent == NULL || x->parent->right == x)) { base->right = x; } insertBalance(x, &base->parent); } } return(x); } /* ** unlink a node from the tree and rebalance it. ** returns the removed node pointer */ rbTreeNode *rbTreeUnlinkNode(rbTreeNode *base, rbTreeNode *z) { int swapColor; rbTreeNode *x, *y, *x_parent; y = z; if (y->left == NULL) { x = y->right; if (y == base->left) { base->left = rbTreeNext(y); } } else { if (y->right == NULL) { x = y->left; if (y == base->right) { base->right = rbTreePrevious(y); } } else { y = y->right; while (y->left != NULL) { y = y->left; } x = y->right; } } if (y != z) { z->left->parent = y; y->left = z->left; if (y != z->right) { x_parent = y->parent; if (x != NULL) { x->parent = y->parent; } y->parent->left = x; y->right = z->right; z->right->parent = y; } else { x_parent = y; } if (base->parent == z) { base->parent = y; } else if (z->parent->left == z) { z->parent->left = y; } else { z->parent->right = y; } y->parent = z->parent; swapColor = y->color; y->color = z->color; z->color = swapColor; y = z; } else { x_parent = y->parent; if (x != NULL) { x->parent = y->parent; } if (base->parent == z) { base->parent = x; } else { if (z->parent->left == z) { z->parent->left = x; } else { z->parent->right = x; } } } --(base->color); if (y->color != rbTreeNodeRed) { while (x != base->parent && (x == NULL || x->color == rbTreeNodeBlack)) { if (x == x_parent->left) { rbTreeNode *w = x_parent->right; if (w->color == rbTreeNodeRed) { w->color = rbTreeNodeBlack; x_parent->color = rbTreeNodeRed; rotateLeft(x_parent, &base->parent); w = x_parent->right; } if ((w->left == NULL || w->left->color == rbTreeNodeBlack) && (w->right == NULL || w->right->color == rbTreeNodeBlack)) { w->color = rbTreeNodeRed; x = x_parent; x_parent = x_parent->parent; } else { if (w->right == NULL || w->right->color == rbTreeNodeBlack) { if (w->left) { w->left->color = rbTreeNodeBlack; } w->color = rbTreeNodeRed; rotateRight(w, &base->parent); w = x_parent->right; } w->color = x_parent->color; x_parent->color = rbTreeNodeBlack; if (w->right) { w->right->color = rbTreeNodeBlack; } rotateLeft(x_parent, &base->parent); break; } } else { rbTreeNode *w = x_parent->left; if (w->color == rbTreeNodeRed) { w->color = rbTreeNodeBlack; x_parent->color = rbTreeNodeRed; rotateRight(x_parent, &base->parent); w = x_parent->left; } if ((w->right == NULL || w->right->color == rbTreeNodeBlack) && (w->left == NULL || w->left->color == rbTreeNodeBlack)) { w->color = rbTreeNodeRed; x = x_parent; x_parent = x_parent->parent; } else { if (w->left == NULL || w->left->color == rbTreeNodeBlack) { if (w->right) { w->right->color = rbTreeNodeBlack; } w->color = rbTreeNodeRed; rotateLeft(w, &base->parent); w = x_parent->left; } w->color = x_parent->color; x_parent->color = rbTreeNodeBlack; if (w->left) { w->left->color = rbTreeNodeBlack; } rotateRight(x_parent, &base->parent); break; } } } if (x) { x->color = rbTreeNodeBlack; } } return(y); } /* ** delete an already found node and dispose it */ void rbTreeDeleteNode(rbTreeNode *base, rbTreeNode *foundNode, rbTreeDisposeNodeCB disposeNode) { disposeNode(rbTreeUnlinkNode(base, foundNode)); } /* ** search for a node and remove it from the tree ** disposing the removed node ** returns 1 if a node was found, otherwise 0 */ int rbTreeDelete(rbTreeNode *base, rbTreeNode *searchNode, rbTreeCompareNodeCB compareRecords, rbTreeDisposeNodeCB disposeNode) { int foundNode = 0; rbTreeNode *z; z = rbTreeFind(base, searchNode, compareRecords); if (z != NULL) { rbTreeDeleteNode(base, z, disposeNode); foundNode = 1; } return(foundNode); } /* ** move an iterator foreward one element ** note that a valid pointer must be passed, ** passing NULL will result in unpredictable results */ rbTreeNode *rbTreeNext(rbTreeNode *x) { if (x->right != NULL) { x = x->right; while (x->left != NULL) { x = x->left; } } else { rbTreeNode *fromX; do { fromX = x; x = x->parent; } while (x && fromX == x->right); } return(x); } /* ** move an iterator back one element ** note that a valid pointer must be passed, ** passing NULL will result in unpredictable results */ rbTreeNode *rbTreePrevious(rbTreeNode *x) { if (x->left != NULL) { x = x->left; while (x->right != NULL) { x = x->right; } } else { rbTreeNode *fromX; do { fromX = x; x = x->parent; } while (x && fromX == x->left); } return(x); } /* ** returns the number of real data nodes in the tree, not counting ** the base node since it contains no data */ int rbTreeSize(rbTreeNode *base) { return(base->color); } /* ** Allocate a new red-black tree using an empty node to hold pointers */ rbTreeNode *rbTreeNew(rbTreeAllocateEmptyNodeCB allocateEmptyNode) { rbTreeNode *rootStorage = allocateEmptyNode(); if (rootStorage) { rootStorage->left = NULL; /* leftmost node */ rootStorage->right = NULL; /* rightmost node */ rootStorage->parent = NULL; /* root node */ rootStorage->color = 0; /* node count */ } return(rootStorage); } /* ** iterate through all nodes, unlinking and disposing them ** extra effort is made to maintain all links, the size, and ** leftmost/rightmost pointers, so that the tree can be dumped ** when debugging problems. We could probably ifdef some of this ** since it goes unused most of the time ** the tree is not kept balanced since all nodes will be removed */ void rbTreeDispose(rbTreeNode *base, rbTreeDisposeNodeCB disposeNode) { rbTreeNode *iter = rbTreeBegin(base); while (iter != NULL) { rbTreeNode *nextIter = rbTreeNext(iter); if (iter->parent) { if (iter->parent->left == iter) { iter->parent->left = iter->right; } else { iter->parent->right = iter->right; } } if (iter->right != NULL) { iter->right->parent = iter->parent; } base->left = nextIter; if (base->right == iter) { base->right = NULL; } --(base->color); if (base->parent == iter) { base->parent = nextIter; } disposeNode(iter); iter = nextIter; } disposeNode(base); } #ifdef RBTREE_TEST_CODE /* ================================================================== */ /* ** code to test basic stuff of tree routines */ typedef struct TestNode { rbTreeNode nodePointers; /* MUST BE FIRST MEMBER */ char *str; char *key; } TestNode; static int rbTreeCompareNode_TestNode(rbTreeNode *left, rbTreeNode *right) { return(strcmp(((TestNode *)left)->key, ((TestNode *)right)->key)); } static rbTreeNode *rbTreeAllocateNode_TestNode(rbTreeNode *src) { TestNode *newNode = NEditMalloc(sizeof(TestNode)); if (newNode) { newNode->str = NEditMalloc(strlen(((TestNode *)src)->str) + 1); if (newNode->str) { strcpy(newNode->str, ((TestNode *)src)->str); newNode->key = NEditMalloc(strlen(((TestNode *)src)->key) + 1); if (newNode->key) { strcpy(newNode->key, ((TestNode *)src)->key); } else { NEditFree(newNode->str); newNode->str = NULL; NEditFree(newNode); newNode = NULL; } } else { NEditFree(newNode); newNode = NULL; } } return((rbTreeNode *)newNode); } rbTreeNode *rbTreeAllocateEmptyNodeCB_TestNode(void) { TestNode *newNode = NEditMalloc(sizeof(TestNode)); if (newNode) { newNode->str = NULL; newNode->key = NULL; } return((rbTreeNode *)newNode); } static void rbTreeDisposeNode_TestNode(rbTreeNode *src) { if (src) { if (((TestNode *)src)->str) { NEditFree(((TestNode *)src)->str); ((TestNode *)src)->str = NULL; } if (((TestNode *)src)->key) { NEditFree(((TestNode *)src)->key); ((TestNode *)src)->key = NULL; } src->left = (void *)-1; src->right = (void *)-1; src->parent = (void *)-1; src->color = rbTreeNodeBlack; NEditFree(src); } } static int rbTreeCopyToNode_TestNode(rbTreeNode *dst, rbTreeNode *src) { TestNode newValues; int copiedOK = 0; newValues.str = NEditMalloc(strlen(((TestNode *)src)->str) + 1); if (newValues.str) { strcpy(newValues.str, ((TestNode *)src)->str); newValues.key = NEditMalloc(strlen(((TestNode *)src)->key) + 1); if (newValues.key) { strcpy(newValues.key, ((TestNode *)src)->key); ((TestNode *)dst)->str = newValues.str; ((TestNode *)dst)->key = newValues.key; copiedOK = 1; } else { NEditFree(newValues.str); newValues.str = NULL; } } return(copiedOK); } static void DumpTree(rbTreeNode *base) { rbTreeNode *newNode; newNode = rbTreeBegin(base); while (newNode != NULL) { rbTreeNode *nextNode = rbTreeNext(newNode); printf("[%s] = \"%s\"\n", ((TestNode *)newNode)->key, ((TestNode *)newNode)->str); printf("[%x] l[%x] r[%x] p[%x] <%s>\n", (int)newNode, (int)newNode->left, (int)newNode->right, (int)newNode->parent, ((newNode->color == rbTreeNodeBlack)?"Black":"Red")); newNode = nextNode; } } int main(int argc, char **argv) { rbTreeNode *base, *newNode; TestNode searchNode; char tmpkey[20], tmpValue[40]; int i; searchNode.key = tmpkey; searchNode.str = tmpValue; base = rbTreeNew(rbTreeAllocateEmptyNodeCB_TestNode); if (!base) { printf("Failed New!!!\n"); exit(1); } for (i = 0; i < 100; ++i) { sprintf(tmpkey, "%d", i); sprintf(tmpValue, "<%d>", i * i); newNode = rbTreeInsert(base, (rbTreeNode *)&searchNode, rbTreeCompareNode_TestNode, rbTreeAllocateNode_TestNode, rbTreeCopyToNode_TestNode); if (!newNode) { printf("Failed!!!\n"); exit(1); } } newNode = rbTreeBegin(base); while (newNode != NULL) { rbTreeNode *nextNode = rbTreeNext(newNode); printf("[%s] = \"%s\"\n", ((TestNode *)newNode)->key, ((TestNode *)newNode)->str); if (strlen(((TestNode *)newNode)->str) < 7) { int didDelete; printf("Deleting [%s]\n", ((TestNode *)newNode)->key); didDelete = rbTreeDelete(base, newNode, rbTreeCompareNode_TestNode, rbTreeDisposeNode_TestNode); printf("delete result = %d\n", didDelete); } newNode = nextNode; } printf("Tree Size = %d\n", rbTreeSize(base)); printf("\n++++++++++++++++\n"); DumpTree(base); printf("\n++++++++++++++++\n"); rbTreeDispose(base, rbTreeDisposeNode_TestNode); printf("\nDone.\n"); return(0); } #endif