Pull in changes from internal project
This commit is contained in:
parent
765b8255f7
commit
6b403eae71
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$\ $$$$$$$$\
|
||||
@ -12,6 +13,7 @@
|
||||
// dqn_cpp_file.h -- Functions to emit C++ formatted code
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
#if !defined(DQN_CPP_FILE_H)
|
||||
#define DQN_CPP_FILE_H
|
||||
|
||||
|
@ -13,7 +13,7 @@ pushd Build
|
||||
REM O2 Optimisation Level 2
|
||||
REM Oi Use CPU Intrinsics
|
||||
REM Z7 Combine multi-debug files to one debug file
|
||||
set common_flags=-D DQN_WITH_UNIT_TESTS -D DQN_UNIT_TESTS_WITH_MAIN -D DQN_UNIT_TESTS_WITH_KECCAK -D DQN_USE_STD_PRINTF %script_dir%\dqn.cpp
|
||||
set common_flags=-D DQN_UNIT_TESTS_WITH_MAIN -D DQN_UNIT_TESTS_WITH_KECCAK -D DQN_USE_STD_PRINTF /Tp %script_dir%\dqn.cpp
|
||||
|
||||
set msvc_driver_flags=%common_flags% -MT -EHa -GR- -Od -Oi -Z7 -wd4201 -W4 -nologo
|
||||
|
||||
|
14
dqn.cpp
14
dqn.cpp
@ -21,7 +21,17 @@
|
||||
#if !defined(DQN_NO_METADESK)
|
||||
DQN_MSVC_WARNING_PUSH
|
||||
DQN_MSVC_WARNING_DISABLE(4505) // warning C4505: '<function>': unreferenced function with internal linkage has been removed
|
||||
|
||||
DQN_GCC_WARNING_PUSH
|
||||
DQN_GCC_WARNING_DISABLE(-Wwrite-strings)
|
||||
DQN_GCC_WARNING_DISABLE(-Wunused-but-set-variable)
|
||||
DQN_GCC_WARNING_DISABLE(-Wsign-compare)
|
||||
DQN_GCC_WARNING_DISABLE(-Wunused-function)
|
||||
DQN_GCC_WARNING_DISABLE(-Wunused-result)
|
||||
|
||||
#include "External/metadesk/md.c"
|
||||
|
||||
DQN_GCC_WARNING_POP
|
||||
DQN_MSVC_WARNING_POP
|
||||
#endif
|
||||
#define DQN_CPP_FILE_IMPLEMENTATION
|
||||
@ -34,13 +44,13 @@
|
||||
#endif
|
||||
|
||||
#include "dqn_base.cpp"
|
||||
#include "dqn_thread_context.cpp"
|
||||
#include "dqn_external.cpp"
|
||||
#include "dqn_allocator.cpp"
|
||||
#include "dqn_debug.cpp"
|
||||
#include "dqn_string.cpp"
|
||||
#include "dqn_containers.cpp"
|
||||
#include "dqn_type_info.cpp"
|
||||
#include "dqn_os.cpp"
|
||||
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN) || defined(DQN_PLATFORM_POSIX) || defined(DQN_PLATFORM_ARM64)
|
||||
#include "dqn_os_posix.cpp"
|
||||
@ -50,7 +60,7 @@
|
||||
#error Please define a platform e.g. 'DQN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
|
||||
#endif
|
||||
|
||||
#include "dqn_os.cpp"
|
||||
#include "dqn_tls.cpp"
|
||||
#include "dqn_math.cpp"
|
||||
#include "dqn_hash.cpp"
|
||||
#include "dqn_helpers.cpp"
|
||||
|
11
dqn.h
11
dqn.h
@ -126,7 +126,7 @@
|
||||
//
|
||||
// Allocations are stored in a global hash-table and their respective stack
|
||||
// traces for the allocation location. Memory leaks can be dumped at the end
|
||||
// of the program or some epoch by calling Dqn_Library_DumpLeaks()
|
||||
// of the program or some epoch by calling Dqn_Debug_DumpLeaks()
|
||||
//
|
||||
// #define DQN_LEAK_TRACKING
|
||||
//
|
||||
@ -300,17 +300,14 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#define DQN_UNDO_CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#define MD_DEFAULT_SPRINTF 0
|
||||
#define MD_IMPL_Vsnprintf DQN_VSNPRINTF
|
||||
#include "External/metadesk/md.h"
|
||||
#if defined(DQN_UNDO_CRT_SECURE_NO_WARNINGS)
|
||||
#undef _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Metadesk includes 'stb_sprintf.h' already
|
||||
#if !defined(DQN_STB_SPRINTF_HEADER_ONLY)
|
||||
#define DQN_STB_SPRINTF_HEADER_ONLY
|
||||
#endif
|
||||
|
||||
// Metadesk includes Windows.h
|
||||
#define DQN_NO_WIN32_MIN_HEADER
|
||||
#endif
|
||||
@ -321,7 +318,7 @@
|
||||
#include "dqn_win32.h"
|
||||
#endif
|
||||
#include "dqn_allocator.h"
|
||||
#include "dqn_thread_context.h"
|
||||
#include "dqn_tls.h"
|
||||
#include "dqn_debug.h"
|
||||
#include "dqn_string.h"
|
||||
#include "dqn_containers.h"
|
||||
|
@ -56,11 +56,28 @@ DQN_API Dqn_ArenaBlock *Dqn_Arena_BlockInitFlags(uint64_t reserve, uint64_t comm
|
||||
return result;
|
||||
}
|
||||
|
||||
static void Dqn_Arena_UpdateStatsOnNewBlock_(Dqn_Arena *arena, Dqn_ArenaBlock const *block)
|
||||
{
|
||||
DQN_ASSERT(arena);
|
||||
if (block) {
|
||||
arena->stats.info.used += block->used;
|
||||
arena->stats.info.commit += block->commit;
|
||||
arena->stats.info.reserve += block->reserve;
|
||||
arena->stats.info.blocks += 1;
|
||||
|
||||
arena->stats.hwm.used = DQN_MAX(arena->stats.hwm.used, arena->stats.info.used);
|
||||
arena->stats.hwm.commit = DQN_MAX(arena->stats.hwm.commit, arena->stats.info.commit);
|
||||
arena->stats.hwm.reserve = DQN_MAX(arena->stats.hwm.reserve, arena->stats.info.reserve);
|
||||
arena->stats.hwm.blocks = DQN_MAX(arena->stats.hwm.blocks, arena->stats.info.blocks);
|
||||
}
|
||||
}
|
||||
|
||||
DQN_API Dqn_Arena Dqn_Arena_InitSize(uint64_t reserve, uint64_t commit, uint8_t flags)
|
||||
{
|
||||
Dqn_Arena result = {};
|
||||
result.flags = flags;
|
||||
result.curr = Dqn_Arena_BlockInitFlags(reserve, commit, flags);
|
||||
Dqn_Arena_UpdateStatsOnNewBlock_(&result, result.curr);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -123,8 +140,10 @@ DQN_API void *Dqn_Arena_Alloc(Dqn_Arena *arena, uint64_t size, uint8_t align, Dq
|
||||
if (!arena)
|
||||
return nullptr;
|
||||
|
||||
if (!arena->curr)
|
||||
if (!arena->curr) {
|
||||
arena->curr = Dqn_Arena_BlockInitFlags(DQN_ARENA_RESERVE_SIZE, DQN_ARENA_COMMIT_SIZE, arena->flags);
|
||||
Dqn_Arena_UpdateStatsOnNewBlock_(arena, arena->curr);
|
||||
}
|
||||
|
||||
if (!arena->curr)
|
||||
return nullptr;
|
||||
@ -147,6 +166,7 @@ DQN_API void *Dqn_Arena_Alloc(Dqn_Arena *arena, uint64_t size, uint8_t align, Dq
|
||||
new_block->prev = arena->curr;
|
||||
arena->curr = new_block;
|
||||
new_block->reserve_sum = new_block->prev->reserve_sum + new_block->prev->reserve;
|
||||
Dqn_Arena_UpdateStatsOnNewBlock_(arena, arena->curr);
|
||||
goto try_alloc_again;
|
||||
}
|
||||
|
||||
@ -159,11 +179,16 @@ DQN_API void *Dqn_Arena_Alloc(Dqn_Arena *arena, uint64_t size, uint8_t align, Dq
|
||||
return nullptr;
|
||||
if (poison)
|
||||
Dqn_ASAN_PoisonMemoryRegion(commit_ptr, commit_size);
|
||||
curr->commit = end_commit;
|
||||
curr->commit = end_commit;
|
||||
arena->stats.info.commit += commit_size;
|
||||
arena->stats.hwm.commit = DQN_MAX(arena->stats.hwm.commit, arena->stats.info.commit);
|
||||
}
|
||||
|
||||
void *result = DQN_CAST(char *) curr + offset_pos;
|
||||
curr->used = end_pos;
|
||||
void *result = DQN_CAST(char *) curr + offset_pos;
|
||||
Dqn_usize alloc_size = end_pos - curr->used;
|
||||
curr->used += alloc_size;
|
||||
arena->stats.info.used += alloc_size;
|
||||
arena->stats.hwm.used = DQN_MAX(arena->stats.hwm.used, arena->stats.info.used);
|
||||
Dqn_ASAN_UnpoisonMemoryRegion(result, size);
|
||||
|
||||
if (zero_mem == Dqn_ZeroMem_Yes) {
|
||||
@ -171,6 +196,10 @@ DQN_API void *Dqn_Arena_Alloc(Dqn_Arena *arena, uint64_t size, uint8_t align, Dq
|
||||
DQN_MEMSET(result, 0, reused_bytes);
|
||||
}
|
||||
|
||||
DQN_ASSERT(arena->stats.hwm.used >= arena->stats.info.used);
|
||||
DQN_ASSERT(arena->stats.hwm.commit >= arena->stats.info.commit);
|
||||
DQN_ASSERT(arena->stats.hwm.reserve >= arena->stats.info.reserve);
|
||||
DQN_ASSERT(arena->stats.hwm.blocks >= arena->stats.info.blocks);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -195,21 +224,27 @@ DQN_API void *Dqn_Arena_Copy(Dqn_Arena *arena, void const *data, uint64_t size,
|
||||
|
||||
DQN_API void Dqn_Arena_PopTo(Dqn_Arena *arena, uint64_t init_used)
|
||||
{
|
||||
if (!arena)
|
||||
if (!arena || !arena->curr)
|
||||
return;
|
||||
uint64_t used = DQN_MAX(DQN_ARENA_HEADER_SIZE, init_used);
|
||||
Dqn_ArenaBlock *curr = arena->curr;
|
||||
while (curr->reserve_sum >= used) {
|
||||
Dqn_ArenaBlock *block_to_free = curr;
|
||||
arena->stats.info.used -= block_to_free->used;
|
||||
arena->stats.info.commit -= block_to_free->commit;
|
||||
arena->stats.info.reserve -= block_to_free->reserve;
|
||||
arena->stats.info.blocks -= 1;
|
||||
curr = curr->prev;
|
||||
Dqn_Arena_BlockDeinit_(arena, block_to_free);
|
||||
}
|
||||
|
||||
arena->curr = curr;
|
||||
curr->used = used - curr->reserve_sum;
|
||||
char *poison_ptr = (char *)curr + Dqn_AlignUpPowerOfTwo(curr->used, DQN_ASAN_POISON_ALIGNMENT);
|
||||
Dqn_usize poison_size = ((char *)curr + curr->commit) - poison_ptr;
|
||||
arena->stats.info.used -= curr->used;
|
||||
arena->curr = curr;
|
||||
curr->used = used - curr->reserve_sum;
|
||||
char *poison_ptr = (char *)curr + Dqn_AlignUpPowerOfTwo(curr->used, DQN_ASAN_POISON_ALIGNMENT);
|
||||
Dqn_usize poison_size = ((char *)curr + curr->commit) - poison_ptr;
|
||||
Dqn_ASAN_PoisonMemoryRegion(poison_ptr, poison_size);
|
||||
arena->stats.info.used += curr->used;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Arena_Pop(Dqn_Arena *arena, uint64_t amount)
|
||||
@ -237,7 +272,7 @@ DQN_API bool Dqn_Arena_OwnsPtr(Dqn_Arena const *arena, void *ptr)
|
||||
{
|
||||
bool result = false;
|
||||
uintptr_t uint_ptr = DQN_CAST(uintptr_t)ptr;
|
||||
for (Dqn_ArenaBlock const *block = arena ? arena->curr : nullptr; !result && block; ) {
|
||||
for (Dqn_ArenaBlock const *block = arena ? arena->curr : nullptr; !result && block; block = block->prev) {
|
||||
uintptr_t begin = DQN_CAST(uintptr_t) block + DQN_ARENA_HEADER_SIZE;
|
||||
uintptr_t end = begin + block->reserve;
|
||||
result = uint_ptr >= begin && uint_ptr <= end;
|
||||
@ -245,6 +280,42 @@ DQN_API bool Dqn_Arena_OwnsPtr(Dqn_Arena const *arena, void *ptr)
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DQN_API Dqn_ArenaStats Dqn_Arena_SumStatsArray(Dqn_ArenaStats const *array, Dqn_usize size)
|
||||
{
|
||||
Dqn_ArenaStats result = {};
|
||||
DQN_FOR_UINDEX(index, size) {
|
||||
Dqn_ArenaStats stats = array[index];
|
||||
result.info.used += stats.info.used;
|
||||
result.info.commit += stats.info.commit;
|
||||
result.info.reserve += stats.info.reserve;
|
||||
result.info.blocks += stats.info.blocks;
|
||||
|
||||
result.hwm.used = DQN_MAX(result.hwm.used, result.info.used);
|
||||
result.hwm.commit = DQN_MAX(result.hwm.commit, result.info.commit);
|
||||
result.hwm.reserve = DQN_MAX(result.hwm.reserve, result.info.reserve);
|
||||
result.hwm.blocks = DQN_MAX(result.hwm.blocks, result.info.blocks);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_ArenaStats Dqn_Arena_SumStats(Dqn_ArenaStats lhs, Dqn_ArenaStats rhs)
|
||||
{
|
||||
Dqn_ArenaStats array[] = {lhs, rhs};
|
||||
Dqn_ArenaStats result = Dqn_Arena_SumStatsArray(array, DQN_ARRAY_UCOUNT(array));
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_ArenaStats Dqn_Arena_SumArenaArrayToStats(Dqn_Arena const *array, Dqn_usize size)
|
||||
{
|
||||
Dqn_ArenaStats result = {};
|
||||
for (Dqn_usize index = 0; index < size; index++) {
|
||||
Dqn_Arena const *arena = array + index;
|
||||
result = Dqn_Arena_SumStats(result, arena->stats);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_ArenaTempMem Dqn_Arena_TempMemBegin(Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_ArenaTempMem result = {};
|
||||
@ -299,13 +370,8 @@ DQN_API void *Dqn_ChunkPool_Alloc(Dqn_ChunkPool *pool, Dqn_usize size)
|
||||
Dqn_usize const size_to_slot_offset = 5; // __lzcnt64(32) e.g. Dqn_ChunkPoolSlotSize_32B
|
||||
Dqn_usize slot_index = 0;
|
||||
if (required_size > 32) {
|
||||
#if defined(DQN_OS_WIN32)
|
||||
Dqn_usize dist_to_next_msb = __lzcnt64(required_size) + 1;
|
||||
#else
|
||||
Dqn_usize dist_to_next_msb = __builtin_clzll(required_size) + 1;
|
||||
#endif
|
||||
|
||||
// NOTE: Round up if not PoT as the low bits are set.
|
||||
Dqn_usize dist_to_next_msb = Dqn_CountLeadingZerosU64(required_size) + 1;
|
||||
dist_to_next_msb -= DQN_CAST(Dqn_usize)(!Dqn_IsPowerOfTwo(required_size));
|
||||
|
||||
Dqn_usize const register_size = sizeof(Dqn_usize) * 8;
|
||||
@ -401,14 +467,17 @@ DQN_API Dqn_Str8 Dqn_ChunkPool_AllocStr8Copy(Dqn_ChunkPool *pool, Dqn_Str8 strin
|
||||
|
||||
DQN_API void Dqn_ChunkPool_Dealloc(Dqn_ChunkPool *pool, void *ptr)
|
||||
{
|
||||
if (!Dqn_ChunkPool_IsValid(pool))
|
||||
if (!Dqn_ChunkPool_IsValid(pool) || !ptr)
|
||||
return;
|
||||
|
||||
Dqn_usize offset_to_original_ptr = 0;
|
||||
DQN_MEMCPY(&offset_to_original_ptr, &(DQN_CAST(char *)ptr)[-1], 1);
|
||||
DQN_ASSERT(Dqn_Arena_OwnsPtr(pool->arena, ptr));
|
||||
|
||||
char const *one_byte_behind_ptr = DQN_CAST(char *) ptr - 1;
|
||||
Dqn_usize offset_to_original_ptr = 0;
|
||||
DQN_MEMCPY(&offset_to_original_ptr, one_byte_behind_ptr, 1);
|
||||
DQN_ASSERT(offset_to_original_ptr <= sizeof(Dqn_ChunkPoolSlot) + pool->align);
|
||||
|
||||
char *original_ptr = DQN_CAST(char *)ptr - offset_to_original_ptr;
|
||||
char *original_ptr = DQN_CAST(char *)ptr - offset_to_original_ptr;
|
||||
Dqn_ChunkPoolSlot *slot = DQN_CAST(Dqn_ChunkPoolSlot *)original_ptr;
|
||||
Dqn_ChunkPoolSlotSize slot_index = DQN_CAST(Dqn_ChunkPoolSlotSize)(DQN_CAST(uintptr_t)slot->next);
|
||||
DQN_ASSERT(slot_index < Dqn_ChunkPoolSlotSize_Count);
|
||||
@ -417,6 +486,20 @@ DQN_API void Dqn_ChunkPool_Dealloc(Dqn_ChunkPool *pool, void *ptr)
|
||||
pool->slots[slot_index] = slot;
|
||||
}
|
||||
|
||||
DQN_API void *Dqn_ChunkPool_Copy(Dqn_ChunkPool *pool, void const *data, uint64_t size, uint8_t align)
|
||||
{
|
||||
if (!pool || !data || size == 0)
|
||||
return nullptr;
|
||||
|
||||
// TODO: Hmm should align be part of the alloc interface in general? I'm not going to worry
|
||||
// about this until we crash because of misalignment.
|
||||
DQN_ASSERT(pool->align >= align);
|
||||
|
||||
void *result = Dqn_ChunkPool_Alloc(pool, size);
|
||||
if (result)
|
||||
DQN_MEMCPY(result, data, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$ACAT] Dqn_ArenaCatalog //////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_ArenaCatalog_Init(Dqn_ArenaCatalog *catalog, Dqn_ChunkPool *pool)
|
||||
@ -440,7 +523,7 @@ DQN_API Dqn_ArenaCatalogItem *Dqn_ArenaCatalog_Find(Dqn_ArenaCatalog *catalog, D
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_ArenaCatalog_AddLabelRef(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_Str8 label)
|
||||
static void Dqn_ArenaCatalog_AddInternal_(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_Str8 label, bool arena_pool_allocated)
|
||||
{
|
||||
// NOTE: We could use an atomic for appending to the sentinel but it is such
|
||||
// a rare operation to append to the catalog that we don't bother.
|
||||
@ -451,6 +534,7 @@ DQN_API void Dqn_ArenaCatalog_AddLabelRef(Dqn_ArenaCatalog *catalog, Dqn_Arena *
|
||||
if (result) {
|
||||
result->arena = arena;
|
||||
result->label = label;
|
||||
result->arena_pool_allocated = arena_pool_allocated;
|
||||
|
||||
// NOTE: Add to the catalog (linked list)
|
||||
Dqn_ArenaCatalogItem *sentinel = &catalog->sentinel;
|
||||
@ -463,14 +547,6 @@ DQN_API void Dqn_ArenaCatalog_AddLabelRef(Dqn_ArenaCatalog *catalog, Dqn_Arena *
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_ArenaCatalog_AddLabelCopy(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_Str8 label)
|
||||
{
|
||||
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
Dqn_Str8 label_copy = Dqn_ChunkPool_AllocStr8Copy(catalog->pool, label);
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label_copy);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_ArenaCatalog_AddF(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
@ -479,7 +555,7 @@ DQN_API void Dqn_ArenaCatalog_AddF(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena,
|
||||
Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args);
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
va_end(args);
|
||||
Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label);
|
||||
Dqn_ArenaCatalog_AddInternal_(catalog, arena, label, false /*arena_pool_allocated*/);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_ArenaCatalog_AddFV(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
@ -487,35 +563,18 @@ DQN_API void Dqn_ArenaCatalog_AddFV(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena,
|
||||
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args);
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label);
|
||||
}
|
||||
|
||||
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocLabelRef(Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, Dqn_Str8 label)
|
||||
{
|
||||
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
Dqn_Arena *result = Dqn_ChunkPool_New(catalog->pool, Dqn_Arena);
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
|
||||
*result = Dqn_Arena_InitSize(reserve, commit, arena_flags);
|
||||
Dqn_ArenaCatalog_AddLabelRef(catalog, result, label);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocLabelCopy(Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, Dqn_Str8 label)
|
||||
{
|
||||
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
Dqn_Str8 label_copy = Dqn_ChunkPool_AllocStr8Copy(catalog->pool, label);
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
Dqn_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label_copy);
|
||||
return result;
|
||||
Dqn_ArenaCatalog_AddInternal_(catalog, arena, label, false /*arena_pool_allocated*/);
|
||||
}
|
||||
|
||||
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocFV(Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args);
|
||||
Dqn_Arena *result = Dqn_ChunkPool_New(catalog->pool, Dqn_Arena);
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
Dqn_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label);
|
||||
|
||||
*result = Dqn_Arena_InitSize(reserve, commit, arena_flags);
|
||||
Dqn_ArenaCatalog_AddInternal_(catalog, result, label, true /*arena_pool_allocated*/);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -525,8 +584,34 @@ DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocF(Dqn_ArenaCatalog *catalog, Dqn_usize
|
||||
va_start(args, fmt);
|
||||
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args);
|
||||
Dqn_Arena *result = Dqn_ChunkPool_New(catalog->pool, Dqn_Arena);
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
Dqn_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label);
|
||||
va_end(args);
|
||||
|
||||
*result = Dqn_Arena_InitSize(reserve, commit, arena_flags);
|
||||
Dqn_ArenaCatalog_AddInternal_(catalog, result, label, true /*arena_pool_allocated*/);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_ArenaCatalog_Erase(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_ArenaCatalogFreeArena free_arena)
|
||||
{
|
||||
bool result = false;
|
||||
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
for (Dqn_ArenaCatalogItem *item = catalog->sentinel.next; item != &catalog->sentinel; item = item->next) {
|
||||
if (item->arena == arena) {
|
||||
item->next->prev = item->prev;
|
||||
item->prev->next = item->next;
|
||||
if (item->arena_pool_allocated) {
|
||||
if (free_arena == Dqn_ArenaCatalogFreeArena_Yes)
|
||||
Dqn_Arena_Deinit(item->arena);
|
||||
Dqn_ChunkPool_Dealloc(catalog->pool, item->arena);
|
||||
}
|
||||
Dqn_ChunkPool_Dealloc(catalog->pool, item->label.data);
|
||||
Dqn_ChunkPool_Dealloc(catalog->pool, item);
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
return result;
|
||||
}
|
||||
|
@ -50,11 +50,26 @@ enum Dqn_ArenaFlag
|
||||
Dqn_ArenaFlag_AllocCanLeak = 1 << 3,
|
||||
};
|
||||
|
||||
struct Dqn_ArenaInfo
|
||||
{
|
||||
uint64_t used;
|
||||
uint64_t commit;
|
||||
uint64_t reserve;
|
||||
uint64_t blocks;
|
||||
};
|
||||
|
||||
struct Dqn_ArenaStats
|
||||
{
|
||||
Dqn_ArenaInfo info;
|
||||
Dqn_ArenaInfo hwm;
|
||||
};
|
||||
|
||||
struct Dqn_Arena
|
||||
{
|
||||
Dqn_ArenaBlock *curr;
|
||||
uint8_t flags;
|
||||
Dqn_ArenaStats stats;
|
||||
Dqn_TicketMutex mutex; // For user code to lock the arena, the arena itself does not use.
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
struct Dqn_ArenaTempMem
|
||||
@ -131,6 +146,7 @@ struct Dqn_ArenaCatalogItem
|
||||
{
|
||||
Dqn_Arena *arena;
|
||||
Dqn_Str8 label;
|
||||
bool arena_pool_allocated;
|
||||
Dqn_ArenaCatalogItem *next;
|
||||
Dqn_ArenaCatalogItem *prev;
|
||||
};
|
||||
@ -143,6 +159,12 @@ struct Dqn_ArenaCatalog
|
||||
uint16_t arena_count;
|
||||
};
|
||||
|
||||
enum Dqn_ArenaCatalogFreeArena
|
||||
{
|
||||
Dqn_ArenaCatalogFreeArena_No,
|
||||
Dqn_ArenaCatalogFreeArena_Yes,
|
||||
};
|
||||
|
||||
// NOTE: [$AREN] Dqn_Arena /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_Arena Dqn_Arena_InitSize (uint64_t reserve, uint64_t commit, uint8_t flags);
|
||||
DQN_API void Dqn_Arena_Deinit (Dqn_Arena *arena);
|
||||
@ -156,6 +178,9 @@ DQN_API void Dqn_Arena_Pop (Dqn_Arena *arena,
|
||||
DQN_API uint64_t Dqn_Arena_Pos (Dqn_Arena const *arena);
|
||||
DQN_API void Dqn_Arena_Clear (Dqn_Arena *arena);
|
||||
DQN_API bool Dqn_Arena_OwnsPtr (Dqn_Arena const *arena, void *ptr);
|
||||
DQN_API Dqn_ArenaStats Dqn_Arena_SumStatsArray (Dqn_ArenaStats const *array, Dqn_usize size);
|
||||
DQN_API Dqn_ArenaStats Dqn_Arena_SumStats (Dqn_ArenaStats lhs, Dqn_ArenaStats rhs);
|
||||
DQN_API Dqn_ArenaStats Dqn_Arena_SumArenaArrayToStats (Dqn_Arena const *array, Dqn_usize size);
|
||||
DQN_API Dqn_ArenaTempMem Dqn_Arena_TempMemBegin (Dqn_Arena *arena);
|
||||
DQN_API void Dqn_Arena_TempMemEnd (Dqn_ArenaTempMem mem);
|
||||
#define Dqn_Arena_New(arena, T, zero_mem) (T *)Dqn_Arena_Alloc(arena, sizeof(T), alignof(T), zero_mem)
|
||||
@ -164,7 +189,6 @@ DQN_API void Dqn_Arena_TempMemEnd (Dqn_ArenaTempMem m
|
||||
#define Dqn_Arena_NewArrayCopy(arena, T, src, count) (T *)Dqn_Arena_Copy (arena, (src), sizeof(T) * (count), alignof(T))
|
||||
|
||||
// NOTE: [$CHUN] Dqn_ChunkPool /////////////////////////////////////////////////////////////////////
|
||||
#define Dqn_ChunkPool_New(pool, T) (T *)Dqn_ChunkPool_Alloc(pool, sizeof(T))
|
||||
DQN_API Dqn_ChunkPool Dqn_ChunkPool_Init (Dqn_Arena *arena, uint8_t align);
|
||||
DQN_API bool Dqn_ChunkPool_IsValid (Dqn_ChunkPool const *pool);
|
||||
DQN_API void * Dqn_ChunkPool_Alloc (Dqn_ChunkPool *pool, Dqn_usize size);
|
||||
@ -172,15 +196,18 @@ DQN_API Dqn_Str8 Dqn_ChunkPool_AllocStr8FV (Dqn_ChunkPool *poo
|
||||
DQN_API Dqn_Str8 Dqn_ChunkPool_AllocStr8F (Dqn_ChunkPool *pool, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API Dqn_Str8 Dqn_ChunkPool_AllocStr8Copy (Dqn_ChunkPool *pool, Dqn_Str8 string);
|
||||
DQN_API void Dqn_ChunkPool_Dealloc (Dqn_ChunkPool *pool, void *ptr);
|
||||
DQN_API void * Dqn_ChunkPool_Copy (Dqn_ChunkPool *pool, void const *data, uint64_t size, uint8_t align);
|
||||
|
||||
#define Dqn_ChunkPool_New(pool, T) (T *)Dqn_ChunkPool_Alloc(pool, sizeof(T))
|
||||
#define Dqn_ChunkPool_NewArray(pool, T, count) (T *)Dqn_ChunkPool_Alloc(pool, count * sizeof(T))
|
||||
#define Dqn_ChunkPool_NewCopy(arena, T, src) (T *)Dqn_ChunkPool_Copy (arena, (src), sizeof(T), alignof(T))
|
||||
#define Dqn_ChunkPool_NewArrayCopy(arena, T, src, count) (T *)Dqn_ChunkPool_Copy (arena, (src), sizeof(T) * (count), alignof(T))
|
||||
|
||||
// NOTE: [$ACAT] Dqn_ArenaCatalog //////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_ArenaCatalog_Init (Dqn_ArenaCatalog *catalog, Dqn_ChunkPool *pool);
|
||||
DQN_API Dqn_ArenaCatalogItem *Dqn_ArenaCatalog_Find (Dqn_ArenaCatalog *catalog, Dqn_Str8 label);
|
||||
DQN_API void Dqn_ArenaCatalog_AddLabelRef (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_Str8 label);
|
||||
DQN_API void Dqn_ArenaCatalog_AddLabelCopy (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_Str8 label);
|
||||
DQN_API void Dqn_ArenaCatalog_AddF (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API void Dqn_ArenaCatalog_AddFV (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API Dqn_Arena * Dqn_ArenaCatalog_AllocLabelRef (Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, Dqn_Str8 label);
|
||||
DQN_API Dqn_Arena * Dqn_ArenaCatalog_AllocLabelCopy(Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, Dqn_Str8 label);
|
||||
DQN_API Dqn_Arena * Dqn_ArenaCatalog_AllocFV (Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API Dqn_Arena * Dqn_ArenaCatalog_AllocF (Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API void Dqn_ArenaCatalog_Init (Dqn_ArenaCatalog *catalog, Dqn_ChunkPool *pool);
|
||||
DQN_API Dqn_ArenaCatalogItem *Dqn_ArenaCatalog_Find (Dqn_ArenaCatalog *catalog, Dqn_Str8 label);
|
||||
DQN_API void Dqn_ArenaCatalog_AddF (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API void Dqn_ArenaCatalog_AddFV (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API Dqn_Arena * Dqn_ArenaCatalog_AllocFV (Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API Dqn_Arena * Dqn_ArenaCatalog_AllocF (Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API bool Dqn_ArenaCatalog_Erase (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_ArenaCatalogFreeArena free_arena);
|
||||
|
49
dqn_base.cpp
49
dqn_base.cpp
@ -77,8 +77,8 @@ DQN_API void Dqn_CPU_SetFeature(Dqn_CPUReport *report, Dqn_CPUFeature feature)
|
||||
DQN_API Dqn_CPUReport Dqn_CPU_Report()
|
||||
{
|
||||
Dqn_CPUReport result = {};
|
||||
Dqn_CPUIDResult fn_0000_[16] = {};
|
||||
Dqn_CPUIDResult fn_8000_[64] = {};
|
||||
Dqn_CPUIDResult fn_0000_[512] = {};
|
||||
Dqn_CPUIDResult fn_8000_[512] = {};
|
||||
int const EXTENDED_FUNC_BASE_EAX = 0x8000'0000;
|
||||
int const REGISTER_SIZE = sizeof(fn_0000_[0].reg.eax);
|
||||
|
||||
@ -102,8 +102,10 @@ DQN_API Dqn_CPUReport Dqn_CPU_Report()
|
||||
|
||||
// NOTE: Enumerate all CPUID results for the known function counts /////////////////////////////
|
||||
{
|
||||
DQN_ASSERT((STANDARD_FUNC_MAX_EAX + 1) <= DQN_ARRAY_ICOUNT(fn_0000_));
|
||||
DQN_ASSERT((DQN_CAST(Dqn_isize)EXTENDED_FUNC_MAX_EAX - EXTENDED_FUNC_BASE_EAX + 1) <= DQN_ARRAY_ICOUNT(fn_8000_));
|
||||
DQN_ASSERTF((STANDARD_FUNC_MAX_EAX + 1) <= DQN_ARRAY_ICOUNT(fn_0000_),
|
||||
"Max standard count is %zu", STANDARD_FUNC_MAX_EAX + 1);
|
||||
DQN_ASSERTF((DQN_CAST(Dqn_isize)EXTENDED_FUNC_MAX_EAX - EXTENDED_FUNC_BASE_EAX + 1) <= DQN_ARRAY_ICOUNT(fn_8000_),
|
||||
"Max extended count is %zu", DQN_CAST(Dqn_isize)EXTENDED_FUNC_MAX_EAX - EXTENDED_FUNC_BASE_EAX + 1);
|
||||
|
||||
for (int eax = 1; eax <= STANDARD_FUNC_MAX_EAX; eax++) {
|
||||
Dqn_CPUIDArgs args = {};
|
||||
@ -535,15 +537,15 @@ DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_Str8 type, int log_type, void *user_d
|
||||
// NOTE: Open log file for appending if requested //////////////////////////
|
||||
Dqn_TicketMutex_Begin(&lib->log_file_mutex);
|
||||
if (lib->log_to_file && !lib->log_file.handle && !lib->log_file.error) {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 log_path = Dqn_OS_PathConvertF(scratch.arena, "%.*s/dqn.log", DQN_STR_FMT(lib->exe_dir));
|
||||
lib->log_file = Dqn_OS_FileOpen(log_path, Dqn_OSFileOpen_CreateAlways, Dqn_OSFileAccess_AppendOnly, nullptr);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 log_path = Dqn_OS_PathF(t_mem.arena, "%.*s/dqn.log", DQN_STR_FMT(lib->exe_dir));
|
||||
lib->log_file = Dqn_OS_FileOpen(log_path, Dqn_OSFileOpen_CreateAlways, Dqn_OSFileAccess_AppendOnly, nullptr);
|
||||
}
|
||||
Dqn_TicketMutex_End(&lib->log_file_mutex);
|
||||
|
||||
// NOTE: Generate the log header ///////////////////////////////////////////
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 log_line = Dqn_Log_MakeStr8(scratch.arena, !lib->log_no_colour, type, log_type, call_site, fmt, args);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 log_line = Dqn_Log_MakeStr8(t_mem.arena, !lib->log_no_colour, type, log_type, call_site, fmt, args);
|
||||
|
||||
// NOTE: Print log /////////////////////////////////////////////////////////
|
||||
Dqn_Print_StdLn(Dqn_PrintStd_Out, log_line);
|
||||
@ -597,13 +599,14 @@ DQN_API void Dqn_Log_TypeFCallSite(Dqn_LogType type, Dqn_CallSite call_site, DQN
|
||||
// NOTE: [$ERRS] Dqn_ErrorSink /////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_ErrorSink *Dqn_ErrorSink_Begin(Dqn_ErrorSinkMode mode)
|
||||
{
|
||||
Dqn_ThreadContext *thread_context = Dqn_ThreadContext_Get();
|
||||
Dqn_ErrorSink *result = &thread_context->error_sink;
|
||||
Dqn_ErrorSinkNode *node = Dqn_Arena_New(result->arena, Dqn_ErrorSinkNode, Dqn_ZeroMem_Yes);
|
||||
node->next = result->stack;
|
||||
node->arena_pos = Dqn_Arena_Pos(result->arena);
|
||||
node->mode = mode;
|
||||
result->stack = node;
|
||||
Dqn_TLS *tls = Dqn_TLS_Get();
|
||||
Dqn_ErrorSink *result = &tls->error_sink;
|
||||
Dqn_usize arena_pos = Dqn_Arena_Pos(result->arena);
|
||||
Dqn_ErrorSinkNode *node = Dqn_Arena_New(result->arena, Dqn_ErrorSinkNode, Dqn_ZeroMem_Yes);
|
||||
node->next = result->stack;
|
||||
node->arena_pos = arena_pos;
|
||||
node->mode = mode;
|
||||
result->stack = node;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -640,8 +643,8 @@ DQN_API void Dqn_ErrorSink_EndAndIgnore(Dqn_ErrorSink *error)
|
||||
|
||||
DQN_API bool Dqn_ErrorSink_EndAndLogError(Dqn_ErrorSink *error, Dqn_Str8 error_msg)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_ErrorSinkNode node = Dqn_ErrorSink_End(scratch.arena, error);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_ErrorSinkNode node = Dqn_ErrorSink_End(t_mem.arena, error);
|
||||
if (node.error) {
|
||||
if (Dqn_Str8_HasData(error_msg)) {
|
||||
Dqn_Log_TypeFCallSite(Dqn_LogType_Error, node.call_site, "%.*s: %.*s", DQN_STR_FMT(error_msg), DQN_STR_FMT(node.msg));
|
||||
@ -655,8 +658,8 @@ DQN_API bool Dqn_ErrorSink_EndAndLogError(Dqn_ErrorSink *error, Dqn_Str8 error_m
|
||||
|
||||
DQN_API bool Dqn_ErrorSink_EndAndLogErrorFV(Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 log = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 log = Dqn_Str8_InitFV(t_mem.arena, fmt, args);
|
||||
bool result = Dqn_ErrorSink_EndAndLogError(error, log);
|
||||
return result;
|
||||
}
|
||||
@ -665,8 +668,8 @@ DQN_API bool Dqn_ErrorSink_EndAndLogErrorF(Dqn_ErrorSink *error, DQN_FMT_ATTRIB
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 log = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 log = Dqn_Str8_InitFV(t_mem.arena, fmt, args);
|
||||
bool result = Dqn_ErrorSink_EndAndLogError(error, log);
|
||||
va_end(args);
|
||||
return result;
|
||||
@ -699,7 +702,7 @@ DQN_API void Dqn_ErrorSink_MakeFV_(Dqn_ErrorSink *error, uint32_t error_code, DQ
|
||||
node->msg = Dqn_Str8_InitFV(error->arena, fmt, args);
|
||||
node->error_code = error_code;
|
||||
node->error = true;
|
||||
node->call_site = Dqn_ThreadContext_Get()->call_site;
|
||||
node->call_site = Dqn_TLS_Get()->call_site;
|
||||
if (node->mode == Dqn_ErrorSinkMode_ExitOnError)
|
||||
Dqn_ErrorSink_EndAndExitIfErrorF(error, error_code, "Fatal error %u", error_code);
|
||||
}
|
||||
|
17
dqn_base.h
17
dqn_base.h
@ -145,16 +145,16 @@
|
||||
#if !defined(DQN_MEMCPY) || !defined(DQN_MEMSET) || !defined(DQN_MEMCMP) || !defined(DQN_MEMMOVE)
|
||||
#include <string.h>
|
||||
#if !defined(DQN_MEMCPY)
|
||||
#define DQN_MEMCPY(dest, src, count) memcpy(dest, src, count)
|
||||
#define DQN_MEMCPY(dest, src, count) memcpy((dest), (src), (count))
|
||||
#endif
|
||||
#if !defined(DQN_MEMSET)
|
||||
#define DQN_MEMSET(dest, value, count) memset(dest, value, count)
|
||||
#define DQN_MEMSET(dest, value, count) memset((dest), (value), (count))
|
||||
#endif
|
||||
#if !defined(DQN_MEMCMP)
|
||||
#define DQN_MEMCMP(lhs, rhs, count) memcmp(lhs, rhs, count)
|
||||
#define DQN_MEMCMP(lhs, rhs, count) memcmp((lhs), (rhs), (count))
|
||||
#endif
|
||||
#if !defined(DQN_MEMMOVE)
|
||||
#define DQN_MEMMOVE(dest, src, count) memmove(dest, src, count)
|
||||
#define DQN_MEMMOVE(dest, src, count) memmove((dest), (src), (count))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -424,6 +424,9 @@ struct Dqn_ErrorSink
|
||||
#define Dqn_Atomic_AddU64(target, value) _InterlockedExchangeAdd64((__int64 volatile *)target, value)
|
||||
#define Dqn_Atomic_SubU32(target, value) Dqn_Atomic_AddU32(DQN_CAST(long volatile *)target, (long)-value)
|
||||
#define Dqn_Atomic_SubU64(target, value) Dqn_Atomic_AddU64(target, (uint64_t)-value)
|
||||
|
||||
#define Dqn_CountLeadingZerosU64(value) __lzcnt64(value)
|
||||
|
||||
#define Dqn_CPU_TSC() __rdtsc()
|
||||
#define Dqn_CompilerReadBarrierAndCPUReadFence _ReadBarrier(); _mm_lfence()
|
||||
#define Dqn_CompilerWriteBarrierAndCPUWriteFence _WriteBarrier(); _mm_sfence()
|
||||
@ -439,6 +442,8 @@ struct Dqn_ErrorSink
|
||||
#define Dqn_Atomic_AddU64(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL)
|
||||
#define Dqn_Atomic_SubU32(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL)
|
||||
#define Dqn_Atomic_SubU64(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL)
|
||||
|
||||
#define Dqn_CountLeadingZerosU64(value) __builtin_clzll(value)
|
||||
#if defined(DQN_COMPILER_GCC)
|
||||
#define Dqn_CPU_TSC() __rdtsc()
|
||||
#else
|
||||
@ -752,8 +757,8 @@ DQN_API bool Dqn_ErrorSink_EndAndLogErrorF
|
||||
DQN_API void Dqn_ErrorSink_EndAndExitIfErrorF (Dqn_ErrorSink *error, uint32_t exit_code, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API void Dqn_ErrorSink_EndAndExitIfErrorFV (Dqn_ErrorSink *error, uint32_t exit_code, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
|
||||
#define Dqn_ErrorSink_MakeFV(error, error_code, fmt, args) do { Dqn_ThreadContext_SaveCallSite; Dqn_ErrorSink_MakeFV_(error, error_code, fmt, args); } while (0)
|
||||
#define Dqn_ErrorSink_MakeF(error, error_code, fmt, ...) do { Dqn_ThreadContext_SaveCallSite; Dqn_ErrorSink_MakeF_(error, error_code, fmt, ## __VA_ARGS__); } while (0)
|
||||
#define Dqn_ErrorSink_MakeFV(error, error_code, fmt, args) do { Dqn_TLS_SaveCallSite; Dqn_ErrorSink_MakeFV_(error, error_code, fmt, args); } while (0)
|
||||
#define Dqn_ErrorSink_MakeF(error, error_code, fmt, ...) do { Dqn_TLS_SaveCallSite; Dqn_ErrorSink_MakeF_(error, error_code, fmt, ## __VA_ARGS__); } while (0)
|
||||
DQN_API void Dqn_ErrorSink_MakeFV_ (Dqn_ErrorSink *error, uint32_t error_code, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API void Dqn_ErrorSink_MakeF_ (Dqn_ErrorSink *error, uint32_t error_code, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
|
||||
|
517
dqn_cgen.cpp
517
dqn_cgen.cpp
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$$$$$$$\ $$\ $$\
|
||||
@ -12,6 +13,7 @@
|
||||
// dqn_cgen.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
Dqn_CGenMapNodeToEnum const DQN_CGEN_TABLE_KEY_LIST[] =
|
||||
{
|
||||
@ -48,6 +50,7 @@ Dqn_CGenTableHeaderType const DQN_CGEN_TABLE_CODE_GEN_STRUCT_HEADER_LIST[] =
|
||||
Dqn_CGenTableHeaderType_CppOpEquals,
|
||||
Dqn_CGenTableHeaderType_CppArraySize,
|
||||
Dqn_CGenTableHeaderType_CppArraySizeField,
|
||||
Dqn_CGenTableHeaderType_CppLabel,
|
||||
Dqn_CGenTableHeaderType_GenTypeInfo,
|
||||
};
|
||||
|
||||
@ -57,6 +60,7 @@ Dqn_CGenTableHeaderType const DQN_CGEN_TABLE_CODE_GEN_ENUM_HEADER_LIST[] =
|
||||
Dqn_CGenTableHeaderType_Table,
|
||||
Dqn_CGenTableHeaderType_CppName,
|
||||
Dqn_CGenTableHeaderType_CppValue,
|
||||
Dqn_CGenTableHeaderType_CppLabel,
|
||||
Dqn_CGenTableHeaderType_GenTypeInfo,
|
||||
Dqn_CGenTableHeaderType_GenEnumCount,
|
||||
};
|
||||
@ -159,6 +163,7 @@ static bool Dqn_CGen_GatherTables_(Dqn_CGen *cgen, Dqn_ErrorSink *error)
|
||||
// NOTE: Validate table headers ////////////////////////////////////////////////////////////
|
||||
switch (table->type) {
|
||||
case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH;
|
||||
case Dqn_CGenTableType_Count: DQN_INVALID_CODE_PATH;
|
||||
|
||||
case Dqn_CGenTableType_Data: {
|
||||
} break;
|
||||
@ -299,7 +304,6 @@ DQN_API Dqn_CGen Dqn_CGen_InitFilesArgV(int argc, char const **argv, Dqn_ErrorSi
|
||||
MD_String8 file_name = MD_S8CString(DQN_CAST(char *)argv[arg_index]);
|
||||
MD_ParseResult parse_result = MD_ParseWholeFile(result.arena, file_name);
|
||||
for (MD_Message *message = parse_result.errors.first; message != 0; message = message->next) {
|
||||
MD_CodeLoc code_loc = MD_CodeLocFromNode(message->node);
|
||||
has_error = true;
|
||||
Dqn_CGen_LogF(message->kind, message->node, error, "%.*s", MD_S8VArg(message->string));
|
||||
}
|
||||
@ -324,6 +328,7 @@ DQN_API Dqn_Str8 Dqn_CGen_TableHeaderTypeToDeclStr8(Dqn_CGenTableHeaderType type
|
||||
case Dqn_CGenTableHeaderType_CppOpEquals: result = DQN_STR8("cpp_op_equals"); break;
|
||||
case Dqn_CGenTableHeaderType_CppArraySize: result = DQN_STR8("cpp_array_size"); break;
|
||||
case Dqn_CGenTableHeaderType_CppArraySizeField: result = DQN_STR8("cpp_array_size_field"); break;
|
||||
case Dqn_CGenTableHeaderType_CppLabel: result = DQN_STR8("cpp_label"); break;
|
||||
case Dqn_CGenTableHeaderType_GenTypeInfo: result = DQN_STR8("gen_type_info"); break;
|
||||
case Dqn_CGenTableHeaderType_GenEnumCount: result = DQN_STR8("gen_enum_count"); break;
|
||||
case Dqn_CGenTableHeaderType_Count: result = DQN_STR8("XX BAD ENUM VALUE XX"); break;
|
||||
@ -345,56 +350,63 @@ DQN_API Dqn_CGenMapNodeToEnum Dqn_CGen_MapNodeToEnumOrExit(MD_Node const *node,
|
||||
|
||||
if (result.enum_val == 0) {
|
||||
MD_CodeLoc loc = MD_CodeLocFromNode(DQN_CAST(MD_Node *)node);
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Str8 user_msg = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
||||
Dqn_Str8 user_msg = Dqn_Str8_InitFV(tmem.arena, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = scratch.arena;
|
||||
builder.arena = tmem.arena;
|
||||
|
||||
Dqn_Str8Builder_AppendF(&builder, "%.*s: '%.*s' is not recognised, the supported values are ", DQN_STR_FMT(user_msg), MD_S8VArg(node->string));
|
||||
Dqn_Str8Builder_AddF(&builder, "%.*s: '%.*s' is not recognised, the supported values are ", DQN_STR_FMT(user_msg), MD_S8VArg(node->string));
|
||||
for (Dqn_usize index = 0; index < valid_keys_size; index++) {
|
||||
Dqn_CGenMapNodeToEnum const *validator = valid_keys + index;
|
||||
Dqn_Str8Builder_AppendF(&builder, DQN_CAST(char *)"%s'%.*s'", index ? ", " : "", DQN_STR_FMT(validator->node_string));
|
||||
Dqn_Str8Builder_AddF(&builder, DQN_CAST(char *)"%s'%.*s'", index ? ", " : "", DQN_STR_FMT(validator->node_string));
|
||||
}
|
||||
|
||||
Dqn_Str8 error_msg = Dqn_Str8Builder_Build(&builder, scratch.arena);
|
||||
Dqn_Str8 error_msg = Dqn_Str8Builder_Build(&builder, tmem.arena);
|
||||
MD_PrintMessageFmt(stderr, loc, MD_MessageKind_Error, DQN_CAST(char *) "%.*s", DQN_STR_FMT(error_msg));
|
||||
Dqn_OS_Exit(DQN_CAST(uint32_t)-1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_usize Dqn_CGen_NodeChildrenCount(MD_Node const *node)
|
||||
{
|
||||
Dqn_usize result = 0;
|
||||
for (MD_EachNode(item, node->first_child))
|
||||
result++;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_CGen_LogF(MD_MessageKind kind, MD_Node *node, Dqn_ErrorSink *error, char const *fmt, ...)
|
||||
{
|
||||
if (!error)
|
||||
return;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = scratch.arena;
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_PushTMem(nullptr);
|
||||
Dqn_Str8Builder builder = Dqn_Str8Builder_Init_TLS();
|
||||
|
||||
MD_String8 kind_string = MD_StringFromMessageKind(kind);
|
||||
MD_CodeLoc loc = MD_CodeLocFromNode(node);
|
||||
Dqn_Str8Builder_AppendF(&builder, "" MD_FmtCodeLoc " %.*s: ", MD_CodeLocVArg(loc), MD_S8VArg(kind_string));
|
||||
Dqn_Str8Builder_AddF(&builder, "" MD_FmtCodeLoc " %.*s: ", MD_CodeLocVArg(loc), MD_S8VArg(kind_string));
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Str8Builder_AppendFV(&builder, fmt, args);
|
||||
Dqn_Str8Builder_AddFV(&builder, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
Dqn_Str8 msg = Dqn_Str8Builder_Build(&builder, scratch.arena);
|
||||
Dqn_Str8 msg = Dqn_Str8Builder_Build(&builder, tmem.arena);
|
||||
Dqn_ErrorSink_MakeF(error, DQN_CAST(uint32_t)-1, "%.*s", DQN_STR_FMT(msg));
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_CGen_TableHasHeaders(Dqn_CGenTable const *table, Dqn_Str8 const *headers, Dqn_usize header_count, Dqn_ErrorSink *error)
|
||||
{
|
||||
bool result = true;
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = scratch.arena;
|
||||
builder.arena = tmem.arena;
|
||||
|
||||
for (Dqn_usize index = 0; index < header_count; index++) {
|
||||
Dqn_Str8 header = headers[index];
|
||||
@ -402,12 +414,12 @@ DQN_API bool Dqn_CGen_TableHasHeaders(Dqn_CGenTable const *table, Dqn_Str8 const
|
||||
MD_MapSlot *slot = MD_MapLookup(DQN_CAST(MD_Map *)&table->headers_map, MD_MapKeyStr(header_md));
|
||||
if (!slot) {
|
||||
result = false;
|
||||
Dqn_Str8Builder_AppendF(&builder, "%s%.*s", builder.count ? ", " : "", DQN_STR_FMT(header));
|
||||
Dqn_Str8Builder_AddF(&builder, "%s%.*s", builder.count ? ", " : "", DQN_STR_FMT(header));
|
||||
}
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
Dqn_Str8 missing_headers = Dqn_Str8Builder_Build(&builder, scratch.arena);
|
||||
Dqn_Str8 missing_headers = Dqn_Str8Builder_Build(&builder, tmem.arena);
|
||||
Dqn_CGen_LogF(MD_MessageKind_Error,
|
||||
table->headers_node,
|
||||
error,
|
||||
@ -482,6 +494,26 @@ DQN_API bool Dqn_CGen_LookupNextTableInCodeGenTable(Dqn_CGen *cgen, Dqn_CGenTabl
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_CGen_WillCodeGenTypeName(Dqn_CGen const *cgen, Dqn_Str8 name)
|
||||
{
|
||||
if (!Dqn_Str8_HasData(name))
|
||||
return false;
|
||||
|
||||
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
|
||||
if (table->type != Dqn_CGenTableType_CodeGenStruct && table->type != Dqn_CGenTableType_CodeGenEnum)
|
||||
continue;
|
||||
|
||||
for (Dqn_usize row_index = 0; row_index < table->row_count; row_index++) {
|
||||
Dqn_CGenTableRow const *row = table->rows + row_index;
|
||||
Dqn_CGenTableColumn const *column = row->columns + table->column_indexes[Dqn_CGenTableHeaderType_Name];
|
||||
if (column->string == name)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void Dqn_CGen_EmitRowWhitespace_(Dqn_CGenTableRow const *row, Dqn_CppFile *cpp)
|
||||
{
|
||||
for (Dqn_CGenTableRowTag *tag = row->first_tag; tag; tag = tag->next) {
|
||||
@ -492,8 +524,8 @@ static void Dqn_CGen_EmitRowWhitespace_(Dqn_CGenTableRow const *row, Dqn_CppFile
|
||||
if (tag->comment.size <= 0)
|
||||
break;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 prefix = Dqn_Str8_InitF(scratch.arena, "// NOTE: %.*s ", MD_S8VArg(tag->comment));
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 prefix = Dqn_Str8_InitF(tmem.arena, "// NOTE: %.*s ", MD_S8VArg(tag->comment));
|
||||
int line_padding = DQN_MAX(100 - (DQN_CAST(int) prefix.size + (Dqn_CppSpacePerIndent(cpp) * cpp->indent)), 0);
|
||||
Dqn_CppPrint(cpp, "%.*s", DQN_STR_FMT(prefix));
|
||||
for (int index = 0; index < line_padding; index++)
|
||||
@ -508,62 +540,105 @@ static void Dqn_CGen_EmitRowWhitespace_(Dqn_CGenTableRow const *row, Dqn_CppFile
|
||||
}
|
||||
}
|
||||
|
||||
Dqn_Str8 Dqn_CGen_StripQualifiersOnCppType_(Dqn_Arena *arena, Dqn_Str8 type)
|
||||
{
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
|
||||
Dqn_Str8 result = Dqn_Str8_TrimWhitespaceAround(type);
|
||||
result = Dqn_Str8_Replace(result, /*find*/ DQN_STR8("*"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, tmem.arena, Dqn_Str8EqCase_Sensitive);
|
||||
result = Dqn_Str8_Replace(result, /*find*/ DQN_STR8("constexpr"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, tmem.arena, Dqn_Str8EqCase_Sensitive);
|
||||
result = Dqn_Str8_Replace(result, /*find*/ DQN_STR8("const"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, tmem.arena, Dqn_Str8EqCase_Sensitive);
|
||||
result = Dqn_Str8_Replace(result, /*find*/ DQN_STR8("static"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, tmem.arena, Dqn_Str8EqCase_Sensitive);
|
||||
result = Dqn_Str8_Replace(result, /*find*/ DQN_STR8(" "), /*replace*/ DQN_STR8(""), /*start_index*/ 0, arena, Dqn_Str8EqCase_Sensitive);
|
||||
result = Dqn_Str8_TrimWhitespaceAround(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_CppFile *cpp, Dqn_Str8 emit_prefix)
|
||||
{
|
||||
DQN_ASSERT(Dqn_Str8_HasData(emit_prefix));
|
||||
|
||||
if (emit & Dqn_CGenEmit_Prototypes) {
|
||||
// NOTE: Generate type info enums //////////////////////////////////////////////////////////
|
||||
Dqn_CppEnumBlock(cpp, "%.*s_Type", DQN_STR_FMT(emit_prefix)) {
|
||||
Dqn_CppLine(cpp, "%.*s_Type_Nil,", DQN_STR_FMT(emit_prefix));
|
||||
Dqn_CppEnumBlock(cpp, "%.*sType", DQN_STR_FMT(emit_prefix)) {
|
||||
Dqn_CppLine(cpp, "%.*sType_Nil,", DQN_STR_FMT(emit_prefix));
|
||||
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
|
||||
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);)
|
||||
Dqn_CppLine(cpp, "%.*s_Type_%.*s,", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string));
|
||||
Dqn_CppLine(cpp, "%.*sType_%.*s,", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string));
|
||||
}
|
||||
Dqn_CppLine(cpp, "%.*s_Type_Count,", DQN_STR_FMT(emit_prefix));
|
||||
Dqn_CppLine(cpp, "%.*sType_Count,", DQN_STR_FMT(emit_prefix));
|
||||
}
|
||||
Dqn_CppNewLine(cpp);
|
||||
|
||||
// NOTE: Generate structs + enums //////////////////////////////////////////////////////////////
|
||||
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
|
||||
switch (table->type) {
|
||||
case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH;
|
||||
case Dqn_CGenTableType_Data: continue;
|
||||
case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH;
|
||||
case Dqn_CGenTableType_Count: DQN_INVALID_CODE_PATH;
|
||||
case Dqn_CGenTableType_CodeGenBuiltinTypes: continue;
|
||||
case Dqn_CGenTableType_Data: continue;
|
||||
|
||||
case Dqn_CGenTableType_CodeGenStruct: {
|
||||
|
||||
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it); ) {
|
||||
// TODO(doyle): Verify the codegen table has the headers from the table it references
|
||||
Dqn_CppStructBlock(cpp, "%.*s", DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string)) {
|
||||
int longest_type_name = 0;
|
||||
for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) {
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_CGenTableRow const *row = it.table->rows + row_index;
|
||||
Dqn_CGenLookupColumnAtHeader cpp_type = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppType].string, row);
|
||||
|
||||
Dqn_usize length = cpp_type.column.string.size;
|
||||
Dqn_Str8 find_name = Dqn_CGen_StripQualifiersOnCppType_(tmem.arena, cpp_type.column.string);
|
||||
if (Dqn_CGen_WillCodeGenTypeName(cgen, find_name))
|
||||
length += emit_prefix.size;
|
||||
|
||||
longest_type_name = DQN_MAX(longest_type_name, DQN_CAST(int)length);
|
||||
}
|
||||
|
||||
Dqn_CppStructBlock(cpp, "%.*s%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string)) {
|
||||
for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) {
|
||||
Dqn_CGenTableRow const *row = it.table->rows + row_index;
|
||||
Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row);
|
||||
Dqn_CGenLookupColumnAtHeader cpp_type = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppType].string, row);
|
||||
Dqn_CGenLookupColumnAtHeader cpp_array_size = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppArraySize].string, row);
|
||||
int name_to_type_padding = 1 + it.table->headers[cpp_type.index].longest_string - DQN_CAST(int) cpp_type.column.string.size;
|
||||
if (cpp_name.column.string.size <= 0 || cpp_type.column.string.size <= 0)
|
||||
continue;
|
||||
Dqn_CGen_EmitRowWhitespace_(row, cpp);
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
// NOTE: Generate cpp array size ///////////////////////////////////
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 array_size = {};
|
||||
if (cpp_array_size.column.string.size)
|
||||
array_size = Dqn_Str8_InitF(scratch.arena, "[%.*s]", DQN_STR_FMT(cpp_array_size.column.string));
|
||||
array_size = Dqn_Str8_InitF(tmem.arena, "[%.*s]", DQN_STR_FMT(cpp_array_size.column.string));
|
||||
|
||||
// NOTE: Check if we're referencing a code generated type. If we
|
||||
// are, append the `emit_prefix`
|
||||
Dqn_Str8 emit_cpp_type = cpp_type.column.string;
|
||||
{
|
||||
Dqn_Str8 find_name = Dqn_CGen_StripQualifiersOnCppType_(tmem.arena, emit_cpp_type);
|
||||
if (Dqn_CGen_WillCodeGenTypeName(cgen, find_name))
|
||||
emit_cpp_type = Dqn_Str8_InitF(tmem.arena, "%.*s%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(cpp_type.column.string));
|
||||
}
|
||||
|
||||
int name_to_type_padding = 1 + longest_type_name - DQN_CAST(int) emit_cpp_type.size;
|
||||
|
||||
// NOTE: Emit decl /////////////////////////////////////////////////
|
||||
Dqn_CGen_EmitRowWhitespace_(row, cpp);
|
||||
Dqn_CppLine(cpp,
|
||||
"%.*s%*s%.*s%.*s;",
|
||||
DQN_STR_FMT(cpp_type.column.string),
|
||||
DQN_STR_FMT(emit_cpp_type),
|
||||
name_to_type_padding,
|
||||
"",
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
DQN_STR_FMT(array_size));
|
||||
}
|
||||
}
|
||||
Dqn_CppNewLine(cpp);
|
||||
Dqn_CppNewLine(cpp);
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case Dqn_CGenTableType_CodeGenEnum: {
|
||||
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it); ) {
|
||||
Dqn_CppEnumBlock(cpp, "%.*s", DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string)) {
|
||||
Dqn_CppEnumBlock(cpp, "%.*s%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string)) {
|
||||
Dqn_usize enum_count = 0;
|
||||
for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) {
|
||||
Dqn_CGenTableRow const *row = it.table->rows + row_index;
|
||||
@ -576,13 +651,15 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
Dqn_CGen_EmitRowWhitespace_(row, cpp);
|
||||
if (cpp_value.column.string.size) {
|
||||
Dqn_CppLine(cpp,
|
||||
"%.*s_%.*s = %.*s,",
|
||||
"%.*s%.*s_%.*s = %.*s,",
|
||||
DQN_STR_FMT(emit_prefix),
|
||||
DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string),
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
DQN_STR_FMT(cpp_value.column.string));
|
||||
} else {
|
||||
Dqn_CppLine(cpp,
|
||||
"%.*s_%.*s = %zu,",
|
||||
"%.*s%.*s_%.*s = %zu,",
|
||||
DQN_STR_FMT(emit_prefix),
|
||||
DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string),
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
row_index);
|
||||
@ -593,11 +670,13 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
Dqn_CGenTableColumn gen_enum_count_column = it.cgen_table_column[Dqn_CGenTableHeaderType_GenEnumCount];
|
||||
if (gen_enum_count_column.string.size)
|
||||
Dqn_CppLine(cpp,
|
||||
"%.*s_%.*s = %zu,",
|
||||
"%.*s%.*s_%.*s = %zu,",
|
||||
DQN_STR_FMT(emit_prefix),
|
||||
DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string),
|
||||
DQN_STR_FMT(gen_enum_count_column.string),
|
||||
enum_count);
|
||||
}
|
||||
Dqn_CppNewLine(cpp);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
@ -606,24 +685,27 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
// NOTE: Generate enums for struct fields //////////////////////////////////////////////////
|
||||
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
|
||||
switch (table->type) {
|
||||
case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH;
|
||||
case Dqn_CGenTableType_Data: continue;
|
||||
case Dqn_CGenTableType_CodeGenEnum: continue;
|
||||
case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH;
|
||||
case Dqn_CGenTableType_Count: DQN_INVALID_CODE_PATH;
|
||||
case Dqn_CGenTableType_Data: continue;
|
||||
case Dqn_CGenTableType_CodeGenBuiltinTypes: continue;
|
||||
case Dqn_CGenTableType_CodeGenEnum: continue;
|
||||
|
||||
case Dqn_CGenTableType_CodeGenStruct: {
|
||||
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it); ) {
|
||||
Dqn_Str8 struct_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
|
||||
Dqn_CppEnumBlock(cpp, "%.*sTypeField", DQN_STR_FMT(struct_name)) {
|
||||
Dqn_CppEnumBlock(cpp, "%.*s%.*sTypeField", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(struct_name)) {
|
||||
for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) {
|
||||
Dqn_CGenTableRow const *row = it.table->rows + row_index;
|
||||
Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row);
|
||||
if (cpp_name.column.string.size <= 0)
|
||||
continue;
|
||||
Dqn_CGen_EmitRowWhitespace_(row, cpp);
|
||||
Dqn_CppLine(cpp, "%.*sTypeField_%.*s,", DQN_STR_FMT(struct_name), DQN_STR_FMT(cpp_name.column.string));
|
||||
Dqn_CppLine(cpp, "%.*s%.*sTypeField_%.*s,", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(struct_name), DQN_STR_FMT(cpp_name.column.string));
|
||||
}
|
||||
Dqn_CppLine(cpp, "%.*sTypeField_Count,", DQN_STR_FMT(struct_name));
|
||||
Dqn_CppLine(cpp, "%.*s%.*sTypeField_Count,", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(struct_name));
|
||||
}
|
||||
Dqn_CppNewLine(cpp);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
@ -636,10 +718,11 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
|
||||
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) {
|
||||
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
|
||||
Dqn_CppStructBlock(cpp, "%.*sStr8ToEnumResult", DQN_STR_FMT(type_name)) {
|
||||
Dqn_CppStructBlock(cpp, "%.*s%.*sStr8ToEnumResult", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name)) {
|
||||
Dqn_CppLine(cpp, "bool success;");
|
||||
Dqn_CppLine(cpp, "%.*s value;", DQN_STR_FMT(type_name));
|
||||
Dqn_CppLine(cpp, "%.*s%.*s value;", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name));
|
||||
}
|
||||
Dqn_CppNewLine(cpp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -649,7 +732,12 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
|
||||
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) {
|
||||
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
|
||||
Dqn_CppLine(cpp, "%.*sStr8ToEnumResult %.*s_Str8ToEnum(Dqn_Str8 string);", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name));
|
||||
Dqn_CppLine(cpp,
|
||||
"%.*s%.*sStr8ToEnumResult %.*s%.*s_Str8ToEnum(Dqn_Str8 string);",
|
||||
DQN_STR_FMT(emit_prefix),
|
||||
DQN_STR_FMT(type_name),
|
||||
DQN_STR_FMT(emit_prefix),
|
||||
DQN_STR_FMT(type_name));
|
||||
}
|
||||
}
|
||||
|
||||
@ -664,8 +752,18 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
continue;
|
||||
|
||||
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
|
||||
Dqn_CppLine(cpp, "bool operator==(%.*s const &lhs, %.*s const &rhs);", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name));
|
||||
Dqn_CppLine(cpp, "bool operator!=(%.*s const &lhs, %.*s const &rhs);", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name));
|
||||
Dqn_CppLine(cpp,
|
||||
"bool operator==(%.*s%.*s const &lhs, %.*s%.*s const &rhs);",
|
||||
DQN_STR_FMT(emit_prefix),
|
||||
DQN_STR_FMT(type_name),
|
||||
DQN_STR_FMT(emit_prefix),
|
||||
DQN_STR_FMT(type_name));
|
||||
Dqn_CppLine(cpp,
|
||||
"bool operator!=(%.*s%.*s const &lhs, %.*s%.*s const &rhs);",
|
||||
DQN_STR_FMT(emit_prefix),
|
||||
DQN_STR_FMT(type_name),
|
||||
DQN_STR_FMT(emit_prefix),
|
||||
DQN_STR_FMT(type_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -679,8 +777,29 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
continue;
|
||||
|
||||
Dqn_Str8 struct_or_enum_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
|
||||
Dqn_CppBlock(cpp, ";\n\n", "Dqn_TypeField const g_%.*s_type_fields[] =", DQN_STR_FMT(struct_or_enum_name)) {
|
||||
Dqn_CppBlock(cpp, ";\n\n", "Dqn_TypeField const g_%.*s%.*s_type_fields[] =", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(struct_or_enum_name)) {
|
||||
if (table->type == Dqn_CGenTableType_CodeGenStruct) {
|
||||
|
||||
// NOTE: Construct the cpp type string first. We will prepend `emit_prefix`
|
||||
// for types that are declared in the same mdesk file. We will also
|
||||
// calculate the longest type name that we will generate for whitespace
|
||||
// padding purposes.
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_PushTMem(nullptr);
|
||||
Dqn_usize longest_cpp_type_name = 0;
|
||||
auto cpp_type_list = Dqn_SArray_Init<Dqn_Str8>(tmem.arena, it.table->row_count, Dqn_ZeroMem_Yes);
|
||||
|
||||
for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) {
|
||||
Dqn_CGenTableRow const *row = it.table->rows + row_index;
|
||||
Dqn_CGenLookupColumnAtHeader cpp_type = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppType].string, row);
|
||||
Dqn_Str8 cpp_type_name = Dqn_CGen_StripQualifiersOnCppType_(tmem.arena, cpp_type.column.string);
|
||||
if (Dqn_CGen_WillCodeGenTypeName(cgen, cpp_type_name))
|
||||
cpp_type_name = Dqn_Str8_InitF_TLS("%.*s%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(cpp_type_name));
|
||||
|
||||
longest_cpp_type_name = DQN_MAX(longest_cpp_type_name, cpp_type_name.size);
|
||||
Dqn_SArray_Add(&cpp_type_list, cpp_type_name);
|
||||
}
|
||||
|
||||
// NOTE: Iterate each row and emit the C++ declarations ////////////////////
|
||||
for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) {
|
||||
Dqn_CGenTableRow const *row = it.table->rows + row_index;
|
||||
Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row);
|
||||
@ -688,12 +807,12 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
Dqn_CGenLookupColumnAtHeader cpp_is_ptr = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppIsPtr].string, row);
|
||||
Dqn_CGenLookupColumnAtHeader cpp_array_size = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppArraySize].string, row);
|
||||
Dqn_CGenLookupColumnAtHeader cpp_array_size_field = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppArraySizeField].string, row);
|
||||
Dqn_CGenLookupColumnAtHeader cpp_label = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppLabel].string, row);
|
||||
|
||||
bool cpp_is_ptr_b32 = cpp_is_ptr.column.string == DQN_STR8("true");
|
||||
Dqn_Str8 cpp_array_size_str8 = Dqn_Str8_HasData(cpp_array_size.column.string) ? cpp_array_size.column.string : DQN_STR8("0");
|
||||
|
||||
Dqn_CGenTableColumn struct_name = it.cgen_table_row->columns[table->column_indexes[Dqn_CGenTableHeaderType_Name]];
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 cpp_array_size_field_str8 = DQN_STR8("NULL");
|
||||
if (cpp_array_size_field.column.string.size) {
|
||||
|
||||
@ -707,57 +826,100 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
if (sub_cpp_name.string == cpp_array_size_field.column.string)
|
||||
index_the_field_references = sub_row_index;
|
||||
}
|
||||
cpp_array_size_field_str8 = Dqn_Str8_InitF(scratch.arena,
|
||||
"&g_%.*s_type_fields[%zu]",
|
||||
DQN_STR_FMT(struct_name.string),
|
||||
index_the_field_references);
|
||||
cpp_array_size_field_str8 =
|
||||
Dqn_Str8_InitF_TLS("&g_%.*s%.*s_type_fields[%zu]",
|
||||
DQN_STR_FMT(emit_prefix),
|
||||
DQN_STR_FMT(struct_name.string),
|
||||
index_the_field_references);
|
||||
}
|
||||
|
||||
Dqn_Str8 orig_cpp_type_info = Dqn_Str8_InitF(scratch.arena, "%.*s", DQN_STR_FMT(cpp_type.column.string));
|
||||
Dqn_Str8 cpp_type_info = orig_cpp_type_info;
|
||||
cpp_type_info = Dqn_Str8_Replace(cpp_type_info, /*find*/ DQN_STR8("*"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, scratch.arena, Dqn_Str8EqCase_Sensitive);
|
||||
cpp_type_info = Dqn_Str8_Replace(cpp_type_info, /*find*/ DQN_STR8("constexpr"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, scratch.arena, Dqn_Str8EqCase_Sensitive);
|
||||
cpp_type_info = Dqn_Str8_Replace(cpp_type_info, /*find*/ DQN_STR8("const"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, scratch.arena, Dqn_Str8EqCase_Sensitive);
|
||||
cpp_type_info = Dqn_Str8_Replace(cpp_type_info, /*find*/ DQN_STR8("static"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, scratch.arena, Dqn_Str8EqCase_Sensitive);
|
||||
cpp_type_info = Dqn_Str8_Replace(cpp_type_info, /*find*/ DQN_STR8(" "), /*replace*/ DQN_STR8(""), /*start_index*/ 0, scratch.arena, Dqn_Str8EqCase_Sensitive);
|
||||
Dqn_Str8 cpp_type_name = cpp_type_list.data[row_index];
|
||||
Dqn_Str8 orig_cpp_type = Dqn_CGen_StripQualifiersOnCppType_(tmem.arena, cpp_type.column.string);
|
||||
|
||||
Dqn_Str8 cpp_type_enum = Dqn_Str8_InitF(scratch.arena, "%.*s_Type_%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(cpp_type_info));
|
||||
Dqn_usize cpp_name_padding = 1 + it.table->headers[cpp_name.index].longest_string - cpp_name.column.string.size;
|
||||
Dqn_usize cpp_type_padding = 1 + it.table->headers[cpp_type.index].longest_string - cpp_type.column.string.size;
|
||||
Dqn_usize cpp_type_enum_padding = cpp_type_padding + (orig_cpp_type_info.size - cpp_type_info.size);
|
||||
Dqn_usize cpp_name_padding = 1 + it.table->headers[cpp_name.index].longest_string - cpp_name.column.string.size;
|
||||
Dqn_usize cpp_type_padding = 1 + longest_cpp_type_name - cpp_type_name.size;
|
||||
|
||||
Dqn_CppLine(cpp,
|
||||
"{%2d, DQN_STR8(\"%.*s\"),%*s/*value*/ 0, offsetof(%.*s, %.*s),%*ssizeof(((%.*s*)0)->%.*s),%*salignof(%.*s),%*sDQN_STR8(\"%.*s\"),%*s%.*s,%*s/*is_pointer*/ %s,%s /*array_size*/ %.*s, /*array_size_field*/ %.*s},",
|
||||
row_index,
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
cpp_name_padding, "",
|
||||
Dqn_Str8 cpp_type_enum = Dqn_Str8_InitF_TLS("%.*sType_%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(orig_cpp_type));
|
||||
Dqn_usize cpp_type_enum_padding = cpp_type_padding + (orig_cpp_type.size - cpp_type_name.size);
|
||||
|
||||
// NOTE: offsetof(a, b)
|
||||
DQN_STR_FMT(struct_or_enum_name),
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
cpp_name_padding, "",
|
||||
Dqn_Str8 cpp_label_str8 = cpp_name.column.string;
|
||||
Dqn_usize cpp_label_str8_padding = cpp_name_padding;
|
||||
if (cpp_label.column.string.size) {
|
||||
cpp_label_str8 = cpp_label.column.string;
|
||||
cpp_label_str8_padding = 1 + it.table->headers[cpp_label.index].longest_string - cpp_label.column.string.size;
|
||||
}
|
||||
|
||||
// NOTE: sizeof(a->b)
|
||||
DQN_STR_FMT(struct_or_enum_name),
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
cpp_name_padding, "",
|
||||
Dqn_Str8Builder builder = Dqn_Str8Builder_Init(tmem.arena);
|
||||
|
||||
// NOTE: alignof(a->b)
|
||||
DQN_STR_FMT(cpp_type.column.string),
|
||||
cpp_type_padding, "",
|
||||
// NOTE: row
|
||||
Dqn_Str8Builder_AddF(&builder, "{%2d, ", row_index);
|
||||
|
||||
// NOTE: Type string
|
||||
DQN_STR_FMT(cpp_type.column.string),
|
||||
cpp_type_padding, "",
|
||||
// NOTE: name
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
"DQN_STR8(\"%.*s\"),%*s",
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
cpp_name_padding, "");
|
||||
|
||||
// NOTE: Type as enum
|
||||
DQN_STR_FMT(cpp_type_enum),
|
||||
cpp_type_enum_padding, "",
|
||||
// NOTE: label
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
"DQN_STR8(\"%.*s\"),%*s",
|
||||
DQN_STR_FMT(cpp_label_str8),
|
||||
cpp_label_str8_padding, "");
|
||||
|
||||
cpp_is_ptr_b32 ? "true" : "false",
|
||||
cpp_is_ptr_b32 ? " " : "",
|
||||
DQN_STR_FMT(cpp_array_size_str8),
|
||||
DQN_STR_FMT(cpp_array_size_field_str8));
|
||||
// NOTE: value
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
"/*value*/ 0, ");
|
||||
|
||||
|
||||
// NOTE: offsetof(a, b)
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
"offsetof(%.*s%.*s, %.*s),%*s",
|
||||
DQN_STR_FMT(emit_prefix),
|
||||
DQN_STR_FMT(struct_or_enum_name),
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
cpp_name_padding, "");
|
||||
|
||||
// NOTE: sizeof(a->b)
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
"sizeof(((%.*s%.*s*)0)->%.*s),%*s",
|
||||
DQN_STR_FMT(emit_prefix),
|
||||
DQN_STR_FMT(struct_or_enum_name),
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
cpp_name_padding, "");
|
||||
|
||||
// NOTE: alignof(a)
|
||||
if (cpp_type_name == DQN_STR8("void")) {
|
||||
Dqn_Str8 proxy_type = DQN_STR8("char");
|
||||
Dqn_usize proxy_type_padding = 1 + longest_cpp_type_name - proxy_type.size;
|
||||
Dqn_Str8Builder_AddF(&builder, "alignof(%.*s),%*s", DQN_STR_FMT(proxy_type), proxy_type_padding, "");
|
||||
} else {
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
"alignof(%.*s),%*s",
|
||||
DQN_STR_FMT(cpp_type_name),
|
||||
cpp_type_padding, "");
|
||||
}
|
||||
|
||||
// NOTE: Type string
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
"DQN_STR8(\"%.*s\"),%*s",
|
||||
DQN_STR_FMT(cpp_type_name),
|
||||
cpp_type_padding, "");
|
||||
|
||||
// NOTE: Type as enum
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
"%.*s,%*s",
|
||||
DQN_STR_FMT(cpp_type_enum),
|
||||
cpp_type_enum_padding, "");
|
||||
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
"/*is_pointer*/ %s,%s /*array_size*/ %.*s, /*array_size_field*/ %.*s},",
|
||||
cpp_is_ptr_b32 ? "true" : "false",
|
||||
cpp_is_ptr_b32 ? " " : "",
|
||||
DQN_STR_FMT(cpp_array_size_str8),
|
||||
DQN_STR_FMT(cpp_array_size_field_str8));
|
||||
|
||||
Dqn_Str8 line = Dqn_Str8Builder_Build(&builder, tmem.arena);
|
||||
Dqn_CppLine(cpp, "%.*s", DQN_STR_FMT(line));
|
||||
}
|
||||
} else {
|
||||
DQN_ASSERT(table->type == Dqn_CGenTableType_CodeGenEnum);
|
||||
@ -765,32 +927,68 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
Dqn_CGenTableRow const *row = it.table->rows + row_index;
|
||||
Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row);
|
||||
Dqn_CGenLookupColumnAtHeader cpp_value = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppValue].string, row);
|
||||
Dqn_CGenLookupColumnAtHeader cpp_label = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppLabel].string, row);
|
||||
if (cpp_name.column.string.size <= 0)
|
||||
continue;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_PushTMem(nullptr);
|
||||
Dqn_usize cpp_name_padding = 1 + it.table->headers[cpp_name.index].longest_string - cpp_name.column.string.size;
|
||||
Dqn_Str8 cpp_value_str8 = Dqn_Str8_HasData(cpp_value.column.string) ? cpp_value.column.string : Dqn_Str8_InitF(scratch.arena, "%zu", row_index);
|
||||
Dqn_CppLine(cpp,
|
||||
"{%2d, DQN_STR8(\"%.*s\"),%*s/*value*/ %.*s, /*offset_of*/ 0, sizeof(%.*s), alignof(%.*s), DQN_STR8(\"\"), %.*s_Type_%.*s, /*is_pointer*/ false, /*array_size*/ 0, /*array_size_field*/ NULL},",
|
||||
row_index,
|
||||
Dqn_Str8 cpp_value_str8 = Dqn_Str8_HasData(cpp_value.column.string) ? cpp_value.column.string : Dqn_Str8_InitF_TLS("%zu", row_index);
|
||||
Dqn_Str8 cpp_type_enum = Dqn_Str8_InitF_TLS("%.*sType_%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(struct_or_enum_name));
|
||||
|
||||
// NOTE: Name string
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
cpp_name_padding, "",
|
||||
Dqn_Str8 cpp_label_str8 = cpp_name.column.string;
|
||||
Dqn_usize cpp_label_str8_padding = cpp_name_padding;
|
||||
if (cpp_label.column.string.size) {
|
||||
cpp_label_str8 = cpp_label.column.string;
|
||||
cpp_label_str8_padding = 1 + it.table->headers[cpp_label.index].longest_string - cpp_label.column.string.size;
|
||||
}
|
||||
|
||||
// NOTE: Value
|
||||
DQN_STR_FMT(cpp_value_str8),
|
||||
Dqn_Str8Builder builder = Dqn_Str8Builder_Init_TLS();
|
||||
// NOTE: row
|
||||
Dqn_Str8Builder_AddF(&builder, "{%2d, ", row_index);
|
||||
|
||||
// NOTE: sizeof(a)
|
||||
DQN_STR_FMT(struct_or_enum_name),
|
||||
// NOTE: name
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
"DQN_STR8(\"%.*s\"),%*s",
|
||||
DQN_STR_FMT(cpp_name.column.string),
|
||||
cpp_name_padding, "");
|
||||
|
||||
// NOTE: alignof(a)
|
||||
DQN_STR_FMT(struct_or_enum_name),
|
||||
// NOTE: label
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
"DQN_STR8(\"%.*s\"),%*s",
|
||||
DQN_STR_FMT(cpp_label_str8),
|
||||
cpp_label_str8_padding, "");
|
||||
|
||||
// NOTE: ..._Type_...
|
||||
DQN_STR_FMT(emit_prefix),
|
||||
DQN_STR_FMT(struct_or_enum_name));
|
||||
// NOTE: value
|
||||
Dqn_Str8Builder_AddF(&builder, "/*value*/ %.*s, ", DQN_STR_FMT(cpp_value_str8));
|
||||
|
||||
// NOTE: offsetof(a, b)
|
||||
Dqn_Str8Builder_AddF(&builder, "/*offsetof*/ 0, ");
|
||||
|
||||
// NOTE: sizeof(a->b)
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
"sizeof(%.*s%.*s), ",
|
||||
DQN_STR_FMT(emit_prefix),
|
||||
DQN_STR_FMT(struct_or_enum_name));
|
||||
|
||||
// NOTE: alignof(a->b)
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
"alignof(%.*s%.*s), ",
|
||||
DQN_STR_FMT(emit_prefix),
|
||||
DQN_STR_FMT(struct_or_enum_name));
|
||||
|
||||
// TODO: Type string
|
||||
Dqn_Str8Builder_AddF(&builder, "DQN_STR8(\"\"), ");
|
||||
|
||||
// NOTE: Type as enum
|
||||
Dqn_Str8Builder_AddF(&builder, "%.*s, ", DQN_STR_FMT(cpp_type_enum));
|
||||
|
||||
Dqn_Str8Builder_AddF(&builder, "/*is_pointer*/ false, ");
|
||||
Dqn_Str8Builder_AddF(&builder, "/*array_size*/ 0, ");
|
||||
Dqn_Str8Builder_AddF(&builder, "/*array_size_field*/ NULL},");
|
||||
|
||||
Dqn_Str8 line = Dqn_Str8Builder_Build_TLS(&builder);
|
||||
Dqn_CppLine(cpp, "%.*s", DQN_STR_FMT(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -800,18 +998,38 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
int longest_name_across_all_tables = 0;
|
||||
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
|
||||
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) {
|
||||
longest_name_across_all_tables = DQN_MAX(longest_name_across_all_tables, DQN_CAST(int)it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string.size);
|
||||
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
|
||||
if (Dqn_CGen_WillCodeGenTypeName(cgen, type_name))
|
||||
type_name = Dqn_Str8_InitF(tmem.arena, "%.*s%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name));
|
||||
|
||||
longest_name_across_all_tables = DQN_MAX(longest_name_across_all_tables, DQN_CAST(int)type_name.size);
|
||||
}
|
||||
}
|
||||
|
||||
Dqn_CppBlock(cpp, ";\n\n", "Dqn_TypeInfo const g_%.*s_types[] =", DQN_STR_FMT(emit_prefix)) {
|
||||
Dqn_CppBlock(cpp, ";\n\n", "Dqn_TypeInfo const g_%.*stypes[] =", DQN_STR_FMT(emit_prefix)) {
|
||||
Dqn_CppLine(cpp, "{DQN_STR8(\"\"),%*sDqn_TypeKind_Nil, 0, /*fields*/ NULL, /*count*/ 0},", 1 + longest_name_across_all_tables, "");
|
||||
|
||||
Dqn_usize longest_type_name = 0;
|
||||
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
|
||||
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) {
|
||||
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
int name_padding = 1 + longest_name_across_all_tables - DQN_CAST(int) type_name.size;
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
|
||||
if (Dqn_CGen_WillCodeGenTypeName(cgen, type_name))
|
||||
type_name = Dqn_Str8_InitF(tmem.arena, "%.*s%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name));
|
||||
longest_type_name = DQN_MAX(longest_type_name, type_name.size);
|
||||
}
|
||||
}
|
||||
|
||||
for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) {
|
||||
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) {
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_PushTMem(nullptr);
|
||||
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
|
||||
if (Dqn_CGen_WillCodeGenTypeName(cgen, type_name))
|
||||
type_name = Dqn_Str8_InitF_TLS("%.*s%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name));
|
||||
|
||||
int name_padding = 1 + longest_name_across_all_tables - DQN_CAST(int) type_name.size;
|
||||
Dqn_Str8 type_info_kind = {};
|
||||
char const *type_info_kind_padding = "";
|
||||
if (table->type == Dqn_CGenTableType_CodeGenEnum) {
|
||||
@ -836,27 +1054,56 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row);
|
||||
fields_count_int += Dqn_Str8_HasData(cpp_name.column.string);
|
||||
}
|
||||
fields_count = Dqn_Str8_InitF(scratch.arena, "%d", fields_count_int);
|
||||
fields_count = Dqn_Str8_InitF_TLS("%d", fields_count_int);
|
||||
}
|
||||
|
||||
Dqn_Str8 fields = DQN_STR8("NULL");
|
||||
int fields_padding = 1;
|
||||
if (table->type != Dqn_CGenTableType_CodeGenBuiltinTypes) {
|
||||
fields_padding = name_padding;
|
||||
fields = Dqn_Str8_InitF(scratch.arena, "g_%.*s_type_fields", DQN_STR_FMT(type_name));
|
||||
fields = Dqn_Str8_InitF(tmem.arena, "g_%.*s_type_fields", DQN_STR_FMT(type_name));
|
||||
}
|
||||
|
||||
Dqn_CppLine(cpp,
|
||||
"{DQN_STR8(\"%.*s\"),%*s%.*s, %ssizeof(%.*s),%*s/*fields*/ %.*s,%*s/*count*/ %.*s},",
|
||||
DQN_STR_FMT(type_name),
|
||||
name_padding, "",
|
||||
DQN_STR_FMT(type_info_kind),
|
||||
type_info_kind_padding,
|
||||
DQN_STR_FMT(type_name),
|
||||
name_padding, "",
|
||||
DQN_STR_FMT(fields),
|
||||
fields_padding, "",
|
||||
DQN_STR_FMT(fields_count));
|
||||
Dqn_Str8Builder builder = Dqn_Str8Builder_Init_TLS();
|
||||
|
||||
// NOTE: name
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
"{DQN_STR8(\"%.*s\"),%*s",
|
||||
DQN_STR_FMT(type_name),
|
||||
name_padding,
|
||||
"");
|
||||
|
||||
// NOTE: Dqn_TypeKind_{Nil|Basic|Enum|Struct}
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
"%.*s,%s",
|
||||
DQN_STR_FMT(type_info_kind),
|
||||
type_info_kind_padding);
|
||||
|
||||
// NOTE: sizeof(T)
|
||||
if (type_name == DQN_STR8("void")) {
|
||||
Dqn_Str8Builder_AddF(&builder, "0,%*s", name_padding, "");
|
||||
} else {
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
"sizeof(%.*s),%*s",
|
||||
DQN_STR_FMT(type_name),
|
||||
name_padding,
|
||||
"");
|
||||
}
|
||||
|
||||
// NOTE: Pointer to Dqn_TypeField[]
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
"/*fields*/ %.*s,%*s",
|
||||
DQN_STR_FMT(fields),
|
||||
fields_padding,
|
||||
"");
|
||||
|
||||
// NOTE: Dqn_TypeField length
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
"/*count*/ %.*s},",
|
||||
DQN_STR_FMT(fields_count));
|
||||
|
||||
Dqn_Str8 line = Dqn_Str8Builder_Build_TLS(&builder);
|
||||
Dqn_CppLine(cpp, "%.*s", DQN_STR_FMT(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -868,19 +1115,21 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
|
||||
for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) {
|
||||
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
|
||||
Dqn_CppFuncBlock(cpp, "%.*sStr8ToEnumResult %.*s_Str8ToEnum(Dqn_Str8 string)", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name)) {
|
||||
Dqn_CppLine(cpp, "%.*sStr8ToEnumResult result = {};", DQN_STR_FMT(type_name));
|
||||
Dqn_CppForBlock(cpp, "Dqn_usize index = 0; !result.success && index < DQN_ARRAY_UCOUNT(g_%.*s_type_fields); index++", DQN_STR_FMT(type_name)) {
|
||||
|
||||
Dqn_CppFuncBlock(cpp, "%.*s%.*sStr8ToEnumResult %.*s%.*s_Str8ToEnum(Dqn_Str8 string)", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name), DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name)) {
|
||||
Dqn_CppLine(cpp, "%.*s%.*sStr8ToEnumResult result = {};", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name));
|
||||
Dqn_CppForBlock(cpp, "Dqn_usize index = 0; !result.success && index < DQN_ARRAY_UCOUNT(g_%.*s%.*s_type_fields); index++", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name)) {
|
||||
Dqn_CppIfChain(cpp) {
|
||||
Dqn_CppLine(cpp, "Dqn_TypeField field = g_%.*s_type_fields[index];", DQN_STR_FMT(type_name));
|
||||
Dqn_CppLine(cpp, "Dqn_TypeField field = g_%.*s%.*s_type_fields[index];", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name));
|
||||
Dqn_CppIfOrElseIfBlock(cpp, "string == field.name") {
|
||||
Dqn_CppLine(cpp, "result.success = true;");
|
||||
Dqn_CppLine(cpp, "result.value = (%.*s)index;", DQN_STR_FMT(type_name));
|
||||
Dqn_CppLine(cpp, "result.value = (%.*s%.*s)index;", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
Dqn_CppLine(cpp, "return result;");
|
||||
}
|
||||
Dqn_CppNewLine(cpp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -895,7 +1144,7 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
continue;
|
||||
|
||||
Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string;
|
||||
Dqn_CppFuncBlock(cpp, "bool operator==(%.*s const &lhs, %.*s const &rhs)", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name)) {
|
||||
Dqn_CppFuncBlock(cpp, "bool operator==(%.*s%.*s const &lhs, %.*s%.*s const &rhs)", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name), DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name)) {
|
||||
for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) {
|
||||
Dqn_CGenTableRow const *row = it.table->rows + row_index;
|
||||
Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row);
|
||||
@ -955,11 +1204,13 @@ DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_C
|
||||
}
|
||||
Dqn_CppLine(cpp, "return true;");
|
||||
}
|
||||
Dqn_CppNewLine(cpp);
|
||||
|
||||
Dqn_CppFuncBlock(cpp, "bool operator!=(%.*s const &lhs, %.*s const &rhs)", DQN_STR_FMT(type_name), DQN_STR_FMT(type_name)) {
|
||||
Dqn_CppFuncBlock(cpp, "bool operator!=(%.*s%.*s const &lhs, %.*s%.*s const &rhs)", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name), DQN_STR_FMT(emit_prefix), DQN_STR_FMT(type_name)) {
|
||||
Dqn_CppLine(cpp, "bool result = !(lhs == rhs);");
|
||||
Dqn_CppLine(cpp, "return result;");
|
||||
}
|
||||
Dqn_CppNewLine(cpp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#if !defined(DQN_CGEN_H)
|
||||
#define DQN_CGEN_H
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$$$$$$$\ $$\ $$\
|
||||
@ -15,6 +16,7 @@
|
||||
// dqn_cgen.h -- C/C++ code generation from table data in Metadesk files
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$CGEN] Dqn_CGen //////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(MD_H)
|
||||
@ -70,6 +72,7 @@ enum Dqn_CGenTableHeaderType
|
||||
Dqn_CGenTableHeaderType_CppOpEquals,
|
||||
Dqn_CGenTableHeaderType_CppArraySize,
|
||||
Dqn_CGenTableHeaderType_CppArraySizeField,
|
||||
Dqn_CGenTableHeaderType_CppLabel,
|
||||
Dqn_CGenTableHeaderType_GenTypeInfo,
|
||||
Dqn_CGenTableHeaderType_GenEnumCount,
|
||||
Dqn_CGenTableHeaderType_Count,
|
||||
@ -162,6 +165,7 @@ enum Dqn_CGenEmit
|
||||
DQN_API Dqn_CGen Dqn_CGen_InitFilesArgV (int argc, char const **argv, Dqn_ErrorSink *error);
|
||||
DQN_API Dqn_Str8 Dqn_CGen_TableHeaderTypeToDeclStr8 (Dqn_CGenTableHeaderType type);
|
||||
DQN_API Dqn_CGenMapNodeToEnum Dqn_CGen_MapNodeToEnumOrExit (MD_Node const *node, Dqn_CGenMapNodeToEnum const *valid_keys, Dqn_usize valid_keys_size, char const *fmt, ...);
|
||||
DQN_API Dqn_usize Dqn_CGen_NodeChildrenCount (MD_Node const *node);
|
||||
DQN_API void Dqn_CGen_LogF (MD_MessageKind kind, MD_Node *node, Dqn_ErrorSink *error, char const *fmt, ...);
|
||||
DQN_API bool Dqn_CGen_TableHasHeaders (Dqn_CGenTable const *table, Dqn_Str8 const *headers, Dqn_usize header_count, Dqn_ErrorSink *error);
|
||||
DQN_API Dqn_CGenLookupColumnAtHeader Dqn_CGen_LookupColumnAtHeader (Dqn_CGenTable *table, Dqn_Str8 header, Dqn_CGenTableRow const *row);
|
||||
|
@ -56,14 +56,52 @@ DQN_API Dqn_Str8 Dqn_Slice_Str8RenderSpaceSeparated(Dqn_Arena *arena, Dqn_Slice<
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str16 Dqn_Slice_Str16Render(Dqn_Arena *arena, Dqn_Slice<Dqn_Str16> array, Dqn_Str16 separator)
|
||||
{
|
||||
Dqn_Str16 result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
|
||||
Dqn_usize total_size = 0;
|
||||
for (Dqn_usize index = 0; index < array.size; index++) {
|
||||
if (index)
|
||||
total_size += separator.size;
|
||||
Dqn_Str16 item = array.data[index];
|
||||
total_size += item.size;
|
||||
}
|
||||
|
||||
result = {Dqn_Arena_NewArray(arena, wchar_t, total_size + 1, Dqn_ZeroMem_No), total_size};
|
||||
if (result.data) {
|
||||
Dqn_usize write_index = 0;
|
||||
for (Dqn_usize index = 0; index < array.size; index++) {
|
||||
if (index) {
|
||||
DQN_MEMCPY(result.data + write_index, separator.data, separator.size * sizeof(result.data[0]));
|
||||
write_index += separator.size;
|
||||
}
|
||||
Dqn_Str16 item = array.data[index];
|
||||
DQN_MEMCPY(result.data + write_index, item.data, item.size * sizeof(result.data[0]));
|
||||
write_index += item.size;
|
||||
}
|
||||
}
|
||||
|
||||
result.data[total_size] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str16 Dqn_Slice_Str16RenderSpaceSeparated(Dqn_Arena *arena, Dqn_Slice<Dqn_Str16> array)
|
||||
{
|
||||
Dqn_Str16 result = Dqn_Slice_Str16Render(arena, array, DQN_STR16(L" "));
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(DQN_NO_DSMAP)
|
||||
// NOTE: [$DMAP] Dqn_DSMap /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64NoHash(uint64_t u64)
|
||||
{
|
||||
Dqn_DSMapKey result = {};
|
||||
result.type = Dqn_DSMapKeyType_U64NoHash;
|
||||
result.payload.u64 = u64;
|
||||
result.hash = DQN_CAST(uint32_t)u64;
|
||||
result.u64 = u64;
|
||||
result.hash = DQN_CAST(uint32_t) u64;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -72,11 +110,15 @@ DQN_API bool Dqn_DSMap_KeyEquals(Dqn_DSMapKey lhs, Dqn_DSMapKey rhs)
|
||||
bool result = false;
|
||||
if (lhs.type == rhs.type && lhs.hash == rhs.hash) {
|
||||
switch (lhs.type) {
|
||||
case Dqn_DSMapKeyType_Invalid: result = true; break;
|
||||
case Dqn_DSMapKeyType_U64NoHash: result = true; break;
|
||||
case Dqn_DSMapKeyType_U64: result = lhs.payload.u64 == rhs.payload.u64; break;
|
||||
case Dqn_DSMapKeyType_Buffer: result = lhs.payload.buffer.size == rhs.payload.buffer.size &&
|
||||
memcmp(lhs.payload.buffer.data, rhs.payload.buffer.data, lhs.payload.buffer.size) == 0; break;
|
||||
case Dqn_DSMapKeyType_Invalid: result = true; break;
|
||||
case Dqn_DSMapKeyType_U64NoHash: result = true; break;
|
||||
case Dqn_DSMapKeyType_U64: result = lhs.u64 == rhs.u64; break;
|
||||
|
||||
case Dqn_DSMapKeyType_BufferAsU64NoHash: /*FALLTHRU*/
|
||||
case Dqn_DSMapKeyType_Buffer: {
|
||||
if (lhs.buffer_size == rhs.buffer_size)
|
||||
result = DQN_MEMCMP(lhs.buffer_data, rhs.buffer_data, lhs.buffer_size) == 0;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
312
dqn_containers.h
312
dqn_containers.h
@ -110,23 +110,22 @@ template <typename T> using Dqn_FArray64 = Dqn_FArray<T, 64>;
|
||||
// NOTE: [$DMAP] Dqn_DSMap /////////////////////////////////////////////////////////////////////////
|
||||
enum Dqn_DSMapKeyType
|
||||
{
|
||||
// Key | Key Hash | Map Index
|
||||
Dqn_DSMapKeyType_Invalid,
|
||||
Dqn_DSMapKeyType_U64, // Use a U64 key that is `hash(u64) % size` to map into the table
|
||||
Dqn_DSMapKeyType_U64NoHash, // Use a U64 key that is `u64 % size` to map into the table
|
||||
Dqn_DSMapKeyType_Buffer, // Use a buffer key that is `hash(buffer) % size` to map into the table
|
||||
Dqn_DSMapKeyType_U64, // U64 | Hash(U64) | Hash(U64) % map_size
|
||||
Dqn_DSMapKeyType_U64NoHash, // U64 | U64 | U64 % map_size
|
||||
Dqn_DSMapKeyType_Buffer, // Buffer | Hash(buffer) | Hash(buffer) % map_size
|
||||
Dqn_DSMapKeyType_BufferAsU64NoHash, // Buffer | U64(buffer[0:4]) | U64(buffer[0:4]) % map_size
|
||||
};
|
||||
|
||||
struct Dqn_DSMapKey
|
||||
{
|
||||
Dqn_DSMapKeyType type;
|
||||
uint32_t hash;
|
||||
union Payload {
|
||||
struct Buffer {
|
||||
void const *data;
|
||||
uint32_t size;
|
||||
} buffer;
|
||||
uint64_t u64;
|
||||
} payload;
|
||||
uint32_t hash; // Hash to lookup in the map. If it equals, we check that the original key payload matches
|
||||
void const *buffer_data;
|
||||
uint32_t buffer_size;
|
||||
uint64_t u64;
|
||||
bool no_copy_buffer;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@ -136,6 +135,13 @@ struct Dqn_DSMapSlot
|
||||
T value; ///< Hash table value
|
||||
};
|
||||
|
||||
typedef uint32_t Dqn_DSMapFlags;
|
||||
enum Dqn_DSMapFlags_
|
||||
{
|
||||
Dqn_DSMapFlags_Nil = 0,
|
||||
Dqn_DSMapFlags_DontFreeArenaOnResize = 1 << 0,
|
||||
};
|
||||
|
||||
using Dqn_DSMapHashFunction = uint32_t(Dqn_DSMapKey key, uint32_t seed);
|
||||
template <typename T> struct Dqn_DSMap
|
||||
{
|
||||
@ -147,6 +153,7 @@ template <typename T> struct Dqn_DSMap
|
||||
uint32_t initial_size; // Initial map size, map cannot shrink on erase below this size
|
||||
Dqn_DSMapHashFunction *hash_function; // Custom hashing function to use if field is set
|
||||
uint32_t hash_seed; // Seed for the hashing function, when 0, DQN_DS_MAP_DEFAULT_HASH_SEED is used
|
||||
Dqn_DSMapFlags flags;
|
||||
};
|
||||
|
||||
template <typename T> struct Dqn_DSMapResult
|
||||
@ -179,7 +186,6 @@ template <typename T> struct Dqn_ListIterator
|
||||
|
||||
template <typename T> struct Dqn_List
|
||||
{
|
||||
Dqn_Arena *arena;
|
||||
Dqn_usize count; // Cumulative count of all items made across all list chunks
|
||||
Dqn_usize chunk_size; // When new ListChunk's are required, the minimum 'data' entries to allocate for that node.
|
||||
Dqn_ListChunk<T> *head;
|
||||
@ -254,6 +260,7 @@ template <typename T> void Dqn_SArra
|
||||
#endif // !defined(DQN_NO_SARRAY)
|
||||
#if !defined(DQN_NO_FARRAY)
|
||||
template <typename T, Dqn_usize N> Dqn_FArray<T, N> Dqn_FArray_Init (T const *array, Dqn_usize count);
|
||||
#define Dqn_FArray_HasData(array) ((array).data && (array).size)
|
||||
template <typename T, Dqn_usize N> Dqn_FArray<T, N> Dqn_FArray_InitSlice (Dqn_Slice<T> slice);
|
||||
template <typename T, Dqn_usize N, Dqn_usize K> Dqn_FArray<T, N> Dqn_FArray_InitCArray (T const (&items)[K]);
|
||||
template <typename T, Dqn_usize N> bool Dqn_FArray_IsValid (Dqn_FArray<T, N> const *array);
|
||||
@ -289,9 +296,11 @@ template <typename T> Dqn_Slice<T> Dqn_Slice
|
||||
template <typename T> Dqn_Slice<T> Dqn_Slice_Alloc (Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem);
|
||||
Dqn_Str8 Dqn_Slice_Str8Render (Dqn_Arena *arena, Dqn_Slice<Dqn_Str8> array, Dqn_Str8 separator);
|
||||
Dqn_Str8 Dqn_Slice_Str8RenderSpaceSeparated (Dqn_Arena *arena, Dqn_Slice<Dqn_Str8> array);
|
||||
Dqn_Str16 Dqn_Slice_Str16Render (Dqn_Arena *arena, Dqn_Slice<Dqn_Str16> array, Dqn_Str16 separator);
|
||||
Dqn_Str16 Dqn_Slice_Str16RenderSpaceSeparated(Dqn_Arena *arena, Dqn_Slice<Dqn_Str16> array);
|
||||
#endif // !defined(DQN_NO_SLICE)
|
||||
#if !defined(DQN_NO_DSMAP)
|
||||
template <typename T> Dqn_DSMap<T> Dqn_DSMap_Init (Dqn_Arena *arena, uint32_t size);
|
||||
template <typename T> Dqn_DSMap<T> Dqn_DSMap_Init (Dqn_Arena *arena, uint32_t size, Dqn_DSMapFlags flags);
|
||||
template <typename T> void Dqn_DSMap_Deinit (Dqn_DSMap<T> *map, Dqn_ZeroMem zero_mem);
|
||||
template <typename T> bool Dqn_DSMap_IsValid (Dqn_DSMap<T> const *map);
|
||||
template <typename T> uint32_t Dqn_DSMap_Hash (Dqn_DSMap<T> const *map, Dqn_DSMapKey key);
|
||||
@ -305,27 +314,28 @@ template <typename T> Dqn_DSMapResult<T> Dqn_DSMap
|
||||
template <typename T> Dqn_DSMapResult<T> Dqn_DSMap_FindKeyStr8 (Dqn_DSMap<T> const *map, Dqn_Str8 key);
|
||||
template <typename T> Dqn_DSMapResult<T> Dqn_DSMap_MakeKeyStr8 (Dqn_DSMap<T> *map, Dqn_Str8 key);
|
||||
template <typename T> Dqn_DSMapResult<T> Dqn_DSMap_SetKeyStr8 (Dqn_DSMap<T> *map, Dqn_Str8 key, T const &value);
|
||||
template <typename T> Dqn_DSMapResult<T> Dqn_DSMap_MakeKeyStr8Copy (Dqn_DSMap<T> *map, Dqn_Arena *arena, Dqn_Str8 key);
|
||||
template <typename T> Dqn_DSMapResult<T> Dqn_DSMap_SetKeyStr8Copy (Dqn_DSMap<T> *map, Dqn_Arena *arena, Dqn_Str8 key, T const &value);
|
||||
template <typename T> bool Dqn_DSMap_Resize (Dqn_DSMap<T> *map, uint32_t size);
|
||||
template <typename T> bool Dqn_DSMap_Erase (Dqn_DSMap<T> *map, Dqn_DSMapKey key);
|
||||
template <typename T> Dqn_DSMapKey Dqn_DSMap_KeyBuffer (Dqn_DSMap<T> const *map, void const *data, uint32_t size);
|
||||
template <typename T> Dqn_DSMapKey Dqn_DSMap_KeyBufferAsU64NoHash (Dqn_DSMap<T> const *map, void const *data, uint32_t size);
|
||||
template <typename T> Dqn_DSMapKey Dqn_DSMap_KeyU64 (Dqn_DSMap<T> const *map, uint64_t u64);
|
||||
template <typename T> Dqn_DSMapKey Dqn_DSMap_KeyStr8 (Dqn_DSMap<T> const *map, Dqn_Str8 string);
|
||||
template <typename T> Dqn_DSMapKey Dqn_DSMap_KeyStr8Copy (Dqn_DSMap<T> const *map, Dqn_Arena *arena, Dqn_Str8 string);
|
||||
#define Dqn_DSMap_KeyCStr8(map, string) Dqn_DSMap_KeyBuffer(map, string, sizeof((string))/sizeof((string)[0]) - 1)
|
||||
DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64NoHash (uint64_t u64);
|
||||
DQN_API bool Dqn_DSMap_KeyEquals (Dqn_DSMapKey lhs, Dqn_DSMapKey rhs);
|
||||
DQN_API bool operator== (Dqn_DSMapKey lhs, Dqn_DSMapKey rhs);
|
||||
#endif // !defined(DQN_NO_DSMAP)
|
||||
#if !defined(DQN_NO_LIST)
|
||||
template <typename T> Dqn_List<T> Dqn_List_Init (Dqn_Arena *arena, Dqn_usize chunk_size);
|
||||
template <typename T> Dqn_List<T> Dqn_List_Init (Dqn_usize chunk_size);
|
||||
template <typename T, size_t N> Dqn_List<T> Dqn_List_InitCArray (Dqn_Arena *arena, Dqn_usize chunk_size, T const (&array)[N]);
|
||||
template <typename T> T * Dqn_List_At (Dqn_List<T> *list, Dqn_usize index, Dqn_ListChunk<T> *at_chunk);
|
||||
template <typename T> bool Dqn_List_Iterate (Dqn_List<T> *list, Dqn_ListIterator<T> *it, Dqn_usize start_index);
|
||||
template <typename T> T * Dqn_List_Make (Dqn_List<T> *list, Dqn_usize count);
|
||||
template <typename T> T * Dqn_List_Add (Dqn_List<T> *list, T const &value);
|
||||
template <typename T> void Dqn_List_AddList (Dqn_List<T> *list, Dqn_List<T> other);
|
||||
template <typename T> T * Dqn_List_MakeArena (Dqn_List<T> *list, Dqn_Arena *arena, Dqn_usize count);
|
||||
template <typename T> T * Dqn_List_MakePool (Dqn_List<T> *list, Dqn_ChunkPool *pool, Dqn_usize count);
|
||||
template <typename T> T * Dqn_List_AddArena (Dqn_List<T> *list, Dqn_Arena *arena, T const &value);
|
||||
template <typename T> T * Dqn_List_AddPool (Dqn_List<T> *list, Dqn_ChunkPool *pool, T const &value);
|
||||
template <typename T> void Dqn_List_AddListArena (Dqn_List<T> *list, Dqn_Arena *arena, Dqn_List<T> other);
|
||||
template <typename T> void Dqn_List_AddListArena (Dqn_List<T> *list, Dqn_ChunkPool *pool, Dqn_List<T> other);
|
||||
template <typename T> Dqn_Slice<T> Dqn_List_ToSliceCopy (Dqn_List<T> const *list, Dqn_Arena* arena);
|
||||
#endif // !defined(DQN_NO_LIST)
|
||||
|
||||
@ -932,19 +942,22 @@ template <typename T> Dqn_Slice<T> Dqn_Slice_Alloc(Dqn_Arena *arena, Dqn_usize s
|
||||
uint32_t const DQN_DS_MAP_DEFAULT_HASH_SEED = 0x8a1ced49;
|
||||
uint32_t const DQN_DS_MAP_SENTINEL_SLOT = 0;
|
||||
|
||||
template <typename T> Dqn_DSMap<T> Dqn_DSMap_Init(Dqn_Arena *arena, uint32_t size)
|
||||
template <typename T> Dqn_DSMap<T> Dqn_DSMap_Init(Dqn_Arena *arena, uint32_t size, Dqn_DSMapFlags flags)
|
||||
{
|
||||
Dqn_DSMap<T> result = {};
|
||||
if (!DQN_CHECKF(Dqn_IsPowerOfTwo(size), "Power-of-two size required, given size was '%u'", size))
|
||||
return result;
|
||||
if (!arena)
|
||||
if (!DQN_CHECKF(size > 0, "Non-zero size must be given"))
|
||||
return result;
|
||||
if (!DQN_CHECK(arena))
|
||||
return result;
|
||||
result.arena = arena;
|
||||
result.hash_to_slot = Dqn_Arena_NewArray(result.arena, uint32_t, size, Dqn_ZeroMem_No);
|
||||
result.slots = Dqn_Arena_NewArray(result.arena, Dqn_DSMapSlot<T>, size, Dqn_ZeroMem_No);
|
||||
result.hash_to_slot = Dqn_Arena_NewArray(result.arena, uint32_t, size, Dqn_ZeroMem_Yes);
|
||||
result.slots = Dqn_Arena_NewArray(result.arena, Dqn_DSMapSlot<T>, size, Dqn_ZeroMem_Yes);
|
||||
result.occupied = 1; // For sentinel
|
||||
result.size = size;
|
||||
result.initial_size = size;
|
||||
result.flags = flags;
|
||||
DQN_ASSERTF(result.hash_to_slot && result.slots, "We pre-allocated a block of memory sufficient in size for the 2 arrays. Maybe the pointers needed extra space because of natural alignment?");
|
||||
return result;
|
||||
}
|
||||
@ -980,7 +993,12 @@ uint32_t Dqn_DSMap_Hash(Dqn_DSMap<T> const *map, Dqn_DSMapKey key)
|
||||
return result;
|
||||
|
||||
if (key.type == Dqn_DSMapKeyType_U64NoHash) {
|
||||
result = DQN_CAST(uint32_t)key.payload.u64;
|
||||
result = DQN_CAST(uint32_t)key.u64;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (key.type == Dqn_DSMapKeyType_BufferAsU64NoHash) {
|
||||
result = key.hash;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -998,17 +1016,18 @@ uint32_t Dqn_DSMap_Hash(Dqn_DSMap<T> const *map, Dqn_DSMapKey key)
|
||||
uint32_t len = 0;
|
||||
uint32_t h = seed;
|
||||
switch (key.type) {
|
||||
case Dqn_DSMapKeyType_BufferAsU64NoHash: /*FALLTHRU*/
|
||||
case Dqn_DSMapKeyType_U64NoHash: DQN_INVALID_CODE_PATH; /*FALLTHRU*/
|
||||
case Dqn_DSMapKeyType_Invalid: break;
|
||||
|
||||
case Dqn_DSMapKeyType_Buffer:
|
||||
key_ptr = DQN_CAST(char const *)key.payload.buffer.data;
|
||||
len = key.payload.buffer.size;
|
||||
key_ptr = DQN_CAST(char const *)key.buffer_data;
|
||||
len = key.buffer_size;
|
||||
break;
|
||||
|
||||
case Dqn_DSMapKeyType_U64:
|
||||
key_ptr = DQN_CAST(char const *)&key.payload.u64;
|
||||
len = sizeof(key.payload.u64);
|
||||
key_ptr = DQN_CAST(char const *)&key.u64;
|
||||
len = sizeof(key.u64);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1048,6 +1067,7 @@ uint32_t Dqn_DSMap_Hash(Dqn_DSMap<T> const *map, Dqn_DSMapKey key)
|
||||
template <typename T>
|
||||
uint32_t Dqn_DSMap_HashToSlotIndex(Dqn_DSMap<T> const *map, Dqn_DSMapKey key)
|
||||
{
|
||||
DQN_ASSERT(key.type != Dqn_DSMapKeyType_Invalid);
|
||||
uint32_t result = DQN_DS_MAP_SENTINEL_SLOT;
|
||||
if (!Dqn_DSMap_IsValid(map))
|
||||
return result;
|
||||
@ -1101,6 +1121,11 @@ Dqn_DSMapResult<T> Dqn_DSMap_Make(Dqn_DSMap<T> *map, Dqn_DSMapKey key)
|
||||
} else {
|
||||
result.slot = map->slots + map->hash_to_slot[index];
|
||||
result.slot->key = key; // NOTE: Assign key to new slot
|
||||
if ((result.slot->key.type == Dqn_DSMapKeyType_Buffer ||
|
||||
result->slot.key.type == Dqn_DSMapKeyType_BufferAsU64NoHash) &&
|
||||
!key.no_copy_buffer) {
|
||||
result.slot->key.buffer_data = Dqn_Arena_Copy(map->arena, key.buffer_data, key.buffer_size, 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result.slot = map->slots + map->hash_to_slot[index];
|
||||
@ -1108,6 +1133,7 @@ Dqn_DSMapResult<T> Dqn_DSMap_Make(Dqn_DSMap<T> *map, Dqn_DSMapKey key)
|
||||
}
|
||||
|
||||
result.value = &result.slot->value;
|
||||
DQN_ASSERT(result.slot->key.type != Dqn_DSMapKeyType_Invalid);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1171,30 +1197,6 @@ Dqn_DSMapResult<T> Dqn_DSMap_SetKeyStr8(Dqn_DSMap<T> *map, Dqn_Str8 key, T const
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Dqn_DSMapResult<T> Dqn_DSMap_MakeKeyStr8Copy(Dqn_DSMap<T> *map, Dqn_Arena *arena, Dqn_Str8 key)
|
||||
{
|
||||
Dqn_ArenaTempMem temp_mem = Dqn_Arena_TempMemBegin(arena);
|
||||
Dqn_DSMapKey map_key = Dqn_DSMap_KeyStr8Copy(map, arena, key);
|
||||
Dqn_DSMapResult<T> result = Dqn_DSMap_Make(map, map_key);
|
||||
// NOTE: If it already exists then we already have the key, we can deallocate
|
||||
if (result.found)
|
||||
Dqn_Arena_TempMemEnd(temp_mem);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Dqn_DSMapResult<T> Dqn_DSMap_SetKeyStr8Copy(Dqn_DSMap<T> *map, Dqn_Arena *arena, Dqn_Str8 key, T const &value)
|
||||
{
|
||||
Dqn_ArenaTempMem temp_mem = Dqn_Arena_TempMemBegin(arena);
|
||||
Dqn_DSMapKey map_key = Dqn_DSMap_KeyStr8Copy(map, arena, key);
|
||||
Dqn_DSMapResult<T> result = Dqn_DSMap_Set(map, map_key);
|
||||
// NOTE: If it already exists then we already have the key, we can deallocate
|
||||
if (result.found)
|
||||
Dqn_Arena_TempMemEnd(temp_mem);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Dqn_DSMap_Resize(Dqn_DSMap<T> *map, uint32_t size)
|
||||
{
|
||||
@ -1205,20 +1207,21 @@ bool Dqn_DSMap_Resize(Dqn_DSMap<T> *map, uint32_t size)
|
||||
Dqn_Arena new_arena = {};
|
||||
new_arena.flags = prev_arena->flags;
|
||||
|
||||
Dqn_DSMap<T> new_map = Dqn_DSMap_Init<T>(&new_arena, size);
|
||||
Dqn_DSMap<T> new_map = Dqn_DSMap_Init<T>(&new_arena, size, map->flags);
|
||||
if (!Dqn_DSMap_IsValid(&new_map))
|
||||
return false;
|
||||
|
||||
new_map.initial_size = map->initial_size;
|
||||
for (uint32_t old_index = 1 /*Sentinel*/; old_index < map->occupied; old_index++) {
|
||||
Dqn_DSMapSlot<T> *old_slot = map->slots + old_index;
|
||||
if (old_slot->key.type != Dqn_DSMapKeyType_Invalid) {
|
||||
Dqn_DSMap_Set(&new_map, old_slot->key, old_slot->value);
|
||||
}
|
||||
Dqn_DSMapKey old_key = old_slot->key;
|
||||
if (old_key.type == Dqn_DSMapKeyType_Invalid)
|
||||
continue;
|
||||
Dqn_DSMap_Set(&new_map, old_key, old_slot->value);
|
||||
}
|
||||
|
||||
DQN_MEMCPY(new_map.slots, map->slots, sizeof(*map->slots) * map->occupied);
|
||||
Dqn_DSMap_Deinit(map, Dqn_ZeroMem_No);
|
||||
if ((map->flags & Dqn_DSMapFlags_DontFreeArenaOnResize) == 0)
|
||||
Dqn_DSMap_Deinit(map, Dqn_ZeroMem_No);
|
||||
*map = new_map; // Update the map inplace
|
||||
map->arena = prev_arena; // Restore the previous arena pointer, it's been de-init-ed
|
||||
*map->arena = new_arena; // Re-init the old arena with the new data
|
||||
@ -1269,14 +1272,17 @@ bool Dqn_DSMap_Erase(Dqn_DSMap<T> *map, Dqn_DSMapKey key)
|
||||
// We will now fill in the vacant spot that we erased using the last
|
||||
// element in the slot list.
|
||||
if (map->occupied >= 3 /*Ignoring sentinel, at least 2 other elements to unstable erase*/) {
|
||||
// NOTE: Copy in last slot to the erase slot
|
||||
Dqn_DSMapSlot<T> *last_slot = map->slots + map->occupied - 1;
|
||||
map->slots[slot_index] = *last_slot;
|
||||
uint32_t last_index = map->occupied - 1;
|
||||
if (last_index != slot_index) {
|
||||
// NOTE: Copy in last slot to the erase slot
|
||||
Dqn_DSMapSlot<T> *last_slot = map->slots + last_index;
|
||||
map->slots[slot_index] = *last_slot;
|
||||
|
||||
// NOTE: Update the hash-to-slot mapping for the value that was copied in
|
||||
uint32_t hash_to_slot_index = Dqn_DSMap_HashToSlotIndex(map, last_slot->key);
|
||||
map->hash_to_slot[hash_to_slot_index] = slot_index;
|
||||
*last_slot = {}; // TODO: Optional?
|
||||
// NOTE: Update the hash-to-slot mapping for the value that was copied in
|
||||
uint32_t hash_to_slot_index = Dqn_DSMap_HashToSlotIndex(map, last_slot->key);
|
||||
map->hash_to_slot[hash_to_slot_index] = slot_index;
|
||||
*last_slot = {}; // TODO: Optional?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1289,53 +1295,52 @@ bool Dqn_DSMap_Erase(Dqn_DSMap<T> *map, Dqn_DSMapKey key)
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DQN_API Dqn_DSMapKey Dqn_DSMap_KeyBuffer(Dqn_DSMap<T> const *map, void const *data, uint32_t size)
|
||||
{
|
||||
Dqn_DSMapKey result = {};
|
||||
result.type = Dqn_DSMapKeyType_Buffer;
|
||||
result.payload.buffer.data = data;
|
||||
result.payload.buffer.size = size;
|
||||
result.hash = Dqn_DSMap_Hash(map, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64(Dqn_DSMap<T> const *map, uint64_t u64)
|
||||
Dqn_DSMapKey Dqn_DSMap_KeyBuffer(Dqn_DSMap<T> const *map, void const *data, Dqn_usize size)
|
||||
{
|
||||
DQN_ASSERT(size > 0 && size <= UINT32_MAX);
|
||||
Dqn_DSMapKey result = {};
|
||||
result.type = Dqn_DSMapKeyType_U64;
|
||||
result.payload.u64 = u64;
|
||||
result.type = Dqn_DSMapKeyType_Buffer;
|
||||
result.buffer_data = data;
|
||||
result.buffer_size = DQN_CAST(uint32_t) size;
|
||||
result.hash = Dqn_DSMap_Hash(map, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DQN_API Dqn_DSMapKey Dqn_DSMap_KeyStr8(Dqn_DSMap<T> const *map, Dqn_Str8 string)
|
||||
Dqn_DSMapKey Dqn_DSMap_KeyBufferAsU64NoHash(Dqn_DSMap<T> const *map, void const *data, uint32_t size)
|
||||
{
|
||||
DQN_ASSERT(string.size > 0 && string.size <= UINT32_MAX);
|
||||
Dqn_DSMapKey result = {};
|
||||
result.type = Dqn_DSMapKeyType_Buffer;
|
||||
result.payload.buffer.data = string.data;
|
||||
result.payload.buffer.size = DQN_CAST(uint32_t)string.size;
|
||||
result.hash = Dqn_DSMap_Hash(map, result);
|
||||
Dqn_DSMapKey result = {};
|
||||
result.type = Dqn_DSMapKeyType_BufferAsU64NoHash;
|
||||
result.buffer_data = data;
|
||||
result.buffer_size = DQN_CAST(uint32_t) size;
|
||||
DQN_ASSERT(size >= sizeof(result.hash));
|
||||
DQN_MEMCPY(&result.hash, data, sizeof(result.hash));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DQN_API Dqn_DSMapKey Dqn_DSMap_KeyStr8Copy(Dqn_DSMap<T> const *map, Dqn_Arena *arena, Dqn_Str8 string)
|
||||
Dqn_DSMapKey Dqn_DSMap_KeyU64(Dqn_DSMap<T> const *map, uint64_t u64)
|
||||
{
|
||||
Dqn_Str8 copy = Dqn_Str8_Copy(arena, string);
|
||||
Dqn_DSMapKey result = Dqn_DSMap_KeyStr8(map, copy);
|
||||
Dqn_DSMapKey result = {};
|
||||
result.type = Dqn_DSMapKeyType_U64;
|
||||
result.u64 = u64;
|
||||
result.hash = Dqn_DSMap_Hash(map, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Dqn_DSMapKey Dqn_DSMap_KeyStr8(Dqn_DSMap<T> const *map, Dqn_Str8 string)
|
||||
{
|
||||
Dqn_DSMapKey result = Dqn_DSMap_KeyBuffer(map, string.data, string.size);
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DQN_NO_DSMAP)
|
||||
|
||||
#if !defined(DQN_NO_LIST)
|
||||
// NOTE: [$LIST] Dqn_List //////////////////////////////////////////////////////////////////////////
|
||||
template <typename T> Dqn_List<T> Dqn_List_Init(Dqn_Arena *arena, Dqn_usize chunk_size)
|
||||
template <typename T> Dqn_List<T> Dqn_List_Init(Dqn_usize chunk_size)
|
||||
{
|
||||
Dqn_List<T> result = {};
|
||||
result.arena = arena;
|
||||
result.chunk_size = chunk_size;
|
||||
return result;
|
||||
}
|
||||
@ -1356,31 +1361,68 @@ template <typename T> Dqn_List<T> Dqn_List_InitSliceCopy(Dqn_Arena *arena, Dqn_u
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T> DQN_API T *Dqn_List_Make(Dqn_List<T> *list, Dqn_usize count)
|
||||
template <typename T> DQN_API bool Dqn_List_AttachTail_(Dqn_List<T> *list, Dqn_ListChunk<T> *tail)
|
||||
{
|
||||
if (!tail)
|
||||
return false;
|
||||
|
||||
if (list->tail) {
|
||||
list->tail->next = tail;
|
||||
tail->prev = list->tail;
|
||||
}
|
||||
|
||||
list->tail = tail;
|
||||
|
||||
if (!list->head)
|
||||
list->head = list->tail;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T> DQN_API Dqn_ListChunk<T> *Dqn_List_AllocArena_(Dqn_List<T> *list, Dqn_Arena *arena, Dqn_usize count)
|
||||
{
|
||||
auto *result = Dqn_Arena_New(arena, Dqn_ListChunk<T>, Dqn_ZeroMem_Yes);
|
||||
Dqn_ArenaTempMem tmem = Dqn_Arena_TempMemBegin(arena);
|
||||
if (!result)
|
||||
return nullptr;
|
||||
|
||||
Dqn_usize items = DQN_MAX(list->chunk_size, count);
|
||||
result->data = Dqn_Arena_NewArray(arena, T, items, Dqn_ZeroMem_Yes);
|
||||
result->size = items;
|
||||
if (!result->data) {
|
||||
Dqn_Arena_TempMemEnd(tmem);
|
||||
result = nullptr;
|
||||
}
|
||||
|
||||
Dqn_List_AttachTail_(list, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T> DQN_API Dqn_ListChunk<T> *Dqn_List_AllocPool_(Dqn_List<T> *list, Dqn_ChunkPool *pool, Dqn_usize count)
|
||||
{
|
||||
auto *result = Dqn_ChunkPool_New(pool, Dqn_ListChunk<T>);
|
||||
if (!result)
|
||||
return nullptr;
|
||||
|
||||
Dqn_usize items = DQN_MAX(list->chunk_size, count);
|
||||
result->data = Dqn_ChunkPool_NewArray(pool, T, items);
|
||||
result->size = items;
|
||||
if (!result->data) {
|
||||
Dqn_ChunkPool_Dealloc(result);
|
||||
result = nullptr;
|
||||
}
|
||||
|
||||
Dqn_List_AttachTail_(list, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T> DQN_API T *Dqn_List_MakeArena(Dqn_List<T> *list, Dqn_Arena *arena, Dqn_usize count)
|
||||
{
|
||||
if (list->chunk_size == 0)
|
||||
list->chunk_size = 128;
|
||||
|
||||
if (!list->tail || (list->tail->count + count) > list->tail->size) {
|
||||
auto *tail = Dqn_Arena_New(list->arena, Dqn_ListChunk<T>, Dqn_ZeroMem_Yes);
|
||||
if (!tail)
|
||||
return nullptr;
|
||||
|
||||
Dqn_usize items = DQN_MAX(list->chunk_size, count);
|
||||
tail->data = Dqn_Arena_NewArray(list->arena, T, items, Dqn_ZeroMem_Yes);
|
||||
tail->size = items;
|
||||
|
||||
if (!tail->data)
|
||||
if (!Dqn_List_AllocArena_(list, arena, count))
|
||||
return nullptr;
|
||||
|
||||
if (list->tail) {
|
||||
list->tail->next = tail;
|
||||
tail->prev = list->tail;
|
||||
}
|
||||
|
||||
list->tail = tail;
|
||||
|
||||
if (!list->head)
|
||||
list->head = list->tail;
|
||||
}
|
||||
|
||||
T *result = list->tail->data + list->tail->count;
|
||||
@ -1389,9 +1431,32 @@ template <typename T> DQN_API T *Dqn_List_Make(Dqn_List<T> *list, Dqn_usize coun
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T> DQN_API T *Dqn_List_Add(Dqn_List<T> *list, T const &value)
|
||||
template <typename T> DQN_API T *Dqn_List_MakePool(Dqn_List<T> *list, Dqn_ChunkPool *pool, Dqn_usize count)
|
||||
{
|
||||
T *result = Dqn_List_Make(list, 1);
|
||||
if (list->chunk_size == 0)
|
||||
list->chunk_size = 128;
|
||||
|
||||
if (!list->tail || (list->tail->count + count) > list->tail->size) {
|
||||
if (!Dqn_List_AllocPool_(list, pool, count))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
T *result = list->tail->data + list->tail->count;
|
||||
list->tail->count += count;
|
||||
list->count += count;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T> DQN_API T *Dqn_List_AddArena(Dqn_List<T> *list, Dqn_Arena *arena, T const &value)
|
||||
{
|
||||
T *result = Dqn_List_MakeArena(list, arena, 1);
|
||||
*result = value;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T> DQN_API T *Dqn_List_AddPool(Dqn_List<T> *list, Dqn_ChunkPool *pool, T const &value)
|
||||
{
|
||||
T *result = Dqn_List_MakePool(list, pool, 1);
|
||||
*result = value;
|
||||
return result;
|
||||
}
|
||||
@ -1407,14 +1472,22 @@ template <typename T, size_t N> DQN_API bool Dqn_List_AddCArray(Dqn_List<T> *lis
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T> DQN_API void Dqn_List_AddList(Dqn_List<T> *list, Dqn_List<T> other)
|
||||
template <typename T> DQN_API void Dqn_List_AddListArena(Dqn_List<T> *list, Dqn_Arena *arena, Dqn_List<T> other)
|
||||
{
|
||||
if (!list || list->chunk_size <= 0)
|
||||
return;
|
||||
|
||||
// TODO(doyle): Copy chunk by chunk
|
||||
for (Dqn_ListIterator<Dqn_Str8> it = {}; Dqn_List_Iterate(&other, &it, 0 /*start_index*/); )
|
||||
Dqn_List_Add(list, *it.data);
|
||||
for (Dqn_ListIterator<T> it = {}; Dqn_List_Iterate(&other, &it, 0 /*start_index*/); )
|
||||
Dqn_List_AddArena(list, arena, *it.data);
|
||||
}
|
||||
|
||||
template <typename T> DQN_API void Dqn_List_AddListPool(Dqn_List<T> *list, Dqn_ChunkPool *pool, Dqn_List<T> other)
|
||||
{
|
||||
if (!list || list->chunk_size <= 0)
|
||||
return;
|
||||
// TODO(doyle): Copy chunk by chunk
|
||||
for (Dqn_ListIterator<T> it = {}; Dqn_List_Iterate(&other, &it, 0 /*start_index*/); )
|
||||
Dqn_List_AddPool(list, pool, *it.data);
|
||||
}
|
||||
|
||||
template <typename T> DQN_API bool Dqn_List_Iterate(Dqn_List<T> *list, Dqn_ListIterator<T> *it, Dqn_usize start_index)
|
||||
@ -1497,8 +1570,11 @@ template <typename T> Dqn_Slice<T> Dqn_List_ToSliceCopy(Dqn_List<T> const *list,
|
||||
Dqn_Slice<T> result = Dqn_Slice_Alloc<T>(arena, list->count, Dqn_ZeroMem_No);
|
||||
if (result.size) {
|
||||
Dqn_usize slice_index = 0;
|
||||
DQN_MSVC_WARNING_PUSH
|
||||
DQN_MSVC_WARNING_DISABLE(6011) // Dereferencing NULL pointer 'x'
|
||||
for (Dqn_ListIterator<T> it = {}; Dqn_List_Iterate<T>(DQN_CAST(Dqn_List<T> *)list, &it, 0);)
|
||||
result.data[slice_index++] = *it.data;
|
||||
DQN_MSVC_WARNING_POP
|
||||
DQN_ASSERT(slice_index == result.size);
|
||||
}
|
||||
return result;
|
||||
|
@ -67,8 +67,8 @@ DQN_API Dqn_StackTraceWalkResult Dqn_StackTrace_Walk(Dqn_Arena *arena, uint16_t
|
||||
g_dqn_library->win32_sym_initialised = true;
|
||||
SymSetOptions(SYMOPT_LOAD_LINES);
|
||||
if (!SymInitialize(result.process, nullptr /*UserSearchPath*/, true /*fInvadeProcess*/)) {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_WinError error = Dqn_Win_LastError(scratch.arena);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
|
||||
Dqn_WinError error = Dqn_Win_LastError(tmem.arena);
|
||||
Dqn_Log_ErrorF("SymInitialize failed, stack trace can not be generated (%lu): %.*s\n", error.code, DQN_STR_FMT(error.msg));
|
||||
}
|
||||
}
|
||||
@ -115,8 +115,8 @@ DQN_API Dqn_StackTraceWalkResult Dqn_StackTrace_Walk(Dqn_Arena *arena, uint16_t
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_StackTrace_WalkStr8CRT(uint16_t limit, uint16_t skip)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_StackTraceWalkResult walk_result = Dqn_StackTrace_Walk(scratch.arena, limit);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_StackTraceWalkResult walk_result = Dqn_StackTrace_Walk(tmem.arena, limit);
|
||||
Dqn_Str8 result = Dqn_StackTrace_WalkResultStr8CRT(&walk_result, skip);
|
||||
return result;
|
||||
}
|
||||
@ -128,7 +128,7 @@ static void Dqn_StackTrace_AddWalkToStr8Builder_(Dqn_StackTraceWalkResult const
|
||||
for (Dqn_usize index = skip; index < walk->size; index++) {
|
||||
raw_frame.base_addr = walk->base_addr[index];
|
||||
Dqn_StackTraceFrame frame = Dqn_StackTrace_RawFrameToFrame(builder->arena, raw_frame);
|
||||
Dqn_Str8Builder_AppendF(builder, "%.*s(%zu): %.*s%s", DQN_STR_FMT(frame.file_name), frame.line_number, DQN_STR_FMT(frame.function_name), (DQN_CAST(int)index == walk->size - 1) ? "" : "\n");
|
||||
Dqn_Str8Builder_AddF(builder, "%.*s(%zu): %.*s%s", DQN_STR_FMT(frame.file_name), frame.line_number, DQN_STR_FMT(frame.function_name), (DQN_CAST(int)index == walk->size - 1) ? "" : "\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,9 +167,8 @@ DQN_API Dqn_Str8 Dqn_StackTrace_WalkResultStr8(Dqn_Arena *arena, Dqn_StackTraceW
|
||||
if (!walk || !arena)
|
||||
return result;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = scratch.arena;
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
|
||||
Dqn_Str8Builder builder = Dqn_Str8Builder_Init(tmem.arena);
|
||||
Dqn_StackTrace_AddWalkToStr8Builder_(walk, &builder, skip);
|
||||
result = Dqn_Str8Builder_Build(&builder, arena);
|
||||
return result;
|
||||
@ -181,9 +180,8 @@ DQN_API Dqn_Str8 Dqn_StackTrace_WalkResultStr8CRT(Dqn_StackTraceWalkResult const
|
||||
if (!walk)
|
||||
return result;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = scratch.arena;
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8Builder builder = Dqn_Str8Builder_Init(tmem.arena);
|
||||
Dqn_StackTrace_AddWalkToStr8Builder_(walk, &builder, skip);
|
||||
result = Dqn_Str8Builder_BuildCRT(&builder);
|
||||
return result;
|
||||
@ -196,8 +194,8 @@ DQN_API Dqn_Slice<Dqn_StackTraceFrame> Dqn_StackTrace_GetFrames(Dqn_Arena *arena
|
||||
if (!arena)
|
||||
return result;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(scratch.arena, limit);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
|
||||
Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(tmem.arena, limit);
|
||||
if (!walk.size)
|
||||
return result;
|
||||
|
||||
@ -259,8 +257,8 @@ DQN_API Dqn_StackTraceFrame Dqn_StackTrace_RawFrameToFrame(Dqn_Arena *arena, Dqn
|
||||
|
||||
DQN_API void Dqn_StackTrace_Print(uint16_t limit)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Slice<Dqn_StackTraceFrame> stack_trace = Dqn_StackTrace_GetFrames(scratch.arena, limit);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Slice<Dqn_StackTraceFrame> stack_trace = Dqn_StackTrace_GetFrames(tmem.arena, limit);
|
||||
for (Dqn_StackTraceFrame &frame : stack_trace)
|
||||
Dqn_Print_ErrLnF("%.*s(%I64u): %.*s", DQN_STR_FMT(frame.file_name), frame.line_number, DQN_STR_FMT(frame.function_name));
|
||||
}
|
||||
|
138
dqn_docs.cpp
138
dqn_docs.cpp
@ -48,6 +48,14 @@ void Dqn_Docs_Demo()
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Dqn_BytesToHex ////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
unsigned char bytes[2] = {0xFA, 0xCE};
|
||||
Dqn_Str8 hex = Dqn_BytesToHex(tmem.arena, bytes, sizeof(bytes));
|
||||
DQN_ASSERT(hex == DQN_STR8("face")); // NOTE: Guaranteed to be null-terminated
|
||||
}
|
||||
|
||||
// NOTE: DQN_CHECK /////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Check the expression trapping in debug, whilst in release- trapping is
|
||||
@ -140,8 +148,8 @@ void Dqn_Docs_Demo()
|
||||
// A 'Deinit' of the map will similarly deallocate the passed in arena (as
|
||||
// the map takes ownership of the arena).
|
||||
Dqn_Arena arena = {};
|
||||
Dqn_DSMap<int> map = Dqn_DSMap_Init<int>(&arena, /*size*/ 1024); // Size must be PoT!
|
||||
DQN_ASSERT(Dqn_DSMap_IsValid(&map)); // Valid if no initialisation failure (e.g. mem alloc failure)
|
||||
Dqn_DSMap<int> map = Dqn_DSMap_Init<int>(&arena, /*size*/ 1024, Dqn_DSMapFlags_Nil); // Size must be PoT!
|
||||
DQN_ASSERT(Dqn_DSMap_IsValid(&map)); // Valid if no initialisation failure (e.g. mem alloc failure)
|
||||
|
||||
// NOTE: Dqn_DSMap_KeyCStringLit ///////////////////////////////////////////////////////////
|
||||
// NOTE: Dqn_DSMap_KeyU64 ///////////////////////////////////////////////////////////
|
||||
@ -204,7 +212,7 @@ void Dqn_Docs_Demo()
|
||||
int *it_value = &it->value;
|
||||
DQN_ASSERT(*it_value == 0xCAFE);
|
||||
|
||||
DQN_ASSERT(Dqn_Str8_Init(it_key.payload.buffer.data, it_key.payload.buffer.size) == DQN_STR8("Sample Key"));
|
||||
DQN_ASSERT(Dqn_Str8_Init(it_key.buffer_data, it_key.buffer_size) == DQN_STR8("Sample Key"));
|
||||
}
|
||||
|
||||
// NOTE: Dqn_DSMap_Erase ///////////////////////////////////////////////////////////////////
|
||||
@ -327,6 +335,15 @@ void Dqn_Docs_Demo()
|
||||
// The lifetime of the slice is bound to the lifetime of the FStr8 and is
|
||||
// invalidated when the FStr8 is.
|
||||
|
||||
// NOTE: Dqn_HexToBytes ////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
unsigned char bytes[2];
|
||||
Dqn_usize bytes_written = Dqn_HexToBytesPtr(DQN_STR8("0xFACE"), bytes, sizeof(bytes));
|
||||
DQN_ASSERT(bytes_written == 2);
|
||||
DQN_ASSERT(bytes[0] == 0xFA);
|
||||
DQN_ASSERT(bytes[1] == 0xCE);
|
||||
}
|
||||
|
||||
// NOTE: Dqn_JSONBuilder_Build /////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Convert the internal JSON buffer in the builder into a string.
|
||||
@ -371,8 +388,8 @@ void Dqn_Docs_Demo()
|
||||
|
||||
// NOTE: Dqn_List_Iterate //////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_List<int> list = Dqn_List_Init<int>(scratch.arena, /*chunk_size*/ 128);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_List<int> list = Dqn_List_Init<int>(/*chunk_size*/ 128);
|
||||
for (Dqn_ListIterator<int> it = {}; Dqn_List_Iterate(&list, &it, 0);) {
|
||||
int *item = it.data;
|
||||
(void)item;
|
||||
@ -450,7 +467,7 @@ void Dqn_Docs_Demo()
|
||||
// If 'tmp_path' is written to successfuly, the file will be copied over into
|
||||
// 'path'.
|
||||
if (0) {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_ErrorSink *error = Dqn_ErrorSink_Begin(Dqn_ErrorSinkMode_Nil);
|
||||
Dqn_OS_WriteAllSafe(/*path*/ DQN_STR8("C:/Home/my.txt"), /*buffer*/ DQN_STR8("Hello world"), error);
|
||||
Dqn_ErrorSink_EndAndLogErrorF(error, "");
|
||||
@ -550,12 +567,11 @@ void Dqn_Docs_Demo()
|
||||
// that can be converted into a duration.
|
||||
//
|
||||
// This profiler uses a double buffer scheme for storing profiling markers.
|
||||
// After an application's typical update/frame cycle you can swap the profiler's
|
||||
// buffer whereby the front buffer contains the previous frames profiling
|
||||
// metrics and the back buffer will be populated with the new frame's profiling
|
||||
// metrics.
|
||||
// After an application's typical update/frame cycle you can swap the
|
||||
// profiler's buffer whereby the front buffer contains the previous frames
|
||||
// profiling metrics and the back buffer will be populated with the new
|
||||
// frame's profiling metrics.
|
||||
{
|
||||
uint64_t tsc_per_seconds = Dqn_OS_EstimateTSCPerSecond(/*duration_ms_to_gauge_tsc_frequency*/ 100);
|
||||
enum Zone { Zone_MainLoop, Zone_Count };
|
||||
Dqn_ProfilerZone profiler_zone_main_update = Dqn_Profiler_BeginZone(Zone_MainLoop);
|
||||
|
||||
@ -573,22 +589,34 @@ void Dqn_Docs_Demo()
|
||||
// the front buffer which contain the metrics that you can visualise
|
||||
// regarding the most profiling metrics recorded.
|
||||
|
||||
Dqn_ProfilerAnchor *anchors = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Front);
|
||||
for (size_t index = 0; index < Zone_Count; index++) {
|
||||
Dqn_ProfilerAnchor *anchor = anchors + index;
|
||||
|
||||
// Print the result like so
|
||||
if (0) {
|
||||
printf("%.*s[%u] %" PRIu64 " cycles (%.1fms)\n",
|
||||
DQN_STR_FMT(anchor->name),
|
||||
anchor->hit_count,
|
||||
anchor->tsc_inclusive,
|
||||
anchor->tsc_inclusive * tsc_per_seconds * 1000.f);
|
||||
// NOTE: Dqn_Profiler_ReadBuffer ///////////////////////////////////////////////////////////
|
||||
//
|
||||
// Retrieve the buffer of anchors of which there are
|
||||
// `DQN_PROFILER_ANCHOR_BUFFER_SIZE` anchors from the most recent run
|
||||
// of the profiler after you have called `SwapAnchorBuffer` to trigger
|
||||
// the double buffer
|
||||
Dqn_ProfilerAnchor *read_anchors = Dqn_Profiler_ReadBuffer();
|
||||
for (Dqn_usize index = 0; index < DQN_PROFILER_ANCHOR_BUFFER_SIZE; index++) {
|
||||
Dqn_ProfilerAnchor *anchor = read_anchors + index;
|
||||
if (Dqn_Str8_HasData(anchor->name)) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Dqn_Profiler_WriteBuffer //////////////////////////////////////////////////////////
|
||||
//
|
||||
// Same as `ReadBuffer` however we return the buffer that the profiler
|
||||
// is currently writing anchors into.
|
||||
Dqn_ProfilerAnchor *write_anchors = Dqn_Profiler_WriteBuffer();
|
||||
for (Dqn_usize index = 0; index < DQN_PROFILER_ANCHOR_BUFFER_SIZE; index++) {
|
||||
Dqn_ProfilerAnchor *anchor = write_anchors + index;
|
||||
if (Dqn_Str8_HasData(anchor->name)) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
Dqn_Profiler_EndZone(profiler_zone_main_update);
|
||||
Dqn_Profiler_SwapAnchorBuffer(); // Should occur after all profiling zones are ended!
|
||||
|
||||
*g_dqn_library->profiler = {};
|
||||
}
|
||||
#endif // !defined(DQN_NO_PROFILER)
|
||||
@ -672,7 +700,7 @@ void Dqn_Docs_Demo()
|
||||
// the debug APIs are aware of how to resolve the new addresses imported
|
||||
// into the address space.
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
|
||||
// NOTE: Dqn_StackTrace_Walk ///////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -680,7 +708,7 @@ void Dqn_Docs_Demo()
|
||||
// functions on the call-stack at the current instruction pointer. The
|
||||
// addresses are stored in order from the current executing function
|
||||
// first to the most ancestor function last in the walk.
|
||||
Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(scratch.arena, /*depth limit*/ 128);
|
||||
Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(tmem.arena, /*depth limit*/ 128);
|
||||
|
||||
// Loop over the addresses produced in the stack trace
|
||||
for (Dqn_StackTraceWalkResultIterator it = {}; Dqn_StackTrace_WalkResultIterate(&it, &walk); ) {
|
||||
@ -689,7 +717,7 @@ void Dqn_Docs_Demo()
|
||||
//
|
||||
// Converts the base address into a human readable stack trace
|
||||
// entry (e.g. address, line number, file and function name).
|
||||
Dqn_StackTraceFrame frame = Dqn_StackTrace_RawFrameToFrame(scratch.arena, it.raw_frame);
|
||||
Dqn_StackTraceFrame frame = Dqn_StackTrace_RawFrameToFrame(tmem.arena, it.raw_frame);
|
||||
|
||||
// You may then print out the frame like so
|
||||
if (0)
|
||||
@ -706,7 +734,7 @@ void Dqn_Docs_Demo()
|
||||
// Helper function to create a stack trace and automatically convert the
|
||||
// raw frames into human readable frames. This function effectively
|
||||
// calls 'Walk' followed by 'RawFrameToFrame'.
|
||||
Dqn_Slice<Dqn_StackTraceFrame> frames = Dqn_StackTrace_GetFrames(scratch.arena, /*depth limit*/ 128);
|
||||
Dqn_Slice<Dqn_StackTraceFrame> frames = Dqn_StackTrace_GetFrames(tmem.arena, /*depth limit*/ 128);
|
||||
(void)frames;
|
||||
}
|
||||
|
||||
@ -719,8 +747,8 @@ void Dqn_Docs_Demo()
|
||||
// The returned string's 'size' member variable does *not* include this
|
||||
// additional null-terminating byte.
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 string = Dqn_Str8_Alloc(scratch.arena, /*size*/ 1, Dqn_ZeroMem_Yes);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 string = Dqn_Str8_Alloc(tmem.arena, /*size*/ 1, Dqn_ZeroMem_Yes);
|
||||
DQN_ASSERT(string.size == 1);
|
||||
DQN_ASSERT(string.data[string.size] == 0); // It is null-terminated!
|
||||
}
|
||||
@ -799,12 +827,12 @@ void Dqn_Docs_Demo()
|
||||
// always be a newly allocated copy, irrespective of if any replacements
|
||||
// were done or not.
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 string = Dqn_Str8_Replace(/*string*/ DQN_STR8("Foo Foo Bar"),
|
||||
/*find*/ DQN_STR8("Foo"),
|
||||
/*replace*/ DQN_STR8("Moo"),
|
||||
/*start_index*/ 1,
|
||||
/*arena*/ scratch.arena,
|
||||
/*arena*/ tmem.arena,
|
||||
/*eq_case*/ Dqn_Str8EqCase_Sensitive);
|
||||
DQN_ASSERT(string == DQN_STR8("Foo Moo Bar"));
|
||||
}
|
||||
@ -817,8 +845,8 @@ void Dqn_Docs_Demo()
|
||||
// Reverse segment delimits the string counting 'segment_size' from the back
|
||||
// of the string.
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 string = Dqn_Str8_Segment(scratch.arena, /*string*/ DQN_STR8("123456789"), /*segment_size*/ 3, /*segment_char*/ ',');
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 string = Dqn_Str8_Segment(tmem.arena, /*string*/ DQN_STR8("123456789"), /*segment_size*/ 3, /*segment_char*/ ',');
|
||||
DQN_ASSERT(string == DQN_STR8("123,456,789"));
|
||||
}
|
||||
|
||||
@ -826,9 +854,9 @@ void Dqn_Docs_Demo()
|
||||
{
|
||||
// Splits the string at each delimiter into substrings occuring prior and
|
||||
// after until the next delimiter.
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
{
|
||||
Dqn_Slice<Dqn_Str8> splits = Dqn_Str8_SplitAlloc(/*arena*/ scratch.arena,
|
||||
Dqn_Slice<Dqn_Str8> splits = Dqn_Str8_SplitAlloc(/*arena*/ tmem.arena,
|
||||
/*string*/ DQN_STR8("192.168.8.1"),
|
||||
/*delimiter*/ DQN_STR8("."),
|
||||
/*mode*/ Dqn_Str8SplitIncludeEmptyStrings_No);
|
||||
@ -839,7 +867,7 @@ void Dqn_Docs_Demo()
|
||||
// You can include empty strings that occur when splitting by setting
|
||||
// the split mode to include empty strings.
|
||||
{
|
||||
Dqn_Slice<Dqn_Str8> splits = Dqn_Str8_SplitAlloc(/*arena*/ scratch.arena,
|
||||
Dqn_Slice<Dqn_Str8> splits = Dqn_Str8_SplitAlloc(/*arena*/ tmem.arena,
|
||||
/*string*/ DQN_STR8("a--b"),
|
||||
/*delimiter*/ DQN_STR8("-"),
|
||||
/*mode*/ Dqn_Str8SplitIncludeEmptyStrings_Yes);
|
||||
@ -953,7 +981,7 @@ void Dqn_Docs_Demo()
|
||||
|
||||
// NOTE: Dqn_ThreadContext /////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Each thread is assigned in their thread-local storage (TLS) scratch and
|
||||
// Each thread is assigned in their thread-local storage (TLS) tmem and
|
||||
// permanent arena allocators. These can be used for allocations with a
|
||||
// lifetime scoped to the lexical scope or for storing data permanently
|
||||
// using the arena paradigm.
|
||||
@ -961,40 +989,40 @@ void Dqn_Docs_Demo()
|
||||
// TLS in this implementation is implemented using the `thread_local` C/C++
|
||||
// keyword.
|
||||
//
|
||||
// 99% of the time you will want Dqn_Scratch_Get(...) which returns you a
|
||||
// 99% of the time you will want Dqn_TLS_TMem(...) which returns you a
|
||||
// temporary arena for function lifetime allocations. On scope exit, the
|
||||
// arena is cleared out.
|
||||
//
|
||||
// This library's paradigm revolves heavily around arenas including scratch
|
||||
// This library's paradigm revolves heavily around arenas including tmem
|
||||
// arenas into child functions for temporary calculations. If an arena is
|
||||
// passed into a function, this poses a problem sometimes known as
|
||||
// 'arena aliasing'.
|
||||
//
|
||||
// If an arena aliases another arena (e.g. the arena passed in) is the same
|
||||
// as the scratch arena requested in the function, we risk the scratch arena
|
||||
// as the tmem arena requested in the function, we risk the tmem arena
|
||||
// on scope exit deallocating memory belonging to the caller.
|
||||
//
|
||||
// To avoid this we the 'Dqn_Scratch_Get(...)' API takes in a list of arenas
|
||||
// to ensure that we provide a scratch arena that *won't* alias with the
|
||||
// To avoid this we the 'Dqn_TLS_TMem(...)' API takes in a list of arenas
|
||||
// to ensure that we provide a tmem arena that *won't* alias with the
|
||||
// caller's arena. If arena aliasing occurs, with ASAN on, generally
|
||||
// the library will trap and report use-after-poison once violated.
|
||||
{
|
||||
Dqn_Scratch scratch_a = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_TLSTMem tmem_a = Dqn_TLS_TMem(nullptr);
|
||||
|
||||
// Now imagine we call a function where we pass scratch_a.arena down
|
||||
// into it .. If we call scratch again, we need to pass in the arena
|
||||
// Now imagine we call a function where we pass tmem_a.arena down
|
||||
// into it .. If we call tmem again, we need to pass in the arena
|
||||
// to prevent aliasing.
|
||||
Dqn_Scratch scratch_b = Dqn_Scratch_Get(scratch_a.arena);
|
||||
DQN_ASSERT(scratch_a.arena != scratch_b.arena);
|
||||
Dqn_TLSTMem tmem_b = Dqn_TLS_TMem(tmem_a.arena);
|
||||
DQN_ASSERT(tmem_a.arena != tmem_b.arena);
|
||||
}
|
||||
|
||||
// @proc Dqn_Thread_GetScratch
|
||||
// @proc Dqn_Thread_GetTMem
|
||||
// @desc Retrieve the per-thread temporary arena allocator that is reset on scope
|
||||
// exit.
|
||||
|
||||
// The scratch arena must be deconflicted with any existing arenas in the
|
||||
// The tmem arena must be deconflicted with any existing arenas in the
|
||||
// function to avoid trampling over each other's memory. Consider the situation
|
||||
// where the scratch arena is passed into the function. Inside the function, if
|
||||
// where the tmem arena is passed into the function. Inside the function, if
|
||||
// the same arena is reused then, if both arenas allocate, when the inner arena
|
||||
// is reset, this will undo the passed in arena's allocations in the function.
|
||||
|
||||
@ -1010,8 +1038,8 @@ void Dqn_Docs_Demo()
|
||||
|
||||
// NOTE: Dqn_U64ToAge //////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 string = Dqn_U64ToAge(scratch.arena, DQN_HOURS_TO_S(2) + DQN_MINS_TO_S(30), Dqn_U64AgeUnit_All);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 string = Dqn_U64ToAge(tmem.arena, DQN_HOURS_TO_S(2) + DQN_MINS_TO_S(30), Dqn_U64AgeUnit_All);
|
||||
if (0) // Prints "2hr 30m"
|
||||
printf("%.*s", DQN_STR_FMT(string));
|
||||
}
|
||||
@ -1131,12 +1159,12 @@ void Dqn_Docs_Demo()
|
||||
if (0) {
|
||||
// Generate the error string for the last Win32 API called that return
|
||||
// an error value.
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_WinError get_last_error = Dqn_Win_LastError(scratch.arena);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_WinError get_last_error = Dqn_Win_LastError(tmem.arena);
|
||||
printf("Error (%lu): %.*s", get_last_error.code, DQN_STR_FMT(get_last_error.msg));
|
||||
|
||||
// Alternatively, pass in the error code directly
|
||||
Dqn_WinError error_msg_for_code = Dqn_Win_ErrorCodeToMsg(scratch.arena, /*error_code*/ 0);
|
||||
Dqn_WinError error_msg_for_code = Dqn_Win_ErrorCodeToMsg(tmem.arena, /*error_code*/ 0);
|
||||
printf("Error (%lu): %.*s", error_msg_for_code.code, DQN_STR_FMT(error_msg_for_code.msg));
|
||||
}
|
||||
|
||||
|
219
dqn_helpers.cpp
219
dqn_helpers.cpp
@ -143,17 +143,16 @@ DQN_API void Dqn_JSONBuilder_KeyValue(Dqn_JSONBuilder *builder, Dqn_Str8 key, Dq
|
||||
int spaces = builder->indent_level * spaces_per_indent;
|
||||
|
||||
if (key.size) {
|
||||
Dqn_Str8Builder_AppendF(&builder->string_builder,
|
||||
"%.*s%*c\"%.*s\": %.*s",
|
||||
prefix_size,
|
||||
prefix,
|
||||
spaces,
|
||||
' ',
|
||||
DQN_STR_FMT(key),
|
||||
DQN_STR_FMT(value));
|
||||
Dqn_Str8Builder_AddF(&builder->string_builder,
|
||||
"%.*s%*c\"%.*s\": %.*s",
|
||||
prefix_size,
|
||||
prefix,
|
||||
spaces,
|
||||
' ',
|
||||
DQN_STR_FMT(key),
|
||||
DQN_STR_FMT(value));
|
||||
} else {
|
||||
Dqn_Str8Builder_AppendF(
|
||||
&builder->string_builder, "%.*s%*c%.*s", prefix_size, prefix, spaces, ' ', DQN_STR_FMT(value));
|
||||
Dqn_Str8Builder_AddF(&builder->string_builder, "%.*s%*c%.*s", prefix_size, prefix, spaces, ' ', DQN_STR_FMT(value));
|
||||
}
|
||||
|
||||
if (item == Dqn_JSONBuilderItem_OpenContainer)
|
||||
@ -164,8 +163,8 @@ DQN_API void Dqn_JSONBuilder_KeyValue(Dqn_JSONBuilder *builder, Dqn_Str8 key, Dq
|
||||
|
||||
DQN_API void Dqn_JSONBuilder_KeyValueFV(Dqn_JSONBuilder *builder, Dqn_Str8 key, char const *value_fmt, va_list args)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(builder->string_builder.arena);
|
||||
Dqn_Str8 value = Dqn_Str8_InitFV(scratch.arena, value_fmt, args);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(builder->string_builder.arena);
|
||||
Dqn_Str8 value = Dqn_Str8_InitFV(tmem.arena, value_fmt, args);
|
||||
Dqn_JSONBuilder_KeyValue(builder, key, value);
|
||||
}
|
||||
|
||||
@ -765,55 +764,55 @@ DQN_API Dqn_Str8 Dqn_U64ByteSizeTypeString(Dqn_U64ByteSizeType type)
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_U64ToAge(Dqn_Arena *arena, uint64_t age_s, Dqn_usize type)
|
||||
DQN_API Dqn_Str8 Dqn_U64ToAge(Dqn_Arena *arena, uint64_t age_s, Dqn_U64AgeUnit unit)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = arena;
|
||||
uint64_t remainder = age_s;
|
||||
|
||||
if (type & Dqn_U64AgeUnit_Year) {
|
||||
Dqn_usize unit = remainder / DQN_YEARS_TO_S(1);
|
||||
remainder -= DQN_YEARS_TO_S(unit);
|
||||
if (unit)
|
||||
Dqn_Str8Builder_AppendF(&builder, "%s%I64uyr", builder.string_size ? " " : "", unit);
|
||||
if (unit & Dqn_U64AgeUnit_Year) {
|
||||
Dqn_usize value = remainder / DQN_YEARS_TO_S(1);
|
||||
remainder -= DQN_YEARS_TO_S(value);
|
||||
if (value)
|
||||
Dqn_Str8Builder_AddF(&builder, "%s%I64uyr", builder.string_size ? " " : "", value);
|
||||
}
|
||||
|
||||
if (type & Dqn_U64AgeUnit_Week) {
|
||||
Dqn_usize unit = remainder / DQN_WEEKS_TO_S(1);
|
||||
remainder -= DQN_WEEKS_TO_S(unit);
|
||||
if (unit)
|
||||
Dqn_Str8Builder_AppendF(&builder, "%s%I64uw", builder.string_size ? " " : "", unit);
|
||||
if (unit & Dqn_U64AgeUnit_Week) {
|
||||
Dqn_usize value = remainder / DQN_WEEKS_TO_S(1);
|
||||
remainder -= DQN_WEEKS_TO_S(value);
|
||||
if (value)
|
||||
Dqn_Str8Builder_AddF(&builder, "%s%I64uw", builder.string_size ? " " : "", value);
|
||||
}
|
||||
|
||||
if (type & Dqn_U64AgeUnit_Day) {
|
||||
Dqn_usize unit = remainder / DQN_DAYS_TO_S(1);
|
||||
remainder -= DQN_DAYS_TO_S(unit);
|
||||
if (unit)
|
||||
Dqn_Str8Builder_AppendF(&builder, "%s%I64ud", builder.string_size ? " " : "", unit);
|
||||
if (unit & Dqn_U64AgeUnit_Day) {
|
||||
Dqn_usize value = remainder / DQN_DAYS_TO_S(1);
|
||||
remainder -= DQN_DAYS_TO_S(value);
|
||||
if (value)
|
||||
Dqn_Str8Builder_AddF(&builder, "%s%I64ud", builder.string_size ? " " : "", value);
|
||||
}
|
||||
|
||||
if (type & Dqn_U64AgeUnit_Hr) {
|
||||
Dqn_usize unit = remainder / DQN_HOURS_TO_S(1);
|
||||
remainder -= DQN_HOURS_TO_S(unit);
|
||||
if (unit)
|
||||
Dqn_Str8Builder_AppendF(&builder, "%s%I64uh", builder.string_size ? " " : "", unit);
|
||||
if (unit & Dqn_U64AgeUnit_Hr) {
|
||||
Dqn_usize value = remainder / DQN_HOURS_TO_S(1);
|
||||
remainder -= DQN_HOURS_TO_S(value);
|
||||
if (value)
|
||||
Dqn_Str8Builder_AddF(&builder, "%s%I64uh", builder.string_size ? " " : "", value);
|
||||
}
|
||||
|
||||
if (type & Dqn_U64AgeUnit_Min) {
|
||||
Dqn_usize unit = remainder / DQN_MINS_TO_S(1);
|
||||
remainder -= DQN_MINS_TO_S(unit);
|
||||
if (unit)
|
||||
Dqn_Str8Builder_AppendF(&builder, "%s%I64um", builder.string_size ? " " : "", unit);
|
||||
if (unit & Dqn_U64AgeUnit_Min) {
|
||||
Dqn_usize value = remainder / DQN_MINS_TO_S(1);
|
||||
remainder -= DQN_MINS_TO_S(value);
|
||||
if (value)
|
||||
Dqn_Str8Builder_AddF(&builder, "%s%I64um", builder.string_size ? " " : "", value);
|
||||
}
|
||||
|
||||
if (type & Dqn_U64AgeUnit_Sec) {
|
||||
Dqn_usize unit = remainder;
|
||||
Dqn_Str8Builder_AppendF(&builder, "%s%I64us", builder.string_size ? " " : "", unit);
|
||||
if (unit & Dqn_U64AgeUnit_Sec) {
|
||||
Dqn_usize value = remainder;
|
||||
Dqn_Str8Builder_AddF(&builder, "%s%I64us", builder.string_size ? " " : "", value);
|
||||
}
|
||||
|
||||
result = Dqn_Str8Builder_Build(&builder, arena);
|
||||
@ -841,10 +840,10 @@ DQN_API uint64_t Dqn_HexToU64(Dqn_Str8 hex)
|
||||
DQN_API Dqn_Str8 Dqn_U64ToHex(Dqn_Arena *arena, uint64_t number, uint32_t flags)
|
||||
{
|
||||
Dqn_Str8 prefix = {};
|
||||
if (!(flags & Dqn_BinHexU64Str8Flags_No0xPrefix))
|
||||
if ((flags & Dqn_HexU64Str8Flags_0xPrefix))
|
||||
prefix = DQN_STR8("0x");
|
||||
|
||||
char const *fmt = (flags & Dqn_BinHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
|
||||
char const *fmt = (flags & Dqn_HexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
|
||||
Dqn_usize required_size = Dqn_CStr8_FSize(fmt, number) + prefix.size;
|
||||
Dqn_Str8 result = Dqn_Str8_Alloc(arena, required_size, Dqn_ZeroMem_No);
|
||||
|
||||
@ -859,14 +858,14 @@ DQN_API Dqn_Str8 Dqn_U64ToHex(Dqn_Arena *arena, uint64_t number, uint32_t flags)
|
||||
DQN_API Dqn_U64HexStr8 Dqn_U64ToHexStr8(uint64_t number, uint32_t flags)
|
||||
{
|
||||
Dqn_Str8 prefix = {};
|
||||
if (!(flags & Dqn_BinHexU64Str8Flags_No0xPrefix))
|
||||
if (flags & Dqn_HexU64Str8Flags_0xPrefix)
|
||||
prefix = DQN_STR8("0x");
|
||||
|
||||
Dqn_U64HexStr8 result = {};
|
||||
DQN_MEMCPY(result.data, prefix.data, prefix.size);
|
||||
result.size += DQN_CAST(int8_t) prefix.size;
|
||||
|
||||
char const *fmt = (flags & Dqn_BinHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
|
||||
char const *fmt = (flags & Dqn_HexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
|
||||
int size = DQN_SNPRINTF(result.data + result.size, DQN_ARRAY_UCOUNT(result.data) - result.size, fmt, number);
|
||||
result.size += DQN_CAST(uint8_t) size;
|
||||
DQN_ASSERT(result.size < DQN_ARRAY_UCOUNT(result.data));
|
||||
@ -905,9 +904,9 @@ DQN_API Dqn_Str8 Dqn_BytesToHex(Dqn_Arena *arena, void const *src, Dqn_usize siz
|
||||
if (!src || size <= 0)
|
||||
return result;
|
||||
|
||||
result = Dqn_Str8_Alloc(arena, size * 2, Dqn_ZeroMem_No);
|
||||
result.data[result.size - 1] = 0;
|
||||
bool converted = Dqn_BytesToHexPtr(src, size, result.data, result.size);
|
||||
result = Dqn_Str8_Alloc(arena, size * 2, Dqn_ZeroMem_No);
|
||||
result.data[result.size] = 0;
|
||||
bool converted = Dqn_BytesToHexPtr(src, size, result.data, result.size);
|
||||
DQN_ASSERT(converted);
|
||||
return result;
|
||||
}
|
||||
@ -1029,10 +1028,10 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
|
||||
// NOTE: Setup the allocation table with allocation tracking turned off on
|
||||
// the arena we're using to initialise the table.
|
||||
result->alloc_table_arena.flags |= Dqn_ArenaFlag_NoAllocTrack;
|
||||
result->alloc_table = Dqn_DSMap_Init<Dqn_DebugAlloc>(&result->alloc_table_arena, 4096);
|
||||
result->alloc_table = Dqn_DSMap_Init<Dqn_DebugAlloc>(&result->alloc_table_arena, 4096, Dqn_DSMapFlags_Nil);
|
||||
#endif
|
||||
|
||||
result->arena = Dqn_Arena_InitSize(0, 0, Dqn_ArenaFlag_AllocCanLeak);
|
||||
result->arena = Dqn_Arena_InitSize(DQN_MEGABYTES(2), DQN_KILOBYTES(64), Dqn_ArenaFlag_AllocCanLeak);
|
||||
result->pool = Dqn_ChunkPool_Init(&result->arena, /*align*/ 0);
|
||||
Dqn_ArenaCatalog_Init(&result->arena_catalog, &result->pool);
|
||||
Dqn_ArenaCatalog_AddF(&result->arena_catalog, &result->arena, "Dqn Library");
|
||||
@ -1041,47 +1040,49 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
|
||||
Dqn_ArenaCatalog_AddF(&result->arena_catalog, &result->alloc_table_arena, "Dqn Allocation Table");
|
||||
#endif
|
||||
|
||||
// NOTE: Initialise scratch arenas which allocate memory and will be
|
||||
// NOTE: Initialise tmem arenas which allocate memory and will be
|
||||
// recorded to the now initialised allocation table. The initialisation
|
||||
// of scratch memory may request scratch memory itself in leak tracing mode.
|
||||
// This is supported as the scratch arenas defer allocation tracking until
|
||||
// of tmem memory may request tmem memory itself in leak tracing mode.
|
||||
// This is supported as the tmem arenas defer allocation tracking until
|
||||
// initialisation is done.
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_TLS_Init(&result->tls);
|
||||
Dqn_OS_ThreadSetTLS(&result->tls);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
// NOTE: END IMPORTANT ORDER OF STATEMENTS /////////////////////////////////////////////////////
|
||||
|
||||
result->exe_dir = Dqn_OS_EXEDir(&result->arena);
|
||||
|
||||
// NOTE: Print out init features ///////////////////////////////////////////////////////////////
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = scratch.arena;
|
||||
builder.arena = tmem.arena;
|
||||
if (on_init & Dqn_LibraryOnInit_LogLibFeatures) {
|
||||
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8("Dqn Library initialised:\n"));
|
||||
Dqn_Str8Builder_AddRef(&builder, DQN_STR8("Dqn Library initialised:\n"));
|
||||
|
||||
Dqn_f64 page_size_kib = result->os_page_size / 1024.0;
|
||||
Dqn_f64 alloc_granularity_kib = result->os_alloc_granularity / 1024.0;
|
||||
Dqn_Str8Builder_AppendF(
|
||||
Dqn_Str8Builder_AddF(
|
||||
&builder, " OS Page Size/Alloc Granularity: %.1f/%.1fKiB\n", page_size_kib, alloc_granularity_kib);
|
||||
|
||||
#if DQN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
if (DQN_ASAN_POISON) {
|
||||
Dqn_Str8Builder_AppendF(
|
||||
Dqn_Str8Builder_AddF(
|
||||
&builder, " ASAN manual poisoning%s\n", DQN_ASAN_VET_POISON ? " (+vet sanity checks)" : "");
|
||||
Dqn_Str8Builder_AppendF(&builder, " ASAN poison guard size: %u\n", DQN_ASAN_POISON_GUARD_SIZE);
|
||||
Dqn_Str8Builder_AddF(&builder, " ASAN poison guard size: %u\n", DQN_ASAN_POISON_GUARD_SIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(DQN_LEAK_TRACKING)
|
||||
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8(" Allocation leak tracing\n"));
|
||||
Dqn_Str8Builder_AddRef(&builder, DQN_STR8(" Allocation leak tracing\n"));
|
||||
#endif
|
||||
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8(" TSC profiler available\n"));
|
||||
Dqn_Str8Builder_AddRef(&builder, DQN_STR8(" TSC profiler available\n"));
|
||||
#endif
|
||||
|
||||
#if defined(DQN_USE_STD_PRINTF)
|
||||
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8(" Using stdio's printf functions\n"));
|
||||
Dqn_Str8Builder_AddRef(&builder, DQN_STR8(" Using stdio's printf functions\n"));
|
||||
#else
|
||||
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8(" Using stb_sprintf functions\n"));
|
||||
Dqn_Str8Builder_AddRef(&builder, DQN_STR8(" Using stb_sprintf functions\n"));
|
||||
#endif
|
||||
|
||||
// TODO(doyle): Add stacktrace feature log
|
||||
@ -1090,10 +1091,10 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
|
||||
if (on_init & Dqn_LibraryOnInit_LogCPUFeatures) {
|
||||
Dqn_CPUReport const *report = &result->cpu_report;
|
||||
Dqn_Str8 brand = Dqn_Str8_TrimWhitespaceAround(Dqn_Str8_Init(report->brand, sizeof(report->brand) - 1));
|
||||
Dqn_Str8Builder_AppendF(&builder,
|
||||
" CPU '%.*s' from '%s' detected:\n",
|
||||
DQN_STR_FMT(brand),
|
||||
report->vendor);
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
" CPU '%.*s' from '%s' detected:\n",
|
||||
DQN_STR_FMT(brand),
|
||||
report->vendor);
|
||||
|
||||
Dqn_usize longest_feature_name = 0;
|
||||
DQN_FOR_UINDEX(feature_index, Dqn_CPUFeature_Count) {
|
||||
@ -1104,16 +1105,16 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
|
||||
DQN_FOR_UINDEX(feature_index, Dqn_CPUFeature_Count) {
|
||||
Dqn_CPUFeatureDecl feature_decl = g_dqn_cpu_feature_decl[feature_index];
|
||||
bool has_feature = Dqn_CPU_HasFeature(report, feature_decl.value);
|
||||
Dqn_Str8Builder_AppendF(&builder,
|
||||
" %.*s:%*s%s\n",
|
||||
DQN_STR_FMT(feature_decl.label),
|
||||
DQN_CAST(int)(longest_feature_name - feature_decl.label.size),
|
||||
"",
|
||||
has_feature ? "available" : "not available");
|
||||
Dqn_Str8Builder_AddF(&builder,
|
||||
" %.*s:%*s%s\n",
|
||||
DQN_STR_FMT(feature_decl.label),
|
||||
DQN_CAST(int)(longest_feature_name - feature_decl.label.size),
|
||||
"",
|
||||
has_feature ? "available" : "not available");
|
||||
}
|
||||
}
|
||||
|
||||
Dqn_Str8 info_log = Dqn_Str8Builder_Build(&builder, scratch.arena);
|
||||
Dqn_Str8 info_log = Dqn_Str8Builder_Build(&builder, tmem.arena);
|
||||
if (Dqn_Str8_HasData(info_log))
|
||||
Dqn_Log_DebugF("%.*s", DQN_STR_FMT(info_log));
|
||||
return result;
|
||||
@ -1121,8 +1122,10 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
|
||||
|
||||
DQN_API void Dqn_Library_SetPointer(Dqn_Library *library)
|
||||
{
|
||||
if (library)
|
||||
if (library) {
|
||||
g_dqn_library = library;
|
||||
Dqn_OS_ThreadSetTLS(&library->tls);
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
@ -1216,21 +1219,42 @@ DQN_API Dqn_Arena *Dqn_Library_AllocArenaF(Dqn_usize reserve, Dqn_usize commit,
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
// NOTE: [$PROF] Dqn_Profiler //////////////////////////////////////////////////////////////////////
|
||||
Dqn_ProfilerZoneScope::Dqn_ProfilerZoneScope(Dqn_Str8 name, uint16_t anchor_index)
|
||||
DQN_API bool Dqn_Library_EraseArena(Dqn_Arena *arena, Dqn_ArenaCatalogFreeArena free_arena)
|
||||
{
|
||||
zone = Dqn_Profiler_BeginZoneWithIndex(name, anchor_index);
|
||||
Dqn_ArenaCatalog *catalog = &g_dqn_library->arena_catalog;
|
||||
bool result = Dqn_ArenaCatalog_Erase(catalog, arena, free_arena);
|
||||
return result;
|
||||
}
|
||||
|
||||
Dqn_ProfilerZoneScope::~Dqn_ProfilerZoneScope()
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
// NOTE: [$PROF] Dqn_Profiler //////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_ProfilerZoneScope::Dqn_ProfilerZoneScope(Dqn_Str8 name, uint16_t anchor_index)
|
||||
{
|
||||
zone = Dqn_Profiler_BeginZoneAtIndex(name, anchor_index);
|
||||
}
|
||||
|
||||
DQN_API Dqn_ProfilerZoneScope::~Dqn_ProfilerZoneScope()
|
||||
{
|
||||
Dqn_Profiler_EndZone(zone);
|
||||
}
|
||||
|
||||
Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex(Dqn_Str8 name, uint16_t anchor_index)
|
||||
DQN_API Dqn_ProfilerAnchor *Dqn_Profiler_ReadBuffer()
|
||||
{
|
||||
Dqn_ProfilerAnchor *anchor = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back) + anchor_index;
|
||||
Dqn_ProfilerAnchor *result = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Front);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_ProfilerAnchor *Dqn_Profiler_WriteBuffer()
|
||||
{
|
||||
Dqn_ProfilerAnchor *result = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_ProfilerZone Dqn_Profiler_BeginZoneAtIndex(Dqn_Str8 name, uint16_t anchor_index)
|
||||
{
|
||||
Dqn_ProfilerAnchor *anchor = Dqn_Profiler_WriteBuffer() + anchor_index;
|
||||
if (Dqn_Str8_HasData(anchor->name))
|
||||
DQN_ASSERTF(name == anchor->name, "Potentially overwriting a zone by accident?");
|
||||
anchor->name = name;
|
||||
Dqn_ProfilerZone result = {};
|
||||
result.begin_tsc = Dqn_CPU_TSC();
|
||||
@ -1241,10 +1265,10 @@ Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex(Dqn_Str8 name, uint16_t anchor_
|
||||
return result;
|
||||
}
|
||||
|
||||
void Dqn_Profiler_EndZone(Dqn_ProfilerZone zone)
|
||||
DQN_API void Dqn_Profiler_EndZone(Dqn_ProfilerZone zone)
|
||||
{
|
||||
uint64_t elapsed_tsc = Dqn_CPU_TSC() - zone.begin_tsc;
|
||||
Dqn_ProfilerAnchor *anchor_buffer = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back);
|
||||
Dqn_ProfilerAnchor *anchor_buffer = Dqn_Profiler_WriteBuffer();
|
||||
Dqn_ProfilerAnchor *anchor = anchor_buffer + zone.anchor_index;
|
||||
|
||||
anchor->hit_count++;
|
||||
@ -1256,7 +1280,7 @@ void Dqn_Profiler_EndZone(Dqn_ProfilerZone zone)
|
||||
g_dqn_library->profiler->parent_zone = zone.parent_zone;
|
||||
}
|
||||
|
||||
Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer buffer)
|
||||
DQN_API Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer buffer)
|
||||
{
|
||||
uint8_t offset = buffer == Dqn_ProfilerAnchorBuffer_Back ? 0 : 1;
|
||||
uint8_t anchor_buffer =
|
||||
@ -1265,7 +1289,7 @@ Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer buffer)
|
||||
return result;
|
||||
}
|
||||
|
||||
void Dqn_Profiler_SwapAnchorBuffer()
|
||||
DQN_API void Dqn_Profiler_SwapAnchorBuffer()
|
||||
{
|
||||
g_dqn_library->profiler->active_anchor_buffer++;
|
||||
Dqn_ProfilerAnchor *anchors = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back);
|
||||
@ -1274,9 +1298,9 @@ void Dqn_Profiler_SwapAnchorBuffer()
|
||||
DQN_ARRAY_UCOUNT(g_dqn_library->profiler->anchors[0]) * sizeof(g_dqn_library->profiler->anchors[0][0]));
|
||||
}
|
||||
|
||||
void Dqn_Profiler_Dump(uint64_t tsc_per_second)
|
||||
DQN_API void Dqn_Profiler_Dump(uint64_t tsc_per_second)
|
||||
{
|
||||
Dqn_ProfilerAnchor *anchors = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back);
|
||||
Dqn_ProfilerAnchor *anchors = Dqn_Profiler_ReadBuffer();
|
||||
for (size_t anchor_index = 1; anchor_index < DQN_PROFILER_ANCHOR_BUFFER_SIZE; anchor_index++) {
|
||||
Dqn_ProfilerAnchor const *anchor = anchors + anchor_index;
|
||||
if (!anchor->hit_count)
|
||||
@ -1343,8 +1367,8 @@ DQN_API bool Dqn_OS_JobQueueSPMCAdd(Dqn_JobQueueSPMC *queue, Dqn_Job job)
|
||||
|
||||
DQN_API int32_t Dqn_OS_JobQueueSPMCThread(Dqn_OSThread *thread)
|
||||
{
|
||||
Dqn_JobQueueSPMC *queue = DQN_CAST(Dqn_JobQueueSPMC *) thread->user_context;
|
||||
uint32_t const pot_mask = DQN_ARRAY_UCOUNT(queue->jobs) - 1;
|
||||
Dqn_JobQueueSPMC *queue = DQN_CAST(Dqn_JobQueueSPMC *) thread->user_context;
|
||||
uint32_t const pot_mask = DQN_ARRAY_UCOUNT(queue->jobs) - 1;
|
||||
static_assert(DQN_ARRAY_UCOUNT(queue->jobs) == DQN_ARRAY_UCOUNT(queue->complete_queue), "PoT mask is used to mask access to both arrays");
|
||||
|
||||
for (;;) {
|
||||
@ -1360,8 +1384,9 @@ DQN_API int32_t Dqn_OS_JobQueueSPMCThread(Dqn_OSThread *thread)
|
||||
queue->read_index += 1;
|
||||
Dqn_OS_MutexUnlock(&queue->mutex);
|
||||
|
||||
job.func(job.user_context);
|
||||
Dqn_Arena_Deinit(job.arena);
|
||||
job.elapsed_tsc -= Dqn_CPU_TSC();
|
||||
job.func(thread, job.user_context);
|
||||
job.elapsed_tsc += Dqn_CPU_TSC();
|
||||
|
||||
if (job.add_to_completion_queue) {
|
||||
Dqn_OS_SemaphoreWait(&queue->complete_queue_write_semaphore, DQN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
@ -1371,9 +1396,15 @@ DQN_API int32_t Dqn_OS_JobQueueSPMCThread(Dqn_OSThread *thread)
|
||||
Dqn_OS_SemaphoreIncrement(&queue->complete_queue_write_semaphore, 1);
|
||||
}
|
||||
|
||||
// NOTE: Update finish counter
|
||||
Dqn_OS_MutexLock(&queue->mutex);
|
||||
queue->finish_index += 1;
|
||||
if (queue->finish_index == queue->write_index && queue->threads_waiting_for_completion) {
|
||||
|
||||
// NOTE: If all jobs are finished and we have another thread who is
|
||||
// blocked via `WaitForCompletion` for this job queue, we will go
|
||||
// release the semaphore to wake them all up.
|
||||
bool all_jobs_finished = queue->finish_index == queue->write_index;
|
||||
if (all_jobs_finished && queue->threads_waiting_for_completion) {
|
||||
Dqn_OS_SemaphoreIncrement(&queue->wait_for_completion_semaphore,
|
||||
queue->threads_waiting_for_completion);
|
||||
queue->threads_waiting_for_completion = 0;
|
||||
|
136
dqn_helpers.h
136
dqn_helpers.h
@ -64,8 +64,7 @@ enum Dqn_BinarySearchType
|
||||
{
|
||||
// Index of the match. If no match is found, found is set to false and the
|
||||
// index is set to the index where the match should be inserted/exist, if
|
||||
// it were in the array (in practice this turns out to be the first or
|
||||
// [last index + 1] of the array).
|
||||
// it were in the array
|
||||
Dqn_BinarySearchType_Match,
|
||||
|
||||
// Index of the first element in the array that is `element >= find`. If no such
|
||||
@ -97,6 +96,9 @@ struct Dqn_BinarySearchResult
|
||||
Dqn_usize index;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using Dqn_QSortLessThanProc = bool(T const &a, T const &b, void *user_context);
|
||||
|
||||
// NOTE: [$MISC] Misc //////////////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_U64Str8
|
||||
{
|
||||
@ -122,7 +124,8 @@ struct Dqn_U64ByteSize
|
||||
Dqn_f64 bytes;
|
||||
};
|
||||
|
||||
enum Dqn_U64AgeUnit
|
||||
typedef uint32_t Dqn_U64AgeUnit;
|
||||
enum Dqn_U64AgeUnit_
|
||||
{
|
||||
Dqn_U64AgeUnit_Sec = 1 << 0,
|
||||
Dqn_U64AgeUnit_Min = 1 << 1,
|
||||
@ -142,8 +145,9 @@ struct Dqn_U64HexStr8
|
||||
|
||||
enum Dqn_U64HexStr8Flags
|
||||
{
|
||||
Dqn_BinHexU64Str8Flags_No0xPrefix = 1 << 0, /// Remove the 0x prefix from the string
|
||||
Dqn_BinHexU64Str8Flags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex
|
||||
Dqn_HexU64Str8Flags_Nil = 0,
|
||||
Dqn_HexU64Str8Flags_0xPrefix = 1 << 0, /// Add the '0x' prefix from the string
|
||||
Dqn_HexU64Str8Flags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex
|
||||
};
|
||||
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
@ -182,12 +186,12 @@ struct Dqn_ProfilerZoneScope
|
||||
~Dqn_ProfilerZoneScope();
|
||||
Dqn_ProfilerZone zone;
|
||||
};
|
||||
#define Dqn_Profiler_ZoneScopeWithIndex(name, anchor_index) auto DQN_UNIQUE_NAME(profile_zone_) = Dqn_ProfilerZoneScope(DQN_STR8(name), anchor_index)
|
||||
#define Dqn_Profiler_ZoneScope(name) Dqn_Profiler_ZoneScopeWithIndex(name, __COUNTER__ + 1)
|
||||
#define Dqn_Profiler_ZoneScopeAtIndex(name, anchor_index) auto DQN_UNIQUE_NAME(profile_zone_) = Dqn_ProfilerZoneScope(DQN_STR8(name), anchor_index)
|
||||
#define Dqn_Profiler_ZoneScope(name) Dqn_Profiler_ZoneScopeAtIndex(name, __COUNTER__ + 1)
|
||||
#endif
|
||||
|
||||
#define Dqn_Profiler_ZoneBlockIndex(name, index) \
|
||||
for (Dqn_ProfilerZone DQN_UNIQUE_NAME(profile_zone__) = Dqn_Profiler_BeginZoneWithIndex(name, index), DQN_UNIQUE_NAME(dummy__) = {}; \
|
||||
for (Dqn_ProfilerZone DQN_UNIQUE_NAME(profile_zone__) = Dqn_Profiler_BeginZoneAtIndex(name, index), DQN_UNIQUE_NAME(dummy__) = {}; \
|
||||
DQN_UNIQUE_NAME(dummy__).begin_tsc == 0; \
|
||||
Dqn_Profiler_EndZone(DQN_UNIQUE_NAME(profile_zone__)), DQN_UNIQUE_NAME(dummy__).begin_tsc = 1)
|
||||
|
||||
@ -208,13 +212,14 @@ struct Dqn_Profiler
|
||||
#endif // !defined(DQN_NO_PROFILER)
|
||||
|
||||
// NOTE: [$JOBQ] Dqn_JobQueue ///////////////////////////////////////////////////////////////////////
|
||||
typedef void (Dqn_JobQueueFunc)(void *user_context);
|
||||
typedef void (Dqn_JobQueueFunc)(Dqn_OSThread *thread, void *user_context);
|
||||
struct Dqn_Job
|
||||
{
|
||||
bool add_to_completion_queue;
|
||||
Dqn_Arena *arena;
|
||||
Dqn_JobQueueFunc *func;
|
||||
void *user_context;
|
||||
Dqn_JobQueueFunc *func; // The function to invoke for the job
|
||||
void *user_context; // Pointer user can set to use in their `job_func`
|
||||
uint64_t elapsed_tsc;
|
||||
uint16_t user_tag; // Arbitrary value the user can set to identiy the type of `user_context` this job has
|
||||
bool add_to_completion_queue; // When true, on job completion, job must be dequeued from the completion queue via `GetFinishedJobs`
|
||||
};
|
||||
|
||||
#if !defined(DQN_JOB_QUEUE_SPMC_SIZE)
|
||||
@ -248,13 +253,14 @@ struct Dqn_Library
|
||||
{
|
||||
bool lib_init; // True if the library has been initialised via `Dqn_Library_Init`
|
||||
Dqn_TicketMutex lib_mutex;
|
||||
Dqn_TicketMutex thread_context_init_mutex;
|
||||
Dqn_Str8 exe_dir; // The directory of the current executable
|
||||
Dqn_Arena arena;
|
||||
Dqn_ChunkPool pool; // Uses 'arena' for malloc-like allocations
|
||||
Dqn_ArenaCatalog arena_catalog;
|
||||
bool slow_verification_checks; // Enable expensive library verification checks
|
||||
Dqn_CPUReport cpu_report;
|
||||
Dqn_TLS tls; // Thread local storage state for the main thread.
|
||||
|
||||
// NOTE: Logging ///////////////////////////////////////////////////////////////////////////////
|
||||
Dqn_LogProc * log_callback; // Set this pointer to override the logging routine
|
||||
void * log_user_data;
|
||||
@ -262,22 +268,27 @@ struct Dqn_Library
|
||||
Dqn_OSFile log_file; // TODO(dqn): Hmmm, how should we do this... ?
|
||||
Dqn_TicketMutex log_file_mutex; // Is locked when instantiating the log_file for the first time
|
||||
bool log_no_colour; // Disable colours in the logging output
|
||||
|
||||
// NOTE: Leak Tracing //////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DQN_LEAK_TRACKING)
|
||||
Dqn_DSMap<Dqn_DebugAlloc> alloc_table;
|
||||
Dqn_TicketMutex alloc_table_mutex;
|
||||
Dqn_Arena alloc_table_arena;
|
||||
#endif
|
||||
|
||||
// NOTE: Win32 /////////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DQN_OS_WIN32)
|
||||
LARGE_INTEGER win32_qpc_frequency;
|
||||
Dqn_TicketMutex win32_bcrypt_rng_mutex;
|
||||
void * win32_bcrypt_rng_handle;
|
||||
#endif
|
||||
|
||||
bool win32_sym_initialised;
|
||||
|
||||
// NOTE: OS ////////////////////////////////////////////////////////////////////////////////////
|
||||
uint32_t os_page_size;
|
||||
uint32_t os_alloc_granularity;
|
||||
|
||||
// NOTE: Profiler //////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
Dqn_Profiler *profiler;
|
||||
@ -354,6 +365,13 @@ template <typename T> Dqn_BinarySearchResult Dqn_BinarySearch (T
|
||||
Dqn_BinarySearchType type = Dqn_BinarySearchType_Match,
|
||||
Dqn_BinarySearchLessThanProc<T> less_than = Dqn_BinarySearch_DefaultLessThan);
|
||||
|
||||
// NOTE: [$QSOR] Dqn_QSort /////////////////////////////////////////////////////////////////////////
|
||||
template <typename T> bool Dqn_QSort_DefaultLessThan(T const &lhs, T const &rhs);
|
||||
template <typename T> void Dqn_QSort (T *array,
|
||||
Dqn_usize array_size,
|
||||
void *user_context,
|
||||
Dqn_QSortLessThanProc<T> less_than = Dqn_QSort_DefaultLessThan);
|
||||
|
||||
// NOTE: [$BITS] Dqn_Bit ///////////////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_Bit_UnsetInplace (Dqn_usize *flags, Dqn_usize bitfield);
|
||||
DQN_API void Dqn_Bit_SetInplace (Dqn_usize *flags, Dqn_usize bitfield);
|
||||
@ -426,23 +444,28 @@ DQN_API Dqn_U64Str8 Dqn_U64ToStr8 (uint64_t v
|
||||
DQN_API Dqn_U64ByteSize Dqn_U64ToByteSize (uint64_t bytes, Dqn_U64ByteSizeType type);
|
||||
DQN_API Dqn_Str8 Dqn_U64ToByteSizeStr8 (Dqn_Arena *arena, uint64_t bytes, Dqn_U64ByteSizeType desired_type);
|
||||
DQN_API Dqn_Str8 Dqn_U64ByteSizeTypeString (Dqn_U64ByteSizeType type);
|
||||
DQN_API Dqn_Str8 Dqn_U64ToAge (Dqn_Arena *arena, uint64_t age_s, Dqn_usize type);
|
||||
DQN_API Dqn_Str8 Dqn_U64ToAge (Dqn_Arena *arena, uint64_t age_s, Dqn_U64AgeUnit unit);
|
||||
|
||||
DQN_API uint64_t Dqn_HexToU64 (Dqn_Str8 hex);
|
||||
DQN_API Dqn_Str8 Dqn_U64ToHex (Dqn_Arena *arena, uint64_t number, uint32_t flags);
|
||||
DQN_API Dqn_U64HexStr8 Dqn_U64ToHexStr8 (uint64_t number, uint32_t flags);
|
||||
|
||||
DQN_API bool Dqn_BytesToHexPtr (Dqn_Arena *arena, void const *src, Dqn_usize src_size, char *dest);
|
||||
DQN_API bool Dqn_BytesToHexPtr (void const *src, Dqn_usize src_size, char *dest);
|
||||
DQN_API Dqn_Str8 Dqn_BytesToHex (Dqn_Arena *arena, void const *src, Dqn_usize size);
|
||||
#define Dqn_BytesToHex_TLS(...) Dqn_BytesToHex(Dqn_TLS_TopArena(), __VA_ARGS__)
|
||||
|
||||
DQN_API Dqn_usize Dqn_HexToBytesPtrUnchecked (Dqn_Str8 hex, void *dest, Dqn_usize dest_sizek);
|
||||
DQN_API Dqn_usize Dqn_HexToBytesPtr (Dqn_Str8 hex, void *dest, Dqn_usize dest_sizek);
|
||||
DQN_API Dqn_usize Dqn_HexToBytesPtrUnchecked (Dqn_Str8 hex, void *dest, Dqn_usize dest_size);
|
||||
DQN_API Dqn_usize Dqn_HexToBytesPtr (Dqn_Str8 hex, void *dest, Dqn_usize dest_size);
|
||||
DQN_API Dqn_Str8 Dqn_HexToBytesUnchecked (Dqn_Arena *arena, Dqn_Str8 hex);
|
||||
#define Dqn_HexToBytesUnchecked_TLS(...) Dqn_HexToBytesUnchecked(Dqn_TLS_TopArena(), __VA_ARGS__)
|
||||
DQN_API Dqn_Str8 Dqn_HexToBytes (Dqn_Arena *arena, Dqn_Str8 hex);
|
||||
#define Dqn_HexToBytes_TLS(...) Dqn_HexToBytes(Dqn_TLS_TopArena(), __VA_ARGS__)
|
||||
|
||||
// NOTE: [$PROF] Dqn_Profiler //////////////////////////////////////////////////////////////////////
|
||||
#define Dqn_Profiler_BeginZone(name) Dqn_Profiler_BeginZoneWithIndex(DQN_STR8(name), __COUNTER__ + 1)
|
||||
DQN_API Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex (Dqn_Str8 name, uint16_t anchor_index);
|
||||
DQN_API Dqn_ProfilerAnchor *Dqn_Profiler_ReadBuffer ();
|
||||
DQN_API Dqn_ProfilerAnchor *Dqn_Profiler_WriteBuffer ();
|
||||
#define Dqn_Profiler_BeginZone(name) Dqn_Profiler_BeginZoneAtIndex(DQN_STR8(name), __COUNTER__ + 1)
|
||||
DQN_API Dqn_ProfilerZone Dqn_Profiler_BeginZoneAtIndex (Dqn_Str8 name, uint16_t anchor_index);
|
||||
DQN_API void Dqn_Profiler_EndZone (Dqn_ProfilerZone zone);
|
||||
DQN_API Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer (Dqn_ProfilerAnchorBuffer buffer);
|
||||
DQN_API void Dqn_Profiler_SwapAnchorBuffer ();
|
||||
@ -465,6 +488,7 @@ DQN_API void Dqn_Library_SetProfiler (Dqn_Profil
|
||||
DQN_API void Dqn_Library_SetLogCallback (Dqn_LogProc *proc, void *user_data);
|
||||
DQN_API void Dqn_Library_DumpThreadContextArenaStat (Dqn_Str8 file_path);
|
||||
DQN_API Dqn_Arena * Dqn_Library_AllocArenaF (Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, char const *fmt, ...);
|
||||
DQN_API bool Dqn_Library_EraseArena (Dqn_Arena *arena, Dqn_ArenaCatalogFreeArena free_arena);
|
||||
|
||||
// NOTE: [$BSEA] Dqn_BinarySearch //////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
@ -482,7 +506,7 @@ Dqn_BinarySearchResult Dqn_BinarySearch(T const *array,
|
||||
Dqn_BinarySearchLessThanProc<T> less_than)
|
||||
{
|
||||
Dqn_BinarySearchResult result = {};
|
||||
if (!array || array_size <= 0)
|
||||
if (!array || array_size <= 0 || !less_than)
|
||||
return result;
|
||||
|
||||
T const *end = array + array_size;
|
||||
@ -519,3 +543,73 @@ Dqn_BinarySearchResult Dqn_BinarySearch(T const *array,
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$QSOR] Dqn_QSort /////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
bool Dqn_QSort_DefaultLessThan(T const &lhs, T const &rhs, void *user_context)
|
||||
{
|
||||
(void)user_context;
|
||||
bool result = lhs < rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Dqn_QSort(T *array, Dqn_usize array_size, void *user_context, Dqn_QSortLessThanProc<T> less_than)
|
||||
{
|
||||
if (!array || array_size <= 1 || !less_than)
|
||||
return;
|
||||
|
||||
// NOTE: Insertion Sort, under 24->32 is an optimal amount /////////////////////////////////////
|
||||
const Dqn_usize QSORT_THRESHOLD = 24;
|
||||
if (array_size < QSORT_THRESHOLD) {
|
||||
for (Dqn_usize item_to_insert_index = 1; item_to_insert_index < array_size; item_to_insert_index++) {
|
||||
for (Dqn_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 (Dqn_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 /////////////////////////////////////////
|
||||
Dqn_usize last_index = array_size - 1;
|
||||
Dqn_usize pivot_index = array_size / 2;
|
||||
Dqn_usize partition_index = 0;
|
||||
Dqn_usize start_index = 0;
|
||||
|
||||
// Swap pivot with last index, so pivot is always at the end of the array.
|
||||
// This makes logic much simpler.
|
||||
DQN_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 (Dqn_usize index = start_index; index < last_index; index++) {
|
||||
if (less_than(array[index], array[pivot_index], user_context)) {
|
||||
DQN_SWAP(array[partition_index], array[index]);
|
||||
partition_index++;
|
||||
}
|
||||
}
|
||||
|
||||
// Move pivot to right of partition
|
||||
// 4, 5, 2, 3, |6, 8, ^7*
|
||||
DQN_SWAP(array[partition_index], array[pivot_index]);
|
||||
Dqn_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
|
||||
Dqn_usize one_after_partition_index = partition_index + 1;
|
||||
Dqn_QSort(array + one_after_partition_index, (array_size - one_after_partition_index), user_context, less_than);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "dqn.h"
|
||||
|
||||
#if !defined(SHEREDOM_JSON_H_INCLUDED)
|
||||
#error Sheredom's json.h (github.com/sheredom/json.h) must be included before this file
|
||||
#error Sheredom json.h (github.com/sheredom/json.h) must be included before this file
|
||||
#endif
|
||||
|
||||
// NOTE: Dqn_JSON //////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1212,10 +1212,10 @@ DQN_API Dqn_FStr8<256> Dqn_M4_ColumnMajorString(Dqn_M4 mat)
|
||||
Dqn_FStr8<256> result = {};
|
||||
for (int row = 0; row < 4; row++) {
|
||||
for (int it = 0; it < 4; it++) {
|
||||
if (it == 0) Dqn_FStr8_Append(&result, DQN_STR8("|"));
|
||||
Dqn_FStr8_AppendF(&result, "%.5f", mat.columns[it][row]);
|
||||
if (it != 3) Dqn_FStr8_Append(&result, DQN_STR8(", "));
|
||||
else Dqn_FStr8_Append(&result, DQN_STR8("|\n"));
|
||||
if (it == 0) Dqn_FStr8_Add(&result, DQN_STR8("|"));
|
||||
Dqn_FStr8_AddF(&result, "%.5f", mat.columns[it][row]);
|
||||
if (it != 3) Dqn_FStr8_Add(&result, DQN_STR8(", "));
|
||||
else Dqn_FStr8_Add(&result, DQN_STR8("|\n"));
|
||||
}
|
||||
}
|
||||
|
||||
|
79
dqn_os.cpp
79
dqn_os.cpp
@ -75,8 +75,8 @@ DQN_API Dqn_Str8 Dqn_OS_EXEDir(Dqn_Arena *arena)
|
||||
Dqn_Str8 result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_Str8 exe_path = Dqn_OS_EXEPath(scratch.arena);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
|
||||
Dqn_Str8 exe_path = Dqn_OS_EXEPath(tmem.arena);
|
||||
Dqn_Str8 separators[] = {DQN_STR8("/"), DQN_STR8("\\")};
|
||||
Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitLastArray(exe_path, separators, DQN_ARRAY_UCOUNT(separators));
|
||||
result = Dqn_Str8_Copy(arena, split.lhs);
|
||||
@ -172,6 +172,14 @@ DQN_API uint64_t Dqn_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_tsc_f
|
||||
|
||||
#if !defined(DQN_NO_OS_FILE_API)
|
||||
// NOTE: [$FILE] Dqn_OSPathInfo/File ///////////////////////////////////////////////////////////////
|
||||
DQN_API bool Dqn_OS_FileIsOlderThan(Dqn_Str8 file, Dqn_Str8 check_against)
|
||||
{
|
||||
Dqn_OSPathInfo file_info = Dqn_OS_PathInfo(file);
|
||||
Dqn_OSPathInfo check_against_info = Dqn_OS_PathInfo(check_against);
|
||||
bool result = !file_info.exists || file_info.last_write_time_in_s < check_against_info.last_write_time_in_s;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_FileWrite(Dqn_OSFile *file, Dqn_Str8 buffer, Dqn_ErrorSink *error)
|
||||
{
|
||||
bool result = Dqn_OS_FileWritePtr(file, buffer.data, buffer.size, error);
|
||||
@ -183,8 +191,8 @@ DQN_API bool Dqn_OS_FileWriteFV(Dqn_OSFile *file, Dqn_ErrorSink *error, DQN_FMT_
|
||||
bool result = false;
|
||||
if (!file || !fmt)
|
||||
return result;
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 buffer = Dqn_Str8_InitFV(tmem.arena, fmt, args);
|
||||
result = Dqn_OS_FileWritePtr(file, buffer.data, buffer.size, error);
|
||||
return result;
|
||||
}
|
||||
@ -199,7 +207,7 @@ DQN_API bool Dqn_OS_FileWriteF(Dqn_OSFile *file, Dqn_ErrorSink *error, DQN_FMT_A
|
||||
}
|
||||
|
||||
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_Str8 Dqn_OS_ReadAll(Dqn_Str8 path, Dqn_Arena *arena, Dqn_ErrorSink *error)
|
||||
DQN_API Dqn_Str8 Dqn_OS_ReadAll(Dqn_Arena *arena, Dqn_Str8 path, Dqn_ErrorSink *error)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
if (!arena)
|
||||
@ -215,8 +223,8 @@ DQN_API Dqn_Str8 Dqn_OS_ReadAll(Dqn_Str8 path, Dqn_Arena *arena, Dqn_ErrorSink *
|
||||
Dqn_ArenaTempMem temp_mem = Dqn_Arena_TempMemBegin(arena);
|
||||
result = Dqn_Str8_Alloc(arena, path_info.size, Dqn_ZeroMem_No);
|
||||
if (!Dqn_Str8_HasData(result)) {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8(scratch.arena, path_info.size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8(tmem.arena, path_info.size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_ErrorSink_MakeF(error, 1 /*error_code*/, "Failed to allocate %.*s for reading file '%.*s'", DQN_STR_FMT(buffer_size_str8), DQN_STR_FMT(path));
|
||||
Dqn_Arena_TempMemEnd(temp_mem);
|
||||
result = {};
|
||||
@ -244,8 +252,8 @@ DQN_API bool Dqn_OS_WriteAll(Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *erro
|
||||
|
||||
DQN_API bool Dqn_OS_WriteAllFV(Dqn_Str8 file_path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 buffer = Dqn_Str8_InitFV(tmem.arena, fmt, args);
|
||||
bool result = Dqn_OS_WriteAll(file_path, buffer, error);
|
||||
return result;
|
||||
}
|
||||
@ -261,8 +269,8 @@ DQN_API bool Dqn_OS_WriteAllF(Dqn_Str8 file_path, Dqn_ErrorSink *error, DQN_FMT_
|
||||
|
||||
DQN_API bool Dqn_OS_WriteAllSafe(Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 tmp_path = Dqn_Str8_InitF(scratch.arena, "%.*s.tmp", DQN_STR_FMT(path));
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 tmp_path = Dqn_Str8_InitF(tmem.arena, "%.*s.tmp", DQN_STR_FMT(path));
|
||||
if (!Dqn_OS_WriteAll(tmp_path, buffer, error))
|
||||
return false;
|
||||
if (!Dqn_OS_CopyFile(tmp_path, path, true /*overwrite*/, error))
|
||||
@ -274,8 +282,8 @@ DQN_API bool Dqn_OS_WriteAllSafe(Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *
|
||||
|
||||
DQN_API bool Dqn_OS_WriteAllSafeFV(Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 buffer = Dqn_Str8_InitFV(tmem.arena, fmt, args);
|
||||
bool result = Dqn_OS_WriteAllSafe(path, buffer, error);
|
||||
return result;
|
||||
}
|
||||
@ -375,7 +383,7 @@ DQN_API bool Dqn_OS_PathPop(Dqn_OSPath *fs_path)
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathConvertTo(Dqn_Arena *arena, Dqn_Str8 path, Dqn_Str8 path_separator)
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathTo(Dqn_Arena *arena, Dqn_Str8 path, Dqn_Str8 path_separator)
|
||||
{
|
||||
Dqn_OSPath fs_path = {};
|
||||
Dqn_OS_PathAddRef(arena, &fs_path, path);
|
||||
@ -383,31 +391,31 @@ DQN_API Dqn_Str8 Dqn_OS_PathConvertTo(Dqn_Arena *arena, Dqn_Str8 path, Dqn_Str8
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathConvertToF(Dqn_Arena *arena, Dqn_Str8 path_separator, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathToF(Dqn_Arena *arena, Dqn_Str8 path_separator, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Str8 path = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
||||
Dqn_Str8 path = Dqn_Str8_InitFV(tmem.arena, fmt, args);
|
||||
va_end(args);
|
||||
Dqn_Str8 result = Dqn_OS_PathConvertTo(arena, path, path_separator);
|
||||
Dqn_Str8 result = Dqn_OS_PathTo(arena, path, path_separator);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathConvert(Dqn_Arena *arena, Dqn_Str8 path)
|
||||
DQN_API Dqn_Str8 Dqn_OS_Path(Dqn_Arena *arena, Dqn_Str8 path)
|
||||
{
|
||||
Dqn_Str8 result = Dqn_OS_PathConvertTo(arena, path, Dqn_OSPathSeperatorString);
|
||||
Dqn_Str8 result = Dqn_OS_PathTo(arena, path, Dqn_OSPathSeperatorString);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathConvertF(Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathF(Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Str8 path = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
||||
Dqn_Str8 path = Dqn_Str8_InitFV(tmem.arena, fmt, args);
|
||||
va_end(args);
|
||||
Dqn_Str8 result = Dqn_OS_PathConvert(arena, path);
|
||||
Dqn_Str8 result = Dqn_OS_Path(arena, path);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -480,6 +488,23 @@ DQN_API Dqn_OSExecResult Dqn_OS_ExecOrAbort(Dqn_Slice<Dqn_Str8> cmd_line, Dqn_St
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$THRD] Dqn_OSThread //////////////////////////////////////////////////////////////////////
|
||||
DQN_THREAD_LOCAL Dqn_TLS *g_dqn_os_thread_tls;
|
||||
|
||||
static void Dqn_OS_ThreadExecute_(void *user_context)
|
||||
{
|
||||
Dqn_OSThread *thread = DQN_CAST(Dqn_OSThread *)user_context;
|
||||
Dqn_TLS_Init(&thread->tls);
|
||||
Dqn_OS_ThreadSetTLS(&thread->tls);
|
||||
Dqn_OS_SemaphoreWait(&thread->init_semaphore, DQN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
thread->func(thread);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_OS_ThreadSetTLS(Dqn_TLS *tls)
|
||||
{
|
||||
g_dqn_os_thread_tls = tls;
|
||||
}
|
||||
|
||||
// NOTE: [$HTTP] Dqn_OSHttp ////////////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_OS_HttpRequestWait(Dqn_OSHttpResponse *response)
|
||||
{
|
||||
@ -490,9 +515,9 @@ DQN_API void Dqn_OS_HttpRequestWait(Dqn_OSHttpResponse *response)
|
||||
DQN_API Dqn_OSHttpResponse Dqn_OS_HttpRequest(Dqn_Arena *arena, Dqn_Str8 host, Dqn_Str8 path, Dqn_OSHttpRequestSecure secure, Dqn_Str8 method, Dqn_Str8 body, Dqn_Str8 headers)
|
||||
{
|
||||
// TODO(doyle): Revise the memory allocation and its lifetime
|
||||
Dqn_OSHttpResponse result = {};
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
result.scratch_arena = scratch.arena;
|
||||
Dqn_OSHttpResponse result = {};
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
|
||||
result.tmem_arena = tmem.arena;
|
||||
|
||||
Dqn_OS_HttpRequestAsync(&result, arena, host, path, secure, method, body, headers);
|
||||
Dqn_OS_HttpRequestWait(&result);
|
||||
|
80
dqn_os.h
80
dqn_os.h
@ -122,6 +122,13 @@ struct Dqn_OSPathInfo
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
struct Dqn_OS_DirIterator
|
||||
{
|
||||
void *handle;
|
||||
Dqn_Str8 file_name;
|
||||
char buffer[512];
|
||||
};
|
||||
|
||||
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_OSFile
|
||||
{
|
||||
@ -231,6 +238,7 @@ typedef int32_t (Dqn_OSThreadFunc)(struct Dqn_OSThread*);
|
||||
|
||||
struct Dqn_OSThread
|
||||
{
|
||||
Dqn_TLS tls;
|
||||
void *handle;
|
||||
uint64_t thread_id;
|
||||
void *user_context;
|
||||
@ -262,7 +270,7 @@ struct Dqn_OSHttpResponse
|
||||
// Synchronous HTTP response uses the TLS scratch arena whereas async
|
||||
// calls use their own dedicated arena.
|
||||
Dqn_Arena tmp_arena;
|
||||
Dqn_Arena *scratch_arena;
|
||||
Dqn_Arena *tmem_arena;
|
||||
Dqn_Str8Builder builder;
|
||||
Dqn_OSSemaphore on_complete_semaphore;
|
||||
|
||||
@ -295,6 +303,7 @@ DQN_API bool Dqn_OS_DateIsValid (Dqn_OSDateTime dat
|
||||
DQN_API bool Dqn_OS_SecureRNGBytes (void *buffer, uint32_t size);
|
||||
DQN_API Dqn_Str8 Dqn_OS_EXEPath (Dqn_Arena *arena);
|
||||
DQN_API Dqn_Str8 Dqn_OS_EXEDir (Dqn_Arena *arena);
|
||||
#define Dqn_OS_EXEDir_TLS() Dqn_OS_EXEDir(Dqn_TLS_TopArena())
|
||||
DQN_API void Dqn_OS_SleepMs (Dqn_uint milliseconds);
|
||||
// NOTE: Counters //////////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API uint64_t Dqn_OS_PerfCounterNow ();
|
||||
@ -313,13 +322,15 @@ DQN_API uint64_t Dqn_OS_EstimateTSCPerSecond(uint64_t duration_
|
||||
|
||||
#if !defined(DQN_NO_OS_FILE_API)
|
||||
// NOTE: File system paths /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo (Dqn_Str8 path);
|
||||
DQN_API bool Dqn_OS_PathDelete(Dqn_Str8 path);
|
||||
DQN_API bool Dqn_OS_FileExists(Dqn_Str8 path);
|
||||
DQN_API bool Dqn_OS_CopyFile (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_ErrorSink *error);
|
||||
DQN_API bool Dqn_OS_MoveFile (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_ErrorSink *error);
|
||||
DQN_API bool Dqn_OS_MakeDir (Dqn_Str8 path, Dqn_ErrorSink *error);
|
||||
DQN_API bool Dqn_OS_DirExists (Dqn_Str8 path, Dqn_ErrorSink *error);
|
||||
DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo (Dqn_Str8 path);
|
||||
DQN_API bool Dqn_OS_FileIsOlderThan(Dqn_Str8 file, Dqn_Str8 check_against);
|
||||
DQN_API bool Dqn_OS_PathDelete (Dqn_Str8 path);
|
||||
DQN_API bool Dqn_OS_FileExists (Dqn_Str8 path);
|
||||
DQN_API bool Dqn_OS_CopyFile (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_ErrorSink *error);
|
||||
DQN_API bool Dqn_OS_MoveFile (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_ErrorSink *error);
|
||||
DQN_API bool Dqn_OS_MakeDir (Dqn_Str8 path);
|
||||
DQN_API bool Dqn_OS_DirExists (Dqn_Str8 path);
|
||||
DQN_API bool Dqn_OS_DirIterate (Dqn_Str8 path, Dqn_OS_DirIterator *it);
|
||||
|
||||
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSFile Dqn_OS_FileOpen (Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint32_t access, Dqn_ErrorSink *error);
|
||||
@ -331,35 +342,45 @@ DQN_API bool Dqn_OS_FileWriteF (Dqn_OSFile *file, Dqn_Erro
|
||||
DQN_API void Dqn_OS_FileClose (Dqn_OSFile *file);
|
||||
|
||||
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_Str8 Dqn_OS_ReadAll (Dqn_Str8 path, Dqn_Arena *arena, Dqn_ErrorSink *error);
|
||||
DQN_API bool Dqn_OS_WriteAll (Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error);
|
||||
DQN_API bool Dqn_OS_WriteAllFV (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API bool Dqn_OS_WriteAllF (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API bool Dqn_OS_WriteAllSafe (Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error);
|
||||
DQN_API bool Dqn_OS_WriteAllSafeFV(Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API bool Dqn_OS_WriteAllSafeF (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API Dqn_Str8 Dqn_OS_ReadAll (Dqn_Arena *arena, Dqn_Str8 path, Dqn_ErrorSink *error);
|
||||
#define Dqn_OS_ReadAll_TLS(...) Dqn_OS_ReadAll(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DQN_API bool Dqn_OS_WriteAll (Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error);
|
||||
DQN_API bool Dqn_OS_WriteAllFV (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API bool Dqn_OS_WriteAllF (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API bool Dqn_OS_WriteAllSafe (Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error);
|
||||
DQN_API bool Dqn_OS_WriteAllSafeFV (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API bool Dqn_OS_WriteAllSafeF (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
#endif // !defined(DQN_NO_OS_FILE_API)
|
||||
|
||||
// NOTE: File system paths /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API bool Dqn_OS_PathAddRef (Dqn_Arena *arena, Dqn_OSPath *fs_path, Dqn_Str8 path);
|
||||
DQN_API bool Dqn_OS_PathAdd (Dqn_Arena *arena, Dqn_OSPath *fs_path, Dqn_Str8 path);
|
||||
DQN_API bool Dqn_OS_PathAddF (Dqn_Arena *arena, Dqn_OSPath *fs_path, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API bool Dqn_OS_PathPop (Dqn_OSPath *fs_path);
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathBuildWithSeparator(Dqn_Arena *arena, Dqn_OSPath const *fs_path, Dqn_Str8 path_separator);
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathConvertTo (Dqn_Arena *arena, Dqn_Str8 path, Dqn_Str8 path_separtor);
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathConvertToF (Dqn_Arena *arena, Dqn_Str8 path_separator, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathConvert (Dqn_Arena *arena, Dqn_Str8 path);
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathConvertF (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API bool Dqn_OS_PathAddRef (Dqn_Arena *arena, Dqn_OSPath *fs_path, Dqn_Str8 path);
|
||||
#define Dqn_OS_PathAddRef_TLS(...) Dqn_OS_PathAddRef(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DQN_API bool Dqn_OS_PathAdd (Dqn_Arena *arena, Dqn_OSPath *fs_path, Dqn_Str8 path);
|
||||
#define Dqn_OS_PathAdd_TLS(...) Dqn_OS_PathAdd(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DQN_API bool Dqn_OS_PathAddF (Dqn_Arena *arena, Dqn_OSPath *fs_path, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
#define Dqn_OS_PathAddF_TLS(...) Dqn_OS_PathAddF(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DQN_API bool Dqn_OS_PathPop (Dqn_OSPath *fs_path);
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathBuildWithSeparator (Dqn_Arena *arena, Dqn_OSPath const *fs_path, Dqn_Str8 path_separator);
|
||||
#define Dqn_OS_PathBuildWithSeperator_TLS(...) Dqn_OS_PathBuildWithSeperator(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathTo (Dqn_Arena *arena, Dqn_Str8 path, Dqn_Str8 path_separtor);
|
||||
#define Dqn_OS_PathTo_TLS(...) Dqn_OS_PathTo(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathToF (Dqn_Arena *arena, Dqn_Str8 path_separator, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
#define Dqn_OS_PathToF_TLS(...) Dqn_OS_PathToF(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DQN_API Dqn_Str8 Dqn_OS_Path (Dqn_Arena *arena, Dqn_Str8 path);
|
||||
#define Dqn_OS_Path_TLS(...) Dqn_OS_Path(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathF (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
#define Dqn_OS_PathF_TLS(...) Dqn_OS_PathF(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
#define Dqn_OS_PathBuildFwdSlash(allocator, fs_path) Dqn_OS_PathBuildWithSeparator(allocator, fs_path, DQN_STR8("/"))
|
||||
#define Dqn_OS_PathBuildBackSlash(allocator, fs_path) Dqn_OS_PathBuildWithSeparator(allocator, fs_path, DQN_STR8("\\"))
|
||||
#define Dqn_OS_PathBuild(allocator, fs_path) Dqn_OS_PathBuildWithSeparator(allocator, fs_path, Dqn_OSPathSeparatorString)
|
||||
|
||||
// NOTE: [$EXEC] Dqn_OSExec ////////////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_OS_Exit (int32_t exit_code);
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_ExecWait (Dqn_OSExecAsyncHandle handle, Dqn_Arena *arena, Dqn_ErrorSink *error);
|
||||
DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_ErrorSink *error);
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_Exec (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_Arena *arena, Dqn_ErrorSink *error);
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_ExecOrAbort(Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_Arena *arena);
|
||||
DQN_API void Dqn_OS_Exit (int32_t exit_code);
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_ExecWait (Dqn_OSExecAsyncHandle handle, Dqn_Arena *arena, Dqn_ErrorSink *error);
|
||||
DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_ErrorSink *error);
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_Exec (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_Arena *arena, Dqn_ErrorSink *error);
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_ExecOrAbort (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_Arena *arena);
|
||||
#define Dqn_OS_ExecOrAbort_TLS(...) Dqn_OS_ExecOrAbort(__VA_ARGS__, Dqn_TLS_TopArena())
|
||||
|
||||
// NOTE: [$SEMA] Dqn_OSSemaphore ///////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_NO_SEMAPHORE)
|
||||
@ -381,6 +402,7 @@ DQN_API void Dqn_OS_MutexUnlock(Dqn_OSMutex *mutex);
|
||||
DQN_API bool Dqn_OS_ThreadInit (Dqn_OSThread *thread, Dqn_OSThreadFunc *func, void *user_context);
|
||||
DQN_API void Dqn_OS_ThreadDeinit(Dqn_OSThread thread);
|
||||
DQN_API uint32_t Dqn_OS_ThreadID ();
|
||||
DQN_API void Dqn_OS_ThreadSetTLS(Dqn_TLS *tls);
|
||||
#endif // !defined(DQN_NO_THREAD)
|
||||
|
||||
// NOTE: [$HTTP] Dqn_OSHttp ////////////////////////////////////////////////////////////////////////
|
||||
|
119
dqn_os_posix.cpp
119
dqn_os_posix.cpp
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
#include <dirent.h> // readdir, opendir, closedir
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -271,6 +273,12 @@ DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo(Dqn_Str8 path)
|
||||
// shoddily deal with this.
|
||||
result.create_time_in_s =
|
||||
DQN_MIN(result.last_access_time_in_s, result.last_write_time_in_s);
|
||||
|
||||
if (S_ISDIR(file_stat.st_mode)) {
|
||||
result.type = Dqn_OSPathInfoType_Directory;
|
||||
} else if (S_ISREG(file_stat.st_mode)) {
|
||||
result.type = Dqn_OSPathInfoType_File;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -350,11 +358,11 @@ DQN_API bool Dqn_OS_CopyFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_Er
|
||||
result = (bytes_written == stat_existing.st_size);
|
||||
if (!result) {
|
||||
int error_code = errno;
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 file_size_str8 =
|
||||
Dqn_U64ToByteSizeStr8(scratch.arena, stat_existing.st_size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_U64ToByteSizeStr8(tmem.arena, stat_existing.st_size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_Str8 bytes_written_str8 =
|
||||
Dqn_U64ToByteSizeStr8(scratch.arena, bytes_written, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_U64ToByteSizeStr8(tmem.arena, bytes_written, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_ErrorSink_MakeF(error,
|
||||
error_code,
|
||||
"Failed to copy file '%.*s' to '%.*s', we copied %.*s but the file "
|
||||
@ -399,29 +407,17 @@ DQN_API bool Dqn_OS_MoveFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_Er
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_DirExists(Dqn_Str8 path)
|
||||
{
|
||||
bool result = false;
|
||||
if (!Dqn_Str8_HasData(path))
|
||||
return result;
|
||||
|
||||
struct stat stat_result;
|
||||
if (lstat(path.data, &stat_result) != -1)
|
||||
result = S_ISDIR(stat_result.st_mode);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_MakeDir(Dqn_Str8 path)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
bool result = true;
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
bool result = true;
|
||||
|
||||
// TODO(doyle): Implement this without using the path indexes, it's not
|
||||
// necessary. See Windows implementation.
|
||||
Dqn_usize path_indexes_size = 0;
|
||||
uint16_t path_indexes[64] = {};
|
||||
|
||||
Dqn_Str8 copy = Dqn_Str8_Copy(scratch.arena, path);
|
||||
Dqn_Str8 copy = Dqn_Str8_Copy(tmem.arena, path);
|
||||
for (Dqn_usize index = copy.size - 1; index < copy.size; index--) {
|
||||
bool first_char = index == (copy.size - 1);
|
||||
char ch = copy.data[index];
|
||||
@ -467,6 +463,51 @@ DQN_API bool Dqn_OS_MakeDir(Dqn_Str8 path)
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_DirExists(Dqn_Str8 path)
|
||||
{
|
||||
bool result = false;
|
||||
if (!Dqn_Str8_HasData(path))
|
||||
return result;
|
||||
|
||||
struct stat stat_result;
|
||||
if (lstat(path.data, &stat_result) != -1)
|
||||
result = S_ISDIR(stat_result.st_mode);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_DirIterate(Dqn_Str8 path, Dqn_OS_DirIterator *it)
|
||||
{
|
||||
if (!it->handle) {
|
||||
it->handle = opendir(path.data);
|
||||
if (!it->handle)
|
||||
return false;
|
||||
}
|
||||
|
||||
struct dirent *entry;
|
||||
for (;;) {
|
||||
entry = readdir(DQN_CAST(DIR*)it->handle);
|
||||
if (entry == NULL)
|
||||
break;
|
||||
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
|
||||
Dqn_usize name_size = Dqn_CStr8_Size(entry->d_name);
|
||||
Dqn_usize clamped_size = DQN_MIN(sizeof(it->buffer) - 1, name_size);
|
||||
DQN_ASSERTF(name_size == clamped_size, "name: %s, name_size: %zu, clamped_size: %zu", entry->d_name, name_size, clamped_size);
|
||||
DQN_MEMCPY(it->buffer, entry->d_name, clamped_size);
|
||||
it->buffer[clamped_size] = 0;
|
||||
it->file_name = Dqn_Str8_Init(it->buffer, clamped_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
closedir(DQN_CAST(DIR*)it->handle);
|
||||
it->handle = NULL;
|
||||
it->file_name = {};
|
||||
it->buffer[0] = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSFile Dqn_OS_FileOpen(Dqn_Str8 path,
|
||||
Dqn_OSFileOpen open_mode,
|
||||
@ -548,9 +589,9 @@ DQN_API bool Dqn_OS_FileRead(Dqn_OSFile *file, void *buffer, Dqn_usize size, Dqn
|
||||
return false;
|
||||
|
||||
if (fread(buffer, size, 1, DQN_CAST(FILE *) file->handle) != 1) {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 buffer_size_str8 =
|
||||
Dqn_U64ToByteSizeStr8(scratch.arena, size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_U64ToByteSizeStr8(tmem.arena, size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_ErrorSink_MakeF(
|
||||
error, 1, "Failed to read %.*s from file", DQN_STR_FMT(buffer_size_str8));
|
||||
return false;
|
||||
@ -568,9 +609,9 @@ Dqn_OS_FileWritePtr(Dqn_OSFile *file, void const *buffer, Dqn_usize size, Dqn_Er
|
||||
fwrite(buffer, DQN_CAST(Dqn_usize) size, 1 /*count*/, DQN_CAST(FILE *) file->handle) ==
|
||||
1 /*count*/;
|
||||
if (!result) {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 buffer_size_str8 =
|
||||
Dqn_U64ToByteSizeStr8(scratch.arena, size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_U64ToByteSizeStr8(tmem.arena, size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_ErrorSink_MakeF(
|
||||
error, 1, "Failed to write buffer (%s) to file handle", DQN_STR_FMT(buffer_size_str8));
|
||||
}
|
||||
@ -663,17 +704,16 @@ DQN_API Dqn_OSExecResult Dqn_OS_ExecWait(Dqn_OSExecAsyncHandle handle,
|
||||
|
||||
// NOTE: Read the data from the read end of the pipe
|
||||
if (result.os_error_code == 0) {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
|
||||
if (arena && handle.stdout_read) {
|
||||
char buffer[4096];
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = scratch.arena;
|
||||
Dqn_Str8Builder builder = Dqn_Str8Builder_Init(tmem.arena);
|
||||
for (;;) {
|
||||
ssize_t bytes_read =
|
||||
read(stdout_pipe[Dqn_OSPipeType__Read], buffer, sizeof(buffer));
|
||||
if (bytes_read <= 0)
|
||||
break;
|
||||
Dqn_Str8Builder_AppendF(&builder, "%.*s", bytes_read, buffer);
|
||||
Dqn_Str8Builder_AddF(&builder, "%.*s", bytes_read, buffer);
|
||||
}
|
||||
|
||||
result.stdout_text = Dqn_Str8Builder_Build(&builder, arena);
|
||||
@ -681,14 +721,13 @@ DQN_API Dqn_OSExecResult Dqn_OS_ExecWait(Dqn_OSExecAsyncHandle handle,
|
||||
|
||||
if (arena && handle.stderr_read) {
|
||||
char buffer[4096];
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = scratch.arena;
|
||||
Dqn_Str8Builder builder = Dqn_Str8Builder_Init(tmem.arena);
|
||||
for (;;) {
|
||||
ssize_t bytes_read =
|
||||
read(stderr_pipe[Dqn_OSPipeType__Read], buffer, sizeof(buffer));
|
||||
if (bytes_read <= 0)
|
||||
break;
|
||||
Dqn_Str8Builder_AppendF(&builder, "%.*s", bytes_read, buffer);
|
||||
Dqn_Str8Builder_AddF(&builder, "%.*s", bytes_read, buffer);
|
||||
}
|
||||
|
||||
result.stderr_text = Dqn_Str8Builder_Build(&builder, arena);
|
||||
@ -713,8 +752,8 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line,
|
||||
if (cmd_line.size == 0)
|
||||
return result;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 cmd_rendered = Dqn_Slice_Str8Render(scratch.arena, cmd_line, DQN_STR8(" "));
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 cmd_rendered = Dqn_Slice_Str8Render(tmem.arena, cmd_line, DQN_STR8(" "));
|
||||
int stdout_pipe[Dqn_OSPipeType__Count] = {};
|
||||
int stderr_pipe[Dqn_OSPipeType__Count] = {};
|
||||
|
||||
@ -807,7 +846,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line,
|
||||
|
||||
// NOTE: Convert the command into something suitable for execvp
|
||||
char **argv =
|
||||
Dqn_Arena_NewArray(scratch.arena, char *, cmd_line.size + 1 /*null*/, Dqn_ZeroMem_Yes);
|
||||
Dqn_Arena_NewArray(tmem.arena, char *, cmd_line.size + 1 /*null*/, Dqn_ZeroMem_Yes);
|
||||
if (!argv) {
|
||||
result.exit_code = -1;
|
||||
Dqn_ErrorSink_MakeF(
|
||||
@ -820,7 +859,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line,
|
||||
|
||||
for (Dqn_usize arg_index = 0; arg_index < cmd_line.size; arg_index++) {
|
||||
Dqn_Str8 arg = cmd_line.data[arg_index];
|
||||
argv[arg_index] = Dqn_Str8_Copy(scratch.arena, arg).data; // NOTE: Copy string to guarantee it is null-terminated
|
||||
argv[arg_index] = Dqn_Str8_Copy(tmem.arena, arg).data; // NOTE: Copy string to guarantee it is null-terminated
|
||||
}
|
||||
|
||||
// NOTE: Change the working directory if there is one
|
||||
@ -1008,9 +1047,7 @@ DQN_API void Dqn_OS_MutexUnlock(Dqn_OSMutex *mutex)
|
||||
// NOTE: [$THRD] Dqn_OSThread /////////////////////////////////////////////////////////////////////
|
||||
static void *Dqn_OS_ThreadFunc_(void *user_context)
|
||||
{
|
||||
Dqn_OSThread *thread = DQN_CAST(Dqn_OSThread *) user_context;
|
||||
Dqn_OS_SemaphoreWait(&thread->init_semaphore, DQN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
thread->func(thread);
|
||||
Dqn_OS_ThreadExecute_(user_context);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -1167,12 +1204,12 @@ DQN_API void Dqn_OS_HttpRequestAsync(Dqn_OSHttpResponse *response,
|
||||
|
||||
response->arena = arena;
|
||||
response->builder.arena =
|
||||
response->scratch_arena ? response->scratch_arena : &response->tmp_arena;
|
||||
response->tmem_arena ? response->tmem_arena : &response->tmp_arena;
|
||||
|
||||
Dqn_Arena *scratch_arena = response->scratch_arena;
|
||||
Dqn_Scratch scratch_ = Dqn_Scratch_Get(arena);
|
||||
if (!scratch_arena)
|
||||
scratch_arena = scratch_.arena;
|
||||
Dqn_Arena *tmem = response->tmem_arena;
|
||||
Dqn_TLSTMem tmem_ = Dqn_TLS_TMem(arena);
|
||||
if (!tmem)
|
||||
tmem = tmem_.arena;
|
||||
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
emscripten_fetch_attr_t fetch_attribs = {};
|
||||
|
273
dqn_os_win32.cpp
273
dqn_os_win32.cpp
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\
|
||||
@ -15,8 +16,9 @@
|
||||
// dqn_os_win32.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$VMEM] Dqn_OSMem //////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: [$VMEM] Dqn_OSMem /////////////////////////////////////////////////////////////////////////
|
||||
static uint32_t Dqn_OS_MemConvertPageToOSFlags_(uint32_t protect)
|
||||
{
|
||||
DQN_ASSERT((protect & ~Dqn_OSMemPage_All) == 0);
|
||||
@ -217,8 +219,8 @@ DQN_API Dqn_Str8 Dqn_OS_EXEPath(Dqn_Arena *arena)
|
||||
Dqn_Str8 result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_Str16 exe_dir16 = Dqn_Win_EXEPathW(scratch.arena);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(arena);
|
||||
Dqn_Str16 exe_dir16 = Dqn_Win_EXEPathW(t_mem.arena);
|
||||
result = Dqn_Win_Str16ToStr8(arena, exe_dir16);
|
||||
return result;
|
||||
}
|
||||
@ -259,8 +261,8 @@ DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo(Dqn_Str8 path)
|
||||
if (!Dqn_Str8_HasData(path))
|
||||
return result;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem.arena, path);
|
||||
|
||||
WIN32_FILE_ATTRIBUTE_DATA attrib_data = {};
|
||||
if (!GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data))
|
||||
@ -292,8 +294,8 @@ DQN_API bool Dqn_OS_PathDelete(Dqn_Str8 path)
|
||||
if (!Dqn_Str8_HasData(path))
|
||||
return result;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem.arena, path);
|
||||
if (path16.size) {
|
||||
result = DeleteFileW(path16.data);
|
||||
if (!result)
|
||||
@ -308,8 +310,8 @@ DQN_API bool Dqn_OS_FileExists(Dqn_Str8 path)
|
||||
if (!Dqn_Str8_HasData(path))
|
||||
return result;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem.arena, path);
|
||||
if (path16.size) {
|
||||
WIN32_FILE_ATTRIBUTE_DATA attrib_data = {};
|
||||
if (GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data)) {
|
||||
@ -323,15 +325,15 @@ DQN_API bool Dqn_OS_FileExists(Dqn_Str8 path)
|
||||
DQN_API bool Dqn_OS_CopyFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_ErrorSink *error)
|
||||
{
|
||||
bool result = false;
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str16 src16 = Dqn_Win_Str8ToStr16(scratch.arena, src);
|
||||
Dqn_Str16 dest16 = Dqn_Win_Str8ToStr16(scratch.arena, dest);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str16 src16 = Dqn_Win_Str8ToStr16(t_mem.arena, src);
|
||||
Dqn_Str16 dest16 = Dqn_Win_Str8ToStr16(t_mem.arena, dest);
|
||||
|
||||
int fail_if_exists = overwrite == false;
|
||||
result = CopyFileW(src16.data, dest16.data, fail_if_exists) != 0;
|
||||
|
||||
if (!result) {
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
|
||||
Dqn_ErrorSink_MakeF(error,
|
||||
win_error.code,
|
||||
"Failed to copy file '%.*s' to '%.*s': (%u) %.*s",
|
||||
@ -346,9 +348,9 @@ DQN_API bool Dqn_OS_CopyFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_Er
|
||||
DQN_API bool Dqn_OS_MoveFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_ErrorSink *error)
|
||||
{
|
||||
bool result = false;
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str16 src16 = Dqn_Win_Str8ToStr16(scratch.arena, src);
|
||||
Dqn_Str16 dest16 = Dqn_Win_Str8ToStr16(scratch.arena, dest);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str16 src16 = Dqn_Win_Str8ToStr16(t_mem.arena, src);
|
||||
Dqn_Str16 dest16 = Dqn_Win_Str8ToStr16(t_mem.arena, dest);
|
||||
|
||||
unsigned long flags = MOVEFILE_COPY_ALLOWED;
|
||||
if (overwrite) {
|
||||
@ -357,7 +359,7 @@ DQN_API bool Dqn_OS_MoveFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_Er
|
||||
|
||||
result = MoveFileExW(src16.data, dest16.data, flags) != 0;
|
||||
if (!result) {
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
|
||||
Dqn_ErrorSink_MakeF(error,
|
||||
win_error.code,
|
||||
"Failed to move file '%.*s' to '%.*s': (%u) %.*s",
|
||||
@ -371,9 +373,9 @@ DQN_API bool Dqn_OS_MoveFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_Er
|
||||
|
||||
DQN_API bool Dqn_OS_MakeDir(Dqn_Str8 path)
|
||||
{
|
||||
bool result = true;
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path);
|
||||
bool result = true;
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem.arena, path);
|
||||
|
||||
// NOTE: Go back from the end of the string to all the directories in the
|
||||
// string, and try to create them. Since Win32 API cannot create
|
||||
@ -427,8 +429,8 @@ DQN_API bool Dqn_OS_DirExists(Dqn_Str8 path)
|
||||
if (!Dqn_Str8_HasData(path))
|
||||
return result;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem.arena, path);
|
||||
if (path16.size) {
|
||||
WIN32_FILE_ATTRIBUTE_DATA attrib_data = {};
|
||||
if (GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data)) {
|
||||
@ -474,8 +476,8 @@ DQN_API Dqn_OSFile Dqn_OS_FileOpen(Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint
|
||||
access_mode |= GENERIC_EXECUTE;
|
||||
}
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch.arena, path);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem.arena, path);
|
||||
void *handle = CreateFileW(/*LPCWSTR lpFileName*/ path16.data,
|
||||
/*DWORD dwDesiredAccess*/ access_mode,
|
||||
/*DWORD dwShareMode*/ 0,
|
||||
@ -485,7 +487,7 @@ DQN_API Dqn_OSFile Dqn_OS_FileOpen(Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint
|
||||
/*HANDLE hTemplateFile*/ nullptr);
|
||||
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
|
||||
result.error = true;
|
||||
Dqn_ErrorSink_MakeF(error, win_error.code, "Failed to open file at '%.*s': '%.*s'", DQN_STR_FMT(path), DQN_STR_FMT(win_error.msg));
|
||||
return result;
|
||||
@ -500,9 +502,9 @@ DQN_API bool Dqn_OS_FileRead(Dqn_OSFile *file, void *buffer, Dqn_usize size, Dqn
|
||||
if (!file || !file->handle || file->error || !buffer || size <= 0)
|
||||
return false;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
|
||||
if (!DQN_CHECK(size <= (unsigned long)-1)) {
|
||||
Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8(scratch.arena, size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8(t_mem.arena, size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_ErrorSink_MakeF(
|
||||
error,
|
||||
1 /*error_code*/,
|
||||
@ -518,13 +520,13 @@ DQN_API bool Dqn_OS_FileRead(Dqn_OSFile *file, void *buffer, Dqn_usize size, Dqn
|
||||
/*LPDWORD lpNumberOfByesRead*/ &bytes_read,
|
||||
/*LPOVERLAPPED lpOverlapped*/ nullptr);
|
||||
if (read_result == 0) {
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
|
||||
Dqn_ErrorSink_MakeF(error, win_error.code, "Failed to read data from file: (%u) %.*s", win_error.code, DQN_STR_FMT(win_error.msg));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bytes_read != size) {
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
|
||||
Dqn_ErrorSink_MakeF(
|
||||
error,
|
||||
win_error.code,
|
||||
@ -554,9 +556,9 @@ DQN_API bool Dqn_OS_FileWritePtr(Dqn_OSFile *file, void const *buffer, Dqn_usize
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||
Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8(scratch.arena, size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
|
||||
Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8(t_mem.arena, size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_ErrorSink_MakeF(error, win_error.code, "Failed to write buffer (%.*s) to file handle: %.*s", DQN_STR_FMT(buffer_size_str8), DQN_STR_FMT(win_error.msg));
|
||||
}
|
||||
return result;
|
||||
@ -594,69 +596,106 @@ DQN_API Dqn_OSExecResult Dqn_OS_ExecWait(Dqn_OSExecAsyncHandle handle, Dqn_Arena
|
||||
return result;
|
||||
}
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
DWORD exec_result = WaitForSingleObject(handle.process, INFINITE);
|
||||
CloseHandle(handle.stdout_write);
|
||||
CloseHandle(handle.stderr_write);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(arena);
|
||||
Dqn_Str8Builder stdout_builder = {};
|
||||
Dqn_Str8Builder stderr_builder = {};
|
||||
if (arena) {
|
||||
stdout_builder.arena = t_mem.arena;
|
||||
stderr_builder.arena = t_mem.arena;
|
||||
}
|
||||
|
||||
if (exec_result == WAIT_FAILED) {
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||
result.os_error_code = win_error.code;
|
||||
Dqn_ErrorSink_MakeF(error, result.os_error_code, "Executed command failed to terminate: %.*s", DQN_STR_FMT(win_error.msg));
|
||||
CloseHandle(handle.process);
|
||||
} else {
|
||||
// NOTE: Get exit code /////////////////////////////////////////////////////////////////////
|
||||
DWORD const SLOW_WAIT_TIME_MS = 100;
|
||||
DWORD const FAST_WAIT_TIME_MS = 20;
|
||||
DWORD wait_ms = FAST_WAIT_TIME_MS;
|
||||
DWORD exec_result = WAIT_TIMEOUT;
|
||||
while (exec_result == WAIT_TIMEOUT) {
|
||||
exec_result = WaitForSingleObject(handle.process, wait_ms);
|
||||
if (exec_result == WAIT_FAILED) {
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
|
||||
result.os_error_code = win_error.code;
|
||||
Dqn_ErrorSink_MakeF(error, result.os_error_code, "Executed command failed to terminate: %.*s", DQN_STR_FMT(win_error.msg));
|
||||
} else if (DQN_CHECK(exec_result == WAIT_TIMEOUT || exec_result == WAIT_OBJECT_0)) {
|
||||
// NOTE: If the pipes are full, the process will block. We
|
||||
// periodically flush the pipes to make sure this doesn't happen
|
||||
|
||||
// NOTE: Read stdout from process //////////////////////////////////////////////////////
|
||||
// TODO: Allow the user to pump the execution of the command w/ a user passed in buffer.
|
||||
char output_buffer[DQN_KILOBYTES(8)];
|
||||
DWORD stdout_bytes_available = 0;
|
||||
if (PeekNamedPipe(handle.stdout_read, nullptr, 0, nullptr, &stdout_bytes_available, nullptr)) {
|
||||
if (stdout_bytes_available) {
|
||||
for (;;) {
|
||||
DWORD bytes_read = 0;
|
||||
BOOL success = ReadFile(handle.stdout_read, output_buffer, sizeof(output_buffer), &bytes_read, NULL);
|
||||
if (!success || bytes_read == 0)
|
||||
break;
|
||||
|
||||
if (handle.stdout_write && arena)
|
||||
Dqn_Str8Builder_AddF(&stdout_builder, "%.*s", bytes_read, output_buffer);
|
||||
|
||||
if (bytes_read < sizeof(output_buffer))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Read stderr from process //////////////////////////////////////////////////////
|
||||
DWORD stderr_bytes_available = 0;
|
||||
if (PeekNamedPipe(handle.stderr_read, nullptr, 0, nullptr, &stderr_bytes_available, nullptr)) {
|
||||
if (stderr_bytes_available) {
|
||||
for (;;) {
|
||||
DWORD bytes_read = 0;
|
||||
BOOL success = ReadFile(handle.stderr_read, output_buffer, sizeof(output_buffer), &bytes_read, NULL);
|
||||
if (!success || bytes_read == 0)
|
||||
break;
|
||||
|
||||
if (handle.stderr_read && arena)
|
||||
Dqn_Str8Builder_AddF(&stderr_builder, "%.*s", bytes_read, output_buffer);
|
||||
|
||||
if (bytes_read < sizeof(output_buffer))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: If we produced some data, we'll sleep for a short amount of
|
||||
// time because if we have the presence of data it's typical we deal
|
||||
// with N amount of. Otherwise if we timed-out and we didn't produce
|
||||
// any data, its more likely this is a long running operation so we
|
||||
// sleep longer to avoid slamming the CPU.
|
||||
if (stdout_bytes_available || stderr_bytes_available) {
|
||||
wait_ms = FAST_WAIT_TIME_MS;
|
||||
} else {
|
||||
wait_ms = SLOW_WAIT_TIME_MS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Get stdout/stderr. If no arena is passed this is a no-op //////////////////////////////
|
||||
result.stdout_text = Dqn_Str8Builder_Build(&stdout_builder, arena);
|
||||
result.stderr_text = Dqn_Str8Builder_Build(&stderr_builder, arena);
|
||||
|
||||
// NOTE: Get exit code /////////////////////////////////////////////////////////////////////////
|
||||
if (exec_result != WAIT_FAILED) {
|
||||
DWORD exit_status;
|
||||
if (GetExitCodeProcess(handle.process, &exit_status)) {
|
||||
result.exit_code = exit_status;
|
||||
} else {
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
|
||||
result.os_error_code = win_error.code;
|
||||
Dqn_ErrorSink_MakeF(error,
|
||||
result.os_error_code,
|
||||
"Failed to retrieve command exit code: %.*s",
|
||||
DQN_STR_FMT(win_error.msg));
|
||||
}
|
||||
|
||||
CloseHandle(handle.process);
|
||||
|
||||
// NOTE: Read stdout from process //////////////////////////////////////////////////////////
|
||||
if (arena && handle.stdout_write) {
|
||||
char stdout_buffer[4096];
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = scratch.arena;
|
||||
for (;;) {
|
||||
DWORD bytes_read = 0;
|
||||
BOOL success = ReadFile(handle.stdout_read, stdout_buffer, sizeof(stdout_buffer), &bytes_read, NULL);
|
||||
if (!success || bytes_read == 0)
|
||||
break;
|
||||
|
||||
Dqn_Str8Builder_AppendF(&builder, "%.*s", bytes_read, stdout_buffer);
|
||||
}
|
||||
|
||||
result.stdout_text = Dqn_Str8Builder_Build(&builder, arena);
|
||||
}
|
||||
|
||||
// NOTE: Read stderr from process //////////////////////////////////////////////////////////
|
||||
if (arena && handle.stderr_read) {
|
||||
char stderr_buffer[4096];
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = scratch.arena;
|
||||
for (;;) {
|
||||
DWORD bytes_read = 0;
|
||||
BOOL success = ReadFile(handle.stderr_read, stderr_buffer, sizeof(stderr_buffer), &bytes_read, NULL);
|
||||
if (!success || bytes_read == 0)
|
||||
break;
|
||||
|
||||
Dqn_Str8Builder_AppendF(&builder, "%.*s", bytes_read, stderr_buffer);
|
||||
}
|
||||
|
||||
result.stderr_text = Dqn_Str8Builder_Build(&builder, arena);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Cleanup ///////////////////////////////////////////////////////////////////////////////
|
||||
CloseHandle(handle.stdout_write);
|
||||
CloseHandle(handle.stderr_write);
|
||||
CloseHandle(handle.stdout_read);
|
||||
CloseHandle(handle.stderr_read);
|
||||
CloseHandle(handle.process);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -667,10 +706,10 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line, Dqn
|
||||
if (cmd_line.size == 0)
|
||||
return result;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 cmd_rendered = Dqn_Slice_Str8Render(scratch.arena, cmd_line, DQN_STR8(" "));
|
||||
Dqn_Str16 cmd16 = Dqn_Win_Str8ToStr16(scratch.arena, cmd_rendered);
|
||||
Dqn_Str16 working_dir16 = Dqn_Win_Str8ToStr16(scratch.arena, working_dir);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 cmd_rendered = Dqn_Slice_Str8Render(t_mem.arena, cmd_line, DQN_STR8(" "));
|
||||
Dqn_Str16 cmd16 = Dqn_Win_Str8ToStr16(t_mem.arena, cmd_rendered);
|
||||
Dqn_Str16 working_dir16 = Dqn_Win_Str8ToStr16(t_mem.arena, working_dir);
|
||||
|
||||
// NOTE: Stdout/err security attributes ////////////////////////////////////////////////////////
|
||||
SECURITY_ATTRIBUTES save_std_security_attribs = {};
|
||||
@ -689,7 +728,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line, Dqn
|
||||
|
||||
if (Dqn_Bit_IsSet(exec_flags, Dqn_OSExecFlag_SaveStdout)) {
|
||||
if (!CreatePipe(&stdout_read, &stdout_write, &save_std_security_attribs, /*nSize*/ 0)) {
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
|
||||
result.os_error_code = win_error.code;
|
||||
Dqn_ErrorSink_MakeF(
|
||||
error,
|
||||
@ -701,7 +740,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line, Dqn
|
||||
}
|
||||
|
||||
if (!SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0)) {
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
|
||||
result.os_error_code = win_error.code;
|
||||
Dqn_ErrorSink_MakeF(error,
|
||||
result.os_error_code,
|
||||
@ -729,7 +768,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line, Dqn
|
||||
stderr_write = stdout_write;
|
||||
} else {
|
||||
if (!CreatePipe(&stderr_read, &stderr_write, &save_std_security_attribs, /*nSize*/ 0)) {
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
|
||||
result.os_error_code = win_error.code;
|
||||
Dqn_ErrorSink_MakeF(
|
||||
error,
|
||||
@ -741,7 +780,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line, Dqn
|
||||
}
|
||||
|
||||
if (!SetHandleInformation(stderr_read, HANDLE_FLAG_INHERIT, 0)) {
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
|
||||
result.os_error_code = win_error.code;
|
||||
Dqn_ErrorSink_MakeF(error,
|
||||
result.os_error_code,
|
||||
@ -764,7 +803,7 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line, Dqn
|
||||
startup_info.dwFlags |= STARTF_USESTDHANDLES;
|
||||
BOOL create_result = CreateProcessW(nullptr, cmd16.data, nullptr, nullptr, true, 0, nullptr, working_dir16.data, &startup_info, &proc_info);
|
||||
if (!create_result) {
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||
Dqn_WinError win_error = Dqn_Win_LastError(t_mem.arena);
|
||||
result.os_error_code = win_error.code;
|
||||
Dqn_ErrorSink_MakeF(error,
|
||||
result.os_error_code,
|
||||
@ -882,9 +921,7 @@ DQN_API void Dqn_OS_MutexUnlock(Dqn_OSMutex *mutex)
|
||||
// NOTE: [$THRD] Dqn_OSThread /////////////////////////////////////////////////////////////////////
|
||||
static DWORD __stdcall Dqn_OS_ThreadFunc_(void *user_context)
|
||||
{
|
||||
Dqn_OSThread *thread = DQN_CAST(Dqn_OSThread *)user_context;
|
||||
Dqn_OS_SemaphoreWait(&thread->init_semaphore, DQN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
thread->func(thread);
|
||||
Dqn_OS_ThreadExecute_(user_context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1001,7 +1038,7 @@ void Dqn_OS_HttpRequestWin32Callback(HINTERNET session, DWORD *dwContext, DWORD
|
||||
DWORD bytes_read = dwStatusInformationLength;
|
||||
if (bytes_read) {
|
||||
Dqn_Str8 prev_buffer = Dqn_Str8_Init(DQN_CAST(char *) lpvStatusInformation, bytes_read);
|
||||
Dqn_Str8Builder_AppendRef(&response->builder, prev_buffer);
|
||||
Dqn_Str8Builder_AddRef(&response->builder, prev_buffer);
|
||||
|
||||
void *buffer = Dqn_Arena_Alloc(response->builder.arena, READ_BUFFER_SIZE, 1 /*align*/, Dqn_ZeroMem_No);
|
||||
if (!WinHttpReadData(request, buffer, READ_BUFFER_SIZE, nullptr))
|
||||
@ -1050,12 +1087,12 @@ DQN_API void Dqn_OS_HttpRequestAsync(Dqn_OSHttpResponse *response,
|
||||
return;
|
||||
|
||||
response->arena = arena;
|
||||
response->builder.arena = response->scratch_arena ? response->scratch_arena : &response->tmp_arena;
|
||||
response->builder.arena = response->tmem_arena ? response->tmem_arena : &response->tmp_arena;
|
||||
|
||||
Dqn_Arena *scratch_arena = response->scratch_arena;
|
||||
Dqn_Scratch scratch_ = Dqn_Scratch_Get(arena);
|
||||
if (!scratch_arena) {
|
||||
scratch_arena = scratch_.arena;
|
||||
Dqn_Arena *t_mem_arena = response->tmem_arena;
|
||||
Dqn_TLSTMem t_mem_ = Dqn_TLS_TMem(arena);
|
||||
if (!t_mem_arena) {
|
||||
t_mem_arena = t_mem_.arena;
|
||||
}
|
||||
|
||||
Dqn_WinError error = {};
|
||||
@ -1088,15 +1125,15 @@ DQN_API void Dqn_OS_HttpRequestAsync(Dqn_OSHttpResponse *response,
|
||||
return;
|
||||
}
|
||||
|
||||
Dqn_Str16 host16 = Dqn_Win_Str8ToStr16(scratch_arena, host);
|
||||
Dqn_Str16 host16 = Dqn_Win_Str8ToStr16(t_mem_arena, host);
|
||||
response->win32_request_connection = WinHttpConnect(response->win32_request_session, host16.data, secure ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT, 0 /*reserved*/);
|
||||
if (!response->win32_request_connection) {
|
||||
error = Dqn_Win_LastError(&response->tmp_arena);
|
||||
return;
|
||||
}
|
||||
|
||||
Dqn_Str16 method16 = Dqn_Win_Str8ToStr16(scratch_arena, method);
|
||||
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(scratch_arena, path);
|
||||
Dqn_Str16 method16 = Dqn_Win_Str8ToStr16(t_mem_arena, method);
|
||||
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16(t_mem_arena, path);
|
||||
response->win32_request_handle = WinHttpOpenRequest(response->win32_request_connection,
|
||||
method16.data,
|
||||
path16.data,
|
||||
@ -1109,7 +1146,7 @@ DQN_API void Dqn_OS_HttpRequestAsync(Dqn_OSHttpResponse *response,
|
||||
return;
|
||||
}
|
||||
|
||||
Dqn_Str16 headers16 = Dqn_Win_Str8ToStr16(scratch_arena, headers);
|
||||
Dqn_Str16 headers16 = Dqn_Win_Str8ToStr16(t_mem_arena, headers);
|
||||
response->on_complete_semaphore = Dqn_OS_SemaphoreInit(0);
|
||||
if (!WinHttpSendRequest(response->win32_request_handle,
|
||||
headers16.data,
|
||||
@ -1295,13 +1332,13 @@ DQN_API Dqn_Str8 Dqn_Win_Str16ToStr8(Dqn_Arena *arena, Dqn_Str16 src)
|
||||
// NOTE: Windows Executable Directory //////////////////////////////////////////
|
||||
DQN_API Dqn_Str16 Dqn_Win_EXEPathW(Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(arena);
|
||||
Dqn_Str16 result = {};
|
||||
Dqn_usize module_size = 0;
|
||||
wchar_t *module_path = nullptr;
|
||||
do {
|
||||
module_size += 256;
|
||||
module_path = Dqn_Arena_NewArray(scratch.arena, wchar_t, module_size, Dqn_ZeroMem_No);
|
||||
module_path = Dqn_Arena_NewArray(t_mem.arena, wchar_t, module_size, Dqn_ZeroMem_No);
|
||||
if (!module_path)
|
||||
return result;
|
||||
module_size = DQN_CAST(Dqn_usize)GetModuleFileNameW(nullptr /*module*/, module_path, DQN_CAST(int)module_size);
|
||||
@ -1321,13 +1358,13 @@ DQN_API Dqn_Str16 Dqn_Win_EXEPathW(Dqn_Arena *arena)
|
||||
DQN_API Dqn_Str16 Dqn_Win_EXEDirW(Dqn_Arena *arena)
|
||||
{
|
||||
// TODO(doyle): Implement a Dqn_Str16_BinarySearchReverse
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(arena);
|
||||
Dqn_Str16 result = {};
|
||||
Dqn_usize module_size = 0;
|
||||
wchar_t *module_path = nullptr;
|
||||
do {
|
||||
module_size += 256;
|
||||
module_path = Dqn_Arena_NewArray(scratch.arena, wchar_t, module_size, Dqn_ZeroMem_No);
|
||||
module_path = Dqn_Arena_NewArray(t_mem.arena, wchar_t, module_size, Dqn_ZeroMem_No);
|
||||
if (!module_path)
|
||||
return result;
|
||||
module_size = DQN_CAST(Dqn_usize)GetModuleFileNameW(nullptr /*module*/, module_path, DQN_CAST(int)module_size);
|
||||
@ -1346,9 +1383,9 @@ DQN_API Dqn_Str16 Dqn_Win_EXEDirW(Dqn_Arena *arena)
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Win_WorkingDir(Dqn_Arena *arena, Dqn_Str8 suffix)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_Str16 suffix16 = Dqn_Win_Str8ToStr16(scratch.arena, suffix);
|
||||
Dqn_Str16 dir16 = Dqn_Win_WorkingDirW(scratch.arena, suffix16);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(arena);
|
||||
Dqn_Str16 suffix16 = Dqn_Win_Str8ToStr16(t_mem.arena, suffix);
|
||||
Dqn_Str16 dir16 = Dqn_Win_WorkingDirW(t_mem.arena, suffix16);
|
||||
Dqn_Str8 result = Dqn_Win_Str16ToStr8(arena, dir16);
|
||||
return result;
|
||||
}
|
||||
@ -1359,15 +1396,15 @@ DQN_API Dqn_Str16 Dqn_Win_WorkingDirW(Dqn_Arena *arena, Dqn_Str16 suffix)
|
||||
Dqn_Str16 result = {};
|
||||
|
||||
// NOTE: required_size is the size required *including* the null-terminator
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(arena);
|
||||
unsigned long required_size = GetCurrentDirectoryW(0, nullptr);
|
||||
unsigned long desired_size = required_size + DQN_CAST(unsigned long) suffix.size;
|
||||
|
||||
wchar_t *scratch_w_path = Dqn_Arena_NewArray(scratch.arena, wchar_t, desired_size, Dqn_ZeroMem_No);
|
||||
if (!scratch_w_path)
|
||||
wchar_t *t_mem_w_path = Dqn_Arena_NewArray(t_mem.arena, wchar_t, desired_size, Dqn_ZeroMem_No);
|
||||
if (!t_mem_w_path)
|
||||
return result;
|
||||
|
||||
unsigned long bytes_written_wo_null_terminator = GetCurrentDirectoryW(desired_size, scratch_w_path);
|
||||
unsigned long bytes_written_wo_null_terminator = GetCurrentDirectoryW(desired_size, t_mem_w_path);
|
||||
if ((bytes_written_wo_null_terminator + 1) != required_size) {
|
||||
// TODO(dqn): Error
|
||||
return result;
|
||||
@ -1378,7 +1415,7 @@ DQN_API Dqn_Str16 Dqn_Win_WorkingDirW(Dqn_Arena *arena, Dqn_Str16 suffix)
|
||||
return result;
|
||||
|
||||
if (suffix.size) {
|
||||
DQN_MEMCPY(w_path, scratch_w_path, sizeof(*scratch_w_path) * bytes_written_wo_null_terminator);
|
||||
DQN_MEMCPY(w_path, t_mem_w_path, sizeof(*t_mem_w_path) * bytes_written_wo_null_terminator);
|
||||
DQN_MEMCPY(w_path + bytes_written_wo_null_terminator, suffix.data, sizeof(suffix.data[0]) * suffix.size);
|
||||
w_path[desired_size] = 0;
|
||||
}
|
||||
@ -1427,7 +1464,7 @@ DQN_API bool Dqn_Win_FolderIterate(Dqn_Str8 path, Dqn_Win_FolderIterator *it)
|
||||
if (!Dqn_Str8_HasData(path) || !it || path.size <= 0)
|
||||
return false;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_TLSTMem t_mem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Win_FolderIteratorW wide_it = {};
|
||||
Dqn_Str16 path16 = {};
|
||||
if (it->handle) {
|
||||
@ -1444,12 +1481,12 @@ DQN_API bool Dqn_Win_FolderIterate(Dqn_Str8 path, Dqn_Win_FolderIterator *it)
|
||||
// add those characters in this branch, so overwrite the null
|
||||
// character, add the glob and re-null terminate the buffer.
|
||||
if (needs_asterisks)
|
||||
adjusted_path = Dqn_OS_PathConvertF(scratch.arena, "%.*s*", DQN_STR_FMT(path));
|
||||
adjusted_path = Dqn_OS_PathF(t_mem.arena, "%.*s*", DQN_STR_FMT(path));
|
||||
else
|
||||
adjusted_path = Dqn_OS_PathConvertF(scratch.arena, "%.*s/*", DQN_STR_FMT(path));
|
||||
adjusted_path = Dqn_OS_PathF(t_mem.arena, "%.*s/*", DQN_STR_FMT(path));
|
||||
}
|
||||
|
||||
path16 = Dqn_Win_Str8ToStr16(scratch.arena, adjusted_path);
|
||||
path16 = Dqn_Win_Str8ToStr16(t_mem.arena, adjusted_path);
|
||||
if (path16.size <= 0) // Conversion error
|
||||
return false;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\
|
||||
@ -15,6 +16,7 @@
|
||||
// dqn_os_win32.h -- Windows only functions, and, implementation of the OS layer
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
struct Dqn_WinError
|
||||
{
|
||||
|
261
dqn_string.cpp
261
dqn_string.cpp
@ -95,7 +95,7 @@ DQN_API bool Dqn_Str8_IsAll(Dqn_Str8 string, Dqn_Str8IsAll is_all)
|
||||
|
||||
case Dqn_Str8IsAll_Hex: {
|
||||
Dqn_Str8 trimmed = Dqn_Str8_TrimPrefix(string, DQN_STR8("0x"), Dqn_Str8EqCase_Insensitive);
|
||||
for (Dqn_usize index = 0; result && index < string.size; index++) {
|
||||
for (Dqn_usize index = 0; result && index < trimmed.size; index++) {
|
||||
char ch = trimmed.data[index];
|
||||
result = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F');
|
||||
}
|
||||
@ -221,7 +221,7 @@ DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8_SplitAlloc(Dqn_Arena *arena, Dqn_Str8 strin
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8Array(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size)
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8Array(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size, Dqn_Str8EqCase eq_case)
|
||||
{
|
||||
Dqn_Str8FindResult result = {};
|
||||
if (!Dqn_Str8_HasData(string) || !find || find_size == 0)
|
||||
@ -231,7 +231,7 @@ DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8Array(Dqn_Str8 string, Dqn_Str8 cons
|
||||
for (Dqn_usize find_index = 0; find_index < find_size; find_index++) {
|
||||
Dqn_Str8 find_item = find[find_index];
|
||||
Dqn_Str8 string_slice = Dqn_Str8_Slice(string, index, find_item.size);
|
||||
if (Dqn_Str8_Eq(string_slice, find_item)) {
|
||||
if (Dqn_Str8_Eq(string_slice, find_item, eq_case)) {
|
||||
result.found = true;
|
||||
result.index = index;
|
||||
result.start_to_before_match = Dqn_Str8_Init(string.data, index);
|
||||
@ -244,9 +244,9 @@ DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8Array(Dqn_Str8 string, Dqn_Str8 cons
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8(Dqn_Str8 string, Dqn_Str8 find)
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8(Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8EqCase eq_case)
|
||||
{
|
||||
Dqn_Str8FindResult result = Dqn_Str8_FindStr8Array(string, &find, 1);
|
||||
Dqn_Str8FindResult result = Dqn_Str8_FindStr8Array(string, &find, 1, eq_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -403,6 +403,12 @@ DQN_API Dqn_Str8 Dqn_Str8_TrimPrefix(Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8E
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimHexPrefix(Dqn_Str8 string)
|
||||
{
|
||||
Dqn_Str8 result = Dqn_Str8_TrimPrefix(string, DQN_STR8("0x"), Dqn_Str8EqCase_Insensitive);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimSuffix(Dqn_Str8 string, Dqn_Str8 suffix, Dqn_Str8EqCase eq_case)
|
||||
{
|
||||
Dqn_Str8 result = string;
|
||||
@ -591,11 +597,10 @@ DQN_API Dqn_Str8 Dqn_Str8_Replace(Dqn_Str8 string,
|
||||
return result;
|
||||
}
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_Str8Builder string_builder = {};
|
||||
string_builder.arena = scratch.arena;
|
||||
Dqn_usize max = string.size - find.size;
|
||||
Dqn_usize head = start_index;
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(arena);
|
||||
Dqn_Str8Builder string_builder = Dqn_Str8Builder_Init(tmem.arena);
|
||||
Dqn_usize max = string.size - find.size;
|
||||
Dqn_usize head = start_index;
|
||||
|
||||
for (Dqn_usize tail = head; tail <= max; tail++) {
|
||||
Dqn_Str8 check = Dqn_Str8_Slice(string, tail, find.size);
|
||||
@ -608,12 +613,12 @@ DQN_API Dqn_Str8 Dqn_Str8_Replace(Dqn_Str8 string,
|
||||
// a replacement action, otherwise we have a special case for no
|
||||
// replacements, where the entire string gets copied.
|
||||
Dqn_Str8 slice = Dqn_Str8_Init(string.data, head);
|
||||
Dqn_Str8Builder_AppendRef(&string_builder, slice);
|
||||
Dqn_Str8Builder_AddRef(&string_builder, slice);
|
||||
}
|
||||
|
||||
Dqn_Str8 range = Dqn_Str8_Slice(string, head, (tail - head));
|
||||
Dqn_Str8Builder_AppendRef(&string_builder, range);
|
||||
Dqn_Str8Builder_AppendRef(&string_builder, replace);
|
||||
Dqn_Str8Builder_AddRef(&string_builder, range);
|
||||
Dqn_Str8Builder_AddRef(&string_builder, replace);
|
||||
head = tail + find.size;
|
||||
tail += find.size - 1; // NOTE: -1 since the for loop will post increment us past the end of the find string
|
||||
}
|
||||
@ -623,7 +628,7 @@ DQN_API Dqn_Str8 Dqn_Str8_Replace(Dqn_Str8 string,
|
||||
result = Dqn_Str8_Copy(arena, string);
|
||||
} else {
|
||||
Dqn_Str8 remainder = Dqn_Str8_Init(string.data + head, string.size - head);
|
||||
Dqn_Str8Builder_AppendRef(&string_builder, remainder);
|
||||
Dqn_Str8Builder_AddRef(&string_builder, remainder);
|
||||
result = Dqn_Str8Builder_Build(&string_builder, arena);
|
||||
}
|
||||
|
||||
@ -693,6 +698,8 @@ DQN_API Dqn_Str8 Dqn_Str8_Alloc(Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem ze
|
||||
result.data = Dqn_Arena_NewArray(arena, char, size + 1, zero_mem);
|
||||
if (result.data)
|
||||
result.size = size;
|
||||
if (zero_mem == Dqn_ZeroMem_No)
|
||||
result.data[result.size] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -717,18 +724,46 @@ DQN_API Dqn_Str8 Dqn_Str8_Copy(Dqn_Arena *arena, Dqn_Str8 string)
|
||||
}
|
||||
|
||||
// NOTE: [$STRB] Dqn_Str8Builder ////////////////////////////////////////////////////////////////
|
||||
DQN_API bool Dqn_Str8Builder_AppendRefArray(Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> array)
|
||||
DQN_API Dqn_Str8Builder Dqn_Str8Builder_Init(Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_Str8Builder result = {};
|
||||
result.arena = arena;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8Builder Dqn_Str8Builder_InitArrayRef(Dqn_Arena *arena,
|
||||
Dqn_Str8 const *strings,
|
||||
Dqn_usize size)
|
||||
{
|
||||
Dqn_Str8Builder result = Dqn_Str8Builder_Init(arena);
|
||||
Dqn_Str8Builder_AddArrayRef(&result, strings, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8Builder Dqn_Str8Builder_InitArrayCopy(Dqn_Arena *arena,
|
||||
Dqn_Str8 const *strings,
|
||||
Dqn_usize size)
|
||||
{
|
||||
Dqn_Str8Builder result = Dqn_Str8Builder_Init(arena);
|
||||
Dqn_Str8Builder_AddArrayCopy(&result, strings, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Str8Builder_AddArrayRef(Dqn_Str8Builder *builder, Dqn_Str8 const *strings, Dqn_usize size)
|
||||
{
|
||||
if (!builder)
|
||||
return false;
|
||||
|
||||
for (Dqn_Str8 string : array) {
|
||||
if (!builder || !string.data || string.size <= 0)
|
||||
return false;
|
||||
if (!strings || size <= 0)
|
||||
return true;
|
||||
|
||||
Dqn_Str8Link *link = Dqn_Arena_New(builder->arena, Dqn_Str8Link, Dqn_ZeroMem_No);
|
||||
if (!link)
|
||||
return false;
|
||||
Dqn_Str8Link *links = Dqn_Arena_NewArray(builder->arena, Dqn_Str8Link, size, Dqn_ZeroMem_No);
|
||||
if (!links)
|
||||
return false;
|
||||
|
||||
DQN_FOR_UINDEX(index, size) {
|
||||
Dqn_Str8 string = strings[index];
|
||||
Dqn_Str8Link *link = links + index;
|
||||
|
||||
link->string = string;
|
||||
link->next = NULL;
|
||||
@ -746,69 +781,203 @@ DQN_API bool Dqn_Str8Builder_AppendRefArray(Dqn_Str8Builder *builder, Dqn_Slice<
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Str8Builder_AppendCopyArray(Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> array)
|
||||
DQN_API bool Dqn_Str8Builder_AddArrayCopy(Dqn_Str8Builder *builder, Dqn_Str8 const *strings, Dqn_usize size)
|
||||
{
|
||||
for (Dqn_Str8 string : array) {
|
||||
Dqn_Str8 copy = Dqn_Str8_Copy(builder->arena, string);
|
||||
if (!Dqn_Str8Builder_AppendRef(builder, copy))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!builder)
|
||||
return false;
|
||||
|
||||
DQN_API bool Dqn_Str8Builder_AppendFV(Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_Str8 string = Dqn_Str8_InitFV(builder->arena, fmt, args);
|
||||
if (string.size == 0)
|
||||
if (!strings || size <= 0)
|
||||
return true;
|
||||
|
||||
Dqn_ArenaTempMem tmp_mem = Dqn_Arena_TempMemBegin(builder->arena);
|
||||
bool result = true;
|
||||
Dqn_Str8 *strings_copy = Dqn_Arena_NewArray(builder->arena, Dqn_Str8, size, Dqn_ZeroMem_No);
|
||||
DQN_FOR_UINDEX (index, size) {
|
||||
strings_copy[index] = Dqn_Str8_Copy(builder->arena, strings[index]);
|
||||
if (strings_copy[index].size != strings[index].size) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result)
|
||||
result = Dqn_Str8Builder_AddArrayRef(builder, strings_copy, size);
|
||||
|
||||
if (!result)
|
||||
Dqn_Arena_TempMemEnd(tmp_mem);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Str8Builder_AddSliceRef(Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> array)
|
||||
{
|
||||
bool result = Dqn_Str8Builder_AddArrayRef(builder, array.data, array.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Str8Builder_AddSliceCopy(Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> array)
|
||||
{
|
||||
bool result = Dqn_Str8Builder_AddArrayCopy(builder, array.data, array.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Str8Builder_AddRef(Dqn_Str8Builder *builder, Dqn_Str8 string)
|
||||
{
|
||||
bool result = Dqn_Str8Builder_AddArrayRef(builder, &string, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Str8Builder_AddCopy(Dqn_Str8Builder *builder, Dqn_Str8 string)
|
||||
{
|
||||
bool result = Dqn_Str8Builder_AddArrayCopy(builder, &string, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Str8Builder_AddFV(Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_Str8 string = Dqn_Str8_InitFV(builder->arena, fmt, args);
|
||||
Dqn_ArenaTempMem temp_mem = Dqn_Arena_TempMemBegin(builder->arena);
|
||||
bool result = Dqn_Str8Builder_AppendRef(builder, string);
|
||||
bool result = Dqn_Str8Builder_AddRef(builder, string);
|
||||
if (!result)
|
||||
Dqn_Arena_TempMemEnd(temp_mem);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Str8Builder_AppendF(Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
DQN_API bool Dqn_Str8Builder_AddF(Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
bool result = Dqn_Str8Builder_AppendFV(builder, fmt, args);
|
||||
bool result = Dqn_Str8Builder_AddFV(builder, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Str8Builder_AppendRef(Dqn_Str8Builder *builder, Dqn_Str8 string)
|
||||
DQN_API bool Dqn_Str8Builder_AddBytesRef(Dqn_Str8Builder *builder, void const *ptr, Dqn_usize size)
|
||||
{
|
||||
Dqn_Slice<Dqn_Str8> array = Dqn_Slice_Init(&string, 1);
|
||||
bool result = Dqn_Str8Builder_AppendRefArray(builder, array);
|
||||
Dqn_Str8 input = Dqn_Str8_Init(ptr, size);
|
||||
bool result = Dqn_Str8Builder_AddRef(builder, input);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Str8Builder_AppendCopy(Dqn_Str8Builder *builder, Dqn_Str8 string)
|
||||
DQN_API bool Dqn_Str8Builder_AddBytesCopy(Dqn_Str8Builder *builder, void const *ptr, Dqn_usize size)
|
||||
{
|
||||
Dqn_Slice<Dqn_Str8> array = Dqn_Slice_Init(&string, 1);
|
||||
bool result = Dqn_Str8Builder_AppendCopyArray(builder, array);
|
||||
Dqn_Str8 input = Dqn_Str8_Init(ptr, size);
|
||||
bool result = Dqn_Str8Builder_AddCopy(builder, input);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool Dqn_Str8Builder_AddBuilder_(Dqn_Str8Builder *dest, Dqn_Str8Builder const *src, bool copy)
|
||||
{
|
||||
if (!dest)
|
||||
return false;
|
||||
if (!src)
|
||||
return true;
|
||||
|
||||
Dqn_Arena_TempMemBegin(dest->arena);
|
||||
Dqn_Str8Link *links = Dqn_Arena_NewArray(dest->arena, Dqn_Str8Link, src->count, Dqn_ZeroMem_No);
|
||||
if (!links)
|
||||
return false;
|
||||
|
||||
Dqn_Str8Link *first = nullptr;
|
||||
Dqn_Str8Link *last = nullptr;
|
||||
Dqn_usize link_index = 0;
|
||||
bool result = true;
|
||||
for (Dqn_Str8Link const *it = src->head; it; it = it->next) {
|
||||
Dqn_Str8Link *link = links + link_index++;
|
||||
link->next = nullptr;
|
||||
link->string = it->string;
|
||||
|
||||
if (copy) {
|
||||
link->string = Dqn_Str8_Copy(dest->arena, it->string);
|
||||
if (link->string.size != it->string.size) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (last) {
|
||||
last->next = link;
|
||||
} else {
|
||||
first = link;
|
||||
}
|
||||
last = link;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
if (dest->head)
|
||||
dest->tail->next = first;
|
||||
else
|
||||
dest->head = first;
|
||||
dest->tail = last;
|
||||
dest->count += src->count;
|
||||
dest->string_size += src->string_size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Str8Builder_AddBuilderRef(Dqn_Str8Builder *dest, Dqn_Str8Builder const *src)
|
||||
{
|
||||
bool result = Dqn_Str8Builder_AddBuilder_(dest, src, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Str8Builder_AddBuilderCopy(Dqn_Str8Builder *dest, Dqn_Str8Builder const *src)
|
||||
{
|
||||
bool result = Dqn_Str8Builder_AddBuilder_(dest, src, true);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Str8Builder_Erase(Dqn_Str8Builder *builder, Dqn_Str8 string)
|
||||
{
|
||||
for (Dqn_Str8Link **it = &builder->head; *it; it = &((*it)->next)) {
|
||||
if ((*it)->string == string) {
|
||||
*it = (*it)->next;
|
||||
builder->string_size -= string.size;
|
||||
builder->count -= 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8Builder Dqn_Str8Builder_Copy(Dqn_Arena *arena, Dqn_Str8Builder const *builder)
|
||||
{
|
||||
Dqn_Str8Builder result = Dqn_Str8Builder_Init(arena);
|
||||
Dqn_Str8Builder_AddBuilderCopy(&result, builder);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8Builder_Build(Dqn_Str8Builder const *builder, Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_Str8 result = Dqn_Str8Builder_BuildDelimited(builder, DQN_STR8(""), arena);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8Builder_BuildDelimited(Dqn_Str8Builder const *builder, Dqn_Str8 delimiter, Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_Str8 result = DQN_ZERO_INIT;
|
||||
if (!builder || builder->string_size <= 0 || builder->count <= 0)
|
||||
return result;
|
||||
|
||||
result.data = Dqn_Arena_NewArray(arena, char, builder->string_size + 1, Dqn_ZeroMem_No);
|
||||
Dqn_usize size_for_delimiter = Dqn_Str8_HasData(delimiter) ? ((builder->count - 1) * delimiter.size) : 0;
|
||||
result.data = Dqn_Arena_NewArray(arena,
|
||||
char,
|
||||
builder->string_size + size_for_delimiter + 1 /*null terminator*/,
|
||||
Dqn_ZeroMem_No);
|
||||
if (!result.data)
|
||||
return result;
|
||||
|
||||
for (Dqn_Str8Link *link = builder->head; link; link = link->next) {
|
||||
DQN_MEMCPY(result.data + result.size, link->string.data, link->string.size);
|
||||
result.size += link->string.size;
|
||||
if (link->next && Dqn_Str8_HasData(delimiter)) {
|
||||
DQN_MEMCPY(result.data + result.size, delimiter.data, delimiter.size);
|
||||
result.size += delimiter.size;
|
||||
}
|
||||
}
|
||||
|
||||
result.data[result.size] = 0;
|
||||
DQN_ASSERT(result.size == builder->string_size);
|
||||
DQN_ASSERT(result.size == builder->string_size + size_for_delimiter);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -850,13 +1019,13 @@ DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8Builder_BuildSlice(Dqn_Str8Builder const *bu
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Str8Builder_PrintF(Dqn_Str8Builder const *builder)
|
||||
DQN_API void Dqn_Str8Builder_Print(Dqn_Str8Builder const *builder)
|
||||
{
|
||||
for (Dqn_Str8Link *link = builder ? builder->head : nullptr; link; link = link->next)
|
||||
Dqn_Print(link->string);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Str8Builder_PrintLnF(Dqn_Str8Builder const *builder)
|
||||
DQN_API void Dqn_Str8Builder_PrintLn(Dqn_Str8Builder const *builder)
|
||||
{
|
||||
for (Dqn_Str8Link *link = builder ? builder->head : nullptr; link; link = link->next) {
|
||||
if (link->next) {
|
||||
|
200
dqn_string.h
200
dqn_string.h
@ -143,88 +143,123 @@ DQN_API bool operator!=
|
||||
#endif
|
||||
|
||||
// NOTE: [$STR8] Dqn_Str8 //////////////////////////////////////////////////////////////////////////
|
||||
#define DQN_STR8(string) Dqn_Str8{(char *)(string), (sizeof(string) - 1)}
|
||||
#define DQN_STR_FMT(string) (int)((string).size), (string).data
|
||||
#define Dqn_Str8_Init(data, size) Dqn_Str8{(char *)(data), (size_t)(size)}
|
||||
#define DQN_STR8(string) Dqn_Str8{(char *)(string), (sizeof(string) - 1)}
|
||||
#define DQN_STR_FMT(string) (int)((string).size), (string).data
|
||||
#define Dqn_Str8_Init(data, size) Dqn_Str8{(char *)(data), (size_t)(size)}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_InitCStr8 (char const *src);
|
||||
#define Dqn_Str8_HasData(string) ((string).data && (string).size)
|
||||
DQN_API bool Dqn_Str8_IsAll (Dqn_Str8 string, Dqn_Str8IsAll is_all);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_InitCStr8 (char const *src);
|
||||
#define Dqn_Str8_HasData(string) ((string).data && (string).size)
|
||||
DQN_API bool Dqn_Str8_IsAll (Dqn_Str8 string, Dqn_Str8IsAll is_all);
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_InitF (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_InitFV (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Alloc (Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_CopyCString (Dqn_Arena *arena, char const *string, Dqn_usize size);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Copy (Dqn_Arena *arena, Dqn_Str8 string);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_InitF (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
#define Dqn_Str8_InitF_TLS(...) Dqn_Str8_InitF(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DQN_API Dqn_Str8 Dqn_Str8_InitFV (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
#define Dqn_Str8_InitFV_TLS(...) Dqn_Str8_InitFV(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Alloc (Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem);
|
||||
#define Dqn_Str8_Alloc_TLS(...) Dqn_Str8_Alloc(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DQN_API Dqn_Str8 Dqn_Str8_CopyCString (Dqn_Arena *arena, char const *string, Dqn_usize size);
|
||||
#define Dqn_Str8_CopyCString_TLS(...) Dqn_Str8_CopyCString(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Copy (Dqn_Arena *arena, Dqn_Str8 string);
|
||||
#define Dqn_Str8_Copy_TLS(...) Dqn_Str8_Copy(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Slice (Dqn_Str8 string, Dqn_usize offset, Dqn_usize size);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Advance (Dqn_Str8 string, Dqn_usize amount);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_NextLine (Dqn_Str8 string);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitArray (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplit (Dqn_Str8 string, Dqn_Str8 find);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLastArray (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLast (Dqn_Str8 string, Dqn_Str8 find);
|
||||
DQN_API Dqn_usize Dqn_Str8_Split (Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *splits, Dqn_usize splits_count, Dqn_Str8SplitIncludeEmptyStrings mode);
|
||||
DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8_SplitAlloc (Dqn_Arena *arena, Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8SplitIncludeEmptyStrings mode);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Slice (Dqn_Str8 string, Dqn_usize offset, Dqn_usize size);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Advance (Dqn_Str8 string, Dqn_usize amount);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_NextLine (Dqn_Str8 string);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitArray (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplit (Dqn_Str8 string, Dqn_Str8 find);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLastArray (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitLast (Dqn_Str8 string, Dqn_Str8 find);
|
||||
DQN_API Dqn_usize Dqn_Str8_Split (Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *splits, Dqn_usize splits_count, Dqn_Str8SplitIncludeEmptyStrings mode);
|
||||
DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8_SplitAlloc (Dqn_Arena *arena, Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8SplitIncludeEmptyStrings mode);
|
||||
#define Dqn_Str8_SplitAlloc_TLS(...) Dqn_Str8_SplitAlloc(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8Array (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size);
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8 (Dqn_Str8 string, Dqn_Str8 find);
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_Find (Dqn_Str8 string, uint32_t flags);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Segment (Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_ReverseSegment (Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char);
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8Array (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size, Dqn_Str8EqCase eq_case);
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindStr8 (Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8EqCase eq_case);
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_Find (Dqn_Str8 string, uint32_t flags);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Segment (Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char);
|
||||
#define Dqn_Str8_Segment_TLS(...) Dqn_Str8_Segment(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DQN_API Dqn_Str8 Dqn_Str8_ReverseSegment (Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char);
|
||||
#define Dqn_Str8_ReverseSegment_TLS(...) Dqn_Str8_ReverseSegment(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
|
||||
DQN_API bool Dqn_Str8_Eq (Dqn_Str8 lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API bool Dqn_Str8_EqInsensitive (Dqn_Str8 lhs, Dqn_Str8 rhs);
|
||||
DQN_API bool Dqn_Str8_StartsWith (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API bool Dqn_Str8_StartsWithInsensitive (Dqn_Str8 string, Dqn_Str8 prefix);
|
||||
DQN_API bool Dqn_Str8_EndsWith (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API bool Dqn_Str8_EndsWithInsensitive (Dqn_Str8 string, Dqn_Str8 prefix);
|
||||
DQN_API bool Dqn_Str8_HasChar (Dqn_Str8 string, char ch);
|
||||
DQN_API bool Dqn_Str8_Eq (Dqn_Str8 lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API bool Dqn_Str8_EqInsensitive (Dqn_Str8 lhs, Dqn_Str8 rhs);
|
||||
DQN_API bool Dqn_Str8_StartsWith (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API bool Dqn_Str8_StartsWithInsensitive (Dqn_Str8 string, Dqn_Str8 prefix);
|
||||
DQN_API bool Dqn_Str8_EndsWith (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API bool Dqn_Str8_EndsWithInsensitive (Dqn_Str8 string, Dqn_Str8 prefix);
|
||||
DQN_API bool Dqn_Str8_HasChar (Dqn_Str8 string, char ch);
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimPrefix (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimSuffix (Dqn_Str8 string, Dqn_Str8 suffix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimAround (Dqn_Str8 string, Dqn_Str8 trim_string);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimWhitespaceAround (Dqn_Str8 string);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimByteOrderMark (Dqn_Str8 string);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimPrefix (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimHexPrefix (Dqn_Str8 string);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimSuffix (Dqn_Str8 string, Dqn_Str8 suffix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimAround (Dqn_Str8 string, Dqn_Str8 trim_string);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimWhitespaceAround (Dqn_Str8 string);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimByteOrderMark (Dqn_Str8 string);
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FileNameFromPath (Dqn_Str8 path);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FileNameNoExtension (Dqn_Str8 path);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FilePathNoExtension (Dqn_Str8 path);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FileExtension (Dqn_Str8 path);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FileNameFromPath (Dqn_Str8 path);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FileNameNoExtension (Dqn_Str8 path);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FilePathNoExtension (Dqn_Str8 path);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FileExtension (Dqn_Str8 path);
|
||||
|
||||
DQN_API Dqn_Str8ToU64Result Dqn_Str8_ToU64 (Dqn_Str8 string, char separator);
|
||||
DQN_API Dqn_Str8ToI64Result Dqn_Str8_ToI64 (Dqn_Str8 string, char separator);
|
||||
DQN_API Dqn_Str8ToU64Result Dqn_Str8_ToU64 (Dqn_Str8 string, char separator);
|
||||
DQN_API Dqn_Str8ToI64Result Dqn_Str8_ToI64 (Dqn_Str8 string, char separator);
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Replace (Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Arena *arena, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_ReplaceInsensitive (Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Arena *arena);
|
||||
DQN_API void Dqn_Str8_Remove (Dqn_Str8 *string, Dqn_usize offset, Dqn_usize size);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Replace (Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Arena *arena, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_ReplaceInsensitive (Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Arena *arena);
|
||||
DQN_API void Dqn_Str8_Remove (Dqn_Str8 *string, Dqn_usize offset, Dqn_usize size);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
DQN_API bool operator== (Dqn_Str8 const &lhs, Dqn_Str8 const &rhs);
|
||||
DQN_API bool operator!= (Dqn_Str8 const &lhs, Dqn_Str8 const &rhs);
|
||||
DQN_API bool operator== (Dqn_Str8 const &lhs, Dqn_Str8 const &rhs);
|
||||
DQN_API bool operator!= (Dqn_Str8 const &lhs, Dqn_Str8 const &rhs);
|
||||
#endif
|
||||
|
||||
// NOTE: [$FSTR] Dqn_Str8Builder ///////////////////////////////////////////////////////////////////
|
||||
DQN_API bool Dqn_Str8Builder_AppendRefArray (Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> string);
|
||||
DQN_API bool Dqn_Str8Builder_AppendCopyArray(Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> string);
|
||||
DQN_API bool Dqn_Str8Builder_AppendFV (Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API bool Dqn_Str8Builder_AppendF (Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API bool Dqn_Str8Builder_AppendRef (Dqn_Str8Builder *builder, Dqn_Str8 string);
|
||||
DQN_API bool Dqn_Str8Builder_AppendCopy (Dqn_Str8Builder *builder, Dqn_Str8 string);
|
||||
DQN_API Dqn_Str8 Dqn_Str8Builder_Build (Dqn_Str8Builder const *builder, Dqn_Arena *arena);
|
||||
DQN_API Dqn_Str8 Dqn_Str8Builder_BuildCRT (Dqn_Str8Builder const *builder);
|
||||
DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8Builder_BuildSlice (Dqn_Str8Builder const *builder, Dqn_Arena *arena);
|
||||
DQN_API void Dqn_Str8Builder_PrintF (Dqn_Str8Builder const *builder);
|
||||
// NOTE: [$STRB] Dqn_Str8Builder ///////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_Str8Builder Dqn_Str8Builder_Init (Dqn_Arena *arena);
|
||||
#define Dqn_Str8Builder_Init_TLS() Dqn_Str8Builder_Init(Dqn_TLS_TopArena())
|
||||
DQN_API Dqn_Str8Builder Dqn_Str8Builder_InitArrayRef (Dqn_Arena *arena, Dqn_Str8 const *strings, Dqn_usize size);
|
||||
#define Dqn_Str8Builder_InitArrayRef_TLS(...) Dqn_Str8Builder_InitArrayRef(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DQN_API Dqn_Str8Builder Dqn_Str8Builder_InitArrayCopy (Dqn_Arena *arena, Dqn_Str8 const *strings, Dqn_usize size);
|
||||
#define Dqn_Str8Builder_InitArrayCopy_TLS(...) Dqn_Str8Builder_InitArrayCopy(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
template <Dqn_usize N> Dqn_Str8Builder Dqn_Str8Builder_InitCArrayRef (Dqn_Arena *arena, Dqn_Str8 const (&array)[N]);
|
||||
#define Dqn_Str8Builder_InitCArrayRef_TLS(...) Dqn_Str8Builder_InitCArrayRef(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
template <Dqn_usize N> Dqn_Str8Builder Dqn_Str8Builder_InitCArrayCopy (Dqn_Arena *arena, Dqn_Str8 const (&array)[N]);
|
||||
#define Dqn_Str8Builder_InitCArrayCopy_TLS(...) Dqn_Str8Builder_InitCArrayCopy(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DQN_API bool Dqn_Str8Builder_AddArrayRef (Dqn_Str8Builder *builder, Dqn_Str8 const *strings, Dqn_usize size);
|
||||
DQN_API bool Dqn_Str8Builder_AddArrayCopy (Dqn_Str8Builder *builder, Dqn_Str8 const *strings, Dqn_usize size);
|
||||
template <Dqn_usize N> bool Dqn_Str8Builder_AddCArrayRef (Dqn_Str8Builder *builder, Dqn_Str8 const (&array)[N]);
|
||||
template <Dqn_usize N> bool Dqn_Str8Builder_AddCArrayCopy (Dqn_Str8Builder *builder, Dqn_Str8 const (&array)[N]);
|
||||
DQN_API bool Dqn_Str8Builder_AddSliceRef (Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> string);
|
||||
DQN_API bool Dqn_Str8Builder_AddSliceCopy (Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> string);
|
||||
DQN_API bool Dqn_Str8Builder_AddRef (Dqn_Str8Builder *builder, Dqn_Str8 string);
|
||||
DQN_API bool Dqn_Str8Builder_AddCopy (Dqn_Str8Builder *builder, Dqn_Str8 string);
|
||||
DQN_API bool Dqn_Str8Builder_AddFV (Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API bool Dqn_Str8Builder_AddF (Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API bool Dqn_Str8Builder_AddBytesRef (Dqn_Str8Builder *builder, void const *ptr, Dqn_usize size);
|
||||
DQN_API bool Dqn_Str8Builder_AddBytesCopy (Dqn_Str8Builder *builder, void const *ptr, Dqn_usize size);
|
||||
DQN_API bool Dqn_Str8Builder_AddBuilderRef (Dqn_Str8Builder *dest, Dqn_Str8Builder const *src);
|
||||
DQN_API bool Dqn_Str8Builder_AddBuilderCopy (Dqn_Str8Builder *dest, Dqn_Str8Builder const *src);
|
||||
DQN_API bool Dqn_Str8Builder_Erase (Dqn_Str8Builder *builder, Dqn_Str8 string);
|
||||
DQN_API Dqn_Str8Builder Dqn_Str8Builder_Copy (Dqn_Arena *arena, Dqn_Str8Builder const *builder);
|
||||
#define Dqn_Str8Builder_Copy_TLS(...) Dqn_Str8Builder_Copy(Dqn_TLS_TopArena(), ##__VA_ARGS__)
|
||||
DQN_API Dqn_Str8 Dqn_Str8Builder_Build (Dqn_Str8Builder const *builder, Dqn_Arena *arena);
|
||||
#define Dqn_Str8Builder_Build_TLS(...) Dqn_Str8Builder_Build(__VA_ARGS__, Dqn_TLS_TopArena())
|
||||
DQN_API Dqn_Str8 Dqn_Str8Builder_BuildDelimited (Dqn_Str8Builder const *builder, Dqn_Str8 delimiter, Dqn_Arena *arena);
|
||||
#define Dqn_Str8Builder_BuildDelimited_TLS(...) Dqn_Str8Builder_BuildDelimited(__VA_ARGS__, Dqn_TLS_TopArena())
|
||||
DQN_API Dqn_Str8 Dqn_Str8Builder_BuildCRT (Dqn_Str8Builder const *builder);
|
||||
DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8Builder_BuildSlice (Dqn_Str8Builder const *builder, Dqn_Arena *arena);
|
||||
#define Dqn_Str8Builder_BuildSlice_TLS(...) Dqn_Str8Builder_BuildSlice(__VA_ARGS__, Dqn_TLS_TopArena())
|
||||
DQN_API void Dqn_Str8Builder_Print (Dqn_Str8Builder const *builder);
|
||||
DQN_API void Dqn_Str8Builder_PrintLn (Dqn_Str8Builder const *builder);
|
||||
|
||||
// NOTE: [$FSTR] Dqn_FStr8 //////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_NO_FSTR8)
|
||||
template <Dqn_usize N> Dqn_FStr8<N> Dqn_FStr8_InitF (DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
template <Dqn_usize N> Dqn_usize Dqn_FStr8_Max (Dqn_FStr8<N> const *string);
|
||||
template <Dqn_usize N> void Dqn_FStr8_Clear (Dqn_FStr8<N> *string);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AppendFV (Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, va_list va);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AppendF (Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AppendCStr8 (Dqn_FStr8<N> *string, char const *value, Dqn_usize size);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_Append (Dqn_FStr8<N> *string, Dqn_Str8 value);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AddFV (Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, va_list va);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AddF (Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AddCStr8 (Dqn_FStr8<N> *string, char const *value, Dqn_usize size);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_Add (Dqn_FStr8<N> *string, Dqn_Str8 value);
|
||||
template <Dqn_usize N> Dqn_Str8 Dqn_FStr8_ToStr8 (Dqn_FStr8<N> const *string);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_Eq (Dqn_FStr8<N> const *lhs, Dqn_FStr8<N> const *rhs, Dqn_Str8EqCase eq_case);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_EqStr8 (Dqn_FStr8<N> const *lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case);
|
||||
@ -257,6 +292,31 @@ DQN_API char Dqn_Char_ToLower
|
||||
DQN_API int Dqn_UTF8_EncodeCodepoint (uint8_t utf8[4], uint32_t codepoint);
|
||||
DQN_API int Dqn_UTF16_EncodeCodepoint (uint16_t utf16[2], uint32_t codepoint);
|
||||
|
||||
// NOTE: [$STRB] Dqn_Str8Builder ///////////////////////////////////////////////////////////////////
|
||||
template <Dqn_usize N> Dqn_Str8Builder Dqn_Str8Builder_InitCArrayRef(Dqn_Arena *arena, Dqn_Str8 const (&array)[N])
|
||||
{
|
||||
Dqn_Str8Builder result = Dqn_Str8Builder_InitArrayRef(arena, array, N);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> Dqn_Str8Builder Dqn_Str8Builder_InitCArrayCopy(Dqn_Arena *arena, Dqn_Str8 const (&array)[N])
|
||||
{
|
||||
Dqn_Str8Builder result = Dqn_Str8Builder_InitArrayCopy(arena, array, N);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_Str8Builder_AddCArrayRef(Dqn_Str8Builder *builder, Dqn_Str8 const (&array)[N])
|
||||
{
|
||||
bool result = Dqn_Str8Builder_AddArrayRef(builder, array, N);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_Str8Builder_AddCArrayCopy(Dqn_Str8Builder *builder, Dqn_Str8 const (&array)[N])
|
||||
{
|
||||
bool result = Dqn_Str8Builder_AddArrayCopy(builder, array, N);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(DQN_NO_FSTR8)
|
||||
// NOTE: [$FSTR] Dqn_FStr8 /////////////////////////////////////////////////////////////////////////
|
||||
template <Dqn_usize N> Dqn_FStr8<N> Dqn_FStr8_InitF(DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
@ -265,7 +325,7 @@ template <Dqn_usize N> Dqn_FStr8<N> Dqn_FStr8_InitF(DQN_FMT_ATTRIB char const *f
|
||||
if (fmt) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_FStr8_AppendFV(&result, fmt, args);
|
||||
Dqn_FStr8_AddFV(&result, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
return result;
|
||||
@ -282,7 +342,7 @@ template <Dqn_usize N> void Dqn_FStr8_Clear(Dqn_FStr8<N> *string)
|
||||
*string = {};
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AppendFV(Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AddFV(Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
bool result = false;
|
||||
if (!string || !fmt)
|
||||
@ -299,19 +359,19 @@ template <Dqn_usize N> bool Dqn_FStr8_AppendFV(Dqn_FStr8<N> *string, DQN_FMT_ATT
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AppendF(Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AddF(Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
bool result = false;
|
||||
if (!string || !fmt)
|
||||
return result;
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
result = Dqn_FStr8_AppendFV(string, fmt, args);
|
||||
result = Dqn_FStr8_AddFV(string, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AppendCStr8(Dqn_FStr8<N> *string, char const *src, Dqn_usize size)
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AddCStr8(Dqn_FStr8<N> *string, char const *src, Dqn_usize size)
|
||||
{
|
||||
DQN_ASSERT(string->size <= N);
|
||||
bool result = false;
|
||||
@ -326,9 +386,9 @@ template <Dqn_usize N> bool Dqn_FStr8_AppendCStr8(Dqn_FStr8<N> *string, char con
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_Append(Dqn_FStr8<N> *string, Dqn_Str8 src)
|
||||
template <Dqn_usize N> bool Dqn_FStr8_Add(Dqn_FStr8<N> *string, Dqn_Str8 src)
|
||||
{
|
||||
bool result = Dqn_FStr8_AppendCStr8(string, src.data, src.size);
|
||||
bool result = Dqn_FStr8_AddCStr8(string, src.data, src.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1,127 +0,0 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$$\ $$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$\
|
||||
// \__$$ __|$$ | $$ |$$ __$$\ $$ _____|$$ __$$\ $$ __$$\
|
||||
// $$ | $$ | $$ |$$ | $$ |$$ | $$ / $$ |$$ | $$ |
|
||||
// $$ | $$$$$$$$ |$$$$$$$ |$$$$$\ $$$$$$$$ |$$ | $$ |
|
||||
// $$ | $$ __$$ |$$ __$$< $$ __| $$ __$$ |$$ | $$ |
|
||||
// $$ | $$ | $$ |$$ | $$ |$$ | $$ | $$ |$$ | $$ |
|
||||
// $$ | $$ | $$ |$$ | $$ |$$$$$$$$\ $$ | $$ |$$$$$$$ |
|
||||
// \__| \__| \__|\__| \__|\________|\__| \__|\_______/
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$\ $$\ $$$$$$$$\
|
||||
// $$ __$$\ $$ __$$\ $$$\ $$ |\__$$ __|$$ _____|$$ | $$ |\__$$ __|
|
||||
// $$ / \__|$$ / $$ |$$$$\ $$ | $$ | $$ | \$$\ $$ | $$ |
|
||||
// $$ | $$ | $$ |$$ $$\$$ | $$ | $$$$$\ \$$$$ / $$ |
|
||||
// $$ | $$ | $$ |$$ \$$$$ | $$ | $$ __| $$ $$< $$ |
|
||||
// $$ | $$\ $$ | $$ |$$ |\$$$ | $$ | $$ | $$ /\$$\ $$ |
|
||||
// \$$$$$$ | $$$$$$ |$$ | \$$ | $$ | $$$$$$$$\ $$ / $$ | $$ |
|
||||
// \______/ \______/ \__| \__| \__| \________|\__| \__| \__|
|
||||
//
|
||||
// dqn_thread_context.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
DQN_THREAD_LOCAL Dqn_ThreadContext g_dqn_thread_context;
|
||||
|
||||
// NOTE: [$TCTX] Dqn_ThreadContext /////////////////////////////////////////////////////////////////
|
||||
Dqn_Scratch::Dqn_Scratch(Dqn_ThreadContext *context, uint8_t context_index)
|
||||
{
|
||||
arena = context->scratch_arenas[context_index];
|
||||
temp_mem = Dqn_Arena_TempMemBegin(arena);
|
||||
destructed = false;
|
||||
}
|
||||
|
||||
Dqn_Scratch::~Dqn_Scratch()
|
||||
{
|
||||
DQN_ASSERT(destructed == false);
|
||||
Dqn_Arena_TempMemEnd(temp_mem);
|
||||
destructed = true;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Thread_ContextIsInit()
|
||||
{
|
||||
bool result = g_dqn_thread_context.init;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_ThreadContext *Dqn_ThreadContext_Get()
|
||||
{
|
||||
Dqn_ThreadContext *result = &g_dqn_thread_context;
|
||||
if (result->init)
|
||||
return result;
|
||||
|
||||
result->init = true;
|
||||
Dqn_ArenaCatalog *catalog = &g_dqn_library->arena_catalog;
|
||||
DQN_HARD_ASSERTF(g_dqn_library && g_dqn_library->lib_init, "Library must be initialised by calling Dqn_Library_Init()");
|
||||
|
||||
// NOTE: Setup scratch arenas //////////////////////////////////////////////////////////////////
|
||||
Dqn_TicketMutex_Begin(&g_dqn_library->thread_context_init_mutex);
|
||||
DQN_FOR_UINDEX (index, DQN_ARRAY_UCOUNT(result->scratch_arenas)) {
|
||||
|
||||
// NOTE: We allocate arenas so that they all come from the memory
|
||||
// allocated from the address space of this library. This allows the
|
||||
// library to be used across DLL boundaries for example as long as
|
||||
// across the boundaries the same g_dqn_library instance is shared.
|
||||
//
|
||||
// On unload of the DLL, the address space is deallocated. If we stored
|
||||
// these as TLS stack variables, these arenas would persist and point to
|
||||
// invalid memory addresses.
|
||||
Dqn_FStr8<128> label = Dqn_FStr8_InitF<128>("T%05u Scratch %zu", Dqn_OS_ThreadID(), index);
|
||||
|
||||
// NOTE: Hence here we search for the arena. If it already exists then
|
||||
// we are in that DLL boundary situation where the TLS data has been
|
||||
// reinitialised and zero-ed out. We will try and find the matching
|
||||
// arena in the catalog and re-use it.
|
||||
//
|
||||
// NOTE: This operation is so infrequent and the number of arenas one
|
||||
// has in their program should be low that a string look-up should be
|
||||
// cheap and fine.
|
||||
Dqn_ArenaCatalogItem *catalog_item = Dqn_ArenaCatalog_Find(catalog, Dqn_FStr8_ToStr8(&label));
|
||||
if (catalog_item == &catalog->sentinel) {
|
||||
Dqn_Arena *scratch = Dqn_ArenaCatalog_AllocLabelCopy(catalog, 0, 0, Dqn_ArenaFlag_AllocCanLeak, Dqn_FStr8_ToStr8(&label));
|
||||
result->scratch_arenas[index] = scratch;
|
||||
} else {
|
||||
// NOTE: Reuse the arena
|
||||
result->scratch_arenas[index] = catalog_item->arena;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Setup error sink //////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
Dqn_FStr8<128> label = Dqn_FStr8_InitF<128>("T%05u Error Sink", Dqn_OS_ThreadID());
|
||||
Dqn_ArenaCatalogItem *catalog_item = Dqn_ArenaCatalog_Find(catalog, Dqn_FStr8_ToStr8(&label));
|
||||
if (catalog_item == &catalog->sentinel) {
|
||||
result->error_sink_arena = Dqn_ArenaCatalog_AllocLabelCopy(catalog, 0, 0, Dqn_ArenaFlag_AllocCanLeak, Dqn_FStr8_ToStr8(&label));
|
||||
} else {
|
||||
// NOTE: Reuse the arena
|
||||
result->error_sink_arena = catalog_item->arena;
|
||||
}
|
||||
result->error_sink.arena = result->error_sink_arena;
|
||||
}
|
||||
Dqn_TicketMutex_End(&g_dqn_library->thread_context_init_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: Is there a way to handle conflict arenas without the user needing to
|
||||
// manually pass it in?
|
||||
DQN_API Dqn_Scratch Dqn_Scratch_Get(void const *conflict_arena)
|
||||
{
|
||||
Dqn_ThreadContext *context = Dqn_ThreadContext_Get();
|
||||
uint8_t context_index = (uint8_t)-1;
|
||||
for (uint8_t index = 0; index < DQN_ARRAY_UCOUNT(context->scratch_arenas); index++) {
|
||||
Dqn_Arena *arena = context->scratch_arenas[index];
|
||||
if (!conflict_arena || arena != conflict_arena) {
|
||||
context_index = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DQN_ASSERT(context_index != (uint8_t)-1);
|
||||
return Dqn_Scratch(context, context_index);
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$$\ $$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$\
|
||||
// \__$$ __|$$ | $$ |$$ __$$\ $$ _____|$$ __$$\ $$ __$$\
|
||||
// $$ | $$ | $$ |$$ | $$ |$$ | $$ / $$ |$$ | $$ |
|
||||
// $$ | $$$$$$$$ |$$$$$$$ |$$$$$\ $$$$$$$$ |$$ | $$ |
|
||||
// $$ | $$ __$$ |$$ __$$< $$ __| $$ __$$ |$$ | $$ |
|
||||
// $$ | $$ | $$ |$$ | $$ |$$ | $$ | $$ |$$ | $$ |
|
||||
// $$ | $$ | $$ |$$ | $$ |$$$$$$$$\ $$ | $$ |$$$$$$$ |
|
||||
// \__| \__| \__|\__| \__|\________|\__| \__|\_______/
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$\ $$\ $$$$$$$$\
|
||||
// $$ __$$\ $$ __$$\ $$$\ $$ |\__$$ __|$$ _____|$$ | $$ |\__$$ __|
|
||||
// $$ / \__|$$ / $$ |$$$$\ $$ | $$ | $$ | \$$\ $$ | $$ |
|
||||
// $$ | $$ | $$ |$$ $$\$$ | $$ | $$$$$\ \$$$$ / $$ |
|
||||
// $$ | $$ | $$ |$$ \$$$$ | $$ | $$ __| $$ $$< $$ |
|
||||
// $$ | $$\ $$ | $$ |$$ |\$$$ | $$ | $$ | $$ /\$$\ $$ |
|
||||
// \$$$$$$ | $$$$$$ |$$ | \$$ | $$ | $$$$$$$$\ $$ / $$ | $$ |
|
||||
// \______/ \______/ \__| \__| \__| \________|\__| \__| \__|
|
||||
//
|
||||
// dqn_thread_context.h -- Per thread data (e.g. scratch arenas)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
struct Dqn_ThreadContext
|
||||
{
|
||||
Dqn_b32 init;
|
||||
Dqn_Arena *scratch_arenas[2];
|
||||
Dqn_Arena *error_sink_arena;
|
||||
Dqn_CallSite call_site;
|
||||
Dqn_ErrorSink error_sink;
|
||||
};
|
||||
|
||||
struct Dqn_Scratch
|
||||
{
|
||||
Dqn_Scratch(Dqn_ThreadContext *context, uint8_t context_index);
|
||||
~Dqn_Scratch();
|
||||
|
||||
Dqn_Arena *arena;
|
||||
Dqn_b32 destructed;
|
||||
Dqn_ArenaTempMem temp_mem;
|
||||
};
|
||||
|
||||
DQN_API bool Dqn_ThreadContext_IsInit();
|
||||
DQN_API Dqn_ThreadContext *Dqn_ThreadContext_Get();
|
||||
#define Dqn_ThreadContext_SaveCallSite do { Dqn_ThreadContext_Get()->call_site = DQN_CALL_SITE; } while (0)
|
||||
DQN_API Dqn_Scratch Dqn_Scratch_Get(void const *conflict_arena);
|
126
dqn_tls.cpp
Normal file
126
dqn_tls.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$$\ $$\ $$$$$$\
|
||||
// \__$$ __|$$ | $$ __$$\
|
||||
// $$ | $$ | $$ / \__|
|
||||
// $$ | $$ | \$$$$$$\
|
||||
// $$ | $$ | \____$$\
|
||||
// $$ | $$ | $$\ $$ |
|
||||
// $$ | $$$$$$$$\\$$$$$$ |
|
||||
// \__| \________|\______/
|
||||
//
|
||||
// dqn_tls.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$TCTX] Dqn_TLS /////////////////////////////////////////////////////////////////
|
||||
Dqn_TLSTMem::Dqn_TLSTMem(Dqn_TLS *tls, uint8_t arena_index, Dqn_TLSPushTMem push_tmem)
|
||||
{
|
||||
DQN_ASSERT(arena_index == Dqn_TLSArena_TMem0 || arena_index == Dqn_TLSArena_TMem1);
|
||||
arena = tls->arenas + arena_index;
|
||||
temp_mem = Dqn_Arena_TempMemBegin(arena);
|
||||
destructed = false;
|
||||
push_arena = push_tmem;
|
||||
if (push_arena)
|
||||
Dqn_TLS_PushArena(arena);
|
||||
}
|
||||
|
||||
Dqn_TLSTMem::~Dqn_TLSTMem()
|
||||
{
|
||||
DQN_ASSERT(destructed == false);
|
||||
Dqn_Arena_TempMemEnd(temp_mem);
|
||||
destructed = true;
|
||||
if (push_arena)
|
||||
Dqn_TLS_PopArena();
|
||||
}
|
||||
|
||||
DQN_API void Dqn_TLS_Init(Dqn_TLS *tls)
|
||||
{
|
||||
DQN_CHECK(tls);
|
||||
if (tls->init)
|
||||
return;
|
||||
|
||||
DQN_FOR_UINDEX (index, Dqn_TLSArena_Count) {
|
||||
Dqn_Arena *arena = tls->arenas + index;
|
||||
switch (DQN_CAST(Dqn_TLSArena)index) {
|
||||
default: *arena = Dqn_Arena_InitSize(DQN_MEGABYTES(4), DQN_KILOBYTES(4), Dqn_ArenaFlag_AllocCanLeak); break;
|
||||
case Dqn_TLSArena_ErrorSink: *arena = Dqn_Arena_InitSize(DQN_KILOBYTES(64), DQN_KILOBYTES(4), Dqn_ArenaFlag_AllocCanLeak); break;
|
||||
case Dqn_TLSArena_Count: DQN_INVALID_CODE_PATH; break;
|
||||
}
|
||||
}
|
||||
|
||||
tls->thread_id = Dqn_OS_ThreadID();
|
||||
tls->error_sink.arena = tls->arenas + Dqn_TLSArena_ErrorSink;
|
||||
tls->init = true;
|
||||
}
|
||||
|
||||
DQN_API Dqn_TLS *Dqn_TLS_Get()
|
||||
{
|
||||
Dqn_TLS *result = g_dqn_os_thread_tls;
|
||||
// TODO(doyle): Fix stack-trace infinite loop with requiring the TLS that is not initialised
|
||||
// yet.
|
||||
DQN_ASSERT(
|
||||
g_dqn_library->lib_init &&
|
||||
"Library context must be be initialised first by calling Dqn_Library_Init. This "
|
||||
"initialises the main thread's TLS for you (no need to call Dqn_OS_ThreadSetTLS on main)");
|
||||
|
||||
DQN_ASSERT(result &&
|
||||
"Thread must be assigned the TLS with Dqn_OS_ThreadSetTLS. If the library is "
|
||||
"initialised, then, this thread was created without calling the set TLS function "
|
||||
"for the spawned thread.");
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Arena *Dqn_TLS_Arena()
|
||||
{
|
||||
Dqn_TLS *tls = Dqn_TLS_Get();
|
||||
Dqn_Arena *result = tls->arenas + Dqn_TLSArena_Main;
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: Is there a way to handle conflict arenas without the user needing to
|
||||
// manually pass it in?
|
||||
DQN_API Dqn_TLSTMem Dqn_TLS_GetTMem(void const *conflict_arena, Dqn_TLSPushTMem push_tmem)
|
||||
{
|
||||
Dqn_TLS *tls = Dqn_TLS_Get();
|
||||
uint8_t tls_index = (uint8_t)-1;
|
||||
for (uint8_t index = Dqn_TLSArena_TMem0; index <= Dqn_TLSArena_TMem1; index++) {
|
||||
Dqn_Arena *arena = tls->arenas + index;
|
||||
if (!conflict_arena || arena != conflict_arena) {
|
||||
tls_index = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DQN_ASSERT(tls_index != (uint8_t)-1);
|
||||
return Dqn_TLSTMem(tls, tls_index, push_tmem);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_TLS_PushArena(Dqn_Arena *arena)
|
||||
{
|
||||
DQN_ASSERT(arena);
|
||||
Dqn_TLS *tls = Dqn_TLS_Get();
|
||||
DQN_ASSERT(tls->arena_stack_index < DQN_ARRAY_UCOUNT(tls->arena_stack));
|
||||
tls->arena_stack[tls->arena_stack_index++] = arena;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_TLS_PopArena()
|
||||
{
|
||||
Dqn_TLS *tls = Dqn_TLS_Get();
|
||||
DQN_ASSERT(tls->arena_stack_index > 0);
|
||||
tls->arena_stack_index--;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Arena *Dqn_TLS_TopArena()
|
||||
{
|
||||
Dqn_TLS *tls = Dqn_TLS_Get();
|
||||
Dqn_Arena *result = nullptr;
|
||||
if (tls->arena_stack_index)
|
||||
result = tls->arena_stack[tls->arena_stack_index - 1];
|
||||
return result;
|
||||
}
|
84
dqn_tls.h
Normal file
84
dqn_tls.h
Normal file
@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$$\ $$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$\
|
||||
// \__$$ __|$$ | $$ |$$ __$$\ $$ _____|$$ __$$\ $$ __$$\
|
||||
// $$ | $$ | $$ |$$ | $$ |$$ | $$ / $$ |$$ | $$ |
|
||||
// $$ | $$$$$$$$ |$$$$$$$ |$$$$$\ $$$$$$$$ |$$ | $$ |
|
||||
// $$ | $$ __$$ |$$ __$$< $$ __| $$ __$$ |$$ | $$ |
|
||||
// $$ | $$ | $$ |$$ | $$ |$$ | $$ | $$ |$$ | $$ |
|
||||
// $$ | $$ | $$ |$$ | $$ |$$$$$$$$\ $$ | $$ |$$$$$$$ |
|
||||
// \__| \__| \__|\__| \__|\________|\__| \__|\_______/
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$\ $$\ $$$$$$$$\
|
||||
// $$ __$$\ $$ __$$\ $$$\ $$ |\__$$ __|$$ _____|$$ | $$ |\__$$ __|
|
||||
// $$ / \__|$$ / $$ |$$$$\ $$ | $$ | $$ | \$$\ $$ | $$ |
|
||||
// $$ | $$ | $$ |$$ $$\$$ | $$ | $$$$$\ \$$$$ / $$ |
|
||||
// $$ | $$ | $$ |$$ \$$$$ | $$ | $$ __| $$ $$< $$ |
|
||||
// $$ | $$\ $$ | $$ |$$ |\$$$ | $$ | $$ | $$ /\$$\ $$ |
|
||||
// \$$$$$$ | $$$$$$ |$$ | \$$ | $$ | $$$$$$$$\ $$ / $$ | $$ |
|
||||
// \______/ \______/ \__| \__| \__| \________|\__| \__| \__|
|
||||
//
|
||||
// dqn_thread_context.h -- Per thread data (e.g. scratch arenas)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
enum Dqn_TLSArena
|
||||
{
|
||||
Dqn_TLSArena_Main, // NOTE: Arena for Permanent allocations
|
||||
Dqn_TLSArena_ErrorSink, // NOTE: Arena for logging error information for this thread
|
||||
|
||||
// NOTE: Per-thread scratch arenas (2 to prevent aliasing)
|
||||
Dqn_TLSArena_TMem0,
|
||||
Dqn_TLSArena_TMem1,
|
||||
|
||||
Dqn_TLSArena_Count,
|
||||
};
|
||||
|
||||
struct Dqn_TLS
|
||||
{
|
||||
Dqn_b32 init; // Flag to track if Thread has been initialised
|
||||
uint64_t thread_id;
|
||||
Dqn_CallSite call_site; // Stores call-site information when requested by thread
|
||||
Dqn_ErrorSink error_sink; // Error handling state
|
||||
Dqn_Arena arenas[Dqn_TLSArena_Count];
|
||||
|
||||
// Push and pop arenas onto the stack. Functions suffixed 'TLS' will use
|
||||
// these arenas for memory allocation.
|
||||
Dqn_Arena *arena_stack[8];
|
||||
Dqn_usize arena_stack_index;
|
||||
};
|
||||
|
||||
// Push the temporary memory arena when retrieved, popped when the arena goes
|
||||
// out of scope. Pushed arenas are used automatically as the allocator in TLS
|
||||
// suffixed function.
|
||||
enum Dqn_TLSPushTMem
|
||||
{
|
||||
Dqn_TLSPushTMem_No,
|
||||
Dqn_TLSPushTMem_Yes,
|
||||
};
|
||||
|
||||
struct Dqn_TLSTMem
|
||||
{
|
||||
Dqn_TLSTMem(Dqn_TLS *context, uint8_t context_index, Dqn_TLSPushTMem push_scratch);
|
||||
~Dqn_TLSTMem();
|
||||
|
||||
Dqn_Arena *arena;
|
||||
Dqn_b32 destructed;
|
||||
Dqn_TLSPushTMem push_arena;
|
||||
Dqn_ArenaTempMem temp_mem;
|
||||
};
|
||||
|
||||
DQN_API void Dqn_TLS_Init(Dqn_TLS *tls);
|
||||
DQN_API Dqn_TLS * Dqn_TLS_Get();
|
||||
#define Dqn_TLS_SaveCallSite do { Dqn_TLS_Get()->call_site = DQN_CALL_SITE; } while (0)
|
||||
DQN_API Dqn_TLSTMem Dqn_TLS_GetTMem(void const *conflict_arena, Dqn_TLSPushTMem push_tmp_mem);
|
||||
#define Dqn_TLS_TMem(...) Dqn_TLS_GetTMem(__VA_ARGS__, Dqn_TLSPushTMem_No)
|
||||
#define Dqn_TLS_PushTMem(...) Dqn_TLS_GetTMem(__VA_ARGS__, Dqn_TLSPushTMem_Yes)
|
||||
DQN_API void Dqn_TLS_PushArena(Dqn_Arena *arena);
|
||||
DQN_API void Dqn_TLS_PopArena();
|
||||
DQN_API Dqn_Arena * Dqn_TLS_TopArena();
|
@ -30,6 +30,7 @@ struct Dqn_TypeField
|
||||
{
|
||||
uint16_t index;
|
||||
Dqn_Str8 name;
|
||||
Dqn_Str8 label;
|
||||
Dqn_isize value;
|
||||
Dqn_usize offset_of;
|
||||
Dqn_usize size_of;
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#if !defined(__clang__)
|
||||
|
||||
// NOTE: Taken from MSDN __cpuid example implementation
|
||||
// https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=msvc-170
|
||||
#include <bitset>
|
||||
@ -110,7 +110,7 @@ private:
|
||||
for (int i = 0; i <= nIds_; ++i)
|
||||
{
|
||||
__cpuidex(cpui.data(), i, 0);
|
||||
data_.push_back(cpui);
|
||||
data_[dataSize_++] = cpui;
|
||||
}
|
||||
|
||||
// Capture vendor string
|
||||
@ -154,7 +154,7 @@ private:
|
||||
for (int i = 0x80000000; i <= nExIds_; ++i)
|
||||
{
|
||||
__cpuidex(cpui.data(), i, 0);
|
||||
extdata_.push_back(cpui);
|
||||
extdata_[extdataSize_++] = cpui;
|
||||
}
|
||||
|
||||
// load bitset with flags for function 0x80000001
|
||||
@ -186,14 +186,15 @@ private:
|
||||
std::bitset<32> f_7_ECX_;
|
||||
std::bitset<32> f_81_ECX_;
|
||||
std::bitset<32> f_81_EDX_;
|
||||
std::vector<std::array<int, 4>> data_;
|
||||
std::vector<std::array<int, 4>> extdata_;
|
||||
std::array<std::array<int, 4>, 512> data_{};
|
||||
size_t dataSize_ = 0;
|
||||
std::array<std::array<int, 4>, 512> extdata_{};
|
||||
size_t extdataSize_ = 0;
|
||||
};
|
||||
};
|
||||
|
||||
// Initialize static member data
|
||||
const Dqn_RefImplCPUReport::Dqn_RefImplCPUReport_Internal Dqn_RefImplCPUReport::CPU_Rep;
|
||||
#endif // !defined(__clang__)
|
||||
|
||||
#if 0
|
||||
static void Dqn_RefImpl_CPUReportDump() // Print out supported instruction set features
|
||||
@ -263,8 +264,6 @@ static Dqn_UTest Dqn_Test_Base()
|
||||
{
|
||||
Dqn_UTest test = {};
|
||||
DQN_UTEST_GROUP(test, "Dqn_Base") {
|
||||
// TODO(doyle): cpuid refimpl doesn't work on clang
|
||||
#if !defined(__clang__)
|
||||
DQN_UTEST_TEST("Query CPUID") {
|
||||
Dqn_CPUReport cpu_report = Dqn_CPU_Report();
|
||||
|
||||
@ -324,8 +323,8 @@ static Dqn_UTest Dqn_Test_Base()
|
||||
DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_XOP) == Dqn_RefImplCPUReport::XOP());
|
||||
DQN_UTEST_ASSERT(&test, Dqn_CPU_HasFeature(&cpu_report, Dqn_CPUFeature_XSAVE) == Dqn_RefImplCPUReport::XSAVE());
|
||||
#endif
|
||||
|
||||
}
|
||||
#endif // !defined(__clang__)
|
||||
}
|
||||
return test;
|
||||
}
|
||||
@ -427,7 +426,7 @@ static Dqn_UTest Dqn_Test_Arena()
|
||||
|
||||
static Dqn_UTest Dqn_Test_Bin()
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_UTest test = {};
|
||||
DQN_UTEST_GROUP(test, "Dqn_Bin") {
|
||||
DQN_UTEST_TEST("Convert 0x123") {
|
||||
@ -487,23 +486,23 @@ static Dqn_UTest Dqn_Test_Bin()
|
||||
|
||||
uint32_t number = 0xd095f6;
|
||||
DQN_UTEST_TEST("Convert %x to string", number) {
|
||||
Dqn_Str8 number_hex = Dqn_BytesToHex(scratch.arena, &number, sizeof(number));
|
||||
Dqn_Str8 number_hex = Dqn_BytesToHex(tmem.arena, &number, sizeof(number));
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(number_hex, DQN_STR8("f695d000")), "number_hex=%.*s", DQN_STR_FMT(number_hex));
|
||||
}
|
||||
|
||||
number = 0xf6ed00;
|
||||
DQN_UTEST_TEST("Convert %x to string", number) {
|
||||
Dqn_Str8 number_hex = Dqn_BytesToHex(scratch.arena, &number, sizeof(number));
|
||||
Dqn_Str8 number_hex = Dqn_BytesToHex(tmem.arena, &number, sizeof(number));
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(number_hex, DQN_STR8("00edf600")), "number_hex=%.*s", DQN_STR_FMT(number_hex));
|
||||
}
|
||||
|
||||
Dqn_Str8 hex = DQN_STR8("0xf6ed00");
|
||||
DQN_UTEST_TEST("Convert %.*s to bytes", DQN_STR_FMT(hex)) {
|
||||
Dqn_Str8 bytes = Dqn_HexToBytes(scratch.arena, hex);
|
||||
Dqn_Str8 bytes = Dqn_HexToBytes(tmem.arena, hex);
|
||||
DQN_UTEST_ASSERTF(&test,
|
||||
Dqn_Str8_Eq(bytes, DQN_STR8("\xf6\xed\x00")),
|
||||
"number_hex=%.*s",
|
||||
DQN_STR_FMT(Dqn_BytesToHex(scratch.arena, bytes.data, bytes.size)));
|
||||
DQN_STR_FMT(Dqn_BytesToHex(tmem.arena, bytes.data, bytes.size)));
|
||||
}
|
||||
|
||||
}
|
||||
@ -822,11 +821,11 @@ static Dqn_UTest Dqn_Test_DSMap()
|
||||
{
|
||||
Dqn_UTest test = {};
|
||||
DQN_UTEST_GROUP(test, "Dqn_DSMap") {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
{
|
||||
Dqn_Arena arena = {};
|
||||
uint32_t const MAP_SIZE = 64;
|
||||
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(&arena, MAP_SIZE);
|
||||
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(&arena, MAP_SIZE, Dqn_DSMapFlags_Nil);
|
||||
DQN_DEFER { Dqn_DSMap_Deinit(&map, Dqn_ZeroMem_Yes); };
|
||||
|
||||
DQN_UTEST_TEST("Find non-existent value") {
|
||||
@ -867,10 +866,10 @@ static Dqn_UTest Dqn_Test_DSMap()
|
||||
case DSMapTestType_MakeSlot: prefix = DQN_STR8("Make slot"); break;
|
||||
}
|
||||
|
||||
Dqn_ArenaTempMemScope temp_mem_scope = Dqn_ArenaTempMemScope(scratch.arena);
|
||||
Dqn_ArenaTempMemScope temp_mem_scope = Dqn_ArenaTempMemScope(tmem.arena);
|
||||
Dqn_Arena arena = {};
|
||||
uint32_t const MAP_SIZE = 64;
|
||||
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(&arena, MAP_SIZE);
|
||||
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(&arena, MAP_SIZE, Dqn_DSMapFlags_Nil);
|
||||
DQN_DEFER { Dqn_DSMap_Deinit(&map, Dqn_ZeroMem_Yes); };
|
||||
|
||||
DQN_UTEST_TEST("%.*s: Test growing", DQN_STR_FMT(prefix)) {
|
||||
@ -878,8 +877,7 @@ static Dqn_UTest Dqn_Test_DSMap()
|
||||
uint64_t value = 0;
|
||||
uint64_t grow_threshold = map_start_size * 3 / 4;
|
||||
for (; map.occupied != grow_threshold; value++) {
|
||||
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value);
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value);
|
||||
DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find<uint64_t>(&map, key).value);
|
||||
Dqn_DSMapResult<uint64_t> make_result = {};
|
||||
if (test_type == DSMapTestType_Set) {
|
||||
@ -895,8 +893,7 @@ static Dqn_UTest Dqn_Test_DSMap()
|
||||
DQN_UTEST_ASSERT(&test, map.occupied == 1 /*Sentinel*/ + value);
|
||||
|
||||
{ // NOTE: One more item should cause the table to grow by 2x
|
||||
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value);
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value);
|
||||
Dqn_DSMapResult<uint64_t> make_result = {};
|
||||
if (test_type == DSMapTestType_Set) {
|
||||
make_result = Dqn_DSMap_Set(&map, key, value);
|
||||
@ -924,7 +921,7 @@ static Dqn_UTest Dqn_Test_DSMap()
|
||||
|
||||
// NOTE: Validate each slot value
|
||||
uint64_t value_test = index - 1;
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, &value_test, sizeof(value_test));
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value_test);
|
||||
DQN_UTEST_ASSERT(&test, Dqn_DSMap_KeyEquals(slot->key, key));
|
||||
if (test_type == DSMapTestType_Set) {
|
||||
DQN_UTEST_ASSERT(&test, slot->value == value_test);
|
||||
@ -945,9 +942,7 @@ static Dqn_UTest Dqn_Test_DSMap()
|
||||
uint64_t value = 0;
|
||||
uint64_t shrink_threshold = map.size * 1 / 4;
|
||||
for (; map.occupied != shrink_threshold; value++) {
|
||||
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value);
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
|
||||
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value);
|
||||
DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find<uint64_t>(&map, key).value);
|
||||
Dqn_DSMap_Erase(&map, key);
|
||||
DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find<uint64_t>(&map, key).value);
|
||||
@ -955,9 +950,8 @@ static Dqn_UTest Dqn_Test_DSMap()
|
||||
DQN_UTEST_ASSERT(&test, map.size == start_map_size);
|
||||
DQN_UTEST_ASSERT(&test, map.occupied == start_map_occupied - value);
|
||||
|
||||
{ // NOTE: One more item should cause the table to grow by 2x
|
||||
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value);
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
|
||||
{ // NOTE: One more item should cause the table to shrink by 2x
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value);
|
||||
Dqn_DSMap_Erase(&map, key);
|
||||
value++;
|
||||
|
||||
@ -971,12 +965,12 @@ static Dqn_UTest Dqn_Test_DSMap()
|
||||
DQN_UTEST_ASSERT(&test, DQN_MEMCMP(&sentinel, &NIL_SLOT, sizeof(NIL_SLOT)) == 0);
|
||||
}
|
||||
|
||||
// NOTE: Recheck all the hash table values after growing
|
||||
// NOTE: Recheck all the hash table values after shrinking
|
||||
for (uint64_t index = 1 /*Sentinel*/; index < map.occupied; index++) {
|
||||
|
||||
// NOTE: Generate the key
|
||||
uint64_t value_test = value + (index - 1);
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)&value_test, sizeof(value_test));
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value_test);
|
||||
|
||||
// NOTE: Validate each slot value
|
||||
Dqn_DSMapResult<uint64_t> find_result = Dqn_DSMap_Find(&map, key);
|
||||
@ -995,8 +989,7 @@ static Dqn_UTest Dqn_Test_DSMap()
|
||||
}
|
||||
|
||||
for (; map.occupied != 1; value++) { // NOTE: Remove all items from the table
|
||||
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value);
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&map, value);
|
||||
DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find<uint64_t>(&map, key).value);
|
||||
Dqn_DSMap_Erase(&map, key);
|
||||
DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find<uint64_t>(&map, key).value);
|
||||
@ -1016,12 +1009,12 @@ static Dqn_UTest Dqn_Test_FStr8()
|
||||
DQN_UTEST_GROUP(test, "Dqn_FStr8") {
|
||||
DQN_UTEST_TEST("Append too much fails") {
|
||||
Dqn_FStr8<4> str = {};
|
||||
DQN_UTEST_ASSERT(&test, !Dqn_FStr8_Append(&str, DQN_STR8("abcde")));
|
||||
DQN_UTEST_ASSERT(&test, !Dqn_FStr8_Add(&str, DQN_STR8("abcde")));
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Append format string too much fails") {
|
||||
Dqn_FStr8<4> str = {};
|
||||
DQN_UTEST_ASSERT(&test, !Dqn_FStr8_AppendF(&str, "abcde"));
|
||||
DQN_UTEST_ASSERT(&test, !Dqn_FStr8_AddF(&str, "abcde"));
|
||||
}
|
||||
}
|
||||
return test;
|
||||
@ -1049,8 +1042,8 @@ static Dqn_UTest Dqn_Test_Fs()
|
||||
DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(SRC_FILE));
|
||||
|
||||
// NOTE: Read step
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 read_file = Dqn_OS_ReadAll(SRC_FILE, scratch.arena, nullptr);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 read_file = Dqn_OS_ReadAll(tmem.arena, SRC_FILE, nullptr);
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Str8_HasData(read_file), "Failed to load file");
|
||||
DQN_UTEST_ASSERTF(&test, read_file.size == 4, "File read wrong amount of bytes");
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(read_file, DQN_STR8("test")), "read(%zu): %.*s", read_file.size, DQN_STR_FMT(read_file));
|
||||
@ -1301,8 +1294,8 @@ Dqn_Str8 const DQN_UTEST_HASH_STRING_[] =
|
||||
|
||||
void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 input_hex = Dqn_BytesToHex(scratch.arena, input.data, input.size);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 input_hex = Dqn_BytesToHex(tmem.arena, input.data, input.size);
|
||||
|
||||
switch(hash_type)
|
||||
{
|
||||
@ -1536,8 +1529,8 @@ static Dqn_UTest Dqn_Test_OS()
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Query executable directory") {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 result = Dqn_OS_EXEDir(scratch.arena);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 result = Dqn_OS_EXEDir(tmem.arena);
|
||||
DQN_UTEST_ASSERT(&test, Dqn_Str8_HasData(result));
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_OS_DirExists(result), "result(%zu): %.*s", result.size, DQN_STR_FMT(result));
|
||||
}
|
||||
@ -1709,8 +1702,8 @@ static Dqn_UTest Dqn_Test_Str8()
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Initialise with format string") {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 string = Dqn_Str8_InitF(scratch.arena, "%s", "AB");
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 string = Dqn_Str8_InitF(tmem.arena, "%s", "AB");
|
||||
DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %zu", string.size);
|
||||
DQN_UTEST_ASSERTF(&test, string.data[0] == 'A', "string[0]: %c", string.data[0]);
|
||||
DQN_UTEST_ASSERTF(&test, string.data[1] == 'B', "string[1]: %c", string.data[1]);
|
||||
@ -1718,9 +1711,9 @@ static Dqn_UTest Dqn_Test_Str8()
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Copy string") {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 string = DQN_STR8("AB");
|
||||
Dqn_Str8 copy = Dqn_Str8_Copy(scratch.arena, string);
|
||||
Dqn_Str8 copy = Dqn_Str8_Copy(tmem.arena, string);
|
||||
DQN_UTEST_ASSERTF(&test, copy.size == 2, "size: %zu", copy.size);
|
||||
DQN_UTEST_ASSERTF(&test, copy.data[0] == 'A', "copy[0]: %c", copy.data[0]);
|
||||
DQN_UTEST_ASSERTF(&test, copy.data[1] == 'B', "copy[1]: %c", copy.data[1]);
|
||||
@ -1733,8 +1726,8 @@ static Dqn_UTest Dqn_Test_Str8()
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Allocate string from arena") {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 string = Dqn_Str8_Alloc(scratch.arena, 2, Dqn_ZeroMem_No);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 string = Dqn_Str8_Alloc(tmem.arena, 2, Dqn_ZeroMem_No);
|
||||
DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %zu", string.size);
|
||||
}
|
||||
|
||||
@ -1950,7 +1943,7 @@ static Dqn_UTest Dqn_Test_Str8()
|
||||
DQN_UTEST_TEST("Find: String (char) is not in buffer") {
|
||||
Dqn_Str8 buf = DQN_STR8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55");
|
||||
Dqn_Str8 find = DQN_STR8("2");
|
||||
Dqn_Str8FindResult result = Dqn_Str8_FindStr8(buf, find);
|
||||
Dqn_Str8FindResult result = Dqn_Str8_FindStr8(buf, find, Dqn_Str8EqCase_Sensitive);
|
||||
DQN_UTEST_ASSERT(&test, !result.found);
|
||||
DQN_UTEST_ASSERT(&test, result.index == 0);
|
||||
DQN_UTEST_ASSERT(&test, result.match.data == nullptr);
|
||||
@ -1960,7 +1953,7 @@ static Dqn_UTest Dqn_Test_Str8()
|
||||
DQN_UTEST_TEST("Find: String (char) is in buffer") {
|
||||
Dqn_Str8 buf = DQN_STR8("836a35becd4e74b66a0d6844d51f1a63018c7ebc44cf7e109e8e4bba57eefb55");
|
||||
Dqn_Str8 find = DQN_STR8("6");
|
||||
Dqn_Str8FindResult result = Dqn_Str8_FindStr8(buf, find);
|
||||
Dqn_Str8FindResult result = Dqn_Str8_FindStr8(buf, find, Dqn_Str8EqCase_Sensitive);
|
||||
DQN_UTEST_ASSERT(&test, result.found);
|
||||
DQN_UTEST_ASSERT(&test, result.index == 2);
|
||||
DQN_UTEST_ASSERT(&test, result.match.data[0] == '6');
|
||||
@ -2175,23 +2168,23 @@ static Dqn_UTest Dqn_Test_Win()
|
||||
{
|
||||
Dqn_UTest test = {};
|
||||
DQN_UTEST_GROUP(test, "OS Win32") {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_TLSTMem tmem = Dqn_TLS_TMem(nullptr);
|
||||
Dqn_Str8 input8 = DQN_STR8("String");
|
||||
Dqn_Str16 input16 = Dqn_Str16{(wchar_t *)(L"String"), sizeof(L"String") / sizeof(L"String"[0]) - 1};
|
||||
|
||||
DQN_UTEST_TEST("Str8 to Str16") {
|
||||
Dqn_Str16 result = Dqn_Win_Str8ToStr16(scratch.arena, input8);
|
||||
Dqn_Str16 result = Dqn_Win_Str8ToStr16(tmem.arena, input8);
|
||||
DQN_UTEST_ASSERT(&test, result == input16);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Str16 to Str8") {
|
||||
Dqn_Str8 result = Dqn_Win_Str16ToStr8(scratch.arena, input16);
|
||||
Dqn_Str8 result = Dqn_Win_Str16ToStr8(tmem.arena, input16);
|
||||
DQN_UTEST_ASSERT(&test, result == input8);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Str16 to Str8: Null terminates string") {
|
||||
int size_required = Dqn_Win_Str16ToStr8Buffer(input16, nullptr, 0);
|
||||
char *string = Dqn_Arena_NewArray(scratch.arena, char, size_required + 1, Dqn_ZeroMem_No);
|
||||
char *string = Dqn_Arena_NewArray(tmem.arena, char, size_required + 1, Dqn_ZeroMem_No);
|
||||
|
||||
// Fill the string with error sentinels
|
||||
DQN_MEMSET(string, 'Z', size_required + 1);
|
||||
@ -2205,7 +2198,7 @@ static Dqn_UTest Dqn_Test_Win()
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Str16 to Str8: Arena null terminates string") {
|
||||
Dqn_Str8 string8 = Dqn_Win_Str16ToStr8(scratch.arena, input16);
|
||||
Dqn_Str8 string8 = Dqn_Win_Str16ToStr8(tmem.arena, input16);
|
||||
int size_returned = Dqn_Win_Str16ToStr8Buffer(input16, nullptr, 0);
|
||||
char const EXPECTED[] = {'S', 't', 'r', 'i', 'n', 'g', 0};
|
||||
|
||||
@ -2259,7 +2252,7 @@ void Dqn_Test_RunSuite()
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
(void)argv; (void)argc;
|
||||
Dqn_Library_Init(Dqn_LibraryOnInit_LogAllFeatures);
|
||||
Dqn_Library_Init(Dqn_LibraryOnInit_LogFeatures);
|
||||
Dqn_Test_RunSuite();
|
||||
return 0;
|
||||
}
|
||||
|
188
dqn_win32.h
188
dqn_win32.h
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "dqn.h"
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\
|
||||
@ -15,11 +16,15 @@
|
||||
// dqn_win32.h -- Windows replacement header
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
|
||||
#pragma comment(lib, "bcrypt")
|
||||
#pragma comment(lib, "winhttp")
|
||||
#pragma comment(lib, "dbghelp")
|
||||
#pragma comment(lib, "comdlg32")
|
||||
#pragma comment(lib, "pathcch")
|
||||
#pragma comment(lib, "shlwapi")
|
||||
#endif
|
||||
|
||||
#if defined(DQN_NO_WIN32_MIN_HEADER) || defined(_INC_WINDOWS)
|
||||
@ -27,6 +32,8 @@
|
||||
#include <Windows.h> // LONG
|
||||
#include <bcrypt.h> // Dqn_OS_SecureRNGBytes -> BCryptOpenAlgorithmProvider ... etc
|
||||
#include <shellapi.h> // Dqn_Win_MakeProcessDPIAware -> SetProcessDpiAwareProc
|
||||
#include <shlwapi.h> // PathRelativePathTO
|
||||
#include <pathcch.h> // PathCchCanonicalizeEx
|
||||
#include <winhttp.h> // WinHttp*
|
||||
#include <DbgHelp.h>
|
||||
#else
|
||||
@ -35,6 +42,7 @@
|
||||
|
||||
// NOTE: basetsd.h /////////////////////////////////////////////////////////////////////////////
|
||||
typedef unsigned __int64 ULONG_PTR, *PULONG_PTR;
|
||||
typedef unsigned __int64 UINT_PTR, *PUINT_PTR;
|
||||
typedef ULONG_PTR SIZE_T, *PSIZE_T;
|
||||
typedef __int64 LONG_PTR, *PLONG_PTR;
|
||||
typedef ULONG_PTR DWORD_PTR, *PDWORD_PTR;
|
||||
@ -47,17 +55,21 @@
|
||||
};
|
||||
typedef struct HINSTANCE__ *HINSTANCE;
|
||||
|
||||
typedef unsigned long DWORD;
|
||||
typedef int BOOL;
|
||||
typedef int INT;
|
||||
typedef unsigned long ULONG;
|
||||
typedef unsigned int UINT;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned char UCHAR;
|
||||
typedef HINSTANCE HMODULE; /* HMODULEs can be used in place of HINSTANCEs */
|
||||
typedef void * HANDLE;
|
||||
typedef HANDLE HLOCAL;
|
||||
typedef unsigned long DWORD;
|
||||
typedef int BOOL;
|
||||
typedef int INT;
|
||||
typedef unsigned long ULONG;
|
||||
typedef unsigned int UINT;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned char UCHAR;
|
||||
typedef HINSTANCE HMODULE; /* HMODULEs can be used in place of HINSTANCEs */
|
||||
typedef void * HANDLE;
|
||||
typedef HANDLE HLOCAL;
|
||||
|
||||
typedef unsigned __int64 WPARAM;
|
||||
typedef LONG_PTR LPARAM;
|
||||
typedef LONG_PTR LRESULT;
|
||||
|
||||
#define MAX_PATH 260
|
||||
|
||||
@ -85,6 +97,7 @@
|
||||
typedef wchar_t WCHAR; // wc, 16-bit UNICODE character
|
||||
typedef CHAR * NPSTR, *LPSTR, *PSTR;
|
||||
typedef WCHAR * NWPSTR, *LPWSTR, *PWSTR;
|
||||
typedef long HRESULT;
|
||||
|
||||
// NOTE: VirtualAlloc: Allocation Type
|
||||
#define MEM_RESERVE 0x00002000
|
||||
@ -130,6 +143,9 @@
|
||||
#define STATUS_WAIT_0 ((DWORD )0x00000000L)
|
||||
#define STATUS_ABANDONED_WAIT_0 ((DWORD )0x00000080L)
|
||||
|
||||
#define S_OK ((HRESULT)0L)
|
||||
#define S_FALSE ((HRESULT)1L)
|
||||
|
||||
typedef union _ULARGE_INTEGER {
|
||||
struct {
|
||||
DWORD LowPart;
|
||||
@ -1047,12 +1063,11 @@
|
||||
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) BOOL __stdcall CreateProcessW(WCHAR const *lpApplicationName, WCHAR *lpCommandLine, SECURITY_ATTRIBUTES *lpProcessAttributes, SECURITY_ATTRIBUTES *lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, VOID *lpEnvironment, WCHAR const *lpCurrentDirectory, STARTUPINFOW *lpStartupInfo, PROCESS_INFORMATION *lpProcessInformation);
|
||||
__declspec(dllimport) HANDLE __stdcall CreateThread(SECURITY_ATTRIBUTES *lpThreadAttributes, SIZE_T dwStackSize, PTHREAD_START_ROUTINE lpStartAddress, VOID *lpParameter, DWORD dwCreationFlags, DWORD *lpThreadId);
|
||||
__declspec(dllimport) BOOL __stdcall CreateProcessW (WCHAR const *lpApplicationName, WCHAR *lpCommandLine, SECURITY_ATTRIBUTES *lpProcessAttributes, SECURITY_ATTRIBUTES *lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, VOID *lpEnvironment, WCHAR const *lpCurrentDirectory, STARTUPINFOW *lpStartupInfo, PROCESS_INFORMATION *lpProcessInformation);
|
||||
__declspec(dllimport) HANDLE __stdcall CreateThread (SECURITY_ATTRIBUTES *lpThreadAttributes, SIZE_T dwStackSize, PTHREAD_START_ROUTINE lpStartAddress, VOID *lpParameter, DWORD dwCreationFlags, DWORD *lpThreadId);
|
||||
__declspec(dllimport) DWORD __stdcall GetCurrentThreadId(VOID);
|
||||
__declspec(dllimport) BOOL __stdcall GetExitCodeProcess(HANDLE hProcess, DWORD *lpExitCode);
|
||||
__declspec(dllimport) void __stdcall ExitProcess(UINT uExitCode);
|
||||
|
||||
__declspec(dllimport) void __stdcall ExitProcess (UINT uExitCode);
|
||||
}
|
||||
|
||||
// NOTE: um/memoryapi.h ////////////////////////////////////////////////////////////////////////
|
||||
@ -1077,6 +1092,7 @@
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) HINSTANCE __stdcall ShellExecuteA(HWND hwnd, CHAR const *lpOperation, CHAR const *lpFile, CHAR const *lpParameters, CHAR const *lpDirectory, INT nShowCmd);
|
||||
__declspec(dllimport) HINSTANCE __stdcall ShellExecuteW(HWND hwnd, WCHAR const *lpOperation, WCHAR const *lpFile, WCHAR const *lpParameters, WCHAR const *lpDirectory, INT nShowCmd);
|
||||
}
|
||||
|
||||
// NOTE: um/debugapi.h /////////////////////////////////////////////////////////////////////////
|
||||
@ -1088,14 +1104,150 @@
|
||||
// NOTE: um/namedpipeapi.h /////////////////////////////////////////////////////////////////////
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) BOOL __stdcall CreatePipe (HANDLE *hReadPipe, HANDLE *hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize);
|
||||
__declspec(dllimport) BOOL __stdcall CreatePipe (HANDLE *hReadPipe, HANDLE *hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize);
|
||||
__declspec(dllimport) BOOL __stdcall PeekNamedPipe(HANDLE hNamedPipe, VOID *lpBuffer, DWORD nBufferSize, DWORD *lpBytesRead, DWORD *lpTotalBytesAvail, DWORD *lpBytesLeftThisMessage);
|
||||
}
|
||||
|
||||
// NOTE: um/handleapi.h /////////////////////////////////////////////////////////////////////
|
||||
// NOTE: um/handleapi.h ////////////////////////////////////////////////////////////////////////
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) BOOL __stdcall SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags);
|
||||
__declspec(dllimport) BOOL __stdcall SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags);
|
||||
}
|
||||
|
||||
// NOTE: um/commdlg.h //////////////////////////////////////////////////////////////////////////
|
||||
typedef UINT_PTR (__stdcall *LPOFNHOOKPROC)(HWND, UINT, WPARAM, LPARAM);
|
||||
typedef struct tagOFNW {
|
||||
DWORD lStructSize;
|
||||
HWND hwndOwner;
|
||||
HINSTANCE hInstance;
|
||||
WCHAR const * lpstrFilter;
|
||||
LPWSTR lpstrCustomFilter;
|
||||
DWORD nMaxCustFilter;
|
||||
DWORD nFilterIndex;
|
||||
LPWSTR lpstrFile;
|
||||
DWORD nMaxFile;
|
||||
LPWSTR lpstrFileTitle;
|
||||
DWORD nMaxFileTitle;
|
||||
WCHAR const * lpstrInitialDir;
|
||||
WCHAR const * lpstrTitle;
|
||||
DWORD Flags;
|
||||
WORD nFileOffset;
|
||||
WORD nFileExtension;
|
||||
WCHAR const * lpstrDefExt;
|
||||
LPARAM lCustData;
|
||||
LPOFNHOOKPROC lpfnHook;
|
||||
WCHAR const * lpTemplateName;
|
||||
#ifdef _MAC
|
||||
LPEDITMENU lpEditInfo;
|
||||
LPCSTR lpstrPrompt;
|
||||
#endif
|
||||
#if (_WIN32_WINNT >= 0x0500)
|
||||
void * pvReserved;
|
||||
DWORD dwReserved;
|
||||
DWORD FlagsEx;
|
||||
#endif // (_WIN32_WINNT >= 0x0500)
|
||||
} OPENFILENAMEW, *LPOPENFILENAMEW;
|
||||
|
||||
|
||||
#define OFN_READONLY 0x00000001
|
||||
#define OFN_OVERWRITEPROMPT 0x00000002
|
||||
#define OFN_HIDEREADONLY 0x00000004
|
||||
#define OFN_NOCHANGEDIR 0x00000008
|
||||
#define OFN_SHOWHELP 0x00000010
|
||||
#define OFN_ENABLEHOOK 0x00000020
|
||||
#define OFN_ENABLETEMPLATE 0x00000040
|
||||
#define OFN_ENABLETEMPLATEHANDLE 0x00000080
|
||||
#define OFN_NOVALIDATE 0x00000100
|
||||
#define OFN_ALLOWMULTISELECT 0x00000200
|
||||
#define OFN_EXTENSIONDIFFERENT 0x00000400
|
||||
#define OFN_PATHMUSTEXIST 0x00000800
|
||||
#define OFN_FILEMUSTEXIST 0x00001000
|
||||
#define OFN_CREATEPROMPT 0x00002000
|
||||
#define OFN_SHAREAWARE 0x00004000
|
||||
#define OFN_NOREADONLYRETURN 0x00008000
|
||||
#define OFN_NOTESTFILECREATE 0x00010000
|
||||
#define OFN_NONETWORKBUTTON 0x00020000
|
||||
#define OFN_NOLONGNAMES 0x00040000 // force no long names for 4.x modules
|
||||
#if(WINVER >= 0x0400)
|
||||
#define OFN_EXPLORER 0x00080000 // new look commdlg
|
||||
#define OFN_NODEREFERENCELINKS 0x00100000
|
||||
#define OFN_LONGNAMES 0x00200000 // force long names for 3.x modules
|
||||
// OFN_ENABLEINCLUDENOTIFY and OFN_ENABLESIZING require
|
||||
// Windows 2000 or higher to have any effect.
|
||||
#define OFN_ENABLEINCLUDENOTIFY 0x00400000 // send include message to callback
|
||||
#define OFN_ENABLESIZING 0x00800000
|
||||
#endif /* WINVER >= 0x0400 */
|
||||
#if (_WIN32_WINNT >= 0x0500)
|
||||
#define OFN_DONTADDTORECENT 0x02000000
|
||||
#define OFN_FORCESHOWHIDDEN 0x10000000 // Show All files including System and hidden files
|
||||
#endif // (_WIN32_WINNT >= 0x0500)
|
||||
|
||||
//FlagsEx Values
|
||||
#if (_WIN32_WINNT >= 0x0500)
|
||||
#define OFN_EX_NOPLACESBAR 0x00000001
|
||||
#endif // (_WIN32_WINNT >= 0x0500)
|
||||
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) BOOL __stdcall GetSaveFileNameW(LPOPENFILENAMEW);
|
||||
__declspec(dllimport) BOOL __stdcall GetOpenFileNameW(LPOPENFILENAMEW);
|
||||
}
|
||||
|
||||
// NOTE: um/shlwapi.h //////////////////////////////////////////////////////////////////////////
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) BOOL __stdcall PathRelativePathToW(WCHAR *pszPath, WCHAR const *pszFrom, DWORD dwAttrFrom, WCHAR const *pszTo, DWORD dwAttrTo);
|
||||
__declspec(dllimport) BOOL __stdcall PathIsRelativeW(WCHAR *pszPath);
|
||||
}
|
||||
|
||||
// NOTE: um/pathcch.h //////////////////////////////////////////////////////////////////////////
|
||||
typedef enum PATHCCH_OPTIONS
|
||||
{
|
||||
PATHCCH_NONE = 0x0,
|
||||
|
||||
// This option allows applications to gain access to long paths. It has two
|
||||
// different behaviors. For process configured to enable long paths it will allow
|
||||
// the returned path to be longer than the max path limit that is normally imposed.
|
||||
// For process that are not this option will convert long paths into the extended
|
||||
// length DOS device form (with \\?\ prefix) when the path is longer than the limit.
|
||||
// This form is not length limited by the Win32 file system API on all versions of Windows.
|
||||
// This second behavior is the same behavior for OSes that don't have the long path feature.
|
||||
// This can not be specified with PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH.
|
||||
PATHCCH_ALLOW_LONG_PATHS = 0x01,
|
||||
|
||||
// Can only be used when PATHCCH_ALLOW_LONG_PATHS is specified. This
|
||||
// Forces the API to treat the caller as long path enabled, independent of the
|
||||
// process's long name enabled state. Cannot be used with PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS.
|
||||
PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS = 0x02,
|
||||
|
||||
// Can only be used when PATHCCH_ALLOW_LONG_PATHS is specified. This
|
||||
// Forces the API to treat the caller as long path disabled, independent of the
|
||||
// process's long name enabled state. Cannot be used with PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS.
|
||||
PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS = 0x04,
|
||||
|
||||
// Disable the normalization of path segments that includes removing trailing dots and spaces.
|
||||
// This enables access to paths that win32 path normalization will block.
|
||||
PATHCCH_DO_NOT_NORMALIZE_SEGMENTS = 0x08,
|
||||
|
||||
// Convert the input path into the extended length DOS device path form (with the \\?\ prefix)
|
||||
// if not already in that form. This enables access to paths that are otherwise not addressable
|
||||
// due to Win32 normalization rules (that can strip trailing dots and spaces) and path
|
||||
// length limitations. This option implies the same behavior of PATHCCH_DO_NOT_NORMALIZE_SEGMENTS.
|
||||
// This can not be specified with PATHCCH_ALLOW_LONG_PATHS.
|
||||
PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH = 0x10,
|
||||
|
||||
// When combining or normalizing a path ensure there is a trailing backslash.
|
||||
PATHCCH_ENSURE_TRAILING_SLASH = 0x020,
|
||||
|
||||
// Convert forward slashes to back slashes and collapse multiple slashes.
|
||||
// This is needed to to support sub-path or identity comparisons.
|
||||
PATHCCH_CANONICALIZE_SLASHES = 0x040,
|
||||
} PATHCCH_OPTIONS;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) HRESULT __stdcall PathCchCanonicalizeEx(PWSTR pszPathOut, size_t cchPathOut, WCHAR const *pszPathIn, ULONG dwFlags);
|
||||
};
|
||||
|
||||
DQN_MSVC_WARNING_POP
|
||||
#endif // !defined(_INC_WINDOWS)
|
||||
|
Loading…
Reference in New Issue
Block a user