summaryrefslogtreecommitdiff
path: root/src/vendor/pk.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/vendor/pk.h')
-rw-r--r--src/vendor/pk.h1246
1 files changed, 1246 insertions, 0 deletions
diff --git a/src/vendor/pk.h b/src/vendor/pk.h
new file mode 100644
index 0000000..8d788d1
--- /dev/null
+++ b/src/vendor/pk.h
@@ -0,0 +1,1246 @@
+#ifndef PK_SINGLE_HEADER_FILE_H
+#define PK_SINGLE_HEADER_FILE_H
+/******************************************************************************
+* PK Single-Header-Library V0.0.2
+*
+* Author: Jonathan Bradley
+* Copyright: © 2024-2024 Jonathan Bradley
+* Description:
+*
+*******************************************************************************
+* pkmacros.h:
+*
+*******************************************************************************
+* pkmem-types.h:
+*
+*******************************************************************************
+* pkmem.h:
+*
+*******************************************************************************
+* pkstr.h:
+*
+*******************************************************************************
+* pkev.h:
+*
+******************************************************************************/
+
+#define PK_VERSION "0.0.2"
+
+#ifdef PK_IMPL_ALL
+# ifndef PK_IMPL_MEM_TYPES
+# define PK_IMPL_MEM_TYPES
+# endif
+# ifndef PK_IMPL_MEM
+# define PK_IMPL_MEM
+# endif
+# ifndef PK_IMPL_STR
+# define PK_IMPL_STR
+# endif
+# ifndef PK_IMPL_EV
+# define PK_IMPL_EV
+# endif
+#endif
+#ifndef PK_MACROS_H
+#define PK_MACROS_H
+
+#ifndef PK_LOG_OVERRIDE
+# ifdef NDEBUG
+# define PK_LOG_ERR(str) (void)str
+# define PK_LOG_INF(str) (void)str
+# define PK_LOGV_ERR(str, ...) (void)str
+# define PK_LOGV_INF(str, ...) (void)str
+# else
+# define PK_LOG_ERR(str, ...) fprintf(stderr, str)
+# define PK_LOG_INF(str, ...) fprintf(stdout, str)
+# define PK_LOGV_ERR(str, ...) fprintf(stderr, str, __VA_ARGS__)
+# define PK_LOGV_INF(str, ...) fprintf(stdout, str, __VA_ARGS__)
+# endif
+#endif
+
+#define PK_Q(x) #x
+#define PK_QUOTE(x) PK_Q(x)
+#define PK_CONCAT2(x, y) x##y
+#define PK_CONCAT(x, y) PK_CONCAT2(x, y)
+
+#define PK_HAS_FLAG(val, flag) ((val & flag) == flag)
+#define PK_CLAMP(val, min, max) (val < min ? min : val > max ? max : val)
+#define PK_MIN(val, min) (val < min ? val : min)
+#define PK_MAX(val, max) (val > max ? val : max)
+
+#define PK_TO_BIN_PAT PK_Q(%c%c%c%c%c%c%c%c)
+#define PK_TO_BIN_PAT_8 PK_TO_BIN_PAT
+#define PK_TO_BIN_PAT_16 PK_TO_BIN_PAT PK_TO_BIN_PAT
+#define PK_TO_BIN_PAT_32 PK_TO_BIN_PAT_16 PK_TO_BIN_PAT_16
+#define PK_TO_BIN_PAT_64 PK_TO_BIN_PAT_32 PK_TO_BIN_PAT_32
+#define PK_TO_BIN(byte) \
+ ((byte) & 0x80 ? '1' : '0'), \
+ ((byte) & 0x40 ? '1' : '0'), \
+ ((byte) & 0x20 ? '1' : '0'), \
+ ((byte) & 0x10 ? '1' : '0'), \
+ ((byte) & 0x08 ? '1' : '0'), \
+ ((byte) & 0x04 ? '1' : '0'), \
+ ((byte) & 0x02 ? '1' : '0'), \
+ ((byte) & 0x01 ? '1' : '0')
+#define PK_TO_BIN_8(u8) PK_TO_BIN(u8)
+#define PK_TO_BIN_16(u16) PK_TO_BIN((u16 >> 8)), PK_TO_BIN(u16 & 0x00FF)
+#define PK_TO_BIN_32(u32) PK_TO_BIN_16((u32 >> 16)), PK_TO_BIN_16(u32 & 0x0000FFFF)
+#define PK_TO_BIN_64(u64) PK_TO_BIN_32((u64 >> 32)), PK_TO_BIN_32(u64 & 0x00000000FFFFFFFF)
+
+#if defined(__cplusplus)
+# define CAFE_BABE(T) reinterpret_cast<T *>(0xCAFEBABE)
+#else
+# define CAFE_BABE(T) (T *)(0xCAFEBABE)
+#endif
+
+#define NULL_CHAR_ARR(v, len) char v[len]; v[0] = '\0'; v[len-1] = '\0';
+
+#define IS_CONSTRUCTIBLE(T) constexpr(std::is_default_constructible<T>::value && !std::is_integral<T>::value && !std::is_floating_point<T>::value)
+#define IS_DESTRUCTIBLE(T) constexpr(std::is_destructible<T>::value && !std::is_integral<T>::value && !std::is_floating_point<T>::value && !std::is_array<T>::value)
+
+#define TypeSafeInt2_H(TypeName, Type, Max, TypeName_T, TypeName_MAX, TypeName_T_MAX) \
+ using TypeName_T = Type; \
+ enum class TypeName : TypeName_T; \
+ constexpr TypeName_T TypeName_T_MAX = TypeName_T{Max}; \
+ constexpr TypeName TypeName_MAX = TypeName{TypeName_T_MAX}; \
+ TypeName operator+(const TypeName& a, const TypeName& b); \
+ TypeName operator-(const TypeName& a, const TypeName& b); \
+ TypeName operator&(const TypeName& a, const TypeName& b); \
+ TypeName operator|(const TypeName& a, const TypeName& b); \
+ TypeName operator^(const TypeName& a, const TypeName& b); \
+ TypeName& operator++(TypeName& a); \
+ TypeName& operator--(TypeName& a); \
+ TypeName operator++(TypeName& a, int); \
+ TypeName operator--(TypeName& a, int); \
+ TypeName operator<<(const TypeName& a, const TypeName& b); \
+ TypeName operator>>(const TypeName& a, const TypeName& b); \
+ TypeName operator+=(TypeName& a, const TypeName& b); \
+ TypeName operator-=(TypeName& a, const TypeName& b); \
+ TypeName operator&=(TypeName& a, const TypeName& b); \
+ TypeName operator|=(TypeName& a, const TypeName& b); \
+ TypeName operator^=(TypeName& a, const TypeName& b); \
+ TypeName operator~(TypeName& a);
+#define TypeSafeInt2_B(TypeName, TypeName_T) \
+ inline TypeName operator+(const TypeName& a, const TypeName& b) { \
+ return TypeName(static_cast<TypeName_T>(a) + static_cast<TypeName_T>(b)); \
+ } \
+ inline TypeName operator-(const TypeName& a, const TypeName& b) { \
+ return TypeName(static_cast<TypeName_T>(a) - static_cast<TypeName_T>(b)); \
+ } \
+ inline TypeName operator&(const TypeName& a, const TypeName& b) { \
+ return TypeName(static_cast<TypeName_T>(a) & static_cast<TypeName_T>(b)); \
+ } \
+ inline TypeName operator|(const TypeName& a, const TypeName& b) { \
+ return TypeName(static_cast<TypeName_T>(a) | static_cast<TypeName_T>(b)); \
+ } \
+ inline TypeName operator^(const TypeName& a, const TypeName& b) { \
+ return TypeName(static_cast<TypeName_T>(a) ^ static_cast<TypeName_T>(b)); \
+ } \
+ inline TypeName& operator++(TypeName& a) { \
+ a = a + TypeName{1}; \
+ return a; \
+ } \
+ inline TypeName& operator--(TypeName& a) { \
+ a = a - TypeName{1}; \
+ return a; \
+ }; \
+ inline TypeName operator++(TypeName& a, int) { \
+ a = a + TypeName{1}; \
+ return a; \
+ } \
+ inline TypeName operator--(TypeName& a, int) { \
+ a = a - TypeName{1}; \
+ return a; \
+ }; \
+ inline TypeName operator<<(const TypeName& a, const TypeName& b) { \
+ return TypeName(static_cast<TypeName_T>(a) << static_cast<TypeName_T>(b)); \
+ }; \
+ inline TypeName operator>>(const TypeName& a, const TypeName& b) { \
+ return TypeName(static_cast<TypeName_T>(a) >> static_cast<TypeName_T>(b)); \
+ }; \
+ inline TypeName operator+=(TypeName& a, const TypeName& b) { \
+ a = TypeName{a + b}; \
+ return a; \
+ }; \
+ inline TypeName operator-=(TypeName& a, const TypeName& b) { \
+ a = TypeName{a - b}; \
+ return a; \
+ }; \
+ inline TypeName operator&=(TypeName& a, const TypeName& b) { \
+ a = TypeName{a & b}; \
+ return a; \
+ }; \
+ inline TypeName operator|=(TypeName& a, const TypeName& b) { \
+ a = TypeName{a | b}; \
+ return a; \
+ }; \
+ inline TypeName operator^=(TypeName& a, const TypeName& b) { \
+ a = TypeName{a ^ b}; \
+ return a; \
+ }; \
+ inline TypeName operator~(TypeName& a) { \
+ a = static_cast<TypeName>(~static_cast<TypeName_T>(a)); \
+ return a; \
+ };
+#define TypeSafeInt_H(TypeName, Type, Max) \
+ TypeSafeInt2_H(TypeName, Type, Max, PK_CONCAT(TypeName, _T), PK_CONCAT(TypeName, _MAX), PK_CONCAT(TypeName, _T_MAX))
+#define TypeSafeInt_B(TypeName) \
+ TypeSafeInt2_B(TypeName, PK_CONCAT(TypeName, _T))
+
+#define TypeSafeInt2_H_constexpr(TypeName, Type, Max, TypeName_T, TypeName_MAX, TypeName_T_MAX) \
+ using TypeName_T = Type; \
+ enum class TypeName : TypeName_T; \
+ constexpr TypeName_T TypeName_T_MAX = TypeName_T{Max}; \
+ constexpr TypeName TypeName_MAX = TypeName{TypeName_T_MAX}; \
+ constexpr TypeName operator+(const TypeName& a, const TypeName& b) { \
+ return TypeName(static_cast<TypeName_T>(a) + static_cast<TypeName_T>(b)); \
+ } \
+ constexpr TypeName operator-(const TypeName& a, const TypeName& b) { \
+ return TypeName(static_cast<TypeName_T>(a) - static_cast<TypeName_T>(b)); \
+ } \
+ constexpr TypeName operator&(const TypeName& a, const TypeName& b) { \
+ return TypeName(static_cast<TypeName_T>(a) & static_cast<TypeName_T>(b)); \
+ } \
+ constexpr TypeName operator|(const TypeName& a, const TypeName& b) { \
+ return TypeName(static_cast<TypeName_T>(a) | static_cast<TypeName_T>(b)); \
+ } \
+ constexpr TypeName operator^(const TypeName& a, const TypeName& b) { \
+ return TypeName(static_cast<TypeName_T>(a) ^ static_cast<TypeName_T>(b)); \
+ } \
+ constexpr TypeName& operator++(TypeName& a) { \
+ a = a + TypeName{1}; \
+ return a; \
+ } \
+ constexpr TypeName& operator--(TypeName& a) { \
+ a = a - TypeName{1}; \
+ return a; \
+ }; \
+ constexpr TypeName operator++(TypeName& a, int) { \
+ a = a + TypeName{1}; \
+ return a; \
+ } \
+ constexpr TypeName operator--(TypeName& a, int) { \
+ a = a - TypeName{1}; \
+ return a; \
+ }; \
+ constexpr TypeName operator<<(const TypeName& a, const TypeName& b) { \
+ return TypeName(static_cast<TypeName_T>(a) << static_cast<TypeName_T>(b)); \
+ }; \
+ constexpr TypeName operator>>(const TypeName& a, const TypeName& b) { \
+ return TypeName(static_cast<TypeName_T>(a) >> static_cast<TypeName_T>(b)); \
+ }; \
+ constexpr TypeName operator+=(TypeName& a, const TypeName& b) { \
+ a = TypeName{a + b}; \
+ return a; \
+ }; \
+ constexpr TypeName operator-=(TypeName& a, const TypeName& b) { \
+ a = TypeName{a - b}; \
+ return a; \
+ }; \
+ constexpr TypeName operator&=(TypeName& a, const TypeName& b) { \
+ a = TypeName{a & b}; \
+ return a; \
+ }; \
+ constexpr TypeName operator|=(TypeName& a, const TypeName& b) { \
+ a = TypeName{a | b}; \
+ return a; \
+ }; \
+ constexpr TypeName operator^=(TypeName& a, const TypeName& b) { \
+ a = TypeName{a ^ b}; \
+ return a; \
+ }; \
+ constexpr TypeName operator~(const TypeName& a) { \
+ return static_cast<TypeName>(~static_cast<TypeName_T>(a)); \
+ };
+#define TypeSafeInt_constexpr(TypeName, Type, Max) \
+ TypeSafeInt2_H_constexpr(TypeName, Type, Max, PK_CONCAT(TypeName, _T), PK_CONCAT(TypeName, _MAX), PK_CONCAT(TypeName, _T_MAX))
+
+#endif /* PK_MACROS_H */
+#ifndef PK_MEM_TYPES_H
+#define PK_MEM_TYPES_H
+
+#include <stdint.h>
+
+typedef uint32_t pk_handle_bucket_index_T;
+typedef uint32_t pk_handle_item_index_T;
+
+enum PK_HANDLE_VALIDATION : uint8_t {
+ PK_HANDLE_VALIDATION_VALID = 0,
+ PK_HANDLE_VALIDATION_BUCKET_INDEX_TOO_HIGH = 1,
+ PK_HANDLE_VALIDATION_ITEM_INDEX_TOO_HIGH = 2,
+ PK_HANDLE_VALIDATION_VALUE_MAX = 3,
+};
+
+struct pk_handle {
+ pk_handle_bucket_index_T bucketIndex;
+ pk_handle_item_index_T itemIndex;
+};
+
+#define PK_HANDLE_MAX ((struct pk_handle){ .bucketIndex = 0xFFFFFFFF, .itemIndex = 0xFFFFFFFF })
+
+enum PK_HANDLE_VALIDATION pk_handle_validate(const struct pk_handle handle, const struct pk_handle bucketHandle, const uint64_t maxItems);
+
+
+#if defined(__cplusplus)
+
+constexpr struct pk_handle pk_handle_MAX_constexpr = (struct pk_handle){ .bucketIndex = 0xFFFFFFFF, .itemIndex = 0xFFFFFFFF };
+
+inline constexpr bool
+operator==(const pk_handle& lhs, const pk_handle& rhs)
+{
+ return lhs.bucketIndex == rhs.bucketIndex && lhs.itemIndex == rhs.itemIndex;
+}
+
+template<const pk_handle handle, const pk_handle bucketHandle, const uint64_t maxItems>
+inline constexpr enum PK_HANDLE_VALIDATION
+pk_handle_validate_constexpr()
+{
+ if constexpr (handle == pk_handle_MAX_constexpr)
+ return PK_HANDLE_VALIDATION_VALUE_MAX;
+ if constexpr (handle.bucketIndex > bucketHandle.bucketIndex)
+ return PK_HANDLE_VALIDATION_BUCKET_INDEX_TOO_HIGH;
+ if constexpr (handle.itemIndex > maxItems)
+ return PK_HANDLE_VALIDATION_ITEM_INDEX_TOO_HIGH;
+ if constexpr (handle.bucketIndex == bucketHandle.bucketIndex && handle.itemIndex > bucketHandle.itemIndex)
+ return PK_HANDLE_VALIDATION_ITEM_INDEX_TOO_HIGH;
+ return PK_HANDLE_VALIDATION_VALID;
+}
+#endif /* __cplusplus */
+
+struct pk_membucket;
+
+#endif /* PK_MEM_TYPES_H */
+
+#ifdef PK_IMPL_MEM_TYPES
+
+enum PK_HANDLE_VALIDATION
+pk_handle_validate(const struct pk_handle handle, const struct pk_handle bucketHandle, const uint64_t maxItems)
+{
+ if (handle.bucketIndex == PK_HANDLE_MAX.bucketIndex && handle.itemIndex == PK_HANDLE_MAX.itemIndex)
+ return PK_HANDLE_VALIDATION_VALUE_MAX;
+ if (handle.bucketIndex > bucketHandle.bucketIndex)
+ return PK_HANDLE_VALIDATION_BUCKET_INDEX_TOO_HIGH;
+ if (handle.itemIndex > maxItems)
+ return PK_HANDLE_VALIDATION_ITEM_INDEX_TOO_HIGH;
+ if (handle.bucketIndex == bucketHandle.bucketIndex && handle.itemIndex > bucketHandle.itemIndex)
+ return PK_HANDLE_VALIDATION_ITEM_INDEX_TOO_HIGH;
+ return PK_HANDLE_VALIDATION_VALID;
+}
+
+#endif /* PK_IMPL_MEM_TYPES */
+#ifndef PK_MEM_H
+#define PK_MEM_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifndef PK_DEFAULT_BUCKET_SIZE
+# define PK_DEFAULT_BUCKET_SIZE (1ULL * 1024ULL * 1024ULL * 256ULL)
+#endif
+#ifndef PK_MINIMUM_ALIGNMENT
+# define PK_MINIMUM_ALIGNMENT 1
+#endif
+#ifndef PK_MAXIMUM_ALIGNMENT
+# define PK_MAXIMUM_ALIGNMENT 64
+#endif
+
+struct pk_membucket* pk_bucket_create(const char* description, int64_t sz, bool transient);
+void pk_bucket_destroy(struct pk_membucket* bkt);
+void pk_bucket_reset(struct pk_membucket* bkt);
+
+void pk_memory_debug_print();
+void pk_memory_flush();
+void pk_memory_teardown_all();
+bool pk_memory_is_in_bucket(const void* ptr, const struct pk_membucket* bkt);
+
+void* pk_new_base(size_t sz, size_t alignment);
+void* pk_new_bkt(size_t sz, size_t alignment, struct pk_membucket* bkt);
+void pk_delete_base(const void* ptr, size_t sz);
+void pk_delete_bkt(const void* ptr, size_t sz, struct pk_membucket* bkt);
+
+#if defined(__cplusplus)
+
+#include <type_traits>
+
+static inline void stupid_header_warnings_cpp() { (void)std::is_const<void>::value; }
+
+template <typename T>
+inline T*
+pk_new(pk_membucket* bucket = nullptr)
+{
+ void* ptr = nullptr;
+ if (bucket) {
+ ptr = pk_new_bkt(sizeof(T), alignof(T), bucket);
+ } else {
+ ptr = pk_new_base(sizeof(T), alignof(T));
+ }
+ if IS_CONSTRUCTIBLE(T) {
+ return new (ptr) T{};
+ }
+ return reinterpret_cast<T*>(ptr);
+}
+
+template <typename T>
+inline T*
+pk_new(long count, pk_membucket* bucket = nullptr)
+{
+ char* ptr = nullptr;
+ if (bucket) {
+ ptr = static_cast<char*>(pk_new_bkt(sizeof(T) * count, alignof(T), bucket));
+ } else {
+ ptr = static_cast<char*>(pk_new_base(sizeof(T) * count, alignof(T)));
+ }
+ if IS_CONSTRUCTIBLE(T) {
+ for (long i = 0; i < count; ++i) {
+ new (ptr + (i * sizeof(T))) T{};
+ }
+ }
+ return reinterpret_cast<T*>(ptr);
+}
+
+template <typename T>
+inline void
+pk_delete(const T* ptr, pk_membucket* bucket = nullptr)
+{
+ if IS_DESTRUCTIBLE(T) {
+ reinterpret_cast<const T*>(ptr)->~T();
+ }
+ if (bucket) {
+ return pk_delete_bkt(static_cast<const void*>(ptr), sizeof(T), bucket);
+ } else {
+ return pk_delete_base(static_cast<const void*>(ptr), sizeof(T));
+ }
+}
+
+template <typename T>
+inline void
+pk_delete(const T* ptr, long count, pk_membucket* bucket = nullptr)
+{
+ if IS_DESTRUCTIBLE(T) {
+ for (long i = 0; i < count; ++i) {
+ reinterpret_cast<const T*>(reinterpret_cast<const char*>(ptr) + (i * sizeof(T)))->~T();
+ }
+ }
+ if (bucket) {
+ return pk_delete_bkt(static_cast<const void*>(ptr), sizeof(T) * count, bucket);
+ } else {
+ return pk_delete_base(static_cast<const void*>(ptr), sizeof(T) * count);
+ }
+}
+
+#endif /* __cplusplus */
+
+#endif /* PK_MEM */
+
+#ifdef PK_IMPL_MEM
+
+#include <string.h>
+#include <stdio.h>
+#include <threads.h>
+#include <assert.h>
+
+static inline void pkmem_stupid_header_warnings() { (void)stdout; }
+
+#if defined(PK_MEMORY_DEBUGGER)
+/*
+ * Note that certain aspects of this expect that you only have one non-transient bucket.
+ * If you need to track multiple non-transient buckets, these sections will need a refactor.
+ */
+#endif
+
+struct pk_memblock {
+ char* data;
+ size_t size;
+};
+
+struct pk_membucket {
+ // the total size of the bucket, `blocks+ptr`
+ int64_t size;
+ // the current head of the bucket: byte offset from `ptr`.
+ // All currently alloc'd data is before this offset
+ int64_t head;
+ // amount of lost bytes in this membucket, hopefully zero
+ int64_t lostBytes;
+ // the number of active allocations from this bucket
+ int64_t allocs;
+ // the index of the last empty block.
+ // Should always point to `pk_memblock{ .data = ptr+head, .size=size-head }`
+ int64_t lastEmptyBlockIndex;
+ // number of pk_memblocks in the `*blocks` array
+ int64_t maxBlockCount;
+ // ptr to an array of pk_memblock to track ALL free space between ptr and ptr+sz
+ struct pk_memblock* blocks;
+ // starting point for alloc'd data
+ union {
+ char* ptr;
+ void* raw;
+ };
+ const char* description;
+ mtx_t mtx;
+ bool transient;
+};
+
+static struct pk_membucket pk_buckets[8];
+static int64_t pk_bucket_head = 0;
+
+#ifdef PK_MEMORY_DEBUGGER
+struct pk_dbg_memblock {
+ struct pk_memblock blk;
+ struct pk_membucket *bkt;
+};
+static struct pk_dbg_memblock debug_all_allocs[1024 * 1024];
+static int64_t debug_alloc_head = 0;
+static bool has_init_debug = false;
+#endif
+
+bool
+pk_memory_is_in_bucket(const void* ptr, const struct pk_membucket* bkt)
+{
+ if (ptr >= bkt->raw && (const char*)ptr < bkt->ptr + bkt->size) return true;
+ return false;
+}
+
+void
+pk_memory_debug_print()
+{
+ PK_LOGV_INF("Memory Manager printout:\nBucket count: %li\n", pk_bucket_head);
+ for (long i = 0; i < pk_bucket_head; ++i) {
+ PK_LOGV_INF("- bucket #%li\n", i);
+ PK_LOGV_INF("\tdescription: %s\n", pk_buckets[i].description);
+ PK_LOGV_INF("\tsize: %li\n", pk_buckets[i].size);
+ PK_LOGV_INF("\thead: %li\n", pk_buckets[i].head);
+ PK_LOGV_INF("\tlostBytes: %li\n", pk_buckets[i].lostBytes);
+ PK_LOGV_INF("\tallocs: %li\n", pk_buckets[i].allocs);
+ PK_LOGV_INF("\tlastEmptyBlockIndex: %li\n", pk_buckets[i].lastEmptyBlockIndex);
+ PK_LOGV_INF("\tmaxBlockCount: %li\n", pk_buckets[i].maxBlockCount);
+ PK_LOGV_INF("\tblocks: %p\n", pk_buckets[i].blocks);
+ PK_LOGV_INF("\tptr: %p\n", pk_buckets[i].ptr);
+ PK_LOGV_INF("\ttransient: %i\n", pk_buckets[i].transient);
+#ifdef PK_MEMORY_DEBUGGER
+ uint64_t count = 0;
+ for (int64_t d = 0; d < debug_alloc_head; ++d) {
+ if (debug_all_allocs[d].bkt == &pk_buckets[d] && debug_all_allocs[d].blk.size > 0) {
+ count += 1;
+ }
+ }
+ PK_LOGV_INF("\tdebug alloc count: %lu\n", count);
+ PK_LOGV_INF("\tdebug alloc last: %lu\n", debug_alloc_head);
+#endif
+ }
+}
+
+void
+pk_memory_flush()
+{
+ for (long i = pk_bucket_head - 2; i > -1; --i) {
+ if (pk_buckets[i].head != 0) break;
+ if (pk_buckets[i+1].head != 0) break;
+ if (pk_buckets[i].transient == true) break;
+ if (pk_buckets[i+1].transient == true) break;
+ pk_bucket_head--;
+ pk_bucket_destroy(&pk_buckets[i + 1]);
+ }
+}
+
+void
+pk_memory_teardown_all()
+{
+ for (int64_t i = pk_bucket_head - 1; i > 0; --i) {
+ if (pk_buckets[i].ptr == nullptr) continue;
+ pk_bucket_destroy(&pk_buckets[i]);
+ }
+ pk_bucket_head = 0;
+}
+
+static int64_t
+pk_bucket_create_inner(int64_t sz, bool transient, const char* description)
+{
+#ifdef PK_MEMORY_DEBUGGER
+ if (has_init_debug == false) {
+ has_init_debug = true;
+ memset(debug_all_allocs, 0, sizeof(struct pk_dbg_memblock) * 1024 * 1024);
+ }
+#endif
+ int64_t blockCount = sz * 0.01;
+ struct pk_membucket* bkt = &pk_buckets[pk_bucket_head];
+ bkt->size = sz;
+ bkt->head = 0;
+ bkt->lostBytes = 0;
+ bkt->allocs = 0;
+ bkt->lastEmptyBlockIndex = 0;
+ bkt->maxBlockCount = blockCount < 10 ? 10 : blockCount;
+ bkt->blocks = (struct pk_memblock*)malloc(sz);
+ assert(bkt->blocks != nullptr && "failed to allocate memory");
+#if 1
+ memset(bkt->blocks, 0, sz);
+#endif
+ bkt->ptr = ((char*)(bkt->blocks)) + (sizeof(struct pk_memblock) * bkt->maxBlockCount);
+ size_t misalignment = (uint64_t)(bkt->ptr) % PK_MAXIMUM_ALIGNMENT;
+ if (misalignment != 0) {
+ size_t moreBlocks = misalignment / sizeof(struct pk_memblock);
+ bkt->maxBlockCount += moreBlocks;
+ bkt->ptr += (PK_MAXIMUM_ALIGNMENT - misalignment);
+ }
+ bkt->description = description;
+ bkt->transient = transient;
+ struct pk_memblock* memBlock = (struct pk_memblock*)(bkt->blocks);
+ memBlock->data = bkt->ptr;
+ memBlock->size = sz - (sizeof(struct pk_memblock) * bkt->maxBlockCount);
+ return pk_bucket_head++;
+}
+
+struct pk_membucket*
+pk_bucket_create(const char* description, int64_t sz, bool transient)
+{
+ return &pk_buckets[pk_bucket_create_inner(sz, transient, description)];
+}
+
+void
+pk_bucket_destroy(struct pk_membucket* bkt)
+{
+ int64_t i;
+ for (i = 0; i < pk_bucket_head; ++i) {
+ if (&pk_buckets[i] == bkt) {
+ if (pk_bucket_head == i)
+ pk_bucket_head--;
+ break;
+ }
+ }
+ free(bkt->blocks);
+ bkt->size = 0;
+ bkt->head = 0;
+ bkt->lostBytes = 0;
+ bkt->allocs = 0;
+ bkt->lastEmptyBlockIndex = -1;
+ bkt->maxBlockCount = 0;
+ bkt->blocks = CAFE_BABE(struct pk_memblock);
+ bkt->ptr = CAFE_BABE(char);
+ bkt->transient = false;
+#ifdef PK_MEMORY_DEBUGGER
+ for (i = debug_alloc_head; i > -1; --i) {
+ if (debug_all_allocs[i].bkt == bkt) {
+ debug_all_allocs[i].blk.data = NULL;
+ debug_all_allocs[i].blk.size = 0u;
+ }
+ }
+#endif
+}
+
+void
+pk_bucket_reset(struct pk_membucket* bkt)
+{
+ int64_t i;
+ if (bkt->transient != true) {
+ PK_LOG_ERR("WARNING: pk_bucket_reset called on non-transient pk_membucket\n");
+ }
+ bkt->head = 0;
+ bkt->lostBytes = 0;
+ bkt->allocs = 0;
+ bkt->lastEmptyBlockIndex = 0;
+ bkt->blocks->data = bkt->ptr;
+ bkt->blocks->size = bkt->size - (sizeof(struct pk_memblock) * bkt->maxBlockCount);
+#ifdef PK_MEMORY_DEBUGGER
+ for (i = debug_alloc_head; i > -1; --i) {
+ if (debug_all_allocs[i].bkt == bkt) {
+ debug_all_allocs[i].blk.data = NULL;
+ debug_all_allocs[i].blk.size = 0u;
+ }
+ }
+#endif
+}
+
+void
+pk_bucket_insert_block(struct pk_membucket* bkt, const struct pk_memblock* block)
+{
+ int64_t index = bkt->lastEmptyBlockIndex;
+ while (index >= 0) {
+ struct pk_memblock* b = &bkt->blocks[index];
+ struct pk_memblock* nb = &bkt->blocks[index + 1];
+ if (b->data < block->data) {
+ break;
+ }
+ nb->data = b->data;
+ nb->size = b->size;
+ index -= 1;
+ }
+ struct pk_memblock *b = &bkt->blocks[index + 1];
+ b->data = block->data;
+ b->size = block->size;
+ bkt->lastEmptyBlockIndex += 1;
+}
+
+void
+pk_bucket_collapse_empty_blocks(struct pk_membucket* bkt) {
+ for (int64_t i = bkt->lastEmptyBlockIndex; i > -1; --i) {
+ struct pk_memblock* block = &bkt->blocks[i];
+ if (block->size == 0 && i == bkt->lastEmptyBlockIndex) {
+ block->data = nullptr;
+ bkt->lastEmptyBlockIndex -= 1;
+ continue;
+ }
+ if (block->size > 0) {
+ continue;
+ }
+ for (int64_t k = i; k < bkt->lastEmptyBlockIndex; ++k) {
+ bkt->blocks[k].data = bkt->blocks[k + 1].data;
+ bkt->blocks[k].size = bkt->blocks[k + 1].size;
+ }
+ bkt->lastEmptyBlockIndex -= 1;
+ }
+}
+
+void*
+pk_new_bkt(size_t sz, size_t alignment, struct pk_membucket* bkt)
+{
+#ifdef PK_MEMORY_FORCE_MALLOC
+ return malloc(sz);
+#endif
+ if (sz == 0) return nullptr;
+ size_t calculatedAlignment = alignment < PK_MINIMUM_ALIGNMENT ? PK_MINIMUM_ALIGNMENT : alignment;
+ size_t misalignment = 0;
+ struct pk_memblock* prevBlock = nullptr;
+ struct pk_memblock* block = nullptr;
+ struct pk_memblock* nextBlock = nullptr;
+ void* data = nullptr;
+ mtx_lock(&bkt->mtx);
+ for (int64_t i = 0; i <= bkt->lastEmptyBlockIndex; ++i) {
+ struct pk_memblock* blk = &bkt->blocks[i];
+ misalignment = (size_t)(blk->data) % calculatedAlignment;
+ misalignment = (calculatedAlignment - misalignment) % calculatedAlignment;
+ if (blk->size >= sz + misalignment) {
+ block = blk;
+ if (i < bkt->lastEmptyBlockIndex && bkt->blocks[i + 1].data == block->data + block->size) {
+ nextBlock = &bkt->blocks[i + 1];
+ }
+ if (i > 0 && i != bkt->lastEmptyBlockIndex && (bkt->blocks[i-1].data + bkt->blocks[i-1].size) == block->data) {
+ prevBlock = &bkt->blocks[i - 1];
+ }
+ break;
+ }
+ }
+ assert(block != nullptr && "memory corruption: failed to find bucket with enough space");
+ data = block->data + misalignment;
+#ifdef PK_MEMORY_DEBUGGER
+ bool handled = bkt->transient;
+ if (handled == false) {
+ for (int64_t i = 0; i < debug_alloc_head; ++i) {
+ struct pk_dbg_memblock* mb = &debug_all_allocs[i];
+ if (mb->bkt != NULL) continue;
+ assert((mb->blk.size == 0 || (void*)(mb->blk.data) != data) && "mem address alloc'd twice!");
+ if (mb->blk.size == 0) {
+ mb->blk.data = (char*)(data);
+ mb->blk.size = sz;
+ mb->bkt = bkt;
+ handled = true;
+ break;
+ }
+ }
+ }
+ if (handled == false) {
+ debug_all_allocs[debug_alloc_head++] = (struct pk_dbg_memblock){
+ .blk = (struct pk_memblock) {
+ .data = (char*)(data),
+ .size = sz,
+ },
+ .bkt = bkt,
+ };
+ }
+#endif
+ int64_t afterSize = block->size - (misalignment + sz);
+ if (block->data == bkt->ptr + bkt->head) {
+ bkt->head += (sz + misalignment);
+ }
+ if (afterSize > 0 && nextBlock == nullptr) {
+ struct pk_memblock newBlock;
+ memset(&newBlock, 0, sizeof(struct pk_memblock));
+ newBlock.data = block->data + misalignment + sz;
+ newBlock.size = afterSize;
+ pk_bucket_insert_block(bkt, &newBlock);
+ }
+ if (prevBlock == nullptr && nextBlock == nullptr) {
+ block->size = misalignment;
+ } else if (nextBlock != nullptr) {
+ block->size = misalignment;
+ nextBlock->data -= afterSize;
+ nextBlock->size += afterSize;
+ } else if (prevBlock != nullptr) {
+ prevBlock->size += misalignment;
+ block->data += misalignment + sz;
+ block->size = 0; // if you make it here, afterSize has already been handled
+ }
+ bkt->allocs++;
+ assert(data >= bkt->raw && "allocated data is before bucket data");
+ assert((char*)data <= bkt->ptr + bkt->size && "allocated data is after bucket data");
+ pk_bucket_collapse_empty_blocks(bkt);
+#ifdef PK_MEMORY_DEBUGGER
+ if (!bkt->transient) {
+ int64_t debug_tracked_alloc_size = 0;
+ int64_t debug_bucket_alloc_size = bkt->size - (sizeof(struct pk_memblock) * bkt->maxBlockCount);
+ for (int64_t i = 0; i < debug_alloc_head; ++i) {
+ if (debug_all_allocs[i].bkt != bkt) continue;
+ debug_tracked_alloc_size += debug_all_allocs[i].blk.size;
+ }
+ for (int64_t i = 0; i <= bkt->lastEmptyBlockIndex; ++i) {
+ debug_bucket_alloc_size -= bkt->blocks[i].size;
+ }
+ assert(debug_tracked_alloc_size == debug_bucket_alloc_size && "allocation size mismatch!");
+ }
+#endif
+ mtx_unlock(&bkt->mtx);
+ return data;
+}
+
+void*
+pk_new_base(size_t sz, size_t alignment)
+{
+ struct pk_membucket* bkt = nullptr;
+ for (long i = 0; i < pk_bucket_head; ++i) {
+ if (pk_buckets[i].transient == false && pk_buckets[i].size - pk_buckets[i].head > sz + PK_MAXIMUM_ALIGNMENT) {
+ bkt = &pk_buckets[i];
+ break;
+ }
+ }
+ if (bkt == nullptr) {
+ bkt = &pk_buckets[pk_bucket_create_inner(PK_DEFAULT_BUCKET_SIZE, false, "pk_bucket internally created")];
+ }
+ return pk_new_bkt(sz, alignment, bkt);
+}
+
+void
+pk_delete_bkt(const void* ptr, size_t sz, struct pk_membucket* bkt)
+{
+#ifdef PK_MEMORY_FORCE_MALLOC
+ return std::free(const_cast<void*>(ptr));
+#endif
+ mtx_lock(&bkt->mtx);
+ assert(ptr >= bkt->raw && (char*)ptr < bkt->ptr + bkt->size && "pointer not in memory bucket range");
+ assert(sz > 0 && "attempted to free pointer of size 0");
+#ifdef PK_MEMORY_DEBUGGER
+ bool found = bkt->transient;
+ if (found == false) {
+ for (int64_t i = debug_alloc_head - 1; i > -1; --i) {
+ struct pk_dbg_memblock* mb = &debug_all_allocs[i];
+ if (mb->bkt != bkt) continue;
+ if (mb->blk.size == 0) continue;
+ if ((void*)(mb->blk.data) == ptr) {
+ assert(mb->blk.size == sz && "[PK_MEMORY_HPP] incorrect free size");
+ mb->blk.size = 0;
+ mb->bkt = NULL;
+ found = true;
+ if (i == (debug_alloc_head - 1)) {
+ debug_alloc_head--;
+ }
+ break;
+ }
+ }
+ }
+ assert(found && "[PK_MEMORY_HPP] double free or invalid ptr");
+#endif
+ bkt->allocs--;
+ if (bkt->allocs == 0) {
+ bkt->head = 0;
+ bkt->lastEmptyBlockIndex = 0;
+ bkt->blocks[0].data = bkt->ptr;
+ bkt->blocks[0].size = bkt->size - (sizeof(struct pk_memblock) * bkt->maxBlockCount);
+ return;
+ }
+ char* afterPtr = ((char*)(ptr))+sz;
+ struct pk_memblock* beforeBlk = nullptr;
+ struct pk_memblock* afterBlk = nullptr;
+ for (int64_t i = bkt->lastEmptyBlockIndex; i > 0; --i) {
+ if (bkt->blocks[i-1].data + bkt->blocks[i-1].size == ptr) {
+ beforeBlk = &bkt->blocks[i-1];
+ }
+ if (bkt->blocks[i].data == afterPtr) {
+ afterBlk = &bkt->blocks[i];
+ break;
+ }
+ if (bkt->blocks[i-1].data < (char*)ptr) {
+ break;
+ }
+ }
+ if (ptr == bkt->ptr && afterBlk == nullptr && bkt->blocks[0].data == afterPtr) {
+ afterBlk = &bkt->blocks[0];
+ }
+ if (afterBlk != nullptr && afterBlk->data == bkt->ptr + bkt->head) {
+ bkt->head -= sz;
+ if (beforeBlk != nullptr) {
+ bkt->head -= beforeBlk->size;
+ }
+ }
+ if (beforeBlk == nullptr && afterBlk == nullptr) {
+ struct pk_memblock newBlock;
+ memset(&newBlock, 0, sizeof(struct pk_memblock));
+ newBlock.data = (char*)ptr;
+ newBlock.size = sz;
+ pk_bucket_insert_block(bkt, &newBlock);
+ } else if (beforeBlk != nullptr && afterBlk != nullptr) {
+ beforeBlk->size += sz + afterBlk->size;
+ afterBlk->size = 0;
+ } else if (beforeBlk != nullptr) {
+ beforeBlk->size += sz;
+ } else if (afterBlk != nullptr) {
+ afterBlk->data -= sz;
+ afterBlk->size += sz;
+ }
+ pk_bucket_collapse_empty_blocks(bkt);
+#ifdef PK_MEMORY_DEBUGGER
+ if (!bkt->transient) {
+ int64_t debug_tracked_alloc_size = 0;
+ int64_t debug_bucket_alloc_size = bkt->size - (sizeof(struct pk_memblock) * bkt->maxBlockCount);
+ for (int64_t i = 0; i < debug_alloc_head; ++i) {
+ if (debug_all_allocs[i].bkt != bkt) continue;
+ debug_tracked_alloc_size += debug_all_allocs[i].blk.size;
+ }
+ for (int64_t i = 0; i <= bkt->lastEmptyBlockIndex; ++i) {
+ debug_bucket_alloc_size -= bkt->blocks[i].size;
+ }
+ assert(debug_tracked_alloc_size == debug_bucket_alloc_size && "allocation size mismatch!");
+ }
+#endif
+ mtx_unlock(&bkt->mtx);
+}
+
+void
+pk_delete_base(const void* ptr, size_t sz)
+{
+ struct pk_membucket* bkt = nullptr;
+ for (long i = 0; i < pk_bucket_head; ++i) {
+ bkt = &pk_buckets[i];
+ if (ptr >= bkt->raw && (char*)ptr < bkt->ptr + bkt->size) break;
+ }
+ assert(bkt != nullptr && "failed to determine correct memory bucket");
+ pk_delete_bkt(ptr, sz, bkt);
+}
+
+#endif /* PK_IMPL_MEM */
+#ifndef PK_STR_H
+#define PK_STR_H
+
+#include <stdint.h>
+
+struct pk_str {
+ char *val;
+ uint32_t length;
+ uint32_t reserved;
+};
+struct pk_cstr {
+ const char *val;
+ uint32_t length;
+ uint32_t reserved;
+};
+
+struct pk_str cstring_to_pk_str(char *s);
+struct pk_cstr cstring_to_pk_cstr(const char *s);
+struct pk_str pk_cstr_to_pk_str(const struct pk_cstr *s);
+struct pk_cstr pk_str_to_pk_cstr(const struct pk_str *s);
+int pk_compare_str(const struct pk_str *lhs, const struct pk_str *rhs);
+int pk_compare_cstr(const struct pk_cstr *lhs, const struct pk_cstr *rhs);
+
+#endif /* PK_STR_H */
+
+#ifdef PK_IMPL_STR
+
+#include <string.h>
+
+struct pk_str
+cstring_to_pk_str(char *s)
+{
+ return (struct pk_str) {
+ .val = s,
+ .length = (uint32_t)(strlen(s)),
+ .reserved = 0,
+ };
+}
+
+struct pk_cstr
+cstring_to_pk_cstr(const char *s)
+{
+ return (struct pk_cstr) {
+ .val = s,
+ .length = (uint32_t)(strlen(s)),
+ .reserved = 0,
+ };
+}
+
+struct pk_str
+pk_cstr_to_pk_str(const struct pk_cstr *s)
+{
+ return (struct pk_str) {
+ .val = (char *)(s->val),
+ .length = s->length,
+ .reserved = s->reserved,
+ };
+}
+
+struct pk_cstr
+pk_str_to_pk_cstr(const struct pk_str *s)
+{
+ return (struct pk_cstr) {
+ .val = (char *)(s->val),
+ .length = s->length,
+ .reserved = s->reserved,
+ };
+}
+
+int
+pk_compare_str(const struct pk_str *lhs, const struct pk_str *rhs)
+{
+ return strncmp(lhs->val, rhs->val, PK_MIN(lhs->length, rhs->length));
+}
+
+int
+pk_compare_cstr(const struct pk_cstr *lhs, const struct pk_cstr *rhs)
+{
+ return strncmp(lhs->val, rhs->val, PK_MIN(lhs->length, rhs->length));
+}
+
+#endif /* PK_IMPL_STR */
+#ifndef PK_EV_H
+#define PK_EV_H
+
+#include <stdint.h>
+
+typedef uint64_t pk_ev_mgr_id_T;
+typedef uint64_t pk_ev_id_T;
+
+// note: pk_ev_init() is NOT thread-safe
+void pk_ev_init();
+// note: pk_ev_teardown() is NOT thread-safe
+void pk_ev_teardown();
+
+const pk_ev_mgr_id_T pk_ev_create_mgr();
+void pk_ev_destroy_mgr(pk_ev_mgr_id_T evmgr);
+
+typedef void (pk_ev_cb_fn)(void *user_event_data, void *user_cb_data, void *user_ev_data);
+
+const pk_ev_id_T pk_ev_register_ev(pk_ev_mgr_id_T evmgr, void *user_ev_data);
+bool pk_ev_register_cb(pk_ev_mgr_id_T evmgr, pk_ev_id_T evid, pk_ev_cb_fn *cb, void *user_cb_data);
+void pk_ev_emit(pk_ev_mgr_id_T evmgr, pk_ev_id_T evid, void *user_emit_data);
+
+#endif /* PK_EV_H */
+
+#ifdef PK_IMPL_EV
+
+#include <assert.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <threads.h>
+#include <string.h>
+
+#ifndef PK_EV_INIT_MGR_COUNT
+# define PK_EV_INIT_MGR_COUNT 1
+#endif
+
+#ifndef PK_EV_INIT_EV_COUNT
+# define PK_EV_INIT_EV_COUNT 16
+#endif
+
+#ifndef PK_EV_INIT_CB_COUNT
+# define PK_EV_INIT_CB_COUNT 8
+#endif
+
+#ifndef PK_EV_GROW_RATIO
+# define PK_EV_GROW_RATIO 1.5
+#endif
+
+struct pk_ev_cb {
+ pk_ev_cb_fn *cb;
+ void *user_cb_data;
+};
+
+struct pk_ev {
+ struct pk_ev_cb *ev_cbs;
+ void *user_ev_data;
+ atomic_uint_fast8_t n_ev_cbs;
+};
+
+struct pk_ev_mgr {
+ struct pk_ev *ev;
+ atomic_uint_fast8_t n_ev;
+ atomic_uint_fast8_t rn_ev;
+ atomic_uint_fast8_t rn_cb;
+};
+
+struct pk_ev_mstr {
+ atomic_uint_fast64_t flg_mgrs;
+ atomic_uint_fast64_t rn_mgrs;
+ struct pk_ev_mgr **mgrs;
+ mtx_t *mtxs;
+};
+
+struct pk_ev_mstr pk_ev_mstr;
+
+void
+pk_ev_init()
+{
+ int i;
+ pk_ev_mstr.mgrs = (struct pk_ev_mgr **)malloc(sizeof(void *) * PK_EV_INIT_MGR_COUNT);
+ pk_ev_mstr.mtxs = (mtx_t*)malloc(sizeof(mtx_t) * PK_EV_INIT_MGR_COUNT);
+ memset(pk_ev_mstr.mgrs, 0, sizeof(void *) * PK_EV_INIT_MGR_COUNT);
+ memset(pk_ev_mstr.mtxs, 0, sizeof(mtx_t) * PK_EV_INIT_MGR_COUNT);
+ for (i = 0; i < PK_EV_INIT_MGR_COUNT; ++i) {
+ mtx_init(&pk_ev_mstr.mtxs[i], mtx_plain);
+ }
+ atomic_store(&pk_ev_mstr.flg_mgrs, 0);
+ atomic_store(&pk_ev_mstr.rn_mgrs, PK_EV_INIT_MGR_COUNT);
+}
+
+void
+pk_ev_teardown()
+{
+ int i;
+ for (i = 0; i < pk_ev_mstr.rn_mgrs; ++i) {
+ if ((atomic_load(&pk_ev_mstr.rn_mgrs) & (1lu << i)) == 0) continue;
+ mtx_lock(&pk_ev_mstr.mtxs[i]);
+ free(pk_ev_mstr.mgrs[i]);
+ pk_ev_mstr.mgrs[i] = NULL;
+ mtx_unlock(&pk_ev_mstr.mtxs[i]);
+ mtx_destroy(&pk_ev_mstr.mtxs[i]);
+ }
+ free(pk_ev_mstr.mgrs);
+ free(pk_ev_mstr.mtxs);
+ pk_ev_mstr.mgrs = NULL;
+ pk_ev_mstr.mtxs = NULL;
+}
+
+static struct pk_ev_mgr*
+pk_ev_inner_ev_mgr_create(uint64_t ev_count, uint64_t cb_count)
+{
+ int i;
+ struct pk_ev *ev;
+ size_t sz = sizeof(struct pk_ev_mgr) + ((sizeof(struct pk_ev) * ev_count)) + (sizeof (struct pk_ev_cb) * ev_count * cb_count);
+ size_t sz_ev = (sizeof(struct pk_ev_cb) * cb_count);
+ size_t sz_evs = sizeof(struct pk_ev) * ev_count;
+
+ struct pk_ev_mgr *mgr = (struct pk_ev_mgr*)malloc(sz);
+ if (mgr == NULL) goto early_exit;
+
+ memset(mgr, 0, sz);
+ mgr->ev = (struct pk_ev*)(((char *)mgr) + sizeof(struct pk_ev_mgr));
+ atomic_init(&mgr->rn_ev, ev_count);
+ atomic_init(&mgr->rn_cb, cb_count);
+ atomic_init(&mgr->n_ev, 0);
+ for (i = 0; i < mgr->rn_ev; ++i) {
+ ev = &mgr->ev[i];
+ atomic_init(&ev->n_ev_cbs, 0);
+ ev->ev_cbs = (struct pk_ev_cb*)(((char *)mgr) + sizeof(struct pk_ev_mgr) + sz_evs + (sz_ev * i));
+ }
+
+early_exit:
+ return mgr;
+}
+
+static void
+pk_ev_inner_ev_mgr_clone(struct pk_ev_mgr *old, struct pk_ev_mgr *mgr)
+{
+ int i;
+ struct pk_ev *ev_old;
+ struct pk_ev *ev;
+ atomic_store(&mgr->n_ev, atomic_load(&old->n_ev));
+ for (i = 0; i < old->n_ev; ++i) {
+ ev_old = &old->ev[i];
+ ev = &mgr->ev[i];
+ memcpy(ev->ev_cbs, ev_old->ev_cbs, sizeof(struct pk_ev_cb) * atomic_load(&ev_old->n_ev_cbs));
+ atomic_store(&ev->n_ev_cbs, atomic_load(&ev_old->n_ev_cbs));
+ }
+}
+
+const pk_ev_mgr_id_T
+pk_ev_create_mgr()
+{
+ uint64_t i;
+ pk_ev_mgr_id_T flg;
+ pk_ev_mgr_id_T flg_new;
+ pk_ev_mgr_id_T id;
+ struct pk_ev_mgr *mgr = pk_ev_inner_ev_mgr_create(PK_EV_INIT_EV_COUNT, PK_EV_INIT_CB_COUNT);
+ if (mgr == NULL) return -1;
+start:
+ flg = atomic_load(&pk_ev_mstr.flg_mgrs);
+ while (1) {
+ flg_new = flg;
+ for (i = 0; i < atomic_load(&pk_ev_mstr.rn_mgrs); ++i) {
+ if ((flg & (1u << i)) == 0) break;
+ }
+ if (i == atomic_load(&pk_ev_mstr.rn_mgrs)) {
+ goto recreate;
+ }
+ id = i;
+ flg_new |= (1u << i);
+ if (atomic_compare_exchange_strong(&pk_ev_mstr.flg_mgrs, &flg, flg_new)) break;
+ thrd_yield();
+ }
+ pk_ev_mstr.mgrs[id]= mgr;
+ return id;
+recreate:
+ // TODO recreate mgr, out of space
+ assert(1 == 0 && "[pkev.h] Out of mgr space.");
+ exit(1);
+ goto start;
+}
+
+void
+pk_ev_destroy_mgr(pk_ev_mgr_id_T evmgr)
+{
+ assert(evmgr >= 0);
+ mtx_lock(&pk_ev_mstr.mtxs[evmgr]);
+ free(pk_ev_mstr.mgrs[evmgr]);
+ pk_ev_mstr.mgrs[evmgr] = NULL;
+ mtx_unlock(&pk_ev_mstr.mtxs[evmgr]);
+}
+
+const pk_ev_id_T
+pk_ev_register_ev(pk_ev_mgr_id_T evmgr, void *user_ev_data)
+{
+ assert(evmgr < 64);
+ pk_ev_id_T id;
+ struct pk_ev_mgr *mgr;
+ mtx_lock(&pk_ev_mstr.mtxs[evmgr]);
+ if (pk_ev_mstr.mgrs[evmgr]->n_ev == pk_ev_mstr.mgrs[evmgr]->rn_ev) {
+ mgr = pk_ev_inner_ev_mgr_create(pk_ev_mstr.mgrs[evmgr]->rn_ev * PK_EV_GROW_RATIO, pk_ev_mstr.mgrs[evmgr]->rn_cb);
+ pk_ev_inner_ev_mgr_clone(pk_ev_mstr.mgrs[evmgr], mgr);
+ free(pk_ev_mstr.mgrs[evmgr]);
+ pk_ev_mstr.mgrs[evmgr] = mgr;
+ }
+ id = pk_ev_mstr.mgrs[evmgr]->n_ev++;
+ pk_ev_mstr.mgrs[evmgr]->ev[id].user_ev_data = user_ev_data;
+ mtx_unlock(&pk_ev_mstr.mtxs[evmgr]);
+ return id;
+}
+
+bool
+pk_ev_register_cb(pk_ev_mgr_id_T evmgr, pk_ev_id_T evid, pk_ev_cb_fn *cb, void *user_cb_data)
+{
+ assert(evmgr < 64);
+ struct pk_ev_mgr *mgr;
+ uint8_t cb_index;
+ mtx_lock(&pk_ev_mstr.mtxs[evmgr]);
+ if (pk_ev_mstr.mgrs[evmgr]->ev[evid].n_ev_cbs == pk_ev_mstr.mgrs[evmgr]->rn_cb) {
+ mgr = pk_ev_inner_ev_mgr_create(pk_ev_mstr.mgrs[evmgr]->rn_ev, pk_ev_mstr.mgrs[evmgr]->rn_cb * PK_EV_GROW_RATIO);
+ pk_ev_inner_ev_mgr_clone(pk_ev_mstr.mgrs[evmgr], mgr);
+ free(pk_ev_mstr.mgrs[evmgr]);
+ pk_ev_mstr.mgrs[evmgr] = mgr;
+ }
+ cb_index = pk_ev_mstr.mgrs[evmgr]->ev[evid].n_ev_cbs++;
+ pk_ev_mstr.mgrs[evmgr]->ev[evid].ev_cbs[cb_index].cb = cb;
+ pk_ev_mstr.mgrs[evmgr]->ev[evid].ev_cbs[cb_index].user_cb_data = user_cb_data;
+ mtx_unlock(&pk_ev_mstr.mtxs[evmgr]);
+ return true;
+}
+
+void
+pk_ev_emit(pk_ev_mgr_id_T evmgr, pk_ev_id_T evid, void *user_emit_data)
+{
+ assert(evmgr < 64);
+ uint8_t i;
+ for (i = 0; i < pk_ev_mstr.mgrs[evmgr]->ev[evid].n_ev_cbs; ++i) {
+ (*pk_ev_mstr.mgrs[evmgr]->ev[evid].ev_cbs[i].cb)(
+ pk_ev_mstr.mgrs[evmgr]->ev[evid].user_ev_data,
+ pk_ev_mstr.mgrs[evmgr]->ev[evid].ev_cbs[i].user_cb_data,
+ user_emit_data);
+ }
+}
+
+#endif /* PK_IMPL_EV */
+#endif /* PK_SINGLE_HEADER_FILE_H */