From 2f6954b520f0175e801a78dbfb00ab026f75c1bf Mon Sep 17 00:00:00 2001 From: FelixBrendel Date: Thu, 3 Oct 2019 16:52:01 +0200 Subject: [PATCH] made hashmaps extensibe by packing them in a horrible macro --- hashmap.hpp | 328 +++++++++++++++++++++++++--------------------------- 1 file changed, 159 insertions(+), 169 deletions(-) diff --git a/hashmap.hpp b/hashmap.hpp index 3f462db..12d560d 100644 --- a/hashmap.hpp +++ b/hashmap.hpp @@ -4,173 +4,163 @@ #include #include "types.hpp" -#define for_str_hash_map(hm) \ - if (char* key = nullptr); else \ - if (void* value = nullptr); else \ - for(int i = 0; i < hm->current_capacity; ++i) \ - if (!((key = hm->data[i].original_string) && \ - (value = hm->data[i].object))); else - - -struct StringHashMapCell { - char* original_string; - void* object; - u64 hash; - bool deleted; -}; - -struct StringHashMap { - int current_capacity; - int cell_count; - StringHashMapCell* data; -}; - -inline void print_hm(StringHashMap* hm) { - for_str_hash_map(hm) { - printf("index: %d key: %s value: %llu\n", i, key, (unsigned long long)value); - } -} - -inline bool strings_match(char* a, char* b) { - return strcmp(a, b) == 0; -} - -u32 hash(char* str) { - u32 value = str[0] << 7; - int i = 0; - while (str[i]) { - value = (10000003 * value) ^ str[i++]; - } - return value ^ i; -} - -StringHashMap* create_hashmap(int initial_capacity = 8) { - StringHashMap* hm = new StringHashMap; - hm->current_capacity = initial_capacity; - hm->cell_count = 0; - // set all data to nullptr - hm->data = (StringHashMapCell *)calloc(initial_capacity, sizeof(StringHashMapCell)); - // printf("check: %s\n", hm->data[6].original_string); - return hm; -} - -int hm_get_index_of_living_cell_if_it_exists(StringHashMap* hm, char* key, u64 hash_val) { - int index = hash_val % hm->current_capacity; - StringHashMapCell cell = hm->data[index]; - // test if cell exists at that index - if (cell.original_string) { - // check if strings match - if (strings_match(key, cell.original_string)) { - // we found it, now check it it is deleted: - if (cell.deleted) { - // we found it but it was deleted, we dont have to - // check for collisions then - return -1; - } else { - // we found it and it is not deleted - return index; - } - } else { - // strings dont match, this means we have a collision. - // We just search front to back, lol - for (int i = 0; i < hm->current_capacity; ++i) { - cell = hm->data[i]; - if (!cell.original_string) - continue; - if (!strings_match(key, cell.original_string)) - continue; - if (cell.deleted) - continue; - return i; - } - // not or only deleted cells found - return -1; - } - } else { - // no cell exists at this index so the item was never in the - // hashmap. Either it would be there or be ther and 'deleted' - // or another item would be there and therefore a collistion - // would exist - return -1; - } -} - -inline bool hm_key_exists(StringHashMap* hm, char* key) { - return hm_get_index_of_living_cell_if_it_exists(hm, key, hash(key)) != -1; -} - -inline void* hm_get_object(StringHashMap* hm, char* key) { - int index = hm_get_index_of_living_cell_if_it_exists(hm, key, hash(key)); - if (index != -1) { - return hm->data[index].object; - } - return nullptr; -} - -inline void hm_delete_object(StringHashMap* hm, char* key) { - int index = hm_get_index_of_living_cell_if_it_exists(hm, key, hash(key)); - if (index != -1) { - hm->data[index].deleted = true; - } -} - -void hm_set(StringHashMap* hm, char* key, void* obj) { - u64 hash_val = hash(key); - int index = hash_val % hm->current_capacity; - - // if we the desired cell is just empty, write to it and done :) - if (!hm->data[index].original_string) { - // insert new cell into desired slot - ++hm->cell_count; - } else { - if (strings_match(key, hm->data[index].original_string)) { - // overwrite object with same key, dont increment cell - // count - } else { - // collision, check resize - ++hm->cell_count; - if ((hm->cell_count*1.0f / hm->current_capacity) > 0.666f) { - StringHashMapCell* old_data = hm->data; - hm->data = (StringHashMapCell*)calloc(hm->current_capacity*4, sizeof(StringHashMapCell)); - hm->cell_count = 0; - hm->current_capacity *= 4; - - // insert all old items again - for (int i = 0; i < hm->current_capacity/4; ++i) { - StringHashMapCell cell = old_data[i]; - if (cell.original_string) { - hm_set(hm, cell.original_string, cell.object); - } - } - free(old_data); - index = hash_val % hm->current_capacity; - } - // search for empty slot for new cell starting at desired index; - // preventing gotos using lambdas! - [&](){ - for (int i = index; i < hm->current_capacity; ++i) { - if (!hm->data[i].original_string || - strings_match(hm->data[i].original_string, key)) - { - index = i; - return; - } - } - for (int i = 0; i < index; ++i) { - if (!hm->data[i].original_string || - strings_match(hm->data[i].original_string, key)) - { - index = i; - return; - } - } - }(); - } - } - - hm->data[index].deleted = false; - hm->data[index].original_string = key; - hm->data[index].hash = hash_val; - hm->data[index].object = obj; -} +// inline void print_hm(StringHashMap* hm) { + // for_str_hash_map(hm) { + // printf("index: %d desired-index: %llu key: %s hash: %llu value: %llu\n", i, (hm->data[i].hash % hm->current_capacity), (char*)key, hm->data[i].hash, (unsigned long long)value); + // } +// } + +#define __for_hm_generator(key_type, hm) \ + if (key_type key = nullptr); else \ + if (void* value = nullptr); else \ + for(int index = 0; index < hm->current_capacity; ++index) \ + if (!((key = hm->data[index].original) && \ + (value = hm->data[index].object))); else + + +#define define_hash_map(type, name) \ + \ + struct name##_Hash_Map_Cell { \ + type original; \ + u64 hash; \ + bool deleted; \ + void* object; \ + }; \ + \ + struct name##_Hash_Map { \ + int current_capacity; \ + int cell_count; \ + name##_Hash_Map_Cell* data; \ + }; \ + \ + name##_Hash_Map* create_##name##_hashmap(int initial_capacity=8) { \ + name##_Hash_Map* hm = new name##_Hash_Map; \ + hm->current_capacity = initial_capacity; \ + hm->cell_count = 0; \ + /* set all data to nullptr */ \ + hm->data = (name##_Hash_Map_Cell*)calloc(initial_capacity, sizeof(name##_Hash_Map_Cell)); \ + /* printf("check: %s\n", hm->data[6].original_string); */ \ + return hm; \ + } \ + \ + int hm_get_index_of_living_cell_if_it_exists(name##_Hash_Map* hm, type key, u64 hash_val) { \ + int index = hash_val % hm->current_capacity; \ + name##_Hash_Map_Cell cell = hm->data[index]; \ + /* test if cell exists at that index */ \ + if (cell.original) { \ + /* check if strings match */ \ + if (hm_objects_match(key, cell.original)) { \ + /* we found it, now check it it is deleted: */ \ + if (cell.deleted) { \ + /* we found it but it was deleted, we */ \ + /* dont have to check for collisions then */ \ + return -1; \ + } else { \ + /* we found it and it is not deleted */ \ + return index; \ + } \ + } else { \ + /* strings dont match, this means we have */ \ + /* a collision. We just search forward */ \ + for (int i = 0; i < hm->current_capacity; ++i) { \ + int new_idx = (i + index) % hm->current_capacity; \ + cell = hm->data[new_idx]; \ + if (!cell.original) \ + return -1; \ + if (!hm_objects_match(key, cell.original)) \ + continue; \ + if (cell.deleted) \ + continue; \ + return new_idx; \ + } \ + /* not or only deleted cells found */ \ + return -1; \ + } \ + } else { \ + /* no cell exists at this index so the item was never in the */ \ + /* hashmap. Either it would be there or be ther and 'deleted' */ \ + /* or another item would be there and therefore a collistion */ \ + /* would exist */ \ + return -1; \ + } \ + } \ + \ + inline bool hm_key_exists(name##_Hash_Map* hm, type key) { \ + return hm_get_index_of_living_cell_if_it_exists(hm, key, hm_hash(key)) != -1; \ + } \ + \ + inline void* hm_get_object(name##_Hash_Map* hm, type key) { \ + int index = hm_get_index_of_living_cell_if_it_exists(hm, key, hm_hash(key)); \ + if (index != -1) { \ + return hm->data[index].object; \ + } \ + return nullptr; \ + } \ + \ + inline void hm_delete_object(name##_Hash_Map* hm, char* key) { \ + int index = hm_get_index_of_living_cell_if_it_exists(hm, key, hm_hash(key)); \ + if (index != -1) { \ + hm->data[index].deleted = true; \ + } \ + } \ + \ + void hm_set(name##_Hash_Map* hm, type key, void* obj) { \ + u64 hash_val = hm_hash(key); \ + int index = hash_val % hm->current_capacity; \ + \ + /* if we the desired cell is just empty, write to it and done :) */ \ + if (!hm->data[index].original) { \ + /* insert new cell into desired slot */ \ + ++hm->cell_count; \ + } else { \ + if (hm_objects_match(key, hm->data[index].original)) { \ + /* overwrite object with same key, dont increment cell */ \ + /* count */ \ + } else { \ + /* collision, check resize */ \ + ++hm->cell_count; \ + if ((hm->cell_count*1.0f / hm->current_capacity) > 0.666f) { \ + auto old_data = hm->data; \ + hm->data = (name##_Hash_Map_Cell*)calloc(hm->current_capacity*4, sizeof(name##_Hash_Map_Cell)); \ + hm->cell_count = 0; \ + hm->current_capacity *= 4; \ + \ + /* insert all old items again */ \ + for (int i = 0; i < hm->current_capacity/4; ++i) { \ + auto cell = old_data[i]; \ + if (cell.original) { \ + hm_set(hm, cell.original, cell.object); \ + } \ + } \ + free(old_data); \ + index = hash_val % hm->current_capacity; \ + } \ + /* search for empty slot for new cell starting at desired index; */ \ + /* preventing gotos using lambdas! */ \ + [&](){ \ + for (int i = index; i < hm->current_capacity; ++i) { \ + if (!hm->data[i].original || \ + hm_objects_match(hm->data[i].original, key)) \ + { \ + index = i; \ + return; \ + } \ + } \ + for (int i = 0; i < index; ++i) { \ + if (!hm->data[i].original || \ + hm_objects_match(hm->data[i].original, key)) \ + { \ + index = i; \ + return; \ + } \ + } \ + }(); \ + } \ + } \ + \ + hm->data[index].deleted = false; \ + hm->data[index].original = key; \ + hm->data[index].hash = hash_val; \ + hm->data[index].object = obj; \ + } \