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); DTRDebug_TestMeshFaceAndVertexParser(&state->mesh);
bool regionValid; bool regionValid;
auto memRegion = DqnMemStackTempRegionScoped(&memory->tempStack, &regionValid); auto memRegion = DqnMemStackTempRegionGuard(&memory->tempStack, &regionValid);
if (regionValid) if (regionValid)
{ {
DTRBitmap test = {}; DTRBitmap test = {};
@ -949,7 +949,7 @@ extern "C" void DTR_Update(PlatformRenderBuffer *const platformRenderBuffer,
{ {
bool regionValid; bool regionValid;
auto tempMemRegion = DqnMemStackTempRegionScoped(&memory->tempStack, &regionValid); auto tempMemRegion = DqnMemStackTempRegionGuard(&memory->tempStack, &regionValid);
if (regionValid) if (regionValid)
{ {
size_t debugSize = DQN_MEGABYTE(1); 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 result = false;
bool regionValid; bool regionValid;
auto tmpMemRegion = DqnMemStackTempRegionScoped(tmpMemStack, &regionValid); auto tmpMemRegion = DqnMemStackTempRegionGuard(tmpMemStack, &regionValid);
if (!regionValid) if (!regionValid)
{ {
// TODO(doyle): Logging // 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)) if (!api.FileOpen(path, &file, PlatformFilePermissionFlag_Read, PlatformFileAction_OpenOnly))
return result; return result;
DqnMemStackTempRegionScoped tmpMemRegion = tempStack->TempRegionScoped(); DqnMemStackTempRegionGuard tmpMemRegion = tempStack->TempRegionGuard();
u8 *const rawData = (u8 *)DqnMemStack_Push(tempStack, file.size); u8 *const rawData = (u8 *)DqnMemStack_Push(tempStack, file.size);
size_t bytesRead = api.FileRead(&file, rawData, 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; bool regionValid;
auto memRegion = DqnMemStackTempRegionScoped(tempStack, &regionValid); auto memRegion = DqnMemStackTempRegionGuard(tempStack, &regionValid);
if (regionValid) if (regionValid)
{ {
size_t bufSize = DQN_MEGABYTE(16); 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; if (!file || !buf) return 0;
DqnFile dqnFile = PlatformFileToDqnFileInternal(*file); DqnFile dqnFile = PlatformFileToDqnFileInternal(*file);
size_t numBytesRead = DqnFile_Read(dqnFile, buf, bytesToRead); size_t numBytesRead = DqnFile_Read(&dqnFile, buf, bytesToRead);
return numBytesRead; return numBytesRead;
} }

464
src/dqn.h
View File

@ -12,6 +12,13 @@
#include "dqn.h" #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 // Table Of Contents #TOC #TableOfContents
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -53,7 +60,7 @@
// #DqnSprintf Cross-platform Sprintf Implementation (Public Domain lib stb_sprintf) // #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 // This needs to be above the portable layer so that, if the user requests
// a platform implementation, platform specific implementations in the portable // 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_Alloc (const size_t size);
DQN_FILE_SCOPE void *DqnMem_Calloc (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_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); DQN_FILE_SCOPE void DqnMem_Free (void *memory);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -209,9 +216,14 @@ enum DqnMemStackFlag
typedef struct DqnMemStack typedef struct DqnMemStack
{ {
// The memory block allocated for the stack
struct DqnMemStackBlock *block; struct DqnMemStackBlock *block;
// Bits set from enum DqnMemStackFlag
u32 flags; u32 flags;
i32 tempRegionCount; i32 tempRegionCount;
// Allocations are address aligned to this value. Not to be modified as popping allocations uses this to realign size
u32 byteAlign; u32 byteAlign;
#if defined(DQN_CPP_MODE) #if defined(DQN_CPP_MODE)
@ -233,7 +245,7 @@ typedef struct DqnMemStack
void TempRegionEnd (DqnMemStackTempRegion region); void TempRegionEnd (DqnMemStackTempRegion region);
// Scoped Temporary Regions API // Scoped Temporary Regions API
struct DqnMemStackTempRegionScoped TempRegionScoped(); struct DqnMemStackTempRegionGuard TempRegionGuard();
// Advanced API // Advanced API
DqnMemStackBlock *AllocateCompatibleBlock(size_t size); 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 // 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 // not have enough space for allocation it will automatically attach another MemBlock using
// DqnMem_Alloc(). // 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 // 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. // Allocate memory from the MemStack.
// size: "size" gets aligned to the byte alignment of the stack. // 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. // 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. // 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. // 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. // Frees the specified block belonging to the stack.
// return: FALSE if block doesn't belong this into calls DqnMem_Free() or invalid args. // 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. // Frees the last-most memory block. If last block, free that block making the MemStack blockless.
// Next allocate will attach a block. // 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. // Reset the current memory block usage to 0.
DQN_FILE_SCOPE void DqnMemStack_ClearCurrBlock(DqnMemStack *const stack, const bool zeroClear); 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 // regions
typedef struct DqnMemStackTempRegion 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; struct DqnMemStackBlock *startingBlock;
size_t used; size_t used;
} DqnMemStackTempRegion; } DqnMemStackTempRegion;
// region: Takes pointer to a zero-cleared DqnMemStackTempRegion struct. // region: Takes pointer to a zero-cleared DqnMemStackTempRegion struct.
// return: FALSE if arguments are invalid. // 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); 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 #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); // lock: Takes a pointer to a pre-existing and already initialised stack
~DqnMemStackTempRegionScoped(); // 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: private:
DqnMemStackTempRegion tempMemStack; DqnMemStackTempRegion tempMemStack;
@ -330,10 +348,12 @@ private:
// NOT be modified directly, only indirectly through the regular API. // NOT be modified directly, only indirectly through the regular API.
typedef struct DqnMemStackBlock typedef struct DqnMemStackBlock
{ {
// The raw memory block, size and used count
u8 *memory; u8 *memory;
size_t size; size_t size;
size_t used; size_t used;
// The allocator uses a linked list approach for additional blocks beyond capacity
DqnMemStackBlock *prevBlock; DqnMemStackBlock *prevBlock;
} DqnMemStackBlock; } DqnMemStackBlock;
@ -404,7 +424,7 @@ typedef struct DqnMemAPICallbackResult
enum DqnMemAPICallbackType type; enum DqnMemAPICallbackType type;
} DqnMemAPICallbackResult; } 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. // result: Is always guaranteed to be a valid pointer.
typedef void DqnMemAPI_Callback(DqnMemAPICallbackInfo info, DqnMemAPICallbackResult *result); typedef void DqnMemAPI_Callback(DqnMemAPICallbackInfo info, DqnMemAPICallbackResult *result);
@ -424,41 +444,32 @@ DQN_FILE_SCOPE DqnMemAPI DqnMemAPI_DefaultUseCalloc();
template <typename T> template <typename T>
struct DqnArray struct DqnArray
{ {
// Function pointers to custom allocators
DqnMemAPI memAPI; DqnMemAPI memAPI;
// Array state
u64 count; u64 count;
u64 capacity; 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 = FILE_SCOPE const char *const DQN_MEM_API_CALLBACK_RESULT_TYPE_INCORRECT =
"DqnMemAPICallbackResult type is incorrect"; "DqnMemAPICallbackResult type is incorrect";
// Implementation taken from Milton, developed by Serge at
// https://github.com/serge-rgb/milton#license
template <typename T> template <typename T>
bool DqnArray_Free(DqnArray<T> *array) bool DqnArray_Init(DqnArray<T> *const array, const size_t capacity,
{ const DqnMemAPI memAPI = DqnMemAPI_DefaultUseCalloc())
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())
{ {
if (!array) return false; if (!array) return false;
if (array->data) DqnArray_Free(array); if (array->data) DqnArray_Free(array);
@ -482,8 +493,30 @@ bool DqnArray_Init(DqnArray<T> *array, size_t capacity,
return true; return true;
} }
// Implementation taken from Milton, developed by Serge at
// https://github.com/serge-rgb/milton#license
template <typename T> 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; if (!array || !array->data) return false;
@ -518,7 +551,7 @@ bool DqnArray_Grow(DqnArray<T> *array)
} }
template <typename T> 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; if (!array) return NULL;
@ -544,7 +577,7 @@ void DqnArray_Pop(DqnArray<T> *array)
} }
template <typename T> template <typename T>
T *DqnArray_Get(DqnArray<T> *array, u64 index) T *DqnArray_Get(DqnArray<T> *const array, const u64 index)
{ {
T *result = NULL; T *result = NULL;
if (index >= 0 && index <= array->count) result = &array->data[index]; 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> template <typename T>
bool DqnArray_Clear(DqnArray<T> *array) bool DqnArray_Clear(DqnArray<T> *const array)
{ {
if (array) if (array)
{ {
@ -564,7 +597,7 @@ bool DqnArray_Clear(DqnArray<T> *array)
} }
template <typename T> 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 (!array) return false;
if (index >= array->count) return false; if (index >= array->count) return false;
@ -583,7 +616,7 @@ bool DqnArray_Remove(DqnArray<T> *array, u64 index)
} }
template <typename T> 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 (!array) return false;
if (index >= array->count) return false; if (index >= array->count) return false;
@ -609,6 +642,17 @@ bool DqnArray_RemoveStable(DqnArray<T> *array, u64 index)
array->count--; array->count--;
return true; 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 #endif // DQN_CPP_MODE
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -857,18 +901,18 @@ DQN_FILE_SCOPE bool DqnChar_IsAlphanum(char c);
// #DqnStr Public API - Str Operations // #DqnStr Public API - Str Operations
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// return: 0 if equal. 0 < if a is before b, > 0 if a is after b // 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. // 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. // 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) // 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 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); 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 #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 // 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. // 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); DQN_FILE_SCOPE i64 Dqn_StrToI64(const char *const buf, const i32 bufSize);
// WARNING: Not robust, precision errors and whatnot but good enough! // WARNING: Not robust, precision errors and whatnot but good enough!
DQN_FILE_SCOPE f32 Dqn_StrToF32(const char *const buf, const i32 bufSize); 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 // 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_UCSToUTF8(u32 *const dest, const u32 character);
DQN_FILE_SCOPE u32 Dqn_UTF8ToUCS(u32 *dest, u32 character); DQN_FILE_SCOPE u32 Dqn_UTF8ToUCS(u32 *const dest, const u32 character);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// #DqnWChar Public API - WChar Operations // #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 bool DqnWChar_IsDigit(const wchar_t c);
DQN_FILE_SCOPE wchar_t DqnWChar_ToLower(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_Len(const wchar_t *const a);
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);
DQN_FILE_SCOPE bool Dqn_WStrReverse(wchar_t *buf, const i32 bufSize); 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); DQN_FILE_SCOPE i32 Dqn_WStrToI32(const wchar_t *const buf, const i32 bufSize);
@ -954,10 +998,23 @@ enum DqnFileAction
typedef struct DqnFile typedef struct DqnFile
{ {
u32 permissionFlags; u32 permissionFlags;
void *handle; void *handle;
size_t size; 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; } DqnFile;
// NOTE: W(ide) versions of functions only work on Win32, since Unix is UTF-8 compatible. // 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); 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. // 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 size_t DqnFile_Read (const DqnFile *const file, u8 *const buffer, const size_t numBytesToRead);
DQN_FILE_SCOPE void DqnFile_Close(DqnFile *const file);
// 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. // 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); DQN_FILE_SCOPE bool DqnFile_Delete (const char *const path);
@ -1015,8 +1074,31 @@ DQN_FILE_SCOPE f64 DqnTimer_NowInS ();
typedef struct DqnLock typedef struct DqnLock
{ {
CRITICAL_SECTION win32Handle; CRITICAL_SECTION win32Handle;
#if defined(DQN_CPP_MODE)
bool Init(const u32 spinCount = 16000);
void Acquire();
void Release();
void Delete();
#endif
} DqnLock; } 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 bool DqnLock_Init (DqnLock *const lock, const u32 spinCount = 16000);
DQN_FILE_SCOPE void DqnLock_Acquire(DqnLock *const lock); DQN_FILE_SCOPE void DqnLock_Acquire(DqnLock *const lock);
DQN_FILE_SCOPE void DqnLock_Release(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 // 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 // DqnJobQueue_AllJobsComplete(). Alternatively you can combine both for the main thread to help
// complete work and not move on until all tasks are complete. // complete work and not move on until all tasks are complete.
typedef struct DqnJobQueue DqnJobQueue; typedef struct DqnJobQueue DqnJobQueue;
typedef void DqnJob_Callback(DqnJobQueue *const queue, void *const userData); typedef void DqnJob_Callback(DqnJobQueue *const queue, void *const userData);
@ -1054,20 +1137,54 @@ typedef struct DqnJob
void *userData; void *userData;
} DqnJob; } DqnJob;
// memSize: The size of the supplied memory. If "mem" is NULL OR "memsize" is NULL/0 OR "queueSize" typedef struct DqnJobQueue
// is 0, the function puts required size it needs into "memSize". {
// return: The JobQueue or NULL if args invalid. // JobList Circular Array, is setup in Init()
DQN_FILE_SCOPE DqnJobQueue *DqnJobQueue_InitWithMem(const void *const mem, size_t *const memSize, const u32 queueSize, const u32 numThreads); 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. // 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); 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 // 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 // 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 // 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 // are any remaining jobs left. That can only be definitively known using
// DqnJobQueue_AllJobsComplete(). This is typically combined like so .. // 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_TryExecuteNextJob(DqnJobQueue *const queue);
DQN_FILE_SCOPE bool DqnJobQueue_AllJobsComplete (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); 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); void *result = realloc(memory, newSize);
return result; return result;
@ -1714,10 +1831,9 @@ DqnMemStackInternal_AllocateBlock(u32 byteAlign, size_t size)
// #DqnMemStack CPP Implementation // #DqnMemStack CPP Implementation
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#if defined(DQN_CPP_MODE) #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, byteAlignment); }
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, byteAlignment); }
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, byteAlignment); }
bool DqnMemStack::Init (const size_t size, const bool zeroClear, const u32 byteAlignment) { return DqnMemStack_Init (this, size, zeroClear, byteAlign); }
void *DqnMemStack::Push(size_t size) { return DqnMemStack_Push(this, size); } 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); } 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); } 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. // 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); } DqnMemStackBlock *DqnMemStack::AllocateCompatibleBlock(size_t size) { return DqnMemStack_AllocateCompatibleBlock(this, size); }
bool DqnMemStack::AttachBlock (DqnMemStackBlock *const newBlock) { return DqnMemStack_AttachBlock (this, newBlock); } 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 // #DqnMemStackTempRegion Implementation
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
DQN_FILE_SCOPE bool DqnMemStackTempRegion_Begin(DqnMemStackTempRegion *region, DQN_FILE_SCOPE bool DqnMemStackTempRegion_Begin(DqnMemStackTempRegion *const region,
DqnMemStack *const stack) DqnMemStack *const stack)
{ {
if (!region || !stack) return false; if (!region || !stack) return false;
@ -1977,13 +2093,20 @@ DQN_FILE_SCOPE void DqnMemStackTempRegion_End(DqnMemStackTempRegion region)
} }
#ifdef DQN_CPP_MODE #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 (stack)
if (succeeded) *succeeded = result; {
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); DqnMemStackTempRegion_End(this->tempMemStack);
} }
@ -3026,23 +3149,26 @@ DQN_FILE_SCOPE bool DqnChar_IsAlphaNum(char c)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// #DqnStr Implementation // #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 && !b) return -1;
if (!a) return -1; if (!a) return -1;
if (!b) return -1; if (!b) return -1;
while ((*a) == (*b)) char const *aPtr = a;
char const *bPtr = b;
while ((*aPtr) == (*bPtr))
{ {
if (!(*a)) return 0; if (!(*aPtr)) return 0;
a++; aPtr++;
b++; 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; i32 result = 0;
while (a && a[result]) result++; while (a && a[result]) result++;
@ -3057,7 +3183,7 @@ DQN_FILE_SCOPE i32 DqnStr_LenDelimitWith(const char *a, const char delimiter)
return result; 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 (!dest) return NULL;
if (!src) 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; 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; if (!buf) return false;
i32 mid = bufSize / 2; i32 mid = bufSize / 2;
@ -3133,7 +3259,7 @@ DQN_FILE_SCOPE bool DqnStr_HasSubstring(const char *const src, const i32 srcLen,
return true; 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; bool validBuffer = true;
if (!buf || bufSize == 0) validBuffer = false; 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 The UCS code values 0xd8000xdfff (UTF-16 surrogates) as well as 0xfffe and
0xffff (UCS noncharacters) should not appear in conforming UTF-8 streams. 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; if (!dest) return 0;
@ -3422,7 +3548,7 @@ DQN_FILE_SCOPE u32 Dqn_UCSToUTF8(u32 *dest, u32 character)
return 0; 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; if (!dest) return 0;
@ -3518,27 +3644,30 @@ DQN_FILE_SCOPE wchar_t DqnWChar_ToLower(const wchar_t c)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// #DqnWStr Implementation // #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; i32 result = 0;
while (a && a[result]) result++; while (a && a[result]) result++;
return 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 && !b) return -1;
if (!a) return -1; if (!a) return -1;
if (!b) 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; if (!(*aPtr)) return 0;
a++; aPtr++;
b++; bPtr++;
} }
return (((*a) < (*b)) ? -1 : 1); return (((*aPtr) < (*bPtr)) ? -1 : 1);
} }
DQN_FILE_SCOPE bool Dqn_WStrReverse(wchar_t *buf, const i32 bufSize) 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() #include <dirent.h> // readdir()/opendir()/closedir()
#endif #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 // XPlatform > #DqnFileInternal Implementation
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -5788,16 +5947,16 @@ DQN_FILE_SCOPE size_t DqnFile_Write(const DqnFile *const file,
return numBytesWritten; 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) const size_t numBytesToRead)
{ {
size_t numBytesRead = 0; size_t numBytesRead = 0;
if (file.handle && buffer) if (file && file->handle && buffer)
{ {
#if defined(DQN_WIN32_PLATFORM) #if defined(DQN_WIN32_PLATFORM)
DWORD bytesToRead = (DWORD)numBytesToRead; DWORD bytesToRead = (DWORD)numBytesToRead;
DWORD bytesRead = 0; DWORD bytesRead = 0;
HANDLE win32Handle = file.handle; HANDLE win32Handle = file->handle;
BOOL result = ReadFile(win32Handle, (void *)buffer, bytesToRead, BOOL result = ReadFile(win32Handle, (void *)buffer, bytesToRead,
&bytesRead, NULL); &bytesRead, NULL);
@ -5812,10 +5971,10 @@ DQN_FILE_SCOPE size_t DqnFile_Read(DqnFile file, u8 *const buffer,
#elif defined(DQN_UNIX_PLATFORM) #elif defined(DQN_UNIX_PLATFORM)
// TODO(doyle): Syscall version // TODO(doyle): Syscall version
const size_t ITEMS_TO_READ = 1; 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) ITEMS_TO_READ)
{ {
rewind((FILE *)file.handle); rewind((FILE *)file->handle);
numBytesRead = ITEMS_TO_READ * numBytesToRead; numBytesRead = ITEMS_TO_READ * numBytesToRead;
} }
else else
@ -5990,6 +6149,35 @@ void DqnLock_Delete(DqnLock *const lock)
#endif #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 // Win32Platform > #DqnAtomic Implementation
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -6030,23 +6218,6 @@ DQN_FILE_SCOPE u32 DqnAtomic_Sub32(u32 volatile *src)
#endif #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 // Win32Platform > #DqnJobQueueInternal Implementation
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -6095,35 +6266,13 @@ FILE_SCOPE u32 DqnJobQueueInternal_ThreadCallback(void *threadParam)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Win32Platform > #DqnJobQueue Implementation // Win32Platform > #DqnJobQueue Implementation
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
DQN_FILE_SCOPE DqnJobQueue *DqnJobQueue_InitWithMem(const void *const mem, size_t *const memSize, DQN_FILE_SCOPE bool DqnJobQueue_Init(DqnJobQueue *const queue, DqnJob *const jobList,
const u32 queueSize, const u32 numThreads) const u32 jobListSize, const u32 numThreads)
{ {
DqnJobQueue emptyQueue = {}; if (!queue || !jobList || jobListSize == 0 || numThreads == 0) return false;
size_t reqStructSize = sizeof(emptyQueue); queue->jobList = jobList;
size_t reqQueueSize = sizeof(*emptyQueue.jobList) * queueSize; 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 #ifdef DQN_WIN32_PLATFORM
queue->semaphore = (void *)CreateSemaphore(NULL, 0, numThreads, NULL); queue->semaphore = (void *)CreateSemaphore(NULL, 0, numThreads, NULL);
DQN_ASSERT_HARD(queue->semaphore); 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, DQN_JOB_QUEUE_INTERNAL_THREAD_DEFAULT_STACK_SIZE, DqnJobQueueInternal_ThreadCallback,
(void *)queue, numThreads); (void *)queue, numThreads);
DQN_ASSERT_HARD(numThreads == numThreadsCreated); DQN_ASSERT_HARD(numThreads == numThreadsCreated);
return queue;
return true;
} }
DQN_FILE_SCOPE bool DqnJobQueue_AddJob(DqnJobQueue *const queue, const DqnJob job) 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; 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) DQN_FILE_SCOPE bool DqnJobQueue_TryExecuteNextJob(DqnJobQueue *const queue)
{ {
if (!queue) return false;
u32 originalJobToExecute = queue->jobToExecuteIndex; u32 originalJobToExecute = queue->jobToExecuteIndex;
if (originalJobToExecute != queue->jobInsertIndex) 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) DQN_FILE_SCOPE bool DqnJobQueue_AllJobsComplete(DqnJobQueue *const queue)
{ {
if (!queue) return false;
bool result = (queue->numJobsToComplete == 0); bool result = (queue->numJobsToComplete == 0);
return result; 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 // Win32Platform > #DqnWin32 Implementation
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////