Created
July 13, 2020 16:35
-
-
Save colbyhall/d22cae8309249cb696874582d92ec1d6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #define FNV_OFFSET_BASIC 0xcbf29ce484222325 | |
| #define FNV_PRIME 0x100000001b3 | |
| inline u64 fnv1_hash(const void* s, usize size) { | |
| u64 hash = FNV_OFFSET_BASIC; | |
| const u8* const casted_s = s; | |
| for (usize i = 0; i < size; ++i) { | |
| hash *= FNV_PRIME; | |
| hash = hash ^ casted_s[i]; | |
| } | |
| return hash; | |
| } | |
| typedef struct Hash_Bucket { | |
| u64 hash; | |
| int index; | |
| struct Hash_Bucket* next; | |
| } Hash_Bucket; | |
| typedef u64 (Hash_Table_Func)(void* a, void* b, int size); | |
| u64 hash_string(void* a, void* b, int size) { | |
| assert(size == sizeof(String)); | |
| String* const s_a = a; | |
| String* const s_b = b; | |
| if (b) return string_equal(*s_a, *s_b); | |
| return fnv1_hash(s_a->data, s_a->len); | |
| } | |
| typedef struct Hash_Table { | |
| void* keys; | |
| int key_size; | |
| void* values; | |
| int value_size; | |
| Hash_Bucket* buckets; | |
| Hash_Bucket** bucket_layout; | |
| int pair_count; | |
| int pair_cap; | |
| Hash_Table_Func* func; | |
| Allocator allocator; | |
| } Hash_Table; | |
| static void rebuild_hash_table(Hash_Table* ht) { | |
| for (int i = 0; i < ht->pair_count; ++i) { | |
| Hash_Bucket* const bucket = ht->buckets + i; | |
| bucket->hash = ht->func((u8*)ht->keys + ht->key_size * i, 0, ht->key_size); | |
| bucket->index = i; | |
| bucket->next = 0; | |
| const int index = bucket->hash % ht->pair_cap; | |
| Hash_Bucket** last = 0; | |
| Hash_Bucket** slot = ht->bucket_layout + index; | |
| while (*slot) { | |
| last = slot; | |
| slot = &(*slot)->next; | |
| } | |
| *slot = bucket; | |
| if (last) (*last)->next = *slot; | |
| } | |
| } | |
| Hash_Table _make_hash_table(int key_size, int value_size, Hash_Table_Func* func, Allocator allocator) { | |
| assert(func); | |
| return (Hash_Table) { | |
| .key_size = key_size, | |
| .value_size = value_size, | |
| .func = func, | |
| .allocator = allocator | |
| }; | |
| } | |
| #define make_hash_table(key, value, func, allocator) _make_hash_table(sizeof(key), sizeof(value), func, allocator) | |
| void reserve_hash_table(Hash_Table* ht, int reserve_amount) { | |
| assert(reserve_amount > 0); | |
| ht->pair_cap += reserve_amount; // @TODO(colby): Small alg for best reserve amount | |
| ht->keys = mem_realloc(ht->allocator, ht->keys, ht->key_size * ht->pair_cap); | |
| ht->values = mem_realloc(ht->allocator, ht->values, ht->value_size * ht->pair_cap); | |
| ht->buckets = mem_realloc(ht->allocator, ht->buckets, sizeof(Hash_Bucket) * ht->pair_cap); | |
| ht->bucket_layout = mem_realloc(ht->allocator, ht->bucket_layout, sizeof(Hash_Bucket*) * ht->pair_cap); | |
| mem_set(ht->bucket_layout, 0, sizeof(Hash_Bucket*) * ht->pair_cap); | |
| } | |
| void* _push_hash_table(Hash_Table* ht, void* key, int key_size, void* value, int value_size) { | |
| assert(key_size == ht->key_size && value_size == ht->value_size); | |
| void* found = _find_hash_table(ht, key, key_size); | |
| if (found) return found; | |
| if (ht->pair_count == ht->pair_cap) { | |
| reserve_hash_table(ht, 1); | |
| mem_copy((u8*)ht->keys + key_size * ht->pair_count, key, key_size); | |
| mem_copy((u8*)ht->values + value_size * ht->pair_count, value, value_size); | |
| ht->pair_count += 1; | |
| rebuild_hash_table(ht); | |
| return 0; | |
| } | |
| mem_copy((u8*)ht->keys + key_size * ht->pair_count, key, key_size); | |
| mem_copy((u8*)ht->values + value_size * ht->pair_count, value, value_size); | |
| Hash_Bucket* const bucket = ht->buckets + ht->pair_count; | |
| bucket->hash = ht->func((u8*)ht->keys + key_size * ht->pair_count, 0, key_size); | |
| bucket->index = ht->pair_count; | |
| bucket->next = 0; | |
| const int index = bucket->hash % ht->pair_cap; | |
| Hash_Bucket** last = 0; | |
| Hash_Bucket** slot = ht->bucket_layout + index; | |
| while (*slot) { | |
| last = slot; | |
| slot = &(*slot)->next; | |
| } | |
| *slot = bucket; | |
| if (last) (*last)->next = *slot; | |
| ht->pair_count++; | |
| return 0; | |
| } | |
| #define push_hash_table(ht, key, value) _push_hash_table(ht, &key, sizeof(key), &value, sizeof(value)) | |
| void* _find_hash_table(Hash_Table* ht, void* key, int key_size) { | |
| assert(key_size == ht->key_size); | |
| if (!ht->pair_count) return 0; | |
| const u64 hash = ht->func(key, 0, key_size); | |
| const int index = hash % ht->pair_cap; | |
| Hash_Bucket* found = ht->bucket_layout[index]; | |
| while (found) { | |
| const u64 my_hash = found->hash; | |
| if (my_hash == hash) { // @TEMP } && ht->func(key, (u8*)ht->keys + found->index * key_size, key_size)) { | |
| return (u8*)ht->values + ht->value_size * found->index; | |
| } | |
| found = found->next; | |
| } | |
| return 0; | |
| } | |
| #define find_hash_table(ht, key) _find_hash_table(ht, &key, sizeof(key)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
wait this isn't an ikea table