forked from Telodendria/Telodendria
357 lines
6.6 KiB
C
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);
|
|
}
|
|
}
|