verified the C hashtable has parity with the C++ templated gencpp hashtable.

This commit is contained in:
Edward R. Gonzalez 2024-12-05 23:02:26 -05:00
parent 63dd77237a
commit ec07c70dcf
8 changed files with 268 additions and 132 deletions

View File

@ -397,8 +397,9 @@ int gen_main()
containers.append( gen_array_base() );
containers.append( gen_array_generic_selection_interface());
containers.append( gen_hashtable_base() );
containers.append(fmt_newline);
containers.append( gen_hashtable_generic_selection_interface());
containers.append(array_ssize);
containers.append( def_pragma(code(endregion Containers)));

View File

@ -5,7 +5,7 @@
using namespace gen;
// Used to know what slot the array will be for generic selection
global s32 ArrayDefinitionCounter = 0;
global s32 Array_DefinitionCounter = 0;
CodeBody gen_array_base()
{
@ -359,8 +359,8 @@ CodeBody gen_array( StrC type, StrC array_name )
#pragma pop_macro( "typeof" )
#pragma pop_macro( "forceinline" )
++ ArrayDefinitionCounter;
StrC slot_str = String::fmt_buf(GlobalAllocator, "%d", ArrayDefinitionCounter).to_strc();
++ Array_DefinitionCounter;
StrC slot_str = String::fmt_buf(GlobalAllocator, "%d", Array_DefinitionCounter).to_strc();
Code generic_interface_slot = untyped_str(token_fmt( "type_delimiter", (StrC)array_type, "slot", (StrC)slot_str,
R"(#define GENERIC_SLOT_<slot>__array_init <type_delimiter>, <type_delimiter>_init
@ -398,19 +398,20 @@ R"(#define GENERIC_SLOT_<slot>__array_init <type_delimiter>, <type_deli
CodeBody gen_array_generic_selection_interface()
{
CodeBody interface_defines = def_body(CT_Global_Body);
interface_defines.append( gen_generic_selection_function_macro( ArrayDefinitionCounter, txt("array_init"), GenericSel_Direct_Type ));
interface_defines.append( gen_generic_selection_function_macro( ArrayDefinitionCounter, txt("array_init_reserve"), GenericSel_Direct_Type ));
interface_defines.append( gen_generic_selection_function_macro( ArrayDefinitionCounter, txt("array_append"), GenericSel_By_Ref ));
interface_defines.append( gen_generic_selection_function_macro( ArrayDefinitionCounter, txt("array_append_items"), GenericSel_By_Ref ));
interface_defines.append( gen_generic_selection_function_macro( ArrayDefinitionCounter, txt("array_back"), GenericSel_Default, GenericSel_One_Arg ));
interface_defines.append( gen_generic_selection_function_macro( ArrayDefinitionCounter, txt("array_clear"), GenericSel_Default, GenericSel_One_Arg ));
interface_defines.append( gen_generic_selection_function_macro( ArrayDefinitionCounter, txt("array_fill")) );
interface_defines.append( gen_generic_selection_function_macro( ArrayDefinitionCounter, txt("array_free"), GenericSel_By_Ref, GenericSel_One_Arg ) );
interface_defines.append( gen_generic_selection_function_macro( ArrayDefinitionCounter, txt("array_grow")) );
interface_defines.append( gen_generic_selection_function_macro( ArrayDefinitionCounter, txt("array_num")) );
interface_defines.append( gen_generic_selection_function_macro( ArrayDefinitionCounter, txt("array_pop"), GenericSel_Default, GenericSel_One_Arg ));
interface_defines.append( gen_generic_selection_function_macro( ArrayDefinitionCounter, txt("array_remove_at")) );
interface_defines.append( gen_generic_selection_function_macro( ArrayDefinitionCounter, txt("array_reserve"), GenericSel_By_Ref) );
interface_defines.append( gen_generic_selection_function_macro( ArrayDefinitionCounter, txt("array_set_capacity")) );
interface_defines.append( gen_generic_selection_function_macro( Array_DefinitionCounter, txt("array_init"), GenericSel_Direct_Type ));
interface_defines.append( gen_generic_selection_function_macro( Array_DefinitionCounter, txt("array_init_reserve"), GenericSel_Direct_Type ));
interface_defines.append( gen_generic_selection_function_macro( Array_DefinitionCounter, txt("array_append"), GenericSel_By_Ref ));
interface_defines.append( gen_generic_selection_function_macro( Array_DefinitionCounter, txt("array_append_items"), GenericSel_By_Ref ));
interface_defines.append( gen_generic_selection_function_macro( Array_DefinitionCounter, txt("array_back"), GenericSel_Default, GenericSel_One_Arg ));
interface_defines.append( gen_generic_selection_function_macro( Array_DefinitionCounter, txt("array_clear"), GenericSel_Default, GenericSel_One_Arg ));
interface_defines.append( gen_generic_selection_function_macro( Array_DefinitionCounter, txt("array_fill")) );
interface_defines.append( gen_generic_selection_function_macro( Array_DefinitionCounter, txt("array_free"), GenericSel_By_Ref, GenericSel_One_Arg ) );
interface_defines.append( gen_generic_selection_function_macro( Array_DefinitionCounter, txt("array_grow")) );
interface_defines.append( gen_generic_selection_function_macro( Array_DefinitionCounter, txt("array_num"), GenericSel_Default, GenericSel_One_Arg ));
interface_defines.append( gen_generic_selection_function_macro( Array_DefinitionCounter, txt("array_pop"), GenericSel_Default, GenericSel_One_Arg ));
interface_defines.append( gen_generic_selection_function_macro( Array_DefinitionCounter, txt("array_remove_at")) );
interface_defines.append( gen_generic_selection_function_macro( Array_DefinitionCounter, txt("array_reserve"), GenericSel_By_Ref) );
interface_defines.append( gen_generic_selection_function_macro( Array_DefinitionCounter, txt("array_resize"), GenericSel_By_Ref) );
interface_defines.append( gen_generic_selection_function_macro( Array_DefinitionCounter, txt("array_set_capacity")) );
return interface_defines;
}

View File

@ -5,11 +5,13 @@
using namespace gen;
global s32 HashTable_DefinitionCounter = 0;
CodeBody gen_hashtable_base()
{
CodeBody struct_def = parse_global_body( code(
typedef struct HT_FindResult HT_FindResult;
struct HT_FindResult
typedef struct HT_FindResult_Def HT_FindResult;
struct HT_FindResult_Def
{
ssize HashIndex;
ssize PrevIndex;
@ -22,7 +24,8 @@ R"(#define HashTable(_type) struct _type
)"
));
return def_global_body(args(struct_def, define_type));
Code define_critical_load_scale = untyped_str(txt("#define HashTable_CriticalLoadScale 0.7f\n"));
return def_global_body(args(struct_def, define_type, define_critical_load_scale));
}
CodeBody gen_hashtable( StrC type, StrC hashtable_name )
@ -46,8 +49,7 @@ CodeBody gen_hashtable( StrC type, StrC hashtable_name )
stringize(
typedef struct HashTable_<type> <tbl_type>;
typedef struct HTE_<tbl_name> HTE_<tbl_name>;
struct HTE_<tbl_name>
{
struct HTE_<tbl_name> {
u64 Key;
ssize Next;
<type> Value;
@ -61,8 +63,17 @@ CodeBody gen_hashtable( StrC type, StrC hashtable_name )
#pragma push_macro( "GEN_ASSERT" )
#pragma push_macro( "GEN_ASSERT_NOT_NULL" )
#pragma push_macro( "rcast" )
#pragma push_macro( "cast" )
#pragma push_macro( "typeof" )
#pragma push_macro( "forceinline" )
#undef GEN_ASSERT
#undef GEN_ASSERT_NOT_NULL
#undef GEN_ASSERT
#undef rcast
#undef cast
#undef typeof
#undef forceinline
CodeBody hashtable_def = parse_global_body( token_fmt(
"type", (StrC) type,
"tbl_name", (StrC) hashtable_name,
@ -72,146 +83,151 @@ CodeBody gen_hashtable( StrC type, StrC hashtable_name )
"array_entry", (StrC) entry_array_name,
"fn_array", (StrC) entry_array_fn_ns,
stringize(
struct HashTable_<type>
{
struct HashTable_<type> {
Array_ssize Hashes;
<array_entry> Entries;
};
<tbl_type> <fn>_make ( AllocatorInfo allocator );
<tbl_type> <fn>_make_reserve( AllocatorInfo allocator, ssize num );
void <fn>_clear ( <tbl_type> self );
void <fn>_destroy ( <tbl_type> self );
<type>* <fn>_get ( <tbl_type> self, u64 key );
void <fn>_map ( <tbl_type> self, <tbl_type>_MapProc map_proc );
void <fn>_map_mut ( <tbl_type> self, <tbl_type>_MapMutProc map_proc );
<tbl_type> <fn>_init ( AllocatorInfo allocator );
<tbl_type> <fn>_init_reserve( AllocatorInfo allocator, ssize num );
void <fn>_clear ( <tbl_type> self );
void <fn>_destroy ( <tbl_type>* self );
<type>* <fn>_get ( <tbl_type> self, u64 key );
void <fn>_map ( <tbl_type> self, <tbl_type>_MapProc map_proc );
void <fn>_map_mut ( <tbl_type> self, <tbl_type>_MapMutProc map_proc );
void <fn>_grow ( <tbl_type>* self );
void <fn>_rehash ( <tbl_type>* self, ssize new_num );
void <fn>_rehash_fast ( <tbl_type> self );
void <fn>_remove ( <tbl_type> self, u64 key );
void <fn>_remove_entry( <tbl_type> self, ssize idx );
void <fn>_rehash_fast ( <tbl_type> self );
void <fn>_remove ( <tbl_type> self, u64 key );
void <fn>_remove_entry( <tbl_type> self, ssize idx );
void <fn>_set ( <tbl_type>* self, u64 key, <type> value );
ssize <fn>_slot ( <tbl_type> self, u64 key );
ssize <fn>_slot ( <tbl_type> self, u64 key );
ssize <fn>__add_entry( <tbl_type> self, u64 key );
HT_FindResult <fn>__find ( <tbl_type> self, u64 key );
b32 <fn>__full ( <tbl_type> self );
ssize <fn>__add_entry( <tbl_type>* self, u64 key );
HT_FindResult <fn>__find ( <tbl_type> self, u64 key );
b32 <fn>__full ( <tbl_type> self );
<tbl_type> <fn>_make( AllocatorInfo allocator )
<tbl_type> <fn>init( AllocatorInfo allocator )
{
<tbl_type>
result = { NULL, NULL };
result.Hashes = array_init(Array_ssize, allocator );
result.Entries = array_init(<array_entry>, allocator );
<tbl_type> result = hashtable_init_reserve(<tbl_type>, allocator, 8);
return result;
}
<tbl_type> <fn>_make_reserve( AllocatorInfo allocator, ssize num )
<tbl_type> <fn>_init_reserve( AllocatorInfo allocator, ssize num )
{
<tbl_type>
result = { NULL, NULL };
<tbl_type> result = { NULL, NULL };
result.Hashes = array_init_reserve(Array_ssize, allocator, num );
result.Entries = array_init_reserve(<array_entry>, allocator, num );
array_get_header(result.Hashes)->Num = num;
array_resize(result.Hashes, num);
array_fill(result.Hashes, 0, num, -1);
result.Entries = array_init_reserve(<array_entry>, allocator, num );
return result;
}
void <fn>_clear( <tbl_type> self )
{
for ( ssize idx = 0; idx < array_get_header( self.Hashes )->Num; idx++ )
self.Hashes[idx] = -1;
array_clear( self.Hashes );
GEN_ASSERT_NOT_NULL(self.Hashes);
GEN_ASSERT_NOT_NULL(self.Entries);
array_clear( self.Entries );
s32 what = array_num(self.Hashes);
array_fill( self.Hashes, 0, what, (ssize)-1 );
}
void <fn>_destroy( <tbl_type> self )
void <fn>_destroy( <tbl_type>* self )
{
if ( self.Hashes && self.Entries )
{
array_free( self.Hashes );
array_free( self.Entries );
GEN_ASSERT_NOT_NULL(self);
GEN_ASSERT_NOT_NULL(self->Hashes);
GEN_ASSERT_NOT_NULL(self->Entries);
if ( self->Hashes && array_get_header(self->Hashes)->Capacity) {
array_free( self->Hashes );
array_free( self->Entries );
}
}
<type>* <fn>_get( <tbl_type> self, u64 key )
{
GEN_ASSERT_NOT_NULL(self.Hashes);
GEN_ASSERT_NOT_NULL(self.Entries);
ssize idx = <fn>__find( self, key ).EntryIndex;
if ( idx > 0 )
return & self.Entries[idx].Value;
return NULL;
return nullptr;
}
void <fn>_map( <tbl_type> self, <tbl_type>_MapProc map_proc )
{
GEN_ASSERT_NOT_NULL(self.Hashes);
GEN_ASSERT_NOT_NULL(self.Entries);
GEN_ASSERT_NOT_NULL( map_proc );
for ( ssize idx = 0; idx < array_get_header( self.Entries )->Num; idx++ )
{
for ( ssize idx = 0; idx < array_get_header( self.Entries )->Num; idx++ ) {
map_proc( self, self.Entries[idx].Key, self.Entries[idx].Value );
}
}
void <fn>_map_mut( <tbl_type> self, <tbl_type>_MapMutProc map_proc )
{
GEN_ASSERT_NOT_NULL(self.Hashes);
GEN_ASSERT_NOT_NULL(self.Entries);
GEN_ASSERT_NOT_NULL( map_proc );
for ( ssize idx = 0; idx < array_get_header( self.Entries )->Num; idx++ )
{
for ( ssize idx = 0; idx < array_get_header( self.Entries )->Num; idx++ ) {
map_proc( self, self.Entries[idx].Key, & self.Entries[idx].Value );
}
}
void <fn>_grow( <tbl_type>* self )
{
GEN_ASSERT_NOT_NULL(self);
GEN_ASSERT_NOT_NULL(self->Hashes);
GEN_ASSERT_NOT_NULL(self->Entries);
ssize new_num = array_grow_formula( array_get_header( self->Entries )->Num );
<fn>_rehash( self, new_num );
hashtable_rehash( self, new_num );
}
void <fn>_rehash( <tbl_type>* self, ssize new_num )
{
GEN_ASSERT_NOT_NULL(self);
GEN_ASSERT_NOT_NULL(self->Hashes);
GEN_ASSERT_NOT_NULL(self->Entries);
GEN_ASSERT( new_num > 0 );
ssize idx;
ssize last_added_index;
ArrayHeader* old_hash_header = array_get_header( self->Hashes );
ArrayHeader* old_entries_header = array_get_header( self->Entries );
<tbl_type> new_tbl = <fn>_make_reserve( old_hash_header->Allocator, old_hash_header->Num );
<tbl_type> new_tbl = hashtable_init_reserve( <tbl_type>, old_hash_header->Allocator, old_hash_header->Num );
ArrayHeader* new_hash_header = array_get_header( new_tbl.Hashes );
for ( idx = 0; idx < new_hash_header->Num; idx++ )
new_tbl.Hashes[idx] = -1;
for ( idx = 0; idx < old_entries_header->Num; idx++ )
for (ssize idx = 0; idx < cast(ssize, old_hash_header->Num); ++idx)
{
<entry_type>* entry;
HT_FindResult find_result;
<entry_type>* entry = & self->Entries[idx];
HT_FindResult find_result;
if ( new_hash_header->Num == 0 )
<fn>_grow( & new_tbl );
find_result = <fn>__find( new_tbl, entry->Key);
last_added_index = <fn>__add_entry( & new_tbl, entry->Key);
entry = & self->Entries[ idx ];
find_result = <fn>__find( new_tbl, entry->Key );
last_added_index = <fn>__add_entry( new_tbl, entry->Key );
if ( find_result.PrevIndex < 0 )
new_tbl.Hashes[ find_result.HashIndex ] = last_added_index;
if (find_result.PrevIndex < 0)
new_tbl.Hashes[find_result.HashIndex] = last_added_index;
else
new_tbl.Entries[ find_result.PrevIndex ].Next = last_added_index;
new_tbl.Entries[find_result.PrevIndex].Next = last_added_index;
new_tbl.Entries[ last_added_index ].Next = find_result.EntryIndex;
new_tbl.Entries[ last_added_index ].Value = entry->Value;
new_tbl.Entries[last_added_index].Next = find_result.EntryIndex;
new_tbl.Entries[last_added_index].Value = entry->Value;
}
<fn>_destroy( *self );
<fn>_destroy( self );
* self = new_tbl;
}
void <fn>_rehash_fast( <tbl_type> self )
{
GEN_ASSERT_NOT_NULL(self.Hashes);
GEN_ASSERT_NOT_NULL(self.Entries);
ssize idx;
for ( idx = 0; idx < array_get_header( self.Entries )->Num; idx++ )
@ -237,44 +253,47 @@ CodeBody gen_hashtable( StrC type, StrC hashtable_name )
void <fn>_remove( <tbl_type> self, u64 key )
{
GEN_ASSERT_NOT_NULL(self.Hashes);
GEN_ASSERT_NOT_NULL(self.Entries);
HT_FindResult find_result = <fn>__find( self, key );
if ( find_result.EntryIndex >= 0 )
{
if ( find_result.EntryIndex >= 0 ) {
array_remove_at( self.Entries, find_result.EntryIndex );
<fn>_rehash_fast( self );
hashtable_rehash_fast( self );
}
}
void <fn>_remove_entry( <tbl_type> self, ssize idx )
{
GEN_ASSERT_NOT_NULL(self.Hashes);
GEN_ASSERT_NOT_NULL(self.Entries);
array_remove_at( self.Entries, idx );
}
void <fn>_set( <tbl_type>* self, u64 key, <type> value )
{
GEN_ASSERT_NOT_NULL(self);
GEN_ASSERT_NOT_NULL(self->Hashes);
GEN_ASSERT_NOT_NULL(self->Entries);
ssize idx;
HT_FindResult find_result;
if ( array_get_header( self->Hashes )->Num == 0 )
<fn>_grow( self );
hashtable_grow( self );
find_result = <fn>__find( * self, key );
if ( find_result.EntryIndex >= 0 )
{
if ( find_result.EntryIndex >= 0 ) {
idx = find_result.EntryIndex;
}
else
{
idx = <fn>__add_entry( * self, key );
idx = <fn>__add_entry( self, key );
if ( find_result.PrevIndex >= 0 )
{
if ( find_result.PrevIndex >= 0 ) {
self->Entries[ find_result.PrevIndex ].Next = idx;
}
else
{
else {
self->Hashes[ find_result.HashIndex ] = idx;
}
}
@ -282,11 +301,13 @@ CodeBody gen_hashtable( StrC type, StrC hashtable_name )
self->Entries[ idx ].Value = value;
if ( <fn>__full( * self ) )
<fn>_grow( self );
hashtable_grow( self );
}
ssize <fn>_slot( <tbl_type> self, u64 key )
{
GEN_ASSERT_NOT_NULL(self.Hashes);
GEN_ASSERT_NOT_NULL(self.Entries);
for ( ssize idx = 0; idx < array_get_header( self.Hashes )->Num; ++idx )
if ( self.Hashes[ idx ] == key )
return idx;
@ -294,22 +315,26 @@ CodeBody gen_hashtable( StrC type, StrC hashtable_name )
return -1;
}
ssize <fn>__add_entry( <tbl_type> self, u64 key )
ssize <fn>__add_entry( <tbl_type>* self, u64 key )
{
GEN_ASSERT_NOT_NULL(self);
GEN_ASSERT_NOT_NULL(self->Hashes);
GEN_ASSERT_NOT_NULL(self->Entries);
ssize idx;
<entry_type> entry = { key, -1 };
idx = array_get_header( self.Entries )->Num;
array_append( self.Entries, entry );
idx = array_get_header( self->Entries )->Num;
array_append( self->Entries, entry );
return idx;
}
HT_FindResult <fn>__find( <tbl_type> self, u64 key )
{
GEN_ASSERT_NOT_NULL(self.Hashes);
GEN_ASSERT_NOT_NULL(self.Entries);
HT_FindResult result = { -1, -1, -1 };
ArrayHeader* hash_header = array_get_header( self.Hashes );
if ( hash_header->Num > 0 )
{
result.HashIndex = key % hash_header->Num;
@ -330,14 +355,46 @@ CodeBody gen_hashtable( StrC type, StrC hashtable_name )
b32 <fn>__full( <tbl_type> self )
{
GEN_ASSERT_NOT_NULL(self.Hashes);
GEN_ASSERT_NOT_NULL(self.Entries);
ArrayHeader* hash_header = array_get_header( self.Hashes );
ArrayHeader* entries_header = array_get_header( self.Entries );
return 0.75f * hash_header->Num < entries_header->Num;
usize critical_load = cast(usize, HashTable_CriticalLoadScale * cast(f32, hash_header->Num));
b32 result = entries_header->Num > critical_load;
return result;
}
)));
#pragma pop_macro( "GEN_ASSERT" )
#pragma pop_macro( "GEN_ASSERT_NOT_NULL" )
#pragma pop_macro( "rcast" )
#pragma pop_macro( "cast" )
#pragma pop_macro( "typeof" )
#pragma pop_macro( "forceinline" )
++ HashTable_DefinitionCounter;
StrC slot_str = String::fmt_buf(GlobalAllocator, "%d", Array_DefinitionCounter).to_strc();
Code generic_interface_slot = untyped_str(token_fmt( "type_delimiter", (StrC)tbl_type, "slot", (StrC)slot_str,
R"(#define GENERIC_SLOT_<slot>__hashtable_init <type_delimiter>, <type_delimiter>_init
#define GENERIC_SLOT_<slot>__hashtable_init_reserve <type_delimiter>, <type_delimiter>_init_reserve
#define GENERIC_SLOT_<slot>__hashtable_clear <type_delimiter>, <type_delimiter>_clear
#define GENERIC_SLOT_<slot>__hashtable_destroy <type_delimiter>*, <type_delimiter>_destroy
#define GENERIC_SLOT_<slot>__hashtable_get <type_delimiter>, <type_delimiter>_get
#define GENERIC_SLOT_<slot>__hashtable_map <type_delimiter>, <type_delimiter>_map
#define GENERIC_SLOT_<slot>__hashtable_map_mut <type_delimiter>, <type_delimiter>_map_mut
#define GENERIC_SLOT_<slot>__hashtable_grow <type_delimiter>*, <type_delimiter>_grow
#define GENERIC_SLOT_<slot>__hashtable_rehash <type_delimiter>*, <type_delimiter>_rehash
#define GENERIC_SLOT_<slot>__hashtable_rehash_fast <type_delimiter>, <type_delimiter>_rehash_fast
#define GENERIC_SLOT_<slot>__hashtable_remove_entry <type_delimiter>, <type_delimiter>_remove_entry
#define GENERIC_SLOT_<slot>__hashtable_set <type_delimiter>*, <type_delimiter>_set
#define GENERIC_SLOT_<slot>__hashtable_slot <type_delimiter>, <type_delimiter>_slot
#define GENERIC_SLOT_<slot>__hashtable__add_entry <type_delimiter>*, <type_delimiter>__add_entry
#define GENERIC_SLOT_<slot>__hashtable__find <type_delimiter>, <type_delimiter>__find
#define GENERIC_SLOT_<slot>__hashtable__full <type_delimiter>, <type_delimiter>__full
)"
));
char const* cmt_str = str_fmt_buf( "Name: %.*s Type: %.*s"
, tbl_type.length(), tbl_type.Data
@ -346,6 +403,8 @@ CodeBody gen_hashtable( StrC type, StrC hashtable_name )
return def_global_body(args(
def_pragma( string_to_strc( string_fmt_buf( GlobalAllocator, "region %S", tbl_type ))),
fmt_newline,
generic_interface_slot,
fmt_newline,
hashtable_types,
fmt_newline,
entry_array,
@ -355,3 +414,21 @@ CodeBody gen_hashtable( StrC type, StrC hashtable_name )
fmt_newline
));
}
CodeBody gen_hashtable_generic_selection_interface()
{
CodeBody interface_defines = def_body(CT_Global_Body);
interface_defines.append( gen_generic_selection_function_macro( HashTable_DefinitionCounter, txt("hashtable_init"), GenericSel_Direct_Type ));
interface_defines.append( gen_generic_selection_function_macro( HashTable_DefinitionCounter, txt("hashtable_init_reserve"), GenericSel_Direct_Type ));
interface_defines.append( gen_generic_selection_function_macro( HashTable_DefinitionCounter, txt("hashtable_clear"), GenericSel_Default, GenericSel_One_Arg ));
interface_defines.append( gen_generic_selection_function_macro( HashTable_DefinitionCounter, txt("hashtable_destroy"), GenericSel_By_Ref, GenericSel_One_Arg ) );
interface_defines.append( gen_generic_selection_function_macro( HashTable_DefinitionCounter, txt("hashtable_get") ));
interface_defines.append( gen_generic_selection_function_macro( HashTable_DefinitionCounter, txt("hashtable_grow"), GenericSel_Default, GenericSel_One_Arg ));
interface_defines.append( gen_generic_selection_function_macro( HashTable_DefinitionCounter, txt("hashtable_rehash") ));
interface_defines.append( gen_generic_selection_function_macro( HashTable_DefinitionCounter, txt("hashtable_rehash_fast"), GenericSel_Default, GenericSel_One_Arg ));
interface_defines.append( gen_generic_selection_function_macro( HashTable_DefinitionCounter, txt("hashtable_remove") ));
interface_defines.append( gen_generic_selection_function_macro( HashTable_DefinitionCounter, txt("hashtable_remove_entry") ));
interface_defines.append( gen_generic_selection_function_macro( HashTable_DefinitionCounter, txt("hashtable_set"), GenericSel_By_Ref ));
interface_defines.append( gen_generic_selection_function_macro( HashTable_DefinitionCounter, txt("hashtable_slot") ));
return interface_defines;
}

View File

@ -96,6 +96,14 @@ R"(#define <macro_name>(selector_arg, ...) _Generic( (selector_arg), \
R"( GEN_IF_MACRO_DEFINED_INCLUDE_THIS_SLOT_LAST( GENERIC_SLOT_<slot>__<macro_name> ) \
)"
));
// if ( one_arg )
// define_builder.append(token_fmt( "macro_name", macro_name, stringize(
// default: static_assert(false, "<macro_name>: Failed to select correct function signature (Did you pass the type?)")
// )));
// else
// define_builder.append(token_fmt( "macro_name", macro_name, stringize(
// default: static_assert(false, "<macro_name>: Failed to select correct function signature")
// )));
continue;
}

View File

@ -481,12 +481,13 @@ template<class Type> void hashtable_remove (HashTable<Type
template<class Type> void hashtable_remove_entry(HashTable<Type> table, ssize idx);
template<class Type> void hashtable_set (HashTable<Type>* table, u64 key, Type value);
template<class Type> ssize hashtable_slot (HashTable<Type> table, u64 key);
template<class Type> ssize hashtable_add_entry (HashTable<Type>* table, u64 key);
template<class Type> HashTableFindResult hashtable_find (HashTable<Type> table, u64 key);
template<class Type> bool hashtable_full (HashTable<Type> table);
template<class Type> void hashtable_map (HashTable<Type> table, void (*map_proc)(u64 key, Type value));
template<class Type> void hashtable_map_mut (HashTable<Type> table, void (*map_proc)(u64 key, Type* value));
template<class Type> ssize hashtable__add_entry (HashTable<Type>* table, u64 key);
template<class Type> HashTableFindResult hashtable__find (HashTable<Type> table, u64 key);
template<class Type> bool hashtable__full (HashTable<Type> table);
static constexpr f32 HashTable_CriticalLoadScale = 0.7f;
template<typename Type>
@ -540,37 +541,45 @@ HashTable<Type> hashtable_init_reserve(AllocatorInfo allocator, usize num)
result.Hashes = array_init_reserve<ssize>(allocator, num);
array_get_header(result.Hashes)->Num = num;
array_resize(& result.Hashes, num);
array_fill<ssize>(result.Hashes, 0, num, -1);
array_fill(result.Hashes, 0, num, (ssize)-1);
result.Entries = array_init_reserve<HashTableEntry<Type>>(allocator, num);
return result;
}
template<typename Type> inline
template<typename Type> forceinline
void hashtable_clear(HashTable<Type> table) {
GEN_ASSERT_NOT_NULL(table.Hashes);
GEN_ASSERT_NOT_NULL(table.Entries);
array_clear(table.Entries);
array_fill(table.Hashes, 0, array_num(table.Hashes), (ssize)-1);
}
template<typename Type> inline
template<typename Type> forceinline
void hashtable_destroy(HashTable<Type>* table) {
GEN_ASSERT_NOT_NULL(table->Hashes);
GEN_ASSERT_NOT_NULL(table->Entries);
if (table->Hashes && array_get_header(table->Hashes)->Capacity) {
array_free(table->Hashes);
array_free(table->Entries);
}
}
template<typename Type> inline
template<typename Type> forceinline
Type* hashtable_get(HashTable<Type> table, u64 key) {
ssize idx = hashtable_find(table, key).EntryIndex;
GEN_ASSERT_NOT_NULL(table.Hashes);
GEN_ASSERT_NOT_NULL(table.Entries);
ssize idx = hashtable__find(table, key).EntryIndex;
if (idx >= 0)
return & table.Entries[idx].Value;
return nullptr;
}
template<typename Type> inline
template<typename Type> forceinline
void hashtable_map(HashTable<Type> table, void (*map_proc)(u64 key, Type value)) {
GEN_ASSERT_NOT_NULL(table.Hashes);
GEN_ASSERT_NOT_NULL(table.Entries);
GEN_ASSERT_NOT_NULL(map_proc);
for (ssize idx = 0; idx < ssize(num(table.Entries)); ++idx) {
@ -578,8 +587,10 @@ void hashtable_map(HashTable<Type> table, void (*map_proc)(u64 key, Type value))
}
}
template<typename Type> inline
template<typename Type> forceinline
void hashtable_map_mut(HashTable<Type> table, void (*map_proc)(u64 key, Type* value)) {
GEN_ASSERT_NOT_NULL(table.Hashes);
GEN_ASSERT_NOT_NULL(table.Entries);
GEN_ASSERT_NOT_NULL(map_proc);
for (ssize idx = 0; idx < ssize(num(table.Entries)); ++idx) {
@ -587,8 +598,11 @@ void hashtable_map_mut(HashTable<Type> table, void (*map_proc)(u64 key, Type* va
}
}
template<typename Type> inline
template<typename Type> forceinline
void hashtable_grow(HashTable<Type>* table) {
GEN_ASSERT_NOT_NULL(table);
GEN_ASSERT_NOT_NULL(table->Hashes);
GEN_ASSERT_NOT_NULL(table->Entries);
ssize new_num = array_grow_formula( array_num(table->Entries));
hashtable_rehash(table, new_num);
}
@ -596,6 +610,9 @@ void hashtable_grow(HashTable<Type>* table) {
template<typename Type> inline
void hashtable_rehash(HashTable<Type>* table, ssize new_num)
{
GEN_ASSERT_NOT_NULL(table);
GEN_ASSERT_NOT_NULL(table->Hashes);
GEN_ASSERT_NOT_NULL(table->Entries);
ssize last_added_index;
HashTable<Type> new_ht = hashtable_init_reserve<Type>( array_get_header(table->Hashes)->Allocator, new_num);
@ -604,8 +621,8 @@ void hashtable_rehash(HashTable<Type>* table, ssize new_num)
HashTableFindResult find_result;
HashTableEntry<Type>& entry = table->Entries[idx];
find_result = hashtable_find(new_ht, entry.Key);
last_added_index = hashtable_add_entry(& new_ht, entry.Key);
find_result = hashtable__find(new_ht, entry.Key);
last_added_index = hashtable__add_entry(& new_ht, entry.Key);
if (find_result.PrevIndex < 0)
new_ht.Hashes[find_result.HashIndex] = last_added_index;
@ -623,6 +640,8 @@ void hashtable_rehash(HashTable<Type>* table, ssize new_num)
template<typename Type> inline
void hashtable_rehash_fast(HashTable<Type> table)
{
GEN_ASSERT_NOT_NULL(table.Hashes);
GEN_ASSERT_NOT_NULL(table.Entries);
ssize idx;
for (idx = 0; idx < ssize(num(table.Entries)); idx++)
@ -646,8 +665,10 @@ void hashtable_rehash_fast(HashTable<Type> table)
}
}
template<typename Type> inline
template<typename Type> forceinline
void hashtable_remove(HashTable<Type> table, u64 key) {
GEN_ASSERT_NOT_NULL(table.Hashes);
GEN_ASSERT_NOT_NULL(table.Entries);
HashTableFindResult find_result = find(table, key);
if (find_result.EntryIndex >= 0) {
@ -656,27 +677,32 @@ void hashtable_remove(HashTable<Type> table, u64 key) {
}
}
template<typename Type> inline
template<typename Type> forceinline
void hashtable_remove_entry(HashTable<Type> table, ssize idx) {
GEN_ASSERT_NOT_NULL(table.Hashes);
GEN_ASSERT_NOT_NULL(table.Entries);
remove_at(table.Entries, idx);
}
template<typename Type> inline
void hashtable_set(HashTable<Type>* table, u64 key, Type value)
{
GEN_ASSERT_NOT_NULL(table);
GEN_ASSERT_NOT_NULL(table->Hashes);
GEN_ASSERT_NOT_NULL(table->Entries);
ssize idx;
HashTableFindResult find_result;
if (hashtable_full(* table))
hashtable_grow(table);
find_result = hashtable_find(* table, key);
find_result = hashtable__find(* table, key);
if (find_result.EntryIndex >= 0) {
idx = find_result.EntryIndex;
}
else
{
idx = hashtable_add_entry(table, key);
idx = hashtable__add_entry(table, key);
if (find_result.PrevIndex >= 0) {
table->Entries[find_result.PrevIndex].Next = idx;
@ -692,8 +718,10 @@ void hashtable_set(HashTable<Type>* table, u64 key, Type value)
hashtable_grow(table);
}
template<typename Type> inline
template<typename Type> forceinline
ssize hashtable_slot(HashTable<Type> table, u64 key) {
GEN_ASSERT_NOT_NULL(table.Hashes);
GEN_ASSERT_NOT_NULL(table.Entries);
for (ssize idx = 0; idx < ssize(num(table.Hashes)); ++idx)
if (table.Hashes[idx] == key)
return idx;
@ -701,8 +729,11 @@ ssize hashtable_slot(HashTable<Type> table, u64 key) {
return -1;
}
template<typename Type> inline
ssize hashtable_add_entry(HashTable<Type>* table, u64 key) {
template<typename Type> forceinline
ssize hashtable__add_entry(HashTable<Type>* table, u64 key) {
GEN_ASSERT_NOT_NULL(table);
GEN_ASSERT_NOT_NULL(table->Hashes);
GEN_ASSERT_NOT_NULL(table->Entries);
ssize idx;
HashTableEntry<Type> entry = { key, -1 };
@ -712,8 +743,10 @@ ssize hashtable_add_entry(HashTable<Type>* table, u64 key) {
}
template<typename Type> inline
HashTableFindResult hashtable_find(HashTable<Type> table, u64 key)
HashTableFindResult hashtable__find(HashTable<Type> table, u64 key)
{
GEN_ASSERT_NOT_NULL(table.Hashes);
GEN_ASSERT_NOT_NULL(table.Entries);
HashTableFindResult result = { -1, -1, -1 };
if (array_num(table.Hashes) > 0)
@ -734,8 +767,10 @@ HashTableFindResult hashtable_find(HashTable<Type> table, u64 key)
return result;
}
template<typename Type> inline
template<typename Type> forceinline
bool hashtable_full(HashTable<Type> table) {
GEN_ASSERT_NOT_NULL(table.Hashes);
GEN_ASSERT_NOT_NULL(table.Entries);
usize critical_load = usize(HashTable_CriticalLoadScale * f32(array_num(table.Hashes)));
b32 result = array_num(table.Entries) > critical_load;
return result;
@ -753,12 +788,13 @@ bool hashtable_full(HashTable<Type> table) {
#define hashtable_remove_entry(table, idx) hashtable_remove_entry< get_hashtable_underlying_type(table) >(table, idx)
#define hashtable_set(table, key, value) hashtable_set < get_hashtable_underlying_type(table) >(& table, key, value)
#define hashtable_slot(table, key) hashtable_slot < get_hashtable_underlying_type(table) >(table, key)
#define hashtable_add_entry(table, key) hashtable_add_entry < get_hashtable_underlying_type(table) >(& table, key)
#define hashtable_find(table, key) hashtable_find < get_hashtable_underlying_type(table) >(table, key)
#define hashtable_full(table) hashtable_full < get_hashtable_underlying_type(table) >(table)
#define hashtable_map(table, map_proc) hashtable_map < get_hashtable_underlying_type(table) >(table, map_proc)
#define hashtable_map_mut(table, map_proc) hashtable_map_mut < get_hashtable_underlying_type(table) >(table, map_proc)
//#define hashtable_add_entry(table, key) hashtable_add_entry < get_hashtable_underlying_type(table) >(& table, key)
//#define hashtable_find(table, key) hashtable_find < get_hashtable_underlying_type(table) >(table, key)
//#define hashtable_full(table) hashtable_full < get_hashtable_underlying_type(table) >(table)
#pragma endregion HashTable
#pragma endregion Containers

View File

@ -187,6 +187,17 @@
# endif
#endif
#if GEN_COMPILER_C
#ifndef static_assert
#undef static_assert
#if GEN_COMPILER_C && __STDC_VERSION__ >= 201112L
#define static_assert(condition, message) _Static_assert(condition, message)
#else
#define static_assert(condition, message) typedef char static_assertion_##__LINE__[(condition)?1:-1]
#endif
#endif
#endif
#if GEN_COMPILER_CPP
// Already Defined
#elif GEN_COMPILER_C && __STDC_VERSION__ >= 201112L
@ -277,7 +288,7 @@
// Extensive effort was put in below to make this as easy as possible to understand what is going on with this mess of a preoprocessor.
// Where the signature would be defined using:
#define GEN_TYPE_TO_EXP(type) (type*)NULL
#define GEN_TYPE_TO_EXP(type) (* (type*)NULL)
#define GEN_COMMA_OPERATOR , // The comma operator is used by preprocessor macros to delimit arguments, so we have to represent it via a macro to prevent parsing incorrectly.

View File

@ -31,8 +31,9 @@
#undef hashtable_remove_entry
#undef hashtable_set
#undef hashtable_slot
#undef hashtable_add_entry
#undef hashtable_find
#undef hashtable_full
#undef hashtable_map
#undef hashtable_map_mut
//#undef hashtable_add_entry
//#undef hashtable_find
//#undef hashtable_full

View File

@ -31,8 +31,9 @@
#define hashtable_remove_entry(table, idx) hashtable_remove_entry< get_hashtable_underlying_type(table) >(table, idx)
#define hashtable_set(table, key, value) hashtable_set < get_hashtable_underlying_type(table) >(& table, key, value)
#define hashtable_slot(table, key) hashtable_slot < get_hashtable_underlying_type(table) >(table, key)
#define hashtable_add_entry(table, key) hashtable_add_entry < get_hashtable_underlying_type(table) >(& table, key)
#define hashtable_find(table, key) hashtable_find < get_hashtable_underlying_type(table) >(table, key)
#define hashtable_full(table) hashtable_full < get_hashtable_underlying_type(table) >(table)
#define hashtable_map(table, map_proc) hashtable_map < get_hashtable_underlying_type(table) >(table, map_proc)
#define hashtable_map_mut(table, map_proc) hashtable_map_mut < get_hashtable_underlying_type(table) >(table, map_proc)
//#define hashtable_add_entry(table, key) hashtable_add_entry < get_hashtable_underlying_type(table) >(& table, key)
//#define hashtable_find(table, key) hashtable_find < get_hashtable_underlying_type(table) >(table, key)
//#define hashtable_full(table) hashtable_full < get_hashtable_underlying_type(table) >(table)