aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorMichel Lespinasse <walken@google.com>2012-10-08 19:31:11 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-10-09 03:22:37 -0400
commit46b6135a7402ac23c5b25f2bd79b03bab8f98278 (patch)
tree8430c191a455b1ff48c62229731ded4cbc71a9a1 /lib
parent60670b8034d6e2ba860af79c9379b7788d09db73 (diff)
rbtree: handle 1-child recoloring in rb_erase() instead of rb_erase_color()
An interesting observation for rb_erase() is that when a node has exactly one child, the node must be black and the child must be red. An interesting consequence is that removing such a node can be done by simply replacing it with its child and making the child black, which we can do efficiently in rb_erase(). __rb_erase_color() then only needs to handle the no-childs case and can be modified accordingly. Signed-off-by: Michel Lespinasse <walken@google.com> Acked-by: Rik van Riel <riel@redhat.com> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: David Woodhouse <dwmw2@infradead.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'lib')
-rw-r--r--lib/rbtree.c105
1 files changed, 62 insertions, 43 deletions
diff --git a/lib/rbtree.c b/lib/rbtree.c
index bde1b5c5fb33..80b092538fa9 100644
--- a/lib/rbtree.c
+++ b/lib/rbtree.c
@@ -2,7 +2,8 @@
2 Red Black Trees 2 Red Black Trees
3 (C) 1999 Andrea Arcangeli <andrea@suse.de> 3 (C) 1999 Andrea Arcangeli <andrea@suse.de>
4 (C) 2002 David Woodhouse <dwmw2@infradead.org> 4 (C) 2002 David Woodhouse <dwmw2@infradead.org>
5 5 (C) 2012 Michel Lespinasse <walken@google.com>
6
6 This program is free software; you can redistribute it and/or modify 7 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by 8 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or 9 the Free Software Foundation; either version 2 of the License, or
@@ -50,6 +51,11 @@
50#define rb_is_red(r) (!rb_color(r)) 51#define rb_is_red(r) (!rb_color(r))
51#define rb_is_black(r) rb_color(r) 52#define rb_is_black(r) rb_color(r)
52 53
54static inline void rb_set_black(struct rb_node *rb)
55{
56 rb->__rb_parent_color |= RB_BLACK;
57}
58
53static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) 59static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)
54{ 60{
55 rb->__rb_parent_color = rb_color(rb) | (unsigned long)p; 61 rb->__rb_parent_color = rb_color(rb) | (unsigned long)p;
@@ -214,27 +220,18 @@ void rb_insert_color(struct rb_node *node, struct rb_root *root)
214} 220}
215EXPORT_SYMBOL(rb_insert_color); 221EXPORT_SYMBOL(rb_insert_color);
216 222
217static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, 223static void __rb_erase_color(struct rb_node *parent, struct rb_root *root)
218 struct rb_root *root)
219{ 224{
220 struct rb_node *sibling, *tmp1, *tmp2; 225 struct rb_node *node = NULL, *sibling, *tmp1, *tmp2;
221 226
222 while (true) { 227 while (true) {
223 /* 228 /*
224 * Loop invariant: all leaf paths going through node have a 229 * Loop invariants:
225 * black node count that is 1 lower than other leaf paths. 230 * - node is black (or NULL on first iteration)
226 * 231 * - node is not the root (parent is not NULL)
227 * If node is red, we can flip it to black to adjust. 232 * - All leaf paths going through parent and node have a
228 * If node is the root, all leaf paths go through it. 233 * black node count that is 1 lower than other leaf paths.
229 * Otherwise, we need to adjust the tree through color flips
230 * and tree rotations as per one of the 4 cases below.
231 */ 234 */
232 if (node && rb_is_red(node)) {
233 rb_set_parent_color(node, parent, RB_BLACK);
234 break;
235 } else if (!parent) {
236 break;
237 }
238 sibling = parent->rb_right; 235 sibling = parent->rb_right;
239 if (node != sibling) { /* node == parent->rb_left */ 236 if (node != sibling) { /* node == parent->rb_left */
240 if (rb_is_red(sibling)) { 237 if (rb_is_red(sibling)) {
@@ -268,17 +265,22 @@ static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
268 * / \ / \ 265 * / \ / \
269 * Sl Sr Sl Sr 266 * Sl Sr Sl Sr
270 * 267 *
271 * This leaves us violating 5), so 268 * This leaves us violating 5) which
272 * recurse at p. If p is red, the 269 * can be fixed by flipping p to black
273 * recursion will just flip it to black 270 * if it was red, or by recursing at p.
274 * and exit. If coming from Case 1, 271 * p is red when coming from Case 1.
275 * p is known to be red.
276 */ 272 */
277 rb_set_parent_color(sibling, parent, 273 rb_set_parent_color(sibling, parent,
278 RB_RED); 274 RB_RED);
279 node = parent; 275 if (rb_is_red(parent))
280 parent = rb_parent(node); 276 rb_set_black(parent);
281 continue; 277 else {
278 node = parent;
279 parent = rb_parent(node);
280 if (parent)
281 continue;
282 }
283 break;
282 } 284 }
283 /* 285 /*
284 * Case 3 - right rotate at sibling 286 * Case 3 - right rotate at sibling
@@ -339,9 +341,15 @@ static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
339 /* Case 2 - sibling color flip */ 341 /* Case 2 - sibling color flip */
340 rb_set_parent_color(sibling, parent, 342 rb_set_parent_color(sibling, parent,
341 RB_RED); 343 RB_RED);
342 node = parent; 344 if (rb_is_red(parent))
343 parent = rb_parent(node); 345 rb_set_black(parent);
344 continue; 346 else {
347 node = parent;
348 parent = rb_parent(node);
349 if (parent)
350 continue;
351 }
352 break;
345 } 353 }
346 /* Case 3 - right rotate at sibling */ 354 /* Case 3 - right rotate at sibling */
347 sibling->rb_right = tmp1 = tmp2->rb_left; 355 sibling->rb_right = tmp1 = tmp2->rb_left;
@@ -369,23 +377,31 @@ static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
369void rb_erase(struct rb_node *node, struct rb_root *root) 377void rb_erase(struct rb_node *node, struct rb_root *root)
370{ 378{
371 struct rb_node *child = node->rb_right, *tmp = node->rb_left; 379 struct rb_node *child = node->rb_right, *tmp = node->rb_left;
372 struct rb_node *parent; 380 struct rb_node *parent, *rebalance;
373 int color;
374 381
375 if (!tmp) { 382 if (!tmp) {
376 case1: 383 /*
377 /* Case 1: node to erase has no more than 1 child (easy!) */ 384 * Case 1: node to erase has no more than 1 child (easy!)
385 *
386 * Note that if there is one child it must be red due to 5)
387 * and node must be black due to 4). We adjust colors locally
388 * so as to bypass __rb_erase_color() later on.
389 */
378 390
379 parent = rb_parent(node); 391 parent = rb_parent(node);
380 color = rb_color(node);
381
382 if (child)
383 rb_set_parent(child, parent);
384 __rb_change_child(node, child, parent, root); 392 __rb_change_child(node, child, parent, root);
393 if (child) {
394 rb_set_parent_color(child, parent, RB_BLACK);
395 rebalance = NULL;
396 } else {
397 rebalance = rb_is_black(node) ? parent : NULL;
398 }
385 } else if (!child) { 399 } else if (!child) {
386 /* Still case 1, but this time the child is node->rb_left */ 400 /* Still case 1, but this time the child is node->rb_left */
387 child = tmp; 401 parent = rb_parent(node);
388 goto case1; 402 __rb_change_child(node, tmp, parent, root);
403 rb_set_parent_color(tmp, parent, RB_BLACK);
404 rebalance = NULL;
389 } else { 405 } else {
390 struct rb_node *old = node, *left; 406 struct rb_node *old = node, *left;
391 407
@@ -397,26 +413,29 @@ void rb_erase(struct rb_node *node, struct rb_root *root)
397 413
398 child = node->rb_right; 414 child = node->rb_right;
399 parent = rb_parent(node); 415 parent = rb_parent(node);
400 color = rb_color(node);
401 416
402 if (parent == old) { 417 if (parent == old) {
403 parent = node; 418 parent = node;
404 } else { 419 } else {
405 if (child)
406 rb_set_parent(child, parent);
407 parent->rb_left = child; 420 parent->rb_left = child;
408 421
409 node->rb_right = old->rb_right; 422 node->rb_right = old->rb_right;
410 rb_set_parent(old->rb_right, node); 423 rb_set_parent(old->rb_right, node);
411 } 424 }
412 425
426 if (child) {
427 rb_set_parent_color(child, parent, RB_BLACK);
428 rebalance = NULL;
429 } else {
430 rebalance = rb_is_black(node) ? parent : NULL;
431 }
413 node->__rb_parent_color = old->__rb_parent_color; 432 node->__rb_parent_color = old->__rb_parent_color;
414 node->rb_left = old->rb_left; 433 node->rb_left = old->rb_left;
415 rb_set_parent(old->rb_left, node); 434 rb_set_parent(old->rb_left, node);
416 } 435 }
417 436
418 if (color == RB_BLACK) 437 if (rebalance)
419 __rb_erase_color(child, parent, root); 438 __rb_erase_color(rebalance, root);
420} 439}
421EXPORT_SYMBOL(rb_erase); 440EXPORT_SYMBOL(rb_erase);
422 441