summaryrefslogtreecommitdiffstats
path: root/klib/rbtree.c
blob: 29203841ccf5bf957dc6fa1948c2ae2c2330c481 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
#include "rbtree.h"
#include "assert.h"
#include "string.h"

#define BLACK 0
#define RED 1

#define LEFT 0
#define left children[LEFT]

#define RIGHT 1
#define right children[RIGHT]

#define CHILD_DIR(c) (c->parent->children[LEFT] == c ? LEFT : RIGHT)

/**Given a root and a direction, rotate the subtree such that the order of the
 * elements are unchanged.
 * The opposite direction child of the given root is treated as the pivot point
 * The dir-most child of the pivot becomes the opposite dir child of the root.
 * The pivot becomes the root and vice-versa.
 * @param tree to do rotation on.
 * @param root of rotation.
 * @param dir to rotate by.
 * @return new root node.*/
rbnode_t*
s_node_rotate(rbtree_t *tree, rbnode_t *root, bool dir)
{
    assert(root != NULL);
    rbnode_t *gparent = root->parent;
    rbnode_t *sibling = root->children[1-dir];
    rbnode_t *child = NULL;

    assert(sibling != NULL);
    child = sibling->children[dir];

    /* Opposite child of root is dir-most child of sibling.*/
    root->children[1-dir] = child;
    if(child != NULL) child->parent = root;

    /* Child of sibling is the root.*/
    sibling->children[  dir] = root;
    root->parent = sibling;

    /* Parent of sibling is the grandparent. */
    sibling->parent = gparent;
    if(gparent != NULL)
        gparent->children[CHILD_DIR(root)] = sibling;
    else
        tree->root = sibling;

    return sibling;
}

/**Search the rbtree for a node that matches the key.
 * If no such node is found, returns NULL.
 * @param tree to search through.
 * @param key to search for.
 * @return found node.*/
rbnode_t*
s_find(rbtree_t *tree, intmax_t key)
{
    rbnode_t *node = tree->root;
    while(node != NULL && node->key != key) {
        node = node->children[node->key > key];
    }
    return node;
}

rbnode_t*
s_closest(rbtree_t *tree, intmax_t key)
{
    rbnode_t *node = tree->root;
    while(node != NULL && node->key != key) {
        rbnode_t *child = node->children[node->key > key];
        if(child == NULL) return node;
        node = child;
    }
    return node;
}

/**Find the left-most node for this subtree.
 * @param node root of the subtree to search through.
 * @return left-most node.*/
rbnode_t*
s_node_min(rbnode_t *node)
{
    while(node->left != NULL) node = node->left;
    return node;
}

/**Find the successor for the specified node.
 * The successor is defined as the left-most node of the right sub-tree.
 * (the smallest node larger than the root)
 * @param node to search for.
 * @return the successor.*/
rbnode_t*
s_node_successor(rbnode_t *node)
{
    if(node->right != NULL)
        return s_node_min(node->right);
    /* If the node has no left child (meaning that the node is larger than it's
     * parents), we cycle up through the RHS of the parent tree (such that each
     * parent is smaller than the child) until we reach a point by which the 
     * child node is smaller than the parent.*/
    rbnode_t *parent = node->parent;
    while(parent != NULL && node == parent->right) {
        node = parent;
        parent = node->parent;
    }
    return parent;
}

/**Copy the data from one node onto another, keeping parent and child relations.
 * @prarm tree for metadata purposes.
 * @param dest destination node.
 * @param src source node.*/
void
s_node_copy_value(rbtree_t *tree, rbnode_t *dest, rbnode_t *src)
{
    dest->key = src->key;
    memcpy(dest->data, src->data, tree->objw);
}

/**Perform tree adjustments after insertion to rebalance the rbtree.
 * @param tree to rebalance.
 * @param node that was inserted.
 * @param parent of the node.
 * @param dir that the node was inserted in.*/
void
s_node_insert_fix(
        rbtree_t *tree,
        rbnode_t *node,
        rbnode_t *parent,
        bool dir)
{
    rbnode_t *gparent;
    rbnode_t *uncle;
    while((parent = node->parent) != NULL) {
        if(parent->color == BLACK) return; /* Tree is valid. no fixes needed.*/
        /*Parent is known to be red.*/
        if((gparent = parent->parent) == NULL) {
            /*Parent is red and the root. We can just fix it and return.*/
            parent->color = BLACK;
            return;
        }
        /* Parent is red and has a parent.
         * We now need to fix the parent.*/
        dir = CHILD_DIR(parent);
        uncle = gparent->children[1-dir];
        if(uncle == NULL || uncle->color == BLACK) {
            /* Parent is red, but uncle (opposite child of gparent) is black.
             * We need to rotate the gparent node such that the parent takes
             * its place. However, if the current node is an inner child of 
             * gparent, we need to rotate N into P.*/
            if(node == parent->children[1-dir]) {
                /* Node is an inner child. We need to rotate N and P first.*/
                s_node_rotate(tree, parent, dir);
                node = parent;
                parent = gparent->children[dir];
            }
            /* N is not an inner child, we can rotate the tree properly.*/
            s_node_rotate(tree, gparent, 1-dir);
            parent->color = BLACK;
            gparent->color = RED;
            return;
        }
    }
}

/**Insert a node into the rbtree on the parent and direction given.
 * @param tree to insert into.
 * @param node to insert.
 * @param parent node to insert onto.
 * @param dir direction to insert node.*/
