blob: b86a589b4d4986ab41d9611d8e8e2afeae70ffee [file] [log] [blame]
/*
* rbtree - Implements a red-black tree with parent pointers.
*
* Copyright (C) 2010-2014 Franck Bui-Huu <fbuihuu@gmail.com>
*
* This file is part of libtree which is free software; you can
* redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the LICENSE file for license rights and limitations.
*/
/*
* For recall a red-black tree has the following properties:
*
* 1. All nodes are either BLACK or RED
* 2. Leafs are BLACK
* 3. A RED node has BLACK children only
* 4. Path from a node to any leafs has the same number of BLACK nodes.
*
*/
#include "avltree.h"
/*
* Some helpers
*/
#ifdef UINTPTR_MAX
static inline enum rb_color get_color(const struct rbtree_node *node)
{
return node->parent & 1;
}
static inline void set_color(enum rb_color color, struct rbtree_node *node)
{
node->parent = (node->parent & ~1UL) | color;
}
static inline struct rbtree_node *get_parent(const struct rbtree_node *node)
{
return (struct rbtree_node *)(node->parent & ~1UL);
}
static inline void set_parent(struct rbtree_node *parent,
struct rbtree_node *node)
{
node->parent = (uintptr_t) parent | (node->parent & 1);
}
#else
static inline enum rb_color get_color(const struct rbtree_node *node)
{
return node->color;
}
static inline void set_color(enum rb_color color, struct rbtree_node *node)
{
node->color = color;
}
static inline struct rbtree_node *get_parent(const struct rbtree_node *node)
{
return node->parent;
}
static inline void set_parent(struct rbtree_node *parent,
struct rbtree_node *node)
{
node->parent = parent;
}
#endif /* UINTPTR_MAX */
static inline int is_root(struct rbtree_node *node)
{
return get_parent(node) == NULL;
}
static inline int is_black(struct rbtree_node *node)
{
return get_color(node) == RB_BLACK;
}
static inline int is_red(struct rbtree_node *node)
{
return !is_black(node);
}
/*
* Iterators
*/
static inline struct rbtree_node *get_first(struct rbtree_node *node)
{
while (node->left)
node = node->left;
return node;
}
static inline struct rbtree_node *get_last(struct rbtree_node *node)
{
while (node->right)
node = node->right;
return node;
}
struct rbtree_node *rbtree_first(const struct rbtree *tree)
{
return tree->first;
}
struct rbtree_node *rbtree_last(const struct rbtree *tree)
{
return tree->last;
}
struct rbtree_node *rbtree_next(const struct rbtree_node *node)
{
struct rbtree_node *parent;
if (node->right)
return get_first(node->right);
while ((parent = get_parent(node)) && parent->right == node)
node = parent;
return parent;
}
struct rbtree_node *rbtree_prev(const struct rbtree_node *node)
{
struct rbtree_node *parent;
if (node->left)
return get_last(node->left);
while ((parent = get_parent(node)) && parent->left == node)
node = parent;
return parent;
}
/*
* 'pparent' and 'is_left' are only used for insertions. Normally GCC
* will notice this and get rid of them for lookups.
*/
static inline struct rbtree_node *do_lookup(const struct rbtree_node *key,
const struct rbtree *tree,
struct rbtree_node **pparent,
int *is_left)
{
struct rbtree_node *node = tree->root;
*pparent = NULL;
*is_left = 0;
while (node) {
int res = tree->cmp_fn(node, key);
if (res == 0)
return node;
*pparent = node;
if ((*is_left = res > 0))
node = node->left;
else
node = node->right;
}
return NULL;
}
/*
* Rotate operations (They preserve the binary search tree property,
* assuming that the keys are unique).
*/
static void rotate_left(struct rbtree_node *node, struct rbtree *tree)
{
struct rbtree_node *p = node;
struct rbtree_node *q = node->right; /* can't be NULL */
struct rbtree_node *parent = get_parent(p);
if (!is_root(p)) {
if (parent->left == p)
parent->left = q;
else
parent->right = q;
} else
tree->root = q;
set_parent(parent, q);
set_parent(q, p);
p->right = q->left;
if (p->right)
set_parent(p, p->right);
q->left = p;
}
static void rotate_right(struct rbtree_node *node, struct rbtree *tree)
{
struct rbtree_node *p = node;
struct rbtree_node *q = node->left; /* can't be NULL */
struct rbtree_node *parent = get_parent(p);
if (!is_root(p)) {
if (parent->left == p)
parent->left = q;
else
parent->right = q;
} else
tree->root = q;
set_parent(parent, q);
set_parent(q, p);
p->left = q->right;
if (p->left)
set_parent(p, p->left);
q->right = p;
}
struct rbtree_node *rbtree_lookup(const struct rbtree_node *key,
const struct rbtree *tree)
{
struct rbtree_node *parent;
int is_left;
return do_lookup(key, tree, &parent, &is_left);
}
static void set_child(struct rbtree_node *child, struct rbtree_node *node,
int left)
{
if (left)
node->left = child;
else
node->right = child;
}
struct rbtree_node *rbtree_insert(struct rbtree_node *node, struct rbtree *tree)
{
struct rbtree_node *key, *parent;
int is_left;
key = do_lookup(node, tree, &parent, &is_left);
if (key)
return key;
node->left = NULL;
node->right = NULL;
set_color(RB_RED, node);
set_parent(parent, node);
if (parent) {
if (is_left) {
if (parent == tree->first)
tree->first = node;
} else {
if (parent == tree->last)
tree->last = node;
}
set_child(node, parent, is_left);
} else {
tree->root = node;
tree->first = node;
tree->last = node;
}
/*
* Fixup the modified tree by recoloring nodes and performing
* rotations (2 at most) hence the red-black tree properties are
* preserved.
*/
while ((parent = get_parent(node)) && is_red(parent)) {
struct rbtree_node *grandpa = get_parent(parent);
if (parent == grandpa->left) {
struct rbtree_node *uncle = grandpa->right;
if (uncle && is_red(uncle)) {
set_color(RB_BLACK, parent);
set_color(RB_BLACK, uncle);
set_color(RB_RED, grandpa);
node = grandpa;
} else {
if (node == parent->right) {
rotate_left(parent, tree);
node = parent;
parent = get_parent(node);
}
set_color(RB_BLACK, parent);
set_color(RB_RED, grandpa);
rotate_right(grandpa, tree);
}
} else {
struct rbtree_node *uncle = grandpa->left;
if (uncle && is_red(uncle)) {
set_color(RB_BLACK, parent);
set_color(RB_BLACK, uncle);
set_color(RB_RED, grandpa);
node = grandpa;
} else {
if (node == parent->left) {
rotate_right(parent, tree);
node = parent;
parent = get_parent(node);
}
set_color(RB_BLACK, parent);
set_color(RB_RED, grandpa);
rotate_left(grandpa, tree);
}
}
}
set_color(RB_BLACK, tree->root);
return NULL;
}
void rbtree_remove(struct rbtree_node *node, struct rbtree *tree)
{
struct rbtree_node *parent = get_parent(node);
struct rbtree_node *left = node->left;
struct rbtree_node *right = node->right;
struct rbtree_node *next;
enum rb_color color;
if (node == tree->first)
tree->first = rbtree_next(node);
if (node == tree->last)
tree->last = rbtree_prev(node);
if (!left)
next = right;
else if (!right)
next = left;
else
next = get_first(right);
if (parent)
set_child(next, parent, parent->left == node);
else
tree->root = next;
if (left && right) {
color = get_color(next);
set_color(get_color(node), next);
next->left = left;
set_parent(next, left);
if (next != right) {
parent = get_parent(next);
set_parent(get_parent(node), next);
node = next->right;
parent->left = node;
next->right = right;
set_parent(next, right);
} else {
set_parent(parent, next);
parent = next;
node = next->right;
}
} else {
color = get_color(node);
node = next;
}
/*
* 'node' is now the sole successor's child and 'parent' its
* new parent (since the successor can have been moved).
*/
if (node)
set_parent(parent, node);
/*
* The 'easy' cases.
*/
if (color == RB_RED)
return;
if (node && is_red(node)) {
set_color(RB_BLACK, node);
return;
}
do {
if (node == tree->root)
break;
if (node == parent->left) {
struct rbtree_node *sibling = parent->right;
if (is_red(sibling)) {
set_color(RB_BLACK, sibling);
set_color(RB_RED, parent);
rotate_left(parent, tree);
sibling = parent->right;
}
if ((!sibling->left || is_black(sibling->left))
&& (!sibling->right || is_black(sibling->right))) {
set_color(RB_RED, sibling);
node = parent;
parent = get_parent(parent);
continue;
}
if (!sibling->right || is_black(sibling->right)) {
set_color(RB_BLACK, sibling->left);
set_color(RB_RED, sibling);
rotate_right(sibling, tree);
sibling = parent->right;
}
set_color(get_color(parent), sibling);
set_color(RB_BLACK, parent);
set_color(RB_BLACK, sibling->right);
rotate_left(parent, tree);
node = tree->root;
break;
} else {
struct rbtree_node *sibling = parent->left;
if (is_red(sibling)) {
set_color(RB_BLACK, sibling);
set_color(RB_RED, parent);
rotate_right(parent, tree);
sibling = parent->left;
}
if ((!sibling->left || is_black(sibling->left))
&& (!sibling->right || is_black(sibling->right))) {
set_color(RB_RED, sibling);
node = parent;
parent = get_parent(parent);
continue;
}
if (!sibling->left || is_black(sibling->left)) {
set_color(RB_BLACK, sibling->right);
set_color(RB_RED, sibling);
rotate_left(sibling, tree);
sibling = parent->left;
}
set_color(get_color(parent), sibling);
set_color(RB_BLACK, parent);
set_color(RB_BLACK, sibling->left);
rotate_right(parent, tree);
node = tree->root;
break;
}
} while (is_black(node));
if (node)
set_color(RB_BLACK, node);
}
void rbtree_replace(struct rbtree_node *old, struct rbtree_node *new,
struct rbtree *tree)
{
struct rbtree_node *parent = get_parent(old);
if (parent)
set_child(new, parent, parent->left == old);
else
tree->root = new;
if (old->left)
set_parent(new, old->left);
if (old->right)
set_parent(new, old->right);
if (tree->first == old)
tree->first = new;
if (tree->last == old)
tree->last = new;
*new = *old;
}
int rbtree_init(struct rbtree *tree, rbtree_cmp_fn_t fn, unsigned long flags)
{
if (flags)
return -1;
tree->root = NULL;
tree->cmp_fn = fn;
tree->first = NULL;
tree->last = NULL;
return 0;
}