Tests 1/2

This commit is contained in:
2025-11-23 02:06:57 +05:30
commit d1b1ba12c0
36 changed files with 3785 additions and 0 deletions

2
Src/CMakeLists.txt Normal file
View File

@ -0,0 +1,2 @@
add_subdirectory(IACore/)

29
Src/IACore/CMakeLists.txt Normal file
View File

@ -0,0 +1,29 @@
set(SRC_FILES
"imp/cpp/IACore.cpp"
)
add_library(IACore STATIC ${SRC_FILES})
target_include_directories(IACore PUBLIC inc/)
target_include_directories(IACore PRIVATE imp/hpp/)
target_link_libraries(IACore PUBLIC tl::expected unordered_dense::unordered_dense)
if(WIN32)
target_link_libraries(IACore PUBLIC mimalloc-static)
target_link_options(IACore PUBLIC "/INCLUDE:mi_version")
else()
target_link_libraries(IACore PUBLIC mimalloc)
endif()
target_precompile_headers(IACore PUBLIC inc/IACore/PCH.hpp)
target_compile_options(IACore PRIVATE -fno-exceptions)
target_compile_options(IACore INTERFACE
$<$<NOT:$<BOOL:$<TARGET_PROPERTY:USE_EXCEPTIONS>>>:-fno-exceptions>
)
define_property(TARGET PROPERTY USE_EXCEPTIONS
BRIEF_DOCS "If ON, this target is allowed to use C++ exceptions."
FULL_DOCS "Prevents IACore from propagating -fno-exceptions to this target."
)

View File

@ -0,0 +1,24 @@
// 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/>.
#include <IACore/IACore.hpp>
#include <mimalloc-new-delete.h>
namespace IACore
{
}

View File

@ -0,0 +1,101 @@
// 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/>.
#pragma once
#include <IACore/PCH.hpp>
namespace IACore {
class BinaryReader {
public:
// ---------------------------------------------------------------------
// Construction (Zero Copy)
// ---------------------------------------------------------------------
// Accepts Vector<UINT8>, std::array, or C-arrays automatically
BinaryReader(std::span<const UINT8> data)
: m_span(data), m_cursor(0) {}
// ---------------------------------------------------------------------
// Core API
// ---------------------------------------------------------------------
// Generic Primitive Reader (Read<UINT32>(), Read<FLOAT32>(), etc.)
template <typename T>
NO_DISCARD("Check for EOF")
tl::expected<T, String> Read() {
constexpr SIZE_T size = sizeof(T);
if B_UNLIKELY((m_cursor + size > m_span.size())) {
return tl::make_unexpected(String("Unexpected EOF reading type"));
}
T value;
// SAFE: memcpy handles alignment issues on ARM/Android automatically.
// Modern compilers optimize this into a single register load instruction.
std::memcpy(&value, &m_span[m_cursor], size);
m_cursor += size;
return value;
}
// Batch Read (Copy to external buffer)
tl::expected<void, String> Read(PVOID buffer, SIZE_T size) {
if B_UNLIKELY((m_cursor + size > m_span.size())) {
return tl::make_unexpected(String("Unexpected EOF reading buffer"));
}
std::memcpy(buffer, &m_span[m_cursor], size);
m_cursor += size;
return {};
}
// String Reader (Null Terminated or Length Prefixed)
tl::expected<String, String> ReadString(SIZE_T length) {
if B_UNLIKELY((m_cursor + length > m_span.size())) {
return tl::make_unexpected(String("Unexpected EOF reading string"));
}
// Create string from current pointer
String str(REINTERPRET(&m_span[m_cursor], const char*), length);
m_cursor += length;
return str;
}
// ---------------------------------------------------------------------
// Navigation
// ---------------------------------------------------------------------
VOID Skip(SIZE_T amount) {
// Clamp to end
m_cursor = std::min(m_cursor + amount, m_span.size());
}
VOID Seek(SIZE_T pos) {
if (pos > m_span.size()) m_cursor = m_span.size();
else m_cursor = pos;
}
SIZE_T Cursor() CONST { return m_cursor; }
SIZE_T Remaining() CONST { return m_span.size() - m_cursor; }
BOOL IsEOF() CONST { return m_cursor >= m_span.size(); }
private:
std::span<const UINT8> m_span;
SIZE_T m_cursor;
};
}

View File

