diff options
author | Michel Lespinasse <walken@google.com> | 2012-10-08 19:31:11 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-09 03:22:37 -0400 |
commit | 46b6135a7402ac23c5b25f2bd79b03bab8f98278 (patch) | |
tree | 8430c191a455b1ff48c62229731ded4cbc71a9a1 /lib | |
parent | 60670b8034d6e2ba860af79c9379b7788d09db73 (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.c | 105 |
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 | ||
54 | static inline void rb_set_black(struct rb_node *rb) | ||
55 | { | ||
56 | rb->__rb_parent_color |= RB_BLACK; | ||
57 | } | ||
58 | |||
53 | static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) | 59 | static 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 | } |
215 | EXPORT_SYMBOL(rb_insert_color); | 221 | EXPORT_SYMBOL(rb_insert_color); |
216 | 222 | ||
217 | static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, | 223 | static 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, | |||
369 | void rb_erase(struct rb_node *node, struct rb_root *root) | 377 | void 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 | } |
421 | EXPORT_SYMBOL(rb_erase); | 440 | EXPORT_SYMBOL(rb_erase); |
422 | 441 | ||