mirror of
				https://github.com/Ed94/gencpp.git
				synced 2025-10-31 06:50:53 -07:00 
			
		
		
		
	verified the C hashtable has parity with the C++ templated gencpp hashtable.
This commit is contained in:
		| @@ -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))); | ||||||
|   | |||||||
| @@ -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; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -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; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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. | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user