/* * Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net> * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include typedef struct HashMapBucket { unsigned long hash; char *key; void *value; } HashMapBucket; struct HashMap { size_t count; size_t capacity; HashMapBucket **entries; float maxLoad; size_t iterator; }; static unsigned long HashMapHashKey(const char *key) { unsigned long hash = 2166136261u; size_t i = 0; while (key[i]) { hash ^= (unsigned char) key[i]; hash *= 16777619; i++; } return hash; } static int HashMapGrow(HashMap * map) { size_t oldCapacity; size_t i; HashMapBucket **newEntries; if (!map) { return 0; } oldCapacity = map->capacity; map->capacity *= 2; newEntries = calloc(map->capacity, sizeof(HashMapBucket *)); if (!newEntries) { return 0; } for (i = 0; i < oldCapacity; i++) { /* If there is a value here, and it isn't a tombstone */ if (map->entries[i] && map->entries[i]->hash) { /* Copy it to the new entries array */ size_t index = map->entries[i]->hash % map->capacity; for (;;) { if (newEntries[index]) { if (!newEntries[index]->hash) { free(newEntries[index]); newEntries[index] = map->entries[i]; break; } } else { newEntries[index] = map->entries[i]; break; } index = (index + 1) % map->capacity; } } else { /* Either NULL or a tombstone */ free(map->entries[i]); } } free(map->entries); map->entries = newEntries; return 1; } HashMap * HashMapCreate(void) { HashMap *map = malloc(sizeof(HashMap)); if (!map) { return NULL; } map->maxLoad = 0.75; map->count = 0; map->capacity = 16; map->iterator = 0; map->entries = calloc(map->capacity, sizeof(HashMapBucket *)); if (!map->entries) { free(map); return NULL; } return map; } void * HashMapDelete(HashMap * map, const char *key) { unsigned long hash; size_t index; if (!map || !key) { return NULL; } hash = HashMapHashKey(key); index = hash % map->capacity; for (;;) { HashMapBucket *bucket = map->entries[index]; if (!bucket) { break; } if (bucket->hash == hash) { bucket->hash = 0; return bucket->value; } index = (index + 1) % map->capacity; } return NULL; } void HashMapFree(HashMap * map) { if (map) { size_t i; for (i = 0; i < map->capacity; i++) { if (map->entries[i]) { free(map->entries[i]); } } free(map->entries); free(map); } } void * HashMapGet(HashMap * map, const char *key) { unsigned long hash; size_t index; if (!map || !key) { return NULL; } hash = HashMapHashKey(key); index = hash % map->capacity; for (;;) { HashMapBucket *bucket = map->entries[index]; if (!bucket) { break; } if (bucket->hash == hash) { return bucket->value; } index = (index + 1) % map->capacity; } return NULL; } int HashMapIterate(HashMap * map, char **key, void **value) { if (!map) { return 0; } if (map->iterator >= map->capacity) { map->iterator = 0; *key = NULL; *value = NULL; return 0; } while (map->iterator < map->capacity) { HashMapBucket *bucket = map->entries[map->iterator]; map->iterator++; if (bucket) { *key = bucket->key; *value = bucket->value; return 1; } } map->iterator = 0; return 0; } void HashMapMaxLoadSet(HashMap * map, float load) { if (!map || (load > 1.0 || load <= 0)) { return; } map->maxLoad = load; } void * HashMapSet(HashMap * map, char *key, void *value) { unsigned long hash; size_t index; if (!map || !key || !value) { return NULL; } if (map->count + 1 > map->capacity * map->maxLoad) { HashMapGrow(map); } hash = HashMapHashKey(key); index = hash % map->capacity; for (;;) { HashMapBucket *bucket = map->entries[index]; if (!bucket) { bucket = malloc(sizeof(HashMapBucket)); if (!bucket) { break; } bucket->hash = hash; bucket->key = key; bucket->value = value; map->entries[index] = bucket; map->count++; break; } if (!bucket->hash) { bucket->hash = hash; bucket->key = key; bucket->value = value; break; } if (bucket->hash == hash) { void *oldValue = bucket->value; bucket->value = value; return oldValue; } index = (index + 1) % map->capacity; } return NULL; } void HashMapIterateFree(char *key, void *value) { if (key) { free(key); } if (value) { free(value); } }