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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2004 The FreeRADIUS server project
23 static const char rcsid[] = "$Id$";
31 #include "libradius.h"
33 /* red-black tree description */
34 typedef enum { Black, Red } NodeColor;
37 rbnode_t *Left; /* left child */
38 rbnode_t *Right; /* right child */
39 rbnode_t *Parent; /* parent */
40 NodeColor Color; /* node color (black, red) */
41 void *Data; /* data stored in node */
44 #define NIL &Sentinel /* all leafs are sentinels */
45 static const rbnode_t Sentinel = { NIL, NIL, NULL, Black, NULL};
53 int (*Compare)(const void *, const void *);
55 void (*freeNode)(void *);
57 #define RBTREE_MAGIC (0x5ad09c42)
60 * Walks the tree to delete all nodes.
61 * Does NOT re-balance it!
63 static void FreeWalker(rbtree_t *tree, rbnode_t *X)
65 if (X->Left != NIL) FreeWalker(tree, X->Left);
66 if (X->Right != NIL) FreeWalker(tree, X->Right);
68 if (tree->freeNode) tree->freeNode(X->Data);
72 void rbtree_free(rbtree_t *tree)
77 * Walk the tree, deleting the nodes...
79 if (tree->Root != NIL) FreeWalker(tree, tree->Root);
89 * Create a new red-black tree.
91 rbtree_t *rbtree_create(int (*Compare)(const void *, const void *),
92 void (*freeNode)(void *),
97 if (!Compare) return NULL;
99 tree = malloc(sizeof(*tree));
100 if (!tree) return NULL;
102 memset(tree, 0, sizeof(*tree));
104 tree->magic = RBTREE_MAGIC;
107 tree->Compare = Compare;
108 tree->replace_flag = replace_flag;
109 tree->freeNode = freeNode;
115 static void RotateLeft(rbtree_t *tree, rbnode_t *X)
117 /**************************
118 * rotate Node X to left *
119 **************************/
121 rbnode_t *Y = X->Right;
123 /* establish X->Right link */
125 if (Y->Left != NIL) Y->Left->Parent = X;
127 /* establish Y->Parent link */
128 if (Y != NIL) Y->Parent = X->Parent;
130 if (X == X->Parent->Left)
133 X->Parent->Right = Y;
140 if (X != NIL) X->Parent = Y;
143 static void RotateRight(rbtree_t *tree, rbnode_t *X)
145 /****************************
146 * rotate Node X to right *
147 ****************************/
149 rbnode_t *Y = X->Left;
151 /* establish X->Left link */
153 if (Y->Right != NIL) Y->Right->Parent = X;
155 /* establish Y->Parent link */
156 if (Y != NIL) Y->Parent = X->Parent;
158 if (X == X->Parent->Right)
159 X->Parent->Right = Y;
168 if (X != NIL) X->Parent = Y;
171 static void InsertFixup(rbtree_t *tree, rbnode_t *X)
173 /*************************************
174 * maintain red-black tree balance *
175 * after inserting node X *
176 *************************************/
178 /* check red-black properties */
179 while (X != tree->Root && X->Parent->Color == Red) {
180 /* we have a violation */
181 if (X->Parent == X->Parent->Parent->Left) {
182 rbnode_t *Y = X->Parent->Parent->Right;
183 if (Y->Color == Red) {
186 X->Parent->Color = Black;
188 X->Parent->Parent->Color = Red;
189 X = X->Parent->Parent;
193 if (X == X->Parent->Right) {
194 /* make X a left child */
199 /* recolor and rotate */
200 X->Parent->Color = Black;
201 X->Parent->Parent->Color = Red;
202 RotateRight(tree, X->Parent->Parent);
206 /* mirror image of above code */
207 rbnode_t *Y = X->Parent->Parent->Left;
208 if (Y->Color == Red) {
211 X->Parent->Color = Black;
213 X->Parent->Parent->Color = Red;
214 X = X->Parent->Parent;
218 if (X == X->Parent->Left) {
220 RotateRight(tree, X);
222 X->Parent->Color = Black;
223 X->Parent->Parent->Color = Red;
224 RotateLeft(tree, X->Parent->Parent);
229 tree->Root->Color = Black;
234 * Insert an element into the tree.
236 int rbtree_insert(rbtree_t *tree, void *Data)
238 rbnode_t *Current, *Parent, *X;
240 /***********************************************
241 * allocate node for Data and insert in tree *
242 ***********************************************/
244 /* find where node belongs */
245 Current = tree->Root;
247 while (Current != NIL) {
251 * See if two entries are identical.
253 result = tree->Compare(Data, Current->Data);
256 * Don't replace the entry.
258 if (tree->replace_flag == 0) {
263 * Do replace the entry.
265 if (tree->freeNode) tree->freeNode(Current->Data);
266 Current->Data = Data;
271 Current = (result < 0) ? Current->Left : Current->Right;
275 if ((X = malloc (sizeof(*X))) == NULL) {
276 exit(1); /* FIXME! */
285 /* insert node in tree */
287 if (tree->Compare(Data, Parent->Data) <= 0)
295 InsertFixup(tree, X);
297 tree->num_elements++;
302 static void DeleteFixup(rbtree_t *tree, rbnode_t *X, rbnode_t *Parent)
304 /*************************************
305 * maintain red-black tree balance *
306 * after deleting node X *
307 *************************************/
309 while (X != tree->Root && X->Color == Black) {
310 if (X == Parent->Left) {
311 rbnode_t *W = Parent->Right;
312 if (W->Color == Red) {
314 Parent->Color = Red; /* Parent != NIL? */
315 RotateLeft(tree, Parent);
318 if (W->Left->Color == Black && W->Right->Color == Black) {
319 if (W != NIL) W->Color = Red;
323 if (W->Right->Color == Black) {
324 if (W->Left != NIL) W->Left->Color = Black;
326 RotateRight(tree, W);
329 W->Color = Parent->Color;
330 if (Parent != NIL) Parent->Color = Black;
331 if (W->Right->Color != Black) {
332 W->Right->Color = Black;
334 RotateLeft(tree, Parent);
338 rbnode_t *W = Parent->Left;
339 if (W->Color == Red) {
341 Parent->Color = Red; /* Parent != NIL? */
342 RotateRight(tree, Parent);
345 if (W->Right->Color == Black && W->Left->Color == Black) {
346 if (W != NIL) W->Color = Red;
350 if (W->Left->Color == Black) {
351 if (W->Right != NIL) W->Right->Color = Black;
356 W->Color = Parent->Color;
357 if (Parent != NIL) Parent->Color = Black;
358 if (W->Left->Color != Black) {
359 W->Left->Color = Black;
361 RotateRight(tree, Parent);
370 * Delete an element from the tree.
372 void rbtree_delete(rbtree_t *tree, rbnode_t *Z)
377 /*****************************
378 * delete node Z from tree *
379 *****************************/
381 if (!Z || Z == NIL) return;
383 if (Z->Left == NIL || Z->Right == NIL) {
384 /* Y has a NIL node as a child */
387 /* find tree successor with a NIL node as a child */
389 while (Y->Left != NIL) Y = Y->Left;
392 /* X is Y's only child */
396 X = Y->Right; /* may be NIL! */
398 /* remove Y from the parent chain */
400 if (X != NIL) X->Parent = Parent;
403 if (Y == Parent->Left)
412 * Move the child's data to here, and then
413 * re-balance the tree.
415 if (tree->freeNode) tree->freeNode(Z->Data);
418 } else if (tree->freeNode) {
419 tree->freeNode(Z->Data);
422 if (Y->Color == Black && X != NIL)
423 DeleteFixup(tree, X, Parent);
427 tree->num_elements--;
431 * Delete a node from the tree, based on given data, which MUST
432 * have come from rbtree_finddata().
434 int rbtree_deletebydata(rbtree_t *tree, const void *data)
436 rbnode_t *node = rbtree_find(tree, data);
438 if (!node) return 0; /* false */
440 rbtree_delete(tree, node);
447 * Find an element in the tree, returning the data, not the node.
449 rbnode_t *rbtree_find(rbtree_t *tree, const void *Data)
451 /*******************************
452 * find node containing Data *
453 *******************************/
455 rbnode_t *Current = tree->Root;
457 while (Current != NIL) {
458 int result = tree->Compare(Data, Current->Data);
463 Current = (result < 0) ?
464 Current->Left : Current->Right;
471 * Find the user data.
473 void *rbtree_finddata(rbtree_t *tree, const void *Data)
477 X = rbtree_find(tree, Data);
484 * Walk the tree, Pre-order
486 * We call ourselves recursively for each function, but that's OK,
487 * as the stack is only log(N) deep, which is ~12 entries deep.
489 static int WalkNodePreOrder(rbnode_t *X,
490 int (*callback)(void *, void *), void *context)
494 rcode = callback(context, X->Data);
495 if (rcode != 0) return rcode;
497 if (X->Left != NIL) {
498 rcode = WalkNodePreOrder(X->Left, callback, context);
499 if (rcode != 0) return rcode;
502 if (X->Right != NIL) {
503 rcode = WalkNodePreOrder(X->Right, callback, context);
504 if (rcode != 0) return rcode;
507 return 0; /* we know everything returned zero */
513 static int WalkNodeInOrder(rbnode_t *X,
514 int (*callback)(void *, void *), void *context)
518 if (X->Left != NIL) {
519 rcode = WalkNodeInOrder(X->Left, callback, context);
520 if (rcode != 0) return rcode;
523 rcode = callback(context, X->Data);
524 if (rcode != 0) return rcode;
526 if (X->Right != NIL) {
527 rcode = WalkNodeInOrder(X->Right, callback, context);
528 if (rcode != 0) return rcode;
531 return 0; /* we know everything returned zero */
538 static int WalkNodePostOrder(rbnode_t *X,
539 int (*callback)(void *, void*), void *context)
543 if (X->Left != NIL) {
544 rcode = WalkNodeInOrder(X->Left, callback, context);
545 if (rcode != 0) return rcode;
548 if (X->Right != NIL) {
549 rcode = WalkNodeInOrder(X->Right, callback, context);
550 if (rcode != 0) return rcode;
553 rcode = callback(context, X->Data);
554 if (rcode != 0) return rcode;
556 return 0; /* we know everything returned zero */
560 * Walk the entire tree. The callback function CANNOT modify
563 * The callback function should return 0 to continue walking.
564 * Any other value stops the walk, and is returned.
566 int rbtree_walk(rbtree_t *tree, RBTREE_ORDER order,
567 int (*callback)(void *, void *), void *context)
571 return WalkNodePreOrder(tree->Root, callback, context);
573 return WalkNodeInOrder(tree->Root, callback, context);
575 return WalkNodePostOrder(tree->Root, callback, context);
584 int rbtree_num_elements(rbtree_t *tree)
588 return tree->num_elements;
593 * Given a Node, return the data.
595 void *rbtree_node2data(rbtree_t *tree, rbnode_t *node)
597 tree = tree; /* -Wunused */
599 if (!node) return NULL;