mirror of
				https://github.com/Ed94/gencpp.git
				synced 2025-10-31 06:50:53 -07:00 
			
		
		
		
	Compare commits
	
		
			8 Commits
		
	
	
		
			v0.19-Alph
			...
			056a5863b8
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 056a5863b8 | |||
| 79eb5f1f76 | |||
| c6cb583518 | |||
| 34eec66f35 | |||
| 4137ebfbd8 | |||
| 5958dd2055 | |||
| 163ad0a511 | |||
| e3c2a577ba | 
							
								
								
									
										3
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							| @@ -37,7 +37,8 @@ | |||||||
| 		"propidl.h": "c", | 		"propidl.h": "c", | ||||||
| 		"android_native_app_glue.h": "c", | 		"android_native_app_glue.h": "c", | ||||||
| 		"raylib.h": "c", | 		"raylib.h": "c", | ||||||
| 		"*.m": "cpp" | 		"*.m": "cpp", | ||||||
|  | 		"atomic": "cpp" | ||||||
| 	}, | 	}, | ||||||
| 	"C_Cpp.intelliSenseEngineFallback": "disabled", | 	"C_Cpp.intelliSenseEngineFallback": "disabled", | ||||||
| 	"mesonbuild.configureOnOpen": true, | 	"mesonbuild.configureOnOpen": true, | ||||||
|   | |||||||
							
								
								
									
										23
									
								
								project/auxillary/gen_template.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								project/auxillary/gen_template.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | #ifdef GEN_INTELLISENSE_DIRECTIVES | ||||||
