libhmap-c

C hashmap library
git clone https://git.sinitax.com/sinitax/libhmap-c
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

hmap.c (5677B)


      1#include "hmap.h"
      2
      3#include <errno.h>
      4#include <stdbool.h>
      5#include <string.h>
      6
      7static inline size_t
      8hmap_key_bucket(const struct hmap *map, struct hmap_key key)
      9{
     10	return map->hash(key) % map->bucketcnt;
     11}
     12
     13int
     14hmap_init(struct hmap *map, size_t size, hmap_hash_func hasher,
     15	hmap_keycmp_func keycmp, const struct allocator *allocator)
     16{
     17	int rc;
     18
     19	LIBHMAP_ABORT_ON_ARGS(!size || !hasher || !keycmp || !allocator);
     20
     21	map->allocator = allocator;
     22	map->keycmp = keycmp;
     23	map->bucketcnt = size;
     24	map->hash = hasher;
     25	map->buckets = map->allocator->alloc(map->allocator,
     26		sizeof(void *) * size, &rc);
     27	if (!map->buckets) return -rc;
     28	memset(map->buckets, 0, size * sizeof(void *));
     29
     30	return 0;
     31}
     32
     33int
     34hmap_deinit(struct hmap *map)
     35{
     36	LIBHMAP_ABORT_ON_ARGS(!map);
     37
     38	hmap_clear(map);
     39
     40	return map->allocator->free(map->allocator, map->buckets);
     41}
     42
     43struct hmap *
     44hmap_alloc(size_t size, hmap_hash_func hasher,
     45	hmap_keycmp_func keycmp, const struct allocator *allocator, int *_rc)
     46{
     47	struct hmap *map;
     48	int *rc, stub;
     49
     50	LIBHMAP_ABORT_ON_ARGS(!allocator);
     51
     52	rc = _rc ? _rc : &stub;
     53
     54	map = allocator->alloc(allocator, sizeof(struct hmap), rc);
     55	if (!map && _rc) *rc = -*rc;
     56	if (!map) return NULL;
     57
     58	*rc = hmap_init(map, size, hasher, keycmp, allocator);
     59	if (*rc) {
     60		allocator->free(allocator, map);
     61		return NULL;
     62	}
     63
     64	return map;
     65}
     66
     67int
     68hmap_free(struct hmap *map)
     69{
     70	const struct allocator *allocator;
     71	int rc;
     72
     73	LIBHMAP_ABORT_ON_ARGS(!map);
     74
     75	allocator = map->allocator;
     76	rc = hmap_deinit(map);
     77	if (rc) return rc;
     78
     79	rc = allocator->free(allocator, map);
     80	if (rc) return -rc;
     81
     82	return 0;
     83}
     84
     85int
     86hmap_copy(const struct hmap *dst, const struct hmap *src)
     87{
     88	struct hmap_iter iter;
     89	int rc;
     90
     91	LIBHMAP_ABORT_ON_ARGS(!dst || !src);
     92
     93	for (HMAP_ITER(src, iter)) {
     94		rc = hmap_set(dst, iter.link->key, iter.link->value);
     95		if (rc) return rc;
     96	}
     97
     98	return 0;
     99}
    100
    101void
    102hmap_swap(struct hmap *m1, struct hmap *m2)
    103{
    104	struct hmap tmp;
    105
    106	LIBHMAP_ABORT_ON_ARGS(!m1 || !m2);
    107
    108	memcpy(&tmp, m1, sizeof(struct hmap));
    109	memcpy(m1, m2, sizeof(struct hmap));
    110	memcpy(m2, &tmp, sizeof(struct hmap));
    111}
    112
    113void
    114hmap_clear(const struct hmap *map)
    115{
    116	struct hmap_iter iter;
    117	struct hmap_link *prev;
    118	size_t i;
    119
    120	LIBHMAP_ABORT_ON_ARGS(!map);
    121
    122	prev = NULL;
    123	for (HMAP_ITER(map, iter)) {
    124		map->allocator->free(map->allocator, prev);
    125		prev = iter.link;
    126	}
    127	map->allocator->free(map->allocator, prev);
    128
    129	for (i = 0; i < map->bucketcnt; i++)
    130		map->buckets[i] = NULL;
    131}
    132
    133struct hmap_link **
    134hmap_link_get(const struct hmap *map, struct hmap_key key)
    135{
    136	struct hmap_link **iter, *link;
    137
    138	LIBHMAP_ABORT_ON_ARGS(!map);
    139
    140	iter = &map->buckets[hmap_key_bucket(map, key)];
    141	while (*iter != NULL) {
    142		link = *iter;
    143		if (map->keycmp(link->key, key))
    144			return iter;
    145		iter = &(*iter)->next;
    146	}
    147
    148	return NULL;
    149}
    150
    151struct hmap_link **
    152hmap_link_pos(const struct hmap *map, struct hmap_key key)
    153{
    154	struct hmap_link **iter, *link;
    155
    156	LIBHMAP_ABORT_ON_ARGS(!map);
    157
    158	iter = &map->buckets[hmap_key_bucket(map, key)];
    159	while (*iter != NULL) {
    160		link = *iter;
    161		if (map->keycmp(link->key, key))
    162			return iter;
    163		iter = &(*iter)->next;
    164	}
    165
    166	return iter;
    167}
    168
    169struct hmap_link *
    170hmap_link_pop(const struct hmap *map, struct hmap_link **link)
    171{
    172	struct hmap_link *tmp;
    173
    174	LIBHMAP_ABORT_ON_ARGS(!map || !link);
    175
    176	tmp = *link;
    177	*link = (*link)->next;
    178
    179	return tmp;
    180}
    181
    182struct hmap_link *
    183hmap_link_alloc(const struct hmap *map,
    184	struct hmap_key key, struct hmap_val value, int *rc)
    185{
    186	struct hmap_link *link;
    187
    188	LIBHMAP_ABORT_ON_ARGS(!map);
    189
    190	link = map->allocator->alloc(map->allocator,
    191		sizeof(struct hmap_link), rc);
    192	if (!link && rc) *rc = -*rc;
    193	if (!link) return NULL;
    194
    195	link->key = key;
    196	link->value = value;
    197	link->next = NULL;
    198
    199	return link;
    200}
    201
    202struct hmap_link *
    203hmap_get(const struct hmap *map, struct hmap_key key)
    204{
    205	struct hmap_link **iter;
    206
    207	LIBHMAP_ABORT_ON_ARGS(!map);
    208
    209	iter = hmap_link_get(map, key);
    210
    211	return iter ? *iter : NULL;
    212}
    213
    214int
    215hmap_set(const struct hmap *map, struct hmap_key key, struct hmap_val value)
    216{
    217	struct hmap_link **iter;
    218
    219	LIBHMAP_ABORT_ON_ARGS(!map);
    220
    221	iter = hmap_link_pos(map, key);
    222	if (!*iter) return HMAP_KEY_MISSING;
    223
    224	(*iter)->value = value;
    225
    226	return 0;
    227}
    228
    229int
    230hmap_rm(const struct hmap *map, struct hmap_key key)
    231{
    232	struct hmap_link *link, **linkp;
    233	int rc;
    234
    235	LIBHMAP_ABORT_ON_ARGS(!map);
    236
    237	linkp = hmap_link_get(map, key);
    238	if (!linkp) return HMAP_KEY_MISSING;
    239	link = hmap_link_pop(map, linkp);
    240
    241	rc = map->allocator->free(map->allocator, link);
    242	if (rc) return -rc;
    243
    244	return 0;
    245}
    246
    247int
    248hmap_add(const struct hmap *map, struct hmap_key key, struct hmap_val value)
    249{
    250	struct hmap_link **iter;
    251	int rc;
    252
    253	LIBHMAP_ABORT_ON_ARGS(!map);
    254
    255	iter = hmap_link_pos(map, key);
    256	if (*iter) return HMAP_KEY_EXISTS;
    257
    258	*iter = hmap_link_alloc(map, key, value, &rc);
    259	if (!*iter) return rc;
    260
    261	return 0;
    262}
    263
    264void
    265hmap_iter_init(struct hmap_iter *iter)
    266{
    267	LIBHMAP_ABORT_ON_ARGS(!iter);
    268
    269	iter->bucket = 0;
    270	iter->link = NULL;
    271}
    272
    273bool
    274hmap_iter_next(const struct hmap *map, struct hmap_iter *iter)
    275{
    276	size_t i;
    277
    278	LIBHMAP_ABORT_ON_ARGS(!map || !iter);
    279
    280	if (iter->link && iter->link->next) {
    281		iter->link = iter->link->next;
    282		return true;
    283	}
    284
    285	i = iter->bucket + (iter->link ? 1 : 0);
    286	for (; i < map->bucketcnt; i++) {
    287		if (map->buckets[i]) {
    288			iter->bucket = i;
    289			iter->link = map->buckets[i];
    290			return true;
    291		}
    292	}
    293
    294	iter->link = NULL;
    295
    296	return false;
    297}
    298
    299uint32_t
    300hmap_str_hash(struct hmap_key key)
    301{
    302	const char *c;
    303	uint32_t hash;
    304
    305	LIBHMAP_ABORT_ON_ARGS(!key.p);
    306
    307	hash = 5381;
    308	for (c = key.p; *c; c++)
    309		hash = 33 * hash + (uint8_t) *c;
    310
    311	return hash;
    312}
    313
    314bool
    315hmap_str_keycmp(struct hmap_key k1, struct hmap_key k2)
    316{
    317	LIBHMAP_ABORT_ON_ARGS(!k1.p || !k2.p);
    318
    319	return !strcmp(k1.p, k2.p);
    320}