YREA-SLS/SLS_C/src/hash_table.c

156 lines
3.8 KiB
C

// Kyler Olsen
// YREA SLS
// Hash Table
// November 2025
#include <stdlib.h>
#include "sls/hash_table.h"
#include "sls/string.h"
static size_t next_power_of_two(size_t n) {
if (n < 2) return 2;
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
#if SIZE_MAX > UINT32_MAX
n |= n >> 32;
#endif
n++;
return n;
}
HashTable *init_hash_table(size_t min_buckets_count) {
size_t buckets_count = next_power_of_two(min_buckets_count);
size_t size = sizeof(HashTable) + buckets_count * sizeof(Bucket);
HashTable *ht = (HashTable *)malloc(size);
if (!ht) return NULL;
ht->buckets_count = buckets_count;
for (size_t i = 0; i < buckets_count; i++)
ht->buckets[i] = NULL;
return ht;
}
void del_hash_table(HashTable *ht) {
for (size_t i = 0; i < ht->buckets_count; i++) {
Bucket *head = ht->buckets[i];
while (head) {
Bucket *next = head->next;
// TODO: Does item need to be freed?
free(head);
head = next;
}
}
free(ht);
}
static inline uint32_t sls_hash_a(SlsStr key) {
// FNV-1a
uint32_t hash = 2166136261u;
const uint8_t *p = (const uint8_t *)key.str;
for (size_t i = 0; i < key.len; i++) {
hash ^= p[i];
hash *= 16777619u;
}
return hash;
}
static inline uint32_t sls_hash_b(SlsStr key) {
// Murmur2A
const uint32_t m = 0x5bd1e995;
uint32_t hash = 0;
while (key.len >= 4) {
uint32_t k = (uint32_t)key.str[0]
| (uint32_t)key.str[1] << 8
| (uint32_t)key.str[2] << 16
| (uint32_t)key.str[3] << 24;
k *= m;
k ^= k >> 24;
k *= m;
hash *= m;
hash ^= k;
key.str += 4;
key.len -= 4;
}
if (key.len >= 3)
hash ^= key.str[2] << 16;
if (key.len >= 2)
hash ^= key.str[1] << 8;
if (key.len >= 1)
hash ^= key.str[0];
hash *= m;
hash ^= hash >> 13;
hash *= m;
hash ^= hash >> 15;
return hash;
}
Boolean hash_table_put(HashTable *ht, SlsStr key, void *item) {
uint32_t hash_a = sls_hash_a(key);
uint32_t hash_b = sls_hash_b(key);
size_t bucket_index = hash_a & (ht->buckets_count - 1);
for (Bucket *node = ht->buckets[bucket_index]; node; node = node->next) {
if (node->hash_a == hash_a && node->hash_b == hash_b) {
// TODO: Does item need to be freed before being replaced?
node->item = item;
return TRUE;
}
}
Bucket *bucket = (Bucket *)malloc(sizeof(Bucket));
if (!bucket)
return FALSE;
bucket->hash_a = hash_a;
bucket->hash_b = hash_b;
bucket->item = item;
bucket->next = ht->buckets[bucket_index];
ht->buckets[bucket_index] = bucket;
return TRUE;
}
void *hash_table_get(const HashTable *ht, SlsStr key, void *default_item) {
uint32_t hash_a = sls_hash_a(key);
uint32_t hash_b = sls_hash_b(key);
size_t bucket_index = hash_a & (ht->buckets_count - 1);
for (Bucket *node = ht->buckets[bucket_index]; node; node = node->next)
if (node->hash_a == hash_a && node->hash_b == hash_b)
return node->item;
return default_item;
}
Boolean hash_table_del(HashTable *ht, SlsStr key) {
uint32_t hash_a = sls_hash_a(key);
uint32_t hash_b = sls_hash_b(key);
size_t bucket_index = hash_a & (ht->buckets_count - 1);
Bucket *prev = NULL;
for (Bucket *node = ht->buckets[bucket_index]; node; node = node->next) {
if (node->hash_a == hash_a && node->hash_b == hash_b) {
if (prev == NULL)
ht->buckets[bucket_index] = node->next;
else
prev->next = node->next;
// TODO: Does item need to be freed?
free(node);
return TRUE;
}
prev = node;
}
return FALSE;
}