1
0

initial commit
Some checks failed
Tests / Ubuntu (push) Failing after 8s

This commit is contained in:
Jonathan Hoffstadt 2024-08-26 20:21:28 -05:00
commit 95b7130b7c
17 changed files with 7771 additions and 0 deletions

79
.github/workflows/tests.yml vendored Normal file
View File

@ -0,0 +1,79 @@
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-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- 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)

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
# directories
.vs/
.vscode/
.idea/
out/
# debug files
tests/*.pdb

637
pl_ds.h Normal file
View File

@ -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 <stdlib.h>
#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 <assert.h>
#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 <stdint.h> // uint32_t
#include <stdlib.h> // malloc, free
#include <string.h> // memset, memmove
#include <stdbool.h> // bool
#include <stdarg.h> // arg vars
#include <stdio.h> // 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

2006
pl_json.h Normal file

File diff suppressed because it is too large Load Diff

1262
pl_log.h Normal file

File diff suppressed because it is too large Load Diff

798
pl_math.h Normal file
View File

@ -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 <math.h>
#include <stdbool.h> // bool
#include <stdint.h> // uint*_t
#include <assert.h>
//-----------------------------------------------------------------------------
// [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

743
pl_memory.h Normal file
View File

@ -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 <stdint.h> // uint*_t
#include <stddef.h> // size_t
#include <stdbool.h> // 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 <stdlib.h>
#define PL_MEMORY_ALLOC(x) malloc(x)
#define PL_MEMORY_FREE(x) free(x)
#endif
#ifndef PL_ASSERT
#include <assert.h>
#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 <windows.h> // VirtualAlloc, VirtualFree
#include <sysinfoapi.h> // page size
#elif defined(__APPLE__)
#include <unistd.h>
#include <sys/mman.h>
#else // linux
#include <unistd.h>
#include <sys/mman.h>
#endif
#define PL__ALIGN_UP(num, align) (((num) + ((align)-1)) & ~((align)-1))
#ifndef pl_vnsprintf
#include <stdio.h>
#define pl_vnsprintf vnsprintf
#endif
#include <stdarg.h>
//-----------------------------------------------------------------------------
// [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

512
pl_profile.h Normal file
View File

@ -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 <stdint.h>
//-----------------------------------------------------------------------------
// [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 <assert.h>
#define PL_ASSERT(x) assert((x))
#endif
#ifndef PL_PROFILE_ALLOC
#include <stdlib.h>
#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 <windows.h>
#elif defined(__APPLE__)
#include <time.h> // clock_gettime_nsec_np
#else // linux
#include <time.h> // 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

335
pl_stl.h Normal file
View File

@ -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 <stdbool.h>
//-----------------------------------------------------------------------------
// [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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <float.h>
//-----------------------------------------------------------------------------
// [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

420
pl_string.h Normal file
View File

@ -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 <stdint.h> // uint32_t
#include <stdbool.h> // bool
#include <stddef.h> // 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 <assert.h>
#define PL_ASSERT(x) assert((x))
#endif
//-----------------------------------------------------------------------------
// [SECTION] includes
//-----------------------------------------------------------------------------
#include <string.h> // memcpy, strlen
#include <stdbool.h> // 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

429
pl_test.h Normal file
View File

@ -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 <stdbool.h>
#include <stdint.h>
//-----------------------------------------------------------------------------
// [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 <stdlib.h>
#include <stdarg.h>
#include <string.h>
//-----------------------------------------------------------------------------
// [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

View File

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

98
tests/build_mac_tests.sh Normal file
View File

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

70
tests/build_win_tests.bat Normal file
View File

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

26
tests/main_tests.c Normal file
View File

@ -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"

102
tests/pl_ds_tests.h Normal file
View File

@ -0,0 +1,102 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pl_test.h"
#include <stdint.h>
#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);
}
}

171
tests/pl_json_tests.h Normal file
View File

@ -0,0 +1,171 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pl_test.h"
#include <stdint.h>
#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);
}
}