diff --git a/Single-Header/dn_single_header.cpp b/Single-Header/dn_single_header.cpp index b03aa4d..7e50594 100644 --- a/Single-Header/dn_single_header.cpp +++ b/Single-Header/dn_single_header.cpp @@ -1,4 +1,4 @@ -// Generated by the DN single header generator 2026-05-18 14:23:35 +// Generated by the DN single header generator 2026-06-01 21:40:06 // DN: Single header generator commented out => #if defined(_CLANGD) // #define DN_H_WITH_OS 1 @@ -14,6 +14,12 @@ // #include "../dn.h" // #endif +enum DN_ArenaUAFCheckReportType_ +{ + DN_ArenaUAFCheckReportType_AllocViolation, + DN_ArenaUAFCheckReportType_TempEndOutOfOrder, +}; + DN_API bool DN_MemStartsWith(void const *lhs, DN_USize lhs_size, void const *rhs, DN_USize rhs_size) { bool result = false; @@ -985,12 +991,13 @@ static void DN_MemBlockDeinit_(DN_MemList const *mem, DN_MemBlock *block) DN_API void DN_MemListDeinit(DN_MemList *mem) { + bool mem_allocated_from_itself = DN_MemListOwnsPtr(mem, mem); for (DN_MemBlock *block = mem ? mem->curr : nullptr; block;) { DN_MemBlock *block_to_free = block; - block = block->prev; + block = block->prev; DN_MemBlockDeinit_(mem, block_to_free); } - if (mem) + if (mem && !mem_allocated_from_itself) *mem = {}; } @@ -1119,7 +1126,7 @@ DN_API void *DN_MemListAllocContiguous(DN_MemList *mem, DN_U64 size, uint8_t ali { DN_MemFlags prev_flags = mem->flags; mem->flags |= (DN_MemFlags_NoGrow | DN_MemFlags_NoPoison); - void *memory = DN_MemListAlloc(mem, size, align, z_mem); + void *memory = DN_MemListAlloc(mem, size, align, z_mem); mem->flags = prev_flags; return memory; } @@ -1216,9 +1223,9 @@ DN_API bool DN_MemListOwnsPtr(DN_MemList const *mem, void *ptr) DN_API DN_Str8x64 DN_MemListInfoStr8x64(DN_MemListInfo info) { DN_Str8x64 result = {}; - DN_Str8x32 used = DN_ByteCountStr8x32(info.used); - DN_Str8x32 commit = DN_ByteCountStr8x32(info.commit); - DN_Str8x32 reserve = DN_ByteCountStr8x32(info.reserve); + DN_Str8x32 used = DN_Str8x32FromByteCountU64Auto(info.used); + DN_Str8x32 commit = DN_Str8x32FromByteCountU64Auto(info.commit); + DN_Str8x32 reserve = DN_Str8x32FromByteCountU64Auto(info.reserve); // NOTE: Blocks, Used, Commit, Reserve result = DN_Str8x64FromFmt("B=%u U=%.*s C=%.*s R=%.*s", DN_Cast(DN_U32)info.blocks, DN_Str8PrintFmt(used), DN_Str8PrintFmt(commit), DN_Str8PrintFmt(reserve)); return result; @@ -1256,9 +1263,10 @@ static bool DN_MemListUAFTracingEnabled_(DN_MemList *mem) } #endif -DN_API void DN_ArenaUAFCheck(DN_Arena *arena) +static void DN_ArenaUAFCheck_(DN_Arena *arena, DN_ArenaUAFCheckReportType_ type) { (void)arena; + (void)type; #if DN_ARENA_TEMP_MEM_UAF_GUARD DN_MemList *mem = arena->mem; if (!arena || !mem) @@ -1269,32 +1277,52 @@ DN_API void DN_ArenaUAFCheck(DN_Arena *arena) // check which would cause infinite recursion so we set a flag here to prevent that. arena->uaf_guard_is_being_checked = true; if (mem->uaf_guard_active_id != arena->uaf_guard_id) { + // NOTE: We use the MemList on the arena directly to bypass any potential recursive UAF (if the + // current arena is triggering the UAF check then it's already violating so we use the + // underlying primitive to allocate memory). + DN_Allocator allocator = DN_AllocatorFromMemList(mem); + // NOTE: MSVC does not recognise %'u which is a STB extension which causes a lot of incorrect // format arguments warnings that we mute here. DN_MSVC_WARNING_PUSH DN_MSVC_WARNING_DISABLE(6271) // Extra argument passed to 'DN_Str8FromFmtArena' DN_MSVC_WARNING_DISABLE(6067) // _Param_(10) in call to 'DN_LogPrint' must be the address of a string. Actual type: 'int'. DN_MSVC_WARNING_DISABLE(6273) // Non-integer passed as _Param_(11) when an integer is required in call to 'DN_LogPrint' Actual type: 'char *'. - DN_Str8 prefix = DN_Str8LineBreakStr8(DN_Str8FromFmtArena(arena, - "Arena use-after-free (UAF) detected in temporary memory usage! This allocation (trace " - "shown above) is attempting to allocate memory inside the active temporary region (id: %'u) " - "but belongs to a different region (id: %'u). This means when the active temporary region is " - "released, this allocation will be released and scrubbed causing a potential UAF.\n\nEnsure " - "that scratch memory is deconflicting correctly, scratch and or temporary memory regions have " - "matching begin and end pairs and only the arena view with the active temporary memory region " - "is being allocated from.", - mem->uaf_guard_active_id, - arena->uaf_guard_id), - 100, - arena); + DN_Str8 error_msg = {}; + if (type == DN_ArenaUAFCheckReportType_AllocViolation) { + error_msg = DN_Str8FromFmtAllocator(allocator, + "Arena use-after-free (UAF) detected in temporary memory usage! This allocation (trace " + "shown above) is attempting to allocate memory inside the active temporary region (id: %'u) " + "but belongs to a different region (id: %'u). This means when the active temporary region is " + "released, this allocation will be released and scrubbed causing a potential UAF.\n\nEnsure " + "that scratch memory is deconflicting correctly, scratch and or temporary memory regions have " + "matching begin and end pairs and only the arena view with the active temporary memory region " + "is being allocated from.", + mem->uaf_guard_active_id, + arena->uaf_guard_id); + } else { + error_msg = DN_Str8Lit("The active temporary memory region recorded on the arena is " + "different from the current temporary memory region recorded on " + "the memory list allocator. This means that a temporary region " + "began but was not ended after the region was completed. Temporary " + "memory regions are enforced in a first-in-last-out manner (FILO) " + "to ensure the developer's intent of what the temporary region " + "spans is logically consistent and always strictly ends and begins " + "within a known lifetime."); + } + DN_Str8 prefix = DN_Str8LineBreakAllocator(error_msg, 100, DN_Str8Lit("\n"), DN_Str8LineBreakMode_AtWord, allocator); if (DN_MemListUAFTracingEnabled_(mem)) { DN_Str8 curr_stack_trace = DN_Str8Lit(""); if (arena->uaf_guard_temp_mem) - curr_stack_trace = DN_StackTraceWalkResultToStr8(arena, &arena->uaf_guard_temp_mem->trace, 1); - curr_stack_trace = DN_Str8PadNewLines(curr_stack_trace, DN_Str8Lit(" "), arena); + curr_stack_trace = DN_Str8FromStackTraceAllocator(allocator, &arena->uaf_guard_temp_mem->trace, 1); + curr_stack_trace = DN_Str8PadNewLinesAllocator(curr_stack_trace, DN_Str8Lit(" "), allocator); + + DN_Str8 active_stack_trace = DN_Str8Lit(""); + if (mem->uaf_guard_active_temp_mem) + active_stack_trace = DN_Str8FromStackTraceAllocator(allocator, &mem->uaf_guard_active_temp_mem->trace, 1); + active_stack_trace = DN_Str8PadNewLinesAllocator(active_stack_trace, DN_Str8Lit(" "), allocator); - DN_Str8 active_stack_trace = DN_Str8PadNewLines(DN_StackTraceWalkResultToStr8(arena, &mem->uaf_guard_active_temp_mem->trace, 1), DN_Str8Lit(" "), arena); DN_AssertF(mem->uaf_guard_active_id == arena->uaf_guard_id, "%.*s\n\nThe originating temporary memory region (id: %'u) was created at:" "\n\n %.*s\n\nThe active temporary memory region (id: %'u) was created at:\n\n %.*s", @@ -1304,7 +1332,7 @@ DN_API void DN_ArenaUAFCheck(DN_Arena *arena) mem->uaf_guard_active_id, DN_Str8PrintFmt(active_stack_trace)); } else { - DN_Str8 suffix = DN_Str8LineBreakStr8(DN_MEM_LIST_UAF_TRACING_DISABLED_MORE_INFO_STR8_, 100, arena); + DN_Str8 suffix = DN_Str8LineBreakAllocator(DN_MEM_LIST_UAF_TRACING_DISABLED_MORE_INFO_STR8_, 100, DN_Str8Lit("\n"), DN_Str8LineBreakMode_AtWord, allocator); DN_AssertF(mem->uaf_guard_active_id == arena->uaf_guard_id, "%.*s%.*s", DN_Str8PrintFmt(prefix), DN_Str8PrintFmt(suffix)); } DN_MSVC_WARNING_POP @@ -1327,8 +1355,10 @@ DN_API DN_Arena DN_ArenaTempBeginFromMemList(DN_MemList* mem) DN_MemListTemp temp_mem = DN_MemListTempBegin(mem); #if DN_ARENA_TEMP_MEM_UAF_GUARD + // NOTE: Below we use the `MemList` and bypass the UAF checks which could cause infinite recursion + // depending on how, say, stack-traces are implemented. if (DN_MemListUAFTracingEnabled_(mem)) - temp_mem.trace = DN_StackTraceWalk(&result, 256); + temp_mem.trace = DN_StackTraceFromAllocator(DN_AllocatorFromMemList(mem), 256); // NOTE: Create persistent temp mem and set it on the mem list result.uaf_guard_temp_mem = DN_MemListNewCopy(mem, DN_MemListTemp, &temp_mem); @@ -1356,52 +1386,11 @@ DN_API void DN_ArenaTempEnd(DN_Arena *arena, DN_ArenaReset reset) { #if DN_ARENA_TEMP_MEM_UAF_GUARD DN_AssertF(arena->uaf_guard_temp_mem, "Arena was not created with temp memory"); + DN_ArenaUAFCheck_(arena, DN_ArenaUAFCheckReportType_TempEndOutOfOrder); #else DN_AssertF(arena->temp_mem.mem, "Arena was not created with temp memory"); #endif -#if DN_ARENA_TEMP_MEM_UAF_GUARD - DN_MemList *mem = arena->mem; - if (mem->uaf_guard_active_id != arena->uaf_guard_id) { - // NOTE: MSVC does not recognise %'u which is a STB extension which causes a lot of incorrect - // format arguments warnings that we mute here. - DN_MSVC_WARNING_PUSH - DN_MSVC_WARNING_DISABLE(6271) // Extra argument passed to 'DN_Str8FromFmtArena' - DN_MSVC_WARNING_DISABLE(6067) // _Param_(10) in call to 'DN_LogPrint' must be the address of a string. Actual type: 'int'. - DN_MSVC_WARNING_DISABLE(6273) // Non-integer passed as _Param_(11) when an integer is required in call to 'DN_LogPrint' Actual type: 'char *'. - - // TODO: If this triggers, using the arena to format the error message is going to trigger the UAF check which is already failing. - DN_Str8 prefix = DN_Str8LineBreakStr8(DN_Str8Lit("The active temporary memory region recorded on the arena is " - "different from the current temporary memory region recorded on " - "the memory list allocator. This means that a temporary region " - "began but was not ended after the region was completed. Temporary " - "memory regions are enforced in a first-in-last-out manner (FILO) " - "to ensure the developer's intent of what the temporary region " - "spans is logically consistent and always strictly ends and begins " - "within a known lifetime."), - 100, - arena); - - if (DN_MemListUAFTracingEnabled_(mem)) { - DN_Str8 curr_stack_trace = DN_Str8PadNewLines(DN_StackTraceWalkResultToStr8(arena, &arena->uaf_guard_temp_mem->trace, 1), DN_Str8Lit(" "), arena); - DN_Str8 active_stack_trace = DN_Str8PadNewLines(DN_StackTraceWalkResultToStr8(arena, &mem->uaf_guard_active_temp_mem->trace, 1), DN_Str8Lit(" "), arena); - DN_AssertF(mem->uaf_guard_active_id == arena->uaf_guard_id, - "%.*s\n\nThe originating temporary memory region (id: %'u) was created at:" - "\n\n %.*s\n\nThe active temporary memory region (id: %'u) was created at:\n\n %.*s", - DN_Str8PrintFmt(prefix), - arena->uaf_guard_id, - DN_Str8PrintFmt(curr_stack_trace), - mem->uaf_guard_active_id, - DN_Str8PrintFmt(active_stack_trace)); - } else { - DN_Str8 suffix = DN_Str8LineBreakStr8(DN_MEM_LIST_UAF_TRACING_DISABLED_MORE_INFO_STR8_, 100, arena); - DN_AssertF(mem->uaf_guard_active_id == arena->uaf_guard_id, "%.*s%.*s", DN_Str8PrintFmt(prefix), DN_Str8PrintFmt(suffix)); - } - DN_MSVC_WARNING_POP - DN_Assert(arena->mem->uaf_guard_active_id == arena->uaf_guard_id); - } -#endif - if (reset == DN_ArenaReset_Yes) { #if DN_ARENA_TEMP_MEM_UAF_GUARD DN_MemListTempEnd(*arena->uaf_guard_temp_mem); @@ -1411,6 +1400,7 @@ DN_API void DN_ArenaTempEnd(DN_Arena *arena, DN_ArenaReset reset) } #if DN_ARENA_TEMP_MEM_UAF_GUARD + DN_MemList *mem = arena->mem; mem->uaf_guard_active_id = arena->uaf_guard_prev_id; mem->uaf_guard_active_temp_mem = arena->uaf_guard_prev_temp_mem; @@ -1422,21 +1412,21 @@ DN_API void DN_ArenaTempEnd(DN_Arena *arena, DN_ArenaReset reset) DN_API void *DN_ArenaAlloc(DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZMem z_mem) { - DN_ArenaUAFCheck(arena); + DN_ArenaUAFCheck_(arena, DN_ArenaUAFCheckReportType_AllocViolation); void *result = DN_MemListAlloc(arena->mem, size, align, z_mem); return result; } DN_API void *DN_ArenaAllocContiguous(DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZMem z_mem) { - DN_ArenaUAFCheck(arena); + DN_ArenaUAFCheck_(arena, DN_ArenaUAFCheckReportType_AllocViolation); void *result = DN_MemListAllocContiguous(arena->mem, size, align, z_mem); return result; } DN_API void *DN_ArenaCopy(DN_Arena *arena, void const *data, DN_U64 size, uint8_t align) { - DN_ArenaUAFCheck(arena); + DN_ArenaUAFCheck_(arena, DN_ArenaUAFCheckReportType_AllocViolation); void *result = DN_MemListCopy(arena->mem, data, size, align); return result; } @@ -1578,7 +1568,7 @@ DN_API DN_ErrSink* DN_ErrSinkBegin_(DN_ErrSink *err, DN_ErrSinkMode mode, DN_Cal DN_Str8Builder builder = DN_Str8BuilderFromArena(err->arena); for (DN_ForItSize(it, DN_ErrSinkNode, err->stack, err->stack_size)) DN_Str8BuilderAppendF(&builder, " [%04zu] %.*s:%u %.*s\n", it.index, DN_Str8PrintFmt(it.data->call_site.file), it.data->call_site.line, DN_Str8PrintFmt(it.data->call_site.function)); - DN_Str8 msg = DN_Str8BuilderBuild(&builder, err->arena); + DN_Str8 msg = DN_Str8FromStr8BuilderArena(&builder, err->arena); DN_AssertF(err->stack_size < DN_ArrayCountU(err->stack), "Error sink has run out of error scopes, potential leak. Scopes were\n%.*s", DN_Str8PrintFmt(msg)); } @@ -1685,7 +1675,7 @@ DN_API DN_Str8 DN_ErrSinkEndStr8(DN_Arena *arena, DN_ErrSink *err) err->stack_size--; DN_MemListPopTo(err->arena->mem, node->arena_pos); - result = DN_Str8BuilderBuild(&builder, arena); + result = DN_Str8FromStr8BuilderArena(&builder, arena); return result; } @@ -1719,7 +1709,7 @@ DN_API bool DN_ErrSinkEndLogError_(DN_ErrSink *err, DN_CallSite call_site, DN_St } // NOTE: Log the error - DN_Str8 log = DN_Str8BuilderBuild(&builder, err->arena); + DN_Str8 log = DN_Str8FromStr8BuilderArena(&builder, err->arena); DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Error), call_site, DN_LogFlags_Nil, "%.*s", DN_Str8PrintFmt(log)); if (node->mode == DN_ErrSinkMode_DebugBreakOnErrorLog) @@ -1794,43 +1784,52 @@ DN_API void DN_ErrSinkAppendF_(DN_ErrSink *err, DN_U32 error_code, DN_CallSite c DN_THREAD_LOCAL DN_TCCore *g_dn_thread_context; -DN_API void DN_TCInit(DN_TCCore *tc, DN_U64 thread_id, DN_Arena *main_arena, DN_Arena *temp_a_arena, DN_Arena *temp_b_arena, DN_Arena *err_sink_arena) +DN_API void DN_TCInit(DN_TCCore *tc, DN_U64 thread_id, DN_Arena *main_arena, DN_Arena *temp_arenas, DN_USize temp_arenas_count, DN_Arena *err_sink_arena) { tc->thread_id = thread_id; tc->main_arena = main_arena; tc->main_pool = DN_PoolFromArena(tc->main_arena, 0); - tc->temp_a_arena = temp_a_arena; - tc->temp_b_arena = temp_b_arena; tc->err_sink.arena = err_sink_arena; + DN_Assert(temp_arenas_count < DN_ArrayCountU(tc->temp_arenas)); + for (DN_ForIndexU(index, temp_arenas_count)) + tc->temp_arenas[tc->temp_arenas_count++] = temp_arenas + index; } -DN_API void DN_TCInitFromMemFuncs(DN_TCCore *tc, DN_U64 thread_id, DN_TCInitArgs *args, DN_MemFuncs mem_funcs) +DN_API DN_TCInitArgs DN_TCInitArgsDefault() { - DN_U64 main_reserve = (args && args->main_reserve) ? args->main_reserve : DN_Kilobytes(64); - DN_U64 main_commit = (args && args->main_commit) ? args->main_commit : DN_Kilobytes(4); - DN_U64 temp_reserve = (args && args->temp_reserve) ? args->temp_reserve : DN_Kilobytes(64); - DN_U64 temp_commit = (args && args->temp_commit) ? args->temp_commit : DN_Kilobytes(4); - DN_U64 err_sink_reserve = (args && args->err_sink_reserve) ? args->err_sink_reserve : DN_Kilobytes(64); - DN_U64 err_sink_commit = (args && args->err_sink_commit) ? args->err_sink_commit : DN_Kilobytes(4); + DN_TCInitArgs result = {}; + result.main_reserve = DN_Kilobytes(64); + result.main_commit = DN_Kilobytes(4); + result.temp_reserve = DN_Kilobytes(64); + result.temp_commit = DN_Kilobytes(4); + result.temp_count = 2; + result.err_sink_reserve = DN_Kilobytes(64); + result.err_sink_commit = DN_Kilobytes(4); + return result; +} - tc->main_arena_mem_ = DN_MemListFromMemFuncs(main_reserve, main_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, mem_funcs); - tc->temp_a_arena_mem_ = DN_MemListFromMemFuncs(temp_reserve, temp_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, mem_funcs); - tc->temp_b_arena_mem_ = DN_MemListFromMemFuncs(temp_reserve, temp_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, mem_funcs); - tc->err_sink_arena_mem_ = DN_MemListFromMemFuncs(err_sink_reserve, err_sink_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, mem_funcs); +DN_API void DN_TCInitFromMemFuncs(DN_TCCore *tc, DN_U64 thread_id, DN_TCInitArgs args, DN_MemFuncs mem_funcs) +{ + DN_Assert(args.temp_count <= DN_ArrayCountU(tc->temp_arenas)); + tc->main_arena_mem_ = DN_MemListFromMemFuncs(args.main_reserve, args.main_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, mem_funcs); + for (DN_ForIndexU(index, args.temp_count)) { + tc->temp_arena_mems_[index] = DN_MemListFromMemFuncs(args.temp_reserve, args.temp_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, mem_funcs); + tc->temp_arenas_[index] = DN_ArenaFromMemList(&tc->temp_arena_mems_[index]); + } + tc->err_sink_arena_mem_ = DN_MemListFromMemFuncs(args.err_sink_reserve, args.err_sink_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, mem_funcs); tc->main_arena_ = DN_ArenaFromMemList(&tc->main_arena_mem_); - tc->temp_a_arena_ = DN_ArenaFromMemList(&tc->temp_a_arena_mem_); - tc->temp_b_arena_ = DN_ArenaFromMemList(&tc->temp_b_arena_mem_); tc->err_sink_arena_ = DN_ArenaFromMemList(&tc->err_sink_arena_mem_); - - DN_TCInit(tc, thread_id, &tc->main_arena_, &tc->temp_a_arena_, &tc->temp_b_arena_, &tc->err_sink_arena_); + DN_TCInit(tc, thread_id, &tc->main_arena_, tc->temp_arenas_, args.temp_count, &tc->err_sink_arena_); } DN_API void DN_TCDeinit(DN_TCCore *tc, DN_TCDeinitArenas deinit_arenas) { if (deinit_arenas == DN_TCDeinitArenas_Yes) { DN_MemListDeinit(tc->main_arena->mem); - DN_MemListDeinit(tc->temp_a_arena->mem); - DN_MemListDeinit(tc->temp_b_arena->mem); + for (DN_ForIndexU(index, tc->temp_arenas_count)) { + DN_MemListDeinit(tc->temp_arenas[index]->mem); + DN_MemListDeinit(tc->temp_arenas[index]->mem); + } DN_MemListDeinit(tc->err_sink.arena->mem); } } @@ -1858,19 +1857,74 @@ DN_API DN_Arena *DN_TCMainArena() DN_API DN_Pool *DN_TCMainPool() { - DN_TCCore *tc = DN_TCGet(); - DN_Pool *result = &tc->main_pool; + DN_TCCore *tc = DN_TCGet(); + DN_Pool *result = &tc->main_pool; return result; } -DN_API DN_Arena DN_TCTempArena(DN_Arena **conflicts, DN_USize count) +DN_API DN_Arena DN_TCTempArenaAllocator(DN_Allocator *conflicts, DN_USize count) +{ + DN_MemList *conflict_mem_lists[8]; + DN_USize conflict_mem_lists_count = 0; + for (DN_ForItSize(it, DN_Allocator, conflicts, count)) { + DN_Allocator *allocator = it.data; + if (!allocator->context) + continue; + + DN_MemList *mem_list = nullptr; + switch (allocator->type) { + case DN_AllocatorType_MemList: mem_list = DN_Cast(DN_MemList *)allocator->context; break; + + case DN_AllocatorType_Arena: { + DN_Arena *arena = DN_Cast(DN_Arena *) allocator->context; + mem_list = arena->mem; + } break; + + case DN_AllocatorType_Pool: { + DN_Pool *pool = DN_Cast(DN_Pool *) allocator->context; + mem_list = pool->arena ? pool->arena->mem : nullptr; + } break; + } + + if (!mem_list) + continue; + + void *added = DN_LArrayAppend(conflict_mem_lists, &conflict_mem_lists_count, mem_list); + DN_Assert(added); + } + + DN_TCCore *tc = DN_TCGet(); + DN_Arena result = {}; + for (DN_ForItSize(it, DN_Arena *, tc->temp_arenas, tc->temp_arenas_count)) { + bool is_usable = true; + DN_Arena *rhs_arena = *it.data; + DN_MemList *rhs_mem = rhs_arena->mem; + for (DN_ForItSize(conflict_it, DN_MemList*, conflict_mem_lists, conflict_mem_lists_count)) { + DN_MemList *lhs_mem = *conflict_it.data; + if (lhs_mem == rhs_mem) { + is_usable = false; + break; + } + } + + if (is_usable) { + result = DN_ArenaTempBeginFromMemList(rhs_mem); + break; + } + } + + DN_AssertF(result.mem, "All temp arenas are being used, there are none left to return to the caller"); + return result; +} + +DN_API DN_Arena DN_TCTempArenaFromArena(DN_Arena **conflicts, DN_USize count) { DN_TCCore *tc = DN_TCGet(); - DN_MemList *candidates[] = {tc->temp_a_arena->mem, tc->temp_b_arena->mem}; DN_Arena result = {}; - for (DN_ForItCArray(it, DN_MemList *, candidates)) { + for (DN_ForItSize(it, DN_Arena *, tc->temp_arenas, tc->temp_arenas_count)) { bool is_usable = true; - DN_MemList *rhs_mem = *it.data; + DN_Arena *rhs_arena = *it.data; + DN_MemList *rhs_mem = rhs_arena->mem; for (DN_ForItSize(conflict_it, DN_Arena *, conflicts, count)) { DN_Arena *lhs_arena = *conflict_it.data; DN_MemList *lhs_mem = lhs_arena->mem; @@ -1893,7 +1947,7 @@ DN_API DN_Arena DN_TCTempArena(DN_Arena **conflicts, DN_USize count) #if defined(__cplusplus) DN_TCScratchCpp::DN_TCScratchCpp(DN_Arena **conflicts, DN_USize count) { - this->data = DN_TCScratchBegin(conflicts, count); + this->data = DN_TCScratchBeginArena(conflicts, count); } DN_TCScratchCpp::~DN_TCScratchCpp() @@ -1902,10 +1956,17 @@ DN_TCScratchCpp::~DN_TCScratchCpp() } #endif -DN_API DN_TCScratch DN_TCScratchBegin(DN_Arena **conflicts, DN_USize count) +DN_API DN_TCScratch DN_TCScratchBeginAllocator(DN_Allocator *conflicts, DN_USize count) { DN_TCScratch result = {}; - result.arena = DN_TCTempArena(conflicts, count); + result.arena = DN_TCTempArenaAllocator(conflicts, count); + return result; +} + +DN_API DN_TCScratch DN_TCScratchBeginArena(DN_Arena **conflicts, DN_USize count) +{ + DN_TCScratch result = {}; + result.arena = DN_TCTempArenaFromArena(conflicts, count); return result; } @@ -2284,6 +2345,43 @@ DN_API DN_U8x32FromResult DN_U8x32FromDecimalStr8(DN_Str8 decimal) return result; } +DN_API DN_Allocator DN_AllocatorFromMemList(DN_MemList *mem) +{ + DN_Allocator result = {}; + result.type = DN_AllocatorType_MemList; + result.context = mem; + return result; +} + +DN_API DN_Allocator DN_AllocatorFromArena(DN_Arena *arena) +{ + DN_Allocator result = {}; + result.type = DN_AllocatorType_Arena; + result.context = arena; + return result; +} + +DN_API DN_Allocator DN_AllocatorFromPool(DN_Pool *pool) +{ + DN_Allocator result = {}; + result.type = DN_AllocatorType_Pool; + result.context = pool; + return result; +} + +DN_API void *DN_AllocatorAlloc(DN_Allocator allocator, DN_USize size, DN_U8 align, DN_ZMem z_mem) +{ + void *result = nullptr; + if (allocator.context) { + switch (allocator.type) { + case DN_AllocatorType_Arena: result = DN_ArenaAlloc (DN_Cast(DN_Arena *) allocator.context, size + 1, align, z_mem); break; + case DN_AllocatorType_Pool: result = DN_PoolAlloc (DN_Cast(DN_Pool *) allocator.context, size + 1); break; + case DN_AllocatorType_MemList: result = DN_MemListAlloc(DN_Cast(DN_MemList *) allocator.context, size + 1, align, z_mem); break; + } + } + return result; +} + DN_API DN_FmtAppendResult DN_FmtVAppend(char *buf, DN_USize *buf_size, DN_USize buf_max, char const *fmt, va_list args) { DN_FmtAppendResult result = {}; @@ -2352,23 +2450,26 @@ DN_API DN_USize DN_CStr16Size(wchar_t const *src) return result; } -DN_API DN_Str8 DN_Str8AllocArena(DN_USize size, DN_ZMem z_mem, DN_Arena *arena) +DN_API DN_Str8 DN_Str8AllocAllocator(DN_USize size, DN_ZMem z_mem, DN_Allocator allocator) { DN_Str8 result = {}; - result.data = DN_ArenaNewArray(arena, char, size + 1, z_mem); - if (result.data) - result.size = size; - result.data[result.size] = 0; + result.data = DN_Cast(char *) DN_AllocatorAlloc(allocator, size + 1, alignof(char), z_mem); + if (result.data) { + result.size = size; + result.data[result.size] = 0; + } + return result; +} + +DN_API DN_Str8 DN_Str8AllocArena(DN_USize size, DN_ZMem z_mem, DN_Arena *arena) +{ + DN_Str8 result = DN_Str8AllocAllocator(size, z_mem, DN_AllocatorFromArena(arena)); return result; } DN_API DN_Str8 DN_Str8AllocPool(DN_USize size, DN_Pool *pool) { - DN_Str8 result = {}; - result.data = DN_PoolNewArray(pool, char, size + 1); - if (result.data) - result.size = size; - result.data[result.size] = 0; + DN_Str8 result = DN_Str8AllocAllocator(size, DN_ZMem_No, DN_AllocatorFromPool(pool)); return result; } @@ -2402,10 +2503,10 @@ DN_API DN_Str8 DN_Str8FromPtrPool(void const *data, DN_USize size, DN_Pool *pool return result; } -DN_API DN_Str8 DN_Str8FromStr8Arena(DN_Str8 string, DN_Arena *arena) +DN_API DN_Str8 DN_Str8FromStr8Allocator(DN_Str8 string, DN_Allocator allocator) { DN_Str8 result = {}; - result.data = DN_Cast(char *) DN_ArenaAlloc(arena, string.size + 1, alignof(char), DN_ZMem_No); + result.data = DN_Cast(char *) DN_AllocatorAlloc(allocator, string.size + 1, alignof(char), DN_ZMem_No); if (result.data) { DN_Memcpy(result.data, string.data, string.size); result.data[string.size] = 0; @@ -2414,27 +2515,42 @@ DN_API DN_Str8 DN_Str8FromStr8Arena(DN_Str8 string, DN_Arena *arena) return result; } +DN_API DN_Str8 DN_Str8FromStr8Arena(DN_Str8 string, DN_Arena *arena) +{ + DN_Str8 result = DN_Str8FromStr8Allocator(string, DN_AllocatorFromArena(arena)); + return result; +} + DN_API DN_Str8 DN_Str8FromStr8Pool(DN_Str8 string, DN_Pool *pool) { - DN_Str8 result = {}; - result.data = DN_Cast(char *) DN_PoolAlloc(pool, string.size + 1); + DN_Str8 result = DN_Str8FromStr8Allocator(string, DN_AllocatorFromPool(pool)); + return result; +} + +DN_API DN_Str8 DN_Str8FromFmtVAllocator(DN_Allocator allocator, DN_FMT_ATTRIB char const *fmt, va_list args) +{ + DN_USize size = DN_FmtVSize(fmt, args); + DN_Str8 result = DN_Str8AllocAllocator(size, DN_ZMem_No, allocator); if (result.data) { - DN_Memcpy(result.data, string.data, string.size); - result.data[string.size] = 0; - result.size = string.size; + DN_USize written = 0; + DN_FmtVAppend(result.data, &written, result.size + 1, fmt, args); + DN_Assert(written == result.size); } return result; } DN_API DN_Str8 DN_Str8FromFmtVArena(DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, va_list args) { - DN_USize size = DN_FmtVSize(fmt, args); - DN_Str8 result = DN_Str8AllocArena(size, DN_ZMem_No, arena); - if (result.data) { - DN_USize written = 0; - DN_FmtVAppend(result.data, &written, result.size + 1, fmt, args); - DN_Assert(written == result.size); - } + DN_Str8 result = DN_Str8FromFmtVAllocator(DN_AllocatorFromArena(arena), fmt, args); + return result; +} + +DN_API DN_Str8 DN_Str8FromFmtAllocator(DN_Allocator allocator, DN_FMT_ATTRIB char const *fmt, ...) +{ + va_list va; + va_start(va, fmt); + DN_Str8 result = DN_Str8FromFmtVAllocator(allocator, fmt, va); + va_end(va); return result; } @@ -2449,13 +2565,7 @@ DN_API DN_Str8 DN_Str8FromFmtArena(DN_Arena *arena, DN_FMT_ATTRIB char const *fm DN_API DN_Str8 DN_Str8FromFmtVPool(DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args) { - DN_USize size = DN_FmtVSize(fmt, args); - DN_Str8 result = DN_Str8AllocPool(size, pool); - if (result.data) { - DN_USize written = 0; - DN_FmtVAppend(result.data, &written, result.size + 1, fmt, args); - DN_Assert(written == result.size); - } + DN_Str8 result = DN_Str8FromFmtVAllocator(DN_AllocatorFromPool(pool), fmt, args); return result; } @@ -3274,7 +3384,7 @@ DN_API DN_Str8 DN_Str8Replace(DN_Str8 string, return result; } - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_Str8Builder string_builder = DN_Str8BuilderFromArena(&scratch.arena); DN_USize max = string.size - find.size; DN_USize head = start_index; @@ -3306,7 +3416,7 @@ DN_API DN_Str8 DN_Str8Replace(DN_Str8 string, } else { DN_Str8 remainder = DN_Str8FromPtr(string.data + head, string.size - head); DN_Str8BuilderAppendRef(&string_builder, remainder); - result = DN_Str8BuilderBuild(&string_builder, arena); + result = DN_Str8FromStr8BuilderArena(&string_builder, arena); } DN_TCScratchEnd(&scratch); return result; @@ -3324,9 +3434,9 @@ DN_API DN_Str8 DN_Str8ReplaceInsensitive(DN_Str8 string, DN_Str8 find, DN_Str8 r return result; } -DN_API DN_Str8 DN_Str8PadNewLines(DN_Str8 string, DN_Str8 pad_string, DN_Arena *arena) +DN_API DN_Str8 DN_Str8PadNewLinesAllocator(DN_Str8 string, DN_Str8 pad_string, DN_Allocator allocator) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginAllocator(&allocator, 1); DN_Str8Builder builder = DN_Str8BuilderFromArena(&scratch.arena); DN_Str8 it = string; while (it.size) { @@ -3335,11 +3445,17 @@ DN_API DN_Str8 DN_Str8PadNewLines(DN_Str8 string, DN_Str8 pad_string, DN_Arena * it = split.rhs; } - DN_Str8 result = DN_Str8BuilderBuildDelimited(&builder, pad_string, arena); + DN_Str8 result = DN_Str8FromStr8BuilderDelimitAllocator(&builder, pad_string, allocator); DN_TCScratchEnd(&scratch); return result; } +DN_API DN_Str8 DN_Str8PadNewLinesArena(DN_Str8 string, DN_Str8 pad_string, DN_Arena *arena) +{ + DN_Str8 result = DN_Str8PadNewLinesAllocator(string, pad_string, DN_AllocatorFromArena(arena)); + return result; +} + DN_API DN_USize DN_USizeCodepointCountFromUTF8(DN_Str8 str, DN_CodepointCountFlags flags) { DN_USize result = 0; @@ -3381,73 +3497,88 @@ DN_API DN_USize DN_USizeCodepointCountFromUTF8(DN_Str8 str, DN_CodepointCountFla return result; } -DN_API DN_Str8 DN_Str8LineBreakStr8(DN_Str8 src, DN_USize desired_width, DN_Arena *arena) +DN_API DN_Str8 DN_Str8LineBreakAllocator(DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Allocator allocator) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginAllocator(&allocator, 1); DN_Str8Builder builder = DN_Str8BuilderFromArena(&scratch.arena); - char* start = src.data; - char* end = src.data; - DN_Str8 it = src; - while (it.size) { - DN_Str8 splitters[] = {DN_Str8Lit(" "), DN_Str8Lit("\n")}; - DN_Str8BSplitResult split = DN_Str8BSplitArray(it, splitters, DN_ArrayCountU(splitters)); - DN_USize curr_line_length = end - start; + if (mode == DN_Str8LineBreakMode_AtWord) { + char* start = src.data; + char* end = src.data; + DN_Str8 it = src; + while (it.size) { + DN_Str8 splitters[] = {DN_Str8Lit(" "), DN_Str8Lit("\n")}; + DN_Str8BSplitResult split = DN_Str8BSplitArray(it, splitters, DN_ArrayCountU(splitters)); + DN_USize curr_line_length = end - start; - // Handle explicit newlines in input - if (split.input_index == 1 /*the newline*/) { - if (curr_line_length == 0 && split.lhs.size) + // Handle explicit newlines in input + if (split.input_index == 1 /*the newline*/) { + if (curr_line_length == 0 && split.lhs.size) + start = split.lhs.data; + if (split.lhs.size) + end = DN_Str8End(split.lhs); + DN_Str8BuilderAppendRef(&builder, DN_Str8FromPtr(start, end - start)); + start = split.rhs.data; + end = split.rhs.data; + it = split.rhs; + continue; + } + + // Skip empty segments (multiple spaces, leading/trailing spaces) + if (split.lhs.size == 0) { + it = split.rhs; + continue; + } + + // First word on this line + if (curr_line_length == 0) { start = split.lhs.data; - if (split.lhs.size) + end = DN_Str8End(split.lhs); + it = split.rhs; + continue; + } + + // Check if adding this word (plus separator space) would overflow + DN_USize combined_length = curr_line_length + 1 + split.lhs.size; + if (combined_length > desired_width) { + // Commit current line, start new line with current word + DN_Str8BuilderAppendRef(&builder, DN_Str8FromPtr(start, end - start)); + start = split.lhs.data; + end = DN_Str8End(split.lhs); + it = split.rhs; + } else { + // Add word to current line end = DN_Str8End(split.lhs); + it = split.rhs; + } + } + + // Append final line + if (end > start) DN_Str8BuilderAppendRef(&builder, DN_Str8FromPtr(start, end - start)); - start = split.rhs.data; - end = split.rhs.data; - it = split.rhs; - continue; - } - - // Skip empty segments (multiple spaces, leading/trailing spaces) - if (split.lhs.size == 0) { - it = split.rhs; - continue; - } - - // First word on this line - if (curr_line_length == 0) { - start = split.lhs.data; - end = DN_Str8End(split.lhs); - it = split.rhs; - continue; - } - - // Check if adding this word (plus separator space) would overflow - DN_USize combined_length = curr_line_length + 1 + split.lhs.size; - if (combined_length > desired_width) { - // Commit current line, start new line with current word - DN_Str8BuilderAppendRef(&builder, DN_Str8FromPtr(start, end - start)); - start = split.lhs.data; - end = DN_Str8End(split.lhs); - it = split.rhs; - } else { - // Add word to current line - end = DN_Str8End(split.lhs); - it = split.rhs; + } else { + DN_Str8 it = src; + while (it.size) { + DN_Str8 chunk = DN_Str8Subset(it, 0, desired_width); + DN_Str8BuilderAppendRef(&builder, chunk); + it = DN_Str8Advance(it, desired_width); } } - // Append final line - if (end > start) - DN_Str8BuilderAppendRef(&builder, DN_Str8FromPtr(start, end - start)); - - DN_Str8 result = DN_Str8BuilderBuildDelimited(&builder, DN_Str8Lit("\n"), arena); + DN_Str8 result = DN_Str8FromStr8BuilderDelimitAllocator(&builder, delimiter, allocator); DN_TCScratchEnd(&scratch); return result; } +DN_API DN_Str8 DN_Str8LineBreakArena(DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Arena *arena) +{ + DN_Str8 result = DN_Str8LineBreakAllocator(src, desired_width, delimiter, mode, DN_AllocatorFromArena(arena)); + return result; +} + DN_API DN_Str8 DN_Str8Table(DN_Str8 const *rows, DN_USize num_rows, DN_USize num_cols, DN_Str8TableFlags flags, DN_Arena *arena) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_U16 col_widths[128] = {}; for (DN_USize i = 0; i < num_cols; i++) { for (DN_USize j = 0; j < num_rows; j++) { @@ -3500,7 +3631,7 @@ DN_API DN_Str8 DN_Str8Table(DN_Str8 const *rows, DN_USize num_rows, DN_USize num DN_Str8BuilderAppendF(&builder, "+"); } - DN_Str8 result = DN_Str8BuilderBuild(&builder, arena); + DN_Str8 result = DN_Str8FromStr8BuilderArena(&builder, arena); DN_TCScratchEnd(&scratch); return result; } @@ -3542,6 +3673,99 @@ DN_API DN_Str8 DN_Str8RenderSpaceSep(DN_Str8Slice slice, DN_Arena *arena) return result; } +DN_API int DN_Str8CompareNatural(DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case) +{ + const char *lhs_it = lhs.data; + const char *rhs_it = rhs.data; + const char *lhs_end = lhs.data + lhs.size; + const char *rhs_end = rhs.data + rhs.size; + + while (lhs_it < lhs_end && rhs_it < rhs_end) { + // NOTE: Skip leading spaces + while (lhs_it < lhs_end && DN_CharIsWhitespace(*lhs_it)) + lhs_it++; + while (rhs_it < rhs_end && DN_CharIsWhitespace(*rhs_it)) + rhs_it++; + + if (lhs_it >= lhs_end || rhs_it >= rhs_end) + break; + + // NOTE: Check if current positions are digits + if (DN_CharIsDigit(*lhs_it) && DN_CharIsDigit(*rhs_it)) { + // NOTE: Extract full number from lhs + DN_U64 lhs_num = 0; + while (lhs_it < lhs_end && DN_CharIsDigit(*lhs_it)) { + lhs_num = lhs_num * 10 + (*lhs_it - '0'); + lhs_it++; + } + + // NOTE: Extract full number from rhs + DN_U64 rhs_num = 0; + while (rhs_it < rhs_end && DN_CharIsDigit(*rhs_it)) { + rhs_num = rhs_num * 10 + (*rhs_it - '0'); + rhs_it++; + } + + if (lhs_num != rhs_num) + return (lhs_num < rhs_num) ? -1 : 1; + } else { + // NOTE: Compare non-digit characters + char lhs_ch = *lhs_it; + char rhs_ch = *rhs_it; + + if (eq_case == DN_Str8EqCase_Insensitive) { + if (DN_CharIsAlphabet(lhs_ch)) + lhs_ch = DN_CharToLower(lhs_ch); + if (DN_CharIsAlphabet(rhs_ch)) + rhs_ch = DN_CharToLower(rhs_ch); + } + + if (lhs_ch != rhs_ch) + return (lhs_ch < rhs_ch) ? -1 : 1; + lhs_it++; + rhs_it++; + } + } + + // NOTE: One string is prefix of other; shorter comes first + if (lhs_it < lhs_end) + return 1; + if (rhs_it < rhs_end) + return -1; + return 0; +} + +DN_API int DN_Str8CompareLexicographic(DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case) +{ + const char *lhs_it = lhs.data; + const char *rhs_it = rhs.data; + const char *lhs_end = lhs.data + lhs.size; + const char *rhs_end = rhs.data + rhs.size; + + while (lhs_it < lhs_end && rhs_it < rhs_end) { + char lhs_ch = *lhs_it; + char rhs_ch = *rhs_it; + if (eq_case == DN_Str8EqCase_Insensitive) { + if (DN_CharIsAlphabet(lhs_ch)) + lhs_ch = DN_CharToLower(lhs_ch); + if (DN_CharIsAlphabet(rhs_ch)) + rhs_ch = DN_CharToLower(rhs_ch); + } + if (lhs_ch != rhs_ch) + return (lhs_ch < rhs_ch) ? -1 : 1; + lhs_it++; + rhs_it++; + } + + // NOTE: One string is prefix of other; shorter comes first + if (lhs.size < rhs.size) + return -1; + if (rhs.size < lhs.size) + return 1; + return 0; +} + + DN_API bool DN_Str16Eq(DN_Str16 lhs, DN_Str16 rhs) { if (lhs.size != rhs.size) @@ -3829,37 +4053,47 @@ DN_API bool DN_Str8BuilderErase(DN_Str8Builder *builder, DN_Str8 string) return false; } -DN_API DN_Str8 DN_Str8BuilderBuild(DN_Str8Builder const *builder, DN_Arena *arena) +DN_API DN_Str8 DN_Str8FromStr8BuilderAllocator(DN_Str8Builder const *builder, DN_Allocator allocator) { - DN_Str8 result = DN_Str8BuilderBuildDelimited(builder, DN_Str8Lit(""), arena); + DN_Str8 result = DN_Str8FromStr8BuilderDelimitAllocator(builder, DN_Str8Lit(""), allocator); return result; } -DN_API DN_Str8 DN_Str8BuilderBuildDelimited(DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Arena *arena) +DN_API DN_Str8 DN_Str8FromStr8BuilderArena(DN_Str8Builder const *builder, DN_Arena *arena) { - DN_Str8 result = DN_ZeroInit; + DN_Str8 result = DN_Str8FromStr8BuilderAllocator(builder, DN_AllocatorFromArena(arena)); + return result; +} + +DN_API DN_Str8 DN_Str8FromStr8BuilderDelimitAllocator(DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Allocator allocator) +{ + DN_Str8 result = {}; if (!builder || builder->string_size <= 0 || builder->count <= 0) return result; DN_USize size_for_delimiter = delimiter.size ? ((builder->count - 1) * delimiter.size) : 0; - result.data = DN_ArenaNewArray(arena, - char, - builder->string_size + size_for_delimiter + 1 /*null terminator*/, - DN_ZMem_No); + result = DN_Str8AllocAllocator(builder->string_size + size_for_delimiter, DN_ZMem_No, allocator); if (!result.data) return result; + DN_USize write_count = 0; 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; + DN_Memcpy(result.data + write_count, link->string.data, link->string.size); + write_count += link->string.size; if (link->next && delimiter.size) { - DN_Memcpy(result.data + result.size, delimiter.data, delimiter.size); - result.size += delimiter.size; + DN_Memcpy(result.data + write_count, delimiter.data, delimiter.size); + write_count += delimiter.size; } } - result.data[result.size] = 0; - DN_Assert(result.size == builder->string_size + size_for_delimiter); + result.data[write_count] = 0; + DN_Assert(write_count == builder->string_size + size_for_delimiter); + return result; +} + +DN_API DN_Str8 DN_Str8FromStr8BuilderDelimitArena(DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Arena *arena) +{ + DN_Str8 result = DN_Str8FromStr8BuilderDelimitAllocator(builder, delimiter, DN_AllocatorFromArena(arena)); return result; } @@ -4053,8 +4287,22 @@ DN_API DN_USize DN_BytesFromHex(DN_Str8 hex, void *dest, DN_USize dest_count) if (hex_trimmed.size > (dest_count * 2)) return result; - DN_U8 *ptr = DN_Cast(DN_U8 *) dest; - for (DN_USize index = 0; index < hex_trimmed.size; index += 2) { + DN_U8 *ptr = DN_Cast(DN_U8 *) dest; + DN_USize index = 0; + + // NOTE: We are given an odd-sized hex string e.g.: 'F' instead of '0F', we 'left-pad' the parser + // and support reading the single nibble as 'F' + if (hex_trimmed.size % 2 != 0) { + DN_U8 nibble0 = 0; + DN_U8 nibble1 = DN_U8FromHexNibble(hex_trimmed.data[index++]); + if (nibble1 == 0xFF) + return result; + *ptr++ = nibble0 << 4 | nibble1 << 0; + result++; + } + + // NOTE: Parse the rest of the hex which is in byte pairs + for (; index < hex_trimmed.size; index += 2) { DN_U8 nibble0 = DN_U8FromHexNibble(hex_trimmed.data[index + 0]); DN_U8 nibble1 = DN_U8FromHexNibble(hex_trimmed.data[index + 1]); if (nibble0 == 0xFF || nibble1 == 0xFF) @@ -4139,6 +4387,7 @@ DN_API DN_USize DN_HexFromPtrBytes(void const *bytes, DN_USize bytes_count, void DN_U8 const *src_u8 = DN_Cast(DN_U8 const *) bytes; DN_U8 *ptr = DN_Cast(DN_U8 *) hex; bool leading_zeros = true; + for (DN_USize index = 0; index < bytes_count; index++) { char ch = src_u8[index]; if (leading_zeros) @@ -4179,6 +4428,17 @@ DN_API DN_USize DN_HexFromStr8Bytes(DN_Str8 bytes, void *hex, DN_USize hex_count return result; } +DN_API DN_Str8 DN_HexFromStr8BytesArena(DN_Str8 bytes, DN_Arena *arena, DN_TrimLeadingZero trim_leading_z) +{ + DN_Str8 result = {}; + if (bytes.size) { + result.data = DN_ArenaNewArray(arena, char, bytes.size * 2, DN_ZMem_No); + if (result.data) + result.size = DN_HexFromStr8Bytes(bytes, result.data, bytes.size * 2, trim_leading_z); + } + return result; +} + DN_API DN_Hex32 DN_Hex32FromPtr16b(void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z) { DN_Hex32 result = {}; @@ -4396,40 +4656,40 @@ DN_API DN_U64 DN_UnixTimeMsFromDate(DN_Date date) return result; } -DN_API DN_Str8 DN_Str8FromByteCountType(DN_ByteCountType type) +DN_API DN_Str8 DN_Str8FromByteType(DN_ByteType type) { DN_Str8 result = DN_Str8Lit(""); switch (type) { - case DN_ByteCountType_B: result = DN_Str8Lit("B"); break; - case DN_ByteCountType_KiB: result = DN_Str8Lit("KiB"); break; - case DN_ByteCountType_MiB: result = DN_Str8Lit("MiB"); break; - case DN_ByteCountType_GiB: result = DN_Str8Lit("GiB"); break; - case DN_ByteCountType_TiB: result = DN_Str8Lit("TiB"); break; - case DN_ByteCountType_Count: result = DN_Str8Lit(""); break; - case DN_ByteCountType_Auto: result = DN_Str8Lit(""); break; + case DN_ByteType_B: result = DN_Str8Lit("B"); break; + case DN_ByteType_KiB: result = DN_Str8Lit("KiB"); break; + case DN_ByteType_MiB: result = DN_Str8Lit("MiB"); break; + case DN_ByteType_GiB: result = DN_Str8Lit("GiB"); break; + case DN_ByteType_TiB: result = DN_Str8Lit("TiB"); break; + case DN_ByteType_Count: result = DN_Str8Lit(""); break; + case DN_ByteType_Auto: result = DN_Str8Lit(""); break; } return result; } -DN_API DN_ByteCountResult DN_ByteCountFromType(DN_U64 bytes, DN_ByteCountType type) +DN_API DN_ByteCount DN_ByteCountFromU64(DN_U64 bytes, DN_ByteType type) { - DN_Assert(type != DN_ByteCountType_Count); - DN_ByteCountResult result = {}; + DN_Assert(type != DN_ByteType_Count); + DN_ByteCount result = {}; result.bytes = DN_Cast(DN_F64) bytes; - if (type == DN_ByteCountType_Auto) - for (; result.type < DN_ByteCountType_Count && result.bytes >= 1024.0; result.type = DN_Cast(DN_ByteCountType)(DN_Cast(DN_USize) result.type + 1)) + if (type == DN_ByteType_Auto) + for (; result.type < DN_ByteType_Count && result.bytes >= 1024.0; result.type = DN_Cast(DN_ByteType)(DN_Cast(DN_USize) result.type + 1)) result.bytes /= 1024.0; else - for (; result.type < type; result.type = DN_Cast(DN_ByteCountType)(DN_Cast(DN_USize) result.type + 1)) + for (; result.type < type; result.type = DN_Cast(DN_ByteType)(DN_Cast(DN_USize) result.type + 1)) result.bytes /= 1024.0; - result.suffix = DN_Str8FromByteCountType(result.type); + result.suffix = DN_Str8FromByteType(result.type); return result; } -DN_API DN_Str8x32 DN_ByteCountStr8x32FromType(DN_U64 bytes, DN_ByteCountType type) +DN_API DN_Str8x32 DN_Str8x32FromByteCountU64(DN_U64 bytes, DN_ByteType type) { - DN_ByteCountResult byte_count = DN_ByteCountFromType(bytes, type); - DN_Str8x32 result = DN_Str8x32FromFmt("%.2f%.*s", byte_count.bytes, DN_Str8PrintFmt(byte_count.suffix)); + DN_ByteCount byte_count = DN_ByteCountFromU64(bytes, type); + DN_Str8x32 result = DN_Str8x32FromFmt("%.2f%.*s", byte_count.bytes, DN_Str8PrintFmt(byte_count.suffix)); return result; } @@ -4492,6 +4752,9 @@ DN_API DN_ProfilerZone DN_ProfilerBeginZone(DN_Profiler *profiler, DN_Str8 name, if (!profiler || profiler->paused) return result; + if (anchor_index != 0) { + DN_AssertF(profiler->frame_zone.profiler, "DN_ProfilerNewFrame() must be called before calling BeginZone"); + } DN_Assert(anchor_index < profiler->anchors_per_frame); DN_ProfilerAnchor *anchor = DN_ProfilerFrameAnchors(profiler).data + anchor_index; anchor->name = name; @@ -4503,6 +4766,7 @@ DN_API DN_ProfilerZone DN_ProfilerBeginZone(DN_Profiler *profiler, DN_Str8 name, DN_AssertF(name == anchor->name, "Potentially overwriting a zone by accident? Anchor is '%.*s', name is '%.*s'", DN_Str8PrintFmt(anchor->name), DN_Str8PrintFmt(name)); #endif + result.profiler = profiler; result.begin_tsc = profiler->tsc_now ? profiler->tsc_now() : DN_CPUGetTSC(); result.anchor_index = anchor_index; result.parent_zone = profiler->parent_zone; @@ -4511,8 +4775,9 @@ DN_API DN_ProfilerZone DN_ProfilerBeginZone(DN_Profiler *profiler, DN_Str8 name, return result; } -DN_API void DN_ProfilerEndZone(DN_Profiler *profiler, DN_ProfilerZone zone) +DN_API void DN_ProfilerEndZone(DN_ProfilerZone zone) { + DN_Profiler *profiler = zone.profiler; if (!profiler || profiler->paused) return; @@ -4524,9 +4789,14 @@ DN_API void DN_ProfilerEndZone(DN_Profiler *profiler, DN_ProfilerZone zone) DN_U64 tsc_now = profiler->tsc_now ? profiler->tsc_now() : DN_CPUGetTSC(); DN_U64 elapsed_tsc = tsc_now - zone.begin_tsc; + // NOTE: We snap the elapsed TSC at the zone start and overwrite every time we end zones. If we + // nest zones, the nested zones will clobber the inclusive timestamp with their values. + // This is fine, as long as all the zones and begun and ended correctly, when the top-most zone + // in the stack ends, it will overwrite the TSC with the elapsed time overall for just that top + // most function, unclobbering the elapsed time sitting in the anchor. + anchor->tsc_inclusive = zone.elapsed_tsc_at_zone_start + elapsed_tsc; + anchor->tsc_exclusive += elapsed_tsc; anchor->hit_count++; - anchor->tsc_inclusive = zone.elapsed_tsc_at_zone_start + elapsed_tsc; - anchor->tsc_exclusive += elapsed_tsc; if (zone.parent_zone != zone.anchor_index) { DN_ProfilerAnchor *parent_anchor = array.data + zone.parent_zone; @@ -4541,7 +4811,7 @@ DN_API void DN_ProfilerNewFrame(DN_Profiler *profiler) return; // NOTE: End the frame's zone - DN_ProfilerEndZone(profiler, profiler->frame_zone); + DN_ProfilerEndZone(profiler->frame_zone); DN_ProfilerAnchorArray old_frame_anchors = DN_ProfilerFrameAnchors(profiler); DN_ProfilerAnchor old_frame_anchor = old_frame_anchors.data[0]; profiler->frame_avg_tsc = (profiler->frame_avg_tsc + old_frame_anchor.tsc_inclusive) / 2.f; @@ -4558,33 +4828,48 @@ DN_API void DN_ProfilerNewFrame(DN_Profiler *profiler) profiler->frame_zone = DN_ProfilerBeginZone(profiler, DN_Str8Lit("Profiler Frame"), 0); } -DN_API void DN_ProfilerDump(DN_Profiler *profiler) +DN_API DN_USize DN_ProfilerFmtAnchor(DN_ProfilerAnchor anchor, DN_U64 tsc_frequency, char *buffer, DN_USize count) +{ + DN_USize result = 0; + if (!anchor.hit_count) + return result; + + DN_U64 tsc_exclusive = anchor.tsc_exclusive; + DN_U64 tsc_inclusive = anchor.tsc_inclusive; + DN_F64 tsc_exclusive_milliseconds = tsc_exclusive * 1000 / DN_Cast(DN_F64) tsc_frequency; + if (tsc_exclusive == tsc_inclusive) { + DN_FmtAppend(buffer, &result, count, "%.*s[%u]: %.1fms", DN_Str8PrintFmt(anchor.name), anchor.hit_count, tsc_exclusive_milliseconds); + } else { + DN_F64 tsc_inclusive_milliseconds = tsc_inclusive * 1000 / DN_Cast(DN_F64) tsc_frequency; + DN_FmtAppend(buffer, &result, count, "%.*s[%u]: %.1f/%.1fms", DN_Str8PrintFmt(anchor.name), anchor.hit_count, tsc_exclusive_milliseconds, tsc_inclusive_milliseconds); + } + return result; +} + +DN_API DN_Str8 DN_ProfilerFmtAnchorStr8(DN_ProfilerAnchor anchor, DN_U64 tsc_frequency, DN_Arena *arena) +{ + DN_Str8 result = {}; + DN_USize size_req = DN_ProfilerFmtAnchor(anchor, tsc_frequency, nullptr, 0); + if (size_req) { + result = DN_Str8AllocArena(size_req, DN_ZMem_No, arena); + DN_ProfilerFmtAnchor(anchor, tsc_frequency, result.data, result.size + 1); + } + return result; +} + +DN_API void DN_ProfilerFmtToStdout(DN_Profiler *profiler) { if (!profiler || profiler->frame_index == 0) return; DN_USize frame_index = profiler->frame_index - 1; - DN_Assert(profiler->frame_index < profiler->anchors_per_frame); - DN_ProfilerAnchor *anchors = profiler->anchors + (frame_index * profiler->anchors_per_frame); for (DN_USize index = 1; index < profiler->anchors_per_frame; index++) { - DN_ProfilerAnchor const *anchor = anchors + index; - if (!anchor->hit_count) - continue; - - DN_U64 tsc_exclusive = anchor->tsc_exclusive; - DN_U64 tsc_inclusive = anchor->tsc_inclusive; - DN_F64 tsc_exclusive_milliseconds = tsc_exclusive * 1000 / DN_Cast(DN_F64) profiler->tsc_frequency; - if (tsc_exclusive == tsc_inclusive) { - DN_OS_PrintOutLnF("%.*s[%u]: %.1fms", DN_Str8PrintFmt(anchor->name), anchor->hit_count, tsc_exclusive_milliseconds); - } else { - DN_F64 tsc_inclusive_milliseconds = tsc_inclusive * 1000 / DN_Cast(DN_F64) profiler->tsc_frequency; - DN_OS_PrintOutLnF("%.*s[%u]: %.1f/%.1fms", - DN_Str8PrintFmt(anchor->name), - anchor->hit_count, - tsc_exclusive_milliseconds, - tsc_inclusive_milliseconds); - } + char buffer[2048]; + buffer[0] = 0; + DN_USize fmt_len = DN_ProfilerFmtAnchor(anchors[index], profiler->tsc_frequency, buffer, DN_ArrayCountU(buffer)); + DN_Str8 msg = DN_Str8FromPtr(buffer, fmt_len); + DN_OS_PrintOutLnF("%.*s", DN_Str8PrintFmt(msg)); } } @@ -4600,6 +4885,204 @@ DN_API DN_F64 DN_ProfilerMsFromTSC(DN_Profiler *profiler, DN_U64 duration_tsc) return result; } +static void DN_QSortSetElem_(void *array, DN_USize elem_size, DN_USize dest_index, DN_USize src_index) +{ + char *src = DN_Cast(char *) array + (src_index * elem_size); + char *dest = DN_Cast(char *) array + (dest_index * elem_size); + DN_Memcpy(dest, src, elem_size); +} + +static void DN_QSortSwapElems_(void *array, DN_USize elem_size, DN_USize lhs_index, DN_USize rhs_index) +{ + if (lhs_index == rhs_index) + return; + + char temp_buffer[512]; + bool use_buffer = elem_size <= DN_ArrayCountU(temp_buffer); + DN_TCScratch scratch = {}; + char *temp = {}; + if (use_buffer) { + temp = temp_buffer; + } else { + scratch = DN_TCScratchBeginArena(nullptr, 0); + temp = DN_ArenaNewArray(&scratch.arena, char, elem_size, DN_ZMem_No); + } + + char *lhs = DN_Cast(char *) array + (lhs_index * elem_size); + char *rhs = DN_Cast(char *) array + (rhs_index * elem_size); + DN_Memcpy(temp, lhs, elem_size); + DN_Memcpy(lhs, rhs, elem_size); + DN_Memcpy(rhs, temp, elem_size); + + if (!use_buffer) + DN_TCScratchEnd(&scratch); +} + +static void DN_QSortInsertion_(void *array, DN_USize array_size, DN_USize elem_size, void *user_context, DN_QSortCompareFunc *compare) +{ + char temp_buffer[512]; + bool use_buffer = elem_size <= DN_ArrayCountU(temp_buffer); + DN_TCScratch scratch = {}; + char *temp = {}; + if (use_buffer) { + temp = temp_buffer; + } else { + scratch = DN_TCScratchBeginArena(nullptr, 0); + temp = DN_ArenaNewArray(&scratch.arena, char, elem_size, DN_ZMem_No); + } + + DN_U8 *array_u8 = DN_Cast(DN_U8 *)array; + for (DN_USize item_to_insert_index = 1; item_to_insert_index < array_size; item_to_insert_index++) { + for (DN_USize index = 0; index < item_to_insert_index; index++) { + DN_U8 *lhs = array_u8 + (index * elem_size); + DN_U8 *rhs = array_u8 + (item_to_insert_index * elem_size); + if (compare(lhs, rhs, user_context)) + continue; + + DN_Memcpy(temp, rhs, elem_size); + for (DN_USize i = item_to_insert_index; i > index; i--) + DN_QSortSetElem_(array, elem_size, i, i - 1); + DN_Memcpy(lhs, temp, elem_size); + break; + } + } + + if (!use_buffer) + DN_TCScratchEnd(&scratch); +} + +DN_API void DN_QSort(void *array, DN_USize array_size, DN_USize elem_size, void *user_context, DN_QSortCompareFunc *compare) +{ + if (!array || array_size <= 1 || elem_size == 0 || !compare) + return; + + // NOTE: Insertion Sort, under 24->32 is an optimal amount + DN_U8 *array_u8 = DN_Cast(DN_U8 *)array; + DN_USize const QSORT_THRESHOLD = 24; + if (array_size < QSORT_THRESHOLD) { + DN_QSortInsertion_(array, array_size, elem_size, user_context, compare); + return; + } + + // NOTE: Quick sort, under 24->32 is an optimal amount + DN_USize last_index = array_size - 1; + DN_USize pivot_index = array_size / 2; + DN_USize partition_index = 0; + DN_USize start_index = 0; + + // Swap pivot with last index, so pivot is always at the end of the array. + // This makes logic much simpler. + DN_QSortSwapElems_(array, elem_size, last_index, pivot_index); + pivot_index = last_index; + + // 4^, 8, 7, 5, 2, 3, 6 + if (compare(array_u8 + (start_index * elem_size), array_u8 + (pivot_index * elem_size), user_context)) + partition_index++; + start_index++; + + // 4, |8, 7, 5^, 2, 3, 6* + // 4, 5, |7, 8, 2^, 3, 6* + // 4, 5, 2, |8, 7, ^3, 6* + // 4, 5, 2, 3, |7, 8, ^6* + for (DN_USize index = start_index; index < last_index; index++) { + if (compare(array_u8 + (index * elem_size), array_u8 + (pivot_index * elem_size), user_context)) { + DN_QSortSwapElems_(array, elem_size, partition_index, index); + partition_index++; + } + } + + // Move pivot to right of partition + // 4, 5, 2, 3, |6, 8, ^7* + DN_QSortSwapElems_(array, elem_size, partition_index, pivot_index); + DN_QSort(array_u8, partition_index, elem_size, user_context, compare); + + // Skip the value at partion index since that is guaranteed to be sorted. + // 4, 5, 2, 3, (x), 8, 7 + DN_USize one_after_partition_index = partition_index + 1; + DN_QSort(array_u8 + (one_after_partition_index * elem_size), (array_size - one_after_partition_index), elem_size, user_context, compare); +} + +DN_API bool DN_QSortCompareStr8NaturalAsc(void const* lhs, void const *rhs, void *user_context) +{ + DN_Str8EqCase eq_case = *DN_Cast(DN_Str8EqCase *) user_context; + DN_Str8 lhs_str8 = *DN_Cast(DN_Str8 *) lhs; + DN_Str8 rhs_str8 = *DN_Cast(DN_Str8 *) rhs; + bool result = DN_Str8CompareNatural(lhs_str8, rhs_str8, eq_case) < 0; + return result; +} + +DN_API bool DN_QSortCompareStr8NaturalDesc(void const* lhs, void const *rhs, void *user_context) +{ + DN_Str8EqCase eq_case = *DN_Cast(DN_Str8EqCase *) user_context; + DN_Str8 lhs_str8 = *DN_Cast(DN_Str8 *) lhs; + DN_Str8 rhs_str8 = *DN_Cast(DN_Str8 *) rhs; + bool result = DN_Str8CompareNatural(lhs_str8, rhs_str8, eq_case) > 0; + return result; +} + +DN_API bool DN_QSortCompareStr8LexicographicAsc(void const* lhs, void const *rhs, void *user_context) +{ + DN_Str8EqCase eq_case = *DN_Cast(DN_Str8EqCase *) user_context; + DN_Str8 lhs_str8 = *DN_Cast(DN_Str8 *) lhs; + DN_Str8 rhs_str8 = *DN_Cast(DN_Str8 *) rhs; + bool result = DN_Str8CompareLexicographic(lhs_str8, rhs_str8, eq_case) < 0; + return result; +} + +DN_API bool DN_QSortCompareStr8LexicographicDesc(void const* lhs, void const *rhs, void *user_context) +{ + DN_Str8EqCase eq_case = *DN_Cast(DN_Str8EqCase *) user_context; + DN_Str8 lhs_str8 = *DN_Cast(DN_Str8 *) lhs; + DN_Str8 rhs_str8 = *DN_Cast(DN_Str8 *) rhs; + bool result = DN_Str8CompareLexicographic(lhs_str8, rhs_str8, eq_case) > 0; + return result; +} + +DN_API bool DN_QSortCompareBytesLT(void const* lhs, void const *rhs, void *user_context) +{ + DN_USize elem_size = *DN_Cast(DN_USize *)user_context; + bool result = DN_Memcmp(lhs, rhs, elem_size) < 0; + return result; +} + +DN_API bool DN_QSortCompareBytesGT(void const* lhs, void const *rhs, void *user_context) +{ + DN_USize elem_size = *DN_Cast(DN_USize *)user_context; + bool result = DN_Memcmp(lhs, rhs, elem_size) > 0; + return result; +} + +DN_API void DN_QSortBytesLT(void *array, DN_USize array_size, DN_USize elem_size) +{ + DN_QSort(array, array_size, elem_size, &elem_size, DN_QSortCompareBytesLT); +} + +DN_API void DN_QSortBytesGT(void *array, DN_USize array_size, DN_USize elem_size) +{ + DN_QSort(array, array_size, elem_size, &elem_size, DN_QSortCompareBytesGT); +} + +DN_API void DN_QSortStr8NaturalAsc(DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case) +{ + DN_QSort(array, array_size, sizeof(*array), /*user_context=*/ &eq_case, DN_QSortCompareStr8NaturalAsc); +} + +DN_API void DN_QSortStr8NaturalDesc(DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case) +{ + DN_QSort(array, array_size, sizeof(*array), /*user_context=*/ &eq_case, DN_QSortCompareStr8NaturalDesc); +} + +DN_API void DN_QSortStr8LexicographicAsc(DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case) +{ + DN_QSort(array, array_size, sizeof(*array), /*user_context=*/ &eq_case, DN_QSortCompareStr8LexicographicAsc); +} + +DN_API void DN_QSortStr8LexicographicDesc(DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case) +{ + DN_QSort(array, array_size, sizeof(*array), /*user_context=*/ &eq_case, DN_QSortCompareStr8LexicographicDesc); +} + + #define DN_PCG_DEFAULT_MULTIPLIER_64 6364136223846793005ULL #define DN_PCG_DEFAULT_INCREMENT_64 1442695040888963407ULL DN_API DN_PCG32 DN_PCG32Init(DN_U64 seed) @@ -4961,7 +5444,7 @@ DN_API DN_Str8 DN_Str8FromStr8ANSIColourV3F32RGB255Arena(DN_ANSIColourMode mode, DN_API DN_Str8 DN_Str8ANSIColourU8RGBFromFmtVArena(DN_ANSIColourMode mode, DN_U8 r, DN_U8 g, DN_U8 b, DN_Arena *arena, char const *fmt, va_list args) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_Str8 string = DN_Str8FromFmtVArena(&scratch.arena, fmt, args); DN_Str8 result = DN_Str8FromStr8ANSIColourU8RGBArena(mode, string, r, g, b, arena); DN_TCScratchEnd(&scratch); @@ -6841,6 +7324,13 @@ DN_API DN_RaycastV2 DN_RaycastLineIntersectV2(DN_V2F32 origin_a, DN_V2F32 dir_a, // #include "../dn.h" // #endif +struct DN_ArrayFindEqMemcmpContext_ +{ + DN_USize elem_size; + void const *find; +}; + + DN_API void *DN_SliceAllocArena(void **data, DN_USize *slice_size_field, DN_USize size, DN_USize elem_size, DN_U8 align, DN_ZMem zmem, DN_Arena *arena) { void *result = *data; @@ -6850,7 +7340,42 @@ DN_API void *DN_SliceAllocArena(void **data, DN_USize *slice_size_field, DN_USiz return result; } -DN_API void *DN_CArrayInsertArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize index, void const *items, DN_USize count) +DN_API DN_ArrayFindResult DN_ArrayFind(void *data, DN_USize size, DN_USize elem_size, void const *find, DN_ArrayFindEqFunc *eq_func) +{ + DN_ArrayFindResult result = {}; + DN_Assert(data); + DN_Assert(elem_size); + if (find) { + for (DN_ForIndexU(index, size)) { + DN_U8 *it = DN_Cast(DN_U8 *) data + (index * elem_size); + if (eq_func(it, find)) { + result.index = index; + result.value = it; + result.success = true; + break; + } + } + } + return result; +} + +static bool DN_ArrayFindEqMemEqUnsafe_(void const *lhs, void const *find) +{ + DN_ArrayFindEqMemcmpContext_ *context = DN_Cast(DN_ArrayFindEqMemcmpContext_ *) find; + bool result = DN_MemEqUnsafe(lhs, context->find, context->elem_size); + return result; +} + +DN_API DN_ArrayFindResult DN_ArrayFindMemEq(void *data, DN_USize size, DN_USize elem_size, void const *find) +{ + DN_ArrayFindEqMemcmpContext_ context = {}; + context.elem_size = elem_size; + context.find = find; + DN_ArrayFindResult result = DN_ArrayFind(data, size, elem_size, &context, DN_ArrayFindEqMemEqUnsafe_); + return result; +} + +DN_API void *DN_ArrayInsertArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize index, void const *items, DN_USize count) { void *result = nullptr; if (!data || !size || !items || count <= 0 || ((*size + count) > max)) @@ -6871,7 +7396,7 @@ DN_API void *DN_CArrayInsertArray(void *data, DN_USize *size, DN_USize max, DN_U return result; } -DN_API void *DN_CArrayPopFront(void *data, DN_USize *size, DN_USize elem_size, DN_USize count) +DN_API void *DN_ArrayPopFront(void *data, DN_USize *size, DN_USize elem_size, DN_USize count) { if (!data || !size || *size == 0 || count == 0) return nullptr; @@ -6890,7 +7415,7 @@ DN_API void *DN_CArrayPopFront(void *data, DN_USize *size, DN_USize elem_size, D return result; } -DN_API void *DN_CArrayPopBack(void *data, DN_USize *size, DN_USize elem_size, DN_USize count) +DN_API void *DN_ArrayPopBack(void *data, DN_USize *size, DN_USize elem_size, DN_USize count) { if (!data || !size || *size == 0 || count == 0) return nullptr; @@ -6901,7 +7426,7 @@ DN_API void *DN_CArrayPopBack(void *data, DN_USize *size, DN_USize elem_size, DN return DN_Cast(char *)data + (*size * elem_size); } -DN_API DN_ArrayEraseResult DN_CArrayEraseRange(void *data, DN_USize *size, DN_USize elem_size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase) +DN_API DN_ArrayEraseResult DN_ArrayEraseRange(void *data, DN_USize *size, DN_USize elem_size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase) { DN_ArrayEraseResult result = {}; if (!data || !size || *size == 0 || count == 0) @@ -6943,48 +7468,47 @@ DN_API DN_ArrayEraseResult DN_CArrayEraseRange(void *data, DN_USize *size, DN_US } result.items_erased = erase_count; - result.it_index = start; + result.it_index = start ? start - 1 : start; return result; } -DN_API void *DN_CArrayMakeArray(void *data, DN_USize *size, DN_USize max, DN_USize data_size, DN_USize make_size, DN_ZMem z_mem) +DN_API void *DN_ArrayMakeArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize make_count, DN_ZMem z_mem) { - void *result = nullptr; - DN_USize new_size = *size + make_size; + void *result = nullptr; + DN_USize new_size = *size + make_count; if (new_size <= max) { - result = DN_Cast(char *) data + (data_size * size[0]); + result = DN_Cast(char *) data + (elem_size * size[0]); *size = new_size; if (z_mem == DN_ZMem_Yes) - DN_Memset(result, 0, data_size * make_size); + DN_Memset(result, 0, elem_size * make_count); } - return result; } -DN_API void *DN_CArrayAddArray(void *data, DN_USize *size, DN_USize max, DN_USize data_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add) +DN_API void *DN_ArrayAddArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add) { - void *result = DN_CArrayMakeArray(data, size, max, data_size, elems_count, DN_ZMem_No); + void *result = DN_ArrayMakeArray(data, size, max, elem_size, elems_count, DN_ZMem_No); if (result) { if (add == DN_ArrayAdd_Append) { - DN_Memcpy(result, elems, elems_count * data_size); + DN_Memcpy(result, elems, elems_count * elem_size); } else { - char *move_dest = DN_Cast(char *)data + (elems_count * data_size); // Shift elements forward + char *move_dest = DN_Cast(char *)data + (elems_count * elem_size); // Shift elements forward char *move_src = DN_Cast(char *)data; - DN_Memmove(move_dest, move_src, data_size * size[0]); - DN_Memcpy(data, elems, data_size * elems_count); + DN_Memmove(move_dest, move_src, elem_size * size[0]); + DN_Memcpy(data, elems, elem_size * elems_count); } } return result; } -DN_API bool DN_CArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize new_max) +DN_API bool DN_ArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max) { bool result = true; if (new_max != *max) { - DN_USize bytes_to_alloc = data_size * new_max; + DN_USize bytes_to_alloc = elem_size * new_max; void *buffer = DN_PoolNewArray(pool, DN_U8, bytes_to_alloc); if (buffer) { - DN_USize bytes_to_copy = data_size * DN_Min(*size, new_max); + DN_USize bytes_to_copy = elem_size * DN_Min(*size, new_max); DN_Memcpy(buffer, *data, bytes_to_copy); DN_PoolDealloc(pool, *data); *data = buffer; @@ -6998,14 +7522,14 @@ DN_API bool DN_CArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, return result; } -DN_API bool DN_CArrayResizeFromPool(void **data, DN_USize *size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize new_max) +DN_API bool DN_ArrayResizeFromPool(void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max) { bool result = true; if (new_max != *max) { - DN_USize bytes_to_alloc = data_size * new_max; + DN_USize bytes_to_alloc = elem_size * new_max; void *buffer = DN_PoolNewArray(pool, DN_U8, bytes_to_alloc); if (buffer) { - DN_USize bytes_to_copy = data_size * DN_Min(*size, new_max); + DN_USize bytes_to_copy = elem_size * DN_Min(*size, new_max); DN_Memcpy(buffer, *data, bytes_to_copy); DN_PoolDealloc(pool, *data); *data = buffer; @@ -7019,14 +7543,14 @@ DN_API bool DN_CArrayResizeFromPool(void **data, DN_USize *size, DN_USize *max, return result; } -DN_API bool DN_CArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, DN_USize data_size, DN_Arena *arena, DN_USize new_max) +DN_API bool DN_ArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max) { bool result = true; if (new_max != *max) { - DN_USize bytes_to_alloc = data_size * new_max; + DN_USize bytes_to_alloc = elem_size * new_max; void *buffer = DN_ArenaNewArray(arena, DN_U8, bytes_to_alloc, DN_ZMem_No); if (buffer) { - DN_USize bytes_to_copy = data_size * DN_Min(*size, new_max); + DN_USize bytes_to_copy = elem_size * DN_Min(*size, new_max); DN_Memcpy(buffer, *data, bytes_to_copy); *data = buffer; *max = new_max; @@ -7039,41 +7563,41 @@ DN_API bool DN_CArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, return result; } -DN_API bool DN_CArrayGrowFromPool(void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize new_max) +DN_API bool DN_ArrayGrowFromPool(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max) { bool result = true; if (new_max > *max) - result = DN_CArrayResizeFromPool(data, &size, max, data_size, pool, new_max); + result = DN_ArrayResizeFromPool(data, &size, max, elem_size, pool, new_max); return result; } -DN_API bool DN_CArrayGrowFromArena(void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Arena *arena, DN_USize new_max) +DN_API bool DN_ArrayGrowFromArena(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max) { bool result = true; if (new_max > *max) - result = DN_CArrayResizeFromArena(data, &size, max, data_size, arena, new_max); + result = DN_ArrayResizeFromArena(data, &size, max, elem_size, arena, new_max); return result; } -DN_API bool DN_CArrayGrowIfNeededFromPool(void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize add_count) +DN_API bool DN_ArrayGrowIfNeededFromPool(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize add_count) { bool result = true; DN_USize new_size = size + add_count; if (new_size > *max) { DN_USize new_max = DN_Max(DN_Max(*max * 2, new_size), 8); - result = DN_CArrayResizeFromPool(data, &size, max, data_size, pool, new_max); + result = DN_ArrayResizeFromPool(data, &size, max, elem_size, pool, new_max); } return result; } -DN_API bool DN_CArrayGrowIfNeededFromArena(void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Arena *arena, DN_USize add_count) +DN_API bool DN_ArrayGrowIfNeededFromArena(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize add_count) { bool result = true; DN_USize new_size = size + add_count; if (new_size > *max) { DN_USize new_max = DN_Max(DN_Max(*max * 2, new_size), 8); - result = DN_CArrayResizeFromArena(data, &size, max, data_size, arena, new_max); + result = DN_ArrayResizeFromArena(data, &size, max, elem_size, arena, new_max); } return result; } @@ -7608,19 +8132,15 @@ DN_API void DN_LeakTrackAlloc_(DN_LeakTracker *leak, void *ptr, DN_USize size, b return; DN_TicketMutex_Begin(&leak->alloc_table_mutex); - DN_DEFER - { - DN_TicketMutex_End(&leak->alloc_table_mutex); - }; - DN_Str8 stack_trace = DN_StackTraceWalkStr8FromHeap(128, 3 /*skip*/); + DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/); DN_DSMap *alloc_table = &leak->alloc_table; DN_DSMapResult alloc_entry = DN_DSMapMakeKeyU64(alloc_table, DN_Cast(DN_U64) ptr); DN_LeakAlloc *alloc = alloc_entry.value; if (alloc_entry.found) { if ((alloc->flags & DN_LeakAllocFlag_Freed) == 0) { - DN_Str8x32 alloc_size = DN_ByteCountStr8x32(alloc->size); - DN_Str8x32 new_alloc_size = DN_ByteCountStr8x32(size); + DN_Str8x32 alloc_size = DN_Str8x32FromByteCountU64Auto(alloc->size); + DN_Str8x32 new_alloc_size = DN_Str8x32FromByteCountU64Auto(size); DN_HardAssertF( alloc->flags & DN_LeakAllocFlag_Freed, "This pointer is already in the leak tracker, however it has not been freed yet. This " @@ -7656,6 +8176,7 @@ DN_API void DN_LeakTrackAlloc_(DN_LeakTracker *leak, void *ptr, DN_USize size, b alloc->stack_trace = stack_trace; alloc->flags |= leak_permitted ? DN_LeakAllocFlag_LeakPermitted : 0; leak->alloc_table_bytes_allocated_for_stack_traces += alloc->stack_trace.size; + DN_TicketMutex_End(&leak->alloc_table_mutex); } DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr) @@ -7664,12 +8185,8 @@ DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr) return; DN_TicketMutex_Begin(&leak->alloc_table_mutex); - DN_DEFER - { - DN_TicketMutex_End(&leak->alloc_table_mutex); - }; - DN_Str8 stack_trace = DN_StackTraceWalkStr8FromHeap(128, 3 /*skip*/); + DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/); DN_DSMap *alloc_table = &leak->alloc_table; DN_DSMapResult alloc_entry = DN_DSMapFindKeyU64(alloc_table, DN_Cast(uintptr_t) ptr); DN_HardAssertF(alloc_entry.found, @@ -7680,7 +8197,7 @@ DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr) DN_LeakAlloc *alloc = alloc_entry.value; if (alloc->flags & DN_LeakAllocFlag_Freed) { - DN_Str8x32 freed_size = DN_ByteCountStr8x32(alloc->freed_size); + DN_Str8x32 freed_size = DN_Str8x32FromByteCountU64Auto(alloc->freed_size); DN_HardAssertF((alloc->flags & DN_LeakAllocFlag_Freed) == 0, "Double free detected, pointer to free was already marked " "as freed. Either the pointer was reallocated but not " @@ -7708,6 +8225,7 @@ DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr) alloc->flags |= DN_LeakAllocFlag_Freed; alloc->freed_stack_trace = stack_trace; leak->alloc_table_bytes_allocated_for_stack_traces += alloc->freed_stack_trace.size; + DN_TicketMutex_End(&leak->alloc_table_mutex); } DN_API void DN_LeakDump_(DN_LeakTracker *leak) @@ -7722,7 +8240,7 @@ DN_API void DN_LeakDump_(DN_LeakTracker *leak) if (alloc_leaked && !leak_permitted) { leaked_bytes += alloc->size; leak_count++; - DN_Str8x32 alloc_size = DN_ByteCountStr8x32(alloc->size); + DN_Str8x32 alloc_size = DN_Str8x32FromByteCountU64Auto(alloc->size); DN_LogWarningF( "Pointer (0x%p) leaked %.*s at:\n" "%.*s", @@ -7733,7 +8251,7 @@ DN_API void DN_LeakDump_(DN_LeakTracker *leak) } if (leak_count) { - DN_Str8x32 leak_size = DN_ByteCountStr8x32(leaked_bytes); + DN_Str8x32 leak_size = DN_Str8x32FromByteCountU64Auto(leaked_bytes); DN_LogWarningF("There were %I64u leaked allocations totalling %.*s", leak_count, DN_Str8PrintFmt(leak_size)); } } @@ -7811,6 +8329,30 @@ DN_API DN_MemList DN_MemListFromVMem(DN_U64 reserve, DN_U64 commit, DN_MemFlags return result; } +DN_API DN_Arena DN_ArenaFromHeap(DN_U64 size, DN_MemFlags flags) +{ + DN_MemList mem = DN_MemListFromHeap(size, flags); + DN_Arena result = {}; + result.flags |= DN_ArenaFlags_OwnsMemList; + result.mem = DN_MemListNewCopy(&mem, DN_MemList, &mem); + return result; +} + +DN_API DN_Arena DN_ArenaFromVMem(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags) +{ + DN_MemList mem = DN_MemListFromVMem(reserve, commit, flags); + DN_Arena result = {}; + result.flags |= DN_ArenaFlags_OwnsMemList; + result.mem = DN_MemListNewCopy(&mem, DN_MemList, &mem); + return result; +} + +DN_API void DN_ArenaDeinit(DN_Arena *arena) +{ + if (arena->flags & DN_ArenaFlags_OwnsMemList) + DN_MemListDeinit(arena->mem); +} + DN_API DN_Str8 DN_Str8FromHeapF(DN_FMT_ATTRIB char const *fmt, ...) { va_list args; @@ -7836,7 +8378,7 @@ DN_API DN_Str8 DN_Str8FromHeap(DN_USize size, DN_ZMem z_mem) DN_API DN_Str8 DN_Str8PadNewLines(DN_Arena *arena, DN_Str8 src, DN_Str8 pad) { // TODO: Implement this without requiring TLS so it can go into base strings - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_Str8Builder builder = DN_Str8BuilderFromArena(&scratch.arena); DN_Str8BSplitResult split = DN_Str8BSplit(src, DN_Str8Lit("\n")); while (split.lhs.size) { @@ -7846,7 +8388,7 @@ DN_API DN_Str8 DN_Str8PadNewLines(DN_Arena *arena, DN_Str8 src, DN_Str8 pad) if (split.lhs.size) DN_Str8BuilderAppendRef(&builder, DN_Str8Lit("\n")); } - DN_Str8 result = DN_Str8BuilderBuild(&builder, arena); + DN_Str8 result = DN_Str8FromStr8BuilderArena(&builder, arena); DN_TCScratchEnd(&scratch); return result; } @@ -7879,7 +8421,7 @@ DN_API void DN_OS_LogPrint(DN_LogTypeParam type, void *user_data, DN_CallSite ca // NOTE: Open log file for appending if requested DN_TicketMutex_Begin(&os->log_file_mutex); if (os->log_to_file && !os->log_file.handle && !os->log_file.error) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 exe_dir = DN_OS_EXEDir(&scratch.arena); DN_Str8 log_path = DN_OS_PathF(&scratch.arena, "%.*s/dn.log", DN_Str8PrintFmt(exe_dir)); os->log_file = DN_OS_FileOpen(log_path, DN_OSFileOpen_CreateAlways, DN_OSFileAccess_AppendOnly, nullptr); @@ -7994,7 +8536,7 @@ DN_API DN_Str8 DN_OS_EXEDir(DN_Arena *arena) DN_Str8 result = {}; if (!arena) return result; - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_Str8 exe_path = DN_OS_EXEPath(&scratch.arena); DN_Str8 separators[] = {DN_Str8Lit("/"), DN_Str8Lit("\\")}; DN_Str8BSplitResult split = DN_Str8BSplitLastArray(exe_path, separators, DN_ArrayCountU(separators)); @@ -8165,7 +8707,7 @@ DN_API DN_Str8 DN_OS_FileReadAll(DN_Allocator allocator, DN_Str8 path, DN_ErrSin } if (!result.data) { - DN_Str8x32 bytes_str = DN_ByteCountStr8x32(path_info.size); + DN_Str8x32 bytes_str = DN_Str8x32FromByteCountU64Auto(path_info.size); DN_ErrSinkAppendF(err, 1 /*err_code*/, "Failed to allocate %.*s for reading file '%.*s'", DN_Str8PrintFmt(bytes_str), DN_Str8PrintFmt(path)); return result; } @@ -8219,7 +8761,7 @@ DN_API bool DN_OS_FileWriteAll(DN_Str8 path, DN_Str8 buffer, DN_ErrSink *error) DN_API bool DN_OS_FileWriteAllFV(DN_Str8 file_path, DN_ErrSink *error, DN_FMT_ATTRIB char const *fmt, va_list args) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 buffer = DN_Str8FromFmtVArena(&scratch.arena, fmt, args); bool result = DN_OS_FileWriteAll(file_path, buffer, error); DN_TCScratchEnd(&scratch); @@ -8237,7 +8779,7 @@ DN_API bool DN_OS_FileWriteAllF(DN_Str8 file_path, DN_ErrSink *error, DN_FMT_ATT DN_API bool DN_OS_FileWriteAllSafe(DN_Str8 path, DN_Str8 buffer, DN_ErrSink *error) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 tmp_path = DN_Str8FromFmtArena(&scratch.arena, "%.*s.tmp", DN_Str8PrintFmt(path)); if (!DN_OS_FileWriteAll(tmp_path, buffer, error)) { DN_TCScratchEnd(&scratch); @@ -8257,7 +8799,7 @@ DN_API bool DN_OS_FileWriteAllSafe(DN_Str8 path, DN_Str8 buffer, DN_ErrSink *err DN_API bool DN_OS_FileWriteAllSafeFV(DN_Str8 path, DN_ErrSink *error, DN_FMT_ATTRIB char const *fmt, va_list args) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 buffer = DN_Str8FromFmtVArena(&scratch.arena, fmt, args); bool result = DN_OS_FileWriteAllSafe(path, buffer, error); DN_TCScratchEnd(&scratch); @@ -8374,7 +8916,7 @@ DN_API DN_Str8 DN_OS_PathTo(DN_Arena *arena, DN_Str8 path, DN_Str8 path_separato DN_API DN_Str8 DN_OS_PathToF(DN_Arena *arena, DN_Str8 path_separator, DN_FMT_ATTRIB char const *fmt, ...) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); va_list args; va_start(args, fmt); DN_Str8 path = DN_Str8FromFmtVArena(&scratch.arena, fmt, args); @@ -8392,7 +8934,7 @@ DN_API DN_Str8 DN_OS_Path(DN_Arena *arena, DN_Str8 path) DN_API DN_Str8 DN_OS_PathF(DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); va_list args; va_start(args, fmt); DN_Str8 path = DN_Str8FromFmtVArena(&scratch.arena, fmt, args); @@ -8458,9 +9000,8 @@ DN_API DN_OSExecResult DN_OS_ExecOrAbort(DN_Str8Slice cmd_line, DN_OSExecArgs *a // NOTE: DN_OSThread static void DN_OS_ThreadExecute_(void *user_context) { - DN_OSThread *thread = DN_Cast(DN_OSThread *) user_context; - DN_MemFuncs mem_funcs = DN_MemFuncsDefault(); - DN_TCInitFromMemFuncs(&thread->context, thread->thread_id, /*args=*/nullptr, mem_funcs); + DN_OSThread *thread = DN_Cast(DN_OSThread *) user_context; + DN_TCInitFromMemFuncs(&thread->context, thread->thread_id, DN_TCInitArgsDefault(), DN_MemFuncsDefault()); DN_TCEquip(&thread->context); if (thread->is_lane_set) { DN_OS_TCThreadLaneEquip(thread->lane); @@ -8577,7 +9118,7 @@ DN_API DN_OSHttpResponse DN_OS_HttpRequest(DN_Arena *arena, DN_Str8 host, DN_Str { // TODO(doyle): Revise the memory allocation and its lifetime DN_OSHttpResponse result = {}; - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); result.scratch_arena = scratch.arena; DN_OS_HttpRequestAsync(&result, arena, host, path, secure, method, body, headers); @@ -8860,7 +9401,7 @@ T *DN_OS_VArrayInsertArray(DN_VArray *array, DN_USize index, T const *items, if (!DN_OS_VArrayIsValid(array)) return result; if (DN_OS_VArrayReserve(array, array->size + count)) - result = DN_CArrayInsertArray(array->data, &array->size, array->max, index, items, count); + result = DN_ArrayInsertArray(array->data, &array->size, array->max, index, items, count); return result; } @@ -8881,14 +9422,14 @@ T *DN_OS_VArrayInsert(DN_VArray *array, DN_USize index, T const &item) template T *DN_OS_VArrayPopFront(DN_VArray *array, DN_USize count) { - T *result = DN_Cast(T *)DN_CArrayPopFront(array->data, &array->size, sizeof(T), count); + T *result = DN_Cast(T *)DN_ArrayPopFront(array->data, &array->size, sizeof(T), count); return result; } template T *DN_OS_VArrayPopBack(DN_VArray *array, DN_USize count) { - T *result = DN_Cast(T *)DN_CArrayPopBack(array->data, &array->size, sizeof(T), count); + T *result = DN_Cast(T *)DN_ArrayPopBack(array->data, &array->size, sizeof(T), count); return result; } @@ -8898,7 +9439,7 @@ DN_ArrayEraseResult DN_OS_VArrayEraseRange(DN_VArray *array, DN_USize begin_i DN_ArrayEraseResult result = {}; if (!DN_OS_VArrayIsValid(array)) return result; - result = DN_CArrayEraseRange(array->data, &array->size, sizeof(T), begin_index, count, erase); + result = DN_ArrayEraseRange(array->data, &array->size, sizeof(T), begin_index, count, erase); return result; } @@ -8927,12 +9468,11 @@ bool DN_OS_VArrayReserve(DN_VArray *array, DN_USize count) return result; } -// NOTE: Stack Trace -DN_API DN_StackTraceWalkResult DN_StackTraceWalk(DN_Arena *arena, DN_U16 limit) +DN_API DN_StackTrace DN_StackTraceFromAllocator(DN_Allocator allocator, DN_U16 limit) { - DN_StackTraceWalkResult result = {}; + DN_StackTrace result = {}; #if defined(DN_OS_WIN32) - if (!arena) + if (!allocator.context) return result; static DN_TicketMutex mutex = {}; @@ -8946,7 +9486,7 @@ DN_API DN_StackTraceWalkResult DN_StackTraceWalk(DN_Arena *arena, DN_U16 limit) w32->sym_initialised = true; SymSetOptions(SYMOPT_LOAD_LINES); if (!SymInitialize(result.process, nullptr /*UserSearchPath*/, true /*fInvadeProcess*/)) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginAllocator(&allocator, 1); DN_OSW32Error error = DN_OS_W32LastError(&scratch.arena); DN_LogErrorF("SymInitialize failed, stack trace can not be generated (%lu): %.*s\n", error.code, DN_Str8PrintFmt(error.msg)); DN_TCScratchEnd(&scratch); @@ -8984,7 +9524,9 @@ DN_API DN_StackTraceWalkResult DN_StackTraceWalk(DN_Arena *arena, DN_U16 limit) } DN_TicketMutex_End(&mutex); - result.base_addr = DN_ArenaNewArray(arena, DN_U64, raw_frames_count, DN_ZMem_No); + result.base_addr = DN_Cast(DN_U64 *)DN_AllocatorAlloc(allocator, raw_frames_count * sizeof(DN_U64), alignof(DN_U64), DN_ZMem_No); + DN_Assert(result.base_addr); + result.size = DN_Cast(DN_U16) raw_frames_count; DN_Memcpy(result.base_addr, raw_frames, raw_frames_count * sizeof(raw_frames[0])); #else @@ -8994,66 +9536,91 @@ DN_API DN_StackTraceWalkResult DN_StackTraceWalk(DN_Arena *arena, DN_U16 limit) return result; } -static void DN_StackTraceAddWalkToStr8Builder(DN_StackTraceWalkResult const *walk, DN_Str8Builder *builder, DN_USize skip) + +DN_API DN_StackTrace DN_StackTraceFromArena(DN_Arena *arena, DN_U16 limit) +{ + DN_Allocator allocator = DN_AllocatorFromArena(arena); + DN_StackTrace result = DN_StackTraceFromAllocator(allocator, limit); + return result; +} + +static void DN_StackTraceAddToStr8Builder_(DN_StackTrace const *trace, DN_Str8Builder *builder, DN_USize skip) { DN_StackTraceRawFrame raw_frame = {}; - raw_frame.process = walk->process; - for (DN_USize index = skip; index < walk->size; index++) { - raw_frame.base_addr = walk->base_addr[index]; + raw_frame.process = trace->process; + for (DN_USize index = skip; index < trace->size; index++) { + raw_frame.base_addr = trace->base_addr[index]; DN_StackTraceFrame frame = DN_StackTraceRawFrameToFrame(builder->arena, raw_frame); - DN_Str8BuilderAppendF(builder, "%.*s(%zu): %.*s%s", DN_Str8PrintFmt(frame.file_name), frame.line_number, DN_Str8PrintFmt(frame.function_name), (DN_Cast(int) index == walk->size - 1) ? "" : "\n"); + DN_Str8BuilderAppendF(builder, "%.*s(%zu): %.*s%s", DN_Str8PrintFmt(frame.file_name), frame.line_number, DN_Str8PrintFmt(frame.function_name), (DN_Cast(int) index == trace->size - 1) ? "" : "\n"); } } -DN_API bool DN_StackTraceWalkResultIterate(DN_StackTraceWalkResultIterator *it, DN_StackTraceWalkResult const *walk) +DN_API bool DN_StackTraceIterate(DN_StackTraceIterator *it, DN_StackTrace const *trace) { bool result = false; - if (!it || !walk || !walk->base_addr || !walk->process) + if (!it || !trace || !trace->base_addr || !trace->process) return result; - if (it->index >= walk->size) + if (it->index >= trace->size) return false; result = true; - it->raw_frame.process = walk->process; - it->raw_frame.base_addr = walk->base_addr[it->index++]; + it->raw_frame.process = trace->process; + it->raw_frame.base_addr = trace->base_addr[it->index++]; return result; } -DN_API DN_Str8 DN_StackTraceWalkResultToStr8(DN_Arena *arena, DN_StackTraceWalkResult const *walk, DN_U16 skip) +DN_API DN_Str8 DN_Str8FromStackTraceAllocator(DN_Allocator allocator, DN_StackTrace const *trace, DN_U16 skip) { - DN_Str8 result{}; - if (!walk || !arena) + DN_Str8 result = {}; + if (!trace) return result; - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginAllocator(&allocator, 1); DN_Str8Builder builder = DN_Str8BuilderFromArena(&scratch.arena); - DN_StackTraceAddWalkToStr8Builder(walk, &builder, skip); - result = DN_Str8BuilderBuild(&builder, arena); + DN_StackTraceAddToStr8Builder_(trace, &builder, skip); + result = DN_Str8FromStr8BuilderAllocator(&builder, allocator); DN_TCScratchEnd(&scratch); return result; } -DN_API DN_Str8 DN_StackTraceWalkStr8(DN_Arena *arena, DN_U16 limit, DN_U16 skip) +DN_API DN_Str8 DN_Str8FromStackTraceArena(DN_Arena *arena, DN_StackTrace const *trace, DN_U16 skip) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); - DN_StackTraceWalkResult walk = DN_StackTraceWalk(&scratch.arena, limit); - DN_Str8 result = DN_StackTraceWalkResultToStr8(arena, &walk, skip); + DN_Str8 result = {}; + if (!trace || !arena) + return result; + + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); + DN_Str8Builder builder = DN_Str8BuilderFromArena(&scratch.arena); + DN_StackTraceAddToStr8Builder_(trace, &builder, skip); + result = DN_Str8FromStr8BuilderArena(&builder, arena); DN_TCScratchEnd(&scratch); return result; } -DN_API DN_Str8 DN_StackTraceWalkStr8FromHeap(DN_U16 limit, DN_U16 skip) +DN_API DN_Str8 DN_Str8FromStackTraceNowAllocator(DN_Allocator allocator, DN_U16 limit, DN_U16 skip) { - // NOTE: We don't use WalkResultToStr8 because that uses the TLS arenas which - // does not use the OS heap. - DN_MemList mem = DN_MemListFromHeap(DN_Kilobytes(64), DN_MemFlags_NoAllocTrack); - DN_Arena arena = DN_ArenaFromMemList(&mem); - DN_Str8Builder builder = DN_Str8BuilderFromArena(&arena); - DN_StackTraceWalkResult walk = DN_StackTraceWalk(&arena, limit); - DN_StackTraceAddWalkToStr8Builder(&walk, &builder, skip); + DN_TCScratch scratch = DN_TCScratchBeginArena(DN_Cast(DN_Arena **) & allocator.context, 1); + DN_StackTrace walk = DN_StackTraceFromArena(&scratch.arena, limit); + DN_Str8 result = DN_Str8FromStackTraceAllocator(allocator, &walk, skip); + DN_TCScratchEnd(&scratch); + return result; +} + +DN_API DN_Str8 DN_Str8FromStackTraceNowArena(DN_Arena *arena, DN_U16 limit, DN_U16 skip) +{ + DN_Str8 result = DN_Str8FromStackTraceNowAllocator(DN_AllocatorFromArena(arena), limit, skip); + return result; +} + +DN_API DN_Str8 DN_Str8FromStackTraceNowHeap(DN_U16 limit, DN_U16 skip) +{ + DN_Arena arena = DN_ArenaFromHeap(DN_Kilobytes(64), DN_MemFlags_NoAllocTrack); + DN_Str8Builder builder = DN_Str8BuilderFromArena(&arena); + DN_StackTrace walk = DN_StackTraceFromArena(&arena, limit); + DN_StackTraceAddToStr8Builder_(&walk, &builder, skip); DN_Str8 result = DN_Str8BuilderBuildFromHeap(&builder); - DN_MemListDeinit(&mem); + DN_ArenaDeinit(&arena); return result; } @@ -9063,12 +9630,12 @@ DN_API DN_StackTraceFrameSlice DN_StackTraceGetFrames(DN_Arena *arena, DN_U16 li if (!arena) return result; - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); - DN_StackTraceWalkResult walk = DN_StackTraceWalk(&scratch.arena, limit); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); + DN_StackTrace walk = DN_StackTraceFromArena(&scratch.arena, limit); if (walk.size) { if (DN_ISliceAllocArena(&result, walk.size, DN_ZMem_No, arena)) { DN_USize slice_index = 0; - for (DN_StackTraceWalkResultIterator it = {}; DN_StackTraceWalkResultIterate(&it, &walk);) + for (DN_StackTraceIterator it = {}; DN_StackTraceIterate(&it, &walk);) result.data[slice_index++] = DN_StackTraceRawFrameToFrame(arena, it.raw_frame); } } @@ -9125,7 +9692,7 @@ DN_API DN_StackTraceFrame DN_StackTraceRawFrameToFrame(DN_Arena *arena, DN_Stack DN_API void DN_StackTracePrint(DN_U16 limit) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_StackTraceFrameSlice stack_trace = DN_StackTraceGetFrames(&scratch.arena, limit); for (DN_ForItSize(it, DN_StackTraceFrame, stack_trace.data, stack_trace.count)) { DN_StackTraceFrame frame = *it.data; @@ -10952,7 +11519,7 @@ DN_API void DN_OS_GenBytesSecure(void *buffer, DN_U32 size) DN_API DN_OSDiskSpace DN_OS_DiskSpace(DN_Str8 path) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_OSDiskSpace result = {}; DN_Str16 path16 = DN_OS_W32Str8ToStr16(&scratch.arena, path); @@ -10976,7 +11543,7 @@ DN_API DN_OSDiskSpace DN_OS_DiskSpace(DN_Str8 path) DN_API bool DN_OS_SetEnvVar(DN_Str8 name, DN_Str8 value) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str16 name16 = DN_OS_W32Str8ToStr16(&scratch.arena, name); DN_Str16 value16 = DN_OS_W32Str8ToStr16(&scratch.arena, value); bool result = SetEnvironmentVariableW(name16.data, value16.data) != 0; @@ -10989,7 +11556,7 @@ DN_API DN_Str8 DN_OS_EXEPath(DN_Arena *arena) DN_Str8 result = {}; if (!arena) return result; - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_Str16 exe_dir16 = DN_OS_W32EXEPathW(&scratch.arena); result = DN_OS_W32Str16ToStr8(arena, exe_dir16); DN_TCScratchEnd(&scratch); @@ -11029,7 +11596,7 @@ static DN_U64 DN_OS_W32FileTimeToSeconds_(FILETIME const *time) DN_API bool DN_OS_FileCopy(DN_Str8 src, DN_Str8 dest, bool overwrite, DN_ErrSink *err) { bool result = false; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str16 src16 = DN_OS_W32Str8ToStr16(&scratch.arena, src); DN_Str16 dest16 = DN_OS_W32Str8ToStr16(&scratch.arena, dest); @@ -11053,7 +11620,7 @@ DN_API bool DN_OS_FileCopy(DN_Str8 src, DN_Str8 dest, bool overwrite, DN_ErrSink DN_API bool DN_OS_FileMove(DN_Str8 src, DN_Str8 dest, bool overwrite, DN_ErrSink *err) { bool result = false; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str16 src16 = DN_OS_W32Str8ToStr16(&scratch.arena, src); DN_Str16 dest16 = DN_OS_W32Str8ToStr16(&scratch.arena, dest); @@ -11109,7 +11676,7 @@ DN_API DN_OSFile DN_OS_FileOpen(DN_Str8 path, DN_OSFileOpen open_mode, DN_OSFile access_mode |= GENERIC_EXECUTE; } - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str16 path16 = DN_OS_W32Str8ToStr16(&scratch.arena, path); void *handle = CreateFileW(/*LPCWSTR lpFileName*/ path16.data, /*DWORD dwDesiredAccess*/ access_mode, @@ -11138,9 +11705,9 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size if (!file || !file->handle || file->error || !buffer || size <= 0) return result; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); if (!DN_Check(size <= (unsigned long)-1)) { - DN_Str8x32 buffer_size_str8 = DN_ByteCountStr8x32(size); + DN_Str8x32 buffer_size_str8 = DN_Str8x32FromByteCountU64Auto(size); DN_ErrSinkAppendF( err, 1 /*error_code*/, @@ -11198,9 +11765,9 @@ DN_API bool DN_OS_FileWritePtr(DN_OSFile *file, void const *buffer, DN_USize siz } if (!result) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena); - DN_Str8x32 buffer_size_str8 = DN_ByteCountStr8x32(size); + DN_Str8x32 buffer_size_str8 = DN_Str8x32FromByteCountU64Auto(size); DN_ErrSinkAppendF(err, win_error.code, "Failed to write buffer (%.*s) to file handle: %.*s", DN_Str8PrintFmt(buffer_size_str8), DN_Str8PrintFmt(win_error.msg)); DN_TCScratchEnd(&scratch); } @@ -11214,7 +11781,7 @@ DN_API bool DN_OS_FileFlush(DN_OSFile *file, DN_ErrSink *err) BOOL result = FlushFileBuffers(DN_Cast(HANDLE) file->handle); if (!result) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena); DN_ErrSinkAppendF(err, win_error.code, "Failed to flush file buffer to disk: %.*s", DN_Str8PrintFmt(win_error.msg)); DN_TCScratchEnd(&scratch); @@ -11237,7 +11804,7 @@ DN_API DN_OSPathInfo DN_OS_PathInfo(DN_Str8 path) if (path.size == 0) return result; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str16 path16 = DN_OS_W32Str8ToStr16(&scratch.arena, path); WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; @@ -11273,7 +11840,7 @@ DN_API bool DN_OS_PathDelete(DN_Str8 path) if (path.size == 0) return result; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str16 path16 = DN_OS_W32Str8ToStr16(&scratch.arena, path); if (path16.size) { result = DeleteFileW(path16.data); @@ -11290,7 +11857,7 @@ DN_API bool DN_OS_PathIsFile(DN_Str8 path) if (path.size == 0) return result; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str16 path16 = DN_OS_W32Str8ToStr16(&scratch.arena, path); if (path16.size) { WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; @@ -11308,7 +11875,7 @@ DN_API bool DN_OS_PathIsDir(DN_Str8 path) if (path.size == 0) return result; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str16 path16 = DN_OS_W32Str8ToStr16(&scratch.arena, path); if (path16.size) { WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; @@ -11324,7 +11891,7 @@ DN_API bool DN_OS_PathIsDir(DN_Str8 path) DN_API bool DN_OS_PathMakeDir(DN_Str8 path) { bool result = true; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str16 path16 = DN_OS_W32Str8ToStr16(&scratch.arena, path); // NOTE: Go back from the end of the string to all the directories in the @@ -11379,7 +11946,7 @@ DN_API bool DN_OS_PathIterateDir(DN_Str8 path, DN_OSDirIterator *it) if (path.size == 0 || !it || path.size <= 0) return false; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_OSW32FolderIteratorW wide_it = {}; DN_Str16 path16 = {}; if (it->handle) { @@ -11459,7 +12026,7 @@ DN_API DN_OSExecResult DN_OS_ExecPump(DN_OSExecAsyncHandle handle, return result; } - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DWORD stdout_bytes_available = 0; DWORD stderr_bytes_available = 0; PeekNamedPipe(handle.stdout_read, nullptr, 0, nullptr, &stdout_bytes_available, nullptr); @@ -11565,7 +12132,7 @@ DN_API DN_OSExecResult DN_OS_ExecWait(DN_OSExecAsyncHandle handle, DN_Arena *are return result; } - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_Str8Builder stdout_builder = {}; DN_Str8Builder stderr_builder = {}; if (arena) { @@ -11588,8 +12155,8 @@ DN_API DN_OSExecResult DN_OS_ExecWait(DN_OSExecAsyncHandle handle, DN_Arena *are } // NOTE: Get stdout/stderr. If no arena is passed this is a no-op - result.stdout_text = DN_Str8BuilderBuild(&stdout_builder, arena); - result.stderr_text = DN_Str8BuilderBuild(&stderr_builder, arena); + result.stdout_text = DN_Str8FromStr8BuilderArena(&stdout_builder, arena); + result.stderr_text = DN_Str8FromStr8BuilderArena(&stderr_builder, arena); DN_TCScratchEnd(&scratch); return result; } @@ -11601,7 +12168,7 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs if (cmd_line.count == 0) return result; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 cmd_rendered = DN_Str8SliceRender(cmd_line, DN_Str8Lit(" "), &scratch.arena); DN_Str16 cmd16 = DN_OS_W32Str8ToStr16(&scratch.arena, cmd_rendered); DN_Str16 working_dir16 = DN_OS_W32Str8ToStr16(&scratch.arena, args->working_dir); @@ -11611,7 +12178,7 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs if (env_builder.string_size) DN_Str8BuilderAppendRef(&env_builder, DN_Str8Lit("\0")); - DN_Str8 env_block8 = DN_Str8BuilderBuildDelimited(&env_builder, DN_Str8Lit("\0"), &scratch.arena); + DN_Str8 env_block8 = DN_Str8FromStr8BuilderDelimitArena(&env_builder, DN_Str8Lit("\0"), &scratch.arena); DN_Str16 env_block16 = {}; if (env_block8.size) env_block16 = DN_OS_W32Str8ToStr16(&scratch.arena, env_block8); @@ -12048,7 +12615,7 @@ DN_API void DN_OS_W32ThreadSetName(DN_Str8 name) // // See: https://learn.microsoft.com/en-us/windows/w32/api/processthreadsapi/nf-processthreadsapi-setthreaddescription DN_OSW32Core *w32 = DN_OS_W32GetCore(); - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); if (w32->set_thread_description) { DN_Str16 name16 = DN_OS_W32Str8ToStr16(&scratch.arena, name); w32->set_thread_description(GetCurrentThread(), (WCHAR *)name16.data); @@ -12163,7 +12730,7 @@ void DN_OS_HttpRequestWin32Callback(HINTERNET session, DWORD *dwContext, DWORD d if (request) { bool read_complete = dwInternetStatus == WINHTTP_CALLBACK_STATUS_READ_COMPLETE && dwStatusInformationLength == 0; if (read_complete) - response->body = DN_Str8BuilderBuild(&response->builder, response->arena); + response->body = DN_Str8FromStr8BuilderArena(&response->builder, response->arena); if (read_complete || dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR || error.code) { DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1); @@ -12192,7 +12759,7 @@ DN_API void DN_OS_HttpRequestAsync(DN_OSHttpResponse *response, response->arena = arena; response->builder = DN_Str8BuilderFromArena(response->scratch_arena.mem ? &response->scratch_arena : &response->tmp_arena); - DN_TCScratch scratch_ = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch_ = DN_TCScratchBeginArena(&arena, 1); if (!response->scratch_arena.mem) response->scratch_arena = scratch_.arena; @@ -12486,7 +13053,7 @@ DN_API DN_Str8 DN_OS_W32Str16ToStr8FromHeap(DN_Str16 src) // NOTE: Windows Executable Directory ////////////////////////////////////////// DN_API DN_Str16 DN_OS_W32EXEPathW(DN_Arena *arena) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_Str16 result = {}; DN_USize module_size = 0; wchar_t *module_path = nullptr; @@ -12515,7 +13082,7 @@ DN_API DN_Str16 DN_OS_W32EXEPathW(DN_Arena *arena) DN_API DN_Str16 DN_OS_W32EXEDirW(DN_Arena *arena) { // TODO(doyle): Implement a DN_Str16_BinarySearchReverse - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_Str16 result = {}; DN_USize module_size = 0; wchar_t *module_path = nullptr; @@ -12543,7 +13110,7 @@ DN_API DN_Str16 DN_OS_W32EXEDirW(DN_Arena *arena) DN_API DN_Str8 DN_OS_W32WorkingDir(DN_Arena *arena, DN_Str8 suffix) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_Str16 suffix16 = DN_OS_W32Str8ToStr16(&scratch.arena, suffix); DN_Str16 dir16 = DN_OS_W32WorkingDirW(&scratch.arena, suffix16); DN_Str8 result = DN_OS_W32Str16ToStr8(arena, dir16); @@ -12557,7 +13124,7 @@ DN_API DN_Str16 DN_OS_W32WorkingDirW(DN_Arena *arena, DN_Str16 suffix) DN_Str16 result = {}; // NOTE: required_size is the size required *including* the null-terminator - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); unsigned long required_size = GetCurrentDirectoryW(0, nullptr); unsigned long desired_size = required_size + DN_Cast(unsigned long) suffix.size; @@ -12635,7 +13202,7 @@ DN_API bool DN_OS_W32DirWIterate(DN_Str16 path, DN_OSW32FolderIteratorW *it) #endif #endif -DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_InitArgs *args) +DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_TCInitArgs args) { DN_Set(dn); dn->init_flags = flags; @@ -12721,8 +13288,7 @@ DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_InitArgs *args) if (DN_BitIsSet(flags, DN_InitFlags_ThreadContext)) { DN_Assert(dn->os_init); #if DN_H_WITH_OS - DN_TCInitArgs *tc_init_args = args ? &args->thread_context_init_args : nullptr; - DN_TCInitFromMemFuncs(&dn->main_tc, DN_OS_ThreadID(), tc_init_args, DN_MemFuncsDefault()); + DN_TCInitFromMemFuncs(&dn->main_tc, DN_OS_ThreadID(), args, DN_MemFuncsDefault()); DN_TCEquip(&dn->main_tc); #endif } @@ -12754,24 +13320,18 @@ DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_InitArgs *args) case DN_MemFuncsType_Heap: mem_funcs = DN_Str8Lit("Heap"); break; case DN_MemFuncsType_Virtual: mem_funcs = DN_Str8Lit("Virtual"); break; } - DN_Str8x32 main_commit = DN_ByteCountStr8x32(dn->main_tc.main_arena->mem->curr->commit); - DN_Str8x32 main_reserve = DN_ByteCountStr8x32(dn->main_tc.main_arena->mem->curr->reserve); - DN_Str8x32 temp_commit = DN_ByteCountStr8x32(dn->main_tc.temp_a_arena->mem->curr->commit); - DN_Str8x32 temp_reserve = DN_ByteCountStr8x32(dn->main_tc.temp_a_arena->mem->curr->reserve); - DN_Str8x32 err_commit = DN_ByteCountStr8x32(dn->main_tc.err_sink.arena->mem->curr->commit); - DN_Str8x32 err_reserve = DN_ByteCountStr8x32(dn->main_tc.err_sink.arena->mem->curr->reserve); - DN_FmtAppendTruncate(buf, - &buf_size, - sizeof(buf), - DN_Str8Lit("..."), - "M %.*s/%.*s S(x2) %.*s/%.*s E %.*s/%.*s (%.*s)\n", - DN_Str8PrintFmt(main_commit), - DN_Str8PrintFmt(main_reserve), - DN_Str8PrintFmt(temp_commit), - DN_Str8PrintFmt(temp_reserve), - DN_Str8PrintFmt(err_commit), - DN_Str8PrintFmt(err_reserve), - DN_Str8PrintFmt(mem_funcs)); + DN_Str8x32 main_commit = DN_Str8x32FromByteCountU64Auto(dn->main_tc.main_arena->mem->curr->commit); + DN_Str8x32 main_reserve = DN_Str8x32FromByteCountU64Auto(dn->main_tc.main_arena->mem->curr->reserve); + DN_Str8x32 err_commit = DN_Str8x32FromByteCountU64Auto(dn->main_tc.err_sink.arena->mem->curr->commit); + DN_Str8x32 err_reserve = DN_Str8x32FromByteCountU64Auto(dn->main_tc.err_sink.arena->mem->curr->reserve); + DN_FmtAppendTruncate(buf, &buf_size, sizeof(buf), DN_Str8Lit("..."), "M %.*s/%.*s", DN_Str8PrintFmt(main_commit), DN_Str8PrintFmt(main_reserve)); + if (dn->main_tc.temp_arenas_count) { + DN_Arena *temp = dn->main_tc.temp_arenas[0]; + DN_Str8x32 temp_commit = DN_Str8x32FromByteCountU64Auto(temp->mem->curr->commit); + DN_Str8x32 temp_reserve = DN_Str8x32FromByteCountU64Auto(temp->mem->curr->reserve); + DN_FmtAppendTruncate(buf, &buf_size, sizeof(buf), DN_Str8Lit("..."), " T(x%zu) %.*s/%.*s", dn->main_tc.temp_arenas_count, DN_Str8PrintFmt(temp_commit), DN_Str8PrintFmt(temp_reserve)); + } + DN_FmtAppendTruncate(buf, &buf_size, sizeof(buf), DN_Str8Lit("..."), " E %.*s/%.*s (%.*s)\n", DN_Str8PrintFmt(err_commit), DN_Str8PrintFmt(err_reserve), DN_Str8PrintFmt(mem_funcs)); } else { DN_FmtAppendTruncate(buf, &buf_size, sizeof(buf), DN_Str8Lit("..."), "N/A\n"); } @@ -12864,7 +13424,7 @@ DN_API DN_JSONBuilder DN_JSONBuilder_Init(DN_Arena *arena, int spaces_per_indent DN_API DN_Str8 DN_JSONBuilder_Build(DN_JSONBuilder const *builder, DN_Arena *arena) { - DN_Str8 result = DN_Str8BuilderBuild(&builder->string_builder, arena); + DN_Str8 result = DN_Str8FromStr8BuilderArena(&builder->string_builder, arena); return result; } @@ -12921,7 +13481,7 @@ DN_API void DN_JSONBuilder_KeyValue(DN_JSONBuilder *builder, DN_Str8 key, DN_Str DN_API void DN_JSONBuilder_KeyValueFV(DN_JSONBuilder *builder, DN_Str8 key, char const *value_fmt, va_list args) { - DN_TCScratch scratch = DN_TCScratchBegin(&builder->string_builder.arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&builder->string_builder.arena, 1); DN_Str8 value = DN_Str8FromFmtVArena(&scratch.arena, value_fmt, args); DN_JSONBuilder_KeyValue(builder, key, value); DN_TCScratchEnd(&scratch); @@ -13177,15 +13737,40 @@ DN_NETRequestHandle DN_NET_HandleFromRequest(DN_NETRequest *request) return result; } -void DN_NET_EndFinishedRequest_(DN_NETRequest *request) +bool DN_NET_ResponseHasFailed(DN_NETResponse const* resp) { - // NOTE: Deallocate the memory used in the request and reset the string builder - DN_ArenaTempEnd(&request->start_response_arena, DN_ArenaReset_Yes); - // NOTE: Check that the request is completely detached - DN_Assert(request->next == nullptr); + bool result = false; + if (resp->type == DN_NETRequestType_HTTP) + result = resp->state == DN_NETResponseState_Error || resp->http_status >= 400; + else + result = resp->state == DN_NETResponseState_Error; + return result; } -void DN_NET_BaseInit_(DN_NETCore *net, char *base, DN_U64 base_size) +DN_Str8 DN_NET_Str8DiagnosticFromResponse(DN_NETResponse const* resp, DN_Arena *arena) +{ + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); + DN_Str8Builder builder = DN_Str8BuilderFromArena(&scratch.arena); + bool resp_failed = DN_NET_ResponseHasFailed(resp); + DN_Str8BuilderAppendF(&builder, "Request %s (%s", resp_failed ? "failed" : "succeeded", resp->type == DN_NETRequestType_HTTP ? "HTTP" : "WS"); + if (resp->type == DN_NETRequestType_HTTP) { + if (resp->http_status) + DN_Str8BuilderAppendF(&builder, " %u", resp->http_status); + } + DN_Str8BuilderAppendF(&builder, ")"); + if (resp->body.size || resp->error_str8.size) { + DN_Str8BuilderAppendRef(&builder, DN_Str8Lit(" with ")); + if (resp->body.size) + DN_Str8BuilderAppendF(&builder, "%.*s", DN_Str8PrintFmt(resp->body)); + if (resp->error_str8.size) + DN_Str8BuilderAppendF(&builder, "%s%.*s", resp->body.size ? ". " : "", DN_Str8PrintFmt(resp->error_str8)); + } + DN_Str8 result = DN_Str8FromStr8BuilderArena(&builder, arena); + DN_TCScratchEnd(&scratch); + return result; +} + +void DN_NET_BaseInit(DN_NETCore *net, char *base, DN_U64 base_size) { net->base = base; net->base_size = base_size; @@ -13194,7 +13779,7 @@ void DN_NET_BaseInit_(DN_NETCore *net, char *base, DN_U64 base_size) net->completion_sem = DN_OS_SemaphoreInit(0); } -DN_NETRequestHandle DN_NET_SetupRequest_(DN_NETRequest *request, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type) +DN_NETRequestHandle DN_NET_SetupRequest(DN_NETRequest *request, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type) { // NOTE: Setup request DN_Assert(request); @@ -13229,8 +13814,17 @@ DN_NETRequestHandle DN_NET_SetupRequest_(DN_NETRequest *request, DN_Str8 url, DN DN_NETRequestHandle result = DN_NET_HandleFromRequest(request); request->response.request = result; + request->response.type = request->type; return result; } + +void DN_NET_EndFinishedRequest(DN_NETRequest *request) +{ + // NOTE: Deallocate the memory used in the request and reset the string builder + DN_ArenaTempEnd(&request->start_response_arena, DN_ArenaReset_Yes); + // NOTE: Check that the request is completely detached + DN_Assert(request->next == nullptr); +} #endif #if DN_CPP_WITH_TESTS @@ -13602,6 +14196,66 @@ static DN_UTCore DN_TST_Base() DN_UT_Assert(&result, mutex.ticket == ticket_b + 1); } + for (DN_UT_Test(&result, "QSort String (Natural)")) { + DN_Str8 list[] = { + DN_Str8Lit("item10"), + DN_Str8Lit("item2"), + DN_Str8Lit("item1"), + DN_Str8Lit("item20"), + DN_Str8Lit("item12"), + DN_Str8Lit("Afile"), + DN_Str8Lit("file2"), + DN_Str8Lit("file10"), + DN_Str8Lit("file1"), + DN_Str8Lit("z_last"), + DN_Str8Lit("m_middle"), + DN_Str8Lit("a_first"), + DN_Str8Lit("version-1.2.10"), + DN_Str8Lit("version-1.2.2"), + DN_Str8Lit("version-1.10.0"), + }; + + DN_QSortStr8NaturalAsc(list, DN_ArrayCountU(list), DN_Str8EqCase_Sensitive); + + DN_USize list_index = 0; + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("Afile")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("a_first")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("file1")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("file2")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("file10")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("item1")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("item2")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("item10")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("item12")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("item20")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("m_middle")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("version-1.2.2")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("version-1.2.10")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("version-1.10.0")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("z_last")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + } + + for (DN_UT_Test(&result, "QSort String (Lexicographic)")) { + DN_Str8 list[] = { + DN_Str8Lit("z_last"), + DN_Str8Lit("m_middle"), + DN_Str8Lit("a_first"), + DN_Str8Lit("version-1.2.10"), + DN_Str8Lit("version-1.2.2"), + DN_Str8Lit("version-1.10.0"), + }; + + DN_QSortStr8LexicographicAsc(list, DN_ArrayCountU(list), DN_Str8EqCase_Insensitive); + + DN_USize list_index = 0; + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("a_first")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("m_middle")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("version-1.10.0")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("version-1.2.10")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("version-1.2.2")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("z_last")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + } + // NOTE: MSVC SAL complains that we are using Interlocked functionality on // variables it has detected as *not* being shared across threads. This is // fine, we're just running some basic results, so permit it. @@ -13766,7 +14420,7 @@ static DN_UTCore DN_TST_BaseArena() static DN_UTCore DN_TST_BaseBytesHex() { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_UTCore test = DN_UT_Init(); DN_UT_LogF(&test, "Bytes <-> Hex\n"); { @@ -14172,7 +14826,7 @@ static DN_UTCore DN_TST_BaseDSMap() DN_UTCore result = DN_UT_Init(); DN_UT_LogF(&result, "DN_DSMap\n"); { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); { DN_MemList mem = DN_MemListFromVMem(0, 0, DN_MemFlags_Nil); DN_Arena arena = DN_ArenaFromMemList(&mem); @@ -14371,7 +15025,7 @@ static DN_UTCore DN_TST_BaseIArray() struct CustomArray { int *data; - DN_USize size; + DN_USize count; DN_USize max; }; @@ -14382,24 +15036,24 @@ static DN_UTCore DN_TST_BaseIArray() for (DN_UT_Test(&result, "Make item")) { int *item = DN_IArrayMake(&array, DN_ZMem_Yes); - DN_UT_Assert(&result, item && array.size == 1); + DN_UT_Assert(&result, item && array.count == 1); } } return result; } -static DN_UTCore DN_TST_BaseCArray2() +static DN_UTCore DN_TST_BaseArray() { DN_UTCore result = DN_UT_Init(); - DN_UT_LogF(&result, "DN_CArray2\n"); + DN_UT_LogF(&result, "DN_Array\n"); { for (DN_UT_Test(&result, "Positive count, middle of array, stable erase")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 3, 2, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 3, 2, DN_ArrayErase_Stable); int expected[] = {0, 1, 2, 5, 6, 7, 8, 9}; DN_UT_Assert(&result, erase.items_erased == 2); - DN_UT_Assert(&result, erase.it_index == 3); + DN_UT_AssertF(&result, erase.it_index == 2, "erase.it_index=%zu", erase.it_index); DN_UT_Assert(&result, size == 8); DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0); } @@ -14407,10 +15061,10 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Negative count, middle of array, stable erase")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 5, -3, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, -3, DN_ArrayErase_Stable); int expected[] = {0, 1, 2, 6, 7, 8, 9}; DN_UT_Assert(&result, erase.items_erased == 3); - DN_UT_Assert(&result, erase.it_index == 3); + DN_UT_Assert(&result, erase.it_index == 2); DN_UT_Assert(&result, size == 7); DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0); } @@ -14418,10 +15072,10 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "count = -1, stable erase")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 5, -1, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, -1, DN_ArrayErase_Stable); int expected[] = {0, 1, 2, 3, 4, 6, 7, 8, 9}; DN_UT_Assert(&result, erase.items_erased == 1); - DN_UT_Assert(&result, erase.it_index == 5); + DN_UT_Assert(&result, erase.it_index == 4); DN_UT_Assert(&result, size == 9); DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0); } @@ -14429,10 +15083,10 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Positive count, unstable erase")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 3, 2, DN_ArrayErase_Unstable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 3, 2, DN_ArrayErase_Unstable); int expected[] = {0, 1, 2, 8, 9, 5, 6, 7}; DN_UT_Assert(&result, erase.items_erased == 2); - DN_UT_Assert(&result, erase.it_index == 3); + DN_UT_Assert(&result, erase.it_index == 2); DN_UT_Assert(&result, size == 8); DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0); } @@ -14440,10 +15094,10 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Negative count, unstable erase")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 5, -3, DN_ArrayErase_Unstable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, -3, DN_ArrayErase_Unstable); int expected[] = {0, 1, 2, 7, 8, 9, 6}; DN_UT_Assert(&result, erase.items_erased == 3); - DN_UT_Assert(&result, erase.it_index == 3); + DN_UT_Assert(&result, erase.it_index == 2); DN_UT_Assert(&result, size == 7); DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0); } @@ -14451,7 +15105,7 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Edge case - begin_index at start, negative count")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 0, -2, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 0, -2, DN_ArrayErase_Stable); int expected[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_UT_Assert(&result, erase.items_erased == 1); DN_UT_Assert(&result, erase.it_index == 0); @@ -14462,10 +15116,10 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Edge case - begin_index at end, positive count")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 9, 2, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 9, 2, DN_ArrayErase_Stable); int expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; DN_UT_Assert(&result, erase.items_erased == 1); - DN_UT_Assert(&result, erase.it_index == 9); + DN_UT_Assert(&result, erase.it_index == 8); DN_UT_Assert(&result, size == 9); DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0); } @@ -14473,7 +15127,7 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Invalid input - count = 0")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 5, 0, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, 0, DN_ArrayErase_Stable); int expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_UT_Assert(&result, erase.items_erased == 0); DN_UT_Assert(&result, erase.it_index == 0); @@ -14483,7 +15137,7 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Invalid input - null data")) { DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(nullptr, &size, sizeof(int), 5, 2, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(nullptr, &size, sizeof(int), 5, 2, DN_ArrayErase_Stable); DN_UT_Assert(&result, erase.items_erased == 0); DN_UT_Assert(&result, erase.it_index == 0); DN_UT_Assert(&result, size == 10); @@ -14491,7 +15145,7 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Invalid input - null size")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, NULL, sizeof(arr[0]), 5, 2, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, NULL, sizeof(arr[0]), 5, 2, DN_ArrayErase_Stable); DN_UT_Assert(&result, erase.items_erased == 0); DN_UT_Assert(&result, erase.it_index == 0); } @@ -14499,7 +15153,7 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Invalid input - empty array")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 0; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 5, 2, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, 2, DN_ArrayErase_Stable); DN_UT_Assert(&result, erase.items_erased == 0); DN_UT_Assert(&result, erase.it_index == 0); DN_UT_Assert(&result, size == 0); @@ -14508,10 +15162,10 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Out-of-bounds begin_index")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 15, 2, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 15, 2, DN_ArrayErase_Stable); int expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_UT_Assert(&result, erase.items_erased == 0); - DN_UT_Assert(&result, erase.it_index == 10); + DN_UT_Assert(&result, erase.it_index == 9); DN_UT_Assert(&result, size == 10); DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0); } @@ -15356,7 +16010,7 @@ DN_Str8 const DN_UT_HASH_STRING_[] = void DN_TST_KeccakDispatch_(DN_UTCore *test, int hash_type, DN_Str8 input) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 input_hex = DN_HexFromPtrBytesArena(input.data, input.size, &scratch.arena, DN_TrimLeadingZero_No); switch (hash_type) { @@ -15584,7 +16238,7 @@ static DN_UTCore DN_TST_OS() } for (DN_UT_Test(&result, "Query executable directory")) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 os_result = DN_OS_EXEDir(&scratch.arena); DN_UT_Assert(&result, os_result.size); DN_UT_AssertF(&result, DN_OS_PathIsDir(os_result), "result(%zu): %.*s", os_result.size, DN_Str8PrintFmt(os_result)); @@ -15635,7 +16289,7 @@ static DN_UTCore DN_TST_OS() DN_UT_Assert(&result, DN_OS_PathIsFile(SRC_FILE)); // NOTE: Read step - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 read_file = DN_OS_FileReadAllArena(&scratch.arena, SRC_FILE, nullptr); DN_UT_AssertF(&result, read_file.size, "Failed to load file"); DN_UT_AssertF(&result, read_file.size == 4, "File read wrong amount of bytes (%zu)", read_file.size); @@ -15890,7 +16544,7 @@ static DN_UTCore DN_TST_BaseStrings() } for (DN_UT_Test(&result, "Initialise with format string")) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 string = DN_Str8FromFmtArena(&scratch.arena, "%s", "AB"); DN_UT_AssertF(&result, string.size == 2, "size: %zu", string.size); DN_UT_AssertF(&result, string.data[0] == 'A', "string[0]: %c", string.data[0]); @@ -15900,7 +16554,7 @@ static DN_UTCore DN_TST_BaseStrings() } for (DN_UT_Test(&result, "Copy string")) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 string = DN_Str8Lit("AB"); DN_Str8 copy = DN_Str8FromStr8Arena(string, &scratch.arena); DN_UT_AssertF(&result, copy.size == 2, "size: %zu", copy.size); @@ -15916,7 +16570,7 @@ static DN_UTCore DN_TST_BaseStrings() } for (DN_UT_Test(&result, "Allocate string from arena")) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 string = DN_Str8AllocArena(2, DN_ZMem_No, &scratch.arena); DN_UT_AssertF(&result, string.size == 2, "size: %zu", string.size); DN_TCScratchEnd(&scratch); @@ -16254,7 +16908,7 @@ static DN_UTCore DN_TST_BaseStrings() // NOTE: DN_Str8TruncMiddle (arena wrapper) for (DN_UT_Test(&result, "TruncMiddle: Arena wrapper allocates and truncates correctly")) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 str = DN_Str8Lit("HelloBeautifulWorld"); DN_Str8 trunc = DN_Str8Lit("..."); DN_Str8TruncResult res = DN_Str8TruncMiddle(str, 5, trunc, &scratch.arena); @@ -16275,7 +16929,7 @@ static DN_UTCore DN_TST_Win() #if defined(DN_PLATFORM_WIN32) DN_UT_LogF(&result, "OS Win32\n"); { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 input8 = DN_Str8Lit("String"); DN_Str16 input16 = DN_Str16{(wchar_t *)(L"String"), sizeof(L"String") / sizeof(L"String"[0]) - 1}; @@ -16424,7 +17078,7 @@ DN_TSTResult DN_TST_RunSuite(DN_TSTPrint print) #endif DN_TST_BaseDSMap(), DN_TST_BaseIArray(), - DN_TST_BaseCArray2(), + DN_TST_BaseArray(), DN_TST_BaseVArray(), DN_TST_Keccak(), DN_TST_M4(), @@ -17817,7 +18471,7 @@ DN_API void DN_BinPackCBuffer(DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_API DN_Str8 DN_BinPackBuild(DN_BinPack const *pack, DN_Arena *arena) { - DN_Str8 result = DN_Str8BuilderBuild(&pack->writer, arena); + DN_Str8 result = DN_Str8FromStr8BuilderArena(&pack->writer, arena); return result; } #define DN_CSV_CPP diff --git a/Single-Header/dn_single_header.h b/Single-Header/dn_single_header.h index 4874573..2c87e84 100644 --- a/Single-Header/dn_single_header.h +++ b/Single-Header/dn_single_header.h @@ -1,4 +1,4 @@ -// Generated by the DN single header generator 2026-05-18 14:23:35 +// Generated by the DN single header generator 2026-06-01 21:40:06 #if !defined(DN_H) #define DN_H @@ -329,6 +329,80 @@ #define DN_TokenCombine2(x, y) x ## y #define DN_TokenCombine(x, y) DN_TokenCombine2(x, y) +// NOTE: Asserts +#define DN_HardAssertF(expr, fmt, ...) \ + do { \ + if (!(expr)) { \ + DN_Str8 stack_trace_ = DN_Str8FromStackTraceNowHeap(128 /*limit*/, 3 /*skip*/); \ + DN_LogErrorF("Hard assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \ + DN_Str8PrintFmt(stack_trace_), \ + ##__VA_ARGS__); \ + DN_DebugBreak; \ + } \ + } while (0) +#define DN_HardAssert(expr) DN_HardAssertF(expr, "") + +// NOTE: Our default assert requires stack traces which has a bit of a chicken-and-egg problem if +// we're trying to detect some code related to the DN startup sequence. If we try to assert before +// the OS layer is initialised stack-traces will try to use temporary memory which requires TLS to +// be setup which belongs to the OS. +// +// This causes recursion errors as they call into each other. We use RawAsserts for these kind of +// checks. +#if defined(DN_NO_ASSERT) + #define DN_RawAssert(...) + #define DN_Assert(...) + #define DN_AssertOnce(...) + #define DN_AssertF(...) + #define DN_AssertFOnce(...) +#else + #define DN_RawAssert(expr) do { if (!(expr)) DN_DebugBreak; } while (0) + + #define DN_AssertF(expr, fmt, ...) \ + do { \ + if (!(expr)) { \ + DN_Str8 stack_trace_ = DN_Str8FromStackTraceNowHeap(128 /*limit*/, 3 /*skip*/); \ + DN_LogErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \ + DN_Str8PrintFmt(stack_trace_), \ + ##__VA_ARGS__); \ + DN_DebugBreak; \ + } \ + } while (0) + + #define DN_AssertFOnce(expr, fmt, ...) \ + do { \ + static bool once = true; \ + if (!(expr) && once) { \ + once = false; \ + DN_Str8 stack_trace_ = DN_Str8FromStackTraceNowHeap(128 /*limit*/, 3 /*skip*/); \ + DN_LogErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \ + DN_Str8PrintFmt(stack_trace_), \ + ##__VA_ARGS__); \ + DN_DebugBreak; \ + } \ + } while (0) + + #define DN_Assert(expr) DN_AssertF((expr), "") + #define DN_AssertOnce(expr) DN_AssertFOnce((expr), "") +#endif + +#define DN_InvalidCodePathF(fmt, ...) DN_HardAssertF(0, fmt, ##__VA_ARGS__) +#define DN_InvalidCodePath DN_InvalidCodePathF("Invalid code path triggered") +#define DN_StaticAssert(expr) \ + DN_GCC_WARNING_PUSH \ + DN_GCC_WARNING_DISABLE(-Wunused-local-typedefs) \ + typedef char DN_TokenCombine(static_assert_dummy__, __LINE__)[(expr) ? 1 : -1]; \ + DN_GCC_WARNING_POP + +#define DN_Check(expr) DN_CheckF(expr, "") +#if defined(DN_NO_CHECK_BREAK) + #define DN_CheckF(expr, fmt, ...) \ + ((expr) ? true : (DN_LogWarningF(fmt, ##__VA_ARGS__), false)) +#else + #define DN_CheckF(expr, fmt, ...) \ + ((expr) ? true : (DN_LogErrorF(fmt, ##__VA_ARGS__), DN_StackTracePrint(128 /*limit*/), DN_DebugBreak, false)) +#endif + #if defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) #define DN_64_BIT #else @@ -345,13 +419,13 @@ #include // exit() #endif -#define DN_ForIndexU(index, size) DN_USize index = 0; index < size; index++ -#define DN_ForIndexI(index, size) DN_ISize index = 0; index < size; index++ -#define DN_ForItSize(it, T, array, size) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < (size); it.index++, it.data = (array) + it.index -#define DN_ForItSizeReverse(it, T, array, size) struct { DN_USize index; T *data; } it = {(size) - 1, &(array)[size - 1]}; it.index < (size); it.index--, it.data = (array) + it.index -#define DN_ForIt(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)->data[0]}; it.index < (array)->size; it.index++, it.data = ((array)->data) + it.index -#define DN_ForLinkedListIt(it, T, list) struct { DN_USize index; T *data; } it = {0, list}; it.data; it.index++, it.data = ((it).data->next) -#define DN_ForItCArray(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < DN_ArrayCountU(array); it.index++, it.data = (array) + it.index +#define DN_ForIndexU(index, count) DN_USize index = 0; index < count; index++ +#define DN_ForIndexI(index, count) DN_ISize index = 0; index < count; index++ +#define DN_ForItSize(it, T, array, count) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < (count); it.index++, it.data = (array) + it.index +#define DN_ForItSizeReverse(it, T, array, count) struct { DN_USize index; T *data; } it = {(count) - 1, &(array)[count - 1]}; it.index < (count); it.index--, it.data = (array) + it.index +#define DN_ForIt(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)->data[0]}; it.index < (array)->count; it.index++, it.data = ((array)->data) + it.index +#define DN_ForLinkedListIt(it, T, list) struct { DN_USize index; T *data; } it = {0, list}; it.data; it.index++, it.data = ((it).data->next) +#define DN_ForItCArray(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < DN_ArrayCountU(array); it.index++, it.data = (array) + it.index #define DN_AlignUpPowerOfTwo(value, pot) (((uintptr_t)(value) + ((uintptr_t)(pot) - 1)) & ~((uintptr_t)(pot) - 1)) #define DN_AlignDownPowerOfTwo(value, pot) ((uintptr_t)(value) & ~((uintptr_t)(pot) - 1)) @@ -832,14 +906,14 @@ struct DN_StackTraceRawFrame DN_U64 base_addr; }; -struct DN_StackTraceWalkResult +struct DN_StackTrace { void *process; // [Internal] Windows handle to the process DN_U64 *base_addr; // The addresses of the functions in the stack trace DN_U16 size; // The number of `base_addr`'s stored from the walk }; -struct DN_StackTraceWalkResultIterator +struct DN_StackTraceIterator { DN_StackTraceRawFrame raw_frame; DN_U16 index; @@ -999,15 +1073,16 @@ struct DN_MemList struct DN_MemListTemp { - DN_MemList* mem; - DN_U64 used_sum; + DN_MemList* mem; + DN_U64 used_sum; #if DN_ARENA_TEMP_MEM_UAF_GUARD - DN_StackTraceWalkResult trace; + DN_StackTrace trace; #endif }; enum DN_AllocatorType { + DN_AllocatorType_MemList, DN_AllocatorType_Arena, DN_AllocatorType_Pool, }; @@ -1030,8 +1105,16 @@ enum DN_ArenaReset DN_ArenaReset_Yes, }; +typedef DN_U32 DN_ArenaFlags; +enum DN_ArenaFlags_ +{ + DN_ArenaFlags_Nil = 0, + DN_ArenaFlags_OwnsMemList = 1 << 0, +}; + struct DN_Arena { + DN_ArenaFlags flags; DN_MemList* mem; #if DN_ARENA_TEMP_MEM_UAF_GUARD DN_U32 uaf_guard_id; @@ -1190,6 +1273,12 @@ struct DN_Str8SplitResult DN_USize count; }; +enum DN_Str8LineBreakMode +{ + DN_Str8LineBreakMode_AtWord, // Add delimiter to string at ' ' and '\n' boundaries + DN_Str8LineBreakMode_AtWidth, // Add delimiter to string at width intervals +}; + typedef DN_USize DN_Str8TableFlags; enum DN_Str8TableFlags_ { @@ -1235,22 +1324,22 @@ enum DN_AgeUnit_ DN_AgeUnit_All = DN_AgeUnit_Ms | DN_AgeUnit_HMS | DN_AgeUnit_Day | DN_AgeUnit_Week | DN_AgeUnit_Year, }; -enum DN_ByteCountType +enum DN_ByteType { - DN_ByteCountType_B, - DN_ByteCountType_KiB, - DN_ByteCountType_MiB, - DN_ByteCountType_GiB, - DN_ByteCountType_TiB, - DN_ByteCountType_Count, - DN_ByteCountType_Auto, + DN_ByteType_B, + DN_ByteType_KiB, + DN_ByteType_MiB, + DN_ByteType_GiB, + DN_ByteType_TiB, + DN_ByteType_Count, + DN_ByteType_Auto, }; -struct DN_ByteCountResult +struct DN_ByteCount { - DN_ByteCountType type; - DN_Str8 suffix; // "KiB", "MiB", "GiB" .. e.t.c - DN_F64 bytes; + DN_ByteType type; + DN_Str8 suffix; // "KiB", "MiB", "GiB" .. e.t.c + DN_F64 bytes; }; struct DN_Date @@ -1288,6 +1377,7 @@ struct DN_ProfilerAnchor struct DN_ProfilerZone { + struct DN_Profiler *profiler; DN_U16 anchor_index; DN_U64 begin_tsc; DN_U16 parent_zone; @@ -1315,6 +1405,7 @@ struct DN_Profiler DN_F64 frame_avg_tsc; }; +typedef bool (DN_QSortCompareFunc)(void const *a, void const *b, void *user_context); enum DN_ErrSinkMode { DN_ErrSinkMode_Nil, // Default behaviour to accumulate errors into the sink @@ -1367,6 +1458,7 @@ struct DN_TCInitArgs DN_U64 main_commit; DN_U64 temp_reserve; DN_U64 temp_commit; + DN_U64 temp_count; DN_U64 err_sink_reserve; DN_U64 err_sink_commit; }; @@ -1377,21 +1469,20 @@ struct DN_TCCore // (T)hread (C)ontext sitting in thread-local storage DN_U64 thread_id; DN_CallSite call_site; char lane_opaque[sizeof(DN_U64) * 4]; + void* user_context; DN_MemList main_arena_mem_; - DN_MemList temp_a_arena_mem_; - DN_MemList temp_b_arena_mem_; + DN_MemList temp_arena_mems_[4]; DN_MemList err_sink_arena_mem_; DN_Arena main_arena_; - DN_Arena temp_a_arena_; - DN_Arena temp_b_arena_; + DN_Arena temp_arenas_[4]; DN_Arena err_sink_arena_; DN_Arena* main_arena; DN_Pool main_pool; - DN_Arena* temp_a_arena; - DN_Arena* temp_b_arena; + DN_Arena* temp_arenas[4]; + DN_USize temp_arenas_count; DN_ErrSink err_sink; @@ -1593,6 +1684,103 @@ struct DN_RaycastV2 DN_F32 t_b; // Distance along `dir_b` that the intersection occurred, e.g. `origin_b + (dir_b * t_b)` }; +struct DN_Ring +{ + DN_U64 size; + char *base; + DN_U64 write_pos; + DN_U64 read_pos; +}; + +enum DN_ArrayErase +{ + DN_ArrayErase_Unstable, + DN_ArrayErase_Stable, +}; + +enum DN_ArrayAdd +{ + DN_ArrayAdd_Append, + DN_ArrayAdd_Prepend, +}; + +struct DN_ArrayEraseResult +{ + // The next index your for-index should be set to such that you can continue + // to iterate the remainder of the array, e.g: + // + // for (DN_USize index = 0; index < array.size; index++) { + // if (erase) + // index = DN_ArrayEraseRange(&array, index, -3, DN_ArrayErase_Unstable).it_index; + // } + DN_USize it_index; + DN_USize items_erased; // The number of items erased +}; + +struct DN_ArrayFindResult +{ + bool success; + DN_USize index; + void *value; +}; +typedef bool (DN_ArrayFindEqFunc)(void const *lhs, void const *find); + +enum DN_DSMapKeyType +{ + // Key | Key Hash | Map Index + DN_DSMapKeyType_Invalid, + DN_DSMapKeyType_U64, // U64 | Hash(U64) | Hash(U64) % map_size + DN_DSMapKeyType_U64NoHash, // U64 | U64 | U64 % map_size + DN_DSMapKeyType_Buffer, // Buffer | Hash(buffer) | Hash(buffer) % map_size + DN_DSMapKeyType_BufferAsU64NoHash, // Buffer | U64(buffer[0:4]) | U64(buffer[0:4]) % map_size +}; + +struct DN_DSMapKey +{ + DN_DSMapKeyType type; + DN_U32 hash; // Hash to lookup in the map. If it equals, we check that the original key payload matches + void const *buffer_data; + DN_U32 buffer_size; + DN_U64 u64; + bool no_copy_buffer; +}; + +template +struct DN_DSMapSlot +{ + DN_DSMapKey key; // Hash table lookup key + T value; // Hash table value +}; + +typedef DN_U32 DN_DSMapFlags; +enum DN_DSMapFlags_ +{ + DN_DSMapFlags_Nil = 0, + DN_DSMapFlags_DontFreeArenaOnResize = 1 << 0, +}; + +using DN_DSMapHashFunction = DN_U32(DN_DSMapKey key, DN_U32 seed); +template struct DN_DSMap +{ + DN_U32 *hash_to_slot; // Mapping from hash to a index in the slots array + DN_DSMapSlot *slots; // Values of the array stored contiguously, non-sorted order + DN_U32 size; // Total capacity of the map and is a power of two + DN_U32 occupied; // Number of slots used in the hash table + DN_Arena *arena; // Backing arena for the hash table + DN_Pool pool; // Allocator for keys that are variable-sized buffers + DN_U32 initial_size; // Initial map size, map cannot shrink on erase below this size + DN_DSMapHashFunction *hash_function; // Custom hashing function to use if field is set + DN_U32 hash_seed; // Seed for the hashing function, when 0, DN_DS_MAP_DEFAULT_HASH_SEED is used + DN_DSMapFlags flags; +}; + +template struct DN_DSMapResult +{ + bool found; + DN_DSMapSlot *slot; + T *value; +}; + #if !defined(DN_STB_SPRINTF_HEADER_ONLY) #define STB_SPRINTF_IMPLEMENTATION #define STB_SPRINTF_STATIC @@ -3670,6 +3858,10 @@ DN_API DN_F32 DN_EpsilonClampF32 DN_API DN_MemStats DN_MemStatsSum (DN_MemStats lhs, DN_MemStats rhs); DN_API DN_MemStats DN_MemStatsSumArray (DN_MemStats const *array, DN_USize size); +// NOTE: `MemList` is an implementation of a classical `Arena` (e.g. bump allocator, can dynamically +// grow, frees by bumping pointer back, sub-divides a block of memory). The term `Arena` is reserved +// as a thin-layer over the functionality here to provide some use-after-free protection. See +// `Arena` for more info. DN_API DN_MemList DN_MemListFromBuffer (void *buffer, DN_USize size, DN_MemFlags flags); DN_API DN_MemList DN_MemListFromMemFuncs (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_MemFuncs mem_funcs); DN_API void DN_MemListDeinit (DN_MemList *mem); @@ -3697,8 +3889,46 @@ DN_API void DN_MemListTempEnd #define DN_MemListNewCopy(arena, T, src) (T *)DN_MemListCopy(arena, (src), sizeof(T), alignof(T)) #define DN_MemListNewArrayCopy(arena, T, src, count) (T *)DN_MemListCopy(arena, (src), sizeof(T) * (count), alignof(T)) -DN_API void DN_ArenaUAFCheck (DN_Arena *arena); -#define DN_ArenaDeref(arena_view) (DN_ArenaUAFCheck(arena_view), (arena_view)->arena) +// NOTE: `Arena`'s in this codebase are thin-layers over `MemList` but additionally provide +// use-after-free (UAF) protection when using temporary memory regions (e.g. thread context scratch +// `TCScratch` or `Temp[Begin|End]` family of functions). +// +// These arenas associate themselves with the temporary memory region they begin in if it is +// constructed using the `Temp[Begin|End]` family of functions (TCScratch implicitly call these for +// you before handing you the arena). If you attempt to allocate from a different arena bound with a +// different temporary memory region than the active one an assertion is triggered. This protection +// is gated by the presence of the preprocessor definition `#define DN_ARENA_TEMP_MEM_UAF_GUARD 1`. +// +// Without the preprocessor definition UAF protection is compiled out (e.g. no-op). UAF protection +// is also not enabled if you use `ArenaFromMemList` which simply sets up a plain arena that +// forwards all calls into the `MemList` API. +// +// To get UAF protection, all allocations _must_ go through the `Arena` API, using the `MemList` +// field directly in the `Arena` will bypass these checks and lead to unusual behaviour. If you want +// to forgo any of this infrastructure store and use the `MemList` directly in your codebase. +// +// UAF Example +/* + DN_Arena arena = DN_ArenaFromHeap(DN_Megabytes(1), DN_MemFlags_Nil); + DN_Arena temp = DN_ArenaTempBeginFromArena(&arena); + { + // NOTE: You can also `TempBegin` with `&temp`, either is valid. They both have pointers to + // the same underlying memory block owned by `arena`. + DN_Arena nested_temp = DN_ArenaTempBeginFromArena(&arena); + + // NOTE: This allocation triggers the UAF guard and asserts! An allocation into `temp`'s memory + // region would be reset when we end `nested_temp`'s memory region since they are spawned from + // the same underlying memory block sitting in `arena`. + // + // But the intent here is that the caller is resetting `nested_temp`'s allocations and not + // `temp` hence the UAF protection triggers. + DN_U64 *u64 = DN_ArenaNewZ(&temp, DN_U64); + + DN_ArenaTempEnd(&nested_temp); + } + DN_ArenaTempEnd(&temp); + DN_ArenaDeinit(&arena); // Frees the memory + */ DN_API DN_Arena DN_ArenaFromMemList (DN_MemList *mem); DN_API DN_Arena DN_ArenaTempBeginFromMemList (DN_MemList *mem); DN_API DN_Arena DN_ArenaTempBeginFromArena (DN_Arena *arena); @@ -3706,6 +3936,7 @@ DN_API void DN_ArenaTempEnd DN_API void* DN_ArenaAlloc (DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZMem z_mem); DN_API void* DN_ArenaAllocContiguous (DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZMem z_arena); DN_API void* DN_ArenaCopy (DN_Arena *arena, void const *data, DN_U64 size, uint8_t align); +DN_API void DN_ArenaDeinit (DN_Arena *arena); #define DN_ArenaNew(arena, T, zmem) (T *)DN_ArenaAlloc(arena, sizeof(T), alignof(T), zmem) #define DN_ArenaNewZ(arena, T) (T *)DN_ArenaAlloc(arena, sizeof(T), alignof(T), DN_ZMem_Yes) @@ -3749,15 +3980,18 @@ DN_API void DN_ErrSinkAppendFV_ DN_API void DN_ErrSinkAppendF_ (DN_ErrSink *err, DN_U32 error_code, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...); #define DN_ErrSinkAppendF(error, error_code, fmt, ...) DN_ErrSinkAppendF_(error, error_code, DN_CALL_SITE, fmt, ##__VA_ARGS__) -DN_API void DN_TCInit (DN_TCCore *tc, DN_U64 thread_id, DN_Arena *main_arena, DN_Arena *temp_a_arena, DN_Arena *temp_b_arena, DN_Arena *err_sink_arena); -DN_API void DN_TCInitFromMemFuncs (DN_TCCore *tc, DN_U64 thread_id, DN_TCInitArgs *args, DN_MemFuncs mem_funcs); +DN_API DN_TCInitArgs DN_TCInitArgsDefault (); +DN_API void DN_TCInit (DN_TCCore *tc, DN_U64 thread_id, DN_Arena *main_arena, DN_Arena *temp_arenas, DN_USize temp_arenas_count, DN_Arena *err_sink_arena); +DN_API void DN_TCInitFromMemFuncs (DN_TCCore *tc, DN_U64 thread_id, DN_TCInitArgs args, DN_MemFuncs mem_funcs); DN_API void DN_TCDeinit (DN_TCCore *tc, DN_TCDeinitArenas deinit_arenas); DN_API void DN_TCEquip (DN_TCCore *tc); DN_API DN_TCCore* DN_TCGet (); DN_API DN_Arena* DN_TCMainArena (); DN_API DN_Pool* DN_TCMainPool (); -DN_API DN_Arena DN_TCTempArena (DN_Arena **conflicts, DN_USize count); -DN_API DN_TCScratch DN_TCScratchBegin (DN_Arena **conflicts, DN_USize count); +DN_API DN_Arena DN_TCTempArenaFromAllocator (DN_Allocator *conflicts, DN_USize count); +DN_API DN_Arena DN_TCTempArenaFromArena (DN_Arena **conflicts, DN_USize count); +DN_API DN_TCScratch DN_TCScratchBeginAllocator (DN_Allocator *conflicts, DN_USize count); +DN_API DN_TCScratch DN_TCScratchBeginArena (DN_Arena **conflicts, DN_USize count); DN_API void DN_TCScratchEnd (DN_TCScratch *scratch); DN_API void DN_TCSetFrameArena (DN_Arena *arena); DN_API DN_Arena* DN_TCFrameArena (); @@ -3794,6 +4028,11 @@ DN_API DN_U8x32 DN_U8x32FromHexUnsafe DN_API DN_U8x32FromResult DN_U8x32FromHex (DN_Str8 hex_32b); DN_API DN_U8x32FromResult DN_U8x32FromDecimalStr8 (DN_Str8 decimal); // Write decimal string (e.g. "12345") as big-endian 256-bit value +DN_API DN_Allocator DN_AllocatorFromMemList (DN_MemList *mem); +DN_API DN_Allocator DN_AllocatorFromArena (DN_Arena *arena); +DN_API DN_Allocator DN_AllocatorFromPool (DN_Pool *pool); +DN_API void* DN_AllocatorAlloc (DN_Allocator allocator, DN_USize size, DN_U8 align, DN_ZMem z_mem); + DN_API DN_USize DN_FmtVSize (DN_FMT_ATTRIB char const *fmt, va_list args); DN_API DN_USize DN_FmtSize (DN_FMT_ATTRIB char const *fmt, ...); DN_API DN_FmtAppendResult DN_FmtVAppend (char *buf, DN_USize *buf_size, DN_USize buf_max, char const *fmt, va_list args); @@ -3810,19 +4049,22 @@ DN_API DN_USize DN_CStr16Size #define DN_Str8FromPtr(data, size) DN_Literal(DN_Str8){(char *)(data), (DN_USize)(size)} #define DN_Str8FromStruct(ptr) DN_Str8FromPtr((ptr)->data, (ptr)->size) #define DN_Str8FromLitArray(c_array) DN_Str8FromPtr(c_array, DN_ArrayCountU(c_array)) +DN_API DN_Str8 DN_Str8AllocAllocator (DN_USize size, DN_ZMem z_mem, DN_Allocator allocator); DN_API DN_Str8 DN_Str8AllocArena (DN_USize size, DN_ZMem z_mem, DN_Arena *arena); DN_API DN_Str8 DN_Str8AllocPool (DN_USize size, DN_Pool *pool); DN_API DN_Str8 DN_Str8FromCStr8 (char const *src); DN_API DN_Str8 DN_Str8FromCStr8Arena (char const *src, DN_Arena *arena); DN_API DN_Str8 DN_Str8FromPtrArena (void const *data, DN_USize size, DN_Arena *arena); DN_API DN_Str8 DN_Str8FromPtrPool (void const *data, DN_USize size, DN_Pool *pool); +DN_API DN_Str8 DN_Str8FromStr8Allocator (DN_Str8 string, DN_Allocator allocator); DN_API DN_Str8 DN_Str8FromStr8Arena (DN_Str8 string, DN_Arena *arena); DN_API DN_Str8 DN_Str8FromStr8Pool (DN_Str8 string, DN_Pool *pool); +DN_API DN_Str8 DN_Str8FromFmtVAllocator (DN_Allocator allocator, DN_FMT_ATTRIB char const *fmt, va_list args); DN_API DN_Str8 DN_Str8FromFmtVArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8 DN_Str8FromFmtAllocator (DN_Allocator allocator, DN_FMT_ATTRIB char const *fmt, ...); DN_API DN_Str8 DN_Str8FromFmtArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...); DN_API DN_Str8 DN_Str8FromFmtVPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args); DN_API DN_Str8 DN_Str8FromFmtPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8 DN_Str8FromByteCountType (DN_ByteCountType type); DN_API DN_Str8x16 DN_Str8x16FromFmt (DN_FMT_ATTRIB char const *fmt, ...); DN_API DN_Str8x16 DN_Str8x16FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); DN_API DN_Str8x32 DN_Str8x32FromFmt (DN_FMT_ATTRIB char const *fmt, ...); @@ -3900,12 +4142,16 @@ DN_API DN_Str8 DN_Str8Upper DN_API DN_Str8 DN_Str8Replace (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena, DN_Str8EqCase eq_case); DN_API DN_Str8 DN_Str8ReplaceSensitive (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena); DN_API DN_Str8 DN_Str8ReplaceInsensitive (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena); -DN_API DN_Str8 DN_Str8PadNewLines (DN_Str8 string, DN_Str8 pad_string, DN_Arena *arena); -DN_API DN_Str8 DN_Str8LineBreakStr8 (DN_Str8 src, DN_USize desired_width, DN_Arena *arena); +DN_API DN_Str8 DN_Str8PadNewLinesAllocator (DN_Str8 string, DN_Str8 pad_string, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8PadNewLinesArena (DN_Str8 string, DN_Str8 pad_string, DN_Arena *arena); +DN_API DN_Str8 DN_Str8LineBreakAllocator (DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8LineBreakArena (DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Arena *arena); DN_API DN_Str8 DN_Str8Table (DN_Str8 const* rows, DN_USize num_rows, DN_USize num_cols, DN_Str8TableFlags flags, DN_Arena *arena); DN_API DN_Str8 DN_Str8SliceRender (DN_Str8Slice array, DN_Str8 separator, DN_Arena *arena); DN_API DN_Str8 DN_Str8RenderSpaceSep (DN_Str8Slice array, DN_Arena *arena); +DN_API int DN_Str8CompareNatural (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case); +DN_API int DN_Str8CompareLexicographic (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case); DN_API bool DN_Str16Eq (DN_Str16 lhs, DN_Str16 rhs); DN_API DN_Str16 DN_Str16SliceRender (DN_Str16Slice array, DN_Str16 separator, DN_Arena *arena); @@ -3939,9 +4185,10 @@ DN_API bool DN_Str8BuilderPrependCopy #define DN_Str8BuilderPrependFV(builder, fmt, args) DN_Str8BuilderAddFV(builder, DN_Str8BuilderAdd_Prepend, fmt, args) DN_API bool DN_Str8BuilderPrependF (DN_Str8Builder *builder, DN_FMT_ATTRIB char const *fmt, ...); DN_API bool DN_Str8BuilderErase (DN_Str8Builder *builder, DN_Str8 string); -DN_API DN_Str8 DN_Str8BuilderBuild (DN_Str8Builder const *builder, DN_Arena *arena); -DN_API DN_Str8 DN_Str8BuilderBuildDelimited (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Arena *arena); -DN_API DN_Str8Slice DN_Str8BuilderBuildSlice (DN_Str8Builder const *builder, DN_Arena *arena); +DN_API DN_Str8 DN_Str8FromStr8BuilderAllocator (DN_Str8Builder const *builder, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8FromStr8BuilderArena (DN_Str8Builder const *builder, DN_Arena *arena); +DN_API DN_Str8 DN_Str8FromStr8BuilderDelimitAllocator (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8FromStr8BuilderDelimitArena (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Arena *arena); DN_API int DN_UTF8Encode (DN_U8 utf8[4], DN_U32 codepoint); DN_API int DN_UTF16Encode (DN_U16 utf16[2], DN_U32 codepoint); @@ -3963,7 +4210,8 @@ DN_API DN_U8x32 DN_BytesFromHex64Ptr DN_API DN_HexU64Str8 DN_HexFromU64 (DN_U64 value, DN_HexFromU64Type type); DN_API DN_USize DN_HexFromPtrBytes (void const *bytes, DN_USize bytes_count, void *hex, DN_USize hex_count, DN_TrimLeadingZero trim_leading_z); DN_API DN_Str8 DN_HexFromPtrBytesArena (void const *bytes, DN_USize bytes_count, DN_Arena *arena, DN_TrimLeadingZero trim_leading_z); -DN_API DN_USize DN_HexFromStr8Bytes (void const *bytes, DN_USize bytes_count, void *hex, DN_USize hex_count, DN_TrimLeadingZero trim_leading_z); +DN_API DN_USize DN_HexFromStr8Bytes (DN_Str8 bytes, void *hex, DN_USize hex_count, DN_TrimLeadingZero trim_leading_z); +DN_API DN_Str8 DN_HexFromStr8BytesArena (DN_Str8 bytes, DN_Arena *arena, DN_TrimLeadingZero trim_leading_z); DN_API DN_Hex32 DN_Hex32FromPtr16b (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z); DN_API DN_Hex64 DN_Hex64FromPtr32b (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z); DN_API DN_Hex128 DN_Hex128FromPtr64b (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z); @@ -3977,29 +4225,87 @@ DN_API bool DN_DateIsValid DN_API DN_Date DN_DateFromUnixTimeMs (DN_USize unix_ts_ms); DN_API DN_U64 DN_UnixTimeMsFromDate (DN_Date date); -DN_API DN_ByteCountResult DN_ByteCountFromType (DN_U64 bytes, DN_ByteCountType type); -#define DN_ByteCount(bytes) DN_ByteCountFromType(bytes, DN_ByteCountType_Auto) -DN_API DN_Str8x32 DN_ByteCountStr8x32FromType (DN_U64 bytes, DN_ByteCountType type); -#define DN_ByteCountStr8x32(bytes) DN_ByteCountStr8x32FromType(bytes, DN_ByteCountType_Auto) +DN_API DN_Str8 DN_Str8FromByteType (DN_ByteType type); +DN_API DN_ByteCount DN_ByteCountFromU64 (DN_U64 byte_count, DN_ByteType type); +DN_API DN_Str8x32 DN_Str8x32FromByteCountU64 (DN_U64 byte_count, DN_ByteType type); +#define DN_Str8x32FromByteCountU64Auto(bytes) DN_Str8x32FromByteCountU64(bytes, DN_ByteType_Auto) +// NOTE: Profiler +// Overview +// Basic profiler that tracks the duration of marked-up regions which are denoted by an +// opening and closing `anchor`. The profiler works in "frame" life-cycles which can by cycled by +// calling `DN_ProfilerNewFrame`. The number of frames that the profiler will persist is chosen by +// `anchors_per_frame`. +// +// For example if you pass a buffer of 1024 `anchors` and `anchors_per_frame = 128` then the +// profiler will hold onto 8 (1024 anchors / 128 anchors per frame) frames of profiling +// information. The profiler will cycle through the 8 frames of anchors and upon reaching the end +// begin overwriting the oldest frames worth of anchors. +// +// Once a frame has ended the just completed buffer of anchors that was just written to can be +// read by the caller and visualised to find the timings of each region, relative to the duration +// of the frame until it is eventually overwritten by calling `DN_ProfilerNewFrame` once the +// profiler has cycled through all the other frames. +// +// The caller must upfront determine the number of anchors and frames the profiler should have and +// pass it in. The profiler does not allocate any memory. +// +// When profiling functions that are invoked from different call-stacks the exclusive elapsed +// duration can exceed its inclusive duration. This is by design as the profiler makes no +// effort to distinguish between the different call-tree that a zone may have been triggered by. +// The profiler simpler always updates the same static anchor assigned for that zone. +// +// API +// DN_ProfilerInit +// You can set `tsc_now` to NULL to use the default timer mechanic which relies on +// DN_CPUGetTSC() which essentially uses __rdtsc. `tsc_frequency` must however always be +// provided, for, DN_CPUGetTSC() you can use `DN_OS_EstimateTSCPerSecond()` to calculate the +// frequency of `__rdtsc`. +// DN_ProfilerNewFrame +// Always call `DN_ProfilerNewFrame` at-least once using the `Zone` family of functions as it +// sets up +// DN_ProfilerBeginZone +// The zeroth anchor is reserved for profiling the begin and end of a profiler frame. If you are +// manually specifying `anchor_index` (e.g. using an enum) ensure that the first anchor index +// starts from `1`. Note that the `BeginZoneAuto` macro uses `__COUNTER__ + 1` to skip the 0th +// zone. +// DN_ProfilerFrameAnchors +// Returns the current frame's (e.g. the anchors that the profiler is currently writing to) +// worth of anchors #define DN_ProfilerZoneLoop(prof, name, index) \ DN_ProfilerZone DN_UniqueName(zone_) = DN_ProfilerBeginZone(prof, DN_Str8Lit(name), index), DN_UniqueName(dummy_) = {}; \ DN_UniqueName(dummy_).begin_tsc == 0; \ - DN_ProfilerEndZone(prof, DN_UniqueName(zone_)), DN_UniqueName(dummy_).begin_tsc = 1 + DN_ProfilerEndZone(DN_UniqueName(zone_)), DN_UniqueName(dummy_).begin_tsc = 1 #define DN_ProfilerZoneLoopAuto(prof, name) DN_ProfilerZoneLoop(prof, name, __COUNTER__ + 1) DN_API DN_Profiler DN_ProfilerInit (DN_ProfilerAnchor *anchors, DN_USize count, DN_USize anchors_per_frame, DN_ProfilerTSCNowFunc *tsc_now, DN_U64 tsc_frequency); DN_API DN_ProfilerZone DN_ProfilerBeginZone (DN_Profiler *profiler, DN_Str8 name, DN_U16 anchor_index); #define DN_ProfilerBeginZoneAuto(prof, name) DN_ProfilerBeginZone(prof, DN_Str8Lit(name), __COUNTER__ + 1) -DN_API void DN_ProfilerEndZone (DN_Profiler *profiler, DN_ProfilerZone zone); +DN_API void DN_ProfilerEndZone (DN_ProfilerZone zone); DN_API DN_USize DN_ProfilerFrameCount (DN_Profiler const *profiler); DN_API DN_ProfilerAnchorArray DN_ProfilerFrameAnchorsFromIndex (DN_Profiler *profiler, DN_USize frame_index); DN_API DN_ProfilerAnchorArray DN_ProfilerFrameAnchors (DN_Profiler *profiler); DN_API void DN_ProfilerNewFrame (DN_Profiler *profiler); -DN_API void DN_ProfilerDump (DN_Profiler *profiler); +DN_API DN_USize DN_ProfilerFmtAnchor (DN_ProfilerAnchor anchor, DN_U64 tsc_frequency, char *buffer, DN_USize count); +DN_API DN_Str8 DN_ProfilerFmtAnchorStr8 (DN_ProfilerAnchor anchor, DN_U64 tsc_frequency, DN_Arena *arena); +DN_API void DN_ProfilerFmtToStdout (DN_Profiler *profiler); DN_API DN_F64 DN_ProfilerSecFromTSC (DN_Profiler *profiler, DN_U64 duration_tsc); DN_API DN_F64 DN_ProfilerMsFromTSC (DN_Profiler *profiler, DN_U64 duration_tsc); +DN_API void DN_QSort (void *array, DN_USize array_size, DN_USize elem_size, void *user_context, DN_QSortCompareFunc *compare); +DN_API bool DN_QSortCompareStr8NaturalAsc (void const* lhs, void const *rhs, void *user_context); +DN_API bool DN_QSortCompareStr8NaturalDesc (void const* lhs, void const *rhs, void *user_context); +DN_API bool DN_QSortCompareStr8LexicographicAsc (void const* lhs, void const *rhs, void *user_context); +DN_API bool DN_QSortCompareStr8LexicographicDesc (void const* lhs, void const *rhs, void *user_context); +DN_API bool DN_QSortCompareBytesLT (void const* lhs, void const *rhs, void *user_context); +DN_API bool DN_QSortCompareBytesGT (void const* lhs, void const *rhs, void *user_context); +DN_API void DN_QSortBytesLT (void *array, DN_USize array_size, DN_USize elem_size); +DN_API void DN_QSortBytesGT (void *array, DN_USize array_size, DN_USize elem_size); +DN_API void DN_QSortStr8NaturalAsc (DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case); +DN_API void DN_QSortStr8NaturalDesc (DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case); +DN_API void DN_QSortStr8LexicographicAsc (DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case); +DN_API void DN_QSortStr8LexicographicDesc (DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case); + DN_API DN_PCG32 DN_PCG32Init (DN_U64 seed); DN_API DN_U32 DN_PCG32Next (DN_PCG32 *rng); DN_API DN_U64 DN_PCG32Next64 (DN_PCG32 *rng); @@ -4040,6 +4346,10 @@ DN_API DN_Str8 DN_Str8FromStr8ANSIColourV3F32RGB255Arena DN_API DN_Str8 DN_Str8FromFmtANSIColourU8RGBArena (DN_ANSIColourMode mode, DN_U8 r, DN_U8 g, DN_U8 b, DN_Arena *arena, char const *fmt, ...); DN_API DN_Str8 DN_Str8FromFmtANSIColourV3F32RGB255Arena (DN_ANSIColourMode mode, DN_V3F32 rgb_255, DN_Arena *arena, char const *fmt, ...); +// NOTE: Create log printable lines with a date prefix, severity and message. The platform +// implementation should call `SetPrintFunc` to intercept the log messages and output to the desired +// destination (log file, standard out, e.t.c.). When the library is initialised `DN_Init` to with +// OS functionality enabled, the log callback is by default set to outputting via standard out. DN_API DN_LogPrefixSize DN_LogMakePrefix (DN_LogStyle style, DN_LogTypeParam type, DN_CallSite call_site, DN_LogDate date, char *dest, DN_USize dest_size); DN_API void DN_LogSetPrintFunc (DN_LogPrintFunc *print_func, void *user_data); DN_API void DN_LogPrint (DN_LogTypeParam type, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, ...); @@ -4060,21 +4370,27 @@ DN_API DN_LogTypeParam DN_LogTypeParamFromType // NOTE: OS primitives that the OS layer can provide for the base layer but is optional. #if defined(DN_FREESTANDING) -#define DN_StackTraceWalk(...) -#define DN_StackTraceWalkResultIterate(...) -#define DN_StackTraceWalkResultToStr8(...) DN_Str8Lit("N/A") -#define DN_StackTraceWalkStr8(...) DN_Str8Lit("N/A") -#define DN_StackTraceWalkStr8FromHeap(...) DN_Str8Lit("N/A") +#define DN_StackTraceFromArena(...) {} +#define DN_StackTraceFromAllocator(...) {} +#define DN_StackTraceIterate(...) false +#define DN_Str8FromStackTraceAllocator(...) DN_Str8Lit("N/A") +#define DN_Str8FromStackTraceArena(...) DN_Str8Lit("N/A") +#define DN_Str8FromStackTraceNowAllocator(...) DN_Str8Lit("N/A") +#define DN_Str8FromStackTraceNowArena(...) DN_Str8Lit("N/A") +#define DN_Str8FromStackTraceNowHeap(...) DN_Str8Lit("N/A") #define DN_StackTraceGetFrames(...) #define DN_StackTraceRawFrameToFrame(...) #define DN_StackTracePrint(...) #define DN_StackTraceReloadSymbols(...) #else -DN_API DN_StackTraceWalkResult DN_StackTraceWalk (DN_Arena *arena, DN_U16 limit); -DN_API bool DN_StackTraceWalkResultIterate (DN_StackTraceWalkResultIterator *it, DN_StackTraceWalkResult const *walk); -DN_API DN_Str8 DN_StackTraceWalkResultToStr8 (DN_Arena *arena, DN_StackTraceWalkResult const *walk, DN_U16 skip); -DN_API DN_Str8 DN_StackTraceWalkStr8 (DN_Arena *arena, DN_U16 limit, DN_U16 skip); -DN_API DN_Str8 DN_StackTraceWalkStr8FromHeap (DN_U16 limit, DN_U16 skip); +DN_API DN_StackTrace DN_StackTraceFromArena (DN_Arena *arena, DN_U16 limit); +DN_API DN_StackTrace DN_StackTraceFromAllocator (DN_Allocator allocator, DN_U16 limit); +DN_API bool DN_StackTraceIterate (DN_StackTraceIterator *it, DN_StackTrace const *walk); +DN_API DN_Str8 DN_Str8FromStackTraceAllocator (DN_Allocator allocator, DN_StackTrace const *walk, DN_U16 skip); +DN_API DN_Str8 DN_Str8FromStackTraceArena (DN_Arena *arena, DN_StackTrace const *walk, DN_U16 skip); +DN_API DN_Str8 DN_Str8FromStackTraceNowAllocator (DN_Arena *arena, DN_U16 limit, DN_U16 skip); +DN_API DN_Str8 DN_Str8FromStackTraceNowArena (DN_Arena *arena, DN_U16 limit, DN_U16 skip); +DN_API DN_Str8 DN_Str8FromStackTraceNowHeap (DN_U16 limit, DN_U16 skip); DN_API DN_StackTraceFrameSlice DN_StackTraceGetFrames (DN_Arena *arena, DN_U16 limit); DN_API DN_StackTraceFrame DN_StackTraceRawFrameToFrame (DN_Arena *arena, DN_StackTraceRawFrame raw_frame); DN_API void DN_StackTracePrint (DN_U16 limit); @@ -4371,135 +4687,13 @@ DN_API DN_Rect DN_RectCutCut #define DN_RectCutInit(rect, side) DN_Literal(DN_RectCut){rect, side} DN_API DN_RaycastV2 DN_RaycastLineIntersectV2 (DN_V2F32 origin_a, DN_V2F32 dir_a, DN_V2F32 origin_b, DN_V2F32 dir_b); -#endif // !defined(DN_BASE_H) -// DN: Single header generator commented out => #include "Base/dn_base_assert.h" -#if !defined(DN_BASE_ASSERT_H) -#define DN_BASE_ASSERT_H -#define DN_HardAssertF(expr, fmt, ...) \ - do { \ - if (!(expr)) { \ - DN_Str8 stack_trace_ = DN_StackTraceWalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \ - DN_LogErrorF("Hard assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \ - DN_Str8PrintFmt(stack_trace_), \ - ##__VA_ARGS__); \ - DN_DebugBreak; \ - } \ - } while (0) -#define DN_HardAssert(expr) DN_HardAssertF(expr, "") +// NOTE: Containers that are imlpemented using primarily macros for operating on data structures +// that are embedded into a C style struct or from a set of defined variables from the available +// scope. Keep it stupid simple, structs and functions. Minimal amount of container types with +// flexible construction leads to less duplicated container code and less template meta-programming. -// NOTE: Our default assert requires stack traces which has a bit of a chicken-and-egg problem if -// we're trying to detect some code related to the DN startup sequence. If we try to assert before -// the OS layer is initialised stack-traces will try to use temporary memory which requires TLS to -// be setup which belongs to the OS. -// -// This causes recursion errors as they call into each other. We use RawAsserts for these kind of -// checks. -#if defined(DN_NO_ASSERT) - #define DN_RawAssert(...) - #define DN_Assert(...) - #define DN_AssertOnce(...) - #define DN_AssertF(...) - #define DN_AssertFOnce(...) -#else - #define DN_RawAssert(expr) do { if (!(expr)) DN_DebugBreak; } while (0) - - #define DN_AssertF(expr, fmt, ...) \ - do { \ - if (!(expr)) { \ - DN_Str8 stack_trace_ = DN_StackTraceWalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \ - DN_LogErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \ - DN_Str8PrintFmt(stack_trace_), \ - ##__VA_ARGS__); \ - DN_DebugBreak; \ - } \ - } while (0) - - #define DN_AssertFOnce(expr, fmt, ...) \ - do { \ - static bool once = true; \ - if (!(expr) && once) { \ - once = false; \ - DN_Str8 stack_trace_ = DN_StackTraceWalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \ - DN_LogErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \ - DN_Str8PrintFmt(stack_trace_), \ - ##__VA_ARGS__); \ - DN_DebugBreak; \ - } \ - } while (0) - - #define DN_Assert(expr) DN_AssertF((expr), "") - #define DN_AssertOnce(expr) DN_AssertFOnce((expr), "") -#endif - -#define DN_InvalidCodePathF(fmt, ...) DN_HardAssertF(0, fmt, ##__VA_ARGS__) -#define DN_InvalidCodePath DN_InvalidCodePathF("Invalid code path triggered") -#define DN_StaticAssert(expr) \ - DN_GCC_WARNING_PUSH \ - DN_GCC_WARNING_DISABLE(-Wunused-local-typedefs) \ - typedef char DN_TokenCombine(static_assert_dummy__, __LINE__)[(expr) ? 1 : -1]; \ - DN_GCC_WARNING_POP - -#define DN_Check(expr) DN_CheckF(expr, "") -#if defined(DN_NO_CHECK_BREAK) - #define DN_CheckF(expr, fmt, ...) \ - ((expr) ? true : (DN_LogWarningF(fmt, ##__VA_ARGS__), false)) -#else - #define DN_CheckF(expr, fmt, ...) \ - ((expr) ? true : (DN_LogErrorF(fmt, ##__VA_ARGS__), DN_StackTracePrint(128 /*limit*/), DN_DebugBreak, false)) -#endif - -#endif -// DN: Single header generator commented out => #include "Base/dn_base_containers.h" -#if !defined(DN_CONTAINERS_H) -#define DN_CONTAINERS_H -// Containers that are imlpemented using primarily macros for operating on data structures that are -// embedded into a C style struct or from a set of defined variables from the available scope. Keep -// it stupid simple, structs and functions. Minimal amount of container types with flexible -// construction leads to less duplicated container code and less template meta-programming. -// -// Arrays -// -// Data structures that have a `T *data`, `DN_USize count` and `DN_USize max` capacity that can be -// dynamically shrunk or expanded. -// -// API -// ResizeFrom: Resizes the array to `new_max` erase elements if resizing to a smaller size -// GrowFrom: Expands the capacity of the array if `new_max > array.max` otherwise no-op -// GrowIfNeeded: Expands the capacity of the array if `array.size + add_count > array.max` otherwise no-op -// -// Variants -// PArray => Pointer (to) Array -// LArray => Literal Array -// Define a C array and size. (P) array macros take a pointer to the aray, its size and its max -// capacity. The (L) array macros take the literal array and derives the max capacity -// automatically using DN_ArrayCountU(l_array). -// -// MyStruct buffer[TB_ASType_Count] = {}; -// DN_USize size = 0; -// MyStruct *item_0 = DN_PArrayMake(buffer, &size, DN_ArrayCountU(buffer), DN_ZMem_No); -// MyStruct *item_1 = DN_LArrayMake(buffer, &size, DN_ZMem_No); -// -// IArray => Intrusive Array -// Define a struct with the members `data`, `size` and `max`: -// -// struct MyArray { -// MyStruct *data; -// DN_USize size; -// DN_USize max; -// } my_array = {}; -// DN_Arena arena = {}; -// DN_IArrayResizeFromArena(&my_array, &arena, 256); -// MyStruct *item = DN_IArrayMake(&my_array, DN_ZMem_No); -// -// Slices -// -// Fixed size container allocated up front that have a `T *data` and `DN_USize count` elements. -// -// API -// AllocArena: Allocates the container with the requested `count` elements -// -// ISinglyLL => Intrusive Singly Linked List +// NOTE: Intrusive Singly Linked List // Define a struct with the members `next`: // // struct MyLinkItem { @@ -4508,35 +4702,9 @@ DN_API DN_RaycastV2 DN_RaycastLineIntersectV2 // } my_link = {}; // // MyLinkItem *first_item = DN_ISinglyLLDetach(&my_link, MyLinkItem); -// -// DoublyLL => Doubly Linked List -// Define a struct with the members `next` and `prev`. This list has null pointers for head->prev -// and tail->next. -// -// struct MyLinkItem { -// int data; -// MyLinkItem *next; -// MyLinkItem *prev; -// } my_link = {}; -// -// MyLinkItem first_item = {}, second_item = {}; -// DN_DoublyLLAppend(&first_item, &second_item); // first_item -> second_item -// -// SentinelDoublyLL => Sentinel Doubly Linked List -// Uses a sentinel/dummy node as the list head. The sentinel points to itself when empty. -// Define a struct with the members `next` and `prev`: -// -// struct MyLinkItem { -// int data; -// MyLinkItem *next; -// MyLinkItem *prev; -// } my_list = {}; -// -// DN_SentinelDoublyLLInit(&my_list); -// DN_SentinelDoublyLLAppend(&my_list, &new_item); -// DN_SentinelDoublyLLForEach(it, &my_list) { /* ... */ } -// -// SinglyHeadTailLL => Singly Linked List with Head and Tail pointer (or First and Last pointer) +#define DN_ISinglyLLDetach(list) (decltype(list))DN_SinglyLLDetach((void **)&(list), (void **)&(list)->next) + +// NOTE: Singly Linked List with Head and Tail pointer /* struct MyLinkItem { int data; @@ -4555,108 +4723,6 @@ DN_API DN_RaycastV2 DN_RaycastLineIntersectV2 for (MyLinkItem *it = container.head; it; it = it->next) { } */ - -// DN: Single header generator commented out => #if defined(_CLANGD) -// #include "../dn.h" -// #endif - -struct DN_Ring -{ - DN_U64 size; - char *base; - DN_U64 write_pos; - DN_U64 read_pos; -}; - -enum DN_ArrayErase -{ - DN_ArrayErase_Unstable, - DN_ArrayErase_Stable, -}; - -enum DN_ArrayAdd -{ - DN_ArrayAdd_Append, - DN_ArrayAdd_Prepend, -}; - -struct DN_ArrayEraseResult -{ - // The next index your for-index should be set to such that you can continue - // to iterate the remainder of the array, e.g: - // - // for (DN_USize index = 0; index < array.size; index++) { - // if (erase) - // index = DN_FArray_EraseRange(&array, index, -3, DN_ArrayErase_Unstable); - // } - DN_USize it_index; - DN_USize items_erased; // The number of items erased -}; - -template struct DN_ArrayFindResult -{ - T *data; // Pointer to the value if a match is found, null pointer otherwise - DN_USize index; // Index to the value if a match is found, 0 otherwise -}; - -enum DN_DSMapKeyType -{ - // Key | Key Hash | Map Index - DN_DSMapKeyType_Invalid, - DN_DSMapKeyType_U64, // U64 | Hash(U64) | Hash(U64) % map_size - DN_DSMapKeyType_U64NoHash, // U64 | U64 | U64 % map_size - DN_DSMapKeyType_Buffer, // Buffer | Hash(buffer) | Hash(buffer) % map_size - DN_DSMapKeyType_BufferAsU64NoHash, // Buffer | U64(buffer[0:4]) | U64(buffer[0:4]) % map_size -}; - -struct DN_DSMapKey -{ - DN_DSMapKeyType type; - DN_U32 hash; // Hash to lookup in the map. If it equals, we check that the original key payload matches - void const *buffer_data; - DN_U32 buffer_size; - DN_U64 u64; - bool no_copy_buffer; -}; - -template -struct DN_DSMapSlot -{ - DN_DSMapKey key; // Hash table lookup key - T value; // Hash table value -}; - -typedef DN_U32 DN_DSMapFlags; -enum DN_DSMapFlags_ -{ - DN_DSMapFlags_Nil = 0, - DN_DSMapFlags_DontFreeArenaOnResize = 1 << 0, -}; - -using DN_DSMapHashFunction = DN_U32(DN_DSMapKey key, DN_U32 seed); -template struct DN_DSMap -{ - DN_U32 *hash_to_slot; // Mapping from hash to a index in the slots array - DN_DSMapSlot *slots; // Values of the array stored contiguously, non-sorted order - DN_U32 size; // Total capacity of the map and is a power of two - DN_U32 occupied; // Number of slots used in the hash table - DN_Arena *arena; // Backing arena for the hash table - DN_Pool pool; // Allocator for keys that are variable-sized buffers - DN_U32 initial_size; // Initial map size, map cannot shrink on erase below this size - DN_DSMapHashFunction *hash_function; // Custom hashing function to use if field is set - DN_U32 hash_seed; // Seed for the hashing function, when 0, DN_DS_MAP_DEFAULT_HASH_SEED is used - DN_DSMapFlags flags; -}; - -template struct DN_DSMapResult -{ - bool found; - DN_DSMapSlot *slot; - T *value; -}; - -#define DN_ISinglyLLDetach(list) (decltype(list))DN_SinglyLLDetach((void **)&(list), (void **)&(list)->next) - #define DN_SinglyHeadTailLLAppend(head, tail, to_append) \ do { \ if (!head) \ @@ -4667,6 +4733,20 @@ template struct DN_DSMapResult } while (0) #define DN_ISinglyHeadTailLLAppend(container_ptr, to_append) DN_SinglyHeadTailLLAppend((container_ptr)->head, (container_ptr)->tail, to_append) +// NOTE: Sentinel Doubly Linked List +// Uses a sentinel/dummy node as the list head. The sentinel points to itself when empty. +// Define a struct with the members `next` and `prev`: +// +// struct MyLinkItem { +// int data; +// MyLinkItem *next; +// MyLinkItem *prev; +// } my_list = {}; +// +// DN_SentinelDoublyLLInit(&my_list); +// DN_SentinelDoublyLLAppend(&my_list, &new_item); +// DN_SentinelDoublyLLForEach(it, &my_list) { /* ... */ } +// #define DN_SentinelDoublyLLInit(list) (list)->next = (list)->prev = (list) #define DN_SentinelDoublyLLIsSentinel(list, item) ((list) == (item)) #define DN_SentinelDoublyLLIsEmpty(list) (!(list) || ((list) == (list)->next)) @@ -4726,18 +4806,19 @@ template struct DN_DSMapResult } \ } while (0) -// DoublyLL => Non-intrusive Doubly Linked List -// A simple doubly linked list where each node has `next` and `prev` pointers. -// The head is passed as a pointer-to-pointer to allow head updates. +// NOTE: Doubly Linked List +// Define a struct with the members `next` and `prev`. This list has null pointers for head->prev +// and tail->next. // // struct MyLinkItem { // int data; // MyLinkItem *next; // MyLinkItem *prev; -// } *head = nullptr; -// DN_DoublyLLAppend(&head, new_item); -// for (MyLinkItem *it = head; it; it = it->next) { /* ... */ } - +// } my_link = {}; +// +// MyLinkItem first_item = {}, second_item = {}; +// DN_DoublyLLAppend(&first_item, &second_item); // first_item -> second_item +// #define DN_DoublyLLDetach(head, ptr) \ do { \ if ((head) && (head) == (ptr)) \ @@ -4789,100 +4870,192 @@ template struct DN_DSMapResult #define DN_CppDeclType #endif -#define DN_PArrayResizeFromPool(ptr, ptr_size, ptr_max, pool, new_max) DN_CArrayResizeFromPool((void **)&(ptr), ptr_size, ptr_max, sizeof((ptr)[0]), pool, new_max) -#define DN_PArrayResizeFromArena(ptr, ptr_size, ptr_max, arena, new_max) DN_CArrayResizeFromArena((void **)&(ptr), ptr_size, ptr_max, sizeof((ptr)[0]), arena, new_max) -#define DN_PArrayGrowFromPool(ptr, size, ptr_max, pool, new_max) DN_CArrayGrowFromPool((void **)&(ptr), size, ptr_max, sizeof((ptr)[0]), pool, new_max) -#define DN_PArrayGrowFromArena(ptr, size, ptr_max, arena, new_max) DN_CArrayGrowFromArena((void **)&(ptr), size, ptr_max, sizeof((ptr)[0]), arena, new_max) -#define DN_PArrayGrowIfNeededFromPool(ptr, size, ptr_max, pool, add_count) DN_CArrayGrowIfNeededFromPool((void **)(ptr), size, ptr_max, sizeof((*ptr)[0]), pool, add_count) -#define DN_PArrayGrowIfNeededFromArena(ptr, size, ptr_max, arena, add_count) DN_CArrayGrowIfNeededFromArena((void **)(ptr), size, ptr_max, sizeof((*ptr)[0]), arena, add_count) -#define DN_PArrayMakeArray(ptr, ptr_size, max, count, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_CArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), count, z_mem) -#define DN_PArrayMakeArrayZ(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_CArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_Yes) -#define DN_PArrayMake(ptr, ptr_size, max, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_CArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), 1, z_mem) -#define DN_PArrayMakeZ(ptr, ptr_size, max) (DN_CppDeclType(&(ptr)[0]))DN_CArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_Yes) -#define DN_PArrayAddArray(ptr, ptr_size, max, items, count, add) (DN_CppDeclType(&(ptr)[0]))DN_CArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, add) -#define DN_PArrayAdd(ptr, ptr_size, max, item, add) (DN_CppDeclType(&(ptr)[0]))DN_CArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, add) -#define DN_PArrayAppendArray(ptr, ptr_size, max, items, count) (DN_CppDeclType(&(ptr)[0]))DN_CArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, DN_ArrayAdd_Append) -#define DN_PArrayAppend(ptr, ptr_size, max, item) (DN_CppDeclType(&(ptr)[0]))DN_CArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, DN_ArrayAdd_Append) -#define DN_PArrayPrependArray(ptr, ptr_size, max, items, count) (DN_CppDeclType(&(ptr)[0]))DN_CArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, DN_ArrayAdd_Prepend) -#define DN_PArrayPrepend(ptr, ptr_size, max, item) (DN_CppDeclType(&(ptr)[0]))DN_CArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, DN_ArrayAdd_Prepend) -#define DN_PArrayEraseRange(ptr, ptr_size, begin_index, count, erase) DN_CArrayEraseRange(ptr, ptr_size, sizeof((ptr)[0]), begin_index, count, erase) -#define DN_PArrayErase(ptr, ptr_size, index, erase) DN_CArrayEraseRange(ptr, ptr_size, sizeof((ptr)[0]), index, 1, erase) -#define DN_PArrayInsertArray(ptr, ptr_size, max, index, items, count) (DN_CppDeclType(&(ptr)[0]))DN_CArrayInsertArray(ptr, ptr_size, max, sizeof((ptr)[0]), index, items, count) -#define DN_PArrayInsert(ptr, ptr_size, max, index, item) (DN_CppDeclType(&(ptr)[0]))DN_CArrayInsertArray(ptr, ptr_size, max, sizeof((ptr)[0]), index, &item, 1) -#define DN_PArrayPopFront(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_CArrayPopFront(ptr, ptr_size, sizeof((ptr)[0]), count) -#define DN_PArrayPopBack(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_CArrayPopBack(ptr, ptr_size, sizeof((ptr)[0]), count) +// NOTE: Arrays +// Data structures that have a `T *data`, `DN_USize count` and `DN_USize max` capacity that can be +// dynamically shrunk or expanded. +// +// API +// ResizeFrom: Resizes the array to `new_max` erase elements if resizing to a smaller size +// GrowFrom: Expands the capacity of the array if `new_max > array.max` otherwise no-op +// GrowIfNeeded: Expands the capacity of the array if `array.size + add_count > array.max` otherwise no-op +// +// Variants +// PArray => Pointer (to) Array +// LArray => Literal Array +// Define a C array and size. (P) array macros take a pointer to the aray, its size and its max +// capacity. The (L) array macros take the literal array and derives the max capacity +// automatically using DN_ArrayCountU(l_array). +// +// MyStruct buffer[TB_ASType_Count] = {}; +// DN_USize size = 0; +// MyStruct *item_0 = DN_PArrayMake(buffer, &size, DN_ArrayCountU(buffer), DN_ZMem_No); +// MyStruct *item_1 = DN_LArrayMake(buffer, &size, DN_ZMem_No); +// +// IArray => Intrusive Array +// Define a struct with the members `data`, `count` and `max`: +// +// struct MyArray { +// MyStruct *data; +// DN_USize count; +// DN_USize max; +// } my_array = {}; +// DN_Arena arena = {}; +// DN_IArrayResizeFromArena(&my_array, &arena, 256); +// MyStruct *item = DN_IArrayMake(&my_array, DN_ZMem_No); +// +#if defined(__cplusplus) + #define DN_PArrayFind(ptr, size, ptr_find, eq_func) DN_TArrayFind(ptr, size, ptr_find, eq_func) + #define DN_PArrayFindMemEq(ptr, size, ptr_find) DN_TArrayFindMemEq(ptr, size, ptr_find) + #define DN_PArrayResizeFromPool(ptr, ptr_size, ptr_max, pool, new_max) DN_TArrayResizeFromPool(&(ptr), ptr_size, ptr_max, pool, new_max) + #define DN_PArrayResizeFromArena(ptr, ptr_size, ptr_max, arena, new_max) DN_TArrayResizeFromArena(&(ptr), ptr_size, ptr_max, arena, new_max) + #define DN_PArrayGrowFromPool(ptr, size, ptr_max, pool, new_max) DN_TArrayGrowFromPool(&(ptr), size, ptr_max, pool, new_max) + #define DN_PArrayGrowFromArena(ptr, size, ptr_max, arena, new_max) DN_TArrayGrowFromArena(&(ptr), size, ptr_max, arena, new_max) + #define DN_PArrayGrowIfNeededFromPool(ptr, size, ptr_max, pool, add_count) DN_TArrayGrowIfNeededFromPool(ptr, size, ptr_max, pool, add_count) + #define DN_PArrayGrowIfNeededFromArena(ptr, size, ptr_max, arena, add_count) DN_TArrayGrowIfNeededFromArena(ptr, size, ptr_max, arena, add_count) + #define DN_PArrayMakeArray(ptr, ptr_size, max, count, z_mem) DN_TArrayMakeArray(ptr, ptr_size, max, count, z_mem) + #define DN_PArrayMakeArrayZ(ptr, ptr_size, max, count) DN_TArrayMakeArray(ptr, ptr_size, max, count, DN_ZMem_Yes) + #define DN_PArrayMake(ptr, ptr_size, max, z_mem) DN_TArrayMakeArray(ptr, ptr_size, max, 1, z_mem) + #define DN_PArrayMakeZ(ptr, ptr_size, max) DN_TArrayMakeArray(ptr, ptr_size, max, 1, DN_ZMem_Yes) + #define DN_PArrayAddArray(ptr, ptr_size, max, items, count, add) DN_TArrayAddArray(ptr, ptr_size, max, items, count, add) + #define DN_PArrayAdd(ptr, ptr_size, max, item, add) DN_TArrayAddArray(ptr, ptr_size, max, &item, 1, add) + #define DN_PArrayAppendArray(ptr, ptr_size, max, items, count) DN_TArrayAddArray(ptr, ptr_size, max, items, count, DN_ArrayAdd_Append) + #define DN_PArrayAppend(ptr, ptr_size, max, item) DN_TArrayAddArray(ptr, ptr_size, max, &item, 1, DN_ArrayAdd_Append) + #define DN_PArrayPrependArray(ptr, ptr_size, max, items, count) DN_TArrayAddArray(ptr, ptr_size, max, items, count, DN_ArrayAdd_Prepend) + #define DN_PArrayPrepend(ptr, ptr_size, max, item) DN_TArrayAddArray(ptr, ptr_size, max, &item, 1, DN_ArrayAdd_Prepend) + #define DN_PArrayEraseRange(ptr, ptr_size, begin_index, count, erase) DN_TArrayEraseRange(ptr, ptr_size, begin_index, count, erase) + #define DN_PArrayErase(ptr, ptr_size, index, erase) DN_TArrayEraseRange(ptr, ptr_size, index, 1, erase) + #define DN_PArrayInsertArray(ptr, ptr_size, max, index, items, count) DN_TArrayInsertArray(ptr, ptr_size, max, index, items, count) + #define DN_PArrayInsert(ptr, ptr_size, max, index, item) DN_TArrayInsertArray(ptr, ptr_size, max, index, &item, 1) + #define DN_PArrayPopFront(ptr, ptr_size, max, count) DN_TArrayPopFront(ptr, ptr_size, count) + #define DN_PArrayPopBack(ptr, ptr_size, max, count) DN_TArrayPopBack(ptr, ptr_size, count) +#else + #define DN_PArrayFind(ptr, size, ptr_find, eq_func) DN_ArrayFind(ptr, size, sizeof(*(ptr)), ptr_find, eq_func) + #define DN_PArrayFindMemEq(ptr, size, ptr_find) DN_ArrayFindMemEq(ptr, size, sizeof(*(ptr)), ptr_find) + #define DN_PArrayResizeFromPool(ptr, ptr_size, ptr_max, pool, new_max) DN_ArrayResizeFromPool((void **)&(ptr), ptr_size, ptr_max, sizeof((ptr)[0]), pool, new_max) + #define DN_PArrayResizeFromArena(ptr, ptr_size, ptr_max, arena, new_max) DN_ArrayResizeFromArena((void **)&(ptr), ptr_size, ptr_max, sizeof((ptr)[0]), arena, new_max) + #define DN_PArrayGrowFromPool(ptr, size, ptr_max, pool, new_max) DN_ArrayGrowFromPool((void **)&(ptr), size, ptr_max, sizeof((ptr)[0]), pool, new_max) + #define DN_PArrayGrowFromArena(ptr, size, ptr_max, arena, new_max) DN_ArrayGrowFromArena((void **)&(ptr), size, ptr_max, sizeof((ptr)[0]), arena, new_max) + #define DN_PArrayGrowIfNeededFromPool(ptr, size, ptr_max, pool, add_count) DN_ArrayGrowIfNeededFromPool((void **)(ptr), size, ptr_max, sizeof((*ptr)[0]), pool, add_count) + #define DN_PArrayGrowIfNeededFromArena(ptr, size, ptr_max, arena, add_count) DN_ArrayGrowIfNeededFromArena((void **)(ptr), size, ptr_max, sizeof((*ptr)[0]), arena, add_count) + #define DN_PArrayMakeArray(ptr, ptr_size, max, count, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), count, z_mem) + #define DN_PArrayMakeArrayZ(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_Yes) + #define DN_PArrayMake(ptr, ptr_size, max, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), 1, z_mem) + #define DN_PArrayMakeZ(ptr, ptr_size, max) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_Yes) + #define DN_PArrayAddArray(ptr, ptr_size, max, items, count, add) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, add) + #define DN_PArrayAdd(ptr, ptr_size, max, item, add) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, add) + #define DN_PArrayAppendArray(ptr, ptr_size, max, items, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, DN_ArrayAdd_Append) + #define DN_PArrayAppend(ptr, ptr_size, max, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, DN_ArrayAdd_Append) + #define DN_PArrayPrependArray(ptr, ptr_size, max, items, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, DN_ArrayAdd_Prepend) + #define DN_PArrayPrepend(ptr, ptr_size, max, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, DN_ArrayAdd_Prepend) + #define DN_PArrayEraseRange(ptr, ptr_size, begin_index, count, erase) DN_ArrayEraseRange(ptr, ptr_size, sizeof((ptr)[0]), begin_index, count, erase) + #define DN_PArrayErase(ptr, ptr_size, index, erase) DN_ArrayEraseRange(ptr, ptr_size, sizeof((ptr)[0]), index, 1, erase) + #define DN_PArrayInsertArray(ptr, ptr_size, max, index, items, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayInsertArray(ptr, ptr_size, max, sizeof((ptr)[0]), index, items, count) + #define DN_PArrayInsert(ptr, ptr_size, max, index, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayInsertArray(ptr, ptr_size, max, sizeof((ptr)[0]), index, &item, 1) + #define DN_PArrayPopFront(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayPopFront(ptr, ptr_size, sizeof((ptr)[0]), count) + #define DN_PArrayPopBack(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayPopBack(ptr, ptr_size, sizeof((ptr)[0]), count) +#endif -#define DN_LArrayResizeFromPool(c_array, size, pool, new_max) DN_PArrayResizeFromPool(c_array, size, DN_ArrayCountU(c_array), pool, new_max) -#define DN_LArrayResizeFromArena(c_array, size, arena, new_max) DN_PArrayResizeFromArena(c_array, size, DN_ArrayCountU(c_array), arena, new_max) -#define DN_LArrayGrowFromPool(c_array, size, pool, new_max) DN_PArrayGrowFromPool(c_array, size, DN_ArrayCountU(c_array), pool, new_max) -#define DN_LArrayGrowFromArena(c_array, size, arena, new_max) DN_PArrayGrowFromArena(c_array, size, DN_ArrayCountU(c_array), arena, new_max) -#define DN_LArrayGrowIfNeededFromPool(c_array, size, pool, add_count) DN_PArrayGrowIfNeededFromPool(c_array, size, DN_ArrayCountU(c_array), pool, add_count) -#define DN_LArrayGrowIfNeededFromArena(c_array, size, arena, add_count) DN_PArrayGrowIfNeededFromArena(c_array, size, DN_ArrayCountU(c_array), arena, add_count) -#define DN_LArrayMakeArray(c_array, ptr_size, count, z_mem) DN_PArrayMakeArray(c_array, ptr_size, DN_ArrayCountU(c_array), count, z_mem) -#define DN_LArrayMakeArrayZ(c_array, ptr_size, count) DN_PArrayMakeArrayZ(c_array, ptr_size, DN_ArrayCountU(c_array), count) -#define DN_LArrayMake(c_array, ptr_size, z_mem) DN_PArrayMake(c_array, ptr_size, DN_ArrayCountU(c_array), z_mem) -#define DN_LArrayMakeZ(c_array, ptr_size) DN_PArrayMakeZ(c_array, ptr_size, DN_ArrayCountU(c_array)) -#define DN_LArrayAddArray(c_array, ptr_size, items, count, add) DN_PArrayAddArray(c_array, ptr_size, DN_ArrayCountU(c_array), items, count, add) -#define DN_LArrayAdd(c_array, ptr_size, item, add) DN_PArrayAdd(c_array, ptr_size, DN_ArrayCountU(c_array), item, add) -#define DN_LArrayAppendArray(c_array, ptr_size, items, count) DN_PArrayAppendArray(c_array, ptr_size, DN_ArrayCountU(c_array), items, count) -#define DN_LArrayAppend(c_array, ptr_size, item) DN_PArrayAppend(c_array, ptr_size, DN_ArrayCountU(c_array), item) -#define DN_LArrayPrependArray(c_array, ptr_size, items, count) DN_PArrayPrependArray(c_array, ptr_size, DN_ArrayCountU(c_array), items, count) -#define DN_LArrayPrepend(c_array, ptr_size, item) DN_PArrayPrepend(c_array, ptr_size, DN_ArrayCountU(c_array), item) -#define DN_LArrayEraseRange(c_array, ptr_size, begin_index, count, erase) DN_PArrayEraseRange(c_array, ptr_size, begin_index, count, erase) -#define DN_LArrayErase(c_array, ptr_size, index, erase) DN_PArrayErase(c_array, ptr_size, index, erase) -#define DN_LArrayInsertArray(c_array, ptr_size, index, items, count) DN_PArrayInsertArray(c_array, ptr_size, DN_ArrayCountU(c_array), index, items, count) -#define DN_LArrayInsert(c_array, ptr_size, index, item) DN_PArrayInsert(c_array, ptr_size, DN_ArrayCountU(c_array), index, item) -#define DN_LArrayPopFront(c_array, ptr_size, count) DN_PArrayPopFront(c_array, ptr_size, DN_ArrayCountU(c_array), count) -#define DN_LArrayPopBack(c_array, ptr_size, count) DN_PArrayPopBack(c_array, ptr_size, DN_ArrayCountU(c_array), count) +#define DN_LArrayFind(c_array, size, ptr_find, eq_func) DN_PArrayFind(c_array, size, ptr_find, eq_func) +#define DN_LArrayFindMemEq(c_array, size, ptr_find) DN_PArrayFindMemEq(c_array, size, ptr_find) +#define DN_LArrayResizeFromPool(c_array, size, pool, new_max) DN_PArrayResizeFromPool(c_array, size, DN_ArrayCountU(c_array), pool, new_max) +#define DN_LArrayResizeFromArena(c_array, size, arena, new_max) DN_PArrayResizeFromArena(c_array, size, DN_ArrayCountU(c_array), arena, new_max) +#define DN_LArrayGrowFromPool(c_array, size, pool, new_max) DN_PArrayGrowFromPool(c_array, size, DN_ArrayCountU(c_array), pool, new_max) +#define DN_LArrayGrowFromArena(c_array, size, arena, new_max) DN_PArrayGrowFromArena(c_array, size, DN_ArrayCountU(c_array), arena, new_max) +#define DN_LArrayGrowIfNeededFromPool(c_array, size, pool, add_count) DN_PArrayGrowIfNeededFromPool(c_array, size, DN_ArrayCountU(c_array), pool, add_count) +#define DN_LArrayGrowIfNeededFromArena(c_array, size, arena, add_count) DN_PArrayGrowIfNeededFromArena(c_array, size, DN_ArrayCountU(c_array), arena, add_count) +#define DN_LArrayMakeArray(c_array, ptr_size, count, z_mem) DN_PArrayMakeArray(c_array, ptr_size, DN_ArrayCountU(c_array), count, z_mem) +#define DN_LArrayMakeArrayZ(c_array, ptr_size, count) DN_PArrayMakeArrayZ(c_array, ptr_size, DN_ArrayCountU(c_array), count) +#define DN_LArrayMake(c_array, ptr_size, z_mem) DN_PArrayMake(c_array, ptr_size, DN_ArrayCountU(c_array), z_mem) +#define DN_LArrayMakeZ(c_array, ptr_size) DN_PArrayMakeZ(c_array, ptr_size, DN_ArrayCountU(c_array)) +#define DN_LArrayAddArray(c_array, ptr_size, items, count, add) DN_PArrayAddArray(c_array, ptr_size, DN_ArrayCountU(c_array), items, count, add) +#define DN_LArrayAdd(c_array, ptr_size, item, add) DN_PArrayAdd(c_array, ptr_size, DN_ArrayCountU(c_array), item, add) +#define DN_LArrayAppendArray(c_array, ptr_size, items, count) DN_PArrayAppendArray(c_array, ptr_size, DN_ArrayCountU(c_array), items, count) +#define DN_LArrayAppend(c_array, ptr_size, item) DN_PArrayAppend(c_array, ptr_size, DN_ArrayCountU(c_array), item) +#define DN_LArrayPrependArray(c_array, ptr_size, items, count) DN_PArrayPrependArray(c_array, ptr_size, DN_ArrayCountU(c_array), items, count) +#define DN_LArrayPrepend(c_array, ptr_size, item) DN_PArrayPrepend(c_array, ptr_size, DN_ArrayCountU(c_array), item) +#define DN_LArrayEraseRange(c_array, ptr_size, begin_index, count, erase) DN_PArrayEraseRange(c_array, ptr_size, begin_index, count, erase) +#define DN_LArrayErase(c_array, ptr_size, index, erase) DN_PArrayErase(c_array, ptr_size, index, erase) +#define DN_LArrayInsertArray(c_array, ptr_size, index, items, count) DN_PArrayInsertArray(c_array, ptr_size, DN_ArrayCountU(c_array), index, items, count) +#define DN_LArrayInsert(c_array, ptr_size, index, item) DN_PArrayInsert(c_array, ptr_size, DN_ArrayCountU(c_array), index, item) +#define DN_LArrayPopFront(c_array, ptr_size, count) DN_PArrayPopFront(c_array, ptr_size, DN_ArrayCountU(c_array), count) +#define DN_LArrayPopBack(c_array, ptr_size, count) DN_PArrayPopBack(c_array, ptr_size, DN_ArrayCountU(c_array), count) -#define DN_IArrayResizeFromPool(ptr_array, pool, new_max) DN_CArrayResizeFromPool((void **)(&(ptr_array)->data), &(ptr_array)->size, &(ptr_array)->max, sizeof((ptr_array)->data[0]), pool, new_max) -#define DN_IArrayResizeFromArena(ptr_array, arena, new_max) DN_CArrayResizeFromArena((void **)(&(ptr_array)->data), &(ptr_array)->size, &(ptr_array)->max, sizeof((ptr_array)->data[0]), arena, new_max) -#define DN_IArrayGrowFromPool(ptr_array, pool, new_max) DN_CArrayGrowFromPool((void **)(&(ptr_array)->data), (ptr_array)->size, &(ptr_array)->max, sizeof((ptr_array)->data[0]), pool, new_max) -#define DN_IArrayGrowFromArena(ptr_array, arena, new_max) DN_CArrayGrowFromArena((void **)(&(ptr_array)->data), (ptr_array)->size, &(ptr_array)->max, sizeof((ptr_array)->data[0]), arena, new_max) -#define DN_IArrayGrowIfNeededFromPool(ptr_array, pool, add_count) DN_CArrayGrowIfNeededFromPool((void **)(&(ptr_array)->data), (ptr_array)->size, &(ptr_array)->max, sizeof((ptr_array)->data[0]), pool, add_count) -#define DN_IArrayGrowIfNeededFromArena(ptr_array, arena, add_count) DN_CArrayGrowIfNeededFromArena((void **)(&(ptr_array)->data), (ptr_array)->size, &(ptr_array)->max, sizeof((ptr_array)->data[0]), arena, add_count) -#define DN_IArrayMakeArray(ptr_array, count, z_mem) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayMakeArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), count, z_mem) -#define DN_IArrayMakeArrayZ(ptr_array, count) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayMakeArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), count, DN_ZMem_Yes) -#define DN_IArrayMake(ptr_array, z_mem) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayMakeArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), 1, z_mem) -#define DN_IArrayMakeZ(ptr_array) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayMakeArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), 1, DN_ZMem_Yes) -#define DN_IArrayAddArray(ptr_array, items, count, add) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayAddArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), items, count, add) -#define DN_IArrayAdd(ptr_array, item, add) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayAddArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), &item, 1, add) -#define DN_IArrayAppendArray(ptr_array, items, count) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayAddArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), items, count, DN_ArrayAdd_Append) -#define DN_IArrayAppend(ptr_array, item) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayAddArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), &item, 1, DN_ArrayAdd_Append) -#define DN_IArrayPrependArray(ptr_array, items, count) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayAddArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), items, count, DN_ArrayAdd_Prepend) -#define DN_IArrayPrepend(ptr_array, item) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayAddArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), &item, 1, DN_ArrayAdd_Prepend) -#define DN_IArrayEraseRange(ptr_array, begin_index, count, erase) DN_CArrayEraseRange((ptr_array)->data, &(ptr_array)->size, sizeof(((ptr_array)->data)[0]), begin_index, count, erase) -#define DN_IArrayErase(ptr_array, index, erase) DN_CArrayEraseRange((ptr_array)->data, &(ptr_array)->size, sizeof(((ptr_array)->data)[0]), index, 1, erase) -#define DN_IArrayInsertArray(ptr_array, index, items, count) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayInsertArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), index, items, count) -#define DN_IArrayInsert(ptr_array, index, item, count) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayInsertArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), index, &item, 1) -#define DN_IArrayPopFront(ptr_array, count) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayPopFront((ptr_array)->data, &(ptr_array)->size, sizeof(((ptr_array)->data)[0]), count) -#define DN_IArrayPopBack(ptr_array, count) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayPopBack((ptr_array)->data, &(ptr_array)->size, sizeof(((ptr_array)->data)[0]), count) +#define DN_IArrayFind(ptr_array, ptr_find, eq_func) DN_PArrayFind((ptr_array)->data, (ptr_array)->count, ptr_find, eq_func) +#define DN_IArrayFindMemEq(ptr_array, ptr_find) DN_PArrayFindMemEq((ptr_array)->data, (ptr_array)->count, ptr_find) +#define DN_IArrayResizeFromPool(ptr_array, pool, new_max) DN_PArrayResizeFromPool((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, pool, new_max) +#define DN_IArrayResizeFromArena(ptr_array, arena, new_max) DN_PArrayResizeFromArena((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, arena, new_max) +#define DN_IArrayGrowFromPool(ptr_array, pool, new_max) DN_PArrayGrowFromPool((ptr_array)->data, (ptr_array)->count, &(ptr_array)->max, pool, new_max) +#define DN_IArrayGrowFromArena(ptr_array, arena, new_max) DN_PArrayGrowFromArena((ptr_array)->data, (ptr_array)->count, &(ptr_array)->max, arena, new_max) +#define DN_IArrayGrowIfNeededFromPool(ptr_array, pool, add_count) DN_PArrayGrowIfNeededFromPool(&(ptr_array)->data, (ptr_array)->count, &(ptr_array)->max, pool, add_count) +#define DN_IArrayGrowIfNeededFromArena(ptr_array, arena, add_count) DN_PArrayGrowIfNeededFromArena(&(ptr_array)->data, (ptr_array)->count, &(ptr_array)->max, arena, add_count) +#define DN_IArrayMakeArray(ptr_array, count, z_mem) DN_PArrayMakeArray((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, count, z_mem) +#define DN_IArrayMakeArrayZ(ptr_array, count) DN_PArrayMakeArrayZ((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, count) +#define DN_IArrayMake(ptr_array, z_mem) DN_PArrayMake((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, z_mem) +#define DN_IArrayMakeZ(ptr_array) DN_PArrayMakeZ((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max) +#define DN_IArrayAddArray(ptr_array, items, count, add) DN_PArrayAddArray((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, items, count, add) +#define DN_IArrayAdd(ptr_array, item, add) DN_PArrayAdd((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, item, add) +#define DN_IArrayAppendArray(ptr_array, items, count) DN_PArrayAppendArray((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, items, count) +#define DN_IArrayAppend(ptr_array, item) DN_PArrayAppend((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, item) +#define DN_IArrayPrependArray(ptr_array, items, count) DN_PArrayPrependArray((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, items, count) +#define DN_IArrayPrepend(ptr_array, item) DN_PArrayPrepend((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, item) +#define DN_IArrayEraseRange(ptr_array, begin_index, count, erase) DN_PArrayEraseRange((ptr_array)->data, &(ptr_array)->count, begin_index, count, erase) +#define DN_IArrayErase(ptr_array, index, erase) DN_PArrayErase((ptr_array)->data, &(ptr_array)->count, index, erase) +#define DN_IArrayInsertArray(ptr_array, index, items, count) DN_PArrayInsertArray((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, index, items, count) +#define DN_IArrayInsert(ptr_array, index, item) DN_PArrayInsert((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, index, item) +#define DN_IArrayPopFront(ptr_array, count) DN_PArrayPopFront((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, count) +#define DN_IArrayPopBack(ptr_array, count) DN_PArrayPopBack((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, count) -#define DN_ISliceAllocArena(slice_ptr, count_, zmem, arena) (DN_CppDeclType(&((slice_ptr)->data[0])))DN_SliceAllocArena((void **)&((slice_ptr)->data), &((slice_ptr)->count), count_, sizeof((slice_ptr)->data[0]), alignof(DN_CppDeclType((slice_ptr)->data[0])), zmem, arena) +// NOTE: Slices +// +// Fixed size container allocated up front that have a `T *data` and `DN_USize count` elements. +// +// API +// AllocArena: Allocates the container with the requested `count` elements +// +#define DN_ISliceAllocArena(slice_ptr, count_, zmem, arena) (DN_CppDeclType(&((slice_ptr)->data[0])))DN_SliceAllocArena((void **)&((slice_ptr)->data), &((slice_ptr)->count), count_, sizeof((slice_ptr)->data[0]), alignof(DN_CppDeclType((slice_ptr)->data[0])), zmem, arena) -DN_API void* DN_SliceAllocArena (void **data, DN_USize *slice_size_field, DN_USize size, DN_USize elem_size, DN_U8 align, DN_ZMem zmem, DN_Arena *arena); -DN_API void* DN_CArrayInsertArray (void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize index, void const *items, DN_USize count); -DN_API void* DN_CArrayPopFront (void *data, DN_USize *size, DN_USize elem_size, DN_USize count); -DN_API void* DN_CArrayPopBack (void *data, DN_USize *size, DN_USize elem_size, DN_USize count); -DN_API DN_ArrayEraseResult DN_CArrayEraseRange (void *data, DN_USize *size, DN_USize elem_size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase); -DN_API void* DN_CArrayMakeArray (void *data, DN_USize *size, DN_USize max, DN_USize data_size, DN_USize make_size, DN_ZMem z_mem); -DN_API void* DN_CArrayAddArray (void *data, DN_USize *size, DN_USize max, DN_USize data_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add); -DN_API bool DN_CArrayResizeFromPool (void **data, DN_USize *size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize new_max); -DN_API bool DN_CArrayResizeFromArena (void **data, DN_USize *size, DN_USize *max, DN_USize data_size, DN_Arena *arena, DN_USize new_max); -DN_API bool DN_CArrayGrowFromPool (void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize new_max); -DN_API bool DN_CArrayGrowFromArena (void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize new_max); -DN_API bool DN_CArrayGrowIfNeededFromPool (void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize add_count); -DN_API bool DN_CArrayGrowIfNeededFromArena (void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Arena *pool, DN_USize add_count); +DN_API void* DN_SliceAllocArena (void **data, DN_USize *slice_size_field, DN_USize size, DN_USize elem_size, DN_U8 align, DN_ZMem zmem, DN_Arena *arena); -DN_API void* DN_SinglyLLDetach (void **link, void **next); +DN_API DN_ArrayFindResult DN_ArrayFind (void *data, DN_USize size, DN_USize elem_size, void const *find, DN_ArrayFindEqFunc *eq_func); +DN_API DN_ArrayFindResult DN_ArrayFindMemEq (void *data, DN_USize size, DN_USize elem_size, void const *find); +DN_API void* DN_ArrayInsertArray (void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize index, void const *items, DN_USize count); +DN_API void* DN_ArrayPopFront (void *data, DN_USize *size, DN_USize elem_size, DN_USize count); +DN_API void* DN_ArrayPopBack (void *data, DN_USize *size, DN_USize elem_size, DN_USize count); +DN_API DN_ArrayEraseResult DN_ArrayEraseRange (void *data, DN_USize *size, DN_USize elem_size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase); +DN_API void* DN_ArrayMakeArray (void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize make_count, DN_ZMem z_mem); +DN_API void* DN_ArrayAddArray (void *data, DN_USize *size, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add); +DN_API bool DN_ArrayResizeFromPool (void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max); +DN_API bool DN_ArrayResizeFromArena (void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max); +DN_API bool DN_ArrayGrowFromPool (void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max); +DN_API bool DN_ArrayGrowFromArena (void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max); +DN_API bool DN_ArrayGrowIfNeededFromPool (void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize add_count); +DN_API bool DN_ArrayGrowIfNeededFromArena (void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize add_count); -DN_API bool DN_RingHasSpace (DN_Ring const *ring, DN_U64 size); -DN_API bool DN_RingHasData (DN_Ring const *ring, DN_U64 size); -DN_API void DN_RingWrite (DN_Ring *ring, void const *src, DN_U64 src_size); -#define DN_RingWriteStruct(ring, item) DN_RingWrite((ring), (item), sizeof(*(item))) -DN_API void DN_RingRead (DN_Ring *ring, void *dest, DN_U64 dest_size); -#define DN_RingReadStruct(ring, dest) DN_RingRead((ring), (dest), sizeof(*(dest))) +#if defined (__cplusplus) +template DN_ArrayFindResult DN_TArrayFind (T *data, DN_USize size, void const *find, DN_ArrayFindEqFunc *eq_func); +template DN_ArrayFindResult DN_TArrayFindMemEq (T *data, DN_USize size, void const *find, DN_ArrayFindEqFunc *eq_func); +template T* DN_TArrayInsertArray (T *data, DN_USize *size, DN_USize max, DN_USize index, void const *items, DN_USize count); +template T* DN_TArrayPopFront (T *data, DN_USize *size, DN_USize count); +template T* DN_TArrayPopBack (T *data, DN_USize *size, DN_USize count); +template DN_ArrayEraseResult DN_TArrayEraseRange (T *data, DN_USize *size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase); +template T* DN_TArrayMakeArray (T *data, DN_USize *size, DN_USize max, DN_USize make_count, DN_ZMem z_mem); +template T* DN_TArrayAddArray (T *data, DN_USize *size, DN_USize max, T const *elems, DN_USize elems_count, DN_ArrayAdd add); +template bool DN_TArrayResizeFromPool (T **data, DN_USize *size, DN_USize *max, DN_Pool *pool, DN_USize new_max); +template bool DN_TArrayResizeFromArena (T **data, DN_USize *size, DN_USize *max, DN_Arena *arena, DN_USize new_max); +template bool DN_TArrayGrowFromPool (T **data, DN_USize size, DN_USize *max, DN_Pool *pool, DN_USize new_max); +template bool DN_TArrayGrowFromArena (T **data, DN_USize size, DN_USize *max, DN_Pool *pool, DN_USize new_max); +template bool DN_TArrayGrowIfNeededFromPool (T **data, DN_USize size, DN_USize *max, DN_Pool *pool, DN_USize add_count); +template bool DN_TArrayGrowIfNeededFromArena (T **data, DN_USize size, DN_USize *max, DN_Arena *pool, DN_USize add_count); +#endif + +DN_API void* DN_SinglyLLDetach (void **link, void **next); +DN_API bool DN_RingHasSpace (DN_Ring const *ring, DN_U64 size); +DN_API bool DN_RingHasData (DN_Ring const *ring, DN_U64 size); +DN_API void DN_RingWrite (DN_Ring *ring, void const *src, DN_U64 src_size); +#define DN_RingWriteStruct(ring, item) DN_RingWrite((ring), (item), sizeof(*(item))) +DN_API void DN_RingRead (DN_Ring *ring, void *dest, DN_U64 dest_size); +#define DN_RingReadStruct(ring, dest) DN_RingRead((ring), (dest), sizeof(*(dest))) DN_U32 const DN_DS_MAP_DEFAULT_HASH_SEED = 0x8a1ced49; DN_U32 const DN_DS_MAP_SENTINEL_SLOT = 0; @@ -4913,7 +5086,107 @@ template DN_DSMapKey DN_DSMapKeyStr8 (DN_DSMap DN_API DN_DSMapKey DN_DSMapKeyU64NoHash (DN_U64 u64); DN_API bool DN_DSMapKeyEquals (DN_DSMapKey lhs, DN_DSMapKey rhs); DN_API bool operator== (DN_DSMapKey lhs, DN_DSMapKey rhs); -#endif // !defined(DN_CONTAINER_H) + +#if defined(__cplusplus) +template +DN_ArrayFindResult DN_TArrayFind(T *data, DN_USize size, void const *find, DN_ArrayFindEqFunc *eq_func) +{ + DN_ArrayFindResult result = DN_ArrayFind(data, size, sizeof(*data), find, eq_func); + return result; +} + +template +DN_ArrayFindResult DN_TArrayFindMemEq(T *data, DN_USize size, void const *find) +{ + DN_ArrayFindResult result = DN_ArrayFindMemEq(data, size, sizeof(*data), find); + return result; +} + +template +T *DN_TArrayInsertArray(T *data, DN_USize *size, DN_USize max, DN_USize index, T const *items, DN_USize count) +{ + T *result = DN_Cast(T *)DN_ArrayInsertArray(data, size, max, sizeof(*data), index, items, count); + return result; +} + +template +T *DN_TArrayPopFront(T *data, DN_USize *size, DN_USize count) +{ + T *result = DN_Cast(T *)DN_ArrayPopFront(data, size, sizeof(*data), count); + return result; +} + +template +T *DN_TArrayPopBack(T *data, DN_USize *size, DN_USize count) +{ + T *result = DN_Cast(T *)DN_ArrayPopBack(data, size, sizeof(*data), count); + return result; +} + +template +DN_ArrayEraseResult DN_TArrayEraseRange(T *data, DN_USize *size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase) +{ + DN_ArrayEraseResult result = DN_ArrayEraseRange(data, size, sizeof(*data), begin_index, count, erase); + return result; +} + +template +T *DN_TArrayMakeArray(T *data, DN_USize *size, DN_USize max, DN_USize make_count, DN_ZMem z_mem) +{ + T *result = DN_Cast(T *)DN_ArrayMakeArray(data, size, max, sizeof(*data), make_count, z_mem); + return result; +} + +template +T *DN_TArrayAddArray(T *data, DN_USize *size, DN_USize max, T const *elems, DN_USize elems_count, DN_ArrayAdd add) +{ + T* result = DN_Cast(T *)DN_ArrayAddArray(data, size, max, sizeof(*elems), elems, elems_count, add); + return result; +} + +template +bool DN_TArrayResizeFromPool(T **data, DN_USize *size, DN_USize *max, DN_Pool *pool, DN_USize new_max) +{ + bool result = DN_ArrayResizeFromPool(DN_Cast(void **)data, size, max, sizeof(**data), pool, new_max); + return result; +} + +template +bool DN_TArrayResizeFromArena(T **data, DN_USize *size, DN_USize *max, DN_Arena *arena, DN_USize new_max) +{ + bool result = DN_ArrayResizeFromArena(DN_Cast(void **)data, size, max, sizeof(**data), arena, new_max); + return result; +} + +template +bool DN_TArrayGrowFromPool(T **data, DN_USize size, DN_USize *max, DN_Pool *pool, DN_USize new_max) +{ + bool result = DN_ArrayGrowFromPool(DN_Cast(void **)data, size, max, sizeof(**data), pool, new_max); + return result; +} + +template +bool DN_TArrayGrowFromArena(T **data, DN_USize size, DN_USize *max, DN_Arena *arena, DN_USize new_max) +{ + bool result = DN_ArrayGrowFromArena(DN_Cast(void **)data, size, max, sizeof(**data), arena, new_max); + return result; +} + +template +bool DN_TArrayGrowIfNeededFromPool(T **data, DN_USize size, DN_USize *max, DN_Pool *pool, DN_USize add_count) +{ + bool result = DN_ArrayGrowIfNeededFromPool(DN_Cast(void **)data, size, max, sizeof(**data), pool, add_count); + return result; +} + +template +bool DN_TArrayGrowIfNeededFromArena(T **data, DN_USize size, DN_USize *max, DN_Arena *arena, DN_USize add_count) +{ + bool result = DN_ArrayGrowIfNeededFromArena(DN_Cast(void **)data, size, max, sizeof(**data), arena, add_count); + return result; +} +#endif // defined(__cplusplus) +#endif // !defined(DN_BASE_H) // DN: Single header generator commented out => #include "Base/dn_base_leak.h" #if !defined(DN_BASE_LEAK_H) #define DN_BASE_LEAK_H @@ -6784,7 +7057,8 @@ DN_API DN_MemFuncs DN_MemFuncsFromType (D DN_API DN_MemFuncs DN_MemFuncsDefault (); DN_API DN_MemList DN_MemListFromHeap (DN_U64 size, DN_MemFlags flags); DN_API DN_MemList DN_MemListFromVMem (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags); - +DN_API DN_Arena DN_ArenaFromHeap (DN_U64 wize, DN_MemFlags flags); +DN_API DN_Arena DN_ArenaFromVMem (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags); DN_API DN_Str8 DN_Str8FromHeapF (DN_FMT_ATTRIB char const *fmt, ...); DN_API DN_Str8 DN_Str8FromHeap (DN_USize size, DN_ZMem z_mem); @@ -7027,11 +7301,6 @@ template void DN_OS_VArr #endif // !defined(DN_OS_H) #endif -struct DN_InitArgs -{ - DN_TCInitArgs thread_context_init_args; -}; - typedef DN_USize DN_InitFlags; enum DN_InitFlags_ { @@ -7061,7 +7330,7 @@ struct DN_Core #endif }; -DN_API void DN_Init (DN_Core *dn, DN_InitFlags flags, DN_InitArgs *args); +DN_API void DN_Init (DN_Core *dn, DN_InitFlags flags, DN_TCInitArgs args); DN_API void DN_Set (DN_Core *dn); DN_API DN_Core *DN_Get (); DN_API void DN_BeginFrame(); @@ -7149,15 +7418,12 @@ enum DN_BinarySearchType struct DN_BinarySearchResult { - bool found; - DN_USize index; + bool found; + DN_USize index; }; -template -using DN_QSortLessThanProc = bool(T const &a, T const &b, void *user_context); - #if !defined(DN_NO_JSON_BUILDER) -// NOTE: DN_JSONBuilder //////////////////////////////////////////////////////////////////////////// +// NOTE: DN_JSONBuilder #define DN_JSONBuilder_Object(builder) \ DN_DeferLoop(DN_JSONBuilder_ObjectBegin(builder), \ DN_JSONBuilder_ObjectEnd(builder)) @@ -7203,10 +7469,6 @@ DN_API void DN_JSONBuilder_BoolNamed (DN_JSONBuilde template bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs); template DN_BinarySearchResult DN_BinarySearch (T const *array, DN_USize array_size, T const &find, DN_BinarySearchType type = DN_BinarySearchType_Match, DN_BinarySearchLessThanProc less_than = DN_BinarySearch_DefaultLessThan); -// NOTE: DN_QSort -template bool DN_QSort_DefaultLessThan(T const &lhs, T const &rhs, void *user_context); -template void DN_QSort (T *array, DN_USize array_size, void *user_context, DN_QSortLessThanProc less_than = DN_QSort_DefaultLessThan); - // NOTE: DN_BinarySearch template bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs) @@ -7259,78 +7521,6 @@ DN_BinarySearchResult DN_BinarySearch(T const *array, result.index = first - array; return result; } - -// NOTE: DN_QSort ////////////////////////////////////////////////////////////////////////////////// -template -bool DN_QSort_DefaultLessThan(T const &lhs, T const &rhs, void *user_context) -{ - (void)user_context; - bool result = lhs < rhs; - return result; -} - -template -void DN_QSort(T *array, DN_USize array_size, void *user_context, DN_QSortLessThanProc less_than) -{ - if (!array || array_size <= 1 || !less_than) - return; - - // NOTE: Insertion Sort, under 24->32 is an optimal amount ///////////////////////////////////// - const DN_USize QSORT_THRESHOLD = 24; - if (array_size < QSORT_THRESHOLD) { - for (DN_USize item_to_insert_index = 1; item_to_insert_index < array_size; item_to_insert_index++) { - for (DN_USize index = 0; index < item_to_insert_index; index++) { - if (!less_than(array[index], array[item_to_insert_index], user_context)) { - T item_to_insert = array[item_to_insert_index]; - for (DN_USize i = item_to_insert_index; i > index; i--) - array[i] = array[i - 1]; - - array[index] = item_to_insert; - break; - } - } - } - return; - } - - // NOTE: Quick sort, under 24->32 is an optimal amount ///////////////////////////////////////// - DN_USize last_index = array_size - 1; - DN_USize pivot_index = array_size / 2; - DN_USize partition_index = 0; - DN_USize start_index = 0; - - // Swap pivot with last index, so pivot is always at the end of the array. - // This makes logic much simpler. - DN_Swap(array[last_index], array[pivot_index]); - pivot_index = last_index; - - // 4^, 8, 7, 5, 2, 3, 6 - if (less_than(array[start_index], array[pivot_index], user_context)) - partition_index++; - start_index++; - - // 4, |8, 7, 5^, 2, 3, 6* - // 4, 5, |7, 8, 2^, 3, 6* - // 4, 5, 2, |8, 7, ^3, 6* - // 4, 5, 2, 3, |7, 8, ^6* - for (DN_USize index = start_index; index < last_index; index++) { - if (less_than(array[index], array[pivot_index], user_context)) { - DN_Swap(array[partition_index], array[index]); - partition_index++; - } - } - - // Move pivot to right of partition - // 4, 5, 2, 3, |6, 8, ^7* - DN_Swap(array[partition_index], array[pivot_index]); - DN_QSort(array, partition_index, user_context, less_than); - - // Skip the value at partion index since that is guaranteed to be sorted. - // 4, 5, 2, 3, (x), 8, 7 - DN_USize one_after_partition_index = partition_index + 1; - DN_QSort(array + one_after_partition_index, (array_size - one_after_partition_index), user_context, less_than); -} - #endif // !defined(DN_HELPERS_H) #endif @@ -7449,7 +7639,7 @@ struct DN_NETDoHTTPArgs DN_U16 headers_size; // NOTE: HTTP args only - DN_Str8 payload; + DN_Str8 payload; }; struct DN_NETRequestHandle @@ -7461,6 +7651,7 @@ struct DN_NETRequestHandle struct DN_NETResponse { // NOTE: Common to WS and HTTP responses + DN_NETRequestType type; DN_NETResponseState state; DN_NETRequestHandle request; DN_Str8 error_str8; @@ -7517,14 +7708,16 @@ struct DN_NETCore DN_NETInterface api; }; -DN_Str8 DN_NET_Str8FromResponseState(DN_NETResponseState state); -DN_NETRequest * DN_NET_RequestFromHandle (DN_NETRequestHandle handle); -DN_NETRequestHandle DN_NET_HandleFromRequest (DN_NETRequest *request); +DN_Str8 DN_NET_Str8FromResponseState (DN_NETResponseState state); +DN_NETRequest * DN_NET_RequestFromHandle (DN_NETRequestHandle handle); +DN_NETRequestHandle DN_NET_HandleFromRequest (DN_NETRequest *request); +bool DN_NET_ResponseHasFailed (DN_NETResponse const* resp); +DN_Str8 DN_NET_Str8DiagnosticFromResponse(DN_NETResponse const* resp, DN_Arena *arena); // NOTE: Internal functions for different networking implementations to use -void DN_NET_BaseInit_ (DN_NETCore *net, char *base, DN_U64 base_size); -DN_NETRequestHandle DN_NET_SetupRequest_ (DN_NETRequest *request, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type); -void DN_NET_EndFinishedRequest_ (DN_NETRequest *request); +void DN_NET_BaseInit (DN_NETCore *net, char *base, DN_U64 base_size); +DN_NETRequestHandle DN_NET_SetupRequest (DN_NETRequest *request, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type); +void DN_NET_EndFinishedRequest (DN_NETRequest *request); #endif // DN_NET_H #endif diff --git a/Source/Base/dn_base.cpp b/Source/Base/dn_base.cpp index 3c2eefe..f5cbef0 100644 --- a/Source/Base/dn_base.cpp +++ b/Source/Base/dn_base.cpp @@ -6,6 +6,12 @@ #include "../dn.h" #endif +enum DN_ArenaUAFCheckReportType_ +{ + DN_ArenaUAFCheckReportType_AllocViolation, + DN_ArenaUAFCheckReportType_TempEndOutOfOrder, +}; + DN_API bool DN_MemStartsWith(void const *lhs, DN_USize lhs_size, void const *rhs, DN_USize rhs_size) { bool result = false; @@ -977,12 +983,13 @@ static void DN_MemBlockDeinit_(DN_MemList const *mem, DN_MemBlock *block) DN_API void DN_MemListDeinit(DN_MemList *mem) { + bool mem_allocated_from_itself = DN_MemListOwnsPtr(mem, mem); for (DN_MemBlock *block = mem ? mem->curr : nullptr; block;) { DN_MemBlock *block_to_free = block; - block = block->prev; + block = block->prev; DN_MemBlockDeinit_(mem, block_to_free); } - if (mem) + if (mem && !mem_allocated_from_itself) *mem = {}; } @@ -1111,7 +1118,7 @@ DN_API void *DN_MemListAllocContiguous(DN_MemList *mem, DN_U64 size, uint8_t ali { DN_MemFlags prev_flags = mem->flags; mem->flags |= (DN_MemFlags_NoGrow | DN_MemFlags_NoPoison); - void *memory = DN_MemListAlloc(mem, size, align, z_mem); + void *memory = DN_MemListAlloc(mem, size, align, z_mem); mem->flags = prev_flags; return memory; } @@ -1208,9 +1215,9 @@ DN_API bool DN_MemListOwnsPtr(DN_MemList const *mem, void *ptr) DN_API DN_Str8x64 DN_MemListInfoStr8x64(DN_MemListInfo info) { DN_Str8x64 result = {}; - DN_Str8x32 used = DN_ByteCountStr8x32(info.used); - DN_Str8x32 commit = DN_ByteCountStr8x32(info.commit); - DN_Str8x32 reserve = DN_ByteCountStr8x32(info.reserve); + DN_Str8x32 used = DN_Str8x32FromByteCountU64Auto(info.used); + DN_Str8x32 commit = DN_Str8x32FromByteCountU64Auto(info.commit); + DN_Str8x32 reserve = DN_Str8x32FromByteCountU64Auto(info.reserve); // NOTE: Blocks, Used, Commit, Reserve result = DN_Str8x64FromFmt("B=%u U=%.*s C=%.*s R=%.*s", DN_Cast(DN_U32)info.blocks, DN_Str8PrintFmt(used), DN_Str8PrintFmt(commit), DN_Str8PrintFmt(reserve)); return result; @@ -1248,9 +1255,10 @@ static bool DN_MemListUAFTracingEnabled_(DN_MemList *mem) } #endif -DN_API void DN_ArenaUAFCheck(DN_Arena *arena) +static void DN_ArenaUAFCheck_(DN_Arena *arena, DN_ArenaUAFCheckReportType_ type) { (void)arena; + (void)type; #if DN_ARENA_TEMP_MEM_UAF_GUARD DN_MemList *mem = arena->mem; if (!arena || !mem) @@ -1261,32 +1269,52 @@ DN_API void DN_ArenaUAFCheck(DN_Arena *arena) // check which would cause infinite recursion so we set a flag here to prevent that. arena->uaf_guard_is_being_checked = true; if (mem->uaf_guard_active_id != arena->uaf_guard_id) { + // NOTE: We use the MemList on the arena directly to bypass any potential recursive UAF (if the + // current arena is triggering the UAF check then it's already violating so we use the + // underlying primitive to allocate memory). + DN_Allocator allocator = DN_AllocatorFromMemList(mem); + // NOTE: MSVC does not recognise %'u which is a STB extension which causes a lot of incorrect // format arguments warnings that we mute here. DN_MSVC_WARNING_PUSH DN_MSVC_WARNING_DISABLE(6271) // Extra argument passed to 'DN_Str8FromFmtArena' DN_MSVC_WARNING_DISABLE(6067) // _Param_(10) in call to 'DN_LogPrint' must be the address of a string. Actual type: 'int'. DN_MSVC_WARNING_DISABLE(6273) // Non-integer passed as _Param_(11) when an integer is required in call to 'DN_LogPrint' Actual type: 'char *'. - DN_Str8 prefix = DN_Str8LineBreakStr8(DN_Str8FromFmtArena(arena, - "Arena use-after-free (UAF) detected in temporary memory usage! This allocation (trace " - "shown above) is attempting to allocate memory inside the active temporary region (id: %'u) " - "but belongs to a different region (id: %'u). This means when the active temporary region is " - "released, this allocation will be released and scrubbed causing a potential UAF.\n\nEnsure " - "that scratch memory is deconflicting correctly, scratch and or temporary memory regions have " - "matching begin and end pairs and only the arena view with the active temporary memory region " - "is being allocated from.", - mem->uaf_guard_active_id, - arena->uaf_guard_id), - 100, - arena); + DN_Str8 error_msg = {}; + if (type == DN_ArenaUAFCheckReportType_AllocViolation) { + error_msg = DN_Str8FromFmtAllocator(allocator, + "Arena use-after-free (UAF) detected in temporary memory usage! This allocation (trace " + "shown above) is attempting to allocate memory inside the active temporary region (id: %'u) " + "but belongs to a different region (id: %'u). This means when the active temporary region is " + "released, this allocation will be released and scrubbed causing a potential UAF.\n\nEnsure " + "that scratch memory is deconflicting correctly, scratch and or temporary memory regions have " + "matching begin and end pairs and only the arena view with the active temporary memory region " + "is being allocated from.", + mem->uaf_guard_active_id, + arena->uaf_guard_id); + } else { + error_msg = DN_Str8Lit("The active temporary memory region recorded on the arena is " + "different from the current temporary memory region recorded on " + "the memory list allocator. This means that a temporary region " + "began but was not ended after the region was completed. Temporary " + "memory regions are enforced in a first-in-last-out manner (FILO) " + "to ensure the developer's intent of what the temporary region " + "spans is logically consistent and always strictly ends and begins " + "within a known lifetime."); + } + DN_Str8 prefix = DN_Str8LineBreakAllocator(error_msg, 100, DN_Str8Lit("\n"), DN_Str8LineBreakMode_AtWord, allocator); if (DN_MemListUAFTracingEnabled_(mem)) { DN_Str8 curr_stack_trace = DN_Str8Lit(""); if (arena->uaf_guard_temp_mem) - curr_stack_trace = DN_StackTraceWalkResultToStr8(arena, &arena->uaf_guard_temp_mem->trace, 1); - curr_stack_trace = DN_Str8PadNewLines(curr_stack_trace, DN_Str8Lit(" "), arena); + curr_stack_trace = DN_Str8FromStackTraceAllocator(allocator, &arena->uaf_guard_temp_mem->trace, 1); + curr_stack_trace = DN_Str8PadNewLinesAllocator(curr_stack_trace, DN_Str8Lit(" "), allocator); + + DN_Str8 active_stack_trace = DN_Str8Lit(""); + if (mem->uaf_guard_active_temp_mem) + active_stack_trace = DN_Str8FromStackTraceAllocator(allocator, &mem->uaf_guard_active_temp_mem->trace, 1); + active_stack_trace = DN_Str8PadNewLinesAllocator(active_stack_trace, DN_Str8Lit(" "), allocator); - DN_Str8 active_stack_trace = DN_Str8PadNewLines(DN_StackTraceWalkResultToStr8(arena, &mem->uaf_guard_active_temp_mem->trace, 1), DN_Str8Lit(" "), arena); DN_AssertF(mem->uaf_guard_active_id == arena->uaf_guard_id, "%.*s\n\nThe originating temporary memory region (id: %'u) was created at:" "\n\n %.*s\n\nThe active temporary memory region (id: %'u) was created at:\n\n %.*s", @@ -1296,7 +1324,7 @@ DN_API void DN_ArenaUAFCheck(DN_Arena *arena) mem->uaf_guard_active_id, DN_Str8PrintFmt(active_stack_trace)); } else { - DN_Str8 suffix = DN_Str8LineBreakStr8(DN_MEM_LIST_UAF_TRACING_DISABLED_MORE_INFO_STR8_, 100, arena); + DN_Str8 suffix = DN_Str8LineBreakAllocator(DN_MEM_LIST_UAF_TRACING_DISABLED_MORE_INFO_STR8_, 100, DN_Str8Lit("\n"), DN_Str8LineBreakMode_AtWord, allocator); DN_AssertF(mem->uaf_guard_active_id == arena->uaf_guard_id, "%.*s%.*s", DN_Str8PrintFmt(prefix), DN_Str8PrintFmt(suffix)); } DN_MSVC_WARNING_POP @@ -1319,8 +1347,10 @@ DN_API DN_Arena DN_ArenaTempBeginFromMemList(DN_MemList* mem) DN_MemListTemp temp_mem = DN_MemListTempBegin(mem); #if DN_ARENA_TEMP_MEM_UAF_GUARD + // NOTE: Below we use the `MemList` and bypass the UAF checks which could cause infinite recursion + // depending on how, say, stack-traces are implemented. if (DN_MemListUAFTracingEnabled_(mem)) - temp_mem.trace = DN_StackTraceWalk(&result, 256); + temp_mem.trace = DN_StackTraceFromAllocator(DN_AllocatorFromMemList(mem), 256); // NOTE: Create persistent temp mem and set it on the mem list result.uaf_guard_temp_mem = DN_MemListNewCopy(mem, DN_MemListTemp, &temp_mem); @@ -1348,52 +1378,11 @@ DN_API void DN_ArenaTempEnd(DN_Arena *arena, DN_ArenaReset reset) { #if DN_ARENA_TEMP_MEM_UAF_GUARD DN_AssertF(arena->uaf_guard_temp_mem, "Arena was not created with temp memory"); + DN_ArenaUAFCheck_(arena, DN_ArenaUAFCheckReportType_TempEndOutOfOrder); #else DN_AssertF(arena->temp_mem.mem, "Arena was not created with temp memory"); #endif -#if DN_ARENA_TEMP_MEM_UAF_GUARD - DN_MemList *mem = arena->mem; - if (mem->uaf_guard_active_id != arena->uaf_guard_id) { - // NOTE: MSVC does not recognise %'u which is a STB extension which causes a lot of incorrect - // format arguments warnings that we mute here. - DN_MSVC_WARNING_PUSH - DN_MSVC_WARNING_DISABLE(6271) // Extra argument passed to 'DN_Str8FromFmtArena' - DN_MSVC_WARNING_DISABLE(6067) // _Param_(10) in call to 'DN_LogPrint' must be the address of a string. Actual type: 'int'. - DN_MSVC_WARNING_DISABLE(6273) // Non-integer passed as _Param_(11) when an integer is required in call to 'DN_LogPrint' Actual type: 'char *'. - - // TODO: If this triggers, using the arena to format the error message is going to trigger the UAF check which is already failing. - DN_Str8 prefix = DN_Str8LineBreakStr8(DN_Str8Lit("The active temporary memory region recorded on the arena is " - "different from the current temporary memory region recorded on " - "the memory list allocator. This means that a temporary region " - "began but was not ended after the region was completed. Temporary " - "memory regions are enforced in a first-in-last-out manner (FILO) " - "to ensure the developer's intent of what the temporary region " - "spans is logically consistent and always strictly ends and begins " - "within a known lifetime."), - 100, - arena); - - if (DN_MemListUAFTracingEnabled_(mem)) { - DN_Str8 curr_stack_trace = DN_Str8PadNewLines(DN_StackTraceWalkResultToStr8(arena, &arena->uaf_guard_temp_mem->trace, 1), DN_Str8Lit(" "), arena); - DN_Str8 active_stack_trace = DN_Str8PadNewLines(DN_StackTraceWalkResultToStr8(arena, &mem->uaf_guard_active_temp_mem->trace, 1), DN_Str8Lit(" "), arena); - DN_AssertF(mem->uaf_guard_active_id == arena->uaf_guard_id, - "%.*s\n\nThe originating temporary memory region (id: %'u) was created at:" - "\n\n %.*s\n\nThe active temporary memory region (id: %'u) was created at:\n\n %.*s", - DN_Str8PrintFmt(prefix), - arena->uaf_guard_id, - DN_Str8PrintFmt(curr_stack_trace), - mem->uaf_guard_active_id, - DN_Str8PrintFmt(active_stack_trace)); - } else { - DN_Str8 suffix = DN_Str8LineBreakStr8(DN_MEM_LIST_UAF_TRACING_DISABLED_MORE_INFO_STR8_, 100, arena); - DN_AssertF(mem->uaf_guard_active_id == arena->uaf_guard_id, "%.*s%.*s", DN_Str8PrintFmt(prefix), DN_Str8PrintFmt(suffix)); - } - DN_MSVC_WARNING_POP - DN_Assert(arena->mem->uaf_guard_active_id == arena->uaf_guard_id); - } -#endif - if (reset == DN_ArenaReset_Yes) { #if DN_ARENA_TEMP_MEM_UAF_GUARD DN_MemListTempEnd(*arena->uaf_guard_temp_mem); @@ -1403,6 +1392,7 @@ DN_API void DN_ArenaTempEnd(DN_Arena *arena, DN_ArenaReset reset) } #if DN_ARENA_TEMP_MEM_UAF_GUARD + DN_MemList *mem = arena->mem; mem->uaf_guard_active_id = arena->uaf_guard_prev_id; mem->uaf_guard_active_temp_mem = arena->uaf_guard_prev_temp_mem; @@ -1414,21 +1404,21 @@ DN_API void DN_ArenaTempEnd(DN_Arena *arena, DN_ArenaReset reset) DN_API void *DN_ArenaAlloc(DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZMem z_mem) { - DN_ArenaUAFCheck(arena); + DN_ArenaUAFCheck_(arena, DN_ArenaUAFCheckReportType_AllocViolation); void *result = DN_MemListAlloc(arena->mem, size, align, z_mem); return result; } DN_API void *DN_ArenaAllocContiguous(DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZMem z_mem) { - DN_ArenaUAFCheck(arena); + DN_ArenaUAFCheck_(arena, DN_ArenaUAFCheckReportType_AllocViolation); void *result = DN_MemListAllocContiguous(arena->mem, size, align, z_mem); return result; } DN_API void *DN_ArenaCopy(DN_Arena *arena, void const *data, DN_U64 size, uint8_t align) { - DN_ArenaUAFCheck(arena); + DN_ArenaUAFCheck_(arena, DN_ArenaUAFCheckReportType_AllocViolation); void *result = DN_MemListCopy(arena->mem, data, size, align); return result; } @@ -1570,7 +1560,7 @@ DN_API DN_ErrSink* DN_ErrSinkBegin_(DN_ErrSink *err, DN_ErrSinkMode mode, DN_Cal DN_Str8Builder builder = DN_Str8BuilderFromArena(err->arena); for (DN_ForItSize(it, DN_ErrSinkNode, err->stack, err->stack_size)) DN_Str8BuilderAppendF(&builder, " [%04zu] %.*s:%u %.*s\n", it.index, DN_Str8PrintFmt(it.data->call_site.file), it.data->call_site.line, DN_Str8PrintFmt(it.data->call_site.function)); - DN_Str8 msg = DN_Str8BuilderBuild(&builder, err->arena); + DN_Str8 msg = DN_Str8FromStr8BuilderArena(&builder, err->arena); DN_AssertF(err->stack_size < DN_ArrayCountU(err->stack), "Error sink has run out of error scopes, potential leak. Scopes were\n%.*s", DN_Str8PrintFmt(msg)); } @@ -1677,7 +1667,7 @@ DN_API DN_Str8 DN_ErrSinkEndStr8(DN_Arena *arena, DN_ErrSink *err) err->stack_size--; DN_MemListPopTo(err->arena->mem, node->arena_pos); - result = DN_Str8BuilderBuild(&builder, arena); + result = DN_Str8FromStr8BuilderArena(&builder, arena); return result; } @@ -1711,7 +1701,7 @@ DN_API bool DN_ErrSinkEndLogError_(DN_ErrSink *err, DN_CallSite call_site, DN_St } // NOTE: Log the error - DN_Str8 log = DN_Str8BuilderBuild(&builder, err->arena); + DN_Str8 log = DN_Str8FromStr8BuilderArena(&builder, err->arena); DN_LogPrint(DN_LogTypeParamFromType(DN_LogType_Error), call_site, DN_LogFlags_Nil, "%.*s", DN_Str8PrintFmt(log)); if (node->mode == DN_ErrSinkMode_DebugBreakOnErrorLog) @@ -1786,43 +1776,52 @@ DN_API void DN_ErrSinkAppendF_(DN_ErrSink *err, DN_U32 error_code, DN_CallSite c DN_THREAD_LOCAL DN_TCCore *g_dn_thread_context; -DN_API void DN_TCInit(DN_TCCore *tc, DN_U64 thread_id, DN_Arena *main_arena, DN_Arena *temp_a_arena, DN_Arena *temp_b_arena, DN_Arena *err_sink_arena) +DN_API void DN_TCInit(DN_TCCore *tc, DN_U64 thread_id, DN_Arena *main_arena, DN_Arena *temp_arenas, DN_USize temp_arenas_count, DN_Arena *err_sink_arena) { tc->thread_id = thread_id; tc->main_arena = main_arena; tc->main_pool = DN_PoolFromArena(tc->main_arena, 0); - tc->temp_a_arena = temp_a_arena; - tc->temp_b_arena = temp_b_arena; tc->err_sink.arena = err_sink_arena; + DN_Assert(temp_arenas_count < DN_ArrayCountU(tc->temp_arenas)); + for (DN_ForIndexU(index, temp_arenas_count)) + tc->temp_arenas[tc->temp_arenas_count++] = temp_arenas + index; } -DN_API void DN_TCInitFromMemFuncs(DN_TCCore *tc, DN_U64 thread_id, DN_TCInitArgs *args, DN_MemFuncs mem_funcs) +DN_API DN_TCInitArgs DN_TCInitArgsDefault() { - DN_U64 main_reserve = (args && args->main_reserve) ? args->main_reserve : DN_Kilobytes(64); - DN_U64 main_commit = (args && args->main_commit) ? args->main_commit : DN_Kilobytes(4); - DN_U64 temp_reserve = (args && args->temp_reserve) ? args->temp_reserve : DN_Kilobytes(64); - DN_U64 temp_commit = (args && args->temp_commit) ? args->temp_commit : DN_Kilobytes(4); - DN_U64 err_sink_reserve = (args && args->err_sink_reserve) ? args->err_sink_reserve : DN_Kilobytes(64); - DN_U64 err_sink_commit = (args && args->err_sink_commit) ? args->err_sink_commit : DN_Kilobytes(4); + DN_TCInitArgs result = {}; + result.main_reserve = DN_Kilobytes(64); + result.main_commit = DN_Kilobytes(4); + result.temp_reserve = DN_Kilobytes(64); + result.temp_commit = DN_Kilobytes(4); + result.temp_count = 2; + result.err_sink_reserve = DN_Kilobytes(64); + result.err_sink_commit = DN_Kilobytes(4); + return result; +} - tc->main_arena_mem_ = DN_MemListFromMemFuncs(main_reserve, main_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, mem_funcs); - tc->temp_a_arena_mem_ = DN_MemListFromMemFuncs(temp_reserve, temp_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, mem_funcs); - tc->temp_b_arena_mem_ = DN_MemListFromMemFuncs(temp_reserve, temp_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, mem_funcs); - tc->err_sink_arena_mem_ = DN_MemListFromMemFuncs(err_sink_reserve, err_sink_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, mem_funcs); +DN_API void DN_TCInitFromMemFuncs(DN_TCCore *tc, DN_U64 thread_id, DN_TCInitArgs args, DN_MemFuncs mem_funcs) +{ + DN_Assert(args.temp_count <= DN_ArrayCountU(tc->temp_arenas)); + tc->main_arena_mem_ = DN_MemListFromMemFuncs(args.main_reserve, args.main_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, mem_funcs); + for (DN_ForIndexU(index, args.temp_count)) { + tc->temp_arena_mems_[index] = DN_MemListFromMemFuncs(args.temp_reserve, args.temp_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, mem_funcs); + tc->temp_arenas_[index] = DN_ArenaFromMemList(&tc->temp_arena_mems_[index]); + } + tc->err_sink_arena_mem_ = DN_MemListFromMemFuncs(args.err_sink_reserve, args.err_sink_commit, DN_MemFlags_AllocCanLeak | DN_MemFlags_NoAllocTrack, mem_funcs); tc->main_arena_ = DN_ArenaFromMemList(&tc->main_arena_mem_); - tc->temp_a_arena_ = DN_ArenaFromMemList(&tc->temp_a_arena_mem_); - tc->temp_b_arena_ = DN_ArenaFromMemList(&tc->temp_b_arena_mem_); tc->err_sink_arena_ = DN_ArenaFromMemList(&tc->err_sink_arena_mem_); - - DN_TCInit(tc, thread_id, &tc->main_arena_, &tc->temp_a_arena_, &tc->temp_b_arena_, &tc->err_sink_arena_); + DN_TCInit(tc, thread_id, &tc->main_arena_, tc->temp_arenas_, args.temp_count, &tc->err_sink_arena_); } DN_API void DN_TCDeinit(DN_TCCore *tc, DN_TCDeinitArenas deinit_arenas) { if (deinit_arenas == DN_TCDeinitArenas_Yes) { DN_MemListDeinit(tc->main_arena->mem); - DN_MemListDeinit(tc->temp_a_arena->mem); - DN_MemListDeinit(tc->temp_b_arena->mem); + for (DN_ForIndexU(index, tc->temp_arenas_count)) { + DN_MemListDeinit(tc->temp_arenas[index]->mem); + DN_MemListDeinit(tc->temp_arenas[index]->mem); + } DN_MemListDeinit(tc->err_sink.arena->mem); } } @@ -1850,19 +1849,74 @@ DN_API DN_Arena *DN_TCMainArena() DN_API DN_Pool *DN_TCMainPool() { - DN_TCCore *tc = DN_TCGet(); - DN_Pool *result = &tc->main_pool; + DN_TCCore *tc = DN_TCGet(); + DN_Pool *result = &tc->main_pool; return result; } -DN_API DN_Arena DN_TCTempArena(DN_Arena **conflicts, DN_USize count) +DN_API DN_Arena DN_TCTempArenaAllocator(DN_Allocator *conflicts, DN_USize count) +{ + DN_MemList *conflict_mem_lists[8]; + DN_USize conflict_mem_lists_count = 0; + for (DN_ForItSize(it, DN_Allocator, conflicts, count)) { + DN_Allocator *allocator = it.data; + if (!allocator->context) + continue; + + DN_MemList *mem_list = nullptr; + switch (allocator->type) { + case DN_AllocatorType_MemList: mem_list = DN_Cast(DN_MemList *)allocator->context; break; + + case DN_AllocatorType_Arena: { + DN_Arena *arena = DN_Cast(DN_Arena *) allocator->context; + mem_list = arena->mem; + } break; + + case DN_AllocatorType_Pool: { + DN_Pool *pool = DN_Cast(DN_Pool *) allocator->context; + mem_list = pool->arena ? pool->arena->mem : nullptr; + } break; + } + + if (!mem_list) + continue; + + void *added = DN_LArrayAppend(conflict_mem_lists, &conflict_mem_lists_count, mem_list); + DN_Assert(added); + } + + DN_TCCore *tc = DN_TCGet(); + DN_Arena result = {}; + for (DN_ForItSize(it, DN_Arena *, tc->temp_arenas, tc->temp_arenas_count)) { + bool is_usable = true; + DN_Arena *rhs_arena = *it.data; + DN_MemList *rhs_mem = rhs_arena->mem; + for (DN_ForItSize(conflict_it, DN_MemList*, conflict_mem_lists, conflict_mem_lists_count)) { + DN_MemList *lhs_mem = *conflict_it.data; + if (lhs_mem == rhs_mem) { + is_usable = false; + break; + } + } + + if (is_usable) { + result = DN_ArenaTempBeginFromMemList(rhs_mem); + break; + } + } + + DN_AssertF(result.mem, "All temp arenas are being used, there are none left to return to the caller"); + return result; +} + +DN_API DN_Arena DN_TCTempArenaFromArena(DN_Arena **conflicts, DN_USize count) { DN_TCCore *tc = DN_TCGet(); - DN_MemList *candidates[] = {tc->temp_a_arena->mem, tc->temp_b_arena->mem}; DN_Arena result = {}; - for (DN_ForItCArray(it, DN_MemList *, candidates)) { + for (DN_ForItSize(it, DN_Arena *, tc->temp_arenas, tc->temp_arenas_count)) { bool is_usable = true; - DN_MemList *rhs_mem = *it.data; + DN_Arena *rhs_arena = *it.data; + DN_MemList *rhs_mem = rhs_arena->mem; for (DN_ForItSize(conflict_it, DN_Arena *, conflicts, count)) { DN_Arena *lhs_arena = *conflict_it.data; DN_MemList *lhs_mem = lhs_arena->mem; @@ -1885,7 +1939,7 @@ DN_API DN_Arena DN_TCTempArena(DN_Arena **conflicts, DN_USize count) #if defined(__cplusplus) DN_TCScratchCpp::DN_TCScratchCpp(DN_Arena **conflicts, DN_USize count) { - this->data = DN_TCScratchBegin(conflicts, count); + this->data = DN_TCScratchBeginArena(conflicts, count); } DN_TCScratchCpp::~DN_TCScratchCpp() @@ -1894,10 +1948,17 @@ DN_TCScratchCpp::~DN_TCScratchCpp() } #endif -DN_API DN_TCScratch DN_TCScratchBegin(DN_Arena **conflicts, DN_USize count) +DN_API DN_TCScratch DN_TCScratchBeginAllocator(DN_Allocator *conflicts, DN_USize count) { DN_TCScratch result = {}; - result.arena = DN_TCTempArena(conflicts, count); + result.arena = DN_TCTempArenaAllocator(conflicts, count); + return result; +} + +DN_API DN_TCScratch DN_TCScratchBeginArena(DN_Arena **conflicts, DN_USize count) +{ + DN_TCScratch result = {}; + result.arena = DN_TCTempArenaFromArena(conflicts, count); return result; } @@ -2276,6 +2337,43 @@ DN_API DN_U8x32FromResult DN_U8x32FromDecimalStr8(DN_Str8 decimal) return result; } +DN_API DN_Allocator DN_AllocatorFromMemList(DN_MemList *mem) +{ + DN_Allocator result = {}; + result.type = DN_AllocatorType_MemList; + result.context = mem; + return result; +} + +DN_API DN_Allocator DN_AllocatorFromArena(DN_Arena *arena) +{ + DN_Allocator result = {}; + result.type = DN_AllocatorType_Arena; + result.context = arena; + return result; +} + +DN_API DN_Allocator DN_AllocatorFromPool(DN_Pool *pool) +{ + DN_Allocator result = {}; + result.type = DN_AllocatorType_Pool; + result.context = pool; + return result; +} + +DN_API void *DN_AllocatorAlloc(DN_Allocator allocator, DN_USize size, DN_U8 align, DN_ZMem z_mem) +{ + void *result = nullptr; + if (allocator.context) { + switch (allocator.type) { + case DN_AllocatorType_Arena: result = DN_ArenaAlloc (DN_Cast(DN_Arena *) allocator.context, size + 1, align, z_mem); break; + case DN_AllocatorType_Pool: result = DN_PoolAlloc (DN_Cast(DN_Pool *) allocator.context, size + 1); break; + case DN_AllocatorType_MemList: result = DN_MemListAlloc(DN_Cast(DN_MemList *) allocator.context, size + 1, align, z_mem); break; + } + } + return result; +} + DN_API DN_FmtAppendResult DN_FmtVAppend(char *buf, DN_USize *buf_size, DN_USize buf_max, char const *fmt, va_list args) { DN_FmtAppendResult result = {}; @@ -2344,23 +2442,26 @@ DN_API DN_USize DN_CStr16Size(wchar_t const *src) return result; } -DN_API DN_Str8 DN_Str8AllocArena(DN_USize size, DN_ZMem z_mem, DN_Arena *arena) +DN_API DN_Str8 DN_Str8AllocAllocator(DN_USize size, DN_ZMem z_mem, DN_Allocator allocator) { DN_Str8 result = {}; - result.data = DN_ArenaNewArray(arena, char, size + 1, z_mem); - if (result.data) - result.size = size; - result.data[result.size] = 0; + result.data = DN_Cast(char *) DN_AllocatorAlloc(allocator, size + 1, alignof(char), z_mem); + if (result.data) { + result.size = size; + result.data[result.size] = 0; + } + return result; +} + +DN_API DN_Str8 DN_Str8AllocArena(DN_USize size, DN_ZMem z_mem, DN_Arena *arena) +{ + DN_Str8 result = DN_Str8AllocAllocator(size, z_mem, DN_AllocatorFromArena(arena)); return result; } DN_API DN_Str8 DN_Str8AllocPool(DN_USize size, DN_Pool *pool) { - DN_Str8 result = {}; - result.data = DN_PoolNewArray(pool, char, size + 1); - if (result.data) - result.size = size; - result.data[result.size] = 0; + DN_Str8 result = DN_Str8AllocAllocator(size, DN_ZMem_No, DN_AllocatorFromPool(pool)); return result; } @@ -2394,10 +2495,10 @@ DN_API DN_Str8 DN_Str8FromPtrPool(void const *data, DN_USize size, DN_Pool *pool return result; } -DN_API DN_Str8 DN_Str8FromStr8Arena(DN_Str8 string, DN_Arena *arena) +DN_API DN_Str8 DN_Str8FromStr8Allocator(DN_Str8 string, DN_Allocator allocator) { DN_Str8 result = {}; - result.data = DN_Cast(char *) DN_ArenaAlloc(arena, string.size + 1, alignof(char), DN_ZMem_No); + result.data = DN_Cast(char *) DN_AllocatorAlloc(allocator, string.size + 1, alignof(char), DN_ZMem_No); if (result.data) { DN_Memcpy(result.data, string.data, string.size); result.data[string.size] = 0; @@ -2406,27 +2507,42 @@ DN_API DN_Str8 DN_Str8FromStr8Arena(DN_Str8 string, DN_Arena *arena) return result; } +DN_API DN_Str8 DN_Str8FromStr8Arena(DN_Str8 string, DN_Arena *arena) +{ + DN_Str8 result = DN_Str8FromStr8Allocator(string, DN_AllocatorFromArena(arena)); + return result; +} + DN_API DN_Str8 DN_Str8FromStr8Pool(DN_Str8 string, DN_Pool *pool) { - DN_Str8 result = {}; - result.data = DN_Cast(char *) DN_PoolAlloc(pool, string.size + 1); + DN_Str8 result = DN_Str8FromStr8Allocator(string, DN_AllocatorFromPool(pool)); + return result; +} + +DN_API DN_Str8 DN_Str8FromFmtVAllocator(DN_Allocator allocator, DN_FMT_ATTRIB char const *fmt, va_list args) +{ + DN_USize size = DN_FmtVSize(fmt, args); + DN_Str8 result = DN_Str8AllocAllocator(size, DN_ZMem_No, allocator); if (result.data) { - DN_Memcpy(result.data, string.data, string.size); - result.data[string.size] = 0; - result.size = string.size; + DN_USize written = 0; + DN_FmtVAppend(result.data, &written, result.size + 1, fmt, args); + DN_Assert(written == result.size); } return result; } DN_API DN_Str8 DN_Str8FromFmtVArena(DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, va_list args) { - DN_USize size = DN_FmtVSize(fmt, args); - DN_Str8 result = DN_Str8AllocArena(size, DN_ZMem_No, arena); - if (result.data) { - DN_USize written = 0; - DN_FmtVAppend(result.data, &written, result.size + 1, fmt, args); - DN_Assert(written == result.size); - } + DN_Str8 result = DN_Str8FromFmtVAllocator(DN_AllocatorFromArena(arena), fmt, args); + return result; +} + +DN_API DN_Str8 DN_Str8FromFmtAllocator(DN_Allocator allocator, DN_FMT_ATTRIB char const *fmt, ...) +{ + va_list va; + va_start(va, fmt); + DN_Str8 result = DN_Str8FromFmtVAllocator(allocator, fmt, va); + va_end(va); return result; } @@ -2441,13 +2557,7 @@ DN_API DN_Str8 DN_Str8FromFmtArena(DN_Arena *arena, DN_FMT_ATTRIB char const *fm DN_API DN_Str8 DN_Str8FromFmtVPool(DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args) { - DN_USize size = DN_FmtVSize(fmt, args); - DN_Str8 result = DN_Str8AllocPool(size, pool); - if (result.data) { - DN_USize written = 0; - DN_FmtVAppend(result.data, &written, result.size + 1, fmt, args); - DN_Assert(written == result.size); - } + DN_Str8 result = DN_Str8FromFmtVAllocator(DN_AllocatorFromPool(pool), fmt, args); return result; } @@ -3266,7 +3376,7 @@ DN_API DN_Str8 DN_Str8Replace(DN_Str8 string, return result; } - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_Str8Builder string_builder = DN_Str8BuilderFromArena(&scratch.arena); DN_USize max = string.size - find.size; DN_USize head = start_index; @@ -3298,7 +3408,7 @@ DN_API DN_Str8 DN_Str8Replace(DN_Str8 string, } else { DN_Str8 remainder = DN_Str8FromPtr(string.data + head, string.size - head); DN_Str8BuilderAppendRef(&string_builder, remainder); - result = DN_Str8BuilderBuild(&string_builder, arena); + result = DN_Str8FromStr8BuilderArena(&string_builder, arena); } DN_TCScratchEnd(&scratch); return result; @@ -3316,9 +3426,9 @@ DN_API DN_Str8 DN_Str8ReplaceInsensitive(DN_Str8 string, DN_Str8 find, DN_Str8 r return result; } -DN_API DN_Str8 DN_Str8PadNewLines(DN_Str8 string, DN_Str8 pad_string, DN_Arena *arena) +DN_API DN_Str8 DN_Str8PadNewLinesAllocator(DN_Str8 string, DN_Str8 pad_string, DN_Allocator allocator) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginAllocator(&allocator, 1); DN_Str8Builder builder = DN_Str8BuilderFromArena(&scratch.arena); DN_Str8 it = string; while (it.size) { @@ -3327,11 +3437,17 @@ DN_API DN_Str8 DN_Str8PadNewLines(DN_Str8 string, DN_Str8 pad_string, DN_Arena * it = split.rhs; } - DN_Str8 result = DN_Str8BuilderBuildDelimited(&builder, pad_string, arena); + DN_Str8 result = DN_Str8FromStr8BuilderDelimitAllocator(&builder, pad_string, allocator); DN_TCScratchEnd(&scratch); return result; } +DN_API DN_Str8 DN_Str8PadNewLinesArena(DN_Str8 string, DN_Str8 pad_string, DN_Arena *arena) +{ + DN_Str8 result = DN_Str8PadNewLinesAllocator(string, pad_string, DN_AllocatorFromArena(arena)); + return result; +} + DN_API DN_USize DN_USizeCodepointCountFromUTF8(DN_Str8 str, DN_CodepointCountFlags flags) { DN_USize result = 0; @@ -3373,73 +3489,88 @@ DN_API DN_USize DN_USizeCodepointCountFromUTF8(DN_Str8 str, DN_CodepointCountFla return result; } -DN_API DN_Str8 DN_Str8LineBreakStr8(DN_Str8 src, DN_USize desired_width, DN_Arena *arena) +DN_API DN_Str8 DN_Str8LineBreakAllocator(DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Allocator allocator) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginAllocator(&allocator, 1); DN_Str8Builder builder = DN_Str8BuilderFromArena(&scratch.arena); - char* start = src.data; - char* end = src.data; - DN_Str8 it = src; - while (it.size) { - DN_Str8 splitters[] = {DN_Str8Lit(" "), DN_Str8Lit("\n")}; - DN_Str8BSplitResult split = DN_Str8BSplitArray(it, splitters, DN_ArrayCountU(splitters)); - DN_USize curr_line_length = end - start; + if (mode == DN_Str8LineBreakMode_AtWord) { + char* start = src.data; + char* end = src.data; + DN_Str8 it = src; + while (it.size) { + DN_Str8 splitters[] = {DN_Str8Lit(" "), DN_Str8Lit("\n")}; + DN_Str8BSplitResult split = DN_Str8BSplitArray(it, splitters, DN_ArrayCountU(splitters)); + DN_USize curr_line_length = end - start; - // Handle explicit newlines in input - if (split.input_index == 1 /*the newline*/) { - if (curr_line_length == 0 && split.lhs.size) + // Handle explicit newlines in input + if (split.input_index == 1 /*the newline*/) { + if (curr_line_length == 0 && split.lhs.size) + start = split.lhs.data; + if (split.lhs.size) + end = DN_Str8End(split.lhs); + DN_Str8BuilderAppendRef(&builder, DN_Str8FromPtr(start, end - start)); + start = split.rhs.data; + end = split.rhs.data; + it = split.rhs; + continue; + } + + // Skip empty segments (multiple spaces, leading/trailing spaces) + if (split.lhs.size == 0) { + it = split.rhs; + continue; + } + + // First word on this line + if (curr_line_length == 0) { start = split.lhs.data; - if (split.lhs.size) + end = DN_Str8End(split.lhs); + it = split.rhs; + continue; + } + + // Check if adding this word (plus separator space) would overflow + DN_USize combined_length = curr_line_length + 1 + split.lhs.size; + if (combined_length > desired_width) { + // Commit current line, start new line with current word + DN_Str8BuilderAppendRef(&builder, DN_Str8FromPtr(start, end - start)); + start = split.lhs.data; + end = DN_Str8End(split.lhs); + it = split.rhs; + } else { + // Add word to current line end = DN_Str8End(split.lhs); + it = split.rhs; + } + } + + // Append final line + if (end > start) DN_Str8BuilderAppendRef(&builder, DN_Str8FromPtr(start, end - start)); - start = split.rhs.data; - end = split.rhs.data; - it = split.rhs; - continue; - } - - // Skip empty segments (multiple spaces, leading/trailing spaces) - if (split.lhs.size == 0) { - it = split.rhs; - continue; - } - - // First word on this line - if (curr_line_length == 0) { - start = split.lhs.data; - end = DN_Str8End(split.lhs); - it = split.rhs; - continue; - } - - // Check if adding this word (plus separator space) would overflow - DN_USize combined_length = curr_line_length + 1 + split.lhs.size; - if (combined_length > desired_width) { - // Commit current line, start new line with current word - DN_Str8BuilderAppendRef(&builder, DN_Str8FromPtr(start, end - start)); - start = split.lhs.data; - end = DN_Str8End(split.lhs); - it = split.rhs; - } else { - // Add word to current line - end = DN_Str8End(split.lhs); - it = split.rhs; + } else { + DN_Str8 it = src; + while (it.size) { + DN_Str8 chunk = DN_Str8Subset(it, 0, desired_width); + DN_Str8BuilderAppendRef(&builder, chunk); + it = DN_Str8Advance(it, desired_width); } } - // Append final line - if (end > start) - DN_Str8BuilderAppendRef(&builder, DN_Str8FromPtr(start, end - start)); - - DN_Str8 result = DN_Str8BuilderBuildDelimited(&builder, DN_Str8Lit("\n"), arena); + DN_Str8 result = DN_Str8FromStr8BuilderDelimitAllocator(&builder, delimiter, allocator); DN_TCScratchEnd(&scratch); return result; } +DN_API DN_Str8 DN_Str8LineBreakArena(DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Arena *arena) +{ + DN_Str8 result = DN_Str8LineBreakAllocator(src, desired_width, delimiter, mode, DN_AllocatorFromArena(arena)); + return result; +} + DN_API DN_Str8 DN_Str8Table(DN_Str8 const *rows, DN_USize num_rows, DN_USize num_cols, DN_Str8TableFlags flags, DN_Arena *arena) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_U16 col_widths[128] = {}; for (DN_USize i = 0; i < num_cols; i++) { for (DN_USize j = 0; j < num_rows; j++) { @@ -3492,7 +3623,7 @@ DN_API DN_Str8 DN_Str8Table(DN_Str8 const *rows, DN_USize num_rows, DN_USize num DN_Str8BuilderAppendF(&builder, "+"); } - DN_Str8 result = DN_Str8BuilderBuild(&builder, arena); + DN_Str8 result = DN_Str8FromStr8BuilderArena(&builder, arena); DN_TCScratchEnd(&scratch); return result; } @@ -3534,6 +3665,99 @@ DN_API DN_Str8 DN_Str8RenderSpaceSep(DN_Str8Slice slice, DN_Arena *arena) return result; } +DN_API int DN_Str8CompareNatural(DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case) +{ + const char *lhs_it = lhs.data; + const char *rhs_it = rhs.data; + const char *lhs_end = lhs.data + lhs.size; + const char *rhs_end = rhs.data + rhs.size; + + while (lhs_it < lhs_end && rhs_it < rhs_end) { + // NOTE: Skip leading spaces + while (lhs_it < lhs_end && DN_CharIsWhitespace(*lhs_it)) + lhs_it++; + while (rhs_it < rhs_end && DN_CharIsWhitespace(*rhs_it)) + rhs_it++; + + if (lhs_it >= lhs_end || rhs_it >= rhs_end) + break; + + // NOTE: Check if current positions are digits + if (DN_CharIsDigit(*lhs_it) && DN_CharIsDigit(*rhs_it)) { + // NOTE: Extract full number from lhs + DN_U64 lhs_num = 0; + while (lhs_it < lhs_end && DN_CharIsDigit(*lhs_it)) { + lhs_num = lhs_num * 10 + (*lhs_it - '0'); + lhs_it++; + } + + // NOTE: Extract full number from rhs + DN_U64 rhs_num = 0; + while (rhs_it < rhs_end && DN_CharIsDigit(*rhs_it)) { + rhs_num = rhs_num * 10 + (*rhs_it - '0'); + rhs_it++; + } + + if (lhs_num != rhs_num) + return (lhs_num < rhs_num) ? -1 : 1; + } else { + // NOTE: Compare non-digit characters + char lhs_ch = *lhs_it; + char rhs_ch = *rhs_it; + + if (eq_case == DN_Str8EqCase_Insensitive) { + if (DN_CharIsAlphabet(lhs_ch)) + lhs_ch = DN_CharToLower(lhs_ch); + if (DN_CharIsAlphabet(rhs_ch)) + rhs_ch = DN_CharToLower(rhs_ch); + } + + if (lhs_ch != rhs_ch) + return (lhs_ch < rhs_ch) ? -1 : 1; + lhs_it++; + rhs_it++; + } + } + + // NOTE: One string is prefix of other; shorter comes first + if (lhs_it < lhs_end) + return 1; + if (rhs_it < rhs_end) + return -1; + return 0; +} + +DN_API int DN_Str8CompareLexicographic(DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case) +{ + const char *lhs_it = lhs.data; + const char *rhs_it = rhs.data; + const char *lhs_end = lhs.data + lhs.size; + const char *rhs_end = rhs.data + rhs.size; + + while (lhs_it < lhs_end && rhs_it < rhs_end) { + char lhs_ch = *lhs_it; + char rhs_ch = *rhs_it; + if (eq_case == DN_Str8EqCase_Insensitive) { + if (DN_CharIsAlphabet(lhs_ch)) + lhs_ch = DN_CharToLower(lhs_ch); + if (DN_CharIsAlphabet(rhs_ch)) + rhs_ch = DN_CharToLower(rhs_ch); + } + if (lhs_ch != rhs_ch) + return (lhs_ch < rhs_ch) ? -1 : 1; + lhs_it++; + rhs_it++; + } + + // NOTE: One string is prefix of other; shorter comes first + if (lhs.size < rhs.size) + return -1; + if (rhs.size < lhs.size) + return 1; + return 0; +} + + DN_API bool DN_Str16Eq(DN_Str16 lhs, DN_Str16 rhs) { if (lhs.size != rhs.size) @@ -3821,37 +4045,47 @@ DN_API bool DN_Str8BuilderErase(DN_Str8Builder *builder, DN_Str8 string) return false; } -DN_API DN_Str8 DN_Str8BuilderBuild(DN_Str8Builder const *builder, DN_Arena *arena) +DN_API DN_Str8 DN_Str8FromStr8BuilderAllocator(DN_Str8Builder const *builder, DN_Allocator allocator) { - DN_Str8 result = DN_Str8BuilderBuildDelimited(builder, DN_Str8Lit(""), arena); + DN_Str8 result = DN_Str8FromStr8BuilderDelimitAllocator(builder, DN_Str8Lit(""), allocator); return result; } -DN_API DN_Str8 DN_Str8BuilderBuildDelimited(DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Arena *arena) +DN_API DN_Str8 DN_Str8FromStr8BuilderArena(DN_Str8Builder const *builder, DN_Arena *arena) { - DN_Str8 result = DN_ZeroInit; + DN_Str8 result = DN_Str8FromStr8BuilderAllocator(builder, DN_AllocatorFromArena(arena)); + return result; +} + +DN_API DN_Str8 DN_Str8FromStr8BuilderDelimitAllocator(DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Allocator allocator) +{ + DN_Str8 result = {}; if (!builder || builder->string_size <= 0 || builder->count <= 0) return result; DN_USize size_for_delimiter = delimiter.size ? ((builder->count - 1) * delimiter.size) : 0; - result.data = DN_ArenaNewArray(arena, - char, - builder->string_size + size_for_delimiter + 1 /*null terminator*/, - DN_ZMem_No); + result = DN_Str8AllocAllocator(builder->string_size + size_for_delimiter, DN_ZMem_No, allocator); if (!result.data) return result; + DN_USize write_count = 0; 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; + DN_Memcpy(result.data + write_count, link->string.data, link->string.size); + write_count += link->string.size; if (link->next && delimiter.size) { - DN_Memcpy(result.data + result.size, delimiter.data, delimiter.size); - result.size += delimiter.size; + DN_Memcpy(result.data + write_count, delimiter.data, delimiter.size); + write_count += delimiter.size; } } - result.data[result.size] = 0; - DN_Assert(result.size == builder->string_size + size_for_delimiter); + result.data[write_count] = 0; + DN_Assert(write_count == builder->string_size + size_for_delimiter); + return result; +} + +DN_API DN_Str8 DN_Str8FromStr8BuilderDelimitArena(DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Arena *arena) +{ + DN_Str8 result = DN_Str8FromStr8BuilderDelimitAllocator(builder, delimiter, DN_AllocatorFromArena(arena)); return result; } @@ -4045,8 +4279,22 @@ DN_API DN_USize DN_BytesFromHex(DN_Str8 hex, void *dest, DN_USize dest_count) if (hex_trimmed.size > (dest_count * 2)) return result; - DN_U8 *ptr = DN_Cast(DN_U8 *) dest; - for (DN_USize index = 0; index < hex_trimmed.size; index += 2) { + DN_U8 *ptr = DN_Cast(DN_U8 *) dest; + DN_USize index = 0; + + // NOTE: We are given an odd-sized hex string e.g.: 'F' instead of '0F', we 'left-pad' the parser + // and support reading the single nibble as 'F' + if (hex_trimmed.size % 2 != 0) { + DN_U8 nibble0 = 0; + DN_U8 nibble1 = DN_U8FromHexNibble(hex_trimmed.data[index++]); + if (nibble1 == 0xFF) + return result; + *ptr++ = nibble0 << 4 | nibble1 << 0; + result++; + } + + // NOTE: Parse the rest of the hex which is in byte pairs + for (; index < hex_trimmed.size; index += 2) { DN_U8 nibble0 = DN_U8FromHexNibble(hex_trimmed.data[index + 0]); DN_U8 nibble1 = DN_U8FromHexNibble(hex_trimmed.data[index + 1]); if (nibble0 == 0xFF || nibble1 == 0xFF) @@ -4131,6 +4379,7 @@ DN_API DN_USize DN_HexFromPtrBytes(void const *bytes, DN_USize bytes_count, void DN_U8 const *src_u8 = DN_Cast(DN_U8 const *) bytes; DN_U8 *ptr = DN_Cast(DN_U8 *) hex; bool leading_zeros = true; + for (DN_USize index = 0; index < bytes_count; index++) { char ch = src_u8[index]; if (leading_zeros) @@ -4171,6 +4420,17 @@ DN_API DN_USize DN_HexFromStr8Bytes(DN_Str8 bytes, void *hex, DN_USize hex_count return result; } +DN_API DN_Str8 DN_HexFromStr8BytesArena(DN_Str8 bytes, DN_Arena *arena, DN_TrimLeadingZero trim_leading_z) +{ + DN_Str8 result = {}; + if (bytes.size) { + result.data = DN_ArenaNewArray(arena, char, bytes.size * 2, DN_ZMem_No); + if (result.data) + result.size = DN_HexFromStr8Bytes(bytes, result.data, bytes.size * 2, trim_leading_z); + } + return result; +} + DN_API DN_Hex32 DN_Hex32FromPtr16b(void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z) { DN_Hex32 result = {}; @@ -4388,40 +4648,40 @@ DN_API DN_U64 DN_UnixTimeMsFromDate(DN_Date date) return result; } -DN_API DN_Str8 DN_Str8FromByteCountType(DN_ByteCountType type) +DN_API DN_Str8 DN_Str8FromByteType(DN_ByteType type) { DN_Str8 result = DN_Str8Lit(""); switch (type) { - case DN_ByteCountType_B: result = DN_Str8Lit("B"); break; - case DN_ByteCountType_KiB: result = DN_Str8Lit("KiB"); break; - case DN_ByteCountType_MiB: result = DN_Str8Lit("MiB"); break; - case DN_ByteCountType_GiB: result = DN_Str8Lit("GiB"); break; - case DN_ByteCountType_TiB: result = DN_Str8Lit("TiB"); break; - case DN_ByteCountType_Count: result = DN_Str8Lit(""); break; - case DN_ByteCountType_Auto: result = DN_Str8Lit(""); break; + case DN_ByteType_B: result = DN_Str8Lit("B"); break; + case DN_ByteType_KiB: result = DN_Str8Lit("KiB"); break; + case DN_ByteType_MiB: result = DN_Str8Lit("MiB"); break; + case DN_ByteType_GiB: result = DN_Str8Lit("GiB"); break; + case DN_ByteType_TiB: result = DN_Str8Lit("TiB"); break; + case DN_ByteType_Count: result = DN_Str8Lit(""); break; + case DN_ByteType_Auto: result = DN_Str8Lit(""); break; } return result; } -DN_API DN_ByteCountResult DN_ByteCountFromType(DN_U64 bytes, DN_ByteCountType type) +DN_API DN_ByteCount DN_ByteCountFromU64(DN_U64 bytes, DN_ByteType type) { - DN_Assert(type != DN_ByteCountType_Count); - DN_ByteCountResult result = {}; + DN_Assert(type != DN_ByteType_Count); + DN_ByteCount result = {}; result.bytes = DN_Cast(DN_F64) bytes; - if (type == DN_ByteCountType_Auto) - for (; result.type < DN_ByteCountType_Count && result.bytes >= 1024.0; result.type = DN_Cast(DN_ByteCountType)(DN_Cast(DN_USize) result.type + 1)) + if (type == DN_ByteType_Auto) + for (; result.type < DN_ByteType_Count && result.bytes >= 1024.0; result.type = DN_Cast(DN_ByteType)(DN_Cast(DN_USize) result.type + 1)) result.bytes /= 1024.0; else - for (; result.type < type; result.type = DN_Cast(DN_ByteCountType)(DN_Cast(DN_USize) result.type + 1)) + for (; result.type < type; result.type = DN_Cast(DN_ByteType)(DN_Cast(DN_USize) result.type + 1)) result.bytes /= 1024.0; - result.suffix = DN_Str8FromByteCountType(result.type); + result.suffix = DN_Str8FromByteType(result.type); return result; } -DN_API DN_Str8x32 DN_ByteCountStr8x32FromType(DN_U64 bytes, DN_ByteCountType type) +DN_API DN_Str8x32 DN_Str8x32FromByteCountU64(DN_U64 bytes, DN_ByteType type) { - DN_ByteCountResult byte_count = DN_ByteCountFromType(bytes, type); - DN_Str8x32 result = DN_Str8x32FromFmt("%.2f%.*s", byte_count.bytes, DN_Str8PrintFmt(byte_count.suffix)); + DN_ByteCount byte_count = DN_ByteCountFromU64(bytes, type); + DN_Str8x32 result = DN_Str8x32FromFmt("%.2f%.*s", byte_count.bytes, DN_Str8PrintFmt(byte_count.suffix)); return result; } @@ -4484,6 +4744,9 @@ DN_API DN_ProfilerZone DN_ProfilerBeginZone(DN_Profiler *profiler, DN_Str8 name, if (!profiler || profiler->paused) return result; + if (anchor_index != 0) { + DN_AssertF(profiler->frame_zone.profiler, "DN_ProfilerNewFrame() must be called before calling BeginZone"); + } DN_Assert(anchor_index < profiler->anchors_per_frame); DN_ProfilerAnchor *anchor = DN_ProfilerFrameAnchors(profiler).data + anchor_index; anchor->name = name; @@ -4495,6 +4758,7 @@ DN_API DN_ProfilerZone DN_ProfilerBeginZone(DN_Profiler *profiler, DN_Str8 name, DN_AssertF(name == anchor->name, "Potentially overwriting a zone by accident? Anchor is '%.*s', name is '%.*s'", DN_Str8PrintFmt(anchor->name), DN_Str8PrintFmt(name)); #endif + result.profiler = profiler; result.begin_tsc = profiler->tsc_now ? profiler->tsc_now() : DN_CPUGetTSC(); result.anchor_index = anchor_index; result.parent_zone = profiler->parent_zone; @@ -4503,8 +4767,9 @@ DN_API DN_ProfilerZone DN_ProfilerBeginZone(DN_Profiler *profiler, DN_Str8 name, return result; } -DN_API void DN_ProfilerEndZone(DN_Profiler *profiler, DN_ProfilerZone zone) +DN_API void DN_ProfilerEndZone(DN_ProfilerZone zone) { + DN_Profiler *profiler = zone.profiler; if (!profiler || profiler->paused) return; @@ -4516,9 +4781,14 @@ DN_API void DN_ProfilerEndZone(DN_Profiler *profiler, DN_ProfilerZone zone) DN_U64 tsc_now = profiler->tsc_now ? profiler->tsc_now() : DN_CPUGetTSC(); DN_U64 elapsed_tsc = tsc_now - zone.begin_tsc; + // NOTE: We snap the elapsed TSC at the zone start and overwrite every time we end zones. If we + // nest zones, the nested zones will clobber the inclusive timestamp with their values. + // This is fine, as long as all the zones and begun and ended correctly, when the top-most zone + // in the stack ends, it will overwrite the TSC with the elapsed time overall for just that top + // most function, unclobbering the elapsed time sitting in the anchor. + anchor->tsc_inclusive = zone.elapsed_tsc_at_zone_start + elapsed_tsc; + anchor->tsc_exclusive += elapsed_tsc; anchor->hit_count++; - anchor->tsc_inclusive = zone.elapsed_tsc_at_zone_start + elapsed_tsc; - anchor->tsc_exclusive += elapsed_tsc; if (zone.parent_zone != zone.anchor_index) { DN_ProfilerAnchor *parent_anchor = array.data + zone.parent_zone; @@ -4533,7 +4803,7 @@ DN_API void DN_ProfilerNewFrame(DN_Profiler *profiler) return; // NOTE: End the frame's zone - DN_ProfilerEndZone(profiler, profiler->frame_zone); + DN_ProfilerEndZone(profiler->frame_zone); DN_ProfilerAnchorArray old_frame_anchors = DN_ProfilerFrameAnchors(profiler); DN_ProfilerAnchor old_frame_anchor = old_frame_anchors.data[0]; profiler->frame_avg_tsc = (profiler->frame_avg_tsc + old_frame_anchor.tsc_inclusive) / 2.f; @@ -4550,33 +4820,48 @@ DN_API void DN_ProfilerNewFrame(DN_Profiler *profiler) profiler->frame_zone = DN_ProfilerBeginZone(profiler, DN_Str8Lit("Profiler Frame"), 0); } -DN_API void DN_ProfilerDump(DN_Profiler *profiler) +DN_API DN_USize DN_ProfilerFmtAnchor(DN_ProfilerAnchor anchor, DN_U64 tsc_frequency, char *buffer, DN_USize count) +{ + DN_USize result = 0; + if (!anchor.hit_count) + return result; + + DN_U64 tsc_exclusive = anchor.tsc_exclusive; + DN_U64 tsc_inclusive = anchor.tsc_inclusive; + DN_F64 tsc_exclusive_milliseconds = tsc_exclusive * 1000 / DN_Cast(DN_F64) tsc_frequency; + if (tsc_exclusive == tsc_inclusive) { + DN_FmtAppend(buffer, &result, count, "%.*s[%u]: %.1fms", DN_Str8PrintFmt(anchor.name), anchor.hit_count, tsc_exclusive_milliseconds); + } else { + DN_F64 tsc_inclusive_milliseconds = tsc_inclusive * 1000 / DN_Cast(DN_F64) tsc_frequency; + DN_FmtAppend(buffer, &result, count, "%.*s[%u]: %.1f/%.1fms", DN_Str8PrintFmt(anchor.name), anchor.hit_count, tsc_exclusive_milliseconds, tsc_inclusive_milliseconds); + } + return result; +} + +DN_API DN_Str8 DN_ProfilerFmtAnchorStr8(DN_ProfilerAnchor anchor, DN_U64 tsc_frequency, DN_Arena *arena) +{ + DN_Str8 result = {}; + DN_USize size_req = DN_ProfilerFmtAnchor(anchor, tsc_frequency, nullptr, 0); + if (size_req) { + result = DN_Str8AllocArena(size_req, DN_ZMem_No, arena); + DN_ProfilerFmtAnchor(anchor, tsc_frequency, result.data, result.size + 1); + } + return result; +} + +DN_API void DN_ProfilerFmtToStdout(DN_Profiler *profiler) { if (!profiler || profiler->frame_index == 0) return; DN_USize frame_index = profiler->frame_index - 1; - DN_Assert(profiler->frame_index < profiler->anchors_per_frame); - DN_ProfilerAnchor *anchors = profiler->anchors + (frame_index * profiler->anchors_per_frame); for (DN_USize index = 1; index < profiler->anchors_per_frame; index++) { - DN_ProfilerAnchor const *anchor = anchors + index; - if (!anchor->hit_count) - continue; - - DN_U64 tsc_exclusive = anchor->tsc_exclusive; - DN_U64 tsc_inclusive = anchor->tsc_inclusive; - DN_F64 tsc_exclusive_milliseconds = tsc_exclusive * 1000 / DN_Cast(DN_F64) profiler->tsc_frequency; - if (tsc_exclusive == tsc_inclusive) { - DN_OS_PrintOutLnF("%.*s[%u]: %.1fms", DN_Str8PrintFmt(anchor->name), anchor->hit_count, tsc_exclusive_milliseconds); - } else { - DN_F64 tsc_inclusive_milliseconds = tsc_inclusive * 1000 / DN_Cast(DN_F64) profiler->tsc_frequency; - DN_OS_PrintOutLnF("%.*s[%u]: %.1f/%.1fms", - DN_Str8PrintFmt(anchor->name), - anchor->hit_count, - tsc_exclusive_milliseconds, - tsc_inclusive_milliseconds); - } + char buffer[2048]; + buffer[0] = 0; + DN_USize fmt_len = DN_ProfilerFmtAnchor(anchors[index], profiler->tsc_frequency, buffer, DN_ArrayCountU(buffer)); + DN_Str8 msg = DN_Str8FromPtr(buffer, fmt_len); + DN_OS_PrintOutLnF("%.*s", DN_Str8PrintFmt(msg)); } } @@ -4592,6 +4877,204 @@ DN_API DN_F64 DN_ProfilerMsFromTSC(DN_Profiler *profiler, DN_U64 duration_tsc) return result; } +static void DN_QSortSetElem_(void *array, DN_USize elem_size, DN_USize dest_index, DN_USize src_index) +{ + char *src = DN_Cast(char *) array + (src_index * elem_size); + char *dest = DN_Cast(char *) array + (dest_index * elem_size); + DN_Memcpy(dest, src, elem_size); +} + +static void DN_QSortSwapElems_(void *array, DN_USize elem_size, DN_USize lhs_index, DN_USize rhs_index) +{ + if (lhs_index == rhs_index) + return; + + char temp_buffer[512]; + bool use_buffer = elem_size <= DN_ArrayCountU(temp_buffer); + DN_TCScratch scratch = {}; + char *temp = {}; + if (use_buffer) { + temp = temp_buffer; + } else { + scratch = DN_TCScratchBeginArena(nullptr, 0); + temp = DN_ArenaNewArray(&scratch.arena, char, elem_size, DN_ZMem_No); + } + + char *lhs = DN_Cast(char *) array + (lhs_index * elem_size); + char *rhs = DN_Cast(char *) array + (rhs_index * elem_size); + DN_Memcpy(temp, lhs, elem_size); + DN_Memcpy(lhs, rhs, elem_size); + DN_Memcpy(rhs, temp, elem_size); + + if (!use_buffer) + DN_TCScratchEnd(&scratch); +} + +static void DN_QSortInsertion_(void *array, DN_USize array_size, DN_USize elem_size, void *user_context, DN_QSortCompareFunc *compare) +{ + char temp_buffer[512]; + bool use_buffer = elem_size <= DN_ArrayCountU(temp_buffer); + DN_TCScratch scratch = {}; + char *temp = {}; + if (use_buffer) { + temp = temp_buffer; + } else { + scratch = DN_TCScratchBeginArena(nullptr, 0); + temp = DN_ArenaNewArray(&scratch.arena, char, elem_size, DN_ZMem_No); + } + + DN_U8 *array_u8 = DN_Cast(DN_U8 *)array; + for (DN_USize item_to_insert_index = 1; item_to_insert_index < array_size; item_to_insert_index++) { + for (DN_USize index = 0; index < item_to_insert_index; index++) { + DN_U8 *lhs = array_u8 + (index * elem_size); + DN_U8 *rhs = array_u8 + (item_to_insert_index * elem_size); + if (compare(lhs, rhs, user_context)) + continue; + + DN_Memcpy(temp, rhs, elem_size); + for (DN_USize i = item_to_insert_index; i > index; i--) + DN_QSortSetElem_(array, elem_size, i, i - 1); + DN_Memcpy(lhs, temp, elem_size); + break; + } + } + + if (!use_buffer) + DN_TCScratchEnd(&scratch); +} + +DN_API void DN_QSort(void *array, DN_USize array_size, DN_USize elem_size, void *user_context, DN_QSortCompareFunc *compare) +{ + if (!array || array_size <= 1 || elem_size == 0 || !compare) + return; + + // NOTE: Insertion Sort, under 24->32 is an optimal amount + DN_U8 *array_u8 = DN_Cast(DN_U8 *)array; + DN_USize const QSORT_THRESHOLD = 24; + if (array_size < QSORT_THRESHOLD) { + DN_QSortInsertion_(array, array_size, elem_size, user_context, compare); + return; + } + + // NOTE: Quick sort, under 24->32 is an optimal amount + DN_USize last_index = array_size - 1; + DN_USize pivot_index = array_size / 2; + DN_USize partition_index = 0; + DN_USize start_index = 0; + + // Swap pivot with last index, so pivot is always at the end of the array. + // This makes logic much simpler. + DN_QSortSwapElems_(array, elem_size, last_index, pivot_index); + pivot_index = last_index; + + // 4^, 8, 7, 5, 2, 3, 6 + if (compare(array_u8 + (start_index * elem_size), array_u8 + (pivot_index * elem_size), user_context)) + partition_index++; + start_index++; + + // 4, |8, 7, 5^, 2, 3, 6* + // 4, 5, |7, 8, 2^, 3, 6* + // 4, 5, 2, |8, 7, ^3, 6* + // 4, 5, 2, 3, |7, 8, ^6* + for (DN_USize index = start_index; index < last_index; index++) { + if (compare(array_u8 + (index * elem_size), array_u8 + (pivot_index * elem_size), user_context)) { + DN_QSortSwapElems_(array, elem_size, partition_index, index); + partition_index++; + } + } + + // Move pivot to right of partition + // 4, 5, 2, 3, |6, 8, ^7* + DN_QSortSwapElems_(array, elem_size, partition_index, pivot_index); + DN_QSort(array_u8, partition_index, elem_size, user_context, compare); + + // Skip the value at partion index since that is guaranteed to be sorted. + // 4, 5, 2, 3, (x), 8, 7 + DN_USize one_after_partition_index = partition_index + 1; + DN_QSort(array_u8 + (one_after_partition_index * elem_size), (array_size - one_after_partition_index), elem_size, user_context, compare); +} + +DN_API bool DN_QSortCompareStr8NaturalAsc(void const* lhs, void const *rhs, void *user_context) +{ + DN_Str8EqCase eq_case = *DN_Cast(DN_Str8EqCase *) user_context; + DN_Str8 lhs_str8 = *DN_Cast(DN_Str8 *) lhs; + DN_Str8 rhs_str8 = *DN_Cast(DN_Str8 *) rhs; + bool result = DN_Str8CompareNatural(lhs_str8, rhs_str8, eq_case) < 0; + return result; +} + +DN_API bool DN_QSortCompareStr8NaturalDesc(void const* lhs, void const *rhs, void *user_context) +{ + DN_Str8EqCase eq_case = *DN_Cast(DN_Str8EqCase *) user_context; + DN_Str8 lhs_str8 = *DN_Cast(DN_Str8 *) lhs; + DN_Str8 rhs_str8 = *DN_Cast(DN_Str8 *) rhs; + bool result = DN_Str8CompareNatural(lhs_str8, rhs_str8, eq_case) > 0; + return result; +} + +DN_API bool DN_QSortCompareStr8LexicographicAsc(void const* lhs, void const *rhs, void *user_context) +{ + DN_Str8EqCase eq_case = *DN_Cast(DN_Str8EqCase *) user_context; + DN_Str8 lhs_str8 = *DN_Cast(DN_Str8 *) lhs; + DN_Str8 rhs_str8 = *DN_Cast(DN_Str8 *) rhs; + bool result = DN_Str8CompareLexicographic(lhs_str8, rhs_str8, eq_case) < 0; + return result; +} + +DN_API bool DN_QSortCompareStr8LexicographicDesc(void const* lhs, void const *rhs, void *user_context) +{ + DN_Str8EqCase eq_case = *DN_Cast(DN_Str8EqCase *) user_context; + DN_Str8 lhs_str8 = *DN_Cast(DN_Str8 *) lhs; + DN_Str8 rhs_str8 = *DN_Cast(DN_Str8 *) rhs; + bool result = DN_Str8CompareLexicographic(lhs_str8, rhs_str8, eq_case) > 0; + return result; +} + +DN_API bool DN_QSortCompareBytesLT(void const* lhs, void const *rhs, void *user_context) +{ + DN_USize elem_size = *DN_Cast(DN_USize *)user_context; + bool result = DN_Memcmp(lhs, rhs, elem_size) < 0; + return result; +} + +DN_API bool DN_QSortCompareBytesGT(void const* lhs, void const *rhs, void *user_context) +{ + DN_USize elem_size = *DN_Cast(DN_USize *)user_context; + bool result = DN_Memcmp(lhs, rhs, elem_size) > 0; + return result; +} + +DN_API void DN_QSortBytesLT(void *array, DN_USize array_size, DN_USize elem_size) +{ + DN_QSort(array, array_size, elem_size, &elem_size, DN_QSortCompareBytesLT); +} + +DN_API void DN_QSortBytesGT(void *array, DN_USize array_size, DN_USize elem_size) +{ + DN_QSort(array, array_size, elem_size, &elem_size, DN_QSortCompareBytesGT); +} + +DN_API void DN_QSortStr8NaturalAsc(DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case) +{ + DN_QSort(array, array_size, sizeof(*array), /*user_context=*/ &eq_case, DN_QSortCompareStr8NaturalAsc); +} + +DN_API void DN_QSortStr8NaturalDesc(DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case) +{ + DN_QSort(array, array_size, sizeof(*array), /*user_context=*/ &eq_case, DN_QSortCompareStr8NaturalDesc); +} + +DN_API void DN_QSortStr8LexicographicAsc(DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case) +{ + DN_QSort(array, array_size, sizeof(*array), /*user_context=*/ &eq_case, DN_QSortCompareStr8LexicographicAsc); +} + +DN_API void DN_QSortStr8LexicographicDesc(DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case) +{ + DN_QSort(array, array_size, sizeof(*array), /*user_context=*/ &eq_case, DN_QSortCompareStr8LexicographicDesc); +} + + #define DN_PCG_DEFAULT_MULTIPLIER_64 6364136223846793005ULL #define DN_PCG_DEFAULT_INCREMENT_64 1442695040888963407ULL DN_API DN_PCG32 DN_PCG32Init(DN_U64 seed) @@ -4953,7 +5436,7 @@ DN_API DN_Str8 DN_Str8FromStr8ANSIColourV3F32RGB255Arena(DN_ANSIColourMode mode, DN_API DN_Str8 DN_Str8ANSIColourU8RGBFromFmtVArena(DN_ANSIColourMode mode, DN_U8 r, DN_U8 g, DN_U8 b, DN_Arena *arena, char const *fmt, va_list args) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_Str8 string = DN_Str8FromFmtVArena(&scratch.arena, fmt, args); DN_Str8 result = DN_Str8FromStr8ANSIColourU8RGBArena(mode, string, r, g, b, arena); DN_TCScratchEnd(&scratch); diff --git a/Source/Base/dn_base.h b/Source/Base/dn_base.h index 8804153..25ef8f9 100644 --- a/Source/Base/dn_base.h +++ b/Source/Base/dn_base.h @@ -182,6 +182,80 @@ #define DN_TokenCombine2(x, y) x ## y #define DN_TokenCombine(x, y) DN_TokenCombine2(x, y) +// NOTE: Asserts +#define DN_HardAssertF(expr, fmt, ...) \ + do { \ + if (!(expr)) { \ + DN_Str8 stack_trace_ = DN_Str8FromStackTraceNowHeap(128 /*limit*/, 3 /*skip*/); \ + DN_LogErrorF("Hard assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \ + DN_Str8PrintFmt(stack_trace_), \ + ##__VA_ARGS__); \ + DN_DebugBreak; \ + } \ + } while (0) +#define DN_HardAssert(expr) DN_HardAssertF(expr, "") + +// NOTE: Our default assert requires stack traces which has a bit of a chicken-and-egg problem if +// we're trying to detect some code related to the DN startup sequence. If we try to assert before +// the OS layer is initialised stack-traces will try to use temporary memory which requires TLS to +// be setup which belongs to the OS. +// +// This causes recursion errors as they call into each other. We use RawAsserts for these kind of +// checks. +#if defined(DN_NO_ASSERT) + #define DN_RawAssert(...) + #define DN_Assert(...) + #define DN_AssertOnce(...) + #define DN_AssertF(...) + #define DN_AssertFOnce(...) +#else + #define DN_RawAssert(expr) do { if (!(expr)) DN_DebugBreak; } while (0) + + #define DN_AssertF(expr, fmt, ...) \ + do { \ + if (!(expr)) { \ + DN_Str8 stack_trace_ = DN_Str8FromStackTraceNowHeap(128 /*limit*/, 3 /*skip*/); \ + DN_LogErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \ + DN_Str8PrintFmt(stack_trace_), \ + ##__VA_ARGS__); \ + DN_DebugBreak; \ + } \ + } while (0) + + #define DN_AssertFOnce(expr, fmt, ...) \ + do { \ + static bool once = true; \ + if (!(expr) && once) { \ + once = false; \ + DN_Str8 stack_trace_ = DN_Str8FromStackTraceNowHeap(128 /*limit*/, 3 /*skip*/); \ + DN_LogErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \ + DN_Str8PrintFmt(stack_trace_), \ + ##__VA_ARGS__); \ + DN_DebugBreak; \ + } \ + } while (0) + + #define DN_Assert(expr) DN_AssertF((expr), "") + #define DN_AssertOnce(expr) DN_AssertFOnce((expr), "") +#endif + +#define DN_InvalidCodePathF(fmt, ...) DN_HardAssertF(0, fmt, ##__VA_ARGS__) +#define DN_InvalidCodePath DN_InvalidCodePathF("Invalid code path triggered") +#define DN_StaticAssert(expr) \ + DN_GCC_WARNING_PUSH \ + DN_GCC_WARNING_DISABLE(-Wunused-local-typedefs) \ + typedef char DN_TokenCombine(static_assert_dummy__, __LINE__)[(expr) ? 1 : -1]; \ + DN_GCC_WARNING_POP + +#define DN_Check(expr) DN_CheckF(expr, "") +#if defined(DN_NO_CHECK_BREAK) + #define DN_CheckF(expr, fmt, ...) \ + ((expr) ? true : (DN_LogWarningF(fmt, ##__VA_ARGS__), false)) +#else + #define DN_CheckF(expr, fmt, ...) \ + ((expr) ? true : (DN_LogErrorF(fmt, ##__VA_ARGS__), DN_StackTracePrint(128 /*limit*/), DN_DebugBreak, false)) +#endif + #if defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) #define DN_64_BIT #else @@ -198,13 +272,13 @@ #include // exit() #endif -#define DN_ForIndexU(index, size) DN_USize index = 0; index < size; index++ -#define DN_ForIndexI(index, size) DN_ISize index = 0; index < size; index++ -#define DN_ForItSize(it, T, array, size) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < (size); it.index++, it.data = (array) + it.index -#define DN_ForItSizeReverse(it, T, array, size) struct { DN_USize index; T *data; } it = {(size) - 1, &(array)[size - 1]}; it.index < (size); it.index--, it.data = (array) + it.index -#define DN_ForIt(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)->data[0]}; it.index < (array)->size; it.index++, it.data = ((array)->data) + it.index -#define DN_ForLinkedListIt(it, T, list) struct { DN_USize index; T *data; } it = {0, list}; it.data; it.index++, it.data = ((it).data->next) -#define DN_ForItCArray(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < DN_ArrayCountU(array); it.index++, it.data = (array) + it.index +#define DN_ForIndexU(index, count) DN_USize index = 0; index < count; index++ +#define DN_ForIndexI(index, count) DN_ISize index = 0; index < count; index++ +#define DN_ForItSize(it, T, array, count) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < (count); it.index++, it.data = (array) + it.index +#define DN_ForItSizeReverse(it, T, array, count) struct { DN_USize index; T *data; } it = {(count) - 1, &(array)[count - 1]}; it.index < (count); it.index--, it.data = (array) + it.index +#define DN_ForIt(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)->data[0]}; it.index < (array)->count; it.index++, it.data = ((array)->data) + it.index +#define DN_ForLinkedListIt(it, T, list) struct { DN_USize index; T *data; } it = {0, list}; it.data; it.index++, it.data = ((it).data->next) +#define DN_ForItCArray(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < DN_ArrayCountU(array); it.index++, it.data = (array) + it.index #define DN_AlignUpPowerOfTwo(value, pot) (((uintptr_t)(value) + ((uintptr_t)(pot) - 1)) & ~((uintptr_t)(pot) - 1)) #define DN_AlignDownPowerOfTwo(value, pot) ((uintptr_t)(value) & ~((uintptr_t)(pot) - 1)) @@ -685,14 +759,14 @@ struct DN_StackTraceRawFrame DN_U64 base_addr; }; -struct DN_StackTraceWalkResult +struct DN_StackTrace { void *process; // [Internal] Windows handle to the process DN_U64 *base_addr; // The addresses of the functions in the stack trace DN_U16 size; // The number of `base_addr`'s stored from the walk }; -struct DN_StackTraceWalkResultIterator +struct DN_StackTraceIterator { DN_StackTraceRawFrame raw_frame; DN_U16 index; @@ -852,15 +926,16 @@ struct DN_MemList struct DN_MemListTemp { - DN_MemList* mem; - DN_U64 used_sum; + DN_MemList* mem; + DN_U64 used_sum; #if DN_ARENA_TEMP_MEM_UAF_GUARD - DN_StackTraceWalkResult trace; + DN_StackTrace trace; #endif }; enum DN_AllocatorType { + DN_AllocatorType_MemList, DN_AllocatorType_Arena, DN_AllocatorType_Pool, }; @@ -883,8 +958,16 @@ enum DN_ArenaReset DN_ArenaReset_Yes, }; +typedef DN_U32 DN_ArenaFlags; +enum DN_ArenaFlags_ +{ + DN_ArenaFlags_Nil = 0, + DN_ArenaFlags_OwnsMemList = 1 << 0, +}; + struct DN_Arena { + DN_ArenaFlags flags; DN_MemList* mem; #if DN_ARENA_TEMP_MEM_UAF_GUARD DN_U32 uaf_guard_id; @@ -1043,6 +1126,12 @@ struct DN_Str8SplitResult DN_USize count; }; +enum DN_Str8LineBreakMode +{ + DN_Str8LineBreakMode_AtWord, // Add delimiter to string at ' ' and '\n' boundaries + DN_Str8LineBreakMode_AtWidth, // Add delimiter to string at width intervals +}; + typedef DN_USize DN_Str8TableFlags; enum DN_Str8TableFlags_ { @@ -1088,22 +1177,22 @@ enum DN_AgeUnit_ DN_AgeUnit_All = DN_AgeUnit_Ms | DN_AgeUnit_HMS | DN_AgeUnit_Day | DN_AgeUnit_Week | DN_AgeUnit_Year, }; -enum DN_ByteCountType +enum DN_ByteType { - DN_ByteCountType_B, - DN_ByteCountType_KiB, - DN_ByteCountType_MiB, - DN_ByteCountType_GiB, - DN_ByteCountType_TiB, - DN_ByteCountType_Count, - DN_ByteCountType_Auto, + DN_ByteType_B, + DN_ByteType_KiB, + DN_ByteType_MiB, + DN_ByteType_GiB, + DN_ByteType_TiB, + DN_ByteType_Count, + DN_ByteType_Auto, }; -struct DN_ByteCountResult +struct DN_ByteCount { - DN_ByteCountType type; - DN_Str8 suffix; // "KiB", "MiB", "GiB" .. e.t.c - DN_F64 bytes; + DN_ByteType type; + DN_Str8 suffix; // "KiB", "MiB", "GiB" .. e.t.c + DN_F64 bytes; }; struct DN_Date @@ -1141,6 +1230,7 @@ struct DN_ProfilerAnchor struct DN_ProfilerZone { + struct DN_Profiler *profiler; DN_U16 anchor_index; DN_U64 begin_tsc; DN_U16 parent_zone; @@ -1168,6 +1258,7 @@ struct DN_Profiler DN_F64 frame_avg_tsc; }; +typedef bool (DN_QSortCompareFunc)(void const *a, void const *b, void *user_context); enum DN_ErrSinkMode { DN_ErrSinkMode_Nil, // Default behaviour to accumulate errors into the sink @@ -1220,6 +1311,7 @@ struct DN_TCInitArgs DN_U64 main_commit; DN_U64 temp_reserve; DN_U64 temp_commit; + DN_U64 temp_count; DN_U64 err_sink_reserve; DN_U64 err_sink_commit; }; @@ -1230,21 +1322,20 @@ struct DN_TCCore // (T)hread (C)ontext sitting in thread-local storage DN_U64 thread_id; DN_CallSite call_site; char lane_opaque[sizeof(DN_U64) * 4]; + void* user_context; DN_MemList main_arena_mem_; - DN_MemList temp_a_arena_mem_; - DN_MemList temp_b_arena_mem_; + DN_MemList temp_arena_mems_[4]; DN_MemList err_sink_arena_mem_; DN_Arena main_arena_; - DN_Arena temp_a_arena_; - DN_Arena temp_b_arena_; + DN_Arena temp_arenas_[4]; DN_Arena err_sink_arena_; DN_Arena* main_arena; DN_Pool main_pool; - DN_Arena* temp_a_arena; - DN_Arena* temp_b_arena; + DN_Arena* temp_arenas[4]; + DN_USize temp_arenas_count; DN_ErrSink err_sink; @@ -1446,6 +1537,103 @@ struct DN_RaycastV2 DN_F32 t_b; // Distance along `dir_b` that the intersection occurred, e.g. `origin_b + (dir_b * t_b)` }; +struct DN_Ring +{ + DN_U64 size; + char *base; + DN_U64 write_pos; + DN_U64 read_pos; +}; + +enum DN_ArrayErase +{ + DN_ArrayErase_Unstable, + DN_ArrayErase_Stable, +}; + +enum DN_ArrayAdd +{ + DN_ArrayAdd_Append, + DN_ArrayAdd_Prepend, +}; + +struct DN_ArrayEraseResult +{ + // The next index your for-index should be set to such that you can continue + // to iterate the remainder of the array, e.g: + // + // for (DN_USize index = 0; index < array.size; index++) { + // if (erase) + // index = DN_ArrayEraseRange(&array, index, -3, DN_ArrayErase_Unstable).it_index; + // } + DN_USize it_index; + DN_USize items_erased; // The number of items erased +}; + +struct DN_ArrayFindResult +{ + bool success; + DN_USize index; + void *value; +}; +typedef bool (DN_ArrayFindEqFunc)(void const *lhs, void const *find); + +enum DN_DSMapKeyType +{ + // Key | Key Hash | Map Index + DN_DSMapKeyType_Invalid, + DN_DSMapKeyType_U64, // U64 | Hash(U64) | Hash(U64) % map_size + DN_DSMapKeyType_U64NoHash, // U64 | U64 | U64 % map_size + DN_DSMapKeyType_Buffer, // Buffer | Hash(buffer) | Hash(buffer) % map_size + DN_DSMapKeyType_BufferAsU64NoHash, // Buffer | U64(buffer[0:4]) | U64(buffer[0:4]) % map_size +}; + +struct DN_DSMapKey +{ + DN_DSMapKeyType type; + DN_U32 hash; // Hash to lookup in the map. If it equals, we check that the original key payload matches + void const *buffer_data; + DN_U32 buffer_size; + DN_U64 u64; + bool no_copy_buffer; +}; + +template +struct DN_DSMapSlot +{ + DN_DSMapKey key; // Hash table lookup key + T value; // Hash table value +}; + +typedef DN_U32 DN_DSMapFlags; +enum DN_DSMapFlags_ +{ + DN_DSMapFlags_Nil = 0, + DN_DSMapFlags_DontFreeArenaOnResize = 1 << 0, +}; + +using DN_DSMapHashFunction = DN_U32(DN_DSMapKey key, DN_U32 seed); +template struct DN_DSMap +{ + DN_U32 *hash_to_slot; // Mapping from hash to a index in the slots array + DN_DSMapSlot *slots; // Values of the array stored contiguously, non-sorted order + DN_U32 size; // Total capacity of the map and is a power of two + DN_U32 occupied; // Number of slots used in the hash table + DN_Arena *arena; // Backing arena for the hash table + DN_Pool pool; // Allocator for keys that are variable-sized buffers + DN_U32 initial_size; // Initial map size, map cannot shrink on erase below this size + DN_DSMapHashFunction *hash_function; // Custom hashing function to use if field is set + DN_U32 hash_seed; // Seed for the hashing function, when 0, DN_DS_MAP_DEFAULT_HASH_SEED is used + DN_DSMapFlags flags; +}; + +template struct DN_DSMapResult +{ + bool found; + DN_DSMapSlot *slot; + T *value; +}; + #if !defined(DN_STB_SPRINTF_HEADER_ONLY) #define STB_SPRINTF_IMPLEMENTATION #define STB_SPRINTF_STATIC @@ -1595,6 +1783,10 @@ DN_API DN_F32 DN_EpsilonClampF32 DN_API DN_MemStats DN_MemStatsSum (DN_MemStats lhs, DN_MemStats rhs); DN_API DN_MemStats DN_MemStatsSumArray (DN_MemStats const *array, DN_USize size); +// NOTE: `MemList` is an implementation of a classical `Arena` (e.g. bump allocator, can dynamically +// grow, frees by bumping pointer back, sub-divides a block of memory). The term `Arena` is reserved +// as a thin-layer over the functionality here to provide some use-after-free protection. See +// `Arena` for more info. DN_API DN_MemList DN_MemListFromBuffer (void *buffer, DN_USize size, DN_MemFlags flags); DN_API DN_MemList DN_MemListFromMemFuncs (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_MemFuncs mem_funcs); DN_API void DN_MemListDeinit (DN_MemList *mem); @@ -1622,8 +1814,46 @@ DN_API void DN_MemListTempEnd #define DN_MemListNewCopy(arena, T, src) (T *)DN_MemListCopy(arena, (src), sizeof(T), alignof(T)) #define DN_MemListNewArrayCopy(arena, T, src, count) (T *)DN_MemListCopy(arena, (src), sizeof(T) * (count), alignof(T)) -DN_API void DN_ArenaUAFCheck (DN_Arena *arena); -#define DN_ArenaDeref(arena_view) (DN_ArenaUAFCheck(arena_view), (arena_view)->arena) +// NOTE: `Arena`'s in this codebase are thin-layers over `MemList` but additionally provide +// use-after-free (UAF) protection when using temporary memory regions (e.g. thread context scratch +// `TCScratch` or `Temp[Begin|End]` family of functions). +// +// These arenas associate themselves with the temporary memory region they begin in if it is +// constructed using the `Temp[Begin|End]` family of functions (TCScratch implicitly call these for +// you before handing you the arena). If you attempt to allocate from a different arena bound with a +// different temporary memory region than the active one an assertion is triggered. This protection +// is gated by the presence of the preprocessor definition `#define DN_ARENA_TEMP_MEM_UAF_GUARD 1`. +// +// Without the preprocessor definition UAF protection is compiled out (e.g. no-op). UAF protection +// is also not enabled if you use `ArenaFromMemList` which simply sets up a plain arena that +// forwards all calls into the `MemList` API. +// +// To get UAF protection, all allocations _must_ go through the `Arena` API, using the `MemList` +// field directly in the `Arena` will bypass these checks and lead to unusual behaviour. If you want +// to forgo any of this infrastructure store and use the `MemList` directly in your codebase. +// +// UAF Example +/* + DN_Arena arena = DN_ArenaFromHeap(DN_Megabytes(1), DN_MemFlags_Nil); + DN_Arena temp = DN_ArenaTempBeginFromArena(&arena); + { + // NOTE: You can also `TempBegin` with `&temp`, either is valid. They both have pointers to + // the same underlying memory block owned by `arena`. + DN_Arena nested_temp = DN_ArenaTempBeginFromArena(&arena); + + // NOTE: This allocation triggers the UAF guard and asserts! An allocation into `temp`'s memory + // region would be reset when we end `nested_temp`'s memory region since they are spawned from + // the same underlying memory block sitting in `arena`. + // + // But the intent here is that the caller is resetting `nested_temp`'s allocations and not + // `temp` hence the UAF protection triggers. + DN_U64 *u64 = DN_ArenaNewZ(&temp, DN_U64); + + DN_ArenaTempEnd(&nested_temp); + } + DN_ArenaTempEnd(&temp); + DN_ArenaDeinit(&arena); // Frees the memory + */ DN_API DN_Arena DN_ArenaFromMemList (DN_MemList *mem); DN_API DN_Arena DN_ArenaTempBeginFromMemList (DN_MemList *mem); DN_API DN_Arena DN_ArenaTempBeginFromArena (DN_Arena *arena); @@ -1631,6 +1861,7 @@ DN_API void DN_ArenaTempEnd DN_API void* DN_ArenaAlloc (DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZMem z_mem); DN_API void* DN_ArenaAllocContiguous (DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZMem z_arena); DN_API void* DN_ArenaCopy (DN_Arena *arena, void const *data, DN_U64 size, uint8_t align); +DN_API void DN_ArenaDeinit (DN_Arena *arena); #define DN_ArenaNew(arena, T, zmem) (T *)DN_ArenaAlloc(arena, sizeof(T), alignof(T), zmem) #define DN_ArenaNewZ(arena, T) (T *)DN_ArenaAlloc(arena, sizeof(T), alignof(T), DN_ZMem_Yes) @@ -1674,15 +1905,18 @@ DN_API void DN_ErrSinkAppendFV_ DN_API void DN_ErrSinkAppendF_ (DN_ErrSink *err, DN_U32 error_code, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...); #define DN_ErrSinkAppendF(error, error_code, fmt, ...) DN_ErrSinkAppendF_(error, error_code, DN_CALL_SITE, fmt, ##__VA_ARGS__) -DN_API void DN_TCInit (DN_TCCore *tc, DN_U64 thread_id, DN_Arena *main_arena, DN_Arena *temp_a_arena, DN_Arena *temp_b_arena, DN_Arena *err_sink_arena); -DN_API void DN_TCInitFromMemFuncs (DN_TCCore *tc, DN_U64 thread_id, DN_TCInitArgs *args, DN_MemFuncs mem_funcs); +DN_API DN_TCInitArgs DN_TCInitArgsDefault (); +DN_API void DN_TCInit (DN_TCCore *tc, DN_U64 thread_id, DN_Arena *main_arena, DN_Arena *temp_arenas, DN_USize temp_arenas_count, DN_Arena *err_sink_arena); +DN_API void DN_TCInitFromMemFuncs (DN_TCCore *tc, DN_U64 thread_id, DN_TCInitArgs args, DN_MemFuncs mem_funcs); DN_API void DN_TCDeinit (DN_TCCore *tc, DN_TCDeinitArenas deinit_arenas); DN_API void DN_TCEquip (DN_TCCore *tc); DN_API DN_TCCore* DN_TCGet (); DN_API DN_Arena* DN_TCMainArena (); DN_API DN_Pool* DN_TCMainPool (); -DN_API DN_Arena DN_TCTempArena (DN_Arena **conflicts, DN_USize count); -DN_API DN_TCScratch DN_TCScratchBegin (DN_Arena **conflicts, DN_USize count); +DN_API DN_Arena DN_TCTempArenaFromAllocator (DN_Allocator *conflicts, DN_USize count); +DN_API DN_Arena DN_TCTempArenaFromArena (DN_Arena **conflicts, DN_USize count); +DN_API DN_TCScratch DN_TCScratchBeginAllocator (DN_Allocator *conflicts, DN_USize count); +DN_API DN_TCScratch DN_TCScratchBeginArena (DN_Arena **conflicts, DN_USize count); DN_API void DN_TCScratchEnd (DN_TCScratch *scratch); DN_API void DN_TCSetFrameArena (DN_Arena *arena); DN_API DN_Arena* DN_TCFrameArena (); @@ -1719,6 +1953,11 @@ DN_API DN_U8x32 DN_U8x32FromHexUnsafe DN_API DN_U8x32FromResult DN_U8x32FromHex (DN_Str8 hex_32b); DN_API DN_U8x32FromResult DN_U8x32FromDecimalStr8 (DN_Str8 decimal); // Write decimal string (e.g. "12345") as big-endian 256-bit value +DN_API DN_Allocator DN_AllocatorFromMemList (DN_MemList *mem); +DN_API DN_Allocator DN_AllocatorFromArena (DN_Arena *arena); +DN_API DN_Allocator DN_AllocatorFromPool (DN_Pool *pool); +DN_API void* DN_AllocatorAlloc (DN_Allocator allocator, DN_USize size, DN_U8 align, DN_ZMem z_mem); + DN_API DN_USize DN_FmtVSize (DN_FMT_ATTRIB char const *fmt, va_list args); DN_API DN_USize DN_FmtSize (DN_FMT_ATTRIB char const *fmt, ...); DN_API DN_FmtAppendResult DN_FmtVAppend (char *buf, DN_USize *buf_size, DN_USize buf_max, char const *fmt, va_list args); @@ -1735,19 +1974,22 @@ DN_API DN_USize DN_CStr16Size #define DN_Str8FromPtr(data, size) DN_Literal(DN_Str8){(char *)(data), (DN_USize)(size)} #define DN_Str8FromStruct(ptr) DN_Str8FromPtr((ptr)->data, (ptr)->size) #define DN_Str8FromLitArray(c_array) DN_Str8FromPtr(c_array, DN_ArrayCountU(c_array)) +DN_API DN_Str8 DN_Str8AllocAllocator (DN_USize size, DN_ZMem z_mem, DN_Allocator allocator); DN_API DN_Str8 DN_Str8AllocArena (DN_USize size, DN_ZMem z_mem, DN_Arena *arena); DN_API DN_Str8 DN_Str8AllocPool (DN_USize size, DN_Pool *pool); DN_API DN_Str8 DN_Str8FromCStr8 (char const *src); DN_API DN_Str8 DN_Str8FromCStr8Arena (char const *src, DN_Arena *arena); DN_API DN_Str8 DN_Str8FromPtrArena (void const *data, DN_USize size, DN_Arena *arena); DN_API DN_Str8 DN_Str8FromPtrPool (void const *data, DN_USize size, DN_Pool *pool); +DN_API DN_Str8 DN_Str8FromStr8Allocator (DN_Str8 string, DN_Allocator allocator); DN_API DN_Str8 DN_Str8FromStr8Arena (DN_Str8 string, DN_Arena *arena); DN_API DN_Str8 DN_Str8FromStr8Pool (DN_Str8 string, DN_Pool *pool); +DN_API DN_Str8 DN_Str8FromFmtVAllocator (DN_Allocator allocator, DN_FMT_ATTRIB char const *fmt, va_list args); DN_API DN_Str8 DN_Str8FromFmtVArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, va_list args); +DN_API DN_Str8 DN_Str8FromFmtAllocator (DN_Allocator allocator, DN_FMT_ATTRIB char const *fmt, ...); DN_API DN_Str8 DN_Str8FromFmtArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...); DN_API DN_Str8 DN_Str8FromFmtVPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args); DN_API DN_Str8 DN_Str8FromFmtPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...); -DN_API DN_Str8 DN_Str8FromByteCountType (DN_ByteCountType type); DN_API DN_Str8x16 DN_Str8x16FromFmt (DN_FMT_ATTRIB char const *fmt, ...); DN_API DN_Str8x16 DN_Str8x16FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); DN_API DN_Str8x32 DN_Str8x32FromFmt (DN_FMT_ATTRIB char const *fmt, ...); @@ -1825,12 +2067,16 @@ DN_API DN_Str8 DN_Str8Upper DN_API DN_Str8 DN_Str8Replace (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena, DN_Str8EqCase eq_case); DN_API DN_Str8 DN_Str8ReplaceSensitive (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena); DN_API DN_Str8 DN_Str8ReplaceInsensitive (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena); -DN_API DN_Str8 DN_Str8PadNewLines (DN_Str8 string, DN_Str8 pad_string, DN_Arena *arena); -DN_API DN_Str8 DN_Str8LineBreakStr8 (DN_Str8 src, DN_USize desired_width, DN_Arena *arena); +DN_API DN_Str8 DN_Str8PadNewLinesAllocator (DN_Str8 string, DN_Str8 pad_string, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8PadNewLinesArena (DN_Str8 string, DN_Str8 pad_string, DN_Arena *arena); +DN_API DN_Str8 DN_Str8LineBreakAllocator (DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8LineBreakArena (DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Arena *arena); DN_API DN_Str8 DN_Str8Table (DN_Str8 const* rows, DN_USize num_rows, DN_USize num_cols, DN_Str8TableFlags flags, DN_Arena *arena); DN_API DN_Str8 DN_Str8SliceRender (DN_Str8Slice array, DN_Str8 separator, DN_Arena *arena); DN_API DN_Str8 DN_Str8RenderSpaceSep (DN_Str8Slice array, DN_Arena *arena); +DN_API int DN_Str8CompareNatural (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case); +DN_API int DN_Str8CompareLexicographic (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case); DN_API bool DN_Str16Eq (DN_Str16 lhs, DN_Str16 rhs); DN_API DN_Str16 DN_Str16SliceRender (DN_Str16Slice array, DN_Str16 separator, DN_Arena *arena); @@ -1864,9 +2110,10 @@ DN_API bool DN_Str8BuilderPrependCopy #define DN_Str8BuilderPrependFV(builder, fmt, args) DN_Str8BuilderAddFV(builder, DN_Str8BuilderAdd_Prepend, fmt, args) DN_API bool DN_Str8BuilderPrependF (DN_Str8Builder *builder, DN_FMT_ATTRIB char const *fmt, ...); DN_API bool DN_Str8BuilderErase (DN_Str8Builder *builder, DN_Str8 string); -DN_API DN_Str8 DN_Str8BuilderBuild (DN_Str8Builder const *builder, DN_Arena *arena); -DN_API DN_Str8 DN_Str8BuilderBuildDelimited (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Arena *arena); -DN_API DN_Str8Slice DN_Str8BuilderBuildSlice (DN_Str8Builder const *builder, DN_Arena *arena); +DN_API DN_Str8 DN_Str8FromStr8BuilderAllocator (DN_Str8Builder const *builder, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8FromStr8BuilderArena (DN_Str8Builder const *builder, DN_Arena *arena); +DN_API DN_Str8 DN_Str8FromStr8BuilderDelimitAllocator (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Allocator allocator); +DN_API DN_Str8 DN_Str8FromStr8BuilderDelimitArena (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Arena *arena); DN_API int DN_UTF8Encode (DN_U8 utf8[4], DN_U32 codepoint); DN_API int DN_UTF16Encode (DN_U16 utf16[2], DN_U32 codepoint); @@ -1888,7 +2135,8 @@ DN_API DN_U8x32 DN_BytesFromHex64Ptr DN_API DN_HexU64Str8 DN_HexFromU64 (DN_U64 value, DN_HexFromU64Type type); DN_API DN_USize DN_HexFromPtrBytes (void const *bytes, DN_USize bytes_count, void *hex, DN_USize hex_count, DN_TrimLeadingZero trim_leading_z); DN_API DN_Str8 DN_HexFromPtrBytesArena (void const *bytes, DN_USize bytes_count, DN_Arena *arena, DN_TrimLeadingZero trim_leading_z); -DN_API DN_USize DN_HexFromStr8Bytes (void const *bytes, DN_USize bytes_count, void *hex, DN_USize hex_count, DN_TrimLeadingZero trim_leading_z); +DN_API DN_USize DN_HexFromStr8Bytes (DN_Str8 bytes, void *hex, DN_USize hex_count, DN_TrimLeadingZero trim_leading_z); +DN_API DN_Str8 DN_HexFromStr8BytesArena (DN_Str8 bytes, DN_Arena *arena, DN_TrimLeadingZero trim_leading_z); DN_API DN_Hex32 DN_Hex32FromPtr16b (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z); DN_API DN_Hex64 DN_Hex64FromPtr32b (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z); DN_API DN_Hex128 DN_Hex128FromPtr64b (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z); @@ -1902,29 +2150,87 @@ DN_API bool DN_DateIsValid DN_API DN_Date DN_DateFromUnixTimeMs (DN_USize unix_ts_ms); DN_API DN_U64 DN_UnixTimeMsFromDate (DN_Date date); -DN_API DN_ByteCountResult DN_ByteCountFromType (DN_U64 bytes, DN_ByteCountType type); -#define DN_ByteCount(bytes) DN_ByteCountFromType(bytes, DN_ByteCountType_Auto) -DN_API DN_Str8x32 DN_ByteCountStr8x32FromType (DN_U64 bytes, DN_ByteCountType type); -#define DN_ByteCountStr8x32(bytes) DN_ByteCountStr8x32FromType(bytes, DN_ByteCountType_Auto) +DN_API DN_Str8 DN_Str8FromByteType (DN_ByteType type); +DN_API DN_ByteCount DN_ByteCountFromU64 (DN_U64 byte_count, DN_ByteType type); +DN_API DN_Str8x32 DN_Str8x32FromByteCountU64 (DN_U64 byte_count, DN_ByteType type); +#define DN_Str8x32FromByteCountU64Auto(bytes) DN_Str8x32FromByteCountU64(bytes, DN_ByteType_Auto) +// NOTE: Profiler +// Overview +// Basic profiler that tracks the duration of marked-up regions which are denoted by an +// opening and closing `anchor`. The profiler works in "frame" life-cycles which can by cycled by +// calling `DN_ProfilerNewFrame`. The number of frames that the profiler will persist is chosen by +// `anchors_per_frame`. +// +// For example if you pass a buffer of 1024 `anchors` and `anchors_per_frame = 128` then the +// profiler will hold onto 8 (1024 anchors / 128 anchors per frame) frames of profiling +// information. The profiler will cycle through the 8 frames of anchors and upon reaching the end +// begin overwriting the oldest frames worth of anchors. +// +// Once a frame has ended the just completed buffer of anchors that was just written to can be +// read by the caller and visualised to find the timings of each region, relative to the duration +// of the frame until it is eventually overwritten by calling `DN_ProfilerNewFrame` once the +// profiler has cycled through all the other frames. +// +// The caller must upfront determine the number of anchors and frames the profiler should have and +// pass it in. The profiler does not allocate any memory. +// +// When profiling functions that are invoked from different call-stacks the exclusive elapsed +// duration can exceed its inclusive duration. This is by design as the profiler makes no +// effort to distinguish between the different call-tree that a zone may have been triggered by. +// The profiler simpler always updates the same static anchor assigned for that zone. +// +// API +// DN_ProfilerInit +// You can set `tsc_now` to NULL to use the default timer mechanic which relies on +// DN_CPUGetTSC() which essentially uses __rdtsc. `tsc_frequency` must however always be +// provided, for, DN_CPUGetTSC() you can use `DN_OS_EstimateTSCPerSecond()` to calculate the +// frequency of `__rdtsc`. +// DN_ProfilerNewFrame +// Always call `DN_ProfilerNewFrame` at-least once using the `Zone` family of functions as it +// sets up +// DN_ProfilerBeginZone +// The zeroth anchor is reserved for profiling the begin and end of a profiler frame. If you are +// manually specifying `anchor_index` (e.g. using an enum) ensure that the first anchor index +// starts from `1`. Note that the `BeginZoneAuto` macro uses `__COUNTER__ + 1` to skip the 0th +// zone. +// DN_ProfilerFrameAnchors +// Returns the current frame's (e.g. the anchors that the profiler is currently writing to) +// worth of anchors #define DN_ProfilerZoneLoop(prof, name, index) \ DN_ProfilerZone DN_UniqueName(zone_) = DN_ProfilerBeginZone(prof, DN_Str8Lit(name), index), DN_UniqueName(dummy_) = {}; \ DN_UniqueName(dummy_).begin_tsc == 0; \ - DN_ProfilerEndZone(prof, DN_UniqueName(zone_)), DN_UniqueName(dummy_).begin_tsc = 1 + DN_ProfilerEndZone(DN_UniqueName(zone_)), DN_UniqueName(dummy_).begin_tsc = 1 #define DN_ProfilerZoneLoopAuto(prof, name) DN_ProfilerZoneLoop(prof, name, __COUNTER__ + 1) DN_API DN_Profiler DN_ProfilerInit (DN_ProfilerAnchor *anchors, DN_USize count, DN_USize anchors_per_frame, DN_ProfilerTSCNowFunc *tsc_now, DN_U64 tsc_frequency); DN_API DN_ProfilerZone DN_ProfilerBeginZone (DN_Profiler *profiler, DN_Str8 name, DN_U16 anchor_index); #define DN_ProfilerBeginZoneAuto(prof, name) DN_ProfilerBeginZone(prof, DN_Str8Lit(name), __COUNTER__ + 1) -DN_API void DN_ProfilerEndZone (DN_Profiler *profiler, DN_ProfilerZone zone); +DN_API void DN_ProfilerEndZone (DN_ProfilerZone zone); DN_API DN_USize DN_ProfilerFrameCount (DN_Profiler const *profiler); DN_API DN_ProfilerAnchorArray DN_ProfilerFrameAnchorsFromIndex (DN_Profiler *profiler, DN_USize frame_index); DN_API DN_ProfilerAnchorArray DN_ProfilerFrameAnchors (DN_Profiler *profiler); DN_API void DN_ProfilerNewFrame (DN_Profiler *profiler); -DN_API void DN_ProfilerDump (DN_Profiler *profiler); +DN_API DN_USize DN_ProfilerFmtAnchor (DN_ProfilerAnchor anchor, DN_U64 tsc_frequency, char *buffer, DN_USize count); +DN_API DN_Str8 DN_ProfilerFmtAnchorStr8 (DN_ProfilerAnchor anchor, DN_U64 tsc_frequency, DN_Arena *arena); +DN_API void DN_ProfilerFmtToStdout (DN_Profiler *profiler); DN_API DN_F64 DN_ProfilerSecFromTSC (DN_Profiler *profiler, DN_U64 duration_tsc); DN_API DN_F64 DN_ProfilerMsFromTSC (DN_Profiler *profiler, DN_U64 duration_tsc); +DN_API void DN_QSort (void *array, DN_USize array_size, DN_USize elem_size, void *user_context, DN_QSortCompareFunc *compare); +DN_API bool DN_QSortCompareStr8NaturalAsc (void const* lhs, void const *rhs, void *user_context); +DN_API bool DN_QSortCompareStr8NaturalDesc (void const* lhs, void const *rhs, void *user_context); +DN_API bool DN_QSortCompareStr8LexicographicAsc (void const* lhs, void const *rhs, void *user_context); +DN_API bool DN_QSortCompareStr8LexicographicDesc (void const* lhs, void const *rhs, void *user_context); +DN_API bool DN_QSortCompareBytesLT (void const* lhs, void const *rhs, void *user_context); +DN_API bool DN_QSortCompareBytesGT (void const* lhs, void const *rhs, void *user_context); +DN_API void DN_QSortBytesLT (void *array, DN_USize array_size, DN_USize elem_size); +DN_API void DN_QSortBytesGT (void *array, DN_USize array_size, DN_USize elem_size); +DN_API void DN_QSortStr8NaturalAsc (DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case); +DN_API void DN_QSortStr8NaturalDesc (DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case); +DN_API void DN_QSortStr8LexicographicAsc (DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case); +DN_API void DN_QSortStr8LexicographicDesc (DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case); + DN_API DN_PCG32 DN_PCG32Init (DN_U64 seed); DN_API DN_U32 DN_PCG32Next (DN_PCG32 *rng); DN_API DN_U64 DN_PCG32Next64 (DN_PCG32 *rng); @@ -1965,6 +2271,10 @@ DN_API DN_Str8 DN_Str8FromStr8ANSIColourV3F32RGB255Arena DN_API DN_Str8 DN_Str8FromFmtANSIColourU8RGBArena (DN_ANSIColourMode mode, DN_U8 r, DN_U8 g, DN_U8 b, DN_Arena *arena, char const *fmt, ...); DN_API DN_Str8 DN_Str8FromFmtANSIColourV3F32RGB255Arena (DN_ANSIColourMode mode, DN_V3F32 rgb_255, DN_Arena *arena, char const *fmt, ...); +// NOTE: Create log printable lines with a date prefix, severity and message. The platform +// implementation should call `SetPrintFunc` to intercept the log messages and output to the desired +// destination (log file, standard out, e.t.c.). When the library is initialised `DN_Init` to with +// OS functionality enabled, the log callback is by default set to outputting via standard out. DN_API DN_LogPrefixSize DN_LogMakePrefix (DN_LogStyle style, DN_LogTypeParam type, DN_CallSite call_site, DN_LogDate date, char *dest, DN_USize dest_size); DN_API void DN_LogSetPrintFunc (DN_LogPrintFunc *print_func, void *user_data); DN_API void DN_LogPrint (DN_LogTypeParam type, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, ...); @@ -1985,21 +2295,27 @@ DN_API DN_LogTypeParam DN_LogTypeParamFromType // NOTE: OS primitives that the OS layer can provide for the base layer but is optional. #if defined(DN_FREESTANDING) -#define DN_StackTraceWalk(...) -#define DN_StackTraceWalkResultIterate(...) -#define DN_StackTraceWalkResultToStr8(...) DN_Str8Lit("N/A") -#define DN_StackTraceWalkStr8(...) DN_Str8Lit("N/A") -#define DN_StackTraceWalkStr8FromHeap(...) DN_Str8Lit("N/A") +#define DN_StackTraceFromArena(...) {} +#define DN_StackTraceFromAllocator(...) {} +#define DN_StackTraceIterate(...) false +#define DN_Str8FromStackTraceAllocator(...) DN_Str8Lit("N/A") +#define DN_Str8FromStackTraceArena(...) DN_Str8Lit("N/A") +#define DN_Str8FromStackTraceNowAllocator(...) DN_Str8Lit("N/A") +#define DN_Str8FromStackTraceNowArena(...) DN_Str8Lit("N/A") +#define DN_Str8FromStackTraceNowHeap(...) DN_Str8Lit("N/A") #define DN_StackTraceGetFrames(...) #define DN_StackTraceRawFrameToFrame(...) #define DN_StackTracePrint(...) #define DN_StackTraceReloadSymbols(...) #else -DN_API DN_StackTraceWalkResult DN_StackTraceWalk (DN_Arena *arena, DN_U16 limit); -DN_API bool DN_StackTraceWalkResultIterate (DN_StackTraceWalkResultIterator *it, DN_StackTraceWalkResult const *walk); -DN_API DN_Str8 DN_StackTraceWalkResultToStr8 (DN_Arena *arena, DN_StackTraceWalkResult const *walk, DN_U16 skip); -DN_API DN_Str8 DN_StackTraceWalkStr8 (DN_Arena *arena, DN_U16 limit, DN_U16 skip); -DN_API DN_Str8 DN_StackTraceWalkStr8FromHeap (DN_U16 limit, DN_U16 skip); +DN_API DN_StackTrace DN_StackTraceFromArena (DN_Arena *arena, DN_U16 limit); +DN_API DN_StackTrace DN_StackTraceFromAllocator (DN_Allocator allocator, DN_U16 limit); +DN_API bool DN_StackTraceIterate (DN_StackTraceIterator *it, DN_StackTrace const *walk); +DN_API DN_Str8 DN_Str8FromStackTraceAllocator (DN_Allocator allocator, DN_StackTrace const *walk, DN_U16 skip); +DN_API DN_Str8 DN_Str8FromStackTraceArena (DN_Arena *arena, DN_StackTrace const *walk, DN_U16 skip); +DN_API DN_Str8 DN_Str8FromStackTraceNowAllocator (DN_Arena *arena, DN_U16 limit, DN_U16 skip); +DN_API DN_Str8 DN_Str8FromStackTraceNowArena (DN_Arena *arena, DN_U16 limit, DN_U16 skip); +DN_API DN_Str8 DN_Str8FromStackTraceNowHeap (DN_U16 limit, DN_U16 skip); DN_API DN_StackTraceFrameSlice DN_StackTraceGetFrames (DN_Arena *arena, DN_U16 limit); DN_API DN_StackTraceFrame DN_StackTraceRawFrameToFrame (DN_Arena *arena, DN_StackTraceRawFrame raw_frame); DN_API void DN_StackTracePrint (DN_U16 limit); @@ -2296,4 +2612,503 @@ DN_API DN_Rect DN_RectCutCut #define DN_RectCutInit(rect, side) DN_Literal(DN_RectCut){rect, side} DN_API DN_RaycastV2 DN_RaycastLineIntersectV2 (DN_V2F32 origin_a, DN_V2F32 dir_a, DN_V2F32 origin_b, DN_V2F32 dir_b); + +// NOTE: Containers that are imlpemented using primarily macros for operating on data structures +// that are embedded into a C style struct or from a set of defined variables from the available +// scope. Keep it stupid simple, structs and functions. Minimal amount of container types with +// flexible construction leads to less duplicated container code and less template meta-programming. + +// NOTE: Intrusive Singly Linked List +// Define a struct with the members `next`: +// +// struct MyLinkItem { +// int data; +// MyLinkItem *next; +// } my_link = {}; +// +// MyLinkItem *first_item = DN_ISinglyLLDetach(&my_link, MyLinkItem); +#define DN_ISinglyLLDetach(list) (decltype(list))DN_SinglyLLDetach((void **)&(list), (void **)&(list)->next) + +// NOTE: Singly Linked List with Head and Tail pointer +/* + struct MyLinkItem { + int data; + MyLinkItem *next; + } my_list = {}; + + struct MyContainer { + MyLinkItem *head; + MyLinkItem *tail; + }; + + MyLinkItem item = {}; + MyContainer container = {}; + DN_ISinglyHeadTailLLAppend(container, item); + // ... or alternatively, DN_SinglyHeadTailLLAppend(container.head, container.tail, item); + + for (MyLinkItem *it = container.head; it; it = it->next) { } +*/ +#define DN_SinglyHeadTailLLAppend(head, tail, to_append) \ + do { \ + if (!head) \ + head = to_append; \ + if (tail) \ + tail->next = to_append; \ + tail = to_append; \ + } while (0) +#define DN_ISinglyHeadTailLLAppend(container_ptr, to_append) DN_SinglyHeadTailLLAppend((container_ptr)->head, (container_ptr)->tail, to_append) + +// NOTE: Sentinel Doubly Linked List +// Uses a sentinel/dummy node as the list head. The sentinel points to itself when empty. +// Define a struct with the members `next` and `prev`: +// +// struct MyLinkItem { +// int data; +// MyLinkItem *next; +// MyLinkItem *prev; +// } my_list = {}; +// +// DN_SentinelDoublyLLInit(&my_list); +// DN_SentinelDoublyLLAppend(&my_list, &new_item); +// DN_SentinelDoublyLLForEach(it, &my_list) { /* ... */ } +// +#define DN_SentinelDoublyLLInit(list) (list)->next = (list)->prev = (list) +#define DN_SentinelDoublyLLIsSentinel(list, item) ((list) == (item)) +#define DN_SentinelDoublyLLIsEmpty(list) (!(list) || ((list) == (list)->next)) +#define DN_SentinelDoublyLLIsInit(list) ((list)->next && (list)->prev) +#define DN_SentinelDoublyLLHasItems(list) ((list) && ((list) != (list)->next)) +#define DN_SentinelDoublyLLForEach(it, list) auto *it = (list)->next; (it) != (list); (it) = (it)->next + +#define DN_SentinelDoublyLLInitArena(list, T, ptr_arena) \ + do { \ + (list) = DN_ArenaNew(ptr_arena, T, DN_ZMem_Yes); \ + DN_SentinelDoublyLLInit(list); \ + } while (0) + +#define DN_SentinelDoublyLLInitPool(list, T, pool) \ + do { \ + (list) = DN_PoolNew(pool, T); \ + DN_SentinelDoublyLLInit(list); \ + } while (0) + +#define DN_SentinelDoublyLLDetach(item) \ + do { \ + if (item) { \ + (item)->prev->next = (item)->next; \ + (item)->next->prev = (item)->prev; \ + (item)->next = nullptr; \ + (item)->prev = nullptr; \ + } \ + } while (0) + +#define DN_SentinelDoublyLLDequeue(list, dest_ptr) \ + if (DN_SentinelDoublyLLHasItems(list)) { \ + dest_ptr = (list)->next; \ + DN_SentinelDoublyLLDetach(dest_ptr); \ + } + +#define DN_SentinelDoublyLLAppend(list, item) \ + do { \ + if (item) { \ + if ((item)->next) \ + DN_SentinelDoublyLLDetach(item); \ + (item)->next = (list)->next; \ + (item)->prev = (list); \ + (item)->next->prev = (item); \ + (item)->prev->next = (item); \ + } \ + } while (0) + +#define DN_SentinelDoublyLLPrepend(list, item) \ + do { \ + if (item) { \ + if ((item)->next) \ + DN_SentinelDoublyLLDetach(item); \ + (item)->next = (list); \ + (item)->prev = (list)->prev; \ + (item)->next->prev = (item); \ + (item)->prev->next = (item); \ + } \ + } while (0) + +// NOTE: Doubly Linked List +// Define a struct with the members `next` and `prev`. This list has null pointers for head->prev +// and tail->next. +// +// struct MyLinkItem { +// int data; +// MyLinkItem *next; +// MyLinkItem *prev; +// } my_link = {}; +// +// MyLinkItem first_item = {}, second_item = {}; +// DN_DoublyLLAppend(&first_item, &second_item); // first_item -> second_item +// +#define DN_DoublyLLDetach(head, ptr) \ + do { \ + if ((head) && (head) == (ptr)) \ + (head) = (head)->next; \ + if ((ptr)) { \ + if ((ptr)->next) \ + (ptr)->next->prev = (ptr)->prev; \ + if ((ptr)->prev) \ + (ptr)->prev->next = (ptr)->next; \ + (ptr)->prev = (ptr)->next = 0; \ + } \ + } while (0) + +#define DN_DoublyLLAppend(head, ptr) \ + do { \ + if ((ptr)) { \ + DN_AssertF((ptr)->prev == 0 && (ptr)->next == 0, "Detach the pointer first"); \ + (ptr)->prev = (head); \ + (ptr)->next = 0; \ + if ((head)) { \ + (ptr)->next = (head)->next; \ + (head)->next = (ptr); \ + } else { \ + (head) = (ptr); \ + } \ + } \ + } while (0) + +#define DN_DoublyLLPrepend(head, ptr) \ + do { \ + if ((ptr)) { \ + DN_AssertF((ptr)->prev == 0 && (ptr)->next == 0, "Detach the pointer first"); \ + (ptr)->prev = nullptr; \ + (ptr)->next = (head); \ + if ((head)) { \ + (ptr)->prev = (head)->prev; \ + (head)->prev = (ptr); \ + } else { \ + (head) = (ptr); \ + } \ + } \ + } while (0) + +// NOTE: For C++ we need to cast the void* returned in these functions to the concrete type. In C, +// no cast is needed. +#if defined(__cplusplus) + #define DN_CppDeclType(x) decltype(x) +#else + #define DN_CppDeclType +#endif + +// NOTE: Arrays +// Data structures that have a `T *data`, `DN_USize count` and `DN_USize max` capacity that can be +// dynamically shrunk or expanded. +// +// API +// ResizeFrom: Resizes the array to `new_max` erase elements if resizing to a smaller size +// GrowFrom: Expands the capacity of the array if `new_max > array.max` otherwise no-op +// GrowIfNeeded: Expands the capacity of the array if `array.size + add_count > array.max` otherwise no-op +// +// Variants +// PArray => Pointer (to) Array +// LArray => Literal Array +// Define a C array and size. (P) array macros take a pointer to the aray, its size and its max +// capacity. The (L) array macros take the literal array and derives the max capacity +// automatically using DN_ArrayCountU(l_array). +// +// MyStruct buffer[TB_ASType_Count] = {}; +// DN_USize size = 0; +// MyStruct *item_0 = DN_PArrayMake(buffer, &size, DN_ArrayCountU(buffer), DN_ZMem_No); +// MyStruct *item_1 = DN_LArrayMake(buffer, &size, DN_ZMem_No); +// +// IArray => Intrusive Array +// Define a struct with the members `data`, `count` and `max`: +// +// struct MyArray { +// MyStruct *data; +// DN_USize count; +// DN_USize max; +// } my_array = {}; +// DN_Arena arena = {}; +// DN_IArrayResizeFromArena(&my_array, &arena, 256); +// MyStruct *item = DN_IArrayMake(&my_array, DN_ZMem_No); +// +#if defined(__cplusplus) + #define DN_PArrayFind(ptr, size, ptr_find, eq_func) DN_TArrayFind(ptr, size, ptr_find, eq_func) + #define DN_PArrayFindMemEq(ptr, size, ptr_find) DN_TArrayFindMemEq(ptr, size, ptr_find) + #define DN_PArrayResizeFromPool(ptr, ptr_size, ptr_max, pool, new_max) DN_TArrayResizeFromPool(&(ptr), ptr_size, ptr_max, pool, new_max) + #define DN_PArrayResizeFromArena(ptr, ptr_size, ptr_max, arena, new_max) DN_TArrayResizeFromArena(&(ptr), ptr_size, ptr_max, arena, new_max) + #define DN_PArrayGrowFromPool(ptr, size, ptr_max, pool, new_max) DN_TArrayGrowFromPool(&(ptr), size, ptr_max, pool, new_max) + #define DN_PArrayGrowFromArena(ptr, size, ptr_max, arena, new_max) DN_TArrayGrowFromArena(&(ptr), size, ptr_max, arena, new_max) + #define DN_PArrayGrowIfNeededFromPool(ptr, size, ptr_max, pool, add_count) DN_TArrayGrowIfNeededFromPool(ptr, size, ptr_max, pool, add_count) + #define DN_PArrayGrowIfNeededFromArena(ptr, size, ptr_max, arena, add_count) DN_TArrayGrowIfNeededFromArena(ptr, size, ptr_max, arena, add_count) + #define DN_PArrayMakeArray(ptr, ptr_size, max, count, z_mem) DN_TArrayMakeArray(ptr, ptr_size, max, count, z_mem) + #define DN_PArrayMakeArrayZ(ptr, ptr_size, max, count) DN_TArrayMakeArray(ptr, ptr_size, max, count, DN_ZMem_Yes) + #define DN_PArrayMake(ptr, ptr_size, max, z_mem) DN_TArrayMakeArray(ptr, ptr_size, max, 1, z_mem) + #define DN_PArrayMakeZ(ptr, ptr_size, max) DN_TArrayMakeArray(ptr, ptr_size, max, 1, DN_ZMem_Yes) + #define DN_PArrayAddArray(ptr, ptr_size, max, items, count, add) DN_TArrayAddArray(ptr, ptr_size, max, items, count, add) + #define DN_PArrayAdd(ptr, ptr_size, max, item, add) DN_TArrayAddArray(ptr, ptr_size, max, &item, 1, add) + #define DN_PArrayAppendArray(ptr, ptr_size, max, items, count) DN_TArrayAddArray(ptr, ptr_size, max, items, count, DN_ArrayAdd_Append) + #define DN_PArrayAppend(ptr, ptr_size, max, item) DN_TArrayAddArray(ptr, ptr_size, max, &item, 1, DN_ArrayAdd_Append) + #define DN_PArrayPrependArray(ptr, ptr_size, max, items, count) DN_TArrayAddArray(ptr, ptr_size, max, items, count, DN_ArrayAdd_Prepend) + #define DN_PArrayPrepend(ptr, ptr_size, max, item) DN_TArrayAddArray(ptr, ptr_size, max, &item, 1, DN_ArrayAdd_Prepend) + #define DN_PArrayEraseRange(ptr, ptr_size, begin_index, count, erase) DN_TArrayEraseRange(ptr, ptr_size, begin_index, count, erase) + #define DN_PArrayErase(ptr, ptr_size, index, erase) DN_TArrayEraseRange(ptr, ptr_size, index, 1, erase) + #define DN_PArrayInsertArray(ptr, ptr_size, max, index, items, count) DN_TArrayInsertArray(ptr, ptr_size, max, index, items, count) + #define DN_PArrayInsert(ptr, ptr_size, max, index, item) DN_TArrayInsertArray(ptr, ptr_size, max, index, &item, 1) + #define DN_PArrayPopFront(ptr, ptr_size, max, count) DN_TArrayPopFront(ptr, ptr_size, count) + #define DN_PArrayPopBack(ptr, ptr_size, max, count) DN_TArrayPopBack(ptr, ptr_size, count) +#else + #define DN_PArrayFind(ptr, size, ptr_find, eq_func) DN_ArrayFind(ptr, size, sizeof(*(ptr)), ptr_find, eq_func) + #define DN_PArrayFindMemEq(ptr, size, ptr_find) DN_ArrayFindMemEq(ptr, size, sizeof(*(ptr)), ptr_find) + #define DN_PArrayResizeFromPool(ptr, ptr_size, ptr_max, pool, new_max) DN_ArrayResizeFromPool((void **)&(ptr), ptr_size, ptr_max, sizeof((ptr)[0]), pool, new_max) + #define DN_PArrayResizeFromArena(ptr, ptr_size, ptr_max, arena, new_max) DN_ArrayResizeFromArena((void **)&(ptr), ptr_size, ptr_max, sizeof((ptr)[0]), arena, new_max) + #define DN_PArrayGrowFromPool(ptr, size, ptr_max, pool, new_max) DN_ArrayGrowFromPool((void **)&(ptr), size, ptr_max, sizeof((ptr)[0]), pool, new_max) + #define DN_PArrayGrowFromArena(ptr, size, ptr_max, arena, new_max) DN_ArrayGrowFromArena((void **)&(ptr), size, ptr_max, sizeof((ptr)[0]), arena, new_max) + #define DN_PArrayGrowIfNeededFromPool(ptr, size, ptr_max, pool, add_count) DN_ArrayGrowIfNeededFromPool((void **)(ptr), size, ptr_max, sizeof((*ptr)[0]), pool, add_count) + #define DN_PArrayGrowIfNeededFromArena(ptr, size, ptr_max, arena, add_count) DN_ArrayGrowIfNeededFromArena((void **)(ptr), size, ptr_max, sizeof((*ptr)[0]), arena, add_count) + #define DN_PArrayMakeArray(ptr, ptr_size, max, count, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), count, z_mem) + #define DN_PArrayMakeArrayZ(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_Yes) + #define DN_PArrayMake(ptr, ptr_size, max, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), 1, z_mem) + #define DN_PArrayMakeZ(ptr, ptr_size, max) (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_Yes) + #define DN_PArrayAddArray(ptr, ptr_size, max, items, count, add) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, add) + #define DN_PArrayAdd(ptr, ptr_size, max, item, add) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, add) + #define DN_PArrayAppendArray(ptr, ptr_size, max, items, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, DN_ArrayAdd_Append) + #define DN_PArrayAppend(ptr, ptr_size, max, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, DN_ArrayAdd_Append) + #define DN_PArrayPrependArray(ptr, ptr_size, max, items, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, DN_ArrayAdd_Prepend) + #define DN_PArrayPrepend(ptr, ptr_size, max, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, DN_ArrayAdd_Prepend) + #define DN_PArrayEraseRange(ptr, ptr_size, begin_index, count, erase) DN_ArrayEraseRange(ptr, ptr_size, sizeof((ptr)[0]), begin_index, count, erase) + #define DN_PArrayErase(ptr, ptr_size, index, erase) DN_ArrayEraseRange(ptr, ptr_size, sizeof((ptr)[0]), index, 1, erase) + #define DN_PArrayInsertArray(ptr, ptr_size, max, index, items, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayInsertArray(ptr, ptr_size, max, sizeof((ptr)[0]), index, items, count) + #define DN_PArrayInsert(ptr, ptr_size, max, index, item) (DN_CppDeclType(&(ptr)[0]))DN_ArrayInsertArray(ptr, ptr_size, max, sizeof((ptr)[0]), index, &item, 1) + #define DN_PArrayPopFront(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayPopFront(ptr, ptr_size, sizeof((ptr)[0]), count) + #define DN_PArrayPopBack(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_ArrayPopBack(ptr, ptr_size, sizeof((ptr)[0]), count) +#endif + +#define DN_LArrayFind(c_array, size, ptr_find, eq_func) DN_PArrayFind(c_array, size, ptr_find, eq_func) +#define DN_LArrayFindMemEq(c_array, size, ptr_find) DN_PArrayFindMemEq(c_array, size, ptr_find) +#define DN_LArrayResizeFromPool(c_array, size, pool, new_max) DN_PArrayResizeFromPool(c_array, size, DN_ArrayCountU(c_array), pool, new_max) +#define DN_LArrayResizeFromArena(c_array, size, arena, new_max) DN_PArrayResizeFromArena(c_array, size, DN_ArrayCountU(c_array), arena, new_max) +#define DN_LArrayGrowFromPool(c_array, size, pool, new_max) DN_PArrayGrowFromPool(c_array, size, DN_ArrayCountU(c_array), pool, new_max) +#define DN_LArrayGrowFromArena(c_array, size, arena, new_max) DN_PArrayGrowFromArena(c_array, size, DN_ArrayCountU(c_array), arena, new_max) +#define DN_LArrayGrowIfNeededFromPool(c_array, size, pool, add_count) DN_PArrayGrowIfNeededFromPool(c_array, size, DN_ArrayCountU(c_array), pool, add_count) +#define DN_LArrayGrowIfNeededFromArena(c_array, size, arena, add_count) DN_PArrayGrowIfNeededFromArena(c_array, size, DN_ArrayCountU(c_array), arena, add_count) +#define DN_LArrayMakeArray(c_array, ptr_size, count, z_mem) DN_PArrayMakeArray(c_array, ptr_size, DN_ArrayCountU(c_array), count, z_mem) +#define DN_LArrayMakeArrayZ(c_array, ptr_size, count) DN_PArrayMakeArrayZ(c_array, ptr_size, DN_ArrayCountU(c_array), count) +#define DN_LArrayMake(c_array, ptr_size, z_mem) DN_PArrayMake(c_array, ptr_size, DN_ArrayCountU(c_array), z_mem) +#define DN_LArrayMakeZ(c_array, ptr_size) DN_PArrayMakeZ(c_array, ptr_size, DN_ArrayCountU(c_array)) +#define DN_LArrayAddArray(c_array, ptr_size, items, count, add) DN_PArrayAddArray(c_array, ptr_size, DN_ArrayCountU(c_array), items, count, add) +#define DN_LArrayAdd(c_array, ptr_size, item, add) DN_PArrayAdd(c_array, ptr_size, DN_ArrayCountU(c_array), item, add) +#define DN_LArrayAppendArray(c_array, ptr_size, items, count) DN_PArrayAppendArray(c_array, ptr_size, DN_ArrayCountU(c_array), items, count) +#define DN_LArrayAppend(c_array, ptr_size, item) DN_PArrayAppend(c_array, ptr_size, DN_ArrayCountU(c_array), item) +#define DN_LArrayPrependArray(c_array, ptr_size, items, count) DN_PArrayPrependArray(c_array, ptr_size, DN_ArrayCountU(c_array), items, count) +#define DN_LArrayPrepend(c_array, ptr_size, item) DN_PArrayPrepend(c_array, ptr_size, DN_ArrayCountU(c_array), item) +#define DN_LArrayEraseRange(c_array, ptr_size, begin_index, count, erase) DN_PArrayEraseRange(c_array, ptr_size, begin_index, count, erase) +#define DN_LArrayErase(c_array, ptr_size, index, erase) DN_PArrayErase(c_array, ptr_size, index, erase) +#define DN_LArrayInsertArray(c_array, ptr_size, index, items, count) DN_PArrayInsertArray(c_array, ptr_size, DN_ArrayCountU(c_array), index, items, count) +#define DN_LArrayInsert(c_array, ptr_size, index, item) DN_PArrayInsert(c_array, ptr_size, DN_ArrayCountU(c_array), index, item) +#define DN_LArrayPopFront(c_array, ptr_size, count) DN_PArrayPopFront(c_array, ptr_size, DN_ArrayCountU(c_array), count) +#define DN_LArrayPopBack(c_array, ptr_size, count) DN_PArrayPopBack(c_array, ptr_size, DN_ArrayCountU(c_array), count) + +#define DN_IArrayFind(ptr_array, ptr_find, eq_func) DN_PArrayFind((ptr_array)->data, (ptr_array)->count, ptr_find, eq_func) +#define DN_IArrayFindMemEq(ptr_array, ptr_find) DN_PArrayFindMemEq((ptr_array)->data, (ptr_array)->count, ptr_find) +#define DN_IArrayResizeFromPool(ptr_array, pool, new_max) DN_PArrayResizeFromPool((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, pool, new_max) +#define DN_IArrayResizeFromArena(ptr_array, arena, new_max) DN_PArrayResizeFromArena((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, arena, new_max) +#define DN_IArrayGrowFromPool(ptr_array, pool, new_max) DN_PArrayGrowFromPool((ptr_array)->data, (ptr_array)->count, &(ptr_array)->max, pool, new_max) +#define DN_IArrayGrowFromArena(ptr_array, arena, new_max) DN_PArrayGrowFromArena((ptr_array)->data, (ptr_array)->count, &(ptr_array)->max, arena, new_max) +#define DN_IArrayGrowIfNeededFromPool(ptr_array, pool, add_count) DN_PArrayGrowIfNeededFromPool(&(ptr_array)->data, (ptr_array)->count, &(ptr_array)->max, pool, add_count) +#define DN_IArrayGrowIfNeededFromArena(ptr_array, arena, add_count) DN_PArrayGrowIfNeededFromArena(&(ptr_array)->data, (ptr_array)->count, &(ptr_array)->max, arena, add_count) +#define DN_IArrayMakeArray(ptr_array, count, z_mem) DN_PArrayMakeArray((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, count, z_mem) +#define DN_IArrayMakeArrayZ(ptr_array, count) DN_PArrayMakeArrayZ((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, count) +#define DN_IArrayMake(ptr_array, z_mem) DN_PArrayMake((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, z_mem) +#define DN_IArrayMakeZ(ptr_array) DN_PArrayMakeZ((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max) +#define DN_IArrayAddArray(ptr_array, items, count, add) DN_PArrayAddArray((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, items, count, add) +#define DN_IArrayAdd(ptr_array, item, add) DN_PArrayAdd((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, item, add) +#define DN_IArrayAppendArray(ptr_array, items, count) DN_PArrayAppendArray((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, items, count) +#define DN_IArrayAppend(ptr_array, item) DN_PArrayAppend((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, item) +#define DN_IArrayPrependArray(ptr_array, items, count) DN_PArrayPrependArray((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, items, count) +#define DN_IArrayPrepend(ptr_array, item) DN_PArrayPrepend((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, item) +#define DN_IArrayEraseRange(ptr_array, begin_index, count, erase) DN_PArrayEraseRange((ptr_array)->data, &(ptr_array)->count, begin_index, count, erase) +#define DN_IArrayErase(ptr_array, index, erase) DN_PArrayErase((ptr_array)->data, &(ptr_array)->count, index, erase) +#define DN_IArrayInsertArray(ptr_array, index, items, count) DN_PArrayInsertArray((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, index, items, count) +#define DN_IArrayInsert(ptr_array, index, item) DN_PArrayInsert((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, index, item) +#define DN_IArrayPopFront(ptr_array, count) DN_PArrayPopFront((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, count) +#define DN_IArrayPopBack(ptr_array, count) DN_PArrayPopBack((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, count) + +// NOTE: Slices +// +// Fixed size container allocated up front that have a `T *data` and `DN_USize count` elements. +// +// API +// AllocArena: Allocates the container with the requested `count` elements +// +#define DN_ISliceAllocArena(slice_ptr, count_, zmem, arena) (DN_CppDeclType(&((slice_ptr)->data[0])))DN_SliceAllocArena((void **)&((slice_ptr)->data), &((slice_ptr)->count), count_, sizeof((slice_ptr)->data[0]), alignof(DN_CppDeclType((slice_ptr)->data[0])), zmem, arena) + + +DN_API void* DN_SliceAllocArena (void **data, DN_USize *slice_size_field, DN_USize size, DN_USize elem_size, DN_U8 align, DN_ZMem zmem, DN_Arena *arena); + +DN_API DN_ArrayFindResult DN_ArrayFind (void *data, DN_USize size, DN_USize elem_size, void const *find, DN_ArrayFindEqFunc *eq_func); +DN_API DN_ArrayFindResult DN_ArrayFindMemEq (void *data, DN_USize size, DN_USize elem_size, void const *find); +DN_API void* DN_ArrayInsertArray (void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize index, void const *items, DN_USize count); +DN_API void* DN_ArrayPopFront (void *data, DN_USize *size, DN_USize elem_size, DN_USize count); +DN_API void* DN_ArrayPopBack (void *data, DN_USize *size, DN_USize elem_size, DN_USize count); +DN_API DN_ArrayEraseResult DN_ArrayEraseRange (void *data, DN_USize *size, DN_USize elem_size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase); +DN_API void* DN_ArrayMakeArray (void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize make_count, DN_ZMem z_mem); +DN_API void* DN_ArrayAddArray (void *data, DN_USize *size, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add); +DN_API bool DN_ArrayResizeFromPool (void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max); +DN_API bool DN_ArrayResizeFromArena (void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max); +DN_API bool DN_ArrayGrowFromPool (void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max); +DN_API bool DN_ArrayGrowFromArena (void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max); +DN_API bool DN_ArrayGrowIfNeededFromPool (void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize add_count); +DN_API bool DN_ArrayGrowIfNeededFromArena (void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize add_count); + +#if defined (__cplusplus) +template DN_ArrayFindResult DN_TArrayFind (T *data, DN_USize size, void const *find, DN_ArrayFindEqFunc *eq_func); +template DN_ArrayFindResult DN_TArrayFindMemEq (T *data, DN_USize size, void const *find, DN_ArrayFindEqFunc *eq_func); +template T* DN_TArrayInsertArray (T *data, DN_USize *size, DN_USize max, DN_USize index, void const *items, DN_USize count); +template T* DN_TArrayPopFront (T *data, DN_USize *size, DN_USize count); +template T* DN_TArrayPopBack (T *data, DN_USize *size, DN_USize count); +template DN_ArrayEraseResult DN_TArrayEraseRange (T *data, DN_USize *size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase); +template T* DN_TArrayMakeArray (T *data, DN_USize *size, DN_USize max, DN_USize make_count, DN_ZMem z_mem); +template T* DN_TArrayAddArray (T *data, DN_USize *size, DN_USize max, T const *elems, DN_USize elems_count, DN_ArrayAdd add); +template bool DN_TArrayResizeFromPool (T **data, DN_USize *size, DN_USize *max, DN_Pool *pool, DN_USize new_max); +template bool DN_TArrayResizeFromArena (T **data, DN_USize *size, DN_USize *max, DN_Arena *arena, DN_USize new_max); +template bool DN_TArrayGrowFromPool (T **data, DN_USize size, DN_USize *max, DN_Pool *pool, DN_USize new_max); +template bool DN_TArrayGrowFromArena (T **data, DN_USize size, DN_USize *max, DN_Pool *pool, DN_USize new_max); +template bool DN_TArrayGrowIfNeededFromPool (T **data, DN_USize size, DN_USize *max, DN_Pool *pool, DN_USize add_count); +template bool DN_TArrayGrowIfNeededFromArena (T **data, DN_USize size, DN_USize *max, DN_Arena *pool, DN_USize add_count); +#endif + +DN_API void* DN_SinglyLLDetach (void **link, void **next); +DN_API bool DN_RingHasSpace (DN_Ring const *ring, DN_U64 size); +DN_API bool DN_RingHasData (DN_Ring const *ring, DN_U64 size); +DN_API void DN_RingWrite (DN_Ring *ring, void const *src, DN_U64 src_size); +#define DN_RingWriteStruct(ring, item) DN_RingWrite((ring), (item), sizeof(*(item))) +DN_API void DN_RingRead (DN_Ring *ring, void *dest, DN_U64 dest_size); +#define DN_RingReadStruct(ring, dest) DN_RingRead((ring), (dest), sizeof(*(dest))) + +DN_U32 const DN_DS_MAP_DEFAULT_HASH_SEED = 0x8a1ced49; +DN_U32 const DN_DS_MAP_SENTINEL_SLOT = 0; + +template DN_DSMap DN_DSMapInit (DN_Arena *arena, DN_U32 size, DN_DSMapFlags flags); +template void DN_DSMapDeinit (DN_DSMap *map, DN_ZMem z_mem); +template bool DN_DSMapIsValid (DN_DSMap const *map); +template DN_U32 DN_DSMapHash (DN_DSMap const *map, DN_DSMapKey key); +template DN_U32 DN_DSMapHashToSlotIndex (DN_DSMap const *map, DN_DSMapKey key); +template DN_DSMapResult DN_DSMapFind (DN_DSMap const *map, DN_DSMapKey key); +template DN_DSMapResult DN_DSMapMake (DN_DSMap *map, DN_DSMapKey key); +template DN_DSMapResult DN_DSMapSet (DN_DSMap *map, DN_DSMapKey key, T const &value); +template DN_DSMapResult DN_DSMapFindKeyU64 (DN_DSMap const *map, DN_U64 key); +template DN_DSMapResult DN_DSMapMakeKeyU64 (DN_DSMap *map, DN_U64 key); +template DN_DSMapResult DN_DSMapSetKeyU64 (DN_DSMap *map, DN_U64 key, T const &value); +template DN_DSMapResult DN_DSMapFindKeyStr8 (DN_DSMap const *map, DN_Str8 key); +template DN_DSMapResult DN_DSMapMakeKeyStr8 (DN_DSMap *map, DN_Str8 key); +template DN_DSMapResult DN_DSMapSetKeyStr8 (DN_DSMap *map, DN_Str8 key, T const &value); +template bool DN_DSMapResize (DN_DSMap *map, DN_U32 size); +template bool DN_DSMapErase (DN_DSMap *map, DN_DSMapKey key); +template bool DN_DSMapEraseKeyU64 (DN_DSMap *map, DN_U64 key); +template bool DN_DSMapEraseKeyStr8 (DN_DSMap *map, DN_Str8 key); +template DN_DSMapKey DN_DSMapKeyBuffer (DN_DSMap const *map, void const *data, DN_USize size); +template DN_DSMapKey DN_DSMapKeyBufferAsU64NoHash (DN_DSMap const *map, void const *data, DN_USize size); +template DN_DSMapKey DN_DSMapKeyU64 (DN_DSMap const *map, DN_U64 u64); +template DN_DSMapKey DN_DSMapKeyStr8 (DN_DSMap const *map, DN_Str8 string); +#define DN_DSMapKeyCStr8(map, string) DN_DSMapKeyBuffer(map, string, sizeof((string))/sizeof((string)[0]) - 1) +DN_API DN_DSMapKey DN_DSMapKeyU64NoHash (DN_U64 u64); +DN_API bool DN_DSMapKeyEquals (DN_DSMapKey lhs, DN_DSMapKey rhs); +DN_API bool operator== (DN_DSMapKey lhs, DN_DSMapKey rhs); + +#if defined(__cplusplus) +template +DN_ArrayFindResult DN_TArrayFind(T *data, DN_USize size, void const *find, DN_ArrayFindEqFunc *eq_func) +{ + DN_ArrayFindResult result = DN_ArrayFind(data, size, sizeof(*data), find, eq_func); + return result; +} + +template +DN_ArrayFindResult DN_TArrayFindMemEq(T *data, DN_USize size, void const *find) +{ + DN_ArrayFindResult result = DN_ArrayFindMemEq(data, size, sizeof(*data), find); + return result; +} + +template +T *DN_TArrayInsertArray(T *data, DN_USize *size, DN_USize max, DN_USize index, T const *items, DN_USize count) +{ + T *result = DN_Cast(T *)DN_ArrayInsertArray(data, size, max, sizeof(*data), index, items, count); + return result; +} + +template +T *DN_TArrayPopFront(T *data, DN_USize *size, DN_USize count) +{ + T *result = DN_Cast(T *)DN_ArrayPopFront(data, size, sizeof(*data), count); + return result; +} + +template +T *DN_TArrayPopBack(T *data, DN_USize *size, DN_USize count) +{ + T *result = DN_Cast(T *)DN_ArrayPopBack(data, size, sizeof(*data), count); + return result; +} + +template +DN_ArrayEraseResult DN_TArrayEraseRange(T *data, DN_USize *size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase) +{ + DN_ArrayEraseResult result = DN_ArrayEraseRange(data, size, sizeof(*data), begin_index, count, erase); + return result; +} + +template +T *DN_TArrayMakeArray(T *data, DN_USize *size, DN_USize max, DN_USize make_count, DN_ZMem z_mem) +{ + T *result = DN_Cast(T *)DN_ArrayMakeArray(data, size, max, sizeof(*data), make_count, z_mem); + return result; +} + +template +T *DN_TArrayAddArray(T *data, DN_USize *size, DN_USize max, T const *elems, DN_USize elems_count, DN_ArrayAdd add) +{ + T* result = DN_Cast(T *)DN_ArrayAddArray(data, size, max, sizeof(*elems), elems, elems_count, add); + return result; +} + +template +bool DN_TArrayResizeFromPool(T **data, DN_USize *size, DN_USize *max, DN_Pool *pool, DN_USize new_max) +{ + bool result = DN_ArrayResizeFromPool(DN_Cast(void **)data, size, max, sizeof(**data), pool, new_max); + return result; +} + +template +bool DN_TArrayResizeFromArena(T **data, DN_USize *size, DN_USize *max, DN_Arena *arena, DN_USize new_max) +{ + bool result = DN_ArrayResizeFromArena(DN_Cast(void **)data, size, max, sizeof(**data), arena, new_max); + return result; +} + +template +bool DN_TArrayGrowFromPool(T **data, DN_USize size, DN_USize *max, DN_Pool *pool, DN_USize new_max) +{ + bool result = DN_ArrayGrowFromPool(DN_Cast(void **)data, size, max, sizeof(**data), pool, new_max); + return result; +} + +template +bool DN_TArrayGrowFromArena(T **data, DN_USize size, DN_USize *max, DN_Arena *arena, DN_USize new_max) +{ + bool result = DN_ArrayGrowFromArena(DN_Cast(void **)data, size, max, sizeof(**data), arena, new_max); + return result; +} + +template +bool DN_TArrayGrowIfNeededFromPool(T **data, DN_USize size, DN_USize *max, DN_Pool *pool, DN_USize add_count) +{ + bool result = DN_ArrayGrowIfNeededFromPool(DN_Cast(void **)data, size, max, sizeof(**data), pool, add_count); + return result; +} + +template +bool DN_TArrayGrowIfNeededFromArena(T **data, DN_USize size, DN_USize *max, DN_Arena *arena, DN_USize add_count) +{ + bool result = DN_ArrayGrowIfNeededFromArena(DN_Cast(void **)data, size, max, sizeof(**data), arena, add_count); + return result; +} +#endif // defined(__cplusplus) #endif // !defined(DN_BASE_H) diff --git a/Source/Base/dn_base_assert.h b/Source/Base/dn_base_assert.h deleted file mode 100644 index 85793d3..0000000 --- a/Source/Base/dn_base_assert.h +++ /dev/null @@ -1,77 +0,0 @@ -#if !defined(DN_BASE_ASSERT_H) -#define DN_BASE_ASSERT_H - -#define DN_HardAssertF(expr, fmt, ...) \ - do { \ - if (!(expr)) { \ - DN_Str8 stack_trace_ = DN_StackTraceWalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \ - DN_LogErrorF("Hard assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \ - DN_Str8PrintFmt(stack_trace_), \ - ##__VA_ARGS__); \ - DN_DebugBreak; \ - } \ - } while (0) -#define DN_HardAssert(expr) DN_HardAssertF(expr, "") - -// NOTE: Our default assert requires stack traces which has a bit of a chicken-and-egg problem if -// we're trying to detect some code related to the DN startup sequence. If we try to assert before -// the OS layer is initialised stack-traces will try to use temporary memory which requires TLS to -// be setup which belongs to the OS. -// -// This causes recursion errors as they call into each other. We use RawAsserts for these kind of -// checks. -#if defined(DN_NO_ASSERT) - #define DN_RawAssert(...) - #define DN_Assert(...) - #define DN_AssertOnce(...) - #define DN_AssertF(...) - #define DN_AssertFOnce(...) -#else - #define DN_RawAssert(expr) do { if (!(expr)) DN_DebugBreak; } while (0) - - #define DN_AssertF(expr, fmt, ...) \ - do { \ - if (!(expr)) { \ - DN_Str8 stack_trace_ = DN_StackTraceWalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \ - DN_LogErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \ - DN_Str8PrintFmt(stack_trace_), \ - ##__VA_ARGS__); \ - DN_DebugBreak; \ - } \ - } while (0) - - #define DN_AssertFOnce(expr, fmt, ...) \ - do { \ - static bool once = true; \ - if (!(expr) && once) { \ - once = false; \ - DN_Str8 stack_trace_ = DN_StackTraceWalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \ - DN_LogErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \ - DN_Str8PrintFmt(stack_trace_), \ - ##__VA_ARGS__); \ - DN_DebugBreak; \ - } \ - } while (0) - - #define DN_Assert(expr) DN_AssertF((expr), "") - #define DN_AssertOnce(expr) DN_AssertFOnce((expr), "") -#endif - -#define DN_InvalidCodePathF(fmt, ...) DN_HardAssertF(0, fmt, ##__VA_ARGS__) -#define DN_InvalidCodePath DN_InvalidCodePathF("Invalid code path triggered") -#define DN_StaticAssert(expr) \ - DN_GCC_WARNING_PUSH \ - DN_GCC_WARNING_DISABLE(-Wunused-local-typedefs) \ - typedef char DN_TokenCombine(static_assert_dummy__, __LINE__)[(expr) ? 1 : -1]; \ - DN_GCC_WARNING_POP - -#define DN_Check(expr) DN_CheckF(expr, "") -#if defined(DN_NO_CHECK_BREAK) - #define DN_CheckF(expr, fmt, ...) \ - ((expr) ? true : (DN_LogWarningF(fmt, ##__VA_ARGS__), false)) -#else - #define DN_CheckF(expr, fmt, ...) \ - ((expr) ? true : (DN_LogErrorF(fmt, ##__VA_ARGS__), DN_StackTracePrint(128 /*limit*/), DN_DebugBreak, false)) -#endif - -#endif diff --git a/Source/Base/dn_base_containers.cpp b/Source/Base/dn_base_containers.cpp index 681affa..a8bd93d 100644 --- a/Source/Base/dn_base_containers.cpp +++ b/Source/Base/dn_base_containers.cpp @@ -2,6 +2,13 @@ #include "../dn.h" #endif +struct DN_ArrayFindEqMemcmpContext_ +{ + DN_USize elem_size; + void const *find; +}; + + DN_API void *DN_SliceAllocArena(void **data, DN_USize *slice_size_field, DN_USize size, DN_USize elem_size, DN_U8 align, DN_ZMem zmem, DN_Arena *arena) { void *result = *data; @@ -11,7 +18,42 @@ DN_API void *DN_SliceAllocArena(void **data, DN_USize *slice_size_field, DN_USiz return result; } -DN_API void *DN_CArrayInsertArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize index, void const *items, DN_USize count) +DN_API DN_ArrayFindResult DN_ArrayFind(void *data, DN_USize size, DN_USize elem_size, void const *find, DN_ArrayFindEqFunc *eq_func) +{ + DN_ArrayFindResult result = {}; + DN_Assert(data); + DN_Assert(elem_size); + if (find) { + for (DN_ForIndexU(index, size)) { + DN_U8 *it = DN_Cast(DN_U8 *) data + (index * elem_size); + if (eq_func(it, find)) { + result.index = index; + result.value = it; + result.success = true; + break; + } + } + } + return result; +} + +static bool DN_ArrayFindEqMemEqUnsafe_(void const *lhs, void const *find) +{ + DN_ArrayFindEqMemcmpContext_ *context = DN_Cast(DN_ArrayFindEqMemcmpContext_ *) find; + bool result = DN_MemEqUnsafe(lhs, context->find, context->elem_size); + return result; +} + +DN_API DN_ArrayFindResult DN_ArrayFindMemEq(void *data, DN_USize size, DN_USize elem_size, void const *find) +{ + DN_ArrayFindEqMemcmpContext_ context = {}; + context.elem_size = elem_size; + context.find = find; + DN_ArrayFindResult result = DN_ArrayFind(data, size, elem_size, &context, DN_ArrayFindEqMemEqUnsafe_); + return result; +} + +DN_API void *DN_ArrayInsertArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize index, void const *items, DN_USize count) { void *result = nullptr; if (!data || !size || !items || count <= 0 || ((*size + count) > max)) @@ -32,7 +74,7 @@ DN_API void *DN_CArrayInsertArray(void *data, DN_USize *size, DN_USize max, DN_U return result; } -DN_API void *DN_CArrayPopFront(void *data, DN_USize *size, DN_USize elem_size, DN_USize count) +DN_API void *DN_ArrayPopFront(void *data, DN_USize *size, DN_USize elem_size, DN_USize count) { if (!data || !size || *size == 0 || count == 0) return nullptr; @@ -51,7 +93,7 @@ DN_API void *DN_CArrayPopFront(void *data, DN_USize *size, DN_USize elem_size, D return result; } -DN_API void *DN_CArrayPopBack(void *data, DN_USize *size, DN_USize elem_size, DN_USize count) +DN_API void *DN_ArrayPopBack(void *data, DN_USize *size, DN_USize elem_size, DN_USize count) { if (!data || !size || *size == 0 || count == 0) return nullptr; @@ -62,7 +104,7 @@ DN_API void *DN_CArrayPopBack(void *data, DN_USize *size, DN_USize elem_size, DN return DN_Cast(char *)data + (*size * elem_size); } -DN_API DN_ArrayEraseResult DN_CArrayEraseRange(void *data, DN_USize *size, DN_USize elem_size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase) +DN_API DN_ArrayEraseResult DN_ArrayEraseRange(void *data, DN_USize *size, DN_USize elem_size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase) { DN_ArrayEraseResult result = {}; if (!data || !size || *size == 0 || count == 0) @@ -104,48 +146,47 @@ DN_API DN_ArrayEraseResult DN_CArrayEraseRange(void *data, DN_USize *size, DN_US } result.items_erased = erase_count; - result.it_index = start; + result.it_index = start ? start - 1 : start; return result; } -DN_API void *DN_CArrayMakeArray(void *data, DN_USize *size, DN_USize max, DN_USize data_size, DN_USize make_size, DN_ZMem z_mem) +DN_API void *DN_ArrayMakeArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize make_count, DN_ZMem z_mem) { - void *result = nullptr; - DN_USize new_size = *size + make_size; + void *result = nullptr; + DN_USize new_size = *size + make_count; if (new_size <= max) { - result = DN_Cast(char *) data + (data_size * size[0]); + result = DN_Cast(char *) data + (elem_size * size[0]); *size = new_size; if (z_mem == DN_ZMem_Yes) - DN_Memset(result, 0, data_size * make_size); + DN_Memset(result, 0, elem_size * make_count); } - return result; } -DN_API void *DN_CArrayAddArray(void *data, DN_USize *size, DN_USize max, DN_USize data_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add) +DN_API void *DN_ArrayAddArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add) { - void *result = DN_CArrayMakeArray(data, size, max, data_size, elems_count, DN_ZMem_No); + void *result = DN_ArrayMakeArray(data, size, max, elem_size, elems_count, DN_ZMem_No); if (result) { if (add == DN_ArrayAdd_Append) { - DN_Memcpy(result, elems, elems_count * data_size); + DN_Memcpy(result, elems, elems_count * elem_size); } else { - char *move_dest = DN_Cast(char *)data + (elems_count * data_size); // Shift elements forward + char *move_dest = DN_Cast(char *)data + (elems_count * elem_size); // Shift elements forward char *move_src = DN_Cast(char *)data; - DN_Memmove(move_dest, move_src, data_size * size[0]); - DN_Memcpy(data, elems, data_size * elems_count); + DN_Memmove(move_dest, move_src, elem_size * size[0]); + DN_Memcpy(data, elems, elem_size * elems_count); } } return result; } -DN_API bool DN_CArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize new_max) +DN_API bool DN_ArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max) { bool result = true; if (new_max != *max) { - DN_USize bytes_to_alloc = data_size * new_max; + DN_USize bytes_to_alloc = elem_size * new_max; void *buffer = DN_PoolNewArray(pool, DN_U8, bytes_to_alloc); if (buffer) { - DN_USize bytes_to_copy = data_size * DN_Min(*size, new_max); + DN_USize bytes_to_copy = elem_size * DN_Min(*size, new_max); DN_Memcpy(buffer, *data, bytes_to_copy); DN_PoolDealloc(pool, *data); *data = buffer; @@ -159,14 +200,14 @@ DN_API bool DN_CArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, return result; } -DN_API bool DN_CArrayResizeFromPool(void **data, DN_USize *size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize new_max) +DN_API bool DN_ArrayResizeFromPool(void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max) { bool result = true; if (new_max != *max) { - DN_USize bytes_to_alloc = data_size * new_max; + DN_USize bytes_to_alloc = elem_size * new_max; void *buffer = DN_PoolNewArray(pool, DN_U8, bytes_to_alloc); if (buffer) { - DN_USize bytes_to_copy = data_size * DN_Min(*size, new_max); + DN_USize bytes_to_copy = elem_size * DN_Min(*size, new_max); DN_Memcpy(buffer, *data, bytes_to_copy); DN_PoolDealloc(pool, *data); *data = buffer; @@ -180,14 +221,14 @@ DN_API bool DN_CArrayResizeFromPool(void **data, DN_USize *size, DN_USize *max, return result; } -DN_API bool DN_CArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, DN_USize data_size, DN_Arena *arena, DN_USize new_max) +DN_API bool DN_ArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max) { bool result = true; if (new_max != *max) { - DN_USize bytes_to_alloc = data_size * new_max; + DN_USize bytes_to_alloc = elem_size * new_max; void *buffer = DN_ArenaNewArray(arena, DN_U8, bytes_to_alloc, DN_ZMem_No); if (buffer) { - DN_USize bytes_to_copy = data_size * DN_Min(*size, new_max); + DN_USize bytes_to_copy = elem_size * DN_Min(*size, new_max); DN_Memcpy(buffer, *data, bytes_to_copy); *data = buffer; *max = new_max; @@ -200,41 +241,41 @@ DN_API bool DN_CArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, return result; } -DN_API bool DN_CArrayGrowFromPool(void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize new_max) +DN_API bool DN_ArrayGrowFromPool(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max) { bool result = true; if (new_max > *max) - result = DN_CArrayResizeFromPool(data, &size, max, data_size, pool, new_max); + result = DN_ArrayResizeFromPool(data, &size, max, elem_size, pool, new_max); return result; } -DN_API bool DN_CArrayGrowFromArena(void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Arena *arena, DN_USize new_max) +DN_API bool DN_ArrayGrowFromArena(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max) { bool result = true; if (new_max > *max) - result = DN_CArrayResizeFromArena(data, &size, max, data_size, arena, new_max); + result = DN_ArrayResizeFromArena(data, &size, max, elem_size, arena, new_max); return result; } -DN_API bool DN_CArrayGrowIfNeededFromPool(void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize add_count) +DN_API bool DN_ArrayGrowIfNeededFromPool(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize add_count) { bool result = true; DN_USize new_size = size + add_count; if (new_size > *max) { DN_USize new_max = DN_Max(DN_Max(*max * 2, new_size), 8); - result = DN_CArrayResizeFromPool(data, &size, max, data_size, pool, new_max); + result = DN_ArrayResizeFromPool(data, &size, max, elem_size, pool, new_max); } return result; } -DN_API bool DN_CArrayGrowIfNeededFromArena(void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Arena *arena, DN_USize add_count) +DN_API bool DN_ArrayGrowIfNeededFromArena(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize add_count) { bool result = true; DN_USize new_size = size + add_count; if (new_size > *max) { DN_USize new_max = DN_Max(DN_Max(*max * 2, new_size), 8); - result = DN_CArrayResizeFromArena(data, &size, max, data_size, arena, new_max); + result = DN_ArrayResizeFromArena(data, &size, max, elem_size, arena, new_max); } return result; } diff --git a/Source/Base/dn_base_containers.h b/Source/Base/dn_base_containers.h deleted file mode 100644 index 436c402..0000000 --- a/Source/Base/dn_base_containers.h +++ /dev/null @@ -1,463 +0,0 @@ -#if !defined(DN_CONTAINERS_H) -#define DN_CONTAINERS_H -// Containers that are imlpemented using primarily macros for operating on data structures that are -// embedded into a C style struct or from a set of defined variables from the available scope. Keep -// it stupid simple, structs and functions. Minimal amount of container types with flexible -// construction leads to less duplicated container code and less template meta-programming. -// -// Arrays -// -// Data structures that have a `T *data`, `DN_USize count` and `DN_USize max` capacity that can be -// dynamically shrunk or expanded. -// -// API -// ResizeFrom: Resizes the array to `new_max` erase elements if resizing to a smaller size -// GrowFrom: Expands the capacity of the array if `new_max > array.max` otherwise no-op -// GrowIfNeeded: Expands the capacity of the array if `array.size + add_count > array.max` otherwise no-op -// -// Variants -// PArray => Pointer (to) Array -// LArray => Literal Array -// Define a C array and size. (P) array macros take a pointer to the aray, its size and its max -// capacity. The (L) array macros take the literal array and derives the max capacity -// automatically using DN_ArrayCountU(l_array). -// -// MyStruct buffer[TB_ASType_Count] = {}; -// DN_USize size = 0; -// MyStruct *item_0 = DN_PArrayMake(buffer, &size, DN_ArrayCountU(buffer), DN_ZMem_No); -// MyStruct *item_1 = DN_LArrayMake(buffer, &size, DN_ZMem_No); -// -// IArray => Intrusive Array -// Define a struct with the members `data`, `size` and `max`: -// -// struct MyArray { -// MyStruct *data; -// DN_USize size; -// DN_USize max; -// } my_array = {}; -// DN_Arena arena = {}; -// DN_IArrayResizeFromArena(&my_array, &arena, 256); -// MyStruct *item = DN_IArrayMake(&my_array, DN_ZMem_No); -// -// Slices -// -// Fixed size container allocated up front that have a `T *data` and `DN_USize count` elements. -// -// API -// AllocArena: Allocates the container with the requested `count` elements -// -// ISinglyLL => Intrusive Singly Linked List -// Define a struct with the members `next`: -// -// struct MyLinkItem { -// int data; -// MyLinkItem *next; -// } my_link = {}; -// -// MyLinkItem *first_item = DN_ISinglyLLDetach(&my_link, MyLinkItem); -// -// DoublyLL => Doubly Linked List -// Define a struct with the members `next` and `prev`. This list has null pointers for head->prev -// and tail->next. -// -// struct MyLinkItem { -// int data; -// MyLinkItem *next; -// MyLinkItem *prev; -// } my_link = {}; -// -// MyLinkItem first_item = {}, second_item = {}; -// DN_DoublyLLAppend(&first_item, &second_item); // first_item -> second_item -// -// SentinelDoublyLL => Sentinel Doubly Linked List -// Uses a sentinel/dummy node as the list head. The sentinel points to itself when empty. -// Define a struct with the members `next` and `prev`: -// -// struct MyLinkItem { -// int data; -// MyLinkItem *next; -// MyLinkItem *prev; -// } my_list = {}; -// -// DN_SentinelDoublyLLInit(&my_list); -// DN_SentinelDoublyLLAppend(&my_list, &new_item); -// DN_SentinelDoublyLLForEach(it, &my_list) { /* ... */ } -// -// SinglyHeadTailLL => Singly Linked List with Head and Tail pointer (or First and Last pointer) -/* - struct MyLinkItem { - int data; - MyLinkItem *next; - } my_list = {}; - - struct MyContainer { - MyLinkItem *head; - MyLinkItem *tail; - }; - - MyLinkItem item = {}; - MyContainer container = {}; - DN_ISinglyHeadTailLLAppend(container, item); - // ... or alternatively, DN_SinglyHeadTailLLAppend(container.head, container.tail, item); - - for (MyLinkItem *it = container.head; it; it = it->next) { } -*/ - -#if defined(_CLANGD) - #include "../dn.h" -#endif - -struct DN_Ring -{ - DN_U64 size; - char *base; - DN_U64 write_pos; - DN_U64 read_pos; -}; - -enum DN_ArrayErase -{ - DN_ArrayErase_Unstable, - DN_ArrayErase_Stable, -}; - -enum DN_ArrayAdd -{ - DN_ArrayAdd_Append, - DN_ArrayAdd_Prepend, -}; - -struct DN_ArrayEraseResult -{ - // The next index your for-index should be set to such that you can continue - // to iterate the remainder of the array, e.g: - // - // for (DN_USize index = 0; index < array.size; index++) { - // if (erase) - // index = DN_FArray_EraseRange(&array, index, -3, DN_ArrayErase_Unstable); - // } - DN_USize it_index; - DN_USize items_erased; // The number of items erased -}; - -template struct DN_ArrayFindResult -{ - T *data; // Pointer to the value if a match is found, null pointer otherwise - DN_USize index; // Index to the value if a match is found, 0 otherwise -}; - -enum DN_DSMapKeyType -{ - // Key | Key Hash | Map Index - DN_DSMapKeyType_Invalid, - DN_DSMapKeyType_U64, // U64 | Hash(U64) | Hash(U64) % map_size - DN_DSMapKeyType_U64NoHash, // U64 | U64 | U64 % map_size - DN_DSMapKeyType_Buffer, // Buffer | Hash(buffer) | Hash(buffer) % map_size - DN_DSMapKeyType_BufferAsU64NoHash, // Buffer | U64(buffer[0:4]) | U64(buffer[0:4]) % map_size -}; - -struct DN_DSMapKey -{ - DN_DSMapKeyType type; - DN_U32 hash; // Hash to lookup in the map. If it equals, we check that the original key payload matches - void const *buffer_data; - DN_U32 buffer_size; - DN_U64 u64; - bool no_copy_buffer; -}; - -template -struct DN_DSMapSlot -{ - DN_DSMapKey key; // Hash table lookup key - T value; // Hash table value -}; - -typedef DN_U32 DN_DSMapFlags; -enum DN_DSMapFlags_ -{ - DN_DSMapFlags_Nil = 0, - DN_DSMapFlags_DontFreeArenaOnResize = 1 << 0, -}; - -using DN_DSMapHashFunction = DN_U32(DN_DSMapKey key, DN_U32 seed); -template struct DN_DSMap -{ - DN_U32 *hash_to_slot; // Mapping from hash to a index in the slots array - DN_DSMapSlot *slots; // Values of the array stored contiguously, non-sorted order - DN_U32 size; // Total capacity of the map and is a power of two - DN_U32 occupied; // Number of slots used in the hash table - DN_Arena *arena; // Backing arena for the hash table - DN_Pool pool; // Allocator for keys that are variable-sized buffers - DN_U32 initial_size; // Initial map size, map cannot shrink on erase below this size - DN_DSMapHashFunction *hash_function; // Custom hashing function to use if field is set - DN_U32 hash_seed; // Seed for the hashing function, when 0, DN_DS_MAP_DEFAULT_HASH_SEED is used - DN_DSMapFlags flags; -}; - -template struct DN_DSMapResult -{ - bool found; - DN_DSMapSlot *slot; - T *value; -}; - -#define DN_ISinglyLLDetach(list) (decltype(list))DN_SinglyLLDetach((void **)&(list), (void **)&(list)->next) - -#define DN_SinglyHeadTailLLAppend(head, tail, to_append) \ - do { \ - if (!head) \ - head = to_append; \ - if (tail) \ - tail->next = to_append; \ - tail = to_append; \ - } while (0) -#define DN_ISinglyHeadTailLLAppend(container_ptr, to_append) DN_SinglyHeadTailLLAppend((container_ptr)->head, (container_ptr)->tail, to_append) - -#define DN_SentinelDoublyLLInit(list) (list)->next = (list)->prev = (list) -#define DN_SentinelDoublyLLIsSentinel(list, item) ((list) == (item)) -#define DN_SentinelDoublyLLIsEmpty(list) (!(list) || ((list) == (list)->next)) -#define DN_SentinelDoublyLLIsInit(list) ((list)->next && (list)->prev) -#define DN_SentinelDoublyLLHasItems(list) ((list) && ((list) != (list)->next)) -#define DN_SentinelDoublyLLForEach(it, list) auto *it = (list)->next; (it) != (list); (it) = (it)->next - -#define DN_SentinelDoublyLLInitArena(list, T, ptr_arena) \ - do { \ - (list) = DN_ArenaNew(ptr_arena, T, DN_ZMem_Yes); \ - DN_SentinelDoublyLLInit(list); \ - } while (0) - -#define DN_SentinelDoublyLLInitPool(list, T, pool) \ - do { \ - (list) = DN_PoolNew(pool, T); \ - DN_SentinelDoublyLLInit(list); \ - } while (0) - -#define DN_SentinelDoublyLLDetach(item) \ - do { \ - if (item) { \ - (item)->prev->next = (item)->next; \ - (item)->next->prev = (item)->prev; \ - (item)->next = nullptr; \ - (item)->prev = nullptr; \ - } \ - } while (0) - -#define DN_SentinelDoublyLLDequeue(list, dest_ptr) \ - if (DN_SentinelDoublyLLHasItems(list)) { \ - dest_ptr = (list)->next; \ - DN_SentinelDoublyLLDetach(dest_ptr); \ - } - -#define DN_SentinelDoublyLLAppend(list, item) \ - do { \ - if (item) { \ - if ((item)->next) \ - DN_SentinelDoublyLLDetach(item); \ - (item)->next = (list)->next; \ - (item)->prev = (list); \ - (item)->next->prev = (item); \ - (item)->prev->next = (item); \ - } \ - } while (0) - -#define DN_SentinelDoublyLLPrepend(list, item) \ - do { \ - if (item) { \ - if ((item)->next) \ - DN_SentinelDoublyLLDetach(item); \ - (item)->next = (list); \ - (item)->prev = (list)->prev; \ - (item)->next->prev = (item); \ - (item)->prev->next = (item); \ - } \ - } while (0) - -// DoublyLL => Non-intrusive Doubly Linked List -// A simple doubly linked list where each node has `next` and `prev` pointers. -// The head is passed as a pointer-to-pointer to allow head updates. -// -// struct MyLinkItem { -// int data; -// MyLinkItem *next; -// MyLinkItem *prev; -// } *head = nullptr; -// DN_DoublyLLAppend(&head, new_item); -// for (MyLinkItem *it = head; it; it = it->next) { /* ... */ } - -#define DN_DoublyLLDetach(head, ptr) \ - do { \ - if ((head) && (head) == (ptr)) \ - (head) = (head)->next; \ - if ((ptr)) { \ - if ((ptr)->next) \ - (ptr)->next->prev = (ptr)->prev; \ - if ((ptr)->prev) \ - (ptr)->prev->next = (ptr)->next; \ - (ptr)->prev = (ptr)->next = 0; \ - } \ - } while (0) - -#define DN_DoublyLLAppend(head, ptr) \ - do { \ - if ((ptr)) { \ - DN_AssertF((ptr)->prev == 0 && (ptr)->next == 0, "Detach the pointer first"); \ - (ptr)->prev = (head); \ - (ptr)->next = 0; \ - if ((head)) { \ - (ptr)->next = (head)->next; \ - (head)->next = (ptr); \ - } else { \ - (head) = (ptr); \ - } \ - } \ - } while (0) - -#define DN_DoublyLLPrepend(head, ptr) \ - do { \ - if ((ptr)) { \ - DN_AssertF((ptr)->prev == 0 && (ptr)->next == 0, "Detach the pointer first"); \ - (ptr)->prev = nullptr; \ - (ptr)->next = (head); \ - if ((head)) { \ - (ptr)->prev = (head)->prev; \ - (head)->prev = (ptr); \ - } else { \ - (head) = (ptr); \ - } \ - } \ - } while (0) - -// NOTE: For C++ we need to cast the void* returned in these functions to the concrete type. In C, -// no cast is needed. -#if defined(__cplusplus) - #define DN_CppDeclType(x) decltype(x) -#else - #define DN_CppDeclType -#endif - -#define DN_PArrayResizeFromPool(ptr, ptr_size, ptr_max, pool, new_max) DN_CArrayResizeFromPool((void **)&(ptr), ptr_size, ptr_max, sizeof((ptr)[0]), pool, new_max) -#define DN_PArrayResizeFromArena(ptr, ptr_size, ptr_max, arena, new_max) DN_CArrayResizeFromArena((void **)&(ptr), ptr_size, ptr_max, sizeof((ptr)[0]), arena, new_max) -#define DN_PArrayGrowFromPool(ptr, size, ptr_max, pool, new_max) DN_CArrayGrowFromPool((void **)&(ptr), size, ptr_max, sizeof((ptr)[0]), pool, new_max) -#define DN_PArrayGrowFromArena(ptr, size, ptr_max, arena, new_max) DN_CArrayGrowFromArena((void **)&(ptr), size, ptr_max, sizeof((ptr)[0]), arena, new_max) -#define DN_PArrayGrowIfNeededFromPool(ptr, size, ptr_max, pool, add_count) DN_CArrayGrowIfNeededFromPool((void **)(ptr), size, ptr_max, sizeof((*ptr)[0]), pool, add_count) -#define DN_PArrayGrowIfNeededFromArena(ptr, size, ptr_max, arena, add_count) DN_CArrayGrowIfNeededFromArena((void **)(ptr), size, ptr_max, sizeof((*ptr)[0]), arena, add_count) -#define DN_PArrayMakeArray(ptr, ptr_size, max, count, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_CArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), count, z_mem) -#define DN_PArrayMakeArrayZ(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_CArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_Yes) -#define DN_PArrayMake(ptr, ptr_size, max, z_mem) (DN_CppDeclType(&(ptr)[0]))DN_CArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), 1, z_mem) -#define DN_PArrayMakeZ(ptr, ptr_size, max) (DN_CppDeclType(&(ptr)[0]))DN_CArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_Yes) -#define DN_PArrayAddArray(ptr, ptr_size, max, items, count, add) (DN_CppDeclType(&(ptr)[0]))DN_CArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, add) -#define DN_PArrayAdd(ptr, ptr_size, max, item, add) (DN_CppDeclType(&(ptr)[0]))DN_CArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, add) -#define DN_PArrayAppendArray(ptr, ptr_size, max, items, count) (DN_CppDeclType(&(ptr)[0]))DN_CArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, DN_ArrayAdd_Append) -#define DN_PArrayAppend(ptr, ptr_size, max, item) (DN_CppDeclType(&(ptr)[0]))DN_CArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, DN_ArrayAdd_Append) -#define DN_PArrayPrependArray(ptr, ptr_size, max, items, count) (DN_CppDeclType(&(ptr)[0]))DN_CArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, DN_ArrayAdd_Prepend) -#define DN_PArrayPrepend(ptr, ptr_size, max, item) (DN_CppDeclType(&(ptr)[0]))DN_CArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, DN_ArrayAdd_Prepend) -#define DN_PArrayEraseRange(ptr, ptr_size, begin_index, count, erase) DN_CArrayEraseRange(ptr, ptr_size, sizeof((ptr)[0]), begin_index, count, erase) -#define DN_PArrayErase(ptr, ptr_size, index, erase) DN_CArrayEraseRange(ptr, ptr_size, sizeof((ptr)[0]), index, 1, erase) -#define DN_PArrayInsertArray(ptr, ptr_size, max, index, items, count) (DN_CppDeclType(&(ptr)[0]))DN_CArrayInsertArray(ptr, ptr_size, max, sizeof((ptr)[0]), index, items, count) -#define DN_PArrayInsert(ptr, ptr_size, max, index, item) (DN_CppDeclType(&(ptr)[0]))DN_CArrayInsertArray(ptr, ptr_size, max, sizeof((ptr)[0]), index, &item, 1) -#define DN_PArrayPopFront(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_CArrayPopFront(ptr, ptr_size, sizeof((ptr)[0]), count) -#define DN_PArrayPopBack(ptr, ptr_size, max, count) (DN_CppDeclType(&(ptr)[0]))DN_CArrayPopBack(ptr, ptr_size, sizeof((ptr)[0]), count) - -#define DN_LArrayResizeFromPool(c_array, size, pool, new_max) DN_PArrayResizeFromPool(c_array, size, DN_ArrayCountU(c_array), pool, new_max) -#define DN_LArrayResizeFromArena(c_array, size, arena, new_max) DN_PArrayResizeFromArena(c_array, size, DN_ArrayCountU(c_array), arena, new_max) -#define DN_LArrayGrowFromPool(c_array, size, pool, new_max) DN_PArrayGrowFromPool(c_array, size, DN_ArrayCountU(c_array), pool, new_max) -#define DN_LArrayGrowFromArena(c_array, size, arena, new_max) DN_PArrayGrowFromArena(c_array, size, DN_ArrayCountU(c_array), arena, new_max) -#define DN_LArrayGrowIfNeededFromPool(c_array, size, pool, add_count) DN_PArrayGrowIfNeededFromPool(c_array, size, DN_ArrayCountU(c_array), pool, add_count) -#define DN_LArrayGrowIfNeededFromArena(c_array, size, arena, add_count) DN_PArrayGrowIfNeededFromArena(c_array, size, DN_ArrayCountU(c_array), arena, add_count) -#define DN_LArrayMakeArray(c_array, ptr_size, count, z_mem) DN_PArrayMakeArray(c_array, ptr_size, DN_ArrayCountU(c_array), count, z_mem) -#define DN_LArrayMakeArrayZ(c_array, ptr_size, count) DN_PArrayMakeArrayZ(c_array, ptr_size, DN_ArrayCountU(c_array), count) -#define DN_LArrayMake(c_array, ptr_size, z_mem) DN_PArrayMake(c_array, ptr_size, DN_ArrayCountU(c_array), z_mem) -#define DN_LArrayMakeZ(c_array, ptr_size) DN_PArrayMakeZ(c_array, ptr_size, DN_ArrayCountU(c_array)) -#define DN_LArrayAddArray(c_array, ptr_size, items, count, add) DN_PArrayAddArray(c_array, ptr_size, DN_ArrayCountU(c_array), items, count, add) -#define DN_LArrayAdd(c_array, ptr_size, item, add) DN_PArrayAdd(c_array, ptr_size, DN_ArrayCountU(c_array), item, add) -#define DN_LArrayAppendArray(c_array, ptr_size, items, count) DN_PArrayAppendArray(c_array, ptr_size, DN_ArrayCountU(c_array), items, count) -#define DN_LArrayAppend(c_array, ptr_size, item) DN_PArrayAppend(c_array, ptr_size, DN_ArrayCountU(c_array), item) -#define DN_LArrayPrependArray(c_array, ptr_size, items, count) DN_PArrayPrependArray(c_array, ptr_size, DN_ArrayCountU(c_array), items, count) -#define DN_LArrayPrepend(c_array, ptr_size, item) DN_PArrayPrepend(c_array, ptr_size, DN_ArrayCountU(c_array), item) -#define DN_LArrayEraseRange(c_array, ptr_size, begin_index, count, erase) DN_PArrayEraseRange(c_array, ptr_size, begin_index, count, erase) -#define DN_LArrayErase(c_array, ptr_size, index, erase) DN_PArrayErase(c_array, ptr_size, index, erase) -#define DN_LArrayInsertArray(c_array, ptr_size, index, items, count) DN_PArrayInsertArray(c_array, ptr_size, DN_ArrayCountU(c_array), index, items, count) -#define DN_LArrayInsert(c_array, ptr_size, index, item) DN_PArrayInsert(c_array, ptr_size, DN_ArrayCountU(c_array), index, item) -#define DN_LArrayPopFront(c_array, ptr_size, count) DN_PArrayPopFront(c_array, ptr_size, DN_ArrayCountU(c_array), count) -#define DN_LArrayPopBack(c_array, ptr_size, count) DN_PArrayPopBack(c_array, ptr_size, DN_ArrayCountU(c_array), count) - -#define DN_IArrayResizeFromPool(ptr_array, pool, new_max) DN_CArrayResizeFromPool((void **)(&(ptr_array)->data), &(ptr_array)->size, &(ptr_array)->max, sizeof((ptr_array)->data[0]), pool, new_max) -#define DN_IArrayResizeFromArena(ptr_array, arena, new_max) DN_CArrayResizeFromArena((void **)(&(ptr_array)->data), &(ptr_array)->size, &(ptr_array)->max, sizeof((ptr_array)->data[0]), arena, new_max) -#define DN_IArrayGrowFromPool(ptr_array, pool, new_max) DN_CArrayGrowFromPool((void **)(&(ptr_array)->data), (ptr_array)->size, &(ptr_array)->max, sizeof((ptr_array)->data[0]), pool, new_max) -#define DN_IArrayGrowFromArena(ptr_array, arena, new_max) DN_CArrayGrowFromArena((void **)(&(ptr_array)->data), (ptr_array)->size, &(ptr_array)->max, sizeof((ptr_array)->data[0]), arena, new_max) -#define DN_IArrayGrowIfNeededFromPool(ptr_array, pool, add_count) DN_CArrayGrowIfNeededFromPool((void **)(&(ptr_array)->data), (ptr_array)->size, &(ptr_array)->max, sizeof((ptr_array)->data[0]), pool, add_count) -#define DN_IArrayGrowIfNeededFromArena(ptr_array, arena, add_count) DN_CArrayGrowIfNeededFromArena((void **)(&(ptr_array)->data), (ptr_array)->size, &(ptr_array)->max, sizeof((ptr_array)->data[0]), arena, add_count) -#define DN_IArrayMakeArray(ptr_array, count, z_mem) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayMakeArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), count, z_mem) -#define DN_IArrayMakeArrayZ(ptr_array, count) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayMakeArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), count, DN_ZMem_Yes) -#define DN_IArrayMake(ptr_array, z_mem) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayMakeArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), 1, z_mem) -#define DN_IArrayMakeZ(ptr_array) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayMakeArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), 1, DN_ZMem_Yes) -#define DN_IArrayAddArray(ptr_array, items, count, add) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayAddArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), items, count, add) -#define DN_IArrayAdd(ptr_array, item, add) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayAddArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), &item, 1, add) -#define DN_IArrayAppendArray(ptr_array, items, count) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayAddArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), items, count, DN_ArrayAdd_Append) -#define DN_IArrayAppend(ptr_array, item) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayAddArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), &item, 1, DN_ArrayAdd_Append) -#define DN_IArrayPrependArray(ptr_array, items, count) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayAddArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), items, count, DN_ArrayAdd_Prepend) -#define DN_IArrayPrepend(ptr_array, item) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayAddArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), &item, 1, DN_ArrayAdd_Prepend) -#define DN_IArrayEraseRange(ptr_array, begin_index, count, erase) DN_CArrayEraseRange((ptr_array)->data, &(ptr_array)->size, sizeof(((ptr_array)->data)[0]), begin_index, count, erase) -#define DN_IArrayErase(ptr_array, index, erase) DN_CArrayEraseRange((ptr_array)->data, &(ptr_array)->size, sizeof(((ptr_array)->data)[0]), index, 1, erase) -#define DN_IArrayInsertArray(ptr_array, index, items, count) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayInsertArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), index, items, count) -#define DN_IArrayInsert(ptr_array, index, item, count) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayInsertArray((ptr_array)->data, &(ptr_array)->size, (ptr_array)->max, sizeof(((ptr_array)->data)[0]), index, &item, 1) -#define DN_IArrayPopFront(ptr_array, count) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayPopFront((ptr_array)->data, &(ptr_array)->size, sizeof(((ptr_array)->data)[0]), count) -#define DN_IArrayPopBack(ptr_array, count) (DN_CppDeclType(&((ptr_array)->data)[0]))DN_CArrayPopBack((ptr_array)->data, &(ptr_array)->size, sizeof(((ptr_array)->data)[0]), count) - -#define DN_ISliceAllocArena(slice_ptr, count_, zmem, arena) (DN_CppDeclType(&((slice_ptr)->data[0])))DN_SliceAllocArena((void **)&((slice_ptr)->data), &((slice_ptr)->count), count_, sizeof((slice_ptr)->data[0]), alignof(DN_CppDeclType((slice_ptr)->data[0])), zmem, arena) - -DN_API void* DN_SliceAllocArena (void **data, DN_USize *slice_size_field, DN_USize size, DN_USize elem_size, DN_U8 align, DN_ZMem zmem, DN_Arena *arena); - -DN_API void* DN_CArrayInsertArray (void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize index, void const *items, DN_USize count); -DN_API void* DN_CArrayPopFront (void *data, DN_USize *size, DN_USize elem_size, DN_USize count); -DN_API void* DN_CArrayPopBack (void *data, DN_USize *size, DN_USize elem_size, DN_USize count); -DN_API DN_ArrayEraseResult DN_CArrayEraseRange (void *data, DN_USize *size, DN_USize elem_size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase); -DN_API void* DN_CArrayMakeArray (void *data, DN_USize *size, DN_USize max, DN_USize data_size, DN_USize make_size, DN_ZMem z_mem); -DN_API void* DN_CArrayAddArray (void *data, DN_USize *size, DN_USize max, DN_USize data_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add); -DN_API bool DN_CArrayResizeFromPool (void **data, DN_USize *size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize new_max); -DN_API bool DN_CArrayResizeFromArena (void **data, DN_USize *size, DN_USize *max, DN_USize data_size, DN_Arena *arena, DN_USize new_max); -DN_API bool DN_CArrayGrowFromPool (void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize new_max); -DN_API bool DN_CArrayGrowFromArena (void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize new_max); -DN_API bool DN_CArrayGrowIfNeededFromPool (void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize add_count); -DN_API bool DN_CArrayGrowIfNeededFromArena (void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Arena *pool, DN_USize add_count); - -DN_API void* DN_SinglyLLDetach (void **link, void **next); - -DN_API bool DN_RingHasSpace (DN_Ring const *ring, DN_U64 size); -DN_API bool DN_RingHasData (DN_Ring const *ring, DN_U64 size); -DN_API void DN_RingWrite (DN_Ring *ring, void const *src, DN_U64 src_size); -#define DN_RingWriteStruct(ring, item) DN_RingWrite((ring), (item), sizeof(*(item))) -DN_API void DN_RingRead (DN_Ring *ring, void *dest, DN_U64 dest_size); -#define DN_RingReadStruct(ring, dest) DN_RingRead((ring), (dest), sizeof(*(dest))) - -DN_U32 const DN_DS_MAP_DEFAULT_HASH_SEED = 0x8a1ced49; -DN_U32 const DN_DS_MAP_SENTINEL_SLOT = 0; - -template DN_DSMap DN_DSMapInit (DN_Arena *arena, DN_U32 size, DN_DSMapFlags flags); -template void DN_DSMapDeinit (DN_DSMap *map, DN_ZMem z_mem); -template bool DN_DSMapIsValid (DN_DSMap const *map); -template DN_U32 DN_DSMapHash (DN_DSMap const *map, DN_DSMapKey key); -template DN_U32 DN_DSMapHashToSlotIndex (DN_DSMap const *map, DN_DSMapKey key); -template DN_DSMapResult DN_DSMapFind (DN_DSMap const *map, DN_DSMapKey key); -template DN_DSMapResult DN_DSMapMake (DN_DSMap *map, DN_DSMapKey key); -template DN_DSMapResult DN_DSMapSet (DN_DSMap *map, DN_DSMapKey key, T const &value); -template DN_DSMapResult DN_DSMapFindKeyU64 (DN_DSMap const *map, DN_U64 key); -template DN_DSMapResult DN_DSMapMakeKeyU64 (DN_DSMap *map, DN_U64 key); -template DN_DSMapResult DN_DSMapSetKeyU64 (DN_DSMap *map, DN_U64 key, T const &value); -template DN_DSMapResult DN_DSMapFindKeyStr8 (DN_DSMap const *map, DN_Str8 key); -template DN_DSMapResult DN_DSMapMakeKeyStr8 (DN_DSMap *map, DN_Str8 key); -template DN_DSMapResult DN_DSMapSetKeyStr8 (DN_DSMap *map, DN_Str8 key, T const &value); -template bool DN_DSMapResize (DN_DSMap *map, DN_U32 size); -template bool DN_DSMapErase (DN_DSMap *map, DN_DSMapKey key); -template bool DN_DSMapEraseKeyU64 (DN_DSMap *map, DN_U64 key); -template bool DN_DSMapEraseKeyStr8 (DN_DSMap *map, DN_Str8 key); -template DN_DSMapKey DN_DSMapKeyBuffer (DN_DSMap const *map, void const *data, DN_USize size); -template DN_DSMapKey DN_DSMapKeyBufferAsU64NoHash (DN_DSMap const *map, void const *data, DN_USize size); -template DN_DSMapKey DN_DSMapKeyU64 (DN_DSMap const *map, DN_U64 u64); -template DN_DSMapKey DN_DSMapKeyStr8 (DN_DSMap const *map, DN_Str8 string); -#define DN_DSMapKeyCStr8(map, string) DN_DSMapKeyBuffer(map, string, sizeof((string))/sizeof((string)[0]) - 1) -DN_API DN_DSMapKey DN_DSMapKeyU64NoHash (DN_U64 u64); -DN_API bool DN_DSMapKeyEquals (DN_DSMapKey lhs, DN_DSMapKey rhs); -DN_API bool operator== (DN_DSMapKey lhs, DN_DSMapKey rhs); -#endif // !defined(DN_CONTAINER_H) diff --git a/Source/Base/dn_base_leak.cpp b/Source/Base/dn_base_leak.cpp index 12fd748..7082ded 100644 --- a/Source/Base/dn_base_leak.cpp +++ b/Source/Base/dn_base_leak.cpp @@ -10,19 +10,15 @@ DN_API void DN_LeakTrackAlloc_(DN_LeakTracker *leak, void *ptr, DN_USize size, b return; DN_TicketMutex_Begin(&leak->alloc_table_mutex); - DN_DEFER - { - DN_TicketMutex_End(&leak->alloc_table_mutex); - }; - DN_Str8 stack_trace = DN_StackTraceWalkStr8FromHeap(128, 3 /*skip*/); + DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/); DN_DSMap *alloc_table = &leak->alloc_table; DN_DSMapResult alloc_entry = DN_DSMapMakeKeyU64(alloc_table, DN_Cast(DN_U64) ptr); DN_LeakAlloc *alloc = alloc_entry.value; if (alloc_entry.found) { if ((alloc->flags & DN_LeakAllocFlag_Freed) == 0) { - DN_Str8x32 alloc_size = DN_ByteCountStr8x32(alloc->size); - DN_Str8x32 new_alloc_size = DN_ByteCountStr8x32(size); + DN_Str8x32 alloc_size = DN_Str8x32FromByteCountU64Auto(alloc->size); + DN_Str8x32 new_alloc_size = DN_Str8x32FromByteCountU64Auto(size); DN_HardAssertF( alloc->flags & DN_LeakAllocFlag_Freed, "This pointer is already in the leak tracker, however it has not been freed yet. This " @@ -58,6 +54,7 @@ DN_API void DN_LeakTrackAlloc_(DN_LeakTracker *leak, void *ptr, DN_USize size, b alloc->stack_trace = stack_trace; alloc->flags |= leak_permitted ? DN_LeakAllocFlag_LeakPermitted : 0; leak->alloc_table_bytes_allocated_for_stack_traces += alloc->stack_trace.size; + DN_TicketMutex_End(&leak->alloc_table_mutex); } DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr) @@ -66,12 +63,8 @@ DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr) return; DN_TicketMutex_Begin(&leak->alloc_table_mutex); - DN_DEFER - { - DN_TicketMutex_End(&leak->alloc_table_mutex); - }; - DN_Str8 stack_trace = DN_StackTraceWalkStr8FromHeap(128, 3 /*skip*/); + DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/); DN_DSMap *alloc_table = &leak->alloc_table; DN_DSMapResult alloc_entry = DN_DSMapFindKeyU64(alloc_table, DN_Cast(uintptr_t) ptr); DN_HardAssertF(alloc_entry.found, @@ -82,7 +75,7 @@ DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr) DN_LeakAlloc *alloc = alloc_entry.value; if (alloc->flags & DN_LeakAllocFlag_Freed) { - DN_Str8x32 freed_size = DN_ByteCountStr8x32(alloc->freed_size); + DN_Str8x32 freed_size = DN_Str8x32FromByteCountU64Auto(alloc->freed_size); DN_HardAssertF((alloc->flags & DN_LeakAllocFlag_Freed) == 0, "Double free detected, pointer to free was already marked " "as freed. Either the pointer was reallocated but not " @@ -110,6 +103,7 @@ DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr) alloc->flags |= DN_LeakAllocFlag_Freed; alloc->freed_stack_trace = stack_trace; leak->alloc_table_bytes_allocated_for_stack_traces += alloc->freed_stack_trace.size; + DN_TicketMutex_End(&leak->alloc_table_mutex); } DN_API void DN_LeakDump_(DN_LeakTracker *leak) @@ -124,7 +118,7 @@ DN_API void DN_LeakDump_(DN_LeakTracker *leak) if (alloc_leaked && !leak_permitted) { leaked_bytes += alloc->size; leak_count++; - DN_Str8x32 alloc_size = DN_ByteCountStr8x32(alloc->size); + DN_Str8x32 alloc_size = DN_Str8x32FromByteCountU64Auto(alloc->size); DN_LogWarningF( "Pointer (0x%p) leaked %.*s at:\n" "%.*s", @@ -135,7 +129,7 @@ DN_API void DN_LeakDump_(DN_LeakTracker *leak) } if (leak_count) { - DN_Str8x32 leak_size = DN_ByteCountStr8x32(leaked_bytes); + DN_Str8x32 leak_size = DN_Str8x32FromByteCountU64Auto(leaked_bytes); DN_LogWarningF("There were %I64u leaked allocations totalling %.*s", leak_count, DN_Str8PrintFmt(leak_size)); } } diff --git a/Source/Extra/dn_bin_pack.cpp b/Source/Extra/dn_bin_pack.cpp index e2ab4d9..46432ab 100644 --- a/Source/Extra/dn_bin_pack.cpp +++ b/Source/Extra/dn_bin_pack.cpp @@ -201,6 +201,6 @@ DN_API void DN_BinPackCBuffer(DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_API DN_Str8 DN_BinPackBuild(DN_BinPack const *pack, DN_Arena *arena) { - DN_Str8 result = DN_Str8BuilderBuild(&pack->writer, arena); + DN_Str8 result = DN_Str8FromStr8BuilderArena(&pack->writer, arena); return result; } diff --git a/Source/Extra/dn_helpers.cpp b/Source/Extra/dn_helpers.cpp index 9b33396..bdebaf0 100644 --- a/Source/Extra/dn_helpers.cpp +++ b/Source/Extra/dn_helpers.cpp @@ -14,7 +14,7 @@ DN_API DN_JSONBuilder DN_JSONBuilder_Init(DN_Arena *arena, int spaces_per_indent DN_API DN_Str8 DN_JSONBuilder_Build(DN_JSONBuilder const *builder, DN_Arena *arena) { - DN_Str8 result = DN_Str8BuilderBuild(&builder->string_builder, arena); + DN_Str8 result = DN_Str8FromStr8BuilderArena(&builder->string_builder, arena); return result; } @@ -71,7 +71,7 @@ DN_API void DN_JSONBuilder_KeyValue(DN_JSONBuilder *builder, DN_Str8 key, DN_Str DN_API void DN_JSONBuilder_KeyValueFV(DN_JSONBuilder *builder, DN_Str8 key, char const *value_fmt, va_list args) { - DN_TCScratch scratch = DN_TCScratchBegin(&builder->string_builder.arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&builder->string_builder.arena, 1); DN_Str8 value = DN_Str8FromFmtVArena(&scratch.arena, value_fmt, args); DN_JSONBuilder_KeyValue(builder, key, value); DN_TCScratchEnd(&scratch); diff --git a/Source/Extra/dn_helpers.h b/Source/Extra/dn_helpers.h index a5f276c..357cc39 100644 --- a/Source/Extra/dn_helpers.h +++ b/Source/Extra/dn_helpers.h @@ -79,15 +79,12 @@ enum DN_BinarySearchType struct DN_BinarySearchResult { - bool found; - DN_USize index; + bool found; + DN_USize index; }; -template -using DN_QSortLessThanProc = bool(T const &a, T const &b, void *user_context); - #if !defined(DN_NO_JSON_BUILDER) -// NOTE: DN_JSONBuilder //////////////////////////////////////////////////////////////////////////// +// NOTE: DN_JSONBuilder #define DN_JSONBuilder_Object(builder) \ DN_DeferLoop(DN_JSONBuilder_ObjectBegin(builder), \ DN_JSONBuilder_ObjectEnd(builder)) @@ -133,10 +130,6 @@ DN_API void DN_JSONBuilder_BoolNamed (DN_JSONBuilde template bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs); template DN_BinarySearchResult DN_BinarySearch (T const *array, DN_USize array_size, T const &find, DN_BinarySearchType type = DN_BinarySearchType_Match, DN_BinarySearchLessThanProc less_than = DN_BinarySearch_DefaultLessThan); -// NOTE: DN_QSort -template bool DN_QSort_DefaultLessThan(T const &lhs, T const &rhs, void *user_context); -template void DN_QSort (T *array, DN_USize array_size, void *user_context, DN_QSortLessThanProc less_than = DN_QSort_DefaultLessThan); - // NOTE: DN_BinarySearch template bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs) @@ -189,76 +182,4 @@ DN_BinarySearchResult DN_BinarySearch(T const *array, result.index = first - array; return result; } - -// NOTE: DN_QSort ////////////////////////////////////////////////////////////////////////////////// -template -bool DN_QSort_DefaultLessThan(T const &lhs, T const &rhs, void *user_context) -{ - (void)user_context; - bool result = lhs < rhs; - return result; -} - -template -void DN_QSort(T *array, DN_USize array_size, void *user_context, DN_QSortLessThanProc less_than) -{ - if (!array || array_size <= 1 || !less_than) - return; - - // NOTE: Insertion Sort, under 24->32 is an optimal amount ///////////////////////////////////// - const DN_USize QSORT_THRESHOLD = 24; - if (array_size < QSORT_THRESHOLD) { - for (DN_USize item_to_insert_index = 1; item_to_insert_index < array_size; item_to_insert_index++) { - for (DN_USize index = 0; index < item_to_insert_index; index++) { - if (!less_than(array[index], array[item_to_insert_index], user_context)) { - T item_to_insert = array[item_to_insert_index]; - for (DN_USize i = item_to_insert_index; i > index; i--) - array[i] = array[i - 1]; - - array[index] = item_to_insert; - break; - } - } - } - return; - } - - // NOTE: Quick sort, under 24->32 is an optimal amount ///////////////////////////////////////// - DN_USize last_index = array_size - 1; - DN_USize pivot_index = array_size / 2; - DN_USize partition_index = 0; - DN_USize start_index = 0; - - // Swap pivot with last index, so pivot is always at the end of the array. - // This makes logic much simpler. - DN_Swap(array[last_index], array[pivot_index]); - pivot_index = last_index; - - // 4^, 8, 7, 5, 2, 3, 6 - if (less_than(array[start_index], array[pivot_index], user_context)) - partition_index++; - start_index++; - - // 4, |8, 7, 5^, 2, 3, 6* - // 4, 5, |7, 8, 2^, 3, 6* - // 4, 5, 2, |8, 7, ^3, 6* - // 4, 5, 2, 3, |7, 8, ^6* - for (DN_USize index = start_index; index < last_index; index++) { - if (less_than(array[index], array[pivot_index], user_context)) { - DN_Swap(array[partition_index], array[index]); - partition_index++; - } - } - - // Move pivot to right of partition - // 4, 5, 2, 3, |6, 8, ^7* - DN_Swap(array[partition_index], array[pivot_index]); - DN_QSort(array, partition_index, user_context, less_than); - - // Skip the value at partion index since that is guaranteed to be sorted. - // 4, 5, 2, 3, (x), 8, 7 - DN_USize one_after_partition_index = partition_index + 1; - DN_QSort(array + one_after_partition_index, (array_size - one_after_partition_index), user_context, less_than); -} - #endif // !defined(DN_HELPERS_H) diff --git a/Source/Extra/dn_net.cpp b/Source/Extra/dn_net.cpp index 82ca963..211bc20 100644 --- a/Source/Extra/dn_net.cpp +++ b/Source/Extra/dn_net.cpp @@ -42,15 +42,40 @@ DN_NETRequestHandle DN_NET_HandleFromRequest(DN_NETRequest *request) return result; } -void DN_NET_EndFinishedRequest_(DN_NETRequest *request) +bool DN_NET_ResponseHasFailed(DN_NETResponse const* resp) { - // NOTE: Deallocate the memory used in the request and reset the string builder - DN_ArenaTempEnd(&request->start_response_arena, DN_ArenaReset_Yes); - // NOTE: Check that the request is completely detached - DN_Assert(request->next == nullptr); + bool result = false; + if (resp->type == DN_NETRequestType_HTTP) + result = resp->state == DN_NETResponseState_Error || resp->http_status >= 400; + else + result = resp->state == DN_NETResponseState_Error; + return result; } -void DN_NET_BaseInit_(DN_NETCore *net, char *base, DN_U64 base_size) +DN_Str8 DN_NET_Str8DiagnosticFromResponse(DN_NETResponse const* resp, DN_Arena *arena) +{ + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); + DN_Str8Builder builder = DN_Str8BuilderFromArena(&scratch.arena); + bool resp_failed = DN_NET_ResponseHasFailed(resp); + DN_Str8BuilderAppendF(&builder, "Request %s (%s", resp_failed ? "failed" : "succeeded", resp->type == DN_NETRequestType_HTTP ? "HTTP" : "WS"); + if (resp->type == DN_NETRequestType_HTTP) { + if (resp->http_status) + DN_Str8BuilderAppendF(&builder, " %u", resp->http_status); + } + DN_Str8BuilderAppendF(&builder, ")"); + if (resp->body.size || resp->error_str8.size) { + DN_Str8BuilderAppendRef(&builder, DN_Str8Lit(" with ")); + if (resp->body.size) + DN_Str8BuilderAppendF(&builder, "%.*s", DN_Str8PrintFmt(resp->body)); + if (resp->error_str8.size) + DN_Str8BuilderAppendF(&builder, "%s%.*s", resp->body.size ? ". " : "", DN_Str8PrintFmt(resp->error_str8)); + } + DN_Str8 result = DN_Str8FromStr8BuilderArena(&builder, arena); + DN_TCScratchEnd(&scratch); + return result; +} + +void DN_NET_BaseInit(DN_NETCore *net, char *base, DN_U64 base_size) { net->base = base; net->base_size = base_size; @@ -59,7 +84,7 @@ void DN_NET_BaseInit_(DN_NETCore *net, char *base, DN_U64 base_size) net->completion_sem = DN_OS_SemaphoreInit(0); } -DN_NETRequestHandle DN_NET_SetupRequest_(DN_NETRequest *request, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type) +DN_NETRequestHandle DN_NET_SetupRequest(DN_NETRequest *request, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type) { // NOTE: Setup request DN_Assert(request); @@ -94,5 +119,14 @@ DN_NETRequestHandle DN_NET_SetupRequest_(DN_NETRequest *request, DN_Str8 url, DN DN_NETRequestHandle result = DN_NET_HandleFromRequest(request); request->response.request = result; + request->response.type = request->type; return result; } + +void DN_NET_EndFinishedRequest(DN_NETRequest *request) +{ + // NOTE: Deallocate the memory used in the request and reset the string builder + DN_ArenaTempEnd(&request->start_response_arena, DN_ArenaReset_Yes); + // NOTE: Check that the request is completely detached + DN_Assert(request->next == nullptr); +} diff --git a/Source/Extra/dn_net.h b/Source/Extra/dn_net.h index 459e734..6706493 100644 --- a/Source/Extra/dn_net.h +++ b/Source/Extra/dn_net.h @@ -51,7 +51,7 @@ struct DN_NETDoHTTPArgs DN_U16 headers_size; // NOTE: HTTP args only - DN_Str8 payload; + DN_Str8 payload; }; struct DN_NETRequestHandle @@ -63,6 +63,7 @@ struct DN_NETRequestHandle struct DN_NETResponse { // NOTE: Common to WS and HTTP responses + DN_NETRequestType type; DN_NETResponseState state; DN_NETRequestHandle request; DN_Str8 error_str8; @@ -119,13 +120,15 @@ struct DN_NETCore DN_NETInterface api; }; -DN_Str8 DN_NET_Str8FromResponseState(DN_NETResponseState state); -DN_NETRequest * DN_NET_RequestFromHandle (DN_NETRequestHandle handle); -DN_NETRequestHandle DN_NET_HandleFromRequest (DN_NETRequest *request); +DN_Str8 DN_NET_Str8FromResponseState (DN_NETResponseState state); +DN_NETRequest * DN_NET_RequestFromHandle (DN_NETRequestHandle handle); +DN_NETRequestHandle DN_NET_HandleFromRequest (DN_NETRequest *request); +bool DN_NET_ResponseHasFailed (DN_NETResponse const* resp); +DN_Str8 DN_NET_Str8DiagnosticFromResponse(DN_NETResponse const* resp, DN_Arena *arena); // NOTE: Internal functions for different networking implementations to use -void DN_NET_BaseInit_ (DN_NETCore *net, char *base, DN_U64 base_size); -DN_NETRequestHandle DN_NET_SetupRequest_ (DN_NETRequest *request, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type); -void DN_NET_EndFinishedRequest_ (DN_NETRequest *request); +void DN_NET_BaseInit (DN_NETCore *net, char *base, DN_U64 base_size); +DN_NETRequestHandle DN_NET_SetupRequest (DN_NETRequest *request, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type); +void DN_NET_EndFinishedRequest (DN_NETRequest *request); #endif // DN_NET_H diff --git a/Source/Extra/dn_net_curl.cpp b/Source/Extra/dn_net_curl.cpp index 575974e..fee143d 100644 --- a/Source/Extra/dn_net_curl.cpp +++ b/Source/Extra/dn_net_curl.cpp @@ -93,7 +93,7 @@ static int32_t DN_NET_CurlThreadEntryPoint_(DN_OSThread *thread) DN_OS_ThreadSetNameFmt("%.*s", DN_Str8PrintFmt(curl->thread.name)); while (!curl->kill_thread) { - DN_TCScratch tmem = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch tmem = DN_TCScratchBeginArena(nullptr, 0); // NOTE: Handle events sitting in the ring queue for (bool dequeue_ring = true; dequeue_ring;) { @@ -159,7 +159,7 @@ static int32_t DN_NET_CurlThreadEntryPoint_(DN_OSThread *thread) // NOTE: End the temp memory storing the WS data we just read and the user returned to us // (we got their receipt back). Then restart the temp memory scope for the next websocket // payload - DN_NET_EndFinishedRequest_(req); + DN_NET_EndFinishedRequest(req); req->start_response_arena = DN_ArenaTempBeginFromArena(&req->arena); curl_req->str8_builder = DN_Str8BuilderFromArena(&req->start_response_arena); @@ -175,12 +175,15 @@ static int32_t DN_NET_CurlThreadEntryPoint_(DN_OSThread *thread) DN_NETRequest *request = DN_Cast(DN_NETRequest *) event.request.handle; // NOTE: Release resources - DN_NET_EndFinishedRequest_(request); + DN_NET_EndFinishedRequest(request); DN_OS_SemaphoreDeinit(&request->completion_sem); curl_multi_remove_handle(curl->thread_curlm, curl_req->handle); - curl_easy_reset(curl_req->handle); curl_slist_free_all(curl_req->slist); + curl_easy_reset(curl_req->handle); + CURL *copy = curl_req->handle; + *curl_req = {}; + curl_req->handle = copy; // NOTE: Zero the struct preserving just the data we need to retain DN_NETRequest resetter = {}; @@ -230,13 +233,13 @@ static int32_t DN_NET_CurlThreadEntryPoint_(DN_OSThread *thread) req->response.state = DN_NETResponseState_WSOpen; } } else { - req->response.error_str8 = DN_Str8FromFmtArena(&req->arena, "Failed to get HTTP response status (CURL %d): %s", msg->data.result, curl_easy_strerror(get_result)); + req->response.error_str8 = DN_Str8FromFmtArena(&req->start_response_arena, "Failed to get HTTP response status (CURL %d): %s", msg->data.result, curl_easy_strerror(get_result)); req->response.state = DN_NETResponseState_Error; } } else { DN_USize curl_extended_error_size = DN_CStr8Size(curl_req->error); req->response.state = DN_NETResponseState_Error; - req->response.error_str8 = DN_Str8FromFmtArena(&req->arena, + req->response.error_str8 = DN_Str8FromFmtArena(&req->start_response_arena, "HTTP request '%.*s' failed (CURL %d): %s%s%s%s", DN_Str8PrintFmt(req->url), msg->data.result, @@ -400,7 +403,7 @@ DN_NETInterface DN_NET_CurlInterface() void DN_NET_CurlInit(DN_NETCore *net, char *base, DN_U64 base_size) { - DN_NET_BaseInit_(net, base, base_size); + DN_NET_BaseInit(net, base, base_size); DN_NETCurlCore *curl = DN_ArenaNew(&net->arena, DN_NETCurlCore, DN_ZMem_Yes); net->context = curl; net->api = DN_NET_CurlInterface(); @@ -458,8 +461,7 @@ static DN_NETRequestHandle DN_NET_CurlDoRequest_(DN_NETCore *net, DN_Str8 url, D // NOTE: Setup the request DN_NETCurlRequest *curl_req = DN_NET_CurlRequestFromRequest_(req); { - result = DN_NET_SetupRequest_(req, url, method, args, type); - req->response.request = result; + result = DN_NET_SetupRequest(req, url, method, args, type); req->context[1] = DN_Cast(DN_UPtr) net; curl_req->str8_builder = DN_Str8BuilderFromArena(&req->start_response_arena); } @@ -480,8 +482,10 @@ static DN_NETRequestHandle DN_NET_CurlDoRequest_(DN_NETCore *net, DN_Str8 url, D curl_easy_setopt(curl, CURLOPT_WRITEDATA, req); // NOTE: Assign HTTP headers - for (DN_ForItSize(it, DN_Str8, req->args.headers, req->args.headers_size)) + for (DN_ForItSize(it, DN_Str8, req->args.headers, req->args.headers_size)) { + DN_Assert(it.data->data[it.data->size] == 0); curl_req->slist = curl_slist_append(curl_req->slist, it.data->data); + } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_req->slist); // NOTE: Setup handle for protocol @@ -593,7 +597,7 @@ static DN_NETResponse DN_NET_CurlHandleFinishedRequest_(DN_NETCurlCore *curl, DN DN_NETResponse result = req->response; DN_NETCurlRequest *curl_req = DN_NET_CurlRequestFromRequest_(req); { - result.body = DN_Str8BuilderBuild(&curl_req->str8_builder, arena); + result.body = DN_Str8FromStr8BuilderArena(&curl_req->str8_builder, arena); if (result.error_str8.size) result.error_str8 = DN_Str8FromStr8Arena(result.error_str8, arena); } diff --git a/Source/Extra/dn_tests.cpp b/Source/Extra/dn_tests.cpp index 5fc42f0..4960c92 100644 --- a/Source/Extra/dn_tests.cpp +++ b/Source/Extra/dn_tests.cpp @@ -365,6 +365,66 @@ static DN_UTCore DN_TST_Base() DN_UT_Assert(&result, mutex.ticket == ticket_b + 1); } + for (DN_UT_Test(&result, "QSort String (Natural)")) { + DN_Str8 list[] = { + DN_Str8Lit("item10"), + DN_Str8Lit("item2"), + DN_Str8Lit("item1"), + DN_Str8Lit("item20"), + DN_Str8Lit("item12"), + DN_Str8Lit("Afile"), + DN_Str8Lit("file2"), + DN_Str8Lit("file10"), + DN_Str8Lit("file1"), + DN_Str8Lit("z_last"), + DN_Str8Lit("m_middle"), + DN_Str8Lit("a_first"), + DN_Str8Lit("version-1.2.10"), + DN_Str8Lit("version-1.2.2"), + DN_Str8Lit("version-1.10.0"), + }; + + DN_QSortStr8NaturalAsc(list, DN_ArrayCountU(list), DN_Str8EqCase_Sensitive); + + DN_USize list_index = 0; + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("Afile")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("a_first")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("file1")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("file2")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("file10")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("item1")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("item2")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("item10")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("item12")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("item20")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("m_middle")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("version-1.2.2")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("version-1.2.10")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("version-1.10.0")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("z_last")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + } + + for (DN_UT_Test(&result, "QSort String (Lexicographic)")) { + DN_Str8 list[] = { + DN_Str8Lit("z_last"), + DN_Str8Lit("m_middle"), + DN_Str8Lit("a_first"), + DN_Str8Lit("version-1.2.10"), + DN_Str8Lit("version-1.2.2"), + DN_Str8Lit("version-1.10.0"), + }; + + DN_QSortStr8LexicographicAsc(list, DN_ArrayCountU(list), DN_Str8EqCase_Insensitive); + + DN_USize list_index = 0; + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("a_first")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("m_middle")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("version-1.10.0")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("version-1.2.10")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("version-1.2.2")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + DN_AssertF(DN_Str8Eq(list[list_index++], DN_Str8Lit("z_last")), "%.*s", DN_Str8PrintFmt(list[list_index-1])); + } + // NOTE: MSVC SAL complains that we are using Interlocked functionality on // variables it has detected as *not* being shared across threads. This is // fine, we're just running some basic results, so permit it. @@ -529,7 +589,7 @@ static DN_UTCore DN_TST_BaseArena() static DN_UTCore DN_TST_BaseBytesHex() { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_UTCore test = DN_UT_Init(); DN_UT_LogF(&test, "Bytes <-> Hex\n"); { @@ -935,7 +995,7 @@ static DN_UTCore DN_TST_BaseDSMap() DN_UTCore result = DN_UT_Init(); DN_UT_LogF(&result, "DN_DSMap\n"); { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); { DN_MemList mem = DN_MemListFromVMem(0, 0, DN_MemFlags_Nil); DN_Arena arena = DN_ArenaFromMemList(&mem); @@ -1134,7 +1194,7 @@ static DN_UTCore DN_TST_BaseIArray() struct CustomArray { int *data; - DN_USize size; + DN_USize count; DN_USize max; }; @@ -1145,24 +1205,24 @@ static DN_UTCore DN_TST_BaseIArray() for (DN_UT_Test(&result, "Make item")) { int *item = DN_IArrayMake(&array, DN_ZMem_Yes); - DN_UT_Assert(&result, item && array.size == 1); + DN_UT_Assert(&result, item && array.count == 1); } } return result; } -static DN_UTCore DN_TST_BaseCArray2() +static DN_UTCore DN_TST_BaseArray() { DN_UTCore result = DN_UT_Init(); - DN_UT_LogF(&result, "DN_CArray2\n"); + DN_UT_LogF(&result, "DN_Array\n"); { for (DN_UT_Test(&result, "Positive count, middle of array, stable erase")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 3, 2, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 3, 2, DN_ArrayErase_Stable); int expected[] = {0, 1, 2, 5, 6, 7, 8, 9}; DN_UT_Assert(&result, erase.items_erased == 2); - DN_UT_Assert(&result, erase.it_index == 3); + DN_UT_AssertF(&result, erase.it_index == 2, "erase.it_index=%zu", erase.it_index); DN_UT_Assert(&result, size == 8); DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0); } @@ -1170,10 +1230,10 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Negative count, middle of array, stable erase")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 5, -3, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, -3, DN_ArrayErase_Stable); int expected[] = {0, 1, 2, 6, 7, 8, 9}; DN_UT_Assert(&result, erase.items_erased == 3); - DN_UT_Assert(&result, erase.it_index == 3); + DN_UT_Assert(&result, erase.it_index == 2); DN_UT_Assert(&result, size == 7); DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0); } @@ -1181,10 +1241,10 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "count = -1, stable erase")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 5, -1, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, -1, DN_ArrayErase_Stable); int expected[] = {0, 1, 2, 3, 4, 6, 7, 8, 9}; DN_UT_Assert(&result, erase.items_erased == 1); - DN_UT_Assert(&result, erase.it_index == 5); + DN_UT_Assert(&result, erase.it_index == 4); DN_UT_Assert(&result, size == 9); DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0); } @@ -1192,10 +1252,10 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Positive count, unstable erase")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 3, 2, DN_ArrayErase_Unstable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 3, 2, DN_ArrayErase_Unstable); int expected[] = {0, 1, 2, 8, 9, 5, 6, 7}; DN_UT_Assert(&result, erase.items_erased == 2); - DN_UT_Assert(&result, erase.it_index == 3); + DN_UT_Assert(&result, erase.it_index == 2); DN_UT_Assert(&result, size == 8); DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0); } @@ -1203,10 +1263,10 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Negative count, unstable erase")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 5, -3, DN_ArrayErase_Unstable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, -3, DN_ArrayErase_Unstable); int expected[] = {0, 1, 2, 7, 8, 9, 6}; DN_UT_Assert(&result, erase.items_erased == 3); - DN_UT_Assert(&result, erase.it_index == 3); + DN_UT_Assert(&result, erase.it_index == 2); DN_UT_Assert(&result, size == 7); DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0); } @@ -1214,7 +1274,7 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Edge case - begin_index at start, negative count")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 0, -2, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 0, -2, DN_ArrayErase_Stable); int expected[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_UT_Assert(&result, erase.items_erased == 1); DN_UT_Assert(&result, erase.it_index == 0); @@ -1225,10 +1285,10 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Edge case - begin_index at end, positive count")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 9, 2, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 9, 2, DN_ArrayErase_Stable); int expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; DN_UT_Assert(&result, erase.items_erased == 1); - DN_UT_Assert(&result, erase.it_index == 9); + DN_UT_Assert(&result, erase.it_index == 8); DN_UT_Assert(&result, size == 9); DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0); } @@ -1236,7 +1296,7 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Invalid input - count = 0")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 5, 0, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, 0, DN_ArrayErase_Stable); int expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_UT_Assert(&result, erase.items_erased == 0); DN_UT_Assert(&result, erase.it_index == 0); @@ -1246,7 +1306,7 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Invalid input - null data")) { DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(nullptr, &size, sizeof(int), 5, 2, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(nullptr, &size, sizeof(int), 5, 2, DN_ArrayErase_Stable); DN_UT_Assert(&result, erase.items_erased == 0); DN_UT_Assert(&result, erase.it_index == 0); DN_UT_Assert(&result, size == 10); @@ -1254,7 +1314,7 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Invalid input - null size")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, NULL, sizeof(arr[0]), 5, 2, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, NULL, sizeof(arr[0]), 5, 2, DN_ArrayErase_Stable); DN_UT_Assert(&result, erase.items_erased == 0); DN_UT_Assert(&result, erase.it_index == 0); } @@ -1262,7 +1322,7 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Invalid input - empty array")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 0; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 5, 2, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, 2, DN_ArrayErase_Stable); DN_UT_Assert(&result, erase.items_erased == 0); DN_UT_Assert(&result, erase.it_index == 0); DN_UT_Assert(&result, size == 0); @@ -1271,10 +1331,10 @@ static DN_UTCore DN_TST_BaseCArray2() for (DN_UT_Test(&result, "Out-of-bounds begin_index")) { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_USize size = 10; - DN_ArrayEraseResult erase = DN_CArrayEraseRange(arr, &size, sizeof(arr[0]), 15, 2, DN_ArrayErase_Stable); + DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 15, 2, DN_ArrayErase_Stable); int expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; DN_UT_Assert(&result, erase.items_erased == 0); - DN_UT_Assert(&result, erase.it_index == 10); + DN_UT_Assert(&result, erase.it_index == 9); DN_UT_Assert(&result, size == 10); DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0); } @@ -1632,7 +1692,7 @@ DN_Str8 const DN_UT_HASH_STRING_[] = void DN_TST_KeccakDispatch_(DN_UTCore *test, int hash_type, DN_Str8 input) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 input_hex = DN_HexFromPtrBytesArena(input.data, input.size, &scratch.arena, DN_TrimLeadingZero_No); switch (hash_type) { @@ -1860,7 +1920,7 @@ static DN_UTCore DN_TST_OS() } for (DN_UT_Test(&result, "Query executable directory")) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 os_result = DN_OS_EXEDir(&scratch.arena); DN_UT_Assert(&result, os_result.size); DN_UT_AssertF(&result, DN_OS_PathIsDir(os_result), "result(%zu): %.*s", os_result.size, DN_Str8PrintFmt(os_result)); @@ -1911,7 +1971,7 @@ static DN_UTCore DN_TST_OS() DN_UT_Assert(&result, DN_OS_PathIsFile(SRC_FILE)); // NOTE: Read step - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 read_file = DN_OS_FileReadAllArena(&scratch.arena, SRC_FILE, nullptr); DN_UT_AssertF(&result, read_file.size, "Failed to load file"); DN_UT_AssertF(&result, read_file.size == 4, "File read wrong amount of bytes (%zu)", read_file.size); @@ -2166,7 +2226,7 @@ static DN_UTCore DN_TST_BaseStrings() } for (DN_UT_Test(&result, "Initialise with format string")) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 string = DN_Str8FromFmtArena(&scratch.arena, "%s", "AB"); DN_UT_AssertF(&result, string.size == 2, "size: %zu", string.size); DN_UT_AssertF(&result, string.data[0] == 'A', "string[0]: %c", string.data[0]); @@ -2176,7 +2236,7 @@ static DN_UTCore DN_TST_BaseStrings() } for (DN_UT_Test(&result, "Copy string")) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 string = DN_Str8Lit("AB"); DN_Str8 copy = DN_Str8FromStr8Arena(string, &scratch.arena); DN_UT_AssertF(&result, copy.size == 2, "size: %zu", copy.size); @@ -2192,7 +2252,7 @@ static DN_UTCore DN_TST_BaseStrings() } for (DN_UT_Test(&result, "Allocate string from arena")) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 string = DN_Str8AllocArena(2, DN_ZMem_No, &scratch.arena); DN_UT_AssertF(&result, string.size == 2, "size: %zu", string.size); DN_TCScratchEnd(&scratch); @@ -2530,7 +2590,7 @@ static DN_UTCore DN_TST_BaseStrings() // NOTE: DN_Str8TruncMiddle (arena wrapper) for (DN_UT_Test(&result, "TruncMiddle: Arena wrapper allocates and truncates correctly")) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 str = DN_Str8Lit("HelloBeautifulWorld"); DN_Str8 trunc = DN_Str8Lit("..."); DN_Str8TruncResult res = DN_Str8TruncMiddle(str, 5, trunc, &scratch.arena); @@ -2551,7 +2611,7 @@ static DN_UTCore DN_TST_Win() #if defined(DN_PLATFORM_WIN32) DN_UT_LogF(&result, "OS Win32\n"); { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 input8 = DN_Str8Lit("String"); DN_Str16 input16 = DN_Str16{(wchar_t *)(L"String"), sizeof(L"String") / sizeof(L"String"[0]) - 1}; @@ -2700,7 +2760,7 @@ DN_TSTResult DN_TST_RunSuite(DN_TSTPrint print) #endif DN_TST_BaseDSMap(), DN_TST_BaseIArray(), - DN_TST_BaseCArray2(), + DN_TST_BaseArray(), DN_TST_BaseVArray(), DN_TST_Keccak(), DN_TST_M4(), diff --git a/Source/Extra/dn_tests_main.cpp b/Source/Extra/dn_tests_main.cpp index 093f63c..49af0bd 100644 --- a/Source/Extra/dn_tests_main.cpp +++ b/Source/Extra/dn_tests_main.cpp @@ -35,8 +35,8 @@ DN_MSVC_WARNING_PUSH DN_MSVC_WARNING_DISABLE(6262) // Function uses '29804' bytes of stack. Consider moving some data to heap. int main(int, char**) { - DN_Core dn = {}; - DN_Init(&dn, DN_InitFlags_LogAllFeatures | DN_InitFlags_OS | DN_InitFlags_ThreadContext, nullptr); + DN_Core dn = {}; + DN_Init(&dn, DN_InitFlags_LogAllFeatures | DN_InitFlags_OS | DN_InitFlags_ThreadContext, DN_TCInitArgsDefault()); DN_TST_RunSuite(DN_TSTPrint_Yes); return 0; } diff --git a/Source/OS/dn_os.cpp b/Source/OS/dn_os.cpp index 0c62c4f..94ac11a 100644 --- a/Source/OS/dn_os.cpp +++ b/Source/OS/dn_os.cpp @@ -67,6 +67,30 @@ DN_API DN_MemList DN_MemListFromVMem(DN_U64 reserve, DN_U64 commit, DN_MemFlags return result; } +DN_API DN_Arena DN_ArenaFromHeap(DN_U64 size, DN_MemFlags flags) +{ + DN_MemList mem = DN_MemListFromHeap(size, flags); + DN_Arena result = {}; + result.flags |= DN_ArenaFlags_OwnsMemList; + result.mem = DN_MemListNewCopy(&mem, DN_MemList, &mem); + return result; +} + +DN_API DN_Arena DN_ArenaFromVMem(DN_U64 reserve, DN_U64 commit, DN_MemFlags flags) +{ + DN_MemList mem = DN_MemListFromVMem(reserve, commit, flags); + DN_Arena result = {}; + result.flags |= DN_ArenaFlags_OwnsMemList; + result.mem = DN_MemListNewCopy(&mem, DN_MemList, &mem); + return result; +} + +DN_API void DN_ArenaDeinit(DN_Arena *arena) +{ + if (arena->flags & DN_ArenaFlags_OwnsMemList) + DN_MemListDeinit(arena->mem); +} + DN_API DN_Str8 DN_Str8FromHeapF(DN_FMT_ATTRIB char const *fmt, ...) { va_list args; @@ -92,7 +116,7 @@ DN_API DN_Str8 DN_Str8FromHeap(DN_USize size, DN_ZMem z_mem) DN_API DN_Str8 DN_Str8PadNewLines(DN_Arena *arena, DN_Str8 src, DN_Str8 pad) { // TODO: Implement this without requiring TLS so it can go into base strings - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_Str8Builder builder = DN_Str8BuilderFromArena(&scratch.arena); DN_Str8BSplitResult split = DN_Str8BSplit(src, DN_Str8Lit("\n")); while (split.lhs.size) { @@ -102,7 +126,7 @@ DN_API DN_Str8 DN_Str8PadNewLines(DN_Arena *arena, DN_Str8 src, DN_Str8 pad) if (split.lhs.size) DN_Str8BuilderAppendRef(&builder, DN_Str8Lit("\n")); } - DN_Str8 result = DN_Str8BuilderBuild(&builder, arena); + DN_Str8 result = DN_Str8FromStr8BuilderArena(&builder, arena); DN_TCScratchEnd(&scratch); return result; } @@ -135,7 +159,7 @@ DN_API void DN_OS_LogPrint(DN_LogTypeParam type, void *user_data, DN_CallSite ca // NOTE: Open log file for appending if requested DN_TicketMutex_Begin(&os->log_file_mutex); if (os->log_to_file && !os->log_file.handle && !os->log_file.error) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 exe_dir = DN_OS_EXEDir(&scratch.arena); DN_Str8 log_path = DN_OS_PathF(&scratch.arena, "%.*s/dn.log", DN_Str8PrintFmt(exe_dir)); os->log_file = DN_OS_FileOpen(log_path, DN_OSFileOpen_CreateAlways, DN_OSFileAccess_AppendOnly, nullptr); @@ -250,7 +274,7 @@ DN_API DN_Str8 DN_OS_EXEDir(DN_Arena *arena) DN_Str8 result = {}; if (!arena) return result; - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_Str8 exe_path = DN_OS_EXEPath(&scratch.arena); DN_Str8 separators[] = {DN_Str8Lit("/"), DN_Str8Lit("\\")}; DN_Str8BSplitResult split = DN_Str8BSplitLastArray(exe_path, separators, DN_ArrayCountU(separators)); @@ -421,7 +445,7 @@ DN_API DN_Str8 DN_OS_FileReadAll(DN_Allocator allocator, DN_Str8 path, DN_ErrSin } if (!result.data) { - DN_Str8x32 bytes_str = DN_ByteCountStr8x32(path_info.size); + DN_Str8x32 bytes_str = DN_Str8x32FromByteCountU64Auto(path_info.size); DN_ErrSinkAppendF(err, 1 /*err_code*/, "Failed to allocate %.*s for reading file '%.*s'", DN_Str8PrintFmt(bytes_str), DN_Str8PrintFmt(path)); return result; } @@ -475,7 +499,7 @@ DN_API bool DN_OS_FileWriteAll(DN_Str8 path, DN_Str8 buffer, DN_ErrSink *error) DN_API bool DN_OS_FileWriteAllFV(DN_Str8 file_path, DN_ErrSink *error, DN_FMT_ATTRIB char const *fmt, va_list args) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 buffer = DN_Str8FromFmtVArena(&scratch.arena, fmt, args); bool result = DN_OS_FileWriteAll(file_path, buffer, error); DN_TCScratchEnd(&scratch); @@ -493,7 +517,7 @@ DN_API bool DN_OS_FileWriteAllF(DN_Str8 file_path, DN_ErrSink *error, DN_FMT_ATT DN_API bool DN_OS_FileWriteAllSafe(DN_Str8 path, DN_Str8 buffer, DN_ErrSink *error) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 tmp_path = DN_Str8FromFmtArena(&scratch.arena, "%.*s.tmp", DN_Str8PrintFmt(path)); if (!DN_OS_FileWriteAll(tmp_path, buffer, error)) { DN_TCScratchEnd(&scratch); @@ -513,7 +537,7 @@ DN_API bool DN_OS_FileWriteAllSafe(DN_Str8 path, DN_Str8 buffer, DN_ErrSink *err DN_API bool DN_OS_FileWriteAllSafeFV(DN_Str8 path, DN_ErrSink *error, DN_FMT_ATTRIB char const *fmt, va_list args) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 buffer = DN_Str8FromFmtVArena(&scratch.arena, fmt, args); bool result = DN_OS_FileWriteAllSafe(path, buffer, error); DN_TCScratchEnd(&scratch); @@ -630,7 +654,7 @@ DN_API DN_Str8 DN_OS_PathTo(DN_Arena *arena, DN_Str8 path, DN_Str8 path_separato DN_API DN_Str8 DN_OS_PathToF(DN_Arena *arena, DN_Str8 path_separator, DN_FMT_ATTRIB char const *fmt, ...) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); va_list args; va_start(args, fmt); DN_Str8 path = DN_Str8FromFmtVArena(&scratch.arena, fmt, args); @@ -648,7 +672,7 @@ DN_API DN_Str8 DN_OS_Path(DN_Arena *arena, DN_Str8 path) DN_API DN_Str8 DN_OS_PathF(DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); va_list args; va_start(args, fmt); DN_Str8 path = DN_Str8FromFmtVArena(&scratch.arena, fmt, args); @@ -714,9 +738,8 @@ DN_API DN_OSExecResult DN_OS_ExecOrAbort(DN_Str8Slice cmd_line, DN_OSExecArgs *a // NOTE: DN_OSThread static void DN_OS_ThreadExecute_(void *user_context) { - DN_OSThread *thread = DN_Cast(DN_OSThread *) user_context; - DN_MemFuncs mem_funcs = DN_MemFuncsDefault(); - DN_TCInitFromMemFuncs(&thread->context, thread->thread_id, /*args=*/nullptr, mem_funcs); + DN_OSThread *thread = DN_Cast(DN_OSThread *) user_context; + DN_TCInitFromMemFuncs(&thread->context, thread->thread_id, DN_TCInitArgsDefault(), DN_MemFuncsDefault()); DN_TCEquip(&thread->context); if (thread->is_lane_set) { DN_OS_TCThreadLaneEquip(thread->lane); @@ -833,7 +856,7 @@ DN_API DN_OSHttpResponse DN_OS_HttpRequest(DN_Arena *arena, DN_Str8 host, DN_Str { // TODO(doyle): Revise the memory allocation and its lifetime DN_OSHttpResponse result = {}; - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); result.scratch_arena = scratch.arena; DN_OS_HttpRequestAsync(&result, arena, host, path, secure, method, body, headers); @@ -1116,7 +1139,7 @@ T *DN_OS_VArrayInsertArray(DN_VArray *array, DN_USize index, T const *items, if (!DN_OS_VArrayIsValid(array)) return result; if (DN_OS_VArrayReserve(array, array->size + count)) - result = DN_CArrayInsertArray(array->data, &array->size, array->max, index, items, count); + result = DN_ArrayInsertArray(array->data, &array->size, array->max, index, items, count); return result; } @@ -1137,14 +1160,14 @@ T *DN_OS_VArrayInsert(DN_VArray *array, DN_USize index, T const &item) template T *DN_OS_VArrayPopFront(DN_VArray *array, DN_USize count) { - T *result = DN_Cast(T *)DN_CArrayPopFront(array->data, &array->size, sizeof(T), count); + T *result = DN_Cast(T *)DN_ArrayPopFront(array->data, &array->size, sizeof(T), count); return result; } template T *DN_OS_VArrayPopBack(DN_VArray *array, DN_USize count) { - T *result = DN_Cast(T *)DN_CArrayPopBack(array->data, &array->size, sizeof(T), count); + T *result = DN_Cast(T *)DN_ArrayPopBack(array->data, &array->size, sizeof(T), count); return result; } @@ -1154,7 +1177,7 @@ DN_ArrayEraseResult DN_OS_VArrayEraseRange(DN_VArray *array, DN_USize begin_i DN_ArrayEraseResult result = {}; if (!DN_OS_VArrayIsValid(array)) return result; - result = DN_CArrayEraseRange(array->data, &array->size, sizeof(T), begin_index, count, erase); + result = DN_ArrayEraseRange(array->data, &array->size, sizeof(T), begin_index, count, erase); return result; } @@ -1183,12 +1206,11 @@ bool DN_OS_VArrayReserve(DN_VArray *array, DN_USize count) return result; } -// NOTE: Stack Trace -DN_API DN_StackTraceWalkResult DN_StackTraceWalk(DN_Arena *arena, DN_U16 limit) +DN_API DN_StackTrace DN_StackTraceFromAllocator(DN_Allocator allocator, DN_U16 limit) { - DN_StackTraceWalkResult result = {}; + DN_StackTrace result = {}; #if defined(DN_OS_WIN32) - if (!arena) + if (!allocator.context) return result; static DN_TicketMutex mutex = {}; @@ -1202,7 +1224,7 @@ DN_API DN_StackTraceWalkResult DN_StackTraceWalk(DN_Arena *arena, DN_U16 limit) w32->sym_initialised = true; SymSetOptions(SYMOPT_LOAD_LINES); if (!SymInitialize(result.process, nullptr /*UserSearchPath*/, true /*fInvadeProcess*/)) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginAllocator(&allocator, 1); DN_OSW32Error error = DN_OS_W32LastError(&scratch.arena); DN_LogErrorF("SymInitialize failed, stack trace can not be generated (%lu): %.*s\n", error.code, DN_Str8PrintFmt(error.msg)); DN_TCScratchEnd(&scratch); @@ -1240,7 +1262,9 @@ DN_API DN_StackTraceWalkResult DN_StackTraceWalk(DN_Arena *arena, DN_U16 limit) } DN_TicketMutex_End(&mutex); - result.base_addr = DN_ArenaNewArray(arena, DN_U64, raw_frames_count, DN_ZMem_No); + result.base_addr = DN_Cast(DN_U64 *)DN_AllocatorAlloc(allocator, raw_frames_count * sizeof(DN_U64), alignof(DN_U64), DN_ZMem_No); + DN_Assert(result.base_addr); + result.size = DN_Cast(DN_U16) raw_frames_count; DN_Memcpy(result.base_addr, raw_frames, raw_frames_count * sizeof(raw_frames[0])); #else @@ -1250,66 +1274,91 @@ DN_API DN_StackTraceWalkResult DN_StackTraceWalk(DN_Arena *arena, DN_U16 limit) return result; } -static void DN_StackTraceAddWalkToStr8Builder(DN_StackTraceWalkResult const *walk, DN_Str8Builder *builder, DN_USize skip) + +DN_API DN_StackTrace DN_StackTraceFromArena(DN_Arena *arena, DN_U16 limit) +{ + DN_Allocator allocator = DN_AllocatorFromArena(arena); + DN_StackTrace result = DN_StackTraceFromAllocator(allocator, limit); + return result; +} + +static void DN_StackTraceAddToStr8Builder_(DN_StackTrace const *trace, DN_Str8Builder *builder, DN_USize skip) { DN_StackTraceRawFrame raw_frame = {}; - raw_frame.process = walk->process; - for (DN_USize index = skip; index < walk->size; index++) { - raw_frame.base_addr = walk->base_addr[index]; + raw_frame.process = trace->process; + for (DN_USize index = skip; index < trace->size; index++) { + raw_frame.base_addr = trace->base_addr[index]; DN_StackTraceFrame frame = DN_StackTraceRawFrameToFrame(builder->arena, raw_frame); - DN_Str8BuilderAppendF(builder, "%.*s(%zu): %.*s%s", DN_Str8PrintFmt(frame.file_name), frame.line_number, DN_Str8PrintFmt(frame.function_name), (DN_Cast(int) index == walk->size - 1) ? "" : "\n"); + DN_Str8BuilderAppendF(builder, "%.*s(%zu): %.*s%s", DN_Str8PrintFmt(frame.file_name), frame.line_number, DN_Str8PrintFmt(frame.function_name), (DN_Cast(int) index == trace->size - 1) ? "" : "\n"); } } -DN_API bool DN_StackTraceWalkResultIterate(DN_StackTraceWalkResultIterator *it, DN_StackTraceWalkResult const *walk) +DN_API bool DN_StackTraceIterate(DN_StackTraceIterator *it, DN_StackTrace const *trace) { bool result = false; - if (!it || !walk || !walk->base_addr || !walk->process) + if (!it || !trace || !trace->base_addr || !trace->process) return result; - if (it->index >= walk->size) + if (it->index >= trace->size) return false; result = true; - it->raw_frame.process = walk->process; - it->raw_frame.base_addr = walk->base_addr[it->index++]; + it->raw_frame.process = trace->process; + it->raw_frame.base_addr = trace->base_addr[it->index++]; return result; } -DN_API DN_Str8 DN_StackTraceWalkResultToStr8(DN_Arena *arena, DN_StackTraceWalkResult const *walk, DN_U16 skip) +DN_API DN_Str8 DN_Str8FromStackTraceAllocator(DN_Allocator allocator, DN_StackTrace const *trace, DN_U16 skip) { - DN_Str8 result{}; - if (!walk || !arena) + DN_Str8 result = {}; + if (!trace) return result; - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginAllocator(&allocator, 1); DN_Str8Builder builder = DN_Str8BuilderFromArena(&scratch.arena); - DN_StackTraceAddWalkToStr8Builder(walk, &builder, skip); - result = DN_Str8BuilderBuild(&builder, arena); + DN_StackTraceAddToStr8Builder_(trace, &builder, skip); + result = DN_Str8FromStr8BuilderAllocator(&builder, allocator); DN_TCScratchEnd(&scratch); return result; } -DN_API DN_Str8 DN_StackTraceWalkStr8(DN_Arena *arena, DN_U16 limit, DN_U16 skip) +DN_API DN_Str8 DN_Str8FromStackTraceArena(DN_Arena *arena, DN_StackTrace const *trace, DN_U16 skip) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); - DN_StackTraceWalkResult walk = DN_StackTraceWalk(&scratch.arena, limit); - DN_Str8 result = DN_StackTraceWalkResultToStr8(arena, &walk, skip); + DN_Str8 result = {}; + if (!trace || !arena) + return result; + + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); + DN_Str8Builder builder = DN_Str8BuilderFromArena(&scratch.arena); + DN_StackTraceAddToStr8Builder_(trace, &builder, skip); + result = DN_Str8FromStr8BuilderArena(&builder, arena); DN_TCScratchEnd(&scratch); return result; } -DN_API DN_Str8 DN_StackTraceWalkStr8FromHeap(DN_U16 limit, DN_U16 skip) +DN_API DN_Str8 DN_Str8FromStackTraceNowAllocator(DN_Allocator allocator, DN_U16 limit, DN_U16 skip) { - // NOTE: We don't use WalkResultToStr8 because that uses the TLS arenas which - // does not use the OS heap. - DN_MemList mem = DN_MemListFromHeap(DN_Kilobytes(64), DN_MemFlags_NoAllocTrack); - DN_Arena arena = DN_ArenaFromMemList(&mem); - DN_Str8Builder builder = DN_Str8BuilderFromArena(&arena); - DN_StackTraceWalkResult walk = DN_StackTraceWalk(&arena, limit); - DN_StackTraceAddWalkToStr8Builder(&walk, &builder, skip); + DN_TCScratch scratch = DN_TCScratchBeginArena(DN_Cast(DN_Arena **) & allocator.context, 1); + DN_StackTrace walk = DN_StackTraceFromArena(&scratch.arena, limit); + DN_Str8 result = DN_Str8FromStackTraceAllocator(allocator, &walk, skip); + DN_TCScratchEnd(&scratch); + return result; +} + +DN_API DN_Str8 DN_Str8FromStackTraceNowArena(DN_Arena *arena, DN_U16 limit, DN_U16 skip) +{ + DN_Str8 result = DN_Str8FromStackTraceNowAllocator(DN_AllocatorFromArena(arena), limit, skip); + return result; +} + +DN_API DN_Str8 DN_Str8FromStackTraceNowHeap(DN_U16 limit, DN_U16 skip) +{ + DN_Arena arena = DN_ArenaFromHeap(DN_Kilobytes(64), DN_MemFlags_NoAllocTrack); + DN_Str8Builder builder = DN_Str8BuilderFromArena(&arena); + DN_StackTrace walk = DN_StackTraceFromArena(&arena, limit); + DN_StackTraceAddToStr8Builder_(&walk, &builder, skip); DN_Str8 result = DN_Str8BuilderBuildFromHeap(&builder); - DN_MemListDeinit(&mem); + DN_ArenaDeinit(&arena); return result; } @@ -1319,12 +1368,12 @@ DN_API DN_StackTraceFrameSlice DN_StackTraceGetFrames(DN_Arena *arena, DN_U16 li if (!arena) return result; - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); - DN_StackTraceWalkResult walk = DN_StackTraceWalk(&scratch.arena, limit); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); + DN_StackTrace walk = DN_StackTraceFromArena(&scratch.arena, limit); if (walk.size) { if (DN_ISliceAllocArena(&result, walk.size, DN_ZMem_No, arena)) { DN_USize slice_index = 0; - for (DN_StackTraceWalkResultIterator it = {}; DN_StackTraceWalkResultIterate(&it, &walk);) + for (DN_StackTraceIterator it = {}; DN_StackTraceIterate(&it, &walk);) result.data[slice_index++] = DN_StackTraceRawFrameToFrame(arena, it.raw_frame); } } @@ -1381,7 +1430,7 @@ DN_API DN_StackTraceFrame DN_StackTraceRawFrameToFrame(DN_Arena *arena, DN_Stack DN_API void DN_StackTracePrint(DN_U16 limit) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_StackTraceFrameSlice stack_trace = DN_StackTraceGetFrames(&scratch.arena, limit); for (DN_ForItSize(it, DN_StackTraceFrame, stack_trace.data, stack_trace.count)) { DN_StackTraceFrame frame = *it.data; diff --git a/Source/OS/dn_os.h b/Source/OS/dn_os.h index a29f1f0..f641817 100644 --- a/Source/OS/dn_os.h +++ b/Source/OS/dn_os.h @@ -297,7 +297,8 @@ DN_API DN_MemFuncs DN_MemFuncsFromType (D DN_API DN_MemFuncs DN_MemFuncsDefault (); DN_API DN_MemList DN_MemListFromHeap (DN_U64 size, DN_MemFlags flags); DN_API DN_MemList DN_MemListFromVMem (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags); - +DN_API DN_Arena DN_ArenaFromHeap (DN_U64 wize, DN_MemFlags flags); +DN_API DN_Arena DN_ArenaFromVMem (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags); DN_API DN_Str8 DN_Str8FromHeapF (DN_FMT_ATTRIB char const *fmt, ...); DN_API DN_Str8 DN_Str8FromHeap (DN_USize size, DN_ZMem z_mem); diff --git a/Source/OS/dn_os_w32.cpp b/Source/OS/dn_os_w32.cpp index 1c6e1a4..8808894 100644 --- a/Source/OS/dn_os_w32.cpp +++ b/Source/OS/dn_os_w32.cpp @@ -209,7 +209,7 @@ DN_API void DN_OS_GenBytesSecure(void *buffer, DN_U32 size) DN_API DN_OSDiskSpace DN_OS_DiskSpace(DN_Str8 path) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_OSDiskSpace result = {}; DN_Str16 path16 = DN_OS_W32Str8ToStr16(&scratch.arena, path); @@ -233,7 +233,7 @@ DN_API DN_OSDiskSpace DN_OS_DiskSpace(DN_Str8 path) DN_API bool DN_OS_SetEnvVar(DN_Str8 name, DN_Str8 value) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str16 name16 = DN_OS_W32Str8ToStr16(&scratch.arena, name); DN_Str16 value16 = DN_OS_W32Str8ToStr16(&scratch.arena, value); bool result = SetEnvironmentVariableW(name16.data, value16.data) != 0; @@ -246,7 +246,7 @@ DN_API DN_Str8 DN_OS_EXEPath(DN_Arena *arena) DN_Str8 result = {}; if (!arena) return result; - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_Str16 exe_dir16 = DN_OS_W32EXEPathW(&scratch.arena); result = DN_OS_W32Str16ToStr8(arena, exe_dir16); DN_TCScratchEnd(&scratch); @@ -286,7 +286,7 @@ static DN_U64 DN_OS_W32FileTimeToSeconds_(FILETIME const *time) DN_API bool DN_OS_FileCopy(DN_Str8 src, DN_Str8 dest, bool overwrite, DN_ErrSink *err) { bool result = false; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str16 src16 = DN_OS_W32Str8ToStr16(&scratch.arena, src); DN_Str16 dest16 = DN_OS_W32Str8ToStr16(&scratch.arena, dest); @@ -310,7 +310,7 @@ DN_API bool DN_OS_FileCopy(DN_Str8 src, DN_Str8 dest, bool overwrite, DN_ErrSink DN_API bool DN_OS_FileMove(DN_Str8 src, DN_Str8 dest, bool overwrite, DN_ErrSink *err) { bool result = false; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str16 src16 = DN_OS_W32Str8ToStr16(&scratch.arena, src); DN_Str16 dest16 = DN_OS_W32Str8ToStr16(&scratch.arena, dest); @@ -366,7 +366,7 @@ DN_API DN_OSFile DN_OS_FileOpen(DN_Str8 path, DN_OSFileOpen open_mode, DN_OSFile access_mode |= GENERIC_EXECUTE; } - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str16 path16 = DN_OS_W32Str8ToStr16(&scratch.arena, path); void *handle = CreateFileW(/*LPCWSTR lpFileName*/ path16.data, /*DWORD dwDesiredAccess*/ access_mode, @@ -395,9 +395,9 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size if (!file || !file->handle || file->error || !buffer || size <= 0) return result; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); if (!DN_Check(size <= (unsigned long)-1)) { - DN_Str8x32 buffer_size_str8 = DN_ByteCountStr8x32(size); + DN_Str8x32 buffer_size_str8 = DN_Str8x32FromByteCountU64Auto(size); DN_ErrSinkAppendF( err, 1 /*error_code*/, @@ -455,9 +455,9 @@ DN_API bool DN_OS_FileWritePtr(DN_OSFile *file, void const *buffer, DN_USize siz } if (!result) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena); - DN_Str8x32 buffer_size_str8 = DN_ByteCountStr8x32(size); + DN_Str8x32 buffer_size_str8 = DN_Str8x32FromByteCountU64Auto(size); DN_ErrSinkAppendF(err, win_error.code, "Failed to write buffer (%.*s) to file handle: %.*s", DN_Str8PrintFmt(buffer_size_str8), DN_Str8PrintFmt(win_error.msg)); DN_TCScratchEnd(&scratch); } @@ -471,7 +471,7 @@ DN_API bool DN_OS_FileFlush(DN_OSFile *file, DN_ErrSink *err) BOOL result = FlushFileBuffers(DN_Cast(HANDLE) file->handle); if (!result) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena); DN_ErrSinkAppendF(err, win_error.code, "Failed to flush file buffer to disk: %.*s", DN_Str8PrintFmt(win_error.msg)); DN_TCScratchEnd(&scratch); @@ -494,7 +494,7 @@ DN_API DN_OSPathInfo DN_OS_PathInfo(DN_Str8 path) if (path.size == 0) return result; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str16 path16 = DN_OS_W32Str8ToStr16(&scratch.arena, path); WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; @@ -530,7 +530,7 @@ DN_API bool DN_OS_PathDelete(DN_Str8 path) if (path.size == 0) return result; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str16 path16 = DN_OS_W32Str8ToStr16(&scratch.arena, path); if (path16.size) { result = DeleteFileW(path16.data); @@ -547,7 +547,7 @@ DN_API bool DN_OS_PathIsFile(DN_Str8 path) if (path.size == 0) return result; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str16 path16 = DN_OS_W32Str8ToStr16(&scratch.arena, path); if (path16.size) { WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; @@ -565,7 +565,7 @@ DN_API bool DN_OS_PathIsDir(DN_Str8 path) if (path.size == 0) return result; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str16 path16 = DN_OS_W32Str8ToStr16(&scratch.arena, path); if (path16.size) { WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; @@ -581,7 +581,7 @@ DN_API bool DN_OS_PathIsDir(DN_Str8 path) DN_API bool DN_OS_PathMakeDir(DN_Str8 path) { bool result = true; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str16 path16 = DN_OS_W32Str8ToStr16(&scratch.arena, path); // NOTE: Go back from the end of the string to all the directories in the @@ -636,7 +636,7 @@ DN_API bool DN_OS_PathIterateDir(DN_Str8 path, DN_OSDirIterator *it) if (path.size == 0 || !it || path.size <= 0) return false; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_OSW32FolderIteratorW wide_it = {}; DN_Str16 path16 = {}; if (it->handle) { @@ -716,7 +716,7 @@ DN_API DN_OSExecResult DN_OS_ExecPump(DN_OSExecAsyncHandle handle, return result; } - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DWORD stdout_bytes_available = 0; DWORD stderr_bytes_available = 0; PeekNamedPipe(handle.stdout_read, nullptr, 0, nullptr, &stdout_bytes_available, nullptr); @@ -822,7 +822,7 @@ DN_API DN_OSExecResult DN_OS_ExecWait(DN_OSExecAsyncHandle handle, DN_Arena *are return result; } - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_Str8Builder stdout_builder = {}; DN_Str8Builder stderr_builder = {}; if (arena) { @@ -845,8 +845,8 @@ DN_API DN_OSExecResult DN_OS_ExecWait(DN_OSExecAsyncHandle handle, DN_Arena *are } // NOTE: Get stdout/stderr. If no arena is passed this is a no-op - result.stdout_text = DN_Str8BuilderBuild(&stdout_builder, arena); - result.stderr_text = DN_Str8BuilderBuild(&stderr_builder, arena); + result.stdout_text = DN_Str8FromStr8BuilderArena(&stdout_builder, arena); + result.stderr_text = DN_Str8FromStr8BuilderArena(&stderr_builder, arena); DN_TCScratchEnd(&scratch); return result; } @@ -858,7 +858,7 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs if (cmd_line.count == 0) return result; - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 cmd_rendered = DN_Str8SliceRender(cmd_line, DN_Str8Lit(" "), &scratch.arena); DN_Str16 cmd16 = DN_OS_W32Str8ToStr16(&scratch.arena, cmd_rendered); DN_Str16 working_dir16 = DN_OS_W32Str8ToStr16(&scratch.arena, args->working_dir); @@ -868,7 +868,7 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs if (env_builder.string_size) DN_Str8BuilderAppendRef(&env_builder, DN_Str8Lit("\0")); - DN_Str8 env_block8 = DN_Str8BuilderBuildDelimited(&env_builder, DN_Str8Lit("\0"), &scratch.arena); + DN_Str8 env_block8 = DN_Str8FromStr8BuilderDelimitArena(&env_builder, DN_Str8Lit("\0"), &scratch.arena); DN_Str16 env_block16 = {}; if (env_block8.size) env_block16 = DN_OS_W32Str8ToStr16(&scratch.arena, env_block8); @@ -1305,7 +1305,7 @@ DN_API void DN_OS_W32ThreadSetName(DN_Str8 name) // // See: https://learn.microsoft.com/en-us/windows/w32/api/processthreadsapi/nf-processthreadsapi-setthreaddescription DN_OSW32Core *w32 = DN_OS_W32GetCore(); - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); if (w32->set_thread_description) { DN_Str16 name16 = DN_OS_W32Str8ToStr16(&scratch.arena, name); w32->set_thread_description(GetCurrentThread(), (WCHAR *)name16.data); @@ -1420,7 +1420,7 @@ void DN_OS_HttpRequestWin32Callback(HINTERNET session, DWORD *dwContext, DWORD d if (request) { bool read_complete = dwInternetStatus == WINHTTP_CALLBACK_STATUS_READ_COMPLETE && dwStatusInformationLength == 0; if (read_complete) - response->body = DN_Str8BuilderBuild(&response->builder, response->arena); + response->body = DN_Str8FromStr8BuilderArena(&response->builder, response->arena); if (read_complete || dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR || error.code) { DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1); @@ -1449,7 +1449,7 @@ DN_API void DN_OS_HttpRequestAsync(DN_OSHttpResponse *response, response->arena = arena; response->builder = DN_Str8BuilderFromArena(response->scratch_arena.mem ? &response->scratch_arena : &response->tmp_arena); - DN_TCScratch scratch_ = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch_ = DN_TCScratchBeginArena(&arena, 1); if (!response->scratch_arena.mem) response->scratch_arena = scratch_.arena; @@ -1743,7 +1743,7 @@ DN_API DN_Str8 DN_OS_W32Str16ToStr8FromHeap(DN_Str16 src) // NOTE: Windows Executable Directory ////////////////////////////////////////// DN_API DN_Str16 DN_OS_W32EXEPathW(DN_Arena *arena) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_Str16 result = {}; DN_USize module_size = 0; wchar_t *module_path = nullptr; @@ -1772,7 +1772,7 @@ DN_API DN_Str16 DN_OS_W32EXEPathW(DN_Arena *arena) DN_API DN_Str16 DN_OS_W32EXEDirW(DN_Arena *arena) { // TODO(doyle): Implement a DN_Str16_BinarySearchReverse - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_Str16 result = {}; DN_USize module_size = 0; wchar_t *module_path = nullptr; @@ -1800,7 +1800,7 @@ DN_API DN_Str16 DN_OS_W32EXEDirW(DN_Arena *arena) DN_API DN_Str8 DN_OS_W32WorkingDir(DN_Arena *arena, DN_Str8 suffix) { - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); DN_Str16 suffix16 = DN_OS_W32Str8ToStr16(&scratch.arena, suffix); DN_Str16 dir16 = DN_OS_W32WorkingDirW(&scratch.arena, suffix16); DN_Str8 result = DN_OS_W32Str16ToStr8(arena, dir16); @@ -1814,7 +1814,7 @@ DN_API DN_Str16 DN_OS_W32WorkingDirW(DN_Arena *arena, DN_Str16 suffix) DN_Str16 result = {}; // NOTE: required_size is the size required *including* the null-terminator - DN_TCScratch scratch = DN_TCScratchBegin(&arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); unsigned long required_size = GetCurrentDirectoryW(0, nullptr); unsigned long desired_size = required_size + DN_Cast(unsigned long) suffix.size; diff --git a/Source/dn.cpp b/Source/dn.cpp index 85e400b..98066dd 100644 --- a/Source/dn.cpp +++ b/Source/dn.cpp @@ -20,7 +20,7 @@ DN_Core *g_dn_; #endif #endif -DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_InitArgs *args) +DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_TCInitArgs args) { DN_Set(dn); dn->init_flags = flags; @@ -106,8 +106,7 @@ DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_InitArgs *args) if (DN_BitIsSet(flags, DN_InitFlags_ThreadContext)) { DN_Assert(dn->os_init); #if DN_H_WITH_OS - DN_TCInitArgs *tc_init_args = args ? &args->thread_context_init_args : nullptr; - DN_TCInitFromMemFuncs(&dn->main_tc, DN_OS_ThreadID(), tc_init_args, DN_MemFuncsDefault()); + DN_TCInitFromMemFuncs(&dn->main_tc, DN_OS_ThreadID(), args, DN_MemFuncsDefault()); DN_TCEquip(&dn->main_tc); #endif } @@ -139,24 +138,18 @@ DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_InitArgs *args) case DN_MemFuncsType_Heap: mem_funcs = DN_Str8Lit("Heap"); break; case DN_MemFuncsType_Virtual: mem_funcs = DN_Str8Lit("Virtual"); break; } - DN_Str8x32 main_commit = DN_ByteCountStr8x32(dn->main_tc.main_arena->mem->curr->commit); - DN_Str8x32 main_reserve = DN_ByteCountStr8x32(dn->main_tc.main_arena->mem->curr->reserve); - DN_Str8x32 temp_commit = DN_ByteCountStr8x32(dn->main_tc.temp_a_arena->mem->curr->commit); - DN_Str8x32 temp_reserve = DN_ByteCountStr8x32(dn->main_tc.temp_a_arena->mem->curr->reserve); - DN_Str8x32 err_commit = DN_ByteCountStr8x32(dn->main_tc.err_sink.arena->mem->curr->commit); - DN_Str8x32 err_reserve = DN_ByteCountStr8x32(dn->main_tc.err_sink.arena->mem->curr->reserve); - DN_FmtAppendTruncate(buf, - &buf_size, - sizeof(buf), - DN_Str8Lit("..."), - "M %.*s/%.*s S(x2) %.*s/%.*s E %.*s/%.*s (%.*s)\n", - DN_Str8PrintFmt(main_commit), - DN_Str8PrintFmt(main_reserve), - DN_Str8PrintFmt(temp_commit), - DN_Str8PrintFmt(temp_reserve), - DN_Str8PrintFmt(err_commit), - DN_Str8PrintFmt(err_reserve), - DN_Str8PrintFmt(mem_funcs)); + DN_Str8x32 main_commit = DN_Str8x32FromByteCountU64Auto(dn->main_tc.main_arena->mem->curr->commit); + DN_Str8x32 main_reserve = DN_Str8x32FromByteCountU64Auto(dn->main_tc.main_arena->mem->curr->reserve); + DN_Str8x32 err_commit = DN_Str8x32FromByteCountU64Auto(dn->main_tc.err_sink.arena->mem->curr->commit); + DN_Str8x32 err_reserve = DN_Str8x32FromByteCountU64Auto(dn->main_tc.err_sink.arena->mem->curr->reserve); + DN_FmtAppendTruncate(buf, &buf_size, sizeof(buf), DN_Str8Lit("..."), "M %.*s/%.*s", DN_Str8PrintFmt(main_commit), DN_Str8PrintFmt(main_reserve)); + if (dn->main_tc.temp_arenas_count) { + DN_Arena *temp = dn->main_tc.temp_arenas[0]; + DN_Str8x32 temp_commit = DN_Str8x32FromByteCountU64Auto(temp->mem->curr->commit); + DN_Str8x32 temp_reserve = DN_Str8x32FromByteCountU64Auto(temp->mem->curr->reserve); + DN_FmtAppendTruncate(buf, &buf_size, sizeof(buf), DN_Str8Lit("..."), " T(x%zu) %.*s/%.*s", dn->main_tc.temp_arenas_count, DN_Str8PrintFmt(temp_commit), DN_Str8PrintFmt(temp_reserve)); + } + DN_FmtAppendTruncate(buf, &buf_size, sizeof(buf), DN_Str8Lit("..."), " E %.*s/%.*s (%.*s)\n", DN_Str8PrintFmt(err_commit), DN_Str8PrintFmt(err_reserve), DN_Str8PrintFmt(mem_funcs)); } else { DN_FmtAppendTruncate(buf, &buf_size, sizeof(buf), DN_Str8Lit("..."), "N/A\n"); } diff --git a/Source/dn.h b/Source/dn.h index 3bee6cc..9d6a1da 100644 --- a/Source/dn.h +++ b/Source/dn.h @@ -143,8 +143,6 @@ // occurs. #include "Base/dn_base.h" -#include "Base/dn_base_assert.h" -#include "Base/dn_base_containers.h" #include "Base/dn_base_leak.h" #if DN_H_WITH_OS @@ -159,11 +157,6 @@ #include "OS/dn_os.h" #endif -struct DN_InitArgs -{ - DN_TCInitArgs thread_context_init_args; -}; - typedef DN_USize DN_InitFlags; enum DN_InitFlags_ { @@ -193,7 +186,7 @@ struct DN_Core #endif }; -DN_API void DN_Init (DN_Core *dn, DN_InitFlags flags, DN_InitArgs *args); +DN_API void DN_Init (DN_Core *dn, DN_InitFlags flags, DN_TCInitArgs args); DN_API void DN_Set (DN_Core *dn); DN_API DN_Core *DN_Get (); DN_API void DN_BeginFrame(); diff --git a/single_header_generator.cpp b/single_header_generator.cpp index 779d0d8..0d5e948 100644 --- a/single_header_generator.cpp +++ b/single_header_generator.cpp @@ -29,7 +29,7 @@ struct File static void AppendCppFileLineByLine(DN_Str8Builder *dest, DN_Str8 cpp_path) { - DN_TCScratch scratch = DN_TCScratchBegin(&dest->arena, 1); + DN_TCScratch scratch = DN_TCScratchBeginArena(&dest->arena, 1); DN_ErrSink *err = DN_TCErrSinkBeginDefault(); DN_Str8 buffer = DN_OS_FileReadAllArena(&scratch.arena, cpp_path, err); DN_ErrSinkEndExitIfErrorF(err, -1, "Failed to load file from '%S' for appending", cpp_path); @@ -93,7 +93,7 @@ static void AppendCppFileLineByLine(DN_Str8Builder *dest, DN_Str8 cpp_path) int main(int argc, char **argv) { DN_Core dn = {}; - DN_Init(&dn, DN_InitFlags_OS | DN_InitFlags_ThreadContext, nullptr); + DN_Init(&dn, DN_InitFlags_OS | DN_InitFlags_ThreadContext, DN_TCInitArgsDefault()); if (argc != 3) { DN_OS_PrintErrF("USAGE: %s ", argv[0]); @@ -114,7 +114,7 @@ int main(int argc, char **argv) }; for (DN_ForIndexU(type, FileType_Count)) { - DN_TCScratch scratch = DN_TCScratchBegin(nullptr, 0); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8Builder builder = DN_Str8BuilderFromArena(&scratch.arena); DN_Str8 suffix = type == FileType_Header ? DN_Str8Lit("h") : DN_Str8Lit("cpp"); for (DN_ForItCArray(it, DN_Str8 const, REL_FILE_PATHS)) { @@ -125,7 +125,7 @@ int main(int argc, char **argv) DN_Date date = DN_OS_DateLocalTimeNow(); DN_Str8BuilderPrependF(&builder, "// Generated by the DN single header generator %04u-%02u-%02u %02u:%02u:%02u\n\n", date.year, date.month, date.day, date.hour, date.minutes, date.seconds); - DN_Str8 buffer = DN_Str8TrimWhitespaceAround(DN_Str8BuilderBuild(&builder, &scratch.arena)); + DN_Str8 buffer = DN_Str8TrimWhitespaceAround(DN_Str8FromStr8BuilderArena(&builder, &scratch.arena)); DN_Str8 single_header_path = DN_OS_PathF(&scratch.arena, "%S/dn_single_header.%S", output_dir, suffix); DN_ErrSink *err = DN_TCErrSinkBeginDefault(); DN_OS_FileWriteAllSafe(single_header_path, buffer, err);