aboutsummaryrefslogtreecommitdiff
path: root/src/common/libkogata/hashtbl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/libkogata/hashtbl.c')
-rw-r--r--src/common/libkogata/hashtbl.c177
1 files changed, 177 insertions, 0 deletions
diff --git a/src/common/libkogata/hashtbl.c b/src/common/libkogata/hashtbl.c
new file mode 100644
index 0000000..b0097cd
--- /dev/null
+++ b/src/common/libkogata/hashtbl.c
@@ -0,0 +1,177 @@
+#include <kogata/debug.h>
+#include <kogata/malloc.h>
+
+#include <kogata/hashtbl.h>
+
+#define DEFAULT_HT_INIT_SIZE 16
+#define SLOT_OF_HASH(k, nslots) (((size_t)(k)*21179)%(size_t)(nslots))
+
+typedef struct hashtbl_item {
+ void* key;
+ void* val;
+ struct hashtbl_item *next;
+} hashtbl_item_t;
+
+// When nitems > size * 3/4, size is multiplied by two
+// When nitems < size * 1/4, size is divided by two
+struct hashtbl {
+ key_eq_fun_t ef;
+ hash_fun_t hf;
+ kv_iter_fun_t releasef;
+ size_t size, nitems;
+ hashtbl_item_t **items;
+};
+
+hashtbl_t *create_hashtbl(key_eq_fun_t ef, hash_fun_t hf, kv_iter_fun_t on_release) {
+ hashtbl_t *ht = (hashtbl_t*)malloc(sizeof(hashtbl_t));
+ if (ht == 0) return 0;
+
+ ht->ef = ef;
+ ht->hf = hf;
+ ht->releasef = on_release;
+
+ ht->size = DEFAULT_HT_INIT_SIZE;
+ ht->nitems = 0;
+
+ ht->items = (hashtbl_item_t**)malloc(ht->size * sizeof(hashtbl_item_t*));
+ if (ht->items == 0) {
+ free(ht);
+ return 0;
+ }
+
+ for (size_t i = 0; i < ht->size; i++) ht->items[i] = 0;
+
+ return ht;
+}
+
+void delete_hashtbl(hashtbl_t *ht) {
+ // Free items
+ for (size_t i = 0; i < ht->size; i++) {
+ while (ht->items[i] != 0) {
+ hashtbl_item_t *x = ht->items[i];
+ ht->items[i] = x->next;
+
+ if (ht->releasef) ht->releasef(x->key, x->val);
+
+ free(x);
+ }
+ }
+
+ // Free table
+ free(ht->items);
+ free(ht);
+}
+
+void hashtbl_check_size(hashtbl_t *ht) {
+ size_t nsize = 0;
+ if (4 * ht->nitems < ht->size) nsize = ht->size / 2;
+ if (4 * ht->nitems > 3 * ht->size) nsize = ht->size * 2;
+
+ if (nsize != 0 && nsize >= DEFAULT_HT_INIT_SIZE) {
+ hashtbl_item_t **nitems = (hashtbl_item_t**)malloc(nsize * sizeof(hashtbl_item_t*));
+ if (nitems == 0) return; // if we can't realloc, too bad, we just lose space/efficienty
+
+ for (size_t i = 0; i < nsize; i++) nitems[i] = 0;
+
+ // rehash
+ for (size_t i = 0; i < ht->size; i++) {
+ while (ht->items[i] != 0) {
+ hashtbl_item_t *x = ht->items[i];
+ ht->items[i] = x->next;
+
+ size_t slot = SLOT_OF_HASH(ht->hf(x->key), nsize);
+ x->next = nitems[slot];
+ nitems[slot] = x;
+ }
+ }
+ free(ht->items);
+ ht->size = nsize;
+ ht->items = nitems;
+ }
+}
+
+bool hashtbl_add(hashtbl_t *ht, void* key, void* v) {
+ hashtbl_item_t *i = (hashtbl_item_t*)malloc(sizeof(hashtbl_item_t));
+ if (i == 0) return false; // OOM
+
+ // make sure item is not already present
+ hashtbl_remove(ht, key);
+
+ size_t slot = SLOT_OF_HASH(ht->hf(key), ht->size);
+
+ i->key = key;
+ i->val = v;
+ i->next = ht->items[slot];
+ ht->items[slot] = i;
+ ht->nitems++;
+
+ hashtbl_check_size(ht);
+
+ return true;
+}
+
+void hashtbl_remove(hashtbl_t* ht, const void* key) {
+ size_t slot = SLOT_OF_HASH(ht->hf(key), ht->size);
+
+ if (ht->items[slot] == 0) return;
+
+ hashtbl_item_t *x = 0;
+
+ if (ht->ef(ht->items[slot]->key, key)) {
+ x = ht->items[slot];
+ ht->items[slot] = x->next;
+ } else {
+ for (hashtbl_item_t *i = ht->items[slot]; i->next != 0; i = i->next) {
+ if (ht->ef(i->next->key, key)) {
+ x = i->next;
+ i->next = x->next;
+ break;
+ }
+ }
+ }
+
+ if (x != 0) {
+ ht->nitems--;
+ if (ht->releasef) ht->releasef(x->key, x->val);
+ free(x);
+ }
+
+ hashtbl_check_size(ht);
+}
+
+void* hashtbl_find(hashtbl_t* ht, const void* key) {
+ size_t slot = SLOT_OF_HASH(ht->hf(key), ht->size);
+
+ for (hashtbl_item_t *i = ht->items[slot]; i != 0; i = i->next) {
+ if (ht->ef(i->key, key)) return i->val;
+ }
+
+ return 0;
+}
+
+void hashtbl_iter(hashtbl_t *ht, kv_iter_fun_t f) {
+ for (size_t s = 0; s < ht->size; s++) {
+ for (hashtbl_item_t *i = ht->items[s]; i != 0; i = i->next) {
+ f(i->key, i->val);
+ }
+ }
+}
+
+size_t hashtbl_count(hashtbl_t* ht) {
+ return ht->nitems;
+}
+
+bool hashtbl_change(hashtbl_t* ht, void* key, void* newval) {
+ size_t slot = SLOT_OF_HASH(ht->hf(key), ht->size);
+
+ for (hashtbl_item_t *i = ht->items[slot]; i != 0; i = i->next) {
+ if (ht->ef(i->key, key)) {
+ i->val = newval;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* vim: set ts=4 sw=4 tw=0 noet :*/