From 8058daf234141bd6ce92774ee1043bd2663a7e2a Mon Sep 17 00:00:00 2001 From: Gregory Lirent Date: Sun, 5 Jun 2022 21:40:37 +0300 Subject: [PATCH] Add red-black tree base --- src/__internal/rbtree.h | 49 +++++ src/__internal/vnode.h | 6 - src/rbtree.c | 383 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 432 insertions(+), 6 deletions(-) create mode 100644 src/__internal/rbtree.h create mode 100644 src/rbtree.c diff --git a/src/__internal/rbtree.h b/src/__internal/rbtree.h new file mode 100644 index 0000000..f0eb203 --- /dev/null +++ b/src/__internal/rbtree.h @@ -0,0 +1,49 @@ +/* This software is licensed by the Apache License 2.0, see LICENSE file */ +/* Copyright © 2022 Gregory Lirent */ + +#include "vnode.h" + +#ifndef LIBCDSB_SRC_INTERNAL_RBTREE_H +#define LIBCDSB_SRC_INTERNAL_RBTREE_H + +typedef enum libcdsb_rbtree_iter_flags { + RBI_INORDER = 0, + RBI_PREORDER = 1, + RBI_POSTORDER = 2, + + RBI_REVERSE = 4, +} rbiter_f; + +typedef struct libcdsb_rbtree_node { + struct libcdsb_rbtree_node* left; + struct libcdsb_rbtree_node* right; + struct libcdsb_rbtree_node* parent; + + vnode_t value; + short colored; +} rbnode_t; + +typedef struct libcdsb_rbtree_iter { + struct libcdsb_rbtree_node* cursor; + int flags; +} rbiter_t; + +extern const rbnode_t LIBCDSB_RBTREE_NODE_EMPTY[1]; + +extern void* libcdsb_rbtree_node_create(void* value, rbnode_t* parent, int colored, int size); +extern void libcdsb_rbtree_node_fixup (rbnode_t** root, rbnode_t* node); +extern rbnode_t* libcdsb_rbtree_node_delete(rbnode_t** root, rbnode_t* node); + +extern _Bool libcdsb_rbtree_iter_init (rbiter_t* iter, rbnode_t *const *root, rbiter_f flags); +extern rbnode_t* libcdsb_rbtree_iter_next (rbiter_t* iter); +extern void libcdsb_rbtree_iter_reset(rbiter_t* iter); + +#define rbnode_empty ((rbnode_t*)LIBCDSB_RBTREE_NODE_EMPTY) +#define rbnode_create(v, p, c) ((rbnode_t*)libcdsb_rbtree_node_create(v, p, c, sizeof(rbnode_t))) +#define rbnode_fixup libcdsb_rbtree_node_fixup +#define rbnode_delete libcdsb_rbtree_node_delete + +#define rbnode_is_empty(n) ((n) == rbnode_empty) +#define rbnode_is_root(n) rbnode_is_empty((n)->parent) + +#endif /* LIBCDSB_SRC_INTERNAL_RBTREE_H */ diff --git a/src/__internal/vnode.h b/src/__internal/vnode.h index 6a03b14..4303a37 100644 --- a/src/__internal/vnode.h +++ b/src/__internal/vnode.h @@ -6,12 +6,6 @@ #ifndef LIBCDSB_SRC_INTERNAL_VNODE_H #define LIBCDSB_SRC_INTERNAL_VNODE_H -#define mark_ptr(x, m) ((void*)((uintptr_t)(x)|(uintptr_t)(m))) -#define unmark_ptr(x) ((void*)((uintptr_t)(x)&~(uintptr_t)(_Alignof(max_align_t)-1))) -#define get_mark(x) ((int)((uintptr_t)(x)&(uintptr_t)(_Alignof(max_align_t)-1))) -#define unmark_ptr16(x) ((void*)((uintptr_t)(x)&~(uintptr_t)(15))) -#define get_mark16(x) ((int)((uintptr_t)(x)&(uintptr_t)(15))) - #define is_permissible(T) (sizeof(void*) >= sizeof(T) && _Alignof(void*) >= _Alignof(T)) typedef union { diff --git a/src/rbtree.c b/src/rbtree.c new file mode 100644 index 0000000..22dee93 --- /dev/null +++ b/src/rbtree.c @@ -0,0 +1,383 @@ +/* This software is licensed by the MIT License, see LICENSE file */ +/* Copyright © 2022 Gregory Lirent */ + +#include "__internal/rbtree.h" + +#define RBI_BREAK ((rbiter_f)3) +#define RBI_FIRST ((rbiter_f)8) + +typedef enum libcdsb_rbtree_node_direction { + RBD_LEFT = 1, + RBD_RIGHT = 2 +} rbdir_t; + +#define rbdir_dir(cur, d) (&((cur)->left))[(d)>>1] +#define rbdir_inv(cur, d) (&((cur)->left))[(d)&1] + +/*#####################################################################################################################*/ + +const rbnode_t LIBCDSB_RBTREE_NODE_EMPTY[1] = {{ + .colored = 0, + .value = 0, + .parent = rbnode_empty, + .left = rbnode_empty, + .right = rbnode_empty +}}; + +/*#####################################################################################################################*/ + +static inline void rotate(rbnode_t **x, rbnode_t *c, rbdir_t d) { + rbnode_t* n = rbdir_inv(c, d); + + rbdir_inv(c, d) = rbdir_dir(n, d); + n->parent = c->parent; + + if (!rbnode_is_empty(rbdir_dir(n, d))) { + rbdir_dir(n, d)->parent = c; + } + + if (!rbnode_is_root(c)) { + rbnode_t* p = c->parent; + + if (rbdir_dir(p, d) == c) { + rbdir_dir(p, d) = n; + } else rbdir_inv(c, d) = n; + } else *x = n; + + rbdir_inv(n, d) = c; + c->parent = n; +} + +static inline void replace(rbnode_t** x, rbnode_t* c, rbnode_t* n) { + rbnode_t* p; + + p = c->parent; + + if (!rbnode_is_empty(p)) { + if (p->left == c) { + p->left = n; + } else p->right = n; + } else *x = n; + + n->parent = p; +} + + +/*#####################################################################################################################*/ + + +static rbnode_t* inorder_next(rbnode_t* c, rbiter_f f) { + rbdir_t d = (f&RBI_REVERSE) ? RBD_RIGHT : RBD_LEFT; + rbnode_t* p; + + for (;;) { + if (rbnode_is_empty(rbdir_inv(c, d))) { + p = c->parent; + + if (rbnode_is_root(p) || rbdir_dir(p, d) == c) { + return p; + } + + c = p; + p = c->parent; + + if (rbnode_is_root(p) || rbdir_dir(p, d) == c) { + return p; + } + + do { + c = p; + p = c->parent; + } while(rbdir_inv(p, d) == c); + + c = p; + + } else { c = rbdir_inv(c, d); break; } + + if (rbnode_is_root(c)) return c; + } + + while (!rbnode_is_empty(rbdir_dir(c, d))) + c = rbdir_dir(c, d); + + return c; +} + +static rbnode_t* postorder_next(rbnode_t* c, rbiter_f f) { + rbdir_t d = (f&RBI_REVERSE) ? RBD_RIGHT : RBD_LEFT; + rbnode_t* p; + + if (rbnode_is_empty(p = c->parent)) + return c; + + if (rbdir_dir(p, d) == c && !rbnode_is_empty(rbdir_inv(p, d))) { + c = rbdir_inv(p, d); + + while (!rbnode_is_empty(c)) { + p = c; + c = (!rbnode_is_empty(rbdir_inv(p, d))) + ? rbdir_inv(p, d) + : rbdir_dir(p, d); + } + } + + return p; +} + +static rbnode_t* preorder_next(rbnode_t* c, rbiter_f f) { + rbdir_t d = (f&RBI_REVERSE) ? RBD_RIGHT : RBD_LEFT; + rbnode_t* p; + + if (!rbnode_is_empty(rbdir_dir(c, d))) + return rbdir_dir(c, d); + + for (;;) { + if (!rbnode_is_empty(rbdir_inv(c, d))) { + return rbdir_inv(c, d); + } + + p = c->parent; + + while (rbdir_inv(p, d) == c) { + c = p; + p = c->parent; + } + + if (rbnode_is_empty(p)) + return c; + + c = p; + } +} + + +/*#####################################################################################################################*/ + + +static void delete_fixup(rbnode_t** x, rbnode_t* n) { + rbdir_t d; + rbnode_t *s, *p; + + while (!rbnode_is_root(n) && !n->colored) { + p = n->parent; + d = (p->left == n) ? RBD_LEFT : RBD_RIGHT; + s = rbdir_inv(p, d); + + if (s->colored) { + s->colored = 0; + p->colored = 1; + rotate(x, p, d); + p = n->parent; + s = rbdir_inv(p, d); + } + + if (!rbdir_dir(s, d)->colored && !rbdir_inv(s, d)->colored) { + s->colored = 1; + n = p; + } else { + if (!rbdir_inv(s, d)->colored) { + rbdir_dir(s, d)->colored = 0; + s->colored = 1; + rotate(x, s, (d == RBD_LEFT) ? RBD_RIGHT : RBD_LEFT); + p = n->parent; + s = rbdir_inv(p, d); + } + + s->colored = p->colored; + p->colored = 0; + rbdir_inv(s, d)->colored = 0; + + rotate(x, p, d); + + n = *x; + } + } + + n->colored = 0; +} + +/*#####################################################################################################################*/ + + +rbnode_t* libcdsb_rbtree_node_delete(rbnode_t** x, rbnode_t* c) { + rbnode_t *n, *t; + int s; + void* v; + + s = c->colored; + + if (rbnode_is_empty(c->left)) { + n = c->right; + replace(x, c, c->right); + } else if (rbnode_is_empty(c->right)) { + n = c->left; + replace(x, c, c->left); + } else { + t = c->right; + + while (!rbnode_is_empty(t->left)) { + t = t->left; + } + + s = t->colored; + n = t->right; + + if (t->parent == c) { + n->parent = t; + } else { + replace(x, t, t->right); + c->right->parent = t; + t->right = c->right; + } + + replace(x, c, t); + c->left->parent = t; + t->colored = c->colored; + t->left = c->left; + } + + if (!s) delete_fixup(x, n); + + return c; +} + + + +void libcdsb_rbtree_node_fixup(rbnode_t** x, rbnode_t* n) { + rbdir_t d[2]; + rbnode_t *u, *p, *gp; + + while (n->parent->colored) { + p = n->parent; + gp = p->parent; + d[0] = (gp->right == p) ? RBD_LEFT : RBD_RIGHT; + u = rbdir_dir(gp, d[0]); + + if (rbdir_dir(p, d[0]) == n) { + d[1] = (d[0] == RBD_LEFT) ? RBD_RIGHT : RBD_LEFT; + } else d[1] = 0; + + if (u->colored) { + u->colored = 0; + p->colored = 0; + gp->colored = 1; + + n = gp; + } else { + if (d[1]) { + rotate(x, n = p, d[1]); + p = n->parent; + gp = p->parent; + } + p->colored = 0; + gp->colored = 1; + + rotate(x, gp, d[0]); + } + + if (rbnode_is_root(n)) break; + } + + (*x)->colored = 0; +} + + +void* libcdsb_rbtree_node_create(void* v, rbnode_t* p, int c, int n) { + rbnode_t* x; + + x = malloc(n); + + x->left = rbnode_empty; + x->right = rbnode_empty; + x->value = v; + x->colored = c; + x->parent = p; + + return x; +} + + +/*#####################################################################################################################*/ + + +_Bool libcdsb_rbtree_iter_init(rbiter_t* iter, rbnode_t *const *root, rbiter_f f) { + rbdir_t d = (f&RBI_REVERSE) ? RBD_RIGHT : RBD_LEFT; + rbnode_t* c = *root; + + if (!rbnode_is_empty(c)) { + if (!(f&RBI_BREAK)) { + while (rbdir_dir(c, d) != rbnode_empty) { + c = rbdir_dir(c, d); + } + } else if ((f&RBI_BREAK) == RBI_POSTORDER) { + rbnode_t* p; + + do { + p = c; + c = (!rbnode_is_empty(rbdir_dir(c, d))) ? rbdir_dir(c, d) : rbdir_inv(c, d); + } while (!rbnode_is_empty(c)); + + c = p; + } + } else { + iter->cursor = rbnode_empty; + iter->flags = 0; + return false; + } + + iter->cursor = c; + iter->flags = f|RBI_FIRST; + + return true; +} + + + +void libcdsb_rbtree_iter_reset(rbiter_t* iter) { + if (iter->flags&RBI_FIRST) return; + + while (!rbnode_is_empty(iter->cursor->parent)) { + iter->cursor = iter->cursor->parent; + } + + if ((iter->flags&RBI_BREAK) == RBI_BREAK) { + libcdsb_rbtree_iter_init(iter, &iter->cursor, iter->flags&~RBI_BREAK); + } else libcdsb_rbtree_iter_init(iter, &iter->cursor, iter->flags); +} + + +rbnode_t* hhttpc_rbtree_iter_next(rbiter_t* iter) { + + rbnode_t* c; + + c = iter->cursor; + + if (rbnode_is_empty(c)) + return rbnode_empty; + + if (!(iter->flags&RBI_FIRST)) { + if (rbnode_is_root(c)) { + if ((iter->flags&RBI_BREAK) == RBI_INORDER) { + iter->flags |= RBI_BREAK; + } else { + libcdsb_rbtree_iter_reset(iter); + if (iter->flags&RBI_POSTORDER) { + iter->flags |= RBI_BREAK; + return c; + } else return rbnode_empty; + } + } + } else if ((iter->flags&RBI_BREAK) == RBI_BREAK) { + iter->flags = (iter->flags&~(RBI_BREAK^RBI_POSTORDER)); + return rbnode_empty; + } else iter->flags &= ~RBI_FIRST; + + switch (iter->flags&RBI_BREAK) { + case RBI_POSTORDER: iter->cursor = postorder_next(c, iter->flags); break; + case RBI_PREORDER: iter->cursor = preorder_next(c, iter->flags); break; + case RBI_INORDER: + default: iter->cursor = inorder_next(c, iter->flags); break; + } + + return c; +}