diff options
Diffstat (limited to 'mons_collections/src/hashmap.c')
-rw-r--r-- | mons_collections/src/hashmap.c | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/mons_collections/src/hashmap.c b/mons_collections/src/hashmap.c new file mode 100644 index 0000000..c2ff172 --- /dev/null +++ b/mons_collections/src/hashmap.c @@ -0,0 +1,141 @@ +#include "hashmap.h" +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#define MONS_HASHMAP_MAX_LOAD_FACTOR 3.0f + +unsigned int djb2_hash(unsigned char *str, unsigned int bucket_count) { + unsigned long hash = 5381; + int c; + + while ((c = *str++)) { + hash = ((hash << 5) + hash) + c; + } + + return hash % bucket_count; +} + +mons_hashmap mons_hashmap_new(unsigned int member_size, unsigned int buckets) { + mons_hashmap result = { + .member_size = member_size, + .bucket_count = buckets, + .data = calloc(buckets, sizeof(mons_hashmap_pair *)), + }; + return result; +} + +int mons_hashmap_rehash(mons_hashmap *map) { + // TODO: Expand number of buckets, re-hash all entries + return EXIT_SUCCESS; +} + +int mons_hashmap_insert(mons_hashmap *map, char *key, void *value) { + unsigned int bucket = djb2_hash((unsigned char*)key, map->bucket_count); + mons_hashmap_pair *tail = map->data[bucket]; + if (tail == NULL) { + map->data[bucket] = calloc(1, sizeof(mons_hashmap_pair *)); + tail = map->data[bucket]; + } else { + mons_hashmap_pair *node = tail; + while(1) { + if(strcmp(key, node->key) == 0) { + memcpy(node->value, value, map->member_size); + return EXIT_SUCCESS; + } + if(node->next == NULL) { + tail = node; + break; + } + node = node->next; + } + tail->next = calloc(1, sizeof(mons_hashmap_pair *)); + tail = tail->next; + } + tail->key = calloc(strlen(key) + 1, 1); + strcpy(tail->key, key); + tail->value = calloc(1, map->member_size); + memcpy(tail->value, value, map->member_size); + map->len++; + if (map->len / map->bucket_count > MONS_HASHMAP_MAX_LOAD_FACTOR) { + mons_hashmap_rehash(map); + } + return EXIT_SUCCESS; +} + +int mons_hashmap_get(mons_hashmap map, char *key, void *out) { + unsigned int bucket = djb2_hash((unsigned char*)key, map.bucket_count); + mons_hashmap_pair *tail = map.data[bucket]; + while(tail != NULL) { + if(strcmp(key, tail->key) == 0) { + memcpy(out, tail->value, map.member_size); + return EXIT_SUCCESS; + } + tail = tail->next; + } + return EXIT_FAILURE; +} + +int mons_hashmap_remove(mons_hashmap *map, char *key) { + unsigned int bucket = djb2_hash((unsigned char*)key, map->bucket_count); + mons_hashmap_pair *tail = map->data[bucket]; + mons_hashmap_pair *prev = NULL; + while(tail != NULL) { + if(strcmp(key, tail->key) == 0) { + if (prev == NULL) { + map->data[bucket] = tail->next; + } else { + prev->next = tail->next; + } + free(tail->key); + free(tail->value); + free(tail); + map->len--; + return EXIT_SUCCESS; + } + prev = tail; + tail = tail->next; + } + return EXIT_FAILURE; +} + +void mons_hashmap_free(mons_hashmap *hashmap) { + for (int i = 0; i < hashmap->bucket_count; i++) { + mons_hashmap_pair *node = hashmap->data[i]; + while(node != NULL) { + mons_hashmap_pair *next = node->next; + free(node->key); + free(node->value); + free(node); + node = next; + } + } + free(hashmap->data); + hashmap->data = NULL; + hashmap->bucket_count = 0; + hashmap->member_size = 0; + hashmap->len = 0; +} + +int mons_hashmap_at(mons_hashmap map, unsigned int index, mons_hashmap_pair *out) { + mons_hashmap_pair **bucket = map.data; + mons_hashmap_pair *node = *bucket; + while(node == NULL) { + bucket++; + node = *bucket; + } + for(int i = 0; i < index; i++) { + if (node == NULL || node->next == NULL) { + bucket++; + node = *bucket; + while(node == NULL) { + bucket++; + node = *bucket; + } + } else { + node = node->next; + } + } + *out = *node; + return EXIT_SUCCESS; +} |