Hashtable done

This commit is contained in:
Edward R. Gonzalez 2024-11-30 13:31:59 -05:00
parent 34eec66f35
commit c6cb583518

View File

@ -38,8 +38,7 @@ template<class Type> bool resize(Array<Type>& array, usize num);
template<class Type> bool set_capacity(Array<Type>& array, usize new_capacity); template<class Type> bool set_capacity(Array<Type>& array, usize new_capacity);
template<class Type> ArrayHeader* get_header(Array<Type>& array); template<class Type> ArrayHeader* get_header(Array<Type>& array);
struct ArrayHeader struct ArrayHeader {
{
AllocatorInfo Allocator; AllocatorInfo Allocator;
usize Capacity; usize Capacity;
usize Num; usize Num;
@ -83,8 +82,7 @@ struct Array
}; };
template<class Type> inline template<class Type> inline
Array<Type> array_init(AllocatorInfo allocator) Array<Type> array_init(AllocatorInfo allocator) {
{
return array_init_reserve<Type>(allocator, array_grow_formula<Type>(0)); return array_init_reserve<Type>(allocator, array_grow_formula<Type>(0));
} }
@ -104,14 +102,12 @@ Array<Type> array_init_reserve(AllocatorInfo allocator, ssize capacity)
} }
template<class Type> inline template<class Type> inline
usize array_grow_formula(ssize value) usize array_grow_formula(ssize value) {
{
return 2 * value + 8; return 2 * value + 8;
} }
template<class Type> inline template<class Type> inline
bool append(Array<Type>& array, Array<Type> other) bool append(Array<Type>& array, Array<Type> other) {
{
return append(array, other, num(other)); return append(array, other, num(other));
} }
@ -124,7 +120,6 @@ bool append(Array<Type>& array, Type value)
{ {
if (!grow(array, header->Capacity)) if (!grow(array, header->Capacity))
return false; return false;
header = get_header(array); header = get_header(array);
} }
@ -143,7 +138,6 @@ bool append(Array<Type>& array, Type* items, usize item_num)
{ {
if (!grow(array, header->Capacity + item_num)) if (!grow(array, header->Capacity + item_num))
return false; return false;
header = get_header(array); header = get_header(array);
} }
@ -209,15 +203,13 @@ bool append_at(Array<Type>& array, Type* items, usize item_num, usize idx)
} }
template<class Type> inline template<class Type> inline
Type& back(Array<Type>& array) Type& back(Array<Type>& array) {
{
ArrayHeader* header = get_header(array); ArrayHeader* header = get_header(array);
return array.Data[header->Num - 1]; return array.Data[header->Num - 1];
} }
template<class Type> inline template<class Type> inline
void clear(Array<Type>& array) void clear(Array<Type>& array) {
{
ArrayHeader* header = get_header(array); ArrayHeader* header = get_header(array);
header->Num = 0; header->Num = 0;
} }
@ -239,16 +231,14 @@ bool fill(Array<Type>& array, usize begin, usize end, Type value)
} }
template<class Type> inline template<class Type> inline
void free(Array<Type>& array) void free(Array<Type>& array) {
{
ArrayHeader* header = get_header(array); ArrayHeader* header = get_header(array);
gen::free(header->Allocator, header); gen::free(header->Allocator, header);
array.Data = nullptr; array.Data = nullptr;
} }
template<class Type> inline template<class Type> inline
ArrayHeader* get_header(Array<Type>& array) ArrayHeader* get_header(Array<Type>& array) {
{
using NonConstType = TRemoveConst<Type>; using NonConstType = TRemoveConst<Type>;
return rcast(ArrayHeader*, const_cast<NonConstType*>(array.Data)) - 1; return rcast(ArrayHeader*, const_cast<NonConstType*>(array.Data)) - 1;
} }
@ -266,14 +256,12 @@ bool grow(Array<Type>& array, usize min_capacity)
} }
template<class Type> inline template<class Type> inline
usize num(Array<Type>& array) usize num(Array<Type>& array) {
{
return get_header(array)->Num; return get_header(array)->Num;
} }
template<class Type> inline template<class Type> inline
void pop(Array<Type>& array) void pop(Array<Type>& array) {
{
ArrayHeader* header = get_header(array); ArrayHeader* header = get_header(array);
GEN_ASSERT(header->Num > 0); GEN_ASSERT(header->Num > 0);
header->Num--; header->Num--;
@ -305,11 +293,9 @@ bool resize(Array<Type>& array, usize num)
{ {
ArrayHeader* header = get_header(array); ArrayHeader* header = get_header(array);
if (header->Capacity < num) if (header->Capacity < num) {
{
if (!grow(array, num)) if (!grow(array, num))
return false; return false;
header = get_header(array); header = get_header(array);
} }
@ -341,7 +327,7 @@ bool set_capacity(Array<Type>& array, usize new_capacity)
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); array.Data = rcast(Type*, new_header + 1);
return true; return true;
@ -350,252 +336,284 @@ bool set_capacity(Array<Type>& array, usize new_capacity)
// TODO(Ed) : This thing needs ALOT of work. // TODO(Ed) : This thing needs ALOT of work.
template<typename Type> #pragma region HashTable
struct HashTable template<class Type> struct HashTable;
{
struct FindResult struct HashTableFindResult {
{
ssize HashIndex; ssize HashIndex;
ssize PrevIndex; ssize PrevIndex;
ssize EntryIndex; ssize EntryIndex;
}; };
struct Entry template<class Type>
{ struct HashTableEntry {
u64 Key; u64 Key;
ssize Next; ssize Next;
Type Value; Type Value;
}; };
// Forward declarations for all lifted functions
template<class Type> HashTable<Type> hashtable_init(AllocatorInfo allocator);
template<class Type> HashTable<Type> hashtable_init_reserve(AllocatorInfo allocator, usize num);
template<class Type> void clear(HashTable<Type>& table);
template<class Type> void destroy(HashTable<Type>& table);
template<class Type> Type* get(HashTable<Type>& table, u64 key);
template<class Type> void grow(HashTable<Type>& table);
template<class Type> void rehash(HashTable<Type>& table, ssize new_num);
template<class Type> void rehash_fast(HashTable<Type>& table);
template<class Type> void remove(HashTable<Type>& table, u64 key);
template<class Type> void remove_entry(HashTable<Type>& table, ssize idx);
template<class Type> void set(HashTable<Type>& table, u64 key, Type value);
template<class Type> ssize slot(HashTable<Type>& table, u64 key);
template<class Type> ssize add_entry(HashTable<Type>& table, u64 key);
template<class Type> HashTableFindResult find(HashTable<Type>& table, u64 key);
template<class Type> bool full(HashTable<Type>& table);
template<class Type> void map(HashTable<Type>& table, void (*map_proc)(u64 key, Type value));
template<class Type> void map_mut(HashTable<Type>& table, void (*map_proc)(u64 key, Type* value));
template<typename Type>
struct HashTable
{
static constexpr f32 CriticalLoadScale = 0.7f; static constexpr f32 CriticalLoadScale = 0.7f;
static Array<ssize> Hashes;
HashTable init( AllocatorInfo allocator ) Array<HashTableEntry<Type>> Entries;
{
HashTable<Type> result = init_reserve(allocator, 8);
return result;
}
static #if 1
HashTable init_reserve( AllocatorInfo allocator, usize num ) #pragma region Member Mapping
{ forceinline static HashTable init(AllocatorInfo allocator) { return GEN_NS hashtable_init<Type>(allocator); }
forceinline static HashTable init_reserve(AllocatorInfo allocator, usize num) { return GEN_NS hashtable_init_reserve<Type>(allocator, num); }
forceinline void clear() { GEN_NS clear<Type>(*this); }
forceinline void destroy() { GEN_NS destroy<Type>(*this); }
forceinline Type* get(u64 key) { return GEN_NS get<Type>(*this, key); }
forceinline void grow() { GEN_NS grow<Type>(*this); }
forceinline void rehash(ssize new_num) { GEN_NS rehash<Type>(*this, new_num); }
forceinline void rehash_fast() { GEN_NS rehash_fast<Type>(*this); }
forceinline void remove(u64 key) { GEN_NS remove<Type>(*this, key); }
forceinline void remove_entry(ssize idx) { GEN_NS remove_entry<Type>(*this, idx); }
forceinline void set(u64 key, Type value) { GEN_NS set<Type>(*this, key, value); }
forceinline ssize slot(u64 key) { return GEN_NS slot<Type>(*this, key); }
forceinline void map(void (*proc)(u64, Type)) { GEN_NS map<Type>(*this, proc); }
forceinline void map_mut(void (*proc)(u64, Type*)) { GEN_NS map_mut<Type>(*this, proc); }
#pragma endregion Member Mapping
#endif
};
template<typename Type> inline
HashTable<Type> hashtable_init(AllocatorInfo allocator) {
HashTable<Type> result = hashtable_init_reserve<Type>(allocator, 8);
return result;
}
template<typename Type> inline
HashTable<Type> hashtable_init_reserve(AllocatorInfo allocator, usize num)
{
HashTable<Type> result = { { nullptr }, { nullptr } }; HashTable<Type> result = { { nullptr }, { nullptr } };
result.Hashes = Array<ssize>::init_reserve( allocator, num ); result.Hashes = Array<ssize>::init_reserve(allocator, num);
result.Hashes.get_header()->Num = num; result.Hashes.get_header()->Num = num;
result.Hashes.resize( num ); result.Hashes.resize(num);
result.Hashes.fill( 0, num, -1); result.Hashes.fill(0, num, -1);
result.Entries = Array<Entry>::init_reserve( allocator, num ); result.Entries = Array<HashTableEntry<Type>>::init_reserve(allocator, num);
return result; return result;
} }
void clear( void ) template<typename Type> inline
{ void clear(HashTable<Type>& table) {
Entries.clear(); table.Entries.clear();
Hashes.fill( 0, Hashes.num(), -1); table.Hashes.fill(0, table.Hashes.num(), -1);
} }
void destroy( void ) template<typename Type> inline
{ void destroy(HashTable<Type>& table) {
if ( Hashes && Hashes.get_header()->Capacity ) if (table.Hashes && table.Hashes.get_header()->Capacity) {
{ table.Hashes.free();
Hashes.free(); table.Entries.free();
Entries.free();
}
} }
}
Type* get( u64 key ) template<typename Type> inline
{ Type* get(HashTable<Type>& table, u64 key) {
ssize idx = find( key ).EntryIndex; ssize idx = find(table, key).EntryIndex;
if ( idx >= 0 ) if (idx >= 0)
return & Entries[ idx ].Value; return &table.Entries[idx].Value;
return nullptr; return nullptr;
}
template<typename Type> inline
void map(HashTable<Type>& 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);
} }
}
using MapProc = void (*)( u64 key, Type value ); template<typename Type> inline
void map_mut(HashTable<Type>& table, void (*map_proc)(u64 key, Type* value)) {
GEN_ASSERT_NOT_NULL(map_proc);
void map( MapProc map_proc ) for (ssize idx = 0; idx < ssize(table.Entries.num()); ++idx) {
{ map_proc(table.Entries[idx].Key, &table.Entries[idx].Value);
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 ); template<typename Type> inline
void grow(HashTable<Type>& table) {
ssize new_num = Array<HashTableEntry<Type>>::grow_formula(table.Entries.num());
rehash(table, new_num);
}
void map_mut( MapMutProc map_proc ) template<typename Type> inline
{ void rehash(HashTable<Type>& table, ssize new_num)
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<Entry>::grow_formula( Entries.num() );
rehash( new_num );
}
void rehash( ssize new_num )
{
ssize last_added_index; ssize last_added_index;
HashTable<Type> new_ht = hashtable_init_reserve<Type>(table.Hashes.get_header()->Allocator, new_num);
HashTable<Type> new_ht = init_reserve( Hashes.get_header()->Allocator, new_num ); for (ssize idx = 0; idx < ssize(table.Entries.num()); ++idx)
for ( ssize idx = 0; idx < ssize(Entries.num()); ++idx )
{ {
FindResult find_result; HashTableFindResult find_result;
HashTableEntry<Type>& entry = table.Entries[idx];
Entry& entry = Entries[ idx ]; find_result = find(new_ht, entry.Key);
find_result = new_ht.find( entry.Key ); last_added_index = add_entry(new_ht, entry.Key);
last_added_index = new_ht.add_entry( entry.Key );
if ( find_result.PrevIndex < 0 ) if (find_result.PrevIndex < 0)
new_ht.Hashes[ find_result.HashIndex ] = last_added_index; new_ht.Hashes[find_result.HashIndex] = last_added_index;
else else
new_ht.Entries[ find_result.PrevIndex ].Next = last_added_index; 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].Next = find_result.EntryIndex;
new_ht.Entries[ last_added_index ].Value = entry.Value; new_ht.Entries[last_added_index].Value = entry.Value;
} }
destroy(); destroy(table);
*this = new_ht; table = new_ht;
} }
void rehash_fast() template<typename Type> inline
{ void rehash_fast(HashTable<Type>& table)
{
ssize idx; ssize idx;
for ( idx = 0; idx < ssize(Entries.num()); idx++ ) for (idx = 0; idx < ssize(table.Entries.num()); idx++)
Entries[ idx ].Next = -1; table.Entries[idx].Next = -1;
for ( idx = 0; idx < ssize(Hashes.num()); idx++ ) for (idx = 0; idx < ssize(table.Hashes.num()); idx++)
Hashes[ idx ] = -1; table.Hashes[idx] = -1;
for ( idx = 0; idx < ssize(Entries.num()); idx++ ) for (idx = 0; idx < ssize(table.Entries.num()); idx++)
{ {
Entry* entry; HashTableEntry<Type>* entry;
FindResult find_result; HashTableFindResult find_result;
entry = & Entries[ idx ]; entry = &table.Entries[idx];
find_result = find( entry->Key ); find_result = find(table, entry->Key);
if ( find_result.PrevIndex < 0 ) if (find_result.PrevIndex < 0)
Hashes[ find_result.HashIndex ] = idx; table.Hashes[find_result.HashIndex] = idx;
else else
Entries[ find_result.PrevIndex ].Next = idx; table.Entries[find_result.PrevIndex].Next = idx;
}
} }
}
void remove( u64 key ) template<typename Type> inline
{ void remove(HashTable<Type>& table, u64 key) {
FindResult find_result = find( key); HashTableFindResult find_result = find(table, key);
if ( find_result.EntryIndex >= 0 ) if (find_result.EntryIndex >= 0) {
{ table.Entries.remove_at(find_result.EntryIndex);
Entries.remove_at( find_result.EntryIndex ); rehash_fast(table);
rehash_fast();
}
} }
}
void remove_entry( ssize idx ) template<typename Type> inline
{ void remove_entry(HashTable<Type>& table, ssize idx) {
Entries.remove_at( idx ); table.Entries.remove_at(idx);
} }
void set( u64 key, Type value ) template<typename Type> inline
{ void set(HashTable<Type>& table, u64 key, Type value)
{
ssize idx; ssize idx;
FindResult find_result; HashTableFindResult find_result;
if ( full() ) if (full(table))
grow(); grow(table);
find_result = find( key ); find_result = find(table, key);
if ( find_result.EntryIndex >= 0 ) if (find_result.EntryIndex >= 0) {
{
idx = find_result.EntryIndex; idx = find_result.EntryIndex;
} }
else else
{ {
idx = add_entry( key ); idx = add_entry(table, key);
if ( find_result.PrevIndex >= 0 ) if (find_result.PrevIndex >= 0) {
{ table.Entries[find_result.PrevIndex].Next = idx;
Entries[ find_result.PrevIndex ].Next = idx;
} }
else else {
{ table.Hashes[find_result.HashIndex] = idx;
Hashes[ find_result.HashIndex ] = idx;
} }
} }
Entries[ idx ].Value = value; table.Entries[idx].Value = value;
if ( full() ) if (full(table))
grow(); grow(table);
} }
ssize slot( u64 key ) template<typename Type> inline
{ ssize slot(HashTable<Type>& table, u64 key) {
for ( ssize idx = 0; idx < ssize(Hashes.num()); ++idx ) for (ssize idx = 0; idx < ssize(table.Hashes.num()); ++idx)
if ( Hashes[ idx ] == key ) if (table.Hashes[idx] == key)
return idx; return idx;
return -1; return -1;
} }
Array< ssize> Hashes; template<typename Type> inline
Array< Entry> Entries; ssize add_entry(HashTable<Type>& table, u64 key) {
protected:
ssize add_entry( u64 key )
{
ssize idx; ssize idx;
Entry entry = { key, -1 }; HashTableEntry<Type> entry = { key, -1 };
idx = Entries.num(); idx = table.Entries.num();
Entries.append( entry ); table.Entries.append(entry);
return idx; return idx;
} }
FindResult find( u64 key ) template<typename Type> inline
{ HashTableFindResult find(HashTable<Type>& table, u64 key)
FindResult result = { -1, -1, -1 }; {
HashTableFindResult result = { -1, -1, -1 };
if ( Hashes.num() > 0 ) if (table.Hashes.num() > 0)
{ {
result.HashIndex = key % Hashes.num(); result.HashIndex = key % table.Hashes.num();
result.EntryIndex = Hashes[ result.HashIndex ]; result.EntryIndex = table.Hashes[result.HashIndex];
while ( result.EntryIndex >= 0 ) while (result.EntryIndex >= 0)
{ {
if ( Entries[ result.EntryIndex ].Key == key ) if (table.Entries[result.EntryIndex].Key == key)
break; break;
result.PrevIndex = result.EntryIndex; result.PrevIndex = result.EntryIndex;
result.EntryIndex = Entries[ result.EntryIndex ].Next; result.EntryIndex = table.Entries[result.EntryIndex].Next;
} }
} }
return result; return result;
} }
b32 full() template<typename Type> inline
{ bool full(HashTable<Type>& table) {
usize critical_load = usize( CriticalLoadScale * f32(Hashes.num()) ); usize critical_load = usize(HashTable<Type>::CriticalLoadScale * f32(table.Hashes.num()));
b32 result = Entries.num() > critical_load; b32 result = table.Entries.num() > critical_load;
return result; return result;
} }
}; #pragma endregion HashTable
#pragma endregion Containers #pragma endregion Containers