diff --git a/libctru/include/3ds/util/rbtree.h b/libctru/include/3ds/util/rbtree.h index 08778b9..5b27892 100644 --- a/libctru/include/3ds/util/rbtree.h +++ b/libctru/include/3ds/util/rbtree.h @@ -4,6 +4,7 @@ */ #pragma once +#include <3ds/synchronization.h> #include #include @@ -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 diff --git a/libctru/source/util/rbtree/rbtree_clear.c b/libctru/source/util/rbtree/rbtree_clear.c index fe5c803..084e947 100644 --- a/libctru/source/util/rbtree/rbtree_clear.c +++ b/libctru/source/util/rbtree/rbtree_clear.c @@ -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); } diff --git a/libctru/source/util/rbtree/rbtree_empty.c b/libctru/source/util/rbtree/rbtree_empty.c index c47170d..4f592ba 100644 --- a/libctru/source/util/rbtree/rbtree_empty.c +++ b/libctru/source/util/rbtree/rbtree_empty.c @@ -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; } diff --git a/libctru/source/util/rbtree/rbtree_find.c b/libctru/source/util/rbtree/rbtree_find.c index 19c9f27..d6d3c8b 100644 --- a/libctru/source/util/rbtree/rbtree_find.c +++ b/libctru/source/util/rbtree/rbtree_find.c @@ -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; } diff --git a/libctru/source/util/rbtree/rbtree_init.c b/libctru/source/util/rbtree/rbtree_init.c index b1a826c..f66f0ea 100644 --- a/libctru/source/util/rbtree/rbtree_init.c +++ b/libctru/source/util/rbtree/rbtree_init.c @@ -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); } diff --git a/libctru/source/util/rbtree/rbtree_insert.c b/libctru/source/util/rbtree/rbtree_insert.c index 536b9d2..56943fb 100644 --- a/libctru/source/util/rbtree/rbtree_insert.c +++ b/libctru/source/util/rbtree/rbtree_insert.c @@ -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; } diff --git a/libctru/source/util/rbtree/rbtree_internal.h b/libctru/source/util/rbtree/rbtree_internal.h index 8719afe..b0376f4 100644 --- a/libctru/source/util/rbtree/rbtree_internal.h +++ b/libctru/source/util/rbtree/rbtree_internal.h @@ -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, diff --git a/libctru/source/util/rbtree/rbtree_iterator.c b/libctru/source/util/rbtree/rbtree_iterator.c index 424658d..a577d93 100644 --- a/libctru/source/util/rbtree/rbtree_iterator.c +++ b/libctru/source/util/rbtree/rbtree_iterator.c @@ -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); diff --git a/libctru/source/util/rbtree/rbtree_minmax.c b/libctru/source/util/rbtree/rbtree_minmax.c index 54c9cf4..5455cca 100644 --- a/libctru/source/util/rbtree/rbtree_minmax.c +++ b/libctru/source/util/rbtree/rbtree_minmax.c @@ -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); } diff --git a/libctru/source/util/rbtree/rbtree_remove.c b/libctru/source/util/rbtree/rbtree_remove.c index 412474c..2de1cbd 100644 --- a/libctru/source/util/rbtree/rbtree_remove.c +++ b/libctru/source/util/rbtree/rbtree_remove.c @@ -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; } diff --git a/libctru/source/util/rbtree/rbtree_rotate.c b/libctru/source/util/rbtree/rbtree_rotate.c index c47d290..4897a04 100644 --- a/libctru/source/util/rbtree/rbtree_rotate.c +++ b/libctru/source/util/rbtree/rbtree_rotate.c @@ -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; diff --git a/libctru/source/util/rbtree/rbtree_size.c b/libctru/source/util/rbtree/rbtree_size.c index e596366..5bc35ad 100644 --- a/libctru/source/util/rbtree/rbtree_size.c +++ b/libctru/source/util/rbtree/rbtree_size.c @@ -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; } diff --git a/libctru/source/util/rbtree/rbtree_validate.c b/libctru/source/util/rbtree/rbtree_validate.c index 431635e..281b06c 100644 --- a/libctru/source/util/rbtree/rbtree_validate.c +++ b/libctru/source/util/rbtree/rbtree_validate.c @@ -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); +} diff --git a/libctru/source/util/rbtree/test/main.cpp b/libctru/source/util/rbtree/test/main.cpp index 3799e1b..ed45f06 100644 --- a/libctru/source/util/rbtree/test/main.cpp +++ b/libctru/source/util/rbtree/test/main.cpp @@ -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); }