/* 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; }