Add printf-like log function backed by String_Builder.

This commit is contained in:
Musa Mahmood 2025-11-27 08:23:33 -05:00
parent 1a2de6e2f8
commit a29b67e524
13 changed files with 181 additions and 59 deletions

2
.gitignore vendored
View File

@ -3,6 +3,8 @@
## ##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
.stfolder/
# User-specific files # User-specific files
*.suo *.suo
*.user *.user

View File

@ -2,7 +2,7 @@
#include "Base.h" #include "Base.h"
#define ALLOCATOR_DEBUG_MODE 0 #define ALLOCATOR_DEBUG_MODE 1
#define ALLOCATOR_POISON_MEMORY_ON_ALLOCATION \ #define ALLOCATOR_POISON_MEMORY_ON_ALLOCATION \
(BUILD_DEBUG && ALLOCATOR_DEBUG_MODE) (BUILD_DEBUG && ALLOCATOR_DEBUG_MODE)
@ -27,7 +27,7 @@ struct Allocator {
void* data; void* data;
bool operator ! () { bool operator ! () {
return (proc == nullptr && data == nullptr); return (proc == nullptr);
} }
}; };

View File

@ -15,6 +15,7 @@ global Array<Arena*> arenas_in_flight[6];
#endif #endif
void initialize_arena_table () { void initialize_arena_table () {
// debug_break();
for (s32 i = 0; i < 6; i += 1) { for (s32 i = 0; i < 6; i += 1) {
arena_free_table[i].allocator = GPAllocator(); arena_free_table[i].allocator = GPAllocator();
array_reserve(arena_free_table[i], 64); array_reserve(arena_free_table[i], 64);

View File

@ -165,25 +165,27 @@ T pop (Array<T>& src) {
return result; return result;
} }
template <typename T, typename U> // template <typename T, typename U>
void array_add (Array<T>& src, U new_item) { // void array_add (Array<T>& src, U new_item) {
static_assert(sizeof(U) <= sizeof(T)); // static_assert(sizeof(U) <= sizeof(T));
auto new_count = src.count + 1; // auto new_count = src.count + 1;
array_maybe_grow(src); // array_maybe_grow(src);
T new_item_casted = (T)new_item; // T new_item_casted = (T)new_item;
src.count += 1; // src.count += 1;
memcpy(&src[src.count-1], &new_item_casted, sizeof(T)); // memcpy(&src[src.count-1], &new_item_casted, sizeof(T));
} // }
template <typename T> template <typename T>
void array_add (Array<T>& src, T new_item) { void array_add (Array<T>& src, T new_item) {
auto new_count = src.count + 1;
array_maybe_grow(src); array_maybe_grow(src);
src.data[src.count] = new_item;
src.count += 1; 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 <typename T> template <typename T>

View File

@ -1,7 +1,7 @@
// See Context_Base in jai, and TCTX in raddebugger: // See Context_Base in jai, and TCTX in raddebugger:
internal void Bootstrap_Main_Thread_Context () { internal void Bootstrap_Main_Thread_Context () {
// 0. Setup general MB(70) allocator // 0. Setup general allocator
GPAllocator_Initialize_Allocation_Tracker(); GPAllocator_Initialize_Allocation_Tracker();
// 1. Setup arena table // 1. Setup arena table
initialize_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->allocator = get_allocator(arena_ex);
thread_local_context->thread_idx = 0; thread_local_context->thread_idx = 0;
thread_local_context->thread_name = "Main Thread"; 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 { 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: // Thread-local allocators:
PROTOTYPING_API Allocator get_temp_allocator();
PROTOTYPING_API Allocator get_context_allocator();
// Start from w32_entry_point_caller -> // Start from w32_entry_point_caller ->
// see main_thread_base_entry_point // see main_thread_base_entry_point
@ -81,7 +65,11 @@ Thread_Context* get_thread_context () {
return (Thread_Context*)thread_local_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); return get_allocator(get_thread_context()->temp);
} }

View File

@ -8,7 +8,8 @@ struct Thread_Context {
s32 thread_idx; s32 thread_idx;
u16 _padding0; u16 _padding0;
u16 GPAllocator_alignment = 16; u16 GPAllocator_alignment = 16;
// Logger logger; Logger logger = {nullptr, nullptr};
String_Builder* log_builder;
// Stack_Trace* stack_trace; // Stack_Trace* stack_trace;
Array<Thread*> child_threads; // maybe should be linked-list? Array<Thread*> child_threads; // maybe should be linked-list?
@ -23,3 +24,18 @@ thread_static Thread_Context* thread_local_context;
Thread_Context* get_thread_context (); Thread_Context* get_thread_context ();
internal void Bootstrap_Main_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;
}
};

View File

@ -10,9 +10,12 @@
#define Aligned_Realloc(old_sz, ptr, sz, align) _aligned_realloc_dbg(ptr, sz, align, __FILE__, __LINE__) #define Aligned_Realloc(old_sz, ptr, sz, align) _aligned_realloc_dbg(ptr, sz, align, __FILE__, __LINE__)
#define Aligned_Free(ptr) _aligned_free_dbg(ptr) #define Aligned_Free(ptr) _aligned_free_dbg(ptr)
#else #else
#define Aligned_Alloc(sz, align) _aligned_malloc(sz, align) #define Aligned_Alloc(sz, align) std::malloc(sz)//_aligned_malloc(sz, align)
#define Aligned_Realloc(old_sz, ptr, sz, align) _aligned_realloc(ptr, sz, align) #define Aligned_Realloc(old_sz, ptr, sz, align) std::realloc(ptr, sz)//_aligned_realloc(ptr, sz, align)
#define Aligned_Free(ptr) _aligned_free(ptr) #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 #endif
#else // Non-MSVC (POSIX / GCC / Clang) #else // Non-MSVC (POSIX / GCC / Clang)
#include <cstdlib> // std::aligned_alloc #include <cstdlib> // 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_Realloc(old_sz, ptr, sz, align) gp_aligned_realloc(old_sz, ptr, sz, align)
#define Aligned_Free(ptr) std::free(ptr) #define Aligned_Free(ptr) std::free(ptr)
#endif #endif
struct Allocation { struct Allocation {
s64 size; s64 size;
void* memory; void* memory;

27
lib/Base/Logger.cpp Normal file
View File

@ -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);
}

View File

@ -4,7 +4,7 @@
// See Logger.jai in our jiim-dev-gui project for how to do fancy colored text. // See Logger.jai in our jiim-dev-gui project for how to do fancy colored text.
enum class Log_Level : s32 { enum class Log_Level : s32 {
TODO = -2, TODO = -2,
Trace = -1; Trace = -1,
None = 0, None = 0,
Info = 1, Info = 1,
Warning = 2, Warning = 2,
@ -12,5 +12,64 @@ enum class Log_Level : s32 {
Fatal_Error = 4, Fatal_Error = 4,
}; };
// log_function pointer // log_function pointer
typedef void (*Logger_Proc)(string log_message, ...); 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<Mutex>(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);

View File

@ -1,5 +1,3 @@
#include "String.h"
// #TODO #string module // #TODO #string module
// [ ] I'm debating if string type should automatically null-terminate. // [ ] 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. // 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<u8> str) {
return {str.count, str.data}; return {str.count, str.data};
} }
void free (string& s) { void string_free (string& s) {
internal_free(s.data); internal_free(s.data);
s.data = nullptr; 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. // Unfortunately this follows the printf format, which is annoying.
// I'd rather have something like fmt:: // 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, ...) { void print_to_builder (String_Builder* sb, string format, ...) {
s64 expected_final_count = sb->count + format.count + 4096; 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. // 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); poison_range(*sb, 0, sb->count);
reset_keeping_memory(*sb); 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) { force_inline string builder_to_string (String_Builder* sb) {
string final_string = copy_string(to_string(to_view(*sb))); string final_string = copy_string(to_string(to_view(*sb)));
free(sb); free_string_builder(sb);
return final_string; 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); arena_array_free(*sb);
} }

View File

@ -1,3 +1,4 @@
#pragma once
// #TODO: #strings: // #TODO: #strings:
// [ ] Always null-terminate strings! // [ ] Always null-terminate strings!
// [ ] How do I accept variadic arguments of any type to my print function? // [ ] 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<string> strings); void append (String_Builder* sb, ArrayView<string> strings);
internal force_inline void append_no_add (String_Builder* sb, string s); // for appending null terminators, does not increment count. 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, ...);
void print_to_builder (String_Builder* sb, string format, va_list args);
string string_view (String_Builder* sb); 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 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);

View File

@ -18,7 +18,10 @@
#include "lib/Base/Arena.h" #include "lib/Base/Arena.h"
#include "lib/Base/Arena_Array.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/String.cpp"
#include "lib/Base/ErrorType.cpp" #include "lib/Base/ErrorType.cpp"
#include "lib/Base/Arena_Table.cpp" #include "lib/Base/Arena_Table.cpp"
#include "lib/Base/Base_Thread_Context.h" #include "lib/Base/Base_Thread_Context.h"
@ -27,6 +30,7 @@
#include "lib/Base/Arena.cpp" #include "lib/Base/Arena.cpp"
#include "lib/Base/Base_Thread_Context.cpp" #include "lib/Base/Base_Thread_Context.cpp"
#include "lib/Base/Logger.cpp"
#include "lib/Base/Expandable_Arena.cpp" #include "lib/Base/Expandable_Arena.cpp"
#include "lib/Base/Allocator.cpp" #include "lib/Base/Allocator.cpp"
#include "lib/Base/General_Purpose_Allocator.cpp" #include "lib/Base/General_Purpose_Allocator.cpp"

View File

@ -189,13 +189,14 @@ string string_literal_example = "Hello, I am a string literal.";
internal void Main_Entry_Point (int argc, WCHAR **argv) { internal void Main_Entry_Point (int argc, WCHAR **argv) {
temp_reset(); temp_reset();
push_allocator(get_temp_allocator());
// tip: use auto_reset or auto_release with `get_thread_context()->arena` // tip: use auto_reset or auto_release with `get_thread_context()->arena`
debug_break(); // debug_break();
// String builder example: // String builder example:
// OK. I can work with this. // OK. I can work with this.
/*
auto sb = new_string_builder(Arena_Reserve::Size_64K); auto sb = new_string_builder(Arena_Reserve::Size_64K);
append(sb, "string_literal_example"); append(sb, "string_literal_example");
append(sb, " "); append(sb, " ");
@ -203,18 +204,23 @@ internal void Main_Entry_Point (int argc, WCHAR **argv) {
append(sb, " > "); append(sb, " > ");
print_to_builder(sb, "some size_t: %u", (u64)3982739867); print_to_builder(sb, "some size_t: %u", (u64)3982739867);
append(sb, "\n"); append(sb, "\n");
print_to_builder(sb, "the literal: %s", string_literal_example.data);
// auto result = string_view(sb); // string result = string_view(sb);
auto result = builder_to_string(sb); auto result = builder_to_string(sb); // also frees
printf((char*)result.data); // 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<s32>(6,7,8,9,10,51); print("Hello, I am just a printed message to stdout");
auto view = array_view_from_values<s32>(1,2,3,4,5);
*/ // free_string_builder(sb);
// ImGui_Application(); // ImGui_Application();
// #TODO: #Main - `Main_Entry_Point` // #TODO: #Main - `Main_Entry_Point`
// [ ] Setup Mouse and Keyboard Inputs // [ ] Setup Mouse and Keyboard Inputs
// [ ] Launch second thread // [ ] Launch second thread