@ -0,0 +1,100 @@
// 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/>.
#pragma once
#include <IACore/PCH.hpp>
namespace IACore {
class BinaryWriter {
public:
// Mode 1: Append to a Vector (Growing)
BinaryWriter(Vector<UINT8>& target) : m_buffer(&target), m_span({}), m_cursor(target.size()), m_isVector(true) {}
// Mode 2: Write into existing fixed memory (No allocs)
BinaryWriter(std::span<UINT8> target) : m_buffer(nullptr), m_span(target), m_cursor(0), m_isVector(false) {}
// ---------------------------------------------------------------------
// Core API
// ---------------------------------------------------------------------
// Append T (Handling Endianness automatically if needed)
template <typename T>
VOID Write(T value) {
// If we are Little Endian (x86/ARM), this compiles to a raw MOV/memcpy.
// If we need specific Endianness (Network Byte Order), use std::byteswap (C++23)
// or a simple swap helper.
// For a game engine, we usually stick to Native Endian (LE) for speed.
CONST SIZE_T size = sizeof(T);
if (m_isVector) {
// Vector guarantees contiguous memory, but push_back is slow for primitives.
// We resize and memcpy.
SIZE_T currentPos = m_buffer->size();
m_buffer->resize(currentPos + size);
std::memcpy(m_buffer->data() + currentPos, &value, size);
} else {
// Fixed Buffer Safety Check
if B_UNLIKELY((m_cursor + size > m_span.size())) {
IA_PANIC("BinaryWriter Overflow");
}
std::memcpy(m_span.data() + m_cursor, &value, size);
m_cursor += size;
}
}
// Random Access Write (Replaces put32, put16, etc.)
template <typename T>
VOID WriteAt(SIZE_T position, T value) {
PUINT8 ptr = GetPtrAt(position);
if (ptr) {
std::memcpy(ptr, &value, sizeof(T));
} else {
IA_PANIC("BinaryWriter Out of Bounds Write");
}
}
VOID WriteBytes(CONST PVOID data, SIZE_T size) {
if (m_isVector) {
SIZE_T currentPos = m_buffer->size();
m_buffer->resize(currentPos + size);
std::memcpy(m_buffer->data() + currentPos, data, size);
} else {
if B_UNLIKELY((m_cursor + size > m_span.size())) IA_PANIC("Overflow");
std::memcpy(m_span.data() + m_cursor, data, size);
m_cursor += size;
}
}
PUINT8 GetPtrAt(SIZE_T pos) {
if (m_isVector) {
if (pos >= m_buffer->size()) return nullptr;
return m_buffer->data() + pos;
} else {
if (pos >= m_span.size()) return nullptr;
return m_span.data() + pos;
}
}
private:
Vector<UINT8>* m_buffer;
std::span<UINT8> m_span;
SIZE_T m_cursor;
BOOL m_isVector;
};
}

View File

@ -0,0 +1,184 @@
// 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/>.
#pragma once
#include <IACore/PCH.hpp>
#if IA_PLATFORM_WINDOWS
#include <libloaderapi.h>
#include <errhandlingapi.h>
#else
#include <dlfcn.h>
#endif
namespace IACore {
class DynamicLib {
public:
// ---------------------------------------------------------------------
// Constructors / Destructors (Move Only)
// ---------------------------------------------------------------------
DynamicLib() : m_handle(nullptr) {}
// Move Constructor: Steal ownership, nullify source
DynamicLib(DynamicLib&& other) NOEXCEPT : m_handle(other.m_handle) {
other.m_handle = nullptr;
}
// Move Assignment
DynamicLib& operator=(DynamicLib&& other) NOEXCEPT {
if (this != &other) {
Unload(); // Free current if exists
m_handle = other.m_handle;
other.m_handle = nullptr;
}
return *this;
}
// No Copying allowed (Library handles are unique resources)
DynamicLib(CONST DynamicLib&) = delete;
DynamicLib& operator=(CONST DynamicLib&) = delete;
~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) {
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
}
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
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"));
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
return sym;
}
// Template helper for casting
template<typename FuncT>
tl::expected<FuncT, String> GetFunction(CONST String& name) CONST {
auto res = GetSymbol(name);
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
m_handle = nullptr;
}
}
BOOL IsLoaded() CONST {
return m_handle != nullptr;
}
private:
PVOID m_handle;
// ---------------------------------------------------------------------
// Private Helpers
// ---------------------------------------------------------------------
#if IA_PLATFORM_WINDOWS
STATIC String GetWindowsError() {
DWORD errorID = ::GetLastError();
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
);
String message(messageBuffer, size);
LocalFree(messageBuffer);
return String("Win32 Error: ") + message;
}
#endif
};
}

View File

@ -0,0 +1,123 @@
// 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/>.
#pragma once
#include <IACore/PCH.hpp>
namespace IACore
{
class Environment
{
public:
// ---------------------------------------------------------------------
// Getters
// ---------------------------------------------------------------------
// Modern approach: Returns nullopt if variable doesn't exist
STATIC std::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)
return std::nullopt;
}
// 2. Allocate (bufferSize includes the null terminator request)
String result;
result.resize(bufferSize);
// 3. Fetch
// Returns num chars written EXCLUDING null terminator
DWORD actualSize = GetEnvironmentVariableA(name.c_str(), result.data(), bufferSize);
if (actualSize == 0 || actualSize > bufferSize)
{
return std::nullopt;
}
// Resize down to exclude the null terminator and any slack
result.resize(actualSize);
return result;
#else
// POSIX (Linux/Mac)
// getenv returns a pointer to the environment area. Do NOT free it.
const char *val = std::getenv(name.c_str());
if (val == nullptr)
{
return std::nullopt;
}
return String(val);
#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())
return FALSE;
#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
}
STATIC BOOL Unset(CONST String &name)
{
if (name.empty())
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
return GetEnvironmentVariableA(name.c_str(), nullptr, 0) > 0;
#else
return std::getenv(name.c_str()) != nullptr;
#endif
}
};
} // namespace IACore

View File

