Coverage Report

Created: 2026-08-13 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/njs/src/njs_rbtree.c
Line
Count
Source
1
2
/*
3
 * Copyright (C) Igor Sysoev
4
 * Copyright (C) NGINX, Inc.
5
 */
6
7
8
#include <njs_main.h>
9
10
11
/*
12
 * The red-black tree code is based on the algorithm described in
13
 * the "Introduction to Algorithms" by Cormen, Leiserson and Rivest.
14
 */
15
16
17
static void njs_rbtree_insert_fixup(njs_rbtree_node_t *node);
18
static void njs_rbtree_delete_fixup(njs_rbtree_t *tree,
19
    njs_rbtree_node_t *node);
20
njs_inline void njs_rbtree_left_rotate(njs_rbtree_node_t *node);
21
njs_inline void njs_rbtree_right_rotate(njs_rbtree_node_t *node);
22
njs_inline void njs_rbtree_parent_relink(njs_rbtree_node_t *subst,
23
    njs_rbtree_node_t *node);
24
25
26
43.9M
#define NJS_RBTREE_BLACK  0
27
14.9M
#define NJS_RBTREE_RED    1
28
29
30
#define njs_rbtree_comparison_callback(tree)                                  \
31
17.8M
    ((njs_rbtree_compare_t) (tree)->sentinel.right)
