Add printf-like log function backed by String_Builder.
This commit is contained in:
parent
1a2de6e2f8
commit
a29b67e524
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,6 +3,8 @@
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
.stfolder/
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
|
||||
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ global Array<Arena*> 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);
|
||||
|
||||
@ -165,25 +165,27 @@ T pop (Array<T>& src) {
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
void array_add (Array<T>& src, U new_item) {
|
||||
static_assert(sizeof(U) <= sizeof(T));
|
||||
auto new_count = src.count + 1;
|
||||
array_maybe_grow(src);
|
||||
// template <typename T, typename U>
|
||||
// void array_add (Array<T>& 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 <typename T>
|
||||
void array_add (Array<T>& 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 <typename T>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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<Thread*> 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;
|
||||
}
|
||||
};
|
||||
|
||||
@ -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 <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_Free(ptr) std::free(ptr)
|
||||
#endif
|
||||
|
||||
struct Allocation {
|
||||
s64 size;
|
||||
void* memory;
|
||||
|
||||
27
lib/Base/Logger.cpp
Normal file
27
lib/Base/Logger.cpp
Normal 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);
|
||||
}
|
||||
@ -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, ...);
|
||||
// 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<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);
|
||||
|
||||
|
||||
@ -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<u8> 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);
|
||||
}
|
||||
|
||||
@ -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<string> 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);
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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<s32>(6,7,8,9,10,51);
|
||||
auto view = array_view_from_values<s32>(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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user