Blender V4.3
DLRB_tree.c
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009 Blender Authors, Joshua Leung. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "MEM_guardedalloc.h"
10
11#include "BLI_dlrbTree.h"
12#include "BLI_listbase.h"
13
14/* *********************************************** */
15/* Tree API */
16
18{
19 /* just allocate for now */
20 return MEM_callocN(sizeof(DLRBT_Tree), "DLRBT_Tree");
21}
22
24{
25 if (tree == NULL) {
26 return;
27 }
28
29 tree->first = tree->last = tree->root = NULL;
30}
31
32/* Helper for traversing tree and freeing sub-nodes */
34{
35 /* sanity check */
36 if (node == NULL) {
37 return;
38 }
39
40 /* free child nodes + subtrees */
41 recursive_tree_free_nodes(node->left, free_cb);
42 recursive_tree_free_nodes(node->right, free_cb);
43
44 /* free self */
45 if (free_cb) {
46 free_cb(node);
47 }
48}
49
51{
52 if (tree == NULL) {
53 return;
54 }
55
56 /* if the list-base stuff is set, just use that (and assume its set),
57 * otherwise, we'll need to traverse the tree...
58 */
59 if (tree->first) {
60 /* free list */
61 if (free_cb) {
63 free_cb(node);
64 }
66 }
67 else {
69 }
70 }
71 else {
72 /* traverse tree, freeing sub-nodes */
73 recursive_tree_free_nodes(tree->root, free_cb);
74 }
75
76 /* clear pointers */
77 tree->first = tree->last = tree->root = NULL;
78}
79
80/* ------- */
81
82/* Helper function - used for traversing down the tree from the root to add nodes in order */
84{
85 /* sanity checks */
86 if ((tree == NULL) || (node == NULL)) {
87 return;
88 }
89
90 /* add left-node (and its subtree) */
91 linkedlist_sync_add_node(tree, node->left);
92
93 /* now add self
94 * - must remove detach from other links first
95 * (for now, only clear own pointers)
96 */
97 node->prev = node->next = NULL;
98 BLI_addtail((ListBase *)tree, (Link *)node);
99
100 /* finally, add right node (and its subtree) */
101 linkedlist_sync_add_node(tree, node->right);
102}
103
105{
106 /* sanity checks */
107 if (tree == NULL) {
108 return;
109 }
110
111 /* clear list-base pointers so that the new list can be added properly */
112 tree->first = tree->last = NULL;
113
114 /* start adding items from the root */
116}
117
118/* *********************************************** */
119/* Tree Search Utilities */
120
122 DLRBT_Comparator_FP cmp_cb,
123 void *search_data)
124{
125 DLRBT_Node *node = (tree) ? tree->root : NULL;
126 short found = 0;
127
128 /* check that there is a comparator to use */
129 /* TODO: if no comparator is supplied, try using the one supplied with the tree... */
130 if (cmp_cb == NULL) {
131 return NULL;
132 }
133
134 /* iteratively perform this search */
135 while (node && found == 0) {
136 /* check if traverse further or not
137 * NOTE: it is assumed that the values will be unit values only
138 */
139 switch (cmp_cb(node, search_data)) {
140 case -1: /* data less than node */
141 if (node->left) {
142 node = node->left;
143 }
144 else {
145 found = 1;
146 }
147 break;
148
149 case 1: /* data greater than node */
150 if (node->right) {
151 node = node->right;
152 }
153 else {
154 found = 1;
155 }
156 break;
157
158 default: /* data equals node */
159 found = 1;
160 break;
161 }
162 }
163
164 /* return the nearest matching node */
165 return node;
166}
167
169 DLRBT_Comparator_FP cmp_cb,
170 void *search_data)
171{
172 DLRBT_Node *node = (tree) ? tree->root : NULL;
173 short found = 0;
174
175 /* check that there is a comparator to use */
176 /* TODO: if no comparator is supplied, try using the one supplied with the tree... */
177 if (cmp_cb == NULL) {
178 return NULL;
179 }
180
181 /* iteratively perform this search */
182 while (node && found == 0) {
183 /* check if traverse further or not
184 * NOTE: it is assumed that the values will be unit values only
185 */
186 switch (cmp_cb(node, search_data)) {
187 case -1: /* data less than node */
188 if (node->left) {
189 node = node->left;
190 }
191 else {
192 found = -1;
193 }
194 break;
195
196 case 1: /* data greater than node */
197 if (node->right) {
198 node = node->right;
199 }
200 else {
201 found = -1;
202 }
203 break;
204
205 default: /* data equals node */
206 found = 1;
207 break;
208 }
209 }
210
211 /* return the exactly matching node */
212 return (found == 1) ? (node) : (NULL);
213}
214
216 DLRBT_Comparator_FP cmp_cb,
217 void *search_data)
218{
220
221 /* check that there is a comparator to use */
222 /* TODO: if no comparator is supplied, try using the one supplied with the tree... */
223 if (cmp_cb == NULL) {
224 return NULL;
225 }
226
227 /* get the node which best matches this description */
228 node = BLI_dlrbTree_search(tree, cmp_cb, search_data);
229
230 if (node) {
231 /* if the item we're searching for is greater than the node found, we've found the match */
232 if (cmp_cb(node, search_data) > 0) {
233 return node;
234 }
235
236 /* return the previous node otherwise */
237 /* NOTE: what happens if there is no previous node? */
238 return node->prev;
239 }
240
241 /* nothing matching was found */
242 return NULL;
243}
244
246 DLRBT_Comparator_FP cmp_cb,
247 void *search_data)
248{
250
251 /* check that there is a comparator to use */
252 /* TODO: if no comparator is supplied, try using the one supplied with the tree... */
253 if (cmp_cb == NULL) {
254 return NULL;
255 }
256
257 /* get the node which best matches this description */
258 node = BLI_dlrbTree_search(tree, cmp_cb, search_data);
259
260 if (node) {
261 /* if the item we're searching for is less than the node found, we've found the match */
262 if (cmp_cb(node, search_data) < 0) {
263 return node;
264 }
265
266 /* return the previous node otherwise */
267 /* NOTE: what happens if there is no previous node? */
268 return node->next;
269 }
270
271 /* nothing matching was found */
272 return NULL;
273}
274
276{
277 /* check if an exact search throws up anything... */
278 return (BLI_dlrbTree_search_exact(tree, cmp_cb, search_data) != NULL);
279}
280
281/* *********************************************** */
282/* Tree Relationships Utilities */
283
284/* get the 'grandparent' - the parent of the parent - of the given node */
286{
287 if (node && node->parent) {
288 return node->parent->parent;
289 }
290 return NULL;
291}
292
293/* get the sibling node (e.g. if node is left child of parent, return right child of parent) */
295{
296 if (node && node->parent) {
297 if (node == node->parent->left) {
298 return node->parent->right;
299 }
300 return node->parent->left;
301 }
302
303 /* sibling not found */
304 return NULL;
305}
306
307/* get the 'uncle' - the sibling of the parent - of the given node */
309{
310 if (node) {
311 /* return the child of the grandparent which isn't the node's parent */
312 return get_sibling(node->parent);
313 }
314
315 /* uncle not found */
316 return NULL;
317}
318
319/* *********************************************** */
320/* Tree Rotation Utilities */
321
322/* make right child of 'root' the new root */
324{
325 DLRBT_Node **root_slot, *pivot;
326
327 /* pivot is simply the root's right child, to become the root's parent */
328 pivot = root->right;
329 if (pivot == NULL) {
330 return;
331 }
332
333 if (root->parent) {
334 if (root == root->parent->left) {
335 root_slot = &root->parent->left;
336 }
337 else {
338 root_slot = &root->parent->right;
339 }
340 }
341 else {
342 root_slot = ((DLRBT_Node **)&tree->root); /* &((DLRBT_Node *)tree->root); */
343 }
344
345 /* - pivot's left child becomes root's right child
346 * - root now becomes pivot's left child
347 */
348 root->right = pivot->left;
349 if (pivot->left) {
350 pivot->left->parent = root;
351 }
352
353 pivot->left = root;
354 pivot->parent = root->parent;
355 root->parent = pivot;
356
357 /* make the pivot the new root */
358 if (root_slot) {
359 *root_slot = pivot;
360 }
361}
362
363/* make the left child of the 'root' the new root */
365{
366 DLRBT_Node **root_slot, *pivot;
367
368 /* pivot is simply the root's left child, to become the root's parent */
369 pivot = root->left;
370 if (pivot == NULL) {
371 return;
372 }
373
374 if (root->parent) {
375 if (root == root->parent->left) {
376 root_slot = &root->parent->left;
377 }
378 else {
379 root_slot = &root->parent->right;
380 }
381 }
382 else {
383 root_slot = ((DLRBT_Node **)&tree->root); /* &((DLRBT_Node *)tree->root); */
384 }
385
386 /* - pivot's right child becomes root's left child
387 * - root now becomes pivot's right child
388 */
389 root->left = pivot->right;
390 if (pivot->right) {
391 pivot->right->parent = root;
392 }
393
394 pivot->right = root;
395 pivot->parent = root->parent;
396 root->parent = pivot;
397
398 /* make the pivot the new root */
399 if (root_slot) {
400 *root_slot = pivot;
401 }
402}
403
404/* *********************************************** */
405/* Post-Insertion Balancing */
406
407/* forward defines for insertion checks */
408static void insert_check_1(DLRBT_Tree *tree, DLRBT_Node *node);
409static void insert_check_2(DLRBT_Tree *tree, DLRBT_Node *node);
410static void insert_check_3(DLRBT_Tree *tree, DLRBT_Node *node);
411
412/* ----- */
413
414/* W. 1) Root must be black (so that the 2nd-generation can have a black parent) */
416{
417 if (node) {
418 /* if this is the root, just ensure that it is black */
419 if (node->parent == NULL) {
420 node->tree_col = DLRBT_BLACK;
421 }
422 else {
423 insert_check_2(tree, node);
424 }
425 }
426}
427
428/* W. 2+3) Parent of node must be black, otherwise recolor and flush */
430{
431 /* if the parent is not black, we need to change that... */
432 if (node && node->parent && node->parent->tree_col) {
433 DLRBT_Node *unc = get_uncle(node);
434
435 /* if uncle and parent are both red, need to change them to black and make
436 * the parent black in order to satisfy the criteria of each node having the
437 * same number of black nodes to its leaves
438 */
439 if (unc && unc->tree_col) {
440 DLRBT_Node *gp = get_grandparent(node);
441
442 /* make the n-1 generation nodes black */
443 node->parent->tree_col = unc->tree_col = DLRBT_BLACK;
444
445 /* - make the grandparent red, so that we maintain alternating red/black property
446 * (it must exist, so no need to check for NULL here),
447 * - as the grandparent may now cause inconsistencies with the rest of the tree,
448 * we must flush up the tree and perform checks/re-balancing/re-painting, using the
449 * grandparent as the node of interest
450 */
451 gp->tree_col = DLRBT_RED;
452 insert_check_1(tree, gp);
453 }
454 else {
455 /* we've got an unbalanced branch going down the grandparent to the parent,
456 * so need to perform some rotations to re-balance the tree
457 */
458 insert_check_3(tree, node);
459 }
460 }
461}
462
463/* W. 4+5) Perform rotation on sub-tree containing the 'new' node, then do any. */
465{
466 DLRBT_Node *gp = get_grandparent(node);
467
468 /* check that grandparent and node->parent exist
469 * (jut in case... really shouldn't happen on a good tree) */
470 if (node && node->parent && gp) {
471 /* a left rotation will switch the roles of node and its parent, assuming that
472 * the parent is the left child of the grandparent... otherwise, rotation direction
473 * should be swapped
474 */
475 if ((node == node->parent->right) && (node->parent == gp->left)) {
476 rotate_left(tree, node);
477 node = node->left;
478 }
479 else if ((node == node->parent->left) && (node->parent == gp->right)) {
480 rotate_right(tree, node);
481 node = node->right;
482 }
483
484 /* fix old parent's color-tagging, and perform rotation on the old parent in the
485 * opposite direction if needed for the current situation
486 * NOTE: in the code above, node pointer is changed to point to the old parent
487 */
488 if (node) {
489 /* get 'new' grandparent (i.e. grandparent for old-parent (node)) */
490 gp = get_grandparent(node);
491
492 /* modify the coloring of the grandparent and parent
493 * so that they still satisfy the constraints */
494 node->parent->tree_col = DLRBT_BLACK;
495 gp->tree_col = DLRBT_RED;
496
497 /* if there are several nodes that all form a left chain, do a right rotation to correct
498 * this (or a rotation in the opposite direction if they all form a right chain) */
499 if ((node == node->parent->left) && (node->parent == gp->left)) {
500 rotate_right(tree, gp);
501 }
502 else { // if ((node == node->parent->right) && (node->parent == gp->right))
503 rotate_left(tree, gp);
504 }
505 }
506 }
507}
508
509/* ----- */
510
512{
513 /* sanity checks */
514 if ((tree == NULL) || (node == NULL)) {
515 return;
516 }
517
518 /* firstly, the node we just added should be red by default */
519 node->tree_col = DLRBT_RED;
520
521 /* start from case 1, an trek through the tail-recursive insertion checks */
522 insert_check_1(tree, node);
523}
524
525/* ----- */
526
528 DLRBT_Comparator_FP cmp_cb,
529 DLRBT_NAlloc_FP new_cb,
530 DLRBT_NUpdate_FP update_cb,
531 void *data)
532{
533 DLRBT_Node *parNode, *node = NULL;
534 short new_node = 0;
535
536 /* sanity checks */
537 if (tree == NULL) {
538 return NULL;
539 }
540
541 /* TODO: if no comparator is supplied, try using the one supplied with the tree... */
542 if (cmp_cb == NULL) {
543 return NULL;
544 }
545 /* TODO: if no allocator is supplied, try using the one supplied with the tree... */
546 if (new_cb == NULL) {
547 return NULL;
548 }
549 /* TODO: if no updater is supplied, try using the one supplied with the tree... */
550
551 /* try to find the nearest node to this one */
552 parNode = BLI_dlrbTree_search(tree, cmp_cb, data);
553
554 /* add new node to the BST in the 'standard way' as appropriate
555 * NOTE: we do not support duplicates in our tree...
556 */
557 if (parNode) {
558 /* check how this new node compares with the existing ones
559 * NOTE: it is assumed that the values will be unit values only
560 */
561 switch (cmp_cb(parNode, data)) {
562 case -1: /* add new node as left child */
563 {
564 node = new_cb(data);
565 new_node = 1;
566
567 parNode->left = node;
568 node->parent = parNode;
569 break;
570 }
571 case 1: /* add new node as right child */
572 {
573 node = new_cb(data);
574 new_node = 1;
575
576 parNode->right = node;
577 node->parent = parNode;
578 break;
579 }
580 default: /* update the duplicate node as appropriate */
581 {
582 /* Return the updated node after calling the callback. */
583 node = parNode;
584 if (update_cb) {
585 update_cb(node, data);
586 }
587 break;
588 }
589 }
590 }
591 else {
592 /* no nodes in the tree yet... add a new node as the root */
593 node = new_cb(data);
594 new_node = 1;
595
596 tree->root = node;
597 }
598
599 /* if a new node was added, it should be tagged as red, and then balanced as appropriate */
600 if (new_node) {
601 /* tag this new node as being 'red' */
602 node->tree_col = DLRBT_RED;
603
604 /* perform BST balancing steps:
605 * start from case 1, an trek through the tail-recursive insertion checks
606 */
607 insert_check_1(tree, node);
608 }
609
610 /* return the node added */
611 return node;
612}
613
614/* *********************************************** */
615/* Remove */
616
617/* TODO: this hasn't been coded yet, since this functionality was not needed by the author */
618
619/* *********************************************** */
void(* DLRBT_NFree_FP)(void *node)
void(* DLRBT_NUpdate_FP)(void *node, void *data)
DLRBT_Node *(* DLRBT_NAlloc_FP)(void *data)
short(* DLRBT_Comparator_FP)(void *node, void *data)
@ DLRBT_BLACK
@ DLRBT_RED
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
DLRBT_Node * BLI_dlrbTree_search(const DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data)
Definition DLRB_tree.c:121
void BLI_dlrbTree_init(DLRBT_Tree *tree)
Definition DLRB_tree.c:23
DLRBT_Node * BLI_dlrbTree_search_next(const DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data)
Definition DLRB_tree.c:245
static void rotate_left(DLRBT_Tree *tree, DLRBT_Node *root)
Definition DLRB_tree.c:323
static void recursive_tree_free_nodes(DLRBT_Node *node, DLRBT_NFree_FP free_cb)
Definition DLRB_tree.c:33
static DLRBT_Node * get_uncle(DLRBT_Node *node)
Definition DLRB_tree.c:308
static DLRBT_Node * get_sibling(DLRBT_Node *node)
Definition DLRB_tree.c:294
DLRBT_Node * BLI_dlrbTree_add(DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, DLRBT_NAlloc_FP new_cb, DLRBT_NUpdate_FP update_cb, void *data)
Definition DLRB_tree.c:527
static void insert_check_1(DLRBT_Tree *tree, DLRBT_Node *node)
Definition DLRB_tree.c:415
void BLI_dlrbTree_linkedlist_sync(DLRBT_Tree *tree)
Definition DLRB_tree.c:104
static void rotate_right(DLRBT_Tree *tree, DLRBT_Node *root)
Definition DLRB_tree.c:364
static DLRBT_Node * get_grandparent(DLRBT_Node *node)
Definition DLRB_tree.c:285
short BLI_dlrbTree_contains(DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data)
Definition DLRB_tree.c:275
void BLI_dlrbTree_free(DLRBT_Tree *tree, DLRBT_NFree_FP free_cb)
Definition DLRB_tree.c:50
void BLI_dlrbTree_insert(DLRBT_Tree *tree, DLRBT_Node *node)
Definition DLRB_tree.c:511
DLRBT_Tree * BLI_dlrbTree_new(void)
Definition DLRB_tree.c:17
static void insert_check_2(DLRBT_Tree *tree, DLRBT_Node *node)
Definition DLRB_tree.c:429
DLRBT_Node * BLI_dlrbTree_search_exact(const DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data)
Definition DLRB_tree.c:168
static void insert_check_3(DLRBT_Tree *tree, DLRBT_Node *node)
Definition DLRB_tree.c:464
static void linkedlist_sync_add_node(DLRBT_Tree *tree, DLRBT_Node *node)
Definition DLRB_tree.c:83
DLRBT_Node * BLI_dlrbTree_search_prev(const DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data)
Definition DLRB_tree.c:215
Read Guarded memory(de)allocation.
OperationNode * node
#define NULL
KDTree_3d * tree
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
struct DLRBT_Node * parent
struct DLRBT_Node * left
struct DLRBT_Node * right