89 lines
1.9 KiB
C
89 lines
1.9 KiB
C
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
|
|
#include "hash.h"
|
|
|
|
#define HASH_CHECK_DUP
|
|
|
|
static size_t shash_hash(const void *_s) {
|
|
const char *s = _s;
|
|
size_t hash = 5381;
|
|
int c;
|
|
|
|
while ((c = *s++)) {
|
|
hash = ((hash << 5) + hash) + c;
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
static struct hash_pair *shash_pair_new(void *key, void *value) {
|
|
// Must fit into 128-sized block
|
|
assert(strlen(key) < 128 - sizeof(struct hash_pair));
|
|
struct hash_pair *res;
|
|
|
|
res = calloc(sizeof(struct hash_pair), 1);
|
|
if (!res) {
|
|
return NULL;
|
|
}
|
|
|
|
res->key = (void *) res + sizeof(struct hash_pair);
|
|
res->value = value;
|
|
strcpy((void *) res + sizeof(struct hash_pair), key);
|
|
list_head_init(&res->link);
|
|
|
|
return res;
|
|
}
|
|
|
|
static void shash_pair_free(struct hash_pair *pair) {
|
|
free(pair);
|
|
}
|
|
|
|
int shash_init(struct hash *h, size_t cap) {
|
|
h->hash = shash_hash;
|
|
h->pair_new = shash_pair_new;
|
|
h->pair_free = shash_pair_free;
|
|
h->keycmp = (int (*) (const void *, const void *)) strcmp;
|
|
|
|
h->bucket_count = cap;
|
|
h->buckets = malloc(sizeof(struct list_head) * cap);
|
|
|
|
if (!h->buckets) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
for (size_t i = 0; i < cap; ++i) {
|
|
list_head_init(&h->buckets[i]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hash_insert(struct hash *h, const void *key, void *value) {
|
|
#if defined(HASH_CHECK_DUP)
|
|
assert(!hash_lookup(h, key));
|
|
#endif
|
|
struct hash_pair *pair = h->pair_new((void *) key, value);
|
|
if (!pair) {
|
|
return -ENOMEM;
|
|
}
|
|
size_t index = h->hash(key) % h->bucket_count;
|
|
list_add(&pair->link, &h->buckets[index]);
|
|
return 0;
|
|
}
|
|
|
|
struct hash_pair *hash_lookup(struct hash *h, const void *key) {
|
|
size_t index = h->hash(key) % h->bucket_count;
|
|
struct hash_pair *pair;
|
|
|
|
list_for_each_entry(pair, &h->buckets[index], link) {
|
|
if (!h->keycmp(pair->key, key)) {
|
|
return pair;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|