initial commit
This commit is contained in:
commit
f33209447e
90
.github/workflows/tests.yml
vendored
Normal file
90
.github/workflows/tests.yml
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
name: Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
|
||||||
|
push:
|
||||||
|
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- dev
|
||||||
|
- feature/*
|
||||||
|
- dev/*
|
||||||
|
- fix/*
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
# Windows:
|
||||||
|
|
||||||
|
# runs-on: windows-2019
|
||||||
|
|
||||||
|
# steps:
|
||||||
|
|
||||||
|
# - name: Checkout
|
||||||
|
# uses: actions/checkout@v3
|
||||||
|
|
||||||
|
# - name: Run Tests
|
||||||
|
# shell: cmd
|
||||||
|
# run: |
|
||||||
|
# cd $GITHUB_WORKSPACE
|
||||||
|
# cd tests
|
||||||
|
# call build_win_tests.bat -c debug
|
||||||
|
# cd ..
|
||||||
|
# cd out
|
||||||
|
# pilot_light_test.exe
|
||||||
|
# @set PL_TEST_STATUS=%ERRORLEVEL%
|
||||||
|
# @if %PL_TEST_STATUS% NEQ 0 (exit 1)
|
||||||
|
|
||||||
|
# MacOS:
|
||||||
|
|
||||||
|
# runs-on: MacOS-latest
|
||||||
|
|
||||||
|
# steps:
|
||||||
|
|
||||||
|
# - name: Checkout
|
||||||
|
# uses: actions/checkout@v3
|
||||||
|
|
||||||
|
# - name: Run Tests
|
||||||
|
# run: |
|
||||||
|
# cd $GITHUB_WORKSPACE
|
||||||
|
# cd tests
|
||||||
|
# chmod +x build_mac_tests.sh
|
||||||
|
# ./build_mac_tests.sh -c debug
|
||||||
|
# cd ..
|
||||||
|
# cd out
|
||||||
|
# ./pilot_light_test
|
||||||
|
# ((exit 1) || if [ $? = 1 ]; then exit 0; else exit 1; fi)
|
||||||
|
|
||||||
|
Ubuntu:
|
||||||
|
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install libx11-dev -y
|
||||||
|
sudo apt install libxkbcommon-x11-dev -y
|
||||||
|
sudo apt install libx11-xcb-dev -y
|
||||||
|
sudo apt install libxcb-xfixes0-dev -y
|
||||||
|
sudo apt install libxcb-cursor-dev -y
|
||||||
|
sudo apt install libxcb-cursor0 -y
|
||||||
|
sudo apt install libxcb-keysyms1-dev -y
|
||||||
|
|
||||||
|
- name: Run Tests
|
||||||
|
run: |
|
||||||
|
cd $GITHUB_WORKSPACE
|
||||||
|
cd tests
|
||||||
|
chmod +x build_linux_tests.sh
|
||||||
|
./build_linux_tests.sh
|
||||||
|
cd ..
|
||||||
|
cd out
|
||||||
|
./pilot_light_test
|
||||||
|
((exit 1) || if [ $? = 1 ]; then exit 0; else exit 1; fi)
|
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
# directories
|
||||||
|
.vs/
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
out/
|
||||||
|
|
||||||
|
# debug files
|
||||||
|
tests/*.pdb
|
637
pl_ds.h
Normal file
637
pl_ds.h
Normal 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
|
||||||
|
|
798
pl_math.h
Normal file
798
pl_math.h
Normal 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
743
pl_memory.h
Normal 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
512
pl_profile.h
Normal 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
335
pl_stl.h
Normal 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
420
pl_string.h
Normal 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
429
pl_test.h
Normal 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("[91m");
|
||||||
|
#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("[0m");
|
||||||
|
#else
|
||||||
|
printf("\033[0m");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pl__test_print_green(const char* cPFormat, const char* pcMsg, ...)
|
||||||
|
{
|
||||||
|
if(!gptTestContext->bPrintPasses)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
printf("[92m");
|
||||||
|
#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("[0m");
|
||||||
|
#else
|
||||||
|
printf("\033[0m");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PL_TEST_IMPLEMENTATION
|
74
tests/build_linux_tests.sh
Normal file
74
tests/build_linux_tests.sh
Normal 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
98
tests/build_mac_tests.sh
Normal 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
70
tests/build_win_tests.bat
Normal 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=[1m[92mSuccessful.[0m
|
||||||
|
|
||||||
|
@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 [1m[93mStep: pilot_light_test[0m
|
||||||
|
@echo [1m[93m~~~~~~~~~~~~~~~~~~~~~~[0m
|
||||||
|
@echo [1m[36mCompiling and Linking...[0m
|
||||||
|
|
||||||
|
@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 [1m[91mCompilation Failed with error code[0m: %PL_BUILD_STATUS%
|
||||||
|
@set PL_RESULT=[1m[91mFailed.[0m
|
||||||
|
goto Cleanuppilot_light_test
|
||||||
|
)
|
||||||
|
|
||||||
|
@rem cleanup obj files
|
||||||
|
:Cleanuppilot_light_test
|
||||||
|
@echo [1m[36mCleaning...[0m
|
||||||
|
@del "../out/*.obj" > nul 2> nul
|
||||||
|
|
||||||
|
@rem print results
|
||||||
|
@echo.
|
||||||
|
@echo [36mResult: [0m %PL_RESULT%
|
||||||
|
@echo [36m~~~~~~~~~~~~~~~~~~~~~~[0m
|
||||||
|
|
||||||
|
@rem return CWD to previous CWD
|
||||||
|
@popd
|
26
tests/main_tests.c
Normal file
26
tests/main_tests.c
Normal 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
102
tests/pl_ds_tests.h
Normal 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
171
tests/pl_json_tests.h
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user