Create a cached string_builder in the thread local context.

This commit is contained in:
Musa Mahmood 2025-12-18 20:19:47 -05:00
parent 8ebdcb27a8
commit 14eb817631
13 changed files with 68 additions and 37 deletions

View File

@ -1,7 +1,7 @@
// Treat library files as a single-file (single translation unit) header
#include "lib_main.cpp"
#define BASE_RUN_TESTS 0
#define BASE_RUN_TESTS BUILD_DEBUG
#define BUILD_EXPLORER_APP_WIN32 1
#define BUILD_CUSTOM_GUI 0

View File

@ -209,6 +209,7 @@ template <typename T> void init_range (T* ptr, s64 start_offset, s64 end_offset)
template <typename T> void array_poison_range (ArenaArray<T>& array, s64 start, s64 count) {
#if BUILD_DEBUG
if (count == 0) return;
Assert(start >= 0 && start < array.count);
Assert(start + count <= array.count);
// Check that these ranges make sense

View File

@ -342,6 +342,7 @@ force_inline s64 Next_Power_Of_Two(s64 v) {
#define temp() allocator(thread_context()->temp)
#define context_allocator() thread_context()->allocator
#define context_logger() &thread_context()->logger
#define context_builder() thread_context()->string_builder
// CHECK THAT THESE ARE CORRECT!
constexpr f32 TAU = 6.283185f;

View File

@ -28,6 +28,7 @@ internal void Bootstrap_Main_Thread_Context () {
thread_local_context->thread_idx = 0;
thread_local_context->thread_name = "Main Thread";
thread_local_context->log_builder = new_string_builder(Arena_Reserve::Size_64M);
thread_local_context->string_builder = new_string_builder(Arena_Reserve::Size_2M);
thread_local_context->error_arena = bootstrap_arena(Arena_Reserve::Size_64M);
default_logger_initialize();

View File

@ -24,7 +24,8 @@ struct Thread_Context {
// u16 _padding0;
u16 default_allocator_alignment = 16;
Logger logger = {nullptr, nullptr};
String_Builder* log_builder;
String_Builder* log_builder; // String builder used by log() and log_error_internal()
String_Builder* string_builder; // Secondary builder just for convenience!
Stack_Trace_Node* stack_trace; // use `list(stack_trace)` in watch window of raddbg to view as array!
Array<Thread*> child_threads; // maybe should be linked-list?
@ -127,7 +128,8 @@ struct Push_Stack_Trace {
};
// #TODO: precede with something like: os_write_string_unsynchronized("Fatal Error!\n\nStack trace:", true);
string generate_stack_trace (Thread_Context* context) { // #no_context
string generate_stack_trace (Thread_Context* context) {
// #no_context - we want this to work even if context is utterly broken.
String_Builder* sb = new_string_builder(Arena_Reserve::Size_64K);
Stack_Trace_Node* node = context->stack_trace;
@ -143,7 +145,12 @@ string generate_stack_trace (Thread_Context* context) { // #no_context
append(sb, "\n");
push_allocator(default_allocator());
return builder_to_string(sb);
string stack_trace_copy = builder_to_string(sb);
free_string_builder(sb);
return stack_trace_copy;
}
// We don't want to use context logger here!

View File

@ -282,7 +282,7 @@ internal force_inline void reset_string_builder (String_Builder* sb, bool keep_m
force_inline string builder_to_string (String_Builder* sb) {
string final_string = copy_string(to_string(to_view(*sb)));
free_string_builder(sb);
// reset_string_builder(sb, false); // maybe this shouldn't be here! Too magical...
return final_string;
}

View File

@ -1,10 +1,14 @@
// #string_builder
// #limitations This won't be as fast as Jon's String_Builder in jai because we're backing it with an
// Arena, which requires a variable number of cycles depending on if our process has
// memory available already. It also has a max capacity depending on what Arena_Reserve we choose.
// That being said, the implementation is much simpler.
// Arena, which calls VirtualAlloc, which is much slower than just using stack space to start.
// It also has a max capacity depending on what Arena_Reserve we choose.
// That being said, the implementation is much simpler, and we can keep it around for a bit.
// We can make it a lot faster by always having a string builder available in the thread_context,
// and just fetching that when we need it.
typedef ArenaArray<u8> String_Builder; // struct String_Builder
force_inline String_Builder* new_string_builder (Arena_Reserve new_reserve=Arena_Reserve::Size_64K);
force_inline void append (String_Builder* sb, string s);
void append (String_Builder* sb, ArrayView<string> strings);
@ -18,3 +22,5 @@ internal force_inline void reset_string_builder (String_Builder* sb, bool keep_m
// #rename copy_string_and_free_builder
force_inline string builder_to_string (String_Builder* sb); // returns copy and frees string_builder
internal force_inline void free_string_builder (String_Builder* sb);

View File

@ -19,7 +19,9 @@ void run_post_setup_tests() {
// String builder example:
// OK. I can work with this.
auto sb = new_string_builder(Arena_Reserve::Size_64K);
String_Builder* sb = context_builder();
reset_string_builder(sb, true);
append(sb, "string_literal_example");
append(sb, " ");
print_to_builder(sb, "There are %d cats in the %s", 64, "house.\n");
@ -47,9 +49,9 @@ void run_post_setup_tests() {
// push_allocator(allocator(thread_context()->arena));
push_allocator(default_allocator());
string file_path = "D:/Work/OpenBCI/ToolZ/prototyping-gui-main/modules/native-proto-lib/native-sdk-prototyping/src/SignalProcessing.cpp";
ArrayView<u8> file_data = read_entire_file(file_path, true);
ArrayView<u8> file_data = read_entire_file(file_path, true, true);
log("file_data: \n");
log("%s\n", file_data.data);
log("%s\n", file_data.data); // #note this is not null-terminated
}
{ // Test hashing:
u64 start = 0x512585;

View File

@ -84,8 +84,8 @@ string get_full_path (string drive_label, Dense_FS* dfs, s32 first_parent_index,
array_add(path_list_reverse, drive_letter_with_colon);
// reset_string_builder(sb, true);
String_Builder* sb = new_string_builder();
String_Builder* sb = context_builder();
reset_string_builder(sb, true);
for (s64 i = path_list_reverse.count-1; i >= 0; i -= 1) {
append(sb, path_list_reverse[i]);

View File

@ -303,6 +303,7 @@ internal bool thread_init (Thread* thread, Thread_Proc proc, string thread_name)
// #NOTE: This will disappear once the thread is de-initted. If we want this string, copy it!
thread->context->thread_name = copy_string(thread_name);
thread->context->log_builder = new_string_builder(Arena_Reserve::Size_64M);
thread->context->string_builder = new_string_builder(Arena_Reserve::Size_2M);
thread->context->error_arena = bootstrap_arena(Arena_Reserve::Size_64M);
thread->context->logger = {default_logger_proc, &default_logger};
@ -557,7 +558,7 @@ internal bool file_set_position (File file, s64 position) {
return (bool)SetFilePointerEx(file.handle, position_li, nullptr, FILE_BEGIN);
}
internal ArrayView<u8> read_entire_file (File file) {
ArrayView<u8> read_entire_file (File file, bool add_null_terminator) {
ArrayView<u8> file_data;
bool result = file_length(file, &file_data.count);
@ -565,7 +566,13 @@ internal ArrayView<u8> read_entire_file (File file) {
result = file_set_position(file, 0);
if (!result) return {};
file_data.data = NewArray<u8>(file_data.count, false);
s64 total_file_size = file_data.count;
if (add_null_terminator) {
total_file_size += 1;
}
file_data.data = NewArray<u8>(total_file_size, false);
if (file_data.data == nullptr) return {};
s64 bytes_read = 0;
@ -575,17 +582,21 @@ internal ArrayView<u8> read_entire_file (File file) {
return {};
}
if (add_null_terminator) {
file_data[total_file_size-1] = 0;
}
Assert(bytes_read == file_data.count);
file_data.count = bytes_read;
return file_data;
}
internal ArrayView<u8> read_entire_file (string file_path, bool log_errors) {
internal ArrayView<u8> read_entire_file (string file_path, bool add_null_terminator, bool log_errors) {
File f = file_open(file_path, false, false, log_errors);
if (!file_is_valid(f)) return {};
ArrayView<u8> file_data = read_entire_file(f);
ArrayView<u8> file_data = read_entire_file(f, add_null_terminator);
file_close(&f);
@ -1416,7 +1427,7 @@ bool Deserialize_ST_File_Enumeration (string file_path) {
push_allocator(temp());
auto_release_temp();
Deserializer deserializer = read_entire_file(file_path, true);
Deserializer deserializer = read_entire_file(file_path, false, true);
if (deserializer.count == 0) return false;
auto d = &deserializer;

View File

@ -41,8 +41,8 @@ internal bool file_length (File file, s64* length);
internal bool file_length (string file_path, s64* length);
internal s64 file_current_position (File file);
internal bool file_set_position (File file, s64 position);
internal ArrayView<u8> read_entire_file (File file);
internal ArrayView<u8> read_entire_file (string file_path, bool log_errors=false);
internal ArrayView<u8> read_entire_file (File file, bool add_null_terminator);
internal ArrayView<u8> read_entire_file (string file_path, bool add_null_terminator=false, bool log_errors=false);
// use to_byte_view to convert ArrayView<non-u8> to ArrayView<u8>
internal bool file_write (File* file, void* data, s64 length);

View File

@ -105,7 +105,9 @@ string directory_get_full_path (Thread_Group* group, Parent_Index pid, string di
}
// go in reverse order and add together string
String_Builder* sb = new_string_builder(Arena_Reserve::Size_64K);
String_Builder* sb = context_builder();
reset_string_builder(sb, true);
for (s64 i = paths.count-1; i >= 0; i -= 1) {
append(sb, paths[i]);
append(sb, "\\");

View File

@ -457,7 +457,7 @@ bool Deserialize_Win32_Drives (string file_path) {
push_allocator(temp());
auto_release_temp();
Deserializer deserializer = read_entire_file(file_path, true);
Deserializer deserializer = read_entire_file(file_path, false, true);
if (deserializer.count == 0) return false;
auto d = &deserializer;