The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: d9e119053b47df9c6ea5033a4cbc6046ed34f808 $")
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#ifndef NDEBUG
118 tree->magic = 0;
119#endif
120 tree->root = NIL;
121 tree->num_elements = 0;
122
123 /*
124 * Ensure all dependents on the tree run their
125 * destructors. The tree at this point should
126 * and any tree operations should be empty.
127 */
128 talloc_free_children(tree);
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{
348
349 if (unlikely(tree->being_freed)) return -1;
350
351#ifndef TALLOC_GET_TYPE_ABORT_NOOP
352 if (tree->type) (void)_talloc_get_type_abort(data, tree->type, __location__);
353#endif
354
355 /* find where node belongs */
356 current = tree->root;
357 parent = NIL;
358 while (current != NIL) {
359 int result;
360
361 /*
362 * See if two entries are identical.
363 */
364 result = tree->data_cmp(data, current->data);
365 if (result == 0) {
366 if (existing) *existing = current;
367 return 1;
368 }
369
370 parent = current;
371 current = (result < 0) ? current->left : current->right;
372 }
373
374 /* setup new node */
375 x = tree->node_alloc(tree, data);
376 if (unlikely(!x)) return -1;
377
378 *x = (fr_rb_node_t){
379 .data = data,
380 .parent = parent,
381 .left = NIL,
382 .right = NIL,
383 .colour = RED
384 };
385
386 /* insert node in tree */
387 if (parent != NIL) {
388 if (tree->data_cmp(data, parent->data) <= 0) {
389 parent->left = x;
390 } else {
391 parent->right = x;
392 }
393 } else {
394 tree->root = x;
395 }
396
397 insert_fixup(tree, x);
398
399 tree->num_elements++;
400
401 return 0;
402}
403
404/** Maintain RED-BLACK tree balance after deleting node x
405 *
406 */
408{
409 while (x != tree->root && x->colour == BLACK) {
410 if (x == parent->left) {
412 if (w->colour == RED) {
413 w->colour = BLACK;
414 parent->colour = RED; /* parent != NIL? */
415 rotate_left(tree, parent);
416 w = parent->right;
417 }
418 if ((w->left->colour == BLACK) && (w->right->colour == BLACK)) {
419 if (w != NIL) w->colour = RED;
420 x = parent;
421 parent = x->parent;
422 } else {
423 if (w->right->colour == BLACK) {
424 if (w->left != NIL) w->left->colour = BLACK;
425 w->colour = RED;
426 rotate_right(tree, w);
427 w = parent->right;
428 }
429 w->colour = parent->colour;
430 if (parent != NIL) parent->colour = BLACK;
431 if (w->right->colour != BLACK) {
432 w->right->colour = BLACK;
433 }
434 rotate_left(tree, parent);
435 x = tree->root;
436 }
437 } else {
439 if (w->colour == RED) {
440 w->colour = BLACK;
441 parent->colour = RED; /* parent != NIL? */
442 rotate_right(tree, parent);
443 w = parent->left;
444 }
445 if ((w->right->colour == BLACK) && (w->left->colour == BLACK)) {
446 if (w != NIL) w->colour = RED;
447 x = parent;
448 parent = x->parent;
449 } else {
450 if (w->left->colour == BLACK) {
451 if (w->right != NIL) w->right->colour = BLACK;
452 w->colour = RED;
453 rotate_left(tree, w);
454 w = parent->left;
455 }
456 w->colour = parent->colour;
457 if (parent != NIL) parent->colour = BLACK;
458 if (w->left->colour != BLACK) {
459 w->left->colour = BLACK;
460 }
461 rotate_right(tree, parent);
462 x = tree->root;
463 }
464 }
465 }
466 if (x != NIL) x->colour = BLACK; /* Avoid cache-dirty on NIL */
467}
468
469/** Delete an element (z) from the tree
470 *
471 */
472static void delete_internal(fr_rb_tree_t *tree, fr_rb_node_t *z, bool free_data)
473{
474 fr_rb_node_t *x, *y;
476
477 if (!z || z == NIL) return;
478
479 if (z->left == NIL || z->right == NIL) {
480 /* y has a NIL node as a child */
481 y = z;
482 } else {
483 /* find tree successor with a NIL node as a child */
484 y = z->right;
485 while (y->left != NIL) y = y->left;
486 }
487
488 /* x is y's only child */
489 if (y->left != NIL) {
490 x = y->left;
491 } else {
492 x = y->right; /* may be NIL! */
493 }
494
495 /* remove y from the parent chain */
496 parent = y->parent;
497 if (x != NIL) x->parent = parent;
498
499 if (parent != NIL) {
500 if (y == parent->left) {
501 parent->left = x;
502 } else {
503 parent->right = x;
504 }
505 } else {
506 tree->root = x;
507 }
508
509 if (y != z) {
510 void *y_data = y->data;
511
512 if ((y->colour == BLACK) && parent) delete_fixup(tree, x, parent);
513
514 /*
515 * The user structure in y->data May include a
516 * pointer to y. In that case, we CANNOT delete
517 * y. Instead, we copy z (which is now in the
518 * tree) to y, and fix up the parent/child
519 * pointers.
520 */
521 memcpy(y, z, sizeof(*y));
522 y->data = y_data;
523
524 if (y->parent == NIL) {
525 tree->root = y;
526 } else {
527 if (y->parent->left == z) y->parent->left = y;
528 if (y->parent->right == z) y->parent->right = y;
529 }
530 if (y->left->parent == z) y->left->parent = y;
531 if (y->right->parent == z) y->right->parent = y;
532
533 tree->node_free(tree, z, free_data);
534 } else {
535 if (y->colour == BLACK) delete_fixup(tree, x, parent);
536
537 tree->node_free(tree, y, free_data);
538 }
539
540 tree->num_elements--;
541}
542
543
544/* Find user data, returning the node
545 *
546 */
547static inline CC_HINT(always_inline) fr_rb_node_t *find_node(fr_rb_tree_t const *tree, void const *data)
548{
550
551 if (unlikely(tree->being_freed)) return NULL;
552
553 current = tree->root;
554
555 while (current != NIL) {
556 int result = tree->data_cmp(data, current->data);
557
558 if (result == 0) return current;
559
560 current = (result < 0) ? current->left : current->right;
561 }
562
563 return NULL;
564}
565
566/** Find an element in the tree, returning the data, not the node
567 *
568 * @param[in] tree to search in.
569 * @param[in] data to find.
570 * @return
571 * - User data matching the data passed in.
572 * - NULL if nothing matched passed data.
573 *
574 * @hidecallergraph
575 */
576CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
577void *fr_rb_find(fr_rb_tree_t const *tree, void const *data)
578{
579 fr_rb_node_t *x;
580
581 if (unlikely(tree->being_freed)) return NULL;
582 x = find_node(tree, data);
583 if (!x) return NULL;
584
585 return x->data;
586}
587
588/** Attempt to find current data in the tree, if it does not exist insert it
589 *
590 * @param[out] found Pre-existing data we found.
591 * @param[in] tree to search/insert into.
592 * @param[in] data to find.
593 * @return
594 * - 1 if existing data was found, found will be populated.
595 * - 0 if no existing data was found.
596 * - -1 on insert error.
597 */
598int fr_rb_find_or_insert(void **found, fr_rb_tree_t *tree, void const *data)
599{
600 fr_rb_node_t *existing;
601
602 switch (insert_node(&existing, tree, UNCONST(void *, data))) {
603 case 1:
604 if (found) *found = existing->data;
605 return 1;
606
607 case 0:
608 if (found) *found = NULL;
609 return 0;
610
611 default:
612 if (found) *found = NULL;
613 return -1;
614 }
615}
616
617/** Insert data into a tree
618 *
619 * @param[in] tree to insert data into.
620 * @param[in] data to insert.
621 * @return
622 * - true if data was inserted.
623 * - false if data already existed and was not inserted.
624 */
625CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
627{
628 if (insert_node(NULL, tree, UNCONST(void *, data)) == 0) return true;
629
630 return false;
631}
632
633/** Replace old data with new data, OR insert if there is no old
634 *
635 * @param[out] old data that was replaced. If this argument
636 * is not NULL, then the old data will not
637 * be freed, even if a free function is
638 * configured.
639 * @param[in] tree to insert data into.
640 * @param[in] data to replace.
641 * @return
642 * - 1 if data was replaced.
643 * - 0 if data was inserted.
644 * - -1 if we failed to replace data
645 */
646CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
647int fr_rb_replace(void **old, fr_rb_tree_t *tree, void const *data)
648{
649 fr_rb_node_t *node;
650
651 switch (insert_node(&node, tree, UNCONST(void *, data))) {
652 case 1: /* Something exists */
653 {
654 void *old_data = node->data;
655
656 /*
657 * If the fr_node_t is inline with the
658 * data structure, we need to delete
659 * the old node out of the tree, and
660 * perform a normal insert operation.
661 */
662 if (tree->node_alloc == _node_inline_alloc) {
663 delete_internal(tree, node, false);
664 insert_node(NULL, tree, UNCONST(void *, data));
665 } else {
666 node->data = UNCONST(void *, data);
667 }
668
669 if (old) {
670 *old = old_data;
671 } else if (tree->data_free) {
672 tree->data_free(old_data);
673 }
674 return 1;
675 }
676 case 0: /* New node was inserted - There was no pre-existing node */
677 if (old) *old = NULL;
678 return 0;
679
680 default:
681 if (old) *old = NULL;
682 return -1;
683 }
684}
685
686/** Remove an entry from the tree, without freeing the data
687 *
688 * @param[in] tree to remove data from.
689 * @param[in] data to remove.
690 * @return
691 * - The user data we removed.
692 * - NULL if we couldn't find any matching data.
693 */
694CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
695void *fr_rb_remove(fr_rb_tree_t *tree, void const *data)
696{
697 fr_rb_node_t *node;
698 void *node_data;
699
700 if (unlikely(tree->being_freed)) return false;
701 node = find_node(tree, data);
702 if (!node) return NULL;
703
704 if (unlikely(node->being_freed)) return node->data;
705
706 node_data = node->data;
707 delete_internal(tree, node, false); /* nullified node->data */
708 return node_data;
709}
710
711/** Remove an entry from the tree, using the node structure, without freeing the data
712 *
713 * This function can help where multiple items may be in the
714 * tree with the same comparator value.
715 *
716 * @param[in] tree to remove data from.
717 * @param[in] node to remove.
718 * @return
719 * - The user data we removed.
720 * - NULL if the user data wasn't in the tree.
721 */
723{
724 void *node_data;
725
726 if (!fr_rb_node_inline_in_tree(node)) return NULL;
727 node_data = node->data;
728 delete_internal(tree, node, false); /* nullified node->data */
729 return node_data;
730}
731
732/** Remove node and free data (if a free function was specified)
733 *
734 * @param[in] tree to remove data from.
735 * @param[in] data to remove/free.
736 * @return
737 * - true if we removed data.
738 * - false if we couldn't find any matching data.
739 */
740CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
742{
743 fr_rb_node_t *node;
744
745 if (unlikely(tree->being_freed)) return false;
746 node = find_node(tree, data);
747 if (!node) return false;
748
749 if (unlikely(node->being_freed)) return true;
750
751 delete_internal(tree, node, true);
752
753 return true;
754}
755
756/** Remove node and free data (if a free function was specified)
757 *
758 * This function can help where multiple items may be in the
759 * tree with the same comparator value.
760 *
761 * @param[in] tree to remove data from.
762 * @param[in] node to remove/free.
763 * @return
764 * - true if we removed data.
765 * - false if we couldn't find any matching data.
766 */
768{
769 if (!fr_rb_node_inline_in_tree(node)) return false;
770
771 delete_internal(tree, node, true);
772
773 return true;
774}
775
776/** Return how many nodes there are in a tree
777 *
778 * @param[in] tree to return node count for.
779 */
780CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
782{
783 return tree->num_elements;
784}
785
787{
788 fr_rb_node_t *x = tree->root;
789
790 if (x == NIL) return NULL;
791
792 /*
793 * First node is the leftmost
794 */
795 while (x->left != NIL) x = x->left;
796
797 return x->data;
798}
799
800
802{
803 fr_rb_node_t *x = tree->root;
804
805 if (x == NIL) return NULL;
806
807 /*
808 * Last node is the rightmost
809 */
810 while (x->right != NIL) x = x->right;
811
812 return x->data;
813}
814
815
816/** Initialise an in-order iterator
817 *
818 * @param[out] iter to initialise.
819 * @param[in] tree to iterate over.
820 * @return
821 * - The first node. Mutex will be held.
822 * - NULL if the tree is empty.
823 */
825{
826 fr_rb_node_t *x = tree->root;
827
828 if (x == NIL) return NULL;
829
830 /*
831 * First node is the leftmost
832 */
833 while (x->left != NIL) x = x->left;
834
835 *iter = (fr_rb_iter_inorder_t){
836 .tree = tree,
837 .node = x
838 };
839
840 return x->data;
841}
842
843/** Return the next node
844 *
845 * @param[in] iter previously initialised with #fr_rb_iter_init_inorder
846 * @return
847 * - The next node.
848 * - NULL if no more nodes remain.
849 */
851{
852 fr_rb_node_t *x = iter->node, *y;
853
854 /*
855 * Catch callers repeatedly calling iterator
856 * at the end.
857 */
858 if (unlikely(iter->node == NIL)) return NULL;
859
860 /*
861 * fr_rb_iter_delete() has already deleted this node,
862 * and saved the next one for us. (We check for NULL;
863 * NIL just means we're at the end.)
864 */
865 if (!iter->node) {
866 iter->node = iter->next;
867 iter->next = NULL;
868 return iter->node->data;
869 }
870
871 if (x->right != NIL) {
872 x = x->right;
873
874 while (x->left != NIL) x = x->left;
875 iter->node = x;
876
877 return x->data;
878 }
879
880 y = x;
881 x = x->parent;
882 while ((x != NIL) && (y == x->right)) {
883 y = x;
884 x = x->parent;
885 }
886
887 iter->node = x;
888
889 return x->data;
890}
891
892/** Remove the current node from the tree
893 *
894 * @note Only makes sense for in-order traversals.
895 *
896 * @param[in] iter previously initialised with #fr_rb_iter_init_inorder
897 */
899{
900 fr_rb_node_t *x = iter->node;
901
902 if (unlikely(x == NIL)) return;
903 (void) fr_rb_iter_next_inorder(iter);
904 iter->next = iter->node;
905 iter->node = NULL;
906 delete_internal(iter->tree, x, true);
907}
908
909/** Initialise a pre-order iterator
910 *
911 * @param[out] iter to initialise.
912 * @param[in] tree to iterate over.
913 * @return
914 * - The first node. Mutex will be held.
915 * - NULL if the tree is empty.
916 */
918{
919 fr_rb_node_t *x = tree->root;
920
921 if (x == NIL) return NULL;
922
923 /*
924 * First, the root.
925 */
926 *iter = (fr_rb_iter_preorder_t){
927 .tree = tree,
928 .node = x
929 };
930
931 return x->data;
932}
933
934/** Return the next node
935 *
936 * @param[in] iter previously initialised with #fr_rb_iter_init_preorder
937 * @return
938 * - The next node.
939 * - NULL if no more nodes remain.
940 */
942{
943 fr_rb_node_t *x = iter->node, *y;
944
945 /*
946 * Catch callers repeatedly calling iterator
947 * at the end.
948 */
949 if (unlikely(iter->node == NIL)) return NULL;
950
951 /*
952 * Next is a child of the just-returned node, if it has one.
953 * (Left child first.)
954 */
955 if (x->left != NIL) {
956 x = x->left;
957 iter->node = x;
958 return x->data;
959 }
960 if (x->right != NIL) {
961 x = x->right;
962 iter->node = x;
963 return x->data;
964 }
965
966 /*
967 * Otherwise, the nearest ancestor's unreturned right
968 * child, if one exists.
969 */
970 for (; (y = x->parent) != NIL; x = y) {
971 if (y->right != NIL && y->right != x) {
972 x = y->right;
973 iter->node = x;
974 return x->data;
975 }
976 }
977
978 /*
979 * None of the above? We're done.
980 */
981 iter->node = NIL;
982
983 return NULL;
984}
985
986/** Initialise a post-order iterator
987 *
988 * @param[out] iter to initialise.
989 * @param[in] tree to iterate over.
990 * @return
991 * - The first node.
992 * - NULL if the tree is empty.
993 */
995{
996 fr_rb_node_t *x = tree->root;
997
998 if (x == NIL) return NULL;
999
1000 /*
1001 * First: the deepest leaf to the left (jogging to the
1002 * right if there's a right child but no left).
1003 */
1004 for (;;) {
1005 for (; x->left != NIL; x = x->left) ;
1006 if (x->right == NIL) break;
1007 x = x->right;
1008 }
1009
1010 *iter = (fr_rb_iter_postorder_t){
1011 .tree = tree,
1012 .node = x
1013 };
1014
1015 return x->data;
1016}
1017
1018/** Return the next node
1019 *
1020 * @param[in] iter previously initialised with #fr_rb_iter_init_postorder
1021 * @return
1022 * - The next node.
1023 * - NULL if no more nodes remain.
1024 */
1026{
1027 fr_rb_node_t *x = iter->node, *y;
1028
1029 /*
1030 * Catch callers repeatedly calling iterator
1031 * at the end.
1032 */
1033 if (unlikely(iter->node == NIL)) return NULL;
1034
1035 /*
1036 * This is postorder, so a just-returned node's
1037 * descendants have all been returned. If there
1038 * is another node, it's an ancestor or one of
1039 * its not-yet-returned descendants...but if
1040 * we're at the root, we're done.
1041 */
1042 y = x->parent;
1043 if (y == NIL) {
1044 iter->node = NIL;
1045 return NULL;
1046 }
1047
1048 /*
1049 * Return the parent if it has no right child, or it has one but
1050 * it's been returned.
1051 */
1052 if (y->right == NIL || y->right == x) {
1053 iter->node = y;
1054 return y->data;
1055 }
1056
1057 /*
1058 * Otherwise, it's as if we're starting over with the right child.
1059 */
1060 x = y->right;
1061 for (;;) {
1062 for (; x->left != NIL; x = x->left) ;
1063 if (x->right == NIL) break;
1064 x = x->right;
1065 }
1066
1067 iter->node = x;
1068 return x->data;
1069}
1070
1071#define DEF_RB_FLATTEN_FUNC(_order) \
1072int fr_rb_flatten_##_order(TALLOC_CTX *ctx, void **out[], fr_rb_tree_t *tree) \
1073{ \
1074 uint32_t num = fr_rb_num_elements(tree), i; \
1075 fr_rb_iter_##_order##_t iter; \
1076 void *item, **list; \
1077 if (unlikely(!(list = talloc_array(ctx, void *, num)))) return -1; \
1078 for (item = fr_rb_iter_init_##_order(&iter, tree), i = 0; \
1079 item; \
1080 item = fr_rb_iter_next_##_order(&iter), i++) list[i] = item; \
1081 *out = list; \
1082 return 0; \
1083}
1084DEF_RB_FLATTEN_FUNC(preorder)
1085DEF_RB_FLATTEN_FUNC(postorder)
1086DEF_RB_FLATTEN_FUNC(inorder)
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:167
#define RCSID(id)
Definition build.h:483
#define CC_NO_UBSAN(_sanitize)
Definition build.h:426
#define unlikely(_x)
Definition build.h:381
#define UNUSED
Definition build.h:315
talloc_free(reap)
unsigned short uint16_t
unsigned int uint32_t
long int ssize_t
unsigned char bool
int8_t(* fr_cmp_t)(void const *a, void const *b)
Definition misc.h:38
void(* fr_free_t)(void *)
Definition misc.h:39
static rc_request_t * current
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
Definition rb.c:781
void * fr_rb_iter_init_inorder(fr_rb_iter_inorder_t *iter, fr_rb_tree_t *tree)
Initialise an in-order iterator.
Definition rb.c:824
static fr_rb_node_t sentinel
Definition rb.c:30
#define RB_MAGIC
Definition rb.c:33
#define NIL
Definition rb.c:29
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:598
void fr_rb_iter_delete_inorder(fr_rb_iter_inorder_t *iter)
Remove the current node from the tree.
Definition rb.c:898
void * fr_rb_remove(fr_rb_tree_t *tree, void const *data)
Remove an entry from the tree, without freeing the data.
Definition rb.c:695
#define DEF_RB_FLATTEN_FUNC(_order)
Definition rb.c:1071
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 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:407
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:472
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:647
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
void * fr_rb_iter_next_preorder(fr_rb_iter_preorder_t *iter)
Return the next node.
Definition rb.c:941
void * fr_rb_iter_init_preorder(fr_rb_iter_preorder_t *iter, fr_rb_tree_t *tree)
Initialise a pre-order iterator.
Definition rb.c:917
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
void * fr_rb_iter_init_postorder(fr_rb_iter_postorder_t *iter, fr_rb_tree_t *tree)
Initialise a post-order iterator.
Definition rb.c:994
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_inorder(fr_rb_iter_inorder_t *iter)
Return the next node.
Definition rb.c:850
static int _tree_free(fr_rb_tree_t *tree)
Free the rbtree cleaning up any nodes.
Definition rb.c:104
void * fr_rb_iter_next_postorder(fr_rb_iter_postorder_t *iter)
Return the next node.
Definition rb.c:1025
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:722
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:767
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition rb.c:577
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:786
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:626
void * fr_rb_last(fr_rb_tree_t *tree)
Definition rb.c:801
static fr_rb_node_t * find_node(fr_rb_tree_t const *tree, void const *data)
Definition rb.c:547
bool 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:741
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
static void rotate_left(fr_rb_tree_t *tree, fr_rb_node_t *x)
Rotate Node x to left.
Definition rb.c:224
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_tree_t * tree
Tree being iterated over.
Definition rb.h:322
fr_rb_node_t * node
current node
Definition rb.h:358
fr_rb_node_t * parent
Parent.
Definition rb.h:45
struct fr_rb_node_s fr_rb_node_t
Definition rb.h:41
fr_rb_node_t * left
Left child.
Definition rb.h:43
bool being_freed
Disable frees if we're currently calling a free function.
Definition rb.h:49
struct fr_rb_tree_s fr_rb_tree_t
Definition rb.h:53
fr_rb_node_t * next
if non-NULL, next node cached by fr_rb_iter_delete()
Definition rb.h:324
fr_rb_node_t * node
current node
Definition rb.h:342
fr_cmp_t data_cmp
Callback to compare node data.
Definition rb.h:84
@ BLACK
Definition rb.h:37
@ RED
Definition rb.h:38
TALLOC_CTX * node_ctx
Talloc ctx to allocate nodes in.
Definition rb.h:80
fr_rb_node_t * right
Right child.
Definition rb.h:44
char const * type
Talloc type to check elements against.
Definition rb.h:82
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:314
void * data
data stored in node
Definition rb.h:46
uint32_t num_elements
How many elements are inside the tree.
Definition rb.h:97
fr_free_t data_free
Callback to free node data.
Definition rb.h:85
uint32_t magic
Definition rb.h:75
fr_rb_tree_t * tree
Tree being iterated over.
Definition rb.h:341
uint16_t offset
Where's the fr_rb_node_t is located in the structure being inserted.
Definition rb.h:94
rb_node_free_t node_free
Callback to free a node.
Definition rb.h:88
bool being_freed
Prevent double frees in talloc_destructor.
Definition rb.h:96
fr_rb_tree_t * tree
Tree being iterated over.
Definition rb.h:357
fr_rb_node_t * node
current nodeā€“set to NULL (not NIL) by fr_rb_iter_delete()
Definition rb.h:323
fr_rb_colour_t colour
Node colour (BLACK, RED)
Definition rb.h:48
rb_node_alloc_t node_alloc
Callback to allocate a new node.
Definition rb.h:87
fr_rb_node_t * root
Root of the rbtree.
Definition rb.h:78
Iterator structure for in-order traversal of an rbtree.
Definition rb.h:321
Iterator structure for post-order traversal of an rbtree.
Definition rb.h:356
Iterator structure for pre-order traversal of an rbtree.
Definition rb.h:340
The main red black tree structure.
Definition rb.h:73
static int8_t data_cmp(const void *one, const void *two)
Definition rlm_stats.c:355
fr_aka_sim_id_type_t type
static fr_slen_t parent
Definition pair.h:851
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
static fr_slen_t data
Definition value.h:1265
int nonnull(2, 5))