#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include "ctree.h"
#include "out.h"
#include "sys_util.h"
#define BIT_IS_SET(n, i) (!!((n) & (1ULL << (i))))
#define NODE_IS_INTERNAL(node) (BIT_IS_SET((uintptr_t)(node), 0))
#define NODE_INTERNAL_GET(node) ((void *)((char *)(node) - 1))
#define NODE_INTERNAL_SET(d, node) ((d) = (void *)((char *)(node) + 1))
#ifndef CTREE_FAST_RECURSIVE_DELETE
#define CTREE_FAST_RECURSIVE_DELETE 1
#endif
struct node {
void *slots[2];
unsigned diff;
};
struct node_leaf {
uint64_t key;
uint64_t value;
};
struct ctree {
void *root;
os_mutex_t lock;
};
static unsigned
find_crit_bit(uint64_t lhs, uint64_t rhs)
{
uint64_t val = lhs ^ rhs;
ASSERTne(val, 0);
return util_mssb_index64(val);
}
struct ctree *
ctree_new(void)
{
struct ctree *t = Malloc(sizeof(*t));
if (t == NULL)
return NULL;
util_mutex_init(&t->lock);
t->root = NULL;
return t;
}
static void
ctree_free_internal_recursive(void *dst, ctree_destroy_cb cb, void *ctx)
{
if (NODE_IS_INTERNAL(dst)) {
struct node *a = NODE_INTERNAL_GET(dst);
ctree_free_internal_recursive(a->slots[0], cb, ctx);
ctree_free_internal_recursive(a->slots[1], cb, ctx);
Free(a);
} else {
if (cb) {
struct node_leaf *leaf = dst;
cb(leaf->key, leaf->value, ctx);
}
Free(dst);
}
}
void
ctree_delete(struct ctree *t)
{
ctree_clear_unlocked(t);
util_mutex_destroy(&t->lock);
Free(t);
}
void
ctree_clear_unlocked(struct ctree *t)
{
#if CTREE_FAST_RECURSIVE_DELETE
if (t->root)
ctree_free_internal_recursive(t->root, NULL, NULL);
#else
while (t->root)
ctree_remove_unlocked(t, 0, 0);
#endif
t->root = NULL;
}
void
ctree_clear(struct ctree *t)
{
util_mutex_lock(&t->lock);
ctree_clear_unlocked(t);
util_mutex_unlock(&t->lock);
}
void
ctree_delete_cb(struct ctree *t, ctree_destroy_cb cb, void *ctx)
{
if (t->root)
ctree_free_internal_recursive(t->root, cb, ctx);
util_mutex_destroy(&t->lock);
Free(t);
}
int
ctree_insert_unlocked(struct ctree *t, uint64_t key, uint64_t value)
{
void **dst = &t->root;
struct node *a = NULL;
int err = 0;
while (NODE_IS_INTERNAL(*dst)) {
a = NODE_INTERNAL_GET(*dst);
dst = &a->slots[BIT_IS_SET(key, a->diff)];
}
struct node_leaf *dstleaf = *dst;
struct node_leaf *nleaf = Malloc(sizeof(*nleaf));
if (nleaf == NULL)
return ENOMEM;
nleaf->key = key;
nleaf->value = value;
if (dstleaf == NULL) {
*dst = nleaf;
goto out;
}
struct node *n = Malloc(sizeof(*n));
if (n == NULL) {
err = ENOMEM;
goto error_internal_malloc;
}
if (dstleaf->key == key) {
err = EEXIST;
goto error_duplicate;
}
n->diff = find_crit_bit(dstleaf->key, key);
int d = BIT_IS_SET(key, n->diff);
n->slots[d] = nleaf;
dst = &t->root;
while (NODE_IS_INTERNAL(*dst)) {
a = NODE_INTERNAL_GET(*dst);
if (a->diff < n->diff)
break;
dst = &a->slots[BIT_IS_SET(key, a->diff)];
}
n->slots[!d] = *dst;
NODE_INTERNAL_SET(*dst, n);
out:
return 0;
error_duplicate:
Free(n);
error_internal_malloc:
Free(nleaf);
return err;
}
int
ctree_insert(struct ctree *t, uint64_t key, uint64_t value)
{
util_mutex_lock(&t->lock);
int ret = ctree_insert_unlocked(t, key, value);
util_mutex_unlock(&t->lock);
return ret;
}
uint64_t
ctree_find_unlocked(struct ctree *t, uint64_t key)
{
struct node_leaf *dst = t->root;
struct node *a = NULL;
while (NODE_IS_INTERNAL(dst)) {
a = NODE_INTERNAL_GET(dst);
dst = a->slots[BIT_IS_SET(key, a->diff)];
}
if (dst && dst->key == key)
return key;
else
return 0;
}
uint64_t
ctree_find(struct ctree *t, uint64_t key)
{
util_mutex_lock(&t->lock);
uint64_t ret = ctree_find_unlocked(t, key);
util_mutex_unlock(&t->lock);
return ret;
}
uint64_t
ctree_find_le_unlocked(struct ctree *t, uint64_t *key)
{
struct node_leaf *dst = t->root;
struct node *a = NULL;
while (NODE_IS_INTERNAL(dst)) {
a = NODE_INTERNAL_GET(dst);
dst = a->slots[BIT_IS_SET(*key, a->diff)];
}
if (dst == NULL || dst->key == *key)
goto out;
unsigned diff = find_crit_bit(dst->key, *key);
struct node *top = NULL;
dst = t->root;
while (NODE_IS_INTERNAL(dst)) {
a = NODE_INTERNAL_GET(dst);
if (a->diff < diff)
break;
if (BIT_IS_SET(*key, a->diff)) {
top = a->slots[0];
dst = a->slots[1];
} else {
dst = a->slots[0];
}
}
if (!BIT_IS_SET(*key, diff))
dst = (struct node_leaf *)top;
while (NODE_IS_INTERNAL(dst)) {
a = NODE_INTERNAL_GET(dst);
dst = a->slots[1];
}
if (dst && dst->key > *key)
dst = NULL;
out:
*key = dst ? dst->key : 0;
uint64_t ret = dst ? dst->value : 0;
return ret;
}
uint64_t
ctree_find_le(struct ctree *t, uint64_t *key)
{
util_mutex_lock(&t->lock);
uint64_t ret = ctree_find_le_unlocked(t, key);
util_mutex_unlock(&t->lock);
return ret;
}
static void
ctree_remove_leaf(struct ctree *t, void **dst, void **pparent)
{
if (t->root == *dst) {
Free(*dst);
*dst = NULL;
} else {
struct node *parent = NODE_INTERNAL_GET(*pparent);
*pparent = parent->slots[parent->slots[0] == *dst];
Free(*dst);
Free(parent);
}
}
int
ctree_remove_max_unlocked(struct ctree *t, uint64_t *key, uint64_t *value)
{
void **dst = &t->root;
void **p = NULL;
struct node *a = NULL;
while (NODE_IS_INTERNAL(*dst)) {
a = NODE_INTERNAL_GET(*dst);
p = dst;
dst = &a->slots[1];
}
struct node_leaf *leaf = *dst;
if (leaf == NULL) {
return -1;
}
*key = leaf->key;
*value = leaf->value;
ctree_remove_leaf(t, dst, p);
return 0;
}
int
ctree_remove_max(struct ctree *t, uint64_t *key, uint64_t *value)
{
util_mutex_lock(&t->lock);
int ret = ctree_remove_max_unlocked(t, key, value);
util_mutex_unlock(&t->lock);
return ret;
}
uint64_t
ctree_remove_unlocked(struct ctree *t, uint64_t key, int eq)
{
void **p = NULL;
void **dst = &t->root;
struct node *a = NULL;
struct node_leaf *leaf = NULL;
uint64_t k = 0;
if (t->root == NULL)
goto out;
while (NODE_IS_INTERNAL(*dst)) {
a = NODE_INTERNAL_GET(*dst);
p = dst;
dst = &a->slots[BIT_IS_SET(key, a->diff)];
}
leaf = *dst;
k = leaf->key;
if (leaf->key == key) {
goto remove;
} else if (eq && leaf->key != key) {
k = 0;
goto out;
}
unsigned diff = find_crit_bit(k, key);
void **top = NULL;
void **topp = NULL;
p = NULL;
dst = &t->root;
while (NODE_IS_INTERNAL(*dst)) {
a = NODE_INTERNAL_GET(*dst);
p = dst;
if (a->diff < diff)
break;
if (BIT_IS_SET(key, a->diff)) {
dst = &a->slots[1];
} else {
topp = dst;
top = &a->slots[1];
dst = &a->slots[0];
}
}
if (BIT_IS_SET(key, diff)) {
dst = top;
p = topp;
a = p ? NODE_INTERNAL_GET(*p) : NULL;
}
if (dst == NULL) {
k = 0;
goto out;
}
while (NODE_IS_INTERNAL(*dst)) {
a = NODE_INTERNAL_GET(*dst);
p = dst;
dst = &a->slots[0];
}
leaf = *dst;
k = leaf->key;
ASSERT(k > key);
remove:
if (a == NULL) {
Free(*dst);
*dst = NULL;
} else {
ASSERTne(p, NULL);
*p = a->slots[a->slots[0] == *dst];
Free(*dst);
Free(a);
}
out:
return k;
}
uint64_t
ctree_remove(struct ctree *t, uint64_t key, int eq)
{
util_mutex_lock(&t->lock);
uint64_t ret = ctree_remove_unlocked(t, key, eq);
util_mutex_unlock(&t->lock);
return ret;
}
int
ctree_is_empty_unlocked(struct ctree *t)
{
return t->root == NULL;
}
int
ctree_is_empty(struct ctree *t)
{
util_mutex_lock(&t->lock);
int ret = ctree_is_empty_unlocked(t);
util_mutex_unlock(&t->lock);
return ret;
}