1 Commits

Author SHA1 Message Date
21a474b6f9 [FIX]: Window Build Fix 2026-01-26 22:55:39 +05:30
75 changed files with 5430 additions and 4643 deletions

View File

@ -1,10 +1,9 @@
---
Language: Cpp Language: Cpp
Standard: c++20 Standard: c++20
BasedOnStyle: Microsoft BasedOnStyle: Microsoft
IndentWidth: 4 IndentWidth: 2
TabWidth: 4 TabWidth: 2
UseTab: Never UseTab: Never
AccessModifierOffset: -4 AccessModifierOffset: -4
@ -28,5 +27,4 @@ SpaceInEmptyParentheses: false
SpacesInAngles: false SpacesInAngles: false
SeparateDefinitionBlocks: Always SeparateDefinitionBlocks: Always
SortIncludes: false SortIncludes: Never
---

View File

@ -18,6 +18,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
target: [x64-linux] target: [x64-linux]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -29,3 +30,23 @@ jobs:
- name: Build - name: Build
run: cmake --build --preset ${{ matrix.target }} --config Release run: cmake --build --preset ${{ matrix.target }} --config Release
build-windows:
runs-on: windows-latest
permissions:
contents: read
strategy:
fail-fast: false
matrix:
target: [x64-windows]
steps:
- uses: actions/checkout@v4
- uses: ilammy/msvc-dev-cmd@v1
- name: Configure
run: cmake --preset ${{ matrix.target }}
- name: Build
run: cmake --build --preset ${{ matrix.target }} --config Release

View File

@ -92,6 +92,8 @@ FetchContent_Declare(
EXCLUDE_FROM_ALL EXCLUDE_FROM_ALL
) )
set(PUGIXML_NO_EXCEPTIONS ON)
FetchContent_Declare( FetchContent_Declare(
pugixml pugixml
GIT_REPOSITORY https://github.com/zeux/pugixml.git GIT_REPOSITORY https://github.com/zeux/pugixml.git

View File

@ -18,6 +18,8 @@ macro(iacore_setup_project)
add_compile_options(-Wall -Wextra -Wpedantic -Wno-language-extension-token) add_compile_options(-Wall -Wextra -Wpedantic -Wno-language-extension-token)
endif() endif()
add_compile_options(-Wno-missing-field-initializers -Wno-missing-designated-field-initializers)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64") if(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64")
set(IACORE_ARCH_X64 TRUE CACHE INTERNAL "") set(IACORE_ARCH_X64 TRUE CACHE INTERNAL "")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64") elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64")

View File

@ -4,6 +4,8 @@ set(CMAKE_C_COMPILER clang-cl)
set(CMAKE_CXX_COMPILER clang-cl) set(CMAKE_CXX_COMPILER clang-cl)
set(CMAKE_RC_COMPILER llvm-rc) set(CMAKE_RC_COMPILER llvm-rc)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded")
set(triple x86_64-pc-windows-msvc) set(triple x86_64-pc-windows-msvc)
set(CMAKE_C_COMPILER_TARGET ${triple}) set(CMAKE_C_COMPILER_TARGET ${triple})
set(CMAKE_CXX_COMPILER_TARGET ${triple}) set(CMAKE_CXX_COMPILER_TARGET ${triple})

View File

@ -0,0 +1,48 @@
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR AMD64)
set(CMAKE_C_COMPILER clang-cl)
set(CMAKE_CXX_COMPILER clang-cl)
set(CMAKE_RC_COMPILER llvm-rc)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded")
set(triple x86_64-pc-windows-msvc)
set(CMAKE_C_COMPILER_TARGET ${triple})
set(CMAKE_CXX_COMPILER_TARGET ${triple})
set(CMAKE_LINKER lld-link)
string(APPEND CMAKE_C_FLAGS " /arch:AVX2 /clang:-mfma")
string(APPEND CMAKE_CXX_FLAGS " /arch:AVX2 /clang:-mfma")
# ---------------------------------------------------------------------
# WinSDK directory can be splatted on linux using 'xwin' cargo package
# Please also ensure to place OpenSSL windows binaries in WINSDK_PATH/openssl/x64
# OpenSSL minimum required version is 3.0.0
if(NOT DEFINED ENV{WINSDK_PATH} OR "$ENV{WINSDK_PATH}" STREQUAL "")
message(FATAL_ERROR "Environment variable WINSDK_PATH is not set. Please set it before running the cross toolchain.")
endif()
message(STATUS "Using Windows SDK at : $ENV{WINSDK_PATH}")
set(OPENSSL_ROOT_DIR "$ENV{WINSDK_PATH}/openssl/x64")
set(OPENSSL_USE_STATIC_LIBS TRUE)
if(NOT EXISTS "${OPENSSL_ROOT_DIR}")
message(FATAL_ERROR "OpenSSL directory not found: ${OPENSSL_ROOT_DIR}")
elseif(NOT IS_DIRECTORY "${OPENSSL_ROOT_DIR}")
message(FATAL_ERROR "OpenSSL path exists but is not a directory: ${OPENSSL_ROOT_DIR}")
endif()
string(APPEND CMAKE_C_FLAGS " /imsvc $ENV{WINSDK_PATH}/crt/include /imsvc $ENV{WINSDK_PATH}/sdk/include/ucrt /imsvc $ENV{WINSDK_PATH}/sdk/include/um /imsvc $ENV{WINSDK_PATH}/sdk/include/shared")
string(APPEND CMAKE_CXX_FLAGS " /imsvc $ENV{WINSDK_PATH}/crt/include /imsvc $ENV{WINSDK_PATH}/sdk/include/ucrt /imsvc $ENV{WINSDK_PATH}/sdk/include/um /imsvc $ENV{WINSDK_PATH}/sdk/include/shared")
set(CRT_LIB_PATH "$ENV{WINSDK_PATH}/crt/lib/x86_64")
set(SDK_LIB_PATH "$ENV{WINSDK_PATH}/sdk/lib")
set(CMAKE_EXE_LINKER_FLAGS
"${CMAKE_EXE_LINKER_FLAGS} /libpath:\"${CRT_LIB_PATH}\" /libpath:\"${SDK_LIB_PATH}/ucrt/x86_64\" /libpath:\"${SDK_LIB_PATH}/um/x86_64\""
)
# ---------------------------------------------------------------------

View File

@ -47,10 +47,19 @@
} }
}, },
{ {
"name": "wasm", "name": "wasm32-emscripten",
"displayName": "IACore WebAssembly (Clang)", "displayName": "IACore WebAssembly (Clang)",
"inherits": "iacore-base", "inherits": "iacore-base",
"cacheVariables": {} "cacheVariables": {}
},
{
"name": "x64-windows-cross",
"displayName": "IACore Windows x64 Cross (Linux Host) (Clang)",
"inherits": "iacore-base",
"cacheVariables": {
"CMAKE_C_COMPILER": "clang-cl",
"CMAKE_CXX_COMPILER": "clang-cl"
}
} }
], ],
"buildPresets": [ "buildPresets": [
@ -71,8 +80,12 @@
"configurePreset": "arm64-windows" "configurePreset": "arm64-windows"
}, },
{ {
"name": "wasm", "name": "wasm32-emscripten",
"configurePreset": "wasm" "configurePreset": "wasm32-emscripten"
},
{
"name": "x64-windows-cross",
"configurePreset": "x64-windows-cross"
} }
] ]
} }

View File

@ -57,7 +57,11 @@ endif()
target_precompile_headers(IACore PUBLIC inc/IACore/PCH.hpp) target_precompile_headers(IACore PUBLIC inc/IACore/PCH.hpp)
set(NO_EXCEPT_FLAG "$<IF:$<CXX_COMPILER_ID:MSVC>,/EHs-c-,-fno-exceptions>") if(MSVC)
set(NO_EXCEPT_FLAG "/EHs-c-")
else()
set(NO_EXCEPT_FLAG "-fno-exceptions")
endif()
target_compile_options(IACore PRIVATE ${NO_EXCEPT_FLAG}) target_compile_options(IACore PRIVATE ${NO_EXCEPT_FLAG})
target_compile_options(IACore INTERFACE target_compile_options(IACore INTERFACE
$<$<NOT:$<BOOL:$<TARGET_PROPERTY:USE_EXCEPTIONS>>>:${NO_EXCEPT_FLAG}> $<$<NOT:$<BOOL:$<TARGET_PROPERTY:USE_EXCEPTIONS>>>:${NO_EXCEPT_FLAG}>
@ -71,6 +75,7 @@ define_property(TARGET PROPERTY USE_EXCEPTIONS
target_compile_definitions(IACore PRIVATE target_compile_definitions(IACore PRIVATE
CPPHTTPLIB_OPENSSL_SUPPORT CPPHTTPLIB_OPENSSL_SUPPORT
CPPHTTPLIB_ZLIB_SUPPORT CPPHTTPLIB_ZLIB_SUPPORT
_CRT_SECURE_NO_WARNINGS
NOMINMAX NOMINMAX
) )

View File

@ -15,89 +15,104 @@
#include <IACore/AsyncOps.hpp> #include <IACore/AsyncOps.hpp>
namespace IACore { namespace IACore
Mut<std::mutex> AsyncOps::s_queue_mutex; {
Mut<std::condition_variable> AsyncOps::s_wake_condition; Mut<std::mutex> AsyncOps::s_queue_mutex;
Mut<Vec<std::jthread>> AsyncOps::s_schedule_workers; Mut<std::condition_variable> AsyncOps::s_wake_condition;
Mut<std::deque<AsyncOps::ScheduledTask>> AsyncOps::s_high_priority_queue; Mut<Vec<std::jthread>> AsyncOps::s_schedule_workers;
Mut<std::deque<AsyncOps::ScheduledTask>> AsyncOps::s_normal_priority_queue; Mut<std::deque<AsyncOps::ScheduledTask>> AsyncOps::s_high_priority_queue;
Mut<std::deque<AsyncOps::ScheduledTask>> AsyncOps::s_normal_priority_queue;
auto AsyncOps::run_task(Mut<std::function<void()>> task) -> void { auto AsyncOps::run_task(Mut<std::function<void()>> task) -> void
{
std::jthread(std::move(task)).detach(); std::jthread(std::move(task)).detach();
} }
auto AsyncOps::initialize_scheduler(Mut<u8> worker_count) -> Result<void> { auto AsyncOps::initialize_scheduler(Mut<u8> worker_count) -> Result<void>
if (worker_count == 0) { {
if (worker_count == 0)
{
const u32 hw_concurrency = std::thread::hardware_concurrency(); const u32 hw_concurrency = std::thread::hardware_concurrency();
Mut<u32> threads = 2; Mut<u32> threads = 2;
if (hw_concurrency > 2) { if (hw_concurrency > 2)
{
threads = hw_concurrency - 2; threads = hw_concurrency - 2;
} }
if (threads > 255) { if (threads > 255)
{
threads = 255; threads = 255;
} }
worker_count = static_cast<u8>(threads); worker_count = static_cast<u8>(threads);
} }
for (Mut<u32> i = 0; i < worker_count; ++i) { for (Mut<u32> i = 0; i < worker_count; ++i)
s_schedule_workers.emplace_back(schedule_worker_loop, {
static_cast<WorkerId>(i + 1)); s_schedule_workers.emplace_back(schedule_worker_loop, static_cast<WorkerId>(i + 1));
} }
return {}; return {};
} }
auto AsyncOps::terminate_scheduler() -> void { auto AsyncOps::terminate_scheduler() -> void
for (MutRef<std::jthread> worker : s_schedule_workers) { {
for (MutRef<std::jthread> worker : s_schedule_workers)
{
worker.request_stop(); worker.request_stop();
} }
s_wake_condition.notify_all(); s_wake_condition.notify_all();
for (MutRef<std::jthread> worker : s_schedule_workers) { for (MutRef<std::jthread> worker : s_schedule_workers)
if (worker.joinable()) { {
if (worker.joinable())
{
worker.join(); worker.join();
} }
} }
s_schedule_workers.clear(); s_schedule_workers.clear();
} }
auto AsyncOps::schedule_task(Mut<std::function<void(WorkerId worker_id)>> task, auto AsyncOps::schedule_task(Mut<std::function<void(WorkerId worker_id)>> task, const TaskTag tag, Schedule *schedule,
const TaskTag tag, Schedule *schedule, const Priority priority) -> void
const Priority priority) -> void { {
ensure(!s_schedule_workers.empty(), ensure(!s_schedule_workers.empty(), "Scheduler must be initialized before calling schedule_task");
"Scheduler must be initialized before calling schedule_task");
schedule->counter.fetch_add(1); schedule->counter.fetch_add(1);
{ {
const std::lock_guard<std::mutex> lock(s_queue_mutex); const std::lock_guard<std::mutex> lock(s_queue_mutex);
if (priority == Priority::High) { if (priority == Priority::High)
s_high_priority_queue.emplace_back( {
ScheduledTask{tag, schedule, std::move(task)}); s_high_priority_queue.emplace_back(ScheduledTask{tag, schedule, std::move(task)});
} else { }
s_normal_priority_queue.emplace_back( else
ScheduledTask{tag, schedule, std::move(task)}); {
s_normal_priority_queue.emplace_back(ScheduledTask{tag, schedule, std::move(task)});
} }
} }
s_wake_condition.notify_one(); s_wake_condition.notify_one();
} }
auto AsyncOps::cancel_tasks_of_tag(const TaskTag tag) -> void { auto AsyncOps::cancel_tasks_of_tag(const TaskTag tag) -> void
{
const std::lock_guard<std::mutex> lock(s_queue_mutex); const std::lock_guard<std::mutex> lock(s_queue_mutex);
{ {
MutRef<std::deque<ScheduledTask>> queue = s_high_priority_queue; MutRef<std::deque<ScheduledTask>> queue = s_high_priority_queue;
for (Mut<std::deque<ScheduledTask>::iterator> it = queue.begin(); for (Mut<std::deque<ScheduledTask>::iterator> it = queue.begin(); it != queue.end();
it != queue.end(); /* no incr */)
/* no incr */) { {
if (it->tag == tag) { if (it->tag == tag)
if (it->schedule_handle->counter.fetch_sub(1) == 1) { {
if (it->schedule_handle->counter.fetch_sub(1) == 1)
{
it->schedule_handle->counter.notify_all(); it->schedule_handle->counter.notify_all();
} }
it = queue.erase(it); it = queue.erase(it);
} else { }
else
{
++it; ++it;
} }
} }
@ -105,95 +120,115 @@ auto AsyncOps::cancel_tasks_of_tag(const TaskTag tag) -> void {
{ {
MutRef<std::deque<ScheduledTask>> queue = s_normal_priority_queue; MutRef<std::deque<ScheduledTask>> queue = s_normal_priority_queue;
for (Mut<std::deque<ScheduledTask>::iterator> it = queue.begin(); for (Mut<std::deque<ScheduledTask>::iterator> it = queue.begin(); it != queue.end();
it != queue.end(); /* no incr */)
/* no incr */) { {
if (it->tag == tag) { if (it->tag == tag)
if (it->schedule_handle->counter.fetch_sub(1) == 1) { {
if (it->schedule_handle->counter.fetch_sub(1) == 1)
{
it->schedule_handle->counter.notify_all(); it->schedule_handle->counter.notify_all();
} }
it = queue.erase(it); it = queue.erase(it);
} else { }
else
{
++it; ++it;
} }
} }
} }
} }
auto AsyncOps::wait_for_schedule_completion(Schedule *schedule) -> void { auto AsyncOps::wait_for_schedule_completion(Schedule *schedule) -> void
{
ensure(!s_schedule_workers.empty(), "Scheduler must be initialized before " ensure(!s_schedule_workers.empty(), "Scheduler must be initialized before "
"calling wait_for_schedule_completion"); "calling wait_for_schedule_completion");
while (schedule->counter.load() > 0) { while (schedule->counter.load() > 0)
{
Mut<ScheduledTask> task; Mut<ScheduledTask> task;
Mut<bool> found_task = false; Mut<bool> found_task = false;
{ {
Mut<std::unique_lock<std::mutex>> lock(s_queue_mutex); Mut<std::unique_lock<std::mutex>> lock(s_queue_mutex);
if (!s_high_priority_queue.empty()) { if (!s_high_priority_queue.empty())
{
task = std::move(s_high_priority_queue.front()); task = std::move(s_high_priority_queue.front());
s_high_priority_queue.pop_front(); s_high_priority_queue.pop_front();
found_task = true; found_task = true;
} else if (!s_normal_priority_queue.empty()) { }
else if (!s_normal_priority_queue.empty())
{
task = std::move(s_normal_priority_queue.front()); task = std::move(s_normal_priority_queue.front());
s_normal_priority_queue.pop_front(); s_normal_priority_queue.pop_front();
found_task = true; found_task = true;
} }
} }
if (found_task) { if (found_task)
{
task.task(MAIN_THREAD_WORKER_ID); task.task(MAIN_THREAD_WORKER_ID);
if (task.schedule_handle->counter.fetch_sub(1) == 1) { if (task.schedule_handle->counter.fetch_sub(1) == 1)
{
task.schedule_handle->counter.notify_all(); task.schedule_handle->counter.notify_all();
} }
} else { }
else
{
const u32 current_val = schedule->counter.load(); const u32 current_val = schedule->counter.load();
if (current_val > 0) { if (current_val > 0)
{
schedule->counter.wait(current_val); schedule->counter.wait(current_val);
} }
} }
} }
} }
auto AsyncOps::get_worker_count() -> WorkerId { auto AsyncOps::get_worker_count() -> WorkerId
{
return static_cast<WorkerId>(s_schedule_workers.size()); return static_cast<WorkerId>(s_schedule_workers.size());
} }
auto AsyncOps::schedule_worker_loop(const std::stop_token stop_token, auto AsyncOps::schedule_worker_loop(const std::stop_token stop_token, const WorkerId worker_id) -> void
const WorkerId worker_id) -> void { {
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested())
{
Mut<ScheduledTask> task; Mut<ScheduledTask> task;
Mut<bool> found_task = false; Mut<bool> found_task = false;
{ {
Mut<std::unique_lock<std::mutex>> lock(s_queue_mutex); Mut<std::unique_lock<std::mutex>> lock(s_queue_mutex);
s_wake_condition.wait(lock, [&stop_token] { s_wake_condition.wait(lock, [&stop_token] {
return !s_high_priority_queue.empty() || return !s_high_priority_queue.empty() || !s_normal_priority_queue.empty() || stop_token.stop_requested();
!s_normal_priority_queue.empty() || stop_token.stop_requested();
}); });
if (stop_token.stop_requested() && s_high_priority_queue.empty() && if (stop_token.stop_requested() && s_high_priority_queue.empty() && s_normal_priority_queue.empty())
s_normal_priority_queue.empty()) { {
return; return;
} }
if (!s_high_priority_queue.empty()) { if (!s_high_priority_queue.empty())
{
task = std::move(s_high_priority_queue.front()); task = std::move(s_high_priority_queue.front());
s_high_priority_queue.pop_front(); s_high_priority_queue.pop_front();
found_task = true; found_task = true;
} else if (!s_normal_priority_queue.empty()) { }
else if (!s_normal_priority_queue.empty())
{
task = std::move(s_normal_priority_queue.front()); task = std::move(s_normal_priority_queue.front());
s_normal_priority_queue.pop_front(); s_normal_priority_queue.pop_front();
found_task = true; found_task = true;
} }
} }
if (found_task) { if (found_task)
{
task.task(worker_id); task.task(worker_id);
if (task.schedule_handle->counter.fetch_sub(1) == 1) { if (task.schedule_handle->counter.fetch_sub(1) == 1)
{
task.schedule_handle->counter.notify_all(); task.schedule_handle->counter.notify_all();
} }
} }
} }
} }
} // namespace IACore } // namespace IACore

View File

@ -15,13 +15,16 @@
#include <IACore/CLI.hpp> #include <IACore/CLI.hpp>
namespace IACore { namespace IACore
CLIParser::CLIParser(const Span<const String> args) : m_arg_list(args) { {
CLIParser::CLIParser(const Span<const String> args) : m_arg_list(args)
{
m_current_arg = m_arg_list.begin(); m_current_arg = m_arg_list.begin();
// Skip executable path // Skip executable path
if (m_current_arg != m_arg_list.end()) { if (m_current_arg != m_arg_list.end())
{
m_current_arg++; m_current_arg++;
} }
} }
} // namespace IACore } // namespace IACore

View File

@ -22,157 +22,172 @@
#include <zstd.h> #include <zstd.h>
#if IA_ARCH_X64 #if IA_ARCH_X64
#include <immintrin.h> # include <immintrin.h>
#endif #endif
#if IA_ARCH_ARM64 #if IA_ARCH_ARM64
#include <arm_acle.h> # include <arm_acle.h>
#endif #endif
namespace IACore { namespace IACore
template <typename T> {
[[nodiscard]] inline auto read_unaligned(const u8 *ptr) -> T { template<typename T> [[nodiscard]] inline auto read_unaligned(const u8 *ptr) -> T
{
Mut<T> v; Mut<T> v;
std::memcpy(&v, ptr, sizeof(T)); std::memcpy(&v, ptr, sizeof(T));
return v; return v;
} }
struct Crc32Tables { struct Crc32Tables
{
Mut<u32> table[8][256] = {}; Mut<u32> table[8][256] = {};
consteval Crc32Tables() { consteval Crc32Tables()
{
constexpr const u32 T = 0x82F63B78; constexpr const u32 T = 0x82F63B78;
for (Mut<u32> i = 0; i < 256; i++) { for (Mut<u32> i = 0; i < 256; i++)
{
Mut<u32> crc = i; Mut<u32> crc = i;
for (Mut<i32> j = 0; j < 8; j++) { for (Mut<i32> j = 0; j < 8; j++)
{
crc = (crc >> 1) ^ ((crc & 1) ? T : 0); crc = (crc >> 1) ^ ((crc & 1) ? T : 0);
} }
table[0][i] = crc; table[0][i] = crc;
} }
for (Mut<i32> i = 0; i < 256; i++) { for (Mut<i32> i = 0; i < 256; i++)
for (Mut<i32> slice = 1; slice < 8; slice++) { {
for (Mut<i32> slice = 1; slice < 8; slice++)
{
const u32 prev = table[slice - 1][i]; const u32 prev = table[slice - 1][i];
table[slice][i] = (prev >> 8) ^ table[0][prev & 0xFF]; table[slice][i] = (prev >> 8) ^ table[0][prev & 0xFF];
} }
} }
} }
}; };
static constexpr const Crc32Tables CRC32_TABLES{}; static constexpr const Crc32Tables CRC32_TABLES{};
#if IA_ARCH_X64 #if IA_ARCH_X64
inline auto crc32_x64_hw(Ref<Span<const u8>> data) -> u32 { inline auto crc32_x64_hw(Ref<Span<const u8>> data) -> u32
{
Mut<const u8 *> p = data.data(); Mut<const u8 *> p = data.data();
Mut<u32> crc = 0xFFFFFFFF; Mut<u32> crc = 0xFFFFFFFF;
Mut<usize> len = data.size(); Mut<usize> len = data.size();
while (len >= 8) { while (len >= 8)
{
const u64 chunk = read_unaligned<u64>(p); const u64 chunk = read_unaligned<u64>(p);
crc = static_cast<u32>(_mm_crc32_u64(static_cast<u64>(crc), chunk)); crc = static_cast<u32>(_mm_crc32_u64(static_cast<u64>(crc), chunk));
p += 8; p += 8;
len -= 8; len -= 8;
} }
while (len--) { while (len--)
{
crc = _mm_crc32_u8(crc, *p++); crc = _mm_crc32_u8(crc, *p++);
} }
return ~crc; return ~crc;
} }
#endif #endif
#if IA_ARCH_ARM64 #if IA_ARCH_ARM64
__attribute__((target("+crc"))) inline auto __attribute__((target("+crc"))) inline auto crc32_arm64_hw(Ref<Span<const u8>> data) -> u32
crc32_arm64_hw(Ref<Span<const u8>> data) -> u32 { {
Mut<const u8 *> p = data.data(); Mut<const u8 *> p = data.data();
Mut<u32> crc = 0xFFFFFFFF; Mut<u32> crc = 0xFFFFFFFF;
Mut<usize> len = data.size(); Mut<usize> len = data.size();
while (len >= 8) { while (len >= 8)
{
const u64 chunk = read_unaligned<u64>(p); const u64 chunk = read_unaligned<u64>(p);
crc = __crc32cd(crc, chunk); crc = __crc32cd(crc, chunk);
p += 8; p += 8;
len -= 8; len -= 8;
} }
while (len--) { while (len--)
{
crc = __crc32cb(crc, *p++); crc = __crc32cb(crc, *p++);
} }
return ~crc; return ~crc;
} }
#endif #endif
inline auto crc32_software_slice8(Ref<Span<const u8>> data) -> u32 { inline auto crc32_software_slice8(Ref<Span<const u8>> data) -> u32
{
Mut<const u8 *> p = data.data(); Mut<const u8 *> p = data.data();
Mut<u32> crc = 0xFFFFFFFF; Mut<u32> crc = 0xFFFFFFFF;
Mut<usize> len = data.size(); Mut<usize> len = data.size();
while (len >= 8) { while (len >= 8)
{
const u32 term1 = crc ^ read_unaligned<u32>(p); const u32 term1 = crc ^ read_unaligned<u32>(p);
const u32 term2 = read_unaligned<u32>(p + 4); const u32 term2 = read_unaligned<u32>(p + 4);
crc = CRC32_TABLES.table[7][term1 & 0xFF] ^ crc = CRC32_TABLES.table[7][term1 & 0xFF] ^ CRC32_TABLES.table[6][(term1 >> 8) & 0xFF] ^
CRC32_TABLES.table[6][(term1 >> 8) & 0xFF] ^ CRC32_TABLES.table[5][(term1 >> 16) & 0xFF] ^ CRC32_TABLES.table[4][(term1 >> 24)] ^
CRC32_TABLES.table[5][(term1 >> 16) & 0xFF] ^ CRC32_TABLES.table[3][term2 & 0xFF] ^ CRC32_TABLES.table[2][(term2 >> 8) & 0xFF] ^
CRC32_TABLES.table[4][(term1 >> 24)] ^ CRC32_TABLES.table[1][(term2 >> 16) & 0xFF] ^ CRC32_TABLES.table[0][(term2 >> 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; p += 8;
len -= 8; len -= 8;
} }
while (len--) { while (len--)
{
crc = (crc >> 8) ^ CRC32_TABLES.table[0][(crc ^ *p++) & 0xFF]; crc = (crc >> 8) ^ CRC32_TABLES.table[0][(crc ^ *p++) & 0xFF];
} }
return ~crc; return ~crc;
} }
auto DataOps::crc32(Ref<Span<const u8>> data) -> u32 { auto DataOps::crc32(Ref<Span<const u8>> data) -> u32
{
#if IA_ARCH_X64 #if IA_ARCH_X64
// IACore mandates AVX2 so no need to check // IACore mandates AVX2 so no need to check
return crc32_x64_hw(data); return crc32_x64_hw(data);
#elif IA_ARCH_ARM64 #elif IA_ARCH_ARM64
if (Platform::GetCapabilities().HardwareCRC32) { if (Platform::GetCapabilities().HardwareCRC32)
{
return crc32_arm64_hw(data); return crc32_arm64_hw(data);
} }
#endif #endif
return crc32_software_slice8(data); return crc32_software_slice8(data);
} }
constexpr const u32 XXH_PRIME32_1 = 0x9E3779B1U; constexpr const u32 XXH_PRIME32_1 = 0x9E3779B1U;
constexpr const u32 XXH_PRIME32_2 = 0x85EBCA77U; constexpr const u32 XXH_PRIME32_2 = 0x85EBCA77U;
constexpr const u32 XXH_PRIME32_3 = 0xC2B2AE3DU; constexpr const u32 XXH_PRIME32_3 = 0xC2B2AE3DU;
constexpr const u32 XXH_PRIME32_4 = 0x27D4EB2FU; constexpr const u32 XXH_PRIME32_4 = 0x27D4EB2FU;
constexpr const u32 XXH_PRIME32_5 = 0x165667B1U; constexpr const u32 XXH_PRIME32_5 = 0x165667B1U;
inline auto xxh32_round(Mut<u32> seed, const u32 input) -> u32 { inline auto xxh32_round(Mut<u32> seed, const u32 input) -> u32
{
seed += input * XXH_PRIME32_2; seed += input * XXH_PRIME32_2;
seed = std::rotl(seed, 13); seed = std::rotl(seed, 13);
seed *= XXH_PRIME32_1; seed *= XXH_PRIME32_1;
return seed; return seed;
} }
auto DataOps::hash_xxhash(Ref<String> string, const u32 seed) -> u32 { auto DataOps::hash_xxhash(Ref<String> string, const u32 seed) -> u32
return hash_xxhash(Span<const u8>(reinterpret_cast<const u8 *>(string.data()), {
string.length()), return hash_xxhash(Span<const u8>(reinterpret_cast<const u8 *>(string.data()), string.length()), seed);
seed); }
}
auto DataOps::hash_xxhash(Ref<Span<const u8>> data, const u32 seed) -> u32 { auto DataOps::hash_xxhash(Ref<Span<const u8>> data, const u32 seed) -> u32
{
Mut<const u8 *> p = data.data(); Mut<const u8 *> p = data.data();
const u8 *b_end = p + data.size(); const u8 *b_end = p + data.size();
Mut<u32> h32{}; Mut<u32> h32{};
if (data.size() >= 16) { if (data.size() >= 16)
{
const u8 *limit = b_end - 16; const u8 *limit = b_end - 16;
Mut<u32> v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2; Mut<u32> v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2;
@ -180,7 +195,8 @@ auto DataOps::hash_xxhash(Ref<Span<const u8>> data, const u32 seed) -> u32 {
Mut<u32> v3 = seed + 0; Mut<u32> v3 = seed + 0;
Mut<u32> v4 = seed - XXH_PRIME32_1; Mut<u32> v4 = seed - XXH_PRIME32_1;
do { do
{
v1 = xxh32_round(v1, read_unaligned<u32>(p)); v1 = xxh32_round(v1, read_unaligned<u32>(p));
p += 4; p += 4;
v2 = xxh32_round(v2, read_unaligned<u32>(p)); v2 = xxh32_round(v2, read_unaligned<u32>(p));
@ -191,22 +207,25 @@ auto DataOps::hash_xxhash(Ref<Span<const u8>> data, const u32 seed) -> u32 {
p += 4; p += 4;
} while (p <= limit); } while (p <= limit);
h32 = std::rotl(v1, 1) + std::rotl(v2, 7) + std::rotl(v3, 12) + h32 = std::rotl(v1, 1) + std::rotl(v2, 7) + std::rotl(v3, 12) + std::rotl(v4, 18);
std::rotl(v4, 18); }
} else { else
{
h32 = seed + XXH_PRIME32_5; h32 = seed + XXH_PRIME32_5;
} }
h32 += static_cast<u32>(data.size()); h32 += static_cast<u32>(data.size());
while (p + 4 <= b_end) { while (p + 4 <= b_end)
{
const u32 t = read_unaligned<u32>(p) * XXH_PRIME32_3; const u32 t = read_unaligned<u32>(p) * XXH_PRIME32_3;
h32 += t; h32 += t;
h32 = std::rotl(h32, 17) * XXH_PRIME32_4; h32 = std::rotl(h32, 17) * XXH_PRIME32_4;
p += 4; p += 4;
} }
while (p < b_end) { while (p < b_end)
{
h32 += (*p++) * XXH_PRIME32_5; h32 += (*p++) * XXH_PRIME32_5;
h32 = std::rotl(h32, 11) * XXH_PRIME32_1; h32 = std::rotl(h32, 11) * XXH_PRIME32_1;
} }
@ -218,55 +237,64 @@ auto DataOps::hash_xxhash(Ref<Span<const u8>> data, const u32 seed) -> u32 {
h32 ^= h32 >> 16; h32 ^= h32 >> 16;
return h32; return h32;
} }
constexpr const u32 FNV1A_32_PRIME = 0x01000193; constexpr const u32 FNV1A_32_PRIME = 0x01000193;
constexpr const u32 FNV1A_32_OFFSET = 0x811c9dc5; constexpr const u32 FNV1A_32_OFFSET = 0x811c9dc5;
auto DataOps::hash_fnv1a(Ref<String> string) -> u32 { auto DataOps::hash_fnv1a(Ref<String> string) -> u32
{
Mut<u32> hash = FNV1A_32_OFFSET; Mut<u32> hash = FNV1A_32_OFFSET;
for (const char c : string) { for (const char c : string)
{
hash ^= static_cast<u8>(c); hash ^= static_cast<u8>(c);
hash *= FNV1A_32_PRIME; hash *= FNV1A_32_PRIME;
} }
return hash; return hash;
} }
auto DataOps::hash_fnv1a(Ref<Span<const u8>> data) -> u32 { auto DataOps::hash_fnv1a(Ref<Span<const u8>> data) -> u32
{
Mut<u32> hash = FNV1A_32_OFFSET; Mut<u32> hash = FNV1A_32_OFFSET;
const u8 *ptr = data.data(); const u8 *ptr = data.data();
for (Mut<usize> i = 0; i < data.size(); ++i) { for (Mut<usize> i = 0; i < data.size(); ++i)
{
hash ^= ptr[i]; hash ^= ptr[i];
hash *= FNV1A_32_PRIME; hash *= FNV1A_32_PRIME;
} }
return hash; return hash;
} }
auto DataOps::detect_compression(const Span<const u8> data) -> CompressionType { auto DataOps::detect_compression(const Span<const u8> data) -> CompressionType
if (data.size() < 2) { {
if (data.size() < 2)
{
return CompressionType::None; return CompressionType::None;
} }
if (data[0] == 0x1F && data[1] == 0x8B) { if (data[0] == 0x1F && data[1] == 0x8B)
{
return CompressionType::Gzip; return CompressionType::Gzip;
} }
if (data[0] == 0x78 && if (data[0] == 0x78 && (data[1] == 0x01 || data[1] == 0x9C || data[1] == 0xDA))
(data[1] == 0x01 || data[1] == 0x9C || data[1] == 0xDA)) { {
return CompressionType::Zlib; return CompressionType::Zlib;
} }
return CompressionType::None; return CompressionType::None;
} }
auto DataOps::zlib_inflate(Ref<Span<const u8>> data) -> Result<Vec<u8>> { auto DataOps::zlib_inflate(Ref<Span<const u8>> data) -> Result<Vec<u8>>
{
Mut<z_stream> zs{}; Mut<z_stream> zs{};
zs.zalloc = Z_NULL; zs.zalloc = Z_NULL;
zs.zfree = Z_NULL; zs.zfree = Z_NULL;
zs.opaque = Z_NULL; zs.opaque = Z_NULL;
if (inflateInit2(&zs, 15 + 32) != Z_OK) { if (inflateInit2(&zs, 15 + 32) != Z_OK)
{
return fail("Failed to initialize zlib inflate"); return fail("Failed to initialize zlib inflate");
} }
@ -274,16 +302,17 @@ auto DataOps::zlib_inflate(Ref<Span<const u8>> data) -> Result<Vec<u8>> {
zs.avail_in = static_cast<uInt>(data.size()); zs.avail_in = static_cast<uInt>(data.size());
Mut<Vec<u8>> out_buffer; Mut<Vec<u8>> out_buffer;
const usize guess_size = const usize guess_size = data.size() < 1024 ? data.size() * 4 : data.size() * 2;
data.size() < 1024 ? data.size() * 4 : data.size() * 2;
out_buffer.resize(guess_size); out_buffer.resize(guess_size);
zs.next_out = reinterpret_cast<Bytef *>(out_buffer.data()); zs.next_out = reinterpret_cast<Bytef *>(out_buffer.data());
zs.avail_out = static_cast<uInt>(out_buffer.size()); zs.avail_out = static_cast<uInt>(out_buffer.size());
Mut<int> ret; Mut<int> ret;
do { do
if (zs.avail_out == 0) { {
if (zs.avail_out == 0)
{
const usize current_pos = zs.total_out; const usize current_pos = zs.total_out;
const usize new_size = out_buffer.size() * 2; const usize new_size = out_buffer.size() * 2;
out_buffer.resize(new_size); out_buffer.resize(new_size);
@ -298,22 +327,25 @@ auto DataOps::zlib_inflate(Ref<Span<const u8>> data) -> Result<Vec<u8>> {
inflateEnd(&zs); inflateEnd(&zs);
if (ret != Z_STREAM_END) { if (ret != Z_STREAM_END)
{
return fail("Failed to inflate: corrupt data or stream error"); return fail("Failed to inflate: corrupt data or stream error");
} }
out_buffer.resize(zs.total_out); out_buffer.resize(zs.total_out);
return out_buffer; return out_buffer;
} }
auto DataOps::zlib_deflate(Ref<Span<const u8>> data) -> Result<Vec<u8>> { auto DataOps::zlib_deflate(Ref<Span<const u8>> data) -> Result<Vec<u8>>
{
Mut<z_stream> zs{}; Mut<z_stream> zs{};
zs.zalloc = Z_NULL; zs.zalloc = Z_NULL;
zs.zfree = Z_NULL; zs.zfree = Z_NULL;
zs.opaque = Z_NULL; zs.opaque = Z_NULL;
if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK) { if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK)
{
return fail("Failed to initialize zlib deflate"); return fail("Failed to initialize zlib deflate");
} }
@ -328,7 +360,8 @@ auto DataOps::zlib_deflate(Ref<Span<const u8>> data) -> Result<Vec<u8>> {
const int ret = deflate(&zs, Z_FINISH); const int ret = deflate(&zs, Z_FINISH);
if (ret != Z_STREAM_END) { if (ret != Z_STREAM_END)
{
deflateEnd(&zs); deflateEnd(&zs);
return fail("Failed to deflate, ran out of buffer memory"); return fail("Failed to deflate, ran out of buffer memory");
} }
@ -337,24 +370,26 @@ auto DataOps::zlib_deflate(Ref<Span<const u8>> data) -> Result<Vec<u8>> {
deflateEnd(&zs); deflateEnd(&zs);
return out_buffer; return out_buffer;
} }
auto DataOps::zstd_inflate(Ref<Span<const u8>> data) -> Result<Vec<u8>> { auto DataOps::zstd_inflate(Ref<Span<const u8>> data) -> Result<Vec<u8>>
const unsigned long long content_size = {
ZSTD_getFrameContentSize(data.data(), data.size()); const unsigned long long content_size = ZSTD_getFrameContentSize(data.data(), data.size());
if (content_size == ZSTD_CONTENTSIZE_ERROR) { if (content_size == ZSTD_CONTENTSIZE_ERROR)
{
return fail("Failed to inflate: Not valid ZSTD compressed data"); return fail("Failed to inflate: Not valid ZSTD compressed data");
} }
if (content_size != ZSTD_CONTENTSIZE_UNKNOWN) { if (content_size != ZSTD_CONTENTSIZE_UNKNOWN)
{
Mut<Vec<u8>> out_buffer; Mut<Vec<u8>> out_buffer;
out_buffer.resize(static_cast<usize>(content_size)); out_buffer.resize(static_cast<usize>(content_size));
const usize d_size = ZSTD_decompress(out_buffer.data(), out_buffer.size(), const usize d_size = ZSTD_decompress(out_buffer.data(), out_buffer.size(), data.data(), data.size());
data.data(), data.size());
if (ZSTD_isError(d_size)) { if (ZSTD_isError(d_size))
{
return fail("Failed to inflate: {}", ZSTD_getErrorName(d_size)); return fail("Failed to inflate: {}", ZSTD_getErrorName(d_size));
} }
@ -369,15 +404,18 @@ auto DataOps::zstd_inflate(Ref<Span<const u8>> data) -> Result<Vec<u8>> {
Mut<ZSTD_outBuffer> output = {out_buffer.data(), out_buffer.size(), 0}; Mut<ZSTD_outBuffer> output = {out_buffer.data(), out_buffer.size(), 0};
Mut<usize> ret; Mut<usize> ret;
do { do
{
ret = ZSTD_decompressStream(dctx, &output, &input); ret = ZSTD_decompressStream(dctx, &output, &input);
if (ZSTD_isError(ret)) { if (ZSTD_isError(ret))
{
ZSTD_freeDCtx(dctx); ZSTD_freeDCtx(dctx);
return fail("Failed to inflate: {}", ZSTD_getErrorName(ret)); return fail("Failed to inflate: {}", ZSTD_getErrorName(ret));
} }
if (output.pos == output.size) { if (output.pos == output.size)
{
const usize new_size = out_buffer.size() * 2; const usize new_size = out_buffer.size() * 2;
out_buffer.resize(new_size); out_buffer.resize(new_size);
output.dst = out_buffer.data(); output.dst = out_buffer.data();
@ -390,33 +428,35 @@ auto DataOps::zstd_inflate(Ref<Span<const u8>> data) -> Result<Vec<u8>> {
ZSTD_freeDCtx(dctx); ZSTD_freeDCtx(dctx);
return out_buffer; return out_buffer;
} }
auto DataOps::zstd_deflate(Ref<Span<const u8>> data) -> Result<Vec<u8>> { auto DataOps::zstd_deflate(Ref<Span<const u8>> data) -> Result<Vec<u8>>
{
const usize max_dst_size = ZSTD_compressBound(data.size()); const usize max_dst_size = ZSTD_compressBound(data.size());
Mut<Vec<u8>> out_buffer; Mut<Vec<u8>> out_buffer;
out_buffer.resize(max_dst_size); out_buffer.resize(max_dst_size);
const usize compressed_size = ZSTD_compress(out_buffer.data(), max_dst_size, const usize compressed_size = ZSTD_compress(out_buffer.data(), max_dst_size, data.data(), data.size(), 3);
data.data(), data.size(), 3);
if (ZSTD_isError(compressed_size)) { if (ZSTD_isError(compressed_size))
{
return fail("Failed to deflate: {}", ZSTD_getErrorName(compressed_size)); return fail("Failed to deflate: {}", ZSTD_getErrorName(compressed_size));
} }
out_buffer.resize(compressed_size); out_buffer.resize(compressed_size);
return out_buffer; return out_buffer;
} }
auto DataOps::gzip_deflate(Ref<Span<const u8>> data) -> Result<Vec<u8>> { auto DataOps::gzip_deflate(Ref<Span<const u8>> data) -> Result<Vec<u8>>
{
Mut<z_stream> zs{}; Mut<z_stream> zs{};
zs.zalloc = Z_NULL; zs.zalloc = Z_NULL;
zs.zfree = Z_NULL; zs.zfree = Z_NULL;
zs.opaque = Z_NULL; zs.opaque = Z_NULL;
if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK)
Z_DEFAULT_STRATEGY) != Z_OK) { {
return fail("Failed to initialize gzip deflate"); return fail("Failed to initialize gzip deflate");
} }
@ -432,7 +472,8 @@ auto DataOps::gzip_deflate(Ref<Span<const u8>> data) -> Result<Vec<u8>> {
const int ret = deflate(&zs, Z_FINISH); const int ret = deflate(&zs, Z_FINISH);
if (ret != Z_STREAM_END) { if (ret != Z_STREAM_END)
{
deflateEnd(&zs); deflateEnd(&zs);
return fail("Failed to deflate"); return fail("Failed to deflate");
} }
@ -441,10 +482,11 @@ auto DataOps::gzip_deflate(Ref<Span<const u8>> data) -> Result<Vec<u8>> {
deflateEnd(&zs); deflateEnd(&zs);
return out_buffer; return out_buffer;
} }
auto DataOps::gzip_inflate(Ref<Span<const u8>> data) -> Result<Vec<u8>> { auto DataOps::gzip_inflate(Ref<Span<const u8>> data) -> Result<Vec<u8>>
{
return zlib_inflate(data); return zlib_inflate(data);
} }
} // namespace IACore } // namespace IACore

View File

@ -18,19 +18,21 @@
#include <cstdio> #include <cstdio>
#if IA_PLATFORM_UNIX #if IA_PLATFORM_UNIX
#include <fcntl.h> # include <fcntl.h>
#include <sys/mman.h> # include <sys/mman.h>
#include <sys/stat.h> # include <sys/stat.h>
#include <unistd.h> # include <unistd.h>
#endif #endif
namespace IACore { namespace IACore
{
Mut<HashMap<const u8 *, std::tuple<void *, void *, void *>>> Mut<HashMap<const u8 *, std::tuple<void *, void *, void *>>> FileOps::s_mapped_files;
FileOps::s_mapped_files;
auto FileOps::unmap_file(const u8 *mapped_ptr) -> void { auto FileOps::unmap_file(const u8 *mapped_ptr) -> void
if (!s_mapped_files.contains(mapped_ptr)) { {
if (!s_mapped_files.contains(mapped_ptr))
{
return; return;
} }
@ -43,218 +45,242 @@ auto FileOps::unmap_file(const u8 *mapped_ptr) -> void {
::CloseHandle(static_cast<HANDLE>(std::get<2>(handles))); ::CloseHandle(static_cast<HANDLE>(std::get<2>(handles)));
const HANDLE handle = static_cast<HANDLE>(std::get<0>(handles)); const HANDLE handle = static_cast<HANDLE>(std::get<0>(handles));
if (handle != INVALID_HANDLE_VALUE) { if (handle != INVALID_HANDLE_VALUE)
{
::CloseHandle(handle); ::CloseHandle(handle);
} }
#elif IA_PLATFORM_UNIX #elif IA_PLATFORM_UNIX
::munmap(std::get<1>(handles), (usize)std::get<2>(handles)); ::munmap(std::get<1>(handles), (usize) std::get<2>(handles));
const i32 fd = (i32)((u64)std::get<0>(handles)); const i32 fd = (i32) ((u64) std::get<0>(handles));
if (fd != -1) { if (fd != -1)
{
::close(fd); ::close(fd);
} }
#endif #endif
} }
auto FileOps::map_shared_memory(Ref<String> name, const usize size, auto FileOps::map_shared_memory(Ref<String> name, const usize size, const bool is_owner) -> Result<u8 *>
const bool is_owner) -> Result<u8 *> { {
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
const int wchars_num = const int wchars_num = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), -1, NULL, 0);
MultiByteToWideChar(CP_UTF8, 0, name.c_str(), -1, NULL, 0);
Mut<std::wstring> w_name(wchars_num, 0); Mut<std::wstring> w_name(wchars_num, 0);
MultiByteToWideChar(CP_UTF8, 0, name.c_str(), -1, &w_name[0], wchars_num); MultiByteToWideChar(CP_UTF8, 0, name.c_str(), -1, &w_name[0], wchars_num);
Mut<HANDLE> h_map = NULL; Mut<HANDLE> h_map = NULL;
if (is_owner) { if (is_owner)
h_map = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, {
(DWORD)(size >> 32), (DWORD)(size & 0xFFFFFFFF), h_map = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, (DWORD) (size >> 32),
w_name.c_str()); (DWORD) (size & 0xFFFFFFFF), w_name.c_str());
} else { }
else
{
h_map = OpenFileMappingW(FILE_MAP_ALL_ACCESS, false, w_name.c_str()); h_map = OpenFileMappingW(FILE_MAP_ALL_ACCESS, false, w_name.c_str());
} }
if (h_map == NULL) { if (h_map == NULL)
return fail("Failed to {} shared memory '{}'", {
is_owner ? "owner" : "consumer", name); return fail("Failed to {} shared memory '{}'", is_owner ? "owner" : "consumer", name);
} }
Mut<u8 *> result = Mut<u8 *> result = static_cast<u8 *>(MapViewOfFile(h_map, FILE_MAP_ALL_ACCESS, 0, 0, size));
static_cast<u8 *>(MapViewOfFile(h_map, FILE_MAP_ALL_ACCESS, 0, 0, size)); if (result == NULL)
if (result == NULL) { {
CloseHandle(h_map); CloseHandle(h_map);
return fail("Failed to map view of shared memory '{}'", name); return fail("Failed to map view of shared memory '{}'", name);
} }
s_mapped_files[result] = std::make_tuple((void *)INVALID_HANDLE_VALUE, s_mapped_files[result] = std::make_tuple((void *) INVALID_HANDLE_VALUE, (void *) result, (void *) h_map);
(void *)result, (void *)h_map);
return result; return result;
#elif IA_PLATFORM_UNIX #elif IA_PLATFORM_UNIX
Mut<int> fd = -1; Mut<int> fd = -1;
if (is_owner) { if (is_owner)
{
fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666); fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd != -1) { if (fd != -1)
if (ftruncate(fd, size) == -1) { {
if (ftruncate(fd, size) == -1)
{
close(fd); close(fd);
shm_unlink(name.c_str()); shm_unlink(name.c_str());
return fail("Failed to truncate shared memory '{}'", name); return fail("Failed to truncate shared memory '{}'", name);
} }
} }
} else { }
else
{
fd = shm_open(name.c_str(), O_RDWR, 0666); fd = shm_open(name.c_str(), O_RDWR, 0666);
} }
if (fd == -1) { if (fd == -1)
return fail("Failed to {} shared memory '{}'", {
is_owner ? "owner" : "consumer", name); return fail("Failed to {} shared memory '{}'", is_owner ? "owner" : "consumer", name);
} }
Mut<void *> addr = Mut<void *> addr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (addr == MAP_FAILED)
if (addr == MAP_FAILED) { {
close(fd); close(fd);
return fail("Failed to mmap shared memory '{}'", name); return fail("Failed to mmap shared memory '{}'", name);
} }
Mut<u8 *> result = static_cast<u8 *>(addr); Mut<u8 *> result = static_cast<u8 *>(addr);
s_mapped_files[result] = s_mapped_files[result] = std::make_tuple((void *) ((u64) fd), (void *) addr, (void *) size);
std::make_tuple((void *)((u64)fd), (void *)addr, (void *)size);
return result; return result;
#endif #endif
} }
auto FileOps::unlink_shared_memory(Ref<String> name) -> void { auto FileOps::unlink_shared_memory(Ref<String> name) -> void
if (name.empty()) { {
if (name.empty())
{
return; return;
} }
#if IA_PLATFORM_UNIX #if IA_PLATFORM_UNIX
shm_unlink(name.c_str()); shm_unlink(name.c_str());
#endif #endif
} }
auto FileOps::map_file(Ref<Path> path, MutRef<usize> size) auto FileOps::map_file(Ref<Path> path, MutRef<usize> size) -> Result<const u8 *>
-> Result<const u8 *> { {
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
const HANDLE handle = CreateFileA( const HANDLE handle = CreateFileA(path.string().c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
path.string().c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (handle == INVALID_HANDLE_VALUE) { if (handle == INVALID_HANDLE_VALUE)
{
return fail("Failed to open {} for memory mapping", path.string()); return fail("Failed to open {} for memory mapping", path.string());
} }
Mut<LARGE_INTEGER> file_size; Mut<LARGE_INTEGER> file_size;
if (!GetFileSizeEx(handle, &file_size)) { if (!GetFileSizeEx(handle, &file_size))
{
CloseHandle(handle); CloseHandle(handle);
return fail("Failed to get size of {} for memory mapping", path.string()); return fail("Failed to get size of {} for memory mapping", path.string());
} }
size = static_cast<usize>(file_size.QuadPart); size = static_cast<usize>(file_size.QuadPart);
if (size == 0) { if (size == 0)
{
CloseHandle(handle); CloseHandle(handle);
return fail("Failed to get size of {} for memory mapping", path.string()); return fail("Failed to get size of {} for memory mapping", path.string());
} }
Mut<HANDLE> h_map = Mut<HANDLE> h_map = CreateFileMappingW(handle, NULL, PAGE_READONLY, 0, 0, NULL);
CreateFileMappingW(handle, NULL, PAGE_READONLY, 0, 0, NULL); if (h_map == NULL)
if (h_map == NULL) { {
CloseHandle(handle); CloseHandle(handle);
return fail("Failed to memory map {}", path.string()); return fail("Failed to memory map {}", path.string());
} }
const u8 *result = const u8 *result = static_cast<const u8 *>(MapViewOfFile(h_map, FILE_MAP_READ, 0, 0, 0));
static_cast<const u8 *>(MapViewOfFile(h_map, FILE_MAP_READ, 0, 0, 0)); if (result == NULL)
if (result == NULL) { {
CloseHandle(handle); CloseHandle(handle);
CloseHandle(h_map); CloseHandle(h_map);
return fail("Failed to memory map {}", path.string()); return fail("Failed to memory map {}", path.string());
} }
s_mapped_files[result] = std::make_tuple( s_mapped_files[result] = std::make_tuple((void *) handle, (void *) const_cast<u8 *>(result), (void *) h_map);
(void *)handle, (void *)const_cast<u8 *>(result), (void *)h_map);
return result; return result;
#elif IA_PLATFORM_UNIX #elif IA_PLATFORM_UNIX
const int handle = open(path.string().c_str(), O_RDONLY); const int handle = open(path.string().c_str(), O_RDONLY);
if (handle == -1) { if (handle == -1)
{
return fail("Failed to open {} for memory mapping", path.string()); return fail("Failed to open {} for memory mapping", path.string());
} }
Mut<struct stat> sb; Mut<struct stat> sb;
if (fstat(handle, &sb) == -1) { if (fstat(handle, &sb) == -1)
{
close(handle); close(handle);
return fail("Failed to get stats of {} for memory mapping", path.string()); return fail("Failed to get stats of {} for memory mapping", path.string());
} }
size = static_cast<usize>(sb.st_size); size = static_cast<usize>(sb.st_size);
if (size == 0) { if (size == 0)
{
close(handle); close(handle);
return fail("Failed to get size of {} for memory mapping", path.string()); return fail("Failed to get size of {} for memory mapping", path.string());
} }
Mut<void *> addr = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, handle, 0); Mut<void *> addr = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, handle, 0);
if (addr == MAP_FAILED) { if (addr == MAP_FAILED)
{
close(handle); close(handle);
return fail("Failed to memory map {}", path.string()); return fail("Failed to memory map {}", path.string());
} }
const u8 *result = static_cast<const u8 *>(addr); const u8 *result = static_cast<const u8 *>(addr);
madvise(addr, size, MADV_SEQUENTIAL); madvise(addr, size, MADV_SEQUENTIAL);
s_mapped_files[result] = s_mapped_files[result] = std::make_tuple((void *) ((u64) handle), (void *) addr, (void *) size);
std::make_tuple((void *)((u64)handle), (void *)addr, (void *)size);
return result; return result;
#endif #endif
} }
auto FileOps::stream_to_file(Ref<Path> path, const bool overwrite) auto FileOps::stream_to_file(Ref<Path> path, const bool overwrite) -> Result<StreamWriter>
-> Result<StreamWriter> { {
if (!overwrite && std::filesystem::exists(path)) { if (!overwrite && std::filesystem::exists(path))
{
return fail("File already exists: {}", path.string()); return fail("File already exists: {}", path.string());
} }
return StreamWriter::create_from_file(path); return StreamWriter::create_from_file(path);
} }
auto FileOps::stream_from_file(Ref<Path> path) -> Result<StreamReader> { auto FileOps::stream_from_file(Ref<Path> path) -> Result<StreamReader>
if (!std::filesystem::exists(path)) { {
if (!std::filesystem::exists(path))
{
return fail("File does not exist: {}", path.string()); return fail("File does not exist: {}", path.string());
} }
return StreamReader::create_from_file(path); return StreamReader::create_from_file(path);
} }
auto FileOps::read_text_file(Ref<Path> path) -> Result<String> { auto FileOps::read_text_file(Ref<Path> path) -> Result<String>
{
Mut<FILE *> f = fopen(path.string().c_str(), "r"); Mut<FILE *> f = fopen(path.string().c_str(), "r");
if (!f) { if (!f)
{
return fail("Failed to open file: {}", path.string()); return fail("Failed to open file: {}", path.string());
} }
Mut<String> result; Mut<String> result;
fseek(f, 0, SEEK_END); fseek(f, 0, SEEK_END);
const long len = ftell(f); const long len = ftell(f);
if (len > 0) { if (len > 0)
{
result.resize(static_cast<usize>(len)); result.resize(static_cast<usize>(len));
fseek(f, 0, SEEK_SET); fseek(f, 0, SEEK_SET);
fread(result.data(), 1, result.size(), f); const usize read_length = fread(result.data(), 1, result.size(), f);
result.resize(read_length);
} }
fclose(f); fclose(f);
return result; return result;
} }
auto FileOps::read_binary_file(Ref<Path> path) -> Result<Vec<u8>> { auto FileOps::read_binary_file(Ref<Path> path) -> Result<Vec<u8>>
{
Mut<FILE *> f = fopen(path.string().c_str(), "rb"); Mut<FILE *> f = fopen(path.string().c_str(), "rb");
if (!f) { if (!f)
{
return fail("Failed to open file: {}", path.string()); return fail("Failed to open file: {}", path.string());
} }
Mut<Vec<u8>> result; Mut<Vec<u8>> result;
fseek(f, 0, SEEK_END); fseek(f, 0, SEEK_END);
const long len = ftell(f); const long len = ftell(f);
if (len > 0) { if (len > 0)
{
result.resize(static_cast<usize>(len)); result.resize(static_cast<usize>(len));
fseek(f, 0, SEEK_SET); fseek(f, 0, SEEK_SET);
fread(result.data(), 1, result.size(), f); fread(result.data(), 1, result.size(), f);
} }
fclose(f); fclose(f);
return result; return result;
} }
auto FileOps::write_text_file(Ref<Path> path, Ref<String> contents, auto FileOps::write_text_file(Ref<Path> path, Ref<String> contents, const bool overwrite) -> Result<usize>
const bool overwrite) -> Result<usize> { {
const char *mode = overwrite ? "w" : "wx"; const char *mode = overwrite ? "w" : "wx";
Mut<FILE *> f = fopen(path.string().c_str(), mode); Mut<FILE *> f = fopen(path.string().c_str(), mode);
if (!f) { if (!f)
if (!overwrite && errno == EEXIST) { {
if (!overwrite && errno == EEXIST)
{
return fail("File already exists: {}", path.string()); return fail("File already exists: {}", path.string());
} }
return fail("Failed to write to file: {}", path.string()); return fail("Failed to write to file: {}", path.string());
@ -262,14 +288,16 @@ auto FileOps::write_text_file(Ref<Path> path, Ref<String> contents,
const usize result = fwrite(contents.data(), 1, contents.size(), f); const usize result = fwrite(contents.data(), 1, contents.size(), f);
fclose(f); fclose(f);
return result; return result;
} }
auto FileOps::write_binary_file(Ref<Path> path, const Span<const u8> contents, auto FileOps::write_binary_file(Ref<Path> path, const Span<const u8> contents, const bool overwrite) -> Result<usize>
const bool overwrite) -> Result<usize> { {
const char *mode = overwrite ? "w" : "wx"; const char *mode = overwrite ? "w" : "wx";
Mut<FILE *> f = fopen(path.string().c_str(), mode); Mut<FILE *> f = fopen(path.string().c_str(), mode);
if (!f) { if (!f)
if (!overwrite && errno == EEXIST) { {
if (!overwrite && errno == EEXIST)
{
return fail("File already exists: {}", path.string()); return fail("File already exists: {}", path.string());
} }
return fail("Failed to write to file: {}", path.string()); return fail("Failed to write to file: {}", path.string());
@ -277,40 +305,48 @@ auto FileOps::write_binary_file(Ref<Path> path, const Span<const u8> contents,
const usize result = fwrite(contents.data(), 1, contents.size(), f); const usize result = fwrite(contents.data(), 1, contents.size(), f);
fclose(f); fclose(f);
return result; return result;
} }
auto FileOps::normalize_executable_path(Ref<Path> path) -> Path { auto FileOps::normalize_executable_path(Ref<Path> path) -> Path
{
Mut<Path> result = path; Mut<Path> result = path;
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
if (!result.has_extension()) { if (!result.has_extension())
{
result.replace_extension(".exe"); result.replace_extension(".exe");
} }
#elif IA_PLATFORM_UNIX #elif IA_PLATFORM_UNIX
if (result.extension() == ".exe") { if (result.extension() == ".exe")
{
result.replace_extension(""); result.replace_extension("");
} }
if (result.is_relative()) { if (result.is_relative())
{
Mut<String> path_str = result.string(); Mut<String> path_str = result.string();
if (!path_str.starts_with("./") && !path_str.starts_with("../")) { if (!path_str.starts_with("./") && !path_str.starts_with("../"))
{
result = "./" + path_str; result = "./" + path_str;
} }
} }
#endif #endif
return result; return result;
} }
auto FileOps::native_open_file(Ref<Path> path, const FileAccess access, auto FileOps::native_open_file(Ref<Path> path, const FileAccess access, const FileMode mode, const u32 permissions)
const FileMode mode, const u32 permissions) -> Result<NativeFileHandle>
-> Result<NativeFileHandle> { {
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
AU_UNUSED(permissions);
Mut<DWORD> dw_access = 0; Mut<DWORD> dw_access = 0;
Mut<DWORD> dw_share = FILE_SHARE_READ; Mut<DWORD> dw_share = FILE_SHARE_READ;
Mut<DWORD> dw_disposition = 0; Mut<DWORD> dw_disposition = 0;
Mut<DWORD> dw_flags_and_attributes = FILE_ATTRIBUTE_NORMAL; Mut<DWORD> dw_flags_and_attributes = FILE_ATTRIBUTE_NORMAL;
switch (access) { switch (access)
{
case FileAccess::Read: case FileAccess::Read:
dw_access = GENERIC_READ; dw_access = GENERIC_READ;
break; break;
@ -322,7 +358,8 @@ auto FileOps::native_open_file(Ref<Path> path, const FileAccess access,
break; break;
} }
switch (mode) { switch (mode)
{
case FileMode::OpenExisting: case FileMode::OpenExisting:
dw_disposition = OPEN_EXISTING; dw_disposition = OPEN_EXISTING;
break; break;
@ -341,10 +378,10 @@ auto FileOps::native_open_file(Ref<Path> path, const FileAccess access,
} }
Mut<HANDLE> h_file = Mut<HANDLE> h_file =
CreateFileA(path.string().c_str(), dw_access, dw_share, NULL, CreateFileA(path.string().c_str(), dw_access, dw_share, NULL, dw_disposition, dw_flags_and_attributes, NULL);
dw_disposition, dw_flags_and_attributes, NULL);
if (h_file == INVALID_HANDLE_VALUE) { if (h_file == INVALID_HANDLE_VALUE)
{
return fail("Failed to open file '{}': {}", path.string(), GetLastError()); return fail("Failed to open file '{}': {}", path.string(), GetLastError());
} }
@ -353,7 +390,8 @@ auto FileOps::native_open_file(Ref<Path> path, const FileAccess access,
#elif IA_PLATFORM_UNIX #elif IA_PLATFORM_UNIX
Mut<int> flags = 0; Mut<int> flags = 0;
switch (access) { switch (access)
{
case FileAccess::Read: case FileAccess::Read:
flags = O_RDONLY; flags = O_RDONLY;
break; break;
@ -365,7 +403,8 @@ auto FileOps::native_open_file(Ref<Path> path, const FileAccess access,
break; break;
} }
switch (mode) { switch (mode)
{
case FileMode::OpenExisting: case FileMode::OpenExisting:
break; break;
case FileMode::OpenAlways: case FileMode::OpenAlways:
@ -384,16 +423,19 @@ auto FileOps::native_open_file(Ref<Path> path, const FileAccess access,
Mut<int> fd = open(path.string().c_str(), flags, permissions); Mut<int> fd = open(path.string().c_str(), flags, permissions);
if (fd == -1) { if (fd == -1)
{
return fail("Failed to open file '{}': {}", path.string(), errno); return fail("Failed to open file '{}': {}", path.string(), errno);
} }
return fd; return fd;
#endif #endif
} }
auto FileOps::native_close_file(const NativeFileHandle handle) -> void { auto FileOps::native_close_file(const NativeFileHandle handle) -> void
if (handle == INVALID_FILE_HANDLE) { {
if (handle == INVALID_FILE_HANDLE)
{
return; return;
} }
@ -402,19 +444,23 @@ auto FileOps::native_close_file(const NativeFileHandle handle) -> void {
#elif IA_PLATFORM_UNIX #elif IA_PLATFORM_UNIX
close(handle); close(handle);
#endif #endif
} }
FileOps::MemoryMappedRegion::~MemoryMappedRegion() { unmap(); } FileOps::MemoryMappedRegion::~MemoryMappedRegion()
{
unmap();
}
FileOps::MemoryMappedRegion::MemoryMappedRegion( FileOps::MemoryMappedRegion::MemoryMappedRegion(ForwardRef<MemoryMappedRegion> other) noexcept
ForwardRef<MemoryMappedRegion> other) noexcept { {
*this = std::move(other); *this = std::move(other);
} }
auto FileOps::MemoryMappedRegion::operator=( auto FileOps::MemoryMappedRegion::operator=(ForwardRef<MemoryMappedRegion> other) noexcept
ForwardRef<MemoryMappedRegion> other) noexcept -> MutRef<MemoryMappedRegion>
-> MutRef<MemoryMappedRegion> { {
if (this != &other) { if (this != &other)
{
unmap(); unmap();
m_ptr = other.m_ptr; m_ptr = other.m_ptr;
m_size = other.m_size; m_size = other.m_size;
@ -426,74 +472,83 @@ auto FileOps::MemoryMappedRegion::operator=(
other.m_size = 0; other.m_size = 0;
} }
return *this; return *this;
} }
auto FileOps::MemoryMappedRegion::map(const NativeFileHandle handle, auto FileOps::MemoryMappedRegion::map(const NativeFileHandle handle, const u64 offset, const usize size)
const u64 offset, const usize size) -> Result<void>
-> Result<void> { {
unmap(); unmap();
if (handle == INVALID_FILE_HANDLE) { if (handle == INVALID_FILE_HANDLE)
{
return fail("Invalid file handle provided to Map"); return fail("Invalid file handle provided to Map");
} }
if (size == 0) { if (size == 0)
{
return fail("Cannot map region of size 0"); return fail("Cannot map region of size 0");
} }
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
Mut<LARGE_INTEGER> file_size; Mut<LARGE_INTEGER> file_size;
if (!GetFileSizeEx(handle, &file_size)) { if (!GetFileSizeEx(handle, &file_size))
{
return fail("Failed to get file size"); return fail("Failed to get file size");
} }
const u64 end_offset = offset + size; const u64 end_offset = offset + size;
if (static_cast<u64>(file_size.QuadPart) < end_offset) { if (static_cast<u64>(file_size.QuadPart) < end_offset)
{
Mut<LARGE_INTEGER> new_size; Mut<LARGE_INTEGER> new_size;
new_size.QuadPart = static_cast<LONGLONG>(end_offset); new_size.QuadPart = static_cast<LONGLONG>(end_offset);
if (!SetFilePointerEx(handle, new_size, NULL, FILE_BEGIN)) { if (!SetFilePointerEx(handle, new_size, NULL, FILE_BEGIN))
{
return fail("Failed to seek to new end of file"); return fail("Failed to seek to new end of file");
} }
if (!SetEndOfFile(handle)) { if (!SetEndOfFile(handle))
{
return fail("Failed to extend file for mapping"); return fail("Failed to extend file for mapping");
} }
} }
m_map_handle = CreateFileMappingW(handle, NULL, PAGE_READWRITE, 0, 0, NULL); m_map_handle = CreateFileMappingW(handle, NULL, PAGE_READWRITE, 0, 0, NULL);
if (m_map_handle == NULL) { if (m_map_handle == NULL)
{
return fail("CreateFileMapping failed: {}", GetLastError()); return fail("CreateFileMapping failed: {}", GetLastError());
} }
const DWORD offset_high = static_cast<DWORD>(offset >> 32); const DWORD offset_high = static_cast<DWORD>(offset >> 32);
const DWORD offset_low = static_cast<DWORD>(offset & 0xFFFFFFFF); const DWORD offset_low = static_cast<DWORD>(offset & 0xFFFFFFFF);
m_ptr = static_cast<u8 *>(MapViewOfFile(m_map_handle, FILE_MAP_WRITE, m_ptr = static_cast<u8 *>(MapViewOfFile(m_map_handle, FILE_MAP_WRITE, offset_high, offset_low, size));
offset_high, offset_low, size)); if (m_ptr == NULL)
if (m_ptr == NULL) { {
CloseHandle(m_map_handle); CloseHandle(m_map_handle);
m_map_handle = NULL; m_map_handle = NULL;
return fail("MapViewOfFile failed (Offset: {}, Size: {}): {}", offset, size, return fail("MapViewOfFile failed (Offset: {}, Size: {}): {}", offset, size, GetLastError());
GetLastError());
} }
m_size = size; m_size = size;
#elif IA_PLATFORM_UNIX #elif IA_PLATFORM_UNIX
Mut<struct stat> sb; Mut<struct stat> sb;
if (fstat(handle, &sb) == -1) { if (fstat(handle, &sb) == -1)
{
return fail("Failed to fstat file"); return fail("Failed to fstat file");
} }
const u64 end_offset = offset + size; const u64 end_offset = offset + size;
if (static_cast<u64>(sb.st_size) < end_offset) { if (static_cast<u64>(sb.st_size) < end_offset)
if (ftruncate(handle, static_cast<off_t>(end_offset)) == -1) { {
if (ftruncate(handle, static_cast<off_t>(end_offset)) == -1)
{
return fail("Failed to ftruncate (extend) file"); return fail("Failed to ftruncate (extend) file");
} }
} }
Mut<void *> ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, Mut<void *> ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, handle, static_cast<off_t>(offset));
handle, static_cast<off_t>(offset)); if (ptr == MAP_FAILED)
if (ptr == MAP_FAILED) { {
return fail("mmap failed: {}", errno); return fail("mmap failed: {}", errno);
} }
@ -504,16 +559,19 @@ auto FileOps::MemoryMappedRegion::map(const NativeFileHandle handle,
#endif #endif
return {}; return {};
} }
auto FileOps::MemoryMappedRegion::unmap() -> void { auto FileOps::MemoryMappedRegion::unmap() -> void
if (!m_ptr) { {
if (!m_ptr)
{
return; return;
} }
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
UnmapViewOfFile(m_ptr); UnmapViewOfFile(m_ptr);
if (m_map_handle) { if (m_map_handle)
{
CloseHandle(m_map_handle); CloseHandle(m_map_handle);
m_map_handle = NULL; m_map_handle = NULL;
} }
@ -522,10 +580,12 @@ auto FileOps::MemoryMappedRegion::unmap() -> void {
#endif #endif
m_ptr = nullptr; m_ptr = nullptr;
m_size = 0; m_size = 0;
} }
auto FileOps::MemoryMappedRegion::flush() -> void { auto FileOps::MemoryMappedRegion::flush() -> void
if (!m_ptr) { {
if (!m_ptr)
{
return; return;
} }
@ -534,6 +594,6 @@ auto FileOps::MemoryMappedRegion::flush() -> void {
#elif IA_PLATFORM_UNIX #elif IA_PLATFORM_UNIX
msync(m_ptr, m_size, MS_SYNC); msync(m_ptr, m_size, MS_SYNC);
#endif #endif
} }
} // namespace IACore } // namespace IACore

View File

@ -16,58 +16,65 @@
#include <IACore/DataOps.hpp> #include <IACore/DataOps.hpp>
#include <IACore/Http/Client.hpp> #include <IACore/Http/Client.hpp>
namespace IACore { namespace IACore
auto HttpClient::create(Ref<String> host) -> Result<Box<HttpClient>> { {
auto HttpClient::create(Ref<String> host) -> Result<Box<HttpClient>>
{
return make_box_protected<HttpClient>(httplib::Client(host)); return make_box_protected<HttpClient>(httplib::Client(host));
} }
static auto build_headers(Span<const HttpClient::Header> headers, static auto build_headers(Span<const HttpClient::Header> headers, const char *default_content_type)
const char *default_content_type) -> httplib::Headers
-> httplib::Headers { {
Mut<httplib::Headers> out; Mut<httplib::Headers> out;
Mut<bool> has_content_type = false; Mut<bool> has_content_type = false;
for (Ref<HttpClient::Header> h : headers) { for (Ref<HttpClient::Header> h : headers)
{
out.emplace(h.first, h.second); out.emplace(h.first, h.second);
if (h.first == HttpClient::header_type_to_string( if (h.first == HttpClient::header_type_to_string(HttpClient::EHeaderType::CONTENT_TYPE))
HttpClient::EHeaderType::CONTENT_TYPE)) { {
has_content_type = true; has_content_type = true;
} }
} }
if (!has_content_type && default_content_type) { if (!has_content_type && default_content_type)
{
out.emplace("Content-Type", default_content_type); out.emplace("Content-Type", default_content_type);
} }
return out; return out;
} }
HttpClient::HttpClient(ForwardRef<httplib::Client> client) HttpClient::HttpClient(ForwardRef<httplib::Client> client)
: m_client(std::move(client)), : m_client(std::move(client)), m_last_response_code(EResponseCode::INTERNAL_SERVER_ERROR)
m_last_response_code(EResponseCode::INTERNAL_SERVER_ERROR) { {
m_client.enable_server_certificate_verification(true); m_client.enable_server_certificate_verification(true);
} }
HttpClient::~HttpClient() = default; HttpClient::~HttpClient() = default;
auto HttpClient::enable_certificate_verification() -> void { auto HttpClient::enable_certificate_verification() -> void
{
m_client.enable_server_certificate_verification(true); m_client.enable_server_certificate_verification(true);
} }
auto HttpClient::disable_certificate_verification() -> void { auto HttpClient::disable_certificate_verification() -> void
{
m_client.enable_server_certificate_verification(false); m_client.enable_server_certificate_verification(false);
} }
auto HttpClient::preprocess_response(Ref<String> response) -> String { auto HttpClient::preprocess_response(Ref<String> response) -> String
const Span<const u8> response_bytes = { {
reinterpret_cast<const u8 *>(response.data()), response.size()}; const Span<const u8> response_bytes = {reinterpret_cast<const u8 *>(response.data()), response.size()};
const DataOps::CompressionType compression = const DataOps::CompressionType compression = DataOps::detect_compression(response_bytes);
DataOps::detect_compression(response_bytes);
switch (compression) { switch (compression)
{
case DataOps::CompressionType::Gzip: { case DataOps::CompressionType::Gzip: {
const Result<Vec<u8>> data = DataOps::gzip_inflate(response_bytes); const Result<Vec<u8>> data = DataOps::gzip_inflate(response_bytes);
if (!data) { if (!data)
{
return response; return response;
} }
return String(reinterpret_cast<const char *>(data->data()), data->size()); return String(reinterpret_cast<const char *>(data->data()), data->size());
@ -75,7 +82,8 @@ auto HttpClient::preprocess_response(Ref<String> response) -> String {
case DataOps::CompressionType::Zlib: { case DataOps::CompressionType::Zlib: {
const Result<Vec<u8>> data = DataOps::zlib_inflate(response_bytes); const Result<Vec<u8>> data = DataOps::zlib_inflate(response_bytes);
if (!data) { if (!data)
{
return response; return response;
} }
return String(reinterpret_cast<const char *>(data->data()), data->size()); return String(reinterpret_cast<const char *>(data->data()), data->size());
@ -86,39 +94,42 @@ auto HttpClient::preprocess_response(Ref<String> response) -> String {
break; break;
} }
return response; return response;
} }
auto HttpClient::raw_get(Ref<String> path, Span<const Header> headers, auto HttpClient::raw_get(Ref<String> path, Span<const Header> headers, const char *default_content_type)
const char *default_content_type) -> Result<String> { -> Result<String>
const httplib::Headers http_headers = {
build_headers(headers, default_content_type); const httplib::Headers http_headers = build_headers(headers, default_content_type);
Mut<String> adjusted_path = path; Mut<String> adjusted_path = path;
if (!path.empty() && path[0] != '/') { if (!path.empty() && path[0] != '/')
{
adjusted_path = "/" + path; adjusted_path = "/" + path;
} }
const httplib::Result res = m_client.Get(adjusted_path.c_str(), http_headers); const httplib::Result res = m_client.Get(adjusted_path.c_str(), http_headers);
if (res) { if (res)
{
m_last_response_code = static_cast<EResponseCode>(res->status); m_last_response_code = static_cast<EResponseCode>(res->status);
if (res->status >= 200 && res->status < 300) { if (res->status >= 200 && res->status < 300)
{
return preprocess_response(res->body); return preprocess_response(res->body);
} }
return fail("HTTP Error {} : {}", res->status, res->body); return fail("HTTP Error {} : {}", res->status, res->body);
} }
return fail("Network Error: {}", httplib::to_string(res.error())); return fail("Network Error: {}", httplib::to_string(res.error()));
} }
auto HttpClient::raw_post(Ref<String> path, Span<const Header> headers, auto HttpClient::raw_post(Ref<String> path, Span<const Header> headers, Ref<String> body,
Ref<String> body, const char *default_content_type) const char *default_content_type) -> Result<String>
-> Result<String> { {
Mut<httplib::Headers> http_headers = Mut<httplib::Headers> http_headers = build_headers(headers, default_content_type);
build_headers(headers, default_content_type);
Mut<String> content_type = default_content_type; Mut<String> content_type = default_content_type;
if (http_headers.count("Content-Type")) { if (http_headers.count("Content-Type"))
{
const httplib::Headers::iterator t = http_headers.find("Content-Type"); const httplib::Headers::iterator t = http_headers.find("Content-Type");
content_type = t->second; content_type = t->second;
http_headers.erase(t); http_headers.erase(t);
@ -127,21 +138,23 @@ auto HttpClient::raw_post(Ref<String> path, Span<const Header> headers,
m_client.set_keep_alive(true); m_client.set_keep_alive(true);
Mut<String> adjusted_path = path; Mut<String> adjusted_path = path;
if (!path.empty() && path[0] != '/') { if (!path.empty() && path[0] != '/')
{
adjusted_path = "/" + path; adjusted_path = "/" + path;
} }
const httplib::Result res = m_client.Post(adjusted_path.c_str(), http_headers, const httplib::Result res = m_client.Post(adjusted_path.c_str(), http_headers, body, content_type.c_str());
body, content_type.c_str());
if (res) { if (res)
{
m_last_response_code = static_cast<EResponseCode>(res->status); m_last_response_code = static_cast<EResponseCode>(res->status);
if (res->status >= 200 && res->status < 300) { if (res->status >= 200 && res->status < 300)
{
return preprocess_response(res->body); return preprocess_response(res->body);
} }
return fail("HTTP Error {} : {}", res->status, res->body); return fail("HTTP Error {} : {}", res->status, res->body);
} }
return fail("Network Error: {}", httplib::to_string(res.error())); return fail("Network Error: {}", httplib::to_string(res.error()));
} }
} // namespace IACore } // namespace IACore

View File

@ -15,46 +15,52 @@
#include <IACore/Http/Common.hpp> #include <IACore/Http/Common.hpp>
namespace IACore { namespace IACore
auto HttpCommon::url_encode(Ref<String> value) -> String { {
auto HttpCommon::url_encode(Ref<String> value) -> String
{
Mut<std::stringstream> escaped; Mut<std::stringstream> escaped;
escaped.fill('0'); escaped.fill('0');
escaped << std::hex << std::uppercase; escaped << std::hex << std::uppercase;
for (const char c : value) { for (const char c : value)
if (std::isalnum(static_cast<unsigned char>(c)) || c == '-' || c == '_' || {
c == '.' || c == '~') if (std::isalnum(static_cast<unsigned char>(c)) || c == '-' || c == '_' || c == '.' || c == '~')
escaped << c; escaped << c;
else else
escaped << '%' << std::setw(2) escaped << '%' << std::setw(2) << static_cast<int>(static_cast<unsigned char>(c));
<< static_cast<int>(static_cast<unsigned char>(c));
} }
return escaped.str(); return escaped.str();
} }
auto HttpCommon::url_decode(Ref<String> value) -> String { auto HttpCommon::url_decode(Ref<String> value) -> String
{
Mut<String> result; Mut<String> result;
result.reserve(value.length()); result.reserve(value.length());
for (Mut<size_t> i = 0; i < value.length(); ++i) { for (Mut<size_t> i = 0; i < value.length(); ++i)
if (value[i] == '%' && i + 2 < value.length()) { {
if (value[i] == '%' && i + 2 < value.length())
{
const std::string hex_str = value.substr(i + 1, 2); const std::string hex_str = value.substr(i + 1, 2);
const char decoded_char = const char decoded_char = static_cast<char>(std::strtol(hex_str.c_str(), nullptr, 16));
static_cast<char>(std::strtol(hex_str.c_str(), nullptr, 16));
result += decoded_char; result += decoded_char;
i += 2; i += 2;
} else if (value[i] == '+') }
else if (value[i] == '+')
result += ' '; result += ' ';
else else
result += value[i]; result += value[i];
} }
return result; return result;
} }
auto HttpCommon::header_type_to_string(const EHeaderType type) -> String { auto HttpCommon::header_type_to_string(const EHeaderType type) -> String
switch (type) { {
switch (type)
{
case EHeaderType::ACCEPT: case EHeaderType::ACCEPT:
return "Accept"; return "Accept";
case EHeaderType::ACCEPT_CHARSET: case EHeaderType::ACCEPT_CHARSET:
@ -109,9 +115,10 @@ auto HttpCommon::header_type_to_string(const EHeaderType type) -> String {
return "Warning"; return "Warning";
} }
return "<Unknown>"; return "<Unknown>";
} }
auto HttpCommon::is_success_response_code(const EResponseCode code) -> bool { auto HttpCommon::is_success_response_code(const EResponseCode code) -> bool
return (i32)code >= 200 && (i32)code < 300; {
} return (i32) code >= 200 && (i32) code < 300;
}
} // namespace IACore } // namespace IACore

View File

@ -15,99 +15,126 @@
#include <IACore/Http/Server.hpp> #include <IACore/Http/Server.hpp>
namespace IACore { namespace IACore
{
auto HttpServer::Request::get_header(Ref<String> key) const -> String { auto HttpServer::Request::get_header(Ref<String> key) const -> String
if (auto it = headers.find(key); it != headers.end()) { {
if (auto it = headers.find(key); it != headers.end())
{
return it->second; return it->second;
} }
return ""; return "";
} }
auto HttpServer::Request::get_param(Ref<String> key) const -> String { auto HttpServer::Request::get_param(Ref<String> key) const -> String
if (auto it = params.find(key); it != params.end()) { {
if (auto it = params.find(key); it != params.end())
{
return it->second; return it->second;
} }
return ""; return "";
} }
auto HttpServer::Request::get_path_param(Ref<String> key) const -> String { auto HttpServer::Request::get_path_param(Ref<String> key) const -> String
if (auto it = path_params.find(key); it != path_params.end()) { {
if (auto it = path_params.find(key); it != path_params.end())
{
return it->second; return it->second;
} }
return ""; return "";
} }
auto HttpServer::Request::has_header(Ref<String> key) const -> bool { auto HttpServer::Request::has_header(Ref<String> key) const -> bool
{
return headers.contains(key); return headers.contains(key);
} }
auto HttpServer::Request::has_param(Ref<String> key) const -> bool { auto HttpServer::Request::has_param(Ref<String> key) const -> bool
{
return params.contains(key); return params.contains(key);
} }
auto HttpServer::Request::has_path_param(Ref<String> key) const -> bool { auto HttpServer::Request::has_path_param(Ref<String> key) const -> bool
{
return path_params.contains(key); return path_params.contains(key);
} }
void HttpServer::Response::set_content(Ref<String> content, Ref<String> type) { void HttpServer::Response::set_content(Ref<String> content, Ref<String> type)
{
body = content; body = content;
content_type = type; content_type = type;
} }
void HttpServer::Response::set_status(const EResponseCode status_code) { void HttpServer::Response::set_status(const EResponseCode status_code)
{
code = status_code; code = status_code;
} }
void HttpServer::Response::add_header(Ref<String> key, Ref<String> value) { void HttpServer::Response::add_header(Ref<String> key, Ref<String> value)
{
headers[key] = value; headers[key] = value;
} }
struct PublicHttpServer : public HttpServer { struct PublicHttpServer : public HttpServer
{
PublicHttpServer() = default; PublicHttpServer() = default;
}; };
HttpServer::HttpServer() = default; HttpServer::HttpServer() = default;
HttpServer::~HttpServer() { stop(); } HttpServer::~HttpServer()
{
stop();
}
auto HttpServer::create() -> Result<Box<HttpServer>> { auto HttpServer::create() -> Result<Box<HttpServer>>
{
return make_box<PublicHttpServer>(); return make_box<PublicHttpServer>();
} }
auto HttpServer::listen(Ref<String> host, const u32 port) -> Result<void> { auto HttpServer::listen(Ref<String> host, const u32 port) -> Result<void>
if (!m_server.listen(host.c_str(), static_cast<int>(port))) { {
if (!m_server.listen(host.c_str(), static_cast<int>(port)))
{
return fail("Failed to start HTTP server on {}:{}", host, port); return fail("Failed to start HTTP server on {}:{}", host, port);
} }
return {}; return {};
} }
void HttpServer::stop() { void HttpServer::stop()
if (m_server.is_running()) { {
if (m_server.is_running())
{
m_server.stop(); m_server.stop();
} }
} }
auto HttpServer::is_running() const -> bool { return m_server.is_running(); } auto HttpServer::is_running() const -> bool
{
return m_server.is_running();
}
void HttpServer::register_handler(Ref<String> method, Ref<String> pattern, void HttpServer::register_handler(Ref<String> method, Ref<String> pattern, const Handler handler)
const Handler handler) { {
const httplib::Server::Handler wrapper = const httplib::Server::Handler wrapper = [handler](Ref<httplib::Request> req, MutRef<httplib::Response> res) {
[handler](Ref<httplib::Request> req, MutRef<httplib::Response> res) {
Mut<Request> ia_req; Mut<Request> ia_req;
ia_req.path = req.path; ia_req.path = req.path;
ia_req.method = req.method; ia_req.method = req.method;
ia_req.body = req.body; ia_req.body = req.body;
for (Ref<Pair<const String, String>> item : req.headers) { for (Ref<Pair<const String, String>> item : req.headers)
{
ia_req.headers[item.first] = item.second; ia_req.headers[item.first] = item.second;
} }
for (Ref<Pair<const String, String>> item : req.params) { for (Ref<Pair<const String, String>> item : req.params)
{
ia_req.params[item.first] = item.second; ia_req.params[item.first] = item.second;
} }
for (Ref<Pair<const String, String>> item : req.path_params) { for (Ref<Pair<const String, String>> item : req.path_params)
{
ia_req.path_params[item.first] = item.second; ia_req.path_params[item.first] = item.second;
} }
@ -117,42 +144,57 @@ void HttpServer::register_handler(Ref<String> method, Ref<String> pattern,
res.status = static_cast<int>(ia_res.code); res.status = static_cast<int>(ia_res.code);
res.set_content(ia_res.body, ia_res.content_type.c_str()); res.set_content(ia_res.body, ia_res.content_type.c_str());
for (Ref<Pair<String, String>> item : ia_res.headers) { for (Ref<Pair<String, String>> item : ia_res.headers)
{
res.set_header(item.first.c_str(), item.second.c_str()); res.set_header(item.first.c_str(), item.second.c_str());
} }
}; };
if (method == "GET") { if (method == "GET")
{
m_server.Get(pattern.c_str(), wrapper); m_server.Get(pattern.c_str(), wrapper);
} else if (method == "POST") { }
else if (method == "POST")
{
m_server.Post(pattern.c_str(), wrapper); m_server.Post(pattern.c_str(), wrapper);
} else if (method == "PUT") { }
else if (method == "PUT")
{
m_server.Put(pattern.c_str(), wrapper); m_server.Put(pattern.c_str(), wrapper);
} else if (method == "DELETE") { }
else if (method == "DELETE")
{
m_server.Delete(pattern.c_str(), wrapper); m_server.Delete(pattern.c_str(), wrapper);
} else if (method == "OPTIONS") { }
else if (method == "OPTIONS")
{
m_server.Options(pattern.c_str(), wrapper); m_server.Options(pattern.c_str(), wrapper);
} }
} }
void HttpServer::get(Ref<String> pattern, const Handler handler) { void HttpServer::get(Ref<String> pattern, const Handler handler)
{
register_handler("GET", pattern, handler); register_handler("GET", pattern, handler);
} }
void HttpServer::post(Ref<String> pattern, const Handler handler) { void HttpServer::post(Ref<String> pattern, const Handler handler)
{
register_handler("POST", pattern, handler); register_handler("POST", pattern, handler);
} }
void HttpServer::put(Ref<String> pattern, const Handler handler) { void HttpServer::put(Ref<String> pattern, const Handler handler)
{
register_handler("PUT", pattern, handler); register_handler("PUT", pattern, handler);
} }
void HttpServer::del(Ref<String> pattern, const Handler handler) { void HttpServer::del(Ref<String> pattern, const Handler handler)
{
register_handler("DELETE", pattern, handler); register_handler("DELETE", pattern, handler);
} }
void HttpServer::options(Ref<String> pattern, const Handler handler) { void HttpServer::options(Ref<String> pattern, const Handler handler)
{
register_handler("OPTIONS", pattern, handler); register_handler("OPTIONS", pattern, handler);
} }
} // namespace IACore } // namespace IACore

View File

@ -19,15 +19,18 @@
#include <chrono> #include <chrono>
#include <mimalloc.h> #include <mimalloc.h>
namespace IACore { namespace IACore
Mut<std::chrono::high_resolution_clock::time_point> g_start_time = {}; {
Mut<std::chrono::high_resolution_clock::time_point> g_start_time = {};
static Mut<std::thread::id> g_main_thread_id = {}; static Mut<std::thread::id> g_main_thread_id = {};
static Mut<i32> g_core_init_count = 0; static Mut<i32> g_core_init_count = 0;
auto initialize() -> void { auto initialize() -> void
{
g_core_init_count++; g_core_init_count++;
if (g_core_init_count > 1) { if (g_core_init_count > 1)
{
return; return;
} }
@ -37,20 +40,26 @@ auto initialize() -> void {
Logger::initialize(); Logger::initialize();
mi_option_set(mi_option_verbose, 0); mi_option_set(mi_option_verbose, 0);
} }
auto terminate() -> void { auto terminate() -> void
{
g_core_init_count--; g_core_init_count--;
if (g_core_init_count > 0) { if (g_core_init_count > 0)
{
return; 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 { auto is_main_thread() -> bool
{
return std::this_thread::get_id() == g_main_thread_id; return std::this_thread::get_id() == g_main_thread_id;
} }
} // namespace IACore } // namespace IACore

View File

@ -20,31 +20,41 @@
#include <charconv> #include <charconv>
#include <fcntl.h> #include <fcntl.h>
namespace IACore { namespace IACore
struct IpcConnectionDescriptor { {
struct IpcConnectionDescriptor
{
Mut<String> socket_path; Mut<String> socket_path;
Mut<String> shared_mem_path; Mut<String> shared_mem_path;
Mut<u32> shared_mem_size; Mut<u32> shared_mem_size;
[[nodiscard]] auto serialize() const -> String { [[nodiscard]] auto serialize() const -> String
return std::format("{}|{}|{}|", socket_path, shared_mem_path, {
shared_mem_size); return std::format("{}|{}|{}|", socket_path, shared_mem_path, shared_mem_size);
} }
static auto deserialize(const StringView data) static auto deserialize(const StringView data) -> Option<IpcConnectionDescriptor>
-> Option<IpcConnectionDescriptor> { {
enum class ParseState { SocketPath, SharedMemPath, SharedMemSize }; enum class ParseState
{
SocketPath,
SharedMemPath,
SharedMemSize
};
Mut<IpcConnectionDescriptor> result{}; Mut<IpcConnectionDescriptor> result{};
Mut<usize> t = 0; Mut<usize> t = 0;
Mut<ParseState> state = ParseState::SocketPath; Mut<ParseState> state = ParseState::SocketPath;
for (Mut<usize> i = 0; i < data.size(); ++i) { for (Mut<usize> i = 0; i < data.size(); ++i)
if (data[i] != '|') { {
if (data[i] != '|')
{
continue; continue;
} }
switch (state) { switch (state)
{
case ParseState::SocketPath: case ParseState::SocketPath:
result.socket_path = String(data.substr(t, i - t)); result.socket_path = String(data.substr(t, i - t));
state = ParseState::SharedMemPath; state = ParseState::SharedMemPath;
@ -58,8 +68,8 @@ struct IpcConnectionDescriptor {
case ParseState::SharedMemSize: { case ParseState::SharedMemSize: {
const char *start = data.data() + t; const char *start = data.data() + t;
const char *end = data.data() + i; const char *end = data.data() + i;
if (std::from_chars(start, end, result.shared_mem_size).ec != if (std::from_chars(start, end, result.shared_mem_size).ec != std::errc{})
std::errc{}) { {
return std::nullopt; return std::nullopt;
} }
return result; return result;
@ -69,51 +79,51 @@ struct IpcConnectionDescriptor {
} }
return std::nullopt; return std::nullopt;
} }
}; };
IpcNode::~IpcNode() { IpcNode::~IpcNode()
if (m_socket != INVALID_SOCKET) { {
if (m_socket != INVALID_SOCKET)
{
SocketOps::close(m_socket); SocketOps::close(m_socket);
} }
} }
auto IpcNode::connect(const char *connection_string) -> Result<void> { auto IpcNode::connect(const char *connection_string) -> Result<void>
const Option<IpcConnectionDescriptor> desc_opt = {
IpcConnectionDescriptor::deserialize(connection_string); const Option<IpcConnectionDescriptor> desc_opt = IpcConnectionDescriptor::deserialize(connection_string);
if (!desc_opt) { if (!desc_opt)
{
return fail("Failed to parse connection string"); return fail("Failed to parse connection string");
} }
Ref<IpcConnectionDescriptor> desc = *desc_opt; Ref<IpcConnectionDescriptor> desc = *desc_opt;
m_shm_name = desc.shared_mem_path; m_shm_name = desc.shared_mem_path;
m_socket = AU_TRY(SocketOps::create_unix_socket()); m_socket = AU_TRY(SocketOps::create_unix_socket());
AU_TRY_PURE( AU_TRY_PURE(SocketOps::connect_unix_socket(m_socket, desc.socket_path.c_str()));
SocketOps::connect_unix_socket(m_socket, desc.socket_path.c_str()));
Mut<u8 *> mapped_ptr = AU_TRY(FileOps::map_shared_memory( Mut<u8 *> mapped_ptr = AU_TRY(FileOps::map_shared_memory(desc.shared_mem_path, desc.shared_mem_size, false));
desc.shared_mem_path, desc.shared_mem_size, false));
m_shared_memory = mapped_ptr; m_shared_memory = mapped_ptr;
Mut<IpcSharedMemoryLayout *> layout = Mut<IpcSharedMemoryLayout *> layout = reinterpret_cast<IpcSharedMemoryLayout *>(m_shared_memory);
reinterpret_cast<IpcSharedMemoryLayout *>(m_shared_memory);
if (layout->meta.magic != 0x49414950) { if (layout->meta.magic != 0x49414950)
{
return fail("Invalid shared memory header signature"); return fail("Invalid shared memory header signature");
} }
if (layout->meta.version != 1) { if (layout->meta.version != 1)
{
return fail("IPC version mismatch"); return fail("IPC version mismatch");
} }
Mut<u8 *> moni_ptr = m_shared_memory + layout->moni_data_offset; Mut<u8 *> moni_ptr = m_shared_memory + layout->moni_data_offset;
Mut<u8 *> mino_ptr = m_shared_memory + layout->mino_data_offset; Mut<u8 *> mino_ptr = m_shared_memory + layout->mino_data_offset;
m_moni = AU_TRY(RingBufferView::create( m_moni = AU_TRY(RingBufferView::create(&layout->moni_control,
&layout->moni_control,
Span<u8>(moni_ptr, static_cast<usize>(layout->moni_data_size)), false)); Span<u8>(moni_ptr, static_cast<usize>(layout->moni_data_size)), false));
m_mino = AU_TRY(RingBufferView::create( m_mino = AU_TRY(RingBufferView::create(&layout->mino_control,
&layout->mino_control,
Span<u8>(mino_ptr, static_cast<usize>(layout->mino_data_size)), false)); Span<u8>(mino_ptr, static_cast<usize>(layout->mino_data_size)), false));
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
@ -126,70 +136,79 @@ auto IpcNode::connect(const char *connection_string) -> Result<void> {
m_receive_buffer.resize(UINT16_MAX + 1); m_receive_buffer.resize(UINT16_MAX + 1);
return {}; return {};
} }
void IpcNode::update() { void IpcNode::update()
if (!m_moni.is_valid()) { {
if (!m_moni.is_valid())
{
return; return;
} }
Mut<IpcPacketHeader> header; Mut<IpcPacketHeader> header;
while (m_moni.pop( while (m_moni.pop(header, Span<u8>(m_receive_buffer.data(), m_receive_buffer.size())))
header, Span<u8>(m_receive_buffer.data(), m_receive_buffer.size()))) { {
on_packet(header.id, {m_receive_buffer.data(), header.payload_size}); on_packet(header.id, {m_receive_buffer.data(), header.payload_size});
} }
Mut<u8> signal = 0; Mut<u8> signal = 0;
const isize res = recv(m_socket, reinterpret_cast<char *>(&signal), 1, 0); const isize res = recv(m_socket, reinterpret_cast<char *>(&signal), 1, 0);
if (res == 1) { if (res == 1)
{
on_signal(signal); on_signal(signal);
} else if (res == 0 || (res < 0 && !SocketOps::is_would_block())) { }
else if (res == 0 || (res < 0 && !SocketOps::is_would_block()))
{
SocketOps::close(m_socket); SocketOps::close(m_socket);
FileOps::unlink_shared_memory(m_shm_name); FileOps::unlink_shared_memory(m_shm_name);
std::exit(-1); std::exit(-1);
} }
} }
void IpcNode::send_signal(const u8 signal) { void IpcNode::send_signal(const u8 signal)
if (m_socket != INVALID_SOCKET) { {
if (m_socket != INVALID_SOCKET)
{
send(m_socket, reinterpret_cast<const char *>(&signal), sizeof(signal), 0); send(m_socket, reinterpret_cast<const char *>(&signal), sizeof(signal), 0);
} }
} }
auto IpcNode::send_packet(const u16 packet_id, const Span<const u8> payload) auto IpcNode::send_packet(const u16 packet_id, const Span<const u8> payload) -> Result<void>
-> Result<void> { {
if (!m_mino.is_valid()) if (!m_mino.is_valid())
return fail("invalid MINO"); return fail("invalid MINO");
return m_mino.push(packet_id, payload); return m_mino.push(packet_id, payload);
}
void IpcManager::NodeSession::send_signal(const u8 signal) {
if (data_socket != INVALID_SOCKET) {
send(data_socket, reinterpret_cast<const char *>(&signal), sizeof(signal),
0);
} }
}
auto IpcManager::NodeSession::send_packet(const u16 packet_id, void IpcManager::NodeSession::send_signal(const u8 signal)
const Span<const u8> payload) {
-> Result<void> { if (data_socket != INVALID_SOCKET)
{
send(data_socket, reinterpret_cast<const char *>(&signal), sizeof(signal), 0);
}
}
auto IpcManager::NodeSession::send_packet(const u16 packet_id, const Span<const u8> payload) -> Result<void>
{
const std::scoped_lock<std::mutex> lock(send_mutex); const std::scoped_lock<std::mutex> lock(send_mutex);
if (!moni.is_valid()) if (!moni.is_valid())
return fail("invalid MONI"); return fail("invalid MONI");
return moni.push(packet_id, payload); return moni.push(packet_id, payload);
} }
IpcManager::IpcManager() { IpcManager::IpcManager()
ensure(SocketOps::is_initialized(), {
"SocketOps must be initialized before using IpcManager"); ensure(SocketOps::is_initialized(), "SocketOps must be initialized before using IpcManager");
m_receive_buffer.resize(UINT16_MAX + 1); m_receive_buffer.resize(UINT16_MAX + 1);
} }
IpcManager::~IpcManager() { IpcManager::~IpcManager()
for (MutRef<Box<NodeSession>> session : m_active_sessions) { {
for (MutRef<Box<NodeSession>> session : m_active_sessions)
{
ProcessOps::terminate_process(session->node_process); ProcessOps::terminate_process(session->node_process);
FileOps::unmap_file(session->mapped_ptr); FileOps::unmap_file(session->mapped_ptr);
FileOps::unlink_shared_memory(session->shared_mem_name); FileOps::unlink_shared_memory(session->shared_mem_name);
@ -197,25 +216,26 @@ IpcManager::~IpcManager() {
} }
m_active_sessions.clear(); m_active_sessions.clear();
for (MutRef<Box<NodeSession>> session : m_pending_sessions) { for (MutRef<Box<NodeSession>> session : m_pending_sessions)
{
ProcessOps::terminate_process(session->node_process); ProcessOps::terminate_process(session->node_process);
FileOps::unmap_file(session->mapped_ptr); FileOps::unmap_file(session->mapped_ptr);
FileOps::unlink_shared_memory(session->shared_mem_name); FileOps::unlink_shared_memory(session->shared_mem_name);
SocketOps::close(session->listener_socket); SocketOps::close(session->listener_socket);
} }
m_pending_sessions.clear(); m_pending_sessions.clear();
} }
void IpcManager::update() { void IpcManager::update()
const std::chrono::system_clock::time_point now = {
std::chrono::system_clock::now(); const std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
for (Mut<isize> i = static_cast<isize>(m_pending_sessions.size()) - 1; i >= 0; for (Mut<isize> i = static_cast<isize>(m_pending_sessions.size()) - 1; i >= 0; --i)
--i) { {
MutRef<Box<NodeSession>> session = MutRef<Box<NodeSession>> session = m_pending_sessions[static_cast<usize>(i)];
m_pending_sessions[static_cast<usize>(i)];
if (now - session->creation_time > std::chrono::seconds(5)) { if (now - session->creation_time > std::chrono::seconds(5))
{
ProcessOps::terminate_process(session->node_process); ProcessOps::terminate_process(session->node_process);
FileOps::unmap_file(session->mapped_ptr); FileOps::unmap_file(session->mapped_ptr);
@ -232,7 +252,8 @@ void IpcManager::update() {
Mut<i32> new_sock = accept(session->listener_socket, nullptr, nullptr); Mut<i32> new_sock = accept(session->listener_socket, nullptr, nullptr);
#endif #endif
if (new_sock != INVALID_SOCKET) { if (new_sock != INVALID_SOCKET)
{
session->data_socket = new_sock; session->data_socket = new_sock;
session->is_ready = true; session->is_ready = true;
@ -254,27 +275,28 @@ void IpcManager::update() {
} }
} }
for (Mut<isize> i = static_cast<isize>(m_active_sessions.size()) - 1; i >= 0; for (Mut<isize> i = static_cast<isize>(m_active_sessions.size()) - 1; i >= 0; --i)
--i) { {
MutRef<Box<NodeSession>> node = m_active_sessions[static_cast<usize>(i)]; MutRef<Box<NodeSession>> node = m_active_sessions[static_cast<usize>(i)];
Mut<NativeProcessID> node_id = node->node_process->id.load(); Mut<NativeProcessID> node_id = node->node_process->id.load();
Mut<IpcPacketHeader> header; Mut<IpcPacketHeader> header;
while (node->mino.pop( while (node->mino.pop(header, Span<u8>(m_receive_buffer.data(), m_receive_buffer.size())))
header, Span<u8>(m_receive_buffer.data(), m_receive_buffer.size()))) { {
on_packet(node_id, header.id, on_packet(node_id, header.id, {m_receive_buffer.data(), header.payload_size});
{m_receive_buffer.data(), header.payload_size});
} }
Mut<u8> signal = 0; Mut<u8> signal = 0;
const isize res = const isize res = recv(node->data_socket, reinterpret_cast<char *>(&signal), 1, 0);
recv(node->data_socket, reinterpret_cast<char *>(&signal), 1, 0);
if (res == 1) { if (res == 1)
{
on_signal(node_id, signal); on_signal(node_id, signal);
} else if (res == 0 || (res < 0 && !SocketOps::is_would_block())) { }
else if (res == 0 || (res < 0 && !SocketOps::is_would_block()))
{
ProcessOps::terminate_process(node->node_process); ProcessOps::terminate_process(node->node_process);
FileOps::unmap_file(node->mapped_ptr); FileOps::unmap_file(node->mapped_ptr);
@ -285,11 +307,10 @@ void IpcManager::update() {
m_active_session_map.erase(node_id); m_active_session_map.erase(node_id);
} }
} }
} }
auto IpcManager::spawn_node(Ref<Path> executable_path, auto IpcManager::spawn_node(Ref<Path> executable_path, const u32 shared_memory_size) -> Result<NativeProcessID>
const u32 shared_memory_size) {
-> Result<NativeProcessID> {
Mut<Box<NodeSession>> session = make_box<NodeSession>(); Mut<Box<NodeSession>> session = make_box<NodeSession>();
static Mut<std::atomic<u32>> s_id_gen{0}; static Mut<std::atomic<u32>> s_id_gen{0};
@ -305,8 +326,7 @@ auto IpcManager::spawn_node(Ref<Path> executable_path,
#endif #endif
session->listener_socket = AU_TRY(SocketOps::create_unix_socket()); session->listener_socket = AU_TRY(SocketOps::create_unix_socket());
AU_TRY_PURE( AU_TRY_PURE(SocketOps::bind_unix_socket(session->listener_socket, sock_path.c_str()));
SocketOps::bind_unix_socket(session->listener_socket, sock_path.c_str()));
AU_TRY_PURE(SocketOps::listen(session->listener_socket, 1)); AU_TRY_PURE(SocketOps::listen(session->listener_socket, 1));
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
@ -317,11 +337,9 @@ auto IpcManager::spawn_node(Ref<Path> executable_path,
#endif #endif
const String shm_name = std::format("ia_shm_{}", sid); const String shm_name = std::format("ia_shm_{}", sid);
session->mapped_ptr = session->mapped_ptr = AU_TRY(FileOps::map_shared_memory(shm_name, shared_memory_size, true));
AU_TRY(FileOps::map_shared_memory(shm_name, shared_memory_size, true));
Mut<IpcSharedMemoryLayout *> layout = Mut<IpcSharedMemoryLayout *> layout = reinterpret_cast<IpcSharedMemoryLayout *>(session->mapped_ptr);
reinterpret_cast<IpcSharedMemoryLayout *>(session->mapped_ptr);
layout->meta.magic = 0x49414950; layout->meta.magic = 0x49414950;
layout->meta.version = 1; layout->meta.version = 1;
@ -341,15 +359,11 @@ auto IpcManager::spawn_node(Ref<Path> executable_path,
session->moni = AU_TRY(RingBufferView::create( session->moni = AU_TRY(RingBufferView::create(
&layout->moni_control, &layout->moni_control,
Span<u8>(session->mapped_ptr + layout->moni_data_offset, Span<u8>(session->mapped_ptr + layout->moni_data_offset, static_cast<usize>(layout->moni_data_size)), true));
static_cast<usize>(layout->moni_data_size)),
true));
session->mino = AU_TRY(RingBufferView::create( session->mino = AU_TRY(RingBufferView::create(
&layout->mino_control, &layout->mino_control,
Span<u8>(session->mapped_ptr + layout->mino_data_offset, Span<u8>(session->mapped_ptr + layout->mino_data_offset, static_cast<usize>(layout->mino_data_size)), true));
static_cast<usize>(layout->mino_data_size)),
true));
Mut<IpcConnectionDescriptor> desc; Mut<IpcConnectionDescriptor> desc;
desc.socket_path = sock_path; desc.socket_path = sock_path;
@ -361,29 +375,31 @@ auto IpcManager::spawn_node(Ref<Path> executable_path,
session->node_process = AU_TRY(ProcessOps::spawn_process_async( session->node_process = AU_TRY(ProcessOps::spawn_process_async(
FileOps::normalize_executable_path(executable_path).string(), args, FileOps::normalize_executable_path(executable_path).string(), args,
[sid](StringView line) { [sid](StringView line) {
if (Env::IS_DEBUG) { if (Env::IS_DEBUG)
std::cout << std::format("{}[Node:{}:STDOUT|STDERR]: {}{}\n", {
console::MAGENTA, sid, line, console::RESET); std::cout << std::format("{}[Node:{}:STDOUT|STDERR]: {}{}\n", console::MAGENTA, sid, line, console::RESET);
} }
}, },
[sid](Result<i32> result) { [sid](Result<i32> result) {
if (Env::IS_DEBUG) { if (Env::IS_DEBUG)
if (!result) { {
std::cout << std::format( if (!result)
"{}[Node: {}]: Failed to spawn with error '{}'{}\n", {
console::RED, sid, result.error(), console::RESET); std::cout << std::format("{}[Node: {}]: Failed to spawn with error '{}'{}\n", console::RED, sid,
} else { result.error(), console::RESET);
std::cout << std::format("{}[Node: {}]: Exited with code {}{}\n", }
console::RED, sid, *result, else
{
std::cout << std::format("{}[Node: {}]: Exited with code {}{}\n", console::RED, sid, *result,
console::RESET); console::RESET);
} }
} }
})); }));
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));
if (!session->node_process->is_active()) { if (!session->node_process->is_active())
return fail("Failed to spawn the child process \"{}\"", {
executable_path.string()); return fail("Failed to spawn the child process \"{}\"", executable_path.string());
} }
const NativeProcessID process_id = session->node_process->id.load(); const NativeProcessID process_id = session->node_process->id.load();
@ -393,15 +409,18 @@ auto IpcManager::spawn_node(Ref<Path> executable_path,
m_pending_sessions.push_back(std::move(session)); m_pending_sessions.push_back(std::move(session));
return process_id; return process_id;
} }
auto IpcManager::wait_till_node_is_online(const NativeProcessID node_id) auto IpcManager::wait_till_node_is_online(const NativeProcessID node_id) -> bool
-> bool { {
Mut<bool> is_pending = true; Mut<bool> is_pending = true;
while (is_pending) { while (is_pending)
{
is_pending = false; is_pending = false;
for (const Box<NodeSession> &session : m_pending_sessions) { for (const Box<NodeSession> &session : m_pending_sessions)
if (session->node_process->id.load() == node_id) { {
if (session->node_process->id.load() == node_id)
{
is_pending = true; is_pending = true;
break; break;
} }
@ -410,12 +429,13 @@ auto IpcManager::wait_till_node_is_online(const NativeProcessID node_id)
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
return m_active_session_map.contains(node_id); return m_active_session_map.contains(node_id);
} }
void IpcManager::shutdown_node(const NativeProcessID node_id) { void IpcManager::shutdown_node(const NativeProcessID node_id)
const HashMap<NativeProcessID, NodeSession *>::iterator it_node = {
m_active_session_map.find(node_id); const HashMap<NativeProcessID, NodeSession *>::iterator it_node = m_active_session_map.find(node_id);
if (it_node == m_active_session_map.end()) { if (it_node == m_active_session_map.end())
{
return; return;
} }
@ -426,26 +446,26 @@ void IpcManager::shutdown_node(const NativeProcessID node_id) {
FileOps::unlink_shared_memory(node->shared_mem_name); FileOps::unlink_shared_memory(node->shared_mem_name);
SocketOps::close(node->data_socket); SocketOps::close(node->data_socket);
std::erase_if(m_active_sessions, std::erase_if(m_active_sessions, [&](Ref<Box<NodeSession>> s) { return s.get() == node; });
[&](Ref<Box<NodeSession>> s) { return s.get() == node; });
m_active_session_map.erase(it_node); m_active_session_map.erase(it_node);
} }
void IpcManager::send_signal(const NativeProcessID node, const u8 signal) { void IpcManager::send_signal(const NativeProcessID node, const u8 signal)
const HashMap<NativeProcessID, NodeSession *>::iterator it_node = {
m_active_session_map.find(node); const HashMap<NativeProcessID, NodeSession *>::iterator it_node = m_active_session_map.find(node);
if (it_node == m_active_session_map.end()) { if (it_node == m_active_session_map.end())
{
return; return;
} }
it_node->second->send_signal(signal); it_node->second->send_signal(signal);
} }
auto IpcManager::send_packet(const NativeProcessID node, const u16 packet_id, auto IpcManager::send_packet(const NativeProcessID node, const u16 packet_id, const Span<const u8> payload)
const Span<const u8> payload) -> Result<void> { -> Result<void>
const HashMap<NativeProcessID, NodeSession *>::iterator it_node = {
m_active_session_map.find(node); const HashMap<NativeProcessID, NodeSession *>::iterator it_node = m_active_session_map.find(node);
if (it_node == m_active_session_map.end()) if (it_node == m_active_session_map.end())
return fail("no such node"); return fail("no such node");
return it_node->second->send_packet(packet_id, payload); return it_node->second->send_packet(packet_id, payload);
} }
} // namespace IACore } // namespace IACore

View File

@ -15,4 +15,6 @@
#include <IACore/JSON.hpp> #include <IACore/JSON.hpp>
namespace IACore {} // namespace IACore namespace IACore
{
} // namespace IACore

View File

@ -19,67 +19,76 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
namespace IACore { namespace IACore
Mut<Logger::LogLevel> Logger::m_log_level = Logger::LogLevel::Info; {
Mut<std::ofstream> Logger::m_log_file; Mut<Logger::LogLevel> Logger::m_log_level = Logger::LogLevel::Info;
Mut<std::ofstream> Logger::m_log_file;
static auto get_seconds_count() -> f64 { static auto get_seconds_count() -> f64
static const std::chrono::time_point<std::chrono::steady_clock> start_time = {
std::chrono::steady_clock::now(); static const std::chrono::time_point<std::chrono::steady_clock> start_time = std::chrono::steady_clock::now();
const std::chrono::time_point<std::chrono::steady_clock> now = const std::chrono::time_point<std::chrono::steady_clock> now = std::chrono::steady_clock::now();
std::chrono::steady_clock::now();
const std::chrono::duration<f64> duration = now - start_time; const std::chrono::duration<f64> duration = now - start_time;
return duration.count(); return duration.count();
} }
auto Logger::initialize() -> void {} auto Logger::initialize() -> void
{
}
auto Logger::terminate() -> void { auto Logger::terminate() -> void
if (m_log_file.is_open()) { {
if (m_log_file.is_open())
{
m_log_file.flush(); m_log_file.flush();
m_log_file.close(); m_log_file.close();
} }
} }
auto Logger::enable_logging_to_disk(const char *file_path) -> Result<void> { auto Logger::enable_logging_to_disk(const char *file_path) -> Result<void>
if (m_log_file.is_open()) { {
if (m_log_file.is_open())
{
m_log_file.flush(); m_log_file.flush();
m_log_file.close(); m_log_file.close();
} }
m_log_file.open(file_path); m_log_file.open(file_path);
if (!m_log_file.is_open()) { if (!m_log_file.is_open())
{
return fail("Failed to open log file: {}", file_path); return fail("Failed to open log file: {}", file_path);
} }
return {}; return {};
} }
auto Logger::set_log_level(const LogLevel log_level) -> void { auto Logger::set_log_level(const LogLevel log_level) -> void
{
m_log_level = log_level; m_log_level = log_level;
} }
auto Logger::flush_logs() -> void { auto Logger::flush_logs() -> void
{
std::cout.flush(); std::cout.flush();
if (m_log_file.is_open()) { if (m_log_file.is_open())
{
m_log_file.flush(); m_log_file.flush();
} }
} }
auto Logger::log_internal(const char *prefix, const char *tag, auto Logger::log_internal(const char *prefix, const char *tag, ForwardRef<String> msg) -> void
ForwardRef<String> msg) -> void { {
const f64 seconds = get_seconds_count(); const f64 seconds = get_seconds_count();
const String out_line = const String out_line = std::format("[{:>8.3f}]: [{}]: {}", seconds, tag, msg);
std::format("[{:>8.3f}]: [{}]: {}", seconds, tag, msg);
std::cout << prefix << out_line << console::RESET << '\n'; std::cout << prefix << out_line << console::RESET << '\n';
if (m_log_file.is_open()) { if (m_log_file.is_open())
m_log_file.write(out_line.data(), {
static_cast<std::streamsize>(out_line.size())); m_log_file.write(out_line.data(), static_cast<std::streamsize>(out_line.size()));
m_log_file.put('\n'); m_log_file.put('\n');
m_log_file.flush(); m_log_file.flush();
} }
} }
} // namespace IACore } // namespace IACore

View File

@ -16,26 +16,26 @@
#include <IACore/Platform.hpp> #include <IACore/Platform.hpp>
#if defined(IA_ARCH_X64) #if defined(IA_ARCH_X64)
#ifndef _MSC_VER # ifndef _MSC_VER
#include <cpuid.h> # include <cpuid.h>
#endif # endif
#elif defined(IA_ARCH_ARM64) #elif defined(IA_ARCH_ARM64)
#if defined(__linux__) || defined(__ANDROID__) # if defined(__linux__) || defined(__ANDROID__)
#include <asm/hwcap.h> # include <asm/hwcap.h>
#include <sys/auxv.h> # include <sys/auxv.h>
#endif # endif
#endif #endif
namespace IACore { namespace IACore
Mut<Platform::Capabilities> Platform::s_capabilities{}; {
Mut<Platform::Capabilities> Platform::s_capabilities{};
#if defined(IA_ARCH_X64) #if defined(IA_ARCH_X64)
auto Platform::cpuid(const i32 function, const i32 sub_function, auto Platform::cpuid(const i32 function, const i32 sub_function, Mut<i32> out[4]) -> void
Mut<i32> out[4]) -> void { {
#ifdef _MSC_VER # ifdef _MSC_VER
__cpuidex(reinterpret_cast<i32 *>(out), static_cast<i32>(function), __cpuidex(reinterpret_cast<i32 *>(out), static_cast<i32>(function), static_cast<i32>(sub_function));
static_cast<i32>(sub_function)); # else
#else
Mut<u32> a = 0; Mut<u32> a = 0;
Mut<u32> b = 0; Mut<u32> b = 0;
Mut<u32> c = 0; Mut<u32> c = 0;
@ -45,16 +45,18 @@ auto Platform::cpuid(const i32 function, const i32 sub_function,
out[1] = static_cast<i32>(b); out[1] = static_cast<i32>(b);
out[2] = static_cast<i32>(c); out[2] = static_cast<i32>(c);
out[3] = static_cast<i32>(d); out[3] = static_cast<i32>(d);
#endif # endif
} }
#endif #endif
auto Platform::check_cpu() -> bool { auto Platform::check_cpu() -> bool
{
#if defined(IA_ARCH_X64) #if defined(IA_ARCH_X64)
Mut<i32> cpu_info[4]; Mut<i32> cpu_info[4];
cpuid(0, 0, cpu_info); cpuid(0, 0, cpu_info);
if (cpu_info[0] < 7) { if (cpu_info[0] < 7)
{
return false; return false;
} }
@ -63,44 +65,48 @@ auto Platform::check_cpu() -> bool {
const bool avx = (cpu_info[2] & (1 << 28)) != 0; const bool avx = (cpu_info[2] & (1 << 28)) != 0;
const bool fma = (cpu_info[2] & (1 << 12)) != 0; const bool fma = (cpu_info[2] & (1 << 12)) != 0;
if (!osxsave || !avx || !fma) { if (!osxsave || !avx || !fma)
{
return false; return false;
} }
const u64 xcr_feature_mask = _xgetbv(0); const u64 xcr_feature_mask = _xgetbv(0);
if ((xcr_feature_mask & 0x6) != 0x6) { if ((xcr_feature_mask & 0x6) != 0x6)
{
return false; return false;
} }
cpuid(7, 0, cpu_info); cpuid(7, 0, cpu_info);
const bool avx2 = (cpu_info[1] & (1 << 5)) != 0; const bool avx2 = (cpu_info[1] & (1 << 5)) != 0;
if (!avx2) { if (!avx2)
{
return false; return false;
} }
s_capabilities.hardware_crc32 = true; s_capabilities.hardware_crc32 = true;
#elif defined(IA_ARCH_ARM64) #elif defined(IA_ARCH_ARM64)
#if defined(__linux__) || defined(__ANDROID__) # if defined(__linux__) || defined(__ANDROID__)
const usize hw_caps = getauxval(AT_HWCAP); const usize hw_caps = getauxval(AT_HWCAP);
#ifndef HWCAP_CRC32 # ifndef HWCAP_CRC32
#define HWCAP_CRC32 (1 << 7) # define HWCAP_CRC32 (1 << 7)
#endif # endif
s_capabilities.hardware_crc32 = (hw_caps & HWCAP_CRC32) != 0; s_capabilities.hardware_crc32 = (hw_caps & HWCAP_CRC32) != 0;
#elif defined(IA_PLATFORM_APPLE) # elif defined(IA_PLATFORM_APPLE)
s_capabilities.hardware_crc32 = true; s_capabilities.hardware_crc32 = true;
#else # else
s_capabilities.hardware_crc32 = false; s_capabilities.hardware_crc32 = false;
#endif # endif
#else #else
s_capabilities.hardware_crc32 = false; s_capabilities.hardware_crc32 = false;
#endif #endif
return true; return true;
} }
auto Platform::get_architecture_name() -> const char * { auto Platform::get_architecture_name() -> const char *
{
#if defined(IA_ARCH_X64) #if defined(IA_ARCH_X64)
return "x86_64"; return "x86_64";
#elif defined(IA_ARCH_ARM64) #elif defined(IA_ARCH_ARM64)
@ -110,17 +116,18 @@ auto Platform::get_architecture_name() -> const char * {
#else #else
return "unknown"; return "unknown";
#endif #endif
} }
auto Platform::get_operating_system_name() -> const char * { auto Platform::get_operating_system_name() -> const char *
{
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
return "Windows"; return "Windows";
#elif defined(IA_PLATFORM_APPLE) #elif defined(IA_PLATFORM_APPLE)
#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE # if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
return "iOS"; return "iOS";
#else # else
return "macOS"; return "macOS";
#endif # endif
#elif defined(__ANDROID__) #elif defined(__ANDROID__)
return "Android"; return "Android";
#elif IA_PLATFORM_LINUX #elif IA_PLATFORM_LINUX
@ -130,5 +137,5 @@ auto Platform::get_operating_system_name() -> const char * {
#else #else
return "Unknown"; return "Unknown";
#endif #endif
} }
} // namespace IACore } // namespace IACore

View File

@ -15,117 +15,144 @@
#include <IACore/ProcessOps.hpp> #include <IACore/ProcessOps.hpp>
namespace IACore { namespace IACore
struct LineBuffer { {
struct LineBuffer
{
Mut<String> m_accumulator; Mut<String> m_accumulator;
const std::function<void(StringView)> m_callback; const std::function<void(StringView)> m_callback;
auto append(const char *data, const usize size) -> void; auto append(const char *data, const usize size) -> void;
auto flush() -> void; auto flush() -> void;
}; };
auto LineBuffer::append(const char *data, const usize size) -> void { auto LineBuffer::append(const char *data, const usize size) -> void
{
Mut<usize> start = 0; Mut<usize> start = 0;
for (Mut<usize> i = 0; i < size; ++i) { for (Mut<usize> i = 0; i < size; ++i)
if (data[i] == '\n' || data[i] == '\r') { {
if (!m_accumulator.empty()) { if (data[i] == '\n' || data[i] == '\r')
{
if (!m_accumulator.empty())
{
m_accumulator.append(data + start, i - start); m_accumulator.append(data + start, i - start);
if (!m_accumulator.empty()) { if (!m_accumulator.empty())
{
m_callback(m_accumulator); m_callback(m_accumulator);
} }
m_accumulator.clear(); m_accumulator.clear();
} else { }
if (i > start) { else
{
if (i > start)
{
m_callback(StringView(data + start, i - start)); m_callback(StringView(data + start, i - start));
} }
} }
if (data[i] == '\r' && i + 1 < size && data[i + 1] == '\n') { if (data[i] == '\r' && i + 1 < size && data[i + 1] == '\n')
{
i++; i++;
} }
start = i + 1; start = i + 1;
} }
} }
if (start < size) { if (start < size)
{
m_accumulator.append(data + start, size - start); m_accumulator.append(data + start, size - start);
} }
} }
auto LineBuffer::flush() -> void { auto LineBuffer::flush() -> void
if (!m_accumulator.empty()) { {
if (!m_accumulator.empty())
{
m_callback(m_accumulator); m_callback(m_accumulator);
m_accumulator.clear(); m_accumulator.clear();
} }
} }
auto ProcessOps::get_current_process_id() -> NativeProcessID { auto ProcessOps::get_current_process_id() -> NativeProcessID
{
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
return ::GetCurrentProcessId(); return ::GetCurrentProcessId();
#else #else
return getpid(); return getpid();
#endif #endif
} }
auto ProcessOps::spawn_process_sync( auto ProcessOps::spawn_process_sync(Ref<String> command, Ref<String> args,
Ref<String> command, Ref<String> args, const std::function<void(StringView)> on_output_line_callback) -> Result<i32>
const std::function<void(StringView)> on_output_line_callback) {
-> Result<i32> {
Mut<std::atomic<NativeProcessID>> id = 0; Mut<std::atomic<NativeProcessID>> id = 0;
if constexpr (Env::IS_WINDOWS) { if constexpr (Env::IS_WINDOWS)
{
return spawn_process_windows(command, args, on_output_line_callback, id); return spawn_process_windows(command, args, on_output_line_callback, id);
} else { }
else
{
return spawn_process_posix(command, args, on_output_line_callback, id); return spawn_process_posix(command, args, on_output_line_callback, id);
} }
} }
auto ProcessOps::spawn_process_async( auto ProcessOps::spawn_process_async(Ref<String> command, Ref<String> args,
Ref<String> command, Ref<String> args,
const std::function<void(StringView)> on_output_line_callback, const std::function<void(StringView)> on_output_line_callback,
const std::function<void(Result<i32>)> on_finish_callback) const std::function<void(Result<i32>)> on_finish_callback)
-> Result<Box<ProcessHandle>> { -> Result<Box<ProcessHandle>>
{
Mut<Box<ProcessHandle>> handle = make_box<ProcessHandle>(); Mut<Box<ProcessHandle>> handle = make_box<ProcessHandle>();
handle->is_running = true; handle->is_running = true;
Mut<ProcessHandle *> h_ptr = handle.get(); Mut<ProcessHandle *> h_ptr = handle.get();
handle->m_thread_handle = std::jthread([h_ptr, cmd = command, arg = args, handle->m_thread_handle = std::jthread(
cb = on_output_line_callback, [h_ptr, cmd = command, arg = args, cb = on_output_line_callback, fin = on_finish_callback]() mutable {
fin = on_finish_callback]() mutable {
Mut<Result<i32>> result = fail("Platform not supported"); Mut<Result<i32>> 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); result = spawn_process_windows(cmd, arg, cb, h_ptr->id);
} else { }
else
{
result = spawn_process_posix(cmd, arg, cb, h_ptr->id); result = spawn_process_posix(cmd, arg, cb, h_ptr->id);
} }
h_ptr->is_running = false; h_ptr->is_running = false;
if (fin) { if (fin)
if (!result) { {
if (!result)
{
fin(fail(std::move(result.error()))); fin(fail(std::move(result.error())));
} else { }
else
{
fin(*result); fin(*result);
} }
} }
}); });
return handle; return handle;
} }
auto ProcessOps::terminate_process(Ref<Box<ProcessHandle>> handle) -> void { auto ProcessOps::terminate_process(Ref<Box<ProcessHandle>> handle) -> void
if (!handle || !handle->is_active()) { {
if (!handle || !handle->is_active())
{
return; return;
} }
const NativeProcessID pid = handle->id.load(); const NativeProcessID pid = handle->id.load();
if (pid == 0) { if (pid == 0)
{
return; return;
} }
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
Mut<HANDLE> h_process = OpenProcess(PROCESS_TERMINATE, false, pid); Mut<HANDLE> h_process = OpenProcess(PROCESS_TERMINATE, false, pid);
if (h_process != NULL) { if (h_process != NULL)
{
::TerminateProcess(h_process, 9); ::TerminateProcess(h_process, 9);
CloseHandle(h_process); CloseHandle(h_process);
} }
@ -133,22 +160,24 @@ auto ProcessOps::terminate_process(Ref<Box<ProcessHandle>> handle) -> void {
#if IA_PLATFORM_UNIX #if IA_PLATFORM_UNIX
kill(pid, SIGKILL); kill(pid, SIGKILL);
#endif #endif
} }
auto ProcessOps::spawn_process_windows( auto ProcessOps::spawn_process_windows(Ref<String> command, Ref<String> args,
Ref<String> command, Ref<String> args,
const std::function<void(StringView)> on_output_line_callback, const std::function<void(StringView)> on_output_line_callback,
MutRef<std::atomic<NativeProcessID>> id) -> Result<i32> { MutRef<std::atomic<NativeProcessID>> id) -> Result<i32>
{
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
Mut<SECURITY_ATTRIBUTES> sa_attr = {sizeof(SECURITY_ATTRIBUTES), NULL, true}; Mut<SECURITY_ATTRIBUTES> sa_attr = {sizeof(SECURITY_ATTRIBUTES), NULL, true};
Mut<HANDLE> h_read = NULL; Mut<HANDLE> h_read = NULL;
Mut<HANDLE> h_write = NULL; Mut<HANDLE> h_write = NULL;
if (!CreatePipe(&h_read, &h_write, &sa_attr, 0)) { if (!CreatePipe(&h_read, &h_write, &sa_attr, 0))
{
return fail("Failed to create pipe"); return fail("Failed to create pipe");
} }
if (!SetHandleInformation(h_read, HANDLE_FLAG_INHERIT, 0)) { if (!SetHandleInformation(h_read, HANDLE_FLAG_INHERIT, 0))
{
return fail("Failed to secure pipe handles"); return fail("Failed to secure pipe handles");
} }
@ -162,12 +191,12 @@ auto ProcessOps::spawn_process_windows(
Mut<String> command_line = std::format("\"{}\" {}", command, args); Mut<String> command_line = std::format("\"{}\" {}", command, args);
const BOOL success = CreateProcessA(NULL, command_line.data(), NULL, NULL, const BOOL success = CreateProcessA(NULL, command_line.data(), NULL, NULL, true, 0, NULL, NULL, &si, &pi);
true, 0, NULL, NULL, &si, &pi);
CloseHandle(h_write); CloseHandle(h_write);
if (!success) { if (!success)
{
CloseHandle(h_read); CloseHandle(h_read);
return fail("CreateProcess failed: {}", GetLastError()); return fail("CreateProcess failed: {}", GetLastError());
} }
@ -178,9 +207,8 @@ auto ProcessOps::spawn_process_windows(
Mut<DWORD> bytes_read = 0; Mut<DWORD> bytes_read = 0;
Mut<Array<char, 4096>> buffer; Mut<Array<char, 4096>> buffer;
while (ReadFile(h_read, buffer.data(), static_cast<DWORD>(buffer.size()), while (ReadFile(h_read, buffer.data(), static_cast<DWORD>(buffer.size()), &bytes_read, NULL) && bytes_read != 0)
&bytes_read, NULL) && {
bytes_read != 0) {
line_buf.append(buffer.data(), bytes_read); line_buf.append(buffer.data(), bytes_read);
} }
line_buf.flush(); line_buf.flush();
@ -202,23 +230,27 @@ auto ProcessOps::spawn_process_windows(
AU_UNUSED(id); AU_UNUSED(id);
return fail("Windows implementation not available."); return fail("Windows implementation not available.");
#endif #endif
} }
auto ProcessOps::spawn_process_posix( auto ProcessOps::spawn_process_posix(Ref<String> command, Ref<String> args,
Ref<String> command, Ref<String> args,
const std::function<void(StringView)> on_output_line_callback, const std::function<void(StringView)> on_output_line_callback,
MutRef<std::atomic<NativeProcessID>> id) -> Result<i32> { MutRef<std::atomic<NativeProcessID>> id) -> Result<i32>
{
#if IA_PLATFORM_UNIX #if IA_PLATFORM_UNIX
Mut<Array<i32, 2>> pipefd; Mut<Array<i32, 2>> pipefd;
if (pipe(pipefd.data()) == -1) { if (pipe(pipefd.data()) == -1)
{
return fail("Failed to create pipe"); return fail("Failed to create pipe");
} }
const pid_t pid = fork(); const pid_t pid = fork();
if (pid == -1) { if (pid == -1)
{
return fail("Failed to fork process"); return fail("Failed to fork process");
} else if (pid == 0) { }
else if (pid == 0)
{
close(pipefd[0]); close(pipefd[0]);
dup2(pipefd[1], STDOUT_FILENO); dup2(pipefd[1], STDOUT_FILENO);
@ -235,45 +267,57 @@ auto ProcessOps::spawn_process_posix(
Mut<bool> in_quotes = false; Mut<bool> in_quotes = false;
Mut<bool> is_escaped = false; Mut<bool> is_escaped = false;
for (const char c : args) { for (const char c : args)
if (is_escaped) { {
if (is_escaped)
{
current_token += c; current_token += c;
is_escaped = false; is_escaped = false;
continue; continue;
} }
if (c == '\\') { if (c == '\\')
{
is_escaped = true; is_escaped = true;
continue; continue;
} }
if (c == '\"') { if (c == '\"')
{
in_quotes = !in_quotes; in_quotes = !in_quotes;
continue; continue;
} }
if (c == ' ' && !in_quotes) { if (c == ' ' && !in_quotes)
if (!current_token.empty()) { {
if (!current_token.empty())
{
arg_storage.push_back(current_token); arg_storage.push_back(current_token);
current_token.clear(); current_token.clear();
} }
} else { }
else
{
current_token += c; current_token += c;
} }
} }
if (!current_token.empty()) { if (!current_token.empty())
{
arg_storage.push_back(current_token); arg_storage.push_back(current_token);
} }
for (MutRef<String> s : arg_storage) { for (MutRef<String> s : arg_storage)
{
argv.push_back(s.data()); argv.push_back(s.data());
} }
argv.push_back(nullptr); argv.push_back(nullptr);
execvp(argv[0], argv.data()); execvp(argv[0], argv.data());
_exit(127); _exit(127);
} else { }
else
{
id.store(pid); id.store(pid);
close(pipefd[1]); close(pipefd[1]);
@ -282,7 +326,8 @@ auto ProcessOps::spawn_process_posix(
Mut<Array<char, 4096>> buffer; Mut<Array<char, 4096>> buffer;
Mut<isize> count; Mut<isize> count;
while ((count = read(pipefd[0], buffer.data(), buffer.size())) > 0) { while ((count = read(pipefd[0], buffer.data(), buffer.size())) > 0)
{
line_buf.append(buffer.data(), static_cast<usize>(count)); line_buf.append(buffer.data(), static_cast<usize>(count));
} }
line_buf.flush(); line_buf.flush();
@ -292,7 +337,8 @@ auto ProcessOps::spawn_process_posix(
waitpid(pid, &status, 0); waitpid(pid, &status, 0);
id.store(0); id.store(0);
if (WIFEXITED(status)) { if (WIFEXITED(status))
{
return WEXITSTATUS(status); return WEXITSTATUS(status);
} }
return -1; return -1;
@ -304,6 +350,6 @@ auto ProcessOps::spawn_process_posix(
AU_UNUSED(id); AU_UNUSED(id);
return fail("Posix implementation not available."); return fail("Posix implementation not available.");
#endif #endif
} }
} // namespace IACore } // namespace IACore

View File

@ -15,4 +15,6 @@
#include <IACore/SIMD.hpp> #include <IACore/SIMD.hpp>
namespace IACore {} // namespace IACore namespace IACore
{
} // namespace IACore

View File

@ -14,13 +14,15 @@
// limitations under the License. // limitations under the License.
#include <IACore/SocketOps.hpp> #include <IACore/SocketOps.hpp>
#include <cstring>
namespace IACore { namespace IACore
Mut<i32> SocketOps::s_init_count = 0; {
Mut<i32> SocketOps::s_init_count = 0;
auto SocketOps::close(const SocketHandle sock) -> void { auto SocketOps::close(const SocketHandle sock) -> void
if (sock == INVALID_SOCKET) { {
if (sock == INVALID_SOCKET)
{
return; return;
} }
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
@ -28,11 +30,12 @@ auto SocketOps::close(const SocketHandle sock) -> void {
#else #else
::close(sock); ::close(sock);
#endif #endif
} }
auto SocketOps::listen(const SocketHandle sock, const i32 queue_size) auto SocketOps::listen(const SocketHandle sock, const i32 queue_size) -> Result<void>
-> Result<void> { {
if (::listen(sock, queue_size) == 0) { if (::listen(sock, queue_size) == 0)
{
return {}; return {};
} }
@ -41,11 +44,13 @@ auto SocketOps::listen(const SocketHandle sock, const i32 queue_size)
#else #else
return fail("listen failed: {}", errno); return fail("listen failed: {}", errno);
#endif #endif
} }
auto SocketOps::create_unix_socket() -> Result<SocketHandle> { auto SocketOps::create_unix_socket() -> Result<SocketHandle>
{
const SocketHandle sock = socket(AF_UNIX, SOCK_STREAM, 0); const SocketHandle sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) { if (sock == INVALID_SOCKET)
{
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
return fail("socket(AF_UNIX) failed: {}", WSAGetLastError()); return fail("socket(AF_UNIX) failed: {}", WSAGetLastError());
#else #else
@ -53,11 +58,12 @@ auto SocketOps::create_unix_socket() -> Result<SocketHandle> {
#endif #endif
} }
return sock; return sock;
} }
auto SocketOps::bind_unix_socket(const SocketHandle sock, const char *path) auto SocketOps::bind_unix_socket(const SocketHandle sock, const char *path) -> Result<void>
-> Result<void> { {
if (sock == INVALID_SOCKET) { if (sock == INVALID_SOCKET)
{
return fail("Invalid socket handle"); return fail("Invalid socket handle");
} }
@ -73,8 +79,8 @@ auto SocketOps::bind_unix_socket(const SocketHandle sock, const char *path)
std::strncpy(addr.sun_path, path, max_len); std::strncpy(addr.sun_path, path, max_len);
#endif #endif
if (::bind(sock, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) == if (::bind(sock, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) == -1)
-1) { {
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
return fail("bind failed: {}", WSAGetLastError()); return fail("bind failed: {}", WSAGetLastError());
#else #else
@ -83,11 +89,12 @@ auto SocketOps::bind_unix_socket(const SocketHandle sock, const char *path)
} }
return {}; return {};
} }
auto SocketOps::connect_unix_socket(const SocketHandle sock, const char *path) auto SocketOps::connect_unix_socket(const SocketHandle sock, const char *path) -> Result<void>
-> Result<void> { {
if (sock == INVALID_SOCKET) { if (sock == INVALID_SOCKET)
{
return fail("Invalid socket handle"); return fail("Invalid socket handle");
} }
@ -101,8 +108,8 @@ auto SocketOps::connect_unix_socket(const SocketHandle sock, const char *path)
std::strncpy(addr.sun_path, path, max_len); std::strncpy(addr.sun_path, path, max_len);
#endif #endif
if (::connect(sock, reinterpret_cast<struct sockaddr *>(&addr), if (::connect(sock, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) == -1)
sizeof(addr)) == -1) { {
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
return fail("connect failed: {}", WSAGetLastError()); return fail("connect failed: {}", WSAGetLastError());
#else #else
@ -111,11 +118,13 @@ auto SocketOps::connect_unix_socket(const SocketHandle sock, const char *path)
} }
return {}; return {};
} }
auto SocketOps::is_port_available(const u16 port, const i32 type) -> bool { auto SocketOps::is_port_available(const u16 port, const i32 type) -> bool
{
const SocketHandle sock = socket(AF_INET, type, 0); const SocketHandle sock = socket(AF_INET, type, 0);
if (sock == INVALID_SOCKET) { if (sock == INVALID_SOCKET)
{
return false; return false;
} }
@ -125,22 +134,23 @@ auto SocketOps::is_port_available(const u16 port, const i32 type) -> bool {
addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_addr.s_addr = htonl(INADDR_ANY);
Mut<bool> is_free = false; Mut<bool> is_free = false;
if (::bind(sock, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) == if (::bind(sock, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) == 0)
0) { {
is_free = true; is_free = true;
} }
close(sock); close(sock);
return is_free; return is_free;
} }
auto SocketOps::is_would_block() -> bool { auto SocketOps::is_would_block() -> bool
{
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
return WSAGetLastError() == WSAEWOULDBLOCK; return WSAGetLastError() == WSAEWOULDBLOCK;
#else #else
return errno == EWOULDBLOCK || errno == EAGAIN; return errno == EWOULDBLOCK || errno == EAGAIN;
#endif #endif
} }
} // namespace IACore } // namespace IACore

View File

@ -16,8 +16,10 @@
#include <IACore/FileOps.hpp> #include <IACore/FileOps.hpp>
#include <IACore/StreamReader.hpp> #include <IACore/StreamReader.hpp>
namespace IACore { namespace IACore
auto StreamReader::create_from_file(Ref<Path> path) -> Result<StreamReader> { {
auto StreamReader::create_from_file(Ref<Path> path) -> Result<StreamReader>
{
Mut<usize> size = 0; Mut<usize> size = 0;
const u8 *ptr = AU_TRY(FileOps::map_file(path, size)); const u8 *ptr = AU_TRY(FileOps::map_file(path, size));
@ -26,37 +28,40 @@ auto StreamReader::create_from_file(Ref<Path> path) -> Result<StreamReader> {
reader.m_storage_type = StorageType::OwningMmap; reader.m_storage_type = StorageType::OwningMmap;
return reader; return reader;
} }
StreamReader::StreamReader(ForwardRef<Vec<u8>> data) StreamReader::StreamReader(ForwardRef<Vec<u8>> data)
: m_owning_vector(std::move(data)), : m_owning_vector(std::move(data)), m_storage_type(StorageType::OwningVector)
m_storage_type(StorageType::OwningVector) { {
m_data = m_owning_vector.data(); m_data = m_owning_vector.data();
m_data_size = m_owning_vector.size(); m_data_size = m_owning_vector.size();
} }
StreamReader::StreamReader(const Span<const u8> data) StreamReader::StreamReader(const Span<const u8> data)
: m_data(data.data()), m_data_size(data.size()), : m_data(data.data()), m_data_size(data.size()), m_storage_type(StorageType::NonOwning)
m_storage_type(StorageType::NonOwning) {} {
}
StreamReader::StreamReader(ForwardRef<StreamReader> other) StreamReader::StreamReader(ForwardRef<StreamReader> other)
: m_data(other.m_data), m_cursor(other.m_cursor), : m_data(other.m_data), m_cursor(other.m_cursor), m_data_size(other.m_data_size),
m_data_size(other.m_data_size), m_owning_vector(std::move(other.m_owning_vector)), m_storage_type(other.m_storage_type)
m_owning_vector(std::move(other.m_owning_vector)), {
m_storage_type(other.m_storage_type) {
other.m_storage_type = StorageType::NonOwning; other.m_storage_type = StorageType::NonOwning;
other.m_data = {}; other.m_data = {};
other.m_data_size = 0; other.m_data_size = 0;
if (m_storage_type == StorageType::OwningVector) { if (m_storage_type == StorageType::OwningVector)
{
m_data = m_owning_vector.data(); m_data = m_owning_vector.data();
} }
} }
auto StreamReader::operator=(ForwardRef<StreamReader> other) auto StreamReader::operator=(ForwardRef<StreamReader> other) -> MutRef<StreamReader>
-> MutRef<StreamReader> { {
if (this != &other) { if (this != &other)
if (m_storage_type == StorageType::OwningMmap) { {
if (m_storage_type == StorageType::OwningMmap)
{
FileOps::unmap_file(m_data); FileOps::unmap_file(m_data);
} }
@ -66,7 +71,8 @@ auto StreamReader::operator=(ForwardRef<StreamReader> other)
m_owning_vector = std::move(other.m_owning_vector); m_owning_vector = std::move(other.m_owning_vector);
m_storage_type = other.m_storage_type; 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(); m_data = m_owning_vector.data();
} }
@ -75,11 +81,13 @@ auto StreamReader::operator=(ForwardRef<StreamReader> other)
other.m_data_size = 0; other.m_data_size = 0;
} }
return *this; return *this;
} }
StreamReader::~StreamReader() { StreamReader::~StreamReader()
if (m_storage_type == StorageType::OwningMmap) { {
if (m_storage_type == StorageType::OwningMmap)
{
FileOps::unmap_file(m_data); FileOps::unmap_file(m_data);
} }
} }
} // namespace IACore } // namespace IACore

View File

@ -15,11 +15,14 @@
#include <IACore/StreamWriter.hpp> #include <IACore/StreamWriter.hpp>
namespace IACore { namespace IACore
{
auto StreamWriter::create_from_file(Ref<Path> path) -> Result<StreamWriter> { auto StreamWriter::create_from_file(Ref<Path> path) -> Result<StreamWriter>
{
Mut<FILE *> f = std::fopen(path.string().c_str(), "wb"); Mut<FILE *> f = std::fopen(path.string().c_str(), "wb");
if (!f) { if (!f)
{
return fail("Failed to open file for writing: {}", path.string()); return fail("Failed to open file for writing: {}", path.string());
} }
std::fclose(f); std::fclose(f);
@ -29,38 +32,42 @@ auto StreamWriter::create_from_file(Ref<Path> path) -> Result<StreamWriter> {
writer.m_storage_type = StorageType::OwningFile; writer.m_storage_type = StorageType::OwningFile;
return writer; return writer;
} }
StreamWriter::StreamWriter() : m_storage_type(StorageType::OwningVector) { StreamWriter::StreamWriter() : m_storage_type(StorageType::OwningVector)
{
m_capacity = 256; m_capacity = 256;
m_owning_vector.resize(m_capacity); m_owning_vector.resize(m_capacity);
m_buffer = m_owning_vector.data(); m_buffer = m_owning_vector.data();
} }
StreamWriter::StreamWriter(const Span<u8> data) StreamWriter::StreamWriter(const Span<u8> data)
: m_buffer(data.data()), m_cursor(0), m_capacity(data.size()), : m_buffer(data.data()), m_cursor(0), m_capacity(data.size()), m_storage_type(StorageType::NonOwning)
m_storage_type(StorageType::NonOwning) {} {
}
StreamWriter::StreamWriter(ForwardRef<StreamWriter> other) StreamWriter::StreamWriter(ForwardRef<StreamWriter> other)
: m_buffer(other.m_buffer), m_cursor(other.m_cursor), : m_buffer(other.m_buffer), m_cursor(other.m_cursor), m_capacity(other.m_capacity),
m_capacity(other.m_capacity), m_file_path(other.m_file_path), m_file_path(other.m_file_path), m_owning_vector(std::move(other.m_owning_vector)),
m_owning_vector(std::move(other.m_owning_vector)), m_storage_type(other.m_storage_type)
m_storage_type(other.m_storage_type) { {
other.m_capacity = {}; other.m_capacity = {};
other.m_buffer = {}; other.m_buffer = {};
other.m_storage_type = StorageType::NonOwning; other.m_storage_type = StorageType::NonOwning;
if (m_storage_type == StorageType::OwningVector) if (m_storage_type == StorageType::OwningVector)
m_buffer = m_owning_vector.data(); m_buffer = m_owning_vector.data();
} }
auto StreamWriter::operator=(ForwardRef<StreamWriter> other) auto StreamWriter::operator=(ForwardRef<StreamWriter> other) -> MutRef<StreamWriter>
-> MutRef<StreamWriter> { {
if (this != &other) { if (this != &other)
if (m_storage_type == StorageType::OwningFile) { {
if (const Result<void> res = flush_to_disk(); !res) { if (m_storage_type == StorageType::OwningFile)
std::fprintf(stderr, "[IACore] Data loss in StreamWriter move: %s\n", {
res.error().c_str()); if (const Result<void> res = flush_to_disk(); !res)
{
std::fprintf(stderr, "[IACore] Data loss in StreamWriter move: %s\n", res.error().c_str());
} }
} }
@ -80,47 +87,58 @@ auto StreamWriter::operator=(ForwardRef<StreamWriter> other)
other.m_storage_type = StorageType::NonOwning; other.m_storage_type = StorageType::NonOwning;
} }
return *this; return *this;
} }
StreamWriter::~StreamWriter() { StreamWriter::~StreamWriter()
if (m_storage_type == StorageType::OwningFile) { {
if (const Result<void> res = flush_to_disk(); !res) { if (m_storage_type == StorageType::OwningFile)
std::fprintf(stderr, "[IACore] LOST DATA in ~StreamWriter: %s\n", {
res.error().c_str()); if (const Result<void> res = flush_to_disk(); !res)
{
std::fprintf(stderr, "[IACore] LOST DATA in ~StreamWriter: %s\n", res.error().c_str());
}
} }
} }
}
auto StreamWriter::flush() -> Result<void> { auto StreamWriter::flush() -> Result<void>
{
Mut<Result<void>> res = flush_to_disk(); Mut<Result<void>> res = flush_to_disk();
if (res.has_value()) { if (res.has_value())
{
m_storage_type = StorageType::OwningVector; m_storage_type = StorageType::OwningVector;
} }
return res; return res;
} }
auto StreamWriter::flush_to_disk() -> Result<void> { auto StreamWriter::flush_to_disk() -> Result<void>
if (m_storage_type != StorageType::OwningFile || m_file_path.empty()) { {
if (m_storage_type != StorageType::OwningFile || m_file_path.empty())
{
return {}; return {};
} }
Mut<FILE *> f = std::fopen(m_file_path.string().c_str(), "wb"); Mut<FILE *> f = std::fopen(m_file_path.string().c_str(), "wb");
if (!f) { if (!f)
{
return fail("Failed to open file for writing: {}", m_file_path.string()); return fail("Failed to open file for writing: {}", m_file_path.string());
} }
const usize written = std::fwrite(m_buffer, 1, m_cursor, f); const usize written = std::fwrite(m_buffer, 1, m_cursor, f);
std::fclose(f); std::fclose(f);
if (written != m_cursor) { if (written != m_cursor)
{
return fail("Incomplete write: {} of {} bytes written", written, m_cursor); return fail("Incomplete write: {} of {} bytes written", written, m_cursor);
} }
return {}; return {};
} }
auto StreamWriter::write(const u8 byte, const usize count) -> Result<void> { auto StreamWriter::write(const u8 byte, const usize count) -> Result<void>
if (m_cursor + count > m_capacity) { {
if (m_storage_type == StorageType::NonOwning) { if (m_cursor + count > m_capacity)
{
if (m_storage_type == StorageType::NonOwning)
{
return fail("StreamWriter buffer overflow (NonOwning)"); return fail("StreamWriter buffer overflow (NonOwning)");
} }
@ -136,11 +154,14 @@ auto StreamWriter::write(const u8 byte, const usize count) -> Result<void> {
std::memset(m_buffer + m_cursor, byte, count); std::memset(m_buffer + m_cursor, byte, count);
m_cursor += count; m_cursor += count;
return {}; return {};
} }
auto StreamWriter::write(const void *buffer, const usize size) -> Result<void> { auto StreamWriter::write(const void *buffer, const usize size) -> Result<void>
if (m_cursor + size > m_capacity) { {
if (m_storage_type == StorageType::NonOwning) { if (m_cursor + size > m_capacity)
{
if (m_storage_type == StorageType::NonOwning)
{
return fail("StreamWriter buffer overflow (NonOwning)"); return fail("StreamWriter buffer overflow (NonOwning)");
} }
@ -156,6 +177,6 @@ auto StreamWriter::write(const void *buffer, const usize size) -> Result<void> {
std::memcpy(m_buffer + m_cursor, buffer, size); std::memcpy(m_buffer + m_cursor, buffer, size);
m_cursor += size; m_cursor += size;
return {}; return {};
} }
} // namespace IACore } // namespace IACore

View File

@ -15,17 +15,18 @@
#include <IACore/StringOps.hpp> #include <IACore/StringOps.hpp>
namespace IACore { namespace IACore
{
static const String BASE64_CHAR_TABLE = static const String BASE64_CHAR_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static auto is_base64(const u8 c) -> bool { static auto is_base64(const u8 c) -> bool
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || {
(c >= '0' && c <= '9') || (c == '+') || (c == '/'); return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c == '+') || (c == '/');
} }
static auto get_base64_index(const u8 c) -> u8 { static auto get_base64_index(const u8 c) -> u8
{
if (c >= 'A' && c <= 'Z') if (c >= 'A' && c <= 'Z')
return c - 'A'; return c - 'A';
if (c >= 'a' && c <= 'z') if (c >= 'a' && c <= 'z')
@ -37,13 +38,15 @@ static auto get_base64_index(const u8 c) -> u8 {
if (c == '/') if (c == '/')
return 63; return 63;
return 0; return 0;
} }
auto StringOps::encode_base64(const Span<const u8> data) -> String { auto StringOps::encode_base64(const Span<const u8> data) -> String
{
Mut<String> result; Mut<String> result;
result.reserve(((data.size() + 2) / 3) * 4); result.reserve(((data.size() + 2) / 3) * 4);
for (Mut<usize> i = 0; i < data.size(); i += 3) { for (Mut<usize> i = 0; i < data.size(); i += 3)
{
const u32 b0 = data[i]; const u32 b0 = data[i];
const u32 b1 = (i + 1 < data.size()) ? data[i + 1] : 0; const u32 b1 = (i + 1 < data.size()) ? data[i + 1] : 0;
const u32 b2 = (i + 2 < data.size()) ? data[i + 2] : 0; const u32 b2 = (i + 2 < data.size()) ? data[i + 2] : 0;
@ -53,39 +56,50 @@ auto StringOps::encode_base64(const Span<const u8> data) -> String {
result += BASE64_CHAR_TABLE[(triple >> 18) & 0x3F]; result += BASE64_CHAR_TABLE[(triple >> 18) & 0x3F];
result += BASE64_CHAR_TABLE[(triple >> 12) & 0x3F]; result += BASE64_CHAR_TABLE[(triple >> 12) & 0x3F];
if (i + 1 < data.size()) { if (i + 1 < data.size())
{
result += BASE64_CHAR_TABLE[(triple >> 6) & 0x3F]; result += BASE64_CHAR_TABLE[(triple >> 6) & 0x3F];
} else { }
else
{
result += '='; result += '=';
} }
if (i + 2 < data.size()) { if (i + 2 < data.size())
{
result += BASE64_CHAR_TABLE[triple & 0x3F]; result += BASE64_CHAR_TABLE[triple & 0x3F];
} else { }
else
{
result += '='; result += '=';
} }
} }
return result; return result;
} }
auto StringOps::decode_base64(Ref<String> data) -> Vec<u8> { auto StringOps::decode_base64(Ref<String> data) -> Vec<u8>
{
Mut<Vec<u8>> result; Mut<Vec<u8>> result;
result.reserve(data.size() * 3 / 4); result.reserve(data.size() * 3 / 4);
Mut<i32> i = 0; Mut<i32> i = 0;
Mut<Array<u8, 4>> tmp_buf = {}; Mut<Array<u8, 4>> tmp_buf = {};
for (const char c_char : data) { for (const char c_char : data)
{
const u8 c = static_cast<u8>(c_char); const u8 c = static_cast<u8>(c_char);
if (c == '=') { if (c == '=')
{
break; break;
} }
if (!is_base64(c)) { if (!is_base64(c))
{
break; break;
} }
tmp_buf[i++] = c; tmp_buf[i++] = c;
if (i == 4) { if (i == 4)
{
const u8 n0 = get_base64_index(tmp_buf[0]); const u8 n0 = get_base64_index(tmp_buf[0]);
const u8 n1 = get_base64_index(tmp_buf[1]); const u8 n1 = get_base64_index(tmp_buf[1]);
const u8 n2 = get_base64_index(tmp_buf[2]); const u8 n2 = get_base64_index(tmp_buf[2]);
@ -99,8 +113,10 @@ auto StringOps::decode_base64(Ref<String> data) -> Vec<u8> {
} }
} }
if (i > 0) { if (i > 0)
for (Mut<i32> j = i; j < 4; ++j) { {
for (Mut<i32> j = i; j < 4; ++j)
{
tmp_buf[j] = 'A'; tmp_buf[j] = 'A';
} }
@ -108,15 +124,17 @@ auto StringOps::decode_base64(Ref<String> data) -> Vec<u8> {
const u8 n1 = get_base64_index(tmp_buf[1]); const u8 n1 = get_base64_index(tmp_buf[1]);
const u8 n2 = get_base64_index(tmp_buf[2]); const u8 n2 = get_base64_index(tmp_buf[2]);
if (i > 1) { if (i > 1)
{
result.push_back((n0 << 2) | ((n1 & 0x30) >> 4)); result.push_back((n0 << 2) | ((n1 & 0x30) >> 4));
} }
if (i > 2) { if (i > 2)
{
result.push_back(((n1 & 0x0F) << 4) | ((n2 & 0x3C) >> 2)); result.push_back(((n1 & 0x0F) << 4) | ((n2 & 0x3C) >> 2));
} }
} }
return result; return result;
} }
} // namespace IACore } // namespace IACore

View File

@ -17,90 +17,104 @@
#include <chrono> #include <chrono>
#include <cstdlib> #include <cstdlib>
namespace IACore { namespace IACore
namespace { {
auto from_hex_char(const char c) -> i32 { namespace
if (c >= '0' && c <= '9') { {
auto from_hex_char(const char c) -> i32
{
if (c >= '0' && c <= '9')
{
return c - '0'; return c - '0';
} }
if (c >= 'A' && c <= 'F') { if (c >= 'A' && c <= 'F')
{
return c - 'A' + 10; return c - 'A' + 10;
} }
if (c >= 'a' && c <= 'f') { if (c >= 'a' && c <= 'f')
{
return c - 'a' + 10; return c - 'a' + 10;
} }
return -1; return -1;
} }
} // namespace } // namespace
extern Mut<std::chrono::high_resolution_clock::time_point> g_start_time; extern Mut<std::chrono::high_resolution_clock::time_point> g_start_time;
auto Utils::get_unix_time() -> u64 { auto Utils::get_unix_time() -> u64
const std::chrono::system_clock::time_point now = {
std::chrono::system_clock::now(); const std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
return std::chrono::duration_cast<std::chrono::seconds>( return std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
now.time_since_epoch()) }
.count();
}
auto Utils::get_ticks_count() -> u64 { auto Utils::get_ticks_count() -> u64
{
const std::chrono::high_resolution_clock::duration duration = const std::chrono::high_resolution_clock::duration duration =
std::chrono::high_resolution_clock::now() - g_start_time; std::chrono::high_resolution_clock::now() - g_start_time;
return std::chrono::duration_cast<std::chrono::milliseconds>(duration) return std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
.count(); }
}
auto Utils::get_seconds_count() -> f64 { auto Utils::get_seconds_count() -> f64
{
const std::chrono::high_resolution_clock::duration duration = const std::chrono::high_resolution_clock::duration duration =
std::chrono::high_resolution_clock::now() - g_start_time; std::chrono::high_resolution_clock::now() - g_start_time;
return static_cast<f64>( return static_cast<f64>(std::chrono::duration_cast<std::chrono::seconds>(duration).count());
std::chrono::duration_cast<std::chrono::seconds>(duration).count()); }
}
auto Utils::get_random() -> f32 { auto Utils::get_random() -> f32
{
return static_cast<f32>(std::rand()) / static_cast<f32>(RAND_MAX); return static_cast<f32>(std::rand()) / static_cast<f32>(RAND_MAX);
} }
auto Utils::get_random(const u64 max) -> u64 { auto Utils::get_random(const u64 max) -> u64
{
return static_cast<u64>(static_cast<f32>(max) * get_random()); return static_cast<u64>(static_cast<f32>(max) * get_random());
} }
auto Utils::get_random(const i64 min, const i64 max) -> i64 { auto Utils::get_random(const i64 min, const i64 max) -> i64
{
return min + static_cast<i64>(static_cast<f32>(max - min) * get_random()); return min + static_cast<i64>(static_cast<f32>(max - min) * get_random());
} }
auto Utils::sleep(const u64 milliseconds) -> void { auto Utils::sleep(const u64 milliseconds) -> void
{
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
} }
auto Utils::binary_to_hex_string(const Span<const u8> data) -> String { auto Utils::binary_to_hex_string(const Span<const u8> data) -> String
{
static constexpr const char LUT[17] = "0123456789ABCDEF"; static constexpr const char LUT[17] = "0123456789ABCDEF";
Mut<String> res = String(); Mut<String> res = String();
res.reserve(data.size() * 2); res.reserve(data.size() * 2);
for (u8 b : data) { for (u8 b : data)
{
res.push_back(LUT[(b >> 4) & 0x0F]); res.push_back(LUT[(b >> 4) & 0x0F]);
res.push_back(LUT[b & 0x0F]); res.push_back(LUT[b & 0x0F]);
} }
return res; return res;
} }
auto Utils::hex_string_to_binary(const StringView hex) -> Result<Vec<u8>> { auto Utils::hex_string_to_binary(const StringView hex) -> Result<Vec<u8>>
if (hex.size() % 2 != 0) { {
if (hex.size() % 2 != 0)
{
return fail("Hex string must have even length"); return fail("Hex string must have even length");
} }
Mut<Vec<u8>> out = Vec<u8>(); Mut<Vec<u8>> out = Vec<u8>();
out.reserve(hex.size() / 2); out.reserve(hex.size() / 2);
for (Mut<usize> i = 0; i < hex.size(); i += 2) { for (Mut<usize> i = 0; i < hex.size(); i += 2)
{
const char high = hex[i]; const char high = hex[i];
const char low = hex[i + 1]; const char low = hex[i + 1];
const i32 h = from_hex_char(high); const i32 h = from_hex_char(high);
const i32 l = from_hex_char(low); const i32 l = from_hex_char(low);
if (h == -1 || l == -1) { if (h == -1 || l == -1)
{
return fail("Invalid hex character found"); return fail("Invalid hex character found");
} }
@ -108,5 +122,5 @@ auto Utils::hex_string_to_binary(const StringView hex) -> Result<Vec<u8>> {
} }
return out; return out;
} }
} // namespace IACore } // namespace IACore

View File

@ -16,45 +16,54 @@
#include <IACore/XML.hpp> #include <IACore/XML.hpp>
#include <sstream> #include <sstream>
namespace IACore { namespace IACore
{
auto XML::parse_from_string(Ref<String> data) -> Result<Document> { auto XML::parse_from_string(Ref<String> data) -> Result<Document>
{
Mut<Document> doc; Mut<Document> doc;
const pugi::xml_parse_result parse_result = doc.load_string(data.c_str()); const pugi::xml_parse_result parse_result = doc.load_string(data.c_str());
if (!parse_result) { if (!parse_result)
{
return fail("Failed to parse XML {}", parse_result.description()); return fail("Failed to parse XML {}", parse_result.description());
} }
return std::move(doc); return std::move(doc);
} }
auto XML::parse_from_file(Ref<Path> path) -> Result<Document> { auto XML::parse_from_file(Ref<Path> path) -> Result<Document>
{
Mut<Document> doc; Mut<Document> doc;
const pugi::xml_parse_result parse_result = const pugi::xml_parse_result parse_result = doc.load_file(path.string().c_str());
doc.load_file(path.string().c_str()); if (!parse_result)
if (!parse_result) { {
return fail("Failed to parse XML {}", parse_result.description()); return fail("Failed to parse XML {}", parse_result.description());
} }
return std::move(doc); return std::move(doc);
} }
auto XML::serialize_to_string(Ref<Node> node, const bool escape) -> String { auto XML::serialize_to_string(Ref<Node> node, const bool escape) -> String
{
Mut<std::ostringstream> oss; Mut<std::ostringstream> oss;
node.print(oss); node.print(oss);
return escape ? escape_xml_string(oss.str()) : oss.str(); return escape ? escape_xml_string(oss.str()) : oss.str();
} }
auto XML::serialize_to_string(Ref<Document> doc, const bool escape) -> String { auto XML::serialize_to_string(Ref<Document> doc, const bool escape) -> String
{
Mut<std::ostringstream> oss; Mut<std::ostringstream> oss;
doc.save(oss); doc.save(oss);
return escape ? escape_xml_string(oss.str()) : oss.str(); return escape ? escape_xml_string(oss.str()) : oss.str();
} }
auto XML::escape_xml_string(Ref<String> xml) -> String { auto XML::escape_xml_string(Ref<String> xml) -> String
{
Mut<String> buffer; Mut<String> buffer;
buffer.reserve(xml.size() + (xml.size() / 10)); buffer.reserve(xml.size() + (xml.size() / 10));
for (const char c : xml) { for (const char c : xml)
switch (c) { {
switch (c)
{
case '&': case '&':
buffer.append("&amp;"); buffer.append("&amp;");
break; break;
@ -77,6 +86,6 @@ auto XML::escape_xml_string(Ref<String> xml) -> String {
} }
return buffer; return buffer;
} }
} // namespace IACore } // namespace IACore

View File

@ -17,32 +17,42 @@
#include <IACore/PCH.hpp> #include <IACore/PCH.hpp>
namespace IACore { namespace IACore
class RingBufferView { {
class RingBufferView
{
public: public:
static constexpr const u16 PACKET_ID_SKIP = 0; static constexpr const u16 PACKET_ID_SKIP = 0;
struct ControlBlock { struct ControlBlock
struct alignas(64) { {
struct alignas(64)
{
Mut<std::atomic<u32>> write_offset{0}; Mut<std::atomic<u32>> write_offset{0};
} producer; } producer;
struct alignas(64) { struct alignas(64)
{
Mut<std::atomic<u32>> read_offset{0}; Mut<std::atomic<u32>> read_offset{0};
Mut<u32> capacity{0}; Mut<u32> capacity{0};
} consumer; } consumer;
}; };
static_assert(offsetof(ControlBlock, consumer) == 64, static_assert(offsetof(ControlBlock, consumer) == 64, "False sharing detected in ControlBlock");
"False sharing detected in ControlBlock");
struct PacketHeader { struct PacketHeader
PacketHeader() : id(0), payload_size(0) {} {
PacketHeader() : id(0), payload_size(0)
{
}
PacketHeader(const u16 id) : id(id), payload_size(0) {} PacketHeader(const u16 id) : id(id), payload_size(0)
{
}
PacketHeader(const u16 id, const u16 payload_size) PacketHeader(const u16 id, const u16 payload_size) : id(id), payload_size(payload_size)
: id(id), payload_size(payload_size) {} {
}
Mut<u16> id{}; Mut<u16> id{};
Mut<u16> payload_size{}; Mut<u16> payload_size{};
@ -51,17 +61,15 @@ public:
public: public:
static auto default_instance() -> RingBufferView; static auto default_instance() -> RingBufferView;
static auto create(Ref<Span<u8>> buffer, const bool is_owner) static auto create(Ref<Span<u8>> buffer, const bool is_owner) -> Result<RingBufferView>;
static auto create(ControlBlock *control_block, Ref<Span<u8>> buffer, const bool is_owner)
-> Result<RingBufferView>; -> Result<RingBufferView>;
static auto create(ControlBlock *control_block, Ref<Span<u8>> buffer,
const bool is_owner) -> Result<RingBufferView>;
// Returns: // Returns:
// - nullopt if empty // - nullopt if empty
// - bytes_read if success // - bytes_read if success
// - Error if buffer too small // - Error if buffer too small
auto pop(MutRef<PacketHeader> out_header, Ref<Span<u8>> out_buffer) auto pop(MutRef<PacketHeader> out_header, Ref<Span<u8>> out_buffer) -> Result<Option<usize>>;
-> Result<Option<usize>>;
auto push(const u16 packet_id, Ref<Span<const u8>> data) -> Result<void>; auto push(const u16 packet_id, Ref<Span<const u8>> data) -> Result<void>;
@ -71,8 +79,7 @@ public:
protected: protected:
RingBufferView(Ref<Span<u8>> buffer, const bool is_owner); RingBufferView(Ref<Span<u8>> buffer, const bool is_owner);
RingBufferView(ControlBlock *control_block, Ref<Span<u8>> buffer, RingBufferView(ControlBlock *control_block, Ref<Span<u8>> buffer, const bool is_owner);
const bool is_owner);
private: private:
Mut<u8 *> m_data_ptr{}; Mut<u8 *> m_data_ptr{};
@ -80,125 +87,127 @@ private:
Mut<ControlBlock *> m_control_block{}; Mut<ControlBlock *> m_control_block{};
private: private:
auto write_wrapped(const u32 offset, const void *data, const u32 size) auto write_wrapped(const u32 offset, const void *data, const u32 size) -> void;
-> void;
auto read_wrapped(const u32 offset, void *out_data, const u32 size) -> void; auto read_wrapped(const u32 offset, void *out_data, const u32 size) -> void;
}; };
inline auto RingBufferView::default_instance() -> RingBufferView { inline auto RingBufferView::default_instance() -> RingBufferView
{
return RingBufferView(nullptr, {}, false); return RingBufferView(nullptr, {}, false);
} }
inline auto RingBufferView::create(Ref<Span<u8>> buffer, const bool is_owner) inline auto RingBufferView::create(Ref<Span<u8>> buffer, const bool is_owner) -> Result<RingBufferView>
-> Result<RingBufferView> { {
if (buffer.size() <= sizeof(ControlBlock)) { if (buffer.size() <= sizeof(ControlBlock))
{
return fail("Buffer too small for ControlBlock"); return fail("Buffer too small for ControlBlock");
} }
if (!is_owner) { if (!is_owner)
{
const ControlBlock *cb = reinterpret_cast<ControlBlock *>(buffer.data()); const ControlBlock *cb = reinterpret_cast<ControlBlock *>(buffer.data());
const u32 capacity = static_cast<u32>(buffer.size()) - sizeof(ControlBlock); const u32 capacity = static_cast<u32>(buffer.size()) - sizeof(ControlBlock);
if (cb->consumer.capacity != capacity) { if (cb->consumer.capacity != capacity)
{
return fail("Capacity mismatch"); return fail("Capacity mismatch");
} }
} }
return RingBufferView(buffer, is_owner); return RingBufferView(buffer, is_owner);
} }
inline auto RingBufferView::create(ControlBlock *control_block, inline auto RingBufferView::create(ControlBlock *control_block, Ref<Span<u8>> buffer, const bool is_owner)
Ref<Span<u8>> buffer, const bool is_owner) -> Result<RingBufferView>
-> Result<RingBufferView> { {
if (control_block == nullptr) { if (control_block == nullptr)
{
return fail("ControlBlock is null"); return fail("ControlBlock is null");
} }
if (buffer.empty()) { if (buffer.empty())
{
return fail("Buffer is empty"); return fail("Buffer is empty");
} }
return RingBufferView(control_block, buffer, is_owner); return RingBufferView(control_block, buffer, is_owner);
} }
inline RingBufferView::RingBufferView(Ref<Span<u8>> buffer, inline RingBufferView::RingBufferView(Ref<Span<u8>> buffer, const bool is_owner)
const bool is_owner) { {
m_control_block = reinterpret_cast<ControlBlock *>(buffer.data()); m_control_block = reinterpret_cast<ControlBlock *>(buffer.data());
m_data_ptr = buffer.data() + sizeof(ControlBlock); m_data_ptr = buffer.data() + sizeof(ControlBlock);
m_capacity = static_cast<u32>(buffer.size()) - sizeof(ControlBlock); m_capacity = static_cast<u32>(buffer.size()) - sizeof(ControlBlock);
if (is_owner) { if (is_owner)
{
m_control_block->consumer.capacity = m_capacity; m_control_block->consumer.capacity = m_capacity;
m_control_block->producer.write_offset.store(0, std::memory_order_release); m_control_block->producer.write_offset.store(0, std::memory_order_release);
m_control_block->consumer.read_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, inline RingBufferView::RingBufferView(ControlBlock *control_block, Ref<Span<u8>> buffer, const bool is_owner)
Ref<Span<u8>> buffer, {
const bool is_owner) {
m_control_block = control_block; m_control_block = control_block;
m_data_ptr = buffer.data(); m_data_ptr = buffer.data();
m_capacity = static_cast<u32>(buffer.size()); m_capacity = static_cast<u32>(buffer.size());
if (is_owner) { if (is_owner)
{
m_control_block->consumer.capacity = m_capacity; m_control_block->consumer.capacity = m_capacity;
m_control_block->producer.write_offset.store(0, std::memory_order_release); m_control_block->producer.write_offset.store(0, std::memory_order_release);
m_control_block->consumer.read_offset.store(0, std::memory_order_release); m_control_block->consumer.read_offset.store(0, std::memory_order_release);
} }
} }
inline auto RingBufferView::pop(MutRef<PacketHeader> out_header, inline auto RingBufferView::pop(MutRef<PacketHeader> out_header, Ref<Span<u8>> out_buffer) -> Result<Option<usize>>
Ref<Span<u8>> out_buffer) {
-> Result<Option<usize>> { const u32 write = m_control_block->producer.write_offset.load(std::memory_order_acquire);
const u32 write = const u32 read = m_control_block->consumer.read_offset.load(std::memory_order_relaxed);
m_control_block->producer.write_offset.load(std::memory_order_acquire);
const u32 read =
m_control_block->consumer.read_offset.load(std::memory_order_relaxed);
const u32 cap = m_capacity; const u32 cap = m_capacity;
if (read == write) { if (read == write)
{
return std::nullopt; return std::nullopt;
} }
read_wrapped(read, &out_header, sizeof(PacketHeader)); read_wrapped(read, &out_header, sizeof(PacketHeader));
if (out_header.payload_size > out_buffer.size()) { if (out_header.payload_size > out_buffer.size())
return fail("Buffer too small: needed {}, provided {}", {
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) { if (out_header.payload_size > 0)
{
const u32 data_read_offset = (read + sizeof(PacketHeader)) % cap; const u32 data_read_offset = (read + sizeof(PacketHeader)) % cap;
read_wrapped(data_read_offset, out_buffer.data(), out_header.payload_size); read_wrapped(data_read_offset, out_buffer.data(), out_header.payload_size);
} }
const u32 new_read_offset = const u32 new_read_offset = (read + sizeof(PacketHeader) + out_header.payload_size) % cap;
(read + sizeof(PacketHeader) + out_header.payload_size) % cap; m_control_block->consumer.read_offset.store(new_read_offset, std::memory_order_release);
m_control_block->consumer.read_offset.store(new_read_offset,
std::memory_order_release);
return std::make_optional(static_cast<usize>(out_header.payload_size)); return std::make_optional(static_cast<usize>(out_header.payload_size));
} }
inline auto RingBufferView::push(const u16 packet_id, Ref<Span<const u8>> data) inline auto RingBufferView::push(const u16 packet_id, Ref<Span<const u8>> data) -> Result<void>
-> Result<void> { {
if (data.size() > std::numeric_limits<u16>::max()) { if (data.size() > std::numeric_limits<u16>::max())
{
return fail("Data size exceeds u16 limit"); return fail("Data size exceeds u16 limit");
} }
const u32 total_size = sizeof(PacketHeader) + static_cast<u32>(data.size()); const u32 total_size = sizeof(PacketHeader) + static_cast<u32>(data.size());
const u32 read = const u32 read = m_control_block->consumer.read_offset.load(std::memory_order_acquire);
m_control_block->consumer.read_offset.load(std::memory_order_acquire); const u32 write = m_control_block->producer.write_offset.load(std::memory_order_relaxed);
const u32 write =
m_control_block->producer.write_offset.load(std::memory_order_relaxed);
const u32 cap = m_capacity; const u32 cap = m_capacity;
const u32 free_space = const u32 free_space = (read <= write) ? (m_capacity - write) + read : (read - write);
(read <= write) ? (m_capacity - write) + read : (read - write);
// Leave 1 byte empty (prevent ambiguities) // Leave 1 byte empty (prevent ambiguities)
if (free_space <= total_size) { if (free_space <= total_size)
{
return fail("RingBuffer full"); return fail("RingBuffer full");
} }
@ -207,27 +216,30 @@ inline auto RingBufferView::push(const u16 packet_id, Ref<Span<const u8>> data)
const u32 data_write_offset = (write + sizeof(PacketHeader)) % cap; const u32 data_write_offset = (write + sizeof(PacketHeader)) % cap;
if (!data.empty()) { if (!data.empty())
write_wrapped(data_write_offset, data.data(), {
static_cast<u32>(data.size())); write_wrapped(data_write_offset, data.data(), static_cast<u32>(data.size()));
} }
const u32 new_write_offset = (data_write_offset + data.size()) % cap; const u32 new_write_offset = (data_write_offset + data.size()) % cap;
m_control_block->producer.write_offset.store(new_write_offset, m_control_block->producer.write_offset.store(new_write_offset, std::memory_order_release);
std::memory_order_release);
return {}; return {};
} }
inline auto RingBufferView::get_control_block() -> ControlBlock * { inline auto RingBufferView::get_control_block() -> ControlBlock *
{
return m_control_block; return m_control_block;
} }
inline auto RingBufferView::write_wrapped(const u32 offset, const void *data, inline auto RingBufferView::write_wrapped(const u32 offset, const void *data, const u32 size) -> void
const u32 size) -> void { {
if (offset + size <= m_capacity) { if (offset + size <= m_capacity)
{
std::memcpy(m_data_ptr + offset, data, size); std::memcpy(m_data_ptr + offset, data, size);
} else { }
else
{
const u32 first_chunk = m_capacity - offset; const u32 first_chunk = m_capacity - offset;
const u32 second_chunk = size - first_chunk; const u32 second_chunk = size - first_chunk;
@ -236,13 +248,16 @@ inline auto RingBufferView::write_wrapped(const u32 offset, const void *data,
std::memcpy(m_data_ptr + offset, src, first_chunk); std::memcpy(m_data_ptr + offset, src, first_chunk);
std::memcpy(m_data_ptr, src + first_chunk, second_chunk); std::memcpy(m_data_ptr, src + first_chunk, second_chunk);
} }
} }
inline auto RingBufferView::read_wrapped(const u32 offset, void *out_data, inline auto RingBufferView::read_wrapped(const u32 offset, void *out_data, const u32 size) -> void
const u32 size) -> void { {
if (offset + size <= m_capacity) { if (offset + size <= m_capacity)
{
std::memcpy(out_data, m_data_ptr + offset, size); std::memcpy(out_data, m_data_ptr + offset, size);
} else { }
else
{
const u32 first_chunk = m_capacity - offset; const u32 first_chunk = m_capacity - offset;
const u32 second_chunk = size - first_chunk; const u32 second_chunk = size - first_chunk;
@ -251,10 +266,11 @@ inline auto RingBufferView::read_wrapped(const u32 offset, void *out_data,
std::memcpy(dst, m_data_ptr + offset, first_chunk); std::memcpy(dst, m_data_ptr + offset, first_chunk);
std::memcpy(dst + first_chunk, m_data_ptr, second_chunk); std::memcpy(dst + first_chunk, m_data_ptr, second_chunk);
} }
} }
[[nodiscard]] inline auto RingBufferView::is_valid() const -> bool { [[nodiscard]] inline auto RingBufferView::is_valid() const -> bool
{
return m_control_block && m_data_ptr && m_capacity; return m_control_block && m_data_ptr && m_capacity;
} }
} // namespace IACore } // namespace IACore

View File

@ -20,17 +20,24 @@
#include <functional> #include <functional>
#include <stop_token> #include <stop_token>
namespace IACore { namespace IACore
class AsyncOps { {
class AsyncOps
{
public: public:
using TaskTag = u64; using TaskTag = u64;
using WorkerId = u16; using WorkerId = u16;
static constexpr const WorkerId MAIN_THREAD_WORKER_ID = 0; static constexpr const WorkerId MAIN_THREAD_WORKER_ID = 0;
enum class Priority : u8 { High, Normal }; enum class Priority : u8
{
High,
Normal
};
struct Schedule { struct Schedule
{
Mut<std::atomic<i32>> counter{0}; Mut<std::atomic<i32>> counter{0};
}; };
@ -38,9 +45,8 @@ public:
static auto initialize_scheduler(const u8 worker_count = 0) -> Result<void>; static auto initialize_scheduler(const u8 worker_count = 0) -> Result<void>;
static auto terminate_scheduler() -> void; static auto terminate_scheduler() -> void;
static auto schedule_task(Mut<std::function<void(const WorkerId)>> task, static auto schedule_task(Mut<std::function<void(const WorkerId)>> task, const TaskTag tag,
const TaskTag tag, Mut<Schedule *> schedule, Mut<Schedule *> schedule, const Priority priority = Priority::Normal) -> void;
const Priority priority = Priority::Normal) -> void;
static auto cancel_tasks_of_tag(const TaskTag tag) -> void; static auto cancel_tasks_of_tag(const TaskTag tag) -> void;
@ -51,14 +57,14 @@ public:
IA_NODISCARD static auto get_worker_count() -> WorkerId; IA_NODISCARD static auto get_worker_count() -> WorkerId;
private: private:
struct ScheduledTask { struct ScheduledTask
{
Mut<TaskTag> tag{}; Mut<TaskTag> tag{};
Mut<Schedule *> schedule_handle{}; Mut<Schedule *> schedule_handle{};
Mut<std::function<void(const WorkerId)>> task{}; Mut<std::function<void(const WorkerId)>> task{};
}; };
static auto schedule_worker_loop(Mut<std::stop_token> stop_token, static auto schedule_worker_loop(Mut<std::stop_token> stop_token, const WorkerId worker_id) -> void;
const WorkerId worker_id) -> void;
private: private:
static Mut<std::mutex> s_queue_mutex; static Mut<std::mutex> s_queue_mutex;
@ -66,5 +72,5 @@ private:
static Mut<Vec<std::jthread>> s_schedule_workers; static Mut<Vec<std::jthread>> s_schedule_workers;
static Mut<std::deque<ScheduledTask>> s_high_priority_queue; static Mut<std::deque<ScheduledTask>> s_high_priority_queue;
static Mut<std::deque<ScheduledTask>> s_normal_priority_queue; static Mut<std::deque<ScheduledTask>> s_normal_priority_queue;
}; };
} // namespace IACore } // namespace IACore

View File

@ -17,8 +17,10 @@
#include <IACore/PCH.hpp> #include <IACore/PCH.hpp>
namespace IACore { namespace IACore
class CLIParser { {
class CLIParser
{
/* /*
* PLEASE READ * PLEASE READ
* *
@ -32,24 +34,29 @@ public:
~CLIParser() = default; ~CLIParser() = default;
public: public:
IA_NODISCARD auto remaining() const -> bool { IA_NODISCARD auto remaining() const -> bool
{
return m_current_arg < m_arg_list.end(); return m_current_arg < m_arg_list.end();
} }
IA_NODISCARD auto peek() const -> StringView { IA_NODISCARD auto peek() const -> StringView
{
if (!remaining()) if (!remaining())
return ""; return "";
return *m_current_arg; return *m_current_arg;
} }
auto next() -> StringView { auto next() -> StringView
{
if (!remaining()) if (!remaining())
return ""; return "";
return *m_current_arg++; return *m_current_arg++;
} }
auto consume(Ref<StringView> expected) -> bool { auto consume(Ref<StringView> expected) -> bool
if (peek() == expected) { {
if (peek() == expected)
{
next(); next();
return true; return true;
} }
@ -59,5 +66,5 @@ public:
private: private:
const Span<const String> m_arg_list; const Span<const String> m_arg_list;
Mut<Span<const String>::const_iterator> m_current_arg; Mut<Span<const String>::const_iterator> m_current_arg;
}; };
} // namespace IACore } // namespace IACore

View File

@ -17,10 +17,17 @@
#include <IACore/PCH.hpp> #include <IACore/PCH.hpp>
namespace IACore { namespace IACore
class DataOps { {
class DataOps
{
public: public:
enum class CompressionType { None, Gzip, Zlib }; enum class CompressionType
{
None,
Gzip,
Zlib
};
public: public:
static auto hash_fnv1a(Ref<String> string) -> u32; static auto hash_fnv1a(Ref<String> string) -> u32;
@ -41,5 +48,5 @@ public:
static auto zstd_inflate(Ref<Span<const u8>> data) -> Result<Vec<u8>>; static auto zstd_inflate(Ref<Span<const u8>> data) -> Result<Vec<u8>>;
static auto zstd_deflate(Ref<Span<const u8>> data) -> Result<Vec<u8>>; static auto zstd_deflate(Ref<Span<const u8>> data) -> Result<Vec<u8>>;
}; };
} // namespace IACore } // namespace IACore

View File

@ -18,19 +18,22 @@
#include <IACore/PCH.hpp> #include <IACore/PCH.hpp>
#if !IA_PLATFORM_WINDOWS #if !IA_PLATFORM_WINDOWS
#include <dlfcn.h> # include <dlfcn.h>
#endif #endif
namespace IACore { namespace IACore
{
class DynamicLib { class DynamicLib
{
public: public:
IA_NODISCARD static auto load(Ref<String> search_path, Ref<String> name) IA_NODISCARD static auto load(Ref<String> search_path, Ref<String> name) -> Result<DynamicLib>
-> Result<DynamicLib> { {
namespace fs = std::filesystem; namespace fs = std::filesystem;
Mut<Path> full_path = fs::path(search_path) / name; Mut<Path> full_path = fs::path(search_path) / name;
if (!full_path.has_extension()) { if (!full_path.has_extension())
{
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
full_path += ".dll"; full_path += ".dll";
#elif IA_PLATFORM_APPLE #elif IA_PLATFORM_APPLE
@ -44,13 +47,15 @@ public:
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
const HMODULE h = LoadLibraryA(full_path.string().c_str()); const HMODULE h = LoadLibraryA(full_path.string().c_str());
if (!h) { if (!h)
{
return fail(get_windows_error()); return fail(get_windows_error());
} }
lib.m_handle = static_cast<void *>(h); lib.m_handle = static_cast<void *>(h);
#else #else
Mut<void *> h = dlopen(full_path.c_str(), RTLD_LAZY | RTLD_LOCAL); Mut<void *> h = dlopen(full_path.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!h) { if (!h)
{
const char *err = dlerror(); const char *err = dlerror();
return fail(err ? err : "Unknown dlopen error"); return fail(err ? err : "Unknown dlopen error");
} }
@ -62,12 +67,15 @@ public:
DynamicLib() = default; DynamicLib() = default;
DynamicLib(ForwardRef<DynamicLib> other) noexcept : m_handle(other.m_handle) { DynamicLib(ForwardRef<DynamicLib> other) noexcept : m_handle(other.m_handle)
{
other.m_handle = nullptr; other.m_handle = nullptr;
} }
auto operator=(ForwardRef<DynamicLib> other) noexcept -> MutRef<DynamicLib> { auto operator=(ForwardRef<DynamicLib> other) noexcept -> MutRef<DynamicLib>
if (this != &other) { {
if (this != &other)
{
unload(); unload();
m_handle = other.m_handle; m_handle = other.m_handle;
other.m_handle = nullptr; other.m_handle = nullptr;
@ -78,25 +86,31 @@ public:
DynamicLib(Ref<DynamicLib>) = delete; DynamicLib(Ref<DynamicLib>) = delete;
auto operator=(Ref<DynamicLib>) -> MutRef<DynamicLib> = delete; auto operator=(Ref<DynamicLib>) -> MutRef<DynamicLib> = delete;
~DynamicLib() { unload(); } ~DynamicLib()
{
unload();
}
IA_NODISCARD auto get_symbol(Ref<String> name) const -> Result<void *> { IA_NODISCARD auto get_symbol(Ref<String> name) const -> Result<void *>
if (!m_handle) { {
if (!m_handle)
{
return fail("Library not loaded"); return fail("Library not loaded");
} }
Mut<void *> sym = nullptr; Mut<void *> sym = nullptr;
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
sym = static_cast<void *>( sym = static_cast<void *>(GetProcAddress(static_cast<HMODULE>(m_handle), name.c_str()));
GetProcAddress(static_cast<HMODULE>(m_handle), name.c_str())); if (!sym)
if (!sym) { {
return fail(get_windows_error()); return fail(get_windows_error());
} }
#else #else
dlerror(); // Clear prev errors dlerror(); // Clear prev errors
sym = dlsym(m_handle, name.c_str()); sym = dlsym(m_handle, name.c_str());
if (const char *err = dlerror()) { if (const char *err = dlerror())
{
return fail(err); return fail(err);
} }
#endif #endif
@ -104,15 +118,17 @@ public:
return sym; return sym;
} }
template <typename FuncT> template<typename FuncT> IA_NODISCARD auto get_function(Ref<String> name) const -> Result<FuncT>
IA_NODISCARD auto get_function(Ref<String> name) const -> Result<FuncT> { {
Mut<void *> sym = nullptr; Mut<void *> sym = nullptr;
sym = AU_TRY(get_symbol(name)); sym = AU_TRY(get_symbol(name));
return reinterpret_cast<FuncT>(sym); return reinterpret_cast<FuncT>(sym);
} }
void unload() { void unload()
if (m_handle) { {
if (m_handle)
{
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
FreeLibrary(static_cast<HMODULE>(m_handle)); FreeLibrary(static_cast<HMODULE>(m_handle));
#else #else
@ -122,30 +138,33 @@ public:
} }
} }
IA_NODISCARD auto is_loaded() const -> bool { return m_handle != nullptr; } IA_NODISCARD auto is_loaded() const -> bool
{
return m_handle != nullptr;
}
private: private:
Mut<void *> m_handle = nullptr; Mut<void *> m_handle = nullptr;
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
static auto get_windows_error() -> String { static auto get_windows_error() -> String
{
const DWORD error_id = ::GetLastError(); const DWORD error_id = ::GetLastError();
if (error_id == 0) { if (error_id == 0)
{
return String(); return String();
} }
Mut<LPSTR> message_buffer = nullptr; Mut<LPSTR> message_buffer = nullptr;
const usize size = FormatMessageA( const usize size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
FORMAT_MESSAGE_IGNORE_INSERTS, error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPSTR>(&message_buffer), 0, nullptr);
nullptr, error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPSTR>(&message_buffer), 0, nullptr);
const String message(message_buffer, size); const String message(message_buffer, size);
LocalFree(message_buffer); LocalFree(message_buffer);
return "Win32 Error: " + message; return "Win32 Error: " + message;
} }
#endif #endif
}; };
} // namespace IACore } // namespace IACore

View File

@ -18,26 +18,29 @@
#include <IACore/PCH.hpp> #include <IACore/PCH.hpp>
#include <cstdlib> #include <cstdlib>
namespace IACore { namespace IACore
{
class Environment { class Environment
{
public: public:
static auto find(Ref<String> name) -> Option<String> { static auto find(Ref<String> name) -> Option<String>
{
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
const u32 buffer_size = const u32 buffer_size = static_cast<u32>(GetEnvironmentVariableA(name.c_str(), nullptr, 0));
static_cast<u32>(GetEnvironmentVariableA(name.c_str(), nullptr, 0));
if (buffer_size == 0) { if (buffer_size == 0)
{
return std::nullopt; return std::nullopt;
} }
Mut<String> result; Mut<String> result;
result.resize(buffer_size); result.resize(buffer_size);
const u32 actual_size = static_cast<u32>( const u32 actual_size = static_cast<u32>(GetEnvironmentVariableA(name.c_str(), result.data(), buffer_size));
GetEnvironmentVariableA(name.c_str(), result.data(), buffer_size));
if (actual_size == 0 || actual_size > buffer_size) { if (actual_size == 0 || actual_size > buffer_size)
{
return std::nullopt; return std::nullopt;
} }
@ -46,54 +49,65 @@ public:
#else #else
const char *val = std::getenv(name.c_str()); const char *val = std::getenv(name.c_str());
if (val == nullptr) { if (val == nullptr)
{
return std::nullopt; return std::nullopt;
} }
return String(val); return String(val);
#endif #endif
} }
static auto get(Ref<String> name, Ref<String> default_value = "") -> String { static auto get(Ref<String> name, Ref<String> default_value = "") -> String
{
return find(name).value_or(default_value); return find(name).value_or(default_value);
} }
static auto set(Ref<String> name, Ref<String> value) -> Result<void> { static auto set(Ref<String> name, Ref<String> value) -> Result<void>
if (name.empty()) { {
if (name.empty())
{
return fail("Environment variable name cannot be empty"); return fail("Environment variable name cannot be empty");
} }
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
if (SetEnvironmentVariableA(name.c_str(), value.c_str()) == 0) { if (SetEnvironmentVariableA(name.c_str(), value.c_str()) == 0)
{
return fail("Failed to set environment variable: {}", name); return fail("Failed to set environment variable: {}", name);
} }
#else #else
if (setenv(name.c_str(), value.c_str(), 1) != 0) { if (setenv(name.c_str(), value.c_str(), 1) != 0)
{
return fail("Failed to set environment variable: {}", name); return fail("Failed to set environment variable: {}", name);
} }
#endif #endif
return {}; return {};
} }
static auto unset(Ref<String> name) -> Result<void> { static auto unset(Ref<String> name) -> Result<void>
if (name.empty()) { {
if (name.empty())
{
return fail("Environment variable name cannot be empty"); return fail("Environment variable name cannot be empty");
} }
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
if (SetEnvironmentVariableA(name.c_str(), nullptr) == 0) { if (SetEnvironmentVariableA(name.c_str(), nullptr) == 0)
{
return fail("Failed to unset environment variable: {}", name); return fail("Failed to unset environment variable: {}", name);
} }
#else #else
if (unsetenv(name.c_str()) != 0) { if (unsetenv(name.c_str()) != 0)
{
return fail("Failed to unset environment variable: {}", name); return fail("Failed to unset environment variable: {}", name);
} }
#endif #endif
return {}; return {};
} }
static auto exists(Ref<String> name) -> bool { static auto exists(Ref<String> name) -> bool
{
return find(name).has_value(); return find(name).has_value();
} }
}; };
} // namespace IACore } // namespace IACore

View File

@ -22,25 +22,29 @@
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
using NativeFileHandle = HANDLE; using NativeFileHandle = HANDLE;
static constexpr NativeFileHandle INVALID_FILE_HANDLE = INVALID_HANDLE_VALUE; static const NativeFileHandle INVALID_FILE_HANDLE = INVALID_HANDLE_VALUE;
#else #else
using NativeFileHandle = int; using NativeFileHandle = int;
static constexpr NativeFileHandle INVALID_FILE_HANDLE = -1; static const NativeFileHandle INVALID_FILE_HANDLE = -1;
#endif #endif
namespace IACore { namespace IACore
{
class FileOps { class FileOps
{
public: public:
class MemoryMappedRegion; class MemoryMappedRegion;
enum class FileAccess : u8 { enum class FileAccess : u8
{
Read, // Read-only Read, // Read-only
Write, // Write-only Write, // Write-only
ReadWrite // Read and Write ReadWrite // Read and Write
}; };
enum class FileMode : u8 { enum class FileMode : u8
{
OpenExisting, // Fails if file doesn't exist OpenExisting, // Fails if file doesn't exist
OpenAlways, // Opens if exists, creates if not OpenAlways, // Opens if exists, creates if not
CreateNew, // Fails if file exists CreateNew, // Fails if file exists
@ -48,10 +52,8 @@ public:
TruncateExisting // Opens existing and clears it TruncateExisting // Opens existing and clears it
}; };
static auto native_open_file(Ref<Path> path, const FileAccess access, static auto native_open_file(Ref<Path> path, const FileAccess access, const FileMode mode,
const FileMode mode, const u32 permissions = 0644) -> Result<NativeFileHandle>;
const u32 permissions = 0644)
-> Result<NativeFileHandle>;
static auto native_close_file(const NativeFileHandle handle) -> void; static auto native_close_file(const NativeFileHandle handle) -> void;
@ -61,36 +63,32 @@ public:
public: public:
static auto unmap_file(const u8 *mapped_ptr) -> void; static auto unmap_file(const u8 *mapped_ptr) -> void;
static auto map_file(Ref<Path> path, MutRef<usize> size) static auto map_file(Ref<Path> path, MutRef<usize> size) -> Result<const u8 *>;
-> Result<const u8 *>;
// @param `is_owner` true to allocate/truncate. false to just open. // @param `is_owner` true to allocate/truncate. false to just open.
static auto map_shared_memory(Ref<String> name, const usize size, static auto map_shared_memory(Ref<String> name, const usize size, const bool is_owner) -> Result<u8 *>;
const bool is_owner) -> Result<u8 *>;
static auto unlink_shared_memory(Ref<String> name) -> void; static auto unlink_shared_memory(Ref<String> name) -> void;
static auto stream_from_file(Ref<Path> path) -> Result<StreamReader>; static auto stream_from_file(Ref<Path> path) -> Result<StreamReader>;
static auto stream_to_file(Ref<Path> path, const bool overwrite = false) static auto stream_to_file(Ref<Path> path, const bool overwrite = false) -> Result<StreamWriter>;
-> Result<StreamWriter>;
static auto read_text_file(Ref<Path> path) -> Result<String>; static auto read_text_file(Ref<Path> path) -> Result<String>;
static auto read_binary_file(Ref<Path> path) -> Result<Vec<u8>>; static auto read_binary_file(Ref<Path> path) -> Result<Vec<u8>>;
static auto write_text_file(Ref<Path> path, Ref<String> contents, static auto write_text_file(Ref<Path> path, Ref<String> contents, const bool overwrite = false) -> Result<usize>;
const bool overwrite = false) -> Result<usize>;
static auto write_binary_file(Ref<Path> path, const Span<const u8> contents, static auto write_binary_file(Ref<Path> path, const Span<const u8> contents, const bool overwrite = false)
const bool overwrite = false) -> Result<usize>; -> Result<usize>;
private: private:
static Mut<HashMap<const u8 *, std::tuple<void *, void *, void *>>> static Mut<HashMap<const u8 *, std::tuple<void *, void *, void *>>> s_mapped_files;
s_mapped_files; };
};
class FileOps::MemoryMappedRegion { class FileOps::MemoryMappedRegion
{
public: public:
MemoryMappedRegion() = default; MemoryMappedRegion() = default;
~MemoryMappedRegion(); ~MemoryMappedRegion();
@ -99,20 +97,27 @@ public:
auto operator=(Ref<MemoryMappedRegion>) -> MemoryMappedRegion & = delete; auto operator=(Ref<MemoryMappedRegion>) -> MemoryMappedRegion & = delete;
MemoryMappedRegion(ForwardRef<MemoryMappedRegion> other) noexcept; MemoryMappedRegion(ForwardRef<MemoryMappedRegion> other) noexcept;
auto operator=(ForwardRef<MemoryMappedRegion> other) noexcept auto operator=(ForwardRef<MemoryMappedRegion> other) noexcept -> MemoryMappedRegion &;
-> MemoryMappedRegion &;
auto map(const NativeFileHandle handle, const u64 offset, const usize size) auto map(const NativeFileHandle handle, const u64 offset, const usize size) -> Result<void>;
-> Result<void>;
auto unmap() -> void; auto unmap() -> void;
auto flush() -> void; auto flush() -> void;
[[nodiscard]] auto get_ptr() const -> u8 * { return m_ptr; } [[nodiscard]] auto get_ptr() const -> u8 *
{
return m_ptr;
}
[[nodiscard]] auto get_size() const -> usize { return m_size; } [[nodiscard]] auto get_size() const -> usize
{
return m_size;
}
[[nodiscard]] auto is_valid() const -> bool { return m_ptr != nullptr; } [[nodiscard]] auto is_valid() const -> bool
{
return m_ptr != nullptr;
}
private: private:
Mut<u8 *> m_ptr = nullptr; Mut<u8 *> m_ptr = nullptr;
@ -121,6 +126,6 @@ private:
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
Mut<HANDLE> m_map_handle = NULL; Mut<HANDLE> m_map_handle = NULL;
#endif #endif
}; };
} // namespace IACore } // namespace IACore

View File

@ -18,8 +18,10 @@
#include <IACore/Http/Common.hpp> #include <IACore/Http/Common.hpp>
#include <IACore/JSON.hpp> #include <IACore/JSON.hpp>
namespace IACore { namespace IACore
class HttpClient : public HttpCommon { {
class HttpClient : public HttpCommon
{
public: public:
static auto create(Ref<String> host) -> Result<Box<HttpClient>>; static auto create(Ref<String> host) -> Result<Box<HttpClient>>;
@ -32,27 +34,25 @@ public:
public: public:
auto raw_get(Ref<String> path, Span<const Header> headers, auto raw_get(Ref<String> path, Span<const Header> headers,
const char *default_content_type = const char *default_content_type = "application/x-www-form-urlencoded") -> Result<String>;
"application/x-www-form-urlencoded") -> Result<String>;
auto raw_post(Ref<String> path, Span<const Header> headers, Ref<String> body, auto raw_post(Ref<String> path, Span<const Header> headers, Ref<String> body,
const char *default_content_type = const char *default_content_type = "application/x-www-form-urlencoded") -> Result<String>;
"application/x-www-form-urlencoded") -> Result<String>;
template <typename ResponseType> template<typename ResponseType> auto json_get(Ref<String> path, Span<const Header> headers) -> Result<ResponseType>;
auto json_get(Ref<String> path, Span<const Header> headers)
-> Result<ResponseType>;
template <typename PayloadType, typename ResponseType> template<typename PayloadType, typename ResponseType>
auto json_post(Ref<String> path, Span<const Header> headers, auto json_post(Ref<String> path, Span<const Header> headers, Ref<PayloadType> body) -> Result<ResponseType>;
Ref<PayloadType> body) -> Result<ResponseType>;
// Certificate verification is enabled by default // Certificate verification is enabled by default
auto enable_certificate_verification() -> void; auto enable_certificate_verification() -> void;
auto disable_certificate_verification() -> void; auto disable_certificate_verification() -> void;
public: public:
auto last_response_code() -> EResponseCode { return m_last_response_code; } auto last_response_code() -> EResponseCode
{
return m_last_response_code;
}
private: private:
Mut<httplib::Client> m_client; Mut<httplib::Client> m_client;
@ -63,33 +63,32 @@ private:
protected: protected:
explicit HttpClient(ForwardRef<httplib::Client> client); explicit HttpClient(ForwardRef<httplib::Client> client);
}; };
template <typename ResponseType> template<typename ResponseType>
auto HttpClient::json_get(Ref<String> path, Span<const Header> headers) auto HttpClient::json_get(Ref<String> path, Span<const Header> headers) -> Result<ResponseType>
-> Result<ResponseType> { {
const String raw_response = const String raw_response = AU_TRY(raw_get(path, headers, "application/json"));
AU_TRY(raw_get(path, headers, "application/json"));
if (last_response_code() != EResponseCode::OK) { if (last_response_code() != EResponseCode::OK)
return fail("Server responded with code {}", {
static_cast<i32>(last_response_code())); return fail("Server responded with code {}", static_cast<i32>(last_response_code()));
} }
return Json::parse_to_struct<ResponseType>(raw_response); return Json::parse_to_struct<ResponseType>(raw_response);
} }
template <typename PayloadType, typename ResponseType> template<typename PayloadType, typename ResponseType>
auto HttpClient::json_post(Ref<String> path, Span<const Header> headers, auto HttpClient::json_post(Ref<String> path, Span<const Header> headers, Ref<PayloadType> body)
Ref<PayloadType> body) -> Result<ResponseType> { -> Result<ResponseType>
{
const String encoded_body = AU_TRY(Json::encode_struct(body)); const String encoded_body = AU_TRY(Json::encode_struct(body));
const String raw_response = const String raw_response = AU_TRY(raw_post(path, headers, encoded_body, "application/json"));
AU_TRY(raw_post(path, headers, encoded_body, "application/json"));
if (last_response_code() != EResponseCode::OK) { if (last_response_code() != EResponseCode::OK)
return fail("Server responded with code {}", {
static_cast<i32>(last_response_code())); return fail("Server responded with code {}", static_cast<i32>(last_response_code()));
} }
return Json::parse_to_struct<ResponseType>(raw_response); return Json::parse_to_struct<ResponseType>(raw_response);
} }
} // namespace IACore } // namespace IACore

View File

@ -19,10 +19,13 @@
#include <httplib.h> #include <httplib.h>
namespace IACore { namespace IACore
class HttpCommon { {
class HttpCommon
{
public: public:
enum class EHeaderType { enum class EHeaderType
{
ACCEPT, ACCEPT,
ACCEPT_CHARSET, ACCEPT_CHARSET,
ACCEPT_ENCODING, ACCEPT_ENCODING,
@ -51,7 +54,8 @@ public:
WARNING WARNING
}; };
enum class EResponseCode : i32 { enum class EResponseCode : i32
{
// 1xx Informational // 1xx Informational
CONTINUE = 100, CONTINUE = 100,
SWITCHING_PROTOCOLS = 101, SWITCHING_PROTOCOLS = 101,
@ -132,24 +136,22 @@ public:
static auto header_type_to_string(const EHeaderType type) -> String; static auto header_type_to_string(const EHeaderType type) -> String;
static inline auto create_header(const EHeaderType key, Ref<String> value) static inline auto create_header(const EHeaderType key, Ref<String> value) -> Header;
-> Header; static inline auto create_header(Ref<String> key, Ref<String> value) -> Header;
static inline auto create_header(Ref<String> key, Ref<String> value)
-> Header;
static auto is_success_response_code(const EResponseCode code) -> bool; static auto is_success_response_code(const EResponseCode code) -> bool;
protected: protected:
HttpCommon() = default; HttpCommon() = default;
}; };
auto HttpCommon::create_header(const EHeaderType key, Ref<String> value) auto HttpCommon::create_header(const EHeaderType key, Ref<String> value) -> HttpCommon::Header
-> HttpCommon::Header { {
return Header{header_type_to_string(key), value}; return Header{header_type_to_string(key), value};
} }
auto HttpCommon::create_header(Ref<String> key, Ref<String> value) auto HttpCommon::create_header(Ref<String> key, Ref<String> value) -> HttpCommon::Header
-> HttpCommon::Header { {
return Header{key, value}; return Header{key, value};
} }
} // namespace IACore } // namespace IACore

View File

@ -19,10 +19,13 @@
#include <IACore/JSON.hpp> #include <IACore/JSON.hpp>
#include <functional> #include <functional>
namespace IACore { namespace IACore
class HttpServer : public HttpCommon { {
class HttpServer : public HttpCommon
{
public: public:
struct Request { struct Request
{
Mut<String> path; Mut<String> path;
Mut<String> method; Mut<String> method;
Mut<String> body; Mut<String> body;
@ -39,7 +42,8 @@ public:
[[nodiscard]] auto has_path_param(Ref<String> key) const -> bool; [[nodiscard]] auto has_path_param(Ref<String> key) const -> bool;
}; };
struct Response { struct Response
{
Mut<EResponseCode> code = EResponseCode::OK; Mut<EResponseCode> code = EResponseCode::OK;
Mut<String> body; Mut<String> body;
Mut<HashMap<String, String>> headers; Mut<HashMap<String, String>> headers;
@ -72,15 +76,11 @@ public:
void del(Ref<String> pattern, const Handler handler); void del(Ref<String> pattern, const Handler handler);
void options(Ref<String> pattern, const Handler handler); void options(Ref<String> pattern, const Handler handler);
template <typename ResponseType> template<typename ResponseType>
void void json_get(Ref<String> pattern, const std::function<Result<ResponseType(Ref<Request>)>> handler);
json_get(Ref<String> pattern,
const std::function<Result<ResponseType(Ref<Request>)>> handler);
template <typename PayloadType, typename ResponseType> template<typename PayloadType, typename ResponseType>
void json_post( void json_post(Ref<String> pattern, const std::function<Result<ResponseType(Ref<PayloadType>)>> handler);
Ref<String> pattern,
const std::function<Result<ResponseType(Ref<PayloadType>)>> handler);
protected: protected:
HttpServer(); HttpServer();
@ -88,24 +88,24 @@ protected:
private: private:
Mut<httplib::Server> m_server; Mut<httplib::Server> m_server;
void register_handler(Ref<String> method, Ref<String> pattern, void register_handler(Ref<String> method, Ref<String> pattern, const Handler handler);
const Handler handler); };
};
template <typename ResponseType> template<typename ResponseType>
void HttpServer::json_get( void HttpServer::json_get(Ref<String> pattern, const std::function<Result<ResponseType(Ref<Request>)>> handler)
Ref<String> pattern, {
const std::function<Result<ResponseType(Ref<Request>)>> handler) {
get(pattern, [handler](Ref<Request> req, MutRef<Response> res) { get(pattern, [handler](Ref<Request> req, MutRef<Response> res) {
const Result<ResponseType> result = handler(req); const Result<ResponseType> result = handler(req);
if (!result) { if (!result)
{
res.set_status(EResponseCode::INTERNAL_SERVER_ERROR); res.set_status(EResponseCode::INTERNAL_SERVER_ERROR);
res.set_content(result.error(), "text/plain"); res.set_content(result.error(), "text/plain");
return; return;
} }
const Result<String> json_res = Json::encode_struct(*result); const Result<String> json_res = Json::encode_struct(*result);
if (!json_res) { if (!json_res)
{
res.set_status(EResponseCode::INTERNAL_SERVER_ERROR); res.set_status(EResponseCode::INTERNAL_SERVER_ERROR);
res.set_content("Failed to encode JSON response", "text/plain"); res.set_content("Failed to encode JSON response", "text/plain");
return; return;
@ -114,30 +114,31 @@ void HttpServer::json_get(
res.set_status(EResponseCode::OK); res.set_status(EResponseCode::OK);
res.set_content(*json_res, "application/json"); res.set_content(*json_res, "application/json");
}); });
} }
template <typename PayloadType, typename ResponseType> template<typename PayloadType, typename ResponseType>
void HttpServer::json_post( void HttpServer::json_post(Ref<String> pattern, const std::function<Result<ResponseType(Ref<PayloadType>)>> handler)
Ref<String> pattern, {
const std::function<Result<ResponseType(Ref<PayloadType>)>> handler) {
post(pattern, [handler](Ref<Request> req, MutRef<Response> res) { post(pattern, [handler](Ref<Request> req, MutRef<Response> res) {
const Result<PayloadType> payload = const Result<PayloadType> payload = Json::parse_to_struct<PayloadType>(req.body);
Json::parse_to_struct<PayloadType>(req.body); if (!payload)
if (!payload) { {
res.set_status(EResponseCode::BAD_REQUEST); res.set_status(EResponseCode::BAD_REQUEST);
res.set_content("Invalid JSON Payload", "text/plain"); res.set_content("Invalid JSON Payload", "text/plain");
return; return;
} }
const Result<ResponseType> result = handler(*payload); const Result<ResponseType> result = handler(*payload);
if (!result) { if (!result)
{
res.set_status(EResponseCode::INTERNAL_SERVER_ERROR); res.set_status(EResponseCode::INTERNAL_SERVER_ERROR);
res.set_content(result.error(), "text/plain"); res.set_content(result.error(), "text/plain");
return; return;
} }
const Result<String> json_res = Json::encode_struct(*result); const Result<String> json_res = Json::encode_struct(*result);
if (!json_res) { if (!json_res)
{
res.set_status(EResponseCode::INTERNAL_SERVER_ERROR); res.set_status(EResponseCode::INTERNAL_SERVER_ERROR);
res.set_content("Failed to encode JSON response", "text/plain"); res.set_content("Failed to encode JSON response", "text/plain");
return; return;
@ -146,6 +147,6 @@ void HttpServer::json_post(
res.set_status(EResponseCode::OK); res.set_status(EResponseCode::OK);
res.set_content(*json_res, "application/json"); res.set_content(*json_res, "application/json");
}); });
} }
} // namespace IACore } // namespace IACore

View File

@ -19,48 +19,53 @@
#include <IACore/PCH.hpp> #include <IACore/PCH.hpp>
#define IACORE_MAIN() \ #define IACORE_MAIN() \
auto _app_entry(IACore::Ref<IACore::Vec<IACore::String>> args) \ auto _app_entry(IACore::Ref<IACore::Vec<IACore::String>> args) -> IACore::Result<IACore::i32>; \
-> IACore::Result<IACore::i32>; \ auto main(int argc, Mut<char *> argv[]) -> int \
auto main(int argc, Mut<char *> argv[]) -> int { \ { \
IACore::Mut<IACore::i32> exit_code = 0; \ IACore::Mut<IACore::i32> exit_code = 0; \
IACore::initialize(); \ IACore::initialize(); \
IACore::Mut<IACore::Vec<IACore::String>> args; \ IACore::Mut<IACore::Vec<IACore::String>> args; \
args.reserve(static_cast<IACore::usize>(argc)); \ args.reserve(static_cast<IACore::usize>(argc)); \
for (IACore::Mut<IACore::i32> i = 0; i < argc; ++i) { \ for (IACore::Mut<IACore::i32> i = 0; i < argc; ++i) \
{ \
args.push_back(argv[i]); \ args.push_back(argv[i]); \
} \ } \
IACore::Result<IACore::i32> result = _app_entry(args); \ IACore::Result<IACore::i32> result = _app_entry(args); \
if (!result) { \ if (!result) \
IACore::Logger::error("Application exited with an error: '{}'.", \ { \
result.error()); \ IACore::Logger::error("Application exited with an error: '{}'.", result.error()); \
exit_code = -20; \ exit_code = -20; \
} else { \ } \
else \
{ \
exit_code = *result; \ exit_code = *result; \
if (exit_code == 0) { \ if (exit_code == 0) \
{ \
IACore::Logger::info("Application exited successfully."); \ IACore::Logger::info("Application exited successfully."); \
} else { \ } \
IACore::Logger::error("Application exited with error code: {}.", \ else \
exit_code); \ { \
IACore::Logger::error("Application exited with error code: {}.", exit_code); \
} \ } \
} \ } \
IACore::terminate(); \ IACore::terminate(); \
return exit_code; \ return exit_code; \
} \ } \
auto _app_entry(IACore::Ref<IACore::Vec<IACore::String>> args) \ auto _app_entry(IACore::Ref<IACore::Vec<IACore::String>> args) -> IACore::Result<IACore::i32>
-> IACore::Result<IACore::i32>
namespace IACore { namespace IACore
// Must be called from main thread {
// Safe to call multiple times but, given every initialize call is paired with a // Must be called from main thread
// corresponding terminate call // Safe to call multiple times but, given every initialize call is paired with a
auto initialize() -> void; // corresponding terminate call
auto initialize() -> void;
// Must be called from same thread as initialize // Must be called from same thread as initialize
// Safe to call multiple times but, given every initialize call is paired with a // Safe to call multiple times but, given every initialize call is paired with a
// corresponding terminate call // corresponding terminate call
auto terminate() -> void; auto terminate() -> void;
auto is_initialized() -> bool; auto is_initialized() -> bool;
auto is_main_thread() -> bool; auto is_main_thread() -> bool;
} // namespace IACore } // namespace IACore

View File

@ -27,13 +27,10 @@
#define IAT_CHECK(v) __iat_micro_test(_test((v), #v)) #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_NOT(v) __iat_micro_test(_test_not((v), "NOT " #v))
#define IAT_CHECK_EQ(lhs, rhs) \ #define IAT_CHECK_EQ(lhs, rhs) __iat_micro_test(_test_eq((lhs), (rhs), #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_NEQ(lhs, rhs) \
__iat_micro_test(_test_neq((lhs), (rhs), #lhs " != " #rhs))
#define IAT_CHECK_APPROX(lhs, rhs) \ #define IAT_CHECK_APPROX(lhs, rhs) __iat_micro_test(_test_approx((lhs), (rhs), #lhs " ~= " #rhs))
__iat_micro_test(_test_approx((lhs), (rhs), #lhs " ~= " #rhs))
#define IAT_UNIT(func) _test_unit([this]() { return this->func(); }, #func) #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_NAMED_UNIT(n, func) _test_unit([this]() { return this->func(); }, n)
@ -41,147 +38,171 @@
#define IAT_BLOCK(name) class name : public IACore::Test::Block #define IAT_BLOCK(name) class name : public IACore::Test::Block
#define IAT_BEGIN_BLOCK(_group, _name) \ #define IAT_BEGIN_BLOCK(_group, _name) \
class _group##_##_name : public IACore::Test::Block { \ class _group##_##_name : public IACore::Test::Block \
public: \ { \
[[nodiscard]] auto get_name() const -> const char * override { \ public: \
[[nodiscard]] auto get_name() const -> const char * override \
{ \
return #_group "::" #_name; \ return #_group "::" #_name; \
} \ } \
\ \
private: private:
#define IAT_END_BLOCK() \ #define IAT_END_BLOCK() \
} \ } \
; ;
#define IAT_BEGIN_TEST_LIST() \ #define IAT_BEGIN_TEST_LIST() \
public: \ public: \
auto declare_tests() -> void override { auto declare_tests() -> void override \
{
#define IAT_ADD_TEST(name) IAT_UNIT(name) #define IAT_ADD_TEST(name) IAT_UNIT(name)
#define IAT_END_TEST_LIST() \ #define IAT_END_TEST_LIST() \
} \ } \
\ \
private: private:
namespace IACore::Test { namespace IACore::Test
// ------------------------------------------------------------------------- {
// String Conversion Helpers // -------------------------------------------------------------------------
// ------------------------------------------------------------------------- // String Conversion Helpers
template <typename T> auto to_string(Ref<T> value) -> String { // -------------------------------------------------------------------------
if constexpr (std::is_arithmetic_v<T>) { template<typename T> auto to_string(Ref<T> value) -> String
{
if constexpr (std::is_arithmetic_v<T>)
{
return std::to_string(value); return std::to_string(value);
} else if constexpr (std::is_same_v<T, String> || }
std::is_same_v<T, const char *> || else if constexpr (std::is_same_v<T, String> || std::is_same_v<T, const char *> || std::is_same_v<T, char *>)
std::is_same_v<T, char *>) { {
return String("\"") + String(value) + "\""; return String("\"") + String(value) + "\"";
} else { }
else
{
return "{Object}"; return "{Object}";
} }
} }
template <typename T> auto to_string(T *value) -> String { template<typename T> auto to_string(T *value) -> String
if (value == nullptr) { {
if (value == nullptr)
{
return "nullptr"; return "nullptr";
} }
return std::format("ptr({})", static_cast<const void *>(value)); return std::format("ptr({})", static_cast<const void *>(value));
} }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Types // Types
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
using TestFunctor = std::function<bool()>; using TestFunctor = std::function<bool()>;
struct TestUnit { struct TestUnit
{
Mut<String> name; Mut<String> name;
Mut<TestFunctor> functor; Mut<TestFunctor> functor;
}; };
class Block { class Block
{
public: public:
virtual ~Block() = default; virtual ~Block() = default;
[[nodiscard]] virtual auto get_name() const -> const char * = 0; [[nodiscard]] virtual auto get_name() const -> const char * = 0;
virtual auto declare_tests() -> void = 0; virtual auto declare_tests() -> void = 0;
auto units() -> MutRef<Vec<TestUnit>> { return m_units; } auto units() -> MutRef<Vec<TestUnit>>
{
return m_units;
}
protected: protected:
template <typename T1, typename T2> template<typename T1, typename T2> auto _test_eq(Ref<T1> lhs, Ref<T2> rhs, const char *description) -> bool
auto _test_eq(Ref<T1> lhs, Ref<T2> rhs, const char *description) -> bool { {
if (lhs != rhs) { if (lhs != rhs)
{
print_fail(description, to_string(lhs), to_string(rhs)); print_fail(description, to_string(lhs), to_string(rhs));
return false; return false;
} }
return true; return true;
} }
template <typename T1, typename T2> template<typename T1, typename T2> auto _test_neq(Ref<T1> lhs, Ref<T2> rhs, const char *description) -> bool
auto _test_neq(Ref<T1> lhs, Ref<T2> rhs, const char *description) -> bool { {
if (lhs == rhs) { if (lhs == rhs)
{
print_fail(description, to_string(lhs), "NOT " + to_string(rhs)); print_fail(description, to_string(lhs), "NOT " + to_string(rhs));
return false; return false;
} }
return true; return true;
} }
template <typename T> template<typename T> auto _test_approx(const T lhs, const T rhs, const char *description) -> bool
auto _test_approx(const T lhs, const T rhs, const char *description) -> bool { {
static_assert(std::is_floating_point_v<T>, static_assert(std::is_floating_point_v<T>, "Approx only works for floats/doubles");
"Approx only works for floats/doubles");
const T diff = std::abs(lhs - rhs); const T diff = std::abs(lhs - rhs);
if (diff > static_cast<T>(0.0001)) { if (diff > static_cast<T>(0.0001))
{
print_fail(description, to_string(lhs), to_string(rhs)); print_fail(description, to_string(lhs), to_string(rhs));
return false; return false;
} }
return true; return true;
} }
auto _test(const bool value, const char *description) -> bool { auto _test(const bool value, const char *description) -> bool
if (!value) { {
std::cout << console::BLUE << " " << description << "... " if (!value)
<< console::RED << "FAILED" << console::RESET << "\n"; {
std::cout << console::BLUE << " " << description << "... " << console::RED << "FAILED" << console::RESET
<< "\n";
return false; return false;
} }
return true; return true;
} }
auto _test_not(const bool value, const char *description) -> bool { auto _test_not(const bool value, const char *description) -> bool
if (value) { {
std::cout << console::BLUE << " " << description << "... " if (value)
<< console::RED << "FAILED" << console::RESET << "\n"; {
std::cout << console::BLUE << " " << description << "... " << console::RED << "FAILED" << console::RESET
<< "\n";
return false; return false;
} }
return true; return true;
} }
auto _test_unit(Mut<TestFunctor> functor, const char *name) -> void { auto _test_unit(Mut<TestFunctor> functor, const char *name) -> void
{
m_units.push_back({name, std::move(functor)}); m_units.push_back({name, std::move(functor)});
} }
private: private:
auto print_fail(const char *desc, Ref<String> v1, Ref<String> v2) -> void { auto print_fail(const char *desc, Ref<String> v1, Ref<String> v2) -> void
std::cout << console::BLUE << " " << desc << "... " << console::RED {
<< "FAILED" << console::RESET << "\n"; std::cout << console::BLUE << " " << desc << "... " << console::RED << "FAILED" << console::RESET << "\n";
std::cout << console::RED << " Expected: " << v2 << console::RESET std::cout << console::RED << " Expected: " << v2 << console::RESET << "\n";
<< "\n"; std::cout << console::RED << " Actual: " << v1 << console::RESET << "\n";
std::cout << console::RED << " Actual: " << v1 << console::RESET
<< "\n";
} }
Mut<Vec<TestUnit>> m_units; Mut<Vec<TestUnit>> m_units;
}; };
template <typename T> template<typename T>
concept ValidBlockClass = std::derived_from<T, Block>; concept ValidBlockClass = std::derived_from<T, Block>;
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Runner // Runner
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
template <bool StopOnFail = false, bool IsVerbose = false> class Runner { template<bool StopOnFail = false, bool IsVerbose = false> class Runner
{
public: public:
Runner() = default; Runner() = default;
~Runner() { summarize(); } ~Runner()
{
summarize();
}
template <typename BlockClass> template<typename BlockClass>
requires ValidBlockClass<BlockClass> requires ValidBlockClass<BlockClass>
auto test_block() -> void; auto test_block() -> void;
@ -191,99 +212,101 @@ private:
Mut<usize> m_test_count{0}; Mut<usize> m_test_count{0};
Mut<usize> m_fail_count{0}; Mut<usize> m_fail_count{0};
Mut<usize> m_block_count{0}; Mut<usize> m_block_count{0};
}; };
template <bool StopOnFail, bool IsVerbose> template<bool StopOnFail, bool IsVerbose>
template <typename BlockClass> template<typename BlockClass>
requires ValidBlockClass<BlockClass> requires ValidBlockClass<BlockClass>
auto Runner<StopOnFail, IsVerbose>::test_block() -> void { auto Runner<StopOnFail, IsVerbose>::test_block() -> void
{
m_block_count++; m_block_count++;
Mut<BlockClass> b; Mut<BlockClass> b;
b.declare_tests(); b.declare_tests();
std::cout << console::MAGENTA << "Testing [" << b.get_name() << "]..." std::cout << console::MAGENTA << "Testing [" << b.get_name() << "]..." << console::RESET << "\n";
<< console::RESET << "\n";
for (MutRef<TestUnit> v : b.units()) { for (MutRef<TestUnit> v : b.units())
{
m_test_count++; m_test_count++;
if constexpr (IsVerbose) { if constexpr (IsVerbose)
std::cout << console::YELLOW << " Testing " << v.name << "...\n" {
<< console::RESET; std::cout << console::YELLOW << " Testing " << v.name << "...\n" << console::RESET;
} }
const bool result = v.functor(); const bool result = v.functor();
if (!result) { if (!result)
{
m_fail_count++; m_fail_count++;
if constexpr (StopOnFail) { if constexpr (StopOnFail)
{
summarize(); summarize();
std::exit(-1); std::exit(-1);
} }
} }
} }
std::cout << "\n"; std::cout << "\n";
} }
template <bool StopOnFail, bool IsVerbose> template<bool StopOnFail, bool IsVerbose> auto Runner<StopOnFail, IsVerbose>::summarize() -> void
auto Runner<StopOnFail, IsVerbose>::summarize() -> void { {
std::cout << console::GREEN std::cout << console::GREEN
<< "\n-----------------------------------\n\t " << "\n-----------------------------------\n\t "
"SUMMARY\n-----------------------------------\n"; "SUMMARY\n-----------------------------------\n";
if (m_fail_count == 0) { if (m_fail_count == 0)
{
std::cout << "\n\tALL TESTS PASSED!\n\n"; std::cout << "\n\tALL TESTS PASSED!\n\n";
} else { }
const f64 success_rate = else
(100.0 * static_cast<f64>(m_test_count - m_fail_count) / {
static_cast<f64>(m_test_count)); const f64 success_rate = (100.0 * static_cast<f64>(m_test_count - m_fail_count) / static_cast<f64>(m_test_count));
std::cout << console::RED << m_fail_count << " OF " << m_test_count std::cout << console::RED << m_fail_count << " OF " << m_test_count << " TESTS FAILED\n"
<< " TESTS FAILED\n" << console::YELLOW << std::format("Success Rate: {:.2f}%\n", success_rate);
<< console::YELLOW
<< std::format("Success Rate: {:.2f}%\n", success_rate);
} }
std::cout << console::MAGENTA << "Ran " << m_test_count << " test(s) across " std::cout << console::MAGENTA << "Ran " << m_test_count << " test(s) across " << m_block_count << " block(s)\n"
<< m_block_count << " block(s)\n" << console::GREEN << "-----------------------------------" << console::RESET << "\n";
<< console::GREEN << "-----------------------------------" }
<< console::RESET << "\n";
}
using DefaultRunner = Runner<false, true>; using DefaultRunner = Runner<false, true>;
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Registry // Registry
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
class TestRegistry { class TestRegistry
{
public: public:
using TestEntry = std::function<void(MutRef<DefaultRunner>)>; using TestEntry = std::function<void(MutRef<DefaultRunner>)>;
static auto get_entries() -> MutRef<Vec<TestEntry>> { static auto get_entries() -> MutRef<Vec<TestEntry>>
{
static Mut<Vec<TestEntry>> entries; static Mut<Vec<TestEntry>> entries;
return entries; return entries;
} }
static auto run_all() -> i32 { static auto run_all() -> i32
{
Mut<DefaultRunner> r; Mut<DefaultRunner> r;
MutRef<Vec<TestEntry>> entries = get_entries(); MutRef<Vec<TestEntry>> entries = get_entries();
std::cout << console::CYAN << "[IATest] Discovered " << entries.size() std::cout << console::CYAN << "[IATest] Discovered " << entries.size() << " Test Blocks\n\n" << console::RESET;
<< " Test Blocks\n\n"
<< console::RESET;
for (MutRef<TestEntry> entry : entries) { for (MutRef<TestEntry> entry : entries)
{
entry(r); entry(r);
} }
return 0; return 0;
} }
}; };
template <typename BlockType> struct AutoRegister { template<typename BlockType> struct AutoRegister
AutoRegister() { {
TestRegistry::get_entries().push_back( AutoRegister()
[](MutRef<DefaultRunner> r) { r.test_block<BlockType>(); }); {
TestRegistry::get_entries().push_back([](MutRef<DefaultRunner> r) { r.test_block<BlockType>(); });
} }
}; };
} // namespace IACore::Test } // namespace IACore::Test
#define IAT_REGISTER_ENTRY(Group, Name) \ #define IAT_REGISTER_ENTRY(Group, Name) static IACore::Test::AutoRegister<Group##_##Name> _iat_reg_##Group##_##Name;
static IACore::Test::AutoRegister<Group##_##Name> _iat_reg_##Group##_##Name;

View File

@ -19,14 +19,17 @@
#include <IACore/ProcessOps.hpp> #include <IACore/ProcessOps.hpp>
#include <IACore/SocketOps.hpp> #include <IACore/SocketOps.hpp>
namespace IACore { namespace IACore
using IpcPacketHeader = RingBufferView::PacketHeader; {
using IpcPacketHeader = RingBufferView::PacketHeader;
struct alignas(64) IpcSharedMemoryLayout { struct alignas(64) IpcSharedMemoryLayout
{
// ========================================================= // =========================================================
// METADATA & HANDSHAKE // METADATA & HANDSHAKE
// ========================================================= // =========================================================
struct Header { struct Header
{
Mut<u32> magic; // 0x49414950 ("IAIP") Mut<u32> magic; // 0x49414950 ("IAIP")
Mut<u32> version; // 1 Mut<u32> version; // 1
Mut<u64> total_size; // Total size of SHM block Mut<u64> total_size; // Total size of SHM block
@ -58,16 +61,17 @@ struct alignas(64) IpcSharedMemoryLayout {
// Pad to ensure the actual Data Buffer starts on a fresh cache line // Pad to ensure the actual Data Buffer starts on a fresh cache line
const Array<u8, 64 - (sizeof(u64) * 4)> _pad1; const Array<u8, 64 - (sizeof(u64) * 4)> _pad1;
static constexpr auto get_header_size() -> usize { static constexpr auto get_header_size() -> usize
{
return sizeof(IpcSharedMemoryLayout); return sizeof(IpcSharedMemoryLayout);
} }
}; };
// Check padding logic is gucci // Check padding logic is gucci
static_assert(sizeof(IpcSharedMemoryLayout) % 64 == 0, static_assert(sizeof(IpcSharedMemoryLayout) % 64 == 0, "IPC Layout is not cache-line aligned!");
"IPC Layout is not cache-line aligned!");
class IpcNode { class IpcNode
{
public: public:
virtual ~IpcNode(); virtual ~IpcNode();
@ -78,13 +82,11 @@ public:
auto update() -> void; auto update() -> void;
auto send_signal(const u8 signal) -> void; auto send_signal(const u8 signal) -> void;
auto send_packet(const u16 packet_id, const Span<const u8> payload) auto send_packet(const u16 packet_id, const Span<const u8> payload) -> Result<void>;
-> Result<void>;
protected: protected:
virtual auto on_signal(const u8 signal) -> void = 0; virtual auto on_signal(const u8 signal) -> void = 0;
virtual auto on_packet(const u16 packet_id, const Span<const u8> payload) virtual auto on_packet(const u16 packet_id, const Span<const u8> payload) -> void = 0;
-> void = 0;
private: private:
Mut<String> m_shm_name; Mut<String> m_shm_name;
@ -94,10 +96,12 @@ private:
Mut<RingBufferView> m_moni; // Manager Out, Node In Mut<RingBufferView> m_moni; // Manager Out, Node In
Mut<RingBufferView> m_mino; // Manager In, Node Out Mut<RingBufferView> m_mino; // Manager In, Node Out
}; };
class IpcManager { class IpcManager
struct NodeSession { {
struct NodeSession
{
Mut<std::chrono::system_clock::time_point> creation_time{}; Mut<std::chrono::system_clock::time_point> creation_time{};
Mut<Box<ProcessHandle>> node_process; Mut<Box<ProcessHandle>> node_process;
@ -109,16 +113,13 @@ class IpcManager {
Mut<SocketHandle> listener_socket{INVALID_SOCKET}; Mut<SocketHandle> listener_socket{INVALID_SOCKET};
Mut<SocketHandle> data_socket{INVALID_SOCKET}; Mut<SocketHandle> data_socket{INVALID_SOCKET};
Mut<RingBufferView> moni = Mut<RingBufferView> moni = RingBufferView::default_instance(); // Manager Out, Node In
RingBufferView::default_instance(); // Manager Out, Node In Mut<RingBufferView> mino = RingBufferView::default_instance(); // Manager In, Node Out
Mut<RingBufferView> mino =
RingBufferView::default_instance(); // Manager In, Node Out
Mut<bool> is_ready{false}; Mut<bool> is_ready{false};
auto send_signal(const u8 signal) -> void; auto send_signal(const u8 signal) -> void;
auto send_packet(const u16 packet_id, const Span<const u8> payload) auto send_packet(const u16 packet_id, const Span<const u8> payload) -> Result<void>;
-> Result<void>;
}; };
public: public:
@ -129,9 +130,7 @@ public:
auto update() -> void; auto update() -> void;
auto auto spawn_node(Ref<Path> executable_path, const u32 shared_memory_size = DEFAULT_NODE_SHARED_MEMORY_SIZE)
spawn_node(Ref<Path> executable_path,
const u32 shared_memory_size = DEFAULT_NODE_SHARED_MEMORY_SIZE)
-> Result<NativeProcessID>; -> Result<NativeProcessID>;
auto wait_till_node_is_online(const NativeProcessID node) -> bool; auto wait_till_node_is_online(const NativeProcessID node) -> bool;
@ -139,14 +138,11 @@ public:
auto shutdown_node(const NativeProcessID node) -> void; auto shutdown_node(const NativeProcessID node) -> void;
auto send_signal(const NativeProcessID node, const u8 signal) -> void; auto send_signal(const NativeProcessID node, const u8 signal) -> void;
auto send_packet(const NativeProcessID node, const u16 packet_id, auto send_packet(const NativeProcessID node, const u16 packet_id, const Span<const u8> payload) -> Result<void>;
const Span<const u8> payload) -> Result<void>;
protected: protected:
virtual auto on_signal(const NativeProcessID node, const u8 signal) virtual auto on_signal(const NativeProcessID node, const u8 signal) -> void = 0;
-> void = 0; virtual auto on_packet(const NativeProcessID node, const u16 packet_id, const Span<const u8> payload) -> void = 0;
virtual auto on_packet(const NativeProcessID node, const u16 packet_id,
const Span<const u8> payload) -> void = 0;
private: private:
Mut<Vec<u8>> m_receive_buffer; Mut<Vec<u8>> m_receive_buffer;
@ -156,5 +152,5 @@ private:
protected: protected:
IpcManager(); IpcManager();
}; };
} // namespace IACore } // namespace IACore

View File

@ -21,35 +21,38 @@
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <simdjson.h> #include <simdjson.h>
namespace IACore { namespace IACore
class JsonDocument { {
class JsonDocument
{
public: public:
JsonDocument(ForwardRef<JsonDocument>) noexcept = default; JsonDocument(ForwardRef<JsonDocument>) noexcept = default;
auto operator=(ForwardRef<JsonDocument>) noexcept auto operator=(ForwardRef<JsonDocument>) noexcept -> MutRef<JsonDocument> = default;
-> MutRef<JsonDocument> = default;
JsonDocument(Ref<JsonDocument>) = delete; JsonDocument(Ref<JsonDocument>) = delete;
auto operator=(Ref<JsonDocument>) -> MutRef<JsonDocument> = delete; auto operator=(Ref<JsonDocument>) -> MutRef<JsonDocument> = delete;
[[nodiscard]] [[nodiscard]]
auto root() const noexcept -> simdjson::dom::element { auto root() const noexcept -> simdjson::dom::element
{
return m_root; return m_root;
} }
private: private:
friend class Json; friend class Json;
JsonDocument(Mut<Box<simdjson::dom::parser>> p, Mut<simdjson::dom::element> r) JsonDocument(Mut<Box<simdjson::dom::parser>> p, Mut<simdjson::dom::element> r) : m_parser(std::move(p)), m_root(r)
: m_parser(std::move(p)), m_root(r) {} {
}
Mut<Box<simdjson::dom::parser>> m_parser; Mut<Box<simdjson::dom::parser>> m_parser;
Mut<simdjson::dom::element> m_root; Mut<simdjson::dom::element> m_root;
}; };
class Json { class Json
{
private: private:
static constexpr const glz::opts GLAZE_OPTS = static constexpr const glz::opts GLAZE_OPTS = glz::opts{.error_on_unknown_keys = false};
glz::opts{.error_on_unknown_keys = false};
public: public:
static auto parse(Ref<String> json_str) -> Result<nlohmann::json>; static auto parse(Ref<String> json_str) -> Result<nlohmann::json>;
@ -57,63 +60,65 @@ public:
static auto parse_read_only(Ref<String> json_str) -> Result<JsonDocument>; static auto parse_read_only(Ref<String> json_str) -> Result<JsonDocument>;
template <typename T> template<typename T> static auto parse_to_struct(Ref<String> json_str) -> Result<T>;
static auto parse_to_struct(Ref<String> json_str) -> Result<T>;
template <typename T> template<typename T> static auto encode_struct(Ref<T> data) -> Result<String>;
static auto encode_struct(Ref<T> data) -> Result<String>; };
};
inline auto Json::parse(Ref<String> json_str) -> Result<nlohmann::json> { inline auto Json::parse(Ref<String> json_str) -> Result<nlohmann::json>
const nlohmann::json res = {
nlohmann::json::parse(json_str, nullptr, false, true); const nlohmann::json res = nlohmann::json::parse(json_str, nullptr, false, true);
if (res.is_discarded()) { if (res.is_discarded())
{
return fail("Failed to parse JSON (Invalid Syntax)"); return fail("Failed to parse JSON (Invalid Syntax)");
} }
return res; return res;
} }
inline auto Json::parse_read_only(Ref<String> json_str) inline auto Json::parse_read_only(Ref<String> json_str) -> Result<JsonDocument>
-> Result<JsonDocument> { {
Mut<Box<simdjson::dom::parser>> parser = make_box<simdjson::dom::parser>(); Mut<Box<simdjson::dom::parser>> parser = make_box<simdjson::dom::parser>();
Mut<simdjson::dom::element> root; Mut<simdjson::dom::element> root;
const simdjson::error_code error = parser->parse(json_str).get(root); const simdjson::error_code error = parser->parse(json_str).get(root);
if (error) { if (error)
{
return fail("JSON Error: {}", simdjson::error_message(error)); return fail("JSON Error: {}", simdjson::error_message(error));
} }
return JsonDocument(std::move(parser), root); return JsonDocument(std::move(parser), root);
} }
inline auto Json::encode(Ref<nlohmann::json> data) -> String { inline auto Json::encode(Ref<nlohmann::json> data) -> String
{
return data.dump(); return data.dump();
} }
template <typename T> template<typename T> inline auto Json::parse_to_struct(Ref<String> json_str) -> Result<T>
inline auto Json::parse_to_struct(Ref<String> json_str) -> Result<T> { {
Mut<T> result{}; Mut<T> result{};
const glz::error_ctx err = glz::read<GLAZE_OPTS>(result, json_str); const glz::error_ctx err = glz::read<GLAZE_OPTS>(result, json_str);
if (err) { if (err)
return fail("JSON Struct Parse Error: {}", {
glz::format_error(err, json_str)); return fail("JSON Struct Parse Error: {}", glz::format_error(err, json_str));
} }
return result; return result;
} }
template <typename T> template<typename T> inline auto Json::encode_struct(Ref<T> data) -> Result<String>
inline auto Json::encode_struct(Ref<T> data) -> Result<String> { {
Mut<String> result; Mut<String> result;
const glz::error_ctx err = glz::write_json(data, result); const glz::error_ctx err = glz::write_json(data, result);
if (err) { if (err)
{
return fail("JSON Struct Encode Error"); return fail("JSON Struct Encode Error");
} }
return result; return result;
} }
} // namespace IACore } // namespace IACore

View File

@ -18,8 +18,7 @@
#include <IACore/PCH.hpp> #include <IACore/PCH.hpp>
#define IA_LOG_SET_FILE(path) IACore::Logger::enable_logging_to_disk(path) #define IA_LOG_SET_FILE(path) IACore::Logger::enable_logging_to_disk(path)
#define IA_LOG_SET_LEVEL(level) \ #define IA_LOG_SET_LEVEL(level) IACore::Logger::set_log_level(IACore::Logger::LogLevel::level)
IACore::Logger::set_log_level(IACore::Logger::LogLevel::level)
#define IA_LOG_TRACE(...) IACore::Logger::trace(__VA_ARGS__) #define IA_LOG_TRACE(...) IACore::Logger::trace(__VA_ARGS__)
#define IA_LOG_DEBUG(...) IACore::Logger::debug(__VA_ARGS__) #define IA_LOG_DEBUG(...) IACore::Logger::debug(__VA_ARGS__)
@ -27,42 +26,49 @@
#define IA_LOG_WARN(...) IACore::Logger::warn(__VA_ARGS__) #define IA_LOG_WARN(...) IACore::Logger::warn(__VA_ARGS__)
#define IA_LOG_ERROR(...) IACore::Logger::error(__VA_ARGS__) #define IA_LOG_ERROR(...) IACore::Logger::error(__VA_ARGS__)
namespace IACore { namespace IACore
class Logger { {
class Logger
{
public: public:
enum class LogLevel { Trace, Debug, Info, Warn, Error }; enum class LogLevel
{
Trace,
Debug,
Info,
Warn,
Error
};
public: public:
static auto enable_logging_to_disk(const char *file_path) -> Result<void>; static auto enable_logging_to_disk(const char *file_path) -> Result<void>;
static auto set_log_level(const LogLevel log_level) -> void; static auto set_log_level(const LogLevel log_level) -> void;
template <typename... Args> template<typename... Args>
static auto trace(const std::format_string<Args...> fmt, static auto trace(const std::format_string<Args...> fmt, ForwardRef<Args>... args) -> void
ForwardRef<Args>... args) -> void { {
log_trace(std::vformat(fmt.get(), std::make_format_args(args...))); log_trace(std::vformat(fmt.get(), std::make_format_args(args...)));
} }
template <typename... Args> template<typename... Args>
static auto debug(const std::format_string<Args...> fmt, static auto debug(const std::format_string<Args...> fmt, ForwardRef<Args>... args) -> void
ForwardRef<Args>... args) -> void { {
log_debug(std::vformat(fmt.get(), std::make_format_args(args...))); log_debug(std::vformat(fmt.get(), std::make_format_args(args...)));
} }
template <typename... Args> template<typename... Args> static auto info(const std::format_string<Args...> fmt, ForwardRef<Args>... args) -> void
static auto info(const std::format_string<Args...> fmt, {
ForwardRef<Args>... args) -> void {
log_info(std::vformat(fmt.get(), std::make_format_args(args...))); log_info(std::vformat(fmt.get(), std::make_format_args(args...)));
} }
template <typename... Args> template<typename... Args> static auto warn(const std::format_string<Args...> fmt, ForwardRef<Args>... args) -> void
static auto warn(const std::format_string<Args...> fmt, {
ForwardRef<Args>... args) -> void {
log_warn(std::vformat(fmt.get(), std::make_format_args(args...))); log_warn(std::vformat(fmt.get(), std::make_format_args(args...)));
} }
template <typename... Args> template<typename... Args>
static auto error(const std::format_string<Args...> fmt, static auto error(const std::format_string<Args...> fmt, ForwardRef<Args>... args) -> void
ForwardRef<Args>... args) -> void { {
log_error(std::vformat(fmt.get(), std::make_format_args(args...))); log_error(std::vformat(fmt.get(), std::make_format_args(args...)));
} }
@ -70,44 +76,63 @@ public:
private: private:
#if IA_DISABLE_LOGGING > 0 #if IA_DISABLE_LOGGING > 0
static auto log_trace(ForwardRef<String> msg) -> void { IA_UNUSED(msg); } static auto log_trace(ForwardRef<String> msg) -> void
{
IA_UNUSED(msg);
}
static auto log_debug(ForwardRef<String> msg) -> void { IA_UNUSED(msg); } static auto log_debug(ForwardRef<String> msg) -> void
{
IA_UNUSED(msg);
}
static auto log_info(ForwardRef<String> msg) -> void { IA_UNUSED(msg); } static auto log_info(ForwardRef<String> msg) -> void
{
IA_UNUSED(msg);
}
static auto log_warn(ForwardRef<String> msg) -> void { IA_UNUSED(msg); } static auto log_warn(ForwardRef<String> msg) -> void
{
IA_UNUSED(msg);
}
static auto log_error(ForwardRef<String> msg) -> void { IA_UNUSED(msg); } static auto log_error(ForwardRef<String> msg) -> void
{
IA_UNUSED(msg);
}
#else #else
static auto log_trace(ForwardRef<String> msg) -> void { static auto log_trace(ForwardRef<String> msg) -> void
{
if (m_log_level <= LogLevel::Trace) if (m_log_level <= LogLevel::Trace)
log_internal(console::RESET, "TRACE", std::move(msg)); log_internal(console::RESET, "TRACE", std::move(msg));
} }
static auto log_debug(ForwardRef<String> msg) -> void { static auto log_debug(ForwardRef<String> msg) -> void
{
if (m_log_level <= LogLevel::Debug) if (m_log_level <= LogLevel::Debug)
log_internal(console::CYAN, "DEBUG", std::move(msg)); log_internal(console::CYAN, "DEBUG", std::move(msg));
} }
static auto log_info(ForwardRef<String> msg) -> void { static auto log_info(ForwardRef<String> msg) -> void
{
if (m_log_level <= LogLevel::Info) if (m_log_level <= LogLevel::Info)
log_internal(console::GREEN, "INFO", std::move(msg)); log_internal(console::GREEN, "INFO", std::move(msg));
} }
static auto log_warn(ForwardRef<String> msg) -> void { static auto log_warn(ForwardRef<String> msg) -> void
{
if (m_log_level <= LogLevel::Warn) if (m_log_level <= LogLevel::Warn)
log_internal(console::YELLOW, "WARN", std::move(msg)); log_internal(console::YELLOW, "WARN", std::move(msg));
} }
static auto log_error(ForwardRef<String> msg) -> void { static auto log_error(ForwardRef<String> msg) -> void
{
if (m_log_level <= LogLevel::Error) if (m_log_level <= LogLevel::Error)
log_internal(console::RED, "ERROR", std::move(msg)); log_internal(console::RED, "ERROR", std::move(msg));
} }
#endif #endif
static auto log_internal(const char *prefix, const char *tag, static auto log_internal(const char *prefix, const char *tag, ForwardRef<String> msg) -> void;
ForwardRef<String> msg) -> void;
private: private:
static Mut<LogLevel> m_log_level; static Mut<LogLevel> m_log_level;
@ -118,5 +143,5 @@ private:
friend void initialize(); friend void initialize();
friend void terminate(); friend void terminate();
}; };
} // namespace IACore } // namespace IACore

View File

@ -16,42 +16,42 @@
#pragma once #pragma once
#if defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64) #if defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64)
#define IA_ARCH_X64 1 # define IA_ARCH_X64 1
#elif defined(__aarch64__) || defined(_M_ARM64) #elif defined(__aarch64__) || defined(_M_ARM64)
#define IA_ARCH_ARM64 1 # define IA_ARCH_ARM64 1
#elif defined(__wasm__) || defined(__wasm32__) || defined(__wasm64__) #elif defined(__wasm__) || defined(__wasm32__) || defined(__wasm64__)
#define IA_ARCH_WASM 1 # define IA_ARCH_WASM 1
#else #else
#error "IACore: Unsupported Architecture." # error "IACore: Unsupported Architecture."
#endif #endif
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
#define IA_PLATFORM_WINDOWS 1 # define IA_PLATFORM_WINDOWS 1
#elif __APPLE__ #elif __APPLE__
#include <TargetConditionals.h> # include <TargetConditionals.h>
#define IA_PLATFORM_APPLE 1 # define IA_PLATFORM_APPLE 1
#define IA_PLATFORM_UNIX 1 # define IA_PLATFORM_UNIX 1
#elif __linux__ #elif __linux__
#define IA_PLATFORM_LINUX 1 # define IA_PLATFORM_LINUX 1
#define IA_PLATFORM_UNIX 1 # define IA_PLATFORM_UNIX 1
#elif __wasm__ #elif __wasm__
#define IA_PLATFORM_WASM 1 # define IA_PLATFORM_WASM 1
#else #else
#error "IACore: Unsupported Platform." # error "IACore: Unsupported Platform."
#endif #endif
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
#ifndef WIN32_LEAN_AND_MEAN # ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN
#endif # endif
#ifndef NOMINMAX # ifndef NOMINMAX
#define NOMINMAX # define NOMINMAX
#endif # endif
#include <windows.h> # include <windows.h>
#elif IA_PLATFORM_UNIX #elif IA_PLATFORM_UNIX
#include <signal.h> # include <signal.h>
#include <sys/wait.h> # include <sys/wait.h>
#include <unistd.h> # include <unistd.h>
#endif #endif
#include <atomic> #include <atomic>
@ -70,66 +70,69 @@
#include <auxid/auxid.hpp> #include <auxid/auxid.hpp>
namespace IACore { namespace IACore
{
using namespace Auxid; using namespace Auxid;
// ============================================================================= // =============================================================================
// Build Environment & Constants // Build Environment & Constants
// ============================================================================= // =============================================================================
namespace Env { namespace Env
{
using namespace Auxid::Env; using namespace Auxid::Env;
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
constexpr const bool IS_WINDOWS = true; constexpr const bool IS_WINDOWS = true;
constexpr const bool IS_UNIX = false; constexpr const bool IS_UNIX = false;
#else #else
constexpr const bool IS_WINDOWS = false; constexpr const bool IS_WINDOWS = false;
constexpr const bool IS_UNIX = true; constexpr const bool IS_UNIX = true;
#endif #endif
constexpr const usize MAX_PATH_LEN = 4096; constexpr const usize MAX_PATH_LEN = 4096;
} // namespace Env } // namespace Env
// ============================================================================= // =============================================================================
// Data Structures & Aliases // Data Structures & Aliases
// ============================================================================= // =============================================================================
template <typename K, typename V> template<typename K, typename V> using HashMap = ankerl::unordered_dense::map<K, V>;
using HashMap = ankerl::unordered_dense::map<K, V>; template<typename T> using HashSet = ankerl::unordered_dense::set<T>;
template <typename T> using HashSet = ankerl::unordered_dense::set<T>;
using Path = std::filesystem::path; using Path = std::filesystem::path;
// ============================================================================= // =============================================================================
// Versioning // Versioning
// ============================================================================= // =============================================================================
struct Version { struct Version
{
u32 major = 0; u32 major = 0;
u32 minor = 0; u32 minor = 0;
u32 patch = 0; u32 patch = 0;
[[nodiscard]] constexpr auto to_u64() const -> u64 { [[nodiscard]] constexpr auto to_u64() const -> u64
return (static_cast<u64>(major) << 40) | (static_cast<u64>(minor) << 16) | {
(static_cast<u64>(patch)); return (static_cast<u64>(major) << 40) | (static_cast<u64>(minor) << 16) | (static_cast<u64>(patch));
} }
}; };
// ============================================================================= // =============================================================================
// Console Colors // Console Colors
// ============================================================================= // =============================================================================
namespace console { namespace console
constexpr const char *RESET = "\033[0m"; {
constexpr const char *RED = "\033[31m"; constexpr const char *RESET = "\033[0m";
constexpr const char *GREEN = "\033[32m"; constexpr const char *RED = "\033[31m";
constexpr const char *YELLOW = "\033[33m"; constexpr const char *GREEN = "\033[32m";
constexpr const char *BLUE = "\033[34m"; constexpr const char *YELLOW = "\033[33m";
constexpr const char *MAGENTA = "\033[35m"; constexpr const char *BLUE = "\033[34m";
constexpr const char *CYAN = "\033[36m"; constexpr const char *MAGENTA = "\033[35m";
} // namespace console constexpr const char *CYAN = "\033[36m";
} // namespace console
} // namespace IACore } // namespace IACore
#define IA_NODISCARD [[nodiscard]] #define IA_NODISCARD [[nodiscard]]
#define IA_UNUSED(v) (void)(v) #define IA_UNUSED(v) (void) (v)

View File

@ -18,35 +18,40 @@
#include <IACore/PCH.hpp> #include <IACore/PCH.hpp>
#if IA_ARCH_X64 #if IA_ARCH_X64
#ifdef _MSC_VER # ifdef _MSC_VER
#include <intrin.h> # include <intrin.h>
#else # else
#include <immintrin.h> # include <immintrin.h>
#endif # endif
#elif IA_ARCH_ARM64 #elif IA_ARCH_ARM64
#include <arm_acle.h> # include <arm_acle.h>
#endif #endif
namespace IACore { namespace IACore
class Platform { {
class Platform
{
public: public:
struct Capabilities { struct Capabilities
{
Mut<bool> hardware_crc32 = false; Mut<bool> hardware_crc32 = false;
}; };
static auto check_cpu() -> bool; static auto check_cpu() -> bool;
#if IA_ARCH_X64 #if IA_ARCH_X64
static auto cpuid(const i32 function, const i32 sub_function, Mut<i32 *> out) static auto cpuid(const i32 function, const i32 sub_function, Mut<i32 *> out) -> void;
-> void;
#endif #endif
static auto get_architecture_name() -> const char *; static auto get_architecture_name() -> const char *;
static auto get_operating_system_name() -> const char *; static auto get_operating_system_name() -> const char *;
static auto get_capabilities() -> Ref<Capabilities> { return s_capabilities; } static auto get_capabilities() -> Ref<Capabilities>
{
return s_capabilities;
}
private: private:
static Mut<Capabilities> s_capabilities; static Mut<Capabilities> s_capabilities;
}; };
} // namespace IACore } // namespace IACore

View File

@ -22,33 +22,36 @@ using NativeProcessID = DWORD;
#elif IA_PLATFORM_UNIX #elif IA_PLATFORM_UNIX
using NativeProcessID = pid_t; using NativeProcessID = pid_t;
#else #else
#error "This platform does not support IACore ProcessOps" # error "This platform does not support IACore ProcessOps"
#endif #endif
namespace IACore { namespace IACore
struct ProcessHandle { {
struct ProcessHandle
{
Mut<std::atomic<NativeProcessID>> id{0}; Mut<std::atomic<NativeProcessID>> id{0};
Mut<std::atomic<bool>> is_running{false}; Mut<std::atomic<bool>> is_running{false};
[[nodiscard]] auto is_active() const -> bool { return is_running && id != 0; } [[nodiscard]] auto is_active() const -> bool
{
return is_running && id != 0;
}
private: private:
Mut<std::jthread> m_thread_handle; Mut<std::jthread> m_thread_handle;
friend class ProcessOps; friend class ProcessOps;
}; };
class ProcessOps { class ProcessOps
{
public: public:
static auto get_current_process_id() -> NativeProcessID; static auto get_current_process_id() -> NativeProcessID;
static auto spawn_process_sync( static auto spawn_process_sync(Ref<String> command, Ref<String> args,
Ref<String> command, Ref<String> args, const std::function<void(StringView)> on_output_line_callback) -> Result<i32>;
const std::function<void(StringView)> on_output_line_callback)
-> Result<i32>;
static auto spawn_process_async( static auto spawn_process_async(Ref<String> command, Ref<String> args,
Ref<String> command, Ref<String> args,
const std::function<void(StringView)> on_output_line_callback, const std::function<void(StringView)> on_output_line_callback,
const std::function<void(Result<i32>)> on_finish_callback) const std::function<void(Result<i32>)> on_finish_callback)
-> Result<Box<ProcessHandle>>; -> Result<Box<ProcessHandle>>;
@ -56,14 +59,12 @@ public:
static auto terminate_process(Ref<Box<ProcessHandle>> handle) -> void; static auto terminate_process(Ref<Box<ProcessHandle>> handle) -> void;
private: private:
static auto spawn_process_windows( static auto spawn_process_windows(Ref<String> command, Ref<String> args,
Ref<String> command, Ref<String> args,
const std::function<void(StringView)> on_output_line_callback, const std::function<void(StringView)> on_output_line_callback,
MutRef<std::atomic<NativeProcessID>> id) -> Result<i32>; MutRef<std::atomic<NativeProcessID>> id) -> Result<i32>;
static auto spawn_process_posix( static auto spawn_process_posix(Ref<String> command, Ref<String> args,
Ref<String> command, Ref<String> args,
const std::function<void(StringView)> on_output_line_callback, const std::function<void(StringView)> on_output_line_callback,
MutRef<std::atomic<NativeProcessID>> id) -> Result<i32>; MutRef<std::atomic<NativeProcessID>> id) -> Result<i32>;
}; };
} // namespace IACore } // namespace IACore

View File

@ -18,26 +18,27 @@
#include <IACore/PCH.hpp> #include <IACore/PCH.hpp>
#if defined(__clang__) #if defined(__clang__)
#pragma GCC diagnostic push # pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter" # pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" # pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
#endif #endif
#include <hwy/highway.h> #include <hwy/highway.h>
#if defined(__clang__) #if defined(__clang__)
#pragma GCC diagnostic pop # pragma GCC diagnostic pop
#endif #endif
namespace IACore { namespace IACore
namespace hn = hwy::HWY_NAMESPACE; {
namespace hn = hwy::HWY_NAMESPACE;
#if HWY_TARGET == HWY_SCALAR #if HWY_TARGET == HWY_SCALAR
#pragma message( \ # pragma message("Warning: Configuration mismatch. IACore is being compiled for SCALAR SIMD (Slow)")
"Warning: Configuration mismatch. IACore is being compiled for SCALAR SIMD (Slow)")
#endif #endif
class alignas(16) IntVec4 { class alignas(16) IntVec4
{
public: public:
IntVec4() = default; IntVec4() = default;
@ -60,11 +61,9 @@ public:
[[nodiscard]] inline auto sat_add(Ref<IntVec4> other) const -> IntVec4; [[nodiscard]] inline auto sat_add(Ref<IntVec4> other) const -> IntVec4;
[[nodiscard]] inline auto sat_sub(Ref<IntVec4> other) const -> IntVec4; [[nodiscard]] inline auto sat_sub(Ref<IntVec4> other) const -> IntVec4;
[[nodiscard]] inline auto clamp(const u32 min, const u32 max) const [[nodiscard]] inline auto clamp(const u32 min, const u32 max) const -> IntVec4;
-> IntVec4;
[[nodiscard]] inline auto mult_add(Ref<IntVec4> multiplier, [[nodiscard]] inline auto mult_add(Ref<IntVec4> multiplier, Ref<IntVec4> addend) const -> IntVec4;
Ref<IntVec4> addend) const -> IntVec4;
inline auto store(Mut<u32 *> values) -> void; inline auto store(Mut<u32 *> values) -> void;
static inline auto load(const u32 *values) -> IntVec4; static inline auto load(const u32 *values) -> IntVec4;
@ -74,10 +73,13 @@ private:
Mut<hn::Vec<Tag>> m_data; Mut<hn::Vec<Tag>> m_data;
inline explicit IntVec4(const hn::Vec<Tag> v) : m_data(v) {} inline explicit IntVec4(const hn::Vec<Tag> v) : m_data(v)
}; {
}
};
class alignas(16) FloatVec4 { class alignas(16) FloatVec4
{
public: public:
FloatVec4() = default; FloatVec4() = default;
@ -90,8 +92,7 @@ public:
inline auto operator*(Ref<FloatVec4> other) const -> FloatVec4; inline auto operator*(Ref<FloatVec4> other) const -> FloatVec4;
inline auto operator/(Ref<FloatVec4> other) const -> FloatVec4; inline auto operator/(Ref<FloatVec4> other) const -> FloatVec4;
[[nodiscard]] inline auto clamp(const f32 min, const f32 max) const [[nodiscard]] inline auto clamp(const f32 min, const f32 max) const -> FloatVec4;
-> FloatVec4;
[[nodiscard]] inline auto abs() const -> FloatVec4; [[nodiscard]] inline auto abs() const -> FloatVec4;
[[nodiscard]] inline auto sqrt() const -> FloatVec4; [[nodiscard]] inline auto sqrt() const -> FloatVec4;
@ -100,8 +101,7 @@ public:
[[nodiscard]] inline auto dot(Ref<FloatVec4> other) const -> f32; [[nodiscard]] inline auto dot(Ref<FloatVec4> other) const -> f32;
[[nodiscard]] inline auto mult_add(Ref<FloatVec4> multiplier, [[nodiscard]] inline auto mult_add(Ref<FloatVec4> multiplier, Ref<FloatVec4> addend) const -> FloatVec4;
Ref<FloatVec4> addend) const -> FloatVec4;
inline auto store(Mut<f32 *> values) -> void; inline auto store(Mut<f32 *> values) -> void;
static inline auto load(const f32 *values) -> FloatVec4; static inline auto load(const f32 *values) -> FloatVec4;
@ -111,168 +111,208 @@ private:
Mut<hn::Vec<Tag>> m_data; Mut<hn::Vec<Tag>> m_data;
inline explicit FloatVec4(const hn::Vec<Tag> v) : m_data(v) {} inline explicit FloatVec4(const hn::Vec<Tag> v) : m_data(v)
}; {
}
};
} // namespace IACore } // namespace IACore
namespace IACore { namespace IACore
IntVec4::IntVec4(const u32 s) { {
IntVec4::IntVec4(const u32 s)
{
const Tag d; const Tag d;
m_data = hn::Set(d, s); m_data = hn::Set(d, s);
} }
IntVec4::IntVec4(const u32 *values) { IntVec4::IntVec4(const u32 *values)
{
const Tag data; const Tag data;
m_data = hn::Load(data, values); m_data = hn::Load(data, values);
} }
IntVec4::IntVec4(const u32 a, const u32 b, const u32 c, const u32 d) { IntVec4::IntVec4(const u32 a, const u32 b, const u32 c, const u32 d)
{
const Tag data; const Tag data;
alignas(16) Mut<Array<u32, 4>> values = {a, b, c, d}; alignas(16) Mut<Array<u32, 4>> values = {a, b, c, d};
m_data = hn::Load(data, values.data()); m_data = hn::Load(data, values.data());
} }
auto IntVec4::operator+(Ref<IntVec4> other) const -> IntVec4 { auto IntVec4::operator+(Ref<IntVec4> other) const -> IntVec4
{
return IntVec4(hn::Add(m_data, other.m_data)); return IntVec4(hn::Add(m_data, other.m_data));
} }
auto IntVec4::operator-(Ref<IntVec4> other) const -> IntVec4 { auto IntVec4::operator-(Ref<IntVec4> other) const -> IntVec4
{
return IntVec4(hn::Sub(m_data, other.m_data)); return IntVec4(hn::Sub(m_data, other.m_data));
} }
auto IntVec4::operator*(Ref<IntVec4> other) const -> IntVec4 { auto IntVec4::operator*(Ref<IntVec4> other) const -> IntVec4
{
return IntVec4(hn::Mul(m_data, other.m_data)); return IntVec4(hn::Mul(m_data, other.m_data));
} }
auto IntVec4::operator&(Ref<IntVec4> other) const -> IntVec4 { auto IntVec4::operator&(Ref<IntVec4> other) const -> IntVec4
{
return IntVec4(hn::And(m_data, other.m_data)); return IntVec4(hn::And(m_data, other.m_data));
} }
auto IntVec4::operator|(Ref<IntVec4> other) const -> IntVec4 { auto IntVec4::operator|(Ref<IntVec4> other) const -> IntVec4
{
return IntVec4(hn::Or(m_data, other.m_data)); return IntVec4(hn::Or(m_data, other.m_data));
} }
auto IntVec4::operator^(Ref<IntVec4> other) const -> IntVec4 { auto IntVec4::operator^(Ref<IntVec4> other) const -> IntVec4
{
return IntVec4(hn::Xor(m_data, other.m_data)); 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<<(const u32 amount) const -> IntVec4 { auto IntVec4::operator<<(const u32 amount) const -> IntVec4
{
return IntVec4(hn::ShiftLeftSame(m_data, amount)); return IntVec4(hn::ShiftLeftSame(m_data, amount));
} }
auto IntVec4::operator>>(const u32 amount) const -> IntVec4 { auto IntVec4::operator>>(const u32 amount) const -> IntVec4
{
return IntVec4(hn::ShiftRightSame(m_data, amount)); return IntVec4(hn::ShiftRightSame(m_data, amount));
} }
auto IntVec4::mult_add(Ref<IntVec4> multiplier, Ref<IntVec4> addend) const auto IntVec4::mult_add(Ref<IntVec4> multiplier, Ref<IntVec4> addend) const -> IntVec4
-> IntVec4 { {
return IntVec4(hn::MulAdd(m_data, multiplier.m_data, addend.m_data)); return IntVec4(hn::MulAdd(m_data, multiplier.m_data, addend.m_data));
} }
auto IntVec4::sat_add(Ref<IntVec4> other) const -> IntVec4 { auto IntVec4::sat_add(Ref<IntVec4> other) const -> IntVec4
{
return IntVec4(hn::SaturatedAdd(m_data, other.m_data)); return IntVec4(hn::SaturatedAdd(m_data, other.m_data));
} }
auto IntVec4::sat_sub(Ref<IntVec4> other) const -> IntVec4 { auto IntVec4::sat_sub(Ref<IntVec4> other) const -> IntVec4
{
return IntVec4(hn::SaturatedSub(m_data, other.m_data)); return IntVec4(hn::SaturatedSub(m_data, other.m_data));
} }
auto IntVec4::clamp(const u32 min, const u32 max) const -> IntVec4 { auto IntVec4::clamp(const u32 min, const u32 max) const -> IntVec4
{
const Tag d; const Tag d;
const hn::Vec<Tag> v_min = hn::Set(d, min); const hn::Vec<Tag> v_min = hn::Set(d, min);
const hn::Vec<Tag> v_max = hn::Set(d, max); const hn::Vec<Tag> v_max = hn::Set(d, max);
return IntVec4(hn::Min(hn::Max(m_data, v_min), v_max)); return IntVec4(hn::Min(hn::Max(m_data, v_min), v_max));
} }
auto IntVec4::store(Mut<u32 *> values) -> void { auto IntVec4::store(Mut<u32 *> values) -> void
{
const Tag d; const Tag d;
hn::Store(m_data, d, values); hn::Store(m_data, d, values);
} }
auto IntVec4::load(const u32 *values) -> IntVec4 { auto IntVec4::load(const u32 *values) -> IntVec4
{
const Tag d; const Tag d;
return IntVec4(hn::Load(d, values)); return IntVec4(hn::Load(d, values));
} }
} // namespace IACore } // namespace IACore
namespace IACore { namespace IACore
FloatVec4::FloatVec4(const f32 s) { {
FloatVec4::FloatVec4(const f32 s)
{
const Tag d; const Tag d;
m_data = hn::Set(d, s); m_data = hn::Set(d, s);
} }
FloatVec4::FloatVec4(const f32 *values) { FloatVec4::FloatVec4(const f32 *values)
{
const Tag d; const Tag d;
m_data = hn::Load(d, values); m_data = hn::Load(d, values);
} }
FloatVec4::FloatVec4(const f32 a, const f32 b, const f32 c, const f32 d) { FloatVec4::FloatVec4(const f32 a, const f32 b, const f32 c, const f32 d)
{
const Tag data; const Tag data;
alignas(16) Mut<Array<f32, 4>> temp = {a, b, c, d}; alignas(16) Mut<Array<f32, 4>> temp = {a, b, c, d};
m_data = hn::Load(data, temp.data()); m_data = hn::Load(data, temp.data());
} }
auto FloatVec4::operator+(Ref<FloatVec4> other) const -> FloatVec4 { auto FloatVec4::operator+(Ref<FloatVec4> other) const -> FloatVec4
{
return FloatVec4(hn::Add(m_data, other.m_data)); return FloatVec4(hn::Add(m_data, other.m_data));
} }
auto FloatVec4::operator-(Ref<FloatVec4> other) const -> FloatVec4 { auto FloatVec4::operator-(Ref<FloatVec4> other) const -> FloatVec4
{
return FloatVec4(hn::Sub(m_data, other.m_data)); return FloatVec4(hn::Sub(m_data, other.m_data));
} }
auto FloatVec4::operator*(Ref<FloatVec4> other) const -> FloatVec4 { auto FloatVec4::operator*(Ref<FloatVec4> other) const -> FloatVec4
{
return FloatVec4(hn::Mul(m_data, other.m_data)); return FloatVec4(hn::Mul(m_data, other.m_data));
} }
auto FloatVec4::operator/(Ref<FloatVec4> other) const -> FloatVec4 { auto FloatVec4::operator/(Ref<FloatVec4> other) const -> FloatVec4
{
return FloatVec4(hn::Div(m_data, other.m_data)); return FloatVec4(hn::Div(m_data, other.m_data));
} }
auto FloatVec4::mult_add(Ref<FloatVec4> multiplier, Ref<FloatVec4> addend) const auto FloatVec4::mult_add(Ref<FloatVec4> multiplier, Ref<FloatVec4> addend) const -> FloatVec4
-> FloatVec4 { {
return FloatVec4(hn::MulAdd(m_data, multiplier.m_data, addend.m_data)); return FloatVec4(hn::MulAdd(m_data, multiplier.m_data, addend.m_data));
} }
auto FloatVec4::clamp(const f32 min, const f32 max) const -> FloatVec4 { auto FloatVec4::clamp(const f32 min, const f32 max) const -> FloatVec4
{
const Tag d; const Tag d;
const hn::Vec<Tag> v_min = hn::Set(d, min); const hn::Vec<Tag> v_min = hn::Set(d, min);
const hn::Vec<Tag> v_max = hn::Set(d, max); const hn::Vec<Tag> v_max = hn::Set(d, max);
return FloatVec4(hn::Min(hn::Max(m_data, v_min), v_max)); return FloatVec4(hn::Min(hn::Max(m_data, v_min), v_max));
} }
auto FloatVec4::sqrt() const -> FloatVec4 { auto FloatVec4::sqrt() const -> FloatVec4
{
return FloatVec4(hn::Sqrt(m_data)); return FloatVec4(hn::Sqrt(m_data));
} }
auto FloatVec4::rsqrt() const -> FloatVec4 { auto FloatVec4::rsqrt() const -> FloatVec4
{
return FloatVec4(hn::ApproximateReciprocalSqrt(m_data)); 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(Ref<FloatVec4> other) const -> f32 { auto FloatVec4::dot(Ref<FloatVec4> other) const -> f32
{
const Tag d; const Tag d;
const hn::Vec<Tag> v_mul = hn::Mul(m_data, other.m_data); const hn::Vec<Tag> v_mul = hn::Mul(m_data, other.m_data);
return hn::ReduceSum(d, v_mul); return hn::ReduceSum(d, v_mul);
} }
auto FloatVec4::normalize() const -> FloatVec4 { auto FloatVec4::normalize() const -> FloatVec4
{
const Tag d; const Tag d;
const hn::Vec<Tag> v_mul = hn::Mul(m_data, m_data); const hn::Vec<Tag> v_mul = hn::Mul(m_data, m_data);
const hn::Vec<Tag> v_len_sq = hn::SumOfLanes(d, v_mul); const hn::Vec<Tag> v_len_sq = hn::SumOfLanes(d, v_mul);
const hn::Vec<Tag> v_inv_len = hn::ApproximateReciprocalSqrt(v_len_sq); const hn::Vec<Tag> v_inv_len = hn::ApproximateReciprocalSqrt(v_len_sq);
return FloatVec4(hn::Mul(m_data, v_inv_len)); return FloatVec4(hn::Mul(m_data, v_inv_len));
} }
auto FloatVec4::store(Mut<f32 *> values) -> void { auto FloatVec4::store(Mut<f32 *> values) -> void
{
const Tag d; const Tag d;
hn::Store(m_data, d, values); hn::Store(m_data, d, values);
} }
auto FloatVec4::load(const f32 *values) -> FloatVec4 { auto FloatVec4::load(const f32 *values) -> FloatVec4
{
const Tag d; const Tag d;
return FloatVec4(hn::Load(d, values)); return FloatVec4(hn::Load(d, values));
} }
} // namespace IACore } // namespace IACore

View File

@ -18,43 +18,48 @@
#include <IACore/PCH.hpp> #include <IACore/PCH.hpp>
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
#include <afunix.h> # include <winsock2.h>
#include <winsock2.h> # include <ws2tcpip.h>
#include <ws2tcpip.h> # include <afunix.h>
#pragma comment(lib, "ws2_32.lib") # pragma comment(lib, "ws2_32.lib")
#elif IA_PLATFORM_UNIX #elif IA_PLATFORM_UNIX
#include <netinet/in.h> # include <netinet/in.h>
#include <sys/socket.h> # include <sys/socket.h>
#include <sys/types.h> # include <sys/types.h>
#include <sys/un.h> # include <sys/un.h>
#ifndef INVALID_SOCKET # ifndef INVALID_SOCKET
#define INVALID_SOCKET -1 # define INVALID_SOCKET -1
#endif # endif
#else #else
#error "IACore SocketOps is not supported on this platform." # error "IACore SocketOps is not supported on this platform."
#endif #endif
namespace IACore { namespace IACore
{
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
using SocketHandle = SOCKET; using SocketHandle = SOCKET;
#elif IA_PLATFORM_UNIX #elif IA_PLATFORM_UNIX
using SocketHandle = i32; using SocketHandle = i32;
#endif #endif
class SocketOps { class SocketOps
{
public: public:
// SocketOps correctly handles multiple calls to initialize and terminate. // SocketOps correctly handles multiple calls to initialize and terminate.
// Make sure every initialize call is paired with a corresponding terminate // Make sure every initialize call is paired with a corresponding terminate
// call. // call.
static auto initialize() -> Result<void> { static auto initialize() -> Result<void>
{
s_init_count++; s_init_count++;
if (s_init_count > 1) { if (s_init_count > 1)
{
return {}; return {};
} }
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
Mut<WSADATA> wsa_data; Mut<WSADATA> wsa_data;
const i32 res = WSAStartup(MAKEWORD(2, 2), &wsa_data); const i32 res = WSAStartup(MAKEWORD(2, 2), &wsa_data);
if (res != 0) { if (res != 0)
{
s_init_count--; s_init_count--;
return fail("WSAStartup failed with error: {}", res); return fail("WSAStartup failed with error: {}", res);
} }
@ -65,9 +70,11 @@ public:
// SocketOps correctly handles multiple calls to initialize and terminate. // SocketOps correctly handles multiple calls to initialize and terminate.
// Make sure every initialize call is paired with a corresponding terminate // Make sure every initialize call is paired with a corresponding terminate
// call. // call.
static auto terminate() -> void { static auto terminate() -> void
{
s_init_count--; s_init_count--;
if (s_init_count > 0) { if (s_init_count > 0)
{
return; return;
} }
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
@ -75,13 +82,18 @@ public:
#endif #endif
} }
static auto is_initialized() -> bool { return s_init_count > 0; } static auto is_initialized() -> bool
{
return s_init_count > 0;
}
static auto is_port_available_tcp(const u16 port) -> bool { static auto is_port_available_tcp(const u16 port) -> bool
{
return is_port_available(port, SOCK_STREAM); return is_port_available(port, SOCK_STREAM);
} }
static auto is_port_available_udp(const u16 port) -> bool { static auto is_port_available_udp(const u16 port) -> bool
{
return is_port_available(port, SOCK_DGRAM); return is_port_available(port, SOCK_DGRAM);
} }
@ -89,17 +101,15 @@ public:
static auto close(const SocketHandle sock) -> void; static auto close(const SocketHandle sock) -> void;
static auto listen(const SocketHandle sock, const i32 queue_size = 5) static auto listen(const SocketHandle sock, const i32 queue_size = 5) -> Result<void>;
-> Result<void>;
static auto create_unix_socket() -> Result<SocketHandle>; static auto create_unix_socket() -> Result<SocketHandle>;
static auto bind_unix_socket(const SocketHandle sock, const char *path) static auto bind_unix_socket(const SocketHandle sock, const char *path) -> Result<void>;
-> Result<void>; static auto connect_unix_socket(const SocketHandle sock, const char *path) -> Result<void>;
static auto connect_unix_socket(const SocketHandle sock, const char *path)
-> Result<void>;
static auto unlink_file(const char *path) -> void { static auto unlink_file(const char *path) -> void
{
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
DeleteFileA(path); DeleteFileA(path);
#elif IA_PLATFORM_UNIX #elif IA_PLATFORM_UNIX
@ -112,5 +122,5 @@ private:
private: private:
static Mut<i32> s_init_count; static Mut<i32> s_init_count;
}; };
} // namespace IACore } // namespace IACore

View File

@ -19,10 +19,13 @@
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
namespace IACore { namespace IACore
class StreamReader { {
class StreamReader
{
public: public:
enum class StorageType { enum class StorageType
{
NonOwning, NonOwning,
OwningMmap, OwningMmap,
OwningVector, OwningVector,
@ -42,27 +45,39 @@ public:
auto read(Mut<void *> buffer, const usize size) -> Result<void>; auto read(Mut<void *> buffer, const usize size) -> Result<void>;
template <typename T> template<typename T>
[[nodiscard("Check for EOF")]] [[nodiscard("Check for EOF")]]
auto read() -> Result<T>; auto read() -> Result<T>;
auto skip(const usize amount) -> void { auto skip(const usize amount) -> void
{
m_cursor = std::min(m_cursor + amount, m_data_size); m_cursor = std::min(m_cursor + amount, m_data_size);
} }
auto seek(const usize pos) -> void { auto seek(const usize pos) -> void
{
m_cursor = (pos > m_data_size) ? m_data_size : pos; m_cursor = (pos > m_data_size) ? m_data_size : pos;
} }
[[nodiscard]] auto cursor() const -> usize { return m_cursor; } [[nodiscard]] auto cursor() const -> usize
{
return m_cursor;
}
[[nodiscard]] auto size() const -> usize { return m_data_size; } [[nodiscard]] auto size() const -> usize
{
return m_data_size;
}
[[nodiscard]] auto remaining() const -> usize { [[nodiscard]] auto remaining() const -> usize
{
return m_data_size - m_cursor; return m_data_size - m_cursor;
} }
[[nodiscard]] auto is_eof() const -> bool { return m_cursor >= m_data_size; } [[nodiscard]] auto is_eof() const -> bool
{
return m_cursor >= m_data_size;
}
private: private:
Mut<const u8 *> m_data = nullptr; Mut<const u8 *> m_data = nullptr;
@ -70,11 +85,12 @@ private:
Mut<usize> m_data_size = 0; Mut<usize> m_data_size = 0;
Mut<Vec<u8>> m_owning_vector; Mut<Vec<u8>> m_owning_vector;
Mut<StorageType> m_storage_type = StorageType::NonOwning; Mut<StorageType> m_storage_type = StorageType::NonOwning;
}; };
inline auto StreamReader::read(Mut<void *> buffer, const usize size) inline auto StreamReader::read(Mut<void *> buffer, const usize size) -> Result<void>
-> Result<void> { {
if (m_cursor + size > m_data_size) [[unlikely]] { if (m_cursor + size > m_data_size) [[unlikely]]
{
return fail("Unexpected EOF while reading"); return fail("Unexpected EOF while reading");
} }
@ -82,17 +98,18 @@ inline auto StreamReader::read(Mut<void *> buffer, const usize size)
m_cursor += size; m_cursor += size;
return {}; return {};
} }
template <typename T> template<typename T>
[[nodiscard("Check for EOF")]] [[nodiscard("Check for EOF")]]
inline auto StreamReader::read() -> Result<T> { inline auto StreamReader::read() -> Result<T>
static_assert(std::is_trivially_copyable_v<T>, {
"T must be trivially copyable to read via memcpy"); static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable to read via memcpy");
constexpr const usize SIZE = sizeof(T); constexpr const usize SIZE = sizeof(T);
if (m_cursor + SIZE > m_data_size) [[unlikely]] { if (m_cursor + SIZE > m_data_size) [[unlikely]]
{
return fail("Unexpected EOF while reading"); return fail("Unexpected EOF while reading");
} }
@ -101,6 +118,6 @@ inline auto StreamReader::read() -> Result<T> {
m_cursor += SIZE; m_cursor += SIZE;
return value; return value;
} }
} // namespace IACore } // namespace IACore

View File

@ -17,11 +17,14 @@
#include <IACore/PCH.hpp> #include <IACore/PCH.hpp>
namespace IACore { namespace IACore
{
class StreamWriter { class StreamWriter
{
public: public:
enum class StorageType { enum class StorageType
{
NonOwning, NonOwning,
OwningFile, OwningFile,
OwningVector, OwningVector,
@ -43,11 +46,17 @@ public:
auto write(const u8 byte, const usize count) -> Result<void>; auto write(const u8 byte, const usize count) -> Result<void>;
auto write(const void *buffer, const usize size) -> Result<void>; auto write(const void *buffer, const usize size) -> Result<void>;
template <typename T> auto write(Ref<T> value) -> Result<void>; template<typename T> auto write(Ref<T> value) -> Result<void>;
[[nodiscard]] auto data() const -> const u8 * { return m_buffer; } [[nodiscard]] auto data() const -> const u8 *
{
return m_buffer;
}
[[nodiscard]] auto cursor() const -> usize { return m_cursor; } [[nodiscard]] auto cursor() const -> usize
{
return m_cursor;
}
auto flush() -> Result<void>; auto flush() -> Result<void>;
@ -61,11 +70,11 @@ private:
private: private:
auto flush_to_disk() -> Result<void>; auto flush_to_disk() -> Result<void>;
}; };
template <typename T> template<typename T> inline auto StreamWriter::write(Ref<T> value) -> Result<void>
inline auto StreamWriter::write(Ref<T> value) -> Result<void> { {
return write(&value, sizeof(T)); return write(&value, sizeof(T));
} }
} // namespace IACore } // namespace IACore

View File

@ -17,10 +17,12 @@
#include <IACore/PCH.hpp> #include <IACore/PCH.hpp>
namespace IACore { namespace IACore
class StringOps { {
class StringOps
{
public: public:
static auto encode_base64(const Span<const u8> data) -> String; static auto encode_base64(const Span<const u8> data) -> String;
static auto decode_base64(Ref<String> data) -> Vec<u8>; static auto decode_base64(Ref<String> data) -> Vec<u8>;
}; };
} // namespace IACore } // namespace IACore

View File

@ -19,8 +19,10 @@
#include <algorithm> #include <algorithm>
namespace IACore { namespace IACore
class Utils { {
class Utils
{
public: public:
static auto get_unix_time() -> u64; static auto get_unix_time() -> u64;
@ -38,32 +40,35 @@ public:
static auto hex_string_to_binary(const StringView hex) -> Result<Vec<u8>>; static auto hex_string_to_binary(const StringView hex) -> Result<Vec<u8>>;
template <typename Range> template<typename Range> inline static auto sort(ForwardRef<Range> range) -> void
inline static auto sort(ForwardRef<Range> range) -> void { {
std::ranges::sort(std::forward<Range>(range)); std::ranges::sort(std::forward<Range>(range));
} }
template <typename Range, typename T> template<typename Range, typename T>
inline static auto binary_search_left(ForwardRef<Range> range, Ref<T> value) inline static auto binary_search_left(ForwardRef<Range> range, Ref<T> value) -> auto
-> auto { {
return std::ranges::lower_bound(std::forward<Range>(range), value); return std::ranges::lower_bound(std::forward<Range>(range), value);
} }
template <typename Range, typename T> template<typename Range, typename T>
inline static auto binary_search_right(ForwardRef<Range> range, Ref<T> value) inline static auto binary_search_right(ForwardRef<Range> range, Ref<T> value) -> auto
-> auto { {
return std::ranges::upper_bound(std::forward<Range>(range), value); return std::ranges::upper_bound(std::forward<Range>(range), value);
} }
template <typename T> template<typename T> inline static auto hash_combine(MutRef<u64> seed, Ref<T> v) -> void
inline static auto hash_combine(MutRef<u64> seed, Ref<T> v) -> void { {
Mut<u64> h = 0; Mut<u64> h = 0;
if constexpr (std::is_constructible_v<StringView, T>) { if constexpr (std::is_constructible_v<StringView, T>)
{
const StringView sv(v); const StringView sv(v);
const ankerl::unordered_dense::hash<StringView> hasher; const ankerl::unordered_dense::hash<StringView> hasher;
h = hasher(sv); h = hasher(sv);
} else { }
else
{
const ankerl::unordered_dense::hash<T> hasher; const ankerl::unordered_dense::hash<T> hasher;
h = hasher(v); h = hasher(v);
} }
@ -71,21 +76,21 @@ public:
seed ^= h + 0x9e3779b97f4a7c15 + (seed << 6) + (seed >> 2); seed ^= h + 0x9e3779b97f4a7c15 + (seed << 6) + (seed >> 2);
} }
template <typename... Args> template<typename... Args> inline static auto compute_hash(Ref<Args>... args) -> u64
inline static auto compute_hash(Ref<Args>... args) -> u64 { {
Mut<u64> seed = 0; Mut<u64> seed = 0;
(hash_combine(seed, args), ...); (hash_combine(seed, args), ...);
return seed; return seed;
} }
template <typename T, typename... MemberPtrs> template<typename T, typename... MemberPtrs>
inline static auto compute_hash_flat(Ref<T> obj, const MemberPtrs... members) inline static auto compute_hash_flat(Ref<T> obj, const MemberPtrs... members) -> u64
-> u64 { {
Mut<u64> seed = 0; Mut<u64> seed = 0;
(hash_combine(seed, obj.*members), ...); (hash_combine(seed, obj.*members), ...);
return seed; return seed;
} }
}; };
} // namespace IACore } // namespace IACore
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -98,10 +103,12 @@ public:
// IA_MAKE_HASHABLE(Vector3, &Vector3::x, &Vector3::y, &Vector3::z) // IA_MAKE_HASHABLE(Vector3, &Vector3::x, &Vector3::y, &Vector3::z)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#define IA_MAKE_HASHABLE(Type, ...) \ #define IA_MAKE_HASHABLE(Type, ...) \
template <> struct ankerl::unordered_dense::hash<Type> { \ template<> struct ankerl::unordered_dense::hash<Type> \
{ \
using is_avalanching = void; \ using is_avalanching = void; \
IA_NODISCARD \ IA_NODISCARD \
auto operator()(IACore::Ref<Type> v) const noexcept -> IACore::u64 { \ auto operator()(IACore::Ref<Type> v) const noexcept -> IACore::u64 \
{ \
return IACore::Utils::compute_hash_flat(v, __VA_ARGS__); \ return IACore::Utils::compute_hash_flat(v, __VA_ARGS__); \
} \ } \
}; };

View File

@ -19,8 +19,10 @@
#include <pugixml.hpp> #include <pugixml.hpp>
namespace IACore { namespace IACore
class XML { {
class XML
{
public: public:
using Node = pugi::xml_node; using Node = pugi::xml_node;
using Document = pugi::xml_document; using Document = pugi::xml_document;
@ -29,11 +31,9 @@ public:
static auto parse_from_string(Ref<String> data) -> Result<Document>; static auto parse_from_string(Ref<String> data) -> Result<Document>;
static auto parse_from_file(Ref<Path> path) -> Result<Document>; static auto parse_from_file(Ref<Path> path) -> Result<Document>;
static auto serialize_to_string(Ref<Node> node, const bool escape = false) static auto serialize_to_string(Ref<Node> node, const bool escape = false) -> String;
-> String; static auto serialize_to_string(Ref<Document> doc, const bool escape = false) -> String;
static auto serialize_to_string(Ref<Document> doc, const bool escape = false)
-> String;
static auto escape_xml_string(Ref<String> xml) -> String; static auto escape_xml_string(Ref<String> xml) -> String;
}; };
} // namespace IACore } // namespace IACore

View File

@ -20,18 +20,24 @@
using namespace IACore; using namespace IACore;
struct SchedulerGuard { struct SchedulerGuard
SchedulerGuard(u8 worker_count = 2) { {
SchedulerGuard(u8 worker_count = 2)
{
(void)AsyncOps::initialize_scheduler(worker_count); (void) AsyncOps::initialize_scheduler(worker_count);
} }
~SchedulerGuard() { AsyncOps::terminate_scheduler(); } ~SchedulerGuard()
{
AsyncOps::terminate_scheduler();
}
}; };
IAT_BEGIN_BLOCK(Core, AsyncOps) IAT_BEGIN_BLOCK(Core, AsyncOps)
auto test_initialization() -> bool { auto test_initialization() -> bool
{
AsyncOps::terminate_scheduler(); AsyncOps::terminate_scheduler();
@ -50,14 +56,14 @@ auto test_initialization() -> bool {
return true; return true;
} }
auto test_basic_execution() -> bool { auto test_basic_execution() -> bool
{
SchedulerGuard guard(2); SchedulerGuard guard(2);
AsyncOps::Schedule schedule; AsyncOps::Schedule schedule;
std::atomic<i32> run_count{0}; std::atomic<i32> run_count{0};
AsyncOps::schedule_task([&](AsyncOps::WorkerId) { run_count++; }, 0, AsyncOps::schedule_task([&](AsyncOps::WorkerId) { run_count++; }, 0, &schedule);
&schedule);
AsyncOps::wait_for_schedule_completion(&schedule); AsyncOps::wait_for_schedule_completion(&schedule);
@ -66,14 +72,16 @@ auto test_basic_execution() -> bool {
return true; return true;
} }
auto test_concurrency() -> bool { auto test_concurrency() -> bool
{
SchedulerGuard guard(4); SchedulerGuard guard(4);
AsyncOps::Schedule schedule; AsyncOps::Schedule schedule;
std::atomic<i32> run_count{0}; std::atomic<i32> run_count{0};
const i32 total_tasks = 100; const i32 total_tasks = 100;
for (i32 i = 0; i < total_tasks; ++i) { for (i32 i = 0; i < total_tasks; ++i)
{
AsyncOps::schedule_task( AsyncOps::schedule_task(
[&](AsyncOps::WorkerId) { [&](AsyncOps::WorkerId) {
std::this_thread::sleep_for(std::chrono::microseconds(10)); std::this_thread::sleep_for(std::chrono::microseconds(10));
@ -89,17 +97,16 @@ auto test_concurrency() -> bool {
return true; return true;
} }
auto test_priorities() -> bool { auto test_priorities() -> bool
{
SchedulerGuard guard(2); SchedulerGuard guard(2);
AsyncOps::Schedule schedule; AsyncOps::Schedule schedule;
std::atomic<i32> high_priority_ran{0}; std::atomic<i32> high_priority_ran{0};
std::atomic<i32> normal_priority_ran{0}; std::atomic<i32> normal_priority_ran{0};
AsyncOps::schedule_task([&](AsyncOps::WorkerId) { high_priority_ran++; }, 0, AsyncOps::schedule_task([&](AsyncOps::WorkerId) { high_priority_ran++; }, 0, &schedule, AsyncOps::Priority::High);
&schedule, AsyncOps::Priority::High);
AsyncOps::schedule_task([&](AsyncOps::WorkerId) { normal_priority_ran++; }, 0, AsyncOps::schedule_task([&](AsyncOps::WorkerId) { normal_priority_ran++; }, 0, &schedule, AsyncOps::Priority::Normal);
&schedule, AsyncOps::Priority::Normal);
AsyncOps::wait_for_schedule_completion(&schedule); AsyncOps::wait_for_schedule_completion(&schedule);
@ -109,14 +116,16 @@ auto test_priorities() -> bool {
return true; return true;
} }
auto test_run_task_fire_and_forget() -> bool { auto test_run_task_fire_and_forget() -> bool
{
SchedulerGuard guard(2); SchedulerGuard guard(2);
std::atomic<bool> executed{false}; std::atomic<bool> executed{false};
AsyncOps::run_task([&]() { executed = true; }); AsyncOps::run_task([&]() { executed = true; });
for (int i = 0; i < 100; ++i) { for (int i = 0; i < 100; ++i)
{
if (executed.load()) if (executed.load())
break; break;
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
@ -127,7 +136,8 @@ auto test_run_task_fire_and_forget() -> bool {
return true; return true;
} }
auto test_cancellation_safety() -> bool { auto test_cancellation_safety() -> bool
{
SchedulerGuard guard(2); SchedulerGuard guard(2);
AsyncOps::cancel_tasks_of_tag(999); AsyncOps::cancel_tasks_of_tag(999);
@ -135,8 +145,7 @@ auto test_cancellation_safety() -> bool {
AsyncOps::Schedule schedule; AsyncOps::Schedule schedule;
std::atomic<i32> counter{0}; std::atomic<i32> counter{0};
AsyncOps::schedule_task([&](AsyncOps::WorkerId) { counter++; }, 10, AsyncOps::schedule_task([&](AsyncOps::WorkerId) { counter++; }, 10, &schedule);
&schedule);
AsyncOps::wait_for_schedule_completion(&schedule); AsyncOps::wait_for_schedule_completion(&schedule);
IAT_CHECK_EQ(counter.load(), 1); IAT_CHECK_EQ(counter.load(), 1);

View File

@ -20,7 +20,8 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, CLI) IAT_BEGIN_BLOCK(Core, CLI)
auto test_basic_traversal() -> bool { auto test_basic_traversal() -> bool
{
const Vec<String> args = {"ignored", "one", "two", "three"}; const Vec<String> args = {"ignored", "one", "two", "three"};
CLIParser parser(args); CLIParser parser(args);
@ -41,7 +42,8 @@ auto test_basic_traversal() -> bool {
return true; return true;
} }
auto test_peek() -> bool { auto test_peek() -> bool
{
const Vec<String> args = {"ignored", "peek_val", "next_val"}; const Vec<String> args = {"ignored", "peek_val", "next_val"};
CLIParser parser(args); CLIParser parser(args);
@ -58,7 +60,8 @@ auto test_peek() -> bool {
return true; return true;
} }
auto test_consume() -> bool { auto test_consume() -> bool
{
const Vec<String> args = {"ignored", "-v", "--output", "file.txt"}; const Vec<String> args = {"ignored", "-v", "--output", "file.txt"};
CLIParser parser(args); CLIParser parser(args);
@ -79,7 +82,8 @@ auto test_consume() -> bool {
return true; return true;
} }
auto test_empty() -> bool { auto test_empty() -> bool
{
const Vec<String> args = {}; const Vec<String> args = {};
CLIParser parser(args); CLIParser parser(args);

View File

@ -24,7 +24,11 @@ set(TEST_SOURCES
add_executable(IACore_Test_Suite ${TEST_SOURCES}) add_executable(IACore_Test_Suite ${TEST_SOURCES})
target_compile_options(IACore_Test_Suite PRIVATE -fexceptions) if(MSVC)
target_compile_options(IACore_Test_Suite PRIVATE "/EHsc")
else()
target_compile_options(IACore_Test_Suite PRIVATE "-fexceptions")
endif()
set_target_properties(IACore_Test_Suite PROPERTIES USE_EXCEPTIONS ON) set_target_properties(IACore_Test_Suite PROPERTIES USE_EXCEPTIONS ON)
target_link_libraries(IACore_Test_Suite PRIVATE IACore) target_link_libraries(IACore_Test_Suite PRIVATE IACore)

View File

@ -20,7 +20,8 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, DataOps) IAT_BEGIN_BLOCK(Core, DataOps)
auto test_crc32() -> bool { auto test_crc32() -> bool
{
{ {
const String s = "123456789"; const String s = "123456789";
const Span<const u8> span(reinterpret_cast<const u8 *>(s.data()), s.size()); const Span<const u8> span(reinterpret_cast<const u8 *>(s.data()), s.size());
@ -36,20 +37,20 @@ auto test_crc32() -> bool {
{ {
Vec<u8> buffer(33); Vec<u8> buffer(33);
for (usize i = 1; i < 33; ++i) { for (usize i = 1; i < 33; ++i)
{
buffer[i] = static_cast<u8>(i); buffer[i] = static_cast<u8>(i);
} }
Vec<u8> ref_data(32); Vec<u8> ref_data(32);
for (usize i = 0; i < 32; ++i) { for (usize i = 0; i < 32; ++i)
{
ref_data[i] = static_cast<u8>(i + 1); ref_data[i] = static_cast<u8>(i + 1);
} }
const u32 hash_ref = const u32 hash_ref = DataOps::crc32(Span<const u8>(ref_data.data(), ref_data.size()));
DataOps::crc32(Span<const u8>(ref_data.data(), ref_data.size()));
const u32 hash_unaligned = const u32 hash_unaligned = DataOps::crc32(Span<const u8>(buffer.data() + 1, 32));
DataOps::crc32(Span<const u8>(buffer.data() + 1, 32));
IAT_CHECK_EQ(hash_ref, hash_unaligned); IAT_CHECK_EQ(hash_ref, hash_unaligned);
} }
@ -57,7 +58,8 @@ auto test_crc32() -> bool {
return true; return true;
} }
auto test_hash_xxhash() -> bool { auto test_hash_xxhash() -> bool
{
{ {
const String s = "123456789"; const String s = "123456789";
const u32 result = DataOps::hash_xxhash(s); const u32 result = DataOps::hash_xxhash(s);
@ -73,19 +75,18 @@ auto test_hash_xxhash() -> bool {
{ {
const String s = "Test"; const String s = "Test";
const u32 r1 = DataOps::hash_xxhash(s); const u32 r1 = DataOps::hash_xxhash(s);
const u32 r2 = DataOps::hash_xxhash( const u32 r2 = DataOps::hash_xxhash(Span<const u8>(reinterpret_cast<const u8 *>(s.data()), s.size()));
Span<const u8>(reinterpret_cast<const u8 *>(s.data()), s.size()));
IAT_CHECK_EQ(r1, r2); IAT_CHECK_EQ(r1, r2);
} }
return true; return true;
} }
auto test_hash_fnv1a() -> bool { auto test_hash_fnv1a() -> bool
{
{ {
const String s = "123456789"; const String s = "123456789";
const u32 result = DataOps::hash_fnv1a( const u32 result = DataOps::hash_fnv1a(Span<const u8>(reinterpret_cast<const u8 *>(s.data()), s.size()));
Span<const u8>(reinterpret_cast<const u8 *>(s.data()), s.size()));
IAT_CHECK_EQ(result, 0xbb86b11c); IAT_CHECK_EQ(result, 0xbb86b11c);
} }

View File

@ -23,9 +23,10 @@ static constexpr const char *TEST_VAL = "Hello World";
IAT_BEGIN_BLOCK(Core, Environment) IAT_BEGIN_BLOCK(Core, Environment)
auto test_basic_cycle() -> bool { auto test_basic_cycle() -> bool
{
(void)Environment::unset(TEST_KEY); (void) Environment::unset(TEST_KEY);
IAT_CHECK_NOT(Environment::exists(TEST_KEY)); IAT_CHECK_NOT(Environment::exists(TEST_KEY));
const auto set_res = Environment::set(TEST_KEY, TEST_VAL); const auto set_res = Environment::set(TEST_KEY, TEST_VAL);
@ -39,23 +40,25 @@ auto test_basic_cycle() -> bool {
const String val = Environment::get(TEST_KEY); const String val = Environment::get(TEST_KEY);
IAT_CHECK_EQ(val, String(TEST_VAL)); IAT_CHECK_EQ(val, String(TEST_VAL));
(void)Environment::unset(TEST_KEY); (void) Environment::unset(TEST_KEY);
return true; return true;
} }
auto test_overwrite() -> bool { auto test_overwrite() -> bool
(void)Environment::set(TEST_KEY, "ValueA"); {
(void) Environment::set(TEST_KEY, "ValueA");
IAT_CHECK_EQ(Environment::get(TEST_KEY), String("ValueA")); IAT_CHECK_EQ(Environment::get(TEST_KEY), String("ValueA"));
(void)Environment::set(TEST_KEY, "ValueB"); (void) Environment::set(TEST_KEY, "ValueB");
IAT_CHECK_EQ(Environment::get(TEST_KEY), String("ValueB")); IAT_CHECK_EQ(Environment::get(TEST_KEY), String("ValueB"));
(void)Environment::unset(TEST_KEY); (void) Environment::unset(TEST_KEY);
return true; return true;
} }
auto test_unset() -> bool { auto test_unset() -> bool
(void)Environment::set(TEST_KEY, "To Be Deleted"); {
(void) Environment::set(TEST_KEY, "To Be Deleted");
IAT_CHECK(Environment::exists(TEST_KEY)); IAT_CHECK(Environment::exists(TEST_KEY));
const auto unset_res = Environment::unset(TEST_KEY); const auto unset_res = Environment::unset(TEST_KEY);
@ -69,10 +72,11 @@ auto test_unset() -> bool {
return true; return true;
} }
auto test_defaults() -> bool { auto test_defaults() -> bool
{
const char *ghost_key = "IA_THIS_KEY_DOES_NOT_EXIST"; const char *ghost_key = "IA_THIS_KEY_DOES_NOT_EXIST";
(void)Environment::unset(ghost_key); (void) Environment::unset(ghost_key);
const String empty = Environment::get(ghost_key); const String empty = Environment::get(ghost_key);
IAT_CHECK(empty.empty()); IAT_CHECK(empty.empty());
@ -83,8 +87,9 @@ auto test_defaults() -> bool {
return true; return true;
} }
auto test_empty_value() -> bool { auto test_empty_value() -> bool
(void)Environment::set(TEST_KEY, ""); {
(void) Environment::set(TEST_KEY, "");
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
@ -96,13 +101,14 @@ auto test_empty_value() -> bool {
IAT_CHECK(opt->empty()); IAT_CHECK(opt->empty());
#endif #endif
(void)Environment::unset(TEST_KEY); (void) Environment::unset(TEST_KEY);
IAT_CHECK_NOT(Environment::exists(TEST_KEY)); IAT_CHECK_NOT(Environment::exists(TEST_KEY));
return true; return true;
} }
auto test_bad_input() -> bool { auto test_bad_input() -> bool
{
const auto res = Environment::set("", "Value"); const auto res = Environment::set("", "Value");
IAT_CHECK_NOT(res.has_value()); IAT_CHECK_NOT(res.has_value());

View File

@ -20,14 +20,17 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, FileOps) IAT_BEGIN_BLOCK(Core, FileOps)
void cleanup_file(const Path &path) { void cleanup_file(const Path &path)
{
std::error_code ec; std::error_code ec;
if (std::filesystem::exists(path, ec)) { if (std::filesystem::exists(path, ec))
{
std::filesystem::remove(path, ec); std::filesystem::remove(path, ec);
} }
} }
auto test_text_io() -> bool { auto test_text_io() -> bool
{
const Path path = "iatest_fileops_text.txt"; const Path path = "iatest_fileops_text.txt";
const String content = "Hello IACore FileOps!\nLine 2"; const String content = "Hello IACore FileOps!\nLine 2";
@ -43,7 +46,8 @@ auto test_text_io() -> bool {
return true; return true;
} }
auto test_binary_io() -> bool { auto test_binary_io() -> bool
{
const Path path = "iatest_fileops_bin.bin"; const Path path = "iatest_fileops_bin.bin";
const Vec<u8> content = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF}; const Vec<u8> content = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF};
@ -55,7 +59,8 @@ auto test_binary_io() -> bool {
IAT_CHECK(read_res.has_value()); IAT_CHECK(read_res.has_value());
IAT_CHECK_EQ(read_res->size(), content.size()); IAT_CHECK_EQ(read_res->size(), content.size());
for (usize i = 0; i < content.size(); ++i) { for (usize i = 0; i < content.size(); ++i)
{
IAT_CHECK_EQ((*read_res)[i], content[i]); IAT_CHECK_EQ((*read_res)[i], content[i]);
} }
@ -63,11 +68,12 @@ auto test_binary_io() -> bool {
return true; return true;
} }
auto test_file_mapping() -> bool { auto test_file_mapping() -> bool
{
const Path path = "iatest_fileops_map.txt"; const Path path = "iatest_fileops_map.txt";
const String content = "MappedContent"; const String content = "MappedContent";
(void)FileOps::write_text_file(path, content, true); (void) FileOps::write_text_file(path, content, true);
usize size = 0; usize size = 0;
const auto map_res = FileOps::map_file(path, size); const auto map_res = FileOps::map_file(path, size);
@ -86,7 +92,8 @@ auto test_file_mapping() -> bool {
return true; return true;
} }
auto test_shared_memory() -> bool { auto test_shared_memory() -> bool
{
const String shm_name = "iatest_shm_block"; const String shm_name = "iatest_shm_block";
const usize shm_size = 4096; const usize shm_size = 4096;
@ -112,7 +119,8 @@ auto test_shared_memory() -> bool {
return true; return true;
} }
auto test_stream_integration() -> bool { auto test_stream_integration() -> bool
{
const Path path = "iatest_fileops_stream.bin"; const Path path = "iatest_fileops_stream.bin";
cleanup_file(path); cleanup_file(path);
@ -121,8 +129,8 @@ auto test_stream_integration() -> bool {
IAT_CHECK(writer_res.has_value()); IAT_CHECK(writer_res.has_value());
auto &writer = *writer_res; auto &writer = *writer_res;
(void)writer.write<u32>(0x12345678); (void) writer.write<u32>(0x12345678);
(void)writer.write<u8>(0xFF); (void) writer.write<u8>(0xFF);
} }
{ {

View File

@ -21,27 +21,26 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, IPC) IAT_BEGIN_BLOCK(Core, IPC)
auto test_layout_constraints() -> bool { auto test_layout_constraints() -> bool
{
IAT_CHECK_EQ(alignof(IpcSharedMemoryLayout), static_cast<usize>(64)); IAT_CHECK_EQ(alignof(IpcSharedMemoryLayout), static_cast<usize>(64));
IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, meta), static_cast<usize>(0)); IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, meta), static_cast<usize>(0));
IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, moni_control), IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, moni_control), static_cast<usize>(64));
static_cast<usize>(64));
IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, mino_control), IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, mino_control), static_cast<usize>(192));
static_cast<usize>(192));
IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, moni_data_offset), IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, moni_data_offset), static_cast<usize>(320));
static_cast<usize>(320));
IAT_CHECK_EQ(sizeof(IpcSharedMemoryLayout) % 64, static_cast<usize>(0)); IAT_CHECK_EQ(sizeof(IpcSharedMemoryLayout) % 64, static_cast<usize>(0));
return true; return true;
} }
auto test_manual_shm_ringbuffer() -> bool { auto test_manual_shm_ringbuffer() -> bool
{
const String shm_name = "IA_TEST_IPC_LAYOUT_CHECK"; const String shm_name = "IA_TEST_IPC_LAYOUT_CHECK";
const usize shm_size = 16 * 1024; const usize shm_size = 16 * 1024;
@ -67,28 +66,20 @@ auto test_manual_shm_ringbuffer() -> bool {
layout->mino_data_offset = header_size + half_data; layout->mino_data_offset = header_size + half_data;
layout->mino_data_size = half_data; layout->mino_data_size = half_data;
Span<u8> moni_data_span(base_ptr + layout->moni_data_offset, Span<u8> moni_data_span(base_ptr + layout->moni_data_offset, static_cast<usize>(layout->moni_data_size));
static_cast<usize>(layout->moni_data_size)); auto moni_res = RingBufferView::create(&layout->moni_control, moni_data_span, true);
auto moni_res =
RingBufferView::create(&layout->moni_control, moni_data_span, true);
IAT_CHECK(moni_res.has_value()); IAT_CHECK(moni_res.has_value());
auto moni = std::move(*moni_res); auto moni = std::move(*moni_res);
Span<u8> mino_data_span(base_ptr + layout->mino_data_offset, Span<u8> mino_data_span(base_ptr + layout->mino_data_offset, static_cast<usize>(layout->mino_data_size));
static_cast<usize>(layout->mino_data_size)); auto mino_res = RingBufferView::create(&layout->mino_control, mino_data_span, true);
auto mino_res =
RingBufferView::create(&layout->mino_control, mino_data_span, true);
IAT_CHECK(mino_res.has_value()); IAT_CHECK(mino_res.has_value());
auto _ = std::move(*mino_res); auto _ = std::move(*mino_res);
String msg = "IPC_TEST_MESSAGE"; String msg = "IPC_TEST_MESSAGE";
IAT_CHECK( IAT_CHECK(moni.push(100, Span<const u8>(reinterpret_cast<const u8 *>(msg.data()), msg.size())).has_value());
moni.push(100, Span<const u8>(reinterpret_cast<const u8 *>(msg.data()),
msg.size()))
.has_value());
auto moni_reader_res = auto moni_reader_res = RingBufferView::create(&layout->moni_control, moni_data_span, false);
RingBufferView::create(&layout->moni_control, moni_data_span, false);
IAT_CHECK(moni_reader_res.has_value()); IAT_CHECK(moni_reader_res.has_value());
auto moni_reader = std::move(*moni_reader_res); auto moni_reader = std::move(*moni_reader_res);
@ -99,7 +90,7 @@ auto test_manual_shm_ringbuffer() -> bool {
IAT_CHECK(pop_res->has_value()); IAT_CHECK(pop_res->has_value());
IAT_CHECK_EQ(header.id, static_cast<u16>(100)); IAT_CHECK_EQ(header.id, static_cast<u16>(100));
String received((char *)buffer, *pop_res.value()); String received((char *) buffer, *pop_res.value());
IAT_CHECK_EQ(received, msg); IAT_CHECK_EQ(received, msg);
FileOps::unmap_file(base_ptr); FileOps::unmap_file(base_ptr);
@ -108,13 +99,20 @@ auto test_manual_shm_ringbuffer() -> bool {
return true; return true;
} }
class TestManager : public IpcManager { class TestManager : public IpcManager
public: {
void on_signal(NativeProcessID, u8) override {} public:
void on_packet(NativeProcessID, u16, Span<const u8>) override {} void on_signal(NativeProcessID, u8) override
{
}
void on_packet(NativeProcessID, u16, Span<const u8>) override
{
}
}; };
auto test_manager_instantiation() -> bool { auto test_manager_instantiation() -> bool
{
TestManager mgr; TestManager mgr;
return true; return true;
} }

View File

@ -18,21 +18,23 @@
using namespace IACore; using namespace IACore;
struct UserProfile { struct UserProfile
{
String username; String username;
u32 id; u32 id;
bool is_active; bool is_active;
Vec<String> roles; Vec<String> roles;
bool operator==(const UserProfile &other) const { bool operator==(const UserProfile &other) const
return username == other.username && id == other.id && {
is_active == other.is_active && roles == other.roles; return username == other.username && id == other.id && is_active == other.is_active && roles == other.roles;
} }
}; };
IAT_BEGIN_BLOCK(Core, JSON) IAT_BEGIN_BLOCK(Core, JSON)
auto test_dynamic_parse() -> bool { auto test_dynamic_parse() -> bool
{
const String json_text = R"({ const String json_text = R"({
"string": "Hello World", "string": "Hello World",
"int": 42, "int": 42,
@ -69,7 +71,8 @@ auto test_dynamic_parse() -> bool {
return true; return true;
} }
auto test_dynamic_encode() -> bool { auto test_dynamic_encode() -> bool
{
nlohmann::json j; nlohmann::json j;
j["name"] = "IACore"; j["name"] = "IACore";
j["version"] = 2; j["version"] = 2;
@ -83,18 +86,17 @@ auto test_dynamic_encode() -> bool {
return true; return true;
} }
auto test_parse_invalid() -> bool { auto test_parse_invalid() -> bool
{
const String bad_json = "{ key: value }"; const String bad_json = "{ key: value }";
auto res = Json::parse(bad_json); auto res = Json::parse(bad_json);
IAT_CHECK_NOT(res.has_value()); IAT_CHECK_NOT(res.has_value());
return true; return true;
} }
auto test_struct_round_trip() -> bool { auto test_struct_round_trip() -> bool
UserProfile original{.username = "test_user", {
.id = 12345, UserProfile original{.username = "test_user", .id = 12345, .is_active = true, .roles = {"admin", "editor"}};
.is_active = true,
.roles = {"admin", "editor"}};
auto encode_res = Json::encode_struct(original); auto encode_res = Json::encode_struct(original);
IAT_CHECK(encode_res.has_value()); IAT_CHECK(encode_res.has_value());
@ -112,14 +114,16 @@ auto test_struct_round_trip() -> bool {
return true; return true;
} }
auto test_struct_parse_error() -> bool { auto test_struct_parse_error() -> bool
{
const String malformed = "{ broken_json: "; const String malformed = "{ broken_json: ";
auto res = Json::parse_to_struct<UserProfile>(malformed); auto res = Json::parse_to_struct<UserProfile>(malformed);
IAT_CHECK_NOT(res.has_value()); IAT_CHECK_NOT(res.has_value());
return true; return true;
} }
auto test_read_only() -> bool { auto test_read_only() -> bool
{
const String json_text = R"({ const String json_text = R"({
"id": 999, "id": 999,
"name": "Simd", "name": "Simd",

View File

@ -23,7 +23,17 @@ IAT_BEGIN_BLOCK(Core, Logger)
static constexpr const char *LOG_FILE = "iacore_test_log.txt"; static constexpr const char *LOG_FILE = "iacore_test_log.txt";
auto test_file_logging() -> bool { void cleanup_file(const Path &path)
{
std::error_code ec;
if (std::filesystem::exists(path, ec))
{
std::filesystem::remove(path, ec);
}
}
auto test_file_logging() -> bool
{
const auto res = Logger::enable_logging_to_disk(LOG_FILE); const auto res = Logger::enable_logging_to_disk(LOG_FILE);
IAT_CHECK(res.has_value()); IAT_CHECK(res.has_value());
@ -41,9 +51,10 @@ auto test_file_logging() -> bool {
Logger::flush_logs(); Logger::flush_logs();
auto read_res = FileOps::read_text_file(LOG_FILE); auto read_res = FileOps::read_text_file(LOG_FILE);
if (!read_res) { if (!read_res)
std::cout << console::YELLOW << " Warning: Could not read log file (" {
<< read_res.error() << "). Skipping verification.\n" std::cout << console::YELLOW << " Warning: Could not read log file (" << read_res.error()
<< "). Skipping verification.\n"
<< console::RESET; << console::RESET;
return true; return true;
} }
@ -58,10 +69,13 @@ auto test_file_logging() -> bool {
IAT_CHECK(content.find("ERROR") != String::npos); IAT_CHECK(content.find("ERROR") != String::npos);
IAT_CHECK(content.find("WARN") != String::npos); IAT_CHECK(content.find("WARN") != String::npos);
cleanup_file(LOG_FILE);
return true; return true;
} }
auto test_log_levels() -> bool { auto test_log_levels() -> bool
{
Logger::set_log_level(Logger::LogLevel::Warn); Logger::set_log_level(Logger::LogLevel::Warn);
@ -74,7 +88,8 @@ auto test_log_levels() -> bool {
Logger::flush_logs(); Logger::flush_logs();
auto read_res = FileOps::read_text_file(LOG_FILE); auto read_res = FileOps::read_text_file(LOG_FILE);
if (!read_res) { if (!read_res)
{
return true; return true;
} }
@ -87,7 +102,8 @@ auto test_log_levels() -> bool {
return true; return true;
} }
auto test_formatting() -> bool { auto test_formatting() -> bool
{
Logger::set_log_level(Logger::LogLevel::Info); Logger::set_log_level(Logger::LogLevel::Info);
const String name = "IACore"; const String name = "IACore";
@ -97,7 +113,8 @@ auto test_formatting() -> bool {
Logger::flush_logs(); Logger::flush_logs();
auto read_res = FileOps::read_text_file(LOG_FILE); auto read_res = FileOps::read_text_file(LOG_FILE);
if (!read_res) { if (!read_res)
{
return true; return true;
} }

View File

@ -20,18 +20,15 @@
using namespace IACore; using namespace IACore;
IACORE_MAIN() { IACORE_MAIN()
(void)args; {
(void) args;
AU_TRY_PURE(SocketOps::initialize()); AU_TRY_PURE(SocketOps::initialize());
std::cout std::cout << console::GREEN << "\n===============================================================\n";
<< console::GREEN
<< "\n===============================================================\n";
std::cout << " IACore (Independent Architecture Core) - Unit Test Suite\n"; std::cout << " IACore (Independent Architecture Core) - Unit Test Suite\n";
std::cout std::cout << "===============================================================\n" << console::RESET << "\n";
<< "===============================================================\n"
<< console::RESET << "\n";
const i32 result = Test::TestRegistry::run_all(); const i32 result = Test::TestRegistry::run_all();

View File

@ -21,7 +21,8 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, Platform) IAT_BEGIN_BLOCK(Core, Platform)
auto test_os_name() -> bool { auto test_os_name() -> bool
{
const char *os_name = Platform::get_operating_system_name(); const char *os_name = Platform::get_operating_system_name();
IAT_CHECK(os_name != nullptr); IAT_CHECK(os_name != nullptr);
@ -41,7 +42,8 @@ auto test_os_name() -> bool {
return true; return true;
} }
auto test_arch_name() -> bool { auto test_arch_name() -> bool
{
const char *arch_name = Platform::get_architecture_name(); const char *arch_name = Platform::get_architecture_name();
IAT_CHECK(arch_name != nullptr); IAT_CHECK(arch_name != nullptr);
@ -59,7 +61,8 @@ auto test_arch_name() -> bool {
return true; return true;
} }
auto test_capabilities() -> bool { auto test_capabilities() -> bool
{
const bool check_result = Platform::check_cpu(); const bool check_result = Platform::check_cpu();
IAT_CHECK(check_result); IAT_CHECK(check_result);
@ -67,13 +70,14 @@ auto test_capabilities() -> bool {
const auto &caps = Platform::get_capabilities(); const auto &caps = Platform::get_capabilities();
volatile bool has_crc = caps.hardware_crc32; volatile bool has_crc = caps.hardware_crc32;
(void)has_crc; (void) has_crc;
return true; return true;
} }
#if IA_ARCH_X64 #if IA_ARCH_X64
auto test_cpuid() -> bool { auto test_cpuid() -> bool
{
i32 regs[4] = {0}; i32 regs[4] = {0};
Platform::cpuid(0, 0, regs); Platform::cpuid(0, 0, regs);
@ -91,12 +95,11 @@ auto test_cpuid() -> bool {
const String vendor_str(vendor); const String vendor_str(vendor);
IAT_CHECK(!vendor_str.empty()); IAT_CHECK(!vendor_str.empty());
bool is_known = bool is_known = (vendor_str == "GenuineIntel" || vendor_str == "AuthenticAMD" || vendor_str == "KVMKVMKVM" ||
(vendor_str == "GenuineIntel" || vendor_str == "AuthenticAMD" || vendor_str == "Microsoft Hv" || vendor_str == "VBoxVBoxVBox");
vendor_str == "KVMKVMKVM" || vendor_str == "Microsoft Hv" ||
vendor_str == "VBoxVBoxVBox");
if (!is_known) { if (!is_known)
{
std::cout << " [Info] Unknown CPU Vendor: " << vendor_str << "\n"; std::cout << " [Info] Unknown CPU Vendor: " << vendor_str << "\n";
} }

View File

@ -19,23 +19,23 @@
using namespace IACore; using namespace IACore;
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
#define CMD_ECHO_EXE "cmd.exe" # define CMD_ECHO_EXE "cmd.exe"
#define CMD_ARG_PREFIX "/c echo" # define CMD_ARG_PREFIX "/c echo"
#define NULL_DEVICE "NUL" # define NULL_DEVICE "NUL"
#else #else
#define CMD_ECHO_EXE "/bin/echo" # define CMD_ECHO_EXE "/bin/echo"
#define CMD_ARG_PREFIX "" # define CMD_ARG_PREFIX ""
#define NULL_DEVICE "/dev/null" # define NULL_DEVICE "/dev/null"
#endif #endif
IAT_BEGIN_BLOCK(Core, ProcessOps) IAT_BEGIN_BLOCK(Core, ProcessOps)
auto test_basic_run() -> bool { auto test_basic_run() -> bool
{
String captured; String captured;
const auto result = const auto result = ProcessOps::spawn_process_sync(CMD_ECHO_EXE, CMD_ARG_PREFIX " HelloIA",
ProcessOps::spawn_process_sync(CMD_ECHO_EXE, CMD_ARG_PREFIX " HelloIA",
[&](StringView line) { captured = line; }); [&](StringView line) { captured = line; });
IAT_CHECK(result.has_value()); IAT_CHECK(result.has_value());
@ -46,18 +46,18 @@ auto test_basic_run() -> bool {
return true; return true;
} }
auto test_arguments() -> bool { auto test_arguments() -> bool
{
Vec<String> lines; Vec<String> lines;
String args = String(CMD_ARG_PREFIX) + " one two"; String args = String(CMD_ARG_PREFIX) + " one two";
if (!args.empty() && args[0] == ' ') { if (!args.empty() && args[0] == ' ')
{
args.erase(0, 1); args.erase(0, 1);
} }
const auto result = const auto result =
ProcessOps::spawn_process_sync(CMD_ECHO_EXE, args, [&](StringView line) { ProcessOps::spawn_process_sync(CMD_ECHO_EXE, args, [&](StringView line) { lines.push_back(String(line)); });
lines.push_back(String(line));
});
IAT_CHECK_EQ(*result, 0); IAT_CHECK_EQ(*result, 0);
IAT_CHECK(lines.size() > 0); IAT_CHECK(lines.size() > 0);
@ -67,7 +67,8 @@ auto test_arguments() -> bool {
return true; return true;
} }
auto test_exit_codes() -> bool { auto test_exit_codes() -> bool
{
String cmd; String cmd;
String arg; String arg;
@ -80,8 +81,7 @@ auto test_exit_codes() -> bool {
arg = "-c \"exit 42\""; arg = "-c \"exit 42\"";
#endif #endif
const auto result = const auto result = ProcessOps::spawn_process_sync(cmd, arg, [](StringView) {});
ProcessOps::spawn_process_sync(cmd, arg, [](StringView) {});
IAT_CHECK(result.has_value()); IAT_CHECK(result.has_value());
IAT_CHECK_EQ(*result, 42); IAT_CHECK_EQ(*result, 42);
@ -89,10 +89,10 @@ auto test_exit_codes() -> bool {
return true; return true;
} }
auto test_missing_exe() -> bool { auto test_missing_exe() -> bool
{
const auto result = const auto result = ProcessOps::spawn_process_sync("sdflkjghsdflkjg", "", [](StringView) {});
ProcessOps::spawn_process_sync("sdflkjghsdflkjg", "", [](StringView) {});
#if IA_PLATFORM_WINDOWS #if IA_PLATFORM_WINDOWS
IAT_CHECK_NOT(result.has_value()); IAT_CHECK_NOT(result.has_value());
@ -105,11 +105,13 @@ auto test_missing_exe() -> bool {
return true; return true;
} }
auto test_large_output() -> bool { auto test_large_output() -> bool
{
String massive_string; String massive_string;
massive_string.reserve(5000); massive_string.reserve(5000);
for (i32 i = 0; i < 500; ++i) { for (i32 i = 0; i < 500; ++i)
{
massive_string += "1234567890"; massive_string += "1234567890";
} }
@ -126,8 +128,7 @@ auto test_large_output() -> bool {
#endif #endif
String captured; String captured;
const auto result = ProcessOps::spawn_process_sync( const auto result = ProcessOps::spawn_process_sync(cmd, arg, [&](StringView line) { captured += line; });
cmd, arg, [&](StringView line) { captured += line; });
IAT_CHECK(result.has_value()); IAT_CHECK(result.has_value());
IAT_CHECK_EQ(*result, 0); IAT_CHECK_EQ(*result, 0);
@ -137,7 +138,8 @@ auto test_large_output() -> bool {
return true; return true;
} }
auto test_multi_line() -> bool { auto test_multi_line() -> bool
{
String cmd; String cmd;
String arg; String arg;
@ -153,13 +155,14 @@ auto test_multi_line() -> bool {
bool found_a = false; bool found_a = false;
bool found_b = false; bool found_b = false;
const auto res = const auto res = ProcessOps::spawn_process_sync(cmd, arg, [&](StringView line) {
ProcessOps::spawn_process_sync(cmd, arg, [&](StringView line) {
line_count++; line_count++;
if (line.find("LineA") != String::npos) { if (line.find("LineA") != String::npos)
{
found_a = true; found_a = true;
} }
if (line.find("LineB") != String::npos) { if (line.find("LineB") != String::npos)
{
found_b = true; found_b = true;
} }
}); });
@ -173,10 +176,10 @@ auto test_multi_line() -> bool {
return true; return true;
} }
auto test_complex_arguments() -> bool { auto test_complex_arguments() -> bool
{
const String complex_args = const String complex_args = "-DDEFINED_MSG=\\\"Hello World\\\" -v path/to/file";
"-DDEFINED_MSG=\\\"Hello World\\\" -v path/to/file";
const String cmd = CMD_ECHO_EXE; const String cmd = CMD_ECHO_EXE;
@ -188,8 +191,7 @@ auto test_complex_arguments() -> bool {
#endif #endif
String captured; String captured;
const auto result = ProcessOps::spawn_process_sync( const auto result = ProcessOps::spawn_process_sync(cmd, final_args, [&](StringView line) { captured += line; });
cmd, final_args, [&](StringView line) { captured += line; });
IAT_CHECK(result.has_value()); IAT_CHECK(result.has_value());
IAT_CHECK_EQ(*result, 0); IAT_CHECK_EQ(*result, 0);

View File

@ -20,7 +20,8 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, RingBuffer) IAT_BEGIN_BLOCK(Core, RingBuffer)
auto test_push_pop() -> bool { auto test_push_pop() -> bool
{
Vec<u8> memory(sizeof(RingBufferView::ControlBlock) + 1024); Vec<u8> memory(sizeof(RingBufferView::ControlBlock) + 1024);
@ -33,8 +34,7 @@ auto test_push_pop() -> bool {
auto consumer = std::move(*consumer_res); auto consumer = std::move(*consumer_res);
String msg = "Hello RingBuffer"; String msg = "Hello RingBuffer";
const auto push_res = producer.push( const auto push_res = producer.push(1, Span<const u8>(reinterpret_cast<const u8 *>(msg.data()), msg.size()));
1, Span<const u8>(reinterpret_cast<const u8 *>(msg.data()), msg.size()));
IAT_CHECK(push_res.has_value()); IAT_CHECK(push_res.has_value());
RingBufferView::PacketHeader header; RingBufferView::PacketHeader header;
@ -57,7 +57,8 @@ auto test_push_pop() -> bool {
return true; return true;
} }
auto test_wrap_around() -> bool { auto test_wrap_around() -> bool
{
Vec<u8> memory(sizeof(RingBufferView::ControlBlock) + 100); Vec<u8> memory(sizeof(RingBufferView::ControlBlock) + 100);
@ -87,8 +88,10 @@ auto test_wrap_around() -> bool {
IAT_CHECK_EQ(pop_size, static_cast<usize>(40)); IAT_CHECK_EQ(pop_size, static_cast<usize>(40));
bool match = true; bool match = true;
for (usize i = 0; i < 40; i++) { for (usize i = 0; i < 40; i++)
if (out_buf[i] != 0xAA) { {
if (out_buf[i] != 0xAA)
{
match = false; match = false;
} }
} }

View File

@ -20,7 +20,8 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, FloatVec4) IAT_BEGIN_BLOCK(Core, FloatVec4)
auto test_float_arithmetic() -> bool { auto test_float_arithmetic() -> bool
{
FloatVec4 v1(10.0f, 20.0f, 30.0f, 40.0f); FloatVec4 v1(10.0f, 20.0f, 30.0f, 40.0f);
FloatVec4 v2(2.0f, 4.0f, 5.0f, 8.0f); FloatVec4 v2(2.0f, 4.0f, 5.0f, 8.0f);
@ -39,7 +40,8 @@ auto test_float_arithmetic() -> bool {
return true; return true;
} }
auto test_math_helpers() -> bool { auto test_math_helpers() -> bool
{
alignas(16) f32 res[4]; alignas(16) f32 res[4];
FloatVec4 v_sq(4.0f, 9.0f, 16.0f, 25.0f); FloatVec4 v_sq(4.0f, 9.0f, 16.0f, 25.0f);
@ -61,7 +63,8 @@ auto test_math_helpers() -> bool {
return true; return true;
} }
auto test_approx_math() -> bool { auto test_approx_math() -> bool
{
alignas(16) f32 res[4]; alignas(16) f32 res[4];
FloatVec4 v(16.0f, 25.0f, 100.0f, 1.0f); FloatVec4 v(16.0f, 25.0f, 100.0f, 1.0f);
@ -73,7 +76,8 @@ auto test_approx_math() -> bool {
return true; return true;
} }
auto test_linear_algebra() -> bool { auto test_linear_algebra() -> bool
{
FloatVec4 v1(1.0f, 2.0f, 3.0f, 4.0f); FloatVec4 v1(1.0f, 2.0f, 3.0f, 4.0f);
FloatVec4 v2(1.0f, 0.0f, 1.0f, 0.0f); FloatVec4 v2(1.0f, 0.0f, 1.0f, 0.0f);

View File

@ -20,7 +20,8 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, IntVec4) IAT_BEGIN_BLOCK(Core, IntVec4)
auto test_constructors() -> bool { auto test_constructors() -> bool
{
IntVec4 v_broadcast(10); IntVec4 v_broadcast(10);
alignas(16) u32 store_buf[4]; alignas(16) u32 store_buf[4];
v_broadcast.store(store_buf); v_broadcast.store(store_buf);
@ -41,7 +42,8 @@ auto test_constructors() -> bool {
return true; return true;
} }
auto test_arithmetic() -> bool { auto test_arithmetic() -> bool
{
const IntVec4 v1(10, 20, 30, 40); const IntVec4 v1(10, 20, 30, 40);
const IntVec4 v2(1, 2, 3, 4); const IntVec4 v2(1, 2, 3, 4);
@ -64,9 +66,10 @@ auto test_arithmetic() -> bool {
return true; return true;
} }
auto test_bitwise() -> bool { auto test_bitwise() -> bool
{
const IntVec4 v_all_ones(0xFFFFFFFF); const IntVec4 v_all_ones(0xFFFFFFFF);
const IntVec4 v_zero((u32)0); const IntVec4 v_zero((u32) 0);
const IntVec4 v_pattern(0xAAAAAAAA); const IntVec4 v_pattern(0xAAAAAAAA);
alignas(16) u32 res[4]; alignas(16) u32 res[4];
@ -94,7 +97,8 @@ auto test_bitwise() -> bool {
return true; return true;
} }
auto test_saturation() -> bool { auto test_saturation() -> bool
{
const u32 max = 0xFFFFFFFF; const u32 max = 0xFFFFFFFF;
const IntVec4 v_high(max - 10); const IntVec4 v_high(max - 10);
const IntVec4 v_add(20); const IntVec4 v_add(20);
@ -112,7 +116,8 @@ auto test_saturation() -> bool {
return true; return true;
} }
auto test_advanced_ops() -> bool { auto test_advanced_ops() -> bool
{
const IntVec4 v(0, 50, 100, 150); const IntVec4 v(0, 50, 100, 150);
alignas(16) u32 res[4]; alignas(16) u32 res[4];

View File

@ -20,7 +20,8 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, SocketOps) IAT_BEGIN_BLOCK(Core, SocketOps)
auto test_initialization() -> bool { auto test_initialization() -> bool
{
IAT_CHECK(SocketOps::is_initialized()); IAT_CHECK(SocketOps::is_initialized());
const auto res = SocketOps::initialize(); const auto res = SocketOps::initialize();
@ -33,17 +34,19 @@ auto test_initialization() -> bool {
return true; return true;
} }
auto test_port_availability() -> bool { auto test_port_availability() -> bool
{
const u16 port = 54321; const u16 port = 54321;
(void)SocketOps::is_port_available_tcp(port); (void) SocketOps::is_port_available_tcp(port);
(void)SocketOps::is_port_available_udp(port); (void) SocketOps::is_port_available_udp(port);
return true; return true;
} }
auto test_unix_socket_lifecycle() -> bool { auto test_unix_socket_lifecycle() -> bool
{
const String socket_path = "iatest_ipc.sock"; const String socket_path = "iatest_ipc.sock";
SocketOps::unlink_file(socket_path.c_str()); SocketOps::unlink_file(socket_path.c_str());
@ -53,7 +56,8 @@ auto test_unix_socket_lifecycle() -> bool {
SocketHandle server = *server_res; SocketHandle server = *server_res;
auto bind_res = SocketOps::bind_unix_socket(server, socket_path.c_str()); auto bind_res = SocketOps::bind_unix_socket(server, socket_path.c_str());
if (!bind_res) { if (!bind_res)
{
SocketOps::close(server); SocketOps::close(server);
return false; return false;
@ -66,8 +70,7 @@ auto test_unix_socket_lifecycle() -> bool {
IAT_CHECK(client_res.has_value()); IAT_CHECK(client_res.has_value());
SocketHandle client = *client_res; SocketHandle client = *client_res;
auto connect_res = auto connect_res = SocketOps::connect_unix_socket(client, socket_path.c_str());
SocketOps::connect_unix_socket(client, socket_path.c_str());
IAT_CHECK(connect_res.has_value()); IAT_CHECK(connect_res.has_value());
SocketOps::close(client); SocketOps::close(client);
@ -77,7 +80,8 @@ auto test_unix_socket_lifecycle() -> bool {
return true; return true;
} }
auto test_unix_socket_errors() -> bool { auto test_unix_socket_errors() -> bool
{
const String socket_path = "iatest_missing.sock"; const String socket_path = "iatest_missing.sock";
SocketOps::unlink_file(socket_path.c_str()); SocketOps::unlink_file(socket_path.c_str());
@ -86,8 +90,7 @@ auto test_unix_socket_errors() -> bool {
IAT_CHECK(client_res.has_value()); IAT_CHECK(client_res.has_value());
SocketHandle client = *client_res; SocketHandle client = *client_res;
auto connect_res = auto connect_res = SocketOps::connect_unix_socket(client, socket_path.c_str());
SocketOps::connect_unix_socket(client, socket_path.c_str());
IAT_CHECK_NOT(connect_res.has_value()); IAT_CHECK_NOT(connect_res.has_value());
SocketOps::close(client); SocketOps::close(client);

View File

@ -20,7 +20,8 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, StreamReader) IAT_BEGIN_BLOCK(Core, StreamReader)
auto test_read_uint8() -> bool { auto test_read_uint8() -> bool
{
u8 data[] = {0xAA, 0xBB, 0xCC}; u8 data[] = {0xAA, 0xBB, 0xCC};
StreamReader reader(data); StreamReader reader(data);
@ -36,7 +37,8 @@ auto test_read_uint8() -> bool {
return true; return true;
} }
auto test_read_multi_byte() -> bool { auto test_read_multi_byte() -> bool
{
u8 data[] = {0x01, 0x02, 0x03, 0x04}; u8 data[] = {0x01, 0x02, 0x03, 0x04};
StreamReader reader(data); StreamReader reader(data);
@ -52,7 +54,8 @@ auto test_read_multi_byte() -> bool {
return true; return true;
} }
auto test_read_float() -> bool { auto test_read_float() -> bool
{
const f32 pi = 3.14159f; const f32 pi = 3.14159f;
u8 data[4]; u8 data[4];
@ -67,7 +70,8 @@ auto test_read_float() -> bool {
return true; return true;
} }
auto test_read_buffer() -> bool { auto test_read_buffer() -> bool
{
u8 src[] = {1, 2, 3, 4, 5}; u8 src[] = {1, 2, 3, 4, 5};
u8 dst[3] = {0}; u8 dst[3] = {0};
StreamReader reader(src); StreamReader reader(src);
@ -84,7 +88,8 @@ auto test_read_buffer() -> bool {
return true; return true;
} }
auto test_navigation() -> bool { auto test_navigation() -> bool
{
u8 data[10] = {0}; u8 data[10] = {0};
StreamReader reader(data); StreamReader reader(data);
@ -106,11 +111,12 @@ auto test_navigation() -> bool {
return true; return true;
} }
auto test_boundary_checks() -> bool { auto test_boundary_checks() -> bool
{
u8 data[] = {0x00, 0x00}; u8 data[] = {0x00, 0x00};
StreamReader reader(data); StreamReader reader(data);
(void)reader.read<u16>(); (void) reader.read<u16>();
IAT_CHECK(reader.is_eof()); IAT_CHECK(reader.is_eof());
auto val = reader.read<u8>(); auto val = reader.read<u8>();

View File

@ -21,7 +21,8 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, StreamWriter) IAT_BEGIN_BLOCK(Core, StreamWriter)
auto test_memory_writer() -> bool { auto test_memory_writer() -> bool
{
StreamWriter writer; StreamWriter writer;
IAT_CHECK(writer.write(static_cast<u8>(0xAA), 1).has_value()); IAT_CHECK(writer.write(static_cast<u8>(0xAA), 1).has_value());
@ -39,7 +40,8 @@ auto test_memory_writer() -> bool {
return true; return true;
} }
auto test_fixed_buffer() -> bool { auto test_fixed_buffer() -> bool
{
u8 buffer[4] = {0}; u8 buffer[4] = {0};
StreamWriter writer(Span<u8>(buffer, 4)); StreamWriter writer(Span<u8>(buffer, 4));
@ -60,10 +62,12 @@ auto test_fixed_buffer() -> bool {
return true; return true;
} }
auto test_file_writer() -> bool { auto test_file_writer() -> bool
{
const Path path = "test_stream_writer.bin"; const Path path = "test_stream_writer.bin";
if (std::filesystem::exists(path)) { if (std::filesystem::exists(path))
{
std::filesystem::remove(path); std::filesystem::remove(path);
} }
@ -81,8 +85,7 @@ auto test_file_writer() -> bool {
auto read_res = FileOps::read_binary_file(path); auto read_res = FileOps::read_binary_file(path);
IAT_CHECK(read_res.has_value()); IAT_CHECK(read_res.has_value());
const String read_str(reinterpret_cast<const char *>(read_res->data()), const String read_str(reinterpret_cast<const char *>(read_res->data()), read_res->size());
read_res->size());
IAT_CHECK_EQ(read_str, String("Hello World")); IAT_CHECK_EQ(read_str, String("Hello World"));
std::filesystem::remove(path); std::filesystem::remove(path);
@ -90,7 +93,8 @@ auto test_file_writer() -> bool {
return true; return true;
} }
auto test_primitives() -> bool { auto test_primitives() -> bool
{
StreamWriter writer; StreamWriter writer;
const f32 f = 1.5f; const f32 f = 1.5f;

View File

@ -20,7 +20,8 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, StringOps) IAT_BEGIN_BLOCK(Core, StringOps)
auto test_base64_encode() -> bool { auto test_base64_encode() -> bool
{
{ {
const String s = "Hello World"; const String s = "Hello World";
@ -58,13 +59,13 @@ auto test_base64_encode() -> bool {
return true; return true;
} }
auto test_base64_decode() -> bool { auto test_base64_decode() -> bool
{
{ {
const String encoded = "SGVsbG8gV29ybGQ="; const String encoded = "SGVsbG8gV29ybGQ=";
const Vec<u8> decoded = StringOps::decode_base64(encoded); const Vec<u8> decoded = StringOps::decode_base64(encoded);
const String result(reinterpret_cast<const char *>(decoded.data()), const String result(reinterpret_cast<const char *>(decoded.data()), decoded.size());
decoded.size());
IAT_CHECK_EQ(result, String("Hello World")); IAT_CHECK_EQ(result, String("Hello World"));
} }
@ -76,10 +77,12 @@ auto test_base64_decode() -> bool {
return true; return true;
} }
auto test_base64_round_trip() -> bool { auto test_base64_round_trip() -> bool
{
Vec<u8> original; Vec<u8> original;
original.reserve(256); original.reserve(256);
for (usize i = 0; i < 256; ++i) { for (usize i = 0; i < 256; ++i)
{
original.push_back(static_cast<u8>(i)); original.push_back(static_cast<u8>(i));
} }
@ -89,8 +92,10 @@ auto test_base64_round_trip() -> bool {
IAT_CHECK_EQ(original.size(), decoded.size()); IAT_CHECK_EQ(original.size(), decoded.size());
bool match = true; bool match = true;
for (usize i = 0; i < original.size(); ++i) { for (usize i = 0; i < original.size(); ++i)
if (original[i] != decoded[i]) { {
if (original[i] != decoded[i])
{
match = false; match = false;
break; break;
} }

View File

@ -18,10 +18,12 @@
using namespace IACore; using namespace IACore;
struct TestVec3 { struct TestVec3
{
f32 x, y, z; f32 x, y, z;
bool operator==(const TestVec3 &other) const { bool operator==(const TestVec3 &other) const
{
return x == other.x && y == other.y && z == other.z; return x == other.x && y == other.y && z == other.z;
} }
}; };
@ -30,7 +32,8 @@ IA_MAKE_HASHABLE(TestVec3, &TestVec3::x, &TestVec3::y, &TestVec3::z);
IAT_BEGIN_BLOCK(Core, Utils) IAT_BEGIN_BLOCK(Core, Utils)
auto test_hex_conversion() -> bool { auto test_hex_conversion() -> bool
{
u8 bin[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF}; u8 bin[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF};
String hex = Utils::binary_to_hex_string(bin); String hex = Utils::binary_to_hex_string(bin);
@ -57,7 +60,8 @@ auto test_hex_conversion() -> bool {
return true; return true;
} }
auto test_hex_errors() -> bool { auto test_hex_errors() -> bool
{
auto odd = Utils::hex_string_to_binary("ABC"); auto odd = Utils::hex_string_to_binary("ABC");
IAT_CHECK_NOT(odd.has_value()); IAT_CHECK_NOT(odd.has_value());
@ -72,7 +76,8 @@ auto test_hex_errors() -> bool {
return true; return true;
} }
auto test_sort() -> bool { auto test_sort() -> bool
{
Vec<i32> nums = {5, 1, 4, 2, 3}; Vec<i32> nums = {5, 1, 4, 2, 3};
Utils::sort(nums); Utils::sort(nums);
@ -86,7 +91,8 @@ auto test_sort() -> bool {
return true; return true;
} }
auto test_binary_search() -> bool { auto test_binary_search() -> bool
{
Vec<i32> nums = {10, 20, 20, 20, 30}; Vec<i32> nums = {10, 20, 20, 20, 30};
@ -106,7 +112,8 @@ auto test_binary_search() -> bool {
return true; return true;
} }
auto test_hash_basics() -> bool { auto test_hash_basics() -> bool
{
u64 h1 = Utils::compute_hash(10, 20.5f, "Hello"); u64 h1 = Utils::compute_hash(10, 20.5f, "Hello");
u64 h2 = Utils::compute_hash(10, 20.5f, "Hello"); u64 h2 = Utils::compute_hash(10, 20.5f, "Hello");
u64 h3 = Utils::compute_hash(10, 20.5f, "World"); u64 h3 = Utils::compute_hash(10, 20.5f, "World");
@ -122,7 +129,8 @@ auto test_hash_basics() -> bool {
return true; return true;
} }
auto test_hash_macro() -> bool { auto test_hash_macro() -> bool
{
TestVec3 v1{1.0f, 2.0f, 3.0f}; TestVec3 v1{1.0f, 2.0f, 3.0f};
TestVec3 v2{1.0f, 2.0f, 3.0f}; TestVec3 v2{1.0f, 2.0f, 3.0f};
TestVec3 v3{1.0f, 2.0f, 4.0f}; TestVec3 v3{1.0f, 2.0f, 4.0f};

View File

@ -21,7 +21,8 @@ using namespace IACore;
IAT_BEGIN_BLOCK(Core, XML) IAT_BEGIN_BLOCK(Core, XML)
auto test_parse_string() -> bool { auto test_parse_string() -> bool
{
const String xml_content = R"( const String xml_content = R"(
<root> <root>
<item id="1">Value1</item> <item id="1">Value1</item>
@ -47,14 +48,16 @@ auto test_parse_string() -> bool {
return true; return true;
} }
auto test_parse_error() -> bool { auto test_parse_error() -> bool
{
const String invalid_xml = "<root><unclosed>"; const String invalid_xml = "<root><unclosed>";
auto res = XML::parse_from_string(invalid_xml); auto res = XML::parse_from_string(invalid_xml);
IAT_CHECK_NOT(res.has_value()); IAT_CHECK_NOT(res.has_value());
return true; return true;
} }
auto test_serialize() -> bool { auto test_serialize() -> bool
{
const String xml_content = "<root><node>Text</node></root>"; const String xml_content = "<root><node>Text</node></root>";
auto res = XML::parse_from_string(xml_content); auto res = XML::parse_from_string(xml_content);
IAT_CHECK(res.has_value()); IAT_CHECK(res.has_value());
@ -67,7 +70,8 @@ auto test_serialize() -> bool {
return true; return true;
} }
auto test_escape() -> bool { auto test_escape() -> bool
{
const String raw = "< & > \" '"; const String raw = "< & > \" '";
const String escaped = XML::escape_xml_string(raw); const String escaped = XML::escape_xml_string(raw);
@ -80,7 +84,8 @@ auto test_escape() -> bool {
return true; return true;
} }
auto test_file_io() -> bool { auto test_file_io() -> bool
{
const Path path = "test_temp_xml_doc.xml"; const Path path = "test_temp_xml_doc.xml";
const String content = "<config><ver>1.0</ver></config>"; const String content = "<config><ver>1.0</ver></config>";
@ -91,8 +96,7 @@ auto test_file_io() -> bool {
IAT_CHECK(parse_res.has_value()); IAT_CHECK(parse_res.has_value());
auto &doc = *parse_res; auto &doc = *parse_res;
IAT_CHECK_EQ(String(doc.child("config").child("ver").child_value()), IAT_CHECK_EQ(String(doc.child("config").child("ver").child_value()), String("1.0"));
String("1.0"));
std::filesystem::remove(path); std::filesystem::remove(path);