diff --git a/src/testing/main.c b/src/testing/main.c index 24c148b..7c6027b 100644 --- a/src/testing/main.c +++ b/src/testing/main.c @@ -1,11 +1,13 @@ #include "codegen/x86/test_x86.h" #include "lexer/test_lexer.h" #include +#include int main() { test_lexer(); test_x86(); test_list(); + test_util(); return 0; } diff --git a/src/util/hashmap.c b/src/util/hashmap.c new file mode 100644 index 0000000..1cb3e9a --- /dev/null +++ b/src/util/hashmap.c @@ -0,0 +1,174 @@ +#include "hashmap.h" +#include +#include +#include +#include +#include +#include + +unsigned fnva1(char *value) { + unsigned long h = 16777619; + long int prime = 2166136261; + + while (*value != '\0') { + h ^= *value; + h *= prime; + ++value; + } + + return h; +} + +unsigned equal_key(char *a, char *b) { return strcmp(a, b) == 0; } + +struct Hashmap *create_hashmap(int capacity) { + struct Hashmap *h = malloc(sizeof(struct Hashmap)); + + h->buckets = calloc(capacity, sizeof(struct BucketNode *)); + + h->size = 0; + h->cap = capacity; + + h->hash = fnva1; + h->equals = equal_key; + + return h; +} + +void destroy_hashmap(struct Hashmap *h) { + free(h->buckets); + free(h); +} + +struct BucketNode *create_bucket(char *key, void *value) { + struct BucketNode *b = malloc(sizeof(struct BucketNode)); + + b->key = key; + b->value = value; + b->next = NULL; + + return b; +} + +struct BucketNode *hm_get(struct Hashmap *h, char *key) { + unsigned a = h->hash(key) % h->cap; + + struct BucketNode *b = h->buckets[a]; + + if (b == NULL) { + return NULL; + } + + if (h->equals(b->key, key)) { + return b; + } + + // check for linear probing + + return NULL; +} + +int hm_set(struct Hashmap *h, char *key, void *value) { + unsigned a = h->hash(key) % h->cap; + + struct BucketNode *b = h->buckets[a]; + + if (b == NULL) { + if (h->size == h->cap) { + double_cap(h); + } + + h->size++; + + h->buckets[a] = malloc(sizeof(struct BucketNode *)); + h->buckets[a]->key = key; + h->buckets[a]->value = value; + h->buckets[a]->next = NULL; + + return 0; + } else { + // Handle linear probing + return -1; + } +} + +void double_cap(struct Hashmap *h) { + struct BucketNode **new_buckets = + calloc(h->cap * 2, sizeof(struct BucketNode *)); + + for (int i = 0; i < h->cap; i++) { + + if (h->buckets[i] != NULL) { + struct BucketNode *b = h->buckets[i]; + unsigned a = h->hash(b->key) % h->cap; + new_buckets[a] = b; + } + } + + h->buckets = new_buckets; + + h->cap = h->cap * 2; +} + +int test_hash_init() { + testing_func_setup(); + struct Hashmap *h = create_hashmap(100); + + tassert(h->size == 0); + tassert(h->cap == 100); +} + +int test_hash_init_and_store() { + testing_func_setup(); + struct Hashmap *h = create_hashmap(100); + + tassert(h->size == 0); + tassert(h->cap == 100); + + char name[5] = "jake"; + + char key[5] = "test"; + int ret = hm_set(h, key, name); + tassert(ret != -1); + + uint64_t ind = h->hash(key) % h->cap; + struct BucketNode *b = h->buckets[ind]; + tassert(strcmp(b->key, key) == 0); + + tassert(h->size == 1); + tassert(h->cap == 100); +} + +int test_hash_set_and_get() { + testing_func_setup(); + struct Hashmap *h = create_hashmap(100); + + char name[100] = "jake"; + char key[10] = "test"; + + int ret = hm_set(h, key, name); + tassert(ret != -1); + + struct BucketNode *got = hm_get(h, "test"); + tassert(strcmp(got->value, "jake") == 0); + + return 0; +} + +int test_hash_set_and_double_get() { + testing_func_setup(); + struct Hashmap *h = create_hashmap(100); + + char name[100] = "jake"; + char key[10] = "test"; + + int ret = hm_set(h, key, name); + tassert(ret != -1); + + double_cap(h); + + struct BucketNode *got = hm_get(h, "test"); + tassert(strcmp(got->value, "jake") == 0); + + return 0; +} diff --git a/src/util/hashmap.h b/src/util/hashmap.h new file mode 100644 index 0000000..80c9b03 --- /dev/null +++ b/src/util/hashmap.h @@ -0,0 +1,32 @@ +struct BucketNode { + char *key; + void *value; + struct BucketNode *next; +}; + +struct Hashmap { + struct BucketNode **buckets; + int size; + int cap; + + unsigned (*hash)(char *); + unsigned (*equals)(char *, char *); +}; + +struct Hashmap *create_hashmap(int capacity); +void destroy_hashmap(struct Hashmap *h); + +struct BucketNode *create_bucket(char *key, void *value); + +// Get a value with a key +struct BucketNode *hm_get(struct Hashmap *h, char *key); +// Set a value with a key +int hm_set(struct Hashmap *h, char *key, void *value); +// Double the capacity of the hashmap (happens automatically when size > +// capacity) +void double_cap(struct Hashmap *h); + +int test_hash_init(); +int test_hash_init_and_store(); +int test_hash_set_and_get(); +int test_hash_set_and_double_get(); diff --git a/src/util/test_util.c b/src/util/test_util.c new file mode 100644 index 0000000..413a5f3 --- /dev/null +++ b/src/util/test_util.c @@ -0,0 +1,14 @@ +#include "hashmap.h" +#include + +int test_util() { + testing_module_setup(); + + test_hash_init(); + test_hash_init_and_store(); + test_hash_set_and_get(); + test_hash_set_and_double_get(); + + testing_module_cleanup(); + return 0; +} diff --git a/src/util/test_util.h b/src/util/test_util.h new file mode 100644 index 0000000..ef0a526 --- /dev/null +++ b/src/util/test_util.h @@ -0,0 +1,5 @@ +#pragma once + +#include "hashmap.h" + +int test_util();