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

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