Add rb_tree_find_node_{geq,leq}. Add stats. Make TAILQ DEBUG only.
Keep track of tree's min/max nodes for easier iteration. Improve comments. Move functions around to improve locality.
This commit is contained in:
parent
cec3b712bd
commit
75de26ab17
|
@ -1,4 +1,4 @@
|
||||||
/* $NetBSD: rb.c,v 1.8 2006/09/08 04:07:15 matt Exp $ */
|
/* $NetBSD: rb.c,v 1.9 2006/09/09 05:55:51 matt Exp $ */
|
||||||
|
|
||||||
/*-
|
/*-
|
||||||
* Copyright (c) 2001 The NetBSD Foundation, Inc.
|
* Copyright (c) 2001 The NetBSD Foundation, Inc.
|
||||||
|
@ -68,7 +68,7 @@ static const struct rb_node sentinel_node = {
|
||||||
.rb_nodes[0] = __UNCONST(&sentinel_node),
|
.rb_nodes[0] = __UNCONST(&sentinel_node),
|
||||||
.rb_nodes[1] = __UNCONST(&sentinel_node),
|
.rb_nodes[1] = __UNCONST(&sentinel_node),
|
||||||
.rb_sentinel = 1
|
.rb_sentinel = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_tree_init(struct rb_tree *rbt, rb_compare_nodes_fn compare_nodes,
|
rb_tree_init(struct rb_tree *rbt, rb_compare_nodes_fn compare_nodes,
|
||||||
|
@ -79,72 +79,77 @@ rb_tree_init(struct rb_tree *rbt, rb_compare_nodes_fn compare_nodes,
|
||||||
rbt->rbt_compare_nodes = compare_nodes;
|
rbt->rbt_compare_nodes = compare_nodes;
|
||||||
rbt->rbt_compare_key = compare_key;
|
rbt->rbt_compare_key = compare_key;
|
||||||
*((const struct rb_node **)&rbt->rbt_root) = &sentinel_node;
|
*((const struct rb_node **)&rbt->rbt_root) = &sentinel_node;
|
||||||
|
rbt->rbt_minmax[RB_LEFT] = rbt->rbt_root; /* minimum node */
|
||||||
|
rbt->rbt_minmax[RB_RIGHT] = rbt->rbt_root; /* maximum node */
|
||||||
|
#ifdef RBSTATS
|
||||||
|
rbt->rbt_insertions = 0;
|
||||||
|
rbt->rbt_removals = 0;
|
||||||
|
rbt->rbt_insertion_rebalance_calls = 0;
|
||||||
|
rbt->rbt_insertion_rebalance_passes = 0;
|
||||||
|
rbt->rbt_removal_rebalance_calls = 0;
|
||||||
|
rbt->rbt_removal_rebalance_passes = 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
struct rb_node *
|
||||||
* Swap the location and colors of 'self' and its child @ which. The child
|
rb_tree_find_node(struct rb_tree *rbt, const void *key)
|
||||||
* can not be a sentinel node.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
rb_tree_reparent_nodes(struct rb_tree *rbt, struct rb_node *old_father, int which)
|
|
||||||
{
|
{
|
||||||
const unsigned int other = which ^ RB_OTHER;
|
struct rb_node *parent = rbt->rbt_root;
|
||||||
struct rb_node * const grandpa = old_father->rb_parent;
|
|
||||||
struct rb_node * const old_child = old_father->rb_nodes[which];
|
|
||||||
struct rb_node * const new_father = old_child;
|
|
||||||
struct rb_node * const new_child = old_father;
|
|
||||||
unsigned int properties;
|
|
||||||
|
|
||||||
KASSERT(which == RB_LEFT || which == RB_RIGHT);
|
while (!RB_SENTINEL_P(parent)) {
|
||||||
|
const int diff = (*rbt->rbt_compare_key)(parent, key);
|
||||||
KASSERT(!RB_SENTINEL_P(old_child));
|
if (diff == 0)
|
||||||
KASSERT(old_child->rb_parent == old_father);
|
return parent;
|
||||||
|
parent = parent->rb_nodes[diff > 0];
|
||||||
KASSERT(rb_tree_check_node(rbt, old_father, NULL, false));
|
|
||||||
KASSERT(rb_tree_check_node(rbt, old_child, NULL, false));
|
|
||||||
KASSERT(RB_ROOT_P(old_father) || rb_tree_check_node(rbt, grandpa, NULL, false));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Exchange descendant linkages.
|
|
||||||
*/
|
|
||||||
grandpa->rb_nodes[old_father->rb_position] = new_father;
|
|
||||||
new_child->rb_nodes[which] = old_child->rb_nodes[other];
|
|
||||||
new_father->rb_nodes[other] = new_child;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Update ancestor linkages
|
|
||||||
*/
|
|
||||||
new_father->rb_parent = grandpa;
|
|
||||||
new_child->rb_parent = new_father;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Exchange properties between new_father and new_child. The only
|
|
||||||
* change is that new_child's position is now on the other side.
|
|
||||||
*/
|
|
||||||
properties = old_child->rb_properties;
|
|
||||||
new_father->rb_properties = old_father->rb_properties;
|
|
||||||
new_child->rb_properties = properties;
|
|
||||||
new_child->rb_position = other;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Make sure to reparent the new child to ourself.
|
|
||||||
*/
|
|
||||||
if (!RB_SENTINEL_P(new_child->rb_nodes[which])) {
|
|
||||||
new_child->rb_nodes[which]->rb_parent = new_child;
|
|
||||||
new_child->rb_nodes[which]->rb_position = which;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KASSERT(rb_tree_check_node(rbt, new_father, NULL, false));
|
return NULL;
|
||||||
KASSERT(rb_tree_check_node(rbt, new_child, NULL, false));
|
|
||||||
KASSERT(RB_ROOT_P(new_father) || rb_tree_check_node(rbt, grandpa, NULL, false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct rb_node *
|
||||||
|
rb_tree_find_node_geq(struct rb_tree *rbt, const void *key)
|
||||||
|
{
|
||||||
|
struct rb_node *parent = rbt->rbt_root;
|
||||||
|
struct rb_node *last = NULL;
|
||||||
|
|
||||||
|
while (!RB_SENTINEL_P(parent)) {
|
||||||
|
const int diff = (*rbt->rbt_compare_key)(parent, key);
|
||||||
|
if (diff == 0)
|
||||||
|
return parent;
|
||||||
|
if (diff < 0)
|
||||||
|
last = parent;
|
||||||
|
parent = parent->rb_nodes[diff > 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rb_node *
|
||||||
|
rb_tree_find_node_leq(struct rb_tree *rbt, const void *key)
|
||||||
|
{
|
||||||
|
struct rb_node *parent = rbt->rbt_root;
|
||||||
|
struct rb_node *last = NULL;
|
||||||
|
|
||||||
|
while (!RB_SENTINEL_P(parent)) {
|
||||||
|
const int diff = (*rbt->rbt_compare_key)(parent, key);
|
||||||
|
if (diff == 0)
|
||||||
|
return parent;
|
||||||
|
if (diff > 0)
|
||||||
|
last = parent;
|
||||||
|
parent = parent->rb_nodes[diff > 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_tree_insert_node(struct rb_tree *rbt, struct rb_node *self)
|
rb_tree_insert_node(struct rb_tree *rbt, struct rb_node *self)
|
||||||
{
|
{
|
||||||
struct rb_node *parent, *tmp;
|
struct rb_node *parent, *tmp;
|
||||||
unsigned int position;
|
unsigned int position;
|
||||||
|
|
||||||
|
RBSTAT_INC(rbt->rbt_insertions);
|
||||||
|
|
||||||
self->rb_properties = 0;
|
self->rb_properties = 0;
|
||||||
tmp = rbt->rbt_root;
|
tmp = rbt->rbt_root;
|
||||||
/*
|
/*
|
||||||
|
@ -206,9 +211,18 @@ rb_tree_insert_node(struct rb_tree *rbt, struct rb_node *self)
|
||||||
self->rb_position = position;
|
self->rb_position = position;
|
||||||
if (__predict_false(parent == (struct rb_node *) &rbt->rbt_root)) {
|
if (__predict_false(parent == (struct rb_node *) &rbt->rbt_root)) {
|
||||||
RB_MARK_ROOT(self);
|
RB_MARK_ROOT(self);
|
||||||
|
rbt->rbt_minmax[RB_LEFT] = self;
|
||||||
|
rbt->rbt_minmax[RB_RIGHT] = self;
|
||||||
} else {
|
} else {
|
||||||
KASSERT(position == RB_LEFT || position == RB_RIGHT);
|
KASSERT(position == RB_LEFT || position == RB_RIGHT);
|
||||||
KASSERT(!RB_ROOT_P(self)); /* Already done */
|
KASSERT(!RB_ROOT_P(self)); /* Already done */
|
||||||
|
/*
|
||||||
|
* Keep track of the minimum and maximum nodes. If our
|
||||||
|
* parent is a minmax node and we on their min/max side,
|
||||||
|
* we must be the new min/max node.
|
||||||
|
*/
|
||||||
|
if (parent == rbt->rbt_minmax[position])
|
||||||
|
rbt->rbt_minmax[position] = self;
|
||||||
}
|
}
|
||||||
KASSERT(RB_SENTINEL_P(parent->rb_nodes[position]));
|
KASSERT(RB_SENTINEL_P(parent->rb_nodes[position]));
|
||||||
self->rb_left = parent->rb_nodes[position];
|
self->rb_left = parent->rb_nodes[position];
|
||||||
|
@ -254,10 +268,72 @@ rb_tree_insert_node(struct rb_tree *rbt, struct rb_node *self)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Swap the location and colors of 'self' and its child @ which. The child
|
||||||
|
* can not be a sentinel node. This is our rotation function. However,
|
||||||
|
* since it preserves coloring, it great simplifies both insertion and
|
||||||
|
* removal since rotation almost always involves the exchanging of colors
|
||||||
|
* as a separate step.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
rb_tree_reparent_nodes(struct rb_tree *rbt, struct rb_node *old_father, int which)
|
||||||
|
{
|
||||||
|
const unsigned int other = which ^ RB_OTHER;
|
||||||
|
struct rb_node * const grandpa = old_father->rb_parent;
|
||||||
|
struct rb_node * const old_child = old_father->rb_nodes[which];
|
||||||
|
struct rb_node * const new_father = old_child;
|
||||||
|
struct rb_node * const new_child = old_father;
|
||||||
|
unsigned int properties;
|
||||||
|
|
||||||
|
KASSERT(which == RB_LEFT || which == RB_RIGHT);
|
||||||
|
|
||||||
|
KASSERT(!RB_SENTINEL_P(old_child));
|
||||||
|
KASSERT(old_child->rb_parent == old_father);
|
||||||
|
|
||||||
|
KASSERT(rb_tree_check_node(rbt, old_father, NULL, false));
|
||||||
|
KASSERT(rb_tree_check_node(rbt, old_child, NULL, false));
|
||||||
|
KASSERT(RB_ROOT_P(old_father) || rb_tree_check_node(rbt, grandpa, NULL, false));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exchange descendant linkages.
|
||||||
|
*/
|
||||||
|
grandpa->rb_nodes[old_father->rb_position] = new_father;
|
||||||
|
new_child->rb_nodes[which] = old_child->rb_nodes[other];
|
||||||
|
new_father->rb_nodes[other] = new_child;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update ancestor linkages
|
||||||
|
*/
|
||||||
|
new_father->rb_parent = grandpa;
|
||||||
|
new_child->rb_parent = new_father;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exchange properties between new_father and new_child. The only
|
||||||
|
* change is that new_child's position is now on the other side.
|
||||||
|
*/
|
||||||
|
properties = old_child->rb_properties;
|
||||||
|
new_father->rb_properties = old_father->rb_properties;
|
||||||
|
new_child->rb_properties = properties;
|
||||||
|
new_child->rb_position = other;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure to reparent the new child to ourself.
|
||||||
|
*/
|
||||||
|
if (!RB_SENTINEL_P(new_child->rb_nodes[which])) {
|
||||||
|
new_child->rb_nodes[which]->rb_parent = new_child;
|
||||||
|
new_child->rb_nodes[which]->rb_position = which;
|
||||||
|
}
|
||||||
|
|
||||||
|
KASSERT(rb_tree_check_node(rbt, new_father, NULL, false));
|
||||||
|
KASSERT(rb_tree_check_node(rbt, new_child, NULL, false));
|
||||||
|
KASSERT(RB_ROOT_P(new_father) || rb_tree_check_node(rbt, grandpa, NULL, false));
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
rb_tree_insert_rebalance(struct rb_tree *rbt, struct rb_node *self)
|
rb_tree_insert_rebalance(struct rb_tree *rbt, struct rb_node *self)
|
||||||
{
|
{
|
||||||
RB_MARK_RED(self);
|
RB_MARK_RED(self);
|
||||||
|
RBSTAT_INC(rbt->rbt_insertion_rebalance_calls);
|
||||||
|
|
||||||
while (!RB_ROOT_P(self) && RB_RED_P(self->rb_parent)) {
|
while (!RB_ROOT_P(self) && RB_RED_P(self->rb_parent)) {
|
||||||
const unsigned int which =
|
const unsigned int which =
|
||||||
|
@ -269,6 +345,7 @@ rb_tree_insert_rebalance(struct rb_tree *rbt, struct rb_node *self)
|
||||||
struct rb_node * grandpa = father->rb_parent;
|
struct rb_node * grandpa = father->rb_parent;
|
||||||
struct rb_node * const uncle = grandpa->rb_nodes[other];
|
struct rb_node * const uncle = grandpa->rb_nodes[other];
|
||||||
|
|
||||||
|
RBSTAT_INC(rbt->rbt_insertion_rebalance_passes);
|
||||||
KASSERT(!RB_SENTINEL_P(self));
|
KASSERT(!RB_SENTINEL_P(self));
|
||||||
/*
|
/*
|
||||||
* We are red and our parent is red, therefore we must have a
|
* We are red and our parent is red, therefore we must have a
|
||||||
|
@ -331,21 +408,6 @@ rb_tree_insert_rebalance(struct rb_tree *rbt, struct rb_node *self)
|
||||||
RB_MARK_BLACK(rbt->rbt_root);
|
RB_MARK_BLACK(rbt->rbt_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct rb_node *
|
|
||||||
rb_tree_find(struct rb_tree *rbt, void *key)
|
|
||||||
{
|
|
||||||
struct rb_node *parent = rbt->rbt_root;
|
|
||||||
|
|
||||||
while (!RB_SENTINEL_P(parent)) {
|
|
||||||
const int diff = (*rbt->rbt_compare_key)(parent, key);
|
|
||||||
if (diff == 0)
|
|
||||||
return parent;
|
|
||||||
parent = parent->rb_nodes[diff > 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
rb_tree_prune_node(struct rb_tree *rbt, struct rb_node *self, int rebalance)
|
rb_tree_prune_node(struct rb_tree *rbt, struct rb_node *self, int rebalance)
|
||||||
{
|
{
|
||||||
|
@ -357,19 +419,42 @@ rb_tree_prune_node(struct rb_tree *rbt, struct rb_node *self, int rebalance)
|
||||||
KASSERT(RB_CHILDLESS_P(self));
|
KASSERT(RB_CHILDLESS_P(self));
|
||||||
KASSERT(rb_tree_check_node(rbt, self, NULL, false));
|
KASSERT(rb_tree_check_node(rbt, self, NULL, false));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since we are childless, we know that self->rb_left is pointing
|
||||||
|
* to the sentinel node.
|
||||||
|
*/
|
||||||
father->rb_nodes[which] = self->rb_left;
|
father->rb_nodes[which] = self->rb_left;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remove ourselves from the node list and decrement the count.
|
* Remove ourselves from the node list, decrement the count,
|
||||||
|
* and update min/max.
|
||||||
*/
|
*/
|
||||||
RB_TAILQ_REMOVE(&rbt->rbt_nodes, self, rb_link);
|
RB_TAILQ_REMOVE(&rbt->rbt_nodes, self, rb_link);
|
||||||
rbt->rbt_count--;
|
rbt->rbt_count--;
|
||||||
|
if (__predict_false(rbt->rbt_minmax[self->rb_position] == self)) {
|
||||||
|
rbt->rbt_minmax[self->rb_position] = father;
|
||||||
|
/*
|
||||||
|
* When removing the root, rbt->rbt_minmax[RB_LEFT] is
|
||||||
|
* updated automatically, but we also need to update
|
||||||
|
* rbt->rbt_minmax[RB_RIGHT];
|
||||||
|
*/
|
||||||
|
if (__predict_false(RB_ROOT_P(self))) {
|
||||||
|
rbt->rbt_minmax[RB_RIGHT] = father;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self->rb_sentinel = 1; /* so remove_node will fail */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rebalance if requested.
|
||||||
|
*/
|
||||||
if (rebalance)
|
if (rebalance)
|
||||||
rb_tree_removal_rebalance(rbt, father, which);
|
rb_tree_removal_rebalance(rbt, father, which);
|
||||||
KASSERT(RB_ROOT_P(self) || rb_tree_check_node(rbt, father, NULL, true));
|
KASSERT(RB_ROOT_P(self) || rb_tree_check_node(rbt, father, NULL, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When deleting an interior node
|
||||||
|
*/
|
||||||
static void
|
static void
|
||||||
rb_tree_swap_prune_and_rebalance(struct rb_tree *rbt, struct rb_node *self,
|
rb_tree_swap_prune_and_rebalance(struct rb_tree *rbt, struct rb_node *self,
|
||||||
struct rb_node *standin)
|
struct rb_node *standin)
|
||||||
|
@ -383,14 +468,14 @@ rb_tree_swap_prune_and_rebalance(struct rb_tree *rbt, struct rb_node *self,
|
||||||
if (standin->rb_parent == self) {
|
if (standin->rb_parent == self) {
|
||||||
/*
|
/*
|
||||||
* As a child of self, any childen would be opposite of
|
* As a child of self, any childen would be opposite of
|
||||||
* our parent (self).
|
* our parent.
|
||||||
*/
|
*/
|
||||||
KASSERT(RB_SENTINEL_P(standin->rb_nodes[standin_other]));
|
KASSERT(RB_SENTINEL_P(standin->rb_nodes[standin_other]));
|
||||||
standin_child = standin->rb_nodes[standin_which];
|
standin_child = standin->rb_nodes[standin_which];
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* Since we aren't a child of self, any childen would be
|
* Since we aren't a child of self, any childen would be
|
||||||
* on the same side as our parent (self).
|
* on the same side as our parent.
|
||||||
*/
|
*/
|
||||||
KASSERT(RB_SENTINEL_P(standin->rb_nodes[standin_which]));
|
KASSERT(RB_SENTINEL_P(standin->rb_nodes[standin_which]));
|
||||||
standin_child = standin->rb_nodes[standin_other];
|
standin_child = standin->rb_nodes[standin_other];
|
||||||
|
@ -481,10 +566,15 @@ rb_tree_swap_prune_and_rebalance(struct rb_tree *rbt, struct rb_node *self,
|
||||||
standin->rb_parent->rb_nodes[standin->rb_position] = standin;
|
standin->rb_parent->rb_nodes[standin->rb_position] = standin;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remove ourselves from the node list and decrement the count.
|
* Remove ourselves from the node list, decrement the count,
|
||||||
|
* and update min/max.
|
||||||
*/
|
*/
|
||||||
RB_TAILQ_REMOVE(&rbt->rbt_nodes, self, rb_link);
|
RB_TAILQ_REMOVE(&rbt->rbt_nodes, self, rb_link);
|
||||||
rbt->rbt_count--;
|
rbt->rbt_count--;
|
||||||
|
if (__predict_false(rbt->rbt_minmax[self->rb_position] == self))
|
||||||
|
rbt->rbt_minmax[self->rb_position] = self->rb_parent;
|
||||||
|
self->rb_sentinel = 1;
|
||||||
|
|
||||||
|
|
||||||
KASSERT(rb_tree_check_node(rbt, standin, NULL, false));
|
KASSERT(rb_tree_check_node(rbt, standin, NULL, false));
|
||||||
KASSERT(rb_tree_check_node(rbt, standin_father, NULL, false));
|
KASSERT(rb_tree_check_node(rbt, standin_father, NULL, false));
|
||||||
|
@ -507,32 +597,40 @@ static void
|
||||||
rb_tree_prune_blackred_branch(struct rb_tree *rbt, struct rb_node *self,
|
rb_tree_prune_blackred_branch(struct rb_tree *rbt, struct rb_node *self,
|
||||||
unsigned int which)
|
unsigned int which)
|
||||||
{
|
{
|
||||||
struct rb_node *parent = self->rb_parent;
|
struct rb_node *father = self->rb_parent;
|
||||||
struct rb_node *child = self->rb_nodes[which];
|
struct rb_node *son = self->rb_nodes[which];
|
||||||
|
|
||||||
KASSERT(which == RB_LEFT || which == RB_RIGHT);
|
KASSERT(which == RB_LEFT || which == RB_RIGHT);
|
||||||
KASSERT(RB_BLACK_P(self) && RB_RED_P(child));
|
KASSERT(RB_BLACK_P(self) && RB_RED_P(son));
|
||||||
KASSERT(!RB_TWOCHILDREN_P(child));
|
KASSERT(!RB_TWOCHILDREN_P(son));
|
||||||
KASSERT(RB_CHILDLESS_P(child));
|
KASSERT(RB_CHILDLESS_P(son));
|
||||||
KASSERT(rb_tree_check_node(rbt, self, NULL, false));
|
KASSERT(rb_tree_check_node(rbt, self, NULL, false));
|
||||||
KASSERT(rb_tree_check_node(rbt, child, NULL, false));
|
KASSERT(rb_tree_check_node(rbt, son, NULL, false));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remove ourselves from the tree and give our former child our
|
* Remove ourselves from the tree and give our former child our
|
||||||
* properties (position, color, root).
|
* properties (position, color, root).
|
||||||
*/
|
*/
|
||||||
parent->rb_nodes[self->rb_position] = child;
|
father->rb_nodes[self->rb_position] = son;
|
||||||
child->rb_parent = parent;
|
son->rb_parent = father;
|
||||||
child->rb_properties = self->rb_properties;
|
son->rb_properties = self->rb_properties;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remove ourselves from the node list and decrement the count.
|
* Remove ourselves from the node list, decrement the count,
|
||||||
|
* and update minmax.
|
||||||
*/
|
*/
|
||||||
RB_TAILQ_REMOVE(&rbt->rbt_nodes, self, rb_link);
|
RB_TAILQ_REMOVE(&rbt->rbt_nodes, self, rb_link);
|
||||||
rbt->rbt_count--;
|
rbt->rbt_count--;
|
||||||
|
if (__predict_false(RB_ROOT_P(self))) {
|
||||||
|
KASSERT(rbt->rbt_minmax[which] == son);
|
||||||
|
rbt->rbt_minmax[which ^ RB_OTHER] = son;
|
||||||
|
} else if (rbt->rbt_minmax[self->rb_position] == self) {
|
||||||
|
rbt->rbt_minmax[self->rb_position] = son;
|
||||||
|
}
|
||||||
|
self->rb_sentinel = 1;
|
||||||
|
|
||||||
KASSERT(RB_ROOT_P(self) || rb_tree_check_node(rbt, parent, NULL, true));
|
KASSERT(RB_ROOT_P(self) || rb_tree_check_node(rbt, father, NULL, true));
|
||||||
KASSERT(rb_tree_check_node(rbt, child, NULL, true));
|
KASSERT(rb_tree_check_node(rbt, son, NULL, true));
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
|
@ -542,6 +640,10 @@ rb_tree_remove_node(struct rb_tree *rbt, struct rb_node *self)
|
||||||
{
|
{
|
||||||
struct rb_node *standin;
|
struct rb_node *standin;
|
||||||
unsigned int which;
|
unsigned int which;
|
||||||
|
|
||||||
|
KASSERT(!RB_SENTINEL_P(self));
|
||||||
|
RBSTAT_INC(rbt->rbt_removals);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In the following diagrams, we (the node to be removed) are S. Red
|
* In the following diagrams, we (the node to be removed) are S. Red
|
||||||
* nodes are lowercase. T could be either red or black.
|
* nodes are lowercase. T could be either red or black.
|
||||||
|
@ -560,11 +662,8 @@ rb_tree_remove_node(struct rb_tree *rbt, struct rb_node *self)
|
||||||
* | s --> * |
|
* | s --> * |
|
||||||
*/
|
*/
|
||||||
if (RB_CHILDLESS_P(self)) {
|
if (RB_CHILDLESS_P(self)) {
|
||||||
if (RB_RED_P(self) || RB_ROOT_P(self)) {
|
bool rebalance = RB_BLACK_P(self) && !RB_ROOT_P(self);
|
||||||
rb_tree_prune_node(rbt, self, false);
|
rb_tree_prune_node(rbt, self, rebalance);
|
||||||
return;
|
|
||||||
}
|
|
||||||
rb_tree_prune_node(rbt, self, true);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
KASSERT(!RB_CHILDLESS_P(self));
|
KASSERT(!RB_CHILDLESS_P(self));
|
||||||
|
@ -607,11 +706,14 @@ rb_tree_removal_rebalance(struct rb_tree *rbt, struct rb_node *parent,
|
||||||
KASSERT(!RB_SENTINEL_P(parent));
|
KASSERT(!RB_SENTINEL_P(parent));
|
||||||
KASSERT(RB_SENTINEL_P(parent->rb_nodes[which]));
|
KASSERT(RB_SENTINEL_P(parent->rb_nodes[which]));
|
||||||
KASSERT(which == RB_LEFT || which == RB_RIGHT);
|
KASSERT(which == RB_LEFT || which == RB_RIGHT);
|
||||||
|
RBSTAT_INC(rbt->rbt_removal_rebalance_calls);
|
||||||
|
|
||||||
while (RB_BLACK_P(parent->rb_nodes[which])) {
|
while (RB_BLACK_P(parent->rb_nodes[which])) {
|
||||||
unsigned int other = which ^ RB_OTHER;
|
unsigned int other = which ^ RB_OTHER;
|
||||||
struct rb_node *brother = parent->rb_nodes[other];
|
struct rb_node *brother = parent->rb_nodes[other];
|
||||||
|
|
||||||
|
RBSTAT_INC(rbt->rbt_removal_rebalance_passes);
|
||||||
|
|
||||||
KASSERT(!RB_SENTINEL_P(brother));
|
KASSERT(!RB_SENTINEL_P(brother));
|
||||||
/*
|
/*
|
||||||
* For cases 1, 2a, and 2b, our brother's children must
|
* For cases 1, 2a, and 2b, our brother's children must
|
||||||
|
@ -620,21 +722,24 @@ rb_tree_removal_rebalance(struct rb_tree *rbt, struct rb_node *parent,
|
||||||
if (RB_BLACK_P(parent)
|
if (RB_BLACK_P(parent)
|
||||||
&& RB_BLACK_P(brother->rb_left)
|
&& RB_BLACK_P(brother->rb_left)
|
||||||
&& RB_BLACK_P(brother->rb_right)) {
|
&& RB_BLACK_P(brother->rb_right)) {
|
||||||
/*
|
|
||||||
* Case 1: Our brother is red, swap its position
|
|
||||||
* (and colors) with our parent. This is now case 2b.
|
|
||||||
*
|
|
||||||
* B -> D
|
|
||||||
* x d -> b E
|
|
||||||
* C E -> x C
|
|
||||||
*/
|
|
||||||
if (RB_RED_P(brother)) {
|
if (RB_RED_P(brother)) {
|
||||||
|
/*
|
||||||
|
* Case 1: Our brother is red, swap its
|
||||||
|
* position (and colors) with our parent.
|
||||||
|
* This should now be case 2b (unless C or E
|
||||||
|
* has a red child which is case 3; thus no
|
||||||
|
* explicit branch to case 2b).
|
||||||
|
*
|
||||||
|
* B -> D
|
||||||
|
* A d -> b E
|
||||||
|
* C E -> A C
|
||||||
|
*/
|
||||||
KASSERT(RB_BLACK_P(parent));
|
KASSERT(RB_BLACK_P(parent));
|
||||||
rb_tree_reparent_nodes(rbt, parent, other);
|
rb_tree_reparent_nodes(rbt, parent, other);
|
||||||
brother = parent->rb_nodes[other];
|
brother = parent->rb_nodes[other];
|
||||||
KASSERT(!RB_SENTINEL_P(brother));
|
KASSERT(!RB_SENTINEL_P(brother));
|
||||||
KASSERT(RB_BLACK_P(brother));
|
|
||||||
KASSERT(RB_RED_P(parent));
|
KASSERT(RB_RED_P(parent));
|
||||||
|
KASSERT(RB_BLACK_P(brother));
|
||||||
KASSERT(rb_tree_check_node(rbt, brother, NULL, false));
|
KASSERT(rb_tree_check_node(rbt, brother, NULL, false));
|
||||||
KASSERT(rb_tree_check_node(rbt, parent, NULL, false));
|
KASSERT(rb_tree_check_node(rbt, parent, NULL, false));
|
||||||
} else {
|
} else {
|
||||||
|
@ -643,8 +748,8 @@ rb_tree_removal_rebalance(struct rb_tree *rbt, struct rb_node *parent,
|
||||||
* Change our brother to red, advance up rank
|
* Change our brother to red, advance up rank
|
||||||
* and go through the loop again.
|
* and go through the loop again.
|
||||||
*
|
*
|
||||||
* B -> B
|
* B -> *B
|
||||||
* A D -> A d
|
* *A D -> A d
|
||||||
* C E -> C E
|
* C E -> C E
|
||||||
*/
|
*/
|
||||||
RB_MARK_RED(brother);
|
RB_MARK_RED(brother);
|
||||||
|
@ -656,14 +761,31 @@ rb_tree_removal_rebalance(struct rb_tree *rbt, struct rb_node *parent,
|
||||||
KASSERT(rb_tree_check_node(rbt, parent, NULL, false));
|
KASSERT(rb_tree_check_node(rbt, parent, NULL, false));
|
||||||
which = parent->rb_position;
|
which = parent->rb_position;
|
||||||
parent = parent->rb_parent;
|
parent = parent->rb_parent;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
} else if (RB_RED_P(parent)
|
}
|
||||||
|
/*
|
||||||
|
* Avoid an else here so that case 2a above can hit either
|
||||||
|
* case 2b, 3, or 4.
|
||||||
|
*/
|
||||||
|
if (RB_RED_P(parent)
|
||||||
&& RB_BLACK_P(brother)
|
&& RB_BLACK_P(brother)
|
||||||
&& RB_BLACK_P(brother->rb_left)
|
&& RB_BLACK_P(brother->rb_left)
|
||||||
&& RB_BLACK_P(brother->rb_right)) {
|
&& RB_BLACK_P(brother->rb_right)) {
|
||||||
|
KASSERT(RB_RED_P(parent));
|
||||||
KASSERT(RB_BLACK_P(brother));
|
KASSERT(RB_BLACK_P(brother));
|
||||||
KASSERT(RB_BLACK_P(brother->rb_left));
|
KASSERT(RB_BLACK_P(brother->rb_left));
|
||||||
KASSERT(RB_BLACK_P(brother->rb_right));
|
KASSERT(RB_BLACK_P(brother->rb_right));
|
||||||
|
/*
|
||||||
|
* We are black, our father is red, our brother and
|
||||||
|
* both nephews are black. Simply invert/exchange the
|
||||||
|
* colors of our father and brother (to black and red
|
||||||
|
* respectively).
|
||||||
|
*
|
||||||
|
* | f --> F |
|
||||||
|
* | * B --> * b |
|
||||||
|
* | N N --> N N |
|
||||||
|
*/
|
||||||
RB_MARK_BLACK(parent);
|
RB_MARK_BLACK(parent);
|
||||||
RB_MARK_RED(brother);
|
RB_MARK_RED(brother);
|
||||||
KASSERT(rb_tree_check_node(rbt, brother, NULL, true));
|
KASSERT(rb_tree_check_node(rbt, brother, NULL, true));
|
||||||
|
@ -671,17 +793,18 @@ rb_tree_removal_rebalance(struct rb_tree *rbt, struct rb_node *parent,
|
||||||
} else {
|
} else {
|
||||||
KASSERT(RB_BLACK_P(brother));
|
KASSERT(RB_BLACK_P(brother));
|
||||||
KASSERT(!RB_CHILDLESS_P(brother));
|
KASSERT(!RB_CHILDLESS_P(brother));
|
||||||
/*
|
|
||||||
* Case 3: our brother is black, our left nephew is
|
|
||||||
* red, and our right nephew is black. Swap our
|
|
||||||
* brother with our left nephew. This result in a
|
|
||||||
* tree that matches case 4.
|
|
||||||
*
|
|
||||||
* B -> D
|
|
||||||
* A D -> B E
|
|
||||||
* c e -> A C
|
|
||||||
*/
|
|
||||||
if (RB_BLACK_P(brother->rb_nodes[other])) {
|
if (RB_BLACK_P(brother->rb_nodes[other])) {
|
||||||
|
/*
|
||||||
|
* Case 3: our brother is black, our near
|
||||||
|
* nephew is red, and our far nephew is black.
|
||||||
|
* Swap our brother with our near nephew.
|
||||||
|
* This result in a tree that matches case 4.
|
||||||
|
* (Our father could be red or black).
|
||||||
|
*
|
||||||
|
* | F --> F |
|
||||||
|
* | x B --> x B |
|
||||||
|
* | n --> n |
|
||||||
|
*/
|
||||||
KASSERT(RB_RED_P(brother->rb_nodes[which]));
|
KASSERT(RB_RED_P(brother->rb_nodes[which]));
|
||||||
rb_tree_reparent_nodes(rbt, brother, which);
|
rb_tree_reparent_nodes(rbt, brother, which);
|
||||||
KASSERT(brother->rb_parent == parent->rb_nodes[other]);
|
KASSERT(brother->rb_parent == parent->rb_nodes[other]);
|
||||||
|
@ -689,16 +812,25 @@ rb_tree_removal_rebalance(struct rb_tree *rbt, struct rb_node *parent,
|
||||||
KASSERT(RB_RED_P(brother->rb_nodes[other]));
|
KASSERT(RB_RED_P(brother->rb_nodes[other]));
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Case 4: our brother is black and our right nephew
|
* Case 4: our brother is black and our far nephew
|
||||||
* is red. Swap our parent and brother locations and
|
* is red. Swap our father and brother locations and
|
||||||
* change our right nephew to black. (these can be
|
* change our far nephew to black. (these can be
|
||||||
* done in either order so we change the color first).
|
* done in either order so we change the color first).
|
||||||
* The result is a valid red-black tree and is a
|
* The result is a valid red-black tree and is a
|
||||||
* terminal case.
|
* terminal case. (again we don't care about the
|
||||||
|
* father's color)
|
||||||
*
|
*
|
||||||
* B -> D
|
* If the father is red, we will get a red-black-black
|
||||||
* A D -> B E
|
* tree:
|
||||||
* c e -> A C
|
* | f -> f --> b |
|
||||||
|
* | B -> B --> F N |
|
||||||
|
* | n -> N --> |
|
||||||
|
*
|
||||||
|
* If the father is black, we will get an all black
|
||||||
|
* tree:
|
||||||
|
* | F -> F --> B |
|
||||||
|
* | B -> B --> F N |
|
||||||
|
* | n -> N --> |
|
||||||
*/
|
*/
|
||||||
RB_MARK_BLACK(brother->rb_nodes[other]);
|
RB_MARK_BLACK(brother->rb_nodes[other]);
|
||||||
rb_tree_reparent_nodes(rbt, parent, other);
|
rb_tree_reparent_nodes(rbt, parent, other);
|
||||||
|
@ -716,12 +848,9 @@ rb_tree_iterate(struct rb_tree *rbt, struct rb_node *self,
|
||||||
KASSERT(direction == RB_LEFT || direction == RB_RIGHT);
|
KASSERT(direction == RB_LEFT || direction == RB_RIGHT);
|
||||||
|
|
||||||
if (self == NULL) {
|
if (self == NULL) {
|
||||||
self = rbt->rbt_root;
|
if (RB_SENTINEL_P(rbt->rbt_root))
|
||||||
if (RB_SENTINEL_P(self))
|
|
||||||
return NULL;
|
return NULL;
|
||||||
while (!RB_SENTINEL_P(self->rb_nodes[other]))
|
return rbt->rbt_minmax[direction];
|
||||||
self = self->rb_nodes[other];
|
|
||||||
return self;
|
|
||||||
}
|
}
|
||||||
KASSERT(!RB_SENTINEL_P(self));
|
KASSERT(!RB_SENTINEL_P(self));
|
||||||
/*
|
/*
|
||||||
|
@ -757,12 +886,9 @@ rb_tree_iterate_const(const struct rb_tree *rbt, const struct rb_node *self,
|
||||||
KASSERT(direction == RB_LEFT || direction == RB_RIGHT);
|
KASSERT(direction == RB_LEFT || direction == RB_RIGHT);
|
||||||
|
|
||||||
if (self == NULL) {
|
if (self == NULL) {
|
||||||
self = rbt->rbt_root;
|
if (RB_SENTINEL_P(rbt->rbt_root))
|
||||||
if (RB_SENTINEL_P(self))
|
|
||||||
return NULL;
|
return NULL;
|
||||||
while (!RB_SENTINEL_P(self->rb_nodes[other]))
|
return rbt->rbt_minmax[direction];
|
||||||
self = self->rb_nodes[other];
|
|
||||||
return self;
|
|
||||||
}
|
}
|
||||||
KASSERT(!RB_SENTINEL_P(self));
|
KASSERT(!RB_SENTINEL_P(self));
|
||||||
/*
|
/*
|
||||||
|
@ -825,9 +951,9 @@ rb_tree_check_node(const struct rb_tree *rbt, const struct rb_node *self,
|
||||||
const struct rb_node *prev0 = rb_tree_iterate_const(rbt, self, RB_LEFT);
|
const struct rb_node *prev0 = rb_tree_iterate_const(rbt, self, RB_LEFT);
|
||||||
const struct rb_node *next0 = rb_tree_iterate_const(rbt, self, RB_RIGHT);
|
const struct rb_node *next0 = rb_tree_iterate_const(rbt, self, RB_RIGHT);
|
||||||
KASSERT(prev0 == TAILQ_PREV(self, rb_node_qh, rb_link));
|
KASSERT(prev0 == TAILQ_PREV(self, rb_node_qh, rb_link));
|
||||||
if (next0 != TAILQ_NEXT(self, rb_link))
|
|
||||||
next0 = rb_tree_iterate_const(rbt, self, RB_RIGHT);
|
|
||||||
KASSERT(next0 == TAILQ_NEXT(self, rb_link));
|
KASSERT(next0 == TAILQ_NEXT(self, rb_link));
|
||||||
|
KASSERT(prev0 != NULL || self == rbt->rbt_minmax[RB_LEFT]);
|
||||||
|
KASSERT(next0 != NULL || self == rbt->rbt_minmax[RB_RIGHT]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1005,7 +1131,11 @@ rb_tree_check(const struct rb_tree *rbt, bool red_check)
|
||||||
const struct rb_node *prev;
|
const struct rb_node *prev;
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
|
|
||||||
KASSERT(rbt->rbt_root == NULL || rbt->rbt_root->rb_position == RB_LEFT);
|
KASSERT(rbt->rbt_root != NULL);
|
||||||
|
KASSERT(rbt->rbt_root->rb_position == RB_LEFT);
|
||||||
|
|
||||||
|
KASSERT(rbt->rbt_count > 1
|
||||||
|
|| rbt->rbt_minmax[RB_LEFT] == rbt->rbt_minmax[RB_RIGHT]);
|
||||||
|
|
||||||
prev = NULL;
|
prev = NULL;
|
||||||
count = 0;
|
count = 0;
|
||||||
|
@ -1028,4 +1158,33 @@ rb_tree_check(const struct rb_tree *rbt, bool red_check)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif /* !NDEBUG || DIAGNOSTIC */
|
||||||
|
|
||||||
|
#ifdef RBSTATS
|
||||||
|
static void
|
||||||
|
rb_tree_mark_depth(const struct rb_tree *rbt, const struct rb_node *self,
|
||||||
|
size_t *depths, size_t depth)
|
||||||
|
{
|
||||||
|
if (RB_SENTINEL_P(self))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (RB_TWOCHILDREN_P(self)) {
|
||||||
|
rb_tree_mark_depth(rbt, self->rb_left, depths, depth + 1);
|
||||||
|
rb_tree_mark_depth(rbt, self->rb_right, depths, depth + 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
depths[depth]++;
|
||||||
|
if (!RB_LEFT_SENTINEL_P(self)) {
|
||||||
|
rb_tree_mark_depth(rbt, self->rb_left, depths, depth + 1);
|
||||||
|
}
|
||||||
|
if (!RB_RIGHT_SENTINEL_P(self)) {
|
||||||
|
rb_tree_mark_depth(rbt, self->rb_right, depths, depth + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rb_tree_depths(const struct rb_tree *rbt, size_t *depths)
|
||||||
|
{
|
||||||
|
rb_tree_mark_depth(rbt, rbt->rbt_root, depths, 1);
|
||||||
|
}
|
||||||
|
#endif /* RBSTATS */
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* $NetBSD: rb.h,v 1.4 2006/09/08 04:07:15 matt Exp $ */
|
/* $NetBSD: rb.h,v 1.5 2006/09/09 05:55:51 matt Exp $ */
|
||||||
|
|
||||||
/*-
|
/*-
|
||||||
* Copyright (c) 2001 The NetBSD Foundation, Inc.
|
* Copyright (c) 2001 The NetBSD Foundation, Inc.
|
||||||
|
@ -97,11 +97,11 @@ struct rb_node {
|
||||||
#if !defined(NDEBUG) || defined(DEBUG)
|
#if !defined(NDEBUG) || defined(DEBUG)
|
||||||
TAILQ_HEAD(rb_node_qh, rb_node);
|
TAILQ_HEAD(rb_node_qh, rb_node);
|
||||||
|
|
||||||
#define RB_TAILQ_REMOVE TAILQ_REMOVE
|
#define RB_TAILQ_REMOVE(a, b, c) TAILQ_REMOVE(a, b, c)
|
||||||
#define RB_TAILQ_INIT TAILQ_INIT
|
#define RB_TAILQ_INIT(a) TAILQ_INIT(a)
|
||||||
#define RB_TAILQ_INSERT_HEAD(a, b, c) TAILQ_INSERT_HEAD
|
#define RB_TAILQ_INSERT_HEAD(a, b, c) TAILQ_INSERT_HEAD(a, b, c)
|
||||||
#define RB_TAILQ_INSERT_BEFORE(a, b, c) TAILQ_INSERT_BEFORE
|
#define RB_TAILQ_INSERT_BEFORE(a, b, c) TAILQ_INSERT_BEFORE(a, b, c)
|
||||||
#define RB_TAILQ_INSERT_AFTER(a, b, c, d) TAILQ_INSERT_AFTER
|
#define RB_TAILQ_INSERT_AFTER(a, b, c, d) TAILQ_INSERT_AFTER(a, b, c, d)
|
||||||
#else
|
#else
|
||||||
#define RB_TAILQ_REMOVE(a, b, c) do { } while (0)
|
#define RB_TAILQ_REMOVE(a, b, c) do { } while (0)
|
||||||
#define RB_TAILQ_INIT(a) do { } while (0)
|
#define RB_TAILQ_INIT(a) do { } while (0)
|
||||||
|
@ -121,16 +121,40 @@ struct rb_tree {
|
||||||
#endif
|
#endif
|
||||||
rb_compare_nodes_fn rbt_compare_nodes;
|
rb_compare_nodes_fn rbt_compare_nodes;
|
||||||
rb_compare_key_fn rbt_compare_key;
|
rb_compare_key_fn rbt_compare_key;
|
||||||
|
struct rb_node *rbt_minmax[2];
|
||||||
unsigned int rbt_count;
|
unsigned int rbt_count;
|
||||||
|
#ifdef RBSTATS
|
||||||
|
unsigned int rbt_insertions;
|
||||||
|
unsigned int rbt_removals;
|
||||||
|
unsigned int rbt_insertion_rebalance_calls;
|
||||||
|
unsigned int rbt_insertion_rebalance_passes;
|
||||||
|
unsigned int rbt_removal_rebalance_calls;
|
||||||
|
unsigned int rbt_removal_rebalance_passes;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef RBSTATS
|
||||||
|
#define RBSTAT_INC(v) ((void)((v)++))
|
||||||
|
#else
|
||||||
|
#define RBSTAT_INC(v) do { } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
void rb_tree_init(struct rb_tree *, rb_compare_nodes_fn, rb_compare_key_fn);
|
void rb_tree_init(struct rb_tree *, rb_compare_nodes_fn, rb_compare_key_fn);
|
||||||
void rb_tree_insert_node(struct rb_tree *, struct rb_node *);
|
void rb_tree_insert_node(struct rb_tree *, struct rb_node *);
|
||||||
struct rb_node *
|
struct rb_node *
|
||||||
rb_tree_find(struct rb_tree *, void *);
|
rb_tree_find_node(struct rb_tree *, const void *);
|
||||||
|
struct rb_node *
|
||||||
|
rb_tree_find_node_geq(struct rb_tree *, const void *);
|
||||||
|
struct rb_node *
|
||||||
|
rb_tree_find_node_leq(struct rb_tree *, const void *);
|
||||||
void rb_tree_remove_node(struct rb_tree *, struct rb_node *);
|
void rb_tree_remove_node(struct rb_tree *, struct rb_node *);
|
||||||
void rb_tree_check(const struct rb_tree *, bool);
|
|
||||||
struct rb_node *
|
struct rb_node *
|
||||||
rb_tree_iterate(struct rb_tree *, struct rb_node *, unsigned int);
|
rb_tree_iterate(struct rb_tree *, struct rb_node *, unsigned int);
|
||||||
|
#if !defined(NDEBUG) || defined(DIAGNOSTIC)
|
||||||
|
void rb_tree_check(const struct rb_tree *, bool);
|
||||||
|
#endif
|
||||||
|
#ifdef RBSTATS
|
||||||
|
void rb_tree_depths(const struct rb_tree *, size_t *);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* _LIBKERN_RB_H_*/
|
#endif /* _LIBKERN_RB_H_*/
|
||||||
|
|
Loading…
Reference in New Issue