2 * rbtree.c RED-BLACK balanced binary trees.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program 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
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; 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 */
44 rbnode_t *left; //!< Left child
45 rbnode_t *right; //!< Right child
46 rbnode_t *parent; //!< Parent
47 node_colour_t colour; //!< Node colour (BLACK, RED)
48 void *data; //!< data stored in node
51 #define NIL &sentinel /* all leafs are sentinels */
52 static rbnode_t sentinel = { NIL, NIL, NULL, BLACK, NULL};
60 rb_comparator_t compare;
65 pthread_mutex_t mutex;
68 #define RBTREE_MAGIC (0x5ad09c42)
70 /** Walks the tree to delete all nodes Does NOT re-balance it!
73 static void free_walker(rbtree_t *tree, rbnode_t *x)
75 (void) talloc_get_type_abort(x, rbnode_t);
77 if (x->left != NIL) free_walker(tree, x->left);
78 if (x->right != NIL) free_walker(tree, x->right);
80 if (tree->free) tree->free(x->data);
84 void rbtree_free(rbtree_t *tree)
88 PTHREAD_MUTEX_LOCK(tree);
91 * walk the tree, deleting the nodes...
93 if (tree->root != NIL) free_walker(tree, tree->root);
100 #ifdef HAVE_PTHREAD_H
101 if (tree->lock) pthread_mutex_destroy(&tree->mutex);
107 /** Create a new RED-BLACK tree
110 rbtree_t *rbtree_create(rb_comparator_t compare, rb_free_t node_free, int flags)
114 if (!compare) return NULL;
116 tree = talloc_zero(NULL, rbtree_t);
117 if (!tree) return NULL;
120 tree->magic = RBTREE_MAGIC;
123 tree->compare = compare;
124 tree->replace = (flags & RBTREE_FLAG_REPLACE) != 0 ? true : false;
125 #ifdef HAVE_PTHREAD_H
126 tree->lock = (flags & RBTREE_FLAG_LOCK) != 0 ? true : false;
128 pthread_mutex_init(&tree->mutex, NULL);
131 tree->free = node_free;
136 /** Rotate Node x to left
139 static void rotate_left(rbtree_t *tree, rbnode_t *x)
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;
162 if (x != NIL) x->parent = y;
165 /** Rotate Node x to right
168 static void rotate_right(rbtree_t *tree, rbnode_t *x)
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;
190 if (x != NIL) x->parent = y;
193 /** Maintain red-black tree balance after inserting node x
196 static void insert_fixup(rbtree_t *tree, rbnode_t *x)
198 /* check RED-BLACK properties */
199 while ((x != tree->root) && (x->parent->colour == RED)) {
200 /* we have a violation */
201 if (x->parent == x->parent->parent->left) {
202 rbnode_t *y = x->parent->parent->right;
203 if (y->colour == RED) {
206 x->parent->colour = BLACK;
208 x->parent->parent->colour = RED;
209 x = x->parent->parent;
213 if (x == x->parent->right) {
214 /* make x a left child */
216 rotate_left(tree, x);
219 /* recolour and rotate */
220 x->parent->colour = BLACK;
221 x->parent->parent->colour = RED;
222 rotate_right(tree, x->parent->parent);
226 /* mirror image of above code */
227 rbnode_t *y = x->parent->parent->left;
228 if (y->colour == RED) {
231 x->parent->colour = BLACK;
233 x->parent->parent->colour = RED;
234 x = x->parent->parent;
238 if (x == x->parent->left) {
240 rotate_right(tree, x);
242 x->parent->colour = BLACK;
243 x->parent->parent->colour = RED;
244 rotate_left(tree, x->parent->parent);
249 tree->root->colour = BLACK;
253 /** Insert an element into the tree
256 rbnode_t *rbtree_insert_node(rbtree_t *tree, void *data)
258 rbnode_t *current, *parent, *x;
260 PTHREAD_MUTEX_LOCK(tree);
262 /* find where node belongs */
263 current = tree->root;
265 while (current != NIL) {
269 * See if two entries are identical.
271 result = tree->compare(data, current->data);
274 * Don't replace the entry.
276 if (!tree->replace) {
277 PTHREAD_MUTEX_UNLOCK(tree);
282 * Do replace the entry.
284 if (tree->free) tree->free(current->data);
285 current->data = data;
286 PTHREAD_MUTEX_UNLOCK(tree);
291 current = (result < 0) ? current->left : current->right;
295 x = talloc_zero(tree, rbnode_t);
297 fr_strerror_printf("No memory for new rbtree node");
298 PTHREAD_MUTEX_UNLOCK(tree);
308 /* insert node in tree */
310 if (tree->compare(data, parent->data) <= 0) {
319 insert_fixup(tree, x);
321 tree->num_elements++;
323 PTHREAD_MUTEX_UNLOCK(tree);
327 bool rbtree_insert(rbtree_t *tree, void *data)
329 if (rbtree_insert_node(tree, data)) return true;
333 /** Maintain RED-BLACK tree balance after deleting node x
336 static void delete_fixup(rbtree_t *tree, rbnode_t *x, rbnode_t *parent)
339 while (x != tree->root && x->colour == BLACK) {
340 if (x == parent->left) {
341 rbnode_t *w = parent->right;
342 if (w->colour == RED) {
344 parent->colour = RED; /* parent != NIL? */
345 rotate_left(tree, parent);
348 if ((w->left->colour == BLACK) && (w->right->colour == BLACK)) {
349 if (w != NIL) w->colour = RED;
353 if (w->right->colour == BLACK) {
354 if (w->left != NIL) w->left->colour = BLACK;
356 rotate_right(tree, w);
359 w->colour = parent->colour;
360 if (parent != NIL) parent->colour = BLACK;
361 if (w->right->colour != BLACK) {
362 w->right->colour = BLACK;
364 rotate_left(tree, parent);
368 rbnode_t *w = parent->left;
369 if (w->colour == RED) {
371 parent->colour = RED; /* parent != NIL? */
372 rotate_right(tree, parent);
375 if ((w->right->colour == BLACK) && (w->left->colour == BLACK)) {
376 if (w != NIL) w->colour = RED;
380 if (w->left->colour == BLACK) {
381 if (w->right != NIL) w->right->colour = BLACK;
383 rotate_left(tree, w);
386 w->colour = parent->colour;
387 if (parent != NIL) parent->colour = BLACK;
388 if (w->left->colour != BLACK) {
389 w->left->colour = BLACK;
391 rotate_right(tree, parent);
396 if (x != NIL) x->colour = BLACK; /* Avoid cache-dirty on NIL */
399 /** Delete an element (z) from the tree
402 static void rbtree_delete_internal(rbtree_t *tree, rbnode_t *z, bool skiplock)
407 if (!z || z == NIL) return;
410 PTHREAD_MUTEX_LOCK(tree);
413 if (z->left == NIL || z->right == NIL) {
414 /* y has a NIL node as a child */
417 /* find tree successor with a NIL node as a child */
419 while (y->left != NIL) y = y->left;
422 /* x is y's only child */
423 if (y->left != NIL) {
426 x = y->right; /* may be NIL! */
429 /* remove y from the parent chain */
431 if (x != NIL) x->parent = parent;
434 if (y == parent->left) {
444 if (tree->free) tree->free(z->data);
448 if ((y->colour == BLACK) && parent) {
449 delete_fixup(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->free) tree->free(y->data);
475 if (y->colour == BLACK)
476 delete_fixup(tree, x, parent);
481 tree->num_elements--;
483 PTHREAD_MUTEX_UNLOCK(tree);
486 void rbtree_delete(rbtree_t *tree, rbnode_t *z) {
487 rbtree_delete_internal(tree, z, false);
490 /** Delete a node from the tree, based on given data, which MUST have come from rbtree_finddata().
494 bool rbtree_deletebydata(rbtree_t *tree, void const *data)
496 rbnode_t *node = rbtree_find(tree, data);
498 if (!node) return false;
500 rbtree_delete(tree, node);
506 /** Find an element in the tree, returning the data, not the node
509 rbnode_t *rbtree_find(rbtree_t *tree, void const *data)
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);
532 /** Find the user data.
535 void *rbtree_finddata(rbtree_t *tree, void const *data)
539 x = rbtree_find(tree, data);
545 /** Walk the tree, Pre-order
547 * We call ourselves recursively for each function, but that's OK,
548 * as the stack is only log(N) deep, which is ~12 entries deep.
550 static int walk_node_pre_order(rbnode_t *x, rb_walker_t compare, void *context)
553 rbnode_t *left, *right;
558 rcode = compare(context, x->data);
559 if (rcode != 0) return rcode;
562 rcode = walk_node_pre_order(left, compare, context);
563 if (rcode != 0) return rcode;
567 rcode = walk_node_pre_order(right, compare, context);
568 if (rcode != 0) return rcode;
571 return 0; /* we know everything returned zero */
577 static int walk_node_in_order(rbnode_t *x, rb_walker_t compare, void *context)
582 if (x->left != NIL) {
583 rcode = walk_node_in_order(x->left, compare, context);
584 if (rcode != 0) return rcode;
589 rcode = compare(context, x->data);
590 if (rcode != 0) return rcode;
593 rcode = walk_node_in_order(right, compare, context);
594 if (rcode != 0) return rcode;
597 return 0; /* we know everything returned zero */
601 /** rbtree_post_order
604 static int walk_node_post_order(rbnode_t *x, rb_walker_t compare, void *context)
608 if (x->left != NIL) {
609 rcode = walk_node_post_order(x->left, compare, context);
610 if (rcode != 0) return rcode;
613 if (x->right != NIL) {
614 rcode = walk_node_post_order(x->right, compare, context);
615 if (rcode != 0) return rcode;
618 rcode = compare(context, x->data);
619 if (rcode != 0) return rcode;
621 return 0; /* we know everything returned zero */
625 /** rbtree_delete_order
627 * This executes an rbtree_in_order-like walk that adapts to changes in the
628 * tree above it, which may occur because we allow the compare to
629 * tell us to delete the current node.
631 * The compare should return:
634 * 0 - continue walking, don't delete the node
635 * 1 - delete the node and stop walking
636 * 2 - delete the node and continue walking
638 static int walk_delete_order(rbtree_t *tree, rb_walker_t compare, void *context)
643 /* Keep track of last node that refused deletion. */
645 while (solid == NIL) {
649 while (x->left != NIL) {
653 rcode = compare(context, x->data);
658 rbtree_delete_internal(tree, x, true);
668 if (x->right != NIL) {
673 if (x->parent->left == x) {
685 * walk the entire tree. The compare function CANNOT modify
688 * The compare function should return 0 to continue walking.
689 * Any other value stops the walk, and is returned.
691 int rbtree_walk(rbtree_t *tree, rb_order_t order, rb_walker_t compare, void *context)
695 if (tree->root == NIL) return 0;
697 PTHREAD_MUTEX_LOCK(tree);
700 case RBTREE_PRE_ORDER:
701 rcode = walk_node_pre_order(tree->root, compare, context);
704 case RBTREE_IN_ORDER:
705 rcode = walk_node_in_order(tree->root, compare, context);
708 case RBTREE_POST_ORDER:
709 rcode = walk_node_post_order(tree->root, compare, context);
712 case RBTREE_DELETE_ORDER:
713 rcode = walk_delete_order(tree, compare, context);
721 PTHREAD_MUTEX_UNLOCK(tree);
725 uint32_t rbtree_num_elements(rbtree_t *tree)
729 return tree->num_elements;
733 * Given a Node, return the data.
735 void *rbtree_node2data(UNUSED rbtree_t *tree, rbnode_t *node)
737 if (!node) return NULL;