From 957a3572c3eda9d54c05a9c36888ea82b3fcb828 Mon Sep 17 00:00:00 2001 From: dev0 Date: Mon, 15 Dec 2025 23:51:06 +0530 Subject: [PATCH] Fixes --- CMakeLists.txt | 3 - README.md | 39 +++++ Src/IACore/imp/cpp/DataOps.cpp | 4 +- Src/IACore/imp/cpp/IPC.cpp | 4 +- Src/IACore/imp/cpp/ProcessOps.cpp | 5 +- Src/IACore/inc/IACore/DynamicLib.hpp | 201 +++++++++++++------------- Src/IACore/inc/IACore/Environment.hpp | 27 +--- Src/IACore/inc/IACore/IATest.hpp | 26 +--- Src/IACore/inc/IACore/IPC.hpp | 2 +- Src/IACore/inc/IACore/PCH.hpp | 7 +- Src/IACore/inc/IACore/SocketOps.hpp | 2 +- Src/IACore/inc/IACore/Utils.hpp | 44 ++---- Tests/Unit/CMakeLists.txt | 4 +- Tests/Unit/Environment.cpp | 3 - Tests/Unit/ProcessOps.cpp | 12 +- Tests/Unit/RingBuffer.cpp | 6 +- Tests/Unit/StreamReader.cpp | 7 +- Tests/Unit/Utils.cpp | 10 +- 18 files changed, 175 insertions(+), 231 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f122d6e..9bc39e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,8 +23,6 @@ message(STATUS "Configured IACore for Multi-Config (Debug/Release rules generate message(STATUS "Detected Compiler ID: ${CMAKE_CXX_COMPILER_ID}") # Check if the compiler is MSVC (cl.exe), but allow Clang acting like MSVC (clang-cl) if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - # Note: clang-cl usually reports itself as "Clang" in newer CMake versions, - # but if it reports MSVC with a Clang simulation, we want to allow it. if (NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") message(FATAL_ERROR "\n\n" @@ -40,7 +38,6 @@ endif() if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") add_compile_options( -Wall -Wextra -Wpedantic - # Suppress warning for the statement expression macro if -pedantic is on -Wno-language-extension-token ) endif() diff --git a/README.md b/README.md index bdcdcb8..6736546 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,45 @@ if (res) { } ``` +## ๐Ÿค Contributing + +We welcome contributions from the community! However, to maintain the architectural integrity and licensing flexibility of the project, we have specific guidelines for Pull Requests. + +### What we accept immediately: +* **๐Ÿ“š Documentation:** Improvements to comments, the README, or external docs. +* **๐Ÿงช Tests:** New unit tests (in `Tests/`) to improve coverage or reproduce bugs. +* **๐Ÿ’ก Examples:** New usage examples or sample projects. +* **๐Ÿ› Bug Reports:** detailed issues describing reproduction steps are highly valued. + +### Core Library Policy (`Src/` Directory) +Currently, **we are not accepting Pull Requests that modify the core source code (`Src/`)**. + +**Why?** +IACore is a dual-licensed product. To offer commercial licenses to proprietary software vendors in the future, **IASoft (PVT) LTD.** must retain 100% copyright ownership of the core library. + +We are currently establishing a **Contributor License Agreement (CLA)** process. Once that is in place, we will open the core library for contributions, provided the contributor signs the CLA to assign copyright or grant an unlimited license to the project maintainers. + +If you find a critical bug in `Src/`, please open an **Issue** rather than a PR, and the core team will implement the fix to ensure legal compliance. + +## ๐Ÿค Credits & Acknowledgements +**IACore** is an architectural effort by **IASoft (PVT) LTD.**, designed and maintained by its lead developers. + +While the core architecture, API design, and final logic verification were strictly human-led, this project leveraged **Google Gemini 3** as an advanced AI thought partner to accelerate development. + +#### AI contributions include: + +* **Boilerplate Generation:** Rapid prototyping of standard C++20 structures and repetitive implementations. + +* **Research & Selection:** Analyzing and comparing open-source libraries (e.g., `glaze` vs. `nlohmann`, `zstd` vs. `lz4`) to select the best-in-class dependencies. + +* **Design Analysis:** Researching and contrasting design patterns (e.g., SPSC Ring Buffers vs. Mutex Queues) for optimal performance. + +* **Documentation:** Drafting comprehensive documentation and this README. + +* **Code Review:** Automated logic checking and static analysis support. + +**Methodology:** Every line of code, whether written by hand or generated by AI, has been manually reviewed, tested, and verified by human engineers to ensure zero hallucinations and maximum reliability. *Trust, but verify.* + ## โš–๏ธ License This project is licensed under the GNU General Public License v3 (GPLv3). \ No newline at end of file diff --git a/Src/IACore/imp/cpp/DataOps.cpp b/Src/IACore/imp/cpp/DataOps.cpp index 040e278..c101bd1 100644 --- a/Src/IACore/imp/cpp/DataOps.cpp +++ b/Src/IACore/imp/cpp/DataOps.cpp @@ -111,7 +111,6 @@ namespace IACore // Check for ZLIB Magic Number (starts with 0x78) // 0x78 = Deflate compression with 32k window size - // Valid second bytes: 0x01 (Fastest), 0x9C (Default), 0xDA (Best) if (data[0] == 0x78 && (data[1] == 0x01 || data[1] == 0x9C || data[1] == 0xDA)) return CompressionType::Zlib; @@ -134,7 +133,6 @@ namespace IACore Vector outBuffer; // Start with 2x input size. - // Small packets compress well, so maybe 4x for very small inputs. size_t guessSize = data.size() < 1024 ? data.size() * 4 : data.size() * 2; outBuffer.resize(guessSize); @@ -282,7 +280,7 @@ namespace IACore zs.zfree = Z_NULL; zs.opaque = Z_NULL; - // WindowBits = 15 + 16 (31) -> This forces GZIP encoding + // WindowBits = 15 + 16 (31) = Enforce GZIP encoding // MemLevel = 8 (default) // Strategy = Z_DEFAULT_STRATEGY if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK) diff --git a/Src/IACore/imp/cpp/IPC.cpp b/Src/IACore/imp/cpp/IPC.cpp index 71f6fa2..d5bf305 100644 --- a/Src/IACore/imp/cpp/IPC.cpp +++ b/Src/IACore/imp/cpp/IPC.cpp @@ -182,7 +182,7 @@ namespace IACore IPC_Manager::IPC_Manager() { - // SocketOps is smart enough to handle multiple inits + // SocketOps is smart enough to track multiple inits SocketOps::Initialize(); m_recieveBuffer.resize(UINT16_MAX + 1); @@ -208,7 +208,7 @@ namespace IACore } m_pendingSessions.clear(); - // SocketOps is smart enough to handle multiple terminates + // SocketOps is smart enough to track multiple terminates SocketOps::Terminate(); } diff --git a/Src/IACore/imp/cpp/ProcessOps.cpp b/Src/IACore/imp/cpp/ProcessOps.cpp index 172c851..6e9b1dd 100644 --- a/Src/IACore/imp/cpp/ProcessOps.cpp +++ b/Src/IACore/imp/cpp/ProcessOps.cpp @@ -136,7 +136,7 @@ namespace IACore BOOL success = CreateProcessA(NULL, commandLine.data(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); - // Important: Close write end in parent, otherwise ReadFile never returns EOF! + // Close write end in parent, otherwise ReadFile never returns EOF! CloseHandle(hWrite); if (!success) @@ -196,7 +196,6 @@ namespace IACore dup2(pipefd[1], STDERR_FILENO); close(pipefd[1]); - // --- ARGUMENT PARSING START --- std::vector argStorage; // To keep strings alive std::vector argv; @@ -258,7 +257,6 @@ namespace IACore argv.push_back(s.data()); } argv.push_back(nullptr); - // --- ARGUMENT PARSING END --- execvp(argv[0], argv.data()); _exit(127); @@ -312,7 +310,6 @@ namespace IACore } else { - // Zero copy optimization for pure lines in one chunk if (i > start) Callback(StringView(data + start, i - start)); } diff --git a/Src/IACore/inc/IACore/DynamicLib.hpp b/Src/IACore/inc/IACore/DynamicLib.hpp index fe4194a..e09a2f7 100644 --- a/Src/IACore/inc/IACore/DynamicLib.hpp +++ b/Src/IACore/inc/IACore/DynamicLib.hpp @@ -1,16 +1,16 @@ -// IACore-OSS; The Core Library for All IA Open Source Projects +// IACore-OSS; The Core Library for All IA Open Source Projects // Copyright (C) 2025 IAS (ias@iasoft.dev) -// +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . @@ -19,30 +19,31 @@ #include #if IA_PLATFORM_WINDOWS - #include - #include +# include +# include #else - #include +# include #endif -namespace IACore { +namespace IACore +{ - class DynamicLib { - public: - // --------------------------------------------------------------------- - // Constructors / Destructors (Move Only) - // --------------------------------------------------------------------- - - DynamicLib() : m_handle(nullptr) {} + class DynamicLib + { + public: + DynamicLib() : m_handle(nullptr) + { + } - // Move Constructor: Steal ownership, nullify source - DynamicLib(DynamicLib&& other) NOEXCEPT : m_handle(other.m_handle) { + DynamicLib(DynamicLib &&other) NOEXCEPT : m_handle(other.m_handle) + { other.m_handle = nullptr; } - // Move Assignment - DynamicLib& operator=(DynamicLib&& other) NOEXCEPT { - if (this != &other) { + DynamicLib &operator=(DynamicLib &&other) NOEXCEPT + { + if (this != &other) + { Unload(); // Free current if exists m_handle = other.m_handle; other.m_handle = nullptr; @@ -50,135 +51,129 @@ namespace IACore { return *this; } - // No Copying allowed (Library handles are unique resources) - DynamicLib(CONST DynamicLib&) = delete; - DynamicLib& operator=(CONST DynamicLib&) = delete; + DynamicLib(CONST DynamicLib &) = delete; + DynamicLib &operator=(CONST DynamicLib &) = delete; - ~DynamicLib() { + ~DynamicLib() + { Unload(); } - // --------------------------------------------------------------------- - // Static Loader - // --------------------------------------------------------------------- - // Automatically detects extension (.dll/.so) if not provided NO_DISCARD("Check for load errors") - STATIC tl::expected Load(CONST String& searchPath, CONST String& name) { + + STATIC tl::expected Load(CONST String &searchPath, CONST String &name) + { namespace fs = std::filesystem; - // 1. Build Path safely fs::path fullPath = fs::path(searchPath) / name; - // 2. Auto-append extension if missing - if (!fullPath.has_extension()) { - #if IA_PLATFORM_WINDOWS - fullPath += ".dll"; - #elif IA_PLATFORM_MAC - fullPath += ".dylib"; - #else - fullPath += ".so"; - #endif + if (!fullPath.has_extension()) + { +#if IA_PLATFORM_WINDOWS + fullPath += ".dll"; +#elif IA_PLATFORM_MAC + fullPath += ".dylib"; +#else + fullPath += ".so"; +#endif } DynamicLib lib; - - #if IA_PLATFORM_WINDOWS - // Use LoadLibraryA (ANSI/UTF-8) assuming manifest is set for UTF-8 - HMODULE h = LoadLibraryA(fullPath.string().c_str()); - if (!h) { - return tl::make_unexpected(GetWindowsError()); - } - lib.m_handle = CAST(h, PVOID); - #else - // RTLD_LAZY: Resolve symbols only as code executes (Standard for plugins) - // RTLD_NOW: Resolve all immediately (Safer for strict engines) - void* h = dlopen(fullPath.c_str(), RTLD_LAZY | RTLD_LOCAL); - if (!h) { - // dlerror returns a string describing the last error - const char* err = dlerror(); - return tl::make_unexpected(String(err ? err : "Unknown dlopen error")); - } - lib.m_handle = h; - #endif + +#if IA_PLATFORM_WINDOWS + HMODULE h = LoadLibraryA(fullPath.string().c_str()); + if (!h) + { + return tl::make_unexpected(GetWindowsError()); + } + lib.m_handle = CAST(h, PVOID); +#else + // RTLD_LAZY: Resolve symbols only as code executes (Standard for plugins) + void *h = dlopen(fullPath.c_str(), RTLD_LAZY | RTLD_LOCAL); + if (!h) + { + // dlerror returns a string describing the last error + const char *err = dlerror(); + return tl::make_unexpected(String(err ? err : "Unknown dlopen error")); + } + lib.m_handle = h; +#endif return IA_MOVE(lib); } - // --------------------------------------------------------------------- - // Symbol Access - // --------------------------------------------------------------------- - NO_DISCARD("Check if symbol exists") - tl::expected GetSymbol(CONST String& name) CONST { - if (!m_handle) return tl::make_unexpected(String("Library not loaded")); + + tl::expected GetSymbol(CONST String &name) CONST + { + if (!m_handle) + return tl::make_unexpected(String("Library not loaded")); PVOID sym = nullptr; - #if IA_PLATFORM_WINDOWS - sym = CAST(GetProcAddress(CAST(m_handle, HMODULE), name.c_str()), PVOID); - if (!sym) return tl::make_unexpected(GetWindowsError()); - #else - // Clear any previous error - dlerror(); - sym = dlsym(m_handle, name.c_str()); - const char* err = dlerror(); - if (err) return tl::make_unexpected(String(err)); - #endif +#if IA_PLATFORM_WINDOWS + sym = CAST(GetProcAddress(CAST(m_handle, HMODULE), name.c_str()), PVOID); + if (!sym) + return tl::make_unexpected(GetWindowsError()); +#else + // Clear any previous error + dlerror(); + sym = dlsym(m_handle, name.c_str()); + const char *err = dlerror(); + if (err) + return tl::make_unexpected(String(err)); +#endif return sym; } // Template helper for casting - template - tl::expected GetFunction(CONST String& name) CONST { + template tl::expected GetFunction(CONST String &name) CONST + { auto res = GetSymbol(name); - if (!res) return tl::make_unexpected(res.error()); + if (!res) + return tl::make_unexpected(res.error()); return REINTERPRET(*res, FuncT); } - // --------------------------------------------------------------------- - // State Management - // --------------------------------------------------------------------- - - VOID Unload() { - if (m_handle) { - #if IA_PLATFORM_WINDOWS - FreeLibrary(CAST(m_handle, HMODULE)); - #else - dlclose(m_handle); - #endif + VOID Unload() + { + if (m_handle) + { +#if IA_PLATFORM_WINDOWS + FreeLibrary(CAST(m_handle, HMODULE)); +#else + dlclose(m_handle); +#endif m_handle = nullptr; } } - BOOL IsLoaded() CONST { + BOOL IsLoaded() CONST + { return m_handle != nullptr; } - private: + private: PVOID m_handle; - // --------------------------------------------------------------------- - // Private Helpers - // --------------------------------------------------------------------- - - #if IA_PLATFORM_WINDOWS - STATIC String GetWindowsError() { +#if IA_PLATFORM_WINDOWS + STATIC String GetWindowsError() + { DWORD errorID = ::GetLastError(); - if(errorID == 0) return String(); + if (errorID == 0) + return String(); LPSTR messageBuffer = nullptr; size_t size = FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, errorID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&messageBuffer, 0, NULL - ); + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, + errorID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &messageBuffer, 0, NULL); String message(messageBuffer, size); LocalFree(messageBuffer); return String("Win32 Error: ") + message; } - #endif +#endif }; -} \ No newline at end of file +} // namespace IACore \ No newline at end of file diff --git a/Src/IACore/inc/IACore/Environment.hpp b/Src/IACore/inc/IACore/Environment.hpp index bd94ecc..4fa5de4 100644 --- a/Src/IACore/inc/IACore/Environment.hpp +++ b/Src/IACore/inc/IACore/Environment.hpp @@ -23,20 +23,14 @@ namespace IACore class Environment { public: - // --------------------------------------------------------------------- - // Getters - // --------------------------------------------------------------------- - - // Modern approach: Returns nullopt if variable doesn't exist - STATIC std::optional Find(CONST String &name) + STATIC Optional Find(CONST String &name) { #if IA_PLATFORM_WINDOWS - // 1. Get required size (result includes null terminator if buffer is null/too small) DWORD bufferSize = GetEnvironmentVariableA(name.c_str(), nullptr, 0); if (bufferSize == 0) { - // DWORD 0 means failed (usually ERROR_ENVVAR_NOT_FOUND) + // DWORD 0 means failed return std::nullopt; } @@ -45,7 +39,7 @@ namespace IACore result.resize(bufferSize); // 3. Fetch - // Returns num chars written EXCLUDING null terminator + // Returns num chars written excluding null terminator DWORD actualSize = GetEnvironmentVariableA(name.c_str(), result.data(), bufferSize); if (actualSize == 0 || actualSize > bufferSize) @@ -58,8 +52,7 @@ namespace IACore return result; #else - // POSIX (Linux/Mac) - // getenv returns a pointer to the environment area. Do NOT free it. + // getenv returns a pointer to the environment area const char *val = std::getenv(name.c_str()); if (val == nullptr) { @@ -69,17 +62,11 @@ namespace IACore #endif } - // Classic approach: Returns value or default (defaulting to "") - // Matches your old API behavior but is safer STATIC String Get(CONST String &name, CONST String &defaultValue = "") { return Find(name).value_or(defaultValue); } - // --------------------------------------------------------------------- - // Setters - // --------------------------------------------------------------------- - STATIC BOOL Set(CONST String &name, CONST String &value) { if (name.empty()) @@ -88,7 +75,6 @@ namespace IACore #if IA_PLATFORM_WINDOWS return SetEnvironmentVariableA(name.c_str(), value.c_str()) != 0; #else - // setenv(name, value, overwrite) // Returns 0 on success, -1 on error return setenv(name.c_str(), value.c_str(), 1) == 0; #endif @@ -100,17 +86,12 @@ namespace IACore return FALSE; #if IA_PLATFORM_WINDOWS - // Windows unsets a variable by setting it to NULL return SetEnvironmentVariableA(name.c_str(), nullptr) != 0; #else return unsetenv(name.c_str()) == 0; #endif } - // --------------------------------------------------------------------- - // Utilities - // --------------------------------------------------------------------- - STATIC BOOL Exists(CONST String &name) { #if IA_PLATFORM_WINDOWS diff --git a/Src/IACore/inc/IACore/IATest.hpp b/Src/IACore/inc/IACore/IATest.hpp index 107a598..79f8896 100644 --- a/Src/IACore/inc/IACore/IATest.hpp +++ b/Src/IACore/inc/IACore/IATest.hpp @@ -28,7 +28,6 @@ # define valid_iatest_runner(type) iatest::_valid_iatest_runner::value_type -// Internal macro to handle the return logic # define __iat_micro_test(call) \ if (!(call)) \ return FALSE @@ -38,7 +37,6 @@ # define IAT_CHECK_EQ(lhs, rhs) __iat_micro_test(_test_eq((lhs), (rhs), #lhs " == " #rhs)) # define IAT_CHECK_NEQ(lhs, rhs) __iat_micro_test(_test_neq((lhs), (rhs), #lhs " != " #rhs)) -// Float specific checks (Game dev essential) # define IAT_CHECK_APPROX(lhs, rhs) __iat_micro_test(_test_approx((lhs), (rhs), #lhs " ~= " #rhs)) # define IAT_UNIT(func) _test_unit([this]() { return this->func(); }, #func) @@ -46,7 +44,6 @@ # define IAT_BLOCK(name) class name : public ia::iatest::block -// Concatenation fix for macros # define IAT_BEGIN_BLOCK(_group, _name) \ class _group##_##_name : public ia::iatest::block \ { \ @@ -74,9 +71,6 @@ namespace ia::iatest { - // ------------------------------------------------------------------------- - // Type Printing Helper (To show WHAT failed) - // ------------------------------------------------------------------------- template std::string ToString(CONST T &value) { if constexpr (std::is_arithmetic_v) @@ -93,7 +87,6 @@ namespace ia::iatest } } - // Specialization for pointers template std::string ToString(T *value) { if (value == NULLPTR) @@ -103,10 +96,6 @@ namespace ia::iatest return ss.str(); } - // ------------------------------------------------------------------------- - // Core Structures - // ------------------------------------------------------------------------- - DEFINE_TYPE(functor_t, std::function); struct unit_t @@ -128,7 +117,6 @@ namespace ia::iatest } protected: - // Generic Equality template BOOL _test_eq(IN CONST T1 &lhs, IN CONST T2 &rhs, IN PCCHAR description) { if (lhs != rhs) @@ -139,7 +127,6 @@ namespace ia::iatest return TRUE; } - // Generic Inequality template BOOL _test_neq(IN CONST T1 &lhs, IN CONST T2 &rhs, IN PCCHAR description) { if (lhs == rhs) @@ -150,13 +137,12 @@ namespace ia::iatest return TRUE; } - // Floating Point Approximation (Epsilon check) template BOOL _test_approx(IN T lhs, IN T rhs, IN PCCHAR description) { static_assert(std::is_floating_point_v, "Approx only works for floats/doubles"); T diff = std::abs(lhs - rhs); if (diff > static_cast(0.0001)) - { // Default epsilon + { print_fail(description, ToString(lhs), ToString(rhs)); return FALSE; } @@ -202,10 +188,6 @@ namespace ia::iatest template concept valid_block_class = std::derived_from; - // ------------------------------------------------------------------------- - // Runner - // ------------------------------------------------------------------------- - template class runner { public: @@ -253,7 +235,6 @@ namespace ia::iatest BOOL result = FALSE; try { - // Execute the test function result = v.Functor(); } catch (const std::exception &e) @@ -311,10 +292,6 @@ namespace ia::iatest { }; - // ------------------------------------------------------------------------- - // Global Test Registry - // ------------------------------------------------------------------------- - // Standard runner configuration for the single executable using DefaultRunner = runner; class TestRegistry @@ -352,7 +329,6 @@ namespace ia::iatest }; } // namespace ia::iatest -// Usage: IAT_REGISTER_ENTRY(Core, Utils) # define IAT_REGISTER_ENTRY(Group, Name) static ia::iatest::AutoRegister _iat_reg_##Group##_##Name; #endif // __cplusplus \ No newline at end of file diff --git a/Src/IACore/inc/IACore/IPC.hpp b/Src/IACore/inc/IACore/IPC.hpp index 93c603f..f326a2b 100644 --- a/Src/IACore/inc/IACore/IPC.hpp +++ b/Src/IACore/inc/IACore/IPC.hpp @@ -43,7 +43,7 @@ namespace IACore // SECTION 2: RING BUFFER CONTROL BLOCKS // ========================================================= - // RingBufferView::ControlBlock is already 64-byte aligned internally. + // RingBufferView ControlBlock is already 64-byte aligned internally. RingBufferView::ControlBlock MONI_Control; RingBufferView::ControlBlock MINO_Control; diff --git a/Src/IACore/inc/IACore/PCH.hpp b/Src/IACore/inc/IACore/PCH.hpp index c9b9714..a5a3ace 100644 --- a/Src/IACore/inc/IACore/PCH.hpp +++ b/Src/IACore/inc/IACore/PCH.hpp @@ -54,8 +54,8 @@ # define NOMINMAX # endif # include -#undef VOID -#undef ERROR +# undef VOID +# undef ERROR #elif IA_PLATFORM_UNIX # include # include @@ -238,7 +238,6 @@ #define ALIGN(a) __attribute__((aligned(a))) -// Mark every explicit assembly instruction as volatile #define ASM(...) __asm__ volatile(__VA_ARGS__) #ifndef NULL @@ -539,7 +538,7 @@ STATIC CONST FLOAT64 FLOAT64_EPSILON = DBL_EPSILON; #endif // ------------------------------------------------------------------------- -// C++ Containers and Helpers +// Containers and Helpers // ------------------------------------------------------------------------- #ifdef __cplusplus diff --git a/Src/IACore/inc/IACore/SocketOps.hpp b/Src/IACore/inc/IACore/SocketOps.hpp index c6c72b6..216b9a6 100644 --- a/Src/IACore/inc/IACore/SocketOps.hpp +++ b/Src/IACore/inc/IACore/SocketOps.hpp @@ -43,7 +43,7 @@ using SocketHandle = int; #else -# warning "IACore SocketOps is not supported on this platform." +# error "IACore SocketOps is not supported on this platform." #endif diff --git a/Src/IACore/inc/IACore/Utils.hpp b/Src/IACore/inc/IACore/Utils.hpp index 8fedd95..ad6ec5c 100644 --- a/Src/IACore/inc/IACore/Utils.hpp +++ b/Src/IACore/inc/IACore/Utils.hpp @@ -55,7 +55,6 @@ namespace IACore char high = hex[i]; char low = hex[i + 1]; - // Quick helper to decode nibble auto fromHexChar = [](char c) -> int { if (c >= '0' && c <= '9') return c - '0'; @@ -95,16 +94,10 @@ namespace IACore return std::ranges::upper_bound(range, value); } - // ------------------------------------------------------------------------- - // Hash Combination Logic - // Uses the "golden ratio" mix (similar to Boost) to preserve entropy. - // ------------------------------------------------------------------------- template INLINE STATIC void HashCombine(UINT64 &seed, CONST T &v) { UINT64 h; - // 1. Compile-Time check: Can this be treated as a string view? - // This catches "Literal Strings" (char[N]), const char*, and std::string if constexpr (std::is_constructible_v) { std::string_view sv(v); @@ -113,34 +106,24 @@ namespace IACore } else { - // 2. Standard types (int, float, custom structs) auto hasher = ankerl::unordered_dense::hash(); h = hasher(v); } - // 0x9e3779b97f4a7c15 is the 64-bit golden ratio (phi) approximation seed ^= h + 0x9e3779b97f4a7c15 + (seed << 6) + (seed >> 2); } - // ------------------------------------------------------------------------- - // Variadic Hasher - // Allows: IACore::ComputeHash(x, y, z); - // ------------------------------------------------------------------------- template INLINE STATIC UINT64 ComputeHash(CONST Args &...args) { UINT64 seed = 0; - (HashCombine(seed, args), ...); // C++17/20 Fold Expression + (HashCombine(seed, args), ...); return seed; } - // ------------------------------------------------------------------------- - // Flat Hasher - // Allows: IACore::ComputeHashFlat(x, y, z); - // ------------------------------------------------------------------------- - template INLINE STATIC UINT64 ComputeHashFlat(CONST T &obj, MemberPtrs... members) + template + INLINE STATIC UINT64 ComputeHashFlat(CONST T &obj, MemberPtrs... members) { UINT64 seed = 0; - // C++17 Fold Expression: applies (seed, obj.*ptr) for every ptr in members (HashCombine(seed, obj.*members), ...); return seed; } @@ -156,15 +139,14 @@ namespace IACore // struct Vector3 { float x, y, z; }; // IA_MAKE_HASHABLE(Vector3, &Vector3::x, &Vector3::y, &Vector3::z) // ----------------------------------------------------------------------------- -#define IA_MAKE_HASHABLE(Type, ...) \ - template<> struct ankerl::unordered_dense::hash \ - { \ - using is_avalanching = void; \ - NO_DISCARD("Hash value should be used") \ - UINT64 operator()(CONST Type &v) const NOEXCEPT \ - { \ - /* Pass the object and the list of member pointers */ \ - return IACore::Utils::ComputeHashFlat(v, __VA_ARGS__); \ - } \ +#define IA_MAKE_HASHABLE(Type, ...) \ + template<> struct ankerl::unordered_dense::hash \ + { \ + using is_avalanching = void; \ + NO_DISCARD("Hash value should be used") \ + UINT64 operator()(CONST Type &v) const NOEXCEPT \ + { \ + /* Pass the object and the list of member pointers */ \ + return IACore::Utils::ComputeHashFlat(v, __VA_ARGS__); \ + } \ }; - diff --git a/Tests/Unit/CMakeLists.txt b/Tests/Unit/CMakeLists.txt index c27036b..cdb66bd 100644 --- a/Tests/Unit/CMakeLists.txt +++ b/Tests/Unit/CMakeLists.txt @@ -23,13 +23,13 @@ set(TEST_SOURCES add_executable(IACore_Test_Suite ${TEST_SOURCES}) -# Enable exceptions for testing framework (even if Core is no-except) +# Enable exceptions for testing framework target_compile_options(IACore_Test_Suite PRIVATE -fexceptions) set_target_properties(IACore_Test_Suite PROPERTIES USE_EXCEPTIONS ON) target_link_libraries(IACore_Test_Suite PRIVATE IACore) -# Copy necessary runtime assets if you have them (like the LongProcess test subject) +# Copy necessary runtime assets add_custom_command(TARGET IACore_Test_Suite POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ diff --git a/Tests/Unit/Environment.cpp b/Tests/Unit/Environment.cpp index e9be987..432ce8b 100644 --- a/Tests/Unit/Environment.cpp +++ b/Tests/Unit/Environment.cpp @@ -23,8 +23,6 @@ using namespace IACore; // ----------------------------------------------------------------------------- // Constants // ----------------------------------------------------------------------------- -// We use a unique prefix to ensure we don't accidentally mess with real -// system variables like PATH or HOME. static const char *TEST_KEY = "IA_TEST_ENV_VAR_12345"; static const char *TEST_VAL = "Hello World"; @@ -123,7 +121,6 @@ BOOL TestDefaults() // ------------------------------------------------------------------------- // 5. Empty Strings vs Null/Unset // ------------------------------------------------------------------------- -// This is a critical edge case. // Does Set(Key, "") create an existing empty variable, or unset it? // Standard POSIX/Windows API behavior is that it EXISTS, but is empty. BOOL TestEmptyValue() diff --git a/Tests/Unit/ProcessOps.cpp b/Tests/Unit/ProcessOps.cpp index 1050c55..6f9f393 100644 --- a/Tests/Unit/ProcessOps.cpp +++ b/Tests/Unit/ProcessOps.cpp @@ -50,7 +50,6 @@ BOOL TestBasicRun() IAT_CHECK(result.has_value()); IAT_CHECK_EQ(*result, 0); // Exit code 0 - // Note: Echo might add newline, but your LineBuffer trims/handles it. // We check if "HelloIA" is contained or equal. IAT_CHECK(captured.find("HelloIA") != String::npos); @@ -137,14 +136,9 @@ BOOL TestMissingExe() // ------------------------------------------------------------------------- BOOL TestLargeOutput() { - // We need to generate output larger than the internal 4096 buffer + // Need to generate output larger than the internal 4096 buffer // to ensure the "partial line" logic works when a line crosses a buffer boundary. - // We will construct a python script or shell command to print a massive line. - // Cross platform approach: Use Python if available, or just a long echo. - // Let's assume 'python3' or 'python' is in path, otherwise skip? - // Safer: Use pure shell loop if possible, or just a massive command line arg. - String massiveString; massiveString.reserve(5000); for (int i = 0; i < 500; ++i) @@ -223,10 +217,6 @@ BOOL TestComplexArguments() // 3. path/to/file String complexArgs = "-DDEFINED_MSG=\\\"Hello World\\\" -v path/to/file"; - // We can't easily inspect the child process argv in this unit test framework without - // writing a dedicated child program that prints its argv. - // However, for now, we ensure it doesn't crash or return error code 127. - // Use "echo" to verify what it received. // Expected output: -DDEFINED_MSG="Hello World" -v path/to/file String cmd = "/bin/echo"; diff --git a/Tests/Unit/RingBuffer.cpp b/Tests/Unit/RingBuffer.cpp index 989f33e..4189138 100644 --- a/Tests/Unit/RingBuffer.cpp +++ b/Tests/Unit/RingBuffer.cpp @@ -21,7 +21,9 @@ using namespace IACore; IAT_BEGIN_BLOCK(Core, RingBuffer) +// ------------------------------------------------------------------------- // 1. Basic Push Pop +// ------------------------------------------------------------------------- BOOL TestPushPop() { // Allocate raw memory for the ring buffer @@ -54,7 +56,9 @@ BOOL TestPushPop() return TRUE; } -// 2. Wrap Around (The hardest logic to get right) +// ------------------------------------------------------------------------- +// 2. Wrap Around +// ------------------------------------------------------------------------- BOOL TestWrapAround() { // Small buffer to force wrapping quickly diff --git a/Tests/Unit/StreamReader.cpp b/Tests/Unit/StreamReader.cpp index bcdb5df..cb9803b 100644 --- a/Tests/Unit/StreamReader.cpp +++ b/Tests/Unit/StreamReader.cpp @@ -20,10 +20,6 @@ using namespace IACore; -// ----------------------------------------------------------------------------- -// Test Block Definition -// ----------------------------------------------------------------------------- - IAT_BEGIN_BLOCK(Core, StreamReader) // ------------------------------------------------------------------------- @@ -53,14 +49,13 @@ BOOL TestReadUint8() BOOL TestReadMultiByte() { // 0x04030201 in Little Endian memory layout + // IACore always assumes a Little Endian machine UINT8 data[] = {0x01, 0x02, 0x03, 0x04}; StreamReader reader(data); auto val = reader.Read(); IAT_CHECK(val.has_value()); - // Assuming standard x86/ARM Little Endian for this test - // If your engine supports Big Endian, you'd check architecture here IAT_CHECK_EQ(*val, (UINT32) 0x04030201); IAT_CHECK_EQ(reader.Cursor(), (SIZE_T) 4); diff --git a/Tests/Unit/Utils.cpp b/Tests/Unit/Utils.cpp index d2c2930..785bb20 100644 --- a/Tests/Unit/Utils.cpp +++ b/Tests/Unit/Utils.cpp @@ -88,7 +88,6 @@ BOOL TestHexErrors() // Odd Length auto odd = IACore::Utils::HexStringToBinary("ABC"); IAT_CHECK_NOT(odd.has_value()); - // Optional: IAT_CHECK_EQ(odd.error(), "Hex string must have even length"); // Invalid Characters auto invalid = IACore::Utils::HexStringToBinary("ZZTOP"); @@ -178,9 +177,8 @@ BOOL TestHashMacro() { TestVec3 v1{1.0f, 2.0f, 3.0f}; TestVec3 v2{1.0f, 2.0f, 3.0f}; - TestVec3 v3{1.0f, 2.0f, 4.0f}; // Slight change + TestVec3 v3{1.0f, 2.0f, 4.0f}; - // Instantiate the hasher manually to verify the struct specialization exists ankerl::unordered_dense::hash hasher; UINT64 h1 = hasher(v1); @@ -194,10 +192,6 @@ BOOL TestHashMacro() // Verify ComputeHash integration // ------------------------------------------------------------- - // We cannot check EQ(h1, ComputeHash(v1)) because ComputeHash applies - // one extra layer of "Golden Ratio Mixing" on top of the object's hash. - // Instead, we verify that ComputeHash behaves exactly like a manual HashCombine. - UINT64 hManual = 0; IACore::Utils::HashCombine(hManual, v1); @@ -206,7 +200,7 @@ BOOL TestHashMacro() // This proves ComputeHash found the specialization and mixed it correctly IAT_CHECK_EQ(hManual, hWrapper); - // Optional: Verify the avalanche effect took place (hWrapper should NOT be h1) + // Verify the avalanche effect took place (hWrapper should NOT be h1) IAT_CHECK_NEQ(h1, hWrapper); return TRUE;