mirror of
				https://github.com/Ed94/gencpp.git
				synced 2025-10-30 14:30:53 -07:00 
			
		
		
		
	strings done
This commit is contained in:
		| @@ -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 ) | ||||||
| 	{ | 	{ | ||||||
|   | |||||||
| @@ -376,26 +376,26 @@ struct HashTable | |||||||
| { | { | ||||||
| 	static constexpr f32 CriticalLoadScale = 0.7f; | 	static constexpr f32 CriticalLoadScale = 0.7f; | ||||||
|  |  | ||||||
| 	Array<ssize>              Hashes; | 	Array<ssize>                Hashes; | ||||||
| 	Array<HashTableEntry<Type>> Entries; | 	Array<HashTableEntry<Type>> Entries; | ||||||
|  |  | ||||||
| #if 1 | #if 1 | ||||||
| #pragma region Member Mapping | #pragma region Member Mapping | ||||||
| 	forceinline static HashTable init(AllocatorInfo allocator)                  { return GEN_NS hashtable_init<Type>(allocator); } | 	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 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  clear()                           { GEN_NS clear<Type>(*this); } | ||||||
| 	forceinline void         destroy()                         { GEN_NS destroy<Type>(*this); } | 	forceinline void  destroy()                         { GEN_NS destroy<Type>(*this); } | ||||||
| 	forceinline Type*        get(u64 key)                      { return GEN_NS get<Type>(*this, key); } | 	forceinline Type* get(u64 key)                      { return GEN_NS get<Type>(*this, key); } | ||||||
| 	forceinline void         grow()                            { GEN_NS grow<Type>(*this); } | 	forceinline void  grow()                            { GEN_NS grow<Type>(*this); } | ||||||
| 	forceinline void         rehash(ssize new_num)             { GEN_NS rehash<Type>(*this, new_num); } | 	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  rehash_fast()                     { GEN_NS rehash_fast<Type>(*this); } | ||||||
| 	forceinline void         remove(u64 key)                   { GEN_NS remove<Type>(*this, key); } | 	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  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 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 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(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); } | 	forceinline void  map_mut(void (*proc)(u64, Type*)) { GEN_NS map_mut<Type>(*this, proc); } | ||||||
| #pragma endregion Member Mapping | #pragma endregion Member Mapping | ||||||
| #endif | #endif | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -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,417 +27,523 @@ 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. | ||||||
|  | #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 String | ||||||
| { | { | ||||||
| 	struct Header | 	char* Data; | ||||||
| 	{ |  | ||||||
| 		AllocatorInfo Allocator; | #if 1 | ||||||
| 		ssize            Capacity; | #pragma region Member Mapping | ||||||
| 		ssize            Length; | 	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 | 	static | ||||||
| 	usize grow_formula( usize value ) | 	String fmt(AllocatorInfo allocator, char* buf, ssize buf_size, char const* fmt, ...) { | ||||||
| 	{ | 		va_list va; | ||||||
| 		// Using a very aggressive growth formula to reduce time mem_copying with recursive calls to append in this library. | 		va_start(va, fmt); | ||||||
| 		return 4 * value + 8; | 		str_fmt_va(buf, buf_size, fmt, va); | ||||||
|  | 		va_end(va); | ||||||
|  | 		return GEN_NS string_make(allocator, buf); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	static | 	static | ||||||
| 	String make( AllocatorInfo allocator, char const* str ) | 	String fmt_buf(AllocatorInfo allocator, char const* fmt, ...) { | ||||||
| 	{ | 		local_persist thread_local | ||||||
| 		ssize length = str ? str_len( str ) : 0; | 		char buf[GEN_PRINTF_MAXLEN] = { 0 }; | ||||||
| 		return make_length( allocator, str, length ); | 		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 | 	forceinline bool          make_space_for(char const* str, ssize add_len) { return GEN_NS make_space_for(*this, str, add_len); } | ||||||
| 	String make( AllocatorInfo allocator, StrC str ) | 	forceinline bool          append(char c)                                 { return GEN_NS append(*this, c); } | ||||||
| 	{ | 	forceinline bool          append(char const* str)                        { return GEN_NS append(*this, str); } | ||||||
| 		return make_length( allocator, str.Ptr, str.Len ); | 	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 | 	forceinline operator bool()              { return Data != nullptr; } | ||||||
| 	String make_reserve( AllocatorInfo allocator, ssize capacity ); | 	forceinline operator char*()             { return Data; } | ||||||
|  | 	forceinline operator char const*() const { return Data; } | ||||||
|  | 	forceinline operator StrC() const        { return { length(), Data }; } | ||||||
|  |  | ||||||
| 	static | 	String const& operator=(String const& other) const { | ||||||
| 	String make_length( AllocatorInfo allocator, char const* str, ssize length ); | 		if (this == &other) | ||||||
|  |  | ||||||
| 	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 ) |  | ||||||
| 			return *this; | 			return *this; | ||||||
|  |  | ||||||
| 		String* | 		String* this_ = ccast(String*, this); | ||||||
| 		this_ = ccast(String*, this); |  | ||||||
| 		this_->Data = other.Data; | 		this_->Data = other.Data; | ||||||
|  |  | ||||||
| 		return *this; | 		return *this; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	char& operator [] ( ssize index ) | 	forceinline char& operator[](ssize index)             { return Data[index]; } | ||||||
| 	{ | 	forceinline char const& operator[](ssize index) const { return Data[index]; } | ||||||
| 		return Data[ index ]; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	char const& operator [] ( ssize index ) const | 	forceinline char* begin() const { return Data; } | ||||||
| 	{ | 	forceinline char* end() const   { return Data + length(); } | ||||||
| 		return Data[ index ]; | #pragma endregion Member Mapping | ||||||
| 	} | #endif | ||||||
|  |  | ||||||
| 	char* Data; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| 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; | 	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