Move source code into Source folder and add a single header generator"
This commit is contained in:
@@ -0,0 +1,748 @@
|
||||
#define DN_OS_CPP
|
||||
|
||||
#if defined(DN_PLATFORM_POSIX)
|
||||
#include <sys/sysinfo.h> // get_nprocs
|
||||
#include <unistd.h> // getpagesize
|
||||
#endif
|
||||
|
||||
static DN_OSCore *g_dn_os_core_;
|
||||
|
||||
static void DN_OS_LOGEmitFromTypeTypeFV_(DN_LOGTypeParam type, void *user_data, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_Assert(user_data);
|
||||
DN_OSCore *core = DN_CAST(DN_OSCore *)user_data;
|
||||
|
||||
// NOTE: Open log file for appending if requested ////////////////////////////////////////////////
|
||||
DN_TicketMutex_Begin(&core->log_file_mutex);
|
||||
if (core->log_to_file && !core->log_file.handle && !core->log_file.error) {
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
||||
DN_MSVC_WARNING_PUSH
|
||||
DN_MSVC_WARNING_DISABLE(6284) // Object passed as _Param_(3) when a string is required in call to 'DN_OS_PathF' Actual type: 'struct DN_Str8'.
|
||||
DN_Str8 log_path = DN_OS_PathF(tmem.arena, "%S/dn.log", DN_OS_EXEDir(tmem.arena));
|
||||
DN_MSVC_WARNING_POP
|
||||
core->log_file = DN_OS_FileOpen(log_path, DN_OSFileOpen_CreateAlways, DN_OSFileAccess_AppendOnly, nullptr);
|
||||
}
|
||||
DN_TicketMutex_End(&core->log_file_mutex);
|
||||
|
||||
DN_LOGStyle style = {};
|
||||
if (!core->log_no_colour) {
|
||||
style.colour = true;
|
||||
style.bold = DN_LOGBold_Yes;
|
||||
if (type.is_u32_enum) {
|
||||
switch (type.u32) {
|
||||
case DN_LOGType_Debug: {
|
||||
style.colour = false;
|
||||
style.bold = DN_LOGBold_No;
|
||||
} break;
|
||||
|
||||
case DN_LOGType_Info: {
|
||||
style.g = 0x87;
|
||||
style.b = 0xff;
|
||||
} break;
|
||||
|
||||
case DN_LOGType_Warning: {
|
||||
style.r = 0xff;
|
||||
style.g = 0xff;
|
||||
} break;
|
||||
|
||||
case DN_LOGType_Error: {
|
||||
style.r = 0xff;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DN_OSDateTime os_date = DN_OS_DateLocalTimeNow();
|
||||
DN_LOGDate log_date = {};
|
||||
log_date.year = os_date.year;
|
||||
log_date.month = os_date.month;
|
||||
log_date.day = os_date.day;
|
||||
log_date.hour = os_date.hour;
|
||||
log_date.minute = os_date.minutes;
|
||||
log_date.second = os_date.seconds;
|
||||
|
||||
char prefix_buffer[128] = {};
|
||||
DN_LOGPrefixSize prefix_size = DN_LOG_MakePrefix(style, type, call_site, log_date, prefix_buffer, sizeof(prefix_buffer));
|
||||
|
||||
va_list args_copy;
|
||||
va_copy(args_copy, args);
|
||||
DN_TicketMutex_Begin(&core->log_file_mutex);
|
||||
{
|
||||
DN_OS_FileWrite(&core->log_file, DN_Str8_Init(prefix_buffer, prefix_size.size), nullptr);
|
||||
DN_OS_FileWriteF(&core->log_file, nullptr, "%*s ", DN_CAST(int)prefix_size.padding, "");
|
||||
DN_OS_FileWriteFV(&core->log_file, nullptr, fmt, args_copy);
|
||||
DN_OS_FileWrite(&core->log_file, DN_STR8("\n"), nullptr);
|
||||
}
|
||||
DN_TicketMutex_End(&core->log_file_mutex);
|
||||
va_end(args_copy);
|
||||
|
||||
DN_OSPrintDest dest = (type.is_u32_enum && type.u32 == DN_LOGType_Error) ? DN_OSPrintDest_Err : DN_OSPrintDest_Out;
|
||||
DN_OS_Print(dest, DN_Str8_Init(prefix_buffer, prefix_size.size));
|
||||
DN_OS_PrintF(dest, "%*s ", DN_CAST(int)prefix_size.padding, "");
|
||||
DN_OS_PrintLnFV(dest, fmt, args);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_Init(DN_OSCore *os, DN_OSInitArgs *args)
|
||||
{
|
||||
g_dn_os_core_ = os;
|
||||
|
||||
// NOTE: OS
|
||||
{
|
||||
#if defined(DN_PLATFORM_WIN32)
|
||||
SYSTEM_INFO system_info = {};
|
||||
GetSystemInfo(&system_info);
|
||||
|
||||
os->logical_processor_count = system_info.dwNumberOfProcessors;
|
||||
os->page_size = system_info.dwPageSize;
|
||||
os->alloc_granularity = system_info.dwAllocationGranularity;
|
||||
#else
|
||||
os->logical_processor_count = get_nprocs();
|
||||
os->page_size = getpagesize();
|
||||
os->alloc_granularity = os->page_size;
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOTE: Setup logging
|
||||
DN_OS_EmitLogsWithOSPrintFunctions(os);
|
||||
|
||||
{
|
||||
os->arena = DN_Arena_InitFromOSVMem(DN_Megabytes(1), DN_Kilobytes(4), DN_ArenaFlags_NoAllocTrack);
|
||||
#if defined(DN_PLATFORM_WIN32)
|
||||
os->platform_context = DN_Arena_New(&os->arena, DN_W32Core, DN_ZeroMem_Yes);
|
||||
#elif defined(DN_PLATFORM_POSIX)
|
||||
os->platform_context = DN_Arena_New(&os->arena, DN_POSIXCore, DN_ZeroMem_Yes);
|
||||
#endif
|
||||
|
||||
#if defined(DN_PLATFORM_WIN32)
|
||||
DN_W32Core *w32 = DN_CAST(DN_W32Core *) os->platform_context;
|
||||
InitializeCriticalSection(&w32->sync_primitive_free_list_mutex);
|
||||
|
||||
QueryPerformanceFrequency(&w32->qpc_frequency);
|
||||
HMODULE module = LoadLibraryA("kernel32.dll");
|
||||
w32->set_thread_description = DN_CAST(DN_W32SetThreadDescriptionFunc *) GetProcAddress(module, "SetThreadDescription");
|
||||
FreeLibrary(module);
|
||||
|
||||
// NOTE: win32 bcrypt
|
||||
wchar_t const BCRYPT_ALGORITHM[] = L"RNG";
|
||||
long /*NTSTATUS*/ init_status = BCryptOpenAlgorithmProvider(&w32->bcrypt_rng_handle, BCRYPT_ALGORITHM, nullptr /*implementation*/, 0 /*flags*/);
|
||||
if (w32->bcrypt_rng_handle && init_status == 0)
|
||||
w32->bcrypt_init_success = true;
|
||||
else
|
||||
DN_LOG_ErrorF("Failed to initialise Windows secure random number generator, error: %d", init_status);
|
||||
#else
|
||||
DN_POSIXCore *posix = DN_CAST(DN_POSIXCore *) os->platform_context;
|
||||
int mutex_init = pthread_mutex_init(&posix->sync_primitive_free_list_mutex, nullptr);
|
||||
DN_Assert(mutex_init == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOTE: Initialise tmem arenas which allocate memory and will be
|
||||
// recorded to the now initialised allocation table. The initialisation
|
||||
// of tmem memory may request tmem memory itself in leak tracing mode.
|
||||
// This is supported as the tmem arenas defer allocation tracking until
|
||||
// initialisation is done.
|
||||
DN_OSTLSInitArgs tls_init_args = {};
|
||||
if (args) {
|
||||
tls_init_args.commit = args->tls_commit;
|
||||
tls_init_args.reserve = args->tls_reserve;
|
||||
tls_init_args.err_sink_reserve = args->tls_err_sink_reserve;
|
||||
tls_init_args.err_sink_commit = args->tls_err_sink_commit;
|
||||
}
|
||||
|
||||
DN_OS_TLSInit(&os->tls, tls_init_args);
|
||||
DN_OS_TLSSetCurrentThreadTLS(&os->tls);
|
||||
os->cpu_report = DN_CPU_Report();
|
||||
|
||||
#define DN_CPU_FEAT_XENTRY(label) g_dn_cpu_feature_decl[DN_CPUFeature_##label] = {DN_CPUFeature_##label, DN_STR8(#label)};
|
||||
DN_CPU_FEAT_XMACRO
|
||||
#undef DN_CPU_FEAT_XENTRY
|
||||
DN_Assert(g_dn_os_core_);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_EmitLogsWithOSPrintFunctions(DN_OSCore *os)
|
||||
{
|
||||
DN_Assert(os);
|
||||
DN_LOG_SetEmitFromTypeFVFunc(DN_OS_LOGEmitFromTypeTypeFV_, os);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_DumpThreadContextArenaStat(DN_Str8 file_path)
|
||||
{
|
||||
#if defined(DN_DEBUG_THREAD_CONTEXT)
|
||||
// NOTE: Open a file to write the arena stats to
|
||||
FILE *file = nullptr;
|
||||
fopen_s(&file, file_path.data, "a+b");
|
||||
if (file) {
|
||||
DN_LOG_ErrorF("Failed to dump thread context arenas [file=%.*s]", DN_STR_FMT(file_path));
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: Copy the stats from library book-keeping
|
||||
// NOTE: Extremely short critical section, copy the stats then do our
|
||||
// work on it.
|
||||
DN_ArenaStat stats[DN_CArray_CountI(g_dn_core->thread_context_arena_stats)];
|
||||
int stats_size = 0;
|
||||
|
||||
DN_TicketMutex_Begin(&g_dn_core->thread_context_mutex);
|
||||
stats_size = g_dn_core->thread_context_arena_stats_count;
|
||||
DN_Memcpy(stats, g_dn_core->thread_context_arena_stats, sizeof(stats[0]) * stats_size);
|
||||
DN_TicketMutex_End(&g_dn_core->thread_context_mutex);
|
||||
|
||||
// NOTE: Print the cumulative stat
|
||||
DN_DateHMSTimeStr now = DN_Date_HMSLocalTimeStrNow();
|
||||
fprintf(file,
|
||||
"Time=%.*s %.*s | Thread Context Arenas | Count=%d\n",
|
||||
now.date_size,
|
||||
now.date,
|
||||
now.hms_size,
|
||||
now.hms,
|
||||
g_dn_core->thread_context_arena_stats_count);
|
||||
|
||||
// NOTE: Write the cumulative thread arena data
|
||||
{
|
||||
DN_ArenaStat stat = {};
|
||||
for (DN_USize index = 0; index < stats_size; index++) {
|
||||
DN_ArenaStat const *current = stats + index;
|
||||
stat.capacity += current->capacity;
|
||||
stat.used += current->used;
|
||||
stat.wasted += current->wasted;
|
||||
stat.blocks += current->blocks;
|
||||
|
||||
stat.capacity_hwm = DN_Max(stat.capacity_hwm, current->capacity_hwm);
|
||||
stat.used_hwm = DN_Max(stat.used_hwm, current->used_hwm);
|
||||
stat.wasted_hwm = DN_Max(stat.wasted_hwm, current->wasted_hwm);
|
||||
stat.blocks_hwm = DN_Max(stat.blocks_hwm, current->blocks_hwm);
|
||||
}
|
||||
|
||||
DN_ArenaStatStr stats_string = DN_Arena_StatStr(&stat);
|
||||
fprintf(file, " [ALL] CURR %.*s\n", stats_string.size, stats_string.data);
|
||||
}
|
||||
|
||||
// NOTE: Print individual thread arena data
|
||||
for (DN_USize index = 0; index < stats_size; index++) {
|
||||
DN_ArenaStat const *current = stats + index;
|
||||
DN_ArenaStatStr current_string = DN_Arena_StatStr(current);
|
||||
fprintf(file, " [%03d] CURR %.*s\n", DN_CAST(int) index, current_string.size, current_string.data);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
DN_LOG_InfoF("Dumped thread context arenas [file=%.*s]", DN_STR_FMT(file_path));
|
||||
#else
|
||||
(void)file_path;
|
||||
#endif // #if defined(DN_DEBUG_THREAD_CONTEXT)
|
||||
}
|
||||
|
||||
// NOTE: Date //////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSDateTimeStr8 DN_OS_DateLocalTimeStr8(DN_OSDateTime time, char date_separator, char hms_separator)
|
||||
{
|
||||
DN_OSDateTimeStr8 result = {};
|
||||
result.hms_size = DN_CAST(uint8_t) DN_SNPrintF(result.hms,
|
||||
DN_ArrayCountI(result.hms),
|
||||
"%02hhu%c%02hhu%c%02hhu",
|
||||
time.hour,
|
||||
hms_separator,
|
||||
time.minutes,
|
||||
hms_separator,
|
||||
time.seconds);
|
||||
|
||||
result.date_size = DN_CAST(uint8_t) DN_SNPrintF(result.date,
|
||||
DN_ArrayCountI(result.date),
|
||||
"%hu%c%02hhu%c%02hhu",
|
||||
time.year,
|
||||
date_separator,
|
||||
time.month,
|
||||
date_separator,
|
||||
time.day);
|
||||
|
||||
DN_Assert(result.hms_size < DN_ArrayCountU(result.hms));
|
||||
DN_Assert(result.date_size < DN_ArrayCountU(result.date));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_OSDateTimeStr8 DN_OS_DateLocalTimeStr8Now(char date_separator, char hms_separator)
|
||||
{
|
||||
DN_OSDateTime time = DN_OS_DateLocalTimeNow();
|
||||
DN_OSDateTimeStr8 result = DN_OS_DateLocalTimeStr8(time, date_separator, hms_separator);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_DateIsValid(DN_OSDateTime date)
|
||||
{
|
||||
if (date.year < 1970)
|
||||
return false;
|
||||
if (date.month <= 0 || date.month >= 13)
|
||||
return false;
|
||||
if (date.day <= 0 || date.day >= 32)
|
||||
return false;
|
||||
if (date.hour >= 24)
|
||||
return false;
|
||||
if (date.minutes >= 60)
|
||||
return false;
|
||||
if (date.seconds >= 60)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOTE: Other /////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Str8 DN_OS_EXEDir(DN_Arena *arena)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
||||
DN_Str8 exe_path = DN_OS_EXEPath(tmem.arena);
|
||||
DN_Str8 separators[] = {DN_STR8("/"), DN_STR8("\\")};
|
||||
DN_Str8BinarySplitResult split = DN_Str8_BinarySplitLastArray(exe_path, separators, DN_ArrayCountU(separators));
|
||||
result = DN_Str8_Copy(arena, split.lhs);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Counters //////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_F64 DN_OS_PerfCounterS(uint64_t begin, uint64_t end)
|
||||
{
|
||||
uint64_t frequency = DN_OS_PerfCounterFrequency();
|
||||
uint64_t ticks = end - begin;
|
||||
DN_F64 result = ticks / DN_CAST(DN_F64) frequency;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_PerfCounterMs(uint64_t begin, uint64_t end)
|
||||
{
|
||||
uint64_t frequency = DN_OS_PerfCounterFrequency();
|
||||
uint64_t ticks = end - begin;
|
||||
DN_F64 result = (ticks * 1'000) / DN_CAST(DN_F64) frequency;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_PerfCounterUs(uint64_t begin, uint64_t end)
|
||||
{
|
||||
uint64_t frequency = DN_OS_PerfCounterFrequency();
|
||||
uint64_t ticks = end - begin;
|
||||
DN_F64 result = (ticks * 1'000'000) / DN_CAST(DN_F64) frequency;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_PerfCounterNs(uint64_t begin, uint64_t end)
|
||||
{
|
||||
uint64_t frequency = DN_OS_PerfCounterFrequency();
|
||||
uint64_t ticks = end - begin;
|
||||
DN_F64 result = (ticks * 1'000'000'000) / DN_CAST(DN_F64) frequency;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_OSTimer DN_OS_TimerBegin()
|
||||
{
|
||||
DN_OSTimer result = {};
|
||||
result.start = DN_OS_PerfCounterNow();
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_TimerEnd(DN_OSTimer *timer)
|
||||
{
|
||||
timer->end = DN_OS_PerfCounterNow();
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_TimerS(DN_OSTimer timer)
|
||||
{
|
||||
DN_F64 result = DN_OS_PerfCounterS(timer.start, timer.end);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_TimerMs(DN_OSTimer timer)
|
||||
{
|
||||
DN_F64 result = DN_OS_PerfCounterMs(timer.start, timer.end);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_TimerUs(DN_OSTimer timer)
|
||||
{
|
||||
DN_F64 result = DN_OS_PerfCounterUs(timer.start, timer.end);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_F64 DN_OS_TimerNs(DN_OSTimer timer)
|
||||
{
|
||||
DN_F64 result = DN_OS_PerfCounterNs(timer.start, timer.end);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API uint64_t DN_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_tsc_frequency)
|
||||
{
|
||||
uint64_t os_frequency = DN_OS_PerfCounterFrequency();
|
||||
uint64_t os_target_elapsed = duration_ms_to_gauge_tsc_frequency * os_frequency / 1000ULL;
|
||||
uint64_t tsc_begin = DN_CPU_TSC();
|
||||
uint64_t result = 0;
|
||||
if (tsc_begin) {
|
||||
uint64_t os_elapsed = 0;
|
||||
for (uint64_t os_begin = DN_OS_PerfCounterNow(); os_elapsed < os_target_elapsed;)
|
||||
os_elapsed = DN_OS_PerfCounterNow() - os_begin;
|
||||
uint64_t tsc_end = DN_CPU_TSC();
|
||||
uint64_t tsc_elapsed = tsc_end - tsc_begin;
|
||||
result = tsc_elapsed / os_elapsed * os_frequency;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(DN_NO_OS_FILE_API)
|
||||
// NOTE: DN_OSPathInfo/File ////////////////////////////////////////////////////////////////////////
|
||||
DN_API bool DN_OS_FileIsOlderThan(DN_Str8 file, DN_Str8 check_against)
|
||||
{
|
||||
DN_OSPathInfo file_info = DN_OS_PathInfo(file);
|
||||
DN_OSPathInfo check_against_info = DN_OS_PathInfo(check_against);
|
||||
bool result = !file_info.exists || file_info.last_write_time_in_s < check_against_info.last_write_time_in_s;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_FileWrite(DN_OSFile *file, DN_Str8 buffer, DN_OSErrSink *error)
|
||||
{
|
||||
bool result = DN_OS_FileWritePtr(file, buffer.data, buffer.size, error);
|
||||
return result;
|
||||
}
|
||||
|
||||
struct DN_OSFileWriteChunker_
|
||||
{
|
||||
DN_OSErrSink *err;
|
||||
DN_OSFile *file;
|
||||
bool success;
|
||||
};
|
||||
|
||||
static char *DN_OS_FileWriteChunker_(const char *buf, void *user, int len)
|
||||
{
|
||||
DN_OSFileWriteChunker_ *chunker = DN_CAST(DN_OSFileWriteChunker_ *)user;
|
||||
chunker->success = DN_OS_FileWritePtr(chunker->file, buf, len, chunker->err);
|
||||
char *result = chunker->success ? DN_CAST(char *) buf : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_FileWriteFV(DN_OSFile *file, DN_OSErrSink *error, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
bool result = false;
|
||||
if (!file || !fmt)
|
||||
return result;
|
||||
|
||||
DN_OSFileWriteChunker_ chunker = {};
|
||||
chunker.err = error;
|
||||
chunker.file = file;
|
||||
char buffer[STB_SPRINTF_MIN];
|
||||
STB_SPRINTF_DECORATE(vsprintfcb)(DN_OS_FileWriteChunker_, &chunker, buffer, fmt, args);
|
||||
|
||||
result = chunker.success;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_FileWriteF(DN_OSFile *file, DN_OSErrSink *error, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
bool result = DN_OS_FileWriteFV(file, error, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Str8 DN_OS_ReadAll(DN_Arena *arena, DN_Str8 path, DN_OSErrSink *error)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
|
||||
// NOTE: Query file size + allocate buffer /////////////////////////////////////////////////////
|
||||
DN_OSPathInfo path_info = DN_OS_PathInfo(path);
|
||||
if (!path_info.exists) {
|
||||
DN_OS_ErrSinkAppendF(error, 1, "File does not exist/could not be queried for reading '%.*s'", DN_STR_FMT(path));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_ArenaTempMem temp_mem = DN_Arena_TempMemBegin(arena);
|
||||
result = DN_Str8_Alloc(arena, path_info.size, DN_ZeroMem_No);
|
||||
if (!DN_Str8_HasData(result)) {
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
||||
DN_Str8 buffer_size_str8 = DN_CVT_U64ToByteSizeStr8(tmem.arena, path_info.size, DN_CVTU64ByteSizeType_Auto);
|
||||
DN_OS_ErrSinkAppendF(error, 1 /*error_code*/, "Failed to allocate %.*s for reading file '%.*s'", DN_STR_FMT(buffer_size_str8), DN_STR_FMT(path));
|
||||
DN_Arena_TempMemEnd(temp_mem);
|
||||
result = {};
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Read the file from disk ///////////////////////////////////////////////////////////////
|
||||
DN_OSFile file = DN_OS_FileOpen(path, DN_OSFileOpen_OpenIfExist, DN_OSFileAccess_Read, error);
|
||||
DN_OSFileRead read = DN_OS_FileRead(&file, result.data, result.size, error);
|
||||
if (file.error || !read.success) {
|
||||
DN_Arena_TempMemEnd(temp_mem);
|
||||
result = {};
|
||||
}
|
||||
DN_OS_FileClose(&file);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_WriteAll(DN_Str8 path, DN_Str8 buffer, DN_OSErrSink *error)
|
||||
{
|
||||
DN_OSFile file = DN_OS_FileOpen(path, DN_OSFileOpen_CreateAlways, DN_OSFileAccess_Write, error);
|
||||
bool result = DN_OS_FileWrite(&file, buffer, error);
|
||||
DN_OS_FileClose(&file);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_WriteAllFV(DN_Str8 file_path, DN_OSErrSink *error, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
||||
DN_Str8 buffer = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
bool result = DN_OS_WriteAll(file_path, buffer, error);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_WriteAllF(DN_Str8 file_path, DN_OSErrSink *error, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
bool result = DN_OS_WriteAllFV(file_path, error, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_WriteAllSafe(DN_Str8 path, DN_Str8 buffer, DN_OSErrSink *error)
|
||||
{
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
||||
DN_Str8 tmp_path = DN_Str8_InitF(tmem.arena, "%.*s.tmp", DN_STR_FMT(path));
|
||||
if (!DN_OS_WriteAll(tmp_path, buffer, error))
|
||||
return false;
|
||||
if (!DN_OS_CopyFile(tmp_path, path, true /*overwrite*/, error))
|
||||
return false;
|
||||
if (!DN_OS_PathDelete(tmp_path))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_WriteAllSafeFV(DN_Str8 path, DN_OSErrSink *error, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
||||
DN_Str8 buffer = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
bool result = DN_OS_WriteAllSafe(path, buffer, error);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_WriteAllSafeF(DN_Str8 path, DN_OSErrSink *error, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
bool result = DN_OS_WriteAllSafeFV(path, error, fmt, args);
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DN_NO_OS_FILE_API)
|
||||
|
||||
// NOTE: DN_OSPath /////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API bool DN_OS_PathAddRef(DN_Arena *arena, DN_OSPath *fs_path, DN_Str8 path)
|
||||
{
|
||||
if (!arena || !fs_path || !DN_Str8_HasData(path))
|
||||
return false;
|
||||
|
||||
if (path.size <= 0)
|
||||
return true;
|
||||
|
||||
DN_Str8 const delimiter_array[] = {
|
||||
DN_STR8("\\"),
|
||||
DN_STR8("/")};
|
||||
|
||||
if (fs_path->links_size == 0)
|
||||
fs_path->has_prefix_path_separator = (path.data[0] == '/');
|
||||
|
||||
for (;;) {
|
||||
DN_Str8BinarySplitResult delimiter = DN_Str8_BinarySplitArray(path, delimiter_array, DN_ArrayCountU(delimiter_array));
|
||||
for (; delimiter.lhs.data; delimiter = DN_Str8_BinarySplitArray(delimiter.rhs, delimiter_array, DN_ArrayCountU(delimiter_array))) {
|
||||
if (delimiter.lhs.size <= 0)
|
||||
continue;
|
||||
|
||||
DN_OSPathLink *link = DN_Arena_New(arena, DN_OSPathLink, DN_ZeroMem_Yes);
|
||||
if (!link)
|
||||
return false;
|
||||
|
||||
link->string = delimiter.lhs;
|
||||
link->prev = fs_path->tail;
|
||||
if (fs_path->tail)
|
||||
fs_path->tail->next = link;
|
||||
else
|
||||
fs_path->head = link;
|
||||
fs_path->tail = link;
|
||||
fs_path->links_size += 1;
|
||||
fs_path->string_size += delimiter.lhs.size;
|
||||
}
|
||||
|
||||
if (!delimiter.lhs.data)
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_PathAdd(DN_Arena *arena, DN_OSPath *fs_path, DN_Str8 path)
|
||||
{
|
||||
DN_Str8 copy = DN_Str8_Copy(arena, path);
|
||||
bool result = DN_Str8_HasData(copy) ? true : DN_OS_PathAddRef(arena, fs_path, copy);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_PathAddF(DN_Arena *arena, DN_OSPath *fs_path, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 path = DN_Str8_InitFV(arena, fmt, args);
|
||||
va_end(args);
|
||||
bool result = DN_OS_PathAddRef(arena, fs_path, path);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_PathPop(DN_OSPath *fs_path)
|
||||
{
|
||||
if (!fs_path)
|
||||
return false;
|
||||
|
||||
if (fs_path->tail) {
|
||||
DN_Assert(fs_path->head);
|
||||
fs_path->links_size -= 1;
|
||||
fs_path->string_size -= fs_path->tail->string.size;
|
||||
fs_path->tail = fs_path->tail->prev;
|
||||
if (fs_path->tail)
|
||||
fs_path->tail->next = nullptr;
|
||||
else
|
||||
fs_path->head = nullptr;
|
||||
} else {
|
||||
DN_Assert(!fs_path->head);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_OS_PathTo(DN_Arena *arena, DN_Str8 path, DN_Str8 path_separator)
|
||||
{
|
||||
DN_OSPath fs_path = {};
|
||||
DN_OS_PathAddRef(arena, &fs_path, path);
|
||||
DN_Str8 result = DN_OS_PathBuildWithSeparator(arena, &fs_path, path_separator);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_OS_PathToF(DN_Arena *arena, DN_Str8 path_separator, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 path = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
va_end(args);
|
||||
DN_Str8 result = DN_OS_PathTo(arena, path, path_separator);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_OS_Path(DN_Arena *arena, DN_Str8 path)
|
||||
{
|
||||
DN_Str8 result = DN_OS_PathTo(arena, path, DN_OSPathSeperatorString);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_OS_PathF(DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 path = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
va_end(args);
|
||||
DN_Str8 result = DN_OS_Path(arena, path);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_OS_PathBuildWithSeparator(DN_Arena *arena, DN_OSPath const *fs_path, DN_Str8 path_separator)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
if (!fs_path || fs_path->links_size <= 0)
|
||||
return result;
|
||||
|
||||
// NOTE: Each link except the last one needs the path separator appended to it, '/' or '\\'
|
||||
DN_USize string_size = (fs_path->has_prefix_path_separator ? path_separator.size : 0) + fs_path->string_size + ((fs_path->links_size - 1) * path_separator.size);
|
||||
result = DN_Str8_Alloc(arena, string_size, DN_ZeroMem_No);
|
||||
if (result.data) {
|
||||
char *dest = result.data;
|
||||
if (fs_path->has_prefix_path_separator) {
|
||||
DN_Memcpy(dest, path_separator.data, path_separator.size);
|
||||
dest += path_separator.size;
|
||||
}
|
||||
|
||||
for (DN_OSPathLink *link = fs_path->head; link; link = link->next) {
|
||||
DN_Str8 string = link->string;
|
||||
DN_Memcpy(dest, string.data, string.size);
|
||||
dest += string.size;
|
||||
|
||||
if (link != fs_path->tail) {
|
||||
DN_Memcpy(dest, path_separator.data, path_separator.size);
|
||||
dest += path_separator.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.data[string_size] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_OSExec /////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSExecResult DN_OS_Exec(DN_Slice<DN_Str8> cmd_line,
|
||||
DN_OSExecArgs *args,
|
||||
DN_Arena *arena,
|
||||
DN_OSErrSink *error)
|
||||
{
|
||||
DN_OSExecAsyncHandle async_handle = DN_OS_ExecAsync(cmd_line, args, error);
|
||||
DN_OSExecResult result = DN_OS_ExecWait(async_handle, arena, error);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_OSExecResult DN_OS_ExecOrAbort(DN_Slice<DN_Str8> cmd_line, DN_OSExecArgs *args, DN_Arena *arena)
|
||||
{
|
||||
DN_OSErrSink *error = DN_OS_ErrSinkBegin(DN_OSErrSinkMode_Nil);
|
||||
DN_OSExecResult result = DN_OS_Exec(cmd_line, args, arena, error);
|
||||
if (result.os_error_code)
|
||||
DN_OS_ErrSinkEndAndExitIfErrorF(error, result.os_error_code, "OS failed to execute the requested command returning the error code %u", result.os_error_code);
|
||||
|
||||
if (result.exit_code)
|
||||
DN_OS_ErrSinkEndAndExitIfErrorF(error, result.exit_code, "OS executed command and returned non-zero exit code %u", result.exit_code);
|
||||
DN_OS_ErrSinkEndAndIgnore(error);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_OSThread ///////////////////////////////////////////////////////////////////////////////
|
||||
static void DN_OS_ThreadExecute_(void *user_context)
|
||||
{
|
||||
DN_OSThread *thread = DN_CAST(DN_OSThread *) user_context;
|
||||
DN_OS_TLSInit(&thread->tls, thread->tls_init_args);
|
||||
DN_OS_TLSSetCurrentThreadTLS(&thread->tls);
|
||||
DN_OS_SemaphoreWait(&thread->init_semaphore, DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
thread->func(thread);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_ThreadSetName(DN_Str8 name)
|
||||
{
|
||||
DN_OSTLS *tls = DN_OS_TLSGet();
|
||||
tls->name_size = DN_CAST(uint8_t) DN_Min(name.size, sizeof(tls->name) - 1);
|
||||
DN_Memcpy(tls->name, name.data, tls->name_size);
|
||||
tls->name[tls->name_size] = 0;
|
||||
|
||||
#if defined(DN_PLATFORM_WIN32)
|
||||
DN_W32_ThreadSetName(name);
|
||||
#else
|
||||
DN_Posix_ThreadSetName(name);
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOTE: DN_OSHttp /////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_OS_HttpRequestWait(DN_OSHttpResponse *response)
|
||||
{
|
||||
if (response && response->on_complete_semaphore.handle != 0)
|
||||
DN_OS_SemaphoreWait(&response->on_complete_semaphore, DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
}
|
||||
|
||||
DN_API DN_OSHttpResponse DN_OS_HttpRequest(DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers)
|
||||
{
|
||||
// TODO(doyle): Revise the memory allocation and its lifetime
|
||||
DN_OSHttpResponse result = {};
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
||||
result.tmem_arena = tmem.arena;
|
||||
|
||||
DN_OS_HttpRequestAsync(&result, arena, host, path, secure, method, body, headers);
|
||||
DN_OS_HttpRequestWait(&result);
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,457 @@
|
||||
#if !defined(DN_OS_H)
|
||||
#define DN_OS_H
|
||||
|
||||
#include <new> // operator new
|
||||
|
||||
#if !defined(DN_OS_WIN32) || defined(DN_OS_WIN32_USE_PTHREADS)
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
#if defined(DN_PLATFORM_POSIX) || defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
#include <errno.h> // errno
|
||||
#include <fcntl.h> // O_RDONLY ... etc
|
||||
#include <sys/ioctl.h> // ioctl
|
||||
#include <sys/mman.h> // mmap
|
||||
#include <sys/random.h> // getrandom
|
||||
#include <sys/stat.h> // stat
|
||||
#include <sys/types.h> // pid_t
|
||||
#include <sys/wait.h> // waitpid
|
||||
#include <time.h> // clock_gettime, nanosleep
|
||||
#include <unistd.h> // access, gettid, write
|
||||
|
||||
#if !defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
#include <linux/fs.h> // FICLONE
|
||||
#include <sys/sendfile.h> // sendfile
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
#include <emscripten/fetch.h> // emscripten_fetch (for DN_OSHttpResponse)
|
||||
#endif
|
||||
|
||||
// NOTE: DN_OSDate /////////////////////////////////////////////////////////////////////////////////
|
||||
struct DN_OSDateTimeStr8
|
||||
{
|
||||
char date[DN_ArrayCountU("YYYY-MM-SS")];
|
||||
DN_U8 date_size;
|
||||
char hms[DN_ArrayCountU("HH:MM:SS")];
|
||||
DN_U8 hms_size;
|
||||
};
|
||||
|
||||
struct DN_OSDateTime
|
||||
{
|
||||
DN_U8 day;
|
||||
DN_U8 month;
|
||||
DN_U16 year;
|
||||
DN_U8 hour;
|
||||
DN_U8 minutes;
|
||||
DN_U8 seconds;
|
||||
};
|
||||
|
||||
struct DN_OSTimer /// Record time between two time-points using the OS's performance counter.
|
||||
{
|
||||
DN_U64 start;
|
||||
DN_U64 end;
|
||||
};
|
||||
|
||||
#if !defined(DN_NO_OS_FILE_API)
|
||||
// NOTE: DN_OSFile /////////////////////////////////////////////////////////////////////////////////
|
||||
enum DN_OSPathInfoType
|
||||
{
|
||||
DN_OSPathInfoType_Unknown,
|
||||
DN_OSPathInfoType_Directory,
|
||||
DN_OSPathInfoType_File,
|
||||
};
|
||||
|
||||
struct DN_OSPathInfo
|
||||
{
|
||||
bool exists;
|
||||
DN_OSPathInfoType type;
|
||||
DN_U64 create_time_in_s;
|
||||
DN_U64 last_write_time_in_s;
|
||||
DN_U64 last_access_time_in_s;
|
||||
DN_U64 size;
|
||||
};
|
||||
|
||||
struct DN_OSDirIterator
|
||||
{
|
||||
void *handle;
|
||||
DN_Str8 file_name;
|
||||
char buffer[512];
|
||||
};
|
||||
|
||||
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
||||
struct DN_OSFileRead
|
||||
{
|
||||
bool success;
|
||||
DN_USize bytes_read;
|
||||
};
|
||||
|
||||
struct DN_OSFile
|
||||
{
|
||||
bool error;
|
||||
void *handle;
|
||||
};
|
||||
|
||||
enum DN_OSFileOpen
|
||||
{
|
||||
DN_OSFileOpen_CreateAlways, // Create file if it does not exist, otherwise, zero out the file and open
|
||||
DN_OSFileOpen_OpenIfExist, // Open file at path only if it exists
|
||||
DN_OSFileOpen_OpenAlways, // Open file at path, create file if it does not exist
|
||||
};
|
||||
|
||||
typedef DN_U32 DN_OSFileAccess;
|
||||
|
||||
enum DN_OSFileAccess_
|
||||
{
|
||||
DN_OSFileAccess_Read = 1 << 0,
|
||||
DN_OSFileAccess_Write = 1 << 1,
|
||||
DN_OSFileAccess_Execute = 1 << 2,
|
||||
DN_OSFileAccess_AppendOnly = 1 << 3, // This flag cannot be combined with any other access mode
|
||||
DN_OSFileAccess_ReadWrite = DN_OSFileAccess_Read | DN_OSFileAccess_Write,
|
||||
DN_OSFileAccess_All = DN_OSFileAccess_ReadWrite | DN_OSFileAccess_Execute | DN_OSFileAccess_AppendOnly,
|
||||
};
|
||||
#endif // DN_NO_OS_FILE_API
|
||||
|
||||
// NOTE: DN_OSPath ////////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DN_OSPathSeperator)
|
||||
#if defined(DN_OS_WIN32)
|
||||
#define DN_OSPathSeperator "\\"
|
||||
#else
|
||||
#define DN_OSPathSeperator "/"
|
||||
#endif
|
||||
#define DN_OSPathSeperatorString DN_STR8(DN_OSPathSeperator)
|
||||
#endif
|
||||
|
||||
struct DN_OSPathLink
|
||||
{
|
||||
DN_Str8 string;
|
||||
DN_OSPathLink *next;
|
||||
DN_OSPathLink *prev;
|
||||
};
|
||||
|
||||
struct DN_OSPath
|
||||
{
|
||||
bool has_prefix_path_separator;
|
||||
DN_OSPathLink *head;
|
||||
DN_OSPathLink *tail;
|
||||
DN_USize string_size;
|
||||
DN_U16 links_size;
|
||||
};
|
||||
|
||||
// NOTE: DN_OSExec /////////////////////////////////////////////////////////////////////////////////
|
||||
typedef DN_U32 DN_OSExecFlags;
|
||||
|
||||
enum DN_OSExecFlags_
|
||||
{
|
||||
DN_OSExecFlags_Nil = 0,
|
||||
DN_OSExecFlags_SaveStdout = 1 << 0,
|
||||
DN_OSExecFlags_SaveStderr = 1 << 1,
|
||||
DN_OSExecFlags_SaveOutput = DN_OSExecFlags_SaveStdout | DN_OSExecFlags_SaveStderr,
|
||||
DN_OSExecFlags_MergeStderrToStdout = 1 << 2 | DN_OSExecFlags_SaveOutput,
|
||||
};
|
||||
|
||||
struct DN_OSExecAsyncHandle
|
||||
{
|
||||
DN_OSExecFlags exec_flags;
|
||||
DN_U32 os_error_code;
|
||||
DN_U32 exit_code;
|
||||
void *process;
|
||||
void *stdout_read;
|
||||
void *stdout_write;
|
||||
void *stderr_read;
|
||||
void *stderr_write;
|
||||
};
|
||||
|
||||
struct DN_OSExecResult
|
||||
{
|
||||
bool finished;
|
||||
DN_Str8 stdout_text;
|
||||
DN_Str8 stderr_text;
|
||||
DN_U32 os_error_code;
|
||||
DN_U32 exit_code;
|
||||
};
|
||||
|
||||
struct DN_OSExecArgs
|
||||
{
|
||||
DN_OSExecFlags flags;
|
||||
DN_Str8 working_dir;
|
||||
DN_Slice<DN_Str8> environment;
|
||||
};
|
||||
|
||||
// NOTE: DN_OSSemaphore ////////////////////////////////////////////////////////////////////////////
|
||||
DN_U32 const DN_OS_SEMAPHORE_INFINITE_TIMEOUT = UINT32_MAX;
|
||||
|
||||
struct DN_OSSemaphore
|
||||
{
|
||||
DN_U64 handle;
|
||||
};
|
||||
|
||||
enum DN_OSSemaphoreWaitResult
|
||||
{
|
||||
DN_OSSemaphoreWaitResult_Failed,
|
||||
DN_OSSemaphoreWaitResult_Success,
|
||||
DN_OSSemaphoreWaitResult_Timeout,
|
||||
};
|
||||
|
||||
struct DN_OSMutex
|
||||
{
|
||||
DN_U64 handle;
|
||||
};
|
||||
|
||||
struct DN_OSConditionVariable
|
||||
{
|
||||
DN_U64 handle;
|
||||
};
|
||||
|
||||
// NOTE: DN_OSThread ///////////////////////////////////////////////////////////////////////////////
|
||||
typedef DN_I32(DN_OSThreadFunc)(struct DN_OSThread *);
|
||||
|
||||
struct DN_OSThread
|
||||
{
|
||||
DN_FStr8<64> name;
|
||||
DN_OSTLS tls;
|
||||
DN_OSTLSInitArgs tls_init_args;
|
||||
void *handle;
|
||||
DN_U64 thread_id;
|
||||
void *user_context;
|
||||
DN_OSThreadFunc *func;
|
||||
DN_OSSemaphore init_semaphore;
|
||||
};
|
||||
|
||||
// NOTE: DN_OSHttp /////////////////////////////////////////////////////////////////////////////////
|
||||
enum DN_OSHttpRequestSecure
|
||||
{
|
||||
DN_OSHttpRequestSecure_No,
|
||||
DN_OSHttpRequestSecure_Yes,
|
||||
};
|
||||
|
||||
struct DN_OSHttpResponse
|
||||
{
|
||||
// NOTE: Response data
|
||||
DN_U32 error_code;
|
||||
DN_Str8 error_msg;
|
||||
DN_U16 http_status;
|
||||
DN_Str8 body;
|
||||
DN_B32 done;
|
||||
|
||||
// NOTE: Book-keeping
|
||||
DN_Arena *arena; // Allocates memory for the response
|
||||
|
||||
// NOTE: Async book-keeping
|
||||
// Synchronous HTTP response uses the TLS scratch arena whereas async
|
||||
// calls use their own dedicated arena.
|
||||
DN_Arena tmp_arena;
|
||||
DN_Arena *tmem_arena;
|
||||
DN_Str8Builder builder;
|
||||
DN_OSSemaphore on_complete_semaphore;
|
||||
|
||||
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
||||
emscripten_fetch_t *em_handle;
|
||||
#elif defined(DN_PLATFORM_WIN32)
|
||||
HINTERNET w32_request_session;
|
||||
HINTERNET w32_request_connection;
|
||||
HINTERNET w32_request_handle;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct DN_OSInitArgs
|
||||
{
|
||||
DN_U64 tls_reserve;
|
||||
DN_U64 tls_commit;
|
||||
DN_U64 tls_err_sink_reserve;
|
||||
DN_U64 tls_err_sink_commit;
|
||||
};
|
||||
|
||||
struct DN_OSCore
|
||||
{
|
||||
DN_CPUReport cpu_report;
|
||||
DN_OSTLS tls; // Thread local storage state for the main thread.
|
||||
|
||||
// NOTE: Logging ///////////////////////////////////////////////////////////////////////////////
|
||||
DN_LOGEmitFromTypeFVFunc * log_callback; // Set this pointer to override the logging routine
|
||||
void * log_user_data; // User pointer passed into 'log_callback'
|
||||
bool log_to_file; // Output logs to file as well as standard out
|
||||
DN_OSFile log_file; // TODO(dn): Hmmm, how should we do this... ?
|
||||
DN_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
|
||||
|
||||
// NOTE: OS //////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_U32 logical_processor_count;
|
||||
DN_U32 page_size;
|
||||
DN_U32 alloc_granularity;
|
||||
|
||||
// NOTE: Memory ////////////////////////////////////////////////////////////////////////////////
|
||||
// Total OS mem allocs in lifetime of program (e.g. malloc, VirtualAlloc, HeapAlloc ...). This
|
||||
// only includes allocations routed through the library such as the growing nature of arenas or
|
||||
// using the memory allocation routines in the library like DN_OS_MemCommit and so forth.
|
||||
DN_U64 vmem_allocs_total;
|
||||
DN_U64 vmem_allocs_frame; // Total OS virtual memory allocs since the last 'DN_Core_FrameBegin' was invoked
|
||||
DN_U64 mem_allocs_total;
|
||||
DN_U64 mem_allocs_frame; // Total OS heap allocs since the last 'DN_Core_FrameBegin' was invoked
|
||||
|
||||
DN_Arena arena;
|
||||
void *platform_context;
|
||||
};
|
||||
|
||||
struct DN_OSDiskSpace
|
||||
{
|
||||
bool success;
|
||||
DN_U64 avail;
|
||||
DN_U64 size;
|
||||
};
|
||||
|
||||
DN_API void DN_OS_Init (DN_OSCore *os, DN_OSInitArgs *args);
|
||||
DN_API void DN_OS_EmitLogsWithOSPrintFunctions(DN_OSCore *os);
|
||||
DN_API void DN_OS_DumpThreadContextArenaStat (DN_Str8 file_path);
|
||||
|
||||
// NOTE: Memory ////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API void * DN_OS_MemReserve (DN_USize size, DN_MemCommit commit, DN_MemPage page_flags);
|
||||
DN_API bool DN_OS_MemCommit (void *ptr, DN_USize size, DN_U32 page_flags);
|
||||
DN_API void DN_OS_MemDecommit(void *ptr, DN_USize size);
|
||||
DN_API void DN_OS_MemRelease (void *ptr, DN_USize size);
|
||||
DN_API int DN_OS_MemProtect (void *ptr, DN_USize size, DN_U32 page_flags);
|
||||
|
||||
// NOTE: Heap
|
||||
DN_API void * DN_OS_MemAlloc (DN_USize size, DN_ZeroMem zero_mem);
|
||||
DN_API void DN_OS_MemDealloc (void *ptr);
|
||||
|
||||
// NOTE: DN_OSDate /////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSDateTime DN_OS_DateLocalTimeNow ();
|
||||
DN_API DN_OSDateTimeStr8 DN_OS_DateLocalTimeStr8Now(char date_separator = '-', char hms_separator = ':');
|
||||
DN_API DN_OSDateTimeStr8 DN_OS_DateLocalTimeStr8 (DN_OSDateTime time, char date_separator = '-', char hms_separator = ':');
|
||||
DN_API DN_U64 DN_OS_DateUnixTimeNs ();
|
||||
#define DN_OS_DateUnixTimeUs() (DN_OS_DateUnixTimeNs() / 1000)
|
||||
#define DN_OS_DateUnixTimeMs() (DN_OS_DateUnixTimeNs() / (1000 * 1000))
|
||||
#define DN_OS_DateUnixTimeS() (DN_OS_DateUnixTimeNs() / (1000 * 1000 * 1000))
|
||||
DN_API DN_OSDateTime DN_OS_DateUnixTimeSToDate (DN_U64 time);
|
||||
DN_API DN_U64 DN_OS_DateLocalToUnixTimeS(DN_OSDateTime date);
|
||||
DN_API DN_U64 DN_OS_DateToUnixTimeS (DN_OSDateTime date);
|
||||
DN_API bool DN_OS_DateIsValid (DN_OSDateTime date);
|
||||
|
||||
// NOTE: Other /////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API bool DN_OS_SecureRNGBytes (void *buffer, DN_U32 size);
|
||||
DN_API bool DN_OS_SetEnvVar (DN_Str8 name, DN_Str8 value);
|
||||
DN_API DN_OSDiskSpace DN_OS_DiskSpace (DN_Str8 path);
|
||||
DN_API DN_Str8 DN_OS_EXEPath (DN_Arena *arena);
|
||||
DN_API DN_Str8 DN_OS_EXEDir (DN_Arena *arena);
|
||||
#define DN_OS_EXEDirFromTLS() DN_OS_EXEDir(DN_OS_TLSTopArena())
|
||||
DN_API void DN_OS_SleepMs (DN_UInt milliseconds);
|
||||
|
||||
// NOTE: Counters //////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_U64 DN_OS_PerfCounterNow ();
|
||||
DN_API DN_U64 DN_OS_PerfCounterFrequency();
|
||||
DN_API DN_F64 DN_OS_PerfCounterS (DN_U64 begin, uint64_t end);
|
||||
DN_API DN_F64 DN_OS_PerfCounterMs (DN_U64 begin, uint64_t end);
|
||||
DN_API DN_F64 DN_OS_PerfCounterUs (DN_U64 begin, uint64_t end);
|
||||
DN_API DN_F64 DN_OS_PerfCounterNs (DN_U64 begin, uint64_t end);
|
||||
DN_API DN_OSTimer DN_OS_TimerBegin ();
|
||||
DN_API void DN_OS_TimerEnd (DN_OSTimer *timer);
|
||||
DN_API DN_F64 DN_OS_TimerS (DN_OSTimer timer);
|
||||
DN_API DN_F64 DN_OS_TimerMs (DN_OSTimer timer);
|
||||
DN_API DN_F64 DN_OS_TimerUs (DN_OSTimer timer);
|
||||
DN_API DN_F64 DN_OS_TimerNs (DN_OSTimer timer);
|
||||
DN_API DN_U64 DN_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_tsc_frequency);
|
||||
#if !defined(DN_NO_OS_FILE_API)
|
||||
// NOTE: File system paths /////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSPathInfo DN_OS_PathInfo (DN_Str8 path);
|
||||
DN_API bool DN_OS_FileIsOlderThan(DN_Str8 file, DN_Str8 check_against);
|
||||
DN_API bool DN_OS_PathDelete (DN_Str8 path);
|
||||
DN_API bool DN_OS_FileExists (DN_Str8 path);
|
||||
DN_API bool DN_OS_CopyFile (DN_Str8 src, DN_Str8 dest, bool overwrite, DN_OSErrSink *err);
|
||||
DN_API bool DN_OS_MoveFile (DN_Str8 src, DN_Str8 dest, bool overwrite, DN_OSErrSink *err);
|
||||
DN_API bool DN_OS_MakeDir (DN_Str8 path);
|
||||
DN_API bool DN_OS_DirExists (DN_Str8 path);
|
||||
DN_API bool DN_OS_DirIterate (DN_Str8 path, DN_OSDirIterator *it);
|
||||
|
||||
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSFile DN_OS_FileOpen (DN_Str8 path, DN_OSFileOpen open_mode, DN_OSFileAccess access, DN_OSErrSink *err);
|
||||
DN_API DN_OSFileRead DN_OS_FileRead (DN_OSFile *file, void *buffer, DN_USize size, DN_OSErrSink *err);
|
||||
DN_API bool DN_OS_FileWritePtr(DN_OSFile *file, void const *data, DN_USize size, DN_OSErrSink *err);
|
||||
DN_API bool DN_OS_FileWrite (DN_OSFile *file, DN_Str8 buffer, DN_OSErrSink *err);
|
||||
DN_API bool DN_OS_FileWriteFV (DN_OSFile *file, DN_OSErrSink *err, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API bool DN_OS_FileWriteF (DN_OSFile *file, DN_OSErrSink *err, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API bool DN_OS_FileFlush (DN_OSFile *file, DN_OSErrSink *err);
|
||||
DN_API void DN_OS_FileClose (DN_OSFile *file);
|
||||
|
||||
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Str8 DN_OS_ReadAll (DN_Arena *arena, DN_Str8 path, DN_OSErrSink *err);
|
||||
#define DN_OS_ReadAllFromTLS(...) DN_OS_ReadAll(DN_OS_TLSTopArena(), ##__VA_ARGS__)
|
||||
DN_API bool DN_OS_WriteAll (DN_Str8 path, DN_Str8 buffer, DN_OSErrSink *err);
|
||||
DN_API bool DN_OS_WriteAllFV (DN_Str8 path, DN_OSErrSink *err, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API bool DN_OS_WriteAllF (DN_Str8 path, DN_OSErrSink *err, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API bool DN_OS_WriteAllSafe (DN_Str8 path, DN_Str8 buffer, DN_OSErrSink *err);
|
||||
DN_API bool DN_OS_WriteAllSafeFV (DN_Str8 path, DN_OSErrSink *err, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API bool DN_OS_WriteAllSafeF (DN_Str8 path, DN_OSErrSink *err, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
#endif // !defined(DN_NO_OS_FILE_API)
|
||||
|
||||
// NOTE: File system paths /////////////////////////////////////////////////////////////////////////
|
||||
DN_API bool DN_OS_PathAddRef (DN_Arena *arena, DN_OSPath *fs_path, DN_Str8 path);
|
||||
#define DN_OS_PathAddRefFromTLS(...) DN_OS_PathAddRef(DN_OS_TLSTopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathAddRefFromFrame(...) DN_OS_PathAddRef(DN_OS_TLSFrameArena(), ##__VA_ARGS__)
|
||||
DN_API bool DN_OS_PathAdd (DN_Arena *arena, DN_OSPath *fs_path, DN_Str8 path);
|
||||
#define DN_OS_PathAddFromTLS(...) DN_OS_PathAdd(DN_OS_TLSTopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathAddFromFrame(...) DN_OS_PathAdd(DN_OS_TLSFrameArena(), ##__VA_ARGS__)
|
||||
DN_API bool DN_OS_PathAddF (DN_Arena *arena, DN_OSPath *fs_path, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
#define DN_OS_PathAddFFromTLS(...) DN_OS_PathAddF(DN_OS_TLSTopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathAddFFromFrame(...) DN_OS_PathAddF(DN_OS_TLSFrameArena(), ##__VA_ARGS__)
|
||||
DN_API bool DN_OS_PathPop (DN_OSPath *fs_path);
|
||||
DN_API DN_Str8 DN_OS_PathBuildWithSeparator (DN_Arena *arena, DN_OSPath const *fs_path, DN_Str8 path_separator);
|
||||
#define DN_OS_PathBuildWithSeperatorFromTLS(...) DN_OS_PathBuildWithSeperator(DN_OS_TLSTopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathBuildWithSeperatorFromFrame(...) DN_OS_PathBuildWithSeperator(DN_OS_TLSFrameArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_OS_PathTo (DN_Arena *arena, DN_Str8 path, DN_Str8 path_separtor);
|
||||
#define DN_OS_PathToFromTLS(...) DN_OS_PathTo(DN_OS_TLSTopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathToFromFrame(...) DN_OS_PathTo(DN_OS_TLSFrameArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_OS_PathToF (DN_Arena *arena, DN_Str8 path_separator, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
#define DN_OS_PathToFFromTLS(...) DN_OS_PathToF(DN_OS_TLSTopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathToFFromFrame(...) DN_OS_PathToF(DN_OS_TLSFrameArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_OS_Path (DN_Arena *arena, DN_Str8 path);
|
||||
#define DN_OS_PathFromTLS(...) DN_OS_Path(DN_OS_TLSTopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathFromFrame(...) DN_OS_Path(DN_OS_TLSFrameArena(), ##__VA_ARGS__)
|
||||
DN_API DN_Str8 DN_OS_PathF (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
#define DN_OS_PathFFromTLS(...) DN_OS_PathF(DN_OS_TLSTopArena(), ##__VA_ARGS__)
|
||||
#define DN_OS_PathFFromFrame(...) DN_OS_PathF(DN_OS_TLSFrameArena(), ##__VA_ARGS__)
|
||||
|
||||
#define DN_OS_PathBuildFwdSlash(allocator, fs_path) DN_OS_PathBuildWithSeparator(allocator, fs_path, DN_STR8("/"))
|
||||
#define DN_OS_PathBuildBackSlash(allocator, fs_path) DN_OS_PathBuildWithSeparator(allocator, fs_path, DN_STR8("\\"))
|
||||
#define DN_OS_PathBuild(allocator, fs_path) DN_OS_PathBuildWithSeparator(allocator, fs_path, DN_OSPathSeparatorString)
|
||||
|
||||
// NOTE: DN_OSExec /////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_OS_Exit (int32_t exit_code);
|
||||
DN_API DN_OSExecResult DN_OS_ExecPump (DN_OSExecAsyncHandle handle, char *stdout_buffer, size_t *stdout_size, char *stderr_buffer, size_t *stderr_size, DN_U32 timeout_ms, DN_OSErrSink *err);
|
||||
DN_API DN_OSExecResult DN_OS_ExecWait (DN_OSExecAsyncHandle handle, DN_Arena *arena, DN_OSErrSink *err);
|
||||
DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync (DN_Slice<DN_Str8> cmd_line, DN_OSExecArgs *args, DN_OSErrSink *err);
|
||||
DN_API DN_OSExecResult DN_OS_Exec (DN_Slice<DN_Str8> cmd_line, DN_OSExecArgs *args, DN_Arena *arena, DN_OSErrSink *err);
|
||||
DN_API DN_OSExecResult DN_OS_ExecOrAbort (DN_Slice<DN_Str8> cmd_line, DN_OSExecArgs *args, DN_Arena *arena);
|
||||
#define DN_OS_ExecOrAbortFromTLS(...) DN_OS_ExecOrAbort(__VA_ARGS__, DN_OS_TLSTopArena())
|
||||
|
||||
DN_API DN_OSSemaphore DN_OS_SemaphoreInit (DN_U32 initial_count);
|
||||
DN_API bool DN_OS_SemaphoreIsValid (DN_OSSemaphore *semaphore);
|
||||
DN_API void DN_OS_SemaphoreDeinit (DN_OSSemaphore *semaphore);
|
||||
DN_API void DN_OS_SemaphoreIncrement(DN_OSSemaphore *semaphore, DN_U32 amount);
|
||||
DN_API DN_OSSemaphoreWaitResult DN_OS_SemaphoreWait (DN_OSSemaphore *semaphore, DN_U32 timeout_ms);
|
||||
|
||||
DN_API DN_OSMutex DN_OS_MutexInit ();
|
||||
DN_API void DN_OS_MutexDeinit(DN_OSMutex *mutex);
|
||||
DN_API void DN_OS_MutexLock (DN_OSMutex *mutex);
|
||||
DN_API void DN_OS_MutexUnlock(DN_OSMutex *mutex);
|
||||
#define DN_OS_MutexScope(mutex) DN_DeferLoop(DN_OS_MutexLock(mutex), DN_OS_MutexUnlock(mutex))
|
||||
|
||||
DN_API DN_OSConditionVariable DN_OS_ConditionVariableInit ();
|
||||
DN_API void DN_OS_ConditionVariableDeinit (DN_OSConditionVariable *cv);
|
||||
DN_API bool DN_OS_ConditionVariableWait (DN_OSConditionVariable *cv, DN_OSMutex *mutex, DN_U64 sleep_ms);
|
||||
DN_API bool DN_OS_ConditionVariableWaitUntil(DN_OSConditionVariable *cv, DN_OSMutex *mutex, DN_U64 end_ts_ms);
|
||||
DN_API void DN_OS_ConditionVariableSignal (DN_OSConditionVariable *cv);
|
||||
DN_API void DN_OS_ConditionVariableBroadcast(DN_OSConditionVariable *cv);
|
||||
|
||||
DN_API bool DN_OS_ThreadInit (DN_OSThread *thread, DN_OSThreadFunc *func, void *user_context);
|
||||
DN_API void DN_OS_ThreadDeinit(DN_OSThread *thread);
|
||||
DN_API DN_U32 DN_OS_ThreadID ();
|
||||
DN_API void DN_OS_ThreadSetName(DN_Str8 name);
|
||||
|
||||
DN_API void DN_OS_HttpRequestAsync(DN_OSHttpResponse *response, DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers);
|
||||
DN_API void DN_OS_HttpRequestWait (DN_OSHttpResponse *response);
|
||||
DN_API void DN_OS_HttpRequestFree (DN_OSHttpResponse *response);
|
||||
DN_API DN_OSHttpResponse DN_OS_HttpRequest (DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers);
|
||||
#endif // !defined(DN_OS_H)
|
||||
@@ -0,0 +1,30 @@
|
||||
#define DN_OS_ALLOCATOR_CPP
|
||||
|
||||
static void *DN_Arena_BasicAllocFromOSHeap(DN_USize size)
|
||||
{
|
||||
void *result = DN_OS_MemAlloc(size, DN_ZeroMem_Yes);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Arena DN_Arena_InitFromOSHeap(DN_U64 size, DN_ArenaFlags flags)
|
||||
{
|
||||
DN_ArenaMemFuncs mem_funcs = {};
|
||||
mem_funcs.type = DN_ArenaMemFuncType_Basic;
|
||||
mem_funcs.basic_alloc = DN_Arena_BasicAllocFromOSHeap;
|
||||
mem_funcs.basic_dealloc = DN_OS_MemDealloc;
|
||||
DN_Arena result = DN_Arena_InitFromMemFuncs(size, size, flags, mem_funcs);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Arena DN_Arena_InitFromOSVMem(DN_U64 reserve, DN_U64 commit, DN_ArenaFlags flags)
|
||||
{
|
||||
DN_ArenaMemFuncs mem_funcs = {};
|
||||
mem_funcs.type = DN_ArenaMemFuncType_VMem;
|
||||
mem_funcs.vmem_page_size = g_dn_os_core_->page_size;
|
||||
mem_funcs.vmem_reserve = DN_OS_MemReserve;
|
||||
mem_funcs.vmem_commit = DN_OS_MemCommit;
|
||||
mem_funcs.vmem_release = DN_OS_MemRelease;
|
||||
DN_Arena result = DN_Arena_InitFromMemFuncs(reserve, commit, flags, mem_funcs);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#if !defined(DN_OS_ALLOCATOR_H)
|
||||
#define DN_OS_ALLOCATOR_H
|
||||
|
||||
DN_API DN_Arena DN_Arena_InitFromOSHeap(DN_U64 size, DN_ArenaFlags flags);
|
||||
DN_API DN_Arena DN_Arena_InitFromOSVMem(DN_U64 reserve, DN_U64 commit, DN_ArenaFlags flags);
|
||||
|
||||
#endif // !defined(DN_OS_ALLOCATOR_H)
|
||||
@@ -0,0 +1,202 @@
|
||||
#define DN_OS_CONTAINERS_CPP
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$\ $$$$$$\
|
||||
// $$ __$$\ $$ __$$\ $$$\ $$ |\__$$ __|$$ __$$\ \_$$ _|$$$\ $$ |$$ _____|$$ __$$\ $$ __$$\
|
||||
// $$ / \__|$$ / $$ |$$$$\ $$ | $$ | $$ / $$ | $$ | $$$$\ $$ |$$ | $$ | $$ |$$ / \__|
|
||||
// $$ | $$ | $$ |$$ $$\$$ | $$ | $$$$$$$$ | $$ | $$ $$\$$ |$$$$$\ $$$$$$$ |\$$$$$$\
|
||||
// $$ | $$ | $$ |$$ \$$$$ | $$ | $$ __$$ | $$ | $$ \$$$$ |$$ __| $$ __$$< \____$$\
|
||||
// $$ | $$\ $$ | $$ |$$ |\$$$ | $$ | $$ | $$ | $$ | $$ |\$$$ |$$ | $$ | $$ |$$\ $$ |
|
||||
// \$$$$$$ | $$$$$$ |$$ | \$$ | $$ | $$ | $$ |$$$$$$\ $$ | \$$ |$$$$$$$$\ $$ | $$ |\$$$$$$ |
|
||||
// \______/ \______/ \__| \__| \__| \__| \__|\______|\__| \__|\________|\__| \__| \______/
|
||||
//
|
||||
// dn_containers.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: DN_VArray /////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
DN_VArray<T> DN_VArray_InitByteSize(DN_USize byte_size)
|
||||
{
|
||||
DN_VArray<T> result = {};
|
||||
result.data = DN_CAST(T *) DN_OS_MemReserve(byte_size, DN_MemCommit_No, DN_MemPage_ReadWrite);
|
||||
if (result.data)
|
||||
result.max = byte_size / sizeof(T);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_VArray<T> DN_VArray_Init(DN_USize max)
|
||||
{
|
||||
DN_VArray<T> result = DN_VArray_InitByteSize<T>(max * sizeof(T));
|
||||
DN_Assert(result.max >= max);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_VArray<T> DN_VArray_InitSlice(DN_Slice<T> slice, DN_USize max)
|
||||
{
|
||||
DN_USize real_max = DN_Max(slice.size, max);
|
||||
DN_VArray<T> result = DN_VArray_Init<T>(real_max);
|
||||
if (DN_VArray_IsValid(&result))
|
||||
DN_VArray_AddArray(&result, slice.data, slice.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, DN_USize N>
|
||||
DN_VArray<T> DN_VArray_InitCArray(T const (&items)[N], DN_USize max)
|
||||
{
|
||||
DN_USize real_max = DN_Max(N, max);
|
||||
DN_VArray<T> result = DN_VArray_InitSlice(DN_Slice_Init(items, N), real_max);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DN_VArray_Deinit(DN_VArray<T> *array)
|
||||
{
|
||||
DN_OS_MemRelease(array->data, array->max * sizeof(T));
|
||||
*array = {};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool DN_VArray_IsValid(DN_VArray<T> const *array)
|
||||
{
|
||||
bool result = array->data && array->size <= array->max;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_Slice<T> DN_VArray_Slice(DN_VArray<T> const *array)
|
||||
{
|
||||
DN_Slice<T> result = {};
|
||||
if (array)
|
||||
result = DN_Slice_Init<T>(array->data, array->size);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *DN_VArray_AddArray(DN_VArray<T> *array, T const *items, DN_USize count)
|
||||
{
|
||||
T *result = DN_VArray_MakeArray(array, count, DN_ZeroMem_No);
|
||||
if (result)
|
||||
DN_Memcpy(result, items, count * sizeof(T));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, DN_USize N>
|
||||
T *DN_VArray_AddCArray(DN_VArray<T> *array, T const (&items)[N])
|
||||
{
|
||||
T *result = DN_VArray_AddArray(array, items, N);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *DN_VArray_Add(DN_VArray<T> *array, T const &item)
|
||||
{
|
||||
T *result = DN_VArray_AddArray(array, &item, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *DN_VArray_MakeArray(DN_VArray<T> *array, DN_USize count, DN_ZeroMem zero_mem)
|
||||
{
|
||||
if (!DN_VArray_IsValid(array))
|
||||
return nullptr;
|
||||
|
||||
if (!DN_CheckF((array->size + count) < array->max, "Array is out of space (user requested +%zu items, array has %zu/%zu items)", count, array->size, array->max))
|
||||
return nullptr;
|
||||
|
||||
if (!DN_VArray_Reserve(array, count))
|
||||
return nullptr;
|
||||
|
||||
// TODO: Use placement new
|
||||
T *result = array->data + array->size;
|
||||
array->size += count;
|
||||
if (zero_mem == DN_ZeroMem_Yes)
|
||||
DN_Memset(result, 0, count * sizeof(T));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *DN_VArray_Make(DN_VArray<T> *array, DN_ZeroMem zero_mem)
|
||||
{
|
||||
T *result = DN_VArray_MakeArray(array, 1, zero_mem);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *DN_VArray_InsertArray(DN_VArray<T> *array, DN_USize index, T const *items, DN_USize count)
|
||||
{
|
||||
T *result = nullptr;
|
||||
if (!DN_VArray_IsValid(array))
|
||||
return result;
|
||||
if (DN_VArray_Reserve(array, array->size + count))
|
||||
result = DN_CArray_InsertArray(array->data, &array->size, array->max, index, items, count);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, DN_USize N>
|
||||
T *DN_VArray_InsertCArray(DN_VArray<T> *array, DN_USize index, T const (&items)[N])
|
||||
{
|
||||
T *result = DN_VArray_InsertArray(array, index, items, N);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *DN_VArray_Insert(DN_VArray<T> *array, DN_USize index, T const &item)
|
||||
{
|
||||
T *result = DN_VArray_InsertArray(array, index, &item, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *DN_VArray_PopFront(DN_VArray<T> *array, DN_USize count)
|
||||
{
|
||||
T *result = DN_CArray_PopFront(array->data, &array->size, count);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *DN_VArray_PopBack(DN_VArray<T> *array, DN_USize count)
|
||||
{
|
||||
T *result = DN_CArray_PopBack(array->data, &array->size, count);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_ArrayEraseResult DN_VArray_EraseRange(DN_VArray<T> *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase)
|
||||
{
|
||||
DN_ArrayEraseResult result = {};
|
||||
if (!DN_VArray_IsValid(array))
|
||||
return result;
|
||||
result = DN_CArray_EraseRange<T>(array->data, &array->size, begin_index, count, erase);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DN_VArray_Clear(DN_VArray<T> *array, DN_ZeroMem zero_mem)
|
||||
{
|
||||
if (array) {
|
||||
if (zero_mem == DN_ZeroMem_Yes)
|
||||
DN_Memset(array->data, 0, array->size * sizeof(T));
|
||||
array->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool DN_VArray_Reserve(DN_VArray<T> *array, DN_USize count)
|
||||
{
|
||||
if (!DN_VArray_IsValid(array) || count == 0)
|
||||
return false;
|
||||
|
||||
DN_USize real_commit = (array->size + count) * sizeof(T);
|
||||
DN_USize aligned_commit = DN_AlignUpPowerOfTwo(real_commit, g_dn_os_core_->page_size);
|
||||
if (array->commit >= aligned_commit)
|
||||
return true;
|
||||
bool result = DN_OS_MemCommit(array->data, aligned_commit, DN_MemPage_ReadWrite);
|
||||
array->commit = aligned_commit;
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#if !defined(DN_OS_CONTAINERS_H)
|
||||
#define DN_OS_CONTAINERS_H
|
||||
|
||||
// NOTE: DN_VArray /////////////////////////////////////////////////////////////////////////////////
|
||||
// TODO(doyle): Add an API for shrinking the array by decomitting pages back to the OS.
|
||||
template <typename T> struct DN_VArray
|
||||
{
|
||||
T *data; // Pointer to the start of the array items in the block of memory
|
||||
DN_USize size; // Number of items currently in the array
|
||||
DN_USize max; // Maximum number of items this array can store
|
||||
DN_USize commit; // Bytes committed
|
||||
|
||||
T *begin() { return data; }
|
||||
T *end () { return data + size; }
|
||||
T const *begin() const { return data; }
|
||||
T const *end () const { return data + size; }
|
||||
};
|
||||
|
||||
template <typename T> DN_VArray<T> DN_VArray_InitByteSize (DN_USize byte_size);
|
||||
template <typename T> DN_VArray<T> DN_VArray_Init (DN_USize max);
|
||||
template <typename T> DN_VArray<T> DN_VArray_InitSlice (DN_Slice<T> slice, DN_USize max);
|
||||
template <typename T, DN_USize N> DN_VArray<T> DN_VArray_InitCArray (T const (&items)[N], DN_USize max);
|
||||
template <typename T> void DN_VArray_Deinit (DN_VArray<T> *array);
|
||||
template <typename T> bool DN_VArray_IsValid (DN_VArray<T> const *array);
|
||||
template <typename T> DN_Slice<T> DN_VArray_Slice (DN_VArray<T> const *array);
|
||||
template <typename T> bool DN_VArray_Reserve (DN_VArray<T> *array, DN_USize count);
|
||||
template <typename T> T * DN_VArray_AddArray (DN_VArray<T> *array, T const *items, DN_USize count);
|
||||
template <typename T, DN_USize N> T * DN_VArray_AddCArray (DN_VArray<T> *array, T const (&items)[N]);
|
||||
template <typename T> T * DN_VArray_Add (DN_VArray<T> *array, T const &item);
|
||||
#define DN_VArray_AddArrayAssert(...) DN_HardAssert(DN_VArray_AddArray(__VA_ARGS__))
|
||||
#define DN_VArray_AddCArrayAssert(...) DN_HardAssert(DN_VArray_AddCArray(__VA_ARGS__))
|
||||
#define DN_VArray_AddAssert(...) DN_HardAssert(DN_VArray_Add(__VA_ARGS__))
|
||||
template <typename T> T * DN_VArray_MakeArray (DN_VArray<T> *array, DN_USize count, DN_ZeroMem zero_mem);
|
||||
template <typename T> T * DN_VArray_Make (DN_VArray<T> *array, DN_ZeroMem zero_mem);
|
||||
#define DN_VArray_MakeArrayAssert(...) DN_HardAssert(DN_VArray_MakeArray(__VA_ARGS__))
|
||||
#define DN_VArray_MakeAssert(...) DN_HardAssert(DN_VArray_Make(__VA_ARGS__))
|
||||
template <typename T> T * DN_VArray_InsertArray (DN_VArray<T> *array, DN_USize index, T const *items, DN_USize count);
|
||||
template <typename T, DN_USize N> T * DN_VArray_InsertCArray (DN_VArray<T> *array, DN_USize index, T const (&items)[N]);
|
||||
template <typename T> T * DN_VArray_Insert (DN_VArray<T> *array, DN_USize index, T const &item);
|
||||
#define DN_VArray_InsertArrayAssert(...) DN_HardAssert(DN_VArray_InsertArray(__VA_ARGS__))
|
||||
#define DN_VArray_InsertCArrayAssert(...) DN_HardAssert(DN_VArray_InsertCArray(__VA_ARGS__))
|
||||
#define DN_VArray_InsertAssert(...) DN_HardAssert(DN_VArray_Insert(__VA_ARGS__))
|
||||
template <typename T> T DN_VArray_PopFront (DN_VArray<T> *array, DN_USize count);
|
||||
template <typename T> T DN_VArray_PopBack (DN_VArray<T> *array, DN_USize count);
|
||||
template <typename T> DN_ArrayEraseResult DN_VArray_EraseRange (DN_VArray<T> *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
|
||||
template <typename T> void DN_VArray_Clear (DN_VArray<T> *array, DN_ZeroMem zero_mem);
|
||||
#endif // !defined(DN_OS_CONTAINERS_H)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\
|
||||
// $$ __$$\ $$ __$$\ $$ __$$\ $$ __$$\ $$ __$$\ \_$$ _|$$ | $$ |
|
||||
// $$ / $$ |$$ / \__| $$ | $$ |$$ / $$ |$$ / \__| $$ | \$$\ $$ |
|
||||
// $$ | $$ |\$$$$$$\ $$$$$$$ |$$ | $$ |\$$$$$$\ $$ | \$$$$ /
|
||||
// $$ | $$ | \____$$\ $$ ____/ $$ | $$ | \____$$\ $$ | $$ $$<
|
||||
// $$ | $$ |$$\ $$ | $$ | $$ | $$ |$$\ $$ | $$ | $$ /\$$\
|
||||
// $$$$$$ |\$$$$$$ | $$ | $$$$$$ |\$$$$$$ |$$$$$$\ $$ / $$ |
|
||||
// \______/ \______/ \__| \______/ \______/ \______|\__| \__|
|
||||
//
|
||||
// dn_os_posix.h
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
struct DN_POSIXProcSelfStatus
|
||||
{
|
||||
char name[64];
|
||||
DN_U8 name_size;
|
||||
DN_U32 pid;
|
||||
DN_U64 vm_peak;
|
||||
DN_U32 vm_size;
|
||||
};
|
||||
|
||||
// NOTE: The POSIX implementation disallows copies of synchronisation objects in
|
||||
// general hence we have to dynamically allocate these primitives to maintain a
|
||||
// consistent address.
|
||||
//
|
||||
//
|
||||
// Source: The Open Group Base Specifications Issue 7, 2018 edition
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_09
|
||||
//
|
||||
// 2.9.9 Synchronization Object Copies and Alternative Mappings
|
||||
//
|
||||
// For barriers, condition variables, mutexes, and read-write locks, [TSH]
|
||||
// [Option Start] if the process-shared attribute is set to
|
||||
// PTHREAD_PROCESS_PRIVATE, [Option End] only the synchronization object at the
|
||||
// address used to initialize it can be used for performing synchronization. The
|
||||
// effect of referring to another mapping of the same object when locking,
|
||||
// unlocking, or destroying the object is undefined. [...] The effect of
|
||||
// referring to a copy of the object when locking, unlocking, or destroying it
|
||||
// is undefined.
|
||||
|
||||
enum DN_POSIXSyncPrimitiveType
|
||||
{
|
||||
DN_OSPOSIXSyncPrimitiveType_Semaphore,
|
||||
DN_OSPOSIXSyncPrimitiveType_Mutex,
|
||||
DN_OSPOSIXSyncPrimitiveType_ConditionVariable,
|
||||
};
|
||||
|
||||
struct DN_POSIXSyncPrimitive
|
||||
{
|
||||
union
|
||||
{
|
||||
sem_t sem;
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cv;
|
||||
};
|
||||
DN_POSIXSyncPrimitive *next;
|
||||
};
|
||||
|
||||
struct DN_POSIXCore
|
||||
{
|
||||
DN_POSIXSyncPrimitive *sync_primitive_free_list;
|
||||
pthread_mutex_t sync_primitive_free_list_mutex;
|
||||
};
|
||||
|
||||
DN_API void DN_Posix_ThreadSetName(DN_Str8 name);
|
||||
DN_API DN_POSIXProcSelfStatus DN_Posix_ProcSelfStatus();
|
||||
@@ -0,0 +1,170 @@
|
||||
#define DN_OS_PRINT_CPP
|
||||
|
||||
DN_API DN_LOGStyle DN_OS_PrintStyleColour(uint8_t r, uint8_t g, uint8_t b, DN_LOGBold bold)
|
||||
{
|
||||
DN_LOGStyle result = {};
|
||||
result.bold = bold;
|
||||
result.colour = true;
|
||||
result.r = r;
|
||||
result.g = g;
|
||||
result.b = b;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_LOGStyle DN_OS_PrintStyleColourU32(uint32_t rgb, DN_LOGBold bold)
|
||||
{
|
||||
uint8_t r = (rgb >> 24) & 0xFF;
|
||||
uint8_t g = (rgb >> 16) & 0xFF;
|
||||
uint8_t b = (rgb >> 8) & 0xFF;
|
||||
DN_LOGStyle result = DN_OS_PrintStyleColour(r, g, b, bold);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_LOGStyle DN_OS_PrintStyleBold()
|
||||
{
|
||||
DN_LOGStyle result = {};
|
||||
result.bold = DN_LOGBold_Yes;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_Print(DN_OSPrintDest dest, DN_Str8 string)
|
||||
{
|
||||
DN_Assert(dest == DN_OSPrintDest_Out || dest == DN_OSPrintDest_Err);
|
||||
|
||||
#if defined(DN_PLATFORM_WIN32)
|
||||
// NOTE: Get the output handles from kernel ////////////////////////////////////////////////////
|
||||
DN_THREAD_LOCAL void *std_out_print_handle = nullptr;
|
||||
DN_THREAD_LOCAL void *std_err_print_handle = nullptr;
|
||||
DN_THREAD_LOCAL bool std_out_print_to_console = false;
|
||||
DN_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 (dest == DN_OSPrintDest_Err) {
|
||||
print_handle = std_err_print_handle;
|
||||
print_to_console = std_err_print_to_console;
|
||||
}
|
||||
|
||||
// NOTE: Write the string //////////////////////////////////////////////////////////////////////
|
||||
DN_Assert(string.size < DN_CAST(unsigned long) - 1);
|
||||
unsigned long bytes_written = 0;
|
||||
(void)bytes_written;
|
||||
if (print_to_console)
|
||||
WriteConsoleA(print_handle, string.data, DN_CAST(unsigned long) string.size, &bytes_written, nullptr);
|
||||
else
|
||||
WriteFile(print_handle, string.data, DN_CAST(unsigned long) string.size, &bytes_written, nullptr);
|
||||
#else
|
||||
fprintf(dest == DN_OSPrintDest_Out ? stdout : stderr, "%.*s", DN_STR_FMT(string));
|
||||
#endif
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintF(DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_OS_PrintFV(dest, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintFStyle(DN_OSPrintDest dest, DN_LOGStyle style, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_OS_PrintFVStyle(dest, style, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintStyle(DN_OSPrintDest dest, DN_LOGStyle style, DN_Str8 string)
|
||||
{
|
||||
if (string.data && string.size) {
|
||||
if (style.colour)
|
||||
DN_OS_Print(dest, DN_LOG_ColourEscapeCodeStr8FromRGB(DN_LOGColourType_Fg, style.r, style.g, style.b));
|
||||
if (style.bold == DN_LOGBold_Yes)
|
||||
DN_OS_Print(dest, DN_STR8(DN_LOG_BoldEscapeCode));
|
||||
DN_OS_Print(dest, string);
|
||||
if (style.colour || style.bold == DN_LOGBold_Yes)
|
||||
DN_OS_Print(dest, DN_STR8(DN_LOG_ResetEscapeCode));
|
||||
}
|
||||
}
|
||||
|
||||
static char *DN_OS_PrintVSPrintfChunker_(const char *buf, void *user, int len)
|
||||
{
|
||||
DN_Str8 string = {};
|
||||
string.data = DN_CAST(char *) buf;
|
||||
string.size = len;
|
||||
|
||||
DN_OSPrintDest dest = DN_CAST(DN_OSPrintDest) DN_CAST(uintptr_t) user;
|
||||
DN_OS_Print(dest, string);
|
||||
return (char *)buf;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintFV(DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
char buffer[STB_SPRINTF_MIN];
|
||||
STB_SPRINTF_DECORATE(vsprintfcb)
|
||||
(DN_OS_PrintVSPrintfChunker_, DN_CAST(void *) DN_CAST(uintptr_t) dest, buffer, fmt, args);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintFVStyle(DN_OSPrintDest dest, DN_LOGStyle style, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
if (fmt) {
|
||||
if (style.colour)
|
||||
DN_OS_Print(dest, DN_LOG_ColourEscapeCodeStr8FromRGB(DN_LOGColourType_Fg, style.r, style.g, style.b));
|
||||
if (style.bold == DN_LOGBold_Yes)
|
||||
DN_OS_Print(dest, DN_STR8(DN_LOG_BoldEscapeCode));
|
||||
DN_OS_PrintFV(dest, fmt, args);
|
||||
if (style.colour || style.bold == DN_LOGBold_Yes)
|
||||
DN_OS_Print(dest, DN_STR8(DN_LOG_ResetEscapeCode));
|
||||
}
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintLn(DN_OSPrintDest dest, DN_Str8 string)
|
||||
{
|
||||
DN_OS_Print(dest, string);
|
||||
DN_OS_Print(dest, DN_STR8("\n"));
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintLnF(DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_OS_PrintLnFV(dest, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintLnFV(DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_OS_PrintFV(dest, fmt, args);
|
||||
DN_OS_Print(dest, DN_STR8("\n"));
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintLnStyle(DN_OSPrintDest dest, DN_LOGStyle style, DN_Str8 string)
|
||||
{
|
||||
DN_OS_PrintStyle(dest, style, string);
|
||||
DN_OS_Print(dest, DN_STR8("\n"));
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintLnFStyle(DN_OSPrintDest dest, DN_LOGStyle style, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_OS_PrintLnFVStyle(dest, style, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_PrintLnFVStyle(DN_OSPrintDest dest, DN_LOGStyle style, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_OS_PrintFVStyle(dest, style, fmt, args);
|
||||
DN_OS_Print(dest, DN_STR8("\n"));
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#if !defined(DN_OS_PRINT_H)
|
||||
#define DN_OS_PRINT_H
|
||||
|
||||
enum DN_OSPrintDest
|
||||
{
|
||||
DN_OSPrintDest_Out,
|
||||
DN_OSPrintDest_Err,
|
||||
};
|
||||
|
||||
// NOTE: Print Macros //////////////////////////////////////////////////////////////////////////////
|
||||
#define DN_OS_PrintOut(string) DN_OS_Print(DN_OSPrintDest_Out, string)
|
||||
#define DN_OS_PrintOutF(fmt, ...) DN_OS_PrintF(DN_OSPrintDest_Out, fmt, ##__VA_ARGS__)
|
||||
#define DN_OS_PrintOutFV(fmt, args) DN_OS_PrintFV(DN_OSPrintDest_Out, fmt, args)
|
||||
|
||||
#define DN_OS_PrintOutStyle(style, string) DN_OS_PrintStyle(DN_OSPrintDest_Out, style, string)
|
||||
#define DN_OS_PrintOutFStyle(style, fmt, ...) DN_OS_PrintFStyle(DN_OSPrintDest_Out, style, fmt, ##__VA_ARGS__)
|
||||
#define DN_OS_PrintOutFVStyle(style, fmt, args, ...) DN_OS_PrintFVStyle(DN_OSPrintDest_Out, style, fmt, args)
|
||||
|
||||
#define DN_OS_PrintOutLn(string) DN_OS_PrintLn(DN_OSPrintDest_Out, string)
|
||||
#define DN_OS_PrintOutLnF(fmt, ...) DN_OS_PrintLnF(DN_OSPrintDest_Out, fmt, ##__VA_ARGS__)
|
||||
#define DN_OS_PrintOutLnFV(fmt, args) DN_OS_PrintLnFV(DN_OSPrintDest_Out, fmt, args)
|
||||
|
||||
#define DN_OS_PrintOutLnStyle(style, string) DN_OS_PrintLnStyle(DN_OSPrintDest_Out, style, string);
|
||||
#define DN_OS_PrintOutLnFStyle(style, fmt, ...) DN_OS_PrintLnFStyle(DN_OSPrintDest_Out, style, fmt, ##__VA_ARGS__)
|
||||
#define DN_OS_PrintOutLnFVStyle(style, fmt, args) DN_OS_PrintLnFVStyle(DN_OSPrintDest_Out, style, fmt, args);
|
||||
|
||||
#define DN_OS_PrintErr(string) DN_OS_Print(DN_OSPrintDest_Err, string)
|
||||
#define DN_OS_PrintErrF(fmt, ...) DN_OS_PrintF(DN_OSPrintDest_Err, fmt, ##__VA_ARGS__)
|
||||
#define DN_OS_PrintErrFV(fmt, args) DN_OS_PrintFV(DN_OSPrintDest_Err, fmt, args)
|
||||
|
||||
#define DN_OS_PrintErrStyle(style, string) DN_OS_PrintStyle(DN_OSPrintDest_Err, style, string)
|
||||
#define DN_OS_PrintErrFStyle(style, fmt, ...) DN_OS_PrintFStyle(DN_OSPrintDest_Err, style, fmt, ##__VA_ARGS__)
|
||||
#define DN_OS_PrintErrFVStyle(style, fmt, args, ...) DN_OS_PrintFVStyle(DN_OSPrintDest_Err, style, fmt, args)
|
||||
|
||||
#define DN_OS_PrintErrLn(string) DN_OS_PrintLn(DN_OSPrintDest_Err, string)
|
||||
#define DN_OS_PrintErrLnF(fmt, ...) DN_OS_PrintLnF(DN_OSPrintDest_Err, fmt, ##__VA_ARGS__)
|
||||
#define DN_OS_PrintErrLnFV(fmt, args) DN_OS_PrintLnFV(DN_OSPrintDest_Err, fmt, args)
|
||||
|
||||
#define DN_OS_PrintErrLnStyle(style, string) DN_OS_PrintLnStyle(DN_OSPrintDest_Err, style, string);
|
||||
#define DN_OS_PrintErrLnFStyle(style, fmt, ...) DN_OS_PrintLnFStyle(DN_OSPrintDest_Err, style, fmt, ##__VA_ARGS__)
|
||||
#define DN_OS_PrintErrLnFVStyle(style, fmt, args) DN_OS_PrintLnFVStyle(DN_OSPrintDest_Err, style, fmt, args);
|
||||
|
||||
// NOTE: Print /////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_OS_Print (DN_OSPrintDest dest, DN_Str8 string);
|
||||
DN_API void DN_OS_PrintF (DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_OS_PrintFV (DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
|
||||
DN_API void DN_OS_PrintStyle (DN_OSPrintDest dest, DN_LOGStyle style, DN_Str8 string);
|
||||
DN_API void DN_OS_PrintFStyle (DN_OSPrintDest dest, DN_LOGStyle style, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_OS_PrintFVStyle (DN_OSPrintDest dest, DN_LOGStyle style, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
|
||||
DN_API void DN_OS_PrintLn (DN_OSPrintDest dest, DN_Str8 string);
|
||||
DN_API void DN_OS_PrintLnF (DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_OS_PrintLnFV (DN_OSPrintDest dest, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
|
||||
DN_API void DN_OS_PrintLnStyle (DN_OSPrintDest dest, DN_LOGStyle style, DN_Str8 string);
|
||||
DN_API void DN_OS_PrintLnFStyle (DN_OSPrintDest dest, DN_LOGStyle style, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_OS_PrintLnFVStyle (DN_OSPrintDest dest, DN_LOGStyle style, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
#endif // !defined(DN_OS_PRINT_H)
|
||||
@@ -0,0 +1,304 @@
|
||||
#define DN_OS_STRING_CPP
|
||||
|
||||
// NOTE: DN_Str8 ///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DN_API DN_Str8 DN_Str8_InitFFromFrame(DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 result = DN_Str8_InitFV(DN_OS_TLSGet()->frame_arena, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_InitFFromOSHeap(DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
DN_Str8 result = {};
|
||||
DN_USize size = DN_CStr8_FVSize(fmt, args);
|
||||
if (size) {
|
||||
result = DN_Str8_AllocFromOSHeap(size, DN_ZeroMem_No);
|
||||
if (DN_Str8_HasData(result))
|
||||
DN_VSNPrintF(result.data, DN_SaturateCastISizeToInt(size + 1 /*null-terminator*/), fmt, args);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_InitFFromTLS(DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 result = DN_Str8_InitFV(DN_OS_TLSTopArena(), fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_InitFVFromFrame(DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_InitFV(DN_OS_TLSGet()->frame_arena, fmt, args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_InitFVFromTLS(DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_InitFV(DN_OS_TLSTopArena(), fmt, args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_AllocFromFrame(DN_USize size, DN_ZeroMem zero_mem)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Alloc(DN_OS_TLSGet()->frame_arena, size, zero_mem);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_AllocFromOSHeap(DN_USize size, DN_ZeroMem zero_mem)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
result.data = DN_CAST(char *)DN_OS_MemAlloc(size + 1, zero_mem);
|
||||
if (result.data)
|
||||
result.size = size;
|
||||
result.data[result.size] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_AllocFromTLS(DN_USize size, DN_ZeroMem zero_mem)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Alloc(DN_OS_TLSTopArena(), size, zero_mem);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_CopyFromFrame(DN_Str8 string)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Copy(DN_OS_TLSGet()->frame_arena, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_CopyFromTLS(DN_Str8 string)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Copy(DN_OS_TLSTopArena(), string);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Slice<DN_Str8> DN_Str8_SplitAllocFromFrame(DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode)
|
||||
{
|
||||
DN_Slice<DN_Str8> result = DN_Str8_SplitAlloc(DN_OS_TLSGet()->frame_arena, string, delimiter, mode);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Slice<DN_Str8> DN_Str8_SplitAllocFromTLS(DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode)
|
||||
{
|
||||
DN_Slice<DN_Str8> result = DN_Str8_SplitAlloc(DN_OS_TLSTopArena(), string, delimiter, mode);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_SegmentFromFrame(DN_Str8 src, DN_USize segment_size, char segment_char)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Segment(DN_OS_TLSGet()->frame_arena, src, segment_size, segment_char);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_SegmentFromTLS(DN_Str8 src, DN_USize segment_size, char segment_char)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Segment(DN_OS_TLSTopArena(), src, segment_size, segment_char);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_ReverseSegmentFromFrame(DN_Str8 src, DN_USize segment_size, char segment_char)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_ReverseSegment(DN_OS_TLSGet()->frame_arena, src, segment_size, segment_char);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_ReverseSegmentFromTLS(DN_Str8 src, DN_USize segment_size, char segment_char)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_ReverseSegment(DN_OS_TLSTopArena(), src, segment_size, segment_char);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_AppendFFromFrame(DN_Str8 string, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 result = DN_Str8_AppendFV(DN_OS_TLSGet()->frame_arena, string, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_AppendFFromTLS(DN_Str8 string, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 result = DN_Str8_AppendFV(DN_OS_TLSTopArena(), string, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_FillFFromFrame(DN_USize count, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 result = DN_Str8_FillFV(DN_OS_TLSGet()->frame_arena, count, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_FillFFromTLS(DN_USize count, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_Str8 result = DN_Str8_FillFV(DN_OS_TLSTopArena(), count, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8DotTruncateResult DN_Str8_DotTruncateMiddleFromFrame(DN_Str8 str8, uint32_t side_size, DN_Str8 truncator)
|
||||
{
|
||||
DN_Str8DotTruncateResult result = DN_Str8_DotTruncateMiddle(DN_OS_TLSGet()->frame_arena, str8, side_size, truncator);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8DotTruncateResult DN_Str8_DotTruncateMiddleFromTLS(DN_Str8 str8, uint32_t side_size, DN_Str8 truncator)
|
||||
{
|
||||
DN_Str8DotTruncateResult result = DN_Str8_DotTruncateMiddle(DN_OS_TLSTopArena(), str8, side_size, truncator);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DN_API DN_Str8 DN_Str8_PadNewLines(DN_Arena *arena, DN_Str8 src, DN_Str8 pad)
|
||||
{
|
||||
// TODO: Implement this without requiring TLS so it can go into base strings
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSPushTMem(arena);
|
||||
DN_Str8Builder builder = DN_Str8Builder_InitFromTLS();
|
||||
|
||||
DN_Str8BinarySplitResult split = DN_Str8_BinarySplit(src, DN_STR8("\n"));
|
||||
while (split.lhs.size) {
|
||||
DN_Str8Builder_AppendRef(&builder, pad);
|
||||
DN_Str8Builder_AppendRef(&builder, split.lhs);
|
||||
split = DN_Str8_BinarySplit(split.rhs, DN_STR8("\n"));
|
||||
if (split.lhs.size)
|
||||
DN_Str8Builder_AppendRef(&builder, DN_STR8("\n"));
|
||||
}
|
||||
|
||||
DN_Str8 result = DN_Str8Builder_Build(&builder, arena);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_PadNewLinesFromFrame(DN_Str8 src, DN_Str8 pad)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_PadNewLines(DN_OS_TLSGet()->frame_arena, src, pad);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_PadNewLinesFromTLS(DN_Str8 src, DN_Str8 pad)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_PadNewLines(DN_OS_TLSTopArena(), src, pad);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_UpperFromFrame(DN_Str8 string)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Upper(DN_OS_TLSGet()->frame_arena, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_UpperFromTLS(DN_Str8 string)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Upper(DN_OS_TLSTopArena(), string);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_LowerFromFrame(DN_Str8 string)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Lower(DN_OS_TLSGet()->frame_arena, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_LowerFromTLS(DN_Str8 string)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Lower(DN_OS_TLSTopArena(), string);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_Replace(DN_Str8 string,
|
||||
DN_Str8 find,
|
||||
DN_Str8 replace,
|
||||
DN_USize start_index,
|
||||
DN_Arena *arena,
|
||||
DN_Str8EqCase eq_case)
|
||||
{
|
||||
// TODO: Implement this without requiring TLS so it can go into base strings
|
||||
DN_Str8 result = {};
|
||||
if (!DN_Str8_HasData(string) || !DN_Str8_HasData(find) || find.size > string.size || find.size == 0 || string.size == 0) {
|
||||
result = DN_Str8_Copy(arena, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
||||
DN_Str8Builder string_builder = DN_Str8Builder_Init(tmem.arena);
|
||||
DN_USize max = string.size - find.size;
|
||||
DN_USize head = start_index;
|
||||
|
||||
for (DN_USize tail = head; tail <= max; tail++) {
|
||||
DN_Str8 check = DN_Str8_Slice(string, tail, find.size);
|
||||
if (!DN_Str8_Eq(check, find, eq_case))
|
||||
continue;
|
||||
|
||||
if (start_index > 0 && string_builder.string_size == 0) {
|
||||
// User provided a hint in the string to start searching from, we
|
||||
// need to add the string up to the hint. We only do this if there's
|
||||
// a replacement action, otherwise we have a special case for no
|
||||
// replacements, where the entire string gets copied.
|
||||
DN_Str8 slice = DN_Str8_Init(string.data, head);
|
||||
DN_Str8Builder_AppendRef(&string_builder, slice);
|
||||
}
|
||||
|
||||
DN_Str8 range = DN_Str8_Slice(string, head, (tail - head));
|
||||
DN_Str8Builder_AppendRef(&string_builder, range);
|
||||
DN_Str8Builder_AppendRef(&string_builder, replace);
|
||||
head = tail + find.size;
|
||||
tail += find.size - 1; // NOTE: -1 since the for loop will post increment us past the end of the find string
|
||||
}
|
||||
|
||||
if (string_builder.string_size == 0) {
|
||||
// NOTE: No replacement possible, so we just do a full-copy
|
||||
result = DN_Str8_Copy(arena, string);
|
||||
} else {
|
||||
DN_Str8 remainder = DN_Str8_Init(string.data + head, string.size - head);
|
||||
DN_Str8Builder_AppendRef(&string_builder, remainder);
|
||||
result = DN_Str8Builder_Build(&string_builder, arena);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_Str8_ReplaceInsensitive(DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena)
|
||||
{
|
||||
DN_Str8 result = DN_Str8_Replace(string, find, replace, start_index, arena, DN_Str8EqCase_Insensitive);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_Str8Builder ////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DN_API DN_Str8 DN_Str8Builder_BuildFromOSHeap(DN_Str8Builder const *builder)
|
||||
{
|
||||
DN_Str8 result = DN_ZeroInit;
|
||||
if (!builder || builder->string_size <= 0 || builder->count <= 0)
|
||||
return result;
|
||||
|
||||
result.data = DN_CAST(char *) DN_OS_MemAlloc(builder->string_size + 1, DN_ZeroMem_No);
|
||||
if (!result.data)
|
||||
return result;
|
||||
|
||||
for (DN_Str8Link *link = builder->head; link; link = link->next) {
|
||||
DN_Memcpy(result.data + result.size, link->string.data, link->string.size);
|
||||
result.size += link->string.size;
|
||||
}
|
||||
|
||||
result.data[result.size] = 0;
|
||||
DN_Assert(result.size == builder->string_size);
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#if !defined(DN_OS_STRING_H)
|
||||
#define DN_OS_STRING_H
|
||||
|
||||
// NOTE: DN_Str8 ///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DN_API DN_Str8 DN_Str8_InitFFromFrame (DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API DN_Str8 DN_Str8_InitFFromOSHeap (DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API DN_Str8 DN_Str8_InitFFromTLS (DN_FMT_ATTRIB char const *fmt, ...);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_InitFVFromFrame (DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API DN_Str8 DN_Str8_InitFVFromTLS (DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_AllocFromFrame (DN_USize size, DN_ZeroMem zero_mem);
|
||||
DN_API DN_Str8 DN_Str8_AllocFromOSHeap (DN_USize size, DN_ZeroMem zero_mem);
|
||||
DN_API DN_Str8 DN_Str8_AllocFromTLS (DN_USize size, DN_ZeroMem zero_mem);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_CopyFromFrame (DN_Arena *arena, DN_Str8 string);
|
||||
DN_API DN_Str8 DN_Str8_CopyFromTLS (DN_Arena *arena, DN_Str8 string);
|
||||
|
||||
DN_API DN_Slice<DN_Str8> DN_Str8_SplitAllocFromFrame (DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode);
|
||||
DN_API DN_Slice<DN_Str8> DN_Str8_SplitAllocFromTLS (DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_SegmentFromFrame (DN_Str8 src, DN_USize segment_size, char segment_char);
|
||||
DN_API DN_Str8 DN_Str8_SegmentFromTLS (DN_Str8 src, DN_USize segment_size, char segment_char);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_ReverseSegmentFromFrame (DN_Str8 src, DN_USize segment_size, char segment_char);
|
||||
DN_API DN_Str8 DN_Str8_ReverseSegmentFromTLS (DN_Str8 src, DN_USize segment_size, char segment_char);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_AppendFFromFrame (DN_Str8 string, char const *fmt, ...);
|
||||
DN_API DN_Str8 DN_Str8_AppendFFromTLS (DN_Str8 string, char const *fmt, ...);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_FillFFromFrame (DN_Str8 string, char const *fmt, ...);
|
||||
DN_API DN_Str8 DN_Str8_FillFFromTLS (DN_Str8 string, char const *fmt, ...);
|
||||
|
||||
DN_API DN_Str8DotTruncateResult DN_Str8_DotTruncateMiddleFromFrame (DN_Str8 str8, uint32_t side_size, DN_Str8 truncator);
|
||||
DN_API DN_Str8DotTruncateResult DN_Str8_DotTruncateMiddleFromTLS (DN_Str8 str8, uint32_t side_size, DN_Str8 truncator);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_PadNewLines (DN_Arena *arena, DN_Str8 src, DN_Str8 pad);
|
||||
DN_API DN_Str8 DN_Str8_PadNewLinesFromFrame (DN_Str8 src, DN_Str8 pad);
|
||||
DN_API DN_Str8 DN_Str8_PadNewLinesFromTLS (DN_Str8 src, DN_Str8 pad);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_UpperFromFrame (DN_Str8 string);
|
||||
DN_API DN_Str8 DN_Str8_UpperFromTLS (DN_Str8 string);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_LowerFromFrame (DN_Str8 string);
|
||||
DN_API DN_Str8 DN_Str8_LowerFromTLS (DN_Str8 string);
|
||||
|
||||
DN_API DN_Str8 DN_Str8_Replace (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
|
||||
DN_API DN_Str8 DN_Str8_ReplaceInsensitive (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena);
|
||||
|
||||
// NOTE: DN_Str8Builder ////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DN_API DN_Str8Builder DN_Str8Builder_InitFromFrame () { return DN_Str8Builder_Init(DN_OS_TLSGet()->frame_arena); }
|
||||
DN_API DN_Str8Builder DN_Str8Builder_InitFromTLS () { return DN_Str8Builder_Init(DN_OS_TLSTopArena()); }
|
||||
|
||||
DN_API DN_Str8Builder DN_Str8Builder_InitArrayRefFromFrame (DN_Str8 const *strings, DN_USize size) { return DN_Str8Builder_InitArrayRef(DN_OS_TLSGet()->frame_arena, strings, size); }
|
||||
DN_API DN_Str8Builder DN_Str8Builder_InitArrayRefFromTLS (DN_Str8 const *strings, DN_USize size) { return DN_Str8Builder_InitArrayRef(DN_OS_TLSTopArena(), strings, size); }
|
||||
DN_API DN_Str8Builder DN_Str8Builder_InitArrayCopyFromFrame (DN_Str8 const *strings, DN_USize size) { return DN_Str8Builder_InitArrayCopy(DN_OS_TLSGet()->frame_arena, strings, size); }
|
||||
DN_API DN_Str8Builder DN_Str8Builder_InitArrayCopyFromTLS (DN_Str8 const *strings, DN_USize size) { return DN_Str8Builder_InitArrayCopy(DN_OS_TLSTopArena(), strings, size); }
|
||||
|
||||
DN_API DN_Str8Builder DN_Str8Builder_CopyFromFrame (DN_Str8Builder const *builder) { return DN_Str8Builder_Copy(DN_OS_TLSGet()->frame_arena, builder); }
|
||||
DN_API DN_Str8Builder DN_Str8Builder_CopyFromTLS (DN_Str8Builder const *builder) { return DN_Str8Builder_Copy(DN_OS_TLSTopArena(), builder); }
|
||||
|
||||
DN_API DN_Str8 DN_Str8Builder_BuildFromFrame (DN_Str8Builder const *builder) { return DN_Str8Builder_Build(builder, DN_OS_TLSGet()->frame_arena); }
|
||||
DN_API DN_Slice<DN_Str8> DN_Str8Builder_BuildFromOSHeap (DN_Str8Builder const *builder, DN_Arena *arena);
|
||||
DN_API DN_Str8 DN_Str8Builder_BuildFromTLS (DN_Str8Builder const *builder) { return DN_Str8Builder_Build(builder, DN_OS_TLSTopArena()); }
|
||||
|
||||
DN_API DN_Str8 DN_Str8Builder_BuildDelimitedFromFrame(DN_Str8Builder const *builder, DN_Str8 delimiter) { return DN_Str8Builder_BuildDelimited(builder, delimiter, DN_OS_TLSGet()->frame_arena); }
|
||||
DN_API DN_Str8 DN_Str8Builder_BuildDelimitedFromTLS (DN_Str8Builder const *builder, DN_Str8 delimiter) { return DN_Str8Builder_BuildDelimited(builder, delimiter, DN_OS_TLSTopArena()); }
|
||||
|
||||
DN_API DN_Slice<DN_Str8> DN_Str8Builder_BuildSliceFromFrame (DN_Str8Builder const *builder) { return DN_Str8Builder_BuildSlice(builder, DN_OS_TLSGet()->frame_arena); }
|
||||
DN_API DN_Slice<DN_Str8> DN_Str8Builder_BuildSliceFromTLS (DN_Str8Builder const *builder) { return DN_Str8Builder_BuildSlice(builder, DN_OS_TLSTopArena()); }
|
||||
|
||||
#endif // !defined(DN_OS_STRING_H)
|
||||
@@ -0,0 +1,397 @@
|
||||
#define DN_OS_TLSCPP
|
||||
|
||||
// NOTE: DN_OSTLS ////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_OSTLSTMem::DN_OSTLSTMem(DN_OSTLS *tls, DN_U8 arena_index, DN_OSTLSPushTMem push_tmem)
|
||||
{
|
||||
DN_Assert(arena_index == DN_OSTLSArena_TMem0 || arena_index == DN_OSTLSArena_TMem1);
|
||||
arena = tls->arenas + arena_index;
|
||||
temp_mem = DN_Arena_TempMemBegin(arena);
|
||||
destructed = false;
|
||||
push_arena = push_tmem;
|
||||
if (push_arena)
|
||||
DN_OS_TLSPushArena(arena);
|
||||
}
|
||||
|
||||
DN_OSTLSTMem::~DN_OSTLSTMem()
|
||||
{
|
||||
DN_Assert(destructed == false);
|
||||
DN_Arena_TempMemEnd(temp_mem);
|
||||
destructed = true;
|
||||
if (push_arena)
|
||||
DN_OS_TLSPopArena();
|
||||
}
|
||||
|
||||
DN_API void DN_OS_TLSInit(DN_OSTLS *tls, DN_OSTLSInitArgs args)
|
||||
{
|
||||
DN_Check(tls);
|
||||
if (tls->init)
|
||||
return;
|
||||
|
||||
DN_U64 reserve = args.reserve ? args.reserve : DN_Kilobytes(64);
|
||||
DN_U64 commit = args.commit ? args.commit : DN_Kilobytes(4);
|
||||
DN_U64 err_sink_reserve = args.err_sink_reserve ? args.err_sink_reserve : DN_Kilobytes(64);
|
||||
DN_U64 err_sink_commit = args.err_sink_commit ? args.err_sink_commit : DN_Kilobytes(4);
|
||||
|
||||
// TODO: We shouldn't have the no alloc track flag here but the initial TLS
|
||||
// init on OS init happens before CORE init. CORE init is the one responsible
|
||||
// for setting up the alloc tracking data structures.
|
||||
for (DN_ForItCArray(it, DN_Arena, tls->arenas)) {
|
||||
DN_Arena *arena = it.data;
|
||||
switch (DN_CAST(DN_OSTLSArena) it.index) {
|
||||
default: *arena = DN_Arena_InitFromOSVMem(reserve, commit, DN_ArenaFlags_AllocCanLeak | DN_ArenaFlags_NoAllocTrack); break;
|
||||
case DN_OSTLSArena_ErrorSink: *arena = DN_Arena_InitFromOSVMem(err_sink_reserve, err_sink_commit, DN_ArenaFlags_AllocCanLeak | DN_ArenaFlags_NoAllocTrack); break;
|
||||
case DN_OSTLSArena_Count: DN_InvalidCodePath; break;
|
||||
}
|
||||
}
|
||||
|
||||
tls->thread_id = DN_OS_ThreadID();
|
||||
tls->err_sink.arena = tls->arenas + DN_OSTLSArena_ErrorSink;
|
||||
tls->init = true;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_TLSDeinit(DN_OSTLS *tls)
|
||||
{
|
||||
tls->init = false;
|
||||
tls->err_sink = {};
|
||||
tls->arena_stack_index = {};
|
||||
for (DN_ForItCArray(it, DN_Arena, tls->arenas))
|
||||
DN_Arena_Deinit(it.data);
|
||||
}
|
||||
|
||||
DN_THREAD_LOCAL DN_OSTLS *g_dn_curr_thread_tls;
|
||||
DN_API void DN_OS_TLSSetCurrentThreadTLS(DN_OSTLS *tls)
|
||||
{
|
||||
g_dn_curr_thread_tls = tls;
|
||||
}
|
||||
|
||||
DN_API DN_OSTLS *DN_OS_TLSGet()
|
||||
{
|
||||
DN_Assert(g_dn_curr_thread_tls &&
|
||||
"DN must be initialised (via DN_Core_Init) before calling any functions depending on "
|
||||
"TLS if this is the main thread, OR, the created thread has not called "
|
||||
"SetCurrentThreadTLS yet so the TLS data structure hasn't been assigned yet");
|
||||
return g_dn_curr_thread_tls;
|
||||
}
|
||||
|
||||
DN_API DN_Arena *DN_OS_TLSArena()
|
||||
{
|
||||
DN_OSTLS *tls = DN_OS_TLSGet();
|
||||
DN_Arena *result = tls->arenas + DN_OSTLSArena_Main;
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: Is there a way to handle conflict arenas without the user needing to
|
||||
// manually pass it in?
|
||||
DN_API DN_OSTLSTMem DN_OS_TLSGetTMem(void const *conflict_arena, DN_OSTLSPushTMem push_tmem)
|
||||
{
|
||||
DN_OSTLS *tls = DN_OS_TLSGet();
|
||||
DN_U8 tls_index = (DN_U8)-1;
|
||||
for (DN_U8 index = DN_OSTLSArena_TMem0; index <= DN_OSTLSArena_TMem1; index++) {
|
||||
DN_Arena *arena = tls->arenas + index;
|
||||
if (!conflict_arena || arena != conflict_arena) {
|
||||
tls_index = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DN_Assert(tls_index != (DN_U8)-1);
|
||||
return DN_OSTLSTMem(tls, tls_index, push_tmem);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_TLSPushArena(DN_Arena *arena)
|
||||
{
|
||||
DN_Assert(arena);
|
||||
DN_OSTLS *tls = DN_OS_TLSGet();
|
||||
DN_Assert(tls->arena_stack_index < DN_ArrayCountU(tls->arena_stack));
|
||||
tls->arena_stack[tls->arena_stack_index++] = arena;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_TLSPopArena()
|
||||
{
|
||||
DN_OSTLS *tls = DN_OS_TLSGet();
|
||||
DN_Assert(tls->arena_stack_index > 0);
|
||||
tls->arena_stack_index--;
|
||||
}
|
||||
|
||||
DN_API DN_Arena *DN_OS_TLSTopArena()
|
||||
{
|
||||
DN_OSTLS *tls = DN_OS_TLSGet();
|
||||
DN_Arena *result = nullptr;
|
||||
if (tls->arena_stack_index)
|
||||
result = tls->arena_stack[tls->arena_stack_index - 1];
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_TLSBeginFrame(DN_Arena *frame_arena)
|
||||
{
|
||||
DN_OSTLS *tls = DN_OS_TLSGet();
|
||||
tls->frame_arena = frame_arena;
|
||||
}
|
||||
|
||||
DN_API DN_Arena *DN_OS_TLSFrameArena()
|
||||
{
|
||||
DN_OSTLS *tls = DN_OS_TLSGet();
|
||||
DN_Arena *result = tls->frame_arena;
|
||||
DN_AssertF(result, "Frame arena must be set by calling DN_OS_TLSBeginFrame at the beginning of the frame");
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_OSErrSink ////////////////////////////////////////////////////////////////////////////////
|
||||
static void DN_OS_ErrSinkCheck_(DN_OSErrSink const *err)
|
||||
{
|
||||
DN_AssertF(err->arena, "Arena should be assigned in TLS init");
|
||||
if (err->stack_size == 0)
|
||||
return;
|
||||
|
||||
DN_OSErrSinkNode const *node = err->stack + (err->stack_size - 1);
|
||||
DN_Assert(node->mode >= DN_OSErrSinkMode_Nil && node->mode <= DN_OSErrSinkMode_ExitOnError);
|
||||
DN_Assert(node->msg_sentinel);
|
||||
|
||||
// NOTE: Walk the list ensuring we eventually terminate at the sentinel (e.g. we have a
|
||||
// well formed doubly-linked-list terminated by a sentinel, or otherwise we will hit the
|
||||
// walk limit or dereference a null pointer and assert)
|
||||
size_t WALK_LIMIT = 99'999;
|
||||
size_t walk = 0;
|
||||
for (DN_OSErrSinkMsg *it = node->msg_sentinel->next; it != node->msg_sentinel; it = it->next, walk++) {
|
||||
DN_AssertF(it, "Encountered null pointer which should not happen in a sentinel DLL");
|
||||
DN_Assert(walk < WALK_LIMIT);
|
||||
}
|
||||
}
|
||||
|
||||
DN_API DN_OSErrSink *DN_OS_ErrSinkBegin_(DN_OSErrSinkMode mode, DN_CallSite call_site)
|
||||
{
|
||||
DN_OSTLS *tls = DN_OS_TLSGet();
|
||||
DN_OSErrSink *err = &tls->err_sink;
|
||||
DN_OSErrSink *result = err;
|
||||
DN_USize arena_pos = DN_Arena_Pos(result->arena);
|
||||
|
||||
if (tls->err_sink.stack_size == DN_ArrayCountU(err->stack)) {
|
||||
DN_Str8Builder builder = DN_Str8Builder_InitFromTLS();
|
||||
DN_USize counter = 0;
|
||||
for (DN_ForItSize(it, DN_OSErrSinkNode, err->stack, err->stack_size)) {
|
||||
DN_MSVC_WARNING_PUSH
|
||||
DN_MSVC_WARNING_DISABLE(6284) // Object passed as _Param_(4) when a string is required in call to 'DN_Str8Builder_AppendF' Actual type: 'struct DN_Str8'.
|
||||
DN_Str8Builder_AppendF(&builder, " [%04zu] %S:%u %S\n", counter++, it.data->call_site.file, it.data->call_site.line, it.data->call_site.function);
|
||||
DN_MSVC_WARNING_POP
|
||||
}
|
||||
|
||||
DN_MSVC_WARNING_PUSH
|
||||
DN_MSVC_WARNING_DISABLE(6284) // Object passed as _Param_(6) when a string is required in call to 'DN_LOG_EmitFromType' Actual type: 'struct DN_Str8'.
|
||||
DN_AssertF(tls->err_sink.stack_size < DN_ArrayCountU(err->stack),
|
||||
"Error sink has run out of error scopes, potential leak. Scopes were\n%S", DN_Str8Builder_BuildFromTLS(&builder));
|
||||
DN_MSVC_WARNING_POP
|
||||
}
|
||||
|
||||
DN_OSErrSinkNode *node = tls->err_sink.stack + tls->err_sink.stack_size++;
|
||||
node->arena_pos = arena_pos;
|
||||
node->mode = mode;
|
||||
node->call_site = call_site;
|
||||
DN_DLList_InitArena(node->msg_sentinel, DN_OSErrSinkMsg, result->arena);
|
||||
|
||||
// NOTE: Handle allocation error
|
||||
if (!DN_Check(node && node->msg_sentinel)) {
|
||||
DN_Arena_PopTo(result->arena, arena_pos);
|
||||
node->msg_sentinel = nullptr;
|
||||
tls->err_sink.stack_size--;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_ErrSinkHasError(DN_OSErrSink *err)
|
||||
{
|
||||
bool result = false;
|
||||
if (err && err->stack_size) {
|
||||
DN_OSErrSinkNode *node = err->stack + (err->stack_size - 1);
|
||||
result = DN_DLList_HasItems(node->msg_sentinel);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_OSErrSinkMsg *DN_OS_ErrSinkEnd(DN_Arena *arena, DN_OSErrSink *err)
|
||||
{
|
||||
DN_OSErrSinkMsg *result = nullptr;
|
||||
DN_OS_ErrSinkCheck_(err);
|
||||
if (!err || err->stack_size == 0)
|
||||
return result;
|
||||
|
||||
DN_AssertF(arena != err->arena,
|
||||
"You are not allowed to reuse the arena for ending the error sink because the memory would get popped and lost");
|
||||
// NOTE: Walk the list and allocate it onto the user's arena
|
||||
DN_OSErrSinkNode *node = err->stack + (err->stack_size - 1);
|
||||
DN_OSErrSinkMsg *prev = nullptr;
|
||||
for (DN_OSErrSinkMsg *it = node->msg_sentinel->next; it != node->msg_sentinel; it = it->next) {
|
||||
DN_OSErrSinkMsg *entry = DN_Arena_New(arena, DN_OSErrSinkMsg, DN_ZeroMem_Yes);
|
||||
entry->msg = DN_Str8_Copy(arena, it->msg);
|
||||
entry->call_site = it->call_site;
|
||||
entry->error_code = it->error_code;
|
||||
if (!result)
|
||||
result = entry; // Assign first entry if we haven't yet
|
||||
if (prev)
|
||||
prev->next = entry; // Link the prev message to the current one
|
||||
prev = entry; // Update prev to latest
|
||||
}
|
||||
|
||||
// NOTE: Deallocate all the memory for this scope
|
||||
err->stack_size--;
|
||||
DN_Arena_PopTo(err->arena, node->arena_pos);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void DN_OS_ErrSinkAddMsgToStr8Builder_(DN_Str8Builder *builder, DN_OSErrSinkMsg *msg, DN_OSErrSinkMsg *end)
|
||||
{
|
||||
if (msg == end) // NOTE: No error messages to add
|
||||
return;
|
||||
|
||||
if (msg->next == end) {
|
||||
DN_OSErrSinkMsg *it = msg;
|
||||
DN_Str8 file_name = DN_Str8_FileNameFromPath(it->call_site.file);
|
||||
DN_Str8Builder_AppendF(builder,
|
||||
"%.*s:%05I32u:%.*s %.*s",
|
||||
DN_STR_FMT(file_name),
|
||||
it->call_site.line,
|
||||
DN_STR_FMT(it->call_site.function),
|
||||
DN_STR_FMT(it->msg));
|
||||
} else {
|
||||
// NOTE: More than one message
|
||||
for (DN_OSErrSinkMsg *it = msg; it != end; it = it->next) {
|
||||
DN_Str8 file_name = DN_Str8_FileNameFromPath(it->call_site.file);
|
||||
DN_Str8Builder_AppendF(builder,
|
||||
"%s - %.*s:%05I32u:%.*s%s%.*s",
|
||||
it == msg ? "" : "\n",
|
||||
DN_STR_FMT(file_name),
|
||||
it->call_site.line,
|
||||
DN_STR_FMT(it->call_site.function),
|
||||
DN_Str8_HasData(it->msg) ? " " : "",
|
||||
DN_STR_FMT(it->msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DN_API DN_Str8 DN_OS_ErrSinkEndStr8(DN_Arena *arena, DN_OSErrSink *err)
|
||||
{
|
||||
DN_Str8 result = {};
|
||||
DN_OS_ErrSinkCheck_(err);
|
||||
if (!err || err->stack_size == 0)
|
||||
return result;
|
||||
|
||||
DN_AssertF(arena != err->arena,
|
||||
"You are not allowed to reuse the arena for ending the error sink because the memory would get popped and lost");
|
||||
|
||||
// NOTE: Walk the list and allocate it onto the user's arena
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSPushTMem(arena);
|
||||
DN_Str8Builder builder = DN_Str8Builder_InitFromTLS();
|
||||
DN_OSErrSinkNode *node = err->stack + (err->stack_size - 1);
|
||||
DN_OS_ErrSinkAddMsgToStr8Builder_(&builder, node->msg_sentinel->next, node->msg_sentinel);
|
||||
|
||||
// NOTE: Deallocate all the memory for this scope
|
||||
err->stack_size--;
|
||||
DN_U64 arena_pos = node->arena_pos;
|
||||
DN_Arena_PopTo(err->arena, arena_pos);
|
||||
|
||||
result = DN_Str8Builder_Build(&builder, arena);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_ErrSinkEndAndIgnore(DN_OSErrSink *err)
|
||||
{
|
||||
DN_OS_ErrSinkEnd(nullptr, err);
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_ErrSinkEndAndLogError_(DN_OSErrSink *err, DN_CallSite call_site, DN_Str8 err_msg)
|
||||
{
|
||||
DN_AssertF(err->stack_size, "Begin must be called before calling end");
|
||||
DN_OSErrSinkNode *node = err->stack + (err->stack_size - 1);
|
||||
DN_AssertF(node->msg_sentinel, "Begin must be called before calling end");
|
||||
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSPushTMem(nullptr);
|
||||
DN_OSErrSinkMode mode = node->mode;
|
||||
DN_OSErrSinkMsg *msg = DN_OS_ErrSinkEnd(tmem.arena, err);
|
||||
if (!msg)
|
||||
return false;
|
||||
|
||||
DN_Str8Builder builder = DN_Str8Builder_InitFromTLS();
|
||||
if (DN_Str8_HasData(err_msg)) {
|
||||
DN_Str8Builder_AppendRef(&builder, err_msg);
|
||||
DN_Str8Builder_AppendRef(&builder, DN_STR8(":"));
|
||||
} else {
|
||||
DN_Str8Builder_AppendRef(&builder, DN_STR8("Error(s) encountered:"));
|
||||
}
|
||||
|
||||
if (msg->next) // NOTE: More than 1 message
|
||||
DN_Str8Builder_AppendRef(&builder, DN_STR8("\n"));
|
||||
DN_OS_ErrSinkAddMsgToStr8Builder_(&builder, msg, nullptr);
|
||||
|
||||
DN_Str8 log = DN_Str8Builder_BuildFromTLS(&builder);
|
||||
DN_LOG_EmitFromType(DN_LOG_MakeU32LogTypeParam(DN_LOGType_Error), call_site, "%.*s", DN_STR_FMT(log));
|
||||
|
||||
if (mode == DN_OSErrSinkMode_DebugBreakOnEndAndLog)
|
||||
DN_DebugBreak;
|
||||
return true;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_ErrSinkEndAndLogErrorFV_(DN_OSErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
||||
DN_Str8 log = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
bool result = DN_OS_ErrSinkEndAndLogError_(err, call_site, log);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_ErrSinkEndAndLogErrorF_(DN_OSErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
||||
DN_Str8 log = DN_Str8_InitFV(tmem.arena, fmt, args);
|
||||
bool result = DN_OS_ErrSinkEndAndLogError_(err, call_site, log);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_OS_ErrSinkEndAndExitIfErrorFV_(DN_OSErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
if (DN_OS_ErrSinkEndAndLogErrorFV_(err, call_site, fmt, args)) {
|
||||
DN_DebugBreak;
|
||||
DN_OS_Exit(exit_val);
|
||||
}
|
||||
}
|
||||
|
||||
DN_API void DN_OS_ErrSinkEndAndExitIfErrorF_(DN_OSErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_OS_ErrSinkEndAndExitIfErrorFV_(err, call_site, exit_val, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DN_API void DN_OS_ErrSinkAppendFV_(DN_OSErrSink *err, DN_U32 error_code, DN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
if (!err)
|
||||
return;
|
||||
DN_Assert(err && err->stack_size);
|
||||
DN_OSErrSinkNode *node = err->stack + (err->stack_size - 1);
|
||||
DN_AssertF(node, "Error sink must be begun by calling 'Begin' before using this function.");
|
||||
|
||||
DN_OSErrSinkMsg *msg = DN_Arena_New(err->arena, DN_OSErrSinkMsg, DN_ZeroMem_Yes);
|
||||
if (DN_Check(msg)) {
|
||||
msg->msg = DN_Str8_InitFV(err->arena, fmt, args);
|
||||
msg->error_code = error_code;
|
||||
msg->call_site = DN_OS_TLSGet()->call_site;
|
||||
DN_DLList_Prepend(node->msg_sentinel, msg);
|
||||
|
||||
if (node->mode == DN_OSErrSinkMode_ExitOnError)
|
||||
DN_OS_ErrSinkEndAndExitIfErrorF_(err, msg->call_site, error_code, "Fatal error %u", error_code);
|
||||
}
|
||||
}
|
||||
|
||||
DN_API void DN_OS_ErrSinkAppendF_(DN_OSErrSink *err, DN_U32 error_code, DN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
if (!err)
|
||||
return;
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_OS_ErrSinkAppendFV_(err, error_code, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
#if !defined(DN_OS_TLS_H)
|
||||
#define DN_OS_TLS_H
|
||||
|
||||
// NOTE: DN_OSErrSink /////////////////////////////////////////////////////////////////////////////
|
||||
enum DN_OSErrSinkMode
|
||||
{
|
||||
DN_OSErrSinkMode_Nil, // Default behaviour to accumulate errors into the sink
|
||||
DN_OSErrSinkMode_DebugBreakOnEndAndLog, // Debug break (int3) when error is encountered and the sink is ended by the 'end and log' functions.
|
||||
DN_OSErrSinkMode_ExitOnError, // When an error is encountered, exit the program with the error code of the error that was caught.
|
||||
};
|
||||
|
||||
struct DN_OSErrSinkMsg
|
||||
{
|
||||
DN_I32 error_code;
|
||||
DN_Str8 msg;
|
||||
DN_CallSite call_site;
|
||||
DN_OSErrSinkMsg *next;
|
||||
DN_OSErrSinkMsg *prev;
|
||||
};
|
||||
|
||||
struct DN_OSErrSinkNode
|
||||
{
|
||||
DN_CallSite call_site; // Call site that the node was created
|
||||
DN_OSErrSinkMode mode; // Controls how the sink behaves when an error is registered onto the sink.
|
||||
DN_OSErrSinkMsg *msg_sentinel; // List of error messages accumulated for the current scope
|
||||
DN_U64 arena_pos; // Position to reset the arena when the scope is ended
|
||||
};
|
||||
|
||||
struct DN_OSErrSink
|
||||
{
|
||||
DN_Arena * arena; // Dedicated allocator from the thread's local storage
|
||||
DN_OSErrSinkNode stack[128]; // Each entry contains errors accumulated between a [begin, end] region of the active sink.
|
||||
DN_USize stack_size;
|
||||
};
|
||||
|
||||
enum DN_OSTLSArena
|
||||
{
|
||||
DN_OSTLSArena_Main, // NOTE: Arena for permanent allocations
|
||||
DN_OSTLSArena_ErrorSink, // NOTE: Arena for logging error information for this thread
|
||||
|
||||
// NOTE: Per-thread scratch arenas (2 to prevent aliasing)
|
||||
DN_OSTLSArena_TMem0,
|
||||
DN_OSTLSArena_TMem1,
|
||||
|
||||
DN_OSTLSArena_Count,
|
||||
};
|
||||
|
||||
struct DN_OSTLS
|
||||
{
|
||||
DN_B32 init; // Flag to track if TLS has been initialised
|
||||
DN_U64 thread_id;
|
||||
DN_CallSite call_site; // Stores call-site information when requested by thread
|
||||
DN_OSErrSink err_sink; // Error handling state
|
||||
DN_Arena arenas[DN_OSTLSArena_Count]; // Default arenas that the thread has access to implicitly
|
||||
DN_Arena * arena_stack[8]; // Active stack of arenas push/popped arenas on into the TLS
|
||||
DN_USize arena_stack_index;
|
||||
|
||||
DN_Arena * frame_arena;
|
||||
char name[64];
|
||||
DN_U8 name_size;
|
||||
};
|
||||
|
||||
// Push the temporary memory arena when retrieved, popped when the arena goes
|
||||
// out of scope. Pushed arenas are used automatically as the allocator in TLS
|
||||
// suffixed function.
|
||||
enum DN_OSTLSPushTMem
|
||||
{
|
||||
DN_OSTLSPushTMem_No,
|
||||
DN_OSTLSPushTMem_Yes,
|
||||
};
|
||||
|
||||
struct DN_OSTLSTMem
|
||||
{
|
||||
DN_OSTLSTMem(DN_OSTLS *context, uint8_t context_index, DN_OSTLSPushTMem push_scratch);
|
||||
~DN_OSTLSTMem();
|
||||
DN_Arena *arena;
|
||||
DN_B32 destructed;
|
||||
DN_OSTLSPushTMem push_arena;
|
||||
DN_ArenaTempMem temp_mem;
|
||||
};
|
||||
|
||||
struct DN_OSTLSInitArgs
|
||||
{
|
||||
DN_U64 reserve;
|
||||
DN_U64 commit;
|
||||
DN_U64 err_sink_reserve;
|
||||
DN_U64 err_sink_commit;
|
||||
};
|
||||
|
||||
// NOTE: DN_OSTLS ////////////////////////////////////////////////////////////////////////////////////
|
||||
DN_API void DN_OS_TLSInit (DN_OSTLS *tls, DN_OSTLSInitArgs args);
|
||||
DN_API void DN_OS_TLSDeinit (DN_OSTLS *tls);
|
||||
DN_API DN_OSTLS * DN_OS_TLSGet ();
|
||||
DN_API void DN_OS_TLSSetCurrentThreadTLS (DN_OSTLS *tls);
|
||||
DN_API DN_Arena * DN_OS_TLSArena ();
|
||||
DN_API DN_OSTLSTMem DN_OS_TLSGetTMem (void const *conflict_arena, DN_OSTLSPushTMem push_tmp_mem);
|
||||
DN_API void DN_OS_TLSPushArena (DN_Arena *arena);
|
||||
DN_API void DN_OS_TLSPopArena ();
|
||||
DN_API DN_Arena * DN_OS_TLSTopArena ();
|
||||
DN_API void DN_OS_TLSBeginFrame (DN_Arena *frame_arena);
|
||||
DN_API DN_Arena * DN_OS_TLSFrameArena ();
|
||||
#define DN_OS_TLSSaveCallSite do { DN_OS_TLSGet()->call_site = DN_CALL_SITE; } while (0)
|
||||
#define DN_OS_TLSTMem(...) DN_OS_TLSGetTMem(__VA_ARGS__, DN_OSTLSPushTMem_No)
|
||||
#define DN_OS_TLSPushTMem(...) DN_OS_TLSGetTMem(__VA_ARGS__, DN_OSTLSPushTMem_Yes)
|
||||
|
||||
// NOTE: DN_OS_ErrSink ////////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_OSErrSink * DN_OS_ErrSinkBegin_ (DN_OSErrSinkMode mode, DN_CallSite call_site);
|
||||
#define DN_OS_ErrSinkBegin(mode) DN_OS_ErrSinkBegin_(mode, DN_CALL_SITE)
|
||||
#define DN_OS_ErrSinkBeginDefault() DN_OS_ErrSinkBegin(DN_OSErrSinkMode_Nil)
|
||||
DN_API bool DN_OS_ErrSinkHasError (DN_OSErrSink *err);
|
||||
DN_API DN_OSErrSinkMsg *DN_OS_ErrSinkEnd (DN_Arena *arena, DN_OSErrSink *err);
|
||||
DN_API DN_Str8 DN_OS_ErrSinkEndStr8 (DN_Arena *arena, DN_OSErrSink *err);
|
||||
DN_API void DN_OS_ErrSinkEndAndIgnore (DN_OSErrSink *err);
|
||||
DN_API bool DN_OS_ErrSinkEndAndLogError_ (DN_OSErrSink *err, DN_CallSite call_site, DN_Str8 msg);
|
||||
DN_API bool DN_OS_ErrSinkEndAndLogErrorFV_ (DN_OSErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API bool DN_OS_ErrSinkEndAndLogErrorF_ (DN_OSErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_OS_ErrSinkEndAndExitIfErrorF_ (DN_OSErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_OS_ErrSinkEndAndExitIfErrorFV_ (DN_OSErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API void DN_OS_ErrSinkAppendFV_ (DN_OSErrSink *err, DN_U32 error_code, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API void DN_OS_ErrSinkAppendF_ (DN_OSErrSink *err, DN_U32 error_code, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
#define DN_OS_ErrSinkEndAndLogError(err, err_msg) DN_OS_ErrSinkEndAndLogError_(err, DN_CALL_SITE, err_msg)
|
||||
#define DN_OS_ErrSinkEndAndLogErrorFV(err, fmt, args) DN_OS_ErrSinkEndAndLogErrorFV_(err, DN_CALL_SITE, fmt, args)
|
||||
#define DN_OS_ErrSinkEndAndLogErrorF(err, fmt, ...) DN_OS_ErrSinkEndAndLogErrorF_(err, DN_CALL_SITE, fmt, ##__VA_ARGS__)
|
||||
#define DN_OS_ErrSinkEndAndExitIfErrorFV(err, exit_val, fmt, args) DN_OS_ErrSinkEndAndExitIfErrorFV_(err, DN_CALL_SITE, exit_val, fmt, args)
|
||||
#define DN_OS_ErrSinkEndAndExitIfErrorF(err, exit_val, fmt, ...) DN_OS_ErrSinkEndAndExitIfErrorF_(err, DN_CALL_SITE, exit_val, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define DN_OS_ErrSinkAppendFV(error, error_code, fmt, args) \
|
||||
do { \
|
||||
DN_OS_TLSSaveCallSite; \
|
||||
DN_OS_ErrSinkAppendFV_(error, error_code, fmt, args); \
|
||||
} while (0)
|
||||
|
||||
#define DN_OS_ErrSinkAppendF(error, error_code, fmt, ...) \
|
||||
do { \
|
||||
DN_OS_TLSSaveCallSite; \
|
||||
DN_OS_ErrSinkAppendF_(error, error_code, fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#endif // defined(DN_OS_TLS_H)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,71 @@
|
||||
#if !defined(DN_OS_WIN32_H)
|
||||
#define DN_OS_WIN32_H
|
||||
|
||||
struct DN_W32Error
|
||||
{
|
||||
unsigned long code;
|
||||
DN_Str8 msg;
|
||||
};
|
||||
|
||||
struct DN_W32FolderIteratorW
|
||||
{
|
||||
void *handle;
|
||||
DN_Str16 file_name;
|
||||
wchar_t file_name_buf[512];
|
||||
};
|
||||
|
||||
enum DN_W32SyncPrimitiveType
|
||||
{
|
||||
DN_OSW32SyncPrimitiveType_Semaphore,
|
||||
DN_OSW32SyncPrimitiveType_Mutex,
|
||||
DN_OSW32SyncPrimitiveType_ConditionVariable,
|
||||
};
|
||||
|
||||
struct DN_W32SyncPrimitive
|
||||
{
|
||||
union
|
||||
{
|
||||
void *sem;
|
||||
CRITICAL_SECTION mutex;
|
||||
CONDITION_VARIABLE cv;
|
||||
};
|
||||
|
||||
DN_W32SyncPrimitive *next;
|
||||
};
|
||||
|
||||
typedef HRESULT DN_W32SetThreadDescriptionFunc(HANDLE hThread, PWSTR const lpThreadDescription);
|
||||
struct DN_W32Core
|
||||
{
|
||||
DN_W32SetThreadDescriptionFunc *set_thread_description;
|
||||
LARGE_INTEGER qpc_frequency;
|
||||
void *bcrypt_rng_handle;
|
||||
bool bcrypt_init_success;
|
||||
bool sym_initialised;
|
||||
|
||||
CRITICAL_SECTION sync_primitive_free_list_mutex;
|
||||
DN_W32SyncPrimitive *sync_primitive_free_list;
|
||||
};
|
||||
|
||||
DN_API void DN_W32_ThreadSetName (DN_Str8 name);
|
||||
|
||||
DN_API DN_Str16 DN_W32_ErrorCodeToMsg16Alloc(uint32_t error_code);
|
||||
DN_API DN_W32Error DN_W32_ErrorCodeToMsg (DN_Arena *arena, uint32_t error_code);
|
||||
DN_API DN_W32Error DN_W32_ErrorCodeToMsgAlloc (uint32_t error_code);
|
||||
DN_API DN_W32Error DN_W32_LastError (DN_Arena *arena);
|
||||
DN_API DN_W32Error DN_W32_LastErrorAlloc ();
|
||||
DN_API void DN_W32_MakeProcessDPIAware ();
|
||||
|
||||
// NOTE: Windows Str8 <-> Str16 ////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Str16 DN_W32_Str8ToStr16 (DN_Arena *arena, DN_Str8 src);
|
||||
DN_API int DN_W32_Str8ToStr16Buffer (DN_Str16 src, char *dest, int dest_size);
|
||||
DN_API DN_Str8 DN_W32_Str16ToStr8 (DN_Arena *arena, DN_Str16 src);
|
||||
DN_API int DN_W32_Str16ToStr8Buffer (DN_Str16 src, char *dest, int dest_size);
|
||||
DN_API DN_Str8 DN_W32_Str16ToStr8FromHeap(DN_Str16 src);
|
||||
|
||||
// NOTE: Path navigation ///////////////////////////////////////////////////////////////////////////
|
||||
DN_API DN_Str16 DN_W32_EXEPathW (DN_Arena *arena);
|
||||
DN_API DN_Str16 DN_W32_EXEDirW (DN_Arena *arena);
|
||||
DN_API DN_Str8 DN_W32_WorkingDir (DN_Arena *arena, DN_Str8 suffix);
|
||||
DN_API DN_Str16 DN_W32_WorkingDirW (DN_Arena *arena, DN_Str16 suffix);
|
||||
DN_API bool DN_W32_DirWIterate (DN_Str16 path, DN_W32FolderIteratorW *it);
|
||||
#endif // !defined(DN_OS_WIN32)
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user