@ -0,0 +1,174 @@
// 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/>.
#pragma once
#include <IACore/PCH.hpp>
#include <fstream>
#include <filesystem>
namespace IACore {
namespace fs = std::filesystem;
class File {
public:
// Modern mapping of flags to standard IO streams
enum class EOpenFlags : UINT32 {
Read = 1 << 0, // std::ios::in
Write = 1 << 1, // std::ios::out
Binary = 1 << 2, // std::ios::binary
Append = 1 << 3, // std::ios::app
Trunc = 1 << 4 // std::ios::trunc
};
// ---------------------------------------------------------------------
// Static Helper API (The "One-Liners")
// ---------------------------------------------------------------------
// Reads entire file into a binary vector
NO_DISCARD("Handle the error")
STATIC tl::expected<Vector<UINT8>, String> ReadToVector(CONST String& path) {
// 1. Check File Existence & Size
std::error_code ec;
auto fileSize = fs::file_size(path, ec);
if (ec) {
return tl::make_unexpected(String("File not found or inaccessible: ") + path);
}
// 2. Open Stream
std::ifstream file(path, std::ios::binary);
if (!file.is_open()) {
return tl::make_unexpected(String("Failed to open file handle: ") + path);
}
// 3. Read
Vector<UINT8> buffer;
buffer.resize(CAST(fileSize, SIZE_T));
file.read(REINTERPRET(buffer.data(), char*), fileSize);
if(file.fail())
return tl::make_unexpected(String("Read error: ") + path);
return buffer;
}
// Reads entire file into a String
NO_DISCARD("Handle the error")
STATIC tl::expected<String, String> ReadToString(CONST String& path) {
// Reuse the binary logic to avoid code duplication, then cast
auto result = ReadToVector(path);
if (!result) {
return tl::make_unexpected(result.error());
}
// Efficient move into string (reinterpret_cast approach or move)
// Since Vector<UINT8> and String memory layout isn't guaranteed identical, we copy.
// (Though in many STL implementations you could theoretically steal the buffer, copying is safer)
String str(result->begin(), result->end());
return str;
}
// ---------------------------------------------------------------------
// Path Utilities (Replaces manual parsing)
// ---------------------------------------------------------------------
// Old: ExtractFilenameFromPath<true/false>
// New: true -> filename(), false -> stem()
template<BOOL includeExtension = true>
STATIC String ExtractFilename(CONST String& pathStr) {
fs::path p(pathStr);
if CONSTEXPR (includeExtension) {
return p.filename().string(); // "sprite.png"
} else {
return p.stem().string(); // "sprite"
}
}
// Old: ExtractDirectoryFromPath
STATIC String ExtractDirectory(CONST String& pathStr) {
fs::path p(pathStr);
return p.parent_path().string(); // "assets/textures"
}
STATIC BOOL Exists(CONST String& pathStr) {
std::error_code ec;
return fs::exists(pathStr, ec);
}
public:
// ---------------------------------------------------------------------
// Instance API (For streaming/partial reads)
// ---------------------------------------------------------------------
File() = default;
// RAII Constructor
File(CONST String& path, UINT32 flags) {
UNUSED(Open(path, flags));
}
~File() {
Close();
}
// Returns void (success) or error String
tl::expected<void, String> Open(CONST String& path, UINT32 flags) {
Close(); // Ensure previous handle is closed
std::ios_base::openmode mode = static_cast<std::ios_base::openmode>(0);
if (flags & (UINT32)EOpenFlags::Read) mode |= std::ios::in;
if (flags & (UINT32)EOpenFlags::Write) mode |= std::ios::out;
if (flags & (UINT32)EOpenFlags::Binary) mode |= std::ios::binary;
if (flags & (UINT32)EOpenFlags::Append) mode |= std::ios::app;
if (flags & (UINT32)EOpenFlags::Trunc) mode |= std::ios::trunc;
m_fs.open(path, mode);
if (!m_fs.is_open()) {
return tl::make_unexpected(String("Failed to open file: ") + path);
}
return {};
}
VOID Close() {
if (m_fs.is_open()) {
m_fs.close();
}
}
BOOL IsOpen() CONST {
return m_fs.is_open();
}
// Returns number of bytes read
SIZE_T Read(PVOID buffer, SIZE_T size) {
if (!m_fs.is_open()) return 0;
m_fs.read(REINTERPRET(buffer, char*), size);
return static_cast<SIZE_T>(m_fs.gcount());
}
VOID Write(CONST PVOID buffer, SIZE_T size) {
if (m_fs.is_open()) {
m_fs.write(REINTERPRET(buffer, const char*), size);
}
}
private:
std::fstream m_fs;
};
}

View File

@ -0,0 +1,28 @@
// 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/>.
#pragma once
#include <IACore/PCH.hpp>
#ifdef __cplusplus
namespace IACore
{
}
#endif

View File

