2023-08-28 20:46:50 -07:00
|
|
|
#ifdef GEN_INTELLISENSE_DIRECTIVES
|
|
|
|
# pragma once
|
|
|
|
# include "hashing.hpp"
|
|
|
|
#endif
|
2023-08-21 17:30:13 -07:00
|
|
|
|
2023-08-09 15:47:59 -07:00
|
|
|
#pragma region Strings
|
2023-07-25 20:00:57 -07:00
|
|
|
|
2023-07-24 15:19:37 -07:00
|
|
|
// Constant string with length.
|
|
|
|
struct StrC
|
|
|
|
{
|
2024-12-01 00:06:30 -08:00
|
|
|
ssize Len;
|
2023-07-24 15:19:37 -07:00
|
|
|
char const* Ptr;
|
|
|
|
|
2024-12-01 09:48:58 -08:00
|
|
|
#if ! GEN_COMPILER_C
|
2024-10-27 15:58:37 -07:00
|
|
|
operator char const* () const { return Ptr; }
|
|
|
|
char const& operator[]( ssize index ) const { return Ptr[index]; }
|
2024-12-01 09:48:58 -08:00
|
|
|
#endif
|
2023-07-24 15:19:37 -07:00
|
|
|
};
|
|
|
|
|
2024-10-27 15:58:37 -07:00
|
|
|
#define cast_to_strc( str ) * rcast( StrC*, (str) - sizeof(ssize) )
|
|
|
|
#define txt( text ) StrC { sizeof( text ) - 1, ( text ) }
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-10-25 02:01:37 -07:00
|
|
|
inline
|
2024-11-30 11:13:30 -08:00
|
|
|
StrC to_str( char const* str ) {
|
2023-07-24 15:19:37 -07:00
|
|
|
return { str_len( str ), str };
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dynamic String
|
|
|
|
// 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.
|
2024-11-30 11:13:30 -08:00
|
|
|
#pragma region String
|
|
|
|
struct StringHeader;
|
2024-12-01 00:06:30 -08:00
|
|
|
struct String;
|
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2024-12-01 00:06:30 -08:00
|
|
|
#if ! GEN_COMPILER_C
|
2023-07-24 15:19:37 -07:00
|
|
|
struct String
|
|
|
|
{
|
2024-11-30 11:13:30 -08:00
|
|
|
char* Data;
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-12-01 00:06:30 -08:00
|
|
|
forceinline operator bool() { return Data != nullptr; }
|
|
|
|
forceinline operator char*() { return Data; }
|
|
|
|
forceinline operator char const*() const { return Data; }
|
2024-12-01 02:30:37 -08:00
|
|
|
forceinline operator StrC() const { return { GEN_NS length(* this), Data }; }
|
2024-12-01 00:06:30 -08:00
|
|
|
|
|
|
|
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; }
|
2024-12-01 02:30:37 -08:00
|
|
|
forceinline char* end() const { return Data + GEN_NS length(* this); }
|
2024-12-01 00:06:30 -08:00
|
|
|
|
|
|
|
#if GEN_SUPPORT_CPP_MEMBER_FEATURES
|
2024-11-30 11:13:30 -08:00
|
|
|
#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); }
|
2023-07-24 15:19:37 -07:00
|
|
|
|
|
|
|
static
|
2024-11-30 11:13:30 -08:00
|
|
|
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);
|
2023-07-24 15:19:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static
|
2024-11-30 11:13:30 -08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
#pragma endregion Member Mapping
|
|
|
|
#endif
|
|
|
|
};
|
2024-12-01 00:06:30 -08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
inline char* begin(String& str) { return str; }
|
|
|
|
inline char* end(String& str) { return scast(char*, str) + length(str); }
|
|
|
|
inline char* next(String& str) { return scast(char*, str) + 1; }
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
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;
|
|
|
|
}
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
String string_make(AllocatorInfo allocator, char const* str) {
|
|
|
|
ssize length = str ? str_len(str) : 0;
|
|
|
|
return string_make_length(allocator, str, length);
|
|
|
|
}
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
String string_make(AllocatorInfo allocator, StrC str) {
|
|
|
|
return string_make_length(allocator, str.Ptr, str.Len);
|
|
|
|
}
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
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);
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
return string_make(allocator, buf);
|
|
|
|
}
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
String string_fmt_buf(AllocatorInfo allocator, char const* fmt, ...)
|
|
|
|
{
|
|
|
|
local_persist thread_local
|
|
|
|
char buf[GEN_PRINTF_MAXLEN] = { 0 };
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
|
|
|
str_fmt_va(buf, GEN_PRINTF_MAXLEN, fmt, va);
|
|
|
|
va_end(va);
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
return string_make(allocator, buf);
|
|
|
|
}
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
String string_join(AllocatorInfo allocator, char const** parts, ssize num_parts, char const* glue)
|
|
|
|
{
|
|
|
|
String result = string_make(allocator, "");
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
for (ssize idx = 0; idx < num_parts; ++idx)
|
2024-05-05 18:53:22 -07:00
|
|
|
{
|
2024-11-30 11:13:30 -08:00
|
|
|
append(result, parts[idx]);
|
2024-05-05 18:53:22 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
if (idx < num_parts - 1)
|
|
|
|
append(result, glue);
|
2024-05-05 18:53:22 -07:00
|
|
|
}
|
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
return result;
|
|
|
|
}
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
bool append(String& str, char c) {
|
|
|
|
return append(str, &c, 1);
|
|
|
|
}
|
2023-09-03 17:29:45 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
bool append(String& str, char const* str_to_append) {
|
|
|
|
return append(str, str_to_append, str_len(str_to_append));
|
|
|
|
}
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
bool append(String& str, char const* str_to_append, ssize append_length)
|
|
|
|
{
|
|
|
|
if (sptr(str_to_append) > 0)
|
2023-07-24 15:19:37 -07:00
|
|
|
{
|
2024-11-30 11:13:30 -08:00
|
|
|
ssize curr_len = length(str);
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
if (!make_space_for(str, str_to_append, append_length))
|
|
|
|
return false;
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
StringHeader& header = get_header(str);
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-12-01 00:06:30 -08:00
|
|
|
mem_copy( scast(char*, str) + curr_len, str_to_append, append_length);
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-12-01 00:06:30 -08:00
|
|
|
str[curr_len + append_length] = '\0';
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
header.Length = curr_len + append_length;
|
2023-07-24 15:19:37 -07:00
|
|
|
}
|
2024-11-30 11:13:30 -08:00
|
|
|
return str_to_append != nullptr;
|
|
|
|
}
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
bool append(String& str, StrC str_to_append) {
|
|
|
|
return append(str, str_to_append.Ptr, str_to_append.Len);
|
|
|
|
}
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
bool append(String& str, const String other) {
|
2024-12-01 00:06:30 -08:00
|
|
|
return append(str, other, length(other));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool append_fmt(String& str, 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(str, buf, res);
|
2024-11-30 11:13:30 -08:00
|
|
|
}
|
2023-08-08 19:14:58 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
bool are_equal(String lhs, String rhs)
|
|
|
|
{
|
|
|
|
if (length(lhs) != length(rhs))
|
|
|
|
return false;
|
2024-11-29 11:50:54 -08:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
for (ssize idx = 0; idx < length(lhs); ++idx)
|
|
|
|
if (lhs[idx] != rhs[idx])
|
2024-11-29 11:50:54 -08:00
|
|
|
return false;
|
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
return true;
|
|
|
|
}
|
2024-11-29 11:50:54 -08:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
bool are_equal(String lhs, StrC rhs)
|
|
|
|
{
|
|
|
|
if (length(lhs) != (rhs.Len))
|
2024-11-29 11:50:54 -08:00
|
|
|
return false;
|
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
for (ssize idx = 0; idx < length(lhs); ++idx)
|
|
|
|
if (lhs[idx] != rhs[idx])
|
2024-11-29 11:50:54 -08:00
|
|
|
return false;
|
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
return true;
|
|
|
|
}
|
2024-11-29 11:50:54 -08:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
ssize avail_space(String const& str) {
|
2024-12-01 00:06:30 -08:00
|
|
|
StringHeader const& header = *rcast(StringHeader const*, scast(char const*, str) - sizeof(StringHeader));
|
2024-11-30 11:13:30 -08:00
|
|
|
return header.Capacity - header.Length;
|
|
|
|
}
|
2024-11-29 11:50:54 -08:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
char& back(String& str) {
|
|
|
|
return str.Data[length(str) - 1];
|
|
|
|
}
|
2024-11-29 11:50:54 -08:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
bool contains(String const& str, StrC substring)
|
|
|
|
{
|
2024-12-01 00:06:30 -08:00
|
|
|
StringHeader const& header = *rcast(StringHeader const*, scast(char const*, str) - sizeof(StringHeader));
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
if (substring.Len > header.Length)
|
|
|
|
return false;
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
ssize main_len = header.Length;
|
|
|
|
ssize sub_len = substring.Len;
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
for (ssize idx = 0; idx <= main_len - sub_len; ++idx)
|
2023-07-24 15:19:37 -07:00
|
|
|
{
|
2024-11-30 11:13:30 -08:00
|
|
|
if (str_compare(str.Data + idx, substring.Ptr, sub_len) == 0)
|
|
|
|
return true;
|
2023-07-24 15:19:37 -07:00
|
|
|
}
|
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
return false;
|
|
|
|
}
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
bool contains(String const& str, String const& substring)
|
|
|
|
{
|
2024-12-01 00:06:30 -08:00
|
|
|
StringHeader const& header = *rcast(StringHeader const*, scast(char const*, str) - sizeof(StringHeader));
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
if (length(substring) > header.Length)
|
|
|
|
return false;
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
ssize main_len = header.Length;
|
|
|
|
ssize sub_len = length(substring);
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
for (ssize idx = 0; idx <= main_len - sub_len; ++idx)
|
2023-07-24 15:19:37 -07:00
|
|
|
{
|
2024-11-30 11:13:30 -08:00
|
|
|
if (str_compare(str.Data + idx, substring.Data, sub_len) == 0)
|
|
|
|
return true;
|
2023-07-24 15:19:37 -07:00
|
|
|
}
|
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
return false;
|
|
|
|
}
|
2024-05-05 18:53:22 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
ssize capacity(String const& str) {
|
2024-12-01 00:06:30 -08:00
|
|
|
StringHeader const& header = *rcast(StringHeader const*, scast(char const*, str) - sizeof(StringHeader));
|
2024-11-30 11:13:30 -08:00
|
|
|
return header.Capacity;
|
|
|
|
}
|
2024-05-05 18:53:22 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
void clear(String& str) {
|
|
|
|
get_header(str).Length = 0;
|
|
|
|
}
|
2024-05-05 18:53:22 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
String duplicate(String const& str, AllocatorInfo allocator) {
|
2024-12-01 00:06:30 -08:00
|
|
|
return string_make_length(allocator, str, length(str));
|
2024-11-30 11:13:30 -08:00
|
|
|
}
|
2024-05-05 18:53:22 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
void free(String& str) {
|
|
|
|
if (!str.Data)
|
|
|
|
return;
|
2023-08-21 17:30:13 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
StringHeader& header = get_header(str);
|
|
|
|
GEN_NS free(header.Allocator, &header);
|
|
|
|
}
|
2023-08-21 17:30:13 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
StringHeader& get_header(String& str) {
|
|
|
|
return *(StringHeader*)(str.Data - sizeof(StringHeader));
|
|
|
|
}
|
2023-08-21 17:30:13 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
ssize length(String const& str)
|
|
|
|
{
|
2024-12-01 00:06:30 -08:00
|
|
|
StringHeader const& header = *rcast(StringHeader const*, scast(char const*, str) - sizeof(StringHeader));
|
2024-11-30 11:13:30 -08:00
|
|
|
return header.Length;
|
|
|
|
}
|
2023-08-21 17:30:13 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
bool make_space_for(String& str, char const* to_append, ssize add_len)
|
|
|
|
{
|
|
|
|
ssize available = avail_space(str);
|
2023-08-21 17:30:13 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
if (available >= add_len) {
|
|
|
|
return true;
|
2023-08-26 08:55:05 -07:00
|
|
|
}
|
2024-11-30 11:13:30 -08:00
|
|
|
else
|
2023-07-24 15:19:37 -07:00
|
|
|
{
|
2024-11-30 11:13:30 -08:00
|
|
|
ssize new_len, old_size, new_size;
|
|
|
|
void* ptr;
|
|
|
|
void* new_ptr;
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
AllocatorInfo allocator = get_header(str).Allocator;
|
|
|
|
StringHeader* header = nullptr;
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
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;
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
new_ptr = resize(allocator, ptr, old_size, new_size);
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
if (new_ptr == nullptr)
|
|
|
|
return false;
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
header = rcast(StringHeader*, new_ptr);
|
|
|
|
header->Allocator = allocator;
|
|
|
|
header->Capacity = new_len;
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
str.Data = rcast(char*, header + 1);
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
return true;
|
2023-07-24 15:19:37 -07:00
|
|
|
}
|
2024-11-30 11:13:30 -08:00
|
|
|
}
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
b32 starts_with(String const& str, StrC substring) {
|
|
|
|
if (substring.Len > length(str))
|
|
|
|
return false;
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
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;
|
2023-09-03 17:29:45 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
b32 result = str_compare(str.Data, substring.Data, length(substring) - 1) == 0;
|
|
|
|
return result;
|
|
|
|
}
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
void skip_line(String& str)
|
|
|
|
{
|
|
|
|
#define current (*scanner)
|
|
|
|
char* scanner = str.Data;
|
|
|
|
while (current != '\r' && current != '\n') {
|
|
|
|
++scanner;
|
2023-07-24 15:19:37 -07:00
|
|
|
}
|
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
s32 new_length = scanner - str.Data;
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
if (current == '\r') {
|
|
|
|
new_length += 1;
|
2023-07-24 15:19:37 -07:00
|
|
|
}
|
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
mem_move(str.Data, scanner, new_length);
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
StringHeader* header = &get_header(str);
|
|
|
|
header->Length = new_length;
|
|
|
|
#undef current
|
|
|
|
}
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
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;
|
|
|
|
}
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
inline
|
|
|
|
void trim(String& str, char const* cut_set)
|
|
|
|
{
|
|
|
|
ssize len = 0;
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
char* start_pos = str.Data;
|
|
|
|
char* end_pos = str.Data + length(str) - 1;
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
while (start_pos <= end_pos && char_first_occurence(cut_set, *start_pos))
|
|
|
|
start_pos++;
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
while (end_pos > start_pos && char_first_occurence(cut_set, *end_pos))
|
|
|
|
end_pos--;
|
2023-07-25 20:00:57 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
len = scast(ssize, (start_pos > end_pos) ? 0 : ((end_pos - start_pos) + 1));
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
if (str.Data != start_pos)
|
|
|
|
mem_move(str.Data, start_pos, len);
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
str.Data[len] = '\0';
|
2023-07-24 15:19:37 -07:00
|
|
|
|
2024-11-30 11:13:30 -08:00
|
|
|
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)
|
2023-07-24 15:19:37 -07:00
|
|
|
{
|
2024-12-01 00:06:30 -08:00
|
|
|
StringHeader* header = (StringHeader*)(scast(char const*, str) - sizeof(StringHeader));
|
2024-11-30 11:13:30 -08:00
|
|
|
String result = string_make_reserve(header->Allocator, length(str) * 2); // Assume worst case for space requirements.
|
|
|
|
|
2024-12-01 00:06:30 -08:00
|
|
|
for (auto c : str) switch (c)
|
2024-11-30 11:13:30 -08:00
|
|
|
{
|
|
|
|
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 {
|
2023-07-24 15:19:37 -07:00
|
|
|
char* Data;
|
|
|
|
};
|
|
|
|
static_assert( sizeof( String_POD ) == sizeof( String ), "String is not a POD" );
|
2023-07-25 20:00:57 -07:00
|
|
|
|
2023-08-09 15:47:59 -07:00
|
|
|
// Implements basic string interning. Data structure is based off the ZPL Hashtable.
|
2024-12-01 00:06:30 -08:00
|
|
|
typedef HashTable<String const> StringTable;
|
2023-08-09 15:47:59 -07:00
|
|
|
|
|
|
|
// Represents strings cached with the string table.
|
|
|
|
// Should never be modified, if changed string is desired, cache_string( str ) another.
|
2024-12-01 00:06:30 -08:00
|
|
|
typedef String const StringCached;
|
2023-08-09 15:47:59 -07:00
|
|
|
|
|
|
|
#pragma endregion Strings
|