217 lines
5.6 KiB
C++
217 lines
5.6 KiB
C++
// #TODO #string module
|
|
// [ ] I'm debating if string type should automatically null-terminate.
|
|
// I personally do not like it, and think we should temp-copy c-strings as they're needed.
|
|
bool is_valid (string s) {
|
|
return (s.data != nullptr && s.count > 0);
|
|
}
|
|
|
|
bool is_c_string (string s) {
|
|
return (s.data && s.data[s.count] == '\0');
|
|
}
|
|
|
|
u8* to_c_string (string s) {
|
|
u8* result = (u8*)internal_alloc(s.count + 1);
|
|
|
|
memcpy(result, s.data, s.count);
|
|
result[s.count] = '\0';
|
|
|
|
return result;
|
|
}
|
|
|
|
string copy_string (string s) {
|
|
Assert(s.count > 0);
|
|
if (s.count <= 0)
|
|
return "";
|
|
string str = {};
|
|
|
|
str.count = s.count;
|
|
str.data = (u8*)internal_alloc(s.count + 1);
|
|
|
|
memcpy(str.data, s.data, s.count);
|
|
|
|
str.data[str.count] = '\0'; // null-terminate for backwards compatibility?
|
|
|
|
return str;
|
|
}
|
|
|
|
string copy_string (char* c_string) {
|
|
string str = {0};
|
|
s64 string_length = strlen(c_string);
|
|
if (string_length == 0)
|
|
return "";
|
|
|
|
str.data = NewArray<u8>(string_length + 1);
|
|
memcpy(str.data, c_string, string_length);
|
|
str.count = string_length;
|
|
|
|
str.data[str.count] = '\0'; // null-terminate for backwards compatibility?
|
|
|
|
return str;
|
|
}
|
|
|
|
string to_string (ArrayView<u8> str) {
|
|
return {str.count, str.data};
|
|
}
|
|
|
|
void string_free (string& s) {
|
|
internal_free(s.data);
|
|
|
|
s.data = nullptr;
|
|
s.count = 0;
|
|
}
|
|
|
|
force_inline string string_view (string s, s64 start_index, s64 view_count) {
|
|
Assert(view_count >= 0); Assert(start_index >= 0);
|
|
if (view_count < 0 || start_index < 0 || start_index >= s.count) return "";
|
|
|
|
s64 new_count = view_count;
|
|
if (start_index + view_count > s.count) {
|
|
new_count = s.count - start_index;
|
|
}
|
|
|
|
return { new_count, s.data + start_index };
|
|
}
|
|
|
|
string copy_string_view (string s, s64 start_index, s64 view_count) {
|
|
// maybe redundant...
|
|
return copy_string(string_view(s, start_index, view_count));
|
|
}
|
|
|
|
bool strings_match (string first_string, string second_string) {
|
|
return (first_string == second_string);
|
|
}
|
|
|
|
// #Unicode
|
|
string wide_to_utf8 (u16* source, s32 length) {
|
|
if (length == 0) return { };
|
|
|
|
s32 query_result = WideCharToMultiByte(CP_UTF8, 0, (LPCWCH)source, length,
|
|
nullptr, 0, nullptr, nullptr);
|
|
|
|
if (query_result <= 0) return { };
|
|
|
|
// Make room for a null terminator:
|
|
query_result += 1;
|
|
|
|
u8* memory = NewArray<u8>(query_result);
|
|
|
|
string utf8_string;
|
|
utf8_string.count = query_result;
|
|
utf8_string.data = memory;
|
|
|
|
s32 result = WideCharToMultiByte(CP_UTF8, 0, (LPCWCH)source, length,
|
|
(LPSTR)memory, query_result, nullptr, nullptr);
|
|
if (result <= 0) {
|
|
internal_free(memory);
|
|
return { };
|
|
}
|
|
|
|
return utf8_string;
|
|
}
|
|
|
|
string format_string (char* format, ...) {
|
|
constexpr s64 BUFFER_SIZE = 4096;
|
|
|
|
string str = {0};
|
|
|
|
str.data = NewArray<u8>(BUFFER_SIZE);
|
|
|
|
va_list args;
|
|
va_start(args, format);
|
|
// Note that this *is* null-terminated for compatibility.
|
|
str.count = (s64)vsnprintf((char*)str.data, (size_t)BUFFER_SIZE, format, args);
|
|
va_end(args);
|
|
|
|
return str;
|
|
}
|
|
|
|
force_inline String_Builder* new_string_builder (Arena_Reserve new_reserve) {
|
|
return arena_array_new<u8>(1, new_reserve);
|
|
}
|
|
|
|
force_inline void append (String_Builder* sb, string s) {
|
|
array_add(*sb, ArrayView<u8>(s.count, s.data));
|
|
}
|
|
|
|
void append (String_Builder* sb, ArrayView<string> strings) {
|
|
s64 combined_length = 0;
|
|
for (s64 i = 0; i < strings.count; i += 1) {
|
|
combined_length += strings[i].count;
|
|
}
|
|
|
|
s64 final_length = sb->count + combined_length;
|
|
|
|
if (sb->allocated < final_length) {
|
|
array_reserve(*sb, final_length);
|
|
}
|
|
|
|
for (s64 i = 0; i < strings.count; i += 1) {
|
|
string s = strings[i];
|
|
array_add(*sb, ArrayView<u8>(s.count, s.data));
|
|
}
|
|
}
|
|
|
|
force_inline void append_no_add (String_Builder* sb, string s) {
|
|
array_add(*sb, ArrayView<u8>(s.count, s.data));
|
|
sb->count -= s.count;
|
|
}
|
|
|
|
// Unfortunately this follows the printf format, which is annoying.
|
|
// I'd rather have something like fmt::
|
|
void print_to_builder (String_Builder* sb, string format, va_list args) {
|
|
s64 expected_final_count = sb->count + format.count + 4096;
|
|
|
|
if (sb->allocated < expected_final_count) {
|
|
array_reserve(*sb, expected_final_count);
|
|
}
|
|
|
|
s64 buffer_size = sb->allocated - sb->count; // available space
|
|
u8* current_point = &sb->data[sb->count];
|
|
|
|
s64 print_count = (s64)vsnprintf((char*)current_point, (size_t)buffer_size, (char*)format.data, args);
|
|
|
|
sb->count += print_count;
|
|
}
|
|
|
|
void print_to_builder (String_Builder* sb, string format, ...) {
|
|
s64 expected_final_count = sb->count + format.count + 4096;
|
|
|
|
if (sb->allocated < expected_final_count) {
|
|
array_reserve(*sb, expected_final_count);
|
|
}
|
|
|
|
s64 buffer_size = sb->allocated - sb->count; // available space
|
|
u8* current_point = &sb->data[sb->count];
|
|
|
|
va_list args;
|
|
va_start(args, format);
|
|
s64 print_count = (s64)vsnprintf((char*)current_point, (size_t)buffer_size, (char*)format.data, args);
|
|
va_end(args);
|
|
|
|
sb->count += print_count;
|
|
}
|
|
|
|
string string_view (String_Builder* sb) {
|
|
// should probably ensure final byte is null terminated...
|
|
append_no_add(sb, "\0"); // doesn't increment sb.count
|
|
return to_string(to_view(*sb));
|
|
}
|
|
|
|
// for when we want to keep the string builder around and recycle the memory.
|
|
internal force_inline void reset_string_builder (String_Builder* sb) {
|
|
poison_range(*sb, 0, sb->count);
|
|
reset_keeping_memory(*sb);
|
|
}
|
|
|
|
force_inline string builder_to_string (String_Builder* sb) {
|
|
string final_string = copy_string(to_string(to_view(*sb)));
|
|
|
|
free_string_builder(sb);
|
|
|
|
return final_string;
|
|
}
|
|
|
|
internal force_inline void free_string_builder (String_Builder* sb) {
|
|
arena_array_free(*sb);
|
|
}
|