]> Chaos Git - corbenik/ctrulib.git/commitdiff
Add rbtree
authormtheall <pigman46@gmail.com>
Thu, 20 Nov 2014 21:28:13 +0000 (15:28 -0600)
committermtheall <pigman46@gmail.com>
Thu, 20 Nov 2014 21:28:13 +0000 (15:28 -0600)
13 files changed:
libctru/Makefile
libctru/include/3ds/util/rbtree.h [new file with mode: 0644]
libctru/source/util/rbtree_clear.c [new file with mode: 0644]
libctru/source/util/rbtree_empty.c [new file with mode: 0644]
libctru/source/util/rbtree_find.c [new file with mode: 0644]
libctru/source/util/rbtree_init.c [new file with mode: 0644]
libctru/source/util/rbtree_insert.c [new file with mode: 0644]
libctru/source/util/rbtree_internal.h [new file with mode: 0644]
libctru/source/util/rbtree_iterator.c [new file with mode: 0644]
libctru/source/util/rbtree_minmax.c [new file with mode: 0644]
libctru/source/util/rbtree_remove.c [new file with mode: 0644]
libctru/source/util/rbtree_rotate.c [new file with mode: 0644]
libctru/source/util/rbtree_size.c [new file with mode: 0644]

index d3ae023b87a942a7eb8c5140f33bf298ea1a4fb3..114d61bdd60c05af7d3c7f0e1c205935cf7b4fc6 100644 (file)
@@ -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 (file)
index 0000000..73110f0
--- /dev/null
@@ -0,0 +1,78 @@
+#pragma once
+
+#include <stdint.h>
+#include <stddef.h>
+
+#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 (file)
index 0000000..fd945f1
--- /dev/null
@@ -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 (file)
index 0000000..5ccdfff
--- /dev/null
@@ -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 (file)
index 0000000..2bcf06b
--- /dev/null
@@ -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 (file)
index 0000000..a8c30d5
--- /dev/null
@@ -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 (file)
index 0000000..29950ec
--- /dev/null
@@ -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 (file)
index 0000000..6d0d0a9
--- /dev/null
@@ -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 (file)
index 0000000..424658d
--- /dev/null
@@ -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 (file)
index 0000000..f74293c
--- /dev/null
@@ -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 (file)
index 0000000..8a0359a
--- /dev/null
@@ -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 (file)
index 0000000..c47d290
--- /dev/null
@@ -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 (file)
index 0000000..401d43d
--- /dev/null
@@ -0,0 +1,7 @@
+#include <3ds/util/rbtree.h>
+
+size_t
+rbtree_size(const rbtree_t *tree)
+{
+  return tree->size;
+}