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,2006 The FreeRADIUS server project
25 #include <freeradius-devel/libradius.h>
30 #define PTHREAD_MUTEX_LOCK(_x) if (_x->lock) pthread_mutex_lock(&((_x)->mutex))
31 #define PTHREAD_MUTEX_UNLOCK(_x) if (_x->lock) pthread_mutex_unlock(&((_x)->mutex))
33 #define PTHREAD_MUTEX_LOCK(_x)
34 #define PTHREAD_MUTEX_UNLOCK(_x)
37 /* red-black tree description */
38 typedef enum { Black, Red } NodeColor;
41 rbnode_t *Left; /* left child */
42 rbnode_t *Right; /* right child */
43 rbnode_t *Parent; /* parent */
44 NodeColor Color; /* node color (black, red) */
45 void *Data; /* data stored in node */
48 #define NIL &Sentinel /* all leafs are sentinels */
49 static rbnode_t Sentinel = { NIL, NIL, NULL, Black, NULL};
57 int (*Compare)(void const *, void const *);
59 void (*freeNode)(void *);
62 pthread_mutex_t mutex;
65 #define RBTREE_MAGIC (0x5ad09c42)
68 * Walks the tree to delete all nodes.
69 * Does NOT re-balance it!
71 static void FreeWalker(rbtree_t *tree, rbnode_t *X)
73 if (X->Left != NIL) FreeWalker(tree, X->Left);
74 if (X->Right != NIL) FreeWalker(tree, X->Right);
76 if (tree->freeNode) tree->freeNode(X->Data);
80 void rbtree_free(rbtree_t *tree)
84 PTHREAD_MUTEX_LOCK(tree);
87 * Walk the tree, deleting the nodes...
89 if (tree->Root != NIL) FreeWalker(tree, tree->Root);
97 if (tree->lock) pthread_mutex_destroy(&tree->mutex);
104 * Create a new red-black tree.
106 rbtree_t *rbtree_create(int (*Compare)(void const *, void const *),
107 void (*freeNode)(void *),
112 if (!Compare) return NULL;
114 tree = malloc(sizeof(*tree));
115 if (!tree) return NULL;
117 memset(tree, 0, sizeof(*tree));
119 tree->magic = RBTREE_MAGIC;
122 tree->Compare = Compare;
123 tree->replace_flag = flags & RBTREE_FLAG_REPLACE;
124 #ifdef HAVE_PTHREAD_H
125 tree->lock = flags & RBTREE_FLAG_LOCK;
127 pthread_mutex_init(&tree->mutex, NULL);
130 tree->freeNode = freeNode;
136 static void RotateLeft(rbtree_t *tree, rbnode_t *X)
138 /**************************
139 * rotate Node X to left *
140 **************************/
142 rbnode_t *Y = X->Right;
144 /* establish X->Right link */
146 if (Y->Left != NIL) Y->Left->Parent = X;
148 /* establish Y->Parent link */
149 if (Y != NIL) Y->Parent = X->Parent;
151 if (X == X->Parent->Left)
154 X->Parent->Right = Y;
161 if (X != NIL) X->Parent = Y;
164 static void RotateRight(rbtree_t *tree, rbnode_t *X)
166 /****************************
167 * rotate Node X to right *
168 ****************************/
170 rbnode_t *Y = X->Left;
172 /* establish X->Left link */
174 if (Y->Right != NIL) Y->Right->Parent = X;
176 /* establish Y->Parent link */
177 if (Y != NIL) Y->Parent = X->Parent;
179 if (X == X->Parent->Right)
180 X->Parent->Right = Y;
189 if (X != NIL) X->Parent = Y;
192 static void InsertFixup(rbtree_t *tree, rbnode_t *X)
194 /*************************************
195 * maintain red-black tree balance *
196 * after inserting node X *
197 *************************************/
199 /* check red-black properties */
200 while (X != tree->Root && X->Parent->Color == Red) {
201 /* we have a violation */
202 if (X->Parent == X->Parent->Parent->Left) {
203 rbnode_t *Y = X->Parent->Parent->Right;
204 if (Y->Color == Red) {
207 X->Parent->Color = Black;
209 X->Parent->Parent->Color = Red;
210 X = X->Parent->Parent;
214 if (X == X->Parent->Right) {
215 /* make X a left child */
220 /* recolor and rotate */
221 X->Parent->Color = Black;
222 X->Parent->Parent->Color = Red;
223 RotateRight(tree, X->Parent->Parent);
227 /* mirror image of above code */
228 rbnode_t *Y = X->Parent->Parent->Left;
229 if (Y->Color == Red) {
232 X->Parent->Color = Black;
234 X->Parent->Parent->Color = Red;
235 X = X->Parent->Parent;
239 if (X == X->Parent->Left) {
241 RotateRight(tree, X);
243 X->Parent->Color = Black;
244 X->Parent->Parent->Color = Red;
245 RotateLeft(tree, X->Parent->Parent);
250 tree->Root->Color = Black;
255 * Insert an element into the tree.
257 rbnode_t *rbtree_insertnode(rbtree_t *tree, void *Data)
259 rbnode_t *Current, *Parent, *X;
261 /***********************************************
262 * allocate node for Data and insert in tree *
263 ***********************************************/
265 PTHREAD_MUTEX_LOCK(tree);
267 /* find where node belongs */
268 Current = tree->Root;
270 while (Current != NIL) {
274 * See if two entries are identical.
276 result = tree->Compare(Data, Current->Data);
279 * Don't replace the entry.
281 if (tree->replace_flag == 0) {
282 PTHREAD_MUTEX_UNLOCK(tree);
287 * Do replace the entry.
289 if (tree->freeNode) tree->freeNode(Current->Data);
290 Current->Data = Data;
291 PTHREAD_MUTEX_UNLOCK(tree);
296 Current = (result < 0) ? Current->Left : Current->Right;
300 if ((X = malloc (sizeof(*X))) == NULL) {
301 exit(1); /* FIXME! */
310 /* insert node in tree */
312 if (tree->Compare(Data, Parent->Data) <= 0)
320 InsertFixup(tree, X);
322 tree->num_elements++;
324 PTHREAD_MUTEX_UNLOCK(tree);
328 bool rbtree_insert(rbtree_t *tree, void *Data)
330 if (rbtree_insertnode(tree, Data)) return true;
334 static void DeleteFixup(rbtree_t *tree, rbnode_t *X, rbnode_t *Parent)
336 /*************************************
337 * maintain red-black tree balance *
338 * after deleting node X *
339 *************************************/
341 while (X != tree->Root && X->Color == Black) {
342 if (X == Parent->Left) {
343 rbnode_t *W = Parent->Right;
344 if (W->Color == Red) {
346 Parent->Color = Red; /* Parent != NIL? */
347 RotateLeft(tree, Parent);
350 if (W->Left->Color == Black && W->Right->Color == Black) {
351 if (W != NIL) W->Color = Red;
355 if (W->Right->Color == Black) {
356 if (W->Left != NIL) W->Left->Color = Black;
358 RotateRight(tree, W);
361 W->Color = Parent->Color;
362 if (Parent != NIL) Parent->Color = Black;
363 if (W->Right->Color != Black) {
364 W->Right->Color = Black;
366 RotateLeft(tree, Parent);
370 rbnode_t *W = Parent->Left;
371 if (W->Color == Red) {
373 Parent->Color = Red; /* Parent != NIL? */
374 RotateRight(tree, Parent);
377 if (W->Right->Color == Black && W->Left->Color == Black) {
378 if (W != NIL) W->Color = Red;
382 if (W->Left->Color == Black) {
383 if (W->Right != NIL) W->Right->Color = Black;
388 W->Color = Parent->Color;
389 if (Parent != NIL) Parent->Color = Black;
390 if (W->Left->Color != Black) {
391 W->Left->Color = Black;
393 RotateRight(tree, Parent);
398 if (X != NIL) X->Color = Black; /* Avoid cache-dirty on NIL */
402 * Delete an element from the tree.
404 void rbtree_delete(rbtree_t *tree, rbnode_t *Z)
409 /*****************************
410 * delete node Z from tree *
411 *****************************/
413 if (!Z || Z == NIL) return;
415 PTHREAD_MUTEX_LOCK(tree);
417 if (Z->Left == NIL || Z->Right == NIL) {
418 /* Y has a NIL node as a child */
421 /* find tree successor with a NIL node as a child */
423 while (Y->Left != NIL) Y = Y->Left;
426 /* X is Y's only child */
430 X = Y->Right; /* may be NIL! */
432 /* remove Y from the parent chain */
434 if (X != NIL) X->Parent = Parent;
437 if (Y == Parent->Left)
445 if (tree->freeNode) tree->freeNode(Z->Data);
449 if (Y->Color == Black)
450 DeleteFixup(tree, X, Parent);
453 * The user structure in Y->Data MAY include a
454 * pointer to Y. In that case, we CANNOT delete
455 * Y. Instead, we copy Z (which is now in the
456 * tree) to Y, and fix up the parent/child
459 memcpy(Y, Z, sizeof(*Y));
464 if (Y->Parent->Left == Z) Y->Parent->Left = Y;
465 if (Y->Parent->Right == Z) Y->Parent->Right = Y;
467 if (Y->Left->Parent == Z) Y->Left->Parent = Y;
468 if (Y->Right->Parent == Z) Y->Right->Parent = Y;
473 if (tree->freeNode) tree->freeNode(Y->Data);
475 if (Y->Color == Black)
476 DeleteFixup(tree, X, Parent);
481 tree->num_elements--;
482 PTHREAD_MUTEX_UNLOCK(tree);
486 * Delete a node from the tree, based on given data, which MUST
487 * have come from rbtree_finddata().
489 bool rbtree_deletebydata(rbtree_t *tree, void const *data)
491 rbnode_t *node = rbtree_find(tree, data);
493 if (!node) return false;
495 rbtree_delete(tree, node);
502 * Find an element in the tree, returning the data, not the node.
504 rbnode_t *rbtree_find(rbtree_t *tree, void const *Data)
506 /*******************************
507 * find node containing Data *
508 *******************************/
513 PTHREAD_MUTEX_LOCK(tree);
514 Current = tree->Root;
516 while (Current != NIL) {
517 int result = tree->Compare(Data, Current->Data);
520 PTHREAD_MUTEX_UNLOCK(tree);
523 Current = (result < 0) ?
524 Current->Left : Current->Right;
528 PTHREAD_MUTEX_UNLOCK(tree);
533 * Find the user data.
535 void *rbtree_finddata(rbtree_t *tree, void const *Data)
539 X = rbtree_find(tree, Data);
546 * Walk the tree, Pre-order
548 * We call ourselves recursively for each function, but that's OK,
549 * as the stack is only log(N) deep, which is ~12 entries deep.
551 static int WalkNodePreOrder(rbnode_t *X,
552 int (*callback)(void *, void *), void *context)
555 rbnode_t *Left, *Right;
560 rcode = callback(context, X->Data);
561 if (rcode != 0) return rcode;
564 rcode = WalkNodePreOrder(Left, callback, context);
565 if (rcode != 0) return rcode;
569 rcode = WalkNodePreOrder(Right, callback, context);
570 if (rcode != 0) return rcode;
573 return 0; /* we know everything returned zero */
579 static int WalkNodeInOrder(rbnode_t *X,
580 int (*callback)(void *, void *), void *context)
585 if (X->Left != NIL) {
586 rcode = WalkNodeInOrder(X->Left, callback, context);
587 if (rcode != 0) return rcode;
592 rcode = callback(context, X->Data);
593 if (rcode != 0) return rcode;
596 rcode = WalkNodeInOrder(Right, callback, context);
597 if (rcode != 0) return rcode;
600 return 0; /* we know everything returned zero */
607 static int WalkNodePostOrder(rbnode_t *X,
608 int (*callback)(void *, void*), void *context)
612 if (X->Left != NIL) {
613 rcode = WalkNodePostOrder(X->Left, callback, context);
614 if (rcode != 0) return rcode;
617 if (X->Right != NIL) {
618 rcode = WalkNodePostOrder(X->Right, callback, context);
619 if (rcode != 0) return rcode;
622 rcode = callback(context, X->Data);
623 if (rcode != 0) return rcode;
625 return 0; /* we know everything returned zero */
629 * Walk the entire tree. The callback function CANNOT modify
632 * The callback function should return 0 to continue walking.
633 * Any other value stops the walk, and is returned.
635 int rbtree_walk(rbtree_t *tree, RBTREE_ORDER order,
636 int (*callback)(void *, void *), void *context)
640 if (tree->Root == NIL) return 0;
642 PTHREAD_MUTEX_LOCK(tree);
645 rcode = WalkNodePreOrder(tree->Root, callback, context);
648 rcode = WalkNodeInOrder(tree->Root, callback, context);
651 rcode = WalkNodePostOrder(tree->Root, callback, context);
658 PTHREAD_MUTEX_UNLOCK(tree);
662 int rbtree_num_elements(rbtree_t *tree)
666 return tree->num_elements;
671 * Given a Node, return the data.
673 void *rbtree_node2data(UNUSED rbtree_t *tree, rbnode_t *node)
675 if (!node) return NULL;
681 * Return left-most child.
683 void *rbtree_min(rbtree_t *tree)
688 if (!tree || !tree->Root) return NULL;
690 PTHREAD_MUTEX_LOCK(tree);
691 Current = tree->Root;
692 while (Current->Left != NIL) Current = Current->Left;
694 data = Current->Data;
695 PTHREAD_MUTEX_UNLOCK(tree);