Update dqn lib

This commit is contained in:
Doyle Thai 2017-06-24 15:33:18 +10:00
parent 54c6abd9d7
commit e7ebe1e243
5 changed files with 325 additions and 152 deletions

View File

@ -936,7 +936,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
{
DTRDebug_TestMeshFaceAndVertexParser(&state->mesh);
bool regionValid;
auto memRegion = DqnMemStackTempRegionScoped(&memory->tempStack, &regionValid);
auto memRegion = DqnMemStackTempRegionGuard(&memory->tempStack, &regionValid);
if (regionValid)
{
DTRBitmap test = {};
@ -949,7 +949,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
{
bool regionValid;
auto tempMemRegion = DqnMemStackTempRegionScoped(&memory->tempStack, &regionValid);
auto tempMemRegion = DqnMemStackTempRegionGuard(&memory->tempStack, &regionValid);
if (regionValid)
{
size_t debugSize = DQN_MEGABYTE(1);

View File

@ -635,7 +635,7 @@ bool DTRAsset_LoadFontToBitmap(const PlatformAPI api, DqnMemStack *const memStac
bool result = false;
bool regionValid;
auto tmpMemRegion = DqnMemStackTempRegionScoped(tmpMemStack, &regionValid);
auto tmpMemRegion = DqnMemStackTempRegionGuard(tmpMemStack, &regionValid);
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);

View File

@ -78,7 +78,7 @@ void DTRDebug_DumpZBuffer(DTRRenderBuffer *const renderBuffer, DqnMemStack *cons
}
bool regionValid;
auto memRegion = DqnMemStackTempRegionScoped(tempStack, &regionValid);
auto memRegion = DqnMemStackTempRegionGuard(tempStack, &regionValid);
if (regionValid)
{
size_t bufSize = DQN_MEGABYTE(16);

View File

@ -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;
}

464
src/dqn.h
View File

@ -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 <typename T>
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 <typename T>
bool DqnArray_Free(DqnArray<T> *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 <typename T>
bool DqnArray_Init(DqnArray<T> *array, size_t capacity,
DqnMemAPI memAPI = DqnMemAPI_DefaultUseCalloc())
bool DqnArray_Init(DqnArray<T> *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<T> *array, size_t capacity,
return true;
}
// Implementation taken from Milton, developed by Serge at
// https://github.com/serge-rgb/milton#license
template <typename T>
bool DqnArray_Grow(DqnArray<T> *array)
bool DqnArray_Free(DqnArray<T> *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 <typename T>
bool DqnArray_Grow(DqnArray<T> *const array)
{
if (!array || !array->data) return false;
@ -518,7 +551,7 @@ bool DqnArray_Grow(DqnArray<T> *array)
}
template <typename T>
T *DqnArray_Push(DqnArray<T> *array, T item)
T *DqnArray_Push(DqnArray<T> *const array, const T item)
{
if (!array) return NULL;
@ -544,7 +577,7 @@ void DqnArray_Pop(DqnArray<T> *array)
}
template <typename T>
T *DqnArray_Get(DqnArray<T> *array, u64 index)
T *DqnArray_Get(DqnArray<T> *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<T> *array, u64 index)
}
template <typename T>
bool DqnArray_Clear(DqnArray<T> *array)
bool DqnArray_Clear(DqnArray<T> *const array)
{
if (array)
{
@ -564,7 +597,7 @@ bool DqnArray_Clear(DqnArray<T> *array)
}
template <typename T>
bool DqnArray_Remove(DqnArray<T> *array, u64 index)
bool DqnArray_Remove(DqnArray<T> *const array, const u64 index)
{
if (!array) return false;
if (index >= array->count) return false;
@ -583,7 +616,7 @@ bool DqnArray_Remove(DqnArray<T> *array, u64 index)
}
template <typename T>
bool DqnArray_RemoveStable(DqnArray<T> *array, u64 index)
bool DqnArray_RemoveStable(DqnArray<T> *const array, const u64 index)
{
if (!array) return false;
if (index >= array->count) return false;
@ -609,6 +642,17 @@ bool DqnArray_RemoveStable(DqnArray<T> *array, u64 index)
array->count--;
return true;
}
template <typename T> void DqnArray<T>::Init (const size_t capacity, DqnMemAPI memAPI) { DqnArray_Init(this, capacity, memAPI); }
template <typename T> bool DqnArray<T>::Free () { return DqnArray_Free(this); }
template <typename T> bool DqnArray<T>::Grow () { return DqnArray_Grow(this);}
template <typename T> T* DqnArray<T>::Push (const T item) { return DqnArray_Push(this, item); }
template <typename T> void DqnArray<T>::Pop () { DqnArray_Pop(this); }
template <typename T> T* DqnArray<T>::Get (const u64 index) { return DqnArray_Get(this, index); }
template <typename T> bool DqnArray<T>::Clear() { return DqnArray_Clear (this); }
template <typename T> bool DqnArray<T>::Remove (const u64 index) { return DqnArray_Remove(this, index); }
template <typename T> bool DqnArray<T>::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 <utility>
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 0xd8000xdfff (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 <dirent.h> // 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
////////////////////////////////////////////////////////////////////////////////