|
|
|
|
|
|
|
|
|
|
|
|
#include "global.h" |
|
RCSID("$Id: rbtree.c,v 1.14 2002/12/05 20:13:04 mast Exp $"); |
|
#include "interpret.h" |
#include "pike_error.h" |
#include "rbtree_low.h" |
|
#include <assert.h> |
#include <stdlib.h> |
|
#if defined (PIKE_DEBUG) || defined (TEST_MULTISET) |
|
DECLSPEC(noreturn) static void debug_rb_fatal ( |
struct rb_node_hdr *tree, |
const char *fmt, ...) ATTRIBUTE((noreturn, format (printf, 2, 3))); |
#define rb_fatal \ |
(fprintf (stderr, "%s:%d: Fatal in rbtree: ", __FILE__, __LINE__), \ |
debug_rb_fatal) |
DECLSPEC(noreturn) static void debug_custom_rb_fatal ( |
struct rb_node_hdr *tree, dump_data_fn *dump_data, |
void *extra, const char *fmt, ...) ATTRIBUTE((noreturn, format (printf, 4, 5))); |
#define custom_rb_fatal \ |
(fprintf (stderr, "%s:%d: Fatal in rbtree: ", __FILE__, __LINE__), \ |
debug_custom_rb_fatal) |
|
#endif |
|
void rbstack_push (struct rbstack_ptr *rbstack, struct rb_node_hdr *node) |
{ |
struct rbstack_slice *new = ALLOC_STRUCT (rbstack_slice); |
new->up = rbstack->slice; |
new->stack[0] = node; |
#ifdef RB_STATS |
rbstack_slice_allocs++; |
new->depth = rbstack->slice->depth; |
new->maxdepth = rbstack->slice->maxdepth; |
#endif |
rbstack->slice = new; |
rbstack->ssp = 1; |
} |
|
void rbstack_pop (struct rbstack_ptr *rbstack) |
{ |
struct rbstack_slice *old = rbstack->slice; |
rbstack->slice = old->up; |
#ifdef RB_STATS |
rbstack->slice->maxdepth = old->maxdepth; |
#endif |
xfree (old); |
rbstack->ssp = STACK_SLICE_SIZE; |
} |
|
void rbstack_up (struct rbstack_ptr *rbstack) |
{ |
rbstack->slice = rbstack->slice->up; |
rbstack->ssp = STACK_SLICE_SIZE; |
} |
|
void rbstack_up_to_root (struct rbstack_ptr *rbstack) |
{ |
struct rbstack_slice *up; |
while ((up = rbstack->slice->up)) rbstack->slice = up; |
rbstack->ssp = 0; |
} |
|
void rbstack_free (struct rbstack_ptr *rbstack) |
{ |
struct rbstack_slice *ptr = rbstack->slice; |
do { |
struct rbstack_slice *old = ptr; |
ptr = ptr->up; |
#ifdef RB_STATS |
ptr->maxdepth = old->maxdepth; |
#endif |
xfree (old); |
} while (ptr->up); |
rbstack->slice = ptr; |
rbstack->ssp = 0; |
} |
|
|
|
void rbstack_insert (struct rbstack_ptr *top, struct rbstack_ptr *pos, |
struct rb_node_hdr *node) |
{ |
struct rbstack_ptr rbp1 = *top, rbp2 = *top, rbpos = *pos; |
RBSTACK_PUSH (rbp2, NULL); |
*top = rbp2; |
|
while (rbp1.ssp != rbpos.ssp || rbp1.slice != rbpos.slice) { |
rbp2.slice->stack[--rbp2.ssp] = rbp1.slice->stack[--rbp1.ssp]; |
if (!rbp2.ssp) rbp2.slice = rbp1.slice, rbp2.ssp = STACK_SLICE_SIZE; |
if (!rbp1.ssp) { |
if (rbp1.slice->up) rbstack_up (&rbp1); |
#ifdef PIKE_DEBUG |
else if (rbp1.ssp != rbpos.ssp || rbp1.slice != rbpos.slice) |
Pike_fatal ("Didn't find the given position on the stack.\n"); |
#endif |
} |
} |
|
RBSTACK_POKE (rbp2, node); |
*pos = rbp2; |
} |
|
#if 0 |
|
|
void rbstack_assign (struct rbstack_ptr *target, struct rbstack_ptr *source) |
{ |
struct rbstack_slice *src_slice = source->slice; |
struct rbstack_slice *tgt_slice = target->slice; |
|
#ifdef PIKE_DEBUG |
if (target->ssp) Pike_fatal ("target rbstack not empty.\n"); |
#endif |
|
target->ssp = source->ssp; |
source->ssp = 0; |
|
if (src_slice->up) { |
struct rbstack_slice *prev_slice; |
target->slice = src_slice; |
do { |
prev_slice = src_slice; |
src_slice = src_slice->up; |
} while (src_slice->up); |
MEMCPY ((char *) &tgt_slice->stack, (char *) &src_slice->stack, |
STACK_SLICE_SIZE * sizeof (struct rb_node_hdr *)); |
prev_slice->up = tgt_slice; |
source->slice = src_slice; |
} |
else { |
MEMCPY ((char *) &tgt_slice->stack, (char *) &src_slice->stack, |
target->ssp * sizeof (struct rb_node_hdr *)); |
#ifdef RB_STATS |
tgt_slice->maxdepth = src_slice->maxdepth; |
tgt_slice->depth = src_slice->depth; |
#endif |
} |
} |
|
void rbstack_copy (struct rbstack_ptr *target, struct rbstack_ptr *source) |
{ |
struct rbstack_slice *src_slice = source->slice; |
struct rbstack_slice *tgt_stack_slice = target->slice; |
size_t ssp = source->ssp; |
|
#ifdef PIKE_DEBUG |
if (target->ssp) Pike_fatal ("target rbstack not empty.\n"); |
#endif |
|
target->ssp = ssp; |
|
if (src_slice->up) { |
struct rbstack_slice *tgt_slice = |
target->slice = ALLOC_STRUCT (rbstack_slice); |
#ifdef RB_STATS |
rbstack_slice_allocs++; |
#endif |
MEMCPY ((char *) &tgt_slice->stack, (char *) &src_slice->stack, |
ssp * sizeof (struct rb_node_hdr *)); |
ssp = STACK_SLICE_SIZE; |
while ((src_slice = src_slice->up)->up) { |
tgt_slice->up = ALLOC_STRUCT (rbstack_slice); |
tgt_slice = tgt_slice->up; |
#ifdef RB_STATS |
rbstack_slice_allocs++; |
#endif |
MEMCPY ((char *) &tgt_slice->stack, (char *) &src_slice->stack, |
STACK_SLICE_SIZE * sizeof (struct rb_node_hdr *)); |
} |
tgt_slice->up = tgt_stack_slice; |
} |
|
MEMCPY ((char *) &tgt_stack_slice->stack, (char *) &src_slice->stack, |
ssp * sizeof (struct rb_node_hdr *)); |
|
#ifdef RB_STATS |
target->slice->maxdepth = target->slice->depth = source->slice->depth; |
#endif |
} |
|
#endif /* Not tested or used. */ |
|
|
|
|
void rbstack_shift (struct rbstack_ptr rbstack, |
struct rb_node_hdr *oldbase, |
struct rb_node_hdr *newbase) |
{ |
struct rb_node_hdr *node; |
while ((node = RBSTACK_PEEK (rbstack))) { |
rbstack.slice->stack[rbstack.ssp - 1] = |
(struct rb_node_hdr *) ((char *) node - (char *) oldbase + (char *) newbase); |
RBSTACK_UP_IGNORE (rbstack); |
} |
} |
|
|
|
|
|
PMOD_EXPORT inline struct rb_node_hdr *rb_first (struct rb_node_hdr *root) |
{ |
DO_IF_RB_STATS (rb_num_sidesteps++; rb_num_sidestep_ops++); |
if (root) |
while (root->prev) { |
root = root->prev; |
DO_IF_RB_STATS (rb_num_sidestep_ops++); |
} |
return root; |
} |
|
PMOD_EXPORT inline struct rb_node_hdr *rb_last (struct rb_node_hdr *root) |
{ |
DO_IF_RB_STATS (rb_num_sidesteps++; rb_num_sidestep_ops++); |
if (root) |
while (root->next) { |
root = root->next; |
DO_IF_RB_STATS (rb_num_sidestep_ops++); |
} |
return root; |
} |
|
PMOD_EXPORT inline struct rb_node_hdr *rb_link_prev (struct rb_node_hdr *node) |
{ |
node = node->prev; |
DO_IF_RB_STATS (rb_num_sidestep_ops++); |
while (!(node->flags & RB_THREAD_NEXT)) { |
node = node->next; |
DO_IF_RB_STATS (rb_num_sidestep_ops++); |
} |
return node; |
} |
|
PMOD_EXPORT inline struct rb_node_hdr *rb_link_next (struct rb_node_hdr *node) |
{ |
node = node->next; |
DO_IF_RB_STATS (rb_num_sidestep_ops++); |
while (!(node->flags & RB_THREAD_PREV)) { |
node = node->prev; |
DO_IF_RB_STATS (rb_num_sidestep_ops++); |
} |
return node; |
} |
|
|
#define SET_PTR_TO_CHILD(PARENT, CHILD, PREV_VAL, NEXT_VAL) do { \ |
if (CHILD == PARENT->prev) \ |
PARENT->prev = PREV_VAL; \ |
else { \ |
DO_IF_DEBUG( \ |
if (CHILD != PARENT->next) \ |
rb_fatal (PARENT, "Got invalid parent to %p " \ |
"(stack probably wrong).\n", CHILD); \ |
); \ |
PARENT->next = NEXT_VAL; \ |
} \ |
} while (0) |
|
|
|
|
|
|
|
|
|
static inline struct rb_node_hdr *rot_right (struct rb_node_hdr *node) |
{ |
|
|
|
struct rb_node_hdr *ret = node->prev; |
if (ret->flags & RB_THREAD_NEXT) { |
#ifdef PIKE_DEBUG |
if (ret->next != node) rb_fatal (node, "Bogus next thread pointer.\n"); |
#endif |
ret->flags &= ~RB_THREAD_NEXT; |
node->flags |= RB_THREAD_PREV; |
} |
else { |
node->prev = ret->next; |
ret->next = node; |
} |
return ret; |
} |
|
|
|
|
|
|
|
|
|
static inline struct rb_node_hdr *rot_left (struct rb_node_hdr *node) |
{ |
struct rb_node_hdr *ret = node->next; |
if (ret->flags & RB_THREAD_PREV) { |
#ifdef PIKE_DEBUG |
if (ret->prev != node) rb_fatal (node, "Bogus prev thread pointer.\n"); |
#endif |
ret->flags &= ~RB_THREAD_PREV; |
node->flags |= RB_THREAD_NEXT; |
} |
else { |
node->next = ret->prev; |
ret->prev = node; |
} |
return ret; |
} |
|
|
|
static struct rb_node_hdr *rebalance_after_add (struct rb_node_hdr *node, |
struct rbstack_ptr rbstack) |
{ |
struct rb_node_hdr *parent, *grandparent, *uncle, *top; |
RBSTACK_POP (rbstack, parent); |
#ifdef PIKE_DEBUG |
if (!parent) rb_fatal (node, "No parent on stack.\n"); |
#endif |
RBSTACK_POP (rbstack, grandparent); |
top = grandparent ? grandparent : parent; |
|
while (parent->flags & RB_RED) { |
|
#ifdef PIKE_DEBUG |
if (!grandparent) rb_fatal (parent, "No parent for red node.\n"); |
#endif |
#ifdef RB_STATS |
rb_add_rebalance_cnt++; |
#endif |
|
if (parent == grandparent->prev) { |
uncle = grandparent->next; |
|
if (!(grandparent->flags & RB_THREAD_NEXT) && uncle->flags & RB_RED) { |
|
|
|
|
|
|
|
|
|
parent->flags &= ~RB_RED; |
uncle->flags &= ~RB_RED; |
RBSTACK_POP (rbstack, parent); |
if (!parent) { |
|
top = grandparent; |
break; |
} |
grandparent->flags |= RB_RED; |
node = grandparent; |
RBSTACK_POP (rbstack, grandparent); |
top = grandparent ? grandparent : parent; |
} |
|
else { |
if (node == parent->next) { |
|
|
|
|
|
|
|
|
|
node = parent; |
parent = grandparent->prev = rot_left (node); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
rot_right (grandparent); |
grandparent->flags |= RB_RED; |
parent->flags &= ~RB_RED; |
RBSTACK_POP (rbstack, top); |
if (top) |
SET_PTR_TO_CHILD (top, grandparent, parent, parent); |
else |
top = parent; |
break; |
} |
} |
|
else { |
#ifdef PIKE_DEBUG |
if (parent != grandparent->next) |
rb_fatal (grandparent, |
"Childs parent doesn't know about it (stack probably wrong).\n"); |
#endif |
|
uncle = grandparent->prev; |
|
if (!(grandparent->flags & RB_THREAD_PREV) && uncle->flags & RB_RED) { |
|
parent->flags &= ~RB_RED; |
uncle->flags &= ~RB_RED; |
RBSTACK_POP (rbstack, parent); |
if (!parent) { |
top = grandparent; |
break; |
} |
grandparent->flags |= RB_RED; |
node = grandparent; |
RBSTACK_POP (rbstack, grandparent); |
top = grandparent ? grandparent : parent; |
} |
|
else { |
if (node == parent->prev) { |
|
node = parent; |
parent = grandparent->next = rot_right (node); |
} |
|
|
rot_left (grandparent); |
grandparent->flags |= RB_RED; |
parent->flags &= ~RB_RED; |
RBSTACK_POP (rbstack, top); |
if (top) |
SET_PTR_TO_CHILD (top, grandparent, parent, parent); |
else |
top = parent; |
break; |
} |
} |
} |
|
RBSTACK_FREE_SET_ROOT (rbstack, top); |
#ifdef PIKE_DEBUG |
if (top->flags & RB_RED) rb_fatal (top, "Root node not black.\n"); |
#endif |
return top; |
} |
|
|
|
|
|
|
|
|
static struct rb_node_hdr *rebalance_after_delete (struct rb_node_hdr *node, |
struct rbstack_ptr *rbstack_ptr, |
int keep_rbstack) |
{ |
struct rb_node_hdr *parent, *sibling, *top = node; |
struct rbstack_ptr rbstack = *rbstack_ptr, rbstack_top; |
|
if (keep_rbstack) { |
rbstack_top = rbstack; |
RBSTACK_UP (rbstack, parent); |
} |
else |
RBSTACK_POP (rbstack, parent); |
|
if (!parent) { |
if (!node) return NULL; |
node->flags &= ~RB_RED; |
} |
else do { |
top = parent; |
#ifdef RB_STATS |
rb_add_rebalance_cnt++; |
#endif |
|
if (node == parent->prev) { |
if (!(parent->flags & RB_THREAD_PREV) && node->flags & RB_RED) { |
node->flags &= ~RB_RED; |
break; |
} |
#ifdef PIKE_DEBUG |
if (parent->flags & RB_THREAD_NEXT) |
|
|
|
|
rb_fatal (parent, "Sibling in next is null; tree was unbalanced.\n"); |
#endif |
sibling = parent->next; |
|
if (sibling->flags & RB_RED) { |
|
|
|
|
|
|
|
|
|
parent->flags |= RB_RED; |
sibling->flags &= ~RB_RED; |
rot_left (parent); |
top = RBSTACK_PEEK (rbstack); |
if (top) |
SET_PTR_TO_CHILD (top, parent, sibling, sibling); |
else |
top = sibling; |
if (keep_rbstack) rbstack_insert (&rbstack_top, &rbstack, sibling); |
else RBSTACK_PUSH (rbstack, sibling); |
#ifdef PIKE_DEBUG |
if (parent->flags & RB_THREAD_NEXT) |
rb_fatal (parent, "Sibling in next is null; tree was unbalanced.\n"); |
#endif |
sibling = parent->next; |
goto prev_node_red_parent; |
} |
|
else if (!(parent->flags & RB_RED)) { |
if ((sibling->flags & RB_THREAD_PREV || !(sibling->prev->flags & RB_RED)) && |
(sibling->flags & RB_THREAD_NEXT || !(sibling->next->flags & RB_RED))) { |
|
|
|
|
|
|
|
|
|
sibling->flags |= RB_RED; |
node = parent; |
if (keep_rbstack) RBSTACK_UP (rbstack, parent); |
else RBSTACK_POP (rbstack, parent); |
continue; |
} |
} |
|
else { |
prev_node_red_parent: |
if ((sibling->flags & RB_THREAD_PREV || !(sibling->prev->flags & RB_RED)) && |
(sibling->flags & RB_THREAD_NEXT || !(sibling->next->flags & RB_RED))) { |
|
|
|
|
|
|
|
|
|
|
parent->flags &= ~RB_RED; |
sibling->flags |= RB_RED; |
break; |
} |
} |
|
if (sibling->flags & RB_THREAD_NEXT || !(sibling->next->flags & RB_RED)) { |
|
|
|
|
|
|
|
|
|
|
|
|
sibling->flags |= RB_RED; |
sibling = parent->next = rot_right (sibling); |
sibling->flags &= ~RB_RED; |
} |
|
|
|
|
|
|
|
|
|
|
|
assert (!(sibling->flags & RB_RED)); |
rot_left (parent); |
if (keep_rbstack) top = RBSTACK_PEEK (rbstack); |
else RBSTACK_POP (rbstack, top); |
if (top) { |
SET_PTR_TO_CHILD (top, parent, sibling, sibling); |
if (parent->flags & RB_RED) { |
sibling->flags |= RB_RED; |
parent->flags &= ~RB_RED; |
} |
} |
else { |
top = sibling; |
|
assert (!(parent->flags & RB_RED)); |
} |
sibling->next->flags &= ~RB_RED; |
if (keep_rbstack) { |
|
|
struct rbstack_ptr tmp = rbstack; |
rbstack_insert (&rbstack_top, &tmp, sibling); |
} |
break; |
} |
|
else { |
#ifdef PIKE_DEBUG |
if (node != parent->next) |
rb_fatal (parent, |
"Childs parent doesn't know about it (stack probably wrong).\n"); |
#endif |
|
|
if (!(parent->flags & RB_THREAD_NEXT) && node->flags & RB_RED) { |
node->flags &= ~RB_RED; |
break; |
} |
#ifdef PIKE_DEBUG |
if (parent->flags & RB_THREAD_PREV) |
rb_fatal (parent, "Sibling in prev is null; tree was unbalanced.\n"); |
#endif |
sibling = parent->prev; |
|
if (sibling->flags & RB_RED) { |
|
parent->flags |= RB_RED; |
sibling->flags &= ~RB_RED; |
rot_right (parent); |
top = RBSTACK_PEEK (rbstack); |
if (top) |
SET_PTR_TO_CHILD (top, parent, sibling, sibling); |
else |
top = sibling; |
if (keep_rbstack) rbstack_insert (&rbstack_top, &rbstack, sibling); |
else RBSTACK_PUSH (rbstack, sibling); |
#ifdef PIKE_DEBUG |
if (parent->flags & RB_THREAD_PREV) |
rb_fatal (parent, "Sibling in prev is null; tree was unbalanced.\n"); |
#endif |
sibling = parent->prev; |
goto next_node_red_parent; |
} |
|
else if (!(parent->flags & RB_RED)) { |
if ((sibling->flags & RB_THREAD_PREV || !(sibling->prev->flags & RB_RED)) && |
(sibling->flags & RB_THREAD_NEXT || !(sibling->next->flags & RB_RED))) { |
|
sibling->flags |= RB_RED; |
node = parent; |
if (keep_rbstack) RBSTACK_UP (rbstack, parent); |
else RBSTACK_POP (rbstack, parent); |
continue; |
} |
} |
|
else { |
next_node_red_parent: |
if ((sibling->flags & RB_THREAD_PREV || !(sibling->prev->flags & RB_RED)) && |
(sibling->flags & RB_THREAD_NEXT || !(sibling->next->flags & RB_RED))) { |
|
parent->flags &= ~RB_RED; |
sibling->flags |= RB_RED; |
break; |
} |
} |
|
if (sibling->flags & RB_THREAD_PREV || !(sibling->prev->flags & RB_RED)) { |
|
sibling->flags |= RB_RED; |
sibling = parent->prev = rot_left (sibling); |
sibling->flags &= ~RB_RED; |
} |
|
|
assert (!(sibling->flags & RB_RED)); |
rot_right (parent); |
if (keep_rbstack) top = RBSTACK_PEEK (rbstack); |
else RBSTACK_POP (rbstack, top); |
if (top) { |
SET_PTR_TO_CHILD (top, parent, sibling, sibling); |
if (parent->flags & RB_RED) { |
sibling->flags |= RB_RED; |
parent->flags &= ~RB_RED; |
} |
} |
else { |
top = sibling; |
assert (!(parent->flags & RB_RED)); |
} |
sibling->prev->flags &= ~RB_RED; |
if (keep_rbstack) { |
struct rbstack_ptr tmp = rbstack; |
rbstack_insert (&rbstack_top, &tmp, sibling); |
} |
break; |
} |
} while (parent); |
|
if (keep_rbstack) { |
RBSTACK_UP_TO_ROOT (rbstack, top); |
*rbstack_ptr = rbstack_top; |
} |
else { |
RBSTACK_FREE_SET_ROOT (rbstack, top); |
*rbstack_ptr = rbstack; |
} |
|
#ifdef PIKE_DEBUG |
if (top->flags & RB_RED) rb_fatal (top, "Root node not black.\n"); |
#endif |
return top; |
} |
|
void low_rb_init_root (struct rb_node_hdr *node) |
{ |
#ifdef PIKE_DEBUG |
if (!node) Pike_fatal ("New node is null.\n"); |
#endif |
#ifdef RB_STATS |
rb_num_adds++; |
#endif |
node->flags = (node->flags & ~RB_RED) | RB_THREAD_PREV | RB_THREAD_NEXT; |
node->prev = node->next = NULL; |
} |
|
|
void low_rb_link_at_prev (struct rb_node_hdr **root, struct rbstack_ptr rbstack, |
struct rb_node_hdr *new) |
{ |
struct rb_node_hdr *parent = RBSTACK_PEEK (rbstack); |
|
#ifdef PIKE_DEBUG |
if (!new) Pike_fatal ("New node is null.\n"); |
if (!parent) Pike_fatal ("Cannot link in root node.\n"); |
if (!(parent->flags & RB_THREAD_PREV)) |
Pike_fatal ("Cannot link in node at interior prev link.\n"); |
#endif |
#ifdef RB_STATS |
rb_num_adds++; |
#endif |
|
new->flags |= RB_RED | RB_THREAD_PREV | RB_THREAD_NEXT; |
new->prev = parent->prev; |
new->next = parent; |
parent->flags &= ~RB_THREAD_PREV; |
parent->prev = new; |
|
*root = rebalance_after_add (new, rbstack); |
} |
|
|
void low_rb_link_at_next (struct rb_node_hdr **root, struct rbstack_ptr rbstack, |
struct rb_node_hdr *new) |
{ |
struct rb_node_hdr *parent = RBSTACK_PEEK (rbstack); |
|
#ifdef PIKE_DEBUG |
if (!new) Pike_fatal ("New node is null.\n"); |
if (!parent) Pike_fatal ("Cannot link in root node.\n"); |
if (!(parent->flags & RB_THREAD_NEXT)) |
Pike_fatal ("Cannot link in node at interior next link.\n"); |
#endif |
#ifdef RB_STATS |
rb_num_adds++; |
#endif |
|
new->flags |= RB_RED | RB_THREAD_PREV | RB_THREAD_NEXT; |
new->prev = parent; |
new->next = parent->next; |
parent->flags &= ~RB_THREAD_NEXT; |
parent->next = new; |
|
*root = rebalance_after_add (new, rbstack); |
} |
|
#define DO_SIMPLE_UNLINK(UNLINK, PARENT, NODE) do { \ |
PARENT = RBSTACK_PEEK (rbstack); \ |
\ |
if (UNLINK->flags & RB_THREAD_PREV) { \ |
if (UNLINK->flags & RB_THREAD_NEXT) { \ |
if (PARENT) \ |
SET_PTR_TO_CHILD ( \ |
PARENT, UNLINK, \ |
(PARENT->flags |= RB_THREAD_PREV, NODE = UNLINK->prev), \ |
(PARENT->flags |= RB_THREAD_NEXT, NODE = UNLINK->next)); \ |
else \ |
NODE = NULL; \ |
goto simple_unlink_done; \ |
} \ |
\ |
else { /* prev is null. */ \ |
NODE = UNLINK->next; \ |
DO_IF_DEBUG ( \ |
/* Since UNLINK->prev is null, UNLINK->next must be red */ \ |
/* and can't have children, or else the tree is */ \ |
/* unbalanced. */ \ |
if (!(NODE->flags & RB_THREAD_PREV)) \ |
rb_fatal (PARENT, "Expected thread prev pointer in %p; " \ |
"tree is unbalanced.\n", NODE); \ |
); \ |
NODE->prev = UNLINK->prev; \ |
} \ |
} \ |
\ |
else { /* next is null. */ \ |
NODE = UNLINK->prev; \ |
DO_IF_DEBUG ( \ |
if (!(NODE->flags & RB_THREAD_NEXT)) \ |
rb_fatal (PARENT, "Expected thread next pointer in %p; " \ |
"tree is unbalanced.\n", NODE); \ |
); \ |
NODE->next = UNLINK->next; \ |
} \ |
\ |
if (PARENT) SET_PTR_TO_CHILD (PARENT, UNLINK, NODE, NODE); \ |
simple_unlink_done:; \ |
} while (0) |
|
#define ADJUST_STACK_TO_NEXT(RBSTACK, NEXT) do { \ |
struct rb_node_hdr *node = RBSTACK_PEEK (RBSTACK); \ |
if (NEXT && (!node || \ |
(!(node->flags & RB_THREAD_PREV) && node->prev == NEXT) || \ |
(!(node->flags & RB_THREAD_NEXT) && node->next == NEXT))) { \ |
RBSTACK_PUSH (RBSTACK, NEXT); \ |
while (!(NEXT->flags & RB_THREAD_PREV)) \ |
RBSTACK_PUSH (RBSTACK, NEXT = NEXT->prev); \ |
} \ |
else \ |
while (node != NEXT) { \ |
RBSTACK_POP (RBSTACK, node); \ |
assert (!NEXT || node != NULL); \ |
node = RBSTACK_PEEK (RBSTACK); \ |
} \ |
} while (0) |
|
|
|
|
|
|
|
|
|
|
|
|
struct rb_node_hdr *low_rb_unlink_with_move (struct rb_node_hdr **root, |
struct rbstack_ptr *rbstack_ptr, |
int keep_rbstack, |
size_t node_size) |
{ |
struct rb_node_hdr *node, *parent, *unlink, *next; |
struct rbstack_ptr rbstack = *rbstack_ptr; |
|
RBSTACK_POP (rbstack, node); |
#ifdef PIKE_DEBUG |
if (!node) Pike_fatal ("No node to delete on stack.\n"); |
#endif |
#ifdef RB_STATS |
rb_num_deletes++; |
#endif |
|
if (node->flags & (RB_THREAD_PREV|RB_THREAD_NEXT)) { |
next = node->next; |
unlink = node; |
DO_SIMPLE_UNLINK (unlink, parent, node); |
} |
|
else { |
|
|
next = parent = node; |
RBSTACK_PUSH (rbstack, node); |
for (unlink = node->next; !(unlink->flags & RB_THREAD_PREV); unlink = unlink->prev) { |
parent = unlink; |
RBSTACK_PUSH (rbstack, unlink); |
} |
|
keep_flags (node, |
MEMCPY ((char *) node + OFFSETOF (rb_node_hdr, flags), |
(char *) unlink + OFFSETOF (rb_node_hdr, flags), |
node_size - OFFSETOF (rb_node_hdr, flags))); |
|
if (parent == node) { |
node = unlink->next; |
if (unlink->flags & RB_THREAD_NEXT) |
parent->flags |= RB_THREAD_NEXT; |
else { |
#ifdef PIKE_DEBUG |
if (!(node->flags & RB_THREAD_PREV)) |
rb_fatal (parent, "Expected thread prev pointer in %p; " |
"tree is unbalanced.\n", node); |
#endif |
node->prev = unlink->prev; |
} |
parent->next = node; |
} |
|
else |
if (unlink->flags & RB_THREAD_NEXT) { |
parent->flags |= RB_THREAD_PREV; |
parent->prev = node = unlink->prev; |
} |
else { |
node = unlink->next; |
#ifdef PIKE_DEBUG |
if (!(node->flags & RB_THREAD_PREV)) |
rb_fatal (parent, "Expected thread prev pointer in %p; " |
"tree is unbalanced.\n", node); |
#endif |
node->prev = unlink->prev; |
parent->prev = node; |
} |
} |
|
if (unlink->flags & RB_RED) { |
if (!keep_rbstack) RBSTACK_FREE (rbstack); |
} |
else |
*root = rebalance_after_delete (node, &rbstack, keep_rbstack); |
|
if (keep_rbstack) ADJUST_STACK_TO_NEXT (rbstack, next); |
*rbstack_ptr = rbstack; |
return unlink; |
} |
|
#if 0 |
struct rb_node_hdr *low_rb_unlink_with_move (struct rb_node_hdr **root, |
struct rbstack_ptr *rbstack_ptr, |
int keep_rbstack, |
size_t node_size) |
{ |
struct rb_node_hdr *node = RBSTACK_PEEK (*rbstack_ptr), *next = rb_next (node); |
struct rb_node_hdr *unlink = |
real_low_rb_unlink_with_move (root, rbstack_ptr, 1, node_size); |
debug_check_rbstack (*root, *rbstack_ptr); |
if (node != unlink) next = node; |
if (RBSTACK_PEEK (*rbstack_ptr) != next) |
Pike_fatal ("Stack got %p on top, but next node is %p.\n", |
RBSTACK_PEEK (*rbstack_ptr), next); |
if (!keep_rbstack) RBSTACK_FREE (*rbstack_ptr); |
return unlink; |
} |
#endif |
|
|
|
|
void low_rb_unlink_without_move (struct rb_node_hdr **root, |
struct rbstack_ptr *rbstack_ptr, |
int keep_rbstack) |
{ |
struct rb_node_hdr *node, *parent, *unlink, *next; |
struct rbstack_ptr rbstack = *rbstack_ptr; |
int unlink_flags; |
|
RBSTACK_POP (rbstack, node); |
#ifdef PIKE_DEBUG |
if (!node) Pike_fatal ("No node to delete on stack.\n"); |
#endif |
#ifdef RB_STATS |
rb_num_deletes++; |
#endif |
|
if (node->flags & (RB_THREAD_PREV|RB_THREAD_NEXT)) { |
next = node->next; |
unlink = node; |
DO_SIMPLE_UNLINK (unlink, parent, node); |
unlink_flags = unlink->flags; |
} |
|
else { |
|
|
struct rb_node_hdr *orig_parent = RBSTACK_PEEK (rbstack), *tmp; |
struct rbstack_ptr pos; |
parent = unlink = node; |
RBSTACK_PUSH (rbstack, NULL); |
pos = rbstack; |
for (node = node->next; !(node->flags & RB_THREAD_PREV); node = node->prev) { |
parent = node; |
RBSTACK_PUSH (rbstack, node); |
} |
RBSTACK_POKE (pos, next = node); |
unlink_flags = node->flags; |
|
if (orig_parent) SET_PTR_TO_CHILD (orig_parent, unlink, node, node); |
else *root = node; |
for (tmp = unlink->prev; !(tmp->flags & RB_THREAD_NEXT); tmp = tmp->next) {} |
tmp->next = node; |
node->prev = unlink->prev; |
|
if (parent == unlink) { |
node->flags = |
(node->flags & ~(RB_FLAG_MASK & ~RB_THREAD_NEXT)) | |
(unlink->flags & (RB_FLAG_MASK & ~RB_THREAD_NEXT)); |
parent = node; |
node = parent->next; |
} |
|
else { |
if (node->flags & RB_THREAD_NEXT) { |
parent->flags |= RB_THREAD_PREV; |
parent->prev = node; |
} |
else parent->prev = node->next; |
node->flags = (node->flags & ~RB_FLAG_MASK) | (unlink->flags & RB_FLAG_MASK); |
node->next = unlink->next; |
node = parent->prev; |
} |
} |
|
if (unlink_flags & RB_RED) { |
if (!keep_rbstack) RBSTACK_FREE (rbstack); |
} |
else |
*root = rebalance_after_delete (node, &rbstack, keep_rbstack); |
|
if (keep_rbstack) ADJUST_STACK_TO_NEXT (rbstack, next); |
*rbstack_ptr = rbstack; |
} |
|
#if 0 |
void low_rb_unlink_without_move (struct rb_node_hdr **root, |
struct rbstack_ptr *rbstack_ptr, |
int keep_rbstack) |
{ |
struct rb_node_hdr *node = RBSTACK_PEEK (*rbstack_ptr), *next = rb_next (node); |
real_low_rb_unlink_without_move (root, rbstack_ptr, 1); |
debug_check_rbstack (*root, *rbstack_ptr); |
if (RBSTACK_PEEK (*rbstack_ptr) != next) |
Pike_fatal ("Stack got %p on top, but next node is %p.\n", |
RBSTACK_PEEK (*rbstack_ptr), next); |
if (!keep_rbstack) RBSTACK_FREE (*rbstack_ptr); |
} |
#endif |
|
#ifdef ENABLE_UNUSED_RB_FUNCTIONS |
|
* If you like to use them, just let me know (they are tested). /mast */ |
|
|
|
|
struct rb_node_hdr *rb_insert (struct rb_node_hdr **root, |
rb_find_fn *find_fn, void *key, |
struct rb_node_hdr *new) |
{ |
if (*root) { |
struct rb_node_hdr *node = *root; |
RBSTACK_INIT (rbstack); |
LOW_RB_TRACK ( |
rbstack, node, |
cmp_res = find_fn (key, node), |
{ |
low_rb_link_at_next (root, rbstack, new); |
}, { |
RBSTACK_FREE (rbstack); |
return node; |
}, { |
low_rb_link_at_prev (root, rbstack, new); |
}); |
} |
else { |
low_rb_init_root (new); |
*root = new; |
} |
return new; |
} |
|
void rb_add (struct rb_node_hdr **root, |
rb_find_fn *find_fn, void *key, |
struct rb_node_hdr *new) |
{ |
if (*root) { |
struct rb_node_hdr *node = *root; |
RBSTACK_INIT (rbstack); |
LOW_RB_TRACK_NEQ ( |
rbstack, node, cmp_res = find_fn (key, node) >= 0 ? 1 : -1, |
low_rb_link_at_next (root, rbstack, new), |
low_rb_link_at_prev (root, rbstack, new) |
); |
} |
else { |
low_rb_init_root (new); |
*root = new; |
} |
} |
|
void rb_add_after (struct rb_node_hdr **root, |
rb_find_fn *find_fn, void *key, |
struct rb_node_hdr *new, |
struct rb_node_hdr *existing) |
{ |
if (*root) { |
struct rb_node_hdr *node = *root; |
RBSTACK_INIT (rbstack); |
|
if (existing) { |
LOW_RB_TRACK_NEQ ( |
rbstack, node, cmp_res = find_fn (key, node) >= 0 ? 1 : -1, ;, ;); |
while (node != existing) { |
LOW_RB_TRACK_PREV (rbstack, node); |
#ifdef PIKE_DEBUG |
if (!node) Pike_fatal ("Tree doesn't contain the existing node.\n"); |
#endif |
} |
if (node->flags & RB_THREAD_NEXT) { |
low_rb_link_at_next (root, rbstack, new); |
return; |
} |
else node = node->next; |
} |
|
|
RBSTACK_PUSH (rbstack, node); |
while (!(node->flags & RB_THREAD_PREV)) { |
node = node->prev; |
RBSTACK_PUSH (rbstack, node); |
} |
low_rb_link_at_prev (root, rbstack, new); |
} |
|
else { |
#ifdef PIKE_DEBUG |
if (existing) Pike_fatal ("Tree doesn't contain the existing node.\n"); |
#endif |
low_rb_init_root (new); |
*root = new; |
} |
} |
|
|
|
struct rb_node_hdr *rb_remove (struct rb_node_hdr **root, |
rb_find_fn *find_fn, void *key) |
{ |
struct rb_node_hdr *node = *root; |
if (node) { |
RBSTACK_INIT (rbstack); |
LOW_RB_TRACK ( |
rbstack, node, |
cmp_res = find_fn (key, node), |
{ |
}, { |
low_rb_unlink_without_move (root, rbstack); |
return node; |
}, { |
}); |
RBSTACK_FREE (rbstack); |
} |
return NULL; |
} |
|
void rb_remove_node (struct rb_node_hdr **root, |
rb_find_fn *find_fn, void *key, |
struct rb_node_hdr *to_delete) |
{ |
struct rb_node_hdr *node = *root; |
RBSTACK_INIT (rbstack); |
#ifdef PIKE_DEBUG |
if (!node) Pike_fatal ("Tree is empty.\n"); |
#if 0 |
if (find_fn (key, to_delete)) |
Pike_fatal ("Given key doesn't match the node to delete.\n"); |
#endif |
#endif |
LOW_RB_TRACK_NEQ (rbstack, node, cmp_res = find_fn (key, node) >= 0 ? 1 : -1, ;, ;); |
while (node != to_delete) { |
LOW_RB_TRACK_PREV (rbstack, node); |
#ifdef PIKE_DEBUG |
if (!node) Pike_fatal ("Tree doesn't contain the node to delete.\n"); |
#endif |
} |
low_rb_unlink_without_move (root, rbstack); |
} |
|
|
|
|
|
|
struct rb_node_hdr *rb_remove_with_move (struct rb_node_hdr **root, |
rb_find_fn *find_fn, void *key, |
size_t node_size, |
rb_free_fn *cleanup_fn, |
void *cleanup_fn_extra) |
{ |
struct rb_node_hdr *node = *root; |
if (node) { |
RBSTACK_INIT (rbstack); |
LOW_RB_TRACK ( |
rbstack, node, |
cmp_res = find_fn (key, node), |
{ |
}, { |
if (cleanup_fn) cleanup_fn (node, cleanup_fn_extra); |
return low_rb_unlink_with_move (root, rbstack, node_size); |
}, { |
}); |
RBSTACK_FREE (rbstack); |
} |
return NULL; |
} |
|
|
|
struct rb_node_hdr *rb_remove_node_with_move (struct rb_node_hdr **root, |
rb_find_fn *find_fn, void *key, |
struct rb_node_hdr *to_delete, |
size_t node_size) |
{ |
struct rb_node_hdr *node = *root; |
RBSTACK_INIT (rbstack); |
#ifdef PIKE_DEBUG |
if (!node) Pike_fatal ("Tree is empty.\n"); |
#if 0 |
if (find_fn (key, to_delete)) |
Pike_fatal ("Given key doesn't match the node to delete.\n"); |
#endif |
#endif |
LOW_RB_TRACK_NEQ (rbstack, node, cmp_res = find_fn (key, node) >= 0 ? 1 : -1, ;, ;); |
while (node != to_delete) { |
LOW_RB_TRACK_PREV (rbstack, node); |
#ifdef PIKE_DEBUG |
if (!node) Pike_fatal ("Tree doesn't contain the node to delete.\n"); |
#endif |
} |
return low_rb_unlink_with_move (root, rbstack, node_size); |
} |
|
struct rb_node_hdr *rb_find_eq (struct rb_node_hdr *root, |
rb_find_fn *find_fn, void *key) |
{ |
if (root) |
LOW_RB_FIND (root, cmp_res = find_fn (key, root), ;, return root, ;); |
return NULL; |
} |
|
struct rb_node_hdr *rb_find_lt (struct rb_node_hdr *root, |
rb_find_fn *find_fn, void *key) |
{ |
if (root) |
LOW_RB_FIND_NEQ (root, cmp_res = find_fn (key, root) <= 0 ? -1 : 1, |
return root, return root->prev); |
return NULL; |
} |
|
struct rb_node_hdr *rb_find_gt (struct rb_node_hdr *root, |
rb_find_fn *find_fn, void *key) |
{ |
if (root) |
LOW_RB_FIND_NEQ (root, cmp_res = find_fn (key, root) >= 0 ? 1 : -1, |
return root->next, return root); |
return NULL; |
} |
|
struct rb_node_hdr *rb_find_le (struct rb_node_hdr *root, |
rb_find_fn *find_fn, void *key) |
{ |
if (root) |
LOW_RB_FIND_NEQ (root, cmp_res = find_fn (key, root) >= 0 ? 1 : -1, |
return root, return root->prev); |
return NULL; |
} |
|
struct rb_node_hdr *rb_find_ge (struct rb_node_hdr *root, |
rb_find_fn *find_fn, void *key) |
{ |
if (root) |
LOW_RB_FIND_NEQ (root, cmp_res = find_fn (key, root) <= 0 ? -1 : 1, |
return root->next, return root); |
return NULL; |
} |
|
#endif /* ENABLE_UNUSED_RB_FUNCTIONS */ |
|
struct rb_node_hdr *rb_get_nth (struct rb_node_hdr *root, size_t n) |
{ |
#ifdef PIKE_DEBUG |
size_t index = n; |
#endif |
if ((root = rb_first (root))) { |
while (n) { |
n--; |
root = rb_next (root); |
#ifdef PIKE_DEBUG |
if (!root) goto tree_too_small; |
#endif |
} |
return root; |
} |
#ifdef PIKE_DEBUG |
tree_too_small: |
Pike_fatal ("Tree too small for index %"PRINTSIZET"u.\n", index); |
#endif |
return (struct rb_node_hdr *) (ptrdiff_t) -1; |
} |
|
size_t rb_sizeof (struct rb_node_hdr *root) |
{ |
size_t size = 0; |
if ((root = rb_first (root))) { |
do { |
size++; |
} while ((root = rb_next (root))); |
} |
return size; |
} |
|
#ifdef ENABLE_UNUSED_RB_FUNCTIONS |
|
void rb_free (struct rb_node_hdr *root, rb_free_fn *free_node_fn, void *extra) |
{ |
if ((root = rb_first (root))) { |
struct rb_node_hdr *next; |
do { |
next = rb_next (root); |
free_node_fn (root, extra); |
root = next; |
} while (root); |
} |
} |
|
int rb_equal (struct rb_node_hdr *a, struct rb_node_hdr *b, |
rb_equal_fn *node_equal_fn, void *extra) |
{ |
a = rb_first (a); |
b = rb_first (b); |
while (a && b) { |
if (!node_equal_fn (a, b, extra)) return 0; |
a = rb_next (a); |
b = rb_next (b); |
} |
return !(a || b); |
} |
|
struct rb_node_hdr *rb_copy (struct rb_node_hdr *source, |
rb_copy_fn *copy_node_fn, void *extra) |
{ |
if (source) { |
struct rb_node_hdr *copy, *target, *new, *t_prev_tgt = NULL, *t_next_src = NULL; |
RBSTACK_INIT (s_stack); |
RBSTACK_INIT (t_stack); |
|
copy = target = copy_node_fn (source, extra); |
copy->flags = (copy->flags & ~RB_FLAG_MASK) | (source->flags & RB_FLAG_MASK); |
|
LOW_RB_TRAVERSE ( |
1, s_stack, source, |
{ |
}, { |
target->prev = t_prev_tgt; |
}, { |
new = target->prev = copy_node_fn (source->prev, extra); |
new->flags = (new->flags & ~RB_FLAG_MASK) | (source->prev->flags & RB_FLAG_MASK); |
RBSTACK_PUSH (t_stack, target); |
target = new; |
}, { |
t_prev_tgt = target; |
if (t_next_src) { |
t_next_src->next = target; |
t_next_src = NULL; |
} |
}, { |
t_next_src = target; |
}, { |
new = target->next = copy_node_fn (source->next, extra); |
new->flags = (new->flags & ~RB_FLAG_MASK) | (source->next->flags & RB_FLAG_MASK); |
RBSTACK_PUSH (t_stack, target); |
target = new; |
}, { |
RBSTACK_POP (t_stack, target); |
}); |
|
if (t_next_src) t_next_src->next = NULL; |
return copy; |
} |
else return NULL; |
} |
|
#endif /* ENABLE_UNUSED_RB_FUNCTIONS */ |
|
|
|
struct rb_node_hdr *rb_make_list (struct rb_node_hdr *tree) |
{ |
struct rb_node_hdr *list = tree = rb_first (tree); |
if (tree) |
while ((tree = tree->next = rb_next (tree))) {} |
return list; |
} |
|
|
|
|
struct rb_node_hdr *rb_make_tree (struct rb_node_hdr *list, size_t length) |
{ |
struct rb_node_hdr *root = NULL; |
|
if (length > 1) { |
unsigned depth, *top_idx; |
size_t deep_end, count = 0, start = 0, idx = 1; |
struct rb_node_hdr **stack, *prev_tgt = NULL, *next_src = NULL; |
int stack_malloced = 0; |
|
{ |
unsigned l = 0, h = sizeof (length) * CHAR_BIT; |
do { |
depth = (l + h) >> 1; |
if (1u << depth <= length) l = depth + 1; else h = depth; |
} while (l < h); |
if (1u << depth <= length) depth++; |
} |
|
deep_end = (length - (1 << (depth - 1))) << 1; |
if (!(length & 1)) deep_end |= 1; |
|
{ |
size_t stack_size = |
depth * sizeof (struct rb_node_hdr *) + |
(depth + 1) * sizeof (unsigned); |
#ifdef HAVE_ALLOCA |
stack = alloca (stack_size); |
if (!stack) |
#endif |
{ |
stack = xalloc (stack_size); |
stack_malloced = 1; |
} |
top_idx = (unsigned *) stack + depth; |
memset (top_idx, 0, (depth + 1) * sizeof (unsigned)); |
depth--; |
} |
|
while (count < length) { |
struct rb_node_hdr *next; |
#ifdef PIKE_DEBUG |
if (!list) Pike_fatal ("Premature end of list at %"PRINTSIZET"u, " |
"expected %"PRINTSIZET"u.\n", count, length); |
#endif |
next = list->next; |
|
if (idx > start) { |
idx = start; |
list->flags |= RB_THREAD_PREV|RB_THREAD_NEXT; |
next_src = stack[idx] = list; |
list->prev = prev_tgt; |
} |
else { |
idx = top_idx[idx]; |
list->flags &= ~(RB_THREAD_PREV|RB_THREAD_NEXT); |
prev_tgt = next_src->next = stack[idx] = list; |
list->prev = stack[idx - 1]; |
if (idx == depth) { |
assert (!root); |
root = list; |
} |
} |
|
if (count++ == deep_end) { |
if (idx) { |
next_src = list; |
list->flags |= RB_THREAD_NEXT; |
} |
start = 1; |
} |
|
if (idx & 1) list->flags &= ~RB_RED; |
else list->flags |= RB_RED; |
|
if (top_idx[idx] == top_idx[idx + 1]) |
top_idx[idx] = idx + 1; |
else { |
top_idx[idx] = top_idx[idx + 1]; |
stack[idx + 1]->next = list; |
} |
|
list = next; |
} |
|
next_src->next = NULL; |
root->flags &= ~RB_RED; |
if (stack_malloced) xfree (stack); |
} |
else if (length) { |
root = list; |
#ifdef PIKE_DEBUG |
list = list->next; |
#endif |
low_rb_init_root (root); |
} |
|
#ifdef PIKE_DEBUG |
if (list) Pike_fatal ("List longer than expected %d.\n", length); |
#endif |
return root; |
} |
|
#ifdef ENABLE_UNUSED_RB_FUNCTIONS |
|
struct linear_merge_cleanup |
{ |
int operation; |
struct rb_node_hdr *a, *b, *res; |
rb_merge_free_fn *free_node_fn; |
void *extra; |
}; |
|
static void do_linear_merge_cleanup (struct linear_merge_cleanup *s) |
{ |
struct rb_node_hdr *node, *next; |
rb_merge_free_fn *free_node_fn = s->free_node_fn; |
void *extra = s->extra; |
|
for (node = s->res; node; node = next) { |
next = node->next; |
free_node_fn (node, extra, MERGE_TREE_RES); |
} |
|
if (s->operation & PIKE_MERGE_DESTR_A) |
for (node = s->a; node; node = next) { |
next = rb_prev (node); |
free_node_fn (node, extra, MERGE_TREE_A); |
} |
|
if (s->operation & PIKE_MERGE_DESTR_B) |
for (node = s->b; node; node = next) { |
next = rb_prev (node); |
free_node_fn (node, extra, MERGE_TREE_B); |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct rb_node_hdr *rb_linear_merge ( |
struct rb_node_hdr *a, struct rb_node_hdr *b, int operation, |
rb_cmp_fn *cmp_fn, void *cmp_fn_extra, |
rb_merge_copy_fn *copy_node_fn, void *copy_fn_extra, |
rb_merge_free_fn *free_node_fn, void *free_fn_extra, |
size_t *length) |
{ |
struct rb_node_hdr *n; |
size_t len = 0; |
int cmp_res, op; |
struct linear_merge_cleanup s; |
ONERROR uwp; |
|
s.operation = operation; |
s.free_node_fn = free_node_fn; |
s.extra = free_fn_extra; |
SET_ONERROR (uwp, do_linear_merge_cleanup, &s); |
|
LOW_RB_MERGE ( |
x, s.a, s.b, s.res, len, operation, ;, ;, |
{ |
cmp_res = cmp_fn (s.a, s.b, cmp_fn_extra); |
}, { |
new_node = operation & PIKE_MERGE_DESTR_A ? s.a : |
copy_node_fn (s.a, copy_fn_extra, MERGE_TREE_A); |
}, { |
if (operation & PIKE_MERGE_DESTR_A) |
free_node_fn (s.a, free_fn_extra, MERGE_TREE_A); |
}, { |
new_node = operation & PIKE_MERGE_DESTR_B ? s.b : |
copy_node_fn (s.b, copy_fn_extra, MERGE_TREE_B); |
}, { |
if (operation & PIKE_MERGE_DESTR_B) |
free_node_fn (s.b, free_fn_extra, MERGE_TREE_B); |
}); |
|
UNSET_ONERROR (uwp); |
|
if (length) *length = len; |
return s.res; |
} |
|
#endif /* ENABLE_UNUSED_RB_FUNCTIONS */ |
|
#ifdef RB_STATS |
|
size_t rb_num_finds = 0, rb_find_depth = 0; |
size_t rb_num_sidesteps = 0, rb_num_sidestep_ops = 0; |
size_t rb_num_tracks = 0, rb_track_depth = 0; |
size_t rb_num_sidetracks = 0, rb_num_sidetrack_ops = 0; |
size_t rb_max_depth = 0; |
size_t rb_num_traverses = 0, rb_num_traverse_ops = 0; |
size_t rbstack_slice_allocs = 0; |
size_t rb_num_adds = 0, rb_add_rebalance_cnt = 0; |
size_t rb_num_deletes = 0, rb_del_rebalance_cnt = 0; |
|
void reset_rb_stats() |
{ |
rb_num_sidesteps = rb_num_sidestep_ops = 0; |
rb_num_finds = rb_find_depth = 0; |
rb_num_tracks = rb_track_depth = 0; |
rb_num_sidetracks = rb_num_sidetrack_ops = 0; |
rb_max_depth = 0; |
rb_num_traverses = rb_num_traverse_ops = 0; |
rbstack_slice_allocs = 0; |
rb_num_adds = rb_add_rebalance_cnt = 0; |
rb_num_deletes = rb_del_rebalance_cnt = 0; |
} |
|
void print_rb_stats (int reset) |
{ |
fprintf (stderr, |
"rbtree stats:\n" |
" number of finds . . . . . . %10"PRINTSIZET"u\n" |
" avg find depth . . . . . . . %10.2f\n" |
" number of sidesteps . . . . %10"PRINTSIZET"u\n" |
" avg ops per sidestep . . . . %10.2f\n" |
" number of tracks . . . . . . %10"PRINTSIZET"u\n" |
" avg track depth . . . . . . %10.2f\n" |
" number of sidetracks . . . . %10"PRINTSIZET"u\n" |
" avg ops per sidetrack . . . %10.2f\n" |
" maximum depth . . . . . . . %10"PRINTSIZET"u\n" |
" number of traverses . . . . %10"PRINTSIZET"u\n" |
" avg ops per traverse . . . . %10.2f\n" |
" allocated stack slices . . . %10"PRINTSIZET"u\n" |
" number of adds . . . . . . . %10"PRINTSIZET"u\n" |
" avg add rebalance count . . %10.2f\n" |
" number of deletes . . . . . %10"PRINTSIZET"u\n" |
" avg delete rebalance count . %10.2f\n", |
rb_num_finds, |
(double) rb_find_depth / rb_num_finds, |
rb_num_sidesteps, |
(double) rb_num_sidestep_ops / rb_num_sidesteps, |
rb_num_tracks, |
(double) rb_track_depth / rb_num_tracks, |
rb_num_sidetracks, |
(double) rb_num_sidetrack_ops / rb_num_sidetracks, |
rb_max_depth, |
rb_num_traverses, |
(double) rb_num_traverse_ops / rb_num_traverses, |
rbstack_slice_allocs, |
rb_num_adds, |
(double) rb_add_rebalance_cnt / rb_num_adds, |
rb_num_deletes, |
(double) rb_del_rebalance_cnt / rb_num_deletes); |
if (reset) reset_rb_stats(); |
} |
|
#endif /* RB_STATS */ |
|
#if defined (PIKE_DEBUG) || defined (TEST_MULTISET) |
|
void debug_dump_rb_tree (struct rb_node_hdr *root, dump_data_fn *dump_data, |
void *extra) |
{ |
if (root) { |
struct rb_node_hdr *node = root; |
struct rb_node_hdr *n, *n2; |
struct rbstack_ptr p; |
size_t depth = 0; |
RBSTACK_INIT (rbstack); |
|
LOW_RB_TRAVERSE ( |
1, rbstack, node, |
{ |
depth++; |
p = rbstack; |
RBSTACK_UP (p, n); |
while (n) { |
if (n == node) { |
fprintf (stderr, "[Circular %p]", node); |
goto skip_node; |
} |
RBSTACK_UP (p, n); |
} |
fputc ('(', stderr); |
}, { |
p = rbstack; |
n2 = node; |
RBSTACK_UP (p, n); |
while (n && (n->flags & RB_THREAD_NEXT || n->next != n2)) { |
n2 = n; |
RBSTACK_UP (p, n); |
} |
if (node->prev != n) |
fprintf (stderr, "[Thread ptr is %p, expected %p] ", node->prev, n); |
}, { |
if (!node->prev) { |
fputs ("[Zero subtree]", stderr); |
goto between_1; |
} |
}, { |
if (!(node->flags & RB_THREAD_PREV)) |
fprintf (stderr, "\n%*s", depth, ""); |
if (node == root) fputs ("root=", stderr); |
fprintf (stderr, "%p/%c", |
node, node->flags & RB_RED ? 'R' : 'B'); |
if (dump_data) { |
fputs (": ", stderr); |
dump_data (node, extra); |
} |
}, { |
p = rbstack; |
n2 = node; |
RBSTACK_UP (p, n); |
while (n && (n->flags & RB_THREAD_PREV || n->prev != n2)) { |
n2 = n; |
RBSTACK_UP (p, n); |
} |
if (node->next != n) |
fprintf (stderr, " [Thread ptr is %p, expected %p]", node->next, n); |
}, { |
if (!node->next) { |
fputs (" [Zero subtree]", stderr); |
goto leave_1; |
} |
fprintf (stderr, "\n%*s", depth, ""); |
}, { |
fputc (')', stderr); |
depth--; |
skip_node: |
; |
}); |
fputc ('\n', stderr); |
} |
else |
fprintf (stderr, "(empty tree)\n"); |
} |
|
|
|
void debug_dump_rbstack (struct rbstack_ptr rbstack, struct rbstack_ptr *pos) |
{ |
struct rb_node_hdr *node, *ptr_node = (void *) (ptrdiff_t) -1; |
RBSTACK_INIT (reversed); |
|
while (1) { |
int here = pos ? pos->ssp == rbstack.ssp && pos->slice == rbstack.slice : 0; |
RBSTACK_UP (rbstack, node); |
if (here) ptr_node = node; |
if (!node) break; |
RBSTACK_PUSH (reversed, node); |
} |
|
if (pos && !ptr_node) fputs ("* ", stderr); |
|
while (1) { |
RBSTACK_POP (reversed, node); |
if (!node) break; |
fprintf (stderr, "%p ", node); |
if (pos && ptr_node == node) fputs ("* ", stderr); |
} |
|
if (pos && ptr_node == (void *) (ptrdiff_t) -1) |
fputs ("(pos outside stack)", stderr); |
fputc ('\n', stderr); |
} |
|
static void debug_rb_fatal (struct rb_node_hdr *tree, const char *fmt, ...) |
{ |
va_list args; |
va_start (args, fmt); |
(void) VFPRINTF (stderr, fmt, args); |
fputs ("Dumping tree:\n", stderr); |
debug_dump_rb_tree (tree, NULL, NULL); |
debug_fatal ("\r"); |
} |
|
static void debug_custom_rb_fatal (struct rb_node_hdr *tree, dump_data_fn *dump_data, |
void *extra, const char *fmt, ...) |
{ |
va_list args; |
va_start (args, fmt); |
(void) VFPRINTF (stderr, fmt, args); |
fputs ("Dumping tree:\n", stderr); |
debug_dump_rb_tree (tree, dump_data, extra); |
debug_fatal ("\r"); |
} |
|
void debug_check_rb_tree (struct rb_node_hdr *root, dump_data_fn *dump_data, void *extra) |
{ |
if (root) { |
struct rb_node_hdr *node = root, *n, *n2; |
struct rbstack_ptr p; |
size_t blacks = 1, max_blacks = 0, depth = 0; |
RBSTACK_INIT (rbstack); |
|
if (root->flags & RB_RED) custom_rb_fatal (root, dump_data, extra, |
"Root node not black.\n"); |
|
LOW_RB_TRAVERSE ( |
1, rbstack, node, |
{ |
depth++; |
p = rbstack; |
RBSTACK_UP (p, n); |
while (n) { |
if (n == node) custom_rb_fatal (root, dump_data, extra, |
"Circular subtrees @ %p.\n", node); |
RBSTACK_UP (p, n); |
} |
|
if (!(node->flags & RB_RED)) blacks++; |
}, { |
if (max_blacks) { |
if (blacks != max_blacks) |
custom_rb_fatal (root, dump_data, extra, |
"Unbalanced tree - leftmost branch is %d, " |
"prev @ %p is %d.\n", max_blacks, node, blacks); |
} |
else max_blacks = blacks; |
|
p = rbstack; |
n2 = node; |
RBSTACK_UP (p, n); |
while (n && (n->flags & RB_THREAD_NEXT || n->next != n2)) { |
n2 = n; |
RBSTACK_UP (p, n); |
} |
if (node->prev != n) |
custom_rb_fatal (root, dump_data, extra, |
"Thread prev pointer @ %p is %p, expected %p.\n", |
node, node->prev, n); |
}, { |
if (!node->prev) custom_rb_fatal (root, dump_data, extra, |
"Subtree prev is null @ %p.\n", node); |
if (node->flags & RB_RED && node->prev->flags & RB_RED) |
custom_rb_fatal (root, dump_data, extra, |
"Red node got red subtree prev node @ %p.\n", node); |
}, { |
}, { |
if (blacks != max_blacks) |
custom_rb_fatal (root, dump_data, extra, |
"Unbalanced tree - leftmost branch is %d, " |
"next @ %p is %d.\n", max_blacks, node, blacks); |
|
p = rbstack; |
n2 = node; |
RBSTACK_UP (p, n); |
while (n && (n->flags & RB_THREAD_PREV || n->prev != n2)) { |
n2 = n; |
RBSTACK_UP (p, n); |
} |
if (node->next != n) |
custom_rb_fatal (root, dump_data, extra, |
"Thread next pointer @ %p is %p, expected %p.\n", |
node, node->next, n); |
}, { |
if (!node->next) custom_rb_fatal (root, dump_data, extra, |
"Subtree next is null @ %p.\n", node); |
if (node->flags & RB_RED && node->next->flags & RB_RED) |
custom_rb_fatal (root, dump_data, extra, |
"Red node got red subtree next node @ %p.\n", node); |
}, { |
if (!(node->flags & RB_RED)) blacks--; |
depth--; |
}); |
} |
} |
|
void debug_check_rbstack (struct rb_node_hdr *root, struct rbstack_ptr rbstack) |
{ |
struct rbstack_ptr rbstack_top = rbstack; |
struct rb_node_hdr *snode, *tnode = root; |
RBSTACK_INIT (reversed); |
|
while (1) { |
RBSTACK_UP (rbstack, snode); |
if (!snode) break; |
RBSTACK_PUSH (reversed, snode); |
} |
|
RBSTACK_POP (reversed, snode); |
if (!snode) return; |
if (snode != tnode) { |
fputs ("rbstack: ", stderr); |
debug_dump_rbstack (rbstack_top, NULL); |
rb_fatal (root, "rbstack root %p != tree root.\n", snode); |
} |
|
while (1) { |
RBSTACK_POP (reversed, snode); |
if (!snode) break; |
if (!(tnode->flags & RB_THREAD_PREV) && snode == tnode->prev) |
{tnode = snode; continue;} |
if (!(tnode->flags & RB_THREAD_NEXT) && snode == tnode->next) |
{tnode = snode; continue;} |
rb_fatal (root, "rbstack differs from tree: Node %p got no child %p.\n", |
tnode, snode); |
} |
} |
|
#endif /* PIKE_DEBUG || TEST_MULTISET */ |
|
|