diff --git a/dqn.h b/dqn.h index ca7b35f..2e99a4e 100644 --- a/dqn.h +++ b/dqn.h @@ -19,6 +19,16 @@ #define DQN_FILE_SCOPE #endif +#ifdef __cplusplus + #define DQN_CPP_MODE +#endif + +#if (defined(_WIN32) || defined(_WIN64)) && defined(DQN_WIN32_IMPLEMENTATION) + #define DQN_WIN32_PLATFORM + #define WIN32_LEAN_AND_MEAN + #include +#endif + #include // For standard types #include #define LOCAL_PERSIST static @@ -48,7 +58,6 @@ typedef float f32; #define DQN_INVALID_CODE_PATH 0 #define DQN_ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0])) -#define DQN_ASSERT(expr) if (!(expr)) { (*((i32 *)0)) = 0; } #define DQN_PI 3.14159265359f #define DQN_SQUARED(x) ((x) * (x)) @@ -59,27 +68,63 @@ typedef float f32; #define DQN_MAX(a, b) ((a) < (b) ? (b) : (a)) #define DQN_MIN(a, b) ((a) < (b) ? (a) : (b)) #define DQN_SWAP(type, a, b) do { type tmp = a; a = b; b = tmp; } while(0) + +//////////////////////////////////////////////////////////////////////////////// +// DqnAssert +//////////////////////////////////////////////////////////////////////////////// +// DQN_ASSERT() & DQN_ASSERT_MSG() will hard break the program but it can be +// disabled in DqnAssertInternal() for release whilst still allowing the assert +// expressions to be evaluated and instead send diagnostics to console. + +// NOTE: "## __VA_ARGS__" is a GCC hack. Zero variadic arguments won't compile +// because there will be a trailing ',' at the end. Pasting it swallows it. MSVC +// implicitly swallows the trailing comma. + +// Macro returns if the result was true or not. +#define DQN_ASSERT(expr) DqnAssertInternal(expr, __FILE__, __LINE__, #expr, NULL) +#define DQN_ASSERT_MSG(expr, msg, ...) DqnAssertInternal(expr, __FILE__, __LINE__, #expr, msg, ## __VA_ARGS__) + +// Usage example. This protects code against asserts that should fire in release +// mode by still letting the expression evaluate and the ability to redirect +// the code flow to some recovery path. +#if 0 + int *mem = (int *)malloc(sizeof(*mem)); + if (DQN_ASSERT_MSG(mem, "Not enough memory for malloc")) { + // success + } else { + // failed + } +#endif + +// Internal implementation should not be used as the macro above will handle it, +// but is required in header for visibility to external functions calling it. +DQN_FILE_SCOPE bool DqnAssertInternal(const bool result, const char *const file, const i32 lineNum, + const char *const expr, const char *const msg, ...); + +// Hard assert causes an immediate program break at point of assertion by trying +// to modify the 0th mem-address. +#define DQN_ASSERT_HARD(expr) if (!(expr)) { *((int *)0) = 0; } //////////////////////////////////////////////////////////////////////////////// // DqnMem - Memory //////////////////////////////////////////////////////////////////////////////// // TODO(doyle): Use platform allocation, fallback to malloc if platform not defined -DQN_FILE_SCOPE void *DqnMem_Alloc (size_t size); -DQN_FILE_SCOPE void *DqnMem_Calloc (size_t size); -DQN_FILE_SCOPE void DqnMem_Clear (void *memory, u8 clearValue, size_t size); -DQN_FILE_SCOPE void *DqnMem_Realloc(void *memory, size_t newSize); +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_Free (void *memory); //////////////////////////////////////////////////////////////////////////////// // DqnMemStack - Memory Stack, For push stack/ptr memory style management //////////////////////////////////////////////////////////////////////////////// -// DqnMemStack is a data structure to dynamically allocate memory in a stack -// like style. It pre-allocates a block of memory in init and sub-allocates from -// this block to take advantage of memory locality. +// DqnMemStack is an memory allocator in a stack like, push-pop style. It +// pre-allocates a block of memory at init and sub-allocates from this block to +// take advantage of memory locality. // When an allocation requires a larger amount of memory than available in the // block then the MemStack will allocate a new block of sufficient size for -// you in DqnMemStack_Push(..). This _DOES_ mean that there will be wasted -// space at the end of each block and is a tradeoff for memory locality against +// you in DqnMemStack_Push(..). This DOES mean that there will be wasted space +// at the end of each block and is a tradeoff for memory locality against // optimal space usage. // How To Use: @@ -102,26 +147,15 @@ enum DqnMemStackFlag DqnMemStackFlag_IsFixedMemoryFromUser = (1 << 1), // NOTE(doyle): Required to indicate we CAN'T free this memory when free is called. }; -//////////////////////////////////////////////////////////////////////////////// -// Advanced API Structs -//////////////////////////////////////////////////////////////////////////////// typedef struct DqnMemStack { struct DqnMemStackBlock *block; u32 flags; - i32 tempStackCount; + i32 tempRegionCount; u32 byteAlign; } DqnMemStack; -typedef struct DqnTempMemStack -{ - DqnMemStack *stack; - struct DqnMemStackBlock *startingBlock; - size_t used; - -} DqnTempMemStack; - DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedMem (DqnMemStack *const stack, u8 *const mem, const size_t memSize, const u32 byteAlign = 4); // Use preallocated memory, no further allocations, returns NULL on allocate if out of space DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedSize(DqnMemStack *const stack, size_t size, const bool zeroClear, const u32 byteAlign = 4); // Single allocation from platform, no further allocations, returns NULL of allocate if out of space DQN_FILE_SCOPE bool DqnMemStack_Init (DqnMemStack *const stack, size_t size, const bool zeroClear, const u32 byteAlign = 4); // Allocates from platform dynamically as space runs out @@ -133,13 +167,32 @@ DQN_FILE_SCOPE bool DqnMemStack_FreeStackBlock(DqnMemStack *const stack, DqnMem DQN_FILE_SCOPE bool DqnMemStack_FreeLastBlock (DqnMemStack *const stack); // Frees the last-most memory block. If last block, free that block, next allocate will attach a block. DQN_FILE_SCOPE void DqnMemStack_ClearCurrBlock(DqnMemStack *const stack, const bool zeroClear); // Reset the current memory block usage to 0. -// TempMemStack is only required for the function. Once BeginTempRegion() is called, subsequent allocation calls can be made using the original stack. +// TempMemRegion is only required for the function. Once BeginTempRegion() is called, subsequent allocation calls can be made using the original stack. // Upon EndTempRegion() the original stack will free any additional blocks it allocated during the temp region and revert to the original // state before BeginTempRegion() was called. // WARNING: Any calls to Free/Clear functions in a TempRegion will invalidate and trash the stack structure. // TODO(doyle): Look into a way of disallowing calls to free/clear in temp regions -DQN_FILE_SCOPE DqnTempMemStack DqnMemStack_BeginTempRegion(DqnMemStack *const stack); -DQN_FILE_SCOPE void DqnMemStack_EndTempRegion (DqnTempMemStack tempstack); +typedef struct DqnMemStackTempRegion +{ + DqnMemStack *stack; + struct DqnMemStackBlock *startingBlock; + size_t used; +} DqnMemStackTempRegion; + +DQN_FILE_SCOPE bool DqnMemStackTempRegion_Begin(DqnMemStackTempRegion *region, DqnMemStack *const stack); +DQN_FILE_SCOPE void DqnMemStackTempRegion_End (DqnMemStackTempRegion region); + +#ifdef DQN_CPP_MODE +struct DqnMemStackTempRegionScoped +{ + bool isInit; + DqnMemStackTempRegionScoped(DqnMemStack *const stack); + ~DqnMemStackTempRegionScoped(); + +private: + DqnMemStackTempRegion tempMemStack; +}; +#endif //////////////////////////////////////////////////////////////////////////////// // (OPTIONAL) DqnMemStack Advanced API @@ -193,8 +246,8 @@ enum DqnMemAPICallbackType typedef struct DqnMemAPICallbackInfo { - enum DqnMemAPICallbackType type; void *userContext; + enum DqnMemAPICallbackType type; union { struct { size_t requestSize; }; // DqnMemAPICallbackType_Alloc @@ -218,8 +271,8 @@ typedef struct DqnMemAPICallbackInfo typedef struct DqnMemAPICallbackResult { // NOTE: CallbackResult on free has nothing to fill out for result. - enum DqnMemAPICallbackType type; void *newMemPtr; + enum DqnMemAPICallbackType type; } DqnMemAPICallbackResult; typedef void DqnMemAPI_Callback(DqnMemAPICallbackInfo info, DqnMemAPICallbackResult *result); @@ -230,11 +283,13 @@ typedef struct DqnMemAPI } DqnMemAPI; DQN_FILE_SCOPE DqnMemAPI DqnMemAPI_DefaultUseCalloc(); + //////////////////////////////////////////////////////////////////////////////// // DArray - Dynamic Array //////////////////////////////////////////////////////////////////////////////// -// REMINDER: Dynamic Array can be used as a stack. Don't need to create one. -#if 1 +// Cplusplus mode only since it uses templates + +#ifdef DQN_CPP_MODE template struct DqnArray { @@ -258,6 +313,9 @@ bool DqnArray_Remove (DqnArray *array, u64 index); bool DqnArray_RemoveStable(DqnArray *array, u64 index); #endif +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 @@ -267,7 +325,7 @@ bool DqnArray_Free(DqnArray *array) { // TODO(doyle): Right now we assume free always works, and it probably should? size_t sizeToFree = (size_t)array->capacity * sizeof(T); - DqnMemAPICallbackInfo info = DqnMemAPICallback_InfoAskFreeInternal( + DqnMemAPICallbackInfo info = DqnMemAPIInternal_CallbackInfoAskFree( array->memAPI, array->data, sizeToFree); array->memAPI.callback(info, NULL); array->data = NULL; @@ -290,10 +348,13 @@ bool DqnArray_Init(DqnArray *array, size_t capacity, array->memAPI = memAPI; size_t allocateSize = (size_t)capacity * sizeof(T); - DqnMemAPICallbackResult memResult = {}; - DqnMemAPICallbackInfo info = DqnMemAPICallback_InfoAskAllocInternal(array->memAPI, allocateSize); + DqnMemAPICallbackResult memResult = {0}; + DqnMemAPICallbackInfo info = DqnMemAPIInternal_CallbackInfoAskAlloc(array->memAPI, allocateSize); array->memAPI.callback(info, &memResult); - DQN_ASSERT(memResult.type == DqnMemAPICallbackType_Alloc); + if (!DQN_ASSERT_MSG(memResult.type == DqnMemAPICallbackType_Alloc, DQN_MEM_API_CALLBACK_RESULT_TYPE_INCORRECT)) + { + return false; + } array->data = (T *)memResult.newMemPtr; if (!array->data) return false; @@ -315,12 +376,16 @@ bool DqnArray_Grow(DqnArray *array) size_t oldSize = (size_t)array->capacity * sizeof(T); size_t newSize = (size_t)newCapacity * sizeof(T); - DqnMemAPICallbackResult memResult = {}; - DqnMemAPICallbackInfo info = DqnMemAPICallback_InfoAskReallocInternal( + DqnMemAPICallbackResult memResult = {0}; + DqnMemAPICallbackInfo info = DqnMemAPIInternal_CallbackInfoAskRealloc( array->memAPI, array->data, oldSize, newSize); array->memAPI.callback(info, &memResult); - DQN_ASSERT(memResult.type == DqnMemAPICallbackType_Realloc); + if (!DQN_ASSERT_MSG(memResult.type == DqnMemAPICallbackType_Realloc, + DQN_MEM_API_CALLBACK_RESULT_TYPE_INCORRECT)) + { + return false; + } if (memResult.newMemPtr) { @@ -426,7 +491,7 @@ bool DqnArray_RemoveStable(DqnArray *array, u64 index) array->count--; return true; } -#endif +#endif // DQN_CPP_MODE //////////////////////////////////////////////////////////////////////////////// // Math @@ -436,7 +501,7 @@ DQN_FILE_SCOPE f32 DqnMath_Sqrtf (f32 a); DQN_FILE_SCOPE f32 DqnMath_Clampf(f32 val, f32 min, f32 max); //////////////////////////////////////////////////////////////////////////////// -// Vec2 +// DqnV2 Vec2 //////////////////////////////////////////////////////////////////////////////// typedef union DqnV2 { struct { f32 x, y; }; @@ -453,9 +518,9 @@ typedef union DqnV2i { } DqnV2i; // DqnV2 -DQN_FILE_SCOPE DqnV2 DqnV2_2i (i32 x, i32 y); // Typecasts 2 integers to 2 floats DQN_FILE_SCOPE DqnV2 DqnV2_1f (f32 xy); DQN_FILE_SCOPE DqnV2 DqnV2_2f (f32 x, f32 y); +DQN_FILE_SCOPE DqnV2 DqnV2_2i (i32 x, i32 y); // Typecasts 2 integers to 2 floats DQN_FILE_SCOPE DqnV2 DqnV2_V2i(DqnV2i a); DQN_FILE_SCOPE DqnV2 DqnV2_Add (DqnV2 a, DqnV2 b); @@ -474,6 +539,7 @@ DQN_FILE_SCOPE DqnV2 DqnV2_Perpendicular(DqnV2 a); DQN_FILE_SCOPE DqnV2 DqnV2_ConstrainToRatio(DqnV2 dim, DqnV2 ratio); // Resize the dimension to fit the aspect ratio provided. Downscale only. +#ifdef DQN_CPP_MODE DQN_FILE_SCOPE inline DqnV2 operator- (DqnV2 a, DqnV2 b) { return DqnV2_Sub (a, b); } DQN_FILE_SCOPE inline DqnV2 operator+ (DqnV2 a, DqnV2 b) { return DqnV2_Add (a, b); } DQN_FILE_SCOPE inline DqnV2 operator* (DqnV2 a, DqnV2 b) { return DqnV2_Hadamard(a, b); } @@ -485,6 +551,7 @@ DQN_FILE_SCOPE inline DqnV2 &operator*=(DqnV2 &a, i32 b) { return (a = DqnV2_S DQN_FILE_SCOPE inline DqnV2 &operator-=(DqnV2 &a, DqnV2 b) { return (a = DqnV2_Sub (a, b)); } DQN_FILE_SCOPE inline DqnV2 &operator+=(DqnV2 &a, DqnV2 b) { return (a = DqnV2_Add (a, b)); } DQN_FILE_SCOPE inline bool operator==(DqnV2 a, DqnV2 b) { return DqnV2_Equals (a, b); } +#endif // DqnV2i DQN_FILE_SCOPE DqnV2i DqnV2i_2i(i32 x, i32 y); @@ -499,6 +566,7 @@ DQN_FILE_SCOPE DqnV2i DqnV2i_Hadamard(DqnV2i a, DqnV2i b); DQN_FILE_SCOPE f32 DqnV2i_Dot (DqnV2i a, DqnV2i b); DQN_FILE_SCOPE bool DqnV2i_Equals (DqnV2i a, DqnV2i b); +#ifdef DQN_CPP_MODE DQN_FILE_SCOPE inline DqnV2i operator- (DqnV2i a, DqnV2i b) { return DqnV2i_Sub (a, b); } DQN_FILE_SCOPE inline DqnV2i operator+ (DqnV2i a, DqnV2i b) { return DqnV2i_Add (a, b); } DQN_FILE_SCOPE inline DqnV2i operator* (DqnV2i a, DqnV2i b) { return DqnV2i_Hadamard(a, b); } @@ -508,6 +576,7 @@ DQN_FILE_SCOPE inline DqnV2i &operator*=(DqnV2i &a, DqnV2i b) { return (a = DqnV DQN_FILE_SCOPE inline DqnV2i &operator-=(DqnV2i &a, DqnV2i b) { return (a = DqnV2i_Sub (a, b)); } DQN_FILE_SCOPE inline DqnV2i &operator+=(DqnV2i &a, DqnV2i b) { return (a = DqnV2i_Add (a, b)); } DQN_FILE_SCOPE inline bool operator==(DqnV2i a, DqnV2i b) { return DqnV2i_Equals (a, b); } +#endif //////////////////////////////////////////////////////////////////////////////// // Vec3 @@ -546,6 +615,7 @@ DQN_FILE_SCOPE DqnV3 DqnV3_Normalise (DqnV3 a); DQN_FILE_SCOPE f32 DqnV3_Length (DqnV3 a, DqnV3 b); DQN_FILE_SCOPE f32 DqnV3_LengthSquared(DqnV3 a, DqnV3 b); +#ifdef DQN_CPP_MODE DQN_FILE_SCOPE inline DqnV3 operator- (DqnV3 a, DqnV3 b) { return DqnV3_Sub (a, b); } DQN_FILE_SCOPE inline DqnV3 operator+ (DqnV3 a, DqnV3 b) { return DqnV3_Add (a, b); } DQN_FILE_SCOPE inline DqnV3 operator+ (DqnV3 a, f32 b) { return DqnV3_Add (a, DqnV3_1f(b)); } @@ -559,6 +629,7 @@ DQN_FILE_SCOPE inline DqnV3 &operator*=(DqnV3 &a, i32 b) { return (a = DqnV3_S DQN_FILE_SCOPE inline DqnV3 &operator-=(DqnV3 &a, DqnV3 b) { return (a = DqnV3_Sub (a, b)); } DQN_FILE_SCOPE inline DqnV3 &operator+=(DqnV3 &a, DqnV3 b) { return (a = DqnV3_Add (a, b)); } DQN_FILE_SCOPE inline bool operator==(DqnV3 a, DqnV3 b) { return DqnV3_Equals (a, b); } +#endif // DqnV3i DQN_FILE_SCOPE DqnV3i DqnV3i_3i(i32 x, i32 y, i32 z); @@ -582,11 +653,10 @@ typedef union DqnV4 { DqnV2 v2[2]; } DqnV4; -// Create a vector using ints and typecast to floats -DQN_FILE_SCOPE DqnV4 DqnV4_4i(i32 x, i32 y, i32 z, f32 w); -DQN_FILE_SCOPE DqnV4 DqnV4_4f(f32 x, f32 y, f32 z, f32 w); -DQN_FILE_SCOPE DqnV4 DqnV4_V3(DqnV3 a, f32 w); DQN_FILE_SCOPE DqnV4 DqnV4_1f(f32 xyzw); +DQN_FILE_SCOPE DqnV4 DqnV4_4f(f32 x, f32 y, f32 z, f32 w); +DQN_FILE_SCOPE DqnV4 DqnV4_4i(i32 x, i32 y, i32 z, f32 w); // Create a vector using ints and typecast to floats +DQN_FILE_SCOPE DqnV4 DqnV4_V3(DqnV3 a, f32 w); DQN_FILE_SCOPE DqnV4 DqnV4_Add (DqnV4 a, DqnV4 b); DQN_FILE_SCOPE DqnV4 DqnV4_Sub (DqnV4 a, DqnV4 b); @@ -596,6 +666,7 @@ DQN_FILE_SCOPE DqnV4 DqnV4_Hadamard(DqnV4 a, DqnV4 b); DQN_FILE_SCOPE f32 DqnV4_Dot (DqnV4 a, DqnV4 b); DQN_FILE_SCOPE bool DqnV4_Equals (DqnV4 a, DqnV4 b); +#ifdef DQN_CPP_MODE DQN_FILE_SCOPE inline DqnV4 operator- (DqnV4 a, DqnV4 b) { return DqnV4_Sub (a, b); } DQN_FILE_SCOPE inline DqnV4 operator+ (DqnV4 a, DqnV4 b) { return DqnV4_Add (a, b); } DQN_FILE_SCOPE inline DqnV4 operator+ (DqnV4 a, f32 b) { return DqnV4_Add (a, DqnV4_1f(b)); } @@ -608,6 +679,7 @@ DQN_FILE_SCOPE inline DqnV4 &operator*=(DqnV4 &a, i32 b) { return (a = DqnV4_S DQN_FILE_SCOPE inline DqnV4 &operator-=(DqnV4 &a, DqnV4 b) { return (a = DqnV4_Sub (a, b)); } DQN_FILE_SCOPE inline DqnV4 &operator+=(DqnV4 &a, DqnV4 b) { return (a = DqnV4_Add (a, b)); } DQN_FILE_SCOPE inline bool operator==(DqnV4 a, DqnV4 b) { return DqnV4_Equals (a, b); } +#endif //////////////////////////////////////////////////////////////////////////////// // 4D Matrix Mat4 @@ -629,6 +701,7 @@ DQN_FILE_SCOPE DqnMat4 DqnMat4_LookAt (DqnV3 eye, DqnV3 center, DqnV3 up); DQN_FILE_SCOPE DqnMat4 DqnMat4_Translate (f32 x, f32 y, f32 z); DQN_FILE_SCOPE DqnMat4 DqnMat4_Rotate (f32 radians, f32 x, f32 y, f32 z); DQN_FILE_SCOPE DqnMat4 DqnMat4_Scale (f32 x, f32 y, f32 z); +DQN_FILE_SCOPE DqnMat4 DqnMat4_ScaleV3 (DqnV3 scale); DQN_FILE_SCOPE DqnMat4 DqnMat4_Mul (DqnMat4 a, DqnMat4 b); DQN_FILE_SCOPE DqnV4 DqnMat4_MulV4 (DqnMat4 a, DqnV4 b); @@ -644,6 +717,7 @@ typedef struct DqnRect DQN_FILE_SCOPE DqnRect DqnRect_4f (f32 minX, f32 minY, f32 maxX, f32 maxY); DQN_FILE_SCOPE DqnRect DqnRect_4i (i32 minX, i32 minY, i32 maxX, i32 maxY); DQN_FILE_SCOPE DqnRect DqnRect_Init (DqnV2 origin, DqnV2 size); + DQN_FILE_SCOPE void DqnRect_GetSize2f(DqnRect rect, f32 *width, f32 *height); DQN_FILE_SCOPE void DqnRect_GetSize2i(DqnRect rect, i32 *width, i32 *height); DQN_FILE_SCOPE DqnV2 DqnRect_GetSizeV2(DqnRect rect); @@ -661,24 +735,22 @@ DQN_FILE_SCOPE bool DqnChar_IsDigit (char c); DQN_FILE_SCOPE bool DqnChar_IsAlpha (char c); DQN_FILE_SCOPE bool DqnChar_IsAlphanum(char c); -DQN_FILE_SCOPE i32 Dqn_strcmp(const char *a, const char *b); +DQN_FILE_SCOPE i32 DqnStr_Cmp(const char *a, const char *b); // Returns the length without the null terminator -DQN_FILE_SCOPE i32 Dqn_strlen (const char *a); -DQN_FILE_SCOPE i32 Dqn_strlenDelimitWith(const char *a, const char delimiter); -DQN_FILE_SCOPE char *Dqn_strncpy (char *dest, const char *src, i32 numChars); +DQN_FILE_SCOPE i32 DqnStr_Len (const char *a); +DQN_FILE_SCOPE i32 DqnStr_LenDelimitWith(const char *a, const char delimiter); +DQN_FILE_SCOPE char *DqnStr_Copy (char *dest, const char *src, i32 numChars); -DQN_FILE_SCOPE bool Dqn_StrReverse (char *buf, const i32 bufSize); -DQN_FILE_SCOPE i32 Dqn_StrFindFirstOccurence(const char *const src, const i32 srcLen, const char *const find, const i32 findLen); -DQN_FILE_SCOPE bool Dqn_StrHasSubstring (const char *const src, const i32 srcLen, const char *const find, const i32 findLen); +DQN_FILE_SCOPE bool DqnStr_Reverse (char *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); #define DQN_32BIT_NUM_MAX_STR_SIZE 11 #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 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); @@ -692,12 +764,12 @@ 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 Dqn_wstrlen(const wchar_t *a); -DQN_FILE_SCOPE i32 Dqn_wstrcmp(const wchar_t *a, const wchar_t *b); +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 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_I32ToWStr(i32 value, wchar_t *buf, 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_I32ToWStr(i32 value, wchar_t *buf, i32 bufSize); //////////////////////////////////////////////////////////////////////////////// // File Operations @@ -776,27 +848,96 @@ DQN_FILE_SCOPE f32 DqnRnd_PCGNextf(DqnRandPCGState *pcg); // Returns a random integer N between [min, max] DQN_FILE_SCOPE i32 DqnRnd_PCGRange(DqnRandPCGState *pcg, i32 min, i32 max); +//////////////////////////////////////////////////////////////////////////////// +// DqnLock +//////////////////////////////////////////////////////////////////////////////// +typedef struct DqnLock +{ +#ifdef DQN_WIN32_PLATFORM + CRITICAL_SECTION win32Handle; +#else + void *handle; +#endif +} DqnLock; + +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); +DQN_FILE_SCOPE void DqnLock_Delete (DqnLock *const lock); + +//////////////////////////////////////////////////////////////////////////////// +// DqnAtomics +//////////////////////////////////////////////////////////////////////////////// +DQN_FILE_SCOPE u32 DqnAtomic_CompareSwap32(u32 volatile *dest, u32 swapVal, u32 compareVal); +DQN_FILE_SCOPE u32 DqnAtomic_Add32 (u32 volatile *src); +DQN_FILE_SCOPE u32 DqnAtomic_Sub32 (u32 volatile *src); + +//////////////////////////////////////////////////////////////////////////////// +// DqnJobQueue - Multithreaded Job Queue +//////////////////////////////////////////////////////////////////////////////// +// TODO(doyle): Only WIN32!! + +// DqnJobQueue is a platform abstracted "lockless" multithreaded work queue. It +// will create threads and assign threads to complete the job via the job +// 'callback' using the 'userData' supplied. + +// Usage +// 1. Prepare your callback function for threads to execute following the +// 'DqnJob_Callback' function signature. +// 2. Create a work queue with DqnJobQueue_InitWithMem() +// 3. Add jobs with DqnJobQueue_AddJob() and threads will be dispatched. +// +// When all jobs are sent you can also utilise the main executing thread to +// complete jobs whilst you wait for all jobs to complete using +// DqnJobQueue_TryExecuteNextJob(). +typedef struct DqnJobQueue DqnJobQueue; + +typedef void DqnJob_Callback(DqnJobQueue *const queue, void *const userData); +typedef struct DqnJob +{ + DqnJob_Callback *callback; + void *userData; +} DqnJob; + +// If 'mem' is null OR 'memsize' is NULL or 0 OR queueSize is 0, the function +// returns the required size in 'memSize' for initialisation. +DQN_FILE_SCOPE DqnJobQueue *DqnJobQueue_InitWithMem(const void *const mem, size_t *const memSize, const u32 queueSize, const u32 numThreads); + +// Returns false if the job is not able to be added, this can occur if the queue is full. +DQN_FILE_SCOPE bool DqnJobQueue_AddJob(DqnJobQueue *const queue, const DqnJob job); + +// Returns true if there was an available job for the thread to execute. It may +// return false whilst there are still jobs. This can occur if another thread +// has taken the work before you have. Returning false does NOT mean that there +// are no jobs left. That can only be known using DqnJobQueue_AllJobsComplete(). +DQN_FILE_SCOPE bool DqnJobQueue_TryExecuteNextJob(DqnJobQueue *const queue); +DQN_FILE_SCOPE bool DqnJobQueue_AllJobsComplete (DqnJobQueue *const queue); #endif /* DQN_H */ -#if (defined(_WIN32) || defined(_WIN64)) && defined(DQN_WIN32_IMPLEMENTATION) +#ifdef DQN_WIN32_PLATFORM //////////////////////////////////////////////////////////////////////////////// // Win32 Specific //////////////////////////////////////////////////////////////////////////////// -#define WIN32_LEAN_AND_MEAN -#include - #define DQN_WIN32_ERROR_BOX(text, title) MessageBoxA(NULL, text, title, MB_OK); // Out is a pointer to the buffer to receive the characters. // outLen is the length/capacity of the out buffer DQN_FILE_SCOPE bool DqnWin32_UTF8ToWChar (const char *const in, wchar_t *const out, const i32 outLen); DQN_FILE_SCOPE bool DqnWin32_WCharToUTF8 (const wchar_t *const in, char *const out, const i32 outLen); -DQN_FILE_SCOPE void DqnWin32_GetClientDim (const HWND window, LONG *width, LONG *height); -DQN_FILE_SCOPE void DqnWin32_GetRectDim (RECT rect, LONG *width, LONG *height); -DQN_FILE_SCOPE void DqnWin32_DisplayLastError(const char *const errorPrefix); -DQN_FILE_SCOPE void DqnWin32_DisplayErrorCode(const DWORD error, const char *const errorPrefix); -#endif /* DQN_WIN32_IMPLEMENTATION */ +DQN_FILE_SCOPE void DqnWin32_GetClientDim (const HWND window, LONG *width, LONG *height); +DQN_FILE_SCOPE void DqnWin32_GetRectDim (RECT rect, LONG *width, LONG *height); +DQN_FILE_SCOPE void DqnWin32_DisplayLastError (const char *const errorPrefix); +DQN_FILE_SCOPE void DqnWin32_DisplayErrorCode (const DWORD error, const char *const errorPrefix); +DQN_FILE_SCOPE void DqnWin32_OutputDebugString(const char *const formatStr, ...); +// buf: Filled with the path to the executable file. +// Returns the offset to the last backslash, -1 if bufLen was not large enough or buf is null. +DQN_FILE_SCOPE i32 DqnWin32_GetEXEDirectory(char *const buf, const u32 bufLen); + +// numCores: numThreadsPerCore: Can be NULL, the function will just skip it. +// Uses calloc and free for querying numCores. +DQN_FILE_SCOPE void DqnWin32_GetNumThreadsAndCores(i32 *const numCores, i32 *const numThreadsPerCore); +#endif /* DQN_WIN32_PLATFORM */ #ifndef DQN_INI_H #define DQN_INI_H @@ -1331,30 +1472,70 @@ STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char comma, char peri // NOTE: DQN_INI_IMPLEMENTATION modified to be included when DQN_IMPLEMENTATION defined // #define DQN_INI_IMPLEMENTATION -#define DQN_INI_STRLEN(s) Dqn_strlen(s) +#define DQN_INI_STRLEN(s) DqnStr_Len(s) + +//////////////////////////////////////////////////////////////////////////////// +// Dqn Error +//////////////////////////////////////////////////////////////////////////////// +#ifndef DQN_WIN32_PLATFORM +#include +#endif + +DQN_FILE_SCOPE bool DqnAssertInternal(const bool result, const char *const file, const i32 lineNum, + const char *const expr, const char *const msg, ...) +{ + if (!(result)) + { + const char *const formatStrNoMsg = "DqnAssert() failed: %s|%d| (%s)\n"; + const char *const formatStrWithMsg = "DqnAssert() failed: %s|%d| (%s): %s\n"; + const char *const formatStr = (msg) ? formatStrWithMsg : formatStrNoMsg; + + char userMsg[512] = {}; + if (msg) + { + va_list argList; + va_start(argList, msg); + { + i32 numCopied = Dqn_vsprintf(userMsg, msg, argList); + DQN_ASSERT_HARD(numCopied < DQN_ARRAY_COUNT(userMsg)); + } + va_end(argList); + } + +#ifdef DQN_WIN32_PLATFORM + DqnWin32_OutputDebugString(formatStr, file, lineNum, expr, userMsg); +#else + printf(formatStr, file, lineNum, expr, userMsg); +#endif + + (*((i32 *)0)) = 0; + } + return result; +} + //////////////////////////////////////////////////////////////////////////////// // DqnMemory - Default Memory Routines //////////////////////////////////////////////////////////////////////////////// // NOTE: All memory allocations in dqn.h go through these functions. So they can // be rerouted fairly easily especially for platform specific mallocs. -DQN_FILE_SCOPE void *DqnMem_Alloc(size_t size) +DQN_FILE_SCOPE void *DqnMem_Alloc(const size_t size) { void *result = malloc(size); return result; } -DQN_FILE_SCOPE void *DqnMem_Calloc(size_t size) +DQN_FILE_SCOPE void *DqnMem_Calloc(const size_t size) { void *result = calloc(1, size); return result; } -DQN_FILE_SCOPE void DqnMem_Clear(void *memory, u8 clearValue, size_t size) +DQN_FILE_SCOPE void DqnMem_Clear(void *const memory, const u8 clearValue, const size_t size) { if (memory) memset(memory, clearValue, size); } -DQN_FILE_SCOPE void *DqnMem_Realloc(void *memory, size_t newSize) +DQN_FILE_SCOPE void *DqnMem_Realloc(void *const memory, const size_t newSize) { void *result = realloc(memory, newSize); return result; @@ -1370,10 +1551,10 @@ DQN_FILE_SCOPE void DqnMem_Free(void *memory) } //////////////////////////////////////////////////////////////////////////////// -// DqnMemStack - Memory API, For using custom allocators +// DqnMemStackInternal Implementation //////////////////////////////////////////////////////////////////////////////// DQN_FILE_SCOPE DqnMemStackBlock * -DqnMemStack_AllocateBlockInternal(u32 byteAlign, size_t size) +DqnMemStackInternal_AllocateBlock(u32 byteAlign, size_t size) { size_t alignedSize = DQN_ALIGN_POW_N(size, byteAlign); size_t totalSize = alignedSize + sizeof(DqnMemStackBlock) + (byteAlign -1); @@ -1389,6 +1570,252 @@ DqnMemStack_AllocateBlockInternal(u32 byteAlign, size_t size) return result; } +//////////////////////////////////////////////////////////////////////////////// +// DqnMemStack_Init* Implementation +//////////////////////////////////////////////////////////////////////////////// +DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedMem(DqnMemStack *const stack, u8 *const mem, + const size_t memSize, const u32 byteAlign) +{ + if (!stack || !mem) return false; + + if (!DQN_ASSERT_MSG( + memSize > sizeof(DqnMemStackBlock), + "memSize is insufficient to initialise a memstack, memSize: %d, requiredSize: %d", + memSize, sizeof(DqnMemStackBlock))) + { + return false; + } + + stack->block = (DqnMemStackBlock *)mem; + stack->block->memory = mem + sizeof(DqnMemStackBlock); + stack->block->used = 0; + stack->block->size = memSize - sizeof(DqnMemStackBlock); + stack->block->prevBlock = NULL; + stack->flags = (DqnMemStackFlag_IsFixedMemoryFromUser | DqnMemStackFlag_IsNotExpandable); + + const u32 DEFAULT_ALIGNMENT = 4; + stack->tempRegionCount = 0; + stack->byteAlign = (byteAlign == 0) ? DEFAULT_ALIGNMENT : byteAlign; + return true; +} + +DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedSize(DqnMemStack *const stack, size_t size, + const bool zeroClear, const u32 byteAlign) +{ + bool result = DqnMemStack_Init(stack, size, byteAlign); + if (result) + { + stack->flags |= DqnMemStackFlag_IsNotExpandable; + return true; + } + + return false; +} + +DQN_FILE_SCOPE bool DqnMemStack_Init(DqnMemStack *const stack, size_t size, + const bool zeroClear, + const u32 byteAlign) +{ + if (!stack || size <= 0) return false; + if (!DQN_ASSERT_MSG(!stack->block, "MemStack has pre-existing block already attached")) + return false; + + stack->block = DqnMemStackInternal_AllocateBlock(byteAlign, size); + if (!DQN_ASSERT_MSG(stack->block, "MemStack failed to allocate block, not enough memory")) + return false; + + stack->tempRegionCount = 0; + stack->byteAlign = byteAlign; + stack->flags = 0; + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// DqnMemStack Push/Pop/Free Implementation +//////////////////////////////////////////////////////////////////////////////// +DQN_FILE_SCOPE void *DqnMemStack_Push(DqnMemStack *const stack, size_t size) +{ + if (!stack || size == 0) return NULL; + + size_t alignedSize = DQN_ALIGN_POW_N(size, stack->byteAlign); + if (!stack->block || + (stack->block->used + alignedSize) > stack->block->size) + { + size_t newBlockSize; + // TODO(doyle): Allocate block size based on the aligned size or + // a minimum block size? Not allocate based on the current block + // size + if (stack->block) newBlockSize = DQN_MAX(alignedSize, stack->block->size); + else newBlockSize = alignedSize; + + DqnMemStackBlock *newBlock = DqnMemStack_AllocateCompatibleBlock(stack, newBlockSize); + if (newBlock) + { + bool blockAttachResult = DqnMemStack_AttachBlock(stack, newBlock); + // IMPORTANT(doyle): This should be impossible, considering that + // AllocateCompatibleBlock checks the preconditions that the new + // block should be able to be attached. + + // But if we somehow reach this, we need to free the block + // otherwise memory is leaked. + DQN_ASSERT_HARD(blockAttachResult); + } + else + { + // TODO: Better notifying to user, out of space in stack OR stack + // is configured such that new blocks are not allowed. + return NULL; + } + } + + u8 *currPointer = stack->block->memory + stack->block->used; + u8 *alignedResult = (u8 *)DQN_ALIGN_POW_N(currPointer, stack->byteAlign); + size_t alignmentOffset = (size_t)(alignedResult - currPointer); + + // NOTE(doyle): Since all stack can't change alignment once they've been + // initialised and that the base memory ptr is already aligned, then all + // subsequent allocations should also be aligned automatically. + // TODO(doyle): In the future, do we want to allow arbitrary alignment PER + // allocation, not per MemStack? + DQN_ASSERT_HARD(alignmentOffset == 0); + + void *result = alignedResult; + stack->block->used += (alignedSize + alignmentOffset); + DQN_ASSERT_HARD(stack->block->used <= stack->block->size); + return result; +} + +DQN_FILE_SCOPE bool DqnMemStack_Pop(DqnMemStack *const stack, void *ptr, size_t size) +{ + if (!stack || !stack->block) return false; + + u8 *currPtr = stack->block->memory + stack->block->used; + if (DQN_ASSERT_MSG((u8 *)ptr >= stack->block->memory && ptr < currPtr, + "'ptr' to pop does not belong to current memStack attached block")) + { + size_t calcSize = (size_t)currPtr - (size_t)ptr; + if (DQN_ASSERT_MSG(calcSize == size, "'ptr' was not the last item allocated to memStack")) + { + stack->block->used -= size; + return true; + } + } + + return false; +} + +DQN_FILE_SCOPE void DqnMemStack_Free(DqnMemStack *stack) +{ + if (!stack) return; + + // NOTE(doyle): User is in charge of freeing this memory, so all we need to + // do is clear the block. + if (stack->flags & DqnMemStackFlag_IsFixedMemoryFromUser) + { + DQN_ASSERT_HARD(!stack->block->prevBlock); + DqnMemStack_ClearCurrBlock(stack, false); + return; + } + + while (stack->block) + DqnMemStack_FreeLastBlock(stack); + + // After a stack is free, we reset the not expandable flag so that if we + // allocate on an empty stack it still works. + stack->flags &= ~DqnMemStackFlag_IsNotExpandable; +} + +DQN_FILE_SCOPE bool DqnMemStack_FreeStackBlock(DqnMemStack *const stack, DqnMemStackBlock *block) +{ + if (!stack || !block || !stack->block) return false; + if (stack->flags & DqnMemStackFlag_IsFixedMemoryFromUser) return false; + + DqnMemStackBlock **blockPtr = &stack->block; + + while (*blockPtr && (*blockPtr) != block) + blockPtr = &((*blockPtr)->prevBlock); + + if (*blockPtr) + { + DqnMemStackBlock *blockToFree = *blockPtr; + (*blockPtr) = blockToFree->prevBlock; + DqnMem_Free(blockToFree); + + // No more blocks, then last block has been freed + if (!stack->block) DQN_ASSERT_HARD(stack->tempRegionCount == 0); + return true; + } + + return false; +} + +DQN_FILE_SCOPE bool DqnMemStack_FreeLastBlock(DqnMemStack *const stack) +{ + bool result = DqnMemStack_FreeStackBlock(stack, stack->block); + return result; +} + +DQN_FILE_SCOPE void DqnMemStack_ClearCurrBlock(DqnMemStack *const stack, const bool zeroClear) +{ + if (!stack) return; + if (stack->block) + { + stack->block->used = 0; + if (zeroClear) + { + DqnMem_Clear(stack->block->memory, 0, stack->block->size); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// DqnMemStackTempRegion Implementation +//////////////////////////////////////////////////////////////////////////////// +DQN_FILE_SCOPE bool DqnMemStackTempRegion_Begin(DqnMemStackTempRegion *region, + DqnMemStack *const stack) +{ + if (!region || !stack) return false; + + region->stack = stack; + region->startingBlock = stack->block; + region->used = stack->block->used; + + stack->tempRegionCount++; + return true; +} + +DQN_FILE_SCOPE void DqnMemStackTempRegion_End(DqnMemStackTempRegion region) +{ + DqnMemStack *stack = region.stack; + while (stack->block != region.startingBlock) + DqnMemStack_FreeLastBlock(stack); + + if (stack->block) + { + DQN_ASSERT_HARD(stack->block->used >= region.used); + stack->block->used = region.used; + } + + stack->tempRegionCount--; + DQN_ASSERT_HARD(stack->tempRegionCount >= 0); +} + +#ifdef DQN_CPP_MODE +DqnMemStackTempRegionScoped::DqnMemStackTempRegionScoped(DqnMemStack *const stack) +{ + this->isInit = DqnMemStackTempRegion_Begin(&this->tempMemStack, stack); + DQN_ASSERT(this->isInit); +} + +DqnMemStackTempRegionScoped::~DqnMemStackTempRegionScoped() +{ + DqnMemStackTempRegion_End(this->tempMemStack); +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DqnMemStack Advanced API Implementation +//////////////////////////////////////////////////////////////////////////////// DQN_FILE_SCOPE DqnMemStackBlock * DqnMemStack_AllocateCompatibleBlock(const DqnMemStack *const stack, size_t size) { @@ -1397,7 +1824,7 @@ DqnMemStack_AllocateCompatibleBlock(const DqnMemStack *const stack, size_t size) if (stack->flags & DqnMemStackFlag_IsNotExpandable) return NULL; DqnMemStackBlock *block = - DqnMemStack_AllocateBlockInternal(stack->byteAlign, size); + DqnMemStackInternal_AllocateBlock(stack->byteAlign, size); return block; } @@ -1437,241 +1864,22 @@ DQN_FILE_SCOPE bool DqnMemStack_DetachBlock(DqnMemStack *const stack, return true; } - DQN_FILE_SCOPE void DqnMemStack_FreeBlock(DqnMemStackBlock *block) { if (!block) return; DqnMem_Free(block); } -DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedMem(DqnMemStack *const stack, - u8 *const mem, - const size_t memSize, - const u32 byteAlign) -{ - if (!stack || !mem) return false; - - // TODO(doyle): Better logging - if (memSize < sizeof(DqnMemStackBlock)) - DQN_ASSERT(DQN_INVALID_CODE_PATH); - - stack->block = (DqnMemStackBlock *)mem; - stack->block->memory = mem + sizeof(DqnMemStackBlock); - stack->block->used = 0; - stack->block->size = memSize - sizeof(DqnMemStackBlock); - stack->block->prevBlock = NULL; - stack->flags = (DqnMemStackFlag_IsFixedMemoryFromUser | DqnMemStackFlag_IsNotExpandable); - - const u32 DEFAULT_ALIGNMENT = 4; - stack->tempStackCount = 0; - stack->byteAlign = (byteAlign == 0) ? DEFAULT_ALIGNMENT : byteAlign; - return true; -} - -DQN_FILE_SCOPE bool DqnMemStack_Init(DqnMemStack *const stack, size_t size, - const bool zeroClear, - const u32 byteAlign) -{ - if (!stack || size <= 0) return false; - DQN_ASSERT(!stack->block); - - stack->block = DqnMemStack_AllocateBlockInternal(byteAlign, size); - if (!stack->block) return false; - - stack->tempStackCount = 0; - stack->byteAlign = byteAlign; - stack->flags = 0; - return true; -} - -DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedSize(DqnMemStack *const stack, - size_t size, - const bool zeroClear, - const u32 byteAlign) -{ - bool result = DqnMemStack_Init(stack, size, byteAlign); - if (result) - { - stack->flags |= DqnMemStackFlag_IsNotExpandable; - return true; - } - - return false; -} - -DQN_FILE_SCOPE void *DqnMemStack_Push(DqnMemStack *const stack, size_t size) -{ - if (!stack || size == 0) return NULL; - - size_t alignedSize = DQN_ALIGN_POW_N(size, stack->byteAlign); - if (!stack->block || - (stack->block->used + alignedSize) > stack->block->size) - { - size_t newBlockSize; - // TODO(doyle): Allocate block size based on the aligned size or - // a minimum block size? Not allocate based on the current block - // size - if (stack->block) newBlockSize = DQN_MAX(alignedSize, stack->block->size); - else newBlockSize = alignedSize; - - DqnMemStackBlock *newBlock = DqnMemStack_AllocateCompatibleBlock(stack, newBlockSize); - if (newBlock) - { - if (!DqnMemStack_AttachBlock(stack, newBlock)) - { - // IMPORTANT(doyle): This should be impossible, considering that - // AllocateCompatibleBlock checks the preconditions that the new - // block should be able to be attached. - - // But if we somehow reach this, we need to free the block - // otherwise memory is leaked. - DQN_ASSERT(DQN_INVALID_CODE_PATH); - return NULL; - } - } - else - { - // TODO: Better notifying to user, out of space in stack OR stack - // is configured such that new blocks are not allowed. - return NULL; - } - } - - u8 *currPointer = stack->block->memory + stack->block->used; - u8 *alignedResult = (u8 *)DQN_ALIGN_POW_N(currPointer, stack->byteAlign); - size_t alignmentOffset = (size_t)(alignedResult - currPointer); - - // NOTE(doyle): Since all stack can't change alignment once they've been - // initialised and that the base memory ptr is already aligned, then all - // subsequent allocations should also be aligned automatically. - // TODO(doyle): In the future, do we want to allow arbitrary alignment PER - // allocation, not per MemStack? - DQN_ASSERT(alignmentOffset == 0); - - void *result = alignedResult; - stack->block->used += (alignedSize + alignmentOffset); - DQN_ASSERT(stack->block->used <= stack->block->size); - return result; -} - -DQN_FILE_SCOPE bool DqnMemStack_Pop(DqnMemStack *const stack, void *ptr, size_t size) -{ - if (!stack || !stack->block) return false; - - u8 *currPtr = stack->block->memory + stack->block->used; - DQN_ASSERT((u8 *)ptr >= stack->block->memory && ptr < currPtr); - - size_t calcSize = (size_t)currPtr - (size_t)ptr; - DQN_ASSERT(calcSize == size); - - stack->block->used -= size; - - return true; -} - -DQN_FILE_SCOPE bool DqnMemStack_FreeStackBlock(DqnMemStack *const stack, DqnMemStackBlock *block) -{ - if (!stack || !block || !stack->block) return false; - if (stack->flags & DqnMemStackFlag_IsFixedMemoryFromUser) return false; - - DqnMemStackBlock **blockPtr = &stack->block; - - while (*blockPtr && (*blockPtr) != block) - blockPtr = &((*blockPtr)->prevBlock); - - if (*blockPtr) - { - DqnMemStackBlock *blockToFree = *blockPtr; - (*blockPtr) = blockToFree->prevBlock; - DqnMem_Free(blockToFree); - - // No more blocks, then last block has been freed - if (!stack->block) DQN_ASSERT(stack->tempStackCount == 0); - return true; - } - - return false; -} - -DQN_FILE_SCOPE bool -DqnMemStack_FreeLastBlock(DqnMemStack *const stack) -{ - bool result = DqnMemStack_FreeStackBlock(stack, stack->block); - return result; -} - -DQN_FILE_SCOPE void DqnMemStack_Free(DqnMemStack *stack) -{ - if (!stack) return; - - // NOTE(doyle): User is in charge of freeing this memory, so all we need to - // do is clear the block. - if (stack->flags & DqnMemStackFlag_IsFixedMemoryFromUser) - { - DQN_ASSERT(!stack->block->prevBlock); - DqnMemStack_ClearCurrBlock(stack, false); - return; - } - - while (stack->block) - DqnMemStack_FreeLastBlock(stack); - - // After a stack is free, we reset the not expandable flag so that if we - // allocate on an empty stack it still works. - stack->flags &= ~DqnMemStackFlag_IsNotExpandable; -} - -DQN_FILE_SCOPE void DqnMemStack_ClearCurrBlock(DqnMemStack *const stack, - const bool zeroClear) -{ - if (!stack) return; - if (stack->block) - { - stack->block->used = 0; - if (zeroClear) - { - DqnMem_Clear(stack->block->memory, 0, stack->block->size); - } - } -} - -DQN_FILE_SCOPE DqnTempMemStack -DqnMemStack_BeginTempRegion(DqnMemStack *const stack) -{ - DqnTempMemStack result = {}; - result.stack = stack; - result.startingBlock = stack->block; - result.used = stack->block->used; - - stack->tempStackCount++; - return result; -} - -DQN_FILE_SCOPE void DqnMemStack_EndTempRegion(DqnTempMemStack tempStack) -{ - DqnMemStack *stack = tempStack.stack; - while (stack->block != tempStack.startingBlock) - DqnMemStack_FreeLastBlock(stack); - - if (stack->block) - { - DQN_ASSERT(stack->block->used >= tempStack.used); - stack->block->used = tempStack.used; - DQN_ASSERT(stack->tempStackCount >= 0); - } - stack->tempStackCount--; -} - //////////////////////////////////////////////////////////////////////////////// -// DqnMemAPI - Memory API, For using custom allocators +// DqnMemAPIInternal Implementation //////////////////////////////////////////////////////////////////////////////// FILE_SCOPE inline DqnMemAPICallbackInfo -DqnMemAPICallback_InfoAskReallocInternal(const DqnMemAPI memAPI, +DqnMemAPIInternal_CallbackInfoAskRealloc(const DqnMemAPI memAPI, void *const oldMemPtr, const size_t oldSize, const size_t newSize) { - DqnMemAPICallbackInfo info = {}; + DqnMemAPICallbackInfo info = {0}; info.type = DqnMemAPICallbackType_Realloc; info.userContext = memAPI.userContext; info.newRequestSize = newSize; @@ -1681,20 +1889,20 @@ DqnMemAPICallback_InfoAskReallocInternal(const DqnMemAPI memAPI, } FILE_SCOPE inline DqnMemAPICallbackInfo -DqnMemAPICallback_InfoAskAllocInternal(const DqnMemAPI memAPI, +DqnMemAPIInternal_CallbackInfoAskAlloc(const DqnMemAPI memAPI, const size_t size) { - DqnMemAPICallbackInfo info = {}; + DqnMemAPICallbackInfo info = {0}; info.type = DqnMemAPICallbackType_Alloc; info.userContext = memAPI.userContext; info.requestSize = size; return info; } -FILE_SCOPE DqnMemAPICallbackInfo DqnMemAPICallback_InfoAskFreeInternal( +FILE_SCOPE DqnMemAPICallbackInfo DqnMemAPIInternal_CallbackInfoAskFree( const DqnMemAPI memAPI, void *const ptrToFree, const size_t sizeToFree) { - DqnMemAPICallbackInfo info = {}; + DqnMemAPICallbackInfo info = {0}; info.type = DqnMemAPICallbackType_Free; info.userContext = memAPI.userContext; info.ptrToFree = ptrToFree; @@ -1702,23 +1910,23 @@ FILE_SCOPE DqnMemAPICallbackInfo DqnMemAPICallback_InfoAskFreeInternal( return info; } -void DqnMemAPI_ValidateCallbackInfo(DqnMemAPICallbackInfo info) +FILE_SCOPE void DqnMemAPIInternal_ValidateCallbackInfo(DqnMemAPICallbackInfo info) { - DQN_ASSERT(info.type != DqnMemAPICallbackType_Invalid); + DQN_ASSERT_HARD(info.type != DqnMemAPICallbackType_Invalid); switch(info.type) { case DqnMemAPICallbackType_Alloc: { - DQN_ASSERT(info.requestSize > 0); + DQN_ASSERT_HARD(info.requestSize > 0); } break; case DqnMemAPICallbackType_Realloc: { - DQN_ASSERT(info.oldSize > 0); - DQN_ASSERT(info.requestSize > 0); - DQN_ASSERT(info.oldMemPtr); + DQN_ASSERT_HARD(info.oldSize > 0); + DQN_ASSERT_HARD(info.requestSize > 0); + DQN_ASSERT_HARD(info.oldMemPtr); } break; @@ -1730,12 +1938,12 @@ void DqnMemAPI_ValidateCallbackInfo(DqnMemAPICallbackInfo info) } } -FILE_SCOPE void -DqnMemAPI_DefaultUseCallocCallbackInternal(DqnMemAPICallbackInfo info, - DqnMemAPICallbackResult *result) +FILE_SCOPE void DqnMemAPIInternal_DefaultUseCallocCallback(DqnMemAPICallbackInfo info, + DqnMemAPICallbackResult *result) { - DQN_ASSERT(!info.userContext); - DqnMemAPI_ValidateCallbackInfo(info); + DQN_ASSERT_HARD(!info.userContext); + + DqnMemAPIInternal_ValidateCallbackInfo(info); switch(info.type) { case DqnMemAPICallbackType_Alloc: @@ -1765,10 +1973,13 @@ DqnMemAPI_DefaultUseCallocCallbackInternal(DqnMemAPICallbackInfo info, } } +//////////////////////////////////////////////////////////////////////////////// +// DqnMemAPI Implementation - Mem API for custom allocation +//////////////////////////////////////////////////////////////////////////////// DQN_FILE_SCOPE DqnMemAPI DqnMemAPI_DefaultUseCalloc() { - DqnMemAPI result = {}; - result.callback = DqnMemAPI_DefaultUseCallocCallbackInternal; + DqnMemAPI result = {0}; + result.callback = DqnMemAPIInternal_DefaultUseCallocCallback; result.userContext = NULL; return result; } @@ -1808,22 +2019,22 @@ DQN_FILE_SCOPE f32 DqnMath_Clampf(f32 val, f32 min, f32 max) } //////////////////////////////////////////////////////////////////////////////// -// Vec2 +// DqnV2 Init Implementation //////////////////////////////////////////////////////////////////////////////// -DQN_FILE_SCOPE DqnV2 DqnV2_2f(f32 x, f32 y) +DQN_FILE_SCOPE DqnV2 DqnV2_1f(f32 xy) { - DqnV2 result = {}; - result.x = x; - result.y = y; + DqnV2 result = {0}; + result.x = xy; + result.y = xy; return result; } -DQN_FILE_SCOPE DqnV2 DqnV2_1f(f32 xy) +DQN_FILE_SCOPE DqnV2 DqnV2_2f(f32 x, f32 y) { - DqnV2 result = {}; - result.x = xy; - result.y = xy; + DqnV2 result = {0}; + result.x = x; + result.y = y; return result; } @@ -1836,15 +2047,18 @@ DQN_FILE_SCOPE DqnV2 DqnV2_2i(i32 x, i32 y) DQN_FILE_SCOPE DqnV2 DqnV2_V2i(DqnV2i a) { - DqnV2 result = {}; + DqnV2 result = {0}; result.x = (f32)a.x; result.y = (f32)a.y; return result; } +//////////////////////////////////////////////////////////////////////////////// +// DqnV2 Arithmetic Implementation +//////////////////////////////////////////////////////////////////////////////// DQN_FILE_SCOPE DqnV2 DqnV2_Add(DqnV2 a, DqnV2 b) { - DqnV2 result = {}; + DqnV2 result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] + b.e[i]; @@ -1853,7 +2067,7 @@ DQN_FILE_SCOPE DqnV2 DqnV2_Add(DqnV2 a, DqnV2 b) DQN_FILE_SCOPE DqnV2 DqnV2_Sub(DqnV2 a, DqnV2 b) { - DqnV2 result = {}; + DqnV2 result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] - b.e[i]; @@ -1862,7 +2076,7 @@ DQN_FILE_SCOPE DqnV2 DqnV2_Sub(DqnV2 a, DqnV2 b) DQN_FILE_SCOPE DqnV2 DqnV2_Scalei(DqnV2 a, i32 b) { - DqnV2 result = {}; + DqnV2 result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] * b; @@ -1871,7 +2085,7 @@ DQN_FILE_SCOPE DqnV2 DqnV2_Scalei(DqnV2 a, i32 b) DQN_FILE_SCOPE DqnV2 DqnV2_Scalef(DqnV2 a, f32 b) { - DqnV2 result = {}; + DqnV2 result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] * b; @@ -1880,7 +2094,7 @@ DQN_FILE_SCOPE DqnV2 DqnV2_Scalef(DqnV2 a, f32 b) DQN_FILE_SCOPE DqnV2 DqnV2_Hadamard(DqnV2 a, DqnV2 b) { - DqnV2 result = {}; + DqnV2 result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] * b.e[i]; @@ -1970,7 +2184,7 @@ DQN_FILE_SCOPE DqnV2 DqnV2_Perpendicular(DqnV2 a) DQN_FILE_SCOPE DqnV2 DqnV2_ConstrainToRatio(DqnV2 dim, DqnV2 ratio) { - DqnV2 result = {}; + DqnV2 result = {0}; f32 numRatioIncrementsToWidth = (f32)(dim.w / ratio.w); f32 numRatioIncrementsToHeight = (f32)(dim.h / ratio.h); @@ -1982,9 +2196,12 @@ DQN_FILE_SCOPE DqnV2 DqnV2_ConstrainToRatio(DqnV2 dim, DqnV2 ratio) return result; } +//////////////////////////////////////////////////////////////////////////////// +// DqnV2i Init Implementation +//////////////////////////////////////////////////////////////////////////////// DQN_FILE_SCOPE DqnV2i DqnV2i_2i(i32 x, i32 y) { - DqnV2i result = {}; + DqnV2i result = {0}; result.x = x; result.y = y; return result; @@ -1998,15 +2215,18 @@ DQN_FILE_SCOPE DqnV2i DqnV2i_2f(f32 x, f32 y) DQN_FILE_SCOPE DqnV2i DqnV2i_V2(DqnV2 a) { - DqnV2i result = {}; + DqnV2i result = {0}; result.x = (i32)a.x; result.y = (i32)a.y; return result; } +//////////////////////////////////////////////////////////////////////////////// +// DqnV2i Arithmetic Implementation +//////////////////////////////////////////////////////////////////////////////// DQN_FILE_SCOPE DqnV2i DqnV2i_Add(DqnV2i a, DqnV2i b) { - DqnV2i result = {}; + DqnV2i result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] + b.e[i]; @@ -2015,7 +2235,7 @@ DQN_FILE_SCOPE DqnV2i DqnV2i_Add(DqnV2i a, DqnV2i b) DQN_FILE_SCOPE DqnV2i DqnV2i_Sub(DqnV2i a, DqnV2i b) { - DqnV2i result = {}; + DqnV2i result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] - b.e[i]; @@ -2024,7 +2244,7 @@ DQN_FILE_SCOPE DqnV2i DqnV2i_Sub(DqnV2i a, DqnV2i b) DQN_FILE_SCOPE DqnV2i DqnV2i_Scalef(DqnV2i a, f32 b) { - DqnV2i result = {}; + DqnV2i result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = (i32)(a.e[i] * b); @@ -2033,7 +2253,7 @@ DQN_FILE_SCOPE DqnV2i DqnV2i_Scalef(DqnV2i a, f32 b) DQN_FILE_SCOPE DqnV2i DqnV2i_Scalei(DqnV2i a, i32 b) { - DqnV2i result = {}; + DqnV2i result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] * b; @@ -2042,7 +2262,7 @@ DQN_FILE_SCOPE DqnV2i DqnV2i_Scalei(DqnV2i a, i32 b) DQN_FILE_SCOPE DqnV2i DqnV2i_Hadamard(DqnV2i a, DqnV2i b) { - DqnV2i result = {}; + DqnV2i result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] * b.e[i]; @@ -2074,7 +2294,7 @@ DQN_FILE_SCOPE bool DqnV2i_Equals(DqnV2i a, DqnV2i b) } //////////////////////////////////////////////////////////////////////////////// -// Vec3 +// DqnV3 Init Implementation //////////////////////////////////////////////////////////////////////////////// DQN_FILE_SCOPE DqnV3 DqnV3_1f(f32 xyz) { @@ -2094,9 +2314,12 @@ DQN_FILE_SCOPE DqnV3 DqnV3_3i(i32 x, i32 y, i32 z) return result; } +//////////////////////////////////////////////////////////////////////////////// +// DqnV3 Arithmetic Implementation +//////////////////////////////////////////////////////////////////////////////// DQN_FILE_SCOPE DqnV3 DqnV3_Add(DqnV3 a, DqnV3 b) { - DqnV3 result = {}; + DqnV3 result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] + b.e[i]; @@ -2105,7 +2328,7 @@ DQN_FILE_SCOPE DqnV3 DqnV3_Add(DqnV3 a, DqnV3 b) DQN_FILE_SCOPE DqnV3 DqnV3_Sub(DqnV3 a, DqnV3 b) { - DqnV3 result = {}; + DqnV3 result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] - b.e[i]; @@ -2114,7 +2337,7 @@ DQN_FILE_SCOPE DqnV3 DqnV3_Sub(DqnV3 a, DqnV3 b) DQN_FILE_SCOPE DqnV3 DqnV3_Scalei(DqnV3 a, i32 b) { - DqnV3 result = {}; + DqnV3 result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] * b; @@ -2123,7 +2346,7 @@ DQN_FILE_SCOPE DqnV3 DqnV3_Scalei(DqnV3 a, i32 b) DQN_FILE_SCOPE DqnV3 DqnV3_Scalef(DqnV3 a, f32 b) { - DqnV3 result = {}; + DqnV3 result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] * b; @@ -2132,7 +2355,7 @@ DQN_FILE_SCOPE DqnV3 DqnV3_Scalef(DqnV3 a, f32 b) DQN_FILE_SCOPE DqnV3 DqnV3_Hadamard(DqnV3 a, DqnV3 b) { - DqnV3 result = {}; + DqnV3 result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] * b.e[i]; @@ -2172,7 +2395,7 @@ DQN_FILE_SCOPE DqnV3 DqnV3_Cross(DqnV3 a, DqnV3 b) |b| x |e| = |cd - af| |c| |f| |ae - be| */ - DqnV3 result = {}; + DqnV3 result = {0}; result.e[0] = (a.e[1] * b.e[2]) - (a.e[2] * b.e[1]); result.e[1] = (a.e[2] * b.e[0]) - (a.e[0] * b.e[2]); result.e[2] = (a.e[0] * b.e[1]) - (a.e[1] * b.e[0]); @@ -2206,6 +2429,9 @@ DQN_FILE_SCOPE f32 DqnV3_Length(DqnV3 a, DqnV3 b) return result; } +//////////////////////////////////////////////////////////////////////////////// +// DqnV3i Init Implementation +//////////////////////////////////////////////////////////////////////////////// DQN_FILE_SCOPE DqnV3i DqnV3i_3i(i32 x, i32 y, i32 z) { DqnV3i result = {x, y, z}; @@ -2219,8 +2445,14 @@ DQN_FILE_SCOPE DqnV3i DqnV3i_3f(f32 x, f32 y, f32 z) } //////////////////////////////////////////////////////////////////////////////// -// Vec4 +// DqnV4 Vec4 Init Implementation //////////////////////////////////////////////////////////////////////////////// +DQN_FILE_SCOPE DqnV4 DqnV4_1f(f32 xyzw) +{ + DqnV4 result = {xyzw, xyzw, xyzw, xyzw}; + return result; +} + DQN_FILE_SCOPE DqnV4 DqnV4_4f(f32 x, f32 y, f32 z, f32 w) { DqnV4 result = {x, y, z, w}; @@ -2241,15 +2473,12 @@ DQN_FILE_SCOPE DqnV4 DqnV4_V3(DqnV3 a, f32 w) return result; } -DQN_FILE_SCOPE DqnV4 DqnV4_1f(f32 xyzw) -{ - DqnV4 result = {xyzw, xyzw, xyzw, xyzw}; - return result; -} - +//////////////////////////////////////////////////////////////////////////////// +// DqnV4 Vec4 Arithmetic Implementation +//////////////////////////////////////////////////////////////////////////////// DQN_FILE_SCOPE DqnV4 DqnV4_Add(DqnV4 a, DqnV4 b) { - DqnV4 result = {}; + DqnV4 result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] + b.e[i]; @@ -2258,7 +2487,7 @@ DQN_FILE_SCOPE DqnV4 DqnV4_Add(DqnV4 a, DqnV4 b) DQN_FILE_SCOPE DqnV4 DqnV4_Sub(DqnV4 a, DqnV4 b) { - DqnV4 result = {}; + DqnV4 result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] - b.e[i]; @@ -2267,7 +2496,7 @@ DQN_FILE_SCOPE DqnV4 DqnV4_Sub(DqnV4 a, DqnV4 b) DQN_FILE_SCOPE DqnV4 DqnV4_Scalei(DqnV4 a, i32 b) { - DqnV4 result = {}; + DqnV4 result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] * b; @@ -2276,7 +2505,7 @@ DQN_FILE_SCOPE DqnV4 DqnV4_Scalei(DqnV4 a, i32 b) DQN_FILE_SCOPE DqnV4 DqnV4_Scalef(DqnV4 a, f32 b) { - DqnV4 result = {}; + DqnV4 result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] * b; @@ -2285,7 +2514,7 @@ DQN_FILE_SCOPE DqnV4 DqnV4_Scalef(DqnV4 a, f32 b) DQN_FILE_SCOPE DqnV4 DqnV4_Hadamard(DqnV4 a, DqnV4 b) { - DqnV4 result = {}; + DqnV4 result = {0}; for (i32 i = 0; i < DQN_ARRAY_COUNT(a.e); i++) result.e[i] = a.e[i] * b.e[i]; @@ -2430,6 +2659,16 @@ DQN_FILE_SCOPE DqnMat4 DqnMat4_Scale(f32 x, f32 y, f32 z) return result; } +DQN_FILE_SCOPE DqnMat4 DqnMat4_ScaleV3(DqnV3 scale) +{ + DqnMat4 result = {0}; + result.e[0][0] = scale.x; + result.e[1][1] = scale.y; + result.e[2][2] = scale.z; + result.e[3][3] = 1; + return result; +} + DQN_FILE_SCOPE DqnMat4 DqnMat4_Mul(DqnMat4 a, DqnMat4 b) { DqnMat4 result = {0}; @@ -2458,11 +2697,11 @@ DQN_FILE_SCOPE DqnV4 DqnMat4_MulV4(DqnMat4 a, DqnV4 b) } //////////////////////////////////////////////////////////////////////////////// -// Rect +// DqnRect Init Implementation //////////////////////////////////////////////////////////////////////////////// DQN_FILE_SCOPE DqnRect DqnRect_4f(f32 minX, f32 minY, f32 maxX, f32 maxY) { - DqnRect result = {}; + DqnRect result = {0}; result.min = DqnV2_2f(minX, minY); result.max = DqnV2_2f(maxX, maxY); @@ -2471,7 +2710,7 @@ DQN_FILE_SCOPE DqnRect DqnRect_4f(f32 minX, f32 minY, f32 maxX, f32 maxY) DQN_FILE_SCOPE DqnRect DqnRect_4i(i32 minX, i32 minY, i32 maxX, i32 maxY) { - DqnRect result = {}; + DqnRect result = {0}; result.min = DqnV2_2i(minX, minY); result.max = DqnV2_2i(maxX, maxY); @@ -2480,13 +2719,16 @@ DQN_FILE_SCOPE DqnRect DqnRect_4i(i32 minX, i32 minY, i32 maxX, i32 maxY) DQN_FILE_SCOPE DqnRect DqnRect_Init(DqnV2 origin, DqnV2 size) { - DqnRect result = {}; + DqnRect result = {0}; result.min = origin; result.max = DqnV2_Add(origin, size); return result; } +//////////////////////////////////////////////////////////////////////////////// +// DqnRect Implementation +//////////////////////////////////////////////////////////////////////////////// DQN_FILE_SCOPE void DqnRect_GetSize2f(DqnRect rect, f32 *width, f32 *height) { *width = rect.max.x - rect.min.x; @@ -2517,7 +2759,7 @@ DQN_FILE_SCOPE DqnV2 DqnRect_GetCentre(DqnRect rect) DQN_FILE_SCOPE DqnRect DqnRect_ClipRect(DqnRect rect, DqnRect clip) { - DqnRect result = {}; + DqnRect result = {0}; DqnV2 clipSize = DqnRect_GetSizeV2(clip); result.max.x = DQN_MIN(rect.max.x, clipSize.w); @@ -2552,7 +2794,7 @@ DQN_FILE_SCOPE bool DqnRect_ContainsP(DqnRect rect, DqnV2 p) } //////////////////////////////////////////////////////////////////////////////// -// char String Operations +// DqnChar Implementation //////////////////////////////////////////////////////////////////////////////// DQN_FILE_SCOPE char DqnChar_ToLower(char c) { @@ -2561,7 +2803,6 @@ DQN_FILE_SCOPE char DqnChar_ToLower(char c) i32 shiftOffset = 'a' - 'A'; return (c + (char)shiftOffset); } - return c; } @@ -2572,7 +2813,6 @@ DQN_FILE_SCOPE char DqnChar_ToUpper(char c) i32 shiftOffset = 'a' - 'A'; return (c - (char)shiftOffset); } - return c; } @@ -2595,7 +2835,10 @@ DQN_FILE_SCOPE bool DqnChar_IsAlphaNum(char c) return false; } -DQN_FILE_SCOPE i32 Dqn_strcmp(const char *a, const char *b) +//////////////////////////////////////////////////////////////////////////////// +// DqnStr Implementation +//////////////////////////////////////////////////////////////////////////////// +DQN_FILE_SCOPE i32 DqnStr_Cmp(const char *a, const char *b) { if (!a && !b) return -1; if (!a) return -1; @@ -2611,7 +2854,7 @@ DQN_FILE_SCOPE i32 Dqn_strcmp(const char *a, const char *b) return (((*a) < (*b)) ? -1 : 1); } -DQN_FILE_SCOPE i32 Dqn_strlen(const char *a) +DQN_FILE_SCOPE i32 DqnStr_Len(const char *a) { i32 result = 0; while (a && a[result]) result++; @@ -2619,14 +2862,14 @@ DQN_FILE_SCOPE i32 Dqn_strlen(const char *a) return result; } -DQN_FILE_SCOPE i32 Dqn_strlenDelimitWith(const char *a, const char delimiter) +DQN_FILE_SCOPE i32 DqnStr_LenDelimitWith(const char *a, const char delimiter) { i32 result = 0; while (a && a[result] && a[result] != delimiter) result++; return result; } -DQN_FILE_SCOPE char *Dqn_strncpy(char *dest, const char *src, i32 numChars) +DQN_FILE_SCOPE char *DqnStr_Copy(char *dest, const char *src, i32 numChars) { if (!dest) return NULL; if (!src) return dest; @@ -2637,7 +2880,7 @@ DQN_FILE_SCOPE char *Dqn_strncpy(char *dest, const char *src, i32 numChars) return dest; } -DQN_FILE_SCOPE bool Dqn_StrReverse(char *buf, const i32 bufSize) +DQN_FILE_SCOPE bool DqnStr_Reverse(char *buf, const i32 bufSize) { if (!buf) return false; i32 mid = bufSize / 2; @@ -2652,7 +2895,7 @@ DQN_FILE_SCOPE bool Dqn_StrReverse(char *buf, const i32 bufSize) return true; } -DQN_FILE_SCOPE i32 Dqn_StrFindFirstOccurence(const char *const src, const i32 srcLen, +DQN_FILE_SCOPE i32 DqnStr_FindFirstOccurence(const char *const src, const i32 srcLen, const char *const find, const i32 findLen) { if (!src || !find) return -1; @@ -2692,10 +2935,10 @@ DQN_FILE_SCOPE i32 Dqn_StrFindFirstOccurence(const char *const src, const i32 sr return -1; } -DQN_FILE_SCOPE bool Dqn_StrHasSubstring(const char *const src, const i32 srcLen, +DQN_FILE_SCOPE bool DqnStr_HasSubstring(const char *const src, const i32 srcLen, const char *const find, const i32 findLen) { - if (Dqn_StrFindFirstOccurence(src, srcLen, find, findLen) == -1) + if (DqnStr_FindFirstOccurence(src, srcLen, find, findLen) == -1) return false; return true; @@ -2731,7 +2974,7 @@ DQN_FILE_SCOPE i32 Dqn_I64ToStr(i64 value, char *const buf, const i32 bufSize) // it is for positives, so ABS will fail. lastDigitDecremented = true; val = DQN_ABS(val - 1); - DQN_ASSERT(val >= 0); + DQN_ASSERT_HARD(val >= 0); } if (validBuffer) @@ -2755,11 +2998,11 @@ DQN_FILE_SCOPE i32 Dqn_I64ToStr(i64 value, char *const buf, const i32 bufSize) // end if (negative) { - Dqn_StrReverse(buf + 1, charIndex - 1); + DqnStr_Reverse(buf + 1, charIndex - 1); } else { - Dqn_StrReverse(buf, charIndex); + DqnStr_Reverse(buf, charIndex); } } else @@ -2843,7 +3086,7 @@ DQN_FILE_SCOPE f32 Dqn_StrToF32(const char *const buf, const i32 bufSize) if (i < bufSize) { if (buf[i + 1] == '-') digitShiftIsPositive = false; - DQN_ASSERT(buf[i + 1] == '-' || buf[i + 1] == '+'); + DQN_ASSERT_HARD(buf[i + 1] == '-' || buf[i + 1] == '+'); i += 2; } @@ -2869,7 +3112,7 @@ DQN_FILE_SCOPE f32 Dqn_StrToF32(const char *const buf, const i32 bufSize) // NOTE(doyle): If exponent not specified but this branch occurred, // the float string has a malformed scientific notation in the // string, i.e. "e" followed by no number. - DQN_ASSERT(scientificNotation); + DQN_ASSERT_HARD(scientificNotation); numDigitsAfterDecimal += exponentPow; if (digitShiftIsPositive) digitShiftMultiplier = 10.0f; @@ -3059,7 +3302,7 @@ DQN_FILE_SCOPE u32 Dqn_UTF8ToUCS(u32 *dest, u32 character) } //////////////////////////////////////////////////////////////////////////////// -// wchar String Operations +// DqnWChar Implementation //////////////////////////////////////////////////////////////////////////////// DQN_FILE_SCOPE bool DqnWChar_IsDigit(const wchar_t c) { @@ -3078,14 +3321,14 @@ DQN_FILE_SCOPE wchar_t DqnWChar_ToLower(const wchar_t c) return c; } -DQN_FILE_SCOPE i32 Dqn_wstrlen(const wchar_t *a) +DQN_FILE_SCOPE i32 DqnWStr_Len(const wchar_t *a) { i32 result = 0; while (a && a[result]) result++; return result; } -DQN_FILE_SCOPE i32 Dqn_wstrcmp(const wchar_t *a, const wchar_t *b) +DQN_FILE_SCOPE i32 DqnWstr_Cmp(const wchar_t *a, const wchar_t *b) { if (!a && !b) return -1; if (!a) return -1; @@ -3189,86 +3432,18 @@ DQN_FILE_SCOPE i32 Dqn_I32ToWstr(i32 value, wchar_t *buf, i32 bufSize) return charIndex; } + //////////////////////////////////////////////////////////////////////////////// -// File Operations +// DqnFileInternal Implementation //////////////////////////////////////////////////////////////////////////////// -#ifdef DQN_WIN32_IMPLEMENTATION -DQN_FILE_SCOPE bool DqnWin32_UTF8ToWChar(const char *const in, - wchar_t *const out, const i32 outLen) -{ - u32 result = MultiByteToWideChar(CP_UTF8, 0, in, -1, out, outLen-1); - - if (result == 0xFFFD || 0) - { - DQN_WIN32_ERROR_BOX("WideCharToMultiByte() failed.", NULL); - return false; - } - - return true; -} - -DQN_FILE_SCOPE bool DqnWin32_WCharToUTF8(const wchar_t *const in, - char *const out, const i32 outLen) -{ - u32 result = - WideCharToMultiByte(CP_UTF8, 0, in, -1, out, outLen, NULL, NULL); - - if (result == 0xFFFD || 0) - { - DQN_WIN32_ERROR_BOX("WideCharToMultiByte() failed.", NULL); - return false; - } - - return true; -} - -DQN_FILE_SCOPE void DqnWin32_GetClientDim(const HWND window, LONG *width, - LONG *height) -{ - RECT rect; - GetClientRect(window, &rect); - if (width) *width = rect.right - rect.left; - if (height) *height = rect.bottom - rect.top; -} - -DQN_FILE_SCOPE void DqnWin32_GetRectDim(RECT rect, LONG *width, LONG *height) -{ - if (width) *width = rect.right - rect.left; - if (height) *height = rect.bottom - rect.top; -} - -DQN_FILE_SCOPE void DqnWin32_DisplayLastError(const char *const errorPrefix) -{ - DWORD error = GetLastError(); - char errorMsg[1024] = {}; - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, error, 0, errorMsg, DQN_ARRAY_COUNT(errorMsg), NULL); - - char formattedError[2048] = {}; - Dqn_sprintf(formattedError, "%s: %s", errorPrefix, errorMsg); - DQN_WIN32_ERROR_BOX(formattedError, NULL); -} - -DQN_FILE_SCOPE void DqnWin32_DisplayErrorCode(const DWORD error, const char *const errorPrefix) -{ - char errorMsg[1024] = {}; - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, error, 0, errorMsg, DQN_ARRAY_COUNT(errorMsg), NULL); - - char formattedError[2048] = {}; - Dqn_sprintf(formattedError, "%s: %s", errorPrefix, errorMsg); - DQN_WIN32_ERROR_BOX(formattedError, NULL); -} -#endif // DQN_WIN32_PLATFROM - -FILE_SCOPE bool DqnFile_OpenInternal(const wchar_t *const path, +FILE_SCOPE bool DqnFileInternal_Open(const wchar_t *const path, DqnFile *const file, const u32 permissionFlags, const enum DqnFileAction action) { if (!file || !path) return false; -#ifdef DQN_WIN32_IMPLEMENTATION +#ifdef DQN_WIN32_PLATFORM DWORD win32Permission = 0; if (permissionFlags & DqnFilePermissionFlag_All) { @@ -3316,35 +3491,68 @@ FILE_SCOPE bool DqnFile_OpenInternal(const wchar_t *const path, #endif } -DQN_FILE_SCOPE -bool DqnFile_OpenW(const wchar_t *const path, DqnFile *const file, - const u32 permissionFlags, const enum DqnFileAction action) -{ - if (!file || !path) return false; - return DqnFile_OpenInternal(path, file, permissionFlags, action); -} - +//////////////////////////////////////////////////////////////////////////////// +// DqnFile Implementation +//////////////////////////////////////////////////////////////////////////////// DQN_FILE_SCOPE bool DqnFile_Open(const char *const path, DqnFile *const file, const u32 permissionFlags, const enum DqnFileAction action) { if (!file || !path) return false; -#ifdef DQN_WIN32_IMPLEMENTATION - wchar_t widePath[MAX_PATH] = {}; +#ifdef DQN_WIN32_PLATFORM + wchar_t widePath[MAX_PATH] = {0}; DqnWin32_UTF8ToWChar(path, widePath, DQN_ARRAY_COUNT(widePath)); - return DqnFile_OpenInternal(widePath, file, permissionFlags, action); + return DqnFileInternal_Open(widePath, file, permissionFlags, action); #else DQN_ASSERT(DQN_INVALID_CODE_PATH); return false; #endif } +DQN_FILE_SCOPE +bool DqnFile_OpenW(const wchar_t *const path, DqnFile *const file, const u32 permissionFlags, + const enum DqnFileAction action) +{ + if (!file || !path) return false; + return DqnFileInternal_Open(path, file, permissionFlags, action); +} + +DQN_FILE_SCOPE size_t DqnFile_Write(const DqnFile *const file, + const u8 *const buffer, + const size_t numBytesToWrite, + const size_t fileOffset) +{ + size_t numBytesWritten = 0; + // TODO(doyle): Implement when it's needed + if (DQN_ASSERT_MSG(fileOffset != 0, "'fileOffset' not implemented yet")) return 0; + if (!file || !buffer) return numBytesToWrite; + +#ifdef DQN_WIN32_PLATFORM + DWORD bytesToWrite = (DWORD)numBytesToWrite; + DWORD bytesWritten; + BOOL result = + WriteFile(file->handle, buffer, bytesToWrite, &bytesWritten, NULL); + + numBytesWritten = (size_t)bytesWritten; + // TODO(doyle): Better logging system + if (result == 0) + { + DQN_WIN32_ERROR_BOX("ReadFile() failed.", NULL); + } + +#else + DQN_ASSERT_MSG(DQN_INVALID_CODE_PATH, "Non Win32 path not implemented"); +#endif + + return numBytesWritten; +} + DQN_FILE_SCOPE size_t DqnFile_Read(const DqnFile file, const u8 *const buffer, const size_t numBytesToRead) { size_t numBytesRead = 0; -#ifdef DQN_WIN32_IMPLEMENTATION +#ifdef DQN_WIN32_PLATFORM if (file.handle && buffer) { DWORD bytesToRead = (DWORD)numBytesToRead; @@ -3362,44 +3570,13 @@ DQN_FILE_SCOPE size_t DqnFile_Read(const DqnFile file, const u8 *const buffer, } } -#endif // DQN_WIN32_IMPLEMENTATION +#endif return numBytesRead; } -DQN_FILE_SCOPE size_t DqnFile_Write(const DqnFile *const file, - const u8 *const buffer, - const size_t numBytesToWrite, - const size_t fileOffset) -{ - size_t numBytesWritten = 0; - // TODO(doyle): Implement when it's needed - DQN_ASSERT(fileOffset == 0); - if (!file || !buffer) return numBytesToWrite; - -#ifdef DQN_WIN32_IMPLEMENTATION - DWORD bytesToWrite = (DWORD)numBytesToWrite; - DWORD bytesWritten; - BOOL result = - WriteFile(file->handle, buffer, bytesToWrite, &bytesWritten, NULL); - - numBytesWritten = (size_t)bytesWritten; - // TODO(doyle): Better logging system - if (result == 0) - { - DQN_WIN32_ERROR_BOX("ReadFile() failed.", NULL); - } - -#else - DQN_ASSERT(DQN_INVALID_CODE_PATH); -#endif - - return numBytesWritten; -} - - DQN_FILE_SCOPE void DqnFile_Close(DqnFile *const file) { -#ifdef DQN_WIN32_IMPLEMENTATION +#ifdef DQN_WIN32_PLATFORM if (file && file->handle) { CloseHandle(file->handle); @@ -3408,22 +3585,22 @@ DQN_FILE_SCOPE void DqnFile_Close(DqnFile *const file) file->permissionFlags = 0; } #else - DQN_ASSERT(DQN_INVALID_CODE_PATH); + DQN_ASSERT_MSG(DQN_INVALID_CODE_PATH, "Non Win32 path not implemented"); #endif } DQN_FILE_SCOPE char **DqnDir_Read(char *dir, u32 *numFiles) { if (!dir) return NULL; -#ifdef DQN_WIN32_IMPLEMENTATION +#ifdef DQN_WIN32_PLATFORM u32 currNumFiles = 0; - wchar_t wideDir[MAX_PATH] = {}; + wchar_t wideDir[MAX_PATH] = {0}; DqnWin32_UTF8ToWChar(dir, wideDir, DQN_ARRAY_COUNT(wideDir)); // Enumerate number of files first { - WIN32_FIND_DATAW findData = {}; + WIN32_FIND_DATAW findData = {0}; HANDLE findHandle = FindFirstFileW(wideDir, &findData); if (findHandle == INVALID_HANDLE_VALUE) { @@ -3461,7 +3638,7 @@ DQN_FILE_SCOPE char **DqnDir_Read(char *dir, u32 *numFiles) } { - WIN32_FIND_DATAW initFind = {}; + WIN32_FIND_DATAW initFind = {0}; HANDLE findHandle = FindFirstFileW(wideDir, &initFind); if (findHandle == INVALID_HANDLE_VALUE) { @@ -3494,7 +3671,7 @@ DQN_FILE_SCOPE char **DqnDir_Read(char *dir, u32 *numFiles) } i32 listIndex = 0; - WIN32_FIND_DATAW findData = {}; + WIN32_FIND_DATAW findData = {0}; while (FindNextFileW(findHandle, &findData) != 0) { DqnWin32_WCharToUTF8(findData.cFileName, list[listIndex++], @@ -3528,14 +3705,14 @@ DQN_FILE_SCOPE void DqnDir_ReadFree(char **fileList, u32 numFiles) //////////////////////////////////////////////////////////////////////////////// // Timer //////////////////////////////////////////////////////////////////////////////// -#ifdef DQN_WIN32_IMPLEMENTATION -FILE_SCOPE f64 DqnWin32_QueryPerfCounterTimeInSInternal() +#ifdef DQN_WIN32_PLATFORM +FILE_SCOPE f64 DqnTimeInternal_Win32QueryPerfCounterTimeInS() { - LOCAL_PERSIST LARGE_INTEGER queryPerformanceFrequency = {}; + LOCAL_PERSIST LARGE_INTEGER queryPerformanceFrequency = {0}; if (queryPerformanceFrequency.QuadPart == 0) { QueryPerformanceFrequency(&queryPerformanceFrequency); - DQN_ASSERT(queryPerformanceFrequency.QuadPart != 0); + DQN_ASSERT_HARD(queryPerformanceFrequency.QuadPart != 0); } LARGE_INTEGER qpcResult; @@ -3551,11 +3728,11 @@ FILE_SCOPE f64 DqnWin32_QueryPerfCounterTimeInSInternal() f64 DqnTime_NowInS() { f64 result; -#ifdef DQN_WIN32_IMPLEMENTATION - result = DQN_MAX(DqnWin32_QueryPerfCounterTimeInSInternal(), 0); +#ifdef DQN_WIN32_PLATFORM + result = DQN_MAX(DqnTimeInternal_Win32QueryPerfCounterTimeInS(), 0); #else result = 0; - DQN_ASSERT(DQN_INVALID_CODE_PATH); + DQN_ASSERT_MSG(DQN_INVALID_CODE_PATH, "Non Win32 path not implemented yet"); #endif return result; }; @@ -3591,17 +3768,17 @@ FILE_SCOPE u64 DqnRnd_Murmur3Avalanche64Internal(u64 h) FILE_SCOPE u32 DqnRnd_MakeSeedInternal() { -#ifdef DQN_WIN32_IMPLEMENTATION +#ifdef DQN_WIN32_PLATFORM __int64 numClockCycles = __rdtsc(); return (u32)numClockCycles; #elif __ANDROID__ - DQN_ASSERT(DQN_INVALID_CODE_PATH); + DQN_ASSERT_MSG(DQN_INVALID_CODE_PATH, "Android path not implemented yet"); return 0; #elif __linux__ unsigned long long numClockCycles = rdtsc(); return (u32)numClockCycles; #else - DQN_ASSERT(DQN_INVALID_CODE_PATH); + DQN_ASSERT_MSG(DQN_INVALID_CODE_PATH, "Non Win32 path not implemented yet"); return 0; #endif } @@ -3645,6 +3822,416 @@ DQN_FILE_SCOPE i32 DqnRnd_PCGRange(DqnRandPCGState *pcg, i32 min, i32 max) return min + value; } +//////////////////////////////////////////////////////////////////////////////// +// DqnLock +//////////////////////////////////////////////////////////////////////////////// +bool DqnLock_Init(DqnLock *const lock, const u32 spinCount) +{ + if (!lock) return false; + +#ifdef DQN_WIN32_PLATFORM + if (InitializeCriticalSectionEx(&lock->win32Handle, spinCount, 0)) + return true; +#endif + + return false; +} + +void DqnLock_Acquire(DqnLock *const lock) +{ + if (!lock) return; +#ifdef DQN_WIN32_PLATFORM + EnterCriticalSection(&lock->win32Handle); +#else + DQN_ASSERT_HARD(DQN_INVALID_CODE_PATH); +#endif +} + +void DqnLock_Release(DqnLock *const lock) +{ + if (!lock) return; +#ifdef DQN_WIN32_PLATFORM + LeaveCriticalSection(&lock->win32Handle); +#else + DQN_ASSERT_HARD(DQN_INVALID_CODE_PATH); +#endif +} + +void DqnLock_Delete(DqnLock *const lock) +{ + if (!lock) return; +#ifdef DQN_WIN32_PLATFORM + DeleteCriticalSection(&lock->win32Handle); +#else + DQN_ASSERT_HARD(DQN_INVALID_CODE_PATH); +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +// DqnAtomics +//////////////////////////////////////////////////////////////////////////////// +DQN_FILE_SCOPE u32 DqnAtomic_CompareSwap32(u32 volatile *dest, u32 swapVal, u32 compareVal) +{ +#ifdef DQN_WIN32_PLATFORM + DQN_ASSERT(sizeof(LONG) == sizeof(u32)); + u32 result = + (u32)InterlockedCompareExchange((LONG volatile *)dest, (LONG)swapVal, (LONG)compareVal); + return result; +#else + DQN_ASSERT_HARD(DQN_INVALID_CODE_PATH); + return 0; +#endif +} + +DQN_FILE_SCOPE u32 DqnAtomic_Add32(u32 volatile *src) +{ +#ifdef DQN_WIN32_PLATFORM + DQN_ASSERT(sizeof(LONG) == sizeof(u32)); + u32 result = (u32)InterlockedIncrement((LONG volatile *)src); + return result; +#else + DQN_ASSERT_HARD(DQN_INVALID_CODE_PATH); + return 0; +#endif +} + +DQN_FILE_SCOPE u32 DqnAtomic_Sub32(u32 volatile *src) +{ +#ifdef DQN_WIN32_PLATFORM + DQN_ASSERT(sizeof(LONG) == sizeof(u32)); + u32 result = (u32)InterlockedDecrement((LONG volatile *)src); + return result; +#else + DQN_ASSERT_HARD(DQN_INVALID_CODE_PATH); + return 0; +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +// DqnJobQueue - Multithreaded Job Queue +//////////////////////////////////////////////////////////////////////////////// +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; + +//////////////////////////////////////////////////////////////////////////////// +// DqnJobQueueInternal +//////////////////////////////////////////////////////////////////////////////// +size_t DQN_JOB_QUEUE_INTERNAL_THREAD_DEFAULT_STACK_SIZE = 0; +FILE_SCOPE u32 DqnJobQueueInternal_ThreadCreate(const size_t stackSize, void *threadCallback, + void *threadParam, const u32 numThreads) +{ + u32 numThreadsCreated = 0; + +#ifdef DQN_WIN32_PLATFORM + DQN_ASSERT_HARD(stackSize == 0 || !threadCallback); + for (u32 i = 0; i < numThreads; i++) + { + HANDLE handle = CreateThread(NULL, stackSize, (LPTHREAD_START_ROUTINE)threadCallback, + threadParam, 0, NULL); + CloseHandle(handle); + numThreadsCreated++; + } + +#else + DQN_ASSERT(DQN_INVALID_CODE_PATH); +#endif + + DQN_ASSERT(numThreadsCreated == numThreads); + return numThreadsCreated; +} + + +FILE_SCOPE u32 DqnJobQueueInternal_ThreadCallback(void *threadParam) +{ + DqnJobQueue *queue = (DqnJobQueue *)threadParam; +#ifdef DQN_WIN32_PLATFORM + for (;;) + { + if (!DqnJobQueue_TryExecuteNextJob(queue)) + { + WaitForSingleObjectEx(queue->semaphore, INFINITE, false); + } + } +#else + DQN_ASSERT(DQN_INVALID_CODE_PATH); + return 0; +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +// DqnJobQueue Implementation +//////////////////////////////////////////////////////////////////////////////// +DQN_FILE_SCOPE DqnJobQueue *DqnJobQueue_InitWithMem(const void *const mem, size_t *const memSize, + const u32 queueSize, const u32 numThreads) +{ + DqnJobQueue emptyQueue = {}; + size_t reqStructSize = sizeof(emptyQueue); + size_t reqQueueSize = sizeof(*emptyQueue.jobList) * queueSize; + + 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); +#else + DQN_ASSERT_HARD(DQN_INVALID_CODE_PATH); +#endif + + // Create threads + u32 numThreadsCreated = DqnJobQueueInternal_ThreadCreate( + DQN_JOB_QUEUE_INTERNAL_THREAD_DEFAULT_STACK_SIZE, DqnJobQueueInternal_ThreadCallback, + (void *)queue, numThreads); + DQN_ASSERT_HARD(numThreads == numThreadsCreated); + return queue; +} + +DQN_FILE_SCOPE bool DqnJobQueue_AddJob(DqnJobQueue *const queue, const DqnJob job) +{ + u32 newJobInsertIndex = (queue->jobInsertIndex + 1) % queue->size; + if (newJobInsertIndex == queue->jobToExecuteIndex) return false; + + queue->jobList[queue->jobInsertIndex] = job; + + DqnAtomic_Add32(&queue->numJobsToComplete); + +#ifdef DQN_WIN32_PLATFORM + ReleaseSemaphore(queue->semaphore, 1, NULL); +#else + DQN_ASSERT_HARD(DQN_INVALID_CODE_PATH); +#endif + queue->jobInsertIndex = newJobInsertIndex; + return true; +} + +DQN_FILE_SCOPE bool DqnJobQueue_TryExecuteNextJob(DqnJobQueue *const queue) +{ + u32 originalJobToExecute = queue->jobToExecuteIndex; + if (originalJobToExecute != queue->jobInsertIndex) + { + u32 newJobIndexForNextThread = (originalJobToExecute + 1) % queue->size; + u32 index = DqnAtomic_CompareSwap32(&queue->jobToExecuteIndex, newJobIndexForNextThread, + originalJobToExecute); + + // NOTE: If we weren't successful at the interlock, another thread has + // taken the work and we can't know if there's more work or not. So + // irrespective of that result, return true to let the thread check + // again for more work. + if (index == originalJobToExecute) + { + DqnJob job = queue->jobList[index]; + job.callback(queue, job.userData); + DqnAtomic_Sub32(&queue->numJobsToComplete); + } + + return true; + } + + return false; +} + +DQN_FILE_SCOPE bool DqnJobQueue_AllJobsComplete(DqnJobQueue *const queue) +{ + bool result = (queue->numJobsToComplete == 0); + return result; +} + + + +//////////////////////////////////////////////////////////////////////////////// +// DqnWin32 Operations +//////////////////////////////////////////////////////////////////////////////// +#ifdef DQN_WIN32_PLATFORM +DQN_FILE_SCOPE bool DqnWin32_UTF8ToWChar(const char *const in, + wchar_t *const out, const i32 outLen) +{ + u32 result = MultiByteToWideChar(CP_UTF8, 0, in, -1, out, outLen-1); + + if (result == 0xFFFD || 0) + { + DQN_WIN32_ERROR_BOX("WideCharToMultiByte() failed.", NULL); + return false; + } + + return true; +} + +DQN_FILE_SCOPE bool DqnWin32_WCharToUTF8(const wchar_t *const in, + char *const out, const i32 outLen) +{ + u32 result = + WideCharToMultiByte(CP_UTF8, 0, in, -1, out, outLen, NULL, NULL); + + if (result == 0xFFFD || 0) + { + DQN_WIN32_ERROR_BOX("WideCharToMultiByte() failed.", NULL); + return false; + } + + return true; +} + +DQN_FILE_SCOPE void DqnWin32_GetClientDim(const HWND window, LONG *width, + LONG *height) +{ + RECT rect; + GetClientRect(window, &rect); + if (width) *width = rect.right - rect.left; + if (height) *height = rect.bottom - rect.top; +} + +DQN_FILE_SCOPE void DqnWin32_GetRectDim(RECT rect, LONG *width, LONG *height) +{ + if (width) *width = rect.right - rect.left; + if (height) *height = rect.bottom - rect.top; +} + +DQN_FILE_SCOPE void DqnWin32_DisplayLastError(const char *const errorPrefix) +{ + DWORD error = GetLastError(); + char errorMsg[1024] = {0}; + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, error, 0, errorMsg, DQN_ARRAY_COUNT(errorMsg), NULL); + + char formattedError[2048] = {0}; + Dqn_sprintf(formattedError, "%s: %s", errorPrefix, errorMsg); + DQN_WIN32_ERROR_BOX(formattedError, NULL); +} + +DQN_FILE_SCOPE void DqnWin32_DisplayErrorCode(const DWORD error, const char *const errorPrefix) +{ + char errorMsg[1024] = {0}; + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, error, 0, errorMsg, DQN_ARRAY_COUNT(errorMsg), NULL); + + char formattedError[2048] = {0}; + Dqn_sprintf(formattedError, "%s: %s", errorPrefix, errorMsg); + DQN_WIN32_ERROR_BOX(formattedError, NULL); +} + +DQN_FILE_SCOPE void DqnWin32_OutputDebugString(const char *const formatStr, ...) +{ + char str[1024] = {0}; + + va_list argList; + va_start(argList, formatStr); + { + i32 numCopied = Dqn_vsprintf(str, formatStr, argList); + DQN_ASSERT_HARD(numCopied < DQN_ARRAY_COUNT(str)); + } + va_end(argList); + + OutputDebugString(str); +} + +DQN_FILE_SCOPE i32 DqnWin32_GetEXEDirectory(char *const buf, const u32 bufLen) +{ + if (!buf || bufLen == 0) return 0; + u32 copiedLen = GetModuleFileName(NULL, buf, bufLen); + if (copiedLen == bufLen) return -1; + + // NOTE: Should always work if GetModuleFileName works and we're running an + // executable. + i32 lastSlashIndex = 0; + for (i32 i = copiedLen; i > 0; i--) + { + if (buf[i] == '\\') + { + lastSlashIndex = i; + break; + } + } + + return lastSlashIndex; +} + +DQN_FILE_SCOPE void DqnWin32_GetNumThreadsAndCores(i32 *const numCores, i32 *const numThreadsPerCore) +{ + if (numThreadsPerCore) + { + SYSTEM_INFO systemInfo; + GetNativeSystemInfo(&systemInfo); + *numThreadsPerCore = systemInfo.dwNumberOfProcessors; + } + + if (numCores) + { + *numCores = 0; + DWORD requiredSize = 0; + u8 insufficientBuffer = {0}; + GetLogicalProcessorInformationEx( + RelationProcessorCore, (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)insufficientBuffer, + &requiredSize); + + u8 *rawProcInfoArray = (u8 *)DqnMem_Calloc(requiredSize); + if (!DQN_ASSERT_MSG(rawProcInfoArray, "Calloc failed, could not allocate memory")) + { + return; + } + + if (GetLogicalProcessorInformationEx( + RelationProcessorCore, (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)rawProcInfoArray, + &requiredSize)) + { + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *logicalProcInfo = + (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)rawProcInfoArray; + DWORD bytesRead = 0; + + do + { + // NOTE: High efficiency value has greater performance and less efficiency. + PROCESSOR_RELATIONSHIP *procInfo = &logicalProcInfo->Processor; + u32 efficiency = procInfo->EfficiencyClass; + (*numCores)++; + DQN_ASSERT_HARD(logicalProcInfo->Relationship == RelationProcessorCore); + DQN_ASSERT_HARD(procInfo->GroupCount == 1); + + bytesRead += logicalProcInfo->Size; + logicalProcInfo = + (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)((u8 *)logicalProcInfo + + logicalProcInfo->Size); + } while (bytesRead < requiredSize); + } + else + { + DqnWin32_DisplayLastError("GetLogicalProcessorInformationEx() failed"); + } + + DqnMem_Free(rawProcInfoArray); + } +} +#endif // DQN_WIN32_PLATFORM + //////////////////////////////////////////////////////////////////////////////// // STB_Sprintf //////////////////////////////////////////////////////////////////////////////// diff --git a/dqn_unit_test.cpp b/dqn_unit_test.cpp index 1c25ce1..75f5564 100644 --- a/dqn_unit_test.cpp +++ b/dqn_unit_test.cpp @@ -136,21 +136,21 @@ void StringsTest() // Check simple compares { - DQN_ASSERT(Dqn_strcmp(a, "str_a") == +0); - DQN_ASSERT(Dqn_strcmp(a, "str_b") == -1); - DQN_ASSERT(Dqn_strcmp("str_b", a) == +1); - DQN_ASSERT(Dqn_strcmp(a, "") == +1); - DQN_ASSERT(Dqn_strcmp("", "") == 0); + DQN_ASSERT(DqnStr_Cmp(a, "str_a") == +0); + DQN_ASSERT(DqnStr_Cmp(a, "str_b") == -1); + DQN_ASSERT(DqnStr_Cmp("str_b", a) == +1); + DQN_ASSERT(DqnStr_Cmp(a, "") == +1); + DQN_ASSERT(DqnStr_Cmp("", "") == 0); // NOTE: Check that the string has not been trashed. - DQN_ASSERT(Dqn_strcmp(a, "str_a") == +0); + DQN_ASSERT(DqnStr_Cmp(a, "str_a") == +0); } // Check ops against null { - DQN_ASSERT(Dqn_strcmp(NULL, NULL) != +0); - DQN_ASSERT(Dqn_strcmp(a, NULL) != +0); - DQN_ASSERT(Dqn_strcmp(NULL, a) != +0); + DQN_ASSERT(DqnStr_Cmp(NULL, NULL) != +0); + DQN_ASSERT(DqnStr_Cmp(a, NULL) != +0); + DQN_ASSERT(DqnStr_Cmp(NULL, a) != +0); } printf("StringsTest(): strcmp: Completed successfully\n"); @@ -159,15 +159,15 @@ void StringsTest() // strlen { char *a = "str_a"; - DQN_ASSERT(Dqn_strlen(a) == 5); - DQN_ASSERT(Dqn_strlen("") == 0); - DQN_ASSERT(Dqn_strlen(" a ") == 6); - DQN_ASSERT(Dqn_strlen("a\n") == 2); + DQN_ASSERT(DqnStr_Len(a) == 5); + DQN_ASSERT(DqnStr_Len("") == 0); + DQN_ASSERT(DqnStr_Len(" a ") == 6); + DQN_ASSERT(DqnStr_Len("a\n") == 2); // NOTE: Check that the string has not been trashed. - DQN_ASSERT(Dqn_strcmp(a, "str_a") == 0); + DQN_ASSERT(DqnStr_Cmp(a, "str_a") == 0); - DQN_ASSERT(Dqn_strlen(NULL) == 0); + DQN_ASSERT(DqnStr_Len(NULL) == 0); printf("StringsTest(): strlen: Completed successfully\n"); } @@ -179,37 +179,37 @@ void StringsTest() char b[10] = {}; // Check copy into empty array { - char *result = Dqn_strncpy(b, a, Dqn_strlen(a)); - DQN_ASSERT(Dqn_strcmp(b, "str_a") == 0); - DQN_ASSERT(Dqn_strcmp(a, "str_a") == 0); - DQN_ASSERT(Dqn_strcmp(result, "str_a") == 0); - DQN_ASSERT(Dqn_strlen(result) == 5); + char *result = DqnStr_Copy(b, a, DqnStr_Len(a)); + DQN_ASSERT(DqnStr_Cmp(b, "str_a") == 0); + DQN_ASSERT(DqnStr_Cmp(a, "str_a") == 0); + DQN_ASSERT(DqnStr_Cmp(result, "str_a") == 0); + DQN_ASSERT(DqnStr_Len(result) == 5); } // Check copy into array offset, overlap with old results { - char *newResult = Dqn_strncpy(&b[1], a, Dqn_strlen(a)); - DQN_ASSERT(Dqn_strcmp(newResult, "str_a") == 0); - DQN_ASSERT(Dqn_strlen(newResult) == 5); + char *newResult = DqnStr_Copy(&b[1], a, DqnStr_Len(a)); + DQN_ASSERT(DqnStr_Cmp(newResult, "str_a") == 0); + DQN_ASSERT(DqnStr_Len(newResult) == 5); - DQN_ASSERT(Dqn_strcmp(a, "str_a") == 0); - DQN_ASSERT(Dqn_strlen(a) == 5); + DQN_ASSERT(DqnStr_Cmp(a, "str_a") == 0); + DQN_ASSERT(DqnStr_Len(a) == 5); - DQN_ASSERT(Dqn_strcmp(b, "sstr_a") == 0); - DQN_ASSERT(Dqn_strlen(b) == 6); + DQN_ASSERT(DqnStr_Cmp(b, "sstr_a") == 0); + DQN_ASSERT(DqnStr_Len(b) == 6); } } // Check strncpy with NULL pointers { - DQN_ASSERT(Dqn_strncpy(NULL, NULL, 5) == NULL); + DQN_ASSERT(DqnStr_Copy(NULL, NULL, 5) == NULL); char *a = "str"; - char *result = Dqn_strncpy(a, NULL, 5); + char *result = DqnStr_Copy(a, NULL, 5); - DQN_ASSERT(Dqn_strcmp(a, "str") == 0); - DQN_ASSERT(Dqn_strcmp(result, "str") == 0); - DQN_ASSERT(Dqn_strcmp(result, a) == 0); + DQN_ASSERT(DqnStr_Cmp(a, "str") == 0); + DQN_ASSERT(DqnStr_Cmp(result, "str") == 0); + DQN_ASSERT(DqnStr_Cmp(result, a) == 0); } // Check strncpy with 0 chars to copy @@ -217,10 +217,10 @@ void StringsTest() char *a = "str"; char *b = "ing"; - char *result = Dqn_strncpy(a, b, 0); - DQN_ASSERT(Dqn_strcmp(a, "str") == 0); - DQN_ASSERT(Dqn_strcmp(b, "ing") == 0); - DQN_ASSERT(Dqn_strcmp(result, "str") == 0); + char *result = DqnStr_Copy(a, b, 0); + DQN_ASSERT(DqnStr_Cmp(a, "str") == 0); + DQN_ASSERT(DqnStr_Cmp(b, "ing") == 0); + DQN_ASSERT(DqnStr_Cmp(result, "str") == 0); } printf("StringsTest(): strncpy: Completed successfully\n"); @@ -231,37 +231,37 @@ void StringsTest() // Basic reverse operations { char a[] = "aba"; - DQN_ASSERT(Dqn_StrReverse(a, Dqn_strlen(a)) == true); - DQN_ASSERT(Dqn_strcmp(a, "aba") == 0); + DQN_ASSERT(DqnStr_Reverse(a, DqnStr_Len(a)) == true); + DQN_ASSERT(DqnStr_Cmp(a, "aba") == 0); - DQN_ASSERT(Dqn_StrReverse(a, 2) == true); - DQN_ASSERT(Dqn_strcmp(a, "baa") == 0); + DQN_ASSERT(DqnStr_Reverse(a, 2) == true); + DQN_ASSERT(DqnStr_Cmp(a, "baa") == 0); - DQN_ASSERT(Dqn_StrReverse(a, Dqn_strlen(a)) == true); - DQN_ASSERT(Dqn_strcmp(a, "aab") == 0); + DQN_ASSERT(DqnStr_Reverse(a, DqnStr_Len(a)) == true); + DQN_ASSERT(DqnStr_Cmp(a, "aab") == 0); - DQN_ASSERT(Dqn_StrReverse(&a[1], 2) == true); - DQN_ASSERT(Dqn_strcmp(a, "aba") == 0); + DQN_ASSERT(DqnStr_Reverse(&a[1], 2) == true); + DQN_ASSERT(DqnStr_Cmp(a, "aba") == 0); - DQN_ASSERT(Dqn_StrReverse(a, 0) == true); - DQN_ASSERT(Dqn_strcmp(a, "aba") == 0); + DQN_ASSERT(DqnStr_Reverse(a, 0) == true); + DQN_ASSERT(DqnStr_Cmp(a, "aba") == 0); } // Try reverse empty string { char a[] = ""; - DQN_ASSERT(Dqn_StrReverse(a, Dqn_strlen(a)) == true); - DQN_ASSERT(Dqn_strcmp(a, "") == 0); + DQN_ASSERT(DqnStr_Reverse(a, DqnStr_Len(a)) == true); + DQN_ASSERT(DqnStr_Cmp(a, "") == 0); } // Try reverse single char string { char a[] = "a"; - DQN_ASSERT(Dqn_StrReverse(a, Dqn_strlen(a)) == true); - DQN_ASSERT(Dqn_strcmp(a, "a") == 0); + DQN_ASSERT(DqnStr_Reverse(a, DqnStr_Len(a)) == true); + DQN_ASSERT(DqnStr_Cmp(a, "a") == 0); - DQN_ASSERT(Dqn_StrReverse(a, 0) == true); - DQN_ASSERT(Dqn_strcmp(a, "a") == 0); + DQN_ASSERT(DqnStr_Reverse(a, 0) == true); + DQN_ASSERT(DqnStr_Cmp(a, "a") == 0); } printf( @@ -273,26 +273,26 @@ void StringsTest() // StrToI64 { char *a = "123"; - DQN_ASSERT(Dqn_StrToI64(a, Dqn_strlen(a)) == 123); + DQN_ASSERT(Dqn_StrToI64(a, DqnStr_Len(a)) == 123); char *b = "-123"; - DQN_ASSERT(Dqn_StrToI64(b, Dqn_strlen(b)) == -123); + DQN_ASSERT(Dqn_StrToI64(b, DqnStr_Len(b)) == -123); DQN_ASSERT(Dqn_StrToI64(b, 1) == 0); char *c = "-0"; - DQN_ASSERT(Dqn_StrToI64(c, Dqn_strlen(c)) == 0); + DQN_ASSERT(Dqn_StrToI64(c, DqnStr_Len(c)) == 0); char *d = "+123"; - DQN_ASSERT(Dqn_StrToI64(d, Dqn_strlen(d)) == 123); + DQN_ASSERT(Dqn_StrToI64(d, DqnStr_Len(d)) == 123); // TODO(doyle): Unsigned conversion #if 0 char *e = "18446744073709551615"; - DQN_ASSERT((u64)(Dqn_StrToI64(e, Dqn_strlen(e))) == LARGEST_NUM); + DQN_ASSERT((u64)(Dqn_StrToI64(e, DqnStr_Len(e))) == LARGEST_NUM); #endif char *f = "-9223372036854775808"; - DQN_ASSERT(Dqn_StrToI64(f, Dqn_strlen(f)) == SMALLEST_NUM); + DQN_ASSERT(Dqn_StrToI64(f, DqnStr_Len(f)) == SMALLEST_NUM); printf("StringsTest(): StrToI64: Completed successfully\n"); } @@ -301,25 +301,25 @@ void StringsTest() { char a[DQN_64BIT_NUM_MAX_STR_SIZE] = {}; Dqn_I64ToStr(+100, a, DQN_ARRAY_COUNT(a)); - DQN_ASSERT(Dqn_strcmp(a, "100") == 0); + DQN_ASSERT(DqnStr_Cmp(a, "100") == 0); char b[DQN_64BIT_NUM_MAX_STR_SIZE] = {}; Dqn_I64ToStr(-100, b, DQN_ARRAY_COUNT(b)); - DQN_ASSERT(Dqn_strcmp(b, "-100") == 0); + DQN_ASSERT(DqnStr_Cmp(b, "-100") == 0); char c[DQN_64BIT_NUM_MAX_STR_SIZE] = {}; Dqn_I64ToStr(0, c, DQN_ARRAY_COUNT(c)); - DQN_ASSERT(Dqn_strcmp(c, "0") == 0); + DQN_ASSERT(DqnStr_Cmp(c, "0") == 0); #if 0 char d[DQN_64BIT_NUM_MAX_STR_SIZE] = {}; Dqn_I64ToStr(LARGEST_NUM, d, DQN_ARRAY_COUNT(d)); - DQN_ASSERT(Dqn_strcmp(d, "18446744073709551615") == 0); + DQN_ASSERT(DqnStr_Cmp(d, "18446744073709551615") == 0); #endif char e[DQN_64BIT_NUM_MAX_STR_SIZE] = {}; Dqn_I64ToStr(SMALLEST_NUM, e, DQN_ARRAY_COUNT(e)); - DQN_ASSERT(Dqn_strcmp(e, "-9223372036854775808") == 0); + DQN_ASSERT(DqnStr_Cmp(e, "-9223372036854775808") == 0); printf("StringsTest(): I64ToStr: Completed successfully\n"); } @@ -408,26 +408,26 @@ void StringsTest() { char *a = "Microsoft"; char *b = "icro"; - i32 lenA = Dqn_strlen(a); - i32 lenB = Dqn_strlen(b); - DQN_ASSERT(Dqn_StrHasSubstring(a, lenA, b, lenB) == true); - DQN_ASSERT(Dqn_StrHasSubstring(a, lenA, "iro", - Dqn_strlen("iro")) == false); - DQN_ASSERT(Dqn_StrHasSubstring(b, lenB, a, lenA) == false); - DQN_ASSERT(Dqn_StrHasSubstring("iro", Dqn_strlen("iro"), a, + i32 lenA = DqnStr_Len(a); + i32 lenB = DqnStr_Len(b); + DQN_ASSERT(DqnStr_HasSubstring(a, lenA, b, lenB) == true); + DQN_ASSERT(DqnStr_HasSubstring(a, lenA, "iro", + DqnStr_Len("iro")) == false); + DQN_ASSERT(DqnStr_HasSubstring(b, lenB, a, lenA) == false); + DQN_ASSERT(DqnStr_HasSubstring("iro", DqnStr_Len("iro"), a, lenA) == false); - DQN_ASSERT(Dqn_StrHasSubstring("", 0, "iro", 4) == false); - DQN_ASSERT(Dqn_StrHasSubstring("", 0, "", 0) == false); - DQN_ASSERT(Dqn_StrHasSubstring(NULL, 0, NULL, 0) == false); + DQN_ASSERT(DqnStr_HasSubstring("", 0, "iro", 4) == false); + DQN_ASSERT(DqnStr_HasSubstring("", 0, "", 0) == false); + DQN_ASSERT(DqnStr_HasSubstring(NULL, 0, NULL, 0) == false); } { char *a = "Micro"; char *b = "irob"; - i32 lenA = Dqn_strlen(a); - i32 lenB = Dqn_strlen(b); - DQN_ASSERT(Dqn_StrHasSubstring(a, lenA, b, lenB) == false); - DQN_ASSERT(Dqn_StrHasSubstring(b, lenB, a, lenA) == false); + i32 lenA = DqnStr_Len(a); + i32 lenB = DqnStr_Len(b); + DQN_ASSERT(DqnStr_HasSubstring(a, lenA, b, lenB) == false); + DQN_ASSERT(DqnStr_HasSubstring(b, lenB, a, lenA) == false); } printf("StringsTest(): StrHasSubstring: Completed successfully\n"); @@ -532,7 +532,7 @@ void RandomTest() { i32 min = -100; i32 max = 100000; i32 result = DqnRnd_PCGRange(&pcg, min, max); - DQN_ASSERT(result >= min && result <= max) + DQN_ASSERT(result >= min && result <= max); f32 randF32 = DqnRnd_PCGNextf(&pcg); DQN_ASSERT(randF32 >= 0.0f && randF32 <= 1.0f); @@ -1182,7 +1182,9 @@ void MemStackTest() DqnMemStackBlock *blockB = stack.block; // Check temp regions work - DqnTempMemStack tempBuffer = DqnMemStack_BeginTempRegion(&stack); + DqnMemStackTempRegion tempBuffer; + DQN_ASSERT(DqnMemStackTempRegion_Begin(&tempBuffer, &stack)); + size_t sizeC = 1024 + 1; void *resultC = DqnMemStack_Push(tempBuffer.stack, sizeC); u64 resultAddrC = *((u64 *)resultC); @@ -1190,7 +1192,7 @@ void MemStackTest() DQN_ASSERT(stack.block != blockB && stack.block != blockA); DQN_ASSERT(stack.block->used >= sizeC + 0 && stack.block->used <= sizeC + 3); - DQN_ASSERT(stack.tempStackCount == 1); + DQN_ASSERT(stack.tempRegionCount == 1); DQN_ASSERT(stack.byteAlign == ALIGNMENT); // NOTE: Allocation should be aligned to 4 byte boundary @@ -1212,12 +1214,12 @@ void MemStackTest() DQN_ASSERT(ptrC[i] == 3); // End temp region which should revert back to 2 linked stacks, A and B - DqnMemStack_EndTempRegion(tempBuffer); + DqnMemStackTempRegion_End(tempBuffer); DQN_ASSERT(stack.block && stack.block->memory); DQN_ASSERT(stack.block->size == sizeB); DQN_ASSERT(stack.block->used >= sizeB + 0 && stack.block->used <= sizeB + 3); - DQN_ASSERT(stack.tempStackCount == 0); + DQN_ASSERT(stack.tempRegionCount == 0); DQN_ASSERT(resultB); DQN_ASSERT(stack.block->prevBlock == blockA); @@ -1239,7 +1241,7 @@ void MemStackTest() DqnMemStack_FreeLastBlock(&stack); DQN_ASSERT(!stack.block); DQN_ASSERT(stack.byteAlign == ALIGNMENT); - DQN_ASSERT(stack.tempStackCount == 0); + DQN_ASSERT(stack.tempRegionCount == 0); } // Test stack with fixed memory does not allocate more @@ -1338,7 +1340,7 @@ void MemStackTest() // General Check stack struct contains the values we expect from // initialisation DQN_ASSERT(stack.flags == 0); - DQN_ASSERT(stack.tempStackCount == 0); + DQN_ASSERT(stack.tempRegionCount == 0); DQN_ASSERT(stack.byteAlign == ALIGNMENT); DQN_ASSERT(stack.block->size == firstBlockSize); @@ -1489,6 +1491,64 @@ void MemStackTest() } } +FILE_SCOPE u32 volatile globalDebugCounter; +FILE_SCOPE bool volatile globalDebugCounterMemoize[2048]; +FILE_SCOPE DqnLock globalJobQueueLock; +FILE_SCOPE void JobQueueDebugCallbackIncrementCounter(DqnJobQueue *const queue, + void *const userData) +{ + DqnLock_Acquire(&globalJobQueueLock); + DQN_ASSERT(!globalDebugCounterMemoize[globalDebugCounter]); + globalDebugCounterMemoize[globalDebugCounter] = true; + globalDebugCounter++; + u32 number = globalDebugCounter; + DqnLock_Release(&globalJobQueueLock); + + printf("JobQueueDebugCallbackIncrementCounter(): Thread %d: Incrementing Number: %d\n", GetCurrentThreadId(), + number); +} + +FILE_SCOPE void JobQueueTest() +{ + size_t requiredSize; + const i32 QUEUE_SIZE = 256; + DqnJobQueue_InitWithMem(NULL, &requiredSize, QUEUE_SIZE, 0); + + DqnMemStack memStack; + DQN_ASSERT_HARD(DqnMemStack_Init(&memStack, requiredSize, true)); + + i32 numThreads, numCores; + DqnWin32_GetNumThreadsAndCores(&numCores, &numThreads); + DQN_ASSERT(numThreads > 0 && numCores > 0); + i32 totalThreads = (numCores - 1) * numThreads; + + void *jobQueueMem = DqnMemStack_Push(&memStack, requiredSize); + DQN_ASSERT_HARD(jobQueueMem); + DqnJobQueue *jobQueue = DqnJobQueue_InitWithMem(jobQueueMem, &requiredSize, + QUEUE_SIZE, totalThreads); + DQN_ASSERT_HARD(jobQueue); + + DQN_ASSERT(DqnLock_Init(&globalJobQueueLock)); + for (i32 i = 0; i < DQN_ARRAY_COUNT(globalDebugCounterMemoize); i++) + { + DqnJob job = {}; + job.callback = JobQueueDebugCallbackIncrementCounter; + while (!DqnJobQueue_AddJob(jobQueue, job)) + { + DqnJobQueue_TryExecuteNextJob(jobQueue); + } + } + + while (DqnJobQueue_TryExecuteNextJob(jobQueue)) + ; + + for (i32 i = 0; i < DQN_ARRAY_COUNT(globalDebugCounterMemoize); i++) + DQN_ASSERT(globalDebugCounterMemoize[i]); + + printf("\nJobQueueTest(): Final incremented value: %d\n", globalDebugCounter); + DQN_ASSERT(globalDebugCounter == DQN_ARRAY_COUNT(globalDebugCounterMemoize)); +} + int main(void) { StringsTest(); @@ -1500,6 +1560,7 @@ int main(void) ArrayTest(); FileTest(); MemStackTest(); + JobQueueTest(); printf("\nPress 'Enter' Key to Exit\n"); getchar();