17 static const float phi = 1.61803398875f;
18 const float M = phi * k;
19 return (M - floorf(M)) * m;
27static void * __cstl_hash_element(
const struct cstl_hash *
const h,
30 return (
void *)((uintptr_t)n - h->off);
39 const struct cstl_hash *
const h,
const void *
const e)
41 return (
void *)((uintptr_t)e + h->off);
45static struct cstl_hash_bucket * __cstl_hash_get_bucket(
46 struct cstl_hash *
const h,
const size_t k,
49 const size_t i = hash(k, count);
53 return &h->bucket.at[i];
57#define HASH_LIST_FOREACH(HEAD, CURR, NEXT) \
58 NEXT = HEAD; while ((CURR = NEXT) != NULL && (NEXT = (CURR)->next, !0))
59#define HASH_LIST_INSERT(HEAD, N) \
60 (N)->next = HEAD; HEAD = N
64static void cstl_clean_bucket(
65 struct cstl_hash *
const h,
struct cstl_hash_bucket *
const bk)
68 if (h->bucket.cst != bk->cst) {
84 HASH_LIST_FOREACH(n, n, nn) {
85 struct cstl_hash_bucket *
const _bk =
86 __cstl_hash_get_bucket(
87 h, n->key, h->bucket.rh.hash, h->bucket.rh.count);
88 HASH_LIST_INSERT(_bk->n, n);
92 bk->cst = h->bucket.cst;
97static void __cstl_hash_rehash(
struct cstl_hash *
const h,
size_t n)
100 while (h->bucket.rh.clean < h->bucket.count
101 && h->bucket.at[h->bucket.rh.clean].cst == h->bucket.cst) {
102 h->bucket.rh.clean++;
105 while (h->bucket.rh.clean < h->bucket.count && n > 0) {
106 cstl_clean_bucket(h, &h->bucket.at[h->bucket.rh.clean]);
107 h->bucket.rh.clean++;
111 if (h->bucket.rh.clean >= h->bucket.count) {
113 h->bucket.count = h->bucket.rh.count;
114 h->bucket.hash = h->bucket.rh.hash;
116 h->bucket.rh.hash = NULL;
122 if (h->bucket.rh.hash != NULL) {
123 __cstl_hash_rehash(h, SIZE_MAX);
132static struct cstl_hash_bucket * cstl_hash_get_bucket(
133 struct cstl_hash *
const h,
const size_t k)
135 struct cstl_hash_bucket * bk;
137 bk = __cstl_hash_get_bucket(h, k, h->bucket.hash, h->bucket.count);
144 if (h->bucket.rh.hash != NULL) {
145 struct cstl_hash_bucket *
const _bk =
146 __cstl_hash_get_bucket(
147 h, k, h->bucket.rh.hash, h->bucket.rh.count);
154 cstl_clean_bucket(h, bk);
155 cstl_clean_bucket(h, _bk);
162 __cstl_hash_rehash(h, 1);
175static int cstl_hash_bucket_foreach(
182 HASH_LIST_FOREACH(n, n, nn) {
183 if ((res = visit(__cstl_hash_element(h, n), p)) != 0) {
192static int __cstl_hash_foreach(
const struct cstl_hash *
const h,
198 for (i = 0, res = 0; i < h->bucket.count && res == 0; i++) {
199 res = cstl_hash_bucket_foreach(h, h->bucket.at[i].n, visit, p);
209 return __cstl_hash_foreach(h, visit, p);
212struct cstl_hash_foreach_visit_priv
219static int cstl_hash_foreach_visit(
void *
const e,
void *
const p)
221 struct cstl_hash_foreach_visit_priv *
const hfvp = p;
222 return hfvp->visit(e, hfvp->priv);
237 struct cstl_hash_foreach_visit_priv hfvp;
240 return __cstl_hash_foreach(h, cstl_hash_foreach_visit, &hfvp);
244static void __cstl_hash_set_capacity(
245 struct cstl_hash *
const h,
const size_t sz)
247 struct cstl_hash_bucket *
const at =
248 realloc(h->bucket.at,
sizeof(*at) * sz);
251 h->bucket.capacity = sz;
259 if (count > h->bucket.capacity) {
260 __cstl_hash_set_capacity(h, count);
263 if (h->bucket.at != NULL
264 && count <= h->bucket.capacity
265 && (count != h->bucket.count
267 && hash != h->bucket.hash))) {
276 h->bucket.cst = !h->bucket.cst;
281 for (i = h->bucket.count; i < count; i++) {
282 h->bucket.at[i].n = NULL;
284 h->bucket.at[i].cst = h->bucket.cst;
292 h->bucket.rh.hash = hash;
293 }
else if (h->bucket.hash != NULL) {
294 h->bucket.rh.hash = h->bucket.hash;
298 h->bucket.rh.count = count;
299 h->bucket.rh.clean = 0;
301 if (h->bucket.hash == NULL) {
303 h->bucket.hash = h->bucket.rh.hash;
304 h->bucket.count = h->bucket.rh.count;
306 h->bucket.rh.hash = NULL;
319 count = h->bucket.count;
320 if (h->bucket.rh.hash != NULL) {
321 count = h->bucket.rh.count;
324 if (h->bucket.capacity > count) {
326 __cstl_hash_set_capacity(h, h->bucket.count);
331 const size_t k,
void *
const e)
333 struct cstl_hash_bucket *
const bk = cstl_hash_get_bucket(h, k);
342 HASH_LIST_INSERT(bk->n, hn);
348struct cstl_hash_find_priv
365static int cstl_hash_find_visit(
void *
const e,
void *
const p)
367 struct cstl_hash_find_priv *
const hfp = p;
370 if (__cstl_hash_node(hfp->h, e)->key == hfp->k) {
377 if (hfp->visit == NULL || hfp->visit(e, hfp->p) != 0) {
389 struct cstl_hash_find_priv hfp;
397 cstl_hash_bucket_foreach(
398 h, cstl_hash_get_bucket(h, k)->n, cstl_hash_find_visit, &hfp);
399 return (
void *)hfp.e;
403struct cstl_hash_erase_priv
421static int cstl_hash_erase_visit(
void *
const e,
void *
const p)
423 struct cstl_hash_erase_priv *
const hep = p;
430 hep->n = &(*hep->n)->next;
440 struct cstl_hash_bucket *
const bk =
441 cstl_hash_get_bucket(h, __cstl_hash_node(h, e)->key);
442 struct cstl_hash_erase_priv hep;
451 if (cstl_hash_bucket_foreach(
452 h, bk->n, cstl_hash_erase_visit, &hep) != 0) {
454 *hep.n = (*hep.n)->next;
460struct cstl_hash_clear_priv
467static int cstl_hash_clear_visit(
void *
const e,
void *
const p)
469 struct cstl_hash_clear_priv *
const hcp = p;
470 hcp->clr(e, hcp->priv);
477 struct cstl_hash_clear_priv hcp;
482 __cstl_hash_foreach(h, cstl_hash_clear_visit, &hcp);
489 h->bucket.capacity = 0;
491 h->bucket.rh.hash = NULL;
498#include "internal/check.h"
508void __test__cstl_hash_fill(
struct cstl_hash *
const h,
const size_t n)
512 for (i = 0; i < n; i++) {
513 struct integer * in = malloc(
sizeof(*in));
522static void __test_cstl_hash_free(
void *
const p,
void *
const x)
528static int fill_visit(
const void *
const e,
void *
const p)
537 static const size_t n = 10;
543 __test__cstl_hash_fill(&h, n);
547 ck_assert_uint_eq(c, n);
552static int manual_clear_visit(
void *
const e,
void *
const p)
559START_TEST(manual_clear)
561 static const size_t n = 10;
565 __test__cstl_hash_fill(&h, n);
571static size_t bad_hash_func(
const size_t k,
const size_t m)
590static void test_rehash(
struct cstl_hash *
const h,
591 const size_t maxk,
const size_t count)
596 for (i = 0; i < h->bucket.count; i++) {
597 ck_assert_uint_ne(h->bucket.at[i].cst, h->bucket.cst);
601 while (h->bucket.rh.hash != NULL) {
606 ck_assert_uint_eq(h->bucket.count, count);
609 for (i = 0; i < h->bucket.count; i++) {
610 ck_assert_uint_eq(h->bucket.at[i].cst, h->bucket.cst);
614 for (; i < h->bucket.capacity; i++) {
615 ck_assert_ptr_null(h->bucket.at[i].n);
621 static const size_t n = 100;
622 const int i = rand() % n;
629 __test__cstl_hash_fill(&h, n);
632 ck_assert_ptr_ne(e, NULL);
636 ck_assert_uint_eq(cst, h.bucket.cst);
638 ck_assert_uint_eq(cst, h.bucket.cst);
644 ck_assert_uint_ne(cst, h.bucket.cst);
650 test_rehash(&h, n, h.bucket.rh.count);
655 test_rehash(&h, n, h.bucket.rh.count);
661 ck_assert_ptr_null((
void *)(uintptr_t)h.bucket.rh.hash);
662 ck_assert_uint_eq(h.bucket.count, 12);
663 ck_assert_uint_eq(h.bucket.count, h.bucket.capacity);
675Suite * hash_suite(
void)
677 Suite *
const s = suite_create(
"hash");
681 tc = tcase_create(
"hash");
682 tcase_add_test(tc, fill);
683 tcase_add_test(tc, manual_clear);
684 tcase_add_test(tc, bad_hash);
685 tcase_add_test(tc, resize);
686 suite_add_tcase(s, tc);
void cstl_xtor_func_t(void *obj, void *priv)
Type for functions called to construct, clear, or destroy an object.
int cstl_visit_func_t(void *obj, void *priv)
Type for visit callbacks from objects supporting foreach.
int cstl_const_visit_func_t(const void *obj, void *priv)
Type for const visit callbacks from objects supporting foreach.
void cstl_hash_resize(struct cstl_hash *const h, const size_t count, cstl_hash_func_t *const hash)
Resize the hash table.
size_t cstl_hash_func_t(size_t k, size_t m)
Function type for hashing a key into a bucket.
float cstl_hash_load(const struct cstl_hash *const h)
Get the average number of nodes per bucket.
void cstl_hash_erase(struct cstl_hash *const h, void *const e)
Remove an object from the hash.
int cstl_hash_foreach_const(const struct cstl_hash *const h, cstl_const_visit_func_t *const visit, void *const p)
Visit each object within a hash table.
void * cstl_hash_find(struct cstl_hash *const h, const size_t k, cstl_const_visit_func_t *const visit, void *const p)
Lookup/find a previously inserted object in the hash.
int cstl_hash_foreach(struct cstl_hash *const h, cstl_visit_func_t *const visit, void *const p)
Visit each object within a hash table.
size_t cstl_hash_div(const size_t k, const size_t m)
Hash by division.
void cstl_hash_insert(struct cstl_hash *const h, const size_t k, void *const e)
Insert an item into the hash.
void cstl_hash_shrink_to_fit(struct cstl_hash *const h)
Free memory associated with excess buckets.
size_t cstl_hash_mul(const size_t k, const size_t m)
Hash by multiplication.
size_t cstl_hash_size(const struct cstl_hash *const h)
Get the number of objects in the hash.
#define DECLARE_CSTL_HASH(NAME, TYPE, MEMB)
(Statically) declare and initialize a hash
void cstl_hash_clear(struct cstl_hash *const h, cstl_xtor_func_t *const clr)
Remove all elements from the hash.
void cstl_hash_rehash(struct cstl_hash *const h)
Rehash the hash table.
Node to anchor an element within a hash.