|  | #	pragma once | ||||||
|  | #	include "../gen.hpp" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | 	Explicitly generates a resolved definition of a cpp template definition. | ||||||
|  |  | ||||||
|  | 	TODO(Ed): Needs implementing for the C-library variant. | ||||||
|  | 	TODO(Ed): We need a non <token> syntax subst implemtnation for Strings for this to work. It must subst keywords directly based on template parameter names. | ||||||
|  |  | ||||||
|  | 	This is only meant to be used on relatively trivial templates, where the type or numeric is mostly a 'duck' type. | ||||||
|  | 	It cannot parse complex template parameters. | ||||||
|  |  | ||||||
|  | 	The varadic args should correspond 1:1 with the type of objects the generator expects from the template's parameters.alignas. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | CodeOperator gen_operator_template( CodeTemplate template, ... ); | ||||||
|  | CodeFn       gen_func_template( CodeTemplate template, ...  ); | ||||||
|  | Code         gen_class_struct_template( CodeTemplate template, ... ); | ||||||
|  |  | ||||||
|  | Code gen_template( CodeTemplate template, ... ); | ||||||
|  | Code gen_template( StrC template, StrC instantiation ); | ||||||
| @@ -372,7 +372,7 @@ AllocatorInfo get_string_allocator( s32 str_length ) | |||||||
| { | { | ||||||
| 	Arena* last = & StringArenas.back(); | 	Arena* last = & StringArenas.back(); | ||||||
|  |  | ||||||
| 	usize size_req = str_length + sizeof(String::Header) + sizeof(char*); | 	usize size_req = str_length + sizeof(StringHeader) + sizeof(char*); | ||||||
|  |  | ||||||
| 	if ( last->TotalUsed + ssize(size_req) > last->TotalSize ) | 	if ( last->TotalUsed + ssize(size_req) > last->TotalSize ) | ||||||
| 	{ | 	{ | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ ssize token_fmt_va( char* buf, usize buf_size, s32 num_tokens, va_list va ) | |||||||
| 		local_persist | 		local_persist | ||||||
| 		char tok_map_mem[ TokenFmt_TokenMap_MemSize ]; | 		char tok_map_mem[ TokenFmt_TokenMap_MemSize ]; | ||||||
|  |  | ||||||
| 		tok_map_arena = Arena::init_from_memory( tok_map_mem, sizeof(tok_map_mem) ); | 		tok_map_arena = init_from_memory( tok_map_mem, sizeof(tok_map_mem) ); | ||||||
| 		tok_map       = HashTable<StrC>::init( tok_map_arena ); | 		tok_map       = HashTable<StrC>::init( tok_map_arena ); | ||||||
|  |  | ||||||
| 		s32 left = num_tokens - 1; | 		s32 left = num_tokens - 1; | ||||||
|   | |||||||
| @@ -133,7 +133,7 @@ internal | |||||||
| void init() | void init() | ||||||
| { | { | ||||||
| 	Tokens = Array<Token>::init_reserve( LexArena | 	Tokens = Array<Token>::init_reserve( LexArena | ||||||
| 		, ( LexAllocator_Size - sizeof( Array<Token>::Header ) ) / sizeof(Token) | 		, ( LexAllocator_Size - sizeof( ArrayHeader ) ) / sizeof(Token) | ||||||
| 	); | 	); | ||||||
|  |  | ||||||
| 	defines_map_arena = Arena_256KB::init(); | 	defines_map_arena = Arena_256KB::init(); | ||||||
| @@ -712,7 +712,10 @@ Code parse_class_struct( TokType which, bool inplace_def = false ) | |||||||
|  |  | ||||||
| 	local_persist | 	local_persist | ||||||
| 	char interface_arr_mem[ kilobytes(4) ] {0}; | 	char interface_arr_mem[ kilobytes(4) ] {0}; | ||||||
| 	Array<CodeType> interfaces = Array<CodeType>::init_reserve( Arena::init_from_memory(interface_arr_mem, kilobytes(4) ), 4 ); | 	Array<CodeType> interfaces; { | ||||||
|  | 		Arena arena = init_from_memory( interface_arr_mem, kilobytes(4) ); | ||||||
|  | 		Array<CodeType>::init_reserve( arena, 4 ); | ||||||
|  | 	}  | ||||||
|  |  | ||||||
| 	// TODO(Ed) : Make an AST_DerivedType, we'll store any arbitary derived type into there as a linear linked list of them. | 	// TODO(Ed) : Make an AST_DerivedType, we'll store any arbitary derived type into there as a linear linked list of them. | ||||||
| 	if ( check( TokType::Assign_Classifer ) ) | 	if ( check( TokType::Assign_Classifer ) ) | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| #ifdef GEN_INTELLISENSE_DIRECTIVES | #ifdef GEN_INTELLISENSE_DIRECTIVES | ||||||
| #	pragma once | #	pragma once | ||||||
|  | #	include "platform.hpp" | ||||||
| #	include "macros.hpp" | #	include "macros.hpp" | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   | |||||||
| @@ -13,26 +13,83 @@ template<class TType, usize Size> struct RemoveConst<const TType[Size]> { typede | |||||||
| template<class TType> | template<class TType> | ||||||
| using TRemoveConst = typename RemoveConst<TType>::Type; | using TRemoveConst = typename RemoveConst<TType>::Type; | ||||||
|  |  | ||||||
| template<class Type> | #pragma region Array | ||||||
| struct Array | struct ArrayHeader; | ||||||
| { | template<class Type> struct Array; | ||||||
| 	struct Header |  | ||||||
| 	{ | template<class Type> Array<Type>  array_init(AllocatorInfo allocator); | ||||||
|  | template<class Type> Array<Type>  array_init_reserve(AllocatorInfo allocator, ssize capacity); | ||||||
|  | template<class Type> usize        array_grow_formula(ssize value); | ||||||
|  | template<class Type> bool         append(Array<Type>& array, Array<Type> other); | ||||||
|  | template<class Type> bool         append(Array<Type>& array, Type value); | ||||||
|  | template<class Type> bool         append(Array<Type>& array, Type* items, usize item_num); | ||||||
|  | template<class Type> bool         append_at(Array<Type>& array, Type item, usize idx); | ||||||
|  | template<class Type> bool         append_at(Array<Type>& array, Type* items, usize item_num, usize idx); | ||||||
|  | template<class Type> Type&        back(Array<Type>& array); | ||||||
|  | template<class Type> void         clear(Array<Type>& array); | ||||||
|  | template<class Type> bool         fill(Array<Type>& array, usize begin, usize end, Type value); | ||||||
|  | template<class Type> void         free(Array<Type>& array); | ||||||
|  | template<class Type> bool         grow(Array<Type>& array, usize min_capacity); | ||||||
|  | template<class Type> usize        num(Array<Type>& array); | ||||||
|  | template<class Type> void         pop(Array<Type>& array); | ||||||
|  | template<class Type> void         remove_at(Array<Type>& array, usize idx); | ||||||
|  | template<class Type> bool         reserve(Array<Type>& array, usize new_capacity); | ||||||
|  | template<class Type> bool         resize(Array<Type>& array, usize num); | ||||||
|  | template<class Type> bool         set_capacity(Array<Type>& array, usize new_capacity); | ||||||
|  | template<class Type> ArrayHeader* get_header(Array<Type>& array); | ||||||
|  |  | ||||||
|  | struct ArrayHeader { | ||||||
| 	AllocatorInfo Allocator; | 	AllocatorInfo Allocator; | ||||||
| 	usize         Capacity; | 	usize         Capacity; | ||||||
| 	usize         Num; | 	usize         Num; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| 	static | template<class Type> | ||||||
| 	Array init( AllocatorInfo allocator ) | struct Array | ||||||
| { | { | ||||||
| 		return init_reserve( allocator, grow_formula(0) ); | 	Type* Data; | ||||||
|  |  | ||||||
|  | #if 1 | ||||||
|  | #pragma region Member Mapping | ||||||
|  | 	forceinline static Array  init(AllocatorInfo allocator)                         { return GEN_NS array_init<Type>(allocator); } | ||||||
|  | 	forceinline static Array  init_reserve(AllocatorInfo allocator, ssize capacity) { return GEN_NS array_init_reserve<Type>(allocator, capacity); } | ||||||
|  | 	forceinline static usize  grow_formula(ssize value)                             { return GEN_NS array_grow_formula<Type>(value); } | ||||||
|  |  | ||||||
|  | 	forceinline bool         append(Array other)                               { return GEN_NS append<Type>(*this, other); } | ||||||
|  | 	forceinline bool         append(Type value)                                { return GEN_NS append<Type>(*this, value); } | ||||||
|  | 	forceinline bool         append(Type* items, usize item_num)               { return GEN_NS append<Type>(*this, items, item_num); } | ||||||
|  | 	forceinline bool         append_at(Type item, usize idx)                   { return GEN_NS append_at<Type>(*this, item, idx); } | ||||||
|  | 	forceinline bool         append_at(Type* items, usize item_num, usize idx) { return GEN_NS append_at<Type>(*this, items, item_num, idx); } | ||||||
|  | 	forceinline Type&        back()                                            { return GEN_NS back<Type>(*this); } | ||||||
|  | 	forceinline void         clear()                                           { GEN_NS clear<Type>(*this); } | ||||||
|  | 	forceinline bool         fill(usize begin, usize end, Type value)          { return GEN_NS fill<Type>(*this, begin, end, value); } | ||||||
|  | 	forceinline void         free()                                            { GEN_NS free<Type>(*this); } | ||||||
|  | 	forceinline ArrayHeader* get_header()                                      { return GEN_NS get_header<Type>(*this); } | ||||||
|  | 	forceinline bool         grow(usize min_capacity)                          { return GEN_NS grow<Type>(*this, min_capacity); } | ||||||
|  | 	forceinline usize        num()                                             { return GEN_NS num<Type>(*this); } | ||||||
|  | 	forceinline void         pop()                                             { GEN_NS pop<Type>(*this); } | ||||||
|  | 	forceinline void         remove_at(usize idx)                              { GEN_NS remove_at<Type>(*this, idx); } | ||||||
|  | 	forceinline bool         reserve(usize new_capacity)                       { return GEN_NS reserve<Type>(*this, new_capacity); } | ||||||
|  | 	forceinline bool         resize(usize num)                                 { return GEN_NS resize<Type>(*this, num); } | ||||||
|  | 	forceinline bool         set_capacity(usize new_capacity)                  { return GEN_NS set_capacity<Type>(*this, new_capacity); } | ||||||
|  |  | ||||||
|  | 	forceinline operator Type*()             { return Data; } | ||||||
|  | 	forceinline operator Type const*() const { return Data; } | ||||||
|  | 	forceinline Type* begin()                { return Data; } | ||||||
|  | 	forceinline Type* end()                  { return Data + get_header()->Num; } | ||||||
|  | #pragma endregion Member Mapping | ||||||
|  | #endif | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template<class Type> inline | ||||||
|  | Array<Type> array_init(AllocatorInfo allocator) { | ||||||
|  | 	return array_init_reserve<Type>(allocator, array_grow_formula<Type>(0)); | ||||||
| } | } | ||||||
|  |  | ||||||
| 	static | template<class Type> inline | ||||||
| 	Array init_reserve( AllocatorInfo allocator, ssize capacity ) | Array<Type> array_init_reserve(AllocatorInfo allocator, ssize capacity) | ||||||
| { | { | ||||||
| 		Header* header = rcast( Header*, alloc( allocator, sizeof(Header) + sizeof(Type) * capacity )); | 	ArrayHeader* header = rcast(ArrayHeader*, alloc(allocator, sizeof(ArrayHeader) + sizeof(Type) * capacity)); | ||||||
|  |  | ||||||
| 	if (header == nullptr) | 	if (header == nullptr) | ||||||
|  		return {nullptr}; |  		return {nullptr}; | ||||||
| @@ -44,56 +101,56 @@ struct Array | |||||||
| 	return {rcast(Type*, header + 1)}; | 	return {rcast(Type*, header + 1)}; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	static | template<class Type> inline | ||||||
| 	usize grow_formula( usize value ) | usize array_grow_formula(ssize value) { | ||||||
| 	{ |  | ||||||
| 	return 2 * value + 8; | 	return 2 * value + 8; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	bool append( Array other ) | template<class Type> inline | ||||||
| 	{ | bool append(Array<Type>& array, Array<Type> other) { | ||||||
| 		return append( other, other.num() ); | 	return append(array, other, num(other)); | ||||||
| } | } | ||||||
|  |  | ||||||
| 	bool append( Type value ) | template<class Type> inline | ||||||
|  | bool append(Array<Type>& array, Type value) | ||||||
| { | { | ||||||
| 		Header* header = get_header(); | 	ArrayHeader* header = get_header(array); | ||||||
|  |  | ||||||
| 	if (header->Num == header->Capacity) | 	if (header->Num == header->Capacity) | ||||||
| 	{ | 	{ | ||||||
| 			if ( ! grow( header->Capacity )) | 		if (!grow(array, header->Capacity)) | ||||||
| 			return false; | 			return false; | ||||||
|  | 		header = get_header(array); | ||||||
| 			header = get_header(); |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 		Data[ header->Num ] = value; | 	array.Data[header->Num] = value; | ||||||
| 	header->Num++; | 	header->Num++; | ||||||
|  |  | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	bool append( Type* items, usize item_num ) | template<class Type> inline | ||||||
|  | bool append(Array<Type>& array, Type* items, usize item_num) | ||||||
| { | { | ||||||
| 		Header* header = get_header(); | 	ArrayHeader* header = get_header(array); | ||||||
|  |  | ||||||
| 	if (header->Num + item_num > header->Capacity) | 	if (header->Num + item_num > header->Capacity) | ||||||
| 	{ | 	{ | ||||||
| 			if ( ! grow( header->Capacity + item_num )) | 		if (!grow(array, header->Capacity + item_num)) | ||||||
| 			return false; | 			return false; | ||||||
|  | 		header = get_header(array); | ||||||
| 			header = get_header(); |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 		mem_copy( Data + header->Num, items, item_num * sizeof(Type) ); | 	mem_copy(array.Data + header->Num, items, item_num * sizeof(Type)); | ||||||
| 	header->Num += item_num; | 	header->Num += item_num; | ||||||
|  |  | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	bool append_at( Type item, usize idx ) | template<class Type> inline | ||||||
|  | bool append_at(Array<Type>& array, Type item, usize idx) | ||||||
| { | { | ||||||
| 		Header* header = get_header(); | 	ArrayHeader* header = get_header(array); | ||||||
|  |  | ||||||
| 	if (idx >= header->Num) | 	if (idx >= header->Num) | ||||||
| 	 	idx = header->Num - 1; | 	 	idx = header->Num - 1; | ||||||
| @@ -103,13 +160,13 @@ struct Array | |||||||
|  |  | ||||||
| 	if (header->Capacity < header->Num + 1) | 	if (header->Capacity < header->Num + 1) | ||||||
| 	{ | 	{ | ||||||
| 			if ( ! grow( header->Capacity + 1 )) | 		if (!grow(array, header->Capacity + 1)) | ||||||
| 			return false; | 			return false; | ||||||
|  |  | ||||||
| 			header = get_header(); | 		header = get_header(array); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 		Type* target = Data + idx; | 	Type* target = array.Data + idx; | ||||||
|  |  | ||||||
| 	mem_move(target + 1, target, (header->Num - idx) * sizeof(Type)); | 	mem_move(target + 1, target, (header->Num - idx) * sizeof(Type)); | ||||||
| 	header->Num++; | 	header->Num++; | ||||||
| @@ -117,25 +174,26 @@ struct Array | |||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	bool append_at( Type* items, usize item_num, usize idx ) | template<class Type> inline | ||||||
|  | bool append_at(Array<Type>& array, Type* items, usize item_num, usize idx) | ||||||
| { | { | ||||||
| 		Header* header = get_header(); | 	ArrayHeader* header = get_header(array); | ||||||
|  |  | ||||||
| 	if (idx >= header->Num) | 	if (idx >= header->Num) | ||||||
| 	{ | 	{ | ||||||
| 			return append( items, item_num ); | 		return append(array, items, item_num); | ||||||
|  	} |  	} | ||||||
|  |  | ||||||
| 	if (item_num > header->Capacity) | 	if (item_num > header->Capacity) | ||||||
| 	{ | 	{ | ||||||
| 			if ( ! grow( header->Capacity + item_num ) ) | 		if (!grow(array, header->Capacity + item_num)) | ||||||
| 			return false; | 			return false; | ||||||
|  |  | ||||||
| 			header = get_header(); | 		header = get_header(array); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 		Type* target = Data + idx + item_num; | 	Type* target = array.Data + idx + item_num; | ||||||
| 		Type* src    = Data + idx; | 	Type* src    = array.Data + idx; | ||||||
|  |  | ||||||
| 	mem_move(target, src, (header->Num - idx) * sizeof(Type)); | 	mem_move(target, src, (header->Num - idx) * sizeof(Type)); | ||||||
| 	mem_copy(src, items, item_num * sizeof(Type)); | 	mem_copy(src, items, item_num * sizeof(Type)); | ||||||
| @@ -144,190 +202,212 @@ struct Array | |||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	Type& back( void ) | template<class Type> inline | ||||||
| 	{ | Type& back(Array<Type>& array) { | ||||||
| 		Header& header = * get_header(); | 	ArrayHeader* header = get_header(array); | ||||||
| 		return Data[ header.Num - 1 ]; | 	return array.Data[header->Num - 1]; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	void clear( void ) | template<class Type> inline | ||||||
| 	{ | void clear(Array<Type>& array) { | ||||||
| 		Header& header = * get_header(); | 	ArrayHeader* header = get_header(array); | ||||||
| 		header.Num     = 0; | 	header->Num = 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	bool fill( usize begin, usize end, Type value ) | template<class Type> inline | ||||||
|  | bool fill(Array<Type>& array, usize begin, usize end, Type value) | ||||||
| { | { | ||||||
| 		Header& header = * get_header(); | 	ArrayHeader* header = get_header(array); | ||||||
|  |  | ||||||
| 		if ( begin < 0 || end > header.Num ) | 	if (begin < 0 || end > header->Num) | ||||||
| 	return false; | 	return false; | ||||||
|  |  | ||||||
| 	for (ssize idx = ssize(begin); idx < ssize(end); idx++) | 	for (ssize idx = ssize(begin); idx < ssize(end); idx++) | ||||||
| 	{ | 	{ | ||||||
| 			Data[ idx ] = value; | 		array.Data[idx] = value; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	void free( void ) | template<class Type> inline | ||||||
| 	{ | void free(Array<Type>& array) { | ||||||
| 		Header& header = * get_header(); | 	ArrayHeader* header = get_header(array); | ||||||
| 		gen::free( header.Allocator, &header ); | 	gen::free(header->Allocator, header); | ||||||
| 		Data = nullptr; | 	array.Data = nullptr; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	Header* get_header( void ) | template<class Type> inline | ||||||
| 	{ | ArrayHeader* get_header(Array<Type>& array) { | ||||||
| 	using NonConstType = TRemoveConst<Type>; | 	using NonConstType = TRemoveConst<Type>; | ||||||
| 		return rcast( Header*, const_cast<NonConstType*>(Data) ) - 1 ; | 	return rcast(ArrayHeader*, const_cast<NonConstType*>(array.Data)) - 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	bool grow( usize min_capacity ) | template<class Type> inline | ||||||
|  | bool grow(Array<Type>& array, usize min_capacity) | ||||||
| { | { | ||||||
| 		Header& header       = * get_header(); | 	ArrayHeader* header = get_header(array); | ||||||
| 		usize      new_capacity = grow_formula( header.Capacity ); | 	usize new_capacity = array_grow_formula<Type>(header->Capacity); | ||||||
|  |  | ||||||
| 	if (new_capacity < min_capacity) | 	if (new_capacity < min_capacity) | ||||||
| 	new_capacity = min_capacity; | 	new_capacity = min_capacity; | ||||||
|  |  | ||||||
| 		return set_capacity( new_capacity ); | 	return set_capacity(array, new_capacity); | ||||||
| } | } | ||||||
|  |  | ||||||
| 	usize num( void ) | template<class Type> inline | ||||||
| 	{ | usize num(Array<Type>& array) { | ||||||
| 		return get_header()->Num; | 	return get_header(array)->Num; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	void pop( void ) | template<class Type> inline | ||||||
| 	{ | void pop(Array<Type>& array) { | ||||||
| 		Header& header = * get_header(); | 	ArrayHeader* header = get_header(array); | ||||||
|  | 	GEN_ASSERT(header->Num > 0); | ||||||
| 		GEN_ASSERT( header.Num > 0 ); |  | ||||||
| 		header.Num--; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void remove_at( usize idx ) |  | ||||||
| 	{ |  | ||||||
| 		Header* header = get_header(); |  | ||||||
| 		GEN_ASSERT( idx < header->Num ); |  | ||||||
|  |  | ||||||
| 		mem_move( header + idx, header + idx + 1, sizeof( Type ) * ( header->Num - idx - 1 ) ); |  | ||||||
| 	header->Num--; | 	header->Num--; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	bool reserve( usize new_capacity ) | template<class Type> inline | ||||||
|  | void remove_at(Array<Type>& array, usize idx) | ||||||
| { | { | ||||||
| 		Header& header = * get_header(); | 	ArrayHeader* header = get_header(array); | ||||||
|  | 	GEN_ASSERT(idx < header->Num); | ||||||
|  |  | ||||||
| 		if ( header.Capacity < new_capacity ) | 	mem_move(array.Data + idx, array.Data + idx + 1, sizeof(Type) * (header->Num - idx - 1)); | ||||||
| 			return set_capacity( new_capacity ); | 	header->Num--; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template<class Type> inline | ||||||
|  | bool reserve(Array<Type>& array, usize new_capacity) | ||||||
|  | { | ||||||
|  | 	ArrayHeader* header = get_header(array); | ||||||
|  |  | ||||||
|  | 	if (header->Capacity < new_capacity) | ||||||
|  |  		return set_capacity(array, new_capacity); | ||||||
|  |  | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	bool resize( usize num ) | template<class Type> inline | ||||||
|  | bool resize(Array<Type>& array, usize num) | ||||||
| { | { | ||||||
| 		Header* header = get_header(); | 	ArrayHeader* header = get_header(array); | ||||||
|  |  | ||||||
| 		if ( header->Capacity < num ) | 	if (header->Capacity < num) { | ||||||
| 		{ | 		if (!grow(array, num)) | ||||||
| 			if ( ! grow( num ) ) |  | ||||||
| 			return false; | 			return false; | ||||||
|  | 		header = get_header(array); | ||||||
| 			header = get_header(); |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	header->Num = num; | 	header->Num = num; | ||||||
|  	return true; |  	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	bool set_capacity( usize new_capacity ) | template<class Type> inline | ||||||
|  | bool set_capacity(Array<Type>& array, usize new_capacity) | ||||||
| { | { | ||||||
| 		Header& header = * get_header(); | 	ArrayHeader* header = get_header(array); | ||||||
|  |  | ||||||
| 		if ( new_capacity == header.Capacity ) | 	if (new_capacity == header->Capacity) | ||||||
| 	return true; | 	return true; | ||||||
|  |  | ||||||
| 		if ( new_capacity < header.Num ) | 	if (new_capacity < header->Num) | ||||||
| 	{ | 	{ | ||||||
| 			// Already have the memory, mine as well keep it. | 		header->Num = new_capacity; | ||||||
| 			header.Num = new_capacity; |  | ||||||
| 		return true; | 		return true; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 		ssize      size       = sizeof( Header ) + sizeof( Type ) * new_capacity; | 	ssize size = sizeof(ArrayHeader) + sizeof(Type) * new_capacity; | ||||||
| 		Header* new_header = rcast( Header*, alloc( header.Allocator, size ) ); | 	ArrayHeader* new_header = rcast(ArrayHeader*, alloc(header->Allocator, size)); | ||||||
|  |  | ||||||
| 	if (new_header == nullptr) | 	if (new_header == nullptr) | ||||||
| 		return false; | 		return false; | ||||||
|  |  | ||||||
| 		mem_move( new_header, &header, sizeof( Header ) + sizeof( Type ) * header.Num ); | 	mem_move(new_header, header, sizeof(ArrayHeader) + sizeof(Type) * header->Num); | ||||||
|  |  | ||||||
| 	new_header->Capacity = new_capacity; | 	new_header->Capacity = new_capacity; | ||||||
|  |  | ||||||
| 		gen::free( header.Allocator, &header ); | 	GEN_NS free(header->Allocator, header); | ||||||
|  |  | ||||||
| 		Data = rcast( Type*, new_header + 1); | 	array.Data = rcast(Type*, new_header + 1); | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  | #pragma endregion Array | ||||||
| 	Type* Data; |  | ||||||
|  |  | ||||||
| 	operator Type*() |  | ||||||
| 	{ |  | ||||||
| 		return Data; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	operator Type const*() const |  | ||||||
| 	{ |  | ||||||
| 		return Data; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// For-range based support |  | ||||||
|  |  | ||||||
| 	Type* begin() |  | ||||||
| 	{ |  | ||||||
| 		return Data; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	Type* end() |  | ||||||
| 	{ |  | ||||||
| 		return Data + get_header()->Num; |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| // TODO(Ed) : This thing needs ALOT of work. | // TODO(Ed) : This thing needs ALOT of work. | ||||||
|  |  | ||||||
| template<typename Type> | #pragma region HashTable | ||||||
| struct HashTable | template<class Type> struct HashTable; | ||||||
| { |  | ||||||
| 	struct FindResult | struct HashTableFindResult { | ||||||
| 	{ |  | ||||||
| 	ssize HashIndex; | 	ssize HashIndex; | ||||||
| 	ssize PrevIndex; | 	ssize PrevIndex; | ||||||
| 	ssize EntryIndex; | 	ssize EntryIndex; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| 	struct Entry | template<class Type> | ||||||
| 	{ | struct HashTableEntry { | ||||||
| 	u64   Key; | 	u64   Key; | ||||||
| 	ssize Next; | 	ssize Next; | ||||||
| 	Type  Value; | 	Type  Value; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | // Forward declarations for all lifted functions | ||||||
|  | template<class Type> HashTable<Type>       hashtable_init(AllocatorInfo allocator); | ||||||
|  | template<class Type> HashTable<Type>       hashtable_init_reserve(AllocatorInfo allocator, usize num); | ||||||
|  | template<class Type> void                  clear(HashTable<Type>& table); | ||||||
|  | template<class Type> void                  destroy(HashTable<Type>& table); | ||||||
|  | template<class Type> Type*                 get(HashTable<Type>& table, u64 key); | ||||||
|  | template<class Type> void                  grow(HashTable<Type>& table); | ||||||
|  | template<class Type> void                  rehash(HashTable<Type>& table, ssize new_num); | ||||||
|  | template<class Type> void                  rehash_fast(HashTable<Type>& table); | ||||||
|  | template<class Type> void                  remove(HashTable<Type>& table, u64 key); | ||||||
|  | template<class Type> void                  remove_entry(HashTable<Type>& table, ssize idx); | ||||||
|  | template<class Type> void                  set(HashTable<Type>& table, u64 key, Type value); | ||||||
|  | template<class Type> ssize                 slot(HashTable<Type>& table, u64 key); | ||||||
|  | template<class Type> ssize                 add_entry(HashTable<Type>& table, u64 key); | ||||||
|  | template<class Type> HashTableFindResult   find(HashTable<Type>& table, u64 key); | ||||||
|  | template<class Type> bool                  full(HashTable<Type>& table); | ||||||
|  | template<class Type> void                  map(HashTable<Type>& table, void (*map_proc)(u64 key, Type value)); | ||||||
|  | template<class Type> void                  map_mut(HashTable<Type>& table, void (*map_proc)(u64 key, Type* value)); | ||||||
|  |  | ||||||
|  | template<typename Type> | ||||||
|  | struct HashTable | ||||||
|  | { | ||||||
| 	static constexpr f32 CriticalLoadScale = 0.7f; | 	static constexpr f32 CriticalLoadScale = 0.7f; | ||||||
|  |  | ||||||
| 	static | 	Array<ssize>                Hashes; | ||||||
| 	HashTable init( AllocatorInfo allocator ) | 	Array<HashTableEntry<Type>> Entries; | ||||||
| 	{ |  | ||||||
| 		HashTable<Type> result = init_reserve(allocator, 8); | #if 1 | ||||||
|  | #pragma region Member Mapping | ||||||
|  | 	forceinline static HashTable init(AllocatorInfo allocator)                    { return GEN_NS hashtable_init<Type>(allocator); } | ||||||
|  | 	forceinline static HashTable init_reserve(AllocatorInfo allocator, usize num) { return GEN_NS hashtable_init_reserve<Type>(allocator, num); } | ||||||
|  |  | ||||||
|  | 	forceinline void  clear()                           { GEN_NS clear<Type>(*this); } | ||||||
|  | 	forceinline void  destroy()                         { GEN_NS destroy<Type>(*this); } | ||||||
|  | 	forceinline Type* get(u64 key)                      { return GEN_NS get<Type>(*this, key); } | ||||||
|  | 	forceinline void  grow()                            { GEN_NS grow<Type>(*this); } | ||||||
|  | 	forceinline void  rehash(ssize new_num)             { GEN_NS rehash<Type>(*this, new_num); } | ||||||
|  | 	forceinline void  rehash_fast()                     { GEN_NS rehash_fast<Type>(*this); } | ||||||
|  | 	forceinline void  remove(u64 key)                   { GEN_NS remove<Type>(*this, key); } | ||||||
|  | 	forceinline void  remove_entry(ssize idx)           { GEN_NS remove_entry<Type>(*this, idx); } | ||||||
|  | 	forceinline void  set(u64 key, Type value)          { GEN_NS set<Type>(*this, key, value); } | ||||||
|  | 	forceinline ssize slot(u64 key)                     { return GEN_NS slot<Type>(*this, key); } | ||||||
|  | 	forceinline void  map(void (*proc)(u64, Type))      { GEN_NS map<Type>(*this, proc); } | ||||||
|  | 	forceinline void  map_mut(void (*proc)(u64, Type*)) { GEN_NS map_mut<Type>(*this, proc); } | ||||||
|  | #pragma endregion Member Mapping | ||||||
|  | #endif | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template<typename Type> inline | ||||||
|  | HashTable<Type> hashtable_init(AllocatorInfo allocator) { | ||||||
|  | 	HashTable<Type> result = hashtable_init_reserve<Type>(allocator, 8); | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	static | template<typename Type> inline | ||||||
| 	HashTable init_reserve( AllocatorInfo allocator, usize num ) | HashTable<Type> hashtable_init_reserve(AllocatorInfo allocator, usize num) | ||||||
| { | { | ||||||
| 	HashTable<Type> result = { { nullptr }, { nullptr } }; | 	HashTable<Type> result = { { nullptr }, { nullptr } }; | ||||||
|  |  | ||||||
| @@ -336,76 +416,70 @@ struct HashTable | |||||||
| 	result.Hashes.resize(num); | 	result.Hashes.resize(num); | ||||||
| 	result.Hashes.fill(0, num, -1); | 	result.Hashes.fill(0, num, -1); | ||||||
|  |  | ||||||
| 		result.Entries = Array<Entry>::init_reserve( allocator, num ); | 	result.Entries = Array<HashTableEntry<Type>>::init_reserve(allocator, num); | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	void clear( void ) | template<typename Type> inline | ||||||
| 	{ | void clear(HashTable<Type>& table) { | ||||||
| 		Entries.clear(); | 	table.Entries.clear(); | ||||||
| 		Hashes.fill( 0, Hashes.num(), -1); | 	table.Hashes.fill(0, table.Hashes.num(), -1); | ||||||
| } | } | ||||||
|  |  | ||||||
| 	void destroy( void ) | template<typename Type> inline | ||||||
| 	{ | void destroy(HashTable<Type>& table) { | ||||||
| 		if ( Hashes && Hashes.get_header()->Capacity ) | 	if (table.Hashes && table.Hashes.get_header()->Capacity) { | ||||||
| 		{ | 		table.Hashes.free(); | ||||||
| 			Hashes.free(); | 		table.Entries.free(); | ||||||
| 			Entries.free(); |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| 	Type* get( u64 key ) | template<typename Type> inline | ||||||
| 	{ | Type* get(HashTable<Type>& table, u64 key) { | ||||||
| 		ssize idx = find( key ).EntryIndex; | 	ssize idx = find(table, key).EntryIndex; | ||||||
| 	if (idx >= 0) | 	if (idx >= 0) | ||||||
| 			return & Entries[ idx ].Value; | 		return &table.Entries[idx].Value; | ||||||
|  |  | ||||||
| 	return nullptr; | 	return nullptr; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	using MapProc = void (*)( u64 key, Type  value ); | template<typename Type> inline | ||||||
|  | void map(HashTable<Type>& table, void (*map_proc)(u64 key, Type value)) { | ||||||
| 	void map( MapProc map_proc ) |  | ||||||
| 	{ |  | ||||||
| 	GEN_ASSERT_NOT_NULL(map_proc); | 	GEN_ASSERT_NOT_NULL(map_proc); | ||||||
|  |  | ||||||
| 		for ( ssize idx = 0; idx < ssize(Entries.num()); ++idx ) | 	for (ssize idx = 0; idx < ssize(table.Entries.num()); ++idx) { | ||||||
| 		{ | 		map_proc(table.Entries[idx].Key, table.Entries[idx].Value); | ||||||
| 			map_proc( Entries[ idx ].Key, Entries[ idx ].Value ); |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| 	using MapMutProc = void (*)( u64 key, Type* value ); | template<typename Type> inline | ||||||
|  | void map_mut(HashTable<Type>& table, void (*map_proc)(u64 key, Type* value)) { | ||||||
| 	void map_mut( MapMutProc map_proc ) |  | ||||||
| 	{ |  | ||||||
| 	GEN_ASSERT_NOT_NULL(map_proc); | 	GEN_ASSERT_NOT_NULL(map_proc); | ||||||
|  |  | ||||||
| 		for ( ssize idx = 0; idx < ssize(Entries.num()); ++idx ) | 	for (ssize idx = 0; idx < ssize(table.Entries.num()); ++idx) { | ||||||
| 		{ | 		map_proc(table.Entries[idx].Key, &table.Entries[idx].Value); | ||||||
| 			map_proc( Entries[ idx ].Key, & Entries[ idx ].Value ); |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| 	void grow() | template<typename Type> inline | ||||||
| 	{ | void grow(HashTable<Type>& table) { | ||||||
| 		ssize new_num = Array<Entry>::grow_formula( Entries.num() ); | 	ssize new_num = Array<HashTableEntry<Type>>::grow_formula(table.Entries.num()); | ||||||
| 		rehash( new_num ); | 	rehash(table, new_num); | ||||||
| } | } | ||||||
|  |  | ||||||
| 	void rehash( ssize new_num ) | template<typename Type> inline | ||||||
|  | void rehash(HashTable<Type>& table, ssize new_num) | ||||||
| { | { | ||||||
| 	ssize last_added_index; | 	ssize last_added_index; | ||||||
|  | 	HashTable<Type> new_ht = hashtable_init_reserve<Type>(table.Hashes.get_header()->Allocator, new_num); | ||||||
|  |  | ||||||
| 		HashTable<Type> new_ht = init_reserve( Hashes.get_header()->Allocator, new_num ); | 	for (ssize idx = 0; idx < ssize(table.Entries.num()); ++idx) | ||||||
| 		for ( ssize idx = 0; idx < ssize(Entries.num()); ++idx ) |  | ||||||
| 	{ | 	{ | ||||||
| 			FindResult find_result; | 		HashTableFindResult find_result; | ||||||
|  | 		HashTableEntry<Type>& entry = table.Entries[idx]; | ||||||
|  |  | ||||||
| 			Entry& entry     = Entries[ idx ]; | 		find_result = find(new_ht, entry.Key); | ||||||
| 			find_result      = new_ht.find( entry.Key ); | 		last_added_index = add_entry(new_ht, entry.Key); | ||||||
| 			last_added_index = new_ht.add_entry( entry.Key ); |  | ||||||
|  |  | ||||||
| 		if (find_result.PrevIndex < 0) | 		if (find_result.PrevIndex < 0) | ||||||
| 			new_ht.Hashes[find_result.HashIndex] = last_added_index; | 			new_ht.Hashes[find_result.HashIndex] = last_added_index; | ||||||
| @@ -416,136 +490,130 @@ struct HashTable | |||||||
| 		new_ht.Entries[last_added_index].Value = entry.Value; | 		new_ht.Entries[last_added_index].Value = entry.Value; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 		destroy(); | 	destroy(table); | ||||||
| 		*this = new_ht; | 	table = new_ht; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	void rehash_fast() | template<typename Type> inline | ||||||
|  | void rehash_fast(HashTable<Type>& table) | ||||||
| { | { | ||||||
| 	ssize idx; | 	ssize idx; | ||||||
|  |  | ||||||
| 		for ( idx = 0; idx < ssize(Entries.num()); idx++ ) | 	for (idx = 0; idx < ssize(table.Entries.num()); idx++) | ||||||
| 			Entries[ idx ].Next = -1; | 		table.Entries[idx].Next = -1; | ||||||
|  |  | ||||||
| 		for ( idx = 0; idx < ssize(Hashes.num()); idx++ ) | 	for (idx = 0; idx < ssize(table.Hashes.num()); idx++) | ||||||
| 			Hashes[ idx ] = -1; | 		table.Hashes[idx] = -1; | ||||||
|  |  | ||||||
| 		for ( idx = 0; idx < ssize(Entries.num()); idx++ ) | 	for (idx = 0; idx < ssize(table.Entries.num()); idx++) | ||||||
| 	{ | 	{ | ||||||
| 			Entry*     entry; | 		HashTableEntry<Type>* entry; | ||||||
| 			FindResult find_result; | 		HashTableFindResult find_result; | ||||||
|  |  | ||||||
| 			entry       = & Entries[ idx ]; | 		entry = &table.Entries[idx]; | ||||||
| 			find_result = find( entry->Key ); | 		find_result = find(table, entry->Key); | ||||||
|  |  | ||||||
| 		if (find_result.PrevIndex < 0) | 		if (find_result.PrevIndex < 0) | ||||||
| 				Hashes[ find_result.HashIndex ] = idx; | 			table.Hashes[find_result.HashIndex] = idx; | ||||||
| 		else | 		else | ||||||
| 				Entries[ find_result.PrevIndex ].Next = idx; | 			table.Entries[find_result.PrevIndex].Next = idx; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| 	void remove( u64 key ) | template<typename Type> inline | ||||||
| 	{ | void remove(HashTable<Type>& table, u64 key) { | ||||||
| 		FindResult find_result = find( key); | 	HashTableFindResult find_result = find(table, key); | ||||||
|  |  | ||||||
| 		if ( find_result.EntryIndex >= 0 ) | 	if (find_result.EntryIndex >= 0) { | ||||||
| 		{ | 		table.Entries.remove_at(find_result.EntryIndex); | ||||||
| 			Entries.remove_at( find_result.EntryIndex ); | 		rehash_fast(table); | ||||||
| 			rehash_fast(); |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| 	void remove_entry( ssize idx ) | template<typename Type> inline | ||||||
| 	{ | void remove_entry(HashTable<Type>& table, ssize idx) { | ||||||
| 		Entries.remove_at( idx ); | 	table.Entries.remove_at(idx); | ||||||
| } | } | ||||||
|  |  | ||||||
| 	void set( u64 key, Type value ) | template<typename Type> inline | ||||||
|  | void set(HashTable<Type>& table, u64 key, Type value) | ||||||
| { | { | ||||||
| 	ssize idx; | 	ssize idx; | ||||||
| 		FindResult find_result; | 	HashTableFindResult find_result; | ||||||
|  |  | ||||||
| 		if ( full() ) | 	if (full(table)) | ||||||
| 			grow(); | 		grow(table); | ||||||
|  |  | ||||||
| 		find_result = find( key ); | 	find_result = find(table, key); | ||||||
| 		if ( find_result.EntryIndex >= 0 ) | 	if (find_result.EntryIndex >= 0) { | ||||||
| 		{ |  | ||||||
| 		idx = find_result.EntryIndex; | 		idx = find_result.EntryIndex; | ||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| 	{ | 	{ | ||||||
| 			idx = add_entry( key ); | 		idx = add_entry(table, key); | ||||||
|  |  | ||||||
| 			if ( find_result.PrevIndex >= 0 ) | 		if (find_result.PrevIndex >= 0) { | ||||||
| 			{ | 			table.Entries[find_result.PrevIndex].Next = idx; | ||||||
| 				Entries[ find_result.PrevIndex ].Next = idx; |  | ||||||
| 		} | 		} | ||||||
| 			else | 		else { | ||||||
| 			{ | 			table.Hashes[find_result.HashIndex] = idx; | ||||||
| 				Hashes[ find_result.HashIndex ] = idx; |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 		Entries[ idx ].Value = value; | 	table.Entries[idx].Value = value; | ||||||
|  |  | ||||||
| 		if ( full() ) | 	if (full(table)) | ||||||
| 			grow(); | 		grow(table); | ||||||
| } | } | ||||||
|  |  | ||||||
| 	ssize slot( u64 key ) | template<typename Type> inline | ||||||
| 	{ | ssize slot(HashTable<Type>& table, u64 key) { | ||||||
| 		for ( ssize idx = 0; idx < ssize(Hashes.num()); ++idx ) | 	for (ssize idx = 0; idx < ssize(table.Hashes.num()); ++idx) | ||||||
| 			if ( Hashes[ idx ] == key ) | 		if (table.Hashes[idx] == key) | ||||||
| 			return idx; | 			return idx; | ||||||
|  |  | ||||||
| 	return -1; | 	return -1; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	Array< ssize>    Hashes; | template<typename Type> inline | ||||||
| 	Array< Entry> Entries; | ssize add_entry(HashTable<Type>& table, u64 key) { | ||||||
|  |  | ||||||
| protected: |  | ||||||
|  |  | ||||||
| 	ssize add_entry( u64 key ) |  | ||||||
| 	{ |  | ||||||
| 	ssize idx; | 	ssize idx; | ||||||
| 		Entry entry = { key, -1 }; | 	HashTableEntry<Type> entry = { key, -1 }; | ||||||
|  |  | ||||||
| 		idx = Entries.num(); | 	idx = table.Entries.num(); | ||||||
| 		Entries.append( entry ); | 	table.Entries.append(entry); | ||||||
| 	return idx; | 	return idx; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	FindResult find( u64 key ) | template<typename Type> inline | ||||||
|  | HashTableFindResult find(HashTable<Type>& table, u64 key) | ||||||
| { | { | ||||||
| 		FindResult result = { -1, -1, -1 }; | 	HashTableFindResult result = { -1, -1, -1 }; | ||||||
|  |  | ||||||
| 		if ( Hashes.num() > 0 ) | 	if (table.Hashes.num() > 0) | ||||||
| 	{ | 	{ | ||||||
| 			result.HashIndex    = key % Hashes.num(); | 		result.HashIndex = key % table.Hashes.num(); | ||||||
| 			result.EntryIndex  = Hashes[ result.HashIndex ]; | 		result.EntryIndex = table.Hashes[result.HashIndex]; | ||||||
|  |  | ||||||
| 		while (result.EntryIndex >= 0) | 		while (result.EntryIndex >= 0) | ||||||
| 		{ | 		{ | ||||||
| 				if ( Entries[ result.EntryIndex ].Key == key ) | 			if (table.Entries[result.EntryIndex].Key == key) | ||||||
| 				break; | 				break; | ||||||
|  |  | ||||||
| 			result.PrevIndex = result.EntryIndex; | 			result.PrevIndex = result.EntryIndex; | ||||||
| 				result.EntryIndex = Entries[ result.EntryIndex ].Next; | 			result.EntryIndex = table.Entries[result.EntryIndex].Next; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	b32 full() | template<typename Type> inline | ||||||
| 	{ | bool full(HashTable<Type>& table) { | ||||||
| 		usize critical_load = usize( CriticalLoadScale * f32(Hashes.num()) ); | 	usize critical_load = usize(HashTable<Type>::CriticalLoadScale * f32(table.Hashes.num())); | ||||||
| 		b32 result = Entries.num() > critical_load; | 	b32 result = table.Entries.num() > critical_load; | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
| }; | #pragma endregion HashTable | ||||||
|  |  | ||||||
| #pragma endregion Containers | #pragma endregion Containers | ||||||
|   | |||||||
| @@ -334,7 +334,7 @@ ssize virtual_memory_page_size( ssize* alignment_out ) | |||||||
|  |  | ||||||
| #pragma endregion VirtualMemory | #pragma endregion VirtualMemory | ||||||
|  |  | ||||||
| void* Arena::allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ) | void* arena_allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ) | ||||||
| { | { | ||||||
| 	Arena* arena = rcast(Arena*, allocator_data); | 	Arena* arena = rcast(Arena*, allocator_data); | ||||||
| 	void*      ptr   = NULL; | 	void*      ptr   = NULL; | ||||||
| @@ -384,7 +384,7 @@ void* Arena::allocator_proc( void* allocator_data, AllocType type, ssize size, s | |||||||
| 	return ptr; | 	return ptr; | ||||||
| } | } | ||||||
|  |  | ||||||
| void* Pool::allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ) | void* pool_allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ) | ||||||
| { | { | ||||||
| 	Pool* pool = rcast( Pool*, allocator_data); | 	Pool* pool = rcast( Pool*, allocator_data); | ||||||
| 	void* ptr  = NULL; | 	void* ptr  = NULL; | ||||||
| @@ -457,7 +457,7 @@ void* Pool::allocator_proc( void* allocator_data, AllocType type, ssize size, ss | |||||||
| 	return ptr; | 	return ptr; | ||||||
| } | } | ||||||
|  |  | ||||||
| Pool Pool::init_align( AllocatorInfo backing, ssize num_blocks, ssize block_size, ssize block_align ) | Pool pool_init_align( AllocatorInfo backing, ssize num_blocks, ssize block_size, ssize block_align ) | ||||||
| { | { | ||||||
| 	Pool pool = {}; | 	Pool pool = {}; | ||||||
|  |  | ||||||
| @@ -495,16 +495,16 @@ Pool Pool::init_align( AllocatorInfo backing, ssize num_blocks, ssize block_size | |||||||
| 	return pool; | 	return pool; | ||||||
| } | } | ||||||
|  |  | ||||||
| void Pool::clear() | void clear(Pool& pool) | ||||||
| { | { | ||||||
| 	ssize actual_block_size, block_index; | 	ssize actual_block_size, block_index; | ||||||
| 	void* curr; | 	void* curr; | ||||||
| 	uptr* end; | 	uptr* end; | ||||||
|  |  | ||||||
| 	actual_block_size = BlockSize + BlockAlign; | 	actual_block_size = pool.BlockSize + pool.BlockAlign; | ||||||
|  |  | ||||||
| 	curr = PhysicalStart; | 	curr = pool.PhysicalStart; | ||||||
| 	for ( block_index = 0; block_index < NumBlocks - 1; block_index++ ) | 	for ( block_index = 0; block_index < pool.NumBlocks - 1; block_index++ ) | ||||||
| 	{ | 	{ | ||||||
| 		uptr* next = ( uptr* ) curr; | 		uptr* next = ( uptr* ) curr; | ||||||
| 		*next      = ( uptr  ) curr + actual_block_size; | 		*next      = ( uptr  ) curr + actual_block_size; | ||||||
| @@ -514,7 +514,7 @@ void Pool::clear() | |||||||
| 	end  =  ( uptr* ) curr; | 	end  =  ( uptr* ) curr; | ||||||
| 	*end =  ( uptr )  NULL; | 	*end =  ( uptr )  NULL; | ||||||
|  |  | ||||||
| 	FreeList = PhysicalStart; | 	pool.FreeList = pool.PhysicalStart; | ||||||
| } | } | ||||||
|  |  | ||||||
| #pragma endregion Memory | #pragma endregion Memory | ||||||
|   | |||||||
| @@ -170,25 +170,78 @@ b32 gen_vm_purge( VirtualMemory vm ); | |||||||
| //! Retrieve VM's page size and alignment. | //! Retrieve VM's page size and alignment. | ||||||
| ssize gen_virtual_memory_page_size( ssize* alignment_out ); | ssize gen_virtual_memory_page_size( ssize* alignment_out ); | ||||||
|  |  | ||||||
|  | #pragma region Arena | ||||||
|  | struct Arena; | ||||||
|  |  | ||||||
|  | AllocatorInfo allocator_info( Arena& arena ); | ||||||
|  |  | ||||||
|  | // Remove static keyword and rename allocator_proc | ||||||
|  | void* arena_allocator_proc(void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags); | ||||||
|  |  | ||||||
|  | // Add these declarations after the Arena struct | ||||||
|  | Arena init_from_allocator(AllocatorInfo backing, ssize size); | ||||||
|  | Arena init_from_memory( void* start, ssize size ); | ||||||
|  | Arena init_sub(Arena& parent, ssize size); | ||||||
|  | ssize alignment_of(Arena& arena, ssize alignment); | ||||||
|  |  | ||||||
|  | // This id is defined by Unreal for asserts | ||||||
|  | #pragma push_macro("check") | ||||||
|  | #undef check | ||||||
|  | void   check(Arena& arena); | ||||||
|  | #pragma pop_macro("check") | ||||||
|  |  | ||||||
|  | void  free(Arena& arena); | ||||||
|  | ssize size_remaining(Arena& arena, ssize alignment); | ||||||
|  |  | ||||||
| struct Arena | struct Arena | ||||||
| { | { | ||||||
| 	static | 	AllocatorInfo Backing; | ||||||
| 	void* allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ); | 	void*         PhysicalStart; | ||||||
|  | 	ssize         TotalSize; | ||||||
|  | 	ssize         TotalUsed; | ||||||
|  | 	ssize         TempCount; | ||||||
|  |  | ||||||
| 	static | #if 1 | ||||||
|  | #pragma region Member Mapping | ||||||
|  | 	forceinline operator AllocatorInfo() { return GEN_NS allocator_info(* this); } | ||||||
|  |  | ||||||
|  | 	forceinline static void* allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ) { return GEN_NS arena_allocator_proc( allocator_data, type, size, alignment, old_memory, old_size, flags ); } | ||||||
|  | 	forceinline static Arena init_from_memory( void* start, ssize size )                                                                                      { return GEN_NS init_from_memory( start, size ); } | ||||||
|  | 	forceinline static Arena init_from_allocator( AllocatorInfo backing, ssize size )                                                                         { return GEN_NS init_from_allocator( backing, size ); } | ||||||
|  | 	forceinline static Arena init_sub( Arena& parent, ssize size )                                                                                            { return GEN_NS init_from_allocator( parent.Backing, size ); } | ||||||
|  | 	forceinline        ssize alignment_of( ssize alignment )                                                                                                  { return GEN_NS alignment_of(* this, alignment); } | ||||||
|  | 	forceinline        void  free()                                                                                                                           { return GEN_NS free(* this);  } | ||||||
|  | 	forceinline        ssize size_remaining( ssize alignment )                                                                                                { return GEN_NS size_remaining(* this, alignment); } | ||||||
|  |  | ||||||
|  | // This id is defined by Unreal for asserts | ||||||
|  | #pragma push_macro("check") | ||||||
|  | #undef check | ||||||
|  | 	forceinline void check() { GEN_NS check(* this); } | ||||||
|  | #pragma pop_macro("check") | ||||||
|  |  | ||||||
|  | #pragma endregion Member Mapping | ||||||
|  | #endif | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | inline | ||||||
|  | AllocatorInfo allocator_info( Arena& arena ) { | ||||||
|  | 	return { arena_allocator_proc, &arena }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline | ||||||
| Arena init_from_memory( void* start, ssize size ) | Arena init_from_memory( void* start, ssize size ) | ||||||
| { | { | ||||||
| 		return | 	Arena arena = { | ||||||
| 		{ |  | ||||||
| 		{ nullptr, nullptr }, | 		{ nullptr, nullptr }, | ||||||
| 		start, | 		start, | ||||||
| 		size, | 		size, | ||||||
| 		0, | 		0, | ||||||
| 		0 | 		0 | ||||||
| 	}; | 	}; | ||||||
|  | 	return arena; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	static | inline | ||||||
| Arena init_from_allocator(AllocatorInfo backing, ssize size) | Arena init_from_allocator(AllocatorInfo backing, ssize size) | ||||||
| { | { | ||||||
|     Arena result = |     Arena result = | ||||||
| @@ -202,19 +255,20 @@ struct Arena | |||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	static | inline | ||||||
| Arena init_sub(Arena& parent, ssize size) | Arena init_sub(Arena& parent, ssize size) | ||||||
| { | { | ||||||
|     return init_from_allocator(parent.Backing, size); |     return init_from_allocator(parent.Backing, size); | ||||||
| } | } | ||||||
|  |  | ||||||
| 	ssize alignment_of( ssize alignment ) | inline | ||||||
|  | ssize alignment_of(Arena& arena, ssize alignment) | ||||||
| { | { | ||||||
|     ssize alignment_offset, result_pointer, mask; |     ssize alignment_offset, result_pointer, mask; | ||||||
|     GEN_ASSERT(is_power_of_two(alignment)); |     GEN_ASSERT(is_power_of_two(alignment)); | ||||||
|  |  | ||||||
|     alignment_offset = 0; |     alignment_offset = 0; | ||||||
| 		result_pointer   = (ssize) PhysicalStart + TotalUsed; |     result_pointer  = (ssize)arena.PhysicalStart + arena.TotalUsed; | ||||||
|     mask            = alignment - 1; |     mask            = alignment - 1; | ||||||
|  |  | ||||||
|     if (result_pointer & mask) |     if (result_pointer & mask) | ||||||
| @@ -223,68 +277,73 @@ struct Arena | |||||||
|     return alignment_offset; |     return alignment_offset; | ||||||
| } | } | ||||||
|  |  | ||||||
| // This id is defined by Unreal for asserts |  | ||||||
| #pragma push_macro("check") | #pragma push_macro("check") | ||||||
| #undef check | #undef check | ||||||
| 	void check() | inline | ||||||
|  | void check(Arena& arena) | ||||||
| { | { | ||||||
| 		GEN_ASSERT( TempCount == 0 ); |     GEN_ASSERT(arena.TempCount == 0); | ||||||
| } | } | ||||||
| #pragma pop_macro("check") | #pragma pop_macro("check") | ||||||
|  |  | ||||||
| 	void free() | inline | ||||||
|  | void free(Arena& arena) | ||||||
| { | { | ||||||
| 		if ( Backing.Proc ) |     if (arena.Backing.Proc) | ||||||
|     { |     { | ||||||
| 			gen::free( Backing, PhysicalStart ); |         gen::free(arena.Backing, arena.PhysicalStart); | ||||||
| 			PhysicalStart = nullptr; |         arena.PhysicalStart = nullptr; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| 	ssize size_remaining( ssize alignment ) | inline | ||||||
|  | ssize size_remaining(Arena& arena, ssize alignment) | ||||||
| { | { | ||||||
| 		ssize result = TotalSize - ( TotalUsed + alignment_of( alignment ) ); |     ssize result = arena.TotalSize - (arena.TotalUsed + alignment_of(arena, alignment)); | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
|  | #pragma endregion Arena | ||||||
|  |  | ||||||
| 	AllocatorInfo Backing; | #pragma region FixedArena | ||||||
| 	void*         PhysicalStart; | template<s32 Size> | ||||||
| 	ssize            TotalSize; | struct FixedArena; | ||||||
| 	ssize            TotalUsed; |  | ||||||
| 	ssize            TempCount; |  | ||||||
|  |  | ||||||
| 	operator AllocatorInfo() | template<s32 Size> AllocatorInfo    allocator_info( FixedArena<Size>& fixed_arena ); | ||||||
| 	{ | template<s32 Size> FixedArena<Size> fixed_arena_init(); | ||||||
| 		return { allocator_proc, this }; | template<s32 Size> ssize            size_remaining(FixedArena<Size>& fixed_arena, ssize alignment); | ||||||
| 	} |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| // Just a wrapper around using an arena with memory associated with its scope instead of from an allocator. | // Just a wrapper around using an arena with memory associated with its scope instead of from an allocator. | ||||||
| // Used for static segment or stack allocations. | // Used for static segment or stack allocations. | ||||||
| template< s32 Size > | template< s32 Size > | ||||||
| struct FixedArena | struct FixedArena | ||||||
| { | { | ||||||
| 	static |  | ||||||
| 	FixedArena init() |  | ||||||
| 	{ |  | ||||||
| 		FixedArena result = { Arena::init_from_memory( result.memory, Size ), {0} }; |  | ||||||
| 		return result; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ssize size_remaining( ssize alignment ) |  | ||||||
| 	{ |  | ||||||
| 		return arena.size_remaining( alignment ); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	operator AllocatorInfo() |  | ||||||
| 	{ |  | ||||||
| 		return { Arena::allocator_proc, &arena }; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	Arena arena; |  | ||||||
|     char  memory[Size]; |     char  memory[Size]; | ||||||
|  |     Arena arena; | ||||||
|  |  | ||||||
|  | #if 1 | ||||||
|  | #pragma region Member Mapping | ||||||
|  |     forceinline operator AllocatorInfo() { return GEN_NS allocator_info(* this); } | ||||||
|  |  | ||||||
|  |     forceinline static FixedArena init()                          { FixedArena result; GEN_NS fixed_arena_init<Size>(result); return result; } | ||||||
|  |     forceinline ssize             size_remaining(ssize alignment) { GEN_NS size_remaining(*this, alignment); } | ||||||
|  | #pragma endregion Member Mapping | ||||||
|  | #endif | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | template<s32 Size> inline | ||||||
|  | AllocatorInfo allocator_info( FixedArena<Size>& fixed_arena ) { return { arena_allocator_proc, & fixed_arena.arena }; } | ||||||
|  |  | ||||||
|  | template<s32 Size> inline | ||||||
|  | void fixed_arena_init(FixedArena<Size>& result) { | ||||||
|  |     zero_size(& result.memory[0], Size); | ||||||
|  |     result.arena = init_from_memory(& result.memory[0], Size); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template<s32 Size> inline | ||||||
|  | ssize size_remaining(FixedArena<Size>& fixed_arena, ssize alignment) { | ||||||
|  |     return size_remaining(fixed_arena.arena, alignment); | ||||||
|  | } | ||||||
|  |  | ||||||
| using Arena_1KB   = FixedArena< kilobytes( 1 ) >; | using Arena_1KB   = FixedArena< kilobytes( 1 ) >; | ||||||
| using Arena_4KB   = FixedArena< kilobytes( 4 ) >; | using Arena_4KB   = FixedArena< kilobytes( 4 ) >; | ||||||
| using Arena_8KB   = FixedArena< kilobytes( 8 ) >; | using Arena_8KB   = FixedArena< kilobytes( 8 ) >; | ||||||
| @@ -297,31 +356,20 @@ using Arena_512KB = FixedArena< kilobytes( 512 ) >; | |||||||
| using Arena_1MB   = FixedArena< megabytes( 1 ) >; | using Arena_1MB   = FixedArena< megabytes( 1 ) >; | ||||||
| using Arena_2MB   = FixedArena< megabytes( 2 ) >; | using Arena_2MB   = FixedArena< megabytes( 2 ) >; | ||||||
| using Arena_4MB   = FixedArena< megabytes( 4 ) >; | using Arena_4MB   = FixedArena< megabytes( 4 ) >; | ||||||
|  | #pragma endregion FixedArena | ||||||
|  |  | ||||||
|  | #pragma region Pool | ||||||
|  | struct Pool; | ||||||
|  |  | ||||||
|  | AllocatorInfo allocator_info(Pool& pool); | ||||||
|  | void*         pool_allocator_proc(void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags); | ||||||
|  | Pool          pool_init(AllocatorInfo backing, ssize num_blocks, ssize block_size); | ||||||
|  | Pool          pool_init_align(AllocatorInfo backing, ssize num_blocks, ssize block_size, ssize block_align); | ||||||
|  | void          clear(Pool& pool); | ||||||
|  | void          free(Pool& pool); | ||||||
|  |  | ||||||
| struct Pool | struct Pool | ||||||
| { | { | ||||||
| 	static |  | ||||||
| 	void* allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ); |  | ||||||
|  |  | ||||||
| 	static |  | ||||||
| 	Pool init( AllocatorInfo backing, ssize num_blocks, ssize block_size ) |  | ||||||
| 	{ |  | ||||||
| 		return init_align( backing, num_blocks, block_size, GEN_DEFAULT_MEMORY_ALIGNMENT ); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	static |  | ||||||
| 	Pool init_align( AllocatorInfo backing, ssize num_blocks, ssize block_size, ssize block_align ); |  | ||||||
|  |  | ||||||
| 	void clear(); |  | ||||||
|  |  | ||||||
| 	void free() |  | ||||||
| 	{ |  | ||||||
| 		if ( Backing.Proc ) |  | ||||||
| 		{ |  | ||||||
| 			gen::free( Backing, PhysicalStart ); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	AllocatorInfo Backing; | 	AllocatorInfo Backing; | ||||||
| 	void*         PhysicalStart; | 	void*         PhysicalStart; | ||||||
| 	void*         FreeList; | 	void*         FreeList; | ||||||
| @@ -330,12 +378,34 @@ struct Pool | |||||||
| 	ssize         TotalSize; | 	ssize         TotalSize; | ||||||
| 	ssize         NumBlocks; | 	ssize         NumBlocks; | ||||||
|  |  | ||||||
| 	operator AllocatorInfo() | #pragma region Member Mapping | ||||||
| 	{ |     forceinline operator AllocatorInfo() { return GEN_NS allocator_info(* this); } | ||||||
| 		return { allocator_proc, this }; |  | ||||||
| 	} |     forceinline static void* allocator_proc(void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags) { return GEN_NS pool_allocator_proc(allocator_data, type, size, alignment, old_memory, old_size, flags); } | ||||||
|  |     forceinline static Pool  init(AllocatorInfo backing, ssize num_blocks, ssize block_size)                                                                { return GEN_NS pool_init(backing, num_blocks, block_size); } | ||||||
|  |     forceinline static Pool  init_align(AllocatorInfo backing, ssize num_blocks, ssize block_size, ssize block_align)                                       { return GEN_NS pool_init_align(backing, num_blocks, block_size, block_align); } | ||||||
|  |     forceinline        void  clear() { GEN_NS clear(* this); } | ||||||
|  |     forceinline        void  free()  { GEN_NS free(* this); } | ||||||
|  | #pragma endregion | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | inline | ||||||
|  | AllocatorInfo allocator_info(Pool& pool) { | ||||||
|  |    return { pool_allocator_proc, &pool }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline | ||||||
|  | Pool pool_init(AllocatorInfo backing, ssize num_blocks, ssize block_size) { | ||||||
|  |    return pool_init_align(backing, num_blocks, block_size, GEN_DEFAULT_MEMORY_ALIGNMENT); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline | ||||||
|  | void free(Pool& pool) { | ||||||
|  |    if(pool.Backing.Proc) { | ||||||
|  |        GEN_NS free(pool.Backing, pool.PhysicalStart); | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | #pragma endregion Pool | ||||||
|  |  | ||||||
| inline | inline | ||||||
| b32 is_power_of_two( ssize x ) { | b32 is_power_of_two( ssize x ) { | ||||||
|   | |||||||
| @@ -4,20 +4,9 @@ | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #pragma region String | #pragma region String | ||||||
|  | String string_make_length( AllocatorInfo allocator, char const* str, ssize length ) | ||||||
| String String::fmt( AllocatorInfo allocator, char* buf, ssize buf_size, char const* fmt, ... ) |  | ||||||
| { | { | ||||||
| 	va_list va; | 	constexpr ssize header_size = sizeof( StringHeader ); | ||||||
| 	va_start( va, fmt ); |  | ||||||
| 	str_fmt_va( buf, buf_size, fmt, va ); |  | ||||||
| 	va_end( va ); |  | ||||||
|  |  | ||||||
| 	return make( allocator, buf ); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| String String::make_length( AllocatorInfo allocator, char const* str, ssize length ) |  | ||||||
| { |  | ||||||
| 	constexpr ssize header_size = sizeof( Header ); |  | ||||||
|  |  | ||||||
| 	s32   alloc_size = header_size + length + 1; | 	s32   alloc_size = header_size + length + 1; | ||||||
| 	void* allocation = alloc( allocator, alloc_size ); | 	void* allocation = alloc( allocator, alloc_size ); | ||||||
| @@ -25,8 +14,8 @@ String String::make_length( AllocatorInfo allocator, char const* str, ssize leng | |||||||
| 	if ( allocation == nullptr ) | 	if ( allocation == nullptr ) | ||||||
| 		return { nullptr }; | 		return { nullptr }; | ||||||
|  |  | ||||||
| 	Header& | 	StringHeader& | ||||||
| 	header = * rcast(Header*, allocation); | 	header = * rcast(StringHeader*, allocation); | ||||||
| 	header = { allocator, length, length }; | 	header = { allocator, length, length }; | ||||||
|  |  | ||||||
| 	String  result = { rcast( char*, allocation) + header_size }; | 	String  result = { rcast( char*, allocation) + header_size }; | ||||||
| @@ -41,9 +30,9 @@ String String::make_length( AllocatorInfo allocator, char const* str, ssize leng | |||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| String String::make_reserve( AllocatorInfo allocator, ssize capacity ) | String string_make_reserve( AllocatorInfo allocator, ssize capacity ) | ||||||
| { | { | ||||||
| 	constexpr ssize header_size = sizeof( Header ); | 	constexpr ssize header_size = sizeof( StringHeader ); | ||||||
|  |  | ||||||
| 	s32   alloc_size = header_size + capacity + 1; | 	s32   alloc_size = header_size + capacity + 1; | ||||||
| 	void* allocation = alloc( allocator, alloc_size ); | 	void* allocation = alloc( allocator, alloc_size ); | ||||||
| @@ -53,8 +42,8 @@ String String::make_reserve( AllocatorInfo allocator, ssize capacity ) | |||||||
|  |  | ||||||
| 	mem_set( allocation, 0, alloc_size ); | 	mem_set( allocation, 0, alloc_size ); | ||||||
|  |  | ||||||
| 	Header* | 	StringHeader* | ||||||
| 		header            = rcast(Header*, allocation); | 	header            = rcast(StringHeader*, allocation); | ||||||
| 	header->Allocator = allocator; | 	header->Allocator = allocator; | ||||||
| 	header->Capacity  = capacity; | 	header->Capacity  = capacity; | ||||||
| 	header->Length    = 0; | 	header->Length    = 0; | ||||||
| @@ -62,69 +51,4 @@ String String::make_reserve( AllocatorInfo allocator, ssize capacity ) | |||||||
| 	String result = { rcast(char*, allocation) + header_size }; | 	String result = { rcast(char*, allocation) + header_size }; | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| String String::fmt_buf( AllocatorInfo allocator, char const* fmt, ... ) |  | ||||||
| { |  | ||||||
| 	local_persist thread_local |  | ||||||
| 	char buf[ GEN_PRINTF_MAXLEN ] = { 0 }; |  | ||||||
|  |  | ||||||
| 	va_list va; |  | ||||||
| 	va_start( va, fmt ); |  | ||||||
| 	str_fmt_va( buf, GEN_PRINTF_MAXLEN, fmt, va ); |  | ||||||
| 	va_end( va ); |  | ||||||
|  |  | ||||||
| 	return make( allocator, buf ); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool String::append_fmt( char const* fmt, ... ) |  | ||||||
| { |  | ||||||
| 	ssize   res; |  | ||||||
| 	char buf[ GEN_PRINTF_MAXLEN ] = { 0 }; |  | ||||||
|  |  | ||||||
| 	va_list va; |  | ||||||
| 	va_start( va, fmt ); |  | ||||||
| 	res = str_fmt_va( buf, count_of( buf ) - 1, fmt, va ) - 1; |  | ||||||
| 	va_end( va ); |  | ||||||
|  |  | ||||||
| 	return append( buf, res ); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool String::make_space_for( char const* str, ssize add_len ) |  | ||||||
| { |  | ||||||
| 	ssize available = avail_space(); |  | ||||||
|  |  | ||||||
| 	// NOTE: Return if there is enough space left |  | ||||||
| 	if ( available >= add_len ) |  | ||||||
| 	{ |  | ||||||
| 		return true; |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		ssize new_len, old_size, new_size; |  | ||||||
|  |  | ||||||
| 		void* ptr; |  | ||||||
| 		void* new_ptr; |  | ||||||
|  |  | ||||||
| 		AllocatorInfo allocator = get_header().Allocator; |  | ||||||
| 		Header*       header	= nullptr; |  | ||||||
|  |  | ||||||
| 		new_len  = grow_formula( length() + add_len ); |  | ||||||
| 		ptr      = & get_header(); |  | ||||||
| 		old_size = size_of( Header ) + length() + 1; |  | ||||||
| 		new_size = size_of( Header ) + new_len + 1; |  | ||||||
|  |  | ||||||
| 		new_ptr = resize( allocator, ptr, old_size, new_size ); |  | ||||||
|  |  | ||||||
| 		if ( new_ptr == nullptr ) |  | ||||||
| 			return false; |  | ||||||
|  |  | ||||||
| 		header            = rcast( Header*, new_ptr); |  | ||||||
| 		header->Allocator = allocator; |  | ||||||
| 		header->Capacity  = new_len; |  | ||||||
|  |  | ||||||
| 		Data = rcast( char*, header + 1 ); |  | ||||||
|  |  | ||||||
| 		return true; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| #pragma endregion String | #pragma endregion String | ||||||
|   | |||||||
| @@ -19,8 +19,7 @@ struct StrC | |||||||
| #define txt( text )         StrC { sizeof( text ) - 1, ( text ) } | #define txt( text )         StrC { sizeof( text ) - 1, ( text ) } | ||||||
|  |  | ||||||
| inline | inline | ||||||
| StrC to_str( char const* str ) | StrC to_str( char const* str ) { | ||||||
| { |  | ||||||
| 	return { str_len( str ), str }; | 	return { str_len( str ), str }; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -28,232 +27,441 @@ StrC to_str( char const* str ) | |||||||
| // This is directly based off the ZPL string api. | // This is directly based off the ZPL string api. | ||||||
| // They used a header pattern | // They used a header pattern | ||||||
| // I kept it for simplicty of porting but its not necessary to keep it that way. | // I kept it for simplicty of porting but its not necessary to keep it that way. | ||||||
| struct String | #pragma region String | ||||||
| { | struct String; | ||||||
| 	struct Header | struct StringHeader; | ||||||
| 	{ |  | ||||||
|  | // Forward declarations for all file-scope functions | ||||||
|  | String        string_make(AllocatorInfo allocator, char const* str); | ||||||
|  | String        string_make(AllocatorInfo allocator, StrC str); | ||||||
|  | String        string_make_reserve(AllocatorInfo allocator, ssize capacity); | ||||||
|  | String        string_make_length(AllocatorInfo allocator, char const* str, ssize length); | ||||||
|  | String        string_fmt(AllocatorInfo allocator, char* buf, ssize buf_size, char const* fmt, ...); | ||||||
|  | String        string_fmt_buf(AllocatorInfo allocator, char const* fmt, ...); | ||||||
|  | String        string_join(AllocatorInfo allocator, char const** parts, ssize num_parts, char const* glue); | ||||||
|  | usize         string_grow_formula(usize value); | ||||||
|  | bool          are_equal(String lhs, String rhs); | ||||||
|  | bool          are_equal(String lhs, StrC rhs); | ||||||
|  | bool          make_space_for(String& str, char const* to_append, ssize add_len); | ||||||
|  | bool          append(String& str, char c); | ||||||
|  | bool          append(String& str, char const* str_to_append); | ||||||
|  | bool          append(String& str, char const* str_to_append, ssize length); | ||||||
|  | bool          append(String& str, StrC str_to_append); | ||||||
|  | bool          append(String& str, const String other); | ||||||
|  | bool          append_fmt(String& str, char const* fmt, ...); | ||||||
|  | ssize         avail_space(String const& str); | ||||||
|  | char&         back(String& str); | ||||||
|  | bool          contains(String const& str, StrC substring); | ||||||
|  | bool          contains(String const& str, String const& substring); | ||||||
|  | ssize         capacity(String const& str); | ||||||
|  | void          clear(String& str); | ||||||
|  | String        duplicate(String const& str, AllocatorInfo allocator); | ||||||
|  | void          free(String& str); | ||||||
|  | StringHeader& get_header(String& str); | ||||||
|  | ssize         length(String const& str); | ||||||
|  | b32           starts_with(String const& str, StrC substring); | ||||||
|  | b32           starts_with(String const& str, String substring); | ||||||
|  | void          skip_line(String& str); | ||||||
|  | void          strip_space(String& str); | ||||||
|  | void          trim(String& str, char const* cut_set); | ||||||
|  | void          trim_space(String& str); | ||||||
|  | String        visualize_whitespace(String const& str); | ||||||
|  |  | ||||||
|  | struct StringHeader { | ||||||
| 	AllocatorInfo Allocator; | 	AllocatorInfo Allocator; | ||||||
| 	ssize         Capacity; | 	ssize         Capacity; | ||||||
| 	ssize         Length; | 	ssize         Length; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| 	static | struct String | ||||||
| 	usize grow_formula( usize value ) |  | ||||||
| { | { | ||||||
|  | 	char* Data; | ||||||
|  |  | ||||||
|  | #if 1 | ||||||
|  | #pragma region Member Mapping | ||||||
|  | 	forceinline static String make(AllocatorInfo allocator, char const* str)                { return GEN_NS string_make(allocator, str); } | ||||||
|  | 	forceinline static String make(AllocatorInfo allocator, StrC str)                       { return GEN_NS string_make(allocator, str); } | ||||||
|  | 	forceinline static String make_reserve(AllocatorInfo allocator, ssize cap)              { return GEN_NS string_make_reserve(allocator, cap); } | ||||||
|  | 	forceinline static String make_length(AllocatorInfo a, char const* s, ssize l)          { return GEN_NS string_make_length(a, s, l); } | ||||||
|  | 	forceinline static String join(AllocatorInfo a, char const** p, ssize n, char const* g) { return GEN_NS string_join(a, p, n, g); } | ||||||
|  | 	forceinline static usize  grow_formula(usize value)                                     { return GEN_NS string_grow_formula(value); } | ||||||
|  | 	forceinline static bool   are_equal(String lhs, String rhs)                             { return GEN_NS are_equal(lhs, rhs); } | ||||||
|  | 	forceinline static bool   are_equal(String lhs, StrC rhs)                               { return GEN_NS are_equal(lhs, rhs); } | ||||||
|  |  | ||||||
|  | 	static | ||||||
|  | 	String fmt(AllocatorInfo allocator, char* buf, ssize buf_size, char const* fmt, ...) { | ||||||
|  | 		va_list va; | ||||||
|  | 		va_start(va, fmt); | ||||||
|  | 		str_fmt_va(buf, buf_size, fmt, va); | ||||||
|  | 		va_end(va); | ||||||
|  | 		return GEN_NS string_make(allocator, buf); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	static | ||||||
|  | 	String fmt_buf(AllocatorInfo allocator, char const* fmt, ...) { | ||||||
|  | 		local_persist thread_local | ||||||
|  | 		char buf[GEN_PRINTF_MAXLEN] = { 0 }; | ||||||
|  | 		va_list va; | ||||||
|  | 		va_start(va, fmt); | ||||||
|  | 		str_fmt_va(buf, GEN_PRINTF_MAXLEN, fmt, va); | ||||||
|  | 		va_end(va); | ||||||
|  | 		return GEN_NS string_make(allocator, buf); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	forceinline bool          make_space_for(char const* str, ssize add_len) { return GEN_NS make_space_for(*this, str, add_len); } | ||||||
|  | 	forceinline bool          append(char c)                                 { return GEN_NS append(*this, c); } | ||||||
|  | 	forceinline bool          append(char const* str)                        { return GEN_NS append(*this, str); } | ||||||
|  | 	forceinline bool          append(char const* str, ssize length)          { return GEN_NS append(*this, str, length); } | ||||||
|  | 	forceinline bool          append(StrC str)                               { return GEN_NS append(*this, str); } | ||||||
|  | 	forceinline bool          append(const String other)                     { return GEN_NS append(*this, other); } | ||||||
|  | 	forceinline ssize         avail_space() const                            { return GEN_NS avail_space(*this); } | ||||||
|  | 	forceinline char&         back()                                         { return GEN_NS back(*this); } | ||||||
|  | 	forceinline bool          contains(StrC substring) const                 { return GEN_NS contains(*this, substring); } | ||||||
|  | 	forceinline bool          contains(String const& substring) const        { return GEN_NS contains(*this, substring); } | ||||||
|  | 	forceinline ssize         capacity() const                               { return GEN_NS capacity(*this); } | ||||||
|  | 	forceinline void          clear()                                        { GEN_NS clear(*this); } | ||||||
|  | 	forceinline String        duplicate(AllocatorInfo allocator) const       { return GEN_NS duplicate(*this, allocator); } | ||||||
|  | 	forceinline void          free()                                         { GEN_NS free(*this); } | ||||||
|  | 	forceinline ssize         length() const                                 { return GEN_NS length(*this); } | ||||||
|  | 	forceinline b32           starts_with(StrC substring) const              { return GEN_NS starts_with(*this, substring); } | ||||||
|  | 	forceinline b32           starts_with(String substring) const            { return GEN_NS starts_with(*this, substring); } | ||||||
|  | 	forceinline void          skip_line()                                    { GEN_NS skip_line(*this); } | ||||||
|  | 	forceinline void          strip_space()                                  { GEN_NS strip_space(*this); } | ||||||
|  | 	forceinline void          trim(char const* cut_set)                      { GEN_NS trim(*this, cut_set); } | ||||||
|  | 	forceinline void          trim_space()                                   { GEN_NS trim_space(*this); } | ||||||
|  | 	forceinline String        visualize_whitespace() const                   { return GEN_NS visualize_whitespace(*this); } | ||||||
|  | 	forceinline StringHeader& get_header()                                   { return GEN_NS get_header(*this); } | ||||||
|  |  | ||||||
|  | 	bool append_fmt(char const* fmt, ...) { | ||||||
|  | 		ssize res; | ||||||
|  | 		char buf[GEN_PRINTF_MAXLEN] = { 0 }; | ||||||
|  |  | ||||||
|  | 		va_list va; | ||||||
|  | 		va_start(va, fmt); | ||||||
|  | 		res = str_fmt_va(buf, count_of(buf) - 1, fmt, va) - 1; | ||||||
|  | 		va_end(va); | ||||||
|  |  | ||||||
|  | 		return GEN_NS append(*this, buf, res); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	forceinline operator bool()              { return Data != nullptr; } | ||||||
|  | 	forceinline operator char*()             { return Data; } | ||||||
|  | 	forceinline operator char const*() const { return Data; } | ||||||
|  | 	forceinline operator StrC() const        { return { length(), Data }; } | ||||||
|  |  | ||||||
|  | 	String const& operator=(String const& other) const { | ||||||
|  | 		if (this == &other) | ||||||
|  | 			return *this; | ||||||
|  |  | ||||||
|  | 		String* this_ = ccast(String*, this); | ||||||
|  | 		this_->Data = other.Data; | ||||||
|  |  | ||||||
|  | 		return *this; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	forceinline char& operator[](ssize index)             { return Data[index]; } | ||||||
|  | 	forceinline char const& operator[](ssize index) const { return Data[index]; } | ||||||
|  |  | ||||||
|  | 	forceinline char* begin() const { return Data; } | ||||||
|  | 	forceinline char* end() const   { return Data + length(); } | ||||||
|  | #pragma endregion Member Mapping | ||||||
|  | #endif | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | inline | ||||||
|  | usize string_grow_formula(usize value) { | ||||||
| 	// Using a very aggressive growth formula to reduce time mem_copying with recursive calls to append in this library. | 	// Using a very aggressive growth formula to reduce time mem_copying with recursive calls to append in this library. | ||||||
| 	return 4 * value + 8; | 	return 4 * value + 8; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	static | inline | ||||||
| 	String make( AllocatorInfo allocator, char const* str ) | String string_make(AllocatorInfo allocator, char const* str) { | ||||||
| 	{ |  | ||||||
| 	ssize length = str ? str_len(str) : 0; | 	ssize length = str ? str_len(str) : 0; | ||||||
| 		return make_length( allocator, str, length ); | 	return string_make_length(allocator, str, length); | ||||||
| } | } | ||||||
|  |  | ||||||
| 	static | inline | ||||||
| 	String make( AllocatorInfo allocator, StrC str ) | String string_make(AllocatorInfo allocator, StrC str) { | ||||||
| 	{ | 	return string_make_length(allocator, str.Ptr, str.Len); | ||||||
| 		return make_length( allocator, str.Ptr, str.Len ); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| 	static | inline | ||||||
| 	String make_reserve( AllocatorInfo allocator, ssize capacity ); | String string_fmt(AllocatorInfo allocator, char* buf, ssize buf_size, char const* fmt, ...) { | ||||||
|  | 	va_list va; | ||||||
|  | 	va_start(va, fmt); | ||||||
|  | 	str_fmt_va(buf, buf_size, fmt, va); | ||||||
|  | 	va_end(va); | ||||||
|  |  | ||||||
| 	static | 	return string_make(allocator, buf); | ||||||
| 	String make_length( AllocatorInfo allocator, char const* str, ssize length ); | } | ||||||
|  |  | ||||||
| 	static | inline | ||||||
| 	String fmt( AllocatorInfo allocator, char* buf, ssize buf_size, char const* fmt, ... ); | String string_fmt_buf(AllocatorInfo allocator, char const* fmt, ...) | ||||||
|  |  | ||||||
| 	static |  | ||||||
| 	String fmt_buf( AllocatorInfo allocator, char const* fmt, ... ); |  | ||||||
|  |  | ||||||
| 	static |  | ||||||
| 	String join( AllocatorInfo allocator, char const** parts, ssize num_parts, char const* glue ) |  | ||||||
| { | { | ||||||
| 		String result = make( allocator, "" ); | 	local_persist thread_local | ||||||
|  | 	char buf[GEN_PRINTF_MAXLEN] = { 0 }; | ||||||
|  |  | ||||||
|  | 	va_list va; | ||||||
|  | 	va_start(va, fmt); | ||||||
|  | 	str_fmt_va(buf, GEN_PRINTF_MAXLEN, fmt, va); | ||||||
|  | 	va_end(va); | ||||||
|  |  | ||||||
|  | 	return string_make(allocator, buf); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline | ||||||
|  | String string_join(AllocatorInfo allocator, char const** parts, ssize num_parts, char const* glue) | ||||||
|  | { | ||||||
|  | 	String result = string_make(allocator, ""); | ||||||
|  |  | ||||||
| 	for (ssize idx = 0; idx < num_parts; ++idx) | 	for (ssize idx = 0; idx < num_parts; ++idx) | ||||||
| 	{ | 	{ | ||||||
| 			result.append( parts[ idx ] ); | 		append(result, parts[idx]); | ||||||
|  |  | ||||||
| 		if (idx < num_parts - 1) | 		if (idx < num_parts - 1) | ||||||
| 				result.append( glue ); | 			append(result, glue); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	static | inline | ||||||
|  | bool append(String& str, char c) { | ||||||
|  | 	return append(str, &c, 1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline | ||||||
|  | bool append(String& str, char const* str_to_append) { | ||||||
|  | 	return append(str, str_to_append, str_len(str_to_append)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline | ||||||
|  | bool append(String& str, char const* str_to_append, ssize append_length) | ||||||
|  | { | ||||||
|  | 	if (sptr(str_to_append) > 0) | ||||||
|  | 	{ | ||||||
|  | 		ssize curr_len = length(str); | ||||||
|  |  | ||||||
|  | 		if (!make_space_for(str, str_to_append, append_length)) | ||||||
|  | 			return false; | ||||||
|  |  | ||||||
|  | 		StringHeader& header = get_header(str); | ||||||
|  |  | ||||||
|  | 		mem_copy(str.Data + curr_len, str_to_append, append_length); | ||||||
|  |  | ||||||
|  | 		str.Data[curr_len + append_length] = '\0'; | ||||||
|  |  | ||||||
|  | 		header.Length = curr_len + append_length; | ||||||
|  | 	} | ||||||
|  | 	return str_to_append != nullptr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline | ||||||
|  | bool append(String& str, StrC str_to_append) { | ||||||
|  | 	return append(str, str_to_append.Ptr, str_to_append.Len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline | ||||||
|  | bool append(String& str, const String other) { | ||||||
|  | 	return append(str, other.Data, length(other)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline | ||||||
| bool are_equal(String lhs, String rhs) | bool are_equal(String lhs, String rhs) | ||||||
| { | { | ||||||
| 		if ( lhs.length() != rhs.length() ) | 	if (length(lhs) != length(rhs)) | ||||||
| 		return false; | 		return false; | ||||||
|  |  | ||||||
| 		for ( ssize idx = 0; idx < lhs.length(); ++idx ) | 	for (ssize idx = 0; idx < length(lhs); ++idx) | ||||||
| 		if (lhs[idx] != rhs[idx]) | 		if (lhs[idx] != rhs[idx]) | ||||||
| 			return false; | 			return false; | ||||||
|  |  | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	static | inline | ||||||
| bool are_equal(String lhs, StrC rhs) | bool are_equal(String lhs, StrC rhs) | ||||||
| { | { | ||||||
| 		if ( lhs.length() != (rhs.Len) ) | 	if (length(lhs) != (rhs.Len)) | ||||||
| 		return false; | 		return false; | ||||||
|  |  | ||||||
| 		for ( ssize idx = 0; idx < lhs.length(); ++idx ) | 	for (ssize idx = 0; idx < length(lhs); ++idx) | ||||||
| 		if (lhs[idx] != rhs[idx]) | 		if (lhs[idx] != rhs[idx]) | ||||||
| 			return false; | 			return false; | ||||||
|  |  | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	bool make_space_for( char const* str, ssize add_len ); | inline | ||||||
|  | ssize avail_space(String const& str) { | ||||||
| 	bool append( char c ) | 	StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader)); | ||||||
| 	{ |  | ||||||
| 		return append( & c, 1 ); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool append( char const* str ) |  | ||||||
| 	{ |  | ||||||
| 		return append( str, str_len( str ) ); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool append( char const* str, ssize length ) |  | ||||||
| 	{ |  | ||||||
| 		if ( sptr(str) > 0 ) |  | ||||||
| 		{ |  | ||||||
| 			ssize curr_len = this->length(); |  | ||||||
|  |  | ||||||
| 			if ( ! make_space_for( str, length ) ) |  | ||||||
| 				return false; |  | ||||||
|  |  | ||||||
| 			Header& header = get_header(); |  | ||||||
|  |  | ||||||
| 			mem_copy( Data + curr_len, str, length ); |  | ||||||
|  |  | ||||||
| 			Data[ curr_len + length ] = '\0'; |  | ||||||
|  |  | ||||||
| 			header.Length = curr_len + length; |  | ||||||
| 		} |  | ||||||
| 		return str != nullptr; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool append( StrC str) |  | ||||||
| 	{ |  | ||||||
| 		return append( str.Ptr, str.Len ); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool append( const String other ) |  | ||||||
| 	{ |  | ||||||
| 		return append( other.Data, other.length() ); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool append_fmt( char const* fmt, ... ); |  | ||||||
|  |  | ||||||
| 	ssize avail_space() const |  | ||||||
| 	{ |  | ||||||
| 		Header const& |  | ||||||
| 		header = * rcast( Header const*, Data - sizeof( Header )); |  | ||||||
|  |  | ||||||
| 	return header.Capacity - header.Length; | 	return header.Capacity - header.Length; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	char& back() | inline | ||||||
| 	{ | char& back(String& str) { | ||||||
| 		return Data[ length() - 1 ]; | 	return str.Data[length(str) - 1]; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	ssize capacity() const | inline | ||||||
|  | bool contains(String const& str, StrC substring) | ||||||
| { | { | ||||||
| 		Header const& | 	StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader)); | ||||||
| 		header = * rcast( Header const*, Data - sizeof( Header )); |  | ||||||
|  |  | ||||||
|  | 	if (substring.Len > header.Length) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	ssize main_len = header.Length; | ||||||
|  | 	ssize sub_len = substring.Len; | ||||||
|  |  | ||||||
|  | 	for (ssize idx = 0; idx <= main_len - sub_len; ++idx) | ||||||
|  | 	{ | ||||||
|  | 		if (str_compare(str.Data + idx, substring.Ptr, sub_len) == 0) | ||||||
|  | 			return true; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline | ||||||
|  | bool contains(String const& str, String const& substring) | ||||||
|  | { | ||||||
|  | 	StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader)); | ||||||
|  |  | ||||||
|  | 	if (length(substring) > header.Length) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	ssize main_len = header.Length; | ||||||
|  | 	ssize sub_len = length(substring); | ||||||
|  |  | ||||||
|  | 	for (ssize idx = 0; idx <= main_len - sub_len; ++idx) | ||||||
|  | 	{ | ||||||
|  | 		if (str_compare(str.Data + idx, substring.Data, sub_len) == 0) | ||||||
|  | 			return true; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline | ||||||
|  | ssize capacity(String const& str) { | ||||||
|  |    StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader)); | ||||||
|    return header.Capacity; |    return header.Capacity; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	void clear() | inline | ||||||
| 	{ | void clear(String& str) { | ||||||
| 		get_header().Length = 0; |    get_header(str).Length = 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	String duplicate( AllocatorInfo allocator ) const | inline | ||||||
| 	{ | String duplicate(String const& str, AllocatorInfo allocator) { | ||||||
| 		return make_length( allocator, Data, length() ); |    return string_make_length(allocator, str.Data, length(str)); | ||||||
| } | } | ||||||
|  |  | ||||||
| 	void free() | inline | ||||||
| 	{ | void free(String& str) { | ||||||
| 		if ( ! Data ) |    if (!str.Data) | ||||||
|    	return; |    	return; | ||||||
|  |  | ||||||
| 		Header& header = get_header(); |    StringHeader& header = get_header(str); | ||||||
|  |    GEN_NS free(header.Allocator, &header); | ||||||
| 		gen::free( header.Allocator, & header ); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| 	Header& get_header() | inline | ||||||
| 	{ | StringHeader& get_header(String& str) { | ||||||
| 		return *(Header*)(Data - sizeof(Header)); |    return *(StringHeader*)(str.Data - sizeof(StringHeader)); | ||||||
| } | } | ||||||
|  |  | ||||||
| 	ssize length() const | inline | ||||||
|  | ssize length(String const& str) | ||||||
| { | { | ||||||
| 		Header const& |    StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader)); | ||||||
| 		header = * rcast( Header const*, Data - sizeof( Header )); |  | ||||||
|  |  | ||||||
|    return header.Length; |    return header.Length; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	b32 starts_with( StrC substring ) const | inline | ||||||
|  | bool make_space_for(String& str, char const* to_append, ssize add_len) | ||||||
| { | { | ||||||
| 		if (substring.Len > length()) | 	ssize available = avail_space(str); | ||||||
|  |  | ||||||
|  | 	if (available >= add_len) { | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		ssize new_len, old_size, new_size; | ||||||
|  | 		void* ptr; | ||||||
|  | 		void* new_ptr; | ||||||
|  |  | ||||||
|  | 		AllocatorInfo allocator = get_header(str).Allocator; | ||||||
|  | 		StringHeader* header = nullptr; | ||||||
|  |  | ||||||
|  | 		new_len = string_grow_formula(length(str) + add_len); | ||||||
|  | 		ptr = &get_header(str); | ||||||
|  | 		old_size = size_of(StringHeader) + length(str) + 1; | ||||||
|  | 		new_size = size_of(StringHeader) + new_len + 1; | ||||||
|  |  | ||||||
|  | 		new_ptr = resize(allocator, ptr, old_size, new_size); | ||||||
|  |  | ||||||
|  | 		if (new_ptr == nullptr) | ||||||
| 			return false; | 			return false; | ||||||
|  |  | ||||||
| 		b32 result = str_compare(Data, substring.Ptr, substring.Len ) == 0; | 		header = rcast(StringHeader*, new_ptr); | ||||||
|  | 		header->Allocator = allocator; | ||||||
|  | 		header->Capacity = new_len; | ||||||
|  |  | ||||||
|  | 		str.Data = rcast(char*, header + 1); | ||||||
|  |  | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline | ||||||
|  | b32 starts_with(String const& str, StrC substring) { | ||||||
|  | 	if (substring.Len > length(str)) | ||||||
|  | 	return false; | ||||||
|  |  | ||||||
|  | 	b32 result = str_compare(str.Data, substring.Ptr, substring.Len) == 0; | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	b32 starts_with( String substring ) const | inline | ||||||
| 	{ | b32 starts_with(String const& str, String substring) { | ||||||
| 		if (substring.length() > length()) | 	if (length(substring) > length(str)) | ||||||
| 		return false; | 		return false; | ||||||
|  |  | ||||||
| 		b32 result = str_compare(Data, substring, substring.length() - 1 ) == 0; | 	b32 result = str_compare(str.Data, substring.Data, length(substring) - 1) == 0; | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	void skip_line() | inline | ||||||
|  | void skip_line(String& str) | ||||||
| { | { | ||||||
| #define current (*scanner) | #define current (*scanner) | ||||||
| 		char* scanner = Data; | 	char* scanner = str.Data; | ||||||
| 		while ( current != '\r' && current != '\n' ) | 	while (current != '\r' && current != '\n') { | ||||||
| 		{ |  | ||||||
|  		++scanner; |  		++scanner; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 		s32 new_length = scanner - Data; | 	s32 new_length = scanner - str.Data; | ||||||
|  |  | ||||||
| 		if ( current == '\r' ) | 	if (current == '\r') { | ||||||
| 		{ |  | ||||||
| 		new_length += 1; | 		new_length += 1; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 		mem_move( Data, scanner, new_length ); | 	mem_move(str.Data, scanner, new_length); | ||||||
|  |  | ||||||
| 		Header* header = & get_header(); | 	StringHeader* header = &get_header(str); | ||||||
| 	header->Length = new_length; | 	header->Length = new_length; | ||||||
| #undef current | #undef current | ||||||
| } | } | ||||||
|  |  | ||||||
| 	void strip_space() | inline | ||||||
|  | void strip_space(String& str) | ||||||
| { | { | ||||||
| 		char* write_pos = Data; |    char* write_pos = str.Data; | ||||||
| 		char* read_pos  = Data; |    char* read_pos = str.Data; | ||||||
|  |  | ||||||
|    while (*read_pos) |    while (*read_pos) | ||||||
|    { |    { | ||||||
| @@ -268,15 +476,16 @@ struct String | |||||||
|    write_pos[0] = '\0';  // Null-terminate the modified string |    write_pos[0] = '\0';  // Null-terminate the modified string | ||||||
|  |  | ||||||
|    // Update the length if needed |    // Update the length if needed | ||||||
| 		get_header().Length = write_pos - Data; |    get_header(str).Length = write_pos - str.Data; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	void trim( char const* cut_set ) | inline | ||||||
|  | void trim(String& str, char const* cut_set) | ||||||
| { | { | ||||||
|    ssize len = 0; |    ssize len = 0; | ||||||
|  |  | ||||||
| 		char* start_pos = Data; |    char* start_pos = str.Data; | ||||||
| 		char* end_pos   = Data + length() - 1; |    char* end_pos = str.Data + length(str) - 1; | ||||||
|  |  | ||||||
|    while (start_pos <= end_pos && char_first_occurence(cut_set, *start_pos)) |    while (start_pos <= end_pos && char_first_occurence(cut_set, *start_pos)) | ||||||
|    	start_pos++; |    	start_pos++; | ||||||
| @@ -286,121 +495,55 @@ struct String | |||||||
|  |  | ||||||
|    len = scast(ssize, (start_pos > end_pos) ? 0 : ((end_pos - start_pos) + 1)); |    len = scast(ssize, (start_pos > end_pos) ? 0 : ((end_pos - start_pos) + 1)); | ||||||
|  |  | ||||||
| 		if ( Data != start_pos ) |    if (str.Data != start_pos) | ||||||
| 			mem_move( Data, start_pos, len ); |    	mem_move(str.Data, start_pos, len); | ||||||
|  |  | ||||||
| 		Data[ len ] = '\0'; |    str.Data[len] = '\0'; | ||||||
|  |  | ||||||
| 		get_header().Length = len; |    get_header(str).Length = len; | ||||||
| } | } | ||||||
|  |  | ||||||
| 	void trim_space() | inline | ||||||
| 	{ | void trim_space(String& str) { | ||||||
| 		return trim( " \t\r\n\v\f" ); |    trim(str, " \t\r\n\v\f"); | ||||||
| } | } | ||||||
|  |  | ||||||
| 	// Debug function that provides a copy of the string with whitespace characters visualized. | inline | ||||||
| 	String visualize_whitespace() const | String visualize_whitespace(String const& str) | ||||||
| { | { | ||||||
| 		Header* header = (Header*)(Data - sizeof(Header)); | 	StringHeader* header = (StringHeader*)(str.Data - sizeof(StringHeader)); | ||||||
|  | 	String        result = string_make_reserve(header->Allocator, length(str) * 2); // Assume worst case for space requirements. | ||||||
|  |  | ||||||
| 		String result = make_reserve(header->Allocator, length() * 2); // Assume worst case for space requirements. | 	for (char c : str) switch (c) | ||||||
|  |  | ||||||
| 		for ( char c : *this ) |  | ||||||
| 		{ |  | ||||||
| 			switch ( c ) |  | ||||||
| 	{ | 	{ | ||||||
| 		case ' ': | 		case ' ': | ||||||
| 					result.append( txt("·") ); | 			append(result, txt("·")); | ||||||
| 		break; | 		break; | ||||||
| 		case '\t': | 		case '\t': | ||||||
| 					result.append( txt("→") ); | 			append(result, txt("→")); | ||||||
| 		break; | 		break; | ||||||
| 		case '\n': | 		case '\n': | ||||||
| 					result.append( txt("↵") ); | 			append(result, txt("↵")); | ||||||
| 		break; | 		break; | ||||||
| 		case '\r': | 		case '\r': | ||||||
| 					result.append( txt("⏎") ); | 			append(result, txt("⏎")); | ||||||
| 		break; | 		break; | ||||||
| 		case '\v': | 		case '\v': | ||||||
| 					result.append( txt("⇕") ); | 			append(result, txt("⇕")); | ||||||
| 		break; | 		break; | ||||||
| 		case '\f': | 		case '\f': | ||||||
| 					result.append( txt("⌂") ); | 			append(result, txt("⌂")); | ||||||
| 		break; | 		break; | ||||||
| 		default: | 		default: | ||||||
| 					result.append(c); | 			append(result, c); | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  | #pragma endregion String | ||||||
|  |  | ||||||
| 	// For-range support | struct String_POD { | ||||||
|  |  | ||||||
| 	char* begin() const |  | ||||||
| 	{ |  | ||||||
| 		return Data; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	char* end() const |  | ||||||
| 	{ |  | ||||||
| 		Header const& |  | ||||||
| 		header = * rcast( Header const*, Data - sizeof( Header )); |  | ||||||
|  |  | ||||||
| 		return Data + header.Length; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	operator bool() |  | ||||||
| 	{ |  | ||||||
| 		return Data != nullptr; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	operator char* () |  | ||||||
| 	{ |  | ||||||
| 		return Data; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	operator char const* () const |  | ||||||
| 	{ |  | ||||||
| 		return Data; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	operator StrC() const |  | ||||||
| 	{ |  | ||||||
| 		return { length(), Data }; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Used with cached strings |  | ||||||
| 	// Essentially makes the string a string view. |  | ||||||
| 	String const& operator = ( String const& other ) const |  | ||||||
| 	{ |  | ||||||
| 		if ( this == & other ) |  | ||||||
| 			return *this; |  | ||||||
|  |  | ||||||
| 		String* |  | ||||||
| 		this_ = ccast(String*, this); |  | ||||||
| 		this_->Data = other.Data; |  | ||||||
|  |  | ||||||
| 		return *this; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	char& operator [] ( ssize index ) |  | ||||||
| 	{ |  | ||||||
| 		return Data[ index ]; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	char const& operator [] ( ssize index ) const |  | ||||||
| 	{ |  | ||||||
| 		return Data[ index ]; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	char* Data; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct String_POD |  | ||||||
| { |  | ||||||
| 	char* Data; | 	char* Data; | ||||||
| }; | }; | ||||||
| static_assert( sizeof( String_POD ) == sizeof( String ), "String is not a POD" ); | static_assert( sizeof( String_POD ) == sizeof( String ), "String is not a POD" ); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user