The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
rb.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Red/black tree implementation
18 *
19 * @file src/lib/util/rb.c
20 *
21 * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
22 * @copyright 2004,2006 The FreeRADIUS server project
23 */
24RCSID("$Id: 770134614aa8d35bfd952bd8108cef0fd438a3c4 $")
25
26#include <freeradius-devel/util/rb.h>
27#include <freeradius-devel/util/strerror.h>
28
29#define NIL &sentinel /* all leafs are sentinels */
30static fr_rb_node_t sentinel = { NIL, NIL, NULL, NULL, BLACK, false };
31
32#ifndef NDEBUG
33# define RB_MAGIC (0x5ad09c42)
34#endif
35
36static int insert_node(fr_rb_node_t **existing, fr_rb_tree_t *tree, void *data) CC_HINT(nonnull(2,3));
37
38static inline CC_HINT(always_inline) void node_data_free(fr_rb_tree_t const *tree, fr_rb_node_t *node)
39{
40 if (!tree->data_free || unlikely(node->being_freed)) return;
41
42 node->being_freed = true;
43 tree->data_free(node->data);
44}
45
46/** Return the fr_rb_node_t that was allocated as part of the data structure
47 */
49{
50 return (fr_rb_node_t *)((uintptr_t)data + tree->offset);
51}
52
53/** Clear the fr_rb_node_t that was allocated as part of the data structure
54 */
55static void _node_inline_free(fr_rb_tree_t const *tree, fr_rb_node_t *node, bool free_data)
56{
57 if (free_data && tree->data_free) {
58 node_data_free(tree, node);
59 } else {
60 memset(node, 0, sizeof(fr_rb_node_t)); /* makes "still in tree?" checks easier */
61 }
62}
63
64/** Allocate a new fr_rb_node_t on the heap
65 */
67{
68 return talloc_zero(tree->node_ctx, fr_rb_node_t);
69}
70
71/** Clear the fr_rb_node_t that was allocated as part of the data structure
72 */
73static void _node_heap_free(fr_rb_tree_t const *tree, fr_rb_node_t *node, bool free_data)
74{
75 if (free_data) node_data_free(tree, node);
76 talloc_free(node);
77}
78
79/** Walks the tree to delete all nodes Does NOT re-balance it!
80 *
81 */
83{
84 if (x->left != NIL) free_walker(tree, x->left);
85 if (x->right != NIL) free_walker(tree, x->right);
86
87 tree->node_free(tree, x, true);
88}
89
90/** Free the rbtree cleaning up any nodes
91 *
92 * Walk the tree deleting nodes, then free any children of the tree.
93 *
94 * @note If the destructor of a talloc descendent needs to lookup any
95 * information in the tree, it will be unavailable at the point
96 * of freeing. We could fix this by introducing a pre-free callback
97 * which gets called before any of the nodes are deleted.
98 *
99 * @param[in] tree to tree.
100 * @return
101 * - 0 if tree was freed.
102 * - -1 if tree is already being freed.
103 */
104static int _tree_free(fr_rb_tree_t *tree)
105{
106 /*
107 * Prevent duplicate frees
108 */
109 if (unlikely(tree->being_freed)) return -1;
110 tree->being_freed = true;
111
112 /*
113 * walk the tree, deleting the nodes...
114 */
115 if ((tree->root != NIL) && tree->data_free) free_walker(tree, tree->root);
116
117 /*
118 * Ensure all dependents on the tree run their
119 * destructors. The tree at this point should
120 * and any tree operations should be empty.
121 */
122 talloc_free_children(tree);
123
124#ifndef NDEBUG
125 tree->magic = 0;
126#endif
127 tree->root = NIL;
128 tree->num_elements = 0;
129
130 return 0;
131}
132
133/** Initialise a new RED-BLACK tree
134 *
135 * @param[out] tree to initialise.
136 * @param[in] node_ctx the ctx used to allocate #fr_rb_node_t if the
137 * tree isn't using inline #fr_rb_node_t.
138 * @param[in] offset offsetof the #fr_rb_node_t field in the data being inserted.
139 * If < 0, nodes will be allocated on the heap.
140 * @param[in] type Talloc type of structures being inserted, may be NULL.
141 * @param[in] data_cmp Comparator function for ordering data in the tree.
142 * @param[in] data_free Free function to call whenever data is deleted or replaced.
143 * @return
144 * - -1 on error.
145 * - 0 on success.
146 */
147int _fr_rb_init(fr_rb_tree_t *tree, TALLOC_CTX *node_ctx,
148 ssize_t offset, char const *type,
149 fr_cmp_t data_cmp, fr_free_t data_free)
150{
151
152 if (unlikely(offset >= UINT16_MAX)) {
153 fr_strerror_printf("Inline fr_rb_node_t offset too large. "
154 "Expected <= %zu, got %zd", (size_t) UINT16_MAX, offset);
155 return -1;
156 }
157
158 *tree = (fr_rb_tree_t) {
159#ifndef NDEBUG
160 .magic = RB_MAGIC,
161#endif
162 .root = NIL,
163 .node_ctx = node_ctx,
164 .offset = offset < 0 ? 0 : (uint16_t)offset,
165 .type = type,
167 .data_free = data_free,
168 };
169
170 /*
171 * Use inline nodes
172 */
173 if (offset >= 0) {
176 /*
177 * Allocate node data on the heap
178 */
179 } else {
182 }
183
184 return 0;
185}
186
187/** Alloc a new RED-BLACK tree
188 *
189 * @param[in] ctx to allocate the tree in.
190 * Only the tree is allocated in this context, the memory
191 * for the #fr_rb_node_t is allocated as part of the data
192 * being inserted into the tree.
193 * @param[in] offset offsetof the #fr_rb_node_t field in the data being inserted.
194 * If < 0, nodes will be allocated on the heap.
195 * @param[in] type Talloc type of structures being inserted, may be NULL.
196 * @param[in] data_cmp Comparator function for ordering data in the tree.
197 * @param[in] data_free Free function to call whenever data is deleted or replaced.
198 * @return
199 * - A new tree on success.
200 * - NULL on failure.
201 */
202fr_rb_tree_t *_fr_rb_alloc(TALLOC_CTX *ctx,
203 ssize_t offset, char const *type,
204 fr_cmp_t data_cmp, fr_free_t data_free)
205{
206 fr_rb_tree_t *tree;
207
208 tree = talloc(ctx, fr_rb_tree_t);
209 if (unlikely(!tree)) return NULL;
210
211 if (unlikely(_fr_rb_init(tree, tree, offset, type, data_cmp, data_free) < 0)) {
212 talloc_free(tree);
213 return NULL;
214 }
215
216 talloc_set_destructor(tree, _tree_free);
217
218 return tree;
219}
220
221/** Rotate Node x to left
222 *
223 */
224static inline CC_HINT(always_inline) void rotate_left(fr_rb_tree_t *tree, fr_rb_node_t *x)
225{
226
227 fr_rb_node_t *y = x->right;
228
229 /* establish x->right link */
230 x->right = y->left;
231 if (y->left != NIL) y->left->parent = x;
232
233 /* establish y->parent link */
234 if (y != NIL) y->parent = x->parent;
235 if (x->parent != NIL) {
236 if (x == x->parent->left) {
237 x->parent->left = y;
238 } else {
239 x->parent->right = y;
240 }
241 } else {
242 tree->root = y;
243 }
244
245 /* link x and y */
246 y->left = x;
247 if (x != NIL) x->parent = y;
248}
249
250/** Rotate Node x to right
251 *
252 */
253static inline CC_HINT(always_inline) void rotate_right(fr_rb_tree_t *tree, fr_rb_node_t *x)
254{
255 fr_rb_node_t *y = x->left;
256
257 /* establish x->left link */
258 x->left = y->right;
259 if (y->right != NIL) y->right->parent = x;
260
261 /* establish y->parent link */
262 if (y != NIL) y->parent = x->parent;
263 if (x->parent != NIL) {
264 if (x == x->parent->right) {
265 x->parent->right = y;
266 } else {
267 x->parent->left = y;
268 }
269 } else {
270 tree->root = y;
271 }
272
273 /* link x and y */
274 y->right = x;
275 if (x != NIL) x->parent = y;
276}
277
278/** Maintain red-black tree balance after inserting node x
279 *
280 */
281static inline CC_HINT(always_inline) void insert_fixup(fr_rb_tree_t *tree, fr_rb_node_t *x)
282{
283 /* check RED-BLACK properties */
284 while ((x != tree->root) && (x->parent->colour == RED)) {
285 /* we have a violation */
286 if (x->parent == x->parent->parent->left) {
288 if (y->colour == RED) {
289 /* uncle is RED */
290 x->parent->colour = BLACK;
291 y->colour = BLACK;
292 x->parent->parent->colour = RED;
293 x = x->parent->parent;
294 } else {
295 /* uncle is BLACK */
296 if (x == x->parent->right) {
297 /* make x a left child */
298 x = x->parent;
299 rotate_left(tree, x);
300 }
301
302 /* recolour and rotate */
303 x->parent->colour = BLACK;
304 x->parent->parent->colour = RED;
305 rotate_right(tree, x->parent->parent);
306 }
307 } else {
308 /* mirror image of above code */
309 fr_rb_node_t *y = x->parent->parent->left;
310 if (y->colour == RED) {
311 /* uncle is RED */
312 x->parent->colour = BLACK;
313 y->colour = BLACK;
314 x->parent->parent->colour = RED;
315 x = x->parent->parent;
316 } else {
317 /* uncle is BLACK */
318 if (x == x->parent->left) {
319 x = x->parent;
320 rotate_right(tree, x);
321 }
322
323 x->parent->colour = BLACK;
324 x->parent->parent->colour = RED;
325 rotate_left(tree, x->parent->parent);
326 }
327 }
328 }
329
330 tree->root->colour = BLACK;
331}
332
333
334/** Insert an element into the tree
335 *
336 * @param[out] existing if a node exists, and existing is not NULL
337 * this will be populated with the node.
338 * @param[in] tree to search in.
339 * @param[in] data to search for.
340 * @return
341 * - 1 on existing (with existing populated).
342 * - 0 on success.
343 * - -1 on failure.
344 */
345static int insert_node(fr_rb_node_t **existing, fr_rb_tree_t *tree, void *data)
346{
347 fr_rb_node_t *current, *parent, *x;
348 fr_cmp_ret_t last_result = CMP_EQ;
349
350 if (unlikely(tree->being_freed)) return -1;
351
352#ifndef TALLOC_GET_TYPE_ABORT_NOOP
353 if (tree->type) (void)_talloc_get_type_abort(data, tree->type, __location__);
354#endif
355
356 /* find where node belongs */
357 current = tree->root;
358 parent = NIL;
359 while (current != NIL) {
360 fr_cmp_ret_t result;
361
362 /*
363 * See if two entries are identical.
364 */
365 result = tree->data_cmp(data, current->data);
366 if (unlikely(result == CMP_ERR)) return -1;
367 if (result == CMP_EQ) {
368 if (existing) *existing = current;
369 return 1;
370 }
371
372 parent = current;
373 last_result = result;
374 current = (result == CMP_LT) ? current->left : current->right;
375 }
376
377 /* setup new node */
378 x = tree->node_alloc(tree, data);
379 if (unlikely(!x)) return -1;
380
381 *x = (fr_rb_node_t){
382 .data = data,
383 .parent = parent,
384 .left = NIL,
385 .right = NIL,
386 .colour = RED
387 };
388
389 /* insert node in tree, on the side the final descent comparison chose */
390 if (parent != NIL) {
391 if (last_result == CMP_LT) {
392 parent->left = x;
393 } else {
394 parent->right = x;
395 }
396 } else {
397 tree->root = x;
398 }
399
400 insert_fixup(tree, x);
401
402 tree->num_elements++;
403
404 return 0;
405}
406
407/** Maintain RED-BLACK tree balance after deleting node x
408 *
409 */
411{
412 while (x != tree->root && x->colour == BLACK) {
413 if (x == parent->left) {
415 if (w->colour == RED) {
416 w->colour = BLACK;
417 parent->colour = RED; /* parent != NIL? */
418 rotate_left(tree, parent);
419 w = parent->right;
420 }
421 if ((w->left->colour == BLACK) && (w->right->colour == BLACK)) {
422 if (w != NIL) w->colour = RED;
423 x = parent;
424 parent = x->parent;
425 } else {
426 if (w->right->colour == BLACK) {
427 if (w->left != NIL) w->left->colour = BLACK;
428 w->colour = RED;
429 rotate_right(tree, w);
430 w = parent->right;
431 }
432 w->colour = parent->colour;
433 if (parent != NIL) parent->colour = BLACK;
434 if (w->right->colour != BLACK) {
435 w->right->colour = BLACK;
436 }
437 rotate_left(tree, parent);
438 x = tree->root;
439 }
440 } else {
442 if (w->colour == RED) {
443 w->colour = BLACK;
444 parent->colour = RED; /* parent != NIL? */
445 rotate_right(tree, parent);
446 w = parent->left;
447 }
448 if ((w->right->colour == BLACK) && (w->left->colour == BLACK)) {
449 if (w != NIL) w->colour = RED;
450 x = parent;
451 parent = x->parent;
452 } else {
453 if (w->left->colour == BLACK) {
454 if (w->right != NIL) w->right->colour = BLACK;
455 w->colour = RED;
456 rotate_left(tree, w);
457 w = parent->left;
458 }
459 w->colour = parent->colour;
460 if (parent != NIL) parent->colour = BLACK;
461 if (w->left->colour != BLACK) {
462 w->left->colour = BLACK;
463 }
464 rotate_right(tree, parent);
465 x = tree->root;
466 }
467 }
468 }
469 if (x != NIL) x->colour = BLACK; /* Avoid cache-dirty on NIL */
470}
471
472/** Delete an element (z) from the tree
473 *
474 */
475static void delete_internal(fr_rb_tree_t *tree, fr_rb_node_t *z, bool free_data)
476{
477 fr_rb_node_t *x, *y;
479
480 if (!z || z == NIL) return;
481
482 if (z->left == NIL || z->right == NIL) {
483 /* y has a NIL node as a child */
484 y = z;
485 } else {
486 /* find tree successor with a NIL node as a child */
487 y = z->right;
488 while (y->left != NIL) y = y->left;
489 }
490
491 /* x is y's only child */
492 if (y->left != NIL) {
493 x = y->left;
494 } else {
495 x = y->right; /* may be NIL! */
496 }
497
498 /* remove y from the parent chain */
499 parent = y->parent;
500 if (x != NIL) x->parent = parent;
501
502 if (parent != NIL) {
503 if (y == parent->left) {
504 parent->left = x;
505 } else {
506 parent->right = x;
507 }
508 } else {
509 tree->root = x;
510 }
511
512 if (y != z) {
513 void *y_data = y->data;
514
515 if ((y->colour == BLACK) && parent) delete_fixup(tree, x, parent);
516
517 /*
518 * The user structure in y->data May include a
519 * pointer to y. In that case, we CANNOT delete
520 * y. Instead, we copy z (which is now in the
521 * tree) to y, and fix up the parent/child
522 * pointers.
523 */
524 memcpy(y, z, sizeof(*y));
525 y->data = y_data;
526
527 if (y->parent == NIL) {
528 tree->root = y;
529 } else {
530 if (y->parent->left == z) y->parent->left = y;
531 if (y->parent->right == z) y->parent->right = y;
532 }
533 if (y->left->parent == z) y->left->parent = y;
534 if (y->right->parent == z) y->right->parent = y;
535
536 tree->node_free(tree, z, free_data);
537 } else {
538 if (y->colour == BLACK) delete_fixup(tree, x, parent);
539
540 tree->node_free(tree, y, free_data);
541 }
542
543 tree->num_elements--;
544}
545
546
547/* Find user data, returning the node
548 *
549 */
550static inline CC_HINT(always_inline) int find_node(fr_rb_node_t **found, fr_rb_tree_t const *tree, void const *data)
551{
552 fr_rb_node_t *current;
553
554 *found = NULL;
555 if (unlikely(tree->being_freed)) return 0;
556
557 current = tree->root;
558
559 while (current != NIL) {
560 fr_cmp_ret_t result = tree->data_cmp(data, current->data);
561
562 if (unlikely(result == CMP_ERR)) return -1;
563 if (result == CMP_EQ) {
564 *found = current;
565 return 0;
566 }
567
568 current = (result == CMP_LT) ? current->left : current->right;
569 }
570
571 return 0;
572}
573
574/** Find an element in the tree, returning the data, not the node
575 *
576 * @param[out] found the matching element, or NULL if no element matched.
577 * @param[in] tree to search in.
578 * @param[in] data to find.
579 * @return
580 * - 0 the comparison sequence succeeded, check found for the result.
581 * - -1 the comparator errored, retrieve the error with fr_strerror.
582 *
583 * @hidecallergraph
584 */
585CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
586int fr_rb_find(void **found, fr_rb_tree_t const *tree, void const *data)
587{
588 fr_rb_node_t *x;
589
590 *found = NULL;
591 if (unlikely(tree->being_freed)) return 0;
592 if (unlikely(find_node(&x, tree, data) < 0)) return -1;
593 if (x) *found = x->data;
594
595 return 0;
596}
597
598/** Attempt to find current data in the tree, if it does not exist insert it
599 *
600 * @param[out] found Pre-existing data we found.
601 * @param[in] tree to search/insert into.
602 * @param[in] data to find.
603 * @return
604 * - 1 if existing data was found, found will be populated.
605 * - 0 if no existing data was found.
606 * - -1 on insert or comparator error, retrieve the error with fr_strerror.
607 */
608int fr_rb_find_or_insert(void **found, fr_rb_tree_t *tree, void const *data)
609{
610 fr_rb_node_t *existing;
611
612 switch (insert_node(&existing, tree, UNCONST(void *, data))) {
613 case 1:
614 if (found) *found = existing->data;
615 return 1;
616
617 case 0:
618 if (found) *found = NULL;
619 return 0;
620
621 default:
622 if (found) *found = NULL;
623 return -1;
624 }
625}
626
627/** Insert data into a tree
628 *
629 * @param[in] tree to insert data into.
630 * @param[in] data to insert.
631 * @return
632 * - 0 if data was inserted.
633 * - 1 if data already existed and was not inserted.
634 * - -1 on comparator or allocation error, retrieve the error with fr_strerror.
635 */
636CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
637int fr_rb_insert(fr_rb_tree_t *tree, void const *data)
638{
639 return insert_node(NULL, tree, UNCONST(void *, data));
640}
641
642/** Replace old data with new data, OR insert if there is no old
643 *
644 * @param[out] old data that was replaced. If this argument
645 * is not NULL, then the old data will not
646 * be freed, even if a free function is
647 * configured.
648 * @param[in] tree to insert data into.
649 * @param[in] data to replace.
650 * @return
651 * - 1 if data was replaced.
652 * - 0 if data was inserted.
653 * - -1 if we failed to replace data, retrieve the error with fr_strerror.
654 */
655CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
656int fr_rb_replace(void **old, fr_rb_tree_t *tree, void const *data)
657{
658 fr_rb_node_t *node;
659
660 switch (insert_node(&node, tree, UNCONST(void *, data))) {
661 case 1: /* Something exists */
662 {
663 void *old_data = node->data;
664 int ret = 1;
665
666 /*
667 * If the fr_node_t is inline with the
668 * data structure, we need to delete
669 * the old node out of the tree, and
670 * perform a normal insert operation.
671 */
672 if (tree->node_alloc == _node_inline_alloc) {
673 delete_internal(tree, node, false);
674
675 /*
676 * The re-insert repeats comparisons which just succeeded,
677 * so a failure means the comparator is non-deterministic
678 * or allocation failed. The old data has already been
679 * unlinked; it is still handed to the caller (or freed)
680 * below so it isn't leaked.
681 */
682 if (!fr_cond_assert_msg(insert_node(NULL, tree, UNCONST(void *, data)) == 0,
683 "re-insert failed after delete")) {
684 ret = -1;
685 }
686 } else {
687 node->data = UNCONST(void *, data);
688 }
689
690 if (old) {
691 *old = old_data;
692 } else if (tree->data_free) {
693 tree->data_free(old_data);
694 }
695 return ret;
696 }
697 case 0: /* New node was inserted - There was no pre-existing node */
698 if (old) *old = NULL;
699 return 0;
700
701 default:
702 if (old) *old = NULL;
703 return -1;
704 }
705}
706
707/** Remove an entry from the tree, without freeing the data
708 *
709 * @param[out] removed the data we removed, if any. May be NULL.
710 * @param[in] tree to remove data from.
711 * @param[in] data to remove.
712 * @return
713 * - 0 if we removed data, removed is populated.
714 * - 1 if we couldn't find any matching data.
715 * - -1 if the comparator errored, retrieve the error with fr_strerror.
716 */
717CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
718int fr_rb_remove(void **removed, fr_rb_tree_t *tree, void const *data)
719{
720 fr_rb_node_t *node;
721
722 if (removed) *removed = NULL;
723 if (unlikely(tree->being_freed)) return 1;
724 if (unlikely(find_node(&node, tree, data) < 0)) return -1;
725 if (!node) return 1;
726
727 if (unlikely(node->being_freed)) {
728 if (removed) *removed = node->data;
729 return 0;
730 }
731
732 if (removed) *removed = node->data;
733 delete_internal(tree, node, false); /* nullified node->data */
734 return 0;
735}
736
737/** Remove an entry from the tree, using the node structure, without freeing the data
738 *
739 * This function can help where multiple items may be in the
740 * tree with the same comparator value.
741 *
742 * @param[in] tree to remove data from.
743 * @param[in] node to remove.
744 * @return
745 * - The user data we removed.
746 * - NULL if the user data wasn't in the tree.
747 */
749{
750 void *node_data;
751
752 if (!fr_rb_node_inline_in_tree(node)) return NULL;
753 node_data = node->data;
754 delete_internal(tree, node, false); /* nullified node->data */
755 return node_data;
756}
757
758/** Remove node and free data (if a free function was specified)
759 *
760 * @param[in] tree to remove data from.
761 * @param[in] data to remove/free.
762 * @return
763 * - true if we removed data.
764 * - false if we couldn't find any matching data.
765 */
766CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
767int fr_rb_delete(fr_rb_tree_t *tree, void const *data)
768{
769 fr_rb_node_t *node;
770
771 if (unlikely(tree->being_freed)) return 1;
772 if (unlikely(find_node(&node, tree, data) < 0)) return -1;
773 if (!node) return 1;
774
775 if (unlikely(node->being_freed)) return 0;
776
777 delete_internal(tree, node, tree->data_free);
778
779 return 0;
780}
781
782/** Remove node and free data (if a free function was specified)
783 *
784 * This function can help where multiple items may be in the
785 * tree with the same comparator value.
786 *
787 * @param[in] tree to remove data from.
788 * @param[in] node to remove/free.
789 * @return
790 * - true if we removed data.
791 * - false if we couldn't find any matching data.
792 */
794{
795 if (!fr_rb_node_inline_in_tree(node)) return false;
796
797 delete_internal(tree, node, true);
798
799 return true;
800}
801
802/** Return how many nodes there are in a tree
803 *
804 * @param[in] tree to return node count for.
805 */
806CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
808{
809 return tree->num_elements;
810}
811
813{
814 fr_rb_node_t *x = tree->root;
815
816 if (x == NIL) return NULL;
817
818 /*
819 * First node is the leftmost
820 */
821 while (x->left != NIL) x = x->left;
822
823 return x->data;
824}
825
826
828{
829 fr_rb_node_t *x = tree->root;
830
831 if (x == NIL) return NULL;
832
833 /*
834 * Last node is the rightmost
835 */
836 while (x->right != NIL) x = x->right;
837
838 return x->data;
839}
840
841
842/** Initialise an in-order iterator
843 *
844 * @param[in] tree to iterate over.
845 * @param[out] iter to initialise.
846 * @return
847 * - The first node. Mutex will be held.
848 * - NULL if the tree is empty.
849 */
851{
852 fr_rb_node_t *x = tree->root;
853
854 if (x == NIL) return NULL;
855
856 /*
857 * First node is the leftmost
858 */
859 while (x->left != NIL) x = x->left;
860
861 *iter = (fr_rb_iter_inorder_t){
862 .node = x
863 };
864
865 return x->data;
866}
867
868/** Return the next node
869 *
870 * @param[in] tree to iterate over.
871 * @param[in] iter previously initialised with #fr_rb_iter_init_inorder
872 * @return
873 * - The next node.
874 * - NULL if no more nodes remain.
875 */
877{
878 fr_rb_node_t *x = iter->node, *y;
879
880 /*
881 * Catch callers repeatedly calling iterator
882 * at the end.
883 */
884 if (unlikely(iter->node == NIL)) return NULL;
885
886 /*
887 * fr_rb_iter_delete() has already deleted this node,
888 * and saved the next one for us. (We check for NULL;
889 * NIL just means we're at the end.)
890 */
891 if (!iter->node) {
892 iter->node = iter->next;
893 iter->next = NULL;
894 return iter->node->data;
895 }
896
897 if (x->right != NIL) {
898 x = x->right;
899
900 while (x->left != NIL) x = x->left;
901 iter->node = x;
902
903 return x->data;
904 }
905
906 y = x;
907 x = x->parent;
908 while ((x != NIL) && (y == x->right)) {
909 y = x;
910 x = x->parent;
911 }
912
913 iter->node = x;
914
915 return x->data;
916}
917
918/** Remove the current node from the tree
919 *
920 * @note Only makes sense for in-order traversals.
921 *
922 * @param[in] tree to iterate over.
923 * @param[in] iter previously initialised with #fr_rb_iter_init_inorder
924 */
926{
927 fr_rb_node_t *x = iter->node;
928
929 if (unlikely(x == NIL)) return;
930 (void) fr_rb_iter_next_inorder(tree, iter);
931 iter->next = iter->node;
932 iter->node = NULL;
933 delete_internal(tree, x, true);
934}
935
936/** Initialise a pre-order iterator
937 *
938 * @param[in] tree to iterate over.
939 * @param[out] iter to initialise.
940 * @return
941 * - The first node. Mutex will be held.
942 * - NULL if the tree is empty.
943 */
945{
946 fr_rb_node_t *x = tree->root;
947
948 if (x == NIL) return NULL;
949
950 /*
951 * First, the root.
952 */
953 *iter = (fr_rb_iter_preorder_t){
954 .node = x
955 };
956
957 return x->data;
958}
959
960/** Return the next node
961 *
962 * @param[in] tree to iterate over.
963 * @param[in] iter previously initialised with #fr_rb_iter_init_preorder
964 * @return
965 * - The next node.
966 * - NULL if no more nodes remain.
967 */
969{
970 fr_rb_node_t *x = iter->node, *y;
971
972 /*
973 * Catch callers repeatedly calling iterator
974 * at the end.
975 */
976 if (unlikely(iter->node == NIL)) return NULL;
977
978 /*
979 * Next is a child of the just-returned node, if it has one.
980 * (Left child first.)
981 */
982 if (x->left != NIL) {
983 x = x->left;
984 iter->node = x;
985 return x->data;
986 }
987 if (x->right != NIL) {
988 x = x->right;
989 iter->node = x;
990 return x->data;
991 }
992
993 /*
994 * Otherwise, the nearest ancestor's unreturned right
995 * child, if one exists.
996 */
997 for (; (y = x->parent) != NIL; x = y) {
998 if (y->right != NIL && y->right != x) {
999 x = y->right;
1000 iter->node = x;
1001 return x->data;
1002 }
1003 }
1004
1005 /*
1006 * None of the above? We're done.
1007 */
1008 iter->node = NIL;
1009
1010 return NULL;
1011}
1012
1013/** Initialise a post-order iterator
1014 *
1015 * @param[in] tree to iterate over.
1016 * @param[out] iter to initialise.
1017 * @return
1018 * - The first node.
1019 * - NULL if the tree is empty.
1020 */
1022{
1023 fr_rb_node_t *x = tree->root;
1024
1025 if (x == NIL) return NULL;
1026
1027 /*
1028 * First: the deepest leaf to the left (jogging to the
1029 * right if there's a right child but no left).
1030 */
1031 for (;;) {
1032 for (; x->left != NIL; x = x->left) ;
1033 if (x->right == NIL) break;
1034 x = x->right;
1035 }
1036
1037 *iter = (fr_rb_iter_postorder_t){
1038 .node = x
1039 };
1040
1041 return x->data;
1042}
1043
1044/** Return the next node
1045 *
1046 * @param[in] tree to iterate over.
1047 * @param[in] iter previously initialised with #fr_rb_iter_init_postorder
1048 * @return
1049 * - The next node.
1050 * - NULL if no more nodes remain.
1051 */
1053{
1054 fr_rb_node_t *x = iter->node, *y;
1055
1056 /*
1057 * Catch callers repeatedly calling iterator
1058 * at the end.
1059 */
1060 if (unlikely(iter->node == NIL)) return NULL;
1061
1062 /*
1063 * This is postorder, so a just-returned node's
1064 * descendants have all been returned. If there
1065 * is another node, it's an ancestor or one of
1066 * its not-yet-returned descendants...but if
1067 * we're at the root, we're done.
1068 */
1069 y = x->parent;
1070 if (y == NIL) {
1071 iter->node = NIL;
1072 return NULL;
1073 }
1074
1075 /*
1076 * Return the parent if it has no right child, or it has one but
1077 * it's been returned.
1078 */
1079 if (y->right == NIL || y->right == x) {
1080 iter->node = y;
1081 return y->data;
1082 }
1083
1084 /*
1085 * Otherwise, it's as if we're starting over with the right child.
1086 */
1087 x = y->right;
1088 for (;;) {
1089 for (; x->left != NIL; x = x->left) ;
1090 if (x->right == NIL) break;
1091 x = x->right;
1092 }
1093
1094 iter->node = x;
1095 return x->data;
1096}
1097
1098#define DEF_RB_FLATTEN_FUNC(_order) \
1099int fr_rb_flatten_##_order(TALLOC_CTX *ctx, void **out[], fr_rb_tree_t *tree) \
1100{ \
1101 uint32_t num = fr_rb_num_elements(tree), i; \
1102 fr_rb_iter_##_order##_t iter; \
1103 void *item, **list; \
1104 if (unlikely(!(list = talloc_array(ctx, void *, num)))) return -1; \
1105 for (item = fr_rb_iter_init_##_order(tree, &iter), i = 0; \
1106 item; \
1107 item = fr_rb_iter_next_##_order(tree, &iter), i++) list[i] = item; \
1108 *out = list; \
1109 return 0; \
1110}
1111DEF_RB_FLATTEN_FUNC(preorder)
1112DEF_RB_FLATTEN_FUNC(postorder)
1113DEF_RB_FLATTEN_FUNC(inorder)
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:186
#define RCSID(id)
Definition build.h:560
#define CC_NO_UBSAN(_sanitize)
Definition build.h:503
#define unlikely(_x)
Definition build.h:455
#define UNUSED
Definition build.h:384
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:189
talloc_free(hp)
unsigned short uint16_t
unsigned int uint32_t
long int ssize_t
void(* fr_free_t)(void *)
Definition misc.h:58
fr_cmp_ret_t(* fr_cmp_t)(void const *a, void const *b)
Definition misc.h:57
fr_cmp_ret_t
Result of an ordering comparison.
Definition misc.h:50
@ CMP_LT
a < b
Definition misc.h:52
@ CMP_ERR
comparison failed
Definition misc.h:51
@ CMP_EQ
a == b
Definition misc.h:53
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
Definition rb.c:807
int fr_rb_remove(void **removed, fr_rb_tree_t *tree, void const *data)
Remove an entry from the tree, without freeing the data.
Definition rb.c:718
int fr_rb_find(void **found, fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition rb.c:586
void * fr_rb_iter_init_inorder(fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Initialise an in-order iterator.
Definition rb.c:850
static fr_rb_node_t sentinel
Definition rb.c:30
#define RB_MAGIC
Definition rb.c:33
#define NIL
Definition rb.c:29
void fr_rb_iter_delete_inorder(fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Remove the current node from the tree.
Definition rb.c:925
int fr_rb_find_or_insert(void **found, fr_rb_tree_t *tree, void const *data)
Attempt to find current data in the tree, if it does not exist insert it.
Definition rb.c:608
void * fr_rb_iter_init_preorder(fr_rb_tree_t *tree, fr_rb_iter_preorder_t *iter)
Initialise a pre-order iterator.
Definition rb.c:944
#define DEF_RB_FLATTEN_FUNC(_order)
Definition rb.c:1098
static int insert_node(fr_rb_node_t **existing, fr_rb_tree_t *tree, void *data))
Insert an element into the tree.
Definition rb.c:345
static int find_node(fr_rb_node_t **found, fr_rb_tree_t const *tree, void const *data)
Definition rb.c:550
static void delete_fixup(fr_rb_tree_t *tree, fr_rb_node_t *x, fr_rb_node_t *parent)
Maintain RED-BLACK tree balance after deleting node x.
Definition rb.c:410
static void delete_internal(fr_rb_tree_t *tree, fr_rb_node_t *z, bool free_data)
Delete an element (z) from the tree.
Definition rb.c:475
int fr_rb_replace(void **old, fr_rb_tree_t *tree, void const *data)
Replace old data with new data, OR insert if there is no old.
Definition rb.c:656
static fr_rb_node_t * _node_inline_alloc(fr_rb_tree_t const *tree, void *data)
Return the fr_rb_node_t that was allocated as part of the data structure.
Definition rb.c:48
static fr_rb_node_t * _node_heap_alloc(fr_rb_tree_t const *tree, UNUSED void *data)
Allocate a new fr_rb_node_t on the heap.
Definition rb.c:66
fr_rb_tree_t * _fr_rb_alloc(TALLOC_CTX *ctx, ssize_t offset, char const *type, fr_cmp_t data_cmp, fr_free_t data_free)
Alloc a new RED-BLACK tree.
Definition rb.c:202
static void free_walker(fr_rb_tree_t *tree, fr_rb_node_t *x)
Walks the tree to delete all nodes Does NOT re-balance it!
Definition rb.c:82
static void node_data_free(fr_rb_tree_t const *tree, fr_rb_node_t *node)
Definition rb.c:38
void * fr_rb_iter_next_postorder(UNUSED fr_rb_tree_t *tree, fr_rb_iter_postorder_t *iter)
Return the next node.
Definition rb.c:1052
static int _tree_free(fr_rb_tree_t *tree)
Free the rbtree cleaning up any nodes.
Definition rb.c:104
void * fr_rb_remove_by_inline_node(fr_rb_tree_t *tree, fr_rb_node_t *node)
Remove an entry from the tree, using the node structure, without freeing the data.
Definition rb.c:748
bool fr_rb_delete_by_inline_node(fr_rb_tree_t *tree, fr_rb_node_t *node)
Remove node and free data (if a free function was specified)
Definition rb.c:793
void * fr_rb_iter_next_preorder(UNUSED fr_rb_tree_t *tree, fr_rb_iter_preorder_t *iter)
Return the next node.
Definition rb.c:968
int _fr_rb_init(fr_rb_tree_t *tree, TALLOC_CTX *node_ctx, ssize_t offset, char const *type, fr_cmp_t data_cmp, fr_free_t data_free)
Initialise a new RED-BLACK tree.
Definition rb.c:147
void * fr_rb_first(fr_rb_tree_t *tree)
Definition rb.c:812
int fr_rb_delete(fr_rb_tree_t *tree, void const *data)
Remove node and free data (if a free function was specified)
Definition rb.c:767
void * fr_rb_last(fr_rb_tree_t *tree)
Definition rb.c:827
void * fr_rb_iter_init_postorder(fr_rb_tree_t *tree, fr_rb_iter_postorder_t *iter)
Initialise a post-order iterator.
Definition rb.c:1021
static void rotate_right(fr_rb_tree_t *tree, fr_rb_node_t *x)
Rotate Node x to right.
Definition rb.c:253
static void _node_heap_free(fr_rb_tree_t const *tree, fr_rb_node_t *node, bool free_data)
Clear the fr_rb_node_t that was allocated as part of the data structure.
Definition rb.c:73
static void _node_inline_free(fr_rb_tree_t const *tree, fr_rb_node_t *node, bool free_data)
Clear the fr_rb_node_t that was allocated as part of the data structure.
Definition rb.c:55
int fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:637
static void rotate_left(fr_rb_tree_t *tree, fr_rb_node_t *x)
Rotate Node x to left.
Definition rb.c:224
void * fr_rb_iter_next_inorder(UNUSED fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Return the next node.
Definition rb.c:876
static void insert_fixup(fr_rb_tree_t *tree, fr_rb_node_t *x)
Maintain red-black tree balance after inserting node x.
Definition rb.c:281
fr_rb_node_t * node
current node
Definition rb.h:353
fr_rb_node_t * parent
Parent.
Definition rb.h:43
struct fr_rb_node_s fr_rb_node_t
Definition rb.h:39
fr_rb_node_t * left
Left child.
Definition rb.h:41
bool being_freed
Disable frees if we're currently calling a free function.
Definition rb.h:47
struct fr_rb_tree_s fr_rb_tree_t
Definition rb.h:51
fr_rb_node_t * next
if non-NULL, next node cached by fr_rb_iter_delete()
Definition rb.h:321
fr_rb_node_t * node
current node
Definition rb.h:338
fr_cmp_t data_cmp
Callback to compare node data.
Definition rb.h:82
@ BLACK
Definition rb.h:35
@ RED
Definition rb.h:36
TALLOC_CTX * node_ctx
Talloc ctx to allocate nodes in.
Definition rb.h:78
fr_rb_node_t * right
Right child.
Definition rb.h:42
char const * type
Talloc type to check elements against.
Definition rb.h:80
static bool fr_rb_node_inline_in_tree(fr_rb_node_t const *node)
Check to see if an item is in a tree by examining its inline fr_rb_node_t.
Definition rb.h:312
void * data
data stored in node
Definition rb.h:44
uint32_t num_elements
How many elements are inside the tree.
Definition rb.h:95
fr_free_t data_free
Callback to free node data.
Definition rb.h:83
uint32_t magic
Definition rb.h:73
uint16_t offset
Where's the fr_rb_node_t is located in the structure being inserted.
Definition rb.h:92
rb_node_free_t node_free
Callback to free a node.
Definition rb.h:86
bool being_freed
Prevent double frees in talloc_destructor.
Definition rb.h:94
fr_rb_node_t * node
current node–set to NULL (not NIL) by fr_rb_iter_delete()
Definition rb.h:320
fr_rb_colour_t colour
Node colour (BLACK, RED)
Definition rb.h:46
rb_node_alloc_t node_alloc
Callback to allocate a new node.
Definition rb.h:85
fr_rb_node_t * root
Root of the rbtree.
Definition rb.h:76
Iterator structure for in-order traversal of an rbtree.
Definition rb.h:319
Iterator structure for post-order traversal of an rbtree.
Definition rb.h:352
Iterator structure for pre-order traversal of an rbtree.
Definition rb.h:337
The main red black tree structure.
Definition rb.h:71
static fr_cmp_ret_t data_cmp(const void *one, const void *two)
Definition rlm_stats.c:347
fr_aka_sim_id_type_t type
static fr_slen_t parent
Definition pair.h:858
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
static fr_slen_t data
Definition value.h:1340
int nonnull(2, 5))