dqn: Reorganize the library
This commit is contained in:
parent
3c614946e0
commit
a5a4485e29
@ -32,189 +32,134 @@
|
||||
#define DQN_UTEST_IMPLEMENTATION
|
||||
#include "dqn_utest.h"
|
||||
|
||||
enum Guard {
|
||||
Guard_None,
|
||||
Guard_UseAfterFree,
|
||||
Guard_Count,
|
||||
};
|
||||
|
||||
static Dqn_String8 ArenaGuardTestSuffix(uint32_t guard)
|
||||
{
|
||||
Dqn_String8 result = {};
|
||||
switch (guard) {
|
||||
case Guard_None: result = DQN_STRING8(" "); break;
|
||||
case Guard_UseAfterFree: result = DQN_STRING8(" [UAF]"); break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Dqn_UTest TestArena()
|
||||
{
|
||||
Dqn_UTest test = {};
|
||||
DQN_UTEST_GROUP(test, "Dqn_Arena") {
|
||||
for (Dqn_usize guard = 0; guard < Guard_Count; guard++) {
|
||||
Dqn_String8 test_suffix = ArenaGuardTestSuffix(guard);
|
||||
DQN_UTEST_TEST("Reused memory is zeroed out%.*s", DQN_STRING_FMT(test_suffix)) {
|
||||
Dqn_Arena arena = {};
|
||||
arena.use_after_free_guard = guard == Guard_UseAfterFree;
|
||||
|
||||
// NOTE: Allocate 128 kilobytes, fill it with garbage, then reset the arena
|
||||
Dqn_usize size = DQN_KILOBYTES(128);
|
||||
uintptr_t first_ptr_address = 0;
|
||||
{
|
||||
Dqn_ArenaTempMemory temp_mem = Dqn_Arena_BeginTempMemory(&arena);
|
||||
void *ptr = Dqn_Arena_Allocate(&arena, size, 1, Dqn_ZeroMem_Yes);
|
||||
first_ptr_address = DQN_CAST(uintptr_t)ptr;
|
||||
DQN_MEMSET(ptr, 'z', size);
|
||||
Dqn_Arena_EndTempMemory(temp_mem);
|
||||
}
|
||||
|
||||
// NOTE: Reallocate 128 kilobytes
|
||||
char *ptr = DQN_CAST(char *)Dqn_Arena_Allocate(&arena, size, 1, Dqn_ZeroMem_Yes);
|
||||
|
||||
// NOTE: Double check we got the same pointer
|
||||
DQN_UTEST_ASSERT(&test, first_ptr_address == DQN_CAST(uintptr_t)ptr);
|
||||
|
||||
// NOTE: Check that the bytes are set to 0
|
||||
for (Dqn_usize i = 0; i < size; i++)
|
||||
DQN_UTEST_ASSERT(&test, ptr[i] == 0);
|
||||
Dqn_Arena_Free(&arena, Dqn_ZeroMem_No);
|
||||
}
|
||||
DQN_UTEST_TEST("Grow arena, reserve 4k, commit 1k (e.g. different sizes)") {
|
||||
Dqn_Arena arena = {};
|
||||
Dqn_Arena_Grow(&arena, DQN_KILOBYTES(4), DQN_KILOBYTES(1), /*flags*/ 0);
|
||||
Dqn_Arena_Free(&arena);
|
||||
}
|
||||
|
||||
for (Dqn_usize guard = 0; guard < Guard_Count; guard++) {
|
||||
Dqn_String8 test_suffix = ArenaGuardTestSuffix(guard);
|
||||
DQN_UTEST_TEST("Test arena grows naturally, 1mb + 4mb%.*s", DQN_STRING_FMT(test_suffix)) {
|
||||
Dqn_Arena arena = {};
|
||||
arena.use_after_free_guard = guard == Guard_UseAfterFree;
|
||||
DQN_UTEST_TEST("Reused memory is zeroed out") {
|
||||
|
||||
// NOTE: Allocate 1mb, then 4mb, this should force the arena to grow
|
||||
char *ptr_1mb = DQN_CAST(char *)Dqn_Arena_Allocate(&arena, DQN_MEGABYTES(1), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
char *ptr_4mb = DQN_CAST(char *)Dqn_Arena_Allocate(&arena, DQN_MEGABYTES(4), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
DQN_UTEST_ASSERT(&test, ptr_1mb);
|
||||
Dqn_usize size = DQN_KILOBYTES(128);
|
||||
Dqn_Arena arena = {};
|
||||
Dqn_Arena_Grow(&arena, size, /*commit*/ size, /*flags*/ 0);
|
||||
|
||||
// NOTE: Allocate 128 kilobytes, fill it with garbage, then reset the arena
|
||||
uintptr_t first_ptr_address = 0;
|
||||
{
|
||||
Dqn_ArenaTempMemory temp_mem = Dqn_Arena_BeginTempMemory(&arena);
|
||||
void *ptr = Dqn_Arena_Alloc(&arena, size, 1, Dqn_ZeroMem_Yes);
|
||||
first_ptr_address = DQN_CAST(uintptr_t)ptr;
|
||||
DQN_MEMSET(ptr, 'z', size);
|
||||
Dqn_Arena_EndTempMemory(temp_mem, false /*cancel*/);
|
||||
}
|
||||
|
||||
// NOTE: Reallocate 128 kilobytes
|
||||
char *ptr = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, size, 1, Dqn_ZeroMem_Yes);
|
||||
|
||||
// NOTE: Double check we got the same pointer
|
||||
DQN_UTEST_ASSERT(&test, first_ptr_address == DQN_CAST(uintptr_t)ptr);
|
||||
|
||||
// NOTE: Check that the bytes are set to 0
|
||||
for (Dqn_usize i = 0; i < size; i++)
|
||||
DQN_UTEST_ASSERT(&test, ptr[i] == 0);
|
||||
Dqn_Arena_Free(&arena);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Test arena grows naturally, 1mb + 4mb") {
|
||||
Dqn_Arena arena = {};
|
||||
|
||||
// NOTE: Allocate 1mb, then 4mb, this should force the arena to grow
|
||||
char *ptr_1mb = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(1), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
char *ptr_4mb = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(4), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
DQN_UTEST_ASSERT(&test, ptr_1mb);
|
||||
DQN_UTEST_ASSERT(&test, ptr_4mb);
|
||||
|
||||
Dqn_MemBlock const *block_1mb = arena.head;
|
||||
char const *block_1mb_begin = DQN_CAST(char *)block_1mb->data;
|
||||
char const *block_1mb_end = DQN_CAST(char *)block_1mb->data + block_1mb->size;
|
||||
|
||||
Dqn_MemBlock const *block_4mb = arena.curr;
|
||||
char const *block_4mb_begin = DQN_CAST(char *)block_4mb->data;
|
||||
char const *block_4mb_end = DQN_CAST(char *)block_4mb->data + block_4mb->size;
|
||||
|
||||
DQN_UTEST_ASSERTF(&test, block_1mb != block_4mb, "New block should have been allocated and linked");
|
||||
DQN_UTEST_ASSERTF(&test, ptr_1mb >= block_1mb_begin && ptr_1mb <= block_1mb_end, "Pointer was not allocated from correct memory block");
|
||||
DQN_UTEST_ASSERTF(&test, ptr_4mb >= block_4mb_begin && ptr_4mb <= block_4mb_end, "Pointer was not allocated from correct memory block");
|
||||
DQN_UTEST_ASSERT (&test, arena.curr == arena.tail);
|
||||
DQN_UTEST_ASSERT (&test, arena.curr != arena.head);
|
||||
|
||||
Dqn_Arena_Free(&arena);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Test arena grows naturally, 1mb, temp memory 4mb") {
|
||||
Dqn_Arena arena = {};
|
||||
|
||||
// NOTE: Allocate 1mb, then 4mb, this should force the arena to grow
|
||||
char *ptr_1mb = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(1), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
DQN_UTEST_ASSERT(&test, ptr_1mb);
|
||||
|
||||
Dqn_ArenaTempMemory temp_memory = Dqn_Arena_BeginTempMemory(&arena);
|
||||
{
|
||||
char *ptr_4mb = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(4), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
DQN_UTEST_ASSERT(&test, ptr_4mb);
|
||||
|
||||
Dqn_ArenaBlock const *block_1mb = arena.head;
|
||||
char const *block_1mb_begin = DQN_CAST(char *)block_1mb->memory;
|
||||
char const *block_1mb_end = DQN_CAST(char *)block_1mb->memory + block_1mb->size;
|
||||
Dqn_MemBlock const *block_1mb = arena.head;
|
||||
char const *block_1mb_begin = DQN_CAST(char *)block_1mb->data;
|
||||
char const *block_1mb_end = DQN_CAST(char *)block_1mb->data + block_1mb->size;
|
||||
|
||||
Dqn_ArenaBlock const *block_4mb = arena.curr;
|
||||
char const *block_4mb_begin = DQN_CAST(char *)block_4mb->memory;
|
||||
char const *block_4mb_end = DQN_CAST(char *)block_4mb->memory + block_4mb->size;
|
||||
Dqn_MemBlock const *block_4mb = arena.curr;
|
||||
char const *block_4mb_begin = DQN_CAST(char *)block_4mb->data;
|
||||
char const *block_4mb_end = DQN_CAST(char *)block_4mb->data + block_4mb->size;
|
||||
|
||||
DQN_UTEST_ASSERTF(&test, block_1mb != block_4mb, "New block should have been allocated and linked");
|
||||
DQN_UTEST_ASSERTF(&test, ptr_1mb >= block_1mb_begin && ptr_1mb <= block_1mb_end, "Pointer was not allocated from correct memory block");
|
||||
DQN_UTEST_ASSERTF(&test, ptr_4mb >= block_4mb_begin && ptr_4mb <= block_4mb_end, "Pointer was not allocated from correct memory block");
|
||||
DQN_UTEST_ASSERT (&test, arena.curr == arena.tail);
|
||||
DQN_UTEST_ASSERT (&test, arena.curr != arena.head);
|
||||
|
||||
Dqn_Arena_Free(&arena, Dqn_ZeroMem_No);
|
||||
}
|
||||
Dqn_Arena_EndTempMemory(temp_memory, false /*cancel*/);
|
||||
|
||||
DQN_UTEST_ASSERT (&test, arena.curr == arena.head);
|
||||
DQN_UTEST_ASSERT (&test, arena.curr == arena.tail);
|
||||
DQN_UTEST_ASSERT (&test, arena.curr->next == nullptr);
|
||||
DQN_UTEST_ASSERTF(&test, arena.curr->size >= DQN_MEGABYTES(1),
|
||||
"size=%zuMiB (%zuB), expect=%zuB", (arena.curr->size / 1024 / 1024), arena.curr->size, DQN_MEGABYTES(1));
|
||||
|
||||
Dqn_Arena_Free(&arena);
|
||||
}
|
||||
|
||||
for (Dqn_usize guard = 0; guard < Guard_Count; guard++) {
|
||||
Dqn_String8 test_suffix = ArenaGuardTestSuffix(guard);
|
||||
DQN_UTEST_TEST("Test arena grows naturally, 1mb, temp memory 4mb%.*s", DQN_STRING_FMT(test_suffix)) {
|
||||
Dqn_Arena arena = {};
|
||||
arena.use_after_free_guard = guard == Guard_UseAfterFree;
|
||||
DQN_UTEST_TEST("Init arena, temp region then free inside regions") {
|
||||
Dqn_Arena arena = {};
|
||||
Dqn_Arena_Grow(&arena, DQN_KILOBYTES(1), 0, Dqn_ZeroMem_No);
|
||||
|
||||
// NOTE: Allocate 1mb, then 4mb, this should force the arena to grow
|
||||
char *ptr_1mb = DQN_CAST(char *)Dqn_Arena_Allocate(&arena, DQN_MEGABYTES(1), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
DQN_UTEST_ASSERT(&test, ptr_1mb);
|
||||
|
||||
Dqn_ArenaTempMemory temp_memory = Dqn_Arena_BeginTempMemory(&arena);
|
||||
{
|
||||
char *ptr_4mb = DQN_CAST(char *)Dqn_Arena_Allocate(&arena, DQN_MEGABYTES(4), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
DQN_UTEST_ASSERT(&test, ptr_4mb);
|
||||
|
||||
Dqn_ArenaBlock const *block_1mb = arena.head;
|
||||
char const *block_1mb_begin = DQN_CAST(char *)block_1mb->memory;
|
||||
char const *block_1mb_end = DQN_CAST(char *)block_1mb->memory + block_1mb->size;
|
||||
|
||||
Dqn_ArenaBlock const *block_4mb = arena.curr;
|
||||
char const *block_4mb_begin = DQN_CAST(char *)block_4mb->memory;
|
||||
char const *block_4mb_end = DQN_CAST(char *)block_4mb->memory + block_4mb->size;
|
||||
|
||||
DQN_UTEST_ASSERTF(&test, block_1mb != block_4mb, "New block should have been allocated and linked");
|
||||
DQN_UTEST_ASSERTF(&test, ptr_1mb >= block_1mb_begin && ptr_1mb <= block_1mb_end, "Pointer was not allocated from correct memory block");
|
||||
DQN_UTEST_ASSERTF(&test, ptr_4mb >= block_4mb_begin && ptr_4mb <= block_4mb_end, "Pointer was not allocated from correct memory block");
|
||||
DQN_UTEST_ASSERT (&test, arena.curr == arena.tail);
|
||||
DQN_UTEST_ASSERT (&test, arena.curr != arena.head);
|
||||
}
|
||||
Dqn_Arena_EndTempMemory(temp_memory);
|
||||
|
||||
DQN_UTEST_ASSERT (&test, arena.curr == arena.head);
|
||||
DQN_UTEST_ASSERT (&test, arena.curr == arena.tail);
|
||||
DQN_UTEST_ASSERT (&test, arena.curr->next == nullptr);
|
||||
DQN_UTEST_ASSERTF(&test, arena.curr->size >= DQN_MEGABYTES(1),
|
||||
"size=%zuMiB (%zuB), expect=%zuB", (arena.curr->size / 1024 / 1024), arena.curr->size, DQN_MEGABYTES(1));
|
||||
|
||||
Dqn_Arena_Free(&arena, Dqn_ZeroMem_No);
|
||||
Dqn_ArenaTempMemory temp_memory = Dqn_Arena_BeginTempMemory(&arena);
|
||||
{
|
||||
char *ptr = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(1), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
DQN_UTEST_ASSERT(&test, ptr);
|
||||
Dqn_Arena_Free(&arena);
|
||||
}
|
||||
Dqn_Arena_EndTempMemory(temp_memory, false /*cancel*/);
|
||||
Dqn_Arena_Free(&arena);
|
||||
}
|
||||
|
||||
for (Dqn_usize guard = 0; guard < Guard_Count; guard++) {
|
||||
Dqn_String8 test_suffix = ArenaGuardTestSuffix(guard);
|
||||
DQN_UTEST_TEST("Zero init arena, temp region then free inside region%.*s", DQN_STRING_FMT(test_suffix)) {
|
||||
Dqn_Arena arena = {};
|
||||
arena.use_after_free_guard = guard == Guard_UseAfterFree;
|
||||
Dqn_ArenaTempMemory temp_memory = Dqn_Arena_BeginTempMemory(&arena);
|
||||
{
|
||||
char *ptr = DQN_CAST(char *)Dqn_Arena_Allocate(&arena, DQN_MEGABYTES(1), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
DQN_UTEST_ASSERT(&test, ptr);
|
||||
Dqn_Arena_Free(&arena, Dqn_ZeroMem_No);
|
||||
}
|
||||
Dqn_Arena_EndTempMemory(temp_memory);
|
||||
Dqn_Arena_Free(&arena, Dqn_ZeroMem_No);
|
||||
}
|
||||
}
|
||||
|
||||
for (Dqn_usize guard = 0; guard < Guard_Count; guard++) {
|
||||
Dqn_String8 test_suffix = ArenaGuardTestSuffix(guard);
|
||||
DQN_UTEST_TEST("Zero init arena, allocate, temp region then free inside region%.*s", DQN_STRING_FMT(test_suffix)) {
|
||||
Dqn_Arena arena = {};
|
||||
arena.use_after_free_guard = guard == Guard_UseAfterFree;
|
||||
|
||||
char *outside = DQN_CAST(char *)Dqn_Arena_Allocate(&arena, DQN_MEGABYTES(1), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
DQN_UTEST_ASSERT(&test, outside);
|
||||
|
||||
Dqn_ArenaTempMemory temp_memory = Dqn_Arena_BeginTempMemory(&arena);
|
||||
{
|
||||
char *inside = DQN_CAST(char *)Dqn_Arena_Allocate(&arena, DQN_MEGABYTES(2), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
DQN_UTEST_ASSERT(&test, inside);
|
||||
Dqn_Arena_Free(&arena, Dqn_ZeroMem_No);
|
||||
}
|
||||
Dqn_Arena_EndTempMemory(temp_memory);
|
||||
Dqn_Arena_Free(&arena, Dqn_ZeroMem_No);
|
||||
}
|
||||
}
|
||||
|
||||
Dqn_usize sizes[] = {DQN_KILOBYTES(1), DQN_KILOBYTES(4), DQN_KILOBYTES(5)};
|
||||
for (Dqn_usize size : sizes) {
|
||||
DQN_UTEST_TEST("Use-after-free guard on %.1f KiB allocation", size / 1024.0) {
|
||||
Dqn_Arena arena = {};
|
||||
arena.use_after_free_guard = true;
|
||||
Dqn_Arena_Grow(&arena, size, false /*commit*/, 0 /*flags*/);
|
||||
|
||||
// NOTE: Wrap in temp memory, allocate and write
|
||||
Dqn_ArenaTempMemory temp_mem = Dqn_Arena_BeginTempMemory(&arena);
|
||||
uintptr_t first_ptr_address = 0;
|
||||
void *ptr = Dqn_Arena_Allocate(&arena, size, 1, Dqn_ZeroMem_Yes);
|
||||
DQN_MEMSET(ptr, 'z', size);
|
||||
Dqn_Arena_EndTempMemory(temp_mem);
|
||||
|
||||
// NOTE: Temp memory is ended, try and write the pointer
|
||||
// we should trigger the use-after-free guard.
|
||||
bool caught = false;
|
||||
__try {
|
||||
DQN_MEMSET(ptr, 'a', size);
|
||||
} __except (1) {
|
||||
caught = true;
|
||||
}
|
||||
|
||||
DQN_UTEST_ASSERTF(&test, caught, "Exception was not triggered, was page protected properly?");
|
||||
Dqn_Arena_Free(&arena, Dqn_ZeroMem_No);
|
||||
DQN_UTEST_TEST("Init arena, allocate, temp region then free inside region") {
|
||||
Dqn_Arena arena = {};
|
||||
char *outside = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(1), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
DQN_UTEST_ASSERT(&test, outside);
|
||||
|
||||
Dqn_ArenaTempMemory temp_memory = Dqn_Arena_BeginTempMemory(&arena);
|
||||
{
|
||||
char *inside = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(2), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
DQN_UTEST_ASSERT(&test, inside);
|
||||
Dqn_Arena_Free(&arena);
|
||||
}
|
||||
Dqn_Arena_EndTempMemory(temp_memory, false /*cancel*/);
|
||||
Dqn_Arena_Free(&arena);
|
||||
}
|
||||
}
|
||||
return test;
|
||||
@ -483,7 +428,7 @@ Dqn_UTest TestDSMap()
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
{
|
||||
uint32_t const MAP_SIZE = 64;
|
||||
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(MAP_SIZE);
|
||||
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(MAP_SIZE);
|
||||
DQN_DEFER { Dqn_DSMap_Deinit(&map); };
|
||||
|
||||
DQN_UTEST_TEST("Find non-existent value") {
|
||||
@ -524,7 +469,7 @@ Dqn_UTest TestDSMap()
|
||||
case DSMapTestType_MakeSlot: prefix = DQN_STRING8("Make slot"); break;
|
||||
}
|
||||
|
||||
DQN_ARENA_TEMP_MEMORY_SCOPE(scratch.arena);
|
||||
Dqn_Arena_TempMemoryScope(scratch.arena);
|
||||
uint32_t const MAP_SIZE = 64;
|
||||
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(MAP_SIZE);
|
||||
DQN_DEFER { Dqn_DSMap_Deinit(&map); };
|
||||
@ -534,7 +479,7 @@ Dqn_UTest TestDSMap()
|
||||
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_Copy(scratch.arena, uint64_t, &value, 1);
|
||||
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value, 1);
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
|
||||
DQN_UTEST_ASSERT(&test, !Dqn_DSMap_Find<uint64_t>(&map, key));
|
||||
DQN_UTEST_ASSERT(&test, !Dqn_DSMap_FindSlot<uint64_t>(&map, key));
|
||||
@ -553,7 +498,7 @@ Dqn_UTest TestDSMap()
|
||||
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_Copy(scratch.arena, uint64_t, &value, 1);
|
||||
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value, 1);
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
|
||||
bool found = false;
|
||||
if (test_type == DSMapTestType_Set) {
|
||||
@ -603,7 +548,7 @@ Dqn_UTest TestDSMap()
|
||||
uint64_t value = 0;
|
||||
uint64_t shrink_threshold = map.size * 1 / 4;
|
||||
for (; map.occupied != shrink_threshold; value++) {
|
||||
uint64_t *val_copy = Dqn_Arena_Copy(scratch.arena, uint64_t, &value, 1);
|
||||
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value, 1);
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
|
||||
|
||||
DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find<uint64_t>(&map, key));
|
||||
@ -616,7 +561,7 @@ Dqn_UTest TestDSMap()
|
||||
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_Copy(scratch.arena, uint64_t, &value, 1);
|
||||
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value, 1);
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
|
||||
Dqn_DSMap_Erase(&map, key);
|
||||
value++;
|
||||
@ -655,7 +600,7 @@ Dqn_UTest TestDSMap()
|
||||
}
|
||||
|
||||
for (; map.occupied != 1; value++) { // NOTE: Remove all items from the table
|
||||
uint64_t *val_copy = Dqn_Arena_Copy(scratch.arena, uint64_t, &value, 1);
|
||||
uint64_t *val_copy = Dqn_Arena_NewCopy(scratch.arena, uint64_t, &value, 1);
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyBuffer(&map, (char *)val_copy, sizeof(*val_copy));
|
||||
DQN_UTEST_ASSERT(&test, Dqn_DSMap_Find<uint64_t>(&map, key));
|
||||
Dqn_DSMap_Erase(&map, key);
|
||||
@ -710,7 +655,7 @@ Dqn_UTest TestFs()
|
||||
|
||||
// NOTE: Read step
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_String8 read_file = Dqn_Fs_ReadString8(SRC_FILE, scratch.allocator);
|
||||
Dqn_String8 read_file = Dqn_Fs_Read(SRC_FILE, scratch.allocator);
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_String8_IsValid(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_String8_Eq(read_file, DQN_STRING8("test")), "read(%zu): %.*s", read_file.size, DQN_STRING_FMT(read_file));
|
||||
@ -840,8 +785,8 @@ Dqn_UTest TestIntrinsics()
|
||||
DQN_UTEST_ASSERTF(&test, a == b, "a: %I64i, b: %I64i", a, b);
|
||||
}
|
||||
|
||||
Dqn_UTest_Begin(&test, "Dqn_CPUClockCycle");
|
||||
Dqn_CPUClockCycle();
|
||||
Dqn_UTest_Begin(&test, "Dqn_CPU_TSC");
|
||||
Dqn_CPU_TSC();
|
||||
Dqn_UTest_End(&test);
|
||||
|
||||
Dqn_UTest_Begin(&test, "Dqn_CompilerReadBarrierAndCPUReadFence");
|
||||
@ -1159,116 +1104,124 @@ Dqn_UTest TestRect()
|
||||
Dqn_UTest test = {};
|
||||
DQN_UTEST_GROUP(test, "Dqn_Rect") {
|
||||
DQN_UTEST_TEST("No intersection") {
|
||||
Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2(0, 0), Dqn_V2(100, 100));
|
||||
Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2(200, 0), Dqn_V2(200, 200));
|
||||
Dqn_Rect a = Dqn_Rect_InitV2x2(Dqn_V2_InitNx1(0), Dqn_V2_InitNx2(100, 100));
|
||||
Dqn_Rect b = Dqn_Rect_InitV2x2(Dqn_V2_InitNx2(200, 0), Dqn_V2_InitNx2(200, 200));
|
||||
Dqn_Rect ab = Dqn_Rect_Intersection(a, b);
|
||||
|
||||
Dqn_V2 ab_max = ab.pos + ab.size;
|
||||
DQN_UTEST_ASSERTF(&test,
|
||||
ab.min.x == 0 && ab.min.y == 0 && ab.max.x == 0 && ab.max.y == 0,
|
||||
"ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }",
|
||||
ab.min.x,
|
||||
ab.min.y,
|
||||
ab.max.x,
|
||||
ab.max.y);
|
||||
ab.pos.x == 0 && ab.pos.y == 0 && ab_max.x == 0 && ab_max.y == 0,
|
||||
"ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }",
|
||||
ab.pos.x,
|
||||
ab.pos.y,
|
||||
ab_max.x,
|
||||
ab_max.y);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("A's min intersects B") {
|
||||
Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2(50, 50), Dqn_V2(100, 100));
|
||||
Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2( 0, 0), Dqn_V2(100, 100));
|
||||
Dqn_Rect a = Dqn_Rect_InitV2x2(Dqn_V2_InitNx2(50, 50), Dqn_V2_InitNx2(100, 100));
|
||||
Dqn_Rect b = Dqn_Rect_InitV2x2(Dqn_V2_InitNx2( 0, 0), Dqn_V2_InitNx2(100, 100));
|
||||
Dqn_Rect ab = Dqn_Rect_Intersection(a, b);
|
||||
|
||||
Dqn_V2 ab_max = ab.pos + ab.size;
|
||||
DQN_UTEST_ASSERTF(&test,
|
||||
ab.min.x == 50 && ab.min.y == 50 && ab.max.x == 100 && ab.max.y == 100,
|
||||
ab.pos.x == 50 && ab.pos.y == 50 && ab_max.x == 100 && ab_max.y == 100,
|
||||
"ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }",
|
||||
ab.min.x,
|
||||
ab.min.y,
|
||||
ab.max.x,
|
||||
ab.max.y);
|
||||
ab.pos.x,
|
||||
ab.pos.y,
|
||||
ab_max.x,
|
||||
ab_max.y);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("B's min intersects A") {
|
||||
Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2( 0, 0), Dqn_V2(100, 100));
|
||||
Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2(50, 50), Dqn_V2(100, 100));
|
||||
Dqn_Rect a = Dqn_Rect_InitV2x2(Dqn_V2_InitNx2( 0, 0), Dqn_V2_InitNx2(100, 100));
|
||||
Dqn_Rect b = Dqn_Rect_InitV2x2(Dqn_V2_InitNx2(50, 50), Dqn_V2_InitNx2(100, 100));
|
||||
Dqn_Rect ab = Dqn_Rect_Intersection(a, b);
|
||||
|
||||
Dqn_V2 ab_max = ab.pos + ab.size;
|
||||
DQN_UTEST_ASSERTF(&test,
|
||||
ab.min.x == 50 && ab.min.y == 50 && ab.max.x == 100 && ab.max.y == 100,
|
||||
ab.pos.x == 50 && ab.pos.y == 50 && ab_max.x == 100 && ab_max.y == 100,
|
||||
"ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }",
|
||||
ab.min.x,
|
||||
ab.min.y,
|
||||
ab.max.x,
|
||||
ab.max.y);
|
||||
ab.pos.x,
|
||||
ab.pos.y,
|
||||
ab_max.x,
|
||||
ab_max.y);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("A's max intersects B") {
|
||||
Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2(-50, -50), Dqn_V2(100, 100));
|
||||
Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2( 0, 0), Dqn_V2(100, 100));
|
||||
Dqn_Rect a = Dqn_Rect_InitV2x2(Dqn_V2_InitNx2(-50, -50), Dqn_V2_InitNx2(100, 100));
|
||||
Dqn_Rect b = Dqn_Rect_InitV2x2(Dqn_V2_InitNx2( 0, 0), Dqn_V2_InitNx2(100, 100));
|
||||
Dqn_Rect ab = Dqn_Rect_Intersection(a, b);
|
||||
|
||||
Dqn_V2 ab_max = ab.pos + ab.size;
|
||||
DQN_UTEST_ASSERTF(&test,
|
||||
ab.min.x == 0 && ab.min.y == 0 && ab.max.x == 50 && ab.max.y == 50,
|
||||
ab.pos.x == 0 && ab.pos.y == 0 && ab_max.x == 50 && ab_max.y == 50,
|
||||
"ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }",
|
||||
ab.min.x,
|
||||
ab.min.y,
|
||||
ab.max.x,
|
||||
ab.max.y);
|
||||
ab.pos.x,
|
||||
ab.pos.y,
|
||||
ab_max.x,
|
||||
ab_max.y);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("B's max intersects A") {
|
||||
Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2( 0, 0), Dqn_V2(100, 100));
|
||||
Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2(-50, -50), Dqn_V2(100, 100));
|
||||
Dqn_Rect a = Dqn_Rect_InitV2x2(Dqn_V2_InitNx2( 0, 0), Dqn_V2_InitNx2(100, 100));
|
||||
Dqn_Rect b = Dqn_Rect_InitV2x2(Dqn_V2_InitNx2(-50, -50), Dqn_V2_InitNx2(100, 100));
|
||||
Dqn_Rect ab = Dqn_Rect_Intersection(a, b);
|
||||
|
||||
Dqn_V2 ab_max = ab.pos + ab.size;
|
||||
DQN_UTEST_ASSERTF(&test,
|
||||
ab.min.x == 0 && ab.min.y == 0 && ab.max.x == 50 && ab.max.y == 50,
|
||||
ab.pos.x == 0 && ab.pos.y == 0 && ab_max.x == 50 && ab_max.y == 50,
|
||||
"ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }",
|
||||
ab.min.x,
|
||||
ab.min.y,
|
||||
ab.max.x,
|
||||
ab.max.y);
|
||||
ab.pos.x,
|
||||
ab.pos.y,
|
||||
ab_max.x,
|
||||
ab_max.y);
|
||||
}
|
||||
|
||||
|
||||
DQN_UTEST_TEST("B contains A") {
|
||||
Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2(25, 25), Dqn_V2( 25, 25));
|
||||
Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2( 0, 0), Dqn_V2(100, 100));
|
||||
Dqn_Rect a = Dqn_Rect_InitV2x2(Dqn_V2_InitNx2(25, 25), Dqn_V2_InitNx2( 25, 25));
|
||||
Dqn_Rect b = Dqn_Rect_InitV2x2(Dqn_V2_InitNx2( 0, 0), Dqn_V2_InitNx2(100, 100));
|
||||
Dqn_Rect ab = Dqn_Rect_Intersection(a, b);
|
||||
|
||||
Dqn_V2 ab_max = ab.pos + ab.size;
|
||||
DQN_UTEST_ASSERTF(&test,
|
||||
ab.min.x == 25 && ab.min.y == 25 && ab.max.x == 50 && ab.max.y == 50,
|
||||
ab.pos.x == 25 && ab.pos.y == 25 && ab_max.x == 50 && ab_max.y == 50,
|
||||
"ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }",
|
||||
ab.min.x,
|
||||
ab.min.y,
|
||||
ab.max.x,
|
||||
ab.max.y);
|
||||
ab.pos.x,
|
||||
ab.pos.y,
|
||||
ab_max.x,
|
||||
ab_max.y);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("A contains B") {
|
||||
Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2( 0, 0), Dqn_V2(100, 100));
|
||||
Dqn_Rect b = Dqn_Rect_InitFromPosAndSize(Dqn_V2(25, 25), Dqn_V2( 25, 25));
|
||||
Dqn_Rect a = Dqn_Rect_InitV2x2(Dqn_V2_InitNx2( 0, 0), Dqn_V2_InitNx2(100, 100));
|
||||
Dqn_Rect b = Dqn_Rect_InitV2x2(Dqn_V2_InitNx2(25, 25), Dqn_V2_InitNx2( 25, 25));
|
||||
Dqn_Rect ab = Dqn_Rect_Intersection(a, b);
|
||||
|
||||
Dqn_V2 ab_max = ab.pos + ab.size;
|
||||
DQN_UTEST_ASSERTF(&test,
|
||||
ab.min.x == 25 && ab.min.y == 25 && ab.max.x == 50 && ab.max.y == 50,
|
||||
ab.pos.x == 25 && ab.pos.y == 25 && ab_max.x == 50 && ab_max.y == 50,
|
||||
"ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }",
|
||||
ab.min.x,
|
||||
ab.min.y,
|
||||
ab.max.x,
|
||||
ab.max.y);
|
||||
ab.pos.x,
|
||||
ab.pos.y,
|
||||
ab_max.x,
|
||||
ab_max.y);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("A equals B") {
|
||||
Dqn_Rect a = Dqn_Rect_InitFromPosAndSize(Dqn_V2(0, 0), Dqn_V2(100, 100));
|
||||
Dqn_Rect a = Dqn_Rect_InitV2x2(Dqn_V2_InitNx2(0, 0), Dqn_V2_InitNx2(100, 100));
|
||||
Dqn_Rect b = a;
|
||||
Dqn_Rect ab = Dqn_Rect_Intersection(a, b);
|
||||
|
||||
Dqn_V2 ab_max = ab.pos + ab.size;
|
||||
DQN_UTEST_ASSERTF(&test,
|
||||
ab.min.x == 0 && ab.min.y == 0 && ab.max.x == 100 && ab.max.y == 100,
|
||||
ab.pos.x == 0 && ab.pos.y == 0 && ab_max.x == 100 && ab_max.y == 100,
|
||||
"ab = { min.x = %.2f, min.y = %.2f, max.x = %.2f. max.y = %.2f }",
|
||||
ab.min.x,
|
||||
ab.min.y,
|
||||
ab.max.x,
|
||||
ab.max.y);
|
||||
ab.pos.x,
|
||||
ab.pos.y,
|
||||
ab_max.x,
|
||||
ab_max.y);
|
||||
}
|
||||
}
|
||||
return test;
|
||||
@ -1416,95 +1369,113 @@ Dqn_UTest TestString8()
|
||||
// NOTE: Dqn_String8_ToI64
|
||||
// =========================================================================================
|
||||
DQN_UTEST_TEST("To I64: Convert null string") {
|
||||
int64_t result = Dqn_String8_ToI64(Dqn_String8_Init(nullptr, 5), 0);
|
||||
DQN_UTEST_ASSERT(&test, result == 0);
|
||||
Dqn_String8ToI64Result result = Dqn_String8_ToI64(Dqn_String8_Init(nullptr, 5), 0);
|
||||
DQN_UTEST_ASSERT(&test, !result.success);
|
||||
DQN_UTEST_ASSERT(&test, result.value == 0);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("To I64: Convert empty string") {
|
||||
int64_t result = Dqn_String8_ToI64(DQN_STRING8(""), 0);
|
||||
DQN_UTEST_ASSERT(&test, result == 0);
|
||||
Dqn_String8ToI64Result result = Dqn_String8_ToI64(DQN_STRING8(""), 0);
|
||||
DQN_UTEST_ASSERT(&test, !result.success);
|
||||
DQN_UTEST_ASSERT(&test, result.value == 0);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("To I64: Convert \"1\"") {
|
||||
int64_t result = Dqn_String8_ToI64(DQN_STRING8("1"), 0);
|
||||
DQN_UTEST_ASSERT(&test, result == 1);
|
||||
Dqn_String8ToI64Result result = Dqn_String8_ToI64(DQN_STRING8("1"), 0);
|
||||
DQN_UTEST_ASSERT(&test, result.success);
|
||||
DQN_UTEST_ASSERT(&test, result.value == 1);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("To I64: Convert \"-0\"") {
|
||||
int64_t result = Dqn_String8_ToI64(DQN_STRING8("-0"), 0);
|
||||
DQN_UTEST_ASSERT(&test, result == 0);
|
||||
Dqn_String8ToI64Result result = Dqn_String8_ToI64(DQN_STRING8("-0"), 0);
|
||||
DQN_UTEST_ASSERT(&test, result.success);
|
||||
DQN_UTEST_ASSERT(&test, result.value == 0);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("To I64: Convert \"-1\"") {
|
||||
int64_t result = Dqn_String8_ToI64(DQN_STRING8("-1"), 0);
|
||||
DQN_UTEST_ASSERT(&test, result == -1);
|
||||
Dqn_String8ToI64Result result = Dqn_String8_ToI64(DQN_STRING8("-1"), 0);
|
||||
DQN_UTEST_ASSERT(&test, result.success);
|
||||
DQN_UTEST_ASSERT(&test, result.value == -1);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("To I64: Convert \"1.2\"") {
|
||||
int64_t result = Dqn_String8_ToI64(DQN_STRING8("1.2"), 0);
|
||||
DQN_UTEST_ASSERT(&test, result == 1);
|
||||
Dqn_String8ToI64Result result = Dqn_String8_ToI64(DQN_STRING8("1.2"), 0);
|
||||
DQN_UTEST_ASSERT(&test, !result.success);
|
||||
DQN_UTEST_ASSERT(&test, result.value == 1);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("To I64: Convert \"1,234\"") {
|
||||
int64_t result = Dqn_String8_ToI64(DQN_STRING8("1,234"), ',');
|
||||
DQN_UTEST_ASSERT(&test, result == 1234);
|
||||
Dqn_String8ToI64Result result = Dqn_String8_ToI64(DQN_STRING8("1,234"), ',');
|
||||
DQN_UTEST_ASSERT(&test, result.success);
|
||||
DQN_UTEST_ASSERT(&test, result.value == 1234);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("To I64: Convert \"1,2\"") {
|
||||
int64_t result = Dqn_String8_ToI64(DQN_STRING8("1,2"), ',');
|
||||
DQN_UTEST_ASSERT(&test, result == 12);
|
||||
Dqn_String8ToI64Result result = Dqn_String8_ToI64(DQN_STRING8("1,2"), ',');
|
||||
DQN_UTEST_ASSERT(&test, result.success);
|
||||
DQN_UTEST_ASSERT(&test, result.value == 12);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("To I64: Convert \"12a3\"") {
|
||||
int64_t result = Dqn_String8_ToI64(DQN_STRING8("12a3"), 0);
|
||||
DQN_UTEST_ASSERT(&test, result == 12);
|
||||
Dqn_String8ToI64Result result = Dqn_String8_ToI64(DQN_STRING8("12a3"), 0);
|
||||
DQN_UTEST_ASSERT(&test, !result.success);
|
||||
DQN_UTEST_ASSERT(&test, result.value == 12);
|
||||
}
|
||||
|
||||
// NOTE: Dqn_String8_ToU64
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
DQN_UTEST_TEST("To U64: Convert nullptr") {
|
||||
uint64_t result = Dqn_String8_ToU64(Dqn_String8_Init(nullptr, 5), 0);
|
||||
DQN_UTEST_ASSERTF(&test, result == 0, "result: %zu", result);
|
||||
Dqn_String8ToU64Result result = Dqn_String8_ToU64(Dqn_String8_Init(nullptr, 5), 0);
|
||||
DQN_UTEST_ASSERT(&test, !result.success);
|
||||
DQN_UTEST_ASSERTF(&test, result.value == 0, "result: %I64u", result.value);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("To U64: Convert empty string") {
|
||||
uint64_t result = Dqn_String8_ToU64(DQN_STRING8(""), 0);
|
||||
DQN_UTEST_ASSERTF(&test, result == 0, "result: %zu", result);
|
||||
Dqn_String8ToU64Result result = Dqn_String8_ToU64(DQN_STRING8(""), 0);
|
||||
DQN_UTEST_ASSERT(&test, !result.success);
|
||||
DQN_UTEST_ASSERTF(&test, result.value == 0, "result: %I64u", result.value);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("To U64: Convert \"1\"") {
|
||||
uint64_t result = Dqn_String8_ToU64(DQN_STRING8("1"), 0);
|
||||
DQN_UTEST_ASSERTF(&test, result == 1, "result: %zu", result);
|
||||
Dqn_String8ToU64Result result = Dqn_String8_ToU64(DQN_STRING8("1"), 0);
|
||||
DQN_UTEST_ASSERT(&test, result.success);
|
||||
DQN_UTEST_ASSERTF(&test, result.value == 1, "result: %I64u", result.value);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("To U64: Convert \"-0\"") {
|
||||
uint64_t result = Dqn_String8_ToU64(DQN_STRING8("-0"), 0);
|
||||
DQN_UTEST_ASSERTF(&test, result == 0, "result: %zu", result);
|
||||
Dqn_String8ToU64Result result = Dqn_String8_ToU64(DQN_STRING8("-0"), 0);
|
||||
DQN_UTEST_ASSERT(&test, !result.success);
|
||||
DQN_UTEST_ASSERTF(&test, result.value == 0, "result: %I64u", result.value);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("To U64: Convert \"-1\"") {
|
||||
uint64_t result = Dqn_String8_ToU64(DQN_STRING8("-1"), 0);
|
||||
DQN_UTEST_ASSERTF(&test, result == 0, "result: %zu", result);
|
||||
Dqn_String8ToU64Result result = Dqn_String8_ToU64(DQN_STRING8("-1"), 0);
|
||||
DQN_UTEST_ASSERT(&test, !result.success);
|
||||
DQN_UTEST_ASSERTF(&test, result.value == 0, "result: %I64u", result.value);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("To U64: Convert \"1.2\"") {
|
||||
uint64_t result = Dqn_String8_ToU64(DQN_STRING8("1.2"), 0);
|
||||
DQN_UTEST_ASSERTF(&test, result == 1, "result: %zu", result);
|
||||
Dqn_String8ToU64Result result = Dqn_String8_ToU64(DQN_STRING8("1.2"), 0);
|
||||
DQN_UTEST_ASSERT(&test, !result.success);
|
||||
DQN_UTEST_ASSERTF(&test, result.value == 1, "result: %I64u", result.value);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("To U64: Convert \"1,234\"") {
|
||||
uint64_t result = Dqn_String8_ToU64(DQN_STRING8("1,234"), ',');
|
||||
DQN_UTEST_ASSERTF(&test, result == 1234, "result: %zu", result);
|
||||
Dqn_String8ToU64Result result = Dqn_String8_ToU64(DQN_STRING8("1,234"), ',');
|
||||
DQN_UTEST_ASSERT(&test, result.success);
|
||||
DQN_UTEST_ASSERTF(&test, result.value == 1234, "result: %I64u", result.value);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("To U64: Convert \"1,2\"") {
|
||||
uint64_t result = Dqn_String8_ToU64(DQN_STRING8("1,2"), ',');
|
||||
DQN_UTEST_ASSERTF(&test, result == 12, "result: %zu", result);
|
||||
Dqn_String8ToU64Result result = Dqn_String8_ToU64(DQN_STRING8("1,2"), ',');
|
||||
DQN_UTEST_ASSERT(&test, result.success);
|
||||
DQN_UTEST_ASSERTF(&test, result.value == 12, "result: %I64u", result.value);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("To U64: Convert \"12a3\"") {
|
||||
uint64_t result = Dqn_String8_ToU64(DQN_STRING8("12a3"), 0);
|
||||
DQN_UTEST_ASSERTF(&test, result == 12, "result: %zu", result);
|
||||
Dqn_String8ToU64Result result = Dqn_String8_ToU64(DQN_STRING8("12a3"), 0);
|
||||
DQN_UTEST_ASSERT(&test, !result.success);
|
||||
DQN_UTEST_ASSERTF(&test, result.value == 12, "result: %I64u", result.value);
|
||||
}
|
||||
|
||||
// NOTE: Dqn_String8_Find
|
||||
@ -1786,14 +1757,15 @@ Dqn_UTest TestWin()
|
||||
|
||||
void CustomLogProc(Dqn_String8 type, int log_type, void *user_data, Dqn_CallSite call_site, char const *fmt, va_list args)
|
||||
{
|
||||
(void)user_data;
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_String8 log = Dqn_Log_MakeString(scratch.allocator, true /*colour*/, type, log_type, call_site, fmt, args);
|
||||
DQN_UTEST_LOG("%.*s", DQN_STRING_FMT(log));
|
||||
}
|
||||
|
||||
void TestRunSuite()
|
||||
void Dqn_TestRunSuite()
|
||||
{
|
||||
Dqn_Library *dqn_library = Dqn_Library_Init(nullptr);
|
||||
Dqn_Library *dqn_library = Dqn_Library_Init();
|
||||
dqn_library->log_callback = CustomLogProc;
|
||||
|
||||
Dqn_UTest tests[] =
|
||||
@ -1832,7 +1804,7 @@ void TestRunSuite()
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
(void)argv; (void)argc;
|
||||
TestRunSuite();
|
||||
Dqn_TestRunSuite();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -194,14 +194,14 @@ void Dqn_UTest_Begin(Dqn_UTest *test, char const *fmt, ...)
|
||||
void Dqn_UTest_End(Dqn_UTest *test)
|
||||
{
|
||||
assert(test->state != Dqn_UTestState_Nil && "Test was marked as ended but a test was never commenced using Dqn_UTest_Begin");
|
||||
int pad_size = DQN_UTEST_RESULT_LPAD - (DQN_UTEST_SPACING + test->name_size);
|
||||
size_t pad_size = DQN_UTEST_RESULT_LPAD - (DQN_UTEST_SPACING + test->name_size);
|
||||
if (pad_size < 0)
|
||||
pad_size = 0;
|
||||
|
||||
char pad_buffer[DQN_UTEST_RESULT_LPAD] = {};
|
||||
memset(pad_buffer, DQN_UTEST_RESULT_PAD_CHAR, pad_size);
|
||||
|
||||
printf("%*s%.*s%.*s", DQN_UTEST_SPACING, "", (int)test->name_size, test->name, pad_size, pad_buffer);
|
||||
printf("%*s%.*s%.*s", DQN_UTEST_SPACING, "", (int)test->name_size, test->name, (int)pad_size, pad_buffer);
|
||||
if (test->state == Dqn_UTestState_TestFailed) {
|
||||
printf(DQN_UTEST_BAD_COLOR " FAILED");
|
||||
} else {
|
||||
|
411
b_stacktrace.h
Normal file
411
b_stacktrace.h
Normal file
@ -0,0 +1,411 @@
|
||||
#if !defined(B_STACKTRACE_INCLUDED)
|
||||
#define B_STACKTRACE_INCLUDED (1)
|
||||
/*
|
||||
b_stacktrace v0.21 -- a cross-platform stack-trace generator
|
||||
SPDX-License-Identifier: MIT
|
||||
URL: https://github.com/iboB/b_stacktrace
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
#define B_STACKTRACE_IMPL before including b_stacktrace.h in *one* C or C++
|
||||
file to create the implementation
|
||||
|
||||
#include "b_stacktrace.h" to get access to the following functions:
|
||||
|
||||
char* b_stacktrace_get_string();
|
||||
Returns a human-readable stack-trace string from the point of view of the
|
||||
caller.
|
||||
The string is allocated with `malloc` and needs to be freed with `free`
|
||||
|
||||
b_stacktrace_handle b_stacktrace_get();
|
||||
Returns a stack-trace handle from the point of view of the caller which
|
||||
can be expanded to a string via b_stacktrace_to_string.
|
||||
The handle is allocated with `malloc` and needs to be freed with `free`
|
||||
|
||||
b_stacktrace_to_string(b_stacktrace_handle stacktrace);
|
||||
Converts a stack-trace handle to a human-readable string.
|
||||
The string is allocated with `malloc` and needs to be freed with `free`
|
||||
|
||||
|
||||
Config
|
||||
======
|
||||
|
||||
#define B_STACKTRACE_API to custom export symbols to export the library
|
||||
functions from a shared lib
|
||||
|
||||
Revision History
|
||||
================
|
||||
|
||||
* 0.21 (2022-12-20) Fixed typo
|
||||
* 0.20 (2022-12-18) Beta.
|
||||
Expanded interface
|
||||
Minor fixes
|
||||
* 0.10 (2020-12-07) Initial public release. Alpha version
|
||||
|
||||
MIT License
|
||||
===========
|
||||
|
||||
Copyright (c) 2020-2023 Borislav Stanimirov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#if !defined(B_STACKTRACE_API)
|
||||
#define B_STACKTRACE_API extern
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
A stacktrace handle
|
||||
*/
|
||||
typedef struct b_stacktrace_tag* b_stacktrace_handle;
|
||||
|
||||
/*
|
||||
Returns a stack-trace handle from the point of view of the caller which
|
||||
can be expanded to a string via b_stacktrace_to_string.
|
||||
The handle is allocated with `malloc` and needs to be freed with `free`
|
||||
*/
|
||||
B_STACKTRACE_API b_stacktrace_handle b_stacktrace_get();
|
||||
|
||||
/*
|
||||
Converts a stack-trace handle to a human-readable string.
|
||||
The string is allocated with `malloc` and needs to be freed with `free`
|
||||
*/
|
||||
B_STACKTRACE_API char* b_stacktrace_to_string(b_stacktrace_handle stacktrace);
|
||||
|
||||
/*
|
||||
Returns a human-readable stack-trace string from the point of view of the
|
||||
caller.
|
||||
The string is allocated with `malloc` and needs to be freed with `free`
|
||||
*/
|
||||
B_STACKTRACE_API char* b_stacktrace_get_string(void);
|
||||
|
||||
/* version */
|
||||
#define B_STACKTRACE_VER_MAJOR 0
|
||||
#define B_STACKTRACE_VER_MINOR 20
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* B_STACKTRACE_INCLUDED */
|
||||
|
||||
#if defined(B_STACKTRACE_IMPL)
|
||||
|
||||
#if defined(__linux__) && !defined(_GNU_SOURCE)
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
typedef struct print_buf {
|
||||
char* buf;
|
||||
int pos;
|
||||
int size;
|
||||
} print_buf;
|
||||
|
||||
static print_buf buf_init(void) {
|
||||
print_buf ret = {
|
||||
(char*) malloc(1024),
|
||||
0,
|
||||
1024
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void buf_printf(print_buf* b, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
const int len = vsnprintf(NULL, 0, fmt, args) + 1;
|
||||
va_end(args);
|
||||
|
||||
const int new_end = b->pos + len;
|
||||
|
||||
if (new_end > b->size) {
|
||||
while (new_end > b->size) b->size *= 2;
|
||||
b->buf = (char*)realloc(b->buf, b->size);
|
||||
}
|
||||
|
||||
va_start(args, fmt);
|
||||
b->pos += vsnprintf(b->buf + b->pos, len, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
char* b_stacktrace_get_string(void) {
|
||||
b_stacktrace_handle h = b_stacktrace_get();
|
||||
char* ret = b_stacktrace_to_string(h);
|
||||
free(h);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define B_STACKTRACE_MAX_DEPTH 1024
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
// #include <Windows.h>
|
||||
#include <TlHelp32.h>
|
||||
#include <DbgHelp.h>
|
||||
|
||||
#pragma comment(lib, "DbgHelp.lib")
|
||||
|
||||
#define B_STACKTRACE_ERROR_FLAG ((DWORD64)1 << 63)
|
||||
|
||||
typedef struct b_stacktrace_entry {
|
||||
DWORD64 AddrPC_Offset;
|
||||
DWORD64 AddrReturn_Offset;
|
||||
} b_stacktrace_entry;
|
||||
|
||||
static int SymInitialize_called = 0;
|
||||
|
||||
b_stacktrace_handle b_stacktrace_get(void) {
|
||||
HANDLE process = GetCurrentProcess();
|
||||
HANDLE thread = GetCurrentThread();
|
||||
CONTEXT context;
|
||||
STACKFRAME64 frame; /* in/out stackframe */
|
||||
DWORD imageType;
|
||||
b_stacktrace_entry* ret = (b_stacktrace_entry*)malloc(B_STACKTRACE_MAX_DEPTH * sizeof(b_stacktrace_entry));
|
||||
int i = 0;
|
||||
|
||||
if (!SymInitialize_called) {
|
||||
SymInitialize(process, NULL, TRUE);
|
||||
SymInitialize_called = 1;
|
||||
}
|
||||
|
||||
RtlCaptureContext(&context);
|
||||
|
||||
memset(&frame, 0, sizeof(frame));
|
||||
#ifdef _M_IX86
|
||||
imageType = IMAGE_FILE_MACHINE_I386;
|
||||
frame.AddrPC.Offset = context.Eip;
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Offset = context.Ebp;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Offset = context.Esp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_X64
|
||||
imageType = IMAGE_FILE_MACHINE_AMD64;
|
||||
frame.AddrPC.Offset = context.Rip;
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Offset = context.Rsp;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Offset = context.Rsp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_IA64
|
||||
imageType = IMAGE_FILE_MACHINE_IA64;
|
||||
frame.AddrPC.Offset = context.StIIP;
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Offset = context.IntSp;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrBStore.Offset = context.RsBSP;
|
||||
frame.AddrBStore.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Offset = context.IntSp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
#else
|
||||
#error "Platform not supported!"
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
b_stacktrace_entry* cur = ret + i++;
|
||||
if (i == B_STACKTRACE_MAX_DEPTH) {
|
||||
cur->AddrPC_Offset = 0;
|
||||
cur->AddrReturn_Offset = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!StackWalk64(imageType, process, thread, &frame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) {
|
||||
cur->AddrPC_Offset = frame.AddrPC.Offset;
|
||||
cur->AddrReturn_Offset = B_STACKTRACE_ERROR_FLAG; /* mark error */
|
||||
cur->AddrReturn_Offset |= GetLastError();
|
||||
break;
|
||||
}
|
||||
|
||||
cur->AddrPC_Offset = frame.AddrPC.Offset;
|
||||
cur->AddrReturn_Offset = frame.AddrReturn.Offset;
|
||||
|
||||
if (frame.AddrReturn.Offset == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (b_stacktrace_handle)(ret);
|
||||
}
|
||||
|
||||
char* b_stacktrace_to_string(b_stacktrace_handle h) {
|
||||
const b_stacktrace_entry* entries = (b_stacktrace_entry*)h;
|
||||
int i = 0;
|
||||
HANDLE process = GetCurrentProcess();
|
||||
print_buf out = buf_init();
|
||||
IMAGEHLP_SYMBOL64* symbol = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + 1024);
|
||||
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
|
||||
symbol->MaxNameLength = 1024;
|
||||
|
||||
while (1) {
|
||||
IMAGEHLP_LINE64 lineData;
|
||||
DWORD lineOffset = 0;
|
||||
DWORD64 symOffset = 0;
|
||||
const b_stacktrace_entry* cur = entries + i++;
|
||||
|
||||
if (cur->AddrReturn_Offset & B_STACKTRACE_ERROR_FLAG) {
|
||||
DWORD error = cur->AddrReturn_Offset & 0xFFFFFFFF;
|
||||
buf_printf(&out, "StackWalk64 error: %d @ %p\n", error, cur->AddrPC_Offset);
|
||||
break;
|
||||
}
|
||||
|
||||
if (cur->AddrPC_Offset == cur->AddrReturn_Offset) {
|
||||
buf_printf(&out, "Stack overflow @ %p\n", cur->AddrPC_Offset);
|
||||
break;
|
||||
}
|
||||
|
||||
SymGetLineFromAddr64(process, cur->AddrPC_Offset, &lineOffset, &lineData);
|
||||
buf_printf(&out, "%s(%d): ", lineData.FileName, lineData.LineNumber);
|
||||
|
||||
if (SymGetSymFromAddr64(process, cur->AddrPC_Offset, &symOffset, symbol)) {
|
||||
buf_printf(&out, "%s\n", symbol->Name);
|
||||
}
|
||||
else {
|
||||
buf_printf(&out, " Unkown symbol @ %p\n", cur->AddrPC_Offset);
|
||||
}
|
||||
|
||||
if (cur->AddrReturn_Offset == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(symbol);
|
||||
return out.buf;
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
#include <execinfo.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
typedef struct b_stacktrace {
|
||||
void* trace[B_STACKTRACE_MAX_DEPTH];
|
||||
int trace_size;
|
||||
} b_stacktrace;
|
||||
|
||||
b_stacktrace_handle b_stacktrace_get(void) {
|
||||
b_stacktrace* ret = (b_stacktrace*)malloc(sizeof(b_stacktrace));
|
||||
ret->trace_size = backtrace(ret->trace, B_STACKTRACE_MAX_DEPTH);
|
||||
return (b_stacktrace_handle)(ret);
|
||||
}
|
||||
|
||||
char* b_stacktrace_to_string(b_stacktrace_handle h) {
|
||||
const b_stacktrace* stacktrace = (b_stacktrace*)h;
|
||||
char** messages = backtrace_symbols(stacktrace->trace, stacktrace->trace_size);
|
||||
print_buf out = buf_init();
|
||||
*out.buf = 0;
|
||||
|
||||
for (int i = 0; i < stacktrace->trace_size; ++i) {
|
||||
buf_printf(&out, "%s\n", messages[i]);
|
||||
}
|
||||
|
||||
free(messages);
|
||||
return out.buf;
|
||||
}
|
||||
|
||||
#elif defined(__linux__)
|
||||
|
||||
#include <execinfo.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct b_stacktrace {
|
||||
void* trace[B_STACKTRACE_MAX_DEPTH];
|
||||
int trace_size;
|
||||
} b_stacktrace;
|
||||
|
||||
b_stacktrace_handle b_stacktrace_get(void) {
|
||||
b_stacktrace* ret = (b_stacktrace*)malloc(sizeof(b_stacktrace));
|
||||
ret->trace_size = backtrace(ret->trace, B_STACKTRACE_MAX_DEPTH);
|
||||
return (b_stacktrace_handle)(ret);
|
||||
}
|
||||
|
||||
char* b_stacktrace_to_string(b_stacktrace_handle h) {
|
||||
const b_stacktrace* stacktrace = (b_stacktrace*)h;
|
||||
char** messages = backtrace_symbols(stacktrace->trace, stacktrace->trace_size);
|
||||
print_buf out = buf_init();
|
||||
|
||||
for (int i = 0; i < stacktrace->trace_size; ++i) {
|
||||
void* tracei = stacktrace->trace[i];
|
||||
char* msg = messages[i];
|
||||
|
||||
/* calculate load offset */
|
||||
Dl_info info;
|
||||
dladdr(tracei, &info);
|
||||
if (info.dli_fbase == (void*)0x400000) {
|
||||
/* address from executable, so don't offset */
|
||||
info.dli_fbase = NULL;
|
||||
}
|
||||
|
||||
while (*msg && *msg != '(') ++msg;
|
||||
*msg = 0;
|
||||
|
||||
{
|
||||
char cmd[1024];
|
||||
char line[2048];
|
||||
|
||||
FILE* fp;
|
||||
snprintf(cmd, 1024, "addr2line -e %s -f -C -p %p 2>/dev/null", messages[i], (void*)((char*)tracei - (char*)info.dli_fbase));
|
||||
|
||||
fp = popen(cmd, "r");
|
||||
if (!fp) {
|
||||
buf_printf(&out, "Failed to generate trace further...\n");
|
||||
break;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
buf_printf(&out, "%s: ", messages[i]);
|
||||
if (strstr(line, "?? ")) {
|
||||
/* just output address if nothing can be found */
|
||||
buf_printf(&out, "%p\n", tracei);
|
||||
}
|
||||
else {
|
||||
buf_printf(&out, "%s", line);
|
||||
}
|
||||
}
|
||||
|
||||
pclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
free(messages);
|
||||
return out.buf;
|
||||
}
|
||||
|
||||
#else
|
||||
/* noop implementation */
|
||||
char* b_stacktrace_get_string(void) {
|
||||
print_buf out = buf_init();
|
||||
buf_printf("b_stacktrace: unsupported platform\n");
|
||||
return out.buf;
|
||||
}
|
||||
#endif /* platform */
|
||||
|
||||
#endif /* B_STACKTRACE_IMPL */
|
343
dqn.h
343
dqn.h
@ -1,75 +1,300 @@
|
||||
// NOTE: [$CFGM] Config macros =====================================================================
|
||||
// dqn.h "Personal standard library" | MIT licensed | git.doylet.dev/dqn
|
||||
//
|
||||
// #define DQN_IMPLEMENTATION
|
||||
// Define this in one and only one C++ file to enable the implementation
|
||||
// code of the header file
|
||||
// This library is a single-header file-esque library with inspiration taken
|
||||
// from STB libraries for ease of integration and use. It defines a bunch of
|
||||
// primitives and standard library functions that are missing and or more
|
||||
// appropriate for development in modern day computing (e.g. cache friendly
|
||||
// memory management, 64bit MMU, non-pessimized APIs that aren't constrained by
|
||||
// the language specification and operate closer to the OS).
|
||||
//
|
||||
// #define DQN_NO_ASSERT
|
||||
// Turn all assertion macros to no-ops
|
||||
// Define DQN_IMPLEMENTATION macro in one and only one translation unit to
|
||||
// enable the implementation of this library, for example:
|
||||
//
|
||||
// #define DQN_NO_CHECK_BREAK
|
||||
// Disable debug break when a check macro's expression fails. Instead only
|
||||
// the error will be logged.
|
||||
// #define DQN_IMEPLEMENTATION
|
||||
// #include "dqn.h"
|
||||
//
|
||||
// #define DQN_NO_WIN32_MIN_HEADER
|
||||
// Define this to stop this library from defining a minimal subset of Win32
|
||||
// prototypes and definitions in this file. Useful for stopping redefinition
|
||||
// of symbols if another library includes "Windows.h"
|
||||
// Additionally, this library supports including/excluding specific sections
|
||||
// of the library by using #define on the name of the section. These names are
|
||||
// documented in the section table of contents at the #define column, for
|
||||
// example:
|
||||
//
|
||||
// #define DQN_STATIC_API
|
||||
// Apply static to all function definitions and disable external linkage to
|
||||
// other translation units.
|
||||
// #define DQN_ONLY_VARRAY
|
||||
// #define DQN_ONLY_WIN
|
||||
//
|
||||
// #define DQN_STB_SPRINTF_HEADER_ONLY
|
||||
// Define this to stop this library from defining
|
||||
// STB_SPRINTF_IMPLEMENTATION. Useful if another library uses and includes
|
||||
// "stb_sprintf.h"
|
||||
// Compiles the library with all optional APIs turned off except virtual arrays
|
||||
// and the Win32 helpers. Alternatively:
|
||||
//
|
||||
// #define DQN_MEMSET_BYTE 0
|
||||
// Change the byte that DQN_MEMSET will clear memory with. By default this
|
||||
// is set to 0. Some of this library API accepts are clear memory parameter
|
||||
// to scrub memory after certain operations.
|
||||
// #define DQN_NO_VARRAY
|
||||
// #define DQN_NO_WIN
|
||||
//
|
||||
// #define DQN_LEAK_TRACING
|
||||
// When defined to some allocating calls in the library will automatically
|
||||
// get passed in the file name, function name, line number and an optional
|
||||
// leak_msg.
|
||||
// Compiles the library with all optional APIs turned on except the previously
|
||||
// mentioned APIs.
|
||||
//
|
||||
// Below is a table of contents that describes what you can find in each header
|
||||
// of this library and additional macros that can be defined to customise the
|
||||
// behaviour of this library.
|
||||
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
#error Leak tracing not supported because we enter an infinite leak tracing loop tracing our own allocations made to tracks leaks in the internal leak table.
|
||||
#if !defined(DQN_H)
|
||||
#if defined(DQN_ONLY_VARRAY) || \
|
||||
defined(DQN_ONLY_FARRAY) || \
|
||||
defined(DQN_ONLY_DSMAP) || \
|
||||
defined(DQN_ONLY_LIST) || \
|
||||
defined(DQN_ONLY_FSTRING8) || \
|
||||
defined(DQN_ONLY_FS) || \
|
||||
defined(DQN_ONLY_WINNET) || \
|
||||
defined(DQN_ONLY_WIN) || \
|
||||
defined(DQN_ONLY_V2) || \
|
||||
defined(DQN_ONLY_V3) || \
|
||||
defined(DQN_ONLY_V4) || \
|
||||
defined(DQN_ONLY_M4) || \
|
||||
defined(DQN_ONLY_RECT) || \
|
||||
defined(DQN_ONLY_JSON_BUILDER) || \
|
||||
defined(DQN_ONLY_BIN)
|
||||
|
||||
#if !defined(DQN_ONLY_VARRAY)
|
||||
#define DQN_NO_VARRAY
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_FARRAY)
|
||||
#define DQN_NO_FARRAY
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_DSMAP)
|
||||
#define DQN_NO_DSMAP
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_LIST)
|
||||
#define DQN_NO_LIST
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_FSTRING8)
|
||||
#define DQN_NO_FSTRING8
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_FS)
|
||||
#define DQN_NO_FS
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_WINNET)
|
||||
#define DQN_NO_WINNET
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_WIN)
|
||||
#define DQN_NO_WIN
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_V2)
|
||||
#define DQN_NO_V2
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_V3)
|
||||
#define DQN_NO_V3
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_V4)
|
||||
#define DQN_NO_V4
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_M4)
|
||||
#define DQN_NO_M4
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_RECT)
|
||||
#define DQN_NO_RECT
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_JSON_BUILDER)
|
||||
#define DQN_NO_JSON_BUILDER
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_BIN)
|
||||
#define DQN_NO_BIN
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_PROFILER)
|
||||
#define DQN_NO_PROFILER
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//
|
||||
// #define DQN_DEBUG_THREAD_CONTEXT
|
||||
// Define this macro to record allocation stats for arenas used in the
|
||||
// thread context. The thread context arena stats can be printed by using
|
||||
// Dqn_Library_DumpThreadContextArenaStat.
|
||||
#if !defined(DQN_H)
|
||||
#define DQN_H
|
||||
#include <stdarg.h> // va_list
|
||||
#include <stdio.h> // fprintf, FILE, stdout, stderr
|
||||
#include <stdint.h> // [u]int_*, ...
|
||||
#include <limits.h> // [U]INT_MAX, ...
|
||||
// NOTE: Table of Contents =========================================================================
|
||||
// Index | #define Label | Description
|
||||
// NOTE: C Headers =================================================================================
|
||||
#include <stdarg.h> // | | va_list
|
||||
#include <stdio.h> // | | fprintf, FILE, stdout, stderr
|
||||
#include <stdint.h> // | | [u]int_*, ...
|
||||
#include <limits.h> // | | [U]INT_MAX, ...
|
||||
|
||||
#include "dqn_core.h"
|
||||
#include "dqn_memory.h"
|
||||
#include "dqn_print.h"
|
||||
#include "dqn_strings.h"
|
||||
#include "dqn_platform.h"
|
||||
#include "dqn_containers.h"
|
||||
#include "dqn_math.h"
|
||||
#include "dqn_misc.h"
|
||||
#include "dqn_hash.h"
|
||||
// NOTE: Dqn_Base ==================================================================================
|
||||
// [$CMAC] Compiler macros | | Macros for the compiler
|
||||
// [$MACR] Macros | | Define macros used in the library
|
||||
// [$TYPE] Types | | Basic types and typedefs
|
||||
// [$INTR] Intrinsics | | Atomics, cpuid, ticket mutex
|
||||
// [$TMUT] Dqn_TicketMutex | | Userland mutex via spinlocking atomics
|
||||
|
||||
// NOTE: Additional Configuration
|
||||
// - Override the default heap-allocation routine that is called when the
|
||||
// default Dqn_Allocator is used by #define-ing. By default we use the OS's
|
||||
// virtual memory allocators (e.g. VirtualAlloc on Windows and mmap on Linux).
|
||||
//
|
||||
// DQN_ALLOC(size)
|
||||
// DQN_DEALLOC(ptr, size)
|
||||
//
|
||||
// - Override the byte-manipulation routines by #define-ing. By default we use
|
||||
// <strings.h>
|
||||
//
|
||||
// DQN_MEMCPY(dest, src, count)
|
||||
// DQN_MEMSET(dest, value, count)
|
||||
// DQN_MEMCMP(lhs, rhs, count)
|
||||
// DQN_MEMMOVE(dest, src, count)
|
||||
//
|
||||
// - Override these math functions. By default we use <math.h>
|
||||
//
|
||||
// DQN_SQRTF(val)
|
||||
// DQN_SINF(val)
|
||||
// DQN_COSF(val)
|
||||
// DQN_TANF(val)
|
||||
//
|
||||
// - Change the prefix to all declared functions in the library by #define-ing.
|
||||
//
|
||||
// DQN_API
|
||||
//
|
||||
// - Apply static to all function definitions and disable external linkage to
|
||||
// other translation units by #define-ing. This macro is only used if DQN_API
|
||||
// is not overriden.
|
||||
//
|
||||
// DQN_STATIC_API
|
||||
//
|
||||
// - Turn all assertion macros to no-ops except for hard asserts (which are
|
||||
// always enabled and represent unrecoverable errors in the library).
|
||||
//
|
||||
// DQN_NO_ASSERT
|
||||
//
|
||||
// - Augment DQN_CHECK(expr) macro's behaviour. By default it will trigger a
|
||||
// debugger break when when the expression evalutes false otherwise by
|
||||
// #define-ing this macro it will evaluate to false and DQN_CHECK is usually
|
||||
// used in a if branch to recover gracefully from the failed condition.
|
||||
//
|
||||
// DQN_NO_CHECK_BREAK
|
||||
|
||||
#include "dqn_base.h"
|
||||
|
||||
// NOTE: Dqn_External ==============================================================================
|
||||
// [$BSTK] b_stacktrace | | Generating call stacktraces
|
||||
// [$OS_H] OS Headers | | Headers from the operating system
|
||||
// [$STBS] stb_sprintf | | Portable sprintf
|
||||
#include "dqn_external.h"
|
||||
|
||||
// NOTE: Additional Configuration
|
||||
// - Define this to stop this library from defining a minimal subset of Win32
|
||||
// prototypes and definitions in this file. You should use this macro if you
|
||||
// intend to #include <Windows.h> yourself to avoid symbol conflicts with
|
||||
// the redefined declarations in this library.
|
||||
//
|
||||
// DQN_NO_WIN32_MIN_HEADER
|
||||
//
|
||||
// - Define this to stop this library from defining STB_SPRINTF_IMPLEMENTATION.
|
||||
// Useful if another library uses and includes "stb_sprintf.h"
|
||||
//
|
||||
// DQN_STB_SPRINTF_HEADER_ONLY
|
||||
|
||||
// NOTE: Dqn_Memory ================================================================================
|
||||
// [$ALLO] Dqn_Allocator | | Generic allocator interface
|
||||
// [$VMEM] Dqn_VMem | | Virtual memory allocation
|
||||
// [$MEMB] Dqn_MemBlock | | Virtual memory blocks
|
||||
// [$AREN] Dqn_Arena | | Growing bump allocator
|
||||
// [$ACAT] Dqn_ArenaCatalog | | Collate, create & manage arenas in a catalog
|
||||
#include "dqn_memory.h"
|
||||
|
||||
// NOTE: Dqn_Debug =================================================================================
|
||||
// [$DEBM] Debug Macros | |
|
||||
// [$CALL] Dqn_CallSite | | Source code location/tracing
|
||||
// [$DEBG] Dqn_Debug | | Debugging tools/helpers
|
||||
// [$LLOG] Dqn_Log | | Console logging macros
|
||||
#include "dqn_debug.h"
|
||||
|
||||
// NOTE: Additional Configuration
|
||||
// - Override the default break into the active debugger function. By default
|
||||
// we use __debugbreak() on Windows and raise(SIGTRAP) on other platforms.
|
||||
//
|
||||
// DQN_DEBUG_BREAK
|
||||
//
|
||||
// - Change the byte that DQN_MEMSET will clear memory with. By default this
|
||||
// is set to 0. Some of the API's in this library accept a Dqn_ZeroMem enum
|
||||
// which scrubs memory with this #define-d value.
|
||||
//
|
||||
// DQN_MEMSET_BYTE
|
||||
//
|
||||
// - Override the stack trace dump functionality on invocation of this macro.
|
||||
// Currently this is only used in asserts.
|
||||
//
|
||||
// DQN_DUMP_STACK_TRACE
|
||||
//
|
||||
// - Enable memory leak tracking when requesting memory from the OS via this
|
||||
// library. For example calls to Dqn_VMem_Reserve or DQN_ALLOC are recorded.
|
||||
// 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()
|
||||
//
|
||||
// You may mark sections of your program as allowed to leak memory by setting
|
||||
// the arena's or Dqn_Library's runtime struct `allocs_are_allowed_to_leak`
|
||||
// flag.
|
||||
//
|
||||
// DQN_LEAK_TRACING
|
||||
|
||||
// NOTE: Dqn_Strings ===============================================================================
|
||||
// [$CSTR] Dqn_CString8 | | C-string helpers
|
||||
// [$STR8] Dqn_String8 | | Pointer and length strings
|
||||
// [$FSTR] Dqn_FString8 | DQN_FSTRING8 | Fixed-size strings
|
||||
// [$STRB] Dqn_String8Builder | |
|
||||
// [$CHAR] Dqn_Char | | Character ascii/digit.. helpers
|
||||
// [$UTFX] Dqn_UTF | | Unicode helpers
|
||||
#include "dqn_strings.h"
|
||||
|
||||
// NOTE: Dqn_Containers ============================================================================
|
||||
// [$VARR] Dqn_VArray | DQN_VARRAY | Array backed by virtual memory arena
|
||||
// [$FARR] Dqn_FArray | DQN_FARRAY | Fixed-size arrays
|
||||
// [$DMAP] Dqn_DSMap | DQN_DSMAP | Hashtable, 70% max load, PoT size, linear probe, chain repair
|
||||
// [$LIST] Dqn_List | DQN_LIST | Chunked linked lists, append only
|
||||
#include "dqn_containers.h"
|
||||
|
||||
// NOTE: Dqn_Platform ==============================================================================
|
||||
// [$PRIN] Dqn_Print | | Console printing
|
||||
// [$FSYS] Dqn_Fs | DQN_FS | Filesystem helpers
|
||||
// [$DATE] Dqn_Date | | Date-time helpers
|
||||
// [$WIND] Dqn_Win | | Windows OS helpers
|
||||
// [$WINN] Dqn_WinNet | DQN_WINNET | Windows internet download/query helpers
|
||||
// [$OSYS] Dqn_OS | DQN_WIN | Operating-system APIs
|
||||
// [$TCTX] Dqn_ThreadContext | | Per-thread data structure e.g. temp arenas
|
||||
|
||||
// NOTE: Additional Configuration
|
||||
// - Define this macro to record allocation stats for arenas used in the
|
||||
// thread context. The thread context arena stats can be printed by using
|
||||
// Dqn_Library_DumpThreadContextArenaStat.
|
||||
//
|
||||
// DQN_DEBUG_THREAD_CONTEXT
|
||||
|
||||
#include "dqn_platform.h"
|
||||
|
||||
// NOTE: Dqn_Math ==================================================================================
|
||||
// [$VEC2] Dqn_V2, V2i | DQN_V2 |
|
||||
// [$VEC3] Dqn_V3, V3i | DQN_V3 |
|
||||
// [$VEC4] Dqn_V4, V4i | DQN_V4 |
|
||||
// [$MAT4] Dqn_M4 | DQN_M4 |
|
||||
// [$RECT] Dqn_Rect | DQN_RECT |
|
||||
// [$MATH] Other | |
|
||||
#include "dqn_math.h"
|
||||
|
||||
// NOTE: Dqn_Hash ==================================================================================
|
||||
// [$FNV1] Dqn_FNV1A | | Hash(x) -> 32/64bit via FNV1a
|
||||
// [$MMUR] Dqn_MurmurHash3 | | Hash(x) -> 32/128bit via MurmurHash3
|
||||
#include "dqn_hash.h"
|
||||
|
||||
// NOTE: Dqn_Helpers ===============================================================================
|
||||
// [$JSON] Dqn_JSONBuilder | DQN_JSON_BUILDER | Construct json output
|
||||
// [$BHEX] Dqn_Bin | DQN_BIN | Binary <-> hex helpers
|
||||
// [$BSEA] Dqn_BinarySearch | | Binary search
|
||||
// [$BITS] Dqn_Bit | | Bitset manipulation
|
||||
// [$SAFE] Dqn_Safe | | Safe arithmetic, casts, asserts
|
||||
// [$MISC] Misc | | Uncategorised helper functions
|
||||
// [$DLIB] Dqn_Library | | Globally shared runtime data for this library
|
||||
// [$PROF] Dqn_Profiler | DQN_PROFILER | Profiler that measures using a timestamp counter
|
||||
#include "dqn_helpers.h"
|
||||
#endif // DQN_H
|
||||
|
||||
#if defined(DQN_IMPLEMENTATION)
|
||||
#include "dqn_platform.cpp"
|
||||
#include "dqn_memory.cpp"
|
||||
#include "dqn_print.cpp"
|
||||
#include "dqn_strings.cpp"
|
||||
#include "dqn_containers.cpp"
|
||||
#include "dqn_math.cpp"
|
||||
#include "dqn_misc.cpp"
|
||||
#include "dqn_hash.cpp"
|
||||
#include "dqn_core.cpp"
|
||||
#include "dqn_base.cpp"
|
||||
#include "dqn_external.cpp"
|
||||
#include "dqn_memory.cpp"
|
||||
#include "dqn_debug.cpp"
|
||||
#include "dqn_strings.cpp"
|
||||
#include "dqn_containers.cpp"
|
||||
#include "dqn_platform.cpp"
|
||||
#include "dqn_math.cpp"
|
||||
#include "dqn_hash.cpp"
|
||||
#include "dqn_helpers.cpp"
|
||||
#endif // DQN_IMPLEMENTATION
|
||||
|
@ -7,13 +7,13 @@
|
||||
Dqn_CPUIDRegisters Dqn_CPUID(int function_id)
|
||||
{
|
||||
Dqn_CPUIDRegisters result = {};
|
||||
#if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG)
|
||||
#if defined(DQN_COMPILER_W32_MSVC)
|
||||
__cpuid(DQN_CAST(int *)result.array, function_id);
|
||||
#elif defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG)
|
||||
#elif defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG)
|
||||
__get_cpuid(function_id, &result.array[0] /*eax*/, &result.array[1] /*ebx*/, &result.array[2] /*ecx*/ , &result.array[3] /*edx*/);
|
||||
#else
|
||||
#else
|
||||
#error "Compiler not supported"
|
||||
#endif
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DQN_OS_ARM64)
|
@ -1,20 +1,9 @@
|
||||
// NOTE: Table Of Contents =========================================================================
|
||||
// Index | Disable #define | Description
|
||||
// =================================================================================================
|
||||
// [$CMAC] Compiler macros | | Macros for the compiler
|
||||
// [$MACR] Macros | | Define macros used in the library
|
||||
// [$TYPE] Typedefs | | Typedefs used in the library
|
||||
// [$GSTR] Global Structs | | Forward declare useful structs
|
||||
// [$INTR] Intrinsics | | Atomics, cpuid, ticket mutex
|
||||
// [$TMUT] Dqn_TicketMutex | | Userland mutex via spinlocking atomics
|
||||
// [$CALL] Dqn_CallSite | | Source code location/tracing
|
||||
// ===================+=================+===========================================================
|
||||
|
||||
// NOTE: [$CMAC] Compiler macros ===================================================================
|
||||
// NOTE: Warning! Order is important here, clang-cl on Windows defines _MSC_VER
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(__clang__)
|
||||
#define DQN_COMPILER_W32_CLANG
|
||||
#define DQN_COMPILER_CLANG
|
||||
#else
|
||||
#define DQN_COMPILER_W32_MSVC
|
||||
#endif
|
||||
@ -24,8 +13,8 @@
|
||||
#define DQN_COMPILER_GCC
|
||||
#endif
|
||||
|
||||
// Declare struct literals that work in both C and C++ because the syntax is
|
||||
// different between languages.
|
||||
// NOTE: Declare struct literals that work in both C and C++ because the syntax
|
||||
// is different between languages.
|
||||
#if 0
|
||||
struct Foo { int a; }
|
||||
struct Foo foo = DQN_LITERAL(Foo){32}; // Works on both C and C++
|
||||
@ -63,9 +52,10 @@
|
||||
#define DQN_FOR_UINDEX(index, size) for (Dqn_usize index = 0; index < size; index++)
|
||||
#define DQN_FOR_IINDEX(index, size) for (Dqn_isize index = 0; index < size; index++)
|
||||
|
||||
#define Dqn_PowerOfTwoAlign(value, power_of_two) (((value) + ((power_of_two) - 1)) & ~((power_of_two) - 1))
|
||||
#define Dqn_IsPowerOfTwo(value) (((value) & (value - 1)) == 0)
|
||||
#define Dqn_IsPowerOfTwoAligned(value, power_of_two) (((value) & (power_of_two - 1)) == 0)
|
||||
#define Dqn_AlignUpPowerOfTwo(value, pot) (((uintptr_t)(value) + ((uintptr_t)(pot) - 1)) & ~((uintptr_t)(pot) - 1))
|
||||
#define Dqn_AlignDownPowerOfTwo(value, pot) ((uintptr_t)(value) & ((uintptr_t)(pot) - 1))
|
||||
#define Dqn_IsPowerOfTwo(value) ((((uintptr_t)(value)) & (((uintptr_t)(value)) - 1)) == 0)
|
||||
#define Dqn_IsPowerOfTwoAligned(value, pot) ((((uintptr_t)value) & (((uintptr_t)pot) - 1)) == 0)
|
||||
|
||||
// NOTE: Alloc Macros ==============================================================================
|
||||
#if !defined(DQN_ALLOC)
|
||||
@ -76,7 +66,7 @@
|
||||
#define DQN_DEALLOC(ptr, size) Dqn_VMem_Release(ptr, size)
|
||||
#endif
|
||||
|
||||
// NOTE: String.h Dependnecies =====================================================================
|
||||
// NOTE: String.h Dependencies =====================================================================
|
||||
#if !defined(DQN_MEMCPY) || !defined(DQN_MEMSET) || !defined(DQN_MEMCMP) || !defined(DQN_MEMMOVE)
|
||||
#include <string.h>
|
||||
#if !defined(DQN_MEMCPY)
|
||||
@ -86,14 +76,14 @@
|
||||
#define DQN_MEMSET(dest, value, count) memset(dest, value, count)
|
||||
#endif
|
||||
#if !defined(DQN_MEMCMP)
|
||||
#define DQN_MEMCMP(ptr1, ptr2, num) memcmp(ptr1, ptr2, num)
|
||||
#define DQN_MEMCMP(lhs, rhs, count) memcmp(lhs, rhs, count)
|
||||
#endif
|
||||
#if !defined(DQN_MEMMOVE)
|
||||
#define DQN_MEMMOVE(dest, src, num) memmove(dest, src, num)
|
||||
#define DQN_MEMMOVE(dest, src, count) memmove(dest, src, count)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// NOTE: Math.h Dependnecies =======================================================================
|
||||
// NOTE: Math.h Dependencies =======================================================================
|
||||
#if !defined(DQN_SQRTF) || !defined(DQN_SINF) || !defined(DQN_COSF) || !defined(DQN_TANF)
|
||||
#include <math.h>
|
||||
#define DQN_SQRTF(val) sqrtf(val)
|
||||
@ -115,8 +105,8 @@
|
||||
#define DQN_RADIAN_TO_DEGREE(radians) ((radians) * (180.f * DQN_PI))
|
||||
|
||||
#define DQN_ABS(val) (((val) < 0) ? (-(val)) : (val))
|
||||
#define DQN_MAX(a, b) ((a > b) ? (a) : (b))
|
||||
#define DQN_MIN(a, b) ((a < b) ? (a) : (b))
|
||||
#define DQN_MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#define DQN_MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define DQN_CLAMP(val, lo, hi) DQN_MAX(DQN_MIN(val, hi), lo)
|
||||
#define DQN_SQUARED(val) ((val) * (val))
|
||||
|
||||
@ -168,31 +158,12 @@
|
||||
#define DQN_DAYS_TO_S(val) (DQN_HOURS_TO_S(val) * 24ULL)
|
||||
#define DQN_YEARS_TO_S(val) (DQN_DAYS_TO_S(val) * 365ULL)
|
||||
|
||||
// NOTE: Debug Macros ==============================================================================
|
||||
#if !defined(DQN_DEBUG_BREAK)
|
||||
#if defined(NDEBUG)
|
||||
#define DQN_DEBUG_BREAK
|
||||
#else
|
||||
#if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG)
|
||||
#define DQN_DEBUG_BREAK __debugbreak()
|
||||
#elif defined(DQN_COMPILER_CLANG) || defined(DQN_COMPILER_GCC)
|
||||
#include <signal.h>
|
||||
#define DQN_DEBUG_BREAK raise(SIGTRAP)
|
||||
#elif
|
||||
#error "Unhandled compiler"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(DQN_MEMSET_BYTE)
|
||||
#define DQN_MEMSET_BYTE 0
|
||||
#endif
|
||||
|
||||
// NOTE: Assert Macros =============================================================================
|
||||
#define DQN_HARD_ASSERT(expr) DQN_HARD_ASSERTF(expr, "")
|
||||
#define DQN_HARD_ASSERTF(expr, fmt, ...) \
|
||||
if (!(expr)) { \
|
||||
Dqn_Log_ErrorF("Hard assert triggered " #expr ". " fmt, ##__VA_ARGS__); \
|
||||
DQN_DUMP_STACK_TRACE; \
|
||||
DQN_DEBUG_BREAK; \
|
||||
}
|
||||
|
||||
@ -204,6 +175,7 @@
|
||||
#define DQN_ASSERTF(expr, fmt, ...) \
|
||||
if (!(expr)) { \
|
||||
Dqn_Log_ErrorF("Assert triggered " #expr ". " fmt, ##__VA_ARGS__); \
|
||||
DQN_DUMP_STACK_TRACE; \
|
||||
DQN_DEBUG_BREAK; \
|
||||
}
|
||||
#endif
|
||||
@ -243,6 +215,7 @@
|
||||
#endif
|
||||
|
||||
// NOTE: Defer Macro ===============================================================================
|
||||
#if defined(__cplusplus)
|
||||
#if 0
|
||||
#include <stdio.h>
|
||||
int main()
|
||||
@ -273,13 +246,14 @@ struct Dqn_DeferHelper
|
||||
|
||||
#define DQN_UNIQUE_NAME(prefix) DQN_TOKEN_COMBINE(prefix, __LINE__)
|
||||
#define DQN_DEFER const auto DQN_UNIQUE_NAME(defer_lambda_) = Dqn_DeferHelper() + [&]()
|
||||
#endif // defined(__cplusplus)
|
||||
|
||||
#define DQN_DEFER_LOOP(begin, end) \
|
||||
for (bool DQN_UNIQUE_NAME(once) = (begin, true); \
|
||||
DQN_UNIQUE_NAME(once); \
|
||||
end, DQN_UNIQUE_NAME(once) = false)
|
||||
|
||||
// NOTE: [$TYPE] Typedefs ==========================================================================
|
||||
// NOTE: [$TYPE] Types =============================================================================
|
||||
typedef intptr_t Dqn_isize;
|
||||
typedef uintptr_t Dqn_usize;
|
||||
typedef intptr_t Dqn_isize;
|
||||
@ -292,25 +266,40 @@ typedef int32_t Dqn_b32;
|
||||
#define DQN_ISIZE_MAX INTPTR_MAX
|
||||
#define DQN_ISIZE_MIN INTPTR_MIN
|
||||
|
||||
// NOTE [$GSTR] Global Structs =====================================================================
|
||||
typedef enum Dqn_ZeroMem
|
||||
{
|
||||
Dqn_ZeroMem_No, // Memory can be handed out without zero-ing it out
|
||||
Dqn_ZeroMem_Yes, // Memory should be zero-ed out before giving to the callee
|
||||
}
|
||||
Dqn_ZeroMem;
|
||||
|
||||
struct Dqn_String8
|
||||
{
|
||||
char *data; // The bytes of the string
|
||||
Dqn_usize size; // The number of bytes in the string
|
||||
|
||||
#if defined(__cplusplus)
|
||||
char const *begin() const { return data; } // Const begin iterator for range-for loops
|
||||
char const *end () const { return data + size; } // Const end iterator for range-for loops
|
||||
char *begin() { return data; } // Begin iterator for range-for loops
|
||||
char *end () { return data + size; } // End iterator for range-for loops
|
||||
char const *begin() const { return data; }
|
||||
char const *end () const { return data + size; }
|
||||
char *begin() { return data; }
|
||||
char *end () { return data + size; }
|
||||
#endif
|
||||
};
|
||||
|
||||
// NOTE: [$INTR] Intrinsics ========================================================================
|
||||
typedef enum Dqn_ZeroMem {
|
||||
Dqn_ZeroMem_No, ///< Memory can be handed out without zero-ing it out
|
||||
Dqn_ZeroMem_Yes, ///< Memory should be zero-ed out before giving to the callee
|
||||
} Dqn_ZeroMem;
|
||||
// Platform agnostic functions for CPU level instructions like atomics, barriers
|
||||
// and timestamp counters.
|
||||
//
|
||||
// NOTE: API
|
||||
// @proc Dqn_Atomic_SetValue64, Dqn_Atomic_SetValue32
|
||||
// @desc Atomically set the value into the target using an atomic compare and
|
||||
// swap.
|
||||
// @param[in,out] target The target pointer to set atomically
|
||||
// @param[in] value The value to set atomically into the target
|
||||
// @return The value that was last stored in the target
|
||||
|
||||
// @proc Dqn_CPUID
|
||||
// Execute 'CPUID' instruction to query the capabilities of the current CPU.
|
||||
|
||||
// NOTE: Dqn_Atomic_Add/Exchange return the previous value store in the target
|
||||
#if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG)
|
||||
@ -321,24 +310,22 @@ typedef enum Dqn_ZeroMem {
|
||||
#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_CPUClockCycle() __rdtsc()
|
||||
#define Dqn_CPU_TSC() __rdtsc()
|
||||
#define Dqn_CompilerReadBarrierAndCPUReadFence _ReadBarrier(); _mm_lfence()
|
||||
#define Dqn_CompilerWriteBarrierAndCPUWriteFence _WriteBarrier(); _mm_sfence()
|
||||
#elif defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG)
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
#else
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
|
||||
#define Dqn_Atomic_AddU32(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL)
|
||||
#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)
|
||||
#if defined(DQN_COMPILER_GCC)
|
||||
#define Dqn_CPUClockCycle() __rdtsc()
|
||||
#define Dqn_CPU_TSC() __rdtsc()
|
||||
#else
|
||||
#define Dqn_CPUClockCycle() __builtin_readcyclecounter()
|
||||
#define Dqn_CPU_TSC() __builtin_readcyclecounter()
|
||||
#endif
|
||||
#define Dqn_CompilerReadBarrierAndCPUReadFence asm volatile("lfence" ::: "memory")
|
||||
#define Dqn_CompilerWriteBarrierAndCPUWriteFence asm volatile("sfence" ::: "memory")
|
||||
@ -346,10 +333,6 @@ typedef enum Dqn_ZeroMem {
|
||||
#error "Compiler not supported"
|
||||
#endif
|
||||
|
||||
/// Atomically set the value into the target using an atomic compare and swap.
|
||||
/// @param[in,out] target The target pointer to set atomically
|
||||
/// @param[in] value The value to set atomically into the target
|
||||
/// @return The value that was last stored in the target
|
||||
DQN_FORCE_INLINE uint64_t Dqn_Atomic_SetValue64(uint64_t volatile *target, uint64_t value)
|
||||
{
|
||||
#if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG)
|
||||
@ -366,10 +349,6 @@ DQN_FORCE_INLINE uint64_t Dqn_Atomic_SetValue64(uint64_t volatile *target, uint6
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Atomically set the value into the target using an atomic compare and swap.
|
||||
/// @param[in,out] target The target pointer to set atomically
|
||||
/// @param[in] value The value to set atomically into the target
|
||||
/// @return The value that was last stored in the target
|
||||
DQN_FORCE_INLINE long Dqn_Atomic_SetValue32(long volatile *target, long value)
|
||||
{
|
||||
#if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG)
|
||||
@ -392,7 +371,6 @@ struct Dqn_CPUIDRegisters
|
||||
Dqn_uint array[4]; ///< Values from 'CPUID' instruction for each register (EAX, EBX, ECX, EDX)
|
||||
};
|
||||
|
||||
/// Execute 'CPUID' instruction to query the capabilities of the current CPU.
|
||||
Dqn_CPUIDRegisters Dqn_CPUID(int function_id);
|
||||
#endif // DQN_OS_ARM64
|
||||
|
||||
@ -446,12 +424,3 @@ void Dqn_TicketMutex_End (Dqn_TicketMutex *mutex);
|
||||
Dqn_uint Dqn_TicketMutex_MakeTicket (Dqn_TicketMutex *mutex);
|
||||
void Dqn_TicketMutex_BeginTicket(Dqn_TicketMutex const *mutex, Dqn_uint ticket);
|
||||
bool Dqn_TicketMutex_CanLock (Dqn_TicketMutex const *mutex, Dqn_uint ticket);
|
||||
|
||||
// NOTE: [$CALL] Dqn_CallSite ======================================================================
|
||||
struct Dqn_CallSite
|
||||
{
|
||||
Dqn_String8 file;
|
||||
Dqn_String8 function;
|
||||
unsigned int line;
|
||||
};
|
||||
#define DQN_CALL_SITE Dqn_CallSite{DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__}
|
@ -1,12 +1,3 @@
|
||||
// NOTE: Table Of Contents =========================================================================
|
||||
// Index | Disable #define | Description
|
||||
// =================================================================================================
|
||||
// [$VARR] Dqn_VArray | DQN_NO_VARRAY | Array backed by virtual memory arena
|
||||
// [$FARR] Dqn_FArray | DQN_NO_FARRAY | Fixed-size arrays
|
||||
// [$DMAP] Dqn_DSMap | DQN_NO_DSMAP | Hashtable, 70% max load, PoT size, linear probe, chain repair
|
||||
// [$LIST] Dqn_List | DQN_NO_LIST | Chunked linked lists, append only
|
||||
// =================================================================================================
|
||||
|
||||
#if !defined(DQN_NO_VARRAY)
|
||||
// NOTE: [$VARR] Dqn_VArray ========================================================================
|
||||
// An array that is backed by virtual memory by reserving addressing space and
|
||||
@ -72,10 +63,10 @@
|
||||
|
||||
template <typename T> struct Dqn_VArray
|
||||
{
|
||||
Dqn_ArenaBlock *block; // Block of memory from the allocator for this array
|
||||
T *data; // Pointer to the start of the array items in the block of memory
|
||||
Dqn_usize size; // Number of items currently in the array
|
||||
Dqn_usize max; // Maximum number of items this array can store
|
||||
Dqn_MemBlock *block; // Block of memory from the allocator for this array
|
||||
T *data; // Pointer to the start of the array items in the block of memory
|
||||
Dqn_usize size; // Number of items currently in the array
|
||||
Dqn_usize max; // Maximum number of items this array can store
|
||||
|
||||
T *begin() { return data; }
|
||||
T *end () { return data + size; }
|
||||
@ -389,30 +380,25 @@ DQN_API template <typename T> Dqn_List<T> Dqn_List_InitWithArena(Dqn_Arena *aren
|
||||
DQN_API template <typename T> T * Dqn_List_At (Dqn_List<T> *list, Dqn_usize index, Dqn_ListChunk<T> *at_chunk);
|
||||
DQN_API template <typename T> bool Dqn_List_Iterate (Dqn_List<T> *list, Dqn_ListIterator<T> *it, Dqn_usize start_index);
|
||||
|
||||
// NOTE: Macros ====================================================================================
|
||||
#define Dqn_List_Make(list, count) Dqn_List_Make_(DQN_LEAK_TRACE list, count)
|
||||
#define Dqn_List_Add(list, count) Dqn_List_Add_(DQN_LEAK_TRACE list, count)
|
||||
|
||||
// NOTE: Internal ==================================================================================
|
||||
DQN_API template <typename T> T * Dqn_List_Make_ (DQN_LEAK_TRACE_FUNCTION Dqn_List<T> *list, Dqn_usize count);
|
||||
DQN_API template <typename T> T * Dqn_List_Add_ (DQN_LEAK_TRACE_FUNCTION Dqn_List<T> *list, Dqn_usize count);
|
||||
DQN_API template <typename T> T * Dqn_List_Make (Dqn_List<T> *list, Dqn_usize count);
|
||||
DQN_API template <typename T> T * Dqn_List_Add (Dqn_List<T> *list, Dqn_usize count);
|
||||
#endif // !defined(DQN_NO_LIST)
|
||||
|
||||
#if !defined(DQN_NO_VARRAY)
|
||||
// NOTE: [$VARR] Dqn_VArray ========================================================================
|
||||
DQN_API template <typename T> Dqn_VArray<T> Dqn_VArray_InitByteSize(Dqn_Arena *arena, Dqn_usize byte_size)
|
||||
{
|
||||
Dqn_usize byte_size_64k_aligned = Dqn_PowerOfTwoAlign(byte_size, DQN_VMEM_RESERVE_GRANULARITY);
|
||||
Dqn_VArray<T> result = {};
|
||||
result.block = Dqn_Arena_Grow(arena, byte_size_64k_aligned, 0 /*commit*/, Dqn_ArenaBlockFlags_Private);
|
||||
result.max = result.block->size / sizeof(T);
|
||||
result.data = DQN_CAST(T *)Dqn_PowerOfTwoAlign((uintptr_t)result.block->memory, alignof(T));
|
||||
Dqn_VArray<T> result = {};
|
||||
result.block = Dqn_Arena_Grow(arena, byte_size, 0 /*commit*/, Dqn_MemBlockFlag_ArenaPrivate);
|
||||
result.data = DQN_CAST(T *)Dqn_MemBlock_Alloc(result.block, /*size*/ 0, alignof(T), Dqn_ZeroMem_No);
|
||||
result.max = (result.block->size - result.block->used) / sizeof(T);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API template <typename T> Dqn_VArray<T> Dqn_VArray_Init(Dqn_Arena *arena, Dqn_usize max)
|
||||
{
|
||||
Dqn_VArray<T> result = Dqn_VArray_InitByteSize<T>(arena, max * sizeof(T));
|
||||
DQN_ASSERT(result.max >= max);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -431,7 +417,7 @@ DQN_API template <typename T> T *Dqn_VArray_Make(Dqn_VArray<T> *array, Dqn_usize
|
||||
return nullptr;
|
||||
|
||||
// TODO: Use placement new? Why doesn't this work?
|
||||
T *result = Dqn_Arena_NewArrayWithBlock(array->block, T, count, zero_mem);
|
||||
T *result = Dqn_MemBlock_NewArray(array->block, T, count, zero_mem);
|
||||
if (result)
|
||||
array->size += count;
|
||||
return result;
|
||||
@ -960,17 +946,17 @@ template <typename T> DQN_API Dqn_List<T> Dqn_List_InitWithArena(Dqn_Arena *aren
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T> DQN_API T *Dqn_List_Make_(DQN_LEAK_TRACE_FUNCTION Dqn_List<T> *list, Dqn_usize count)
|
||||
template <typename T> DQN_API T *Dqn_List_Make(Dqn_List<T> *list, 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_ListChunk<T> * )Dqn_Arena_Allocate_(list->arena, sizeof(Dqn_ListChunk<T>), alignof(Dqn_ListChunk<T>), Dqn_ZeroMem_Yes DQN_LEAK_TRACE_ARG);
|
||||
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_CAST(T * )Dqn_Arena_Allocate_(list->arena, sizeof(T) * items, alignof(T), Dqn_ZeroMem_Yes DQN_LEAK_TRACE_ARG);
|
||||
tail->data = Dqn_Arena_NewArray(list->arena, T, items, Dqn_ZeroMem_Yes);
|
||||
tail->size = items;
|
||||
|
||||
if (!tail->data)
|
||||
@ -993,10 +979,10 @@ template <typename T> DQN_API T *Dqn_List_Make_(DQN_LEAK_TRACE_FUNCTION Dqn_List
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T> DQN_API T *Dqn_List_Add_(DQN_LEAK_TRACE_FUNCTION Dqn_List<T> *list, T const &value)
|
||||
template <typename T> DQN_API T *Dqn_List_Add(Dqn_List<T> *list, T const &value)
|
||||
{
|
||||
T *result = Dqn_List_Make_(list, 1);
|
||||
*result = value;
|
||||
T *result = Dqn_List_Make(list, 1);
|
||||
*result = value;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
320
dqn_debug.cpp
Normal file
320
dqn_debug.cpp
Normal file
@ -0,0 +1,320 @@
|
||||
DQN_API Dqn_String8 Dqn_Debug_CleanStackTrace(Dqn_String8 stack_trace)
|
||||
{
|
||||
// NOTE: Remove the stacktrace's library invocations from the stack trace
|
||||
// which just adds noise to the actual trace we want to look at, e.g:
|
||||
//
|
||||
// dqn\b_stacktrace.h(198): b_stacktrace_get
|
||||
// dqn\b_stacktrace.h(156): b_stacktrace_get_string
|
||||
|
||||
Dqn_String8BinarySplitResult split_line0 = Dqn_String8_BinarySplit(stack_trace, DQN_STRING8("\n"));
|
||||
Dqn_String8BinarySplitResult split_line1 = Dqn_String8_BinarySplit(split_line0.rhs, DQN_STRING8("\n"));
|
||||
|
||||
Dqn_String8 result = split_line1.rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
DQN_API void Dqn_Debug_TrackAlloc_(Dqn_String8 stack_trace, void *ptr, Dqn_usize size, bool leak_permitted)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
if (g_dqn_library->alloc_tracking_disabled)
|
||||
return;
|
||||
|
||||
// NOTE: In this function we can create alloc records and hence it's
|
||||
// possible for the alloc table to resize. This can cause a nested call
|
||||
// into the tracking alloc function and dead-lock. We don't
|
||||
// care about tracking these alloc records for the alloc table itself so we
|
||||
// disable alloc tracking for the duration of this function.
|
||||
// TODO(doyle): @robust This is not thread safe.
|
||||
Dqn_TicketMutex_Begin(&g_dqn_library->alloc_table_mutex);
|
||||
g_dqn_library->alloc_tracking_disabled = true;
|
||||
|
||||
DQN_DEFER {
|
||||
g_dqn_library->alloc_tracking_disabled = false;
|
||||
Dqn_TicketMutex_End(&g_dqn_library->alloc_table_mutex);
|
||||
};
|
||||
|
||||
// NOTE: If the entry was not added, we are reusing a pointer that has been freed.
|
||||
// TODO: Add API for always making the item but exposing a var to indicate if the item was newly created or it already existed.
|
||||
Dqn_DSMap<Dqn_AllocRecord> *alloc_table = &g_dqn_library->alloc_table;
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyU64(alloc_table, DQN_CAST(uintptr_t)ptr);
|
||||
Dqn_AllocRecord *alloc = Dqn_DSMap_Find(alloc_table, key);
|
||||
if (alloc) {
|
||||
if ((alloc->flags & Dqn_AllocRecordFlag_Freed) == 0) {
|
||||
Dqn_String8 alloc_stack_trace = Dqn_String8_Init(alloc->stack_trace, alloc->stack_trace_size);
|
||||
Dqn_String8 alloc_clean_stack_trace = Dqn_String8_Slice(alloc_stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, alloc_stack_trace.size);
|
||||
Dqn_String8 clean_stack_trace = Dqn_String8_Slice(stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, stack_trace.size);
|
||||
DQN_HARD_ASSERTF(
|
||||
alloc->flags & Dqn_AllocRecordFlag_Freed,
|
||||
"\n\nThis pointer is already in the leak tracker, however it has not "
|
||||
"been freed yet. This same pointer is being ask to be tracked "
|
||||
"twice in the allocation table, e.g. one if its previous free "
|
||||
"calls has not being marked freed with an equivalent call to "
|
||||
"Dqn_Debug_TrackDealloc()\n"
|
||||
"\n"
|
||||
"The pointer (0x%p) originally allocated %_$$zu at:\n"
|
||||
"\n"
|
||||
"%.*s"
|
||||
"\n"
|
||||
"The pointer is being allocated again at:\n"
|
||||
"%.*s"
|
||||
,
|
||||
ptr, alloc->size,
|
||||
DQN_STRING_FMT(alloc_clean_stack_trace),
|
||||
DQN_STRING_FMT(clean_stack_trace));
|
||||
}
|
||||
|
||||
// NOTE: Pointer was reused, clean up the prior entry
|
||||
free(alloc->stack_trace);
|
||||
free(alloc->freed_stack_trace);
|
||||
*alloc = {};
|
||||
} else {
|
||||
alloc = Dqn_DSMap_Make(alloc_table, key, /*found*/ nullptr);
|
||||
}
|
||||
|
||||
alloc->ptr = ptr;
|
||||
alloc->size = size;
|
||||
alloc->stack_trace = stack_trace.data;
|
||||
alloc->stack_trace_size = DQN_CAST(uint16_t)stack_trace.size;
|
||||
|
||||
// TODO(doyle): @robust The global flag is not multi-thread safe
|
||||
if (leak_permitted || g_dqn_library->alloc_is_allowed_to_leak)
|
||||
alloc->flags |= Dqn_AllocRecordFlag_LeakPermitted;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Debug_TrackDealloc_(Dqn_String8 stack_trace, void *ptr)
|
||||
{
|
||||
if (!ptr || g_dqn_library->alloc_tracking_disabled)
|
||||
return;
|
||||
|
||||
Dqn_TicketMutex_Begin(&g_dqn_library->alloc_table_mutex);
|
||||
DQN_DEFER { Dqn_TicketMutex_End(&g_dqn_library->alloc_table_mutex); };
|
||||
|
||||
Dqn_DSMap<Dqn_AllocRecord> *alloc_table = &g_dqn_library->alloc_table;
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyU64(alloc_table, DQN_CAST(uintptr_t)ptr);
|
||||
Dqn_AllocRecord *alloc = Dqn_DSMap_Find(alloc_table, key);
|
||||
|
||||
DQN_HARD_ASSERTF(alloc, "Allocated pointer can not be removed as it does not exist in the "
|
||||
"allocation table. When this memory was allocated, the pointer was "
|
||||
"not added to the allocation table [ptr=%p]",
|
||||
ptr);
|
||||
|
||||
if (alloc->flags & Dqn_AllocRecordFlag_Freed) {
|
||||
Dqn_String8 alloc_stack_trace = Dqn_String8_Init(alloc->stack_trace, alloc->stack_trace_size);
|
||||
Dqn_String8 alloc_clean_stack_trace = Dqn_String8_Slice(alloc_stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, alloc_stack_trace.size);
|
||||
|
||||
Dqn_String8 alloc_freed_stack_trace = Dqn_String8_Init(alloc->freed_stack_trace, alloc->freed_stack_trace_size);
|
||||
Dqn_String8 alloc_freed_clean_stack_trace = Dqn_String8_Slice(alloc_freed_stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, alloc_freed_stack_trace.size);
|
||||
|
||||
Dqn_String8 dealloc_stack_trace = Dqn_String8_Slice(stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, stack_trace.size);
|
||||
|
||||
DQN_HARD_ASSERTF((alloc->flags & Dqn_AllocRecordFlag_Freed) == 0,
|
||||
"\n\nDouble free detected, pointer to free was already marked "
|
||||
" as freed. Either the pointer was reallocated but not"
|
||||
" traced, or, the pointer was freed twice.\n"
|
||||
"\n"
|
||||
"The pointer (0x%p) originally allocated %_$$zu at:\n"
|
||||
"\n"
|
||||
"%.*s"
|
||||
"\n"
|
||||
"The pointer was freed at:\n"
|
||||
"\n"
|
||||
"%.*s"
|
||||
"\n"
|
||||
"The pointer is being freed again at:\n"
|
||||
"%.*s"
|
||||
,
|
||||
ptr, alloc->freed_size,
|
||||
DQN_STRING_FMT(alloc_clean_stack_trace),
|
||||
DQN_STRING_FMT(alloc_freed_clean_stack_trace),
|
||||
DQN_STRING_FMT(dealloc_stack_trace));
|
||||
}
|
||||
|
||||
alloc->flags |= Dqn_AllocRecordFlag_Freed;
|
||||
alloc->freed_size = alloc->size;
|
||||
alloc->freed_stack_trace = stack_trace.data;
|
||||
alloc->freed_stack_trace_size = DQN_CAST(uint16_t)stack_trace.size;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Debug_DumpLeaks()
|
||||
{
|
||||
uint64_t leak_count = 0;
|
||||
uint64_t leaked_bytes = 0;
|
||||
for (Dqn_usize index = 1; index < g_dqn_library->alloc_table.occupied; index++) {
|
||||
Dqn_DSMapSlot<Dqn_AllocRecord> *slot = g_dqn_library->alloc_table.slots + index;
|
||||
Dqn_AllocRecord *alloc = &slot->value;
|
||||
bool alloc_leaked = (alloc->flags & Dqn_AllocRecordFlag_Freed) == 0;
|
||||
bool leak_permitted = (alloc->flags & Dqn_AllocRecordFlag_LeakPermitted);
|
||||
if (alloc_leaked && !leak_permitted) {
|
||||
leaked_bytes += alloc->size;
|
||||
leak_count++;
|
||||
|
||||
Dqn_String8 stack_trace = Dqn_String8_Init(alloc->stack_trace, alloc->stack_trace_size);
|
||||
Dqn_String8 clean_stack_trace = Dqn_String8_Slice(stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, stack_trace.size);
|
||||
Dqn_Log_WarningF("Pointer (0x%p) leaked %_$$zu at:\n"
|
||||
"%.*s",
|
||||
alloc->ptr, alloc->size,
|
||||
DQN_STRING_FMT(clean_stack_trace));
|
||||
}
|
||||
}
|
||||
|
||||
if (leak_count) {
|
||||
Dqn_Log_WarningF("There were %I64u leaked allocations totalling %_$$I64u", leak_count, leaked_bytes);
|
||||
}
|
||||
}
|
||||
#endif /// defined(DQN_LEAK_TRACING)
|
||||
|
||||
// NOTE: [$LLOG] Dqn_Log ==========================================================================
|
||||
DQN_API Dqn_String8 Dqn_Log_MakeString(Dqn_Allocator allocator,
|
||||
bool colour,
|
||||
Dqn_String8 type,
|
||||
int log_type,
|
||||
Dqn_CallSite call_site,
|
||||
char const *fmt,
|
||||
va_list args)
|
||||
{
|
||||
Dqn_usize header_size_no_ansi_codes = 0;
|
||||
Dqn_String8 header = {};
|
||||
{
|
||||
DQN_LOCAL_PERSIST Dqn_usize max_type_length = 0;
|
||||
max_type_length = DQN_MAX(max_type_length, type.size);
|
||||
int type_padding = DQN_CAST(int)(max_type_length - type.size);
|
||||
|
||||
Dqn_String8 colour_esc = {};
|
||||
Dqn_String8 bold_esc = {};
|
||||
Dqn_String8 reset_esc = {};
|
||||
if (colour) {
|
||||
bold_esc = Dqn_Print_ESCBoldString;
|
||||
reset_esc = Dqn_Print_ESCResetString;
|
||||
switch (log_type) {
|
||||
case Dqn_LogType_Debug: break;
|
||||
case Dqn_LogType_Info: colour_esc = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Info); break;
|
||||
case Dqn_LogType_Warning: colour_esc = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Warning); break;
|
||||
case Dqn_LogType_Error: colour_esc = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Error); break;
|
||||
}
|
||||
}
|
||||
|
||||
Dqn_String8 file_name = Dqn_String8_FileNameFromPath(call_site.file);
|
||||
Dqn_DateHMSTimeString const time = Dqn_Date_HMSLocalTimeStringNow();
|
||||
header = Dqn_String8_InitF(allocator,
|
||||
"%.*s " // date
|
||||
"%.*s " // hms
|
||||
"%.*s" // colour
|
||||
"%.*s" // bold
|
||||
"%.*s" // type
|
||||
"%*s" // type padding
|
||||
"%.*s" // reset
|
||||
" %.*s" // file name
|
||||
":%05u ", // line number
|
||||
time.date_size - 2, time.date + 2,
|
||||
time.hms_size, time.hms,
|
||||
colour_esc.size, colour_esc.data,
|
||||
bold_esc.size, bold_esc.data,
|
||||
type.size, type.data,
|
||||
type_padding, "",
|
||||
reset_esc.size, reset_esc.data,
|
||||
file_name.size, file_name.data,
|
||||
call_site.line);
|
||||
header_size_no_ansi_codes = header.size - colour_esc.size - Dqn_Print_ESCResetString.size;
|
||||
}
|
||||
|
||||
// NOTE: Header padding
|
||||
// =========================================================================
|
||||
Dqn_usize header_padding = 0;
|
||||
{
|
||||
DQN_LOCAL_PERSIST Dqn_usize max_header_length = 0;
|
||||
max_header_length = DQN_MAX(max_header_length, header_size_no_ansi_codes);
|
||||
header_padding = max_header_length - header_size_no_ansi_codes;
|
||||
}
|
||||
|
||||
// NOTE: Construct final log
|
||||
// =========================================================================
|
||||
Dqn_String8 user_msg = Dqn_String8_InitFV(allocator, fmt, args);
|
||||
Dqn_String8 result = Dqn_String8_Allocate(allocator, header.size + header_padding + user_msg.size, Dqn_ZeroMem_No);
|
||||
DQN_MEMCPY(result.data, header.data, header.size);
|
||||
DQN_MEMSET(result.data + header.size, ' ', header_padding);
|
||||
DQN_MEMCPY(result.data + header.size + header_padding, user_msg.data, user_msg.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_String8 type, int log_type, void *user_data, Dqn_CallSite call_site, char const *fmt, va_list args)
|
||||
{
|
||||
(void)log_type;
|
||||
(void)user_data;
|
||||
|
||||
// NOTE: Open log file for appending if requested
|
||||
// =========================================================================
|
||||
Dqn_TicketMutex_Begin(&g_dqn_library->log_file_mutex);
|
||||
if (g_dqn_library->log_to_file && !g_dqn_library->log_file) {
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
#if (defined(DQN_OS_WIN32) && !defined(DQN_NO_WIN)) || !defined(DQN_OS_WIN32)
|
||||
Dqn_String8 exe_dir = Dqn_OS_EXEDir(scratch.allocator);
|
||||
#else
|
||||
Dqn_String8 exe_dir = DQN_STRING8(".");
|
||||
#endif
|
||||
Dqn_String8 log_file = Dqn_String8_InitF(scratch.allocator, "%.*s/dqn.log", DQN_STRING_FMT(exe_dir));
|
||||
fopen_s(DQN_CAST(FILE **)&g_dqn_library->log_file, log_file.data, "a");
|
||||
}
|
||||
Dqn_TicketMutex_End(&g_dqn_library->log_file_mutex);
|
||||
|
||||
// NOTE: Generate the log header
|
||||
// =========================================================================
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_String8 log_line = Dqn_Log_MakeString(scratch.allocator,
|
||||
!g_dqn_library->log_no_colour,
|
||||
type,
|
||||
log_type,
|
||||
call_site,
|
||||
fmt,
|
||||
args);
|
||||
|
||||
// NOTE: Print log
|
||||
// =========================================================================
|
||||
Dqn_Print_StdLn(Dqn_PrintStd_Out, log_line);
|
||||
|
||||
Dqn_TicketMutex_Begin(&g_dqn_library->log_file_mutex);
|
||||
if (g_dqn_library->log_to_file) {
|
||||
fprintf(DQN_CAST(FILE *)g_dqn_library->log_file, "%.*s\n", DQN_STRING_FMT(log_line));
|
||||
}
|
||||
Dqn_TicketMutex_End(&g_dqn_library->log_file_mutex);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Log_FVCallSite(Dqn_String8 type, Dqn_CallSite call_site, char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_LogProc *logging_function = g_dqn_library->log_callback ? g_dqn_library->log_callback : Dqn_Log_FVDefault_;
|
||||
logging_function(type, -1 /*log_type*/, g_dqn_library->log_user_data, call_site, fmt, args);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Log_FCallSite(Dqn_String8 type, Dqn_CallSite call_site, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Log_FVCallSite(type, call_site, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Log_TypeFVCallSite(Dqn_LogType type, Dqn_CallSite call_site, char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_String8 type_string = DQN_STRING8("DQN-BAD-LOG-TYPE");
|
||||
switch (type) {
|
||||
case Dqn_LogType_Error: type_string = DQN_STRING8("ERROR"); break;
|
||||
case Dqn_LogType_Info: type_string = DQN_STRING8("INFO"); break;
|
||||
case Dqn_LogType_Warning: type_string = DQN_STRING8("WARN"); break;
|
||||
case Dqn_LogType_Debug: type_string = DQN_STRING8("DEBUG"); break;
|
||||
case Dqn_LogType_Count: type_string = DQN_STRING8("BADXX"); break;
|
||||
}
|
||||
|
||||
Dqn_LogProc *logging_function = g_dqn_library->log_callback ? g_dqn_library->log_callback : Dqn_Log_FVDefault_;
|
||||
logging_function(type_string, type /*log_type*/, g_dqn_library->log_user_data, call_site, fmt, args);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Log_TypeFCallSite(Dqn_LogType type, Dqn_CallSite call_site, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Log_TypeFVCallSite(type, call_site, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
127
dqn_debug.h
Normal file
127
dqn_debug.h
Normal file
@ -0,0 +1,127 @@
|
||||
// NOTE: Debug Macros ==============================================================================
|
||||
#if !defined(DQN_DEBUG_BREAK)
|
||||
#if defined(NDEBUG)
|
||||
#define DQN_DEBUG_BREAK
|
||||
#else
|
||||
#if defined(DQN_COMPILER_W32_MSVC) || defined(DQN_COMPILER_W32_CLANG)
|
||||
#define DQN_DEBUG_BREAK __debugbreak()
|
||||
#elif defined(DQN_COMPILER_CLANG) || defined(DQN_COMPILER_GCC)
|
||||
#include <signal.h>
|
||||
#define DQN_DEBUG_BREAK raise(SIGTRAP)
|
||||
#elif
|
||||
#error "Unhandled compiler"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(DQN_MEMSET_BYTE)
|
||||
#define DQN_MEMSET_BYTE 0
|
||||
#endif
|
||||
|
||||
#if !defined(DQN_DUMP_STACK_TRACE)
|
||||
#define DQN_DUMP_STACK_TRACE \
|
||||
do { \
|
||||
Dqn_String8 stack_trace_ = Dqn_String8_InitCString8(b_stacktrace_get_string()); \
|
||||
Dqn_String8 clean_stack_trace_ = Dqn_Debug_CleanStackTrace(stack_trace_); \
|
||||
Dqn_Print_StdF(Dqn_PrintStd_Err, "%.*s", DQN_STRING_FMT(clean_stack_trace_)); \
|
||||
free(stack_trace_.data); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
// NOTE: [$CALL] Dqn_CallSite ======================================================================
|
||||
struct Dqn_CallSite
|
||||
{
|
||||
Dqn_String8 file;
|
||||
Dqn_String8 function;
|
||||
unsigned int line;
|
||||
};
|
||||
#define DQN_CALL_SITE Dqn_CallSite{DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__}
|
||||
|
||||
// NOTE: [$DEBG] Dqn_Debug =========================================================================
|
||||
enum Dqn_AllocRecordFlag
|
||||
{
|
||||
Dqn_AllocRecordFlag_Freed = 1 << 0,
|
||||
Dqn_AllocRecordFlag_LeakPermitted = 1 << 1,
|
||||
};
|
||||
|
||||
struct Dqn_AllocRecord
|
||||
{
|
||||
void *ptr; // Pointer to the allocation being tracked
|
||||
Dqn_usize size; // Size of the allocation
|
||||
Dqn_usize freed_size; // Store the size of the allocation when it is freed
|
||||
char *stack_trace; // Stack trace at the point of allocation
|
||||
char *freed_stack_trace; // Stack trace of where the allocation was freed
|
||||
uint16_t stack_trace_size; // Size of the `stack_trace`
|
||||
uint16_t freed_stack_trace_size; // Size of `freed_stack_trace`
|
||||
uint16_t flags; // Bit flags from `Dqn_AllocRecordFlag`
|
||||
char padding[2];
|
||||
};
|
||||
static_assert(sizeof(Dqn_AllocRecord) == 48,
|
||||
"We aim to keep the allocation record as light as possible as "
|
||||
"memory tracking can get expensive. Enforce that there is no "
|
||||
"unexpected padding.");
|
||||
|
||||
DQN_API Dqn_String8 Dqn_Debug_CleanStackTrace(Dqn_String8 stack_trace);
|
||||
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
#define Dqn_Debug_TrackAlloc(ptr, size, leak_permitted) Dqn_Debug_TrackAlloc_ (Dqn_String8_InitCString8(b_stacktrace_get_string()), ptr, size, leak_permitted)
|
||||
#define Dqn_Debug_TrackDealloc(ptr) Dqn_Debug_TrackDealloc_(Dqn_String8_InitCString8(b_stacktrace_get_string()), ptr)
|
||||
|
||||
DQN_API void Dqn_Debug_TrackAlloc_(Dqn_String8 stack_trace, void *ptr, Dqn_usize size, bool leak_permitted);
|
||||
DQN_API void Dqn_Debug_TrackDealloc_(Dqn_String8 stack_trace, void *ptr);
|
||||
DQN_API void Dqn_Debug_DumpLeaks();
|
||||
#else
|
||||
#define Dqn_Debug_TrackAlloc(...)
|
||||
#define Dqn_Debug_TrackDealloc(...)
|
||||
#define Dqn_Debug_DumpLeaks(...)
|
||||
#endif
|
||||
|
||||
// NOTE: [$LLOG] Dqn_Log ==========================================================================
|
||||
// NOTE: API
|
||||
// @proc Dqn_LogProc
|
||||
// @desc The logging procedure of the library. Users can override the default
|
||||
// logging function by setting the logging function pointer in Dqn_Library.
|
||||
// This function will be invoked every time a log is recorded using the
|
||||
// following functions.
|
||||
//
|
||||
// @param[in] log_type This value is one of the Dqn_LogType values if the log
|
||||
// was generated from one of the default categories. -1 if the log is not from
|
||||
// one of the default categories.
|
||||
|
||||
enum Dqn_LogType
|
||||
{
|
||||
Dqn_LogType_Debug,
|
||||
Dqn_LogType_Info,
|
||||
Dqn_LogType_Warning,
|
||||
Dqn_LogType_Error,
|
||||
Dqn_LogType_Count,
|
||||
};
|
||||
|
||||
/// RGBA
|
||||
#define Dqn_LogTypeColourU32_Info 0x00'87'ff'ff // Blue
|
||||
#define Dqn_LogTypeColourU32_Warning 0xff'ff'00'ff // Yellow
|
||||
#define Dqn_LogTypeColourU32_Error 0xff'00'00'ff // Red
|
||||
|
||||
typedef void Dqn_LogProc(Dqn_String8 type, int log_type, void *user_data, Dqn_CallSite call_site, char const *fmt, va_list va);
|
||||
|
||||
#define Dqn_Log_DebugF(fmt, ...) Dqn_Log_TypeFCallSite(Dqn_LogType_Debug, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
#define Dqn_Log_InfoF(fmt, ...) Dqn_Log_TypeFCallSite(Dqn_LogType_Info, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
#define Dqn_Log_WarningF(fmt, ...) Dqn_Log_TypeFCallSite(Dqn_LogType_Warning, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
#define Dqn_Log_ErrorF(fmt, ...) Dqn_Log_TypeFCallSite(Dqn_LogType_Error, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
|
||||
#define Dqn_Log_DebugFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Debug, DQN_CALL_SITE, fmt, args)
|
||||
#define Dqn_Log_InfoFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Info, DQN_CALL_SITE, fmt, args)
|
||||
#define Dqn_Log_WarningFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Warning, DQN_CALL_SITE, fmt, args)
|
||||
#define Dqn_Log_ErrorFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Error, DQN_CALL_SITE, fmt, args)
|
||||
|
||||
#define Dqn_Log_TypeFV(type, fmt, args) Dqn_Log_TypeFVCallSite(type, DQN_CALL_SITE, fmt, args)
|
||||
#define Dqn_Log_TypeF(type, fmt, ...) Dqn_Log_TypeFCallSite(type, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
|
||||
#define Dqn_Log_FV(type, fmt, args) Dqn_Log_FVCallSite(type, DQN_CALL_SITE, fmt, args)
|
||||
#define Dqn_Log_F(type, fmt, ...) Dqn_Log_FCallSite(type, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
|
||||
DQN_API Dqn_String8 Dqn_Log_MakeString (Dqn_Allocator allocator, bool colour, Dqn_String8 type, int log_type, Dqn_CallSite call_site, char const *fmt, va_list args);
|
||||
DQN_API void Dqn_Log_TypeFVCallSite(Dqn_LogType type, Dqn_CallSite call_site, char const *fmt, va_list va);
|
||||
DQN_API void Dqn_Log_TypeFCallSite (Dqn_LogType type, Dqn_CallSite call_site, char const *fmt, ...);
|
||||
DQN_API void Dqn_Log_FVCallSite (Dqn_String8 type, Dqn_CallSite call_site, char const *fmt, va_list va);
|
||||
DQN_API void Dqn_Log_FCallSite (Dqn_String8 type, Dqn_CallSite call_site, char const *fmt, ...);
|
1697
dqn_external.cpp
Normal file
1697
dqn_external.cpp
Normal file
File diff suppressed because it is too large
Load Diff
596
dqn_external.h
Normal file
596
dqn_external.h
Normal file
@ -0,0 +1,596 @@
|
||||
// NOTE: [$BSTK] b_stacktrace ======================================================================
|
||||
#if defined(DQN_OS_WIN32)
|
||||
#define DQN_NO_WIN32_MIN_HEADER
|
||||
#define WIN32_MEAN_AND_LEAN
|
||||
#include <Windows.h>
|
||||
#include <shellscalingapi.h>
|
||||
#endif
|
||||
#define B_STACKTRACE_API static
|
||||
#include "b_stacktrace.h"
|
||||
|
||||
// NOTE: [$OS_H] OS Headers ========================================================================
|
||||
#if defined(DQN_OS_WIN32)
|
||||
#pragma comment(lib, "bcrypt")
|
||||
#pragma comment(lib, "wininet")
|
||||
|
||||
#if defined(DQN_NO_WIN32_MIN_HEADER)
|
||||
#include <bcrypt.h> // Dqn_OS_SecureRNGBytes -> BCryptOpenAlgorithmProvider ... etc
|
||||
#include <shellapi.h> // Dqn_Win_MakeProcessDPIAware -> SetProcessDpiAwareProc
|
||||
#if !defined(DQN_NO_WINNET)
|
||||
#include <wininet.h> // Dqn_Win_Net -> InternetConnect ... etc
|
||||
#endif // DQN_NO_WINNET
|
||||
#elif !defined(_INC_WINDOWS)
|
||||
#if defined(DQN_COMPILER_W32_MSVC)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4201) // warning C4201: nonstandard extension used: nameless struct/union
|
||||
#endif
|
||||
#define MAX_PATH 260
|
||||
|
||||
// NOTE: Wait/Synchronization
|
||||
#define INFINITE 0xFFFFFFFF // Infinite timeout
|
||||
|
||||
// NOTE: FormatMessageA
|
||||
#define FORMAT_MESSAGE_FROM_SYSTEM 0x00001000
|
||||
#define FORMAT_MESSAGE_IGNORE_INSERTS 0x00000200
|
||||
#define FORMAT_MESSAGE_FROM_HMODULE 0x00000800
|
||||
#define MAKELANGID(p, s) ((((unsigned short )(s)) << 10) | (unsigned short )(p))
|
||||
#define SUBLANG_DEFAULT 0x01 // user default
|
||||
#define LANG_NEUTRAL 0x00
|
||||
|
||||
// NOTE: MultiByteToWideChar
|
||||
#define CP_UTF8 65001 // UTF-8 translation
|
||||
|
||||
// NOTE: VirtualAlloc
|
||||
// NOTE: Allocation Type
|
||||
#define MEM_RESERVE 0x00002000
|
||||
#define MEM_COMMIT 0x00001000
|
||||
#define MEM_DECOMMIT 0x00004000
|
||||
#define MEM_RELEASE 0x00008000
|
||||
|
||||
// NOTE: Protect
|
||||
#define PAGE_NOACCESS 0x01
|
||||
#define PAGE_READONLY 0x02
|
||||
#define PAGE_READWRITE 0x04
|
||||
#define PAGE_GUARD 0x100
|
||||
|
||||
// NOTE: FindFirstFile
|
||||
#define INVALID_HANDLE_VALUE ((void *)(long *)-1)
|
||||
#define INVALID_FILE_ATTRIBUTES ((unsigned long)-1)
|
||||
#define FILE_ATTRIBUTE_NORMAL 0x00000080
|
||||
#define FIND_FIRST_EX_LARGE_FETCH 0x00000002
|
||||
#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
|
||||
#define FILE_ATTRIBUTE_READONLY 0x00000001
|
||||
#define FILE_ATTRIBUTE_HIDDEN 0x00000002
|
||||
|
||||
// NOTE: GetModuleFileNameW
|
||||
#define ERROR_INSUFFICIENT_BUFFER 122L
|
||||
|
||||
// NOTE: MoveFile
|
||||
#define MOVEFILE_REPLACE_EXISTING 0x00000001
|
||||
#define MOVEFILE_COPY_ALLOWED 0x00000002
|
||||
|
||||
// NOTE: Wininet
|
||||
typedef unsigned short INTERNET_PORT;
|
||||
#define INTERNET_OPEN_TYPE_PRECONFIG 0 // use registry configuration
|
||||
#define INTERNET_DEFAULT_HTTPS_PORT 443 // HTTPS
|
||||
#define INTERNET_SERVICE_HTTP 3
|
||||
#define INTERNET_OPTION_USER_AGENT 41
|
||||
#define INTERNET_FLAG_NO_AUTH 0x00040000 // no automatic authentication handling
|
||||
#define INTERNET_FLAG_SECURE 0x00800000 // use PCT/SSL if applicable (HTTP)
|
||||
|
||||
// NOTE: CreateFile
|
||||
#define GENERIC_READ (0x80000000L)
|
||||
#define GENERIC_WRITE (0x40000000L)
|
||||
#define GENERIC_EXECUTE (0x20000000L)
|
||||
#define GENERIC_ALL (0x10000000L)
|
||||
#define FILE_ATTRIBUTE_NORMAL 0x00000080
|
||||
#define FILE_APPEND_DATA 4
|
||||
|
||||
#define CREATE_NEW 1
|
||||
#define CREATE_ALWAYS 2
|
||||
#define OPEN_EXISTING 3
|
||||
#define OPEN_ALWAYS 4
|
||||
#define TRUNCATE_EXISTING 5
|
||||
|
||||
#define STD_INPUT_HANDLE ((unsigned long)-10)
|
||||
#define STD_OUTPUT_HANDLE ((unsigned long)-11)
|
||||
#define STD_ERROR_HANDLE ((unsigned long)-12)
|
||||
|
||||
#define INVALID_FILE_SIZE ((unsigned long)0xFFFFFFFF)
|
||||
|
||||
#define HTTP_QUERY_RAW_HEADERS 21
|
||||
#define HTTP_QUERY_RAW_HEADERS_CRLF 22
|
||||
|
||||
// NOTE: HttpAddRequestHeadersA
|
||||
#define HTTP_ADDREQ_FLAG_ADD_IF_NEW 0x10000000
|
||||
#define HTTP_ADDREQ_FLAG_ADD 0x20000000
|
||||
#define HTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA 0x40000000
|
||||
#define HTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
|
||||
#define HTTP_ADDREQ_FLAG_COALESCE HTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA
|
||||
#define HTTP_ADDREQ_FLAG_REPLACE 0x80000000
|
||||
|
||||
#define SW_MAXIMIZED 3
|
||||
#define SW_SHOW 5
|
||||
|
||||
typedef enum PROCESS_DPI_AWARENESS {
|
||||
PROCESS_DPI_UNAWARE = 0,
|
||||
PROCESS_SYSTEM_DPI_AWARE = 1,
|
||||
PROCESS_PER_MONITOR_DPI_AWARE = 2
|
||||
} PROCESS_DPI_AWARENESS;
|
||||
|
||||
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((void *)-4)
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
unsigned long LowPart;
|
||||
unsigned long HighPart;
|
||||
} DUMMYSTRUCTNAME;
|
||||
struct {
|
||||
unsigned long LowPart;
|
||||
unsigned long HighPart;
|
||||
} u;
|
||||
uint64_t QuadPart;
|
||||
} ULARGE_INTEGER;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned long dwLowDateTime;
|
||||
unsigned long dwHighDateTime;
|
||||
} FILETIME;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned long dwFileAttributes;
|
||||
FILETIME ftCreationTime;
|
||||
FILETIME ftLastAccessTime;
|
||||
FILETIME ftLastWriteTime;
|
||||
unsigned long nFileSizeHigh;
|
||||
unsigned long nFileSizeLow;
|
||||
} WIN32_FILE_ATTRIBUTE_DATA;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GetFileExInfoStandard,
|
||||
GetFileExMaxInfoLevel
|
||||
} GET_FILEEX_INFO_LEVELS;
|
||||
|
||||
typedef struct {
|
||||
unsigned long nLength;
|
||||
void *lpSecurityDescriptor;
|
||||
bool bInheritHandle;
|
||||
} SECURITY_ATTRIBUTES;
|
||||
|
||||
typedef struct {
|
||||
long left;
|
||||
long top;
|
||||
long right;
|
||||
long bottom;
|
||||
} RECT, *PRECT, *NPRECT, *LPRECT;
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
unsigned long dwOemId; // Obsolete field...do not use
|
||||
struct {
|
||||
uint16_t wProcessorArchitecture;
|
||||
uint16_t wReserved;
|
||||
} DUMMYSTRUCTNAME;
|
||||
} DUMMYUNIONNAME;
|
||||
unsigned long dwPageSize;
|
||||
void *lpMinimumApplicationAddress;
|
||||
void *lpMaximumApplicationAddress;
|
||||
unsigned long *dwActiveProcessorMask;
|
||||
unsigned long dwNumberOfProcessors;
|
||||
unsigned long dwProcessorType;
|
||||
unsigned long dwAllocationGranularity;
|
||||
uint16_t wProcessorLevel;
|
||||
uint16_t wProcessorRevision;
|
||||
} SYSTEM_INFO;
|
||||
|
||||
typedef struct {
|
||||
unsigned short wYear;
|
||||
unsigned short wMonth;
|
||||
unsigned short wDayOfWeek;
|
||||
unsigned short wDay;
|
||||
unsigned short wHour;
|
||||
unsigned short wMinute;
|
||||
unsigned short wSecond;
|
||||
unsigned short wMilliseconds;
|
||||
} SYSTEMTIME;
|
||||
|
||||
typedef struct {
|
||||
unsigned long dwFileAttributes;
|
||||
FILETIME ftCreationTime;
|
||||
FILETIME ftLastAccessTime;
|
||||
FILETIME ftLastWriteTime;
|
||||
unsigned long nFileSizeHigh;
|
||||
unsigned long nFileSizeLow;
|
||||
unsigned long dwReserved0;
|
||||
unsigned long dwReserved1;
|
||||
wchar_t cFileName[MAX_PATH];
|
||||
wchar_t cAlternateFileName[14];
|
||||
#ifdef _MAC
|
||||
unsigned long dwFileType;
|
||||
unsigned long dwCreatorType;
|
||||
unsigned short wFinderFlags;
|
||||
#endif
|
||||
} WIN32_FIND_DATAW;
|
||||
|
||||
typedef enum {
|
||||
FindExInfoStandard,
|
||||
FindExInfoBasic,
|
||||
FindExInfoMaxInfoLevel,
|
||||
} FINDEX_INFO_LEVELS;
|
||||
|
||||
typedef enum {
|
||||
FindExSearchNameMatch,
|
||||
FindExSearchLimitToDirectories,
|
||||
FindExSearchLimitToDevices,
|
||||
FindExSearchMaxSearchOp
|
||||
} FINDEX_SEARCH_OPS;
|
||||
|
||||
typedef enum {
|
||||
INTERNET_SCHEME_PARTIAL = -2,
|
||||
INTERNET_SCHEME_UNKNOWN = -1,
|
||||
INTERNET_SCHEME_DEFAULT = 0,
|
||||
INTERNET_SCHEME_FTP,
|
||||
INTERNET_SCHEME_GOPHER,
|
||||
INTERNET_SCHEME_HTTP,
|
||||
INTERNET_SCHEME_HTTPS,
|
||||
INTERNET_SCHEME_FILE,
|
||||
INTERNET_SCHEME_NEWS,
|
||||
INTERNET_SCHEME_MAILTO,
|
||||
INTERNET_SCHEME_SOCKS,
|
||||
INTERNET_SCHEME_JAVASCRIPT,
|
||||
INTERNET_SCHEME_VBSCRIPT,
|
||||
INTERNET_SCHEME_RES,
|
||||
INTERNET_SCHEME_FIRST = INTERNET_SCHEME_FTP,
|
||||
INTERNET_SCHEME_LAST = INTERNET_SCHEME_RES
|
||||
} INTERNET_SCHEME;
|
||||
|
||||
typedef struct {
|
||||
unsigned long dwStructSize; // size of this structure. Used in version check
|
||||
char *lpszScheme; // pointer to scheme name
|
||||
unsigned long dwSchemeLength; // length of scheme name
|
||||
INTERNET_SCHEME nScheme; // enumerated scheme type (if known)
|
||||
char *lpszHostName; // pointer to host name
|
||||
unsigned long dwHostNameLength; // length of host name
|
||||
INTERNET_PORT nPort; // converted port number
|
||||
char *lpszUserName; // pointer to user name
|
||||
unsigned long dwUserNameLength; // length of user name
|
||||
char *lpszPassword; // pointer to password
|
||||
unsigned long dwPasswordLength; // length of password
|
||||
char *lpszUrlPath; // pointer to URL-path
|
||||
unsigned long dwUrlPathLength; // length of URL-path
|
||||
char *lpszExtraInfo; // pointer to extra information (e.g. ?foo or #foo)
|
||||
unsigned long dwExtraInfoLength; // length of extra information
|
||||
} URL_COMPONENTSA;
|
||||
|
||||
typedef void * HMODULE;
|
||||
typedef union {
|
||||
struct {
|
||||
unsigned long LowPart;
|
||||
long HighPart;
|
||||
};
|
||||
struct {
|
||||
unsigned long LowPart;
|
||||
long HighPart;
|
||||
} u;
|
||||
uint64_t QuadPart;
|
||||
} LARGE_INTEGER;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
/*BOOL*/ int __stdcall CreateDirectoryW (wchar_t const *lpPathName, SECURITY_ATTRIBUTES *lpSecurityAttributes);
|
||||
/*BOOL*/ int __stdcall RemoveDirectoryW (wchar_t const *lpPathName);
|
||||
/*DWORD*/ unsigned long __stdcall GetCurrentDirectoryW (unsigned long nBufferLength, wchar_t *lpBuffer);
|
||||
|
||||
/*BOOL*/ int __stdcall FindNextFileW (void *hFindFile, WIN32_FIND_DATAW *lpFindFileData);
|
||||
/*HANDLE*/ void * __stdcall FindFirstFileExW (wchar_t const *lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, void *lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, void *lpSearchFilter, unsigned long dwAdditionalFlags);
|
||||
/*DWORD*/ unsigned long __stdcall GetFileAttributesExW (wchar_t const *lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, WIN32_FILE_ATTRIBUTE_DATA *lpFileInformation);
|
||||
/*BOOL*/ int __stdcall GetFileSizeEx (void *hFile, LARGE_INTEGER *lpFileSize);
|
||||
|
||||
/*BOOL*/ int __stdcall MoveFileExW (wchar_t const *lpExistingFileName, wchar_t const *lpNewFileName, unsigned long dwFlags);
|
||||
/*BOOL*/ int __stdcall CopyFileW (wchar_t const *lpExistingFileName, wchar_t const *lpNewFileName, int bFailIfExists);
|
||||
/*BOOL*/ int __stdcall DeleteFileW (wchar_t const *lpExistingFileName);
|
||||
/*HANDLE*/ void * __stdcall CreateFileW (wchar_t const *lpFileName, unsigned long dwDesiredAccess, unsigned long dwShareMode, SECURITY_ATTRIBUTES *lpSecurityAttributes, unsigned long dwCreationDisposition, unsigned long dwFlagsAndAttributes, void *hTemplateFile);
|
||||
/*BOOL*/ int __stdcall ReadFile (void *hFile, void *lpBuffer, unsigned long nNumberOfBytesToRead, unsigned long *lpNumberOfBytesRead, struct OVERLAPPED *lpOverlapped);
|
||||
/*BOOL*/ int __stdcall WriteFile (void *hFile, void const *lpBuffer, unsigned long nNumberOfBytesToWrite, unsigned long *lpNumberOfBytesWritten, struct OVERLAPPED *lpOverlapped);
|
||||
/*BOOL*/ int __stdcall CloseHandle (void *hObject);
|
||||
|
||||
/*BOOL*/ int __stdcall WriteConsoleA (void *hConsoleOutput, const char *lpBuffer, unsigned long nNumberOfCharsToWrite, unsigned long *lpNumberOfCharsWritten, void *lpReserved);
|
||||
/*BOOL*/ int __stdcall AllocConsole ();
|
||||
/*BOOL*/ int __stdcall FreeConsole ();
|
||||
/*BOOL*/ int __stdcall AttachConsole (unsigned long dwProcessId);
|
||||
/*HANDLE*/ void * __stdcall GetStdHandle (unsigned long nStdHandle);
|
||||
/*BOOL*/ int __stdcall GetConsoleMode (void *hConsoleHandle, unsigned long *lpMode);
|
||||
|
||||
/*HMODULE*/ void * __stdcall LoadLibraryA (char const *lpFileName);
|
||||
/*BOOL*/ int __stdcall FreeLibrary (void *hModule);
|
||||
/*FARPROC*/ void * __stdcall GetProcAddress (void *hModule, char const *lpProcName);
|
||||
|
||||
/*BOOL*/ int __stdcall GetWindowRect (void *hWnd, RECT *lpRect);
|
||||
/*BOOL*/ int __stdcall SetWindowPos (void *hWnd, void *hWndInsertAfter, int X, int Y, int cx, int cy, unsigned int uFlags);
|
||||
|
||||
/*DWORD*/ unsigned long __stdcall GetWindowModuleFileNameA (void *hwnd, char *pszFileName, unsigned int cchFileNameMax);
|
||||
/*HMODULE*/ void * __stdcall GetModuleHandleA (char const *lpModuleName);
|
||||
/*DWORD*/ unsigned long __stdcall GetModuleFileNameW (void *hModule, wchar_t *lpFilename, unsigned long nSize);
|
||||
|
||||
/*DWORD*/ unsigned long __stdcall WaitForSingleObject (void *hHandle, unsigned long dwMilliseconds);
|
||||
|
||||
/*BOOL*/ int __stdcall QueryPerformanceCounter (LARGE_INTEGER *lpPerformanceCount);
|
||||
/*BOOL*/ int __stdcall QueryPerformanceFrequency (LARGE_INTEGER *lpFrequency);
|
||||
|
||||
/*HANDLE*/ void * __stdcall CreateThread (SECURITY_ATTRIBUTES *lpThreadAttributes, size_t dwStackSize, unsigned long (*lpStartAddress)(void *), void *lpParameter, unsigned long dwCreationFlags, unsigned long *lpThreadId);
|
||||
/*HANDLE*/ void * __stdcall CreateSemaphoreA (SECURITY_ATTRIBUTES *lpSecurityAttributes, long lInitialCount, long lMaxCount, char *lpName);
|
||||
/*BOOL*/ int __stdcall ReleaseSemaphore (void *semaphore, long lReleaseCount, long *lpPreviousCount);
|
||||
void __stdcall Sleep (unsigned long dwMilliseconds);
|
||||
/*DWORD*/ unsigned long __stdcall GetCurrentThreadId ();
|
||||
|
||||
void * __stdcall VirtualAlloc (void *lpAddress, size_t dwSize, unsigned long flAllocationType, unsigned long flProtect);
|
||||
/*BOOL*/ int __stdcall VirtualProtect (void *lpAddress, size_t dwSize, unsigned long flNewProtect, unsigned long *lpflOldProtect);
|
||||
/*BOOL*/ int __stdcall VirtualFree (void *lpAddress, size_t dwSize, unsigned long dwFreeType);
|
||||
|
||||
void __stdcall GetSystemInfo (SYSTEM_INFO *system_info);
|
||||
void __stdcall GetSystemTime (SYSTEMTIME *lpSystemTime);
|
||||
void __stdcall GetSystemTimeAsFileTime (FILETIME *lpFileTime);
|
||||
void __stdcall GetLocalTime (SYSTEMTIME *lpSystemTime);
|
||||
|
||||
/*DWORD*/ unsigned long __stdcall FormatMessageA (unsigned long dwFlags, void *lpSource, unsigned long dwMessageId, unsigned long dwLanguageId, char *lpBuffer, unsigned long nSize, va_list *Arguments);
|
||||
/*DWORD*/ unsigned long __stdcall GetLastError ();
|
||||
|
||||
int __stdcall MultiByteToWideChar (unsigned int CodePage, unsigned long dwFlags, char const *lpMultiByteStr, int cbMultiByte, wchar_t *lpWideCharStr, int cchWideChar);
|
||||
int __stdcall WideCharToMultiByte (unsigned int CodePage, unsigned long dwFlags, wchar_t const *lpWideCharStr, int cchWideChar, char *lpMultiByteStr, int cbMultiByte, char const *lpDefaultChar, bool *lpUsedDefaultChar);
|
||||
|
||||
/*NTSTATUS*/ long __stdcall BCryptOpenAlgorithmProvider(void *phAlgorithm, wchar_t const *pszAlgId, wchar_t const *pszImplementation, unsigned long dwFlags);
|
||||
/*NTSTATUS*/ long __stdcall BCryptGenRandom (void *hAlgorithm, unsigned char *pbBuffer, unsigned long cbBuffer, unsigned long dwFlags);
|
||||
|
||||
/*BOOLAPI*/ int __stdcall InternetCrackUrlA (char const *lpszUrl, unsigned long dwUrlLength, unsigned long dwFlags, URL_COMPONENTSA *lpUrlComponents);
|
||||
/*HANDLE*/ void * __stdcall InternetOpenA (char const *lpszAgent, unsigned long dwAccessType, char const *lpszProxy, char const *lpszProxyBypass, unsigned long dwFlags);
|
||||
/*HANDLE*/ void * __stdcall InternetConnectA (void *hInternet, char const *lpszServerName, INTERNET_PORT nServerPort, char const *lpszUserName, char const *lpszPassword, unsigned long dwService, unsigned long dwFlags, unsigned long *dwContext);
|
||||
/*BOOLAPI*/ int __stdcall InternetSetOptionA (void *hInternet, unsigned long dwOption, void *lpBuffer, unsigned long dwBufferLength);
|
||||
/*BOOLAPI*/ int __stdcall InternetReadFile (void *hFile, void *lpBuffer, unsigned long dwNumberOfBytesToRead, unsigned long *lpdwNumberOfBytesRead);
|
||||
/*BOOLAPI*/ int __stdcall InternetCloseHandle (void *hInternet);
|
||||
/*HANDLE*/ void * __stdcall HttpOpenRequestA (void *hConnect, char const *lpszVerb, char const *lpszObjectName, char const *lpszVersion, char const *lpszReferrer, char const **lplpszAcceptTypes, unsigned long dwFlags, unsigned long *dwContext);
|
||||
/*BOOLAPI*/ int __stdcall HttpSendRequestA (void *hRequest, char const *lpszHeaders, unsigned long dwHeadersLength, void *lpOptional, unsigned long dwOptionalLength);
|
||||
/*BOOLAPI*/ int __stdcall HttpAddRequestHeadersA (void *hRequest, char const *lpszHeaders, unsigned long dwHeadersLength, unsigned long dwModifiers);
|
||||
/*BOOL*/ int __stdcall HttpQueryInfoA (void *hRequest, unsigned long dwInfoLevel, void *lpBuffer, unsigned long *lpdwBufferLength, unsigned long *lpdwIndex);
|
||||
|
||||
/*HINSTANCE*/ void * __stdcall ShellExecuteA (void *hwnd, char const *lpOperation, char const *lpFile, char const *lpParameters, char const *lpDirectory, int nShowCmd);
|
||||
/*BOOL*/ int __stdcall ShowWindow (void *hWnd, int nCmdShow);
|
||||
}
|
||||
#if defined(DQN_COMPILER_W32_MSVC)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
#endif // !defined(_INC_WINDOWS)
|
||||
#elif defined(DQN_OS_UNIX)
|
||||
#include <errno.h> // errno
|
||||
#include <fcntl.h> // O_RDONLY ... etc
|
||||
#include <linux/fs.h> // FICLONE
|
||||
#include <sys/ioctl.h> // ioctl
|
||||
#include <sys/types.h> // pid_t
|
||||
#include <sys/random.h> // getrandom
|
||||
#include <sys/stat.h> // stat
|
||||
#include <sys/sendfile.h> // sendfile
|
||||
#include <sys/mman.h> // mmap
|
||||
#include <time.h> // clock_gettime, nanosleep
|
||||
#include <unistd.h> // access, gettid
|
||||
#endif
|
||||
|
||||
// NOTE: [$STBS] stb_sprintf =======================================================================
|
||||
// stb_sprintf - v1.10 - public domain snprintf() implementation
|
||||
// originally by Jeff Roberts / RAD Game Tools, 2015/10/20
|
||||
// http://github.com/nothings/stb
|
||||
//
|
||||
// allowed types: sc uidBboXx p AaGgEef n
|
||||
// lengths : hh h ll j z t I64 I32 I
|
||||
//
|
||||
// Contributors:
|
||||
// Fabian "ryg" Giesen (reformatting)
|
||||
// github:aganm (attribute format)
|
||||
//
|
||||
// Contributors (bugfixes):
|
||||
// github:d26435
|
||||
// github:trex78
|
||||
// github:account-login
|
||||
// Jari Komppa (SI suffixes)
|
||||
// Rohit Nirmal
|
||||
// Marcin Wojdyr
|
||||
// Leonard Ritter
|
||||
// Stefano Zanotti
|
||||
// Adam Allison
|
||||
// Arvid Gerstmann
|
||||
// Markus Kolb
|
||||
//
|
||||
// LICENSE:
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
#ifndef STB_SPRINTF_H_INCLUDE
|
||||
#define STB_SPRINTF_H_INCLUDE
|
||||
|
||||
/*
|
||||
Single file sprintf replacement.
|
||||
|
||||
Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20.
|
||||
Hereby placed in public domain.
|
||||
|
||||
This is a full sprintf replacement that supports everything that
|
||||
the C runtime sprintfs support, including float/double, 64-bit integers,
|
||||
hex floats, field parameters (%*.*d stuff), length reads backs, etc.
|
||||
|
||||
Why would you need this if sprintf already exists? Well, first off,
|
||||
it's *much* faster (see below). It's also much smaller than the CRT
|
||||
versions code-space-wise. We've also added some simple improvements
|
||||
that are super handy (commas in thousands, callbacks at buffer full,
|
||||
for example). Finally, the format strings for MSVC and GCC differ
|
||||
for 64-bit integers (among other small things), so this lets you use
|
||||
the same format strings in cross platform code.
|
||||
|
||||
It uses the standard single file trick of being both the header file
|
||||
and the source itself. If you just include it normally, you just get
|
||||
the header file function definitions. To get the code, you include
|
||||
it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first.
|
||||
|
||||
It only uses va_args macros from the C runtime to do it's work. It
|
||||
does cast doubles to S64s and shifts and divides U64s, which does
|
||||
drag in CRT code on most platforms.
|
||||
|
||||
It compiles to roughly 8K with float support, and 4K without.
|
||||
As a comparison, when using MSVC static libs, calling sprintf drags
|
||||
in 16K.
|
||||
|
||||
API:
|
||||
====
|
||||
int stbsp_sprintf( char * buf, char const * fmt, ... )
|
||||
int stbsp_snprintf( char * buf, int count, char const * fmt, ... )
|
||||
Convert an arg list into a buffer. stbsp_snprintf always returns
|
||||
a zero-terminated string (unlike regular snprintf).
|
||||
|
||||
int stbsp_vsprintf( char * buf, char const * fmt, va_list va )
|
||||
int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va )
|
||||
Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns
|
||||
a zero-terminated string (unlike regular snprintf).
|
||||
|
||||
int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )
|
||||
typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len );
|
||||
Convert into a buffer, calling back every STB_SPRINTF_MIN chars.
|
||||
Your callback can then copy the chars out, print them or whatever.
|
||||
This function is actually the workhorse for everything else.
|
||||
The buffer you pass in must hold at least STB_SPRINTF_MIN characters.
|
||||
// you return the next buffer to use or 0 to stop converting
|
||||
|
||||
void stbsp_set_separators( char comma, char period )
|
||||
Set the comma and period characters to use.
|
||||
|
||||
FLOATS/DOUBLES:
|
||||
===============
|
||||
This code uses a internal float->ascii conversion method that uses
|
||||
doubles with error correction (double-doubles, for ~105 bits of
|
||||
precision). This conversion is round-trip perfect - that is, an atof
|
||||
of the values output here will give you the bit-exact double back.
|
||||
|
||||
One difference is that our insignificant digits will be different than
|
||||
with MSVC or GCC (but they don't match each other either). We also
|
||||
don't attempt to find the minimum length matching float (pre-MSVC15
|
||||
doesn't either).
|
||||
|
||||
If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT
|
||||
and you'll save 4K of code space.
|
||||
|
||||
64-BIT INTS:
|
||||
============
|
||||
This library also supports 64-bit integers and you can use MSVC style or
|
||||
GCC style indicators (%I64d or %lld). It supports the C99 specifiers
|
||||
for size_t and ptr_diff_t (%jd %zd) as well.
|
||||
|
||||
EXTRAS:
|
||||
=======
|
||||
Like some GCCs, for integers and floats, you can use a ' (single quote)
|
||||
specifier and commas will be inserted on the thousands: "%'d" on 12345
|
||||
would print 12,345.
|
||||
|
||||
For integers and floats, you can use a "$" specifier and the number
|
||||
will be converted to float and then divided to get kilo, mega, giga or
|
||||
tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is
|
||||
"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn
|
||||
2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three
|
||||
$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the
|
||||
suffix, add "_" specifier: "%_$d" -> "2.53M".
|
||||
|
||||
In addition to octal and hexadecimal conversions, you can print
|
||||
integers in binary: "%b" for 256 would print 100.
|
||||
|
||||
PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC):
|
||||
===================================================================
|
||||
"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC)
|
||||
"%24d" across all 32-bit ints (4.5x/4.2x faster)
|
||||
"%x" across all 32-bit ints (4.5x/3.8x faster)
|
||||
"%08x" across all 32-bit ints (4.3x/3.8x faster)
|
||||
"%f" across e-10 to e+10 floats (7.3x/6.0x faster)
|
||||
"%e" across e-10 to e+10 floats (8.1x/6.0x faster)
|
||||
"%g" across e-10 to e+10 floats (10.0x/7.1x faster)
|
||||
"%f" for values near e-300 (7.9x/6.5x faster)
|
||||
"%f" for values near e+300 (10.0x/9.1x faster)
|
||||
"%e" for values near e-300 (10.1x/7.0x faster)
|
||||
"%e" for values near e+300 (9.2x/6.0x faster)
|
||||
"%.320f" for values near e-300 (12.6x/11.2x faster)
|
||||
"%a" for random values (8.6x/4.3x faster)
|
||||
"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster)
|
||||
"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster)
|
||||
"%s%s%s" for 64 char strings (7.1x/7.3x faster)
|
||||
"...512 char string..." ( 35.0x/32.5x faster!)
|
||||
*/
|
||||
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && defined(__has_attribute)
|
||||
#if __has_feature(address_sanitizer)
|
||||
#if __has_attribute(__no_sanitize__)
|
||||
#define STBSP__ASAN __attribute__((__no_sanitize__("address")))
|
||||
#elif __has_attribute(__no_sanitize_address__)
|
||||
#define STBSP__ASAN __attribute__((__no_sanitize_address__))
|
||||
#elif __has_attribute(__no_address_safety_analysis__)
|
||||
#define STBSP__ASAN __attribute__((__no_address_safety_analysis__))
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
|
||||
#if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__
|
||||
#define STBSP__ASAN __attribute__((__no_sanitize_address__))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef STBSP__ASAN
|
||||
#define STBSP__ASAN
|
||||
#endif
|
||||
|
||||
#ifdef STB_SPRINTF_STATIC
|
||||
#define STBSP__PUBLICDEC static
|
||||
#define STBSP__PUBLICDEF static STBSP__ASAN
|
||||
#else
|
||||
#ifdef __cplusplus
|
||||
#define STBSP__PUBLICDEC extern "C"
|
||||
#define STBSP__PUBLICDEF extern "C" STBSP__ASAN
|
||||
#else
|
||||
#define STBSP__PUBLICDEC extern
|
||||
#define STBSP__PUBLICDEF STBSP__ASAN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__has_attribute)
|
||||
#if __has_attribute(format)
|
||||
#define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va)))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef STBSP__ATTRIBUTE_FORMAT
|
||||
#define STBSP__ATTRIBUTE_FORMAT(fmt,va)
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define STBSP__NOTUSED(v) (void)(v)
|
||||
#else
|
||||
#define STBSP__NOTUSED(v) (void)sizeof(v)
|
||||
#endif
|
||||
|
||||
#include <stdarg.h> // for va_arg(), va_list()
|
||||
#include <stddef.h> // size_t, ptrdiff_t
|
||||
|
||||
#ifndef STB_SPRINTF_MIN
|
||||
#define STB_SPRINTF_MIN 512 // how many characters per callback
|
||||
#endif
|
||||
typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len);
|
||||
|
||||
#ifndef STB_SPRINTF_DECORATE
|
||||
#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names
|
||||
#endif
|
||||
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va);
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va);
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3);
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4);
|
||||
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va);
|
||||
STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period);
|
||||
#endif // STB_SPRINTF_H_INCLUDE
|
||||
|
||||
|
@ -1,10 +1,3 @@
|
||||
// NOTE: Table Of Contents =========================================================================
|
||||
// Index | Disable #define | Description
|
||||
// =================================================================================================
|
||||
// [$FNV1] Dqn_FNV1A | | Hash(x) -> 32/64bit via FNV1a
|
||||
// [$MMUR] Dqn_MurmurHash3 | | Hash(x) -> 32/128bit via MurmurHash3
|
||||
// =================================================================================================
|
||||
|
||||
// NOTE: [$FNV1] Dqn_FNV1A =========================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
#if 0
|
||||
|
989
dqn_helpers.cpp
Normal file
989
dqn_helpers.cpp
Normal file
@ -0,0 +1,989 @@
|
||||
#if !defined(DQN_NO_JSON_BUILDER)
|
||||
// NOTE: [$JSON] Dqn_JSONBuilder ===================================================================
|
||||
DQN_API Dqn_JSONBuilder Dqn_JSONBuilder_Init(Dqn_Allocator allocator, int spaces_per_indent)
|
||||
{
|
||||
Dqn_JSONBuilder result = {};
|
||||
result.spaces_per_indent = spaces_per_indent;
|
||||
result.string_builder.allocator = allocator;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_String8 Dqn_JSONBuilder_Build(Dqn_JSONBuilder const *builder, Dqn_Allocator allocator)
|
||||
{
|
||||
Dqn_String8 result = Dqn_String8Builder_Build(&builder->string_builder, allocator);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_JSONBuilder_KeyValue(Dqn_JSONBuilder *builder, Dqn_String8 key, Dqn_String8 value)
|
||||
{
|
||||
if (key.size == 0 && value.size == 0)
|
||||
return;
|
||||
|
||||
Dqn_JSONBuilderItem item = Dqn_JSONBuilderItem_KeyValue;
|
||||
if (value.size == 1) {
|
||||
if (value.data[0] == '{' || value.data[0] == '[') {
|
||||
item = Dqn_JSONBuilderItem_OpenContainer;
|
||||
} else if (value.data[0] == '}' || value.data[0] == ']') {
|
||||
item = Dqn_JSONBuilderItem_CloseContainer;
|
||||
}
|
||||
}
|
||||
|
||||
bool adding_to_container_with_items = item != Dqn_JSONBuilderItem_CloseContainer &&
|
||||
(builder->last_item == Dqn_JSONBuilderItem_KeyValue ||
|
||||
builder->last_item == Dqn_JSONBuilderItem_CloseContainer);
|
||||
|
||||
uint8_t prefix_size = 0;
|
||||
char prefix[2] = {0};
|
||||
if (adding_to_container_with_items)
|
||||
prefix[prefix_size++] = ',';
|
||||
|
||||
if (builder->last_item != Dqn_JSONBuilderItem_Empty)
|
||||
prefix[prefix_size++] = '\n';
|
||||
|
||||
if (item == Dqn_JSONBuilderItem_CloseContainer)
|
||||
builder->indent_level--;
|
||||
|
||||
int spaces_per_indent = builder->spaces_per_indent ? builder->spaces_per_indent : 2;
|
||||
int spaces = builder->indent_level * spaces_per_indent;
|
||||
|
||||
if (key.size) {
|
||||
Dqn_String8Builder_AppendF(&builder->string_builder,
|
||||
"%.*s%*c\"%.*s\": %.*s",
|
||||
prefix_size, prefix,
|
||||
spaces, ' ',
|
||||
DQN_STRING_FMT(key),
|
||||
DQN_STRING_FMT(value));
|
||||
} else {
|
||||
Dqn_String8Builder_AppendF(&builder->string_builder,
|
||||
"%.*s%*c%.*s",
|
||||
prefix_size, prefix,
|
||||
spaces, ' ',
|
||||
DQN_STRING_FMT(value));
|
||||
}
|
||||
|
||||
if (item == Dqn_JSONBuilderItem_OpenContainer)
|
||||
builder->indent_level++;
|
||||
|
||||
builder->last_item = item;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_JSONBuilder_KeyValueFV(Dqn_JSONBuilder *builder, Dqn_String8 key, char const *value_fmt, va_list args)
|
||||
{
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(builder->string_builder.allocator.user_context);
|
||||
Dqn_String8 value = Dqn_String8_InitFV(scratch.allocator, value_fmt, args);
|
||||
Dqn_JSONBuilder_KeyValue(builder, key, value);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_JSONBuilder_KeyValueF(Dqn_JSONBuilder *builder, Dqn_String8 key, char const *value_fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, value_fmt);
|
||||
Dqn_JSONBuilder_KeyValueFV(builder, key, value_fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_JSONBuilder_ObjectBeginNamed(Dqn_JSONBuilder *builder, Dqn_String8 name)
|
||||
{
|
||||
Dqn_JSONBuilder_KeyValue(builder, name, DQN_STRING8("{"));
|
||||
}
|
||||
|
||||
DQN_API void Dqn_JSONBuilder_ObjectEnd(Dqn_JSONBuilder *builder)
|
||||
{
|
||||
Dqn_JSONBuilder_KeyValue(builder, DQN_STRING8(""), DQN_STRING8("}"));
|
||||
}
|
||||
|
||||
DQN_API void Dqn_JSONBuilder_ArrayBeginNamed(Dqn_JSONBuilder *builder, Dqn_String8 name)
|
||||
{
|
||||
Dqn_JSONBuilder_KeyValue(builder, name, DQN_STRING8("["));
|
||||
}
|
||||
|
||||
DQN_API void Dqn_JSONBuilder_ArrayEnd(Dqn_JSONBuilder *builder)
|
||||
{
|
||||
Dqn_JSONBuilder_KeyValue(builder, DQN_STRING8(""), DQN_STRING8("]"));
|
||||
}
|
||||
|
||||
DQN_API void Dqn_JSONBuilder_StringNamed(Dqn_JSONBuilder *builder, Dqn_String8 key, Dqn_String8 value)
|
||||
{
|
||||
Dqn_JSONBuilder_KeyValueF(builder, key, "\"%.*s\"", value.size, value.data);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_JSONBuilder_LiteralNamed(Dqn_JSONBuilder *builder, Dqn_String8 key, Dqn_String8 value)
|
||||
{
|
||||
Dqn_JSONBuilder_KeyValueF(builder, key, "%.*s", value.size, value.data);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_JSONBuilder_U64Named(Dqn_JSONBuilder *builder, Dqn_String8 key, uint64_t value)
|
||||
{
|
||||
Dqn_JSONBuilder_KeyValueF(builder, key, "%I64u", value);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_JSONBuilder_I64Named(Dqn_JSONBuilder *builder, Dqn_String8 key, int64_t value)
|
||||
{
|
||||
Dqn_JSONBuilder_KeyValueF(builder, key, "%I64d", value);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_JSONBuilder_F64Named(Dqn_JSONBuilder *builder, Dqn_String8 key, double value, int decimal_places)
|
||||
{
|
||||
if (!builder)
|
||||
return;
|
||||
|
||||
if (decimal_places >= 16)
|
||||
decimal_places = 16;
|
||||
|
||||
// NOTE: Generate the format string for the float, depending on how many
|
||||
// decimals places it wants.
|
||||
char float_fmt[16];
|
||||
if (decimal_places > 0) {
|
||||
// NOTE: Emit the format string "%.<decimal_places>f" i.e. %.1f
|
||||
STB_SPRINTF_DECORATE(snprintf)(float_fmt, sizeof(float_fmt), "%%.%df", decimal_places);
|
||||
} else {
|
||||
// NOTE: Emit the format string "%f"
|
||||
STB_SPRINTF_DECORATE(snprintf)(float_fmt, sizeof(float_fmt), "%%f");
|
||||
}
|
||||
|
||||
char fmt[32];
|
||||
if (key.size)
|
||||
STB_SPRINTF_DECORATE(snprintf)(fmt, sizeof(fmt), "\"%%.*s\": %s", float_fmt);
|
||||
else
|
||||
STB_SPRINTF_DECORATE(snprintf)(fmt, sizeof(fmt), "%s", float_fmt);
|
||||
|
||||
Dqn_JSONBuilder_KeyValueF(builder, key, fmt, value);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_JSONBuilder_BoolNamed(Dqn_JSONBuilder *builder, Dqn_String8 key, bool value)
|
||||
{
|
||||
Dqn_String8 value_string = value ? DQN_STRING8("true") : DQN_STRING8("false");
|
||||
Dqn_JSONBuilder_KeyValueF(builder, key, "%.*s", value_string.size, value_string.data);
|
||||
}
|
||||
#endif // !defined(DQN_NO_JSON_BUILDER)
|
||||
|
||||
#if !defined(DQN_NO_BIN)
|
||||
// NOTE: [$BHEX] Dqn_Bin ===========================================================================
|
||||
DQN_API char const *Dqn_Bin_HexBufferTrim0x(char const *hex, Dqn_usize size, Dqn_usize *real_size)
|
||||
{
|
||||
Dqn_String8 result = Dqn_String8_TrimWhitespaceAround(Dqn_String8_Init(hex, size));
|
||||
result = Dqn_String8_TrimPrefix(result, DQN_STRING8("0x"), Dqn_String8EqCase_Insensitive);
|
||||
if (real_size)
|
||||
*real_size = result.size;
|
||||
return result.data;
|
||||
}
|
||||
|
||||
DQN_API Dqn_String8 Dqn_Bin_HexTrim0x(Dqn_String8 string)
|
||||
{
|
||||
Dqn_usize trimmed_size = 0;
|
||||
char const *trimmed = Dqn_Bin_HexBufferTrim0x(string.data, string.size, &trimmed_size);
|
||||
Dqn_String8 result = Dqn_String8_Init(trimmed, trimmed_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_BinHexU64String Dqn_Bin_U64ToHexU64String(uint64_t number, uint32_t flags)
|
||||
{
|
||||
Dqn_String8 prefix = {};
|
||||
if (!(flags & Dqn_BinHexU64StringFlags_No0xPrefix))
|
||||
prefix = DQN_STRING8("0x");
|
||||
|
||||
Dqn_BinHexU64String result = {};
|
||||
DQN_MEMCPY(result.data, prefix.data, prefix.size);
|
||||
result.size += DQN_CAST(int8_t)prefix.size;
|
||||
|
||||
char const *fmt = (flags & Dqn_BinHexU64StringFlags_UppercaseHex) ? "%I64X" : "%I64x";
|
||||
int size = STB_SPRINTF_DECORATE(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));
|
||||
|
||||
// NOTE: snprintf returns the required size of the format string
|
||||
// irrespective of if there's space or not, but, always null terminates so
|
||||
// the last byte is wasted.
|
||||
result.size = DQN_MIN(result.size, DQN_ARRAY_UCOUNT(result.data) - 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_String8 Dqn_Bin_U64ToHex(Dqn_Allocator allocator, uint64_t number, uint32_t flags)
|
||||
{
|
||||
Dqn_String8 prefix = {};
|
||||
if (!(flags & Dqn_BinHexU64StringFlags_No0xPrefix))
|
||||
prefix = DQN_STRING8("0x");
|
||||
|
||||
char const *fmt = (flags & Dqn_BinHexU64StringFlags_UppercaseHex) ? "%I64X" : "%I64x";
|
||||
Dqn_usize required_size = Dqn_CString8_FSize(fmt, number) + prefix.size;
|
||||
Dqn_String8 result = Dqn_String8_Allocate(allocator, required_size, Dqn_ZeroMem_No);
|
||||
|
||||
if (Dqn_String8_IsValid(result)) {
|
||||
DQN_MEMCPY(result.data, prefix.data, prefix.size);
|
||||
int space = DQN_CAST(int)DQN_MAX((result.size - prefix.size) + 1, 0); /*null-terminator*/
|
||||
STB_SPRINTF_DECORATE(snprintf)(result.data + prefix.size, space, fmt, number);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_Bin_HexBufferToU64(char const *hex, Dqn_usize size)
|
||||
{
|
||||
Dqn_usize trim_size = size;
|
||||
char const *trim_hex = hex;
|
||||
if (trim_size >= 2) {
|
||||
if (trim_hex[0] == '0' && (trim_hex[1] == 'x' || trim_hex[1] == 'X')) {
|
||||
trim_size -= 2;
|
||||
trim_hex += 2;
|
||||
}
|
||||
}
|
||||
|
||||
DQN_ASSERT(DQN_CAST(Dqn_usize)(trim_size * 4 / 8) /*maximum amount of bytes represented in the hex string*/ <= sizeof(uint64_t));
|
||||
|
||||
uint64_t result = 0;
|
||||
Dqn_usize bits_written = 0;
|
||||
Dqn_usize max_size = DQN_MIN(size, 8 /*bytes*/ * 2 /*hex chars per byte*/);
|
||||
for (Dqn_usize hex_index = 0; hex_index < max_size; hex_index++, bits_written += 4) {
|
||||
char ch = trim_hex[hex_index];
|
||||
if (!Dqn_Char_IsHex(ch))
|
||||
break;
|
||||
uint8_t val = Dqn_Char_HexToU8(ch);
|
||||
Dqn_usize bit_shift = 60 - bits_written;
|
||||
result |= (DQN_CAST(uint64_t)val << bit_shift);
|
||||
}
|
||||
|
||||
result >>= (64 - bits_written); // Shift the remainder digits to the end.
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_Bin_HexToU64(Dqn_String8 hex)
|
||||
{
|
||||
uint64_t result = Dqn_Bin_HexBufferToU64(hex.data, hex.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Bin_BytesToHexBuffer(void const *src, Dqn_usize src_size, char *dest, Dqn_usize dest_size)
|
||||
{
|
||||
if (!src || !dest)
|
||||
return false;
|
||||
|
||||
if (!DQN_CHECK(dest_size >= src_size * 2))
|
||||
return false;
|
||||
|
||||
char const *HEX = "0123456789abcdef";
|
||||
unsigned char const *src_u8 = DQN_CAST(unsigned char const *)src;
|
||||
for (Dqn_usize src_index = 0, dest_index = 0; src_index < src_size; src_index++) {
|
||||
char byte = src_u8[src_index];
|
||||
char hex01 = (byte >> 4) & 0b1111;
|
||||
char hex02 = (byte >> 0) & 0b1111;
|
||||
dest[dest_index++] = HEX[(int)hex01];
|
||||
dest[dest_index++] = HEX[(int)hex02];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_API char *Dqn_Bin_BytesToHexBufferArena(Dqn_Arena *arena, void const *src, Dqn_usize size)
|
||||
{
|
||||
char *result = size > 0 ? Dqn_Arena_NewArray(arena, char, (size * 2) + 1 /*null terminate*/, Dqn_ZeroMem_No) : nullptr;
|
||||
if (result) {
|
||||
bool converted = Dqn_Bin_BytesToHexBuffer(src, size, result, size * 2);
|
||||
DQN_ASSERT(converted);
|
||||
result[size * 2] = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_String8 Dqn_Bin_BytesToHexArena(Dqn_Arena *arena, void const *src, Dqn_usize size)
|
||||
{
|
||||
Dqn_String8 result = {};
|
||||
result.data = Dqn_Bin_BytesToHexBufferArena(arena, src, size);
|
||||
if (result.data)
|
||||
result.size = size * 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytes(char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size)
|
||||
{
|
||||
Dqn_usize result = 0;
|
||||
if (!hex || hex_size <= 0)
|
||||
return result;
|
||||
|
||||
Dqn_usize trim_size = 0;
|
||||
char const *trim_hex = Dqn_Bin_HexBufferTrim0x(hex,
|
||||
hex_size,
|
||||
&trim_size);
|
||||
|
||||
// NOTE: Trimmed hex can be "0xf" -> "f" or "0xAB" -> "AB"
|
||||
// Either way, the size can be odd or even, hence we round up to the nearest
|
||||
// multiple of two to ensure that we calculate the min buffer size orrectly.
|
||||
Dqn_usize trim_size_rounded_up = trim_size + (trim_size % 2);
|
||||
Dqn_usize min_buffer_size = trim_size_rounded_up / 2;
|
||||
if (dest_size < min_buffer_size || trim_size <= 0) {
|
||||
DQN_ASSERTF(dest_size >= min_buffer_size, "Insufficient buffer size for converting hex to binary");
|
||||
return result;
|
||||
}
|
||||
|
||||
result = Dqn_Bin_HexBufferToBytesUnchecked(trim_hex,
|
||||
trim_size,
|
||||
dest,
|
||||
dest_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytesUnchecked(char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size)
|
||||
{
|
||||
Dqn_usize result = 0;
|
||||
unsigned char *dest_u8 = DQN_CAST(unsigned char *)dest;
|
||||
|
||||
for (Dqn_usize hex_index = 0;
|
||||
hex_index < hex_size;
|
||||
hex_index += 2, result += 1)
|
||||
{
|
||||
char hex01 = hex[hex_index];
|
||||
char hex02 = (hex_index + 1 < hex_size) ? hex[hex_index + 1] : 0;
|
||||
|
||||
char bit4_01 = (hex01 >= '0' && hex01 <= '9') ? 0 + (hex01 - '0')
|
||||
: (hex01 >= 'a' && hex01 <= 'f') ? 10 + (hex01 - 'a')
|
||||
: (hex01 >= 'A' && hex01 <= 'F') ? 10 + (hex01 - 'A')
|
||||
: 0;
|
||||
|
||||
char bit4_02 = (hex02 >= '0' && hex02 <= '9') ? 0 + (hex02 - '0')
|
||||
: (hex02 >= 'a' && hex02 <= 'f') ? 10 + (hex02 - 'a')
|
||||
: (hex02 >= 'A' && hex02 <= 'F') ? 10 + (hex02 - 'A')
|
||||
: 0;
|
||||
|
||||
char byte = (bit4_01 << 4) | (bit4_02 << 0);
|
||||
dest_u8[result] = byte;
|
||||
}
|
||||
|
||||
DQN_ASSERT(result <= dest_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexToBytesUnchecked(Dqn_String8 hex, void *dest, Dqn_usize dest_size)
|
||||
{
|
||||
Dqn_usize result = Dqn_Bin_HexBufferToBytesUnchecked(hex.data, hex.size, dest, dest_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexToBytes(Dqn_String8 hex, void *dest, Dqn_usize dest_size)
|
||||
{
|
||||
Dqn_usize result = Dqn_Bin_HexBufferToBytes(hex.data, hex.size, dest, dest_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API char *Dqn_Bin_HexBufferToBytesArena(Dqn_Arena *arena, char const *hex, Dqn_usize size, Dqn_usize *real_size)
|
||||
{
|
||||
char *result = nullptr;
|
||||
if (!arena || !hex || size <= 0)
|
||||
return result;
|
||||
|
||||
Dqn_usize trim_size = 0;
|
||||
char const *trim_hex = Dqn_Bin_HexBufferTrim0x(hex,
|
||||
size,
|
||||
&trim_size);
|
||||
|
||||
Dqn_usize binary_size = trim_size / 2;
|
||||
result = Dqn_Arena_NewArray(arena, char, binary_size, Dqn_ZeroMem_No);
|
||||
if (result) {
|
||||
Dqn_usize convert_size = Dqn_Bin_HexBufferToBytesUnchecked(trim_hex, trim_size, result, binary_size);
|
||||
if (real_size)
|
||||
*real_size = convert_size;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_String8 Dqn_Bin_HexToBytesArena(Dqn_Arena *arena, Dqn_String8 hex)
|
||||
{
|
||||
Dqn_String8 result = {};
|
||||
result.data = Dqn_Bin_HexBufferToBytesArena(arena, hex.data, hex.size, &result.size);
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DQN_NO_BIN)
|
||||
|
||||
// NOTE: [$BITS] Dqn_Bit ===========================================================================
|
||||
DQN_API void Dqn_Bit_UnsetInplace(uint64_t *flags, uint64_t bitfield)
|
||||
{
|
||||
*flags = (*flags & ~bitfield);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Bit_SetInplace(uint64_t *flags, uint64_t bitfield)
|
||||
{
|
||||
*flags = (*flags | bitfield);
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Bit_IsSet(uint64_t bits, uint64_t bits_to_set)
|
||||
{
|
||||
auto result = DQN_CAST(bool)((bits & bits_to_set) == bits_to_set);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Bit_IsNotSet(uint64_t bits, uint64_t bits_to_check)
|
||||
{
|
||||
auto result = !Dqn_Bit_IsSet(bits, bits_to_check);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$SAFE] Dqn_Safe ==========================================================================
|
||||
DQN_API int64_t Dqn_Safe_AddI64(int64_t a, int64_t b)
|
||||
{
|
||||
int64_t result = DQN_CHECKF(a <= INT64_MAX - b, "a=%zd, b=%zd", a, b) ? (a + b) : INT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int64_t Dqn_Safe_MulI64(int64_t a, int64_t b)
|
||||
{
|
||||
int64_t result = DQN_CHECKF(a <= INT64_MAX / b, "a=%zd, b=%zd", a, b) ? (a * b) : INT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_AddU64(uint64_t a, uint64_t b)
|
||||
{
|
||||
uint64_t result = DQN_CHECKF(a <= UINT64_MAX - b, "a=%zu, b=%zu", a, b) ? (a + b) : UINT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_SubU64(uint64_t a, uint64_t b)
|
||||
{
|
||||
uint64_t result = DQN_CHECKF(a >= b, "a=%zu, b=%zu", a, b) ? (a - b) : 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_MulU64(uint64_t a, uint64_t b)
|
||||
{
|
||||
uint64_t result = DQN_CHECKF(a <= UINT64_MAX / b, "a=%zu, b=%zu", a, b) ? (a * b) : UINT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint32_t Dqn_Safe_SubU32(uint32_t a, uint32_t b)
|
||||
{
|
||||
uint32_t result = DQN_CHECKF(a >= b, "a=%u, b=%u", a, b) ? (a - b) : 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_Safe_SaturateCastUSizeToI*
|
||||
// -----------------------------------------------------------------------------
|
||||
// INT*_MAX literals will be promoted to the type of uintmax_t as uintmax_t is
|
||||
// the highest possible rank (unsigned > signed).
|
||||
DQN_API int Dqn_Safe_SaturateCastUSizeToInt(Dqn_usize val)
|
||||
{
|
||||
int result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT_MAX) ? DQN_CAST(int)val : INT_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastUSizeToI8(Dqn_usize val)
|
||||
{
|
||||
int8_t result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT8_MAX) ? DQN_CAST(int8_t)val : INT8_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastUSizeToI16(Dqn_usize val)
|
||||
{
|
||||
int16_t result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT16_MAX) ? DQN_CAST(int16_t)val : INT16_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int32_t Dqn_Safe_SaturateCastUSizeToI32(Dqn_usize val)
|
||||
{
|
||||
int32_t result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT32_MAX) ? DQN_CAST(int32_t)val : INT32_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int64_t Dqn_Safe_SaturateCastUSizeToI64(Dqn_usize val)
|
||||
{
|
||||
int64_t result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT64_MAX) ? DQN_CAST(int64_t)val : INT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_Safe_SaturateCastUSizeToU*
|
||||
// -----------------------------------------------------------------------------
|
||||
// Both operands are unsigned and the lowest rank operand will be promoted to
|
||||
// match the highest rank operand.
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastUSizeToU8(Dqn_usize val)
|
||||
{
|
||||
uint8_t result = DQN_CHECK(val <= UINT8_MAX) ? DQN_CAST(uint8_t)val : UINT8_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastUSizeToU16(Dqn_usize val)
|
||||
{
|
||||
uint16_t result = DQN_CHECK(val <= UINT16_MAX) ? DQN_CAST(uint16_t)val : UINT16_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastUSizeToU32(Dqn_usize val)
|
||||
{
|
||||
uint32_t result = DQN_CHECK(val <= UINT32_MAX) ? DQN_CAST(uint32_t)val : UINT32_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_SaturateCastUSizeToU64(Dqn_usize val)
|
||||
{
|
||||
uint64_t result = DQN_CHECK(val <= UINT64_MAX) ? DQN_CAST(uint64_t)val : UINT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_Safe_SaturateCastU64ToU*
|
||||
// -----------------------------------------------------------------------------
|
||||
// Both operands are unsigned and the lowest rank operand will be promoted to
|
||||
// match the highest rank operand.
|
||||
DQN_API unsigned int Dqn_Safe_SaturateCastU64ToUInt(uint64_t val)
|
||||
{
|
||||
unsigned int result = DQN_CHECK(val <= UINT8_MAX) ? DQN_CAST(unsigned int)val : UINT_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastU64ToU8(uint64_t val)
|
||||
{
|
||||
uint8_t result = DQN_CHECK(val <= UINT8_MAX) ? DQN_CAST(uint8_t)val : UINT8_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastU64ToU16(uint64_t val)
|
||||
{
|
||||
uint16_t result = DQN_CHECK(val <= UINT16_MAX) ? DQN_CAST(uint16_t)val : UINT16_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastU64ToU32(uint64_t val)
|
||||
{
|
||||
uint32_t result = DQN_CHECK(val <= UINT32_MAX) ? DQN_CAST(uint32_t)val : UINT32_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: Dqn_Safe_SaturateCastISizeToI*
|
||||
// -----------------------------------------------------------------------------
|
||||
// Both operands are signed so the lowest rank operand will be promoted to
|
||||
// match the highest rank operand.
|
||||
DQN_API int Dqn_Safe_SaturateCastISizeToInt(Dqn_isize val)
|
||||
{
|
||||
DQN_ASSERT(val >= INT_MIN && val <= INT_MAX);
|
||||
int result = DQN_CAST(int)DQN_CLAMP(val, INT_MIN, INT_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastISizeToI8(Dqn_isize val)
|
||||
{
|
||||
DQN_ASSERT(val >= INT8_MIN && val <= INT8_MAX);
|
||||
int8_t result = DQN_CAST(int8_t)DQN_CLAMP(val, INT8_MIN, INT8_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastISizeToI16(Dqn_isize val)
|
||||
{
|
||||
DQN_ASSERT(val >= INT16_MIN && val <= INT16_MAX);
|
||||
int16_t result = DQN_CAST(int16_t)DQN_CLAMP(val, INT16_MIN, INT16_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int32_t Dqn_Safe_SaturateCastISizeToI32(Dqn_isize val)
|
||||
{
|
||||
DQN_ASSERT(val >= INT32_MIN && val <= INT32_MAX);
|
||||
int32_t result = DQN_CAST(int32_t)DQN_CLAMP(val, INT32_MIN, INT32_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int64_t Dqn_Safe_SaturateCastISizeToI64(Dqn_isize val)
|
||||
{
|
||||
DQN_ASSERT(val >= INT64_MIN && val <= INT64_MAX);
|
||||
int64_t result = DQN_CAST(int64_t)DQN_CLAMP(val, INT64_MIN, INT64_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_Safe_SaturateCastISizeToU*
|
||||
// -----------------------------------------------------------------------------
|
||||
// If the value is a negative integer, we clamp to 0. Otherwise, we know that
|
||||
// the value is >=0, we can upcast safely to bounds check against the maximum
|
||||
// allowed value.
|
||||
DQN_API unsigned int Dqn_Safe_SaturateCastISizeToUInt(Dqn_isize val)
|
||||
{
|
||||
unsigned int result = 0;
|
||||
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT_MAX))
|
||||
result = DQN_CAST(unsigned int)val;
|
||||
else
|
||||
result = UINT_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastISizeToU8(Dqn_isize val)
|
||||
{
|
||||
uint8_t result = 0;
|
||||
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT8_MAX))
|
||||
result = DQN_CAST(uint8_t)val;
|
||||
else
|
||||
result = UINT8_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastISizeToU16(Dqn_isize val)
|
||||
{
|
||||
uint16_t result = 0;
|
||||
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT16_MAX))
|
||||
result = DQN_CAST(uint16_t)val;
|
||||
else
|
||||
result = UINT16_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastISizeToU32(Dqn_isize val)
|
||||
{
|
||||
uint32_t result = 0;
|
||||
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT32_MAX))
|
||||
result = DQN_CAST(uint32_t)val;
|
||||
else
|
||||
result = UINT32_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_SaturateCastISizeToU64(Dqn_isize val)
|
||||
{
|
||||
uint64_t result = 0;
|
||||
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT64_MAX))
|
||||
result = DQN_CAST(uint64_t)val;
|
||||
else
|
||||
result = UINT64_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_Safe_SaturateCastI64To*
|
||||
// -----------------------------------------------------------------------------
|
||||
// Both operands are signed so the lowest rank operand will be promoted to
|
||||
// match the highest rank operand.
|
||||
DQN_API Dqn_isize Dqn_Safe_SaturateCastI64ToISize(int64_t val)
|
||||
{
|
||||
DQN_CHECK(val >= DQN_ISIZE_MIN && val <= DQN_ISIZE_MAX);
|
||||
Dqn_isize result = DQN_CAST(int64_t)DQN_CLAMP(val, DQN_ISIZE_MIN, DQN_ISIZE_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastI64ToI8(int64_t val)
|
||||
{
|
||||
DQN_CHECK(val >= INT8_MIN && val <= INT8_MAX);
|
||||
int8_t result = DQN_CAST(int8_t)DQN_CLAMP(val, INT8_MIN, INT8_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastI64ToI16(int64_t val)
|
||||
{
|
||||
DQN_CHECK(val >= INT16_MIN && val <= INT16_MAX);
|
||||
int16_t result = DQN_CAST(int16_t)DQN_CLAMP(val, INT16_MIN, INT16_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int32_t Dqn_Safe_SaturateCastI64ToI32(int64_t val)
|
||||
{
|
||||
DQN_CHECK(val >= INT32_MIN && val <= INT32_MAX);
|
||||
int32_t result = DQN_CAST(int32_t)DQN_CLAMP(val, INT32_MIN, INT32_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_Safe_SaturateCastIntTo*
|
||||
// -----------------------------------------------------------------------------
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastIntToI8(int val)
|
||||
{
|
||||
DQN_CHECK(val >= INT8_MIN && val <= INT8_MAX);
|
||||
int8_t result = DQN_CAST(int8_t)DQN_CLAMP(val, INT8_MIN, INT8_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastIntToI16(int val)
|
||||
{
|
||||
DQN_CHECK(val >= INT16_MIN && val <= INT16_MAX);
|
||||
int16_t result = DQN_CAST(int16_t)DQN_CLAMP(val, INT16_MIN, INT16_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastIntToU8(int val)
|
||||
{
|
||||
uint8_t result = 0;
|
||||
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT8_MAX))
|
||||
result = DQN_CAST(uint8_t)val;
|
||||
else
|
||||
result = UINT8_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastIntToU16(int val)
|
||||
{
|
||||
uint16_t result = 0;
|
||||
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT16_MAX))
|
||||
result = DQN_CAST(uint16_t)val;
|
||||
else
|
||||
result = UINT16_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastIntToU32(int val)
|
||||
{
|
||||
static_assert(sizeof(val) <= sizeof(uint32_t), "Sanity check to allow simplifying of casting");
|
||||
uint32_t result = 0;
|
||||
if (DQN_CHECK(val >= 0))
|
||||
result = DQN_CAST(uint32_t)val;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_SaturateCastIntToU64(int val)
|
||||
{
|
||||
static_assert(sizeof(val) <= sizeof(uint64_t), "Sanity check to allow simplifying of casting");
|
||||
uint64_t result = 0;
|
||||
if (DQN_CHECK(val >= 0))
|
||||
result = DQN_CAST(uint64_t)val;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$MISC] Misc ==============================================================================
|
||||
DQN_API int Dqn_SNPrintFDotTruncate(char *buffer, int size, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int size_required = STB_SPRINTF_DECORATE(vsnprintf)(buffer, size, fmt, args);
|
||||
int result = DQN_MAX(DQN_MIN(size_required, size - 1), 0);
|
||||
if (result == size - 1) {
|
||||
buffer[size - 2] = '.';
|
||||
buffer[size - 3] = '.';
|
||||
}
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_U64String Dqn_U64ToString(uint64_t val, char separator)
|
||||
{
|
||||
Dqn_U64String result = {};
|
||||
if (val == 0) {
|
||||
result.data[result.size++] = '0';
|
||||
} else {
|
||||
// NOTE: The number is written in reverse because we form the string by
|
||||
// dividing by 10, so we write it in, then reverse it out after all is
|
||||
// done.
|
||||
Dqn_U64String temp = {};
|
||||
for (size_t digit_count = 0; val > 0; digit_count++) {
|
||||
if (separator && (digit_count != 0) && (digit_count % 3 == 0)) {
|
||||
temp.data[temp.size++] = separator;
|
||||
}
|
||||
|
||||
auto digit = DQN_CAST(char)(val % 10);
|
||||
temp.data[temp.size++] = '0' + digit;
|
||||
val /= 10;
|
||||
}
|
||||
|
||||
// NOTE: Reverse the string
|
||||
for (size_t temp_index = temp.size - 1; temp_index < temp.size; temp_index--) {
|
||||
char ch = temp.data[temp_index];
|
||||
result.data[result.size++] = ch;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$DLIB] Dqn_Library =======================================================================
|
||||
Dqn_Library *g_dqn_library;
|
||||
|
||||
DQN_API Dqn_Library *Dqn_Library_Init()
|
||||
{
|
||||
if (!g_dqn_library) {
|
||||
static Dqn_Library default_instance = {};
|
||||
g_dqn_library = &default_instance;
|
||||
}
|
||||
|
||||
Dqn_Library *result = g_dqn_library;
|
||||
Dqn_TicketMutex_Begin(&result->lib_mutex);
|
||||
DQN_DEFER { Dqn_TicketMutex_End(&result->lib_mutex); };
|
||||
if (result->lib_init)
|
||||
return result;
|
||||
|
||||
// =============================================================================================
|
||||
|
||||
Dqn_ArenaCatalog_Init(&result->arena_catalog, &result->arena_catalog_backup_arena);
|
||||
result->lib_init = true;
|
||||
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
result->profiler = &result->profiler_default_instance;
|
||||
#endif
|
||||
|
||||
// =============================================================================================
|
||||
|
||||
{ // NOTE: Query OS page size
|
||||
SYSTEM_INFO system_info = {};
|
||||
#if defined(DQN_OS_WIN32)
|
||||
GetSystemInfo(&system_info);
|
||||
result->os_page_size = system_info.dwPageSize;
|
||||
result->os_alloc_granularity = system_info.dwAllocationGranularity;
|
||||
#else
|
||||
// TODO(doyle): Get the proper page size from the OS.
|
||||
result->os_page_size = DQN_KILOBYTES(4);
|
||||
result->os_alloc_granularity = DQN_KILOBYTES(64);
|
||||
#endif
|
||||
}
|
||||
|
||||
// =============================================================================================
|
||||
|
||||
#if defined(DQN_LEAK_TRACING) // NOTE: Initialise the allocation leak tracker
|
||||
{
|
||||
result->alloc_tracking_disabled = true; // TODO(doyle): @robust Does this need to be atomic?
|
||||
|
||||
Dqn_String8 sample_backtrace = Dqn_String8_InitCString8(b_stacktrace_get_string());
|
||||
Dqn_String8 clean_backtrace = Dqn_Debug_CleanStackTrace(sample_backtrace);
|
||||
result->stack_trace_offset_to_our_call_stack = DQN_CAST(uint16_t)(sample_backtrace.size - clean_backtrace.size);
|
||||
free(sample_backtrace.data);
|
||||
|
||||
result->alloc_table = Dqn_DSMap_Init<Dqn_AllocRecord>(4096);
|
||||
result->alloc_tracking_disabled = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Library_SetPointer(Dqn_Library *library)
|
||||
{
|
||||
if (library)
|
||||
g_dqn_library = library;
|
||||
}
|
||||
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
DQN_API void Dqn_Library_SetProfiler(Dqn_Profiler *profiler)
|
||||
{
|
||||
if (profiler)
|
||||
g_dqn_library->profiler = profiler;
|
||||
}
|
||||
#endif
|
||||
|
||||
DQN_API void Dqn_Library_SetLogCallback(Dqn_LogProc *proc, void *user_data)
|
||||
{
|
||||
g_dqn_library->log_callback = proc;
|
||||
g_dqn_library->log_user_data = user_data;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Library_SetLogFile(FILE *file)
|
||||
{
|
||||
Dqn_TicketMutex_Begin(&g_dqn_library->log_file_mutex);
|
||||
g_dqn_library->log_file = file;
|
||||
g_dqn_library->log_to_file = file ? true : false;
|
||||
Dqn_TicketMutex_End(&g_dqn_library->log_file_mutex);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_String8 file_path)
|
||||
{
|
||||
#if defined(DQN_DEBUG_THREAD_CONTEXT)
|
||||
// NOTE: Open a file to write the arena stats to
|
||||
FILE *file = nullptr;
|
||||
fopen_s(&file, file_path.data, "a+b");
|
||||
if (file) {
|
||||
Dqn_Log_ErrorF("Failed to dump thread context arenas [file=%.*s]", DQN_STRING_FMT(file_path));
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: Copy the stats from library book-keeping
|
||||
// NOTE: Extremely short critical section, copy the stats then do our
|
||||
// work on it.
|
||||
Dqn_ArenaStat stats[Dqn_CArray_CountI(g_dqn_library->thread_context_arena_stats)];
|
||||
int stats_size = 0;
|
||||
|
||||
Dqn_TicketMutex_Begin(&g_dqn_library->thread_context_mutex);
|
||||
stats_size = g_dqn_library->thread_context_arena_stats_count;
|
||||
DQN_MEMCPY(stats, g_dqn_library->thread_context_arena_stats, sizeof(stats[0]) * stats_size);
|
||||
Dqn_TicketMutex_End(&g_dqn_library->thread_context_mutex);
|
||||
|
||||
// NOTE: Print the cumulative stat
|
||||
Dqn_DateHMSTimeString now = Dqn_Date_HMSLocalTimeStringNow();
|
||||
fprintf(file,
|
||||
"Time=%.*s %.*s | Thread Context Arenas | Count=%d\n",
|
||||
now.date_size, now.date,
|
||||
now.hms_size, now.hms,
|
||||
g_dqn_library->thread_context_arena_stats_count);
|
||||
|
||||
// NOTE: Write the cumulative thread arena data
|
||||
{
|
||||
Dqn_ArenaStat stat = {};
|
||||
for (Dqn_usize index = 0; index < stats_size; index++) {
|
||||
Dqn_ArenaStat const *current = stats + index;
|
||||
stat.capacity += current->capacity;
|
||||
stat.used += current->used;
|
||||
stat.wasted += current->wasted;
|
||||
stat.blocks += current->blocks;
|
||||
|
||||
stat.capacity_hwm = DQN_MAX(stat.capacity_hwm, current->capacity_hwm);
|
||||
stat.used_hwm = DQN_MAX(stat.used_hwm, current->used_hwm);
|
||||
stat.wasted_hwm = DQN_MAX(stat.wasted_hwm, current->wasted_hwm);
|
||||
stat.blocks_hwm = DQN_MAX(stat.blocks_hwm, current->blocks_hwm);
|
||||
}
|
||||
|
||||
Dqn_ArenaStatString stats_string = Dqn_Arena_StatString(&stat);
|
||||
fprintf(file, " [ALL] CURR %.*s\n", stats_string.size, stats_string.data);
|
||||
}
|
||||
|
||||
// NOTE: Print individual thread arena data
|
||||
for (Dqn_usize index = 0; index < stats_size; index++) {
|
||||
Dqn_ArenaStat const *current = stats + index;
|
||||
Dqn_ArenaStatString current_string = Dqn_Arena_StatString(current);
|
||||
fprintf(file, " [%03d] CURR %.*s\n", DQN_CAST(int)index, current_string.size, current_string.data);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
Dqn_Log_InfoF("Dumped thread context arenas [file=%.*s]", DQN_STRING_FMT(file_path));
|
||||
#else
|
||||
(void)file_path;
|
||||
#endif // #if defined(DQN_DEBUG_THREAD_CONTEXT)
|
||||
}
|
||||
|
||||
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
// NOTE: [$PROF] Dqn_Profiler ======================================================================
|
||||
Dqn_ProfilerZoneScope::Dqn_ProfilerZoneScope(uint16_t label)
|
||||
{
|
||||
zone = Dqn_Profiler_BeginZone(label);
|
||||
}
|
||||
|
||||
Dqn_ProfilerZoneScope::~Dqn_ProfilerZoneScope()
|
||||
{
|
||||
Dqn_Profiler_EndZone(zone);
|
||||
}
|
||||
|
||||
Dqn_ProfilerZone Dqn_Profiler_BeginZone(uint16_t label)
|
||||
{
|
||||
Dqn_ProfilerAnchor *anchor = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back) + label;
|
||||
Dqn_ProfilerZone result = {};
|
||||
result.begin_tsc = Dqn_CPU_TSC();
|
||||
result.label = label;
|
||||
result.parent_zone = g_dqn_library->profiler->parent_zone;
|
||||
result.elapsed_tsc_at_zone_start = anchor->tsc_inclusive;
|
||||
g_dqn_library->profiler->parent_zone = label;
|
||||
return result;
|
||||
}
|
||||
|
||||
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 = anchor_buffer + zone.label;
|
||||
|
||||
anchor->hit_count++;
|
||||
anchor->tsc_inclusive = zone.elapsed_tsc_at_zone_start + elapsed_tsc;
|
||||
anchor->tsc_exclusive += elapsed_tsc;
|
||||
|
||||
Dqn_ProfilerAnchor *parent_anchor = anchor_buffer + zone.parent_zone;
|
||||
parent_anchor->tsc_exclusive -= elapsed_tsc;
|
||||
g_dqn_library->profiler->parent_zone = zone.parent_zone;
|
||||
}
|
||||
|
||||
Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer buffer)
|
||||
{
|
||||
uint8_t offset = buffer == Dqn_ProfilerAnchorBuffer_Back ? 0 : 1;
|
||||
uint8_t anchor_buffer = (g_dqn_library->profiler->active_anchor_buffer + offset) % DQN_ARRAY_UCOUNT(g_dqn_library->profiler->anchors);
|
||||
Dqn_ProfilerAnchor *result = g_dqn_library->profiler->anchors[anchor_buffer];
|
||||
return result;
|
||||
}
|
||||
|
||||
void Dqn_Profiler_SwapAnchorBuffer(uint32_t anchor_count)
|
||||
{
|
||||
g_dqn_library->profiler->active_anchor_buffer++;
|
||||
Dqn_ProfilerAnchor *anchors = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back);
|
||||
DQN_MEMSET(anchors, 0, anchor_count * sizeof(g_dqn_library->profiler->anchors[0][0]));
|
||||
}
|
||||
#endif // !defined(DQN_NO_PROFILER)
|
655
dqn_helpers.h
Normal file
655
dqn_helpers.h
Normal file
@ -0,0 +1,655 @@
|
||||
#if !defined(DQN_NO_JSON_BUILDER)
|
||||
// NOTE: [$JSON] Dqn_JSONBuilder ===================================================================
|
||||
// Basic helper class to construct JSON output to a string
|
||||
// TODO(dqn): We need to write tests for this
|
||||
//
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_JSONBuilder_Build
|
||||
// @desc Convert the internal JSON buffer in the builder into a string.
|
||||
// @param[in] arena The allocator to use to build the string
|
||||
|
||||
// @proc Dqn_JSONBuilder_KeyValue, Dqn_JSONBuilder_KeyValueF
|
||||
// @desc Add a JSON key value pair untyped. The value is emitted directly
|
||||
// without checking the contents of value.
|
||||
//
|
||||
// All other functions internally call into this function which is the main
|
||||
// workhorse of the builder.
|
||||
|
||||
// @proc Dqn_JSON_Builder_ObjectEnd
|
||||
// @desc End a JSON object in the builder, generates internally a '}' string
|
||||
|
||||
// @proc Dqn_JSON_Builder_ArrayEnd
|
||||
// @desc End a JSON array in the builder, generates internally a ']' string
|
||||
|
||||
// @proc Dqn_JSONBuilder_LiteralNamed
|
||||
// @desc Add a named JSON key-value object whose value is directly written to
|
||||
// the following '"<key>": <value>' (e.g. useful for emitting the 'null'
|
||||
// value)
|
||||
|
||||
// @proc Dqn_JSONBuilder_U64Named, Dqn_JSONBuilder_U64,
|
||||
// Dqn_JSONBuilder_I64Named, Dqn_JSONBuilder_I64,
|
||||
// Dqn_JSONBuilder_F64Named, Dqn_JSONBuilder_F64,
|
||||
// Dqn_JSONBuilder_BoolNamed, Dqn_JSONBuilder_Bool,
|
||||
// @desc Add the named JSON data type as a key-value object. Generates
|
||||
// internally the string '"<key>": <value>'
|
||||
|
||||
enum Dqn_JSONBuilderItem
|
||||
{
|
||||
Dqn_JSONBuilderItem_Empty,
|
||||
Dqn_JSONBuilderItem_OpenContainer,
|
||||
Dqn_JSONBuilderItem_CloseContainer,
|
||||
Dqn_JSONBuilderItem_KeyValue,
|
||||
};
|
||||
|
||||
struct Dqn_JSONBuilder
|
||||
{
|
||||
bool use_stdout; // When set, ignore the string builder and dump immediately to stdout
|
||||
Dqn_String8Builder string_builder; // (Internal)
|
||||
int indent_level; // (Internal)
|
||||
int spaces_per_indent; // The number of spaces per indent level
|
||||
Dqn_JSONBuilderItem last_item;
|
||||
};
|
||||
|
||||
#define Dqn_JSONBuilder_Object(builder) \
|
||||
DQN_DEFER_LOOP(Dqn_JSONBuilder_ObjectBegin(builder), \
|
||||
Dqn_JSONBuilder_ObjectEnd(builder))
|
||||
|
||||
#define Dqn_JSONBuilder_ObjectNamed(builder, name) \
|
||||
DQN_DEFER_LOOP(Dqn_JSONBuilder_ObjectBeginNamed(builder, name), \
|
||||
Dqn_JSONBuilder_ObjectEnd(builder))
|
||||
|
||||
#define Dqn_JSONBuilder_Array(builder) \
|
||||
DQN_DEFER_LOOP(Dqn_JSONBuilder_ArrayBegin(builder), \
|
||||
Dqn_JSONBuilder_ArrayEnd(builder))
|
||||
|
||||
#define Dqn_JSONBuilder_ArrayNamed(builder, name) \
|
||||
DQN_DEFER_LOOP(Dqn_JSONBuilder_ArrayBeginNamed(builder, name), \
|
||||
Dqn_JSONBuilder_ArrayEnd(builder))
|
||||
|
||||
|
||||
DQN_API Dqn_JSONBuilder Dqn_JSONBuilder_Init (Dqn_Allocator allocator, int spaces_per_indent);
|
||||
DQN_API Dqn_String8 Dqn_JSONBuilder_Build (Dqn_JSONBuilder const *builder, Dqn_Allocator allocator);
|
||||
DQN_API void Dqn_JSONBuilder_KeyValue (Dqn_JSONBuilder *builder, Dqn_String8 key, Dqn_String8 value);
|
||||
DQN_API void Dqn_JSONBuilder_KeyValueF (Dqn_JSONBuilder *builder, Dqn_String8 key, char const *value_fmt, ...);
|
||||
DQN_API void Dqn_JSONBuilder_ObjectBeginNamed(Dqn_JSONBuilder *builder, Dqn_String8 name);
|
||||
DQN_API void Dqn_JSONBuilder_ObjectEnd (Dqn_JSONBuilder *builder);
|
||||
DQN_API void Dqn_JSONBuilder_ArrayBeginNamed (Dqn_JSONBuilder *builder, Dqn_String8 name);
|
||||
DQN_API void Dqn_JSONBuilder_ArrayEnd (Dqn_JSONBuilder *builder);
|
||||
DQN_API void Dqn_JSONBuilder_StringNamed (Dqn_JSONBuilder *builder, Dqn_String8 key, Dqn_String8 value);
|
||||
DQN_API void Dqn_JSONBuilder_LiteralNamed (Dqn_JSONBuilder *builder, Dqn_String8 key, Dqn_String8 value);
|
||||
DQN_API void Dqn_JSONBuilder_U64Named (Dqn_JSONBuilder *builder, Dqn_String8 key, uint64_t value);
|
||||
DQN_API void Dqn_JSONBuilder_I64Named (Dqn_JSONBuilder *builder, Dqn_String8 key, int64_t value);
|
||||
DQN_API void Dqn_JSONBuilder_F64Named (Dqn_JSONBuilder *builder, Dqn_String8 key, double value, int decimal_places);
|
||||
DQN_API void Dqn_JSONBuilder_BoolNamed (Dqn_JSONBuilder *builder, Dqn_String8 key, bool value);
|
||||
|
||||
#define Dqn_JSONBuilder_ObjectBegin(builder) Dqn_JSONBuilder_ObjectBeginNamed(builder, DQN_STRING8(""))
|
||||
#define Dqn_JSONBuilder_ArrayBegin(builder) Dqn_JSONBuilder_ArrayBeginNamed(builder, DQN_STRING8(""))
|
||||
#define Dqn_JSONBuilder_String(builder, value) Dqn_JSONBuilder_StringNamed(builder, DQN_STRING8(""), value)
|
||||
#define Dqn_JSONBuilder_Literal(builder, value) Dqn_JSONBuilder_LiteralNamed(builder, DQN_STRING8(""), value)
|
||||
#define Dqn_JSONBuilder_U64(builder, value) Dqn_JSONBuilder_U64Named(builder, DQN_STRING8(""), value)
|
||||
#define Dqn_JSONBuilder_I64(builder, value) Dqn_JSONBuilder_I64Named(builder, DQN_STRING8(""), value)
|
||||
#define Dqn_JSONBuilder_F64(builder, value) Dqn_JSONBuilder_F64Named(builder, DQN_STRING8(""), value)
|
||||
#define Dqn_JSONBuilder_Bool(builder, value) Dqn_JSONBuilder_BoolNamed(builder, DQN_STRING8(""), value)
|
||||
#endif // !defined(DQN_NO_JSON_BUIDLER)
|
||||
|
||||
#if !defined(DQN_NO_BIN)
|
||||
// NOTE: [$BHEX] Dqn_Bin ===========================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_Bin_U64ToHexU64String
|
||||
// @desc Convert a 64 bit number to a hex string
|
||||
// @param[in] number Number to convert to hexadecimal representation
|
||||
// @param[in] flags Bit flags from Dqn_BinHexU64StringFlags to customise the
|
||||
// output of the hexadecimal string.
|
||||
// @return The hexadecimal representation of the number. This string is always
|
||||
// null-terminated.
|
||||
|
||||
// @proc Dqn_Bin_U64ToHex
|
||||
// @copybrief Dqn_Bin_U64ToHexU64String
|
||||
|
||||
// @param[in] allocator The memory allocator to use for the memory of the
|
||||
// hexadecimal string.
|
||||
// @copyparams Dqn_Bin_U64ToHexU64String
|
||||
|
||||
// @proc Dqn_Bin_HexBufferToU64
|
||||
// @desc Convert a hexadecimal string a 64 bit value.
|
||||
// Asserts if the hex string is too big to be converted into a 64 bit number.
|
||||
|
||||
// @proc Dqn_Bin_HexToU64
|
||||
// @copydoc Dqn_Bin_HexToU64
|
||||
|
||||
// @proc Dqn_Bin_BytesToHexBuffer
|
||||
// @desc Convert a binary buffer into its hex representation into dest.
|
||||
//
|
||||
// The dest buffer must be large enough to contain the hex representation, i.e.
|
||||
// atleast (src_size * 2).
|
||||
//
|
||||
// @return True if the conversion into the dest buffer was successful, false
|
||||
// otherwise (e.g. invalid arguments).
|
||||
|
||||
// @proc Dqn_Bin_BytesToHexBufferArena
|
||||
// @desc Convert a series of bytes into a string
|
||||
// @return A null-terminated hex string, null pointer if allocation failed
|
||||
|
||||
// @proc Dqn_Bin_BytesToHexArena
|
||||
// @copydoc Dqn_Bin_BytesToHexBufferArena
|
||||
// @return A hex string, the string is invalid if conversion failed.
|
||||
|
||||
// @proc Dqn_Bin_HexBufferToBytes
|
||||
// @desc Convert a hex string into binary at `dest`.
|
||||
//
|
||||
// The dest buffer must be large enough to contain the binary representation,
|
||||
// i.e. atleast ceil(hex_size / 2). This function will strip whitespace,
|
||||
// leading 0x/0X prefix from the string before conversion.
|
||||
//
|
||||
// @param[in] hex The hexadecimal string to convert
|
||||
// @param[in] hex_size Size of the hex buffer. This function can handle an odd
|
||||
// size hex string i.e. "fff" produces 0xfff0.
|
||||
// @param[out] dest Buffer to write the bytes to
|
||||
// @param[out] dest_size Maximum number of bytes to write to dest
|
||||
//
|
||||
// @return The number of bytes written to `dest_size`, this value will *never*
|
||||
// be greater than `dest_size`.
|
||||
|
||||
// @proc Dqn_Bin_HexToBytes
|
||||
// @desc String8 variant of @see Dqn_Bin_HexBufferToBytes
|
||||
|
||||
// @proc Dqn_Bin_StringHexBufferToBytesUnchecked
|
||||
// @desc Unchecked variant of @see Dqn_Bin_HexBufferToBytes
|
||||
//
|
||||
// This function skips some string checks, it assumes the hex is a valid hex
|
||||
// stream and that the arguments are valid e.g. no trimming or 0x prefix
|
||||
// stripping is performed
|
||||
|
||||
// @proc Dqn_Bin_String
|
||||
// @desc String8 variant of @see Dqn_Bin_HexBufferToBytesUnchecked
|
||||
|
||||
// @proc Dqn_Bin_HexBufferToBytesArena
|
||||
// Dynamic allocating variant of @see Dqn_Bin_HexBufferToBytesUnchecked
|
||||
//
|
||||
// @param[in] arena The arena to allocate the bytes from
|
||||
// @param[in] hex Hex string to convert into bytes
|
||||
// @param[in] size Size of the hex string
|
||||
// @param[out] real_size The size of the buffer returned by the function
|
||||
//
|
||||
// @return The byte representation of the hex string.
|
||||
|
||||
// @proc Dqn_Bin_HexToBytesArena
|
||||
// @copybrief Dqn_Bin_HexBufferToBytesArena
|
||||
//
|
||||
// @param[in] arena The arena to allocate the bytes from
|
||||
// @param[in] hex Hex string to convert into bytes
|
||||
//
|
||||
// @return The byte representation of the hex string.
|
||||
|
||||
struct Dqn_BinHexU64String
|
||||
{
|
||||
char data[2 /*0x*/ + 16 /*hex*/ + 1 /*null-terminator*/];
|
||||
uint8_t size;
|
||||
};
|
||||
|
||||
enum Dqn_BinHexU64StringFlags
|
||||
{
|
||||
Dqn_BinHexU64StringFlags_No0xPrefix = 1 << 0, /// Remove the 0x prefix from the string
|
||||
Dqn_BinHexU64StringFlags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex
|
||||
};
|
||||
|
||||
DQN_API char const * Dqn_Bin_HexBufferTrim0x (char const *hex, Dqn_usize size, Dqn_usize *real_size);
|
||||
DQN_API Dqn_String8 Dqn_Bin_HexTrim0x (Dqn_String8 string);
|
||||
|
||||
DQN_API Dqn_BinHexU64String Dqn_Bin_U64ToHexU64String (uint64_t number, uint32_t flags);
|
||||
DQN_API Dqn_String8 Dqn_Bin_U64ToHex (Dqn_Allocator allocator, uint64_t number, uint32_t flags);
|
||||
|
||||
DQN_API uint64_t Dqn_Bin_HexBufferToU64 (char const *hex, Dqn_usize size);
|
||||
DQN_API uint64_t Dqn_Bin_HexToU64 (Dqn_String8 hex);
|
||||
|
||||
DQN_API Dqn_String8 Dqn_Bin_BytesToHexArena (Dqn_Arena *arena, void const *src, Dqn_usize size);
|
||||
DQN_API char * Dqn_Bin_BytesToHexBufferArena (Dqn_Arena *arena, void const *src, Dqn_usize size);
|
||||
DQN_API bool Dqn_Bin_BytesToHexBuffer (void const *src, Dqn_usize src_size, char *dest, Dqn_usize dest_size);
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytesUnchecked(char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size);
|
||||
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytes (char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size);
|
||||
DQN_API char * Dqn_Bin_HexBufferToBytesArena (Dqn_Arena *arena, char const *hex, Dqn_usize hex_size, Dqn_usize *real_size);
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexToBytesUnchecked (Dqn_String8 hex, void *dest, Dqn_usize dest_size);
|
||||
DQN_API Dqn_usize Dqn_Bin_HexToBytes (Dqn_String8 hex, void *dest, Dqn_usize dest_size);
|
||||
DQN_API Dqn_String8 Dqn_Bin_HexToBytesArena (Dqn_Arena *arena, Dqn_String8 hex);
|
||||
#endif // !defined(DQN_NO_BIN)
|
||||
|
||||
// NOTE: [$BSEA] Dqn_BinarySearch ==================================================================
|
||||
template <typename T>
|
||||
using Dqn_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs);
|
||||
|
||||
template <typename T>
|
||||
DQN_FORCE_INLINE bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
|
||||
|
||||
enum Dqn_BinarySearchType
|
||||
{
|
||||
/// Index of the match. If no match is found, found is set to false and the
|
||||
/// index is set to 0
|
||||
Dqn_BinarySearchType_Match,
|
||||
|
||||
/// Index after the match. If no match is found, found is set to false and
|
||||
/// the index is set to one past the closest match.
|
||||
Dqn_BinarySearchType_OnePastMatch,
|
||||
};
|
||||
|
||||
struct Dqn_BinarySearchResult
|
||||
{
|
||||
bool found;
|
||||
Dqn_usize index;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Dqn_BinarySearchResult
|
||||
Dqn_BinarySearch(T const *array,
|
||||
Dqn_usize array_size,
|
||||
T const &find,
|
||||
Dqn_BinarySearchType type = Dqn_BinarySearchType_Match,
|
||||
Dqn_BinarySearchLessThanProc<T> less_than = Dqn_BinarySearch_DefaultLessThan);
|
||||
|
||||
|
||||
template <typename T> DQN_FORCE_INLINE bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs)
|
||||
{
|
||||
bool result = lhs < rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Dqn_BinarySearchResult
|
||||
Dqn_BinarySearch(T const *array,
|
||||
Dqn_usize array_size,
|
||||
T const &find,
|
||||
Dqn_BinarySearchType type,
|
||||
Dqn_BinarySearchLessThanProc<T> less_than)
|
||||
{
|
||||
Dqn_BinarySearchResult result = {};
|
||||
Dqn_usize head = 0;
|
||||
Dqn_usize tail = array_size - 1;
|
||||
if (array && array_size > 0) {
|
||||
while (!result.found && head <= tail) {
|
||||
Dqn_usize mid = (head + tail) / 2;
|
||||
T const &value = array[mid];
|
||||
if (less_than(find, value)) {
|
||||
tail = mid - 1;
|
||||
if (mid == 0)
|
||||
break;
|
||||
} else if (less_than(value, find)) {
|
||||
head = mid + 1;
|
||||
} else {
|
||||
result.found = true;
|
||||
result.index = mid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type == Dqn_BinarySearchType_OnePastMatch)
|
||||
result.index = result.found ? result.index + 1 : tail + 1;
|
||||
else
|
||||
DQN_ASSERT(type == Dqn_BinarySearchType_Match);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$BITS] Dqn_Bit ===========================================================================
|
||||
DQN_API void Dqn_Bit_UnsetInplace(uint32_t *flags, uint32_t bitfield);
|
||||
DQN_API void Dqn_Bit_SetInplace (uint32_t *flags, uint32_t bitfield);
|
||||
DQN_API bool Dqn_Bit_IsSet (uint32_t bits, uint32_t bits_to_set);
|
||||
DQN_API bool Dqn_Bit_IsNotSet (uint32_t bits, uint32_t bits_to_check);
|
||||
|
||||
// NOTE: [$SAFE] Dqn_Safe ==========================================================================
|
||||
// @proc Dqn_Safe_AddI64, Dqn_Safe_MulI64
|
||||
// @desc Add/multiply 2 I64's together, safe asserting that the operation does
|
||||
// not overflow.
|
||||
// @return The result of operation, INT64_MAX if it overflowed.
|
||||
|
||||
// @proc Dqn_Safe_AddU64, Dqn_Safe_MulU64
|
||||
// @desc Add/multiply 2 U64's together, safe asserting that the operation does
|
||||
// not overflow.
|
||||
// @return The result of operation, UINT64_MAX if it overflowed.
|
||||
|
||||
// @proc Dqn_Safe_SubU64, Dqn_Safe_SubU32
|
||||
// @desc Subtract 2xU64 or 2xU32's together, safe asserting that the operation
|
||||
// does not overflow.
|
||||
// @return The result of operation, 0 if it overflowed.
|
||||
|
||||
// @proc Dqn_Safe_SaturateCastUSizeToInt,
|
||||
// Dqn_Safe_SaturateCastUSizeToI8,
|
||||
// Dqn_Safe_SaturateCastUSizeToI16,
|
||||
// Dqn_Safe_SaturateCastUSizeToI32,
|
||||
// Dqn_Safe_SaturateCastUSizeToI64
|
||||
//
|
||||
// Dqn_Safe_SaturateCastU64ToUInt,
|
||||
// Dqn_Safe_SaturateCastU64ToU8,
|
||||
// Dqn_Safe_SaturateCastU64ToU16,
|
||||
// Dqn_Safe_SaturateCastU64ToU32,
|
||||
//
|
||||
// Dqn_Safe_SaturateCastUSizeToU8,
|
||||
// Dqn_Safe_SaturateCastUSizeToU16,
|
||||
// Dqn_Safe_SaturateCastUSizeToU32,
|
||||
// Dqn_Safe_SaturateCastUSizeToU64
|
||||
//
|
||||
// Dqn_Safe_SaturateCastISizeToInt,
|
||||
// Dqn_Safe_SaturateCastISizeToI8,
|
||||
// Dqn_Safe_SaturateCastISizeToI16,
|
||||
// Dqn_Safe_SaturateCastISizeToI32,
|
||||
// Dqn_Safe_SaturateCastISizeToI64,
|
||||
//
|
||||
// int Dqn_Safe_SaturateCastISizeToUInt,
|
||||
// Dqn_Safe_SaturateCastISizeToU8,
|
||||
// Dqn_Safe_SaturateCastISizeToU16,
|
||||
// Dqn_Safe_SaturateCastISizeToU32,
|
||||
// Dqn_Safe_SaturateCastISizeToU64,
|
||||
//
|
||||
// Dqn_Safe_SaturateCastI64ToISize,
|
||||
// Dqn_Safe_SaturateCastI64ToI8,
|
||||
// Dqn_Safe_SaturateCastI64ToI16,
|
||||
// Dqn_Safe_SaturateCastI64ToI32,
|
||||
//
|
||||
// Dqn_Safe_SaturateCastIntToI8,
|
||||
// Dqn_Safe_SaturateCastIntToU8,
|
||||
// Dqn_Safe_SaturateCastIntToU16,
|
||||
// Dqn_Safe_SaturateCastIntToU32,
|
||||
// Dqn_Safe_SaturateCastIntToU64,
|
||||
//
|
||||
// @desc Truncate the lhs operand to the right clamping the result to the max
|
||||
// value of the desired data type. Safe asserts if clamping occurs.
|
||||
//
|
||||
// The following sentinel values are returned when saturated,
|
||||
// USize -> Int: INT_MAX
|
||||
// USize -> I8: INT8_MAX
|
||||
// USize -> I16: INT16_MAX
|
||||
// USize -> I32: INT32_MAX
|
||||
// USize -> I64: INT64_MAX
|
||||
//
|
||||
// U64 -> UInt: UINT_MAX
|
||||
// U64 -> U8: UINT8_MAX
|
||||
// U64 -> U16: UINT16_MAX
|
||||
// U64 -> U32: UINT32_MAX
|
||||
//
|
||||
// USize -> U8: UINT8_MAX
|
||||
// USize -> U16: UINT16_MAX
|
||||
// USize -> U32: UINT32_MAX
|
||||
// USize -> U64: UINT64_MAX
|
||||
//
|
||||
// ISize -> Int: INT_MIN or INT_MAX
|
||||
// ISize -> I8: INT8_MIN or INT8_MAX
|
||||
// ISize -> I16: INT16_MIN or INT16_MAX
|
||||
// ISize -> I32: INT32_MIN or INT32_MAX
|
||||
// ISize -> I64: INT64_MIN or INT64_MAX
|
||||
//
|
||||
// ISize -> UInt: 0 or UINT_MAX
|
||||
// ISize -> U8: 0 or UINT8_MAX
|
||||
// ISize -> U16: 0 or UINT16_MAX
|
||||
// ISize -> U32: 0 or UINT32_MAX
|
||||
// ISize -> U64: 0 or UINT64_MAX
|
||||
//
|
||||
// I64 -> ISize: DQN_ISIZE_MIN or DQN_ISIZE_MAX
|
||||
// I64 -> I8: INT8_MIN or INT8_MAX
|
||||
// I64 -> I16: INT16_MIN or INT16_MAX
|
||||
// I64 -> I32: INT32_MIN or INT32_MAX
|
||||
//
|
||||
// Int -> I8: INT8_MIN or INT8_MAX
|
||||
// Int -> I16: INT16_MIN or INT16_MAX
|
||||
// Int -> U8: 0 or UINT8_MAX
|
||||
// Int -> U16: 0 or UINT16_MAX
|
||||
// Int -> U32: 0 or UINT32_MAX
|
||||
// Int -> U64: 0 or UINT64_MAX
|
||||
|
||||
DQN_API int64_t Dqn_Safe_AddI64 (int64_t a, int64_t b);
|
||||
DQN_API int64_t Dqn_Safe_MulI64 (int64_t a, int64_t b);
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_AddU64 (uint64_t a, uint64_t b);
|
||||
DQN_API uint64_t Dqn_Safe_MulU64 (uint64_t a, uint64_t b);
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_SubU64 (uint64_t a, uint64_t b);
|
||||
DQN_API uint32_t Dqn_Safe_SubU32 (uint32_t a, uint32_t b);
|
||||
|
||||
DQN_API int Dqn_Safe_SaturateCastUSizeToInt (Dqn_usize val);
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastUSizeToI8 (Dqn_usize val);
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastUSizeToI16 (Dqn_usize val);
|
||||
DQN_API int32_t Dqn_Safe_SaturateCastUSizeToI32 (Dqn_usize val);
|
||||
DQN_API int64_t Dqn_Safe_SaturateCastUSizeToI64 (Dqn_usize val);
|
||||
|
||||
DQN_API unsigned int Dqn_Safe_SaturateCastU64ToUInt (uint64_t val);
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastU64ToU8 (uint64_t val);
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastU64ToU16 (uint64_t val);
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastU64ToU32 (uint64_t val);
|
||||
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastUSizeToU8 (Dqn_usize val);
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastUSizeToU16 (Dqn_usize val);
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastUSizeToU32 (Dqn_usize val);
|
||||
DQN_API uint64_t Dqn_Safe_SaturateCastUSizeToU64 (Dqn_usize val);
|
||||
|
||||
DQN_API int Dqn_Safe_SaturateCastISizeToInt (Dqn_isize val);
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastISizeToI8 (Dqn_isize val);
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastISizeToI16 (Dqn_isize val);
|
||||
DQN_API int32_t Dqn_Safe_SaturateCastISizeToI32 (Dqn_isize val);
|
||||
DQN_API int64_t Dqn_Safe_SaturateCastISizeToI64 (Dqn_isize val);
|
||||
|
||||
DQN_API unsigned int Dqn_Safe_SaturateCastISizeToUInt(Dqn_isize val);
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastISizeToU8 (Dqn_isize val);
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastISizeToU16 (Dqn_isize val);
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastISizeToU32 (Dqn_isize val);
|
||||
DQN_API uint64_t Dqn_Safe_SaturateCastISizeToU64 (Dqn_isize val);
|
||||
|
||||
DQN_API Dqn_isize Dqn_Safe_SaturateCastI64ToISize (int64_t val);
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastI64ToI8 (int64_t val);
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastI64ToI16 (int64_t val);
|
||||
DQN_API int32_t Dqn_Safe_SaturateCastI64ToI32 (int64_t val);
|
||||
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastIntToI8 (int val);
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastIntToI16 (int val);
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastIntToU8 (int val);
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastIntToU16 (int val);
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastIntToU32 (int val);
|
||||
DQN_API uint64_t Dqn_Safe_SaturateCastIntToU64 (int val);
|
||||
|
||||
// NOTE: [$MISC] Misc ==============================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_SNPrintFDotTruncate
|
||||
// @desc Write the format string to the buffer truncating with a trailing ".."
|
||||
// if there is insufficient space in the buffer followed by null-terminating
|
||||
// the buffer (uses stb_sprintf underneath).
|
||||
// @return The size of the string written to the buffer *not* including the
|
||||
// null-terminator.
|
||||
//
|
||||
// @proc Dqn_U64ToString
|
||||
// @desc Convert a 64 bit unsigned value to its string representation.
|
||||
// @param[in] val Value to convert into a string
|
||||
// @param[in] separator The separator to insert every 3 digits. Set this to
|
||||
// 0 if no separator is desired.
|
||||
|
||||
struct Dqn_U64String
|
||||
{
|
||||
char data[27+1]; // NOTE(dqn): 27 is the maximum size of uint64_t including a separtor
|
||||
uint8_t size;
|
||||
};
|
||||
|
||||
DQN_API int Dqn_SNPrintFDotTruncate(char *buffer, int size, char const *fmt, ...);
|
||||
DQN_API Dqn_U64String Dqn_U64ToString (uint64_t val, char separator);
|
||||
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
// NOTE: [$PROF] Dqn_Profiler ======================================================================
|
||||
// A profiler based off Casey Muratori's Computer Enhance course, Performance
|
||||
// Aware Programming. This profiler measures function elapsed time using the
|
||||
// CPU's time stamp counter (e.g. rdtsc) providing a rough cycle count
|
||||
// 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.
|
||||
#if 0
|
||||
uint64_t tsc_per_seconds = Dqn_EstimateTSCPerSecond(/*duration_ms_to_gauge_tsc_frequency*/ 100);
|
||||
for (;;) { // Main update loop
|
||||
enum Zone { Zone_MainLoop, Zone_Count };
|
||||
Dqn_ProfilerZone profiler_zone_main_update = Dqn_Profiler_BeginZone(Zone_MainLoop);
|
||||
Dqn_ProfilerAnchor *anchors = Dqn_ProfilerAnchorBuffer(Dqn_ProfilerAnchorBuffer_Front);
|
||||
for (size_t index = 0; index < Zone_Count; index++) {
|
||||
Dqn_ProfilerAnchor *anchor = anchors + index;
|
||||
printf("%.*s[%u] %zu cycles (%.1fms)\n",
|
||||
DQN_STRING_FMT(anchor->label),
|
||||
anchor->hit_count,
|
||||
anchor->tsc_inclusive,
|
||||
anchor->tsc_inclusive * tsc_per_seconds * 1000.f);
|
||||
}
|
||||
Dqn_Profiler_EndZone(&profiler_zone_main_update);
|
||||
Dqn_Profiler_SwapAnchorBuffer(Zone_Count); // Should occur after all profiling zones are ended!
|
||||
}
|
||||
#endif
|
||||
|
||||
// @proc Dqn_Profiler_AnchorBuffer
|
||||
// Retrieve the requested buffer from the profiler for writing/reading
|
||||
// profiling metrics.
|
||||
//
|
||||
// @param buffer Enum that lets you specify which buffer to grab from the
|
||||
// profiler. The front buffer contains the previous frame's profiling metrics
|
||||
// and the back buffer is where the profiler is current writing to.
|
||||
//
|
||||
// For end user intents and purposes, you likely only need to read the front
|
||||
// buffer which contain the metrics that you can visualise regarding the most
|
||||
// profiling metrics recorded.
|
||||
|
||||
#if !defined(DQN_PROFILER_ANCHOR_BUFFER_SIZE)
|
||||
#define DQN_PROFILER_ANCHOR_BUFFER_SIZE 128
|
||||
#endif
|
||||
|
||||
struct Dqn_ProfilerAnchor
|
||||
{
|
||||
// Inclusive refers to the time spent to complete the function call
|
||||
// including all children functions.
|
||||
//
|
||||
// Exclusive refers to the time spent in the function, not including any
|
||||
// time spent in children functions that we call that are also being
|
||||
// profiled. If we recursively call into ourselves, the time we spent in
|
||||
// our function is accumulated.
|
||||
uint64_t tsc_inclusive;
|
||||
uint64_t tsc_exclusive;
|
||||
uint16_t hit_count;
|
||||
};
|
||||
|
||||
struct Dqn_ProfilerZone
|
||||
{
|
||||
uint16_t label;
|
||||
uint64_t begin_tsc;
|
||||
uint16_t parent_zone;
|
||||
uint64_t elapsed_tsc_at_zone_start;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
struct Dqn_ProfilerZoneScope
|
||||
{
|
||||
Dqn_ProfilerZoneScope(uint16_t label);
|
||||
~Dqn_ProfilerZoneScope();
|
||||
Dqn_ProfilerZone zone;
|
||||
};
|
||||
#define Dqn_Profiler_ZoneScope(label) auto DQN_UNIQUE_NAME(profile_zone_##label_) = Dqn_ProfilerZoneScope(label)
|
||||
#endif
|
||||
|
||||
enum Dqn_ProfilerAnchorBuffer
|
||||
{
|
||||
Dqn_ProfilerAnchorBuffer_Back,
|
||||
Dqn_ProfilerAnchorBuffer_Front,
|
||||
};
|
||||
|
||||
struct Dqn_Profiler
|
||||
{
|
||||
Dqn_ProfilerAnchor anchors[2][DQN_PROFILER_ANCHOR_BUFFER_SIZE];
|
||||
uint8_t active_anchor_buffer;
|
||||
uint16_t parent_zone;
|
||||
};
|
||||
|
||||
Dqn_ProfilerZone Dqn_Profiler_BeginZone (uint16_t label);
|
||||
void Dqn_Profiler_EndZone (Dqn_ProfilerZone zone);
|
||||
Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer (Dqn_ProfilerAnchorBuffer buffer);
|
||||
void Dqn_Profiler_SwapAnchorBuffer (uint32_t anchor_count);
|
||||
#endif // !defined(DQN_NO_PROFILER)
|
||||
|
||||
// NOTE: [$DLIB] Dqn_Library =======================================================================
|
||||
// Book-keeping data for the library and allow customisation of certain features
|
||||
// provided.
|
||||
//
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_Library_SetLogCallback
|
||||
// @desc Update the default logging function, all logging functions will run through
|
||||
// this callback
|
||||
// @param[in] proc The new logging function, set to nullptr to revert back to
|
||||
// the default logger.
|
||||
// @param[in] user_data A user defined parameter to pass to the callback
|
||||
|
||||
// @proc Dqn_Library_SetLogFile
|
||||
// @param[in] file Pass in nullptr to turn off writing logs to disk, otherwise
|
||||
// point it to the FILE that you wish to write to.
|
||||
|
||||
// @proc Dqn_Library_DumpThreadContextArenaStat
|
||||
// @desc Dump the per-thread arena statistics to the specified file
|
||||
|
||||
struct Dqn_Library
|
||||
{
|
||||
bool lib_init;
|
||||
Dqn_TicketMutex lib_mutex;
|
||||
|
||||
Dqn_LogProc *log_callback; ///< Set this pointer to override the logging routine
|
||||
void * log_user_data;
|
||||
bool log_to_file; ///< Output logs to file as well as standard out
|
||||
void * log_file; ///< TODO(dqn): Hmmm, how should we do this... ?
|
||||
Dqn_TicketMutex log_file_mutex; ///< Is locked when instantiating the log_file for the first time
|
||||
bool log_no_colour; ///< Disable colours in the logging output
|
||||
|
||||
/// The backup arena to use if no arena is passed into Dqn_Library_Init
|
||||
Dqn_Arena arena_catalog_backup_arena;
|
||||
Dqn_ArenaCatalog arena_catalog;
|
||||
|
||||
// NOTE: Leak Tracing ==========================================================================
|
||||
|
||||
bool allocs_are_allowed_to_leak;
|
||||
bool alloc_tracking_disabled;
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
#if defined(DQN_NO_DSMAP)
|
||||
#error "DSMap is required for allocation tracing"
|
||||
#endif
|
||||
|
||||
// TODO(doyle): @robust Setting some of these flags is not multi-thread safe
|
||||
uint16_t stack_trace_offset_to_our_call_stack;
|
||||
Dqn_TicketMutex alloc_table_mutex;
|
||||
Dqn_DSMap<Dqn_AllocRecord> alloc_table;
|
||||
#endif
|
||||
|
||||
// NOTE: OS ====================================================================================
|
||||
|
||||
#if defined(DQN_OS_WIN32)
|
||||
LARGE_INTEGER win32_qpc_frequency;
|
||||
Dqn_TicketMutex win32_bcrypt_rng_mutex;
|
||||
void *win32_bcrypt_rng_handle;
|
||||
#endif
|
||||
|
||||
uint32_t os_page_size;
|
||||
uint32_t os_alloc_granularity;
|
||||
|
||||
// NOTE: Thread Context ========================================================================
|
||||
|
||||
#if defined(DQN_DEBUG_THREAD_CONTEXT)
|
||||
Dqn_TicketMutex thread_context_mutex;
|
||||
Dqn_ArenaStat thread_context_arena_stats[256];
|
||||
uint8_t thread_context_arena_stats_count;
|
||||
#endif
|
||||
|
||||
// NOTE: Profiler ==============================================================================
|
||||
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
Dqn_Profiler *profiler;
|
||||
Dqn_Profiler profiler_default_instance;
|
||||
#endif
|
||||
} extern *g_dqn_library;
|
||||
|
||||
// NOTE: API =======================================================================================
|
||||
DQN_API Dqn_Library *Dqn_Library_Init ();
|
||||
DQN_API void Dqn_Library_SetPointer (Dqn_Library *library);
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
DQN_API void Dqn_Library_SetProfiler (Dqn_Profiler *profiler);
|
||||
#endif
|
||||
DQN_API void Dqn_Library_SetLogCallback (Dqn_LogProc *proc, void *user_data);
|
||||
DQN_API void Dqn_Library_SetLogFile (void *file);
|
||||
DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_String8 file_path);
|
||||
|
211
dqn_math.cpp
211
dqn_math.cpp
@ -1,5 +1,6 @@
|
||||
#if !defined(DQN_NO_V2)
|
||||
// NOTE: [$VEC2] Vector2 ===========================================================================
|
||||
// NOTE: Dqn_V2I
|
||||
DQN_API bool operator!=(Dqn_V2I lhs, Dqn_V2I rhs)
|
||||
{
|
||||
bool result = !(lhs == rhs);
|
||||
@ -132,7 +133,140 @@ DQN_API Dqn_V2I &operator+=(Dqn_V2I &lhs, Dqn_V2I rhs)
|
||||
return lhs;
|
||||
}
|
||||
|
||||
// NOTE: Vector2
|
||||
// NOTE: Dqn_V2U16
|
||||
DQN_API bool operator!=(Dqn_V2U16 lhs, Dqn_V2U16 rhs)
|
||||
{
|
||||
bool result = !(lhs == rhs);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool operator==(Dqn_V2U16 lhs, Dqn_V2U16 rhs)
|
||||
{
|
||||
bool result = (lhs.x == rhs.x) && (lhs.y == rhs.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool operator>=(Dqn_V2U16 lhs, Dqn_V2U16 rhs)
|
||||
{
|
||||
bool result = (lhs.x >= rhs.x) && (lhs.y >= rhs.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool operator<=(Dqn_V2U16 lhs, Dqn_V2U16 rhs)
|
||||
{
|
||||
bool result = (lhs.x <= rhs.x) && (lhs.y <= rhs.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool operator<(Dqn_V2U16 lhs, Dqn_V2U16 rhs)
|
||||
{
|
||||
bool result = (lhs.x < rhs.x) && (lhs.y < rhs.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool operator>(Dqn_V2U16 lhs, Dqn_V2U16 rhs)
|
||||
{
|
||||
bool result = (lhs.x > rhs.x) && (lhs.y > rhs.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2U16 operator-(Dqn_V2U16 lhs, Dqn_V2U16 rhs)
|
||||
{
|
||||
Dqn_V2U16 result = Dqn_V2U16_InitNx2(lhs.x - rhs.x, lhs.y - rhs.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2U16 operator+(Dqn_V2U16 lhs, Dqn_V2U16 rhs)
|
||||
{
|
||||
Dqn_V2U16 result = Dqn_V2U16_InitNx2(lhs.x + rhs.x, lhs.y + rhs.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2U16 operator*(Dqn_V2U16 lhs, Dqn_V2U16 rhs)
|
||||
{
|
||||
Dqn_V2U16 result = Dqn_V2U16_InitNx2(lhs.x * rhs.x, lhs.y * rhs.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2U16 operator*(Dqn_V2U16 lhs, Dqn_f32 rhs)
|
||||
{
|
||||
Dqn_V2U16 result = Dqn_V2U16_InitNx2(lhs.x * rhs, lhs.y * rhs);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2U16 operator*(Dqn_V2U16 lhs, int32_t rhs)
|
||||
{
|
||||
Dqn_V2U16 result = Dqn_V2U16_InitNx2(lhs.x * rhs, lhs.y * rhs);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2U16 operator/(Dqn_V2U16 lhs, Dqn_V2U16 rhs)
|
||||
{
|
||||
Dqn_V2U16 result = Dqn_V2U16_InitNx2(lhs.x / rhs.x, lhs.y / rhs.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2U16 operator/(Dqn_V2U16 lhs, Dqn_f32 rhs)
|
||||
{
|
||||
Dqn_V2U16 result = Dqn_V2U16_InitNx2(lhs.x / rhs, lhs.y / rhs);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2U16 operator/(Dqn_V2U16 lhs, int32_t rhs)
|
||||
{
|
||||
Dqn_V2U16 result = Dqn_V2U16_InitNx2(lhs.x / rhs, lhs.y / rhs);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2U16 &operator*=(Dqn_V2U16 &lhs, Dqn_V2U16 rhs)
|
||||
{
|
||||
lhs = lhs * rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2U16 &operator*=(Dqn_V2U16 &lhs, Dqn_f32 rhs)
|
||||
{
|
||||
lhs = lhs * rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2U16 &operator*=(Dqn_V2U16 &lhs, int32_t rhs)
|
||||
{
|
||||
lhs = lhs * rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2U16 &operator/=(Dqn_V2U16 &lhs, Dqn_V2U16 rhs)
|
||||
{
|
||||
lhs = lhs / rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2U16 &operator/=(Dqn_V2U16 &lhs, Dqn_f32 rhs)
|
||||
{
|
||||
lhs = lhs / rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2U16 &operator/=(Dqn_V2U16 &lhs, int32_t rhs)
|
||||
{
|
||||
lhs = lhs / rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2U16 &operator-=(Dqn_V2U16 &lhs, Dqn_V2U16 rhs)
|
||||
{
|
||||
lhs = lhs - rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2U16 &operator+=(Dqn_V2U16 &lhs, Dqn_V2U16 rhs)
|
||||
{
|
||||
lhs = lhs + rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_V2
|
||||
DQN_API bool operator!=(Dqn_V2 lhs, Dqn_V2 rhs)
|
||||
{
|
||||
bool result = !(lhs == rhs);
|
||||
@ -874,7 +1008,7 @@ DQN_API bool Dqn_Rect_Intersects(Dqn_Rect a, Dqn_Rect b)
|
||||
|
||||
DQN_API Dqn_Rect Dqn_Rect_Intersection(Dqn_Rect a, Dqn_Rect b)
|
||||
{
|
||||
Dqn_Rect result = {};
|
||||
Dqn_Rect result = Dqn_Rect_InitV2x2(a.pos, Dqn_V2_InitNx1(0));
|
||||
if (Dqn_Rect_Intersects(a, b)) {
|
||||
Dqn_V2 a_min = a.pos;
|
||||
Dqn_V2 a_max = a.pos + a.size;
|
||||
@ -907,6 +1041,79 @@ DQN_API Dqn_Rect Dqn_Rect_Union(Dqn_Rect a, Dqn_Rect b)
|
||||
Dqn_Rect result = Dqn_Rect_InitV2x2(min, max - min);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_RectMinMax Dqn_Rect_MinMax(Dqn_Rect a)
|
||||
{
|
||||
Dqn_RectMinMax result = {};
|
||||
result.min = a.pos;
|
||||
result.max = a.pos + a.size;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Rect Dqn_Rect_CutLeftClip(Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip)
|
||||
{
|
||||
Dqn_f32 min_x = rect->pos.x;
|
||||
Dqn_f32 max_x = rect->pos.x + rect->size.w;
|
||||
Dqn_f32 result_max_x = min_x + amount;
|
||||
if (clip)
|
||||
result_max_x = DQN_MIN(result_max_x, max_x);
|
||||
Dqn_Rect result = Dqn_Rect_InitNx4(min_x, rect->pos.y, result_max_x - min_x, rect->size.h);
|
||||
rect->pos.x = result_max_x;
|
||||
rect->size.w = max_x - result_max_x;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Rect Dqn_Rect_CutRightClip(Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip)
|
||||
{
|
||||
Dqn_f32 min_x = rect->pos.x;
|
||||
Dqn_f32 max_x = rect->pos.x + rect->size.w;
|
||||
Dqn_f32 result_min_x = max_x - amount;
|
||||
if (clip)
|
||||
result_min_x = DQN_MAX(result_min_x, 0);
|
||||
Dqn_Rect result = Dqn_Rect_InitNx4(result_min_x, rect->pos.y, max_x - result_min_x, rect->size.h);
|
||||
rect->size.w = result_min_x - min_x;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Rect Dqn_Rect_CutTopClip(Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip)
|
||||
{
|
||||
Dqn_f32 min_y = rect->pos.y;
|
||||
Dqn_f32 max_y = rect->pos.y + rect->size.h;
|
||||
Dqn_f32 result_max_y = min_y + amount;
|
||||
if (clip)
|
||||
result_max_y = DQN_MIN(result_max_y, max_y);
|
||||
Dqn_Rect result = Dqn_Rect_InitNx4(rect->pos.x, min_y, rect->size.w, result_max_y - min_y);
|
||||
rect->pos.y = result_max_y;
|
||||
rect->size.h = max_y - result_max_y;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Rect Dqn_Rect_CutBottomClip(Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip)
|
||||
{
|
||||
Dqn_f32 min_y = rect->pos.y;
|
||||
Dqn_f32 max_y = rect->pos.y + rect->size.h;
|
||||
Dqn_f32 result_min_y = max_y - amount;
|
||||
if (clip)
|
||||
result_min_y = DQN_MAX(result_min_y, 0);
|
||||
Dqn_Rect result = Dqn_Rect_InitNx4(rect->pos.x, result_min_y, rect->size.w, max_y - result_min_y);
|
||||
rect->size.h = result_min_y - min_y;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Rect Dqn_RectCut_Cut(Dqn_RectCut rect_cut, Dqn_V2 size, Dqn_RectCutClip clip)
|
||||
{
|
||||
Dqn_Rect result = {};
|
||||
if (rect_cut.rect) {
|
||||
switch (rect_cut.side) {
|
||||
case Dqn_RectCutSide_Left: result = Dqn_Rect_CutLeftClip(rect_cut.rect, size.w, clip); break;
|
||||
case Dqn_RectCutSide_Right: result = Dqn_Rect_CutRightClip(rect_cut.rect, size.w, clip); break;
|
||||
case Dqn_RectCutSide_Top: result = Dqn_Rect_CutTopClip(rect_cut.rect, size.h, clip); break;
|
||||
case Dqn_RectCutSide_Bottom: result = Dqn_Rect_CutBottomClip(rect_cut.rect, size.h, clip); break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // !defined(DQN_NO_RECT)
|
||||
|
||||
// NOTE: [$MATH] Other =============================================================================
|
||||
|
139
dqn_math.h
139
dqn_math.h
@ -1,13 +1,3 @@
|
||||
// NOTE: Table Of Contents =========================================================================
|
||||
// Index | Disable #define | Description
|
||||
// =================================================================================================
|
||||
// [$VEC2] Dqn_V2, V2i | DQN_NO_V2 |
|
||||
// [$VEC3] Dqn_V3, V3i | DQN_NO_V3 |
|
||||
// [$VEC4] Dqn_V4, V4i | DQN_NO_V4 |
|
||||
// [$MAT4] Dqn_M4 | DQN_NO_M4 |
|
||||
// [$RECT] Dqn_Rect | DQN_NO_RECT |
|
||||
// [$MATH] Other | |
|
||||
// =================================================================================================
|
||||
#if defined(DQN_COMPILER_W32_MSVC)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4201) // warning C4201: nonstandard extension used: nameless struct/union
|
||||
@ -22,8 +12,8 @@ union Dqn_V2I
|
||||
int32_t data[2];
|
||||
};
|
||||
|
||||
#define Dqn_V2I_InitNx1(x) DQN_LITERAL(Dqn_V2I){(int32_t)(x), (int32_t)(x)}
|
||||
#define Dqn_V2I_InitNx2(x, y) DQN_LITERAL(Dqn_V2I){(int32_t)(x), (int32_t)(y)}
|
||||
#define Dqn_V2I_InitNx1(x) DQN_LITERAL(Dqn_V2I){{(int32_t)(x), (int32_t)(x)}}
|
||||
#define Dqn_V2I_InitNx2(x, y) DQN_LITERAL(Dqn_V2I){{(int32_t)(x), (int32_t)(y)}}
|
||||
|
||||
DQN_API bool operator!=(Dqn_V2I lhs, Dqn_V2I rhs);
|
||||
DQN_API bool operator==(Dqn_V2I lhs, Dqn_V2I rhs);
|
||||
@ -48,6 +38,39 @@ DQN_API Dqn_V2I &operator/=(Dqn_V2I& lhs, int32_t rhs);
|
||||
DQN_API Dqn_V2I &operator-=(Dqn_V2I& lhs, Dqn_V2I rhs);
|
||||
DQN_API Dqn_V2I &operator+=(Dqn_V2I& lhs, Dqn_V2I rhs);
|
||||
|
||||
union Dqn_V2U16
|
||||
{
|
||||
struct { uint16_t x, y; };
|
||||
struct { uint16_t w, h; };
|
||||
uint16_t data[2];
|
||||
};
|
||||
|
||||
#define Dqn_V2U16_InitNx1(x) DQN_LITERAL(Dqn_V2U16){{(uint16_t)(x), (uint16_t)(x)}}
|
||||
#define Dqn_V2U16_InitNx2(x, y) DQN_LITERAL(Dqn_V2U16){{(uint16_t)(x), (uint16_t)(y)}}
|
||||
|
||||
DQN_API bool operator!=(Dqn_V2U16 lhs, Dqn_V2U16 rhs);
|
||||
DQN_API bool operator==(Dqn_V2U16 lhs, Dqn_V2U16 rhs);
|
||||
DQN_API bool operator>=(Dqn_V2U16 lhs, Dqn_V2U16 rhs);
|
||||
DQN_API bool operator<=(Dqn_V2U16 lhs, Dqn_V2U16 rhs);
|
||||
DQN_API bool operator< (Dqn_V2U16 lhs, Dqn_V2U16 rhs);
|
||||
DQN_API bool operator> (Dqn_V2U16 lhs, Dqn_V2U16 rhs);
|
||||
DQN_API Dqn_V2U16 operator- (Dqn_V2U16 lhs, Dqn_V2U16 rhs);
|
||||
DQN_API Dqn_V2U16 operator+ (Dqn_V2U16 lhs, Dqn_V2U16 rhs);
|
||||
DQN_API Dqn_V2U16 operator* (Dqn_V2U16 lhs, Dqn_V2U16 rhs);
|
||||
DQN_API Dqn_V2U16 operator* (Dqn_V2U16 lhs, Dqn_f32 rhs);
|
||||
DQN_API Dqn_V2U16 operator* (Dqn_V2U16 lhs, int32_t rhs);
|
||||
DQN_API Dqn_V2U16 operator/ (Dqn_V2U16 lhs, Dqn_V2U16 rhs);
|
||||
DQN_API Dqn_V2U16 operator/ (Dqn_V2U16 lhs, Dqn_f32 rhs);
|
||||
DQN_API Dqn_V2U16 operator/ (Dqn_V2U16 lhs, int32_t rhs);
|
||||
DQN_API Dqn_V2U16 &operator*=(Dqn_V2U16& lhs, Dqn_V2U16 rhs);
|
||||
DQN_API Dqn_V2U16 &operator*=(Dqn_V2U16& lhs, Dqn_f32 rhs);
|
||||
DQN_API Dqn_V2U16 &operator*=(Dqn_V2U16& lhs, int32_t rhs);
|
||||
DQN_API Dqn_V2U16 &operator/=(Dqn_V2U16& lhs, Dqn_V2U16 rhs);
|
||||
DQN_API Dqn_V2U16 &operator/=(Dqn_V2U16& lhs, Dqn_f32 rhs);
|
||||
DQN_API Dqn_V2U16 &operator/=(Dqn_V2U16& lhs, int32_t rhs);
|
||||
DQN_API Dqn_V2U16 &operator-=(Dqn_V2U16& lhs, Dqn_V2U16 rhs);
|
||||
DQN_API Dqn_V2U16 &operator+=(Dqn_V2U16& lhs, Dqn_V2U16 rhs);
|
||||
|
||||
union Dqn_V2
|
||||
{
|
||||
struct { Dqn_f32 x, y; };
|
||||
@ -55,8 +78,8 @@ union Dqn_V2
|
||||
Dqn_f32 data[2];
|
||||
};
|
||||
|
||||
#define Dqn_V2_InitNx1(x) DQN_LITERAL(Dqn_V2){(Dqn_f32)(x), (Dqn_f32)(x)}
|
||||
#define Dqn_V2_InitNx2(x, y) DQN_LITERAL(Dqn_V2){(Dqn_f32)(x), (Dqn_f32)(y)}
|
||||
#define Dqn_V2_InitNx1(x) DQN_LITERAL(Dqn_V2){{(Dqn_f32)(x), (Dqn_f32)(x)}}
|
||||
#define Dqn_V2_InitNx2(x, y) DQN_LITERAL(Dqn_V2){{(Dqn_f32)(x), (Dqn_f32)(y)}}
|
||||
|
||||
DQN_API bool operator!=(Dqn_V2 lhs, Dqn_V2 rhs);
|
||||
DQN_API bool operator==(Dqn_V2 lhs, Dqn_V2 rhs);
|
||||
@ -100,9 +123,9 @@ union Dqn_V3
|
||||
Dqn_f32 data[3];
|
||||
};
|
||||
|
||||
#define Dqn_V3_InitNx1(x) DQN_LITERAL(Dqn_V3){(Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x)}
|
||||
#define Dqn_V3_InitNx3(x, y, z) DQN_LITERAL(Dqn_V3){(Dqn_f32)(x), (Dqn_f32)(y), (Dqn_f32)(z)}
|
||||
#define Dqn_V3_InitV2x1_Nx1(xy, z) DQN_LITERAL(Dqn_V3){(Dqn_f32)(xy.x), (Dqn_f32)(xy.y), (Dqn_f32)(z)}
|
||||
#define Dqn_V3_InitNx1(x) DQN_LITERAL(Dqn_V3){{(Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x)}}
|
||||
#define Dqn_V3_InitNx3(x, y, z) DQN_LITERAL(Dqn_V3){{(Dqn_f32)(x), (Dqn_f32)(y), (Dqn_f32)(z)}}
|
||||
#define Dqn_V3_InitV2x1_Nx1(xy, z) DQN_LITERAL(Dqn_V3){{(Dqn_f32)(xy.x), (Dqn_f32)(xy.y), (Dqn_f32)(z)}}
|
||||
|
||||
DQN_API bool operator!=(Dqn_V3 lhs, Dqn_V3 rhs);
|
||||
DQN_API bool operator==(Dqn_V3 lhs, Dqn_V3 rhs);
|
||||
@ -135,10 +158,6 @@ DQN_API Dqn_V3 Dqn_V3_Normalise(Dqn_V3 a);
|
||||
#if !defined(DQN_NO_V4)
|
||||
// NOTE: [$VEC4] Vector4 ===========================================================================
|
||||
|
||||
#if defined(DQN_NO_V3)
|
||||
#error "Dqn_Rect requires Dqn_V3 hence DQN_NO_V3 must *not* be defined"
|
||||
#endif
|
||||
|
||||
#if defined(DQN_COMPILER_W32_MSVC)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4201) // warning C4201: nonstandard extension used: nameless struct/union
|
||||
@ -147,12 +166,14 @@ union Dqn_V4
|
||||
{
|
||||
struct { Dqn_f32 x, y, z, w; };
|
||||
struct { Dqn_f32 r, g, b, a; };
|
||||
#if !defined(DQN_NO_V3)
|
||||
Dqn_V3 rgb;
|
||||
#endif
|
||||
Dqn_f32 data[4];
|
||||
};
|
||||
|
||||
#define Dqn_V4_InitNx1(x) DQN_LITERAL(Dqn_V4){(Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x)}
|
||||
#define Dqn_V4_InitNx4(x, y, z, w) DQN_LITERAL(Dqn_V4){(Dqn_f32)(x), (Dqn_f32)(y), (Dqn_f32)(z), (Dqn_f32)(w)}
|
||||
#define Dqn_V4_InitNx1(x) DQN_LITERAL(Dqn_V4){{(Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x)}}
|
||||
#define Dqn_V4_InitNx4(x, y, z, w) DQN_LITERAL(Dqn_V4){{(Dqn_f32)(x), (Dqn_f32)(y), (Dqn_f32)(z), (Dqn_f32)(w)}}
|
||||
|
||||
bool operator!=(Dqn_V4 lhs, Dqn_V4 rhs);
|
||||
bool operator==(Dqn_V4 lhs, Dqn_V4 rhs);
|
||||
@ -213,7 +234,7 @@ DQN_API Dqn_FString8<256> Dqn_M4_ColumnMajorString(Dqn_M4 mat);
|
||||
// NOTE: [$RECT] Dqn_Rect ==========================================================================
|
||||
#if !defined(DQN_NO_RECT)
|
||||
#if defined(DQN_NO_V2)
|
||||
#error "Dqn_Rect requires Dqn_V2 hence DQN_NO_V2 must *not* be defined"
|
||||
#error "Rectangles requires V2, DQN_NO_V2 must not be defined"
|
||||
#endif
|
||||
|
||||
struct Dqn_Rect
|
||||
@ -221,19 +242,65 @@ struct Dqn_Rect
|
||||
Dqn_V2 pos, size;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
#define Dqn_Rect_InitV2x2(pos, size) Dqn_Rect{pos, size}
|
||||
#else
|
||||
#define Dqn_Rect_InitV2x2(pos, size) (Dqn_Rect){pos, size}
|
||||
#endif
|
||||
struct Dqn_RectMinMax
|
||||
{
|
||||
Dqn_V2 min, max;
|
||||
};
|
||||
|
||||
DQN_API bool operator== (const Dqn_Rect& lhs, const Dqn_Rect& rhs);
|
||||
DQN_API Dqn_V2 Dqn_Rect_Center (Dqn_Rect rect);
|
||||
DQN_API bool Dqn_Rect_ContainsPoint(Dqn_Rect rect, Dqn_V2 p);
|
||||
DQN_API bool Dqn_Rect_ContainsRect (Dqn_Rect a, Dqn_Rect b);
|
||||
DQN_API bool Dqn_Rect_Intersects (Dqn_Rect a, Dqn_Rect b);
|
||||
DQN_API Dqn_Rect Dqn_Rect_Intersection (Dqn_Rect a, Dqn_Rect b);
|
||||
DQN_API Dqn_Rect Dqn_Rect_Union (Dqn_Rect a, Dqn_Rect b);
|
||||
#define Dqn_Rect_InitV2x2(pos, size) DQN_LITERAL(Dqn_Rect){(pos), (size)}
|
||||
#define Dqn_Rect_InitNx4(pos_x, pos_y, size_w, size_h) DQN_LITERAL(Dqn_Rect){DQN_LITERAL(Dqn_V2){{pos_x, pos_y}}, DQN_LITERAL(Dqn_V2){{size_w, size_h}}}
|
||||
|
||||
DQN_API bool operator== (const Dqn_Rect& lhs, const Dqn_Rect& rhs);
|
||||
DQN_API Dqn_V2 Dqn_Rect_Center (Dqn_Rect rect);
|
||||
DQN_API bool Dqn_Rect_ContainsPoint(Dqn_Rect rect, Dqn_V2 p);
|
||||
DQN_API bool Dqn_Rect_ContainsRect (Dqn_Rect a, Dqn_Rect b);
|
||||
DQN_API bool Dqn_Rect_Intersects (Dqn_Rect a, Dqn_Rect b);
|
||||
DQN_API Dqn_Rect Dqn_Rect_Intersection (Dqn_Rect a, Dqn_Rect b);
|
||||
DQN_API Dqn_Rect Dqn_Rect_Union (Dqn_Rect a, Dqn_Rect b);
|
||||
DQN_API Dqn_RectMinMax Dqn_Rect_MinMax (Dqn_Rect a);
|
||||
|
||||
enum Dqn_RectCutClip
|
||||
{
|
||||
Dqn_RectCutClip_No,
|
||||
Dqn_RectCutClip_Yes,
|
||||
};
|
||||
|
||||
DQN_API Dqn_Rect Dqn_Rect_CutLeftClip(Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip);
|
||||
DQN_API Dqn_Rect Dqn_Rect_CutRightClip(Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip);
|
||||
DQN_API Dqn_Rect Dqn_Rect_CutTopClip(Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip);
|
||||
DQN_API Dqn_Rect Dqn_Rect_CutBottomClip(Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip);
|
||||
|
||||
#define Dqn_Rect_CutLeft(rect, amount) Dqn_Rect_CutLeftClip(rect, amount, Dqn_RectCutClip_Yes)
|
||||
#define Dqn_Rect_CutRight(rect, amount) Dqn_Rect_CutRightClip(rect, amount, Dqn_RectCutClip_Yes)
|
||||
#define Dqn_Rect_CutTop(rect, amount) Dqn_Rect_CutTopClip(rect, amount, Dqn_RectCutClip_Yes)
|
||||
#define Dqn_Rect_CutBottom(rect, amount) Dqn_Rect_CutBottomClip(rect, amount, Dqn_RectCutClip_Yes)
|
||||
|
||||
#define Dqn_Rect_CutLeftNoClip(rect, amount) Dqn_Rect_CutLeftClip(rect, amount, Dqn_RectCutClip_No)
|
||||
#define Dqn_Rect_CutRightNoClip(rect, amount) Dqn_Rect_CutRightClip(rect, amount, Dqn_RectCutClip_No)
|
||||
#define Dqn_Rect_CutTopNoClip(rect, amount) Dqn_Rect_CutTopClip(rect, amount, Dqn_RectCutClip_No)
|
||||
#define Dqn_Rect_CutBottomNoClip(rect, amount) Dqn_Rect_CutBottomClip(rect, amount, Dqn_RectCutClip_No)
|
||||
|
||||
enum Dqn_RectCutSide
|
||||
{
|
||||
Dqn_RectCutSide_Left,
|
||||
Dqn_RectCutSide_Right,
|
||||
Dqn_RectCutSide_Top,
|
||||
Dqn_RectCutSide_Bottom,
|
||||
};
|
||||
|
||||
struct Dqn_RectCut
|
||||
{
|
||||
Dqn_Rect* rect;
|
||||
Dqn_RectCutSide side;
|
||||
};
|
||||
|
||||
#define Dqn_RectCut_Init(rect, side) DQN_LITERAL(Dqn_RectCut){rect, side}
|
||||
#define Dqn_RectCut_Left(rect) DQN_LITERAL(Dqn_RectCut){rect, Dqn_RectCutSide_Left}
|
||||
#define Dqn_RectCut_Right(rect) DQN_LITERAL(Dqn_RectCut){rect, Dqn_RectCutSide_Right}
|
||||
#define Dqn_RectCut_Top(rect) DQN_LITERAL(Dqn_RectCut){rect, Dqn_RectCutSide_Top}
|
||||
#define Dqn_RectCut_Bottom(rect) DQN_LITERAL(Dqn_RectCut){rect, Dqn_RectCutSide_Bottom}
|
||||
|
||||
DQN_API Dqn_Rect Dqn_RectCut_Cut(Dqn_RectCut rect_cut, Dqn_V2 size, Dqn_RectCutClip clip);
|
||||
#endif // !defined(DQN_NO_RECT)
|
||||
|
||||
// NOTE: [$MATH] Other =============================================================================
|
||||
|
519
dqn_memory.cpp
519
dqn_memory.cpp
@ -1,23 +1,21 @@
|
||||
// NOTE: [$ALLO] Dqn_Allocator =====================================================================
|
||||
DQN_API void *Dqn_Allocator_Alloc_(DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, size_t size, uint8_t align, Dqn_ZeroMem zero_mem)
|
||||
DQN_API void *Dqn_Allocator_Alloc(Dqn_Allocator allocator, size_t size, uint8_t align, Dqn_ZeroMem zero_mem)
|
||||
{
|
||||
void *result = NULL;
|
||||
if (allocator.alloc) {
|
||||
result = allocator.alloc(DQN_LEAK_TRACE_ARG size, align, zero_mem, allocator.user_context);
|
||||
result = allocator.alloc(size, align, zero_mem, allocator.user_context);
|
||||
} else {
|
||||
result = DQN_ALLOC(size);
|
||||
Dqn_Library_LeakTraceAdd(DQN_LEAK_TRACE_ARG result, size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Allocator_Dealloc_(DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, void *ptr, size_t size)
|
||||
DQN_API void Dqn_Allocator_Dealloc(Dqn_Allocator allocator, void *ptr, size_t size)
|
||||
{
|
||||
if (allocator.dealloc) {
|
||||
allocator.dealloc(DQN_LEAK_TRACE_ARG ptr, size, allocator.user_context);
|
||||
allocator.dealloc(ptr, size, allocator.user_context);
|
||||
} else {
|
||||
DQN_DEALLOC(ptr, size);
|
||||
Dqn_Library_LeakTraceMarkFree(DQN_LEAK_TRACE_ARG ptr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,16 +77,22 @@ DQN_API void *Dqn_VMem_Reserve(Dqn_usize size, Dqn_VMemCommit commit, uint32_t p
|
||||
#else
|
||||
#error "Missing implementation for Dqn_VMem_Reserve"
|
||||
#endif
|
||||
|
||||
Dqn_Debug_TrackAlloc(result, size, (page_flags & Dqn_VMemPage_AllocRecordLeakPermitted));
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_VMem_Commit(void *ptr, Dqn_usize size, uint32_t page_flags)
|
||||
{
|
||||
bool result = false;
|
||||
if (!ptr || size == 0)
|
||||
return false;
|
||||
|
||||
unsigned long os_page_flags = Dqn_VMem_ConvertPageToOSFlags_(page_flags);
|
||||
#if defined(DQN_OS_WIN32)
|
||||
bool result = VirtualAlloc(ptr, size, MEM_COMMIT, os_page_flags) != nullptr;
|
||||
result = VirtualAlloc(ptr, size, MEM_COMMIT, os_page_flags) != nullptr;
|
||||
#elif defined(DQN_OS_UNIX)
|
||||
bool result = mprotect(ptr, size, os_page_flags) == 0;
|
||||
result = mprotect(ptr, size, os_page_flags) == 0;
|
||||
#else
|
||||
#error "Missing implementation for Dqn_VMem_Commit"
|
||||
#endif
|
||||
@ -117,6 +121,7 @@ DQN_API void Dqn_VMem_Release(void *ptr, Dqn_usize size)
|
||||
#else
|
||||
#error "Missing implementation for Dqn_VMem_Release"
|
||||
#endif
|
||||
Dqn_Debug_TrackDealloc(ptr);
|
||||
}
|
||||
|
||||
DQN_API int Dqn_VMem_Protect(void *ptr, Dqn_usize size, uint32_t page_flags)
|
||||
@ -127,8 +132,8 @@ DQN_API int Dqn_VMem_Protect(void *ptr, Dqn_usize size, uint32_t page_flags)
|
||||
static Dqn_String8 const ALIGNMENT_ERROR_MSG =
|
||||
DQN_STRING8("Page protection requires pointers to be page aligned because we "
|
||||
"can only guard memory at a multiple of the page boundary.");
|
||||
DQN_ASSERTF(Dqn_IsPowerOfTwoAligned(DQN_CAST(uintptr_t)ptr, DQN_VMEM_PAGE_GRANULARITY), "%s", ALIGNMENT_ERROR_MSG.data);
|
||||
DQN_ASSERTF(Dqn_IsPowerOfTwoAligned(size, DQN_VMEM_PAGE_GRANULARITY), "%s", ALIGNMENT_ERROR_MSG.data);
|
||||
DQN_ASSERTF(Dqn_IsPowerOfTwoAligned(DQN_CAST(uintptr_t)ptr, g_dqn_library->os_page_size), "%s", ALIGNMENT_ERROR_MSG.data);
|
||||
DQN_ASSERTF(Dqn_IsPowerOfTwoAligned(size, g_dqn_library->os_page_size), "%s", ALIGNMENT_ERROR_MSG.data);
|
||||
|
||||
unsigned long os_page_flags = Dqn_VMem_ConvertPageToOSFlags_(page_flags);
|
||||
#if defined(DQN_OS_WIN32)
|
||||
@ -136,8 +141,12 @@ DQN_API int Dqn_VMem_Protect(void *ptr, Dqn_usize size, uint32_t page_flags)
|
||||
int result = VirtualProtect(ptr, size, os_page_flags, &prev_flags);
|
||||
(void)prev_flags;
|
||||
if (result == 0) {
|
||||
#if defined(DQN_NO_WIN)
|
||||
DQN_ASSERTF(result, "VirtualProtect failed");
|
||||
#else
|
||||
Dqn_WinErrorMsg error = Dqn_Win_LastError();
|
||||
DQN_ASSERTF(result, "VirtualProtect failed (%d): %.*s", error.code, error.size, error.data);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
int result = mprotect(result->memory, result->size, os_page_flags);
|
||||
@ -147,112 +156,107 @@ DQN_API int Dqn_VMem_Protect(void *ptr, Dqn_usize size, uint32_t page_flags)
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$AREN] Dqn_Arena =========================================================================
|
||||
DQN_API void Dqn_Arena_CommitFromBlock(Dqn_ArenaBlock *block, Dqn_usize size, Dqn_ArenaCommit commit)
|
||||
// NOTE: [$MEMB] Dqn_MemBlock ======================================================================
|
||||
DQN_API Dqn_usize Dqn_MemBlock_MetadataSize(uint8_t flags)
|
||||
{
|
||||
Dqn_usize commit_size = 0;
|
||||
switch (commit) {
|
||||
case Dqn_ArenaCommit_GetNewPages: {
|
||||
commit_size = Dqn_PowerOfTwoAlign(size, DQN_VMEM_COMMIT_GRANULARITY);
|
||||
} break;
|
||||
|
||||
case Dqn_ArenaCommit_EnsureSpace: {
|
||||
DQN_ASSERT(block->commit >= block->used);
|
||||
Dqn_usize const unused_commit_space = block->commit - block->used;
|
||||
if (unused_commit_space < size)
|
||||
commit_size = Dqn_PowerOfTwoAlign(size - unused_commit_space, DQN_VMEM_COMMIT_GRANULARITY);
|
||||
} break;
|
||||
}
|
||||
|
||||
if (commit_size) {
|
||||
uint32_t page_flags = Dqn_VMemPage_ReadWrite;
|
||||
char *commit_ptr = DQN_CAST(char *)block->memory + block->commit;
|
||||
Dqn_VMem_Commit(commit_ptr, commit_size, page_flags);
|
||||
block->commit += commit_size;
|
||||
|
||||
DQN_ASSERTF(block->commit < block->size,
|
||||
"Block size should be PoT aligned and its alignment should be greater than the commit granularity [block_size=%_$$d, block_commit=%_$$d]",
|
||||
block->size, block->commit);
|
||||
|
||||
// NOTE: Guard new pages being allocated from the block
|
||||
if (block->arena->use_after_free_guard)
|
||||
Dqn_VMem_Protect(commit_ptr, commit_size, page_flags | Dqn_VMemPage_Guard);
|
||||
}
|
||||
Dqn_usize result = sizeof(Dqn_MemBlock);
|
||||
if (flags & Dqn_MemBlockFlag_PageGuard)
|
||||
result = g_dqn_library->os_page_size;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void *Dqn_Arena_AllocateFromBlock(Dqn_ArenaBlock *block, Dqn_usize size, uint8_t align, Dqn_ZeroMem zero_mem)
|
||||
DQN_API Dqn_MemBlock *Dqn_MemBlock_Init(Dqn_usize reserve, Dqn_usize commit, uint8_t flags)
|
||||
{
|
||||
DQN_ASSERTF(g_dqn_library->os_page_size, "Library needs to be initialised by called Dqn_Library_Init()");
|
||||
DQN_ASSERTF(Dqn_IsPowerOfTwo(g_dqn_library->os_page_size), "Invalid page size");
|
||||
DQN_ASSERTF((flags & ~Dqn_MemBlockFlag_All) == 0, "Invalid flag combination, must adhere to Dqn_MemBlockFlags");
|
||||
|
||||
if (reserve == 0)
|
||||
return nullptr;
|
||||
|
||||
Dqn_usize metadata_size = Dqn_MemBlock_MetadataSize(flags);
|
||||
Dqn_usize reserve_aligned = Dqn_AlignUpPowerOfTwo(reserve + metadata_size, g_dqn_library->os_page_size);
|
||||
Dqn_usize commit_aligned = Dqn_AlignUpPowerOfTwo(commit + metadata_size, g_dqn_library->os_page_size);
|
||||
commit_aligned = DQN_MIN(commit_aligned, reserve_aligned);
|
||||
|
||||
// NOTE: Avoid 1 syscall by committing on reserve if amounts are equal
|
||||
Dqn_VMemCommit commit_on_reserve = commit_aligned == reserve_aligned ? Dqn_VMemCommit_Yes : Dqn_VMemCommit_No;
|
||||
auto *result = DQN_CAST(Dqn_MemBlock *)Dqn_VMem_Reserve(reserve_aligned, commit_on_reserve, Dqn_VMemPage_ReadWrite);
|
||||
if (result) {
|
||||
// NOTE: Commit pages if we did not commit on the initial range.
|
||||
if (!commit_on_reserve)
|
||||
Dqn_VMem_Commit(result, commit_aligned, Dqn_VMemPage_ReadWrite);
|
||||
|
||||
result->data = DQN_CAST(uint8_t *)result + metadata_size;
|
||||
result->size = reserve_aligned - metadata_size;
|
||||
result->commit = commit_aligned - metadata_size;
|
||||
result->flags = flags;
|
||||
|
||||
// NOTE: Guard pages
|
||||
if (flags & Dqn_MemBlockFlag_PageGuard)
|
||||
Dqn_VMem_Protect(result->data, commit_aligned, Dqn_VMemPage_ReadWrite | Dqn_VMemPage_Guard);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void *Dqn_MemBlock_Alloc(Dqn_MemBlock *block, Dqn_usize size, uint8_t alignment, Dqn_ZeroMem zero_mem)
|
||||
{
|
||||
void *result = nullptr;
|
||||
if (!block)
|
||||
return nullptr;
|
||||
return result;
|
||||
|
||||
DQN_ASSERTF((align & (align - 1)) == 0, "Power of two alignment required");
|
||||
align = DQN_MAX(align, 1);
|
||||
DQN_ASSERT(Dqn_IsPowerOfTwo(alignment));
|
||||
Dqn_usize aligned_used = Dqn_AlignUpPowerOfTwo(block->used, alignment);
|
||||
Dqn_usize new_used = aligned_used + size;
|
||||
if (new_used > block->size)
|
||||
return result;
|
||||
|
||||
DQN_ASSERT(block->hwm_used <= block->commit);
|
||||
DQN_ASSERT(block->hwm_used >= block->used);
|
||||
DQN_ASSERTF(block->commit >= block->used,
|
||||
"Internal error: Committed size must always be greater than the used size [commit=%_$$zd, used=%_$$zd]",
|
||||
block->commit, block->used);
|
||||
result = DQN_CAST(char *)block->data + aligned_used;
|
||||
block->used = new_used;
|
||||
|
||||
// NOTE: Calculate how much we need to pad the next pointer to divvy out
|
||||
// (only if it is unaligned)
|
||||
uintptr_t next_ptr = DQN_CAST(uintptr_t)block->memory + block->used;
|
||||
Dqn_usize align_offset = 0;
|
||||
if (next_ptr & (align - 1))
|
||||
align_offset = (align - (next_ptr & (align - 1)));
|
||||
|
||||
Dqn_usize allocation_size = size + align_offset;
|
||||
if ((block->used + allocation_size) > block->size)
|
||||
return nullptr;
|
||||
|
||||
void *result = DQN_CAST(char *)next_ptr + align_offset;
|
||||
if (zero_mem == Dqn_ZeroMem_Yes) {
|
||||
// NOTE: Newly commit pages are always 0-ed out, we only need to
|
||||
// memset the memory that are being reused.
|
||||
Dqn_usize const reused_bytes = DQN_MIN(block->hwm_used - block->used, allocation_size);
|
||||
DQN_MEMSET(DQN_CAST(void *)next_ptr, DQN_MEMSET_BYTE, reused_bytes);
|
||||
Dqn_usize reused_bytes = DQN_MIN(block->commit - aligned_used, size);
|
||||
DQN_MEMSET(result, DQN_MEMSET_BYTE, reused_bytes);
|
||||
}
|
||||
|
||||
// NOTE: Ensure requested bytes are backed by physical pages from the OS
|
||||
Dqn_Arena_CommitFromBlock(block, allocation_size, Dqn_ArenaCommit_EnsureSpace);
|
||||
if (block->commit < block->used) {
|
||||
Dqn_usize commit_size = Dqn_AlignUpPowerOfTwo(block->used - block->commit, g_dqn_library->os_page_size);
|
||||
void *commit_ptr = (void *)Dqn_AlignUpPowerOfTwo((char *)block->data + block->commit, g_dqn_library->os_page_size);
|
||||
block->commit += commit_size - Dqn_MemBlock_MetadataSize(block->flags);
|
||||
Dqn_VMem_Commit(commit_ptr, commit_size, Dqn_VMemPage_ReadWrite);
|
||||
DQN_ASSERT(block->commit <= block->size);
|
||||
}
|
||||
|
||||
// NOTE: Unlock the pages requested
|
||||
if (block->arena->use_after_free_guard)
|
||||
Dqn_VMem_Protect(result, allocation_size, Dqn_VMemPage_ReadWrite);
|
||||
|
||||
// NOTE: Update arena
|
||||
Dqn_Arena *arena = block->arena;
|
||||
arena->stats.used += allocation_size;
|
||||
arena->stats.used_hwm = DQN_MAX(arena->stats.used_hwm, arena->stats.used);
|
||||
block->used += allocation_size;
|
||||
block->hwm_used = DQN_MAX(block->hwm_used, block->used);
|
||||
|
||||
DQN_ASSERTF(block->used <= block->commit, "Internal error: Committed size must be greater than used size [used=%_$$zd, commit=%_$$zd]", block->used, block->commit);
|
||||
DQN_ASSERTF(block->commit <= block->size, "Internal error: Allocation exceeded block capacity [commit=%_$$zd, size=%_$$zd]", block->commit, block->size);
|
||||
DQN_ASSERTF(((DQN_CAST(uintptr_t)result) & (align - 1)) == 0, "Internal error: Pointer alignment failed [address=%p, align=%x]", result, align);
|
||||
if (block->flags & Dqn_MemBlockFlag_PageGuard)
|
||||
Dqn_VMem_Protect(result, size, Dqn_VMemPage_ReadWrite);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE void *Dqn_Arena_AllocatorAlloc_(DQN_LEAK_TRACE_FUNCTION size_t size, uint8_t align, Dqn_ZeroMem zero_mem, void *user_context)
|
||||
DQN_API void Dqn_MemBlock_Free(Dqn_MemBlock *block)
|
||||
{
|
||||
if (block) {
|
||||
Dqn_usize release_size = block->size + Dqn_MemBlock_MetadataSize(block->flags);
|
||||
Dqn_VMem_Release(block, release_size);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: [$AREN] Dqn_Arena =========================================================================
|
||||
DQN_FILE_SCOPE void *Dqn_Arena_AllocatorAlloc(size_t size, uint8_t align, Dqn_ZeroMem zero_mem, void *user_context)
|
||||
{
|
||||
void *result = NULL;
|
||||
if (!user_context)
|
||||
return result;
|
||||
|
||||
Dqn_Arena *arena = DQN_CAST(Dqn_Arena *)user_context;
|
||||
result = Dqn_Arena_Allocate_(DQN_LEAK_TRACE_ARG arena, size, align, zero_mem);
|
||||
result = Dqn_Arena_Alloc(arena, size, align, zero_mem);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE void Dqn_Arena_AllocatorDealloc_(DQN_LEAK_TRACE_FUNCTION void *ptr, size_t size, void *user_context)
|
||||
DQN_FILE_SCOPE void Dqn_Arena_AllocatorDealloc(void *, size_t, void *)
|
||||
{
|
||||
// NOTE: No-op, arenas batch allocate and batch deallocate. Call free on the
|
||||
// underlying arena, since we can't free individual pointers.
|
||||
DQN_LEAK_TRACE_UNUSED;
|
||||
(void)ptr;
|
||||
(void)size;
|
||||
(void)user_context;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Allocator Dqn_Arena_Allocator(Dqn_Arena *arena)
|
||||
@ -260,151 +264,155 @@ DQN_API Dqn_Allocator Dqn_Arena_Allocator(Dqn_Arena *arena)
|
||||
Dqn_Allocator result = {};
|
||||
if (arena) {
|
||||
result.user_context = arena;
|
||||
result.alloc = Dqn_Arena_AllocatorAlloc_;
|
||||
result.dealloc = Dqn_Arena_AllocatorDealloc_;
|
||||
result.alloc = Dqn_Arena_AllocatorAlloc;
|
||||
result.dealloc = Dqn_Arena_AllocatorDealloc;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
struct Dqn_ArenaBlockResetInfo_
|
||||
DQN_API Dqn_MemBlock *Dqn_Arena_Grow(Dqn_Arena *arena, Dqn_usize reserve, Dqn_usize commit, uint8_t flags)
|
||||
{
|
||||
bool free_memory;
|
||||
Dqn_usize used_value;
|
||||
};
|
||||
if (!arena)
|
||||
return nullptr;
|
||||
|
||||
// Calculate the size in bytes required to allocate the memory for the block
|
||||
// (*not* including the memory required for the user in the block!)
|
||||
#define Dqn_Arena_BlockMetadataSize_(arena) ((arena)->use_after_free_guard ? (Dqn_PowerOfTwoAlign(sizeof(Dqn_ArenaBlock), DQN_VMEM_PAGE_GRANULARITY)) : sizeof(Dqn_ArenaBlock))
|
||||
uint8_t mem_block_flags = flags;
|
||||
if (arena->allocs_are_allowed_to_leak)
|
||||
mem_block_flags |= Dqn_MemBlockFlag_AllocRecordLeakPermitted;
|
||||
|
||||
DQN_API void Dqn_Arena_BlockReset_(DQN_LEAK_TRACE_FUNCTION Dqn_ArenaBlock *block, Dqn_ZeroMem zero_mem, Dqn_ArenaBlockResetInfo_ reset_info)
|
||||
{
|
||||
if (!block)
|
||||
return;
|
||||
Dqn_MemBlock *result = Dqn_MemBlock_Init(reserve, commit, mem_block_flags);
|
||||
if (result) {
|
||||
if (!arena->head)
|
||||
arena->head = result;
|
||||
|
||||
if (zero_mem == Dqn_ZeroMem_Yes)
|
||||
DQN_MEMSET(block->memory, DQN_MEMSET_BYTE, block->commit);
|
||||
if (arena->tail)
|
||||
arena->tail->next = result;
|
||||
|
||||
if (reset_info.free_memory) {
|
||||
Dqn_usize block_metadata_size = Dqn_Arena_BlockMetadataSize_(block->arena);
|
||||
Dqn_VMem_Release(block, block_metadata_size + block->size);
|
||||
Dqn_Library_LeakTraceMarkFree(DQN_LEAK_TRACE_ARG block);
|
||||
} else {
|
||||
if (block->arena->use_after_free_guard) {
|
||||
DQN_ASSERTF(
|
||||
block->flags & Dqn_ArenaBlockFlags_UseAfterFreeGuard,
|
||||
"Arena has set use-after-free guard but the memory it uses was not initialised "
|
||||
"with use-after-free semantics. You might have set the arena use-after-free "
|
||||
"flag after it has already done an allocation which is not valid. Make sure "
|
||||
"you set the flag first before the arena is used.");
|
||||
} else {
|
||||
DQN_ASSERTF(
|
||||
(block->flags & Dqn_ArenaBlockFlags_UseAfterFreeGuard) == 0,
|
||||
"The arena's memory block has the use-after-free guard set but the arena does "
|
||||
"not have the use-after-free flag set. This is not valid, a block that has "
|
||||
"use-after-free semantics was allocated from an arena with the equivalent flag set "
|
||||
"and for the lifetime of the block the owning arena must also have the same flag "
|
||||
"set for correct behaviour. Make sure you do not unset the flag on the arena "
|
||||
"whilst it still has memory blocks that it owns.");
|
||||
}
|
||||
if (!arena->curr)
|
||||
arena->curr = result;
|
||||
|
||||
block->used = reset_info.used_value;
|
||||
// NOTE: Guard all the committed pages again
|
||||
if (block->arena->use_after_free_guard)
|
||||
Dqn_VMem_Protect(block->memory, block->commit, Dqn_VMemPage_ReadWrite | Dqn_VMemPage_Guard);
|
||||
result->prev = arena->tail;
|
||||
arena->tail = result;
|
||||
arena->blocks += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Arena_Reset(Dqn_Arena *arena, Dqn_ZeroMem zero_mem)
|
||||
DQN_API void *Dqn_Arena_Alloc(Dqn_Arena *arena, Dqn_usize size, uint8_t align, Dqn_ZeroMem zero_mem)
|
||||
{
|
||||
DQN_ASSERT(Dqn_IsPowerOfTwo(align));
|
||||
|
||||
void *result = nullptr;
|
||||
if (!arena || size == 0 || align == 0)
|
||||
return result;
|
||||
|
||||
for (;;) {
|
||||
while (arena->curr && (arena->curr->flags & Dqn_MemBlockFlag_ArenaPrivate))
|
||||
arena->curr = arena->curr->next;
|
||||
|
||||
if (!arena->curr) {
|
||||
if (!Dqn_Arena_Grow(arena, size, size /*commit*/, 0 /*flags*/))
|
||||
return result;
|
||||
}
|
||||
|
||||
result = Dqn_MemBlock_Alloc(arena->curr, size, align, zero_mem);
|
||||
if (result)
|
||||
break;
|
||||
|
||||
arena->curr = arena->curr->next;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void *Dqn_Arena_Copy(Dqn_Arena *arena, void *src, Dqn_usize size, uint8_t align)
|
||||
{
|
||||
void *result = Dqn_Arena_Alloc(arena, size, align, Dqn_ZeroMem_No);
|
||||
DQN_MEMCPY(result, src, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void *Dqn_Arena_CopyZ(Dqn_Arena *arena, void *src, Dqn_usize size, uint8_t align)
|
||||
{
|
||||
void *result = Dqn_Arena_Alloc(arena, size + 1, align, Dqn_ZeroMem_No);
|
||||
DQN_MEMCPY(result, src, size);
|
||||
(DQN_CAST(char *)result)[size] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Arena_Free(Dqn_Arena *arena)
|
||||
{
|
||||
if (!arena)
|
||||
return;
|
||||
|
||||
// NOTE: Zero all the blocks until we reach the first block in the list
|
||||
for (Dqn_ArenaBlock *block = arena->head; block; block = block->next) {
|
||||
Dqn_ArenaBlockResetInfo_ reset_info = {};
|
||||
Dqn_Arena_BlockReset_(DQN_LEAK_TRACE block, zero_mem, reset_info);
|
||||
for (Dqn_MemBlock *block = arena->head; block; ) {
|
||||
Dqn_MemBlock *next = block->next;
|
||||
Dqn_MemBlock_Free(block);
|
||||
block = next;
|
||||
}
|
||||
|
||||
arena->curr = arena->head;
|
||||
arena->stats.used = 0;
|
||||
arena->stats.wasted = 0;
|
||||
arena->curr = arena->head = arena->tail = nullptr;
|
||||
arena->blocks = 0;
|
||||
}
|
||||
|
||||
DQN_API Dqn_ArenaTempMemory Dqn_Arena_BeginTempMemory(Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_ArenaTempMemory result = {};
|
||||
if (arena) {
|
||||
arena->temp_memory_count++;
|
||||
result.arena = arena;
|
||||
result.head = arena->head;
|
||||
result.curr = arena->curr;
|
||||
result.tail = arena->tail;
|
||||
result.curr_used = (arena->curr) ? arena->curr->used : 0;
|
||||
result.stats = arena->stats;
|
||||
result.blocks = arena->blocks;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Arena_EndTempMemory_(DQN_LEAK_TRACE_FUNCTION Dqn_ArenaTempMemory scope)
|
||||
DQN_API void Dqn_Arena_EndTempMemory(Dqn_ArenaTempMemory temp_memory, bool cancel)
|
||||
{
|
||||
if (!scope.arena)
|
||||
if (cancel || !temp_memory.arena)
|
||||
return;
|
||||
|
||||
Dqn_Arena *arena = scope.arena;
|
||||
if (!DQN_CHECKF(arena->temp_memory_count > 0, "End temp memory has been called without a matching begin pair on the arena"))
|
||||
// NOTE: The arena has been freed or invalidly manipulated (e.g. freed)
|
||||
// since the temp memory started as the head cannot change once it is
|
||||
// captured in the temp memory.
|
||||
Dqn_Arena *arena = temp_memory.arena;
|
||||
if (arena->head != temp_memory.head)
|
||||
return;
|
||||
|
||||
// NOTE: Revert arena stats
|
||||
arena->temp_memory_count--;
|
||||
arena->stats.capacity = scope.stats.capacity;
|
||||
arena->stats.used = scope.stats.used;
|
||||
arena->stats.wasted = scope.stats.wasted;
|
||||
arena->stats.blocks = scope.stats.blocks;
|
||||
|
||||
// NOTE: Revert the current block to the scope's current block
|
||||
arena->head = scope.head;
|
||||
arena->curr = scope.curr;
|
||||
// NOTE: Revert the current block to the temp_memory's current block
|
||||
arena->blocks = temp_memory.blocks;
|
||||
arena->head = temp_memory.head;
|
||||
arena->curr = temp_memory.curr;
|
||||
if (arena->curr) {
|
||||
Dqn_ArenaBlock *curr = arena->curr;
|
||||
Dqn_ArenaBlockResetInfo_ reset_info = {};
|
||||
reset_info.used_value = scope.curr_used;
|
||||
Dqn_Arena_BlockReset_(DQN_LEAK_TRACE_ARG curr, Dqn_ZeroMem_No, reset_info);
|
||||
Dqn_MemBlock *curr = arena->curr;
|
||||
curr->used = temp_memory.curr_used;
|
||||
}
|
||||
|
||||
// NOTE: Free the tail blocks until we reach the scope's tail block
|
||||
while (arena->tail != scope.tail) {
|
||||
Dqn_ArenaBlock *tail = arena->tail;
|
||||
arena->tail = tail->prev;
|
||||
Dqn_ArenaBlockResetInfo_ reset_info = {};
|
||||
reset_info.free_memory = true;
|
||||
Dqn_Arena_BlockReset_(DQN_LEAK_TRACE_ARG tail, Dqn_ZeroMem_No, reset_info);
|
||||
// NOTE: Free the tail blocks until we reach the temp_memory's tail block
|
||||
while (arena->tail != temp_memory.tail) {
|
||||
Dqn_MemBlock *tail = arena->tail;
|
||||
arena->tail = tail->prev;
|
||||
Dqn_MemBlock_Free(tail);
|
||||
}
|
||||
|
||||
// NOTE: Chop the restored tail link
|
||||
if (arena->tail)
|
||||
arena->tail->next = nullptr;
|
||||
|
||||
// NOTE: Reset the usage of all the blocks between the tail and current block's
|
||||
if (arena->tail) {
|
||||
arena->tail->next = nullptr;
|
||||
for (Dqn_ArenaBlock *block = arena->tail; block && block != arena->curr; block = block->prev) {
|
||||
Dqn_ArenaBlockResetInfo_ reset_info = {};
|
||||
Dqn_Arena_BlockReset_(DQN_LEAK_TRACE_ARG block, Dqn_ZeroMem_No, reset_info);
|
||||
}
|
||||
}
|
||||
for (Dqn_MemBlock *block = arena->tail; block != arena->curr; block = block->prev)
|
||||
block->used = 0;
|
||||
}
|
||||
|
||||
Dqn_ArenaTempMemoryScope_::Dqn_ArenaTempMemoryScope_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena)
|
||||
Dqn_ArenaTempMemoryScope::Dqn_ArenaTempMemoryScope(Dqn_Arena *arena)
|
||||
{
|
||||
temp_memory = Dqn_Arena_BeginTempMemory(arena);
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
leak_site__ = DQN_LEAK_TRACE_ARG_NO_COMMA;
|
||||
#endif
|
||||
}
|
||||
|
||||
Dqn_ArenaTempMemoryScope_::~Dqn_ArenaTempMemoryScope_()
|
||||
Dqn_ArenaTempMemoryScope::~Dqn_ArenaTempMemoryScope()
|
||||
{
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
Dqn_Arena_EndTempMemory_(leak_site__, temp_memory);
|
||||
#else
|
||||
Dqn_Arena_EndTempMemory_(temp_memory);
|
||||
#endif
|
||||
Dqn_Arena_EndTempMemory(temp_memory, cancel);
|
||||
}
|
||||
|
||||
DQN_API Dqn_ArenaStatString Dqn_Arena_StatString(Dqn_ArenaStat const *stat)
|
||||
@ -447,155 +455,6 @@ DQN_API Dqn_ArenaStatString Dqn_Arena_StatString(Dqn_ArenaStat const *stat)
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Arena_LogStats(Dqn_Arena const *arena)
|
||||
{
|
||||
Dqn_ArenaStatString string = Dqn_Arena_StatString(&arena->stats);
|
||||
Dqn_Log_InfoF("%.*s\n", DQN_STRING_FMT(string));
|
||||
}
|
||||
|
||||
DQN_API Dqn_ArenaBlock *Dqn_Arena_Grow_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, Dqn_usize size, Dqn_usize commit, uint8_t flags)
|
||||
{
|
||||
DQN_ASSERT(commit <= size);
|
||||
if (!arena || size == 0)
|
||||
return nullptr;
|
||||
|
||||
Dqn_usize block_metadata_size = Dqn_Arena_BlockMetadataSize_(arena);
|
||||
commit = DQN_MIN(commit, size);
|
||||
Dqn_usize reserve_aligned = Dqn_PowerOfTwoAlign(size + block_metadata_size, DQN_VMEM_RESERVE_GRANULARITY);
|
||||
Dqn_usize commit_aligned = Dqn_PowerOfTwoAlign(commit + block_metadata_size, DQN_VMEM_COMMIT_GRANULARITY);
|
||||
DQN_ASSERT(commit_aligned < reserve_aligned);
|
||||
|
||||
// NOTE: If the commit amount is the same as reserve size we can save one
|
||||
// syscall by asking the OS to reserve+commit in the same call.
|
||||
Dqn_VMemCommit commit_on_reserve = reserve_aligned == commit_aligned ? Dqn_VMemCommit_Yes : Dqn_VMemCommit_No;
|
||||
uint32_t page_flags = Dqn_VMemPage_ReadWrite;
|
||||
auto *result = DQN_CAST(Dqn_ArenaBlock *)Dqn_VMem_Reserve(reserve_aligned, commit_on_reserve, page_flags);
|
||||
if (result) {
|
||||
// NOTE: Commit the amount requested by the user if we did not commit
|
||||
// on reserve the initial range.
|
||||
if (commit_on_reserve == Dqn_VMemCommit_No) {
|
||||
Dqn_VMem_Commit(result, commit_aligned, page_flags);
|
||||
} else {
|
||||
DQN_ASSERT(commit_aligned == reserve_aligned);
|
||||
}
|
||||
|
||||
// NOTE: Sanity check memory is zero-ed out
|
||||
DQN_ASSERT(result->used == 0);
|
||||
DQN_ASSERT(result->next == nullptr);
|
||||
DQN_ASSERT(result->prev == nullptr);
|
||||
|
||||
// NOTE: Setup the block
|
||||
result->memory = DQN_CAST(uint8_t *)result + block_metadata_size;
|
||||
result->size = reserve_aligned - block_metadata_size;
|
||||
result->commit = commit_aligned - block_metadata_size;
|
||||
result->flags = flags;
|
||||
result->arena = arena;
|
||||
|
||||
if (arena->use_after_free_guard)
|
||||
result->flags |= Dqn_ArenaBlockFlags_UseAfterFreeGuard;
|
||||
|
||||
// NOTE: Reset the block (this will guard the memory pages if required, otherwise no-op).
|
||||
Dqn_ArenaBlockResetInfo_ reset_info = {};
|
||||
Dqn_Arena_BlockReset_(DQN_LEAK_TRACE_ARG result, Dqn_ZeroMem_No, reset_info);
|
||||
|
||||
// NOTE: Attach the block to the arena
|
||||
if (arena->tail) {
|
||||
arena->tail->next = result;
|
||||
result->prev = arena->tail;
|
||||
} else {
|
||||
DQN_ASSERT(!arena->curr);
|
||||
arena->curr = result;
|
||||
arena->head = result;
|
||||
}
|
||||
arena->tail = result;
|
||||
|
||||
// NOTE: Update stats
|
||||
arena->stats.syscalls += (commit_on_reserve ? 1 : 2);
|
||||
arena->stats.blocks += 1;
|
||||
arena->stats.capacity += arena->curr->size;
|
||||
|
||||
arena->stats.blocks_hwm = DQN_MAX(arena->stats.blocks_hwm, arena->stats.blocks);
|
||||
arena->stats.capacity_hwm = DQN_MAX(arena->stats.capacity_hwm, arena->stats.capacity);
|
||||
|
||||
Dqn_Library_LeakTraceAdd(DQN_LEAK_TRACE_ARG result, size);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void *Dqn_Arena_Allocate_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, Dqn_usize size, uint8_t align, Dqn_ZeroMem zero_mem)
|
||||
{
|
||||
DQN_ASSERTF((align & (align - 1)) == 0, "Power of two alignment required");
|
||||
align = DQN_MAX(align, 1);
|
||||
|
||||
if (arena->use_after_free_guard) {
|
||||
size = Dqn_PowerOfTwoAlign(size, DQN_VMEM_PAGE_GRANULARITY);
|
||||
align = 1;
|
||||
}
|
||||
|
||||
void *result = nullptr;
|
||||
for (; !result;) {
|
||||
while (arena->curr && (arena->curr->flags & Dqn_ArenaBlockFlags_Private))
|
||||
arena->curr = arena->curr->next;
|
||||
|
||||
result = Dqn_Arena_AllocateFromBlock(arena->curr, size, align, zero_mem);
|
||||
if (!result) {
|
||||
if (!arena->curr || arena->curr == arena->tail) {
|
||||
Dqn_usize allocation_size = size + (align - 1);
|
||||
if (!Dqn_Arena_Grow(DQN_LEAK_TRACE_ARG arena, allocation_size, allocation_size /*commit*/, 0 /*flags*/)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (arena->curr != arena->tail)
|
||||
arena->curr = arena->curr->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (result)
|
||||
DQN_ASSERT((arena->curr->flags & Dqn_ArenaBlockFlags_Private) == 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void *Dqn_Arena_Copy_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, void *src, Dqn_usize size, uint8_t align)
|
||||
{
|
||||
void *result = Dqn_Arena_Allocate_(DQN_LEAK_TRACE_ARG arena, size, align, Dqn_ZeroMem_No);
|
||||
DQN_MEMCPY(result, src, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void *Dqn_Arena_CopyZ_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, void *src, Dqn_usize size, uint8_t align)
|
||||
{
|
||||
void *result = Dqn_Arena_Allocate_(DQN_LEAK_TRACE_ARG arena, size + 1, align, Dqn_ZeroMem_No);
|
||||
DQN_MEMCPY(result, src, size);
|
||||
(DQN_CAST(char *)result)[size] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Arena_Free_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, Dqn_ZeroMem zero_mem)
|
||||
{
|
||||
if (!arena)
|
||||
return;
|
||||
|
||||
if (!DQN_CHECKF(arena->temp_memory_count == 0, "You cannot free an arena whilst in an temp memory region"))
|
||||
return;
|
||||
|
||||
while (arena->tail) {
|
||||
Dqn_ArenaBlock *block = arena->tail;
|
||||
arena->tail = block->prev;
|
||||
Dqn_ArenaBlockResetInfo_ reset_info = {};
|
||||
reset_info.free_memory = true;
|
||||
Dqn_Arena_BlockReset_(DQN_LEAK_TRACE_ARG block, zero_mem, reset_info);
|
||||
}
|
||||
|
||||
arena->curr = arena->tail = nullptr;
|
||||
arena->stats.capacity = 0;
|
||||
arena->stats.used = 0;
|
||||
arena->stats.wasted = 0;
|
||||
arena->stats.blocks = 0;
|
||||
}
|
||||
|
||||
// NOTE: [$ACAT] Dqn_ArenaCatalog ==================================================================
|
||||
DQN_API void Dqn_ArenaCatalog_Init(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena)
|
||||
{
|
||||
|
205
dqn_memory.h
205
dqn_memory.h
@ -1,46 +1,6 @@
|
||||
// NOTE: Table Of Contents =========================================================================
|
||||
// Index | Disable #define | Description
|
||||
// =================================================================================================
|
||||
// [$ALLO] Dqn_Allocator | | Generic allocator interface
|
||||
// [$VMEM] Dqn_VMem | | Virtual memory allocation
|
||||
// [$AREN] Dqn_Arena | | Growing bump allocator
|
||||
// [$ACAT] Dqn_ArenaCatalog | | Collate, create & manage arenas in a catalog
|
||||
// =================================================================================================
|
||||
|
||||
// NOTE: [$ALLO] Dqn_Allocator =====================================================================
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
#if defined(DQN_NO_DSMAP)
|
||||
#error "DSMap is required for allocation tracing"
|
||||
#endif
|
||||
#define DQN_LEAK_TRACE DQN_CALL_SITE,
|
||||
#define DQN_LEAK_TRACE_NO_COMMA DQN_CALL_SITE
|
||||
#define DQN_LEAK_TRACE_FUNCTION Dqn_CallSite leak_site_,
|
||||
#define DQN_LEAK_TRACE_FUNCTION_NO_COMMA Dqn_CallSite leak_site_
|
||||
#define DQN_LEAK_TRACE_ARG leak_site_,
|
||||
#define DQN_LEAK_TRACE_ARG_NO_COMMA leak_site_
|
||||
#define DQN_LEAK_TRACE_UNUSED (void)leak_site_;
|
||||
#else
|
||||
#define DQN_LEAK_TRACE
|
||||
#define DQN_LEAK_TRACE_NO_COMMA
|
||||
#define DQN_LEAK_TRACE_FUNCTION
|
||||
#define DQN_LEAK_TRACE_FUNCTION_NO_COMMA
|
||||
#define DQN_LEAK_TRACE_ARG
|
||||
#define DQN_LEAK_TRACE_ARG_NO_COMMA
|
||||
#define DQN_LEAK_TRACE_UNUSED
|
||||
#endif
|
||||
|
||||
struct Dqn_LeakTrace
|
||||
{
|
||||
void *ptr; // The pointer we are tracking
|
||||
Dqn_usize size; // Size of the allocation
|
||||
Dqn_CallSite call_site; // Call site where the allocation was allocated
|
||||
bool freed; // True if this pointer has been freed
|
||||
Dqn_usize freed_size; // Size of the allocation that has been freed
|
||||
Dqn_CallSite freed_call_site; // Call site where the allocation was freed
|
||||
};
|
||||
|
||||
typedef void *Dqn_Allocator_AllocProc(DQN_LEAK_TRACE_FUNCTION size_t size, uint8_t align, Dqn_ZeroMem zero_mem, void *user_context);
|
||||
typedef void Dqn_Allocator_DeallocProc(DQN_LEAK_TRACE_FUNCTION void *ptr, size_t size, void *user_context);
|
||||
typedef void *Dqn_Allocator_AllocProc(size_t size, uint8_t align, Dqn_ZeroMem zero_mem, void *user_context);
|
||||
typedef void Dqn_Allocator_DeallocProc(void *ptr, size_t size, void *user_context);
|
||||
|
||||
struct Dqn_Allocator
|
||||
{
|
||||
@ -49,15 +9,13 @@ struct Dqn_Allocator
|
||||
Dqn_Allocator_DeallocProc *dealloc; // Memory deallocating routine
|
||||
};
|
||||
|
||||
// NOTE: Macros ==================================================================================
|
||||
#define Dqn_Allocator_Alloc(allocator, size, align, zero_mem) Dqn_Allocator_Alloc_(DQN_LEAK_TRACE allocator, size, align, zero_mem)
|
||||
#define Dqn_Allocator_Dealloc(allocator, ptr, size) Dqn_Allocator_Dealloc_(DQN_LEAK_TRACE allocator, ptr, size)
|
||||
#define Dqn_Allocator_NewArray(allocator, Type, count, zero_mem) (Type *)Dqn_Allocator_Alloc_(DQN_LEAK_TRACE allocator, sizeof(Type) * count, alignof(Type), zero_mem)
|
||||
#define Dqn_Allocator_New(allocator, Type, zero_mem) (Type *)Dqn_Allocator_Alloc_(DQN_LEAK_TRACE allocator, sizeof(Type), alignof(Type), zero_mem)
|
||||
// NOTE: Macros ====================================================================================
|
||||
#define Dqn_Allocator_NewArray(allocator, Type, count, zero_mem) (Type *)Dqn_Allocator_Alloc(allocator, sizeof(Type) * count, alignof(Type), zero_mem)
|
||||
#define Dqn_Allocator_New(allocator, Type, zero_mem) (Type *)Dqn_Allocator_Alloc(allocator, sizeof(Type), alignof(Type), zero_mem)
|
||||
|
||||
// NOTE: Internal ==================================================================================
|
||||
void *Dqn_Allocator_Alloc_ (DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, size_t size, uint8_t align, Dqn_ZeroMem zero_mem);
|
||||
void Dqn_Allocator_Dealloc_(DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, void *ptr, size_t size);
|
||||
// NOTE: API =======================================================================================
|
||||
void *Dqn_Allocator_Alloc (Dqn_Allocator allocator, size_t size, uint8_t align, Dqn_ZeroMem zero_mem);
|
||||
void Dqn_Allocator_Dealloc(Dqn_Allocator allocator, void *ptr, size_t size);
|
||||
|
||||
// NOTE: [$VMEM] Dqn_VMem ==========================================================================
|
||||
enum Dqn_VMemCommit
|
||||
@ -85,34 +43,55 @@ enum Dqn_VMemPage
|
||||
// first access to the page, then, the underlying protection flags are
|
||||
// active. This is supported on Windows, on other OS's using this flag will
|
||||
// set the OS equivalent of Dqn_VMemPage_NoAccess.
|
||||
// This flag must only be used in Dqn_VMem_Protect
|
||||
Dqn_VMemPage_Guard = 1 << 3,
|
||||
|
||||
// If leak tracing is enabled, this flag will allow the allocation recorded
|
||||
// from the reserve call to be leaked, e.g. not printed when leaks are
|
||||
// dumped to the console.
|
||||
Dqn_VMemPage_AllocRecordLeakPermitted = 1 << 2,
|
||||
};
|
||||
|
||||
#if !defined(DQN_VMEM_PAGE_GRANULARITY)
|
||||
#define DQN_VMEM_PAGE_GRANULARITY DQN_KILOBYTES(4)
|
||||
#endif
|
||||
|
||||
#if !defined(DQN_VMEM_RESERVE_GRANULARITY)
|
||||
#define DQN_VMEM_RESERVE_GRANULARITY DQN_KILOBYTES(64)
|
||||
#endif
|
||||
|
||||
#if !defined(DQN_VMEM_COMMIT_GRANULARITY)
|
||||
#define DQN_VMEM_COMMIT_GRANULARITY DQN_VMEM_PAGE_GRANULARITY
|
||||
#endif
|
||||
|
||||
static_assert(Dqn_IsPowerOfTwo(DQN_VMEM_RESERVE_GRANULARITY),
|
||||
"This library assumes that the memory allocation routines from the OS has PoT allocation granularity");
|
||||
static_assert(Dqn_IsPowerOfTwo(DQN_VMEM_COMMIT_GRANULARITY),
|
||||
"This library assumes that the memory allocation routines from the OS has PoT allocation granularity");
|
||||
static_assert(DQN_VMEM_COMMIT_GRANULARITY < DQN_VMEM_RESERVE_GRANULARITY,
|
||||
"Minimum commit size must be lower than the reserve size to avoid OOB math on pointers in this library");
|
||||
|
||||
DQN_API void *Dqn_VMem_Reserve (Dqn_usize size, Dqn_VMemCommit commit, uint32_t page_flags);
|
||||
DQN_API bool Dqn_VMem_Commit (void *ptr, Dqn_usize size, uint32_t page_flags);
|
||||
DQN_API void Dqn_VMem_Decommit(void *ptr, Dqn_usize size);
|
||||
DQN_API void Dqn_VMem_Release (void *ptr, Dqn_usize size);
|
||||
DQN_API int Dqn_VMem_Protect (void *ptr, Dqn_usize size, uint32_t page_flags);
|
||||
|
||||
// NOTE: [$MEMB] Dqn_MemBlock ======================================================================
|
||||
enum Dqn_MemBlockFlag
|
||||
{
|
||||
Dqn_MemBlockFlag_PageGuard = 1 << 0,
|
||||
Dqn_MemBlockFlag_ArenaPrivate = 1 << 1,
|
||||
|
||||
// If leak tracing is enabled, this flag will allow the allocation recorded
|
||||
// from the reserve call to be leaked, e.g. not printed when leaks are
|
||||
// dumped to the console.
|
||||
Dqn_MemBlockFlag_AllocRecordLeakPermitted = 1 << 2,
|
||||
Dqn_MemBlockFlag_All = Dqn_MemBlockFlag_PageGuard |
|
||||
Dqn_MemBlockFlag_ArenaPrivate |
|
||||
Dqn_MemBlockFlag_AllocRecordLeakPermitted,
|
||||
};
|
||||
|
||||
struct Dqn_MemBlock
|
||||
{
|
||||
void *data;
|
||||
Dqn_usize used;
|
||||
Dqn_usize size;
|
||||
Dqn_usize commit;
|
||||
Dqn_MemBlock *next;
|
||||
Dqn_MemBlock *prev;
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
DQN_API Dqn_usize Dqn_MemBlock_MetadataSize(uint8_t flags);
|
||||
DQN_API Dqn_MemBlock *Dqn_MemBlock_Init (Dqn_usize reserve, Dqn_usize commit, uint8_t flags);
|
||||
DQN_API void *Dqn_MemBlock_Alloc (Dqn_MemBlock *block, Dqn_usize size, uint8_t alignment, Dqn_ZeroMem zero_mem);
|
||||
DQN_API void Dqn_MemBlock_Free (Dqn_MemBlock *block);
|
||||
|
||||
#define Dqn_MemBlock_New(block, Type, zero_mem) (Type *)Dqn_MemBlock_Alloc(block, sizeof(Type), alignof(Type), zero_mem)
|
||||
#define Dqn_MemBlock_NewArray(block, Type, count, zero_mem) (Type *)Dqn_MemBlock_Alloc(block, sizeof(Type) * count, alignof(Type), zero_mem)
|
||||
|
||||
// NOTE: [$AREN] Dqn_Arena =========================================================================
|
||||
// A bump-allocator that can grow dynamically by chaining blocks of memory
|
||||
// together. The arena's memory is backed by virtual memory allowing the
|
||||
@ -144,17 +123,17 @@ DQN_API int Dqn_VMem_Protect (void *ptr, Dqn_usize size, uint32_t page_flags);
|
||||
// @param flags[in] Bit flags from 'Dqn_ArenaBlockFlags', set to 0 if none
|
||||
// @return The block of memory that
|
||||
|
||||
// @proc Dqn_Arena_Allocate, Dqn_Arena_New, Dqn_Arena_NewArray,
|
||||
// @proc Dqn_Arena_Alloc, Dqn_Arena_New, Dqn_Arena_NewArray,
|
||||
// Dqn_Arena_NewArrayWithBlock,
|
||||
// @desc Allocate byte/objects
|
||||
// `Allocate` allocates bytes
|
||||
// @desc Alloc byte/objects
|
||||
// `Alloc` allocates bytes
|
||||
// `New` allocates an object
|
||||
// `NewArray` allocates an array of objects
|
||||
// `NewArrayWithBlock` allocates an array of objects from the given memory 'block'
|
||||
// @return A pointer to the allocated bytes/object. Null pointer on failure
|
||||
|
||||
// @proc Dqn_Arena_Copy, Dqn_Arena_CopyZ
|
||||
// @desc Allocate a copy of an object's bytes. The 'Z' variant adds
|
||||
// @desc Alloc a copy of an object's bytes. The 'Z' variant adds
|
||||
// a null-terminating byte at the end of the stream.
|
||||
// @return A pointer to the allocated object. Null pointer on failure.
|
||||
|
||||
@ -186,13 +165,6 @@ DQN_API int Dqn_VMem_Protect (void *ptr, Dqn_usize size, uint32_t page_flags);
|
||||
// @proc Dqn_Arena_LogStats
|
||||
// @desc Dump the stats of the given arena to the memory log-stream.
|
||||
// @param[in] arena The arena to dump stats for
|
||||
|
||||
enum Dqn_ArenaBlockFlags
|
||||
{
|
||||
Dqn_ArenaBlockFlags_Private = 1 << 0, // Private blocks can only allocate its memory when used in the 'FromBlock' API variants
|
||||
Dqn_ArenaBlockFlags_UseAfterFreeGuard = 1 << 1, // Block was allocated with use-after-free guard semantics
|
||||
};
|
||||
|
||||
struct Dqn_ArenaStat
|
||||
{
|
||||
Dqn_usize capacity; // Total allocating capacity of the arena in bytes
|
||||
@ -228,36 +200,35 @@ struct Dqn_ArenaStatString
|
||||
|
||||
struct Dqn_Arena
|
||||
{
|
||||
bool use_after_free_guard;
|
||||
uint32_t temp_memory_count;
|
||||
bool allocs_are_allowed_to_leak;
|
||||
Dqn_String8 label; // Optional label to describe the arena
|
||||
Dqn_ArenaBlock *head; // Active block the arena is allocating from
|
||||
Dqn_ArenaBlock *curr; // Active block the arena is allocating from
|
||||
Dqn_ArenaBlock *tail; // Last block in the linked list of blocks
|
||||
Dqn_ArenaStat stats; // Current arena stats, reset when reset usage is invoked.
|
||||
Dqn_MemBlock *head; // Active block the arena is allocating from
|
||||
Dqn_MemBlock *curr; // Active block the arena is allocating from
|
||||
Dqn_MemBlock *tail; // Last block in the linked list of blocks
|
||||
uint64_t blocks;
|
||||
};
|
||||
|
||||
struct Dqn_ArenaTempMemory
|
||||
{
|
||||
Dqn_Arena *arena; // Arena the scope is for
|
||||
Dqn_ArenaBlock *head; // Head block of the arena at the beginning of the scope
|
||||
Dqn_ArenaBlock *curr; // Current block of the arena at the beginning of the scope
|
||||
Dqn_ArenaBlock *tail; // Tail block of the arena at the beginning of the scope
|
||||
Dqn_usize curr_used; // Current used amount of the current block
|
||||
Dqn_ArenaStat stats; // Stats of the arena at the beginning of the scope
|
||||
Dqn_Arena *arena; // Arena the scope is for
|
||||
Dqn_MemBlock *head; // Head block of the arena at the beginning of the scope
|
||||
Dqn_MemBlock *curr; // Current block of the arena at the beginning of the scope
|
||||
Dqn_MemBlock *tail; // Tail block of the arena at the beginning of the scope
|
||||
Dqn_usize blocks;
|
||||
Dqn_usize curr_used; // Current used amount of the current block
|
||||
};
|
||||
|
||||
// Automatically begin and end a temporary memory scope on object construction
|
||||
// and destruction respectively.
|
||||
#define DQN_ARENA_TEMP_MEMORY_SCOPE(arena) Dqn_ArenaTempMemoryScope_ DQN_UNIQUE_NAME(temp_memory_) = Dqn_ArenaTempMemoryScope_(DQN_LEAK_TRACE arena)
|
||||
#define Dqn_ArenaTempMemoryScope(arena) Dqn_ArenaTempMemoryScope_(DQN_LEAK_TRACE arena)
|
||||
struct Dqn_ArenaTempMemoryScope_
|
||||
#define Dqn_Arena_TempMemoryScope(arena) Dqn_ArenaTempMemoryScope DQN_UNIQUE_NAME(temp_memory_) = Dqn_ArenaTempMemoryScope(arena)
|
||||
struct Dqn_ArenaTempMemoryScope
|
||||
{
|
||||
Dqn_ArenaTempMemoryScope_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena);
|
||||
~Dqn_ArenaTempMemoryScope_();
|
||||
Dqn_ArenaTempMemoryScope(Dqn_Arena *arena);
|
||||
~Dqn_ArenaTempMemoryScope();
|
||||
Dqn_ArenaTempMemory temp_memory;
|
||||
bool cancel = false;
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
Dqn_CallSite leak_site__;
|
||||
Dqn_CallSite leak_site_;
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -270,35 +241,25 @@ enum Dqn_ArenaCommit
|
||||
};
|
||||
|
||||
// NOTE: Allocation ================================================================================
|
||||
#define Dqn_Arena_Grow(arena, size, commit, flags) Dqn_Arena_Grow_(DQN_LEAK_TRACE arena, size, commit, flags)
|
||||
#define Dqn_Arena_Allocate(arena, size, align, zero_mem) Dqn_Arena_Allocate_(DQN_LEAK_TRACE arena, size, align, zero_mem)
|
||||
#define Dqn_Arena_New(arena, Type, zero_mem) (Type *)Dqn_Arena_Allocate_(DQN_LEAK_TRACE arena, sizeof(Type), alignof(Type), zero_mem)
|
||||
#define Dqn_Arena_NewArray(arena, Type, count, zero_mem) (Type *)Dqn_Arena_Allocate_(DQN_LEAK_TRACE arena, sizeof(Type) * count, alignof(Type), zero_mem)
|
||||
#define Dqn_Arena_NewArrayWithBlock(block, Type, count, zero_mem) (Type *)Dqn_Arena_AllocateFromBlock(block, sizeof(Type) * count, alignof(Type), zero_mem)
|
||||
#define Dqn_Arena_Copy(arena, Type, src, count) (Type *)Dqn_Arena_Copy_(DQN_LEAK_TRACE arena, src, sizeof(*src) * count, alignof(Type))
|
||||
#define Dqn_Arena_CopyZ(arena, Type, src, count) (Type *)Dqn_Arena_CopyZ_(DQN_LEAK_TRACE arena, src, sizeof(*src) * count, alignof(Type))
|
||||
#define Dqn_Arena_Free(arena, zero_mem) Dqn_Arena_Free_(DQN_LEAK_TRACE arena, zero_mem)
|
||||
#define Dqn_Arena_New(arena, Type, zero_mem) (Type *)Dqn_Arena_Alloc(arena, sizeof(Type), alignof(Type), zero_mem)
|
||||
#define Dqn_Arena_NewArray(arena, Type, count, zero_mem) (Type *)Dqn_Arena_Alloc(arena, sizeof(Type) * count, alignof(Type), zero_mem)
|
||||
#define Dqn_Arena_NewCopy(arena, Type, src, count) (Type *)Dqn_Arena_Copy(arena, src, sizeof(*src) * count, alignof(Type))
|
||||
#define Dqn_Arena_NewCopyZ(arena, Type, src, count) (Type *)Dqn_Arena_CopyZ(arena, src, sizeof(*src) * count, alignof(Type))
|
||||
|
||||
DQN_API void Dqn_Arena_CommitFromBlock (Dqn_ArenaBlock *block, Dqn_usize size, Dqn_ArenaCommit commit);
|
||||
DQN_API void * Dqn_Arena_AllocateFromBlock(Dqn_ArenaBlock *block, Dqn_usize size, uint8_t align, Dqn_ZeroMem zero_mem);
|
||||
DQN_API Dqn_Allocator Dqn_Arena_Allocator (Dqn_Arena *arena);
|
||||
DQN_API void Dqn_Arena_Reset (Dqn_Arena *arena, Dqn_ZeroMem zero_mem);
|
||||
DQN_API Dqn_Allocator Dqn_Arena_Allocator (Dqn_Arena *arena);
|
||||
DQN_API Dqn_MemBlock * Dqn_Arena_Grow (Dqn_Arena *arena, Dqn_usize size, Dqn_usize commit, uint8_t flags);
|
||||
DQN_API void * Dqn_Arena_Alloc (Dqn_Arena *arena, Dqn_usize size, uint8_t align, Dqn_ZeroMem zero_mem);
|
||||
DQN_API void * Dqn_Arena_Copy (Dqn_Arena *arena, void *src, Dqn_usize size, uint8_t alignment);
|
||||
DQN_API void * Dqn_Arena_CopyZ (Dqn_Arena *arena, void *src, Dqn_usize size, uint8_t alignment);
|
||||
DQN_API void Dqn_Arena_Free (Dqn_Arena *arena, Dqn_ZeroMem zero_mem);
|
||||
|
||||
// NOTE: Temp Memory ===============================================================================
|
||||
DQN_API Dqn_ArenaTempMemory Dqn_Arena_BeginTempMemory (Dqn_Arena *arena);
|
||||
#define Dqn_Arena_EndTempMemory(arena_temp_memory) Dqn_Arena_EndTempMemory_(DQN_LEAK_TRACE arena_temp_memory)
|
||||
DQN_API Dqn_ArenaTempMemory Dqn_Arena_BeginTempMemory(Dqn_Arena *arena);
|
||||
DQN_API void Dqn_Arena_EndTempMemory (Dqn_ArenaTempMemory temp_memory, bool cancel);
|
||||
|
||||
// NOTE: Arena Stats ===============================================================================
|
||||
DQN_API Dqn_ArenaStatString Dqn_Arena_StatString (Dqn_ArenaStat const *stat);
|
||||
DQN_API void Dqn_Arena_LogStats (Dqn_Arena const *arena);
|
||||
|
||||
// NOTE: Internal ==================================================================================
|
||||
DQN_API Dqn_ArenaBlock * Dqn_Arena_Grow_ (DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, Dqn_usize size, Dqn_usize commit, uint8_t flags);
|
||||
DQN_API void * Dqn_Arena_Allocate_ (DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, Dqn_usize size, uint8_t align, Dqn_ZeroMem zero_mem);
|
||||
DQN_API void * Dqn_Arena_Copy_ (DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, void *src, Dqn_usize size, uint8_t alignment);
|
||||
DQN_API void Dqn_Arena_Free_ (DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, Dqn_ZeroMem zero_mem);
|
||||
DQN_API void * Dqn_Arena_CopyZ_ (DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, void *src, Dqn_usize size, uint8_t alignment);
|
||||
DQN_API void Dqn_Arena_EndTempMemory_ (DQN_LEAK_TRACE_FUNCTION Dqn_ArenaTempMemory arena_temp_memory);
|
||||
DQN_API Dqn_ArenaStatString Dqn_Arena_StatString (Dqn_ArenaStat const *stat);
|
||||
DQN_API void Dqn_Arena_LogStats (Dqn_Arena const *arena);
|
||||
|
||||
// NOTE: [$ACAT] Dqn_ArenaCatalog ==================================================================
|
||||
struct Dqn_ArenaCatalogItem
|
||||
|
587
dqn_misc.cpp
587
dqn_misc.cpp
@ -1,587 +0,0 @@
|
||||
// NOTE: [$DLIB] Dqn_Library =======================================================================
|
||||
Dqn_Library dqn_library;
|
||||
DQN_API Dqn_Library *Dqn_Library_Init(Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_Library *result = &dqn_library;
|
||||
Dqn_TicketMutex_Begin(&result->lib_mutex);
|
||||
if (!result->lib_init) {
|
||||
Dqn_ArenaCatalog_Init(&result->arena_catalog, arena ? arena : &result->arena_catalog_backup_arena);
|
||||
result->lib_init = true;
|
||||
}
|
||||
Dqn_TicketMutex_End(&result->lib_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Library_SetLogCallback(Dqn_LogProc *proc, void *user_data)
|
||||
{
|
||||
dqn_library.log_callback = proc;
|
||||
dqn_library.log_user_data = user_data;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Library_SetLogFile(FILE *file)
|
||||
{
|
||||
Dqn_TicketMutex_Begin(&dqn_library.log_file_mutex);
|
||||
dqn_library.log_file = file;
|
||||
dqn_library.log_to_file = file ? true : false;
|
||||
Dqn_TicketMutex_End(&dqn_library.log_file_mutex);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_String8 file_path)
|
||||
{
|
||||
#if defined(DQN_DEBUG_THREAD_CONTEXT)
|
||||
// NOTE: Open a file to write the arena stats to
|
||||
FILE *file = nullptr;
|
||||
fopen_s(&file, file_path.data, "a+b");
|
||||
if (file) {
|
||||
Dqn_Log_ErrorF("Failed to dump thread context arenas [file=%.*s]", DQN_STRING_FMT(file_path));
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: Copy the stats from library book-keeping
|
||||
// NOTE: Extremely short critical section, copy the stats then do our
|
||||
// work on it.
|
||||
Dqn_ArenaStat stats[Dqn_CArray_CountI(dqn_library.thread_context_arena_stats)];
|
||||
int stats_size = 0;
|
||||
|
||||
Dqn_TicketMutex_Begin(&dqn_library.thread_context_mutex);
|
||||
stats_size = dqn_library.thread_context_arena_stats_count;
|
||||
DQN_MEMCPY(stats, dqn_library.thread_context_arena_stats, sizeof(stats[0]) * stats_size);
|
||||
Dqn_TicketMutex_End(&dqn_library.thread_context_mutex);
|
||||
|
||||
// NOTE: Print the cumulative stat
|
||||
Dqn_DateHMSTimeString now = Dqn_Date_HMSLocalTimeStringNow();
|
||||
fprintf(file,
|
||||
"Time=%.*s %.*s | Thread Context Arenas | Count=%d\n",
|
||||
now.date_size, now.date,
|
||||
now.hms_size, now.hms,
|
||||
dqn_library.thread_context_arena_stats_count);
|
||||
|
||||
// NOTE: Write the cumulative thread arena data
|
||||
{
|
||||
Dqn_ArenaStat stat = {};
|
||||
for (Dqn_usize index = 0; index < stats_size; index++) {
|
||||
Dqn_ArenaStat const *current = stats + index;
|
||||
stat.capacity += current->capacity;
|
||||
stat.used += current->used;
|
||||
stat.wasted += current->wasted;
|
||||
stat.blocks += current->blocks;
|
||||
|
||||
stat.capacity_hwm = DQN_MAX(stat.capacity_hwm, current->capacity_hwm);
|
||||
stat.used_hwm = DQN_MAX(stat.used_hwm, current->used_hwm);
|
||||
stat.wasted_hwm = DQN_MAX(stat.wasted_hwm, current->wasted_hwm);
|
||||
stat.blocks_hwm = DQN_MAX(stat.blocks_hwm, current->blocks_hwm);
|
||||
}
|
||||
|
||||
Dqn_ArenaStatString stats_string = Dqn_Arena_StatString(&stat);
|
||||
fprintf(file, " [ALL] CURR %.*s\n", stats_string.size, stats_string.data);
|
||||
}
|
||||
|
||||
// NOTE: Print individual thread arena data
|
||||
for (Dqn_usize index = 0; index < stats_size; index++) {
|
||||
Dqn_ArenaStat const *current = stats + index;
|
||||
Dqn_ArenaStatString current_string = Dqn_Arena_StatString(current);
|
||||
fprintf(file, " [%03d] CURR %.*s\n", DQN_CAST(int)index, current_string.size, current_string.data);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
Dqn_Log_InfoF("Dumped thread context arenas [file=%.*s]", DQN_STRING_FMT(file_path));
|
||||
#else
|
||||
(void)file_path;
|
||||
#endif // #if defined(DQN_DEBUG_THREAD_CONTEXT)
|
||||
}
|
||||
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
DQN_API void Dqn_Library_LeakTraceAdd(Dqn_CallSite call_site, void *ptr, Dqn_usize size)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
Dqn_TicketMutex_Begin(&dqn_library.alloc_table_mutex);
|
||||
if (!Dqn_DSMap_IsValid(&dqn_library.alloc_table))
|
||||
dqn_library.alloc_table = Dqn_DSMap_Init<Dqn_LeakTrace>(4096);
|
||||
|
||||
// NOTE: If the entry was not added, we are reusing a pointer that has been freed.
|
||||
// TODO: Add API for always making the item but exposing a var to indicate if the item was newly created or it already existed.
|
||||
Dqn_LeakTrace *trace = Dqn_DSMap_Find(&dqn_library.alloc_table, Dqn_DSMap_KeyU64(DQN_CAST(uintptr_t)ptr));
|
||||
if (trace) {
|
||||
DQN_HARD_ASSERTF(trace->freed, "This pointer is already in the leak tracker, however it"
|
||||
" has not been freed yet. Somehow this pointer has been"
|
||||
" given to the allocation table and has not being marked"
|
||||
" freed with an equivalent call to LeakTraceMarkFree()"
|
||||
" [ptr=%p, size=%_$$d, file=\"%.*s:%u\","
|
||||
" function=\"%.*s\"]",
|
||||
ptr,
|
||||
size,
|
||||
DQN_STRING_FMT(trace->call_site.file),
|
||||
trace->call_site.line,
|
||||
DQN_STRING_FMT(trace->call_site.function));
|
||||
} else {
|
||||
trace = Dqn_DSMap_Make(&dqn_library.alloc_table, Dqn_DSMap_KeyU64(DQN_CAST(uintptr_t)ptr));
|
||||
}
|
||||
|
||||
trace->ptr = ptr;
|
||||
trace->size = size;
|
||||
trace->call_site = call_site;
|
||||
Dqn_TicketMutex_End(&dqn_library.alloc_table_mutex);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Library_LeakTraceMarkFree(Dqn_CallSite call_site, void *ptr)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
Dqn_TicketMutex_Begin(&dqn_library.alloc_table_mutex);
|
||||
|
||||
Dqn_LeakTrace *trace = Dqn_DSMap_Find(&dqn_library.alloc_table, Dqn_DSMap_KeyU64(DQN_CAST(uintptr_t)ptr));
|
||||
DQN_HARD_ASSERTF(trace, "Allocated pointer can not be removed as it does not exist in the"
|
||||
" allocation table. When this memory was allocated, the pointer was"
|
||||
" not added to the allocation table [ptr=%p]",
|
||||
ptr);
|
||||
|
||||
DQN_HARD_ASSERTF(!trace->freed,
|
||||
"Double free detected, pointer was previously allocated at [ptr=%p, %_$$d, file=\"%.*s:%u\", function=\"%.*s\"]",
|
||||
ptr,
|
||||
trace->size,
|
||||
DQN_STRING_FMT(trace->call_site.file),
|
||||
trace->call_site.line,
|
||||
DQN_STRING_FMT(trace->call_site.function));
|
||||
|
||||
trace->freed = true;
|
||||
trace->freed_size = trace->size;
|
||||
trace->freed_call_site = call_site;
|
||||
Dqn_TicketMutex_End(&dqn_library.alloc_table_mutex);
|
||||
}
|
||||
#endif /// defined(DQN_LEAK_TRACING)
|
||||
|
||||
// NOTE: [$BITS] Dqn_Bit ===========================================================================
|
||||
DQN_API void Dqn_Bit_UnsetInplace(uint64_t *flags, uint64_t bitfield)
|
||||
{
|
||||
*flags = (*flags & ~bitfield);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Bit_SetInplace(uint64_t *flags, uint64_t bitfield)
|
||||
{
|
||||
*flags = (*flags | bitfield);
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Bit_IsSet(uint64_t bits, uint64_t bits_to_set)
|
||||
{
|
||||
auto result = DQN_CAST(bool)((bits & bits_to_set) == bits_to_set);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Bit_IsNotSet(uint64_t bits, uint64_t bits_to_check)
|
||||
{
|
||||
auto result = !Dqn_Bit_IsSet(bits, bits_to_check);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$SAFE] Dqn_Safe ==========================================================================
|
||||
DQN_API int64_t Dqn_Safe_AddI64(int64_t a, int64_t b)
|
||||
{
|
||||
int64_t result = DQN_CHECKF(a <= INT64_MAX - b, "a=%zd, b=%zd", a, b) ? (a + b) : INT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int64_t Dqn_Safe_MulI64(int64_t a, int64_t b)
|
||||
{
|
||||
int64_t result = DQN_CHECKF(a <= INT64_MAX / b, "a=%zd, b=%zd", a, b) ? (a * b) : INT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_AddU64(uint64_t a, uint64_t b)
|
||||
{
|
||||
uint64_t result = DQN_CHECKF(a <= UINT64_MAX - b, "a=%zu, b=%zu", a, b) ? (a + b) : UINT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_SubU64(uint64_t a, uint64_t b)
|
||||
{
|
||||
uint64_t result = DQN_CHECKF(a >= b, "a=%zu, b=%zu", a, b) ? (a - b) : 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_MulU64(uint64_t a, uint64_t b)
|
||||
{
|
||||
uint64_t result = DQN_CHECKF(a <= UINT64_MAX / b, "a=%zu, b=%zu", a, b) ? (a * b) : UINT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint32_t Dqn_Safe_SubU32(uint32_t a, uint32_t b)
|
||||
{
|
||||
uint32_t result = DQN_CHECKF(a >= b, "a=%u, b=%u", a, b) ? (a - b) : 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_Safe_SaturateCastUSizeToI*
|
||||
// -----------------------------------------------------------------------------
|
||||
// INT*_MAX literals will be promoted to the type of uintmax_t as uintmax_t is
|
||||
// the highest possible rank (unsigned > signed).
|
||||
DQN_API int Dqn_Safe_SaturateCastUSizeToInt(Dqn_usize val)
|
||||
{
|
||||
int result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT_MAX) ? DQN_CAST(int)val : INT_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastUSizeToI8(Dqn_usize val)
|
||||
{
|
||||
int8_t result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT8_MAX) ? DQN_CAST(int8_t)val : INT8_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastUSizeToI16(Dqn_usize val)
|
||||
{
|
||||
int16_t result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT16_MAX) ? DQN_CAST(int16_t)val : INT16_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int32_t Dqn_Safe_SaturateCastUSizeToI32(Dqn_usize val)
|
||||
{
|
||||
int32_t result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT32_MAX) ? DQN_CAST(int32_t)val : INT32_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int64_t Dqn_Safe_SaturateCastUSizeToI64(Dqn_usize val)
|
||||
{
|
||||
int64_t result = DQN_CHECK(DQN_CAST(uintmax_t)val <= INT64_MAX) ? DQN_CAST(int64_t)val : INT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_Safe_SaturateCastUSizeToU*
|
||||
// -----------------------------------------------------------------------------
|
||||
// Both operands are unsigned and the lowest rank operand will be promoted to
|
||||
// match the highest rank operand.
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastUSizeToU8(Dqn_usize val)
|
||||
{
|
||||
uint8_t result = DQN_CHECK(val <= UINT8_MAX) ? DQN_CAST(uint8_t)val : UINT8_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastUSizeToU16(Dqn_usize val)
|
||||
{
|
||||
uint16_t result = DQN_CHECK(val <= UINT16_MAX) ? DQN_CAST(uint16_t)val : UINT16_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastUSizeToU32(Dqn_usize val)
|
||||
{
|
||||
uint32_t result = DQN_CHECK(val <= UINT32_MAX) ? DQN_CAST(uint32_t)val : UINT32_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_SaturateCastUSizeToU64(Dqn_usize val)
|
||||
{
|
||||
uint64_t result = DQN_CHECK(val <= UINT64_MAX) ? DQN_CAST(uint64_t)val : UINT64_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_Safe_SaturateCastU64ToU*
|
||||
// -----------------------------------------------------------------------------
|
||||
// Both operands are unsigned and the lowest rank operand will be promoted to
|
||||
// match the highest rank operand.
|
||||
DQN_API unsigned int Dqn_Safe_SaturateCastU64ToUInt(uint64_t val)
|
||||
{
|
||||
unsigned int result = DQN_CHECK(val <= UINT8_MAX) ? DQN_CAST(unsigned int)val : UINT_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastU64ToU8(uint64_t val)
|
||||
{
|
||||
uint8_t result = DQN_CHECK(val <= UINT8_MAX) ? DQN_CAST(uint8_t)val : UINT8_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastU64ToU16(uint64_t val)
|
||||
{
|
||||
uint16_t result = DQN_CHECK(val <= UINT16_MAX) ? DQN_CAST(uint16_t)val : UINT16_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastU64ToU32(uint64_t val)
|
||||
{
|
||||
uint32_t result = DQN_CHECK(val <= UINT32_MAX) ? DQN_CAST(uint32_t)val : UINT32_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: Dqn_Safe_SaturateCastISizeToI*
|
||||
// -----------------------------------------------------------------------------
|
||||
// Both operands are signed so the lowest rank operand will be promoted to
|
||||
// match the highest rank operand.
|
||||
DQN_API int Dqn_Safe_SaturateCastISizeToInt(Dqn_isize val)
|
||||
{
|
||||
DQN_ASSERT(val >= INT_MIN && val <= INT_MAX);
|
||||
int result = DQN_CAST(int)DQN_CLAMP(val, INT_MIN, INT_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastISizeToI8(Dqn_isize val)
|
||||
{
|
||||
DQN_ASSERT(val >= INT8_MIN && val <= INT8_MAX);
|
||||
int8_t result = DQN_CAST(int8_t)DQN_CLAMP(val, INT8_MIN, INT8_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastISizeToI16(Dqn_isize val)
|
||||
{
|
||||
DQN_ASSERT(val >= INT16_MIN && val <= INT16_MAX);
|
||||
int16_t result = DQN_CAST(int16_t)DQN_CLAMP(val, INT16_MIN, INT16_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int32_t Dqn_Safe_SaturateCastISizeToI32(Dqn_isize val)
|
||||
{
|
||||
DQN_ASSERT(val >= INT32_MIN && val <= INT32_MAX);
|
||||
int32_t result = DQN_CAST(int32_t)DQN_CLAMP(val, INT32_MIN, INT32_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int64_t Dqn_Safe_SaturateCastISizeToI64(Dqn_isize val)
|
||||
{
|
||||
DQN_ASSERT(val >= INT64_MIN && val <= INT64_MAX);
|
||||
int64_t result = DQN_CAST(int64_t)DQN_CLAMP(val, INT64_MIN, INT64_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_Safe_SaturateCastISizeToU*
|
||||
// -----------------------------------------------------------------------------
|
||||
// If the value is a negative integer, we clamp to 0. Otherwise, we know that
|
||||
// the value is >=0, we can upcast safely to bounds check against the maximum
|
||||
// allowed value.
|
||||
DQN_API unsigned int Dqn_Safe_SaturateCastISizeToUInt(Dqn_isize val)
|
||||
{
|
||||
unsigned int result = 0;
|
||||
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT_MAX))
|
||||
result = DQN_CAST(unsigned int)val;
|
||||
else
|
||||
result = UINT_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastISizeToU8(Dqn_isize val)
|
||||
{
|
||||
uint8_t result = 0;
|
||||
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT8_MAX))
|
||||
result = DQN_CAST(uint8_t)val;
|
||||
else
|
||||
result = UINT8_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastISizeToU16(Dqn_isize val)
|
||||
{
|
||||
uint16_t result = 0;
|
||||
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT16_MAX))
|
||||
result = DQN_CAST(uint16_t)val;
|
||||
else
|
||||
result = UINT16_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastISizeToU32(Dqn_isize val)
|
||||
{
|
||||
uint32_t result = 0;
|
||||
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT32_MAX))
|
||||
result = DQN_CAST(uint32_t)val;
|
||||
else
|
||||
result = UINT32_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_SaturateCastISizeToU64(Dqn_isize val)
|
||||
{
|
||||
uint64_t result = 0;
|
||||
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT64_MAX))
|
||||
result = DQN_CAST(uint64_t)val;
|
||||
else
|
||||
result = UINT64_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_Safe_SaturateCastI64To*
|
||||
// -----------------------------------------------------------------------------
|
||||
// Both operands are signed so the lowest rank operand will be promoted to
|
||||
// match the highest rank operand.
|
||||
DQN_API Dqn_isize Dqn_Safe_SaturateCastI64ToISize(int64_t val)
|
||||
{
|
||||
DQN_CHECK(val >= DQN_ISIZE_MIN && val <= DQN_ISIZE_MAX);
|
||||
Dqn_isize result = DQN_CAST(int64_t)DQN_CLAMP(val, DQN_ISIZE_MIN, DQN_ISIZE_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastI64ToI8(int64_t val)
|
||||
{
|
||||
DQN_CHECK(val >= INT8_MIN && val <= INT8_MAX);
|
||||
int8_t result = DQN_CAST(int8_t)DQN_CLAMP(val, INT8_MIN, INT8_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastI64ToI16(int64_t val)
|
||||
{
|
||||
DQN_CHECK(val >= INT16_MIN && val <= INT16_MAX);
|
||||
int16_t result = DQN_CAST(int16_t)DQN_CLAMP(val, INT16_MIN, INT16_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int32_t Dqn_Safe_SaturateCastI64ToI32(int64_t val)
|
||||
{
|
||||
DQN_CHECK(val >= INT32_MIN && val <= INT32_MAX);
|
||||
int32_t result = DQN_CAST(int32_t)DQN_CLAMP(val, INT32_MIN, INT32_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_Safe_SaturateCastIntTo*
|
||||
// -----------------------------------------------------------------------------
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastIntToI8(int val)
|
||||
{
|
||||
DQN_CHECK(val >= INT8_MIN && val <= INT8_MAX);
|
||||
int8_t result = DQN_CAST(int8_t)DQN_CLAMP(val, INT8_MIN, INT8_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastIntToI16(int val)
|
||||
{
|
||||
DQN_CHECK(val >= INT16_MIN && val <= INT16_MAX);
|
||||
int16_t result = DQN_CAST(int16_t)DQN_CLAMP(val, INT16_MIN, INT16_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastIntToU8(int val)
|
||||
{
|
||||
uint8_t result = 0;
|
||||
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT8_MAX))
|
||||
result = DQN_CAST(uint8_t)val;
|
||||
else
|
||||
result = UINT8_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastIntToU16(int val)
|
||||
{
|
||||
uint16_t result = 0;
|
||||
if (DQN_CHECK(val >= DQN_CAST(Dqn_isize)0)) {
|
||||
if (DQN_CHECK(DQN_CAST(uintmax_t)val <= UINT16_MAX))
|
||||
result = DQN_CAST(uint16_t)val;
|
||||
else
|
||||
result = UINT16_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastIntToU32(int val)
|
||||
{
|
||||
static_assert(sizeof(val) <= sizeof(uint32_t), "Sanity check to allow simplifying of casting");
|
||||
uint32_t result = 0;
|
||||
if (DQN_CHECK(val >= 0))
|
||||
result = DQN_CAST(uint32_t)val;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_SaturateCastIntToU64(int val)
|
||||
{
|
||||
static_assert(sizeof(val) <= sizeof(uint64_t), "Sanity check to allow simplifying of casting");
|
||||
uint64_t result = 0;
|
||||
if (DQN_CHECK(val >= 0))
|
||||
result = DQN_CAST(uint64_t)val;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$TCTX] Dqn_ThreadContext =================================================================
|
||||
Dqn_ThreadScratch::Dqn_ThreadScratch(DQN_LEAK_TRACE_FUNCTION Dqn_ThreadContext *context, uint8_t context_index)
|
||||
{
|
||||
index = context_index;
|
||||
allocator = context->temp_allocators[index];
|
||||
arena = context->temp_arenas[index];
|
||||
temp_memory = Dqn_Arena_BeginTempMemory(arena);
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
leak_site__ = DQN_LEAK_TRACE_ARG_NO_COMMA;
|
||||
#endif
|
||||
}
|
||||
|
||||
Dqn_ThreadScratch::~Dqn_ThreadScratch()
|
||||
{
|
||||
#if defined(DQN_DEBUG_THREAD_CONTEXT)
|
||||
temp_arenas_stat[index] = arena->stats;
|
||||
#endif
|
||||
DQN_ASSERT(destructed == false);
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
Dqn_Arena_EndTempMemory_(leak_site__, temp_memory);
|
||||
#else
|
||||
Dqn_Arena_EndTempMemory_(temp_memory);
|
||||
#endif
|
||||
destructed = true;
|
||||
}
|
||||
|
||||
DQN_API uint32_t Dqn_Thread_GetID()
|
||||
{
|
||||
#if defined(DQN_OS_WIN32)
|
||||
unsigned long result = GetCurrentThreadId();
|
||||
#else
|
||||
pid_t result = gettid();
|
||||
assert(gettid() >= 0);
|
||||
#endif
|
||||
return (uint32_t)result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_ThreadContext *Dqn_Thread_GetContext_(DQN_LEAK_TRACE_FUNCTION_NO_COMMA)
|
||||
{
|
||||
|
||||
DQN_THREAD_LOCAL Dqn_ThreadContext result = {};
|
||||
if (!result.init) {
|
||||
result.init = true;
|
||||
DQN_ASSERTF(dqn_library.lib_init, "Library must be initialised by calling Dqn_Library_Init(nullptr)");
|
||||
|
||||
// NOTE: Setup permanent arena
|
||||
Dqn_ArenaCatalog *catalog = &dqn_library.arena_catalog;
|
||||
result.allocator = Dqn_Arena_Allocator(result.arena);
|
||||
result.arena = Dqn_ArenaCatalog_AllocF(catalog,
|
||||
DQN_GIGABYTES(1) /*size*/,
|
||||
DQN_KILOBYTES(64) /*commit*/,
|
||||
"Thread %u Arena",
|
||||
Dqn_Thread_GetID());
|
||||
|
||||
// NOTE: Setup temporary arenas
|
||||
for (uint8_t index = 0; index < DQN_THREAD_CONTEXT_ARENAS; index++) {
|
||||
result.temp_arenas[index] = Dqn_ArenaCatalog_AllocF(catalog,
|
||||
DQN_GIGABYTES(1) /*size*/,
|
||||
DQN_KILOBYTES(64) /*commit*/,
|
||||
"Thread %u Temp Arena %u",
|
||||
Dqn_Thread_GetID(),
|
||||
index);
|
||||
result.temp_allocators[index] = Dqn_Arena_Allocator(result.temp_arenas[index]);
|
||||
}
|
||||
}
|
||||
return &result;
|
||||
}
|
||||
|
||||
// TODO: Is there a way to handle conflict arenas without the user needing to
|
||||
// manually pass it in?
|
||||
DQN_API Dqn_ThreadScratch Dqn_Thread_GetScratch_(DQN_LEAK_TRACE_FUNCTION void const *conflict_arena)
|
||||
{
|
||||
static_assert(DQN_THREAD_CONTEXT_ARENAS < (uint8_t)-1, "We use UINT8_MAX as a sentinel value");
|
||||
Dqn_ThreadContext *context = Dqn_Thread_GetContext_(DQN_LEAK_TRACE_ARG_NO_COMMA);
|
||||
uint8_t context_index = (uint8_t)-1;
|
||||
for (uint8_t index = 0; index < DQN_THREAD_CONTEXT_ARENAS; index++) {
|
||||
Dqn_Arena *arena = context->temp_arenas[index];
|
||||
if (!conflict_arena || arena != conflict_arena) {
|
||||
context_index = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DQN_ASSERT(context_index != (uint8_t)-1);
|
||||
return Dqn_ThreadScratch(DQN_LEAK_TRACE_ARG context, context_index);
|
||||
}
|
||||
|
386
dqn_misc.h
386
dqn_misc.h
@ -1,386 +0,0 @@
|
||||
// NOTE: Table Of Contents =========================================================================
|
||||
// Index | Disable #define | Description
|
||||
// =================================================================================================
|
||||
// [$DLIB] Dqn_Library | | Library run-time behaviour configuration
|
||||
// [$BITS] Dqn_Bit | | Bitset manipulation
|
||||
// [$SAFE] Dqn_Safe | | Safe arithmetic, casts, asserts
|
||||
// [$TCTX] Dqn_ThreadContext | | Per-thread data structure e.g. temp arenas
|
||||
// [$BSEA] Dqn_BinarySearch | | Binary search
|
||||
// =================================================================================================
|
||||
|
||||
// NOTE: [$DLIB] Dqn_Library =======================================================================
|
||||
// Book-keeping data for the library and allow customisation of certain features
|
||||
// provided.
|
||||
//
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_Library_SetLogCallback
|
||||
// @desc Update the default logging function, all logging functions will run through
|
||||
// this callback
|
||||
// @param[in] proc The new logging function, set to nullptr to revert back to
|
||||
// the default logger.
|
||||
// @param[in] user_data A user defined parameter to pass to the callback
|
||||
//
|
||||
// @proc Dqn_Library_SetLogFile
|
||||
// @param[in] file Pass in nullptr to turn off writing logs to disk, otherwise
|
||||
// point it to the FILE that you wish to write to.
|
||||
//
|
||||
// @proc Dqn_Library_DumpThreadContextArenaStat
|
||||
// @desc Dump the per-thread arena statistics to the specified file
|
||||
|
||||
struct Dqn_Library
|
||||
{
|
||||
bool lib_init;
|
||||
Dqn_TicketMutex lib_mutex;
|
||||
|
||||
Dqn_LogProc *log_callback; ///< Set this pointer to override the logging routine
|
||||
void * log_user_data;
|
||||
bool log_to_file; ///< Output logs to file as well as standard out
|
||||
void * log_file; ///< TODO(dqn): Hmmm, how should we do this... ?
|
||||
Dqn_TicketMutex log_file_mutex; ///< Is locked when instantiating the log_file for the first time
|
||||
bool log_no_colour; ///< Disable colours in the logging output
|
||||
|
||||
/// The backup arena to use if no arena is passed into Dqn_Library_Init
|
||||
Dqn_Arena arena_catalog_backup_arena;
|
||||
Dqn_ArenaCatalog arena_catalog;
|
||||
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
Dqn_TicketMutex alloc_table_mutex;
|
||||
Dqn_DSMap<Dqn_LeakTrace> alloc_table;
|
||||
#endif
|
||||
|
||||
#if defined(DQN_OS_WIN32)
|
||||
LARGE_INTEGER win32_qpc_frequency;
|
||||
Dqn_TicketMutex win32_bcrypt_rng_mutex;
|
||||
void *win32_bcrypt_rng_handle;
|
||||
#endif
|
||||
|
||||
#if defined(DQN_DEBUG_THREAD_CONTEXT)
|
||||
Dqn_TicketMutex thread_context_mutex;
|
||||
Dqn_ArenaStat thread_context_arena_stats[256];
|
||||
uint8_t thread_context_arena_stats_count;
|
||||
#endif
|
||||
} extern dqn_library;
|
||||
|
||||
// NOTE: Properties ================================================================================
|
||||
DQN_API Dqn_Library *Dqn_Library_Init (Dqn_Arena *arena);
|
||||
DQN_API void Dqn_Library_SetLogCallback (Dqn_LogProc *proc, void *user_data);
|
||||
DQN_API void Dqn_Library_SetLogFile (void *file);
|
||||
DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_String8 file_path);
|
||||
|
||||
// NOTE: Leak Trace ================================================================================
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
DQN_API void Dqn_Library_LeakTraceAdd (Dqn_CallSite call_site, void *ptr, Dqn_usize size);
|
||||
DQN_API void Dqn_Library_LeakTraceMarkFree (Dqn_CallSite call_site, void *ptr);
|
||||
#else
|
||||
#define Dqn_Library_LeakTraceAdd(...)
|
||||
#define Dqn_Library_LeakTraceMarkFree(...)
|
||||
#endif
|
||||
|
||||
// NOTE: [$BITS] Dqn_Bit ===========================================================================
|
||||
DQN_API void Dqn_Bit_UnsetInplace(uint32_t *flags, uint32_t bitfield);
|
||||
DQN_API void Dqn_Bit_SetInplace (uint32_t *flags, uint32_t bitfield);
|
||||
DQN_API bool Dqn_Bit_IsSet (uint32_t bits, uint32_t bits_to_set);
|
||||
DQN_API bool Dqn_Bit_IsNotSet (uint32_t bits, uint32_t bits_to_check);
|
||||
|
||||
// NOTE: [$SAFE] Dqn_Safe ==========================================================================
|
||||
// @proc Dqn_Safe_AddI64, Dqn_Safe_MulI64
|
||||
// @desc Add/multiply 2 I64's together, safe asserting that the operation does
|
||||
// not overflow.
|
||||
// @return The result of operation, INT64_MAX if it overflowed.
|
||||
|
||||
// @proc Dqn_Safe_AddU64, Dqn_Safe_MulU64
|
||||
// @desc Add/multiply 2 U64's together, safe asserting that the operation does
|
||||
// not overflow.
|
||||
// @return The result of operation, UINT64_MAX if it overflowed.
|
||||
|
||||
// @proc Dqn_Safe_SubU64, Dqn_Safe_SubU32
|
||||
// @desc Subtract 2xU64 or 2xU32's together, safe asserting that the operation
|
||||
// does not overflow.
|
||||
// @return The result of operation, 0 if it overflowed.
|
||||
|
||||
// @proc Dqn_Safe_SaturateCastUSizeToInt,
|
||||
// Dqn_Safe_SaturateCastUSizeToI8,
|
||||
// Dqn_Safe_SaturateCastUSizeToI16,
|
||||
// Dqn_Safe_SaturateCastUSizeToI32,
|
||||
// Dqn_Safe_SaturateCastUSizeToI64
|
||||
//
|
||||
// Dqn_Safe_SaturateCastU64ToUInt,
|
||||
// Dqn_Safe_SaturateCastU64ToU8,
|
||||
// Dqn_Safe_SaturateCastU64ToU16,
|
||||
// Dqn_Safe_SaturateCastU64ToU32,
|
||||
//
|
||||
// Dqn_Safe_SaturateCastUSizeToU8,
|
||||
// Dqn_Safe_SaturateCastUSizeToU16,
|
||||
// Dqn_Safe_SaturateCastUSizeToU32,
|
||||
// Dqn_Safe_SaturateCastUSizeToU64
|
||||
//
|
||||
// Dqn_Safe_SaturateCastISizeToInt,
|
||||
// Dqn_Safe_SaturateCastISizeToI8,
|
||||
// Dqn_Safe_SaturateCastISizeToI16,
|
||||
// Dqn_Safe_SaturateCastISizeToI32,
|
||||
// Dqn_Safe_SaturateCastISizeToI64,
|
||||
//
|
||||
// int Dqn_Safe_SaturateCastISizeToUInt,
|
||||
// Dqn_Safe_SaturateCastISizeToU8,
|
||||
// Dqn_Safe_SaturateCastISizeToU16,
|
||||
// Dqn_Safe_SaturateCastISizeToU32,
|
||||
// Dqn_Safe_SaturateCastISizeToU64,
|
||||
//
|
||||
// Dqn_Safe_SaturateCastI64ToISize,
|
||||
// Dqn_Safe_SaturateCastI64ToI8,
|
||||
// Dqn_Safe_SaturateCastI64ToI16,
|
||||
// Dqn_Safe_SaturateCastI64ToI32,
|
||||
//
|
||||
// Dqn_Safe_SaturateCastIntToI8,
|
||||
// Dqn_Safe_SaturateCastIntToU8,
|
||||
// Dqn_Safe_SaturateCastIntToU16,
|
||||
// Dqn_Safe_SaturateCastIntToU32,
|
||||
// Dqn_Safe_SaturateCastIntToU64,
|
||||
//
|
||||
// @desc Truncate the lhs operand to the right clamping the result to the max
|
||||
// value of the desired data type. Safe asserts if clamping occurs.
|
||||
//
|
||||
// The following sentinel values are returned when saturated,
|
||||
// USize -> Int: INT_MAX
|
||||
// USize -> I8: INT8_MAX
|
||||
// USize -> I16: INT16_MAX
|
||||
// USize -> I32: INT32_MAX
|
||||
// USize -> I64: INT64_MAX
|
||||
//
|
||||
// U64 -> UInt: UINT_MAX
|
||||
// U64 -> U8: UINT8_MAX
|
||||
// U64 -> U16: UINT16_MAX
|
||||
// U64 -> U32: UINT32_MAX
|
||||
//
|
||||
// USize -> U8: UINT8_MAX
|
||||
// USize -> U16: UINT16_MAX
|
||||
// USize -> U32: UINT32_MAX
|
||||
// USize -> U64: UINT64_MAX
|
||||
//
|
||||
// ISize -> Int: INT_MIN or INT_MAX
|
||||
// ISize -> I8: INT8_MIN or INT8_MAX
|
||||
// ISize -> I16: INT16_MIN or INT16_MAX
|
||||
// ISize -> I32: INT32_MIN or INT32_MAX
|
||||
// ISize -> I64: INT64_MIN or INT64_MAX
|
||||
//
|
||||
// ISize -> UInt: 0 or UINT_MAX
|
||||
// ISize -> U8: 0 or UINT8_MAX
|
||||
// ISize -> U16: 0 or UINT16_MAX
|
||||
// ISize -> U32: 0 or UINT32_MAX
|
||||
// ISize -> U64: 0 or UINT64_MAX
|
||||
//
|
||||
// I64 -> ISize: DQN_ISIZE_MIN or DQN_ISIZE_MAX
|
||||
// I64 -> I8: INT8_MIN or INT8_MAX
|
||||
// I64 -> I16: INT16_MIN or INT16_MAX
|
||||
// I64 -> I32: INT32_MIN or INT32_MAX
|
||||
//
|
||||
// Int -> I8: INT8_MIN or INT8_MAX
|
||||
// Int -> I16: INT16_MIN or INT16_MAX
|
||||
// Int -> U8: 0 or UINT8_MAX
|
||||
// Int -> U16: 0 or UINT16_MAX
|
||||
// Int -> U32: 0 or UINT32_MAX
|
||||
// Int -> U64: 0 or UINT64_MAX
|
||||
|
||||
DQN_API int64_t Dqn_Safe_AddI64 (int64_t a, int64_t b);
|
||||
DQN_API int64_t Dqn_Safe_MulI64 (int64_t a, int64_t b);
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_AddU64 (uint64_t a, uint64_t b);
|
||||
DQN_API uint64_t Dqn_Safe_MulU64 (uint64_t a, uint64_t b);
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_SubU64 (uint64_t a, uint64_t b);
|
||||
DQN_API uint32_t Dqn_Safe_SubU32 (uint32_t a, uint32_t b);
|
||||
|
||||
DQN_API int Dqn_Safe_SaturateCastUSizeToInt (Dqn_usize val);
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastUSizeToI8 (Dqn_usize val);
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastUSizeToI16 (Dqn_usize val);
|
||||
DQN_API int32_t Dqn_Safe_SaturateCastUSizeToI32 (Dqn_usize val);
|
||||
DQN_API int64_t Dqn_Safe_SaturateCastUSizeToI64 (Dqn_usize val);
|
||||
|
||||
DQN_API unsigned int Dqn_Safe_SaturateCastU64ToUInt (uint64_t val);
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastU64ToU8 (uint64_t val);
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastU64ToU16 (uint64_t val);
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastU64ToU32 (uint64_t val);
|
||||
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastUSizeToU8 (Dqn_usize val);
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastUSizeToU16 (Dqn_usize val);
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastUSizeToU32 (Dqn_usize val);
|
||||
DQN_API uint64_t Dqn_Safe_SaturateCastUSizeToU64 (Dqn_usize val);
|
||||
|
||||
DQN_API int Dqn_Safe_SaturateCastISizeToInt (Dqn_isize val);
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastISizeToI8 (Dqn_isize val);
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastISizeToI16 (Dqn_isize val);
|
||||
DQN_API int32_t Dqn_Safe_SaturateCastISizeToI32 (Dqn_isize val);
|
||||
DQN_API int64_t Dqn_Safe_SaturateCastISizeToI64 (Dqn_isize val);
|
||||
|
||||
DQN_API unsigned int Dqn_Safe_SaturateCastISizeToUInt(Dqn_isize val);
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastISizeToU8 (Dqn_isize val);
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastISizeToU16 (Dqn_isize val);
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastISizeToU32 (Dqn_isize val);
|
||||
DQN_API uint64_t Dqn_Safe_SaturateCastISizeToU64 (Dqn_isize val);
|
||||
|
||||
DQN_API Dqn_isize Dqn_Safe_SaturateCastI64ToISize (int64_t val);
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastI64ToI8 (int64_t val);
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastI64ToI16 (int64_t val);
|
||||
DQN_API int32_t Dqn_Safe_SaturateCastI64ToI32 (int64_t val);
|
||||
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastIntToI8 (int val);
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastIntToI16 (int val);
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastIntToU8 (int val);
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastIntToU16 (int val);
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastIntToU32 (int val);
|
||||
DQN_API uint64_t Dqn_Safe_SaturateCastIntToU64 (int val);
|
||||
|
||||
// NOTE: [$TCTX] Dqn_ThreadContext =================================================================
|
||||
// Each thread is assigned in their thread-local storage (TLS) scratch 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.
|
||||
//
|
||||
// TLS in this implementation is implemented using the `thread_local` C/C++
|
||||
// keyword.
|
||||
//
|
||||
// NOTE: API
|
||||
//
|
||||
// @proc Dqn_Thread_GetContext
|
||||
// @desc Get the current thread's context- this contains all the metadata for managing
|
||||
// the thread scratch data. In general you probably want Dqn_Thread_GetScratch()
|
||||
// which ensures you get a usable scratch arena for temporary allocations
|
||||
// without having to worry about selecting the right arena from the state.
|
||||
//
|
||||
// @proc Dqn_Thread_GetScratch
|
||||
// @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
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
// @param[in] conflict_arena A pointer to the arena currently being used in the
|
||||
// function
|
||||
|
||||
#if !defined(DQN_THREAD_CONTEXT_ARENAS)
|
||||
#define DQN_THREAD_CONTEXT_ARENAS 2
|
||||
#endif
|
||||
|
||||
struct Dqn_ThreadContext
|
||||
{
|
||||
Dqn_b32 init;
|
||||
|
||||
Dqn_Arena *arena; ///< Per thread arena
|
||||
Dqn_Allocator allocator; ///< Allocator that uses the arena
|
||||
|
||||
/// Temp memory arena's for the calling thread
|
||||
Dqn_Arena *temp_arenas[DQN_THREAD_CONTEXT_ARENAS];
|
||||
|
||||
/// Allocators that use the corresponding arena from the thread context.
|
||||
/// Provided for convenience when interfacing with allocator interfaces.
|
||||
Dqn_Allocator temp_allocators[DQN_THREAD_CONTEXT_ARENAS];
|
||||
|
||||
#if defined(DQN_DEBUG_THREAD_CONTEXT)
|
||||
Dqn_ArenaStat temp_arenas_stat[DQN_THREAD_CONTEXT_ARENAS];
|
||||
#endif
|
||||
};
|
||||
|
||||
struct Dqn_ThreadScratch
|
||||
{
|
||||
Dqn_ThreadScratch(DQN_LEAK_TRACE_FUNCTION Dqn_ThreadContext *context, uint8_t context_index);
|
||||
~Dqn_ThreadScratch();
|
||||
|
||||
/// Index into the arena/allocator/stat array in the thread context
|
||||
/// specifying what arena was assigned.
|
||||
uint8_t index;
|
||||
Dqn_Allocator allocator;
|
||||
Dqn_Arena *arena;
|
||||
Dqn_b32 destructed = false; /// Detect copies of the scratch
|
||||
Dqn_ArenaTempMemory temp_memory;
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
Dqn_CallSite leak_site__;
|
||||
#endif
|
||||
};
|
||||
|
||||
// NOTE: Context ===================================================================================
|
||||
#define Dqn_Thread_GetContext() Dqn_Thread_GetContext_(DQN_LEAK_TRACE_NO_COMMA)
|
||||
#define Dqn_Thread_GetScratch(conflict_arena) Dqn_Thread_GetScratch_(DQN_LEAK_TRACE conflict_arena)
|
||||
DQN_API uint32_t Dqn_Thread_GetID();
|
||||
|
||||
// NOTE: Internal ==================================================================================
|
||||
DQN_API Dqn_ThreadContext *Dqn_Thread_GetContext_(DQN_LEAK_TRACE_FUNCTION_NO_COMMA);
|
||||
DQN_API Dqn_ThreadScratch Dqn_Thread_GetScratch_(DQN_LEAK_TRACE_FUNCTION void const *conflict_arena);
|
||||
|
||||
// NOTE: [$BSEA] Dqn_BinarySearch ==================================================================
|
||||
template <typename T>
|
||||
using Dqn_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs);
|
||||
|
||||
template <typename T>
|
||||
DQN_FORCE_INLINE bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
|
||||
|
||||
enum Dqn_BinarySearchType
|
||||
{
|
||||
/// Index of the match. If no match is found, found is set to false and the
|
||||
/// index is set to 0
|
||||
Dqn_BinarySearchType_Match,
|
||||
|
||||
/// Index after the match. If no match is found, found is set to false and
|
||||
/// the index is set to one past the closest match.
|
||||
Dqn_BinarySearchType_OnePastMatch,
|
||||
};
|
||||
|
||||
struct Dqn_BinarySearchResult
|
||||
{
|
||||
bool found;
|
||||
Dqn_usize index;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Dqn_BinarySearchResult
|
||||
Dqn_BinarySearch(T const *array,
|
||||
Dqn_usize array_size,
|
||||
T const &find,
|
||||
Dqn_BinarySearchType type = Dqn_BinarySearchType_Match,
|
||||
Dqn_BinarySearchLessThanProc<T> less_than = Dqn_BinarySearch_DefaultLessThan);
|
||||
|
||||
|
||||
template <typename T> DQN_FORCE_INLINE bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs)
|
||||
{
|
||||
bool result = lhs < rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Dqn_BinarySearchResult
|
||||
Dqn_BinarySearch(T const *array,
|
||||
Dqn_usize array_size,
|
||||
T const &find,
|
||||
Dqn_BinarySearchType type,
|
||||
Dqn_BinarySearchLessThanProc<T> less_than)
|
||||
{
|
||||
Dqn_BinarySearchResult result = {};
|
||||
Dqn_usize head = 0;
|
||||
Dqn_usize tail = array_size - 1;
|
||||
if (array && array_size > 0) {
|
||||
while (!result.found && head <= tail) {
|
||||
Dqn_usize mid = (head + tail) / 2;
|
||||
T const &value = array[mid];
|
||||
if (less_than(find, value)) {
|
||||
tail = mid - 1;
|
||||
if (mid == 0)
|
||||
break;
|
||||
} else if (less_than(value, find)) {
|
||||
head = mid + 1;
|
||||
} else {
|
||||
result.found = true;
|
||||
result.index = mid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type == Dqn_BinarySearchType_OnePastMatch)
|
||||
result.index = result.found ? result.index + 1 : tail + 1;
|
||||
else
|
||||
DQN_ASSERT(type == Dqn_BinarySearchType_Match);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
837
dqn_platform.cpp
837
dqn_platform.cpp
File diff suppressed because it is too large
Load Diff
315
dqn_platform.h
315
dqn_platform.h
@ -1,14 +1,91 @@
|
||||
// NOTE: Table Of Contents =========================================================================
|
||||
// Index | Disable #define | Description
|
||||
// =================================================================================================
|
||||
// [$FSYS] Dqn_Fs | | Filesystem helpers
|
||||
// [$DATE] Dqn_Date | | Date-time helpers
|
||||
// [$W32H] Win32 Min Header | DQN_NO_WIN32_MIN_HEADER | Minimal windows.h subset
|
||||
// [$WIND] Dqn_Win | | Windows OS helpers
|
||||
// [$WINN] Dqn_WinNet | DQN_NO_WINNET | Windows internet download/query helpers
|
||||
// [$OSYS] Dqn_OS | DQN_NO_WIN | Operating-system APIs
|
||||
// =================================================================================================
|
||||
// NOTE: [$PRIN] Dqn_Print =========================================================================
|
||||
enum Dqn_PrintStd
|
||||
{
|
||||
Dqn_PrintStd_Out,
|
||||
Dqn_PrintStd_Err,
|
||||
};
|
||||
|
||||
enum Dqn_PrintBold
|
||||
{
|
||||
Dqn_PrintBold_No,
|
||||
Dqn_PrintBold_Yes,
|
||||
};
|
||||
|
||||
struct Dqn_PrintStyle
|
||||
{
|
||||
Dqn_PrintBold bold;
|
||||
bool colour;
|
||||
uint8_t r, g, b;
|
||||
};
|
||||
|
||||
enum Dqn_PrintESCColour
|
||||
{
|
||||
Dqn_PrintESCColour_Fg,
|
||||
Dqn_PrintESCColour_Bg,
|
||||
};
|
||||
|
||||
// NOTE: Print Style ===============================================================================
|
||||
DQN_API Dqn_PrintStyle Dqn_Print_StyleColour (uint8_t r, uint8_t g, uint8_t b, Dqn_PrintBold bold);
|
||||
DQN_API Dqn_PrintStyle Dqn_Print_StyleColourU32 (uint32_t rgb, Dqn_PrintBold bold);
|
||||
DQN_API Dqn_PrintStyle Dqn_Print_StyleBold ();
|
||||
|
||||
// NOTE: Print Standard Out ========================================================================
|
||||
#define Dqn_Print(string) Dqn_Print_Std(Dqn_PrintStd_Out, string)
|
||||
#define Dqn_Print_F(fmt, ...) Dqn_Print_StdF(Dqn_PrintStd_Out, fmt, ## __VA_ARGS__)
|
||||
#define Dqn_Print_FV(fmt, args) Dqn_Print_StdFV(Dqn_PrintStd_Out, fmt, args)
|
||||
|
||||
#define Dqn_Print_Style(style, string) Dqn_Print_StdStyle(Dqn_PrintStd_Out, style, string)
|
||||
#define Dqn_Print_FStyle(style, fmt, ...) Dqn_Print_StdFStyle(Dqn_PrintStd_Out, style, fmt, ## __VA_ARGS__)
|
||||
#define Dqn_Print_FVStyle(style, fmt, args, ...) Dqn_Print_StdFVStyle(Dqn_PrintStd_Out, style, fmt, args)
|
||||
|
||||
#define Dqn_Print_Ln(string) Dqn_Print_StdLn(Dqn_PrintStd_Out, string)
|
||||
#define Dqn_Print_LnF(fmt, ...) Dqn_Print_StdLnF(Dqn_PrintStd_Out, fmt, ## __VA_ARGS__)
|
||||
#define Dqn_Print_LnFV(fmt, args) Dqn_Print_StdLnFV(Dqn_PrintStd_Out, fmt, args)
|
||||
|
||||
#define Dqn_Print_LnStyle(style, string) Dqn_Print_StdLnStyle(Dqn_PrintStd_Out, style, string);
|
||||
#define Dqn_Print_LnFStyle(style, fmt, ...) Dqn_Print_StdLnFStyle(Dqn_PrintStd_Out, style, fmt, ## __VA_ARGS__);
|
||||
#define Dqn_Print_LnFVStyle(style, fmt, args) Dqn_Print_StdLnFVStyle(Dqn_PrintStd_Out, style, fmt, args);
|
||||
|
||||
// NOTE: Print =====================================================================================
|
||||
DQN_API void Dqn_Print_Std (Dqn_PrintStd std_handle, Dqn_String8 string);
|
||||
DQN_API void Dqn_Print_StdF (Dqn_PrintStd std_handle, char const *fmt, ...);
|
||||
DQN_API void Dqn_Print_StdFV (Dqn_PrintStd std_handle, char const *fmt, va_list args);
|
||||
|
||||
DQN_API void Dqn_Print_StdStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string);
|
||||
DQN_API void Dqn_Print_StdFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, ...);
|
||||
DQN_API void Dqn_Print_StdFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, va_list args);
|
||||
|
||||
DQN_API void Dqn_Print_StdLn (Dqn_PrintStd std_handle, Dqn_String8 string);
|
||||
DQN_API void Dqn_Print_StdLnF (Dqn_PrintStd std_handle, char const *fmt, ...);
|
||||
DQN_API void Dqn_Print_StdLnFV (Dqn_PrintStd std_handle, char const *fmt, va_list args);
|
||||
|
||||
DQN_API void Dqn_Print_StdLnStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string);
|
||||
DQN_API void Dqn_Print_StdLnFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, ...);
|
||||
DQN_API void Dqn_Print_StdLnFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, va_list args);
|
||||
|
||||
// NOTE: ANSI Formatting Codes =====================================================================
|
||||
Dqn_String8 Dqn_Print_ESCColourString (Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b);
|
||||
Dqn_String8 Dqn_Print_ESCColourU32String(Dqn_PrintESCColour colour, uint32_t value);
|
||||
|
||||
#define Dqn_Print_ESCColourFgString(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Fg, r, g, b)
|
||||
#define Dqn_Print_ESCColourBgString(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Bg, r, g, b)
|
||||
#define Dqn_Print_ESCColourFg(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Fg, r, g, b).data
|
||||
#define Dqn_Print_ESCColourBg(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Bg, r, g, b).data
|
||||
|
||||
#define Dqn_Print_ESCColourFgU32String(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Fg, value)
|
||||
#define Dqn_Print_ESCColourBgU32String(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Bg, value)
|
||||
#define Dqn_Print_ESCColourFgU32(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Fg, value).data
|
||||
#define Dqn_Print_ESCColourBgU32(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Bg, value).data
|
||||
|
||||
#define Dqn_Print_ESCReset "\x1b[0m"
|
||||
#define Dqn_Print_ESCBold "\x1b[1m"
|
||||
#define Dqn_Print_ESCResetString DQN_STRING8(Dqn_Print_ESCReset)
|
||||
#define Dqn_Print_ESCBoldString DQN_STRING8(Dqn_Print_ESCBold)
|
||||
|
||||
#if !defined(DQN_NO_FS)
|
||||
#if defined(DQN_OS_WIN32) && defined(DQN_NO_WIN)
|
||||
#error "Filesystem APIs requires Windows API, DQN_NO_WIN must not be defined"
|
||||
#endif
|
||||
// NOTE: [$FSYS] Dqn_Fs ============================================================================
|
||||
// NOTE: FS Manipulation =======================================================
|
||||
// TODO(dqn): We should have a Dqn_String8 interface and a CString interface
|
||||
@ -52,15 +129,10 @@ DQN_API bool Dqn_Fs_Delete (Dqn_String8 path);
|
||||
// @proc Dqn_Fs_ReadString8, Dqn_Fs_ReadCString8
|
||||
// @desc Read the file at the path to a string.
|
||||
|
||||
#define Dqn_Fs_ReadCString8(path, path_size, file_size, allocator) Dqn_Fs_ReadCString8_(DQN_LEAK_TRACE path, path_size, file_size, allocator)
|
||||
#define Dqn_Fs_ReadString8(path, allocator) Dqn_Fs_ReadString8_(DQN_LEAK_TRACE path, allocator)
|
||||
|
||||
DQN_API bool Dqn_Fs_WriteCString8(char const *file_path, Dqn_usize file_path_size, char const *buffer, Dqn_usize buffer_size);
|
||||
DQN_API bool Dqn_Fs_WriteString8 (Dqn_String8 file_path, Dqn_String8 buffer);
|
||||
|
||||
// NOTE: Internal ==================================================================================
|
||||
DQN_API char *Dqn_Fs_ReadCString8_(DQN_LEAK_TRACE_FUNCTION char const *path, Dqn_usize path_size, Dqn_usize *file_size, Dqn_Allocator allocator);
|
||||
DQN_API Dqn_String8 Dqn_Fs_ReadString8_ (DQN_LEAK_TRACE_FUNCTION Dqn_String8 path, Dqn_Allocator allocator);
|
||||
DQN_API bool Dqn_Fs_Write (Dqn_String8 file_path, Dqn_String8 buffer);
|
||||
DQN_API char *Dqn_Fs_ReadCString8 (char const *path, Dqn_usize path_size, Dqn_usize *file_size, Dqn_Allocator allocator);
|
||||
DQN_API Dqn_String8 Dqn_Fs_Read (Dqn_String8 path, Dqn_Allocator allocator);
|
||||
|
||||
// NOTE: R/W Stream API ============================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
@ -100,6 +172,7 @@ enum Dqn_FsFileAccess
|
||||
DQN_API Dqn_FsFile Dqn_Fs_OpenFile (Dqn_String8 path, Dqn_FsFileOpen open_mode, uint32_t access);
|
||||
DQN_API bool Dqn_Fs_WriteFile(Dqn_FsFile *file, char const *buffer, Dqn_usize size);
|
||||
DQN_API void Dqn_Fs_CloseFile(Dqn_FsFile *file);
|
||||
#endif // !defined(DQN_NO_FS)
|
||||
|
||||
// NOTE: File system paths =========================================================================
|
||||
// Helper data structure for building paths suitable for OS consumption.
|
||||
@ -122,8 +195,8 @@ DQN_API void Dqn_Fs_CloseFile(Dqn_FsFile *file);
|
||||
// For example "path/to/your/desired/folder" popped produces
|
||||
// "path/to/your/desired"
|
||||
|
||||
// @proc Dqn_FsPath_ConvertString8
|
||||
// @desc Convert the path specified in the string to the OS native separated
|
||||
// @proc Dqn_FsPath_Convert
|
||||
// @desc Convert the path specified in the string to the OS native separated
|
||||
// path.
|
||||
|
||||
#if !defined(Dqn_FsPathOSSeperator)
|
||||
@ -152,9 +225,11 @@ struct Dqn_FsPath
|
||||
|
||||
DQN_API bool Dqn_FsPath_AddRef (Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_String8 path);
|
||||
DQN_API bool Dqn_FsPath_Add (Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_String8 path);
|
||||
DQN_API bool Dqn_FsPath_AddF (Dqn_Arena *arena, Dqn_FsPath *fs_path, char const *fmt, ...);
|
||||
DQN_API bool Dqn_FsPath_Pop (Dqn_FsPath *fs_path);
|
||||
DQN_API Dqn_String8 Dqn_FsPath_BuildWithSeparator(Dqn_Arena *arena, Dqn_FsPath const *fs_path, Dqn_String8 path_separator);
|
||||
DQN_API Dqn_String8 Dqn_FsPath_ConvertString8 (Dqn_Arena *arena, Dqn_String8 path);
|
||||
DQN_API Dqn_String8 Dqn_FsPath_Convert (Dqn_Arena *arena, Dqn_String8 path);
|
||||
DQN_API Dqn_String8 Dqn_FsPath_ConvertF (Dqn_Arena *arena, char const *fmt, ...);
|
||||
#define Dqn_FsPath_BuildFwdSlash(arena, fs_path) Dqn_FsPath_BuildWithSeparator(arena, fs_path, DQN_STRING8("/"))
|
||||
#define Dqn_FsPath_BuildBackSlash(arena, fs_path) Dqn_FsPath_BuildWithSeparator(arena, fs_path, DQN_STRING8("\\"))
|
||||
|
||||
@ -197,39 +272,8 @@ DQN_API Dqn_DateHMSTimeString Dqn_Date_HMSLocalTimeStringNow(char date_separator
|
||||
DQN_API Dqn_DateHMSTimeString Dqn_Date_HMSLocalTimeString (Dqn_DateHMSTime time, char date_separator = '-', char hms_separator = ':');
|
||||
DQN_API uint64_t Dqn_Date_EpochTime ();
|
||||
|
||||
// NOTE: [$W32H] Win32 Min Header ==================================================================
|
||||
#if defined(DQN_OS_WIN32)
|
||||
|
||||
#if !defined(DQN_NO_WIN32_MIN_HEADER) && !defined(_INC_WINDOWS)
|
||||
#if defined(DQN_COMPILER_W32_MSVC)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4201) // warning C4201: nonstandard extension used: nameless struct/union
|
||||
#endif
|
||||
// Taken from Windows.h
|
||||
// typedef unsigned long DWORD;
|
||||
// typedef unsigned short WORD;
|
||||
// typedef int BOOL;
|
||||
// typedef void * HWND;
|
||||
// typedef void * HANDLE;
|
||||
// typedef long NTSTATUS;
|
||||
|
||||
typedef void * HMODULE;
|
||||
typedef union {
|
||||
struct {
|
||||
unsigned long LowPart;
|
||||
long HighPart;
|
||||
};
|
||||
struct {
|
||||
unsigned long LowPart;
|
||||
long HighPart;
|
||||
} u;
|
||||
uint64_t QuadPart;
|
||||
} LARGE_INTEGER;
|
||||
#if defined(DQN_COMPILER_W32_MSVC)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
#endif // !defined(DQN_NO_WIN32_MIN_HEADER) && !defined(_INC_WINDOWS)
|
||||
|
||||
#if !defined(DQN_NO_WIN)
|
||||
// NOTE: [$WIND] Dqn_Win ===========================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_Win_LastErrorToBuffer, Dqn_Win_LastError
|
||||
@ -316,6 +360,7 @@ DQN_API Dqn_String8 Dqn_Win_WorkingDir (Dqn_Allocator allocator, Dqn_Stri
|
||||
DQN_API Dqn_String16 Dqn_Win_WorkingDirW (Dqn_Allocator allocator, Dqn_String16 suffix);
|
||||
DQN_API bool Dqn_Win_FolderIterate (Dqn_String8 path, Dqn_Win_FolderIterator *it);
|
||||
DQN_API bool Dqn_Win_FolderWIterate(Dqn_String16 path, Dqn_Win_FolderIteratorW *it);
|
||||
#endif // !defined(DQN_NO_WIN)
|
||||
|
||||
#if !defined(DQN_NO_WINNET)
|
||||
// NOTE: [$WINN] Dqn_WinNet ========================================================================
|
||||
@ -435,6 +480,20 @@ DQN_API Dqn_String8 Dqn_Win_NetHandlePumpToAllocString (Dqn_W
|
||||
// @desc Retrieve the executable directory without the trailing '/' or
|
||||
// ('\' for windows). If this fails an empty string is returned.
|
||||
|
||||
// @proc Dqn_OS_PerfCounterFrequency
|
||||
// @desc Get the number of ticks in the performance counter per second for the
|
||||
// operating system you're running on. This value can be used to calculate
|
||||
// duration from OS performance counter ticks.
|
||||
|
||||
// @proc Dqn_OS_EstimateTSCPerSecond
|
||||
// @desc Estimate how many timestamp count's (TSC) there are per second. TSC
|
||||
// is evaluated by calling __rdtsc() or the equivalent on the platform. This
|
||||
// value can be used to convert TSC durations into seconds.
|
||||
//
|
||||
// @param duration_ms_to_gauge_tsc_frequency How many milliseconds to spend
|
||||
// measuring the TSC rate of the current machine. 100ms is sufficient to
|
||||
// produce a fairly accurate result with minimal blocking in applications.
|
||||
|
||||
/// Record time between two time-points using the OS's performance counter.
|
||||
struct Dqn_OSTimer
|
||||
{
|
||||
@ -442,74 +501,96 @@ struct Dqn_OSTimer
|
||||
uint64_t end;
|
||||
};
|
||||
|
||||
DQN_API bool Dqn_OS_SecureRNGBytes (void *buffer, uint32_t size);
|
||||
DQN_API Dqn_String8 Dqn_OS_EXEDir (Dqn_Allocator allocator);
|
||||
DQN_API void Dqn_OS_SleepMs (Dqn_uint milliseconds);
|
||||
DQN_API uint64_t Dqn_OS_PerfCounterNow ();
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterS (uint64_t begin, uint64_t end);
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterMs (uint64_t begin, uint64_t end);
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterMicroS(uint64_t begin, uint64_t end);
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterNs (uint64_t begin, uint64_t end);
|
||||
DQN_API Dqn_OSTimer Dqn_OS_TimerBegin ();
|
||||
DQN_API void Dqn_OS_TimerEnd (Dqn_OSTimer *timer);
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerS (Dqn_OSTimer timer);
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerMs (Dqn_OSTimer timer);
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerMicroS (Dqn_OSTimer timer);
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerNs (Dqn_OSTimer timer);
|
||||
DQN_API bool Dqn_OS_SecureRNGBytes (void *buffer, uint32_t size);
|
||||
#if (defined(DQN_OS_WIN32) && !defined(DQN_NO_WIN)) || !defined(DQN_OS_WIN32)
|
||||
DQN_API Dqn_String8 Dqn_OS_EXEDir (Dqn_Allocator allocator);
|
||||
#endif
|
||||
DQN_API void Dqn_OS_SleepMs (Dqn_uint milliseconds);
|
||||
DQN_API uint64_t Dqn_OS_PerfCounterNow ();
|
||||
DQN_API uint64_t Dqn_OS_PerfCounterFrequency();
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterS (uint64_t begin, uint64_t end);
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterMs (uint64_t begin, uint64_t end);
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterMicroS (uint64_t begin, uint64_t end);
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterNs (uint64_t begin, uint64_t end);
|
||||
DQN_API Dqn_OSTimer Dqn_OS_TimerBegin ();
|
||||
DQN_API void Dqn_OS_TimerEnd (Dqn_OSTimer *timer);
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerS (Dqn_OSTimer timer);
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerMs (Dqn_OSTimer timer);
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerMicroS (Dqn_OSTimer timer);
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerNs (Dqn_OSTimer timer);
|
||||
DQN_API uint64_t Dqn_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_tsc_frequency);
|
||||
|
||||
// OS_TimedBlock provides a extremely primitive way of measuring the duration of
|
||||
// code blocks, by sprinkling DQN_OS_TIMED_BLOCK_RECORD("record label"), you can
|
||||
// measure the time between the macro and the next record call.
|
||||
// NOTE: [$TCTX] Dqn_ThreadContext =================================================================
|
||||
// Each thread is assigned in their thread-local storage (TLS) scratch 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.
|
||||
//
|
||||
// Example: Record the duration of the for-loop below and print it at the end.
|
||||
/*
|
||||
int main()
|
||||
{
|
||||
DQN_OS_TIMED_BLOCK_INIT("Profiling Region", 32); // name, records to allocate
|
||||
DQN_OS_TIMED_BLOCK_RECORD("a");
|
||||
for (int unused1_ = 0; unused1_ < 1000000; unused1_++)
|
||||
{
|
||||
for (int unused2_ = 0; unused2_ < 1000000; unused2_++)
|
||||
{
|
||||
(void)unused1_;
|
||||
(void)unused2_;
|
||||
}
|
||||
}
|
||||
DQN_OS_TIMED_BLOCK_RECORD("b");
|
||||
DQN_OS_TIMED_BLOCK_DUMP;
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
struct Dqn_OSTimedBlock
|
||||
// TLS in this implementation is implemented using the `thread_local` C/C++
|
||||
// keyword.
|
||||
//
|
||||
// NOTE: API
|
||||
//
|
||||
// @proc Dqn_Thread_GetContext
|
||||
// @desc Get the current thread's context- this contains all the metadata for managing
|
||||
// the thread scratch data. In general you probably want Dqn_Thread_GetScratch()
|
||||
// which ensures you get a usable scratch arena for temporary allocations
|
||||
// without having to worry about selecting the right arena from the state.
|
||||
//
|
||||
// @proc Dqn_Thread_GetScratch
|
||||
// @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
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
// @param[in] conflict_arena A pointer to the arena currently being used in the
|
||||
// function
|
||||
|
||||
#if !defined(DQN_THREAD_CONTEXT_ARENAS)
|
||||
#define DQN_THREAD_CONTEXT_ARENAS 2
|
||||
#endif
|
||||
|
||||
struct Dqn_ThreadContext
|
||||
{
|
||||
char const *label;
|
||||
uint64_t tick;
|
||||
Dqn_b32 init;
|
||||
|
||||
Dqn_Arena *arena; ///< Per thread arena
|
||||
Dqn_Allocator allocator; ///< Allocator that uses the arena
|
||||
|
||||
/// Temp memory arena's for the calling thread
|
||||
Dqn_Arena *temp_arenas[DQN_THREAD_CONTEXT_ARENAS];
|
||||
|
||||
/// Allocators that use the corresponding arena from the thread context.
|
||||
/// Provided for convenience when interfacing with allocator interfaces.
|
||||
Dqn_Allocator temp_allocators[DQN_THREAD_CONTEXT_ARENAS];
|
||||
|
||||
#if defined(DQN_DEBUG_THREAD_CONTEXT)
|
||||
Dqn_ArenaStat temp_arenas_stat[DQN_THREAD_CONTEXT_ARENAS];
|
||||
#endif
|
||||
};
|
||||
|
||||
// Initialise a timing block region,
|
||||
#define DQN_OS_TIMED_BLOCK_INIT(label, size) \
|
||||
Dqn_OSTimedBlock timings_[size]; \
|
||||
Dqn_usize timings_size_ = 0; \
|
||||
DQN_OS_TIMED_BLOCK_RECORD(label)
|
||||
struct Dqn_ThreadScratch
|
||||
{
|
||||
Dqn_ThreadScratch(Dqn_ThreadContext *context, uint8_t context_index);
|
||||
~Dqn_ThreadScratch();
|
||||
|
||||
// Add a timing record at the current location this macro is invoked.
|
||||
// DQN_OS_TIMED_BLOCK_INIT must have been called in a scope visible to the macro
|
||||
// prior.
|
||||
// label: The label to give to the timing record
|
||||
#define DQN_OS_TIMED_BLOCK_RECORD(label) timings_[timings_size_++] = {label, Dqn_OS_PerfCounterNow()}
|
||||
/// Index into the arena/allocator/stat array in the thread context
|
||||
/// specifying what arena was assigned.
|
||||
uint8_t index;
|
||||
Dqn_Allocator allocator;
|
||||
Dqn_Arena *arena;
|
||||
Dqn_b32 destructed = false; /// Detect copies of the scratch
|
||||
Dqn_ArenaTempMemory temp_memory;
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
Dqn_CallSite leak_site__;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Dump the timing block via Dqn_Log
|
||||
#define DQN_OS_TIMED_BLOCK_DUMP \
|
||||
DQN_ASSERTF(timings_size_ < sizeof(timings_) / sizeof(timings_[0]), \
|
||||
"Timings array indexed out-of-bounds, use a bigger size"); \
|
||||
for (int timings_index_ = 0; timings_index_ < (timings_size_ - 1); timings_index_++) { \
|
||||
Dqn_OSTimedBlock t1 = timings_[timings_index_ + 0]; \
|
||||
Dqn_OSTimedBlock t2 = timings_[timings_index_ + 1]; \
|
||||
DQN_LOG_D("%s -> %s: %fms", t1.label, t2.label, Dqn_OS_PerfCounterMs(t1.tick, t2.tick)); \
|
||||
} \
|
||||
\
|
||||
if (timings_size_ >= 1) { \
|
||||
Dqn_OSTimedBlock t1 = timings_[0]; \
|
||||
Dqn_OSTimedBlock t2 = timings_[timings_size_ - 1]; \
|
||||
DQN_LOG_D("%s -> %s (total): %fms", t1.label, t2.label, Dqn_OS_PerfCounterMs(t1.tick, t2.tick));\
|
||||
}
|
||||
// NOTE: Context ===================================================================================
|
||||
DQN_API uint32_t Dqn_Thread_GetID();
|
||||
DQN_API Dqn_ThreadContext *Dqn_Thread_GetContext();
|
||||
DQN_API Dqn_ThreadScratch Dqn_Thread_GetScratch(void const *conflict_arena);
|
||||
|
341
dqn_print.cpp
341
dqn_print.cpp
@ -1,341 +0,0 @@
|
||||
// NOTE: [$PRIN] Dqn_Print =========================================================================
|
||||
DQN_API Dqn_PrintStyle Dqn_Print_StyleColour(uint8_t r, uint8_t g, uint8_t b, Dqn_PrintBold bold)
|
||||
{
|
||||
Dqn_PrintStyle result = {};
|
||||
result.bold = bold;
|
||||
result.colour = true;
|
||||
result.r = r;
|
||||
result.g = g;
|
||||
result.b = b;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_PrintStyle Dqn_Print_StyleColourU32(uint32_t rgb, Dqn_PrintBold bold)
|
||||
{
|
||||
uint8_t r = (rgb >> 24) & 0xFF;
|
||||
uint8_t g = (rgb >> 16) & 0xFF;
|
||||
uint8_t b = (rgb >> 8) & 0xFF;
|
||||
Dqn_PrintStyle result = Dqn_Print_StyleColour(r, g, b, bold);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_PrintStyle Dqn_Print_StyleBold()
|
||||
{
|
||||
Dqn_PrintStyle result = {};
|
||||
result.bold = Dqn_PrintBold_Yes;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Print_Std(Dqn_PrintStd std_handle, Dqn_String8 string)
|
||||
{
|
||||
DQN_ASSERT(std_handle == Dqn_PrintStd_Out || std_handle == Dqn_PrintStd_Err);
|
||||
|
||||
#if defined(DQN_OS_WIN32)
|
||||
// NOTE: Get the output handles from kernel
|
||||
// =========================================================================
|
||||
DQN_THREAD_LOCAL void *std_out_print_handle = nullptr;
|
||||
DQN_THREAD_LOCAL void *std_err_print_handle = nullptr;
|
||||
DQN_THREAD_LOCAL bool std_out_print_to_console = false;
|
||||
DQN_THREAD_LOCAL bool std_err_print_to_console = false;
|
||||
|
||||
if (!std_out_print_handle) {
|
||||
unsigned long mode = 0; (void)mode;
|
||||
std_out_print_handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
std_out_print_to_console = GetConsoleMode(std_out_print_handle, &mode) != 0;
|
||||
|
||||
std_err_print_handle = GetStdHandle(STD_ERROR_HANDLE);
|
||||
std_err_print_to_console = GetConsoleMode(std_err_print_handle, &mode) != 0;
|
||||
}
|
||||
|
||||
// NOTE: Select the output handle
|
||||
// =========================================================================
|
||||
void *print_handle = std_out_print_handle;
|
||||
bool print_to_console = std_out_print_to_console;
|
||||
if (std_handle == Dqn_PrintStd_Err) {
|
||||
print_handle = std_err_print_handle;
|
||||
print_to_console = std_err_print_to_console;
|
||||
}
|
||||
|
||||
// NOTE: Write the string
|
||||
// =========================================================================
|
||||
DQN_ASSERT(string.size < DQN_CAST(unsigned long)-1);
|
||||
unsigned long bytes_written = 0; (void)bytes_written;
|
||||
if (print_to_console) {
|
||||
WriteConsoleA(print_handle, string.data, DQN_CAST(unsigned long)string.size, &bytes_written, nullptr);
|
||||
} else {
|
||||
WriteFile(print_handle, string.data, DQN_CAST(unsigned long)string.size, &bytes_written, nullptr);
|
||||
}
|
||||
#else
|
||||
fprintf(std_handle == Dqn_PrintStd_Out ? stdout : stderr, "%.*s", DQN_STRING_FMT(string));
|
||||
#endif
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Print_StdStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string)
|
||||
{
|
||||
if (string.data && string.size) {
|
||||
if (style.colour)
|
||||
Dqn_Print_Std(std_handle, Dqn_Print_ESCColourFgString(style.r, style.g, style.b));
|
||||
if (style.bold == Dqn_PrintBold_Yes)
|
||||
Dqn_Print_Std(std_handle, Dqn_Print_ESCBoldString);
|
||||
Dqn_Print_Std(std_handle, string);
|
||||
if (style.colour || style.bold == Dqn_PrintBold_Yes)
|
||||
Dqn_Print_Std(std_handle, Dqn_Print_ESCResetString);
|
||||
}
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE char *Dqn_Print_VSPrintfChunker_(const char *buf, void *user, int len)
|
||||
{
|
||||
Dqn_String8 string = {};
|
||||
string.data = DQN_CAST(char *)buf;
|
||||
string.size = len;
|
||||
|
||||
Dqn_PrintStd std_handle = DQN_CAST(Dqn_PrintStd)DQN_CAST(uintptr_t)user;
|
||||
Dqn_Print_Std(std_handle, string);
|
||||
return (char *)buf;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Print_StdF(Dqn_PrintStd std_handle, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Print_StdFV(std_handle, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Print_StdFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Print_StdFVStyle(std_handle, style, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Print_StdFV(Dqn_PrintStd std_handle, char const *fmt, va_list args)
|
||||
{
|
||||
char buffer[STB_SPRINTF_MIN];
|
||||
STB_SPRINTF_DECORATE(vsprintfcb)(Dqn_Print_VSPrintfChunker_, DQN_CAST(void *)DQN_CAST(uintptr_t)std_handle, buffer, fmt, args);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Print_StdFVStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, va_list args)
|
||||
{
|
||||
if (fmt) {
|
||||
if (style.colour)
|
||||
Dqn_Print_Std(std_handle, Dqn_Print_ESCColourFgString(style.r, style.g, style.b));
|
||||
if (style.bold == Dqn_PrintBold_Yes)
|
||||
Dqn_Print_Std(std_handle, Dqn_Print_ESCBoldString);
|
||||
Dqn_Print_StdFV(std_handle, fmt, args);
|
||||
if (style.colour || style.bold == Dqn_PrintBold_Yes)
|
||||
Dqn_Print_Std(std_handle, Dqn_Print_ESCResetString);
|
||||
}
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Print_StdLn(Dqn_PrintStd std_handle, Dqn_String8 string)
|
||||
{
|
||||
Dqn_Print_Std(std_handle, string);
|
||||
Dqn_Print_Std(std_handle, DQN_STRING8("\n"));
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Print_StdLnF(Dqn_PrintStd std_handle, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Print_StdLnFV(std_handle, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Print_StdLnFV(Dqn_PrintStd std_handle, char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_Print_StdFV(std_handle, fmt, args);
|
||||
Dqn_Print_Std(std_handle, DQN_STRING8("\n"));
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Print_StdLnStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string)
|
||||
{
|
||||
Dqn_Print_StdStyle(std_handle, style, string);
|
||||
Dqn_Print_Std(std_handle, DQN_STRING8("\n"));
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Print_StdLnFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Print_StdLnFVStyle(std_handle, style, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Print_StdLnFVStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_Print_StdFVStyle(std_handle, style, fmt, args);
|
||||
Dqn_Print_Std(std_handle, DQN_STRING8("\n"));
|
||||
}
|
||||
|
||||
DQN_API Dqn_String8 Dqn_Print_ESCColourString(Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
DQN_THREAD_LOCAL char buffer[32];
|
||||
buffer[0] = 0;
|
||||
Dqn_String8 result = {};
|
||||
result.size = STB_SPRINTF_DECORATE(snprintf)(buffer,
|
||||
DQN_ARRAY_UCOUNT(buffer),
|
||||
"\x1b[%d;2;%u;%u;%um",
|
||||
colour == Dqn_PrintESCColour_Fg ? 38 : 48,
|
||||
r, g, b);
|
||||
result.data = buffer;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_String8 Dqn_Print_ESCColourU32String(Dqn_PrintESCColour colour, uint32_t value)
|
||||
{
|
||||
uint8_t r = DQN_CAST(uint8_t)(value >> 24);
|
||||
uint8_t g = DQN_CAST(uint8_t)(value >> 16);
|
||||
uint8_t b = DQN_CAST(uint8_t)(value >> 8);
|
||||
Dqn_String8 result = Dqn_Print_ESCColourString(colour, r, g, b);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$LLOG] Dqn_Log ==========================================================================
|
||||
DQN_API Dqn_String8 Dqn_Log_MakeString(Dqn_Allocator allocator,
|
||||
bool colour,
|
||||
Dqn_String8 type,
|
||||
int log_type,
|
||||
Dqn_CallSite call_site,
|
||||
char const *fmt,
|
||||
va_list args)
|
||||
{
|
||||
Dqn_usize header_size_no_ansi_codes = 0;
|
||||
Dqn_String8 header = {};
|
||||
{
|
||||
DQN_LOCAL_PERSIST Dqn_usize max_type_length = 0;
|
||||
max_type_length = DQN_MAX(max_type_length, type.size);
|
||||
int type_padding = DQN_CAST(int)(max_type_length - type.size);
|
||||
|
||||
Dqn_String8 colour_esc = {};
|
||||
Dqn_String8 bold_esc = {};
|
||||
Dqn_String8 reset_esc = {};
|
||||
if (colour) {
|
||||
bold_esc = Dqn_Print_ESCBoldString;
|
||||
reset_esc = Dqn_Print_ESCResetString;
|
||||
switch (log_type) {
|
||||
case Dqn_LogType_Debug: break;
|
||||
case Dqn_LogType_Info: colour_esc = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Info); break;
|
||||
case Dqn_LogType_Warning: colour_esc = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Warning); break;
|
||||
case Dqn_LogType_Error: colour_esc = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Error); break;
|
||||
}
|
||||
}
|
||||
|
||||
Dqn_String8 file_name = Dqn_String8_FileNameFromPath(call_site.file);
|
||||
Dqn_DateHMSTimeString const time = Dqn_Date_HMSLocalTimeStringNow();
|
||||
header = Dqn_String8_InitF(allocator,
|
||||
"%.*s " // date
|
||||
"%.*s " // hms
|
||||
"%.*s" // colour
|
||||
"%.*s" // bold
|
||||
"%.*s" // type
|
||||
"%*s" // type padding
|
||||
"%.*s" // reset
|
||||
" %.*s" // file name
|
||||
":%05u ", // line number
|
||||
time.date_size - 2, time.date + 2,
|
||||
time.hms_size, time.hms,
|
||||
colour_esc.size, colour_esc.data,
|
||||
bold_esc.size, bold_esc.data,
|
||||
type.size, type.data,
|
||||
type_padding, "",
|
||||
reset_esc.size, reset_esc.data,
|
||||
file_name.size, file_name.data,
|
||||
call_site.line);
|
||||
header_size_no_ansi_codes = header.size - colour_esc.size - Dqn_Print_ESCResetString.size;
|
||||
}
|
||||
|
||||
// NOTE: Header padding
|
||||
// =========================================================================
|
||||
Dqn_usize header_padding = 0;
|
||||
{
|
||||
DQN_LOCAL_PERSIST Dqn_usize max_header_length = 0;
|
||||
max_header_length = DQN_MAX(max_header_length, header_size_no_ansi_codes);
|
||||
header_padding = max_header_length - header_size_no_ansi_codes;
|
||||
}
|
||||
|
||||
// NOTE: Construct final log
|
||||
// =========================================================================
|
||||
Dqn_String8 user_msg = Dqn_String8_InitFV(allocator, fmt, args);
|
||||
Dqn_String8 result = Dqn_String8_Allocate(allocator, header.size + header_padding + user_msg.size, Dqn_ZeroMem_No);
|
||||
DQN_MEMCPY(result.data, header.data, header.size);
|
||||
DQN_MEMSET(result.data + header.size, ' ', header_padding);
|
||||
DQN_MEMCPY(result.data + header.size + header_padding, user_msg.data, user_msg.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_String8 type, int log_type, void *user_data, Dqn_CallSite call_site, char const *fmt, va_list args)
|
||||
{
|
||||
(void)log_type;
|
||||
(void)user_data;
|
||||
|
||||
// NOTE: Open log file for appending if requested
|
||||
// =========================================================================
|
||||
Dqn_TicketMutex_Begin(&dqn_library.log_file_mutex);
|
||||
if (dqn_library.log_to_file && !dqn_library.log_file) {
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_String8 exe_dir = Dqn_OS_EXEDir(scratch.allocator);
|
||||
Dqn_String8 log_file = Dqn_String8_InitF(scratch.allocator, "%.*s/dqn.log", DQN_STRING_FMT(exe_dir));
|
||||
fopen_s(DQN_CAST(FILE **)&dqn_library.log_file, log_file.data, "a");
|
||||
}
|
||||
Dqn_TicketMutex_End(&dqn_library.log_file_mutex);
|
||||
|
||||
// NOTE: Generate the log header
|
||||
// =========================================================================
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_String8 log_line = Dqn_Log_MakeString(scratch.allocator,
|
||||
!dqn_library.log_no_colour,
|
||||
type,
|
||||
log_type,
|
||||
call_site,
|
||||
fmt,
|
||||
args);
|
||||
|
||||
// NOTE: Print log
|
||||
// =========================================================================
|
||||
Dqn_Print_StdLn(Dqn_PrintStd_Out, log_line);
|
||||
|
||||
Dqn_TicketMutex_Begin(&dqn_library.log_file_mutex);
|
||||
if (dqn_library.log_to_file) {
|
||||
fprintf(DQN_CAST(FILE *)dqn_library.log_file, "%.*s\n", DQN_STRING_FMT(log_line));
|
||||
}
|
||||
Dqn_TicketMutex_End(&dqn_library.log_file_mutex);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Log_FVCallSite(Dqn_String8 type, Dqn_CallSite call_site, char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_LogProc *logging_function = dqn_library.log_callback ? dqn_library.log_callback : Dqn_Log_FVDefault_;
|
||||
logging_function(type, -1 /*log_type*/, dqn_library.log_user_data, call_site, fmt, args);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Log_FCallSite(Dqn_String8 type, Dqn_CallSite call_site, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Log_FVCallSite(type, call_site, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Log_TypeFVCallSite(Dqn_LogType type, Dqn_CallSite call_site, char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_String8 type_string = DQN_STRING8("DQN-BAD-LOG-TYPE");
|
||||
switch (type) {
|
||||
case Dqn_LogType_Error: type_string = DQN_STRING8("ERROR"); break;
|
||||
case Dqn_LogType_Info: type_string = DQN_STRING8("INFO"); break;
|
||||
case Dqn_LogType_Warning: type_string = DQN_STRING8("WARN"); break;
|
||||
case Dqn_LogType_Debug: type_string = DQN_STRING8("DEBUG"); break;
|
||||
case Dqn_LogType_Count: type_string = DQN_STRING8("BADXX"); break;
|
||||
}
|
||||
|
||||
Dqn_LogProc *logging_function = dqn_library.log_callback ? dqn_library.log_callback : Dqn_Log_FVDefault_;
|
||||
logging_function(type_string, type /*log_type*/, dqn_library.log_user_data, call_site, fmt, args);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Log_TypeFCallSite(Dqn_LogType type, Dqn_CallSite call_site, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Log_TypeFVCallSite(type, call_site, fmt, args);
|
||||
va_end(args);
|
||||
}
|
138
dqn_print.h
138
dqn_print.h
@ -1,138 +0,0 @@
|
||||
// NOTE: Table Of Contents =========================================================================
|
||||
// Index | Disable #define | Description
|
||||
// =================================================================================================
|
||||
// [$PRIN] Dqn_Print | | Console printing
|
||||
// [$LLOG] Dqn_Log | | Library logging
|
||||
// =================================================================================================
|
||||
|
||||
// NOTE: [$PRIN] Dqn_Print =========================================================================
|
||||
enum Dqn_PrintStd
|
||||
{
|
||||
Dqn_PrintStd_Out,
|
||||
Dqn_PrintStd_Err,
|
||||
};
|
||||
|
||||
enum Dqn_PrintBold
|
||||
{
|
||||
Dqn_PrintBold_No,
|
||||
Dqn_PrintBold_Yes,
|
||||
};
|
||||
|
||||
struct Dqn_PrintStyle
|
||||
{
|
||||
Dqn_PrintBold bold;
|
||||
bool colour;
|
||||
uint8_t r, g, b;
|
||||
};
|
||||
|
||||
enum Dqn_PrintESCColour
|
||||
{
|
||||
Dqn_PrintESCColour_Fg,
|
||||
Dqn_PrintESCColour_Bg,
|
||||
};
|
||||
|
||||
// NOTE: Print Style ===============================================================================
|
||||
DQN_API Dqn_PrintStyle Dqn_Print_StyleColour (uint8_t r, uint8_t g, uint8_t b, Dqn_PrintBold bold);
|
||||
DQN_API Dqn_PrintStyle Dqn_Print_StyleColourU32 (uint32_t rgb, Dqn_PrintBold bold);
|
||||
DQN_API Dqn_PrintStyle Dqn_Print_StyleBold ();
|
||||
|
||||
// NOTE: Print Standard Out ========================================================================
|
||||
#define Dqn_Print(string) Dqn_Print_Std(Dqn_PrintStd_Out, string)
|
||||
#define Dqn_Print_F(fmt, ...) Dqn_Print_StdF(Dqn_PrintStd_Out, fmt, ## __VA_ARGS__)
|
||||
#define Dqn_Print_FV(fmt, args) Dqn_Print_StdFV(Dqn_PrintStd_Out, fmt, args)
|
||||
|
||||
#define Dqn_Print_Style(style, string) Dqn_Print_StdStyle(Dqn_PrintStd_Out, style, string)
|
||||
#define Dqn_Print_FStyle(style, fmt, ...) Dqn_Print_StdFStyle(Dqn_PrintStd_Out, style, fmt, ## __VA_ARGS__)
|
||||
#define Dqn_Print_FVStyle(style, fmt, args, ...) Dqn_Print_StdFVStyle(Dqn_PrintStd_Out, style, fmt, args)
|
||||
|
||||
#define Dqn_Print_Ln(string) Dqn_Print_StdLn(Dqn_PrintStd_Out, string)
|
||||
#define Dqn_Print_LnF(fmt, ...) Dqn_Print_StdLnF(Dqn_PrintStd_Out, fmt, ## __VA_ARGS__)
|
||||
#define Dqn_Print_LnFV(fmt, args) Dqn_Print_StdLnFV(Dqn_PrintStd_Out, fmt, args)
|
||||
|
||||
#define Dqn_Print_LnStyle(style, string) Dqn_Print_StdLnStyle(Dqn_PrintStd_Out, style, string);
|
||||
#define Dqn_Print_LnFStyle(style, fmt, ...) Dqn_Print_StdLnFStyle(Dqn_PrintStd_Out, style, fmt, ## __VA_ARGS__);
|
||||
#define Dqn_Print_LnFVStyle(style, fmt, args) Dqn_Print_StdLnFVStyle(Dqn_PrintStd_Out, style, fmt, args);
|
||||
|
||||
// NOTE: Print =====================================================================================
|
||||
DQN_API void Dqn_Print_Std (Dqn_PrintStd std_handle, Dqn_String8 string);
|
||||
DQN_API void Dqn_Print_StdF (Dqn_PrintStd std_handle, char const *fmt, ...);
|
||||
DQN_API void Dqn_Print_StdFV (Dqn_PrintStd std_handle, char const *fmt, va_list args);
|
||||
|
||||
DQN_API void Dqn_Print_StdStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string);
|
||||
DQN_API void Dqn_Print_StdFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, ...);
|
||||
DQN_API void Dqn_Print_StdFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, va_list args);
|
||||
|
||||
DQN_API void Dqn_Print_StdLn (Dqn_PrintStd std_handle, Dqn_String8 string);
|
||||
DQN_API void Dqn_Print_StdLnF (Dqn_PrintStd std_handle, char const *fmt, ...);
|
||||
DQN_API void Dqn_Print_StdLnFV (Dqn_PrintStd std_handle, char const *fmt, va_list args);
|
||||
|
||||
DQN_API void Dqn_Print_StdLnStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_String8 string);
|
||||
DQN_API void Dqn_Print_StdLnFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, ...);
|
||||
DQN_API void Dqn_Print_StdLnFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, char const *fmt, va_list args);
|
||||
|
||||
// NOTE: ANSI Formatting Codes =====================================================================
|
||||
Dqn_String8 Dqn_Print_ESCColourString (Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b);
|
||||
Dqn_String8 Dqn_Print_ESCColourU32String(Dqn_PrintESCColour colour, uint32_t value);
|
||||
|
||||
#define Dqn_Print_ESCColourFgString(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Fg, r, g, b)
|
||||
#define Dqn_Print_ESCColourBgString(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Bg, r, g, b)
|
||||
#define Dqn_Print_ESCColourFg(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Fg, r, g, b).data
|
||||
#define Dqn_Print_ESCColourBg(r, g, b) Dqn_Print_ESCColourString(Dqn_PrintESCColour_Bg, r, g, b).data
|
||||
|
||||
#define Dqn_Print_ESCColourFgU32String(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Fg, value)
|
||||
#define Dqn_Print_ESCColourBgU32String(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Bg, value)
|
||||
#define Dqn_Print_ESCColourFgU32(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Fg, value).data
|
||||
#define Dqn_Print_ESCColourBgU32(value) Dqn_Print_ESCColourU32String(Dqn_PrintESCColour_Bg, value).data
|
||||
|
||||
#define Dqn_Print_ESCReset "\x1b[0m"
|
||||
#define Dqn_Print_ESCBold "\x1b[1m"
|
||||
#define Dqn_Print_ESCResetString DQN_STRING8(Dqn_Print_ESCReset)
|
||||
#define Dqn_Print_ESCBoldString DQN_STRING8(Dqn_Print_ESCBold)
|
||||
|
||||
// NOTE: [$LLOG] Dqn_Log ==========================================================================
|
||||
enum Dqn_LogType
|
||||
{
|
||||
Dqn_LogType_Debug,
|
||||
Dqn_LogType_Info,
|
||||
Dqn_LogType_Warning,
|
||||
Dqn_LogType_Error,
|
||||
Dqn_LogType_Count,
|
||||
};
|
||||
|
||||
/// RGBA
|
||||
#define Dqn_LogTypeColourU32_Info 0x00'87'ff'ff // Blue
|
||||
#define Dqn_LogTypeColourU32_Warning 0xff'ff'00'ff // Yellow
|
||||
#define Dqn_LogTypeColourU32_Error 0xff'00'00'ff // Red
|
||||
|
||||
/// The logging procedure of the library. Users can override the default logging
|
||||
/// function by setting the logging function pointer in Dqn_Library. This
|
||||
/// function will be invoked every time a log is recorded using the following
|
||||
/// functions.
|
||||
///
|
||||
/// @param[in] log_type This value is one of the Dqn_LogType values if the log
|
||||
/// was generated from one of the default categories. -1 if the log is not from
|
||||
/// one of the default categories.
|
||||
typedef void Dqn_LogProc(Dqn_String8 type, int log_type, void *user_data, Dqn_CallSite call_site, char const *fmt, va_list va);
|
||||
|
||||
#define Dqn_Log_DebugF(fmt, ...) Dqn_Log_TypeFCallSite(Dqn_LogType_Debug, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
#define Dqn_Log_InfoF(fmt, ...) Dqn_Log_TypeFCallSite(Dqn_LogType_Info, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
#define Dqn_Log_WarningF(fmt, ...) Dqn_Log_TypeFCallSite(Dqn_LogType_Warning, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
#define Dqn_Log_ErrorF(fmt, ...) Dqn_Log_TypeFCallSite(Dqn_LogType_Error, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
|
||||
#define Dqn_Log_DebugFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Debug, DQN_CALL_SITE, fmt, args)
|
||||
#define Dqn_Log_InfoFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Info, DQN_CALL_SITE, fmt, args)
|
||||
#define Dqn_Log_WarningFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Warning, DQN_CALL_SITE, fmt, args)
|
||||
#define Dqn_Log_ErrorFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Error, DQN_CALL_SITE, fmt, args)
|
||||
|
||||
#define Dqn_Log_TypeFV(type, fmt, args) Dqn_Log_TypeFVCallSite(type, DQN_CALL_SITE, fmt, args)
|
||||
#define Dqn_Log_TypeF(type, fmt, ...) Dqn_Log_TypeFCallSite(type, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
|
||||
#define Dqn_Log_FV(type, fmt, args) Dqn_Log_FVCallSite(type, DQN_CALL_SITE, fmt, args)
|
||||
#define Dqn_Log_F(type, fmt, ...) Dqn_Log_FCallSite(type, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
|
||||
DQN_API Dqn_String8 Dqn_Log_MakeString (Dqn_Allocator allocator, bool colour, Dqn_String8 type, int log_type, Dqn_CallSite call_site, char const *fmt, va_list args);
|
||||
DQN_API void Dqn_Log_TypeFVCallSite(Dqn_LogType type, Dqn_CallSite call_site, char const *fmt, va_list va);
|
||||
DQN_API void Dqn_Log_TypeFCallSite (Dqn_LogType type, Dqn_CallSite call_site, char const *fmt, ...);
|
||||
DQN_API void Dqn_Log_FVCallSite (Dqn_String8 type, Dqn_CallSite call_site, char const *fmt, va_list va);
|
||||
DQN_API void Dqn_Log_FCallSite (Dqn_String8 type, Dqn_CallSite call_site, char const *fmt, ...);
|
||||
|
2215
dqn_strings.cpp
2215
dqn_strings.cpp
File diff suppressed because it is too large
Load Diff
540
dqn_strings.h
540
dqn_strings.h
@ -1,17 +1,3 @@
|
||||
// NOTE: Table Of Contents =========================================================================
|
||||
// Index | Disable #define | Description
|
||||
// =================================================================================================
|
||||
// [$CSTR] Dqn_CString8 | | C-string helpers
|
||||
// [$STR8] Dqn_String8 | | Pointer and length strings
|
||||
// [$FSTR] Dqn_FString8 | DQN_NO_FSTRING8 | Fixed-size strings
|
||||
// [$STRB] Dqn_String8Builder | |
|
||||
// [$JSON] Dqn_JSONBuilder | DQN_NO_JSON_BUILDER | Construct json output
|
||||
// [$CHAR] Dqn_Char | | Character ascii/digit.. helpers
|
||||
// [$UTFX] Dqn_UTF | | Unicode helpers
|
||||
// [$BHEX] Dqn_Bin | DQN_NO_HEX | Binary <-> hex helpers
|
||||
// [$STBS] stb_sprintf | DQN_STB_SPRINTF_HEADER_ONLY | Portable sprintf
|
||||
// =================================================================================================
|
||||
|
||||
// NOTE: [$CSTR] Dqn_CString8 ======================================================================
|
||||
// @proc Dqn_CString8_ArrayCount
|
||||
// @desc Calculate the size of a cstring literal/array at compile time
|
||||
@ -192,8 +178,7 @@ DQN_API Dqn_usize Dqn_CString16_Size (wcha
|
||||
// @return The file name in the file path, if none is found, the original path
|
||||
// string is returned. Null pointer if arguments are null or invalid.
|
||||
|
||||
// @proc Dqn_String8_ToI64Checked, Dqn_String8_ToI64,
|
||||
// Dqn_String8_ToU64Checked, Dqn_String8_ToU64
|
||||
// @proc Dqn_String8_ToI64, Dqn_String8_ToU64
|
||||
// @desc Convert a number represented as a string to a signed 64 bit number.
|
||||
//
|
||||
// The `separator` is an optional digit separator for example, if `separator`
|
||||
@ -203,23 +188,14 @@ DQN_API Dqn_usize Dqn_CString16_Size (wcha
|
||||
// i.e. "+1234" -> 1234 and "-1234" -> -1234. Strings must consist entirely of
|
||||
// digits, the seperator or the permitted prefixes as previously mentioned
|
||||
// otherwise this function will return false, i.e. "1234 dog" will cause the
|
||||
// function to return false, however, the output is greedily converted and will
|
||||
// be evaluated to "1234".
|
||||
// function to return false, however, the output is greedily converted and
|
||||
// will be evaluated to "1234".
|
||||
//
|
||||
// `ToU64Checked` and `ToU64` only '+' prefix is permitted
|
||||
// `ToI64Checked` and `ToI64` both '+' and '-' prefix is permitted
|
||||
// `ToU64` only '+' prefix is permitted
|
||||
// `ToI64` either '+' or '-' prefix is permitted
|
||||
//
|
||||
// @param[in] buf The string to convert
|
||||
// @param[in] size The size of the string, pass '-1' to calculate the
|
||||
// null-terminated string length in the function.
|
||||
// @param[in] separator The character used to separate the digits, if any. Set
|
||||
// this to 0, if no separators are permitted.
|
||||
// @param[out] output The number to write the parsed value to
|
||||
//
|
||||
// @return The checked variants return false if there were invalid characters
|
||||
// in the string true otherwise.
|
||||
// The non-checked variant returns the number that could optimistically be
|
||||
// parsed from the string e.g. "1234 dog" will return 1234.
|
||||
|
||||
// @proc Dqn_String8_Replace, Dqn_String8_ReplaceInsensitive
|
||||
// @desc TODO(doyle): Write description
|
||||
@ -247,14 +223,14 @@ DQN_API Dqn_usize Dqn_CString16_Size (wcha
|
||||
|
||||
struct Dqn_String8Link
|
||||
{
|
||||
Dqn_String8 string; ///< The string
|
||||
Dqn_String8Link *next; ///< The next string in the linked list
|
||||
Dqn_String8 string; // The string
|
||||
Dqn_String8Link *next; // The next string in the linked list
|
||||
};
|
||||
|
||||
struct Dqn_String16 /// A pointer and length style string that holds slices to UTF16 bytes.
|
||||
{
|
||||
wchar_t *data; ///< The UTF16 bytes of the string
|
||||
Dqn_usize size; ///< The number of characters in the string
|
||||
wchar_t *data; // The UTF16 bytes of the string
|
||||
Dqn_usize size; // The number of characters in the string
|
||||
|
||||
#if defined(__cplusplus)
|
||||
wchar_t const *begin() const { return data; } ///< Const begin iterator for range-for loops
|
||||
@ -289,12 +265,6 @@ struct Dqn_String8FindResult
|
||||
#define Dqn_String8_Init(data, size) (Dqn_String8){(data), (size)}
|
||||
#endif
|
||||
|
||||
#define Dqn_String8_InitF(allocator, fmt, ...) Dqn_String8_InitF_(DQN_LEAK_TRACE allocator, fmt, ## __VA_ARGS__)
|
||||
#define Dqn_String8_InitFV(allocator, fmt, args) Dqn_String8_InitFV_(DQN_LEAK_TRACE allocator, fmt, args)
|
||||
#define Dqn_String8_Allocate(allocator, size, zero_mem) Dqn_String8_Allocate_(DQN_LEAK_TRACE allocator, size, zero_mem)
|
||||
#define Dqn_String8_CopyCString(allocator, string, size) Dqn_String8_CopyCString_(DQN_LEAK_TRACE allocator, string, size)
|
||||
#define Dqn_String8_Copy(allocator, string) Dqn_String8_Copy_(DQN_LEAK_TRACE allocator, string)
|
||||
|
||||
// NOTE: API =======================================================================================
|
||||
enum Dqn_String8IsAll
|
||||
{
|
||||
@ -322,6 +292,12 @@ DQN_API Dqn_String8 Dqn_String8_InitCString8 (char con
|
||||
DQN_API bool Dqn_String8_IsValid (Dqn_String8 string);
|
||||
DQN_API bool Dqn_String8_IsAll (Dqn_String8 string, Dqn_String8IsAll is_all);
|
||||
|
||||
DQN_API Dqn_String8 Dqn_String8_InitF (Dqn_Allocator allocator, char const *fmt, ...);
|
||||
DQN_API Dqn_String8 Dqn_String8_InitFV (Dqn_Allocator allocator, char const *fmt, va_list args);
|
||||
DQN_API Dqn_String8 Dqn_String8_Allocate (Dqn_Allocator allocator, Dqn_usize size, Dqn_ZeroMem zero_mem);
|
||||
DQN_API Dqn_String8 Dqn_String8_CopyCString (Dqn_Allocator allocator, char const *string, Dqn_usize size);
|
||||
DQN_API Dqn_String8 Dqn_String8_Copy (Dqn_Allocator allocator, Dqn_String8 string);
|
||||
|
||||
DQN_API Dqn_String8 Dqn_String8_Slice (Dqn_String8 string, Dqn_usize offset, Dqn_usize size);
|
||||
DQN_API Dqn_String8BinarySplitResult Dqn_String8_BinarySplitArray (Dqn_String8 string, Dqn_String8 const *find, Dqn_usize find_size);
|
||||
DQN_API Dqn_String8BinarySplitResult Dqn_String8_BinarySplit (Dqn_String8 string, Dqn_String8 find);
|
||||
@ -346,10 +322,20 @@ DQN_API Dqn_String8 Dqn_String8_TrimByteOrderMark (Dqn_Stri
|
||||
|
||||
DQN_API Dqn_String8 Dqn_String8_FileNameFromPath (Dqn_String8 path);
|
||||
|
||||
DQN_API bool Dqn_String8_ToU64Checked (Dqn_String8 string, char separator, uint64_t *output);
|
||||
DQN_API uint64_t Dqn_String8_ToU64 (Dqn_String8 string, char separator);
|
||||
DQN_API bool Dqn_String8_ToI64Checked (Dqn_String8 string, char separator, int64_t *output);
|
||||
DQN_API int64_t Dqn_String8_ToI64 (Dqn_String8 string, char separator);
|
||||
struct Dqn_String8ToU64Result
|
||||
{
|
||||
bool success;
|
||||
uint64_t value;
|
||||
};
|
||||
|
||||
struct Dqn_String8ToI64Result
|
||||
{
|
||||
bool success;
|
||||
int64_t value;
|
||||
};
|
||||
|
||||
DQN_API Dqn_String8ToU64Result Dqn_String8_ToU64 (Dqn_String8 string, char separator);
|
||||
DQN_API Dqn_String8ToI64Result Dqn_String8_ToI64 (Dqn_String8 string, char separator);
|
||||
|
||||
DQN_API Dqn_String8 Dqn_String8_Replace (Dqn_String8 string, Dqn_String8 find, Dqn_String8 replace, Dqn_usize start_index, Dqn_Allocator allocator, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive);
|
||||
DQN_API Dqn_String8 Dqn_String8_ReplaceInsensitive (Dqn_String8 string, Dqn_String8 find, Dqn_String8 replace, Dqn_usize start_index, Dqn_Allocator allocator);
|
||||
@ -360,13 +346,6 @@ DQN_API bool operator== (Dqn_Stri
|
||||
DQN_API bool operator!= (Dqn_String8 const &lhs, Dqn_String8 const &rhs);
|
||||
#endif
|
||||
|
||||
// NOTE: Internal ==================================================================================
|
||||
DQN_API Dqn_String8 Dqn_String8_InitF_ (DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, char const *fmt, ...);
|
||||
DQN_API Dqn_String8 Dqn_String8_InitFV_ (DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, char const *fmt, va_list args);
|
||||
DQN_API Dqn_String8 Dqn_String8_Allocate_ (DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, Dqn_usize size, Dqn_ZeroMem zero_mem);
|
||||
DQN_API Dqn_String8 Dqn_String8_CopyCString_ (DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, char const *string, Dqn_usize size);
|
||||
DQN_API Dqn_String8 Dqn_String8_Copy_ (DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, Dqn_String8 string);
|
||||
|
||||
#if !defined(DQN_NO_FSTRING8)
|
||||
// NOTE: [$FSTR] Dqn_FString8 ======================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
@ -500,107 +479,12 @@ struct Dqn_String8Builder
|
||||
Dqn_usize count; ///< The number of links in the linked list of strings
|
||||
};
|
||||
|
||||
#define Dqn_String8Builder_AppendFV(builder, fmt, args) Dqn_String8Builder_AppendFV_(DQN_LEAK_TRACE builder, fmt, args)
|
||||
DQN_API bool Dqn_String8Builder_AppendF (Dqn_String8Builder *builder, char const *fmt, ...);
|
||||
DQN_API bool Dqn_String8Builder_AppendFV (Dqn_String8Builder *builder, char const *fmt, va_list args);
|
||||
DQN_API bool Dqn_String8Builder_AppendRef (Dqn_String8Builder *builder, Dqn_String8 string);
|
||||
DQN_API bool Dqn_String8Builder_AppendCopy(Dqn_String8Builder *builder, Dqn_String8 string);
|
||||
DQN_API Dqn_String8 Dqn_String8Builder_Build (Dqn_String8Builder const *builder, Dqn_Allocator allocator);
|
||||
|
||||
// NOTE: Internal ==================================================================================
|
||||
DQN_API bool Dqn_String8Builder_AppendFV_ (DQN_LEAK_TRACE_FUNCTION Dqn_String8Builder *builder, char const *fmt, va_list args);
|
||||
|
||||
#if !defined(DQN_NO_JSON_BUILDER)
|
||||
// NOTE: [$JSON] Dqn_JSONBuilder ===================================================================
|
||||
// Basic helper class to construct JSON output to a string
|
||||
// TODO(dqn): We need to write tests for this
|
||||
//
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_JSONBuilder_Build
|
||||
// @desc Convert the internal JSON buffer in the builder into a string.
|
||||
// @param[in] arena The allocator to use to build the string
|
||||
|
||||
// @proc Dqn_JSONBuilder_KeyValue, Dqn_JSONBuilder_KeyValueF
|
||||
// @desc Add a JSON key value pair untyped. The value is emitted directly
|
||||
// without checking the contents of value.
|
||||
//
|
||||
// All other functions internally call into this function which is the main
|
||||
// workhorse of the builder.
|
||||
|
||||
// @proc Dqn_JSON_Builder_ObjectEnd
|
||||
// @desc End a JSON object in the builder, generates internally a '}' string
|
||||
|
||||
// @proc Dqn_JSON_Builder_ArrayEnd
|
||||
// @desc End a JSON array in the builder, generates internally a ']' string
|
||||
|
||||
// @proc Dqn_JSONBuilder_LiteralNamed
|
||||
// @desc Add a named JSON key-value object whose value is directly written to
|
||||
// the following '"<key>": <value>' (e.g. useful for emitting the 'null'
|
||||
// value)
|
||||
|
||||
// @proc Dqn_JSONBuilder_U64Named, Dqn_JSONBuilder_U64,
|
||||
// Dqn_JSONBuilder_I64Named, Dqn_JSONBuilder_I64,
|
||||
// Dqn_JSONBuilder_F64Named, Dqn_JSONBuilder_F64,
|
||||
// Dqn_JSONBuilder_BoolNamed, Dqn_JSONBuilder_Bool,
|
||||
// @desc Add the named JSON data type as a key-value object. Generates
|
||||
// internally the string '"<key>": <value>'
|
||||
|
||||
enum Dqn_JSONBuilderItem {
|
||||
Dqn_JSONBuilderItem_Empty,
|
||||
Dqn_JSONBuilderItem_OpenContainer,
|
||||
Dqn_JSONBuilderItem_CloseContainer,
|
||||
Dqn_JSONBuilderItem_KeyValue,
|
||||
};
|
||||
|
||||
struct Dqn_JSONBuilder {
|
||||
bool use_stdout; ///< When set, ignore the string builder and dump immediately to stdout
|
||||
Dqn_String8Builder string_builder; ///< (Internal)
|
||||
int indent_level; ///< (Internal)
|
||||
int spaces_per_indent; ///< The number of spaces per indent level
|
||||
Dqn_JSONBuilderItem last_item;
|
||||
};
|
||||
|
||||
#define Dqn_JSONBuilder_Object(builder) \
|
||||
DQN_DEFER_LOOP(Dqn_JSONBuilder_ObjectBegin(builder), \
|
||||
Dqn_JSONBuilder_ObjectEnd(builder))
|
||||
|
||||
#define Dqn_JSONBuilder_ObjectNamed(builder, name) \
|
||||
DQN_DEFER_LOOP(Dqn_JSONBuilder_ObjectBeginNamed(builder, name), \
|
||||
Dqn_JSONBuilder_ObjectEnd(builder))
|
||||
|
||||
#define Dqn_JSONBuilder_Array(builder) \
|
||||
DQN_DEFER_LOOP(Dqn_JSONBuilder_ArrayBegin(builder), \
|
||||
Dqn_JSONBuilder_ArrayEnd(builder))
|
||||
|
||||
#define Dqn_JSONBuilder_ArrayNamed(builder, name) \
|
||||
DQN_DEFER_LOOP(Dqn_JSONBuilder_ArrayBeginNamed(builder, name), \
|
||||
Dqn_JSONBuilder_ArrayEnd(builder))
|
||||
|
||||
|
||||
DQN_API Dqn_JSONBuilder Dqn_JSONBuilder_Init (Dqn_Allocator allocator, int spaces_per_indent);
|
||||
DQN_API Dqn_String8 Dqn_JSONBuilder_Build (Dqn_JSONBuilder const *builder, Dqn_Allocator allocator);
|
||||
DQN_API void Dqn_JSONBuilder_KeyValue (Dqn_JSONBuilder *builder, Dqn_String8 key, Dqn_String8 value);
|
||||
DQN_API void Dqn_JSONBuilder_KeyValueF (Dqn_JSONBuilder *builder, Dqn_String8 key, char const *value_fmt, ...);
|
||||
DQN_API void Dqn_JSONBuilder_ObjectBeginNamed(Dqn_JSONBuilder *builder, Dqn_String8 name);
|
||||
DQN_API void Dqn_JSONBuilder_ObjectEnd (Dqn_JSONBuilder *builder);
|
||||
DQN_API void Dqn_JSONBuilder_ArrayBeginNamed (Dqn_JSONBuilder *builder, Dqn_String8 name);
|
||||
DQN_API void Dqn_JSONBuilder_ArrayEnd (Dqn_JSONBuilder *builder);
|
||||
DQN_API void Dqn_JSONBuilder_StringNamed (Dqn_JSONBuilder *builder, Dqn_String8 key, Dqn_String8 value);
|
||||
DQN_API void Dqn_JSONBuilder_LiteralNamed (Dqn_JSONBuilder *builder, Dqn_String8 key, Dqn_String8 value);
|
||||
DQN_API void Dqn_JSONBuilder_U64Named (Dqn_JSONBuilder *builder, Dqn_String8 key, uint64_t value);
|
||||
DQN_API void Dqn_JSONBuilder_I64Named (Dqn_JSONBuilder *builder, Dqn_String8 key, int64_t value);
|
||||
DQN_API void Dqn_JSONBuilder_F64Named (Dqn_JSONBuilder *builder, Dqn_String8 key, double value, int decimal_places);
|
||||
DQN_API void Dqn_JSONBuilder_BoolNamed (Dqn_JSONBuilder *builder, Dqn_String8 key, bool value);
|
||||
|
||||
#define Dqn_JSONBuilder_ObjectBegin(builder) Dqn_JSONBuilder_ObjectBeginNamed(builder, DQN_STRING8(""))
|
||||
#define Dqn_JSONBuilder_ArrayBegin(builder) Dqn_JSONBuilder_ArrayBeginNamed(builder, DQN_STRING8(""))
|
||||
#define Dqn_JSONBuilder_String(builder, value) Dqn_JSONBuilder_StringNamed(builder, DQN_STRING8(""), value)
|
||||
#define Dqn_JSONBuilder_Literal(builder, value) Dqn_JSONBuilder_LiteralNamed(builder, DQN_STRING8(""), value)
|
||||
#define Dqn_JSONBuilder_U64(builder, value) Dqn_JSONBuilder_U64Named(builder, DQN_STRING8(""), value)
|
||||
#define Dqn_JSONBuilder_I64(builder, value) Dqn_JSONBuilder_I64Named(builder, DQN_STRING8(""), value)
|
||||
#define Dqn_JSONBuilder_F64(builder, value) Dqn_JSONBuilder_F64Named(builder, DQN_STRING8(""), value)
|
||||
#define Dqn_JSONBuilder_Bool(builder, value) Dqn_JSONBuilder_BoolNamed(builder, DQN_STRING8(""), value)
|
||||
#endif // !defined(DQN_NO_JSON_BUIDLER)
|
||||
|
||||
// NOTE: [$CHAR] Dqn_Char ==========================================================================
|
||||
DQN_API bool Dqn_Char_IsAlphabet (char ch);
|
||||
DQN_API bool Dqn_Char_IsDigit (char ch);
|
||||
@ -616,370 +500,6 @@ DQN_API char Dqn_Char_ToLower (char ch);
|
||||
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);
|
||||
|
||||
#if !defined(DQN_NO_HEX)
|
||||
// NOTE: [$BHEX] Dqn_Bin ===========================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_Bin_U64ToHexU64String
|
||||
// @desc Convert a 64 bit number to a hex string
|
||||
// @param[in] number Number to convert to hexadecimal representation
|
||||
// @param[in] flags Bit flags from Dqn_BinHexU64StringFlags to customise the
|
||||
// output of the hexadecimal string.
|
||||
// @return The hexadecimal representation of the number. This string is always
|
||||
// null-terminated.
|
||||
|
||||
// @proc Dqn_Bin_U64ToHex
|
||||
// @copybrief Dqn_Bin_U64ToHexU64String
|
||||
|
||||
// @param[in] allocator The memory allocator to use for the memory of the
|
||||
// hexadecimal string.
|
||||
// @copyparams Dqn_Bin_U64ToHexU64String
|
||||
|
||||
// @proc Dqn_Bin_HexBufferToU64
|
||||
// @desc Convert a hexadecimal string a 64 bit value.
|
||||
// Asserts if the hex string is too big to be converted into a 64 bit number.
|
||||
|
||||
// @proc Dqn_Bin_HexToU64
|
||||
// @copydoc Dqn_Bin_HexToU64
|
||||
|
||||
// @proc Dqn_Bin_BytesToHexBuffer
|
||||
// @desc Convert a binary buffer into its hex representation into dest.
|
||||
//
|
||||
// The dest buffer must be large enough to contain the hex representation, i.e.
|
||||
// atleast (src_size * 2).
|
||||
//
|
||||
// @return True if the conversion into the dest buffer was successful, false
|
||||
// otherwise (e.g. invalid arguments).
|
||||
|
||||
// @proc Dqn_Bin_BytesToHexBufferArena
|
||||
// @desc Convert a series of bytes into a string
|
||||
// @return A null-terminated hex string, null pointer if allocation failed
|
||||
|
||||
// @proc Dqn_Bin_BytesToHexArena
|
||||
// @copydoc Dqn_Bin_BytesToHexBufferArena
|
||||
// @return A hex string, the string is invalid if conversion failed.
|
||||
|
||||
// @proc Dqn_Bin_HexBufferToBytes
|
||||
// @desc Convert a hex string into binary at `dest`.
|
||||
//
|
||||
// The dest buffer must be large enough to contain the binary representation,
|
||||
// i.e. atleast ceil(hex_size / 2). This function will strip whitespace,
|
||||
// leading 0x/0X prefix from the string before conversion.
|
||||
//
|
||||
// @param[in] hex The hexadecimal string to convert
|
||||
// @param[in] hex_size Size of the hex buffer. This function can handle an odd
|
||||
// size hex string i.e. "fff" produces 0xfff0.
|
||||
// @param[out] dest Buffer to write the bytes to
|
||||
// @param[out] dest_size Maximum number of bytes to write to dest
|
||||
//
|
||||
// @return The number of bytes written to `dest_size`, this value will *never*
|
||||
// be greater than `dest_size`.
|
||||
|
||||
// @proc Dqn_Bin_HexToBytes
|
||||
// @desc String8 variant of @see Dqn_Bin_HexBufferToBytes
|
||||
|
||||
// @proc Dqn_Bin_StringHexBufferToBytesUnchecked
|
||||
// @desc Unchecked variant of @see Dqn_Bin_HexBufferToBytes
|
||||
//
|
||||
// This function skips some string checks, it assumes the hex is a valid hex
|
||||
// stream and that the arguments are valid e.g. no trimming or 0x prefix
|
||||
// stripping is performed
|
||||
|
||||
// @proc Dqn_Bin_String
|
||||
// @desc String8 variant of @see Dqn_Bin_HexBufferToBytesUnchecked
|
||||
|
||||
// @proc Dqn_Bin_HexBufferToBytesArena
|
||||
// Dynamic allocating variant of @see Dqn_Bin_HexBufferToBytesUnchecked
|
||||
//
|
||||
// @param[in] arena The arena to allocate the bytes from
|
||||
// @param[in] hex Hex string to convert into bytes
|
||||
// @param[in] size Size of the hex string
|
||||
// @param[out] real_size The size of the buffer returned by the function
|
||||
//
|
||||
// @return The byte representation of the hex string.
|
||||
|
||||
// @proc Dqn_Bin_HexToBytesArena
|
||||
// @copybrief Dqn_Bin_HexBufferToBytesArena
|
||||
//
|
||||
// @param[in] arena The arena to allocate the bytes from
|
||||
// @param[in] hex Hex string to convert into bytes
|
||||
//
|
||||
// @return The byte representation of the hex string.
|
||||
|
||||
struct Dqn_BinHexU64String
|
||||
{
|
||||
char data[2 /*0x*/ + 16 /*hex*/ + 1 /*null-terminator*/];
|
||||
uint8_t size;
|
||||
};
|
||||
|
||||
enum Dqn_BinHexU64StringFlags
|
||||
{
|
||||
Dqn_BinHexU64StringFlags_No0xPrefix = 1 << 0, /// Remove the 0x prefix from the string
|
||||
Dqn_BinHexU64StringFlags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex
|
||||
};
|
||||
|
||||
DQN_API char const * Dqn_Bin_HexBufferTrim0x (char const *hex, Dqn_usize size, Dqn_usize *real_size);
|
||||
DQN_API Dqn_String8 Dqn_Bin_HexTrim0x (Dqn_String8 string);
|
||||
|
||||
DQN_API Dqn_BinHexU64String Dqn_Bin_U64ToHexU64String (uint64_t number, uint32_t flags);
|
||||
DQN_API Dqn_String8 Dqn_Bin_U64ToHex (Dqn_Allocator allocator, uint64_t number, uint32_t flags);
|
||||
|
||||
DQN_API uint64_t Dqn_Bin_HexBufferToU64 (char const *hex, Dqn_usize size);
|
||||
DQN_API uint64_t Dqn_Bin_HexToU64 (Dqn_String8 hex);
|
||||
|
||||
DQN_API Dqn_String8 Dqn_Bin_BytesToHexArena (Dqn_Arena *arena, void const *src, Dqn_usize size);
|
||||
DQN_API char * Dqn_Bin_BytesToHexBufferArena (Dqn_Arena *arena, void const *src, Dqn_usize size);
|
||||
DQN_API bool Dqn_Bin_BytesToHexBuffer (void const *src, Dqn_usize src_size, char *dest, Dqn_usize dest_size);
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytesUnchecked(char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size);
|
||||
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytes (char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size);
|
||||
DQN_API char * Dqn_Bin_HexBufferToBytesArena (Dqn_Arena *arena, char const *hex, Dqn_usize hex_size, Dqn_usize *real_size);
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexToBytesUnchecked (Dqn_String8 hex, void *dest, Dqn_usize dest_size);
|
||||
DQN_API Dqn_usize Dqn_Bin_HexToBytes (Dqn_String8 hex, void *dest, Dqn_usize dest_size);
|
||||
DQN_API Dqn_String8 Dqn_Bin_HexToBytesArena (Dqn_Arena *arena, Dqn_String8 hex);
|
||||
#endif // !defined(DQN_NO_HEX)
|
||||
|
||||
// NOTE: Other =====================================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_SNPrintFDotTruncate
|
||||
// @desc Write the format string to the buffer truncating with a trailing ".."
|
||||
// if there is insufficient space in the buffer followed by null-terminating
|
||||
// the buffer (uses stb_sprintf underneath).
|
||||
// @return The size of the string written to the buffer *not* including the
|
||||
// null-terminator.
|
||||
//
|
||||
// @proc Dqn_U64ToString
|
||||
// @desc Convert a 64 bit unsigned value to its string representation.
|
||||
// @param[in] val Value to convert into a string
|
||||
// @param[in] separator The separator to insert every 3 digits. Set this to
|
||||
// 0 if no separator is desired.
|
||||
|
||||
struct Dqn_U64String
|
||||
{
|
||||
char data[27+1]; // NOTE(dqn): 27 is the maximum size of uint64_t including a separtor
|
||||
uint8_t size;
|
||||
};
|
||||
|
||||
DQN_API int Dqn_SNPrintFDotTruncate(char *buffer, int size, char const *fmt, ...);
|
||||
DQN_API Dqn_U64String Dqn_U64ToString (uint64_t val, char separator);
|
||||
|
||||
// NOTE: [$STBS] stb_sprintf =======================================================================
|
||||
// stb_sprintf - v1.10 - public domain snprintf() implementation
|
||||
// originally by Jeff Roberts / RAD Game Tools, 2015/10/20
|
||||
// http://github.com/nothings/stb
|
||||
//
|
||||
// allowed types: sc uidBboXx p AaGgEef n
|
||||
// lengths : hh h ll j z t I64 I32 I
|
||||
//
|
||||
// Contributors:
|
||||
// Fabian "ryg" Giesen (reformatting)
|
||||
// github:aganm (attribute format)
|
||||
//
|
||||
// Contributors (bugfixes):
|
||||
// github:d26435
|
||||
// github:trex78
|
||||
// github:account-login
|
||||
// Jari Komppa (SI suffixes)
|
||||
// Rohit Nirmal
|
||||
// Marcin Wojdyr
|
||||
// Leonard Ritter
|
||||
// Stefano Zanotti
|
||||
// Adam Allison
|
||||
// Arvid Gerstmann
|
||||
// Markus Kolb
|
||||
//
|
||||
// LICENSE:
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
#ifndef STB_SPRINTF_H_INCLUDE
|
||||
#define STB_SPRINTF_H_INCLUDE
|
||||
|
||||
/*
|
||||
Single file sprintf replacement.
|
||||
|
||||
Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20.
|
||||
Hereby placed in public domain.
|
||||
|
||||
This is a full sprintf replacement that supports everything that
|
||||
the C runtime sprintfs support, including float/double, 64-bit integers,
|
||||
hex floats, field parameters (%*.*d stuff), length reads backs, etc.
|
||||
|
||||
Why would you need this if sprintf already exists? Well, first off,
|
||||
it's *much* faster (see below). It's also much smaller than the CRT
|
||||
versions code-space-wise. We've also added some simple improvements
|
||||
that are super handy (commas in thousands, callbacks at buffer full,
|
||||
for example). Finally, the format strings for MSVC and GCC differ
|
||||
for 64-bit integers (among other small things), so this lets you use
|
||||
the same format strings in cross platform code.
|
||||
|
||||
It uses the standard single file trick of being both the header file
|
||||
and the source itself. If you just include it normally, you just get
|
||||
the header file function definitions. To get the code, you include
|
||||
it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first.
|
||||
|
||||
It only uses va_args macros from the C runtime to do it's work. It
|
||||
does cast doubles to S64s and shifts and divides U64s, which does
|
||||
drag in CRT code on most platforms.
|
||||
|
||||
It compiles to roughly 8K with float support, and 4K without.
|
||||
As a comparison, when using MSVC static libs, calling sprintf drags
|
||||
in 16K.
|
||||
|
||||
API:
|
||||
====
|
||||
int stbsp_sprintf( char * buf, char const * fmt, ... )
|
||||
int stbsp_snprintf( char * buf, int count, char const * fmt, ... )
|
||||
Convert an arg list into a buffer. stbsp_snprintf always returns
|
||||
a zero-terminated string (unlike regular snprintf).
|
||||
|
||||
int stbsp_vsprintf( char * buf, char const * fmt, va_list va )
|
||||
int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va )
|
||||
Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns
|
||||
a zero-terminated string (unlike regular snprintf).
|
||||
|
||||
int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )
|
||||
typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len );
|
||||
Convert into a buffer, calling back every STB_SPRINTF_MIN chars.
|
||||
Your callback can then copy the chars out, print them or whatever.
|
||||
This function is actually the workhorse for everything else.
|
||||
The buffer you pass in must hold at least STB_SPRINTF_MIN characters.
|
||||
// you return the next buffer to use or 0 to stop converting
|
||||
|
||||
void stbsp_set_separators( char comma, char period )
|
||||
Set the comma and period characters to use.
|
||||
|
||||
FLOATS/DOUBLES:
|
||||
===============
|
||||
This code uses a internal float->ascii conversion method that uses
|
||||
doubles with error correction (double-doubles, for ~105 bits of
|
||||
precision). This conversion is round-trip perfect - that is, an atof
|
||||
of the values output here will give you the bit-exact double back.
|
||||
|
||||
One difference is that our insignificant digits will be different than
|
||||
with MSVC or GCC (but they don't match each other either). We also
|
||||
don't attempt to find the minimum length matching float (pre-MSVC15
|
||||
doesn't either).
|
||||
|
||||
If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT
|
||||
and you'll save 4K of code space.
|
||||
|
||||
64-BIT INTS:
|
||||
============
|
||||
This library also supports 64-bit integers and you can use MSVC style or
|
||||
GCC style indicators (%I64d or %lld). It supports the C99 specifiers
|
||||
for size_t and ptr_diff_t (%jd %zd) as well.
|
||||
|
||||
EXTRAS:
|
||||
=======
|
||||
Like some GCCs, for integers and floats, you can use a ' (single quote)
|
||||
specifier and commas will be inserted on the thousands: "%'d" on 12345
|
||||
would print 12,345.
|
||||
|
||||
For integers and floats, you can use a "$" specifier and the number
|
||||
will be converted to float and then divided to get kilo, mega, giga or
|
||||
tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is
|
||||
"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn
|
||||
2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three
|
||||
$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the
|
||||
suffix, add "_" specifier: "%_$d" -> "2.53M".
|
||||
|
||||
In addition to octal and hexadecimal conversions, you can print
|
||||
integers in binary: "%b" for 256 would print 100.
|
||||
|
||||
PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC):
|
||||
===================================================================
|
||||
"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC)
|
||||
"%24d" across all 32-bit ints (4.5x/4.2x faster)
|
||||
"%x" across all 32-bit ints (4.5x/3.8x faster)
|
||||
"%08x" across all 32-bit ints (4.3x/3.8x faster)
|
||||
"%f" across e-10 to e+10 floats (7.3x/6.0x faster)
|
||||
"%e" across e-10 to e+10 floats (8.1x/6.0x faster)
|
||||
"%g" across e-10 to e+10 floats (10.0x/7.1x faster)
|
||||
"%f" for values near e-300 (7.9x/6.5x faster)
|
||||
"%f" for values near e+300 (10.0x/9.1x faster)
|
||||
"%e" for values near e-300 (10.1x/7.0x faster)
|
||||
"%e" for values near e+300 (9.2x/6.0x faster)
|
||||
"%.320f" for values near e-300 (12.6x/11.2x faster)
|
||||
"%a" for random values (8.6x/4.3x faster)
|
||||
"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster)
|
||||
"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster)
|
||||
"%s%s%s" for 64 char strings (7.1x/7.3x faster)
|
||||
"...512 char string..." ( 35.0x/32.5x faster!)
|
||||
*/
|
||||
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && defined(__has_attribute)
|
||||
#if __has_feature(address_sanitizer)
|
||||
#if __has_attribute(__no_sanitize__)
|
||||
#define STBSP__ASAN __attribute__((__no_sanitize__("address")))
|
||||
#elif __has_attribute(__no_sanitize_address__)
|
||||
#define STBSP__ASAN __attribute__((__no_sanitize_address__))
|
||||
#elif __has_attribute(__no_address_safety_analysis__)
|
||||
#define STBSP__ASAN __attribute__((__no_address_safety_analysis__))
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
|
||||
#if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__
|
||||
#define STBSP__ASAN __attribute__((__no_sanitize_address__))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef STBSP__ASAN
|
||||
#define STBSP__ASAN
|
||||
#endif
|
||||
|
||||
#ifdef STB_SPRINTF_STATIC
|
||||
#define STBSP__PUBLICDEC static
|
||||
#define STBSP__PUBLICDEF static STBSP__ASAN
|
||||
#else
|
||||
#ifdef __cplusplus
|
||||
#define STBSP__PUBLICDEC extern "C"
|
||||
#define STBSP__PUBLICDEF extern "C" STBSP__ASAN
|
||||
#else
|
||||
#define STBSP__PUBLICDEC extern
|
||||
#define STBSP__PUBLICDEF STBSP__ASAN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__has_attribute)
|
||||
#if __has_attribute(format)
|
||||
#define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va)))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef STBSP__ATTRIBUTE_FORMAT
|
||||
#define STBSP__ATTRIBUTE_FORMAT(fmt,va)
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define STBSP__NOTUSED(v) (void)(v)
|
||||
#else
|
||||
#define STBSP__NOTUSED(v) (void)sizeof(v)
|
||||
#endif
|
||||
|
||||
#include <stdarg.h> // for va_arg(), va_list()
|
||||
#include <stddef.h> // size_t, ptrdiff_t
|
||||
|
||||
#ifndef STB_SPRINTF_MIN
|
||||
#define STB_SPRINTF_MIN 512 // how many characters per callback
|
||||
#endif
|
||||
typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len);
|
||||
|
||||
#ifndef STB_SPRINTF_DECORATE
|
||||
#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names
|
||||
#endif
|
||||
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va);
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va);
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3);
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4);
|
||||
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va);
|
||||
STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period);
|
||||
#endif // STB_SPRINTF_H_INCLUDE
|
||||
|
||||
#if !defined(DQN_NO_FSTRING8)
|
||||
// NOTE: [$FSTR] Dqn_FString8 ======================================================================
|
||||
template <Dqn_usize N> Dqn_FString8<N> Dqn_FString8_InitF(char const *fmt, ...)
|
||||
|
100
dqn_tests_helpers.cpp
Normal file
100
dqn_tests_helpers.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
#if defined(DQN_KECCAK_H)
|
||||
// -----------------------------------------------------------------------------
|
||||
// Dqn_Keccak Reference Implementation
|
||||
// -----------------------------------------------------------------------------
|
||||
// A very compact Keccak implementation taken from the reference implementation
|
||||
// repository
|
||||
//
|
||||
// https://github.com/XKCP/XKCP/blob/master/Standalone/CompactFIPS202/C/Keccak-more-compact.c
|
||||
//
|
||||
|
||||
#define FOR(i,n) for(i=0; i<n; ++i)
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned long long int u64;
|
||||
typedef unsigned int ui;
|
||||
|
||||
void Keccak(ui r, ui c, const u8 *in, u64 inLen, u8 sfx, u8 *out, u64 outLen);
|
||||
void FIPS202_SHAKE128(const u8 *in, u64 inLen, u8 *out, u64 outLen) { Keccak(1344, 256, in, inLen, 0x1F, out, outLen); }
|
||||
void FIPS202_SHAKE256(const u8 *in, u64 inLen, u8 *out, u64 outLen) { Keccak(1088, 512, in, inLen, 0x1F, out, outLen); }
|
||||
void FIPS202_SHA3_224(const u8 *in, u64 inLen, u8 *out) { Keccak(1152, 448, in, inLen, 0x06, out, 28); }
|
||||
void FIPS202_SHA3_256(const u8 *in, u64 inLen, u8 *out) { Keccak(1088, 512, in, inLen, 0x06, out, 32); }
|
||||
void FIPS202_SHA3_384(const u8 *in, u64 inLen, u8 *out) { Keccak(832, 768, in, inLen, 0x06, out, 48); }
|
||||
void FIPS202_SHA3_512(const u8 *in, u64 inLen, u8 *out) { Keccak(576, 1024, in, inLen, 0x06, out, 64); }
|
||||
|
||||
int LFSR86540(u8 *R) { (*R)=((*R)<<1)^(((*R)&0x80)?0x71:0); return ((*R)&2)>>1; }
|
||||
#define ROL(a,o) ((((u64)a)<<o)^(((u64)a)>>(64-o)))
|
||||
static u64 load64(const u8 *x) { ui i; u64 u=0; FOR(i,8) { u<<=8; u|=x[7-i]; } return u; }
|
||||
static void store64(u8 *x, u64 u) { ui i; FOR(i,8) { x[i]=u; u>>=8; } }
|
||||
static void xor64(u8 *x, u64 u) { ui i; FOR(i,8) { x[i]^=u; u>>=8; } }
|
||||
#define rL(x,y) load64((u8*)s+8*(x+5*y))
|
||||
#define wL(x,y,l) store64((u8*)s+8*(x+5*y),l)
|
||||
#define XL(x,y,l) xor64((u8*)s+8*(x+5*y),l)
|
||||
void KeccakF1600(void *s)
|
||||
{
|
||||
ui r,x,y,i,j,Y; u8 R=0x01; u64 C[5],D;
|
||||
for(i=0; i<24; i++) {
|
||||
/*θ*/ FOR(x,5) C[x]=rL(x,0)^rL(x,1)^rL(x,2)^rL(x,3)^rL(x,4); FOR(x,5) { D=C[(x+4)%5]^ROL(C[(x+1)%5],1); FOR(y,5) XL(x,y,D); }
|
||||
/*ρπ*/ x=1; y=r=0; D=rL(x,y); FOR(j,24) { r+=j+1; Y=(2*x+3*y)%5; x=y; y=Y; C[0]=rL(x,y); wL(x,y,ROL(D,r%64)); D=C[0]; }
|
||||
/*χ*/ FOR(y,5) { FOR(x,5) C[x]=rL(x,y); FOR(x,5) wL(x,y,C[x]^((~C[(x+1)%5])&C[(x+2)%5])); }
|
||||
/*ι*/ FOR(j,7) if (LFSR86540(&R)) XL(0,0,(u64)1<<((1<<j)-1));
|
||||
}
|
||||
}
|
||||
void Keccak(ui r, ui c, const u8 *in, u64 inLen, u8 sfx, u8 *out, u64 outLen)
|
||||
{
|
||||
/*initialize*/ u8 s[200]; ui R=r/8; ui i,b=0; FOR(i,200) s[i]=0;
|
||||
/*absorb*/ while(inLen>0) { b=(inLen<R)?inLen:R; FOR(i,b) s[i]^=in[i]; in+=b; inLen-=b; if (b==R) { KeccakF1600(s); b=0; } }
|
||||
/*pad*/ s[b]^=sfx; if((sfx&0x80)&&(b==(R-1))) KeccakF1600(s); s[R-1]^=0x80; KeccakF1600(s);
|
||||
/*squeeze*/ while(outLen>0) { b=(outLen<R)?outLen:R; FOR(i,b) out[i]=s[i]; out+=b; outLen-=b; if(outLen>0) KeccakF1600(s); }
|
||||
}
|
||||
|
||||
|
||||
// PCG32 Random Number Generator
|
||||
// -----------------------------------------------------------------------------
|
||||
// NOTE: https://github.com/imneme/pcg-c-basic
|
||||
|
||||
struct pcg_state_setseq_64
|
||||
{ // Internals are *Private*.
|
||||
uint64_t state; // RNG state. All values are possible.
|
||||
uint64_t inc; // Controls which RNG sequence (stream) is
|
||||
// selected. Must *always* be odd.
|
||||
};
|
||||
typedef struct pcg_state_setseq_64 pcg32_random_t;
|
||||
|
||||
// pcg32_random_r(rng)
|
||||
// Generate a uniformly distributed 32-bit random number
|
||||
|
||||
uint32_t pcg32_random_r(pcg32_random_t* rng)
|
||||
{
|
||||
uint64_t oldstate = rng->state;
|
||||
rng->state = oldstate * 6364136223846793005ULL + rng->inc;
|
||||
uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
|
||||
uint32_t rot = oldstate >> 59u;
|
||||
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
|
||||
}
|
||||
|
||||
// pcg32_srandom_r(rng, initstate, initseq):
|
||||
// Seed the rng. Specified in two parts, state initializer and a
|
||||
// sequence selection constant (a.k.a. stream id)
|
||||
|
||||
void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq)
|
||||
{
|
||||
rng->state = 0U;
|
||||
rng->inc = (initseq << 1u) | 1u;
|
||||
pcg32_random_r(rng);
|
||||
rng->state += initstate;
|
||||
pcg32_random_r(rng);
|
||||
}
|
||||
|
||||
// pcg32_boundedrand_r(rng, bound):
|
||||
// Generate a uniformly distributed number, r, where 0 <= r < bound
|
||||
|
||||
uint32_t pcg32_boundedrand_r(pcg32_random_t* rng, uint32_t bound)
|
||||
{
|
||||
uint32_t threshold = -bound % bound;
|
||||
for (;;) {
|
||||
uint32_t r = pcg32_random_r(rng);
|
||||
if (r >= threshold)
|
||||
return r % bound;
|
||||
}
|
||||
}
|
||||
#endif // DQN_KECCAK_H
|
Loading…
Reference in New Issue
Block a user