diff --git a/src/dict/base.c b/src/dict/base.c new file mode 100644 index 0000000..29e4854 --- /dev/null +++ b/src/dict/base.c @@ -0,0 +1,210 @@ +/* This software is licensed by the MIT License, see LICENSE file */ +/* Copyright © 2022 Gregory Lirent */ + +#include "include.h" + +#define dtree_duplicate(s) rbtree_duplicate((rbnode_t*)s, (void*)dnode_duplicate, nullptr) +#define dtree_compare(s0, s1) rbtree_compare((void*)s0, (void*)s1, (void*)dnode_compare, nullptr) + +static dnode_t* dnode_duplicate(const dnode_t* s, dnode_t* p, void* not_used) { + dnode_t* x; + + x = dnode_create(vnode_duplicate(&s->key, s->key_type), p, s->colored); + x->key_type = s->key_type; + x->val_type = s->val_type; + x->value = vnode_duplicate(&s->value, s->val_type); + + return x; +} + +static int dnode_compare(const dnode_t *s0, const dnode_t *s1, void* not_used) { + int c = vnode_compare(&s0->key, s1->key_type, &s1->key, s1->key_type); + + return (!c) ? vnode_compare(&s0->value, s1->val_type, &s1->value, s1->val_type) : c; +} + +static hash_t dnode_hash(const dnode_t* s, void* not_used) { + return vnode_hash(s->key, s->key_type) + vnode_hash(s->value, s->val_type) + s->key_type; +} + +/*#####################################################################################################################*/ + +static int dict_compare_equal_capacity(const dict_t* s0, const dict_t* s1) { + int c; + + for (size_t i = 0; i < s0->capacity; ++i) { + if ((c = dtree_compare(s0->nodes[i], s1->nodes[i]))) { + return c; + } + } + + return 0; +} + + +static int dict_compare_unequal_capacity(const dict_t* s0, const dict_t* s1) { + const dict_t *x, *y; + dnode_t *c0, *c1; + int cmp; + + stack_t z; + + if (s0->capacity <= s1->capacity) { + x = s0; + y = s1; + } else { + x = s1; + y = s0; + } + + z.prev = 0; + z.value = 0; + + for (size_t i = 0; i < x->capacity; ++i) { + if (!dnode_is_empty(x->nodes[i])) + stack_push(&z, x->nodes[i]); + } + + while ((c0 = stack_pop(&z))) { + + c1 = y->nodes[vnode_hash(c0->key, c0->key_type) / y->capacity]; + cmp = 1; + + while (!dnode_is_empty(c1)) { + + cmp = vnode_compare(c0->key, c0->key_type, c1->key, c1->key_type); + + if (cmp == 0) break; + + c1 = (cmp < 0) ? c1->left : c1->right; + } + + if (cmp) return x == s0 ? cmp : ~cmp + 1; + + if (!dnode_is_empty(c0->right)) stack_push(&z, c0->right); + if (!dnode_is_empty(c0->left)) stack_push(&z, c0->left); + } + + return 0; +} + +/*#####################################################################################################################*/ + +hash_t dict_hash(const dict_t* s) { + dnode_t *l, *r; + hash_t hash; + + if (!s->size) return 0; + + l = dnode_empty; + r = dnode_empty; + + for (size_t i = 0; i < s->capacity; ++i) { + if (!dnode_is_empty(s->nodes[i])) { + if (dnode_is_empty(l) || vnode_compare(s->nodes[i]->key, s->nodes[i]->key_type, l->key, l->key_type) < 0) { + l = s->nodes[i]; + } + + if (dnode_is_empty(r) || vnode_compare(s->nodes[i]->key, s->nodes[i]->key_type, r->key, r->key_type) > 0) { + r = s->nodes[i]; + } + } + } + + while (!dnode_is_empty(l->left)) + l = l->left; + + while (!dnode_is_empty(r->right)) + r = r->right; + + hash = vnode_hash(l->key, l->key_type) + vnode_hash(l->value, l->val_type); + + if (l != r) hash += vnode_hash(r->key, r->key_type) + vnode_hash(r->value, r->val_type); + + return (hash ^ s->size) + VTYPE_DICT; +} + +void dict_init(dict_t* x) { + memset(x, 0, sizeof(*x)); +} + + + +void dict_free(dict_t* x) { + for (size_t i = 0; i < x->capacity; ++i) + rbtree_free(x->nodes[i], (void*)dnode_free, nullptr); + + free(x->nodes); + memset(x, 0, sizeof(*x)); +} + +void libcdcb_dnode_free(dnode_t* x, void* not_used) { + vnode_free(&x->key, x->key_type); + vnode_free(&x->value, x->val_type); +} + +/*#####################################################################################################################*/ + +size_t dict_size(const dict_t* x) { + return x->size; +} + +size_t dict_capacity(const dict_t* x) { + return x->capacity; +} + +/*#####################################################################################################################*/ + +int dict_compare(const dict_t* s0, const dict_t* s1) { + + if (s0 == s1) + return 0; + + if (s0->size != s1->size) + return s0->size < s1->size ? -1 : 1; + + if (s0->capacity == s1->capacity) + return dict_compare_equal_capacity(s0, s1); + + return dict_compare_unequal_capacity(s0, s1); +} + +/*#####################################################################################################################*/ + +dict_t dict_copy(const dict_t* s) { + dict_t x; + + x.capacity = s->capacity; + x.size = s->size; + x.nodes = malloc(x.size * sizeof(*x.nodes)); + + for (size_t i = 0; i < x.capacity; ++i) { + x.nodes[i] = dtree_duplicate(s->nodes[i]); + } + + return x; +} + +dict_t* dict_duplicate(const dict_t* s) { + dict_t *x = malloc(sizeof(*x)); + + x->capacity = s->capacity; + x->size = s->size; + x->nodes = malloc(x->size * sizeof(*x->nodes)); + + for (size_t i = 0; i < x->capacity; ++i) { + x->nodes[i] = dtree_duplicate(s->nodes[i]); + } + + return x; +} + +void dict_copy_init(dict_t* x, const dict_t* s) { + x->capacity = s->capacity; + x->size = s->size; + x->nodes = malloc(x->size * sizeof(*x->nodes)); + + for (size_t i = 0; i < x->capacity; ++i) { + x->nodes[i] = dtree_duplicate(s->nodes[i]); + } +} diff --git a/src/dict/extra.c b/src/dict/extra.c new file mode 100644 index 0000000..66e2dd2 --- /dev/null +++ b/src/dict/extra.c @@ -0,0 +1,209 @@ +/* This software is licensed by the MIT License, see LICENSE file */ +/* Copyright © 2022 Gregory Lirent */ + +#include "include.h" + +/*#####################################################################################################################*/ + +static void dict_rehash(dict_t* s, size_t capacity) { + stack_t z; + int cmp; + dnode_t *c, *p, *n, **r; + + dnode_t **nodes = malloc(capacity * sizeof(*nodes)); + + while (s->capacity--) { + if (!dnode_is_empty(s->nodes[s->capacity])) + stack_push(&z, s->nodes[s->capacity]); + } + + while ((c = stack_pop(&z))) { + + if (!dnode_is_empty(c->right)) { + stack_push(&z, c->right); + c->right = dnode_empty; + } + + if (!dnode_is_empty(c->left)) { + stack_push(&z, c->left); + c->left = dnode_empty; + } + + n = *(r = &nodes[vnode_hash(c->key, c->key_type) / capacity]); + + if (!dnode_is_empty(*r)) { + do { + p = n; + cmp = vnode_compare(&c->key, c->key_type, &n->key, n->key_type); + n = (cmp <= 0) ? n->left : n->right; + } while (!dnode_is_empty(n)); + + if (cmp < 0) p->left = c; + else p->right = c; + + c->parent = p; + c->colored = 1; + + if (!dnode_is_root(p)) + dnode_fixup(r, n); + + } else { + *r = c; + c->colored = 0; + c->parent = dnode_empty; + } + } + + free(s->nodes); + + s->capacity = capacity; + s->nodes = nodes; +} + +/*#####################################################################################################################*/ + +bool libcdsb_dict_shrink_to_fit(dict_t* s) { + + size_t capacity; + + capacity = (s->size / CAPACITY_BLOCK) + 1; + capacity *= CAPACITY_BLOCK; + + while (((double)s->size / capacity) > 0.65) + capacity += CAPACITY_BLOCK; + + if (capacity >= s->capacity) + return false; + + dict_rehash(s, capacity); + + return true; +} + + +bool libcdsb_dict_update(dict_t* x, const void* k, vtype kt, const void* v, vtype vt) { + + dnode_t **r, *n, *p; + vnode_t kn, vn; + int cmp; + + if (!x->capacity || (double)x->size / x->capacity > REBUILD_POINT_MAX) { + dict_rehash(x, x->capacity + CAPACITY_BLOCK); + } + + n = *(r = &x->nodes[vnode_hash(k, kt) / x->capacity]); + kn = vnode_create(k, kt); + vn = vnode_create(v, vt); + + if (!dnode_is_empty(n)) { + do { + p = n; + cmp = vtype_compare(k, kt, vnode_peek(&n->key, n->key_type), n->key_type); + + if (cmp == 0) { + dnode_free(n, nullptr); + + n->key = kn; + n->value = vn; + n->key_type = kt; + n->val_type = vt; + + return true; + } + + n = (cmp < 0) ? n->left : n->right; + } while (!dnode_is_empty(n)); + + n = dnode_create(kn, p, 1); + + if (cmp < 0) p->left = n; + else p->right = n; + + if (!dnode_is_root(p)) + dnode_fixup(r, n); + + } else n = *r = dnode_create(kn, dnode_empty, 0); + + n->value = vn; + n->key_type = kt; + n->val_type = vt; + + return false; +} + + +int libcdsb_dict_get(dict_t* x, const void* k, vtype t, void* _, dict_access_callback callback, bool cut) { + + dnode_t *c, **r; + void* key; + int cmp; + + if (x->capacity) { + c = *(r = &x->nodes[vnode_hash(k, t) / x->capacity]); + + while (!dnode_is_empty(c)) { + key = vnode_peek(&c->key, c->key_type); + cmp = vtype_compare(k, t, key, c->key_type); + + if (cmp == 0) { + cmp = (callback) ? callback(key, t, vnode_peek(&c->value, c->val_type), c->val_type, _) : 0; + + if (cut) { + c = dnode_delete(r, c); + dnode_free(c, nullptr); + free(c); + } + + return cmp; + } else c = (cmp < 0) ? c->left : c->right; + } + } + + return -1; +} + + +int libcdsb_dict_foreach(dict_t* x, void* dt, dict_access_callback callback, bool flush) { + stack_t z; + int r; + dnode_t* c; + + r = 0; + z.prev = 0; + z.value = 0; + + for (size_t i = 0; i < x->capacity; ++i) { + if (!dnode_is_empty(x->nodes[i])) + stack_push(&z, x->nodes[i]); + } + + while ((c = stack_pop(&z))) { + if ((r = callback(vnode_peek(&c->key, c->key_type), c->key_type, vnode_peek(&c->value, c->val_type), c->val_type, dt))) + break; + + if (!dnode_is_empty(c->right)) stack_push(&z, c->right); + if (!dnode_is_empty(c->left)) stack_push(&z, c->left); + + if (flush) { + dnode_free(c, nullptr); + free(c); + } + } + + if (flush) { + while (c) { + if (!dnode_is_empty(c->right)) stack_push(&z, c->right); + if (!dnode_is_empty(c->left)) stack_push(&z, c->left); + + dnode_free(c, nullptr); + free(c); + + c = stack_pop(&z); + } + + free(x->nodes); + memset(x, 0, sizeof(*x)); + } else stack_flush(&z); + + return r; +} diff --git a/src/dict/include.h b/src/dict/include.h new file mode 100644 index 0000000..9a10f6f --- /dev/null +++ b/src/dict/include.h @@ -0,0 +1,45 @@ +/* This software is licensed by the MIT License, see LICENSE file */ +/* Copyright © 2022 Gregory Lirent */ + +#include "../../include/extra/dict.h" +#include "../__internal/assert.h" +#include "../__internal/rbtree.h" + +#ifndef LIBCDSB_SRC_DICT_INCLUDE_H +#define LIBCDSB_SRC_DICT_INCLUDE_H + +#define CAPACITY_BLOCK 100 +#define REBUILD_POINT_MAX 0.65 +#define REBUILD_POINT_MIN 1 + +typedef struct libcdsb_dict_node { + struct libcdsb_dict_node* left; + struct libcdsb_dict_node* right; + struct libcdsb_dict_node* parent; + + vnode_t key; + short colored; + u8_t key_type; + u8_t val_type; + vnode_t value; +} dnode_t; + +static_assert(offsetof(struct libcdsb_rbtree_node, left) == offsetof(struct libcdsb_dict_node, left), "Implementation assert"); +static_assert(offsetof(struct libcdsb_rbtree_node, right) == offsetof(struct libcdsb_dict_node, right), "Implementation assert"); +static_assert(offsetof(struct libcdsb_rbtree_node, parent) == offsetof(struct libcdsb_dict_node, parent), "Implementation assert"); +static_assert(offsetof(struct libcdsb_rbtree_node, colored) == offsetof(struct libcdsb_dict_node, colored), "Implementation assert"); +static_assert(offsetof(struct libcdsb_rbtree_node, value) == offsetof(struct libcdsb_dict_node, key), "Implementation assert"); + +#define dnode_empty ((dnode_t*)LIBCDSB_RBTREE_NODE_EMPTY) +#define dnode_create(k, p, c) ((dnode_t*)libcdsb_rbtree_node_create(k, (rbnode_t*)p, c, sizeof(dnode_t))) +#define dnode_fixup(r, n) libcdsb_rbtree_node_fixup((rbnode_t**)(r), (rbnode_t*)(n)) +#define dnode_delete(r, n) (dnode_t*)libcdsb_rbtree_node_delete((rbnode_t**)(r), (rbnode_t*)(n)) + +#define dnode_is_empty(n) ((n) == dnode_empty) +#define dnode_is_root(n) dnode_is_empty((n)->parent) + +extern void libcdcb_dnode_free(dnode_t* x, void* not_used); + +#define dnode_free libcdcb_dnode_free + +#endif /* LIBCDSB_SRC_MAP_INCLUDE_H */