summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile88
-rw-r--r--config.mk26
-rw-r--r--pkmacros.h216
-rw-r--r--pkmem-types.h65
-rw-r--r--pkmem.h556
-rw-r--r--test/pkmacros.c55
-rw-r--r--test/pkmacros.cpp191
-rw-r--r--test/pkmem-types.c42
-rw-r--r--test/pkmem-types.cpp65
-rw-r--r--test/pkmem.c17
-rw-r--r--test/pkmem.cpp17
11 files changed, 1338 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..bf99592
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,88 @@
+# pkh - header-only library
+# See LICENSE file for copyright and license details.
+
+include config.mk
+
+.PHONY: pkmem-types pkmem
+
+SRC = \
+ pkmacros.h \
+ pkmem-types.h \
+ pkmem.h \
+ test/pkmacros.c \
+ test/pkmacros.cpp \
+ test/pkmem-types.c \
+ test/pkmem-types.cpp \
+ test/pkmem.c \
+ test/pkmem.cpp \
+
+OBJ = $(SRC:%.c=.o)
+PPOBJ = $(SRC:%.cpp=.so)
+HOBJ = $(SRC:%.h=.gch)
+HPPOBJ = $(SRC:%.h=.gchpp)
+
+all: options .WAIT clean .WAIT \
+ pkmacros pkmem-types pkmem \
+ test-pkmem test-pkmem-cpp \
+ test-types-pkmem test-types-pkmem-cpp \
+ test-pkmacros test-pkmacros-cpp \
+
+options:
+ @echo at-suite build options:
+ @echo "CFLAGS = $(CFLAGS)"
+ @echo "LDFLAGS = $(LDFLAGS)"
+ @echo "CC = $(CC)"
+
+%.gch: %.h
+ $(CC) -std=c2x $(CFLAGS) -c $< -o $@
+%.gchpp: %.h
+ $(CXX) -std=c++23 $(CPPFLAGS) -x c++-header -c $< -o $@
+%.o: %.c
+ $(CC) -std=c2x $(CFLAGS) -g -O0 -c $< -o $@
+%.so: %.cpp
+ $(CXX) -std=c++23 $(CPPFLAGS) -g -O0 -c $< -o $@
+
+$(OBJ): config.mk
+$(PPOBJ): config.mk
+$(HOBJ): config.mk
+$(HPPOBJ): config.mk
+
+pkmacros: pkmacros.gch pkmacros.gchpp
+
+pkmem-types: pkmacros pkmem-types.gch pkmem-types.gchpp
+
+pkmem: pkmem-types pkmem.gch pkmem.gchpp
+
+test-pkmacros: test/pkmacros.o
+ $(CC) -g -O0 -std=c2x $(CFLAGS) -o test/$@ $^ $(LDFLAGS)
+
+test-pkmacros-cpp: test/pkmacros.so
+ $(CXX) -g -O0 -std=c++23 $(CPPFLAGS) -o test/$@ $^ $(LDFLAGS)
+
+test-pkmem-types: test/pkmem-types.o
+ $(CC) -g -O0 -std=c2x $(CFLAGS) -o test/$@ $^ $(LDFLAGS)
+
+test-pkmem-types-cpp: test/pkmem-types.so
+ $(CXX) -g -O0 -std=c++23 $(CPPFLAGS) -o test/$@ $^ $(LDFLAGS)
+
+test-pkmem: test/pkmem.o
+ $(CC) -g -O0 -std=c2x $(CFLAGS) -o test/$@ $^ $(LDFLAGS)
+
+test-pkmem-cpp: test/pkmem.so
+ $(CXX) -g -O0 -std=c++23 $(CPPFLAGS) -o test/$@ $^ $(LDFLAGS)
+
+test: pkmacros pkmem-types pkmem
+test: test-pkmacros test-pkmacros-cpp
+test: test-pkmem-types test-pkmem-types-cpp
+test: test-pkmem test-pkmem-cpp
+test:
+ @echo ""
+ ./test/test-pkmacros ; echo Result: $$? "\n"
+ ./test/test-pkmacros-cpp ; echo Result: $$? "\n"
+ ./test/test-pkmem-types ; echo Result: $$? "\n"
+ ./test/test-pkmem-types-cpp ; echo Result: $$? "\n"
+ ./test/test-pkmem ; echo Result: $$? "\n"
+ ./test/test-pkmem-cpp ; echo Result: $$? "\n"
+
+clean:
+ rm -f *.plist *.gch *.gchpp *.o *.so test/*.o test/*.so test/test-*
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..cf3ab50
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,26 @@
+# pkh version
+VERSION = 0.0.1
+
+# paths
+PREFIX = /usr/local
+MANPREFIX = $(PREFIX)/share/man
+
+PKG_CONFIG = pkg-config
+
+# includes and libs
+INCS = \
+
+LIBS = -lm \
+
+# flags
+# -pedantic
+SHARED_FLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \
+ -DVERSION=\"$(VERSION)\" -DPK_MEMORY_DEBUGGER -DPK_IMPLEMENTATION \
+
+CPPFLAGS += -Wall $(INCS) $(SHARED_FLAGS)
+CFLAGS += -Wall $(INCS) $(SHARED_FLAGS)
+LDFLAGS = $(LIBS)
+
+# compiler and linker
+CC ?= /usr/bin/clang
+CXX ?= /usr/bin/clang++
diff --git a/pkmacros.h b/pkmacros.h
new file mode 100644
index 0000000..0bda595
--- /dev/null
+++ b/pkmacros.h
@@ -0,0 +1,216 @@
+#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 */
+// vim: ts=2:sw=2:expandtab
diff --git a/pkmem-types.h b/pkmem-types.h
new file mode 100644
index 0000000..27f6f50
--- /dev/null
+++ b/pkmem-types.h
@@ -0,0 +1,65 @@
+#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;
+};
+
+const struct pk_handle pk_handle_MAX = (struct pk_handle){ .bucketIndex = 0xFFFFFFFF, .itemIndex = 0xFFFFFFFF };
+
+static inline 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;
+}
+
+#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 */
diff --git a/pkmem.h b/pkmem.h
new file mode 100644
index 0000000..e0267c4
--- /dev/null
+++ b/pkmem.h
@@ -0,0 +1,556 @@
+#ifndef PK_MEM_H
+#define PK_MEM_H
+
+#include "pkmem-types.h"
+#include "pkmacros.h"
+
+#include <string.h>
+#include <stdint.h>
+#include <stdio.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);
+
+static inline void stupid_header_warnings() { (void)stdout; }
+
+#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_IMPLEMENTATION
+
+#include <threads.h>
+#include <assert.h>
+
+#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
+static struct pk_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 i = 0; i < debug_alloc_head; ++i) {
+ if (debug_all_allocs[i].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_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)
+{
+ for (int64_t 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;
+}
+
+void
+pk_bucket_reset(struct pk_membucket* bkt)
+{
+ 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);
+}
+
+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_memblock* mb = &debug_all_allocs[i];
+ assert((mb->size == 0 || (void*)(mb->data) != data) && "mem address alloc'd twice!");
+ if (mb->size == 0) {
+ mb->data = (char*)(data);
+ mb->size = sz;
+ handled = true;
+ break;
+ }
+ }
+ }
+ if (handled == false) {
+ debug_all_allocs[debug_alloc_head++] = (struct pk_memblock){
+ .data = (char*)(data),
+ .size = sz,
+ };
+ }
+#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) {
+ debug_tracked_alloc_size += debug_all_allocs[i].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
+ 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_memblock* mb = &debug_all_allocs[i];
+ if (mb->size == 0) continue;
+ if ((void*)(mb->data) == ptr) {
+ assert(mb->size == sz && "[pk_MEMORY_HPP] incorrect free size");
+ mb->size = 0;
+ 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) {
+ debug_tracked_alloc_size += debug_all_allocs[i].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
+}
+
+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_IMPLEMENTATION */
diff --git a/test/pkmacros.c b/test/pkmacros.c
new file mode 100644
index 0000000..2a1f626
--- /dev/null
+++ b/test/pkmacros.c
@@ -0,0 +1,55 @@
+
+#include "../pkmacros.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+ (void)argc;
+ (void)argv;
+ (void)stdout;
+
+ // MISC
+ {
+ PK_LOGV_INF("PK_HAS_FLAG 000: %b\n", PK_HAS_FLAG(0xFF, 5));
+ PK_LOGV_INF("PK_HAS_FLAG 001: %b\n", PK_HAS_FLAG(0x0F, 5));
+ PK_LOGV_INF("PK_CLAMP 000: %i\n", PK_CLAMP(0, -10, 10));
+ PK_LOGV_INF("PK_CLAMP 001: %i\n", PK_CLAMP(-20, -10, 10));
+ PK_LOGV_INF("PK_CLAMP 002: %i\n", PK_CLAMP(20, -10, 10));
+ PK_LOGV_INF("PK_MIN 000: %i\n", PK_MIN(0, 10));
+ PK_LOGV_INF("PK_MIN 001: %i\n", PK_MIN(0, -10));
+ PK_LOGV_INF("PK_MAX 000: %i\n", PK_MAX(0, -10));
+ PK_LOGV_INF("PK_MAX 001: %i\n", PK_MAX(0, 10));
+ PK_LOGV_INF("CAFEBABE 000: %p\n", CAFE_BABE(void));
+ NULL_CHAR_ARR(c, 16);
+ PK_LOGV_INF("NULL_CHAR_ARR 000: '%s' '%lu'\n", c, strlen(c));
+ }
+
+ // PK_TO_BIN printing
+ {
+ uint8_t u8 = 0x55; // 01010101
+ uint16_t u16 = 0x5555;
+ uint32_t u32 = 0x55555555;
+ uint64_t u64 = 0x5555555555555555;
+ PK_LOGV_INF(PK_TO_BIN_PAT_8 "\n", PK_TO_BIN_8(u8));
+ PK_LOGV_INF(PK_TO_BIN_PAT_16"\n", PK_TO_BIN_16(u16));
+ PK_LOGV_INF(PK_TO_BIN_PAT_32"\n", PK_TO_BIN_32(u32));
+ PK_LOGV_INF(PK_TO_BIN_PAT_64"\n", PK_TO_BIN_64(u64));
+ u8 = 0xAA; // 10101010
+ u16 = 0xAAAA;
+ u32 = 0xAAAAAAAA;
+ u64 = 0xAAAAAAAAAAAAAAAA;
+ PK_LOGV_INF(PK_TO_BIN_PAT_8 "\n", PK_TO_BIN_8(u8));
+ PK_LOGV_INF(PK_TO_BIN_PAT_16"\n", PK_TO_BIN_16(u16));
+ PK_LOGV_INF(PK_TO_BIN_PAT_32"\n", PK_TO_BIN_32(u32));
+ PK_LOGV_INF(PK_TO_BIN_PAT_64"\n", PK_TO_BIN_64(u64));
+ u64 = 0xFFFFFFFF00000000;
+ PK_LOGV_INF(PK_TO_BIN_PAT_64"\n", PK_TO_BIN_64(u64));
+ u64 = 0x00000000FFFFFFFF;
+ PK_LOGV_INF(PK_TO_BIN_PAT_64"\n", PK_TO_BIN_64(u64));
+ }
+
+ return 0;
+}
diff --git a/test/pkmacros.cpp b/test/pkmacros.cpp
new file mode 100644
index 0000000..5e5377f
--- /dev/null
+++ b/test/pkmacros.cpp
@@ -0,0 +1,191 @@
+
+#include "../pkmacros.h"
+
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+#include <type_traits>
+
+TypeSafeInt_H(MyTSI_8, uint8_t, 0xFF);
+TypeSafeInt_H(MyTSI_16, uint16_t, 0xFFFF);
+TypeSafeInt_H(MyTSI_32, uint32_t, 0xFFFFFFFF);
+TypeSafeInt_H(MyTSI_64, uint64_t, 0xFFFFFFFFFFFFFFFF);
+
+TypeSafeInt_B(MyTSI_8);
+TypeSafeInt_B(MyTSI_16);
+TypeSafeInt_B(MyTSI_32);
+TypeSafeInt_B(MyTSI_64);
+
+TypeSafeInt_constexpr(MyConstexprTSI_8, uint8_t, 0xFF);
+TypeSafeInt_constexpr(MyConstexprTSI_16, uint16_t, 0xFFFF);
+TypeSafeInt_constexpr(MyConstexprTSI_32, uint32_t, 0xFFFFFFFF);
+TypeSafeInt_constexpr(MyConstexprTSI_64, uint64_t, 0xFFFFFFFFFFFFFFFF);
+
+template<typename T, typename T_T>
+bool
+tsi_test_operators()
+{
+ T mtsi0F {0x0F};
+ T mtsiF0 {0xF0};
+ T r;
+ /*
+ r = mtsi0F + T_T{static_cast<T_T>(mtsiF0)};
+ if (r == T{0}) return false;
+ r = mtsi0F - T_T{static_cast<T_T>(mtsiF0)};
+ if (r == T{0}) return false;
+ */
+ r = mtsi0F + mtsiF0;
+ if (r == T{0}) return false;
+ r = mtsi0F - mtsiF0;
+ if (r == T{0}) return false;
+ r = mtsi0F & mtsiF0;
+ if (r != T{0}) return false;
+ r = mtsi0F | mtsiF0;
+ if (r == T{0}) return false;
+ r = mtsi0F ^ mtsiF0;
+ if (r == T{0}) return false;
+ r = mtsi0F++;
+ if (r == T{0}) return false;
+ r = mtsi0F--;
+ if (r == T{0}) return false;
+ r = ++mtsi0F;
+ if (r == T{0}) return false;
+ r = --mtsi0F;
+ if (r == T{0}) return false;
+ r = mtsi0F << T{1};
+ if (r == T{0}) return false;
+ r = mtsi0F >> T{1};
+ if (r == T{0}) return false;
+ r = mtsi0F += mtsiF0;
+ if (r == T{0}) return false;
+ r = mtsi0F -= mtsiF0;
+ if (r == T{0}) return false;
+ r = mtsi0F &= mtsiF0;
+ if (r != T{0}) return false;
+ r = mtsi0F |= mtsiF0;
+ if (r == T{0}) return false;
+ r = mtsi0F ^= mtsiF0;
+ if (r != T{0}) return false;
+ r = mtsi0F = ~mtsiF0;
+ if (r == T{0}) return false;
+ return true;
+}
+
+template<typename T, typename T_T>
+constexpr bool
+tsi_test_operators_constexpr()
+{
+ constexpr T mtsi0F {0x0F};
+ constexpr T mtsiF0 {0xF0};
+ constexpr T mtsi00 {0x00};
+ /*
+ constexpr T r01 = mtsi0F + T_T{static_cast<T_T>(mtsiF0)};
+ if constexpr (r01 == mtsi00) return false;
+ constexpr T r02 = mtsi0F - T_T{static_cast<T_T>(mtsiF0)};
+ if constexpr (r02 == mtsi00) return false;
+ */
+ constexpr T r03 = mtsi0F + mtsiF0;
+ if constexpr (r03 == mtsi00) return false;
+ constexpr T r04 = mtsi0F - mtsiF0;
+ if constexpr (r04 == mtsi00) return false;
+ constexpr T r05 = mtsi0F & mtsiF0;
+ if constexpr (r05 != mtsi00) return false;
+ constexpr T r06 = mtsi0F | mtsiF0;
+ if constexpr (r06 == mtsi00) return false;
+ constexpr T r07 = mtsi0F ^ mtsiF0;
+ if constexpr (r07 == mtsi00) return false;
+ /*
+ constexpr T r08 = mtsi0F++;
+ if constexpr (r08 == mtsi00) return false;
+ constexpr T r09 = mtsi0F--;
+ if constexpr (r09 == mtsi00) return false;
+ constexpr T r10 = ++mtsi0F;
+ if constexpr (r10 == mtsi00) return false;
+ constexpr T r11 = --mtsi0F;
+ if constexpr (r11 == mtsi00) return false;
+ */
+ constexpr T r12 = mtsi0F << T{1};
+ if constexpr (r12 == mtsi00) return false;
+ constexpr T r13 = mtsi0F >> T{1};
+ if constexpr (r13 == mtsi00) return false;
+ /*
+ constexpr T r14 = mtsi0F += mtsiF0;
+ if constexpr (r14 == mtsi00) return false;
+ constexpr T r15 = mtsi0F -= mtsiF0;
+ if constexpr (r15 == mtsi00) return false;
+ constexpr T r16 = mtsi0F &= mtsiF0;
+ if constexpr (r16 != mtsi00) return false;
+ constexpr T r17 = mtsi0F |= mtsiF0;
+ if constexpr (r17 == mtsi00) return false;
+ constexpr T r18 = mtsi0F ^= mtsiF0;
+ if constexpr (r18 != mtsi00) return false;
+ constexpr T r19 = mtsi0F = ~mtsiF0;
+ if constexpr (r19 == mtsi00) return false;
+ */
+ return true;
+}
+
+class sdc {
+};
+
+int main(int argc, char *argv[])
+{
+ (void)argc;
+ (void)argv;
+ (void)std::is_const<void>::value;
+
+ // MISC
+ {
+ PK_LOGV_INF("PK_HAS_FLAG 000: %b\n", PK_HAS_FLAG(0xFF, 5));
+ PK_LOGV_INF("PK_HAS_FLAG 001: %b\n", PK_HAS_FLAG(0x0F, 5));
+ PK_LOGV_INF("PK_CLAMP 000: %i\n", PK_CLAMP(0, -10, 10));
+ PK_LOGV_INF("PK_CLAMP 001: %i\n", PK_CLAMP(-20, -10, 10));
+ PK_LOGV_INF("PK_CLAMP 002: %i\n", PK_CLAMP(20, -10, 10));
+ PK_LOGV_INF("PK_MIN 000: %i\n", PK_MIN(0, 10));
+ PK_LOGV_INF("PK_MIN 001: %i\n", PK_MIN(0, -10));
+ PK_LOGV_INF("PK_MAX 000: %i\n", PK_MAX(0, -10));
+ PK_LOGV_INF("PK_MAX 001: %i\n", PK_MAX(0, 10));
+ PK_LOGV_INF("CAFEBABE 000: %p\n", CAFE_BABE(void));
+ NULL_CHAR_ARR(c, 16);
+ PK_LOGV_INF("NULL_CHAR_ARR 000: '%s' '%lu'\n", c, strlen(c));
+ }
+
+ // IS_CONSTRUCTIBLE
+ {
+ bool b = false;
+ if IS_CONSTRUCTIBLE(sdc) b = true;
+ fprintf(stdout, "class sdc IS_CONSTRUCTIBLE: %b\n", b);
+ if IS_DESTRUCTIBLE(sdc) b = false;
+ fprintf(stdout, "class sdc IS_DESTRUCTIBLE: %b\n", b);
+ }
+
+ // TypeSafeInt
+ {
+ PK_LOGV_INF(PK_TO_BIN_PAT_8 "\n", PK_TO_BIN_8 (static_cast<MyTSI_8_T> (MyTSI_8_MAX)));
+ PK_LOGV_INF(PK_TO_BIN_PAT_16"\n", PK_TO_BIN_16(static_cast<MyTSI_16_T>(MyTSI_16_MAX)));
+ PK_LOGV_INF(PK_TO_BIN_PAT_32"\n", PK_TO_BIN_32(static_cast<MyTSI_32_T>(MyTSI_32_MAX)));
+ PK_LOGV_INF(PK_TO_BIN_PAT_64"\n", PK_TO_BIN_64(static_cast<MyTSI_64_T>(MyTSI_64_MAX)));
+ PK_LOGV_INF(PK_TO_BIN_PAT_8 "\n", PK_TO_BIN_8 (static_cast<MyConstexprTSI_8_T> (MyConstexprTSI_8_MAX)));
+ PK_LOGV_INF(PK_TO_BIN_PAT_16"\n", PK_TO_BIN_16(static_cast<MyConstexprTSI_16_T>(MyConstexprTSI_16_MAX)));
+ PK_LOGV_INF(PK_TO_BIN_PAT_32"\n", PK_TO_BIN_32(static_cast<MyConstexprTSI_32_T>(MyConstexprTSI_32_MAX)));
+ PK_LOGV_INF(PK_TO_BIN_PAT_64"\n", PK_TO_BIN_64(static_cast<MyConstexprTSI_64_T>(MyConstexprTSI_64_MAX)));
+ PK_LOGV_INF("tsi_test_operators 08: %b\n", tsi_test_operators<MyTSI_8, MyTSI_8_T>());
+ PK_LOGV_INF("tsi_test_operators 16: %b\n", tsi_test_operators<MyTSI_16, MyTSI_16_T>());
+ PK_LOGV_INF("tsi_test_operators 32: %b\n", tsi_test_operators<MyTSI_32, MyTSI_32_T>());
+ PK_LOGV_INF("tsi_test_operators 64: %b\n", tsi_test_operators<MyTSI_64, MyTSI_64_T>());
+ PK_LOGV_INF("tsi_test_operators c08: %b\n", tsi_test_operators<MyConstexprTSI_8, MyConstexprTSI_8_T>());
+ PK_LOGV_INF("tsi_test_operators c16: %b\n", tsi_test_operators<MyConstexprTSI_16, MyConstexprTSI_16_T>());
+ PK_LOGV_INF("tsi_test_operators c32: %b\n", tsi_test_operators<MyConstexprTSI_32, MyConstexprTSI_32_T>());
+ PK_LOGV_INF("tsi_test_operators c64: %b\n", tsi_test_operators<MyConstexprTSI_64, MyConstexprTSI_64_T>());
+ constexpr bool b1 = tsi_test_operators_constexpr<MyConstexprTSI_8, MyConstexprTSI_8_T>();
+ constexpr bool b2 = tsi_test_operators_constexpr<MyConstexprTSI_16, MyConstexprTSI_16_T>();
+ constexpr bool b3 = tsi_test_operators_constexpr<MyConstexprTSI_32, MyConstexprTSI_32_T>();
+ constexpr bool b4 = tsi_test_operators_constexpr<MyConstexprTSI_64, MyConstexprTSI_64_T>();
+ PK_LOGV_INF("TypeSafeInt_constexpr_08: %b\n", b1);
+ PK_LOGV_INF("TypeSafeInt_constexpr_16: %b\n", b2);
+ PK_LOGV_INF("TypeSafeInt_constexpr_32: %b\n", b3);
+ PK_LOGV_INF("TypeSafeInt_constexpr_64: %b\n", b4);
+ }
+
+ return 0;
+}
diff --git a/test/pkmem-types.c b/test/pkmem-types.c
new file mode 100644
index 0000000..2399308
--- /dev/null
+++ b/test/pkmem-types.c
@@ -0,0 +1,42 @@
+
+#include "../pkmem-types.h"
+#include "../pkmacros.h"
+
+#include <stdio.h>
+
+int main(int argc, char *argv[])
+{
+ (void)argc;
+ (void)argv;
+ (void)stdout;
+
+ // pk_handle_validate
+ {
+ enum PK_HANDLE_VALIDATION res;
+ struct pk_handle h, bh;
+
+ bh.bucketIndex = 2;
+ bh.itemIndex = 2;
+
+ h.bucketIndex = 0;
+ h.itemIndex = 0;
+ res = pk_handle_validate(h, bh, 1024);
+ PK_LOGV_INF("pk_handle_validate 000: %i\n", res);
+
+ h.bucketIndex = 3;
+ h.itemIndex = 0;
+ res = pk_handle_validate(h, bh, 1024);
+ PK_LOGV_INF("pk_handle_validate 001: %i\n", res);
+
+ h.bucketIndex = 2;
+ h.itemIndex = 3;
+ res = pk_handle_validate(h, bh, 1024);
+ PK_LOGV_INF("pk_handle_validate 002: %i\n", res);
+
+ h = pk_handle_MAX;
+ res = pk_handle_validate(h, bh, 1024);
+ PK_LOGV_INF("pk_handle_validate 003: %i\n", res);
+ }
+
+ return 0;
+}
diff --git a/test/pkmem-types.cpp b/test/pkmem-types.cpp
new file mode 100644
index 0000000..b8e19ef
--- /dev/null
+++ b/test/pkmem-types.cpp
@@ -0,0 +1,65 @@
+
+#include "../pkmem-types.h"
+#include "../pkmacros.h"
+
+#include <cstdio>
+
+int main(int argc, char *argv[])
+{
+ (void)argc;
+ (void)argv;
+ (void)stdout;
+
+ // pk_handle operator==
+ {
+ struct pk_handle h, bh;
+ bool res;
+
+ constexpr struct pk_handle ch1 { .bucketIndex = 0xAAAAAAAA, .itemIndex = 0x55555555 };
+ constexpr struct pk_handle cbh1 { .bucketIndex = 0xAAAAAAAA, .itemIndex = 0x55555555 };
+ constexpr bool ret1 = (ch1 == cbh1);
+ PK_LOGV_INF("pk_handle constexpr operator== 000: %b\n", ret1);
+
+ constexpr struct pk_handle ch2 { .bucketIndex = 0x00000000, .itemIndex = 0x00000000 };
+ constexpr struct pk_handle cbh2 { .bucketIndex = 0xFFFFFFFF, .itemIndex = 0xFFFFFFFF };
+ constexpr bool ret2 = (ch2 == cbh2);
+ PK_LOGV_INF("pk_handle constexpr operator== 001: %b\n", ret2);
+
+ h.bucketIndex = 0xCAFE;
+ h.itemIndex = 0xBABE;
+ bh.bucketIndex = 0xCAFE;
+ bh.itemIndex = 0xBABE;
+ res = h == bh;
+ PK_LOGV_INF("pk_handle operator== 000: %b\n", res);
+
+ h.bucketIndex = 0x00000000;
+ h.itemIndex = 0x00000000;
+ bh.bucketIndex = 0xFFFFFFFF;
+ bh.itemIndex = 0xFFFFFFFF;
+ res = h == bh;
+ PK_LOGV_INF("pk_handle operator== 001: %b\n", res);
+ }
+
+ // pk_handle_validate_constexpr
+ {
+ constexpr struct pk_handle cbh { .bucketIndex = 2, .itemIndex = 2 };
+
+ constexpr struct pk_handle ch1 { .bucketIndex = 0, .itemIndex = 0 };
+ constexpr enum PK_HANDLE_VALIDATION ret1 = pk_handle_validate_constexpr<ch1, cbh, 1024ULL>();
+ PK_LOGV_INF("pk_handle_validate_constexpr: 000: %i\n", ret1);
+
+ constexpr struct pk_handle ch2 { .bucketIndex = 3, .itemIndex = 0 };
+ constexpr enum PK_HANDLE_VALIDATION ret2 = pk_handle_validate_constexpr<ch2, cbh, 1024ULL>();
+ PK_LOGV_INF("pk_handle_validate_constexpr: 000: %i\n", ret2);
+
+ constexpr struct pk_handle ch3 { .bucketIndex = 2, .itemIndex = 3 };
+ constexpr enum PK_HANDLE_VALIDATION ret3 = pk_handle_validate_constexpr<ch3, cbh, 1024ULL>();
+ PK_LOGV_INF("pk_handle_validate_constexpr: 000: %i\n", ret3);
+
+ constexpr struct pk_handle ch4 = pk_handle_MAX_constexpr;
+ constexpr enum PK_HANDLE_VALIDATION ret4 = pk_handle_validate_constexpr<ch4, cbh, 1024ULL>();
+ PK_LOGV_INF("pk_handle_validate_constexpr: 000: %i\n", ret4);
+ }
+
+ return 0;
+}
diff --git a/test/pkmem.c b/test/pkmem.c
new file mode 100644
index 0000000..b6826a2
--- /dev/null
+++ b/test/pkmem.c
@@ -0,0 +1,17 @@
+
+#include "../pkmem.h"
+
+int main(int argc, char *argv[])
+{
+ (void)argc;
+ (void)argv;
+
+ // pk_new_base
+ {
+ char *some_dang_string = (char*)pk_new_base(64, alignof(char*));
+ fprintf(stdout, "some_dang_string: %p\n", some_dang_string);
+ pk_delete_base(some_dang_string, 64);
+ }
+
+ return 0;
+}
diff --git a/test/pkmem.cpp b/test/pkmem.cpp
new file mode 100644
index 0000000..cc7cf9c
--- /dev/null
+++ b/test/pkmem.cpp
@@ -0,0 +1,17 @@
+
+#include "../pkmem.h"
+
+int main(int argc, char *argv[])
+{
+ (void)argc;
+ (void)argv;
+
+ // pk_new<T>
+ {
+ char *some_dang_string = pk_new<char>(64);
+ fprintf(stdout, "some_dang_string: %p\n", some_dang_string);
+ pk_delete<char>(some_dang_string, 64);
+ }
+
+ return 0;
+}