@ -0,0 +1,261 @@
// 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/>.
#pragma once
#include <IACore/PCH.hpp>
#ifdef __cplusplus
#include <exception>
// -----------------------------------------------------------------------------
// Macros
// -----------------------------------------------------------------------------
#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
#define IAT_CHECK(v) __iat_micro_test(_test((v), #v))
#define IAT_CHECK_NOT(v) __iat_micro_test(_test_not((v), "NOT " #v))
#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)
#define IAT_NAMED_UNIT(n, func) _test_unit([this](){ return this->func(); }, n)
#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 { \
public: PCCHAR name() CONST OVERRIDE { return #_group "::" #_name; } private:
#define IAT_END_BLOCK() };
#define IAT_BEGIN_TEST_LIST() public: VOID declareTests() OVERRIDE {
#define IAT_ADD_TEST(name) IAT_UNIT(name)
#define IAT_END_TEST_LIST() } private:
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>) {
return std::to_string(value);
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, const char*>) {
return std::string("\"") + value + "\"";
} else {
return "{Object}"; // Fallback for complex types
}
}
// Specialization for pointers
template<typename T>
std::string ToString(T* value) {
if (value == NULLPTR) return "nullptr";
std::stringstream ss;
ss << "ptr(" << (void*)value << ")";
return ss.str();
}
// -------------------------------------------------------------------------
// Core Structures
// -------------------------------------------------------------------------
DEFINE_TYPE(functor_t, std::function<BOOL()>);
struct unit_t {
std::string Name;
functor_t Functor;
};
class block
{
public:
virtual ~block() = default;
PURE_VIRTUAL(PCCHAR name() CONST);
PURE_VIRTUAL(VOID declareTests());
std::vector<unit_t>& units() { return m_units; }
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) {
print_fail(description, ToString(lhs), ToString(rhs));
return FALSE;
}
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) {
print_fail(description, ToString(lhs), "NOT " + ToString(rhs));
return FALSE;
}
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;
}
return TRUE;
}
BOOL _test(IN BOOL value, IN PCCHAR description)
{
if(!value) {
printf(__CC_BLUE " %s... " __CC_RED "FAILED" __CC_DEFAULT "\n", description);
return FALSE;
}
return TRUE;
}
BOOL _test_not(IN BOOL value, IN PCCHAR description)
{
if(value) {
printf(__CC_BLUE " %s... " __CC_RED "FAILED" __CC_DEFAULT "\n", description);
return FALSE;
}
return TRUE;
}
VOID _test_unit(IN functor_t functor, IN PCCHAR name)
{
m_units.push_back({name, functor});
}
private:
VOID print_fail(PCCHAR desc, std::string v1, std::string v2) {
printf(__CC_BLUE " %s... " __CC_RED "FAILED" __CC_DEFAULT "\n", desc);
printf(__CC_RED " Expected: %s" __CC_DEFAULT "\n", v2.c_str());
printf(__CC_RED " Actual: %s" __CC_DEFAULT "\n", v1.c_str());
}
std::vector<unit_t> m_units;
};
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:
runner(){}
~runner() { summarize(); }
template<typename block_class>
requires valid_block_class<block_class>
VOID testBlock();
private:
VOID summarize();
private:
SIZE_T m_testCount { 0 };
SIZE_T m_failCount { 0 };
SIZE_T m_blockCount { 0 };
};
template<BOOL stopOnFail, BOOL isVerbose>
template<typename block_class>
requires valid_block_class<block_class>
VOID runner<stopOnFail, isVerbose>::testBlock()
{
m_blockCount++;
block_class b;
b.declareTests();
printf(__CC_MAGENTA "Testing [%s]..." __CC_DEFAULT "\n", b.name());
for(auto& v: b.units())
{
m_testCount++;
if constexpr(isVerbose) {
printf(__CC_YELLOW " Testing %s...\n" __CC_DEFAULT, v.Name.c_str());
}
BOOL result = FALSE;
try {
// Execute the test function
result = v.Functor();
}
catch (const std::exception& e) {
printf(__CC_RED " CRITICAL EXCEPTION in %s: %s\n" __CC_DEFAULT, v.Name.c_str(), e.what());
result = FALSE;
}
catch (...) {
printf(__CC_RED " UNKNOWN CRITICAL EXCEPTION in %s\n" __CC_DEFAULT, v.Name.c_str());
result = FALSE;
}
if(!result)
{
m_failCount++;
if constexpr(stopOnFail) { summarize(); exit(-1); }
}
}
fputs("\n", stdout);
}
template<BOOL stopOnFail, BOOL isVerbose>
VOID runner<stopOnFail, isVerbose>::summarize()
{
printf(__CC_GREEN "\n-----------------------------------\n\t SUMMARY\n-----------------------------------\n");
if(!m_failCount) {
printf("\n\tALL TESTS PASSED!\n\n");
} else {
FLOAT64 successRate = (100.0 * static_cast<FLOAT64>(m_testCount - m_failCount)/static_cast<FLOAT64>(m_testCount));
printf(__CC_RED "%zu OUT OF %zu TESTS FAILED\n" __CC_YELLOW "Success Rate: %.2f%%\n", m_failCount, m_testCount, successRate);
}
printf(__CC_MAGENTA "Ran %zu test(s) across %zu block(s)\n" __CC_GREEN "-----------------------------------" __CC_DEFAULT "\n", m_testCount, m_blockCount);
}
template<typename>
struct _valid_iatest_runner : std::false_type {};
template<BOOL stopOnFail, BOOL isVerbose>
struct _valid_iatest_runner<runner<stopOnFail,isVerbose>> : std::true_type {};
}
#endif // __cplusplus

View File

