2022-08-14 21:06:38 +03:00
|
|
|
/* 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) {
|
2022-08-15 17:31:01 +03:00
|
|
|
stack_t z;
|
|
|
|
int cmp;
|
|
|
|
size_t index;
|
|
|
|
dnode_t *c, *p, *n;
|
2022-08-14 21:06:38 +03:00
|
|
|
|
2022-08-15 17:31:01 +03:00
|
|
|
dnode_t **nodes = calloc(capacity, sizeof(*nodes));
|
2022-08-14 21:06:38 +03:00
|
|
|
|
2022-08-15 17:31:01 +03:00
|
|
|
z.prev = 0;
|
|
|
|
z.value = 0;
|
|
|
|
index = s->capacity;
|
|
|
|
|
|
|
|
while (index--) {
|
|
|
|
if (!dnode_is_empty(s->nodes[index]))
|
|
|
|
stack_push(&z, s->nodes[index]);
|
2022-08-14 21:06:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-08-15 17:31:01 +03:00
|
|
|
index = vnode_hash(&c->key, c->key_type) % capacity;
|
|
|
|
n = nodes[index];
|
2022-08-14 21:06:38 +03:00
|
|
|
|
2022-08-15 17:31:01 +03:00
|
|
|
if (!is_null(nodes[index])) {
|
2022-08-14 21:06:38 +03:00
|
|
|
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))
|
2022-08-15 17:31:01 +03:00
|
|
|
dnode_fixup(nodes + index, n);
|
2022-08-14 21:06:38 +03:00
|
|
|
|
|
|
|
} else {
|
2022-08-15 17:31:01 +03:00
|
|
|
nodes[index] = c;
|
|
|
|
c->colored = 0;
|
|
|
|
c->parent = dnode_empty;
|
2022-08-14 21:06:38 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(s->nodes);
|
|
|
|
|
|
|
|
s->nodes = nodes;
|
2022-08-15 17:31:01 +03:00
|
|
|
|
|
|
|
if (capacity > s->capacity) {
|
|
|
|
s->capacity = capacity;
|
|
|
|
while (capacity--) {
|
|
|
|
if (is_null(*nodes))
|
|
|
|
*nodes = dnode_empty;
|
|
|
|
++nodes;
|
|
|
|
}
|
|
|
|
} else s->capacity = capacity;
|
2022-08-14 21:06:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*#####################################################################################################################*/
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
2022-08-15 17:31:01 +03:00
|
|
|
dnode_t *n, *p;
|
2022-08-14 21:06:38 +03:00
|
|
|
vnode_t kn, vn;
|
2022-08-15 17:31:01 +03:00
|
|
|
int cmp;
|
|
|
|
size_t index;
|
2022-08-14 21:06:38 +03:00
|
|
|
|
2022-08-15 17:31:01 +03:00
|
|
|
if (!x->capacity || (double)x->size / x->capacity > REBUILD_POINT_MAX)
|
2022-08-14 21:06:38 +03:00
|
|
|
dict_rehash(x, x->capacity + CAPACITY_BLOCK);
|
|
|
|
|
2022-08-15 17:31:01 +03:00
|
|
|
index = vtype_hash(k, kt) % x->capacity;
|
|
|
|
n = x->nodes[index];
|
|
|
|
kn = vnode_create(k, kt);
|
|
|
|
vn = vnode_create(v, vt);
|
2022-08-14 21:06:38 +03:00
|
|
|
|
|
|
|
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))
|
2022-08-15 17:31:01 +03:00
|
|
|
dnode_fixup(x->nodes + index, n);
|
2022-08-14 21:06:38 +03:00
|
|
|
|
2022-08-15 17:31:01 +03:00
|
|
|
} else x->nodes[index] = n = dnode_create(kn, dnode_empty, 0);
|
2022-08-14 21:06:38 +03:00
|
|
|
|
|
|
|
n->value = vn;
|
|
|
|
n->key_type = kt;
|
|
|
|
n->val_type = vt;
|
|
|
|
|
2022-08-15 17:31:01 +03:00
|
|
|
++x->size;
|
|
|
|
|
2022-08-14 21:06:38 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-08-17 22:21:01 +03:00
|
|
|
int libcdsb_dict_find(dict_t* x, const void* k, vtype t, void* _, dict_access_callback callback, bool cut) {
|
2022-08-14 21:06:38 +03:00
|
|
|
|
2022-08-15 17:31:01 +03:00
|
|
|
dnode_t *c;
|
|
|
|
void* key;
|
|
|
|
int cmp;
|
|
|
|
size_t index;
|
2022-08-14 21:06:38 +03:00
|
|
|
|
|
|
|
if (x->capacity) {
|
2022-08-15 17:31:01 +03:00
|
|
|
index = vtype_hash(k, t) % x->capacity;
|
|
|
|
c = x->nodes[index];
|
2022-08-14 21:06:38 +03:00
|
|
|
|
|
|
|
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) {
|
2022-08-16 21:10:54 +03:00
|
|
|
cmp = (callback) ? callback(key, c->key_type, vnode_peek(&c->value, c->val_type), c->val_type, _) : 0;
|
2022-08-14 21:06:38 +03:00
|
|
|
|
|
|
|
if (cut) {
|
2022-08-15 17:31:01 +03:00
|
|
|
c = dnode_delete(x->nodes + index, c);
|
2022-08-14 21:06:38 +03:00
|
|
|
dnode_free(c, nullptr);
|
|
|
|
free(c);
|
2022-08-15 17:31:01 +03:00
|
|
|
--x->size;
|
2022-08-14 21:06:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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))) {
|
2022-08-15 17:31:01 +03:00
|
|
|
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)))
|
2022-08-14 21:06:38 +03:00
|
|
|
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;
|
|
|
|
}
|