From c6cb5835185a5d9ca71eb7fd63ef59eb778d8a4f Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sat, 30 Nov 2024 13:31:59 -0500 Subject: [PATCH] Hashtable done --- project/dependencies/containers.hpp | 836 ++++++++++++++-------------- 1 file changed, 427 insertions(+), 409 deletions(-) diff --git a/project/dependencies/containers.hpp b/project/dependencies/containers.hpp index 5ef762d..ecea1da 100644 --- a/project/dependencies/containers.hpp +++ b/project/dependencies/containers.hpp @@ -38,8 +38,7 @@ template bool resize(Array& array, usize num); template bool set_capacity(Array& array, usize new_capacity); template ArrayHeader* get_header(Array& array); -struct ArrayHeader -{ +struct ArrayHeader { AllocatorInfo Allocator; usize Capacity; usize Num; @@ -48,554 +47,573 @@ struct ArrayHeader template struct Array { - Type* Data; + Type* Data; #if 1 #pragma region Member Mapping - forceinline static Array init(AllocatorInfo allocator) { return GEN_NS array_init(allocator); } - forceinline static Array init_reserve(AllocatorInfo allocator, ssize capacity) { return GEN_NS array_init_reserve(allocator, capacity); } - forceinline static usize grow_formula(ssize value) { return GEN_NS array_grow_formula(value); } + forceinline static Array init(AllocatorInfo allocator) { return GEN_NS array_init(allocator); } + forceinline static Array init_reserve(AllocatorInfo allocator, ssize capacity) { return GEN_NS array_init_reserve(allocator, capacity); } + forceinline static usize grow_formula(ssize value) { return GEN_NS array_grow_formula(value); } - forceinline bool append(Array other) { return GEN_NS append(*this, other); } - forceinline bool append(Type value) { return GEN_NS append(*this, value); } - forceinline bool append(Type* items, usize item_num) { return GEN_NS append(*this, items, item_num); } - forceinline bool append_at(Type item, usize idx) { return GEN_NS append_at(*this, item, idx); } - forceinline bool append_at(Type* items, usize item_num, usize idx) { return GEN_NS append_at(*this, items, item_num, idx); } - forceinline Type& back() { return GEN_NS back(*this); } - forceinline void clear() { GEN_NS clear(*this); } - forceinline bool fill(usize begin, usize end, Type value) { return GEN_NS fill(*this, begin, end, value); } - forceinline void free() { GEN_NS free(*this); } - forceinline ArrayHeader* get_header() { return GEN_NS get_header(*this); } - forceinline bool grow(usize min_capacity) { return GEN_NS grow(*this, min_capacity); } - forceinline usize num() { return GEN_NS num(*this); } - forceinline void pop() { GEN_NS pop(*this); } - forceinline void remove_at(usize idx) { GEN_NS remove_at(*this, idx); } - forceinline bool reserve(usize new_capacity) { return GEN_NS reserve(*this, new_capacity); } - forceinline bool resize(usize num) { return GEN_NS resize(*this, num); } - forceinline bool set_capacity(usize new_capacity) { return GEN_NS set_capacity(*this, new_capacity); } + forceinline bool append(Array other) { return GEN_NS append(*this, other); } + forceinline bool append(Type value) { return GEN_NS append(*this, value); } + forceinline bool append(Type* items, usize item_num) { return GEN_NS append(*this, items, item_num); } + forceinline bool append_at(Type item, usize idx) { return GEN_NS append_at(*this, item, idx); } + forceinline bool append_at(Type* items, usize item_num, usize idx) { return GEN_NS append_at(*this, items, item_num, idx); } + forceinline Type& back() { return GEN_NS back(*this); } + forceinline void clear() { GEN_NS clear(*this); } + forceinline bool fill(usize begin, usize end, Type value) { return GEN_NS fill(*this, begin, end, value); } + forceinline void free() { GEN_NS free(*this); } + forceinline ArrayHeader* get_header() { return GEN_NS get_header(*this); } + forceinline bool grow(usize min_capacity) { return GEN_NS grow(*this, min_capacity); } + forceinline usize num() { return GEN_NS num(*this); } + forceinline void pop() { GEN_NS pop(*this); } + forceinline void remove_at(usize idx) { GEN_NS remove_at(*this, idx); } + forceinline bool reserve(usize new_capacity) { return GEN_NS reserve(*this, new_capacity); } + forceinline bool resize(usize num) { return GEN_NS resize(*this, num); } + forceinline bool set_capacity(usize new_capacity) { return GEN_NS set_capacity(*this, new_capacity); } - forceinline operator Type*() { return Data; } - forceinline operator Type const*() const { return Data; } - forceinline Type* begin() { return Data; } - forceinline Type* end() { return Data + get_header()->Num; } + forceinline operator Type*() { return Data; } + forceinline operator Type const*() const { return Data; } + forceinline Type* begin() { return Data; } + forceinline Type* end() { return Data + get_header()->Num; } #pragma endregion Member Mapping #endif }; template inline -Array array_init(AllocatorInfo allocator) -{ - return array_init_reserve(allocator, array_grow_formula(0)); +Array array_init(AllocatorInfo allocator) { + return array_init_reserve(allocator, array_grow_formula(0)); } template inline Array array_init_reserve(AllocatorInfo allocator, ssize capacity) { - ArrayHeader* header = rcast(ArrayHeader*, alloc(allocator, sizeof(ArrayHeader) + sizeof(Type) * capacity)); + ArrayHeader* header = rcast(ArrayHeader*, alloc(allocator, sizeof(ArrayHeader) + sizeof(Type) * capacity)); - if (header == nullptr) - return {nullptr}; + if (header == nullptr) + return {nullptr}; - header->Allocator = allocator; - header->Capacity = capacity; - header->Num = 0; + header->Allocator = allocator; + header->Capacity = capacity; + header->Num = 0; - return {rcast(Type*, header + 1)}; + return {rcast(Type*, header + 1)}; } template inline -usize array_grow_formula(ssize value) -{ - return 2 * value + 8; +usize array_grow_formula(ssize value) { + return 2 * value + 8; } template inline -bool append(Array& array, Array other) -{ - return append(array, other, num(other)); +bool append(Array& array, Array other) { + return append(array, other, num(other)); } template inline bool append(Array& array, Type value) { - ArrayHeader* header = get_header(array); + ArrayHeader* header = get_header(array); - if (header->Num == header->Capacity) - { - if (!grow(array, header->Capacity)) - return false; + if (header->Num == header->Capacity) + { + if (!grow(array, header->Capacity)) + return false; + header = get_header(array); + } - header = get_header(array); - } + array.Data[header->Num] = value; + header->Num++; - array.Data[header->Num] = value; - header->Num++; - - return true; + return true; } template inline bool append(Array& array, Type* items, usize item_num) { - ArrayHeader* header = get_header(array); + ArrayHeader* header = get_header(array); - if (header->Num + item_num > header->Capacity) - { - if (!grow(array, header->Capacity + item_num)) - return false; + if (header->Num + item_num > header->Capacity) + { + if (!grow(array, header->Capacity + item_num)) + return false; + header = get_header(array); + } - header = get_header(array); - } + mem_copy(array.Data + header->Num, items, item_num * sizeof(Type)); + header->Num += item_num; - mem_copy(array.Data + header->Num, items, item_num * sizeof(Type)); - header->Num += item_num; - - return true; + return true; } template inline bool append_at(Array& array, Type item, usize idx) { - ArrayHeader* header = get_header(array); + ArrayHeader* header = get_header(array); - if (idx >= header->Num) - idx = header->Num - 1; + if (idx >= header->Num) + idx = header->Num - 1; - if (idx < 0) - idx = 0; + if (idx < 0) + idx = 0; - if (header->Capacity < header->Num + 1) - { - if (!grow(array, header->Capacity + 1)) - return false; + if (header->Capacity < header->Num + 1) + { + if (!grow(array, header->Capacity + 1)) + return false; - header = get_header(array); - } + header = get_header(array); + } - Type* target = array.Data + idx; + Type* target = array.Data + idx; - mem_move(target + 1, target, (header->Num - idx) * sizeof(Type)); - header->Num++; + mem_move(target + 1, target, (header->Num - idx) * sizeof(Type)); + header->Num++; - return true; + return true; } template inline bool append_at(Array& array, Type* items, usize item_num, usize idx) { - ArrayHeader* header = get_header(array); + ArrayHeader* header = get_header(array); - if (idx >= header->Num) - { - return append(array, items, item_num); - } + if (idx >= header->Num) + { + return append(array, items, item_num); + } - if (item_num > header->Capacity) - { - if (!grow(array, header->Capacity + item_num)) - return false; + if (item_num > header->Capacity) + { + if (!grow(array, header->Capacity + item_num)) + return false; - header = get_header(array); - } + header = get_header(array); + } - Type* target = array.Data + idx + item_num; - Type* src = array.Data + idx; + Type* target = array.Data + idx + item_num; + Type* src = array.Data + idx; - mem_move(target, src, (header->Num - idx) * sizeof(Type)); - mem_copy(src, items, item_num * sizeof(Type)); - header->Num += item_num; + mem_move(target, src, (header->Num - idx) * sizeof(Type)); + mem_copy(src, items, item_num * sizeof(Type)); + header->Num += item_num; - return true; + return true; } template inline -Type& back(Array& array) -{ - ArrayHeader* header = get_header(array); - return array.Data[header->Num - 1]; +Type& back(Array& array) { + ArrayHeader* header = get_header(array); + return array.Data[header->Num - 1]; } template inline -void clear(Array& array) -{ - ArrayHeader* header = get_header(array); - header->Num = 0; +void clear(Array& array) { + ArrayHeader* header = get_header(array); + header->Num = 0; } template inline bool fill(Array& array, usize begin, usize end, Type value) { - ArrayHeader* header = get_header(array); + ArrayHeader* header = get_header(array); - if (begin < 0 || end > header->Num) - return false; + if (begin < 0 || end > header->Num) + return false; - for (ssize idx = ssize(begin); idx < ssize(end); idx++) - { - array.Data[idx] = value; - } + for (ssize idx = ssize(begin); idx < ssize(end); idx++) + { + array.Data[idx] = value; + } - return true; + return true; } template inline -void free(Array& array) -{ - ArrayHeader* header = get_header(array); - gen::free(header->Allocator, header); - array.Data = nullptr; +void free(Array& array) { + ArrayHeader* header = get_header(array); + gen::free(header->Allocator, header); + array.Data = nullptr; } template inline -ArrayHeader* get_header(Array& array) -{ - using NonConstType = TRemoveConst; - return rcast(ArrayHeader*, const_cast(array.Data)) - 1; +ArrayHeader* get_header(Array& array) { + using NonConstType = TRemoveConst; + return rcast(ArrayHeader*, const_cast(array.Data)) - 1; } template inline bool grow(Array& array, usize min_capacity) { - ArrayHeader* header = get_header(array); - usize new_capacity = array_grow_formula(header->Capacity); + ArrayHeader* header = get_header(array); + usize new_capacity = array_grow_formula(header->Capacity); - if (new_capacity < min_capacity) - new_capacity = min_capacity; + if (new_capacity < min_capacity) + new_capacity = min_capacity; - return set_capacity(array, new_capacity); + return set_capacity(array, new_capacity); } template inline -usize num(Array& array) -{ - return get_header(array)->Num; +usize num(Array& array) { + return get_header(array)->Num; } template inline -void pop(Array& array) -{ - ArrayHeader* header = get_header(array); - GEN_ASSERT(header->Num > 0); - header->Num--; +void pop(Array& array) { + ArrayHeader* header = get_header(array); + GEN_ASSERT(header->Num > 0); + header->Num--; } template inline void remove_at(Array& array, usize idx) { - ArrayHeader* header = get_header(array); - GEN_ASSERT(idx < header->Num); + ArrayHeader* header = get_header(array); + GEN_ASSERT(idx < header->Num); - mem_move(array.Data + idx, array.Data + idx + 1, sizeof(Type) * (header->Num - idx - 1)); - header->Num--; + mem_move(array.Data + idx, array.Data + idx + 1, sizeof(Type) * (header->Num - idx - 1)); + header->Num--; } template inline bool reserve(Array& array, usize new_capacity) { - ArrayHeader* header = get_header(array); + ArrayHeader* header = get_header(array); - if (header->Capacity < new_capacity) - return set_capacity(array, new_capacity); + if (header->Capacity < new_capacity) + return set_capacity(array, new_capacity); - return true; + return true; } template inline bool resize(Array& array, usize num) { - ArrayHeader* header = get_header(array); + ArrayHeader* header = get_header(array); - if (header->Capacity < num) - { - if (!grow(array, num)) - return false; + if (header->Capacity < num) { + if (!grow(array, num)) + return false; + header = get_header(array); + } - header = get_header(array); - } - - header->Num = num; - return true; + header->Num = num; + return true; } template inline bool set_capacity(Array& array, usize new_capacity) { - ArrayHeader* header = get_header(array); + ArrayHeader* header = get_header(array); - if (new_capacity == header->Capacity) - return true; + if (new_capacity == header->Capacity) + return true; - if (new_capacity < header->Num) - { - header->Num = new_capacity; - return true; - } + if (new_capacity < header->Num) + { + header->Num = new_capacity; + return true; + } - ssize size = sizeof(ArrayHeader) + sizeof(Type) * new_capacity; - ArrayHeader* new_header = rcast(ArrayHeader*, alloc(header->Allocator, size)); + ssize size = sizeof(ArrayHeader) + sizeof(Type) * new_capacity; + ArrayHeader* new_header = rcast(ArrayHeader*, alloc(header->Allocator, size)); - if (new_header == nullptr) - return false; + if (new_header == nullptr) + return false; - mem_move(new_header, header, sizeof(ArrayHeader) + sizeof(Type) * header->Num); + mem_move(new_header, header, sizeof(ArrayHeader) + sizeof(Type) * header->Num); - new_header->Capacity = new_capacity; + new_header->Capacity = new_capacity; - gen::free(header->Allocator, header); + GEN_NS free(header->Allocator, header); - array.Data = rcast(Type*, new_header + 1); - return true; + array.Data = rcast(Type*, new_header + 1); + return true; } #pragma endregion Array // TODO(Ed) : This thing needs ALOT of work. +#pragma region HashTable +template struct HashTable; + +struct HashTableFindResult { + ssize HashIndex; + ssize PrevIndex; + ssize EntryIndex; +}; + +template +struct HashTableEntry { + u64 Key; + ssize Next; + Type Value; +}; + +// Forward declarations for all lifted functions +template HashTable hashtable_init(AllocatorInfo allocator); +template HashTable hashtable_init_reserve(AllocatorInfo allocator, usize num); +template void clear(HashTable& table); +template void destroy(HashTable& table); +template Type* get(HashTable& table, u64 key); +template void grow(HashTable& table); +template void rehash(HashTable& table, ssize new_num); +template void rehash_fast(HashTable& table); +template void remove(HashTable& table, u64 key); +template void remove_entry(HashTable& table, ssize idx); +template void set(HashTable& table, u64 key, Type value); +template ssize slot(HashTable& table, u64 key); +template ssize add_entry(HashTable& table, u64 key); +template HashTableFindResult find(HashTable& table, u64 key); +template bool full(HashTable& table); +template void map(HashTable& table, void (*map_proc)(u64 key, Type value)); +template void map_mut(HashTable& table, void (*map_proc)(u64 key, Type* value)); + template struct HashTable { - struct FindResult - { - ssize HashIndex; - ssize PrevIndex; - ssize EntryIndex; - }; - - struct Entry - { - u64 Key; - ssize Next; - Type Value; - }; - static constexpr f32 CriticalLoadScale = 0.7f; - static - HashTable init( AllocatorInfo allocator ) - { - HashTable result = init_reserve(allocator, 8); - return result; - } + Array Hashes; + Array> Entries; - static - HashTable init_reserve( AllocatorInfo allocator, usize num ) - { - HashTable result = { { nullptr }, { nullptr } }; +#if 1 +#pragma region Member Mapping + forceinline static HashTable init(AllocatorInfo allocator) { return GEN_NS hashtable_init(allocator); } + forceinline static HashTable init_reserve(AllocatorInfo allocator, usize num) { return GEN_NS hashtable_init_reserve(allocator, num); } - result.Hashes = Array::init_reserve( allocator, num ); - result.Hashes.get_header()->Num = num; - result.Hashes.resize( num ); - result.Hashes.fill( 0, num, -1); - - result.Entries = Array::init_reserve( allocator, num ); - return result; - } - - void clear( void ) - { - Entries.clear(); - Hashes.fill( 0, Hashes.num(), -1); - } - - void destroy( void ) - { - if ( Hashes && Hashes.get_header()->Capacity ) - { - Hashes.free(); - Entries.free(); - } - } - - Type* get( u64 key ) - { - ssize idx = find( key ).EntryIndex; - if ( idx >= 0 ) - return & Entries[ idx ].Value; - - return nullptr; - } - - using MapProc = void (*)( u64 key, Type value ); - - void map( MapProc map_proc ) - { - GEN_ASSERT_NOT_NULL( map_proc ); - - for ( ssize idx = 0; idx < ssize(Entries.num()); ++idx ) - { - map_proc( Entries[ idx ].Key, Entries[ idx ].Value ); - } - } - - using MapMutProc = void (*)( u64 key, Type* value ); - - void map_mut( MapMutProc map_proc ) - { - GEN_ASSERT_NOT_NULL( map_proc ); - - for ( ssize idx = 0; idx < ssize(Entries.num()); ++idx ) - { - map_proc( Entries[ idx ].Key, & Entries[ idx ].Value ); - } - } - - void grow() - { - ssize new_num = Array::grow_formula( Entries.num() ); - rehash( new_num ); - } - - void rehash( ssize new_num ) - { - ssize last_added_index; - - HashTable new_ht = init_reserve( Hashes.get_header()->Allocator, new_num ); - for ( ssize idx = 0; idx < ssize(Entries.num()); ++idx ) - { - FindResult find_result; - - Entry& entry = Entries[ idx ]; - find_result = new_ht.find( entry.Key ); - last_added_index = new_ht.add_entry( entry.Key ); - - if ( find_result.PrevIndex < 0 ) - new_ht.Hashes[ find_result.HashIndex ] = last_added_index; - else - new_ht.Entries[ find_result.PrevIndex ].Next = last_added_index; - - new_ht.Entries[ last_added_index ].Next = find_result.EntryIndex; - new_ht.Entries[ last_added_index ].Value = entry.Value; - } - - destroy(); - *this = new_ht; - } - - void rehash_fast() - { - ssize idx; - - for ( idx = 0; idx < ssize(Entries.num()); idx++ ) - Entries[ idx ].Next = -1; - - for ( idx = 0; idx < ssize(Hashes.num()); idx++ ) - Hashes[ idx ] = -1; - - for ( idx = 0; idx < ssize(Entries.num()); idx++ ) - { - Entry* entry; - FindResult find_result; - - entry = & Entries[ idx ]; - find_result = find( entry->Key ); - - if ( find_result.PrevIndex < 0 ) - Hashes[ find_result.HashIndex ] = idx; - else - Entries[ find_result.PrevIndex ].Next = idx; - } - } - - void remove( u64 key ) - { - FindResult find_result = find( key); - - if ( find_result.EntryIndex >= 0 ) - { - Entries.remove_at( find_result.EntryIndex ); - rehash_fast(); - } - } - - void remove_entry( ssize idx ) - { - Entries.remove_at( idx ); - } - - void set( u64 key, Type value ) - { - ssize idx; - FindResult find_result; - - if ( full() ) - grow(); - - find_result = find( key ); - if ( find_result.EntryIndex >= 0 ) - { - idx = find_result.EntryIndex; - } - else - { - idx = add_entry( key ); - - if ( find_result.PrevIndex >= 0 ) - { - Entries[ find_result.PrevIndex ].Next = idx; - } - else - { - Hashes[ find_result.HashIndex ] = idx; - } - } - - Entries[ idx ].Value = value; - - if ( full() ) - grow(); - } - - ssize slot( u64 key ) - { - for ( ssize idx = 0; idx < ssize(Hashes.num()); ++idx ) - if ( Hashes[ idx ] == key ) - return idx; - - return -1; - } - - Array< ssize> Hashes; - Array< Entry> Entries; - -protected: - - ssize add_entry( u64 key ) - { - ssize idx; - Entry entry = { key, -1 }; - - idx = Entries.num(); - Entries.append( entry ); - return idx; - } - - FindResult find( u64 key ) - { - FindResult result = { -1, -1, -1 }; - - if ( Hashes.num() > 0 ) - { - result.HashIndex = key % Hashes.num(); - result.EntryIndex = Hashes[ result.HashIndex ]; - - while ( result.EntryIndex >= 0 ) - { - if ( Entries[ result.EntryIndex ].Key == key ) - break; - - result.PrevIndex = result.EntryIndex; - result.EntryIndex = Entries[ result.EntryIndex ].Next; - } - } - - return result; - } - - b32 full() - { - usize critical_load = usize( CriticalLoadScale * f32(Hashes.num()) ); - b32 result = Entries.num() > critical_load; - return result; - } + forceinline void clear() { GEN_NS clear(*this); } + forceinline void destroy() { GEN_NS destroy(*this); } + forceinline Type* get(u64 key) { return GEN_NS get(*this, key); } + forceinline void grow() { GEN_NS grow(*this); } + forceinline void rehash(ssize new_num) { GEN_NS rehash(*this, new_num); } + forceinline void rehash_fast() { GEN_NS rehash_fast(*this); } + forceinline void remove(u64 key) { GEN_NS remove(*this, key); } + forceinline void remove_entry(ssize idx) { GEN_NS remove_entry(*this, idx); } + forceinline void set(u64 key, Type value) { GEN_NS set(*this, key, value); } + forceinline ssize slot(u64 key) { return GEN_NS slot(*this, key); } + forceinline void map(void (*proc)(u64, Type)) { GEN_NS map(*this, proc); } + forceinline void map_mut(void (*proc)(u64, Type*)) { GEN_NS map_mut(*this, proc); } +#pragma endregion Member Mapping +#endif }; +template inline +HashTable hashtable_init(AllocatorInfo allocator) { + HashTable result = hashtable_init_reserve(allocator, 8); + return result; +} + +template inline +HashTable hashtable_init_reserve(AllocatorInfo allocator, usize num) +{ + HashTable result = { { nullptr }, { nullptr } }; + + result.Hashes = Array::init_reserve(allocator, num); + result.Hashes.get_header()->Num = num; + result.Hashes.resize(num); + result.Hashes.fill(0, num, -1); + + result.Entries = Array>::init_reserve(allocator, num); + return result; +} + +template inline +void clear(HashTable& table) { + table.Entries.clear(); + table.Hashes.fill(0, table.Hashes.num(), -1); +} + +template inline +void destroy(HashTable& table) { + if (table.Hashes && table.Hashes.get_header()->Capacity) { + table.Hashes.free(); + table.Entries.free(); + } +} + +template inline +Type* get(HashTable& table, u64 key) { + ssize idx = find(table, key).EntryIndex; + if (idx >= 0) + return &table.Entries[idx].Value; + + return nullptr; +} + +template inline +void map(HashTable& table, void (*map_proc)(u64 key, Type value)) { + GEN_ASSERT_NOT_NULL(map_proc); + + for (ssize idx = 0; idx < ssize(table.Entries.num()); ++idx) { + map_proc(table.Entries[idx].Key, table.Entries[idx].Value); + } +} + +template inline +void map_mut(HashTable& table, void (*map_proc)(u64 key, Type* value)) { + GEN_ASSERT_NOT_NULL(map_proc); + + for (ssize idx = 0; idx < ssize(table.Entries.num()); ++idx) { + map_proc(table.Entries[idx].Key, &table.Entries[idx].Value); + } +} + +template inline +void grow(HashTable& table) { + ssize new_num = Array>::grow_formula(table.Entries.num()); + rehash(table, new_num); +} + +template inline +void rehash(HashTable& table, ssize new_num) +{ + ssize last_added_index; + HashTable new_ht = hashtable_init_reserve(table.Hashes.get_header()->Allocator, new_num); + + for (ssize idx = 0; idx < ssize(table.Entries.num()); ++idx) + { + HashTableFindResult find_result; + HashTableEntry& entry = table.Entries[idx]; + + find_result = find(new_ht, entry.Key); + last_added_index = add_entry(new_ht, entry.Key); + + if (find_result.PrevIndex < 0) + new_ht.Hashes[find_result.HashIndex] = last_added_index; + else + new_ht.Entries[find_result.PrevIndex].Next = last_added_index; + + new_ht.Entries[last_added_index].Next = find_result.EntryIndex; + new_ht.Entries[last_added_index].Value = entry.Value; + } + + destroy(table); + table = new_ht; +} + +template inline +void rehash_fast(HashTable& table) +{ + ssize idx; + + for (idx = 0; idx < ssize(table.Entries.num()); idx++) + table.Entries[idx].Next = -1; + + for (idx = 0; idx < ssize(table.Hashes.num()); idx++) + table.Hashes[idx] = -1; + + for (idx = 0; idx < ssize(table.Entries.num()); idx++) + { + HashTableEntry* entry; + HashTableFindResult find_result; + + entry = &table.Entries[idx]; + find_result = find(table, entry->Key); + + if (find_result.PrevIndex < 0) + table.Hashes[find_result.HashIndex] = idx; + else + table.Entries[find_result.PrevIndex].Next = idx; + } +} + +template inline +void remove(HashTable& table, u64 key) { + HashTableFindResult find_result = find(table, key); + + if (find_result.EntryIndex >= 0) { + table.Entries.remove_at(find_result.EntryIndex); + rehash_fast(table); + } +} + +template inline +void remove_entry(HashTable& table, ssize idx) { + table.Entries.remove_at(idx); +} + +template inline +void set(HashTable& table, u64 key, Type value) +{ + ssize idx; + HashTableFindResult find_result; + + if (full(table)) + grow(table); + + find_result = find(table, key); + if (find_result.EntryIndex >= 0) { + idx = find_result.EntryIndex; + } + else + { + idx = add_entry(table, key); + + if (find_result.PrevIndex >= 0) { + table.Entries[find_result.PrevIndex].Next = idx; + } + else { + table.Hashes[find_result.HashIndex] = idx; + } + } + + table.Entries[idx].Value = value; + + if (full(table)) + grow(table); +} + +template inline +ssize slot(HashTable& table, u64 key) { + for (ssize idx = 0; idx < ssize(table.Hashes.num()); ++idx) + if (table.Hashes[idx] == key) + return idx; + + return -1; +} + +template inline +ssize add_entry(HashTable& table, u64 key) { + ssize idx; + HashTableEntry entry = { key, -1 }; + + idx = table.Entries.num(); + table.Entries.append(entry); + return idx; +} + +template inline +HashTableFindResult find(HashTable& table, u64 key) +{ + HashTableFindResult result = { -1, -1, -1 }; + + if (table.Hashes.num() > 0) + { + result.HashIndex = key % table.Hashes.num(); + result.EntryIndex = table.Hashes[result.HashIndex]; + + while (result.EntryIndex >= 0) + { + if (table.Entries[result.EntryIndex].Key == key) + break; + + result.PrevIndex = result.EntryIndex; + result.EntryIndex = table.Entries[result.EntryIndex].Next; + } + } + + return result; +} + +template inline +bool full(HashTable& table) { + usize critical_load = usize(HashTable::CriticalLoadScale * f32(table.Hashes.num())); + b32 result = table.Entries.num() > critical_load; + return result; +} +#pragma endregion HashTable + #pragma endregion Containers