diff --git a/build.bat b/build.bat index ffa3631..96330da 100644 --- a/build.bat +++ b/build.bat @@ -34,7 +34,7 @@ REM wd4100 ignore: unused argument parameters REM wd4201 ignore: nonstandard extension used: nameless struct/union REM wd4189 ignore: local variable is initialised but not referenced -set CompileFlags=-EHa- -GR- -Oi -MT -Z7 -W4 -WX -wd4100 -wd4201 -wd4189 -FC +set CompileFlags=-EHa- -GR- -Oi -MT -Z7 -W4 -WX -wd4100 -wd4201 -wd4189 -FC -O2 REM Include directories set IncludeFlags= diff --git a/dqnt.h b/dqnt.h index 7f9fc66..d4da0ed 100644 --- a/dqnt.h +++ b/dqnt.h @@ -42,12 +42,58 @@ typedef float f32; #define DQNT_PI 3.14159265359f #define DQNT_ABS(x) (((x) < 0) ? (-(x)) : (x)) -#define DQNT_DEGREES_TO_RADIANS(x) ((x * (MATH_PI / 180.0f))) -#define DQNT_RADIANS_TO_DEGREES(x) ((x * (180.0f / MATH_PI))) +#define DQNT_DEGREES_TO_RADIANS(x) ((x * (DQNT_PI / 180.0f))) +#define DQNT_RADIANS_TO_DEGREES(x) ((x * (180.0f / DQNT_PI))) #define DQNT_MAX(a, b) ((a) < (b) ? (b) : (a)) #define DQNT_MIN(a, b) ((a) < (b) ? (a) : (b)) #define DQNT_SQUARED(x) ((x) * (x)) +//////////////////////////////////////////////////////////////////////////////// +// DArray - Dynamic Array +//////////////////////////////////////////////////////////////////////////////// +// The DArray stores metadata in the header and returns you a pointer straight +// to the data, to allow direct read/modify access. Adding elements should be +// done using the provided functions since it manages internal state. + +/* + Example Usage: + + uint32_t *uintArray = DQNT_DARRAY_INIT(uint32_t, 16); + uint32_t numberA = 48; + uint32_t numberB = 52; + DQNT_DARRAY_PUSH(&uintArray, &numberA); + DQNT_DARRAY_PUSH(&uintArray, &numberB); + + for (uint32_t i = 0; i < dqnt_darray_get_num_items(uintArray); i++) + { + printf(%d\n", uintArray[i]); + } + + dqnt_darray_free(uintArray); + */ + +// The init macro RETURNS a pointer to your type, you can index this as normal +// with array notation []. +// u32 type - The data type to initiate a dynamic array with +// u32 startingCapacity - Initial number of available slots +#define DQNT_DARRAY_INIT(type, startingCapacity) \ + (type *)dqnt_darray_init_internal(sizeof(type), startingCapacity) + +// Pass in the pointer returned by DQNT_DARRAY_INIT. If the pointer is not +// a valid DArray pointer, this will return 0. +DQNT_FILE_SCOPE u32 dqnt_darray_get_capacity(void *array); +DQNT_FILE_SCOPE u32 dqnt_darray_get_num_items(void *array); + +// void **array - the address of the pointer returned by DQNT_DARRAY_INIT +// void *item - a pointer to the object to insert +// The push macro RETURNS true/false if the push was successful or not. +#define DQNT_DARRAY_PUSH(array, item) \ + dqnt_darray_push_internal((void **)array, (void *)item, sizeof(*item)) + +// Pass in the pointer returned by DQNT_DARRAY_INIT. Returns if the free was +// successful. This will return false if the array is not a valid DArray and +// won't touch the pointer. +DQNT_FILE_SCOPE bool dqnt_darray_free(void *array); //////////////////////////////////////////////////////////////////////////////// // Math //////////////////////////////////////////////////////////////////////////////// @@ -214,7 +260,7 @@ typedef struct DqntRandPCGState u64 state[2]; } DqntRandPCGState; -// You can manually specify a seed by calling init_with_seed, otherwise it + // automatically creates a seed using rdtsc. The generator is not valid until // it's been seeded. DQNT_FILE_SCOPE void dqnt_rnd_pcg_init_with_seed(DqntRandPCGState *pcg, u32 seed); @@ -244,6 +290,122 @@ DQNT_FILE_SCOPE i32 dqnt_rnd_pcg_range(DqntRandPCGState *pcg, i32 min, i32 max) #define WIN32_LEAN_AND_MEAN #endif +//////////////////////////////////////////////////////////////////////////////// +// DArray - Dynamic Array +//////////////////////////////////////////////////////////////////////////////// +#define DQNT_DARRAY_SIGNATURE_INTERNAL 0xAC83DB81 +typedef struct DqntDArrayInternal +{ + u32 index; + u32 itemSize; + u32 capacity; + u32 signature; + + void *data; +} DqntDArrayInternal; + +FILE_SCOPE void *dqnt_darray_init_internal(u32 itemSize, u32 startingCapacity) +{ + if (startingCapacity <= 0 || itemSize == 0) return NULL; + + u32 metadataSize = sizeof(DqntDArrayInternal); + u32 storageSize = itemSize * startingCapacity; + + void *memory = calloc(1, metadataSize + storageSize); + if (!memory) return NULL; + + DqntDArrayInternal *array = (DqntDArrayInternal *)memory; + array->signature = DQNT_DARRAY_SIGNATURE_INTERNAL; + array->itemSize = itemSize; + array->capacity = startingCapacity; + array->data = (u8 *)memory + metadataSize; + + return array->data; +} + +FILE_SCOPE DqntDArrayInternal *dqnt_darray_get_header_internal(void *array) +{ + if (!array) return NULL; + + DqntDArrayInternal *result = (DqntDArrayInternal *)((u8 *)array - sizeof(DqntDArrayInternal)); + if (result->signature != DQNT_DARRAY_SIGNATURE_INTERNAL) return 0; + + return result; +} + +DQNT_FILE_SCOPE u32 dqnt_darray_get_capacity(void *array) +{ + DqntDArrayInternal *header = dqnt_darray_get_header_internal(array); + if (!header) return 0; + return header->capacity; +} + +DQNT_FILE_SCOPE u32 dqnt_darray_get_num_items(void *array) +{ + DqntDArrayInternal *header = dqnt_darray_get_header_internal(array); + if (!header) return 0; + return header->index; +} + +bool dqnt_darray_push_internal(void **array, void *element, u32 itemSize) +{ + if (!element || !array) return false; + + DqntDArrayInternal *header = dqnt_darray_get_header_internal(*array); + if (!header || header->itemSize != itemSize) return false; + + if (header->index >= header->capacity) + { + const f32 GROWTH_FACTOR = 1.2f; + u32 newCapacity = (i32)(header->capacity * GROWTH_FACTOR); + if (newCapacity == header->capacity) newCapacity++; + + u32 metadataSize = sizeof(DqntDArrayInternal); + u32 storageSize = header->itemSize * newCapacity; + void *newMem = realloc(header, metadataSize + storageSize); + if (newMem) + { + header = (DqntDArrayInternal *)newMem; + header->capacity = newCapacity; + header->data = (u8 *)newMem + metadataSize; + *array = header->data; + } + else + { + return false; + } + } + + u32 arrayOffset = header->itemSize * header->index++; + DQNT_ASSERT(header->index <= header->capacity); + u8 *dataPtr = (u8 *)header->data; + + void *dest = (void *)&dataPtr[arrayOffset]; + void *src = element; + memcpy(dest, src, header->itemSize); + + return true; +} + +DQNT_FILE_SCOPE inline bool dqnt_darray_free(void *array) +{ + DqntDArrayInternal *header = dqnt_darray_get_header_internal(array); + if (header) + { + header->index = 0; + header->itemSize = 0; + header->capacity = 0; + header->signature = 0; + + free(header); + return true; + } + else + { + return false; + } +} + //////////////////////////////////////////////////////////////////////////////// // Math //////////////////////////////////////////////////////////////////////////////// @@ -1053,29 +1215,60 @@ DQNT_FILE_SCOPE u32 dqnt_utf8_to_ucs(u32 *dest, u32 character) // File Operations //////////////////////////////////////////////////////////////////////////////// #ifdef DQNT_WIN32 -#define dqnt_win32_error_box(text, title) MessageBoxA(NULL, text, title, MB_OK); + #define DQNT_WIN32_ERROR_BOX(text, title) MessageBoxA(NULL, text, title, MB_OK); + +FILE_SCOPE bool dqnt_win32_utf8_to_wchar_internal(char *in, wchar_t *out, + i32 outLen) +{ + u32 result = MultiByteToWideChar(CP_UTF8, 0, in, -1, out, outLen-1); + + if (result == 0xFFFD || 0) + { + DQNT_WIN32_ERROR_BOX("WideCharToMultiByte() failed.", NULL); + return false; + } + + return true; +} + +FILE_SCOPE bool dqnt_win32_wchar_to_utf8_internal(wchar_t *in, char *out, + i32 outLen) +{ + u32 result = + WideCharToMultiByte(CP_UTF8, 0, in, -1, out, outLen, NULL, NULL); + + if (result == 0xFFFD || 0) + { + DQNT_WIN32_ERROR_BOX("WideCharToMultiByte() failed.", NULL); + return false; + } + + return true; +} #endif -DQNT_FILE_SCOPE bool dqnt_file_open(char *const filePath, DqntFile *file) +DQNT_FILE_SCOPE bool dqnt_file_open(char *const path, DqntFile *file) { - if (!file || !filePath) return false; + if (!file || !path) return false; #ifdef DQNT_WIN32 wchar_t widePath[MAX_PATH] = {}; - MultiByteToWideChar(CP_UTF8, 0, filePath, -1, widePath, MAX_PATH - 1); + dqnt_win32_utf8_to_wchar_internal(path, widePath, + DQNT_ARRAY_COUNT(widePath)); + HANDLE handle = CreateFileW(widePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (handle == INVALID_HANDLE_VALUE) { - dqnt_win32_error_box("CreateFile() failed.", NULL); + DQNT_WIN32_ERROR_BOX("CreateFile() failed.", NULL); return false; } LARGE_INTEGER size; if (GetFileSizeEx(handle, &size) == 0) { - dqnt_win32_error_box("GetFileSizeEx() failed.", NULL); + DQNT_WIN32_ERROR_BOX("GetFileSizeEx() failed.", NULL); return false; } @@ -1104,7 +1297,7 @@ DQNT_FILE_SCOPE u32 dqnt_file_read(DqntFile file, u8 *buffer, u32 numBytesToRead // TODO(doyle): 0 also means it is completing async, but still valid if (result == 0) { - dqnt_win32_error_box("ReadFile() failed.", NULL); + DQNT_WIN32_ERROR_BOX("ReadFile() failed.", NULL); } numBytesRead = (u32)bytesRead; @@ -1126,6 +1319,80 @@ DQNT_FILE_SCOPE inline void dqnt_file_close(DqntFile *file) #endif } +#include "stdio.h" +DQNT_FILE_SCOPE char **dqnt_dir_read(char *dir, u32 *numFiles) +{ + if (!dir) return NULL; +#ifdef DQNT_WIN32 + + u32 currNumFiles = 0; + wchar_t wideDir[MAX_PATH] = {}; + dqnt_win32_utf8_to_wchar_internal(dir, wideDir, DQNT_ARRAY_COUNT(wideDir)); + + // Enumerate number of files first + { + WIN32_FIND_DATAW findData = {}; + HANDLE findHandle = FindFirstFileW(wideDir, &findData); + if (findHandle == INVALID_HANDLE_VALUE) + { + DQNT_WIN32_ERROR_BOX("FindFirstFile() failed.", NULL); + return NULL; + } + + while (FindNextFileW(findHandle, &findData) != 0) + currNumFiles++; + FindClose(findHandle); + } + + if (currNumFiles == 0) return NULL; + + { + WIN32_FIND_DATAW initFind = {}; + HANDLE findHandle = FindFirstFileW(wideDir, &initFind); + if (findHandle == INVALID_HANDLE_VALUE) + { + DQNT_WIN32_ERROR_BOX("FindFirstFile() failed.", NULL); + return NULL; + } + + + char **list = (char **)calloc(1, sizeof(*list) * (currNumFiles)); + if (!list) + { + DQNT_WIN32_ERROR_BOX("calloc() failed.", NULL); + return NULL; + } + + for (u32 i = 0; i < currNumFiles; i++) + { + list[i] = (char *)calloc(1, sizeof(**list) * MAX_PATH); + if (!list[i]) + { + for (u32 j = 0; j < i; j++) + { + free(list[j]); + } + + DQNT_WIN32_ERROR_BOX("calloc() failed.", NULL); + return NULL; + } + } + + i32 listIndex = 0; + WIN32_FIND_DATAW findData = {}; + while (FindNextFileW(findHandle, &findData) != 0) + { + dqnt_win32_wchar_to_utf8_internal( + findData.cFileName, list[listIndex++], MAX_PATH); + } + + *numFiles = currNumFiles; + FindClose(findHandle); + + return list; + } +#endif +} //////////////////////////////////////////////////////////////////////////////// // Timer diff --git a/dqnt_unit_test.cpp b/dqnt_unit_test.cpp index 333ffaf..4ffaae7 100644 --- a/dqnt_unit_test.cpp +++ b/dqnt_unit_test.cpp @@ -512,18 +512,135 @@ void dqnt_vec_test() printf("dqnt_vec_test(): Completed successfully\n"); } +void dqnt_darray_test() +{ + { + DqntV2 *vecDArray = DQNT_DARRAY_INIT(DqntV2, 1); + DQNT_ASSERT(vecDArray); + DQNT_ASSERT(dqnt_darray_get_capacity(vecDArray) == 1); + DQNT_ASSERT(dqnt_darray_get_num_items(vecDArray) == 0); + + // Test basic insert + { + DqntV2 va = dqnt_v2(5, 10); + DQNT_ASSERT(DQNT_DARRAY_PUSH(&vecDArray, &va)); + + DqntV2 vb = vecDArray[0]; + DQNT_ASSERT(dqnt_v2_equals(va, vb)); + + DQNT_ASSERT(dqnt_darray_get_capacity(vecDArray) == 1); + DQNT_ASSERT(dqnt_darray_get_num_items(vecDArray) == 1); + + DqntV2 *empty = NULL; + DQNT_ASSERT(DQNT_DARRAY_PUSH(NULL, empty) == false); + DQNT_ASSERT(DQNT_DARRAY_PUSH(NULL, &va) == false); + DQNT_ASSERT(DQNT_DARRAY_PUSH(&vecDArray, empty) == false); + } + + // Test array resizing and freeing + { + DqntV2 va = dqnt_v2(10, 15); + DQNT_DARRAY_PUSH(&vecDArray, &va); + + DqntV2 vb = vecDArray[0]; + DQNT_ASSERT(dqnt_v2_equals(va, vb) == false); + + vb = vecDArray[1]; + DQNT_ASSERT(dqnt_v2_equals(va, vb) == true); + + DQNT_ASSERT(dqnt_darray_get_capacity(vecDArray) == 2); + DQNT_ASSERT(dqnt_darray_get_num_items(vecDArray) == 2); + + DQNT_DARRAY_PUSH(&vecDArray, &va); + DQNT_ASSERT(dqnt_darray_get_capacity(vecDArray) == 3); + DQNT_ASSERT(dqnt_darray_get_num_items(vecDArray) == 3); + + DQNT_DARRAY_PUSH(&vecDArray, &va); + DQNT_ASSERT(dqnt_darray_get_capacity(vecDArray) == 4); + DQNT_ASSERT(dqnt_darray_get_num_items(vecDArray) == 4); + + DQNT_DARRAY_PUSH(&vecDArray, &va); + DQNT_ASSERT(dqnt_darray_get_capacity(vecDArray) == 5); + DQNT_ASSERT(dqnt_darray_get_num_items(vecDArray) == 5); + + DQNT_DARRAY_PUSH(&vecDArray, &va); + DQNT_ASSERT(dqnt_darray_get_capacity(vecDArray) == 6); + DQNT_ASSERT(dqnt_darray_get_num_items(vecDArray) == 6); + + DQNT_DARRAY_PUSH(&vecDArray, &va); + DQNT_ASSERT(dqnt_darray_get_capacity(vecDArray) == 7); + DQNT_ASSERT(dqnt_darray_get_num_items(vecDArray) == 7); + + DQNT_DARRAY_PUSH(&vecDArray, &va); + DQNT_ASSERT(dqnt_darray_get_capacity(vecDArray) == 8); + DQNT_ASSERT(dqnt_darray_get_num_items(vecDArray) == 8); + + DQNT_DARRAY_PUSH(&vecDArray, &va); + DQNT_ASSERT(dqnt_darray_get_capacity(vecDArray) == 9); + DQNT_ASSERT(dqnt_darray_get_num_items(vecDArray) == 9); + + DQNT_DARRAY_PUSH(&vecDArray, &va); + DQNT_ASSERT(dqnt_darray_get_capacity(vecDArray) == 10); + DQNT_ASSERT(dqnt_darray_get_num_items(vecDArray) == 10); + + DQNT_DARRAY_PUSH(&vecDArray, &va); + DQNT_ASSERT(dqnt_darray_get_capacity(vecDArray) == 12); + DQNT_ASSERT(dqnt_darray_get_num_items(vecDArray) == 11); + + DqntV2 vc = dqnt_v2(90, 100); + DQNT_DARRAY_PUSH(&vecDArray, &vc); + DQNT_ASSERT(dqnt_darray_get_capacity(vecDArray) == 12); + DQNT_ASSERT(dqnt_darray_get_num_items(vecDArray) == 12); + DQNT_ASSERT(dqnt_v2_equals(vc, vecDArray[11])); + + DQNT_ASSERT(dqnt_darray_free(vecDArray) == true); + } + } + + { + f32 *array = DQNT_DARRAY_INIT(f32, 1); + DQNT_ASSERT(array); + DQNT_ASSERT(dqnt_darray_get_capacity(array) == 1); + DQNT_ASSERT(dqnt_darray_get_num_items(array) == 0); + + f32 *empty = NULL; + DQNT_ASSERT(DQNT_DARRAY_PUSH(NULL, empty) == false); + DQNT_ASSERT(DQNT_DARRAY_PUSH(&array, empty) == false); + } + + printf("dqnt_darray_test(): Completed successfully\n"); +} + void dqnt_file_test() { - DqntFile file = {}; - DQNT_ASSERT(dqnt_file_open(".clang-format", &file)); - DQNT_ASSERT(file.size == 1320); + // File i/o + { + DqntFile file = {}; + DQNT_ASSERT(dqnt_file_open(".clang-format", &file)); + DQNT_ASSERT(file.size == 1320); - u8 *buffer = (u8 *)calloc(1, file.size * sizeof(u8)); - DQNT_ASSERT(dqnt_file_read(file, buffer, (u32)file.size) == file.size); - free(buffer); + u8 *buffer = (u8 *)calloc(1, (size_t)file.size * sizeof(u8)); + DQNT_ASSERT(dqnt_file_read(file, buffer, (u32)file.size) == file.size); + free(buffer); - dqnt_file_close(&file); - DQNT_ASSERT(!file.handle && file.size == 0); + dqnt_file_close(&file); + DQNT_ASSERT(!file.handle && file.size == 0); + + printf("dqnt_file_test(): file_io: Completed successfully\n"); + } + + { + u32 numFiles; + char **filelist = dqnt_dir_read("*", &numFiles); + printf("dqnt_file_test(): dir_read: Display read files\n"); + + for (u32 i = 0; i < numFiles; i++) + printf("dqnt_file_test(): dir_read: %s\n", filelist[i]); + + printf("dqnt_file_test(): dir_read: Completed successfully\n"); + } + + printf("dqnt_file_test(): Completed successfully\n"); } int main(void) @@ -533,6 +650,7 @@ int main(void) dqnt_math_test(); dqnt_vec_test(); dqnt_other_test(); + dqnt_darray_test(); dqnt_file_test(); printf("\nPress 'Enter' Key to Exit\n");