diff --git a/.clang-format b/.clang-format
index c354725..97a556b 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,13 +1,32 @@
---
-BasedOnStyle: Microsoft
-IndentWidth: 4
-SortIncludes: false
----
Language: Cpp
+Standard: c++20
+BasedOnStyle: Microsoft
+
+IndentWidth: 4
+TabWidth: 4
+UseTab: Never
+AccessModifierOffset: -4
+
IndentPPDirectives: AfterHash
+IndentRequiresClause: true
+BreakBeforeConceptDeclarations: true
+SpaceAfterTemplateKeyword: false
+
FixNamespaceComments: true
NamespaceIndentation: All
-SeparateDefinitionBlocks: Always
+CompactNamespaces: true
+
+AlignAfterOpenBracket: Align
+AlignOperands: Align
+AlignTrailingComments: true
+
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
-SpaceAfterTemplateKeyword: false
+SpaceBeforeAssignmentOperators: true
+SpaceInEmptyParentheses: false
+SpacesInAngles: false
+
+SeparateDefinitionBlocks: Always
+SortIncludes: false
+---
\ No newline at end of file
diff --git a/.clang-tidy b/.clang-tidy
index e58d809..85ea101 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,18 +1,76 @@
-Checks: 'readability-identifier-naming'
+Checks: >
+ -*,
+ readability-identifier-naming,
+ readability-const-return-type,
+ modernize-use-nodiscard,
+ modernize-use-override,
+ modernize-use-nullptr,
+ bugprone-use-after-move
CheckOptions:
- - key: readability-identifier-naming.MethodCase
- value: PascalCase
-
- - key: readability-identifier-naming.StructMemberCase
- value: lower_case
-
- - key: readability-identifier-naming.PrivateMemberCase
- value: camelCase
- - key: readability-identifier-naming.PrivateMemberPrefix
- value: m_
-
+ # ----------------------------------------------------------------------------
+ # Types (PascalCase) - Matches Rust structs/enums
+ # ----------------------------------------------------------------------------
- key: readability-identifier-naming.ClassCase
value: PascalCase
- key: readability-identifier-naming.StructCase
- value: PascalCase
\ No newline at end of file
+ value: PascalCase
+ - key: readability-identifier-naming.TypedefCase
+ value: PascalCase
+ - key: readability-identifier-naming.EnumCase
+ value: PascalCase
+ - key: readability-identifier-naming.TemplateParameterCase
+ value: PascalCase
+
+ # ----------------------------------------------------------------------------
+ # Functions & Methods (snake_case) - Matches Rust fn
+ # ----------------------------------------------------------------------------
+ - key: readability-identifier-naming.FunctionCase
+ value: lower_case
+ - key: readability-identifier-naming.MethodCase
+ value: lower_case
+ - key: readability-identifier-naming.ParameterCase
+ value: lower_case
+
+ # ----------------------------------------------------------------------------
+ # Variables (snake_case) - Matches Rust let
+ # ----------------------------------------------------------------------------
+ - key: readability-identifier-naming.VariableCase
+ value: lower_case
+ - key: readability-identifier-naming.LocalVariableCase
+ value: lower_case
+
+ # ----------------------------------------------------------------------------
+ # Members (m_snake_case for private, snake_case for public)
+ # ----------------------------------------------------------------------------
+ # Public struct members (like a Rust struct) -> x, y, width
+ - key: readability-identifier-naming.PublicMemberCase
+ value: lower_case
+
+ # Private/Protected class members -> m_parser, m_count
+ - key: readability-identifier-naming.PrivateMemberCase
+ value: lower_case
+ - key: readability-identifier-naming.PrivateMemberPrefix
+ value: m_
+ - key: readability-identifier-naming.ProtectedMemberCase
+ value: lower_case
+ - key: readability-identifier-naming.ProtectedMemberPrefix
+ value: m_
+
+ # ----------------------------------------------------------------------------
+ # Constants (SCREAMING_SNAKE_CASE)
+ # ----------------------------------------------------------------------------
+ - key: readability-identifier-naming.GlobalConstantCase
+ value: UPPER_CASE
+ - key: readability-identifier-naming.ConstexprVariableCase
+ value: UPPER_CASE
+ - key: readability-identifier-naming.EnumConstantCase
+ value: PascalCase
+ # Note: Rust uses PascalCase for Enum Variants (Option::None).
+ # Change to UPPER_CASE if you prefer C-style enums.
+
+ # ----------------------------------------------------------------------------
+ # Macros (SCREAMING_SNAKE_CASE)
+ # ----------------------------------------------------------------------------
+ - key: readability-identifier-naming.MacroDefinitionCase
+ value: UPPER_CASE
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8a6d833..d6c5868 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -37,5 +37,5 @@ if(IACore_BUILD_TESTS)
endif()
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Sandbox")
- add_subdirectory(Sandbox)
+ # add_subdirectory(Sandbox)
endif()
diff --git a/README.md b/README.md
index d7c59c6..c72244c 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,9 @@
# IACore (Independent Architecture Core)
+> [!WARNING]
+> A major rewrite is coming to IACore API and coding style on Jan 30th. While the functionality and classes you're familiar will remain the same, this rewrite will introduce breaking changes to the overall API and how IACore is used. This rewrite will align IACore API and coding style with a Rust-inspired modern, clean and a vastly improved maintainable alternative.
+
+
diff --git a/Src/IACore/CMakeLists.txt b/Src/IACore/CMakeLists.txt
index 40d56b0..279aa27 100644
--- a/Src/IACore/CMakeLists.txt
+++ b/Src/IACore/CMakeLists.txt
@@ -6,6 +6,7 @@ set(SRC_FILES
"imp/cpp/JSON.cpp"
"imp/cpp/IACore.cpp"
"imp/cpp/Logger.cpp"
+ "imp/cpp/Utils.cpp"
"imp/cpp/FileOps.cpp"
"imp/cpp/DataOps.cpp"
"imp/cpp/AsyncOps.cpp"
@@ -16,9 +17,9 @@ set(SRC_FILES
"imp/cpp/StreamReader.cpp"
"imp/cpp/StreamWriter.cpp"
- "imp/cpp/Http/Common.cpp"
- "imp/cpp/Http/Client.cpp"
- "imp/cpp/Http/Server.cpp"
+ #"imp/cpp/Http/Common.cpp"
+ #"imp/cpp/Http/Client.cpp"
+ #"imp/cpp/Http/Server.cpp"
)
add_library(IACore STATIC ${SRC_FILES})
diff --git a/Src/IACore/imp/cpp/AsyncOps.cpp b/Src/IACore/imp/cpp/AsyncOps.cpp
index 134f146..b8168b2 100644
--- a/Src/IACore/imp/cpp/AsyncOps.cpp
+++ b/Src/IACore/imp/cpp/AsyncOps.cpp
@@ -15,164 +15,170 @@
#include
-namespace IACore
-{
- Mutex AsyncOps::s_queueMutex;
- ConditionVariable AsyncOps::s_wakeCondition;
- Vector AsyncOps::s_scheduleWorkers;
- Deque AsyncOps::s_highPriorityQueue;
- Deque AsyncOps::s_normalPriorityQueue;
+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;
- VOID AsyncOps::RunTask(IN Function task)
- {
- JoiningThread(task).detach();
+auto AsyncOps::run_task(std::function task) -> void {
+ std::jthread(std::move(task)).detach();
+}
+
+auto AsyncOps::initialize_scheduler(u8 worker_count) -> Result {
+ if (worker_count == 0) {
+ const auto hw_concurrency = std::thread::hardware_concurrency();
+ u32 threads = 2;
+ if (hw_concurrency > 2) {
+ threads = hw_concurrency - 2;
}
- VOID AsyncOps::InitializeScheduler(IN UINT8 workerCount)
- {
- if (!workerCount)
- workerCount = std::max((UINT32) 2, std::thread::hardware_concurrency() - 2);
- for (UINT32 i = 0; i < workerCount; i++)
- s_scheduleWorkers.emplace_back(AsyncOps::ScheduleWorkerLoop, i + 1);
+ if (threads > 255) {
+ threads = 255;
}
+ worker_count = static_cast(threads);
+ }
- VOID AsyncOps::TerminateScheduler()
- {
- for (auto &w : s_scheduleWorkers)
- {
- w.request_stop();
+ for (u32 i = 0; i < worker_count; ++i) {
+ s_schedule_workers.emplace_back(schedule_worker_loop,
+ static_cast(i + 1));
+ }
+
+ return {};
+}
+
+auto AsyncOps::terminate_scheduler() -> void {
+ for (auto &worker : s_schedule_workers) {
+ worker.request_stop();
+ }
+
+ s_wake_condition.notify_all();
+
+ for (auto &worker : s_schedule_workers) {
+ if (worker.joinable()) {
+ worker.join();
+ }
+ }
+
+ s_schedule_workers.clear();
+}
+
+auto AsyncOps::schedule_task(std::function task,
+ TaskTag tag, Schedule *schedule, Priority 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);
+ if (priority == Priority::High) {
+ s_high_priority_queue.emplace_back(
+ ScheduledTask{tag, schedule, std::move(task)});
+ } else {
+ s_normal_priority_queue.emplace_back(
+ ScheduledTask{tag, schedule, std::move(task)});
+ }
+ }
+ s_wake_condition.notify_one();
+}
+
+auto AsyncOps::cancel_tasks_of_tag(TaskTag tag) -> void {
+ std::lock_guard lock(s_queue_mutex);
+
+ auto cancel_from_queue = [&](std::deque &queue) {
+ for (auto it = queue.begin(); it != queue.end(); /* no increment */) {
+ if (it->tag == tag) {
+ if (it->schedule_handle->counter.fetch_sub(1) == 1) {
+ it->schedule_handle->counter.notify_all();
}
-
- s_wakeCondition.notify_all();
-
- for (auto &w : s_scheduleWorkers)
- {
- if (w.joinable())
- {
- w.join();
- }
- }
-
- s_scheduleWorkers.clear();
+ it = queue.erase(it);
+ } else {
+ ++it;
+ }
}
+ };
- VOID AsyncOps::ScheduleTask(IN Function task, IN TaskTag tag, IN Schedule *schedule,
- IN Priority priority)
+ cancel_from_queue(s_high_priority_queue);
+ cancel_from_queue(s_normal_priority_queue);
+}
+
+auto AsyncOps::wait_for_schedule_completion(Schedule *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;
{
- IA_ASSERT(s_scheduleWorkers.size() && "Scheduler must be initialized before calling this function");
-
- schedule->Counter.fetch_add(1);
- {
- ScopedLock lock(s_queueMutex);
- if (priority == Priority::High)
- s_highPriorityQueue.emplace_back(ScheduledTask{tag, schedule, IA_MOVE(task)});
- else
- s_normalPriorityQueue.emplace_back(ScheduledTask{tag, schedule, IA_MOVE(task)});
- }
- s_wakeCondition.notify_one();
+ std::unique_lock lock(s_queue_mutex);
+ if (!s_high_priority_queue.empty()) {
+ task = std::move(s_high_priority_queue.front());
+ s_high_priority_queue.pop_front();
+ found_task = true;
+ } else if (!s_normal_priority_queue.empty()) {
+ task = std::move(s_normal_priority_queue.front());
+ s_normal_priority_queue.pop_front();
+ found_task = true;
+ }
}
- VOID AsyncOps::CancelTasksOfTag(IN TaskTag tag)
+ if (found_task) {
+ task.task(MAIN_THREAD_WORKER_ID);
+ if (task.schedule_handle->counter.fetch_sub(1) == 1) {
+ task.schedule_handle->counter.notify_all();
+ }
+ } else {
+ const auto current_val = schedule->counter.load();
+ if (current_val > 0) {
+ schedule->counter.wait(current_val);
+ }
+ }
+ }
+}
+
+auto AsyncOps::get_worker_count() -> WorkerId {
+ // +1 for MainThread (Work Stealing)
+ return static_cast(s_schedule_workers.size() + 1);
+}
+
+auto AsyncOps::schedule_worker_loop(std::stop_token stop_token,
+ WorkerId worker_id) -> void {
+ while (!stop_token.stop_requested()) {
+ ScheduledTask task;
+ bool found_task = false;
{
- ScopedLock lock(s_queueMutex);
+ std::unique_lock lock(s_queue_mutex);
- auto cancelFromQueue = [&](Deque &queue) {
- for (auto it = queue.begin(); it != queue.end(); /* no increment here */)
- {
- if (it->Tag == tag)
- {
- if (it->ScheduleHandle->Counter.fetch_sub(1) == 1)
- it->ScheduleHandle->Counter.notify_all();
+ s_wake_condition.wait(lock, [&stop_token] {
+ return !s_high_priority_queue.empty() ||
+ !s_normal_priority_queue.empty() || stop_token.stop_requested();
+ });
- it = queue.erase(it);
- }
- else
- ++it;
- }
- };
+ if (stop_token.stop_requested() && s_high_priority_queue.empty() &&
+ s_normal_priority_queue.empty()) {
+ return;
+ }
- cancelFromQueue(s_highPriorityQueue);
- cancelFromQueue(s_normalPriorityQueue);
+ if (!s_high_priority_queue.empty()) {
+ task = std::move(s_high_priority_queue.front());
+ s_high_priority_queue.pop_front();
+ found_task = true;
+ } else if (!s_normal_priority_queue.empty()) {
+ task = std::move(s_normal_priority_queue.front());
+ s_normal_priority_queue.pop_front();
+ found_task = true;
+ }
}
- VOID AsyncOps::WaitForScheduleCompletion(IN Schedule *schedule)
- {
- IA_ASSERT(s_scheduleWorkers.size() && "Scheduler must be initialized before calling this function");
-
- while (schedule->Counter.load() > 0)
- {
- ScheduledTask task;
- BOOL foundTask{FALSE};
- {
- UniqueLock lock(s_queueMutex);
- if (!s_highPriorityQueue.empty())
- {
- task = IA_MOVE(s_highPriorityQueue.front());
- s_highPriorityQueue.pop_front();
- foundTask = TRUE;
- }
- else if (!s_normalPriorityQueue.empty())
- {
- task = IA_MOVE(s_normalPriorityQueue.front());
- s_normalPriorityQueue.pop_front();
- foundTask = TRUE;
- }
- }
- if (foundTask)
- {
- task.Task(MainThreadWorkerID);
- if (task.ScheduleHandle->Counter.fetch_sub(1) == 1)
- task.ScheduleHandle->Counter.notify_all();
- }
- else
- {
- auto currentVal = schedule->Counter.load();
- if (currentVal > 0)
- schedule->Counter.wait(currentVal);
- }
- }
+ if (found_task) {
+ task.task(worker_id);
+ if (task.schedule_handle->counter.fetch_sub(1) == 1) {
+ task.schedule_handle->counter.notify_all();
+ }
}
+ }
+}
- AsyncOps::WorkerID AsyncOps::GetWorkerCount()
- {
- return static_cast(s_scheduleWorkers.size() + 1); // +1 for MainThread (Work Stealing)
- }
-
- VOID AsyncOps::ScheduleWorkerLoop(IN StopToken stopToken, IN WorkerID workerID)
- {
- while (!stopToken.stop_requested())
- {
- ScheduledTask task;
- BOOL foundTask{FALSE};
- {
- UniqueLock lock(s_queueMutex);
-
- s_wakeCondition.wait(lock, [&stopToken] {
- return !s_highPriorityQueue.empty() || !s_normalPriorityQueue.empty() || stopToken.stop_requested();
- });
-
- if (stopToken.stop_requested() && s_highPriorityQueue.empty() && s_normalPriorityQueue.empty())
- return;
-
- if (!s_highPriorityQueue.empty())
- {
- task = IA_MOVE(s_highPriorityQueue.front());
- s_highPriorityQueue.pop_front();
- foundTask = TRUE;
- }
- else if (!s_normalPriorityQueue.empty())
- {
- task = IA_MOVE(s_normalPriorityQueue.front());
- s_normalPriorityQueue.pop_front();
- foundTask = TRUE;
- }
- }
- if (foundTask)
- {
- task.Task(workerID);
- if (task.ScheduleHandle->Counter.fetch_sub(1) == 1)
- task.ScheduleHandle->Counter.notify_all();
- }
- }
- }
} // namespace IACore
\ No newline at end of file
diff --git a/Src/IACore/imp/cpp/CLI.cpp b/Src/IACore/imp/cpp/CLI.cpp
index a5db98c..33e1675 100644
--- a/Src/IACore/imp/cpp/CLI.cpp
+++ b/Src/IACore/imp/cpp/CLI.cpp
@@ -17,9 +17,9 @@
namespace IACore
{
- CLIParser::CLIParser(IN Span args) : m_argList(args)
+ CLIParser::CLIParser(Span args) : m_argList(args)
{
- IA_RELEASE_ASSERT(args.size());
+ assert(args.size());
m_currentArg = m_argList.begin();
diff --git a/Src/IACore/imp/cpp/DataOps.cpp b/Src/IACore/imp/cpp/DataOps.cpp
index 66c4b67..f24c366 100644
--- a/Src/IACore/imp/cpp/DataOps.cpp
+++ b/Src/IACore/imp/cpp/DataOps.cpp
@@ -16,457 +16,458 @@
#include
#include
+#include
+#include
#include
#include
-namespace IACore
-{
- template INLINE T ReadUnaligned(IN PCUINT8 ptr)
- {
- T v;
- std::memcpy(&v, ptr, sizeof(T));
- return v;
- }
-
- struct CRC32Tables
- {
- UINT32 table[8][256] = {};
-
- CONSTEVAL CRC32Tables()
- {
- CONSTEXPR UINT32 t = 0x82F63B78;
-
- for (UINT32 i = 0; i < 256; i++)
- {
- UINT32 crc = i;
- for (int j = 0; j < 8; j++)
- crc = (crc >> 1) ^ ((crc & 1) ? t : 0);
- table[0][i] = crc;
- }
-
- for (int i = 0; i < 256; i++)
- {
- for (int slice = 1; slice < 8; slice++)
- {
- UINT32 prev = table[slice - 1][i];
- table[slice][i] = (prev >> 8) ^ table[0][prev & 0xFF];
- }
- }
- }
- };
-
- STATIC CONSTEXPR CRC32Tables CRC32_TABLES{};
-
#if IA_ARCH_X64
- INLINE UINT32 CRC32_x64_HW(IN Span data)
- {
- CONST UINT8 *p = data.data();
-
- UINT32 crc = 0xFFFFFFFF;
- SIZE_T len = data.size();
-
- while (len >= 8)
- {
- UINT64 chunk = ReadUnaligned(p);
- crc = (UINT32) _mm_crc32_u64((UINT64) crc, chunk);
- p += 8;
- len -= 8;
- }
-
- while (len--)
- crc = _mm_crc32_u8(crc, *p++);
-
- return ~crc;
- }
+#include
#endif
#if IA_ARCH_ARM64
- __attribute__((target("+crc"))) INLINE UINT32 CRC32_ARM64_HW(IN Span data)
- {
- CONST UINT8 *p = data.data();
-
- UINT32 crc = 0xFFFFFFFF;
- SIZE_T len = data.size();
-
- while (len >= 8)
- {
- UINT64 chunk = ReadUnaligned(p);
- crc = __crc32cd(crc, chunk);
- p += 8;
- len -= 8;
- }
-
- while (len--)
- crc = __crc32cb(crc, *p++);
-
- return ~crc;
- }
+#include
#endif
- INLINE UINT32 CRC32_Software_Slice8(IN Span data)
- {
- CONST UINT8 *p = data.data();
- UINT32 crc = 0xFFFFFFFF;
- SIZE_T len = data.size();
+namespace IACore {
+template
+[[nodiscard]] inline auto read_unaligned(const u8 *ptr) -> T {
+ T v;
+ std::memcpy(&v, ptr, sizeof(T));
+ return v;
+}
- while (len >= 8)
- {
- UINT32 term1 = crc ^ ReadUnaligned(p);
- UINT32 term2 = ReadUnaligned(p + 4);
+struct Crc32Tables {
+ u32 table[8][256] = {};
- crc = CRC32_TABLES.table[7][term1 & 0xFF] ^ CRC32_TABLES.table[6][(term1 >> 8) & 0xFF] ^
- CRC32_TABLES.table[5][(term1 >> 16) & 0xFF] ^ CRC32_TABLES.table[4][(term1 >> 24)] ^
- CRC32_TABLES.table[3][term2 & 0xFF] ^ CRC32_TABLES.table[2][(term2 >> 8) & 0xFF] ^
- CRC32_TABLES.table[1][(term2 >> 16) & 0xFF] ^ CRC32_TABLES.table[0][(term2 >> 24)];
+ consteval Crc32Tables() {
+ constexpr u32 T = 0x82F63B78;
- p += 8;
- len -= 8;
- }
-
- while (len--)
- crc = (crc >> 8) ^ CRC32_TABLES.table[0][(crc ^ *p++) & 0xFF];
-
- return ~crc;
+ for (u32 i = 0; i < 256; i++) {
+ u32 crc = i;
+ for (i32 j = 0; j < 8; j++) {
+ crc = (crc >> 1) ^ ((crc & 1) ? T : 0);
+ }
+ table[0][i] = crc;
}
- UINT32 DataOps::CRC32(IN Span data)
- {
+ for (i32 i = 0; i < 256; i++) {
+ for (i32 slice = 1; slice < 8; slice++) {
+ const u32 prev = table[slice - 1][i];
+ table[slice][i] = (prev >> 8) ^ table[0][prev & 0xFF];
+ }
+ }
+ }
+};
+
+static constexpr Crc32Tables CRC32_TABLES{};
+
#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)
- return CRC32_ARM64_HW(data);
+inline auto crc32_x64_hw(Span data) -> u32 {
+ const u8 *p = data.data();
+
+ u32 crc = 0xFFFFFFFF;
+ usize len = data.size();
+
+ while (len >= 8) {
+ const u64 chunk = read_unaligned(p);
+ crc = static_cast(_mm_crc32_u64(static_cast(crc), chunk));
+ p += 8;
+ len -= 8;
+ }
+
+ while (len--) {
+ crc = _mm_crc32_u8(crc, *p++);
+ }
+
+ return ~crc;
+}
#endif
- return CRC32_Software_Slice8(data);
- }
-} // namespace IACore
-namespace IACore
-{
- CONSTEXPR UINT32 XXH_PRIME32_1 = 0x9E3779B1U;
- CONSTEXPR UINT32 XXH_PRIME32_2 = 0x85EBCA77U;
- CONSTEXPR UINT32 XXH_PRIME32_3 = 0xC2B2AE3DU;
- CONSTEXPR UINT32 XXH_PRIME32_4 = 0x27D4EB2FU;
- CONSTEXPR UINT32 XXH_PRIME32_5 = 0x165667B1U;
+#if IA_ARCH_ARM64
+__attribute__((target("+crc"))) inline auto crc32_arm64_hw(Span data)
+ -> u32 {
+ const u8 *p = data.data();
- INLINE UINT32 XXH32_Round(IN UINT32 seed, IN UINT32 input)
- {
- seed += input * XXH_PRIME32_2;
- seed = std::rotl(seed, 13);
- seed *= XXH_PRIME32_1;
- return seed;
+ u32 crc = 0xFFFFFFFF;
+ usize len = data.size();
+
+ while (len >= 8) {
+ const u64 chunk = read_unaligned(p);
+ crc = __crc32cd(crc, chunk);
+ p += 8;
+ len -= 8;
+ }
+
+ while (len--) {
+ crc = __crc32cb(crc, *p++);
+ }
+
+ return ~crc;
+}
+#endif
+
+inline auto crc32_software_slice8(Span data) -> u32 {
+ const u8 *p = data.data();
+ u32 crc = 0xFFFFFFFF;
+ usize len = data.size();
+
+ while (len >= 8) {
+ const u32 term1 = crc ^ read_unaligned(p);
+ const u32 term2 = read_unaligned(p + 4);
+
+ crc = CRC32_TABLES.table[7][term1 & 0xFF] ^
+ CRC32_TABLES.table[6][(term1 >> 8) & 0xFF] ^
+ CRC32_TABLES.table[5][(term1 >> 16) & 0xFF] ^
+ CRC32_TABLES.table[4][(term1 >> 24)] ^
+ CRC32_TABLES.table[3][term2 & 0xFF] ^
+ CRC32_TABLES.table[2][(term2 >> 8) & 0xFF] ^
+ CRC32_TABLES.table[1][(term2 >> 16) & 0xFF] ^
+ CRC32_TABLES.table[0][(term2 >> 24)];
+
+ p += 8;
+ len -= 8;
+ }
+
+ while (len--) {
+ crc = (crc >> 8) ^ CRC32_TABLES.table[0][(crc ^ *p++) & 0xFF];
+ }
+
+ return ~crc;
+}
+
+auto DataOps::crc32(Span 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) {
+ return crc32_arm64_hw(data);
+ }
+#endif
+ return crc32_software_slice8(data);
+}
+
+// =============================================================================
+// xxHash
+// =============================================================================
+
+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 {
+ 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(Span data, u32 seed) -> u32 {
+ const u8 *p = data.data();
+ const u8 *const b_end = p + data.size();
+ u32 h32{};
+
+ if (data.size() >= 16) {
+ const u8 *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;
+
+ do {
+ v1 = xxh32_round(v1, read_unaligned(p));
+ p += 4;
+ v2 = xxh32_round(v2, read_unaligned(p));
+ p += 4;
+ v3 = xxh32_round(v3, read_unaligned(p));
+ p += 4;
+ v4 = xxh32_round(v4, read_unaligned(p));
+ p += 4;
+ } while (p <= limit);
+
+ h32 = std::rotl(v1, 1) + std::rotl(v2, 7) + std::rotl(v3, 12) +
+ std::rotl(v4, 18);
+ } else {
+ h32 = seed + XXH_PRIME32_5;
+ }
+
+ h32 += static_cast(data.size());
+
+ while (p + 4 <= b_end) {
+ const auto t = read_unaligned(p) * XXH_PRIME32_3;
+ h32 += t;
+ h32 = std::rotl(h32, 17) * XXH_PRIME32_4;
+ p += 4;
+ }
+
+ while (p < b_end) {
+ h32 += (*p++) * XXH_PRIME32_5;
+ h32 = std::rotl(h32, 11) * XXH_PRIME32_1;
+ }
+
+ h32 ^= h32 >> 15;
+ h32 *= XXH_PRIME32_2;
+ h32 ^= h32 >> 13;
+ h32 *= XXH_PRIME32_3;
+ h32 ^= h32 >> 16;
+
+ return h32;
+}
+
+// =============================================================================
+// FNV-1a
+// =============================================================================
+
+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) {
+ 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();
+
+ for (usize i = 0; i < data.size(); ++i) {
+ hash ^= ptr[i];
+ hash *= FNV1A_32_PRIME;
+ }
+ return hash;
+}
+
+// =============================================================================
+// Compression
+// =============================================================================
+
+auto DataOps::detect_compression(Span 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;
+ }
+
+ return CompressionType::None;
+}
+
+auto DataOps::zlib_inflate(Span data) -> Result> {
+ z_stream 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");
+ }
+
+ 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 =
+ 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;
+ do {
+ if (zs.avail_out == 0) {
+ const usize current_pos = zs.total_out;
+ const usize new_size = out_buffer.size() * 2;
+ out_buffer.resize(new_size);
+
+ zs.next_out = reinterpret_cast(out_buffer.data() + current_pos);
+ zs.avail_out = static_cast(new_size - current_pos);
}
- UINT32 DataOps::Hash_xxHash(IN CONST String &string, IN UINT32 seed)
- {
- return Hash_xxHash(Span(reinterpret_cast(string.data()), string.length()), seed);
+ ret = inflate(&zs, Z_NO_FLUSH);
+
+ } while (ret == Z_OK);
+
+ inflateEnd(&zs);
+
+ if (ret != Z_STREAM_END) {
+ return fail("Failed to inflate: corrupt data or stream error");
+ }
+
+ out_buffer.resize(zs.total_out);
+
+ return out_buffer;
+}
+
+auto DataOps::zlib_deflate(Span data) -> Result> {
+ z_stream zs{};
+ zs.zalloc = Z_NULL;
+ zs.zfree = Z_NULL;
+ zs.opaque = Z_NULL;
+
+ if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK) {
+ return fail("Failed to initialize zlib deflate");
+ }
+
+ zs.next_in = const_cast(data.data());
+ zs.avail_in = static_cast(data.size());
+
+ Vec 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);
+
+ if (ret != Z_STREAM_END) {
+ deflateEnd(&zs);
+ return fail("Failed to deflate, ran out of buffer memory");
+ }
+
+ out_buffer.resize(zs.total_out);
+
+ deflateEnd(&zs);
+ return out_buffer;
+}
+
+auto DataOps::zstd_inflate(Span data) -> Result> {
+ const unsigned long long content_size =
+ ZSTD_getFrameContentSize(data.data(), data.size());
+
+ if (content_size == ZSTD_CONTENTSIZE_ERROR) {
+ return fail("Failed to inflate: Not valid ZSTD compressed data");
+ }
+
+ if (content_size != ZSTD_CONTENTSIZE_UNKNOWN) {
+ // FAST PATH: We know the size
+ Vec 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());
+
+ if (ZSTD_isError(d_size)) {
+ return fail("Failed to inflate: {}", ZSTD_getErrorName(d_size));
}
- UINT32 DataOps::Hash_xxHash(IN Span data, IN UINT32 seed)
- {
- CONST UINT8 *p = data.data();
- CONST UINT8 *CONST bEnd = p + data.size();
- UINT32 h32{};
+ return out_buffer;
+ }
- if (data.size() >= 16)
- {
- const UINT8 *const limit = bEnd - 16;
+ ZSTD_DCtx *dctx = ZSTD_createDCtx();
+ Vec out_buffer;
+ out_buffer.resize(data.size() * 2);
- UINT32 v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2;
- UINT32 v2 = seed + XXH_PRIME32_2;
- UINT32 v3 = seed + 0;
- UINT32 v4 = seed - XXH_PRIME32_1;
+ ZSTD_inBuffer input = {data.data(), data.size(), 0};
+ ZSTD_outBuffer output = {out_buffer.data(), out_buffer.size(), 0};
- do
- {
- v1 = XXH32_Round(v1, ReadUnaligned(p));
- p += 4;
- v2 = XXH32_Round(v2, ReadUnaligned(p));
- p += 4;
- v3 = XXH32_Round(v3, ReadUnaligned(p));
- p += 4;
- v4 = XXH32_Round(v4, ReadUnaligned(p));
- p += 4;
- } while (p <= limit);
+ usize ret;
+ do {
+ ret = ZSTD_decompressStream(dctx, &output, &input);
- h32 = std::rotl(v1, 1) + std::rotl(v2, 7) + std::rotl(v3, 12) + std::rotl(v4, 18);
- }
- else
- h32 = seed + XXH_PRIME32_5;
-
- h32 += (UINT32) data.size();
-
- while (p + 4 <= bEnd)
- {
- const auto t = ReadUnaligned(p) * XXH_PRIME32_3;
- h32 += t;
- h32 = std::rotl(h32, 17) * XXH_PRIME32_4;
- p += 4;
- }
-
- while (p < bEnd)
- {
- h32 += (*p++) * XXH_PRIME32_5;
- h32 = std::rotl(h32, 11) * XXH_PRIME32_1;
- }
-
- h32 ^= h32 >> 15;
- h32 *= XXH_PRIME32_2;
- h32 ^= h32 >> 13;
- h32 *= XXH_PRIME32_3;
- h32 ^= h32 >> 16;
-
- return h32;
- }
-} // namespace IACore
-
-namespace IACore
-{
- // FNV-1a 32-bit Constants
- CONSTEXPR UINT32 FNV1A_32_PRIME = 0x01000193;
- CONSTEXPR UINT32 FNV1A_32_OFFSET = 0x811c9dc5;
-
- UINT32 DataOps::Hash_FNV1A(IN CONST String &string)
- {
- UINT32 hash = FNV1A_32_OFFSET;
- for (char c : string)
- {
- hash ^= static_cast(c);
- hash *= FNV1A_32_PRIME;
- }
- return hash;
+ if (ZSTD_isError(ret)) {
+ ZSTD_freeDCtx(dctx);
+ return fail("Failed to inflate: {}", ZSTD_getErrorName(ret));
}
- UINT32 DataOps::Hash_FNV1A(IN Span data)
- {
- UINT32 hash = FNV1A_32_OFFSET;
- const uint8_t *ptr = static_cast(data.data());
-
- for (size_t i = 0; i < data.size(); ++i)
- {
- hash ^= ptr[i];
- hash *= FNV1A_32_PRIME;
- }
- return hash;
- }
-} // namespace IACore
-
-namespace IACore
-{
- DataOps::CompressionType DataOps::DetectCompression(IN Span data)
- {
- 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;
-
- return CompressionType::None;
+ if (output.pos == output.size) {
+ const usize new_size = out_buffer.size() * 2;
+ out_buffer.resize(new_size);
+ output.dst = out_buffer.data();
+ output.size = new_size;
}
- EXPECT(Vector) DataOps::ZlibInflate(IN Span data)
- {
- z_stream zs{};
- zs.zalloc = Z_NULL;
- zs.zfree = Z_NULL;
- zs.opaque = Z_NULL;
+ } while (ret != 0);
- // 15 + 32 = Auto-detect Gzip or Zlib
- if (inflateInit2(&zs, 15 + 32) != Z_OK)
- return MakeUnexpected("Failed to initialize zlib inflate");
+ out_buffer.resize(output.pos);
+ ZSTD_freeDCtx(dctx);
- zs.next_in = const_cast(data.data());
- zs.avail_in = static_cast(data.size());
+ return out_buffer;
+}
- Vector outBuffer;
- // Start with 2x input size.
- size_t guessSize = data.size() < 1024 ? data.size() * 4 : data.size() * 2;
- outBuffer.resize(guessSize);
+auto DataOps::zstd_deflate(Span data) -> Result> {
+ const usize max_dst_size = ZSTD_compressBound(data.size());
- zs.next_out = reinterpret_cast(outBuffer.data());
- zs.avail_out = static_cast(outBuffer.size());
+ Vec out_buffer;
+ out_buffer.resize(max_dst_size);
- int ret;
- do
- {
- if (zs.avail_out == 0)
- {
- size_t currentPos = zs.total_out;
+ const usize compressed_size = ZSTD_compress(out_buffer.data(), max_dst_size,
+ data.data(), data.size(), 3);
- size_t newSize = outBuffer.size() * 2;
- outBuffer.resize(newSize);
+ if (ZSTD_isError(compressed_size)) {
+ return fail("Failed to deflate: {}", ZSTD_getErrorName(compressed_size));
+ }
- zs.next_out = reinterpret_cast(outBuffer.data() + currentPos);
+ out_buffer.resize(compressed_size);
+ return out_buffer;
+}
- zs.avail_out = static_cast(newSize - currentPos);
- }
+auto DataOps::gzip_deflate(Span data) -> Result> {
+ z_stream zs{};
+ zs.zalloc = Z_NULL;
+ zs.zfree = Z_NULL;
+ zs.opaque = Z_NULL;
- ret = inflate(&zs, Z_NO_FLUSH);
+ // 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");
+ }
- } while (ret == Z_OK);
+ zs.next_in = const_cast(data.data());
+ zs.avail_in = static_cast(data.size());
- inflateEnd(&zs);
+ Vec out_buffer;
- if (ret != Z_STREAM_END)
- return MakeUnexpected("Failed to inflate: corrupt data or stream error");
+ out_buffer.resize(deflateBound(&zs, static_cast(data.size())) +
+ 1024); // Additional 1KB buffer for safety
- outBuffer.resize(zs.total_out);
+ zs.next_out = reinterpret_cast(out_buffer.data());
+ zs.avail_out = static_cast(out_buffer.size());
- return outBuffer;
- }
+ const int ret = deflate(&zs, Z_FINISH);
- EXPECT(Vector) DataOps::ZlibDeflate(IN Span data)
- {
- z_stream zs{};
- zs.zalloc = Z_NULL;
- zs.zfree = Z_NULL;
- zs.opaque = Z_NULL;
+ if (ret != Z_STREAM_END) {
+ deflateEnd(&zs);
+ return fail("Failed to deflate");
+ }
- if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK)
- return MakeUnexpected("Failed to initialize zlib deflate");
+ out_buffer.resize(zs.total_out);
- zs.next_in = const_cast(data.data());
- zs.avail_in = static_cast(data.size());
+ deflateEnd(&zs);
+ return out_buffer;
+}
- Vector outBuffer;
+auto DataOps::gzip_inflate(Span data) -> Result> {
+ return zlib_inflate(data);
+}
- outBuffer.resize(deflateBound(&zs, data.size()));
-
- zs.next_out = reinterpret_cast(outBuffer.data());
- zs.avail_out = static_cast(outBuffer.size());
-
- int ret = deflate(&zs, Z_FINISH);
-
- if (ret != Z_STREAM_END)
- {
- deflateEnd(&zs);
- return MakeUnexpected("Failed to deflate, ran out of buffer memory");
- }
-
- outBuffer.resize(zs.total_out);
-
- deflateEnd(&zs);
- return outBuffer;
- }
-
- EXPECT(Vector) DataOps::ZstdInflate(IN Span data)
- {
- unsigned long long const contentSize = ZSTD_getFrameContentSize(data.data(), data.size());
-
- if (contentSize == ZSTD_CONTENTSIZE_ERROR)
- return MakeUnexpected("Failed to inflate: Not valid ZSTD compressed data");
-
- if (contentSize != ZSTD_CONTENTSIZE_UNKNOWN)
- {
- // FAST PATH: We know the size
- Vector outBuffer;
- outBuffer.resize(static_cast(contentSize));
-
- size_t const dSize = ZSTD_decompress(outBuffer.data(), outBuffer.size(), data.data(), data.size());
-
- if (ZSTD_isError(dSize))
- return MakeUnexpected(std::format("Failed to inflate: {}", ZSTD_getErrorName(dSize)));
-
- return outBuffer;
- }
-
- ZSTD_DCtx *dctx = ZSTD_createDCtx();
- Vector outBuffer;
- outBuffer.resize(data.size() * 2);
-
- ZSTD_inBuffer input = {data.data(), data.size(), 0};
- ZSTD_outBuffer output = {outBuffer.data(), outBuffer.size(), 0};
-
- size_t ret;
- do
- {
- ret = ZSTD_decompressStream(dctx, &output, &input);
-
- if (ZSTD_isError(ret))
- {
- ZSTD_freeDCtx(dctx);
- return MakeUnexpected(std::format("Failed to inflate: {}", ZSTD_getErrorName(ret)));
- }
-
- if (output.pos == output.size)
- {
- size_t newSize = outBuffer.size() * 2;
- outBuffer.resize(newSize);
- output.dst = outBuffer.data();
- output.size = newSize;
- }
-
- } while (ret != 0);
-
- outBuffer.resize(output.pos);
- ZSTD_freeDCtx(dctx);
-
- return outBuffer;
- }
-
- EXPECT(Vector) DataOps::ZstdDeflate(IN Span data)
- {
- size_t const maxDstSize = ZSTD_compressBound(data.size());
-
- Vector outBuffer;
- outBuffer.resize(maxDstSize);
-
- size_t const compressedSize = ZSTD_compress(outBuffer.data(), maxDstSize, data.data(), data.size(), 3);
-
- if (ZSTD_isError(compressedSize))
- return MakeUnexpected(std::format("Failed to deflate: {}", ZSTD_getErrorName(compressedSize)));
-
- outBuffer.resize(compressedSize);
- return outBuffer;
- }
-
- EXPECT(Vector) DataOps::GZipDeflate(IN Span data)
- {
- z_stream 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 MakeUnexpected("Failed to initialize gzip deflate");
-
- zs.next_in = const_cast(data.data());
- zs.avail_in = static_cast(data.size());
-
- Vector outBuffer;
-
- outBuffer.resize(deflateBound(&zs, data.size()) + 1024); // Additional 1KB buffer for safety
-
- zs.next_out = reinterpret_cast(outBuffer.data());
- zs.avail_out = static_cast(outBuffer.size());
-
- int ret = deflate(&zs, Z_FINISH);
-
- if (ret != Z_STREAM_END)
- {
- deflateEnd(&zs);
- return MakeUnexpected("Failed to deflate");
- }
-
- outBuffer.resize(zs.total_out);
-
- deflateEnd(&zs);
- return outBuffer;
- }
-
- EXPECT(Vector) DataOps::GZipInflate(IN Span data)
- {
- return ZlibInflate(data);
- }
} // namespace IACore
\ No newline at end of file
diff --git a/Src/IACore/imp/cpp/FileOps.cpp b/Src/IACore/imp/cpp/FileOps.cpp
index 5046907..bb8026e 100644
--- a/Src/IACore/imp/cpp/FileOps.cpp
+++ b/Src/IACore/imp/cpp/FileOps.cpp
@@ -14,501 +14,523 @@
// limitations under the License.
#include
+#include
+#include
-namespace IACore
-{
- UnorderedMap> FileOps::s_mappedFiles;
-
- VOID FileOps::UnmapFile(IN PCUINT8 mappedPtr)
- {
- if (!s_mappedFiles.contains(mappedPtr))
- return;
- const auto handles = s_mappedFiles.extract(mappedPtr)->second;
-#if IA_PLATFORM_WINDOWS
- ::UnmapViewOfFile(std::get<1>(handles));
- ::CloseHandle(std::get<2>(handles));
-
- if (std::get<0>(handles) != INVALID_HANDLE_VALUE)
- ::CloseHandle(std::get<0>(handles));
-#elif IA_PLATFORM_UNIX
- ::munmap(std::get<1>(handles), (SIZE_T) std::get<2>(handles));
- const auto fd = (INT32) ((UINT64) std::get<0>(handles));
- if (fd != -1)
- ::close(fd);
-#endif
- }
-
- EXPECT(PUINT8) FileOps::MapSharedMemory(IN CONST String &name, IN SIZE_T size, IN BOOL isOwner)
- {
-#if IA_PLATFORM_WINDOWS
- int wchars_num = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), -1, NULL, 0);
- std::wstring wName(wchars_num, 0);
- MultiByteToWideChar(CP_UTF8, 0, name.c_str(), -1, &wName[0], wchars_num);
-
- HANDLE hMap = NULL;
- if (isOwner)
- hMap = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, (DWORD) (size >> 32),
- (DWORD) (size & 0xFFFFFFFF), wName.c_str());
- else
- hMap = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, wName.c_str());
-
- if (hMap == NULL)
- return MakeUnexpected(
- std::format("Failed to {} shared memory '{}'", isOwner ? "owner" : "consumer", name.c_str()));
-
- const auto result = static_cast(MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, size));
- if (result == NULL)
- {
- CloseHandle(hMap);
- return MakeUnexpected(std::format("Failed to map view of shared memory '{}'", name.c_str()));
- }
-
- s_mappedFiles[result] = std::make_tuple((PVOID) INVALID_HANDLE_VALUE, (PVOID) result, (PVOID) hMap);
- return result;
-
-#elif IA_PLATFORM_UNIX
- int fd = -1;
- if (isOwner)
- {
- fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
- if (fd != -1)
- {
- if (ftruncate(fd, size) == -1)
- {
- close(fd);
- shm_unlink(name.c_str());
- return MakeUnexpected(std::format("Failed to truncate shared memory '{}'", name.c_str()));
- }
- }
- }
- else
- fd = shm_open(name.c_str(), O_RDWR, 0666);
-
- if (fd == -1)
- return MakeUnexpected(
- std::format("Failed to {} shared memory '{}'", isOwner ? "owner" : "consumer", name.c_str()));
-
- void *addr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (addr == MAP_FAILED)
- {
- close(fd);
- return MakeUnexpected(std::format("Failed to mmap shared memory '{}'", name.c_str()));
- }
-
- const auto result = static_cast(addr);
-
- s_mappedFiles[result] = std::make_tuple((PVOID) ((UINT64) fd), (PVOID) addr, (PVOID) size);
- return result;
-
-#endif
- }
-
- VOID FileOps::UnlinkSharedMemory(IN CONST String &name)
- {
- if (name.empty())
- return;
#if IA_PLATFORM_UNIX
+#include
+#include
+#include
+#include
+#endif
+
+namespace IACore {
+
+HashMap> FileOps::s_mapped_files;
+
+auto FileOps::unmap_file(const u8 *mapped_ptr) -> void {
+ if (!s_mapped_files.contains(mapped_ptr)) {
+ return;
+ }
+
+ auto it = s_mapped_files.find(mapped_ptr);
+ const auto 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));
+ 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));
+ if (fd != -1) {
+ ::close(fd);
+ }
+#endif
+}
+
+auto FileOps::map_shared_memory(const String &name, usize size, bool is_owner)
+ -> Result {
+#if IA_PLATFORM_WINDOWS
+ const int wchars_num =
+ MultiByteToWideChar(CP_UTF8, 0, name.c_str(), -1, NULL, 0);
+ std::wstring w_name(wchars_num, 0);
+ MultiByteToWideChar(CP_UTF8, 0, name.c_str(), -1, &w_name[0], wchars_num);
+
+ HANDLE h_map = NULL;
+ if (is_owner) {
+ h_map = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
+ (DWORD)(size >> 32), (DWORD)(size & 0xFFFFFFFF),
+ w_name.c_str());
+ } else {
+ h_map = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, w_name.c_str());
+ }
+
+ if (h_map == NULL) {
+ return fail("Failed to {} shared memory '{}'",
+ is_owner ? "owner" : "consumer", name);
+ }
+
+ auto *result =
+ static_cast(MapViewOfFile(h_map, FILE_MAP_ALL_ACCESS, 0, 0, size));
+ if (result == NULL) {
+ CloseHandle(h_map);
+ return fail("Failed to map view of shared memory '{}'", name);
+ }
+
+ s_mapped_files[result] = std::make_tuple((void *)INVALID_HANDLE_VALUE,
+ (void *)result, (void *)h_map);
+ return result;
+
+#elif IA_PLATFORM_UNIX
+ int fd = -1;
+ if (is_owner) {
+ fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
+ if (fd != -1) {
+ if (ftruncate(fd, size) == -1) {
+ close(fd);
shm_unlink(name.c_str());
-#endif
+ return fail("Failed to truncate shared memory '{}'", name);
+ }
}
+ } else {
+ fd = shm_open(name.c_str(), O_RDWR, 0666);
+ }
- EXPECT(PCUINT8) FileOps::MapFile(IN CONST FilePath &path, OUT SIZE_T &size)
- {
+ if (fd == -1) {
+ return fail("Failed to {} shared memory '{}'",
+ is_owner ? "owner" : "consumer", name);
+ }
+
+ void *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);
+
+ s_mapped_files[result] =
+ std::make_tuple((void *)((u64)fd), (void *)addr, (void *)size);
+ return result;
+#endif
+}
+
+auto FileOps::unlink_shared_memory(const String &name) -> void {
+ if (name.empty()) {
+ return;
+ }
+#if IA_PLATFORM_UNIX
+ shm_unlink(name.c_str());
+#endif
+}
+
+auto FileOps::map_file(const Path &path, usize &size) -> Result {
#if IA_PLATFORM_WINDOWS
+ const auto handle = CreateFileA(
+ path.string().c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
- const auto handle = CreateFileA(path.string().c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (handle == INVALID_HANDLE_VALUE) {
+ return fail("Failed to open {} for memory mapping", path.string());
+ }
- if (handle == INVALID_HANDLE_VALUE)
- return MakeUnexpected(std::format("Failed to open {} for memory mapping", path.string().c_str()));
+ LARGE_INTEGER file_size;
+ if (!GetFileSizeEx(handle, &file_size)) {
+ CloseHandle(handle);
+ return fail("Failed to get size of {} for memory mapping", path.string());
+ }
+ size = static_cast(file_size.QuadPart);
+ if (size == 0) {
+ CloseHandle(handle);
+ return fail("Failed to get size of {} for memory mapping", path.string());
+ }
- LARGE_INTEGER fileSize;
- if (!GetFileSizeEx(handle, &fileSize))
- {
- CloseHandle(handle);
- return MakeUnexpected(std::format("Failed to get size of {} for memory mapping", path.string().c_str()));
- }
- size = static_cast(fileSize.QuadPart);
- if (size == 0)
- {
- CloseHandle(handle);
- return MakeUnexpected(std::format("Failed to get size of {} for memory mapping", path.string().c_str()));
- }
+ auto h_map = CreateFileMappingW(handle, NULL, PAGE_READONLY, 0, 0, NULL);
+ if (h_map == NULL) {
+ CloseHandle(handle);
+ return fail("Failed to memory map {}", path.string());
+ }
- auto hmap = CreateFileMappingW(handle, NULL, PAGE_READONLY, 0, 0, NULL);
- if (hmap == NULL)
- {
- CloseHandle(handle);
- return MakeUnexpected(std::format("Failed to memory map {}", path.string().c_str()));
- }
-
- const auto result = static_cast(MapViewOfFile(hmap, FILE_MAP_READ, 0, 0, 0));
- if (result == NULL)
- {
- CloseHandle(handle);
- CloseHandle(hmap);
- return MakeUnexpected(std::format("Failed to memory map {}", path.string().c_str()));
- }
- s_mappedFiles[result] = std::make_tuple((PVOID) handle, (PVOID) result, (PVOID) hmap);
- return result;
+ const auto *result =
+ static_cast(MapViewOfFile(h_map, FILE_MAP_READ, 0, 0, 0));
+ if (result == NULL) {
+ CloseHandle(handle);
+ CloseHandle(h_map);
+ return fail("Failed to memory map {}", path.string());
+ }
+ s_mapped_files[result] = std::make_tuple(
+ (void *)handle, (void *)const_cast(result), (void *)h_map);
+ return result;
#elif IA_PLATFORM_UNIX
-
- const auto handle = open(path.string().c_str(), O_RDONLY);
- if (handle == -1)
- return MakeUnexpected(std::format("Failed to open {} for memory mapping", path.string().c_str()));
- struct stat sb;
- if (fstat(handle, &sb) == -1)
- {
- close(handle);
- return MakeUnexpected(std::format("Failed to get stats of {} for memory mapping", path.string().c_str()));
- }
- size = static_cast(sb.st_size);
- if (size == 0)
- {
- close(handle);
- return MakeUnexpected(std::format("Failed to get size of {} for memory mapping", path.string().c_str()));
- }
- void *addr = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, handle, 0);
- if (addr == MAP_FAILED)
- {
- close(handle);
- return MakeUnexpected(std::format("Failed to memory map {}", path.string().c_str()));
- }
- const auto result = static_cast(addr);
- madvise(addr, size, MADV_SEQUENTIAL);
- s_mappedFiles[result] = std::make_tuple((PVOID) ((UINT64) handle), (PVOID) addr, (PVOID) size);
- return result;
+ const auto handle = open(path.string().c_str(), O_RDONLY);
+ if (handle == -1) {
+ return fail("Failed to open {} for memory mapping", path.string());
+ }
+ struct stat sb;
+ if (fstat(handle, &sb) == -1) {
+ close(handle);
+ return fail("Failed to get stats of {} for memory mapping", path.string());
+ }
+ size = static_cast(sb.st_size);
+ if (size == 0) {
+ 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);
+ if (addr == MAP_FAILED) {
+ close(handle);
+ return fail("Failed to memory map {}", path.string());
+ }
+ const auto *result = static_cast(addr);
+ madvise(addr, size, MADV_SEQUENTIAL);
+ s_mapped_files[result] =
+ std::make_tuple((void *)((u64)handle), (void *)addr, (void *)size);
+ return result;
#endif
- }
+}
- EXPECT(StreamWriter) FileOps::StreamToFile(IN CONST FilePath &path, IN BOOL overwrite)
- {
- if (!overwrite && FileSystem::exists(path))
- return MakeUnexpected(std::format("File aready exists: {}", path.string().c_str()));
- return StreamWriter(path);
- }
+auto FileOps::stream_to_file(const Path &path, bool overwrite)
+ -> Result {
+ if (!overwrite && std::filesystem::exists(path)) {
+ return fail("File already exists: {}", path.string());
+ }
+ return StreamWriter::create(path);
+}
- EXPECT(StreamReader) FileOps::StreamFromFile(IN CONST FilePath &path)
- {
- if (!FileSystem::exists(path))
- return MakeUnexpected(std::format("File does not exist: {}", path.string().c_str()));
- return StreamReader(path);
- }
+auto FileOps::stream_from_file(const Path &path) -> Result {
+ if (!std::filesystem::exists(path)) {
+ return fail("File does not exist: {}", path.string());
+ }
+ return StreamReader::create_from_file(path);
+}
- EXPECT(String) FileOps::ReadTextFile(IN CONST FilePath &path)
- {
- const auto f = fopen(path.string().c_str(), "r");
- if (!f)
- return MakeUnexpected(std::format("Failed to open file: {}", path.string().c_str()));
- String result;
- fseek(f, 0, SEEK_END);
- result.resize(ftell(f));
- fseek(f, 0, SEEK_SET);
- fread(result.data(), 1, result.size(), f);
- fclose(f);
- return result;
- }
+auto FileOps::read_text_file(const Path &path) -> Result {
+ auto *f = fopen(path.string().c_str(), "r");
+ if (!f) {
+ return fail("Failed to open file: {}", path.string());
+ }
+ String result;
+ fseek(f, 0, SEEK_END);
+ const long len = ftell(f);
+ if (len > 0) {
+ result.resize(static_cast(len));
+ fseek(f, 0, SEEK_SET);
+ fread(result.data(), 1, result.size(), f);
+ }
+ fclose(f);
+ return result;
+}
- EXPECT(Vector) FileOps::ReadBinaryFile(IN CONST FilePath &path)
- {
- const auto f = fopen(path.string().c_str(), "rb");
- if (!f)
- return MakeUnexpected(std::format("Failed to open file: {}", path.string().c_str()));
- Vector result;
- fseek(f, 0, SEEK_END);
- result.resize(ftell(f));
- fseek(f, 0, SEEK_SET);
- fread(result.data(), 1, result.size(), f);
- fclose(f);
- return result;
- }
+auto FileOps::read_binary_file(const Path &path) -> Result> {
+ auto *f = fopen(path.string().c_str(), "rb");
+ if (!f) {
+ return fail("Failed to open file: {}", path.string());
+ }
+ Vec result;
+ fseek(f, 0, SEEK_END);
+ const long len = ftell(f);
+ if (len > 0) {
+ result.resize(static_cast(len));
+ fseek(f, 0, SEEK_SET);
+ fread(result.data(), 1, result.size(), f);
+ }
+ fclose(f);
+ return result;
+}
- EXPECT(SIZE_T) FileOps::WriteTextFile(IN CONST FilePath &path, IN CONST String &contents, IN BOOL overwrite)
- {
- const char *mode = overwrite ? "w" : "wx";
- const auto f = fopen(path.string().c_str(), mode);
- if (!f)
- {
- if (!overwrite && errno == EEXIST)
- return MakeUnexpected(std::format("File already exists: {}", path.string().c_str()));
- return MakeUnexpected(std::format("Failed to write to file: {}", path.string().c_str()));
- }
- const auto result = fwrite(contents.data(), 1, contents.size(), f);
- fclose(f);
- 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);
+ 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);
+ fclose(f);
+ return result;
+}
- EXPECT(SIZE_T) FileOps::WriteBinaryFile(IN CONST FilePath &path, IN Span contents, IN BOOL overwrite)
- {
- const char *mode = overwrite ? "w" : "wx";
- const auto f = fopen(path.string().c_str(), mode);
- if (!f)
- {
- if (!overwrite && errno == EEXIST)
- return MakeUnexpected(std::format("File already exists: {}", path.string().c_str()));
- return MakeUnexpected(std::format("Failed to write to file: {}", path.string().c_str()));
- }
- const auto 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);
+ 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);
+ fclose(f);
+ return result;
+}
- FilePath FileOps::NormalizeExecutablePath(IN CONST FilePath &path)
- {
- FilePath result = path;
+auto FileOps::normalize_executable_path(const Path &path) -> Path {
+ Path result = path;
#if IA_PLATFORM_WINDOWS
- if (!result.has_extension())
- result.replace_extension(".exe");
+ if (!result.has_extension()) {
+ result.replace_extension(".exe");
+ }
+#elif IA_PLATFORM_UNIX
+ if (result.extension() == ".exe") {
+ result.replace_extension("");
+ }
+
+ if (result.is_relative()) {
+ String path_str = result.string();
+ if (!path_str.starts_with("./") && !path_str.starts_with("../")) {
+ result = "./" + path_str;
+ }
+ }
+#endif
+ return result;
+}
+
+auto FileOps::native_open_file(const Path &path, FileAccess access,
+ FileMode mode, u32 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;
+
+ switch (access) {
+ case FileAccess::Read:
+ dw_access = GENERIC_READ;
+ break;
+ case FileAccess::Write:
+ dw_access = GENERIC_WRITE;
+ break;
+ case FileAccess::ReadWrite:
+ dw_access = GENERIC_READ | GENERIC_WRITE;
+ break;
+ }
+
+ switch (mode) {
+ case FileMode::OpenExisting:
+ dw_disposition = OPEN_EXISTING;
+ break;
+ case FileMode::OpenAlways:
+ dw_disposition = OPEN_ALWAYS;
+ break;
+ case FileMode::CreateNew:
+ dw_disposition = CREATE_NEW;
+ break;
+ case FileMode::CreateAlways:
+ dw_disposition = CREATE_ALWAYS;
+ break;
+ case FileMode::TruncateExisting:
+ dw_disposition = TRUNCATE_EXISTING;
+ break;
+ }
+
+ HANDLE 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());
+ }
+
+ return h_file;
#elif IA_PLATFORM_UNIX
- if (result.extension() == ".exe")
- result.replace_extension("");
+ int flags = 0;
- if (result.is_relative())
- {
- String pathStr = result.string();
- if (!pathStr.starts_with("./") && !pathStr.starts_with("../"))
- result = "./" + pathStr;
- }
+ switch (access) {
+ case FileAccess::Read:
+ flags = O_RDONLY;
+ break;
+ case FileAccess::Write:
+ flags = O_WRONLY;
+ break;
+ case FileAccess::ReadWrite:
+ flags = O_RDWR;
+ break;
+ }
+
+ switch (mode) {
+ case FileMode::OpenExisting:
+ break;
+ case FileMode::OpenAlways:
+ flags |= O_CREAT;
+ break;
+ case FileMode::CreateNew:
+ flags |= O_CREAT | O_EXCL;
+ break;
+ case FileMode::CreateAlways:
+ flags |= O_CREAT | O_TRUNC;
+ break;
+ case FileMode::TruncateExisting:
+ flags |= O_TRUNC;
+ break;
+ }
+
+ int fd = open(path.string().c_str(), flags, permissions);
+
+ if (fd == -1) {
+ return fail("Failed to open file '{}': {}", path.string(), errno);
+ }
+
+ return fd;
#endif
- return result;
- }
-} // namespace IACore
+}
+
+auto FileOps::native_close_file(NativeFileHandle handle) -> void {
+ if (handle == INVALID_FILE_HANDLE) {
+ return;
+ }
-namespace IACore
-{
- EXPECT(NativeFileHandle)
- FileOps::NativeOpenFile(IN CONST FilePath &path, IN EFileAccess access, IN EFileMode mode, IN UINT32 permissions)
- {
#if IA_PLATFORM_WINDOWS
- DWORD dwAccess = 0;
- DWORD dwShare = FILE_SHARE_READ;
- DWORD dwDisposition = 0;
- DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
+ CloseHandle(handle);
+#elif IA_PLATFORM_UNIX
+ close(handle);
+#endif
+}
- switch (access)
- {
- case EFileAccess::READ:
- dwAccess = GENERIC_READ;
- break;
- case EFileAccess::WRITE:
- dwAccess = GENERIC_WRITE;
- break;
- case EFileAccess::READ_WRITE:
- dwAccess = GENERIC_READ | GENERIC_WRITE;
- break;
- }
+// =============================================================================
+// MemoryMappedRegion
+// =============================================================================
- switch (mode)
- {
- case EFileMode::OPEN_EXISTING:
- dwDisposition = OPEN_EXISTING;
- break;
- case EFileMode::OPEN_ALWAYS:
- dwDisposition = OPEN_ALWAYS;
- break;
- case EFileMode::CREATE_NEW:
- dwDisposition = CREATE_NEW;
- break;
- case EFileMode::CREATE_ALWAYS:
- dwDisposition = CREATE_ALWAYS;
- break;
- case EFileMode::TRUNCATE_EXISTING:
- dwDisposition = TRUNCATE_EXISTING;
- break;
- }
+FileOps::MemoryMappedRegion::~MemoryMappedRegion() { unmap(); }
- HANDLE hFile =
- CreateFileA(path.string().c_str(), dwAccess, dwShare, NULL, dwDisposition, dwFlagsAndAttributes, NULL);
+FileOps::MemoryMappedRegion::MemoryMappedRegion(
+ MemoryMappedRegion &&other) noexcept {
+ *this = std::move(other);
+}
- if (hFile == INVALID_HANDLE_VALUE)
- return MakeUnexpected(std::format("Failed to open file '{}': {}", path.string(), GetLastError()));
+auto FileOps::MemoryMappedRegion::operator=(MemoryMappedRegion &&other) noexcept
+ -> MemoryMappedRegion & {
+ if (this != &other) {
+ unmap();
+ m_ptr = other.m_ptr;
+ m_size = other.m_size;
+#if IA_PLATFORM_WINDOWS
+ m_map_handle = other.m_map_handle;
+ other.m_map_handle = NULL;
+#endif
+ other.m_ptr = nullptr;
+ other.m_size = 0;
+ }
+ return *this;
+}
- return hFile;
+auto FileOps::MemoryMappedRegion::map(NativeFileHandle handle, u64 offset,
+ usize size) -> Result {
+ unmap();
+
+ if (handle == INVALID_FILE_HANDLE) {
+ return fail("Invalid file handle provided to Map");
+ }
+
+ if (size == 0) {
+ return fail("Cannot map region of size 0");
+ }
+
+#if IA_PLATFORM_WINDOWS
+ LARGE_INTEGER file_size;
+ if (!GetFileSizeEx(handle, &file_size)) {
+ return fail("Failed to get file size");
+ }
+
+ u64 end_offset = offset + size;
+ if (static_cast(file_size.QuadPart) < end_offset) {
+ LARGE_INTEGER 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");
+ }
+
+ if (!SetEndOfFile(handle)) {
+ return fail("Failed to extend file for mapping");
+ }
+ }
+
+ m_map_handle = CreateFileMappingW(handle, NULL, PAGE_READWRITE, 0, 0, NULL);
+ if (m_map_handle == NULL) {
+ return fail("CreateFileMapping failed: {}", GetLastError());
+ }
+
+ DWORD offset_high = static_cast(offset >> 32);
+ DWORD offset_low = static_cast(offset & 0xFFFFFFFF);
+
+ m_ptr = static_cast(MapViewOfFile(m_map_handle, FILE_MAP_WRITE,
+ offset_high, offset_low, size));
+ if (m_ptr == NULL) {
+ CloseHandle(m_map_handle);
+ m_map_handle = NULL;
+ return fail("MapViewOfFile failed (Offset: {}, Size: {}): {}", offset, size,
+ GetLastError());
+ }
+ m_size = size;
#elif IA_PLATFORM_UNIX
- int flags = 0;
+ struct stat sb;
+ if (fstat(handle, &sb) == -1) {
+ return fail("Failed to fstat file");
+ }
- switch (access)
- {
- case EFileAccess::READ:
- flags = O_RDONLY;
- break;
- case EFileAccess::WRITE:
- flags = O_WRONLY;
- break;
- case EFileAccess::READ_WRITE:
- flags = O_RDWR;
- break;
- }
-
- switch (mode)
- {
- case EFileMode::OPEN_EXISTING:
- break;
- case EFileMode::OPEN_ALWAYS:
- flags |= O_CREAT;
- break;
- case EFileMode::CREATE_NEW:
- flags |= O_CREAT | O_EXCL;
- break;
- case EFileMode::CREATE_ALWAYS:
- flags |= O_CREAT | O_TRUNC;
- break;
- case EFileMode::TRUNCATE_EXISTING:
- flags |= O_TRUNC;
- break;
- }
-
- int fd = open(path.string().c_str(), flags, permissions);
-
- if (fd == -1)
- {
- return MakeUnexpected(std::format("Failed to open file '{}': {}", path.string(), errno));
- }
-
- return fd;
-#endif
+ u64 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 FileOps::NativeCloseFile(IN NativeFileHandle handle)
- {
- if (handle == INVALID_FILE_HANDLE)
- return;
+ void *ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, handle,
+ static_cast(offset));
+ if (ptr == MAP_FAILED) {
+ return fail("mmap failed: {}", errno);
+ }
+
+ m_ptr = static_cast(ptr);
+ m_size = size;
+
+ madvise(m_ptr, m_size, MADV_SEQUENTIAL);
+#endif
+
+ return {};
+}
+
+auto FileOps::MemoryMappedRegion::unmap() -> void {
+ if (!m_ptr) {
+ return;
+ }
#if IA_PLATFORM_WINDOWS
- CloseHandle(handle);
+ UnmapViewOfFile(m_ptr);
+ if (m_map_handle) {
+ CloseHandle(m_map_handle);
+ m_map_handle = NULL;
+ }
#elif IA_PLATFORM_UNIX
- close(handle);
+ munmap(m_ptr, m_size);
#endif
- }
+ m_ptr = nullptr;
+ m_size = 0;
+}
- FileOps::MemoryMappedRegion::~MemoryMappedRegion()
- {
- Unmap();
- }
-
- FileOps::MemoryMappedRegion::MemoryMappedRegion(MemoryMappedRegion &&other) NOEXCEPT
- {
- *this = std::move(other);
- }
-
- FileOps::MemoryMappedRegion &FileOps::MemoryMappedRegion::operator=(MemoryMappedRegion &&other) NOEXCEPT
- {
- if (this != &other)
- {
- Unmap();
- m_ptr = other.m_ptr;
- m_size = other.m_size;
-#if IA_PLATFORM_WINDOWS
- m_hMap = other.m_hMap;
- other.m_hMap = NULL;
-#endif
- other.m_ptr = nullptr;
- other.m_size = 0;
- }
- return *this;
- }
-
- EXPECT(VOID) FileOps::MemoryMappedRegion::Map(NativeFileHandle handle, UINT64 offset, SIZE_T size)
- {
- Unmap();
-
- if (handle == INVALID_FILE_HANDLE)
- return MakeUnexpected("Invalid file handle provided to Map");
-
- if (size == 0)
- return MakeUnexpected("Cannot map region of size 0");
+auto FileOps::MemoryMappedRegion::flush() -> void {
+ if (!m_ptr) {
+ return;
+ }
#if IA_PLATFORM_WINDOWS
- LARGE_INTEGER fileSize;
- if (!GetFileSizeEx(handle, &fileSize))
- return MakeUnexpected("Failed to get file size");
-
- UINT64 endOffset = offset + size;
- if (static_cast(fileSize.QuadPart) < endOffset)
- {
- LARGE_INTEGER newSize;
- newSize.QuadPart = endOffset;
- if (!SetFilePointerEx(handle, newSize, NULL, FILE_BEGIN))
- return MakeUnexpected("Failed to seek to new end of file");
-
- if (!SetEndOfFile(handle))
- return MakeUnexpected("Failed to extend file for mapping");
- }
-
- m_hMap = CreateFileMappingW(handle, NULL, PAGE_READWRITE, 0, 0, NULL);
- if (m_hMap == NULL)
- return MakeUnexpected(std::format("CreateFileMapping failed: {}", GetLastError()));
-
- DWORD offsetHigh = static_cast(offset >> 32);
- DWORD offsetLow = static_cast(offset & 0xFFFFFFFF);
-
- m_ptr = static_cast(MapViewOfFile(m_hMap, FILE_MAP_WRITE, offsetHigh, offsetLow, size));
- if (m_ptr == NULL)
- {
- CloseHandle(m_hMap);
- m_hMap = NULL;
- return MakeUnexpected(
- std::format("MapViewOfFile failed (Offset: {}, Size: {}): {}", offset, size, GetLastError()));
- }
- m_size = size;
-
+ FlushViewOfFile(m_ptr, m_size);
#elif IA_PLATFORM_UNIX
- struct stat sb;
- if (fstat(handle, &sb) == -1)
- return MakeUnexpected("Failed to fstat file");
-
- UINT64 endOffset = offset + size;
- if (static_cast(sb.st_size) < endOffset)
- {
- if (ftruncate(handle, endOffset) == -1)
- return MakeUnexpected("Failed to ftruncate (extend) file");
- }
-
- void *ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, handle, static_cast(offset));
- if (ptr == MAP_FAILED)
- return MakeUnexpected(std::format("mmap failed: {}", errno));
-
- m_ptr = static_cast(ptr);
- m_size = size;
-
- madvise(m_ptr, m_size, MADV_SEQUENTIAL);
+ msync(m_ptr, m_size, MS_SYNC);
#endif
+}
- return {};
- }
-
- VOID FileOps::MemoryMappedRegion::Unmap()
- {
- if (!m_ptr)
- return;
-
-#if IA_PLATFORM_WINDOWS
- UnmapViewOfFile(m_ptr);
- if (m_hMap)
- {
- CloseHandle(m_hMap);
- m_hMap = NULL;
- }
-#elif IA_PLATFORM_UNIX
- munmap(m_ptr, m_size);
-#endif
- m_ptr = nullptr;
- m_size = 0;
- }
-
- VOID FileOps::MemoryMappedRegion::Flush()
- {
- if (!m_ptr)
- return;
-
-#if IA_PLATFORM_WINDOWS
- FlushViewOfFile(m_ptr, m_size);
-#elif IA_PLATFORM_UNIX
- msync(m_ptr, m_size, MS_SYNC);
-#endif
- }
} // namespace IACore
\ No newline at end of file
diff --git a/Src/IACore/imp/cpp/Http/Client.cpp b/Src/IACore/imp/cpp/Http/Client.cpp
index 89e3ad4..9dce4ee 100644
--- a/Src/IACore/imp/cpp/Http/Client.cpp
+++ b/Src/IACore/imp/cpp/Http/Client.cpp
@@ -13,127 +13,121 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include
#include
+#include
-namespace IACore
-{
- EXPECT(UniquePtr) HttpClient::Create(IN CONST String &host)
- {
- return MakeUniqueProtected(httplib::Client(host));
- }
+namespace IACore {
+Result> HttpClient::Create(const String &host) {
+ return MakeUniqueProtected(httplib::Client(host));
+}
- httplib::Headers BuildHeaders(IN Span headers, IN PCCHAR defaultContentType)
- {
- httplib::Headers out;
- bool hasContentType = false;
+httplib::Headers BuildHeaders(Span headers,
+ const char *defaultContentType) {
+ httplib::Headers out;
+ bool hasContentType = false;
- for (const auto &h : headers)
- {
- out.emplace(h.first, h.second);
+ for (const auto &h : headers) {
+ out.emplace(h.first, h.second);
- if (h.first == HttpClient::HeaderTypeToString(HttpClient::EHeaderType::CONTENT_TYPE))
- hasContentType = true;
- }
+ if (h.first ==
+ HttpClient::HeaderTypeToString(HttpClient::EHeaderType::CONTENT_TYPE))
+ hasContentType = true;
+ }
- if (!hasContentType && defaultContentType)
- out.emplace("Content-Type", defaultContentType);
- return out;
- }
+ if (!hasContentType && defaultContentType)
+ out.emplace("Content-Type", defaultContentType);
+ return out;
+}
- HttpClient::HttpClient(IN httplib::Client &&client)
- : m_client(IA_MOVE(client)), m_lastResponseCode(EResponseCode::INTERNAL_SERVER_ERROR)
- {
- m_client.enable_server_certificate_verification(true);
- }
+HttpClient::HttpClient(httplib::Client &&client)
+ : m_client(std::move(client)),
+ m_lastResponseCode(EResponseCode::INTERNAL_SERVER_ERROR) {
+ m_client.enable_server_certificate_verification(true);
+}
- HttpClient::~HttpClient()
- {
- }
+HttpClient::~HttpClient() {}
- VOID HttpClient::EnableCertificateVerfication()
- {
- m_client.enable_server_certificate_verification(true);
- }
+void HttpClient::EnableCertificateVerfication() {
+ m_client.enable_server_certificate_verification(true);
+}
- VOID HttpClient::DisableCertificateVerfication()
- {
- m_client.enable_server_certificate_verification(false);
- }
+void HttpClient::DisableCertificateVerfication() {
+ m_client.enable_server_certificate_verification(false);
+}
- String HttpClient::PreprocessResponse(IN CONST String &response)
- {
- const auto responseBytes = Span{(PCUINT8) response.data(), response.size()};
- const auto compression = DataOps::DetectCompression(responseBytes);
- switch (compression)
- {
- case DataOps::CompressionType::Gzip: {
- const auto data = DataOps::GZipInflate(responseBytes);
- if (!data)
- return response;
- return String((PCCHAR) data->data(), data->size());
- }
+String HttpClient::PreprocessResponse(const String &response) {
+ const auto responseBytes =
+ Span{(const u8 *)response.data(), response.size()};
+ const auto compression = DataOps::DetectCompression(responseBytes);
+ switch (compression) {
+ case DataOps::CompressionType::Gzip: {
+ const auto data = DataOps::GZipInflate(responseBytes);
+ if (!data)
+ return response;
+ return String((const char *)data->data(), data->size());
+ }
- case DataOps::CompressionType::Zlib: {
- const auto data = DataOps::ZlibInflate(responseBytes);
- if (!data)
- return response;
- return String((PCCHAR) data->data(), data->size());
- }
+ case DataOps::CompressionType::Zlib: {
+ const auto data = DataOps::ZlibInflate(responseBytes);
+ if (!data)
+ return response;
+ return String((const char *)data->data(), data->size());
+ }
- case DataOps::CompressionType::None:
- default:
- break;
- }
- return response;
- }
+ case DataOps::CompressionType::None:
+ default:
+ break;
+ }
+ return response;
+}
- EXPECT(String)
- HttpClient::RawGet(IN CONST String &path, IN Span headers, IN PCCHAR defaultContentType)
- {
- auto httpHeaders = BuildHeaders(headers, defaultContentType);
+Result
- auto res = m_client.Get((!path.empty() && path[0] != '/') ? ('/' + path).c_str() : path.c_str(), httpHeaders);
+HttpClient::RawGet(const String &path, Span headers,
+ const char *defaultContentType) {
+ auto httpHeaders = BuildHeaders(headers, defaultContentType);
- if (res)
- {
- m_lastResponseCode = static_cast(res->status);
- if (res->status >= 200 && res->status < 300)
- return PreprocessResponse(res->body);
- else
- return MakeUnexpected(std::format("HTTP Error {} : {}", res->status, res->body));
- }
+ auto res = m_client.Get(
+ (!path.empty() && path[0] != '/') ? ('/' + path).c_str() : path.c_str(),
+ httpHeaders);
- return MakeUnexpected(std::format("Network Error: {}", httplib::to_string(res.error())));
- }
+ if (res) {
+ m_lastResponseCode = static_cast(res->status);
+ if (res->status >= 200 && res->status < 300)
+ return PreprocessResponse(res->body);
+ else
+ return (std::format("HTTP Error {} : {}", res->status, res->body));
+ }
- EXPECT(String)
- HttpClient::RawPost(IN CONST String &path, IN Span headers, IN CONST String &body,
- IN PCCHAR defaultContentType)
- {
- auto httpHeaders = BuildHeaders(headers, defaultContentType);
+ return (std::format("Network Error: {}", httplib::to_string(res.error())));
+}
- String contentType = defaultContentType;
- if (httpHeaders.count("Content-Type"))
- {
- const auto t = httpHeaders.find("Content-Type");
- contentType = t->second;
- httpHeaders.erase(t);
- }
+Result
- m_client.set_keep_alive(true);
- auto res = m_client.Post((!path.empty() && path[0] != '/') ? ('/' + path).c_str() : path.c_str(), httpHeaders,
- body, contentType.c_str());
+HttpClient::RawPost(const String &path, Span headers,
+ const String &body, const char *defaultContentType) {
+ auto httpHeaders = BuildHeaders(headers, defaultContentType);
- if (res)
- {
- m_lastResponseCode = static_cast(res->status);
- if (res->status >= 200 && res->status < 300)
- return PreprocessResponse(res->body);
- else
- return MakeUnexpected(std::format("HTTP Error {} : {}", res->status, res->body));
- }
+ String contentType = defaultContentType;
+ if (httpHeaders.count("Content-Type")) {
+ const auto t = httpHeaders.find("Content-Type");
+ contentType = t->second;
+ httpHeaders.erase(t);
+ }
- return MakeUnexpected(std::format("Network Error: {}", httplib::to_string(res.error())));
- }
+ m_client.set_keep_alive(true);
+ auto res = m_client.Post(
+ (!path.empty() && path[0] != '/') ? ('/' + path).c_str() : path.c_str(),
+ httpHeaders, body, contentType.c_str());
+
+ if (res) {
+ m_lastResponseCode = static_cast(res->status);
+ if (res->status >= 200 && res->status < 300)
+ return PreprocessResponse(res->body);
+ else
+ return (std::format("HTTP Error {} : {}", res->status, res->body));
+ }
+
+ return (std::format("Network Error: {}", httplib::to_string(res.error())));
+}
} // namespace IACore
diff --git a/Src/IACore/imp/cpp/Http/Common.cpp b/Src/IACore/imp/cpp/Http/Common.cpp
index e313f91..e69ec09 100644
--- a/Src/IACore/imp/cpp/Http/Common.cpp
+++ b/Src/IACore/imp/cpp/Http/Common.cpp
@@ -15,110 +15,103 @@
#include
-namespace IACore
-{
- String HttpCommon::UrlEncode(IN CONST String &value)
- {
- std::stringstream escaped;
- escaped.fill('0');
- escaped << std::hex << std::uppercase;
+namespace IACore {
+String HttpCommon::UrlEncode(const String &value) {
+ std::stringstream escaped;
+ escaped.fill('0');
+ escaped << std::hex << std::uppercase;
- for (char c : value)
- {
- if (std::isalnum(static_cast(c)) || c == '-' || c == '_' || c == '.' || c == '~')
- escaped << c;
- else
- escaped << '%' << std::setw(2) << static_cast(static_cast(c));
- }
+ for (char c : value) {
+ if (std::isalnum(static_cast(c)) || c == '-' || c == '_' ||
+ c == '.' || c == '~')
+ escaped << c;
+ else
+ escaped << '%' << std::setw(2)
+ << static_cast(static_cast(c));
+ }
- return escaped.str();
- }
+ return escaped.str();
+}
- String HttpCommon::UrlDecode(IN CONST String &value)
- {
- String result;
- result.reserve(value.length());
+String HttpCommon::UrlDecode(const String &value) {
+ String result;
+ result.reserve(value.length());
- for (size_t i = 0; i < value.length(); ++i)
- {
- if (value[i] == '%' && i + 2 < value.length())
- {
- std::string hexStr = value.substr(i + 1, 2);
- char decodedChar = static_cast(std::strtol(hexStr.c_str(), nullptr, 16));
- result += decodedChar;
- i += 2;
- }
- else if (value[i] == '+')
- result += ' ';
- else
- result += value[i];
- }
+ for (size_t i = 0; i < value.length(); ++i) {
+ if (value[i] == '%' && i + 2 < value.length()) {
+ std::string hexStr = value.substr(i + 1, 2);
+ char decodedChar =
+ static_cast(std::strtol(hexStr.c_str(), nullptr, 16));
+ result += decodedChar;
+ i += 2;
+ } else if (value[i] == '+')
+ result += ' ';
+ else
+ result += value[i];
+ }
- return result;
- }
+ return result;
+}
- String HttpCommon::HeaderTypeToString(IN EHeaderType type)
- {
- switch (type)
- {
- case EHeaderType::ACCEPT:
- return "Accept";
- case EHeaderType::ACCEPT_CHARSET:
- return "Accept-Charset";
- case EHeaderType::ACCEPT_ENCODING:
- return "Accept-Encoding";
- case EHeaderType::ACCEPT_LANGUAGE:
- return "Accept-Language";
- case EHeaderType::AUTHORIZATION:
- return "Authorization";
- case EHeaderType::CACHE_CONTROL:
- return "Cache-Control";
- case EHeaderType::CONNECTION:
- return "Connection";
- case EHeaderType::CONTENT_LENGTH:
- return "Content-Length";
- case EHeaderType::CONTENT_TYPE:
- return "Content-Type";
- case EHeaderType::COOKIE:
- return "Cookie";
- case EHeaderType::DATE:
- return "Date";
- case EHeaderType::EXPECT:
- return "Expect";
- case EHeaderType::HOST:
- return "Host";
- case EHeaderType::IF_MATCH:
- return "If-Match";
- case EHeaderType::IF_MODIFIED_SINCE:
- return "If-Modified-Since";
- case EHeaderType::IF_NONE_MATCH:
- return "If-None-Match";
- case EHeaderType::ORIGIN:
- return "Origin";
- case EHeaderType::PRAGMA:
- return "Pragma";
- case EHeaderType::PROXY_AUTHORIZATION:
- return "Proxy-Authorization";
- case EHeaderType::RANGE:
- return "Range";
- case EHeaderType::REFERER:
- return "Referer";
- case EHeaderType::TE:
- return "TE";
- case EHeaderType::UPGRADE:
- return "Upgrade";
- case EHeaderType::USER_AGENT:
- return "User-Agent";
- case EHeaderType::VIA:
- return "Via";
- case EHeaderType::WARNING:
- return "Warning";
- }
- return "";
- }
+String HttpCommon::HeaderTypeToString(EHeaderType type) {
+ switch (type) {
+ case EHeaderType::ACCEPT:
+ return "Accept";
+ case EHeaderType::ACCEPT_CHARSET:
+ return "Accept-Charset";
+ case EHeaderType::ACCEPT_ENCODING:
+ return "Accept-Encoding";
+ case EHeaderType::ACCEPT_LANGUAGE:
+ return "Accept-Language";
+ case EHeaderType::AUTHORIZATION:
+ return "Authorization";
+ case EHeaderType::CACHE_CONTROL:
+ return "Cache-Control";
+ case EHeaderType::CONNECTION:
+ return "Connection";
+ case EHeaderType::CONTENT_LENGTH:
+ return "Content-Length";
+ case EHeaderType::CONTENT_TYPE:
+ return "Content-Type";
+ case EHeaderType::COOKIE:
+ return "Cookie";
+ case EHeaderType::DATE:
+ return "Date";
+ case EHeaderType::EXPECT:
+ return "Expect";
+ case EHeaderType::HOST:
+ return "Host";
+ case EHeaderType::IF_MATCH:
+ return "If-Match";
+ case EHeaderType::IF_MODIFIED_SINCE:
+ return "If-Modified-Since";
+ case EHeaderType::IF_NONE_MATCH:
+ return "If-None-Match";
+ case EHeaderType::ORIGIN:
+ return "Origin";
+ case EHeaderType::PRAGMA:
+ return "Pragma";
+ case EHeaderType::PROXY_AUTHORIZATION:
+ return "Proxy-Authorization";
+ case EHeaderType::RANGE:
+ return "Range";
+ case EHeaderType::REFERER:
+ return "Referer";
+ case EHeaderType::TE:
+ return "TE";
+ case EHeaderType::UPGRADE:
+ return "Upgrade";
+ case EHeaderType::USER_AGENT:
+ return "User-Agent";
+ case EHeaderType::VIA:
+ return "Via";
+ case EHeaderType::WARNING:
+ return "Warning";
+ }
+ return "";
+}
- BOOL HttpCommon::IsSuccessResponseCode(IN EResponseCode code)
- {
- return (INT32) code >= 200 && (INT32) code < 300;
- }
+bool HttpCommon::IsSuccessResponseCode(EResponseCode code) {
+ return (i32)code >= 200 && (i32)code < 300;
+}
} // 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 6bea1e8..a04f141 100644
--- a/Src/IACore/imp/cpp/IACore.cpp
+++ b/Src/IACore/imp/cpp/IACore.cpp
@@ -17,76 +17,50 @@
#include
#include
+#include
+#include
namespace IACore
{
- HighResTimePoint g_startTime{};
- std::thread::id g_mainThreadID{};
- INT32 g_coreInitCount{};
+ auto g_start_time = std::chrono::high_resolution_clock::time_point{};
- VOID Initialize()
+ static auto g_main_thread_id = std::thread::id{};
+ static auto g_core_init_count = i32{0};
+
+ auto initialize() -> void
{
- g_coreInitCount++;
- if (g_coreInitCount > 1)
+ g_core_init_count++;
+ if (g_core_init_count > 1)
+ {
return;
- g_mainThreadID = std::this_thread::get_id();
- g_startTime = HighResClock::now();
- Logger::Initialize();
+ }
+
+ g_main_thread_id = std::this_thread::get_id();
+ g_start_time = std::chrono::high_resolution_clock::now();
+
+ Logger::initialize();
mi_option_set(mi_option_verbose, 0);
}
- VOID Terminate()
+ auto terminate() -> void
{
- g_coreInitCount--;
- if (g_coreInitCount > 0)
+ g_core_init_count--;
+ if (g_core_init_count > 0)
+ {
return;
- Logger::Terminate();
+ }
+
+ Logger::terminate();
}
- BOOL IsInitialized()
+ auto is_initialized() -> bool
{
- return g_coreInitCount > 0;
+ return g_core_init_count > 0;
}
- UINT64 GetUnixTime()
+ auto is_main_thread() -> bool
{
- auto now = std::chrono::system_clock::now();
- return std::chrono::duration_cast(now.time_since_epoch()).count();
- }
-
- UINT64 GetTicksCount()
- {
- return std::chrono::duration_cast(HighResClock::now() - g_startTime).count();
- }
-
- FLOAT64 GetSecondsCount()
- {
- return std::chrono::duration_cast(HighResClock::now() - g_startTime).count();
- }
-
- FLOAT32 GetRandom()
- {
- return static_cast(rand()) / static_cast(RAND_MAX);
- }
-
- UINT64 GetRandom(IN UINT64 max)
- {
- return max * GetRandom();
- }
-
- INT64 GetRandom(IN INT64 min, IN INT64 max)
- {
- return min + (max - min) * GetRandom();
- }
-
- VOID Sleep(IN UINT64 milliseconds)
- {
- std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
- }
-
- BOOL IsMainThread()
- {
- return std::this_thread::get_id() == g_mainThreadID;
+ 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 800823b..21af9e3 100644
--- a/Src/IACore/imp/cpp/IPC.cpp
+++ b/Src/IACore/imp/cpp/IPC.cpp
@@ -17,435 +17,441 @@
#include
#include
+#include
+#include
-namespace IACore
-{
- struct IPC_ConnectionDescriptor
- {
- String SocketPath;
- String SharedMemPath;
- UINT32 SharedMemSize;
+namespace IACore {
+// =============================================================================
+// Internal: Connection Descriptor
+// =============================================================================
+struct IpcConnectionDescriptor {
+ String socket_path;
+ String shared_mem_path;
+ u32 shared_mem_size;
- String Serialize() CONST
- {
- return std::format("{}|{}|{}|", SocketPath, SharedMemPath, SharedMemSize);
+ [[nodiscard]] auto serialize() const -> String {
+ return std::format("{}|{}|{}|", socket_path, shared_mem_path,
+ shared_mem_size);
+ }
+
+ static auto deserialize(StringView data) -> Option {
+ enum class ParseState { SocketPath, SharedMemPath, SharedMemSize };
+
+ IpcConnectionDescriptor result{};
+ usize t = 0;
+ auto state = ParseState::SocketPath;
+
+ for (usize i = 0; i < data.size(); ++i) {
+ if (data[i] != '|') {
+ continue;
+ }
+
+ switch (state) {
+ case ParseState::SocketPath:
+ result.socket_path = String(data.substr(t, i - t));
+ state = ParseState::SharedMemPath;
+ break;
+
+ case ParseState::SharedMemPath:
+ result.shared_mem_path = String(data.substr(t, i - t));
+ state = ParseState::SharedMemSize;
+ break;
+
+ case ParseState::SharedMemSize: {
+ const auto *start = data.data() + t;
+ const auto *end = data.data() + i;
+ if (std::from_chars(start, end, result.shared_mem_size).ec !=
+ std::errc{}) {
+ return std::nullopt;
}
-
- STATIC IPC_ConnectionDescriptor Deserialize(IN CONST String &data)
- {
- enum class EParseState
- {
- SocketPath,
- SharedMemPath,
- SharedMemSize
- };
-
- IPC_ConnectionDescriptor result{};
-
- SIZE_T t{};
- EParseState state{EParseState::SocketPath};
- for (SIZE_T i = 0; i < data.size(); i++)
- {
- if (data[i] != '|')
- continue;
-
- switch (state)
- {
- case EParseState::SocketPath:
- result.SocketPath = data.substr(t, i - t);
- state = EParseState::SharedMemPath;
- break;
-
- case EParseState::SharedMemPath:
- result.SharedMemPath = data.substr(t, i - t);
- state = EParseState::SharedMemSize;
- break;
-
- case EParseState::SharedMemSize: {
- if (std::from_chars(&data[t], &data[i], result.SharedMemSize).ec != std::errc{})
- return {};
- goto done_parsing;
- }
- }
- t = i + 1;
- }
-
- done_parsing:
- return result;
- }
- };
-} // namespace IACore
-
-namespace IACore
-{
- IPC_Node::~IPC_Node()
- {
- SocketOps::Close(m_socket); // SocketOps gracefully handles INVALID_SOCKET
+ return result;
+ }
+ }
+ t = i + 1;
}
+ return std::nullopt;
+ }
+};
- EXPECT(VOID) IPC_Node::Connect(IN PCCHAR connectionString)
- {
- auto desc = IPC_ConnectionDescriptor::Deserialize(connectionString);
- m_shmName = desc.SharedMemPath;
+// =============================================================================
+// IpcNode Implementation
+// =============================================================================
- m_socket = SocketOps::CreateUnixSocket();
- if (!SocketOps::ConnectUnixSocket(m_socket, desc.SocketPath.c_str()))
- return MakeUnexpected("Failed to create an unix socket");
+IpcNode::~IpcNode() {
+ if (m_socket != INVALID_SOCKET) {
+ SocketOps::close(m_socket);
+ }
+}
- auto mapRes = FileOps::MapSharedMemory(desc.SharedMemPath, desc.SharedMemSize, FALSE);
- if (!mapRes.has_value())
- return MakeUnexpected("Failed to map the shared memory");
+auto IpcNode::connect(const char *connection_string) -> Result {
+ const auto desc_opt = IpcConnectionDescriptor::deserialize(connection_string);
+ if (!desc_opt) {
+ return fail("Failed to parse connection string");
+ }
+ const auto &desc = *desc_opt;
+ m_shm_name = desc.shared_mem_path;
- m_sharedMemory = mapRes.value();
+ IA_TRY(m_socket, SocketOps::create_unix_socket());
+ IA_TRY_PURE(
+ SocketOps::connect_unix_socket(m_socket, desc.socket_path.c_str()));
- auto *layout = reinterpret_cast(m_sharedMemory);
+ u8 *mapped_ptr{};
+ IA_TRY(mapped_ptr, FileOps::map_shared_memory(desc.shared_mem_path,
+ desc.shared_mem_size, false));
+ m_shared_memory = mapped_ptr;
- if (layout->Meta.Magic != 0x49414950) // "IAIP"
- return MakeUnexpected("Invalid shared memory header signature");
+ auto *layout = reinterpret_cast(m_shared_memory);
- if (layout->Meta.Version != 1)
- return MakeUnexpected("IPC version mismatch");
+ if (layout->meta.magic != 0x49414950) // "IAIP"
+ {
+ return fail("Invalid shared memory header signature");
+ }
- PUINT8 moniDataPtr = m_sharedMemory + layout->MONI_DataOffset;
- PUINT8 minoDataPtr = m_sharedMemory + layout->MINO_DataOffset;
+ if (layout->meta.version != 1) {
+ return fail("IPC version mismatch");
+ }
- MONI = std::make_unique(
- &layout->MONI_Control, Span(moniDataPtr, static_cast(layout->MONI_DataSize)), FALSE);
+ u8 *moni_ptr = m_shared_memory + layout->moni_data_offset;
+ u8 *mino_ptr = m_shared_memory + layout->mino_data_offset;
- MINO = std::make_unique(
- &layout->MINO_Control, Span(minoDataPtr, static_cast(layout->MINO_DataSize)), FALSE);
+ m_moni = make_box