This commit is contained in:
2025-12-15 23:51:06 +05:30
parent 534767ced9
commit 957a3572c3
18 changed files with 175 additions and 231 deletions

View File

@ -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()

View File

@ -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).

View File

@ -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<UINT8> 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)

View File

@ -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();
}

View File

@ -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<std::string> argStorage; // To keep strings alive
std::vector<char *> 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));
}

View File

@ -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 <https://www.gnu.org/licenses/>.
@ -19,30 +19,31 @@
#include <IACore/PCH.hpp>
#if IA_PLATFORM_WINDOWS
#include <libloaderapi.h>
#include <errhandlingapi.h>
# include <libloaderapi.h>
# include <errhandlingapi.h>
#else
#include <dlfcn.h>
# include <dlfcn.h>
#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<DynamicLib, String> Load(CONST String& searchPath, CONST String& name) {
STATIC tl::expected<DynamicLib, String> 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<PVOID, String> GetSymbol(CONST String& name) CONST {
if (!m_handle) return tl::make_unexpected(String("Library not loaded"));
tl::expected<PVOID, String> 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<typename FuncT>
tl::expected<FuncT, String> GetFunction(CONST String& name) CONST {
template<typename FuncT> tl::expected<FuncT, String> 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
};
}
} // namespace IACore

View File

@ -23,20 +23,14 @@ namespace IACore
class Environment
{
public:
// ---------------------------------------------------------------------
// Getters
// ---------------------------------------------------------------------
// Modern approach: Returns nullopt if variable doesn't exist
STATIC std::optional<String> Find(CONST String &name)
STATIC Optional<String> 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

View File

@ -28,7 +28,6 @@
# define valid_iatest_runner(type) iatest::_valid_iatest_runner<type>::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<typename T> std::string ToString(CONST T &value)
{
if constexpr (std::is_arithmetic_v<T>)
@ -93,7 +87,6 @@ namespace ia::iatest
}
}
// Specialization for pointers
template<typename T> 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<BOOL()>);
struct unit_t
@ -128,7 +117,6 @@ namespace ia::iatest
}
protected:
// Generic Equality
template<typename T1, typename T2> 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<typename T1, typename T2> 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<typename T> BOOL _test_approx(IN T lhs, IN T rhs, IN PCCHAR description)
{
static_assert(std::is_floating_point_v<T>, "Approx only works for floats/doubles");
T diff = std::abs(lhs - rhs);
if (diff > static_cast<T>(0.0001))
{ // Default epsilon
{
print_fail(description, ToString(lhs), ToString(rhs));
return FALSE;
}
@ -202,10 +188,6 @@ namespace ia::iatest
template<typename block_class>
concept valid_block_class = std::derived_from<block_class, block>;
// -------------------------------------------------------------------------
// Runner
// -------------------------------------------------------------------------
template<BOOL stopOnFail = false, BOOL isVerbose = false> 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<false, true>;
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<Group##_##Name> _iat_reg_##Group##_##Name;
#endif // __cplusplus

View File

@ -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;

View File

@ -54,8 +54,8 @@
# define NOMINMAX
# endif
# include <windows.h>
#undef VOID
#undef ERROR
# undef VOID
# undef ERROR
#elif IA_PLATFORM_UNIX
# include <unistd.h>
# include <sys/wait.h>
@ -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

View File

@ -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

View File

@ -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<typename T> 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, T>)
{
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<T>();
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<typename... Args> 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<typename T, typename... MemberPtrs> INLINE STATIC UINT64 ComputeHashFlat(CONST T &obj, MemberPtrs... members)
template<typename T, typename... MemberPtrs>
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<Type> \
{ \
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<Type> \
{ \
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__); \
} \
};

View File

@ -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
$<TARGET_FILE:LongProcess>

View File

@ -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()

View File

@ -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";

View File

@ -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

View File

@ -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<UINT32>();
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);

View File

@ -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<TestVec3> 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;