Additional debugging
This commit is contained in:
parent
431fd9aa9c
commit
888569bba1
@ -4,6 +4,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <3ds/synchronization.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
@ -21,16 +22,21 @@ typedef int (*rbtree_node_comparator_t)(const rbtree_node_t *lhs,
|
||||
/// An rbtree node.
|
||||
struct rbtree_node
|
||||
{
|
||||
uintptr_t parent_color; ///< Parent color.
|
||||
rbtree_node_t *child[2]; ///< Node children.
|
||||
char nodeTag[4]; ///< Node tag.
|
||||
uintptr_t parent_color; ///< Parent color.
|
||||
rbtree_node_t *child[2]; ///< Node children.
|
||||
rbtree_t *tree; ///< Owning tree.
|
||||
};
|
||||
|
||||
/// An rbtree.
|
||||
struct rbtree
|
||||
{
|
||||
char treeTag[4]; ///< Tree tag.
|
||||
rbtree_node_t *root; ///< Root node.
|
||||
rbtree_node_comparator_t comparator; ///< Node comparator.
|
||||
size_t size; ///< Size.
|
||||
LightLock lock; ///< Tree mutex.
|
||||
bool busy; ///< Busy flag.
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -5,27 +5,28 @@ void
|
||||
rbtree_clear(rbtree_t *tree,
|
||||
rbtree_node_destructor_t destructor)
|
||||
{
|
||||
rbtree_set_busy(tree);
|
||||
rbtree_validate(tree);
|
||||
|
||||
rbtree_node_t *node = tree->root;
|
||||
|
||||
while(tree->root != NULL)
|
||||
while(tree->root)
|
||||
{
|
||||
while(node->child[LEFT] != NULL)
|
||||
while(node->child[LEFT])
|
||||
node = node->child[LEFT];
|
||||
|
||||
if(node->child[RIGHT] != NULL)
|
||||
if(node->child[RIGHT])
|
||||
node = node->child[RIGHT];
|
||||
else
|
||||
{
|
||||
rbtree_node_t *parent = get_parent(node);
|
||||
|
||||
if(parent == NULL)
|
||||
if(!parent)
|
||||
tree->root = NULL;
|
||||
else
|
||||
parent->child[node != parent->child[LEFT]] = NULL;
|
||||
|
||||
if(destructor != NULL)
|
||||
if(destructor)
|
||||
(*destructor)(node);
|
||||
|
||||
node = parent;
|
||||
@ -35,4 +36,5 @@ rbtree_clear(rbtree_t *tree,
|
||||
tree->size = 0;
|
||||
|
||||
rbtree_validate(tree);
|
||||
rbtree_clear_busy(tree);
|
||||
}
|
||||
|
@ -4,7 +4,11 @@
|
||||
int
|
||||
rbtree_empty(const rbtree_t *tree)
|
||||
{
|
||||
rbtree_set_busy((rbtree_t*)tree);
|
||||
rbtree_validate(tree);
|
||||
|
||||
return tree->root == NULL;
|
||||
bool empty = !tree->root;
|
||||
|
||||
rbtree_clear_busy((rbtree_t*)tree);
|
||||
return empty;
|
||||
}
|
||||
|
@ -5,12 +5,13 @@ rbtree_node_t*
|
||||
rbtree_find(const rbtree_t *tree,
|
||||
const rbtree_node_t *node)
|
||||
{
|
||||
rbtree_set_busy((rbtree_t*)tree);
|
||||
rbtree_validate(tree);
|
||||
|
||||
rbtree_node_t *tmp = tree->root;
|
||||
rbtree_node_t *save = NULL;
|
||||
|
||||
while(tmp != NULL)
|
||||
while(tmp)
|
||||
{
|
||||
int rc = (*tree->comparator)(node, tmp);
|
||||
if(rc < 0)
|
||||
@ -29,6 +30,7 @@ rbtree_find(const rbtree_t *tree,
|
||||
}
|
||||
|
||||
rbtree_validate(tree);
|
||||
rbtree_clear_busy((rbtree_t*)tree);
|
||||
|
||||
return save;
|
||||
}
|
||||
|
@ -5,9 +5,15 @@ void
|
||||
rbtree_init(rbtree_t *tree,
|
||||
rbtree_node_comparator_t comparator)
|
||||
{
|
||||
tree->treeTag[3] = 'T';
|
||||
tree->treeTag[2] = 'R';
|
||||
tree->treeTag[1] = 'E';
|
||||
tree->treeTag[0] = 'E';
|
||||
tree->root = NULL;
|
||||
tree->comparator = comparator;
|
||||
tree->size = 0;
|
||||
tree->busy = false;
|
||||
LightLock_Init(&tree->lock);
|
||||
|
||||
rbtree_validate(tree);
|
||||
}
|
||||
|
@ -6,14 +6,24 @@ do_insert(rbtree_t *tree,
|
||||
rbtree_node_t *node,
|
||||
int multi)
|
||||
{
|
||||
rbtree_set_busy(tree);
|
||||
rbtree_validate(tree);
|
||||
|
||||
node->nodeTag[3] = 'N';
|
||||
node->nodeTag[2] = 'O';
|
||||
node->nodeTag[1] = 'D';
|
||||
node->nodeTag[0] = 'E';
|
||||
node->parent_color = 0;
|
||||
node->child[LEFT] = NULL;
|
||||
node->child[RIGHT] = NULL;
|
||||
node->tree = tree;
|
||||
|
||||
rbtree_node_t *original = node;
|
||||
rbtree_node_t **tmp = &tree->root;
|
||||
rbtree_node_t *parent = NULL;
|
||||
rbtree_node_t *save = NULL;
|
||||
|
||||
while(*tmp != NULL)
|
||||
while(*tmp)
|
||||
{
|
||||
int cmp = (*(tree->comparator))(node, *tmp);
|
||||
parent = *tmp;
|
||||
@ -31,9 +41,19 @@ do_insert(rbtree_t *tree,
|
||||
}
|
||||
}
|
||||
|
||||
if(save != NULL)
|
||||
if(save)
|
||||
{
|
||||
node->nodeTag[3] = 'D';
|
||||
node->nodeTag[2] = 'O';
|
||||
node->nodeTag[1] = 'N';
|
||||
node->nodeTag[0] = 'E';
|
||||
node->parent_color = 0;
|
||||
node->child[LEFT] = NULL;
|
||||
node->child[RIGHT] = NULL;
|
||||
node->tree = NULL;
|
||||
|
||||
rbtree_validate(tree);
|
||||
rbtree_clear_busy(tree);
|
||||
|
||||
return save;
|
||||
}
|
||||
@ -83,6 +103,7 @@ do_insert(rbtree_t *tree,
|
||||
tree->size += 1;
|
||||
|
||||
rbtree_validate(tree);
|
||||
rbtree_clear_busy(tree);
|
||||
|
||||
return original;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ set_red(rbtree_node_t *node)
|
||||
static inline rbtree_color_t
|
||||
get_color(const rbtree_node_t *node)
|
||||
{
|
||||
if(node == NULL)
|
||||
if(!node)
|
||||
return BLACK;
|
||||
return (rbtree_color_t)(node->parent_color & COLOR_MASK);
|
||||
}
|
||||
@ -59,6 +59,12 @@ set_parent(rbtree_node_t *node,
|
||||
node->parent_color = (get_color(node)) | ((uintptr_t)parent);
|
||||
}
|
||||
|
||||
void
|
||||
rbtree_set_busy(rbtree_t *tree);
|
||||
|
||||
void
|
||||
rbtree_clear_busy(rbtree_t *tree);
|
||||
|
||||
void
|
||||
rbtree_rotate(rbtree_t *tree,
|
||||
rbtree_node_t *node,
|
||||
|
@ -7,16 +7,16 @@ do_iterate(const rbtree_node_t *node,
|
||||
{
|
||||
rbtree_node_t *it = (rbtree_node_t*)node;
|
||||
|
||||
if(it->child[next] != NULL)
|
||||
if(it->child[next])
|
||||
{
|
||||
it = it->child[next];
|
||||
while(it->child[!next] != NULL)
|
||||
while(it->child[!next])
|
||||
it = it->child[!next];
|
||||
}
|
||||
else
|
||||
{
|
||||
rbtree_node_t *parent = get_parent(node);
|
||||
while(parent != NULL && it == parent->child[next])
|
||||
while(parent && it == parent->child[next])
|
||||
{
|
||||
it = parent;
|
||||
parent = get_parent(it);
|
||||
|
@ -5,35 +5,32 @@ static inline rbtree_node_t*
|
||||
do_minmax(const rbtree_t *tree,
|
||||
int max)
|
||||
{
|
||||
rbtree_set_busy((rbtree_t*)tree);
|
||||
rbtree_validate(tree);
|
||||
|
||||
rbtree_node_t *node = tree->root;
|
||||
|
||||
if(node == NULL)
|
||||
if(!node)
|
||||
{
|
||||
rbtree_clear_busy((rbtree_t*)tree);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while(node->child[max] != NULL)
|
||||
while(node->child[max])
|
||||
node = node->child[max];
|
||||
|
||||
rbtree_clear_busy((rbtree_t*)tree);
|
||||
return node;
|
||||
}
|
||||
|
||||
rbtree_node_t*
|
||||
rbtree_min(const rbtree_t *tree)
|
||||
{
|
||||
rbtree_node_t *node;
|
||||
|
||||
node = do_minmax(tree, LEFT);
|
||||
|
||||
return node;
|
||||
return do_minmax(tree, LEFT);
|
||||
}
|
||||
|
||||
rbtree_node_t*
|
||||
rbtree_max(const rbtree_t *tree)
|
||||
{
|
||||
rbtree_node_t *node;
|
||||
|
||||
node = do_minmax(tree, RIGHT);
|
||||
|
||||
return node;
|
||||
return do_minmax(tree, RIGHT);
|
||||
}
|
||||
|
@ -51,15 +51,37 @@ recolor(rbtree_t *tree,
|
||||
}
|
||||
}
|
||||
|
||||
if(node != NULL)
|
||||
if(node)
|
||||
set_black(node);
|
||||
}
|
||||
|
||||
static bool
|
||||
in_tree(const rbtree_t *tree,
|
||||
const rbtree_node_t *node)
|
||||
{
|
||||
if(tree->root == node)
|
||||
return true;
|
||||
|
||||
const rbtree_node_t *parent = get_parent(node);
|
||||
while(parent)
|
||||
{
|
||||
if(tree->root == parent)
|
||||
return true;
|
||||
|
||||
parent = get_parent(parent);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
rbtree_node_t*
|
||||
rbtree_remove(rbtree_t *tree,
|
||||
rbtree_node_t *node,
|
||||
rbtree_node_destructor_t destructor)
|
||||
{
|
||||
rbtree_set_busy(tree);
|
||||
if(!in_tree(tree, node))
|
||||
svcBreak(USERBREAK_PANIC);
|
||||
rbtree_validate(tree);
|
||||
|
||||
rbtree_color_t color;
|
||||
@ -68,16 +90,16 @@ rbtree_remove(rbtree_t *tree,
|
||||
|
||||
next = rbtree_node_next(node);
|
||||
|
||||
if(node->child[LEFT] != NULL && node->child[RIGHT] != NULL)
|
||||
if(node->child[LEFT] && node->child[RIGHT])
|
||||
{
|
||||
rbtree_node_t *old = node;
|
||||
|
||||
node = node->child[RIGHT];
|
||||
while(node->child[LEFT] != NULL)
|
||||
while(node->child[LEFT])
|
||||
node = node->child[LEFT];
|
||||
|
||||
parent = get_parent(old);
|
||||
if(parent != NULL)
|
||||
if(parent)
|
||||
{
|
||||
if(parent->child[LEFT] == old)
|
||||
parent->child[LEFT] = node;
|
||||
@ -95,7 +117,7 @@ rbtree_remove(rbtree_t *tree,
|
||||
parent = node;
|
||||
else
|
||||
{
|
||||
if(child != NULL)
|
||||
if(child)
|
||||
set_parent(child, parent);
|
||||
parent->child[LEFT] = child;
|
||||
|
||||
@ -109,7 +131,7 @@ rbtree_remove(rbtree_t *tree,
|
||||
}
|
||||
else
|
||||
{
|
||||
if(node->child[LEFT] == NULL)
|
||||
if(!node->child[LEFT])
|
||||
child = node->child[RIGHT];
|
||||
else
|
||||
child = node->child[LEFT];
|
||||
@ -117,9 +139,9 @@ rbtree_remove(rbtree_t *tree,
|
||||
parent = get_parent(node);
|
||||
color = get_color(node);
|
||||
|
||||
if(child != NULL)
|
||||
if(child)
|
||||
set_parent(child, parent);
|
||||
if(parent != NULL)
|
||||
if(parent)
|
||||
{
|
||||
if(parent->child[LEFT] == node)
|
||||
parent->child[LEFT] = child;
|
||||
@ -133,12 +155,22 @@ rbtree_remove(rbtree_t *tree,
|
||||
if(color == BLACK)
|
||||
recolor(tree, parent, child);
|
||||
|
||||
if(destructor != NULL)
|
||||
original->nodeTag[3] = 'D';
|
||||
original->nodeTag[2] = 'O';
|
||||
original->nodeTag[1] = 'N';
|
||||
original->nodeTag[0] = 'E';
|
||||
original->parent_color = 0;
|
||||
original->child[LEFT] = NULL;
|
||||
original->child[RIGHT] = NULL;
|
||||
original->tree = NULL;
|
||||
|
||||
if(destructor)
|
||||
(*destructor)(original);
|
||||
|
||||
tree->size -= 1;
|
||||
|
||||
rbtree_validate(tree);
|
||||
rbtree_clear_busy(tree);
|
||||
|
||||
return next;
|
||||
}
|
||||
|
@ -10,12 +10,12 @@ rbtree_rotate(rbtree_t *tree,
|
||||
rbtree_node_t *parent = get_parent(node);
|
||||
|
||||
node->child[left] = tmp->child[!left];
|
||||
if(tmp->child[!left] != NULL)
|
||||
if(tmp->child[!left])
|
||||
set_parent(tmp->child[!left], node);
|
||||
|
||||
tmp->child[!left] = node;
|
||||
set_parent(tmp, parent);
|
||||
if(parent != NULL)
|
||||
if(parent)
|
||||
{
|
||||
if(node == parent->child[!left])
|
||||
parent->child[!left] = tmp;
|
||||
|
@ -4,7 +4,11 @@
|
||||
size_t
|
||||
rbtree_size(const rbtree_t *tree)
|
||||
{
|
||||
rbtree_set_busy((rbtree_t*)tree);
|
||||
rbtree_validate(tree);
|
||||
|
||||
return tree->size;
|
||||
size_t size = tree->size;
|
||||
|
||||
rbtree_clear_busy((rbtree_t*)tree);
|
||||
return size;
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
abort(); \
|
||||
} while(0)
|
||||
|
||||
|
||||
void
|
||||
rbtree_validate(const rbtree_t *tree)
|
||||
{
|
||||
@ -23,7 +22,7 @@ rbtree_validate(const rbtree_t *tree)
|
||||
// root node's parent must be null
|
||||
if(tree->root)
|
||||
{
|
||||
if(get_parent(tree->root) != NULL)
|
||||
if(get_parent(tree->root))
|
||||
panic();
|
||||
}
|
||||
|
||||
@ -42,7 +41,7 @@ rbtree_node_validate(const rbtree_t *tree, const rbtree_node_t *node, size_t *si
|
||||
if(!node) // implies is_black
|
||||
{
|
||||
if(black)
|
||||
*black += 1;
|
||||
*black = 1;
|
||||
|
||||
if(depth)
|
||||
*depth = 1;
|
||||
@ -108,14 +107,14 @@ rbtree_node_validate(const rbtree_t *tree, const rbtree_node_t *node, size_t *si
|
||||
|
||||
// size is left+right subtrees plus self
|
||||
if(size)
|
||||
*size += left_size + right_size + 1;
|
||||
*size = left_size + right_size + 1;
|
||||
|
||||
// all possible paths to leaf nodes must have the same number of black nodes along the path
|
||||
if(left_black != right_black)
|
||||
panic();
|
||||
|
||||
if(black)
|
||||
*black += left_black + (is_black(node) ? 1 : 0);
|
||||
*black = left_black + (is_black(node) ? 1 : 0);
|
||||
|
||||
// depth of one subtree must not exceed 2x depth of the other subtree
|
||||
if(left_depth < right_depth)
|
||||
@ -134,3 +133,25 @@ rbtree_node_validate(const rbtree_t *tree, const rbtree_node_t *node, size_t *si
|
||||
*depth = right_depth + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rbtree_set_busy(rbtree_t *tree)
|
||||
{
|
||||
LightLock_Lock(&tree->lock);
|
||||
if(tree->busy)
|
||||
panic();
|
||||
|
||||
tree->busy = true;
|
||||
LightLock_Unlock(&tree->lock);
|
||||
}
|
||||
|
||||
void
|
||||
rbtree_clear_busy(rbtree_t *tree)
|
||||
{
|
||||
LightLock_Lock(&tree->lock);
|
||||
if(!tree->busy)
|
||||
panic();
|
||||
|
||||
tree->busy = false;
|
||||
LightLock_Unlock(&tree->lock);
|
||||
}
|
||||
|
@ -111,7 +111,8 @@ int main(int argc, char *argv[])
|
||||
else
|
||||
nodes.emplace_back(node);
|
||||
|
||||
if(remove() < chance)
|
||||
// remove random node
|
||||
if(remove() <= chance)
|
||||
{
|
||||
auto it = std::begin(nodes) + (eng() % nodes.size());
|
||||
rbtree_remove(&tree, &(*it)->node, IntNodeDestructor);
|
||||
@ -122,5 +123,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
std::printf("Ended with %zu nodes in tree\n", rbtree_size(&tree));
|
||||
|
||||
rbtree_clear(&tree, IntNodeDestructor);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user