Compare commits

..

No commits in common. "76cc1f278ca503ea99ac37414c88af9dd209147d" and "2740987956aa52eef8183f4d9284ff0358da1c9f" have entirely different histories.

17 changed files with 458 additions and 533 deletions

View File

@ -15,7 +15,6 @@
#endif #endif
#define DQN_ASAN_POISON 1 #define DQN_ASAN_POISON 1
#define DQN_ASAN_VET_POISON 1
#define DQN_NO_CHECK_BREAK #define DQN_NO_CHECK_BREAK
#define DQN_IMPLEMENTATION #define DQN_IMPLEMENTATION
#include "dqn.h" #include "dqn.h"

View File

@ -16,10 +16,7 @@ pushd Build
set common_flags=-D DQN_TEST_WITH_MAIN -I %script_dir% %script_dir%\Misc\dqn_unit_tests.cpp set common_flags=-D DQN_TEST_WITH_MAIN -I %script_dir% %script_dir%\Misc\dqn_unit_tests.cpp
set msvc_driver_flags=%common_flags% -MT -EHa -GR- -Od -Oi -Z7 -wd4201 -W4 -WX -nologo set msvc_driver_flags=%common_flags% -MT -EHa -GR- -Od -Oi -Z7 -wd4201 -W4 -WX -nologo
set msvc_compile_flags=%msvc_driver_flags% -fsanitize=address -analyze /Fe:dqn_unit_tests_msvc
REM Optionally pass `-analyze` to `msvc_compile_flags` for more checks, but,
REM it slows down compilation by around 5s on my old laptop.
set msvc_compile_flags=%msvc_driver_flags% -fsanitize=address /Fe:dqn_unit_tests_msvc
set clang_compile_flags=%msvc_driver_flags% -fsanitize=address -fsanitize=undefined /Fe:dqn_unit_tests_clang set clang_compile_flags=%msvc_driver_flags% -fsanitize=address -fsanitize=undefined /Fe:dqn_unit_tests_clang
set zig_compile_flags=%common_flags% -fsanitize=address -fsanitize=undefined -o dqn_unit_tests_zig set zig_compile_flags=%common_flags% -fsanitize=address -fsanitize=undefined -o dqn_unit_tests_zig

40
dqn.h
View File

@ -116,7 +116,6 @@
// [$TYPE] Types | | Basic types and typedefs // [$TYPE] Types | | Basic types and typedefs
// [$INTR] Intrinsics | | Atomics, cpuid, ticket mutex // [$INTR] Intrinsics | | Atomics, cpuid, ticket mutex
// [$TMUT] Dqn_TicketMutex | | Userland mutex via spinlocking atomics // [$TMUT] Dqn_TicketMutex | | Userland mutex via spinlocking atomics
// [$PRIN] Dqn_Print | | Console printing
// NOTE: Additional Configuration // NOTE: Additional Configuration
// - Override the default heap-allocation routine that is called when the // - Override the default heap-allocation routine that is called when the
@ -192,6 +191,10 @@
// [$ACAT] Dqn_ArenaCatalog | | Collate, create & manage arenas in a catalog // [$ACAT] Dqn_ArenaCatalog | | Collate, create & manage arenas in a catalog
#include "dqn_memory.h" #include "dqn_memory.h"
// NOTE: Dqn_Platform Print ========================================================================
// [$PRIN] Dqn_Print | | Console printing
#include "dqn_platform_print.h"
// NOTE: Dqn_Debug ================================================================================= // NOTE: Dqn_Debug =================================================================================
// [$DEBM] Debug Macros | | // [$DEBM] Debug Macros | |
// [$CALL] Dqn_CallSite | | Source code location/tracing // [$CALL] Dqn_CallSite | | Source code location/tracing
@ -216,10 +219,8 @@
// //
// DQN_DUMP_STACK_TRACE // DQN_DUMP_STACK_TRACE
// //
// - Define this macro to enable emory leak tracking when requesting memory // - Enable memory leak tracking when requesting memory from the OS via this
// from the OS via this library. For example calls to Dqn_VMem_Reserve or // library. For example calls to Dqn_VMem_Reserve or DQN_ALLOC are recorded.
// DQN_ALLOC are recorded to the leak table.
//
// Allocations are stored in a global hash-table and their respective stack // Allocations are stored in a global hash-table and their respective stack
// traces for the allocation location. Memory leaks can be dumped at the end // traces for the allocation location. Memory leaks can be dumped at the end
// of the program or some epoch by calling Dqn_Library_DumpLeaks() // of the program or some epoch by calling Dqn_Library_DumpLeaks()
@ -229,28 +230,6 @@
// flag. // flag.
// //
// DQN_LEAK_TRACING // DQN_LEAK_TRACING
//
// - Define this macro to 1 to enable poisoning of memory from arenas when ASAN
// `-fsanitize=address` is enabled. Enabling this will detect memory overwrite
// by padding allocated before and after with poisoned memory which will raise
// a use-after-poison in ASAN on read/write. This is a no-op if the library is
// not compiled with ASAN.
//
// DQN_ASAN_POISON 1
//
// - Define this macro 1 to enable sanity checks for manually poisoned memory in
// this library when ASAN `-fsanitize=address` is enabled. These sanity checks
// ensure that memory from arenas are correctly un/poisoned when pointers are
// allocated and returned to the memory arena's. This is a no-op if we are not
// compiled with ASAN or `DQN_ASAN_POISON` is not set to `1`.
//
// DQN_ASAN_VET_POISON 1
//
// - Define this macro to the size of the guard memory reserved before and after
// allocations made that are poisoned to protect against out-of-bounds memory
// accesses. By default the library sets the guard to 128 bytes.
//
// DQN_ASAN_POISON_GUARD_SIZE 128
// NOTE: Dqn_Strings =============================================================================== // NOTE: Dqn_Strings ===============================================================================
// [$CSTR] Dqn_CString8 | | C-string helpers // [$CSTR] Dqn_CString8 | | C-string helpers
@ -284,6 +263,12 @@
// and padded to atleast DQN_ASAN_POISON_ALIGNMENT (e.g. 8 bytes). // and padded to atleast DQN_ASAN_POISON_ALIGNMENT (e.g. 8 bytes).
// //
// DQN_ASAN_POISON 1 // DQN_ASAN_POISON 1
//
// - Define this macro to record allocation stats for arenas used in the
// thread context. The thread context arena stats can be printed by using
// Dqn_Library_DumpThreadContextArenaStat.
//
// DQN_DEBUG_THREAD_CONTEXT
#include "dqn_platform.h" #include "dqn_platform.h"
@ -317,6 +302,7 @@
#include "dqn_base.cpp" #include "dqn_base.cpp"
#include "dqn_external.cpp" #include "dqn_external.cpp"
#include "dqn_memory.cpp" #include "dqn_memory.cpp"
#include "dqn_platform_print.cpp"
#include "dqn_debug.cpp" #include "dqn_debug.cpp"
#include "dqn_strings.cpp" #include "dqn_strings.cpp"
#include "dqn_containers.cpp" #include "dqn_containers.cpp"

View File