void
s_node_insert(rbtree_t *tree, rbnode_t *node, rbnode_t *parent, bool dir)
{
    node->color = RED;
    node->left = NULL;
    node->right = NULL;
    node->parent = parent;
    if(parent == NULL) {
        tree->root = node;
        return;
    }
    parent->children[dir] = node;
    s_node_insert_fix(tree, node, parent, dir);
}

void
s_node_remove_fixup(rbtree_t *tree, rbnode_t *node)
{
    bool dir;
    rbnode_t *parent = node->parent;
    rbnode_t *sibling;
    rbnode_t *cnephew;
    rbnode_t *dnephew;

    dir = CHILD_DIR(node);
    parent->children[dir] = NULL;
    while((parent = node->parent) != NULL)
    {
        dir = CHILD_DIR(node);
        sibling = parent->children[1-dir];
        cnephew = parent->children[  dir];
        dnephew = parent->children[1-dir];
        if(sibling->color == RED)
            goto case3;
        if(dnephew != NULL && dnephew->color == RED)
            goto case6;
        if(cnephew != NULL && cnephew->color == RED)
            goto case5;
        if(parent->color == RED)
            goto case4;

        /* The parent, both nephews, and sibling are black. We need to paint
         * the sibling black and move up a level.*/
        sibling->color = RED;
        node = parent;
    }
    return;

case3:
    /* The sibling is red, which means both nephews and the parent are black.
     * We rotate the subtree so that the parent moves to become the siblings
     * left child. The tree is then repainted.*/
    s_node_rotate(tree, parent, dir);
    parent->color = RED;
    sibling->color = BLACK;
    /* Now we are working on the sibling subtree.*/
    sibling = cnephew;
    dnephew = sibling->children[1-dir];
    if(dnephew != NULL && dnephew->color == BLACK)
        goto case6;
    cnephew = sibling->children[dir];
    if(cnephew != NULL && cnephew->color == RED)
        goto case5;

case4:
    sibling->color = RED;
    parent->color = BLACK;
    return;

case5:
    s_node_rotate(tree, sibling, 1-dir);
    sibling->color = RED;
    cnephew->color = BLACK;
    dnephew = sibling;
    sibling = cnephew;

case6:
    s_node_rotate(tree, parent, dir);
    sibling->color = parent->color;
    parent->color = BLACK;
    dnephew->color = BLACK;
}

void
s_node_remove(rbtree_t *tree, rbnode_t *node)
{
    rbnode_t *parent = node->parent;
    if(node->left != NULL && node->right != NULL) {
        /* Node is a full tree. We can replace the node with it's successor to
         * keep the balance.
         * Needs to recurse if the successor has a right-most child, which is
         * dealt with in the next case.*/
        rbnode_t *successor = s_node_successor(node);
        s_node_copy_value(tree, node, successor);
        s_node_remove(tree, successor);
        return;
    } else if(node->left == NULL && node->right == NULL) {
        if(parent == NULL) {
            tree->root = NULL;
            kfree(node);
            return;
        }
        if(node->color == RED) {
            parent->children[CHILD_DIR(node)] = NULL;
            kfree(node);
            return;
        }
        s_node_remove_fixup(tree, node);
    } else {
        /* Node has a single child. Because the child must be red as per spec,
         * and this node must be black, we can simply replace the node
         * with it's child and color it black.*/
        rbnode_t *child = node->left == NULL ? node->right : node->left;
        memcpy(node, child, sizeof(rbnode_t) + tree->objw);
        node->parent = parent;
        node->color = BLACK;
        kfree(child);
        return;
    }
}

void
__rbtree_new(rbtree_t *tree, size_t objw)
{
    *tree = (rbtree_t) {
        .root = NULL,
        .objw = objw,
    };
}

void*
rbtree_find(rbtree_t *tree, intmax_t key)
{
    rbnode_t *found = s_find(tree, key);
    if(found == NULL) return NULL;
    return found->data;
}

rbnode_t*
s_node_new(rbtree_t *tree, intmax_t key, void *data)
{
    rbnode_t *node = kmalloc(sizeof(rbnode_t) + tree->objw);
    if(data != NULL) memcpy(node->data, data, tree->objw);
    node->key = key;
    return node;
}

void*
rbtree_insert(rbtree_t *tree, intmax_t key, void *data)
{
    assert(tree != NULL);

    rbnode_t *node = s_closest(tree, key);
    if(node != NULL && node->key == key) {
        memcpy(node->data, data, tree->objw);
    } else if(node != NULL) {
        rbnode_t *child = s_node_new(tree, key, data);
        s_node_insert(tree, child, node, node->key > key);
        return child->data;
    } else {
        node = s_node_new(tree, key, data);
        node->parent = NULL;
        node->children[0] = node->children[1] = NULL;
        node->color = BLACK;
        tree->root = node;
    }
    return node->data;
}

void *rbtree_reserve(rbtree_t *tree, intmax_t key)
{
    assert(tree != NULL);

    rbnode_t *node = s_closest(tree, key);
    if(node != NULL && node->key == key) {
        return node->data;
    } else if(node != NULL) {
        rbnode_t *child = s_node_new(tree, key, NULL);
        s_node_insert(tree, child, node, node->key > key);
        return child->data;
    } else {
        node = s_node_new(tree, key, NULL);
        tree->root = node;
    }
    return node->data;
}