2 * rbtree.c Red-black balanced binary trees.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
20 * Copyright 2004 The FreeRADIUS server project
23 static const char rcsid[] = "$Id$";
30 #include "libradius.h"
32 /* red-black tree description */
33 typedef enum { Black, Red } NodeColor;
36 rbnode_t *Left; /* left child */
37 rbnode_t *Right; /* right child */
38 rbnode_t *Parent; /* parent */
39 NodeColor Color; /* node color (black, red) */
40 void *Data; /* data stored in node */
43 #define NIL &Sentinel /* all leafs are sentinels */
44 static rbnode_t Sentinel = { NIL, NIL, NULL, Black, NULL};
52 int (*Compare)(const void *, const void *);
54 void (*freeNode)(void *);
56 #define RBTREE_MAGIC (0x5ad09c42)
59 * Walks the tree to delete all nodes.
60 * Does NOT re-balance it!
62 static void FreeWalker(rbtree_t *tree, rbnode_t *X)
64 if (X->Left != NIL) FreeWalker(tree, X->Left);
65 if (X->Right != NIL) FreeWalker(tree, X->Right);
67 if (tree->freeNode) tree->freeNode(X->Data);
71 void rbtree_free(rbtree_t *tree)
76 * Walk the tree, deleting the nodes...
78 if (tree->Root != NIL) FreeWalker(tree, tree->Root);
88 * Create a new red-black tree.
90 rbtree_t *rbtree_create(int (*Compare)(const void *, const void *),
91 void (*freeNode)(void *),
96 if (!Compare) return NULL;
98 tree = malloc(sizeof(*tree));
99 if (!tree) return NULL;
101 memset(tree, 0, sizeof(*tree));
103 tree->magic = RBTREE_MAGIC;
106 tree->Compare = Compare;
107 tree->replace_flag = replace_flag;
108 tree->freeNode = freeNode;
114 static void RotateLeft(rbtree_t *tree, rbnode_t *X)
116 /**************************
117 * rotate Node X to left *
118 **************************/
120 rbnode_t *Y = X->Right;
122 /* establish X->Right link */
124 if (Y->Left != NIL) Y->Left->Parent = X;
126 /* establish Y->Parent link */
127 if (Y != NIL) Y->Parent = X->Parent;
129 if (X == X->Parent->Left)
132 X->Parent->Right = Y;
139 if (X != NIL) X->Parent = Y;
142 static void RotateRight(rbtree_t *tree, rbnode_t *X)
144 /****************************
145 * rotate Node X to right *
146 ****************************/
148 rbnode_t *Y = X->Left;
150 /* establish X->Left link */
152 if (Y->Right != NIL) Y->Right->Parent = X;
154 /* establish Y->Parent link */
155 if (Y != NIL) Y->Parent = X->Parent;
157 if (X == X->Parent->Right)
158 X->Parent->Right = Y;
167 if (X != NIL) X->Parent = Y;
170 static void InsertFixup(rbtree_t *tree, rbnode_t *X)
172 /*************************************
173 * maintain red-black tree balance *
174 * after inserting node X *
175 *************************************/
177 /* check red-black properties */
178 while (X != tree->Root && X->Parent->Color == Red) {
179 /* we have a violation */
180 if (X->Parent == X->Parent->Parent->Left) {
181 rbnode_t *Y = X->Parent->Parent->Right;
182 if (Y->Color == Red) {
185 X->Parent->Color = Black;
187 X->Parent->Parent->Color = Red;
188 X = X->Parent->Parent;
192 if (X == X->Parent->Right) {
193 /* make X a left child */
198 /* recolor and rotate */
199 X->Parent->Color = Black;
200 X->Parent->Parent->Color = Red;
201 RotateRight(tree, X->Parent->Parent);
205 /* mirror image of above code */
206 rbnode_t *Y = X->Parent->Parent->Left;
207 if (Y->Color == Red) {
210 X->Parent->Color = Black;
212 X->Parent->Parent->Color = Red;
213 X = X->Parent->Parent;
217 if (X == X->Parent->Left) {
219 RotateRight(tree, X);
221 X->Parent->Color = Black;
222 X->Parent->Parent->Color = Red;
223 RotateLeft(tree, X->Parent->Parent);
228 tree->Root->Color = Black;
233 * Insert an element into the tree.
235 int rbtree_insert(rbtree_t *tree, const void *Data)
237 rbnode_t *Current, *Parent, *X;
239 /***********************************************
240 * allocate node for Data and insert in tree *
241 ***********************************************/
243 /* find where node belongs */
244 Current = tree->Root;
246 while (Current != NIL) {
250 * See if two entries are identical.
252 result = tree->Compare(Data, Current->Data);
255 * Don't replace the entry.
257 if (tree->replace_flag == 0) {
262 * Do replace the entry.
264 if (tree->freeNode) tree->freeNode(Current->Data);
265 Current->Data = Data;
270 Current = (result < 0) ? Current->Left : Current->Right;
274 if ((X = malloc (sizeof(*X))) == NULL) {
284 /* insert node in tree */
286 if (tree->Compare(Data, Parent->Data) <= 0)
294 InsertFixup(tree, X);
296 tree->num_elements++;
302 static void DeleteFixup(rbtree_t *tree, rbnode_t *X)
304 /*************************************
305 * maintain red-black tree balance *
306 * after deleting node X *
307 *************************************/
309 while (X != tree->Root && X->Color == Black) {
310 if (X == X->Parent->Left) {
311 rbnode_t *W = X->Parent->Right;
312 if (W->Color == Red) {
314 X->Parent->Color = Red;
315 RotateLeft(tree, X->Parent);
316 W = X->Parent->Right;
318 if (W->Left->Color == Black && W->Right->Color == Black) {
322 if (W->Right->Color == Black) {
323 W->Left->Color = Black;
325 RotateRight(tree, W);
326 W = X->Parent->Right;
328 W->Color = X->Parent->Color;
329 X->Parent->Color = Black;
330 W->Right->Color = Black;
331 RotateLeft(tree, X->Parent);
335 rbnode_t *W = X->Parent->Left;
336 if (W->Color == Red) {
338 X->Parent->Color = Red;
339 RotateRight(tree, X->Parent);
342 if (W->Right->Color == Black && W->Left->Color == Black) {
346 if (W->Left->Color == Black) {
347 W->Right->Color = Black;
352 W->Color = X->Parent->Color;
353 X->Parent->Color = Black;
354 W->Left->Color = Black;
355 RotateRight(tree, X->Parent);
364 * Delete an element from the tree.
366 void rbtree_delete(rbtree_t *tree, rbnode_t *Z)
370 /*****************************
371 * delete node Z from tree *
372 *****************************/
374 if (!Z || Z == NIL) return;
376 if (Z->Left == NIL || Z->Right == NIL) {
377 /* Y has a NIL node as a child */
380 /* find tree successor with a NIL node as a child */
382 while (Y->Left != NIL) Y = Y->Left;
385 /* X is Y's only child */
391 /* remove Y from the parent chain */
392 X->Parent = Y->Parent;
394 if (Y == Y->Parent->Left)
397 Y->Parent->Right = X;
403 * Move the child's data to here, and then
404 * re-balance the tree.
406 if (tree->freeNode) tree->freeNode(Z->Data);
409 } else if (tree->freeNode) {
410 tree->freeNode(Z->Data);
412 if (Y->Color == Black)
413 DeleteFixup(tree, X);
417 tree->num_elements--;
421 * Delete a node from the tree, based on given data, which MUST
422 * have come from rbtree_finddata().
424 int rbtree_deletebydata(rbtree_t *tree, const void *data)
426 rbnode_t *node = rbtree_find(tree, data);
428 if (!node) return 0; /* false */
430 rbtree_delete(tree, node);
437 * Find an element in the tree, returning the data, not the node.
439 rbnode_t *rbtree_find(rbtree_t *tree, const void *Data)
441 /*******************************
442 * find node containing Data *
443 *******************************/
445 rbnode_t *Current = tree->Root;
447 while (Current != NIL) {
448 int result = tree->Compare(Data, Current->Data);
453 Current = (result < 0) ?
454 Current->Left : Current->Right;
461 * Find the user data.
463 void *rbtree_finddata(rbtree_t *tree, const void *Data)
467 X = rbtree_find(tree, Data);
474 * Walk the tree, Pre-order
476 * We call ourselves recursively for each function, but that's OK,
477 * as the stack is only log(N) deep, which is ~12 entries deep.
479 static int WalkNodePreOrder(rbnode_t *X,
480 int (*callback)(void *, void *), void *context)
484 rcode = callback(context, X->Data);
485 if (rcode != 0) return rcode;
487 if (X->Left != NIL) {
488 rcode = WalkNodePreOrder(X->Left, callback, context);
489 if (rcode != 0) return rcode;
492 if (X->Right != NIL) {
493 rcode = WalkNodePreOrder(X->Right, callback, context);
494 if (rcode != 0) return rcode;
497 return 0; /* we know everything returned zero */
503 static int WalkNodeInOrder(rbnode_t *X,
504 int (*callback)(void *, void *), void *context)
508 if (X->Left != NIL) {
509 rcode = WalkNodeInOrder(X->Left, callback, context);
510 if (rcode != 0) return rcode;
513 rcode = callback(context, X->Data);
514 if (rcode != 0) return rcode;
516 if (X->Right != NIL) {
517 rcode = WalkNodeInOrder(X->Right, callback, context);
518 if (rcode != 0) return rcode;
521 return 0; /* we know everything returned zero */
528 static int WalkNodePostOrder(rbnode_t *X,
529 int (*callback)(void *, void*), void *context)
533 if (X->Left != NIL) {
534 rcode = WalkNodeInOrder(X->Left, callback, context);
535 if (rcode != 0) return rcode;
538 if (X->Right != NIL) {
539 rcode = WalkNodeInOrder(X->Right, callback, context);
540 if (rcode != 0) return rcode;
543 rcode = callback(context, X->Data);
544 if (rcode != 0) return rcode;
546 return 0; /* we know everything returned zero */
550 * Walk the entire tree. The callback function CANNOT modify
553 * The callback function should return 0 to continue walking.
554 * Any other value stops the walk, and is returned.
556 int rbtree_walk(rbtree_t *tree, RBTREE_ORDER order,
557 int (*callback)(void *, void *), void *context)
561 return WalkNodePreOrder(tree->Root, callback, context);
563 return WalkNodeInOrder(tree->Root, callback, context);
565 return WalkNodePostOrder(tree->Root, callback, context);
574 int rbtree_num_elements(rbtree_t *tree)
578 return tree->num_elements;
583 * Given a Node, return the data.
585 void *rbtree_node2data(rbtree_t *tree, rbnode_t *node)
587 tree = tree; /* -Wunused */
589 if (!node) return NULL;