strings done

This commit is contained in:
Edward R. Gonzalez 2024-11-30 14:13:30 -05:00
parent c6cb583518
commit 79eb5f1f76
4 changed files with 518 additions and 489 deletions

View File

@ -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 )
{ {

View File

@ -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

View File

@ -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,149 +27,286 @@ StrC to_str( char const* str )
// This is directly based off the ZPL string api. // This is directly based off the ZPL string api.
// They used a header pattern // They used a header pattern
// I kept it for simplicty of porting but its not necessary to keep it that way. // I kept it for simplicty of porting but its not necessary to keep it that way.
struct String #pragma region String
{ struct String;
struct Header struct StringHeader;
{
// Forward declarations for all file-scope functions
String string_make(AllocatorInfo allocator, char const* str);
String string_make(AllocatorInfo allocator, StrC str);
String string_make_reserve(AllocatorInfo allocator, ssize capacity);
String string_make_length(AllocatorInfo allocator, char const* str, ssize length);
String string_fmt(AllocatorInfo allocator, char* buf, ssize buf_size, char const* fmt, ...);
String string_fmt_buf(AllocatorInfo allocator, char const* fmt, ...);
String string_join(AllocatorInfo allocator, char const** parts, ssize num_parts, char const* glue);
usize string_grow_formula(usize value);
bool are_equal(String lhs, String rhs);
bool are_equal(String lhs, StrC rhs);
bool make_space_for(String& str, char const* to_append, ssize add_len);
bool append(String& str, char c);
bool append(String& str, char const* str_to_append);
bool append(String& str, char const* str_to_append, ssize length);
bool append(String& str, StrC str_to_append);
bool append(String& str, const String other);
bool append_fmt(String& str, char const* fmt, ...);
ssize avail_space(String const& str);
char& back(String& str);
bool contains(String const& str, StrC substring);
bool contains(String const& str, String const& substring);
ssize capacity(String const& str);
void clear(String& str);
String duplicate(String const& str, AllocatorInfo allocator);
void free(String& str);
StringHeader& get_header(String& str);
ssize length(String const& str);
b32 starts_with(String const& str, StrC substring);
b32 starts_with(String const& str, String substring);
void skip_line(String& str);
void strip_space(String& str);
void trim(String& str, char const* cut_set);
void trim_space(String& str);
String visualize_whitespace(String const& str);
struct StringHeader {
AllocatorInfo Allocator; AllocatorInfo Allocator;
ssize Capacity; ssize Capacity;
ssize Length; ssize Length;
}; };
struct String
{
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 static
usize grow_formula( usize value ) String fmt(AllocatorInfo allocator, char* buf, ssize buf_size, char const* fmt, ...) {
{ va_list va;
va_start(va, fmt);
str_fmt_va(buf, buf_size, fmt, va);
va_end(va);
return GEN_NS string_make(allocator, buf);
}
static
String fmt_buf(AllocatorInfo allocator, char const* fmt, ...) {
local_persist thread_local
char buf[GEN_PRINTF_MAXLEN] = { 0 };
va_list va;
va_start(va, fmt);
str_fmt_va(buf, GEN_PRINTF_MAXLEN, fmt, va);
va_end(va);
return GEN_NS string_make(allocator, buf);
}
forceinline bool make_space_for(char const* str, ssize add_len) { return GEN_NS make_space_for(*this, str, add_len); }
forceinline bool append(char c) { return GEN_NS append(*this, c); }
forceinline bool append(char const* str) { return GEN_NS append(*this, str); }
forceinline bool append(char const* str, ssize length) { return GEN_NS append(*this, str, length); }
forceinline bool append(StrC str) { return GEN_NS append(*this, str); }
forceinline bool append(const String other) { return GEN_NS append(*this, other); }
forceinline ssize avail_space() const { return GEN_NS avail_space(*this); }
forceinline char& back() { return GEN_NS back(*this); }
forceinline bool contains(StrC substring) const { return GEN_NS contains(*this, substring); }
forceinline bool contains(String const& substring) const { return GEN_NS contains(*this, substring); }
forceinline ssize capacity() const { return GEN_NS capacity(*this); }
forceinline void clear() { GEN_NS clear(*this); }
forceinline String duplicate(AllocatorInfo allocator) const { return GEN_NS duplicate(*this, allocator); }
forceinline void free() { GEN_NS free(*this); }
forceinline ssize length() const { return GEN_NS length(*this); }
forceinline b32 starts_with(StrC substring) const { return GEN_NS starts_with(*this, substring); }
forceinline b32 starts_with(String substring) const { return GEN_NS starts_with(*this, substring); }
forceinline void skip_line() { GEN_NS skip_line(*this); }
forceinline void strip_space() { GEN_NS strip_space(*this); }
forceinline void trim(char const* cut_set) { GEN_NS trim(*this, cut_set); }
forceinline void trim_space() { GEN_NS trim_space(*this); }
forceinline String visualize_whitespace() const { return GEN_NS visualize_whitespace(*this); }
forceinline StringHeader& get_header() { return GEN_NS get_header(*this); }
bool append_fmt(char const* fmt, ...) {
ssize res;
char buf[GEN_PRINTF_MAXLEN] = { 0 };
va_list va;
va_start(va, fmt);
res = str_fmt_va(buf, count_of(buf) - 1, fmt, va) - 1;
va_end(va);
return GEN_NS append(*this, buf, res);
}
forceinline operator bool() { return Data != nullptr; }
forceinline operator char*() { return Data; }
forceinline operator char const*() const { return Data; }
forceinline operator StrC() const { return { length(), Data }; }
String const& operator=(String const& other) const {
if (this == &other)
return *this;
String* this_ = ccast(String*, this);
this_->Data = other.Data;
return *this;
}
forceinline char& operator[](ssize index) { return Data[index]; }
forceinline char const& operator[](ssize index) const { return Data[index]; }
forceinline char* begin() const { return Data; }
forceinline char* end() const { return Data + length(); }
#pragma endregion Member Mapping
#endif
};
inline
usize string_grow_formula(usize value) {
// Using a very aggressive growth formula to reduce time mem_copying with recursive calls to append in this library. // Using a very aggressive growth formula to reduce time mem_copying with recursive calls to append in this library.
return 4 * value + 8; return 4 * value + 8;
} }
static inline
String make( AllocatorInfo allocator, char const* str ) String string_make(AllocatorInfo allocator, char const* str) {
ssize length = str ? str_len(str) : 0;
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)
{ {
ssize length = str ? str_len( str ) : 0; append(result, parts[idx]);
return make_length( allocator, str, length );
}
static if (idx < num_parts - 1)
String make( AllocatorInfo allocator, StrC str ) append(result, glue);
{
return make_length( allocator, str.Ptr, str.Len );
}
static
String make_reserve( AllocatorInfo allocator, ssize capacity );
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; return result;
} }
static inline
bool are_equal( String lhs, String rhs ) 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)
{ {
if ( lhs.length() != rhs.length() ) ssize curr_len = length(str);
if (!make_space_for(str, str_to_append, append_length))
return false; return false;
for ( ssize idx = 0; idx < lhs.length(); ++idx ) StringHeader& header = get_header(str);
if ( lhs[ idx ] != rhs[ idx ] )
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 false;
return true; return true;
} }
static inline
bool are_equal( String lhs, StrC rhs ) bool are_equal(String lhs, StrC rhs)
{ {
if ( lhs.length() != (rhs.Len) ) if (length(lhs) != (rhs.Len))
return false; return false;
for ( ssize idx = 0; idx < lhs.length(); ++idx ) for (ssize idx = 0; idx < length(lhs); ++idx)
if ( lhs[idx] != rhs[idx] ) if (lhs[idx] != rhs[idx])
return false; return false;
return true; return true;
} }
bool make_space_for( char const* str, ssize add_len );
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 ));
inline
ssize avail_space(String const& str) {
StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader));
return header.Capacity - header.Length; return header.Capacity - header.Length;
} }
char& back() inline
{ char& back(String& str) {
return Data[ length() - 1 ]; return str.Data[length(str) - 1];
} }
bool contains(StrC substring) const inline
{ bool contains(String const& str, StrC substring)
Header const& header = * rcast( Header const*, Data - sizeof( Header )); {
StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader));
if (substring.Len > header.Length) if (substring.Len > header.Length)
return false; return false;
@ -180,122 +316,156 @@ struct String
for (ssize idx = 0; idx <= main_len - sub_len; ++idx) for (ssize idx = 0; idx <= main_len - sub_len; ++idx)
{ {
if (str_compare(Data + idx, substring.Ptr, sub_len) == 0) if (str_compare(str.Data + idx, substring.Ptr, sub_len) == 0)
return true; return true;
} }
return false; return false;
} }
bool contains(String const& substring) const inline
{ bool contains(String const& str, String const& substring)
Header const& header = * rcast( Header const*, Data - sizeof( Header )); {
StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader));
if (substring.length() > header.Length) if (length(substring) > header.Length)
return false; return false;
ssize main_len = header.Length; ssize main_len = header.Length;
ssize sub_len = substring.length(); ssize sub_len = length(substring);
for (ssize idx = 0; idx <= main_len - sub_len; ++idx) for (ssize idx = 0; idx <= main_len - sub_len; ++idx)
{ {
if (str_compare(Data + idx, substring.Data, sub_len) == 0) if (str_compare(str.Data + idx, substring.Data, sub_len) == 0)
return true; return true;
} }
return false; return false;
} }
ssize capacity() const
{
Header const&
header = * rcast( Header const*, Data - sizeof( Header ));
inline
ssize capacity(String const& str) {
StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader));
return header.Capacity; return header.Capacity;
} }
void clear() inline
{ void clear(String& str) {
get_header().Length = 0; get_header(str).Length = 0;
} }
String duplicate( AllocatorInfo allocator ) const inline
{ String duplicate(String const& str, AllocatorInfo allocator) {
return make_length( allocator, Data, length() ); return string_make_length(allocator, str.Data, length(str));
} }
void free() inline
{ void free(String& str) {
if ( ! Data ) if (!str.Data)
return; return;
Header& header = get_header(); StringHeader& header = get_header(str);
GEN_NS free(header.Allocator, &header);
}
gen::free( header.Allocator, & header ); inline
} StringHeader& get_header(String& str) {
return *(StringHeader*)(str.Data - sizeof(StringHeader));
Header& get_header() }
{
return *(Header*)(Data - sizeof(Header));
}
ssize length() const
{
Header const&
header = * rcast( Header const*, Data - sizeof( Header ));
inline
ssize length(String const& str)
{
StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader));
return header.Length; return header.Length;
} }
b32 starts_with( StrC substring ) const inline
bool make_space_for(String& str, char const* to_append, ssize add_len)
{
ssize available = avail_space(str);
if (available >= add_len) {
return true;
}
else
{ {
if (substring.Len > length()) ssize new_len, old_size, new_size;
void* ptr;
void* new_ptr;
AllocatorInfo allocator = get_header(str).Allocator;
StringHeader* header = nullptr;
new_len = string_grow_formula(length(str) + add_len);
ptr = &get_header(str);
old_size = size_of(StringHeader) + length(str) + 1;
new_size = size_of(StringHeader) + new_len + 1;
new_ptr = resize(allocator, ptr, old_size, new_size);
if (new_ptr == nullptr)
return false; return false;
b32 result = str_compare(Data, substring.Ptr, substring.Len ) == 0; header = rcast(StringHeader*, new_ptr);
return result; header->Allocator = allocator;
} header->Capacity = new_len;
b32 starts_with( String substring ) const str.Data = rcast(char*, header + 1);
{
if (substring.length() > length()) return true;
}
}
inline
b32 starts_with(String const& str, StrC substring) {
if (substring.Len > length(str))
return false; return false;
b32 result = str_compare(Data, substring, substring.length() - 1 ) == 0; b32 result = str_compare(str.Data, substring.Ptr, substring.Len) == 0;
return result; 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;
} }
void skip_line() s32 new_length = scanner - str.Data;
{
#define current (*scanner)
char* scanner = Data;
while ( current != '\r' && current != '\n' )
{
++ scanner;
}
s32 new_length = scanner - Data; if (current == '\r') {
if ( current == '\r' )
{
new_length += 1; new_length += 1;
} }
mem_move( Data, scanner, new_length ); mem_move(str.Data, scanner, new_length);
Header* header = & get_header(); StringHeader* header = &get_header(str);
header->Length = new_length; header->Length = new_length;
#undef current #undef current
} }
void strip_space() inline
{ void strip_space(String& str)
char* write_pos = Data; {
char* read_pos = Data; char* write_pos = str.Data;
char* read_pos = str.Data;
while ( * read_pos) while (*read_pos)
{ {
if ( ! char_is_space( *read_pos )) if (!char_is_space(*read_pos))
{ {
*write_pos = *read_pos; *write_pos = *read_pos;
write_pos++; write_pos++;
@ -306,139 +476,74 @@ struct String
write_pos[0] = '\0'; // Null-terminate the modified string write_pos[0] = '\0'; // Null-terminate the modified string
// Update the length if needed // Update the length if needed
get_header().Length = write_pos - Data; get_header(str).Length = write_pos - str.Data;
} }
void trim( char const* cut_set ) inline
{ void trim(String& str, char const* cut_set)
{
ssize len = 0; ssize len = 0;
char* start_pos = Data; char* start_pos = str.Data;
char* end_pos = Data + length() - 1; char* end_pos = str.Data + length(str) - 1;
while ( start_pos <= end_pos && char_first_occurence( cut_set, *start_pos ) ) while (start_pos <= end_pos && char_first_occurence(cut_set, *start_pos))
start_pos++; start_pos++;
while ( end_pos > start_pos && char_first_occurence( cut_set, *end_pos ) ) while (end_pos > start_pos && char_first_occurence(cut_set, *end_pos))
end_pos--; end_pos--;
len = scast( ssize, ( start_pos > end_pos ) ? 0 : ( ( end_pos - start_pos ) + 1 ) ); len = scast(ssize, (start_pos > end_pos) ? 0 : ((end_pos - start_pos) + 1));
if ( Data != start_pos ) if (str.Data != start_pos)
mem_move( Data, start_pos, len ); mem_move(str.Data, start_pos, len);
Data[ len ] = '\0'; str.Data[len] = '\0';
get_header().Length = len; get_header(str).Length = len;
} }
void trim_space() inline
{ void trim_space(String& str) {
return trim( " \t\r\n\v\f" ); trim(str, " \t\r\n\v\f");
} }
// Debug function that provides a copy of the string with whitespace characters visualized. inline
String visualize_whitespace() const String visualize_whitespace(String const& str)
{ {
Header* header = (Header*)(Data - sizeof(Header)); StringHeader* header = (StringHeader*)(str.Data - sizeof(StringHeader));
String result = string_make_reserve(header->Allocator, length(str) * 2); // Assume worst case for space requirements.
String result = make_reserve(header->Allocator, length() * 2); // Assume worst case for space requirements. for (char c : str) switch (c)
for ( char c : *this )
{
switch ( c )
{ {
case ' ': case ' ':
result.append( txt("·") ); append(result, txt("·"));
break; break;
case '\t': case '\t':
result.append( txt("") ); append(result, txt(""));
break; break;
case '\n': case '\n':
result.append( txt("") ); append(result, txt(""));
break; break;
case '\r': case '\r':
result.append( txt("") ); append(result, txt(""));
break; break;
case '\v': case '\v':
result.append( txt("") ); append(result, txt(""));
break; break;
case '\f': case '\f':
result.append( txt("") ); append(result, txt(""));
break; break;
default: default:
result.append(c); append(result, c);
break; break;
} }
}
return result; return result;
} }
#pragma endregion String
// For-range support struct String_POD {
char* begin() const
{
return Data;
}
char* end() const
{
Header const&
header = * rcast( Header const*, Data - sizeof( Header ));
return Data + header.Length;
}
operator bool()
{
return Data != nullptr;
}
operator char* ()
{
return Data;
}
operator char const* () const
{
return Data;
}
operator StrC() const
{
return { length(), Data };
}
// Used with cached strings
// Essentially makes the string a string view.
String const& operator = ( String const& other ) const
{
if ( this == & other )
return *this;
String*
this_ = ccast(String*, this);
this_->Data = other.Data;
return *this;
}
char& operator [] ( ssize index )
{
return Data[ index ];
}
char const& operator [] ( ssize index ) const
{
return Data[ index ];
}
char* Data;
};
struct String_POD
{
char* Data; char* Data;
}; };
static_assert( sizeof( String_POD ) == sizeof( String ), "String is not a POD" ); static_assert( sizeof( String_POD ) == sizeof( String ), "String is not a POD" );