Telodendria/src/HashMap.c
2022-08-09 21:05:10 -04:00

357 lines
6.6 KiB
C

/*
* 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 <HashMap.h>
#include <stddef.h>
#include <stdlib.h>
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);
}
}