From: mtheall Date: Thu, 20 Nov 2014 21:28:13 +0000 (-0600) Subject: Add rbtree X-Git-Tag: v0.2.0~51 X-Git-Url: https://chaos.moe/g/?a=commitdiff_plain;h=d2fca0f721012455e4d74d61f652d8396791c45f;p=corbenik%2Fctrulib.git Add rbtree --- diff --git a/libctru/Makefile b/libctru/Makefile index d3ae023..114d61b 100644 --- a/libctru/Makefile +++ b/libctru/Makefile @@ -17,7 +17,11 @@ include $(DEVKITARM)/base_rules #--------------------------------------------------------------------------------- TARGET := ctru BUILD := build -SOURCES := source source/services source/gpu source/allocator +SOURCES := source \ + source/allocator \ + source/gpu \ + source/services \ + source/util DATA := data INCLUDES := include diff --git a/libctru/include/3ds/util/rbtree.h b/libctru/include/3ds/util/rbtree.h new file mode 100644 index 0000000..73110f0 --- /dev/null +++ b/libctru/include/3ds/util/rbtree.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include + +#define rbtree_item(ptr, type, member) \ + ((type*)(((char*)ptr) - offsetof(type, member))) + +typedef struct rbtree rbtree_t; +typedef struct rbtree_node rbtree_node_t; + +typedef void (*rbtree_node_destructor_t)(rbtree_node_t *Node); +typedef int (*rbtree_node_comparator_t)(const rbtree_node_t *lhs, + const rbtree_node_t *rhs); +struct rbtree_node +{ + uintptr_t parent_color; + rbtree_node_t *child[2]; +}; + +struct rbtree +{ + rbtree_node_t *root; + rbtree_node_comparator_t comparator; + size_t size; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +void +rbtree_init(rbtree_t *tree, + rbtree_node_comparator_t comparator); + +int +rbtree_empty(const rbtree_t *tree); + +size_t +rbtree_size(const rbtree_t *tree); + +__attribute__((warn_unused_result)) +rbtree_node_t* +rbtree_insert(rbtree_t *tree, + rbtree_node_t *node); + +void +rbtree_insert_multi(rbtree_t *tree, + rbtree_node_t *node); + +rbtree_node_t* +rbtree_find(const rbtree_t *tree, + const rbtree_node_t *node); + +rbtree_node_t* +rbtree_min(const rbtree_t *tree); + +rbtree_node_t* +rbtree_max(const rbtree_t *tree); + +rbtree_node_t* +rbtree_node_next(const rbtree_node_t *node); + +rbtree_node_t* +rbtree_node_prev(const rbtree_node_t *node); + +rbtree_node_t* +rbtree_remove(rbtree_t *tree, + rbtree_node_t *node, + rbtree_node_destructor_t destructor); + +void +rbtree_clear(rbtree_t *tree, + rbtree_node_destructor_t destructor); + +#ifdef __cplusplus +} +#endif diff --git a/libctru/source/util/rbtree_clear.c b/libctru/source/util/rbtree_clear.c new file mode 100644 index 0000000..fd945f1 --- /dev/null +++ b/libctru/source/util/rbtree_clear.c @@ -0,0 +1,34 @@ +#include <3ds/util/rbtree.h> +#include "rbtree_internal.h" + +void +rbtree_clear(rbtree_t *tree, + rbtree_node_destructor_t destructor) +{ + rbtree_node_t *node = tree->root; + + while(tree->root != NULL) + { + while(node->child[LEFT] != NULL) + node = node->child[LEFT]; + + if(node->child[RIGHT] != NULL) + node = node->child[RIGHT]; + else + { + rbtree_node_t *parent = get_parent(node); + + if(parent == NULL) + tree->root = NULL; + else + parent->child[node != parent->child[LEFT]] = NULL; + + if(destructor != NULL) + (*destructor)(node); + + node = parent; + } + } + + tree->size = 0; +} diff --git a/libctru/source/util/rbtree_empty.c b/libctru/source/util/rbtree_empty.c new file mode 100644 index 0000000..5ccdfff --- /dev/null +++ b/libctru/source/util/rbtree_empty.c @@ -0,0 +1,7 @@ +#include <3ds/util/rbtree.h> + +int +rbtree_empty(const rbtree_t *tree) +{ + return tree->root == NULL; +} diff --git a/libctru/source/util/rbtree_find.c b/libctru/source/util/rbtree_find.c new file mode 100644 index 0000000..2bcf06b --- /dev/null +++ b/libctru/source/util/rbtree_find.c @@ -0,0 +1,30 @@ +#include <3ds/util/rbtree.h> +#include "rbtree_internal.h" + +rbtree_node_t* +rbtree_find(const rbtree_t *tree, + const rbtree_node_t *node) +{ + rbtree_node_t *tmp = tree->root; + rbtree_node_t *save = NULL; + + while(tmp != NULL) + { + int rc = (*tree->comparator)(node, tmp); + if(rc < 0) + { + tmp = tmp->child[LEFT]; + } + else if(rc > 0) + { + tmp = tmp->child[RIGHT]; + } + else + { + save = tmp; + tmp = tmp->child[LEFT]; + } + } + + return save; +} diff --git a/libctru/source/util/rbtree_init.c b/libctru/source/util/rbtree_init.c new file mode 100644 index 0000000..a8c30d5 --- /dev/null +++ b/libctru/source/util/rbtree_init.c @@ -0,0 +1,10 @@ +#include <3ds/util/rbtree.h> + +void +rbtree_init(rbtree_t *tree, + rbtree_node_comparator_t comparator) +{ + tree->root = NULL; + tree->comparator = comparator; + tree->size = 0; +} diff --git a/libctru/source/util/rbtree_insert.c b/libctru/source/util/rbtree_insert.c new file mode 100644 index 0000000..29950ec --- /dev/null +++ b/libctru/source/util/rbtree_insert.c @@ -0,0 +1,96 @@ +#include <3ds/util/rbtree.h> +#include "rbtree_internal.h" + +static rbtree_node_t* +do_insert(rbtree_t *tree, + rbtree_node_t *node, + int multi) +{ + rbtree_node_t *original = node; + rbtree_node_t **tmp = &tree->root; + rbtree_node_t *parent = NULL; + rbtree_node_t *save = NULL; + + while(*tmp != NULL) + { + int cmp = (*(tree->comparator))(node, *tmp); + parent = *tmp; + + if(cmp < 0) + tmp = &((*tmp)->child[LEFT]); + else if(cmp > 0) + tmp = &((*tmp)->child[RIGHT]); + else + { + if(!multi) + save = *tmp; + + tmp = &((*tmp)->child[LEFT]); + } + } + + if(save != NULL) + { + return save; + } + + *tmp = node; + + node->child[LEFT] = node->child[RIGHT] = NULL; + set_parent(node, parent); + + set_red(node); + + while(is_red((parent = get_parent(node)))) + { + rbtree_node_t *grandparent = get_parent(parent); + int left = (parent == grandparent->child[LEFT]); + rbtree_node_t *uncle = grandparent->child[left]; + + if(is_red(uncle)) + { + set_black(uncle); + set_black(parent); + set_red(grandparent); + + node = grandparent; + } + else + { + if(parent->child[left] == node) + { + rbtree_node_t *tmp; + + rbtree_rotate(tree, parent, left); + + tmp = parent; + parent = node; + node = tmp; + } + + set_black(parent); + set_red(grandparent); + rbtree_rotate(tree, grandparent, !left); + } + } + + set_black(tree->root); + + tree->size += 1; + + return original; +} + +rbtree_node_t* +rbtree_insert(rbtree_t *tree, + rbtree_node_t *node) +{ + return do_insert(tree, node, 0); +} + +void +rbtree_insert_multi(rbtree_t *tree, + rbtree_node_t *node) +{ + do_insert(tree, node, 1); +} diff --git a/libctru/source/util/rbtree_internal.h b/libctru/source/util/rbtree_internal.h new file mode 100644 index 0000000..6d0d0a9 --- /dev/null +++ b/libctru/source/util/rbtree_internal.h @@ -0,0 +1,64 @@ +#pragma once + +#define LEFT 0 +#define RIGHT 1 + +typedef enum rbtree_color +{ + RED = 0, + BLACK = 1, +} rbtree_color_t; + +#define COLOR_MASK (RED|BLACK) + +static inline void +set_black(rbtree_node_t *node) +{ + node->parent_color &= ~COLOR_MASK; + node->parent_color |= BLACK; +} + +static inline void +set_red(rbtree_node_t *node) +{ + node->parent_color &= ~COLOR_MASK; + node->parent_color |= RED; +} + +static inline rbtree_color_t +get_color(const rbtree_node_t *node) +{ + if(node == NULL) + return BLACK; + return (rbtree_color_t)(node->parent_color & COLOR_MASK); +} + +static inline int +is_black(const rbtree_node_t *node) +{ + return get_color(node) == BLACK; +} + +static inline int +is_red(const rbtree_node_t *node) +{ + return get_color(node) == RED; +} + +static inline rbtree_node_t* +get_parent(const rbtree_node_t *node) +{ + return (rbtree_node_t*)(node->parent_color & ~COLOR_MASK); +} + +static inline void +set_parent(rbtree_node_t *node, + const rbtree_node_t *parent) +{ + node->parent_color = (get_color(node)) | ((uintptr_t)parent); +} + +void +rbtree_rotate(rbtree_t *tree, + rbtree_node_t *node, + int left); diff --git a/libctru/source/util/rbtree_iterator.c b/libctru/source/util/rbtree_iterator.c new file mode 100644 index 0000000..424658d --- /dev/null +++ b/libctru/source/util/rbtree_iterator.c @@ -0,0 +1,41 @@ +#include <3ds/util/rbtree.h> +#include "rbtree_internal.h" + +static inline rbtree_node_t* +do_iterate(const rbtree_node_t *node, + int next) +{ + rbtree_node_t *it = (rbtree_node_t*)node; + + if(it->child[next] != NULL) + { + it = it->child[next]; + while(it->child[!next] != NULL) + it = it->child[!next]; + } + else + { + rbtree_node_t *parent = get_parent(node); + while(parent != NULL && it == parent->child[next]) + { + it = parent; + parent = get_parent(it); + } + + it = parent; + } + + return it; +} + +rbtree_node_t* +rbtree_node_next(const rbtree_node_t *node) +{ + return do_iterate(node, RIGHT); +} + +rbtree_node_t* +rbtree_node_prev(const rbtree_node_t *node) +{ + return do_iterate(node, LEFT); +} diff --git a/libctru/source/util/rbtree_minmax.c b/libctru/source/util/rbtree_minmax.c new file mode 100644 index 0000000..f74293c --- /dev/null +++ b/libctru/source/util/rbtree_minmax.c @@ -0,0 +1,37 @@ +#include <3ds/util/rbtree.h> +#include "rbtree_internal.h" + +static inline rbtree_node_t* +do_minmax(const rbtree_t *tree, + int max) +{ + rbtree_node_t *node = tree->root; + + if(node == NULL) + return NULL; + + while(node->child[max] != NULL) + node = node->child[max]; + + return node; +} + +rbtree_node_t* +rbtree_min(const rbtree_t *tree) +{ + rbtree_node_t *node; + + node = do_minmax(tree, LEFT); + + return node; +} + +rbtree_node_t* +rbtree_max(const rbtree_t *tree) +{ + rbtree_node_t *node; + + node = do_minmax(tree, RIGHT); + + return node; +} diff --git a/libctru/source/util/rbtree_remove.c b/libctru/source/util/rbtree_remove.c new file mode 100644 index 0000000..8a0359a --- /dev/null +++ b/libctru/source/util/rbtree_remove.c @@ -0,0 +1,140 @@ +#include <3ds/util/rbtree.h> +#include "rbtree_internal.h" + +static void +recolor(rbtree_t *tree, + rbtree_node_t *parent, + rbtree_node_t *node) +{ + rbtree_node_t *sibling; + + while(is_black(node) && node != tree->root) + { + int left = (node == parent->child[LEFT]); + + sibling = parent->child[left]; + + if(is_red(sibling)) + { + set_black(sibling); + set_red(parent); + rbtree_rotate(tree, parent, left); + sibling = parent->child[left]; + } + + if(is_black(sibling->child[LEFT]) && is_black(sibling->child[RIGHT])) + { + set_red(sibling); + node = parent; + parent = get_parent(node); + } + else + { + if(is_black(sibling->child[left])) + { + set_black(sibling->child[!left]); + set_red(sibling); + rbtree_rotate(tree, sibling, !left); + sibling = parent->child[left]; + } + + if(is_black(parent)) + set_black(sibling); + else + set_red(sibling); + set_black(parent); + set_black(sibling->child[left]); + + rbtree_rotate(tree, parent, left); + + node = tree->root; + } + } + + if(node != NULL) + set_black(node); +} + +rbtree_node_t* +rbtree_remove(rbtree_t *tree, + rbtree_node_t *node, + rbtree_node_destructor_t destructor) +{ + rbtree_color_t color; + rbtree_node_t *child, *parent, *original = node; + rbtree_node_t *next; + + next = rbtree_node_next(node); + + if(node->child[LEFT] != NULL && node->child[RIGHT] != NULL) + { + rbtree_node_t *old = node; + + node = node->child[RIGHT]; + while(node->child[LEFT] != NULL) + node = node->child[LEFT]; + + parent = get_parent(old); + if(parent != NULL) + { + if(parent->child[LEFT] == old) + parent->child[LEFT] = node; + else + parent->child[RIGHT] = node; + } + else + tree->root = node; + + child = node->child[RIGHT]; + parent = get_parent(node); + color = get_color(node); + + if(parent == old) + parent = node; + else + { + if(child != NULL) + set_parent(child, parent); + parent->child[LEFT] = child; + + node->child[RIGHT] = old->child[RIGHT]; + set_parent(old->child[RIGHT], node); + } + + node->parent_color = old->parent_color; + node->child[LEFT] = old->child[LEFT]; + set_parent(old->child[LEFT], node); + } + else + { + if(node->child[LEFT] == NULL) + child = node->child[RIGHT]; + else + child = node->child[LEFT]; + + parent = get_parent(node); + color = get_color(node); + + if(child != NULL) + set_parent(child, parent); + if(parent != NULL) + { + if(parent->child[LEFT] == node) + parent->child[LEFT] = child; + else + parent->child[RIGHT] = child; + } + else + tree->root = child; + } + + if(color == BLACK) + recolor(tree, parent, child); + + if(destructor != NULL) + (*destructor)(original); + + tree->size -= 1; + + return next; +} diff --git a/libctru/source/util/rbtree_rotate.c b/libctru/source/util/rbtree_rotate.c new file mode 100644 index 0000000..c47d290 --- /dev/null +++ b/libctru/source/util/rbtree_rotate.c @@ -0,0 +1,28 @@ +#include <3ds/util/rbtree.h> +#include "rbtree_internal.h" + +void +rbtree_rotate(rbtree_t *tree, + rbtree_node_t *node, + int left) +{ + rbtree_node_t *tmp = node->child[left]; + rbtree_node_t *parent = get_parent(node); + + node->child[left] = tmp->child[!left]; + if(tmp->child[!left] != NULL) + set_parent(tmp->child[!left], node); + + tmp->child[!left] = node; + set_parent(tmp, parent); + if(parent != NULL) + { + if(node == parent->child[!left]) + parent->child[!left] = tmp; + else + parent->child[left] = tmp; + } + else + tree->root = tmp; + set_parent(node, tmp); +} diff --git a/libctru/source/util/rbtree_size.c b/libctru/source/util/rbtree_size.c new file mode 100644 index 0000000..401d43d --- /dev/null +++ b/libctru/source/util/rbtree_size.c @@ -0,0 +1,7 @@ +#include <3ds/util/rbtree.h> + +size_t +rbtree_size(const rbtree_t *tree) +{ + return tree->size; +}