From 601b573983b8e7ebb3559760d57865769a82428b Mon Sep 17 00:00:00 2001 From: dev0 Date: Sun, 25 Jan 2026 18:26:59 +0530 Subject: [PATCH] Complete Core --- CMake/FindDeps.cmake | 17 +- Docs/CODING-STYLE.md | 313 +++ Src/IACore/CMakeLists.txt | 2 +- Src/IACore/imp/cpp/AsyncOps.cpp | 83 +- Src/IACore/imp/cpp/CLI.cpp | 5 +- Src/IACore/imp/cpp/DataOps.cpp | 209 +- Src/IACore/imp/cpp/FileOps.cpp | 157 +- Src/IACore/imp/cpp/Http/Client.cpp | 74 +- Src/IACore/imp/cpp/Http/Common.cpp | 20 +- Src/IACore/imp/cpp/Http/Server.cpp | 114 +- Src/IACore/imp/cpp/IACore.cpp | 62 +- Src/IACore/imp/cpp/IPC.cpp | 270 +-- Src/IACore/imp/cpp/JSON.cpp | 5 +- Src/IACore/imp/cpp/Logger.cpp | 42 +- Src/IACore/imp/cpp/Platform.cpp | 214 +- Src/IACore/imp/cpp/ProcessOps.cpp | 162 +- Src/IACore/imp/cpp/SIMD.cpp | 5 +- Src/IACore/imp/cpp/SocketOps.cpp | 34 +- Src/IACore/imp/cpp/StreamReader.cpp | 24 +- Src/IACore/imp/cpp/StreamWriter.cpp | 51 +- Src/IACore/imp/cpp/StringOps.cpp | 50 +- Src/IACore/imp/cpp/Utils.cpp | 184 +- Src/IACore/imp/cpp/XML.cpp | 124 +- Src/IACore/inc/IACore/ADT/RingBuffer.hpp | 139 +- Src/IACore/inc/IACore/AsyncOps.hpp | 41 +- Src/IACore/inc/IACore/CLI.hpp | 12 +- Src/IACore/inc/IACore/DataOps.hpp | 26 +- Src/IACore/inc/IACore/DynamicLib.hpp | 52 +- Src/IACore/inc/IACore/Environment.hpp | 25 +- Src/IACore/inc/IACore/FileOps.hpp | 62 +- Src/IACore/inc/IACore/Http/Client.hpp | 57 +- Src/IACore/inc/IACore/Http/Common.hpp | 20 +- Src/IACore/inc/IACore/Http/Server.hpp | 96 +- Src/IACore/inc/IACore/IACore.hpp | 87 +- Src/IACore/inc/IACore/IATest.hpp | 74 +- Src/IACore/inc/IACore/IPC.hpp | 119 +- Src/IACore/inc/IACore/JSON.hpp | 87 +- Src/IACore/inc/IACore/Logger.hpp | 63 +- Src/IACore/inc/IACore/PCH.hpp | 158 +- Src/IACore/inc/IACore/Platform.hpp | 47 +- Src/IACore/inc/IACore/ProcessOps.hpp | 34 +- Src/IACore/inc/IACore/SIMD.hpp | 433 ++-- Src/IACore/inc/IACore/SocketOps.hpp | 31 +- Src/IACore/inc/IACore/StreamReader.hpp | 37 +- Src/IACore/inc/IACore/StreamWriter.hpp | 32 +- Src/IACore/inc/IACore/StringOps.hpp | 4 +- Src/IACore/inc/IACore/Utils.hpp | 128 +- Src/IACore/inc/IACore/XML.hpp | 28 +- Src/IACore/inc/full_repo_context.txt | 2770 ---------------------- Tests/Unit/Main.cpp | 2 +- Tests/Unit/ProcessOps.cpp | 3 +- 51 files changed, 2110 insertions(+), 4778 deletions(-) create mode 100644 Docs/CODING-STYLE.md delete mode 100644 Src/IACore/inc/full_repo_context.txt diff --git a/CMake/FindDeps.cmake b/CMake/FindDeps.cmake index 88ebb01..a4d20c7 100644 --- a/CMake/FindDeps.cmake +++ b/CMake/FindDeps.cmake @@ -14,6 +14,13 @@ set(ZLIB_COMPAT ON CACHE BOOL "" FORCE) set(ZLIB_ENABLE_TESTS OFF CACHE BOOL "" FORCE) set(WITH_GZFILEOP ON CACHE BOOL "" FORCE) +FetchContent_Declare( + Oxide + GIT_REPOSITORY https://github.com/I-A-S/Oxide + GIT_TAG main + OVERRIDE_FIND_PACKAGE +) + FetchContent_Declare( zlib GIT_REPOSITORY https://github.com/zlib-ng/zlib-ng.git @@ -77,14 +84,6 @@ FetchContent_Declare( -P ${CMAKE_CURRENT_SOURCE_DIR}/CMake/PatchMimalloc.cmake ) -FetchContent_Declare( - tl-expected - GIT_REPOSITORY https://github.com/TartanLlama/expected.git - GIT_TAG v1.3.1 - SYSTEM - EXCLUDE_FROM_ALL -) - FetchContent_Declare( unordered_dense GIT_REPOSITORY https://github.com/martinus/unordered_dense.git @@ -134,7 +133,7 @@ if(NOT TARGET zstd::libzstd) add_library(zstd::libzstd ALIAS libzstd_static) endif() -FetchContent_MakeAvailable(httplib pugixml nlohmann_json glaze simdjson tl-expected unordered_dense mimalloc highway) +FetchContent_MakeAvailable(Oxide httplib pugixml nlohmann_json glaze simdjson unordered_dense mimalloc highway) if(NOT TARGET simdjson::simdjson) add_library(simdjson::simdjson ALIAS simdjson) diff --git a/Docs/CODING-STYLE.md b/Docs/CODING-STYLE.md new file mode 100644 index 0000000..3b34365 --- /dev/null +++ b/Docs/CODING-STYLE.md @@ -0,0 +1,313 @@ +# **IACore - C++ Style Guide** + +This document defines the coding standards for IACore and all IA Projects. +Philosophy: Enforce Rust-like memory safety and discipline within modern C++20. + +--- +### **1. Oxide Usage** + +Usage of [`Oxide`](https://github.com/I-A-S/Oxide) library and its philosophy is mandatory. + +`Oxide` is a single header library and a truncated version of it is listed below. + +```cpp +#define OX_UNUSED(v) (void)(v) + +namespace Oxide { + +// ============================================================================= +// Primitive Types +// ============================================================================= + +using u8 = std::uint8_t; +using u16 = std::uint16_t; +using u32 = std::uint32_t; +using u64 = std::uint64_t; + +using i8 = std::int8_t; +using i16 = std::int16_t; +using i32 = std::int32_t; +using i64 = std::int64_t; + +using f32 = float; +using f64 = double; + +using usize = std::size_t; +using isize = std::ptrdiff_t; + +// ============================================================================= +// Template Types +// ============================================================================= + +template using Const = const T; +template using Mut = T; + +template using Ref = const T &; +template using MutRef = T &; +template using ForwardRef = T &&; + +// ============================================================================= +// Memory & Ownership +// ============================================================================= + +template using Box = std::unique_ptr; +template using Arc = std::shared_ptr; +template using Weak = std::weak_ptr; + +template +[[nodiscard]] inline auto make_box(ForwardRef... args) -> Box { + return std::make_unique(std::forward(args)...); +} + +template +[[nodiscard]] inline auto make_arc(ForwardRef... args) -> Arc { + return std::make_shared(std::forward(args)...); +} + +// ============================================================================= +// Error Handling +// ============================================================================= + +template +using Result = Oxide::Internal::Expected; + +template [[nodiscard]] inline auto fail(ForwardRef error) { + return Oxide::Internal::make_unexpected(std::forward(error)); +} + +template +[[nodiscard]] inline auto fail(Ref> fmt, + ForwardRef... args) { + return Oxide::Internal::make_unexpected( + std::format(fmt, std::forward(args)...)); +} + +// ============================================================================= +// Utilities +// ============================================================================= + +namespace Env { +#if defined(NDEBUG) +constexpr Const IS_DEBUG = false; +constexpr Const IS_RELEASE = true; +#else +constexpr Const IS_DEBUG = true; +constexpr Const IS_RELEASE = false; +#endif +} // namespace Env + +[[noreturn]] inline void +panic(Ref msg, + Ref loc = std::source_location::current()) { + std::cerr << "\n[panic] " << msg << "\n At: " << loc.file_name() + << ":" << loc.line() << "\n"; + std::abort(); +} + +inline void +ensure(Const condition, Ref msg, + Ref loc = std::source_location::current()) { + if (Env::IS_DEBUG && !condition) { + std::cerr << "\n[assert] " << msg << "\n At: " << loc.file_name() + << ":" << loc.line() << "\n"; + std::abort(); + } +} + +using String = std::string; +using StringView = std::string_view; + +template using Option = std::optional; +template using Vec = std::vector; +template using Span = std::span; +template using Pair = std::pair; +template using Array = std::array; + +} // namespace Oxide + +#define OX_TRY_PURE(expr) \ + { \ + auto _ox_res = (expr); \ + if (!_ox_res) { \ + return Oxide::Internal::make_unexpected(std::move(_ox_res.error())); \ + } \ + } + +#define OX_TRY(expr) \ + __extension__({ \ + auto _ox_res = (expr); \ + if (!_ox_res) { \ + return Oxide::Internal::make_unexpected(std::move(_ox_res.error())); \ + } \ + std::move(*_ox_res); \ + }) + +#define OX_TRY_DISCARD(expr) \ + { \ + auto _ox_res = (expr); \ + if (!_ox_res) { \ + return Oxide::Internal::make_unexpected(std::move(_ox_res.error())); \ + } \ + OX_UNUSED(*_ox_res); \ + } + +``` + +Note: no need to explicity use `Oxide::` prefix as Oxide namespace is almost always globally used in IA projects. + +All variables (local, member and parameter/argument) MUST always be wrapped in one of `Const, Mut, Ref, MutRef` or `ForwardRef`. + +> For outer most template of parameters/arguments, use the following Rule to decide between `Ref` or `Const`: +> 1) Is the parameter size <= 64, then use `Const` +> 2) Otherwise use `Ref` + +| Use This | Instead of This | +| :---- | :---- | +| ```Box``` | ```std::unique_ptr``` | +| ```Arc``` | ```std::shared_ptr``` | +| ```Weak``` | ```std::weak_ptr``` | +| ```make_box``` | ```std::make_unique``` | +| ```make_arc``` | ```std::make_shared``` | + +--- + +### **2. Naming Conventions** + +We use **Rust Naming Conventions** to strictly distinguish Types from Values. + +| Element | Style | Example | +| :---- | :---- | :---- | +| **Types / Classes** | PascalCase | ```StreamReader, TcpSocket``` | +| **Functions** | snake_case | ```read_bytes(), create_instance()``` | +| **Variables** | snake_case | ```buffer_size, socket_id``` | +| **Private Members** | m_snake_case | ```m_parser, m_data_size``` | +| **Constants** | SCREAMING_SNAKE | ```MAX_PATH_LENGTH``` | +| **Namespaces** | PascalCase | ```IACore, Oxide, IACore::Env``` | +| **Files** | PascalCase | ```StreamReader.hpp``` | + +**Rule:** Never use Hungarian notation (e.g., iCount, strName) except for the m_ prefix on private members. + +--- + +### **3. Primitives Types** + +Do not use standard C++ primitives (int, long, unsigned int, double, char). +Use the IACore Rust Aliases defined in IACore.hpp. + +| Use This | Instead of This | +| :---- | :---- | +| ```i8, i16, i32, i64``` | ```int8_t, short, int, long long``` | +| ```u8, u16, u32, u64``` | ```uint8_t, unsigned short, unsigned int``` | +| ```f32, f64``` | ```float, double``` | +| ```usize``` | ```size_t, std::size_t``` | +| ```isize``` | ```std::ptrdiff_t``` | + +**Exception:** Use char only for C-string literals or std::format compatibility. + +--- + +### **4. Ownership & Pointers** + +Adhere to strict ownership semantics. **Raw pointers (T\*) are non-owning.** + +* **Box (ia::Box):** Exclusive ownership. Use ia::make_box(). +* **Arc (ia::Arc):** Shared ownership (atomic ref-counting). Use sparingly. +* **Raw Pointers (T\*):** Borrowed, non-owning, mutable view. **Must not be deleted.** +* **Const Pointers (const T\*):** Borrowed, non-owning, read-only view. + +**Forbidden:** + +* new / delete / malloc / free. +* Passing std::shared_ptr by value (unless sharing ownership is the intent). + +--- + +### **5. Error Handling** + +Exceptions are BANNED. +All fallible functions must return ia::Result. +**Correct:** + +```cpp +// Returns value OR error string +auto read_file(const Path& path) -> ia::Result; +``` +Checking Errors (The IA_TRY Macro): +Use IA_TRY to propagate errors (equivalent to Rust's ? operator). + +```cpp +auto process_data() -> Result { + IA_TRY(auto data, read_file("test.txt")); // Returns error if read_file fails + return process(data); +} +``` + +--- + +### **6. Function Signatures** + +Use **Trailing Return Syntax** for all functions (even void returns). This aligns with Rust (fn name() -> type) and handles complex template returns better. + +**Correct:** + +```cpp +auto calculate_sum(u32 a, u32 b) -> u32; +auto do_something() -> void; +``` +**Incorrect:** + +```cpp +u32 calculate_sum(u32 a, u32 b); +void do_something(); +``` +--- + +### **7. Const Correctness and Auto Usage** + +Immutable by Default. + +`auto` keyword is BANNED. + +Variables should be const unless mutation is explicitly required. + +**Correct:** + +```cpp +Const path = "data.bin"; +Const result = process(path); +``` +**Incorrect:** + + +```cpp +String path = "data.bin"; +const auto result = process(path); +``` +--- + +### **8. Macros** + +Avoid Macros. +Use constexpr for constants and inline templates for logic. + +* **Allowed:** IA_TRY, IA_NODISCARD, Platform detection (internal). +* **Banned:** TRUE, FALSE, CONST, IN, OUT. + +### **9. Class Design (The "Safe Wrapper" Pattern)** + +If a class wraps a C-library or raw resource (like simdjson), it must: + +1. Be **Move-Only** (Delete Copy Constructor). +2. Use a **Factory Method** (create()) that returns Result> or Result. +3. Never expose raw handles that outlive the wrapper. + +```cpp +class SafeWrapper { +public: + static auto create() -> Result; + + SafeWrapper(SafeWrapper&&) = default; + SafeWrapper(const SafeWrapper&) = delete; +}; +``` +--- \ No newline at end of file diff --git a/Src/IACore/CMakeLists.txt b/Src/IACore/CMakeLists.txt index 9ffd09e..677c853 100644 --- a/Src/IACore/CMakeLists.txt +++ b/Src/IACore/CMakeLists.txt @@ -30,8 +30,8 @@ target_include_directories(IACore PRIVATE imp/hpp/) target_link_libraries(IACore PUBLIC hwy zlib + Oxide zstd::libzstd - tl::expected glaze::glaze httplib::httplib pugixml::pugixml diff --git a/Src/IACore/imp/cpp/AsyncOps.cpp b/Src/IACore/imp/cpp/AsyncOps.cpp index a3976b8..8f73ad1 100644 --- a/Src/IACore/imp/cpp/AsyncOps.cpp +++ b/Src/IACore/imp/cpp/AsyncOps.cpp @@ -16,20 +16,20 @@ #include namespace IACore { -std::mutex AsyncOps::s_queue_mutex; -std::condition_variable AsyncOps::s_wake_condition; -Vec AsyncOps::s_schedule_workers; -std::deque AsyncOps::s_high_priority_queue; -std::deque AsyncOps::s_normal_priority_queue; +Mut AsyncOps::s_queue_mutex; +Mut AsyncOps::s_wake_condition; +Mut> AsyncOps::s_schedule_workers; +Mut> AsyncOps::s_high_priority_queue; +Mut> AsyncOps::s_normal_priority_queue; -auto AsyncOps::run_task(std::function task) -> void { +auto AsyncOps::run_task(Mut> task) -> void { std::jthread(std::move(task)).detach(); } -auto AsyncOps::initialize_scheduler(u8 worker_count) -> Result { +auto AsyncOps::initialize_scheduler(Mut worker_count) -> Result { if (worker_count == 0) { - const auto hw_concurrency = std::thread::hardware_concurrency(); - u32 threads = 2; + Const hw_concurrency = std::thread::hardware_concurrency(); + Mut threads = 2; if (hw_concurrency > 2) { threads = hw_concurrency - 2; } @@ -40,7 +40,7 @@ auto AsyncOps::initialize_scheduler(u8 worker_count) -> Result { worker_count = static_cast(threads); } - for (u32 i = 0; i < worker_count; ++i) { + for (Mut i = 0; i < worker_count; ++i) { s_schedule_workers.emplace_back(schedule_worker_loop, static_cast(i + 1)); } @@ -49,13 +49,13 @@ auto AsyncOps::initialize_scheduler(u8 worker_count) -> Result { } auto AsyncOps::terminate_scheduler() -> void { - for (auto &worker : s_schedule_workers) { + for (MutRef worker : s_schedule_workers) { worker.request_stop(); } s_wake_condition.notify_all(); - for (auto &worker : s_schedule_workers) { + for (MutRef worker : s_schedule_workers) { if (worker.joinable()) { worker.join(); } @@ -64,15 +64,15 @@ auto AsyncOps::terminate_scheduler() -> void { s_schedule_workers.clear(); } -auto AsyncOps::schedule_task(std::function task, - TaskTag tag, Schedule *schedule, Priority priority) - -> void { +auto AsyncOps::schedule_task(Mut> task, + Const tag, Const schedule, + Const priority) -> void { ensure(!s_schedule_workers.empty(), "Scheduler must be initialized before calling schedule_task"); schedule->counter.fetch_add(1); { - std::lock_guard lock(s_queue_mutex); + Const> lock(s_queue_mutex); if (priority == Priority::High) { s_high_priority_queue.emplace_back( ScheduledTask{tag, schedule, std::move(task)}); @@ -84,11 +84,14 @@ auto AsyncOps::schedule_task(std::function task, s_wake_condition.notify_one(); } -auto AsyncOps::cancel_tasks_of_tag(TaskTag tag) -> void { - std::lock_guard lock(s_queue_mutex); +auto AsyncOps::cancel_tasks_of_tag(Const tag) -> void { + Const> lock(s_queue_mutex); - auto cancel_from_queue = [&](std::deque &queue) { - for (auto it = queue.begin(); it != queue.end(); /* no increment */) { + { + MutRef> queue = s_high_priority_queue; + for (Mut::iterator> it = queue.begin(); + it != queue.end(); + /* no incr */) { if (it->tag == tag) { if (it->schedule_handle->counter.fetch_sub(1) == 1) { it->schedule_handle->counter.notify_all(); @@ -98,21 +101,35 @@ auto AsyncOps::cancel_tasks_of_tag(TaskTag tag) -> void { ++it; } } - }; + } - cancel_from_queue(s_high_priority_queue); - cancel_from_queue(s_normal_priority_queue); + { + MutRef> queue = s_normal_priority_queue; + for (Mut::iterator> it = queue.begin(); + it != queue.end(); + /* no incr */) { + if (it->tag == tag) { + if (it->schedule_handle->counter.fetch_sub(1) == 1) { + it->schedule_handle->counter.notify_all(); + } + it = queue.erase(it); + } else { + ++it; + } + } + } } -auto AsyncOps::wait_for_schedule_completion(Schedule *schedule) -> void { +auto AsyncOps::wait_for_schedule_completion(Const schedule) + -> void { ensure(!s_schedule_workers.empty(), "Scheduler must be initialized before " "calling wait_for_schedule_completion"); while (schedule->counter.load() > 0) { - ScheduledTask task; - bool found_task = false; + Mut task; + Mut found_task = false; { - std::unique_lock lock(s_queue_mutex); + Mut> lock(s_queue_mutex); if (!s_high_priority_queue.empty()) { task = std::move(s_high_priority_queue.front()); s_high_priority_queue.pop_front(); @@ -130,7 +147,7 @@ auto AsyncOps::wait_for_schedule_completion(Schedule *schedule) -> void { task.schedule_handle->counter.notify_all(); } } else { - const auto current_val = schedule->counter.load(); + Const current_val = schedule->counter.load(); if (current_val > 0) { schedule->counter.wait(current_val); } @@ -142,13 +159,13 @@ auto AsyncOps::get_worker_count() -> WorkerId { return static_cast(s_schedule_workers.size()); } -auto AsyncOps::schedule_worker_loop(std::stop_token stop_token, - WorkerId worker_id) -> void { +auto AsyncOps::schedule_worker_loop(Const stop_token, + Const worker_id) -> void { while (!stop_token.stop_requested()) { - ScheduledTask task; - bool found_task = false; + Mut task; + Mut found_task = false; { - std::unique_lock lock(s_queue_mutex); + Mut> lock(s_queue_mutex); s_wake_condition.wait(lock, [&stop_token] { return !s_high_priority_queue.empty() || diff --git a/Src/IACore/imp/cpp/CLI.cpp b/Src/IACore/imp/cpp/CLI.cpp index 8e2c59b..4616c3b 100644 --- a/Src/IACore/imp/cpp/CLI.cpp +++ b/Src/IACore/imp/cpp/CLI.cpp @@ -16,11 +16,12 @@ #include namespace IACore { -CLIParser::CLIParser(Span args) : m_arg_list(args) { +CLIParser::CLIParser(Const>> args) : m_arg_list(args) { m_current_arg = m_arg_list.begin(); // Skip executable path - if (m_current_arg != m_arg_list.end()) + if (m_current_arg != m_arg_list.end()) { m_current_arg++; + } } } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/imp/cpp/DataOps.cpp b/Src/IACore/imp/cpp/DataOps.cpp index f24c366..9810196 100644 --- a/Src/IACore/imp/cpp/DataOps.cpp +++ b/Src/IACore/imp/cpp/DataOps.cpp @@ -31,46 +31,46 @@ namespace IACore { template -[[nodiscard]] inline auto read_unaligned(const u8 *ptr) -> T { - T v; +[[nodiscard]] inline auto read_unaligned(Const *> ptr) -> T { + Mut v; std::memcpy(&v, ptr, sizeof(T)); return v; } struct Crc32Tables { - u32 table[8][256] = {}; + Mut table[8][256] = {}; consteval Crc32Tables() { - constexpr u32 T = 0x82F63B78; + constexpr Const T = 0x82F63B78; - for (u32 i = 0; i < 256; i++) { - u32 crc = i; - for (i32 j = 0; j < 8; j++) { + for (Mut i = 0; i < 256; i++) { + Mut crc = i; + for (Mut j = 0; j < 8; j++) { crc = (crc >> 1) ^ ((crc & 1) ? T : 0); } table[0][i] = crc; } - for (i32 i = 0; i < 256; i++) { - for (i32 slice = 1; slice < 8; slice++) { - const u32 prev = table[slice - 1][i]; + for (Mut i = 0; i < 256; i++) { + for (Mut slice = 1; slice < 8; slice++) { + Const prev = table[slice - 1][i]; table[slice][i] = (prev >> 8) ^ table[0][prev & 0xFF]; } } } }; -static constexpr Crc32Tables CRC32_TABLES{}; +static constexpr Const CRC32_TABLES{}; #if IA_ARCH_X64 -inline auto crc32_x64_hw(Span data) -> u32 { - const u8 *p = data.data(); +inline auto crc32_x64_hw(Ref>> data) -> u32 { + Mut p = data.data(); - u32 crc = 0xFFFFFFFF; - usize len = data.size(); + Mut crc = 0xFFFFFFFF; + Mut len = data.size(); while (len >= 8) { - const u64 chunk = read_unaligned(p); + Const chunk = read_unaligned(p); crc = static_cast(_mm_crc32_u64(static_cast(crc), chunk)); p += 8; len -= 8; @@ -85,15 +85,15 @@ inline auto crc32_x64_hw(Span data) -> u32 { #endif #if IA_ARCH_ARM64 -__attribute__((target("+crc"))) inline auto crc32_arm64_hw(Span data) - -> u32 { - const u8 *p = data.data(); +__attribute__((target("+crc"))) inline auto +crc32_arm64_hw(Ref>> data) -> u32 { + Mut p = data.data(); - u32 crc = 0xFFFFFFFF; - usize len = data.size(); + Mut crc = 0xFFFFFFFF; + Mut len = data.size(); while (len >= 8) { - const u64 chunk = read_unaligned(p); + Const chunk = read_unaligned(p); crc = __crc32cd(crc, chunk); p += 8; len -= 8; @@ -107,14 +107,14 @@ __attribute__((target("+crc"))) inline auto crc32_arm64_hw(Span data) } #endif -inline auto crc32_software_slice8(Span data) -> u32 { - const u8 *p = data.data(); - u32 crc = 0xFFFFFFFF; - usize len = data.size(); +inline auto crc32_software_slice8(Ref>> data) -> u32 { + Mut p = data.data(); + Mut crc = 0xFFFFFFFF; + Mut len = data.size(); while (len >= 8) { - const u32 term1 = crc ^ read_unaligned(p); - const u32 term2 = read_unaligned(p + 4); + Const term1 = crc ^ read_unaligned(p); + Const term2 = read_unaligned(p + 4); crc = CRC32_TABLES.table[7][term1 & 0xFF] ^ CRC32_TABLES.table[6][(term1 >> 8) & 0xFF] ^ @@ -136,10 +136,9 @@ inline auto crc32_software_slice8(Span data) -> u32 { return ~crc; } -auto DataOps::crc32(Span data) -> u32 { +auto DataOps::crc32(Ref>> data) -> u32 { #if IA_ARCH_X64 // IACore mandates AVX2 so no need to check - // for Platform::GetCapabilities().HardwareCRC32 return crc32_x64_hw(data); #elif IA_ARCH_ARM64 if (Platform::GetCapabilities().HardwareCRC32) { @@ -149,41 +148,38 @@ auto DataOps::crc32(Span data) -> u32 { return crc32_software_slice8(data); } -// ============================================================================= -// xxHash -// ============================================================================= +constexpr Const XXH_PRIME32_1 = 0x9E3779B1U; +constexpr Const XXH_PRIME32_2 = 0x85EBCA77U; +constexpr Const XXH_PRIME32_3 = 0xC2B2AE3DU; +constexpr Const XXH_PRIME32_4 = 0x27D4EB2FU; +constexpr Const XXH_PRIME32_5 = 0x165667B1U; -constexpr u32 XXH_PRIME32_1 = 0x9E3779B1U; -constexpr u32 XXH_PRIME32_2 = 0x85EBCA77U; -constexpr u32 XXH_PRIME32_3 = 0xC2B2AE3DU; -constexpr u32 XXH_PRIME32_4 = 0x27D4EB2FU; -constexpr u32 XXH_PRIME32_5 = 0x165667B1U; - -inline auto xxh32_round(u32 seed, u32 input) -> u32 { +inline auto xxh32_round(Mut seed, Const input) -> u32 { seed += input * XXH_PRIME32_2; seed = std::rotl(seed, 13); seed *= XXH_PRIME32_1; return seed; } -auto DataOps::hash_xxhash(const String &string, u32 seed) -> u32 { - return hash_xxhash(Span(reinterpret_cast(string.data()), - string.length()), - seed); +auto DataOps::hash_xxhash(Ref string, Const seed) -> u32 { + return hash_xxhash( + Span>(reinterpret_cast(string.data()), + string.length()), + seed); } -auto DataOps::hash_xxhash(Span data, u32 seed) -> u32 { - const u8 *p = data.data(); - const u8 *const b_end = p + data.size(); - u32 h32{}; +auto DataOps::hash_xxhash(Ref>> data, Const seed) -> u32 { + Mut p = data.data(); + Const b_end = p + data.size(); + Mut h32{}; if (data.size() >= 16) { - const u8 *const limit = b_end - 16; + Const limit = b_end - 16; - u32 v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2; - u32 v2 = seed + XXH_PRIME32_2; - u32 v3 = seed + 0; - u32 v4 = seed - XXH_PRIME32_1; + Mut v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2; + Mut v2 = seed + XXH_PRIME32_2; + Mut v3 = seed + 0; + Mut v4 = seed - XXH_PRIME32_1; do { v1 = xxh32_round(v1, read_unaligned(p)); @@ -205,7 +201,7 @@ auto DataOps::hash_xxhash(Span data, u32 seed) -> u32 { h32 += static_cast(data.size()); while (p + 4 <= b_end) { - const auto t = read_unaligned(p) * XXH_PRIME32_3; + Const t = read_unaligned(p) * XXH_PRIME32_3; h32 += t; h32 = std::rotl(h32, 17) * XXH_PRIME32_4; p += 4; @@ -225,49 +221,39 @@ auto DataOps::hash_xxhash(Span data, u32 seed) -> u32 { return h32; } -// ============================================================================= -// FNV-1a -// ============================================================================= +constexpr Const FNV1A_32_PRIME = 0x01000193; +constexpr Const FNV1A_32_OFFSET = 0x811c9dc5; -constexpr u32 FNV1A_32_PRIME = 0x01000193; -constexpr u32 FNV1A_32_OFFSET = 0x811c9dc5; - -auto DataOps::hash_fnv1a(const String &string) -> u32 { - u32 hash = FNV1A_32_OFFSET; - for (const char c : string) { +auto DataOps::hash_fnv1a(Ref string) -> u32 { + Mut hash = FNV1A_32_OFFSET; + for (Const c : string) { hash ^= static_cast(c); hash *= FNV1A_32_PRIME; } return hash; } -auto DataOps::hash_fnv1a(Span data) -> u32 { - u32 hash = FNV1A_32_OFFSET; - const auto *ptr = data.data(); +auto DataOps::hash_fnv1a(Ref>> data) -> u32 { + Mut hash = FNV1A_32_OFFSET; + Const ptr = data.data(); - for (usize i = 0; i < data.size(); ++i) { + for (Mut i = 0; i < data.size(); ++i) { hash ^= ptr[i]; hash *= FNV1A_32_PRIME; } return hash; } -// ============================================================================= -// Compression -// ============================================================================= - -auto DataOps::detect_compression(Span data) -> CompressionType { +auto DataOps::detect_compression(Const>> data) + -> CompressionType { if (data.size() < 2) { return CompressionType::None; } - // Check for GZIP Magic Number (0x1F 0x8B) if (data[0] == 0x1F && data[1] == 0x8B) { return CompressionType::Gzip; } - // Check for ZLIB Magic Number (starts with 0x78) - // 0x78 = Deflate compression with 32k window size if (data[0] == 0x78 && (data[1] == 0x01 || data[1] == 0x9C || data[1] == 0xDA)) { return CompressionType::Zlib; @@ -276,13 +262,12 @@ auto DataOps::detect_compression(Span data) -> CompressionType { return CompressionType::None; } -auto DataOps::zlib_inflate(Span data) -> Result> { - z_stream zs{}; +auto DataOps::zlib_inflate(Ref>> data) -> Result> { + Mut zs{}; zs.zalloc = Z_NULL; zs.zfree = Z_NULL; zs.opaque = Z_NULL; - // 15 + 32 = Auto-detect Gzip or Zlib if (inflateInit2(&zs, 15 + 32) != Z_OK) { return fail("Failed to initialize zlib inflate"); } @@ -290,20 +275,19 @@ auto DataOps::zlib_inflate(Span data) -> Result> { zs.next_in = const_cast(data.data()); zs.avail_in = static_cast(data.size()); - Vec out_buffer; - // Start with 2x input size. - const usize guess_size = + Mut> out_buffer; + Const guess_size = data.size() < 1024 ? data.size() * 4 : data.size() * 2; out_buffer.resize(guess_size); zs.next_out = reinterpret_cast(out_buffer.data()); zs.avail_out = static_cast(out_buffer.size()); - int ret; + Mut ret; do { if (zs.avail_out == 0) { - const usize current_pos = zs.total_out; - const usize new_size = out_buffer.size() * 2; + Const current_pos = zs.total_out; + Const new_size = out_buffer.size() * 2; out_buffer.resize(new_size); zs.next_out = reinterpret_cast(out_buffer.data() + current_pos); @@ -325,8 +309,8 @@ auto DataOps::zlib_inflate(Span data) -> Result> { return out_buffer; } -auto DataOps::zlib_deflate(Span data) -> Result> { - z_stream zs{}; +auto DataOps::zlib_deflate(Ref>> data) -> Result> { + Mut zs{}; zs.zalloc = Z_NULL; zs.zfree = Z_NULL; zs.opaque = Z_NULL; @@ -338,13 +322,13 @@ auto DataOps::zlib_deflate(Span data) -> Result> { zs.next_in = const_cast(data.data()); zs.avail_in = static_cast(data.size()); - Vec out_buffer; + Mut> out_buffer; out_buffer.resize(deflateBound(&zs, static_cast(data.size()))); zs.next_out = reinterpret_cast(out_buffer.data()); zs.avail_out = static_cast(out_buffer.size()); - const int ret = deflate(&zs, Z_FINISH); + Const ret = deflate(&zs, Z_FINISH); if (ret != Z_STREAM_END) { deflateEnd(&zs); @@ -357,8 +341,8 @@ auto DataOps::zlib_deflate(Span data) -> Result> { return out_buffer; } -auto DataOps::zstd_inflate(Span data) -> Result> { - const unsigned long long content_size = +auto DataOps::zstd_inflate(Ref>> data) -> Result> { + Const content_size = ZSTD_getFrameContentSize(data.data(), data.size()); if (content_size == ZSTD_CONTENTSIZE_ERROR) { @@ -366,12 +350,11 @@ auto DataOps::zstd_inflate(Span data) -> Result> { } if (content_size != ZSTD_CONTENTSIZE_UNKNOWN) { - // FAST PATH: We know the size - Vec out_buffer; + Mut> out_buffer; out_buffer.resize(static_cast(content_size)); - const usize d_size = ZSTD_decompress(out_buffer.data(), out_buffer.size(), - data.data(), data.size()); + Const d_size = ZSTD_decompress(out_buffer.data(), out_buffer.size(), + data.data(), data.size()); if (ZSTD_isError(d_size)) { return fail("Failed to inflate: {}", ZSTD_getErrorName(d_size)); @@ -380,14 +363,14 @@ auto DataOps::zstd_inflate(Span data) -> Result> { return out_buffer; } - ZSTD_DCtx *dctx = ZSTD_createDCtx(); - Vec out_buffer; + Mut dctx = ZSTD_createDCtx(); + Mut> out_buffer; out_buffer.resize(data.size() * 2); - ZSTD_inBuffer input = {data.data(), data.size(), 0}; - ZSTD_outBuffer output = {out_buffer.data(), out_buffer.size(), 0}; + Mut input = {data.data(), data.size(), 0}; + Mut output = {out_buffer.data(), out_buffer.size(), 0}; - usize ret; + Mut ret; do { ret = ZSTD_decompressStream(dctx, &output, &input); @@ -397,7 +380,7 @@ auto DataOps::zstd_inflate(Span data) -> Result> { } if (output.pos == output.size) { - const usize new_size = out_buffer.size() * 2; + Const new_size = out_buffer.size() * 2; out_buffer.resize(new_size); output.dst = out_buffer.data(); output.size = new_size; @@ -411,14 +394,14 @@ auto DataOps::zstd_inflate(Span data) -> Result> { return out_buffer; } -auto DataOps::zstd_deflate(Span data) -> Result> { - const usize max_dst_size = ZSTD_compressBound(data.size()); +auto DataOps::zstd_deflate(Ref>> data) -> Result> { + Const max_dst_size = ZSTD_compressBound(data.size()); - Vec out_buffer; + Mut> out_buffer; out_buffer.resize(max_dst_size); - const usize compressed_size = ZSTD_compress(out_buffer.data(), max_dst_size, - data.data(), data.size(), 3); + Const compressed_size = ZSTD_compress(out_buffer.data(), max_dst_size, + data.data(), data.size(), 3); if (ZSTD_isError(compressed_size)) { return fail("Failed to deflate: {}", ZSTD_getErrorName(compressed_size)); @@ -428,15 +411,12 @@ auto DataOps::zstd_deflate(Span data) -> Result> { return out_buffer; } -auto DataOps::gzip_deflate(Span data) -> Result> { - z_stream zs{}; +auto DataOps::gzip_deflate(Ref>> data) -> Result> { + Mut zs{}; zs.zalloc = Z_NULL; zs.zfree = Z_NULL; zs.opaque = Z_NULL; - // 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) { return fail("Failed to initialize gzip deflate"); @@ -445,15 +425,14 @@ auto DataOps::gzip_deflate(Span data) -> Result> { zs.next_in = const_cast(data.data()); zs.avail_in = static_cast(data.size()); - Vec out_buffer; + Mut> out_buffer; - out_buffer.resize(deflateBound(&zs, static_cast(data.size())) + - 1024); // Additional 1KB buffer for safety + out_buffer.resize(deflateBound(&zs, static_cast(data.size())) + 1024); zs.next_out = reinterpret_cast(out_buffer.data()); zs.avail_out = static_cast(out_buffer.size()); - const int ret = deflate(&zs, Z_FINISH); + Const ret = deflate(&zs, Z_FINISH); if (ret != Z_STREAM_END) { deflateEnd(&zs); @@ -466,7 +445,7 @@ auto DataOps::gzip_deflate(Span data) -> Result> { return out_buffer; } -auto DataOps::gzip_inflate(Span data) -> Result> { +auto DataOps::gzip_inflate(Ref>> data) -> Result> { return zlib_inflate(data); } diff --git a/Src/IACore/imp/cpp/FileOps.cpp b/Src/IACore/imp/cpp/FileOps.cpp index b666191..2f587ef 100644 --- a/Src/IACore/imp/cpp/FileOps.cpp +++ b/Src/IACore/imp/cpp/FileOps.cpp @@ -26,43 +26,44 @@ namespace IACore { -HashMap> FileOps::s_mapped_files; +Mut>> + FileOps::s_mapped_files; -auto FileOps::unmap_file(const u8 *mapped_ptr) -> void { +auto FileOps::unmap_file(Const *> mapped_ptr) -> void { if (!s_mapped_files.contains(mapped_ptr)) { return; } - auto it = s_mapped_files.find(mapped_ptr); - const auto handles = it->second; + Mut it = s_mapped_files.find(mapped_ptr); + Const> handles = it->second; s_mapped_files.erase(it); #if IA_PLATFORM_WINDOWS ::UnmapViewOfFile(std::get<1>(handles)); ::CloseHandle(static_cast(std::get<2>(handles))); - const auto handle = static_cast(std::get<0>(handles)); + Const handle = static_cast(std::get<0>(handles)); if (handle != INVALID_HANDLE_VALUE) { ::CloseHandle(handle); } #elif IA_PLATFORM_UNIX ::munmap(std::get<1>(handles), (usize)std::get<2>(handles)); - const auto fd = (i32)((u64)std::get<0>(handles)); + Const fd = (i32)((u64)std::get<0>(handles)); if (fd != -1) { ::close(fd); } #endif } -auto FileOps::map_shared_memory(const String &name, usize size, bool is_owner) - -> Result { +auto FileOps::map_shared_memory(Ref name, Const size, + Const is_owner) -> Result { #if IA_PLATFORM_WINDOWS - const int wchars_num = + Const wchars_num = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), -1, NULL, 0); - std::wstring w_name(wchars_num, 0); + Mut w_name(wchars_num, 0); MultiByteToWideChar(CP_UTF8, 0, name.c_str(), -1, &w_name[0], wchars_num); - HANDLE h_map = NULL; + Mut h_map = NULL; if (is_owner) { h_map = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, (DWORD)(size >> 32), (DWORD)(size & 0xFFFFFFFF), @@ -76,7 +77,7 @@ auto FileOps::map_shared_memory(const String &name, usize size, bool is_owner) is_owner ? "owner" : "consumer", name); } - auto *result = + Mut result = static_cast(MapViewOfFile(h_map, FILE_MAP_ALL_ACCESS, 0, 0, size)); if (result == NULL) { CloseHandle(h_map); @@ -88,7 +89,7 @@ auto FileOps::map_shared_memory(const String &name, usize size, bool is_owner) return result; #elif IA_PLATFORM_UNIX - int fd = -1; + Mut fd = -1; if (is_owner) { fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666); if (fd != -1) { @@ -107,13 +108,14 @@ auto FileOps::map_shared_memory(const String &name, usize size, bool is_owner) is_owner ? "owner" : "consumer", name); } - void *addr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + Mut addr = + mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (addr == MAP_FAILED) { close(fd); return fail("Failed to mmap shared memory '{}'", name); } - auto *result = static_cast(addr); + Mut result = static_cast(addr); s_mapped_files[result] = std::make_tuple((void *)((u64)fd), (void *)addr, (void *)size); @@ -121,7 +123,7 @@ auto FileOps::map_shared_memory(const String &name, usize size, bool is_owner) #endif } -auto FileOps::unlink_shared_memory(const String &name) -> void { +auto FileOps::unlink_shared_memory(Ref name) -> void { if (name.empty()) { return; } @@ -130,9 +132,10 @@ auto FileOps::unlink_shared_memory(const String &name) -> void { #endif } -auto FileOps::map_file(const Path &path, usize &size) -> Result { +auto FileOps::map_file(Ref path, MutRef size) + -> Result { #if IA_PLATFORM_WINDOWS - const auto handle = CreateFileA( + Const handle = CreateFileA( path.string().c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); @@ -140,7 +143,7 @@ auto FileOps::map_file(const Path &path, usize &size) -> Result { return fail("Failed to open {} for memory mapping", path.string()); } - LARGE_INTEGER file_size; + Mut file_size; if (!GetFileSizeEx(handle, &file_size)) { CloseHandle(handle); return fail("Failed to get size of {} for memory mapping", path.string()); @@ -151,13 +154,14 @@ auto FileOps::map_file(const Path &path, usize &size) -> Result { return fail("Failed to get size of {} for memory mapping", path.string()); } - auto h_map = CreateFileMappingW(handle, NULL, PAGE_READONLY, 0, 0, NULL); + Mut h_map = + CreateFileMappingW(handle, NULL, PAGE_READONLY, 0, 0, NULL); if (h_map == NULL) { CloseHandle(handle); return fail("Failed to memory map {}", path.string()); } - const auto *result = + Const *result = static_cast(MapViewOfFile(h_map, FILE_MAP_READ, 0, 0, 0)); if (result == NULL) { CloseHandle(handle); @@ -169,11 +173,11 @@ auto FileOps::map_file(const Path &path, usize &size) -> Result { return result; #elif IA_PLATFORM_UNIX - const auto handle = open(path.string().c_str(), O_RDONLY); + Const handle = open(path.string().c_str(), O_RDONLY); if (handle == -1) { return fail("Failed to open {} for memory mapping", path.string()); } - struct stat sb; + Mut sb; if (fstat(handle, &sb) == -1) { close(handle); return fail("Failed to get stats of {} for memory mapping", path.string()); @@ -183,12 +187,12 @@ auto FileOps::map_file(const Path &path, usize &size) -> Result { close(handle); return fail("Failed to get size of {} for memory mapping", path.string()); } - void *addr = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, handle, 0); + Mut addr = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, handle, 0); if (addr == MAP_FAILED) { close(handle); return fail("Failed to memory map {}", path.string()); } - const auto *result = static_cast(addr); + Const *> result = static_cast(addr); madvise(addr, size, MADV_SEQUENTIAL); s_mapped_files[result] = std::make_tuple((void *)((u64)handle), (void *)addr, (void *)size); @@ -196,7 +200,7 @@ auto FileOps::map_file(const Path &path, usize &size) -> Result { #endif } -auto FileOps::stream_to_file(const Path &path, bool overwrite) +auto FileOps::stream_to_file(Ref path, Const overwrite) -> Result { if (!overwrite && std::filesystem::exists(path)) { return fail("File already exists: {}", path.string()); @@ -204,21 +208,21 @@ auto FileOps::stream_to_file(const Path &path, bool overwrite) return StreamWriter::create_from_file(path); } -auto FileOps::stream_from_file(const Path &path) -> Result { +auto FileOps::stream_from_file(Ref path) -> Result { if (!std::filesystem::exists(path)) { return fail("File does not exist: {}", path.string()); } return StreamReader::create_from_file(path); } -auto FileOps::read_text_file(const Path &path) -> Result { - auto *f = fopen(path.string().c_str(), "r"); +auto FileOps::read_text_file(Ref path) -> Result { + Mut f = fopen(path.string().c_str(), "r"); if (!f) { return fail("Failed to open file: {}", path.string()); } - String result; + Mut result; fseek(f, 0, SEEK_END); - const long len = ftell(f); + Const len = ftell(f); if (len > 0) { result.resize(static_cast(len)); fseek(f, 0, SEEK_SET); @@ -228,14 +232,14 @@ auto FileOps::read_text_file(const Path &path) -> Result { return result; } -auto FileOps::read_binary_file(const Path &path) -> Result> { - auto *f = fopen(path.string().c_str(), "rb"); +auto FileOps::read_binary_file(Ref path) -> Result> { + Mut f = fopen(path.string().c_str(), "rb"); if (!f) { return fail("Failed to open file: {}", path.string()); } - Vec result; + Mut> result; fseek(f, 0, SEEK_END); - const long len = ftell(f); + Const len = ftell(f); if (len > 0) { result.resize(static_cast(len)); fseek(f, 0, SEEK_SET); @@ -245,38 +249,38 @@ auto FileOps::read_binary_file(const Path &path) -> Result> { return result; } -auto FileOps::write_text_file(const Path &path, const String &contents, - bool overwrite) -> Result { - const char *mode = overwrite ? "w" : "wx"; - auto *f = fopen(path.string().c_str(), mode); +auto FileOps::write_text_file(Ref path, Ref contents, + Const overwrite) -> Result { + Const *> mode = overwrite ? "w" : "wx"; + Mut f = fopen(path.string().c_str(), mode); if (!f) { if (!overwrite && errno == EEXIST) { return fail("File already exists: {}", path.string()); } return fail("Failed to write to file: {}", path.string()); } - const auto result = fwrite(contents.data(), 1, contents.size(), f); + Const result = fwrite(contents.data(), 1, contents.size(), f); fclose(f); return result; } -auto FileOps::write_binary_file(const Path &path, Span contents, - bool overwrite) -> Result { - const char *mode = overwrite ? "w" : "wx"; - auto *f = fopen(path.string().c_str(), mode); +auto FileOps::write_binary_file(Ref path, Const>> contents, + Const overwrite) -> Result { + Const *> mode = overwrite ? "w" : "wx"; + Mut f = fopen(path.string().c_str(), mode); if (!f) { if (!overwrite && errno == EEXIST) { return fail("File already exists: {}", path.string()); } return fail("Failed to write to file: {}", path.string()); } - const auto result = fwrite(contents.data(), 1, contents.size(), f); + Const result = fwrite(contents.data(), 1, contents.size(), f); fclose(f); return result; } -auto FileOps::normalize_executable_path(const Path &path) -> Path { - Path result = path; +auto FileOps::normalize_executable_path(Ref path) -> Path { + Mut result = path; #if IA_PLATFORM_WINDOWS if (!result.has_extension()) { @@ -288,7 +292,7 @@ auto FileOps::normalize_executable_path(const Path &path) -> Path { } if (result.is_relative()) { - String path_str = result.string(); + Mut path_str = result.string(); if (!path_str.starts_with("./") && !path_str.starts_with("../")) { result = "./" + path_str; } @@ -297,14 +301,14 @@ auto FileOps::normalize_executable_path(const Path &path) -> Path { return result; } -auto FileOps::native_open_file(const Path &path, FileAccess access, - FileMode mode, u32 permissions) +auto FileOps::native_open_file(Ref path, Const access, + Const mode, Const permissions) -> Result { #if IA_PLATFORM_WINDOWS - DWORD dw_access = 0; - DWORD dw_share = FILE_SHARE_READ; - DWORD dw_disposition = 0; - DWORD dw_flags_and_attributes = FILE_ATTRIBUTE_NORMAL; + Mut dw_access = 0; + Mut dw_share = FILE_SHARE_READ; + Mut dw_disposition = 0; + Mut dw_flags_and_attributes = FILE_ATTRIBUTE_NORMAL; switch (access) { case FileAccess::Read: @@ -336,8 +340,9 @@ auto FileOps::native_open_file(const Path &path, FileAccess access, break; } - HANDLE h_file = CreateFileA(path.string().c_str(), dw_access, dw_share, NULL, - dw_disposition, dw_flags_and_attributes, NULL); + Mut h_file = + CreateFileA(path.string().c_str(), dw_access, dw_share, NULL, + dw_disposition, dw_flags_and_attributes, NULL); if (h_file == INVALID_HANDLE_VALUE) { return fail("Failed to open file '{}': {}", path.string(), GetLastError()); @@ -346,7 +351,7 @@ auto FileOps::native_open_file(const Path &path, FileAccess access, return h_file; #elif IA_PLATFORM_UNIX - int flags = 0; + Mut flags = 0; switch (access) { case FileAccess::Read: @@ -377,7 +382,7 @@ auto FileOps::native_open_file(const Path &path, FileAccess access, break; } - int fd = open(path.string().c_str(), flags, permissions); + Mut fd = open(path.string().c_str(), flags, permissions); if (fd == -1) { return fail("Failed to open file '{}': {}", path.string(), errno); @@ -387,7 +392,7 @@ auto FileOps::native_open_file(const Path &path, FileAccess access, #endif } -auto FileOps::native_close_file(NativeFileHandle handle) -> void { +auto FileOps::native_close_file(Const handle) -> void { if (handle == INVALID_FILE_HANDLE) { return; } @@ -399,19 +404,16 @@ auto FileOps::native_close_file(NativeFileHandle handle) -> void { #endif } -// ============================================================================= -// MemoryMappedRegion -// ============================================================================= - FileOps::MemoryMappedRegion::~MemoryMappedRegion() { unmap(); } FileOps::MemoryMappedRegion::MemoryMappedRegion( - MemoryMappedRegion &&other) noexcept { + ForwardRef other) noexcept { *this = std::move(other); } -auto FileOps::MemoryMappedRegion::operator=(MemoryMappedRegion &&other) noexcept - -> MemoryMappedRegion & { +auto FileOps::MemoryMappedRegion::operator=( + ForwardRef other) noexcept + -> MutRef { if (this != &other) { unmap(); m_ptr = other.m_ptr; @@ -426,8 +428,9 @@ auto FileOps::MemoryMappedRegion::operator=(MemoryMappedRegion &&other) noexcept return *this; } -auto FileOps::MemoryMappedRegion::map(NativeFileHandle handle, u64 offset, - usize size) -> Result { +auto FileOps::MemoryMappedRegion::map(Const handle, + Const offset, Const size) + -> Result { unmap(); if (handle == INVALID_FILE_HANDLE) { @@ -439,14 +442,14 @@ auto FileOps::MemoryMappedRegion::map(NativeFileHandle handle, u64 offset, } #if IA_PLATFORM_WINDOWS - LARGE_INTEGER file_size; + Mut file_size; if (!GetFileSizeEx(handle, &file_size)) { return fail("Failed to get file size"); } - u64 end_offset = offset + size; + Const end_offset = offset + size; if (static_cast(file_size.QuadPart) < end_offset) { - LARGE_INTEGER new_size; + Mut new_size; new_size.QuadPart = static_cast(end_offset); if (!SetFilePointerEx(handle, new_size, NULL, FILE_BEGIN)) { return fail("Failed to seek to new end of file"); @@ -462,8 +465,8 @@ auto FileOps::MemoryMappedRegion::map(NativeFileHandle handle, u64 offset, return fail("CreateFileMapping failed: {}", GetLastError()); } - DWORD offset_high = static_cast(offset >> 32); - DWORD offset_low = static_cast(offset & 0xFFFFFFFF); + Const offset_high = static_cast(offset >> 32); + Const offset_low = static_cast(offset & 0xFFFFFFFF); m_ptr = static_cast(MapViewOfFile(m_map_handle, FILE_MAP_WRITE, offset_high, offset_low, size)); @@ -476,20 +479,20 @@ auto FileOps::MemoryMappedRegion::map(NativeFileHandle handle, u64 offset, m_size = size; #elif IA_PLATFORM_UNIX - struct stat sb; + Mut sb; if (fstat(handle, &sb) == -1) { return fail("Failed to fstat file"); } - u64 end_offset = offset + size; + Const end_offset = offset + size; if (static_cast(sb.st_size) < end_offset) { if (ftruncate(handle, static_cast(end_offset)) == -1) { return fail("Failed to ftruncate (extend) file"); } } - void *ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, handle, - static_cast(offset)); + Mut ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, + handle, static_cast(offset)); if (ptr == MAP_FAILED) { return fail("mmap failed: {}", errno); } diff --git a/Src/IACore/imp/cpp/Http/Client.cpp b/Src/IACore/imp/cpp/Http/Client.cpp index eb73f4c..32e802a 100644 --- a/Src/IACore/imp/cpp/Http/Client.cpp +++ b/Src/IACore/imp/cpp/Http/Client.cpp @@ -17,23 +17,17 @@ #include namespace IACore { -// Helper struct to access protected constructor via make_box (std::make_unique) -struct PublicHttpClient : public HttpClient { - explicit PublicHttpClient(httplib::Client &&client) - : HttpClient(std::move(client)) {} -}; - -auto HttpClient::create(const String &host) -> Result> { - return make_box(httplib::Client(host)); +auto HttpClient::create(Ref host) -> Result> { + return make_box_protected(httplib::Client(host)); } -static auto build_headers(Span headers, - const char *default_content_type) +static auto build_headers(Span> headers, + Const *> default_content_type) -> httplib::Headers { - httplib::Headers out; - bool has_content_type = false; + Mut out; + Mut has_content_type = false; - for (const auto &h : headers) { + for (Ref h : headers) { out.emplace(h.first, h.second); if (h.first == HttpClient::header_type_to_string( @@ -48,7 +42,7 @@ static auto build_headers(Span headers, return out; } -HttpClient::HttpClient(httplib::Client &&client) +HttpClient::HttpClient(ForwardRef client) : m_client(std::move(client)), m_last_response_code(EResponseCode::INTERNAL_SERVER_ERROR) { m_client.enable_server_certificate_verification(true); @@ -56,34 +50,35 @@ HttpClient::HttpClient(httplib::Client &&client) HttpClient::~HttpClient() = default; -void HttpClient::enable_certificate_verification() { +auto HttpClient::enable_certificate_verification() -> void { m_client.enable_server_certificate_verification(true); } -void HttpClient::disable_certificate_verification() { +auto HttpClient::disable_certificate_verification() -> void { m_client.enable_server_certificate_verification(false); } -auto HttpClient::preprocess_response(const String &response) -> String { - const auto response_bytes = Span{ - reinterpret_cast(response.data()), response.size()}; - const auto compression = DataOps::detect_compression(response_bytes); +auto HttpClient::preprocess_response(Ref response) -> String { + Const>> response_bytes = { + reinterpret_cast *>(response.data()), response.size()}; + Const compression = + DataOps::detect_compression(response_bytes); switch (compression) { case DataOps::CompressionType::Gzip: { - const auto data = DataOps::gzip_inflate(response_bytes); + Const>> data = DataOps::gzip_inflate(response_bytes); if (!data) { return response; } - return String(reinterpret_cast(data->data()), data->size()); + return String(reinterpret_cast *>(data->data()), data->size()); } case DataOps::CompressionType::Zlib: { - const auto data = DataOps::zlib_inflate(response_bytes); + Const>> data = DataOps::zlib_inflate(response_bytes); if (!data) { return response; } - return String(reinterpret_cast(data->data()), data->size()); + return String(reinterpret_cast *>(data->data()), data->size()); } case DataOps::CompressionType::None: @@ -93,16 +88,19 @@ auto HttpClient::preprocess_response(const String &response) -> String { return response; } -auto HttpClient::raw_get(const String &path, Span headers, - const char *default_content_type) -> Result { - auto http_headers = build_headers(headers, default_content_type); +auto HttpClient::raw_get(Ref path, Span> headers, + Const *> default_content_type) + -> Result { + Const http_headers = + build_headers(headers, default_content_type); - String adjusted_path = path; + Mut adjusted_path = path; if (!path.empty() && path[0] != '/') { adjusted_path = "/" + path; } - auto res = m_client.Get(adjusted_path.c_str(), http_headers); + Const res = + m_client.Get(adjusted_path.c_str(), http_headers); if (res) { m_last_response_code = static_cast(res->status); @@ -115,27 +113,29 @@ auto HttpClient::raw_get(const String &path, Span headers, return fail("Network Error: {}", httplib::to_string(res.error())); } -auto HttpClient::raw_post(const String &path, Span headers, - const String &body, const char *default_content_type) +auto HttpClient::raw_post(Ref path, Span> headers, + Ref body, + Const *> default_content_type) -> Result { - auto http_headers = build_headers(headers, default_content_type); + Mut http_headers = + build_headers(headers, default_content_type); - String content_type = default_content_type; + Mut content_type = default_content_type; if (http_headers.count("Content-Type")) { - const auto t = http_headers.find("Content-Type"); + Const t = http_headers.find("Content-Type"); content_type = t->second; http_headers.erase(t); } m_client.set_keep_alive(true); - String adjusted_path = path; + Mut adjusted_path = path; if (!path.empty() && path[0] != '/') { adjusted_path = "/" + path; } - auto res = m_client.Post(adjusted_path.c_str(), http_headers, body, - content_type.c_str()); + Const res = m_client.Post( + adjusted_path.c_str(), http_headers, body, content_type.c_str()); if (res) { m_last_response_code = static_cast(res->status); diff --git a/Src/IACore/imp/cpp/Http/Common.cpp b/Src/IACore/imp/cpp/Http/Common.cpp index adb1f14..b0f1716 100644 --- a/Src/IACore/imp/cpp/Http/Common.cpp +++ b/Src/IACore/imp/cpp/Http/Common.cpp @@ -16,12 +16,12 @@ #include namespace IACore { -auto HttpCommon::url_encode(const String &value) -> String { - std::stringstream escaped; +auto HttpCommon::url_encode(Ref value) -> String { + Mut escaped; escaped.fill('0'); escaped << std::hex << std::uppercase; - for (char c : value) { + for (Const c : value) { if (std::isalnum(static_cast(c)) || c == '-' || c == '_' || c == '.' || c == '~') escaped << c; @@ -33,14 +33,14 @@ auto HttpCommon::url_encode(const String &value) -> String { return escaped.str(); } -auto HttpCommon::url_decode(const String &value) -> String { - String result; +auto HttpCommon::url_decode(Ref value) -> String { + Mut result; result.reserve(value.length()); - for (size_t i = 0; i < value.length(); ++i) { + for (Mut i = 0; i < value.length(); ++i) { if (value[i] == '%' && i + 2 < value.length()) { - std::string hex_str = value.substr(i + 1, 2); - char decoded_char = + Const hex_str = value.substr(i + 1, 2); + Const decoded_char = static_cast(std::strtol(hex_str.c_str(), nullptr, 16)); result += decoded_char; i += 2; @@ -53,7 +53,7 @@ auto HttpCommon::url_decode(const String &value) -> String { return result; } -auto HttpCommon::header_type_to_string(EHeaderType type) -> String { +auto HttpCommon::header_type_to_string(Const type) -> String { switch (type) { case EHeaderType::ACCEPT: return "Accept"; @@ -111,7 +111,7 @@ auto HttpCommon::header_type_to_string(EHeaderType type) -> String { return ""; } -auto HttpCommon::is_success_response_code(EResponseCode code) -> bool { +auto HttpCommon::is_success_response_code(Const code) -> bool { return (i32)code >= 200 && (i32)code < 300; } } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/imp/cpp/Http/Server.cpp b/Src/IACore/imp/cpp/Http/Server.cpp index b1bdc76..e1ccf3d 100644 --- a/Src/IACore/imp/cpp/Http/Server.cpp +++ b/Src/IACore/imp/cpp/Http/Server.cpp @@ -16,65 +16,56 @@ #include namespace IACore { -// ============================================================================= -// Request Implementation -// ============================================================================= -auto HttpServer::Request::get_header(const String &key) const -> String { - if (auto it = headers.find(key); it != headers.end()) { +auto HttpServer::Request::get_header(Ref key) const -> String { + if (Const::const_iterator> it = headers.find(key); + it != headers.end()) { return it->second; } return ""; } -auto HttpServer::Request::get_param(const String &key) const -> String { - if (auto it = params.find(key); it != params.end()) { +auto HttpServer::Request::get_param(Ref key) const -> String { + if (Const::const_iterator> it = params.find(key); + it != params.end()) { return it->second; } return ""; } -auto HttpServer::Request::get_path_param(const String &key) const -> String { - if (auto it = path_params.find(key); it != path_params.end()) { +auto HttpServer::Request::get_path_param(Ref key) const -> String { + if (Const::const_iterator> it = path_params.find(key); + it != path_params.end()) { return it->second; } return ""; } -auto HttpServer::Request::has_header(const String &key) const -> bool { +auto HttpServer::Request::has_header(Ref key) const -> bool { return headers.contains(key); } -auto HttpServer::Request::has_param(const String &key) const -> bool { +auto HttpServer::Request::has_param(Ref key) const -> bool { return params.contains(key); } -auto HttpServer::Request::has_path_param(const String &key) const -> bool { +auto HttpServer::Request::has_path_param(Ref key) const -> bool { return path_params.contains(key); } -// ============================================================================= -// Response Implementation -// ============================================================================= - -void HttpServer::Response::set_content(const String &content, - const String &type) { +void HttpServer::Response::set_content(Ref content, Ref type) { body = content; content_type = type; } -void HttpServer::Response::set_status(EResponseCode status_code) { +void HttpServer::Response::set_status(Const status_code) { code = status_code; } -void HttpServer::Response::add_header(const String &key, const String &value) { +void HttpServer::Response::add_header(Ref key, Ref value) { headers[key] = value; } -// ============================================================================= -// HttpServer Implementation -// ============================================================================= - struct PublicHttpServer : public HttpServer { PublicHttpServer() = default; }; @@ -87,7 +78,7 @@ auto HttpServer::create() -> Result> { return make_box(); } -auto HttpServer::listen(const String &host, u32 port) -> Result { +auto HttpServer::listen(Ref host, Const port) -> Result { if (!m_server.listen(host.c_str(), static_cast(port))) { return fail("Failed to start HTTP server on {}:{}", host, port); } @@ -102,40 +93,37 @@ void HttpServer::stop() { auto HttpServer::is_running() const -> bool { return m_server.is_running(); } -void HttpServer::register_handler(const String &method, const String &pattern, - Handler handler) { - auto wrapper = [handler = std::move(handler)](const httplib::Request &req, - httplib::Response &res) { - Request ia_req; - ia_req.path = req.path; - ia_req.method = req.method; - ia_req.body = req.body; +void HttpServer::register_handler(Ref method, Ref pattern, + Const handler) { + Const wrapper = + [handler](Ref req, MutRef res) { + Mut ia_req; + ia_req.path = req.path; + ia_req.method = req.method; + ia_req.body = req.body; - // Convert headers - for (const auto &item : req.headers) { - ia_req.headers[item.first] = item.second; - } + for (Ref> item : req.headers) { + ia_req.headers[item.first] = item.second; + } - // Convert params - for (const auto &item : req.params) { - ia_req.params[item.first] = item.second; - } + for (Ref> item : req.params) { + ia_req.params[item.first] = item.second; + } - // Convert path params - for (const auto &item : req.path_params) { - ia_req.path_params[item.first] = item.second; - } + for (Ref> item : req.path_params) { + ia_req.path_params[item.first] = item.second; + } - Response ia_res; - handler(ia_req, ia_res); + Mut ia_res; + handler(ia_req, ia_res); - res.status = static_cast(ia_res.code); - res.set_content(ia_res.body, ia_res.content_type.c_str()); + res.status = static_cast(ia_res.code); + res.set_content(ia_res.body, ia_res.content_type.c_str()); - for (const auto &item : ia_res.headers) { - res.set_header(item.first.c_str(), item.second.c_str()); - } - }; + for (Ref> item : ia_res.headers) { + res.set_header(item.first.c_str(), item.second.c_str()); + } + }; if (method == "GET") { m_server.Get(pattern.c_str(), wrapper); @@ -150,24 +138,24 @@ void HttpServer::register_handler(const String &method, const String &pattern, } } -void HttpServer::get(const String &pattern, Handler handler) { - register_handler("GET", pattern, std::move(handler)); +void HttpServer::get(Ref pattern, Const handler) { + register_handler("GET", pattern, handler); } -void HttpServer::post(const String &pattern, Handler handler) { - register_handler("POST", pattern, std::move(handler)); +void HttpServer::post(Ref pattern, Const handler) { + register_handler("POST", pattern, handler); } -void HttpServer::put(const String &pattern, Handler handler) { - register_handler("PUT", pattern, std::move(handler)); +void HttpServer::put(Ref pattern, Const handler) { + register_handler("PUT", pattern, handler); } -void HttpServer::del(const String &pattern, Handler handler) { - register_handler("DELETE", pattern, std::move(handler)); +void HttpServer::del(Ref pattern, Const handler) { + register_handler("DELETE", pattern, handler); } -void HttpServer::options(const String &pattern, Handler handler) { - register_handler("OPTIONS", pattern, std::move(handler)); +void HttpServer::options(Ref pattern, Const handler) { + register_handler("OPTIONS", pattern, handler); } } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/imp/cpp/IACore.cpp b/Src/IACore/imp/cpp/IACore.cpp index a04f141..a759c81 100644 --- a/Src/IACore/imp/cpp/IACore.cpp +++ b/Src/IACore/imp/cpp/IACore.cpp @@ -16,51 +16,41 @@ #include #include -#include #include -#include +#include -namespace IACore -{ - auto g_start_time = std::chrono::high_resolution_clock::time_point{}; +namespace IACore { +Mut g_start_time = {}; - static auto g_main_thread_id = std::thread::id{}; - static auto g_core_init_count = i32{0}; +static Mut g_main_thread_id = {}; +static Mut g_core_init_count = 0; - auto initialize() -> void - { - g_core_init_count++; - if (g_core_init_count > 1) - { - return; - } +auto initialize() -> void { + g_core_init_count++; + if (g_core_init_count > 1) { + return; + } - g_main_thread_id = std::this_thread::get_id(); - g_start_time = std::chrono::high_resolution_clock::now(); + g_main_thread_id = std::this_thread::get_id(); + g_start_time = std::chrono::high_resolution_clock::now(); - Logger::initialize(); + Logger::initialize(); - mi_option_set(mi_option_verbose, 0); - } + mi_option_set(mi_option_verbose, 0); +} - auto terminate() -> void - { - g_core_init_count--; - if (g_core_init_count > 0) - { - return; - } +auto terminate() -> void { + g_core_init_count--; + if (g_core_init_count > 0) { + return; + } - Logger::terminate(); - } + Logger::terminate(); +} - auto is_initialized() -> bool - { - return g_core_init_count > 0; - } +auto is_initialized() -> bool { return g_core_init_count > 0; } - auto is_main_thread() -> bool - { - return std::this_thread::get_id() == g_main_thread_id; - } +auto is_main_thread() -> bool { + return std::this_thread::get_id() == g_main_thread_id; +} } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/imp/cpp/IPC.cpp b/Src/IACore/imp/cpp/IPC.cpp index 804ddda..203cdb9 100644 --- a/Src/IACore/imp/cpp/IPC.cpp +++ b/Src/IACore/imp/cpp/IPC.cpp @@ -21,27 +21,25 @@ #include namespace IACore { -// ============================================================================= -// Internal: Connection Descriptor -// ============================================================================= struct IpcConnectionDescriptor { - String socket_path; - String shared_mem_path; - u32 shared_mem_size; + Mut socket_path; + Mut shared_mem_path; + Mut shared_mem_size; [[nodiscard]] auto serialize() const -> String { return std::format("{}|{}|{}|", socket_path, shared_mem_path, shared_mem_size); } - static auto deserialize(StringView data) -> Option { + static auto deserialize(Const data) + -> Option { enum class ParseState { SocketPath, SharedMemPath, SharedMemSize }; - IpcConnectionDescriptor result{}; - usize t = 0; - auto state = ParseState::SocketPath; + Mut result{}; + Mut t = 0; + Mut state = ParseState::SocketPath; - for (usize i = 0; i < data.size(); ++i) { + for (Mut i = 0; i < data.size(); ++i) { if (data[i] != '|') { continue; } @@ -58,8 +56,8 @@ struct IpcConnectionDescriptor { break; case ParseState::SharedMemSize: { - const auto *start = data.data() + t; - const auto *end = data.data() + i; + Const start = data.data() + t; + Const end = data.data() + i; if (std::from_chars(start, end, result.shared_mem_size).ec != std::errc{}) { return std::nullopt; @@ -73,37 +71,33 @@ struct IpcConnectionDescriptor { } }; -// ============================================================================= -// IpcNode Implementation -// ============================================================================= - IpcNode::~IpcNode() { if (m_socket != INVALID_SOCKET) { SocketOps::close(m_socket); } } -auto IpcNode::connect(const char *connection_string) -> Result { - const auto desc_opt = IpcConnectionDescriptor::deserialize(connection_string); +auto IpcNode::connect(Const connection_string) -> Result { + Const> desc_opt = + IpcConnectionDescriptor::deserialize(connection_string); if (!desc_opt) { return fail("Failed to parse connection string"); } - const auto &desc = *desc_opt; + Ref desc = *desc_opt; m_shm_name = desc.shared_mem_path; - IA_TRY(m_socket, SocketOps::create_unix_socket()); - IA_TRY_PURE( + m_socket = OX_TRY(SocketOps::create_unix_socket()); + OX_TRY_PURE( SocketOps::connect_unix_socket(m_socket, desc.socket_path.c_str())); - u8 *mapped_ptr{}; - IA_TRY(mapped_ptr, FileOps::map_shared_memory(desc.shared_mem_path, - desc.shared_mem_size, false)); + Mut mapped_ptr = OX_TRY(FileOps::map_shared_memory( + desc.shared_mem_path, desc.shared_mem_size, false)); m_shared_memory = mapped_ptr; - auto *layout = reinterpret_cast(m_shared_memory); + Mut layout = + reinterpret_cast(m_shared_memory); - if (layout->meta.magic != 0x49414950) // "IAIP" - { + if (layout->meta.magic != 0x49414950) { return fail("Invalid shared memory header signature"); } @@ -111,23 +105,19 @@ auto IpcNode::connect(const char *connection_string) -> Result { return fail("IPC version mismatch"); } - u8 *moni_ptr = m_shared_memory + layout->moni_data_offset; - u8 *mino_ptr = m_shared_memory + layout->mino_data_offset; + Mut moni_ptr = m_shared_memory + layout->moni_data_offset; + Mut mino_ptr = m_shared_memory + layout->mino_data_offset; - IA_TRY(m_moni, - RingBufferView::create( - &layout->moni_control, - Span(moni_ptr, static_cast(layout->moni_data_size)), - false)); + m_moni = OX_TRY(RingBufferView::create( + &layout->moni_control, + Span(moni_ptr, static_cast(layout->moni_data_size)), false)); - IA_TRY(m_mino, - RingBufferView::create( - &layout->mino_control, - Span(mino_ptr, static_cast(layout->mino_data_size)), - false)); + m_mino = OX_TRY(RingBufferView::create( + &layout->mino_control, + Span(mino_ptr, static_cast(layout->mino_data_size)), false)); #if IA_PLATFORM_WINDOWS - u_long mode = 1; + Mut mode = 1; ioctlsocket(m_socket, FIONBIO, &mode); #else fcntl(m_socket, F_SETFL, O_NONBLOCK); @@ -143,55 +133,49 @@ void IpcNode::update() { return; } - IpcPacketHeader header; + Mut header; - // Process all available messages from Manager while (m_moni.pop( header, Span(m_receive_buffer.data(), m_receive_buffer.size()))) { on_packet(header.id, {m_receive_buffer.data(), header.payload_size}); } - u8 signal = 0; - const auto res = recv(m_socket, reinterpret_cast(&signal), 1, 0); + Mut signal = 0; + Const res = recv(m_socket, reinterpret_cast(&signal), 1, 0); if (res == 1) { on_signal(signal); } else if (res == 0 || (res < 0 && !SocketOps::is_would_block())) { SocketOps::close(m_socket); FileOps::unlink_shared_memory(m_shm_name); - // Manager disconnected, exit immediately std::exit(-1); } } -void IpcNode::send_signal(u8 signal) { +void IpcNode::send_signal(Const signal) { if (m_socket != INVALID_SOCKET) { send(m_socket, reinterpret_cast(&signal), sizeof(signal), 0); } } -auto IpcNode::send_packet(u16 packet_id, Span payload) +auto IpcNode::send_packet(Const packet_id, Const>> payload) -> Result { if (!m_mino.is_valid()) return fail("invalid MINO"); return m_mino.push(packet_id, payload); } -// ============================================================================= -// IpcManager Implementation -// ============================================================================= - -void IpcManager::NodeSession::send_signal(u8 signal) { +void IpcManager::NodeSession::send_signal(Const signal) { if (data_socket != INVALID_SOCKET) { send(data_socket, reinterpret_cast(&signal), sizeof(signal), 0); } } -auto IpcManager::NodeSession::send_packet(u16 packet_id, Span payload) +auto IpcManager::NodeSession::send_packet(Const packet_id, + Const>> payload) -> Result { - // Protect the RingBuffer write cursor from concurrent threads - std::scoped_lock lock(send_mutex); + Const> lock(send_mutex); if (!moni.is_valid()) return fail("invalid MONI"); return moni.push(packet_id, payload); @@ -205,7 +189,7 @@ IpcManager::IpcManager() { } IpcManager::~IpcManager() { - for (auto &session : m_active_sessions) { + for (MutRef> session : m_active_sessions) { ProcessOps::terminate_process(session->node_process); FileOps::unmap_file(session->mapped_ptr); FileOps::unlink_shared_memory(session->shared_mem_name); @@ -213,7 +197,7 @@ IpcManager::~IpcManager() { } m_active_sessions.clear(); - for (auto &session : m_pending_sessions) { + for (MutRef> session : m_pending_sessions) { ProcessOps::terminate_process(session->node_process); FileOps::unmap_file(session->mapped_ptr); FileOps::unlink_shared_memory(session->shared_mem_name); @@ -223,11 +207,13 @@ IpcManager::~IpcManager() { } void IpcManager::update() { - const auto now = std::chrono::system_clock::now(); + Const now = + std::chrono::system_clock::now(); - for (isize i = static_cast(m_pending_sessions.size()) - 1; i >= 0; + for (Mut i = static_cast(m_pending_sessions.size()) - 1; i >= 0; --i) { - auto &session = m_pending_sessions[static_cast(i)]; + MutRef> session = + m_pending_sessions[static_cast(i)]; if (now - session->creation_time > std::chrono::seconds(5)) { ProcessOps::terminate_process(session->node_process); @@ -240,15 +226,18 @@ void IpcManager::update() { continue; } - auto new_sock = accept(session->listener_socket, nullptr, nullptr); +#if IA_PLATFORM_WINDOWS + Mut new_sock = accept(session->listener_socket, nullptr, nullptr); +#else + Mut new_sock = accept(session->listener_socket, nullptr, nullptr); +#endif if (new_sock != INVALID_SOCKET) { session->data_socket = new_sock; session->is_ready = true; - // Set Data Socket to Non-Blocking #if IA_PLATFORM_WINDOWS - u_long mode = 1; + Mut mode = 1; ioctlsocket(session->data_socket, FIONBIO, &mode); #else fcntl(session->data_socket, F_SETFL, O_NONBLOCK); @@ -257,21 +246,21 @@ void IpcManager::update() { SocketOps::close(session->listener_socket); session->listener_socket = INVALID_SOCKET; - const auto session_id = session->node_process->id.load(); - auto *session_ptr = session.get(); + Const session_id = session->node_process->id.load(); + Mut session_ptr = session.get(); m_active_sessions.push_back(std::move(session)); m_pending_sessions.erase(m_pending_sessions.begin() + i); m_active_session_map[session_id] = session_ptr; } } - for (isize i = static_cast(m_active_sessions.size()) - 1; i >= 0; + for (Mut i = static_cast(m_active_sessions.size()) - 1; i >= 0; --i) { - auto &node = m_active_sessions[static_cast(i)]; + MutRef> node = m_active_sessions[static_cast(i)]; - auto node_id = node->node_process->id.load(); + Mut node_id = node->node_process->id.load(); - IpcPacketHeader header; + Mut header; while (node->mino.pop( header, Span(m_receive_buffer.data(), m_receive_buffer.size()))) { @@ -279,8 +268,8 @@ void IpcManager::update() { {m_receive_buffer.data(), header.payload_size}); } - u8 signal = 0; - const auto res = + Mut signal = 0; + Const res = recv(node->data_socket, reinterpret_cast(&signal), 1, 0); if (res == 1) { @@ -298,48 +287,50 @@ void IpcManager::update() { } } -auto IpcManager::spawn_node(const Path &executable_path, u32 shared_memory_size) +auto IpcManager::spawn_node(Ref executable_path, + Const shared_memory_size) -> Result { - auto session = make_box(); + Mut> session = make_box(); - static std::atomic s_id_gen{0}; - const u32 sid = ++s_id_gen; + static Mut> s_id_gen{0}; + Const sid = ++s_id_gen; - String sock_path; + Mut sock_path; #if IA_PLATFORM_WINDOWS - char temp_path[MAX_PATH]; + Mut temp_path; GetTempPathA(MAX_PATH, temp_path); sock_path = std::format("{}\\ia_sess_{}.sock", temp_path, sid); #else sock_path = std::format("/tmp/ia_sess_{}.sock", sid); #endif - IA_TRY(session->listener_socket, SocketOps::create_unix_socket()); - IA_TRY_PURE( + session->listener_socket = OX_TRY(SocketOps::create_unix_socket()); + OX_TRY_PURE( SocketOps::bind_unix_socket(session->listener_socket, sock_path.c_str())); - IA_TRY_PURE(SocketOps::listen(session->listener_socket, 1)); + OX_TRY_PURE(SocketOps::listen(session->listener_socket, 1)); #if IA_PLATFORM_WINDOWS - u_long mode = 1; + Mut mode = 1; ioctlsocket(session->listener_socket, FIONBIO, &mode); #else fcntl(session->listener_socket, F_SETFL, O_NONBLOCK); #endif - const String shm_name = std::format("ia_shm_{}", sid); - IA_TRY(session->mapped_ptr, - FileOps::map_shared_memory(shm_name, shared_memory_size, true)); + Const shm_name = std::format("ia_shm_{}", sid); + session->mapped_ptr = + OX_TRY(FileOps::map_shared_memory(shm_name, shared_memory_size, true)); - auto *layout = reinterpret_cast(session->mapped_ptr); + Mut layout = + reinterpret_cast(session->mapped_ptr); layout->meta.magic = 0x49414950; layout->meta.version = 1; layout->meta.total_size = shared_memory_size; - const u64 header_size = IpcSharedMemoryLayout::get_header_size(); - const u64 usable_bytes = shared_memory_size - header_size; + Const header_size = IpcSharedMemoryLayout::get_header_size(); + Const usable_bytes = shared_memory_size - header_size; - u64 half_size = (usable_bytes / 2); + Mut half_size = (usable_bytes / 2); half_size -= (half_size % 64); layout->moni_data_offset = header_size; @@ -348,59 +339,54 @@ auto IpcManager::spawn_node(const Path &executable_path, u32 shared_memory_size) layout->mino_data_offset = header_size + half_size; layout->mino_data_size = half_size; - IA_TRY(session->moni, - RingBufferView::create( - &layout->moni_control, - Span(session->mapped_ptr + layout->moni_data_offset, - static_cast(layout->moni_data_size)), - true)); + session->moni = OX_TRY(RingBufferView::create( + &layout->moni_control, + Span(session->mapped_ptr + layout->moni_data_offset, + static_cast(layout->moni_data_size)), + true)); - IA_TRY(session->mino, - RingBufferView::create( - &layout->mino_control, - Span(session->mapped_ptr + layout->mino_data_offset, - static_cast(layout->mino_data_size)), - true)); + session->mino = OX_TRY(RingBufferView::create( + &layout->mino_control, + Span(session->mapped_ptr + layout->mino_data_offset, + static_cast(layout->mino_data_size)), + true)); - IpcConnectionDescriptor desc; + Mut desc; desc.socket_path = sock_path; desc.shared_mem_path = shm_name; desc.shared_mem_size = shared_memory_size; - const String args = std::format("\"{}\"", desc.serialize()); + Const args = std::format("\"{}\"", desc.serialize()); - IA_TRY(session->node_process, - ProcessOps::spawn_process_async( - FileOps::normalize_executable_path(executable_path).string(), args, - [sid](StringView line) { - if (env::is_debug) { - std::cout << std::format("{}[Node:{}:STDOUT|STDERR]: {}{}\n", - console::MAGENTA, sid, line, - console::RESET); - } - }, - [sid](Result result) { - if (env::is_debug) { - if (!result) { - std::cout << std::format( - "{}[Node: {}]: Failed to spawn with error '{}'{}\n", - console::RED, sid, result.error(), console::RESET); - } else { - std::cout << std::format( - "{}[Node: {}]: Exited with code {}{}\n", console::RED, - sid, *result, console::RESET); - } - } - })); + session->node_process = OX_TRY(ProcessOps::spawn_process_async( + FileOps::normalize_executable_path(executable_path).string(), args, + [sid](Const line) { + if (Env::IS_DEBUG) { + std::cout << std::format("{}[Node:{}:STDOUT|STDERR]: {}{}\n", + console::MAGENTA, sid, line, console::RESET); + } + }, + [sid](Const> result) { + if (Env::IS_DEBUG) { + if (!result) { + std::cout << std::format( + "{}[Node: {}]: Failed to spawn with error '{}'{}\n", + console::RED, sid, result.error(), console::RESET); + } else { + std::cout << std::format("{}[Node: {}]: Exited with code {}{}\n", + console::RED, sid, *result, + console::RESET); + } + } + })); - // Give some time for child node to stablize std::this_thread::sleep_for(std::chrono::seconds(1)); if (!session->node_process->is_active()) { return fail("Failed to spawn the child process \"{}\"", executable_path.string()); } - const auto process_id = session->node_process->id.load(); + Const process_id = session->node_process->id.load(); session->shared_mem_name = shm_name; session->creation_time = std::chrono::system_clock::now(); @@ -409,11 +395,12 @@ auto IpcManager::spawn_node(const Path &executable_path, u32 shared_memory_size) return process_id; } -auto IpcManager::wait_till_node_is_online(NativeProcessID node_id) -> bool { - bool is_pending = true; +auto IpcManager::wait_till_node_is_online(Const node_id) + -> bool { + Mut is_pending = true; while (is_pending) { is_pending = false; - for (const auto &session : m_pending_sessions) { + for (Const> &session : m_pending_sessions) { if (session->node_process->id.load() == node_id) { is_pending = true; break; @@ -425,13 +412,14 @@ auto IpcManager::wait_till_node_is_online(NativeProcessID node_id) -> bool { return m_active_session_map.contains(node_id); } -void IpcManager::shutdown_node(NativeProcessID node_id) { - const auto it_node = m_active_session_map.find(node_id); +void IpcManager::shutdown_node(Const node_id) { + Const::iterator> it_node = + m_active_session_map.find(node_id); if (it_node == m_active_session_map.end()) { return; } - auto *node = it_node->second; + Mut node = it_node->second; ProcessOps::terminate_process(node->node_process); FileOps::unmap_file(node->mapped_ptr); @@ -439,21 +427,23 @@ void IpcManager::shutdown_node(NativeProcessID node_id) { SocketOps::close(node->data_socket); std::erase_if(m_active_sessions, - [&](const auto &s) { return s.get() == node; }); + [&](Ref> s) { return s.get() == node; }); m_active_session_map.erase(it_node); } -void IpcManager::send_signal(NativeProcessID node, u8 signal) { - const auto it_node = m_active_session_map.find(node); +void IpcManager::send_signal(Const node, Const signal) { + Const::iterator> it_node = + m_active_session_map.find(node); if (it_node == m_active_session_map.end()) { return; } it_node->second->send_signal(signal); } -auto IpcManager::send_packet(NativeProcessID node, u16 packet_id, - Span payload) -> Result { - const auto it_node = m_active_session_map.find(node); +auto IpcManager::send_packet(Const node, Const packet_id, + Const>> payload) -> Result { + Const::iterator> it_node = + m_active_session_map.find(node); if (it_node == m_active_session_map.end()) return fail("no such node"); return it_node->second->send_packet(packet_id, payload); diff --git a/Src/IACore/imp/cpp/JSON.cpp b/Src/IACore/imp/cpp/JSON.cpp index effb325..7fbc6e8 100644 --- a/Src/IACore/imp/cpp/JSON.cpp +++ b/Src/IACore/imp/cpp/JSON.cpp @@ -15,7 +15,4 @@ #include -namespace IACore -{ - -} // namespace IACore \ No newline at end of file +namespace IACore {} // namespace IACore \ No newline at end of file diff --git a/Src/IACore/imp/cpp/Logger.cpp b/Src/IACore/imp/cpp/Logger.cpp index e4522a4..f80dbc3 100644 --- a/Src/IACore/imp/cpp/Logger.cpp +++ b/Src/IACore/imp/cpp/Logger.cpp @@ -1,16 +1,34 @@ +// IACore-OSS; The Core Library for All IA Open Source Projects +// Copyright (C) 2026 IAS (ias@iasoft.dev) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include + #include #include #include namespace IACore { -Logger::LogLevel Logger::m_log_level = Logger::LogLevel::Info; -std::ofstream Logger::m_log_file; +Mut Logger::m_log_level = Logger::LogLevel::Info; +Mut Logger::m_log_file; static auto get_seconds_count() -> f64 { - static const auto start_time = std::chrono::steady_clock::now(); - const auto now = std::chrono::steady_clock::now(); - const std::chrono::duration duration = now - start_time; + static Const> start_time = + std::chrono::steady_clock::now(); + Const> now = + std::chrono::steady_clock::now(); + Const> duration = now - start_time; return duration.count(); } @@ -23,7 +41,8 @@ auto Logger::terminate() -> void { } } -auto Logger::enable_logging_to_disk(const char *file_path) -> Result { +auto Logger::enable_logging_to_disk(Const *> file_path) + -> Result { if (m_log_file.is_open()) { m_log_file.flush(); m_log_file.close(); @@ -38,7 +57,7 @@ auto Logger::enable_logging_to_disk(const char *file_path) -> Result { return {}; } -auto Logger::set_log_level(LogLevel log_level) -> void { +auto Logger::set_log_level(Const log_level) -> void { m_log_level = log_level; } @@ -49,10 +68,11 @@ auto Logger::flush_logs() -> void { } } -auto Logger::log_internal(const char *prefix, const char *tag, String &&msg) - -> void { - const auto seconds = get_seconds_count(); - const auto out_line = std::format("[{:>8.3f}]: [{}]: {}", seconds, tag, msg); +auto Logger::log_internal(Const *> prefix, Const *> tag, + ForwardRef msg) -> void { + Const seconds = get_seconds_count(); + Const out_line = + std::format("[{:>8.3f}]: [{}]: {}", seconds, tag, msg); std::cout << prefix << out_line << console::RESET << '\n'; diff --git a/Src/IACore/imp/cpp/Platform.cpp b/Src/IACore/imp/cpp/Platform.cpp index 3506f3c..112221f 100644 --- a/Src/IACore/imp/cpp/Platform.cpp +++ b/Src/IACore/imp/cpp/Platform.cpp @@ -16,129 +16,119 @@ #include #if defined(IA_ARCH_X64) -# ifndef _MSC_VER -# include -# endif +#ifndef _MSC_VER +#include +#endif #elif defined(IA_ARCH_ARM64) -# if defined(__linux__) || defined(__ANDROID__) -# include -# include -# endif +#if defined(__linux__) || defined(__ANDROID__) +#include +#include +#endif #endif -namespace IACore -{ - Platform::Capabilities Platform::s_capabilities{}; +namespace IACore { +Mut Platform::s_capabilities{}; #if defined(IA_ARCH_X64) - auto Platform::cpuid(i32 function, i32 sub_function, i32 out[4]) -> void - { -# ifdef _MSC_VER - __cpuidex(reinterpret_cast(out), static_cast(function), static_cast(sub_function)); -# else - u32 a = 0; - u32 b = 0; - u32 c = 0; - u32 d = 0; - __cpuid_count(function, sub_function, a, b, c, d); - out[0] = static_cast(a); - out[1] = static_cast(b); - out[2] = static_cast(c); - out[3] = static_cast(d); -# endif - } -#endif - - auto Platform::check_cpu() -> bool - { -#if defined(IA_ARCH_X64) - i32 cpu_info[4]; - - cpuid(0, 0, cpu_info); - if (cpu_info[0] < 7) - { - return false; - } - - cpuid(1, 0, cpu_info); - // Bit 27: OSXSAVE, Bit 28: AVX, Bit 12: FMA - const bool osxsave = (cpu_info[2] & (1 << 27)) != 0; - const bool avx = (cpu_info[2] & (1 << 28)) != 0; - const bool fma = (cpu_info[2] & (1 << 12)) != 0; - - if (!osxsave || !avx || !fma) - { - return false; - } - - const u64 xcr_feature_mask = _xgetbv(0); - if ((xcr_feature_mask & 0x6) != 0x6) - { - return false; - } - - cpuid(7, 0, cpu_info); - // Bit 5: AVX2 - const bool avx2 = (cpu_info[1] & (1 << 5)) != 0; - if (!avx2) - { - return false; - } - - s_capabilities.hardware_crc32 = true; - -#elif defined(IA_ARCH_ARM64) -# if defined(__linux__) || defined(__ANDROID__) - const unsigned long hw_caps = getauxval(AT_HWCAP); - -# ifndef HWCAP_CRC32 -# define HWCAP_CRC32 (1 << 7) -# endif - - s_capabilities.hardware_crc32 = (hw_caps & HWCAP_CRC32) != 0; -# elif defined(IA_PLATFORM_APPLE) - // Apple silicon always has hardware CRC32 - s_capabilities.hardware_crc32 = true; -# else - s_capabilities.hardware_crc32 = false; -# endif +auto Platform::cpuid(Const function, Const sub_function, + Mut out[4]) -> void { +#ifdef _MSC_VER + __cpuidex(reinterpret_cast(out), static_cast(function), + static_cast(sub_function)); #else - s_capabilities.hardware_crc32 = false; + Mut a = 0; + Mut b = 0; + Mut c = 0; + Mut d = 0; + __cpuid_count(function, sub_function, a, b, c, d); + out[0] = static_cast(a); + out[1] = static_cast(b); + out[2] = static_cast(c); + out[3] = static_cast(d); +#endif +} #endif - return true; - } - auto Platform::get_architecture_name() -> const char * - { +auto Platform::check_cpu() -> bool { #if defined(IA_ARCH_X64) - return "x86_64"; -#elif defined(IA_ARCH_ARM64) - return "aarch64"; -#elif defined(IA_ARCH_WASM) - return "wasm"; -#else - return "unknown"; -#endif - } + Mut cpu_info[4]; - auto Platform::get_operating_system_name() -> const char * - { -#if IA_PLATFORM_WINDOWS - return "Windows"; + cpuid(0, 0, cpu_info); + if (cpu_info[0] < 7) { + return false; + } + + cpuid(1, 0, cpu_info); + Const osxsave = (cpu_info[2] & (1 << 27)) != 0; + Const avx = (cpu_info[2] & (1 << 28)) != 0; + Const fma = (cpu_info[2] & (1 << 12)) != 0; + + if (!osxsave || !avx || !fma) { + return false; + } + + Const xcr_feature_mask = _xgetbv(0); + if ((xcr_feature_mask & 0x6) != 0x6) { + return false; + } + + cpuid(7, 0, cpu_info); + Const avx2 = (cpu_info[1] & (1 << 5)) != 0; + if (!avx2) { + return false; + } + + s_capabilities.hardware_crc32 = true; + +#elif defined(IA_ARCH_ARM64) +#if defined(__linux__) || defined(__ANDROID__) + Const hw_caps = getauxval(AT_HWCAP); + +#ifndef HWCAP_CRC32 +#define HWCAP_CRC32 (1 << 7) +#endif + + s_capabilities.hardware_crc32 = (hw_caps & HWCAP_CRC32) != 0; #elif defined(IA_PLATFORM_APPLE) -# if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE - return "iOS"; -# else - return "macOS"; -# endif -#elif defined(__ANDROID__) - return "Android"; -#elif IA_PLATFORM_LINUX - return "Linux"; -#elif IA_PLATFORM_WASM - return "WebAssembly"; + s_capabilities.hardware_crc32 = true; #else - return "Unknown"; + s_capabilities.hardware_crc32 = false; #endif - } +#else + s_capabilities.hardware_crc32 = false; +#endif + return true; +} + +auto Platform::get_architecture_name() -> const char * { +#if defined(IA_ARCH_X64) + return "x86_64"; +#elif defined(IA_ARCH_ARM64) + return "aarch64"; +#elif defined(IA_ARCH_WASM) + return "wasm"; +#else + return "unknown"; +#endif +} + +auto Platform::get_operating_system_name() -> const char * { +#if IA_PLATFORM_WINDOWS + return "Windows"; +#elif defined(IA_PLATFORM_APPLE) +#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE + return "iOS"; +#else + return "macOS"; +#endif +#elif defined(__ANDROID__) + return "Android"; +#elif IA_PLATFORM_LINUX + return "Linux"; +#elif IA_PLATFORM_WASM + return "WebAssembly"; +#else + return "Unknown"; +#endif +} } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/imp/cpp/ProcessOps.cpp b/Src/IACore/imp/cpp/ProcessOps.cpp index da22b82..35a219c 100644 --- a/Src/IACore/imp/cpp/ProcessOps.cpp +++ b/Src/IACore/imp/cpp/ProcessOps.cpp @@ -16,23 +16,18 @@ #include namespace IACore { -// --------------------------------------------------------------------- -// Output Buffering Helper -// Splits raw chunks into lines, preserving partial lines across chunks -// --------------------------------------------------------------------- struct LineBuffer { - String m_accumulator; - std::function &m_callback; + Mut m_accumulator; + Const)>> m_callback; - void append(const char *data, usize size); - void flush(); + auto append(Const data, Const size) -> void; + auto flush() -> void; }; -void LineBuffer::append(const char *data, usize size) { - usize start = 0; - for (usize i = 0; i < size; ++i) { +auto LineBuffer::append(Const data, Const size) -> void { + Mut start = 0; + for (Mut i = 0; i < size; ++i) { if (data[i] == '\n' || data[i] == '\r') { - // Flush Accumulator + current chunk if (!m_accumulator.empty()) { m_accumulator.append(data + start, i - start); if (!m_accumulator.empty()) { @@ -45,20 +40,18 @@ void LineBuffer::append(const char *data, usize size) { } } - // 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) { m_accumulator.append(data + start, size - start); } } -void LineBuffer::flush() { +auto LineBuffer::flush() -> void { if (!m_accumulator.empty()) { m_callback(m_accumulator); m_accumulator.clear(); @@ -74,11 +67,11 @@ auto ProcessOps::get_current_process_id() -> NativeProcessID { } auto ProcessOps::spawn_process_sync( - const String &command, const String &args, - std::function on_output_line_callback) + Ref command, Ref args, + Const)>> on_output_line_callback) -> Result { - std::atomic id = 0; - if constexpr (env::is_windows) { + Mut> id = 0; + if constexpr (Env::IS_WINDOWS) { return spawn_process_windows(command, args, on_output_line_callback, id); } else { return spawn_process_posix(command, args, on_output_line_callback, id); @@ -86,23 +79,21 @@ auto ProcessOps::spawn_process_sync( } auto ProcessOps::spawn_process_async( - const String &command, const String &args, - std::function on_output_line_callback, - std::function)> on_finish_callback) + Ref command, Ref args, + Const)>> on_output_line_callback, + Const>)>> on_finish_callback) -> Result> { - auto handle = make_box(); + Mut> handle = make_box(); handle->is_running = true; - // Capture raw pointer to handle internals safely because jthread joins on - // destruction - ProcessHandle *h_ptr = handle.get(); + Mut h_ptr = handle.get(); handle->m_thread_handle = std::jthread([h_ptr, cmd = command, arg = args, cb = on_output_line_callback, fin = on_finish_callback]() mutable { - Result result = fail("Platform not supported"); + Mut> result = fail("Platform not supported"); - if constexpr (env::is_windows) { + if constexpr (Env::IS_WINDOWS) { result = spawn_process_windows(cmd, arg, cb, h_ptr->id); } else { result = spawn_process_posix(cmd, arg, cb, h_ptr->id); @@ -122,18 +113,18 @@ auto ProcessOps::spawn_process_async( return handle; } -void ProcessOps::terminate_process(const Box &handle) { +auto ProcessOps::terminate_process(Ref> handle) -> void { if (!handle || !handle->is_active()) { return; } - NativeProcessID pid = handle->id.load(); + Const pid = handle->id.load(); if (pid == 0) { return; } #if IA_PLATFORM_WINDOWS - HANDLE h_process = OpenProcess(PROCESS_TERMINATE, false, pid); + Mut h_process = OpenProcess(PROCESS_TERMINATE, false, pid); if (h_process != NULL) { ::TerminateProcess(h_process, 9); CloseHandle(h_process); @@ -145,39 +136,35 @@ void ProcessOps::terminate_process(const Box &handle) { } auto ProcessOps::spawn_process_windows( - const String &command, const String &args, - std::function on_output_line_callback, - std::atomic &id) -> Result { + Ref command, Ref args, + Const)>> on_output_line_callback, + MutRef> id) -> Result { #if IA_PLATFORM_WINDOWS - SECURITY_ATTRIBUTES sa_attr = {sizeof(SECURITY_ATTRIBUTES), NULL, - true}; // Allow inheritance - HANDLE h_read = NULL; - HANDLE h_write = NULL; + Mut sa_attr = {sizeof(SECURITY_ATTRIBUTES), NULL, true}; + Mut h_read = NULL; + Mut h_write = NULL; if (!CreatePipe(&h_read, &h_write, &sa_attr, 0)) { return fail("Failed to create pipe"); } - // Ensure the read handle to the pipe for STDOUT is NOT inherited if (!SetHandleInformation(h_read, HANDLE_FLAG_INHERIT, 0)) { return fail("Failed to secure pipe handles"); } - STARTUPINFOA si = {sizeof(STARTUPINFOA)}; + Mut si = {sizeof(STARTUPINFOA)}; si.dwFlags |= STARTF_USESTDHANDLES; si.hStdOutput = h_write; - si.hStdError = h_write; // Merge stderr - si.hStdInput = NULL; // No input + si.hStdError = h_write; + si.hStdInput = NULL; - PROCESS_INFORMATION pi = {0}; + Mut pi = {0}; - // Windows command line needs to be mutable and concatenated - String command_line = std::format("\"{}\" {}", command, args); + Mut command_line = std::format("\"{}\" {}", command, args); - BOOL success = CreateProcessA(NULL, command_line.data(), NULL, NULL, true, 0, - NULL, NULL, &si, &pi); + Const success = CreateProcessA(NULL, command_line.data(), NULL, NULL, + true, 0, NULL, NULL, &si, &pi); - // Close write end in parent, otherwise ReadFile never returns EOF! CloseHandle(h_write); if (!success) { @@ -187,19 +174,18 @@ auto ProcessOps::spawn_process_windows( id.store(pi.dwProcessId); - // Read Loop - LineBuffer line_buf{"", on_output_line_callback}; - DWORD bytes_read = 0; - char buffer[4096]; + Mut line_buf{"", on_output_line_callback}; + Mut bytes_read = 0; + Mut> buffer; - while (ReadFile(h_read, buffer, sizeof(buffer), &bytes_read, NULL) && + while (ReadFile(h_read, buffer.data(), static_cast(buffer.size()), + &bytes_read, NULL) && bytes_read != 0) { - line_buf.append(buffer, bytes_read); + line_buf.append(buffer.data(), bytes_read); } line_buf.flush(); - // NOW we wait for exit code - DWORD exit_code = 0; + Mut exit_code = 0; WaitForSingleObject(pi.hProcess, INFINITE); GetExitCodeProcess(pi.hProcess, &exit_code); @@ -210,69 +196,63 @@ auto ProcessOps::spawn_process_windows( return static_cast(exit_code); #else - (void)command; - (void)args; - (void)on_output_line_callback; - (void)id; + OX_UNUSED(command); + OX_UNUSED(args); + OX_UNUSED(on_output_line_callback); + OX_UNUSED(id); return fail("Windows implementation not available."); #endif } auto ProcessOps::spawn_process_posix( - const String &command, const String &args, - std::function on_output_line_callback, - std::atomic &id) -> Result { + Ref command, Ref args, + Const)>> on_output_line_callback, + MutRef> id) -> Result { #if IA_PLATFORM_UNIX - int pipefd[2]; - if (pipe(pipefd) == -1) { + Mut> pipefd; + if (pipe(pipefd.data()) == -1) { return fail("Failed to create pipe"); } - pid_t pid = fork(); + Const pid = fork(); if (pid == -1) { return fail("Failed to fork process"); } else if (pid == 0) { - // --- Child Process --- close(pipefd[0]); dup2(pipefd[1], STDOUT_FILENO); dup2(pipefd[1], STDERR_FILENO); close(pipefd[1]); - Vec arg_storage; // To keep strings alive - Vec argv; + Mut> arg_storage; + Mut> argv; - String cmd_str = command; + Mut cmd_str = command; argv.push_back(cmd_str.data()); - // Manual Quote-Aware Splitter - String current_token; - bool in_quotes = false; - bool is_escaped = false; + Mut current_token; + Mut in_quotes = false; + Mut is_escaped = false; - for (char c : args) { + for (Const c : args) { if (is_escaped) { - // Previous char was '\', so we treat this char literally. current_token += c; is_escaped = false; continue; } if (c == '\\') { - // Escape sequence start is_escaped = true; continue; } if (c == '\"') { - // Toggle quote state in_quotes = !in_quotes; continue; } if (c == ' ' && !in_quotes) { - // Token boundary if (!current_token.empty()) { arg_storage.push_back(current_token); current_token.clear(); @@ -286,8 +266,7 @@ auto ProcessOps::spawn_process_posix( arg_storage.push_back(current_token); } - // Build char* array from the std::string storage - for (auto &s : arg_storage) { + for (MutRef s : arg_storage) { argv.push_back(s.data()); } argv.push_back(nullptr); @@ -295,22 +274,21 @@ auto ProcessOps::spawn_process_posix( execvp(argv[0], argv.data()); _exit(127); } else { - // --- Parent Process --- id.store(pid); close(pipefd[1]); - LineBuffer line_buf{"", on_output_line_callback}; - char buffer[4096]; - ssize_t count; + Mut line_buf{"", on_output_line_callback}; + Mut> buffer; + Mut count; - while ((count = read(pipefd[0], buffer, sizeof(buffer))) > 0) { - line_buf.append(buffer, static_cast(count)); + while ((count = read(pipefd[0], buffer.data(), buffer.size())) > 0) { + line_buf.append(buffer.data(), static_cast(count)); } line_buf.flush(); close(pipefd[0]); - int status; + Mut status; waitpid(pid, &status, 0); id.store(0); @@ -320,10 +298,10 @@ auto ProcessOps::spawn_process_posix( return -1; } #else - (void)command; - (void)args; - (void)on_output_line_callback; - (void)id; + OX_UNUSED(command); + OX_UNUSED(args); + OX_UNUSED(on_output_line_callback); + OX_UNUSED(id); return fail("Posix implementation not available."); #endif } diff --git a/Src/IACore/imp/cpp/SIMD.cpp b/Src/IACore/imp/cpp/SIMD.cpp index d098ae1..687d33a 100644 --- a/Src/IACore/imp/cpp/SIMD.cpp +++ b/Src/IACore/imp/cpp/SIMD.cpp @@ -15,7 +15,4 @@ #include -namespace IACore -{ - -} \ No newline at end of file +namespace IACore {} // namespace IACore \ No newline at end of file diff --git a/Src/IACore/imp/cpp/SocketOps.cpp b/Src/IACore/imp/cpp/SocketOps.cpp index 3f02ae8..9d1a421 100644 --- a/Src/IACore/imp/cpp/SocketOps.cpp +++ b/Src/IACore/imp/cpp/SocketOps.cpp @@ -17,9 +17,9 @@ #include namespace IACore { -i32 SocketOps::s_init_count = 0; +Mut SocketOps::s_init_count = 0; -auto SocketOps::close(SocketHandle sock) -> void { +auto SocketOps::close(Const sock) -> void { if (sock == INVALID_SOCKET) { return; } @@ -30,7 +30,8 @@ auto SocketOps::close(SocketHandle sock) -> void { #endif } -auto SocketOps::listen(SocketHandle sock, i32 queue_size) -> Result { +auto SocketOps::listen(Const sock, Const queue_size) + -> Result { if (::listen(sock, queue_size) == 0) { return {}; } @@ -43,7 +44,7 @@ auto SocketOps::listen(SocketHandle sock, i32 queue_size) -> Result { } auto SocketOps::create_unix_socket() -> Result { - const SocketHandle sock = socket(AF_UNIX, SOCK_STREAM, 0); + Const sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { #if IA_PLATFORM_WINDOWS return fail("socket(AF_UNIX) failed: {}", WSAGetLastError()); @@ -54,18 +55,18 @@ auto SocketOps::create_unix_socket() -> Result { return sock; } -auto SocketOps::bind_unix_socket(SocketHandle sock, const char *path) - -> Result { +auto SocketOps::bind_unix_socket(Const sock, + Const path) -> Result { if (sock == INVALID_SOCKET) { return fail("Invalid socket handle"); } unlink_file(path); - sockaddr_un addr{}; + Mut addr{}; addr.sun_family = AF_UNIX; - const usize max_len = sizeof(addr.sun_path) - 1; + Const max_len = sizeof(addr.sun_path) - 1; #if IA_PLATFORM_WINDOWS strncpy_s(addr.sun_path, sizeof(addr.sun_path), path, max_len); #else @@ -84,16 +85,16 @@ auto SocketOps::bind_unix_socket(SocketHandle sock, const char *path) return {}; } -auto SocketOps::connect_unix_socket(SocketHandle sock, const char *path) - -> Result { +auto SocketOps::connect_unix_socket(Const sock, + Const path) -> Result { if (sock == INVALID_SOCKET) { return fail("Invalid socket handle"); } - sockaddr_un addr{}; + Mut addr{}; addr.sun_family = AF_UNIX; - const usize max_len = sizeof(addr.sun_path) - 1; + Const max_len = sizeof(addr.sun_path) - 1; #if IA_PLATFORM_WINDOWS strncpy_s(addr.sun_path, sizeof(addr.sun_path), path, max_len); #else @@ -112,19 +113,18 @@ auto SocketOps::connect_unix_socket(SocketHandle sock, const char *path) return {}; } -auto SocketOps::is_port_available(u16 port, i32 type) -> bool { - // Use 0 for protocol to let OS select default (TCP for STREAM, UDP for DGRAM) - const SocketHandle sock = socket(AF_INET, type, 0); +auto SocketOps::is_port_available(Const port, Const type) -> bool { + Const sock = socket(AF_INET, type, 0); if (sock == INVALID_SOCKET) { return false; } - sockaddr_in addr{}; + Mut addr{}; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = htonl(INADDR_ANY); - bool is_free = false; + Mut is_free = false; if (::bind(sock, reinterpret_cast(&addr), sizeof(addr)) == 0) { is_free = true; diff --git a/Src/IACore/imp/cpp/StreamReader.cpp b/Src/IACore/imp/cpp/StreamReader.cpp index 10ebe90..8dceb19 100644 --- a/Src/IACore/imp/cpp/StreamReader.cpp +++ b/Src/IACore/imp/cpp/StreamReader.cpp @@ -17,30 +17,29 @@ #include namespace IACore { -auto StreamReader::create_from_file(const Path &path) -> Result { - usize size = 0; +auto StreamReader::create_from_file(Ref path) -> Result { + Mut size = 0; - const u8 *ptr; - IA_TRY(ptr, FileOps::map_file(path, size)); + Const ptr = OX_TRY(FileOps::map_file(path, size)); - StreamReader reader(Span(ptr, size)); + Mut reader(Span(ptr, size)); reader.m_storage_type = StorageType::OwningMmap; return reader; } -StreamReader::StreamReader(Vec &&data) +StreamReader::StreamReader(ForwardRef> data) : m_owning_vector(std::move(data)), m_storage_type(StorageType::OwningVector) { m_data = m_owning_vector.data(); m_data_size = m_owning_vector.size(); } -StreamReader::StreamReader(Span data) +StreamReader::StreamReader(Const> data) : m_data(data.data()), m_data_size(data.size()), m_storage_type(StorageType::NonOwning) {} -StreamReader::StreamReader(StreamReader &&other) +StreamReader::StreamReader(ForwardRef other) : m_data(other.m_data), m_cursor(other.m_cursor), m_data_size(other.m_data_size), m_owning_vector(std::move(other.m_owning_vector)), @@ -49,11 +48,13 @@ StreamReader::StreamReader(StreamReader &&other) other.m_data = {}; other.m_data_size = 0; - if (m_storage_type == StorageType::OwningVector) + if (m_storage_type == StorageType::OwningVector) { m_data = m_owning_vector.data(); + } } -auto StreamReader::operator=(StreamReader &&other) -> StreamReader & { +auto StreamReader::operator=(ForwardRef other) + -> MutRef { if (this != &other) { if (m_storage_type == StorageType::OwningMmap) { FileOps::unmap_file(m_data); @@ -65,8 +66,9 @@ auto StreamReader::operator=(StreamReader &&other) -> StreamReader & { m_owning_vector = std::move(other.m_owning_vector); m_storage_type = other.m_storage_type; - if (m_storage_type == StorageType::OwningVector) + if (m_storage_type == StorageType::OwningVector) { m_data = m_owning_vector.data(); + } other.m_storage_type = StorageType::NonOwning; other.m_data = {}; diff --git a/Src/IACore/imp/cpp/StreamWriter.cpp b/Src/IACore/imp/cpp/StreamWriter.cpp index ac1a4f9..7008f5d 100644 --- a/Src/IACore/imp/cpp/StreamWriter.cpp +++ b/Src/IACore/imp/cpp/StreamWriter.cpp @@ -17,14 +17,14 @@ namespace IACore { -auto StreamWriter::create_from_file(const Path &path) -> Result { - FILE *f = std::fopen(path.string().c_str(), "wb"); +auto StreamWriter::create_from_file(Ref path) -> Result { + Mut f = std::fopen(path.string().c_str(), "wb"); if (!f) { return fail("Failed to open file for writing: {}", path.string()); } std::fclose(f); - StreamWriter writer; + Mut writer; writer.m_file_path = path; writer.m_storage_type = StorageType::OwningFile; @@ -37,11 +37,11 @@ StreamWriter::StreamWriter() : m_storage_type(StorageType::OwningVector) { m_buffer = m_owning_vector.data(); } -StreamWriter::StreamWriter(Span data) +StreamWriter::StreamWriter(Const> data) : m_buffer(data.data()), m_cursor(0), m_capacity(data.size()), m_storage_type(StorageType::NonOwning) {} -StreamWriter::StreamWriter(StreamWriter &&other) +StreamWriter::StreamWriter(ForwardRef other) : m_buffer(other.m_buffer), m_cursor(other.m_cursor), m_capacity(other.m_capacity), m_file_path(other.m_file_path), m_owning_vector(std::move(other.m_owning_vector)), @@ -54,11 +54,11 @@ StreamWriter::StreamWriter(StreamWriter &&other) m_buffer = m_owning_vector.data(); } -auto StreamWriter::operator=(StreamWriter &&other) -> StreamWriter & { +auto StreamWriter::operator=(ForwardRef other) + -> MutRef { if (this != &other) { - // Flush current if needed if (m_storage_type == StorageType::OwningFile) { - if (auto res = flush_to_disk(); !res) { + if (Const> res = flush_to_disk(); !res) { std::fprintf(stderr, "[IACore] Data loss in StreamWriter move: %s\n", res.error().c_str()); } @@ -67,7 +67,7 @@ auto StreamWriter::operator=(StreamWriter &&other) -> StreamWriter & { m_buffer = other.m_buffer; m_cursor = other.m_cursor; m_capacity = other.m_capacity; - m_file_path = std::move(other.m_file_path); // Use move for string/path + m_file_path = std::move(other.m_file_path); m_owning_vector = std::move(other.m_owning_vector); m_storage_type = other.m_storage_type; @@ -84,9 +84,7 @@ auto StreamWriter::operator=(StreamWriter &&other) -> StreamWriter & { StreamWriter::~StreamWriter() { if (m_storage_type == StorageType::OwningFile) { - // We can't return errors here, so we log them. - // Ideally, the user calls save() before this runs. - if (auto res = flush_to_disk(); !res) { + if (Const> res = flush_to_disk(); !res) { std::fprintf(stderr, "[IACore] LOST DATA in ~StreamWriter: %s\n", res.error().c_str()); } @@ -94,11 +92,9 @@ StreamWriter::~StreamWriter() { } auto StreamWriter::flush() -> Result { - auto res = flush_to_disk(); - // Prevent double-write in destructor if save was successful + Mut> res = flush_to_disk(); if (res.has_value()) { - m_storage_type = StorageType::OwningVector; // downgrade to vector so dtor - // doesn't write again + m_storage_type = StorageType::OwningVector; } return res; } @@ -108,12 +104,12 @@ auto StreamWriter::flush_to_disk() -> Result { return {}; } - FILE *f = std::fopen(m_file_path.string().c_str(), "wb"); + Mut f = std::fopen(m_file_path.string().c_str(), "wb"); if (!f) { return fail("Failed to open file for writing: {}", m_file_path.string()); } - usize written = std::fwrite(m_buffer, 1, m_cursor, f); + Const written = std::fwrite(m_buffer, 1, m_cursor, f); std::fclose(f); if (written != m_cursor) { @@ -122,15 +118,15 @@ auto StreamWriter::flush_to_disk() -> Result { return {}; } -auto StreamWriter::write(u8 byte, usize count) -> Result { +auto StreamWriter::write(Const byte, Const count) -> Result { if (m_cursor + count > m_capacity) { if (m_storage_type == StorageType::NonOwning) { return fail("StreamWriter buffer overflow (NonOwning)"); } - const usize required = m_cursor + count; - const usize double_cap = m_capacity * 2; - const usize new_capacity = (double_cap > required) ? double_cap : required; + Const required = m_cursor + count; + Const double_cap = m_capacity * 2; + Const new_capacity = (double_cap > required) ? double_cap : required; m_owning_vector.resize(new_capacity); m_capacity = m_owning_vector.size(); @@ -142,17 +138,16 @@ auto StreamWriter::write(u8 byte, usize count) -> Result { return {}; } -auto StreamWriter::write(const void *buffer, usize size) -> Result { +auto StreamWriter::write(Const buffer, Const size) + -> Result { if (m_cursor + size > m_capacity) { if (m_storage_type == StorageType::NonOwning) { return fail("StreamWriter buffer overflow (NonOwning)"); } - // NEW STRATEGY: Max(Double Capacity, Required Size) - // This prevents frequent reallocations for repeated small writes - const usize required = m_cursor + size; - const usize double_cap = m_capacity * 2; - const usize new_capacity = (double_cap > required) ? double_cap : required; + Const required = m_cursor + size; + Const double_cap = m_capacity * 2; + Const new_capacity = (double_cap > required) ? double_cap : required; m_owning_vector.resize(new_capacity); m_capacity = m_owning_vector.size(); diff --git a/Src/IACore/imp/cpp/StringOps.cpp b/Src/IACore/imp/cpp/StringOps.cpp index 8bea68e..5e42375 100644 --- a/Src/IACore/imp/cpp/StringOps.cpp +++ b/Src/IACore/imp/cpp/StringOps.cpp @@ -17,15 +17,15 @@ namespace IACore { -static const String BASE64_CHAR_TABLE = +static Const BASE64_CHAR_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static auto is_base64(u8 c) -> bool { +static auto is_base64(Const c) -> bool { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c == '+') || (c == '/'); } -static auto get_base64_index(u8 c) -> u8 { +static auto get_base64_index(Const c) -> u8 { if (c >= 'A' && c <= 'Z') return c - 'A'; if (c >= 'a' && c <= 'z') @@ -39,16 +39,16 @@ static auto get_base64_index(u8 c) -> u8 { return 0; } -auto StringOps::encode_base64(Span data) -> String { - String result; +auto StringOps::encode_base64(Const>> data) -> String { + Mut result; result.reserve(((data.size() + 2) / 3) * 4); - for (usize i = 0; i < data.size(); i += 3) { - u32 b0 = data[i]; - u32 b1 = (i + 1 < data.size()) ? data[i + 1] : 0; - u32 b2 = (i + 2 < data.size()) ? data[i + 2] : 0; + for (Mut i = 0; i < data.size(); i += 3) { + Const b0 = data[i]; + Const b1 = (i + 1 < data.size()) ? data[i + 1] : 0; + Const b2 = (i + 2 < data.size()) ? data[i + 2] : 0; - u32 triple = (b0 << 16) | (b1 << 8) | b2; + Const triple = (b0 << 16) | (b1 << 8) | b2; result += BASE64_CHAR_TABLE[(triple >> 18) & 0x3F]; result += BASE64_CHAR_TABLE[(triple >> 12) & 0x3F]; @@ -68,15 +68,15 @@ auto StringOps::encode_base64(Span data) -> String { return result; } -auto StringOps::decode_base64(const String &data) -> Vec { - Vec result; +auto StringOps::decode_base64(Ref data) -> Vec { + Mut> result; result.reserve(data.size() * 3 / 4); - i32 i = 0; - u8 tmp_buf[4]; + Mut i = 0; + Mut> tmp_buf = {}; - for (char c_char : data) { - u8 c = static_cast(c_char); + for (Const c_char : data) { + Const c = static_cast(c_char); if (c == '=') { break; } @@ -86,10 +86,10 @@ auto StringOps::decode_base64(const String &data) -> Vec { tmp_buf[i++] = c; if (i == 4) { - u8 n0 = get_base64_index(tmp_buf[0]); - u8 n1 = get_base64_index(tmp_buf[1]); - u8 n2 = get_base64_index(tmp_buf[2]); - u8 n3 = get_base64_index(tmp_buf[3]); + Const n0 = get_base64_index(tmp_buf[0]); + Const n1 = get_base64_index(tmp_buf[1]); + Const n2 = get_base64_index(tmp_buf[2]); + Const n3 = get_base64_index(tmp_buf[3]); result.push_back((n0 << 2) | ((n1 & 0x30) >> 4)); result.push_back(((n1 & 0x0F) << 4) | ((n2 & 0x3C) >> 2)); @@ -100,13 +100,13 @@ auto StringOps::decode_base64(const String &data) -> Vec { } if (i > 0) { - for (i32 j = i; j < 4; ++j) { - tmp_buf[j] = 'A'; // Pad with 'A' (index 0) + for (Mut j = i; j < 4; ++j) { + tmp_buf[j] = 'A'; } - u8 n0 = get_base64_index(tmp_buf[0]); - u8 n1 = get_base64_index(tmp_buf[1]); - u8 n2 = get_base64_index(tmp_buf[2]); + Const n0 = get_base64_index(tmp_buf[0]); + Const n1 = get_base64_index(tmp_buf[1]); + Const n2 = get_base64_index(tmp_buf[2]); if (i > 1) { result.push_back((n0 << 2) | ((n1 & 0x30) >> 4)); diff --git a/Src/IACore/imp/cpp/Utils.cpp b/Src/IACore/imp/cpp/Utils.cpp index 1fe5e49..9e24cdf 100644 --- a/Src/IACore/imp/cpp/Utils.cpp +++ b/Src/IACore/imp/cpp/Utils.cpp @@ -17,104 +17,96 @@ #include #include -namespace IACore -{ - extern std::chrono::high_resolution_clock::time_point g_start_time; +namespace IACore { +namespace { +auto from_hex_char(Const c) -> i32 { + 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; +} +} // namespace - auto Utils::get_unix_time() -> u64 - { - const auto now = std::chrono::system_clock::now(); - return std::chrono::duration_cast(now.time_since_epoch()).count(); +extern Mut g_start_time; + +auto Utils::get_unix_time() -> u64 { + Const now = + std::chrono::system_clock::now(); + return std::chrono::duration_cast( + now.time_since_epoch()) + .count(); +} + +auto Utils::get_ticks_count() -> u64 { + Const duration = + std::chrono::high_resolution_clock::now() - g_start_time; + return std::chrono::duration_cast(duration) + .count(); +} + +auto Utils::get_seconds_count() -> f64 { + Const duration = + std::chrono::high_resolution_clock::now() - g_start_time; + return static_cast( + std::chrono::duration_cast(duration).count()); +} + +auto Utils::get_random() -> f32 { + return static_cast(std::rand()) / static_cast(RAND_MAX); +} + +auto Utils::get_random(Const max) -> u64 { + return static_cast(static_cast(max) * get_random()); +} + +auto Utils::get_random(Const min, Const max) -> i64 { + return min + static_cast(static_cast(max - min) * get_random()); +} + +auto Utils::sleep(Const milliseconds) -> void { + std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); +} + +auto Utils::binary_to_hex_string(Const>> data) -> String { + static constexpr Const lut = "0123456789ABCDEF"; + Mut res = String(); + res.reserve(data.size() * 2); + + for (Const b : data) { + res.push_back(lut[(b >> 4) & 0x0F]); + res.push_back(lut[b & 0x0F]); + } + return res; +} + +auto Utils::hex_string_to_binary(Const hex) -> Result> { + if (hex.size() % 2 != 0) { + return fail("Hex string must have even length"); + } + + Mut> out = Vec(); + out.reserve(hex.size() / 2); + + for (Mut i = 0; i < hex.size(); i += 2) { + Const high = hex[i]; + Const low = hex[i + 1]; + + Const h = from_hex_char(high); + Const l = from_hex_char(low); + + if (h == -1 || l == -1) { + return fail("Invalid hex character found"); } - auto Utils::get_ticks_count() -> u64 - { - const auto duration = std::chrono::high_resolution_clock::now() - g_start_time; - return std::chrono::duration_cast(duration).count(); - } + out.push_back(static_cast((h << 4) | l)); + } - auto Utils::get_seconds_count() -> f64 - { - const auto duration = std::chrono::high_resolution_clock::now() - g_start_time; - return static_cast(std::chrono::duration_cast(duration).count()); - } - - auto Utils::get_random() -> f32 - { - return static_cast(std::rand()) / static_cast(RAND_MAX); - } - - auto Utils::get_random(u64 max) -> u64 - { - return static_cast(static_cast(max) * get_random()); - } - - auto Utils::get_random(i64 min, i64 max) -> i64 - { - return min + static_cast(static_cast(max - min) * get_random()); - } - - auto Utils::sleep(u64 milliseconds) -> void - { - std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); - } - - auto Utils::binary_to_hex_string(Span data) -> String - { - static constexpr char lut[] = "0123456789ABCDEF"; - auto res = String(); - res.reserve(data.size() * 2); - - for (const auto b : data) - { - res.push_back(lut[(b >> 4) & 0x0F]); - res.push_back(lut[b & 0x0F]); - } - return res; - } - - auto Utils::hex_string_to_binary(StringView hex) -> Result> - { - if (hex.size() % 2 != 0) - { - return fail("Hex string must have even length"); - } - - auto out = Vec(); - out.reserve(hex.size() / 2); - - for (usize i = 0; i < hex.size(); i += 2) - { - const auto high = hex[i]; - const auto low = hex[i + 1]; - - const auto from_hex_char = [](char c) -> i32 { - 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; - }; - - const auto h = from_hex_char(high); - const auto l = from_hex_char(low); - - if (h == -1 || l == -1) - { - return fail("Invalid hex character found"); - } - - out.push_back(static_cast((h << 4) | l)); - } - - return out; - } + return out; +} } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/imp/cpp/XML.cpp b/Src/IACore/imp/cpp/XML.cpp index fed8dbd..0831d68 100644 --- a/Src/IACore/imp/cpp/XML.cpp +++ b/Src/IACore/imp/cpp/XML.cpp @@ -14,71 +14,69 @@ // limitations under the License. #include +#include -namespace IACore -{ - Result XML::parse_from_string(const String &data) - { - Document doc; - const auto parseResult = doc.load_string(data.data()); - if (!parseResult) - return fail("Failed to parse XML {}", parseResult.description()); - return std::move(doc); +namespace IACore { + +auto XML::parse_from_string(Ref data) -> Result { + Mut doc; + Const parse_result = doc.load_string(data.c_str()); + if (!parse_result) { + return fail("Failed to parse XML {}", parse_result.description()); + } + return std::move(doc); +} + +auto XML::parse_from_file(Ref path) -> Result { + Mut doc; + Const parse_result = + doc.load_file(path.string().c_str()); + if (!parse_result) { + return fail("Failed to parse XML {}", parse_result.description()); + } + return std::move(doc); +} + +auto XML::serialize_to_string(Ref node, Const escape) -> String { + Mut oss; + node.print(oss); + return escape ? escape_xml_string(oss.str()) : oss.str(); +} + +auto XML::serialize_to_string(Ref doc, Const escape) -> String { + Mut oss; + doc.save(oss); + return escape ? escape_xml_string(oss.str()) : oss.str(); +} + +auto XML::escape_xml_string(Ref xml) -> String { + Mut buffer; + buffer.reserve(xml.size() + (xml.size() / 10)); + + for (Const c : xml) { + switch (c) { + case '&': + buffer.append("&"); + break; + case '\"': + buffer.append("""); + break; + case '\'': + buffer.append("'"); + break; + case '<': + buffer.append("<"); + break; + case '>': + buffer.append(">"); + break; + default: + buffer.push_back(c); + break; } + } - Result XML::parse_from_file(const Path &path) - { - Document doc; - const auto parseResult = doc.load_file(path.string().c_str()); - if (!parseResult) - return fail("Failed to parse XML {}", parseResult.description()); - return std::move(doc); - } + return buffer; +} - String XML::serialize_to_string(const Node &node, bool escape) - { - std::ostringstream oss; - node.print(oss); - return escape ? escape_xml_string(oss.str()) : oss.str(); - } - - String XML::serialize_to_string(const Document &doc, bool escape) - { - std::ostringstream oss; - doc.save(oss); - return escape ? escape_xml_string(oss.str()) : oss.str(); - } - - String XML::escape_xml_string(const String &xml) - { - String buffer; - buffer.reserve(xml.size() + (xml.size() / 10)); - - for (char c : xml) - { - switch (c) - { - case '&': - buffer.append("&"); - break; - case '\"': - buffer.append("""); - break; - case '\'': - buffer.append("'"); - break; - case '<': - buffer.append("<"); - break; - case '>': - buffer.append(">"); - break; - default: - buffer.push_back(c); - break; - } - } - - return buffer; - } } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/inc/IACore/ADT/RingBuffer.hpp b/Src/IACore/inc/IACore/ADT/RingBuffer.hpp index 7248b15..2839c73 100644 --- a/Src/IACore/inc/IACore/ADT/RingBuffer.hpp +++ b/Src/IACore/inc/IACore/ADT/RingBuffer.hpp @@ -16,96 +16,90 @@ #pragma once #include -#include namespace IACore { class RingBufferView { public: - static constexpr u16 PACKET_ID_SKIP = 0; + static constexpr Const PACKET_ID_SKIP = 0; struct ControlBlock { struct alignas(64) { - std::atomic write_offset{0}; + Mut> write_offset{0}; } producer; struct alignas(64) { - std::atomic read_offset{0}; - // Capacity is effectively constant after init, - // so it doesn't cause false sharing invalidations. - u32 capacity{0}; + Mut> read_offset{0}; + Mut capacity{0}; } consumer; }; static_assert(offsetof(ControlBlock, consumer) == 64, "False sharing detected in ControlBlock"); - // All of the data in ring buffer will be stored as packets struct PacketHeader { PacketHeader() : id(0), payload_size(0) {} - PacketHeader(u16 id) : id(id), payload_size(0) {} + PacketHeader(Const id) : id(id), payload_size(0) {} - PacketHeader(u16 id, u16 payload_size) + PacketHeader(Const id, Const payload_size) : id(id), payload_size(payload_size) {} - u16 id{}; - u16 payload_size{}; + Mut id{}; + Mut payload_size{}; }; public: static auto default_instance() -> RingBufferView; - static auto create(Span buffer, bool is_owner) -> Result; - static auto create(ControlBlock *control_block, Span buffer, - bool is_owner) -> Result; + static auto create(Ref> buffer, Const is_owner) + -> Result; + static auto create(Const control_block, Ref> buffer, + Const is_owner) -> Result; // Returns: - // - Ok(nullopt) if empty - // - Ok(bytes_read) if success + // - nullopt if empty + // - bytes_read if success // - Error if buffer too small - auto pop(PacketHeader &out_header, Span out_buffer) + auto pop(MutRef out_header, Ref> out_buffer) -> Result>; - // Returns: - // - Ok() if success - // - Error if full - auto push(u16 packet_id, Span data) -> Result; + auto push(Const packet_id, Ref> data) -> Result; auto get_control_block() -> ControlBlock *; [[nodiscard]] auto is_valid() const -> bool; protected: - RingBufferView(Span buffer, bool is_owner); - RingBufferView(ControlBlock *control_block, Span buffer, bool is_owner); + RingBufferView(Ref> buffer, Const is_owner); + RingBufferView(Const control_block, Ref> buffer, + Const is_owner); private: - u8 *m_data_ptr{}; - u32 m_capacity{}; - ControlBlock *m_control_block{}; + Mut m_data_ptr{}; + Mut m_capacity{}; + Mut m_control_block{}; private: - auto write_wrapped(u32 offset, const void *data, u32 size) -> void; - auto read_wrapped(u32 offset, void *out_data, u32 size) -> void; + auto write_wrapped(Const offset, Const data, + Const size) -> void; + auto read_wrapped(Const offset, Const out_data, Const size) + -> void; }; -// ============================================================================= -// Implementation -// ============================================================================= - inline auto RingBufferView::default_instance() -> RingBufferView { return RingBufferView(nullptr, {}, false); } -inline auto RingBufferView::create(Span buffer, bool is_owner) +inline auto RingBufferView::create(Ref> buffer, Const is_owner) -> Result { if (buffer.size() <= sizeof(ControlBlock)) { return fail("Buffer too small for ControlBlock"); } if (!is_owner) { - auto *cb = reinterpret_cast(buffer.data()); - u32 capacity = static_cast(buffer.size()) - sizeof(ControlBlock); + Const cb = reinterpret_cast(buffer.data()); + Const capacity = + static_cast(buffer.size()) - sizeof(ControlBlock); if (cb->consumer.capacity != capacity) { return fail("Capacity mismatch"); } @@ -114,8 +108,9 @@ inline auto RingBufferView::create(Span buffer, bool is_owner) return RingBufferView(buffer, is_owner); } -inline auto RingBufferView::create(ControlBlock *control_block, Span buffer, - bool is_owner) -> Result { +inline auto RingBufferView::create(Const control_block, + Ref> buffer, Const is_owner) + -> Result { if (control_block == nullptr) { return fail("ControlBlock is null"); } @@ -126,7 +121,8 @@ inline auto RingBufferView::create(ControlBlock *control_block, Span buffer, return RingBufferView(control_block, buffer, is_owner); } -inline RingBufferView::RingBufferView(Span buffer, bool is_owner) { +inline RingBufferView::RingBufferView(Ref> buffer, + Const is_owner) { m_control_block = reinterpret_cast(buffer.data()); m_data_ptr = buffer.data() + sizeof(ControlBlock); @@ -139,8 +135,9 @@ inline RingBufferView::RingBufferView(Span buffer, bool is_owner) { } } -inline RingBufferView::RingBufferView(ControlBlock *control_block, - Span buffer, bool is_owner) { +inline RingBufferView::RingBufferView(Const control_block, + Ref> buffer, + Const is_owner) { m_control_block = control_block; m_data_ptr = buffer.data(); m_capacity = static_cast(buffer.size()); @@ -152,13 +149,14 @@ inline RingBufferView::RingBufferView(ControlBlock *control_block, } } -inline auto RingBufferView::pop(PacketHeader &out_header, Span out_buffer) +inline auto RingBufferView::pop(MutRef out_header, + Ref> out_buffer) -> Result> { - u32 write = + Const write = m_control_block->producer.write_offset.load(std::memory_order_acquire); - u32 read = + Const read = m_control_block->consumer.read_offset.load(std::memory_order_relaxed); - u32 cap = m_capacity; + Const cap = m_capacity; if (read == write) { return std::nullopt; @@ -172,12 +170,11 @@ inline auto RingBufferView::pop(PacketHeader &out_header, Span out_buffer) } if (out_header.payload_size > 0) { - u32 data_read_offset = (read + sizeof(PacketHeader)) % cap; + Const data_read_offset = (read + sizeof(PacketHeader)) % cap; read_wrapped(data_read_offset, out_buffer.data(), out_header.payload_size); } - // Move read pointer forward - u32 new_read_offset = + Const new_read_offset = (read + sizeof(PacketHeader) + out_header.payload_size) % cap; m_control_block->consumer.read_offset.store(new_read_offset, std::memory_order_release); @@ -185,39 +182,39 @@ inline auto RingBufferView::pop(PacketHeader &out_header, Span out_buffer) return std::make_optional(static_cast(out_header.payload_size)); } -inline auto RingBufferView::push(u16 packet_id, Span data) +inline auto RingBufferView::push(Const packet_id, Ref> data) -> Result { if (data.size() > std::numeric_limits::max()) { return fail("Data size exceeds u16 limit"); } - const u32 total_size = sizeof(PacketHeader) + static_cast(data.size()); + Const total_size = sizeof(PacketHeader) + static_cast(data.size()); - u32 read = + Const read = m_control_block->consumer.read_offset.load(std::memory_order_acquire); - u32 write = + Const write = m_control_block->producer.write_offset.load(std::memory_order_relaxed); - u32 cap = m_capacity; + Const cap = m_capacity; - u32 free_space = + Const free_space = (read <= write) ? (m_capacity - write) + read : (read - write); - // Ensure to always leave 1 byte empty to prevent Read == Write ambiguity + // Leave 1 byte empty (prevent ambiguities) if (free_space <= total_size) { return fail("RingBuffer full"); } - PacketHeader header{packet_id, static_cast(data.size())}; + Const header{packet_id, static_cast(data.size())}; write_wrapped(write, &header, sizeof(PacketHeader)); - u32 data_write_offset = (write + sizeof(PacketHeader)) % cap; + Const data_write_offset = (write + sizeof(PacketHeader)) % cap; if (!data.empty()) { write_wrapped(data_write_offset, data.data(), static_cast(data.size())); } - u32 new_write_offset = (data_write_offset + data.size()) % cap; + Const new_write_offset = (data_write_offset + data.size()) % cap; m_control_block->producer.write_offset.store(new_write_offset, std::memory_order_release); @@ -228,34 +225,32 @@ inline auto RingBufferView::get_control_block() -> ControlBlock * { return m_control_block; } -inline auto RingBufferView::write_wrapped(u32 offset, const void *data, - u32 size) -> void { +inline auto RingBufferView::write_wrapped(Const offset, + Const data, + Const size) -> void { if (offset + size <= m_capacity) { - // Contiguous write std::memcpy(m_data_ptr + offset, data, size); } else { - // Split write - u32 first_chunk = m_capacity - offset; - u32 second_chunk = size - first_chunk; + Const first_chunk = m_capacity - offset; + Const second_chunk = size - first_chunk; - const u8 *src = static_cast(data); + Const src = static_cast(data); std::memcpy(m_data_ptr + offset, src, first_chunk); std::memcpy(m_data_ptr, src + first_chunk, second_chunk); } } -inline auto RingBufferView::read_wrapped(u32 offset, void *out_data, u32 size) - -> void { +inline auto RingBufferView::read_wrapped(Const offset, + Const out_data, + Const size) -> void { if (offset + size <= m_capacity) { - // Contiguous read std::memcpy(out_data, m_data_ptr + offset, size); } else { - // Split read - u32 first_chunk = m_capacity - offset; - u32 second_chunk = size - first_chunk; + Const first_chunk = m_capacity - offset; + Const second_chunk = size - first_chunk; - u8 *dst = static_cast(out_data); + Const dst = static_cast(out_data); std::memcpy(dst, m_data_ptr + offset, first_chunk); std::memcpy(dst + first_chunk, m_data_ptr, second_chunk); diff --git a/Src/IACore/inc/IACore/AsyncOps.hpp b/Src/IACore/inc/IACore/AsyncOps.hpp index a352d61..1f15203 100644 --- a/Src/IACore/inc/IACore/AsyncOps.hpp +++ b/Src/IACore/inc/IACore/AsyncOps.hpp @@ -26,45 +26,46 @@ public: using TaskTag = u64; using WorkerId = u16; - static constexpr WorkerId MAIN_THREAD_WORKER_ID = 0; + static constexpr Const MAIN_THREAD_WORKER_ID = 0; enum class Priority : u8 { High, Normal }; struct Schedule { - std::atomic counter{0}; + Mut> counter{0}; }; public: - static auto initialize_scheduler(u8 worker_count = 0) -> Result; + static auto initialize_scheduler(Const worker_count = 0) -> Result; static auto terminate_scheduler() -> void; - static auto schedule_task(std::function task, - TaskTag tag, Schedule *schedule, - Priority priority = Priority::Normal) -> void; + static auto schedule_task(Mut)>> task, + Const tag, Mut schedule, + Const priority = Priority::Normal) + -> void; - static auto cancel_tasks_of_tag(TaskTag tag) -> void; + static auto cancel_tasks_of_tag(Const tag) -> void; - static auto wait_for_schedule_completion(Schedule *schedule) -> void; + static auto wait_for_schedule_completion(Mut schedule) -> void; - static auto run_task(std::function task) -> void; + static auto run_task(Mut> task) -> void; - [[nodiscard]] static auto get_worker_count() -> WorkerId; + IA_NODISCARD static auto get_worker_count() -> WorkerId; private: struct ScheduledTask { - TaskTag tag{}; - Schedule *schedule_handle{}; - std::function task{}; + Mut tag{}; + Mut schedule_handle{}; + Mut)>> task{}; }; - static auto schedule_worker_loop(std::stop_token stop_token, - WorkerId worker_id) -> void; + static auto schedule_worker_loop(Mut stop_token, + Const worker_id) -> void; private: - static std::mutex s_queue_mutex; - static std::condition_variable s_wake_condition; - static Vec s_schedule_workers; - static std::deque s_high_priority_queue; - static std::deque s_normal_priority_queue; + static Mut s_queue_mutex; + static Mut s_wake_condition; + static Mut> s_schedule_workers; + static Mut> s_high_priority_queue; + static Mut> s_normal_priority_queue; }; } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/inc/IACore/CLI.hpp b/Src/IACore/inc/IACore/CLI.hpp index 5b98c08..1a95192 100644 --- a/Src/IACore/inc/IACore/CLI.hpp +++ b/Src/IACore/inc/IACore/CLI.hpp @@ -28,15 +28,15 @@ class CLIParser { */ public: - CLIParser(Span args); + CLIParser(Const>> args); ~CLIParser() = default; public: - [[nodiscard]] auto remaining() const -> bool { + IA_NODISCARD auto remaining() const -> bool { return m_current_arg < m_arg_list.end(); } - [[nodiscard]] auto peek() const -> StringView { + IA_NODISCARD auto peek() const -> StringView { if (!remaining()) return ""; return *m_current_arg; @@ -48,7 +48,7 @@ public: return *m_current_arg++; } - auto consume(const StringView &expected) -> bool { + auto consume(Ref expected) -> bool { if (peek() == expected) { next(); return true; @@ -57,7 +57,7 @@ public: } private: - const Span m_arg_list; - Span::const_iterator m_current_arg; + Const>> m_arg_list; + Mut>::const_iterator> m_current_arg; }; } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/inc/IACore/DataOps.hpp b/Src/IACore/inc/IACore/DataOps.hpp index f1dbc98..135540f 100644 --- a/Src/IACore/inc/IACore/DataOps.hpp +++ b/Src/IACore/inc/IACore/DataOps.hpp @@ -23,23 +23,25 @@ public: enum class CompressionType { None, Gzip, Zlib }; public: - static auto hash_fnv1a(const String &string) -> u32; - static auto hash_fnv1a(Span data) -> u32; + static auto hash_fnv1a(Ref string) -> u32; + static auto hash_fnv1a(Ref>> data) -> u32; - static auto hash_xxhash(const String &string, u32 seed = 0) -> u32; - static auto hash_xxhash(Span data, u32 seed = 0) -> u32; + static auto hash_xxhash(Ref string, Const seed = 0) -> u32; + static auto hash_xxhash(Ref>> data, Const seed = 0) + -> u32; - static auto crc32(Span data) -> u32; + static auto crc32(Ref>> data) -> u32; - static auto detect_compression(Span data) -> CompressionType; + static auto detect_compression(Const>> data) + -> CompressionType; - static auto gzip_inflate(Span data) -> Result>; - static auto gzip_deflate(Span data) -> Result>; + static auto gzip_inflate(Ref>> data) -> Result>; + static auto gzip_deflate(Ref>> data) -> Result>; - static auto zlib_inflate(Span data) -> Result>; - static auto zlib_deflate(Span data) -> Result>; + static auto zlib_inflate(Ref>> data) -> Result>; + static auto zlib_deflate(Ref>> data) -> Result>; - static auto zstd_inflate(Span data) -> Result>; - static auto zstd_deflate(Span data) -> Result>; + static auto zstd_inflate(Ref>> data) -> Result>; + static auto zstd_deflate(Ref>> data) -> Result>; }; } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/inc/IACore/DynamicLib.hpp b/Src/IACore/inc/IACore/DynamicLib.hpp index 4754a82..12d4756 100644 --- a/Src/IACore/inc/IACore/DynamicLib.hpp +++ b/Src/IACore/inc/IACore/DynamicLib.hpp @@ -21,18 +21,14 @@ #include #endif -// Ensure 'ia' namespace alias exists for IA_TRY macro usage -namespace ia = IACore; - namespace IACore { class DynamicLib { public: - // Factory method to load a dynamic library - [[nodiscard]] static auto load(const String &search_path, const String &name) + IA_NODISCARD static auto load(Ref search_path, Ref name) -> Result { namespace fs = std::filesystem; - auto full_path = fs::path(search_path) / name; + Mut full_path = fs::path(search_path) / name; if (!full_path.has_extension()) { #if IA_PLATFORM_WINDOWS @@ -44,19 +40,18 @@ public: #endif } - DynamicLib lib; + Mut lib; #if IA_PLATFORM_WINDOWS - const HMODULE h = LoadLibraryA(full_path.string().c_str()); + Const h = LoadLibraryA(full_path.string().c_str()); if (!h) { return fail(get_windows_error()); } lib.m_handle = static_cast(h); #else - // RTLD_LAZY: Resolve symbols only as code executes (Standard for plugins) - void *h = dlopen(full_path.c_str(), RTLD_LAZY | RTLD_LOCAL); + Mut h = dlopen(full_path.c_str(), RTLD_LAZY | RTLD_LOCAL); if (!h) { - const char *err = dlerror(); + Const err = dlerror(); return fail(err ? err : "Unknown dlopen error"); } lib.m_handle = h; @@ -67,11 +62,11 @@ public: DynamicLib() = default; - DynamicLib(DynamicLib &&other) noexcept : m_handle(other.m_handle) { + DynamicLib(ForwardRef other) noexcept : m_handle(other.m_handle) { other.m_handle = nullptr; } - auto operator=(DynamicLib &&other) noexcept -> DynamicLib & { + auto operator=(ForwardRef other) noexcept -> MutRef { if (this != &other) { unload(); m_handle = other.m_handle; @@ -80,17 +75,17 @@ public: return *this; } - DynamicLib(const DynamicLib &) = delete; - auto operator=(const DynamicLib &) -> DynamicLib & = delete; + DynamicLib(Ref) = delete; + auto operator=(Ref) -> MutRef = delete; ~DynamicLib() { unload(); } - [[nodiscard]] auto get_symbol(const String &name) const -> Result { + IA_NODISCARD auto get_symbol(Ref name) const -> Result { if (!m_handle) { return fail("Library not loaded"); } - void *sym = nullptr; + Mut sym = nullptr; #if IA_PLATFORM_WINDOWS sym = static_cast( @@ -99,10 +94,9 @@ public: return fail(get_windows_error()); } #else - // Clear any previous error - dlerror(); + dlerror(); // Clear prev errors sym = dlsym(m_handle, name.c_str()); - if (const char *err = dlerror()) { + if (Const err = dlerror()) { return fail(err); } #endif @@ -111,9 +105,9 @@ public: } template - [[nodiscard]] auto get_function(const String &name) const -> Result { - void *sym = nullptr; - IA_TRY(sym, get_symbol(name)); + IA_NODISCARD auto get_function(Ref name) const -> Result { + Mut sym = nullptr; + sym = OX_TRY(get_symbol(name)); return reinterpret_cast(sym); } @@ -128,26 +122,26 @@ public: } } - [[nodiscard]] auto is_loaded() const -> bool { return m_handle != nullptr; } + IA_NODISCARD auto is_loaded() const -> bool { return m_handle != nullptr; } private: - void *m_handle = nullptr; + Mut m_handle = nullptr; #if IA_PLATFORM_WINDOWS static auto get_windows_error() -> String { - const DWORD error_id = ::GetLastError(); + Const error_id = ::GetLastError(); if (error_id == 0) { return String(); } - LPSTR message_buffer = nullptr; - const usize size = FormatMessageA( + Mut message_buffer = nullptr; + Const size = FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&message_buffer), 0, nullptr); - String message(message_buffer, size); + Const message(message_buffer, size); LocalFree(message_buffer); return "Win32 Error: " + message; } diff --git a/Src/IACore/inc/IACore/Environment.hpp b/Src/IACore/inc/IACore/Environment.hpp index 0fffbf5..8b44ea9 100644 --- a/Src/IACore/inc/IACore/Environment.hpp +++ b/Src/IACore/inc/IACore/Environment.hpp @@ -19,32 +19,33 @@ #include namespace IACore { + class Environment { public: - static auto find(const String &name) -> Option { + static auto find(Ref name) -> Option { #if IA_PLATFORM_WINDOWS - const DWORD buffer_size = GetEnvironmentVariableA(name.c_str(), nullptr, 0); + Const buffer_size = + static_cast(GetEnvironmentVariableA(name.c_str(), nullptr, 0)); if (buffer_size == 0) { return std::nullopt; } - String result; + Mut result; result.resize(buffer_size); - const DWORD actual_size = - GetEnvironmentVariableA(name.c_str(), result.data(), buffer_size); + Const actual_size = static_cast( + GetEnvironmentVariableA(name.c_str(), result.data(), buffer_size)); if (actual_size == 0 || actual_size > buffer_size) { return std::nullopt; } - // Resize down to exclude the null terminator and any slack result.resize(actual_size); return result; #else - const char *val = std::getenv(name.c_str()); + Const val = std::getenv(name.c_str()); if (val == nullptr) { return std::nullopt; } @@ -52,12 +53,11 @@ public: #endif } - static auto get(const String &name, const String &default_value = "") - -> String { + static auto get(Ref name, Ref default_value = "") -> String { return find(name).value_or(default_value); } - static auto set(const String &name, const String &value) -> Result { + static auto set(Ref name, Ref value) -> Result { if (name.empty()) { return fail("Environment variable name cannot be empty"); } @@ -74,7 +74,7 @@ public: return {}; } - static auto unset(const String &name) -> Result { + static auto unset(Ref name) -> Result { if (name.empty()) { return fail("Environment variable name cannot be empty"); } @@ -91,8 +91,9 @@ public: return {}; } - static auto exists(const String &name) -> bool { + static auto exists(Ref name) -> bool { return find(name).has_value(); } }; + } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/inc/IACore/FileOps.hpp b/Src/IACore/inc/IACore/FileOps.hpp index 814a329..b6eec4e 100644 --- a/Src/IACore/inc/IACore/FileOps.hpp +++ b/Src/IACore/inc/IACore/FileOps.hpp @@ -22,10 +22,11 @@ #if IA_PLATFORM_WINDOWS using NativeFileHandle = HANDLE; -static constexpr NativeFileHandle INVALID_FILE_HANDLE = INVALID_HANDLE_VALUE; +static constexpr ox::Const INVALID_FILE_HANDLE = + INVALID_HANDLE_VALUE; #else using NativeFileHandle = int; -static constexpr NativeFileHandle INVALID_FILE_HANDLE = -1; +static constexpr ox::Const INVALID_FILE_HANDLE = -1; #endif namespace IACore { @@ -48,43 +49,46 @@ public: TruncateExisting // Opens existing and clears it }; - static auto native_open_file(const Path &path, FileAccess access, - FileMode mode, u32 permissions = 0644) + static auto native_open_file(Ref path, Const access, + Const mode, + Const permissions = 0644) -> Result; - static auto native_close_file(NativeFileHandle handle) -> void; + static auto native_close_file(Const handle) -> void; public: - static auto normalize_executable_path(const Path &path) -> Path; + static auto normalize_executable_path(Ref path) -> Path; public: - static auto unmap_file(const u8 *mapped_ptr) -> void; + static auto unmap_file(Const mapped_ptr) -> void; - static auto map_file(const Path &path, usize &size) -> Result; + static auto map_file(Ref path, MutRef size) + -> Result; // @param `is_owner` true to allocate/truncate. false to just open. - static auto map_shared_memory(const String &name, usize size, bool is_owner) - -> Result; + static auto map_shared_memory(Ref name, Const size, + Const is_owner) -> Result; - static auto unlink_shared_memory(const String &name) -> void; + static auto unlink_shared_memory(Ref name) -> void; - static auto stream_from_file(const Path &path) -> Result; + static auto stream_from_file(Ref path) -> Result; - static auto stream_to_file(const Path &path, bool overwrite = false) + static auto stream_to_file(Ref path, Const overwrite = false) -> Result; - static auto read_text_file(const Path &path) -> Result; + static auto read_text_file(Ref path) -> Result; - static auto read_binary_file(const Path &path) -> Result>; + static auto read_binary_file(Ref path) -> Result>; - static auto write_text_file(const Path &path, const String &contents, - bool overwrite = false) -> Result; + static auto write_text_file(Ref path, Ref contents, + Const overwrite = false) -> Result; - static auto write_binary_file(const Path &path, Span contents, - bool overwrite = false) -> Result; + static auto write_binary_file(Ref path, Const> contents, + Const overwrite = false) -> Result; private: - static HashMap> s_mapped_files; + static Mut>> + s_mapped_files; }; class FileOps::MemoryMappedRegion { @@ -92,13 +96,15 @@ public: MemoryMappedRegion() = default; ~MemoryMappedRegion(); - MemoryMappedRegion(const MemoryMappedRegion &) = delete; - auto operator=(const MemoryMappedRegion &) -> MemoryMappedRegion & = delete; + MemoryMappedRegion(Ref) = delete; + auto operator=(Ref) -> MemoryMappedRegion & = delete; - MemoryMappedRegion(MemoryMappedRegion &&other) noexcept; - auto operator=(MemoryMappedRegion &&other) noexcept -> MemoryMappedRegion &; + MemoryMappedRegion(ForwardRef other) noexcept; + auto operator=(ForwardRef other) noexcept + -> MemoryMappedRegion &; - auto map(NativeFileHandle handle, u64 offset, usize size) -> Result; + auto map(Const handle, Const offset, Const size) + -> Result; auto unmap() -> void; auto flush() -> void; @@ -110,11 +116,11 @@ public: [[nodiscard]] auto is_valid() const -> bool { return m_ptr != nullptr; } private: - u8 *m_ptr = nullptr; - usize m_size = 0; + Mut m_ptr = nullptr; + Mut m_size = 0; #if IA_PLATFORM_WINDOWS - HANDLE m_map_handle = NULL; + Mut m_map_handle = NULL; #endif }; diff --git a/Src/IACore/inc/IACore/Http/Client.hpp b/Src/IACore/inc/IACore/Http/Client.hpp index dbf9e10..c48fd6f 100644 --- a/Src/IACore/inc/IACore/Http/Client.hpp +++ b/Src/IACore/inc/IACore/Http/Client.hpp @@ -21,56 +21,55 @@ namespace IACore { class HttpClient : public HttpCommon { public: - static auto create(const String &host) -> Result>; + static auto create(Ref host) -> Result>; ~HttpClient(); - HttpClient(HttpClient &&) = default; - HttpClient(const HttpClient &) = delete; - auto operator=(HttpClient &&) -> HttpClient & = default; - auto operator=(const HttpClient &) -> HttpClient & = delete; + HttpClient(ForwardRef) = default; + HttpClient(Ref) = delete; + auto operator=(ForwardRef) -> MutRef = default; + auto operator=(Ref) -> MutRef = delete; public: - auto raw_get(const String &path, Span headers, - const char *default_content_type = + auto raw_get(Ref path, Span> headers, + Const *default_content_type = "application/x-www-form-urlencoded") -> Result; - auto raw_post( - const String &path, Span headers, const String &body, - const char *default_content_type = "application/x-www-form-urlencoded") - -> Result; + auto raw_post(Ref path, Span> headers, Ref body, + Const *default_content_type = + "application/x-www-form-urlencoded") -> Result; template - auto json_get(const String &path, Span headers) + auto json_get(Ref path, Span> headers) -> Result; template - auto json_post(const String &path, Span headers, - const PayloadType &body) -> Result; + auto json_post(Ref path, Span> headers, + Ref body) -> Result; // Certificate verification is enabled by default - void enable_certificate_verification(); - void disable_certificate_verification(); + auto enable_certificate_verification() -> void; + auto disable_certificate_verification() -> void; public: auto last_response_code() -> EResponseCode { return m_last_response_code; } private: - httplib::Client m_client; - EResponseCode m_last_response_code; + Mut m_client; + Mut m_last_response_code; private: - auto preprocess_response(const String &response) -> String; + auto preprocess_response(Ref response) -> String; protected: - explicit HttpClient(httplib::Client &&client); + explicit HttpClient(ForwardRef client); }; template -auto HttpClient::json_get(const String &path, Span headers) +auto HttpClient::json_get(Ref path, Span> headers) -> Result { - String raw_response; - IA_TRY(raw_response, raw_get(path, headers, "application/json")); + Const raw_response = + OX_TRY(raw_get(path, headers, "application/json")); if (last_response_code() != EResponseCode::OK) { return fail("Server responded with code {}", @@ -80,14 +79,12 @@ auto HttpClient::json_get(const String &path, Span headers) } template -auto HttpClient::json_post(const String &path, Span headers, - const PayloadType &body) -> Result { - String encoded_body; - IA_TRY(encoded_body, Json::encode_struct(body)); +auto HttpClient::json_post(Ref path, Span> headers, + Ref body) -> Result { + Const encoded_body = OX_TRY(Json::encode_struct(body)); - String raw_response; - IA_TRY(raw_response, - raw_post(path, headers, encoded_body, "application/json")); + Const raw_response = + OX_TRY(raw_post(path, headers, encoded_body, "application/json")); if (last_response_code() != EResponseCode::OK) { return fail("Server responded with code {}", diff --git a/Src/IACore/inc/IACore/Http/Common.hpp b/Src/IACore/inc/IACore/Http/Common.hpp index acfd168..96216e9 100644 --- a/Src/IACore/inc/IACore/Http/Common.hpp +++ b/Src/IACore/inc/IACore/Http/Common.hpp @@ -127,29 +127,29 @@ public: using Header = Pair; - static auto url_encode(const String &value) -> String; - static auto url_decode(const String &value) -> String; + static auto url_encode(Ref value) -> String; + static auto url_decode(Ref value) -> String; - static auto header_type_to_string(EHeaderType type) -> String; + static auto header_type_to_string(Const type) -> String; - static inline auto create_header(EHeaderType key, const String &value) + static inline auto create_header(Const key, Ref value) -> Header; - static inline auto create_header(const String &key, const String &value) + static inline auto create_header(Ref key, Ref value) -> Header; - static auto is_success_response_code(EResponseCode code) -> bool; + static auto is_success_response_code(Const code) -> bool; protected: HttpCommon() = default; }; -auto HttpCommon::create_header(EHeaderType key, const String &value) +auto HttpCommon::create_header(Const key, Ref value) -> HttpCommon::Header { - return std::make_pair(header_type_to_string(key), value); + return Header{header_type_to_string(key), value}; } -auto HttpCommon::create_header(const String &key, const String &value) +auto HttpCommon::create_header(Ref key, Ref value) -> HttpCommon::Header { - return std::make_pair(key, value); + return Header{key, value}; } } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/inc/IACore/Http/Server.hpp b/Src/IACore/inc/IACore/Http/Server.hpp index 12cfd2a..80ed89f 100644 --- a/Src/IACore/inc/IACore/Http/Server.hpp +++ b/Src/IACore/inc/IACore/Http/Server.hpp @@ -23,34 +23,34 @@ namespace IACore { class HttpServer : public HttpCommon { public: struct Request { - String path; - String method; - String body; - HashMap headers; - HashMap params; // Query parameters - HashMap path_params; // Path parameters (e.g. /users/:id) + Mut path; + Mut method; + Mut body; + Mut> headers; + Mut> params; // Query params + Mut> path_params; // Path params (like /object/:id) - [[nodiscard]] auto get_header(const String &key) const -> String; - [[nodiscard]] auto get_param(const String &key) const -> String; - [[nodiscard]] auto get_path_param(const String &key) const -> String; + [[nodiscard]] auto get_header(Ref key) const -> String; + [[nodiscard]] auto get_param(Ref key) const -> String; + [[nodiscard]] auto get_path_param(Ref key) const -> String; - [[nodiscard]] auto has_header(const String &key) const -> bool; - [[nodiscard]] auto has_param(const String &key) const -> bool; - [[nodiscard]] auto has_path_param(const String &key) const -> bool; + [[nodiscard]] auto has_header(Ref key) const -> bool; + [[nodiscard]] auto has_param(Ref key) const -> bool; + [[nodiscard]] auto has_path_param(Ref key) const -> bool; }; struct Response { - EResponseCode code = EResponseCode::OK; - String body; - HashMap headers; - String content_type = "text/plain"; + Mut code = EResponseCode::OK; + Mut body; + Mut> headers; + Mut content_type = "text/plain"; - void set_content(const String &content, const String &type); - void set_status(EResponseCode status_code); - void add_header(const String &key, const String &value); + void set_content(Ref content, Ref type); + void set_status(Const status_code); + void add_header(Ref key, Ref value); }; - using Handler = std::function; + using Handler = std::function, MutRef)>; public: static auto create() -> Result>; @@ -62,52 +62,49 @@ public: auto operator=(HttpServer &&) -> HttpServer & = delete; auto operator=(const HttpServer &) -> HttpServer & = delete; - auto listen(const String &host, u32 port) -> Result; + auto listen(Ref host, Const port) -> Result; void stop(); auto is_running() const -> bool; - void get(const String &pattern, Handler handler); - void post(const String &pattern, Handler handler); - void put(const String &pattern, Handler handler); - void del(const String &pattern, Handler handler); - void options(const String &pattern, Handler handler); + void get(Ref pattern, Const handler); + void post(Ref pattern, Const handler); + void put(Ref pattern, Const handler); + void del(Ref pattern, Const handler); + void options(Ref pattern, Const handler); template - void json_get(const String &pattern, - std::function(const Request &)> handler); + void + json_get(Ref pattern, + Const(Ref)>> handler); template - void - json_post(const String &pattern, - std::function(const PayloadType &)> handler); + void json_post( + Ref pattern, + Const(Ref)>> handler); protected: HttpServer(); private: - httplib::Server m_server; + Mut m_server; - void register_handler(const String &method, const String &pattern, - Handler handler); + void register_handler(Ref method, Ref pattern, + Const handler); }; -// ============================================================================= -// Template Implementations -// ============================================================================= - template void HttpServer::json_get( - const String &pattern, - std::function(const Request &)> handler) { - get(pattern, [handler](const Request &req, Response &res) { - auto result = handler(req); + Ref pattern, + Const(Ref)>> handler) { + get(pattern, [handler](Ref req, MutRef res) { + Const> result = handler(req); if (!result) { res.set_status(EResponseCode::INTERNAL_SERVER_ERROR); res.set_content(result.error(), "text/plain"); return; } - auto json_res = Json::encode_struct(*result); + Const> json_res = Json::encode_struct(*result); if (!json_res) { res.set_status(EResponseCode::INTERNAL_SERVER_ERROR); res.set_content("Failed to encode JSON response", "text/plain"); @@ -121,24 +118,25 @@ void HttpServer::json_get( template void HttpServer::json_post( - const String &pattern, - std::function(const PayloadType &)> handler) { - post(pattern, [handler](const Request &req, Response &res) { - auto payload = Json::parse_to_struct(req.body); + Ref pattern, + Const(Ref)>> handler) { + post(pattern, [handler](Ref req, MutRef res) { + Const> payload = + Json::parse_to_struct(req.body); if (!payload) { res.set_status(EResponseCode::BAD_REQUEST); res.set_content("Invalid JSON Payload", "text/plain"); return; } - auto result = handler(*payload); + Const> result = handler(*payload); if (!result) { res.set_status(EResponseCode::INTERNAL_SERVER_ERROR); res.set_content(result.error(), "text/plain"); return; } - auto json_res = Json::encode_struct(*result); + Const> json_res = Json::encode_struct(*result); if (!json_res) { res.set_status(EResponseCode::INTERNAL_SERVER_ERROR); res.set_content("Failed to encode JSON response", "text/plain"); diff --git a/Src/IACore/inc/IACore/IACore.hpp b/Src/IACore/inc/IACore/IACore.hpp index 90839bc..5a1a44f 100644 --- a/Src/IACore/inc/IACore/IACore.hpp +++ b/Src/IACore/inc/IACore/IACore.hpp @@ -15,55 +15,52 @@ #pragma once -#include #include +#include -#define IACORE_MAIN() \ - auto _app_entry(const IACore::Vec &args) -> IACore::Result; \ - auto main(int argc, char *argv[]) -> int \ - { \ - IACore::i32 exit_code = 0; \ - IACore::initialize(); \ - IACore::Vec args; \ - args.reserve(static_cast(argc)); \ - for (int i = 0; i < argc; ++i) \ - { \ - args.push_back(argv[i]); \ - } \ - const auto result = _app_entry(args); \ - if (!result) \ - { \ - IACore::Logger::error("Application exited with an error: '{}'.", result.error()); \ - exit_code = -20; \ - } \ - else \ - { \ - exit_code = *result; \ - if (exit_code == 0) \ - { \ - IACore::Logger::info("Application exited successfully."); \ - } \ - else \ - { \ - IACore::Logger::error("Application exited with error code: {}.", exit_code); \ - } \ - } \ - IACore::terminate(); \ - return exit_code; \ - } \ - auto _app_entry(const IACore::Vec &args) -> IACore::Result +#define IACORE_MAIN() \ + auto _app_entry(IACore::Ref> args) \ + -> IACore::Result; \ + auto main(Const argc, Mut argv[]) -> int { \ + IACore::Mut exit_code = 0; \ + IACore::initialize(); \ + IACore::Mut> args; \ + args.reserve(static_cast(argc)); \ + for (IACore::Mut i = 0; i < argc; ++i) { \ + args.push_back(argv[i]); \ + } \ + IACore::Const> result = _app_entry(args); \ + if (!result) { \ + IACore::Logger::error("Application exited with an error: '{}'.", \ + result.error()); \ + exit_code = -20; \ + } else { \ + exit_code = *result; \ + if (exit_code == 0) { \ + IACore::Logger::info("Application exited successfully."); \ + } else { \ + IACore::Logger::error("Application exited with error code: {}.", \ + exit_code); \ + } \ + } \ + IACore::terminate(); \ + return exit_code; \ + } \ + auto _app_entry(IACore::Ref> args) \ + -> IACore::Result -namespace IACore -{ - // Must be called from main thread - // Safe to call multiple times but, every initialize call is paired with a corresponding terminate call - auto initialize() -> void; +namespace IACore { +// Must be called from main thread +// Safe to call multiple times but, given every initialize call is paired with a +// corresponding terminate call +auto initialize() -> void; - // Must be called from same thread as initialize - // Safe to call multiple times but, every initialize call is paired with a corresponding terminate call - auto terminate() -> void; +// Must be called from same thread as initialize +// Safe to call multiple times but, given every initialize call is paired with a +// corresponding terminate call +auto terminate() -> void; - auto is_initialized() -> bool; +auto is_initialized() -> bool; - auto is_main_thread() -> bool; +auto is_main_thread() -> bool; } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/inc/IACore/IATest.hpp b/Src/IACore/inc/IACore/IATest.hpp index e519c0f..a6173ff 100644 --- a/Src/IACore/inc/IACore/IATest.hpp +++ b/Src/IACore/inc/IACore/IATest.hpp @@ -55,7 +55,7 @@ #define IAT_BEGIN_TEST_LIST() \ public: \ - void declare_tests() override { + auto declare_tests() -> void override { #define IAT_ADD_TEST(name) IAT_UNIT(name) #define IAT_END_TEST_LIST() \ } \ @@ -66,7 +66,7 @@ namespace IACore::Test { // ------------------------------------------------------------------------- // String Conversion Helpers // ------------------------------------------------------------------------- -template auto to_string(const T &value) -> String { +template auto to_string(Ref value) -> String { if constexpr (std::is_arithmetic_v) { return std::to_string(value); } else if constexpr (std::is_same_v || @@ -91,21 +91,22 @@ template auto to_string(T *value) -> String { using TestFunctor = std::function; struct TestUnit { - String name; - TestFunctor functor; + Mut name; + Mut functor; }; class Block { public: virtual ~Block() = default; [[nodiscard]] virtual auto get_name() const -> const char * = 0; - virtual void declare_tests() = 0; + virtual auto declare_tests() -> void = 0; - auto units() -> Vec & { return m_units; } + auto units() -> MutRef> { return m_units; } protected: template - auto _test_eq(const T1 &lhs, const T2 &rhs, const char *description) -> bool { + auto _test_eq(Ref lhs, Ref rhs, Const description) + -> bool { if (lhs != rhs) { print_fail(description, to_string(lhs), to_string(rhs)); return false; @@ -114,7 +115,7 @@ protected: } template - auto _test_neq(const T1 &lhs, const T2 &rhs, const char *description) + auto _test_neq(Ref lhs, Ref rhs, Const description) -> bool { if (lhs == rhs) { print_fail(description, to_string(lhs), "NOT " + to_string(rhs)); @@ -124,10 +125,11 @@ protected: } template - auto _test_approx(T lhs, T rhs, const char *description) -> bool { + auto _test_approx(Const lhs, Const rhs, Const description) + -> bool { static_assert(std::is_floating_point_v, "Approx only works for floats/doubles"); - T diff = std::abs(lhs - rhs); + Const diff = std::abs(lhs - rhs); if (diff > static_cast(0.0001)) { print_fail(description, to_string(lhs), to_string(rhs)); return false; @@ -135,7 +137,7 @@ protected: return true; } - auto _test(bool value, const char *description) -> bool { + auto _test(Const value, Const description) -> bool { if (!value) { std::cout << console::BLUE << " " << description << "... " << console::RED << "FAILED" << console::RESET << "\n"; @@ -144,7 +146,7 @@ protected: return true; } - auto _test_not(bool value, const char *description) -> bool { + auto _test_not(Const value, Const description) -> bool { if (value) { std::cout << console::BLUE << " " << description << "... " << console::RED << "FAILED" << console::RESET << "\n"; @@ -153,12 +155,13 @@ protected: return true; } - void _test_unit(TestFunctor functor, const char *name) { + auto _test_unit(Mut functor, Const name) -> void { m_units.push_back({name, std::move(functor)}); } private: - void print_fail(const char *desc, const String &v1, const String &v2) { + auto print_fail(Const desc, Ref v1, Ref v2) + -> void { std::cout << console::BLUE << " " << desc << "... " << console::RED << "FAILED" << console::RESET << "\n"; std::cout << console::RED << " Expected: " << v2 << console::RESET @@ -167,7 +170,7 @@ private: << "\n"; } - Vec m_units; + Mut> m_units; }; template @@ -184,37 +187,35 @@ public: template requires ValidBlockClass - void test_block(); + auto test_block() -> void; private: - void summarize(); + auto summarize() -> void; - usize m_test_count{0}; - usize m_fail_count{0}; - usize m_block_count{0}; + Mut m_test_count{0}; + Mut m_fail_count{0}; + Mut m_block_count{0}; }; template template requires ValidBlockClass -void Runner::test_block() { +auto Runner::test_block() -> void { m_block_count++; - BlockClass b; + Mut b; b.declare_tests(); std::cout << console::MAGENTA << "Testing [" << b.get_name() << "]..." << console::RESET << "\n"; - for (auto &v : b.units()) { + for (MutRef v : b.units()) { m_test_count++; if constexpr (IsVerbose) { std::cout << console::YELLOW << " Testing " << v.name << "...\n" << console::RESET; } - // Exceptions are DISABLED. We assume tests do not crash. - // If a test crashes (segfault), the OS handles it. - bool result = v.functor(); + Const result = v.functor(); if (!result) { m_fail_count++; @@ -228,7 +229,7 @@ void Runner::test_block() { } template -void Runner::summarize() { +auto Runner::summarize() -> void { std::cout << console::GREEN << "\n-----------------------------------\n\t " "SUMMARY\n-----------------------------------\n"; @@ -236,8 +237,9 @@ void Runner::summarize() { if (m_fail_count == 0) { std::cout << "\n\tALL TESTS PASSED!\n\n"; } else { - f64 success_rate = (100.0 * static_cast(m_test_count - m_fail_count) / - static_cast(m_test_count)); + Const success_rate = + (100.0 * static_cast(m_test_count - m_fail_count) / + static_cast(m_test_count)); std::cout << console::RED << m_fail_count << " OF " << m_test_count << " TESTS FAILED\n" << console::YELLOW @@ -257,21 +259,21 @@ using DefaultRunner = Runner; // ------------------------------------------------------------------------- class TestRegistry { public: - using TestEntry = std::function; + using TestEntry = std::function)>; - static auto get_entries() -> Vec & { - static Vec entries; + static auto get_entries() -> MutRef> { + static Mut> entries; return entries; } static auto run_all() -> i32 { - DefaultRunner r; - auto &entries = get_entries(); + Mut r; + MutRef> entries = get_entries(); std::cout << console::CYAN << "[IATest] Discovered " << entries.size() << " Test Blocks\n\n" << console::RESET; - for (auto &entry : entries) { + for (MutRef entry : entries) { entry(r); } @@ -282,7 +284,7 @@ public: template struct AutoRegister { AutoRegister() { TestRegistry::get_entries().push_back( - [](DefaultRunner &r) { r.test_block(); }); + [](MutRef r) { r.test_block(); }); } }; } // namespace IACore::Test diff --git a/Src/IACore/inc/IACore/IPC.hpp b/Src/IACore/inc/IACore/IPC.hpp index 772a75d..a0cc4b8 100644 --- a/Src/IACore/inc/IACore/IPC.hpp +++ b/Src/IACore/inc/IACore/IPC.hpp @@ -24,44 +24,46 @@ using IpcPacketHeader = RingBufferView::PacketHeader; struct alignas(64) IpcSharedMemoryLayout { // ========================================================= - // SECTION 1: METADATA & HANDSHAKE + // METADATA & HANDSHAKE // ========================================================= struct Header { - u32 magic; // 0x49414950 ("IAIP") - u32 version; // 1 - u64 total_size; // Total size of SHM block - } meta; + Mut magic; // 0x49414950 ("IAIP") + Mut version; // 1 + Mut total_size; // Total size of SHM block + }; + + Mut
meta; // Pad to ensure MONI starts on a fresh cache line (64 bytes) - u8 _pad0[64 - sizeof(Header)]; + Const> _pad0; // ========================================================= - // SECTION 2: RING BUFFER CONTROL BLOCKS + // RING BUFFER CONTROL BLOCKS // ========================================================= // RingBufferView ControlBlock is already 64-byte aligned internally. - RingBufferView::ControlBlock moni_control; - RingBufferView::ControlBlock mino_control; + Mut moni_control; + Mut mino_control; // ========================================================= - // SECTION 3: DATA BUFFER OFFSETS + // DATA BUFFER OFFSETS // ========================================================= - u64 moni_data_offset; - u64 moni_data_size; + Mut moni_data_offset; + Mut moni_data_size; - u64 mino_data_offset; - u64 mino_data_size; + Mut mino_data_offset; + Mut mino_data_size; // Pad to ensure the actual Data Buffer starts on a fresh cache line - u8 _pad1[64 - (sizeof(u64) * 4)]; + Const> _pad1; static constexpr auto get_header_size() -> usize { return sizeof(IpcSharedMemoryLayout); } }; -// Static assert to ensure manual padding logic is correct +// Check padding logic is gucci static_assert(sizeof(IpcSharedMemoryLayout) % 64 == 0, "IPC Layout is not cache-line aligned!"); @@ -71,81 +73,86 @@ public: // When Manager spawns a node, `connection_string` is passed // as the first command line argument - auto connect(const char *connection_string) -> Result; + auto connect(Const connection_string) -> Result; - void update(); + auto update() -> void; - void send_signal(u8 signal); - auto send_packet(u16 packet_id, Span payload) -> Result; + auto send_signal(Const signal) -> void; + auto send_packet(Const packet_id, Const>> payload) + -> Result; protected: - virtual void on_signal(u8 signal) = 0; - virtual void on_packet(u16 packet_id, Span payload) = 0; + virtual auto on_signal(Const signal) -> void = 0; + virtual auto on_packet(Const packet_id, Const>> payload) + -> void = 0; private: - String m_shm_name; - u8 *m_shared_memory{}; - Vec m_receive_buffer; - SocketHandle m_socket{INVALID_SOCKET}; + Mut m_shm_name; + Mut m_shared_memory{}; + Mut> m_receive_buffer; + Mut m_socket{INVALID_SOCKET}; - RingBufferView m_moni; // Manager Out, Node In - RingBufferView m_mino; // Manager In, Node Out + Mut m_moni; // Manager Out, Node In + Mut m_mino; // Manager In, Node Out }; class IpcManager { struct NodeSession { - std::chrono::system_clock::time_point creation_time{}; - Box node_process; + Mut creation_time{}; + Mut> node_process; - std::mutex send_mutex; + Mut send_mutex; - String shared_mem_name; - u8 *mapped_ptr{}; + Mut shared_mem_name; + Mut mapped_ptr{}; - SocketHandle listener_socket{INVALID_SOCKET}; - SocketHandle data_socket{INVALID_SOCKET}; + Mut listener_socket{INVALID_SOCKET}; + Mut data_socket{INVALID_SOCKET}; - RingBufferView moni = + Mut moni = RingBufferView::default_instance(); // Manager Out, Node In - RingBufferView mino = + Mut mino = RingBufferView::default_instance(); // Manager In, Node Out - bool is_ready{false}; + Mut is_ready{false}; - void send_signal(u8 signal); - auto send_packet(u16 packet_id, Span payload) -> Result; + auto send_signal(Const signal) -> void; + auto send_packet(Const packet_id, Const>> payload) + -> Result; }; public: - static constexpr u32 DEFAULT_NODE_SHARED_MEMORY_SIZE = 4 * 1024 * 1024; // 4MB + static constexpr Const DEFAULT_NODE_SHARED_MEMORY_SIZE = 4 * 1024 * 1024; public: virtual ~IpcManager(); - void update(); + auto update() -> void; - auto spawn_node(const Path &executable_path, - u32 shared_memory_size = DEFAULT_NODE_SHARED_MEMORY_SIZE) + auto + spawn_node(Ref executable_path, + Const shared_memory_size = DEFAULT_NODE_SHARED_MEMORY_SIZE) -> Result; - auto wait_till_node_is_online(NativeProcessID node) -> bool; + auto wait_till_node_is_online(Const node) -> bool; - void shutdown_node(NativeProcessID node); + auto shutdown_node(Const node) -> void; - void send_signal(NativeProcessID node, u8 signal); - auto send_packet(NativeProcessID node, u16 packet_id, Span payload) - -> Result; + auto send_signal(Const node, Const signal) -> void; + auto send_packet(Const node, Const packet_id, + Const>> payload) -> Result; protected: - virtual void on_signal(NativeProcessID node, u8 signal) = 0; - virtual void on_packet(NativeProcessID node, u16 packet_id, - Span payload) = 0; + virtual auto on_signal(Const node, Const signal) + -> void = 0; + virtual auto on_packet(Const node, Const packet_id, + Const>> payload) -> void = 0; private: - Vec m_receive_buffer; - Vec> m_active_sessions; - Vec> m_pending_sessions; - HashMap m_active_session_map; + Mut> m_receive_buffer; + Mut>> m_active_sessions; + Mut>> m_pending_sessions; + Mut> m_active_session_map; protected: IpcManager(); diff --git a/Src/IACore/inc/IACore/JSON.hpp b/Src/IACore/inc/IACore/JSON.hpp index 2c2c600..16f12ce 100644 --- a/Src/IACore/inc/IACore/JSON.hpp +++ b/Src/IACore/inc/IACore/JSON.hpp @@ -24,67 +24,49 @@ namespace IACore { class JsonDocument { public: - // Move-only (Safety: Cannot copy the parser state cheaply) - JsonDocument(JsonDocument &&) noexcept = default; - JsonDocument &operator=(JsonDocument &&) noexcept = default; - JsonDocument(const JsonDocument &) = delete; - JsonDocument &operator=(const JsonDocument &) = delete; + JsonDocument(ForwardRef) noexcept = default; + auto operator=(ForwardRef) noexcept + -> MutRef = default; + + JsonDocument(Ref) = delete; + auto operator=(Ref) -> MutRef = delete; - // Accessor: Get the root element (Object, Array, etc.) - // The returned 'element' is valid only as long as this JsonDocument is alive. [[nodiscard]] auto root() const noexcept -> simdjson::dom::element { return m_root; } private: - // Only created via JSON::parse_read_only factory friend class Json; - JsonDocument(Box p, simdjson::dom::element r) + JsonDocument(Mut> p, Mut r) : m_parser(std::move(p)), m_root(r) {} - // ORDER MATTERS: Parser (Owner) must be destroyed AFTER the Root (View). - // In C++, members are destroyed in reverse declaration order. - Box m_parser; - simdjson::dom::element m_root; + Mut> m_parser; + Mut m_root; }; class Json { private: - // Glaze options (Compile-time configuration) - static constexpr auto GLAZE_OPTS = glz::opts{.error_on_unknown_keys = false}; + static constexpr Const GLAZE_OPTS = + glz::opts{.error_on_unknown_keys = false}; public: - // ------------------------------------------------------------------------- - // 1. Standard Parsing (Nlohmann - mutable, owning, slower) - // ------------------------------------------------------------------------- - static auto parse(const String &json_str) -> Result; - static auto encode(const nlohmann::json &data) -> String; + static auto parse(Ref json_str) -> Result; + static auto encode(Ref data) -> String; - // ------------------------------------------------------------------------- - // 2. Read-Only Parsing (Simdjson - immutable, zero-copyish, extremely fast) - // ------------------------------------------------------------------------- - // Returns a safe JsonDocument wrapper instead of a raw pair - static auto parse_read_only(const String &json_str) -> Result; - - // ------------------------------------------------------------------------- - // 3. Struct Serialization (Glaze - reflection-based, very fast) - // ------------------------------------------------------------------------- - template - static auto parse_to_struct(const String &json_str) -> Result; + static auto parse_read_only(Ref json_str) -> Result; template - static auto encode_struct(const T &data) -> Result; + static auto parse_to_struct(Ref json_str) -> Result; + + template + static auto encode_struct(Ref data) -> Result; }; -// ============================================================================= -// Implementation -// ============================================================================= - -inline auto Json::parse(const String &json_str) -> Result { - // 3rd arg=false (no exception), 4th arg=true (ignore comments) - const auto res = nlohmann::json::parse(json_str, nullptr, false, true); +inline auto Json::parse(Ref json_str) -> Result { + Const res = + nlohmann::json::parse(json_str, nullptr, false, true); if (res.is_discarded()) { return fail("Failed to parse JSON (Invalid Syntax)"); @@ -92,35 +74,30 @@ inline auto Json::parse(const String &json_str) -> Result { return res; } -inline auto Json::parse_read_only(const String &json_str) +inline auto Json::parse_read_only(Ref json_str) -> Result { - // 1. Allocate parser on heap (reusing this via a pool would be even faster in - // future) - auto parser = make_box(); + Mut> parser = make_box(); - // 2. Use 'element' to support Arrays/Strings/Null roots, not just Objects - simdjson::dom::element root; + Mut root; - // 3. Parse - simdjson::error_code error = parser->parse(json_str).get(root); + Const error = parser->parse(json_str).get(root); if (error) { return fail("JSON Error: {}", simdjson::error_message(error)); } - // 4. Return Safe Wrapper (Owner + View) return JsonDocument(std::move(parser), root); } -inline auto Json::encode(const nlohmann::json &data) -> String { +inline auto Json::encode(Ref data) -> String { return data.dump(); } template -inline auto Json::parse_to_struct(const String &json_str) -> Result { - T result{}; +inline auto Json::parse_to_struct(Ref json_str) -> Result { + Mut result{}; - const auto err = glz::read(result, json_str); + Const err = glz::read(result, json_str); if (err) { return fail("JSON Struct Parse Error: {}", @@ -130,9 +107,9 @@ inline auto Json::parse_to_struct(const String &json_str) -> Result { } template -inline auto Json::encode_struct(const T &data) -> Result { - String result; - const auto err = glz::write_json(data, result); +inline auto Json::encode_struct(Ref data) -> Result { + Mut result; + Const err = glz::write_json(data, result); if (err) { return fail("JSON Struct Encode Error"); diff --git a/Src/IACore/inc/IACore/Logger.hpp b/Src/IACore/inc/IACore/Logger.hpp index 25aef47..e801e81 100644 --- a/Src/IACore/inc/IACore/Logger.hpp +++ b/Src/IACore/inc/IACore/Logger.hpp @@ -1,3 +1,18 @@ +// IACore-OSS; The Core Library for All IA Open Source Projects +// Copyright (C) 2026 IAS (ias@iasoft.dev) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #pragma once #include @@ -18,31 +33,37 @@ public: enum class LogLevel { Trace, Debug, Info, Warn, Error }; public: - static auto enable_logging_to_disk(const char *file_path) -> Result; - static auto set_log_level(LogLevel log_level) -> void; + static auto enable_logging_to_disk(Const file_path) + -> Result; + static auto set_log_level(Const log_level) -> void; template - static auto trace(std::format_string fmt, Args &&...args) -> void { + static auto trace(Const> fmt, + ForwardRef... args) -> void { log_trace(std::vformat(fmt.get(), std::make_format_args(args...))); } template - static auto debug(std::format_string fmt, Args &&...args) -> void { + static auto debug(Const> fmt, + ForwardRef... args) -> void { log_debug(std::vformat(fmt.get(), std::make_format_args(args...))); } template - static auto info(std::format_string fmt, Args &&...args) -> void { + static auto info(Const> fmt, + ForwardRef... args) -> void { log_info(std::vformat(fmt.get(), std::make_format_args(args...))); } template - static auto warn(std::format_string fmt, Args &&...args) -> void { + static auto warn(Const> fmt, + ForwardRef... args) -> void { log_warn(std::vformat(fmt.get(), std::make_format_args(args...))); } template - static auto error(std::format_string fmt, Args &&...args) -> void { + static auto error(Const> fmt, + ForwardRef... args) -> void { log_error(std::vformat(fmt.get(), std::make_format_args(args...))); } @@ -50,48 +71,48 @@ public: private: #if IA_DISABLE_LOGGING > 0 - static auto log_trace(String &&msg) -> void { UNUSED(msg); } + static auto log_trace(ForwardRef msg) -> void { IA_UNUSED(msg); } - static auto log_debug(String &&msg) -> void { UNUSED(msg); } + static auto log_debug(ForwardRef msg) -> void { IA_UNUSED(msg); } - static auto log_info(String &&msg) -> void { UNUSED(msg); } + static auto log_info(ForwardRef msg) -> void { IA_UNUSED(msg); } - static auto log_warn(String &&msg) -> void { UNUSED(msg); } + static auto log_warn(ForwardRef msg) -> void { IA_UNUSED(msg); } - static auto log_error(String &&msg) -> void { UNUSED(msg); } + static auto log_error(ForwardRef msg) -> void { IA_UNUSED(msg); } #else - static auto log_trace(String &&msg) -> void { + static auto log_trace(ForwardRef msg) -> void { if (m_log_level <= LogLevel::Trace) log_internal(console::RESET, "TRACE", std::move(msg)); } - static auto log_debug(String &&msg) -> void { + static auto log_debug(ForwardRef msg) -> void { if (m_log_level <= LogLevel::Debug) log_internal(console::CYAN, "DEBUG", std::move(msg)); } - static auto log_info(String &&msg) -> void { + static auto log_info(ForwardRef msg) -> void { if (m_log_level <= LogLevel::Info) log_internal(console::GREEN, "INFO", std::move(msg)); } - static auto log_warn(String &&msg) -> void { + static auto log_warn(ForwardRef msg) -> void { if (m_log_level <= LogLevel::Warn) log_internal(console::YELLOW, "WARN", std::move(msg)); } - static auto log_error(String &&msg) -> void { + static auto log_error(ForwardRef msg) -> void { if (m_log_level <= LogLevel::Error) log_internal(console::RED, "ERROR", std::move(msg)); } #endif - static auto log_internal(const char *prefix, const char *tag, String &&msg) - -> void; + static auto log_internal(Const prefix, Const tag, + ForwardRef msg) -> void; private: - static LogLevel m_log_level; - static std::ofstream m_log_file; + static Mut m_log_level; + static Mut m_log_file; static auto initialize() -> void; static auto terminate() -> void; diff --git a/Src/IACore/inc/IACore/PCH.hpp b/Src/IACore/inc/IACore/PCH.hpp index 9f11cb9..2af0af4 100644 --- a/Src/IACore/inc/IACore/PCH.hpp +++ b/Src/IACore/inc/IACore/PCH.hpp @@ -54,144 +54,53 @@ #include #endif -#include #include #include #include #include #include #include -#include #include #include #include #include -#include -#include -#include -#include -#include #include -#include #include -#include + +#include namespace IACore { -// ============================================================================= -// Primitive Types (Rust Style) -// ============================================================================= -using u8 = std::uint8_t; -using u16 = std::uint16_t; -using u32 = std::uint32_t; -using u64 = std::uint64_t; - -using i8 = std::int8_t; -using i16 = std::int16_t; -using i32 = std::int32_t; -using i64 = std::int64_t; - -using f32 = float; -using f64 = double; - -using usize = std::size_t; -using isize = std::ptrdiff_t; +using namespace Oxide; // ============================================================================= // Build Environment & Constants // ============================================================================= -namespace env { -#if defined(NDEBUG) -constexpr bool is_debug = false; -constexpr bool is_release = true; -#else -constexpr bool is_debug = true; -constexpr bool is_release = false; -#endif +namespace Env { + +using namespace Oxide::Env; #if IA_PLATFORM_WINDOWS -constexpr bool is_windows = true; -constexpr bool is_unix = false; +constexpr Const IS_WINDOWS = true; +constexpr Const IS_UNIX = false; #else -constexpr bool is_windows = false; -constexpr bool is_unix = true; +constexpr Const IS_WINDOWS = false; +constexpr Const IS_UNIX = true; #endif -constexpr usize max_path_len = 4096; -} // namespace env +constexpr Const MAX_PATH_LEN = 4096; + +} // namespace Env // ============================================================================= -// Memory & Ownership (Rust Semantics) +// Data Structures & Aliases // ============================================================================= -template using Box = std::unique_ptr; - -template using Arc = std::shared_ptr; - -template using Weak = std::weak_ptr; - -template -[[nodiscard]] inline auto make_box(Args &&...args) -> Box { - return std::make_unique(std::forward(args)...); -} - -template -[[nodiscard]] inline auto make_arc(Args &&...args) -> Arc { - return std::make_shared(std::forward(args)...); -} - -// ============================================================================= -// Error Handling (Result) -// ============================================================================= -template -using Result = tl::expected; - -template [[nodiscard]] inline auto fail(E &&error) { - return tl::make_unexpected(std::forward(error)); -} - -template -[[nodiscard]] inline auto fail(std::format_string fmt, - Args &&...args) { - return tl::make_unexpected(std::format(fmt, std::forward(args)...)); -} - -// ============================================================================= -// Common Data Structures -// ============================================================================= -template using Option = std::optional; -template using Vec = std::vector; -template using Span = std::span; -template using Pair = std::pair; template using HashMap = ankerl::unordered_dense::map; template using HashSet = ankerl::unordered_dense::set; -using String = std::string; using Path = std::filesystem::path; -using StringView = std::string_view; - -// ============================================================================= -// Utilities -// ============================================================================= - -[[noreturn]] inline void -panic(const std::string &msg, - const std::source_location loc = std::source_location::current()) { - std::cerr << "\n[IA_PANIC] " << msg << "\n At: " << loc.file_name() - << ":" << loc.line() << "\n"; - std::abort(); -} - -inline void -ensure(bool condition, const std::string &msg, - const std::source_location loc = std::source_location::current()) { - if (env::is_debug && !condition) { - std::cerr << "\n[assert] " << msg << "\n At: " << loc.file_name() - << ":" << loc.line() << "\n"; - std::abort(); - } -} // ============================================================================= // Versioning @@ -201,7 +110,7 @@ struct Version { u32 minor = 0; u32 patch = 0; - constexpr auto to_u64() const -> u64 { + [[nodiscard]] constexpr auto to_u64() const -> u64 { return (static_cast(major) << 40) | (static_cast(minor) << 16) | (static_cast(patch)); } @@ -211,37 +120,16 @@ struct Version { // Console Colors // ============================================================================= namespace console { -constexpr const char *RESET = "\033[0m"; -constexpr const char *RED = "\033[31m"; -constexpr const char *GREEN = "\033[32m"; -constexpr const char *YELLOW = "\033[33m"; -constexpr const char *BLUE = "\033[34m"; -constexpr const char *MAGENTA = "\033[35m"; -constexpr const char *CYAN = "\033[36m"; +constexpr Const RESET = "\033[0m"; +constexpr Const RED = "\033[31m"; +constexpr Const GREEN = "\033[32m"; +constexpr Const YELLOW = "\033[33m"; +constexpr Const BLUE = "\033[34m"; +constexpr Const MAGENTA = "\033[35m"; +constexpr Const CYAN = "\033[36m"; } // namespace console } // namespace IACore -// ============================================================================= -// Macros -// ============================================================================= - -#define IA_TRY_PURE(expr) \ - { \ - auto _res = expr; \ - if (!_res) { \ - return fail(std::move(_res.error())); \ - } \ - } - -#define IA_TRY(lhs, expr) \ - { \ - auto _res = expr; \ - if (!_res) { \ - return fail(std::move(_res.error())); \ - } \ - lhs = std::move(*_res); \ - } - #define IA_NODISCARD [[nodiscard]] -#define IA_UNUSED [[maybe_unused]] \ No newline at end of file +#define IA_UNUSED(v) (void)(v) diff --git a/Src/IACore/inc/IACore/Platform.hpp b/Src/IACore/inc/IACore/Platform.hpp index aeefe67..b7e452c 100644 --- a/Src/IACore/inc/IACore/Platform.hpp +++ b/Src/IACore/inc/IACore/Platform.hpp @@ -18,40 +18,35 @@ #include #if IA_ARCH_X64 -# ifdef _MSC_VER -# include -# else -# include -# endif +#ifdef _MSC_VER +#include +#else +#include +#endif #elif IA_ARCH_ARM64 -# include +#include #endif -namespace IACore -{ - class Platform - { - public: - struct Capabilities - { - bool hardware_crc32 = false; - }; +namespace IACore { +class Platform { +public: + struct Capabilities { + Mut hardware_crc32 = false; + }; - static auto check_cpu() -> bool; + static auto check_cpu() -> bool; #if IA_ARCH_X64 - static auto cpuid(i32 function, i32 sub_function, i32 out[4]) -> void; + static auto cpuid(Const function, Const sub_function, + Mut out) -> void; #endif - static auto get_architecture_name() -> const char *; - static auto get_operating_system_name() -> const char *; + static auto get_architecture_name() -> const char *; + static auto get_operating_system_name() -> const char *; - static auto get_capabilities() -> const Capabilities & - { - return s_capabilities; - } + static auto get_capabilities() -> Ref { return s_capabilities; } - private: - static Capabilities s_capabilities; - }; +private: + static Mut s_capabilities; +}; } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/inc/IACore/ProcessOps.hpp b/Src/IACore/inc/IACore/ProcessOps.hpp index 9b9d7c6..b21d9e6 100644 --- a/Src/IACore/inc/IACore/ProcessOps.hpp +++ b/Src/IACore/inc/IACore/ProcessOps.hpp @@ -27,13 +27,13 @@ using NativeProcessID = pid_t; namespace IACore { struct ProcessHandle { - std::atomic id{0}; - std::atomic is_running{false}; + Mut> id{0}; + Mut> is_running{false}; [[nodiscard]] auto is_active() const -> bool { return is_running && id != 0; } private: - std::jthread m_thread_handle; + Mut m_thread_handle; friend class ProcessOps; }; @@ -43,27 +43,27 @@ public: static auto get_current_process_id() -> NativeProcessID; static auto spawn_process_sync( - const String &command, const String &args, - std::function on_output_line_callback) + Ref command, Ref args, + Const)>> on_output_line_callback) -> Result; static auto spawn_process_async( - const String &command, const String &args, - std::function on_output_line_callback, - std::function)> on_finish_callback) + Ref command, Ref args, + Const)>> on_output_line_callback, + Const>)>> on_finish_callback) -> Result>; - static auto terminate_process(const Box &handle) -> void; + static auto terminate_process(Ref> handle) -> void; private: - static auto - spawn_process_windows(const String &command, const String &args, - std::function on_output_line_callback, - std::atomic &id) -> Result; + static auto spawn_process_windows( + Ref command, Ref args, + Const)>> on_output_line_callback, + MutRef> id) -> Result; - static auto - spawn_process_posix(const String &command, const String &args, - std::function on_output_line_callback, - std::atomic &id) -> Result; + static auto spawn_process_posix( + Ref command, Ref args, + Const)>> on_output_line_callback, + MutRef> id) -> Result; }; } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/inc/IACore/SIMD.hpp b/Src/IACore/inc/IACore/SIMD.hpp index 4dce8f6..c944c55 100644 --- a/Src/IACore/inc/IACore/SIMD.hpp +++ b/Src/IACore/inc/IACore/SIMD.hpp @@ -1,303 +1,280 @@ +// IACore-OSS; The Core Library for All IA Open Source Projects +// Copyright (C) 2026 IAS (ias@iasoft.dev) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #pragma once #include #if defined(__clang__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-parameter" -# pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" #endif #include #if defined(__clang__) -# pragma GCC diagnostic pop +#pragma GCC diagnostic pop #endif -namespace IACore -{ - namespace hn = hwy::HWY_NAMESPACE; +namespace IACore { +namespace hn = hwy::HWY_NAMESPACE; #if HWY_TARGET == HWY_SCALAR -# pragma message("Warning: Configuration mismatch. IACore is being compiled for SCALAR SIMD (Slow)") +#pragma message( \ + "Warning: Configuration mismatch. IACore is being compiled for SCALAR SIMD (Slow)") #endif - class alignas(16) IntVec4 - { - public: - IntVec4() = default; +class alignas(16) IntVec4 { +public: + IntVec4() = default; - inline explicit IntVec4(u32 s); - inline explicit IntVec4(const u32 *values); - inline explicit IntVec4(u32 a, u32 b, u32 c, u32 d); + inline explicit IntVec4(Const s); + inline explicit IntVec4(Const values); + inline explicit IntVec4(Const a, Const b, Const c, + Const d); - inline auto operator+(const IntVec4 &other) const -> IntVec4; - inline auto operator-(const IntVec4 &other) const -> IntVec4; - inline auto operator*(const IntVec4 &other) const -> IntVec4; + inline auto operator+(Ref other) const -> IntVec4; + inline auto operator-(Ref other) const -> IntVec4; + inline auto operator*(Ref other) const -> IntVec4; - inline auto operator&(const IntVec4 &other) const -> IntVec4; - inline auto operator|(const IntVec4 &other) const -> IntVec4; - inline auto operator^(const IntVec4 &other) const -> IntVec4; - inline auto operator~() const -> IntVec4; + inline auto operator&(Ref other) const -> IntVec4; + inline auto operator|(Ref other) const -> IntVec4; + inline auto operator^(Ref other) const -> IntVec4; + inline auto operator~() const -> IntVec4; - inline auto operator<<(u32 amount) const -> IntVec4; - inline auto operator>>(u32 amount) const -> IntVec4; + inline auto operator<<(Const amount) const -> IntVec4; + inline auto operator>>(Const amount) const -> IntVec4; - inline auto sat_add(const IntVec4 &other) const -> IntVec4; - inline auto sat_sub(const IntVec4 &other) const -> IntVec4; + [[nodiscard]] inline auto sat_add(Ref other) const -> IntVec4; + [[nodiscard]] inline auto sat_sub(Ref other) const -> IntVec4; - inline auto clamp(u32 min, u32 max) const -> IntVec4; + [[nodiscard]] inline auto clamp(Const min, Const max) const + -> IntVec4; - inline auto mult_add(const IntVec4 &multiplier, const IntVec4 &addend) const -> IntVec4; + [[nodiscard]] inline auto mult_add(Ref multiplier, + Ref addend) const -> IntVec4; - inline auto store(u32 *values) -> void; - static inline auto load(const u32 *values) -> IntVec4; + inline auto store(Mut values) -> void; + static inline auto load(Const values) -> IntVec4; - private: - using Tag = hn::FixedTag; +private: + using Tag = hn::FixedTag; - hn::Vec m_data; + Mut> m_data; - inline explicit IntVec4(hn::Vec v) : m_data(v) - { - } - }; + inline explicit IntVec4(Const> v) : m_data(v) {} +}; - class alignas(16) FloatVec4 - { - public: - FloatVec4() = default; +class alignas(16) FloatVec4 { +public: + FloatVec4() = default; - inline explicit FloatVec4(f32 s); - inline explicit FloatVec4(const f32 *values); - inline explicit FloatVec4(f32 a, f32 b, f32 c, f32 d); + inline explicit FloatVec4(Const s); + inline explicit FloatVec4(Const values); + inline explicit FloatVec4(Const a, Const b, Const c, + Const d); - inline auto operator+(const FloatVec4 &other) const -> FloatVec4; - inline auto operator-(const FloatVec4 &other) const -> FloatVec4; - inline auto operator*(const FloatVec4 &other) const -> FloatVec4; - inline auto operator/(const FloatVec4 &other) const -> FloatVec4; + inline auto operator+(Ref other) const -> FloatVec4; + inline auto operator-(Ref other) const -> FloatVec4; + inline auto operator*(Ref other) const -> FloatVec4; + inline auto operator/(Ref other) const -> FloatVec4; - inline auto clamp(f32 min, f32 max) const -> FloatVec4; + [[nodiscard]] inline auto clamp(Const min, Const max) const + -> FloatVec4; - inline auto abs() const -> FloatVec4; - inline auto sqrt() const -> FloatVec4; - inline auto rsqrt() const -> FloatVec4; - inline auto normalize() const -> FloatVec4; + [[nodiscard]] inline auto abs() const -> FloatVec4; + [[nodiscard]] inline auto sqrt() const -> FloatVec4; + [[nodiscard]] inline auto rsqrt() const -> FloatVec4; + [[nodiscard]] inline auto normalize() const -> FloatVec4; - inline auto dot(const FloatVec4 &other) const -> f32; + [[nodiscard]] inline auto dot(Ref other) const -> f32; - inline auto mult_add(const FloatVec4 &multiplier, const FloatVec4 &addend) const -> FloatVec4; + [[nodiscard]] inline auto mult_add(Ref multiplier, + Ref addend) const -> FloatVec4; - inline auto store(f32 *values) -> void; - static inline auto load(const f32 *values) -> FloatVec4; + inline auto store(Mut values) -> void; + static inline auto load(Const values) -> FloatVec4; - private: - using Tag = hn::FixedTag; +private: + using Tag = hn::FixedTag; - hn::Vec m_data; + Mut> m_data; - inline explicit FloatVec4(hn::Vec v) : m_data(v) - { - } - }; + inline explicit FloatVec4(Const> v) : m_data(v) {} +}; } // namespace IACore -namespace IACore -{ - IntVec4::IntVec4(u32 s) - { - const Tag d; - m_data = hn::Set(d, s); - } +namespace IACore { +IntVec4::IntVec4(Const s) { + Const d; + m_data = hn::Set(d, s); +} - IntVec4::IntVec4(const u32 *values) - { - const Tag data; - m_data = hn::Load(data, values); - } +IntVec4::IntVec4(Const values) { + Const data; + m_data = hn::Load(data, values); +} - IntVec4::IntVec4(u32 a, u32 b, u32 c, u32 d) - { - const Tag data; - alignas(16) u32 values[4] = {a, b, c, d}; - m_data = hn::Load(data, values); - } +IntVec4::IntVec4(Const a, Const b, Const c, Const d) { + Const data; + alignas(16) Mut> values = {a, b, c, d}; + m_data = hn::Load(data, values.data()); +} - auto IntVec4::operator+(const IntVec4 &other) const -> IntVec4 - { - return IntVec4(hn::Add(m_data, other.m_data)); - } +auto IntVec4::operator+(Ref other) const -> IntVec4 { + return IntVec4(hn::Add(m_data, other.m_data)); +} - auto IntVec4::operator-(const IntVec4 &other) const -> IntVec4 - { - return IntVec4(hn::Sub(m_data, other.m_data)); - } +auto IntVec4::operator-(Ref other) const -> IntVec4 { + return IntVec4(hn::Sub(m_data, other.m_data)); +} - auto IntVec4::operator*(const IntVec4 &other) const -> IntVec4 - { - return IntVec4(hn::Mul(m_data, other.m_data)); - } +auto IntVec4::operator*(Ref other) const -> IntVec4 { + return IntVec4(hn::Mul(m_data, other.m_data)); +} - auto IntVec4::operator&(const IntVec4 &other) const -> IntVec4 - { - return IntVec4(hn::And(m_data, other.m_data)); - } +auto IntVec4::operator&(Ref other) const -> IntVec4 { + return IntVec4(hn::And(m_data, other.m_data)); +} - auto IntVec4::operator|(const IntVec4 &other) const -> IntVec4 - { - return IntVec4(hn::Or(m_data, other.m_data)); - } +auto IntVec4::operator|(Ref other) const -> IntVec4 { + return IntVec4(hn::Or(m_data, other.m_data)); +} - auto IntVec4::operator^(const IntVec4 &other) const -> IntVec4 - { - return IntVec4(hn::Xor(m_data, other.m_data)); - } +auto IntVec4::operator^(Ref other) const -> IntVec4 { + return IntVec4(hn::Xor(m_data, other.m_data)); +} - auto IntVec4::operator~() const -> IntVec4 - { - return IntVec4(hn::Not(m_data)); - } +auto IntVec4::operator~() const -> IntVec4 { return IntVec4(hn::Not(m_data)); } - auto IntVec4::operator<<(u32 amount) const -> IntVec4 - { - return IntVec4(hn::ShiftLeftSame(m_data, amount)); - } +auto IntVec4::operator<<(Const amount) const -> IntVec4 { + return IntVec4(hn::ShiftLeftSame(m_data, amount)); +} - auto IntVec4::operator>>(u32 amount) const -> IntVec4 - { - return IntVec4(hn::ShiftRightSame(m_data, amount)); - } +auto IntVec4::operator>>(Const amount) const -> IntVec4 { + return IntVec4(hn::ShiftRightSame(m_data, amount)); +} - auto IntVec4::mult_add(const IntVec4 &multiplier, const IntVec4 &addend) const -> IntVec4 - { - return IntVec4(hn::MulAdd(m_data, multiplier.m_data, addend.m_data)); - } +auto IntVec4::mult_add(Ref multiplier, Ref addend) const + -> IntVec4 { + return IntVec4(hn::MulAdd(m_data, multiplier.m_data, addend.m_data)); +} - auto IntVec4::sat_add(const IntVec4 &other) const -> IntVec4 - { - return IntVec4(hn::SaturatedAdd(m_data, other.m_data)); - } +auto IntVec4::sat_add(Ref other) const -> IntVec4 { + return IntVec4(hn::SaturatedAdd(m_data, other.m_data)); +} - auto IntVec4::sat_sub(const IntVec4 &other) const -> IntVec4 - { - return IntVec4(hn::SaturatedSub(m_data, other.m_data)); - } +auto IntVec4::sat_sub(Ref other) const -> IntVec4 { + return IntVec4(hn::SaturatedSub(m_data, other.m_data)); +} - auto IntVec4::clamp(u32 min, u32 max) const -> IntVec4 - { - const Tag d; - auto vMin = hn::Set(d, min); - auto vMax = hn::Set(d, max); - return IntVec4(hn::Min(hn::Max(m_data, vMin), vMax)); - } +auto IntVec4::clamp(Const min, Const max) const -> IntVec4 { + Const d; + Const> v_min = hn::Set(d, min); + Const> v_max = hn::Set(d, max); + return IntVec4(hn::Min(hn::Max(m_data, v_min), v_max)); +} - auto IntVec4::store(u32 *values) -> void - { - const Tag d; - hn::Store(m_data, d, values); - } +auto IntVec4::store(Mut values) -> void { + Const d; + hn::Store(m_data, d, values); +} - auto IntVec4::load(const u32 *values) -> IntVec4 - { - const Tag d; - return IntVec4(hn::Load(d, values)); - } +auto IntVec4::load(Const values) -> IntVec4 { + Const d; + return IntVec4(hn::Load(d, values)); +} } // namespace IACore -namespace IACore -{ - FloatVec4::FloatVec4(f32 s) - { - const Tag d; - m_data = hn::Set(d, s); - } +namespace IACore { +FloatVec4::FloatVec4(Const s) { + Const d; + m_data = hn::Set(d, s); +} - FloatVec4::FloatVec4(const f32 *values) - { - const Tag d; - m_data = hn::Load(d, values); - } +FloatVec4::FloatVec4(Const values) { + Const d; + m_data = hn::Load(d, values); +} - FloatVec4::FloatVec4(f32 a, f32 b, f32 c, f32 d) - { - const Tag data; - alignas(16) f32 temp[4] = {a, b, c, d}; - m_data = hn::Load(data, temp); - } +FloatVec4::FloatVec4(Const a, Const b, Const c, Const d) { + Const data; + alignas(16) Mut> temp = {a, b, c, d}; + m_data = hn::Load(data, temp.data()); +} - auto FloatVec4::operator+(const FloatVec4 &other) const -> FloatVec4 - { - return FloatVec4(hn::Add(m_data, other.m_data)); - } +auto FloatVec4::operator+(Ref other) const -> FloatVec4 { + return FloatVec4(hn::Add(m_data, other.m_data)); +} - auto FloatVec4::operator-(const FloatVec4 &other) const -> FloatVec4 - { - return FloatVec4(hn::Sub(m_data, other.m_data)); - } +auto FloatVec4::operator-(Ref other) const -> FloatVec4 { + return FloatVec4(hn::Sub(m_data, other.m_data)); +} - auto FloatVec4::operator*(const FloatVec4 &other) const -> FloatVec4 - { - return FloatVec4(hn::Mul(m_data, other.m_data)); - } +auto FloatVec4::operator*(Ref other) const -> FloatVec4 { + return FloatVec4(hn::Mul(m_data, other.m_data)); +} - auto FloatVec4::operator/(const FloatVec4 &other) const -> FloatVec4 - { - return FloatVec4(hn::Div(m_data, other.m_data)); - } +auto FloatVec4::operator/(Ref other) const -> FloatVec4 { + return FloatVec4(hn::Div(m_data, other.m_data)); +} - auto FloatVec4::mult_add(const FloatVec4 &multiplier, const FloatVec4 &addend) const -> FloatVec4 - { - return FloatVec4(hn::MulAdd(m_data, multiplier.m_data, addend.m_data)); - } +auto FloatVec4::mult_add(Ref multiplier, Ref addend) const + -> FloatVec4 { + return FloatVec4(hn::MulAdd(m_data, multiplier.m_data, addend.m_data)); +} - auto FloatVec4::clamp(f32 min, f32 max) const -> FloatVec4 - { - const Tag d; - auto vMin = hn::Set(d, min); - auto vMax = hn::Set(d, max); - return FloatVec4(hn::Min(hn::Max(m_data, vMin), vMax)); - } +auto FloatVec4::clamp(Const min, Const max) const -> FloatVec4 { + Const d; + Const> v_min = hn::Set(d, min); + Const> v_max = hn::Set(d, max); + return FloatVec4(hn::Min(hn::Max(m_data, v_min), v_max)); +} - auto FloatVec4::sqrt() const -> FloatVec4 - { - return FloatVec4(hn::Sqrt(m_data)); - } +auto FloatVec4::sqrt() const -> FloatVec4 { + return FloatVec4(hn::Sqrt(m_data)); +} - auto FloatVec4::rsqrt() const -> FloatVec4 - { - return FloatVec4(hn::ApproximateReciprocalSqrt(m_data)); - } +auto FloatVec4::rsqrt() const -> FloatVec4 { + return FloatVec4(hn::ApproximateReciprocalSqrt(m_data)); +} - auto FloatVec4::abs() const -> FloatVec4 - { - return FloatVec4(hn::Abs(m_data)); - } +auto FloatVec4::abs() const -> FloatVec4 { return FloatVec4(hn::Abs(m_data)); } - auto FloatVec4::dot(const FloatVec4 &other) const -> f32 - { - const Tag d; - auto vMul = hn::Mul(m_data, other.m_data); - return hn::ReduceSum(d, vMul); - } +auto FloatVec4::dot(Ref other) const -> f32 { + Const d; + Const> v_mul = hn::Mul(m_data, other.m_data); + return hn::ReduceSum(d, v_mul); +} - auto FloatVec4::normalize() const -> FloatVec4 - { - const Tag d; - auto vMul = hn::Mul(m_data, m_data); - auto vLenSq = hn::SumOfLanes(d, vMul); - auto vInvLen = hn::ApproximateReciprocalSqrt(vLenSq); - return FloatVec4(hn::Mul(m_data, vInvLen)); - } +auto FloatVec4::normalize() const -> FloatVec4 { + Const d; + Const> v_mul = hn::Mul(m_data, m_data); + Const> v_len_sq = hn::SumOfLanes(d, v_mul); + Const> v_inv_len = hn::ApproximateReciprocalSqrt(v_len_sq); + return FloatVec4(hn::Mul(m_data, v_inv_len)); +} - auto FloatVec4::store(f32 *values) -> void - { - const Tag d; - hn::Store(m_data, d, values); - } +auto FloatVec4::store(Mut values) -> void { + Const d; + hn::Store(m_data, d, values); +} - auto FloatVec4::load(const f32 *values) -> FloatVec4 - { - const Tag d; - return FloatVec4(hn::Load(d, values)); - } +auto FloatVec4::load(Const values) -> FloatVec4 { + Const d; + return FloatVec4(hn::Load(d, values)); +} } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/inc/IACore/SocketOps.hpp b/Src/IACore/inc/IACore/SocketOps.hpp index 7afbfc9..440570f 100644 --- a/Src/IACore/inc/IACore/SocketOps.hpp +++ b/Src/IACore/inc/IACore/SocketOps.hpp @@ -27,8 +27,6 @@ #include #include #include -// Define INVALID_SOCKET for Unix to match Windows API for cross-platform -// compatibility #ifndef INVALID_SOCKET #define INVALID_SOCKET -1 #endif @@ -40,7 +38,7 @@ namespace IACore { #if IA_PLATFORM_WINDOWS using SocketHandle = SOCKET; #elif IA_PLATFORM_UNIX -using SocketHandle = int; +using SocketHandle = i32; #endif class SocketOps { @@ -54,8 +52,8 @@ public: return {}; } #if IA_PLATFORM_WINDOWS - WSADATA wsa_data; - const auto res = WSAStartup(MAKEWORD(2, 2), &wsa_data); + Mut wsa_data; + Const res = WSAStartup(MAKEWORD(2, 2), &wsa_data); if (res != 0) { s_init_count--; return fail("WSAStartup failed with error: {}", res); @@ -79,28 +77,29 @@ public: static auto is_initialized() -> bool { return s_init_count > 0; } - static auto is_port_available_tcp(u16 port) -> bool { + static auto is_port_available_tcp(Const port) -> bool { return is_port_available(port, SOCK_STREAM); } - static auto is_port_available_udp(u16 port) -> bool { + static auto is_port_available_udp(Const port) -> bool { return is_port_available(port, SOCK_DGRAM); } static auto is_would_block() -> bool; - static auto close(SocketHandle sock) -> void; + static auto close(Const sock) -> void; - static auto listen(SocketHandle sock, i32 queue_size = 5) -> Result; + static auto listen(Const sock, Const queue_size = 5) + -> Result; static auto create_unix_socket() -> Result; - static auto bind_unix_socket(SocketHandle sock, const char *path) - -> Result; - static auto connect_unix_socket(SocketHandle sock, const char *path) - -> Result; + static auto bind_unix_socket(Const sock, + Const path) -> Result; + static auto connect_unix_socket(Const sock, + Const path) -> Result; - static auto unlink_file(const char *path) -> void { + static auto unlink_file(Const path) -> void { #if IA_PLATFORM_WINDOWS DeleteFileA(path); #elif IA_PLATFORM_UNIX @@ -109,9 +108,9 @@ public: } private: - static auto is_port_available(u16 port, i32 type) -> bool; + static auto is_port_available(Const port, Const type) -> bool; private: - static i32 s_init_count; + static Mut s_init_count; }; } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/inc/IACore/StreamReader.hpp b/Src/IACore/inc/IACore/StreamReader.hpp index 5e09bb1..83543d6 100644 --- a/Src/IACore/inc/IACore/StreamReader.hpp +++ b/Src/IACore/inc/IACore/StreamReader.hpp @@ -28,29 +28,29 @@ public: OwningVector, }; - static auto create_from_file(const Path &path) -> Result; + static auto create_from_file(Ref path) -> Result; - explicit StreamReader(Vec &&data); - explicit StreamReader(Span data); + explicit StreamReader(ForwardRef> data); + explicit StreamReader(Const> data); ~StreamReader(); - StreamReader(StreamReader &&other); - auto operator=(StreamReader &&other) -> StreamReader &; + StreamReader(ForwardRef other); + auto operator=(ForwardRef other) -> MutRef; - StreamReader(const StreamReader &) = delete; - auto operator=(const StreamReader &) -> StreamReader & = delete; + StreamReader(Ref) = delete; + auto operator=(Ref) -> MutRef = delete; - auto read(void *buffer, usize size) -> Result; + auto read(Mut buffer, Const size) -> Result; template [[nodiscard("Check for EOF")]] auto read() -> Result; - auto skip(usize amount) -> void { + auto skip(Const amount) -> void { m_cursor = std::min(m_cursor + amount, m_data_size); } - auto seek(usize pos) -> void { + auto seek(Const pos) -> void { m_cursor = (pos > m_data_size) ? m_data_size : pos; } @@ -65,14 +65,15 @@ public: [[nodiscard]] auto is_eof() const -> bool { return m_cursor >= m_data_size; } private: - const u8 *m_data = nullptr; - usize m_cursor = 0; - usize m_data_size = 0; - Vec m_owning_vector; - StorageType m_storage_type = StorageType::NonOwning; + Mut m_data = nullptr; + Mut m_cursor = 0; + Mut m_data_size = 0; + Mut> m_owning_vector; + Mut m_storage_type = StorageType::NonOwning; }; -inline auto StreamReader::read(void *buffer, usize size) -> Result { +inline auto StreamReader::read(Mut buffer, Const size) + -> Result { if (m_cursor + size > m_data_size) [[unlikely]] { return fail("Unexpected EOF while reading"); } @@ -89,13 +90,13 @@ inline auto StreamReader::read() -> Result { static_assert(std::is_trivially_copyable_v, "T must be trivially copyable to read via memcpy"); - constexpr usize SIZE = sizeof(T); + constexpr Const SIZE = sizeof(T); if (m_cursor + SIZE > m_data_size) [[unlikely]] { return fail("Unexpected EOF while reading"); } - T value; + Mut value; std::memcpy(&value, &m_data[m_cursor], SIZE); m_cursor += SIZE; diff --git a/Src/IACore/inc/IACore/StreamWriter.hpp b/Src/IACore/inc/IACore/StreamWriter.hpp index a676ef4..1c71d43 100644 --- a/Src/IACore/inc/IACore/StreamWriter.hpp +++ b/Src/IACore/inc/IACore/StreamWriter.hpp @@ -27,23 +27,23 @@ public: OwningVector, }; - static auto create_from_file(const Path &path) -> Result; + static auto create_from_file(Ref path) -> Result; StreamWriter(); - explicit StreamWriter(Span data); + explicit StreamWriter(Const> data); - StreamWriter(StreamWriter &&other); - auto operator=(StreamWriter &&other) -> StreamWriter &; + StreamWriter(ForwardRef other); + auto operator=(ForwardRef other) -> MutRef; - StreamWriter(const StreamWriter &) = delete; - auto operator=(const StreamWriter &) -> StreamWriter & = delete; + StreamWriter(Ref) = delete; + auto operator=(Ref) -> MutRef = delete; ~StreamWriter(); - auto write(u8 byte, usize count) -> Result; - auto write(const void *buffer, usize size) -> Result; + auto write(Const byte, Const count) -> Result; + auto write(Const buffer, Const size) -> Result; - template auto write(const T &value) -> Result; + template auto write(Ref value) -> Result; [[nodiscard]] auto data() const -> const u8 * { return m_buffer; } @@ -52,19 +52,19 @@ public: auto flush() -> Result; private: - u8 *m_buffer = nullptr; - usize m_cursor = 0; - usize m_capacity = 0; - Path m_file_path; - Vec m_owning_vector; - StorageType m_storage_type = StorageType::OwningVector; + Mut m_buffer = nullptr; + Mut m_cursor = 0; + Mut m_capacity = 0; + Mut m_file_path; + Mut> m_owning_vector; + Mut m_storage_type = StorageType::OwningVector; private: auto flush_to_disk() -> Result; }; template -inline auto StreamWriter::write(const T &value) -> Result { +inline auto StreamWriter::write(Ref value) -> Result { return write(&value, sizeof(T)); } diff --git a/Src/IACore/inc/IACore/StringOps.hpp b/Src/IACore/inc/IACore/StringOps.hpp index 0c5090e..be6094c 100644 --- a/Src/IACore/inc/IACore/StringOps.hpp +++ b/Src/IACore/inc/IACore/StringOps.hpp @@ -20,7 +20,7 @@ namespace IACore { class StringOps { public: - static auto encode_base64(Span data) -> String; - static auto decode_base64(const String &data) -> Vec; + static auto encode_base64(Const>> data) -> String; + static auto decode_base64(Ref data) -> Vec; }; } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/inc/IACore/Utils.hpp b/Src/IACore/inc/IACore/Utils.hpp index 3a8ce65..36e1431 100644 --- a/Src/IACore/inc/IACore/Utils.hpp +++ b/Src/IACore/inc/IACore/Utils.hpp @@ -19,76 +19,73 @@ #include -namespace IACore -{ - class Utils - { - public: - static auto get_unix_time() -> u64; +namespace IACore { +class Utils { +public: + static auto get_unix_time() -> u64; - static auto get_ticks_count() -> u64; + static auto get_ticks_count() -> u64; - static auto get_seconds_count() -> f64; + static auto get_seconds_count() -> f64; - static auto get_random() -> f32; - static auto get_random(u64 max) -> u64; - static auto get_random(i64 min, i64 max) -> i64; + static auto get_random() -> f32; + static auto get_random(Const max) -> u64; + static auto get_random(Const min, Const max) -> i64; - static auto sleep(u64 milliseconds) -> void; + static auto sleep(Const milliseconds) -> void; - static auto binary_to_hex_string(Span data) -> String; + static auto binary_to_hex_string(Const>> data) -> String; - static auto hex_string_to_binary(StringView hex) -> Result>; + static auto hex_string_to_binary(Const hex) -> Result>; - template inline static void sort(Range &&range) - { - std::ranges::sort(range); - } + template + inline static auto sort(ForwardRef range) -> void { + std::ranges::sort(std::forward(range)); + } - template inline static auto binary_search_left(Range &&range, const T &value) - { - return std::ranges::lower_bound(range, value); - } + template + inline static auto binary_search_left(ForwardRef range, Ref value) + -> auto { + return std::ranges::lower_bound(std::forward(range), value); + } - template inline static auto binary_search_right(Range &&range, const T &value) - { - return std::ranges::upper_bound(range, value); - } + template + inline static auto binary_search_right(ForwardRef range, Ref value) + -> auto { + return std::ranges::upper_bound(std::forward(range), value); + } - template inline static void hash_combine(u64 &seed, const T &v) - { - u64 h; + template + inline static auto hash_combine(MutRef seed, Ref v) -> void { + Mut h = 0; - if constexpr (std::is_constructible_v) - { - StringView sv(v); - auto hasher = ankerl::unordered_dense::hash(); - h = hasher(sv); - } - else - { - auto hasher = ankerl::unordered_dense::hash(); - h = hasher(v); - } + if constexpr (std::is_constructible_v) { + Const sv(v); + Const> hasher; + h = hasher(sv); + } else { + Const> hasher; + h = hasher(v); + } - seed ^= h + 0x9e3779b97f4a7c15 + (seed << 6) + (seed >> 2); - } + seed ^= h + 0x9e3779b97f4a7c15 + (seed << 6) + (seed >> 2); + } - template inline static auto compute_hash(const Args &...args) -> u64 - { - u64 seed = 0; - (hash_combine(seed, args), ...); - return seed; - } + template + inline static auto compute_hash(Ref... args) -> u64 { + Mut seed = 0; + (hash_combine(seed, args), ...); + return seed; + } - template - inline static auto compute_hash_flat(const T &obj, MemberPtrs... members) -> u64 - { - u64 seed = 0; - (hash_combine(seed, obj.*members), ...); - return seed; - } - }; + template + inline static auto compute_hash_flat(Ref obj, Const... members) + -> u64 { + Mut seed = 0; + (hash_combine(seed, obj.*members), ...); + return seed; + } +}; } // namespace IACore // ----------------------------------------------------------------------------- @@ -100,14 +97,11 @@ namespace IACore // struct Vector3 { float x, y, z; }; // IA_MAKE_HASHABLE(Vector3, &Vector3::x, &Vector3::y, &Vector3::z) // ----------------------------------------------------------------------------- -#define IA_MAKE_HASHABLE(Type, ...) \ - template<> struct ankerl::unordered_dense::hash \ - { \ - using is_avalanching = void; \ - IA_NODISCARD \ - auto operator()(const Type &v) const noexcept -> IACore::u64 \ - { \ - /* Pass the object and the list of member pointers */ \ - return IACore::Utils::compute_hash_flat(v, __VA_ARGS__); \ - } \ - }; \ No newline at end of file +#define IA_MAKE_HASHABLE(Type, ...) \ + template <> struct ankerl::unordered_dense::hash { \ + using is_avalanching = void; \ + IA_NODISCARD \ + auto operator()(IACore::Ref v) const noexcept -> IACore::u64 { \ + return IACore::Utils::compute_hash_flat(v, __VA_ARGS__); \ + } \ + }; \ No newline at end of file diff --git a/Src/IACore/inc/IACore/XML.hpp b/Src/IACore/inc/IACore/XML.hpp index 99f3a56..09602f6 100644 --- a/Src/IACore/inc/IACore/XML.hpp +++ b/Src/IACore/inc/IACore/XML.hpp @@ -19,21 +19,21 @@ #include -namespace IACore -{ - class XML - { - public: - using Node = pugi::xml_node; - using Document = pugi::xml_document; +namespace IACore { +class XML { +public: + using Node = pugi::xml_node; + using Document = pugi::xml_document; - public: - static auto parse_from_string(const String &data) -> Result; - static auto parse_from_file(const Path &path) -> Result; +public: + static auto parse_from_string(Ref data) -> Result; + static auto parse_from_file(Ref path) -> Result; - static auto serialize_to_string(const Node &node, bool escape = false) -> String; - static auto serialize_to_string(const Document &doc, bool escape = false) -> String; + static auto serialize_to_string(Ref node, Const escape = false) + -> String; + static auto serialize_to_string(Ref doc, Const escape = false) + -> String; - static auto escape_xml_string(const String &xml) -> String; - }; + static auto escape_xml_string(Ref xml) -> String; +}; } // namespace IACore \ No newline at end of file diff --git a/Src/IACore/inc/full_repo_context.txt b/Src/IACore/inc/full_repo_context.txt deleted file mode 100644 index 5459790..0000000 --- a/Src/IACore/inc/full_repo_context.txt +++ /dev/null @@ -1,2770 +0,0 @@ ---- START FILE: full_repo_context.txt --- -``` - -``` ---- END FILE: full_repo_context.txt --- - ---- START FILE: IACore/AsyncOps.hpp --- -``` - -#pragma once - -#include -#include -#include -#include - -namespace IACore { -class AsyncOps { -public: - using TaskTag = u64; - using WorkerId = u16; - - static constexpr WorkerId MAIN_THREAD_WORKER_ID = 0; - - enum class Priority : u8 { High, Normal }; - - struct Schedule { - std::atomic counter{0}; - }; - -public: - static auto initialize_scheduler(u8 worker_count = 0) -> Result; - static auto terminate_scheduler() -> void; - - static auto schedule_task(std::function task, - TaskTag tag, Schedule *schedule, - Priority priority = Priority::Normal) -> void; - - static auto cancel_tasks_of_tag(TaskTag tag) -> void; - - static auto wait_for_schedule_completion(Schedule *schedule) -> void; - - static auto run_task(std::function task) -> void; - - [[nodiscard]] static auto get_worker_count() -> WorkerId; - -private: - struct ScheduledTask { - TaskTag tag{}; - Schedule *schedule_handle{}; - std::function task{}; - }; - - static auto schedule_worker_loop(std::stop_token stop_token, - WorkerId worker_id) -> void; - -private: - static std::mutex s_queue_mutex; - static std::condition_variable s_wake_condition; - static Vec s_schedule_workers; - static std::deque s_high_priority_queue; - static std::deque s_normal_priority_queue; -}; -} -``` ---- END FILE: IACore/AsyncOps.hpp --- - ---- START FILE: IACore/DataOps.hpp --- -``` - -#pragma once - -#include - -namespace IACore { -class DataOps { -public: - enum class CompressionType { None, Gzip, Zlib }; - -public: - static auto hash_fnv1a(const String &string) -> u32; - static auto hash_fnv1a(Span data) -> u32; - - static auto hash_xxhash(const String &string, u32 seed = 0) -> u32; - static auto hash_xxhash(Span data, u32 seed = 0) -> u32; - - static auto crc32(Span data) -> u32; - - static auto detect_compression(Span data) -> CompressionType; - - static auto gzip_inflate(Span data) -> Result>; - static auto gzip_deflate(Span data) -> Result>; - - static auto zlib_inflate(Span data) -> Result>; - static auto zlib_deflate(Span data) -> Result>; - - static auto zstd_inflate(Span data) -> Result>; - static auto zstd_deflate(Span data) -> Result>; -}; -} -``` ---- END FILE: IACore/DataOps.hpp --- - ---- START FILE: IACore/DynamicLib.hpp --- -``` - -#pragma once - -#include - -#if !IA_PLATFORM_WINDOWS -#include -#endif - -namespace ia = IACore; - -namespace IACore { - -class DynamicLib { -public: - [[nodiscard]] static auto load(const String &search_path, const String &name) - -> Result { - namespace fs = std::filesystem; - auto full_path = fs::path(search_path) / name; - - if (!full_path.has_extension()) { -#if IA_PLATFORM_WINDOWS - full_path += ".dll"; -#elif IA_PLATFORM_APPLE - full_path += ".dylib"; -#else - full_path += ".so"; -#endif - } - - DynamicLib lib; - -#if IA_PLATFORM_WINDOWS - const HMODULE h = LoadLibraryA(full_path.string().c_str()); - if (!h) { - return fail(get_windows_error()); - } - lib.m_handle = static_cast(h); -#else - void *h = dlopen(full_path.c_str(), RTLD_LAZY | RTLD_LOCAL); - if (!h) { - const char *err = dlerror(); - return fail(err ? err : "Unknown dlopen error"); - } - lib.m_handle = h; -#endif - - return lib; - } - - DynamicLib() = default; - - DynamicLib(DynamicLib &&other) noexcept : m_handle(other.m_handle) { - other.m_handle = nullptr; - } - - auto operator=(DynamicLib &&other) noexcept -> DynamicLib & { - if (this != &other) { - unload(); - m_handle = other.m_handle; - other.m_handle = nullptr; - } - return *this; - } - - DynamicLib(const DynamicLib &) = delete; - auto operator=(const DynamicLib &) -> DynamicLib & = delete; - - ~DynamicLib() { unload(); } - - [[nodiscard]] auto get_symbol(const String &name) const -> Result { - if (!m_handle) { - return fail("Library not loaded"); - } - - void *sym = nullptr; - -#if IA_PLATFORM_WINDOWS - sym = static_cast( - GetProcAddress(static_cast(m_handle), name.c_str())); - if (!sym) { - return fail(get_windows_error()); - } -#else - dlerror(); - sym = dlsym(m_handle, name.c_str()); - if (const char *err = dlerror()) { - return fail(err); - } -#endif - - return sym; - } - - template - [[nodiscard]] auto get_function(const String &name) const -> Result { - void *sym = nullptr; - IA_TRY(sym, get_symbol(name)); - return reinterpret_cast(sym); - } - - void unload() { - if (m_handle) { -#if IA_PLATFORM_WINDOWS - FreeLibrary(static_cast(m_handle)); -#else - dlclose(m_handle); -#endif - m_handle = nullptr; - } - } - - [[nodiscard]] auto is_loaded() const -> bool { return m_handle != nullptr; } - -private: - void *m_handle = nullptr; - -#if IA_PLATFORM_WINDOWS - static auto get_windows_error() -> String { - const DWORD error_id = ::GetLastError(); - if (error_id == 0) { - return String(); - } - - LPSTR message_buffer = nullptr; - const usize size = FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast(&message_buffer), 0, nullptr); - - String message(message_buffer, size); - LocalFree(message_buffer); - return "Win32 Error: " + message; - } -#endif -}; - -} -``` ---- END FILE: IACore/DynamicLib.hpp --- - ---- START FILE: IACore/Environment.hpp --- -``` - -#pragma once - -#include -#include - -namespace IACore { -class Environment { -public: - static auto find(const String &name) -> Option { -#if IA_PLATFORM_WINDOWS - const DWORD buffer_size = GetEnvironmentVariableA(name.c_str(), nullptr, 0); - - if (buffer_size == 0) { - return std::nullopt; - } - - String result; - result.resize(buffer_size); - - const DWORD actual_size = - GetEnvironmentVariableA(name.c_str(), result.data(), buffer_size); - - if (actual_size == 0 || actual_size > buffer_size) { - return std::nullopt; - } - - result.resize(actual_size); - return result; - -#else - const char *val = std::getenv(name.c_str()); - if (val == nullptr) { - return std::nullopt; - } - return String(val); -#endif - } - - static auto get(const String &name, const String &default_value = "") - -> String { - return find(name).value_or(default_value); - } - - static auto set(const String &name, const String &value) -> Result { - if (name.empty()) { - return fail("Environment variable name cannot be empty"); - } - -#if IA_PLATFORM_WINDOWS - if (SetEnvironmentVariableA(name.c_str(), value.c_str()) == 0) { - return fail("Failed to set environment variable: {}", name); - } -#else - if (setenv(name.c_str(), value.c_str(), 1) != 0) { - return fail("Failed to set environment variable: {}", name); - } -#endif - return {}; - } - - static auto unset(const String &name) -> Result { - if (name.empty()) { - return fail("Environment variable name cannot be empty"); - } - -#if IA_PLATFORM_WINDOWS - if (SetEnvironmentVariableA(name.c_str(), nullptr) == 0) { - return fail("Failed to unset environment variable: {}", name); - } -#else - if (unsetenv(name.c_str()) != 0) { - return fail("Failed to unset environment variable: {}", name); - } -#endif - return {}; - } - - static auto exists(const String &name) -> bool { - return find(name).has_value(); - } -}; -} -``` ---- END FILE: IACore/Environment.hpp --- - ---- START FILE: IACore/FileOps.hpp --- -``` - -#pragma once - -#include -#include -#include -#include - -#if IA_PLATFORM_WINDOWS -using NativeFileHandle = HANDLE; -static constexpr NativeFileHandle INVALID_FILE_HANDLE = INVALID_HANDLE_VALUE; -#else -using NativeFileHandle = int; -static constexpr NativeFileHandle INVALID_FILE_HANDLE = -1; -#endif - -namespace IACore { - -class FileOps { -public: - class MemoryMappedRegion; - - enum class FileAccess : u8 { - Read, Write, ReadWrite }; - - enum class FileMode : u8 { - OpenExisting, OpenAlways, CreateNew, CreateAlways, TruncateExisting }; - - static auto native_open_file(const Path &path, FileAccess access, - FileMode mode, u32 permissions = 0644) - -> Result; - - static auto native_close_file(NativeFileHandle handle) -> void; - -public: - static auto normalize_executable_path(const Path &path) -> Path; - -public: - static auto unmap_file(const u8 *mapped_ptr) -> void; - - static auto map_file(const Path &path, usize &size) -> Result; - - static auto map_shared_memory(const String &name, usize size, bool is_owner) - -> Result; - - static auto unlink_shared_memory(const String &name) -> void; - - static auto stream_from_file(const Path &path) -> Result; - - static auto stream_to_file(const Path &path, bool overwrite = false) - -> Result; - - static auto read_text_file(const Path &path) -> Result; - - static auto read_binary_file(const Path &path) -> Result>; - - static auto write_text_file(const Path &path, const String &contents, - bool overwrite = false) -> Result; - - static auto write_binary_file(const Path &path, Span contents, - bool overwrite = false) -> Result; - -private: - static HashMap> s_mapped_files; -}; - -class FileOps::MemoryMappedRegion { -public: - MemoryMappedRegion() = default; - ~MemoryMappedRegion(); - - MemoryMappedRegion(const MemoryMappedRegion &) = delete; - auto operator=(const MemoryMappedRegion &) -> MemoryMappedRegion & = delete; - - MemoryMappedRegion(MemoryMappedRegion &&other) noexcept; - auto operator=(MemoryMappedRegion &&other) noexcept -> MemoryMappedRegion &; - - auto map(NativeFileHandle handle, u64 offset, usize size) -> Result; - - auto unmap() -> void; - auto flush() -> void; - - [[nodiscard]] auto get_ptr() const -> u8 * { return m_ptr; } - - [[nodiscard]] auto get_size() const -> usize { return m_size; } - - [[nodiscard]] auto is_valid() const -> bool { return m_ptr != nullptr; } - -private: - u8 *m_ptr = nullptr; - usize m_size = 0; - -#if IA_PLATFORM_WINDOWS - HANDLE m_map_handle = NULL; -#endif -}; - -} -``` ---- END FILE: IACore/FileOps.hpp --- - ---- START FILE: IACore/IATest.hpp --- -``` - -#pragma once - -#include - - -#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)) - -#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 IACore::Test::Block - -#define IAT_BEGIN_BLOCK(_group, _name) \ - class _group##_##_name : public IACore::Test::Block { \ - public: \ - [[nodiscard]] auto get_name() const -> const char * override { \ - return #_group "::" #_name; \ - } \ - \ - private: - -#define IAT_END_BLOCK() \ - } \ - ; - -#define IAT_BEGIN_TEST_LIST() \ -public: \ - void declare_tests() override { -#define IAT_ADD_TEST(name) IAT_UNIT(name) -#define IAT_END_TEST_LIST() \ - } \ - \ -private: - -namespace IACore::Test { -template auto to_string(const T &value) -> String { - if constexpr (std::is_arithmetic_v) { - return std::to_string(value); - } else if constexpr (std::is_same_v || - std::is_same_v || - std::is_same_v) { - return String("\"") + String(value) + "\""; - } else { - return "{Object}"; - } -} - -template auto to_string(T *value) -> String { - if (value == nullptr) { - return "nullptr"; - } - return std::format("ptr({})", static_cast(value)); -} - -using TestFunctor = std::function; - -struct TestUnit { - String name; - TestFunctor functor; -}; - -class Block { -public: - virtual ~Block() = default; - [[nodiscard]] virtual auto get_name() const -> const char * = 0; - virtual void declare_tests() = 0; - - auto units() -> Vec & { return m_units; } - -protected: - template - auto _test_eq(const T1 &lhs, const T2 &rhs, const char *description) -> bool { - if (lhs != rhs) { - print_fail(description, to_string(lhs), to_string(rhs)); - return false; - } - return true; - } - - template - auto _test_neq(const T1 &lhs, const T2 &rhs, const char *description) - -> bool { - if (lhs == rhs) { - print_fail(description, to_string(lhs), "NOT " + to_string(rhs)); - return false; - } - return true; - } - - template - auto _test_approx(T lhs, T rhs, const char *description) -> bool { - static_assert(std::is_floating_point_v, - "Approx only works for floats/doubles"); - T diff = std::abs(lhs - rhs); - if (diff > static_cast(0.0001)) { - print_fail(description, to_string(lhs), to_string(rhs)); - return false; - } - return true; - } - - auto _test(bool value, const char *description) -> bool { - if (!value) { - std::cout << console::BLUE << " " << description << "... " - << console::RED << "FAILED" << console::RESET << "\n"; - return false; - } - return true; - } - - auto _test_not(bool value, const char *description) -> bool { - if (value) { - std::cout << console::BLUE << " " << description << "... " - << console::RED << "FAILED" << console::RESET << "\n"; - return false; - } - return true; - } - - void _test_unit(TestFunctor functor, const char *name) { - m_units.push_back({name, std::move(functor)}); - } - -private: - void print_fail(const char *desc, const String &v1, const String &v2) { - std::cout << console::BLUE << " " << desc << "... " << console::RED - << "FAILED" << console::RESET << "\n"; - std::cout << console::RED << " Expected: " << v2 << console::RESET - << "\n"; - std::cout << console::RED << " Actual: " << v1 << console::RESET - << "\n"; - } - - Vec m_units; -}; - -template -concept ValidBlockClass = std::derived_from; - -template class Runner { -public: - Runner() = default; - - ~Runner() { summarize(); } - - template - requires ValidBlockClass - void test_block(); - -private: - void summarize(); - - usize m_test_count{0}; - usize m_fail_count{0}; - usize m_block_count{0}; -}; - -template -template - requires ValidBlockClass -void Runner::test_block() { - m_block_count++; - BlockClass b; - b.declare_tests(); - - std::cout << console::MAGENTA << "Testing [" << b.get_name() << "]..." - << console::RESET << "\n"; - - for (auto &v : b.units()) { - m_test_count++; - if constexpr (IsVerbose) { - std::cout << console::YELLOW << " Testing " << v.name << "...\n" - << console::RESET; - } - - bool result = v.functor(); - - if (!result) { - m_fail_count++; - if constexpr (StopOnFail) { - summarize(); - std::exit(-1); - } - } - } - std::cout << "\n"; -} - -template -void Runner::summarize() { - std::cout << console::GREEN - << "\n-----------------------------------\n\t " - "SUMMARY\n-----------------------------------\n"; - - if (m_fail_count == 0) { - std::cout << "\n\tALL TESTS PASSED!\n\n"; - } else { - f64 success_rate = (100.0 * static_cast(m_test_count - m_fail_count) / - static_cast(m_test_count)); - std::cout << console::RED << m_fail_count << " OF " << m_test_count - << " TESTS FAILED\n" - << console::YELLOW - << std::format("Success Rate: {:.2f}%\n", success_rate); - } - - std::cout << console::MAGENTA << "Ran " << m_test_count << " test(s) across " - << m_block_count << " block(s)\n" - << console::GREEN << "-----------------------------------" - << console::RESET << "\n"; -} - -using DefaultRunner = Runner; - -class TestRegistry { -public: - using TestEntry = std::function; - - static auto get_entries() -> Vec & { - static Vec entries; - return entries; - } - - static auto run_all() -> i32 { - DefaultRunner r; - auto &entries = get_entries(); - std::cout << console::CYAN << "[IATest] Discovered " << entries.size() - << " Test Blocks\n\n" - << console::RESET; - - for (auto &entry : entries) { - entry(r); - } - - return 0; - } -}; - -template struct AutoRegister { - AutoRegister() { - TestRegistry::get_entries().push_back( - [](DefaultRunner &r) { r.test_block(); }); - } -}; -} -#define IAT_REGISTER_ENTRY(Group, Name) \ - static IACore::Test::AutoRegister _iat_reg_##Group##_##Name; -``` ---- END FILE: IACore/IATest.hpp --- - ---- START FILE: IACore/IPC.hpp --- -``` - -#pragma once - -#include -#include -#include - -namespace IACore { -using IpcPacketHeader = RingBufferView::PacketHeader; - -struct alignas(64) IpcSharedMemoryLayout { - struct Header { - u32 magic; u32 version; u64 total_size; } meta; - - u8 _pad0[64 - sizeof(Header)]; - - - RingBufferView::ControlBlock moni_control; - RingBufferView::ControlBlock mino_control; - - - u64 moni_data_offset; - u64 moni_data_size; - - u64 mino_data_offset; - u64 mino_data_size; - - u8 _pad1[64 - (sizeof(u64) * 4)]; - - static constexpr auto get_header_size() -> usize { - return sizeof(IpcSharedMemoryLayout); - } -}; - -static_assert(sizeof(IpcSharedMemoryLayout) % 64 == 0, - "IPC Layout is not cache-line aligned!"); - -class IpcNode { -public: - virtual ~IpcNode(); - - auto connect(const char *connection_string) -> Result; - - void update(); - - void send_signal(u8 signal); - auto send_packet(u16 packet_id, Span payload) -> Result; - -protected: - virtual void on_signal(u8 signal) = 0; - virtual void on_packet(u16 packet_id, Span payload) = 0; - -private: - String m_shm_name; - u8 *m_shared_memory{}; - Vec m_receive_buffer; - SocketHandle m_socket{INVALID_SOCKET}; - - RingBufferView m_moni; RingBufferView m_mino; }; - -class IpcManager { - struct NodeSession { - std::chrono::system_clock::time_point creation_time{}; - Box node_process; - - std::mutex send_mutex; - - String shared_mem_name; - u8 *mapped_ptr{}; - - SocketHandle listener_socket{INVALID_SOCKET}; - SocketHandle data_socket{INVALID_SOCKET}; - - RingBufferView moni = - RingBufferView::default_instance(); RingBufferView mino = - RingBufferView::default_instance(); - bool is_ready{false}; - - void send_signal(u8 signal); - auto send_packet(u16 packet_id, Span payload) -> Result; - }; - -public: - static constexpr u32 DEFAULT_NODE_SHARED_MEMORY_SIZE = 4 * 1024 * 1024; -public: - virtual ~IpcManager(); - - void update(); - - auto spawn_node(const Path &executable_path, - u32 shared_memory_size = DEFAULT_NODE_SHARED_MEMORY_SIZE) - -> Result; - - auto wait_till_node_is_online(NativeProcessID node) -> bool; - - void shutdown_node(NativeProcessID node); - - void send_signal(NativeProcessID node, u8 signal); - auto send_packet(NativeProcessID node, u16 packet_id, Span payload) - -> Result; - -protected: - virtual void on_signal(NativeProcessID node, u8 signal) = 0; - virtual void on_packet(NativeProcessID node, u16 packet_id, - Span payload) = 0; - -private: - Vec m_receive_buffer; - Vec> m_active_sessions; - Vec> m_pending_sessions; - HashMap m_active_session_map; - -protected: - IpcManager(); -}; -} -``` ---- END FILE: IACore/IPC.hpp --- - ---- START FILE: IACore/ProcessOps.hpp --- -``` - -#pragma once - -#include - -#if IA_PLATFORM_WINDOWS -using NativeProcessID = DWORD; -#elif IA_PLATFORM_UNIX -using NativeProcessID = pid_t; -#else -#error "This platform does not support IACore ProcessOps" -#endif - -namespace IACore { -struct ProcessHandle { - std::atomic id{0}; - std::atomic is_running{false}; - - [[nodiscard]] auto is_active() const -> bool { return is_running && id != 0; } - -private: - std::jthread m_thread_handle; - - friend class ProcessOps; -}; - -class ProcessOps { -public: - static auto get_current_process_id() -> NativeProcessID; - - static auto spawn_process_sync( - const String &command, const String &args, - std::function on_output_line_callback) - -> Result; - - static auto spawn_process_async( - const String &command, const String &args, - std::function on_output_line_callback, - std::function)> on_finish_callback) - -> Result>; - - static auto terminate_process(const Box &handle) -> void; - -private: - static auto - spawn_process_windows(const String &command, const String &args, - std::function on_output_line_callback, - std::atomic &id) -> Result; - - static auto - spawn_process_posix(const String &command, const String &args, - std::function on_output_line_callback, - std::atomic &id) -> Result; -}; -} -``` ---- END FILE: IACore/ProcessOps.hpp --- - ---- START FILE: IACore/SocketOps.hpp --- -``` - -#pragma once - -#include - -#if IA_PLATFORM_WINDOWS -#include -#include -#include -#pragma comment(lib, "ws2_32.lib") -#elif IA_PLATFORM_UNIX -#include -#include -#include -#include -#ifndef INVALID_SOCKET -#define INVALID_SOCKET -1 -#endif -#else -#error "IACore SocketOps is not supported on this platform." -#endif - -namespace IACore { -#if IA_PLATFORM_WINDOWS -using SocketHandle = SOCKET; -#elif IA_PLATFORM_UNIX -using SocketHandle = int; -#endif - -class SocketOps { -public: - static auto initialize() -> Result { - s_init_count++; - if (s_init_count > 1) { - return {}; - } -#if IA_PLATFORM_WINDOWS - WSADATA wsa_data; - const auto res = WSAStartup(MAKEWORD(2, 2), &wsa_data); - if (res != 0) { - s_init_count--; - return fail("WSAStartup failed with error: {}", res); - } -#endif - return {}; - } - - static auto terminate() -> void { - s_init_count--; - if (s_init_count > 0) { - return; - } -#if IA_PLATFORM_WINDOWS - WSACleanup(); -#endif - } - - static auto is_initialized() -> bool { return s_init_count > 0; } - - static auto is_port_available_tcp(u16 port) -> bool { - return is_port_available(port, SOCK_STREAM); - } - - static auto is_port_available_udp(u16 port) -> bool { - return is_port_available(port, SOCK_DGRAM); - } - - static auto is_would_block() -> bool; - - static auto close(SocketHandle sock) -> void; - - static auto listen(SocketHandle sock, i32 queue_size = 5) -> Result; - - static auto create_unix_socket() -> Result; - - static auto bind_unix_socket(SocketHandle sock, const char *path) - -> Result; - static auto connect_unix_socket(SocketHandle sock, const char *path) - -> Result; - - static auto unlink_file(const char *path) -> void { -#if IA_PLATFORM_WINDOWS - DeleteFileA(path); -#elif IA_PLATFORM_UNIX - unlink(path); -#endif - } - -private: - static auto is_port_available(u16 port, i32 type) -> bool; - -private: - static i32 s_init_count; -}; -} -``` ---- END FILE: IACore/SocketOps.hpp --- - ---- START FILE: IACore/StreamReader.hpp --- -``` - -#pragma once - -#include -#include -#include - -namespace IACore { -class StreamReader { -public: - enum class StorageType { - NonOwning, - OwningMmap, - OwningVector, - }; - - static auto create_from_file(const Path &path) -> Result; - - explicit StreamReader(Vec &&data); - explicit StreamReader(Span data); - ~StreamReader(); - - StreamReader(StreamReader &&other); - auto operator=(StreamReader &&other) -> StreamReader &; - - StreamReader(const StreamReader &) = delete; - auto operator=(const StreamReader &) -> StreamReader & = delete; - - auto read(void *buffer, usize size) -> Result; - - template - [[nodiscard("Check for EOF")]] - auto read() -> Result; - - auto skip(usize amount) -> void { - m_cursor = std::min(m_cursor + amount, m_data_size); - } - - auto seek(usize pos) -> void { - m_cursor = (pos > m_data_size) ? m_data_size : pos; - } - - [[nodiscard]] auto cursor() const -> usize { return m_cursor; } - - [[nodiscard]] auto size() const -> usize { return m_data_size; } - - [[nodiscard]] auto remaining() const -> usize { - return m_data_size - m_cursor; - } - - [[nodiscard]] auto is_eof() const -> bool { return m_cursor >= m_data_size; } - -private: - const u8 *m_data = nullptr; - usize m_cursor = 0; - usize m_data_size = 0; - Vec m_owning_vector; - StorageType m_storage_type = StorageType::NonOwning; -}; - -inline auto StreamReader::read(void *buffer, usize size) -> Result { - if (m_cursor + size > m_data_size) [[unlikely]] { - return fail("Unexpected EOF while reading"); - } - - std::memcpy(buffer, &m_data[m_cursor], size); - m_cursor += size; - - return {}; -} - -template -[[nodiscard("Check for EOF")]] -inline auto StreamReader::read() -> Result { - static_assert(std::is_trivially_copyable_v, - "T must be trivially copyable to read via memcpy"); - - constexpr usize SIZE = sizeof(T); - - if (m_cursor + SIZE > m_data_size) [[unlikely]] { - return fail("Unexpected EOF while reading"); - } - - T value; - std::memcpy(&value, &m_data[m_cursor], SIZE); - m_cursor += SIZE; - - return value; -} - -} -``` ---- END FILE: IACore/StreamReader.hpp --- - ---- START FILE: IACore/StreamWriter.hpp --- -``` - -#pragma once - -#include - -namespace IACore { - -class StreamWriter { -public: - enum class StorageType { - NonOwning, - OwningFile, - OwningVector, - }; - - static auto create_from_file(const Path &path) -> Result; - - StreamWriter(); - explicit StreamWriter(Span data); - - StreamWriter(StreamWriter &&other); - auto operator=(StreamWriter &&other) -> StreamWriter &; - - StreamWriter(const StreamWriter &) = delete; - auto operator=(const StreamWriter &) -> StreamWriter & = delete; - - ~StreamWriter(); - - auto write(u8 byte, usize count) -> Result; - auto write(const void *buffer, usize size) -> Result; - - template auto write(const T &value) -> Result; - - [[nodiscard]] auto data() const -> const u8 * { return m_buffer; } - - [[nodiscard]] auto cursor() const -> usize { return m_cursor; } - - auto flush() -> Result; - -private: - u8 *m_buffer = nullptr; - usize m_cursor = 0; - usize m_capacity = 0; - Path m_file_path; - Vec m_owning_vector; - StorageType m_storage_type = StorageType::OwningVector; - -private: - auto flush_to_disk() -> Result; -}; - -template -inline auto StreamWriter::write(const T &value) -> Result { - return write(&value, sizeof(T)); -} - -} -``` ---- END FILE: IACore/StreamWriter.hpp --- - ---- START FILE: IACore/StringOps.hpp --- -``` - -#pragma once - -#include - -namespace IACore { -class StringOps { -public: - static auto encode_base64(Span data) -> String; - static auto decode_base64(const String &data) -> Vec; -}; -} -``` ---- END FILE: IACore/StringOps.hpp --- - ---- START FILE: IACore/Utils.hpp --- -``` - -#pragma once - -#include - -#include - -namespace IACore -{ - class Utils - { - public: - static auto get_unix_time() -> u64; - - static auto get_ticks_count() -> u64; - - static auto get_seconds_count() -> f64; - - static auto get_random() -> f32; - static auto get_random(u64 max) -> u64; - static auto get_random(i64 min, i64 max) -> i64; - - static auto sleep(u64 milliseconds) -> void; - - static auto binary_to_hex_string(Span data) -> String; - - static auto hex_string_to_binary(StringView hex) -> Result>; - - template inline static void sort(Range &&range) - { - std::ranges::sort(range); - } - - template inline static auto binary_search_left(Range &&range, const T &value) - { - return std::ranges::lower_bound(range, value); - } - - template inline static auto binary_search_right(Range &&range, const T &value) - { - return std::ranges::upper_bound(range, value); - } - - template inline static void hash_combine(u64 &seed, const T &v) - { - u64 h; - - if constexpr (std::is_constructible_v) - { - StringView sv(v); - auto hasher = ankerl::unordered_dense::hash(); - h = hasher(sv); - } - else - { - auto hasher = ankerl::unordered_dense::hash(); - h = hasher(v); - } - - seed ^= h + 0x9e3779b97f4a7c15 + (seed << 6) + (seed >> 2); - } - - template inline static auto compute_hash(const Args &...args) -> u64 - { - u64 seed = 0; - (hash_combine(seed, args), ...); - return seed; - } - - template - inline static auto compute_hash_flat(const T &obj, MemberPtrs... members) -> u64 - { - u64 seed = 0; - (hash_combine(seed, obj.*members), ...); - return seed; - } - }; -} -#define IA_MAKE_HASHABLE(Type, ...) \ - template<> struct ankerl::unordered_dense::hash \ - { \ - using is_avalanching = void; \ - IA_NODISCARD \ - auto operator()(const Type &v) const noexcept -> IACore::u64 \ - { \ - \ - return IACore::Utils::compute_hash_flat(v, __VA_ARGS__); \ - } \ - }; -``` ---- END FILE: IACore/Utils.hpp --- - ---- START FILE: IACore/XML.hpp --- -``` - -#pragma once - -#include - -#include - -namespace IACore -{ - class XML - { - public: - using Node = pugi::xml_node; - using Document = pugi::xml_document; - - public: - static auto parse_from_string(const String &data) -> Result; - static auto parse_from_file(const Path &path) -> Result; - - static auto serialize_to_string(const Node &node, bool escape = false) -> String; - static auto serialize_to_string(const Document &doc, bool escape = false) -> String; - - static auto escape_xml_string(const String &xml) -> String; - }; -} -``` ---- END FILE: IACore/XML.hpp --- - ---- START FILE: IACore/Platform.hpp --- -``` - -#pragma once - -#include - -#if IA_ARCH_X64 -# ifdef _MSC_VER -# include -# else -# include -# endif -#elif IA_ARCH_ARM64 -# include -#endif - -namespace IACore -{ - class Platform - { - public: - struct Capabilities - { - bool hardware_crc32 = false; - }; - - static auto check_cpu() -> bool; - -#if IA_ARCH_X64 - static auto cpuid(i32 function, i32 sub_function, i32 out[4]) -> void; -#endif - - static auto get_architecture_name() -> const char *; - static auto get_operating_system_name() -> const char *; - - static auto get_capabilities() -> const Capabilities & - { - return s_capabilities; - } - - private: - static Capabilities s_capabilities; - }; -} -``` ---- END FILE: IACore/Platform.hpp --- - ---- START FILE: IACore/SIMD.hpp --- -``` -#pragma once - -#include - -#if defined(__clang__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-parameter" -# pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" -#endif - -#include - -#if defined(__clang__) -# pragma GCC diagnostic pop -#endif - -namespace IACore -{ - namespace hn = hwy::HWY_NAMESPACE; - -#if HWY_TARGET == HWY_SCALAR -# pragma message("Warning: Configuration mismatch. IACore is being compiled for SCALAR SIMD (Slow)") -#endif - - class alignas(16) IntVec4 - { - public: - IntVec4() = default; - - inline explicit IntVec4(u32 s); - inline explicit IntVec4(const u32 *values); - inline explicit IntVec4(u32 a, u32 b, u32 c, u32 d); - - inline auto operator+(const IntVec4 &other) const -> IntVec4; - inline auto operator-(const IntVec4 &other) const -> IntVec4; - inline auto operator*(const IntVec4 &other) const -> IntVec4; - - inline auto operator&(const IntVec4 &other) const -> IntVec4; - inline auto operator|(const IntVec4 &other) const -> IntVec4; - inline auto operator^(const IntVec4 &other) const -> IntVec4; - inline auto operator~() const -> IntVec4; - - inline auto operator<<(u32 amount) const -> IntVec4; - inline auto operator>>(u32 amount) const -> IntVec4; - - inline auto sat_add(const IntVec4 &other) const -> IntVec4; - inline auto sat_sub(const IntVec4 &other) const -> IntVec4; - - inline auto clamp(u32 min, u32 max) const -> IntVec4; - - inline auto mult_add(const IntVec4 &multiplier, const IntVec4 &addend) const -> IntVec4; - - inline auto store(u32 *values) -> void; - static inline auto load(const u32 *values) -> IntVec4; - - private: - using Tag = hn::FixedTag; - - hn::Vec m_data; - - inline explicit IntVec4(hn::Vec v) : m_data(v) - { - } - }; - - class alignas(16) FloatVec4 - { - public: - FloatVec4() = default; - - inline explicit FloatVec4(f32 s); - inline explicit FloatVec4(const f32 *values); - inline explicit FloatVec4(f32 a, f32 b, f32 c, f32 d); - - inline auto operator+(const FloatVec4 &other) const -> FloatVec4; - inline auto operator-(const FloatVec4 &other) const -> FloatVec4; - inline auto operator*(const FloatVec4 &other) const -> FloatVec4; - inline auto operator/(const FloatVec4 &other) const -> FloatVec4; - - inline auto clamp(f32 min, f32 max) const -> FloatVec4; - - inline auto abs() const -> FloatVec4; - inline auto sqrt() const -> FloatVec4; - inline auto rsqrt() const -> FloatVec4; - inline auto normalize() const -> FloatVec4; - - inline auto dot(const FloatVec4 &other) const -> f32; - - inline auto mult_add(const FloatVec4 &multiplier, const FloatVec4 &addend) const -> FloatVec4; - - inline auto store(f32 *values) -> void; - static inline auto load(const f32 *values) -> FloatVec4; - - private: - using Tag = hn::FixedTag; - - hn::Vec m_data; - - inline explicit FloatVec4(hn::Vec v) : m_data(v) - { - } - }; -} -namespace IACore -{ - IntVec4::IntVec4(u32 s) - { - const Tag d; - m_data = hn::Set(d, s); - } - - IntVec4::IntVec4(const u32 *values) - { - const Tag data; - m_data = hn::Load(data, values); - } - - IntVec4::IntVec4(u32 a, u32 b, u32 c, u32 d) - { - const Tag data; - alignas(16) u32 values[4] = {a, b, c, d}; - m_data = hn::Load(data, values); - } - - auto IntVec4::operator+(const IntVec4 &other) const -> IntVec4 - { - return IntVec4(hn::Add(m_data, other.m_data)); - } - - auto IntVec4::operator-(const IntVec4 &other) const -> IntVec4 - { - return IntVec4(hn::Sub(m_data, other.m_data)); - } - - auto IntVec4::operator*(const IntVec4 &other) const -> IntVec4 - { - return IntVec4(hn::Mul(m_data, other.m_data)); - } - - auto IntVec4::operator&(const IntVec4 &other) const -> IntVec4 - { - return IntVec4(hn::And(m_data, other.m_data)); - } - - auto IntVec4::operator|(const IntVec4 &other) const -> IntVec4 - { - return IntVec4(hn::Or(m_data, other.m_data)); - } - - auto IntVec4::operator^(const IntVec4 &other) const -> IntVec4 - { - return IntVec4(hn::Xor(m_data, other.m_data)); - } - - auto IntVec4::operator~() const -> IntVec4 - { - return IntVec4(hn::Not(m_data)); - } - - auto IntVec4::operator<<(u32 amount) const -> IntVec4 - { - return IntVec4(hn::ShiftLeftSame(m_data, amount)); - } - - auto IntVec4::operator>>(u32 amount) const -> IntVec4 - { - return IntVec4(hn::ShiftRightSame(m_data, amount)); - } - - auto IntVec4::mult_add(const IntVec4 &multiplier, const IntVec4 &addend) const -> IntVec4 - { - return IntVec4(hn::MulAdd(m_data, multiplier.m_data, addend.m_data)); - } - - auto IntVec4::sat_add(const IntVec4 &other) const -> IntVec4 - { - return IntVec4(hn::SaturatedAdd(m_data, other.m_data)); - } - - auto IntVec4::sat_sub(const IntVec4 &other) const -> IntVec4 - { - return IntVec4(hn::SaturatedSub(m_data, other.m_data)); - } - - auto IntVec4::clamp(u32 min, u32 max) const -> IntVec4 - { - const Tag d; - auto vMin = hn::Set(d, min); - auto vMax = hn::Set(d, max); - return IntVec4(hn::Min(hn::Max(m_data, vMin), vMax)); - } - - auto IntVec4::store(u32 *values) -> void - { - const Tag d; - hn::Store(m_data, d, values); - } - - auto IntVec4::load(const u32 *values) -> IntVec4 - { - const Tag d; - return IntVec4(hn::Load(d, values)); - } -} -namespace IACore -{ - FloatVec4::FloatVec4(f32 s) - { - const Tag d; - m_data = hn::Set(d, s); - } - - FloatVec4::FloatVec4(const f32 *values) - { - const Tag d; - m_data = hn::Load(d, values); - } - - FloatVec4::FloatVec4(f32 a, f32 b, f32 c, f32 d) - { - const Tag data; - alignas(16) f32 temp[4] = {a, b, c, d}; - m_data = hn::Load(data, temp); - } - - auto FloatVec4::operator+(const FloatVec4 &other) const -> FloatVec4 - { - return FloatVec4(hn::Add(m_data, other.m_data)); - } - - auto FloatVec4::operator-(const FloatVec4 &other) const -> FloatVec4 - { - return FloatVec4(hn::Sub(m_data, other.m_data)); - } - - auto FloatVec4::operator*(const FloatVec4 &other) const -> FloatVec4 - { - return FloatVec4(hn::Mul(m_data, other.m_data)); - } - - auto FloatVec4::operator/(const FloatVec4 &other) const -> FloatVec4 - { - return FloatVec4(hn::Div(m_data, other.m_data)); - } - - auto FloatVec4::mult_add(const FloatVec4 &multiplier, const FloatVec4 &addend) const -> FloatVec4 - { - return FloatVec4(hn::MulAdd(m_data, multiplier.m_data, addend.m_data)); - } - - auto FloatVec4::clamp(f32 min, f32 max) const -> FloatVec4 - { - const Tag d; - auto vMin = hn::Set(d, min); - auto vMax = hn::Set(d, max); - return FloatVec4(hn::Min(hn::Max(m_data, vMin), vMax)); - } - - auto FloatVec4::sqrt() const -> FloatVec4 - { - return FloatVec4(hn::Sqrt(m_data)); - } - - auto FloatVec4::rsqrt() const -> FloatVec4 - { - return FloatVec4(hn::ApproximateReciprocalSqrt(m_data)); - } - - auto FloatVec4::abs() const -> FloatVec4 - { - return FloatVec4(hn::Abs(m_data)); - } - - auto FloatVec4::dot(const FloatVec4 &other) const -> f32 - { - const Tag d; - auto vMul = hn::Mul(m_data, other.m_data); - return hn::ReduceSum(d, vMul); - } - - auto FloatVec4::normalize() const -> FloatVec4 - { - const Tag d; - auto vMul = hn::Mul(m_data, m_data); - auto vLenSq = hn::SumOfLanes(d, vMul); - auto vInvLen = hn::ApproximateReciprocalSqrt(vLenSq); - return FloatVec4(hn::Mul(m_data, vInvLen)); - } - - auto FloatVec4::store(f32 *values) -> void - { - const Tag d; - hn::Store(m_data, d, values); - } - - auto FloatVec4::load(const f32 *values) -> FloatVec4 - { - const Tag d; - return FloatVec4(hn::Load(d, values)); - } -} -``` ---- END FILE: IACore/SIMD.hpp --- - ---- START FILE: IACore/IACore.hpp --- -``` - -#pragma once - -#include -#include - -#define IACORE_MAIN() \ - auto _app_entry(const IACore::Vec &args) -> IACore::Result; \ - auto main(int argc, char *argv[]) -> int \ - { \ - IACore::i32 exit_code = 0; \ - IACore::initialize(); \ - IACore::Vec args; \ - args.reserve(static_cast(argc)); \ - for (int i = 0; i < argc; ++i) \ - { \ - args.push_back(argv[i]); \ - } \ - const auto result = _app_entry(args); \ - if (!result) \ - { \ - IACore::Logger::error("Application exited with an error: '{}'.", result.error()); \ - exit_code = -20; \ - } \ - else \ - { \ - exit_code = *result; \ - if (exit_code == 0) \ - { \ - IACore::Logger::info("Application exited successfully."); \ - } \ - else \ - { \ - IACore::Logger::error("Application exited with error code: {}.", exit_code); \ - } \ - } \ - IACore::terminate(); \ - return exit_code; \ - } \ - auto _app_entry(const IACore::Vec &args) -> IACore::Result - -namespace IACore -{ - auto initialize() -> void; - - auto terminate() -> void; - - auto is_initialized() -> bool; - - auto is_main_thread() -> bool; -} -``` ---- END FILE: IACore/IACore.hpp --- - ---- START FILE: IACore/CLI.hpp --- -``` - -#pragma once - -#include - -namespace IACore { -class CLIParser { - - -public: - CLIParser(Span args); - ~CLIParser() = default; - -public: - [[nodiscard]] auto remaining() const -> bool { - return m_current_arg < m_arg_list.end(); - } - - [[nodiscard]] auto peek() const -> StringView { - if (!remaining()) - return ""; - return *m_current_arg; - } - - auto next() -> StringView { - if (!remaining()) - return ""; - return *m_current_arg++; - } - - auto consume(const StringView &expected) -> bool { - if (peek() == expected) { - next(); - return true; - } - return false; - } - -private: - const Span m_arg_list; - Span::const_iterator m_current_arg; -}; -} -``` ---- END FILE: IACore/CLI.hpp --- - ---- START FILE: IACore/Logger.hpp --- -``` -#pragma once - -#include - -#define IA_LOG_SET_FILE(path) IACore::Logger::enable_logging_to_disk(path) -#define IA_LOG_SET_LEVEL(level) \ - IACore::Logger::set_log_level(IACore::Logger::LogLevel::level) - -#define IA_LOG_TRACE(...) IACore::Logger::trace(__VA_ARGS__) -#define IA_LOG_DEBUG(...) IACore::Logger::debug(__VA_ARGS__) -#define IA_LOG_INFO(...) IACore::Logger::info(__VA_ARGS__) -#define IA_LOG_WARN(...) IACore::Logger::warn(__VA_ARGS__) -#define IA_LOG_ERROR(...) IACore::Logger::error(__VA_ARGS__) - -namespace IACore { -class Logger { -public: - enum class LogLevel { Trace, Debug, Info, Warn, Error }; - -public: - static auto enable_logging_to_disk(const char *file_path) -> Result; - static auto set_log_level(LogLevel log_level) -> void; - - template - static auto trace(std::format_string fmt, Args &&...args) -> void { - log_trace(std::vformat(fmt.get(), std::make_format_args(args...))); - } - - template - static auto debug(std::format_string fmt, Args &&...args) -> void { - log_debug(std::vformat(fmt.get(), std::make_format_args(args...))); - } - - template - static auto info(std::format_string fmt, Args &&...args) -> void { - log_info(std::vformat(fmt.get(), std::make_format_args(args...))); - } - - template - static auto warn(std::format_string fmt, Args &&...args) -> void { - log_warn(std::vformat(fmt.get(), std::make_format_args(args...))); - } - - template - static auto error(std::format_string fmt, Args &&...args) -> void { - log_error(std::vformat(fmt.get(), std::make_format_args(args...))); - } - - static auto flush_logs() -> void; - -private: -#if IA_DISABLE_LOGGING > 0 - static auto log_trace(String &&msg) -> void { UNUSED(msg); } - - static auto log_debug(String &&msg) -> void { UNUSED(msg); } - - static auto log_info(String &&msg) -> void { UNUSED(msg); } - - static auto log_warn(String &&msg) -> void { UNUSED(msg); } - - static auto log_error(String &&msg) -> void { UNUSED(msg); } -#else - static auto log_trace(String &&msg) -> void { - if (m_log_level <= LogLevel::Trace) - log_internal(console::RESET, "TRACE", std::move(msg)); - } - - static auto log_debug(String &&msg) -> void { - if (m_log_level <= LogLevel::Debug) - log_internal(console::CYAN, "DEBUG", std::move(msg)); - } - - static auto log_info(String &&msg) -> void { - if (m_log_level <= LogLevel::Info) - log_internal(console::GREEN, "INFO", std::move(msg)); - } - - static auto log_warn(String &&msg) -> void { - if (m_log_level <= LogLevel::Warn) - log_internal(console::YELLOW, "WARN", std::move(msg)); - } - - static auto log_error(String &&msg) -> void { - if (m_log_level <= LogLevel::Error) - log_internal(console::RED, "ERROR", std::move(msg)); - } -#endif - - static auto log_internal(const char *prefix, const char *tag, String &&msg) - -> void; - -private: - static LogLevel m_log_level; - static std::ofstream m_log_file; - - static auto initialize() -> void; - static auto terminate() -> void; - - friend void initialize(); - friend void terminate(); -}; -} -``` ---- END FILE: IACore/Logger.hpp --- - ---- START FILE: IACore/PCH.hpp --- -``` - -#pragma once - -#if defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64) -#define IA_ARCH_X64 1 -#elif defined(__aarch64__) || defined(_M_ARM64) -#define IA_ARCH_ARM64 1 -#elif defined(__wasm__) || defined(__wasm32__) || defined(__wasm64__) -#define IA_ARCH_WASM 1 -#else -#error "IACore: Unsupported Architecture." -#endif - -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) -#define IA_PLATFORM_WINDOWS 1 -#elif __APPLE__ -#include -#define IA_PLATFORM_APPLE 1 -#define IA_PLATFORM_UNIX 1 -#elif __linux__ -#define IA_PLATFORM_LINUX 1 -#define IA_PLATFORM_UNIX 1 -#elif __wasm__ -#define IA_PLATFORM_WASM 1 -#else -#error "IACore: Unsupported Platform." -#endif - -#if IA_PLATFORM_WINDOWS -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include -#elif IA_PLATFORM_UNIX -#include -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace IACore { - -using u8 = std::uint8_t; -using u16 = std::uint16_t; -using u32 = std::uint32_t; -using u64 = std::uint64_t; - -using i8 = std::int8_t; -using i16 = std::int16_t; -using i32 = std::int32_t; -using i64 = std::int64_t; - -using f32 = float; -using f64 = double; - -using usize = std::size_t; -using isize = std::ptrdiff_t; - -namespace env { -#if defined(NDEBUG) -constexpr bool is_debug = false; -constexpr bool is_release = true; -#else -constexpr bool is_debug = true; -constexpr bool is_release = false; -#endif - -#if IA_PLATFORM_WINDOWS -constexpr bool is_windows = true; -constexpr bool is_unix = false; -#else -constexpr bool is_windows = false; -constexpr bool is_unix = true; -#endif - -constexpr usize max_path_len = 4096; -} -template using Box = std::unique_ptr; - -template using Arc = std::shared_ptr; - -template using Weak = std::weak_ptr; - -template -[[nodiscard]] inline auto make_box(Args &&...args) -> Box { - return std::make_unique(std::forward(args)...); -} - -template -[[nodiscard]] inline auto make_arc(Args &&...args) -> Arc { - return std::make_shared(std::forward(args)...); -} - -template -using Result = tl::expected; - -template [[nodiscard]] inline auto fail(E &&error) { - return tl::make_unexpected(std::forward(error)); -} - -template -[[nodiscard]] inline auto fail(std::format_string fmt, - Args &&...args) { - return tl::make_unexpected(std::format(fmt, std::forward(args)...)); -} - -template using Option = std::optional; -template using Vec = std::vector; -template using Span = std::span; -template using Pair = std::pair; -template -using HashMap = ankerl::unordered_dense::map; -template using HashSet = ankerl::unordered_dense::set; - -using String = std::string; -using Path = std::filesystem::path; -using StringView = std::string_view; - - -[[noreturn]] inline void -panic(const std::string &msg, - const std::source_location loc = std::source_location::current()) { - std::cerr << "\n[IA_PANIC] " << msg << "\n At: " << loc.file_name() - << ":" << loc.line() << "\n"; - std::abort(); -} - -inline void -ensure(bool condition, const std::string &msg, - const std::source_location loc = std::source_location::current()) { - if (env::is_debug && !condition) { - std::cerr << "\n[assert] " << msg << "\n At: " << loc.file_name() - << ":" << loc.line() << "\n"; - std::abort(); - } -} - -struct Version { - u32 major = 0; - u32 minor = 0; - u32 patch = 0; - - constexpr auto to_u64() const -> u64 { - return (static_cast(major) << 40) | (static_cast(minor) << 16) | - (static_cast(patch)); - } -}; - -namespace console { -constexpr const char *RESET = "\033[0m"; -constexpr const char *RED = "\033[31m"; -constexpr const char *GREEN = "\033[32m"; -constexpr const char *YELLOW = "\033[33m"; -constexpr const char *BLUE = "\033[34m"; -constexpr const char *MAGENTA = "\033[35m"; -constexpr const char *CYAN = "\033[36m"; -} -} - -#define IA_TRY_PURE(expr) \ - { \ - auto _res = expr; \ - if (!_res) { \ - return fail(std::move(_res.error())); \ - } \ - } - -#define IA_TRY(lhs, expr) \ - { \ - auto _res = expr; \ - if (!_res) { \ - return fail(std::move(_res.error())); \ - } \ - lhs = std::move(*_res); \ - } - -#define IA_NODISCARD [[nodiscard]] -#define IA_UNUSED [[maybe_unused]] -``` ---- END FILE: IACore/PCH.hpp --- - ---- START FILE: IACore/JSON.hpp --- -``` - -#pragma once - -#include - -#include -#include -#include - -namespace IACore { -class JsonDocument { -public: - JsonDocument(JsonDocument &&) noexcept = default; - JsonDocument &operator=(JsonDocument &&) noexcept = default; - JsonDocument(const JsonDocument &) = delete; - JsonDocument &operator=(const JsonDocument &) = delete; - - [[nodiscard]] - auto root() const noexcept -> simdjson::dom::element { - return m_root; - } - -private: - friend class Json; - - JsonDocument(Box p, simdjson::dom::element r) - : m_parser(std::move(p)), m_root(r) {} - - Box m_parser; - simdjson::dom::element m_root; -}; - -class Json { -private: - static constexpr auto GLAZE_OPTS = glz::opts{.error_on_unknown_keys = false}; - -public: - static auto parse(const String &json_str) -> Result; - static auto encode(const nlohmann::json &data) -> String; - - static auto parse_read_only(const String &json_str) -> Result; - - template - static auto parse_to_struct(const String &json_str) -> Result; - - template - static auto encode_struct(const T &data) -> Result; -}; - - -inline auto Json::parse(const String &json_str) -> Result { - const auto res = nlohmann::json::parse(json_str, nullptr, false, true); - - if (res.is_discarded()) { - return fail("Failed to parse JSON (Invalid Syntax)"); - } - return res; -} - -inline auto Json::parse_read_only(const String &json_str) - -> Result { - auto parser = make_box(); - - simdjson::dom::element root; - - simdjson::error_code error = parser->parse(json_str).get(root); - - if (error) { - return fail("JSON Error: {}", simdjson::error_message(error)); - } - - return JsonDocument(std::move(parser), root); -} - -inline auto Json::encode(const nlohmann::json &data) -> String { - return data.dump(); -} - -template -inline auto Json::parse_to_struct(const String &json_str) -> Result { - T result{}; - const auto err = glz::read_json(result, json_str); - - if (err) { - return fail("JSON Struct Parse Error: {}", - glz::format_error(err, json_str)); - } - return result; -} - -template -inline auto Json::encode_struct(const T &data) -> Result { - String result; - const auto err = glz::write_json(data, result); - - if (err) { - return fail("JSON Struct Encode Error"); - } - return result; -} -} -``` ---- END FILE: IACore/JSON.hpp --- - ---- START FILE: IACore/ADT/RingBuffer.hpp --- -``` - -#pragma once - -#include -#include - -namespace IACore { -class RingBufferView { -public: - static constexpr u16 PACKET_ID_SKIP = 0; - - struct ControlBlock { - struct alignas(64) { - std::atomic write_offset{0}; - } producer; - - struct alignas(64) { - std::atomic read_offset{0}; - u32 capacity{0}; - } consumer; - }; - - static_assert(offsetof(ControlBlock, consumer) == 64, - "False sharing detected in ControlBlock"); - - struct PacketHeader { - PacketHeader() : id(0), payload_size(0) {} - - PacketHeader(u16 id) : id(id), payload_size(0) {} - - PacketHeader(u16 id, u16 payload_size) - : id(id), payload_size(payload_size) {} - - u16 id{}; - u16 payload_size{}; - }; - -public: - static auto default_instance() -> RingBufferView; - - static auto create(Span buffer, bool is_owner) -> Result; - static auto create(ControlBlock *control_block, Span buffer, - bool is_owner) -> Result; - - auto pop(PacketHeader &out_header, Span out_buffer) - -> Result>; - - auto push(u16 packet_id, Span data) -> Result; - - auto get_control_block() -> ControlBlock *; - - [[nodiscard]] auto is_valid() const -> bool; - -protected: - RingBufferView(Span buffer, bool is_owner); - RingBufferView(ControlBlock *control_block, Span buffer, bool is_owner); - -private: - u8 *m_data_ptr{}; - u32 m_capacity{}; - ControlBlock *m_control_block{}; - -private: - auto write_wrapped(u32 offset, const void *data, u32 size) -> void; - auto read_wrapped(u32 offset, void *out_data, u32 size) -> void; -}; - - -inline auto RingBufferView::default_instance() -> RingBufferView { - return RingBufferView(nullptr, {}, false); -} - -inline auto RingBufferView::create(Span buffer, bool is_owner) - -> Result { - if (buffer.size() <= sizeof(ControlBlock)) { - return fail("Buffer too small for ControlBlock"); - } - - if (!is_owner) { - auto *cb = reinterpret_cast(buffer.data()); - u32 capacity = static_cast(buffer.size()) - sizeof(ControlBlock); - if (cb->consumer.capacity != capacity) { - return fail("Capacity mismatch"); - } - } - - return RingBufferView(buffer, is_owner); -} - -inline auto RingBufferView::create(ControlBlock *control_block, Span buffer, - bool is_owner) -> Result { - if (control_block == nullptr) { - return fail("ControlBlock is null"); - } - if (buffer.empty()) { - return fail("Buffer is empty"); - } - - return RingBufferView(control_block, buffer, is_owner); -} - -inline RingBufferView::RingBufferView(Span buffer, bool is_owner) { - m_control_block = reinterpret_cast(buffer.data()); - m_data_ptr = buffer.data() + sizeof(ControlBlock); - - m_capacity = static_cast(buffer.size()) - sizeof(ControlBlock); - - if (is_owner) { - m_control_block->consumer.capacity = m_capacity; - m_control_block->producer.write_offset.store(0, std::memory_order_release); - m_control_block->consumer.read_offset.store(0, std::memory_order_release); - } -} - -inline RingBufferView::RingBufferView(ControlBlock *control_block, - Span buffer, bool is_owner) { - m_control_block = control_block; - m_data_ptr = buffer.data(); - m_capacity = static_cast(buffer.size()); - - if (is_owner) { - m_control_block->consumer.capacity = m_capacity; - m_control_block->producer.write_offset.store(0, std::memory_order_release); - m_control_block->consumer.read_offset.store(0, std::memory_order_release); - } -} - -inline auto RingBufferView::pop(PacketHeader &out_header, Span out_buffer) - -> Result> { - u32 write = - m_control_block->producer.write_offset.load(std::memory_order_acquire); - u32 read = - m_control_block->consumer.read_offset.load(std::memory_order_relaxed); - u32 cap = m_capacity; - - if (read == write) { - return std::nullopt; - } - - read_wrapped(read, &out_header, sizeof(PacketHeader)); - - if (out_header.payload_size > out_buffer.size()) { - return fail("Buffer too small: needed {}, provided {}", - out_header.payload_size, out_buffer.size()); - } - - if (out_header.payload_size > 0) { - u32 data_read_offset = (read + sizeof(PacketHeader)) % cap; - read_wrapped(data_read_offset, out_buffer.data(), out_header.payload_size); - } - - u32 new_read_offset = - (read + sizeof(PacketHeader) + out_header.payload_size) % cap; - m_control_block->consumer.read_offset.store(new_read_offset, - std::memory_order_release); - - return std::make_optional(static_cast(out_header.payload_size)); -} - -inline auto RingBufferView::push(u16 packet_id, Span data) - -> Result { - if (data.size() > std::numeric_limits::max()) { - return fail("Data size exceeds u16 limit"); - } - - const u32 total_size = sizeof(PacketHeader) + static_cast(data.size()); - - u32 read = - m_control_block->consumer.read_offset.load(std::memory_order_acquire); - u32 write = - m_control_block->producer.write_offset.load(std::memory_order_relaxed); - u32 cap = m_capacity; - - u32 free_space = - (read <= write) ? (m_capacity - write) + read : (read - write); - - if (free_space <= total_size) { - return fail("RingBuffer full"); - } - - PacketHeader header{packet_id, static_cast(data.size())}; - write_wrapped(write, &header, sizeof(PacketHeader)); - - u32 data_write_offset = (write + sizeof(PacketHeader)) % cap; - - if (!data.empty()) { - write_wrapped(data_write_offset, data.data(), - static_cast(data.size())); - } - - u32 new_write_offset = (data_write_offset + data.size()) % cap; - m_control_block->producer.write_offset.store(new_write_offset, - std::memory_order_release); - - return {}; -} - -inline auto RingBufferView::get_control_block() -> ControlBlock * { - return m_control_block; -} - -inline auto RingBufferView::write_wrapped(u32 offset, const void *data, - u32 size) -> void { - if (offset + size <= m_capacity) { - std::memcpy(m_data_ptr + offset, data, size); - } else { - u32 first_chunk = m_capacity - offset; - u32 second_chunk = size - first_chunk; - - const u8 *src = static_cast(data); - - std::memcpy(m_data_ptr + offset, src, first_chunk); - std::memcpy(m_data_ptr, src + first_chunk, second_chunk); - } -} - -inline auto RingBufferView::read_wrapped(u32 offset, void *out_data, u32 size) - -> void { - if (offset + size <= m_capacity) { - std::memcpy(out_data, m_data_ptr + offset, size); - } else { - u32 first_chunk = m_capacity - offset; - u32 second_chunk = size - first_chunk; - - u8 *dst = static_cast(out_data); - - std::memcpy(dst, m_data_ptr + offset, first_chunk); - std::memcpy(dst + first_chunk, m_data_ptr, second_chunk); - } -} - -[[nodiscard]] inline auto RingBufferView::is_valid() const -> bool { - return m_control_block && m_data_ptr && m_capacity; -} - -} -``` ---- END FILE: IACore/ADT/RingBuffer.hpp --- - ---- START FILE: IACore/Http/Client.hpp --- -``` - -#pragma once - -#include -#include - -namespace IACore { -class HttpClient : public HttpCommon { -public: - static auto create(const String &host) -> Result>; - - ~HttpClient(); - - HttpClient(HttpClient &&) = default; - HttpClient(const HttpClient &) = delete; - auto operator=(HttpClient &&) -> HttpClient & = default; - auto operator=(const HttpClient &) -> HttpClient & = delete; - -public: - auto raw_get(const String &path, Span headers, - const char *default_content_type = - "application/x-www-form-urlencoded") -> Result; - - auto raw_post( - const String &path, Span headers, const String &body, - const char *default_content_type = "application/x-www-form-urlencoded") - -> Result; - - template - auto json_get(const String &path, Span headers) - -> Result; - - template - auto json_post(const String &path, Span headers, - const PayloadType &body) -> Result; - - void enable_certificate_verification(); - void disable_certificate_verification(); - -public: - auto last_response_code() -> EResponseCode { return m_last_response_code; } - -private: - httplib::Client m_client; - EResponseCode m_last_response_code; - -private: - auto preprocess_response(const String &response) -> String; - -protected: - explicit HttpClient(httplib::Client &&client); -}; - -template -auto HttpClient::json_get(const String &path, Span headers) - -> Result { - String raw_response; - IA_TRY(raw_response, raw_get(path, headers, "application/json")); - - if (last_response_code() != EResponseCode::OK) { - return fail("Server responded with code {}", - static_cast(last_response_code())); - } - return Json::parse_to_struct(raw_response); -} - -template -auto HttpClient::json_post(const String &path, Span headers, - const PayloadType &body) -> Result { - String encoded_body; - IA_TRY(encoded_body, Json::encode_struct(body)); - - String raw_response; - IA_TRY(raw_response, - raw_post(path, headers, encoded_body, "application/json")); - - if (last_response_code() != EResponseCode::OK) { - return fail("Server responded with code {}", - static_cast(last_response_code())); - } - return Json::parse_to_struct(raw_response); -} -} -``` ---- END FILE: IACore/Http/Client.hpp --- - ---- START FILE: IACore/Http/Common.hpp --- -``` - -#pragma once - -#include - -#include - -namespace IACore { -class HttpCommon { -public: - enum class EHeaderType { - ACCEPT, - ACCEPT_CHARSET, - ACCEPT_ENCODING, - ACCEPT_LANGUAGE, - AUTHORIZATION, - CACHE_CONTROL, - CONNECTION, - CONTENT_LENGTH, - CONTENT_TYPE, - COOKIE, - DATE, - EXPECT, - HOST, - IF_MATCH, - IF_MODIFIED_SINCE, - IF_NONE_MATCH, - ORIGIN, - PRAGMA, - PROXY_AUTHORIZATION, - RANGE, - REFERER, - TE, - UPGRADE, - USER_AGENT, - VIA, - WARNING - }; - - enum class EResponseCode : i32 { - CONTINUE = 100, - SWITCHING_PROTOCOLS = 101, - PROCESSING = 102, - EARLY_HINTS = 103, - - OK = 200, - CREATED = 201, - ACCEPTED = 202, - NON_AUTHORITATIVE_INFORMATION = 203, - NO_CONTENT = 204, - RESET_CONTENT = 205, - PARTIAL_CONTENT = 206, - MULTI_STATUS = 207, - ALREADY_REPORTED = 208, - IM_USED = 226, - - MULTIPLE_CHOICES = 300, - MOVED_PERMANENTLY = 301, - FOUND = 302, - SEE_OTHER = 303, - NOT_MODIFIED = 304, - USE_PROXY = 305, - TEMPORARY_REDIRECT = 307, - PERMANENT_REDIRECT = 308, - - BAD_REQUEST = 400, - UNAUTHORIZED = 401, - PAYMENT_REQUIRED = 402, - FORBIDDEN = 403, - NOT_FOUND = 404, - METHOD_NOT_ALLOWED = 405, - NOT_ACCEPTABLE = 406, - PROXY_AUTHENTICATION_REQUIRED = 407, - REQUEST_TIMEOUT = 408, - CONFLICT = 409, - GONE = 410, - LENGTH_REQUIRED = 411, - PRECONDITION_FAILED = 412, - PAYLOAD_TOO_LARGE = 413, - URI_TOO_LONG = 414, - UNSUPPORTED_MEDIA_TYPE = 415, - RANGE_NOT_SATISFIABLE = 416, - EXPECTATION_FAILED = 417, - IM_A_TEAPOT = 418, - MISDIRECTED_REQUEST = 421, - UNPROCESSABLE_ENTITY = 422, - LOCKED = 423, - FAILED_DEPENDENCY = 424, - TOO_EARLY = 425, - UPGRADE_REQUIRED = 426, - PRECONDITION_REQUIRED = 428, - TOO_MANY_REQUESTS = 429, - REQUEST_HEADER_FIELDS_TOO_LARGE = 431, - UNAVAILABLE_FOR_LEGAL_REASONS = 451, - - INTERNAL_SERVER_ERROR = 500, - NOT_IMPLEMENTED = 501, - BAD_GATEWAY = 502, - SERVICE_UNAVAILABLE = 503, - GATEWAY_TIMEOUT = 504, - HTTP_VERSION_NOT_SUPPORTED = 505, - VARIANT_ALSO_NEGOTIATES = 506, - INSUFFICIENT_STORAGE = 507, - LOOP_DETECTED = 508, - NOT_EXTENDED = 510, - NETWORK_AUTHENTICATION_REQUIRED = 511 - }; - - using Header = Pair; - - static auto url_encode(const String &value) -> String; - static auto url_decode(const String &value) -> String; - - static auto header_type_to_string(EHeaderType type) -> String; - - static inline auto create_header(EHeaderType key, const String &value) - -> Header; - static inline auto create_header(const String &key, const String &value) - -> Header; - - static auto is_success_response_code(EResponseCode code) -> bool; - -protected: - HttpCommon() = default; -}; - -auto HttpCommon::create_header(EHeaderType key, const String &value) - -> HttpCommon::Header { - return std::make_pair(header_type_to_string(key), value); -} - -auto HttpCommon::create_header(const String &key, const String &value) - -> HttpCommon::Header { - return std::make_pair(key, value); -} -} -``` ---- END FILE: IACore/Http/Common.hpp --- - ---- START FILE: IACore/Http/Server.hpp --- -``` - -#pragma once - -#include -#include -#include - -namespace IACore { -class HttpServer : public HttpCommon { -public: - struct Request { - String path; - String method; - String body; - HashMap headers; - HashMap params; HashMap path_params; - [[nodiscard]] auto get_header(const String &key) const -> String; - [[nodiscard]] auto get_param(const String &key) const -> String; - [[nodiscard]] auto get_path_param(const String &key) const -> String; - - [[nodiscard]] auto has_header(const String &key) const -> bool; - [[nodiscard]] auto has_param(const String &key) const -> bool; - [[nodiscard]] auto has_path_param(const String &key) const -> bool; - }; - - struct Response { - EResponseCode code = EResponseCode::OK; - String body; - HashMap headers; - String content_type = "text/plain"; - - void set_content(const String &content, const String &type); - void set_status(EResponseCode status_code); - void add_header(const String &key, const String &value); - }; - - using Handler = std::function; - -public: - static auto create() -> Result>; - - ~HttpServer(); - - HttpServer(HttpServer &&) = delete; - HttpServer(const HttpServer &) = delete; - auto operator=(HttpServer &&) -> HttpServer & = delete; - auto operator=(const HttpServer &) -> HttpServer & = delete; - - auto listen(const String &host, u32 port) -> Result; - void stop(); - auto is_running() const -> bool; - - void get(const String &pattern, Handler handler); - void post(const String &pattern, Handler handler); - void put(const String &pattern, Handler handler); - void del(const String &pattern, Handler handler); - void options(const String &pattern, Handler handler); - - template - void json_get(const String &pattern, - std::function(const Request &)> handler); - - template - void - json_post(const String &pattern, - std::function(const PayloadType &)> handler); - -protected: - HttpServer(); - -private: - httplib::Server m_server; - - void register_handler(const String &method, const String &pattern, - Handler handler); -}; - - -template -void HttpServer::json_get( - const String &pattern, - std::function(const Request &)> handler) { - get(pattern, [handler](const Request &req, Response &res) { - auto result = handler(req); - if (!result) { - res.set_status(EResponseCode::INTERNAL_SERVER_ERROR); - res.set_content(result.error(), "text/plain"); - return; - } - - auto json_res = Json::encode_struct(*result); - if (!json_res) { - res.set_status(EResponseCode::INTERNAL_SERVER_ERROR); - res.set_content("Failed to encode JSON response", "text/plain"); - return; - } - - res.set_status(EResponseCode::OK); - res.set_content(*json_res, "application/json"); - }); -} - -template -void HttpServer::json_post( - const String &pattern, - std::function(const PayloadType &)> handler) { - post(pattern, [handler](const Request &req, Response &res) { - auto payload = Json::parse_to_struct(req.body); - if (!payload) { - res.set_status(EResponseCode::BAD_REQUEST); - res.set_content("Invalid JSON Payload", "text/plain"); - return; - } - - auto result = handler(*payload); - if (!result) { - res.set_status(EResponseCode::INTERNAL_SERVER_ERROR); - res.set_content(result.error(), "text/plain"); - return; - } - - auto json_res = Json::encode_struct(*result); - if (!json_res) { - res.set_status(EResponseCode::INTERNAL_SERVER_ERROR); - res.set_content("Failed to encode JSON response", "text/plain"); - return; - } - - res.set_status(EResponseCode::OK); - res.set_content(*json_res, "application/json"); - }); -} - -} -``` ---- END FILE: IACore/Http/Server.hpp --- - diff --git a/Tests/Unit/Main.cpp b/Tests/Unit/Main.cpp index 896aba7..f00fa46 100644 --- a/Tests/Unit/Main.cpp +++ b/Tests/Unit/Main.cpp @@ -23,7 +23,7 @@ using namespace IACore; IACORE_MAIN() { (void)args; - IA_TRY_PURE(SocketOps::initialize()); + OX_TRY_PURE(SocketOps::initialize()); std::cout << console::GREEN diff --git a/Tests/Unit/ProcessOps.cpp b/Tests/Unit/ProcessOps.cpp index 7afeae2..b757543 100644 --- a/Tests/Unit/ProcessOps.cpp +++ b/Tests/Unit/ProcessOps.cpp @@ -192,7 +192,7 @@ auto test_multi_line() -> bool { bool found_a = false; bool found_b = false; - IA_UNUSED const auto res = + const auto res = ProcessOps::spawn_process_sync(cmd, arg, [&](StringView line) { line_count++; if (line.find("LineA") != String::npos) { @@ -202,6 +202,7 @@ auto test_multi_line() -> bool { found_b = true; } }); + IAT_CHECK(res.has_value()); IAT_CHECK(found_a); IAT_CHECK(found_b);