/* 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; size_t index; dnode_t *c, *p, *n; dnode_t **nodes = calloc(capacity, sizeof(*nodes)); z.prev = 0; z.value = 0; index = s->capacity; while (index--) { if (!dnode_is_empty(s->nodes[index])) stack_push(&z, s->nodes[index]); } 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; } index = vnode_hash(&c->key, c->key_type) % capacity; n = nodes[index]; if (!is_null(nodes[index])) { 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(nodes + index, n); } else { nodes[index] = c; c->colored = 0; c->parent = dnode_empty; } } free(s->nodes); s->nodes = nodes; if (capacity > s->capacity) { s->capacity = capacity; while (capacity--) { if (is_null(*nodes)) *nodes = dnode_empty; ++nodes; } } else s->capacity = capacity; } /*#####################################################################################################################*/ 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 *n, *p; vnode_t kn, vn; int cmp; size_t index; if (!x->capacity || (double)x->size / x->capacity > REBUILD_POINT_MAX) dict_rehash(x, x->capacity + CAPACITY_BLOCK); index = vtype_hash(k, kt) % x->capacity; n = x->nodes[index]; 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(x->nodes + index, n); } else x->nodes[index] = n = dnode_create(kn, dnode_empty, 0); n->value = vn; n->key_type = kt; n->val_type = vt; ++x->size; return false; } int libcdsb_dict_get(dict_t* x, const void* k, vtype t, void* _, dict_access_callback callback, bool cut) { dnode_t *c; void* key; int cmp; size_t index; if (x->capacity) { index = vtype_hash(k, t) % x->capacity; c = x->nodes[index]; 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, c->key_type, vnode_peek(&c->value, c->val_type), c->val_type, _) : 0; if (cut) { c = dnode_delete(x->nodes + index, c); dnode_free(c, nullptr); free(c); --x->size; } 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))) { void* k = vnode_peek(&c->key, c->key_type); void* v = vnode_peek(&c->value, c->val_type); if ((r = callback(k, c->key_type, v, 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; }