|
|
|
/*
|
|
|
|
* Copyright 1995-2015 Andrew Smith
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
|
|
* Software Foundation; either version 3 of the License, or (at your option)
|
|
|
|
* any later version. See COPYING for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "ktree.h"
|
|
|
|
|
|
|
|
static const int dbg = 0;
|
|
|
|
#define DBG if (dbg != 0) printf
|
|
|
|
|
|
|
|
#define FAIL(fmt, ...) do \
|
|
|
|
{ \
|
|
|
|
quithere(1, fmt KTREE_FFL, ##__VA_ARGS__, KTREE_FFL_PASS); \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define RED_RED true
|
|
|
|
#define RED_BLACK false
|
|
|
|
|
|
|
|
#define Yo true
|
|
|
|
#define No false
|
|
|
|
|
|
|
|
static K_NODE nil[1] = { { NULL, Yo, RED_BLACK, NULL, NULL, NULL, NULL, 0 } };
|
|
|
|
|
|
|
|
static K_NODE *_new_knode(K_TREE *tree, LOCK_MAYBE bool chklock, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
K_ITEM *kitem;
|
|
|
|
K_NODE *knode;
|
|
|
|
|
|
|
|
// master protects the tree's node list
|
|
|
|
_TREE_WRITE(tree, chklock, file, func, line);
|
|
|
|
kitem = k_unlink_head_nolock(tree->node_free);
|
|
|
|
if (!kitem)
|
|
|
|
FAIL("%s", "node list OOM");
|
|
|
|
k_add_head_nolock(tree->node_store, kitem);
|
|
|
|
knode = (K_NODE *)(kitem->data);
|
|
|
|
|
|
|
|
knode->kitem = kitem;
|
|
|
|
knode->isNil = Yo;
|
|
|
|
knode->red = RED_BLACK;
|
|
|
|
knode->parent = nil;
|
|
|
|
knode->left = nil;
|
|
|
|
knode->right = nil;
|
|
|
|
knode->data = NULL;
|
|
|
|
knode->test = 0;
|
|
|
|
|
|
|
|
return knode;
|
|
|
|
}
|
|
|
|
|
|
|
|
K_TREE *_new_ktree(const char *name, cmp_t (*cmp_funct)(K_ITEM *, K_ITEM *),
|
|
|
|
K_LIST *master, int alloc, int limit, bool local_tree,
|
|
|
|
KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
K_TREE *tree = (K_TREE *)malloc(sizeof(*tree));
|
|
|
|
|
|
|
|
if (tree == NULL)
|
|
|
|
FAIL("%s", "tree OOM");
|
|
|
|
|
|
|
|
if (name == NULL)
|
|
|
|
tree->name = master->name;
|
|
|
|
else
|
|
|
|
tree->name = name;
|
|
|
|
|
|
|
|
/* A unique "name" isn't needed since it can't use the wrong list
|
|
|
|
* and thus we can also identify all tree node lists */
|
|
|
|
tree->node_free = k_new_tree_list(tree_node_list_name, sizeof(K_NODE),
|
|
|
|
alloc, limit, true, local_tree,
|
|
|
|
tree->name);
|
|
|
|
#if LOCK_CHECK
|
|
|
|
DLPRIO(tree->node, PRIO_TERMINAL);
|
|
|
|
#endif
|
|
|
|
tree->node_store = k_new_store(tree->node_free);
|
|
|
|
|
|
|
|
// A new tree's list doesn't need to be locked during creation
|
|
|
|
tree->root = _new_knode(tree, false, KTREE_FFL_PASS);
|
|
|
|
|
|
|
|
tree->cmp_funct = cmp_funct;
|
|
|
|
|
|
|
|
tree->master = master;
|
|
|
|
|
|
|
|
return tree;
|
|
|
|
}
|
|
|
|
|
|
|
|
static K_NODE *new_data(K_TREE *tree, K_ITEM *data, LOCK_MAYBE bool chklock, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
K_ITEM *kitem;
|
|
|
|
K_NODE *knode;
|
|
|
|
|
|
|
|
// master protects the tree's node list
|
|
|
|
_TREE_WRITE(tree, chklock, file, func, line);
|
|
|
|
kitem = k_unlink_head_nolock(tree->node_free);
|
|
|
|
if (!kitem)
|
|
|
|
FAIL("%s", "node list OOM");
|
|
|
|
k_add_head_nolock(tree->node_store, kitem);
|
|
|
|
knode = (K_NODE *)(kitem->data);
|
|
|
|
|
|
|
|
knode->kitem = kitem;
|
|
|
|
knode->isNil = No;
|
|
|
|
knode->red = RED_RED;
|
|
|
|
knode->parent = nil;
|
|
|
|
knode->left = nil;
|
|
|
|
knode->right = nil;
|
|
|
|
knode->data = data;
|
|
|
|
knode->test = 0;
|
|
|
|
|
|
|
|
return knode;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bCount = 0;
|
|
|
|
static long bTestValue = 0;
|
|
|
|
static long nilTestValue = 0;
|
|
|
|
static long lrpTestValue = 0;
|
|
|
|
|
|
|
|
static long testValue = 1231230L;
|
|
|
|
|
|
|
|
static long getTestValue()
|
|
|
|
{
|
|
|
|
return ++testValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void show_ktree(K_NODE *node, char *path, int pos, char *(*dsp_funct)(K_ITEM *))
|
|
|
|
{
|
|
|
|
char col;
|
|
|
|
|
|
|
|
if (node->isNil == Yo)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (node->left->isNil == No)
|
|
|
|
{
|
|
|
|
path[pos] = 'L';
|
|
|
|
path[pos+1] = '\0';
|
|
|
|
show_ktree(node->left, path, pos+1, dsp_funct);
|
|
|
|
}
|
|
|
|
|
|
|
|
path[pos] = '\0';
|
|
|
|
|
|
|
|
if (node->red == RED_RED)
|
|
|
|
col = 'R';
|
|
|
|
else
|
|
|
|
// if (node->red == RED_BLACK)
|
|
|
|
col = 'B';
|
|
|
|
|
|
|
|
printf(" %c %s=%s\n", col, path, dsp_funct(node->data));
|
|
|
|
|
|
|
|
if (node->right->isNil == No)
|
|
|
|
{
|
|
|
|
path[pos] = 'R';
|
|
|
|
path[pos+1] = '\0';
|
|
|
|
show_ktree(node->right, path, pos+1, dsp_funct);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void _dump_ktree(K_TREE *tree, char *(*dsp_funct)(K_ITEM *), KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
char buf[42424];
|
|
|
|
|
|
|
|
_TREE_READ(tree, true, file, func, line);
|
|
|
|
|
|
|
|
printf("dump:\n");
|
|
|
|
if (tree->root->isNil == No)
|
|
|
|
{
|
|
|
|
buf[0] = 'T';
|
|
|
|
buf[1] = '\0';
|
|
|
|
show_ktree(tree->root, buf, 1, dsp_funct);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
printf(" Empty tree\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void _dsp_ktree(K_TREE *tree, char *filename, char *msg, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
K_TREE_CTX ctx[1];
|
|
|
|
K_ITEM *item;
|
|
|
|
FILE *stream;
|
|
|
|
struct tm tm;
|
|
|
|
time_t now_t;
|
|
|
|
char stamp[128];
|
|
|
|
|
|
|
|
if (!(tree->master->dsp_func))
|
|
|
|
FAIL("NULLDSP NULL dsp_func in %s", tree->master->name);
|
|
|
|
|
|
|
|
_TREE_READ(tree, true, file, func, line);
|
|
|
|
|
|
|
|
now_t = time(NULL);
|
|
|
|
localtime_r(&now_t, &tm);
|
|
|
|
snprintf(stamp, sizeof(stamp),
|
|
|
|
"[%d-%02d-%02d %02d:%02d:%02d]",
|
|
|
|
tm.tm_year + 1900,
|
|
|
|
tm.tm_mon + 1,
|
|
|
|
tm.tm_mday,
|
|
|
|
tm.tm_hour,
|
|
|
|
tm.tm_min,
|
|
|
|
tm.tm_sec);
|
|
|
|
|
|
|
|
stream = fopen(filename, "ae");
|
|
|
|
if (!stream)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s %s() failed to open '%s' (%d) %s",
|
|
|
|
stamp, __func__, filename, errno, strerror(errno));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg)
|
|
|
|
fprintf(stream, "%s %s\n", stamp, msg);
|
|
|
|
else
|
|
|
|
fprintf(stream, "%s Dump of tree '%s':\n", stamp, tree->master->name);
|
|
|
|
|
|
|
|
if (tree->root->isNil == No)
|
|
|
|
{
|
|
|
|
item = first_in_ktree(tree, ctx);
|
|
|
|
while (item)
|
|
|
|
{
|
|
|
|
tree->master->dsp_func(item, stream);
|
|
|
|
item = next_in_ktree(ctx);
|
|
|
|
}
|
|
|
|
fprintf(stream, "End\n\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fprintf(stream, "Empty ktree\n\n");
|
|
|
|
|
|
|
|
fclose(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nilTest(K_NODE *node, char *msg, int depth, int count, K_NODE *nil2, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
if (node->isNil == Yo || node == nil2)
|
|
|
|
{
|
|
|
|
if (node == nil2 && node->isNil == No)
|
|
|
|
FAIL("NIL2NOTNIL '%s' depth=%d count=%d", msg, depth, count);
|
|
|
|
|
|
|
|
if (node != nil && node != nil2)
|
|
|
|
FAIL("NOTNILNIL2 '%s' depth=%d count=%d", msg, depth, count);
|
|
|
|
|
|
|
|
if (node->red == RED_RED)
|
|
|
|
FAIL("NIRED '%s' depth=%d count=%d", msg, depth, count);
|
|
|
|
|
|
|
|
if (node != nil2 && node->parent != NULL)
|
|
|
|
FAIL("NILPARENT '%s' depth=%d count=%d", msg, depth, count);
|
|
|
|
|
|
|
|
if (node != nil2 && node->left != NULL)
|
|
|
|
FAIL("NILLEFT '%s' depth=%d count=%d", msg, depth, count);
|
|
|
|
|
|
|
|
if (node == nil2 && node->left != nil)
|
|
|
|
FAIL("NIL2LEFT '%s' depth=%d count=%d", msg, depth, count);
|
|
|
|
|
|
|
|
if (node != nil2 && node->right != NULL)
|
|
|
|
FAIL("NILRIGHT '%s' depth=%d count=%d", msg, depth, count);
|
|
|
|
|
|
|
|
if (node == nil2 && node->right != nil)
|
|
|
|
FAIL("NIL2RIGHT '%s' depth=%d count=%d", msg, depth, count);
|
|
|
|
|
|
|
|
if (node->data != NULL)
|
|
|
|
FAIL("NIDATA '%s' depth=%d count=%d", msg, depth, count);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
count++;
|
|
|
|
|
|
|
|
if (node->parent == NULL)
|
|
|
|
FAIL("NOPAR '%s' depth=%d count=%d", msg, depth, count);
|
|
|
|
|
|
|
|
if (node->left == NULL)
|
|
|
|
FAIL("NOLEFT '%s' depth=%d count=%d", msg, depth, count);
|
|
|
|
|
|
|
|
if (node->right == NULL)
|
|
|
|
FAIL("NORIGHT '%s' depth=%d count=%d", msg, depth, count);
|
|
|
|
|
|
|
|
if (node->data == NULL)
|
|
|
|
FAIL("NODATA '%s' depth=%d count=%d", msg, depth, count);
|
|
|
|
|
|
|
|
if (node->test != nilTestValue)
|
|
|
|
node->test = nilTestValue;
|
|
|
|
else
|
|
|
|
FAIL("NILTESTVALUE '%s' depth=%d count=%d", msg, depth, count);
|
|
|
|
|
|
|
|
count = nilTest(node->left, msg, depth+1, count, nil2, KTREE_FFL_PASS);
|
|
|
|
count = nilTest(node->right, msg, depth+1, count, nil2, KTREE_FFL_PASS);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(count);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bTest(K_NODE *cur, char *msg, int count, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
if (cur->red != RED_RED)
|
|
|
|
count++;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (cur->left->red == RED_RED)
|
|
|
|
FAIL("CURLR '%s' count=%d", msg, count);
|
|
|
|
|
|
|
|
if (cur->right->red == RED_RED)
|
|
|
|
FAIL("CURRR '%s' count=%d", msg, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cur->isNil == Yo)
|
|
|
|
{
|
|
|
|
if (bCount == 0)
|
|
|
|
bCount = count;
|
|
|
|
|
|
|
|
if (count != bCount)
|
|
|
|
FAIL("BCOUNT '%s' count=%d bCount=%d", msg, count, bCount);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (cur->test != bTestValue)
|
|
|
|
cur->test = bTestValue;
|
|
|
|
else
|
|
|
|
FAIL("BTESTVALUE '%s' count=%d", msg, count);
|
|
|
|
|
|
|
|
bTest(cur->left, msg, count, KTREE_FFL_PASS);
|
|
|
|
bTest(cur->right, msg, count, KTREE_FFL_PASS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bTestInit(K_TREE *tree, char *msg, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
bCount = 0;
|
|
|
|
bTestValue = getTestValue();
|
|
|
|
bTest(tree->root, msg, 0, KTREE_FFL_PASS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void lrpTest(K_NODE *node, char *msg, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
if (node->test != lrpTestValue)
|
|
|
|
node->test = lrpTestValue;
|
|
|
|
else
|
|
|
|
FAIL("LRPTESTVALUE '%s'", msg);
|
|
|
|
|
|
|
|
if (node->left->isNil == No)
|
|
|
|
{
|
|
|
|
if (node->left->parent != node)
|
|
|
|
FAIL("LRPTESTL '%s'", msg);
|
|
|
|
|
|
|
|
lrpTest(node->left, msg, KTREE_FFL_PASS);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node->right->isNil == No)
|
|
|
|
{
|
|
|
|
if (node->right->parent != node)
|
|
|
|
FAIL("LRPTESTR '%s'", msg);
|
|
|
|
|
|
|
|
lrpTest(node->right, msg, KTREE_FFL_PASS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static __maybe_unused void check_ktree(K_TREE *tree, char *msg, K_NODE *nil2, int debugNil, int debugLRP, int debugColor, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
if (tree->root->isNil == Yo)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (debugNil)
|
|
|
|
{
|
|
|
|
nilTestValue = getTestValue();
|
|
|
|
nilTest(tree->root, msg, 1, 0, nil2, KTREE_FFL_PASS);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (debugLRP && tree->root->isNil == No)
|
|
|
|
{
|
|
|
|
lrpTestValue = getTestValue();
|
|
|
|
lrpTest(tree->root, msg, KTREE_FFL_PASS);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (debugColor && tree->root->isNil == No)
|
|
|
|
bTestInit(tree, msg, KTREE_FFL_PASS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static K_ITEM *_first_in_knode(K_NODE *node, K_TREE_CTX *ctx, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
if (node->isNil == No)
|
|
|
|
{
|
|
|
|
while (node->left->isNil == No)
|
|
|
|
node = node->left;
|
|
|
|
|
|
|
|
*ctx = node;
|
|
|
|
return(node->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
*ctx = NULL;
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
K_ITEM *_first_in_ktree(K_TREE *tree, K_TREE_CTX *ctx, LOCK_MAYBE bool chklock, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
_TREE_READ(tree, chklock, file, func, line);
|
|
|
|
|
|
|
|
return _first_in_knode(tree->root, ctx, KTREE_FFL_PASS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static K_ITEM *_last_in_knode(K_NODE *node, K_TREE_CTX *ctx, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
if (node->isNil == No)
|
|
|
|
{
|
|
|
|
while (node->right->isNil == No)
|
|
|
|
node = node->right;
|
|
|
|
|
|
|
|
*ctx = node;
|
|
|
|
return(node->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
*ctx = NULL;
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
K_ITEM *_last_in_ktree(K_TREE *tree, K_TREE_CTX *ctx, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
_TREE_READ(tree, true, file, func, line);
|
|
|
|
|
|
|
|
return _last_in_knode(tree->root, ctx, KTREE_FFL_PASS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: change ctx to a structure of tree and node then can test _TREE_READ
|
|
|
|
* However, next/prev is never called before a first/last/find so it's less
|
|
|
|
* likely to see an error i.e. if code was missing a lock it would be seen
|
|
|
|
* by first/last/find - here would only see coding errors e.g. looping
|
|
|
|
* outside the lock */
|
|
|
|
K_ITEM *_next_in_ktree(K_TREE_CTX *ctx, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
K_NODE *parent;
|
|
|
|
K_NODE *knode = (K_NODE *)(*ctx);
|
|
|
|
|
|
|
|
if (knode->isNil == No)
|
|
|
|
{
|
|
|
|
if (knode->right->isNil == No)
|
|
|
|
return(_first_in_knode(knode->right, ctx, KTREE_FFL_PASS));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
parent = knode->parent;
|
|
|
|
while (parent->isNil == No && knode == parent->right)
|
|
|
|
{
|
|
|
|
knode = parent;
|
|
|
|
parent = parent->parent;
|
|
|
|
}
|
|
|
|
if (parent->isNil == No)
|
|
|
|
{
|
|
|
|
*ctx = parent;
|
|
|
|
return(parent->data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*ctx = NULL;
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
K_ITEM *_prev_in_ktree(K_TREE_CTX *ctx, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
K_NODE *parent;
|
|
|
|
K_NODE *knode = (K_NODE *)(*ctx);
|
|
|
|
|
|
|
|
if (knode->isNil == No)
|
|
|
|
{
|
|
|
|
if (knode->left->isNil == No)
|
|
|
|
return(_last_in_knode(knode->left, ctx, KTREE_FFL_PASS));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
parent = knode->parent;
|
|
|
|
while (parent->isNil == No && knode == parent->left)
|
|
|
|
{
|
|
|
|
knode = parent;
|
|
|
|
parent = parent->parent;
|
|
|
|
}
|
|
|
|
if (parent->isNil == No)
|
|
|
|
{
|
|
|
|
*ctx = parent;
|
|
|
|
return(parent->data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*ctx = NULL;
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static K_NODE *left_rotate(K_NODE *root, K_NODE *about)
|
|
|
|
{
|
|
|
|
K_NODE *rotate;
|
|
|
|
|
|
|
|
rotate = about->right;
|
|
|
|
about->right = rotate->left;
|
|
|
|
|
|
|
|
if (rotate->left->isNil == No)
|
|
|
|
rotate->left->parent = about;
|
|
|
|
|
|
|
|
rotate->parent = about->parent;
|
|
|
|
|
|
|
|
if (about->parent->isNil == Yo)
|
|
|
|
root = rotate;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (about == about->parent->left)
|
|
|
|
about->parent->left = rotate;
|
|
|
|
else
|
|
|
|
about->parent->right = rotate;
|
|
|
|
}
|
|
|
|
|
|
|
|
rotate->left = about;
|
|
|
|
about->parent = rotate;
|
|
|
|
|
|
|
|
return(root);
|
|
|
|
}
|
|
|
|
|
|
|
|
static K_NODE *right_rotate(K_NODE *root, K_NODE *about)
|
|
|
|
{
|
|
|
|
K_NODE *rotate;
|
|
|
|
|
|
|
|
rotate = about->left;
|
|
|
|
about->left = rotate->right;
|
|
|
|
|
|
|
|
if (rotate->right->isNil == No)
|
|
|
|
rotate->right->parent = about;
|
|
|
|
|
|
|
|
rotate->parent = about->parent;
|
|
|
|
|
|
|
|
if (about->parent->isNil == Yo)
|
|
|
|
root = rotate;
|
|
|
|
else
|
|
|
|
if (about == about->parent->right)
|
|
|
|
about->parent->right = rotate;
|
|
|
|
else
|
|
|
|
about->parent->left = rotate;
|
|
|
|
|
|
|
|
rotate->right = about;
|
|
|
|
about->parent = rotate;
|
|
|
|
|
|
|
|
return(root);
|
|
|
|
}
|
|
|
|
|
|
|
|
void _add_to_ktree(K_TREE *tree, K_ITEM *data, LOCK_MAYBE bool chklock, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
K_NODE *knode;
|
|
|
|
K_NODE *x, *y;
|
|
|
|
K_NODE *pp;
|
|
|
|
cmp_t cmp;
|
|
|
|
|
|
|
|
if (tree == NULL)
|
|
|
|
FAIL("%s", "ADDNULL add tree is NULL");
|
|
|
|
|
|
|
|
//check_ktree(tree, ">add", NULL, 1, 1, 1, KTREE_FFL_PASS);
|
|
|
|
|
|
|
|
if (tree->root->parent != nil && tree->root->parent != NULL)
|
|
|
|
FAIL("%s", "ADDROOT add tree->root isn't the root");
|
|
|
|
|
|
|
|
_TREE_WRITE(tree, chklock, file, func, line);
|
|
|
|
|
|
|
|
// chklock is false since we've already tested it
|
|
|
|
knode = new_data(tree, data, false, KTREE_FFL_PASS);
|
|
|
|
|
|
|
|
if (tree->root->isNil == Yo)
|
|
|
|
{
|
|
|
|
if (tree->root != nil)
|
|
|
|
{
|
|
|
|
// _nolock since we've already tested it if necessary
|
|
|
|
k_unlink_item_nolock(tree->node_store, tree->root->kitem);
|
|
|
|
k_add_head_nolock(tree->node_free, tree->root->kitem);
|
|
|
|
}
|
|
|
|
tree->root = knode;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
x = tree->root;
|
|
|
|
y = nil;
|
|
|
|
while (x->isNil == No)
|
|
|
|
{
|
|
|
|
y = x;
|
|
|
|
if ((cmp = tree->cmp_funct(knode->data, x->data)) < 0)
|
|
|
|
x = x->left;
|
|
|
|
else
|
|
|
|
x = x->right;
|
|
|
|
}
|
|
|
|
knode->parent = y;
|
|
|
|
if (cmp < 0)
|
|
|
|
y->left = knode;
|
|
|
|
else
|
|
|
|
y->right = knode;
|
|
|
|
|
|
|
|
x = knode;
|
|
|
|
while (x != tree->root && x->parent->red == RED_RED)
|
|
|
|
{
|
|
|
|
pp = x->parent->parent;
|
|
|
|
if (x->parent == pp->left)
|
|
|
|
{
|
|
|
|
y = pp->right;
|
|
|
|
if (y->red == RED_RED)
|
|
|
|
{
|
|
|
|
x->parent->red = RED_BLACK;
|
|
|
|
y->red = RED_BLACK;
|
|
|
|
pp->red = RED_RED;
|
|
|
|
x = pp;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (x == x->parent->right)
|
|
|
|
{
|
|
|
|
x = x->parent;
|
|
|
|
tree->root = left_rotate(tree->root, x);
|
|
|
|
pp = x->parent->parent;
|
|
|
|
}
|
|
|
|
x->parent->red = RED_BLACK;
|
|
|
|
pp->red = RED_RED;
|
|
|
|
tree->root = right_rotate(tree->root, pp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
y = pp->left;
|
|
|
|
if (y->red == RED_RED)
|
|
|
|
{
|
|
|
|
x->parent->red = RED_BLACK;
|
|
|
|
y->red = RED_BLACK;
|
|
|
|
pp->red = RED_RED;
|
|
|
|
x = pp;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (x == x->parent->left)
|
|
|
|
{
|
|
|
|
x = x->parent;
|
|
|
|
tree->root = right_rotate(tree->root, x);
|
|
|
|
pp = x->parent->parent;
|
|
|
|
}
|
|
|
|
x->parent->red = RED_BLACK;
|
|
|
|
pp->red = RED_RED;
|
|
|
|
tree->root = left_rotate(tree->root, pp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tree->root->red = RED_BLACK;
|
|
|
|
|
|
|
|
//check_ktree(tree, "<add", NULL, 1, 1, 1, KTREE_FFL_PASS);
|
|
|
|
}
|
|
|
|
|
|
|
|
K_ITEM *_find_in_ktree(K_TREE *tree, K_ITEM *data, K_TREE_CTX *ctx, bool chklock, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
K_NODE *knode;
|
|
|
|
cmp_t cmp = -1;
|
|
|
|
|
|
|
|
if (tree == NULL)
|
|
|
|
FAIL("%s", "FINDNULL find tree is NULL");
|
|
|
|
|
|
|
|
if (tree->root == NULL)
|
|
|
|
FAIL("%s", "FINDNULL find tree->root is NULL");
|
|
|
|
|
|
|
|
if (chklock)
|
|
|
|
{
|
|
|
|
_TREE_READ(tree, true, file, func, line);
|
|
|
|
}
|
|
|
|
|
|
|
|
knode = tree->root;
|
|
|
|
|
|
|
|
while (knode->isNil == No && cmp != 0)
|
|
|
|
{
|
|
|
|
if ((cmp = tree->cmp_funct(knode->data, data)))
|
|
|
|
{
|
|
|
|
if (cmp > 0)
|
|
|
|
knode = knode->left;
|
|
|
|
else
|
|
|
|
knode = knode->right;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (knode->isNil == No)
|
|
|
|
{
|
|
|
|
*ctx = knode;
|
|
|
|
return(knode->data);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*ctx = NULL;
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// First item after data
|
|
|
|
K_ITEM *_find_after_in_ktree(K_TREE *tree, K_ITEM *data, K_TREE_CTX *ctx, LOCK_MAYBE bool chklock, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
K_NODE *knode, *old = NULL;
|
|
|
|
cmp_t cmp = -1, oldcmp = -1;
|
|
|
|
|
|
|
|
if (tree == NULL)
|
|
|
|
FAIL("%s", "FINDNULL find_after tree is NULL");
|
|
|
|
|
|
|
|
if (tree->root == NULL)
|
|
|
|
FAIL("%s", "FINDNULL find_after tree->root is NULL");
|
|
|
|
|
|
|
|
_TREE_READ(tree, chklock, file, func, line);
|
|
|
|
|
|
|
|
knode = tree->root;
|
|
|
|
|
|
|
|
while (knode->isNil == No && cmp != 0)
|
|
|
|
{
|
|
|
|
if ((cmp = tree->cmp_funct(knode->data, data)))
|
|
|
|
{
|
|
|
|
old = knode;
|
|
|
|
oldcmp = cmp;
|
|
|
|
if (cmp > 0)
|
|
|
|
knode = knode->left;
|
|
|
|
else
|
|
|
|
knode = knode->right;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (knode->isNil == No)
|
|
|
|
{
|
|
|
|
*ctx = knode;
|
|
|
|
return next_in_ktree(ctx);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (old)
|
|
|
|
{
|
|
|
|
if (oldcmp > 0)
|
|
|
|
{
|
|
|
|
*ctx = old;
|
|
|
|
return(old->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
*ctx = old;
|
|
|
|
return next_in_ktree(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
*ctx = NULL;
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Last item before data
|
|
|
|
K_ITEM *_find_before_in_ktree(K_TREE *tree, K_ITEM *data, K_TREE_CTX *ctx, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
K_NODE *knode, *old = NULL;
|
|
|
|
cmp_t cmp = 1, oldcmp = 1;
|
|
|
|
|
|
|
|
if (tree == NULL)
|
|
|
|
FAIL("%s", "FINDNULL find_before tree is NULL");
|
|
|
|
|
|
|
|
if (tree->root == NULL)
|
|
|
|
FAIL("%s", "FINDNULL find_before tree->root is NULL");
|
|
|
|
|
|
|
|
_TREE_READ(tree, true, file, func, line);
|
|
|
|
|
|
|
|
knode = tree->root;
|
|
|
|
|
|
|
|
while (knode->isNil == No && cmp != 0)
|
|
|
|
{
|
|
|
|
if ((cmp = tree->cmp_funct(knode->data, data)))
|
|
|
|
{
|
|
|
|
old = knode;
|
|
|
|
oldcmp = cmp;
|
|
|
|
if (cmp > 0)
|
|
|
|
knode = knode->left;
|
|
|
|
else
|
|
|
|
knode = knode->right;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (knode->isNil == No)
|
|
|
|
{
|
|
|
|
*ctx = knode;
|
|
|
|
return prev_in_ktree(ctx);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (old)
|
|
|
|
{
|
|
|
|
if (oldcmp < 0)
|
|
|
|
{
|
|
|
|
*ctx = old;
|
|
|
|
return(old->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
*ctx = old;
|
|
|
|
return prev_in_ktree(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
*ctx = NULL;
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static K_NODE *removeFixup(K_NODE *root, K_NODE *fix)
|
|
|
|
{
|
|
|
|
K_NODE *w = NULL;
|
|
|
|
|
|
|
|
while (fix != root && fix->red != RED_RED)
|
|
|
|
{
|
|
|
|
if (fix == fix->parent->left)
|
|
|
|
{
|
|
|
|
w = fix->parent->right;
|
|
|
|
if (w->red == RED_RED)
|
|
|
|
{
|
|
|
|
w->red = RED_BLACK;
|
|
|
|
fix->parent->red = RED_RED;
|
|
|
|
root = left_rotate(root, fix->parent);
|
|
|
|
w = fix->parent->right;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (w->left->red != RED_RED && w->right->red != RED_RED)
|
|
|
|
{
|
|
|
|
w->red = RED_RED;
|
|
|
|
fix = fix->parent;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (w->right->red != RED_RED)
|
|
|
|
{
|
|
|
|
w->left->red = RED_BLACK;
|
|
|
|
w->red = RED_RED;
|
|
|
|
root = right_rotate(root, w);
|
|
|
|
w = fix->parent->right;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->red = fix->parent->red;
|
|
|
|
fix->parent->red = RED_BLACK;
|
|
|
|
w->right->red = RED_BLACK;
|
|
|
|
root = left_rotate(root, fix->parent);
|
|
|
|
fix = root;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
w = fix->parent->left;
|
|
|
|
if (w->red == RED_RED)
|
|
|
|
{
|
|
|
|
w->red = RED_BLACK;
|
|
|
|
fix->parent->red = RED_RED;
|
|
|
|
root = right_rotate(root, fix->parent);
|
|
|
|
w = fix->parent->left;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (w->right->red != RED_RED && w->left->red != RED_RED)
|
|
|
|
{
|
|
|
|
w->red = RED_RED;
|
|
|
|
fix = fix->parent;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (w->left->red != RED_RED)
|
|
|
|
{
|
|
|
|
w->right->red = RED_BLACK;
|
|
|
|
w->red = RED_RED;
|
|
|
|
root = left_rotate(root, w);
|
|
|
|
w = fix->parent->left;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->red = fix->parent->red;
|
|
|
|
fix->parent->red = RED_BLACK;
|
|
|
|
w->left->red = RED_BLACK;
|
|
|
|
root = right_rotate(root, fix->parent);
|
|
|
|
fix = root;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fix->red = RED_BLACK;
|
|
|
|
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Does this work OK when you remove the last element in the tree?
|
|
|
|
// It should return the root as 'nil'
|
|
|
|
void _remove_from_ktree(K_TREE *tree, K_ITEM *data, K_TREE_CTX *ctx, LOCK_MAYBE bool chklock, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
K_TREE_CTX tmpctx[1];
|
|
|
|
K_NODE *found;
|
|
|
|
K_ITEM *fdata;
|
|
|
|
K_NODE *x, *y, *nil2;
|
|
|
|
// cmp_t cmp;
|
|
|
|
int yred;
|
|
|
|
|
|
|
|
//check_ktree(tree, ">remove", NULL, 1, 1, 1, KTREE_FFL_PASS);
|
|
|
|
|
|
|
|
if (tree == NULL)
|
|
|
|
FAIL("%s", "REMNULL remove tree is NULL");
|
|
|
|
|
|
|
|
if (tree->root == NULL)
|
|
|
|
FAIL("%s", "REMNULL remove tree->root is NULL");
|
|
|
|
|
|
|
|
_TREE_WRITE(tree, chklock, file, func, line);
|
|
|
|
|
|
|
|
if (tree->root->isNil == Yo)
|
|
|
|
{
|
|
|
|
*ctx = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tree->root->parent->isNil == No)
|
|
|
|
FAIL("%s", "REMROOT remove tree->root isn't the root");
|
|
|
|
|
|
|
|
fdata = find_in_ktree(tree, data, ctx);
|
|
|
|
|
|
|
|
if (fdata == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (tree->cmp_funct(fdata, data) != 0)
|
|
|
|
FAIL("%s", "BADFIND cmp(found, remove) != 0");
|
|
|
|
|
|
|
|
found = *ctx;
|
|
|
|
|
|
|
|
x = nil;
|
|
|
|
y = nil;
|
|
|
|
|
|
|
|
if (found->left->isNil == Yo || found->right->isNil == Yo)
|
|
|
|
y = found;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*tmpctx = found;
|
|
|
|
next_in_ktree(tmpctx);
|
|
|
|
y = *tmpctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
yred = y->red;
|
|
|
|
|
|
|
|
if (y->left->isNil == No && y->right->isNil == No)
|
|
|
|
FAIL("%s", "REMBADY remove error");
|
|
|
|
|
|
|
|
if (y->left->isNil == Yo)
|
|
|
|
x = y->right;
|
|
|
|
else
|
|
|
|
x = y->left;
|
|
|
|
|
|
|
|
if (x != nil)
|
|
|
|
nil2 = NULL;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// chklock is false since we've already tested it
|
|
|
|
nil2 = _new_knode(tree, false, KTREE_FFL_PASS);
|
|
|
|
x = nil2;
|
|
|
|
}
|
|
|
|
|
|
|
|
x->parent = y->parent;
|
|
|
|
|
|
|
|
if (x->parent->isNil == Yo)
|
|
|
|
tree->root = x;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (x->parent->left == y)
|
|
|
|
x->parent->left = x;
|
|
|
|
else
|
|
|
|
x->parent->right = x;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (y != found)
|
|
|
|
{
|
|
|
|
if (tree->root == found)
|
|
|
|
tree->root = y;
|
|
|
|
|
|
|
|
if (x == found)
|
|
|
|
x = y;
|
|
|
|
|
|
|
|
y->red = found->red;
|
|
|
|
y->parent = found->parent;
|
|
|
|
|
|
|
|
if (y->parent->isNil == No)
|
|
|
|
{
|
|
|
|
if (y->parent->left == found)
|
|
|
|
y->parent->left = y;
|
|
|
|
else
|
|
|
|
y->parent->right = y;
|
|
|
|
}
|
|
|
|
|
|
|
|
y->left = found->left;
|
|
|
|
if (y->left->isNil == No || y->left == nil2)
|
|
|
|
y->left->parent = y;
|
|
|
|
|
|
|
|
y->right = found->right;
|
|
|
|
if (y->right->isNil == No || y->right == nil2)
|
|
|
|
y->right->parent = y;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (yred != RED_RED)
|
|
|
|
tree->root = removeFixup(tree->root, x);
|
|
|
|
|
|
|
|
if (nil2 != NULL)
|
|
|
|
{
|
|
|
|
if (nil2->parent->isNil == No && nil2->parent->left == nil2)
|
|
|
|
nil2->parent->left = nil;
|
|
|
|
|
|
|
|
if (nil2->parent->isNil == No && nil2->parent->right == nil2)
|
|
|
|
nil2->parent->right = nil;
|
|
|
|
|
|
|
|
if (tree->root == nil2)
|
|
|
|
tree->root = nil;
|
|
|
|
|
|
|
|
/*
|
|
|
|
if (dbg != 0)
|
|
|
|
{
|
|
|
|
if (nil2->left != nil)
|
|
|
|
{
|
|
|
|
DBG("@remove nil2->left wasn't nil!!!\n");
|
|
|
|
}
|
|
|
|
if (nil2->right != nil)
|
|
|
|
{
|
|
|
|
DBG("@remove nil2->right wasn't nil!!!\n");
|
|
|
|
}
|
|
|
|
cmp = 0;
|
|
|
|
fdata = first_in_ktree(tree, tmpctx);;
|
|
|
|
while (fdata != NULL)
|
|
|
|
{
|
|
|
|
cmp++;
|
|
|
|
x = *tmpctx;
|
|
|
|
if (x == nil2)
|
|
|
|
{
|
|
|
|
DBG("@remove found nil2 in ktree %d!!!\n", (int)cmp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if (x->left == nil2)
|
|
|
|
{
|
|
|
|
DBG("@remove found nil2 in ktree(left) %d!!!\n", (int)cmp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if (x->right == nil2)
|
|
|
|
{
|
|
|
|
DBG("@remove found nil2 in ktree(right) %d!!!\n", (int)cmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
fdata = next_in_ktree(tmpctx);;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
// _nolock since we've already tested it if necessary
|
|
|
|
k_unlink_item_nolock(tree->node_store, nil2->kitem);
|
|
|
|
k_add_head_nolock(tree->node_free, nil2->kitem);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
if (dbg != 0)
|
|
|
|
{
|
|
|
|
cmp = 0;
|
|
|
|
fdata = first_in_ktree(tree, tmpctx);;
|
|
|
|
while (fdata != NULL)
|
|
|
|
{
|
|
|
|
if (tree->cmp_funct(fdata, tree->root->data) < 0)
|
|
|
|
cmp--;
|
|
|
|
else
|
|
|
|
cmp++;
|
|
|
|
|
|
|
|
fdata = next_in_ktree(tmpctx);;
|
|
|
|
}
|
|
|
|
if (cmp < -10 || cmp > 10)
|
|
|
|
{
|
|
|
|
DBG("@remove after balance=%d :(\n", (int)cmp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
//check_ktree(tree, "<remove", NULL, 1, 1, 1, KTREE_FFL_PASS);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void _remove_from_ktree_free(K_TREE *root, K_ITEM *data, bool chklock, KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
K_TREE_CTX ctx[1];
|
|
|
|
|
|
|
|
_remove_from_ktree(root, data, ctx, chklock, KTREE_FFL_PASS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_ktree_sub(K_NODE *knode, void (*free_funct)(void *))
|
|
|
|
{
|
|
|
|
if (knode != NULL && knode != nil)
|
|
|
|
{
|
|
|
|
if (knode->data != NULL && free_funct)
|
|
|
|
free_funct(knode->data);
|
|
|
|
|
|
|
|
free_ktree_sub(knode->left, free_funct);
|
|
|
|
free_ktree_sub(knode->right, free_funct);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: remove free_funct, it's not the tree's job to free the item data
|
|
|
|
* that should be done when freeing the data list itself */
|
|
|
|
void _free_ktree(K_TREE *tree, void (*free_funct)(void *), KTREE_FFL_ARGS)
|
|
|
|
{
|
|
|
|
if (tree == NULL)
|
|
|
|
FAIL("%s", "FREENULL free NULL tree");
|
|
|
|
|
|
|
|
if (tree->root->parent != NULL && tree->root->parent != nil)
|
|
|
|
FAIL("%s", "FREENOTROOT free tree->root not root");
|
|
|
|
|
|
|
|
if (free_funct)
|
|
|
|
free_ktree_sub(tree->root, free_funct);
|
|
|
|
|
|
|
|
tree->node_store = k_free_store(tree->node_store);
|
|
|
|
tree->node_free = k_free_list(tree->node_free);
|
|
|
|
}
|