You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1100 lines
22 KiB

/*
* 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 *tree, K_ITEM *data, bool chklock, KTREE_FFL_ARGS)
{
K_TREE_CTX ctx[1];
K_NODE *knode;
K_ITEM *kitem;
_remove_from_ktree(tree, data, ctx, chklock, KTREE_FFL_PASS);
if (ctx[0]) {
knode = (K_NODE *)(ctx[0]);
kitem = knode->kitem;
// _nolock since _remove_from_ktree() already tested it
k_unlink_item_nolock(tree->node_store, kitem);
k_add_head_nolock(tree->node_free, kitem);
}
}
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);
}