summaryrefslogtreecommitdiff
path: root/plugins/static/hashtable.h
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/static/hashtable.h')
-rw-r--r--plugins/static/hashtable.h113
1 files changed, 113 insertions, 0 deletions
diff --git a/plugins/static/hashtable.h b/plugins/static/hashtable.h
new file mode 100644
index 0000000..670eac6
--- /dev/null
+++ b/plugins/static/hashtable.h
@@ -0,0 +1,113 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "memory.h"
+
+
+inline size_t string_hash_func(const char *key) {
+ size_t h = 0;
+ for (; *key; key++) h = ((h << 9) | (h >> (64 - 9))) ^ *key;
+ return h;
+}
+
+// API:
+// table prefix_make()
+// void prefix_destroy(table)
+// value* prefix_find(table*, const char *key)
+// void prefix_insert(table*, const char *key, value)
+// bool prefix_erase(table*, const char *key)
+#define HASHTABLE_DEFINE(prefix_, value_t_) \
+ struct prefix_ ## internal_table_entry { \
+ char *key; \
+ value_t_ value; \
+ }; \
+ \
+ struct prefix_ ## table { \
+ size_t size, load; /* size is always a power of 2 */ \
+ struct prefix_ ## internal_table_entry *values; \
+ }; \
+ \
+ struct prefix_ ## table prefix_ ## make(void) { \
+ struct prefix_ ## table ht; \
+ ht.size = 8; \
+ ht.load = 0; \
+ ht.values = malloc(ht.size, struct prefix_ ## internal_table_entry); \
+ for (size_t i = 0; i < ht.size; i++) ht.values[i].key = NULL; \
+ return ht; \
+ } \
+ \
+ void prefix_ ## destroy(struct prefix_ ## table ht) { \
+ for (size_t i = 0; i < ht.size; i++) free(ht.values[i].key); \
+ free(ht.values); \
+ } \
+ \
+ size_t prefix_ ## internal_probe_index(const struct prefix_ ## table *ht, const char *key) { \
+ size_t h = string_hash_func(key) % ht->size; \
+ for (size_t i = 0; i < ht->size; i++) { \
+ if (!ht->values[h].key || strcmp(ht->values[h].key, key) == 0) return h; \
+ h = (h + i) % ht->size; /* quadratic probing with index h + 1/2 i + 1/2 i^2 */ \
+ } \
+ assert(false); /* cannot happen unless the table is full, which we don't allow */ \
+ } \
+ \
+ void prefix_ ## internal_grow_table(struct prefix_ ## table *ht); \
+ \
+ /* Returns pointer to value in table */ \
+ value_t_* prefix_ ## find(struct prefix_ ## table *ht, const char *key) { \
+ size_t idx = prefix_ ## internal_probe_index(ht, key); \
+ if (ht->values[idx].key) return &ht->values[idx].value; \
+ return NULL; \
+ } \
+ \
+ /* Overwrites the existing value if already present */ \
+ void prefix_ ## insert_nostrdup(struct prefix_ ## table *ht, char *key, value_t_ value) { \
+ fprintf(stderr, "HT insert nostrdup load=%zu size=%zu\n", ht->load, ht->size); \
+ if (ht->load * 4 >= ht->size * 3) prefix_ ## internal_grow_table(ht); \
+ size_t idx = prefix_ ## internal_probe_index(ht, key); \
+ if (!ht->values[idx].key) { \
+ ht->values[idx].key = key; \
+ ht->load++; \
+ } \
+ ht->values[idx].value = value; \
+ } \
+ \
+ /* Overwrites the existing value if already present */ \
+ void prefix_ ## insert(struct prefix_ ## table *ht, const char *key, value_t_ value) { \
+ fprintf(stderr, "HT insert load=%zu size=%zu\n", ht->load, ht->size); \
+ if (ht->load * 4 >= ht->size * 3) prefix_ ## internal_grow_table(ht); \
+ size_t idx = prefix_ ## internal_probe_index(ht, key); \
+ if (!ht->values[idx].key) { \
+ ht->values[idx].key = strdup(key); \
+ ht->load++; \
+ } \
+ ht->values[idx].value = value; \
+ } \
+ \
+ /* Returns whether the element was indeed present */ \
+ bool prefix_ ## erase(struct prefix_ ## table *ht, const char *key) { \
+ size_t idx = prefix_ ## internal_probe_index(ht, key); \
+ if (!ht->values[idx].key) return false; \
+ free(ht->values[idx].key); \
+ ht->values[idx].key = NULL; \
+ ht->load--; \
+ fprintf(stderr, "HT erase, afterwards load=%zu size=%zu\n", ht->load, ht->size); \
+ return true; \
+ } \
+ \
+ void prefix_ ## internal_grow_table(struct prefix_ ## table *ht) { \
+ fprintf(stderr, "HT grow load=%zu size=%zu\n", ht->load, ht->size); \
+ assert(ht->size * 2 != 0); \
+ struct prefix_ ## table ht2; \
+ ht2.size = ht->size * 2; \
+ ht2.load = 0; \
+ ht2.values = malloc(ht2.size, struct prefix_ ## internal_table_entry); \
+ for (size_t i = 0; i < ht2.size; i++) ht2.values[i].key = NULL; \
+ for (size_t i = 0; i < ht->size; i++) { \
+ if (ht->values[i].key) prefix_ ## insert_nostrdup(&ht2, ht->values[i].key, ht->values[i].value); \
+ } \
+ free(ht->values); \
+ *ht = ht2; \
+ }