diff --git a/architecture/x86/simd/utils/Utils.h b/architecture/x86/simd/utils/Utils.h index a62b3e0..9d4c4ba 100755 --- a/architecture/x86/simd/utils/Utils.h +++ b/architecture/x86/simd/utils/Utils.h @@ -17,8 +17,8 @@ // Only allowed for data >= 64 bits bool is_empty(const byte* region, uint64 size, int32 steps = 8) { - // Quick check of first 8 bytes - if (*((uint64 *) region) != 0) { + // Quick check of first byte + if (*region != 0) { return false; } diff --git a/check_cpu_features.sh b/check_cpu_features.sh new file mode 100644 index 0000000..4261651 --- /dev/null +++ b/check_cpu_features.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +check_cpu_features() { + local FLAGS="" + local MACROS="" + + # Check for SSE4.2 + if lscpu | grep -qi sse4_2; then + FLAGS+=" -msse4.2" + MACROS+=" -D__SSE4_2__" + fi + + # Check for AVX + if lscpu | grep -qi avx; then + FLAGS+=" -mavx" + MACROS+=" -D__AVX__" + fi + + # Check for AVX2 + if lscpu | grep -qi avx2; then + FLAGS+=" -mavx2" + MACROS+=" -D__AVX2__" + fi + + # Check for AVX512 + if lscpu | grep -qi avx512; then + FLAGS+=" -mavx512f -mavx512cd -mavx512vl -mavx512bw -mavx512dq -mavx512ifma -mavx512vbmi" + MACROS+=" -D__AVX512F__" + fi + + # Check for FMA + if lscpu | grep -qi fma; then + FLAGS+=" -mfma" + MACROS+=" -D__FMA__" + fi + + # Check for POPCNT + if lscpu | grep -qi popcnt; then + FLAGS+=" -mpopcnt" + MACROS+=" -D__POPCNT__" + fi + + # Check for endianness + if [ "$(lscpu | grep -i 'byte order' | awk '{print $3}')" = "Little" ]; then + MACROS+=" -D__LITTLE_ENDIAN__" + elif [ "$(lscpu | grep -i 'byte order' | awk '{print $3}')" = "Big" ]; then + MACROS+=" -D__BIG_ENDIAN__" + fi + + # Return the results (two different methods shown) + + # Method 1: Set global variables + CPU_FLAGS="$FLAGS" + CPU_MACROS="$MACROS" + + # Method 2: Output to stdout (alternative approach) + echo "$FLAGS $MACROS" +} \ No newline at end of file diff --git a/database/DatabasePool.h b/database/DatabasePool.h index fefbb7f..4da2bbe 100644 --- a/database/DatabasePool.h +++ b/database/DatabasePool.h @@ -34,7 +34,7 @@ struct DatabasePool { void db_pool_alloc(DatabasePool* pool, uint8 count) { ASSERT_SIMPLE(count); PROFILE(PROFILE_DB_POOL_ALLOC, NULL, false, true); - LOG_1("Allocating DatabasePool"); + LOG_1("Allocating DatabasePool for %d connections", {{LOG_DATA_BYTE, &count}}); uint64 size = count * sizeof(DatabaseConnection) + sizeof(uint64) * CEIL_DIV(count, 64) // free @@ -43,8 +43,6 @@ void db_pool_alloc(DatabasePool* pool, uint8 count) { pool->connections = (DatabaseConnection *) platform_alloc_aligned(size, 64); pool->free = (uint64 *) ROUND_TO_NEAREST((uintptr_t) (pool->connections + count * sizeof(DatabaseConnection)), 64); pool->count = count; - - LOG_1("Allocated DatabasePool: %n B", {{LOG_DATA_UINT64, &pool->count}}); } void db_pool_add(DatabasePool* __restrict pool, DatabaseConnection* __restrict db) noexcept { @@ -53,6 +51,8 @@ void db_pool_add(DatabasePool* __restrict pool, DatabaseConnection* __restrict d } void db_pool_free(DatabasePool* pool) { + LOG_1("Freeing DatabasePool"); + for (int32 i = 0; i < pool->count; ++i) { db_close(&pool->connections[i]); } @@ -60,8 +60,6 @@ void db_pool_free(DatabasePool* pool) { platform_aligned_free((void **) &pool->connections); pool->free = NULL; pool->count = 0; - - LOG_1("Freed DatabasePool"); } // Returns free database connection or null if none could be found diff --git a/log/Log.h b/log/Log.h index e2cb9c6..9bc3f5e 100755 --- a/log/Log.h +++ b/log/Log.h @@ -67,7 +67,9 @@ enum LogDataType { LOG_DATA_NONE, LOG_DATA_VOID, LOG_DATA_BYTE, + LOG_DATA_INT16, LOG_DATA_INT32, + LOG_DATA_UINT16, LOG_DATA_UINT32, LOG_DATA_INT64, LOG_DATA_UINT64, @@ -100,7 +102,7 @@ struct LogDataArray{ LogData data[LOG_DATA_ARRAY]; }; -// @bug This probably requires thread safety +// @bug This needs to be thread safe byte* log_get_memory() noexcept { if (_log_memory->pos + MAX_LOG_LENGTH > _log_memory->size) { @@ -116,6 +118,7 @@ byte* log_get_memory() noexcept } // @performance This should only be called async to avoid blocking (e.g. render loop) +// @bug This needs to be thread safe void log_to_file() { // we don't log an empty log pool @@ -157,6 +160,7 @@ void log_flush() _log_memory->pos = 0; } +// @bug This needs to be thread safe void log(const char* str, const char* file, const char* function, int32 line) { if (!_log_memory) { @@ -199,6 +203,7 @@ void log(const char* str, const char* file, const char* function, int32 line) } } +// @bug This needs to be thread safe void log(const char* format, LogDataArray data, const char* file, const char* function, int32 line) { if (!_log_memory) { @@ -236,6 +241,12 @@ void log(const char* format, LogDataArray data, const char* file, const char* fu case LOG_DATA_BYTE: { sprintf_fast_iter(msg->message, temp_format, (int32) *((byte *) data.data[i].value)); } break; + case LOG_DATA_INT16: { + sprintf_fast_iter(msg->message, temp_format, (int32) *((int16 *) data.data[i].value)); + } break; + case LOG_DATA_UINT16: { + sprintf_fast_iter(msg->message, temp_format, (uint32) *((uint16 *) data.data[i].value)); + } break; case LOG_DATA_INT32: { sprintf_fast_iter(msg->message, temp_format, *((int32 *) data.data[i].value)); } break; diff --git a/log/PerformanceProfiler.h b/log/PerformanceProfiler.h index 6a96d16..f147ca8 100755 --- a/log/PerformanceProfiler.h +++ b/log/PerformanceProfiler.h @@ -13,7 +13,6 @@ #include "../utils/TimeUtils.h" #include "../thread/Spinlock.cpp" #include "../thread/Atomic.h" -#include "../system/Allocator.h" #include "../hash/GeneralHash.h" #include "../architecture/Intrinsics.h" #include "../compiler/CompilerUtils.h" @@ -24,6 +23,7 @@ enum TimingStats { PROFILE_TEMP, + PROFILE_MEMORY_ALLOC, PROFILE_FILE_UTILS, PROFILE_BUFFER_ALLOC, PROFILE_CHUNK_ALLOC, diff --git a/log/Stats.h b/log/Stats.h index 9e8bb56..b13f0dd 100755 --- a/log/Stats.h +++ b/log/Stats.h @@ -16,6 +16,8 @@ DEBUG_COUNTER_DRIVE_READ, DEBUG_COUNTER_DRIVE_WRITE, + DEBUG_COUNTER_THREAD, + DEBUG_COUNTER_GPU_VERTEX_UPLOAD, DEBUG_COUNTER_GPU_UNIFORM_UPLOAD, DEBUG_COUNTER_GPU_DRAW_CALLS, @@ -50,6 +52,16 @@ void log_increment(int32 id, int64 by = 1) noexcept atomic_add_acquire(&_stats_counter[id], by); } +inline +void log_decrement(int32 id, int64 by = 1) noexcept +{ + if (!_stats_counter) { + return; + } + + atomic_sub_acquire(&_stats_counter[id], by); +} + inline void log_counter(int32 id, int64 value) noexcept { @@ -63,11 +75,13 @@ void log_counter(int32 id, int64 value) noexcept #if (!DEBUG && !INTERNAL) || RELEASE #define LOG_INCREMENT(a) ((void) 0) #define LOG_INCREMENT_BY(a, b) ((void) 0) + #define LOG_DECREMENT(a) ((void) 0) #define LOG_COUNTER(a, b) ((void) 0) #define RESET_COUNTER(a) ((void) 0) #else #define LOG_INCREMENT(a) log_increment((a), 1) #define LOG_INCREMENT_BY(a, b) log_increment((a), (b)) + #define LOG_DECREMENT(a) log_decrement((a), 1) #define LOG_COUNTER(a, b) log_counter((a), (b)) #define RESET_COUNTER(a) reset_counter((a)) #endif diff --git a/math/Evaluator.h b/math/Evaluator.h index 8dc70be..61af860 100755 --- a/math/Evaluator.h +++ b/math/Evaluator.h @@ -111,8 +111,8 @@ f32 evaluator_evaluate_function(const char* name, const char* args); // Shunting-yard algorithm to evaluate the expression f32 evaluator_evaluate_expression(const char* expr) { - EvaluatorOperatorStack operators = { .top = -1 }; - EvaluatorValueStack values = { .top = -1 }; + EvaluatorOperatorStack operators = { .items = {}, .top = -1 }; + EvaluatorValueStack values = { .items = {}, .top = -1 }; const char* ptr = expr; while (*ptr) { diff --git a/memory/ChunkMemory.h b/memory/ChunkMemory.h index f10f9d5..e2647f6 100755 --- a/memory/ChunkMemory.h +++ b/memory/ChunkMemory.h @@ -66,8 +66,6 @@ void chunk_alloc(ChunkMemory* buf, uint32 count, uint32 chunk_size, int32 alignm buf->free = (uint64 *) ROUND_TO_NEAREST((uintptr_t) (buf->memory + count * chunk_size), alignment); memset(buf->memory, 0, buf->size); - - LOG_1("Allocated ChunkMemory: %n B", {{LOG_DATA_UINT64, &buf->size}}); } inline @@ -110,8 +108,7 @@ void chunk_init(ChunkMemory* buf, byte* data, uint32 count, uint32 chunk_size, i + sizeof(uint64) * CEIL_DIV(count, alignment) // free + alignment * 2; // overhead for alignment - // @bug what if an alignment is defined? - buf->memory = data; + buf->memory = (byte *) ROUND_TO_NEAREST((uintptr_t) data, alignment); buf->count = count; buf->size = size; @@ -307,7 +304,7 @@ int32 chunk_reserve(ChunkMemory* buf, uint32 elements = 1) noexcept } if (free_element < 0) { - ASSERT_SIMPLE(false); + LOG_3("No free chunk memory index found"); return -1; } diff --git a/memory/RingMemory.h b/memory/RingMemory.h index 7dbb6d1..e77644f 100755 --- a/memory/RingMemory.h +++ b/memory/RingMemory.h @@ -60,8 +60,6 @@ void ring_alloc(RingMemory* ring, uint64 size, uint32 alignment = 64) ring->alignment = alignment; memset(ring->memory, 0, ring->size); - - LOG_1("Allocated RingMemory: %n B", {{LOG_DATA_UINT64, &ring->size}}); } inline diff --git a/platform/linux/Allocator.h b/platform/linux/Allocator.h index c13b7a5..9d83e22 100755 --- a/platform/linux/Allocator.h +++ b/platform/linux/Allocator.h @@ -17,6 +17,7 @@ #include "../../utils/TestUtils.h" #include "../../log/DebugMemory.h" #include "../../log/Stats.h" +#include "../../log/Log.h" // @todo Currently alignment only effects the starting position, but it should also effect the ending/size @@ -24,11 +25,16 @@ // does this have a negative impact on caching? // Our Memory doesn't start at the cache line beginning but at least offset by sizeof(size_t) +static int32 _page_size = 0; + inline void* platform_alloc(size_t size) { - ssize_t page_size = sysconf(_SC_PAGESIZE); - size = (size + sizeof(size_t) + page_size - 1) & ~(page_size - 1); + if (!_page_size) { + _page_size = (int32) sysconf(_SC_PAGESIZE); + } + + size = ROUND_TO_NEAREST(size + sizeof(size_t), _page_size); void* ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); ASSERT_SIMPLE(ptr != MAP_FAILED); @@ -37,6 +43,7 @@ void* platform_alloc(size_t size) DEBUG_MEMORY_INIT((uintptr_t) ptr, size); LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size); + LOG_3("Allocated %n B", {{LOG_DATA_UINT64, &size}}); return (void *) ((uintptr_t) ptr + sizeof(size_t)); } @@ -44,13 +51,12 @@ void* platform_alloc(size_t size) inline void* platform_alloc_aligned(size_t size, int32 alignment) { - ssize_t page_size = sysconf(_SC_PAGESIZE); - if (alignment < page_size) { - alignment = page_size; + if (!_page_size) { + _page_size = (int32) sysconf(_SC_PAGESIZE); } - size = ROUND_TO_NEAREST(size, alignment); - size += alignment + sizeof(void *) + sizeof(size_t); + size = ROUND_TO_NEAREST(size + sizeof(void *) + sizeof(size_t) + alignment - 1, alignment); + size = ROUND_TO_NEAREST(size, _page_size); void* ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); ASSERT_SIMPLE(ptr != MAP_FAILED); @@ -60,13 +66,14 @@ void* platform_alloc_aligned(size_t size, int32 alignment) // However, when freeing the pointer later on we need the actual start of the memory area, not the manually offset one. // We do the same with the size, which is required when freeing uintptr_t raw_address = (uintptr_t) ptr + sizeof(void *) + sizeof(size_t); - void* aligned_ptr = (void *) ((raw_address + alignment - 1) & ~(alignment - 1)); + void* aligned_ptr = (void *) ROUND_TO_NEAREST(raw_address, alignment); *((void **) ((uintptr_t) aligned_ptr - sizeof(void *) - sizeof(size_t))) = ptr; *((size_t *) ((uintptr_t) aligned_ptr - sizeof(size_t))) = size; DEBUG_MEMORY_INIT((uintptr_t) aligned_ptr, size); LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size); + LOG_3("Aligned allocated %n B", {{LOG_DATA_UINT64, &size}}); return aligned_ptr; } @@ -92,11 +99,14 @@ void platform_aligned_free(void** aligned_ptr) { inline void* platform_shared_alloc(int32* fd, const char* name, size_t size) { + if (!_page_size) { + _page_size = (int32) sysconf(_SC_PAGESIZE); + } + *fd = shm_open(name, O_CREAT | O_RDWR, 0666); ASSERT_SIMPLE(*fd != -1); - ssize_t page_size = sysconf(_SC_PAGESIZE); - size = (size + sizeof(size_t) + page_size - 1) & ~(page_size - 1); + size = ROUND_TO_NEAREST(size + sizeof(size_t), _page_size); ftruncate(*fd, size); @@ -107,6 +117,7 @@ void* platform_shared_alloc(int32* fd, const char* name, size_t size) DEBUG_MEMORY_INIT((uintptr_t) shm_ptr, size); LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size); + LOG_3("Shared allocated %n B", {{LOG_DATA_UINT64, &size}}); return (void *) ((uintptr_t) shm_ptr + sizeof(size_t)); } @@ -117,11 +128,11 @@ void* platform_shared_open(int32* fd, const char* name, size_t size) *fd = shm_open(name, O_RDWR, 0666); ASSERT_SIMPLE(*fd != -1); - ssize_t page_size = sysconf(_SC_PAGESIZE); - size = (size + sizeof(size_t) + page_size - 1) & ~(page_size - 1); + size = ROUND_TO_NEAREST(size + sizeof(size_t), _page_size); void* shm_ptr = mmap(NULL, size, PROT_READ, MAP_SHARED, *fd, 0); ASSERT_SIMPLE(shm_ptr); + LOG_3("Shared opened %n B", {{LOG_DATA_UINT64, &size}}); *((size_t *) shm_ptr) = size; diff --git a/platform/linux/threading/Thread.h b/platform/linux/threading/Thread.h index aa3cfb4..03cd230 100755 --- a/platform/linux/threading/Thread.h +++ b/platform/linux/threading/Thread.h @@ -39,9 +39,17 @@ int32 coms_pthread_create(coms_pthread_t* thread, void*, ThreadJobFunc start_rou return 1; } + const uint64 stack_size = 1 * MEGABYTE; + thread->stack = platform_alloc_aligned(stack_size, 64); + int32 flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM; - *thread = clone((int32 (*)(void*))start_routine, NULL, flags, arg); - if (*thread == -1) { + + // The + stack_size is required since the stack is used "downwards" + thread->h = clone((int32 (*)(void*))start_routine, (void *) ((uintptr_t) thread->stack + stack_size), flags, arg); + + if (thread->h == -1) { + LOG_1("Thread creation faild with error %d", {{LOG_DATA_INT32, &errno}}); + return 1; } @@ -50,9 +58,13 @@ int32 coms_pthread_create(coms_pthread_t* thread, void*, ThreadJobFunc start_rou FORCE_INLINE int32 coms_pthread_join(coms_pthread_t thread, void** retval) { - return syscall(SYS_waitid, P_PID, thread, retval, WEXITED, NULL) == -1 + int32 res = syscall(SYS_waitid, P_PID, thread, retval, WEXITED, NULL) == -1 ? 1 : 0; + + platform_aligned_free((void **) &thread.stack); + + return res; } FORCE_INLINE @@ -126,7 +138,7 @@ int32 mutex_condimedwait(mutex_cond* cond, mutex* mutex, const struct timespec*) return 0; } -inline +FORCE_INLINE int32 coms_pthread_cond_wait(mutex_cond* cond, mutex* mutex) { return mutex_condimedwait(cond, mutex, NULL); } diff --git a/platform/linux/threading/ThreadDefines.h b/platform/linux/threading/ThreadDefines.h index 0edae2e..5d523dd 100755 --- a/platform/linux/threading/ThreadDefines.h +++ b/platform/linux/threading/ThreadDefines.h @@ -34,6 +34,9 @@ struct coms_pthread_rwlock_t { bool exclusive; }; -typedef int coms_pthread_t; +struct coms_pthread_t { + int h; + void* stack; +}; #endif \ No newline at end of file diff --git a/platform/win32/Allocator.h b/platform/win32/Allocator.h index e2b88b6..4eebfb9 100755 --- a/platform/win32/Allocator.h +++ b/platform/win32/Allocator.h @@ -15,15 +15,26 @@ #include "../../utils/TestUtils.h" #include "../../log/DebugMemory.h" #include "../../log/Stats.h" +#include "../../log/Log.h" // @todo Currently alignment only effects the starting position, but it should also effect the ending/size +static int32 _page_size = 0; + inline void* platform_alloc(size_t size) { + if (!_page_size) { + // @todo Fix and get system page size + _page_size = 1; + } + + size = ROUND_TO_NEAREST(size, _page_size); + void* ptr = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); DEBUG_MEMORY_INIT((uintptr_t) ptr, size); LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size); + LOG_3("Allocated %n B", {{LOG_DATA_UINT64, &size}}); return ptr; } @@ -31,7 +42,12 @@ void* platform_alloc(size_t size) inline void* platform_alloc_aligned(size_t size, int32 alignment) { - size = ROUND_TO_NEAREST(size, alignment); + if (!_page_size) { + _page_size = 1; + } + + size = ROUND_TO_NEAREST(size + sizeof(void*) + alignment - 1, alignment); + size = ROUND_TO_NEAREST(size, _page_size); void* ptr = VirtualAlloc(NULL, size + alignment + sizeof(void*), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); ASSERT_SIMPLE(ptr); @@ -39,11 +55,12 @@ void* platform_alloc_aligned(size_t size, int32 alignment) // We want an aligned memory area but mmap doesn't really support that. // That's why we have to manually offset our memory area. // However, when freeing the pointer later on we need the actual start of the memory area, not the manually offset one. - void* aligned_ptr = (void *) (((uintptr_t) ptr + alignment + sizeof(void*) - 1) & ~(alignment - 1)); + void* aligned_ptr = (void *) ROUND_TO_NEAREST((uintptr_t) ptr + sizeof(void*), alignment); ((void**) aligned_ptr)[-1] = ptr; DEBUG_MEMORY_INIT((uintptr_t) aligned_ptr, size); LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size); + LOG_3("Aligned allocated %n B", {{LOG_DATA_UINT64, &size}}); return aligned_ptr; } @@ -67,6 +84,12 @@ void platform_aligned_free(void** aligned_ptr) { inline void* platform_shared_alloc(HANDLE* fd, const char* name, size_t size) { + if (!_page_size) { + _page_size = 1; + } + + size = ROUND_TO_NEAREST(size, _page_size); + *fd = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD) size, name); ASSERT_SIMPLE(*fd); @@ -75,6 +98,7 @@ void* platform_shared_alloc(HANDLE* fd, const char* name, size_t size) DEBUG_MEMORY_INIT((uintptr_t) shm_ptr, size); LOG_INCREMENT_BY(DEBUG_COUNTER_MEM_ALLOC, size); + LOG_3("Shared allocated %n B", {{LOG_DATA_UINT64, &size}}); return shm_ptr; } @@ -87,6 +111,7 @@ void* platform_shared_open(HANDLE* fd, const char* name, size_t size) void* shm_ptr = MapViewOfFile(*fd, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, (DWORD) size); ASSERT_SIMPLE(shm_ptr); + LOG_3("Shared opened %n B", {{LOG_DATA_UINT64, &size}}); return shm_ptr; } diff --git a/platform/win32/threading/ThreadDefines.h b/platform/win32/threading/ThreadDefines.h index 61aeb45..00aa8c6 100755 --- a/platform/win32/threading/ThreadDefines.h +++ b/platform/win32/threading/ThreadDefines.h @@ -18,7 +18,12 @@ typedef CRITICAL_SECTION mutex; typedef void mutexattr_t; typedef void coms_pthread_condattr_t; typedef void coms_pthread_rwlockattr_t; -typedef HANDLE coms_pthread_t; + +struct coms_pthread_t { + HANDLE h; + void* stack; +}; + typedef CONDITION_VARIABLE mutex_cond; // Thread local variable Already exists in c++11 diff --git a/stdlib/HashMap.h b/stdlib/HashMap.h index 971bd45..27ffb81 100755 --- a/stdlib/HashMap.h +++ b/stdlib/HashMap.h @@ -153,7 +153,7 @@ void hashmap_alloc(HashMap* hm, int32 count, int32 element_size, int32 alignment ); hm->table = (uint16 *) data; - chunk_init(&hm->buf, data + sizeof(uint16) * count, count, element_size, 8); + chunk_init(&hm->buf, data + sizeof(uint16) * count, count, element_size, alignment); } inline @@ -161,21 +161,22 @@ void hashmap_alloc(HashMapRef* hmr, int32 count, int32 data_element_size, int32 { int32 element_size = sizeof(HashEntryInt32Int32); LOG_1("Allocate HashMap for %n elements with %n B per element", {{LOG_DATA_INT32, &count}, {LOG_DATA_INT32, &element_size}}); - byte* data = (byte *) platform_alloc( + byte* data = (byte *) platform_alloc_aligned( count * (sizeof(uint16) + element_size) + CEIL_DIV(count, alignment) * sizeof(hmr->hm.buf.free) - + count * data_element_size + + count * data_element_size, + alignment ); hmr->hm.table = (uint16 *) data; - chunk_init(&hmr->hm.buf, data + sizeof(uint16) * count, count, element_size, 8); - chunk_init(&hmr->data, data + hmr->hm.buf.size, count, data_element_size); + chunk_init(&hmr->hm.buf, data + sizeof(uint16) * count, count, element_size, alignment); + chunk_init(&hmr->data, data + hmr->hm.buf.size, count, data_element_size, alignment); } inline void hashmap_free(HashMap* hm) { - platform_free((void **) &hm->table); + platform_aligned_free((void **) &hm->table); hm->table = NULL; hm->buf.size = 0; @@ -194,7 +195,7 @@ void hashmap_create(HashMap* hm, int32 count, int32 element_size, RingMemory* ri ); hm->table = (uint16 *) data; - chunk_init(&hm->buf, data + sizeof(uint16) * count, count, element_size, 8); + chunk_init(&hm->buf, data + sizeof(uint16) * count, count, element_size, alignment); } // WARNING: element_size = element size + remaining HashEntry data size @@ -209,16 +210,16 @@ void hashmap_create(HashMap* hm, int32 count, int32 element_size, BufferMemory* ); hm->table = (uint16 *) data; - chunk_init(&hm->buf, data + sizeof(uint16) * count, count, element_size, 8); + chunk_init(&hm->buf, data + sizeof(uint16) * count, count, element_size, alignment); } // WARNING: element_size = element size + remaining HashEntry data size inline -void hashmap_create(HashMap* hm, int32 count, int32 element_size, byte* buf) noexcept +void hashmap_create(HashMap* hm, int32 count, int32 element_size, byte* buf, int32 alignment = 64) noexcept { LOG_1("Create HashMap for %n elements with %n B per element", {{LOG_DATA_INT32, &count}, {LOG_DATA_INT32, &element_size}}); hm->table = (uint16 *) buf; - chunk_init(&hm->buf, buf + sizeof(uint16) * count, count, element_size, 8); + chunk_init(&hm->buf, buf + sizeof(uint16) * count, count, element_size, alignment); } inline @@ -256,6 +257,8 @@ void hashmap_insert(HashMap* hm, const char* key, int32 value) noexcept { int32 element = chunk_reserve(&hm->buf, 1); HashEntryInt32* entry = (HashEntryInt32 *) chunk_get_element(&hm->buf, element, true); + ASSERT_SIMPLE(((uintptr_t) entry) % 32 == 0); + // Ensure key length str_move_to_pos(&key, -HASH_MAP_MAX_KEY_LENGTH); str_copy_short(entry->key, key, HASH_MAP_MAX_KEY_LENGTH); @@ -908,7 +911,11 @@ int64 hashmap_dump(const HashMap* hm, byte* data, [[maybe_unused]] int32 steps = } else if (value_size == 8) { *((int64 *) data) = SWAP_ENDIAN_LITTLE(((HashEntryInt64 *) entry)->value); } else { - memcpy(data, entry->value, value_size); + if (entry->value) { + memcpy(data, entry->value, value_size); + } else { + memset(data, 0, value_size); + } } data += value_size; } else { diff --git a/stdlib/PerfectHashMap.h b/stdlib/PerfectHashMap.h index b2e8196..7373803 100755 --- a/stdlib/PerfectHashMap.h +++ b/stdlib/PerfectHashMap.h @@ -171,22 +171,23 @@ PerfectHashMap* perfect_hashmap_prepare(PerfectHashMap* hm, const char* keys, in return NULL; } -void perfect_hashmap_alloc(PerfectHashMap* hm, int32 count, int32 element_size) +void perfect_hashmap_alloc(PerfectHashMap* hm, int32 count, int32 element_size, int32 alignment = 64) { LOG_1("Allocating PerfectHashMap for %n elements with %n B per element", {{LOG_DATA_INT32, &count}, {LOG_DATA_INT32, &element_size}}); hm->map_count = count; hm->entry_size = element_size; - hm->hash_entries = (byte *) platform_alloc(count * element_size); + hm->hash_entries = (byte *) platform_alloc_aligned(count * element_size, alignment); } -void perfect_hashmap_alloc(PerfectHashMapRef* hmr, int32 count, int32 total_data_size) +void perfect_hashmap_alloc(PerfectHashMapRef* hmr, int32 count, int32 total_data_size, int32 alignment = 64) { hmr->hm.entry_size = sizeof(PerfectHashEntryInt32Int32); LOG_1("Allocating PerfectHashMap for %n elements with %n B per element", {{LOG_DATA_INT32, &count}, {LOG_DATA_INT32, &hmr->hm.entry_size}}); hmr->hm.map_count = count; - hmr->hm.hash_entries = (byte *) platform_alloc( + hmr->hm.hash_entries = (byte *) platform_alloc_aligned( count * hmr->hm.entry_size - + total_data_size + + total_data_size, + alignment ); hmr->data_pos = 0; @@ -194,6 +195,14 @@ void perfect_hashmap_alloc(PerfectHashMapRef* hmr, int32 count, int32 total_data hmr->data = hmr->hm.hash_entries + count * hmr->hm.entry_size; } +void perfect_hashmap_free(PerfectHashMap* hm) { + platform_aligned_free((void **) &hm->hash_entries); +} + +void perfect_hashmap_free(PerfectHashMapRef* hmr) { + platform_aligned_free((void **) &hmr->hm.hash_entries); +} + // WARNING: element_size = element size + remaining HashEntry data size void perfect_hashmap_create(PerfectHashMap* hm, int32 count, int32 element_size, BufferMemory* buf) { @@ -203,7 +212,7 @@ void perfect_hashmap_create(PerfectHashMap* hm, int32 count, int32 element_size, hm->hash_entries = buffer_get_memory( buf, count * element_size, - 0, true + 64, true ); } diff --git a/system/SystemInfo.cpp b/system/SystemInfo.cpp index 976ab75..e6d29a2 100755 --- a/system/SystemInfo.cpp +++ b/system/SystemInfo.cpp @@ -9,6 +9,7 @@ #ifndef COMS_SYSTEM_INFO_C #define COMS_SYSTEM_INFO_C +#include #if _WIN32 #include "../platform/win32/SystemInfo.cpp" #elif __linux__ @@ -47,7 +48,7 @@ void system_info_render(char* buf, const SystemInfo* info) { "L3: Size %u Line %u\n" "L4: Size %u Line %u\n" "\n" - "Features: %lld\n" + "Features: %" PRId64 "\n" "\n" "GPU:\n" "==============\n" @@ -78,7 +79,7 @@ void system_info_render(char* buf, const SystemInfo* info) { info->cpu.cache[1].size, (uint32) info->cpu.cache[1].line_size, info->cpu.cache[2].size, (uint32) info->cpu.cache[2].line_size, info->cpu.cache[3].size, (uint32) info->cpu.cache[3].line_size, - (long long) info->cpu.features, + info->cpu.features, info->gpu[0].name, info->gpu[0].vram, info->gpu_count < 2 ? "" : info->gpu[1].name, info->gpu_count < 2 ? 0 : info->gpu[1].vram, info->gpu_count < 3 ? "" : info->gpu[2].name, info->gpu_count < 3 ? 0 : info->gpu[2].vram, diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..7f6ce16 --- /dev/null +++ b/test.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +start_time=$(date +%s.%N) +clear + +EXE_NAME="tests" +DESTINATION_DIR="../build/tests" + +mkdir -p "../build" +mkdir -p "$DESTINATION_DIR" + +# Clean up previous build files +rm -f "$DESTINATION_DIR"/*.pdb 2>/dev/null +rm -f "$DESTINATION_DIR"/*.idb 2>/dev/null + +# Parse command-line arguments +BUILD_TYPE="DEBUG" +BUILD_FLAGS="-g -O0 -Wall -Werror -Wextra -DDEBUG -DDEBUG -ggdb -Werror" +DEBUG_DATA="-g" + +if [ "$1" = "-r" ]; then + BUILD_TYPE="RELEASE" + BUILD_FLAGS="-O3 -DNDEBUG" + DEBUG_DATA="" +elif [ "$1" = "-d" ]; then + BUILD_TYPE="DEBUG" + BUILD_FLAGS="-g -O0 -Wall -Werror -Wextra -DDEBUG -ggdb -Werror" + DEBUG_DATA="-g" +fi + +source ./check_cpu_features.sh + +# Detect CPU features +CPU_FEATURES=$(check_cpu_features) + +# Compile the test program +cd "$DESTINATION_DIR" || exit 1 + +g++ $BUILD_FLAGS -std=c++23 -m64 \ + -DUNICODE -D_UNICODE -D__linux__ \ + -Wno-unused-result \ + -fsanitize=address \ + -fsanitize=undefined \ + ${CPU_FEATURES} \ + -o MainTest \ + ../../cOMS/tests/MainTest.cpp \ + $DEBUG_DATA + +# Check if the compilation was successful +if [ $? -ne 0 ]; then + echo "Compilation failed for MainTest.cpp" + exit 1 +fi + +# Run the compiled executable +./MainTest + +# Calculate runtime +end_time=$(date +%s.%N) +elapsed_time=$(echo "$end_time - $start_time" | bc) + +hours=$(printf "%.0f" $(echo "$elapsed_time / 3600" | bc)) +remaining=$(echo "$elapsed_time % 3600" | bc) +minutes=$(printf "%.0f" $(echo "$remaining / 60" | bc)) +seconds=$(echo "$remaining % 60" | bc) + +# Format the output +printf "Test (incl. build) time %02d:%02d:%05.2f (%.2fs total)\n" \ + $hours $minutes $seconds $elapsed_time \ No newline at end of file diff --git a/tests.bat b/tests.bat index 393e3d9..f853297 100755 --- a/tests.bat +++ b/tests.bat @@ -44,7 +44,7 @@ if "%1"=="-d" ( REM Compile each .cpp file into an executable cl ^ %BUILD_FLAGS% /MT /nologo /Gm- /GR- /EHsc /W4 /wd4201 /wd4706 /wd4324 ^ - /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /std:c++20 ^ + /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /std:c++23 ^ /D WIN32 /D _WINDOWS /D _UNICODE /D UNICODE ^ /D _CRT_SECURE_NO_WARNINGS ^ /Fo"%DESTINATION_DIR%/" /Fe"%DESTINATION_DIR%\MainTest.exe" %DEBUG_DATA% ^ diff --git a/tests/MainTest.cpp b/tests/MainTest.cpp index 5037932..a8ba4c8 100755 --- a/tests/MainTest.cpp +++ b/tests/MainTest.cpp @@ -1,16 +1,18 @@ #define UBER_TEST 1 +#define PERFORMANCE_TEST 0 -#include "math/EvaluatorTest.cpp" +//#include "math/EvaluatorTest.cpp" #include "memory/ChunkMemoryTest.cpp" #include "memory/RingMemoryTest.cpp" #include "stdlib/HashMapTest.cpp" -#include "ui/UILayoutTest.cpp" -#include "ui/UIThemeTest.cpp" +//#include "ui/UILayoutTest.cpp" +//#include "ui/UIThemeTest.cpp" #include "utils/BitUtilsTest.cpp" #include "utils/EndianUtilsTest.cpp" #include "utils/StringUtilsTest.cpp" #include "utils/MathUtilsTest.cpp" #include "utils/UtilsTest.cpp" +#include "utils/TimeUtilsTest.cpp" #ifdef UBER_TEST #ifdef main @@ -21,16 +23,18 @@ int main() { TEST_HEADER(); - MathEvaluatorTest(); + //MathEvaluatorTest(); MemoryChunkMemoryTest(); MemoryRingMemoryTest(); StdlibHashMapTest(); - UIUILayoutTest(); - UIUIThemeTest(); + //UIUILayoutTest(); + //UIUIThemeTest(); UtilsBitUtilsTest(); + UtilsEndianUtilsTest(); UtilsStringUtilsTest(); UtilsMathUtilsTest(); UtilsUtilsTest(); + UtilsTimeUtilsTest(); TEST_FOOTER(); diff --git a/tests/TestFramework.h b/tests/TestFramework.h index 8451652..d44e79f 100755 --- a/tests/TestFramework.h +++ b/tests/TestFramework.h @@ -11,6 +11,7 @@ #include #include +#include #include "../architecture/Intrinsics.h" static char **_test_log; @@ -18,28 +19,31 @@ static int32_t _test_assert_count; static int32_t _test_assert_error_count; static int32_t _test_count = -1; +static int32_t _test_suit_count = 0; static int32_t _test_global_assert_count = 0; static int32_t _test_global_assert_error_count = 0; static int32_t _test_global_count = 0; static int64_t _test_start; +static int64_t _test_total_start; #define TEST_PROFILING_LOOPS 1000 #define TEST_HEADER() \ - int64_t _test_total_start = test_start_time(); \ + _test_total_start = test_start_time(); \ printf("\nStat Tests Assert(OK/NG) Time(ms) Details\n"); \ printf("========================================================================================================================\n") #define TEST_FOOTER() \ printf("========================================================================================================================\n"); \ printf( \ - "%s %5d (%5d/%5d) %8.0f\n\n", \ + "%s %5d (%5d/%5d) %8.2f\n\n", \ _test_global_assert_count ? "[NG]" : "[OK]", \ _test_global_count, \ _test_global_assert_count - _test_global_assert_error_count, \ _test_global_assert_count, \ - test_duration_time(_test_total_start) / 1000000) + test_duration_time(_test_total_start) / 1000000); \ + printf("%d test suits\n\n", _test_suit_count) #ifdef UBER_TEST #define TEST_INIT_HEADER() (void)0 @@ -70,11 +74,7 @@ int64_t test_start_time() double test_duration_time(int64_t start) { - LARGE_INTEGER frequency, end; - QueryPerformanceFrequency(&frequency); - QueryPerformanceCounter(&end); - - return (double)(end.QuadPart - start) * 1e9 / frequency.QuadPart; + return (double)(test_start_time() - start) / frequency.QuadPart; } double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *para) @@ -111,6 +111,7 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p } while (0) #else #include "../platform/linux/ExceptionHandler.h" +#include void test_exception_handler(int signum) { printf("Received signal: %d\n", signum); @@ -120,7 +121,7 @@ void test_exception_handler(int signum) int64_t test_start_time() { - struct timespec start, end; + struct timespec start; clock_gettime(CLOCK_MONOTONIC, &start); return start.tv_sec * 1e9 + start.tv_nsec; @@ -128,14 +129,9 @@ int64_t test_start_time() double test_duration_time(int64_t start) { - LARGE_INTEGER frequency, end; - QueryPerformanceFrequency(&frequency); - QueryPerformanceCounter(&end); - - return (double)(end.tv_sec * 1e9 + end.tv_nsec - start); + return (double) (test_start_time() - start); } -#include double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *para) { struct timespec start, end; @@ -151,6 +147,7 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p #define TEST_INIT(test_count) \ do \ { \ + ++_test_suit_count; \ TEST_INIT_HEADER(); \ setvbuf(stdout, NULL, _IONBF, 0); \ signal(SIGSEGV, test_exception_handler); \ @@ -176,7 +173,7 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p if (_test_assert_error_count) \ { \ printf( \ - "[NG] %5d (%5d/%5d) %8.0f %s\n", \ + "[NG] %5d (%5d/%5d) %8.2f %s\n", \ _test_count, _test_assert_count - _test_assert_error_count, _test_assert_count, test_duration_time(_test_start) / 1000000, __FILE__); \ for (int i = 0; i < _test_assert_error_count; ++i) \ { \ @@ -187,7 +184,7 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p else \ { \ printf( \ - "[OK] %5d (%5d/%5d) %8.0f %s\n", \ + "[OK] %5d (%5d/%5d) %8.2f %s\n", \ _test_count, _test_assert_count - _test_assert_error_count, _test_assert_count, test_duration_time(_test_start) / 1000000, __FILE__); \ } \ fflush(stdout); \ @@ -204,34 +201,62 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p ++_test_global_count; \ func() -#define ASSERT_EQUALS(a, b) \ +#define ASSERT_EQUALS(a, b) \ + do \ + { \ + ++_test_assert_count; \ + ++_test_global_assert_count; \ + if ((a) != (b)) \ + { \ + ++_test_global_assert_error_count; \ + snprintf( \ + _test_log[_test_assert_error_count++], 1024, \ + "%4i: %" PRId64 " != %" PRId64, __LINE__, (int64)(a), (int64)(b)); \ + } \ + } while (0) + +#define ASSERT_GREATER_THAN(a, b) \ do \ { \ ++_test_assert_count; \ ++_test_global_assert_count; \ - if ((a) != (b)) \ + if ((a) <= (b)) \ { \ ++_test_global_assert_error_count; \ snprintf( \ _test_log[_test_assert_error_count++], 1024, \ - "%4i: %lld != %lld", __LINE__, (int64)(a), (int64)(b)); \ + "%4i: %.3f <= %.3f", __LINE__, (float)(a), (float)(b)); \ } \ } while (0) -#define ASSERT_NOT_EQUALS(a, b) \ +#define ASSERT_LESSER_THAN(a, b) \ do \ { \ ++_test_assert_count; \ ++_test_global_assert_count; \ - if ((a) == (b)) \ + if ((a) >= (b)) \ { \ ++_test_global_assert_error_count; \ snprintf( \ _test_log[_test_assert_error_count++], 1024, \ - "%4i: %lld == %lld", __LINE__, (int64)(a), (int64)(b)); \ + "%4i: %.3f >= %.3f", __LINE__, (float)(a), (float)(b)); \ } \ } while (0) +#define ASSERT_NOT_EQUALS(a, b) \ + do \ + { \ + ++_test_assert_count; \ + ++_test_global_assert_count; \ + if ((a) == (b)) \ + { \ + ++_test_global_assert_error_count; \ + snprintf( \ + _test_log[_test_assert_error_count++], 1024, \ + "%4i: %" PRId64 " == %" PRId64, __LINE__, (int64)(a), (int64)(b)); \ + } \ + } while (0) + #define ASSERT_EQUALS_WITH_DELTA(a, b, delta) \ do \ { \ @@ -329,7 +354,7 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p { \ ++_test_global_assert_error_count; \ snprintf(_test_log[_test_global_assert_error_count], 1024, \ - "%4i: mismatch at offset %lld", __LINE__, i); \ + "%4i: mismatch at offset %" PRId64, __LINE__, i); \ break; \ } \ } \ @@ -371,7 +396,7 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p ++_test_global_assert_error_count; \ snprintf( \ _test_log[_test_assert_error_count++], 1024, \ - "%4i: %.2f%% (%s: %llu cycles, %s: %llu cycles)", \ + "%4i: %.2f%% (%s: %" PRIu64 " cycles, %s: %" PRIu64 " cycles)", \ __LINE__, percent_diff + 100.0f, #func1, (uint64_t)cycles_func1, #func2, (uint64_t)cycles_func2); \ } \ ASSERT_TRUE((a && b) || a == b); \ @@ -400,7 +425,7 @@ double test_measure_func_time_ns(void (*func)(volatile void *), volatile void *p ++_test_global_assert_error_count; \ snprintf( \ _test_log[_test_assert_error_count++], 1024, \ - "%4i: %.2f%% (%s: %llu cycles)", \ + "%4i: %.2f%% (%s: %" PRIu64 " cycles)", \ __LINE__, percent_diff + 100.0f, #func, (uint64_t)cycles_func); \ } \ } while (0) diff --git a/tests/utils/EndianUtilsTest.cpp b/tests/utils/EndianUtilsTest.cpp index 323e1ed..e03be88 100755 --- a/tests/utils/EndianUtilsTest.cpp +++ b/tests/utils/EndianUtilsTest.cpp @@ -1,3 +1,4 @@ +#include #include "../TestFramework.h" #include "../../utils/EndianUtils.h" @@ -85,7 +86,7 @@ static void test_endian_swap_uint64() { static void test_endian_swap_int64() { int64 val = 0x123456789ABCDEF0; int64 swapped = endian_swap(val); - ASSERT_EQUALS(0xF0DEBC9A78563412, swapped); + ASSERT_EQUALS((int64) 0xF0DEBC9A78563412, swapped); } static void test_endian_swap_float() { diff --git a/tests/utils/MathUtilsTest.cpp b/tests/utils/MathUtilsTest.cpp index e3b2e00..aaca66c 100755 --- a/tests/utils/MathUtilsTest.cpp +++ b/tests/utils/MathUtilsTest.cpp @@ -136,6 +136,7 @@ static void test_pow_approx_f64() { ASSERT_EQUALS_WITH_DELTA(pow_approx(10.0, 0.5), pow(10.0, 0.5), 0.001); } +#if PERFORMANCE_TEST // Performance tests for f32 (float) approximate functions static void _sin_approx_f32(volatile void* val) { f32* res = (f32*)val; @@ -555,6 +556,7 @@ static void test_pow_approx_performance_f64() { COMPARE_FUNCTION_TEST_TIME(_pow_approx_f64, _pow_f64, 5.0); COMPARE_FUNCTION_TEST_CYCLE(_pow_approx_f64, _pow_f64, 5.0); } +#endif #ifdef UBER_TEST #ifdef main @@ -592,31 +594,33 @@ int main() { TEST_RUN(test_log_approx_f64); TEST_RUN(test_pow_approx_f64); - // Run performance tests for f32 functions - TEST_RUN(test_sin_approx_performance_f32); - TEST_RUN(test_cos_approx_performance_f32); - TEST_RUN(test_tan_approx_performance_f32); - TEST_RUN(test_sqrt_approx_performance_f32); - TEST_RUN(test_asin_approx_performance_f32); - TEST_RUN(test_acos_approx_performance_f32); - TEST_RUN(test_atan_approx_performance_f32); - TEST_RUN(test_rsqrt_approx_performance_f32); - TEST_RUN(test_exp_approx_performance_f32); - TEST_RUN(test_log_approx_performance_f32); - TEST_RUN(test_pow_approx_performance_f32); + #if PERFORMANCE_TEST + // Run performance tests for f32 functions + TEST_RUN(test_sin_approx_performance_f32); + TEST_RUN(test_cos_approx_performance_f32); + TEST_RUN(test_tan_approx_performance_f32); + TEST_RUN(test_sqrt_approx_performance_f32); + TEST_RUN(test_asin_approx_performance_f32); + TEST_RUN(test_acos_approx_performance_f32); + TEST_RUN(test_atan_approx_performance_f32); + TEST_RUN(test_rsqrt_approx_performance_f32); + TEST_RUN(test_exp_approx_performance_f32); + TEST_RUN(test_log_approx_performance_f32); + TEST_RUN(test_pow_approx_performance_f32); - // Run performance tests for f64 functions - TEST_RUN(test_sin_approx_performance_f64); - TEST_RUN(test_cos_approx_performance_f64); - TEST_RUN(test_tan_approx_performance_f64); - TEST_RUN(test_sqrt_approx_performance_f64); - TEST_RUN(test_asin_approx_performance_f64); - TEST_RUN(test_acos_approx_performance_f64); - TEST_RUN(test_atan_approx_performance_f64); - TEST_RUN(test_rsqrt_approx_performance_f64); - TEST_RUN(test_exp_approx_performance_f64); - TEST_RUN(test_log_approx_performance_f64); - TEST_RUN(test_pow_approx_performance_f64); + // Run performance tests for f64 functions + TEST_RUN(test_sin_approx_performance_f64); + TEST_RUN(test_cos_approx_performance_f64); + TEST_RUN(test_tan_approx_performance_f64); + TEST_RUN(test_sqrt_approx_performance_f64); + TEST_RUN(test_asin_approx_performance_f64); + TEST_RUN(test_acos_approx_performance_f64); + TEST_RUN(test_atan_approx_performance_f64); + TEST_RUN(test_rsqrt_approx_performance_f64); + TEST_RUN(test_exp_approx_performance_f64); + TEST_RUN(test_log_approx_performance_f64); + TEST_RUN(test_pow_approx_performance_f64); + #endif TEST_FINALIZE(); diff --git a/tests/utils/StringUtilsTest.cpp b/tests/utils/StringUtilsTest.cpp index 3692436..5a72a88 100755 --- a/tests/utils/StringUtilsTest.cpp +++ b/tests/utils/StringUtilsTest.cpp @@ -1,3 +1,5 @@ +#include +#include #include "../TestFramework.h" #include "../../utils/StringUtils.h" #include "../../utils/EndianUtils.h" @@ -83,6 +85,7 @@ static void test_str_length() ASSERT_EQUALS(str_length("2asdf dw"), 8); } +#if PERFORMANCE_TEST static void _str_length(volatile void* val) { volatile int64* res = (volatile int64 *) val; @@ -107,7 +110,9 @@ static void test_str_length_performance() { COMPARE_FUNCTION_TEST_TIME(_str_length, _strlen, 5.0); COMPARE_FUNCTION_TEST_CYCLE(_str_length, _strlen, 5.0); } +#endif +#if PERFORMANCE_TEST static void _str_is_alphanum(volatile void* val) { bool* res = (bool *) val; srand(0); @@ -136,6 +141,7 @@ static void test_str_is_alphanum_performance() { COMPARE_FUNCTION_TEST_TIME(_str_is_alphanum, _isalnum, 5.0); COMPARE_FUNCTION_TEST_CYCLE(_str_is_alphanum, _isalnum, 5.0); } +#endif static void test_sprintf_fast() { @@ -144,6 +150,7 @@ static void test_sprintf_fast() ASSERT_TRUE(strcmp(buffer, "This 1337 is a test with 3.00000 values") == 0); } +#if PERFORMANCE_TEST static void _sprintf_fast(volatile void* val) { volatile bool* res = (volatile bool *) val; @@ -164,6 +171,7 @@ static void test_sprintf_fast_performance() { COMPARE_FUNCTION_TEST_TIME(_sprintf_fast, _sprintf, 5.0); COMPARE_FUNCTION_TEST_CYCLE(_sprintf_fast, _sprintf, 5.0); } +#endif static void test_str_to_float() { @@ -179,8 +187,6 @@ static void test_str_to_float() #define main UtilsStringUtilsTest #endif -#include - int main() { TEST_INIT(100); @@ -196,9 +202,11 @@ int main() { TEST_RUN(test_str_length); TEST_RUN(test_str_to_float); - TEST_RUN(test_str_length_performance); - TEST_RUN(test_str_is_alphanum_performance); - TEST_RUN(test_sprintf_fast_performance); + #if PERFORMANCE_TEST + TEST_RUN(test_str_length_performance); + TEST_RUN(test_str_is_alphanum_performance); + TEST_RUN(test_sprintf_fast_performance); + #endif TEST_FINALIZE(); diff --git a/tests/utils/TimeUtilsTest.cpp b/tests/utils/TimeUtilsTest.cpp new file mode 100644 index 0000000..8b7c378 --- /dev/null +++ b/tests/utils/TimeUtilsTest.cpp @@ -0,0 +1,34 @@ +#include +#include "../TestFramework.h" +#include "../../utils/TimeUtils.h" + +static void test_time_index() { + ASSERT_GREATER_THAN(time_index(), 0.0f); +} + +static void test_system_time() { + ASSERT_GREATER_THAN(system_time(), 0.0f); +} + +static void test_time_mu() { + ASSERT_GREATER_THAN(time_mu(), 0.0f); +} + +#ifdef UBER_TEST + #ifdef main + #undef main + #endif + #define main UtilsTimeUtilsTest +#endif + +int main() { + TEST_INIT(10); + + TEST_RUN(test_time_index); + TEST_RUN(test_system_time); + TEST_RUN(test_time_mu); + + TEST_FINALIZE(); + + return 0; +} diff --git a/tests/utils/UtilsTest.cpp b/tests/utils/UtilsTest.cpp index 7b8f04f..799ced6 100755 --- a/tests/utils/UtilsTest.cpp +++ b/tests/utils/UtilsTest.cpp @@ -5,7 +5,6 @@ static void test_is_equal() { uint8_t region1[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; uint8_t region2[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; uint8_t region3[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09}; - uint8_t region4[] = {0x01, 0x02, 0x03, 0x04}; // Test equal regions ASSERT_TRUE(is_equal(region1, region2, sizeof(region1))); @@ -54,6 +53,7 @@ static void test_is_empty() { ASSERT_TRUE(is_empty(region1, 0)); } +#if PERFORMANCE_TEST static void _is_equal(volatile void* val) { volatile bool* res = (volatile bool *) val; @@ -124,6 +124,7 @@ static void test_is_empty_performance() { COMPARE_FUNCTION_TEST_TIME(_is_empty2, _memcmp_empty2, 10.0); COMPARE_FUNCTION_TEST_CYCLE(_is_empty2, _memcmp_empty2, 10.0); } +#endif #ifdef UBER_TEST #ifdef main @@ -138,8 +139,10 @@ int main() { TEST_RUN(test_is_equal); TEST_RUN(test_is_empty); - TEST_RUN(test_is_equal_performance); - TEST_RUN(test_is_empty_performance); + #if PERFORMANCE_TEST + TEST_RUN(test_is_equal_performance); + TEST_RUN(test_is_empty_performance); + #endif TEST_FINALIZE(); diff --git a/tests_iter.bat b/tests_iter.bat index 6a504d6..8e1b418 100755 --- a/tests_iter.bat +++ b/tests_iter.bat @@ -48,7 +48,7 @@ for /R %%f in (*Test.cpp) do ( REM Compile each .cpp file into an executable cl ^ %BUILD_FLAGS% /MT /nologo /Gm- /GR- /EHsc /W4 /wd4201 /wd4706 /wd4324 ^ - /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /std:c++20 ^ + /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /std:c++23 ^ /D WIN32 /D _WINDOWS /D _UNICODE /D UNICODE ^ /D _CRT_SECURE_NO_WARNINGS ^ /Fo"%DESTINATION_DIR%/" /Fe"%DESTINATION_DIR%\%BASENAME%.exe" %DEBUG_DATA% ^ diff --git a/tests_iter.sh b/tests_iter.sh new file mode 100755 index 0000000..80d2c9b --- /dev/null +++ b/tests_iter.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +clear + +EXE_NAME="tests" +DESTINATION_DIR="../build/tests" + +# Create build directories if they don't exist +mkdir -p "../build" +mkdir -p "$DESTINATION_DIR" + +# Clean up previous build files +rm -f "$DESTINATION_DIR"/*.pdb 2>/dev/null +rm -f "$DESTINATION_DIR"/*.idb 2>/dev/null + +# Default build configuration +BUILD_TYPE="DEBUG" +BUILD_FLAGS="-g -O0 -Wall -Werror -Wextra -DDEBUG -ggdb -Werror" +DEBUG_DATA="-g" + +# Parse command-line arguments +if [ "$1" = "-r" ]; then + BUILD_TYPE="RELEASE" + BUILD_FLAGS="-O3 -DNDEBUG" + DEBUG_DATA="" +elif [ "$1" = "-d" ]; then + BUILD_TYPE="DEBUG" + BUILD_FLAGS="-g -O0 -Wall -Werror -Wextra -DDEBUG -ggdb -Werror" + DEBUG_DATA="-g" +fi + +source ./check_cpu_features.sh + +# Detect CPU features +CPU_FEATURES=$(check_cpu_features) + +# Find all *Test.cpp files in the ./ directory and subdirectories +find ./ -name "*Test.cpp" | while read -r test_file; do + # Get the base name of the file without extension + basename=$(basename "$test_file" .cpp) + + echo "Compiling $test_file..." + + # Compile each test file + g++ $BUILD_FLAGS -std=c++23 -m64 \ + -DUNICODE -D_UNICODE -D__linux__ \ + -Wno-unused-result \ + -fsanitize=address \ + -fsanitize=undefined \ + ${CPU_FEATURES} \ + -o "$DESTINATION_DIR/$basename" \ + "$test_file" \ + $DEBUG_DATA + + # Check if compilation was successful + if [ $? -ne 0 ]; then + echo "Compilation failed for $test_file" + exit 1 + fi + + # Run the compiled test + echo "Running $basename..." + "$DESTINATION_DIR/$basename" + + # Check if test ran successfully + if [ $? -ne 0 ]; then + echo "Test $basename failed" + exit 1 + fi +done diff --git a/thread/Thread.h b/thread/Thread.h index 39c4745..f68409a 100755 --- a/thread/Thread.h +++ b/thread/Thread.h @@ -12,6 +12,7 @@ #include #include "../stdlib/Types.h" #include "../log/Log.h" +#include "../log/Stats.h" #include "Atomic.h" #if _WIN32 @@ -26,14 +27,21 @@ void thread_create(Worker* worker, ThreadJobFunc routine, void* arg) { LOG_1("Thread starting"); + coms_pthread_create(&worker->thread, NULL, routine, arg); + + LOG_INCREMENT(DEBUG_COUNTER_THREAD); + LOG_2("%d threads running", {{LOG_DATA_INT64, (void *) &_stats_counter[DEBUG_COUNTER_THREAD]}}); } void thread_stop(Worker* worker) { atomic_set_release(&worker->state, 0); coms_pthread_join(worker->thread, NULL); + LOG_1("Thread ended"); + LOG_DECREMENT(DEBUG_COUNTER_THREAD); + LOG_2("%d threads running", {{LOG_DATA_INT64, (void *) &_stats_counter[DEBUG_COUNTER_THREAD]}}); } #endif \ No newline at end of file diff --git a/thread/ThreadPool.h b/thread/ThreadPool.h index 5fe288e..5334d37 100755 --- a/thread/ThreadPool.h +++ b/thread/ThreadPool.h @@ -16,6 +16,7 @@ #include "Thread.h" #include "Atomic.h" #include "ThreadJob.h" +#include "../log/DebugContainer.h" struct ThreadPool { // This is not a threaded queue since we want to handle the mutex in here, not in the queue for finer control @@ -25,29 +26,50 @@ struct ThreadPool { mutex_cond work_cond; mutex_cond working_cond; + // By design the working_cnt is <= thread_cnt alignas(4) atomic_32 int32 working_cnt; alignas(4) atomic_32 int32 thread_cnt; int32 size; int32 element_size; + + // 0 = down, 1 = shutting down, 2 = running alignas(4) atomic_32 int32 state; alignas(4) atomic_32 int32 id_counter; + + DebugContainer* debug_container; }; -// @performance Can we optimize this? This is a critical function +// @performance Can we optimize this? This is a critical function. +// If we have a small worker the "spinup"/"re-activation" time is from utmost importance static THREAD_RETURN thread_pool_worker(void* arg) { ThreadPool* pool = (ThreadPool *) arg; + + if (pool->debug_container) { + _log_fp = pool->debug_container->log_fp; + _log_memory = pool->debug_container->log_memory; + _dmc = pool->debug_container->dmc; + _perf_stats = pool->debug_container->perf_stats; + _perf_active = pool->debug_container->perf_active; + _stats_counter = pool->debug_container->stats_counter; + *_perf_active = *pool->debug_container->perf_active; + } + + // @bug Why doesn't this work? There must be some threading issue + LOG_2("Thread pool worker starting up"); + LOG_INCREMENT(DEBUG_COUNTER_THREAD); + PoolWorker* work; while (true) { mutex_lock(&pool->work_mutex); - while (queue_is_empty(&pool->work_queue) && !pool->state) { + while (pool->state > 1 && queue_is_empty(&pool->work_queue)) { coms_pthread_cond_wait(&pool->work_cond, &pool->work_mutex); } - if (pool->state == 1) { + if (pool->state < 2) { mutex_unlock(&pool->work_mutex); break; @@ -55,7 +77,8 @@ THREAD_RETURN thread_pool_worker(void* arg) // We define a queue element as free based on it's id // So even if we "keep" it in the queue the pool will not overwrite it as long as the id > 0 (see pool_add) - // This is only a ThreadPool specific queue behavior to avoid additional copies + // This is only a ThreadPool specific queue behavior to avoid additional memory copy + // @bug this needs to be a threaded queue work = (PoolWorker *) queue_dequeue_keep(&pool->work_queue); mutex_unlock(&pool->work_mutex); @@ -63,7 +86,7 @@ THREAD_RETURN thread_pool_worker(void* arg) continue; } - atomic_increment_relaxed(&pool->working_cnt); + atomic_increment_release(&pool->working_cnt); atomic_set_release(&work->state, 2); LOG_2("ThreadPool worker started"); work->func(work); @@ -79,15 +102,21 @@ THREAD_RETURN thread_pool_worker(void* arg) atomic_set_release(&work->id, 0); } - atomic_decrement_relaxed(&pool->working_cnt); + atomic_decrement_release(&pool->working_cnt); - if (atomic_get_relaxed(&pool->state) == 0 && atomic_get_relaxed(&pool->working_cnt) == 0) { + // Signal that we ran out of work (maybe the main thread needs this info) + // This is not required for the thread pool itself but maybe some other part of the main thread wants to know + if (atomic_get_relaxed(&pool->working_cnt) == 0) { coms_pthread_cond_signal(&pool->working_cond); } } + // We tell the thread pool taht this worker thread is shutting down + atomic_decrement_release(&pool->thread_cnt); coms_pthread_cond_signal(&pool->working_cond); - atomic_decrement_relaxed(&pool->thread_cnt); + + LOG_2("Thread pool worker shutting down"); + LOG_DECREMENT(DEBUG_COUNTER_THREAD); return (THREAD_RETURN) NULL; } @@ -119,11 +148,15 @@ void thread_pool_alloc( coms_pthread_cond_init(&pool->work_cond, NULL); coms_pthread_cond_init(&pool->working_cond, NULL); + pool->state = 2; + coms_pthread_t thread; for (pool->size = 0; pool->size < thread_count; ++pool->size) { coms_pthread_create(&thread, NULL, thread_pool_worker, pool); coms_pthread_detach(thread); } + + LOG_2("%d threads running", {{LOG_DATA_INT64, (void *) &_stats_counter[DEBUG_COUNTER_THREAD]}}); } void thread_pool_create( @@ -154,17 +187,23 @@ void thread_pool_create( coms_pthread_cond_init(&pool->work_cond, NULL); coms_pthread_cond_init(&pool->working_cond, NULL); + pool->state = 2; + coms_pthread_t thread; for (pool->size = 0; pool->size < thread_count; ++pool->size) { coms_pthread_create(&thread, NULL, thread_pool_worker, pool); coms_pthread_detach(thread); } + + LOG_2("%d threads running", {{LOG_DATA_INT64, (void *) &_stats_counter[DEBUG_COUNTER_THREAD]}}); } void thread_pool_wait(ThreadPool* pool) { mutex_lock(&pool->work_mutex); - while ((!pool->state && pool->working_cnt != 0) || (pool->state && pool->thread_cnt != 0)) { + // @question We removed some state checks here, not sure if they were really necessary + // remove this comment once we are sure everything works as expected + while (pool->working_cnt != 0 || pool->thread_cnt != 0) { coms_pthread_cond_wait(&pool->working_cond, &pool->work_mutex); } mutex_unlock(&pool->work_mutex); @@ -184,6 +223,9 @@ void thread_pool_destroy(ThreadPool* pool) mutex_destroy(&pool->work_mutex); coms_pthread_cond_destroy(&pool->work_cond); coms_pthread_cond_destroy(&pool->working_cond); + + // This sets the state to "down" + atomic_set_release(&pool->state, 0); } PoolWorker* thread_pool_add_work(ThreadPool* pool, const PoolWorker* job) diff --git a/ui/UILayout.cpp b/ui/UILayout.cpp index 7576943..2ed780d 100755 --- a/ui/UILayout.cpp +++ b/ui/UILayout.cpp @@ -89,7 +89,7 @@ void ui_layout_assign_children( break; } - str_copy_move_until(&pos, block_name, ":"); + str_copy_move_until(block_name, &pos, ":"); str_move_past(&pos, '\n'); // Children array (located after the UIElement) @@ -182,8 +182,8 @@ void layout_from_file_txt( continue; } - str_copy_move_until(&pos, block_name, ":"); ++pos; - str_copy_move_until(&pos, block_type, " \r\n"); + str_copy_move_until(block_name, &pos, ":"); ++pos; + str_copy_move_until(block_type, &pos, " \r\n"); str_move_past(&pos, '\n'); // Insert new element @@ -235,7 +235,7 @@ void layout_from_file_txt( continue; } - str_copy_move_until(&pos, block_name, ":"); + str_copy_move_until(block_name, &pos, ":"); str_move_past(&pos, '\n'); UIElement* element = (UIElement *) (layout->data + ((HashEntryInt32 *) hashmap_get_entry(&layout->hash_map, block_name))->value); @@ -268,7 +268,7 @@ void layout_from_file_txt( continue; } - str_copy_move_until(&pos, block_name, ":"); + str_copy_move_until(block_name, &pos, ":"); str_move_past(&pos, '\n'); root_children[child++] = ((HashEntryInt32 *) hashmap_get_entry(&layout->hash_map, block_name))->value; } diff --git a/ui/UITheme.h b/ui/UITheme.h index e3a0e2f..800d5bd 100755 --- a/ui/UITheme.h +++ b/ui/UITheme.h @@ -137,7 +137,7 @@ void theme_from_file_txt( // Handle new group ///////////////////////////////////////////////////////// if (!block_open) { - str_copy_move_until(&pos, block_name, " \r\n"); + str_copy_move_until(block_name, &pos, " \r\n"); // All blocks need to start with #. In the past this wasn't the case and may not be in the future. This is why we keep this if here. if (*block_name == '#' || *block_name == '.') { @@ -164,7 +164,7 @@ void theme_from_file_txt( // Handle new attribute for previously found group ///////////////////////////////////////////////////////// - str_copy_move_until(&pos, attribute_name, " :\n"); + str_copy_move_until(attribute_name, &pos, " :\n"); // Skip any white spaces or other delimeter str_skip_list(&pos, " \t:", sizeof(" \t:") - 1); diff --git a/ui/attribute/UIAttribute.h b/ui/attribute/UIAttribute.h index 4dd24de..bce010e 100755 --- a/ui/attribute/UIAttribute.h +++ b/ui/attribute/UIAttribute.h @@ -209,7 +209,7 @@ void ui_attribute_parse_value(UIAttribute* attr, const char* attribute_name, con attr->attribute_id = (UIAttributeType) ui_attribute_type_to_id(attribute_name); char value[64]; - str_copy_until(pos, value, "\r\n"); + str_copy_until(value, pos, "\r\n"); if (str_is_integer(value)) { attr->value_int = (int32) str_to_int(value); @@ -222,7 +222,7 @@ void ui_attribute_parse_value(UIAttribute* attr, const char* attribute_name, con hexstr_to_rgba(&attr->value_v4_f32, pos); attr->datatype = UI_ATTRIBUTE_DATA_TYPE_V4_F32; } else { - str_copy_until(value, attr->value_str, "\r\n"); + str_copy_until(attr->value_str, value, "\r\n"); attr->datatype = UI_ATTRIBUTE_DATA_TYPE_STR; } } diff --git a/utils/BitUtils.h b/utils/BitUtils.h index 2f82fa4..695078d 100755 --- a/utils/BitUtils.h +++ b/utils/BitUtils.h @@ -23,7 +23,7 @@ #define BIT_SET_L2R(num, pos, bits) ((num) | (1U << ((bits - 1) - (pos)))) #define BIT_UNSET_L2R(num, pos, bits) ((num) & ~(1U << ((bits - 1) - (pos)))) #define BIT_FLIP_L2R(num, pos, bits) ((num) ^ (1U << ((bits - 1) - (pos)))) -#define BIT_SET_TO_L2R(num, pos, x, bits) ((num) & ~((uint32) 1 << ((bits - 1) - (pos))) | ((uint32) (x) << ((bits - 1) - (pos)))) +#define BIT_SET_TO_L2R(num, pos, x, bits) (((num) & ~((uint32_t)(1) << ((bits) - 1 - (pos)))) | (((uint32_t)(x) & 1U) << ((bits) - 1 - (pos)))) #define BITS_GET_8_L2R(num, pos, to_read) (((num) >> (8 - (pos) - (to_read))) & ((1U << (to_read)) - 1)) #define BITS_GET_16_L2R(num, pos, to_read) (((num) >> (16 - (pos) - (to_read))) & ((1U << (to_read)) - 1)) #define BITS_GET_32_L2R(num, pos, to_read) (((num) >> (32 - (pos) - (to_read))) & ((1U << (to_read)) - 1)) @@ -41,7 +41,7 @@ #define BIT_SET_R2L(num, pos) ((num) | ((uint32) 1 << (pos))) #define BIT_UNSET_R2L(num, pos) ((num) & ~((uint32) 1 << (pos))) #define BIT_FLIP_R2L(num, pos) ((num) ^ ((uint32) 1 << (pos))) -#define BIT_SET_TO_R2L(num, pos, x) ((num) & ~((uint32) 1 << (pos)) | ((uint32) (x) << (pos))) +#define BIT_SET_TO_R2L(num, pos, x) (((num) & ~((uint32_t)(1) << (pos))) | ((uint32_t)(x) << (pos))) // @performance Try to use this version over the L2R version for performance reasons #define BITS_GET_8_R2L(num, pos, to_read) (((num) >> (pos)) & ((1U << (to_read)) - 1)) #define BITS_GET_16_R2L(num, pos, to_read) (((num) >> (pos)) & ((1U << (to_read)) - 1)) diff --git a/utils/StringUtils.h b/utils/StringUtils.h index 8db2124..41b2241 100755 --- a/utils/StringUtils.h +++ b/utils/StringUtils.h @@ -941,7 +941,7 @@ void str_copy_long(char* __restrict dest, const char* __restrict src) noexcept } inline -void str_copy_move_until(const char** __restrict src, char* __restrict dest, char delim) noexcept +void str_copy_move_until(char* __restrict dest, const char* __restrict* __restrict src, char delim) noexcept { while (**src != delim && **src != '\0') { *dest++ = **src; @@ -952,7 +952,7 @@ void str_copy_move_until(const char** __restrict src, char* __restrict dest, cha } inline -void str_copy_move_until(const char** __restrict src, char* __restrict dest, const char* __restrict delim) noexcept +void str_copy_move_until(char* __restrict dest, const char* __restrict* __restrict src, const char* __restrict delim) noexcept { size_t len = str_length(delim); @@ -1055,7 +1055,7 @@ str_concat_append(char* dst, size_t dst_length, const char* src) noexcept inline int64 str_concat_new( - char* dst, + char* __restrict dst, const char* src1, size_t src1_length, const char* src2, size_t src2_length ) noexcept { @@ -1092,7 +1092,7 @@ void str_concat_append( } inline void -str_concat_new(char* dst, const char* src, int64 data) noexcept +str_concat_new(char* __restrict dst, const char* __restrict src, int64 data) noexcept { size_t src_len = str_length(src); memcpy(dst, src, src_len); @@ -1115,7 +1115,7 @@ void str_remove(char* __restrict dst, size_t remove_pos, size_t remove_length) n } inline -char* strtok(char* str, const char* __restrict delim, char* *key) noexcept { +char* strtok(char* str, const char* __restrict delim, char** key) noexcept { char* result; if (str == NULL) { str = *key; @@ -1139,7 +1139,7 @@ char* strtok(char* str, const char* __restrict delim, char* *key) noexcept { } inline constexpr -bool str_contains(const char* haystack, const char* needle) noexcept +bool str_contains(const char* __restrict haystack, const char* __restrict needle) noexcept { // @performance would it make sense to only check until haystack - strlen(needle)? // I'm not sure the strlen overhead is worth it @@ -1163,7 +1163,7 @@ bool str_contains(const char* haystack, const char* needle) noexcept } inline constexpr -bool str_contains(const char* haystack, const char* needle, size_t length) noexcept +bool str_contains(const char* __restrict haystack, const char* __restrict needle, size_t length) noexcept { while (*haystack != '\0' && length > 0) { const char* p1 = haystack; @@ -1320,7 +1320,7 @@ int32 str_compare_caseless(const char* str1, const char* str2, size_t n) noexcep } inline constexpr -bool str_ends_with(const char* str, const char* suffix) noexcept { +bool str_ends_with(const char* __restrict str, const char* __restrict suffix) noexcept { if (!str || !suffix) { return false; } @@ -1434,7 +1434,7 @@ void str_move_to_pos(const char** str, int32 pos) noexcept } inline -void str_move_past(const char** str, char delim) noexcept +void str_move_past(const char* __restrict* __restrict str, char delim) noexcept { while (**str != delim && **str != '\0') { ++(*str); @@ -1549,7 +1549,7 @@ void str_skip_until_list(const char** __restrict str, const char* __restrict del } inline -void hexstr_to_rgba(v4_f32* rgba, const char* hex) noexcept +void hexstr_to_rgba(v4_f32* __restrict rgba, const char* __restrict hex) noexcept { if (*hex == '#') { ++hex; @@ -1886,7 +1886,7 @@ void sprintf_fast(char* __restrict buffer, int32 buffer_length, const char* __re } // There are situations where you only want to replace a certain amount of % -void sprintf_fast_iter(char* buffer, const char* format, ...) noexcept { +void sprintf_fast_iter(char* __restrict buffer, const char* __restrict format, ...) noexcept { va_list args; va_start(args, format); diff --git a/utils/TestUtils.h b/utils/TestUtils.h index 47216f7..2430bbe 100755 --- a/utils/TestUtils.h +++ b/utils/TestUtils.h @@ -10,13 +10,13 @@ #define COMS_UTILS_TEST_UTILS_H #if DEBUG - #define ASSERT_SIMPLE(a) if (!(a)) { \ + #define ASSERT_SIMPLE(a) if (!(a)) { \ /* cppcheck-suppress nullPointer */ \ - *(volatile int *)0 = 0; \ + *(volatile int *)0 = 0; \ } - #define ASSERT_SIMPLE_CONST(a) if constexpr (!(a)) { \ - /* cppcheck-suppress nullPointer */ \ - *(volatile int *)0 = 0; \ + #define ASSERT_SIMPLE_CONST(a) if constexpr (!(a)) { \ + /* cppcheck-suppress nullPointer */ \ + *(volatile int *)0 = 0; \ } #if DEBUG_STRICT