#include #include #include "hashtable.h" struct _htbl { int (*eq)(const void *, const void *); uint32_t (*hash)(const void *); struct _any **buckets; size_t buckcnt; size_t size; size_t keyoff; size_t chainoff; float ratio; }; #define KEYP(tbl, o) ((void *)(o) + (tbl)->keyoff) #define CHAINP(tbl, o) *(struct _any **)((void *)(o) + (tbl)->chainoff) struct _htbl * _htbl_create(float ratio, int (*eq)(const void *, const void *), uint32_t (*hash)(const void *), size_t keyoff, size_t chainoff) { struct _htbl *tbl = calloc(1, sizeof(*tbl)); if (tbl == NULL) return NULL; tbl->size = 0; tbl->buckcnt = 1; tbl->ratio = ratio; tbl->keyoff = keyoff; tbl->chainoff = chainoff; tbl->hash = hash; tbl->eq = eq; tbl->buckets = calloc(tbl->buckcnt, sizeof(*tbl->buckets)); if (tbl->buckets == NULL) { free(tbl); return NULL; } return tbl; } void _htbl_free(struct _htbl *tbl) { free(tbl->buckets); free(tbl); } static struct _any * bucket_rev(struct _htbl *tbl, struct _any *p, struct _any *n) { while (n != NULL) { struct _any *s = CHAINP(tbl, n); CHAINP(tbl, n) = p; p = n; n = s; } return p; } static void bucket_insert(struct _htbl *tbl, struct _any *o) { size_t bi = tbl->hash(KEYP(tbl, o)) % tbl->buckcnt; CHAINP(tbl, o) = tbl->buckets[bi]; tbl->buckets[bi] = o; } static void _htbl_grow(struct _htbl *tbl) { size_t ncnt = 2 * tbl->buckcnt + 1; size_t ocnt = tbl->buckcnt; struct _any **obuckets = tbl->buckets; struct _any **nbuckets = calloc(ncnt, sizeof(*nbuckets)); if (nbuckets == NULL) return; tbl->buckcnt = ncnt; tbl->buckets = nbuckets; for (size_t i = 0; i < ocnt; i++) { struct _any *o = bucket_rev(tbl, NULL, obuckets[i]); while (o != NULL) { struct _any *s = CHAINP(tbl, o); bucket_insert(tbl, o); o = s; } } free(obuckets); } void _htbl_insert(struct _htbl *tbl, struct _any *o) { bucket_insert(tbl, o); tbl->size++; if (tbl->size > tbl->buckcnt * tbl->ratio) _htbl_grow(tbl); } struct _any * _htbl_find(struct _htbl *tbl, const void *key) { struct _any *ret; size_t bi = tbl->hash(key) % tbl->buckcnt; for (ret = tbl->buckets[bi]; ret != NULL; ret = CHAINP(tbl, ret)) if (tbl->eq(KEYP(tbl, ret), key)) return ret; return NULL; } struct _any * _htbl_remove(struct _htbl *tbl, const void *key) { size_t bi = tbl->hash(key) % tbl->buckcnt; struct _any **p = &tbl->buckets[bi], *o = tbl->buckets[bi]; while (o != NULL && !tbl->eq(KEYP(tbl, o), key)) { p = &CHAINP(tbl, o); o = *p; } if (o != NULL) { *p = CHAINP(tbl, o); tbl->size--; } return o; } void _htbl_fillv(struct _htbl *tbl, struct _any **v) { size_t vi = 0; size_t bi = 0; struct _any *o = tbl->buckets[bi]; while (vi < tbl->size) { while (o == NULL) { bi++; o = tbl->buckets[bi]; } v[vi] = o; vi++; o = CHAINP(tbl, o); } } struct _any ** _htbl_tov(struct _htbl *tbl) { struct _any **v = malloc(sizeof(*v)); if (v != NULL) _htbl_fillv(tbl, v); return v; } size_t _htbl_size(struct _htbl *tbl) { return tbl->size; } static void iter_next_bucket(struct htbl_iter *it) { while (it->tbl->buckets[it->bi] == NULL) it->bi++; it->obj = it->tbl->buckets[it->bi]; it->ptr = &it->tbl->buckets[it->bi]; } struct _any * _htbl_iter_first(struct _htbl *tbl, struct htbl_iter *it) { if (tbl->size == 0) return NULL; it->tbl = tbl; it->cnt = 1; it->bi = 0; iter_next_bucket(it); return it->obj; } struct _any * _htbl_iter_next(struct htbl_iter *it) { struct _any *tmp; if (it->cnt == it->tbl->size) return NULL; it->cnt++; if ((tmp = CHAINP(it->tbl, it->obj)) != NULL) { it->ptr = &CHAINP(it->tbl, it->obj); it->obj = tmp; } else { it->bi++; iter_next_bucket(it); } return it->obj; } #include struct _any * _htbl_iter_del(struct htbl_iter *it) { struct _any *tmp; it->tbl->size--; if (it->cnt > it->tbl->size) { *it->ptr = NULL; return NULL; } if ((tmp = CHAINP(it->tbl, it->obj)) != NULL) { *it->ptr = tmp; it->obj = tmp; } else { *it->ptr = NULL; it->bi++; iter_next_bucket(it); } return it->obj; }