Add red-black tree base
This commit is contained in:
parent
da1f20452c
commit
8058daf234
49
src/__internal/rbtree.h
Normal file
49
src/__internal/rbtree.h
Normal file
@ -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 */
|
@ -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 {
|
||||
|
383
src/rbtree.c
Normal file
383
src/rbtree.c
Normal file
@ -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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user