From a29b67e5248b26eb4e1284fcdba03a450d07b071 Mon Sep 17 00:00:00 2001 From: Musa Mahmood Date: Thu, 27 Nov 2025 08:23:33 -0500 Subject: [PATCH] Add printf-like log function backed by String_Builder. --- .gitignore | 2 + lib/Base/Allocator.h | 4 +- lib/Base/Arena_Table.cpp | 1 + lib/Base/Array.h | 24 +++++----- lib/Base/Base_Thread_Context.cpp | 30 ++++--------- lib/Base/Base_Thread_Context.h | 18 +++++++- lib/Base/General_Purpose_Allocator.h | 10 +++-- lib/Base/Logger.cpp | 27 ++++++++++++ lib/Base/Logger.h | 65 ++++++++++++++++++++++++++-- lib/Base/String.cpp | 25 ++++++++--- lib/Base/String.h | 6 ++- lib_main.cpp | 4 ++ src/Base_Entry_Point.cpp | 24 ++++++---- 13 files changed, 181 insertions(+), 59 deletions(-) create mode 100644 lib/Base/Logger.cpp diff --git a/.gitignore b/.gitignore index 4fcbf54..ca72602 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +.stfolder/ + # User-specific files *.suo *.user diff --git a/lib/Base/Allocator.h b/lib/Base/Allocator.h index 17ee508..cc43e58 100644 --- a/lib/Base/Allocator.h +++ b/lib/Base/Allocator.h @@ -2,7 +2,7 @@ #include "Base.h" -#define ALLOCATOR_DEBUG_MODE 0 +#define ALLOCATOR_DEBUG_MODE 1 #define ALLOCATOR_POISON_MEMORY_ON_ALLOCATION \ (BUILD_DEBUG && ALLOCATOR_DEBUG_MODE) @@ -27,7 +27,7 @@ struct Allocator { void* data; bool operator ! () { - return (proc == nullptr && data == nullptr); + return (proc == nullptr); } }; diff --git a/lib/Base/Arena_Table.cpp b/lib/Base/Arena_Table.cpp index a23280b..efa50d8 100644 --- a/lib/Base/Arena_Table.cpp +++ b/lib/Base/Arena_Table.cpp @@ -15,6 +15,7 @@ global Array arenas_in_flight[6]; #endif void initialize_arena_table () { + // debug_break(); for (s32 i = 0; i < 6; i += 1) { arena_free_table[i].allocator = GPAllocator(); array_reserve(arena_free_table[i], 64); diff --git a/lib/Base/Array.h b/lib/Base/Array.h index 0504e27..ab73669 100644 --- a/lib/Base/Array.h +++ b/lib/Base/Array.h @@ -165,25 +165,27 @@ T pop (Array& src) { return result; } -template -void array_add (Array& src, U new_item) { - static_assert(sizeof(U) <= sizeof(T)); - auto new_count = src.count + 1; - array_maybe_grow(src); +// template +// void array_add (Array& src, U new_item) { +// static_assert(sizeof(U) <= sizeof(T)); +// auto new_count = src.count + 1; +// array_maybe_grow(src); - T new_item_casted = (T)new_item; +// T new_item_casted = (T)new_item; - src.count += 1; - memcpy(&src[src.count-1], &new_item_casted, sizeof(T)); -} +// src.count += 1; +// memcpy(&src[src.count-1], &new_item_casted, sizeof(T)); +// } template void array_add (Array& src, T new_item) { - auto new_count = src.count + 1; array_maybe_grow(src); + src.data[src.count] = new_item; + src.count += 1; - memcpy(&src[src.count-1], &new_item, sizeof(T)); + // auto dst_ptr = &src.data[src.count-1]; + // memcpy(dst_ptr, &new_item, sizeof(T)); } template diff --git a/lib/Base/Base_Thread_Context.cpp b/lib/Base/Base_Thread_Context.cpp index 790f0d2..b183daa 100644 --- a/lib/Base/Base_Thread_Context.cpp +++ b/lib/Base/Base_Thread_Context.cpp @@ -1,7 +1,7 @@ // See Context_Base in jai, and TCTX in raddebugger: internal void Bootstrap_Main_Thread_Context () { - // 0. Setup general MB(70) allocator + // 0. Setup general allocator GPAllocator_Initialize_Allocation_Tracker(); // 1. Setup arena table initialize_arena_table(); @@ -14,8 +14,10 @@ internal void Bootstrap_Main_Thread_Context () { thread_local_context->allocator = get_allocator(arena_ex); thread_local_context->thread_idx = 0; thread_local_context->thread_name = "Main Thread"; - // thread_local_context->logger = init_logger(); + thread_local_context->log_builder = new_string_builder(Arena_Reserve::Size_64M); + default_logger_initialize(); + thread_local_context->logger = {default_logger_proc, &default_logger}; } struct Push_Arena { @@ -43,25 +45,7 @@ struct Push_Arena { } }; -struct Push_Allocator { - Thread_Context* context; - Allocator old_allocator; - - Push_Allocator (Allocator new_allocator) { - context = get_thread_context(); - old_allocator = context->allocator; - context->allocator = new_allocator; - } - - ~Push_Allocator () { - context->allocator = old_allocator; - } -}; - - // Thread-local allocators: -PROTOTYPING_API Allocator get_temp_allocator(); -PROTOTYPING_API Allocator get_context_allocator(); // Start from w32_entry_point_caller -> // see main_thread_base_entry_point @@ -81,7 +65,11 @@ Thread_Context* get_thread_context () { return (Thread_Context*)thread_local_context; } -force_inline Allocator get_temp_allocator() { +Logger* get_context_logger () { + return &get_thread_context()->logger; +} + +force_inline Allocator get_temp_allocator () { return get_allocator(get_thread_context()->temp); } diff --git a/lib/Base/Base_Thread_Context.h b/lib/Base/Base_Thread_Context.h index 790ae16..e402075 100644 --- a/lib/Base/Base_Thread_Context.h +++ b/lib/Base/Base_Thread_Context.h @@ -8,7 +8,8 @@ struct Thread_Context { s32 thread_idx; u16 _padding0; u16 GPAllocator_alignment = 16; - // Logger logger; + Logger logger = {nullptr, nullptr}; + String_Builder* log_builder; // Stack_Trace* stack_trace; Array child_threads; // maybe should be linked-list? @@ -23,3 +24,18 @@ thread_static Thread_Context* thread_local_context; Thread_Context* get_thread_context (); internal void Bootstrap_Main_Thread_Context (); + +struct Push_Allocator { + Thread_Context* context; + Allocator old_allocator; + + Push_Allocator (Allocator new_allocator) { + context = get_thread_context(); + old_allocator = context->allocator; + context->allocator = new_allocator; + } + + ~Push_Allocator () { + context->allocator = old_allocator; + } +}; diff --git a/lib/Base/General_Purpose_Allocator.h b/lib/Base/General_Purpose_Allocator.h index e129c10..d49b671 100644 --- a/lib/Base/General_Purpose_Allocator.h +++ b/lib/Base/General_Purpose_Allocator.h @@ -10,9 +10,12 @@ #define Aligned_Realloc(old_sz, ptr, sz, align) _aligned_realloc_dbg(ptr, sz, align, __FILE__, __LINE__) #define Aligned_Free(ptr) _aligned_free_dbg(ptr) #else - #define Aligned_Alloc(sz, align) _aligned_malloc(sz, align) - #define Aligned_Realloc(old_sz, ptr, sz, align) _aligned_realloc(ptr, sz, align) - #define Aligned_Free(ptr) _aligned_free(ptr) + #define Aligned_Alloc(sz, align) std::malloc(sz)//_aligned_malloc(sz, align) + #define Aligned_Realloc(old_sz, ptr, sz, align) std::realloc(ptr, sz)//_aligned_realloc(ptr, sz, align) + #define Aligned_Free(ptr) std::free(ptr)//_aligned_free(ptr) + // #define Aligned_Alloc(sz, align) _aligned_malloc(sz, align) + // #define Aligned_Realloc(old_sz, ptr, sz, align) _aligned_realloc(ptr, sz, align) + // #define Aligned_Free(ptr) _aligned_free(ptr) #endif #else // Non-MSVC (POSIX / GCC / Clang) #include // std::aligned_alloc @@ -20,7 +23,6 @@ #define Aligned_Realloc(old_sz, ptr, sz, align) gp_aligned_realloc(old_sz, ptr, sz, align) #define Aligned_Free(ptr) std::free(ptr) #endif - struct Allocation { s64 size; void* memory; diff --git a/lib/Base/Logger.cpp b/lib/Base/Logger.cpp new file mode 100644 index 0000000..b1a8c6b --- /dev/null +++ b/lib/Base/Logger.cpp @@ -0,0 +1,27 @@ +void log (string fmt, ...) { + String_Builder* sb = get_thread_context()->log_builder; + + va_list args; + va_start(args, fmt); + print_to_builder(sb, fmt, args); + va_end(args); + + // Append newline if needed + string result = string_view(sb); + bool ends_with_newline = (result.data[result.count-1] == '\n'); + if (!ends_with_newline) { + append(sb, "\n"); + } + + string message = string_view(sb); + + Logger* logger = get_context_logger(); + logger->proc(message, Log_Level::None, logger->data); + + reset_string_builder(sb); +} + +void print (string message) { + Logger* logger = get_context_logger(); + logger->proc(message, Log_Level::None, logger->data); +} diff --git a/lib/Base/Logger.h b/lib/Base/Logger.h index 8f41526..60f60ca 100644 --- a/lib/Base/Logger.h +++ b/lib/Base/Logger.h @@ -4,7 +4,7 @@ // See Logger.jai in our jiim-dev-gui project for how to do fancy colored text. enum class Log_Level : s32 { TODO = -2, - Trace = -1; + Trace = -1, None = 0, Info = 1, Warning = 2, @@ -12,5 +12,64 @@ enum class Log_Level : s32 { Fatal_Error = 4, }; -// log_function pointer -typedef void (*Logger_Proc)(string log_message, ...); \ No newline at end of file +// log_function pointer +typedef void (*Logger_Proc)(string log_message, Log_Level level, void* data); +void default_logger_proc (string log_message, Log_Level level, void* data); //fwd declared + +struct Logger { + Logger_Proc proc; + void* data; +}; + +struct Default_Logger { + Logger_Proc proc = default_logger_proc; + String_Builder* sb = nullptr; + + Mutex* mutex; +#if OS_WINDOWS + void* windows_standard_output; + void* windows_standard_error; +#endif +}; + +global Default_Logger default_logger; + +// default_logger_proc()... +#if OS_WINDOWS + void os_write_string_unsynchronized(string s, bool to_standard_error) { + u32 written = 0; + void* handle = (to_standard_error) ? + default_logger.windows_standard_error : + default_logger.windows_standard_output; + Assert(handle != nullptr); + bool result = (bool)WriteFile(handle, s.data, (u32)s.count, (LPDWORD)&written, nullptr); + } +#endif + +void default_logger_proc (string log_message, Log_Level level, void* data) { + bool to_standard_error = level == Log_Level::Error || level == Log_Level::Fatal_Error; + + lock(default_logger.mutex); + os_write_string_unsynchronized(log_message, to_standard_error); + unlock(default_logger.mutex); +} + +void default_logger_initialize() { + // see: Bootstrap_Main_Thread_Context + default_logger.mutex = New(true); + mutex_init(default_logger.mutex); + +#if OS_WINDOWS + default_logger.windows_standard_output = GetStdHandle(STD_OUTPUT_HANDLE); + default_logger.windows_standard_error = GetStdHandle(STD_ERROR_HANDLE); +#endif +} + +// more hacky forward declares +Logger* get_context_logger (); +Allocator get_temp_allocator (); +Allocator get_context_allocator (); + +void log (string fmt, ...); +void print (string message); + diff --git a/lib/Base/String.cpp b/lib/Base/String.cpp index 1d77e5b..9b90cc9 100644 --- a/lib/Base/String.cpp +++ b/lib/Base/String.cpp @@ -1,5 +1,3 @@ -#include "String.h" - // #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. @@ -55,7 +53,7 @@ string to_string (ArrayView str) { return {str.count, str.data}; } -void free (string& s) { +void string_free (string& s) { internal_free(s.data); s.data = nullptr; @@ -160,6 +158,21 @@ force_inline void append_no_add (String_Builder* sb, string s) { // 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; @@ -185,7 +198,7 @@ string string_view (String_Builder* sb) { } // for when we want to keep the string builder around and recycle the memory. -internal force_inline void reset (String_Builder* sb) { +internal force_inline void reset_string_builder (String_Builder* sb) { poison_range(*sb, 0, sb->count); reset_keeping_memory(*sb); } @@ -193,11 +206,11 @@ internal force_inline void reset (String_Builder* sb) { force_inline string builder_to_string (String_Builder* sb) { string final_string = copy_string(to_string(to_view(*sb))); - free(sb); + free_string_builder(sb); return final_string; } -internal force_inline void free (String_Builder* sb) { +internal force_inline void free_string_builder (String_Builder* sb) { arena_array_free(*sb); } diff --git a/lib/Base/String.h b/lib/Base/String.h index 3ef0fac..2f852d6 100644 --- a/lib/Base/String.h +++ b/lib/Base/String.h @@ -1,3 +1,4 @@ +#pragma once // #TODO: #strings: // [ ] Always null-terminate strings! // [ ] How do I accept variadic arguments of any type to my print function? @@ -92,9 +93,10 @@ force_inline void append (String_Builder* sb, string s); void append (String_Builder* sb, ArrayView strings); internal force_inline void append_no_add (String_Builder* sb, string s); // for appending null terminators, does not increment count. void print_to_builder (String_Builder* sb, string format, ...); +void print_to_builder (String_Builder* sb, string format, va_list args); string string_view (String_Builder* sb); -internal force_inline void reset (String_Builder* sb); +internal force_inline void reset_string_builder (String_Builder* sb); force_inline string builder_to_string (String_Builder* sb); // returns string view -internal force_inline void free (String_Builder* sb); +internal force_inline void free_string_builder (String_Builder* sb); diff --git a/lib_main.cpp b/lib_main.cpp index 0521533..f6b8f7d 100644 --- a/lib_main.cpp +++ b/lib_main.cpp @@ -18,7 +18,10 @@ #include "lib/Base/Arena.h" #include "lib/Base/Arena_Array.h" +#include "lib/Base/String.h" +#include "lib/Base/Logger.h" #include "lib/Base/String.cpp" + #include "lib/Base/ErrorType.cpp" #include "lib/Base/Arena_Table.cpp" #include "lib/Base/Base_Thread_Context.h" @@ -27,6 +30,7 @@ #include "lib/Base/Arena.cpp" #include "lib/Base/Base_Thread_Context.cpp" +#include "lib/Base/Logger.cpp" #include "lib/Base/Expandable_Arena.cpp" #include "lib/Base/Allocator.cpp" #include "lib/Base/General_Purpose_Allocator.cpp" diff --git a/src/Base_Entry_Point.cpp b/src/Base_Entry_Point.cpp index f095b5a..d4b7a7f 100644 --- a/src/Base_Entry_Point.cpp +++ b/src/Base_Entry_Point.cpp @@ -189,13 +189,14 @@ string string_literal_example = "Hello, I am a string literal."; internal void Main_Entry_Point (int argc, WCHAR **argv) { temp_reset(); + push_allocator(get_temp_allocator()); // tip: use auto_reset or auto_release with `get_thread_context()->arena` - debug_break(); + // debug_break(); // String builder example: // OK. I can work with this. - /* + auto sb = new_string_builder(Arena_Reserve::Size_64K); append(sb, "string_literal_example"); append(sb, " "); @@ -203,18 +204,23 @@ internal void Main_Entry_Point (int argc, WCHAR **argv) { append(sb, " > "); print_to_builder(sb, "some size_t: %u", (u64)3982739867); append(sb, "\n"); + print_to_builder(sb, "the literal: %s", string_literal_example.data); - // auto result = string_view(sb); - auto result = builder_to_string(sb); - printf((char*)result.data); + // string result = string_view(sb); + auto result = builder_to_string(sb); // also frees + // print((char*)result.data); + log("Hello."); + log("log() should automatically append newlines to these prints."); + log("If it doesn't, I will be very upset."); + log(result); + log("Hello. There are %s things here\n", string_literal_example.data); - auto array = array_from_values(6,7,8,9,10,51); - auto view = array_view_from_values(1,2,3,4,5); - */ + print("Hello, I am just a printed message to stdout"); + + // free_string_builder(sb); // ImGui_Application(); - // #TODO: #Main - `Main_Entry_Point` // [ ] Setup Mouse and Keyboard Inputs // [ ] Launch second thread