From e7ebe1e243a26c129d48a08e7987065f054a550f Mon Sep 17 00:00:00 2001 From: Doyle Thai Date: Sat, 24 Jun 2017 15:33:18 +1000 Subject: [PATCH] Update dqn lib --- src/DTRenderer.cpp | 4 +- src/DTRendererAsset.cpp | 5 +- src/DTRendererDebug.cpp | 2 +- src/Win32DTRenderer.cpp | 2 +- src/dqn.h | 464 +++++++++++++++++++++++++++------------- 5 files changed, 325 insertions(+), 152 deletions(-) diff --git a/src/DTRenderer.cpp b/src/DTRenderer.cpp index 7df6e94..3c12fc8 100644 --- a/src/DTRenderer.cpp +++ b/src/DTRenderer.cpp @@ -936,7 +936,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer, { DTRDebug_TestMeshFaceAndVertexParser(&state->mesh); bool regionValid; - auto memRegion = DqnMemStackTempRegionScoped(&memory->tempStack, ®ionValid); + auto memRegion = DqnMemStackTempRegionGuard(&memory->tempStack, ®ionValid); if (regionValid) { DTRBitmap test = {}; @@ -949,7 +949,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer, { bool regionValid; - auto tempMemRegion = DqnMemStackTempRegionScoped(&memory->tempStack, ®ionValid); + auto tempMemRegion = DqnMemStackTempRegionGuard(&memory->tempStack, ®ionValid); if (regionValid) { size_t debugSize = DQN_MEGABYTE(1); diff --git a/src/DTRendererAsset.cpp b/src/DTRendererAsset.cpp index 0a0ea29..2586cf6 100644 --- a/src/DTRendererAsset.cpp +++ b/src/DTRendererAsset.cpp @@ -635,7 +635,7 @@ bool DTRAsset_LoadFontToBitmap(const PlatformAPI api, DqnMemStack *const memStac bool result = false; bool regionValid; - auto tmpMemRegion = DqnMemStackTempRegionScoped(tmpMemStack, ®ionValid); + auto tmpMemRegion = DqnMemStackTempRegionGuard(tmpMemStack, ®ionValid); if (!regionValid) { // TODO(doyle): Logging @@ -767,8 +767,7 @@ bool DTRAsset_LoadBitmap(const PlatformAPI api, DqnMemStack *const memStack, if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read, PlatformFileAction_OpenOnly)) return result; - DqnMemStackTempRegionScoped tmpMemRegion = tempStack->TempRegionScoped(); - + DqnMemStackTempRegionGuard tmpMemRegion = tempStack->TempRegionGuard(); u8 *const rawData = (u8 *)DqnMemStack_Push(tempStack, file.size); size_t bytesRead = api.FileRead(&file, rawData, file.size); diff --git a/src/DTRendererDebug.cpp b/src/DTRendererDebug.cpp index 24dbe02..af3b5a9 100644 --- a/src/DTRendererDebug.cpp +++ b/src/DTRendererDebug.cpp @@ -78,7 +78,7 @@ void DTRDebug_DumpZBuffer(DTRRenderBuffer *const renderBuffer, DqnMemStack *cons } bool regionValid; - auto memRegion = DqnMemStackTempRegionScoped(tempStack, ®ionValid); + auto memRegion = DqnMemStackTempRegionGuard(tempStack, ®ionValid); if (regionValid) { size_t bufSize = DQN_MEGABYTE(16); diff --git a/src/Win32DTRenderer.cpp b/src/Win32DTRenderer.cpp index 26b2fb7..834013e 100644 --- a/src/Win32DTRenderer.cpp +++ b/src/Win32DTRenderer.cpp @@ -207,7 +207,7 @@ size_t Platform_FileRead(PlatformFile *const file, u8 *const buf, if (!file || !buf) return 0; DqnFile dqnFile = PlatformFileToDqnFileInternal(*file); - size_t numBytesRead = DqnFile_Read(dqnFile, buf, bytesToRead); + size_t numBytesRead = DqnFile_Read(&dqnFile, buf, bytesToRead); return numBytesRead; } diff --git a/src/dqn.h b/src/dqn.h index 0b81159..e8b9953 100644 --- a/src/dqn.h +++ b/src/dqn.h @@ -12,6 +12,13 @@ #include "dqn.h" */ +// Conventions +// All data structures fields are exposed by default, with exceptions here and there. The rationale +// is I rarely go into libraries and start changing values around in fields unless I know the +// implementation OR we're required to fill out a struct for some function. + +// Just treat all struct fields to be internal and read-only unless explicitly stated otherwise. + //////////////////////////////////////////////////////////////////////////////// // Table Of Contents #TOC #TableOfContents //////////////////////////////////////////////////////////////////////////////// @@ -53,7 +60,7 @@ // #DqnSprintf Cross-platform Sprintf Implementation (Public Domain lib stb_sprintf) //////////////////////////////////////////////////////////////////////////////// -// Global Preprocessor Checks +// Preprocessor Checks //////////////////////////////////////////////////////////////////////////////// // This needs to be above the portable layer so that, if the user requests // a platform implementation, platform specific implementations in the portable @@ -171,7 +178,7 @@ DQN_FILE_SCOPE bool DqnAssertInternal(const bool result, const char *const file, DQN_FILE_SCOPE void *DqnMem_Alloc (const size_t size); DQN_FILE_SCOPE void *DqnMem_Calloc (const size_t size); DQN_FILE_SCOPE void DqnMem_Clear (void *const memory, const u8 clearValue, const size_t size); -DQN_FILE_SCOPE void *DqnMem_Realloc(void *const memory, const size_t newSize); +DQN_FILE_SCOPE void *DqnMem_Realloc(void *memory, const size_t newSize); DQN_FILE_SCOPE void DqnMem_Free (void *memory); //////////////////////////////////////////////////////////////////////////////// @@ -209,9 +216,14 @@ enum DqnMemStackFlag typedef struct DqnMemStack { + // The memory block allocated for the stack struct DqnMemStackBlock *block; + + // Bits set from enum DqnMemStackFlag u32 flags; i32 tempRegionCount; + + // Allocations are address aligned to this value. Not to be modified as popping allocations uses this to realign size u32 byteAlign; #if defined(DQN_CPP_MODE) @@ -233,7 +245,7 @@ typedef struct DqnMemStack void TempRegionEnd (DqnMemStackTempRegion region); // Scoped Temporary Regions API - struct DqnMemStackTempRegionScoped TempRegionScoped(); + struct DqnMemStackTempRegionGuard TempRegionGuard(); // Advanced API DqnMemStackBlock *AllocateCompatibleBlock(size_t size); @@ -262,7 +274,7 @@ DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedSize(DqnMemStack *const stack, size // Dynamically expandable stack. Akin to DqnMemStack_InitWithFixedSize() except if the MemStack does // not have enough space for allocation it will automatically attach another MemBlock using // DqnMem_Alloc(). -DQN_FILE_SCOPE bool DqnMemStack_Init (DqnMemStack *const stack, size_t size, const bool zeroClear, const u32 byteAlign = 4); +DQN_FILE_SCOPE bool DqnMemStack_Init(DqnMemStack *const stack, size_t size, const bool zeroClear, const u32 byteAlign = 4); //////////////////////////////////////////////////////////////////////////////// // DqnMemStack Memory Operations @@ -270,13 +282,13 @@ DQN_FILE_SCOPE bool DqnMemStack_Init (DqnMemStack *const stack, size_t size, con // Allocate memory from the MemStack. // size: "size" gets aligned to the byte alignment of the stack. // return: NULL if out of space OR stack is using fixed memory/size OR stack full and platform malloc fails. -DQN_FILE_SCOPE void *DqnMemStack_Push (DqnMemStack *const stack, size_t size); +DQN_FILE_SCOPE void *DqnMemStack_Push(DqnMemStack *const stack, size_t size); // Frees the given ptr. It MUST be the last allocated item in the stack, fails otherwise. -DQN_FILE_SCOPE bool DqnMemStack_Pop (DqnMemStack *const stack, void *ptr, size_t size); +DQN_FILE_SCOPE bool DqnMemStack_Pop(DqnMemStack *const stack, void *ptr, size_t size); // Frees all blocks belonging to this stack. -DQN_FILE_SCOPE void DqnMemStack_Free (DqnMemStack *const stack); +DQN_FILE_SCOPE void DqnMemStack_Free(DqnMemStack *const stack); // Frees the specified block belonging to the stack. // return: FALSE if block doesn't belong this into calls DqnMem_Free() or invalid args. @@ -284,7 +296,7 @@ DQN_FILE_SCOPE bool DqnMemStack_FreeMemBlock(DqnMemStack *const stack, DqnMemSt // Frees the last-most memory block. If last block, free that block making the MemStack blockless. // Next allocate will attach a block. -DQN_FILE_SCOPE bool DqnMemStack_FreeLastBlock (DqnMemStack *const stack); +DQN_FILE_SCOPE bool DqnMemStack_FreeLastBlock(DqnMemStack *const stack); // Reset the current memory block usage to 0. DQN_FILE_SCOPE void DqnMemStack_ClearCurrBlock(DqnMemStack *const stack, const bool zeroClear); @@ -300,23 +312,29 @@ DQN_FILE_SCOPE void DqnMemStack_ClearCurrBlock(DqnMemStack *const stack, const // regions typedef struct DqnMemStackTempRegion { - DqnMemStack *stack; + // The stack associated with this TempRegion + DqnMemStack *stack; + + // Store memBlock state to revert back to on DqnMemStackTempRegion_End() struct DqnMemStackBlock *startingBlock; size_t used; } DqnMemStackTempRegion; // region: Takes pointer to a zero-cleared DqnMemStackTempRegion struct. // return: FALSE if arguments are invalid. -DQN_FILE_SCOPE bool DqnMemStackTempRegion_Begin(DqnMemStackTempRegion *region, DqnMemStack *const stack); +DQN_FILE_SCOPE bool DqnMemStackTempRegion_Begin(DqnMemStackTempRegion *const region, DqnMemStack *const stack); DQN_FILE_SCOPE void DqnMemStackTempRegion_End (DqnMemStackTempRegion region); -// In cplusplus this allows automatic Begin/End pairs upon constructor of DqnMemStackTempRegionScoped. -// isInit: Constructor was successful #ifdef DQN_CPP_MODE -struct DqnMemStackTempRegionScoped +// Region guard automatically starts a region on construction and ends a region +// on destruction. +struct DqnMemStackTempRegionGuard { - DqnMemStackTempRegionScoped(DqnMemStack *const stack, bool *const succeeded); - ~DqnMemStackTempRegionScoped(); + // lock: Takes a pointer to a pre-existing and already initialised stack + // bool: Pass in (optionally) a pointer to a bool which returns whether construction was successful. + // It can be FALSE if stack is NULL. + DqnMemStackTempRegionGuard(DqnMemStack *const stack, bool *const succeeded); + ~DqnMemStackTempRegionGuard(); private: DqnMemStackTempRegion tempMemStack; @@ -330,10 +348,12 @@ private: // NOT be modified directly, only indirectly through the regular API. typedef struct DqnMemStackBlock { + // The raw memory block, size and used count u8 *memory; size_t size; size_t used; + // The allocator uses a linked list approach for additional blocks beyond capacity DqnMemStackBlock *prevBlock; } DqnMemStackBlock; @@ -404,7 +424,7 @@ typedef struct DqnMemAPICallbackResult enum DqnMemAPICallbackType type; } DqnMemAPICallbackResult; -// Function prototype for implementing a DqnMemAPI_Callback. You must fill out the result structure. +// Function prototype for implementing a DqnMemAPI_Callback. You must fill out the "result" structure. // result: Is always guaranteed to be a valid pointer. typedef void DqnMemAPI_Callback(DqnMemAPICallbackInfo info, DqnMemAPICallbackResult *result); @@ -424,41 +444,32 @@ DQN_FILE_SCOPE DqnMemAPI DqnMemAPI_DefaultUseCalloc(); template struct DqnArray { + // Function pointers to custom allocators DqnMemAPI memAPI; + // Array state u64 count; u64 capacity; - T *data; + T *data; + + // API + void Init (const size_t capacity, DqnMemAPI memAPI = DqnMemAPI_DefaultUseCalloc()); + bool Free (); + bool Grow (); + T *Push (const T item); + void Pop (); + T *Get (u64 index); + bool Clear (); + bool Remove (u64 index); + bool RemoveStable(u64 index); }; FILE_SCOPE const char *const DQN_MEM_API_CALLBACK_RESULT_TYPE_INCORRECT = "DqnMemAPICallbackResult type is incorrect"; -// Implementation taken from Milton, developed by Serge at -// https://github.com/serge-rgb/milton#license template -bool DqnArray_Free(DqnArray *array) -{ - if (array && array->data) - { - // TODO(doyle): Right now we assume free always works, and it probably should? - size_t sizeToFree = (size_t)array->capacity * sizeof(T); - DqnMemAPICallbackInfo info = DqnMemAPIInternal_CallbackInfoAskFree( - array->memAPI, array->data, sizeToFree); - array->memAPI.callback(info, NULL); - array->data = NULL; - - array->count = 0; - array->capacity = 0; - return true; - } - - return false; -} - -template -bool DqnArray_Init(DqnArray *array, size_t capacity, - DqnMemAPI memAPI = DqnMemAPI_DefaultUseCalloc()) +bool DqnArray_Init(DqnArray *const array, const size_t capacity, + const DqnMemAPI memAPI = DqnMemAPI_DefaultUseCalloc()) { if (!array) return false; if (array->data) DqnArray_Free(array); @@ -482,8 +493,30 @@ bool DqnArray_Init(DqnArray *array, size_t capacity, return true; } +// Implementation taken from Milton, developed by Serge at +// https://github.com/serge-rgb/milton#license template -bool DqnArray_Grow(DqnArray *array) +bool DqnArray_Free(DqnArray *const array) +{ + if (array && array->data) + { + // TODO(doyle): Right now we assume free always works, and it probably should? + size_t sizeToFree = (size_t)array->capacity * sizeof(T); + DqnMemAPICallbackInfo info = DqnMemAPIInternal_CallbackInfoAskFree( + array->memAPI, array->data, sizeToFree); + array->memAPI.callback(info, NULL); + array->data = NULL; + + array->count = 0; + array->capacity = 0; + return true; + } + + return false; +} + +template +bool DqnArray_Grow(DqnArray *const array) { if (!array || !array->data) return false; @@ -518,7 +551,7 @@ bool DqnArray_Grow(DqnArray *array) } template -T *DqnArray_Push(DqnArray *array, T item) +T *DqnArray_Push(DqnArray *const array, const T item) { if (!array) return NULL; @@ -544,7 +577,7 @@ void DqnArray_Pop(DqnArray *array) } template -T *DqnArray_Get(DqnArray *array, u64 index) +T *DqnArray_Get(DqnArray *const array, const u64 index) { T *result = NULL; if (index >= 0 && index <= array->count) result = &array->data[index]; @@ -552,7 +585,7 @@ T *DqnArray_Get(DqnArray *array, u64 index) } template -bool DqnArray_Clear(DqnArray *array) +bool DqnArray_Clear(DqnArray *const array) { if (array) { @@ -564,7 +597,7 @@ bool DqnArray_Clear(DqnArray *array) } template -bool DqnArray_Remove(DqnArray *array, u64 index) +bool DqnArray_Remove(DqnArray *const array, const u64 index) { if (!array) return false; if (index >= array->count) return false; @@ -583,7 +616,7 @@ bool DqnArray_Remove(DqnArray *array, u64 index) } template -bool DqnArray_RemoveStable(DqnArray *array, u64 index) +bool DqnArray_RemoveStable(DqnArray *const array, const u64 index) { if (!array) return false; if (index >= array->count) return false; @@ -609,6 +642,17 @@ bool DqnArray_RemoveStable(DqnArray *array, u64 index) array->count--; return true; } + +template void DqnArray::Init (const size_t capacity, DqnMemAPI memAPI) { DqnArray_Init(this, capacity, memAPI); } +template bool DqnArray::Free () { return DqnArray_Free(this); } +template bool DqnArray::Grow () { return DqnArray_Grow(this);} +template T* DqnArray::Push (const T item) { return DqnArray_Push(this, item); } +template void DqnArray::Pop () { DqnArray_Pop(this); } +template T* DqnArray::Get (const u64 index) { return DqnArray_Get(this, index); } +template bool DqnArray::Clear() { return DqnArray_Clear (this); } +template bool DqnArray::Remove (const u64 index) { return DqnArray_Remove(this, index); } +template bool DqnArray::RemoveStable(const u64 index) { return DqnArray_RemoveStable(this, index); } + #endif // DQN_CPP_MODE //////////////////////////////////////////////////////////////////////////////// @@ -857,18 +901,18 @@ DQN_FILE_SCOPE bool DqnChar_IsAlphanum(char c); // #DqnStr Public API - Str Operations //////////////////////////////////////////////////////////////////////////////// // return: 0 if equal. 0 < if a is before b, > 0 if a is after b -DQN_FILE_SCOPE i32 DqnStr_Cmp(const char *a, const char *b); +DQN_FILE_SCOPE i32 DqnStr_Cmp(const char *const a, const char *const b); // return: String length not including the NULL terminator. 0 if invalid args. -DQN_FILE_SCOPE i32 DqnStr_Len (const char *a); +DQN_FILE_SCOPE i32 DqnStr_Len (const char *const a); // Get the String length starting from a, up to and not including the first delimiter character. -DQN_FILE_SCOPE i32 DqnStr_LenDelimitWith(const char *a, const char delimiter); +DQN_FILE_SCOPE i32 DqnStr_LenDelimitWith(char *const a, const char delimiter); // return: The dest argument, NULL if args invalid (i.e. NULL pointers or numChars < 0) -DQN_FILE_SCOPE char *DqnStr_Copy(char *dest, const char *src, i32 numChars); +DQN_FILE_SCOPE char *DqnStr_Copy(char *const dest, const char *const src, const i32 numChars); -DQN_FILE_SCOPE bool DqnStr_Reverse (char *buf, const i32 bufSize); +DQN_FILE_SCOPE bool DqnStr_Reverse (char *const buf, const i32 bufSize); DQN_FILE_SCOPE i32 DqnStr_FindFirstOccurence(const char *const src, const i32 srcLen, const char *const find, const i32 findLen); DQN_FILE_SCOPE bool DqnStr_HasSubstring (const char *const src, const i32 srcLen, const char *const find, const i32 findLen); @@ -876,14 +920,14 @@ DQN_FILE_SCOPE bool DqnStr_HasSubstring (const char *const src, const i32 s #define DQN_64BIT_NUM_MAX_STR_SIZE 21 // Return the len of the derived string. If buf is NULL and or bufSize is 0 the // function returns the required string length for the integer. -DQN_FILE_SCOPE i32 Dqn_I64ToStr(i64 value, char *const buf, const i32 bufSize); +DQN_FILE_SCOPE i32 Dqn_I64ToStr(const i64 value, char *const buf, const i32 bufSize); DQN_FILE_SCOPE i64 Dqn_StrToI64(const char *const buf, const i32 bufSize); // WARNING: Not robust, precision errors and whatnot but good enough! DQN_FILE_SCOPE f32 Dqn_StrToF32(const char *const buf, const i32 bufSize); // Both return the number of bytes read, return 0 if invalid codepoint or UTF8 -DQN_FILE_SCOPE u32 Dqn_UCSToUTF8(u32 *dest, u32 character); -DQN_FILE_SCOPE u32 Dqn_UTF8ToUCS(u32 *dest, u32 character); +DQN_FILE_SCOPE u32 Dqn_UCSToUTF8(u32 *const dest, const u32 character); +DQN_FILE_SCOPE u32 Dqn_UTF8ToUCS(u32 *const dest, const u32 character); //////////////////////////////////////////////////////////////////////////////// // #DqnWChar Public API - WChar Operations @@ -891,8 +935,8 @@ DQN_FILE_SCOPE u32 Dqn_UTF8ToUCS(u32 *dest, u32 character); DQN_FILE_SCOPE bool DqnWChar_IsDigit(const wchar_t c); DQN_FILE_SCOPE wchar_t DqnWChar_ToLower(const wchar_t c); -DQN_FILE_SCOPE i32 DqnWStr_Len(const wchar_t *a); -DQN_FILE_SCOPE i32 DqnWStr_Cmp(const wchar_t *a, const wchar_t *b); +DQN_FILE_SCOPE i32 DqnWStr_Len(const wchar_t *const a); +DQN_FILE_SCOPE i32 DqnWStr_Cmp(const wchar_t *const a, const wchar_t *const b); DQN_FILE_SCOPE bool Dqn_WStrReverse(wchar_t *buf, const i32 bufSize); DQN_FILE_SCOPE i32 Dqn_WStrToI32(const wchar_t *const buf, const i32 bufSize); @@ -954,10 +998,23 @@ enum DqnFileAction typedef struct DqnFile { - u32 permissionFlags; + u32 permissionFlags; void *handle; size_t size; +#if defined(DQN_CPP_MODE) + // If raiiCleanup is true, close() is called in the destructor on scope exit. Can be changed at + // any point by user. + bool raiiCleanup; + DqnFile (const bool raiiCleanup = false); + ~DqnFile(); + + bool Open (const char *const path, const u32 permissionFlags_, const enum DqnFileAction action); + bool OpenW(const wchar_t *const path, const u32 permissionFlags_, const enum DqnFileAction action); + size_t Write(u8 *const buffer, const size_t numBytesToWrite, const size_t fileOffset); + size_t Read (u8 *const buffer, const size_t numBytesToRead); + void Close(); +#endif } DqnFile; // NOTE: W(ide) versions of functions only work on Win32, since Unix is UTF-8 compatible. @@ -973,8 +1030,10 @@ DQN_FILE_SCOPE bool DqnFile_OpenW(const wchar_t *const path, DqnFile *const file DQN_FILE_SCOPE size_t DqnFile_Write(const DqnFile *const file, u8 *const buffer, const size_t numBytesToWrite, const size_t fileOffset); // return: The number of bytes read. 0 if invalid args or it failed to read. -DQN_FILE_SCOPE size_t DqnFile_Read (const DqnFile file, u8 *const buffer, const size_t numBytesToRead); -DQN_FILE_SCOPE void DqnFile_Close(DqnFile *const file); +DQN_FILE_SCOPE size_t DqnFile_Read (const DqnFile *const file, u8 *const buffer, const size_t numBytesToRead); + +// File close invalidates the handle after it is called. +DQN_FILE_SCOPE void DqnFile_Close(DqnFile *const file); // NOTE: You can't delete a file unless the handle has been closed to it on Win32. DQN_FILE_SCOPE bool DqnFile_Delete (const char *const path); @@ -1015,8 +1074,31 @@ DQN_FILE_SCOPE f64 DqnTimer_NowInS (); typedef struct DqnLock { CRITICAL_SECTION win32Handle; + +#if defined(DQN_CPP_MODE) + bool Init(const u32 spinCount = 16000); + void Acquire(); + void Release(); + void Delete(); +#endif } DqnLock; +#if defined(DQN_CPP_MODE) +// Lock guard automatically acquires a lock on construction and releases a lock +// on destruction. +struct DqnLockGuard +{ + // lock: Takes a pointer to a pre-existing and already initialised lock + // bool: Pass in (optionally) a pointer to a bool which returns whether a lock was successful. + // FALSE if lock is NULL. + DqnLockGuard(DqnLock *const lock_, bool *const succeeded); + ~DqnLockGuard(); + +private: + DqnLock *lock; +}; +#endif + DQN_FILE_SCOPE bool DqnLock_Init (DqnLock *const lock, const u32 spinCount = 16000); DQN_FILE_SCOPE void DqnLock_Acquire(DqnLock *const lock); DQN_FILE_SCOPE void DqnLock_Release(DqnLock *const lock); @@ -1045,6 +1127,7 @@ DQN_FILE_SCOPE u32 DqnAtomic_Sub32 (u32 volatile *src); // wait for all jobs to complete using DqnJobQueue_TryExecuteNextJob() or spinlock on // DqnJobQueue_AllJobsComplete(). Alternatively you can combine both for the main thread to help // complete work and not move on until all tasks are complete. + typedef struct DqnJobQueue DqnJobQueue; typedef void DqnJob_Callback(DqnJobQueue *const queue, void *const userData); @@ -1054,20 +1137,54 @@ typedef struct DqnJob void *userData; } DqnJob; -// memSize: The size of the supplied memory. If "mem" is NULL OR "memsize" is NULL/0 OR "queueSize" -// is 0, the function puts required size it needs into "memSize". -// return: The JobQueue or NULL if args invalid. -DQN_FILE_SCOPE DqnJobQueue *DqnJobQueue_InitWithMem(const void *const mem, size_t *const memSize, const u32 queueSize, const u32 numThreads); +typedef struct DqnJobQueue +{ + // JobList Circular Array, is setup in Init() + DqnJob *jobList; + u32 size; + + // NOTE(doyle): Modified by main+worker threads + u32 volatile jobToExecuteIndex; + u32 volatile numJobsToComplete; + void *semaphore; + + // NOTE: Modified by main thread ONLY + u32 volatile jobInsertIndex; + +#if defined(DQN_CPP_MODE) + bool Init (const DqnJob *const jobList_, const u32 jobListSize, const u32 numThreads); + bool AddJob (const DqnJob job); + + void BlockAndCompleteAllJobs(); + bool TryExecuteNextJob(); + bool AllJobsComplete (); +#endif +} DqnJobQueue; + +// TODO(doyle): Queue delete, thread delete + +// queue: Pass a pointer to a zero cleared DqnJobQueue struct +// jobList: Pass in a pointer to an array of DqnJob's +// jobListSize: The number of elements in the jobList array +// numThreads: The number of threads the queue should request from the OS for working on the queue +// return: FALSE if invalid args i.e. NULL ptrs or jobListSize & numThreads == 0 +DQN_FILE_SCOPE bool DqnJobQueue_Init(DqnJobQueue *const queue, const DqnJob *const jobList, + const u32 jobListSize, const u32 numThreads); // return: FALSE if the job is not able to be added, this occurs if the queue is full. DQN_FILE_SCOPE bool DqnJobQueue_AddJob(DqnJobQueue *const queue, const DqnJob job); +// Helper function that combines TryExecuteNextJob() and AllJobsComplete(), i.e. +// complete all work before moving on. Does nothing if queue is NULL. +DQN_FILE_SCOPE void DqnJobQueue_BlockAndCompleteAllJobs(DqnJobQueue *const queue); + // return: TRUE if there was a job to execute (the calling thread executes it). FALSE if it could // not get a job. It may return FALSE whilst there are still jobs, this means that another thread // has taken the job before the calling thread could and should NOT be used to determine if there // are any remaining jobs left. That can only be definitively known using // DqnJobQueue_AllJobsComplete(). This is typically combined like so .. -// while (DqnJobQueue_TryExecuteNextJob(queue) && !DqnJobQueue_AllJobsComplete(queue)); +// while (DqnJobQueue_TryExecuteNextJob(queue) || !DqnJobQueue_AllJobsComplete(queue)); +// Return FALSE also if queue is a NULL pointer. DQN_FILE_SCOPE bool DqnJobQueue_TryExecuteNextJob(DqnJobQueue *const queue); DQN_FILE_SCOPE bool DqnJobQueue_AllJobsComplete (DqnJobQueue *const queue); @@ -1676,7 +1793,7 @@ DQN_FILE_SCOPE void DqnMem_Clear(void *const memory, const u8 clearValue, const if (memory) memset(memory, clearValue, size); } -DQN_FILE_SCOPE void *DqnMem_Realloc(void *const memory, const size_t newSize) +DQN_FILE_SCOPE void *DqnMem_Realloc(void *memory, const size_t newSize) { void *result = realloc(memory, newSize); return result; @@ -1714,10 +1831,9 @@ DqnMemStackInternal_AllocateBlock(u32 byteAlign, size_t size) // #DqnMemStack CPP Implementation //////////////////////////////////////////////////////////////////////////////// #if defined(DQN_CPP_MODE) -#include -bool DqnMemStack::InitWithFixedMem (u8 *const mem, const size_t memSize, const u32 byteAlignment) { return DqnMemStack_InitWithFixedMem (this, mem, memSize, byteAlign); } -bool DqnMemStack::InitWithFixedSize(const size_t size, const bool zeroClear, const u32 byteAlignment) { return DqnMemStack_InitWithFixedSize(this, size, zeroClear, byteAlign); } -bool DqnMemStack::Init (const size_t size, const bool zeroClear, const u32 byteAlignment) { return DqnMemStack_Init (this, size, zeroClear, byteAlign); } +bool DqnMemStack::InitWithFixedMem (u8 *const mem, const size_t memSize, const u32 byteAlignment) { return DqnMemStack_InitWithFixedMem (this, mem, memSize, byteAlignment); } +bool DqnMemStack::InitWithFixedSize(const size_t size, const bool zeroClear, const u32 byteAlignment) { return DqnMemStack_InitWithFixedSize(this, size, zeroClear, byteAlignment); } +bool DqnMemStack::Init (const size_t size, const bool zeroClear, const u32 byteAlignment) { return DqnMemStack_Init (this, size, zeroClear, byteAlignment); } void *DqnMemStack::Push(size_t size) { return DqnMemStack_Push(this, size); } void DqnMemStack::Pop (void *const ptr, size_t size) { DqnMemStack_Pop (this, ptr, size); } @@ -1737,7 +1853,7 @@ DqnMemStackTempRegion DqnMemStack::TempRegionBegin() void DqnMemStack::TempRegionEnd(DqnMemStackTempRegion region) { DqnMemStackTempRegion_End(region); } // NOTE: Guaranteed to always succeed. Fails when arguments are invalid, like a NULL ptr which is impossible here. -DqnMemStackTempRegionScoped DqnMemStack::TempRegionScoped() { return DqnMemStackTempRegionScoped(this, NULL); } +DqnMemStackTempRegionGuard DqnMemStack::TempRegionGuard() { return DqnMemStackTempRegionGuard(this, NULL); } DqnMemStackBlock *DqnMemStack::AllocateCompatibleBlock(size_t size) { return DqnMemStack_AllocateCompatibleBlock(this, size); } bool DqnMemStack::AttachBlock (DqnMemStackBlock *const newBlock) { return DqnMemStack_AttachBlock (this, newBlock); } @@ -1947,7 +2063,7 @@ DQN_FILE_SCOPE void DqnMemStack_ClearCurrBlock(DqnMemStack *const stack, const b //////////////////////////////////////////////////////////////////////////////// // #DqnMemStackTempRegion Implementation //////////////////////////////////////////////////////////////////////////////// -DQN_FILE_SCOPE bool DqnMemStackTempRegion_Begin(DqnMemStackTempRegion *region, +DQN_FILE_SCOPE bool DqnMemStackTempRegion_Begin(DqnMemStackTempRegion *const region, DqnMemStack *const stack) { if (!region || !stack) return false; @@ -1977,13 +2093,20 @@ DQN_FILE_SCOPE void DqnMemStackTempRegion_End(DqnMemStackTempRegion region) } #ifdef DQN_CPP_MODE -DqnMemStackTempRegionScoped::DqnMemStackTempRegionScoped(DqnMemStack *const stack, bool *const succeeded) +DqnMemStackTempRegionGuard::DqnMemStackTempRegionGuard(DqnMemStack *const stack, bool *const succeeded) { - bool result = DqnMemStackTempRegion_Begin(&this->tempMemStack, stack); - if (succeeded) *succeeded = result; + if (stack) + { + DQN_ASSERT_HARD(DqnMemStackTempRegion_Begin(&this->tempMemStack, stack)); + if (succeeded) *succeeded = true; + } + else + { + if (succeeded) *succeeded = false; + } } -DqnMemStackTempRegionScoped::~DqnMemStackTempRegionScoped() +DqnMemStackTempRegionGuard::~DqnMemStackTempRegionGuard() { DqnMemStackTempRegion_End(this->tempMemStack); } @@ -3026,23 +3149,26 @@ DQN_FILE_SCOPE bool DqnChar_IsAlphaNum(char c) //////////////////////////////////////////////////////////////////////////////// // #DqnStr Implementation //////////////////////////////////////////////////////////////////////////////// -DQN_FILE_SCOPE i32 DqnStr_Cmp(const char *a, const char *b) +DQN_FILE_SCOPE i32 DqnStr_Cmp(const char *const a, const char *const b) { if (!a && !b) return -1; if (!a) return -1; if (!b) return -1; - while ((*a) == (*b)) + char const *aPtr = a; + char const *bPtr = b; + + while ((*aPtr) == (*bPtr)) { - if (!(*a)) return 0; - a++; - b++; + if (!(*aPtr)) return 0; + aPtr++; + bPtr++; } - return (((*a) < (*b)) ? -1 : 1); + return (((*aPtr) < (*bPtr)) ? -1 : 1); } -DQN_FILE_SCOPE i32 DqnStr_Len(const char *a) +DQN_FILE_SCOPE i32 DqnStr_Len(const char *const a) { i32 result = 0; while (a && a[result]) result++; @@ -3057,7 +3183,7 @@ DQN_FILE_SCOPE i32 DqnStr_LenDelimitWith(const char *a, const char delimiter) return result; } -DQN_FILE_SCOPE char *DqnStr_Copy(char *dest, const char *src, i32 numChars) +DQN_FILE_SCOPE char *DqnStr_Copy(char *const dest, const char *const src, const i32 numChars) { if (!dest) return NULL; if (!src) return NULL; @@ -3069,7 +3195,7 @@ DQN_FILE_SCOPE char *DqnStr_Copy(char *dest, const char *src, i32 numChars) return dest; } -DQN_FILE_SCOPE bool DqnStr_Reverse(char *buf, const i32 bufSize) +DQN_FILE_SCOPE bool DqnStr_Reverse(char *const buf, const i32 bufSize) { if (!buf) return false; i32 mid = bufSize / 2; @@ -3133,7 +3259,7 @@ DQN_FILE_SCOPE bool DqnStr_HasSubstring(const char *const src, const i32 srcLen, return true; } -DQN_FILE_SCOPE i32 Dqn_I64ToStr(i64 value, char *const buf, const i32 bufSize) +DQN_FILE_SCOPE i32 Dqn_I64ToStr(const i64 value, char *const buf, const i32 bufSize) { bool validBuffer = true; if (!buf || bufSize == 0) validBuffer = false; @@ -3353,7 +3479,7 @@ DQN_FILE_SCOPE f32 Dqn_StrToF32(const char *const buf, const i32 bufSize) The UCS code values 0xd800–0xdfff (UTF-16 surrogates) as well as 0xfffe and 0xffff (UCS noncharacters) should not appear in conforming UTF-8 streams. */ -DQN_FILE_SCOPE u32 Dqn_UCSToUTF8(u32 *dest, u32 character) +DQN_FILE_SCOPE u32 Dqn_UCSToUTF8(u32 *const dest, const u32 character) { if (!dest) return 0; @@ -3422,7 +3548,7 @@ DQN_FILE_SCOPE u32 Dqn_UCSToUTF8(u32 *dest, u32 character) return 0; } -DQN_FILE_SCOPE u32 Dqn_UTF8ToUCS(u32 *dest, u32 character) +DQN_FILE_SCOPE u32 Dqn_UTF8ToUCS(u32 *const dest, const u32 character) { if (!dest) return 0; @@ -3518,27 +3644,30 @@ DQN_FILE_SCOPE wchar_t DqnWChar_ToLower(const wchar_t c) //////////////////////////////////////////////////////////////////////////////// // #DqnWStr Implementation //////////////////////////////////////////////////////////////////////////////// -DQN_FILE_SCOPE i32 DqnWStr_Len(const wchar_t *a) +DQN_FILE_SCOPE i32 DqnWStr_Len(const wchar_t *const a) { i32 result = 0; while (a && a[result]) result++; return result; } -DQN_FILE_SCOPE i32 DqnWStr_Cmp(const wchar_t *a, const wchar_t *b) +DQN_FILE_SCOPE i32 DqnWStr_Cmp(const wchar_t *const a, const wchar_t *const b) { if (!a && !b) return -1; if (!a) return -1; if (!b) return -1; - while ((*a) == (*b)) + const wchar_t *aPtr = a; + const wchar_t *bPtr = b; + + while ((*aPtr) == (*bPtr)) { - if (!(*a)) return 0; - a++; - b++; + if (!(*aPtr)) return 0; + aPtr++; + bPtr++; } - return (((*a) < (*b)) ? -1 : 1); + return (((*aPtr) < (*bPtr)) ? -1 : 1); } DQN_FILE_SCOPE bool Dqn_WStrReverse(wchar_t *buf, const i32 bufSize) @@ -5425,6 +5554,36 @@ void DqnIni_PropertyValueSet(DqnIni *ini, int section, int property, #include // readdir()/opendir()/closedir() #endif +//////////////////////////////////////////////////////////////////////////////// +// XPlatform > #DqnFile CPP Implementation +//////////////////////////////////////////////////////////////////////////////// +DqnFile::DqnFile (const bool raiiCleanup) { this->raiiCleanup = raiiCleanup; } +DqnFile::~DqnFile() { if (this->raiiCleanup) this->Close(); } + +bool DqnFile::Open(const char *const path, const u32 permissionFlags_, + const enum DqnFileAction action) +{ + return DqnFile_Open(path, this, permissionFlags, action); +} + +bool DqnFile::OpenW(const wchar_t *const path, const u32 permissionFlags_, + const enum DqnFileAction action) +{ + return DqnFile_OpenW(path, this, permissionFlags, action); +} + +size_t DqnFile::Write(u8 *const buffer, const size_t numBytesToWrite, const size_t fileOffset) +{ + return DqnFile_Write(this, buffer, numBytesToWrite, fileOffset); +} + +size_t DqnFile::Read(u8 *const buffer, const size_t numBytesToRead) +{ + return DqnFile_Read(this, buffer, numBytesToRead); +} + +void DqnFile::Close() { DqnFile_Close(this); } + //////////////////////////////////////////////////////////////////////////////// // XPlatform > #DqnFileInternal Implementation //////////////////////////////////////////////////////////////////////////////// @@ -5788,16 +5947,16 @@ DQN_FILE_SCOPE size_t DqnFile_Write(const DqnFile *const file, return numBytesWritten; } -DQN_FILE_SCOPE size_t DqnFile_Read(DqnFile file, u8 *const buffer, +DQN_FILE_SCOPE size_t DqnFile_Read(const DqnFile *const file, u8 *const buffer, const size_t numBytesToRead) { size_t numBytesRead = 0; - if (file.handle && buffer) + if (file && file->handle && buffer) { #if defined(DQN_WIN32_PLATFORM) DWORD bytesToRead = (DWORD)numBytesToRead; DWORD bytesRead = 0; - HANDLE win32Handle = file.handle; + HANDLE win32Handle = file->handle; BOOL result = ReadFile(win32Handle, (void *)buffer, bytesToRead, &bytesRead, NULL); @@ -5812,10 +5971,10 @@ DQN_FILE_SCOPE size_t DqnFile_Read(DqnFile file, u8 *const buffer, #elif defined(DQN_UNIX_PLATFORM) // TODO(doyle): Syscall version const size_t ITEMS_TO_READ = 1; - if (fread(buffer, numBytesToRead, ITEMS_TO_READ, (FILE *)file.handle) == + if (fread(buffer, numBytesToRead, ITEMS_TO_READ, (FILE *)file->handle) == ITEMS_TO_READ) { - rewind((FILE *)file.handle); + rewind((FILE *)file->handle); numBytesRead = ITEMS_TO_READ * numBytesToRead; } else @@ -5990,6 +6149,35 @@ void DqnLock_Delete(DqnLock *const lock) #endif } +//////////////////////////////////////////////////////////////////////////////// +// Win32Platform > #DqnLock CPP Implementation +//////////////////////////////////////////////////////////////////////////////// +void DqnLock::Acquire() { DqnLock_Acquire(this); } +void DqnLock::Release() { DqnLock_Release(this); } +void DqnLock::Delete() { DqnLock_Delete (this); } + +//////////////////////////////////////////////////////////////////////////////// +// Win32Platform > #DqnLockGuard CPP Implementation +//////////////////////////////////////////////////////////////////////////////// +DqnLockGuard::DqnLockGuard(DqnLock *const lock_, bool *const succeeded) +{ + if (lock_) + { + this->lock = lock_; + this->lock->Acquire(); + if (succeeded) *succeeded = true; + } + else + { + if (succeeded) *succeeded = false; + } +} + +DqnLockGuard::~DqnLockGuard() +{ + if (this->lock) this->lock->Release(); +} + //////////////////////////////////////////////////////////////////////////////// // Win32Platform > #DqnAtomic Implementation //////////////////////////////////////////////////////////////////////////////// @@ -6030,23 +6218,6 @@ DQN_FILE_SCOPE u32 DqnAtomic_Sub32(u32 volatile *src) #endif } -//////////////////////////////////////////////////////////////////////////////// -// Win32Platform > #DqnJobQueue Implementation -//////////////////////////////////////////////////////////////////////////////// -typedef struct DqnJobQueue -{ - DqnJob *jobList; - u32 size; - - // NOTE: Modified by main+worker threads - u32 volatile jobToExecuteIndex; - u32 volatile numJobsToComplete; - void *semaphore; - - // NOTE: Modified by main thread ONLY - u32 volatile jobInsertIndex; -} DqnJobQueue; - //////////////////////////////////////////////////////////////////////////////// // Win32Platform > #DqnJobQueueInternal Implementation //////////////////////////////////////////////////////////////////////////////// @@ -6095,35 +6266,13 @@ FILE_SCOPE u32 DqnJobQueueInternal_ThreadCallback(void *threadParam) //////////////////////////////////////////////////////////////////////////////// // Win32Platform > #DqnJobQueue Implementation //////////////////////////////////////////////////////////////////////////////// -DQN_FILE_SCOPE DqnJobQueue *DqnJobQueue_InitWithMem(const void *const mem, size_t *const memSize, - const u32 queueSize, const u32 numThreads) +DQN_FILE_SCOPE bool DqnJobQueue_Init(DqnJobQueue *const queue, DqnJob *const jobList, + const u32 jobListSize, const u32 numThreads) { - DqnJobQueue emptyQueue = {}; - size_t reqStructSize = sizeof(emptyQueue); - size_t reqQueueSize = sizeof(*emptyQueue.jobList) * queueSize; + if (!queue || !jobList || jobListSize == 0 || numThreads == 0) return false; + queue->jobList = jobList; + queue->size = jobListSize; - if (!mem || !memSize || *memSize == 0 || queueSize == 0) - { - *memSize = reqStructSize + reqQueueSize; - return NULL; - } - - u8 *memPtr = (u8 *)mem; - - // Sub-allocate Queue - DqnJobQueue *queue = (DqnJobQueue *)memPtr; - *queue = emptyQueue; - queue->size = queueSize; - - // Sub-allocate jobList - memPtr += reqStructSize; - queue->jobList = (DqnJob *)memPtr; - - // Validate memPtr used size - memPtr += reqQueueSize; - DQN_ASSERT_HARD((size_t)(memPtr - (u8 *)mem) <= *memSize); - - // Create semaphore #ifdef DQN_WIN32_PLATFORM queue->semaphore = (void *)CreateSemaphore(NULL, 0, numThreads, NULL); DQN_ASSERT_HARD(queue->semaphore); @@ -6136,7 +6285,8 @@ DQN_FILE_SCOPE DqnJobQueue *DqnJobQueue_InitWithMem(const void *const mem, size_ DQN_JOB_QUEUE_INTERNAL_THREAD_DEFAULT_STACK_SIZE, DqnJobQueueInternal_ThreadCallback, (void *)queue, numThreads); DQN_ASSERT_HARD(numThreads == numThreadsCreated); - return queue; + + return true; } DQN_FILE_SCOPE bool DqnJobQueue_AddJob(DqnJobQueue *const queue, const DqnJob job) @@ -6157,8 +6307,16 @@ DQN_FILE_SCOPE bool DqnJobQueue_AddJob(DqnJobQueue *const queue, const DqnJob jo return true; } +DQN_FILE_SCOPE void DqnJobQueue_BlockAndCompleteAllJobs(DqnJobQueue *const queue) +{ + while (DqnJobQueue_TryExecuteNextJob(queue) || !DqnJobQueue_AllJobsComplete(queue)) + ; +} + DQN_FILE_SCOPE bool DqnJobQueue_TryExecuteNextJob(DqnJobQueue *const queue) { + if (!queue) return false; + u32 originalJobToExecute = queue->jobToExecuteIndex; if (originalJobToExecute != queue->jobInsertIndex) { @@ -6185,10 +6343,26 @@ DQN_FILE_SCOPE bool DqnJobQueue_TryExecuteNextJob(DqnJobQueue *const queue) DQN_FILE_SCOPE bool DqnJobQueue_AllJobsComplete(DqnJobQueue *const queue) { + if (!queue) return false; + bool result = (queue->numJobsToComplete == 0); return result; } +//////////////////////////////////////////////////////////////////////////////// +// Win32Platform > #DqnJobQueue CPP Implementation +//////////////////////////////////////////////////////////////////////////////// +bool DqnJobQueue::Init(const DqnJob *const jobList_, const u32 jobListSize, const u32 numThreads) +{ + bool result = DqnJobQueue_Init(this, jobList, jobListSize, numThreads); + return result; +} + +bool DqnJobQueue::AddJob (const DqnJob job) { return DqnJobQueue_AddJob(this, job); } +void DqnJobQueue::BlockAndCompleteAllJobs() { DqnJobQueue_BlockAndCompleteAllJobs(this); } +bool DqnJobQueue::TryExecuteNextJob() { return DqnJobQueue_TryExecuteNextJob(this); } +bool DqnJobQueue::AllJobsComplete () { return DqnJobQueue_AllJobsComplete(this); } + //////////////////////////////////////////////////////////////////////////////// // Win32Platform > #DqnWin32 Implementation ////////////////////////////////////////////////////////////////////////////////