32
33
34
void
35
njs_rbtree_init(njs_rbtree_t *tree, njs_rbtree_compare_t compare)
36
3.41M
{
37
    /*
38
     * The sentinel is used as a leaf node sentinel and as a tree root
39
     * sentinel: it is a parent of a root node and the root node is
40
     * the left child of the sentinel.  Combining two sentinels in one
41
     * entry and the fact that the sentinel's left child is a root node
42
     * simplifies njs_rbtree_node_successor() and eliminates explicit
43
     * root node test before or inside njs_rbtree_min().
44
     */
45
46
    /* The root is empty. */
47
3.41M
    tree->sentinel.left = &tree->sentinel;
48
49
    /*
50
     * The sentinel's right child is never used so
51
     * comparison callback can be safely stored here.
52
     */
53
3.41M
    tree->sentinel.right = (void *) compare;
54
55
    /* The root and leaf sentinel must be black. */
56
3.41M
    tree->sentinel.color = NJS_RBTREE_BLACK;
57
3.41M
}
58
59
60
void
61
njs_rbtree_insert(njs_rbtree_t *tree, njs_rbtree_part_t *part)
62
7.80M
{
63
7.80M
    njs_rbtree_node_t     *node, *new_node, *sentinel, **child;
64
7.80M
    njs_rbtree_compare_t  compare;
65
66
7.80M
    new_node = (njs_rbtree_node_t *) part;
67
68
7.80M
    node = njs_rbtree_root(tree);
69
7.80M
    sentinel = njs_rbtree_sentinel(tree);
70
71
7.80M
    new_node->left = sentinel;
72
7.80M
    new_node->right = sentinel;
73
7.80M
    new_node->color = NJS_RBTREE_RED;
74
75
7.80M
    compare = (njs_rbtree_compare_t) tree->sentinel.right;
76
7.80M
    child = &njs_rbtree_root(tree);
77
78
68.8M
    while (*child != sentinel) {
79
61.0M
        node = *child;
80
81
61.0M
        njs_prefetch(node->left);
82
61.0M
        njs_prefetch(node->right);
83
84
61.0M
        child = (compare(new_node, node) < 0) ? &node->left : &node->right;
85
61.0M
    }
86
87
7.80M
    *child = new_node;
88
7.80M
    new_node->parent = node;
89
90
7.80M
    njs_rbtree_insert_fixup(new_node);
91
92
7.80M
    node = njs_rbtree_root(tree);
93
7.80M
    node->color = NJS_RBTREE_BLACK;
94
7.80M
}
95
96
97
static void
98
njs_rbtree_insert_fixup(njs_rbtree_node_t *node)
99
7.80M
{
100
7.80M
    njs_rbtree_node_t  *parent, *grandparent, *uncle;
101
102
    /*
103
     * Prefetching parent nodes does not help here because they are
104
     * already traversed during insertion.
105
     */
106
107
10.7M
    for ( ;; ) {
108
10.7M
        parent = node->parent;
109
110
        /*
111
         * Testing whether a node is a tree root is not required here since
112
         * a root node's parent is the sentinel and it is always black.
113
         */
114
10.7M
        if (parent->color == NJS_RBTREE_BLACK) {
115
4.51M
            return;
116
4.51M
        }
117
118
6.25M
        grandparent = parent->parent;
119
120
6.25M
        if (parent == grandparent->left) {
121
936k
            uncle = grandparent->right;
122
123
936k
            if (uncle->color == NJS_RBTREE_BLACK) {
124
125
811k
                if (node == parent->right) {
126
767k
                    node = parent;
127
767k
                    njs_rbtree_left_rotate(node);
128
767k
                }
129
130
                /*
131
                 * njs_rbtree_left_rotate() swaps parent and
132
                 * child whilst keeps grandparent the same.
133
                 */
134
811k
                parent = node->parent;
135
136
811k
                parent->color = NJS_RBTREE_BLACK;
137
811k
                grandparent->color = NJS_RBTREE_RED;
138
139
811k
                njs_rbtree_right_rotate(grandparent);
140
                /*
141
                 * njs_rbtree_right_rotate() does not change node->parent
142
                 * color which is now black, so testing color is not required
143
                 * to return from function.
144
                 */
145
811k
                return;
146
811k
            }
147
148
5.32M
        } else {
149
5.32M
            uncle = grandparent->left;
150
151
5.32M
            if (uncle->color == NJS_RBTREE_BLACK) {
152
153
2.48M
                if (node == parent->left) {
154
64.7k
                    node = parent;
155
64.7k
                    njs_rbtree_right_rotate(node);
156
64.7k
                }
157
158
                /* See the comment in the symmetric branch above. */
159
2.48M
                parent = node->parent;
160
161
2.48M
                parent->color = NJS_RBTREE_BLACK;
162
2.48M
                grandparent->color = NJS_RBTREE_RED;
163
164
2.48M
                njs_rbtree_left_rotate(grandparent);
165
166
                /* See the comment in the symmetric branch above. */
167
2.48M
                return;
168
2.48M
            }
169
5.32M
        }
170
171
2.96M
        uncle->color = NJS_RBTREE_BLACK;
172
2.96M
        parent->color = NJS_RBTREE_BLACK;
173
2.96M
        grandparent->color = NJS_RBTREE_RED;
174
175
2.96M
        node = grandparent;
176
2.96M
    }
177
7.80M
}
178
179
180
njs_rbtree_node_t *
181
njs_rbtree_find(njs_rbtree_t *tree, njs_rbtree_part_t *part)
182
17.8M
{
183
17.8M
    intptr_t              n;
184
17.8M
    njs_rbtree_node_t     *node, *next, *sentinel;
185
17.8M
    njs_rbtree_compare_t  compare;
186
187
17.8M
    node = (njs_rbtree_node_t *) part;
188
189
17.8M
    next = njs_rbtree_root(tree);
190
17.8M
    sentinel = njs_rbtree_sentinel(tree);
191
17.8M
    compare = njs_rbtree_comparison_callback(tree);
192
193
36.8M
    while (next != sentinel) {
194
20.7M
        njs_prefetch(next->left);
195
20.7M
        njs_prefetch(next->right);
196
197
20.7M
        n = compare(node, next);
198
199
20.7M
        if (n < 0) {
200
3.25M
            next = next->left;
201
202
17.4M
        } else if (n > 0) {
203
15.7M
            next = next->right;
204
205
15.7M
        } else {
206
1.73M
            return next;
207
1.73M
        }
208
20.7M
    }
209
210
16.1M
    return NULL;
211
17.8M
}
212
213
214
njs_rbtree_node_t *
215
njs_rbtree_find_less_or_equal(njs_rbtree_t *tree, njs_rbtree_part_t *part)
216
0
{
217
0
    intptr_t              n;
218
0
    njs_rbtree_node_t     *node, *retval, *next, *sentinel;
219
0
    njs_rbtree_compare_t  compare;
220
221
0
    node = (njs_rbtree_node_t *) part;
222
223
0
    retval = NULL;
224
0
    next = njs_rbtree_root(tree);
225
0
    sentinel = njs_rbtree_sentinel(tree);
226
0
    compare = njs_rbtree_comparison_callback(tree);
227
228
0
    while (next != sentinel) {
229
0
        njs_prefetch(next->left);
230
0
        njs_prefetch(next->right);
231
232
0
        n = compare(node, next);
233
234
0
        if (n < 0) {
235
0
            next = next->left;
236
237
0
        } else if (n > 0) {
238
0
            retval = next;
239
0
            next = next->right;
240
241
0
        } else {
242
            /* Exact match. */
243
0
            return next;
244
0
        }
245
0
    }
246
247
0
    return retval;
248
0
}
249
250
251
njs_rbtree_node_t *
252
njs_rbtree_find_greater_or_equal(njs_rbtree_t *tree, njs_rbtree_part_t *part)
253
0
{
254
0
    intptr_t              n;
255
0
    njs_rbtree_node_t     *node, *retval, *next, *sentinel;
256
0
    njs_rbtree_compare_t  compare;
257
258
0
    node = (njs_rbtree_node_t *) part;
259
260
0
    retval = NULL;
261
0
    next = njs_rbtree_root(tree);
262
0
    sentinel = njs_rbtree_sentinel(tree);
263
0
    compare = njs_rbtree_comparison_callback(tree);
264
265
0
    while (next != sentinel) {
266
0
        njs_prefetch(next->left);
267
0
        njs_prefetch(next->right);
268
269
0
        n = compare(node, next);
270
271
0
        if (n < 0) {
272
0
            retval = next;
273
0
            next = next->left;
274
275
0
        } else if (n > 0) {
276
0
            next = next->right;
277
278
0
        } else {
279
            /* Exact match. */
280
0
            return next;
281
0
        }
282
0
    }
283
284
0
    return retval;
285
0
}
286
287
288
void
289
njs_rbtree_delete(njs_rbtree_t *tree, njs_rbtree_part_t *part)
290
1.78M
{
291
1.78M
    uint8_t            color;
292
1.78M
    njs_rbtree_node_t  *node, *sentinel, *subst, *child;
293
294
1.78M
    node = (njs_rbtree_node_t *) part;
295
296
1.78M
    subst = node;
297
1.78M
    sentinel = njs_rbtree_sentinel(tree);
298
299
1.78M
    if (node->left == sentinel) {
300
1.17M
        child = node->right;
301
302
1.17M
    } else if (node->right == sentinel) {
303
52.4k
        child = node->left;
304
305
560k
    } else {
306
560k
        subst = njs_rbtree_branch_min(tree, node->right);
307
560k
        child = subst->right;
308
560k
    }
309
310
1.78M
    njs_rbtree_parent_relink(child, subst);
311
312
1.78M
    color = subst->color;
313
314
1.78M
    if (subst != node) {
315
        /* Move the subst node to the deleted node position in the tree. */
316
317
560k
        subst->color = node->color;
318
319
560k
        subst->left = node->left;
320
560k
        subst->left->parent = subst;
321
322
560k
        subst->right = node->right;
323
560k
        subst->right->parent = subst;
324
325
560k
        njs_rbtree_parent_relink(subst, node);
326
560k
    }
327
328
#if (NJS_DEBUG)
329
    node->left = NULL;
330
    node->right = NULL;
331
    node->parent = NULL;
332
#endif
333
334
1.78M
    if (color == NJS_RBTREE_BLACK) {
335
800k
        njs_rbtree_delete_fixup(tree, child);
336
800k
    }
337
1.78M
}
338
339
340
static void
341
njs_rbtree_delete_fixup(njs_rbtree_t *tree, njs_rbtree_node_t *node)
342
800k
{
343
800k
    njs_rbtree_node_t  *parent, *sibling;
344
345
1.42M
    while (node != njs_rbtree_root(tree) && node->color == NJS_RBTREE_BLACK) {
346
        /*
347
         * Prefetching parent nodes does not help here according
348
         * to microbenchmarks.
349
         */
350
738k
        parent = node->parent;
351
352
738k
        if (node == parent->left) {
353
525k
            sibling = parent->right;
354
355
525k
            if (sibling->color != NJS_RBTREE_BLACK) {
356
357
182k
                sibling->color = NJS_RBTREE_BLACK;
358
182k
                parent->color = NJS_RBTREE_RED;
359
360
182k
                njs_rbtree_left_rotate(parent);
361
362
182k
                sibling = parent->right;
363
182k
            }
364
365
525k
            if (sibling->right->color == NJS_RBTREE_BLACK) {
366
367
508k
                sibling->color = NJS_RBTREE_RED;
368
369
508k
                if (sibling->left->color == NJS_RBTREE_BLACK) {
370
498k
                    node = parent;
371
498k
                    continue;
372
498k
                }
373
374
9.80k
                sibling->left->color = NJS_RBTREE_BLACK;
375
376
9.80k
                njs_rbtree_right_rotate(sibling);
377
                /*
378
                 * If the node is the leaf sentinel then the right
379
                 * rotate above changes its parent so a sibling below
380
                 * becames the leaf sentinel as well and this causes
381
                 * segmentation fault.  This is the reason why usual
382
                 * red-black tree implementations with a leaf sentinel
383
                 * which does not require to test leaf nodes at all
384
                 * nevertheless test the leaf sentinel in the left and
385
                 * right rotate procedures.  Since according to the
386
                 * algorithm node->parent must not be changed by both
387
                 * the left and right rotates above, it can be cached
388
                 * in a local variable.  This not only eliminates the
389
                 * sentinel test in njs_rbtree_parent_relink() but also
390
                 * decreases the code size because C forces to reload
391
                 * non-restrict pointers.
392
                 */
393
9.80k
                sibling = parent->right;
394
9.80k
            }
395
396
26.6k
            sibling->color = parent->color;
397
26.6k
            parent->color = NJS_RBTREE_BLACK;
398
26.6k
            sibling->right->color = NJS_RBTREE_BLACK;
399
400
26.6k
            njs_rbtree_left_rotate(parent);
401
402
26.6k
            return;
403
404
525k
        } else {
405
213k
            sibling = parent->left;
406
407
213k
            if (sibling->color != NJS_RBTREE_BLACK) {
408
409
28.3k
                sibling->color = NJS_RBTREE_BLACK;
410
28.3k
                parent->color = NJS_RBTREE_RED;
411
412
28.3k
                njs_rbtree_right_rotate(parent);
413
414
28.3k
                sibling = parent->left;
415
28.3k
            }
416
417
213k
            if (sibling->left->color == NJS_RBTREE_BLACK) {
418
419
157k
                sibling->color = NJS_RBTREE_RED;
420
421
157k
                if (sibling->right->color == NJS_RBTREE_BLACK) {
422
121k
                    node = parent;
423
121k
                    continue;
424
121k
                }
425
426
35.3k
                sibling->right->color = NJS_RBTREE_BLACK;
427
428
35.3k
                njs_rbtree_left_rotate(sibling);
429
430
                /* See the comment in the symmetric branch above. */
431
35.3k
                sibling = parent->left;
432
35.3k
            }
433
434
91.1k
            sibling->color = parent->color;
435
91.1k
            parent->color = NJS_RBTREE_BLACK;
436
91.1k
            sibling->left->color = NJS_RBTREE_BLACK;
437
438
91.1k
            njs_rbtree_right_rotate(parent);
439
440
91.1k
            return;
441
213k
        }
442
738k
    }
443
444
682k
    node->color = NJS_RBTREE_BLACK;
445
682k
}
446
447
448
njs_inline void
449
njs_rbtree_left_rotate(njs_rbtree_node_t *node)
450
3.49M
{
451
3.49M
    njs_rbtree_node_t  *child;
452
453
3.49M
    child = node->right;
454
3.49M
    node->right = child->left;
455
3.49M
    child->left->parent = node;
456
3.49M
    child->left = node;
457
458
3.49M
    njs_rbtree_parent_relink(child, node);
459
460
3.49M
    node->parent = child;
461
3.49M
}
462
463
464
njs_inline void
465
njs_rbtree_right_rotate(njs_rbtree_node_t *node)
466
1.00M
{
467
1.00M
    njs_rbtree_node_t  *child;
468
469
1.00M
    child = node->left;
470
1.00M
    node->left = child->right;
471
1.00M
    child->right->parent = node;
472
1.00M
    child->right = node;
473
474
1.00M
    njs_rbtree_parent_relink(child, node);
475
476
1.00M
    node->parent = child;
477
1.00M
}
478
479
480
/* Relink a parent from the node to the subst node. */
481
482
njs_inline void
483
njs_rbtree_parent_relink(njs_rbtree_node_t *subst, njs_rbtree_node_t *node)
484
6.84M
{
485
6.84M
    njs_rbtree_node_t  *parent, **link;
486
487
6.84M
    parent = node->parent;
488
    /*
489
     * The leaf sentinel's parent can be safely changed here.
490
     * See the comment in njs_rbtree_delete_fixup() for details.
491
     */
492
6.84M
    subst->parent = parent;
493
    /*
494
     * If the node's parent is the root sentinel it is safely changed
495
     * because the root sentinel's left child is the tree root.
496
     */
497
6.84M
    link = (node == parent->left) ? &parent->left : &parent->right;
498
6.84M
    *link = subst;
499
6.84M
}
500
501
502
njs_rbtree_node_t *
503
njs_rbtree_destroy_next(njs_rbtree_t *tree, njs_rbtree_node_t **next)
504
2.02M
{
505
2.02M
    njs_rbtree_node_t  *node, *subst, *parent, *sentinel;
506
507
2.02M
    sentinel = njs_rbtree_sentinel(tree);
508
509
    /* Find the leftmost node. */
510
3.02M
    for (node = *next; node->left != sentinel; node = node->left);
511
512
    /* Replace the leftmost node with its right child. */
513
2.02M
    subst = node->right;
514
2.02M
    parent = node->parent;
515
516
2.02M
    parent->left = subst;
517
2.02M
    subst->parent = parent;
518
519
    /*
520
     * The right child is used as the next start node.  If the right child
521
     * is the sentinel then parent of the leftmost node is used as the next
522
     * start node.  The parent of the root node is the sentinel so after
523
     * the single root node will be replaced with the sentinel, the next
524
     * start node will be equal to the sentinel and iteration will stop.
525
     */
526
2.02M
    if (subst == sentinel) {
527
1.01M
        subst = parent;
528
1.01M
    }
529
530
2.02M
    *next = subst;
531
532
2.02M
    return node;
533
2.02M
}