diff options
Diffstat (limited to 'src/hashmap.c')
-rw-r--r-- | src/hashmap.c | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/src/hashmap.c b/src/hashmap.c new file mode 100644 index 0000000..038678a --- /dev/null +++ b/src/hashmap.c @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2023 Johnny Richard + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include "hashmap.h" + +#include <assert.h> +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static uint32_t +u32_fnv1a_hash(const char *s); +static void +hashmap_init(hashmap_t *map); +static char * +__strdup(const char *s); + +hashmap_t * +hashmap_new(void) +{ + hashmap_t *map = (hashmap_t *)malloc(sizeof(hashmap_t)); + if (map == NULL) { + fprintf(stderr, "[FATAL] Out of memory: hashmap_new: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + hashmap_init(map); + return map; +} + +static void +hashmap_init(hashmap_t *map) +{ + assert(map); + map->entries = (hashmap_entry_t *)calloc(HASHMAP_INITIAL_CAPACITY, sizeof(hashmap_entry_t)); + if (map->entries == NULL) { + fprintf(stderr, "[FATAL] Out of memory: hashmap_init: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + map->capacity = HASHMAP_INITIAL_CAPACITY; +} + +static uint32_t +u32_fnv1a_hash(const char *s) +{ + uint32_t hash = U32_FNV1A_OFFSET_BASIS; + size_t len = strlen(s); + for (size_t i = 0; i < len; ++i) { + hash ^= s[i]; + hash *= U32_FNV1A_PRIME; + } + return hash; +} + +bool +hashmap_put(hashmap_t *map, char *key, void *value) +{ + assert(map && key); + uint32_t hash = u32_fnv1a_hash(key); + hashmap_entry_t *entry = &(map->entries[hash & (map->capacity - 1)]); + + while (entry != NULL) { + if (entry->hash == hash && strcmp(entry->key, key) == 0) { + entry->value = value; + return true; + } + if (entry->next == NULL) + break; + entry = entry->next; + } + entry->next = (hashmap_entry_t *)malloc(sizeof(hashmap_entry_t)); + *entry->next = (hashmap_entry_t){ .key = __strdup(key), .hash = hash, .value = value, .next = NULL }; + return true; +} + +void * +hashmap_get(hashmap_t *map, char *key) +{ + uint32_t hash = u32_fnv1a_hash(key); + hashmap_entry_t *entry = &map->entries[hash & (map->capacity - 1)]; + while (entry != NULL) { + if (entry->hash == hash && strcmp(entry->key, key) == 0) { + return entry->value; + } + entry = entry->next; + } + return NULL; +} + +void +hashmap_destroy(hashmap_t *map) +{ + for (size_t i = 0; i < map->capacity; ++i) { + hashmap_entry_t *entry = &map->entries[i]; + while (entry != NULL) { + if (entry->key != NULL) + free(entry->key); + entry = entry->next; + } + } + free(map->entries); + free(map); +} + +static char * +__strdup(const char *s) +{ + size_t slen = strlen(s); + char *result = malloc(slen + 1); + if (result == NULL) { + return NULL; + } + + memcpy(result, s, slen + 1); + return result; +} |