From 6dd9ae94bd8836a86ceb976da169e804ca9df8e6 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Thu, 7 Mar 2019 20:02:14 +0100 Subject: [PATCH] Add simple hash table implementation Signed-off-by: David Oberhollenzer --- include/util/hashtable.h | 27 +++++++ lib/Makemodule.am | 1 + lib/util/hashtable.c | 147 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 include/util/hashtable.h create mode 100644 lib/util/hashtable.c diff --git a/include/util/hashtable.h b/include/util/hashtable.h new file mode 100644 index 0000000..401e228 --- /dev/null +++ b/include/util/hashtable.h @@ -0,0 +1,27 @@ +#ifndef HASH_TABLE_H +#define HASH_TABLE_H + +typedef struct hash_bucket_t { + struct hash_bucket_t *next; + char *key; + void *value; +} hash_bucket_t; + +typedef struct { + hash_bucket_t **buckets; + size_t num_buckets; + size_t count; +} hash_table_t; + +int hash_table_init(hash_table_t *table, size_t size); + +void hash_table_cleanup(hash_table_t *table); + +void *hash_table_lookup(hash_table_t *table, const char *key); + +int hash_table_set(hash_table_t *table, const char *key, void *value); + +void hash_table_foreach(hash_table_t *table, void *usr, + int(*fun)(void *usr, const char *key, void *value)); + +#endif /* HASH_TABLE_H */ diff --git a/lib/Makemodule.am b/lib/Makemodule.am index 4a7d667..0fc3d08 100644 --- a/lib/Makemodule.am +++ b/lib/Makemodule.am @@ -2,6 +2,7 @@ libutil_a_SOURCES = lib/util/input_file.c lib/util/mkdir_p.c libutil_a_SOURCES += lib/util/write_retry.c lib/util/read_retry.c libutil_a_SOURCES += lib/util/canonicalize_name.c libutil_a_SOURCES += include/util/util.h include/util/input_file.h +libutil_a_SOURCES += include/util/hashtable.h lib/util/hashtable.c libfilelist_a_SOURCES = lib/filelist/dump_toc.c lib/filelist/image_entry.c libfilelist_a_SOURCES += lib/filelist/image_entry_sort.c diff --git a/lib/util/hashtable.c b/lib/util/hashtable.c new file mode 100644 index 0000000..d5903c9 --- /dev/null +++ b/lib/util/hashtable.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include + +#include "util/hashtable.h" + +/* R5 hash function (borrowed from reiserfs) */ +static uint32_t strhash(const char *s) +{ + const signed char *str = (const signed char *)s; + uint32_t a = 0; + + while (*str != '\0') { + a += *str << 4; + a += *str >> 4; + a *= 11; + str++; + } + + return a; +} + +int hash_table_init(hash_table_t *table, size_t size) +{ + table->num_buckets = size; + table->count = 0; + table->buckets = calloc(size, sizeof(table->buckets[0])); + + if (table->buckets == NULL) { + fputs("out of memory\n", stderr); + return -1; + } + + return 0; +} + +void hash_table_cleanup(hash_table_t *table) +{ + hash_bucket_t *bucket; + size_t i; + + for (i = 0; i < table->num_buckets; ++i) { + while (table->buckets[i] != NULL) { + bucket = table->buckets[i]; + table->buckets[i] = bucket->next; + + free(bucket->key); + free(bucket); + } + } + + free(table->buckets); + + table->buckets = NULL; + table->num_buckets = 0; + table->count = 0; +} + +void *hash_table_lookup(hash_table_t *table, const char *key) +{ + hash_bucket_t *bucket; + uint32_t hash; + + hash = strhash(key); + bucket = table->buckets[hash % table->num_buckets]; + + while (bucket != NULL) { + if (strcmp(bucket->key, key) == 0) + return bucket->value; + + bucket = bucket->next; + } + + return NULL; +} + +int hash_table_set(hash_table_t *table, const char *key, void *value) +{ + hash_bucket_t *bucket; + uint32_t hash; + size_t index; + + hash = strhash(key); + index = hash % table->num_buckets; + bucket = table->buckets[index]; + + while (bucket != NULL) { + if (strcmp(bucket->key, key) == 0) { + bucket->value = value; + return 0; + } + + bucket = bucket->next; + } + + bucket = calloc(1, sizeof(*bucket)); + if (bucket == NULL) + goto fail_oom; + + bucket->key = strdup(key); + if (bucket->key == NULL) + goto fail_oom; + + bucket->value = value; + bucket->next = table->buckets[index]; + + table->buckets[index] = bucket; + table->count += 1; + return 0; +fail_oom: + free(bucket); + fputs("out of memory\n", stderr); + return -1; +} + +void hash_table_foreach(hash_table_t *table, void *usr, + int(*fun)(void *usr, const char *key, void *value)) +{ + hash_bucket_t *bucket, *prev; + size_t i; + + for (i = 0; i < table->num_buckets; ++i) { + prev = NULL; + bucket = table->buckets[i]; + + while (bucket != NULL) { + if (fun(usr, bucket->key, bucket->value)) { + if (prev == NULL) { + table->buckets[i] = bucket->next; + free(bucket->key); + free(bucket); + bucket = table->buckets[i]; + } else { + prev->next = bucket->next; + free(bucket->key); + free(bucket); + bucket = prev->next; + } + table->count -= 1; + } else { + prev = bucket; + bucket = bucket->next; + } + } + } +}