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_base() );
containers.append( gen_array_generic_selection_interface()); containers.append( gen_array_generic_selection_interface());
containers.append( gen_hashtable_base() ); containers.append( gen_hashtable_base() );
containers.append(fmt_newline); containers.append(fmt_newline);
containers.append( gen_hashtable_generic_selection_interface());
containers.append(array_ssize); containers.append(array_ssize);
containers.append( def_pragma(code(endregion Containers))); containers.append( def_pragma(code(endregion Containers)));

View File

@ -5,7 +5,7 @@
using namespace gen; using namespace gen;
// Used to know what slot the array will be for generic selection // 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() CodeBody gen_array_base()
{ {
@ -359,8 +359,8 @@ CodeBody gen_array( StrC type, StrC array_name )
#pragma pop_macro( "typeof" ) #pragma pop_macro( "typeof" )
#pragma pop_macro( "forceinline" ) #pragma pop_macro( "forceinline" )
++ ArrayDefinitionCounter; ++ Array_DefinitionCounter;
StrC slot_str = String::fmt_buf(GlobalAllocator, "%d", ArrayDefinitionCounter).to_strc(); 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, 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 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 gen_array_generic_selection_interface()
{ {
CodeBody interface_defines = def_body(CT_Global_Body); 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( Array_DefinitionCounter, 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( Array_DefinitionCounter, 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( Array_DefinitionCounter, 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( Array_DefinitionCounter, 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( Array_DefinitionCounter, 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( Array_DefinitionCounter, 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( Array_DefinitionCounter, 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( Array_DefinitionCounter, 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( Array_DefinitionCounter, txt("array_grow")) );
interface_defines.append( gen_generic_selection_function_macro( ArrayDefinitionCounter, txt("array_num")) ); 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( ArrayDefinitionCounter, txt("array_pop"), 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( ArrayDefinitionCounter, txt("array_remove_at")) ); interface_defines.append( gen_generic_selection_function_macro( Array_DefinitionCounter, 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( Array_DefinitionCounter, 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_resize"), GenericSel_By_Ref) );
interface_defines.append( gen_generic_selection_function_macro( Array_DefinitionCounter, txt("array_set_capacity")) );
return interface_defines; return interface_defines;
} }

View File

@ -5,11 +5,13 @@
using namespace gen; using namespace gen;
global s32 HashTable_DefinitionCounter = 0;
CodeBody gen_hashtable_base() CodeBody gen_hashtable_base()
{ {
CodeBody struct_def = parse_global_body( code( CodeBody struct_def = parse_global_body( code(
typedef struct HT_FindResult HT_FindResult; typedef struct HT_FindResult_Def HT_FindResult;
struct HT_FindResult struct HT_FindResult_Def
{ {
ssize HashIndex; ssize HashIndex;
ssize PrevIndex; 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 ) CodeBody gen_hashtable( StrC type, StrC hashtable_name )
@ -46,8 +49,7 @@ CodeBody gen_hashtable( StrC type, StrC hashtable_name )
stringize( stringize(
typedef struct HashTable_<type> <tbl_type>; typedef struct HashTable_<type> <tbl_type>;
typedef struct HTE_<tbl_name> HTE_<tbl_name>; typedef struct HTE_<tbl_name> HTE_<tbl_name>;
struct HTE_<tbl_name> struct HTE_<tbl_name> {
{
u64 Key; u64 Key;
ssize Next; ssize Next;
<type> Value; <type> Value;
@ -61,8 +63,17 @@ CodeBody gen_hashtable( StrC type, StrC hashtable_name )
#pragma push_macro( "GEN_ASSERT" ) #pragma push_macro( "GEN_ASSERT" )
#pragma push_macro( "GEN_ASSERT_NOT_NULL" ) #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
#undef GEN_ASSERT_NOT_NULL #undef GEN_ASSERT_NOT_NULL
#undef GEN_ASSERT
#undef rcast
#undef cast
#undef typeof
#undef forceinline
CodeBody hashtable_def = parse_global_body( token_fmt( CodeBody hashtable_def = parse_global_body( token_fmt(
"type", (StrC) type, "type", (StrC) type,
"tbl_name", (StrC) hashtable_name, "tbl_name", (StrC) hashtable_name,
@ -72,146 +83,151 @@ CodeBody gen_hashtable( StrC type, StrC hashtable_name )
"array_entry", (StrC) entry_array_name, "array_entry", (StrC) entry_array_name,
"fn_array", (StrC) entry_array_fn_ns, "fn_array", (StrC) entry_array_fn_ns,
stringize( stringize(
struct HashTable_<type> struct HashTable_<type> {
{
Array_ssize Hashes; Array_ssize Hashes;
<array_entry> Entries; <array_entry> Entries;
}; };
<tbl_type> <fn>_make ( AllocatorInfo allocator ); <tbl_type> <fn>_init ( AllocatorInfo allocator );
<tbl_type> <fn>_make_reserve( AllocatorInfo allocator, ssize num ); <tbl_type> <fn>_init_reserve( AllocatorInfo allocator, ssize num );
void <fn>_clear ( <tbl_type> self ); void <fn>_clear ( <tbl_type> self );
void <fn>_destroy ( <tbl_type> self ); void <fn>_destroy ( <tbl_type>* self );
<type>* <fn>_get ( <tbl_type> self, u64 key ); <type>* <fn>_get ( <tbl_type> self, u64 key );
void <fn>_map ( <tbl_type> self, <tbl_type>_MapProc map_proc ); 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>_map_mut ( <tbl_type> self, <tbl_type>_MapMutProc map_proc );
void <fn>_grow ( <tbl_type>* self ); void <fn>_grow ( <tbl_type>* self );
void <fn>_rehash ( <tbl_type>* self, ssize new_num ); void <fn>_rehash ( <tbl_type>* self, ssize new_num );
void <fn>_rehash_fast ( <tbl_type> self ); void <fn>_rehash_fast ( <tbl_type> self );
void <fn>_remove ( <tbl_type> self, u64 key ); void <fn>_remove ( <tbl_type> self, u64 key );
void <fn>_remove_entry( <tbl_type> self, ssize idx ); void <fn>_remove_entry( <tbl_type> self, ssize idx );
void <fn>_set ( <tbl_type>* self, u64 key, <type> value ); 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 ); ssize <fn>__add_entry( <tbl_type>* self, u64 key );
HT_FindResult <fn>__find ( <tbl_type> self, u64 key ); HT_FindResult <fn>__find ( <tbl_type> self, u64 key );
b32 <fn>__full ( <tbl_type> self ); b32 <fn>__full ( <tbl_type> self );
<tbl_type> <fn>_make( AllocatorInfo allocator ) <tbl_type> <fn>init( AllocatorInfo allocator )
{ {
<tbl_type> <tbl_type> result = hashtable_init_reserve(<tbl_type>, allocator, 8);
result = { NULL, NULL };
result.Hashes = array_init(Array_ssize, allocator );
result.Entries = array_init(<array_entry>, allocator );
return result; return result;
} }
<tbl_type> <fn>_make_reserve( AllocatorInfo allocator, ssize num ) <tbl_type> <fn>_init_reserve( AllocatorInfo allocator, ssize num )
{ {
<tbl_type> <tbl_type> result = { NULL, NULL };
result = { NULL, NULL };
result.Hashes = array_init_reserve(Array_ssize, allocator, num ); 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; return result;
} }
void <fn>_clear( <tbl_type> self ) void <fn>_clear( <tbl_type> self )
{ {
for ( ssize idx = 0; idx < array_get_header( self.Hashes )->Num; idx++ ) GEN_ASSERT_NOT_NULL(self.Hashes);
self.Hashes[idx] = -1; GEN_ASSERT_NOT_NULL(self.Entries);
array_clear( self.Hashes );
array_clear( 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 ) GEN_ASSERT_NOT_NULL(self);
{ GEN_ASSERT_NOT_NULL(self->Hashes);
array_free( self.Hashes ); GEN_ASSERT_NOT_NULL(self->Entries);
array_free( 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 ) <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; ssize idx = <fn>__find( self, key ).EntryIndex;
if ( idx > 0 ) if ( idx > 0 )
return & self.Entries[idx].Value; return & self.Entries[idx].Value;
return NULL; return nullptr;
} }
void <fn>_map( <tbl_type> self, <tbl_type>_MapProc map_proc ) 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 ); 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 ); map_proc( self, self.Entries[idx].Key, self.Entries[idx].Value );
} }
} }
void <fn>_map_mut( <tbl_type> self, <tbl_type>_MapMutProc map_proc ) 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 ); 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 ); map_proc( self, self.Entries[idx].Key, & self.Entries[idx].Value );
} }
} }
void <fn>_grow( <tbl_type>* self ) 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 ); 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 ) 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 idx;
ssize last_added_index; ssize last_added_index;
ArrayHeader* old_hash_header = array_get_header( self->Hashes ); ArrayHeader* old_hash_header = array_get_header( self->Hashes );
ArrayHeader* old_entries_header = array_get_header( self->Entries ); 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 ); ArrayHeader* new_hash_header = array_get_header( new_tbl.Hashes );
for ( idx = 0; idx < new_hash_header->Num; idx++ ) for (ssize idx = 0; idx < cast(ssize, old_hash_header->Num); ++idx)
new_tbl.Hashes[idx] = -1;
for ( idx = 0; idx < old_entries_header->Num; idx++ )
{ {
<entry_type>* entry; <entry_type>* entry = & self->Entries[idx];
HT_FindResult find_result; HT_FindResult find_result;
if ( new_hash_header->Num == 0 ) find_result = <fn>__find( new_tbl, entry->Key);
<fn>_grow( & new_tbl ); last_added_index = <fn>__add_entry( & new_tbl, entry->Key);
entry = & self->Entries[ idx ]; if (find_result.PrevIndex < 0)
find_result = <fn>__find( new_tbl, entry->Key ); new_tbl.Hashes[find_result.HashIndex] = last_added_index;
last_added_index = <fn>__add_entry( new_tbl, entry->Key );
if ( find_result.PrevIndex < 0 )
new_tbl.Hashes[ find_result.HashIndex ] = last_added_index;
else 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].Next = find_result.EntryIndex;
new_tbl.Entries[ last_added_index ].Value = entry->Value; new_tbl.Entries[last_added_index].Value = entry->Value;
} }
<fn>_destroy( *self ); <fn>_destroy( self );
* self = new_tbl; * self = new_tbl;
} }
void <fn>_rehash_fast( <tbl_type> self ) void <fn>_rehash_fast( <tbl_type> self )
{ {
GEN_ASSERT_NOT_NULL(self.Hashes);
GEN_ASSERT_NOT_NULL(self.Entries);
ssize idx; ssize idx;
for ( idx = 0; idx < array_get_header( self.Entries )->Num; 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 ) 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 ); 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 ); 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 ) 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 ); array_remove_at( self.Entries, idx );
} }
void <fn>_set( <tbl_type>* self, u64 key, <type> value ) 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; ssize idx;
HT_FindResult find_result; HT_FindResult find_result;
if ( array_get_header( self->Hashes )->Num == 0 ) if ( array_get_header( self->Hashes )->Num == 0 )
<fn>_grow( self ); hashtable_grow( self );
find_result = <fn>__find( * self, key ); find_result = <fn>__find( * self, key );
if ( find_result.EntryIndex >= 0 ) if ( find_result.EntryIndex >= 0 ) {
{
idx = find_result.EntryIndex; idx = find_result.EntryIndex;
} }
else 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; self->Entries[ find_result.PrevIndex ].Next = idx;
} }
else else {
{
self->Hashes[ find_result.HashIndex ] = idx; self->Hashes[ find_result.HashIndex ] = idx;
} }
} }
@ -282,11 +301,13 @@ CodeBody gen_hashtable( StrC type, StrC hashtable_name )
self->Entries[ idx ].Value = value; self->Entries[ idx ].Value = value;
if ( <fn>__full( * self ) ) if ( <fn>__full( * self ) )
<fn>_grow( self ); hashtable_grow( self );
} }
ssize <fn>_slot( <tbl_type> self, u64 key ) 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 ) for ( ssize idx = 0; idx < array_get_header( self.Hashes )->Num; ++idx )
if ( self.Hashes[ idx ] == key ) if ( self.Hashes[ idx ] == key )
return idx; return idx;
@ -294,22 +315,26 @@ CodeBody gen_hashtable( StrC type, StrC hashtable_name )
return -1; 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; ssize idx;
<entry_type> entry = { key, -1 }; <entry_type> entry = { key, -1 };
idx = array_get_header( self.Entries )->Num; idx = array_get_header( self->Entries )->Num;
array_append( self.Entries, entry ); array_append( self->Entries, entry );
return idx; return idx;
} }
HT_FindResult <fn>__find( <tbl_type> self, u64 key ) 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 }; HT_FindResult result = { -1, -1, -1 };
ArrayHeader* hash_header = array_get_header( self.Hashes ); ArrayHeader* hash_header = array_get_header( self.Hashes );
if ( hash_header->Num > 0 ) if ( hash_header->Num > 0 )
{ {
result.HashIndex = key % hash_header->Num; result.HashIndex = key % hash_header->Num;
@ -330,14 +355,46 @@ CodeBody gen_hashtable( StrC type, StrC hashtable_name )
b32 <fn>__full( <tbl_type> self ) 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* hash_header = array_get_header( self.Hashes );
ArrayHeader* entries_header = array_get_header( self.Entries ); 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" )
#pragma pop_macro( "GEN_ASSERT_NOT_NULL" ) #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" char const* cmt_str = str_fmt_buf( "Name: %.*s Type: %.*s"
, tbl_type.length(), tbl_type.Data , tbl_type.length(), tbl_type.Data
@ -346,6 +403,8 @@ CodeBody gen_hashtable( StrC type, StrC hashtable_name )
return def_global_body(args( return def_global_body(args(
def_pragma( string_to_strc( string_fmt_buf( GlobalAllocator, "region %S", tbl_type ))), def_pragma( string_to_strc( string_fmt_buf( GlobalAllocator, "region %S", tbl_type ))),
fmt_newline, fmt_newline,
generic_interface_slot,
fmt_newline,
hashtable_types, hashtable_types,
fmt_newline, fmt_newline,
entry_array, entry_array,
@ -355,3 +414,21 @@ CodeBody gen_hashtable( StrC type, StrC hashtable_name )
fmt_newline 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> ) \ 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; 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_remove_entry(HashTable<Type> table, ssize idx);
template<class Type> void hashtable_set (HashTable<Type>* table, u64 key, Type value); 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_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 (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> 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; static constexpr f32 HashTable_CriticalLoadScale = 0.7f;
template<typename Type> template<typename Type>
@ -540,37 +541,45 @@ HashTable<Type> hashtable_init_reserve(AllocatorInfo allocator, usize num)
result.Hashes = array_init_reserve<ssize>(allocator, num); result.Hashes = array_init_reserve<ssize>(allocator, num);
array_get_header(result.Hashes)->Num = num; array_get_header(result.Hashes)->Num = num;
array_resize(& result.Hashes, 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); result.Entries = array_init_reserve<HashTableEntry<Type>>(allocator, num);
return result; return result;
} }
template<typename Type> inline template<typename Type> forceinline
void hashtable_clear(HashTable<Type> table) { void hashtable_clear(HashTable<Type> table) {
GEN_ASSERT_NOT_NULL(table.Hashes);
GEN_ASSERT_NOT_NULL(table.Entries);
array_clear(table.Entries); array_clear(table.Entries);
array_fill(table.Hashes, 0, array_num(table.Hashes), (ssize)-1); 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) { 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) { if (table->Hashes && array_get_header(table->Hashes)->Capacity) {
array_free(table->Hashes); array_free(table->Hashes);
array_free(table->Entries); array_free(table->Entries);
} }
} }
template<typename Type> inline template<typename Type> forceinline
Type* hashtable_get(HashTable<Type> table, u64 key) { 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) if (idx >= 0)
return & table.Entries[idx].Value; return & table.Entries[idx].Value;
return nullptr; return nullptr;
} }
template<typename Type> inline template<typename Type> forceinline
void hashtable_map(HashTable<Type> table, void (*map_proc)(u64 key, Type value)) { 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); GEN_ASSERT_NOT_NULL(map_proc);
for (ssize idx = 0; idx < ssize(num(table.Entries)); ++idx) { 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)) { 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); GEN_ASSERT_NOT_NULL(map_proc);
for (ssize idx = 0; idx < ssize(num(table.Entries)); ++idx) { 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) { 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)); ssize new_num = array_grow_formula( array_num(table->Entries));
hashtable_rehash(table, new_num); hashtable_rehash(table, new_num);
} }
@ -596,6 +610,9 @@ void hashtable_grow(HashTable<Type>* table) {
template<typename Type> inline template<typename Type> inline
void hashtable_rehash(HashTable<Type>* table, ssize new_num) 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; ssize last_added_index;
HashTable<Type> new_ht = hashtable_init_reserve<Type>( array_get_header(table->Hashes)->Allocator, new_num); 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; HashTableFindResult find_result;
HashTableEntry<Type>& entry = table->Entries[idx]; HashTableEntry<Type>& entry = table->Entries[idx];
find_result = hashtable_find(new_ht, entry.Key); find_result = hashtable__find(new_ht, entry.Key);
last_added_index = hashtable_add_entry(& new_ht, entry.Key); last_added_index = hashtable__add_entry(& new_ht, 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;
@ -623,6 +640,8 @@ void hashtable_rehash(HashTable<Type>* table, ssize new_num)
template<typename Type> inline template<typename Type> inline
void hashtable_rehash_fast(HashTable<Type> table) void hashtable_rehash_fast(HashTable<Type> table)
{ {
GEN_ASSERT_NOT_NULL(table.Hashes);
GEN_ASSERT_NOT_NULL(table.Entries);
ssize idx; ssize idx;
for (idx = 0; idx < ssize(num(table.Entries)); 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) { 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); HashTableFindResult find_result = find(table, key);
if (find_result.EntryIndex >= 0) { 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) { 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); remove_at(table.Entries, idx);
} }
template<typename Type> inline template<typename Type> inline
void hashtable_set(HashTable<Type>* table, u64 key, Type value) 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; ssize idx;
HashTableFindResult find_result; HashTableFindResult find_result;
if (hashtable_full(* table)) if (hashtable_full(* table))
hashtable_grow(table); hashtable_grow(table);
find_result = hashtable_find(* table, key); find_result = hashtable__find(* table, key);
if (find_result.EntryIndex >= 0) { if (find_result.EntryIndex >= 0) {
idx = find_result.EntryIndex; idx = find_result.EntryIndex;
} }
else else
{ {
idx = hashtable_add_entry(table, key); idx = hashtable__add_entry(table, key);
if (find_result.PrevIndex >= 0) { if (find_result.PrevIndex >= 0) {
table->Entries[find_result.PrevIndex].Next = idx; table->Entries[find_result.PrevIndex].Next = idx;
@ -692,8 +718,10 @@ void hashtable_set(HashTable<Type>* table, u64 key, Type value)
hashtable_grow(table); hashtable_grow(table);
} }
template<typename Type> inline template<typename Type> forceinline
ssize hashtable_slot(HashTable<Type> table, u64 key) { 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) for (ssize idx = 0; idx < ssize(num(table.Hashes)); ++idx)
if (table.Hashes[idx] == key) if (table.Hashes[idx] == key)
return idx; return idx;
@ -701,8 +729,11 @@ ssize hashtable_slot(HashTable<Type> table, u64 key) {
return -1; return -1;
} }
template<typename Type> inline template<typename Type> forceinline
ssize hashtable_add_entry(HashTable<Type>* table, u64 key) { 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; ssize idx;
HashTableEntry<Type> entry = { key, -1 }; HashTableEntry<Type> entry = { key, -1 };
@ -712,8 +743,10 @@ ssize hashtable_add_entry(HashTable<Type>* table, u64 key) {
} }
template<typename Type> inline 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 }; HashTableFindResult result = { -1, -1, -1 };
if (array_num(table.Hashes) > 0) if (array_num(table.Hashes) > 0)
@ -734,8 +767,10 @@ HashTableFindResult hashtable_find(HashTable<Type> table, u64 key)
return result; return result;
} }
template<typename Type> inline template<typename Type> forceinline
bool hashtable_full(HashTable<Type> table) { 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))); usize critical_load = usize(HashTable_CriticalLoadScale * f32(array_num(table.Hashes)));
b32 result = array_num(table.Entries) > critical_load; b32 result = array_num(table.Entries) > critical_load;
return result; 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_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_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_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(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_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 HashTable
#pragma endregion Containers #pragma endregion Containers

View File

@ -187,6 +187,17 @@
# endif # endif
#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 #if GEN_COMPILER_CPP
// Already Defined // Already Defined
#elif GEN_COMPILER_C && __STDC_VERSION__ >= 201112L #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. // 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: // 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. #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_remove_entry
#undef hashtable_set #undef hashtable_set
#undef hashtable_slot #undef hashtable_slot
#undef hashtable_add_entry
#undef hashtable_find
#undef hashtable_full
#undef hashtable_map #undef hashtable_map
#undef hashtable_map_mut #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_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_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_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(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_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)