@ -60,195 +60,3 @@ DQN_API bool Dqn_TicketMutex_CanLock(Dqn_TicketMutex const *mutex, Dqn_uint tick
#undef _CRT_SECURE_NO_WARNINGS #undef _CRT_SECURE_NO_WARNINGS
#endif #endif
#endif #endif
// NOTE: [$PRIN] Dqn_Print =========================================================================
DQN_API Dqn_PrintStyle Dqn_Print_StyleColour(uint8_t r, uint8_t g, uint8_t b, Dqn_PrintBold bold)
{
Dqn_PrintStyle result = {};
result.bold = bold;
result.colour = true;
result.r = r;
result.g = g;
result.b = b;
return result;
}
DQN_API Dqn_PrintStyle Dqn_Print_StyleColourU32(uint32_t rgb, Dqn_PrintBold bold)
{
uint8_t r = (rgb >> 24) & 0xFF;
uint8_t g = (rgb >> 16) & 0xFF;
uint8_t b = (rgb >> 8) & 0xFF;
Dqn_PrintStyle result = Dqn_Print_StyleColour(r, g, b, bold);
return result;
}
DQN_API Dqn_PrintStyle Dqn_Print_StyleBold()
{
Dqn_PrintStyle result = {};
result.bold = Dqn_PrintBold_Yes;
return result;
}
DQN_API void Dqn_Print_Std(Dqn_PrintStd std_handle, Dqn_String8 string)
{
DQN_ASSERT(std_handle == Dqn_PrintStd_Out || std_handle == Dqn_PrintStd_Err);
#if defined(DQN_OS_WIN32)
// NOTE: Get the output handles from kernel ====================================================
DQN_THREAD_LOCAL void *std_out_print_handle = nullptr;
DQN_THREAD_LOCAL void *std_err_print_handle = nullptr;
DQN_THREAD_LOCAL bool std_out_print_to_console = false;
DQN_THREAD_LOCAL bool std_err_print_to_console = false;
if (!std_out_print_handle) {
unsigned long mode = 0; (void)mode;
std_out_print_handle = GetStdHandle(STD_OUTPUT_HANDLE);
std_out_print_to_console = GetConsoleMode(std_out_print_handle, &mode) != 0;
std_err_print_handle = GetStdHandle(STD_ERROR_HANDLE);
std_err_print_to_console = GetConsoleMode(std_err_print_handle, &mode) != 0;
}
// NOTE: Select the output handle ==============================================================
void *print_handle = std_out_print_handle;
bool print_to_console = std_out_print_to_console;
if (std_handle == Dqn_PrintStd_Err) {
print_handle = std_err_print_handle;
print_to_console = std_err_print_to_console;
}
// NOTE: Write the string ======================================================================
DQN_ASSERT(string.size < DQN_CAST(unsigned long)-1);
unsigned long bytes_written = 0; (void)bytes_written;
if (print_to_console) {
WriteConsoleA(print_handle, string.data, DQN_CAST(unsigned long)string.size, &bytes_written, nullptr);
} else {
WriteFile(print_handle, string.data, DQN_CAST(unsigned long)string.size, &bytes_written, nullptr);
}
#else
fprintf(std_handle == Dqn_PrintStd_Out ? stdout : stderr, "%.*s", DQN_STRING_FMT(string));
#endif
}
DQN_API void Dqn_Print_StdStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string)
{
if (string.data && string.size) {
if (style.colour)
Dqn_Print_Std(std_handle, Dqn_Print_ESCColourFgString(style.r, style.g, style.b));
if (style.bold == Dqn_PrintBold_Yes)
Dqn_Print_Std(std_handle, Dqn_Print_ESCBoldString);
Dqn_Print_Std(std_handle, string);
if (style.colour || style.bold == Dqn_PrintBold_Yes)
Dqn_Print_Std(std_handle, Dqn_Print_ESCResetString);
}
}
DQN_FILE_SCOPE char *Dqn_Print_VSPrintfChunker_(const char *buf, void *user, int len)
{
Dqn_String8 string = {};
string.data = DQN_CAST(char *)buf;
string.size = len;
Dqn_PrintStd std_handle = DQN_CAST(Dqn_PrintStd)DQN_CAST(uintptr_t)user;
Dqn_Print_Std(std_handle, string);
return (char *)buf;
}
DQN_API void Dqn_Print_StdF(Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
Dqn_Print_StdFV(std_handle, fmt, args);
va_end(args);
}
DQN_API void Dqn_Print_StdFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
Dqn_Print_StdFVStyle(std_handle, style, fmt, args);
va_end(args);
}
DQN_API void Dqn_Print_StdFV(Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args)
{
char buffer[STB_SPRINTF_MIN];
STB_SPRINTF_DECORATE(vsprintfcb)(Dqn_Print_VSPrintfChunker_, DQN_CAST(void *)DQN_CAST(uintptr_t)std_handle, buffer, fmt, args);
}
DQN_API void Dqn_Print_StdFVStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args)
{
if (fmt) {
if (style.colour)
Dqn_Print_Std(std_handle, Dqn_Print_ESCColourFgString(style.r, style.g, style.b));
if (style.bold == Dqn_PrintBold_Yes)
Dqn_Print_Std(std_handle, Dqn_Print_ESCBoldString);
Dqn_Print_StdFV(std_handle, fmt, args);
if (style.colour || style.bold == Dqn_PrintBold_Yes)
Dqn_Print_Std(std_handle, Dqn_Print_ESCResetString);
}
}
DQN_API void Dqn_Print_StdLn(Dqn_PrintStd std_handle, Dqn_String8 string)
{
Dqn_Print_Std(std_handle, string);
Dqn_Print_Std(std_handle, DQN_STRING8("\n"));
}
DQN_API void Dqn_Print_StdLnF(Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
Dqn_Print_StdLnFV(std_handle, fmt, args);
va_end(args);
}
DQN_API void Dqn_Print_StdLnFV(Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args)
{
Dqn_Print_StdFV(std_handle, fmt, args);
Dqn_Print_Std(std_handle, DQN_STRING8("\n"));
}
DQN_API void Dqn_Print_StdLnStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string)
{
Dqn_Print_StdStyle(std_handle, style, string);
Dqn_Print_Std(std_handle, DQN_STRING8("\n"));
}
DQN_API void Dqn_Print_StdLnFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
Dqn_Print_StdLnFVStyle(std_handle, style, fmt, args);
va_end(args);
}
DQN_API void Dqn_Print_StdLnFVStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args)
{
Dqn_Print_StdFVStyle(std_handle, style, fmt, args);
Dqn_Print_Std(std_handle, DQN_STRING8("\n"));
}
DQN_API Dqn_String8 Dqn_Print_ESCColourString(Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b)
{
DQN_THREAD_LOCAL char buffer[32];
buffer[0] = 0;
Dqn_String8 result = {};
result.size = STB_SPRINTF_DECORATE(snprintf)(buffer,
DQN_ARRAY_UCOUNT(buffer),
"\x1b[%d;2;%u;%u;%um",
colour == Dqn_PrintESCColour_Fg ? 38 : 48,
r, g, b);
result.data = buffer;
return result;
}
DQN_API Dqn_String8 Dqn_Print_ESCColourU32String(Dqn_PrintESCColour colour, uint32_t value)
{
uint8_t r = DQN_CAST(uint8_t)(value >> 24);
uint8_t g = DQN_CAST(uint8_t)(value >> 16);
uint8_t b = DQN_CAST(uint8_t)(value >> 8);
Dqn_String8 result = Dqn_Print_ESCColourString(colour, r, g, b);
return result;
}

View File

@ -290,21 +290,24 @@ typedef int32_t Dqn_b32;
#define DQN_ISIZE_MAX INTPTR_MAX #define DQN_ISIZE_MAX INTPTR_MAX
#define DQN_ISIZE_MIN INTPTR_MIN #define DQN_ISIZE_MIN INTPTR_MIN
enum Dqn_ZeroMem typedef enum Dqn_ZeroMem
{ {
Dqn_ZeroMem_No, // Memory can be handed out without zero-ing it out Dqn_ZeroMem_No, // Memory can be handed out without zero-ing it out
Dqn_ZeroMem_Yes, // Memory should be zero-ed out before giving to the callee Dqn_ZeroMem_Yes, // Memory should be zero-ed out before giving to the callee
}; }
Dqn_ZeroMem;
struct Dqn_String8 struct Dqn_String8
{ {
char *data; // The bytes of the string char *data; // The bytes of the string
Dqn_usize size; // The number of bytes in the string Dqn_usize size; // The number of bytes in the string
#if defined(__cplusplus)
char const *begin() const { return data; } char const *begin() const { return data; }
char const *end () const { return data + size; } char const *end () const { return data + size; }
char *begin() { return data; } char *begin() { return data; }
char *end () { return data + size; } char *end () { return data + size; }
#endif
}; };
// NOTE: [$INTR] Intrinsics ======================================================================== // NOTE: [$INTR] Intrinsics ========================================================================
@ -436,8 +439,8 @@ Dqn_CPUIDRegisters Dqn_CPUID(int function_id);
struct Dqn_TicketMutex struct Dqn_TicketMutex
{ {
unsigned int volatile ticket; // The next ticket to give out to the thread taking the mutex unsigned int volatile ticket; ///< The next ticket to give out to the thread taking the mutex
unsigned int volatile serving; // The ticket ID to block the mutex on until it is returned unsigned int volatile serving; ///< The ticket ID to block the mutex on until it is returned
}; };
void Dqn_TicketMutex_Begin (Dqn_TicketMutex *mutex); void Dqn_TicketMutex_Begin (Dqn_TicketMutex *mutex);
@ -445,105 +448,3 @@ void Dqn_TicketMutex_End (Dqn_TicketMutex *mutex);
Dqn_uint Dqn_TicketMutex_MakeTicket (Dqn_TicketMutex *mutex); Dqn_uint Dqn_TicketMutex_MakeTicket (Dqn_TicketMutex *mutex);
void Dqn_TicketMutex_BeginTicket(Dqn_TicketMutex const *mutex, Dqn_uint ticket); void Dqn_TicketMutex_BeginTicket(Dqn_TicketMutex const *mutex, Dqn_uint ticket);
bool Dqn_TicketMutex_CanLock (Dqn_TicketMutex const *mutex, Dqn_uint ticket); bool Dqn_TicketMutex_CanLock (Dqn_TicketMutex const *mutex, Dqn_uint ticket);
// NOTE: [$PRIN] Dqn_Print =========================================================================
enum Dqn_PrintStd
{
Dqn_PrintStd_Out,
Dqn_PrintStd_Err,
};
enum Dqn_PrintBold
{
Dqn_PrintBold_No,
Dqn_PrintBold_Yes,
};
struct Dqn_PrintStyle
{
Dqn_PrintBold bold;
bool colour;
uint8_t r, g, b;
};
enum Dqn_PrintESCColour
{
Dqn_PrintESCColour_Fg,
Dqn_PrintESCColour_Bg,
};
// NOTE: Print Style ===============================================================================
DQN_API Dqn_PrintStyle Dqn_Print_StyleColour (uint8_t r, uint8_t g, uint8_t b, Dqn_PrintBold bold);
DQN_API Dqn_PrintStyle Dqn_Print_StyleColourU32 (uint32_t rgb, Dqn_PrintBold bold);
DQN_API Dqn_PrintStyle Dqn_Print_StyleBold ();
// NOTE: Print Standard Out ========================================================================
#define Dqn_Print(string) Dqn_Print_Std(Dqn_PrintStd_Out, string)
#define Dqn_Print_F(fmt, ...) Dqn_Print_StdF(Dqn_PrintStd_Out, fmt, ## __VA_ARGS__)
#define Dqn_Print_FV(fmt, args) Dqn_Print_StdFV(Dqn_PrintStd_Out, fmt, args)
#define Dqn_Print_Style(style, string) Dqn_Print_StdStyle(Dqn_PrintStd_Out, style, string)
#define Dqn_Print_FStyle(style, fmt, ...) Dqn_Print_StdFStyle(Dqn_PrintStd_Out, style, fmt, ## __VA_ARGS__)
#define Dqn_Print_FVStyle(style, fmt, args, ...) Dqn_Print_StdFVStyle(Dqn_PrintStd_Out, style, fmt, args)
#define Dqn_Print_Ln(string) Dqn_Print_StdLn(Dqn_PrintStd_Out, string)
#define Dqn_Print_LnF(fmt, ...) Dqn_Print_StdLnF(Dqn_PrintStd_Out, fmt, ## __VA_ARGS__)
#define Dqn_Print_LnFV(fmt, args) Dqn_Print_StdLnFV(Dqn_PrintStd_Out, fmt, args)
#define Dqn_Print_LnStyle(style, string) Dqn_Print_StdLnStyle(Dqn_PrintStd_Out, style, string);
#define Dqn_Print_LnFStyle(style, fmt, ...) Dqn_Print_StdLnFStyle(Dqn_PrintStd_Out, style, fmt, ## __VA_ARGS__);
#define Dqn_Print_LnFVStyle(style, fmt, args) Dqn_Print_StdLnFVStyle(Dqn_PrintStd_Out, style, fmt, args);
#define Dqn_Print_Err(string) Dqn_Print_Std(Dqn_PrintStd_Err, string)
#define Dqn_Print_ErrF(fmt, ...) Dqn_Print_StdF(Dqn_PrintStd_Err, fmt, ## __VA_ARGS__)
#define Dqn_Print_ErrFV(fmt, args) Dqn_Print_StdFV(Dqn_PrintStd_Err, fmt, args)
#define Dqn_Print_ErrStyle(style, string) Dqn_Print_StdStyle(Dqn_PrintStd_Err, style, string)
#define Dqn_Print_ErrFStyle(style, fmt, ...) Dqn_Print_StdFStyle(Dqn_PrintStd_Err, style, fmt, ## __VA_ARGS__)
#define Dqn_Print_ErrFVStyle(style, fmt, args, ...) Dqn_Print_StdFVStyle(Dqn_PrintStd_Err, style, fmt, args)
#define Dqn_Print_ErrLn(string) Dqn_Print_StdLn(Dqn_PrintStd_Err, string)
#define Dqn_Print_ErrLnF(fmt, ...) Dqn_Print_StdLnF(Dqn_PrintStd_Err, fmt, ## __VA_ARGS__)
#define Dqn_Print_ErrLnFV(fmt, args) Dqn_Print_StdLnFV(Dqn_PrintStd_Err, fmt, args)
#define Dqn_Print_ErrLnStyle(style, string) Dqn_Print_StdLnStyle(Dqn_PrintStd_Err, style, string);
#define Dqn_Print_ErrLnFStyle(style, fmt, ...) Dqn_Print_StdLnFStyle(Dqn_PrintStd_Err, style, fmt, ## __VA_ARGS__);
#define Dqn_Print_ErrLnFVStyle(style, fmt, args) Dqn_Print_StdLnFVStyle(Dqn_PrintStd_Err, style, fmt, args);
// NOTE: Print =====================================================================================
DQN_API void Dqn_Print_Std (Dqn_PrintStd std_handle, Dqn_String8 string);
DQN_API void Dqn_Print_StdF (Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, ...);
DQN_API void Dqn_Print_StdFV (Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args);
DQN_API void Dqn_Print_StdStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string);
DQN_API void Dqn_Print_StdFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, ...);
DQN_API void Dqn_Print_StdFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args);
DQN_API void Dqn_Print_StdLn (Dqn_PrintStd std_handle, Dqn_String8 string);
DQN_API void Dqn_Print_StdLnF (Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, ...);
DQN_API void Dqn_Print_StdLnFV (Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args);
DQN_API void Dqn_Print_StdLnStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string);
DQN_API void Dqn_Print_StdLnFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, ...);
DQN_API void Dqn_Print_StdLnFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args);
// NOTE: ANSI Formatting Codes =====================================================================
Dqn_String8 Dqn_Print_ESCColourString (Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b);
Dqn_String8 Dqn_Print_ESCColourU32String(Dqn_PrintESCColour colour, uint32_t value);
#define Dqn_Print_ESCColourFgString(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Fg, r, g, b)
#define Dqn_Print_ESCColourBgString(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Bg, r, g, b)
#define Dqn_Print_ESCColourFg(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Fg, r, g, b).data
#define Dqn_Print_ESCColourBg(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Bg, r, g, b).data
#define Dqn_Print_ESCColourFgU32String(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Fg, value)
#define Dqn_Print_ESCColourBgU32String(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Bg, value)
#define Dqn_Print_ESCColourFgU32(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Fg, value).data
#define Dqn_Print_ESCColourBgU32(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Bg, value).data
#define Dqn_Print_ESCReset "\x1b[0m"
#define Dqn_Print_ESCBold "\x1b[1m"
#define Dqn_Print_ESCResetString DQN_STRING8(Dqn_Print_ESCReset)
#define Dqn_Print_ESCBoldString DQN_STRING8(Dqn_Print_ESCBold)