@ -0,0 +1,527 @@
// 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/>.
#pragma once
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#ifdef __cplusplus
# include <bit>
# include <new>
# include <span>
# include <limits>
# include <cstring>
# include <cstddef>
# include <concepts>
# include <functional>
# include <type_traits>
# include <initializer_list>
# include <array>
# include <string>
# include <vector>
# include <format>
# include <sstream>
# include <optional>
# include <string_view>
# include <tl/expected.hpp>
# include <ankerl/unordered_dense.h>
#else
# include <float.h>
# include <stdbool.h>
# include <stddef.h>
# include <string.h>
#endif
// -----------------------------------------------------------------------------
// Configuration Macros
// -----------------------------------------------------------------------------
#define IA_CHECK(o) (o > 0)
#if defined(__IA_DEBUG) && __IA_DEBUG
# define __DEBUG_MODE__
# define __BUILD_MODE_NAME "debug"
# define DEBUG_ONLY(v) v
#else
# define __RELEASE_MODE__
# define __BUILD_MODE_NAME "release"
# ifndef NDEBUG
# define NDEBUG
# endif
# ifndef __OPTIMIZE__
# define __OPTIMIZE__
# endif
# define DEBUG_ONLY(f)
#endif
// -----------------------------------------------------------------------------
// Security Macros
// -----------------------------------------------------------------------------
#define IA_PANIC(msg) \
{ \
fprintf(stderr, "PANIC: %s\n", msg); \
__builtin_trap(); \
}
// Advanced Security features are not included in OSS builds
// (OSS version does not implement 'IAC_CHECK_*'s)
#define IACORE_SECURITY_LEVEL 0
#define IAC_SEC_LEVEL(v) (IACORE_SECURITY_LEVEL >= v)
#if __IA_DEBUG || IAC_SEC_LEVEL(1)
# define __IAC_OVERFLOW_CHECKS 1
#else
# define __IAC_OVERFLOW_CHECKS 0
#endif
#if __IA_DEBUG || IAC_SEC_LEVEL(2)
# define __IAC_SANITY_CHECKS 1
#else
# define __IAC_SANITY_CHECKS 0
#endif
// -----------------------------------------------------------------------------
// Language Abstraction Macros
// -----------------------------------------------------------------------------
#define AUTO auto
#define CONST const
#define STATIC static
#define EXTERN extern
#ifdef __cplusplus
# define VIRTUAL virtual
# define OVERRIDE override
# define CONSTEXPR constexpr
# define NOEXCEPT noexcept
# define NULLPTR nullptr
# define IA_MOVE(...) std::move(__VA_ARGS__)
# define DECONST(t, v) const_cast<t>(v)
# define NORETURN [[noreturn]]
#else
# define VIRTUAL
# define OVERRIDE
# define CONSTEXPR const
# define NOEXCEPT
# define NULLPTR NULL
# define IA_MOVE(...) (__VA_ARGS__)
# define DECONST(t, v) ((t) (v))
# define NORETURN
#endif
#define DEFINE_TYPE(t, v) typedef v t
#define FORWARD_DECLARE(t, i) t i
#ifdef __cplusplus
# define PURE_VIRTUAL(f) VIRTUAL f = 0
#endif
// -----------------------------------------------------------------------------
// Attributes & Compiler Intrinsics
// -----------------------------------------------------------------------------
#define INLINE inline
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
# define ALWAYS_INLINE __attribute__((always_inline)) inline
#elif defined(_MSC_VER)
# define ALWAYS_INLINE __forceinline
#else
# define ALWAYS_INLINE inline
#endif
#define UNUSED(v) ((void) v);
#if defined(__cplusplus)
# define NO_DISCARD(s) [[nodiscard(s)]]
# define B_LIKELY(cond) (cond) [[likely]]
# define B_UNLIKELY(cond) (cond) [[unlikely]]
#else
# define NO_DISCARD(s)
# define B_LIKELY(cond) (cond)
# define B_UNLIKELY(cond) (cond)
#endif
#define __INTERNAL_IA_STRINGIFY(value) #value
#define IA_STRINGIFY(value) __INTERNAL_IA_STRINGIFY(value)
#define ALIGN(a) __attribute__((aligned(a)))
// Mark every explicit assembly instruction as volatile
#define ASM(...) __asm__ volatile(__VA_ARGS__)
#ifndef NULL
# define NULL 0
#endif
#undef TRUE
#undef FALSE
#ifdef __cplusplus
# define FALSE false
# define TRUE true
#else
# define FALSE 0
# define TRUE 1
#endif
// Parameter Annotations
#define IN
#define OUT
#define INOUT
#undef VOID
// -----------------------------------------------------------------------------
// Extern C Handling
// -----------------------------------------------------------------------------
#ifdef __cplusplus
# define IA_EXTERN_C_BEGIN \
extern "C" \
{
# define IA_EXTERN_C_END }
# define C_DECL(f) extern "C" f
#else
# define IA_EXTERN_C_BEGIN
# define IA_EXTERN_C_END
# define C_DECL(f) f
#endif
// -----------------------------------------------------------------------------
// Utilities
// -----------------------------------------------------------------------------
#ifdef __cplusplus
# define CAST(v, t) (static_cast<t>(v))
# define REINTERPRET(v, t) (reinterpret_cast<t>(v))
#else
# define CAST(v, t) ((t) (v))
#endif
// Templates and Aliases
#ifdef __cplusplus
# define ALIAS_FUNCTION(alias, function) \
template<typename... Args> auto alias(Args &&...args) -> decltype(f(std::forward<Args>(args)...)) \
{ \
return function(std::forward<Args>(args)...); \
}
# define ALIAS_TEMPLATE_FUNCTION(t, alias, function) \
template<typename t, typename... Args> \
auto alias(Args &&...args) -> decltype(function<t>(std::forward<Args>(args)...)) \
{ \
return function<t>(std::forward<Args>(args)...); \
}
#endif
// Assertions
#define IA_RELEASE_ASSERT(v) assert((v))
#define IA_RELEASE_ASSERT_MSG(v, m) assert((v) && m)
#if defined(__DEBUG_MODE__)
# define IA_ASSERT(v) IA_RELEASE_ASSERT(v)
# define IA_ASSERT_MSG(v, m) IA_RELEASE_ASSERT_MSG(v, m)
#else
# define IA_ASSERT(v)
# define IA_ASSERT_MSG(v, m)
#endif
#define IA_ASSERT_EQ(a, b) IA_ASSERT((a) == (b))
#define IA_ASSERT_GE(a, b) IA_ASSERT((a) >= (b))
#define IA_ASSERT_LE(a, b) IA_ASSERT(a <= b)
#define IA_ASSERT_LT(a, b) IA_ASSERT(a < b)
#define IA_ASSERT_GT(a, b) IA_ASSERT(a > b)
#define IA_ASSERT_IMPLIES(a, b) IA_ASSERT(!(a) || (b))
#define IA_ASSERT_NOT_NULL(v) IA_ASSERT(((v) != NULLPTR))
#define IA_UNREACHABLE(msg) IA_RELEASE_ASSERT_MSG(FALSE, "Unreachable code: " msg)
#define IA_TRY(expr) \
__extension__({ \
auto _ia_res = (expr); \
if (!_ia_res) \
{ \
return tl::make_unexpected(std::move(_ia_res.error())); \
} \
std::move(*_ia_res); \
})
#define IA_CONCAT_IMPL(x, y) x##y
#define IA_CONCAT(x, y) IA_CONCAT_IMPL(x, y)
#define IA_UNIQUE_NAME(prefix) IA_CONCAT(prefix, __LINE__)
// -----------------------------------------------------------------------------
// Limits & Versioning
// -----------------------------------------------------------------------------
#ifdef __cplusplus
# define IA_MAX_POSSIBLE_SIZE (static_cast<SIZE_T>(0x7FFFFFFFFFFFF))
#else
# define IA_MAX_POSSIBLE_SIZE ((SIZE_T) (0x7FFFFFFFFFFFF))
#endif
#define IA_MAX_PATH_LENGTH 4096
#define IA_MAX_STRING_LENGTH (IA_MAX_POSSIBLE_SIZE >> 8)
#define IA_VERSION_TYPE uint64_t
#define IA_MAKE_VERSION(major, minor, patch) \
((((uint64_t) (major) & 0xFFFFFF) << 40) | (((uint64_t) (minor) & 0xFFFFFF) << 16) | ((uint64_t) (patch) & 0xFFFF))
// -----------------------------------------------------------------------------
// DLL Export/Import
// -----------------------------------------------------------------------------
#if defined(_MSC_VER)
# define IA_DLL_EXPORT __declspec(dllexport)
# define IA_DLL_IMPORT __declspec(dllimport)
#elif defined(__GNUC__)
# define IA_DLL_EXPORT __attribute__((visibility("default")))
# define IA_DLL_IMPORT
#else
# define IA_DLL_EXPORT
# define IA_DLL_IMPORT
#endif
// -----------------------------------------------------------------------------
// Console Colors (ANSI Escape Codes)
// -----------------------------------------------------------------------------
#define __CC_BLACK "\033[30m"
#define __CC_RED "\033[31m"
#define __CC_GREEN "\033[32m"
#define __CC_YELLOW "\033[33m"
#define __CC_BLUE "\033[34m"
#define __CC_MAGENTA "\033[35m"
#define __CC_CYAN "\033[36m"
#define __CC_WHITE "\033[37m"
#define __CC_DEFAULT "\033[39m"
// -------------------------------------------------------------------------
// Base Types
// -------------------------------------------------------------------------
typedef void VOID;
#ifdef __cplusplus
typedef bool BOOL;
#else
typedef _Bool BOOL;
#endif
typedef char CHAR;
typedef uint16_t CHAR16;
typedef int8_t INT8;
typedef int16_t INT16;
typedef int32_t INT32;
typedef int64_t INT64;
typedef uint8_t UINT8;
typedef uint16_t UINT16;
typedef uint32_t UINT32;
typedef uint64_t UINT64;
typedef float FLOAT32;
typedef double FLOAT64;
typedef INT32 INT;
typedef UINT32 UINT;
typedef size_t SIZE_T;
#ifdef __cplusplus
typedef std::make_signed_t<size_t> SSIZE_T;
typedef std::align_val_t ALIGN_T;
#else
typedef ptrdiff_t SSIZE_T;
typedef size_t ALIGN_T;
#endif
// -------------------------------------------------------------------------
// Pointer Types
// -------------------------------------------------------------------------
typedef VOID *PVOID;
typedef BOOL *PBOOL;
typedef CHAR *PCHAR;
typedef CHAR16 *PCHAR16;
typedef INT8 *PINT8;
typedef INT16 *PINT16;
typedef INT32 *PINT32;
typedef INT64 *PINT64;
typedef UINT8 *PUINT8;
typedef UINT16 *PUINT16;
typedef UINT32 *PUINT32;
typedef UINT64 *PUINT64;
typedef INT *PINT;
typedef UINT *PUINT;
typedef SIZE_T *PSIZE;
typedef SSIZE_T *PSSIZE;
typedef FLOAT32 *PFLOAT32;
typedef FLOAT64 *PFLOAT64;
// -------------------------------------------------------------------------
// Const Pointer Types
// -------------------------------------------------------------------------
typedef CONST VOID *PCVOID;
typedef CONST BOOL *PCBOOL;
typedef CONST CHAR *PCCHAR;
typedef CONST CHAR16 *PCCHAR16;
typedef CONST INT8 *PCINT8;
typedef CONST INT16 *PCINT16;
typedef CONST INT32 *PCINT32;
typedef CONST INT64 *PCINT64;
typedef CONST UINT8 *PCUINT8;
typedef CONST UINT16 *PCUINT16;
typedef CONST UINT32 *PCUINT32;
typedef CONST UINT64 *PCUINT64;
typedef CONST INT *PCINT;
typedef CONST UINT *PCUINT;
typedef CONST SIZE_T *PCSIZE;
typedef CONST SSIZE_T *PCSSIZE;
typedef CONST FLOAT32 *PCFLOAT32;
typedef CONST FLOAT64 *PCFLOAT64;
// -------------------------------------------------------------------------
// GUID Structure
// -------------------------------------------------------------------------
typedef struct _IA_GUID
{
UINT32 Data1;
UINT16 Data2;
UINT16 Data3;
UINT8 Data4[8];
#ifdef __cplusplus
bool operator==(const _IA_GUID &other) const
{
return __builtin_memcmp(this, &other, sizeof(_IA_GUID)) == 0;
}
bool operator!=(const _IA_GUID &other) const
{
return !(*this == other);
}
#endif
} GUID;
STATIC INLINE BOOL IA_GUID_Equals(CONST GUID *a, CONST GUID *b)
{
if (a == NULLPTR || b == NULLPTR)
return FALSE;
return memcmp(a, b, sizeof(GUID)) == 0;
}
// -------------------------------------------------------------------------
// Numeric Constants
// -------------------------------------------------------------------------
#ifdef __cplusplus
STATIC CONSTEXPR FLOAT32 FLOAT32_EPSILON = std::numeric_limits<FLOAT32>::epsilon();
STATIC CONSTEXPR FLOAT64 FLOAT64_EPSILON = std::numeric_limits<FLOAT64>::epsilon();
#else
STATIC CONST FLOAT32 FLOAT32_EPSILON = FLT_EPSILON;
STATIC CONST FLOAT64 FLOAT64_EPSILON = DBL_EPSILON;
#endif
// -------------------------------------------------------------------------
// Platform Detection
// -------------------------------------------------------------------------
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
# ifdef _WIN64
# define IA_PLATFORM_WIN64 1
# define IA_PLATFORM_WINDOWS 1
# else
# define IA_PLATFORM_WIN32 1
# define IA_PLATFORM_WINDOWS 1
# endif
#elif __APPLE__
# include <TargetConditionals.h>
# if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR || TARGET_OS_MACCATALYST
# define IA_PLATFORM_IOS 1
# elif TARGET_OS_MAC
# define IA_PLATFORM_MAC 1
# endif
# define IA_PLATFORM_UNIX 1
#elif __ANDROID__
# define IA_PLATFORM_ANDROID 1
# define IA_PLATFORM_LINUX 1
# define IA_PLATFORM_UNIX 1
#elif __linux__
# define IA_PLATFORM_LINUX 1
# define IA_PLATFORM_UNIX 1
#elif __unix__
# define IA_PLATFORM_UNIX 1
#endif
#if IA_PLATFORM_WIN32 || IA_PLATFORM_WIN64
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# ifndef NOMINMAX
# define NOMINMAX
# endif
# include <windows.h>
#elif IA_PLATFORM_UNIX
# include <unistd.h>
# include <sys/wait.h>
# include <spawn.h>
#endif
#if IA_CHECK(IA_PLATFORM_WIN64) || IA_CHECK(IA_PLATFORM_UNIX)
# define IA_CORE_PLATFORM_FEATURES 1
#else
# define IA_CORE_PLATFORM_FEATURES 0
# warning "IACore Unsupported Platform: Platform specific features will be disabled"
#endif
// -------------------------------------------------------------------------
// C++ Containers and Helpers
// -------------------------------------------------------------------------
#ifdef __cplusplus
template<typename _function_type> using Function = std::function<_function_type>;
template<typename _value_type> using InitializerList = std::initializer_list<_value_type>;
template<typename _value_type, SIZE_T count> using Array = std::array<_value_type, count>;
template<typename _value_type> using Vector = std::vector<_value_type>;
template<typename _value_type> using Optional = std::optional<_value_type>;
template<typename _key_type> using UnorderedSet = ankerl::unordered_dense::set<_key_type>;
template<typename _key_type, typename _value_type>
using UnorderedMap = ankerl::unordered_dense::map<_key_type, _value_type>;
using String = std::string;
using StringView = std::string_view;
using StringStream = std::stringstream;
template<typename... Args> STATIC String BuildString(Args &&...args)
{
std::stringstream ss;
(ss << ... << args);
return ss.str();
}
#endif

View File

@ -0,0 +1,211 @@
// 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/>.
#pragma once
#include <IACore/PCH.hpp>
namespace IACore {
class Process {
public:
// ---------------------------------------------------------------------
// Static One-Shot Execution
// ---------------------------------------------------------------------
// Returns Exit Code or Error String
// callback receives distinct lines of output (stdout + stderr merged)
STATIC tl::expected<INT32, String> Run(
CONST String& cmd,
CONST String& args,
Function<VOID(StringView)> onOutputLine
) {
#if IA_PLATFORM_WINDOWS
return RunWindows(cmd, args, onOutputLine);
#else
return RunPosix(cmd, args, onOutputLine);
#endif
}
private:
// ---------------------------------------------------------------------
// Output Buffering Helper
// Splits raw chunks into lines, preserving partial lines across chunks
// ---------------------------------------------------------------------
struct LineBuffer {
String accumulator;
Function<VOID(StringView)>& callback;
VOID Append(const char* data, SIZE_T size) {
SIZE_T start = 0;
for (SIZE_T i = 0; i < size; ++i) {
if (data[i] == '\n' || data[i] == '\r') {
// Flush accumulator + current chunk
if (!accumulator.empty()) {
accumulator.append(data + start, i - start);
if (!accumulator.empty()) callback(accumulator);
accumulator.clear();
} else {
// Zero copy optimization for pure lines in one chunk
if (i > start) callback(StringView(data + start, i - start));
}
// Skip \r\n sequence if needed, or just start next
if (data[i] == '\r' && i + 1 < size && data[i+1] == '\n') i++;
start = i + 1;
}
}
// Save remaining partial line
if (start < size) {
accumulator.append(data + start, size - start);
}
}
VOID Flush() {
if (!accumulator.empty()) {
callback(accumulator);
accumulator.clear();
}
}
};
// ---------------------------------------------------------------------
// Windows Implementation
// ---------------------------------------------------------------------
#if IA_PLATFORM_WINDOWS
STATIC tl::expected<INT32, String> RunWindows(CONST String& cmd, CONST String& args, Function<VOID(StringView)> cb) {
SECURITY_ATTRIBUTES saAttr = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; // Allow inheritance
HANDLE hRead = NULL, hWrite = NULL;
if (!CreatePipe(&hRead, &hWrite, &saAttr, 0))
return tl::make_unexpected("Failed to create pipe");
// Ensure the read handle to the pipe for STDOUT is NOT inherited
if (!SetHandleInformation(hRead, HANDLE_FLAG_INHERIT, 0))
return tl::make_unexpected("Failed to secure pipe handles");
STARTUPINFOA si = { sizeof(STARTUPINFOA) };
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdOutput = hWrite;
si.hStdError = hWrite; // Merge stderr
si.hStdInput = NULL; // No input
PROCESS_INFORMATION pi = { 0 };
// Windows command line needs to be mutable and concatenated
String commandLine = BuildString("\"", cmd, "\" ", args);
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!
CloseHandle(hWrite);
if (!success) {
CloseHandle(hRead);
return tl::make_unexpected(String("CreateProcess failed: ") + std::to_string(GetLastError()));
}
// Read Loop
LineBuffer lineBuf{ "", cb };
DWORD bytesRead;
CHAR buffer[4096];
while (ReadFile(hRead, buffer, sizeof(buffer), &bytesRead, NULL) && bytesRead != 0) {
lineBuf.Append(buffer, bytesRead);
}
lineBuf.Flush();
// NOW we wait for exit code
DWORD exitCode = 0;
WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, &exitCode);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(hRead);
return static_cast<INT32>(exitCode);
}
#endif
// ---------------------------------------------------------------------
// POSIX (Linux/Mac) Implementation
// ---------------------------------------------------------------------
#if IA_PLATFORM_UNIX
STATIC tl::expected<INT32, String> RunPosix(CONST String& cmd, CONST String& args, Function<VOID(StringView)> cb) {
int pipefd[2];
if (pipe(pipefd) == -1) return tl::make_unexpected("Failed to create pipe");
pid_t pid = fork();
if (pid == -1) {
return tl::make_unexpected("Failed to fork process");
}
else if (pid == 0) {
// --- Child Process ---
close(pipefd[0]); // Close read end
// Redirect stdout/stderr to pipe
dup2(pipefd[1], STDOUT_FILENO);
dup2(pipefd[1], STDERR_FILENO);
close(pipefd[1]); // Close original write end
// Prepare Args. execvp requires an array of char*
// This is quick and dirty splitting.
// In a real engine you might pass args as a vector<string>.
std::vector<char*> argv;
std::string cmdStr = cmd; // copy to modify if needed
argv.push_back(cmdStr.data());
// Split args string by space (simplistic)
// Better: Pass vector<string> to Run()
std::string argsCopy = args;
char* token = strtok(argsCopy.data(), " ");
while (token) {
argv.push_back(token);
token = strtok(NULL, " ");
}
argv.push_back(nullptr);
execvp(argv[0], argv.data());
_exit(127); // If execvp fails
}
else {
// --- Parent Process ---
close(pipefd[1]); // Close write end
LineBuffer lineBuf{ "", cb };
char buffer[4096];
ssize_t count;
while ((count = read(pipefd[0], buffer, sizeof(buffer))) > 0) {
lineBuf.Append(buffer, count);
}
lineBuf.Flush();
close(pipefd[0]);
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status)) return WEXITSTATUS(status);
return -1; // Crashed or killed
}
}
#endif
};
}

