mirror of
				https://github.com/Ed94/gencpp.git
				synced 2025-10-30 22:40:54 -07:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			163ad0a511
			...
			056a5863b8
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 056a5863b8 | |||
| 79eb5f1f76 | |||
| c6cb583518 | |||
| 34eec66f35 | |||
| 4137ebfbd8 | |||
| 5958dd2055 | 
							
								
								
									
										3
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							| @@ -37,7 +37,8 @@ | ||||
| 		"propidl.h": "c", | ||||
| 		"android_native_app_glue.h": "c", | ||||
| 		"raylib.h": "c", | ||||
| 		"*.m": "cpp" | ||||
| 		"*.m": "cpp", | ||||
| 		"atomic": "cpp" | ||||
| 	}, | ||||
| 	"C_Cpp.intelliSenseEngineFallback": "disabled", | ||||
| 	"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(); | ||||
|  | ||||
| 	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 ) | ||||
| 	{ | ||||
|   | ||||
| @@ -16,8 +16,8 @@ ssize token_fmt_va( char* buf, usize buf_size, s32 num_tokens, va_list va ) | ||||
| 		local_persist | ||||
| 		char tok_map_mem[ TokenFmt_TokenMap_MemSize ]; | ||||
|  | ||||
| 		init_from_memory( tok_map_arena, tok_map_mem, sizeof(tok_map_mem) ); | ||||
| 		tok_map = HashTable<StrC>::init( tok_map_arena ); | ||||
| 		tok_map_arena = init_from_memory( tok_map_mem, sizeof(tok_map_mem) ); | ||||
| 		tok_map       = HashTable<StrC>::init( tok_map_arena ); | ||||
|  | ||||
| 		s32 left = num_tokens - 1; | ||||
|  | ||||
|   | ||||
| @@ -133,7 +133,7 @@ internal | ||||
| void init() | ||||
| { | ||||
| 	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(); | ||||
| @@ -713,7 +713,7 @@ Code parse_class_struct( TokType which, bool inplace_def = false ) | ||||
| 	local_persist | ||||
| 	char interface_arr_mem[ kilobytes(4) ] {0}; | ||||
| 	Array<CodeType> interfaces; { | ||||
| 		Arena arena; init_from_memory(arena, interface_arr_mem, kilobytes(4) ); | ||||
| 		Arena arena = init_from_memory( interface_arr_mem, kilobytes(4) ); | ||||
| 		Array<CodeType>::init_reserve( arena, 4 ); | ||||
| 	}  | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| #ifdef GEN_INTELLISENSE_DIRECTIVES | ||||
| #	pragma once | ||||
| #	include "platform.hpp" | ||||
| #	include "macros.hpp" | ||||
| #endif | ||||
|  | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -334,7 +334,7 @@ ssize virtual_memory_page_size( ssize* alignment_out ) | ||||
|  | ||||
| #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); | ||||
| 	void*      ptr   = NULL; | ||||
| @@ -384,7 +384,7 @@ void* Arena::allocator_proc( void* allocator_data, AllocType type, ssize size, s | ||||
| 	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); | ||||
| 	void* ptr  = NULL; | ||||
| @@ -457,7 +457,7 @@ void* Pool::allocator_proc( void* allocator_data, AllocType type, ssize size, ss | ||||
| 	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 = {}; | ||||
|  | ||||
| @@ -495,16 +495,16 @@ Pool Pool::init_align( AllocatorInfo backing, ssize num_blocks, ssize block_size | ||||
| 	return pool; | ||||
| } | ||||
|  | ||||
| void Pool::clear() | ||||
| void clear(Pool& pool) | ||||
| { | ||||
| 	ssize    actual_block_size, block_index; | ||||
| 	ssize actual_block_size, block_index; | ||||
| 	void* curr; | ||||
| 	uptr* end; | ||||
|  | ||||
| 	actual_block_size = BlockSize + BlockAlign; | ||||
| 	actual_block_size = pool.BlockSize + pool.BlockAlign; | ||||
|  | ||||
| 	curr = PhysicalStart; | ||||
| 	for ( block_index = 0; block_index < NumBlocks - 1; block_index++ ) | ||||
| 	curr = pool.PhysicalStart; | ||||
| 	for ( block_index = 0; block_index < pool.NumBlocks - 1; block_index++ ) | ||||
| 	{ | ||||
| 		uptr* next = ( uptr* ) curr; | ||||
| 		*next      = ( uptr  ) curr + actual_block_size; | ||||
| @@ -514,7 +514,7 @@ void Pool::clear() | ||||
| 	end  =  ( uptr* ) curr; | ||||
| 	*end =  ( uptr )  NULL; | ||||
|  | ||||
| 	FreeList = PhysicalStart; | ||||
| 	pool.FreeList = pool.PhysicalStart; | ||||
| } | ||||
|  | ||||
| #pragma endregion Memory | ||||
|   | ||||
| @@ -170,127 +170,180 @@ b32 gen_vm_purge( VirtualMemory vm ); | ||||
| //! Retrieve VM's page size and alignment. | ||||
| ssize gen_virtual_memory_page_size( ssize* alignment_out ); | ||||
|  | ||||
|  | ||||
| #pragma region Arena | ||||
| struct Arena; | ||||
| void init_from_memory( Arena& arena, void* start, ssize size ); | ||||
|  | ||||
| struct Arena | ||||
| { | ||||
| 	static | ||||
| 	void* allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ); | ||||
| AllocatorInfo allocator_info( Arena& arena ); | ||||
|  | ||||
| 	//forceinline static | ||||
| 	//Arena init_from_memory( void* start, ssize size ) {  | ||||
| 	//	Arena result; GEN_NS init_from_memory( result, start, size );  | ||||
| 	//	return result; | ||||
| 	//} | ||||
| // 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); | ||||
|  | ||||
| 	static | ||||
| 	Arena init_from_allocator( AllocatorInfo backing, ssize size ) | ||||
| 	{ | ||||
| 		Arena result = | ||||
| 		{ | ||||
| 		backing, | ||||
| 		alloc( backing, size), | ||||
| 		size, | ||||
| 		0, | ||||
| 		0 | ||||
| 		}; | ||||
| 		return result; | ||||
| 	} | ||||
|  | ||||
| 	static | ||||
| 	Arena init_sub( Arena& parent, ssize size ) | ||||
| 	{ | ||||
| 		return init_from_allocator( parent.Backing, size ); | ||||
| 	} | ||||
|  | ||||
| 	ssize alignment_of( ssize alignment ) | ||||
| 	{ | ||||
| 		ssize alignment_offset, result_pointer, mask; | ||||
| 		GEN_ASSERT( is_power_of_two( alignment ) ); | ||||
|  | ||||
| 		alignment_offset = 0; | ||||
| 		result_pointer   = (ssize) PhysicalStart + TotalUsed; | ||||
| 		mask             = alignment - 1; | ||||
|  | ||||
| 		if ( result_pointer & mask ) | ||||
| 			alignment_offset = alignment - ( result_pointer & mask ); | ||||
|  | ||||
| 		return alignment_offset; | ||||
| 	} | ||||
| // 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() | ||||
| 	{ | ||||
| 		GEN_ASSERT( TempCount == 0 ); | ||||
| 	} | ||||
| void   check(Arena& arena); | ||||
| #pragma pop_macro("check") | ||||
|  | ||||
| 	void free() | ||||
| 	{ | ||||
| 		if ( Backing.Proc ) | ||||
| 		{ | ||||
| 			gen::free( Backing, PhysicalStart ); | ||||
| 			PhysicalStart = nullptr; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	ssize size_remaining( ssize alignment ) | ||||
| 	{ | ||||
| 		ssize result = TotalSize - ( TotalUsed + alignment_of( alignment ) ); | ||||
| 		return result; | ||||
| 	} | ||||
| void  free(Arena& arena); | ||||
| ssize size_remaining(Arena& arena, ssize alignment); | ||||
|  | ||||
| struct Arena | ||||
| { | ||||
| 	AllocatorInfo Backing; | ||||
| 	void*         PhysicalStart; | ||||
| 	ssize         TotalSize; | ||||
| 	ssize         TotalUsed; | ||||
| 	ssize         TempCount; | ||||
|  | ||||
| 	operator AllocatorInfo() { return { allocator_proc, this }; } | ||||
| #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 | ||||
| }; | ||||
|  | ||||
| void init_from_memory( Arena& arena, void* start, ssize size ) | ||||
| inline | ||||
| AllocatorInfo allocator_info( Arena& arena ) { | ||||
| 	return { arena_allocator_proc, &arena }; | ||||
| } | ||||
|  | ||||
| inline | ||||
| Arena init_from_memory( void* start, ssize size ) | ||||
| { | ||||
| 	arena = | ||||
| 	{ | ||||
| 	Arena arena = { | ||||
| 		{ nullptr, nullptr }, | ||||
| 		start, | ||||
| 		size, | ||||
| 		0, | ||||
| 		0 | ||||
| 	}; | ||||
| 	return arena; | ||||
| } | ||||
|  | ||||
| inline | ||||
| Arena init_from_allocator(AllocatorInfo backing, ssize size) | ||||
| { | ||||
|     Arena result = | ||||
|     { | ||||
|         backing, | ||||
|         alloc(backing, size), | ||||
|         size, | ||||
|         0, | ||||
|         0 | ||||
|     }; | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| inline | ||||
| Arena init_sub(Arena& parent, ssize size) | ||||
| { | ||||
|     return init_from_allocator(parent.Backing, size); | ||||
| } | ||||
|  | ||||
| inline | ||||
| ssize alignment_of(Arena& arena, ssize alignment) | ||||
| { | ||||
|     ssize alignment_offset, result_pointer, mask; | ||||
|     GEN_ASSERT(is_power_of_two(alignment)); | ||||
|  | ||||
|     alignment_offset = 0; | ||||
|     result_pointer  = (ssize)arena.PhysicalStart + arena.TotalUsed; | ||||
|     mask            = alignment - 1; | ||||
|  | ||||
|     if (result_pointer & mask) | ||||
|         alignment_offset = alignment - (result_pointer & mask); | ||||
|  | ||||
|     return alignment_offset; | ||||
| } | ||||
|  | ||||
| #pragma push_macro("check") | ||||
| #undef check | ||||
| inline | ||||
| void check(Arena& arena) | ||||
| { | ||||
|     GEN_ASSERT(arena.TempCount == 0); | ||||
| } | ||||
| #pragma pop_macro("check") | ||||
|  | ||||
| inline | ||||
| void free(Arena& arena) | ||||
| { | ||||
|     if (arena.Backing.Proc) | ||||
|     { | ||||
|         gen::free(arena.Backing, arena.PhysicalStart); | ||||
|         arena.PhysicalStart = nullptr; | ||||
|     } | ||||
| } | ||||
|  | ||||
| inline | ||||
| ssize size_remaining(Arena& arena, ssize alignment) | ||||
| { | ||||
|     ssize result = arena.TotalSize - (arena.TotalUsed + alignment_of(arena, alignment)); | ||||
|     return result; | ||||
| } | ||||
| #pragma endregion Arena | ||||
|  | ||||
| #pragma region FixedArena | ||||
| template<s32 Size> | ||||
| struct FixedArena; | ||||
|  | ||||
| template<s32 Size> AllocatorInfo    allocator_info( FixedArena<Size>& fixed_arena ); | ||||
| template<s32 Size> FixedArena<Size> fixed_arena_init(); | ||||
| 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. | ||||
| // Used for static segment or stack allocations. | ||||
| template< s32 Size > | ||||
| struct FixedArena | ||||
| { | ||||
| 	static | ||||
| 	FixedArena init() | ||||
| 	{ | ||||
| 		FixedArena result = { Arena::init_from_memory( result.memory, Size ), {0} }; | ||||
| 		return result; | ||||
| 	} | ||||
|     char  memory[Size]; | ||||
|     Arena arena; | ||||
|  | ||||
| 	ssize size_remaining( ssize alignment ) | ||||
| 	{ | ||||
| 		return arena.size_remaining( alignment ); | ||||
| 	} | ||||
| #if 1 | ||||
| #pragma region Member Mapping | ||||
|     forceinline operator AllocatorInfo() { return GEN_NS allocator_info(* this); } | ||||
|  | ||||
| 	operator AllocatorInfo() | ||||
| 	{ | ||||
| 		return { Arena::allocator_proc, &arena }; | ||||
| 	} | ||||
|  | ||||
| 	Arena arena; | ||||
| 	char  memory[ Size ]; | ||||
|     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_4KB   = FixedArena< kilobytes( 4 ) >; | ||||
| using Arena_8KB   = FixedArena< kilobytes( 8 ) >; | ||||
| @@ -303,31 +356,20 @@ using Arena_512KB = FixedArena< kilobytes( 512 ) >; | ||||
| using Arena_1MB   = FixedArena< megabytes( 1 ) >; | ||||
| using Arena_2MB   = FixedArena< megabytes( 2 ) >; | ||||
| 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 | ||||
| { | ||||
| 	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; | ||||
| 	void*         PhysicalStart; | ||||
| 	void*         FreeList; | ||||
| @@ -336,12 +378,34 @@ struct Pool | ||||
| 	ssize         TotalSize; | ||||
| 	ssize         NumBlocks; | ||||
|  | ||||
| 	operator AllocatorInfo() | ||||
| 	{ | ||||
| 		return { allocator_proc, this }; | ||||
| 	} | ||||
| #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 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 | ||||
| b32 is_power_of_two( ssize x ) { | ||||
|   | ||||
| @@ -4,20 +4,9 @@ | ||||
| #endif | ||||
|  | ||||
| #pragma region String | ||||
|  | ||||
| String String::fmt( AllocatorInfo allocator, char* buf, ssize buf_size, char const* fmt, ... ) | ||||
| String string_make_length( AllocatorInfo allocator, char const* str, ssize length ) | ||||
| { | ||||
| 	va_list va; | ||||
| 	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 ); | ||||
| 	constexpr ssize header_size = sizeof( StringHeader ); | ||||
|  | ||||
| 	s32   alloc_size = header_size + length + 1; | ||||
| 	void* allocation = alloc( allocator, alloc_size ); | ||||
| @@ -25,8 +14,8 @@ String String::make_length( AllocatorInfo allocator, char const* str, ssize leng | ||||
| 	if ( allocation == nullptr ) | ||||
| 		return { nullptr }; | ||||
|  | ||||
| 	Header& | ||||
| 	header = * rcast(Header*, allocation); | ||||
| 	StringHeader& | ||||
| 	header = * rcast(StringHeader*, allocation); | ||||
| 	header = { allocator, length, length }; | ||||
|  | ||||
| 	String  result = { rcast( char*, allocation) + header_size }; | ||||
| @@ -41,9 +30,9 @@ String String::make_length( AllocatorInfo allocator, char const* str, ssize leng | ||||
| 	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; | ||||
| 	void* allocation = alloc( allocator, alloc_size ); | ||||
| @@ -53,8 +42,8 @@ String String::make_reserve( AllocatorInfo allocator, ssize capacity ) | ||||
|  | ||||
| 	mem_set( allocation, 0, alloc_size ); | ||||
|  | ||||
| 	Header* | ||||
| 		header            = rcast(Header*, allocation); | ||||
| 	StringHeader* | ||||
| 	header            = rcast(StringHeader*, allocation); | ||||
| 	header->Allocator = allocator; | ||||
| 	header->Capacity  = capacity; | ||||
| 	header->Length    = 0; | ||||
| @@ -62,69 +51,4 @@ String String::make_reserve( AllocatorInfo allocator, ssize capacity ) | ||||
| 	String result = { rcast(char*, allocation) + header_size }; | ||||
| 	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 | ||||
|   | ||||
| @@ -19,8 +19,7 @@ struct StrC | ||||
| #define txt( text )         StrC { sizeof( text ) - 1, ( text ) } | ||||
|  | ||||
| inline | ||||
| StrC to_str( char const* str ) | ||||
| { | ||||
| StrC to_str( char const* str ) { | ||||
| 	return { str_len( str ), str }; | ||||
| } | ||||
|  | ||||
| @@ -28,417 +27,523 @@ StrC to_str( char const* str ) | ||||
| // This is directly based off the ZPL string api. | ||||
| // They used a header pattern | ||||
| // I kept it for simplicty of porting but its not necessary to keep it that way. | ||||
| #pragma region String | ||||
| struct String; | ||||
| 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; | ||||
| 	ssize         Capacity; | ||||
| 	ssize         Length; | ||||
| }; | ||||
|  | ||||
| struct String | ||||
| { | ||||
| 	struct Header | ||||
| 	{ | ||||
| 		AllocatorInfo Allocator; | ||||
| 		ssize            Capacity; | ||||
| 		ssize            Length; | ||||
| 	}; | ||||
| 	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 | ||||
| 	usize grow_formula( usize value ) | ||||
| 	{ | ||||
| 		// Using a very aggressive growth formula to reduce time mem_copying with recursive calls to append in this library. | ||||
| 		return 4 * value + 8; | ||||
| 	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 make( AllocatorInfo allocator, char const* str ) | ||||
| 	{ | ||||
| 		ssize length = str ? str_len( str ) : 0; | ||||
| 		return make_length( allocator, str, length ); | ||||
| 	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); | ||||
| 	} | ||||
|  | ||||
| 	static | ||||
| 	String make( AllocatorInfo allocator, StrC str ) | ||||
| 	{ | ||||
| 		return make_length( allocator, str.Ptr, str.Len ); | ||||
| 	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); | ||||
| 	} | ||||
|  | ||||
| 	static | ||||
| 	String make_reserve( AllocatorInfo allocator, ssize capacity ); | ||||
| 	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 }; } | ||||
|  | ||||
| 	static | ||||
| 	String make_length( AllocatorInfo allocator, char const* str, ssize length ); | ||||
|  | ||||
| 	static | ||||
| 	String fmt( AllocatorInfo allocator, char* buf, ssize buf_size, 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, "" ); | ||||
|  | ||||
| 		for ( ssize idx = 0; idx < num_parts; ++idx ) | ||||
| 		{ | ||||
| 			result.append( parts[ idx ] ); | ||||
|  | ||||
| 			if ( idx < num_parts - 1 ) | ||||
| 				result.append( glue ); | ||||
| 		} | ||||
|  | ||||
| 		return result; | ||||
| 	} | ||||
|  | ||||
| 	static | ||||
| 	bool are_equal( String lhs, String rhs ) | ||||
| 	{ | ||||
| 		if ( lhs.length() != rhs.length() ) | ||||
| 			return false; | ||||
|  | ||||
| 		for ( ssize idx = 0; idx < lhs.length(); ++idx ) | ||||
| 			if ( lhs[ idx ] != rhs[ idx ] ) | ||||
| 				return false; | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	static | ||||
| 	bool are_equal( String lhs, StrC rhs ) | ||||
| 	{ | ||||
| 		if ( lhs.length() != (rhs.Len) ) | ||||
| 			return false; | ||||
|  | ||||
| 		for ( ssize idx = 0; idx < lhs.length(); ++idx ) | ||||
| 			if ( lhs[idx] != rhs[idx] ) | ||||
| 				return false; | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	bool make_space_for( char const* str, ssize add_len ); | ||||
|  | ||||
| 	bool append( char c ) | ||||
| 	{ | ||||
| 		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; | ||||
| 	} | ||||
|  | ||||
| 	char& back() | ||||
| 	{ | ||||
| 		return Data[ length() - 1 ]; | ||||
| 	} | ||||
|  | ||||
| 	bool contains(StrC substring) const | ||||
| 	{ | ||||
| 		Header const& 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(Data + idx, substring.Ptr, sub_len) == 0) | ||||
| 				return true; | ||||
| 		} | ||||
|  | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	bool contains(String const& substring) const | ||||
| 	{ | ||||
| 		Header const& header = * rcast( Header const*, Data - sizeof( Header )); | ||||
|  | ||||
| 		if (substring.length() > header.Length) | ||||
| 			return false; | ||||
|  | ||||
| 		ssize main_len = header.Length; | ||||
| 		ssize sub_len  = substring.length(); | ||||
|  | ||||
| 		for (ssize idx = 0; idx <= main_len - sub_len; ++idx) | ||||
| 		{ | ||||
| 			if (str_compare(Data + idx, substring.Data, sub_len) == 0) | ||||
| 				return true; | ||||
| 		} | ||||
|  | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	ssize capacity() const | ||||
| 	{ | ||||
| 		Header const& | ||||
| 		header = * rcast( Header const*, Data - sizeof( Header )); | ||||
|  | ||||
| 		return header.Capacity; | ||||
| 	} | ||||
|  | ||||
| 	void clear() | ||||
| 	{ | ||||
| 		get_header().Length = 0; | ||||
| 	} | ||||
|  | ||||
| 	String duplicate( AllocatorInfo allocator ) const | ||||
| 	{ | ||||
| 		return make_length( allocator, Data, length() ); | ||||
| 	} | ||||
|  | ||||
| 	void free() | ||||
| 	{ | ||||
| 		if ( ! Data ) | ||||
| 			return; | ||||
|  | ||||
| 		Header& header = get_header(); | ||||
|  | ||||
| 		gen::free( header.Allocator, & header ); | ||||
| 	} | ||||
|  | ||||
| 	Header& get_header() | ||||
| 	{ | ||||
| 		return *(Header*)(Data - sizeof(Header)); | ||||
| 	} | ||||
|  | ||||
| 	ssize length() const | ||||
| 	{ | ||||
| 		Header const& | ||||
| 		header = * rcast( Header const*, Data - sizeof( Header )); | ||||
|  | ||||
| 		return header.Length; | ||||
| 	} | ||||
|  | ||||
| 	b32 starts_with( StrC substring ) const | ||||
| 	{ | ||||
| 		if (substring.Len > length()) | ||||
| 			return false; | ||||
|  | ||||
| 		b32 result = str_compare(Data, substring.Ptr, substring.Len ) == 0; | ||||
| 		return result; | ||||
| 	} | ||||
|  | ||||
| 	b32 starts_with( String substring ) const | ||||
| 	{ | ||||
| 		if (substring.length() > length()) | ||||
| 			return false; | ||||
|  | ||||
| 		b32 result = str_compare(Data, substring, substring.length() - 1 ) == 0; | ||||
| 		return result; | ||||
| 	} | ||||
|  | ||||
| 	void skip_line() | ||||
| 	{ | ||||
| 	#define current (*scanner) | ||||
| 		char* scanner = Data; | ||||
| 		while ( current != '\r' && current != '\n' ) | ||||
| 		{ | ||||
| 			++ scanner; | ||||
| 		} | ||||
|  | ||||
| 		s32 new_length = scanner - Data; | ||||
|  | ||||
| 		if ( current == '\r' ) | ||||
| 		{ | ||||
| 			new_length += 1; | ||||
| 		} | ||||
|  | ||||
| 		mem_move( Data, scanner, new_length ); | ||||
|  | ||||
| 		Header* header = & get_header(); | ||||
| 		header->Length = new_length; | ||||
| 	#undef current | ||||
| 	} | ||||
|  | ||||
| 	void strip_space() | ||||
| 	{ | ||||
| 		char* write_pos = Data; | ||||
| 		char* read_pos  = Data; | ||||
|  | ||||
| 		while ( * read_pos) | ||||
| 		{ | ||||
| 			if ( ! char_is_space( *read_pos )) | ||||
| 			{ | ||||
| 				*write_pos = *read_pos; | ||||
| 				write_pos++; | ||||
| 			} | ||||
| 			read_pos++; | ||||
| 		} | ||||
|  | ||||
| 		write_pos[0] = '\0';  // Null-terminate the modified string | ||||
|  | ||||
| 		// Update the length if needed | ||||
| 		get_header().Length = write_pos - Data; | ||||
| 	} | ||||
|  | ||||
| 	void trim( char const* cut_set ) | ||||
| 	{ | ||||
| 		ssize len = 0; | ||||
|  | ||||
| 		char* start_pos = Data; | ||||
| 		char* end_pos   = Data + length() - 1; | ||||
|  | ||||
| 		while ( start_pos <= end_pos && char_first_occurence( cut_set, *start_pos ) ) | ||||
| 			start_pos++; | ||||
|  | ||||
| 		while ( end_pos > start_pos && char_first_occurence( cut_set, *end_pos ) ) | ||||
| 			end_pos--; | ||||
|  | ||||
| 		len = scast( ssize, ( start_pos > end_pos ) ? 0 : ( ( end_pos - start_pos ) + 1 ) ); | ||||
|  | ||||
| 		if ( Data != start_pos ) | ||||
| 			mem_move( Data, start_pos, len ); | ||||
|  | ||||
| 		Data[ len ] = '\0'; | ||||
|  | ||||
| 		get_header().Length = len; | ||||
| 	} | ||||
|  | ||||
| 	void trim_space() | ||||
| 	{ | ||||
| 		return trim( " \t\r\n\v\f" ); | ||||
| 	} | ||||
|  | ||||
| 	// Debug function that provides a copy of the string with whitespace characters visualized. | ||||
| 	String visualize_whitespace() const | ||||
| 	{ | ||||
| 		Header* header = (Header*)(Data - sizeof(Header)); | ||||
|  | ||||
| 		String result = make_reserve(header->Allocator, length() * 2); // Assume worst case for space requirements. | ||||
|  | ||||
| 		for ( char c : *this ) | ||||
| 		{ | ||||
| 			switch ( c ) | ||||
| 			{ | ||||
| 				case ' ': | ||||
| 					result.append( txt("·") ); | ||||
| 				break; | ||||
| 				case '\t': | ||||
| 					result.append( txt("→") ); | ||||
| 				break; | ||||
| 				case '\n': | ||||
| 					result.append( txt("↵") ); | ||||
| 				break; | ||||
| 				case '\r': | ||||
| 					result.append( txt("⏎") ); | ||||
| 				break; | ||||
| 				case '\v': | ||||
| 					result.append( txt("⇕") ); | ||||
| 				break; | ||||
| 				case '\f': | ||||
| 					result.append( txt("⌂") ); | ||||
| 				break; | ||||
| 				default: | ||||
| 					result.append(c); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return result; | ||||
| 	} | ||||
|  | ||||
| 	// For-range support | ||||
|  | ||||
| 	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 ) | ||||
| 	String const& operator=(String const& other) const { | ||||
| 		if (this == &other) | ||||
| 			return *this; | ||||
|  | ||||
| 		String* | ||||
| 		this_ = ccast(String*, this); | ||||
| 		String* this_ = ccast(String*, this); | ||||
| 		this_->Data = other.Data; | ||||
|  | ||||
| 		return *this; | ||||
| 	} | ||||
|  | ||||
| 	char& operator [] ( ssize index ) | ||||
| 	{ | ||||
| 		return Data[ index ]; | ||||
| 	} | ||||
| 	forceinline char& operator[](ssize index)             { return Data[index]; } | ||||
| 	forceinline char const& operator[](ssize index) const { return Data[index]; } | ||||
|  | ||||
| 	char const& operator [] ( ssize index ) const | ||||
| 	{ | ||||
| 		return Data[ index ]; | ||||
| 	} | ||||
|  | ||||
| 	char* Data; | ||||
| 	forceinline char* begin() const { return Data; } | ||||
| 	forceinline char* end() const   { return Data + length(); } | ||||
| #pragma endregion Member Mapping | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| struct String_POD | ||||
| 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. | ||||
| 	return 4 * value + 8; | ||||
| } | ||||
|  | ||||
| inline | ||||
| String string_make(AllocatorInfo allocator, char const* str) { | ||||
| 	ssize length = str ? str_len(str) : 0; | ||||
| 	return string_make_length(allocator, str, length); | ||||
| } | ||||
|  | ||||
| inline | ||||
| String string_make(AllocatorInfo allocator, StrC str) { | ||||
| 	return string_make_length(allocator, str.Ptr, str.Len); | ||||
| } | ||||
|  | ||||
| inline | ||||
| 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); | ||||
|  | ||||
| 	return string_make(allocator, buf); | ||||
| } | ||||
|  | ||||
| inline | ||||
| 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 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) | ||||
| 	{ | ||||
| 		append(result, parts[idx]); | ||||
|  | ||||
| 		if (idx < num_parts - 1) | ||||
| 			append(result, glue); | ||||
| 	} | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| 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) | ||||
| { | ||||
| 	if (length(lhs) != length(rhs)) | ||||
| 		return false; | ||||
|  | ||||
| 	for (ssize idx = 0; idx < length(lhs); ++idx) | ||||
| 		if (lhs[idx] != rhs[idx]) | ||||
| 			return false; | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| inline | ||||
| bool are_equal(String lhs, StrC rhs) | ||||
| { | ||||
| 	if (length(lhs) != (rhs.Len)) | ||||
| 		return false; | ||||
|  | ||||
| 	for (ssize idx = 0; idx < length(lhs); ++idx) | ||||
| 		if (lhs[idx] != rhs[idx]) | ||||
| 			return false; | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| inline | ||||
| ssize avail_space(String const& str) { | ||||
| 	StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader)); | ||||
| 	return header.Capacity - header.Length; | ||||
| } | ||||
|  | ||||
| inline | ||||
| char& back(String& str) { | ||||
| 	return str.Data[length(str) - 1]; | ||||
| } | ||||
|  | ||||
| inline | ||||
| bool contains(String const& str, StrC substring) | ||||
| { | ||||
| 	StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader)); | ||||
|  | ||||
| 	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; | ||||
| } | ||||
|  | ||||
| inline | ||||
| void clear(String& str) { | ||||
|    get_header(str).Length = 0; | ||||
| } | ||||
|  | ||||
| inline | ||||
| String duplicate(String const& str, AllocatorInfo allocator) { | ||||
|    return string_make_length(allocator, str.Data, length(str)); | ||||
| } | ||||
|  | ||||
| inline | ||||
| void free(String& str) { | ||||
|    if (!str.Data) | ||||
|    	return; | ||||
|  | ||||
|    StringHeader& header = get_header(str); | ||||
|    GEN_NS free(header.Allocator, &header); | ||||
| } | ||||
|  | ||||
| inline | ||||
| StringHeader& get_header(String& str) { | ||||
|    return *(StringHeader*)(str.Data - sizeof(StringHeader)); | ||||
| } | ||||
|  | ||||
| inline | ||||
| ssize length(String const& str) | ||||
| { | ||||
|    StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader)); | ||||
|    return header.Length; | ||||
| } | ||||
|  | ||||
| inline | ||||
| bool make_space_for(String& str, char const* to_append, ssize add_len) | ||||
| { | ||||
| 	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; | ||||
|  | ||||
| 		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; | ||||
| } | ||||
|  | ||||
| inline | ||||
| b32 starts_with(String const& str, String substring) { | ||||
| 	if (length(substring) > length(str)) | ||||
| 		return false; | ||||
|  | ||||
| 	b32 result = str_compare(str.Data, substring.Data, length(substring) - 1) == 0; | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| inline | ||||
| void skip_line(String& str) | ||||
| { | ||||
| #define current (*scanner) | ||||
| 	char* scanner = str.Data; | ||||
| 	while (current != '\r' && current != '\n') { | ||||
|  		++scanner; | ||||
| 	} | ||||
|  | ||||
| 	s32 new_length = scanner - str.Data; | ||||
|  | ||||
| 	if (current == '\r') { | ||||
| 		new_length += 1; | ||||
| 	} | ||||
|  | ||||
| 	mem_move(str.Data, scanner, new_length); | ||||
|  | ||||
| 	StringHeader* header = &get_header(str); | ||||
| 	header->Length = new_length; | ||||
| #undef current | ||||
| } | ||||
|  | ||||
| inline | ||||
| void strip_space(String& str) | ||||
| { | ||||
|    char* write_pos = str.Data; | ||||
|    char* read_pos = str.Data; | ||||
|  | ||||
|    while (*read_pos) | ||||
|    { | ||||
|    	if (!char_is_space(*read_pos)) | ||||
|    	{ | ||||
|    		*write_pos = *read_pos; | ||||
|    		write_pos++; | ||||
|    	} | ||||
|    	read_pos++; | ||||
|    } | ||||
|  | ||||
|    write_pos[0] = '\0';  // Null-terminate the modified string | ||||
|  | ||||
|    // Update the length if needed | ||||
|    get_header(str).Length = write_pos - str.Data; | ||||
| } | ||||
|  | ||||
| inline | ||||
| void trim(String& str, char const* cut_set) | ||||
| { | ||||
|    ssize len = 0; | ||||
|  | ||||
|    char* start_pos = str.Data; | ||||
|    char* end_pos = str.Data + length(str) - 1; | ||||
|  | ||||
|    while (start_pos <= end_pos && char_first_occurence(cut_set, *start_pos)) | ||||
|    	start_pos++; | ||||
|  | ||||
|    while (end_pos > start_pos && char_first_occurence(cut_set, *end_pos)) | ||||
|    	end_pos--; | ||||
|  | ||||
|    len = scast(ssize, (start_pos > end_pos) ? 0 : ((end_pos - start_pos) + 1)); | ||||
|  | ||||
|    if (str.Data != start_pos) | ||||
|    	mem_move(str.Data, start_pos, len); | ||||
|  | ||||
|    str.Data[len] = '\0'; | ||||
|  | ||||
|    get_header(str).Length = len; | ||||
| } | ||||
|  | ||||
| inline | ||||
| void trim_space(String& str) { | ||||
|    trim(str, " \t\r\n\v\f"); | ||||
| } | ||||
|  | ||||
| inline | ||||
| String visualize_whitespace(String const& str) | ||||
| { | ||||
| 	StringHeader* header = (StringHeader*)(str.Data - sizeof(StringHeader)); | ||||
| 	String        result = string_make_reserve(header->Allocator, length(str) * 2); // Assume worst case for space requirements. | ||||
|  | ||||
| 	for (char c : str) switch (c) | ||||
| 	{ | ||||
| 		case ' ': | ||||
| 			append(result, txt("·")); | ||||
| 		break; | ||||
| 		case '\t': | ||||
| 			append(result, txt("→")); | ||||
| 		break; | ||||
| 		case '\n': | ||||
| 			append(result, txt("↵")); | ||||
| 		break; | ||||
| 		case '\r': | ||||
| 			append(result, txt("⏎")); | ||||
| 		break; | ||||
| 		case '\v': | ||||
| 			append(result, txt("⇕")); | ||||
| 		break; | ||||
| 		case '\f': | ||||
| 			append(result, txt("⌂")); | ||||
| 		break; | ||||
| 		default: | ||||
| 			append(result, c); | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
| #pragma endregion String | ||||
|  | ||||
| struct String_POD { | ||||
| 	char* Data; | ||||
| }; | ||||
| static_assert( sizeof( String_POD ) == sizeof( String ), "String is not a POD" ); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user