libcstl
Loading...
Searching...
No Matches
rbtree.c
Go to the documentation of this file.
1/*!
2 * @file
3 */
4
5#include "cstl/rbtree.h"
6
7#include <assert.h>
8
9/*! @private */
10static inline struct cstl_rbtree_node * __cstl_rbtree_node(
11 const struct cstl_rbtree * const t, const void * e)
12{
13 return (void *)((uintptr_t)e + t->off);
14}
15
16/*!
17 * @private
18 *
19 * Given a pointer to a binary tree node,
20 * get a pointer to the red-black tree node's color
21 */
22static inline cstl_rbtree_color_t * BN_COLOR(
23 const struct cstl_bintree_node * const bn)
24{
25 return &((struct cstl_rbtree_node *)(
26 (uintptr_t)bn - offsetof(struct cstl_rbtree_node, n)))->c;
27}
28
29/*!
30 * @private
31 *
32 * this function is called as a result of x and x's parent
33 * both being red. the goal of this function is to push that
34 * property violation up the tree, toward the root without
35 * breaking the "same number of black nodes on every path" rule
36 *
37 * x's parent is assumed to be the left child of x's grandparent
38 * upon entry to this function. for the case where it is the right
39 * child, the @l and @r parameters must be reversed
40 */
41static struct cstl_bintree_node * cstl_rbtree_fix_insertion(
42 struct cstl_bintree * const t, struct cstl_bintree_node * x,
43 __cstl_bintree_child_func_t * const l,
44 __cstl_bintree_child_func_t * const r)
45{
46 struct cstl_bintree_node * const y = *r(x->p->p);
47
48 /*
49 * if the tree is not violating any of the red-black
50 * properties aside from x and it's parent both being
51 * red, then x's grandparent is guaranteed to be black.
52 */
53
54 if (y != NULL && *BN_COLOR(y) == CSTL_RBTREE_COLOR_R) {
55 /*
56 * if x's parent's sibling is also red, then
57 * the parent and the sibling can be changed to
58 * black and the grandparent to red. now the
59 * red-red violation (if one exists) is between
60 * x's grandparent and great grandparent
61 */
62 *BN_COLOR(x->p) = CSTL_RBTREE_COLOR_B;
63 *BN_COLOR(y) = CSTL_RBTREE_COLOR_B;
64 *BN_COLOR(x->p->p) = CSTL_RBTREE_COLOR_R;
65 x = x->p->p;
66 } else {
67 if (x == *r(x->p)) {
68 /*
69 * x is the right child. rotate such that
70 * x's parent becomes x's left child and
71 * x becomes x's grandparent's left child.
72 *
73 * note that x is moved down to point at
74 * its former parent (now its left child)
75 */
76 x = x->p;
77 __cstl_bintree_rotate(t, x, l, r);
78 }
79
80 /*
81 * x is now a left child.
82 *
83 * x's grandparent is a black node whose left child
84 * and left child's child are both red. rotate the
85 * tree right about the grandparent and re-color
86 * the nodes to make the position formerly occupied
87 * by the grandparent black with two red children
88 */
89
90 *BN_COLOR(x->p) = CSTL_RBTREE_COLOR_B;
91 *BN_COLOR(x->p->p) = CSTL_RBTREE_COLOR_R;
92 __cstl_bintree_rotate(t, x->p->p, r, l);
93 }
94
95 return x;
96}
97
98void cstl_rbtree_insert(struct cstl_rbtree * const t,
99 void * const e, void * const p)
100{
101 struct cstl_rbtree_node * const n = __cstl_rbtree_node(t, e);
102 struct cstl_bintree_node * x;
103
104 /*
105 * insert as normal, with the new node colored
106 */
107 cstl_bintree_insert(&t->t, e, p);
108 n->c = CSTL_RBTREE_COLOR_R;
109
110 /*
111 * it's possible that the new node's parent is red,
112 * which is a violation of the "red nodes can only
113 * have black children" property
114 */
115 x = &n->n;
116 while (x->p != NULL && *BN_COLOR(x->p) == CSTL_RBTREE_COLOR_R) {
117 /*
118 * if has a parent (i.e. is not the root) and is
119 * red, then x must have a grandparent because
120 * the root is always black
121 */
122
123 if (x->p == x->p->p->l) {
124 x = cstl_rbtree_fix_insertion(
125 &t->t, x,
126 __cstl_bintree_left, __cstl_bintree_right);
127 } else {
128 x = cstl_rbtree_fix_insertion(
129 &t->t, x,
130 __cstl_bintree_right, __cstl_bintree_left);
131 }
132 }
133
134 *BN_COLOR(t->t.root) = CSTL_RBTREE_COLOR_B;
135}
136
137/*!
138 * @private
139 *
140 * x must be black and not be the root upon entry to the function.
141 *
142 * the function is written as if x is its parent's left child, but
143 * the code can operate in the case where x is the right child by
144 * reversing the left/right functions passed as the @l and @r
145 * parameters
146 */
147static struct cstl_bintree_node * cstl_rbtree_fix_deletion(
148 struct cstl_bintree * const t, struct cstl_bintree_node * x,
149 __cstl_bintree_child_func_t * const l,
150 __cstl_bintree_child_func_t * const r)
151{
152 struct cstl_bintree_node * w;
153
154 /*
155 * the reason this function gets called is because
156 * there is 1 fewer black node on the path to x than
157 * every other path in the tree. for that reason,
158 * x's (w) sibling must be non-NULL, otherwise, the path
159 * to the sibling would have the same number of blacks
160 * as the path to x.
161 */
162 w = *r(x->p);
163
164 if (*BN_COLOR(w) == CSTL_RBTREE_COLOR_R) {
165 /*
166 * if the sibling is red, it must have black children.
167 *
168 * the tree is rotated left which makes w x's grandparent
169 * and w's (left child), x's new sibling. colors are adjusted
170 * to maintain the current red-black status quo. one of
171 * the conditions below now applies
172 */
173 *BN_COLOR(w) = CSTL_RBTREE_COLOR_B;
174 *BN_COLOR(x->p) = CSTL_RBTREE_COLOR_R;
175 __cstl_bintree_rotate(t, x->p, l, r);
176 w = *r(x->p);
177 }
178
179 /*
180 * based on the case above, x's sibling was either black
181 * or it was transformed so that x's sibling is now black.
182 */
183 if ((*l(w) == NULL || *BN_COLOR(*l(w)) == CSTL_RBTREE_COLOR_B)
184 && (*r(w) == NULL || *BN_COLOR(*r(w)) == CSTL_RBTREE_COLOR_B)) {
185 /* if w has two black children, then make it red */
186 *BN_COLOR(w) = CSTL_RBTREE_COLOR_R;
187 /*
188 * the tree is good up to this point,
189 * move x further up the tree
190 */
191 x = x->p;
192 } else {
193 /* else w has at least one red child */
194 if (*r(w) == NULL || *BN_COLOR(*r(w)) == CSTL_RBTREE_COLOR_B) {
195 /*
196 * if w's right child is black, then the left has
197 * to be red. the case looks something like the first
198 * if clause above. the colors are adjusted similarly
199 * and the tree rotated. this maintains the status quo
200 * as before and x's new sibling (w) has a red right
201 * child
202 */
203 *BN_COLOR(*l(w)) = CSTL_RBTREE_COLOR_B;
204 *BN_COLOR(w) = CSTL_RBTREE_COLOR_R;
205 __cstl_bintree_rotate(t, w, r, l);
206 w = *r(x->p);
207 }
208
209 /*
210 * either the right child of w is red (and the if clause
211 * above was skipped) or the if clause was entered which
212 * transformed the tree such that the right child of w
213 * is *now* red
214 *
215 * rotating the tree left allows the nodes to the left
216 * of w (after the rotation) to be colored black such
217 * that the missing black caused by the deletion can
218 * be restored
219 */
220
221 *BN_COLOR(w) = *BN_COLOR(x->p);
222 *BN_COLOR(x->p) = CSTL_RBTREE_COLOR_B;
223 *BN_COLOR(*r(w)) = CSTL_RBTREE_COLOR_B;
224 __cstl_bintree_rotate(t, x->p, l, r);
225
226 /*
227 * setting x to be the root tells the caller to
228 * stop fixing since there is no further up the
229 * tree to move
230 */
231 x = t->root;
232 }
233
234 return x;
235}
236
237/*! @private */
238void __cstl_rbtree_erase(struct cstl_rbtree * const t,
239 struct cstl_rbtree_node * const n)
240{
241 const struct cstl_bintree_node * const y =
242 __cstl_bintree_erase(&t->t, &n->n);
243
244 /*
245 * y points to the location in the tree from where the
246 * node was *supposed* to be removed. the line below
247 * captures the color that was *supposed* to be removed
248 * from the tree.
249 */
250 const cstl_rbtree_color_t c = *BN_COLOR(y);
251 /*
252 * restore the correct color to the node that remains
253 * in the tree. (note that if the node that was *supposed*
254 * to be removed *was* removed, then this line has no
255 * effect because y == n
256 */
257 *BN_COLOR(y) = n->c;
258
259 /*
260 * if the color of the removed node was black, it's
261 * 1.) possible that the rule that a red node must have
262 * black children, and 2.) certain that the rule that
263 * the same number of black nodes be on every path from
264 * the root to the leaves have been violated, and more
265 * work is needed to fix that.
266 */
267 if (c == CSTL_RBTREE_COLOR_B) {
268 struct cstl_rbtree_node _x;
269 struct cstl_bintree_node * x;
270
271 /*
272 * the removed node can only have had 0 or 1
273 * children; see __cstl_bintree_erase for an explanation
274 */
275 assert(n->n.l == NULL || n->n.r == NULL);
276
277 /*
278 * point x at one of the removed node's (former)
279 * children. if it had no children, fake one,
280 * whose color is black, by convention.
281 */
282 if (n->n.l != NULL) {
283 x = n->n.l;
284 } else if (n->n.r != NULL) {
285 x = n->n.r;
286 } else {
287 x = &_x.n;
288
289 x->p = n->n.p;
290 *BN_COLOR(x) = CSTL_RBTREE_COLOR_B;
291 }
292
293 /*
294 * x's parent is the removed node's parent,
295 * x's former grandparent
296 */
297 assert(x->p == n->n.p);
298
299 /*
300 * any path to x has 1 too few black nodes in
301 * its path since its parent, a black node, was
302 * removed from the tree. work up the tree, trying
303 * to restore red-black properties
304 */
305
306 while (x->p != NULL && *BN_COLOR(x) == CSTL_RBTREE_COLOR_B) {
307 if (x == x->p->l || (x == &_x.n && x->p->l == NULL)) {
308 x = cstl_rbtree_fix_deletion(
309 &t->t, x,
310 __cstl_bintree_left, __cstl_bintree_right);
311 } else {
312 x = cstl_rbtree_fix_deletion(
313 &t->t, x,
314 __cstl_bintree_right, __cstl_bintree_left);
315 }
316 }
317
318 /*
319 * if the loop encounters a red node, making it
320 * black fixes the number of black nodes on
321 * the path to x
322 */
323 *BN_COLOR(x) = CSTL_RBTREE_COLOR_B;
324 }
325}
326
327void * cstl_rbtree_erase(struct cstl_rbtree * const t, const void * const _p)
328{
329 void * const p = (void *)cstl_rbtree_find(t, _p, NULL);
330 if (p != NULL) {
331 __cstl_rbtree_erase(t, __cstl_rbtree_node(t, p));
332 }
333 return p;
334}
335
336#ifdef __cfg_test__
337// GCOV_EXCL_START
338#include <check.h>
339#include <stdlib.h>
340
341struct integer
342{
343 int v;
344 struct cstl_rbtree_node n;
345};
346
347static int cmp_integer(const void * const a, const void * const b,
348 void * const p)
349{
350 (void)p;
351 return ((struct integer *)a)->v - ((struct integer *)b)->v;
352}
353
354START_TEST(init)
355{
356 DECLARE_CSTL_RBTREE(t, struct integer, n, cmp_integer, NULL);
357 (void)t;
358}
359END_TEST
360
361static int __cstl_rbtree_verify(const void * const elem,
362 const cstl_bintree_visit_order_t order,
363 void * const priv)
364{
366 || order == CSTL_BINTREE_VISIT_ORDER_LEAF) {
367 const struct cstl_bintree * const t = priv;
368 const struct cstl_bintree_node * const bn =
369 &((const struct integer *)elem)->n.n;
370
371 size_t bh = 0;
372
373 if (*BN_COLOR(bn) == CSTL_RBTREE_COLOR_R) {
374 ck_assert(bn->l == NULL
375 || *BN_COLOR(bn->l) == CSTL_RBTREE_COLOR_B);
376 ck_assert(bn->r == NULL
377 || *BN_COLOR(bn->r) == CSTL_RBTREE_COLOR_B);
378 }
379
380 if (bn->l != NULL) {
381 ck_assert_int_lt(__cstl_bintree_cmp(t, bn->l, bn), 0);
382 }
383 if (bn->r != NULL) {
384 ck_assert_int_ge(__cstl_bintree_cmp(t, bn->r, bn), 0);
385 }
386
387 if (bn->l == NULL && bn->r == NULL) {
388 const struct cstl_bintree_node * n;
389 size_t h;
390
391 for (h = 0, n = bn; n != NULL; n = n->p) {
392 if (*BN_COLOR(n) == CSTL_RBTREE_COLOR_B) {
393 h++;
394 }
395 }
396
397 ck_assert(bh == 0 || h == bh);
398 bh = h;
399 }
400 }
401
402 return 0;
403}
404
405static void cstl_rbtree_verify(const struct cstl_rbtree * const t)
406{
407 if (t->t.root != NULL) {
408 size_t min, max;
409
410 cstl_rbtree_height(t, &min, &max);
411 ck_assert_uint_le(max, 2 * log2(cstl_rbtree_size(t) + 1));
412 ck_assert_uint_le(max, 2 * min);
413
415 t, __cstl_rbtree_verify, (void *)&t->t,
417 }
418}
419
420static void __test_cstl_rbtree_free(void * const p, void * const x)
421{
422 (void)x;
423 free(p);
424}
425
426static void __test__cstl_rbtree_fill(struct cstl_rbtree * const t,
427 const size_t n)
428{
429 unsigned int i;
430
431 for (i = 0; i < n; i++) {
432 struct integer * const in = malloc(sizeof(*in));
433 in->v = i;
434
435 cstl_rbtree_insert(t, in, NULL);
436 ck_assert_uint_eq(i + 1, cstl_rbtree_size(t));
437 }
438}
439
440START_TEST(fill)
441{
442 static const size_t n = 100;
443
444 DECLARE_CSTL_RBTREE(t, struct integer, n, cmp_integer, NULL);
445
446 __test__cstl_rbtree_fill(&t, n);
447 cstl_rbtree_verify(&t);
448 cstl_rbtree_clear(&t, __test_cstl_rbtree_free, NULL);
449}
450END_TEST
451
452START_TEST(random_fill)
453{
454 static const size_t n = 100;
455
456 DECLARE_CSTL_RBTREE(t, struct integer, n, cmp_integer, NULL);
457 unsigned int i;
458
459 for (i = 0; i < n; i++) {
460 struct integer * const in = malloc(sizeof(*in));
461
462 do {
463 in->v = rand() % n;
464 } while (cstl_rbtree_find(&t, in, NULL) != NULL);
465
466 cstl_rbtree_insert(&t, in, NULL);
467 ck_assert_uint_eq(i + 1, cstl_rbtree_size(&t));
468 }
469
470 cstl_rbtree_verify(&t);
471 cstl_rbtree_clear(&t, __test_cstl_rbtree_free, NULL);
472}
473END_TEST
474
475START_TEST(random_empty)
476{
477 static const size_t n = 100;
478
479 DECLARE_CSTL_RBTREE(t, struct integer, n, cmp_integer, NULL);
480 size_t sz;
481
482 __test__cstl_rbtree_fill(&t, n);
483
484 while ((sz = cstl_rbtree_size(&t)) > 0) {
485 struct integer _in, * in;
486
487 _in.v = rand() % n;
488
489 in = cstl_rbtree_erase(&t, &_in);
490 if (in != NULL) {
491 free(in);
492 ck_assert_uint_eq(sz - 1, cstl_rbtree_size(&t));
493
494 cstl_rbtree_verify(&t);
495 }
496 }
497
498 ck_assert_ptr_null(t.t.root);
499 ck_assert_uint_eq(cstl_rbtree_size(&t), 0);
500}
501END_TEST
502
503Suite * rbtree_suite(void)
504{
505 Suite * const s = suite_create("rbtree");
506
507 TCase * tc;
508
509 tc = tcase_create("rbtree");
510 tcase_add_test(tc, init);
511 tcase_add_test(tc, fill);
512 tcase_add_test(tc, random_fill);
513 tcase_add_test(tc, random_empty);
514
515 suite_add_tcase(s, tc);
516
517 return s;
518}
519
520// GCOV_EXCL_STOP
521#endif
void cstl_bintree_insert(struct cstl_bintree *bt, void *e, void *p)
Insert a new object into the tree.
Definition bintree.c:63
cstl_bintree_visit_order_t
Enumeration indicating the order in which a tree element is being visited during cstl_bintree_foreach...
Definition bintree.h:249
@ CSTL_BINTREE_VISIT_ORDER_LEAF
The only visit to an element that has no children.
Definition bintree.h:263
@ CSTL_BINTREE_VISIT_ORDER_MID
The second visit to an element, after its first child has/would have been visited.
Definition bintree.h:256
@ CSTL_BINTREE_FOREACH_DIR_FWD
Each element in the tree is visited from left-to-right.
Definition bintree.h:277
static int cstl_rbtree_foreach(const struct cstl_rbtree *const t, cstl_bintree_const_visit_func_t *const visit, void *const priv, const cstl_bintree_foreach_dir_t dir)
Visit each element in a tree, calling a user-defined function for each visit.
Definition rbtree.h:275
void cstl_rbtree_insert(struct cstl_rbtree *const t, void *const e, void *const p)
Insert a new object into the tree.
Definition rbtree.c:98
static const void * cstl_rbtree_find(const struct cstl_rbtree *const t, const void *const e, const void **const p)
Find an element within a tree.
Definition rbtree.h:187
static void cstl_rbtree_height(const struct cstl_rbtree *const t, size_t *const min, size_t *const max)
Determine the maximum and minimum heights of a tree.
Definition rbtree.h:291
static void cstl_rbtree_clear(struct cstl_rbtree *const t, cstl_xtor_func_t *const clr, void *const priv)
Remove all elements from the tree.
Definition rbtree.h:233
static size_t cstl_rbtree_size(const struct cstl_rbtree *const t)
Get the number of objects in the tree.
Definition rbtree.h:145
void * cstl_rbtree_erase(struct cstl_rbtree *const t, const void *const _p)
Remove an element from the tree.
Definition rbtree.c:327
#define DECLARE_CSTL_RBTREE(NAME, TYPE, MEMB, CMP, PRIV)
(Statically) declare and initialize a red-black tree
Definition rbtree.h:114
Node to anchor an element within a binary tree.
Definition bintree.h:49
Binary tree object.
Definition bintree.h:63
Node to anchor an element within a red-black tree.
Definition rbtree.h:60
Red-black tree object.
Definition rbtree.h:75