// Kyler Olsen // YREA SLS // Hash Table // November 2025 #include #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; }