View File

@ -1,30 +1,3 @@
// NOTE: [$ASAN] Dqn_Asan ========================================================================== ===
void Dqn_ASAN_PoisonMemoryRegion(void const volatile *ptr, Dqn_usize size)
{
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
__asan_poison_memory_region(ptr, size);
if (DQN_ASAN_VET_POISON) {
DQN_HARD_ASSERT(__asan_address_is_poisoned(ptr));
DQN_HARD_ASSERT(__asan_address_is_poisoned((char *)ptr + (size - 1)));
}
#else
(void)ptr; (void)size;
#endif
}
void Dqn_ASAN_UnpoisonMemoryRegion(void const volatile *ptr, Dqn_usize size)
{
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
__asan_unpoison_memory_region(ptr, size);
if (DQN_ASAN_VET_POISON) {
DQN_HARD_ASSERT(__asan_region_is_poisoned((void *)ptr, size) == 0);
}
#else
(void)ptr; (void)size;
#endif
}
// NOTE: [$DEBG] Dqn_Debug =========================================================================
DQN_API Dqn_String8 Dqn_Debug_CleanStackTrace(Dqn_String8 stack_trace) DQN_API Dqn_String8 Dqn_Debug_CleanStackTrace(Dqn_String8 stack_trace)
{ {
// NOTE: Remove the stacktrace's library invocations from the stack trace // NOTE: Remove the stacktrace's library invocations from the stack trace
@ -191,7 +164,7 @@ DQN_API void Dqn_Debug_DumpLeaks()
Dqn_Log_WarningF("There were %I64u leaked allocations totalling %_$$I64u", leak_count, leaked_bytes); Dqn_Log_WarningF("There were %I64u leaked allocations totalling %_$$I64u", leak_count, leaked_bytes);
} }
} }
#endif // defined(DQN_LEAK_TRACING) #endif /// defined(DQN_LEAK_TRACING)
// NOTE: [$LLOG] Dqn_Log ========================================================================== // NOTE: [$LLOG] Dqn_Log ==========================================================================
DQN_API Dqn_String8 Dqn_Log_MakeString(Dqn_Allocator allocator, DQN_API Dqn_String8 Dqn_Log_MakeString(Dqn_Allocator allocator,
@ -247,7 +220,8 @@ DQN_API Dqn_String8 Dqn_Log_MakeString(Dqn_Allocator allocator,
header_size_no_ansi_codes = header.size - colour_esc.size - Dqn_Print_ESCResetString.size; header_size_no_ansi_codes = header.size - colour_esc.size - Dqn_Print_ESCResetString.size;
} }
// NOTE: Header padding ======================================================================== // NOTE: Header padding
// =========================================================================
Dqn_usize header_padding = 0; Dqn_usize header_padding = 0;
{ {
DQN_LOCAL_PERSIST Dqn_usize max_header_length = 0; DQN_LOCAL_PERSIST Dqn_usize max_header_length = 0;
@ -255,7 +229,8 @@ DQN_API Dqn_String8 Dqn_Log_MakeString(Dqn_Allocator allocator,
header_padding = max_header_length - header_size_no_ansi_codes; header_padding = max_header_length - header_size_no_ansi_codes;
} }
// NOTE: Construct final log =================================================================== // NOTE: Construct final log
// =========================================================================
Dqn_String8 user_msg = Dqn_String8_InitFV(allocator, fmt, args); Dqn_String8 user_msg = Dqn_String8_InitFV(allocator, fmt, args);
Dqn_String8 result = Dqn_String8_Allocate(allocator, header.size + header_padding + user_msg.size, Dqn_ZeroMem_No); Dqn_String8 result = Dqn_String8_Allocate(allocator, header.size + header_padding + user_msg.size, Dqn_ZeroMem_No);
DQN_MEMCPY(result.data, header.data, header.size); DQN_MEMCPY(result.data, header.data, header.size);
@ -266,23 +241,27 @@ DQN_API Dqn_String8 Dqn_Log_MakeString(Dqn_Allocator allocator,
DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_String8 type, int log_type, void *user_data, Dqn_CallSite call_site, char const *fmt, va_list args) DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_String8 type, int log_type, void *user_data, Dqn_CallSite call_site, char const *fmt, va_list args)
{ {
Dqn_Library *lib = g_dqn_library;
(void)log_type; (void)log_type;
(void)user_data; (void)user_data;
// NOTE: Open log file for appending if requested ========================== // NOTE: Open log file for appending if requested ==========================
Dqn_TicketMutex_Begin(&lib->log_file_mutex); Dqn_TicketMutex_Begin(&g_dqn_library->log_file_mutex);
if (lib->log_to_file && !lib->log_file.handle && lib->log_file.error_size == 0) { if (g_dqn_library->log_to_file && !g_dqn_library->log_file) {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_String8 log_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/dqn.log", DQN_STRING_FMT(lib->exe_dir)); #if (defined(DQN_OS_WIN32) && !defined(DQN_NO_WIN)) || !defined(DQN_OS_WIN32)
lib->log_file = Dqn_Fs_OpenFile(log_path, Dqn_FsFileOpen_CreateAlways, Dqn_FsFileAccess_AppendOnly); Dqn_String8 exe_dir = Dqn_OS_EXEDir(scratch.arena);
#else
Dqn_String8 exe_dir = DQN_STRING8(".");
#endif
Dqn_String8 log_file = Dqn_String8_InitF(scratch.allocator, "%.*s/dqn.log", DQN_STRING_FMT(exe_dir));
fopen_s(DQN_CAST(FILE **)&g_dqn_library->log_file, log_file.data, "a");
} }
Dqn_TicketMutex_End(&lib->log_file_mutex); Dqn_TicketMutex_End(&g_dqn_library->log_file_mutex);
// NOTE: Generate the log header =========================================== // NOTE: Generate the log header ===========================================
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_String8 log_line = Dqn_Log_MakeString(scratch.allocator, Dqn_String8 log_line = Dqn_Log_MakeString(scratch.allocator,
!lib->log_no_colour, !g_dqn_library->log_no_colour,
type, type,
log_type, log_type,
call_site, call_site,
@ -292,10 +271,11 @@ DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_String8 type, int log_type, void *use
// NOTE: Print log ========================================================= // NOTE: Print log =========================================================
Dqn_Print_StdLn(Dqn_PrintStd_Out, log_line); Dqn_Print_StdLn(Dqn_PrintStd_Out, log_line);
Dqn_TicketMutex_Begin(&lib->log_file_mutex); Dqn_TicketMutex_Begin(&g_dqn_library->log_file_mutex);
Dqn_Fs_WriteFile(&lib->log_file, log_line); if (g_dqn_library->log_to_file && g_dqn_library->log_file) {
Dqn_Fs_WriteFile(&lib->log_file, DQN_STRING8("\n")); fprintf(DQN_CAST(FILE *)g_dqn_library->log_file, "%.*s\n", DQN_STRING_FMT(log_line));
Dqn_TicketMutex_End(&lib->log_file_mutex); }
Dqn_TicketMutex_End(&g_dqn_library->log_file_mutex);
} }
DQN_API void Dqn_Log_FVCallSite(Dqn_String8 type, Dqn_CallSite call_site, char const *fmt, va_list args) DQN_API void Dqn_Log_FVCallSite(Dqn_String8 type, Dqn_CallSite call_site, char const *fmt, va_list args)

View File

@ -34,31 +34,10 @@
#define DQN_ASAN_POISON 0 #define DQN_ASAN_POISON 0
#endif #endif
#if !defined(DQN_ASAN_POISON_VET) #if !defined(DQN_ASAN_POISON_ALIGNMENT)
#define DQN_ASAN_POISON_VET 0
#endif
#define DQN_ASAN_POISON_ALIGNMENT 8 #define DQN_ASAN_POISON_ALIGNMENT 8
#if !defined(DQN_ASAN_POISON_GUARD_SIZE)
#define DQN_ASAN_POISON_GUARD_SIZE 128
#endif
static_assert(Dqn_IsPowerOfTwoAligned(DQN_ASAN_POISON_GUARD_SIZE, DQN_ASAN_POISON_ALIGNMENT),
"ASAN poison guard size must be a power-of-two and aligned to ASAN's alignment"
"requirement (8 bytes)");
// NOTE: MSVC does not support the feature detection macro for instance so we
// compile it out
#if !defined(__has_feature)
#define __has_feature(x) 0
#endif #endif
// NOTE: [$ASAN] Dqn_Asan ==========================================================================
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
#include <sanitizer/asan_interface.h>
#endif
void Dqn_ASAN_PoisonMemoryRegion(void const volatile *ptr, Dqn_usize size);
void Dqn_ASAN_UnpoisonMemoryRegion(void const volatile *ptr, Dqn_usize size);
// NOTE: [$CALL] Dqn_CallSite ====================================================================== // NOTE: [$CALL] Dqn_CallSite ======================================================================
struct Dqn_CallSite struct Dqn_CallSite

View File

@ -141,10 +141,6 @@ static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uin
return (stbsp__uint32)(sn - s); return (stbsp__uint32)(sn - s);
} }
#if defined(__clang__)
__attribute__((no_sanitize("undefined")))
#endif
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va)
{ {
static char hex[] = "0123456789abcdefxp"; static char hex[] = "0123456789abcdefxp";

View File

@ -10,6 +10,8 @@
#include "b_stacktrace.h" #include "b_stacktrace.h"
// NOTE: [$OS_H] OS Headers ======================================================================== // NOTE: [$OS_H] OS Headers ========================================================================
#include <sanitizer/asan_interface.h>
#if defined(DQN_OS_WIN32) #if defined(DQN_OS_WIN32)
#pragma comment(lib, "bcrypt") #pragma comment(lib, "bcrypt")
#pragma comment(lib, "wininet") #pragma comment(lib, "wininet")

View File

@ -230,14 +230,18 @@ DQN_API uint64_t Dqn_Bin_HexBufferToU64(char const *hex, Dqn_usize size)
DQN_ASSERT(DQN_CAST(Dqn_usize)(trim_size * 4 / 8) /*maximum amount of bytes represented in the hex string*/ <= sizeof(uint64_t)); DQN_ASSERT(DQN_CAST(Dqn_usize)(trim_size * 4 / 8) /*maximum amount of bytes represented in the hex string*/ <= sizeof(uint64_t));
uint64_t result = 0; uint64_t result = 0;
Dqn_usize bits_written = 0;
Dqn_usize max_size = DQN_MIN(size, 8 /*bytes*/ * 2 /*hex chars per byte*/); Dqn_usize max_size = DQN_MIN(size, 8 /*bytes*/ * 2 /*hex chars per byte*/);
for (Dqn_usize hex_index = 0; hex_index < max_size; hex_index++) { for (Dqn_usize hex_index = 0; hex_index < max_size; hex_index++, bits_written += 4) {
char ch = trim_hex[hex_index]; char ch = trim_hex[hex_index];
if (!Dqn_Char_IsHex(ch)) if (!Dqn_Char_IsHex(ch))
break; break;
uint8_t val = Dqn_Char_HexToU8(ch); uint8_t val = Dqn_Char_HexToU8(ch);
result = (result << 4) | val; Dqn_usize bit_shift = 60 - bits_written;
result |= (DQN_CAST(uint64_t)val << bit_shift);
} }
result >>= (64 - bits_written); // Shift the remainder digits to the end.
return result; return result;
} }
@ -790,17 +794,24 @@ DQN_API Dqn_Library *Dqn_Library_Init()
g_dqn_library = &default_instance; g_dqn_library = &default_instance;
} }
// NOTE: Init check ===========================================================================
Dqn_Library *result = g_dqn_library; Dqn_Library *result = g_dqn_library;
Dqn_TicketMutex_Begin(&result->lib_mutex); Dqn_TicketMutex_Begin(&result->lib_mutex);
DQN_DEFER { Dqn_TicketMutex_End(&result->lib_mutex); }; DQN_DEFER { Dqn_TicketMutex_End(&result->lib_mutex); };
if (result->lib_init) if (result->lib_init)
return result; return result;
// NOTE: Query OS page size ==================================================================== // =============================================================================================
{ Dqn_ArenaCatalog_Init(&result->arena_catalog, &result->arena_catalog_backup_arena);
result->lib_init = true;
#if !defined(DQN_NO_PROFILER)
result->profiler = &result->profiler_default_instance;
#endif
// =============================================================================================
{ // NOTE: Query OS page size
SYSTEM_INFO system_info = {}; SYSTEM_INFO system_info = {};
#if defined(DQN_OS_WIN32) #if defined(DQN_OS_WIN32)
GetSystemInfo(&system_info); GetSystemInfo(&system_info);
@ -813,17 +824,7 @@ DQN_API Dqn_Library *Dqn_Library_Init()
#endif #endif
} }
// NOTE Initialise fields ====================================================================== // =============================================================================================
#if !defined(DQN_NO_PROFILER)
result->profiler = &result->profiler_default_instance;
#endif
result->lib_init = true;
Dqn_ArenaCatalog_Init(&result->arena_catalog, &result->arena);
result->exe_dir = Dqn_OS_EXEDir(&result->arena);
// NOTE: Leak tracing ==========================================================================
#if defined(DQN_LEAK_TRACING) // NOTE: Initialise the allocation leak tracker #if defined(DQN_LEAK_TRACING) // NOTE: Initialise the allocation leak tracker
{ {
@ -839,25 +840,6 @@ DQN_API Dqn_Library *Dqn_Library_Init()
} }
#endif #endif
// NOTE: Print out init features ===============================================================
Dqn_Log_DebugF("Dqn Library initialised:\n");
Dqn_Print_StdLnF(Dqn_PrintStd_Err, " OS Page Size/Alloc Granularity: %$$_I32u/%$$_I32u", result->os_page_size, result->os_alloc_granularity);
if (DQN_ASAN_POISON) {
Dqn_Print_StdLnF(Dqn_PrintStd_Err, " ASAN manual poisoning%s", DQN_ASAN_VET_POISON ? " (+vet sanity checks)" : "");
Dqn_Print_StdLnF(Dqn_PrintStd_Err, " ASAN poison guard size: %$$_I32u", DQN_ASAN_POISON_GUARD_SIZE);
}
#if defined(DQN_LEAK_TRACING)
Dqn_Print_StdLnF(Dqn_PrintStd_Err, " Allocation leak tracing");
#endif
#if !defined(DQN_NO_PROFILER)
Dqn_Print_StdLnF(Dqn_PrintStd_Err, " TSC profiler available");
#endif
Dqn_Print_StdLnF(Dqn_PrintStd_Err, "");
return result; return result;
} }
@ -881,6 +863,14 @@ DQN_API void Dqn_Library_SetLogCallback(Dqn_LogProc *proc, void *user_data)
g_dqn_library->log_user_data = user_data; g_dqn_library->log_user_data = user_data;
} }
DQN_API void Dqn_Library_SetLogFile(FILE *file)
{
Dqn_TicketMutex_Begin(&g_dqn_library->log_file_mutex);
g_dqn_library->log_file = file;
g_dqn_library->log_to_file = file ? true : false;
Dqn_TicketMutex_End(&g_dqn_library->log_file_mutex);
}
DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_String8 file_path) DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_String8 file_path)
{ {
#if defined(DQN_DEBUG_THREAD_CONTEXT) #if defined(DQN_DEBUG_THREAD_CONTEXT)

View File

@ -587,19 +587,19 @@ void Dqn_Profiler_SwapAnchorBuffer (uint32_t anchor_count);
struct Dqn_Library struct Dqn_Library
{ {
bool lib_init; // True if the library has been initialised via `Dqn_Library_Init` bool lib_init;
Dqn_TicketMutex lib_mutex; Dqn_TicketMutex lib_mutex;
Dqn_String8 exe_dir; // The directory of the current executable
Dqn_Arena arena; Dqn_LogProc *log_callback; ///< Set this pointer to override the logging routine
Dqn_ArenaCatalog arena_catalog;
Dqn_LogProc *log_callback; // Set this pointer to override the logging routine
void * log_user_data; void * log_user_data;
bool log_to_file; // Output logs to file as well as standard out bool log_to_file; ///< Output logs to file as well as standard out
Dqn_FsFile log_file; // TODO(dqn): Hmmm, how should we do this... ? void * log_file; ///< TODO(dqn): Hmmm, how should we do this... ?
Dqn_TicketMutex log_file_mutex; // Is locked when instantiating the log_file for the first time Dqn_TicketMutex log_file_mutex; ///< Is locked when instantiating the log_file for the first time
bool log_no_colour; // Disable colours in the logging output bool log_no_colour; ///< Disable colours in the logging output
/// The backup arena to use if no arena is passed into Dqn_Library_Init
Dqn_Arena arena_catalog_backup_arena;
Dqn_ArenaCatalog arena_catalog;
// NOTE: Leak Tracing ========================================================================== // NOTE: Leak Tracing ==========================================================================
@ -627,6 +627,14 @@ struct Dqn_Library
uint32_t os_page_size; uint32_t os_page_size;
uint32_t os_alloc_granularity; uint32_t os_alloc_granularity;
// NOTE: Thread Context ========================================================================
#if defined(DQN_DEBUG_THREAD_CONTEXT)
Dqn_TicketMutex thread_context_mutex;
Dqn_ArenaStat thread_context_arena_stats[256];
uint8_t thread_context_arena_stats_count;
#endif
// NOTE: Profiler ============================================================================== // NOTE: Profiler ==============================================================================
#if !defined(DQN_NO_PROFILER) #if !defined(DQN_NO_PROFILER)
@ -642,5 +650,6 @@ DQN_API void Dqn_Library_SetPointer (Dqn_Library *library
DQN_API void Dqn_Library_SetProfiler (Dqn_Profiler *profiler); DQN_API void Dqn_Library_SetProfiler (Dqn_Profiler *profiler);
#endif #endif
DQN_API void Dqn_Library_SetLogCallback (Dqn_LogProc *proc, void *user_data); DQN_API void Dqn_Library_SetLogCallback (Dqn_LogProc *proc, void *user_data);
DQN_API void Dqn_Library_SetLogFile (void *file);
DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_String8 file_path); DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_String8 file_path);

View File

@ -1,4 +1,6 @@
// NOTE: [$ALLO] Dqn_Allocator ===================================================================== // NOTE: [$ALLO] Dqn_Allocator =====================================================================
#include <ios>
#include <sanitizer/asan_interface.h>
DQN_API void *Dqn_Allocator_Alloc(Dqn_Allocator allocator, size_t size, uint8_t align, Dqn_ZeroMem zero_mem) DQN_API void *Dqn_Allocator_Alloc(Dqn_Allocator allocator, size_t size, uint8_t align, Dqn_ZeroMem zero_mem)
{ {
void *result = NULL; void *result = NULL;
@ -179,7 +181,7 @@ DQN_API Dqn_MemBlockSizeRequiredResult Dqn_MemBlock_SizeRequired(Dqn_MemBlock co
// is always guarded with poison-ed memory to prevent read/writes behind // is always guarded with poison-ed memory to prevent read/writes behind
// the block of memory. // the block of memory.
if ((block_flags & Dqn_MemBlockFlag_AllocsAreContiguous) == 0) { if ((block_flags & Dqn_MemBlockFlag_AllocsAreContiguous) == 0) {
result.alloc_size = Dqn_AlignUpPowerOfTwo(size + DQN_ASAN_POISON_GUARD_SIZE, DQN_ASAN_POISON_ALIGNMENT); result.alloc_size = Dqn_AlignUpPowerOfTwo(size + g_dqn_library->os_page_size, DQN_ASAN_POISON_ALIGNMENT);
} }
ptr_alignment = DQN_MAX(alignment, DQN_ASAN_POISON_ALIGNMENT); ptr_alignment = DQN_MAX(alignment, DQN_ASAN_POISON_ALIGNMENT);
} }
@ -199,7 +201,7 @@ DQN_API Dqn_MemBlockSizeRequiredResult Dqn_MemBlock_SizeRequired(Dqn_MemBlock co
Dqn_usize Dqn_MemBlock_MetadataSize() Dqn_usize Dqn_MemBlock_MetadataSize()
{ {
Dqn_usize init_poison_page = DQN_ASAN_POISON ? DQN_ASAN_POISON_GUARD_SIZE : 0; Dqn_usize init_poison_page = DQN_ASAN_POISON ? g_dqn_library->os_page_size : 0;
Dqn_usize poison_alignment = DQN_ASAN_POISON ? DQN_ASAN_POISON_ALIGNMENT : 0; Dqn_usize poison_alignment = DQN_ASAN_POISON ? DQN_ASAN_POISON_ALIGNMENT : 0;
Dqn_usize result = Dqn_AlignUpPowerOfTwo(sizeof(Dqn_MemBlock), poison_alignment) + init_poison_page; Dqn_usize result = Dqn_AlignUpPowerOfTwo(sizeof(Dqn_MemBlock), poison_alignment) + init_poison_page;
return result; return result;
@ -232,16 +234,11 @@ DQN_API Dqn_MemBlock *Dqn_MemBlock_Init(Dqn_usize reserve, Dqn_usize commit, uin
result->commit = commit_aligned - metadata_size; result->commit = commit_aligned - metadata_size;
result->flags = DQN_CAST(uint8_t)flags; result->flags = DQN_CAST(uint8_t)flags;
// NOTE: Poison (guard page + commit). We do *not* poison the entire if (DQN_ASAN_POISON) { // NOTE: Poison (guard page + entire block), we unpoison as we allocate
// block, only the commit pages. Since we may reserve large amounts of
// space vs commit we'd waste time marking those pages as poisoned as
// reads or writes outside of committed pages will page fault.
if (DQN_ASAN_POISON) {
DQN_ASSERT(Dqn_IsPowerOfTwoAligned(result->data, DQN_ASAN_POISON_ALIGNMENT)); DQN_ASSERT(Dqn_IsPowerOfTwoAligned(result->data, DQN_ASAN_POISON_ALIGNMENT));
DQN_ASSERT(Dqn_IsPowerOfTwoAligned(result->size, DQN_ASAN_POISON_ALIGNMENT)); DQN_ASSERT(Dqn_IsPowerOfTwoAligned(result->size, DQN_ASAN_POISON_ALIGNMENT));
void *poison_ptr = DQN_CAST(void *)Dqn_AlignUpPowerOfTwo(DQN_CAST(char *)result + sizeof(Dqn_MemBlock), DQN_ASAN_POISON_ALIGNMENT); void *poison_ptr = DQN_CAST(void *)Dqn_AlignUpPowerOfTwo(DQN_CAST(char *)result + sizeof(Dqn_MemBlock), DQN_ASAN_POISON_ALIGNMENT);
Dqn_usize bytes_to_poison = DQN_ASAN_POISON_GUARD_SIZE + result->commit; ASAN_POISON_MEMORY_REGION(poison_ptr, g_dqn_library->os_page_size + result->size);
Dqn_ASAN_PoisonMemoryRegion(poison_ptr, bytes_to_poison);
} }
} }
return result; return result;
@ -264,8 +261,9 @@ DQN_API void *Dqn_MemBlock_Alloc(Dqn_MemBlock *block, Dqn_usize size, uint8_t al
block->used = new_used; block->used = new_used;
DQN_ASSERT(Dqn_IsPowerOfTwoAligned(result, alignment)); DQN_ASSERT(Dqn_IsPowerOfTwoAligned(result, alignment));
if (DQN_ASAN_POISON) if (DQN_ASAN_POISON) {
Dqn_ASAN_UnpoisonMemoryRegion(result, size); ASAN_UNPOISON_MEMORY_REGION(result, size);
}
if (zero_mem == Dqn_ZeroMem_Yes) { if (zero_mem == Dqn_ZeroMem_Yes) {
Dqn_usize reused_bytes = DQN_MIN(block->commit - size_required.data_offset, size); Dqn_usize reused_bytes = DQN_MIN(block->commit - size_required.data_offset, size);
@ -278,12 +276,6 @@ DQN_API void *Dqn_MemBlock_Alloc(Dqn_MemBlock *block, Dqn_usize size, uint8_t al
block->commit += commit_size; block->commit += commit_size;
Dqn_VMem_Commit(commit_ptr, commit_size, Dqn_VMemPage_ReadWrite); Dqn_VMem_Commit(commit_ptr, commit_size, Dqn_VMemPage_ReadWrite);
DQN_ASSERT(block->commit <= block->size); DQN_ASSERT(block->commit <= block->size);
if (DQN_ASAN_POISON) { // NOTE: Poison newly committed pages that aren't being used.
void *poison_ptr = DQN_CAST(char *)block->data + block->used;
Dqn_usize bytes_to_poison = block->commit - block->used;
Dqn_ASAN_PoisonMemoryRegion(poison_ptr, bytes_to_poison);
}
} }
return result; return result;
@ -294,8 +286,9 @@ DQN_API void Dqn_MemBlock_Free(Dqn_MemBlock *block)
if (!block) if (!block)
return; return;
Dqn_usize release_size = block->size + Dqn_MemBlock_MetadataSize(); Dqn_usize release_size = block->size + Dqn_MemBlock_MetadataSize();
if (DQN_ASAN_POISON) if (DQN_ASAN_POISON) {
Dqn_ASAN_UnpoisonMemoryRegion(block, release_size); ASAN_UNPOISON_MEMORY_REGION(block, release_size);
}
Dqn_VMem_Release(block, release_size); Dqn_VMem_Release(block, release_size);
} }
@ -314,10 +307,13 @@ DQN_API void Dqn_MemBlock_PopTo(Dqn_MemBlock *block, Dqn_usize to)
return; return;
if (DQN_ASAN_POISON) { if (DQN_ASAN_POISON) {
void *poison_ptr = DQN_CAST(char *)block->data + to; // TODO(doyle): The poison API takes addresses that are 8 byte aligned
void *end_ptr = DQN_CAST(void *)Dqn_AlignUpPowerOfTwo((DQN_CAST(uintptr_t)block->data + block->used), DQN_ASAN_POISON_ALIGNMENT); // so there are gaps here if we are dealing with objects that aren't 8
// byte aligned unfortunately.
void *poison_ptr = DQN_CAST(void *)Dqn_AlignUpPowerOfTwo(DQN_CAST(char *)block->data + to, DQN_ASAN_POISON_ALIGNMENT);
void *end_ptr = DQN_CAST(char *)block->data + block->used;
uintptr_t bytes_to_poison = DQN_CAST(uintptr_t)end_ptr - DQN_CAST(uintptr_t)poison_ptr; uintptr_t bytes_to_poison = DQN_CAST(uintptr_t)end_ptr - DQN_CAST(uintptr_t)poison_ptr;
Dqn_ASAN_PoisonMemoryRegion(poison_ptr, bytes_to_poison); ASAN_POISON_MEMORY_REGION(poison_ptr, bytes_to_poison);
} }
block->used = to; block->used = to;
} }

View File

@ -295,6 +295,9 @@ struct Dqn_ArenaTempMemoryScope
~Dqn_ArenaTempMemoryScope(); ~Dqn_ArenaTempMemoryScope();
Dqn_ArenaTempMemory temp_memory; Dqn_ArenaTempMemory temp_memory;
bool cancel = false; bool cancel = false;
#if defined(DQN_LEAK_TRACING)
Dqn_CallSite leak_site_;
#endif
}; };
enum Dqn_ArenaCommit enum Dqn_ArenaCommit

View File

@ -645,15 +645,15 @@ DQN_API Dqn_FsFile Dqn_Fs_OpenFile(Dqn_String8 path, Dqn_FsFileOpen open_mode, u
return result; return result;
} }
DQN_API bool Dqn_Fs_WriteFileBuffer(Dqn_FsFile *file, void const *buffer, Dqn_usize size) DQN_API bool Dqn_Fs_WriteFile(Dqn_FsFile *file, char const *buffer, Dqn_usize size)
{ {
if (!file || !file->handle || !buffer || size <= 0 || file->error_size) if (!file || !file->handle || !buffer || size <= 0 || file->error_size)
return false; return false;
bool result = true; bool result = true;
#if defined(DQN_OS_WIN32) #if defined(DQN_OS_WIN32)
char const *end = DQN_CAST(char *)buffer + size; char const *end = buffer + size;
for (char const *ptr = DQN_CAST(char const *)buffer; result && ptr != end; ) { for (char const *ptr = buffer; result && ptr != end; ) {
unsigned long write_size = DQN_CAST(unsigned long)DQN_MIN((unsigned long)-1, end - ptr); unsigned long write_size = DQN_CAST(unsigned long)DQN_MIN((unsigned long)-1, end - ptr);
unsigned long bytes_written = 0; unsigned long bytes_written = 0;
result = WriteFile(file->handle, ptr, write_size, &bytes_written, nullptr /*lpOverlapped*/) != 0; result = WriteFile(file->handle, ptr, write_size, &bytes_written, nullptr /*lpOverlapped*/) != 0;
@ -675,32 +675,6 @@ DQN_API bool Dqn_Fs_WriteFileBuffer(Dqn_FsFile *file, void const *buffer, Dqn_us
return result; return result;
} }
DQN_API bool Dqn_Fs_WriteFile(Dqn_FsFile *file, Dqn_String8 buffer)
{
bool result = Dqn_Fs_WriteFileBuffer(file, buffer.data, buffer.size);
return result;
}
DQN_API bool Dqn_Fs_WriteFileFV(Dqn_FsFile *file, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args)
{
bool result = false;
if (!file || !fmt)
return result;
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_String8 buffer = Dqn_String8_InitFV(scratch.allocator, fmt, args);
result = Dqn_Fs_WriteFileBuffer(file, buffer.data, buffer.size);
return result;
}
DQN_API bool Dqn_Fs_WriteFileF(Dqn_FsFile *file, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
bool result = Dqn_Fs_WriteFileFV(file, fmt, args);
va_end(args);
return result;
}
DQN_API void Dqn_Fs_CloseFile(Dqn_FsFile *file) DQN_API void Dqn_Fs_CloseFile(Dqn_FsFile *file)
{ {
if (!file || !file->handle || file->error_size) if (!file || !file->handle || file->error_size)
@ -1928,14 +1902,17 @@ DQN_API uint64_t Dqn_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_tsc_f
// NOTE: [$TCTX] Dqn_ThreadContext ================================================================= // NOTE: [$TCTX] Dqn_ThreadContext =================================================================
Dqn_ThreadScratch::Dqn_ThreadScratch(Dqn_ThreadContext *context, uint8_t context_index) Dqn_ThreadScratch::Dqn_ThreadScratch(Dqn_ThreadContext *context, uint8_t context_index)
{ {
allocator = context->scratch_allocators[context_index]; index = context_index;
arena = context->scratch_arenas[context_index]; allocator = context->temp_allocators[index];
arena = context->temp_arenas[index];
temp_memory = Dqn_Arena_BeginTempMemory(arena); temp_memory = Dqn_Arena_BeginTempMemory(arena);
destructed = false;
} }
Dqn_ThreadScratch::~Dqn_ThreadScratch() Dqn_ThreadScratch::~Dqn_ThreadScratch()
{ {
#if defined(DQN_DEBUG_THREAD_CONTEXT)
temp_arenas_stat[index] = arena->stats;
#endif
DQN_ASSERT(destructed == false); DQN_ASSERT(destructed == false);
Dqn_Arena_EndTempMemory(temp_memory, /*cancel*/ false); Dqn_Arena_EndTempMemory(temp_memory, /*cancel*/ false);
destructed = true; destructed = true;
@ -1957,18 +1934,26 @@ DQN_API Dqn_ThreadContext *Dqn_Thread_GetContext()
DQN_THREAD_LOCAL Dqn_ThreadContext result = {}; DQN_THREAD_LOCAL Dqn_ThreadContext result = {};
if (!result.init) { if (!result.init) {
result.init = true; result.init = true;
Dqn_ArenaCatalog *catalog = &g_dqn_library->arena_catalog;
DQN_ASSERTF(g_dqn_library->lib_init, "Library must be initialised by calling Dqn_Library_Init(nullptr)"); DQN_ASSERTF(g_dqn_library->lib_init, "Library must be initialised by calling Dqn_Library_Init(nullptr)");
// NOTE: Setup scratch arenas // NOTE: Setup permanent arena
for (uint8_t index = 0; index < DQN_ARRAY_UCOUNT(result.scratch_arenas); index++) { Dqn_ArenaCatalog *catalog = &g_dqn_library->arena_catalog;
result.scratch_arenas[index] = Dqn_ArenaCatalog_AllocF(catalog, result.allocator = Dqn_Arena_Allocator(result.arena);
result.arena = Dqn_ArenaCatalog_AllocF(catalog,
DQN_GIGABYTES(1) /*size*/, DQN_GIGABYTES(1) /*size*/,
DQN_KILOBYTES(64) /*commit*/, DQN_KILOBYTES(64) /*commit*/,
"Thread %u Scratch Arena %u", "Thread %u Arena",
Dqn_Thread_GetID());
// NOTE: Setup temporary arenas
for (uint8_t index = 0; index < DQN_THREAD_CONTEXT_ARENAS; index++) {
result.temp_arenas[index] = Dqn_ArenaCatalog_AllocF(catalog,
DQN_GIGABYTES(1) /*size*/,
DQN_KILOBYTES(64) /*commit*/,
"Thread %u Temp Arena %u",
Dqn_Thread_GetID(), Dqn_Thread_GetID(),
index); index);
result.scratch_allocators[index] = Dqn_Arena_Allocator(result.scratch_arenas[index]); result.temp_allocators[index] = Dqn_Arena_Allocator(result.temp_arenas[index]);
} }
} }
return &result; return &result;
@ -1978,10 +1963,11 @@ DQN_API Dqn_ThreadContext *Dqn_Thread_GetContext()
// manually pass it in? // manually pass it in?
DQN_API Dqn_ThreadScratch Dqn_Thread_GetScratch(void const *conflict_arena) DQN_API Dqn_ThreadScratch Dqn_Thread_GetScratch(void const *conflict_arena)
{ {
static_assert(DQN_THREAD_CONTEXT_ARENAS < (uint8_t)-1, "We use UINT8_MAX as a sentinel value");
Dqn_ThreadContext *context = Dqn_Thread_GetContext(); Dqn_ThreadContext *context = Dqn_Thread_GetContext();
uint8_t context_index = (uint8_t)-1; uint8_t context_index = (uint8_t)-1;
for (uint8_t index = 0; index < DQN_ARRAY_UCOUNT(context->scratch_arenas); index++) { for (uint8_t index = 0; index < DQN_THREAD_CONTEXT_ARENAS; index++) {
Dqn_Arena *arena = context->scratch_arenas[index]; Dqn_Arena *arena = context->temp_arenas[index];
if (!conflict_arena || arena != conflict_arena) { if (!conflict_arena || arena != conflict_arena) {
context_index = index; context_index = index;
break; break;
@ -1991,3 +1977,4 @@ DQN_API Dqn_ThreadScratch Dqn_Thread_GetScratch(void const *conflict_arena)
DQN_ASSERT(context_index != (uint8_t)-1); DQN_ASSERT(context_index != (uint8_t)-1);
return Dqn_ThreadScratch(context, context_index); return Dqn_ThreadScratch(context, context_index);
} }

View File

@ -70,9 +70,9 @@ struct Dqn_FsFile
enum Dqn_FsFileOpen enum Dqn_FsFileOpen
{ {
Dqn_FsFileOpen_CreateAlways, // Create file if it does not exist, otherwise, zero out the file and open Dqn_FsFileOpen_CreateAlways, ///< Create file if it does not exist, otherwise, zero out the file and open
Dqn_FsFileOpen_OpenIfExist, // Open file at path only if it exists Dqn_FsFileOpen_OpenIfExist, ///< Open file at path only if it exists
Dqn_FsFileOpen_OpenAlways, // Open file at path, create file if it does not exist Dqn_FsFileOpen_OpenAlways, ///< Open file at path, create file if it does not exist
}; };
enum Dqn_FsFileAccess enum Dqn_FsFileAccess
@ -80,16 +80,13 @@ enum Dqn_FsFileAccess
Dqn_FsFileAccess_Read = 1 << 0, Dqn_FsFileAccess_Read = 1 << 0,
Dqn_FsFileAccess_Write = 1 << 1, Dqn_FsFileAccess_Write = 1 << 1,
Dqn_FsFileAccess_Execute = 1 << 2, Dqn_FsFileAccess_Execute = 1 << 2,
Dqn_FsFileAccess_AppendOnly = 1 << 3, // This flag cannot be combined with any other access mode Dqn_FsFileAccess_AppendOnly = 1 << 3, ///< This flag cannot be combined with any other acess mode
Dqn_FsFileAccess_ReadWrite = Dqn_FsFileAccess_Read | Dqn_FsFileAccess_Write, Dqn_FsFileAccess_ReadWrite = Dqn_FsFileAccess_Read | Dqn_FsFileAccess_Write,
Dqn_FsFileAccess_All = Dqn_FsFileAccess_ReadWrite | Dqn_FsFileAccess_Execute, Dqn_FsFileAccess_All = Dqn_FsFileAccess_ReadWrite | Dqn_FsFileAccess_Execute,
}; };
DQN_API Dqn_FsFile Dqn_Fs_OpenFile (Dqn_String8 path, Dqn_FsFileOpen open_mode, uint32_t access); DQN_API Dqn_FsFile Dqn_Fs_OpenFile (Dqn_String8 path, Dqn_FsFileOpen open_mode, uint32_t access);
DQN_API bool Dqn_Fs_WriteFileBuffer(Dqn_FsFile *file, void const *data, Dqn_usize size); DQN_API bool Dqn_Fs_WriteFile(Dqn_FsFile *file, char const *buffer, Dqn_usize size);
DQN_API bool Dqn_Fs_WriteFile (Dqn_FsFile *file, Dqn_String8 buffer);
DQN_API bool Dqn_Fs_WriteFileFV (Dqn_FsFile *file, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args);
DQN_API bool Dqn_Fs_WriteFileF (Dqn_FsFile *file, DQN_FMT_STRING_ANNOTATE char const *fmt, ...);
DQN_API void Dqn_Fs_CloseFile(Dqn_FsFile *file); DQN_API void Dqn_Fs_CloseFile(Dqn_FsFile *file);
#endif // !defined(DQN_NO_FS) #endif // !defined(DQN_NO_FS)
@ -462,16 +459,27 @@ DQN_API uint64_t Dqn_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_ts
// @param[in] conflict_arena A pointer to the arena currently being used in the // @param[in] conflict_arena A pointer to the arena currently being used in the
// function // function
#if !defined(DQN_THREAD_CONTEXT_ARENAS)
#define DQN_THREAD_CONTEXT_ARENAS 2
#endif
struct Dqn_ThreadContext struct Dqn_ThreadContext
{ {
Dqn_b32 init; Dqn_b32 init;
// Scratch memory arena's for the calling thread Dqn_Arena *arena; ///< Per thread arena
Dqn_Arena *scratch_arenas[2]; Dqn_Allocator allocator; ///< Allocator that uses the arena
// Allocators that use the corresponding arena from the thread context. /// Temp memory arena's for the calling thread
// Provided for convenience when interfacing with allocator interfaces. Dqn_Arena *temp_arenas[DQN_THREAD_CONTEXT_ARENAS];
Dqn_Allocator scratch_allocators[2];
/// Allocators that use the corresponding arena from the thread context.
/// Provided for convenience when interfacing with allocator interfaces.
Dqn_Allocator temp_allocators[DQN_THREAD_CONTEXT_ARENAS];
#if defined(DQN_DEBUG_THREAD_CONTEXT)
Dqn_ArenaStat temp_arenas_stat[DQN_THREAD_CONTEXT_ARENAS];
#endif
}; };
struct Dqn_ThreadScratch struct Dqn_ThreadScratch
@ -479,10 +487,16 @@ struct Dqn_ThreadScratch
Dqn_ThreadScratch(Dqn_ThreadContext *context, uint8_t context_index); Dqn_ThreadScratch(Dqn_ThreadContext *context, uint8_t context_index);
~Dqn_ThreadScratch(); ~Dqn_ThreadScratch();
/// Index into the arena/allocator/stat array in the thread context
/// specifying what arena was assigned.
uint8_t index;
Dqn_Allocator allocator; Dqn_Allocator allocator;
Dqn_Arena *arena; Dqn_Arena *arena;
Dqn_b32 destructed; Dqn_b32 destructed = false; /// Detect copies of the scratch
Dqn_ArenaTempMemory temp_memory; Dqn_ArenaTempMemory temp_memory;
#if defined(DQN_LEAK_TRACING)
Dqn_CallSite leak_site__;
#endif
}; };
// NOTE: Context =================================================================================== // NOTE: Context ===================================================================================

194
dqn_platform_print.cpp Normal file
View File

@ -0,0 +1,194 @@
// NOTE: [$PRIN] Dqn_Print =========================================================================
DQN_API Dqn_PrintStyle Dqn_Print_StyleColour(uint8_t r, uint8_t g, uint8_t b, Dqn_PrintBold bold)
{
Dqn_PrintStyle result = {};
result.bold = bold;
result.colour = true;
result.r = r;
result.g = g;
result.b = b;
return result;
}
DQN_API Dqn_PrintStyle Dqn_Print_StyleColourU32(uint32_t rgb, Dqn_PrintBold bold)
{
uint8_t r = (rgb >> 24) & 0xFF;
uint8_t g = (rgb >> 16) & 0xFF;
uint8_t b = (rgb >> 8) & 0xFF;
Dqn_PrintStyle result = Dqn_Print_StyleColour(r, g, b, bold);
return result;
}
DQN_API Dqn_PrintStyle Dqn_Print_StyleBold()
{
Dqn_PrintStyle result = {};
result.bold = Dqn_PrintBold_Yes;
return result;
}
DQN_API void Dqn_Print_Std(Dqn_PrintStd std_handle, Dqn_String8 string)
{
DQN_ASSERT(std_handle == Dqn_PrintStd_Out || std_handle == Dqn_PrintStd_Err);
#if defined(DQN_OS_WIN32)
// NOTE: Get the output handles from kernel
// =========================================================================
DQN_THREAD_LOCAL void *std_out_print_handle = nullptr;
DQN_THREAD_LOCAL void *std_err_print_handle = nullptr;
DQN_THREAD_LOCAL bool std_out_print_to_console = false;
DQN_THREAD_LOCAL bool std_err_print_to_console = false;
if (!std_out_print_handle) {
unsigned long mode = 0; (void)mode;
std_out_print_handle = GetStdHandle(STD_OUTPUT_HANDLE);
std_out_print_to_console = GetConsoleMode(std_out_print_handle, &mode) != 0;
std_err_print_handle = GetStdHandle(STD_ERROR_HANDLE);
std_err_print_to_console = GetConsoleMode(std_err_print_handle, &mode) != 0;
}
// NOTE: Select the output handle
// =========================================================================
void *print_handle = std_out_print_handle;
bool print_to_console = std_out_print_to_console;
if (std_handle == Dqn_PrintStd_Err) {
print_handle = std_err_print_handle;
print_to_console = std_err_print_to_console;
}
// NOTE: Write the string
// =========================================================================
DQN_ASSERT(string.size < DQN_CAST(unsigned long)-1);
unsigned long bytes_written = 0; (void)bytes_written;
if (print_to_console) {
WriteConsoleA(print_handle, string.data, DQN_CAST(unsigned long)string.size, &bytes_written, nullptr);
} else {
WriteFile(print_handle, string.data, DQN_CAST(unsigned long)string.size, &bytes_written, nullptr);
}
#else
fprintf(std_handle == Dqn_PrintStd_Out ? stdout : stderr, "%.*s", DQN_STRING_FMT(string));
#endif
}
DQN_API void Dqn_Print_StdStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string)
{
if (string.data && string.size) {
if (style.colour)
Dqn_Print_Std(std_handle, Dqn_Print_ESCColourFgString(style.r, style.g, style.b));
if (style.bold == Dqn_PrintBold_Yes)
Dqn_Print_Std(std_handle, Dqn_Print_ESCBoldString);
Dqn_Print_Std(std_handle, string);
if (style.colour || style.bold == Dqn_PrintBold_Yes)
Dqn_Print_Std(std_handle, Dqn_Print_ESCResetString);
}
}
DQN_FILE_SCOPE char *Dqn_Print_VSPrintfChunker_(const char *buf, void *user, int len)
{
Dqn_String8 string = {};
string.data = DQN_CAST(char *)buf;
string.size = len;
Dqn_PrintStd std_handle = DQN_CAST(Dqn_PrintStd)DQN_CAST(uintptr_t)user;
Dqn_Print_Std(std_handle, string);
return (char *)buf;
}
DQN_API void Dqn_Print_StdF(Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
Dqn_Print_StdFV(std_handle, fmt, args);
va_end(args);
}
DQN_API void Dqn_Print_StdFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
Dqn_Print_StdFVStyle(std_handle, style, fmt, args);
va_end(args);
}
DQN_API void Dqn_Print_StdFV(Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args)
{
char buffer[STB_SPRINTF_MIN];
STB_SPRINTF_DECORATE(vsprintfcb)(Dqn_Print_VSPrintfChunker_, DQN_CAST(void *)DQN_CAST(uintptr_t)std_handle, buffer, fmt, args);
}
DQN_API void Dqn_Print_StdFVStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args)
{
if (fmt) {
if (style.colour)
Dqn_Print_Std(std_handle, Dqn_Print_ESCColourFgString(style.r, style.g, style.b));
if (style.bold == Dqn_PrintBold_Yes)
Dqn_Print_Std(std_handle, Dqn_Print_ESCBoldString);
Dqn_Print_StdFV(std_handle, fmt, args);
if (style.colour || style.bold == Dqn_PrintBold_Yes)
Dqn_Print_Std(std_handle, Dqn_Print_ESCResetString);
}
}
DQN_API void Dqn_Print_StdLn(Dqn_PrintStd std_handle, Dqn_String8 string)
{
Dqn_Print_Std(std_handle, string);
Dqn_Print_Std(std_handle, DQN_STRING8("\n"));
}
DQN_API void Dqn_Print_StdLnF(Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
Dqn_Print_StdLnFV(std_handle, fmt, args);
va_end(args);
}
DQN_API void Dqn_Print_StdLnFV(Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args)
{
Dqn_Print_StdFV(std_handle, fmt, args);
Dqn_Print_Std(std_handle, DQN_STRING8("\n"));
}
DQN_API void Dqn_Print_StdLnStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string)
{
Dqn_Print_StdStyle(std_handle, style, string);
Dqn_Print_Std(std_handle, DQN_STRING8("\n"));
}
DQN_API void Dqn_Print_StdLnFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
Dqn_Print_StdLnFVStyle(std_handle, style, fmt, args);
va_end(args);
}
DQN_API void Dqn_Print_StdLnFVStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args)
{
Dqn_Print_StdFVStyle(std_handle, style, fmt, args);
Dqn_Print_Std(std_handle, DQN_STRING8("\n"));
}
DQN_API Dqn_String8 Dqn_Print_ESCColourString(Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b)
{
DQN_THREAD_LOCAL char buffer[32];
buffer[0] = 0;
Dqn_String8 result = {};
result.size = STB_SPRINTF_DECORATE(snprintf)(buffer,
DQN_ARRAY_UCOUNT(buffer),
"\x1b[%d;2;%u;%u;%um",
colour == Dqn_PrintESCColour_Fg ? 38 : 48,
r, g, b);
result.data = buffer;
return result;
}
DQN_API Dqn_String8 Dqn_Print_ESCColourU32String(Dqn_PrintESCColour colour, uint32_t value)
{
uint8_t r = DQN_CAST(uint8_t)(value >> 24);
uint8_t g = DQN_CAST(uint8_t)(value >> 16);
uint8_t b = DQN_CAST(uint8_t)(value >> 8);
Dqn_String8 result = Dqn_Print_ESCColourString(colour, r, g, b);
return result;
}

84
dqn_platform_print.h Normal file
View File

@ -0,0 +1,84 @@
// NOTE: [$PRIN] Dqn_Print =========================================================================
enum Dqn_PrintStd
{
Dqn_PrintStd_Out,
Dqn_PrintStd_Err,
};
enum Dqn_PrintBold
{
Dqn_PrintBold_No,
Dqn_PrintBold_Yes,
};
struct Dqn_PrintStyle
{
Dqn_PrintBold bold;
bool colour;
uint8_t r, g, b;
};
enum Dqn_PrintESCColour
{
Dqn_PrintESCColour_Fg,
Dqn_PrintESCColour_Bg,
};
// NOTE: Print Style ===============================================================================
DQN_API Dqn_PrintStyle Dqn_Print_StyleColour (uint8_t r, uint8_t g, uint8_t b, Dqn_PrintBold bold);
DQN_API Dqn_PrintStyle Dqn_Print_StyleColourU32 (uint32_t rgb, Dqn_PrintBold bold);
DQN_API Dqn_PrintStyle Dqn_Print_StyleBold ();
// NOTE: Print Standard Out ========================================================================
#define Dqn_Print(string) Dqn_Print_Std(Dqn_PrintStd_Out, string)
#define Dqn_Print_F(fmt, ...) Dqn_Print_StdF(Dqn_PrintStd_Out, fmt, ## __VA_ARGS__)
#define Dqn_Print_FV(fmt, args) Dqn_Print_StdFV(Dqn_PrintStd_Out, fmt, args)
#define Dqn_Print_Style(style, string) Dqn_Print_StdStyle(Dqn_PrintStd_Out, style, string)
#define Dqn_Print_FStyle(style, fmt, ...) Dqn_Print_StdFStyle(Dqn_PrintStd_Out, style, fmt, ## __VA_ARGS__)
#define Dqn_Print_FVStyle(style, fmt, args, ...) Dqn_Print_StdFVStyle(Dqn_PrintStd_Out, style, fmt, args)
#define Dqn_Print_Ln(string) Dqn_Print_StdLn(Dqn_PrintStd_Out, string)
#define Dqn_Print_LnF(fmt, ...) Dqn_Print_StdLnF(Dqn_PrintStd_Out, fmt, ## __VA_ARGS__)
#define Dqn_Print_LnFV(fmt, args) Dqn_Print_StdLnFV(Dqn_PrintStd_Out, fmt, args)
#define Dqn_Print_LnStyle(style, string) Dqn_Print_StdLnStyle(Dqn_PrintStd_Out, style, string);
#define Dqn_Print_LnFStyle(style, fmt, ...) Dqn_Print_StdLnFStyle(Dqn_PrintStd_Out, style, fmt, ## __VA_ARGS__);
#define Dqn_Print_LnFVStyle(style, fmt, args) Dqn_Print_StdLnFVStyle(Dqn_PrintStd_Out, style, fmt, args);
// NOTE: Print =====================================================================================
DQN_API void Dqn_Print_Std (Dqn_PrintStd std_handle, Dqn_String8 string);
DQN_API void Dqn_Print_StdF (Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, ...);
DQN_API void Dqn_Print_StdFV (Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args);
DQN_API void Dqn_Print_StdStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string);
DQN_API void Dqn_Print_StdFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, ...);
DQN_API void Dqn_Print_StdFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args);
DQN_API void Dqn_Print_StdLn (Dqn_PrintStd std_handle, Dqn_String8 string);
DQN_API void Dqn_Print_StdLnF (Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, ...);
DQN_API void Dqn_Print_StdLnFV (Dqn_PrintStd std_handle, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args);
DQN_API void Dqn_Print_StdLnStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string);
DQN_API void Dqn_Print_StdLnFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, ...);
DQN_API void Dqn_Print_StdLnFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args);
// NOTE: ANSI Formatting Codes =====================================================================
Dqn_String8 Dqn_Print_ESCColourString (Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b);
Dqn_String8 Dqn_Print_ESCColourU32String(Dqn_PrintESCColour colour, uint32_t value);
#define Dqn_Print_ESCColourFgString(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Fg, r, g, b)
#define Dqn_Print_ESCColourBgString(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Bg, r, g, b)
#define Dqn_Print_ESCColourFg(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Fg, r, g, b).data
#define Dqn_Print_ESCColourBg(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Bg, r, g, b).data
#define Dqn_Print_ESCColourFgU32String(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Fg, value)
#define Dqn_Print_ESCColourBgU32String(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Bg, value)
#define Dqn_Print_ESCColourFgU32(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Fg, value).data
#define Dqn_Print_ESCColourBgU32(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Bg, value).data
#define Dqn_Print_ESCReset "\x1b[0m"
#define Dqn_Print_ESCBold "\x1b[1m"
#define Dqn_Print_ESCResetString DQN_STRING8(Dqn_Print_ESCReset)
#define Dqn_Print_ESCBoldString DQN_STRING8(Dqn_Print_ESCBold)