View File

@ -0,0 +1,159 @@
// 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/>.
#pragma once
#include <IACore/PCH.hpp>
#include <algorithm>
namespace IACore
{
class Utils
{
public:
INLINE STATIC String BinaryToHexString(std::span<const UINT8> data)
{
STATIC CONSTEXPR char LUT[] = "0123456789ABCDEF";
String res;
res.reserve(data.size() * 2);
for (UINT8 b : data)
{
res.push_back(LUT[(b >> 4) & 0x0F]);
res.push_back(LUT[b & 0x0F]);
}
return res;
}
INLINE STATIC tl::expected<Vector<UINT8>, String> HexStringToBinary(CONST StringView &hex)
{
if (hex.size() % 2 != 0)
{
return tl::make_unexpected(String("Hex string must have even length"));
}
Vector<UINT8> out;
out.reserve(hex.size() / 2);
for (SIZE_T i = 0; i < hex.size(); i += 2)
{
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';
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
return -1;
};
int h = fromHexChar(high);
int l = fromHexChar(low);
if (h == -1 || l == -1)
{
return tl::make_unexpected(String("Invalid hex character found"));
}
out.push_back(CAST((h << 4) | l, UINT8));
}
return out;
}
template<typename Range> INLINE STATIC VOID Sort(Range &&range)
{
std::ranges::sort(range);
}
template<typename Range, typename T> INLINE STATIC auto BinarySearchLeft(Range &&range, CONST T &value)
{
return std::ranges::lower_bound(range, value);
}
template<typename Range, typename T> INLINE STATIC auto BinarySearchRight(Range &&range, CONST T &value)
{
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)
{
// Use Ankerl's high-speed hasher for the individual type
// This automatically handles ints, floats, strings, etc. efficiently.
auto hasher = ankerl::unordered_dense::hash<T>();
UINT64 h = hasher(v);
// 0x9e3779b97f4a7c15 is the 64-bit golden ratio (phi) approximation
// This spreads bits to avoid collisions in the hash table.
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
return seed;
}
// -------------------------------------------------------------------------
// F;pat Hasher
// Allows: IACore::ComputeHashFlat(x, y, z);
// -------------------------------------------------------------------------
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;
}
};
} // namespace IACore
// -----------------------------------------------------------------------------
// MACRO: IA_MAKE_HASHABLE
//
// Injects the specialization for ankerl::unordered_dense::hash.
//
// Usage:
// 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__); \
} \
};