Tests 1/2
This commit is contained in:
2
Src/CMakeLists.txt
Normal file
2
Src/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
add_subdirectory(IACore/)
|
||||
29
Src/IACore/CMakeLists.txt
Normal file
29
Src/IACore/CMakeLists.txt
Normal 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."
|
||||
)
|
||||
24
Src/IACore/imp/cpp/IACore.cpp
Normal file
24
Src/IACore/imp/cpp/IACore.cpp
Normal 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
|
||||
{
|
||||
|
||||
}
|
||||
101
Src/IACore/inc/IACore/BinaryReader.hpp
Normal file
101
Src/IACore/inc/IACore/BinaryReader.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
100
Src/IACore/inc/IACore/BinaryWriter.hpp
Normal file
100
Src/IACore/inc/IACore/BinaryWriter.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
184
Src/IACore/inc/IACore/DynamicLib.hpp
Normal file
184
Src/IACore/inc/IACore/DynamicLib.hpp
Normal 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
|
||||
};
|
||||
}
|
||||
123
Src/IACore/inc/IACore/Environment.hpp
Normal file
123
Src/IACore/inc/IACore/Environment.hpp
Normal 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
|
||||
174
Src/IACore/inc/IACore/File.hpp
Normal file
174
Src/IACore/inc/IACore/File.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
28
Src/IACore/inc/IACore/IACore.hpp
Normal file
28
Src/IACore/inc/IACore/IACore.hpp
Normal 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
|
||||
261
Src/IACore/inc/IACore/IATest.hpp
Normal file
261
Src/IACore/inc/IACore/IATest.hpp
Normal 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
|
||||
527
Src/IACore/inc/IACore/PCH.hpp
Normal file
527
Src/IACore/inc/IACore/PCH.hpp
Normal 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
|
||||
211
Src/IACore/inc/IACore/Process.hpp
Normal file
211
Src/IACore/inc/IACore/Process.hpp
Normal 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
|
||||
};
|
||||
}
|
||||
159
Src/IACore/inc/IACore/Utils.hpp
Normal file
159
Src/IACore/inc/IACore/Utils.hpp
Normal 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__); \
|
||||
} \
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user