From f33209447e3452babbf15b61bf856b39ba50951c Mon Sep 17 00:00:00 2001 From: Jonathan Hoffstadt Date: Mon, 26 Aug 2024 20:21:28 -0500 Subject: [PATCH] initial commit --- .github/workflows/tests.yml | 90 ++ .gitignore | 9 + pl_ds.h | 637 +++++++++++ pl_json.h | 2006 +++++++++++++++++++++++++++++++++++ pl_log.h | 1262 ++++++++++++++++++++++ pl_math.h | 798 ++++++++++++++ pl_memory.h | 743 +++++++++++++ pl_profile.h | 512 +++++++++ pl_stl.h | 335 ++++++ pl_string.h | 420 ++++++++ pl_test.h | 429 ++++++++ tests/build_linux_tests.sh | 74 ++ tests/build_mac_tests.sh | 98 ++ tests/build_win_tests.bat | 70 ++ tests/main_tests.c | 26 + tests/pl_ds_tests.h | 102 ++ tests/pl_json_tests.h | 171 +++ 17 files changed, 7782 insertions(+) create mode 100644 .github/workflows/tests.yml create mode 100644 .gitignore create mode 100644 pl_ds.h create mode 100644 pl_json.h create mode 100644 pl_log.h create mode 100644 pl_math.h create mode 100644 pl_memory.h create mode 100644 pl_profile.h create mode 100644 pl_stl.h create mode 100644 pl_string.h create mode 100644 pl_test.h create mode 100644 tests/build_linux_tests.sh create mode 100644 tests/build_mac_tests.sh create mode 100644 tests/build_win_tests.bat create mode 100644 tests/main_tests.c create mode 100644 tests/pl_ds_tests.h create mode 100644 tests/pl_json_tests.h diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..102b020 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,90 @@ +name: Tests + +on: + + push: + + branches: + - master + - dev + - feature/* + - dev/* + - fix/* + + pull_request: + + workflow_dispatch: + +jobs: + + # Windows: + + # runs-on: windows-2019 + + # steps: + + # - name: Checkout + # uses: actions/checkout@v3 + + # - name: Run Tests + # shell: cmd + # run: | + # cd $GITHUB_WORKSPACE + # cd tests + # call build_win_tests.bat -c debug + # cd .. + # cd out + # pilot_light_test.exe + # @set PL_TEST_STATUS=%ERRORLEVEL% + # @if %PL_TEST_STATUS% NEQ 0 (exit 1) + + # MacOS: + + # runs-on: MacOS-latest + + # steps: + + # - name: Checkout + # uses: actions/checkout@v3 + + # - name: Run Tests + # run: | + # cd $GITHUB_WORKSPACE + # cd tests + # chmod +x build_mac_tests.sh + # ./build_mac_tests.sh -c debug + # cd .. + # cd out + # ./pilot_light_test + # ((exit 1) || if [ $? = 1 ]; then exit 0; else exit 1; fi) + + Ubuntu: + + runs-on: ubuntu-20.04 + + steps: + + - name: Checkout + uses: actions/checkout@v3 + + - name: Install Dependencies + run: | + sudo apt update + sudo apt install libx11-dev -y + sudo apt install libxkbcommon-x11-dev -y + sudo apt install libx11-xcb-dev -y + sudo apt install libxcb-xfixes0-dev -y + sudo apt install libxcb-cursor-dev -y + sudo apt install libxcb-cursor0 -y + sudo apt install libxcb-keysyms1-dev -y + + - name: Run Tests + run: | + cd $GITHUB_WORKSPACE + cd tests + chmod +x build_linux_tests.sh + ./build_linux_tests.sh + cd .. + cd out + ./pilot_light_test + ((exit 1) || if [ $? = 1 ]; then exit 0; else exit 1; fi) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bcc1833 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ + +# directories +.vs/ +.vscode/ +.idea/ +out/ + +# debug files +tests/*.pdb \ No newline at end of file diff --git a/pl_ds.h b/pl_ds.h new file mode 100644 index 0000000..a5184ad --- /dev/null +++ b/pl_ds.h @@ -0,0 +1,637 @@ +/* + pl_ds.h + * data structures +*/ + +// library version +#define PL_DS_VERSION "0.5.2" +#define PL_DS_VERSION_NUM 00502 + +/* +Index of this file: +// [SECTION] documentation +// [SECTION] header mess +// [SECTION] includes +// [SECTION] forward declarations +// [SECTION] public api (stretchy buffer) +// [SECTION] public api (hashmap) +// [SECTION] internal +// [SECTION] public api implementation (hashmap) +*/ + +//----------------------------------------------------------------------------- +// [SECTION] documentation +//----------------------------------------------------------------------------- + +/* + +STRETCHY BUFFER (dynamic array) + + pl_sb_capacity: + uint32_t pl_sb_capacity(T*); + Returns capacity (max number of items with current allocation) + + pl_sb_size: + uint32_t pl_sb_size(T*); + Returns number of items in buffer + + pl_sb_reserve: + void pl_sb_reserve(T*, n); + Reserves enough memory for n items + + pl_sb_resize: + void pl_sb_resize(T*, n); + Changes size of buffer to n items. + + pl_sb_pop: + T pl_sb_pop(T*); + Returns last item added to buffer and shrinks the buffer by 1(ensure buffer isn't empty!) + + pl_sb_pop_n: + void pl_sb_pop_n(T*, uint32_t n); + Pops the last n items from the buffer (ensure buffer isn't empty!) + + pl_sb_top: + T pl_sb_top(T*); + Returns last item added to buffer(ensure buffer isn't empty!) + + pl_sb_back: + T pl_sb_back(T*); + Returns last item added to buffer(ensure buffer isn't empty!) + + pl_sb_free: + void pl_sb_free(T*); + Frees memory held by buffer and sets pointer to NULL + + pl_sb_reset: + void pl_sb_reset(T*) + Sets size of buffer back to zero without freeing any memory + + pl_sb_end: + T* pl_sb_end(T*); + Returns a pointer to the end of the buffer (after the last item!) + + pl_sb_add: + uint32_t pl_sb_add(T*); + Adds room for 1 more item and returns the index to that item + + pl_sb_add_n: + uint32_t pl_sb_add_(T*, n); + Adds room for n more item and returns the index to the first new item + + pl_sb_add_ptr: + T* pl_sb_add_ptr(T*); + Adds room for 1 more item and returns the pointer to it + + pl_sb_add_ptr_n: + T* pl_sb_add_ptr_n(T*, n); + Adds room for n more item and returns the pointer to the first new item + + pl_sb_push: + T pl_sb_push(T*, T); + Pushes an item into the buffer and returns a copy of it. + + pl_sb_del: + void pl_sb_del(T*, i); + Deletes the ith item from the buffer (uses memmove) + + pl_sb_del_n: + void pl_sb_del_n(T*, i, n); + Deletes n items starting at the ith index (uses memmove) + + pl_sb_del_swap: + void pl_sb_del_swap(T*, i); + Deletes the ith item from the buffer (swaps with last item, so faster but doesn't preserve order) + + pl_sb_insert: + void pl_sb_insert(T*, i, T); + Inserts new item v at the ith index (uses memmove) + + pl_sb_insert_n: + void pl_sb_insert_n(T*, i, N); + Inserts n new items starting at the ith index (uses memmove) + + pl_sb_sprintf: + void pl_sb_sprintf(char*, pcFormat, ...); + Inserts characters into a char stretchy buffer (similar to sprintf) + +HASHMAPS + + pl_hm_hash_str: + uint64_t pl_hm_hash_str(const char*); + Returns the CRC64 hash of a string. + + pl_hm_hash: + uint64_t pl_hm_hash(const void* pData, size_t szDataSize, uint64_t uSeed); + Returns the CRC64 hash of some arbitrary data. + + pl__hm_resize: + void pl__hm_resize(plHashMap*, uint32_t); + Resizes the hashmap or frees it if zero is used. + + pl_hm_free: + void pl_hm_free(plHashMap*); + Frees the hashmap internal memory. + + pl__hm_insert: + void pl__hm_insert(plHashMap*, uint64_t ulKey, uint64_t ulValue); + Adds an entry to the hashmap where ulKey is a hashed key (usually a string) and + ulValue is the index into the value array. + + pl_hm_remove: + void pl_hm_remove(plHashMap*, uint64_t ulKey); + Removes an entry from the hashmap and adds the index to the free index list. + + pl_hm_lookup: + uint64_t pl_hm_lookup(plHashMap*, uint64_t ulKey); + Returns the index into the value array if it already exists or UINT64_MAX if not. + + pl_hm_get_free_index: + uint64_t pl_hm_get_free_index(plHashMap*); + Returns a free index if one exists or UINT64_MAX if not. + + pl_hm_has_key: + bool pl_hm_has_key(plHashMap*, uint64_t); + Checks if key exists. + + pl_hm_has_key_str: + bool pl_hm_has_key(plHashMap*, const char*); + Same as pl_hm_has_key but performs the hash for you. + + pl__hm_insert_str: + void pl__hm_insert_str(plHashMap*, const char* pcKey, uint64_t ulValue); + Same as pl__hm_insert but performs the hash for you. + + pl_hm_lookup_str: + uint64_t pl_hm_lookup_str(plHashMap*, const char* pcKey); + Same as pl_hm_lookup but performs the hash for you. + + pl_hm_remove_str: + void pl_hm_remove_str(plHashMap*, const char* pcKey); + Same as pl_hm_remove but performs the hash for you. + +COMPILE TIME OPTIONS + + * Change allocators by defining both: + PL_LOG_ALLOC(x) + PL_LOG_FREE(x) + * Change initial hashmap size: + PL_DS_HASHMAP_INITIAL_SIZE (default is 256) +*/ + +//----------------------------------------------------------------------------- +// [SECTION] header mess +//----------------------------------------------------------------------------- + +#ifndef PL_DS_H +#define PL_DS_H + +#if defined(PL_DS_ALLOC) && defined(PL_DS_FREE) && defined(PL_DS_ALLOC_INDIRECT) +// ok +#elif !defined(PL_DS_ALLOC) && !defined(PL_DS_FREE) && !defined(PL_DS_ALLOC_INDIRECT) +// ok +#else +#error "Must define all or none of PL_DS_ALLOC and PL_DS_FREE and PL_DS_ALLOC_INDIRECT" +#endif + +#ifndef PL_DS_ALLOC + #include + #define PL_DS_ALLOC(x) malloc((x)) + #define PL_DS_ALLOC_INDIRECT(x, FILE, LINE) malloc((x)) + #define PL_DS_FREE(x) free((x)) +#endif + +#ifndef PL_DS_ASSERT + #include + #define PL_DS_ASSERT(x) assert((x)) +#endif + +#ifndef PL_DS_HASHMAP_INITIAL_SIZE + #define PL_DS_HASHMAP_INITIAL_SIZE 256 +#endif + +//----------------------------------------------------------------------------- +// [SECTION] includes +//----------------------------------------------------------------------------- + +#include // uint32_t +#include // malloc, free +#include // memset, memmove +#include // bool +#include // arg vars +#include // vsprintf + +//----------------------------------------------------------------------------- +// [SECTION] documentation +//----------------------------------------------------------------------------- + +typedef struct _plHashMap +{ + uint32_t _uItemCount; + uint32_t _uBucketCount; + uint64_t* _aulKeys; // stored keys used for rehashing during growth + uint64_t* _aulValueIndices; // indices into value array (user held) + uint64_t* _sbulFreeIndices; // free list of available indices +} plHashMap; + +//----------------------------------------------------------------------------- +// [SECTION] public api (stretchy buffer) +//----------------------------------------------------------------------------- + +#define pl_sb_capacity(buf) \ + ((buf) ? pl__sb_header((buf))->uCapacity : 0u) + +#define pl_sb_size(buf) \ + ((buf) ? pl__sb_header((buf))->uSize : 0u) + +#define pl_sb_pop(buf) \ + (buf)[--pl__sb_header((buf))->uSize] + +#define pl_sb_pop_n(buf, n) \ + pl__sb_header((buf))->uSize-=(n) + +#define pl_sb_top(buf) \ + ((buf)[pl__sb_header((buf))->uSize-1]) + +#define pl_sb_last(buf) \ + pl_sb_top((buf)) + +#define pl_sb_free(buf) \ + if((buf)){ PL_DS_FREE(pl__sb_header(buf));} (buf) = NULL; + +#define pl_sb_reset(buf) \ + if((buf)){ pl__sb_header((buf))->uSize = 0u;} + +#define pl_sb_back(buf) \ + pl_sb_top((buf)) + +#define pl_sb_end(buf) \ + ((buf) ? (buf) + pl__sb_header((buf))->uSize : (buf)) + +#define pl_sb_add_n(buf, n) \ + (pl__sb_may_grow((buf), sizeof(*(buf)), (n), (n), __FILE__, __LINE__), (n) ? (pl__sb_header(buf)->uSize += (n), pl__sb_header(buf)->uSize - (n)) : pl_sb_size(buf)) + +#define pl_sb_add(buf) \ + pl_sb_add_n((buf), 1) + +#define pl_sb_add_ptr_n(buf, n) \ + (pl__sb_may_grow((buf), sizeof(*(buf)), (n), (n), __FILE__, __LINE__), (n) ? (pl__sb_header(buf)->uSize += (n), &(buf)[pl__sb_header(buf)->uSize - (n)]) : (buf)) + +#define pl_sb_add_ptr(buf, n) \ + pl_sb_add_ptr_n((buf), 1) + +#define pl_sb_push(buf, v) \ + (pl__sb_may_grow((buf), sizeof(*(buf)), 1, 8, __FILE__, __LINE__), (buf)[pl__sb_header((buf))->uSize++] = (v)) + +#define pl_sb_reserve(buf, n) \ + (pl__sb_may_grow((buf), sizeof(*(buf)), (n), (n), __FILE__, __LINE__)) + +#define pl_sb_resize(buf, n) \ + (pl__sb_may_grow((buf), sizeof(*(buf)), (n), (n), __FILE__, __LINE__), pl__sb_header((buf))->uSize = (n)) + +#define pl_sb_del_n(buf, i, n) \ + (memmove(&(buf)[i], &(buf)[(i) + (n)], sizeof *(buf) * (pl__sb_header(buf)->uSize - (n) - (i))), pl__sb_header(buf)->uSize -= (n)) + +#define pl_sb_del(buf, i) \ + pl_sb_del_n((buf), (i), 1) + +#define pl_sb_del_swap(buf, i) \ + ((buf)[i] = pl_sb_last(buf), pl__sb_header(buf)->uSize -= 1) + +#define pl_sb_insert_n(buf, i, n) \ + (pl_sb_add_n((buf), (n)), memmove(&(buf)[(i) + (n)], &(buf)[i], sizeof *(buf) * (pl__sb_header(buf)->uSize - (n) - (i)))) + +#define pl_sb_insert(buf, i, v) \ + (pl_sb_insert_n((buf), (i), 1), (buf)[i] = (v)) + +#define pl_sb_sprintf(buf, pcFormat, ...) \ + pl__sb_sprintf(&(buf), (pcFormat), __VA_ARGS__) + +//----------------------------------------------------------------------------- +// [SECTION] public api (hashmap) +//----------------------------------------------------------------------------- + +#define pl_hm_resize(ptHashMap, uBucketCount) \ + pl__hm_resize(ptHashMap, uBucketCount, __FILE__, __LINE__) + +#define pl_hm_insert(ptHashMap, ulKey, ulValue) \ + pl__hm_insert(ptHashMap, ulKey, ulValue, __FILE__, __LINE__) + +#define pl_hm_insert_str(ptHashMap, pcKey, ulValue) \ + pl__hm_insert_str(ptHashMap, pcKey, ulValue, __FILE__, __LINE__) + +static inline void pl__hm_resize (plHashMap* ptHashMap, uint32_t uBucketCount, const char* pcFile, int iLine); +static inline void pl_hm_free (plHashMap* ptHashMap) { pl__hm_resize(ptHashMap, 0, "", 0);} +static inline void pl__hm_insert (plHashMap* ptHashMap, uint64_t ulKey, uint64_t ulValue, const char* pcFile, int iLine); +static inline void pl_hm_remove (plHashMap* ptHashMap, uint64_t ulKey); +static inline uint64_t pl_hm_lookup (plHashMap* ptHashMap, uint64_t ulKey); +static inline uint64_t pl_hm_get_free_index(plHashMap* ptHashMap); +static inline bool pl_hm_has_key (plHashMap* ptHashMap, uint64_t ulKey); +static inline uint64_t pl_hm_hash_str (const char* pcKey); +static inline uint64_t pl_hm_hash (const void* pData, size_t szDataSize, uint64_t uSeed); +static inline void pl__hm_insert_str (plHashMap* ptHashMap, const char* pcKey, uint64_t ulValue, const char* pcFile, int iLine); +static inline void pl_hm_remove_str (plHashMap* ptHashMap, const char* pcKey); +static inline uint64_t pl_hm_lookup_str (plHashMap* ptHashMap, const char* pcKey); +static inline bool pl_hm_has_key_str (plHashMap* ptHashMap, const char* pcKey); + +//----------------------------------------------------------------------------- +// [SECTION] internal +//----------------------------------------------------------------------------- + +#define pl__sb_header(buf) ((plSbHeader_*)(((char*)(buf)) - sizeof(plSbHeader_))) +#define pl__sb_may_grow(buf, s, n, m, X, Y) pl__sb_may_grow_((void**)&(buf), (s), (n), (m), __FILE__, __LINE__) + +typedef struct +{ + uint32_t uSize; + uint32_t uCapacity; +} plSbHeader_; + +static void +pl__sb_grow(void** ptrBuffer, size_t szElementSize, size_t szNewItems, const char* pcFile, int iLine) +{ + + plSbHeader_* ptOldHeader = pl__sb_header(*ptrBuffer); + + const size_t szNewSize = (ptOldHeader->uCapacity + szNewItems) * szElementSize + sizeof(plSbHeader_); + plSbHeader_* ptNewHeader = (plSbHeader_*)PL_DS_ALLOC_INDIRECT(szNewSize, pcFile, iLine); //-V592 + memset(ptNewHeader, 0, (ptOldHeader->uCapacity + szNewItems) * szElementSize + sizeof(plSbHeader_)); + if(ptNewHeader) + { + ptNewHeader->uSize = ptOldHeader->uSize; + ptNewHeader->uCapacity = ptOldHeader->uCapacity + (uint32_t)szNewItems; + memcpy(&ptNewHeader[1], *ptrBuffer, ptOldHeader->uSize * szElementSize); + PL_DS_FREE(ptOldHeader); + *ptrBuffer = &ptNewHeader[1]; + } +} + +static void +pl__sb_may_grow_(void** ptrBuffer, size_t szElementSize, size_t szNewItems, size_t szMinCapacity, const char* pcFile, int iLine) +{ + if(*ptrBuffer) + { + plSbHeader_* ptOriginalHeader = pl__sb_header(*ptrBuffer); + if(ptOriginalHeader->uSize + szNewItems > ptOriginalHeader->uCapacity) + { + pl__sb_grow(ptrBuffer, szElementSize, szNewItems, pcFile, iLine); + } + } + else // first run + { + const size_t szNewSize = szMinCapacity * szElementSize + sizeof(plSbHeader_); + plSbHeader_* ptHeader = (plSbHeader_*)PL_DS_ALLOC_INDIRECT(szNewSize, pcFile, iLine); + memset(ptHeader, 0, szMinCapacity * szElementSize + sizeof(plSbHeader_)); + if(ptHeader) + { + *ptrBuffer = &ptHeader[1]; + ptHeader->uSize = 0u; + ptHeader->uCapacity = (uint32_t)szMinCapacity; + } + } +} + +static void +pl__sb_vsprintf(char** ppcBuffer, const char* pcFormat, va_list args) +{ + va_list args2; + va_copy(args2, args); + int32_t n = vsnprintf(NULL, 0, pcFormat, args2); + va_end(args2); + uint32_t an = pl_sb_size(*ppcBuffer); + pl_sb_resize(*ppcBuffer, an + n + 1); + vsnprintf(*ppcBuffer + an, n + 1, pcFormat, args); +} + +static void +pl__sb_sprintf(char** ppcBuffer, const char* pcFormat, ...) +{ + va_list args; + va_start(args, pcFormat); + pl__sb_vsprintf(ppcBuffer, pcFormat, args); + va_end(args); +} + +//----------------------------------------------------------------------------- +// [SECTION] public api implementation (hashmap) +//----------------------------------------------------------------------------- + +static inline void +pl__hm_resize(plHashMap* ptHashMap, uint32_t uBucketCount, const char* pcFile, int iLine) +{ + const uint32_t uOldBucketCount = ptHashMap->_uBucketCount; + uint64_t* sbulOldValueIndices = ptHashMap->_aulValueIndices; + uint64_t* aulOldKeys = ptHashMap->_aulKeys; + + ptHashMap->_uBucketCount = uBucketCount < PL_DS_HASHMAP_INITIAL_SIZE ? PL_DS_HASHMAP_INITIAL_SIZE : uBucketCount; + if(uBucketCount > 0) + { + + ptHashMap->_aulValueIndices = (uint64_t*)PL_DS_ALLOC_INDIRECT(sizeof(uint64_t) * ptHashMap->_uBucketCount, pcFile, iLine); + ptHashMap->_aulKeys = (uint64_t*)PL_DS_ALLOC_INDIRECT(sizeof(uint64_t) * ptHashMap->_uBucketCount, pcFile, iLine); + memset(ptHashMap->_aulValueIndices, 0xff, sizeof(uint64_t) * ptHashMap->_uBucketCount); + memset(ptHashMap->_aulKeys, 0xff, sizeof(uint64_t) * ptHashMap->_uBucketCount); + + for(uint32_t i = 0; i < uOldBucketCount; i++) + { + const uint64_t ulKey = aulOldKeys[i]; + uint64_t ulOldModKey = ulKey % uOldBucketCount; + + + while(aulOldKeys[ulOldModKey] != ulKey && aulOldKeys[ulOldModKey] != UINT64_MAX) + ulOldModKey = (ulOldModKey + 1) % uOldBucketCount; + + const uint64_t ulValue = sbulOldValueIndices[ulOldModKey]; + ptHashMap->_uItemCount--; + pl__hm_insert(ptHashMap, ulKey, ulValue, pcFile, iLine); + } + } + else + { + ptHashMap->_aulValueIndices = NULL; + ptHashMap->_aulKeys = NULL; + pl_sb_free(ptHashMap->_sbulFreeIndices); + ptHashMap->_uItemCount = 0; + } + + if(sbulOldValueIndices) + { + PL_DS_FREE(sbulOldValueIndices); + } + if(aulOldKeys) + { + PL_DS_FREE(aulOldKeys); + } +} + +static inline void +pl__hm_insert(plHashMap* ptHashMap, uint64_t ulKey, uint64_t ulValue, const char* pcFile, int iLine) +{ + if(ptHashMap->_uBucketCount == 0) + pl__hm_resize(ptHashMap, PL_DS_HASHMAP_INITIAL_SIZE, pcFile, iLine); + else if(((float)ptHashMap->_uItemCount / (float)ptHashMap->_uBucketCount) > 0.60f) + pl__hm_resize(ptHashMap, ptHashMap->_uBucketCount * 2, pcFile, iLine); + + uint64_t ulModKey = ulKey % ptHashMap->_uBucketCount; + + while(ptHashMap->_aulKeys[ulModKey] != ulKey && ptHashMap->_aulKeys[ulModKey] != UINT64_MAX) + { + ulModKey = (ulModKey + 1) % ptHashMap->_uBucketCount; + if(ptHashMap->_aulKeys[ulModKey] == UINT64_MAX - 1) + break; + } + + ptHashMap->_aulKeys[ulModKey] = ulKey; + ptHashMap->_aulValueIndices[ulModKey] = ulValue; + ptHashMap->_uItemCount++; +} + +static inline void +pl_hm_remove(plHashMap* ptHashMap, uint64_t ulKey) +{ + PL_DS_ASSERT(ptHashMap->_uBucketCount > 0 && "hashmap has no items"); + + uint64_t ulModKey = ulKey % ptHashMap->_uBucketCount; + + while(ptHashMap->_aulKeys[ulModKey] != ulKey && ptHashMap->_aulKeys[ulModKey] != UINT64_MAX) + ulModKey = (ulModKey + 1) % ptHashMap->_uBucketCount; + + const uint64_t ulValue = ptHashMap->_aulValueIndices[ulModKey]; + pl_sb_push(ptHashMap->_sbulFreeIndices, ulValue); + + ptHashMap->_aulValueIndices[ulModKey] = UINT64_MAX; + ptHashMap->_aulKeys[ulModKey] = UINT64_MAX - 1; + ptHashMap->_uItemCount--; +} + +static const uint64_t __gauCrc64LookupTableDS[256] = +{ + 0x0000000000000000ULL, 0x01B0000000000000ULL, 0x0360000000000000ULL, 0x02D0000000000000ULL, 0x06C0000000000000ULL, 0x0770000000000000ULL, 0x05A0000000000000ULL, 0x0410000000000000ULL, + 0x0D80000000000000ULL, 0x0C30000000000000ULL, 0x0EE0000000000000ULL, 0x0F50000000000000ULL, 0x0B40000000000000ULL, 0x0AF0000000000000ULL, 0x0820000000000000ULL, 0x0990000000000000ULL, + 0x1B00000000000000ULL, 0x1AB0000000000000ULL, 0x1860000000000000ULL, 0x19D0000000000000ULL, 0x1DC0000000000000ULL, 0x1C70000000000000ULL, 0x1EA0000000000000ULL, 0x1F10000000000000ULL, + 0x1680000000000000ULL, 0x1730000000000000ULL, 0x15E0000000000000ULL, 0x1450000000000000ULL, 0x1040000000000000ULL, 0x11F0000000000000ULL, 0x1320000000000000ULL, 0x1290000000000000ULL, + 0x3600000000000000ULL, 0x37B0000000000000ULL, 0x3560000000000000ULL, 0x34D0000000000000ULL, 0x30C0000000000000ULL, 0x3170000000000000ULL, 0x33A0000000000000ULL, 0x3210000000000000ULL, + 0x3B80000000000000ULL, 0x3A30000000000000ULL, 0x38E0000000000000ULL, 0x3950000000000000ULL, 0x3D40000000000000ULL, 0x3CF0000000000000ULL, 0x3E20000000000000ULL, 0x3F90000000000000ULL, + 0x2D00000000000000ULL, 0x2CB0000000000000ULL, 0x2E60000000000000ULL, 0x2FD0000000000000ULL, 0x2BC0000000000000ULL, 0x2A70000000000000ULL, 0x28A0000000000000ULL, 0x2910000000000000ULL, + 0x2080000000000000ULL, 0x2130000000000000ULL, 0x23E0000000000000ULL, 0x2250000000000000ULL, 0x2640000000000000ULL, 0x27F0000000000000ULL, 0x2520000000000000ULL, 0x2490000000000000ULL, + 0x6C00000000000000ULL, 0x6DB0000000000000ULL, 0x6F60000000000000ULL, 0x6ED0000000000000ULL, 0x6AC0000000000000ULL, 0x6B70000000000000ULL, 0x69A0000000000000ULL, 0x6810000000000000ULL, + 0x6180000000000000ULL, 0x6030000000000000ULL, 0x62E0000000000000ULL, 0x6350000000000000ULL, 0x6740000000000000ULL, 0x66F0000000000000ULL, 0x6420000000000000ULL, 0x6590000000000000ULL, + 0x7700000000000000ULL, 0x76B0000000000000ULL, 0x7460000000000000ULL, 0x75D0000000000000ULL, 0x71C0000000000000ULL, 0x7070000000000000ULL, 0x72A0000000000000ULL, 0x7310000000000000ULL, + 0x7A80000000000000ULL, 0x7B30000000000000ULL, 0x79E0000000000000ULL, 0x7850000000000000ULL, 0x7C40000000000000ULL, 0x7DF0000000000000ULL, 0x7F20000000000000ULL, 0x7E90000000000000ULL, + 0x5A00000000000000ULL, 0x5BB0000000000000ULL, 0x5960000000000000ULL, 0x58D0000000000000ULL, 0x5CC0000000000000ULL, 0x5D70000000000000ULL, 0x5FA0000000000000ULL, 0x5E10000000000000ULL, + 0x5780000000000000ULL, 0x5630000000000000ULL, 0x54E0000000000000ULL, 0x5550000000000000ULL, 0x5140000000000000ULL, 0x50F0000000000000ULL, 0x5220000000000000ULL, 0x5390000000000000ULL, + 0x4100000000000000ULL, 0x40B0000000000000ULL, 0x4260000000000000ULL, 0x43D0000000000000ULL, 0x47C0000000000000ULL, 0x4670000000000000ULL, 0x44A0000000000000ULL, 0x4510000000000000ULL, + 0x4C80000000000000ULL, 0x4D30000000000000ULL, 0x4FE0000000000000ULL, 0x4E50000000000000ULL, 0x4A40000000000000ULL, 0x4BF0000000000000ULL, 0x4920000000000000ULL, 0x4890000000000000ULL, + 0xD800000000000000ULL, 0xD9B0000000000000ULL, 0xDB60000000000000ULL, 0xDAD0000000000000ULL, 0xDEC0000000000000ULL, 0xDF70000000000000ULL, 0xDDA0000000000000ULL, 0xDC10000000000000ULL, + 0xD580000000000000ULL, 0xD430000000000000ULL, 0xD6E0000000000000ULL, 0xD750000000000000ULL, 0xD340000000000000ULL, 0xD2F0000000000000ULL, 0xD020000000000000ULL, 0xD190000000000000ULL, + 0xC300000000000000ULL, 0xC2B0000000000000ULL, 0xC060000000000000ULL, 0xC1D0000000000000ULL, 0xC5C0000000000000ULL, 0xC470000000000000ULL, 0xC6A0000000000000ULL, 0xC710000000000000ULL, + 0xCE80000000000000ULL, 0xCF30000000000000ULL, 0xCDE0000000000000ULL, 0xCC50000000000000ULL, 0xC840000000000000ULL, 0xC9F0000000000000ULL, 0xCB20000000000000ULL, 0xCA90000000000000ULL, + 0xEE00000000000000ULL, 0xEFB0000000000000ULL, 0xED60000000000000ULL, 0xECD0000000000000ULL, 0xE8C0000000000000ULL, 0xE970000000000000ULL, 0xEBA0000000000000ULL, 0xEA10000000000000ULL, + 0xE380000000000000ULL, 0xE230000000000000ULL, 0xE0E0000000000000ULL, 0xE150000000000000ULL, 0xE540000000000000ULL, 0xE4F0000000000000ULL, 0xE620000000000000ULL, 0xE790000000000000ULL, + 0xF500000000000000ULL, 0xF4B0000000000000ULL, 0xF660000000000000ULL, 0xF7D0000000000000ULL, 0xF3C0000000000000ULL, 0xF270000000000000ULL, 0xF0A0000000000000ULL, 0xF110000000000000ULL, + 0xF880000000000000ULL, 0xF930000000000000ULL, 0xFBE0000000000000ULL, 0xFA50000000000000ULL, 0xFE40000000000000ULL, 0xFFF0000000000000ULL, 0xFD20000000000000ULL, 0xFC90000000000000ULL, + 0xB400000000000000ULL, 0xB5B0000000000000ULL, 0xB760000000000000ULL, 0xB6D0000000000000ULL, 0xB2C0000000000000ULL, 0xB370000000000000ULL, 0xB1A0000000000000ULL, 0xB010000000000000ULL, + 0xB980000000000000ULL, 0xB830000000000000ULL, 0xBAE0000000000000ULL, 0xBB50000000000000ULL, 0xBF40000000000000ULL, 0xBEF0000000000000ULL, 0xBC20000000000000ULL, 0xBD90000000000000ULL, + 0xAF00000000000000ULL, 0xAEB0000000000000ULL, 0xAC60000000000000ULL, 0xADD0000000000000ULL, 0xA9C0000000000000ULL, 0xA870000000000000ULL, 0xAAA0000000000000ULL, 0xAB10000000000000ULL, + 0xA280000000000000ULL, 0xA330000000000000ULL, 0xA1E0000000000000ULL, 0xA050000000000000ULL, 0xA440000000000000ULL, 0xA5F0000000000000ULL, 0xA720000000000000ULL, 0xA690000000000000ULL, + 0x8200000000000000ULL, 0x83B0000000000000ULL, 0x8160000000000000ULL, 0x80D0000000000000ULL, 0x84C0000000000000ULL, 0x8570000000000000ULL, 0x87A0000000000000ULL, 0x8610000000000000ULL, + 0x8F80000000000000ULL, 0x8E30000000000000ULL, 0x8CE0000000000000ULL, 0x8D50000000000000ULL, 0x8940000000000000ULL, 0x88F0000000000000ULL, 0x8A20000000000000ULL, 0x8B90000000000000ULL, + 0x9900000000000000ULL, 0x98B0000000000000ULL, 0x9A60000000000000ULL, 0x9BD0000000000000ULL, 0x9FC0000000000000ULL, 0x9E70000000000000ULL, 0x9CA0000000000000ULL, 0x9D10000000000000ULL, + 0x9480000000000000ULL, 0x9530000000000000ULL, 0x97E0000000000000ULL, 0x9650000000000000ULL, 0x9240000000000000ULL, 0x93F0000000000000ULL, 0x9120000000000000ULL, 0x9090000000000000ULL +}; + +static inline uint64_t +pl_hm_hash_str(const char* pcKey) +{ + + uint64_t uCrc = 0; + const unsigned char* pucData = (const unsigned char*)pcKey; + + unsigned char c = *pucData++; + while (c) + { + uCrc = (uCrc >> 8) ^ __gauCrc64LookupTableDS[(uCrc & 0xFF) ^ c]; + c = *pucData; + pucData++; + } + return ~uCrc; +} + +static inline uint64_t +pl_hm_hash(const void* pData, size_t szDataSize, uint64_t uSeed) +{ + uint64_t uCrc = ~uSeed; + const unsigned char* pucData = (const unsigned char*)pData; + while (szDataSize-- != 0) + uCrc = (uCrc >> 8) ^ __gauCrc64LookupTableDS[(uCrc & 0xFF) ^ *pucData++]; + return ~uCrc; +} + +static inline uint64_t +pl_hm_lookup(plHashMap* ptHashMap, uint64_t ulKey) +{ + if(ptHashMap->_uBucketCount == 0) + return UINT64_MAX; + + uint64_t ulModKey = ulKey % ptHashMap->_uBucketCount; + + while(ptHashMap->_aulKeys[ulModKey] != ulKey && ptHashMap->_aulKeys[ulModKey] != UINT64_MAX) + ulModKey = (ulModKey + 1) % ptHashMap->_uBucketCount; + + if(ptHashMap->_aulKeys[ulModKey] == UINT64_MAX) + return UINT64_MAX; + + return ptHashMap->_aulValueIndices[ulModKey]; +} + +static inline uint64_t +pl_hm_get_free_index(plHashMap* ptHashMap) +{ + uint64_t ulResult = UINT64_MAX; + if(pl_sb_size(ptHashMap->_sbulFreeIndices) > 0) + { + ulResult = pl_sb_pop(ptHashMap->_sbulFreeIndices); + } + return ulResult; +} + +static inline void +pl__hm_insert_str(plHashMap* ptHashMap, const char* pcKey, uint64_t ulValue, const char* pcFile, int iLine) +{ + pl__hm_insert(ptHashMap, pl_hm_hash_str(pcKey), ulValue, pcFile, iLine); +} + +static inline void +pl_hm_remove_str(plHashMap* ptHashMap, const char* pcKey) +{ + pl_hm_remove(ptHashMap, pl_hm_hash_str(pcKey)); +} + +static inline uint64_t +pl_hm_lookup_str(plHashMap* ptHashMap, const char* pcKey) +{ + return pl_hm_lookup(ptHashMap, pl_hm_hash_str(pcKey)); +} + +static inline bool +pl_hm_has_key(plHashMap* ptHashMap, uint64_t ulKey) +{ + if(ptHashMap->_uItemCount == 0) + return false; + + uint64_t ulModKey = ulKey % ptHashMap->_uBucketCount; + + while(ptHashMap->_aulKeys[ulModKey] != ulKey && ptHashMap->_aulKeys[ulModKey] != UINT64_MAX) + ulModKey = (ulModKey + 1) % ptHashMap->_uBucketCount; + + return ptHashMap->_aulKeys[ulModKey] != UINT64_MAX; +} + +static inline bool +pl_hm_has_key_str(plHashMap* ptHashMap, const char* pcKey) +{ + return pl_hm_has_key(ptHashMap, pl_hm_hash_str(pcKey)); +} + +#endif // PL_DS_H + diff --git a/pl_json.h b/pl_json.h new file mode 100644 index 0000000..477c93e --- /dev/null +++ b/pl_json.h @@ -0,0 +1,2006 @@ +/* + pl_json.h + + Do this: + #define PL_JSON_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define PL_JSON_IMPLEMENTATION + #include "pl_json.h" +*/ + +// library version +#define PL_JSON_VERSION "0.2.0" +#define PL_JSON_VERSION_NUM 00200 + +/* +Index of this file: +// [SECTION] header mess +// [SECTION] includes +// [SECTION] forward declarations +// [SECTION] public api +// [SECTION] internal structs +// [SECTION] jsmn.h +// [SECTION] c file +*/ + +//----------------------------------------------------------------------------- +// [SECTION] header mess +//----------------------------------------------------------------------------- + +#ifndef PL_JSON_H +#define PL_JSON_H + +#ifndef PL_JSON_MAX_NAME_LENGTH + #define PL_JSON_MAX_NAME_LENGTH 256 +#endif + +//----------------------------------------------------------------------------- +// [SECTION] includes +//----------------------------------------------------------------------------- + +#include +#include + +//----------------------------------------------------------------------------- +// [SECTION] forward declarations +//----------------------------------------------------------------------------- + +// basic types +typedef struct _plJsonObject plJsonObject; // opaque pointer + +// enums +typedef int plJsonType; // internal + +//----------------------------------------------------------------------------- +// [SECTION] public api +//----------------------------------------------------------------------------- + +// main +bool pl_load_json (const char* cPtrJson, plJsonObject* tPtrJsonOut); +void pl_unload_json (plJsonObject* tPtrJson); +char* pl_write_json (plJsonObject* tPtrJson, char* pcBuffer, uint32_t* puBufferSize); + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~reading~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// members +plJsonObject* pl_json_member_by_name (plJsonObject* tPtrJson, const char* pcName); +plJsonObject* pl_json_member_by_index (plJsonObject* tPtrJson, uint32_t uIndex); +void pl_json_member_list (plJsonObject* tPtrJson, char** cPtrListOut, uint32_t* uPtrSizeOut, uint32_t* uPtrLength); +bool pl_json_member_exist (plJsonObject* tPtrJson, const char* pcName); + +// retrieve and cast values (default used if member isn't present) +int pl_json_int_member (plJsonObject* tPtrJson, const char* pcName, int iDefaultValue); +uint32_t pl_json_uint_member (plJsonObject* tPtrJson, const char* pcName, uint32_t uDefaultValue); +float pl_json_float_member (plJsonObject* tPtrJson, const char* pcName, float fDefaultValue); +double pl_json_double_member (plJsonObject* tPtrJson, const char* pcName, double dDefaultValue); +char* pl_json_string_member (plJsonObject* tPtrJson, const char* pcName, char* cPtrDefaultValue, uint32_t uLength); +bool pl_json_bool_member (plJsonObject* tPtrJson, const char* pcName, bool bDefaultValue); +plJsonObject* pl_json_member (plJsonObject* tPtrJson, const char* pcName); +plJsonObject* pl_json_array_member (plJsonObject* tPtrJson, const char* pcName, uint32_t* uPtrSizeOut); + +// retrieve and cast array values (default used if member isn't present) +void pl_json_int_array_member (plJsonObject* tPtrJson, const char* pcName, int* iPtrOut, uint32_t* uPtrSizeOut); +void pl_json_uint_array_member (plJsonObject* tPtrJson, const char* pcName, uint32_t* uPtrOut, uint32_t* uPtrSizeOut); +void pl_json_float_array_member (plJsonObject* tPtrJson, const char* pcName, float* fPtrOut, uint32_t* uPtrSizeOut); +void pl_json_double_array_member(plJsonObject* tPtrJson, const char* pcName, double* dPtrOut, uint32_t* uPtrSizeOut); +void pl_json_bool_array_member (plJsonObject* tPtrJson, const char* pcName, bool* bPtrOut, uint32_t* uPtrSizeOut); +void pl_json_string_array_member(plJsonObject* tPtrJson, const char* pcName, char** cPtrOut, uint32_t* uPtrSizeOut, uint32_t* uPtrLength); + +// cast values +int pl_json_as_int (plJsonObject* tPtrJson); +uint32_t pl_json_as_uint (plJsonObject* tPtrJson); +float pl_json_as_float (plJsonObject* tPtrJson); +double pl_json_as_double (plJsonObject* tPtrJson); +const char* pl_json_as_string (plJsonObject* tPtrJson); +bool pl_json_as_bool (plJsonObject* tPtrJson); + +// cast array values +void pl_json_as_int_array (plJsonObject* tPtrJson, int* iPtrOut, uint32_t* uPtrSizeOut); +void pl_json_as_uint_array (plJsonObject* tPtrJson, uint32_t* uPtrOut, uint32_t* uPtrSizeOut); +void pl_json_as_float_array (plJsonObject* tPtrJson, float* fPtrOut, uint32_t* uPtrSizeOut); +void pl_json_as_double_array (plJsonObject* tPtrJson, double* dPtrOut, uint32_t* uPtrSizeOut); +void pl_json_as_bool_array (plJsonObject* tPtrJson, bool* bPtrOut, uint32_t* uPtrSizeOut); +void pl_json_as_string_array (plJsonObject* tPtrJson, char** cPtrOut, uint32_t* uPtrSizeOut, uint32_t* uPtrLength); + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~writing~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void pl_json_add_int_member (plJsonObject* tPtrJson, const char* pcName, int iValue); +void pl_json_add_uint_member (plJsonObject* tPtrJson, const char* pcName, uint32_t uValue); +void pl_json_add_float_member (plJsonObject* tPtrJson, const char* pcName, float fValue); +void pl_json_add_double_member (plJsonObject* tPtrJson, const char* pcName, double dValue); +void pl_json_add_bool_member (plJsonObject* tPtrJson, const char* pcName, bool bValue); +void pl_json_add_string_member (plJsonObject* tPtrJson, const char* pcName, const char* pcValue); +void pl_json_add_member (plJsonObject* tPtrJson, const char* pcName, plJsonObject* ptValue); +void pl_json_add_member_array (plJsonObject* tPtrJson, const char* pcName, plJsonObject* ptValues, uint32_t uSize); +void pl_json_add_int_array (plJsonObject* tPtrJson, const char* pcName, int* piValues, uint32_t uSize); +void pl_json_add_uint_array (plJsonObject* tPtrJson, const char* pcName, uint32_t* puValues, uint32_t uSize); +void pl_json_add_float_array (plJsonObject* tPtrJson, const char* pcName, float* pfValues, uint32_t uSize); +void pl_json_add_double_array (plJsonObject* tPtrJson, const char* pcName, double* pdValues, uint32_t uSize); +void pl_json_add_bool_array (plJsonObject* tPtrJson, const char* pcName, bool* pbValues, uint32_t uSize); +void pl_json_add_string_array (plJsonObject* tPtrJson, const char* pcName, char** ppcBuffer, uint32_t uSize); + +//----------------------------------------------------------------------------- +// [SECTION] internal structs +//----------------------------------------------------------------------------- + +typedef struct _plJsonObject +{ + plJsonType tType; + uint32_t uChildCount; + char acName[PL_JSON_MAX_NAME_LENGTH]; + plJsonObject* sbtChildren; + uint32_t uChildrenFound; + char* sbcBuffer; + char** psbcBuffer; + + union + { + struct + { + uint32_t* sbuValueOffsets; + uint32_t* sbuValueLength; + }; + + struct + { + uint32_t uValueOffset; + uint32_t uValueLength; + }; + }; + +} plJsonObject; + +#endif //PL_JSON_H + +#ifdef PL_JSON_IMPLEMENTATION + +#ifndef PL_GLTF_EXTENSION_H + +//----------------------------------------------------------------------------- +// [SECTION] jsmn.h +//----------------------------------------------------------------------------- + +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef JSMN_H +#define JSMN_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef JSMN_STATIC +#define JSMN_API static +#else +#define JSMN_API extern +#endif + +typedef enum +{ + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1 << 0, + JSMN_ARRAY = 1 << 1, + JSMN_STRING = 1 << 2, + JSMN_PRIMITIVE = 1 << 3 // number, boolean (true/false) or null +} jsmntype_t; + +enum jsmnerr +{ + + JSMN_ERROR_NOMEM = -1, // Not enough tokens were provided + JSMN_ERROR_INVAL = -2, // Invalid character inside JSON string + JSMN_ERROR_PART = -3 // The string is not a full JSON packet, more bytes expected +}; + +typedef struct jsmntok +{ + jsmntype_t type; // type (object, array, string etc.) + int start; // start position in JSON data string + int end; // end position in JSON data string + int size; +} jsmntok_t; + +typedef struct jsmn_parser +{ + unsigned int pos; // offset in the JSON string + unsigned int toknext; // next token to allocate + int toksuper; // superior token node, e.g. parent object or array +} jsmn_parser; + + +JSMN_API void jsmn_init (jsmn_parser* parser); +JSMN_API int jsmn_parse(jsmn_parser* parser, const char* js, const size_t len, jsmntok_t* tokens, const unsigned int num_tokens); + +#ifndef JSMN_HEADER + +// allocates a fresh unused token from the token pool. +static jsmntok_t* +jsmn_alloc_token(jsmn_parser* parser, jsmntok_t* tokens, const size_t num_tokens) +{ + jsmntok_t* tok; + if (parser->toknext >= num_tokens) + return NULL; + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; + return tok; +} + +// Fills token type and boundaries. +static void +jsmn_fill_token(jsmntok_t* token, const jsmntype_t type, const int start, const int end) +{ + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +// Fills next available token with JSON primitive. +static int +jsmn_parse_primitive(jsmn_parser* parser, const char* js, const size_t len, jsmntok_t* tokens, const size_t num_tokens) +{ + jsmntok_t* token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) + { + switch (js[parser->pos]) + { + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': + case '\t': + case '\r': + case '\n': + case ' ': + case ',': + case ']': + case '}': + goto found; + default: + /* to quiet a warning from gcc*/ + break; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) + { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +found: + if (tokens == NULL) + { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); + parser->pos--; + return 0; +} + +// fills next token with JSON string. +static int +jsmn_parse_string(jsmn_parser* parser, const char* js, const size_t len, jsmntok_t* tokens, const size_t num_tokens) +{ + jsmntok_t* token; + + int start = parser->pos; + + /* Skip starting quote */ + parser->pos++; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) + { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') + { + if (tokens == NULL) + return 0; + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) + { + int i; + parser->pos++; + switch (js[parser->pos]) + { + /* Allowed escaped symbols */ + case '\"': + case '/': + case '\\': + case 'b': + case 'f': + case 'r': + case 'n': + case 't': + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) + { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) /* a-f */ + { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +JSMN_API int +jsmn_parse(jsmn_parser* parser, const char* js, const size_t len, jsmntok_t* tokens, const unsigned int num_tokens) +{ + int r; + int i; + jsmntok_t* token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) + { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) + { + case '{': + case '[': + count++; + if (tokens == NULL) + break; + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) + { + jsmntok_t *t = &tokens[parser->toksuper]; + t->size++; + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': + case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); + for (i = parser->toknext - 1; i >= 0; i--) + { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) + { + if (token->type != type) + return JSMN_ERROR_INVAL; + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) + return JSMN_ERROR_INVAL; + for (; i >= 0; i--) + { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) + { + parser->toksuper = i; + break; + } + } + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) + return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t': + case '\r': + case '\n': + case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) + { + for (i = parser->toknext - 1; i >= 0; i--) + { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) + { + if (tokens[i].start != -1 && tokens[i].end == -1) + { + parser->toksuper = i; + break; + } + } + } + } + break; + + /* In non-strict mode every unquoted value is a primitive */ + default: + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) + return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + } + } + + if (tokens != NULL) + { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) + return JSMN_ERROR_PART; + } + } + + return count; +} + +JSMN_API void +jsmn_init(jsmn_parser* parser) +{ + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +#endif /* JSMN_HEADER */ + +#ifdef __cplusplus +} +#endif + +#endif /* JSMN_H */ + +#endif + +//----------------------------------------------------------------------------- +// [SECTION] c file +//----------------------------------------------------------------------------- + +/* +Index of this file: +// [SECTION] includes +// [SECTION] stretchy buffer +// [SECTION] internal api +// [SECTION] internal enums +// [SECTION] public api implementation +// [SECTION] internal api implementation +*/ + +//----------------------------------------------------------------------------- +// [SECTION] includes +//----------------------------------------------------------------------------- + +#include +#include +#include + +#ifndef PL_ASSERT + #include + #define PL_ASSERT(x) assert((x)) +#endif + +#if defined(PL_JSON_ALLOC) && defined(PL_JSON_FREE) +// ok +#elif !defined(PL_JSON_ALLOC) && !defined(PL_JSON_FREE) +// ok +#else +#error "Must define both or none of PL_JSON_ALLOC and PL_JSON_FREE" +#endif + +#ifndef PL_JSON_ALLOC + #include + #define PL_JSON_ALLOC(x) malloc(x) + #define PL_JSON_FREE(x) free((x)) +#endif + +//----------------------------------------------------------------------------- +// [SECTION] stretchy buffer +//----------------------------------------------------------------------------- + +// based on pl_ds.h version "0.2.0" + +#define pl_sb_json_capacity(buf) \ + ((buf) ? pl__sb_json_header((buf))->uCapacity : 0u) + +#define pl_sb_json_size(buf) \ + ((buf) ? pl__sb_json_header((buf))->uSize : 0u) + +#define pl_sb_json_pop(buf) \ + (buf)[--pl__sb_json_header((buf))->uSize] + +#define pl_sb_json_top(buf) \ + ((buf)[pl__sb_json_header((buf))->uSize-1]) + +#define pl_sb_json_free(buf) \ + if((buf)){ PL_JSON_FREE(pl__sb_json_header(buf));} (buf) = NULL; + +#define pl_sb_json_reset(buf) \ + if((buf)){ pl__sb_json_header((buf))->uSize = 0u;} + +#define pl_sb_json_push(buf, v) \ + (pl__sb_json_may_grow((buf), sizeof(*(buf)), 1, 8), (buf)[pl__sb_json_header((buf))->uSize++] = (v)) + +#define pl_sb_json_reserve(buf, n) \ + (pl__sb_json_may_grow((buf), sizeof(*(buf)), (n), (n))) + +#define pl_sb_json_resize(buf, n) \ + (pl__sb_json_may_grow((buf), sizeof(*(buf)), (n), (n)), pl__sb_json_header((buf))->uSize = (n)) + +#define pl_sb_json_add_n(buf, n) \ + (pl__sb_json_may_grow((buf), sizeof(*(buf)), (n), (n)), (n) ? (pl__sb_json_header(buf)->uSize += (n), pl__sb_json_header(buf)->uSize - (n)) : pl_sb_json_size(buf)) + +#define pl_sb_json_sprintf(buf, pcFormat, ...) \ + pl__sb_json_sprintf(&(buf), (pcFormat), __VA_ARGS__) + +#define pl__sb_json_header(buf) ((plSbJsonHeader_*)(((char*)(buf)) - sizeof(plSbJsonHeader_))) +#define pl__sb_json_may_grow(buf, s, n, m) pl__sb_json_may_grow_((void**)&(buf), (s), (n), (m)) + +typedef struct +{ + uint32_t uSize; + uint32_t uCapacity; +} plSbJsonHeader_; + +static void +pl__sb_json_grow(void** ptrBuffer, size_t szElementSize, size_t szNewItems) +{ + + plSbJsonHeader_* ptOldHeader = pl__sb_json_header(*ptrBuffer); + + plSbJsonHeader_* ptNewHeader = (plSbJsonHeader_*)PL_JSON_ALLOC((ptOldHeader->uCapacity + szNewItems) * szElementSize + sizeof(plSbJsonHeader_)); + memset(ptNewHeader, 0, (ptOldHeader->uCapacity + szNewItems) * szElementSize + sizeof(plSbJsonHeader_)); + if(ptNewHeader) + { + ptNewHeader->uSize = ptOldHeader->uSize; + ptNewHeader->uCapacity = ptOldHeader->uCapacity + (uint32_t)szNewItems; + memcpy(&ptNewHeader[1], *ptrBuffer, ptOldHeader->uSize * szElementSize); + PL_JSON_FREE(ptOldHeader); + *ptrBuffer = &ptNewHeader[1]; + } +} + +static void +pl__sb_json_may_grow_(void** ptrBuffer, size_t szElementSize, size_t szNewItems, size_t szMinCapacity) +{ + if(*ptrBuffer) + { + plSbJsonHeader_* ptOriginalHeader = pl__sb_json_header(*ptrBuffer); + if(ptOriginalHeader->uSize + szNewItems > ptOriginalHeader->uCapacity) + { + pl__sb_json_grow(ptrBuffer, szElementSize, szNewItems); + } + } + else // first run + { + plSbJsonHeader_* ptHeader = (plSbJsonHeader_*)PL_JSON_ALLOC(szMinCapacity * szElementSize + sizeof(plSbJsonHeader_)); + memset(ptHeader, 0, szMinCapacity * szElementSize + sizeof(plSbJsonHeader_)); + if(ptHeader) + { + *ptrBuffer = &ptHeader[1]; + ptHeader->uSize = 0u; + ptHeader->uCapacity = (uint32_t)szMinCapacity; + } + } +} + +static void +pl__sb_json_vsprintf(char** ppcBuffer, const char* pcFormat, va_list args) +{ + va_list args2; + va_copy(args2, args); + int32_t n = vsnprintf(NULL, 0, pcFormat, args2); + va_end(args2); + uint32_t an = pl_sb_json_size(*ppcBuffer); + pl_sb_json_resize(*ppcBuffer, an + n + 1); + vsnprintf(*ppcBuffer + an, n + 1, pcFormat, args); +} + +static void +pl__sb_json_sprintf(char** ppcBuffer, const char* pcFormat, ...) +{ + va_list args; + va_start(args, pcFormat); + pl__sb_json_vsprintf(ppcBuffer, pcFormat, args); + va_end(args); +} + +//----------------------------------------------------------------------------- +// [SECTION] internal api +//----------------------------------------------------------------------------- + +static plJsonType pl__get_json_token_object_type(const char* cPtrJson, jsmntok_t* tPtrToken); +static void pl__write_json_object(plJsonObject* ptJson, char* pcBuffer, uint32_t* puBufferSize, uint32_t* puCursor, uint32_t* puDepth); +static void pl__check_json_object(plJsonObject* ptJson, uint32_t* puBufferSize, uint32_t* puCursor, uint32_t* puDepth); + +//----------------------------------------------------------------------------- +// [SECTION] internal enums +//----------------------------------------------------------------------------- + +enum plJsonType_ +{ + PL_JSON_TYPE_UNSPECIFIED, + PL_JSON_TYPE_STRING, + PL_JSON_TYPE_ARRAY, + PL_JSON_TYPE_NUMBER, + PL_JSON_TYPE_BOOL, + PL_JSON_TYPE_OBJECT, + PL_JSON_TYPE_NULL, +}; + +//----------------------------------------------------------------------------- +// [SECTION] public api implementation +//----------------------------------------------------------------------------- + +bool +pl_load_json(const char* cPtrJson, plJsonObject* tPtrJsonOut) +{ + jsmn_parser tP = {0}; + jsmntok_t* sbtTokens = NULL; + pl_sb_json_resize(sbtTokens, 512); + + jsmn_init(&tP); + + int iResult = 0; + while(true) + { + iResult = jsmn_parse(&tP, cPtrJson, strlen(cPtrJson), sbtTokens, pl_sb_json_size(sbtTokens)); + + if(iResult == JSMN_ERROR_INVAL) + { + PL_ASSERT(false); + } + else if(iResult == JSMN_ERROR_NOMEM) + { + pl_sb_json_add_n(sbtTokens, 256); + } + else if(iResult == JSMN_ERROR_PART) + { + PL_ASSERT(false); + } + else + { + break; + } + } + + uint32_t uLayer = 0; + uint32_t uCurrentTokenIndex = 0; + plJsonObject** sbtObjectStack = NULL; + tPtrJsonOut->tType = PL_JSON_TYPE_OBJECT; + pl_sb_json_reserve(tPtrJsonOut->sbcBuffer, strlen(cPtrJson)); + tPtrJsonOut->psbcBuffer = &tPtrJsonOut->sbcBuffer; + tPtrJsonOut->uChildCount = sbtTokens[uCurrentTokenIndex].size; + strcpy(tPtrJsonOut->acName, "ROOT"); + pl_sb_json_reserve(tPtrJsonOut->sbtChildren, sbtTokens[uCurrentTokenIndex].size); + pl_sb_json_push(sbtObjectStack, tPtrJsonOut); + while(uCurrentTokenIndex < (uint32_t)iResult) + { + + if(pl_sb_json_top(sbtObjectStack)->uChildrenFound == pl_sb_json_top(sbtObjectStack)->uChildCount) + pl_sb_json_pop(sbtObjectStack); + else + { + + plJsonObject* tPtrParentObject = pl_sb_json_top(sbtObjectStack); + + jsmntok_t* tPtrCurrentToken = &sbtTokens[uCurrentTokenIndex]; + jsmntok_t* tPtrNextToken = &sbtTokens[uCurrentTokenIndex + 1]; + + switch (tPtrCurrentToken->type) + { + + // value + case JSMN_PRIMITIVE: + if(tPtrParentObject->tType == PL_JSON_TYPE_ARRAY) + { + const uint32_t uBufferLocation = pl_sb_json_size(tPtrJsonOut->sbcBuffer); + pl_sb_json_resize(tPtrJsonOut->sbcBuffer, uBufferLocation + tPtrCurrentToken->end - tPtrCurrentToken->start + 1); + memcpy(&tPtrJsonOut->sbcBuffer[uBufferLocation], &cPtrJson[tPtrCurrentToken->start], tPtrCurrentToken->end - tPtrCurrentToken->start); + pl_sb_json_push(tPtrParentObject->sbuValueOffsets, uBufferLocation); + pl_sb_json_push(tPtrParentObject->sbuValueLength, tPtrCurrentToken->end - tPtrCurrentToken->start); + tPtrParentObject->uChildrenFound++; + } + else + { + const uint32_t uBufferLocation = pl_sb_json_size(tPtrJsonOut->sbcBuffer); + pl_sb_json_resize(tPtrJsonOut->sbcBuffer, uBufferLocation + tPtrCurrentToken->end - tPtrCurrentToken->start + 1); + memcpy(&tPtrJsonOut->sbcBuffer[uBufferLocation], &cPtrJson[tPtrCurrentToken->start], tPtrCurrentToken->end - tPtrCurrentToken->start); + tPtrParentObject->uValueOffset = uBufferLocation; + tPtrParentObject->uValueLength = tPtrCurrentToken->end - tPtrCurrentToken->start; + tPtrParentObject->uChildrenFound++; + pl_sb_json_pop(sbtObjectStack); + } + break; + + case JSMN_STRING: + { + // value + if(tPtrCurrentToken->size == 0) + { + if(tPtrParentObject->tType == PL_JSON_TYPE_ARRAY) + { + const uint32_t uBufferLocation = pl_sb_json_size(tPtrJsonOut->sbcBuffer); + pl_sb_json_resize(tPtrJsonOut->sbcBuffer, uBufferLocation + tPtrCurrentToken->end - tPtrCurrentToken->start + 1); + memcpy(&tPtrJsonOut->sbcBuffer[uBufferLocation], &cPtrJson[tPtrCurrentToken->start], tPtrCurrentToken->end - tPtrCurrentToken->start); + pl_sb_json_push(tPtrParentObject->sbuValueOffsets, uBufferLocation); + pl_sb_json_push(tPtrParentObject->sbuValueLength, tPtrCurrentToken->end - tPtrCurrentToken->start); + tPtrParentObject->uChildrenFound++; + } + else + { + const uint32_t uBufferLocation = pl_sb_json_size(tPtrJsonOut->sbcBuffer); + pl_sb_json_resize(tPtrJsonOut->sbcBuffer, uBufferLocation + tPtrCurrentToken->end - tPtrCurrentToken->start + 1); + memcpy(&tPtrJsonOut->sbcBuffer[uBufferLocation], &cPtrJson[tPtrCurrentToken->start], tPtrCurrentToken->end - tPtrCurrentToken->start); + tPtrParentObject->uValueOffset = uBufferLocation; + tPtrParentObject->uValueLength = tPtrCurrentToken->end - tPtrCurrentToken->start; + tPtrParentObject->uChildrenFound++; + pl_sb_json_pop(sbtObjectStack); + } + } + + // key + else + { + plJsonObject tNewJsonObject = { + pl__get_json_token_object_type(cPtrJson, tPtrNextToken), + (uint32_t)tPtrNextToken->size + }; + tNewJsonObject.psbcBuffer = &tPtrJsonOut->sbcBuffer; + if(tNewJsonObject.uChildCount == 0) + { + tNewJsonObject.uChildrenFound--; + } + tPtrParentObject->uChildrenFound++; + strncpy(tNewJsonObject.acName, &cPtrJson[tPtrCurrentToken->start], tPtrCurrentToken->end - tPtrCurrentToken->start); + pl_sb_json_push(tPtrParentObject->sbtChildren, tNewJsonObject); + pl_sb_json_reserve(pl_sb_json_top(tPtrParentObject->sbtChildren).sbtChildren, tPtrNextToken->size); + pl_sb_json_reserve(pl_sb_json_top(tPtrParentObject->sbtChildren).sbuValueOffsets, tPtrNextToken->size); + pl_sb_json_reserve(pl_sb_json_top(tPtrParentObject->sbtChildren).sbuValueLength, tPtrNextToken->size); + pl_sb_json_push(sbtObjectStack, &pl_sb_json_top(tPtrParentObject->sbtChildren)); + + if(tNewJsonObject.tType == PL_JSON_TYPE_ARRAY) + { + uCurrentTokenIndex++; + } + } + break; + } + + case JSMN_OBJECT: + { + if(tPtrParentObject->tType == PL_JSON_TYPE_ARRAY) + { + plJsonObject tNewJsonObject = { + pl__get_json_token_object_type(cPtrJson, tPtrCurrentToken), + (uint32_t)tPtrCurrentToken->size + }; + tNewJsonObject.psbcBuffer = &tPtrJsonOut->sbcBuffer; + strcpy(tNewJsonObject.acName, "UNNAMED OBJECT"); + pl_sb_json_push(tPtrParentObject->sbtChildren, tNewJsonObject); + pl_sb_json_reserve(pl_sb_json_top(tPtrParentObject->sbtChildren).sbtChildren, tPtrCurrentToken->size); + pl_sb_json_reserve(pl_sb_json_top(tPtrParentObject->sbtChildren).sbuValueOffsets, tPtrCurrentToken->size); + pl_sb_json_reserve(pl_sb_json_top(tPtrParentObject->sbtChildren).sbuValueLength, tPtrCurrentToken->size); + pl_sb_json_push(sbtObjectStack, &pl_sb_json_top(tPtrParentObject->sbtChildren)); + tPtrParentObject->uChildrenFound++; + } + else if(tPtrParentObject->tType == PL_JSON_TYPE_OBJECT) + { + // combining key/pair + // tPtrParentObject->uChildrenFound++; + } + else + { + + PL_ASSERT(false); // shouldn't be possible + } + break; + } + + case JSMN_ARRAY: + { + if(tPtrParentObject->tType == PL_JSON_TYPE_ARRAY) + { + plJsonObject tNewJsonObject = { + pl__get_json_token_object_type(cPtrJson, tPtrCurrentToken), + (uint32_t)tPtrCurrentToken->size + }; + tNewJsonObject.psbcBuffer = &tPtrJsonOut->sbcBuffer; + strcpy(tNewJsonObject.acName, "UNNAMED ARRAY"); + pl_sb_json_push(tPtrParentObject->sbtChildren, tNewJsonObject); + pl_sb_json_reserve(pl_sb_json_top(tPtrParentObject->sbtChildren).sbtChildren, tPtrCurrentToken->size); + pl_sb_json_reserve(pl_sb_json_top(tPtrParentObject->sbtChildren).sbuValueOffsets, tPtrCurrentToken->size); + pl_sb_json_reserve(pl_sb_json_top(tPtrParentObject->sbtChildren).sbuValueLength, tPtrCurrentToken->size); + pl_sb_json_push(sbtObjectStack, &pl_sb_json_top(tPtrParentObject->sbtChildren)); + tPtrParentObject->uChildrenFound++; + } + else if(tPtrParentObject->tType == PL_JSON_TYPE_STRING) + { + // combining key/pair + } + else + { + // shouldn't be possible + PL_ASSERT(false); + } + break; + } + + default: + break; + } + + uCurrentTokenIndex++; + } + } + + pl_sb_json_free(sbtTokens); + return true; +} + +void +pl_unload_json(plJsonObject* tPtrJson) +{ + for(uint32_t i = 0; i < pl_sb_json_size(tPtrJson->sbtChildren); i++) + pl_unload_json(&tPtrJson->sbtChildren[i]); + + if(tPtrJson->tType == PL_JSON_TYPE_ARRAY) + { + pl_sb_json_free(tPtrJson->sbuValueOffsets); + pl_sb_json_free(tPtrJson->sbtChildren); + pl_sb_json_free(tPtrJson->sbuValueLength); + } + else + { + tPtrJson->uValueOffset = 0; + tPtrJson->uValueLength = 0; + } + + tPtrJson->uChildCount = 0; + tPtrJson->uChildrenFound = 0; + + pl_sb_json_free(tPtrJson->sbcBuffer); + + memset(tPtrJson->acName, 0, PL_JSON_MAX_NAME_LENGTH); + tPtrJson->tType = PL_JSON_TYPE_UNSPECIFIED; +} + +char* +pl_write_json(plJsonObject* tPtrJson, char* pcBuffer, uint32_t* puBufferSize) +{ + uint32_t uCursorPosition = 0; + uint32_t uDepth = 0; + if(pcBuffer) + pl__write_json_object(tPtrJson, pcBuffer, puBufferSize, &uCursorPosition, &uDepth); + else + pl__check_json_object(tPtrJson, puBufferSize, &uCursorPosition, &uDepth); + *puBufferSize = uCursorPosition; + return pcBuffer; +} + +plJsonObject* +pl_json_member_by_name(plJsonObject* tPtrJson, const char* pcName) +{ + + for(uint32_t i = 0; i < tPtrJson->uChildCount; i++) + { + if(strncmp(pcName, tPtrJson->sbtChildren[i].acName, strlen(tPtrJson->sbtChildren[i].acName)) == 0) + return &tPtrJson->sbtChildren[i]; + } + + return NULL; +} + +plJsonObject* +pl_json_member_by_index(plJsonObject* tPtrJson, uint32_t uIndex) +{ + PL_ASSERT(uIndex < tPtrJson->uChildCount); + return &tPtrJson->sbtChildren[uIndex]; +} + +void +pl_json_member_list(plJsonObject* tPtrJson, char** cPtrListOut, uint32_t* uPtrSizeOut, uint32_t* uPtrLength) +{ + if(cPtrListOut) + { + for(uint32_t i = 0; i < pl_sb_json_size(tPtrJson->sbtChildren); i++) + strcpy(cPtrListOut[i], tPtrJson->sbtChildren[i].acName); + } + + if(uPtrSizeOut) + *uPtrSizeOut = pl_sb_json_size(tPtrJson->sbtChildren); + + if(uPtrLength) + { + for(uint32_t i = 0; i < pl_sb_json_size(tPtrJson->sbtChildren); i++) + { + const uint32_t uLength = (uint32_t)strlen(tPtrJson->sbtChildren[i].acName); + if(uLength > *uPtrLength) *uPtrLength = uLength; + } + } +} + +bool +pl_json_member_exist(plJsonObject* tPtrJson, const char* pcName) +{ + return pl_json_member_by_name(tPtrJson, pcName) != NULL; +} + +int +pl_json_int_member(plJsonObject* tPtrJson, const char* pcName, int iDefaultValue) +{ + plJsonObject* tPtrMember = pl_json_member_by_name(tPtrJson, pcName); + + if(tPtrMember) + return pl_json_as_int(tPtrMember); + + return iDefaultValue; +} + +uint32_t +pl_json_uint_member(plJsonObject* tPtrJson, const char* pcName, uint32_t uDefaultValue) +{ + plJsonObject* tPtrMember = pl_json_member_by_name(tPtrJson, pcName); + + if(tPtrMember) + return pl_json_as_uint(tPtrMember); + return uDefaultValue; +} + +float +pl_json_float_member(plJsonObject* tPtrJson, const char* pcName, float fDefaultValue) +{ + plJsonObject* tPtrMember = pl_json_member_by_name(tPtrJson, pcName); + + if(tPtrMember) + return pl_json_as_float(tPtrMember); + return fDefaultValue; +} + +double +pl_json_double_member(plJsonObject* tPtrJson, const char* pcName, double dDefaultValue) +{ + plJsonObject* tPtrMember = pl_json_member_by_name(tPtrJson, pcName); + + if(tPtrMember) + return pl_json_as_double(tPtrMember); + return dDefaultValue; +} + +char* +pl_json_string_member(plJsonObject* tPtrJson, const char* pcName, char* cPtrDefaultValue, uint32_t uLength) +{ + plJsonObject* tPtrMember = pl_json_member_by_name(tPtrJson, pcName); + + if(tPtrMember) + { + PL_ASSERT(uLength >= tPtrMember->uValueLength); + memset(cPtrDefaultValue, 0, uLength); + strncpy(cPtrDefaultValue, &(*tPtrMember->psbcBuffer)[tPtrMember->uValueOffset], tPtrMember->uValueLength); + } + return cPtrDefaultValue; +} + +bool +pl_json_bool_member(plJsonObject* tPtrJson, const char* pcName, bool bDefaultValue) +{ + plJsonObject* tPtrMember = pl_json_member_by_name(tPtrJson, pcName); + + if(tPtrMember) + return pl_json_as_bool(tPtrMember); + return bDefaultValue; +} + +plJsonObject* +pl_json_member(plJsonObject* tPtrJson, const char* pcName) +{ + plJsonObject* tPtrMember = pl_json_member_by_name(tPtrJson, pcName); + + if(tPtrMember) + { + PL_ASSERT(tPtrMember->tType == PL_JSON_TYPE_OBJECT); + return tPtrMember; + } + return NULL; +} + +plJsonObject* +pl_json_array_member(plJsonObject* tPtrJson, const char* pcName, uint32_t* uPtrSizeOut) +{ + plJsonObject* tPtrMember = pl_json_member_by_name(tPtrJson, pcName); + if(uPtrSizeOut) + *uPtrSizeOut = 0; + + if(tPtrMember) + { + PL_ASSERT(tPtrMember->tType == PL_JSON_TYPE_ARRAY); + if(uPtrSizeOut) + *uPtrSizeOut = tPtrMember->uChildCount; + return tPtrMember->sbtChildren; + } + return NULL; +} + +void +pl_json_int_array_member(plJsonObject* tPtrJson, const char* pcName, int* iPtrOut, uint32_t* uPtrSizeOut) +{ + plJsonObject* tPtrMember = pl_json_member_by_name(tPtrJson, pcName); + + if(tPtrMember) + pl_json_as_int_array(tPtrMember, iPtrOut, uPtrSizeOut); +} + +void +pl_json_uint_array_member(plJsonObject* tPtrJson, const char* pcName, uint32_t* uPtrOut, uint32_t* uPtrSizeOut) +{ + plJsonObject* tPtrMember = pl_json_member_by_name(tPtrJson, pcName); + + if(tPtrMember) + pl_json_as_uint_array(tPtrMember, uPtrOut, uPtrSizeOut); +} + +void +pl_json_float_array_member(plJsonObject* tPtrJson, const char* pcName, float* fPtrOut, uint32_t* uPtrSizeOut) +{ + plJsonObject* tPtrMember = pl_json_member_by_name(tPtrJson, pcName); + + if(tPtrMember) + pl_json_as_float_array(tPtrMember, fPtrOut, uPtrSizeOut); +} + +void +pl_json_double_array_member(plJsonObject* tPtrJson, const char* pcName, double* dPtrOut, uint32_t* uPtrSizeOut) +{ + plJsonObject* tPtrMember = pl_json_member_by_name(tPtrJson, pcName); + + if(tPtrMember) + pl_json_as_double_array(tPtrMember, dPtrOut, uPtrSizeOut); +} + +void +pl_json_string_array_member(plJsonObject* tPtrJson, const char* pcName, char** cPtrOut, uint32_t* uPtrSizeOut, uint32_t* uPtrLength) +{ + plJsonObject* tPtrMember = pl_json_member_by_name(tPtrJson, pcName); + + if(tPtrMember) + pl_json_as_string_array(tPtrMember, cPtrOut, uPtrSizeOut, uPtrLength); +} + +void +pl_json_bool_array_member(plJsonObject* tPtrJson, const char* pcName, bool* bPtrOut, uint32_t* uPtrSizeOut) +{ + plJsonObject* tPtrMember = pl_json_member_by_name(tPtrJson, pcName); + + if(tPtrMember) + pl_json_as_bool_array(tPtrMember, bPtrOut, uPtrSizeOut); +} + +int +pl_json_as_int(plJsonObject* tPtrJson) +{ + PL_ASSERT(tPtrJson->tType == PL_JSON_TYPE_NUMBER); + return (int)strtod(&(*tPtrJson->psbcBuffer)[tPtrJson->uValueOffset], NULL); +} + +uint32_t +pl_json_as_uint(plJsonObject* tPtrJson) +{ + PL_ASSERT(tPtrJson->tType == PL_JSON_TYPE_NUMBER); + return (uint32_t)strtod(&(*tPtrJson->psbcBuffer)[tPtrJson->uValueOffset], NULL); +} + +float +pl_json_as_float(plJsonObject* tPtrJson) +{ + PL_ASSERT(tPtrJson->tType == PL_JSON_TYPE_NUMBER); + return (float)strtod(&(*tPtrJson->psbcBuffer)[tPtrJson->uValueOffset], NULL); +} + +double +pl_json_as_double(plJsonObject* tPtrJson) +{ + PL_ASSERT(tPtrJson->tType == PL_JSON_TYPE_NUMBER); + return strtod(&(*tPtrJson->psbcBuffer)[tPtrJson->uValueOffset], NULL); +} + +const char* +pl_json_as_string(plJsonObject* tPtrJson) +{ + PL_ASSERT(tPtrJson->tType == PL_JSON_TYPE_STRING); + return &(*tPtrJson->psbcBuffer)[tPtrJson->uValueOffset]; +} + +bool +pl_json_as_bool(plJsonObject* tPtrJson) +{ + PL_ASSERT(tPtrJson->tType == PL_JSON_TYPE_BOOL); + return (&(*tPtrJson->psbcBuffer)[tPtrJson->uValueOffset])[0] == 't'; +} + +void +pl_json_as_int_array(plJsonObject* tPtrJson, int* iPtrOut, uint32_t* uPtrSizeOut) +{ + PL_ASSERT(tPtrJson->tType == PL_JSON_TYPE_ARRAY); + + if(uPtrSizeOut) + *uPtrSizeOut = tPtrJson->uChildCount; + + if(iPtrOut) + { + for(uint32_t i = 0; i < tPtrJson->uChildCount; i++) + iPtrOut[i] = (int)strtod(&(*tPtrJson->psbcBuffer)[tPtrJson->sbuValueOffsets[i]], NULL); + } +} + +void +pl_json_as_uint_array(plJsonObject* tPtrJson, uint32_t* uPtrOut, uint32_t* uPtrSizeOut) +{ + PL_ASSERT(tPtrJson->tType == PL_JSON_TYPE_ARRAY); + + if(uPtrSizeOut) + *uPtrSizeOut = tPtrJson->uChildCount; + + if(uPtrOut) + { + for(uint32_t i = 0; i < tPtrJson->uChildCount; i++) + uPtrOut[i] = (uint32_t)strtod(&(*tPtrJson->psbcBuffer)[tPtrJson->sbuValueOffsets[i]], NULL); + } +} + +void +pl_json_as_float_array(plJsonObject* tPtrJson, float* fPtrOut, uint32_t* uPtrSizeOut) +{ + PL_ASSERT(tPtrJson->tType == PL_JSON_TYPE_ARRAY); + + if(uPtrSizeOut) + { + *uPtrSizeOut = tPtrJson->uChildCount; + } + + if(fPtrOut) + { + for(uint32_t i = 0; i < tPtrJson->uChildCount; i++) + fPtrOut[i] = (float)strtod(&(*tPtrJson->psbcBuffer)[tPtrJson->sbuValueOffsets[i]], NULL); + } +} + +void +pl_json_as_double_array(plJsonObject* tPtrJson, double* dPtrOut, uint32_t* uPtrSizeOut) +{ + PL_ASSERT(tPtrJson->tType == PL_JSON_TYPE_ARRAY); + + if(uPtrSizeOut) + *uPtrSizeOut = tPtrJson->uChildCount; + + if(dPtrOut) + { + for(uint32_t i = 0; i < tPtrJson->uChildCount; i++) + dPtrOut[i] = strtod(&(*tPtrJson->psbcBuffer)[tPtrJson->sbuValueOffsets[i]], NULL); + } +} + +void +pl_json_as_string_array(plJsonObject* tPtrJson, char** cPtrOut, uint32_t* uPtrSizeOut, uint32_t* uPtrLength) +{ + PL_ASSERT(tPtrJson->tType == PL_JSON_TYPE_ARRAY); + + if(cPtrOut) + { + for(uint32_t i = 0; i < tPtrJson->uChildCount; i++) + { + PL_ASSERT(*uPtrLength >= tPtrJson->sbuValueLength[i]); + memset(cPtrOut[i], 0, *uPtrLength); + strncpy(cPtrOut[i],&(*tPtrJson->psbcBuffer)[tPtrJson->sbuValueOffsets[i]], tPtrJson->sbuValueLength[i]); + } + } + else if(uPtrSizeOut) + *uPtrSizeOut = tPtrJson->uChildCount; + + if(uPtrLength) + { + for(uint32_t i = 0; i < tPtrJson->uChildCount; i++) + { + if(tPtrJson->sbuValueLength[i] > *uPtrLength) + *uPtrLength = tPtrJson->sbuValueLength[i]; + } + } +} + +void +pl_json_as_bool_array(plJsonObject* tPtrJson, bool* bPtrOut, uint32_t* uPtrSizeOut) +{ + PL_ASSERT(tPtrJson->tType == PL_JSON_TYPE_ARRAY); + + if(uPtrSizeOut) + *uPtrSizeOut = tPtrJson->uChildCount; + + if(bPtrOut) + { + for(uint32_t i = 0; i < tPtrJson->uChildCount; i++) + bPtrOut[i] = (&(*tPtrJson->psbcBuffer)[tPtrJson->sbuValueOffsets[i]])[0] == 't'; + } +} + +void +pl_json_add_int_member(plJsonObject* tPtrJson, const char* pcName, int iValue) +{ + tPtrJson->uChildCount++; + tPtrJson->uChildrenFound++; + tPtrJson->tType = PL_JSON_TYPE_OBJECT; + + if(tPtrJson->psbcBuffer == NULL) + tPtrJson->psbcBuffer = &tPtrJson->sbcBuffer; + + plJsonObject tNewJsonObject = {0}; + tNewJsonObject.tType = PL_JSON_TYPE_NUMBER; + snprintf(tNewJsonObject.acName, PL_JSON_MAX_NAME_LENGTH, "%s", pcName); + tNewJsonObject.sbcBuffer = NULL; + tNewJsonObject.psbcBuffer = tPtrJson->psbcBuffer; + tNewJsonObject.uValueOffset = pl_sb_json_size(*tPtrJson->psbcBuffer); + tNewJsonObject.uValueLength = snprintf(NULL, 0, "%i", iValue); + pl_sb_json_resize(*tPtrJson->psbcBuffer, tNewJsonObject.uValueOffset + tNewJsonObject.uValueLength); + snprintf(&(*tPtrJson->psbcBuffer)[tNewJsonObject.uValueOffset], tNewJsonObject.uValueLength + 1, "%i", iValue); + + pl_sb_json_push(tPtrJson->sbtChildren, tNewJsonObject); +} + +void +pl_json_add_uint_member(plJsonObject* tPtrJson, const char* pcName, uint32_t uValue) +{ + tPtrJson->uChildCount++; + tPtrJson->uChildrenFound++; + tPtrJson->tType = PL_JSON_TYPE_OBJECT; + + if(tPtrJson->psbcBuffer == NULL) + tPtrJson->psbcBuffer = &tPtrJson->sbcBuffer; + + plJsonObject tNewJsonObject = {0}; + tNewJsonObject.tType = PL_JSON_TYPE_NUMBER; + snprintf(tNewJsonObject.acName, PL_JSON_MAX_NAME_LENGTH, "%s", pcName); + tNewJsonObject.sbcBuffer = NULL; + tNewJsonObject.psbcBuffer = tPtrJson->psbcBuffer; + tNewJsonObject.uValueOffset = pl_sb_json_size(*tPtrJson->psbcBuffer); + tNewJsonObject.uValueLength = snprintf(NULL, 0, "%u", uValue); + pl_sb_json_resize(*tPtrJson->psbcBuffer, tNewJsonObject.uValueOffset + tNewJsonObject.uValueLength); + snprintf(&(*tPtrJson->psbcBuffer)[tNewJsonObject.uValueOffset], tNewJsonObject.uValueLength, "%u", uValue); + + pl_sb_json_push(tPtrJson->sbtChildren, tNewJsonObject); +} + +void +pl_json_add_float_member(plJsonObject* tPtrJson, const char* pcName, float fValue) +{ + tPtrJson->uChildCount++; + tPtrJson->uChildrenFound++; + tPtrJson->tType = PL_JSON_TYPE_OBJECT; + + if(tPtrJson->psbcBuffer == NULL) + tPtrJson->psbcBuffer = &tPtrJson->sbcBuffer; + + plJsonObject tNewJsonObject = {0}; + tNewJsonObject.tType = PL_JSON_TYPE_NUMBER; + snprintf(tNewJsonObject.acName, PL_JSON_MAX_NAME_LENGTH, "%s", pcName); + tNewJsonObject.sbcBuffer = NULL; + tNewJsonObject.psbcBuffer = tPtrJson->psbcBuffer; + tNewJsonObject.uValueOffset = pl_sb_json_size(*tPtrJson->psbcBuffer); + tNewJsonObject.uValueLength = snprintf(NULL, 0, "%0.7f", fValue); + pl_sb_json_resize(*tPtrJson->psbcBuffer, tNewJsonObject.uValueOffset + tNewJsonObject.uValueLength); + snprintf(&(*tPtrJson->psbcBuffer)[tNewJsonObject.uValueOffset], tNewJsonObject.uValueLength, "%0.7f", fValue); + + pl_sb_json_push(tPtrJson->sbtChildren, tNewJsonObject); +} + +void +pl_json_add_double_member(plJsonObject* tPtrJson, const char* pcName, double dValue) +{ + tPtrJson->uChildCount++; + tPtrJson->uChildrenFound++; + tPtrJson->tType = PL_JSON_TYPE_OBJECT; + + if(tPtrJson->psbcBuffer == NULL) + tPtrJson->psbcBuffer = &tPtrJson->sbcBuffer; + + plJsonObject tNewJsonObject = {0}; + tNewJsonObject.tType = PL_JSON_TYPE_NUMBER; + snprintf(tNewJsonObject.acName, PL_JSON_MAX_NAME_LENGTH, "%s", pcName); + tNewJsonObject.sbcBuffer = NULL; + tNewJsonObject.psbcBuffer = tPtrJson->psbcBuffer; + tNewJsonObject.uValueOffset = pl_sb_json_size(*tPtrJson->psbcBuffer); + tNewJsonObject.uValueLength = snprintf(NULL, 0, "%0.15f", dValue); + pl_sb_json_resize(*tPtrJson->psbcBuffer, tNewJsonObject.uValueOffset + tNewJsonObject.uValueLength); + snprintf(&(*tPtrJson->psbcBuffer)[tNewJsonObject.uValueOffset], tNewJsonObject.uValueLength, "%0.15f", dValue); + + pl_sb_json_push(tPtrJson->sbtChildren, tNewJsonObject); +} + +void +pl_json_add_bool_member(plJsonObject* tPtrJson, const char* pcName, bool bValue) +{ + tPtrJson->uChildCount++; + tPtrJson->uChildrenFound++; + tPtrJson->tType = PL_JSON_TYPE_OBJECT; + + if(tPtrJson->psbcBuffer == NULL) + tPtrJson->psbcBuffer = &tPtrJson->sbcBuffer; + + plJsonObject tNewJsonObject = {0}; + tNewJsonObject.tType = PL_JSON_TYPE_BOOL; + snprintf(tNewJsonObject.acName, PL_JSON_MAX_NAME_LENGTH, "%s", pcName); + tNewJsonObject.sbcBuffer = NULL; + tNewJsonObject.psbcBuffer = tPtrJson->psbcBuffer; + tNewJsonObject.uValueOffset = pl_sb_json_size(*tPtrJson->psbcBuffer); + tNewJsonObject.uValueLength = snprintf(NULL, 0, "%s", bValue ? "true" : "false"); + pl_sb_json_resize(*tPtrJson->psbcBuffer, tNewJsonObject.uValueOffset + tNewJsonObject.uValueLength); + snprintf(&(*tPtrJson->psbcBuffer)[tNewJsonObject.uValueOffset], tNewJsonObject.uValueLength, bValue ? "true" : "false"); + + pl_sb_json_push(tPtrJson->sbtChildren, tNewJsonObject); +} + +void +pl_json_add_string_member(plJsonObject* tPtrJson, const char* pcName, const char* pcValue) +{ + tPtrJson->uChildCount++; + tPtrJson->uChildrenFound++; + tPtrJson->tType = PL_JSON_TYPE_OBJECT; + + if(tPtrJson->psbcBuffer == NULL) + tPtrJson->psbcBuffer = &tPtrJson->sbcBuffer; + + plJsonObject tNewJsonObject = {0}; + tNewJsonObject.tType = PL_JSON_TYPE_STRING; + snprintf(tNewJsonObject.acName, PL_JSON_MAX_NAME_LENGTH, "%s", pcName); + tNewJsonObject.sbcBuffer = NULL; + tNewJsonObject.psbcBuffer = tPtrJson->psbcBuffer; + tNewJsonObject.uValueOffset = pl_sb_json_size(*tPtrJson->psbcBuffer); + tNewJsonObject.uValueLength = (uint32_t)strlen(pcValue); + pl_sb_json_resize(*tPtrJson->psbcBuffer, tNewJsonObject.uValueOffset + tNewJsonObject.uValueLength + 1); + strncpy(&(*tPtrJson->psbcBuffer)[tNewJsonObject.uValueOffset], pcValue, tNewJsonObject.uValueLength); + pl_sb_json_push(tPtrJson->sbtChildren, tNewJsonObject); +} + +void +pl_json_add_member(plJsonObject* tPtrJson, const char* pcName, plJsonObject* ptValue) +{ + tPtrJson->uChildCount++; + tPtrJson->uChildrenFound++; + tPtrJson->tType = PL_JSON_TYPE_OBJECT; + + if(tPtrJson->psbcBuffer == NULL) + tPtrJson->psbcBuffer = &tPtrJson->sbcBuffer; + + snprintf(ptValue->acName, PL_JSON_MAX_NAME_LENGTH, "%s", pcName); + pl_sb_json_push(tPtrJson->sbtChildren, *ptValue); +} + +void +pl_json_add_member_array(plJsonObject* tPtrJson, const char* pcName, plJsonObject* ptValues, uint32_t uSize) +{ + tPtrJson->uChildCount++; + tPtrJson->uChildrenFound++; + tPtrJson->tType = PL_JSON_TYPE_OBJECT; + + if(tPtrJson->psbcBuffer == NULL) + tPtrJson->psbcBuffer = &tPtrJson->sbcBuffer; + + plJsonObject tNewJsonObject = {0}; + tNewJsonObject.tType = PL_JSON_TYPE_ARRAY; + snprintf(tNewJsonObject.acName, PL_JSON_MAX_NAME_LENGTH, "%s", pcName); + tNewJsonObject.sbcBuffer = NULL; + tNewJsonObject.psbcBuffer = tPtrJson->psbcBuffer; + tNewJsonObject.uChildCount = uSize; + tNewJsonObject.uChildrenFound = uSize; + tNewJsonObject.sbuValueLength = NULL; + tNewJsonObject.sbuValueOffsets = NULL; + tNewJsonObject.sbtChildren = NULL; + pl_sb_json_resize(tNewJsonObject.sbtChildren, uSize); + + for(uint32_t i = 0; i < uSize; i++) + tNewJsonObject.sbtChildren[i] = ptValues[i]; + + pl_sb_json_push(tPtrJson->sbtChildren, tNewJsonObject); +} + +void +pl_json_add_int_array(plJsonObject* tPtrJson, const char* pcName, int* piValues, uint32_t uSize) +{ + tPtrJson->uChildCount++; + tPtrJson->uChildrenFound++; + tPtrJson->tType = PL_JSON_TYPE_OBJECT; + + if(tPtrJson->psbcBuffer == NULL) + tPtrJson->psbcBuffer = &tPtrJson->sbcBuffer; + + plJsonObject tNewJsonObject = {0}; + tNewJsonObject.tType = PL_JSON_TYPE_ARRAY; + snprintf(tNewJsonObject.acName, PL_JSON_MAX_NAME_LENGTH, "%s", pcName); + tNewJsonObject.sbcBuffer = NULL; + tNewJsonObject.psbcBuffer = tPtrJson->psbcBuffer; + tNewJsonObject.uChildCount = uSize; + tNewJsonObject.uChildrenFound = uSize; + tNewJsonObject.sbuValueLength = NULL; + tNewJsonObject.sbuValueOffsets = NULL; + pl_sb_json_resize(tNewJsonObject.sbuValueLength, uSize); + pl_sb_json_resize(tNewJsonObject.sbuValueOffsets, uSize); + + for(uint32_t i = 0; i < uSize; i++) + { + const uint32_t uValueOffset = pl_sb_json_size(*tPtrJson->psbcBuffer); + const uint32_t uValueLength = snprintf(NULL, 0, "%i", piValues[i]); + tNewJsonObject.sbuValueOffsets[i] = uValueOffset; + tNewJsonObject.sbuValueLength[i] = uValueLength; + + pl_sb_json_resize(*tPtrJson->psbcBuffer, uValueOffset + uValueLength + 1); + snprintf(&(*tPtrJson->psbcBuffer)[uValueOffset], uValueLength + 1, "%i", piValues[i]); + + } + + pl_sb_json_push(tPtrJson->sbtChildren, tNewJsonObject); +} + +void +pl_json_add_uint_array(plJsonObject* tPtrJson, const char* pcName, uint32_t* puValues, uint32_t uSize) +{ + tPtrJson->uChildCount++; + tPtrJson->uChildrenFound++; + tPtrJson->tType = PL_JSON_TYPE_OBJECT; + + if(tPtrJson->psbcBuffer == NULL) + tPtrJson->psbcBuffer = &tPtrJson->sbcBuffer; + + plJsonObject tNewJsonObject = {0}; + tNewJsonObject.tType = PL_JSON_TYPE_ARRAY; + snprintf(tNewJsonObject.acName, PL_JSON_MAX_NAME_LENGTH, "%s", pcName); + tNewJsonObject.sbcBuffer = NULL; + tNewJsonObject.psbcBuffer = tPtrJson->psbcBuffer; + tNewJsonObject.uChildCount = uSize; + tNewJsonObject.uChildrenFound = uSize; + tNewJsonObject.sbuValueLength = NULL; + tNewJsonObject.sbuValueOffsets = NULL; + pl_sb_json_resize(tNewJsonObject.sbuValueLength, uSize); + pl_sb_json_resize(tNewJsonObject.sbuValueOffsets, uSize); + + for(uint32_t i = 0; i < uSize; i++) + { + const uint32_t uValueOffset = pl_sb_json_size(*tPtrJson->psbcBuffer); + const uint32_t uValueLength = snprintf(NULL, 0, "%u", puValues[i]); + tNewJsonObject.sbuValueOffsets[i] = uValueOffset; + tNewJsonObject.sbuValueLength[i] = uValueLength; + + pl_sb_json_resize(*tPtrJson->psbcBuffer, uValueOffset + uValueLength + 1); + snprintf(&(*tPtrJson->psbcBuffer)[uValueOffset], uValueLength + 1, "%u", puValues[i]); + + } + + pl_sb_json_push(tPtrJson->sbtChildren, tNewJsonObject); +} + +void +pl_json_add_float_array(plJsonObject* tPtrJson, const char* pcName, float* pfValues, uint32_t uSize) +{ + tPtrJson->uChildCount++; + tPtrJson->uChildrenFound++; + tPtrJson->tType = PL_JSON_TYPE_OBJECT; + + if(tPtrJson->psbcBuffer == NULL) + tPtrJson->psbcBuffer = &tPtrJson->sbcBuffer; + + plJsonObject tNewJsonObject = {0}; + tNewJsonObject.tType = PL_JSON_TYPE_ARRAY; + snprintf(tNewJsonObject.acName, PL_JSON_MAX_NAME_LENGTH, "%s", pcName); + tNewJsonObject.sbcBuffer = NULL; + tNewJsonObject.psbcBuffer = tPtrJson->psbcBuffer; + tNewJsonObject.uChildCount = uSize; + tNewJsonObject.uChildrenFound = uSize; + tNewJsonObject.sbuValueLength = NULL; + tNewJsonObject.sbuValueOffsets = NULL; + pl_sb_json_resize(tNewJsonObject.sbuValueLength, uSize); + pl_sb_json_resize(tNewJsonObject.sbuValueOffsets, uSize); + + for(uint32_t i = 0; i < uSize; i++) + { + const uint32_t uValueOffset = pl_sb_json_size(*tPtrJson->psbcBuffer); + const uint32_t uValueLength = snprintf(NULL, 0, "%0.7f", pfValues[i]); + tNewJsonObject.sbuValueOffsets[i] = uValueOffset; + tNewJsonObject.sbuValueLength[i] = uValueLength; + + pl_sb_json_resize(*tPtrJson->psbcBuffer, uValueOffset + uValueLength + 1); + snprintf(&(*tPtrJson->psbcBuffer)[uValueOffset], uValueLength + 1, "%0.7f", pfValues[i]); + + } + + pl_sb_json_push(tPtrJson->sbtChildren, tNewJsonObject); +} + +void +pl_json_add_double_array(plJsonObject* tPtrJson, const char* pcName, double* pdValues, uint32_t uSize) +{ + tPtrJson->uChildCount++; + tPtrJson->uChildrenFound++; + tPtrJson->tType = PL_JSON_TYPE_OBJECT; + + if(tPtrJson->psbcBuffer == NULL) + tPtrJson->psbcBuffer = &tPtrJson->sbcBuffer; + + plJsonObject tNewJsonObject = {0}; + tNewJsonObject.tType = PL_JSON_TYPE_ARRAY; + snprintf(tNewJsonObject.acName, PL_JSON_MAX_NAME_LENGTH, "%s", pcName); + tNewJsonObject.sbcBuffer = NULL; + tNewJsonObject.psbcBuffer = tPtrJson->psbcBuffer; + tNewJsonObject.uChildCount = uSize; + tNewJsonObject.uChildrenFound = uSize; + tNewJsonObject.sbuValueLength = NULL; + tNewJsonObject.sbuValueOffsets = NULL; + pl_sb_json_resize(tNewJsonObject.sbuValueLength, uSize); + pl_sb_json_resize(tNewJsonObject.sbuValueOffsets, uSize); + + for(uint32_t i = 0; i < uSize; i++) + { + const uint32_t uValueOffset = pl_sb_json_size(*tPtrJson->psbcBuffer); + const uint32_t uValueLength = snprintf(NULL, 0, "%0.15f", pdValues[i]); + tNewJsonObject.sbuValueOffsets[i] = uValueOffset; + tNewJsonObject.sbuValueLength[i] = uValueLength; + + pl_sb_json_resize(*tPtrJson->psbcBuffer, uValueOffset + uValueLength + 1); + snprintf(&(*tPtrJson->psbcBuffer)[uValueOffset], uValueLength + 1, "%0.15f", pdValues[i]); + + } + + pl_sb_json_push(tPtrJson->sbtChildren, tNewJsonObject); +} + +void +pl_json_add_bool_array(plJsonObject* tPtrJson, const char* pcName, bool* pbValues, uint32_t uSize) +{ + tPtrJson->uChildCount++; + tPtrJson->uChildrenFound++; + tPtrJson->tType = PL_JSON_TYPE_OBJECT; + + if(tPtrJson->psbcBuffer == NULL) + tPtrJson->psbcBuffer = &tPtrJson->sbcBuffer; + + plJsonObject tNewJsonObject = {0}; + tNewJsonObject.tType = PL_JSON_TYPE_ARRAY; + snprintf(tNewJsonObject.acName, PL_JSON_MAX_NAME_LENGTH, "%s", pcName); + tNewJsonObject.sbcBuffer = NULL; + tNewJsonObject.psbcBuffer = tPtrJson->psbcBuffer; + tNewJsonObject.uChildCount = uSize; + tNewJsonObject.uChildrenFound = uSize; + tNewJsonObject.sbuValueLength = NULL; + tNewJsonObject.sbuValueOffsets = NULL; + pl_sb_json_resize(tNewJsonObject.sbuValueLength, uSize); + pl_sb_json_resize(tNewJsonObject.sbuValueOffsets, uSize); + + for(uint32_t i = 0; i < uSize; i++) + { + const uint32_t uValueOffset = pl_sb_json_size(*tPtrJson->psbcBuffer); + const uint32_t uValueLength = snprintf(NULL, 0, "%s", pbValues[i] ? "true" : "false"); + tNewJsonObject.sbuValueOffsets[i] = uValueOffset; + tNewJsonObject.sbuValueLength[i] = uValueLength; + + pl_sb_json_resize(*tPtrJson->psbcBuffer, uValueOffset + uValueLength + 1); + snprintf(&(*tPtrJson->psbcBuffer)[uValueOffset], uValueLength + 1, "%s", pbValues[i] ? "true" : "false"); + + } + + pl_sb_json_push(tPtrJson->sbtChildren, tNewJsonObject); +} + +void +pl_json_add_string_array(plJsonObject* tPtrJson, const char* pcName, char** ppcBuffer, uint32_t uSize) +{ + tPtrJson->uChildCount++; + tPtrJson->uChildrenFound++; + tPtrJson->tType = PL_JSON_TYPE_OBJECT; + + if(tPtrJson->psbcBuffer == NULL) + tPtrJson->psbcBuffer = &tPtrJson->sbcBuffer; + + plJsonObject tNewJsonObject = {0}; + tNewJsonObject.tType = PL_JSON_TYPE_ARRAY; + snprintf(tNewJsonObject.acName, PL_JSON_MAX_NAME_LENGTH, "%s", pcName); + tNewJsonObject.sbcBuffer = NULL; + tNewJsonObject.psbcBuffer = tPtrJson->psbcBuffer; + tNewJsonObject.uChildCount = uSize; + tNewJsonObject.uChildrenFound = uSize; + tNewJsonObject.sbuValueLength = NULL; + tNewJsonObject.sbuValueOffsets = NULL; + pl_sb_json_resize(tNewJsonObject.sbuValueLength, uSize); + pl_sb_json_resize(tNewJsonObject.sbuValueOffsets, uSize); + + for(uint32_t i = 0; i < uSize; i++) + { + const uint32_t uValueOffset = pl_sb_json_size(*tPtrJson->psbcBuffer); + const uint32_t uValueLength = snprintf(NULL, 0, "\"%s\"", ppcBuffer[i]); + tNewJsonObject.sbuValueOffsets[i] = uValueOffset; + tNewJsonObject.sbuValueLength[i] = uValueLength; + + pl_sb_json_resize(*tPtrJson->psbcBuffer, uValueOffset + uValueLength + 1); + snprintf(&(*tPtrJson->psbcBuffer)[uValueOffset], uValueLength + 1, "\"%s\"", ppcBuffer[i]); + + } + + pl_sb_json_push(tPtrJson->sbtChildren, tNewJsonObject); +} + +//----------------------------------------------------------------------------- +// [SECTION] internal api implementation +//----------------------------------------------------------------------------- + +static plJsonType +pl__get_json_token_object_type(const char* cPtrJson, jsmntok_t* tPtrToken) +{ + switch (tPtrToken->type) + { + case JSMN_ARRAY: return PL_JSON_TYPE_ARRAY; + case JSMN_OBJECT: return PL_JSON_TYPE_OBJECT; + case JSMN_STRING: return PL_JSON_TYPE_STRING; + case JSMN_PRIMITIVE: + if (cPtrJson[tPtrToken->start] == 'n') { return PL_JSON_TYPE_NULL;} + else if(cPtrJson[tPtrToken->start] == 't' || cPtrJson[tPtrToken->start] == 'f') { return PL_JSON_TYPE_BOOL;} + else { return PL_JSON_TYPE_NUMBER;} + default: + PL_ASSERT(false); + break; + } + return PL_JSON_TYPE_UNSPECIFIED; +} + +static void +pl__write_json_object(plJsonObject* ptJson, char* pcBuffer, uint32_t* puBufferSize, uint32_t* puCursor, uint32_t* puDepth) +{ + uint32_t uBufferSize = *puBufferSize; + uint32_t uCursorPosition = *puCursor; + + switch(ptJson->tType) + { + + case PL_JSON_TYPE_NULL: + { + int iSizeNeeded = snprintf(NULL, 0, "null"); + snprintf(&pcBuffer[uCursorPosition], iSizeNeeded + 1, "null"); + uCursorPosition += iSizeNeeded; + break; + } + + case PL_JSON_TYPE_BOOL: + { + int iSizeNeeded = snprintf(NULL, 0, "%s", (&(*ptJson->psbcBuffer)[ptJson->uValueOffset])[0] == 't' ? "true" : "false"); + snprintf(&pcBuffer[uCursorPosition], iSizeNeeded + 1, "%s", (&(*ptJson->psbcBuffer)[ptJson->uValueOffset])[0] == 't' ? "true" : "false"); + uCursorPosition += iSizeNeeded; + break; + } + + case PL_JSON_TYPE_NUMBER: + { + memcpy(&pcBuffer[uCursorPosition], &(*ptJson->psbcBuffer)[ptJson->uValueOffset], ptJson->uValueLength); + uCursorPosition += ptJson->uValueLength; + break; + } + + case PL_JSON_TYPE_STRING: + { + int iSizeNeeded = snprintf(&pcBuffer[uCursorPosition], (int)ptJson->uValueLength + 2 + 1, "\"%s\"", &(*ptJson->psbcBuffer)[ptJson->uValueOffset]); + uCursorPosition += iSizeNeeded; + break; + } + + case PL_JSON_TYPE_OBJECT: + { + + *puDepth += 1; + int iSizeNeeded = 1 + *puDepth * 4; + pcBuffer[uCursorPosition] = '{'; + memset(&pcBuffer[uCursorPosition + 1], 0x20, iSizeNeeded - 1); + uCursorPosition += iSizeNeeded; + + for(uint32_t i = 0; i < ptJson->uChildCount; i++) + { + + int iSizeNeeded3 = *puDepth * 4 + 1; + pcBuffer[uCursorPosition] = '\n'; + memset(&pcBuffer[uCursorPosition + 1], 0x20, iSizeNeeded3); + uCursorPosition += iSizeNeeded3; + + int iSizeNeeded2 = snprintf(NULL, 0, "\"%s\": ", ptJson->sbtChildren[i].acName); + snprintf(&pcBuffer[uCursorPosition], iSizeNeeded2 + 1 , "\"%s\": ", ptJson->sbtChildren[i].acName); + + uCursorPosition += iSizeNeeded2; + + pl__write_json_object(&ptJson->sbtChildren[i], pcBuffer, &uBufferSize, &uCursorPosition, puDepth); + + if(i != ptJson->uChildCount - 1) + { + pcBuffer[uCursorPosition] = ','; + pcBuffer[uCursorPosition + 1] = ' '; + uCursorPosition += 2; + } + } + + *puDepth -= 1; + + int iSizeNeeded3 = *puDepth * 4 + 2; + pcBuffer[uCursorPosition] = '\n'; + memset(&pcBuffer[uCursorPosition + 1], 0x20, iSizeNeeded3 - 2); + uCursorPosition += iSizeNeeded3; + pcBuffer[uCursorPosition - 1] = '}'; + break; + } + + case PL_JSON_TYPE_ARRAY: + { + + *puDepth += 1; + int iSizeNeeded = 2 + *puDepth * 4; + pcBuffer[uCursorPosition] = '['; + pcBuffer[uCursorPosition + 1] = '\n'; + memset(&pcBuffer[uCursorPosition + 2], 0x20, iSizeNeeded - 2); + uCursorPosition += iSizeNeeded; + + + if(ptJson->sbuValueOffsets) + { + for(uint32_t i = 0; i < ptJson->uChildCount; i++) + { + + // pl__write_json_object(&ptJson->sbtChildren[i], pcBuffer, &uBufferSize, &uCursorPosition, puDepth); + + const char* cPtrPrevChar = &(*ptJson->psbcBuffer)[ptJson->sbuValueOffsets[i]]; + char cPreviousChar = ' '; + if(cPtrPrevChar) + { + const char* cPtrPrevCharAddr = cPtrPrevChar - 1; + cPreviousChar = cPtrPrevCharAddr[0]; + } + + + if(cPreviousChar == '\"') + { + int iSizeNeeded2 = ptJson->sbuValueLength[i] + 2; + snprintf(&pcBuffer[uCursorPosition], iSizeNeeded2 + 1, "\"%s\"", &(*ptJson->psbcBuffer)[ptJson->sbuValueOffsets[i]]); + uCursorPosition += iSizeNeeded2; + } + else + { + memcpy(&pcBuffer[uCursorPosition], &(*ptJson->psbcBuffer)[ptJson->sbuValueOffsets[i]], ptJson->sbuValueLength[i]); + uCursorPosition += ptJson->sbuValueLength[i]; + } + + if(i != ptJson->uChildCount - 1) + { + pcBuffer[uCursorPosition] = ','; + pcBuffer[uCursorPosition + 1] = ' '; + uCursorPosition += 2; + } + } + } + else + { + for(uint32_t i = 0; i < ptJson->uChildCount; i++) + { + + pl__write_json_object(&ptJson->sbtChildren[i], pcBuffer, &uBufferSize, &uCursorPosition, puDepth); + + if(i != ptJson->uChildCount - 1) + { + pcBuffer[uCursorPosition] = ','; + pcBuffer[uCursorPosition + 1] = ' '; + uCursorPosition += 2; + } + } + } + + *puDepth -= 1; + + int iSizeNeeded3 = *puDepth * 4 + 2; + pcBuffer[uCursorPosition] = '\n'; + memset(&pcBuffer[uCursorPosition + 1], 0x20, iSizeNeeded3 - 2); + uCursorPosition += iSizeNeeded3; + pcBuffer[uCursorPosition - 1] = ']'; + break; + } + } + + *puBufferSize = uBufferSize; + *puCursor = uCursorPosition; +} + +static void +pl__check_json_object(plJsonObject* ptJson, uint32_t* puBufferSize, uint32_t* puCursor, uint32_t* puDepth) +{ + uint32_t uBufferSize = *puBufferSize; + uint32_t uCursorPosition = *puCursor; + + switch(ptJson->tType) + { + + case PL_JSON_TYPE_NULL: + { + int iSizeNeeded = snprintf(NULL, 0, "null"); + uCursorPosition += iSizeNeeded; + break; + } + + case PL_JSON_TYPE_BOOL: + { + + int iSizeNeeded = snprintf(NULL, 0, "%s", (&(*ptJson->psbcBuffer)[ptJson->uValueOffset])[0] == 't' ? "true" : "false"); + uCursorPosition += iSizeNeeded; + break; + } + + case PL_JSON_TYPE_NUMBER: + { + uCursorPosition += ptJson->uValueLength; + break; + } + + case PL_JSON_TYPE_STRING: + { + int iSizeNeeded = (int)ptJson->uValueLength + 2; + uCursorPosition += iSizeNeeded; + break; + } + + case PL_JSON_TYPE_OBJECT: + { + + *puDepth += 1; + int iSizeNeeded = 1 + *puDepth * 4; + uCursorPosition += iSizeNeeded; + + for(uint32_t i = 0; i < ptJson->uChildCount; i++) + { + + int iSizeNeeded3 = *puDepth * 4 + 1; + uCursorPosition += iSizeNeeded3; + + int iSizeNeeded2 = snprintf(NULL, 0, "\"%s\": ", ptJson->sbtChildren[i].acName); + uCursorPosition += iSizeNeeded2; + + pl__check_json_object(&ptJson->sbtChildren[i], &uBufferSize, &uCursorPosition, puDepth); + + if(i != ptJson->uChildCount - 1) + uCursorPosition += 2; + } + + *puDepth -= 1; + + int iSizeNeeded3 = *puDepth * 4 + 2; + uCursorPosition += iSizeNeeded3; + break; + } + + case PL_JSON_TYPE_ARRAY: + { + + *puDepth += 1; + int iSizeNeeded = 2 + *puDepth * 4; + uCursorPosition += iSizeNeeded; + + if(ptJson->sbuValueOffsets) + { + for(uint32_t i = 0; i < ptJson->uChildCount; i++) + { + + // pl__check_json_object(&ptJson->sbtChildren[i], &uBufferSize, &uCursorPosition, puDepth); + + const char* cPtrPrevChar = &(*ptJson->psbcBuffer)[ptJson->sbuValueOffsets[i]]; + char cPreviousChar = ' '; + if(cPtrPrevChar) + { + const char* cPtrPrevCharAddr = cPtrPrevChar - 1; + cPreviousChar = cPtrPrevCharAddr[0]; + } + + if(cPreviousChar == '\"') + { + int iSizeNeeded2 = ptJson->sbuValueLength[i] + 2; + uCursorPosition += iSizeNeeded2; + } + else + uCursorPosition += ptJson->sbuValueLength[i]; + + + if(i != ptJson->uChildCount - 1) + uCursorPosition += 2; + } + } + else + { + for(uint32_t i = 0; i < ptJson->uChildCount; i++) + { + + pl__check_json_object(&ptJson->sbtChildren[i], &uBufferSize, &uCursorPosition, puDepth); + + if(i != ptJson->uChildCount - 1) + uCursorPosition += 2; + } + } + + *puDepth -= 1; + int iSizeNeeded3 = *puDepth * 4 + 2; + uCursorPosition += iSizeNeeded3; + break; + } + } + + *puBufferSize = uBufferSize; + *puCursor = uCursorPosition; +} + +#endif // PL_JSON_IMPLEMENTATION diff --git a/pl_log.h b/pl_log.h new file mode 100644 index 0000000..5d8fcdb --- /dev/null +++ b/pl_log.h @@ -0,0 +1,1262 @@ +/* + pl_log + Do this: + #define PL_LOG_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define PL_LOG_IMPLEMENTATION + #include "pl_log.h" +*/ + +// library version +#define PL_LOG_VERSION "0.5.2" +#define PL_LOG_VERSION_NUM 00502 + +/* +Index of this file: +// [SECTION] documentation +// [SECTION] header mess +// [SECTION] includes +// [SECTION] defines +// [SECTION] forward declarations & basic types +// [SECTION] public api +// [SECTION] enums +// [SECTION] structs +// [SECTION] internal api +// [SECTION] c file start +*/ + +//----------------------------------------------------------------------------- +// [SECTION] documentation +//----------------------------------------------------------------------------- + +/* + +SETUP + + pl_create_log_context: + plLogContext* pl_create_log_context(); + Creates the global context used by the logging system. Store the + pointer returned if you want to use the logger across DLL boundaries. + See "pl_set_log_context". + + pl_cleanup_log_context: + void pl_cleanup_log_context(); + Frees memory associated with the logging system. Do not call functions + after this. + + pl_set_log_context: + void pl_set_log_context(plLogContext*); + Sets the current log context. Mostly used to allow logging across + DLL boundaries. + + pl_get_log_context: + plLogContext* pl_get_log_context(); + Returns the current log context. + +CHANNELS + + pl_add_log_channel: + uint32_t pl_add_log_channel(const char* pcName, plChannelType tType); + Creates a new log channel and returns the ID. + + pl_set_log_level: + void pl_set_log_level(uID, uLevel); + Sets the runtime logging level of the uID channel + + pl_set_current_log_channel: + void pl_set_current_log_channel(uID); + Sets the current logging channel + + pl_get_current_log_channel: + uint32_t pl_get_current_log_channel(); + Gets the current logging channel + + pl_clear_log_level: + void pl_clear_log_level(uID); + Frees the memory associated with log entries (for buffer channel types). + + pl_reset_log_level: + void pl_reset_log_level(uID); + Resets the log channel but does not free the memory. + + pl_get_log_entries: + plLogEntry* pl_get_log_entries(uID, uint64_t* puEntryCount); + Returns a pointer to the log entries (or NULL if empty). Fills out puEntryCount + with the count. + + pl_get_log_channels: + plLogChannel* pl_get_log_channels(uint32_t* puChannelCount); + Returns a pointer to the log channels (or NULL if empty). Fills out puChannelCount + with the count. + +SIMPLE LOGGING TO CURRENT CHANNEL + + pl_log_trace + pl_log_debug + pl_log_info + pl_log_warn + pl_log_error + pl_log_fatal: + void pl_log_*(pcMessage); + Logs at the specified level. No color information. Faster if in a tight loop. + +LOGGING TO CURRENT CHANNEL WITH FORMAT SPECIFIERS + + pl_log_trace_f + pl_log_debug_f + pl_log_info_f + pl_log_warn_f + pl_log_error_f + pl_log_fatal_f: + void pl_log_*_f(pcFormatString, ...); + Logs at the specified level. Includes color when console. + +SIMPLE LOGGING TO SPECIFIED CHANNEL + + pl_log_trace_to + pl_log_debug_to + pl_log_info_to + pl_log_warn_to + pl_log_error_to + pl_log_fatal_to; + void pl_log_*_to(uID, pcMessage); + Logs at the specified level. No color information. Faster if in a tight loop. + +LOGGING TO SPECIFIED CHANNEL WITH FORMAT SPECIFIERS + + pl_log_trace_to_f + pl_log_debug_to_f + pl_log_info_to_f + pl_log_warn_to_f + pl_log_error_to_f + pl_log_fatal_to_f; + void pl_log_*_to_f(uID, pcFormatString, ...); + Logs at the specified level. Includes color when console. + +LOG LEVELS + PL_LOG_LEVEL_ALL + PL_LOG_LEVEL_TRACE + PL_LOG_LEVEL_DEBUG + PL_LOG_LEVEL_INFO + PL_LOG_LEVEL_WARN + PL_LOG_LEVEL_ERROR + PL_LOG_LEVEL_FATAL + PL_LOG_LEVEL_OFF + +COMPILE TIME OPTIONS + + * Change maximum number of channels, define PL_LOG_MAX_CHANNEL_COUNT. (default is 16) + * Change maximum number of entries for cyclic loggers, define PL_LOG_CYCLIC_BUFFER_SIZE. (default is 256) + * Change initial number of log entries for buffer loggers, define PL_LOG_INITIAL_LOG_SIZE. (default is 1024) + * Change the global log level, define PL_GLOBAL_LOG_LEVEL. (default is PL_LOG_LEVEL_ALL) + * Change background colors by defining the following: + PL_LOG_TRACE_BG_COLOR + PL_LOG_DEBUG_BG_COLOR + PL_LOG_INFO_BG_COLOR + PL_LOG_WARN_BG_COLOR + PL_LOG_ERROR_BG_COLOR + PL_LOG_FATAL_BG_COLOR + * Change foreground colors by defining the following: + PL_LOG_TRACE_FG_COLOR + PL_LOG_DEBUG_FG_COLOR + PL_LOG_INFO_FG_COLOR + PL_LOG_WARN_FG_COLOR + PL_LOG_ERROR_FG_COLOR + PL_LOG_FATAL_FG_COLOR + * Use bold by defining the following: + PL_LOG_TRACE_BOLD + PL_LOG_DEBUG_BOLD + PL_LOG_INFO_BOLD + PL_LOG_WARN_BOLD + PL_LOG_ERROR_BOLD + PL_LOG_FATAL_BOLD + * Use underline by defining the following: + PL_LOG_TRACE_UNDERLINE + PL_LOG_DEBUG_UNDERLINE + PL_LOG_INFO_UNDERLINE + PL_LOG_WARN_UNDERLINE + PL_LOG_ERROR_UNDERLINE + PL_LOG_FATAL_UNDERLINE + * Change allocators by defining both: + PL_LOG_ALLOC(x) + PL_LOG_FREE(x) + +FOREGROUND COLOR OPTIONS + + PL_LOG_FG_COLOR_CODE_BLACK + PL_LOG_FG_COLOR_CODE_RED + PL_LOG_FG_COLOR_CODE_GREEN + PL_LOG_FG_COLOR_CODE_YELLOW + PL_LOG_FG_COLOR_CODE_BLUE + PL_LOG_FG_COLOR_CODE_MAGENTA + PL_LOG_FG_COLOR_CODE_CYAN + PL_LOG_FG_COLOR_CODE_WHITE + PL_LOG_FG_COLOR_CODE_STRONG_BLACK + PL_LOG_FG_COLOR_CODE_STRONG_RED + PL_LOG_FG_COLOR_CODE_STRONG_GREEN + PL_LOG_FG_COLOR_CODE_STRONG_YELLOW + PL_LOG_FG_COLOR_CODE_STRONG_BLUE + PL_LOG_FG_COLOR_CODE_STRONG_MAGENTA + PL_LOG_FG_COLOR_CODE_STRONG_CYAN + PL_LOG_FG_COLOR_CODE_STRONG_WHITE + +BACKGROUND COLOR OPTIONS + + PL_LOG_BG_COLOR_CODE_BLACK + PL_LOG_BG_COLOR_CODE_RED + PL_LOG_BG_COLOR_CODE_GREEN + PL_LOG_BG_COLOR_CODE_YELLOW + PL_LOG_BG_COLOR_CODE_BLUE + PL_LOG_BG_COLOR_CODE_MAGENTA + PL_LOG_BG_COLOR_CODE_CYAN + PL_LOG_BG_COLOR_CODE_WHITE + PL_LOG_BG_COLOR_CODE_STRONG_BLACK + PL_LOG_BG_COLOR_CODE_STRONG_RED + PL_LOG_BG_COLOR_CODE_STRONG_GREEN + PL_LOG_BG_COLOR_CODE_STRONG_YELLOW + PL_LOG_BG_COLOR_CODE_STRONG_BLUE + PL_LOG_BG_COLOR_CODE_STRONG_MAGENTA + PL_LOG_BG_COLOR_CODE_STRONG_CYAN + PL_LOG_BG_COLOR_CODE_STRONG_WHITE + +*/ + +//----------------------------------------------------------------------------- +// [SECTION] header mess +//----------------------------------------------------------------------------- + +#ifndef PL_LOG_H +#define PL_LOG_H + +#ifndef PL_LOG_ALLOC + #include + #define PL_LOG_ALLOC(x) malloc((x)) + #define PL_LOG_FREE(x) free((x)) +#endif + +//----------------------------------------------------------------------------- +// [SECTION] includes +//----------------------------------------------------------------------------- + +#include +#include + +//----------------------------------------------------------------------------- +// [SECTION] defines +//----------------------------------------------------------------------------- + +#ifndef PL_LOG_MAX_LINE_SIZE + #define PL_LOG_MAX_LINE_SIZE 1024 +#endif + +#ifndef PL_LOG_CYCLIC_BUFFER_SIZE + #define PL_LOG_CYCLIC_BUFFER_SIZE 256 +#endif + +#ifndef PL_LOG_INITIAL_LOG_SIZE + #define PL_LOG_INITIAL_LOG_SIZE 1024 +#endif + +#define PL_LOG_LEVEL_ALL 0 +#define PL_LOG_LEVEL_TRACE 5000 +#define PL_LOG_LEVEL_DEBUG 6000 +#define PL_LOG_LEVEL_INFO 7000 +#define PL_LOG_LEVEL_WARN 8000 +#define PL_LOG_LEVEL_ERROR 9000 +#define PL_LOG_LEVEL_FATAL 10000 +#define PL_LOG_LEVEL_OFF 11000 + +#ifdef PL_LOG_ON + #ifndef PL_GLOBAL_LOG_LEVEL + #define PL_GLOBAL_LOG_LEVEL PL_LOG_LEVEL_ALL + #endif +#else + #undef PL_GLOBAL_LOG_LEVEL + #define PL_GLOBAL_LOG_LEVEL PL_LOG_LEVEL_OFF +#endif + +//----------------------------------------------------------------------------- +// [SECTION] forward declarations & basic types +//----------------------------------------------------------------------------- + +// forward declarations +typedef struct _plLogContext plLogContext; // opaque struct +typedef struct _plLogEntry plLogEntry; // represents a single entry for "buffer" channel types + +// enums +typedef int plChannelType; + +//----------------------------------------------------------------------------- +// [SECTION] public api +//----------------------------------------------------------------------------- + +#ifdef PL_LOG_ON + + // setup/shutdown + #define pl_create_log_context() pl__create_log_context() + #define pl_cleanup_log_context() pl__cleanup_log_context() + #define pl_set_log_context(tPContext) pl__set_log_context((tPContext)) + #define pl_get_log_context() pl__get_log_context() + + // channels + #define pl_add_log_channel(pcName, tType) pl__add_log_channel((pcName), (tType)) + #define pl_set_log_level(uID, uLevel) pl__set_log_level((uID), (uLevel)) + #define pl_set_current_log_channel(uID) pl__set_current_log_channel((uID)) + #define pl_get_current_log_channel() pl__get_current_log_channel() + #define pl_clear_log_channel(uID) pl__clear_log_channel((uID)) + #define pl_reset_log_channel(uID) pl__reset_log_channel((uID)) + #define pl_get_log_entries(uID, puEntryCount) pl__get_log_entries((uID), (puEntryCount)) + #define pl_get_log_channels(puChannelCount) pl__get_log_channels((puChannelCount)) + +#endif // PL_LOG_ON + +#if PL_GLOBAL_LOG_LEVEL < PL_LOG_LEVEL_TRACE + 1 && defined(PL_LOG_ON) + #define pl_log_trace(pcMessage) pl__log_trace(pl__get_current_log_channel(), (pcMessage)) + #define pl_log_trace_f(...) pl__log_trace_p(pl__get_current_log_channel(), __VA_ARGS__) + #define pl_log_trace_to(uID, pcMessage) pl__log_trace((uID), (pcMessage)) + #define pl_log_trace_to_f(...) pl__log_trace_p(__VA_ARGS__) +#else + #define pl_log_trace(pcMessage) // + #define pl_log_trace_f(...) // + #define pl_log_trace_to(uID, pcMessage) // + #define pl_log_trace_to_f(...) // +#endif + +#if PL_GLOBAL_LOG_LEVEL < PL_LOG_LEVEL_DEBUG + 1 && defined(PL_LOG_ON) + #define pl_log_debug(pcMessage) pl__log_debug(pl__get_current_log_channel(), (pcMessage)) + #define pl_log_debug_f(...) pl__log_debug_p(pl__get_current_log_channel(), __VA_ARGS__) + #define pl_log_debug_to(uID, pcMessage) pl__log_debug((uID), (pcMessage)) + #define pl_log_debug_to_f(...) pl__log_debug_p(__VA_ARGS__) +#else + #define pl_log_debug(pcMessage) // + #define pl_log_debug_f(...) // + #define pl_log_debug_to(uID, pcMessage) // + #define pl_log_debug_to_f(...) // +#endif + +#if PL_GLOBAL_LOG_LEVEL < PL_LOG_LEVEL_INFO + 1 && defined(PL_LOG_ON) + #define pl_log_info(pcMessage) pl__log_info(pl__get_current_log_channel(), (pcMessage)) + #define pl_log_info_f(...) pl__log_info_p(pl__get_current_log_channel(), __VA_ARGS__) + #define pl_log_info_to(uID, pcMessage) pl__log_info((uID), (pcMessage)) + #define pl_log_info_to_f(...) pl__log_info_p(__VA_ARGS__) +#else + #define pl_log_info(pcMessage) // + #define pl_log_info_f(...) // + #define pl_log_info_to(uID, pcMessage) // + #define pl_log_info_to_f(...) // +#endif + +#if PL_GLOBAL_LOG_LEVEL < PL_LOG_LEVEL_WARN + 1 && defined(PL_LOG_ON) + #define pl_log_warn(pcMessage) pl__log_warn(pl__get_current_log_channel(), (pcMessage)) + #define pl_log_warn_f(...) pl__log_warn_p(pl__get_current_log_channel(), __VA_ARGS__) + #define pl_log_warn_to(uID, pcMessage) pl__log_warn((uID), (pcMessage)) + #define pl_log_warn_to_f(...) pl__log_warn_p(__VA_ARGS__) +#else + #define pl_log_warn(pcMessage) // + #define pl_log_warn_f(...) // + #define pl_log_warn_to(tPContext, uID) // + #define pl_log_warn_to_f(...) // +#endif + +#if PL_GLOBAL_LOG_LEVEL < PL_LOG_LEVEL_ERROR + 1 && defined(PL_LOG_ON) + #define pl_log_error(pcMessage) pl__log_error(pl__get_current_log_channel(), (pcMessage)) + #define pl_log_error_f(...) pl__log_error_p(pl__get_current_log_channel(), __VA_ARGS__) + #define pl_log_error_to(uID, pcMessage) pl__log_error((uID), (pcMessage)) + #define pl_log_error_to_f(...) pl__log_error_p(__VA_ARGS__) +#else + #define pl_log_error(pcMessage) // + #define pl_log_error_f(...) // + #define pl_log_error_to(tPContext, uID) // + #define pl_log_error_to_f(...) // +#endif + +#if PL_GLOBAL_LOG_LEVEL < PL_LOG_LEVEL_FATAL + 1 && defined(PL_LOG_ON) + #define pl_log_fatal(pcMessage) pl__log_fatal(pl__get_current_log_channel(), (pcMessage)) + #define pl_log_fatal_f(...) pl__log_fatal_p(pl__get_current_log_channel(), __VA_ARGS__) + #define pl_log_fatal_to(uID, pcMessage) pl__log_fatal((uID), (pcMessage)) + #define pl_log_fatal_to_f(...) pl__log_fatal_p(__VA_ARGS__) +#else + #define pl_log_fatal(pcMessage) // + #define pl_log_fatal_f(...) // + #define pl_log_fatal_to(uID, pcMessage) // + #define pl_log_fatal_to_f(...) // +#endif + +//----------------------------------------------------------------------------- +// [SECTION] enums +//----------------------------------------------------------------------------- + +enum plChannelType_ +{ + PL_CHANNEL_TYPE_DEFAULT = 0, + PL_CHANNEL_TYPE_CONSOLE = 1 << 0, + PL_CHANNEL_TYPE_BUFFER = 1 << 1, + PL_CHANNEL_TYPE_CYCLIC_BUFFER = 1 << 2 +}; + +//----------------------------------------------------------------------------- +// [SECTION] structs +//----------------------------------------------------------------------------- + +typedef struct _plLogEntry +{ + uint32_t uLevel; + uint64_t uOffset; + uint64_t uGeneration; +} plLogEntry; + +typedef struct _plLogChannel +{ + const char* pcName; + bool bOverflowInUse; + char* pcBuffer0; + char* pcBuffer1; + uint64_t uGeneration; + uint64_t uBufferSize; + uint64_t uBufferCapacity; + plLogEntry atEntries[PL_LOG_CYCLIC_BUFFER_SIZE]; + plLogEntry* pEntries; + uint64_t uEntryCount; + uint64_t uEntryCapacity; + uint64_t uNextEntry; + uint32_t uLevel; + plChannelType tType; + uint32_t uID; +} plLogChannel; + +//----------------------------------------------------------------------------- +// [SECTION] internal api +//----------------------------------------------------------------------------- + +// setup/shutdown +plLogContext* pl__create_log_context (void); +void pl__cleanup_log_context(void); +void pl__set_log_context (plLogContext* tPContext); +plLogContext* pl__get_log_context (void); + +// channels +uint32_t pl__add_log_channel (const char* pcName, plChannelType tType); +uint32_t pl__get_current_log_channel(void); +void pl__set_current_log_channel(uint32_t uID); +void pl__set_log_level (uint32_t uID, uint32_t uLevel); +void pl__clear_log_channel (uint32_t uID); +void pl__reset_log_channel (uint32_t uID); +plLogEntry* pl__get_log_entries (uint32_t uID, uint64_t* puEntryCount); +plLogChannel* pl__get_log_channels (uint32_t* puChannelCount); + +// logging +void pl__log_trace(uint32_t uID, const char* pcMessage); +void pl__log_debug(uint32_t uID, const char* pcMessage); +void pl__log_info (uint32_t uID, const char* pcMessage); +void pl__log_warn (uint32_t uID, const char* pcMessage); +void pl__log_error(uint32_t uID, const char* pcMessage); +void pl__log_fatal(uint32_t uID, const char* pcMessage); + +void pl__log_trace_p(uint32_t uID, const char* cPFormat, ...); +void pl__log_debug_p(uint32_t uID, const char* cPFormat, ...); +void pl__log_info_p (uint32_t uID, const char* cPFormat, ...); +void pl__log_warn_p (uint32_t uID, const char* cPFormat, ...); +void pl__log_error_p(uint32_t uID, const char* cPFormat, ...); +void pl__log_fatal_p(uint32_t uID, const char* cPFormat, ...); + +void pl__log_trace_va(uint32_t uID, const char* cPFormat, va_list args); +void pl__log_debug_va(uint32_t uID, const char* cPFormat, va_list args); +void pl__log_info_va (uint32_t uID, const char* cPFormat, va_list args); +void pl__log_warn_va (uint32_t uID, const char* cPFormat, va_list args); +void pl__log_error_va(uint32_t uID, const char* cPFormat, va_list args); +void pl__log_fatal_va(uint32_t uID, const char* cPFormat, va_list args); + +#ifndef PL_LOG_ON + #define pl_create_log_context() NULL + #define pl_cleanup_log_context() // + #define pl_set_log_context(ctx) // + #define pl_get_log_context() NULL + #define pl_add_log_channel(pcName, tType) 0u + #define pl_set_log_level(uID, uLevel) // + #define pl_clear_log_channel(uID) // + #define pl_reset_log_channel(uID) // + #define pl_get_log_entries(uID, puEntryCount) NULL + #define pl_set_current_log_channel(uID) // + #define pl_get_current_log_channel(uID) 0 + #define pl_get_log_channels(puChannelCount) NULL +#endif + +#endif // PL_LOG_H + +//----------------------------------------------------------------------------- +// [SECTION] c file start +//----------------------------------------------------------------------------- + +/* +Index of this file: +// [SECTION] defines +// [SECTION] includes +// [SECTION] internal structs +// [SECTION] global context +// [SECTION] internal api +// [SECTION] public api implementation +// [SECTION] internal api implementation +*/ + +//----------------------------------------------------------------------------- +// [SECTION] defines +//----------------------------------------------------------------------------- + +#ifdef PL_LOG_IMPLEMENTATION + +#ifndef PL_LOG_MAX_CHANNEL_COUNT + #define PL_LOG_MAX_CHANNEL_COUNT 16 +#endif + +#ifdef _WIN32 +#define PL_LOG_BOLD_CODE "" +#define PL_LOG_UNDERLINE_CODE "" +#define PL_LOG_POP_CODE "" + +#define PL_LOG_FG_COLOR_CODE_BLACK "" +#define PL_LOG_FG_COLOR_CODE_RED "" +#define PL_LOG_FG_COLOR_CODE_GREEN "" +#define PL_LOG_FG_COLOR_CODE_YELLOW "" +#define PL_LOG_FG_COLOR_CODE_BLUE "" +#define PL_LOG_FG_COLOR_CODE_MAGENTA "" +#define PL_LOG_FG_COLOR_CODE_CYAN "" +#define PL_LOG_FG_COLOR_CODE_WHITE "" +#define PL_LOG_FG_COLOR_CODE_STRONG_BLACK "" +#define PL_LOG_FG_COLOR_CODE_STRONG_RED "" +#define PL_LOG_FG_COLOR_CODE_STRONG_GREEN "" +#define PL_LOG_FG_COLOR_CODE_STRONG_YELLOW "" +#define PL_LOG_FG_COLOR_CODE_STRONG_BLUE "" +#define PL_LOG_FG_COLOR_CODE_STRONG_MAGENTA "" +#define PL_LOG_FG_COLOR_CODE_STRONG_CYAN "" +#define PL_LOG_FG_COLOR_CODE_STRONG_WHITE "" + +#define PL_LOG_BG_COLOR_CODE_BLACK "" +#define PL_LOG_BG_COLOR_CODE_RED "" +#define PL_LOG_BG_COLOR_CODE_GREEN "" +#define PL_LOG_BG_COLOR_CODE_YELLOW "" +#define PL_LOG_BG_COLOR_CODE_BLUE "" +#define PL_LOG_BG_COLOR_CODE_MAGENTA "" +#define PL_LOG_BG_COLOR_CODE_CYAN "" +#define PL_LOG_BG_COLOR_CODE_WHITE "" +#define PL_LOG_BG_COLOR_CODE_STRONG_BLACK "" +#define PL_LOG_BG_COLOR_CODE_STRONG_RED "" +#define PL_LOG_BG_COLOR_CODE_STRONG_GREEN "" +#define PL_LOG_BG_COLOR_CODE_STRONG_YELLOW "" +#define PL_LOG_BG_COLOR_CODE_STRONG_BLUE "" +#define PL_LOG_BG_COLOR_CODE_STRONG_MAGENTA "" +#define PL_LOG_BG_COLOR_CODE_STRONG_CYAN "" +#define PL_LOG_BG_COLOR_CODE_STRONG_WHITE "" + +#else + +#define PL_LOG_BOLD_CODE "\033[1m" +#define PL_LOG_UNDERLINE_CODE "\033[4m" +#define PL_LOG_POP_CODE "\033[0m" + +#define PL_LOG_FG_COLOR_CODE_BLACK "\033[30m" +#define PL_LOG_FG_COLOR_CODE_RED "\033[31m" +#define PL_LOG_FG_COLOR_CODE_GREEN "\033[32m" +#define PL_LOG_FG_COLOR_CODE_YELLOW "\033[33m" +#define PL_LOG_FG_COLOR_CODE_BLUE "\033[34m" +#define PL_LOG_FG_COLOR_CODE_MAGENTA "\033[35m" +#define PL_LOG_FG_COLOR_CODE_CYAN "\033[36m" +#define PL_LOG_FG_COLOR_CODE_WHITE "\033[37m" +#define PL_LOG_FG_COLOR_CODE_STRONG_BLACK "\033[90m" +#define PL_LOG_FG_COLOR_CODE_STRONG_RED "\033[91m" +#define PL_LOG_FG_COLOR_CODE_STRONG_GREEN "\033[92m" +#define PL_LOG_FG_COLOR_CODE_STRONG_YELLOW "\033[93m" +#define PL_LOG_FG_COLOR_CODE_STRONG_BLUE "\033[94m" +#define PL_LOG_FG_COLOR_CODE_STRONG_MAGENTA "\033[95m" +#define PL_LOG_FG_COLOR_CODE_STRONG_CYAN "\033[96m" +#define PL_LOG_FG_COLOR_CODE_STRONG_WHITE "\033[97m" + +#define PL_LOG_BG_COLOR_CODE_BLACK "\033[40m" +#define PL_LOG_BG_COLOR_CODE_RED "\033[41m" +#define PL_LOG_BG_COLOR_CODE_GREEN "\033[42m" +#define PL_LOG_BG_COLOR_CODE_YELLOW "\033[43m" +#define PL_LOG_BG_COLOR_CODE_BLUE "\033[44m" +#define PL_LOG_BG_COLOR_CODE_MAGENTA "\033[45m" +#define PL_LOG_BG_COLOR_CODE_CYAN "\033[46m" +#define PL_LOG_BG_COLOR_CODE_WHITE "\033[47m" +#define PL_LOG_BG_COLOR_CODE_STRONG_BLACK "\033[100m" +#define PL_LOG_BG_COLOR_CODE_STRONG_RED "\033[101m" +#define PL_LOG_BG_COLOR_CODE_STRONG_GREEN "\033[102m" +#define PL_LOG_BG_COLOR_CODE_STRONG_YELLOW "\033[103m" +#define PL_LOG_BG_COLOR_CODE_STRONG_BLUE "\033[104m" +#define PL_LOG_BG_COLOR_CODE_STRONG_MAGENTA "\033[105m" +#define PL_LOG_BG_COLOR_CODE_STRONG_CYAN "\033[106m" +#define PL_LOG_BG_COLOR_CODE_STRONG_WHITE "\033[107m" + +#endif + +#ifndef PL_LOG_FATAL_BG_COLOR + #define PL_LOG_FATAL_BG_COLOR PL_LOG_BG_COLOR_CODE_RED +#endif +#ifndef PL_LOG_TRACE_FG_COLOR + #define PL_LOG_TRACE_FG_COLOR PL_LOG_FG_COLOR_CODE_GREEN +#endif +#ifndef PL_LOG_DEBUG_FG_COLOR + #define PL_LOG_DEBUG_FG_COLOR PL_LOG_FG_COLOR_CODE_CYAN +#endif +#ifndef PL_LOG_INFO_FG_COLOR + #define PL_LOG_INFO_FG_COLOR PL_LOG_FG_COLOR_CODE_WHITE +#endif +#ifndef PL_LOG_WARN_FG_COLOR + #define PL_LOG_WARN_FG_COLOR PL_LOG_FG_COLOR_CODE_YELLOW +#endif +#ifndef PL_LOG_ERROR_FG_COLOR + #define PL_LOG_ERROR_FG_COLOR PL_LOG_FG_COLOR_CODE_RED +#endif +#ifndef PL_LOG_FATAL_FG_COLOR + #define PL_LOG_FATAL_FG_COLOR PL_LOG_FG_COLOR_CODE_WHITE +#endif + +//----------------------------------------------------------------------------- +// [SECTION] includes +//----------------------------------------------------------------------------- + +#include // memset +#include + +#ifndef pl_snprintf + #include + #define pl_snprintf snprintf + #define pl_vsnprintf vsnprintf +#endif + +#ifndef PL_ASSERT + #include + #define PL_ASSERT(x) assert((x)) +#endif + +//----------------------------------------------------------------------------- +// [SECTION] internal structs +//----------------------------------------------------------------------------- + +typedef struct _plLogContext +{ + plLogChannel atChannels[PL_LOG_MAX_CHANNEL_COUNT]; + uint32_t uChannelCount; + uint32_t uCurrentChannel; +} plLogContext; + +//----------------------------------------------------------------------------- +// [SECTION] global context +//----------------------------------------------------------------------------- + +plLogContext* gptLogContext = NULL; + +//----------------------------------------------------------------------------- +// [SECTION] internal api +//----------------------------------------------------------------------------- + +static plLogEntry* pl__get_new_log_entry(uint32_t uID); + +static inline void pl__log_buffer_may_grow(plLogChannel* ptChannel, int iAdditionalSize) +{ + if(ptChannel->uBufferSize + iAdditionalSize > ptChannel->uBufferCapacity) // grow + { + char* pcOldBuffer = ptChannel->pcBuffer0; + uint64_t uNewCapacity = ptChannel->uBufferCapacity * 2; + if(uNewCapacity < ptChannel->uBufferSize + iAdditionalSize) + uNewCapacity = (ptChannel->uBufferSize + (uint64_t)iAdditionalSize) * 2; + ptChannel->pcBuffer0 = (char*)PL_LOG_ALLOC(uNewCapacity * 2); + memset(ptChannel->pcBuffer0, 0, uNewCapacity * 2); + memcpy(ptChannel->pcBuffer0, pcOldBuffer, ptChannel->uBufferCapacity); + ptChannel->uBufferCapacity = uNewCapacity; + ptChannel->pcBuffer1 = &ptChannel->pcBuffer0[uNewCapacity]; + PL_LOG_FREE(pcOldBuffer); + } +} + +//----------------------------------------------------------------------------- +// [SECTION] public api implementation +//----------------------------------------------------------------------------- + +plLogContext* +pl__create_log_context(void) +{ + plLogContext* tPContext = (plLogContext*)PL_LOG_ALLOC(sizeof(plLogContext)); + memset(tPContext, 0, sizeof(plLogContext)); + tPContext->uChannelCount = 0; + gptLogContext = tPContext; + + // setup log channels + pl_add_log_channel("Default", PL_CHANNEL_TYPE_CONSOLE | PL_CHANNEL_TYPE_BUFFER); + + pl_log_trace_f("<- global enabled"); + pl_log_debug_f("<- global enabled"); + pl_log_info_f("<- global enabled"); + pl_log_warn_f("<- global enabled"); + pl_log_error_f("<- global enabled"); + pl_log_fatal_f("<- global enabled"); + + return tPContext; +} + +void +pl__cleanup_log_context(void) +{ + PL_ASSERT(gptLogContext && "no global log context set"); + if(gptLogContext) + { + for(uint32_t i = 0; i < gptLogContext->uChannelCount; i++) + { + plLogChannel* ptChannel = &gptLogContext->atChannels[i]; + if(ptChannel->pcBuffer0) + PL_LOG_FREE(ptChannel->pcBuffer0); + if(ptChannel->bOverflowInUse) + PL_LOG_FREE(ptChannel->pEntries); + ptChannel->pEntries = NULL; + ptChannel->pcBuffer0 = NULL; + ptChannel->uBufferCapacity = 0; + ptChannel->uBufferSize = 0; + ptChannel->uEntryCapacity = 0; + ptChannel->uEntryCount = 0; + ptChannel->uLevel = 0; + ptChannel->tType = 0; + ptChannel->uID = 0; + } + memset(gptLogContext->atChannels, 0, sizeof(plLogChannel) * PL_LOG_MAX_CHANNEL_COUNT); + gptLogContext->uChannelCount = 0; + PL_LOG_FREE(gptLogContext); + } + gptLogContext = NULL; +} + +void +pl__set_log_context(plLogContext* tPContext) +{ + PL_ASSERT(tPContext && "log context is NULL"); + gptLogContext = tPContext; +} + +plLogContext* +pl__get_log_context(void) +{ + PL_ASSERT(gptLogContext && "no global log context set"); + return gptLogContext; +} + +uint32_t +pl__add_log_channel(const char* pcName, plChannelType tType) +{ + uint32_t uID = gptLogContext->uChannelCount; + + if((tType & PL_CHANNEL_TYPE_BUFFER) && (tType & PL_CHANNEL_TYPE_CYCLIC_BUFFER)) + { + PL_ASSERT(false && "Can't have PL_CHANNEL_TYPE_BUFFER and PL_CHANNEL_TYPE_CYCLIC_BUFFER together"); + } + + plLogChannel* ptChannel = &gptLogContext->atChannels[uID]; + ptChannel->pcName = pcName; + if(tType & PL_CHANNEL_TYPE_BUFFER) + { + ptChannel->pEntries = (plLogEntry*)PL_LOG_ALLOC(PL_LOG_INITIAL_LOG_SIZE * sizeof(plLogEntry)); + ptChannel->uEntryCapacity = PL_LOG_INITIAL_LOG_SIZE; + } + else if(tType & PL_CHANNEL_TYPE_CYCLIC_BUFFER) + { + ptChannel->pEntries = NULL; + ptChannel->uEntryCapacity = PL_LOG_CYCLIC_BUFFER_SIZE; + pl__log_buffer_may_grow(ptChannel, PL_LOG_MAX_LINE_SIZE); + } + else + { + ptChannel->pEntries = NULL; + ptChannel->uEntryCapacity = 0; + pl__log_buffer_may_grow(ptChannel, PL_LOG_MAX_LINE_SIZE); + } + + ptChannel->uEntryCount = 0; + ptChannel->uNextEntry = 0; + ptChannel->uLevel = 0; + ptChannel->tType = tType; + ptChannel->uID = uID; + + gptLogContext->uChannelCount++; + return uID; +} + +uint32_t +pl__get_current_log_channel(void) +{ + return gptLogContext->uCurrentChannel; +} + +void +pl__set_current_log_channel(uint32_t uID) +{ + PL_ASSERT(uID < gptLogContext->uChannelCount && "channel ID is not valid"); + gptLogContext->uCurrentChannel = uID; +} + +void +pl__set_log_level(uint32_t uID, uint32_t uLevel) +{ + PL_ASSERT(uID < gptLogContext->uChannelCount && "channel ID is not valid"); + gptLogContext->atChannels[uID].uLevel = uLevel; +} + +void +pl__clear_log_channel(uint32_t uID) +{ + PL_ASSERT(uID < gptLogContext->uChannelCount && "channel ID is not valid"); + gptLogContext->atChannels[uID].uEntryCount = 0u; + gptLogContext->atChannels[uID].uNextEntry = 0u; + if(gptLogContext->atChannels[uID].tType & PL_CHANNEL_TYPE_CYCLIC_BUFFER) + memset(gptLogContext->atChannels[uID].atEntries, 0, sizeof(plLogEntry) * PL_LOG_CYCLIC_BUFFER_SIZE); + else if(gptLogContext->atChannels[uID].tType & PL_CHANNEL_TYPE_BUFFER) + { + PL_LOG_FREE(gptLogContext->atChannels[uID].pEntries); + gptLogContext->atChannels[uID].pEntries = NULL; + gptLogContext->atChannels[uID].uEntryCapacity = 0; + } +} + +void +pl__reset_log_channel(uint32_t uID) +{ + PL_ASSERT(uID < gptLogContext->uChannelCount && "channel ID is not valid"); + gptLogContext->atChannels[uID].uEntryCount = 0u; + gptLogContext->atChannels[uID].uNextEntry = 0u; + + if(gptLogContext->atChannels[uID].tType & PL_CHANNEL_TYPE_CYCLIC_BUFFER) + memset(gptLogContext->atChannels[uID].atEntries, 0, sizeof(plLogEntry) * PL_LOG_CYCLIC_BUFFER_SIZE); + else if(gptLogContext->atChannels[uID].tType & PL_CHANNEL_TYPE_BUFFER) + memset(gptLogContext->atChannels[uID].pEntries, 0, sizeof(plLogEntry) * gptLogContext->atChannels[uID].uEntryCapacity); +} + +plLogEntry* +pl__get_log_entries(uint32_t uID, uint64_t* puEntryCount) +{ + PL_ASSERT(uID < gptLogContext->uChannelCount && "channel ID is not valid"); + plLogChannel* tPChannel = &gptLogContext->atChannels[uID]; + if(puEntryCount) + *puEntryCount = tPChannel->uEntryCount; + return tPChannel->pEntries; +} + +plLogChannel* +pl__get_log_channels(uint32_t* puChannelCount) +{ + if(puChannelCount) + *puChannelCount = gptLogContext->uChannelCount; + return gptLogContext->atChannels; +} + +#define PL__LOG_LEVEL_MACRO(level, prefix, prefixSize) \ + plLogChannel* tPChannel = &gptLogContext->atChannels[uID]; \ + plLogEntry* ptEntry = pl__get_new_log_entry(uID); \ + if(tPChannel->uLevel < level + 1) \ + { \ + if(tPChannel->tType & PL_CHANNEL_TYPE_CONSOLE) \ + printf(prefix " (%s) %s\n", tPChannel->pcName, pcMessage); \ + if((tPChannel->tType & PL_CHANNEL_TYPE_CYCLIC_BUFFER) || (tPChannel->tType & PL_CHANNEL_TYPE_BUFFER)) \ + { \ + const size_t szNewSize = strlen(pcMessage) + 10; \ + pl__log_buffer_may_grow(tPChannel, (int)szNewSize); \ + ptEntry->uOffset = tPChannel->uBufferSize; \ + char* cPDest = &tPChannel->pcBuffer0[tPChannel->uBufferSize + tPChannel->uBufferCapacity * (ptEntry->uGeneration % 2)]; \ + ptEntry->uLevel = level; \ + tPChannel->uBufferSize += szNewSize; \ + strcpy(cPDest, prefix); \ + cPDest += prefixSize; \ + strcpy(cPDest, pcMessage); \ + } \ + } + +void +pl__log_trace(uint32_t uID, const char* pcMessage) +{ + PL__LOG_LEVEL_MACRO(PL_LOG_LEVEL_TRACE, "[TRACE] ", 8) +} + +void +pl__log_debug(uint32_t uID, const char* pcMessage) +{ + PL__LOG_LEVEL_MACRO(PL_LOG_LEVEL_DEBUG, "[DEBUG] ", 8) +} + +void +pl__log_info(uint32_t uID, const char* pcMessage) +{ + PL__LOG_LEVEL_MACRO(PL_LOG_LEVEL_INFO, "[INFO ] ", 8) +} + +void +pl__log_warn(uint32_t uID, const char* pcMessage) +{ + PL__LOG_LEVEL_MACRO(PL_LOG_LEVEL_WARN, "[WARN ] ", 8) +} + +void +pl__log_error(uint32_t uID, const char* pcMessage) +{ + PL__LOG_LEVEL_MACRO(PL_LOG_LEVEL_ERROR, "[ERROR] ", 8) +} + +void +pl__log_fatal(uint32_t uID, const char* pcMessage) +{ + PL__LOG_LEVEL_MACRO(PL_LOG_LEVEL_FATAL, "[FATAL] ", 8) +} + +void +pl__log_trace_p(uint32_t uID, const char* cPFormat, ...) +{ + va_list argptr; + va_start(argptr, cPFormat); + pl__log_trace_va(uID, cPFormat, argptr); + va_end(argptr); +} + +void +pl__log_debug_p(uint32_t uID, const char* cPFormat, ...) +{ + va_list argptr; + va_start(argptr, cPFormat); + pl__log_debug_va(uID, cPFormat, argptr); + va_end(argptr); +} + +void +pl__log_info_p(uint32_t uID, const char* cPFormat, ...) +{ + va_list argptr; + va_start(argptr, cPFormat); + pl__log_info_va(uID, cPFormat, argptr); + va_end(argptr); +} + +void +pl__log_warn_p(uint32_t uID, const char* cPFormat, ...) +{ + va_list argptr; + va_start(argptr, cPFormat); + pl__log_warn_va(uID, cPFormat, argptr); + va_end(argptr); +} + +void +pl__log_error_p(uint32_t uID, const char* cPFormat, ...) +{ + va_list argptr; + va_start(argptr, cPFormat); + pl__log_error_va(uID, cPFormat, argptr); + va_end(argptr); +} + +void +pl__log_fatal_p(uint32_t uID, const char* cPFormat, ...) +{ + va_list argptr; + va_start(argptr, cPFormat); + pl__log_fatal_va(uID, cPFormat, argptr); + va_end(argptr); +} + +#define PL__LOG_LEVEL_VA_BUFFER_MACRO(level, prefix) \ + if((tPChannel->tType & PL_CHANNEL_TYPE_CYCLIC_BUFFER) || (tPChannel->tType & PL_CHANNEL_TYPE_BUFFER)) \ + { \ + va_list parm_copy; \ + va_copy(parm_copy, args); \ + plLogEntry* ptEntry = pl__get_new_log_entry(uID); \ + const int iNewSize = pl_vsnprintf(NULL, 0, cPFormat, parm_copy) + 9; \ + va_end(parm_copy); \ + pl__log_buffer_may_grow(tPChannel, iNewSize); \ + ptEntry->uOffset = tPChannel->uBufferSize; \ + char* cPDest = &tPChannel->pcBuffer0[tPChannel->uBufferSize + tPChannel->uBufferCapacity * (ptEntry->uGeneration % 2)]; \ + tPChannel->uBufferSize += iNewSize; \ + ptEntry->uLevel = level; \ + cPDest += pl_snprintf(cPDest, 9, prefix); \ + va_list parm_copy2; \ + va_copy(parm_copy2, args); \ + pl_vsnprintf(cPDest, iNewSize, cPFormat, parm_copy2); \ + va_end(parm_copy2); \ + } + +void +pl__log_trace_va(uint32_t uID, const char* cPFormat, va_list args) +{ + + plLogChannel* tPChannel = &gptLogContext->atChannels[uID]; + + if(tPChannel->uLevel < PL_LOG_LEVEL_TRACE + 1) + { + if(tPChannel->tType & PL_CHANNEL_TYPE_CONSOLE) + { + #ifdef PL_LOG_TRACE_BOLD + printf(PL_LOG_BOLD_CODE); + #endif + + #ifdef PL_LOG_TRACE_UNDERLINE + printf(PL_LOG_UNDERLINE_CODE); + #endif + + #ifdef PL_LOG_TRACE_FG_COLOR + printf(PL_LOG_TRACE_FG_COLOR); + #endif + + #ifdef PL_LOG_TRACE_BG_COLOR + printf(PL_LOG_TRACE_BG_COLOR); + #endif + + printf("[TRACE] (%s) ", tPChannel->pcName); + char dest[PL_LOG_MAX_LINE_SIZE]; + + va_list parm_copy; + va_copy(parm_copy, args); + pl_vsnprintf(dest, PL_LOG_MAX_LINE_SIZE, cPFormat, parm_copy); + printf("%s%s\n", dest, PL_LOG_POP_CODE); + va_end(parm_copy); + } + PL__LOG_LEVEL_VA_BUFFER_MACRO(PL_LOG_LEVEL_TRACE, "[TRACE] ") + } + + +} + +void +pl__log_debug_va(uint32_t uID, const char* cPFormat, va_list args) +{ + + plLogChannel* tPChannel = &gptLogContext->atChannels[uID]; + + if(tPChannel->uLevel < PL_LOG_LEVEL_DEBUG + 1) + { + if(tPChannel->tType & PL_CHANNEL_TYPE_CONSOLE) + { + #ifdef PL_LOG_DEBUG_BOLD + printf(PL_LOG_BOLD_CODE); + #endif + + #ifdef PL_LOG_DEBUG_UNDERLINE + printf(PL_LOG_UNDERLINE_CODE); + #endif + + #ifdef PL_LOG_DEBUG_FG_COLOR + printf(PL_LOG_DEBUG_FG_COLOR); + #endif + + #ifdef PL_LOG_DEBUG_BG_COLOR + printf(PL_LOG_DEBUG_BG_COLOR); + #endif + + printf("[DEBUG] (%s) ", tPChannel->pcName); + char dest[PL_LOG_MAX_LINE_SIZE]; + va_list parm_copy; + va_copy(parm_copy, args); + pl_vsnprintf(dest, PL_LOG_MAX_LINE_SIZE, cPFormat, parm_copy); + printf("%s%s\n", dest, PL_LOG_POP_CODE); + va_end(parm_copy); + } + PL__LOG_LEVEL_VA_BUFFER_MACRO(PL_LOG_LEVEL_DEBUG, "[DEBUG] ") + } +} + +void +pl__log_info_va(uint32_t uID, const char* cPFormat, va_list args) +{ + + plLogChannel* tPChannel = &gptLogContext->atChannels[uID]; + + if(tPChannel->uLevel < PL_LOG_LEVEL_INFO + 1) + { + if(tPChannel->tType & PL_CHANNEL_TYPE_CONSOLE) + { + #ifdef PL_LOG_INFO_BOLD + printf(PL_LOG_BOLD_CODE); + #endif + + #ifdef PL_LOG_INFO_UNDERLINE + printf(PL_LOG_UNDERLINE_CODE); + #endif + + #ifdef PL_LOG_INFO_FG_COLOR + printf(PL_LOG_INFO_FG_COLOR); + #endif + + #ifdef PL_LOG_INFO_BG_COLOR + printf(PL_LOG_INFO_BG_COLOR); + #endif + + printf("[INFO ] (%s) ", tPChannel->pcName); + char dest[PL_LOG_MAX_LINE_SIZE]; + va_list parm_copy; + va_copy(parm_copy, args); + pl_vsnprintf(dest, PL_LOG_MAX_LINE_SIZE, cPFormat, parm_copy); + printf("%s%s\n", dest, PL_LOG_POP_CODE); + va_end(parm_copy); + } + PL__LOG_LEVEL_VA_BUFFER_MACRO(PL_LOG_LEVEL_INFO, "[INFO ] ") + } +} + +void +pl__log_warn_va(uint32_t uID, const char* cPFormat, va_list args) +{ + + plLogChannel* tPChannel = &gptLogContext->atChannels[uID]; + + if(tPChannel->uLevel < PL_LOG_LEVEL_WARN + 1) + { + if(tPChannel->tType & PL_CHANNEL_TYPE_CONSOLE) + { + #ifdef PL_LOG_WARN_BOLD + printf(PL_LOG_BOLD_CODE); + #endif + + #ifdef PL_LOG_WARN_UNDERLINE + printf(PL_LOG_UNDERLINE_CODE); + #endif + + #ifdef PL_LOG_WARN_FG_COLOR + printf(PL_LOG_WARN_FG_COLOR); + #endif + + #ifdef PL_LOG_WARN_BG_COLOR + printf(PL_LOG_WARN_BG_COLOR); + #endif + + printf("[WARN ] (%s) ", tPChannel->pcName); + char dest[PL_LOG_MAX_LINE_SIZE]; + va_list parm_copy; + va_copy(parm_copy, args); + pl_vsnprintf(dest, PL_LOG_MAX_LINE_SIZE, cPFormat, parm_copy); + printf("%s%s\n", dest, PL_LOG_POP_CODE); + va_end(parm_copy); + } + PL__LOG_LEVEL_VA_BUFFER_MACRO(PL_LOG_LEVEL_WARN, "[WARN ] ") + } +} + +void +pl__log_error_va(uint32_t uID, const char* cPFormat, va_list args) +{ + + plLogChannel* tPChannel = &gptLogContext->atChannels[uID]; + + if(tPChannel->uLevel < PL_LOG_LEVEL_ERROR + 1) + { + if(tPChannel->tType & PL_CHANNEL_TYPE_CONSOLE) + { + #ifdef PL_LOG_ERROR_BOLD + printf(PL_LOG_BOLD_CODE); + #endif + + #ifdef PL_LOG_ERROR_UNDERLINE + printf(PL_LOG_UNDERLINE_CODE); + #endif + + #ifdef PL_LOG_ERROR_FG_COLOR + printf(PL_LOG_ERROR_FG_COLOR); + #endif + + #ifdef PL_LOG_ERROR_BG_COLOR + printf(PL_LOG_ERROR_BG_COLOR); + #endif + + printf("[ERROR] (%s) ", tPChannel->pcName); + char dest[PL_LOG_MAX_LINE_SIZE]; + va_list parm_copy; + va_copy(parm_copy, args); + pl_vsnprintf(dest, PL_LOG_MAX_LINE_SIZE, cPFormat, parm_copy); + printf("%s%s\n", dest, PL_LOG_POP_CODE); + va_end(parm_copy); + } + PL__LOG_LEVEL_VA_BUFFER_MACRO(PL_LOG_LEVEL_ERROR, "[ERROR] ") + } +} + +void +pl__log_fatal_va(uint32_t uID, const char* cPFormat, va_list args) +{ + + plLogChannel* tPChannel = &gptLogContext->atChannels[uID]; + + if(tPChannel->uLevel < PL_LOG_LEVEL_FATAL + 1) + { + if(tPChannel->tType & PL_CHANNEL_TYPE_CONSOLE) + { + #ifdef PL_LOG_FATAL_BOLD + printf(PL_LOG_BOLD_CODE); + #endif + + #ifdef PL_LOG_FATAL_UNDERLINE + printf(PL_LOG_UNDERLINE_CODE); + #endif + + #ifdef PL_LOG_FATAL_FG_COLOR + printf(PL_LOG_FATAL_FG_COLOR); + #endif + + #ifdef PL_LOG_FATAL_BG_COLOR + printf(PL_LOG_FATAL_BG_COLOR); + #endif + + printf("[FATAL] (%s) ", tPChannel->pcName); + char dest[PL_LOG_MAX_LINE_SIZE]; + va_list parm_copy; + va_copy(parm_copy, args); + pl_vsnprintf(dest, PL_LOG_MAX_LINE_SIZE, cPFormat, parm_copy); + printf("%s%s\n", dest, PL_LOG_POP_CODE); + va_end(parm_copy); + } + PL__LOG_LEVEL_VA_BUFFER_MACRO(PL_LOG_LEVEL_FATAL, "[FATAL] ") + } +} + +//----------------------------------------------------------------------------- +// [SECTION] internal api implementation +//----------------------------------------------------------------------------- + +static plLogEntry* +pl__get_new_log_entry(uint32_t uID) +{ + plLogChannel* tPChannel = &gptLogContext->atChannels[uID]; + + plLogEntry* ptEntry = NULL; + + if(tPChannel->tType & PL_CHANNEL_TYPE_CYCLIC_BUFFER) + { + ptEntry = &tPChannel->atEntries[tPChannel->uNextEntry]; + tPChannel->uNextEntry++; + tPChannel->uNextEntry = tPChannel->uNextEntry % PL_LOG_CYCLIC_BUFFER_SIZE; + tPChannel->uEntryCount++; + if(tPChannel->uNextEntry == 0) + { + tPChannel->uBufferSize = 0; + tPChannel->uGeneration++; + } + } + else if(tPChannel->tType & PL_CHANNEL_TYPE_BUFFER) + { + + // check if overflow reallocation is needed + if(tPChannel->uEntryCount == tPChannel->uEntryCapacity) + { + plLogEntry* sbtOldEntries = tPChannel->pEntries; + tPChannel->pEntries = (plLogEntry*)PL_LOG_ALLOC(sizeof(plLogEntry) * tPChannel->uEntryCapacity * 2); + memset(tPChannel->pEntries, 0, sizeof(plLogEntry) * tPChannel->uEntryCapacity * 2); + + // copy old values + memcpy(tPChannel->pEntries, sbtOldEntries, sizeof(plLogEntry) * tPChannel->uEntryCapacity); + tPChannel->uEntryCapacity *= 2; + + PL_LOG_FREE(sbtOldEntries); + } + + ptEntry = &tPChannel->pEntries[tPChannel->uEntryCount]; + tPChannel->uEntryCount++; + } + + ptEntry->uGeneration = tPChannel->uGeneration; + return ptEntry; +} + +#endif // PL_LOG_IMPLEMENTATION diff --git a/pl_math.h b/pl_math.h new file mode 100644 index 0000000..95e05b2 --- /dev/null +++ b/pl_math.h @@ -0,0 +1,798 @@ +/* + pl_math.h, v0.6 (WIP) + + Do this: + #define PL_MATH_INCLUDE_FUNCTIONS + before you include this file in *one* C or C++ file to create include math functions. + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define PL_MATH_INCLUDE_FUNCTIONS + #include "pl_math.h" +*/ + +// library version +#define PL_MATH_VERSION "0.7.0" +#define PL_MATH_VERSION_NUM 00700 + +/* +Index of this file: +// [SECTION] header mess +// [SECTION] forward declarations & basic types +// [SECTION] defines +// [SECTION] structs +// [SECTION] header file section +// [SECTION] includes +// [SECTION] general math +// [SECTION] vector ops +// [SECTION] matrix ops +// [SECTION] quaternion ops +// [SECTION] rect ops +// [SECTION] implementations +*/ + +//----------------------------------------------------------------------------- +// [SECTION] header mess +//----------------------------------------------------------------------------- + +#ifndef PL_MATH_INC +#define PL_MATH_INC +#define PL_MATH_DEFINED + +//----------------------------------------------------------------------------- +// [SECTION] forward declarations & basic types +//----------------------------------------------------------------------------- + +// forward declarations +typedef union _plVec2 plVec2; +typedef union _plVec3 plVec3; +typedef union _plVec4 plVec4; +typedef union _plMat4 plMat4; +typedef struct _plRect plRect; +typedef struct _plAABB plAABB; + +//----------------------------------------------------------------------------- +// [SECTION] defines +//----------------------------------------------------------------------------- + +#define PL_E 2.71828182f // e +#define PL_LOG2E 1.44269504f // log2(e) +#define PL_LOG10E 0.43429448f // log10(e) +#define PL_LN2 0.69314718f // ln(2) +#define PL_LN10 2.30258509f // ln(10) +#define PL_PI 3.14159265f // pi +#define PL_2PI 6.28318530f // pi +#define PL_PI_2 1.57079632f // pi/2 +#define PL_PI_3 1.04719755f // pi/3 +#define PL_PI_4 0.78539816f // pi/4 +#define PL_1_PI 0.31830988f // 1/pi +#define PL_2_PI 0.63661977f // 2/pi +#define PL_2_SQRTPI 1.12837916f // 2/sqrt(pi) +#define PL_SQRT2 1.41421356f // sqrt(2) +#define PL_SQRT1_2 0.70710678f // 1/sqrt(2) +#define PL_PI_D 3.1415926535897932 // pi + +//----------------------------------------------------------------------------- +// [SECTION] structs +//----------------------------------------------------------------------------- + +typedef union _plVec2 +{ + struct { float x, y; }; + struct { float r, g; }; + struct { float u, v; }; + float d[2]; +} plVec2; + +typedef union _plVec3 +{ + struct { float x, y, z; }; + struct { float r, g, b; }; + struct { float u, v, __; }; + struct { plVec2 xy; float ignore0_; }; + struct { plVec2 rg; float ignore1_; }; + struct { plVec2 uv; float ignore2_; }; + struct { float ignore3_; plVec2 yz; }; + struct { float ignore4_; plVec2 gb; }; + struct { float ignore5_; plVec2 v__; }; + float d[3]; +} plVec3; + +typedef union _plVec4 +{ + struct + { + union + { + plVec3 xyz; + struct{ float x, y, z;}; + }; + + float w; + }; + struct + { + union + { + plVec3 rgb; + struct{ float r, g, b;}; + }; + float a; + }; + struct + { + plVec2 xy; + float ignored0_, ignored1_; + }; + struct + { + float ignored2_; + plVec2 yz; + float ignored3_; + }; + struct + { + float ignored4_, ignored5_; + plVec2 zw; + }; + float d[4]; +} plVec4; + +typedef union _plMat4 +{ + plVec4 col[4]; + struct { + float x11; + float x21; + float x31; + float x41; + float x12; + float x22; + float x32; + float x42; + float x13; + float x23; + float x33; + float x43; + float x14; + float x24; + float x34; + float x44; + }; + float d[16]; +} plMat4; + +typedef struct _plRect +{ + plVec2 tMin; + plVec2 tMax; +} plRect; + +typedef struct _plAABB +{ + plVec3 tMin; + plVec3 tMax; +} plAABB; + +#endif // PL_MATH_INC + +#if defined(PL_MATH_INCLUDE_FUNCTIONS) && !defined(PL_MATH_INCLUDE_FUNCTIONS_H) +#define PL_MATH_INCLUDE_FUNCTIONS_H + +//----------------------------------------------------------------------------- +// [SECTION] includes +//----------------------------------------------------------------------------- + +#include +#include // bool +#include // uint*_t +#include + +//----------------------------------------------------------------------------- +// [SECTION] helpers +//----------------------------------------------------------------------------- + +#ifdef __cplusplus + #define pl_create_vec2(XARG, YARG) {(XARG), (YARG)} + #define pl_create_vec3(XARG, YARG, ZARG) {(XARG), (YARG), (ZARG)} + #define pl_create_vec4(XARG, YARG, ZARG, WARG) {(XARG), (YARG), (ZARG), (WARG)} + #define pl_create_mat4_diag(XARG, YARG, ZARG, WARG) {(XARG), 0.0, 0.0f, 0.0f, 0.0f, (YARG), 0.0f, 0.0f, 0.0f, 0.0f, (ZARG), 0.0f, 0.0f, 0.0f, 0.0f, (WARG)} + #define pl_create_mat4_cols(XARG, YARG, ZARG, WARG) {(XARG).x, (XARG).y, (XARG).z, (XARG).w, (YARG).x, (YARG).y, (YARG).z, (YARG).w, (ZARG).x, (ZARG).y, (ZARG).z, (ZARG).w, (WARG).x, (WARG).y, (WARG).z, (WARG).w} + #define pl_create_rect_vec2(XARG, YARG) {(XARG), (YARG)} + #define pl_create_rect(XARG, YARG, ZARG, WARG) {{(XARG), (YARG)}, {(ZARG), (WARG)}} +#else + #define pl_create_vec2(XARG, YARG) (plVec2){(XARG), (YARG)} + #define pl_create_vec3(XARG, YARG, ZARG) (plVec3){(XARG), (YARG), (ZARG)} + #define pl_create_vec4(XARG, YARG, ZARG, WARG) (plVec4){(XARG), (YARG), (ZARG), (WARG)} + #define pl_create_mat4_diag(XARG, YARG, ZARG, WARG) (plMat4){.x11 = (XARG), .x22 = (YARG), .x33 = (ZARG), .x44 = (WARG)} + #define pl_create_mat4_cols(XARG, YARG, ZARG, WARG) (plMat4){.col[0] = (XARG), .col[1] = (YARG), .col[2] = (ZARG), .col[3] = (WARG)} + #define pl_create_rect_vec2(XARG, YARG) (plRect){.tMin = (XARG), .tMax = (YARG)} + #define pl_create_rect(XARG, YARG, ZARG, WARG) (plRect){.tMin = {.x = (XARG), .y = (YARG)}, .tMax = {.x = (ZARG), .y = (WARG)}} +#endif + +//----------------------------------------------------------------------------- +// [SECTION] general math +//----------------------------------------------------------------------------- + +#define pl_max(Value1, Value2) ((Value1) > (Value2) ? (Value1) : (Value2)) +#define pl_min(Value1, Value2) ((Value1) > (Value2) ? (Value2) : (Value1)) +#define pl_square(Value) ((Value) * (Value)) +#define pl_cube(Value) ((Value) * (Value) * (Value)) + +static inline float pl_radiansf(float fDegrees) { return fDegrees * 0.0174532925f; } +static inline float pl_degreesf(float fRadians) { return fRadians * 57.29577951f; } +static inline float pl_maxf (float fValue1, float fValue2) { return fValue1 > fValue2 ? fValue1 : fValue2; } +static inline float pl_minf (float fValue1, float fValue2) { return fValue1 > fValue2 ? fValue2 : fValue1; } +static inline int pl_maxi (int iValue1, int iValue2) { return iValue1 > iValue2 ? iValue1 : iValue2; } +static inline int pl_mini (int iValue1, int iValue2) { return iValue1 > iValue2 ? iValue2 : iValue1; } +static inline uint32_t pl_maxu (uint32_t uValue1, uint32_t uValue2) { return uValue1 > uValue2 ? uValue1 : uValue2; } +static inline uint32_t pl_minu (uint32_t uValue1, uint32_t uValue2) { return uValue1 > uValue2 ? uValue2 : uValue1; } +static inline double pl_maxd (double dValue1, double dValue2) { return dValue1 > dValue2 ? dValue1 : dValue2; } +static inline double pl_mind (double dValue1, double dValue2) { return dValue1 > dValue2 ? dValue2 : dValue1; } +static inline float pl_squaref (float fValue) { return fValue * fValue;} +static inline float pl_cubef (float fValue) { return fValue * fValue * fValue;} +static inline int pl_clampi (int iMin, int iValue, int iMax) { if (iValue < iMin) return iMin; else if (iValue > iMax) return iMax; return iValue; } +static inline float pl_clampf (float fMin, float fValue, float fMax) { if (fValue < fMin) return fMin; else if (fValue > fMax) return fMax; return fValue; } +static inline double pl_clampd (double dMin, double dValue, double dMax) { if (dValue < dMin) return dMin; else if (dValue > dMax) return dMax; return dValue; } +static inline float pl_clamp01f(float fValue) { return pl_clampf(0.0f, fValue, 1.0f); } +static inline double pl_clamp01d(double dValue) { return pl_clampd(0.0, dValue, 1.0); } +static inline size_t pl_align_up(size_t szValue, size_t szAlign) { return ((szValue + (szAlign - 1)) & ~(szAlign - 1)); } + +#define PL__ALIGN_UP(num, align) (((num) + ((align)-1)) & ~((align)-1)) + +//----------------------------------------------------------------------------- +// [SECTION] vector ops +//----------------------------------------------------------------------------- + +// unary ops +static inline float pl_length_sqr_vec2 (plVec2 tVec) { return pl_squaref(tVec.x) + pl_squaref(tVec.y); } +static inline float pl_length_sqr_vec3 (plVec3 tVec) { return pl_squaref(tVec.x) + pl_squaref(tVec.y) + pl_squaref(tVec.z); } +static inline float pl_length_sqr_vec4 (plVec4 tVec) { return pl_squaref(tVec.x) + pl_squaref(tVec.y) + pl_squaref(tVec.z) + pl_squaref(tVec.w); } +static inline float pl_length_vec2 (plVec2 tVec) { return sqrtf(pl_length_sqr_vec2(tVec)); } +static inline float pl_length_vec3 (plVec3 tVec) { return sqrtf(pl_length_sqr_vec3(tVec)); } +static inline float pl_length_vec4 (plVec4 tVec) { return sqrtf(pl_length_sqr_vec4(tVec)); } +static inline plVec2 pl_floor_vec2 (plVec2 tVec) { return pl_create_vec2(floorf(tVec.x), floorf(tVec.y));} +static inline plVec3 pl_floor_vec3 (plVec3 tVec) { return pl_create_vec3(floorf(tVec.x), floorf(tVec.y), floorf(tVec.z));} +static inline plVec4 pl_floor_vec4 (plVec4 tVec) { return pl_create_vec4(floorf(tVec.x), floorf(tVec.y), floorf(tVec.z), floorf(tVec.w));} + +// binary ops + +static inline plVec2 pl_lerp_vec2 (plVec2 t0, plVec2 t1, float fAmount) { return pl_create_vec2(t0.x + (t1.x - t0.x) * fAmount, t0.y + (t1.y - t0.y) * fAmount);} +static inline plVec3 pl_lerp_vec3 (plVec3 t0, plVec3 t1, float fAmount) { return pl_create_vec3(t0.x + (t1.x - t0.x) * fAmount, t0.y + (t1.y - t0.y) * fAmount, t0.z + (t1.z - t0.z) * fAmount);} +static inline plVec4 pl_lerp_vec4 (plVec4 t0, plVec4 t1, float fAmount) { return pl_create_vec4(t0.x + (t1.x - t0.x) * fAmount, t0.y + (t1.y - t0.y) * fAmount, t0.z + (t1.z - t0.z) * fAmount, t0.w + (t1.w - t0.w) * fAmount);} + + +static inline plVec2 pl_clamp_vec2 (plVec2 tMin, plVec2 tValue, plVec2 tMax) { return pl_create_vec2(pl_clampf(tMin.x, tValue.x, tMax.x), pl_clampf(tMin.y, tValue.y, tMax.y));} +static inline plVec3 pl_clamp_vec3 (plVec3 tMin, plVec3 tValue, plVec3 tMax) { return pl_create_vec3(pl_clampf(tMin.x, tValue.x, tMax.x), pl_clampf(tMin.y, tValue.y, tMax.y), pl_clampf(tMax.z, tValue.z, tMax.z));} +static inline plVec4 pl_clamp_vec4 (plVec4 tMin, plVec4 tValue, plVec4 tMax) { return pl_create_vec4(pl_clampf(tMin.x, tValue.x, tMax.x), pl_clampf(tMin.y, tValue.y, tMax.y), pl_clampf(tMax.z, tValue.z, tMax.z), pl_clampf(tMax.w, tValue.w, tMax.w));} + +static inline plVec2 pl_min_vec2 (plVec2 tValue0, plVec2 tValue1) { return pl_create_vec2(pl_minf(tValue0.x, tValue1.x), pl_minf(tValue0.y, tValue1.y));} +static inline plVec3 pl_min_vec3 (plVec3 tValue0, plVec3 tValue1) { return pl_create_vec3(pl_minf(tValue0.x, tValue1.x), pl_minf(tValue0.y, tValue1.y), pl_minf(tValue0.z, tValue1.z));} +static inline plVec4 pl_min_vec4 (plVec4 tValue0, plVec4 tValue1) { return pl_create_vec4(pl_minf(tValue0.x, tValue1.x), pl_minf(tValue0.y, tValue1.y), pl_minf(tValue0.z, tValue1.z), pl_minf(tValue0.w, tValue1.w));} +static inline plVec2 pl_max_vec2 (plVec2 tValue0, plVec2 tValue1) { return pl_create_vec2(pl_maxf(tValue0.x, tValue1.x), pl_maxf(tValue0.y, tValue1.y));} +static inline plVec3 pl_max_vec3 (plVec3 tValue0, plVec3 tValue1) { return pl_create_vec3(pl_maxf(tValue0.x, tValue1.x), pl_maxf(tValue0.y, tValue1.y), pl_maxf(tValue0.z, tValue1.z));} +static inline plVec4 pl_max_vec4 (plVec4 tValue0, plVec4 tValue1) { return pl_create_vec4(pl_maxf(tValue0.x, tValue1.x), pl_maxf(tValue0.y, tValue1.y), pl_maxf(tValue0.z, tValue1.z), pl_maxf(tValue0.w, tValue1.w));} + +static inline float pl_dot_vec2 (plVec2 tVec1, plVec2 tVec2) { return tVec1.x * tVec2.x + tVec1.y * tVec2.y; } +static inline float pl_dot_vec3 (plVec3 tVec1, plVec3 tVec2) { return tVec1.x * tVec2.x + tVec1.y * tVec2.y + tVec1.z * tVec2.z; } +static inline float pl_dot_vec4 (plVec4 tVec1, plVec4 tVec2) { return tVec1.x * tVec2.x + tVec1.y * tVec2.y + tVec1.z * tVec2.z + tVec1.w * tVec2.w; } +static inline plVec3 pl_cross_vec3 (plVec3 tVec1, plVec3 tVec2) { return pl_create_vec3(tVec1.y * tVec2.z - tVec2.y * tVec1.z, tVec1.z * tVec2.x - tVec2.z * tVec1.x, tVec1.x * tVec2.y - tVec2.x * tVec1.y); } + +static inline plVec2 pl_add_vec2 (plVec2 tVec1, plVec2 tVec2) { return pl_create_vec2(tVec1.x + tVec2.x, tVec1.y + tVec2.y); } +static inline plVec3 pl_add_vec3 (plVec3 tVec1, plVec3 tVec2) { return pl_create_vec3(tVec1.x + tVec2.x, tVec1.y + tVec2.y, tVec1.z + tVec2.z); } +static inline plVec4 pl_add_vec4 (plVec4 tVec1, plVec4 tVec2) { return pl_create_vec4(tVec1.x + tVec2.x, tVec1.y + tVec2.y, tVec1.z + tVec2.z, tVec1.w + tVec2.w); } + +static inline plVec2 pl_sub_vec2 (plVec2 tVec1, plVec2 tVec2) { return pl_create_vec2(tVec1.x - tVec2.x, tVec1.y - tVec2.y); } +static inline plVec3 pl_sub_vec3 (plVec3 tVec1, plVec3 tVec2) { return pl_create_vec3(tVec1.x - tVec2.x, tVec1.y - tVec2.y, tVec1.z - tVec2.z); } +static inline plVec4 pl_sub_vec4 (plVec4 tVec1, plVec4 tVec2) { return pl_create_vec4(tVec1.x - tVec2.x, tVec1.y - tVec2.y, tVec1.z - tVec2.z, tVec1.w - tVec2.w) ;} + +static inline plVec2 pl_mul_vec2 (plVec2 tVec1, plVec2 tVec2) { return pl_create_vec2(tVec1.x * tVec2.x, tVec1.y * tVec2.y); } +static inline plVec3 pl_mul_vec3 (plVec3 tVec1, plVec3 tVec2) { return pl_create_vec3(tVec1.x * tVec2.x, tVec1.y * tVec2.y, tVec1.z * tVec2.z); } +static inline plVec4 pl_mul_vec4 (plVec4 tVec1, plVec4 tVec2) { return pl_create_vec4(tVec1.x * tVec2.x, tVec1.y * tVec2.y, tVec1.z * tVec2.z, tVec1.w * tVec2.w); } + +static inline plVec2 pl_div_vec2 (plVec2 tVec1, plVec2 tVec2) { return pl_create_vec2(tVec1.x / tVec2.x, tVec1.y / tVec2.y); } +static inline plVec3 pl_div_vec3 (plVec3 tVec1, plVec3 tVec2) { return pl_create_vec3(tVec1.x / tVec2.x, tVec1.y / tVec2.y, tVec1.z / tVec2.z); } +static inline plVec4 pl_div_vec4 (plVec4 tVec1, plVec4 tVec2) { return pl_create_vec4(tVec1.x / tVec2.x, tVec1.y / tVec2.y, tVec1.z / tVec2.z, tVec1.w / tVec2.w); } + +static inline plVec2 pl_mul_vec2_scalarf(plVec2 tVec, float fValue) { return pl_create_vec2(fValue * tVec.x, fValue * tVec.y); } +static inline plVec3 pl_mul_vec3_scalarf(plVec3 tVec, float fValue) { return pl_create_vec3(fValue * tVec.x, fValue * tVec.y, fValue * tVec.z); } +static inline plVec4 pl_mul_vec4_scalarf(plVec4 tVec, float fValue) { return pl_create_vec4(fValue * tVec.x, fValue * tVec.y, fValue * tVec.z, fValue * tVec.w); } + +static inline plVec2 pl_div_vec2_scalarf(plVec2 tVec, float fValue) { return pl_create_vec2(tVec.x / fValue, tVec.y / fValue); } +static inline plVec3 pl_div_vec3_scalarf(plVec3 tVec, float fValue) { return pl_create_vec3(tVec.x / fValue, tVec.y / fValue, tVec.z / fValue); } +static inline plVec4 pl_div_vec4_scalarf(plVec4 tVec, float fValue) { return pl_create_vec4(tVec.x / fValue, tVec.y / fValue, tVec.z / fValue, tVec.w / fValue); } + +static inline plVec2 pl_div_scalarf_vec2(float fValue, plVec2 tVec) { return pl_create_vec2(fValue / tVec.x, fValue / tVec.y); } +static inline plVec3 pl_div_scalarf_vec3(float fValue, plVec3 tVec) { return pl_create_vec3(fValue / tVec.x, fValue / tVec.y, fValue / tVec.z); } +static inline plVec4 pl_div_scalarf_vec4(float fValue, plVec4 tVec) { return pl_create_vec4(fValue / tVec.x, fValue / tVec.y, fValue / tVec.z, fValue / tVec.w); } + +static inline plVec2 pl_norm_vec2 (plVec2 tVec) { float fLength = pl_length_vec2(tVec); if(fLength > 0) fLength = 1.0f / fLength; return pl_mul_vec2_scalarf(tVec, fLength); } +static inline plVec3 pl_norm_vec3 (plVec3 tVec) { float fLength = pl_length_vec3(tVec); if(fLength > 0) fLength = 1.0f / fLength; return pl_mul_vec3_scalarf(tVec, fLength); } +static inline plVec4 pl_norm_vec4 (plVec4 tVec) { float fLength = pl_length_vec4(tVec); if(fLength > 0) fLength = 1.0f / fLength; return pl_mul_vec4_scalarf(tVec, fLength); } + +//----------------------------------------------------------------------------- +// [SECTION] matrix ops +//----------------------------------------------------------------------------- + +// general ops +static inline float pl_mat4_get (const plMat4* ptMat, int iRow, int iCol) { return ptMat->col[iCol].d[iRow];} +static inline void pl_mat4_set (plMat4* ptMat, int iRow, int iCol, float fValue) { ptMat->col[iCol].d[iRow] = fValue;} +static inline plMat4 pl_identity_mat4 (void) { return pl_create_mat4_diag(1.0f, 1.0f, 1.0f, 1.0f);} +static inline plMat4 pl_mat4_transpose (const plMat4* ptMat) { plMat4 tResult = {0}; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) pl_mat4_set(&tResult, i, j, pl_mat4_get(ptMat, j, i)); return tResult;} +static inline plMat4 pl_mat4_invert (const plMat4* ptMat); +static inline plMat4 pl_mul_scalarf_mat4 (float fLeft, const plMat4* ptRight) { plMat4 tResult = {0}; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) pl_mat4_set(&tResult, i, j, fLeft * pl_mat4_get(ptRight, j, i)); return tResult;} +static inline plVec3 pl_mul_mat4_vec3 (const plMat4* ptLeft, plVec3 tRight); +static inline plVec4 pl_mul_mat4_vec4 (const plMat4* ptLeft, plVec4 tRight); +static inline plMat4 pl_mul_mat4 (const plMat4* ptLeft, const plMat4* ptRight); + +// translation, rotation, scaling +static inline plMat4 pl_mat4_translate_xyz (float fX, float fY, float fZ) { plMat4 tResult = pl_create_mat4_diag(1.0f, 1.0f, 1.0f, 1.0f); tResult.x14 = fX; tResult.x24 = fY; tResult.x34 = fZ; return tResult;} +static inline plMat4 pl_mat4_translate_vec3 (plVec3 tVec) { return pl_mat4_translate_xyz(tVec.x, tVec.y, tVec.z);} +static inline plMat4 pl_mat4_rotate_vec3 (float fAngle, plVec3 tVec); +static inline plMat4 pl_mat4_rotate_xyz (float fAngle, float fX, float fY, float fZ) { return pl_mat4_rotate_vec3(fAngle, pl_create_vec3(fX, fY, fZ));} +static inline plMat4 pl_mat4_scale_xyz (float fX, float fY, float fZ) { return pl_create_mat4_diag(fX, fY, fZ, 1.0f);} +static inline plMat4 pl_mat4_scale_vec3 (plVec3 tVec) { return pl_mat4_scale_xyz(tVec.x, tVec.y, tVec.z);} +static inline plMat4 pl_mat4_rotate_quat (plVec4 tQ); +static inline plMat4 pl_rotation_translation_scale(plVec4 tQ, plVec3 tV, plVec3 tS); + +// transforms (optimized for orthogonal matrices) +static inline plMat4 pl_mat4t_invert (const plMat4* ptMat); +static inline plMat4 pl_mul_mat4t (const plMat4* ptLeft, const plMat4* ptRight); + +//----------------------------------------------------------------------------- +// [SECTION] quaternion ops +//----------------------------------------------------------------------------- + +static inline plVec4 pl_mul_quat (plVec4 tQ1, plVec4 tQ2) { return pl_create_vec4(tQ1.w * tQ2.x + tQ1.x * tQ2.w + tQ1.y * tQ2.z - tQ1.z * tQ2.y, tQ1.w * tQ2.y - tQ1.x * tQ2.z + tQ1.y * tQ2.w + tQ1.z * tQ2.x, tQ1.w * tQ2.z + tQ1.x * tQ2.y - tQ1.y * tQ2.x + tQ1.z * tQ2.w, tQ1.w * tQ2.w - tQ1.x * tQ2.x - tQ1.y * tQ2.y - tQ1.z * tQ2.z);} +static inline plVec4 pl_quat_rotation_normal (float fAngle, float fX, float fY, float fZ) { const float fSin2 = sinf(0.5f * fAngle); return pl_create_vec4(fSin2 * fX, fSin2 * fY, fSin2 * fZ, cosf(0.5f * fAngle));} +static inline plVec4 pl_quat_rotation_normal_vec3(float fAngle, plVec3 tNormalAxis) { return pl_quat_rotation_normal(fAngle, tNormalAxis.x, tNormalAxis.y, tNormalAxis.z);} +static inline plVec4 pl_norm_quat (plVec4 tQ) { const plVec3 tNorm = pl_norm_vec3(tQ.xyz); return pl_create_vec4(tNorm.x, tNorm.y, tNorm.z, tQ.w);} +static inline plVec4 pl_quat_slerp (plVec4 tQ1, plVec4 tQ2, float fT); +static inline void pl_decompose_matrix (const plMat4* ptM, plVec3* ptS, plVec4* ptQ, plVec3* ptT); + +//----------------------------------------------------------------------------- +// [SECTION] rect ops +//----------------------------------------------------------------------------- + +static inline plRect pl_calculate_rect (plVec2 tStart, plVec2 tSize) { return pl_create_rect_vec2(tStart, pl_add_vec2(tStart, tSize));} +static inline float pl_rect_width (const plRect* ptRect) { return ptRect->tMax.x - ptRect->tMin.x;} +static inline float pl_rect_height (const plRect* ptRect) { return ptRect->tMax.y - ptRect->tMin.y;} +static inline plVec2 pl_rect_size (const plRect* ptRect) { return pl_sub_vec2(ptRect->tMax, ptRect->tMin);} +static inline plVec2 pl_rect_center (const plRect* ptRect) { return pl_create_vec2((ptRect->tMax.x + ptRect->tMin.x) * 0.5f, (ptRect->tMax.y + ptRect->tMin.y) * 0.5f);} +static inline plVec2 pl_rect_top_left (const plRect* ptRect) { return ptRect->tMin;} +static inline plVec2 pl_rect_top_right (const plRect* ptRect) { return pl_create_vec2(ptRect->tMax.x, ptRect->tMin.y);} +static inline plVec2 pl_rect_bottom_left (const plRect* ptRect) { return pl_create_vec2(ptRect->tMin.x, ptRect->tMax.y);} +static inline plVec2 pl_rect_bottom_right (const plRect* ptRect) { return ptRect->tMax;} +static inline bool pl_rect_contains_point(const plRect* ptRect, plVec2 tP) { return tP.x >= ptRect->tMin.x && tP.y >= ptRect->tMin.y && tP.x < ptRect->tMax.x && tP.y < ptRect->tMax.y; } +static inline bool pl_rect_contains_rect (const plRect* ptRect0, const plRect* ptRect1) { return ptRect1->tMin.x >= ptRect0->tMin.x && ptRect1->tMin.y >= ptRect0->tMin.y && ptRect1->tMax.x <= ptRect0->tMax.x && ptRect1->tMax.y <= ptRect0->tMax.y; } +static inline bool pl_rect_overlaps_rect (const plRect* ptRect0, const plRect* ptRect1) { return ptRect1->tMin.y < ptRect0->tMax.y && ptRect1->tMax.y > ptRect0->tMin.y && ptRect1->tMin.x < ptRect0->tMax.x && ptRect1->tMax.x > ptRect0->tMin.x; } +static inline bool pl_rect_is_inverted (const plRect* ptRect) { return ptRect->tMin.x > ptRect->tMax.x || ptRect->tMin.y > ptRect->tMax.y; } +static inline plRect pl_rect_expand (const plRect* ptRect, float fPadding) { const plVec2 tMin = pl_create_vec2(ptRect->tMin.x - fPadding, ptRect->tMin.y - fPadding); const plVec2 tMax = pl_create_vec2(ptRect->tMax.x + fPadding, ptRect->tMax.y + fPadding); return pl_create_rect_vec2(tMin, tMax);} +static inline plRect pl_rect_expand_vec2 (const plRect* ptRect, plVec2 tPadding) { const plVec2 tMin = pl_create_vec2(ptRect->tMin.x - tPadding.x, ptRect->tMin.y - tPadding.y); const plVec2 tMax = pl_create_vec2(ptRect->tMax.x + tPadding.x, ptRect->tMax.y + tPadding.y); return pl_create_rect_vec2(tMin, tMax);} +static inline plRect pl_rect_clip (const plRect* ptRect0, const plRect* ptRect1) { const plVec2 tMin = pl_create_vec2(pl_maxf(ptRect0->tMin.x, ptRect1->tMin.x), pl_maxf(ptRect0->tMin.y, ptRect1->tMin.y)); const plVec2 tMax = pl_create_vec2(pl_minf(ptRect0->tMax.x, ptRect1->tMax.x), pl_minf(ptRect0->tMax.y, ptRect1->tMax.y)); return pl_create_rect_vec2(tMin, tMax);} +static inline plRect pl_rect_clip_full (const plRect* ptRect0, const plRect* ptRect1) { const plVec2 tMin = pl_clamp_vec2(ptRect1->tMin, ptRect0->tMin, ptRect1->tMax); const plVec2 tMax = pl_clamp_vec2(ptRect1->tMin, ptRect0->tMax, ptRect1->tMax); return pl_create_rect_vec2(tMin, tMax);} +static inline plRect pl_rect_floor (const plRect* ptRect) { const plVec2 tMin = pl_create_vec2( floorf(ptRect->tMin.x), floorf(ptRect->tMin.y)); const plVec2 tMax = pl_create_vec2(floorf(ptRect->tMax.x), floorf(ptRect->tMax.y)); return pl_create_rect_vec2(tMin, tMax);} +static inline plRect pl_rect_translate_vec2(const plRect* ptRect, plVec2 tDelta) { const plVec2 tMin = pl_create_vec2(ptRect->tMin.x + tDelta.x, ptRect->tMin.y + tDelta.y); const plVec2 tMax = pl_create_vec2(ptRect->tMax.x + tDelta.x, ptRect->tMax.y + tDelta.y); return pl_create_rect_vec2(tMin, tMax);} +static inline plRect pl_rect_translate_x (const plRect* ptRect, float fDx) { const plVec2 tMin = pl_create_vec2(ptRect->tMin.x + fDx, ptRect->tMin.y); const plVec2 tMax = pl_create_vec2(ptRect->tMax.x + fDx, ptRect->tMax.y); return pl_create_rect_vec2(tMin, tMax);} +static inline plRect pl_rect_translate_y (const plRect* ptRect, float fDy) { const plVec2 tMin = pl_create_vec2(ptRect->tMin.x, ptRect->tMin.y + fDy); const plVec2 tMax = pl_create_vec2(ptRect->tMax.x, ptRect->tMax.y + fDy); return pl_create_rect_vec2(tMin, tMax);} +static inline plRect pl_rect_add_point (const plRect* ptRect, plVec2 tP) { const plVec2 tMin = pl_create_vec2(ptRect->tMin.x > tP.x ? tP.x : ptRect->tMin.x, ptRect->tMin.y > tP.y ? tP.y : ptRect->tMin.y); const plVec2 tMax = pl_create_vec2(ptRect->tMax.x < tP.x ? tP.x : ptRect->tMax.x, ptRect->tMax.y < tP.y ? tP.y : ptRect->tMax.y); return pl_create_rect_vec2(tMin, tMax);} +static inline plRect pl_rect_add_rect (const plRect* ptRect0, const plRect* ptRect1) { const plVec2 tMin = pl_create_vec2(ptRect0->tMin.x > ptRect1->tMin.x ? ptRect1->tMin.x : ptRect0->tMin.x, ptRect0->tMin.y > ptRect1->tMin.y ? ptRect1->tMin.y : ptRect0->tMin.y); const plVec2 tMax = pl_create_vec2(ptRect0->tMax.x < ptRect1->tMax.x ? ptRect1->tMax.x : ptRect0->tMax.x, ptRect0->tMax.y < ptRect1->tMax.y ? ptRect1->tMax.y : ptRect0->tMax.y); return pl_create_rect_vec2(tMin, tMax);} +static inline plRect pl_rect_move_center (const plRect* ptRect, float fX, float fY) { const plVec2 tCurrentCenter = pl_rect_center(ptRect); const float fDx = fX - tCurrentCenter.x; const float fDy = fY - tCurrentCenter.y; const plRect tResult = {{ ptRect->tMin.x + fDx, ptRect->tMin.y + fDy},{ ptRect->tMax.x + fDx, ptRect->tMax.y + fDy}}; return tResult;} +static inline plRect pl_rect_move_center_y (const plRect* ptRect, float fY) { const plVec2 tCurrentCenter = pl_rect_center(ptRect); const float fDy = fY - tCurrentCenter.y; const plRect tResult = {{ ptRect->tMin.x, ptRect->tMin.y + fDy},{ ptRect->tMax.x, ptRect->tMax.y + fDy}}; return tResult;} +static inline plRect pl_rect_move_center_x (const plRect* ptRect, float fX) { const plVec2 tCurrentCenter = pl_rect_center(ptRect); const float fDx = fX - tCurrentCenter.x; const plRect tResult = { { ptRect->tMin.x + fDx, ptRect->tMin.y}, { ptRect->tMax.x + fDx, ptRect->tMax.y} }; return tResult;} +static inline plRect pl_rect_move_start (const plRect* ptRect, float fX, float fY) { const plRect tResult = {{ fX, fY}, { fX + ptRect->tMax.x - ptRect->tMin.x, fY + ptRect->tMax.y - ptRect->tMin.y} }; return tResult;} +static inline plRect pl_rect_move_start_x (const plRect* ptRect, float fX) { const plRect tResult = { { fX, ptRect->tMin.y}, { fX + ptRect->tMax.x - ptRect->tMin.x, ptRect->tMax.y} }; return tResult;} +static inline plRect pl_rect_move_start_y (const plRect* ptRect, float fY) { const plRect tResult = {{ ptRect->tMin.x, fY}, { ptRect->tMax.x, fY + ptRect->tMax.y - ptRect->tMin.y}}; return tResult;} + +//----------------------------------------------------------------------------- +// [SECTION] implementations +//----------------------------------------------------------------------------- + +static inline plVec3 +pl_mul_mat4_vec3(const plMat4* ptLeft, plVec3 tRight) +{ + const plVec4 Mov0 = { tRight.x, tRight.x, tRight.x, tRight.x }; + const plVec4 Mov1 = { tRight.y, tRight.y, tRight.y, tRight.y }; + const plVec4 Mul0 = pl_mul_vec4(ptLeft->col[0], Mov0); + const plVec4 Mul1 = pl_mul_vec4(ptLeft->col[1], Mov1); + const plVec4 Add0 = pl_add_vec4(Mul0, Mul1); + const plVec4 Mov2 = { tRight.z, tRight.z, tRight.z, tRight.z }; + const plVec4 Mov3 = { 1.0f, 1.0f, 1.0f, 1.0f }; + const plVec4 Mul2 = pl_mul_vec4(ptLeft->col[2], Mov2); + const plVec4 Mul3 = pl_mul_vec4(ptLeft->col[3], Mov3); + const plVec4 Add1 = pl_add_vec4(Mul2, Mul3); + const plVec4 Add2 = pl_add_vec4(Add0, Add1); + return pl_create_vec3(Add2.x, Add2.y, Add2.z ); +} + +static inline plVec4 +pl_mul_mat4_vec4(const plMat4* ptLeft, plVec4 tRight) +{ + const plVec4 Mov0 = { tRight.x, tRight.x, tRight.x, tRight.x }; + const plVec4 Mov1 = { tRight.y, tRight.y, tRight.y, tRight.y }; + const plVec4 Mul0 = pl_mul_vec4(ptLeft->col[0], Mov0); + const plVec4 Mul1 = pl_mul_vec4(ptLeft->col[1], Mov1); + const plVec4 Add0 = pl_add_vec4(Mul0, Mul1); + const plVec4 Mov2 = { tRight.z, tRight.z, tRight.z, tRight.z }; + const plVec4 Mov3 = { tRight.w, tRight.w, tRight.w, tRight.w }; + const plVec4 Mul2 = pl_mul_vec4(ptLeft->col[2], Mov2); + const plVec4 Mul3 = pl_mul_vec4(ptLeft->col[3], Mov3); + const plVec4 Add1 = pl_add_vec4(Mul2, Mul3); + return pl_add_vec4(Add0, Add1); +} + +static inline plMat4 +pl_mul_mat4(const plMat4* ptLeft, const plMat4* ptRight) +{ + plMat4 tResult; + + // row 0 + tResult.x11 = ptLeft->col[0].d[0] * ptRight->col[0].d[0] + ptLeft->col[1].d[0] * ptRight->col[0].d[1] + ptLeft->col[2].d[0] * ptRight->col[0].d[2] + ptLeft->col[3].d[0] * ptRight->col[0].d[3]; + tResult.x12 = ptLeft->col[0].d[0] * ptRight->col[1].d[0] + ptLeft->col[1].d[0] * ptRight->col[1].d[1] + ptLeft->col[2].d[0] * ptRight->col[1].d[2] + ptLeft->col[3].d[0] * ptRight->col[1].d[3]; + tResult.x13 = ptLeft->col[0].d[0] * ptRight->col[2].d[0] + ptLeft->col[1].d[0] * ptRight->col[2].d[1] + ptLeft->col[2].d[0] * ptRight->col[2].d[2] + ptLeft->col[3].d[0] * ptRight->col[2].d[3]; + tResult.x14 = ptLeft->col[0].d[0] * ptRight->col[3].d[0] + ptLeft->col[1].d[0] * ptRight->col[3].d[1] + ptLeft->col[2].d[0] * ptRight->col[3].d[2] + ptLeft->col[3].d[0] * ptRight->col[3].d[3]; + + // row 1 + tResult.x21 = ptLeft->col[0].d[1] * ptRight->col[0].d[0] + ptLeft->col[1].d[1] * ptRight->col[0].d[1] + ptLeft->col[2].d[1] * ptRight->col[0].d[2] + ptLeft->col[3].d[1] * ptRight->col[0].d[3]; + tResult.x22 = ptLeft->col[0].d[1] * ptRight->col[1].d[0] + ptLeft->col[1].d[1] * ptRight->col[1].d[1] + ptLeft->col[2].d[1] * ptRight->col[1].d[2] + ptLeft->col[3].d[1] * ptRight->col[1].d[3]; + tResult.x23 = ptLeft->col[0].d[1] * ptRight->col[2].d[0] + ptLeft->col[1].d[1] * ptRight->col[2].d[1] + ptLeft->col[2].d[1] * ptRight->col[2].d[2] + ptLeft->col[3].d[1] * ptRight->col[2].d[3]; + tResult.x24 = ptLeft->col[0].d[1] * ptRight->col[3].d[0] + ptLeft->col[1].d[1] * ptRight->col[3].d[1] + ptLeft->col[2].d[1] * ptRight->col[3].d[2] + ptLeft->col[3].d[1] * ptRight->col[3].d[3]; + + // row 2 + tResult.x31 = ptLeft->col[0].d[2] * ptRight->col[0].d[0] + ptLeft->col[1].d[2] * ptRight->col[0].d[1] + ptLeft->col[2].d[2] * ptRight->col[0].d[2] + ptLeft->col[3].d[2] * ptRight->col[0].d[3]; + tResult.x32 = ptLeft->col[0].d[2] * ptRight->col[1].d[0] + ptLeft->col[1].d[2] * ptRight->col[1].d[1] + ptLeft->col[2].d[2] * ptRight->col[1].d[2] + ptLeft->col[3].d[2] * ptRight->col[1].d[3]; + tResult.x33 = ptLeft->col[0].d[2] * ptRight->col[2].d[0] + ptLeft->col[1].d[2] * ptRight->col[2].d[1] + ptLeft->col[2].d[2] * ptRight->col[2].d[2] + ptLeft->col[3].d[2] * ptRight->col[2].d[3]; + tResult.x34 = ptLeft->col[0].d[2] * ptRight->col[3].d[0] + ptLeft->col[1].d[2] * ptRight->col[3].d[1] + ptLeft->col[2].d[2] * ptRight->col[3].d[2] + ptLeft->col[3].d[2] * ptRight->col[3].d[3]; + + // row 3 + tResult.x41 = ptLeft->col[0].d[3] * ptRight->col[0].d[0] + ptLeft->col[1].d[3] * ptRight->col[0].d[1] + ptLeft->col[2].d[3] * ptRight->col[0].d[2] + ptLeft->col[3].d[3] * ptRight->col[0].d[3]; + tResult.x42 = ptLeft->col[0].d[3] * ptRight->col[1].d[0] + ptLeft->col[1].d[3] * ptRight->col[1].d[1] + ptLeft->col[2].d[3] * ptRight->col[1].d[2] + ptLeft->col[3].d[3] * ptRight->col[1].d[3]; + tResult.x43 = ptLeft->col[0].d[3] * ptRight->col[2].d[0] + ptLeft->col[1].d[3] * ptRight->col[2].d[1] + ptLeft->col[2].d[3] * ptRight->col[2].d[2] + ptLeft->col[3].d[3] * ptRight->col[2].d[3]; + tResult.x44 = ptLeft->col[0].d[3] * ptRight->col[3].d[0] + ptLeft->col[1].d[3] * ptRight->col[3].d[1] + ptLeft->col[2].d[3] * ptRight->col[3].d[2] + ptLeft->col[3].d[3] * ptRight->col[3].d[3]; + + return tResult; +} + +static inline plMat4 +pl_mat4_rotate_vec3(float fAngle, plVec3 tVec) +{ + const float fCos = cosf(fAngle); + const float fSin = sinf(fAngle); + + const plVec3 tAxis = pl_norm_vec3(tVec); + const plVec3 tTemp = pl_mul_vec3_scalarf(tAxis, 1.0f - fCos); + + const plMat4 tM = pl_identity_mat4(); + const plMat4 tRotate = { + fCos + tTemp.x * tAxis.x, + tTemp.x * tAxis.y + fSin * tAxis.z, + tTemp.x * tAxis.z - fSin * tAxis.y, + 0.0f, + tTemp.y * tAxis.x - fSin * tAxis.z, + fCos + tTemp.y * tAxis.y, + tTemp.y * tAxis.z + fSin * tAxis.x, + 0.0f, + tTemp.z * tAxis.x + fSin * tAxis.y, + tTemp.z * tAxis.y - fSin * tAxis.x, + fCos + tTemp.z * tAxis.z, + 0.0f + }; + + return pl_create_mat4_cols( + pl_add_vec4(pl_mul_vec4_scalarf(tM.col[0], tRotate.col[0].d[0]), pl_add_vec4(pl_mul_vec4_scalarf(tM.col[1], tRotate.col[0].d[1]), pl_mul_vec4_scalarf(tM.col[2], tRotate.col[0].d[2]))), + pl_add_vec4(pl_mul_vec4_scalarf(tM.col[0], tRotate.col[1].d[0]), pl_add_vec4(pl_mul_vec4_scalarf(tM.col[1], tRotate.col[1].d[1]), pl_mul_vec4_scalarf(tM.col[2], tRotate.col[1].d[2]))), + pl_add_vec4(pl_mul_vec4_scalarf(tM.col[0], tRotate.col[2].d[0]), pl_add_vec4(pl_mul_vec4_scalarf(tM.col[1], tRotate.col[2].d[1]), pl_mul_vec4_scalarf(tM.col[2], tRotate.col[2].d[2]))), + tM.col[3]); +} + +static inline plMat4 +pl_mat4_invert(const plMat4* ptMat) +{ + const plVec3 tA = ptMat->col[0].xyz; + const plVec3 tB = ptMat->col[1].xyz; + const plVec3 tC = ptMat->col[2].xyz; + const plVec3 tD = ptMat->col[3].xyz; + + const float fX = pl_mat4_get(ptMat, 3, 0); + const float fY = pl_mat4_get(ptMat, 3, 1); + const float fZ = pl_mat4_get(ptMat, 3, 2); + const float fW = pl_mat4_get(ptMat, 3, 3); + + plVec3 tS = pl_cross_vec3(tA, tB); + plVec3 tT = pl_cross_vec3(tC, tD); + plVec3 tU = pl_sub_vec3(pl_mul_vec3_scalarf(tA, fY), pl_mul_vec3_scalarf(tB, fX)); + plVec3 tV = pl_sub_vec3(pl_mul_vec3_scalarf(tC, fW), pl_mul_vec3_scalarf(tD, fZ)); + + const float fInvDet = 1.0f / (pl_dot_vec3(tS, tV) + pl_dot_vec3(tT, tU)); + tS = pl_mul_vec3_scalarf(tS, fInvDet); + tT = pl_mul_vec3_scalarf(tT, fInvDet); + tU = pl_mul_vec3_scalarf(tU, fInvDet); + tV = pl_mul_vec3_scalarf(tV, fInvDet); + + const plVec3 tR0 = pl_add_vec3(pl_cross_vec3(tB, tV), pl_mul_vec3_scalarf(tT, fY)); + const plVec3 tR1 = pl_sub_vec3(pl_cross_vec3(tV, tA), pl_mul_vec3_scalarf(tT, fX)); + const plVec3 tR2 = pl_add_vec3(pl_cross_vec3(tD, tU), pl_mul_vec3_scalarf(tS, fW)); + const plVec3 tR3 = pl_sub_vec3(pl_cross_vec3(tU, tC), pl_mul_vec3_scalarf(tS, fZ)); + + plMat4 tResult; + tResult.x11 = tR0.x; + tResult.x21 = tR1.x; + tResult.x31 = tR2.x; + tResult.x41 = tR3.x; + tResult.x12 = tR0.y; + tResult.x22 = tR1.y; + tResult.x32 = tR2.y; + tResult.x42 = tR3.y; + tResult.x13 = tR0.z; + tResult.x23 = tR1.z; + tResult.x33 = tR2.z; + tResult.x43 = tR3.z; + tResult.x14 = -pl_dot_vec3(tB, tT); + tResult.x24 = pl_dot_vec3(tA, tT); + tResult.x34 = -pl_dot_vec3(tD, tS); + tResult.x44 = pl_dot_vec3(tC, tS); + return tResult; +} + +static inline plMat4 +pl_mat4_rotate_quat(plVec4 tQ) +{ + const float x2 = tQ.x * tQ.x; + const float y2 = tQ.y * tQ.y; + const float z2 = tQ.z * tQ.z; + const float xy = tQ.x * tQ.y; + const float xz = tQ.x * tQ.z; + const float yz = tQ.y * tQ.z; + const float wx = tQ.w * tQ.x; + const float wy = tQ.w * tQ.y; + const float wz = tQ.w * tQ.z; + + plMat4 tResult = {0}; + tResult.col[0].x = 1.0f - 2.0f * (y2 + z2); + tResult.col[0].y = 2.0f * (xy + wz); + tResult.col[0].z = 2.0f * (xz - wy); + + tResult.col[1].x = 2.0f * (xy - wz); + tResult.col[1].y = 1.0f - 2.0f * (x2 + z2); + tResult.col[1].z = 2.0f * (yz + wx); + + tResult.col[2].x = 2.0f * (xz + wy); + tResult.col[2].y = 2.0f * (yz - wx); + tResult.col[2].z = 1.0f - 2.0f * (x2 + y2); + + tResult.col[3].w = 1.0f; + + return tResult; +} + +static inline plMat4 +pl_rotation_translation_scale(plVec4 tQ, plVec3 tV, plVec3 tS) +{ + + const plMat4 tScale = pl_mat4_scale_vec3(tS); + const plMat4 tTranslation = pl_mat4_translate_vec3(tV); + const plMat4 tRotation = pl_mat4_rotate_quat(tQ); + + plMat4 tResult0 = pl_mul_mat4(&tRotation, &tScale); + tResult0 = pl_mul_mat4(&tTranslation, &tResult0); + return tResult0; +} + +static inline plMat4 +pl_mul_mat4t(const plMat4* ptLeft, const plMat4* ptRight) +{ + plMat4 tResult = pl_create_mat4_diag(0.0f, 0.0f, 0.0f, 1.0f); + + // row 0 + tResult.x11 = ptLeft->x11 * ptRight->x11 + ptLeft->x12 * ptRight->x21 + ptLeft->x13 * ptRight->x31; + tResult.x12 = ptLeft->x11 * ptRight->x12 + ptLeft->x12 * ptRight->x22 + ptLeft->x13 * ptRight->x32; + tResult.x13 = ptLeft->x11 * ptRight->x13 + ptLeft->x12 * ptRight->x23 + ptLeft->x13 * ptRight->x33; + tResult.x14 = ptLeft->x11 * ptRight->x14 + ptLeft->x12 * ptRight->x24 + ptLeft->x13 * ptRight->x34 + ptLeft->x14; + + // row 1 + tResult.x21 = ptLeft->x21 * ptRight->x11 + ptLeft->x22 * ptRight->x21 + ptLeft->x23 * ptRight->x31; + tResult.x22 = ptLeft->x21 * ptRight->x12 + ptLeft->x22 * ptRight->x22 + ptLeft->x23 * ptRight->x32; + tResult.x23 = ptLeft->x21 * ptRight->x13 + ptLeft->x22 * ptRight->x23 + ptLeft->x23 * ptRight->x33; + tResult.x24 = ptLeft->x21 * ptRight->x14 + ptLeft->x22 * ptRight->x24 + ptLeft->x23 * ptRight->x34 + ptLeft->x24; + + // row 2 + tResult.x31 = ptLeft->x31 * ptRight->x11 + ptLeft->x32 * ptRight->x21 + ptLeft->x33 * ptRight->x31; + tResult.x32 = ptLeft->x31 * ptRight->x12 + ptLeft->x32 * ptRight->x22 + ptLeft->x33 * ptRight->x32; + tResult.x33 = ptLeft->x31 * ptRight->x13 + ptLeft->x32 * ptRight->x23 + ptLeft->x33 * ptRight->x33; + tResult.x34 = ptLeft->x31 * ptRight->x14 + ptLeft->x32 * ptRight->x24 + ptLeft->x33 * ptRight->x34 + ptLeft->x34; + + return tResult; +} + +static inline plMat4 +pl_mat4t_invert(const plMat4* ptMat) +{ + const plVec3 tA = ptMat->col[0].xyz; + const plVec3 tB = ptMat->col[1].xyz; + const plVec3 tC = ptMat->col[2].xyz; + const plVec3 tD = ptMat->col[3].xyz; + + plVec3 tS = pl_cross_vec3(tA, tB); + plVec3 tT = pl_cross_vec3(tC, tD); + + const float fInvDet = 1.0f / pl_dot_vec3(tS, tC); + tS = pl_mul_vec3_scalarf(tS, fInvDet); + tT = pl_mul_vec3_scalarf(tT, fInvDet); + + const plVec3 tV = pl_mul_vec3_scalarf(tC, fInvDet); + const plVec3 tR0 = pl_cross_vec3(tB, tV); + const plVec3 tR1 = pl_cross_vec3(tV, tA); + + plMat4 tResult; + tResult.x11 = tR0.x; + tResult.x21 = tR1.x; + tResult.x31 = tS.x; + tResult.x41 = 0.0f; + tResult.x12 = tR0.y; + tResult.x22 = tR1.y; + tResult.x32 = tS.y; + tResult.x42 = 0.0f; + tResult.x13 = tR0.z; + tResult.x23 = tR1.z; + tResult.x33 = tS.z; + tResult.x43 = 0.0f; + tResult.x14 = -pl_dot_vec3(tB, tT); + tResult.x24 = pl_dot_vec3(tA, tT); + tResult.x34 = -pl_dot_vec3(tD, tS); + tResult.x44 = 1.0f; + return tResult; +} + +static inline void +pl_decompose_matrix(const plMat4* ptM, plVec3* ptS, plVec4* ptQ, plVec3* ptT) +{ + // method borrowed from blender source + + // caller must ensure matrices aren't negative for valid results + + *ptT = ptM->col[3].xyz; + + ptS->x = pl_length_vec3(ptM->col[0].xyz); + ptS->y = pl_length_vec3(ptM->col[1].xyz); + ptS->z = pl_length_vec3(ptM->col[2].xyz); + + // method outlined by Mike Day, ref: https://math.stackexchange.com/a/3183435/220949 + // with an additional `sqrtf(..)` for higher precision result. + + plMat4 tMat = {0}; + tMat.col[0] = pl_norm_vec4(ptM->col[0]); + tMat.col[1] = pl_norm_vec4(ptM->col[1]); + tMat.col[2] = pl_norm_vec4(ptM->col[2]); + + if (tMat.col[2].d[2] < 0.0f) + { + if (tMat.col[0].d[0] > tMat.col[1].d[1]) + { + const float fTrace = 1.0f + tMat.col[0].d[0] - tMat.col[1].d[1] - tMat.col[2].d[2]; + float fS = 2.0f * sqrtf(fTrace); + if (tMat.col[1].d[2] < tMat.col[2].d[1]) // ensure W is non-negative for a canonical result + fS = -fS; + ptQ->d[0] = 0.25f * fS; + fS = 1.0f / fS; + ptQ->d[3] = (tMat.col[1].d[2] - tMat.col[2].d[1]) * fS; + ptQ->d[1] = (tMat.col[0].d[1] + tMat.col[1].d[0]) * fS; + ptQ->d[2] = (tMat.col[2].d[0] + tMat.col[0].d[2]) * fS; + if ((fTrace == 1.0f) && (ptQ->d[3] == 0.0f && ptQ->d[1] == 0.0f && ptQ->d[2] == 0.0f)) // avoids the need to normalize the degenerate case + ptQ->d[0] = 1.0f; + } + else + { + const float fTrace = 1.0f - tMat.col[0].d[0] + tMat.col[1].d[1] - tMat.col[2].d[2]; + float fS = 2.0f * sqrtf(fTrace); + if (tMat.col[2].d[0] < tMat.col[0].d[2]) // ensure W is non-negative for a canonical result + fS = -fS; + ptQ->d[1] = 0.25f * fS; + fS = 1.0f / fS; + ptQ->d[3] = (tMat.col[2].d[0] - tMat.col[0].d[2]) * fS; + ptQ->d[0] = (tMat.col[0].d[1] + tMat.col[1].d[0]) * fS; + ptQ->d[2] = (tMat.col[1].d[2] + tMat.col[2].d[1]) * fS; + if ((fTrace == 1.0f) && (ptQ->d[3] == 0.0f && ptQ->d[0] == 0.0f && ptQ->d[2] == 0.0f)) // avoids the need to normalize the degenerate case + ptQ->d[1] = 1.0f; + } + } + else + { + if (tMat.col[0].d[0] < -tMat.col[1].d[1]) + { + const float fTrace = 1.0f - tMat.col[0].d[0] - tMat.col[1].d[1] + tMat.col[2].d[2]; + float fS = 2.0f * sqrtf(fTrace); + if (tMat.col[0].d[1] < tMat.col[1].d[0]) // ensure W is non-negative for a canonical result + fS = -fS; + ptQ->d[2] = 0.25f * fS; + fS = 1.0f / fS; + ptQ->d[3] = (tMat.col[0].d[1] - tMat.col[1].d[0]) * fS; + ptQ->d[0] = (tMat.col[2].d[0] + tMat.col[0].d[2]) * fS; + ptQ->d[1] = (tMat.col[1].d[2] + tMat.col[2].d[1]) * fS; + if ((fTrace == 1.0f) && (ptQ->d[3] == 0.0f && ptQ->d[0] == 0.0f && ptQ->d[1] == 0.0f)) // avoids the need to normalize the degenerate case + ptQ->d[2] = 1.0f; + } + else + { + // note: a zero matrix will fall through to this block, + // needed so a zero scaled matrices to return a quaternion without rotation + const float fTrace = 1.0f + tMat.col[0].d[0] + tMat.col[1].d[1] + tMat.col[2].d[2]; + float fS = 2.0f * sqrtf(fTrace); + ptQ->d[3] = 0.25f * fS; + fS = 1.0f / fS; + ptQ->d[0] = (tMat.col[1].d[2] - tMat.col[2].d[1]) * fS; + ptQ->d[1] = (tMat.col[2].d[0] - tMat.col[0].d[2]) * fS; + ptQ->d[2] = (tMat.col[0].d[1] - tMat.col[1].d[0]) * fS; + if ((fTrace == 1.0f) && (ptQ->d[0] == 0.0f && ptQ->d[1] == 0.0f && ptQ->d[2] == 0.0f)) // avoids the need to normalize the degenerate case + ptQ->d[3] = 1.0f; + } + } + + assert(!(ptQ->d[3] < 0.0f)); +} + +static inline plVec4 +pl_quat_slerp(plVec4 tQ1, plVec4 tQ2, float fT) +{ + + // from https://glmatrix.net/docs/quat.js.html + plVec4 tQn1 = pl_norm_vec4(tQ1); + plVec4 tQn2 = pl_norm_vec4(tQ2); + + plVec4 tResult = {0}; + + float fAx = tQn1.x; + float fAy = tQn1.y; + float fAz = tQn1.z; + float fAw = tQn1.w; + + float fBx = tQn2.x; + float fBy = tQn2.y; + float fBz = tQn2.z; + float fBw = tQn2.w; + + float fOmega = 0.0f; + float fCosom = fAx * fBx + fAy * fBy + fAz * fBz + fAw * fBw; + float fSinom = 0.0f; + float fScale0 = 0.0f; + float fScale1 = 0.0f; + + // adjust signs (if necessary) + if (fCosom < 0.0f) + { + fCosom = -fCosom; + fBx = -fBx; + fBy = -fBy; + fBz = -fBz; + fBw = -fBw; + } + + // calculate coefficients + if (1.0f - fCosom > 0.000001f) + { + // standard case (slerp) + fOmega = acosf(fCosom); + fSinom = sinf(fOmega); + fScale0 = sinf((1.0f - fT) * fOmega) / fSinom; + fScale1 = sinf(fT * fOmega) / fSinom; + } + else + { + // "from" and "to" quaternions are very close + // ... so we can do a linear interpolation + fScale0 = 1.0f - fT; + fScale1 = fT; + } + + // calculate final values + tResult.d[0] = fScale0 * fAx + fScale1 * fBx; + tResult.d[1] = fScale0 * fAy + fScale1 * fBy; + tResult.d[2] = fScale0 * fAz + fScale1 * fBz; + tResult.d[3] = fScale0 * fAw + fScale1 * fBw; + + tResult = pl_norm_vec4(tResult); + + return tResult; +} + +#endif // PL_MATH_INCLUDE_FUNCTIONS diff --git a/pl_memory.h b/pl_memory.h new file mode 100644 index 0000000..fc181b3 --- /dev/null +++ b/pl_memory.h @@ -0,0 +1,743 @@ +/* + pl_memory + * no dependencies + * simple + + Do this: + #define PL_MEMORY_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define PL_MEMORY_IMPLEMENTATION + #include "pl_memory.h" + Notes: + * allocations return NULL on failure + * general allocation uses malloc, free, & realloc by default + * override general allocators by defining PL_MEMORY_ALLOC(x), PL_MEMORY_FREE(x) + * override assert by defining PL_ASSERT(x) +*/ + +// library version +#define PL_MEMORY_VERSION "0.5.0" +#define PL_MEMORY_VERSION_NUM 00500 + +/* +Index of this file: +// [SECTION] header mess +// [SECTION] defines +// [SECTION] includes +// [SECTION] forward declarations & basic types +// [SECTION] public api +// [SECTION] structs +// [SECTION] c/c++ file start +*/ + +//----------------------------------------------------------------------------- +// [SECTION] header mess +//----------------------------------------------------------------------------- + +#ifndef PL_MEMORY_H +#define PL_MEMORY_H + +//----------------------------------------------------------------------------- +// [SECTION] defines +//----------------------------------------------------------------------------- + +#ifndef PL_MEMORY_TEMP_STACK_SIZE + #define PL_MEMORY_TEMP_STACK_SIZE 1024 +#endif + +//----------------------------------------------------------------------------- +// [SECTION] includes +//----------------------------------------------------------------------------- + +#include // uint*_t +#include // size_t +#include // bool + +//----------------------------------------------------------------------------- +// [SECTION] forward declarations & basic types +//----------------------------------------------------------------------------- + +typedef struct _plTempAllocator plTempAllocator; +typedef struct _plStackAllocator plStackAllocator; +typedef struct _plPoolAllocator plPoolAllocator; +typedef struct _plPoolAllocatorNode plPoolAllocatorNode; + +typedef size_t plStackAllocatorMarker; + +//----------------------------------------------------------------------------- +// [SECTION] public api +//----------------------------------------------------------------------------- + +//~~~~~~~~~~~~~~~~~~~~~~~~~general purpose allocation~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void* pl_aligned_alloc(size_t szAlignment, size_t szSize); +void pl_aligned_free (void* pBuffer); + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~virtual memory system~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// Notes +// - API subject to change slightly +// - additional error checks needs to be added +// - committed memory does not necessarily mean the memory has been mapped to physical +// memory. This is happens when the memory is actually touched. Even so, on Windows +// you can not commit more memmory then you have in your page file. +// - uncommitted memory does not necessarily mean the memory will be immediately +// evicted. It is up to the OS. + +size_t pl_get_page_size (void); // returns memory page size +void* pl_virtual_alloc (void* pAddress, size_t szSize); // reserves & commits a block of memory. pAddress is starting address or use NULL to have system choose. szSize must be a multiple of memory page size. +void* pl_virtual_reserve (void* pAddress, size_t szSize); // reserves a block of memory. pAddress is starting address or use NULL to have system choose. szSize must be a multiple of memory page size. +void* pl_virtual_commit (void* pAddress, size_t szSize); // commits a block of reserved memory. szSize must be a multiple of memory page size. +void pl_virtual_uncommit(void* pAddress, size_t szSize); // uncommits a block of committed memory. +void pl_virtual_free (void* pAddress, size_t szSize); // frees a block of previously reserved/committed memory. Must be the starting address returned from "pl_virtual_reserve()" or "pl_virtual_alloc()" + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~temporary allocator~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void* pl_temp_allocator_alloc (plTempAllocator* ptAllocator, size_t szSize); +void pl_temp_allocator_reset (plTempAllocator* ptAllocator); +void pl_temp_allocator_free (plTempAllocator* ptAllocator); +char* pl_temp_allocator_sprintf(plTempAllocator* ptAllocator, const char* cPFormat, ...); + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~stack allocators~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// common +void pl_stack_allocator_init (plStackAllocator* ptAllocator, size_t szSize, void* pBuffer); + +// single stack +void* pl_stack_allocator_alloc (plStackAllocator* ptAllocator, size_t szSize); +void* pl_stack_allocator_aligned_alloc (plStackAllocator* ptAllocator, size_t szSize, size_t szAlignment); +plStackAllocatorMarker pl_stack_allocator_marker (plStackAllocator* ptAllocator); +void pl_stack_allocator_free_to_marker(plStackAllocator* ptAllocator, plStackAllocatorMarker tMarker); +void pl_stack_allocator_reset (plStackAllocator* ptAllocator); + +// double sided stack +void* pl_stack_allocator_aligned_alloc_bottom (plStackAllocator* ptAllocator, size_t szSize, size_t szAlignment); +plStackAllocatorMarker pl_stack_allocator_top_marker (plStackAllocator* ptAllocator); +plStackAllocatorMarker pl_stack_allocator_bottom_marker (plStackAllocator* ptAllocator); +void* pl_stack_allocator_alloc_bottom (plStackAllocator* ptAllocator, size_t szSize); +void* pl_stack_allocator_alloc_top (plStackAllocator* ptAllocator, size_t szSize); +void pl_stack_allocator_free_top_to_marker (plStackAllocator* ptAllocator, plStackAllocatorMarker tMarker); +void pl_stack_allocator_free_bottom_to_marker(plStackAllocator* ptAllocator, plStackAllocatorMarker tMarker); + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pool allocator~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +void pl_pool_allocator_init (plPoolAllocator* ptAllocator, size_t szItemCount, size_t szItemSize, size_t szItemAlignment, size_t* pszBufferSize, void* pBuffer); +void* pl_pool_allocator_alloc(plPoolAllocator* ptAllocator); +void pl_pool_allocator_free (plPoolAllocator* ptAllocator, void* pItem); + +//----------------------------------------------------------------------------- +// [SECTION] structs +//----------------------------------------------------------------------------- + +typedef struct _plTempAllocator +{ + size_t szSize; + size_t szOffset; + char* pcBuffer; + char acStackBuffer[PL_MEMORY_TEMP_STACK_SIZE]; + char** ppcMemoryBlocks; + size_t szMemoryBlockCount; + size_t szMemoryBlockCapacity; +} plTempAllocator; + +typedef struct _plStackAllocator +{ + unsigned char* pucBuffer; + size_t szSize; + size_t szBottomOffset; + size_t szTopOffset; +} plStackAllocator; + +typedef struct _plPoolAllocatorNode +{ + plPoolAllocatorNode* ptNextNode; +} plPoolAllocatorNode; + +typedef struct _plPoolAllocator +{ + unsigned char* pucBuffer; + size_t szGivenSize; + size_t szUsableSize; + size_t szRequestedItemSize; + size_t szItemSize; + size_t szFreeItems; + plPoolAllocatorNode* pFreeList; +} plPoolAllocator; + +#endif // PL_MEMORY_H + +//----------------------------------------------------------------------------- +// [SECTION] c/c++ file start +//----------------------------------------------------------------------------- + +/* +Index of this file: +// [SECTION] defines +// [SECTION] internal api +// [SECTION] public api implementation +// [SECTION] internal api implementation +*/ + +//----------------------------------------------------------------------------- +// [SECTION] defines +//----------------------------------------------------------------------------- + +#ifdef PL_MEMORY_IMPLEMENTATION + +#if defined(PL_MEMORY_ALLOC) && defined(PL_MEMORY_FREE) +// ok +#elif !defined(PL_MEMORY_ALLOC) && !defined(PL_MEMORY_FREE) +// ok +#else +#error "Must define both or none of PL_MEMORY_ALLOC and PL_MEMORY_FREE" +#endif + +#ifndef PL_MEMORY_ALLOC + #include + #define PL_MEMORY_ALLOC(x) malloc(x) + #define PL_MEMORY_FREE(x) free(x) +#endif + + +#ifndef PL_ASSERT +#include +#define PL_ASSERT(x) assert((x)) +#endif + +#ifndef PL_MEMORY_TEMP_STACK_BLOCK_SIZE + #define PL_MEMORY_TEMP_BLOCK_SIZE 4194304 +#endif + +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + #include // VirtualAlloc, VirtualFree + #include // page size +#elif defined(__APPLE__) + #include + #include +#else // linux + #include + #include +#endif + +#define PL__ALIGN_UP(num, align) (((num) + ((align)-1)) & ~((align)-1)) + +#ifndef pl_vnsprintf +#include +#define pl_vnsprintf vnsprintf +#endif + +#include + +//----------------------------------------------------------------------------- +// [SECTION] internal api +//----------------------------------------------------------------------------- + +static inline size_t +pl__get_next_power_of_2(size_t n) +{ + size_t ulResult = 1; + if (n && !(n & (n - 1))) + ulResult = n; + while (ulResult < n) + ulResult <<= 1; + return ulResult; +} + +static inline uintptr_t +pl__align_forward_uintptr(uintptr_t ptr, size_t szAlign) +{ + PL_ASSERT((szAlign & (szAlign-1)) == 0 && "alignment must be power of 2"); + uintptr_t a = (uintptr_t)szAlign; + uintptr_t p = ptr; + uintptr_t pModulo = p & (a - 1); + if (pModulo != 0){ p += a - pModulo;} + return p; +} + +static inline size_t +pl__align_forward_size(size_t szPtr, size_t szAlign) +{ + PL_ASSERT((szAlign & (szAlign-1)) == 0 && "alignment must be power of 2"); + size_t a = szAlign; + size_t p = szPtr; + size_t szModulo = p & (a - 1); + if (szModulo != 0){ p += a - szModulo;} + return p; +} + + +//----------------------------------------------------------------------------- +// [SECTION] public api implementation +//----------------------------------------------------------------------------- + +size_t +pl_get_page_size(void) +{ + #ifdef _WIN32 + SYSTEM_INFO tInfo = {0}; + GetSystemInfo(&tInfo); + return (size_t)tInfo.dwPageSize; + #elif defined(__APPLE__) + return (size_t)getpagesize(); + #else // linux + return (size_t)getpagesize(); + #endif +} + +void* +pl_virtual_alloc(void* pAddress, size_t szSize) +{ + #ifdef _WIN32 + return VirtualAlloc(pAddress, szSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + #elif defined(__APPLE__) + void* pResult = mmap(pAddress, szSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + return pResult; + #else // linux + void* pResult = mmap(pAddress, szSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + return pResult; + #endif +} + +void* +pl_virtual_reserve(void* pAddress, size_t szSize) +{ + #ifdef _WIN32 + return VirtualAlloc(pAddress, szSize, MEM_RESERVE, PAGE_READWRITE); + #elif defined(__APPLE__) + void* pResult = mmap(pAddress, szSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + return pResult; + #else // linux + void* pResult = mmap(pAddress, szSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + return pResult; + #endif +} + +void* +pl_virtual_commit(void* pAddress, size_t szSize) +{ + #ifdef _WIN32 + return VirtualAlloc(pAddress, szSize, MEM_COMMIT, PAGE_READWRITE); + #elif defined(__APPLE__) + mprotect(pAddress, szSize, PROT_READ | PROT_WRITE); + return pAddress; + #else // linux + mprotect(pAddress, szSize, PROT_READ | PROT_WRITE); + return pAddress; + #endif +} + +void +pl_virtual_free(void* pAddress, size_t szSize) +{ + #ifdef _WIN32 + PL_ASSERT(VirtualFree(pAddress, szSize, MEM_RELEASE)); + #elif defined(__APPLE__) + PL_ASSERT(munmap(pAddress, szSize) == 0); + #else // linux + PL_ASSERT(munmap(pAddress, szSize) == 0); //-V586 + #endif +} + +void +pl_virtual_uncommit(void* pAddress, size_t szSize) +{ + #ifdef _WIN32 + PL_ASSERT(VirtualFree(pAddress, szSize, MEM_DECOMMIT)); + #elif defined(__APPLE__) + mprotect(pAddress, szSize, PROT_NONE); + #else // linux + mprotect(pAddress, szSize, PROT_NONE); + #endif +} + +void* +pl_aligned_alloc(size_t szAlignment, size_t szSize) +{ + void* pBuffer = NULL; + + // ensure power of 2 + PL_ASSERT((szAlignment & (szAlignment -1)) == 0 && "alignment must be a power of 2"); + + if(szAlignment && szSize) + { + // allocate extra bytes for alignment + uint64_t ulHeaderSize = sizeof(uint64_t) + (szAlignment - 1); + void* pActualBuffer = PL_MEMORY_ALLOC(szSize + ulHeaderSize); + + if(pActualBuffer) + { + // add offset size to pointer & align + pBuffer = (void*)PL__ALIGN_UP(((uintptr_t)pActualBuffer + sizeof(uint64_t)), szAlignment); + + // calculate offset & store it behind aligned pointer + *((uint64_t*)pBuffer - 1) = (uint64_t)((uintptr_t)pBuffer - (uintptr_t)pActualBuffer); + } + } + return pBuffer; +} + +void +pl_aligned_free(void* pBuffer) +{ + PL_ASSERT(pBuffer); + + // get stored offset + uint64_t ulOffset = *((uint64_t*)pBuffer - 1); + + // get original buffer to free + void* pActualBuffer = ((uint8_t*)pBuffer - ulOffset); + PL_MEMORY_FREE(pActualBuffer); +} + +void* +pl_temp_allocator_alloc(plTempAllocator* ptAllocator, size_t szSize) +{ + + if(ptAllocator->szSize == 0) // first usage ever + { + ptAllocator->ppcMemoryBlocks = NULL; + ptAllocator->szMemoryBlockCount = 0; + ptAllocator->szMemoryBlockCapacity = 0; + ptAllocator->szSize = PL_MEMORY_TEMP_STACK_SIZE; + ptAllocator->pcBuffer = ptAllocator->acStackBuffer; + ptAllocator->szOffset = 0; + memset(ptAllocator->acStackBuffer, 0, PL_MEMORY_TEMP_STACK_SIZE); + } + + void* pRequestedMemory = NULL; + + // not enough room is available + if(szSize > ptAllocator->szSize - ptAllocator->szOffset) + { + PL_ASSERT(szSize < PL_MEMORY_TEMP_BLOCK_SIZE); + if(ptAllocator->szMemoryBlockCapacity == 0) // first overflow + { + // allocate block array + ptAllocator->szMemoryBlockCapacity = 1; + ptAllocator->ppcMemoryBlocks = (char**)PL_MEMORY_ALLOC(sizeof(char*) * ptAllocator->szMemoryBlockCapacity); + memset(ptAllocator->ppcMemoryBlocks, 0, (sizeof(char*) * ptAllocator->szMemoryBlockCapacity)); + + // allocate first block + ptAllocator->ppcMemoryBlocks[0] = (char*)PL_MEMORY_ALLOC(PL_MEMORY_TEMP_BLOCK_SIZE); + ptAllocator->szSize = PL_MEMORY_TEMP_BLOCK_SIZE; + ptAllocator->szOffset = 0; + ptAllocator->pcBuffer = ptAllocator->ppcMemoryBlocks[0]; + } + else if(ptAllocator->szMemoryBlockCount == ptAllocator->szMemoryBlockCapacity) // grow memory block storage + { + char** ppcOldBlocks = ptAllocator->ppcMemoryBlocks; + ptAllocator->ppcMemoryBlocks = (char**)PL_MEMORY_ALLOC(sizeof(char*) * (ptAllocator->szMemoryBlockCapacity + 1)); + memset(ptAllocator->ppcMemoryBlocks, 0, (sizeof(char*) * (ptAllocator->szMemoryBlockCapacity + 1))); + memcpy(ptAllocator->ppcMemoryBlocks, ppcOldBlocks, sizeof(char*) * ptAllocator->szMemoryBlockCapacity); + ptAllocator->szMemoryBlockCapacity++; + ptAllocator->ppcMemoryBlocks[ptAllocator->szMemoryBlockCount] = (char*)PL_MEMORY_ALLOC(PL_MEMORY_TEMP_BLOCK_SIZE); + ptAllocator->szSize = PL_MEMORY_TEMP_BLOCK_SIZE; + ptAllocator->pcBuffer = ptAllocator->ppcMemoryBlocks[ptAllocator->szMemoryBlockCount]; + ptAllocator->szOffset = 0; + } + else // block is available + { + ptAllocator->szSize = PL_MEMORY_TEMP_BLOCK_SIZE; + ptAllocator->szOffset = 0; + ptAllocator->pcBuffer = ptAllocator->ppcMemoryBlocks[ptAllocator->szMemoryBlockCount]; + } + + ptAllocator->szMemoryBlockCount++; + } + + pRequestedMemory = &ptAllocator->pcBuffer[ptAllocator->szOffset]; + ptAllocator->szOffset += szSize; + return pRequestedMemory; +} + +void +pl_temp_allocator_reset(plTempAllocator* ptAllocator) +{ + ptAllocator->szSize = PL_MEMORY_TEMP_STACK_SIZE; + ptAllocator->szOffset = 0; + ptAllocator->szMemoryBlockCount = 0; + ptAllocator->pcBuffer = ptAllocator->acStackBuffer; +} + +void +pl_temp_allocator_free(plTempAllocator* ptAllocator) +{ + for(size_t i = 0; i < ptAllocator->szMemoryBlockCapacity; i++) + { + PL_MEMORY_FREE(ptAllocator->ppcMemoryBlocks[i]); + } + if(ptAllocator->ppcMemoryBlocks) + PL_MEMORY_FREE(ptAllocator->ppcMemoryBlocks); + ptAllocator->ppcMemoryBlocks = NULL; + ptAllocator->szMemoryBlockCapacity = 0; + ptAllocator->szMemoryBlockCount = 0; + ptAllocator->pcBuffer = NULL; + ptAllocator->szSize = 0; + ptAllocator->szOffset = 0; +} + +inline static char* +pl__temp_allocator_sprintf_va(plTempAllocator* ptAllocator, const char* cPFormat, va_list args) +{ + void* pRequestedMemory = NULL; + + // sprint + va_list args2; + va_copy(args2, args); + int32_t n = pl_vnsprintf(NULL, 0, cPFormat, args2); + va_end(args2); + + pRequestedMemory = pl_temp_allocator_alloc(ptAllocator, n + 1); + memset(pRequestedMemory, 0, n + 1); + pl_vnsprintf(pRequestedMemory, n + 1, cPFormat, args); + + return pRequestedMemory; +} + +char* +pl_temp_allocator_sprintf(plTempAllocator* ptAllocator, const char* cPFormat, ...) +{ + void* pRequestedMemory = NULL; + + va_list argptr; + va_start(argptr, cPFormat); + pRequestedMemory = pl__temp_allocator_sprintf_va(ptAllocator, cPFormat, argptr); + va_end(argptr); + + return pRequestedMemory; +} + +void +pl_stack_allocator_init(plStackAllocator* ptAllocator, size_t szSize, void* pBuffer) +{ + PL_ASSERT(ptAllocator); + PL_ASSERT(szSize > 0); + PL_ASSERT(pBuffer); + + ptAllocator->pucBuffer = (unsigned char*)pBuffer; + ptAllocator->szSize = szSize; + ptAllocator->szBottomOffset = 0; + ptAllocator->szTopOffset = szSize; +} + +void* +pl_stack_allocator_alloc(plStackAllocator* ptAllocator, size_t szSize) +{ + size_t szOffset = ptAllocator->szBottomOffset + szSize; + + PL_ASSERT(szOffset < ptAllocator->szTopOffset && "stack allocator full"); + + // update offset + void* pBuffer = ptAllocator->pucBuffer + ptAllocator->szBottomOffset; + ptAllocator->szBottomOffset = szOffset; + + return pBuffer; +} + +void* +pl_stack_allocator_aligned_alloc(plStackAllocator* ptAllocator, size_t szSize, size_t szAlignment) +{ + void* pBuffer = NULL; + + szAlignment = pl__get_next_power_of_2(szAlignment); + uintptr_t pCurrentPointer = (uintptr_t)ptAllocator->pucBuffer + (uintptr_t)ptAllocator->szBottomOffset; + uintptr_t pOffset = pl__align_forward_uintptr(pCurrentPointer, szAlignment); + pOffset -= (uintptr_t)ptAllocator->pucBuffer; + + PL_ASSERT(pOffset + szSize <= ptAllocator->szTopOffset && "linear allocator full"); + + // check if allocator has enough space left + if(pOffset + szSize <= ptAllocator->szSize) + { + pBuffer = &ptAllocator->pucBuffer[pOffset]; + ptAllocator->szBottomOffset = pOffset + szSize; + + // zero new memory + memset(pBuffer, 0, szSize); + } + return pBuffer; +} + +void* +pl_stack_allocator_aligned_alloc_bottom(plStackAllocator* ptAllocator, size_t szSize, size_t szAlignment) +{ + return pl_stack_allocator_aligned_alloc(ptAllocator, szSize, szAlignment); +} + +void* +pl_stack_allocator_aligned_alloc_top(plStackAllocator* ptAllocator, size_t szSize, size_t szAlignment) +{ + void* pBuffer = NULL; + + szAlignment = pl__get_next_power_of_2(szAlignment); + uintptr_t pCurrentPointer = (uintptr_t)ptAllocator->pucBuffer + (uintptr_t)ptAllocator->szBottomOffset; + uintptr_t pOffset = pl__align_forward_uintptr(pCurrentPointer, szAlignment); + pOffset -= (uintptr_t)ptAllocator->pucBuffer; + + PL_ASSERT(pOffset + szSize <= ptAllocator->szTopOffset && "linear allocator full"); + + // check if allocator has enough space left + if(pOffset + szSize <= ptAllocator->szSize) + { + pBuffer = &ptAllocator->pucBuffer[pOffset]; + ptAllocator->szBottomOffset = pOffset + szSize; + + // zero new memory + memset(pBuffer, 0, szSize); + } + return pBuffer; +} + +void* +pl_stack_allocator_alloc_bottom(plStackAllocator* ptAllocator, size_t szSize) +{ + return pl_stack_allocator_alloc(ptAllocator, szSize); +} + +void* +pl_stack_allocator_alloc_top(plStackAllocator* ptAllocator, size_t szSize) +{ + size_t szOffset = ptAllocator->szTopOffset - szSize; + + PL_ASSERT(szOffset > ptAllocator->szBottomOffset && szOffset < ptAllocator->szTopOffset && "stack allocator full"); + + // update offset + void* pBuffer = ptAllocator->pucBuffer + szOffset; + ptAllocator->szTopOffset = szOffset; + + return pBuffer; +} + +plStackAllocatorMarker +pl_stack_allocator_marker(plStackAllocator* ptAllocator) +{ + plStackAllocatorMarker tMarker = ptAllocator->szBottomOffset; + return tMarker; +} + +plStackAllocatorMarker +pl_stack_allocator_bottom_marker(plStackAllocator* ptAllocator) +{ + return pl_stack_allocator_marker(ptAllocator); +} + +plStackAllocatorMarker +pl_stack_allocator_top_marker(plStackAllocator* ptAllocator) +{ + plStackAllocatorMarker tMarker = ptAllocator->szTopOffset; + return tMarker; +} + +void +pl_stack_allocator_free_to_marker(plStackAllocator* ptAllocator, plStackAllocatorMarker tMarker) +{ + ptAllocator->szBottomOffset = tMarker; + + #ifdef _DEBUG + memset(&ptAllocator->pucBuffer[ptAllocator->szBottomOffset], 0, ptAllocator->szTopOffset - ptAllocator->szBottomOffset); + #endif +} + +void +pl_stack_allocator_free_top_to_marker(plStackAllocator* ptAllocator, plStackAllocatorMarker tMarker) +{ + ptAllocator->szTopOffset = tMarker; + + #ifdef _DEBUG + memset(&ptAllocator->pucBuffer[ptAllocator->szBottomOffset], 0, ptAllocator->szTopOffset - ptAllocator->szBottomOffset); + #endif +} + +void +pl_stack_allocator_free_bottom_to_marker(plStackAllocator* ptAllocator, plStackAllocatorMarker tMarker) +{ + ptAllocator->szBottomOffset = tMarker; + + #ifdef _DEBUG + memset(&ptAllocator->pucBuffer[ptAllocator->szBottomOffset], 0, ptAllocator->szTopOffset - ptAllocator->szBottomOffset); + #endif +} + +void +pl_stack_allocator_reset(plStackAllocator* ptAllocator) +{ + ptAllocator->szBottomOffset = 0; + ptAllocator->szTopOffset = ptAllocator->szSize; + + #ifdef _DEBUG + memset(ptAllocator->pucBuffer, 0, ptAllocator->szSize); + #endif +} + +void +pl_pool_allocator_init(plPoolAllocator* ptAllocator, size_t szItemCount, size_t szItemSize, size_t szItemAlignment, size_t* pszBufferSize, void* pBuffer) +{ + PL_ASSERT(ptAllocator); + PL_ASSERT(szItemCount > 0); + PL_ASSERT(szItemSize > 0); + PL_ASSERT(pszBufferSize); + + if(szItemAlignment == 0) + { + szItemAlignment = pl__get_next_power_of_2(szItemSize); + } + + if(pBuffer == NULL) + { + size_t szAlignedItemSize = pl__align_forward_size(szItemSize, szItemAlignment); + *pszBufferSize = szAlignedItemSize * szItemCount + szItemAlignment; + return; + } + + ptAllocator->szFreeItems = szItemCount; + ptAllocator->szRequestedItemSize = szItemSize; + ptAllocator->szGivenSize = *pszBufferSize; + ptAllocator->szUsableSize = *pszBufferSize; + ptAllocator->pucBuffer = (unsigned char*)pBuffer; + ptAllocator->szItemSize = pl__align_forward_size(szItemSize, szItemAlignment); + + uintptr_t pInitialStart = (uintptr_t)pBuffer; + uintptr_t pStart = pl__align_forward_uintptr(pInitialStart, (uintptr_t)szItemAlignment); + ptAllocator->szUsableSize -= (size_t)(pStart - pInitialStart); + + PL_ASSERT(ptAllocator->szItemSize >= sizeof(plPoolAllocatorNode) && "pool allocator item size too small"); + PL_ASSERT(ptAllocator->szUsableSize >= ptAllocator->szItemSize * szItemCount && "pool allocator buffer size too small"); + + unsigned char* pUsableBuffer = (unsigned char*)pStart; + for(size_t i = 0; i < szItemCount - 1; i++) + { + plPoolAllocatorNode* pNode0 = (plPoolAllocatorNode*)&pUsableBuffer[i * ptAllocator->szItemSize]; + plPoolAllocatorNode* pNode1 = (plPoolAllocatorNode*)&pUsableBuffer[(i + 1) * ptAllocator->szItemSize]; + pNode0->ptNextNode = pNode1; + } + ptAllocator->pFreeList = (plPoolAllocatorNode*)pUsableBuffer; +} + +void* +pl_pool_allocator_alloc(plPoolAllocator* ptAllocator) +{ + if(ptAllocator->szFreeItems == 0) + return NULL; + ptAllocator->szFreeItems--; + plPoolAllocatorNode* pFirstNode = ptAllocator->pFreeList; + plPoolAllocatorNode* ptNextNode = pFirstNode->ptNextNode; + ptAllocator->pFreeList = ptNextNode; + memset(pFirstNode, 0, ptAllocator->szItemSize); + return pFirstNode; +} + +void +pl_pool_allocator_free(plPoolAllocator* ptAllocator, void* pItem) +{ + ptAllocator->szFreeItems++; + plPoolAllocatorNode* pOldFreeNode = ptAllocator->pFreeList; + ptAllocator->pFreeList = (plPoolAllocatorNode*)pItem; + ptAllocator->pFreeList->ptNextNode = pOldFreeNode; +} + +#endif diff --git a/pl_profile.h b/pl_profile.h new file mode 100644 index 0000000..bca019e --- /dev/null +++ b/pl_profile.h @@ -0,0 +1,512 @@ +/* + pl_profile + Do this: + #define PL_PROFILE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define PL_PROFILE_IMPLEMENTATION + #include "pl_profile.h" +*/ + +// library version +#define PL_PROFILE_VERSION "0.2.0" +#define PL_PROFILE_VERSION_NUM 00200 + +/* +Index of this file: +// [SECTION] documentation +// [SECTION] header mess +// [SECTION] includes +// [SECTION] forward declarations & basic types +// [SECTION] public api +// [SECTION] structs +// [SECTION] internal api +// [SECTION] c file start +*/ + +//----------------------------------------------------------------------------- +// [SECTION] documentation +//----------------------------------------------------------------------------- + +/* + +SETUP + + pl_create_profile_context: + plProfileContext* pl_create_profile_context(); + Creates the global context used by the profiling system. Store the + pointer returned if you want to use the profiler across DLL boundaries. + See "pl_set_profile_context". + + pl_cleanup_profile_context: + void pl_cleanup_profile_context(); + Frees memory associated with the profiling system. Do not call functions + after this. + + pl_set_profile_context: + void pl_set_profile_context(plProfileContext*); + Sets the current log context. Mostly used to allow profiling across + DLL boundaries. + + pl_get_profile_context: + plProfileContext* pl_get_profile_context(); + Returns the current profile context. + +SAMPLING + + pl_begin_profile_frame: + void pl_begin_profile_frame(); + Begins a CPU profiling frame. Samples can now be taken. + + pl_end_profile_frame: + void pl_end_profile_frame(); + Ends a CPU profiling frame. + + pl_begin_profile_sample: + void pl_begin_profile_sample(pcName); + Begins a CPU sample. Must have begun a profiling frame. + + pl_end_profile_sample: + void pl_end_profile_sample(); + Ends a CPU sample. + +RETRIEVING RESULTS + + pl_get_last_frame_samples: + plProfileSample* pl_get_last_frame_samples(uint32_t* puSize); + Returns samples from last frame. Call after "pl_end_profile_frame". + + +COMPILE TIME OPTIONS + * Turn profiling on by defining PL_PROFILE_ON + * Change allocators by defining both: + PL_PROFILE_ALLOC(x) + PL_PROFILE_FREE(x) +*/ + +//----------------------------------------------------------------------------- +// [SECTION] header mess +//----------------------------------------------------------------------------- + +#ifndef PL_PROFILE_H +#define PL_PROFILE_H + +//----------------------------------------------------------------------------- +// [SECTION] includes +//----------------------------------------------------------------------------- + +#include + +//----------------------------------------------------------------------------- +// [SECTION] forward declarations & basic types +//----------------------------------------------------------------------------- + +// forward declarations +typedef struct _plProfileSample plProfileSample; +typedef struct _plProfileContext plProfileContext; + +//----------------------------------------------------------------------------- +// [SECTION] public api +//----------------------------------------------------------------------------- + +#ifdef PL_PROFILE_ON + +// setup/shutdown +#define pl_create_profile_context(ptContext) pl__create_profile_context() +#define pl_cleanup_profile_context() pl__cleanup_profile_context() +#define pl_set_profile_context(ptContext) pl__set_profile_context((ptContext)) +#define pl_get_profile_context() pl__get_profile_context() + +// frames +#define pl_begin_profile_frame() pl__begin_profile_frame() +#define pl_end_profile_frame() pl__end_profile_frame() + +// samples +#define pl_begin_profile_sample(pcName) pl__begin_profile_sample((pcName)) +#define pl_end_profile_sample() pl__end_profile_sample() +#define pl_get_last_frame_samples(puSize) pl__get_last_frame_samples((puSize)) + +#endif // PL_PROFILE_ON + +//----------------------------------------------------------------------------- +// [SECTION] structs +//----------------------------------------------------------------------------- + +typedef struct _plProfileSample +{ + double dStartTime; + double dDuration; + const char* pcName; + uint32_t uDepth; +} plProfileSample; + +//----------------------------------------------------------------------------- +// [SECTION] internal api +//----------------------------------------------------------------------------- + +// setup/shutdown +plProfileContext* pl__create_profile_context (void); +void pl__cleanup_profile_context(void); +void pl__set_profile_context (plProfileContext* ptContext); +plProfileContext* pl__get_profile_context (void); + +// frames +void pl__begin_profile_frame(void); +void pl__end_profile_frame (void); + +// samples +void pl__begin_profile_sample(const char* pcName); +void pl__end_profile_sample (void); +plProfileSample* pl__get_last_frame_samples(uint32_t* puSize); + +#ifndef PL_PROFILE_ON + #define pl_create_profile_context(ptContext) NULL + #define pl_cleanup_profile_context() // + #define pl_set_profile_context(ptContext) // + #define pl_get_profile_context() NULL + #define pl_begin_profile_frame(ulFrame) // + #define pl_end_profile_frame() // + #define pl_begin_profile_sample(pcName) // + #define pl_end_profile_sample() // + #define pl_get_last_frame_samples(puSize) NULL +#endif + +#endif // PL_PROFILE_H + +//----------------------------------------------------------------------------- +// [SECTION] c file start +//----------------------------------------------------------------------------- + +/* +Index of this file: +// [SECTION] header mess +// [SECTION] includes +// [SECTION] global context +// [SECTION] internal structs +// [SECTION] internal api +// [SECTION] public api implementations +// [SECTION] internal api implementations +*/ + +//----------------------------------------------------------------------------- +// [SECTION] header mess +//----------------------------------------------------------------------------- + +#ifdef PL_PROFILE_IMPLEMENTATION + +#ifndef PL_ASSERT + #include + #define PL_ASSERT(x) assert((x)) +#endif + +#ifndef PL_PROFILE_ALLOC + #include + #define PL_PROFILE_ALLOC(x) malloc((x)) + #define PL_PROFILE_FREE(x) free((x)) +#endif + +//----------------------------------------------------------------------------- +// [SECTION] includes +//----------------------------------------------------------------------------- + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN + #include +#elif defined(__APPLE__) + #include // clock_gettime_nsec_np +#else // linux + #include // clock_gettime, clock_getres +#endif + +//----------------------------------------------------------------------------- +// [SECTION] global context +//----------------------------------------------------------------------------- + +plProfileContext* gTPProfileContext = NULL; + +//----------------------------------------------------------------------------- +// [SECTION] internal structs +//----------------------------------------------------------------------------- + +typedef struct _plProfileFrame +{ + uint64_t ulFrame; + double dStartTime; // beginning of frame time + double dDuration; // total duration + double dInternalDuration; // profiler overhead + + bool bSampleStackOverflowInUse; + uint32_t uTotalSampleStackSize; + uint32_t* puSampleStack; + uint32_t auSampleStack[256]; + uint32_t uSampleStackCapacity; + + uint32_t* puOverflowSampleStack; + uint32_t uOverflowSampleStackCapacity; + + + uint32_t uTotalSampleSize; + plProfileSample* ptSamples; + + bool bOverflowInUse; + plProfileSample atSamples[256]; + uint32_t uSampleCapacity; + uint32_t uOverflowSampleCapacity; +} plProfileFrame; + +typedef struct _plProfileContext +{ + double dStartTime; + uint64_t ulFrame; + plProfileFrame atFrames[2]; + plProfileFrame* ptCurrentFrame; + plProfileFrame* ptLastFrame; + void* pInternal; +} plProfileContext; + +//----------------------------------------------------------------------------- +// [SECTION] internal api +//----------------------------------------------------------------------------- + +static void pl__push_sample_stack(plProfileFrame* ptFrame, uint32_t uSample); +static plProfileSample* pl__get_sample(plProfileFrame* ptFrame); + +static inline uint32_t +pl__pop_sample_stack(plProfileFrame* ptFrame) +{ + ptFrame->uTotalSampleStackSize--; + return ptFrame->puSampleStack[ptFrame->uTotalSampleStackSize]; +} + +static inline double +pl__get_wall_clock(void) +{ + double dResult = 0; + #ifdef _WIN32 + INT64 slPerfFrequency = *(INT64*)gTPProfileContext->pInternal; + INT64 slPerfCounter; + QueryPerformanceCounter((LARGE_INTEGER*)&slPerfCounter); + dResult = (double)slPerfCounter / (double)slPerfFrequency; + #elif defined(__APPLE__) + dResult = ((double)(clock_gettime_nsec_np(CLOCK_UPTIME_RAW)) / 1e9); + #else // linux + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + uint64_t nsec_count = ts.tv_nsec + ts.tv_sec * 1e9; + dResult = (double)nsec_count / *(double*)gTPProfileContext->pInternal; + #endif + return dResult; +} + +//----------------------------------------------------------------------------- +// [SECTION] public api implementations +//----------------------------------------------------------------------------- + +plProfileContext* +pl__create_profile_context(void) +{ + // allocate context + plProfileContext* ptContext = (plProfileContext*)PL_PROFILE_ALLOC(sizeof(plProfileContext)); + memset(ptContext, 0, sizeof(plProfileContext)); + gTPProfileContext = ptContext; + + // clock setup + #ifdef _WIN32 + static INT64 slPerfFrequency = 0; + PL_ASSERT(QueryPerformanceFrequency((LARGE_INTEGER*)&slPerfFrequency)); + ptContext->pInternal = &slPerfFrequency; + #elif defined(__APPLE__) + // no setup required + #else // linux + static struct timespec ts; + if (clock_getres(CLOCK_MONOTONIC, &ts) != 0) + { + PL_ASSERT(false && "clock_getres() failed"); + } + + static double dPerFrequency = 0.0; + dPerFrequency = 1e9/((double)ts.tv_nsec + (double)ts.tv_sec * (double)1e9); + ptContext->pInternal = &dPerFrequency; + #endif + + ptContext->dStartTime = pl__get_wall_clock(); + ptContext->ptCurrentFrame = &ptContext->atFrames[0]; + ptContext->atFrames[0].uSampleCapacity = 256; + ptContext->atFrames[0].uSampleStackCapacity = 256; + ptContext->atFrames[1].uSampleCapacity = 256; + ptContext->atFrames[1].uSampleStackCapacity = 256; + ptContext->atFrames[0].ptSamples = ptContext->atFrames[0].atSamples; + ptContext->atFrames[1].ptSamples = ptContext->atFrames[1].atSamples; + ptContext->atFrames[0].puSampleStack = ptContext->atFrames[0].auSampleStack; + ptContext->atFrames[1].puSampleStack = ptContext->atFrames[1].auSampleStack; + ptContext->ptLastFrame = &ptContext->atFrames[0]; + return ptContext; +} + +void +pl__cleanup_profile_context(void) +{ + + for(uint32_t i = 0; i < 2; i++) + { + if(gTPProfileContext->atFrames[i].bOverflowInUse) + PL_PROFILE_FREE(gTPProfileContext->atFrames[i].ptSamples); + + if(gTPProfileContext->atFrames[i].bSampleStackOverflowInUse) + PL_PROFILE_FREE(gTPProfileContext->atFrames[i].puSampleStack); + } + + PL_PROFILE_FREE(gTPProfileContext); + gTPProfileContext = NULL; +} + +void +pl__set_profile_context(plProfileContext* ptContext) +{ + PL_ASSERT(ptContext && "profile context is NULL"); + gTPProfileContext = ptContext; +} + +plProfileContext* +pl__get_profile_context(void) +{ + PL_ASSERT(gTPProfileContext && "no global log context set"); + return gTPProfileContext; +} + +void +pl__begin_profile_frame(void) +{ + gTPProfileContext->ulFrame++; + gTPProfileContext->ptCurrentFrame = &gTPProfileContext->atFrames[gTPProfileContext->ulFrame % 2]; + gTPProfileContext->ptCurrentFrame->dDuration = 0.0; + gTPProfileContext->ptCurrentFrame->dInternalDuration = 0.0; + gTPProfileContext->ptCurrentFrame->dStartTime = pl__get_wall_clock(); + gTPProfileContext->ptCurrentFrame->uTotalSampleSize = 0; +} + +void +pl__end_profile_frame(void) +{ + gTPProfileContext->ptCurrentFrame->dDuration = pl__get_wall_clock() - gTPProfileContext->ptCurrentFrame->dStartTime; + gTPProfileContext->ptLastFrame = gTPProfileContext->ptCurrentFrame; +} + +void +pl__begin_profile_sample(const char* pcName) +{ + const double dCurrentInternalTime = pl__get_wall_clock(); + plProfileFrame* ptCurrentFrame = gTPProfileContext->ptCurrentFrame; + + uint32_t uSampleIndex = ptCurrentFrame->uTotalSampleSize; + plProfileSample* ptSample = pl__get_sample(ptCurrentFrame); + ptSample->dDuration = 0.0; + ptSample->dStartTime = pl__get_wall_clock(); + ptSample->pcName = pcName; + ptSample->uDepth = ptCurrentFrame->uTotalSampleStackSize; + + pl__push_sample_stack(ptCurrentFrame, uSampleIndex); + + ptCurrentFrame->dInternalDuration += pl__get_wall_clock() - dCurrentInternalTime; +} + +void +pl__end_profile_sample(void) +{ + const double dCurrentInternalTime = pl__get_wall_clock(); + plProfileFrame* ptCurrentFrame = gTPProfileContext->ptCurrentFrame; + plProfileSample* ptLastSample = &ptCurrentFrame->ptSamples[pl__pop_sample_stack(ptCurrentFrame)]; + PL_ASSERT(ptLastSample && "Begin/end profile sample mismatch"); + ptLastSample->dDuration = pl__get_wall_clock() - ptLastSample->dStartTime; + ptLastSample->dStartTime -= ptCurrentFrame->dStartTime; + ptCurrentFrame->dInternalDuration += pl__get_wall_clock() - dCurrentInternalTime; +} + +plProfileSample* +pl__get_last_frame_samples(uint32_t* puSize) +{ + plProfileFrame* ptFrame = gTPProfileContext->ptLastFrame; + + if(puSize) + *puSize = ptFrame->uTotalSampleSize; + return ptFrame->ptSamples; +} + +//----------------------------------------------------------------------------- +// [SECTION] internal api implementations +//----------------------------------------------------------------------------- + +static void +pl__push_sample_stack(plProfileFrame* ptFrame, uint32_t uSample) +{ + // check if new overflow + if(!ptFrame->bSampleStackOverflowInUse && ptFrame->uTotalSampleStackSize == ptFrame->uSampleStackCapacity) + { + ptFrame->puOverflowSampleStack = (uint32_t*)PL_PROFILE_ALLOC(sizeof(uint32_t) * ptFrame->uSampleStackCapacity * 2); + memset(ptFrame->puOverflowSampleStack, 0, sizeof(uint32_t) * ptFrame->uSampleStackCapacity * 2); + ptFrame->uOverflowSampleStackCapacity = ptFrame->uSampleStackCapacity * 2; + + // copy stack samples + memcpy(ptFrame->puOverflowSampleStack, ptFrame->auSampleStack, sizeof(uint32_t) * ptFrame->uSampleStackCapacity); + ptFrame->bSampleStackOverflowInUse = true; + ptFrame->puSampleStack = ptFrame->puOverflowSampleStack; + } + // check if overflow reallocation is needed + else if(ptFrame->bSampleStackOverflowInUse && ptFrame->uTotalSampleStackSize == ptFrame->uOverflowSampleStackCapacity) + { + uint32_t* ptOldOverflowSamples = ptFrame->puOverflowSampleStack; + ptFrame->puOverflowSampleStack = (uint32_t*)PL_PROFILE_ALLOC(sizeof(uint32_t) * ptFrame->uOverflowSampleStackCapacity * 2); + memset(ptFrame->puOverflowSampleStack, 0, sizeof(uint32_t) * ptFrame->uOverflowSampleStackCapacity * 2); + + // copy old values + memcpy(ptFrame->puOverflowSampleStack, ptOldOverflowSamples, sizeof(uint32_t) * ptFrame->uOverflowSampleStackCapacity); + ptFrame->uOverflowSampleStackCapacity *= 2; + + PL_PROFILE_FREE(ptOldOverflowSamples); + ptFrame->puSampleStack = ptFrame->puOverflowSampleStack; + } + + ptFrame->puSampleStack[ptFrame->uTotalSampleStackSize] = uSample; + ptFrame->uTotalSampleStackSize++; +} + +static plProfileSample* +pl__get_sample(plProfileFrame* ptFrame) +{ + plProfileSample* ptSample = NULL; + + // check if new overflow + if(!ptFrame->bOverflowInUse && ptFrame->uTotalSampleSize == ptFrame->uSampleCapacity) + { + ptFrame->ptSamples = (plProfileSample*)PL_PROFILE_ALLOC(sizeof(plProfileSample) * ptFrame->uSampleCapacity * 2); + memset(ptFrame->ptSamples, 0, sizeof(plProfileSample) * ptFrame->uSampleCapacity * 2); + ptFrame->uOverflowSampleCapacity = ptFrame->uSampleCapacity * 2; + + // copy stack samples + memcpy(ptFrame->ptSamples, ptFrame->atSamples, sizeof(plProfileSample) * ptFrame->uSampleCapacity); + ptFrame->bOverflowInUse = true; + } + // check if overflow reallocation is needed + else if(ptFrame->bOverflowInUse && ptFrame->uTotalSampleSize == ptFrame->uOverflowSampleCapacity) + { + plProfileSample* ptOldOverflowSamples = ptFrame->ptSamples; + ptFrame->ptSamples = (plProfileSample*)PL_PROFILE_ALLOC(sizeof(plProfileSample) * ptFrame->uOverflowSampleCapacity * 2); + memset(ptFrame->ptSamples, 0, sizeof(plProfileSample) * ptFrame->uOverflowSampleCapacity * 2); + + // copy old values + memcpy(ptFrame->ptSamples, ptOldOverflowSamples, sizeof(plProfileSample) * ptFrame->uOverflowSampleCapacity); + ptFrame->uOverflowSampleCapacity *= 2; + + PL_PROFILE_FREE(ptOldOverflowSamples); + } + + ptSample = &ptFrame->ptSamples[ptFrame->uTotalSampleSize]; + ptFrame->uTotalSampleSize++; + + return ptSample; +} + +#endif // PL_PROFILE_IMPLEMENTATION \ No newline at end of file diff --git a/pl_stl.h b/pl_stl.h new file mode 100644 index 0000000..ed332f2 --- /dev/null +++ b/pl_stl.h @@ -0,0 +1,335 @@ +/* + pl_stl.h +*/ + +// library version +#define PL_STL_VERSION "0.2.0" +#define PL_STL_VERSION_NUM 00200 + +/* +Index of this file: +// [SECTION] header mess +// [SECTION] includes +// [SECTION] forward declarations & basic types +// [SECTION] public api +// [SECTION] structs +// [SECTION] c file +*/ + +//----------------------------------------------------------------------------- +// [SECTION] header mess +//----------------------------------------------------------------------------- + +#ifndef PL_STL_H +#define PL_STL_H + +//----------------------------------------------------------------------------- +// [SECTION] includes +//----------------------------------------------------------------------------- + +#include + +//----------------------------------------------------------------------------- +// [SECTION] forward declarations & basic types +//----------------------------------------------------------------------------- + +typedef struct _plStlInfo plStlInfo; + +//----------------------------------------------------------------------------- +// [SECTION] public api +//----------------------------------------------------------------------------- + +void pl_load_stl(const char* pcData, size_t szDataSize, float* afPositionStream, float* afNormalStream, uint32_t* auIndexBuffer, plStlInfo* ptInfoOut); + +//----------------------------------------------------------------------------- +// [SECTION] structs +//----------------------------------------------------------------------------- + +typedef struct _plStlInfo +{ + size_t szPositionStreamSize; + size_t szNormalStreamSize; + size_t szIndexBufferSize; + bool bPreloaded; +} plStlInfo; + +#endif // PL_STL_H + +//----------------------------------------------------------------------------- +// [SECTION] c file +//----------------------------------------------------------------------------- + +/* +Index of this file: +// [SECTION] header mess +// [SECTION] includes +// [SECTION] internal api +// [SECTION] public api implementation +// [SECTION] internal api implementation +*/ + +//----------------------------------------------------------------------------- +// [SECTION] header mess +//----------------------------------------------------------------------------- + +#ifdef PL_STL_IMPLEMENTATION + +//----------------------------------------------------------------------------- +// [SECTION] includes +//----------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +//----------------------------------------------------------------------------- +// [SECTION] internal api +//----------------------------------------------------------------------------- + +static bool pl__move_to_next_line(const char* pcData, size_t szDataSize, size_t* pszCurrentPos); +static char pl__move_to_first_char(const char* pcData, size_t szDataSize, size_t* pszCurrentPos); + +//----------------------------------------------------------------------------- +// [SECTION] public api implementation +//----------------------------------------------------------------------------- + +void +pl_load_stl(const char* pcData, size_t szDataSize, float* afPositionStream, float* afNormalStream, uint32_t* auIndexBuffer, plStlInfo* ptInfoOut) +{ + plStlInfo _tInternalInfo = {0}; + + if(ptInfoOut == NULL) + ptInfoOut = &_tInternalInfo; + + bool bAsci = strncmp(pcData, "solid", 5) == 0; + size_t szFacetCount = ptInfoOut->bPreloaded ? ptInfoOut->szIndexBufferSize / 3 : 0; + size_t szCurrentCursor = 0; + size_t szVertexCount = ptInfoOut->bPreloaded ? ptInfoOut->szIndexBufferSize : 0; + + if(!ptInfoOut->bPreloaded) + { + + // find number of vertices & facets + if(bAsci) + { + while(pl__move_to_next_line(pcData, szDataSize, &szCurrentCursor)) + { + const char cFirstChar = pl__move_to_first_char(pcData, szDataSize, &szCurrentCursor); + switch(cFirstChar) + { + case 'v': szVertexCount++; break; + case 'f': szFacetCount++; break; + default: break; + } + } + } + else + { + szFacetCount = *(unsigned int*)&pcData[80]; + szVertexCount = szFacetCount * 3; + } + + ptInfoOut->bPreloaded = true; + } + + ptInfoOut->szIndexBufferSize = szFacetCount * 3; + ptInfoOut->szPositionStreamSize = szVertexCount * 3; + ptInfoOut->szNormalStreamSize = szVertexCount * 3; + + // fill index buffer if provided + if(auIndexBuffer) + { + for(size_t i = 0; i < ptInfoOut->szIndexBufferSize; i++) + { + auIndexBuffer[i] = (uint32_t)i; + } + } + + // reset cursor + szCurrentCursor = 0; + size_t szVertexStream0Pos = 0; + + // fill vertex stream 0 if provided + if(afPositionStream) + { + if(bAsci) + { + while(pl__move_to_next_line(pcData, szDataSize, &szCurrentCursor)) + { + const char cFirstChar = pl__move_to_first_char(pcData, szDataSize, &szCurrentCursor); + if(cFirstChar == 'v') + { + szCurrentCursor += 6; + const char* pcReturnString = &pcData[szCurrentCursor]; + char* pcEnd = NULL; + afPositionStream[szVertexStream0Pos] = strtof(pcReturnString, &pcEnd); + afPositionStream[szVertexStream0Pos + 1] = strtof(pcEnd, &pcEnd); + afPositionStream[szVertexStream0Pos + 2] = strtof(pcEnd, &pcEnd); + afPositionStream += 3; + } + } + } + else + { + szCurrentCursor = 84; + float afFacetBuffer[12] = {0}; + for(int facet = 0; facet < szFacetCount; facet++) + { + memcpy(afFacetBuffer, &pcData[szCurrentCursor], 48); + + // vertex 0 + afPositionStream[szVertexStream0Pos] = afFacetBuffer[3]; + afPositionStream[szVertexStream0Pos + 1] = afFacetBuffer[4]; + afPositionStream[szVertexStream0Pos + 2] = afFacetBuffer[5]; + szVertexStream0Pos += 3; + + + // vertex 1 + afPositionStream[szVertexStream0Pos] = afFacetBuffer[6]; + afPositionStream[szVertexStream0Pos + 1] = afFacetBuffer[7]; + afPositionStream[szVertexStream0Pos + 2] = afFacetBuffer[8]; + szVertexStream0Pos += 3; + + // vertex 2 + afPositionStream[szVertexStream0Pos] = afFacetBuffer[9]; + afPositionStream[szVertexStream0Pos + 1] = afFacetBuffer[10]; + afPositionStream[szVertexStream0Pos + 2] = afFacetBuffer[11]; + szVertexStream0Pos += 3; + + szCurrentCursor += 50; + } + } + } + + szCurrentCursor = 0; + size_t szVertexStream1Pos = 0; + const size_t szStride = 3; + + if(afNormalStream) + { + if(bAsci) + { + while(pl__move_to_next_line(pcData, szDataSize, &szCurrentCursor)) + { + const char cFirstChar = pl__move_to_first_char(pcData, szDataSize, &szCurrentCursor); + if(cFirstChar == 'f') + { + szCurrentCursor += 12; + const char* pcReturnString = &pcData[szCurrentCursor]; + char* pcEnd = NULL; + + const float fNx = strtof(pcReturnString, &pcEnd); + const float fNy = strtof(pcEnd, &pcEnd); + const float fNz = strtof(pcEnd, &pcEnd); + + // vertex 0 + afNormalStream[szVertexStream1Pos] = fNx; + afNormalStream[szVertexStream1Pos + 1] = fNy; + afNormalStream[szVertexStream1Pos + 2] = fNz; + szVertexStream1Pos += szStride; + + // vertex 1 + afNormalStream[szVertexStream1Pos] = fNx; + afNormalStream[szVertexStream1Pos + 1] = fNy; + afNormalStream[szVertexStream1Pos + 2] = fNz; + szVertexStream1Pos += szStride; + + // vertex 2 + afNormalStream[szVertexStream1Pos] = fNx; + afNormalStream[szVertexStream1Pos + 1] = fNy; + afNormalStream[szVertexStream1Pos + 2] = fNz; + szVertexStream1Pos += szStride; + } + } + } + else + { + szCurrentCursor = 84; + float afFacetBuffer[12] = {0}; + for(uint32_t i = 0; i < szFacetCount; i++) + { + memcpy(afFacetBuffer, &pcData[szCurrentCursor], 48); + + const float fNx = afFacetBuffer[0]; + const float fNy = afFacetBuffer[1]; + const float fNz = afFacetBuffer[2]; + + // vertex 0 + afNormalStream[szVertexStream1Pos] = fNx; + afNormalStream[szVertexStream1Pos + 1] = fNy; + afNormalStream[szVertexStream1Pos + 2] = fNz; + szVertexStream1Pos += szStride; + + // vertex 1 + afNormalStream[szVertexStream1Pos] = fNx; + afNormalStream[szVertexStream1Pos + 1] = fNy; + afNormalStream[szVertexStream1Pos + 2] = fNz; + szVertexStream1Pos += szStride; + + // vertex 2 + afNormalStream[szVertexStream1Pos] = fNx; + afNormalStream[szVertexStream1Pos + 1] = fNy; + afNormalStream[szVertexStream1Pos + 2] = fNz; + szVertexStream1Pos += szStride; + + szCurrentCursor += 50; + } + } + } +} + +//----------------------------------------------------------------------------- +// [SECTION] public api implementation +//----------------------------------------------------------------------------- + +static bool +pl__move_to_next_line(const char* pcData, size_t szDataSize, size_t* pszCurrentPos) +{ + bool bLineFound = false; + while(*pszCurrentPos < szDataSize) + { + const char cCurrentCharacter = pcData[*pszCurrentPos]; + + if(cCurrentCharacter == '\n') + { + bLineFound = true; + (*pszCurrentPos)++; + break; + } + (*pszCurrentPos)++; + } + + return bLineFound; +} + +static char +pl__move_to_first_char(const char* pcData, size_t szDataSize, size_t* pszCurrentPos) +{ + while(*pszCurrentPos < szDataSize) + { + const char cCurrentCharacter = pcData[*pszCurrentPos]; + + switch(cCurrentCharacter) + { + // end of line or string + case 0: + case '\n': + (*pszCurrentPos)++; + return 0; + + // whitespace + case ' ': + case '\r': + case '\t': break; + + // actual characters + default: return cCurrentCharacter; + } + (*pszCurrentPos)++; + } + return 0; +} + +#endif // PL_STL_IMPLEMENTATION \ No newline at end of file diff --git a/pl_string.h b/pl_string.h new file mode 100644 index 0000000..a4cb663 --- /dev/null +++ b/pl_string.h @@ -0,0 +1,420 @@ +/* + pl_string + Do this: + #define PL_STRING_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define PL_STRING_IMPLEMENTATION + #include "pl_string.h" +*/ + +// library version +#define PL_STRING_VERSION "0.2.0" +#define PL_STRING_VERSION_NUM 00200 + +/* +Index of this file: +// [SECTION] header mess +// [SECTION] includes +// [SECTION] public api +// [SECTION] c file +*/ + +//----------------------------------------------------------------------------- +// [SECTION] header mess +//----------------------------------------------------------------------------- + +#ifndef PL_STRING_H +#define PL_STRING_H + +//----------------------------------------------------------------------------- +// [SECTION] includes +//----------------------------------------------------------------------------- + +#include // uint32_t +#include // bool +#include // size_t + +//----------------------------------------------------------------------------- +// [SECTION] public api +//----------------------------------------------------------------------------- + +// hashing +uint32_t pl_str_hash_data(const void* pData, size_t szDataSize, uint32_t uSeed); +uint32_t pl_str_hash (const char* pcData, size_t szDataSize, uint32_t uSeed); + +// file/path string ops +const char* pl_str_get_file_extension(const char* pcFilePath, char* pcExtensionOut); +const char* pl_str_get_file_name (const char* pcFilePath, char* pcFileOut); +const char* pl_str_get_file_name_only(const char* pcFilePath, char* pcFileOut); +void pl_str_get_directory (const char* pcFilePath, char* pcDirectoryOut); + +// misc. opts +bool pl_str_concatenate (const char* pcStr0, const char* pcStr1, char* pcStringOut, size_t szDataSize); +bool pl_str_equal (const char* pcStr0, const char* pcStr1); +bool pl_str_contains (const char* pcStr, const char* pcSub); +int pl_text_char_from_utf8(uint32_t* puOutChars, const char* pcInText, const char* pcTextEnd); + +#endif // PL_STRING_H + +//----------------------------------------------------------------------------- +// [SECTION] c file +//----------------------------------------------------------------------------- + +/* +Index of this file: +// [SECTION] header mess +// [SECTION] includes +// [SECTION] CRC lookup table +// [SECTION] public api implementation +*/ + +//----------------------------------------------------------------------------- +// [SECTION] header mess +//----------------------------------------------------------------------------- + +#ifdef PL_STRING_IMPLEMENTATION + +#ifndef PL_ASSERT +#include +#define PL_ASSERT(x) assert((x)) +#endif + +//----------------------------------------------------------------------------- +// [SECTION] includes +//----------------------------------------------------------------------------- + +#include // memcpy, strlen +#include // bool + +//----------------------------------------------------------------------------- +// [SECTION] CRC lookup table +//----------------------------------------------------------------------------- + +// (borrowed from Dear ImGui) +// CRC32 needs a 1KB lookup table (not cache friendly) +static const uint32_t gauCrc32LookupTable[256] = +{ + 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91, + 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5, + 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59, + 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D, + 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01, + 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65, + 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, + 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD, + 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1, + 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5, + 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79, + 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D, + 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21, + 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, + 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9, + 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D, +}; + +//----------------------------------------------------------------------------- +// [SECTION] public api implementation +//----------------------------------------------------------------------------- + +uint32_t +pl_str_hash_data(const void* pData, size_t szDataSize, uint32_t uSeed) +{ + uint32_t uCrc = ~uSeed; + const unsigned char* pucData = (const unsigned char*)pData; + const uint32_t* puCrc32Lut = gauCrc32LookupTable; + while (szDataSize-- != 0) + uCrc = (uCrc >> 8) ^ puCrc32Lut[(uCrc & 0xFF) ^ *pucData++]; + return ~uCrc; +} + +uint32_t +pl_str_hash(const char* pcData, size_t szDataSize, uint32_t uSeed) +{ + uSeed = ~uSeed; + uint32_t uCrc = uSeed; + const unsigned char* pucData = (const unsigned char*)pcData; + const uint32_t* puCrc32Lut = gauCrc32LookupTable; + if (szDataSize != 0) + { + while (szDataSize-- != 0) + { + unsigned char c = *pucData++; + if (c == '#' && szDataSize >= 2 && pucData[0] == '#' && pucData[1] == '#') + uCrc = uSeed; + uCrc = (uCrc >> 8) ^ puCrc32Lut[(uCrc & 0xFF) ^ c]; + } + } + else + { + unsigned char c = *pucData++; + while (c) + { + if (c == '#' && pucData[0] == '#' && pucData[1] == '#') + uCrc = uSeed; + uCrc = (uCrc >> 8) ^ puCrc32Lut[(uCrc & 0xFF) ^ c]; + c = *pucData; + pucData++; + + } + } + return ~uCrc; +} + +const char* +pl_str_get_file_extension(const char* pcFilePath, char* pcExtensionOut) +{ + const char* pcResult = NULL; + const size_t szLen = strlen(pcFilePath); + + // check if string includes directory + bool bHasExtension = false; + for(size_t i = 0; i < szLen; i++) + { + char c = pcFilePath[szLen - i - 1]; + if(c == '/' || c == '\\') + { + break; + } + + if(c == '.') + { + bHasExtension = true; + break; + } + } + + if(bHasExtension) + { + for(size_t i = 0; i < szLen; i++) + { + char c = pcFilePath[szLen - i - 1]; + if(c == '.') + { + if(pcExtensionOut) strcpy(pcExtensionOut, &pcFilePath[szLen - i]); + pcResult = &pcFilePath[szLen - i]; + break; + } + } + } + else + { + if(pcExtensionOut) memset(pcExtensionOut, 0, 1); + } + + return pcResult; +} + +const char* +pl_str_get_file_name(const char* pcFilePath, char* pcFileOut) +{ + const char* pcResult = pcFilePath; + const size_t szLen = strlen(pcFilePath); + + // check if string includes directory + uint32_t uSlashCount = 0; + for(size_t i = 0; i < szLen; i++) + { + char c = pcFilePath[szLen - i - 1]; + if(c == '/' || c == '\\') + uSlashCount++; + } + + if(uSlashCount > 0) + { + for(size_t i = 0; i < szLen; i++) + { + char c = pcFilePath[i]; + if(c == '/' || c == '\\') + uSlashCount--; + + if(uSlashCount == 0) + { + if(pcFileOut) strcpy(pcFileOut, &pcFilePath[i + 1]); + pcResult = &pcFilePath[i + 1]; + break; + } + } + } + else + { + if(pcFileOut) memcpy(pcFileOut, pcFilePath, szLen + 1); + } + + return pcResult; +} + +const char* +pl_str_get_file_name_only(const char* pcFilePath, char* pcFileOut) +{ + const char* pcResult = pcFilePath; + const size_t szLen = strlen(pcFilePath); + + // check if string includes directory + uint32_t uSlashCount = 0; + for(size_t i = 0; i < szLen; i++) + { + char c = pcFilePath[szLen - i - 1]; + if(c == '/' || c == '\\') + uSlashCount++; + } + + if(uSlashCount > 0) + { + for(size_t i = 0; i < szLen; i++) + { + char c = pcFilePath[i]; + if(c == '/' || c == '\\') + uSlashCount--; + + if(uSlashCount == 0) + { + if(pcFileOut) strcpy(pcFileOut, &pcFilePath[i + 1]); + pcResult = &pcFilePath[i + 1]; + break; + } + } + } + else + { + if(pcFileOut) memcpy(pcFileOut, pcFilePath, szLen + 1); + } + + const size_t szOutLen = strlen(pcFileOut); + + bool bPeriodReached = false; + for(size_t i = 0; i < szLen; i++) + { + char c = pcFileOut[i]; + if(c == '.') + { + bPeriodReached = true; + } + + if(bPeriodReached) + { + pcFileOut[i] = 0; + } + } + + return pcResult; +} + +void +pl_str_get_directory(const char* pcFilePath, char* pcDirectoryOut) +{ + size_t szLen = strlen(pcFilePath); + strcpy(pcDirectoryOut, pcFilePath); + + while(szLen > 0) + { + szLen--; + + if(pcDirectoryOut[szLen] == '/' || pcDirectoryOut[szLen] == '\\') + break; + pcDirectoryOut[szLen] = 0; + } + + if(szLen == 0) + { + pcDirectoryOut[0] = '.'; + pcDirectoryOut[1] = '/'; + } +} + +bool +pl_str_concatenate(const char* pcStr0, const char* pcStr1, char* pcStringOut, size_t szDataSize) +{ + const size_t szLen0 = strlen(pcStr0); + const size_t szLen1 = strlen(pcStr1); + + if(szLen0 + szLen1 > szDataSize) + { + PL_ASSERT(false && "buffer provided not big enough"); + return false; + } + + if(pcStringOut) + { + memcpy(pcStringOut, pcStr0, szLen0); + memcpy(&pcStringOut[szLen0], pcStr1, szLen1); + } + + return true; +} + +bool +pl_str_equal(const char* pcStr0, const char* pcStr1) +{ + return strcmp(pcStr0, pcStr1) == 0; +} + +bool +pl_str_contains(const char* pcStr, const char* pcSub) +{ + const char* pcSubString = strstr(pcStr, pcSub); + return pcSubString != NULL; +} + +// Convert UTF-8 to 32-bit character, process single character input. +// A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8). +// We handle UTF-8 decoding error by skipping forward. + +#define pl_string_min(Value1, Value2) ((Value1) > (Value2) ? (Value2) : (Value1)) +int +pl_text_char_from_utf8(uint32_t* puOutChars, const char* pcInText, const char* pcTextEnd) +{ + static const char lengths[32] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 }; + static const int masks[] = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 }; + static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 }; + static const int shiftc[] = { 0, 18, 12, 6, 0 }; + static const int shifte[] = { 0, 6, 4, 2, 0 }; + int len = lengths[*(const unsigned char*)pcInText >> 3]; + int wanted = len + (len ? 0 : 1); + + if (pcTextEnd == NULL) + pcTextEnd = pcInText + wanted; // Max length, nulls will be taken into account. + + // Copy at most 'len' bytes, stop copying at 0 or past pcTextEnd. Branch predictor does a good job here, + // so it is fast even with excessive branching. + unsigned char s[4]; + s[0] = pcInText + 0 < pcTextEnd ? pcInText[0] : 0; + s[1] = pcInText + 1 < pcTextEnd ? pcInText[1] : 0; + s[2] = pcInText + 2 < pcTextEnd ? pcInText[2] : 0; + s[3] = pcInText + 3 < pcTextEnd ? pcInText[3] : 0; + + // Assume a four-byte character and load four bytes. Unused bits are shifted out. + *puOutChars = (uint32_t)(s[0] & masks[len]) << 18; + *puOutChars |= (uint32_t)(s[1] & 0x3f) << 12; + *puOutChars |= (uint32_t)(s[2] & 0x3f) << 6; + *puOutChars |= (uint32_t)(s[3] & 0x3f) << 0; + *puOutChars >>= shiftc[len]; + + // Accumulate the various error conditions. + int e = 0; + e = (*puOutChars < mins[len]) << 6; // non-canonical encoding + e |= ((*puOutChars >> 11) == 0x1b) << 7; // surrogate half? + e |= (*puOutChars > 0xFFFF) << 8; // out of range? + e |= (s[1] & 0xc0) >> 2; + e |= (s[2] & 0xc0) >> 4; + e |= (s[3] ) >> 6; + e ^= 0x2a; // top two bits of each tail byte correct? + e >>= shifte[len]; + + if (e) + { + // No bytes are consumed when *pcInText == 0 || pcInText == pcTextEnd. + // One byte is consumed in case of invalid first byte of pcInText. + // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes. + // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s. + wanted = pl_string_min(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]); + *puOutChars = 0xFFFD; + } + + return wanted; +} + +#endif // PL_STRING_IMPLEMENTATION \ No newline at end of file diff --git a/pl_test.h b/pl_test.h new file mode 100644 index 0000000..30d70a3 --- /dev/null +++ b/pl_test.h @@ -0,0 +1,429 @@ +/* + pl_test + Do this: + #define PL_TEST_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define PL_TEST_IMPLEMENTATION + #include "pl_test.h" +*/ + +// library version +#define PL_TEST_VERSION "0.1.0" +#define PL_TEST_VERSION_NUM 00100 + +/* +Index of this file: +// [SECTION] header mess +// [SECTION] includes +// [SECTION] public api +// [SECTION] c file +*/ + +//----------------------------------------------------------------------------- +// [SECTION] header mess +//----------------------------------------------------------------------------- + +#ifndef PL_TEST_H +#define PL_TEST_H + +//----------------------------------------------------------------------------- +// [SECTION] includes +//----------------------------------------------------------------------------- + +#include +#include + +//----------------------------------------------------------------------------- +// [SECTION] forward declarations & basic types +//----------------------------------------------------------------------------- + +// forward declarations +typedef struct _plTestContext plTestContext; + +typedef void (*PL_TEST_FUNCTION)(void*); + +//----------------------------------------------------------------------------- +// [SECTION] macros +//----------------------------------------------------------------------------- + +#define pl_test_register_test(TEST, DATA) pl__test_register_test((TEST), (DATA), #TEST) + + +//----------------------------------------------------------------------------- +// [SECTION] public api +//----------------------------------------------------------------------------- + +plTestContext* pl_create_test_context(void); + +// tests +void pl__test_register_test(PL_TEST_FUNCTION tTest, void* pData, const char* pcName); +bool pl_test_run(void); + +// booleans +bool pl_test_expect_true (bool bValue, const char* pcMsg); +bool pl_test_expect_false(bool bValue, const char* pcMsg); + +// numbers +bool pl_test_expect_int_equal (int iValue0, int iValue1, const char* pcMsg); +bool pl_test_expect_int_not_equal (int iValue0, int iValue1, const char* pcMsg); +bool pl_test_expect_unsigned_equal (uint32_t uValue0, uint32_t uValue1, const char* pcMsg); +bool pl_test_expect_unsigned_not_equal (uint32_t uValue0, uint32_t uValue1, const char* pcMsg); +bool pl_test_expect_float_near_equal (float fValue0, float fValue1, float fError, const char* pcMsg); +bool pl_test_expect_float_near_not_equal (float fValue0, float fValue1, float fError, const char* pcMsg); +bool pl_test_expect_double_near_equal (double dValue0, double dValue1, double dError, const char* pcMsg); +bool pl_test_expect_double_near_not_equal(double dValue0, double dValue1, double dError, const char* pcMsg); + +// strings +bool pl_test_expect_string_equal (const char* pcValue0, const char* pcValue1, const char* pcMsg); +bool pl_test_expect_string_not_equal(const char* pcValue0, const char* pcValue1, const char* pcMsg); + +#endif // PL_TEST_H + +//----------------------------------------------------------------------------- +// [SECTION] c file +//----------------------------------------------------------------------------- + +/* +Index of this file: +// [SECTION] header mess +// [SECTION] includes +// [SECTION] public api implementation +*/ + +//----------------------------------------------------------------------------- +// [SECTION] header mess +//----------------------------------------------------------------------------- + +#ifdef PL_TEST_IMPLEMENTATION + +//----------------------------------------------------------------------------- +// [SECTION] includes +//----------------------------------------------------------------------------- + +#include +#include +#include + +//----------------------------------------------------------------------------- +// [SECTION] internal api +//----------------------------------------------------------------------------- + +static void pl__test_print_va (const char* cPFormat, va_list args); +void static pl__test_print_red (const char* cPFormat, const char* pcMsg, ...); +void static pl__test_print_green(const char* cPFormat, const char* pcMsg, ...); + +//----------------------------------------------------------------------------- +// [SECTION] internal structs +//----------------------------------------------------------------------------- + +typedef struct _plTest +{ + bool bFailureOccured; + const char* pcName; + PL_TEST_FUNCTION tTest; + void* pData; +} plTest; + +typedef struct _plTestContext +{ + plTest* atTests; + plTest* ptCurrentTest; + uint32_t uTestSize; + uint32_t uTestCapacity; + uint32_t uFailedTest; + bool bPrintPasses; +} plTestContext; + +//----------------------------------------------------------------------------- +// [SECTION] global context +//----------------------------------------------------------------------------- + +plTestContext* gptTestContext = NULL; + +//----------------------------------------------------------------------------- +// [SECTION] public api implementation +//----------------------------------------------------------------------------- + +plTestContext* +pl_create_test_context(void) +{ + gptTestContext = (plTestContext*)malloc(sizeof(plTestContext)); + memset(gptTestContext, 0, sizeof(plTestContext)); + gptTestContext->uTestCapacity = 64; + gptTestContext->bPrintPasses = false; + gptTestContext->atTests = (plTest*)malloc(gptTestContext->uTestCapacity * sizeof(plTest)); + memset(gptTestContext->atTests, 0, sizeof(gptTestContext->uTestCapacity * sizeof(plTest))); + return gptTestContext; +} + +void +pl__test_register_test(PL_TEST_FUNCTION tTest, void* pData, const char* pcName) +{ + gptTestContext->uTestSize++; + if(gptTestContext->uTestSize == gptTestContext->uTestCapacity) + { + plTest* atOldTests = gptTestContext->atTests; + gptTestContext->atTests = (plTest*)malloc(gptTestContext->uTestCapacity * sizeof(plTest) * 2); + memset(gptTestContext->atTests, 0, sizeof(gptTestContext->uTestCapacity * sizeof(plTest) * 2)); + memcpy(gptTestContext->atTests, atOldTests, gptTestContext->uTestCapacity * sizeof(plTest)); + free(atOldTests); + gptTestContext->uTestCapacity *= 2; + } + gptTestContext->atTests[gptTestContext->uTestSize - 1].pcName = pcName; + gptTestContext->atTests[gptTestContext->uTestSize - 1].tTest = tTest; + gptTestContext->atTests[gptTestContext->uTestSize - 1].bFailureOccured = false; + gptTestContext->atTests[gptTestContext->uTestSize - 1].pData = pData; +} + +bool +pl_test_run(void) +{ + for(uint32_t i = 0; i < gptTestContext->uTestSize; i++) + { + + gptTestContext->ptCurrentTest = &gptTestContext->atTests[i]; + gptTestContext->ptCurrentTest->bFailureOccured = false; + + printf("-----------------------------------\n"); + printf("\"%s\" running...\n\n", gptTestContext->ptCurrentTest->pcName); + gptTestContext->ptCurrentTest->tTest(gptTestContext->ptCurrentTest->pData); + + if(gptTestContext->ptCurrentTest->bFailureOccured) + { + pl__test_print_red("\n\n\"%s\" failed", NULL, gptTestContext->ptCurrentTest->pcName); + gptTestContext->uFailedTest++; + } + else + printf("\n\n\"%s\" passed\n\n", gptTestContext->ptCurrentTest->pcName); + printf("-----------------------------------\n"); + } + + return gptTestContext->uFailedTest == 0; +} + +bool +pl_test_expect_true(bool bValue, const char* pcMsg) +{ + if(bValue) + { + pl__test_print_green("Value: true | Expected Value: true", pcMsg); + return true; + } + + pl__test_print_red("Value: false | Expected Value: true", pcMsg); + gptTestContext->ptCurrentTest->bFailureOccured = true; + return false; +} + +bool +pl_test_expect_false(bool bValue, const char* pcMsg) +{ + if(bValue) + { + pl__test_print_red("Value: true | Expected Value: false", pcMsg); + gptTestContext->ptCurrentTest->bFailureOccured = true; + return false; + } + + pl__test_print_green("Value: false | Expected Value: false", pcMsg); + return true; +} + +bool +pl_test_expect_int_equal(int iValue0, int iValue1, const char* pcMsg) +{ + if(iValue0 == iValue1) + { + pl__test_print_green("%i equals %i | Equality Expected", pcMsg, iValue0, iValue1); + return true; + } + + pl__test_print_red("%i does not equal %i | Equality Expected", pcMsg, iValue0, iValue1); + gptTestContext->ptCurrentTest->bFailureOccured = true; + return false; +} + +bool +pl_test_expect_int_not_equal(int iValue0, int iValue1, const char* pcMsg) +{ + if(iValue0 == iValue1) + { + pl__test_print_red("%i equals %i | Equality Not Expected", pcMsg, iValue0, iValue1); + gptTestContext->ptCurrentTest->bFailureOccured = true; + return false; + } + + pl__test_print_green("%i does not equal %i | Equality Not Expected", pcMsg, iValue0, iValue1); + return true; +} + +bool +pl_test_expect_unsigned_equal(uint32_t uValue0, uint32_t uValue1, const char* pcMsg) +{ + if(uValue0 == uValue1) + { + pl__test_print_green("%u equals %u | Equality Expected", pcMsg, uValue0, uValue1); + return true; + } + + pl__test_print_red("%u does not equal %u | Equality Expected", pcMsg, uValue0, uValue1); + gptTestContext->ptCurrentTest->bFailureOccured = true; + return false; +} + +bool +pl_test_expect_unsigned_not_equal(uint32_t uValue0, uint32_t uValue1, const char* pcMsg) +{ + if(uValue0 == uValue1) + { + pl__test_print_red("%u equals %u | Equality Not Expected", pcMsg, uValue0, uValue1); + gptTestContext->ptCurrentTest->bFailureOccured = true; + return false; + } + + pl__test_print_green("%u does not equal %u | Equality Not Expected", pcMsg, uValue0, uValue1); + return true; +} + +bool +pl_test_expect_float_near_equal(float fValue0, float fValue1, float fError, const char* pcMsg) +{ + return pl_test_expect_double_near_equal((double)fValue0, (double)fValue1, (double)fError, pcMsg); +} + +bool +pl_test_expect_float_near_not_equal(float fValue0, float fValue1, float fError, const char* pcMsg) +{ + return pl_test_expect_double_near_not_equal((double)fValue0, (double)fValue1, (double)fError, pcMsg); +} + +bool +pl_test_expect_double_near_equal(double dValue0, double dValue1, double dError, const char* pcMsg) +{ + if(dValue0 >= dValue1 - dError && dValue0 <= dValue1 + dError) + { + pl__test_print_green("%0.6f equals %0.6f | Equality Expected within %0.6f", pcMsg, dValue0, dValue1, dError); + return true; + } + + pl__test_print_red("%0.6f does not equal %0.6f | Equality Expected within %0.6f", pcMsg, dValue0, dValue1, dError); + gptTestContext->ptCurrentTest->bFailureOccured = true; + return false; +} + +bool +pl_test_expect_double_near_not_equal(double dValue0, double dValue1, double dError, const char* pcMsg) +{ + if(dValue0 >= dValue1 - dError && dValue0 <= dValue1 + dError) + { + pl__test_print_red("%0.f equals %0.6f | Equality Not Expected within %0.6f", pcMsg, dValue0, dValue1, dError); + gptTestContext->ptCurrentTest->bFailureOccured = true; + return false; + } + + pl__test_print_green("%0.6f does not equal %0.6f | Equality Not Expected within %0.6f", pcMsg, dValue0, dValue1, dError); + return true; +} + +bool +pl_test_expect_string_equal(const char* pcValue0, const char* pcValue1, const char* pcMsg) +{ + if(strcmp(pcValue0, pcValue1) == 0) + { + pl__test_print_green("\"%s\" equals \"%s\" | Equality Expected", pcMsg, pcValue0, pcValue1); + return true; + } + + pl__test_print_red("\"%s\" does not equal \"%s\" | Equality Expected", pcMsg, pcValue0, pcValue1); + gptTestContext->ptCurrentTest->bFailureOccured = true; + return false; +} + +bool +pl_test_expect_string_not_equal(const char* pcValue0, const char* pcValue1, const char* pcMsg) +{ + if(strcmp(pcValue0, pcValue1) == 0) + { + pl__test_print_green("\"%s\" equals \"%s\" | Equality Not Expected", pcMsg, pcValue0, pcValue1); + gptTestContext->ptCurrentTest->bFailureOccured = true; + return false; + } + + pl__test_print_red("\"%s\" does not equal \"%s\" | Equality Not Expected", pcMsg, pcValue0, pcValue1); + return true; +} + +//----------------------------------------------------------------------------- +// [SECTION] internal api implementation +//----------------------------------------------------------------------------- + +static void +pl__test_print_va(const char* cPFormat, va_list args) +{ + + char dest[1024]; + vsnprintf(dest, 1024, cPFormat, args); + printf("%s", dest); + +} + +void static +pl__test_print_red(const char* cPFormat, const char* pcMsg, ...) +{ + #ifdef _WIN32 + printf(""); + #else + printf("\033[91m"); + #endif + + va_list argptr; + va_start(argptr, pcMsg); + pl__test_print_va(cPFormat, argptr); + va_end(argptr); + + if(pcMsg) + printf(" %s\n", pcMsg); + else + printf("\n"); + + #ifdef _WIN32 + printf(""); + #else + printf("\033[0m"); + #endif +} + +static void +pl__test_print_green(const char* cPFormat, const char* pcMsg, ...) +{ + if(!gptTestContext->bPrintPasses) + return; + + #ifdef _WIN32 + printf(""); + #else + printf("\033[92m"); + #endif + + va_list argptr; + va_start(argptr, pcMsg); + pl__test_print_va(cPFormat, argptr); + va_end(argptr); + + if(pcMsg) + printf(" %s\n", pcMsg); + else + printf("\n"); + + #ifdef _WIN32 + printf(""); + #else + printf("\033[0m"); + #endif +} + +#endif // PL_TEST_IMPLEMENTATION \ No newline at end of file diff --git a/tests/build_linux_tests.sh b/tests/build_linux_tests.sh new file mode 100644 index 0000000..cc37266 --- /dev/null +++ b/tests/build_linux_tests.sh @@ -0,0 +1,74 @@ +# colors +BOLD=$'\e[0;1m' +RED=$'\e[0;31m' +RED_BG=$'\e[0;41m' +GREEN=$'\e[0;32m' +GREEN_BG=$'\e[0;42m' +CYAN=$'\e[0;36m' +MAGENTA=$'\e[0;35m' +YELLOW=$'\e[0;33m' +WHITE=$'\e[0;97m' +NC=$'\e[0m' + +# find directory of this script +SOURCE=${BASH_SOURCE[0]} +while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink + DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd ) + SOURCE=$(readlink "$SOURCE") + [[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located +done +DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd ) + +# make script directory CWD +pushd $DIR >/dev/null + +rm -f ./../out/pilot_light_test + +# create output directory +if ! [[ -d "../out" ]]; then + mkdir "../out" +fi + +# preprocessor defines +PL_DEFINES="-D_USE_MATH_DEFINES " + +# includes directories +PL_INCLUDE_DIRECTORIES="-I.. " + +# link directories +PL_LINK_DIRECTORIES="-L../out -L/usr/lib/x86_64-linux-gnu " + +# compiler flags +PL_COMPILER_FLAGS="-std=gnu99 --debug -g " + +# linker flags +PL_LINKER_FLAGS="-ldl -lm " + +# libraries +PL_LINK_LIBRARIES="-lxcb -lX11 -lX11-xcb -lxkbcommon -lxcb-cursor -lxcb-xfixes -lxcb-keysyms " + +# default compilation result +PL_RESULT=${BOLD}${GREEN}Successful.${NC} + +PL_SOURCES= +PL_SOURCES+=" main_tests.c" + +# run compiler (and linker) +echo +echo ${YELLOW}Step: pilot_light_test${NC} +echo ${YELLOW}~~~~~~~~~~~~~~~~~~~${NC} +echo ${CYAN}Compiling and Linking...${NC} +gcc -fPIC $PL_SOURCES $PL_INCLUDE_DIRECTORIES $PL_DEFINES $PL_COMPILER_FLAGS $PL_INCLUDE_DIRECTORIES $PL_LINK_DIRECTORIES $PL_LINKER_FLAGS $PL_LINK_LIBRARIES -o "./../out/pilot_light_test" + +# check build status +if [ $? -ne 0 ] +then + PL_RESULT=${BOLD}${RED}Failed.${NC} +fi + +# print results +echo ${CYAN}Results: ${NC} ${PL_RESULT} +echo ${CYAN}~~~~~~~~~~~~~~~~~~~~~~${NC} + +# return CWD to previous CWD +popd >/dev/null \ No newline at end of file diff --git a/tests/build_mac_tests.sh b/tests/build_mac_tests.sh new file mode 100644 index 0000000..3b006f2 --- /dev/null +++ b/tests/build_mac_tests.sh @@ -0,0 +1,98 @@ + +# Auto Generated by: +# "pl_build.py" version: 0.8.1 + +################################################################################ +# Development Setup # +################################################################################ + +# colors +BOLD=$'\e[0;1m' +RED=$'\e[0;31m' +RED_BG=$'\e[0;41m' +GREEN=$'\e[0;32m' +GREEN_BG=$'\e[0;42m' +CYAN=$'\e[0;36m' +MAGENTA=$'\e[0;35m' +YELLOW=$'\e[0;33m' +WHITE=$'\e[0;97m' +NC=$'\e[0m' + +# find directory of this script +SOURCE=${BASH_SOURCE[0]} +while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink + DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd ) + SOURCE=$(readlink "$SOURCE") + [[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located +done +DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd ) + +# get architecture (intel or apple silicon) +ARCH="$(uname -m)" +# make script directory CWD +pushd $DIR >/dev/null + +rm -f ./../out/pilot_light_test + +################################################################################ +# debug | pilot_light_test # +################################################################################ + +# create output directory +if ! [[ -d "../out" ]]; then + mkdir "../out" +fi + +# preprocessor defines +PL_DEFINES="-D_USE_MATH_DEFINES " + +# includes directories +PL_INCLUDE_DIRECTORIES="-I.. " + +# link directories +PL_LINK_DIRECTORIES="-L../out " + +# compiler flags +PL_COMPILER_FLAGS="-std=c99 --debug -g -fmodules -ObjC " + +# add flags for specific hardware +if [[ "$ARCH" == "arm64" ]]; then + PL_COMPILER_FLAGS+="-arch arm64 " +else + PL_COMPILER_FLAGS+="-arch x86_64 " +fi + +# linker flags +PL_LINKER_FLAGS="" + +# libraries +PL_LINK_LIBRARIES="" + +# frameworks +PL_LINK_FRAMEWORKS="-framework Metal -framework MetalKit -framework Cocoa -framework IOKit -framework CoreVideo -framework QuartzCore " + +# default compilation result +PL_RESULT=${BOLD}${GREEN}Successful.${NC} + +# source files +PL_SOURCES="main_tests.c " + +# run compiler (and linker) +echo +echo ${YELLOW}Step: pilot_light_test${NC} +echo ${YELLOW}~~~~~~~~~~~~~~~~~~~${NC} +echo ${CYAN}Compiling and Linking...${NC} +clang -fPIC $PL_SOURCES $PL_INCLUDE_DIRECTORIES $PL_DEFINES $PL_COMPILER_FLAGS $PL_INCLUDE_DIRECTORIES $PL_LINK_DIRECTORIES $PL_LINKER_FLAGS $PL_LINK_LIBRARIES -o "./../out/pilot_light_test" + +# check build status +if [ $? -ne 0 ] +then + PL_RESULT=${BOLD}${RED}Failed.${NC} +fi + +# print results +echo ${CYAN}Results: ${NC} ${PL_RESULT} +echo ${CYAN}~~~~~~~~~~~~~~~~~~~~~~${NC} + +# return CWD to previous CWD +popd >/dev/null \ No newline at end of file diff --git a/tests/build_win_tests.bat b/tests/build_win_tests.bat new file mode 100644 index 0000000..74f4ddb --- /dev/null +++ b/tests/build_win_tests.bat @@ -0,0 +1,70 @@ +@rem keep environment variables modifications local +@setlocal + +@rem make script directory CWD +@pushd %~dp0 +@set dir=%~dp0 + +@rem modify PATH to find vcvarsall.bat +@set PATH=C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build;%PATH% +@set PATH=C:\Program Files\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build;%PATH% +@set PATH=C:\Program Files (x86)\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build;%PATH% +@set PATH=C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build;%PATH% +@set PATH=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise/VC\Auxiliary\Build;%PATH% + +@rem setup environment for MSVC dev tools +@call vcvarsall.bat amd64 > nul + +@rem default compilation result +@set PL_RESULT=Successful. + +@rem create main target output directoy +@if not exist "../out" @mkdir "../out" + +@echo. +@if exist "../out/pilot_light_test.exe" del "../out/pilot_light_test.exe" +@if exist "../out/pilot_light_test_*.pdb" del "../out/pilot_light_test_*.pdb" + +@rem create output directory +@if not exist "../out" @mkdir "../out" + +@rem preprocessor defines +@set PL_DEFINES=-D_USE_MATH_DEFINES -D_DEBUG + +@rem include directories +@set PL_INCLUDE_DIRECTORIES=-I".." -I"%WindowsSdkDir%Include\um" -I"%WindowsSdkDir%Include\shared" + +@rem compiler flags +@set PL_COMPILER_FLAGS=-Zc:preprocessor -nologo -std:c11 -W4 -WX -wd4201 -wd4100 -wd4996 -wd4505 -wd4189 -wd5105 -wd4115 -permissive- -Od -MDd -Zi + +@rem run compiler (and linker) +@echo. +@echo Step: pilot_light_test +@echo ~~~~~~~~~~~~~~~~~~~~~~ +@echo Compiling and Linking... + +@rem call compiler +cl %PL_INCLUDE_DIRECTORIES% %PL_DEFINES% %PL_COMPILER_FLAGS% "main_tests.c" -Fe"../out/pilot_light_test.exe" -Fo"../out/" -link -incremental:no -PDB:"../out/pilot_light_test_%random%.pdb" + +@rem check build status +@set PL_BUILD_STATUS=%ERRORLEVEL% + +@rem failed +@if %PL_BUILD_STATUS% NEQ 0 ( + @echo Compilation Failed with error code: %PL_BUILD_STATUS% + @set PL_RESULT=Failed. + goto Cleanuppilot_light_test +) + +@rem cleanup obj files +:Cleanuppilot_light_test + @echo Cleaning... + @del "../out/*.obj" > nul 2> nul + +@rem print results +@echo. +@echo Result:  %PL_RESULT% +@echo ~~~~~~~~~~~~~~~~~~~~~~ + +@rem return CWD to previous CWD +@popd \ No newline at end of file diff --git a/tests/main_tests.c b/tests/main_tests.c new file mode 100644 index 0000000..0a3f520 --- /dev/null +++ b/tests/main_tests.c @@ -0,0 +1,26 @@ +#include "pl_ds_tests.h" +#include "pl_json_tests.h" + +int main() +{ + plTestContext* ptTestContext = pl_create_test_context(); + + // data structure tests + pl_test_register_test(hashmap_test_0, NULL); + + // json tests + pl_test_register_test(json_test_0, NULL); + + if(!pl_test_run()) + { + exit(1); + } + + return 0; +} + +#define PL_TEST_IMPLEMENTATION +#include "pl_test.h" + +#define PL_JSON_IMPLEMENTATION +#include "pl_json.h" \ No newline at end of file diff --git a/tests/pl_ds_tests.h b/tests/pl_ds_tests.h new file mode 100644 index 0000000..e82e731 --- /dev/null +++ b/tests/pl_ds_tests.h @@ -0,0 +1,102 @@ +#include +#include +#include +#include "pl_test.h" + +#include +#include "pl_ds.h" + +static void +hashmap_test_0(void* pData) +{ + // hashmap 0 + { + plHashMap tHashMap = {0}; + + int* sbiValues = NULL; + pl_sb_push(sbiValues, 0); + pl_sb_push(sbiValues, 69); + pl_hm_insert(&tHashMap, pl_hm_hash_str("Dirty Number"), pl_sb_size(sbiValues) - 1); + pl_sb_push(sbiValues, 117); + pl_hm_insert(&tHashMap, pl_hm_hash_str("Spartan Number"), pl_sb_size(sbiValues) - 1); + + for(uint32_t i = 0; i < 3000; i++) + { + pl_sb_push(sbiValues, i); + pl_hm_insert(&tHashMap, pl_hm_hash("Spartan Number2", strlen("Spartan Number2"), i), pl_sb_size(sbiValues) - 1); + } + + pl_test_expect_int_equal(sbiValues[pl_hm_lookup(&tHashMap, pl_hm_hash_str("Dirty Number"))], 69, NULL); + pl_test_expect_int_equal(sbiValues[pl_hm_lookup(&tHashMap, pl_hm_hash_str("Spartan Number"))], 117, NULL); + + pl_hm_remove(&tHashMap, pl_hm_hash_str("Dirty Number")); + + uint64_t ulFreeIndex = pl_hm_get_free_index(&tHashMap); + if(ulFreeIndex == UINT64_MAX) + { + pl_sb_add(sbiValues); + ulFreeIndex = pl_sb_size(sbiValues) - 1; + } + sbiValues[ulFreeIndex] = 666999; + pl_hm_insert(&tHashMap, pl_hm_hash_str("Extra dirty number"), ulFreeIndex); + pl_test_expect_int_equal(sbiValues[pl_hm_lookup(&tHashMap, pl_hm_hash_str("Extra dirty number"))], 666999, NULL); + + pl_test_expect_int_equal(sbiValues[pl_hm_lookup(&tHashMap, pl_hm_hash_str("Extra dirty number"))], 666999, NULL); + pl_test_expect_int_equal(sbiValues[pl_hm_lookup(&tHashMap, pl_hm_hash_str("Spartan Number"))], 117, NULL); + + pl_hm_free(&tHashMap); + pl_sb_free(sbiValues); + } + + // hashmap 1 + { + plHashMap tHashMap = {0}; + + pl_hm_insert(&tHashMap, pl_hm_hash_str("Dirty Number"), 69); + pl_hm_insert(&tHashMap, pl_hm_hash_str("Spartan Number"), 117); + + pl_test_expect_int_equal((int)pl_hm_lookup(&tHashMap, pl_hm_hash_str("Dirty Number")), 69, NULL); + pl_test_expect_int_equal((int)pl_hm_lookup(&tHashMap, pl_hm_hash_str("Spartan Number")), 117, NULL); + + pl_hm_free(&tHashMap); + } + + // hashmap 2 + { + plHashMap tHashMap = {0}; + + int* sbiValues = NULL; + pl_sb_push(sbiValues, 0); + pl_sb_push(sbiValues, 69); + pl_hm_insert_str(&tHashMap, "Dirty Number", pl_sb_size(sbiValues) - 1); + pl_sb_push(sbiValues, 117); + pl_hm_insert_str(&tHashMap, "Spartan Number", pl_sb_size(sbiValues) - 1); + + for(uint32_t i = 0; i < 79; i++) + { + pl_sb_push(sbiValues, 118); + pl_hm_insert_str(&tHashMap, "Spartan Number2", pl_sb_size(sbiValues) - 1); + } + + pl_test_expect_int_equal(sbiValues[pl_hm_lookup_str(&tHashMap, "Dirty Number")], 69, NULL); + pl_test_expect_int_equal(sbiValues[pl_hm_lookup_str(&tHashMap, "Spartan Number")], 117, NULL); + + pl_hm_remove_str(&tHashMap, "Dirty Number"); + + uint64_t ulFreeIndex = pl_hm_get_free_index(&tHashMap); + if(ulFreeIndex == UINT64_MAX) + { + pl_sb_add(sbiValues); + ulFreeIndex = pl_sb_size(sbiValues) - 1; + } + sbiValues[ulFreeIndex] = 666999; + pl_hm_insert_str(&tHashMap, "Extra dirty number", ulFreeIndex); + pl_test_expect_int_equal(sbiValues[pl_hm_lookup_str(&tHashMap, "Extra dirty number")], 666999, NULL); + + pl_test_expect_int_equal(sbiValues[pl_hm_lookup_str(&tHashMap, "Extra dirty number")], 666999, NULL); + pl_test_expect_int_equal(sbiValues[pl_hm_lookup_str(&tHashMap, "Spartan Number")], 117, NULL); + + pl_hm_free(&tHashMap); + pl_sb_free(sbiValues); + } +} \ No newline at end of file diff --git a/tests/pl_json_tests.h b/tests/pl_json_tests.h new file mode 100644 index 0000000..94f6ea6 --- /dev/null +++ b/tests/pl_json_tests.h @@ -0,0 +1,171 @@ +#include +#include +#include +#include "pl_test.h" + +#include +#include "pl_json.h" + +static void +json_test_0(void* pData) +{ + + char* pcBuffer = NULL; + + // write json + { + + // root object + plJsonObject tRootJsonObject = {0}; + pl_json_add_string_member(&tRootJsonObject, "first name", "John"); + pl_json_add_string_member(&tRootJsonObject, "last name", "Doe"); + pl_json_add_int_member(&tRootJsonObject, "age", 40); + pl_json_add_bool_member(&tRootJsonObject, "tall", false); + pl_json_add_bool_member(&tRootJsonObject, "hungry", true); + int aScores[] = {100, 86, 46}; + pl_json_add_int_array(&tRootJsonObject, "scores", aScores, 3); + + char* aPets[] = {"Riley", "Luna", "Chester"}; + pl_json_add_string_array(&tRootJsonObject, "pets", aPets, 3); + + // member object + plJsonObject tBestFriend = {0}; + pl_json_add_string_member(&tBestFriend, "first name", "John"); + pl_json_add_string_member(&tBestFriend, "last name", "Doe"); + pl_json_add_int_member(&tBestFriend, "age", 40); + pl_json_add_bool_member(&tBestFriend, "tall", false); + pl_json_add_bool_member(&tBestFriend, "hungry", true); + pl_json_add_string_array(&tBestFriend, "pets", aPets, 3); + pl_json_add_int_array(&tBestFriend, "scores", aScores, 3); + + pl_json_add_member(&tRootJsonObject, "best friend", &tBestFriend); + + // friend member object + plJsonObject atFriends[2] = {0}; + int aScores0[] = {88, 86, 100}; + pl_json_add_string_member(&atFriends[0], "first name", "Jacob"); + pl_json_add_string_member(&atFriends[0], "last name", "Smith"); + pl_json_add_int_member(&atFriends[0], "age", 23); + pl_json_add_bool_member(&atFriends[0], "tall", true); + pl_json_add_bool_member(&atFriends[0], "hungry", false); + pl_json_add_int_array(&atFriends[0], "scores", aScores0, 3); + + int aScores1[] = {80, 80, 100}; + pl_json_add_string_member(&atFriends[1], "first name", "Chance"); + pl_json_add_string_member(&atFriends[1], "last name", "Dale"); + pl_json_add_int_member(&atFriends[1], "age", 48); + pl_json_add_bool_member(&atFriends[1], "tall", true); + pl_json_add_bool_member(&atFriends[1], "hungry", true); + pl_json_add_int_array(&atFriends[1], "scores", aScores1, 3); + + pl_json_add_member_array(&tRootJsonObject, "friends", atFriends, 2); + + uint32_t uBufferSize = 0; + pl_write_json(&tRootJsonObject, NULL, &uBufferSize); + + pcBuffer = malloc(uBufferSize + 1); + memset(pcBuffer, 0, uBufferSize + 1); + pl_write_json(&tRootJsonObject, pcBuffer, &uBufferSize); + + pl_unload_json(&tRootJsonObject); + } + + // read json + { + + plJsonObject tRootJsonObject = {0}; + pl_load_json(pcBuffer, &tRootJsonObject); + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~reading~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + // check root members + { + int aiScores[3] = {0}; + pl_json_int_array_member(&tRootJsonObject, "scores", aiScores, NULL); + pl_test_expect_int_equal(aiScores[0], 100, NULL); + pl_test_expect_int_equal(aiScores[1], 86, NULL); + pl_test_expect_int_equal(aiScores[2], 46, NULL); + } + { + char acPet0[64] = {0}; + char acPet1[64] = {0}; + char acPet2[64] = {0}; + char* aacPets[3] = {acPet0, acPet1, acPet2}; + uint32_t auLengths[3] = {64, 64, 64}; + pl_json_string_array_member(&tRootJsonObject, "pets", aacPets, NULL, auLengths); + pl_test_expect_string_equal(acPet0, "Riley", NULL); + pl_test_expect_string_equal(acPet1, "Luna", NULL); + pl_test_expect_string_equal(acPet2, "Chester", NULL); + } + + char acFirstName[64] = {0}; + char acLastName[64] = {0}; + pl_test_expect_string_equal(pl_json_string_member(&tRootJsonObject, "first name", acFirstName, 64), "John", NULL); + pl_test_expect_string_equal(pl_json_string_member(&tRootJsonObject, "last name", acLastName, 64), "Doe", NULL); + pl_test_expect_int_equal(pl_json_int_member(&tRootJsonObject, "age", 0), 40, NULL); + pl_test_expect_false(pl_json_bool_member(&tRootJsonObject, "tall", false), NULL); + pl_test_expect_true(pl_json_bool_member(&tRootJsonObject, "hungry", false), NULL); + + // check child members + plJsonObject* ptBestFriend = pl_json_member(&tRootJsonObject, "best friend"); + { + int aiScores[3] = {0}; + pl_json_int_array_member(ptBestFriend, "scores", aiScores, NULL); + pl_test_expect_int_equal(aiScores[0], 100, NULL); + pl_test_expect_int_equal(aiScores[1], 86, NULL); + pl_test_expect_int_equal(aiScores[2], 46, NULL); + } + { + char acPet0[64] = {0}; + char acPet1[64] = {0}; + char acPet2[64] = {0}; + char* aacPets[3] = {acPet0, acPet1, acPet2}; + uint32_t auLengths[3] = {64, 64, 64}; + pl_json_string_array_member(ptBestFriend, "pets", aacPets, NULL, auLengths); + pl_test_expect_string_equal(acPet0, "Riley", NULL); + pl_test_expect_string_equal(acPet1, "Luna", NULL); + pl_test_expect_string_equal(acPet2, "Chester", NULL); + } + + pl_test_expect_string_equal(pl_json_string_member(ptBestFriend, "first name", acFirstName, 64), "John", NULL); + pl_test_expect_string_equal(pl_json_string_member(ptBestFriend, "last name", acLastName, 64), "Doe", NULL); + pl_test_expect_int_equal(pl_json_int_member(ptBestFriend, "age", 0), 40, NULL); + pl_test_expect_false(pl_json_bool_member(ptBestFriend, "tall", false), NULL); + pl_test_expect_true(pl_json_bool_member(ptBestFriend, "hungry", false), NULL); + + uint32_t uFriendCount = 0; + plJsonObject* sbtFriends = pl_json_array_member(&tRootJsonObject, "friends", &uFriendCount); + + plJsonObject* ptFriend0 = &sbtFriends[0]; + plJsonObject* ptFriend1 = &sbtFriends[1]; + { + int aiScores[3] = {0}; + pl_json_int_array_member(ptFriend0, "scores", aiScores, NULL); + pl_test_expect_int_equal(aiScores[0], 88, NULL); + pl_test_expect_int_equal(aiScores[1], 86, NULL); + pl_test_expect_int_equal(aiScores[2], 100, NULL); + } + + { + int aiScores[3] = {0}; + pl_json_int_array_member(ptFriend1, "scores", aiScores, NULL); + pl_test_expect_int_equal(aiScores[0], 80, NULL); + pl_test_expect_int_equal(aiScores[1], 80, NULL); + pl_test_expect_int_equal(aiScores[2], 100, NULL); + } + + pl_test_expect_string_equal(pl_json_string_member(ptFriend0, "first name", acFirstName, 64), "Jacob", NULL); + pl_test_expect_string_equal(pl_json_string_member(ptFriend0, "last name", acLastName, 64), "Smith", NULL); + pl_test_expect_int_equal(pl_json_int_member(ptFriend0, "age", 0), 23, NULL); + pl_test_expect_true(pl_json_bool_member(ptFriend0, "tall", false), NULL); + pl_test_expect_false(pl_json_bool_member(ptFriend0, "hungry", false), NULL); + + pl_test_expect_string_equal(pl_json_string_member(ptFriend1, "first name", acFirstName, 64), "Chance", NULL); + pl_test_expect_string_equal(pl_json_string_member(ptFriend1, "last name", acLastName, 64), "Dale", NULL); + pl_test_expect_int_equal(pl_json_int_member(ptFriend1, "age", 0), 48, NULL); + pl_test_expect_true(pl_json_bool_member(ptFriend1, "tall", false), NULL); + pl_test_expect_true(pl_json_bool_member(ptFriend1, "hungry", false), NULL); + + pl_unload_json(&tRootJsonObject); + } +}