IACore v1.2

This commit is contained in:
2026-01-25 22:55:24 +05:30
commit 49340eaa7b
95 changed files with 10182 additions and 0 deletions

2
Src/CMakeLists.txt Normal file
View File

@ -0,0 +1,2 @@
add_subdirectory(IACore/)

97
Src/IACore/CMakeLists.txt Normal file
View File

@ -0,0 +1,97 @@
set(SRC_FILES
"imp/cpp/CLI.cpp"
"imp/cpp/IPC.cpp"
"imp/cpp/XML.cpp"
"imp/cpp/SIMD.cpp"
"imp/cpp/JSON.cpp"
"imp/cpp/IACore.cpp"
"imp/cpp/Logger.cpp"
"imp/cpp/Utils.cpp"
"imp/cpp/FileOps.cpp"
"imp/cpp/DataOps.cpp"
"imp/cpp/AsyncOps.cpp"
"imp/cpp/Platform.cpp"
"imp/cpp/SocketOps.cpp"
"imp/cpp/StringOps.cpp"
"imp/cpp/ProcessOps.cpp"
"imp/cpp/StreamReader.cpp"
"imp/cpp/StreamWriter.cpp"
"imp/cpp/Http/Common.cpp"
"imp/cpp/Http/Client.cpp"
"imp/cpp/Http/Server.cpp"
)
add_library(IACore STATIC ${SRC_FILES})
target_include_directories(IACore PUBLIC inc/)
target_include_directories(IACore PRIVATE imp/hpp/)
target_link_libraries(IACore PUBLIC
hwy
zlib
Oxide
zstd::libzstd
glaze::glaze
httplib::httplib
pugixml::pugixml
simdjson::simdjson
nlohmann_json::nlohmann_json
unordered_dense::unordered_dense
)
target_link_libraries(IACore PRIVATE
OpenSSL::SSL
OpenSSL::Crypto
)
target_link_libraries(IACore PUBLIC mimalloc-static)
if(WIN32)
if(MSVC)
target_link_options(IACore PUBLIC "/INCLUDE:mi_version")
else()
target_link_options(IACore PUBLIC "")
endif()
endif()
target_precompile_headers(IACore PUBLIC inc/IACore/PCH.hpp)
set(NO_EXCEPT_FLAG "$<IF:$<CXX_COMPILER_ID:MSVC>,/EHs-c-,-fno-exceptions>")
target_compile_options(IACore PRIVATE ${NO_EXCEPT_FLAG})
target_compile_options(IACore INTERFACE
$<$<NOT:$<BOOL:$<TARGET_PROPERTY:USE_EXCEPTIONS>>>:${NO_EXCEPT_FLAG}>
)
define_property(TARGET PROPERTY USE_EXCEPTIONS
BRIEF_DOCS "If ON, this target is allowed to use C++ exceptions."
FULL_DOCS "Prevents IACore from propagating -fno-exceptions to this target."
)
target_compile_definitions(IACore PRIVATE
CPPHTTPLIB_OPENSSL_SUPPORT
CPPHTTPLIB_ZLIB_SUPPORT
NOMINMAX
)
target_compile_definitions(IACore PUBLIC
$<$<CONFIG:Debug>:__IA_DEBUG=1>
$<$<CONFIG:Release>:__IA_DEBUG=0>
)
if(IACORE_ARCH_X64)
if(MSVC)
target_compile_options(IACore INTERFACE /arch:AVX2)
else()
target_compile_options(IACore INTERFACE -mavx2 -mfma -mpclmul -maes -mbmi -mbmi2 -mf16c)
endif()
target_compile_definitions(IACore INTERFACE HWY_BASELINE_TARGETS=HWY_AVX2)
elseif(IACORE_ARCH_ARM64)
if(NOT MSVC)
target_compile_options(IACore INTERFACE -march=armv8-a+simd)
endif()
target_compile_definitions(IACore INTERFACE HWY_BASELINE_TARGETS=HWY_NEON)
elseif(IACORE_ARCH_WASM)
target_compile_options(IACore INTERFACE -msimd128)
target_compile_definitions(IACore INTERFACE HWY_BASELINE_TARGETS=HWY_WASM)
endif()

View File

@ -0,0 +1,200 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/AsyncOps.hpp>
namespace IACore {
Mut<std::mutex> AsyncOps::s_queue_mutex;
Mut<std::condition_variable> AsyncOps::s_wake_condition;
Mut<Vec<std::jthread>> AsyncOps::s_schedule_workers;
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 {
std::jthread(std::move(task)).detach();
}
auto AsyncOps::initialize_scheduler(Mut<u8> worker_count) -> Result<void> {
if (worker_count == 0) {
Const<u32> hw_concurrency = std::thread::hardware_concurrency();
Mut<u32> threads = 2;
if (hw_concurrency > 2) {
threads = hw_concurrency - 2;
}
if (threads > 255) {
threads = 255;
}
worker_count = static_cast<u8>(threads);
}
for (Mut<u32> i = 0; i < worker_count; ++i) {
s_schedule_workers.emplace_back(schedule_worker_loop,
static_cast<WorkerId>(i + 1));
}
return {};
}
auto AsyncOps::terminate_scheduler() -> void {
for (MutRef<std::jthread> worker : s_schedule_workers) {
worker.request_stop();
}
s_wake_condition.notify_all();
for (MutRef<std::jthread> worker : s_schedule_workers) {
if (worker.joinable()) {
worker.join();
}
}
s_schedule_workers.clear();
}
auto AsyncOps::schedule_task(Mut<std::function<void(WorkerId worker_id)>> task,
Const<TaskTag> tag, Const<Schedule *> schedule,
Const<Priority> priority) -> void {
ensure(!s_schedule_workers.empty(),
"Scheduler must be initialized before calling schedule_task");
schedule->counter.fetch_add(1);
{
Const<std::lock_guard<std::mutex>> lock(s_queue_mutex);
if (priority == Priority::High) {
s_high_priority_queue.emplace_back(
ScheduledTask{tag, schedule, std::move(task)});
} else {
s_normal_priority_queue.emplace_back(
ScheduledTask{tag, schedule, std::move(task)});
}
}
s_wake_condition.notify_one();
}
auto AsyncOps::cancel_tasks_of_tag(Const<TaskTag> tag) -> void {
Const<std::lock_guard<std::mutex>> lock(s_queue_mutex);
{
MutRef<std::deque<ScheduledTask>> queue = s_high_priority_queue;
for (Mut<std::deque<ScheduledTask>::iterator> it = queue.begin();
it != queue.end();
/* no incr */) {
if (it->tag == tag) {
if (it->schedule_handle->counter.fetch_sub(1) == 1) {
it->schedule_handle->counter.notify_all();
}
it = queue.erase(it);
} else {
++it;
}
}
}
{
MutRef<std::deque<ScheduledTask>> queue = s_normal_priority_queue;
for (Mut<std::deque<ScheduledTask>::iterator> it = queue.begin();
it != queue.end();
/* no incr */) {
if (it->tag == tag) {
if (it->schedule_handle->counter.fetch_sub(1) == 1) {
it->schedule_handle->counter.notify_all();
}
it = queue.erase(it);
} else {
++it;
}
}
}
}
auto AsyncOps::wait_for_schedule_completion(Const<Schedule *> schedule)
-> void {
ensure(!s_schedule_workers.empty(), "Scheduler must be initialized before "
"calling wait_for_schedule_completion");
while (schedule->counter.load() > 0) {
Mut<ScheduledTask> task;
Mut<bool> found_task = false;
{
Mut<std::unique_lock<std::mutex>> lock(s_queue_mutex);
if (!s_high_priority_queue.empty()) {
task = std::move(s_high_priority_queue.front());
s_high_priority_queue.pop_front();
found_task = true;
} else if (!s_normal_priority_queue.empty()) {
task = std::move(s_normal_priority_queue.front());
s_normal_priority_queue.pop_front();
found_task = true;
}
}
if (found_task) {
task.task(MAIN_THREAD_WORKER_ID);
if (task.schedule_handle->counter.fetch_sub(1) == 1) {
task.schedule_handle->counter.notify_all();
}
} else {
Const<u32> current_val = schedule->counter.load();
if (current_val > 0) {
schedule->counter.wait(current_val);
}
}
}
}
auto AsyncOps::get_worker_count() -> WorkerId {
return static_cast<WorkerId>(s_schedule_workers.size());
}
auto AsyncOps::schedule_worker_loop(Const<std::stop_token> stop_token,
Const<WorkerId> worker_id) -> void {
while (!stop_token.stop_requested()) {
Mut<ScheduledTask> task;
Mut<bool> found_task = false;
{
Mut<std::unique_lock<std::mutex>> lock(s_queue_mutex);
s_wake_condition.wait(lock, [&stop_token] {
return !s_high_priority_queue.empty() ||
!s_normal_priority_queue.empty() || stop_token.stop_requested();
});
if (stop_token.stop_requested() && s_high_priority_queue.empty() &&
s_normal_priority_queue.empty()) {
return;
}
if (!s_high_priority_queue.empty()) {
task = std::move(s_high_priority_queue.front());
s_high_priority_queue.pop_front();
found_task = true;
} else if (!s_normal_priority_queue.empty()) {
task = std::move(s_normal_priority_queue.front());
s_normal_priority_queue.pop_front();
found_task = true;
}
}
if (found_task) {
task.task(worker_id);
if (task.schedule_handle->counter.fetch_sub(1) == 1) {
task.schedule_handle->counter.notify_all();
}
}
}
}
} // namespace IACore

View File

@ -0,0 +1,27 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/CLI.hpp>
namespace IACore {
CLIParser::CLIParser(Const<Span<Const<String>>> args) : m_arg_list(args) {
m_current_arg = m_arg_list.begin();
// Skip executable path
if (m_current_arg != m_arg_list.end()) {
m_current_arg++;
}
}
} // namespace IACore

View File

@ -0,0 +1,452 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/DataOps.hpp>
#include <IACore/Platform.hpp>
#include <bit>
#include <cstring>
#include <zlib.h>
#include <zstd.h>
#if IA_ARCH_X64
#include <immintrin.h>
#endif
#if IA_ARCH_ARM64
#include <arm_acle.h>
#endif
namespace IACore {
template <typename T>
[[nodiscard]] inline auto read_unaligned(Const<Const<u8> *> ptr) -> T {
Mut<T> v;
std::memcpy(&v, ptr, sizeof(T));
return v;
}
struct Crc32Tables {
Mut<u32> table[8][256] = {};
consteval Crc32Tables() {
constexpr Const<u32> T = 0x82F63B78;
for (Mut<u32> i = 0; i < 256; i++) {
Mut<u32> crc = i;
for (Mut<i32> j = 0; j < 8; j++) {
crc = (crc >> 1) ^ ((crc & 1) ? T : 0);
}
table[0][i] = crc;
}
for (Mut<i32> i = 0; i < 256; i++) {
for (Mut<i32> slice = 1; slice < 8; slice++) {
Const<u32> prev = table[slice - 1][i];
table[slice][i] = (prev >> 8) ^ table[0][prev & 0xFF];
}
}
}
};
static constexpr Const<Crc32Tables> CRC32_TABLES{};
#if IA_ARCH_X64
inline auto crc32_x64_hw(Ref<Span<Const<u8>>> data) -> u32 {
Mut<const u8 *> p = data.data();
Mut<u32> crc = 0xFFFFFFFF;
Mut<usize> len = data.size();
while (len >= 8) {
Const<u64> chunk = read_unaligned<u64>(p);
crc = static_cast<u32>(_mm_crc32_u64(static_cast<u64>(crc), chunk));
p += 8;
len -= 8;
}
while (len--) {
crc = _mm_crc32_u8(crc, *p++);
}
return ~crc;
}
#endif
#if IA_ARCH_ARM64
__attribute__((target("+crc"))) inline auto
crc32_arm64_hw(Ref<Span<Const<u8>>> data) -> u32 {
Mut<const u8 *> p = data.data();
Mut<u32> crc = 0xFFFFFFFF;
Mut<usize> len = data.size();
while (len >= 8) {
Const<u64> chunk = read_unaligned<u64>(p);
crc = __crc32cd(crc, chunk);
p += 8;
len -= 8;
}
while (len--) {
crc = __crc32cb(crc, *p++);
}
return ~crc;
}
#endif
inline auto crc32_software_slice8(Ref<Span<Const<u8>>> data) -> u32 {
Mut<const u8 *> p = data.data();
Mut<u32> crc = 0xFFFFFFFF;
Mut<usize> len = data.size();
while (len >= 8) {
Const<u32> term1 = crc ^ read_unaligned<u32>(p);
Const<u32> term2 = read_unaligned<u32>(p + 4);
crc = CRC32_TABLES.table[7][term1 & 0xFF] ^
CRC32_TABLES.table[6][(term1 >> 8) & 0xFF] ^
CRC32_TABLES.table[5][(term1 >> 16) & 0xFF] ^
CRC32_TABLES.table[4][(term1 >> 24)] ^
CRC32_TABLES.table[3][term2 & 0xFF] ^
CRC32_TABLES.table[2][(term2 >> 8) & 0xFF] ^
CRC32_TABLES.table[1][(term2 >> 16) & 0xFF] ^
CRC32_TABLES.table[0][(term2 >> 24)];
p += 8;
len -= 8;
}
while (len--) {
crc = (crc >> 8) ^ CRC32_TABLES.table[0][(crc ^ *p++) & 0xFF];
}
return ~crc;
}
auto DataOps::crc32(Ref<Span<Const<u8>>> data) -> u32 {
#if IA_ARCH_X64
// IACore mandates AVX2 so no need to check
return crc32_x64_hw(data);
#elif IA_ARCH_ARM64
if (Platform::GetCapabilities().HardwareCRC32) {
return crc32_arm64_hw(data);
}
#endif
return crc32_software_slice8(data);
}
constexpr Const<u32> XXH_PRIME32_1 = 0x9E3779B1U;
constexpr Const<u32> XXH_PRIME32_2 = 0x85EBCA77U;
constexpr Const<u32> XXH_PRIME32_3 = 0xC2B2AE3DU;
constexpr Const<u32> XXH_PRIME32_4 = 0x27D4EB2FU;
constexpr Const<u32> XXH_PRIME32_5 = 0x165667B1U;
inline auto xxh32_round(Mut<u32> seed, Const<u32> input) -> u32 {
seed += input * XXH_PRIME32_2;
seed = std::rotl(seed, 13);
seed *= XXH_PRIME32_1;
return seed;
}
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()),
seed);
}
auto DataOps::hash_xxhash(Ref<Span<Const<u8>>> data, Const<u32> seed) -> u32 {
Mut<const u8 *> p = data.data();
Const<const u8 *> b_end = p + data.size();
Mut<u32> h32{};
if (data.size() >= 16) {
Const<const u8 *> limit = b_end - 16;
Mut<u32> v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2;
Mut<u32> v2 = seed + XXH_PRIME32_2;
Mut<u32> v3 = seed + 0;
Mut<u32> v4 = seed - XXH_PRIME32_1;
do {
v1 = xxh32_round(v1, read_unaligned<u32>(p));
p += 4;
v2 = xxh32_round(v2, read_unaligned<u32>(p));
p += 4;
v3 = xxh32_round(v3, read_unaligned<u32>(p));
p += 4;
v4 = xxh32_round(v4, read_unaligned<u32>(p));
p += 4;
} while (p <= limit);
h32 = std::rotl(v1, 1) + std::rotl(v2, 7) + std::rotl(v3, 12) +
std::rotl(v4, 18);
} else {
h32 = seed + XXH_PRIME32_5;
}
h32 += static_cast<u32>(data.size());
while (p + 4 <= b_end) {
Const<u32> t = read_unaligned<u32>(p) * XXH_PRIME32_3;
h32 += t;
h32 = std::rotl(h32, 17) * XXH_PRIME32_4;
p += 4;
}
while (p < b_end) {
h32 += (*p++) * XXH_PRIME32_5;
h32 = std::rotl(h32, 11) * XXH_PRIME32_1;
}
h32 ^= h32 >> 15;
h32 *= XXH_PRIME32_2;
h32 ^= h32 >> 13;
h32 *= XXH_PRIME32_3;
h32 ^= h32 >> 16;
return h32;
}
constexpr Const<u32> FNV1A_32_PRIME = 0x01000193;
constexpr Const<u32> FNV1A_32_OFFSET = 0x811c9dc5;
auto DataOps::hash_fnv1a(Ref<String> string) -> u32 {
Mut<u32> hash = FNV1A_32_OFFSET;
for (Const<char> c : string) {
hash ^= static_cast<u8>(c);
hash *= FNV1A_32_PRIME;
}
return hash;
}
auto DataOps::hash_fnv1a(Ref<Span<Const<u8>>> data) -> u32 {
Mut<u32> hash = FNV1A_32_OFFSET;
Const<const u8 *> ptr = data.data();
for (Mut<usize> i = 0; i < data.size(); ++i) {
hash ^= ptr[i];
hash *= FNV1A_32_PRIME;
}
return hash;
}
auto DataOps::detect_compression(Const<Span<Const<u8>>> data)
-> CompressionType {
if (data.size() < 2) {
return CompressionType::None;
}
if (data[0] == 0x1F && data[1] == 0x8B) {
return CompressionType::Gzip;
}
if (data[0] == 0x78 &&
(data[1] == 0x01 || data[1] == 0x9C || data[1] == 0xDA)) {
return CompressionType::Zlib;
}
return CompressionType::None;
}
auto DataOps::zlib_inflate(Ref<Span<Const<u8>>> data) -> Result<Vec<u8>> {
Mut<z_stream> zs{};
zs.zalloc = Z_NULL;
zs.zfree = Z_NULL;
zs.opaque = Z_NULL;
if (inflateInit2(&zs, 15 + 32) != Z_OK) {
return fail("Failed to initialize zlib inflate");
}
zs.next_in = const_cast<Bytef *>(data.data());
zs.avail_in = static_cast<uInt>(data.size());
Mut<Vec<u8>> out_buffer;
Const<usize> guess_size =
data.size() < 1024 ? data.size() * 4 : data.size() * 2;
out_buffer.resize(guess_size);
zs.next_out = reinterpret_cast<Bytef *>(out_buffer.data());
zs.avail_out = static_cast<uInt>(out_buffer.size());
Mut<int> ret;
do {
if (zs.avail_out == 0) {
Const<usize> current_pos = zs.total_out;
Const<usize> new_size = out_buffer.size() * 2;
out_buffer.resize(new_size);
zs.next_out = reinterpret_cast<Bytef *>(out_buffer.data() + current_pos);
zs.avail_out = static_cast<uInt>(new_size - current_pos);
}
ret = inflate(&zs, Z_NO_FLUSH);
} while (ret == Z_OK);
inflateEnd(&zs);
if (ret != Z_STREAM_END) {
return fail("Failed to inflate: corrupt data or stream error");
}
out_buffer.resize(zs.total_out);
return out_buffer;
}
auto DataOps::zlib_deflate(Ref<Span<Const<u8>>> data) -> Result<Vec<u8>> {
Mut<z_stream> zs{};
zs.zalloc = Z_NULL;
zs.zfree = Z_NULL;
zs.opaque = Z_NULL;
if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK) {
return fail("Failed to initialize zlib deflate");
}
zs.next_in = const_cast<Bytef *>(data.data());
zs.avail_in = static_cast<uInt>(data.size());
Mut<Vec<u8>> out_buffer;
out_buffer.resize(deflateBound(&zs, static_cast<uLong>(data.size())));
zs.next_out = reinterpret_cast<Bytef *>(out_buffer.data());
zs.avail_out = static_cast<uInt>(out_buffer.size());
Const<int> ret = deflate(&zs, Z_FINISH);
if (ret != Z_STREAM_END) {
deflateEnd(&zs);
return fail("Failed to deflate, ran out of buffer memory");
}
out_buffer.resize(zs.total_out);
deflateEnd(&zs);
return out_buffer;
}
auto DataOps::zstd_inflate(Ref<Span<Const<u8>>> data) -> Result<Vec<u8>> {
Const<unsigned long long> content_size =
ZSTD_getFrameContentSize(data.data(), data.size());
if (content_size == ZSTD_CONTENTSIZE_ERROR) {
return fail("Failed to inflate: Not valid ZSTD compressed data");
}
if (content_size != ZSTD_CONTENTSIZE_UNKNOWN) {
Mut<Vec<u8>> out_buffer;
out_buffer.resize(static_cast<usize>(content_size));
Const<usize> d_size = ZSTD_decompress(out_buffer.data(), out_buffer.size(),
data.data(), data.size());
if (ZSTD_isError(d_size)) {
return fail("Failed to inflate: {}", ZSTD_getErrorName(d_size));
}
return out_buffer;
}
Mut<ZSTD_DCtx *> dctx = ZSTD_createDCtx();
Mut<Vec<u8>> out_buffer;
out_buffer.resize(data.size() * 2);
Mut<ZSTD_inBuffer> input = {data.data(), data.size(), 0};
Mut<ZSTD_outBuffer> output = {out_buffer.data(), out_buffer.size(), 0};
Mut<usize> ret;
do {
ret = ZSTD_decompressStream(dctx, &output, &input);
if (ZSTD_isError(ret)) {
ZSTD_freeDCtx(dctx);
return fail("Failed to inflate: {}", ZSTD_getErrorName(ret));
}
if (output.pos == output.size) {
Const<usize> new_size = out_buffer.size() * 2;
out_buffer.resize(new_size);
output.dst = out_buffer.data();
output.size = new_size;
}
} while (ret != 0);
out_buffer.resize(output.pos);
ZSTD_freeDCtx(dctx);
return out_buffer;
}
auto DataOps::zstd_deflate(Ref<Span<Const<u8>>> data) -> Result<Vec<u8>> {
Const<usize> max_dst_size = ZSTD_compressBound(data.size());
Mut<Vec<u8>> out_buffer;
out_buffer.resize(max_dst_size);
Const<usize> compressed_size = ZSTD_compress(out_buffer.data(), max_dst_size,
data.data(), data.size(), 3);
if (ZSTD_isError(compressed_size)) {
return fail("Failed to deflate: {}", ZSTD_getErrorName(compressed_size));
}
out_buffer.resize(compressed_size);
return out_buffer;
}
auto DataOps::gzip_deflate(Ref<Span<Const<u8>>> data) -> Result<Vec<u8>> {
Mut<z_stream> zs{};
zs.zalloc = Z_NULL;
zs.zfree = Z_NULL;
zs.opaque = Z_NULL;
if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8,
Z_DEFAULT_STRATEGY) != Z_OK) {
return fail("Failed to initialize gzip deflate");
}
zs.next_in = const_cast<Bytef *>(data.data());
zs.avail_in = static_cast<uInt>(data.size());
Mut<Vec<u8>> out_buffer;
out_buffer.resize(deflateBound(&zs, static_cast<uLong>(data.size())) + 1024);
zs.next_out = reinterpret_cast<Bytef *>(out_buffer.data());
zs.avail_out = static_cast<uInt>(out_buffer.size());
Const<int> ret = deflate(&zs, Z_FINISH);
if (ret != Z_STREAM_END) {
deflateEnd(&zs);
return fail("Failed to deflate");
}
out_buffer.resize(zs.total_out);
deflateEnd(&zs);
return out_buffer;
}
auto DataOps::gzip_inflate(Ref<Span<Const<u8>>> data) -> Result<Vec<u8>> {
return zlib_inflate(data);
}
} // namespace IACore

View File

@ -0,0 +1,539 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/FileOps.hpp>
#include <cerrno>
#include <cstdio>
#if IA_PLATFORM_UNIX
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
namespace IACore {
Mut<HashMap<const u8 *, std::tuple<void *, void *, void *>>>
FileOps::s_mapped_files;
auto FileOps::unmap_file(Const<Const<u8> *> mapped_ptr) -> void {
if (!s_mapped_files.contains(mapped_ptr)) {
return;
}
Mut<decltype(s_mapped_files)::iterator> it = s_mapped_files.find(mapped_ptr);
Const<std::tuple<void *, void *, void *>> handles = it->second;
s_mapped_files.erase(it);
#if IA_PLATFORM_WINDOWS
::UnmapViewOfFile(std::get<1>(handles));
::CloseHandle(static_cast<HANDLE>(std::get<2>(handles)));
Const<HANDLE> handle = static_cast<HANDLE>(std::get<0>(handles));
if (handle != INVALID_HANDLE_VALUE) {
::CloseHandle(handle);
}
#elif IA_PLATFORM_UNIX
::munmap(std::get<1>(handles), (usize)std::get<2>(handles));
Const<i32> fd = (i32)((u64)std::get<0>(handles));
if (fd != -1) {
::close(fd);
}
#endif
}
auto FileOps::map_shared_memory(Ref<String> name, Const<usize> size,
Const<bool> is_owner) -> Result<u8 *> {
#if IA_PLATFORM_WINDOWS
Const<int> wchars_num =
MultiByteToWideChar(CP_UTF8, 0, name.c_str(), -1, NULL, 0);
Mut<std::wstring> w_name(wchars_num, 0);
MultiByteToWideChar(CP_UTF8, 0, name.c_str(), -1, &w_name[0], wchars_num);
Mut<HANDLE> h_map = NULL;
if (is_owner) {
h_map = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
(DWORD)(size >> 32), (DWORD)(size & 0xFFFFFFFF),
w_name.c_str());
} else {
h_map = OpenFileMappingW(FILE_MAP_ALL_ACCESS, false, w_name.c_str());
}
if (h_map == NULL) {
return fail("Failed to {} shared memory '{}'",
is_owner ? "owner" : "consumer", name);
}
Mut<u8 *> result =
static_cast<u8 *>(MapViewOfFile(h_map, FILE_MAP_ALL_ACCESS, 0, 0, size));
if (result == NULL) {
CloseHandle(h_map);
return fail("Failed to map view of shared memory '{}'", name);
}
s_mapped_files[result] = std::make_tuple((void *)INVALID_HANDLE_VALUE,
(void *)result, (void *)h_map);
return result;
#elif IA_PLATFORM_UNIX
Mut<int> fd = -1;
if (is_owner) {
fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd != -1) {
if (ftruncate(fd, size) == -1) {
close(fd);
shm_unlink(name.c_str());
return fail("Failed to truncate shared memory '{}'", name);
}
}
} else {
fd = shm_open(name.c_str(), O_RDWR, 0666);
}
if (fd == -1) {
return fail("Failed to {} shared memory '{}'",
is_owner ? "owner" : "consumer", name);
}
Mut<void *> addr =
mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
close(fd);
return fail("Failed to mmap shared memory '{}'", name);
}
Mut<u8 *> result = static_cast<u8 *>(addr);
s_mapped_files[result] =
std::make_tuple((void *)((u64)fd), (void *)addr, (void *)size);
return result;
#endif
}
auto FileOps::unlink_shared_memory(Ref<String> name) -> void {
if (name.empty()) {
return;
}
#if IA_PLATFORM_UNIX
shm_unlink(name.c_str());
#endif
}
auto FileOps::map_file(Ref<Path> path, MutRef<usize> size)
-> Result<const u8 *> {
#if IA_PLATFORM_WINDOWS
Const<HANDLE> handle = CreateFileA(
path.string().c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (handle == INVALID_HANDLE_VALUE) {
return fail("Failed to open {} for memory mapping", path.string());
}
Mut<LARGE_INTEGER> file_size;
if (!GetFileSizeEx(handle, &file_size)) {
CloseHandle(handle);
return fail("Failed to get size of {} for memory mapping", path.string());
}
size = static_cast<usize>(file_size.QuadPart);
if (size == 0) {
CloseHandle(handle);
return fail("Failed to get size of {} for memory mapping", path.string());
}
Mut<HANDLE> h_map =
CreateFileMappingW(handle, NULL, PAGE_READONLY, 0, 0, NULL);
if (h_map == NULL) {
CloseHandle(handle);
return fail("Failed to memory map {}", path.string());
}
Const<u8> *result =
static_cast<const u8 *>(MapViewOfFile(h_map, FILE_MAP_READ, 0, 0, 0));
if (result == NULL) {
CloseHandle(handle);
CloseHandle(h_map);
return fail("Failed to memory map {}", path.string());
}
s_mapped_files[result] = std::make_tuple(
(void *)handle, (void *)const_cast<u8 *>(result), (void *)h_map);
return result;
#elif IA_PLATFORM_UNIX
Const<int> handle = open(path.string().c_str(), O_RDONLY);
if (handle == -1) {
return fail("Failed to open {} for memory mapping", path.string());
}
Mut<struct stat> sb;
if (fstat(handle, &sb) == -1) {
close(handle);
return fail("Failed to get stats of {} for memory mapping", path.string());
}
size = static_cast<usize>(sb.st_size);
if (size == 0) {
close(handle);
return fail("Failed to get size of {} for memory mapping", path.string());
}
Mut<void *> addr = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, handle, 0);
if (addr == MAP_FAILED) {
close(handle);
return fail("Failed to memory map {}", path.string());
}
Const<Const<u8> *> result = static_cast<const u8 *>(addr);
madvise(addr, size, MADV_SEQUENTIAL);
s_mapped_files[result] =
std::make_tuple((void *)((u64)handle), (void *)addr, (void *)size);
return result;
#endif
}
auto FileOps::stream_to_file(Ref<Path> path, Const<bool> overwrite)
-> Result<StreamWriter> {
if (!overwrite && std::filesystem::exists(path)) {
return fail("File already exists: {}", path.string());
}
return StreamWriter::create_from_file(path);
}
auto FileOps::stream_from_file(Ref<Path> path) -> Result<StreamReader> {
if (!std::filesystem::exists(path)) {
return fail("File does not exist: {}", path.string());
}
return StreamReader::create_from_file(path);
}
auto FileOps::read_text_file(Ref<Path> path) -> Result<String> {
Mut<FILE *> f = fopen(path.string().c_str(), "r");
if (!f) {
return fail("Failed to open file: {}", path.string());
}
Mut<String> result;
fseek(f, 0, SEEK_END);
Const<long> len = ftell(f);
if (len > 0) {
result.resize(static_cast<usize>(len));
fseek(f, 0, SEEK_SET);
fread(result.data(), 1, result.size(), f);
}
fclose(f);
return result;
}
auto FileOps::read_binary_file(Ref<Path> path) -> Result<Vec<u8>> {
Mut<FILE *> f = fopen(path.string().c_str(), "rb");
if (!f) {
return fail("Failed to open file: {}", path.string());
}
Mut<Vec<u8>> result;
fseek(f, 0, SEEK_END);
Const<long> len = ftell(f);
if (len > 0) {
result.resize(static_cast<usize>(len));
fseek(f, 0, SEEK_SET);
fread(result.data(), 1, result.size(), f);
}
fclose(f);
return result;
}
auto FileOps::write_text_file(Ref<Path> path, Ref<String> contents,
Const<bool> overwrite) -> Result<usize> {
Const<Const<char> *> mode = overwrite ? "w" : "wx";
Mut<FILE *> f = fopen(path.string().c_str(), mode);
if (!f) {
if (!overwrite && errno == EEXIST) {
return fail("File already exists: {}", path.string());
}
return fail("Failed to write to file: {}", path.string());
}
Const<usize> result = fwrite(contents.data(), 1, contents.size(), f);
fclose(f);
return result;
}
auto FileOps::write_binary_file(Ref<Path> path, Const<Span<Const<u8>>> contents,
Const<bool> overwrite) -> Result<usize> {
Const<Const<char> *> mode = overwrite ? "w" : "wx";
Mut<FILE *> f = fopen(path.string().c_str(), mode);
if (!f) {
if (!overwrite && errno == EEXIST) {
return fail("File already exists: {}", path.string());
}
return fail("Failed to write to file: {}", path.string());
}
Const<usize> result = fwrite(contents.data(), 1, contents.size(), f);
fclose(f);
return result;
}
auto FileOps::normalize_executable_path(Ref<Path> path) -> Path {
Mut<Path> result = path;
#if IA_PLATFORM_WINDOWS
if (!result.has_extension()) {
result.replace_extension(".exe");
}
#elif IA_PLATFORM_UNIX
if (result.extension() == ".exe") {
result.replace_extension("");
}
if (result.is_relative()) {
Mut<String> path_str = result.string();
if (!path_str.starts_with("./") && !path_str.starts_with("../")) {
result = "./" + path_str;
}
}
#endif
return result;
}
auto FileOps::native_open_file(Ref<Path> path, Const<FileAccess> access,
Const<FileMode> mode, Const<u32> permissions)
-> Result<NativeFileHandle> {
#if IA_PLATFORM_WINDOWS
Mut<DWORD> dw_access = 0;
Mut<DWORD> dw_share = FILE_SHARE_READ;
Mut<DWORD> dw_disposition = 0;
Mut<DWORD> dw_flags_and_attributes = FILE_ATTRIBUTE_NORMAL;
switch (access) {
case FileAccess::Read:
dw_access = GENERIC_READ;
break;
case FileAccess::Write:
dw_access = GENERIC_WRITE;
break;
case FileAccess::ReadWrite:
dw_access = GENERIC_READ | GENERIC_WRITE;
break;
}
switch (mode) {
case FileMode::OpenExisting:
dw_disposition = OPEN_EXISTING;
break;
case FileMode::OpenAlways:
dw_disposition = OPEN_ALWAYS;
break;
case FileMode::CreateNew:
dw_disposition = CREATE_NEW;
break;
case FileMode::CreateAlways:
dw_disposition = CREATE_ALWAYS;
break;
case FileMode::TruncateExisting:
dw_disposition = TRUNCATE_EXISTING;
break;
}
Mut<HANDLE> h_file =
CreateFileA(path.string().c_str(), dw_access, dw_share, NULL,
dw_disposition, dw_flags_and_attributes, NULL);
if (h_file == INVALID_HANDLE_VALUE) {
return fail("Failed to open file '{}': {}", path.string(), GetLastError());
}
return h_file;
#elif IA_PLATFORM_UNIX
Mut<int> flags = 0;
switch (access) {
case FileAccess::Read:
flags = O_RDONLY;
break;
case FileAccess::Write:
flags = O_WRONLY;
break;
case FileAccess::ReadWrite:
flags = O_RDWR;
break;
}
switch (mode) {
case FileMode::OpenExisting:
break;
case FileMode::OpenAlways:
flags |= O_CREAT;
break;
case FileMode::CreateNew:
flags |= O_CREAT | O_EXCL;
break;
case FileMode::CreateAlways:
flags |= O_CREAT | O_TRUNC;
break;
case FileMode::TruncateExisting:
flags |= O_TRUNC;
break;
}
Mut<int> fd = open(path.string().c_str(), flags, permissions);
if (fd == -1) {
return fail("Failed to open file '{}': {}", path.string(), errno);
}
return fd;
#endif
}
auto FileOps::native_close_file(Const<NativeFileHandle> handle) -> void {
if (handle == INVALID_FILE_HANDLE) {
return;
}
#if IA_PLATFORM_WINDOWS
CloseHandle(handle);
#elif IA_PLATFORM_UNIX
close(handle);
#endif
}
FileOps::MemoryMappedRegion::~MemoryMappedRegion() { unmap(); }
FileOps::MemoryMappedRegion::MemoryMappedRegion(
ForwardRef<MemoryMappedRegion> other) noexcept {
*this = std::move(other);
}
auto FileOps::MemoryMappedRegion::operator=(
ForwardRef<MemoryMappedRegion> other) noexcept
-> MutRef<MemoryMappedRegion> {
if (this != &other) {
unmap();
m_ptr = other.m_ptr;
m_size = other.m_size;
#if IA_PLATFORM_WINDOWS
m_map_handle = other.m_map_handle;
other.m_map_handle = NULL;
#endif
other.m_ptr = nullptr;
other.m_size = 0;
}
return *this;
}
auto FileOps::MemoryMappedRegion::map(Const<NativeFileHandle> handle,
Const<u64> offset, Const<usize> size)
-> Result<void> {
unmap();
if (handle == INVALID_FILE_HANDLE) {
return fail("Invalid file handle provided to Map");
}
if (size == 0) {
return fail("Cannot map region of size 0");
}
#if IA_PLATFORM_WINDOWS
Mut<LARGE_INTEGER> file_size;
if (!GetFileSizeEx(handle, &file_size)) {
return fail("Failed to get file size");
}
Const<u64> end_offset = offset + size;
if (static_cast<u64>(file_size.QuadPart) < end_offset) {
Mut<LARGE_INTEGER> new_size;
new_size.QuadPart = static_cast<LONGLONG>(end_offset);
if (!SetFilePointerEx(handle, new_size, NULL, FILE_BEGIN)) {
return fail("Failed to seek to new end of file");
}
if (!SetEndOfFile(handle)) {
return fail("Failed to extend file for mapping");
}
}
m_map_handle = CreateFileMappingW(handle, NULL, PAGE_READWRITE, 0, 0, NULL);
if (m_map_handle == NULL) {
return fail("CreateFileMapping failed: {}", GetLastError());
}
Const<DWORD> offset_high = static_cast<DWORD>(offset >> 32);
Const<DWORD> offset_low = static_cast<DWORD>(offset & 0xFFFFFFFF);
m_ptr = static_cast<u8 *>(MapViewOfFile(m_map_handle, FILE_MAP_WRITE,
offset_high, offset_low, size));
if (m_ptr == NULL) {
CloseHandle(m_map_handle);
m_map_handle = NULL;
return fail("MapViewOfFile failed (Offset: {}, Size: {}): {}", offset, size,
GetLastError());
}
m_size = size;
#elif IA_PLATFORM_UNIX
Mut<struct stat> sb;
if (fstat(handle, &sb) == -1) {
return fail("Failed to fstat file");
}
Const<u64> end_offset = offset + size;
if (static_cast<u64>(sb.st_size) < end_offset) {
if (ftruncate(handle, static_cast<off_t>(end_offset)) == -1) {
return fail("Failed to ftruncate (extend) file");
}
}
Mut<void *> ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED,
handle, static_cast<off_t>(offset));
if (ptr == MAP_FAILED) {
return fail("mmap failed: {}", errno);
}
m_ptr = static_cast<u8 *>(ptr);
m_size = size;
madvise(m_ptr, m_size, MADV_SEQUENTIAL);
#endif
return {};
}
auto FileOps::MemoryMappedRegion::unmap() -> void {
if (!m_ptr) {
return;
}
#if IA_PLATFORM_WINDOWS
UnmapViewOfFile(m_ptr);
if (m_map_handle) {
CloseHandle(m_map_handle);
m_map_handle = NULL;
}
#elif IA_PLATFORM_UNIX
munmap(m_ptr, m_size);
#endif
m_ptr = nullptr;
m_size = 0;
}
auto FileOps::MemoryMappedRegion::flush() -> void {
if (!m_ptr) {
return;
}
#if IA_PLATFORM_WINDOWS
FlushViewOfFile(m_ptr, m_size);
#elif IA_PLATFORM_UNIX
msync(m_ptr, m_size, MS_SYNC);
#endif
}
} // namespace IACore

View File

@ -0,0 +1,150 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/DataOps.hpp>
#include <IACore/Http/Client.hpp>
namespace IACore {
auto HttpClient::create(Ref<String> host) -> Result<Box<HttpClient>> {
return make_box_protected<HttpClient>(httplib::Client(host));
}
static auto build_headers(Span<Const<HttpClient::Header>> headers,
Const<Const<char> *> default_content_type)
-> httplib::Headers {
Mut<httplib::Headers> out;
Mut<bool> has_content_type = false;
for (Ref<HttpClient::Header> h : headers) {
out.emplace(h.first, h.second);
if (h.first == HttpClient::header_type_to_string(
HttpClient::EHeaderType::CONTENT_TYPE)) {
has_content_type = true;
}
}
if (!has_content_type && default_content_type) {
out.emplace("Content-Type", default_content_type);
}
return out;
}
HttpClient::HttpClient(ForwardRef<httplib::Client> client)
: m_client(std::move(client)),
m_last_response_code(EResponseCode::INTERNAL_SERVER_ERROR) {
m_client.enable_server_certificate_verification(true);
}
HttpClient::~HttpClient() = default;
auto HttpClient::enable_certificate_verification() -> void {
m_client.enable_server_certificate_verification(true);
}
auto HttpClient::disable_certificate_verification() -> void {
m_client.enable_server_certificate_verification(false);
}
auto HttpClient::preprocess_response(Ref<String> response) -> String {
Const<Span<Const<u8>>> response_bytes = {
reinterpret_cast<Const<u8> *>(response.data()), response.size()};
Const<DataOps::CompressionType> compression =
DataOps::detect_compression(response_bytes);
switch (compression) {
case DataOps::CompressionType::Gzip: {
Const<Result<Vec<u8>>> data = DataOps::gzip_inflate(response_bytes);
if (!data) {
return response;
}
return String(reinterpret_cast<Const<char> *>(data->data()), data->size());
}
case DataOps::CompressionType::Zlib: {
Const<Result<Vec<u8>>> data = DataOps::zlib_inflate(response_bytes);
if (!data) {
return response;
}
return String(reinterpret_cast<Const<char> *>(data->data()), data->size());
}
case DataOps::CompressionType::None:
default:
break;
}
return response;
}
auto HttpClient::raw_get(Ref<String> path, Span<Const<Header>> headers,
Const<Const<char> *> default_content_type)
-> Result<String> {
Const<httplib::Headers> http_headers =
build_headers(headers, default_content_type);
Mut<String> adjusted_path = path;
if (!path.empty() && path[0] != '/') {
adjusted_path = "/" + path;
}
Const<httplib::Result> res =
m_client.Get(adjusted_path.c_str(), http_headers);
if (res) {
m_last_response_code = static_cast<EResponseCode>(res->status);
if (res->status >= 200 && res->status < 300) {
return preprocess_response(res->body);
}
return fail("HTTP Error {} : {}", res->status, res->body);
}
return fail("Network Error: {}", httplib::to_string(res.error()));
}
auto HttpClient::raw_post(Ref<String> path, Span<Const<Header>> headers,
Ref<String> body,
Const<Const<char> *> default_content_type)
-> Result<String> {
Mut<httplib::Headers> http_headers =
build_headers(headers, default_content_type);
Mut<String> content_type = default_content_type;
if (http_headers.count("Content-Type")) {
Const<httplib::Headers::iterator> t = http_headers.find("Content-Type");
content_type = t->second;
http_headers.erase(t);
}
m_client.set_keep_alive(true);
Mut<String> adjusted_path = path;
if (!path.empty() && path[0] != '/') {
adjusted_path = "/" + path;
}
Const<httplib::Result> res = m_client.Post(
adjusted_path.c_str(), http_headers, body, content_type.c_str());
if (res) {
m_last_response_code = static_cast<EResponseCode>(res->status);
if (res->status >= 200 && res->status < 300) {
return preprocess_response(res->body);
}
return fail("HTTP Error {} : {}", res->status, res->body);
}
return fail("Network Error: {}", httplib::to_string(res.error()));
}
} // namespace IACore

View File

@ -0,0 +1,117 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/Http/Common.hpp>
namespace IACore {
auto HttpCommon::url_encode(Ref<String> value) -> String {
Mut<std::stringstream> escaped;
escaped.fill('0');
escaped << std::hex << std::uppercase;
for (Const<char> c : value) {
if (std::isalnum(static_cast<unsigned char>(c)) || c == '-' || c == '_' ||
c == '.' || c == '~')
escaped << c;
else
escaped << '%' << std::setw(2)
<< static_cast<int>(static_cast<unsigned char>(c));
}
return escaped.str();
}
auto HttpCommon::url_decode(Ref<String> value) -> String {
Mut<String> result;
result.reserve(value.length());
for (Mut<size_t> i = 0; i < value.length(); ++i) {
if (value[i] == '%' && i + 2 < value.length()) {
Const<std::string> hex_str = value.substr(i + 1, 2);
Const<char> decoded_char =
static_cast<char>(std::strtol(hex_str.c_str(), nullptr, 16));
result += decoded_char;
i += 2;
} else if (value[i] == '+')
result += ' ';
else
result += value[i];
}
return result;
}
auto HttpCommon::header_type_to_string(Const<EHeaderType> type) -> String {
switch (type) {
case EHeaderType::ACCEPT:
return "Accept";
case EHeaderType::ACCEPT_CHARSET:
return "Accept-Charset";
case EHeaderType::ACCEPT_ENCODING:
return "Accept-Encoding";
case EHeaderType::ACCEPT_LANGUAGE:
return "Accept-Language";
case EHeaderType::AUTHORIZATION:
return "Authorization";
case EHeaderType::CACHE_CONTROL:
return "Cache-Control";
case EHeaderType::CONNECTION:
return "Connection";
case EHeaderType::CONTENT_LENGTH:
return "Content-Length";
case EHeaderType::CONTENT_TYPE:
return "Content-Type";
case EHeaderType::COOKIE:
return "Cookie";
case EHeaderType::DATE:
return "Date";
case EHeaderType::EXPECT:
return "Expect";
case EHeaderType::HOST:
return "Host";
case EHeaderType::IF_MATCH:
return "If-Match";
case EHeaderType::IF_MODIFIED_SINCE:
return "If-Modified-Since";
case EHeaderType::IF_NONE_MATCH:
return "If-None-Match";
case EHeaderType::ORIGIN:
return "Origin";
case EHeaderType::PRAGMA:
return "Pragma";
case EHeaderType::PROXY_AUTHORIZATION:
return "Proxy-Authorization";
case EHeaderType::RANGE:
return "Range";
case EHeaderType::REFERER:
return "Referer";
case EHeaderType::TE:
return "TE";
case EHeaderType::UPGRADE:
return "Upgrade";
case EHeaderType::USER_AGENT:
return "User-Agent";
case EHeaderType::VIA:
return "Via";
case EHeaderType::WARNING:
return "Warning";
}
return "<Unknown>";
}
auto HttpCommon::is_success_response_code(Const<EResponseCode> code) -> bool {
return (i32)code >= 200 && (i32)code < 300;
}
} // namespace IACore

View File

@ -0,0 +1,161 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/Http/Server.hpp>
namespace IACore {
auto HttpServer::Request::get_header(Ref<String> key) const -> String {
if (Const<HashMap<String, String>::const_iterator> it = headers.find(key);
it != headers.end()) {
return it->second;
}
return "";
}
auto HttpServer::Request::get_param(Ref<String> key) const -> String {
if (Const<HashMap<String, String>::const_iterator> it = params.find(key);
it != params.end()) {
return it->second;
}
return "";
}
auto HttpServer::Request::get_path_param(Ref<String> key) const -> String {
if (Const<HashMap<String, String>::const_iterator> it = path_params.find(key);
it != path_params.end()) {
return it->second;
}
return "";
}
auto HttpServer::Request::has_header(Ref<String> key) const -> bool {
return headers.contains(key);
}
auto HttpServer::Request::has_param(Ref<String> key) const -> bool {
return params.contains(key);
}
auto HttpServer::Request::has_path_param(Ref<String> key) const -> bool {
return path_params.contains(key);
}
void HttpServer::Response::set_content(Ref<String> content, Ref<String> type) {
body = content;
content_type = type;
}
void HttpServer::Response::set_status(Const<EResponseCode> status_code) {
code = status_code;
}
void HttpServer::Response::add_header(Ref<String> key, Ref<String> value) {
headers[key] = value;
}
struct PublicHttpServer : public HttpServer {
PublicHttpServer() = default;
};
HttpServer::HttpServer() = default;
HttpServer::~HttpServer() { stop(); }
auto HttpServer::create() -> Result<Box<HttpServer>> {
return make_box<PublicHttpServer>();
}
auto HttpServer::listen(Ref<String> host, Const<u32> port) -> Result<void> {
if (!m_server.listen(host.c_str(), static_cast<int>(port))) {
return fail("Failed to start HTTP server on {}:{}", host, port);
}
return {};
}
void HttpServer::stop() {
if (m_server.is_running()) {
m_server.stop();
}
}
auto HttpServer::is_running() const -> bool { return m_server.is_running(); }
void HttpServer::register_handler(Ref<String> method, Ref<String> pattern,
Const<Handler> handler) {
Const<httplib::Server::Handler> wrapper =
[handler](Ref<httplib::Request> req, MutRef<httplib::Response> res) {
Mut<Request> ia_req;
ia_req.path = req.path;
ia_req.method = req.method;
ia_req.body = req.body;
for (Ref<Pair<const String, String>> item : req.headers) {
ia_req.headers[item.first] = item.second;
}
for (Ref<Pair<const String, String>> item : req.params) {
ia_req.params[item.first] = item.second;
}
for (Ref<Pair<const String, String>> item : req.path_params) {
ia_req.path_params[item.first] = item.second;
}
Mut<Response> ia_res;
handler(ia_req, ia_res);
res.status = static_cast<int>(ia_res.code);
res.set_content(ia_res.body, ia_res.content_type.c_str());
for (Ref<Pair<String, String>> item : ia_res.headers) {
res.set_header(item.first.c_str(), item.second.c_str());
}
};
if (method == "GET") {
m_server.Get(pattern.c_str(), wrapper);
} else if (method == "POST") {
m_server.Post(pattern.c_str(), wrapper);
} else if (method == "PUT") {
m_server.Put(pattern.c_str(), wrapper);
} else if (method == "DELETE") {
m_server.Delete(pattern.c_str(), wrapper);
} else if (method == "OPTIONS") {
m_server.Options(pattern.c_str(), wrapper);
}
}
void HttpServer::get(Ref<String> pattern, Const<Handler> handler) {
register_handler("GET", pattern, handler);
}
void HttpServer::post(Ref<String> pattern, Const<Handler> handler) {
register_handler("POST", pattern, handler);
}
void HttpServer::put(Ref<String> pattern, Const<Handler> handler) {
register_handler("PUT", pattern, handler);
}
void HttpServer::del(Ref<String> pattern, Const<Handler> handler) {
register_handler("DELETE", pattern, handler);
}
void HttpServer::options(Ref<String> pattern, Const<Handler> handler) {
register_handler("OPTIONS", pattern, handler);
}
} // namespace IACore

View File

@ -0,0 +1,56 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/IACore.hpp>
#include <IACore/Logger.hpp>
#include <chrono>
#include <mimalloc.h>
namespace IACore {
Mut<std::chrono::high_resolution_clock::time_point> g_start_time = {};
static Mut<std::thread::id> g_main_thread_id = {};
static Mut<i32> g_core_init_count = 0;
auto initialize() -> void {
g_core_init_count++;
if (g_core_init_count > 1) {
return;
}
g_main_thread_id = std::this_thread::get_id();
g_start_time = std::chrono::high_resolution_clock::now();
Logger::initialize();
mi_option_set(mi_option_verbose, 0);
}
auto terminate() -> void {
g_core_init_count--;
if (g_core_init_count > 0) {
return;
}
Logger::terminate();
}
auto is_initialized() -> bool { return g_core_init_count > 0; }
auto is_main_thread() -> bool {
return std::this_thread::get_id() == g_main_thread_id;
}
} // namespace IACore

451
Src/IACore/imp/cpp/IPC.cpp Normal file
View File

@ -0,0 +1,451 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/IPC.hpp>
#include <IACore/FileOps.hpp>
#include <IACore/StringOps.hpp>
#include <charconv>
#include <fcntl.h>
namespace IACore {
struct IpcConnectionDescriptor {
Mut<String> socket_path;
Mut<String> shared_mem_path;
Mut<u32> shared_mem_size;
[[nodiscard]] auto serialize() const -> String {
return std::format("{}|{}|{}|", socket_path, shared_mem_path,
shared_mem_size);
}
static auto deserialize(Const<StringView> data)
-> Option<IpcConnectionDescriptor> {
enum class ParseState { SocketPath, SharedMemPath, SharedMemSize };
Mut<IpcConnectionDescriptor> result{};
Mut<usize> t = 0;
Mut<ParseState> state = ParseState::SocketPath;
for (Mut<usize> i = 0; i < data.size(); ++i) {
if (data[i] != '|') {
continue;
}
switch (state) {
case ParseState::SocketPath:
result.socket_path = String(data.substr(t, i - t));
state = ParseState::SharedMemPath;
break;
case ParseState::SharedMemPath:
result.shared_mem_path = String(data.substr(t, i - t));
state = ParseState::SharedMemSize;
break;
case ParseState::SharedMemSize: {
Const<const char *> start = data.data() + t;
Const<const char *> end = data.data() + i;
if (std::from_chars(start, end, result.shared_mem_size).ec !=
std::errc{}) {
return std::nullopt;
}
return result;
}
}
t = i + 1;
}
return std::nullopt;
}
};
IpcNode::~IpcNode() {
if (m_socket != INVALID_SOCKET) {
SocketOps::close(m_socket);
}
}
auto IpcNode::connect(Const<const char *> connection_string) -> Result<void> {
Const<Option<IpcConnectionDescriptor>> desc_opt =
IpcConnectionDescriptor::deserialize(connection_string);
if (!desc_opt) {
return fail("Failed to parse connection string");
}
Ref<IpcConnectionDescriptor> desc = *desc_opt;
m_shm_name = desc.shared_mem_path;
m_socket = OX_TRY(SocketOps::create_unix_socket());
OX_TRY_PURE(
SocketOps::connect_unix_socket(m_socket, desc.socket_path.c_str()));
Mut<u8 *> mapped_ptr = OX_TRY(FileOps::map_shared_memory(
desc.shared_mem_path, desc.shared_mem_size, false));
m_shared_memory = mapped_ptr;
Mut<IpcSharedMemoryLayout *> layout =
reinterpret_cast<IpcSharedMemoryLayout *>(m_shared_memory);
if (layout->meta.magic != 0x49414950) {
return fail("Invalid shared memory header signature");
}
if (layout->meta.version != 1) {
return fail("IPC version mismatch");
}
Mut<u8 *> moni_ptr = m_shared_memory + layout->moni_data_offset;
Mut<u8 *> mino_ptr = m_shared_memory + layout->mino_data_offset;
m_moni = OX_TRY(RingBufferView::create(
&layout->moni_control,
Span<u8>(moni_ptr, static_cast<usize>(layout->moni_data_size)), false));
m_mino = OX_TRY(RingBufferView::create(
&layout->mino_control,
Span<u8>(mino_ptr, static_cast<usize>(layout->mino_data_size)), false));
#if IA_PLATFORM_WINDOWS
Mut<u_long> mode = 1;
ioctlsocket(m_socket, FIONBIO, &mode);
#else
fcntl(m_socket, F_SETFL, O_NONBLOCK);
#endif
m_receive_buffer.resize(UINT16_MAX + 1);
return {};
}
void IpcNode::update() {
if (!m_moni.is_valid()) {
return;
}
Mut<IpcPacketHeader> header;
while (m_moni.pop(
header, Span<u8>(m_receive_buffer.data(), m_receive_buffer.size()))) {
on_packet(header.id, {m_receive_buffer.data(), header.payload_size});
}
Mut<u8> signal = 0;
Const<isize> res = recv(m_socket, reinterpret_cast<char *>(&signal), 1, 0);
if (res == 1) {
on_signal(signal);
} else if (res == 0 || (res < 0 && !SocketOps::is_would_block())) {
SocketOps::close(m_socket);
FileOps::unlink_shared_memory(m_shm_name);
std::exit(-1);
}
}
void IpcNode::send_signal(Const<u8> signal) {
if (m_socket != INVALID_SOCKET) {
send(m_socket, reinterpret_cast<const char *>(&signal), sizeof(signal), 0);
}
}
auto IpcNode::send_packet(Const<u16> packet_id, Const<Span<Const<u8>>> payload)
-> Result<void> {
if (!m_mino.is_valid())
return fail("invalid MINO");
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,
Const<Span<Const<u8>>> payload)
-> Result<void> {
Const<std::scoped_lock<std::mutex>> lock(send_mutex);
if (!moni.is_valid())
return fail("invalid MONI");
return moni.push(packet_id, payload);
}
IpcManager::IpcManager() {
ensure(SocketOps::is_initialized(),
"SocketOps must be initialized before using IpcManager");
m_receive_buffer.resize(UINT16_MAX + 1);
}
IpcManager::~IpcManager() {
for (MutRef<Box<NodeSession>> session : m_active_sessions) {
ProcessOps::terminate_process(session->node_process);
FileOps::unmap_file(session->mapped_ptr);
FileOps::unlink_shared_memory(session->shared_mem_name);
SocketOps::close(session->data_socket);
}
m_active_sessions.clear();
for (MutRef<Box<NodeSession>> session : m_pending_sessions) {
ProcessOps::terminate_process(session->node_process);
FileOps::unmap_file(session->mapped_ptr);
FileOps::unlink_shared_memory(session->shared_mem_name);
SocketOps::close(session->listener_socket);
}
m_pending_sessions.clear();
}
void IpcManager::update() {
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;
--i) {
MutRef<Box<NodeSession>> session =
m_pending_sessions[static_cast<usize>(i)];
if (now - session->creation_time > std::chrono::seconds(5)) {
ProcessOps::terminate_process(session->node_process);
FileOps::unmap_file(session->mapped_ptr);
FileOps::unlink_shared_memory(session->shared_mem_name);
SocketOps::close(session->listener_socket);
m_pending_sessions.erase(m_pending_sessions.begin() + i);
continue;
}
#if IA_PLATFORM_WINDOWS
Mut<u64> new_sock = accept(session->listener_socket, nullptr, nullptr);
#else
Mut<i32> new_sock = accept(session->listener_socket, nullptr, nullptr);
#endif
if (new_sock != INVALID_SOCKET) {
session->data_socket = new_sock;
session->is_ready = true;
#if IA_PLATFORM_WINDOWS
Mut<u_long> mode = 1;
ioctlsocket(session->data_socket, FIONBIO, &mode);
#else
fcntl(session->data_socket, F_SETFL, O_NONBLOCK);
#endif
SocketOps::close(session->listener_socket);
session->listener_socket = INVALID_SOCKET;
Const<NativeProcessID> session_id = session->node_process->id.load();
Mut<NodeSession *> session_ptr = session.get();
m_active_sessions.push_back(std::move(session));
m_pending_sessions.erase(m_pending_sessions.begin() + i);
m_active_session_map[session_id] = session_ptr;
}
}
for (Mut<isize> i = static_cast<isize>(m_active_sessions.size()) - 1; i >= 0;
--i) {
MutRef<Box<NodeSession>> node = m_active_sessions[static_cast<usize>(i)];
Mut<NativeProcessID> node_id = node->node_process->id.load();
Mut<IpcPacketHeader> header;
while (node->mino.pop(
header, Span<u8>(m_receive_buffer.data(), m_receive_buffer.size()))) {
on_packet(node_id, header.id,
{m_receive_buffer.data(), header.payload_size});
}
Mut<u8> signal = 0;
Const<isize> res =
recv(node->data_socket, reinterpret_cast<char *>(&signal), 1, 0);
if (res == 1) {
on_signal(node_id, signal);
} else if (res == 0 || (res < 0 && !SocketOps::is_would_block())) {
ProcessOps::terminate_process(node->node_process);
FileOps::unmap_file(node->mapped_ptr);
FileOps::unlink_shared_memory(node->shared_mem_name);
SocketOps::close(node->data_socket);
m_active_sessions.erase(m_active_sessions.begin() + i);
m_active_session_map.erase(node_id);
}
}
}
auto IpcManager::spawn_node(Ref<Path> executable_path,
Const<u32> shared_memory_size)
-> Result<NativeProcessID> {
Mut<Box<NodeSession>> session = make_box<NodeSession>();
static Mut<std::atomic<u32>> s_id_gen{0};
Const<u32> sid = ++s_id_gen;
Mut<String> sock_path;
#if IA_PLATFORM_WINDOWS
Mut<char[MAX_PATH]> temp_path;
GetTempPathA(MAX_PATH, temp_path);
sock_path = std::format("{}\\ia_sess_{}.sock", temp_path, sid);
#else
sock_path = std::format("/tmp/ia_sess_{}.sock", sid);
#endif
session->listener_socket = OX_TRY(SocketOps::create_unix_socket());
OX_TRY_PURE(
SocketOps::bind_unix_socket(session->listener_socket, sock_path.c_str()));
OX_TRY_PURE(SocketOps::listen(session->listener_socket, 1));
#if IA_PLATFORM_WINDOWS
Mut<u_long> mode = 1;
ioctlsocket(session->listener_socket, FIONBIO, &mode);
#else
fcntl(session->listener_socket, F_SETFL, O_NONBLOCK);
#endif
Const<String> shm_name = std::format("ia_shm_{}", sid);
session->mapped_ptr =
OX_TRY(FileOps::map_shared_memory(shm_name, shared_memory_size, true));
Mut<IpcSharedMemoryLayout *> layout =
reinterpret_cast<IpcSharedMemoryLayout *>(session->mapped_ptr);
layout->meta.magic = 0x49414950;
layout->meta.version = 1;
layout->meta.total_size = shared_memory_size;
Const<u64> header_size = IpcSharedMemoryLayout::get_header_size();
Const<u64> usable_bytes = shared_memory_size - header_size;
Mut<u64> half_size = (usable_bytes / 2);
half_size -= (half_size % 64);
layout->moni_data_offset = header_size;
layout->moni_data_size = half_size;
layout->mino_data_offset = header_size + half_size;
layout->mino_data_size = half_size;
session->moni = OX_TRY(RingBufferView::create(
&layout->moni_control,
Span<u8>(session->mapped_ptr + layout->moni_data_offset,
static_cast<usize>(layout->moni_data_size)),
true));
session->mino = OX_TRY(RingBufferView::create(
&layout->mino_control,
Span<u8>(session->mapped_ptr + layout->mino_data_offset,
static_cast<usize>(layout->mino_data_size)),
true));
Mut<IpcConnectionDescriptor> desc;
desc.socket_path = sock_path;
desc.shared_mem_path = shm_name;
desc.shared_mem_size = shared_memory_size;
Const<String> args = std::format("\"{}\"", desc.serialize());
session->node_process = OX_TRY(ProcessOps::spawn_process_async(
FileOps::normalize_executable_path(executable_path).string(), args,
[sid](Const<StringView> line) {
if (Env::IS_DEBUG) {
std::cout << std::format("{}[Node:{}:STDOUT|STDERR]: {}{}\n",
console::MAGENTA, sid, line, console::RESET);
}
},
[sid](Const<Result<i32>> result) {
if (Env::IS_DEBUG) {
if (!result) {
std::cout << std::format(
"{}[Node: {}]: Failed to spawn with error '{}'{}\n",
console::RED, sid, result.error(), console::RESET);
} else {
std::cout << std::format("{}[Node: {}]: Exited with code {}{}\n",
console::RED, sid, *result,
console::RESET);
}
}
}));
std::this_thread::sleep_for(std::chrono::seconds(1));
if (!session->node_process->is_active()) {
return fail("Failed to spawn the child process \"{}\"",
executable_path.string());
}
Const<NativeProcessID> process_id = session->node_process->id.load();
session->shared_mem_name = shm_name;
session->creation_time = std::chrono::system_clock::now();
m_pending_sessions.push_back(std::move(session));
return process_id;
}
auto IpcManager::wait_till_node_is_online(Const<NativeProcessID> node_id)
-> bool {
Mut<bool> is_pending = true;
while (is_pending) {
is_pending = false;
for (Const<Box<NodeSession>> &session : m_pending_sessions) {
if (session->node_process->id.load() == node_id) {
is_pending = true;
break;
}
}
update();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return m_active_session_map.contains(node_id);
}
void IpcManager::shutdown_node(Const<NativeProcessID> node_id) {
Const<HashMap<NativeProcessID, NodeSession *>::iterator> it_node =
m_active_session_map.find(node_id);
if (it_node == m_active_session_map.end()) {
return;
}
Mut<NodeSession *> node = it_node->second;
ProcessOps::terminate_process(node->node_process);
FileOps::unmap_file(node->mapped_ptr);
FileOps::unlink_shared_memory(node->shared_mem_name);
SocketOps::close(node->data_socket);
std::erase_if(m_active_sessions,
[&](Ref<Box<NodeSession>> s) { return s.get() == node; });
m_active_session_map.erase(it_node);
}
void IpcManager::send_signal(Const<NativeProcessID> node, Const<u8> signal) {
Const<HashMap<NativeProcessID, NodeSession *>::iterator> it_node =
m_active_session_map.find(node);
if (it_node == m_active_session_map.end()) {
return;
}
it_node->second->send_signal(signal);
}
auto IpcManager::send_packet(Const<NativeProcessID> node, Const<u16> packet_id,
Const<Span<Const<u8>>> payload) -> Result<void> {
Const<HashMap<NativeProcessID, NodeSession *>::iterator> it_node =
m_active_session_map.find(node);
if (it_node == m_active_session_map.end())
return fail("no such node");
return it_node->second->send_packet(packet_id, payload);
}
} // namespace IACore

View File

@ -0,0 +1,18 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/JSON.hpp>
namespace IACore {} // namespace IACore

View File

@ -0,0 +1,86 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/Logger.hpp>
#include <chrono>
#include <fstream>
#include <iostream>
namespace IACore {
Mut<Logger::LogLevel> Logger::m_log_level = Logger::LogLevel::Info;
Mut<std::ofstream> Logger::m_log_file;
static auto get_seconds_count() -> f64 {
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 =
std::chrono::steady_clock::now();
Const<std::chrono::duration<f64>> duration = now - start_time;
return duration.count();
}
auto Logger::initialize() -> void {}
auto Logger::terminate() -> void {
if (m_log_file.is_open()) {
m_log_file.flush();
m_log_file.close();
}
}
auto Logger::enable_logging_to_disk(Const<Const<char> *> file_path)
-> Result<void> {
if (m_log_file.is_open()) {
m_log_file.flush();
m_log_file.close();
}
m_log_file.open(file_path);
if (!m_log_file.is_open()) {
return fail("Failed to open log file: {}", file_path);
}
return {};
}
auto Logger::set_log_level(Const<LogLevel> log_level) -> void {
m_log_level = log_level;
}
auto Logger::flush_logs() -> void {
std::cout.flush();
if (m_log_file.is_open()) {
m_log_file.flush();
}
}
auto Logger::log_internal(Const<Const<char> *> prefix, Const<Const<char> *> tag,
ForwardRef<String> msg) -> void {
Const<f64> seconds = get_seconds_count();
Const<String> out_line =
std::format("[{:>8.3f}]: [{}]: {}", seconds, tag, msg);
std::cout << prefix << out_line << console::RESET << '\n';
if (m_log_file.is_open()) {
m_log_file.write(out_line.data(),
static_cast<std::streamsize>(out_line.size()));
m_log_file.put('\n');
m_log_file.flush();
}
}
} // namespace IACore

View File

@ -0,0 +1,134 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/Platform.hpp>
#if defined(IA_ARCH_X64)
#ifndef _MSC_VER
#include <cpuid.h>
#endif
#elif defined(IA_ARCH_ARM64)
#if defined(__linux__) || defined(__ANDROID__)
#include <asm/hwcap.h>
#include <sys/auxv.h>
#endif
#endif
namespace IACore {
Mut<Platform::Capabilities> Platform::s_capabilities{};
#if defined(IA_ARCH_X64)
auto Platform::cpuid(Const<i32> function, Const<i32> sub_function,
Mut<i32> out[4]) -> void {
#ifdef _MSC_VER
__cpuidex(reinterpret_cast<i32 *>(out), static_cast<i32>(function),
static_cast<i32>(sub_function));
#else
Mut<u32> a = 0;
Mut<u32> b = 0;
Mut<u32> c = 0;
Mut<u32> d = 0;
__cpuid_count(function, sub_function, a, b, c, d);
out[0] = static_cast<i32>(a);
out[1] = static_cast<i32>(b);
out[2] = static_cast<i32>(c);
out[3] = static_cast<i32>(d);
#endif
}
#endif
auto Platform::check_cpu() -> bool {
#if defined(IA_ARCH_X64)
Mut<i32> cpu_info[4];
cpuid(0, 0, cpu_info);
if (cpu_info[0] < 7) {
return false;
}
cpuid(1, 0, cpu_info);
Const<bool> osxsave = (cpu_info[2] & (1 << 27)) != 0;
Const<bool> avx = (cpu_info[2] & (1 << 28)) != 0;
Const<bool> fma = (cpu_info[2] & (1 << 12)) != 0;
if (!osxsave || !avx || !fma) {
return false;
}
Const<u64> xcr_feature_mask = _xgetbv(0);
if ((xcr_feature_mask & 0x6) != 0x6) {
return false;
}
cpuid(7, 0, cpu_info);
Const<bool> avx2 = (cpu_info[1] & (1 << 5)) != 0;
if (!avx2) {
return false;
}
s_capabilities.hardware_crc32 = true;
#elif defined(IA_ARCH_ARM64)
#if defined(__linux__) || defined(__ANDROID__)
Const<usize> hw_caps = getauxval(AT_HWCAP);
#ifndef HWCAP_CRC32
#define HWCAP_CRC32 (1 << 7)
#endif
s_capabilities.hardware_crc32 = (hw_caps & HWCAP_CRC32) != 0;
#elif defined(IA_PLATFORM_APPLE)
s_capabilities.hardware_crc32 = true;
#else
s_capabilities.hardware_crc32 = false;
#endif
#else
s_capabilities.hardware_crc32 = false;
#endif
return true;
}
auto Platform::get_architecture_name() -> const char * {
#if defined(IA_ARCH_X64)
return "x86_64";
#elif defined(IA_ARCH_ARM64)
return "aarch64";
#elif defined(IA_ARCH_WASM)
return "wasm";
#else
return "unknown";
#endif
}
auto Platform::get_operating_system_name() -> const char * {
#if IA_PLATFORM_WINDOWS
return "Windows";
#elif defined(IA_PLATFORM_APPLE)
#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
return "iOS";
#else
return "macOS";
#endif
#elif defined(__ANDROID__)
return "Android";
#elif IA_PLATFORM_LINUX
return "Linux";
#elif IA_PLATFORM_WASM
return "WebAssembly";
#else
return "Unknown";
#endif
}
} // namespace IACore

View File

@ -0,0 +1,309 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/ProcessOps.hpp>
namespace IACore {
struct LineBuffer {
Mut<String> m_accumulator;
Const<std::function<void(Const<StringView>)>> m_callback;
auto append(Const<char *> data, Const<usize> size) -> void;
auto flush() -> void;
};
auto LineBuffer::append(Const<char *> data, Const<usize> size) -> void {
Mut<usize> start = 0;
for (Mut<usize> i = 0; i < size; ++i) {
if (data[i] == '\n' || data[i] == '\r') {
if (!m_accumulator.empty()) {
m_accumulator.append(data + start, i - start);
if (!m_accumulator.empty()) {
m_callback(m_accumulator);
}
m_accumulator.clear();
} else {
if (i > start) {
m_callback(StringView(data + start, i - start));
}
}
if (data[i] == '\r' && i + 1 < size && data[i + 1] == '\n') {
i++;
}
start = i + 1;
}
}
if (start < size) {
m_accumulator.append(data + start, size - start);
}
}
auto LineBuffer::flush() -> void {
if (!m_accumulator.empty()) {
m_callback(m_accumulator);
m_accumulator.clear();
}
}
auto ProcessOps::get_current_process_id() -> NativeProcessID {
#if IA_PLATFORM_WINDOWS
return ::GetCurrentProcessId();
#else
return getpid();
#endif
}
auto ProcessOps::spawn_process_sync(
Ref<String> command, Ref<String> args,
Const<std::function<void(Const<StringView>)>> on_output_line_callback)
-> Result<i32> {
Mut<std::atomic<NativeProcessID>> id = 0;
if constexpr (Env::IS_WINDOWS) {
return spawn_process_windows(command, args, on_output_line_callback, id);
} else {
return spawn_process_posix(command, args, on_output_line_callback, id);
}
}
auto ProcessOps::spawn_process_async(
Ref<String> command, Ref<String> args,
Const<std::function<void(Const<StringView>)>> on_output_line_callback,
Const<std::function<void(Const<Result<i32>>)>> on_finish_callback)
-> Result<Box<ProcessHandle>> {
Mut<Box<ProcessHandle>> handle = make_box<ProcessHandle>();
handle->is_running = true;
Mut<ProcessHandle *> h_ptr = handle.get();
handle->m_thread_handle = std::jthread([h_ptr, cmd = command, arg = args,
cb = on_output_line_callback,
fin = on_finish_callback]() mutable {
Mut<Result<i32>> result = fail("Platform not supported");
if constexpr (Env::IS_WINDOWS) {
result = spawn_process_windows(cmd, arg, cb, h_ptr->id);
} else {
result = spawn_process_posix(cmd, arg, cb, h_ptr->id);
}
h_ptr->is_running = false;
if (fin) {
if (!result) {
fin(fail(std::move(result.error())));
} else {
fin(*result);
}
}
});
return handle;
}
auto ProcessOps::terminate_process(Ref<Box<ProcessHandle>> handle) -> void {
if (!handle || !handle->is_active()) {
return;
}
Const<NativeProcessID> pid = handle->id.load();
if (pid == 0) {
return;
}
#if IA_PLATFORM_WINDOWS
Mut<HANDLE> h_process = OpenProcess(PROCESS_TERMINATE, false, pid);
if (h_process != NULL) {
::TerminateProcess(h_process, 9);
CloseHandle(h_process);
}
#endif
#if IA_PLATFORM_UNIX
kill(pid, SIGKILL);
#endif
}
auto ProcessOps::spawn_process_windows(
Ref<String> command, Ref<String> args,
Const<std::function<void(Const<StringView>)>> on_output_line_callback,
MutRef<std::atomic<NativeProcessID>> id) -> Result<i32> {
#if IA_PLATFORM_WINDOWS
Mut<SECURITY_ATTRIBUTES> sa_attr = {sizeof(SECURITY_ATTRIBUTES), NULL, true};
Mut<HANDLE> h_read = NULL;
Mut<HANDLE> h_write = NULL;
if (!CreatePipe(&h_read, &h_write, &sa_attr, 0)) {
return fail("Failed to create pipe");
}
if (!SetHandleInformation(h_read, HANDLE_FLAG_INHERIT, 0)) {
return fail("Failed to secure pipe handles");
}
Mut<STARTUPINFOA> si = {sizeof(STARTUPINFOA)};
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdOutput = h_write;
si.hStdError = h_write;
si.hStdInput = NULL;
Mut<PROCESS_INFORMATION> pi = {0};
Mut<String> command_line = std::format("\"{}\" {}", command, args);
Const<BOOL> success = CreateProcessA(NULL, command_line.data(), NULL, NULL,
true, 0, NULL, NULL, &si, &pi);
CloseHandle(h_write);
if (!success) {
CloseHandle(h_read);
return fail("CreateProcess failed: {}", GetLastError());
}
id.store(pi.dwProcessId);
Mut<LineBuffer> line_buf{"", on_output_line_callback};
Mut<DWORD> bytes_read = 0;
Mut<Array<char, 4096>> buffer;
while (ReadFile(h_read, buffer.data(), static_cast<DWORD>(buffer.size()),
&bytes_read, NULL) &&
bytes_read != 0) {
line_buf.append(buffer.data(), bytes_read);
}
line_buf.flush();
Mut<DWORD> exit_code = 0;
WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, &exit_code);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(h_read);
id.store(0);
return static_cast<i32>(exit_code);
#else
OX_UNUSED(command);
OX_UNUSED(args);
OX_UNUSED(on_output_line_callback);
OX_UNUSED(id);
return fail("Windows implementation not available.");
#endif
}
auto ProcessOps::spawn_process_posix(
Ref<String> command, Ref<String> args,
Const<std::function<void(Const<StringView>)>> on_output_line_callback,
MutRef<std::atomic<NativeProcessID>> id) -> Result<i32> {
#if IA_PLATFORM_UNIX
Mut<Array<i32, 2>> pipefd;
if (pipe(pipefd.data()) == -1) {
return fail("Failed to create pipe");
}
Const<pid_t> pid = fork();
if (pid == -1) {
return fail("Failed to fork process");
} else if (pid == 0) {
close(pipefd[0]);
dup2(pipefd[1], STDOUT_FILENO);
dup2(pipefd[1], STDERR_FILENO);
close(pipefd[1]);
Mut<Vec<String>> arg_storage;
Mut<Vec<char *>> argv;
Mut<String> cmd_str = command;
argv.push_back(cmd_str.data());
Mut<String> current_token;
Mut<bool> in_quotes = false;
Mut<bool> is_escaped = false;
for (Const<char> c : args) {
if (is_escaped) {
current_token += c;
is_escaped = false;
continue;
}
if (c == '\\') {
is_escaped = true;
continue;
}
if (c == '\"') {
in_quotes = !in_quotes;
continue;
}
if (c == ' ' && !in_quotes) {
if (!current_token.empty()) {
arg_storage.push_back(current_token);
current_token.clear();
}
} else {
current_token += c;
}
}
if (!current_token.empty()) {
arg_storage.push_back(current_token);
}
for (MutRef<String> s : arg_storage) {
argv.push_back(s.data());
}
argv.push_back(nullptr);
execvp(argv[0], argv.data());
_exit(127);
} else {
id.store(pid);
close(pipefd[1]);
Mut<LineBuffer> line_buf{"", on_output_line_callback};
Mut<Array<char, 4096>> buffer;
Mut<isize> count;
while ((count = read(pipefd[0], buffer.data(), buffer.size())) > 0) {
line_buf.append(buffer.data(), static_cast<usize>(count));
}
line_buf.flush();
close(pipefd[0]);
Mut<i32> status;
waitpid(pid, &status, 0);
id.store(0);
if (WIFEXITED(status)) {
return WEXITSTATUS(status);
}
return -1;
}
#else
OX_UNUSED(command);
OX_UNUSED(args);
OX_UNUSED(on_output_line_callback);
OX_UNUSED(id);
return fail("Posix implementation not available.");
#endif
}
} // namespace IACore

View File

@ -0,0 +1,18 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/SIMD.hpp>
namespace IACore {} // namespace IACore

View File

@ -0,0 +1,146 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/SocketOps.hpp>
#include <cstring>
namespace IACore {
Mut<i32> SocketOps::s_init_count = 0;
auto SocketOps::close(Const<SocketHandle> sock) -> void {
if (sock == INVALID_SOCKET) {
return;
}
#if IA_PLATFORM_WINDOWS
closesocket(sock);
#else
::close(sock);
#endif
}
auto SocketOps::listen(Const<SocketHandle> sock, Const<i32> queue_size)
-> Result<void> {
if (::listen(sock, queue_size) == 0) {
return {};
}
#if IA_PLATFORM_WINDOWS
return fail("listen failed: {}", WSAGetLastError());
#else
return fail("listen failed: {}", errno);
#endif
}
auto SocketOps::create_unix_socket() -> Result<SocketHandle> {
Const<SocketHandle> sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
#if IA_PLATFORM_WINDOWS
return fail("socket(AF_UNIX) failed: {}", WSAGetLastError());
#else
return fail("socket(AF_UNIX) failed: {}", errno);
#endif
}
return sock;
}
auto SocketOps::bind_unix_socket(Const<SocketHandle> sock,
Const<const char *> path) -> Result<void> {
if (sock == INVALID_SOCKET) {
return fail("Invalid socket handle");
}
unlink_file(path);
Mut<sockaddr_un> addr{};
addr.sun_family = AF_UNIX;
Const<usize> max_len = sizeof(addr.sun_path) - 1;
#if IA_PLATFORM_WINDOWS
strncpy_s(addr.sun_path, sizeof(addr.sun_path), path, max_len);
#else
std::strncpy(addr.sun_path, path, max_len);
#endif
if (::bind(sock, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) ==
-1) {
#if IA_PLATFORM_WINDOWS
return fail("bind failed: {}", WSAGetLastError());
#else
return fail("bind failed: {}", errno);
#endif
}
return {};
}
auto SocketOps::connect_unix_socket(Const<SocketHandle> sock,
Const<const char *> path) -> Result<void> {
if (sock == INVALID_SOCKET) {
return fail("Invalid socket handle");
}
Mut<sockaddr_un> addr{};
addr.sun_family = AF_UNIX;
Const<usize> max_len = sizeof(addr.sun_path) - 1;
#if IA_PLATFORM_WINDOWS
strncpy_s(addr.sun_path, sizeof(addr.sun_path), path, max_len);
#else
std::strncpy(addr.sun_path, path, max_len);
#endif
if (::connect(sock, reinterpret_cast<struct sockaddr *>(&addr),
sizeof(addr)) == -1) {
#if IA_PLATFORM_WINDOWS
return fail("connect failed: {}", WSAGetLastError());
#else
return fail("connect failed: {}", errno);
#endif
}
return {};
}
auto SocketOps::is_port_available(Const<u16> port, Const<i32> type) -> bool {
Const<SocketHandle> sock = socket(AF_INET, type, 0);
if (sock == INVALID_SOCKET) {
return false;
}
Mut<sockaddr_in> addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
Mut<bool> is_free = false;
if (::bind(sock, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) ==
0) {
is_free = true;
}
close(sock);
return is_free;
}
auto SocketOps::is_would_block() -> bool {
#if IA_PLATFORM_WINDOWS
return WSAGetLastError() == WSAEWOULDBLOCK;
#else
return errno == EWOULDBLOCK || errno == EAGAIN;
#endif
}
} // namespace IACore

View File

@ -0,0 +1,85 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/FileOps.hpp>
#include <IACore/StreamReader.hpp>
namespace IACore {
auto StreamReader::create_from_file(Ref<Path> path) -> Result<StreamReader> {
Mut<usize> size = 0;
Const<const u8 *> ptr = OX_TRY(FileOps::map_file(path, size));
Mut<StreamReader> reader(Span<const u8>(ptr, size));
reader.m_storage_type = StorageType::OwningMmap;
return reader;
}
StreamReader::StreamReader(ForwardRef<Vec<u8>> data)
: m_owning_vector(std::move(data)),
m_storage_type(StorageType::OwningVector) {
m_data = m_owning_vector.data();
m_data_size = m_owning_vector.size();
}
StreamReader::StreamReader(Const<Span<const u8>> data)
: m_data(data.data()), m_data_size(data.size()),
m_storage_type(StorageType::NonOwning) {}
StreamReader::StreamReader(ForwardRef<StreamReader> other)
: m_data(other.m_data), m_cursor(other.m_cursor),
m_data_size(other.m_data_size),
m_owning_vector(std::move(other.m_owning_vector)),
m_storage_type(other.m_storage_type) {
other.m_storage_type = StorageType::NonOwning;
other.m_data = {};
other.m_data_size = 0;
if (m_storage_type == StorageType::OwningVector) {
m_data = m_owning_vector.data();
}
}
auto StreamReader::operator=(ForwardRef<StreamReader> other)
-> MutRef<StreamReader> {
if (this != &other) {
if (m_storage_type == StorageType::OwningMmap) {
FileOps::unmap_file(m_data);
}
m_data = other.m_data;
m_cursor = other.m_cursor;
m_data_size = other.m_data_size;
m_owning_vector = std::move(other.m_owning_vector);
m_storage_type = other.m_storage_type;
if (m_storage_type == StorageType::OwningVector) {
m_data = m_owning_vector.data();
}
other.m_storage_type = StorageType::NonOwning;
other.m_data = {};
other.m_data_size = 0;
}
return *this;
}
StreamReader::~StreamReader() {
if (m_storage_type == StorageType::OwningMmap) {
FileOps::unmap_file(m_data);
}
}
} // namespace IACore

View File

@ -0,0 +1,162 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/StreamWriter.hpp>
namespace IACore {
auto StreamWriter::create_from_file(Ref<Path> path) -> Result<StreamWriter> {
Mut<FILE *> f = std::fopen(path.string().c_str(), "wb");
if (!f) {
return fail("Failed to open file for writing: {}", path.string());
}
std::fclose(f);
Mut<StreamWriter> writer;
writer.m_file_path = path;
writer.m_storage_type = StorageType::OwningFile;
return writer;
}
StreamWriter::StreamWriter() : m_storage_type(StorageType::OwningVector) {
m_capacity = 256;
m_owning_vector.resize(m_capacity);
m_buffer = m_owning_vector.data();
}
StreamWriter::StreamWriter(Const<Span<u8>> data)
: m_buffer(data.data()), m_cursor(0), m_capacity(data.size()),
m_storage_type(StorageType::NonOwning) {}
StreamWriter::StreamWriter(ForwardRef<StreamWriter> other)
: m_buffer(other.m_buffer), m_cursor(other.m_cursor),
m_capacity(other.m_capacity), m_file_path(other.m_file_path),
m_owning_vector(std::move(other.m_owning_vector)),
m_storage_type(other.m_storage_type) {
other.m_capacity = {};
other.m_buffer = {};
other.m_storage_type = StorageType::NonOwning;
if (m_storage_type == StorageType::OwningVector)
m_buffer = m_owning_vector.data();
}
auto StreamWriter::operator=(ForwardRef<StreamWriter> other)
-> MutRef<StreamWriter> {
if (this != &other) {
if (m_storage_type == StorageType::OwningFile) {
if (Const<Result<void>> res = flush_to_disk(); !res) {
std::fprintf(stderr, "[IACore] Data loss in StreamWriter move: %s\n",
res.error().c_str());
}
}
m_buffer = other.m_buffer;
m_cursor = other.m_cursor;
m_capacity = other.m_capacity;
m_file_path = std::move(other.m_file_path);
m_owning_vector = std::move(other.m_owning_vector);
m_storage_type = other.m_storage_type;
if (m_storage_type == StorageType::OwningVector)
m_buffer = m_owning_vector.data();
other.m_capacity = 0;
other.m_cursor = 0;
other.m_buffer = nullptr;
other.m_storage_type = StorageType::NonOwning;
}
return *this;
}
StreamWriter::~StreamWriter() {
if (m_storage_type == StorageType::OwningFile) {
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> {
Mut<Result<void>> res = flush_to_disk();
if (res.has_value()) {
m_storage_type = StorageType::OwningVector;
}
return res;
}
auto StreamWriter::flush_to_disk() -> Result<void> {
if (m_storage_type != StorageType::OwningFile || m_file_path.empty()) {
return {};
}
Mut<FILE *> f = std::fopen(m_file_path.string().c_str(), "wb");
if (!f) {
return fail("Failed to open file for writing: {}", m_file_path.string());
}
Const<usize> written = std::fwrite(m_buffer, 1, m_cursor, f);
std::fclose(f);
if (written != m_cursor) {
return fail("Incomplete write: {} of {} bytes written", written, m_cursor);
}
return {};
}
auto StreamWriter::write(Const<u8> byte, Const<usize> count) -> Result<void> {
if (m_cursor + count > m_capacity) {
if (m_storage_type == StorageType::NonOwning) {
return fail("StreamWriter buffer overflow (NonOwning)");
}
Const<usize> required = m_cursor + count;
Const<usize> double_cap = m_capacity * 2;
Const<usize> new_capacity = (double_cap > required) ? double_cap : required;
m_owning_vector.resize(new_capacity);
m_capacity = m_owning_vector.size();
m_buffer = m_owning_vector.data();
}
std::memset(m_buffer + m_cursor, byte, count);
m_cursor += count;
return {};
}
auto StreamWriter::write(Const<const void *> buffer, Const<usize> size)
-> Result<void> {
if (m_cursor + size > m_capacity) {
if (m_storage_type == StorageType::NonOwning) {
return fail("StreamWriter buffer overflow (NonOwning)");
}
Const<usize> required = m_cursor + size;
Const<usize> double_cap = m_capacity * 2;
Const<usize> new_capacity = (double_cap > required) ? double_cap : required;
m_owning_vector.resize(new_capacity);
m_capacity = m_owning_vector.size();
m_buffer = m_owning_vector.data();
}
std::memcpy(m_buffer + m_cursor, buffer, size);
m_cursor += size;
return {};
}
} // namespace IACore

View File

@ -0,0 +1,122 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/StringOps.hpp>
namespace IACore {
static Const<String> BASE64_CHAR_TABLE =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static auto is_base64(Const<u8> c) -> bool {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') || (c == '+') || (c == '/');
}
static auto get_base64_index(Const<u8> c) -> u8 {
if (c >= 'A' && c <= 'Z')
return c - 'A';
if (c >= 'a' && c <= 'z')
return c - 'a' + 26;
if (c >= '0' && c <= '9')
return c - '0' + 52;
if (c == '+')
return 62;
if (c == '/')
return 63;
return 0;
}
auto StringOps::encode_base64(Const<Span<Const<u8>>> data) -> String {
Mut<String> result;
result.reserve(((data.size() + 2) / 3) * 4);
for (Mut<usize> i = 0; i < data.size(); i += 3) {
Const<u32> b0 = data[i];
Const<u32> b1 = (i + 1 < data.size()) ? data[i + 1] : 0;
Const<u32> b2 = (i + 2 < data.size()) ? data[i + 2] : 0;
Const<u32> triple = (b0 << 16) | (b1 << 8) | b2;
result += BASE64_CHAR_TABLE[(triple >> 18) & 0x3F];
result += BASE64_CHAR_TABLE[(triple >> 12) & 0x3F];
if (i + 1 < data.size()) {
result += BASE64_CHAR_TABLE[(triple >> 6) & 0x3F];
} else {
result += '=';
}
if (i + 2 < data.size()) {
result += BASE64_CHAR_TABLE[triple & 0x3F];
} else {
result += '=';
}
}
return result;
}
auto StringOps::decode_base64(Ref<String> data) -> Vec<u8> {
Mut<Vec<u8>> result;
result.reserve(data.size() * 3 / 4);
Mut<i32> i = 0;
Mut<Array<u8, 4>> tmp_buf = {};
for (Const<char> c_char : data) {
Const<u8> c = static_cast<u8>(c_char);
if (c == '=') {
break;
}
if (!is_base64(c)) {
break;
}
tmp_buf[i++] = c;
if (i == 4) {
Const<u8> n0 = get_base64_index(tmp_buf[0]);
Const<u8> n1 = get_base64_index(tmp_buf[1]);
Const<u8> n2 = get_base64_index(tmp_buf[2]);
Const<u8> n3 = get_base64_index(tmp_buf[3]);
result.push_back((n0 << 2) | ((n1 & 0x30) >> 4));
result.push_back(((n1 & 0x0F) << 4) | ((n2 & 0x3C) >> 2));
result.push_back(((n2 & 0x03) << 6) | n3);
i = 0;
}
}
if (i > 0) {
for (Mut<i32> j = i; j < 4; ++j) {
tmp_buf[j] = 'A';
}
Const<u8> n0 = get_base64_index(tmp_buf[0]);
Const<u8> n1 = get_base64_index(tmp_buf[1]);
Const<u8> n2 = get_base64_index(tmp_buf[2]);
if (i > 1) {
result.push_back((n0 << 2) | ((n1 & 0x30) >> 4));
}
if (i > 2) {
result.push_back(((n1 & 0x0F) << 4) | ((n2 & 0x3C) >> 2));
}
}
return result;
}
} // namespace IACore

View File

@ -0,0 +1,112 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/Utils.hpp>
#include <chrono>
#include <cstdlib>
namespace IACore {
namespace {
auto from_hex_char(Const<char> c) -> i32 {
if (c >= '0' && c <= '9') {
return c - '0';
}
if (c >= 'A' && c <= 'F') {
return c - 'A' + 10;
}
if (c >= 'a' && c <= 'f') {
return c - 'a' + 10;
}
return -1;
}
} // namespace
extern Mut<std::chrono::high_resolution_clock::time_point> g_start_time;
auto Utils::get_unix_time() -> u64 {
Const<std::chrono::system_clock::time_point> now =
std::chrono::system_clock::now();
return std::chrono::duration_cast<std::chrono::seconds>(
now.time_since_epoch())
.count();
}
auto Utils::get_ticks_count() -> u64 {
Const<std::chrono::high_resolution_clock::duration> duration =
std::chrono::high_resolution_clock::now() - g_start_time;
return std::chrono::duration_cast<std::chrono::milliseconds>(duration)
.count();
}
auto Utils::get_seconds_count() -> f64 {
Const<std::chrono::high_resolution_clock::duration> duration =
std::chrono::high_resolution_clock::now() - g_start_time;
return static_cast<f64>(
std::chrono::duration_cast<std::chrono::seconds>(duration).count());
}
auto Utils::get_random() -> f32 {
return static_cast<f32>(std::rand()) / static_cast<f32>(RAND_MAX);
}
auto Utils::get_random(Const<u64> max) -> u64 {
return static_cast<u64>(static_cast<f32>(max) * get_random());
}
auto Utils::get_random(Const<i64> min, Const<i64> max) -> i64 {
return min + static_cast<i64>(static_cast<f32>(max - min) * get_random());
}
auto Utils::sleep(Const<u64> milliseconds) -> void {
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
}
auto Utils::binary_to_hex_string(Const<Span<Const<u8>>> data) -> String {
static constexpr Const<char[17]> lut = "0123456789ABCDEF";
Mut<String> res = String();
res.reserve(data.size() * 2);
for (Const<u8> b : data) {
res.push_back(lut[(b >> 4) & 0x0F]);
res.push_back(lut[b & 0x0F]);
}
return res;
}
auto Utils::hex_string_to_binary(Const<StringView> hex) -> Result<Vec<u8>> {
if (hex.size() % 2 != 0) {
return fail("Hex string must have even length");
}
Mut<Vec<u8>> out = Vec<u8>();
out.reserve(hex.size() / 2);
for (Mut<usize> i = 0; i < hex.size(); i += 2) {
Const<char> high = hex[i];
Const<char> low = hex[i + 1];
Const<i32> h = from_hex_char(high);
Const<i32> l = from_hex_char(low);
if (h == -1 || l == -1) {
return fail("Invalid hex character found");
}
out.push_back(static_cast<u8>((h << 4) | l));
}
return out;
}
} // namespace IACore

View File

@ -0,0 +1,82 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/XML.hpp>
#include <sstream>
namespace IACore {
auto XML::parse_from_string(Ref<String> data) -> Result<Document> {
Mut<Document> doc;
Const<pugi::xml_parse_result> parse_result = doc.load_string(data.c_str());
if (!parse_result) {
return fail("Failed to parse XML {}", parse_result.description());
}
return std::move(doc);
}
auto XML::parse_from_file(Ref<Path> path) -> Result<Document> {
Mut<Document> doc;
Const<pugi::xml_parse_result> parse_result =
doc.load_file(path.string().c_str());
if (!parse_result) {
return fail("Failed to parse XML {}", parse_result.description());
}
return std::move(doc);
}
auto XML::serialize_to_string(Ref<Node> node, Const<bool> escape) -> String {
Mut<std::ostringstream> oss;
node.print(oss);
return escape ? escape_xml_string(oss.str()) : oss.str();
}
auto XML::serialize_to_string(Ref<Document> doc, Const<bool> escape) -> String {
Mut<std::ostringstream> oss;
doc.save(oss);
return escape ? escape_xml_string(oss.str()) : oss.str();
}
auto XML::escape_xml_string(Ref<String> xml) -> String {
Mut<String> buffer;
buffer.reserve(xml.size() + (xml.size() / 10));
for (Const<char> c : xml) {
switch (c) {
case '&':
buffer.append("&amp;");
break;
case '\"':
buffer.append("&quot;");
break;
case '\'':
buffer.append("&apos;");
break;
case '<':
buffer.append("&lt;");
break;
case '>':
buffer.append("&gt;");
break;
default:
buffer.push_back(c);
break;
}
}
return buffer;
}
} // namespace IACore

View File

@ -0,0 +1,264 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
namespace IACore {
class RingBufferView {
public:
static constexpr Const<u16> PACKET_ID_SKIP = 0;
struct ControlBlock {
struct alignas(64) {
Mut<std::atomic<u32>> write_offset{0};
} producer;
struct alignas(64) {
Mut<std::atomic<u32>> read_offset{0};
Mut<u32> capacity{0};
} consumer;
};
static_assert(offsetof(ControlBlock, consumer) == 64,
"False sharing detected in ControlBlock");
struct PacketHeader {
PacketHeader() : id(0), payload_size(0) {}
PacketHeader(Const<u16> id) : id(id), payload_size(0) {}
PacketHeader(Const<u16> id, Const<u16> payload_size)
: id(id), payload_size(payload_size) {}
Mut<u16> id{};
Mut<u16> payload_size{};
};
public:
static auto default_instance() -> RingBufferView;
static auto create(Ref<Span<u8>> buffer, Const<bool> is_owner)
-> Result<RingBufferView>;
static auto create(Const<ControlBlock *> control_block, Ref<Span<u8>> buffer,
Const<bool> is_owner) -> Result<RingBufferView>;
// Returns:
// - nullopt if empty
// - bytes_read if success
// - Error if buffer too small
auto pop(MutRef<PacketHeader> out_header, Ref<Span<u8>> out_buffer)
-> Result<Option<usize>>;
auto push(Const<u16> packet_id, Ref<Span<const u8>> data) -> Result<void>;
auto get_control_block() -> ControlBlock *;
[[nodiscard]] auto is_valid() const -> bool;
protected:
RingBufferView(Ref<Span<u8>> buffer, Const<bool> is_owner);
RingBufferView(Const<ControlBlock *> control_block, Ref<Span<u8>> buffer,
Const<bool> is_owner);
private:
Mut<u8 *> m_data_ptr{};
Mut<u32> m_capacity{};
Mut<ControlBlock *> m_control_block{};
private:
auto write_wrapped(Const<u32> offset, Const<const void *> data,
Const<u32> size) -> void;
auto read_wrapped(Const<u32> offset, Const<void *> out_data, Const<u32> size)
-> void;
};
inline auto RingBufferView::default_instance() -> RingBufferView {
return RingBufferView(nullptr, {}, false);
}
inline auto RingBufferView::create(Ref<Span<u8>> buffer, Const<bool> is_owner)
-> Result<RingBufferView> {
if (buffer.size() <= sizeof(ControlBlock)) {
return fail("Buffer too small for ControlBlock");
}
if (!is_owner) {
Const<ControlBlock *> cb = reinterpret_cast<ControlBlock *>(buffer.data());
Const<u32> capacity =
static_cast<u32>(buffer.size()) - sizeof(ControlBlock);
if (cb->consumer.capacity != capacity) {
return fail("Capacity mismatch");
}
}
return RingBufferView(buffer, is_owner);
}
inline auto RingBufferView::create(Const<ControlBlock *> control_block,
Ref<Span<u8>> buffer, Const<bool> is_owner)
-> Result<RingBufferView> {
if (control_block == nullptr) {
return fail("ControlBlock is null");
}
if (buffer.empty()) {
return fail("Buffer is empty");
}
return RingBufferView(control_block, buffer, is_owner);
}
inline RingBufferView::RingBufferView(Ref<Span<u8>> buffer,
Const<bool> is_owner) {
m_control_block = reinterpret_cast<ControlBlock *>(buffer.data());
m_data_ptr = buffer.data() + sizeof(ControlBlock);
m_capacity = static_cast<u32>(buffer.size()) - sizeof(ControlBlock);
if (is_owner) {
m_control_block->consumer.capacity = m_capacity;
m_control_block->producer.write_offset.store(0, std::memory_order_release);
m_control_block->consumer.read_offset.store(0, std::memory_order_release);
}
}
inline RingBufferView::RingBufferView(Const<ControlBlock *> control_block,
Ref<Span<u8>> buffer,
Const<bool> is_owner) {
m_control_block = control_block;
m_data_ptr = buffer.data();
m_capacity = static_cast<u32>(buffer.size());
if (is_owner) {
m_control_block->consumer.capacity = m_capacity;
m_control_block->producer.write_offset.store(0, std::memory_order_release);
m_control_block->consumer.read_offset.store(0, std::memory_order_release);
}
}
inline auto RingBufferView::pop(MutRef<PacketHeader> out_header,
Ref<Span<u8>> out_buffer)
-> Result<Option<usize>> {
Const<u32> write =
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;
if (read == write) {
return std::nullopt;
}
read_wrapped(read, &out_header, sizeof(PacketHeader));
if (out_header.payload_size > out_buffer.size()) {
return fail("Buffer too small: needed {}, provided {}",
out_header.payload_size, out_buffer.size());
}
if (out_header.payload_size > 0) {
Const<u32> data_read_offset = (read + sizeof(PacketHeader)) % cap;
read_wrapped(data_read_offset, out_buffer.data(), out_header.payload_size);
}
Const<u32> new_read_offset =
(read + sizeof(PacketHeader) + out_header.payload_size) % cap;
m_control_block->consumer.read_offset.store(new_read_offset,
std::memory_order_release);
return std::make_optional(static_cast<usize>(out_header.payload_size));
}
inline auto RingBufferView::push(Const<u16> packet_id, Ref<Span<const u8>> data)
-> Result<void> {
if (data.size() > std::numeric_limits<u16>::max()) {
return fail("Data size exceeds u16 limit");
}
Const<u32> total_size = sizeof(PacketHeader) + static_cast<u32>(data.size());
Const<u32> read =
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> cap = m_capacity;
Const<u32> free_space =
(read <= write) ? (m_capacity - write) + read : (read - write);
// Leave 1 byte empty (prevent ambiguities)
if (free_space <= total_size) {
return fail("RingBuffer full");
}
Const<PacketHeader> header{packet_id, static_cast<u16>(data.size())};
write_wrapped(write, &header, sizeof(PacketHeader));
Const<u32> data_write_offset = (write + sizeof(PacketHeader)) % cap;
if (!data.empty()) {
write_wrapped(data_write_offset, data.data(),
static_cast<u32>(data.size()));
}
Const<u32> new_write_offset = (data_write_offset + data.size()) % cap;
m_control_block->producer.write_offset.store(new_write_offset,
std::memory_order_release);
return {};
}
inline auto RingBufferView::get_control_block() -> ControlBlock * {
return m_control_block;
}
inline auto RingBufferView::write_wrapped(Const<u32> offset,
Const<const void *> data,
Const<u32> size) -> void {
if (offset + size <= m_capacity) {
std::memcpy(m_data_ptr + offset, data, size);
} else {
Const<u32> first_chunk = m_capacity - offset;
Const<u32> second_chunk = size - first_chunk;
Const<const u8 *> src = static_cast<const u8 *>(data);
std::memcpy(m_data_ptr + offset, src, first_chunk);
std::memcpy(m_data_ptr, src + first_chunk, second_chunk);
}
}
inline auto RingBufferView::read_wrapped(Const<u32> offset,
Const<void *> out_data,
Const<u32> size) -> void {
if (offset + size <= m_capacity) {
std::memcpy(out_data, m_data_ptr + offset, size);
} else {
Const<u32> first_chunk = m_capacity - offset;
Const<u32> second_chunk = size - first_chunk;
Const<u8 *> dst = static_cast<u8 *>(out_data);
std::memcpy(dst, m_data_ptr + offset, first_chunk);
std::memcpy(dst + first_chunk, m_data_ptr, second_chunk);
}
}
[[nodiscard]] inline auto RingBufferView::is_valid() const -> bool {
return m_control_block && m_data_ptr && m_capacity;
}
} // namespace IACore

View File

@ -0,0 +1,71 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
#include <deque>
#include <functional>
#include <stop_token>
namespace IACore {
class AsyncOps {
public:
using TaskTag = u64;
using WorkerId = u16;
static constexpr Const<WorkerId> MAIN_THREAD_WORKER_ID = 0;
enum class Priority : u8 { High, Normal };
struct Schedule {
Mut<std::atomic<i32>> counter{0};
};
public:
static auto initialize_scheduler(Const<u8> worker_count = 0) -> Result<void>;
static auto terminate_scheduler() -> void;
static auto schedule_task(Mut<std::function<void(Const<WorkerId>)>> task,
Const<TaskTag> tag, Mut<Schedule *> schedule,
Const<Priority> priority = Priority::Normal)
-> void;
static auto cancel_tasks_of_tag(Const<TaskTag> tag) -> void;
static auto wait_for_schedule_completion(Mut<Schedule *> schedule) -> void;
static auto run_task(Mut<std::function<void()>> task) -> void;
IA_NODISCARD static auto get_worker_count() -> WorkerId;
private:
struct ScheduledTask {
Mut<TaskTag> tag{};
Mut<Schedule *> schedule_handle{};
Mut<std::function<void(Const<WorkerId>)>> task{};
};
static auto schedule_worker_loop(Mut<std::stop_token> stop_token,
Const<WorkerId> worker_id) -> void;
private:
static Mut<std::mutex> s_queue_mutex;
static Mut<std::condition_variable> s_wake_condition;
static Mut<Vec<std::jthread>> s_schedule_workers;
static Mut<std::deque<ScheduledTask>> s_high_priority_queue;
static Mut<std::deque<ScheduledTask>> s_normal_priority_queue;
};
} // namespace IACore

View File

@ -0,0 +1,63 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
namespace IACore {
class CLIParser {
/*
* PLEASE READ
*
* CLIParser is still very much in it's baby stages.
* Subject to heavy and frequent changes, use with
* caution!
*/
public:
CLIParser(Const<Span<Const<String>>> args);
~CLIParser() = default;
public:
IA_NODISCARD auto remaining() const -> bool {
return m_current_arg < m_arg_list.end();
}
IA_NODISCARD auto peek() const -> StringView {
if (!remaining())
return "";
return *m_current_arg;
}
auto next() -> StringView {
if (!remaining())
return "";
return *m_current_arg++;
}
auto consume(Ref<StringView> expected) -> bool {
if (peek() == expected) {
next();
return true;
}
return false;
}
private:
Const<Span<Const<String>>> m_arg_list;
Mut<Span<Const<String>>::const_iterator> m_current_arg;
};
} // namespace IACore

View File

@ -0,0 +1,47 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
namespace IACore {
class DataOps {
public:
enum class CompressionType { None, Gzip, Zlib };
public:
static auto hash_fnv1a(Ref<String> string) -> u32;
static auto hash_fnv1a(Ref<Span<Const<u8>>> data) -> u32;
static auto hash_xxhash(Ref<String> string, Const<u32> seed = 0) -> u32;
static auto hash_xxhash(Ref<Span<Const<u8>>> data, Const<u32> seed = 0)
-> u32;
static auto crc32(Ref<Span<Const<u8>>> data) -> u32;
static auto detect_compression(Const<Span<Const<u8>>> data)
-> CompressionType;
static auto gzip_inflate(Ref<Span<Const<u8>>> data) -> Result<Vec<u8>>;
static auto gzip_deflate(Ref<Span<Const<u8>>> data) -> Result<Vec<u8>>;
static auto zlib_inflate(Ref<Span<Const<u8>>> data) -> Result<Vec<u8>>;
static auto zlib_deflate(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>>;
};
} // namespace IACore

View File

@ -0,0 +1,151 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
#if !IA_PLATFORM_WINDOWS
#include <dlfcn.h>
#endif
namespace IACore {
class DynamicLib {
public:
IA_NODISCARD static auto load(Ref<String> search_path, Ref<String> name)
-> Result<DynamicLib> {
namespace fs = std::filesystem;
Mut<Path> full_path = fs::path(search_path) / name;
if (!full_path.has_extension()) {
#if IA_PLATFORM_WINDOWS
full_path += ".dll";
#elif IA_PLATFORM_APPLE
full_path += ".dylib";
#else
full_path += ".so";
#endif
}
Mut<DynamicLib> lib;
#if IA_PLATFORM_WINDOWS
Const<HMODULE> h = LoadLibraryA(full_path.string().c_str());
if (!h) {
return fail(get_windows_error());
}
lib.m_handle = static_cast<void *>(h);
#else
Mut<void *> h = dlopen(full_path.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!h) {
Const<char *> err = dlerror();
return fail(err ? err : "Unknown dlopen error");
}
lib.m_handle = h;
#endif
return lib;
}
DynamicLib() = default;
DynamicLib(ForwardRef<DynamicLib> other) noexcept : m_handle(other.m_handle) {
other.m_handle = nullptr;
}
auto operator=(ForwardRef<DynamicLib> other) noexcept -> MutRef<DynamicLib> {
if (this != &other) {
unload();
m_handle = other.m_handle;
other.m_handle = nullptr;
}
return *this;
}
DynamicLib(Ref<DynamicLib>) = delete;
auto operator=(Ref<DynamicLib>) -> MutRef<DynamicLib> = delete;
~DynamicLib() { unload(); }
IA_NODISCARD auto get_symbol(Ref<String> name) const -> Result<void *> {
if (!m_handle) {
return fail("Library not loaded");
}
Mut<void *> sym = nullptr;
#if IA_PLATFORM_WINDOWS
sym = static_cast<void *>(
GetProcAddress(static_cast<HMODULE>(m_handle), name.c_str()));
if (!sym) {
return fail(get_windows_error());
}
#else
dlerror(); // Clear prev errors
sym = dlsym(m_handle, name.c_str());
if (Const<char *> err = dlerror()) {
return fail(err);
}
#endif
return sym;
}
template <typename FuncT>
IA_NODISCARD auto get_function(Ref<String> name) const -> Result<FuncT> {
Mut<void *> sym = nullptr;
sym = OX_TRY(get_symbol(name));
return reinterpret_cast<FuncT>(sym);
}
void unload() {
if (m_handle) {
#if IA_PLATFORM_WINDOWS
FreeLibrary(static_cast<HMODULE>(m_handle));
#else
dlclose(m_handle);
#endif
m_handle = nullptr;
}
}
IA_NODISCARD auto is_loaded() const -> bool { return m_handle != nullptr; }
private:
Mut<void *> m_handle = nullptr;
#if IA_PLATFORM_WINDOWS
static auto get_windows_error() -> String {
Const<DWORD> error_id = ::GetLastError();
if (error_id == 0) {
return String();
}
Mut<LPSTR> message_buffer = nullptr;
Const<usize> size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPSTR>(&message_buffer), 0, nullptr);
Const<String> message(message_buffer, size);
LocalFree(message_buffer);
return "Win32 Error: " + message;
}
#endif
};
} // namespace IACore

View File

@ -0,0 +1,99 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
#include <cstdlib>
namespace IACore {
class Environment {
public:
static auto find(Ref<String> name) -> Option<String> {
#if IA_PLATFORM_WINDOWS
Const<u32> buffer_size =
static_cast<u32>(GetEnvironmentVariableA(name.c_str(), nullptr, 0));
if (buffer_size == 0) {
return std::nullopt;
}
Mut<String> result;
result.resize(buffer_size);
Const<u32> actual_size = static_cast<u32>(
GetEnvironmentVariableA(name.c_str(), result.data(), buffer_size));
if (actual_size == 0 || actual_size > buffer_size) {
return std::nullopt;
}
result.resize(actual_size);
return result;
#else
Const<char *> val = std::getenv(name.c_str());
if (val == nullptr) {
return std::nullopt;
}
return String(val);
#endif
}
static auto get(Ref<String> name, Ref<String> default_value = "") -> String {
return find(name).value_or(default_value);
}
static auto set(Ref<String> name, Ref<String> value) -> Result<void> {
if (name.empty()) {
return fail("Environment variable name cannot be empty");
}
#if IA_PLATFORM_WINDOWS
if (SetEnvironmentVariableA(name.c_str(), value.c_str()) == 0) {
return fail("Failed to set environment variable: {}", name);
}
#else
if (setenv(name.c_str(), value.c_str(), 1) != 0) {
return fail("Failed to set environment variable: {}", name);
}
#endif
return {};
}
static auto unset(Ref<String> name) -> Result<void> {
if (name.empty()) {
return fail("Environment variable name cannot be empty");
}
#if IA_PLATFORM_WINDOWS
if (SetEnvironmentVariableA(name.c_str(), nullptr) == 0) {
return fail("Failed to unset environment variable: {}", name);
}
#else
if (unsetenv(name.c_str()) != 0) {
return fail("Failed to unset environment variable: {}", name);
}
#endif
return {};
}
static auto exists(Ref<String> name) -> bool {
return find(name).has_value();
}
};
} // namespace IACore

View File

@ -0,0 +1,127 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
#include <IACore/StreamReader.hpp>
#include <IACore/StreamWriter.hpp>
#include <tuple>
#if IA_PLATFORM_WINDOWS
using NativeFileHandle = HANDLE;
static constexpr ox::Const<NativeFileHandle> INVALID_FILE_HANDLE =
INVALID_HANDLE_VALUE;
#else
using NativeFileHandle = int;
static constexpr ox::Const<NativeFileHandle> INVALID_FILE_HANDLE = -1;
#endif
namespace IACore {
class FileOps {
public:
class MemoryMappedRegion;
enum class FileAccess : u8 {
Read, // Read-only
Write, // Write-only
ReadWrite // Read and Write
};
enum class FileMode : u8 {
OpenExisting, // Fails if file doesn't exist
OpenAlways, // Opens if exists, creates if not
CreateNew, // Fails if file exists
CreateAlways, // Overwrites existing
TruncateExisting // Opens existing and clears it
};
static auto native_open_file(Ref<Path> path, Const<FileAccess> access,
Const<FileMode> mode,
Const<u32> permissions = 0644)
-> Result<NativeFileHandle>;
static auto native_close_file(Const<NativeFileHandle> handle) -> void;
public:
static auto normalize_executable_path(Ref<Path> path) -> Path;
public:
static auto unmap_file(Const<const u8 *> mapped_ptr) -> void;
static auto map_file(Ref<Path> path, MutRef<usize> size)
-> Result<const u8 *>;
// @param `is_owner` true to allocate/truncate. false to just open.
static auto map_shared_memory(Ref<String> name, Const<usize> size,
Const<bool> is_owner) -> Result<u8 *>;
static auto unlink_shared_memory(Ref<String> name) -> void;
static auto stream_from_file(Ref<Path> path) -> Result<StreamReader>;
static auto stream_to_file(Ref<Path> path, Const<bool> overwrite = false)
-> Result<StreamWriter>;
static auto read_text_file(Ref<Path> path) -> Result<String>;
static auto read_binary_file(Ref<Path> path) -> Result<Vec<u8>>;
static auto write_text_file(Ref<Path> path, Ref<String> contents,
Const<bool> overwrite = false) -> Result<usize>;
static auto write_binary_file(Ref<Path> path, Const<Span<const u8>> contents,
Const<bool> overwrite = false) -> Result<usize>;
private:
static Mut<HashMap<const u8 *, std::tuple<void *, void *, void *>>>
s_mapped_files;
};
class FileOps::MemoryMappedRegion {
public:
MemoryMappedRegion() = default;
~MemoryMappedRegion();
MemoryMappedRegion(Ref<MemoryMappedRegion>) = delete;
auto operator=(Ref<MemoryMappedRegion>) -> MemoryMappedRegion & = delete;
MemoryMappedRegion(ForwardRef<MemoryMappedRegion> other) noexcept;
auto operator=(ForwardRef<MemoryMappedRegion> other) noexcept
-> MemoryMappedRegion &;
auto map(Const<NativeFileHandle> handle, Const<u64> offset, Const<usize> size)
-> Result<void>;
auto unmap() -> void;
auto flush() -> void;
[[nodiscard]] auto get_ptr() const -> u8 * { return m_ptr; }
[[nodiscard]] auto get_size() const -> usize { return m_size; }
[[nodiscard]] auto is_valid() const -> bool { return m_ptr != nullptr; }
private:
Mut<u8 *> m_ptr = nullptr;
Mut<usize> m_size = 0;
#if IA_PLATFORM_WINDOWS
Mut<HANDLE> m_map_handle = NULL;
#endif
};
} // namespace IACore

View File

@ -0,0 +1,95 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/Http/Common.hpp>
#include <IACore/JSON.hpp>
namespace IACore {
class HttpClient : public HttpCommon {
public:
static auto create(Ref<String> host) -> Result<Box<HttpClient>>;
~HttpClient();
HttpClient(ForwardRef<HttpClient>) = default;
HttpClient(Ref<HttpClient>) = delete;
auto operator=(ForwardRef<HttpClient>) -> MutRef<HttpClient> = default;
auto operator=(Ref<HttpClient>) -> MutRef<HttpClient> = delete;
public:
auto raw_get(Ref<String> path, Span<Const<Header>> headers,
Const<char> *default_content_type =
"application/x-www-form-urlencoded") -> Result<String>;
auto raw_post(Ref<String> path, Span<Const<Header>> headers, Ref<String> body,
Const<char> *default_content_type =
"application/x-www-form-urlencoded") -> Result<String>;
template <typename ResponseType>
auto json_get(Ref<String> path, Span<Const<Header>> headers)
-> Result<ResponseType>;
template <typename PayloadType, typename ResponseType>
auto json_post(Ref<String> path, Span<Const<Header>> headers,
Ref<PayloadType> body) -> Result<ResponseType>;
// Certificate verification is enabled by default
auto enable_certificate_verification() -> void;
auto disable_certificate_verification() -> void;
public:
auto last_response_code() -> EResponseCode { return m_last_response_code; }
private:
Mut<httplib::Client> m_client;
Mut<EResponseCode> m_last_response_code;
private:
auto preprocess_response(Ref<String> response) -> String;
protected:
explicit HttpClient(ForwardRef<httplib::Client> client);
};
template <typename ResponseType>
auto HttpClient::json_get(Ref<String> path, Span<Const<Header>> headers)
-> Result<ResponseType> {
Const<String> raw_response =
OX_TRY(raw_get(path, headers, "application/json"));
if (last_response_code() != EResponseCode::OK) {
return fail("Server responded with code {}",
static_cast<i32>(last_response_code()));
}
return Json::parse_to_struct<ResponseType>(raw_response);
}
template <typename PayloadType, typename ResponseType>
auto HttpClient::json_post(Ref<String> path, Span<Const<Header>> headers,
Ref<PayloadType> body) -> Result<ResponseType> {
Const<String> encoded_body = OX_TRY(Json::encode_struct(body));
Const<String> raw_response =
OX_TRY(raw_post(path, headers, encoded_body, "application/json"));
if (last_response_code() != EResponseCode::OK) {
return fail("Server responded with code {}",
static_cast<i32>(last_response_code()));
}
return Json::parse_to_struct<ResponseType>(raw_response);
}
} // namespace IACore

View File

@ -0,0 +1,155 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/JSON.hpp>
#include <httplib.h>
namespace IACore {
class HttpCommon {
public:
enum class EHeaderType {
ACCEPT,
ACCEPT_CHARSET,
ACCEPT_ENCODING,
ACCEPT_LANGUAGE,
AUTHORIZATION,
CACHE_CONTROL,
CONNECTION,
CONTENT_LENGTH,
CONTENT_TYPE,
COOKIE,
DATE,
EXPECT,
HOST,
IF_MATCH,
IF_MODIFIED_SINCE,
IF_NONE_MATCH,
ORIGIN,
PRAGMA,
PROXY_AUTHORIZATION,
RANGE,
REFERER,
TE,
UPGRADE,
USER_AGENT,
VIA,
WARNING
};
enum class EResponseCode : i32 {
// 1xx Informational
CONTINUE = 100,
SWITCHING_PROTOCOLS = 101,
PROCESSING = 102,
EARLY_HINTS = 103,
// 2xx Success
OK = 200,
CREATED = 201,
ACCEPTED = 202,
NON_AUTHORITATIVE_INFORMATION = 203,
NO_CONTENT = 204,
RESET_CONTENT = 205,
PARTIAL_CONTENT = 206,
MULTI_STATUS = 207,
ALREADY_REPORTED = 208,
IM_USED = 226,
// 3xx Redirection
MULTIPLE_CHOICES = 300,
MOVED_PERMANENTLY = 301,
FOUND = 302,
SEE_OTHER = 303,
NOT_MODIFIED = 304,
USE_PROXY = 305,
TEMPORARY_REDIRECT = 307,
PERMANENT_REDIRECT = 308,
// 4xx Client Error
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
PAYMENT_REQUIRED = 402,
FORBIDDEN = 403,
NOT_FOUND = 404,
METHOD_NOT_ALLOWED = 405,
NOT_ACCEPTABLE = 406,
PROXY_AUTHENTICATION_REQUIRED = 407,
REQUEST_TIMEOUT = 408,
CONFLICT = 409,
GONE = 410,
LENGTH_REQUIRED = 411,
PRECONDITION_FAILED = 412,
PAYLOAD_TOO_LARGE = 413,
URI_TOO_LONG = 414,
UNSUPPORTED_MEDIA_TYPE = 415,
RANGE_NOT_SATISFIABLE = 416,
EXPECTATION_FAILED = 417,
IM_A_TEAPOT = 418,
MISDIRECTED_REQUEST = 421,
UNPROCESSABLE_ENTITY = 422,
LOCKED = 423,
FAILED_DEPENDENCY = 424,
TOO_EARLY = 425,
UPGRADE_REQUIRED = 426,
PRECONDITION_REQUIRED = 428,
TOO_MANY_REQUESTS = 429,
REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
UNAVAILABLE_FOR_LEGAL_REASONS = 451,
// 5xx Server Error
INTERNAL_SERVER_ERROR = 500,
NOT_IMPLEMENTED = 501,
BAD_GATEWAY = 502,
SERVICE_UNAVAILABLE = 503,
GATEWAY_TIMEOUT = 504,
HTTP_VERSION_NOT_SUPPORTED = 505,
VARIANT_ALSO_NEGOTIATES = 506,
INSUFFICIENT_STORAGE = 507,
LOOP_DETECTED = 508,
NOT_EXTENDED = 510,
NETWORK_AUTHENTICATION_REQUIRED = 511
};
using Header = Pair<String, String>;
static auto url_encode(Ref<String> value) -> String;
static auto url_decode(Ref<String> value) -> String;
static auto header_type_to_string(Const<EHeaderType> type) -> String;
static inline auto create_header(Const<EHeaderType> 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;
protected:
HttpCommon() = default;
};
auto HttpCommon::create_header(Const<EHeaderType> key, Ref<String> value)
-> HttpCommon::Header {
return Header{header_type_to_string(key), value};
}
auto HttpCommon::create_header(Ref<String> key, Ref<String> value)
-> HttpCommon::Header {
return Header{key, value};
}
} // namespace IACore

View File

@ -0,0 +1,151 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/Http/Common.hpp>
#include <IACore/JSON.hpp>
#include <functional>
namespace IACore {
class HttpServer : public HttpCommon {
public:
struct Request {
Mut<String> path;
Mut<String> method;
Mut<String> body;
Mut<HashMap<String, String>> headers;
Mut<HashMap<String, String>> params; // Query params
Mut<HashMap<String, String>> path_params; // Path params (like /object/:id)
[[nodiscard]] auto get_header(Ref<String> key) const -> String;
[[nodiscard]] auto get_param(Ref<String> key) const -> String;
[[nodiscard]] auto get_path_param(Ref<String> key) const -> String;
[[nodiscard]] auto has_header(Ref<String> key) const -> bool;
[[nodiscard]] auto has_param(Ref<String> key) const -> bool;
[[nodiscard]] auto has_path_param(Ref<String> key) const -> bool;
};
struct Response {
Mut<EResponseCode> code = EResponseCode::OK;
Mut<String> body;
Mut<HashMap<String, String>> headers;
Mut<String> content_type = "text/plain";
void set_content(Ref<String> content, Ref<String> type);
void set_status(Const<EResponseCode> status_code);
void add_header(Ref<String> key, Ref<String> value);
};
using Handler = std::function<void(Ref<Request>, MutRef<Response>)>;
public:
static auto create() -> Result<Box<HttpServer>>;
~HttpServer();
HttpServer(HttpServer &&) = delete;
HttpServer(const HttpServer &) = delete;
auto operator=(HttpServer &&) -> HttpServer & = delete;
auto operator=(const HttpServer &) -> HttpServer & = delete;
auto listen(Ref<String> host, Const<u32> port) -> Result<void>;
void stop();
auto is_running() const -> bool;
void get(Ref<String> pattern, Const<Handler> handler);
void post(Ref<String> pattern, Const<Handler> handler);
void put(Ref<String> pattern, Const<Handler> handler);
void del(Ref<String> pattern, Const<Handler> handler);
void options(Ref<String> pattern, Const<Handler> handler);
template <typename ResponseType>
void
json_get(Ref<String> pattern,
Const<std::function<Result<ResponseType>(Ref<Request>)>> handler);
template <typename PayloadType, typename ResponseType>
void json_post(
Ref<String> pattern,
Const<std::function<Result<ResponseType>(Ref<PayloadType>)>> handler);
protected:
HttpServer();
private:
Mut<httplib::Server> m_server;
void register_handler(Ref<String> method, Ref<String> pattern,
Const<Handler> handler);
};
template <typename ResponseType>
void HttpServer::json_get(
Ref<String> pattern,
Const<std::function<Result<ResponseType>(Ref<Request>)>> handler) {
get(pattern, [handler](Ref<Request> req, MutRef<Response> res) {
Const<Result<ResponseType>> result = handler(req);
if (!result) {
res.set_status(EResponseCode::INTERNAL_SERVER_ERROR);
res.set_content(result.error(), "text/plain");
return;
}
Const<Result<String>> json_res = Json::encode_struct(*result);
if (!json_res) {
res.set_status(EResponseCode::INTERNAL_SERVER_ERROR);
res.set_content("Failed to encode JSON response", "text/plain");
return;
}
res.set_status(EResponseCode::OK);
res.set_content(*json_res, "application/json");
});
}
template <typename PayloadType, typename ResponseType>
void HttpServer::json_post(
Ref<String> pattern,
Const<std::function<Result<ResponseType>(Ref<PayloadType>)>> handler) {
post(pattern, [handler](Ref<Request> req, MutRef<Response> res) {
Const<Result<PayloadType>> payload =
Json::parse_to_struct<PayloadType>(req.body);
if (!payload) {
res.set_status(EResponseCode::BAD_REQUEST);
res.set_content("Invalid JSON Payload", "text/plain");
return;
}
Const<Result<ResponseType>> result = handler(*payload);
if (!result) {
res.set_status(EResponseCode::INTERNAL_SERVER_ERROR);
res.set_content(result.error(), "text/plain");
return;
}
Const<Result<String>> json_res = Json::encode_struct(*result);
if (!json_res) {
res.set_status(EResponseCode::INTERNAL_SERVER_ERROR);
res.set_content("Failed to encode JSON response", "text/plain");
return;
}
res.set_status(EResponseCode::OK);
res.set_content(*json_res, "application/json");
});
}
} // namespace IACore

View File

@ -0,0 +1,66 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/Logger.hpp>
#include <IACore/PCH.hpp>
#define IACORE_MAIN() \
auto _app_entry(IACore::Ref<IACore::Vec<IACore::String>> args) \
-> IACore::Result<IACore::i32>; \
auto main(Const<int> argc, Mut<char *> argv[]) -> int { \
IACore::Mut<IACore::i32> exit_code = 0; \
IACore::initialize(); \
IACore::Mut<IACore::Vec<IACore::String>> args; \
args.reserve(static_cast<IACore::usize>(argc)); \
for (IACore::Mut<IACore::i32> i = 0; i < argc; ++i) { \
args.push_back(argv[i]); \
} \
IACore::Const<IACore::Result<IACore::i32>> result = _app_entry(args); \
if (!result) { \
IACore::Logger::error("Application exited with an error: '{}'.", \
result.error()); \
exit_code = -20; \
} else { \
exit_code = *result; \
if (exit_code == 0) { \
IACore::Logger::info("Application exited successfully."); \
} else { \
IACore::Logger::error("Application exited with error code: {}.", \
exit_code); \
} \
} \
IACore::terminate(); \
return exit_code; \
} \
auto _app_entry(IACore::Ref<IACore::Vec<IACore::String>> args) \
-> IACore::Result<IACore::i32>
namespace IACore {
// Must be called from main thread
// Safe to call multiple times but, given every initialize call is paired with a
// corresponding terminate call
auto initialize() -> void;
// Must be called from same thread as initialize
// Safe to call multiple times but, given every initialize call is paired with a
// corresponding terminate call
auto terminate() -> void;
auto is_initialized() -> bool;
auto is_main_thread() -> bool;
} // namespace IACore

View File

@ -0,0 +1,293 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
// -----------------------------------------------------------------------------
// Macros
// -----------------------------------------------------------------------------
#define __iat_micro_test(call) \
if (!(call)) \
return false
#define IAT_CHECK(v) __iat_micro_test(_test((v), #v))
#define IAT_CHECK_NOT(v) __iat_micro_test(_test_not((v), "NOT " #v))
#define IAT_CHECK_EQ(lhs, rhs) \
__iat_micro_test(_test_eq((lhs), (rhs), #lhs " == " #rhs))
#define IAT_CHECK_NEQ(lhs, rhs) \
__iat_micro_test(_test_neq((lhs), (rhs), #lhs " != " #rhs))
#define IAT_CHECK_APPROX(lhs, rhs) \
__iat_micro_test(_test_approx((lhs), (rhs), #lhs " ~= " #rhs))
#define IAT_UNIT(func) _test_unit([this]() { return this->func(); }, #func)
#define IAT_NAMED_UNIT(n, func) _test_unit([this]() { return this->func(); }, n)
#define IAT_BLOCK(name) class name : public IACore::Test::Block
#define IAT_BEGIN_BLOCK(_group, _name) \
class _group##_##_name : public IACore::Test::Block { \
public: \
[[nodiscard]] auto get_name() const -> const char * override { \
return #_group "::" #_name; \
} \
\
private:
#define IAT_END_BLOCK() \
} \
;
#define IAT_BEGIN_TEST_LIST() \
public: \
auto declare_tests() -> void override {
#define IAT_ADD_TEST(name) IAT_UNIT(name)
#define IAT_END_TEST_LIST() \
} \
\
private:
namespace IACore::Test {
// -------------------------------------------------------------------------
// String Conversion Helpers
// -------------------------------------------------------------------------
template <typename T> auto to_string(Ref<T> value) -> String {
if constexpr (std::is_arithmetic_v<T>) {
return std::to_string(value);
} else if constexpr (std::is_same_v<T, String> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char *>) {
return String("\"") + String(value) + "\"";
} else {
return "{Object}";
}
}
template <typename T> auto to_string(T *value) -> String {
if (value == nullptr) {
return "nullptr";
}
return std::format("ptr({})", static_cast<const void *>(value));
}
// -------------------------------------------------------------------------
// Types
// -------------------------------------------------------------------------
using TestFunctor = std::function<bool()>;
struct TestUnit {
Mut<String> name;
Mut<TestFunctor> functor;
};
class Block {
public:
virtual ~Block() = default;
[[nodiscard]] virtual auto get_name() const -> const char * = 0;
virtual auto declare_tests() -> void = 0;
auto units() -> MutRef<Vec<TestUnit>> { return m_units; }
protected:
template <typename T1, typename T2>
auto _test_eq(Ref<T1> lhs, Ref<T2> rhs, Const<const char *> description)
-> bool {
if (lhs != rhs) {
print_fail(description, to_string(lhs), to_string(rhs));
return false;
}
return true;
}
template <typename T1, typename T2>
auto _test_neq(Ref<T1> lhs, Ref<T2> rhs, Const<const char *> description)
-> bool {
if (lhs == rhs) {
print_fail(description, to_string(lhs), "NOT " + to_string(rhs));
return false;
}
return true;
}
template <typename T>
auto _test_approx(Const<T> lhs, Const<T> rhs, Const<const char *> description)
-> bool {
static_assert(std::is_floating_point_v<T>,
"Approx only works for floats/doubles");
Const<T> diff = std::abs(lhs - rhs);
if (diff > static_cast<T>(0.0001)) {
print_fail(description, to_string(lhs), to_string(rhs));
return false;
}
return true;
}
auto _test(Const<bool> value, Const<const char *> description) -> bool {
if (!value) {
std::cout << console::BLUE << " " << description << "... "
<< console::RED << "FAILED" << console::RESET << "\n";
return false;
}
return true;
}
auto _test_not(Const<bool> value, Const<const char *> description) -> bool {
if (value) {
std::cout << console::BLUE << " " << description << "... "
<< console::RED << "FAILED" << console::RESET << "\n";
return false;
}
return true;
}
auto _test_unit(Mut<TestFunctor> functor, Const<const char *> name) -> void {
m_units.push_back({name, std::move(functor)});
}
private:
auto print_fail(Const<const char *> desc, Ref<String> v1, Ref<String> v2)
-> void {
std::cout << console::BLUE << " " << desc << "... " << console::RED
<< "FAILED" << console::RESET << "\n";
std::cout << console::RED << " Expected: " << v2 << console::RESET
<< "\n";
std::cout << console::RED << " Actual: " << v1 << console::RESET
<< "\n";
}
Mut<Vec<TestUnit>> m_units;
};
template <typename T>
concept ValidBlockClass = std::derived_from<T, Block>;
// -------------------------------------------------------------------------
// Runner
// -------------------------------------------------------------------------
template <bool StopOnFail = false, bool IsVerbose = false> class Runner {
public:
Runner() = default;
~Runner() { summarize(); }
template <typename BlockClass>
requires ValidBlockClass<BlockClass>
auto test_block() -> void;
private:
auto summarize() -> void;
Mut<usize> m_test_count{0};
Mut<usize> m_fail_count{0};
Mut<usize> m_block_count{0};
};
template <bool StopOnFail, bool IsVerbose>
template <typename BlockClass>
requires ValidBlockClass<BlockClass>
auto Runner<StopOnFail, IsVerbose>::test_block() -> void {
m_block_count++;
Mut<BlockClass> b;
b.declare_tests();
std::cout << console::MAGENTA << "Testing [" << b.get_name() << "]..."
<< console::RESET << "\n";
for (MutRef<TestUnit> v : b.units()) {
m_test_count++;
if constexpr (IsVerbose) {
std::cout << console::YELLOW << " Testing " << v.name << "...\n"
<< console::RESET;
}
Const<bool> result = v.functor();
if (!result) {
m_fail_count++;
if constexpr (StopOnFail) {
summarize();
std::exit(-1);
}
}
}
std::cout << "\n";
}
template <bool StopOnFail, bool IsVerbose>
auto Runner<StopOnFail, IsVerbose>::summarize() -> void {
std::cout << console::GREEN
<< "\n-----------------------------------\n\t "
"SUMMARY\n-----------------------------------\n";
if (m_fail_count == 0) {
std::cout << "\n\tALL TESTS PASSED!\n\n";
} else {
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
<< " TESTS FAILED\n"
<< console::YELLOW
<< std::format("Success Rate: {:.2f}%\n", success_rate);
}
std::cout << console::MAGENTA << "Ran " << m_test_count << " test(s) across "
<< m_block_count << " block(s)\n"
<< console::GREEN << "-----------------------------------"
<< console::RESET << "\n";
}
using DefaultRunner = Runner<false, true>;
// -------------------------------------------------------------------------
// Registry
// -------------------------------------------------------------------------
class TestRegistry {
public:
using TestEntry = std::function<void(MutRef<DefaultRunner>)>;
static auto get_entries() -> MutRef<Vec<TestEntry>> {
static Mut<Vec<TestEntry>> entries;
return entries;
}
static auto run_all() -> i32 {
Mut<DefaultRunner> r;
MutRef<Vec<TestEntry>> entries = get_entries();
std::cout << console::CYAN << "[IATest] Discovered " << entries.size()
<< " Test Blocks\n\n"
<< console::RESET;
for (MutRef<TestEntry> entry : entries) {
entry(r);
}
return 0;
}
};
template <typename BlockType> struct AutoRegister {
AutoRegister() {
TestRegistry::get_entries().push_back(
[](MutRef<DefaultRunner> r) { r.test_block<BlockType>(); });
}
};
} // namespace IACore::Test
#define IAT_REGISTER_ENTRY(Group, Name) \
static IACore::Test::AutoRegister<Group##_##Name> _iat_reg_##Group##_##Name;

View File

@ -0,0 +1,160 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/ADT/RingBuffer.hpp>
#include <IACore/ProcessOps.hpp>
#include <IACore/SocketOps.hpp>
namespace IACore {
using IpcPacketHeader = RingBufferView::PacketHeader;
struct alignas(64) IpcSharedMemoryLayout {
// =========================================================
// METADATA & HANDSHAKE
// =========================================================
struct Header {
Mut<u32> magic; // 0x49414950 ("IAIP")
Mut<u32> version; // 1
Mut<u64> total_size; // Total size of SHM block
};
Mut<Header> meta;
// Pad to ensure MONI starts on a fresh cache line (64 bytes)
Const<Array<u8, 64 - sizeof(Header)>> _pad0;
// =========================================================
// RING BUFFER CONTROL BLOCKS
// =========================================================
// RingBufferView ControlBlock is already 64-byte aligned internally.
Mut<RingBufferView::ControlBlock> moni_control;
Mut<RingBufferView::ControlBlock> mino_control;
// =========================================================
// DATA BUFFER OFFSETS
// =========================================================
Mut<u64> moni_data_offset;
Mut<u64> moni_data_size;
Mut<u64> mino_data_offset;
Mut<u64> mino_data_size;
// Pad to ensure the actual Data Buffer starts on a fresh cache line
Const<Array<u8, 64 - (sizeof(u64) * 4)>> _pad1;
static constexpr auto get_header_size() -> usize {
return sizeof(IpcSharedMemoryLayout);
}
};
// Check padding logic is gucci
static_assert(sizeof(IpcSharedMemoryLayout) % 64 == 0,
"IPC Layout is not cache-line aligned!");
class IpcNode {
public:
virtual ~IpcNode();
// When Manager spawns a node, `connection_string` is passed
// as the first command line argument
auto connect(Const<const char *> connection_string) -> Result<void>;
auto update() -> void;
auto send_signal(Const<u8> signal) -> void;
auto send_packet(Const<u16> packet_id, Const<Span<Const<u8>>> payload)
-> Result<void>;
protected:
virtual auto on_signal(Const<u8> signal) -> void = 0;
virtual auto on_packet(Const<u16> packet_id, Const<Span<Const<u8>>> payload)
-> void = 0;
private:
Mut<String> m_shm_name;
Mut<u8 *> m_shared_memory{};
Mut<Vec<u8>> m_receive_buffer;
Mut<SocketHandle> m_socket{INVALID_SOCKET};
Mut<RingBufferView> m_moni; // Manager Out, Node In
Mut<RingBufferView> m_mino; // Manager In, Node Out
};
class IpcManager {
struct NodeSession {
Mut<std::chrono::system_clock::time_point> creation_time{};
Mut<Box<ProcessHandle>> node_process;
Mut<std::mutex> send_mutex;
Mut<String> shared_mem_name;
Mut<u8 *> mapped_ptr{};
Mut<SocketHandle> listener_socket{INVALID_SOCKET};
Mut<SocketHandle> data_socket{INVALID_SOCKET};
Mut<RingBufferView> moni =
RingBufferView::default_instance(); // Manager Out, Node In
Mut<RingBufferView> mino =
RingBufferView::default_instance(); // Manager In, Node Out
Mut<bool> is_ready{false};
auto send_signal(Const<u8> signal) -> void;
auto send_packet(Const<u16> packet_id, Const<Span<Const<u8>>> payload)
-> Result<void>;
};
public:
static constexpr Const<u32> DEFAULT_NODE_SHARED_MEMORY_SIZE = 4 * 1024 * 1024;
public:
virtual ~IpcManager();
auto update() -> void;
auto
spawn_node(Ref<Path> executable_path,
Const<u32> shared_memory_size = DEFAULT_NODE_SHARED_MEMORY_SIZE)
-> Result<NativeProcessID>;
auto wait_till_node_is_online(Const<NativeProcessID> node) -> bool;
auto shutdown_node(Const<NativeProcessID> node) -> void;
auto send_signal(Const<NativeProcessID> node, Const<u8> signal) -> void;
auto send_packet(Const<NativeProcessID> node, Const<u16> packet_id,
Const<Span<Const<u8>>> payload) -> Result<void>;
protected:
virtual auto on_signal(Const<NativeProcessID> node, Const<u8> signal)
-> void = 0;
virtual auto on_packet(Const<NativeProcessID> node, Const<u16> packet_id,
Const<Span<Const<u8>>> payload) -> void = 0;
private:
Mut<Vec<u8>> m_receive_buffer;
Mut<Vec<Box<NodeSession>>> m_active_sessions;
Mut<Vec<Box<NodeSession>>> m_pending_sessions;
Mut<HashMap<NativeProcessID, NodeSession *>> m_active_session_map;
protected:
IpcManager();
};
} // namespace IACore

View File

@ -0,0 +1,119 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
#include <glaze/glaze.hpp>
#include <nlohmann/json.hpp>
#include <simdjson.h>
namespace IACore {
class JsonDocument {
public:
JsonDocument(ForwardRef<JsonDocument>) noexcept = default;
auto operator=(ForwardRef<JsonDocument>) noexcept
-> MutRef<JsonDocument> = default;
JsonDocument(Ref<JsonDocument>) = delete;
auto operator=(Ref<JsonDocument>) -> MutRef<JsonDocument> = delete;
[[nodiscard]]
auto root() const noexcept -> simdjson::dom::element {
return m_root;
}
private:
friend class Json;
JsonDocument(Mut<Box<simdjson::dom::parser>> p, Mut<simdjson::dom::element> r)
: m_parser(std::move(p)), m_root(r) {}
Mut<Box<simdjson::dom::parser>> m_parser;
Mut<simdjson::dom::element> m_root;
};
class Json {
private:
static constexpr Const<glz::opts> GLAZE_OPTS =
glz::opts{.error_on_unknown_keys = false};
public:
static auto parse(Ref<String> json_str) -> Result<nlohmann::json>;
static auto encode(Ref<nlohmann::json> data) -> String;
static auto parse_read_only(Ref<String> json_str) -> Result<JsonDocument>;
template <typename T>
static auto parse_to_struct(Ref<String> json_str) -> Result<T>;
template <typename T>
static auto encode_struct(Ref<T> data) -> Result<String>;
};
inline auto Json::parse(Ref<String> json_str) -> Result<nlohmann::json> {
Const<nlohmann::json> res =
nlohmann::json::parse(json_str, nullptr, false, true);
if (res.is_discarded()) {
return fail("Failed to parse JSON (Invalid Syntax)");
}
return res;
}
inline auto Json::parse_read_only(Ref<String> json_str)
-> Result<JsonDocument> {
Mut<Box<simdjson::dom::parser>> parser = make_box<simdjson::dom::parser>();
Mut<simdjson::dom::element> root;
Const<simdjson::error_code> error = parser->parse(json_str).get(root);
if (error) {
return fail("JSON Error: {}", simdjson::error_message(error));
}
return JsonDocument(std::move(parser), root);
}
inline auto Json::encode(Ref<nlohmann::json> data) -> String {
return data.dump();
}
template <typename T>
inline auto Json::parse_to_struct(Ref<String> json_str) -> Result<T> {
Mut<T> result{};
Const<glz::error_ctx> err = glz::read<GLAZE_OPTS>(result, json_str);
if (err) {
return fail("JSON Struct Parse Error: {}",
glz::format_error(err, json_str));
}
return result;
}
template <typename T>
inline auto Json::encode_struct(Ref<T> data) -> Result<String> {
Mut<String> result;
Const<glz::error_ctx> err = glz::write_json(data, result);
if (err) {
return fail("JSON Struct Encode Error");
}
return result;
}
} // namespace IACore

View File

@ -0,0 +1,123 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
#define IA_LOG_SET_FILE(path) IACore::Logger::enable_logging_to_disk(path)
#define IA_LOG_SET_LEVEL(level) \
IACore::Logger::set_log_level(IACore::Logger::LogLevel::level)
#define IA_LOG_TRACE(...) IACore::Logger::trace(__VA_ARGS__)
#define IA_LOG_DEBUG(...) IACore::Logger::debug(__VA_ARGS__)
#define IA_LOG_INFO(...) IACore::Logger::info(__VA_ARGS__)
#define IA_LOG_WARN(...) IACore::Logger::warn(__VA_ARGS__)
#define IA_LOG_ERROR(...) IACore::Logger::error(__VA_ARGS__)
namespace IACore {
class Logger {
public:
enum class LogLevel { Trace, Debug, Info, Warn, Error };
public:
static auto enable_logging_to_disk(Const<const char *> file_path)
-> Result<void>;
static auto set_log_level(Const<LogLevel> log_level) -> void;
template <typename... Args>
static auto trace(Const<std::format_string<Args...>> fmt,
ForwardRef<Args>... args) -> void {
log_trace(std::vformat(fmt.get(), std::make_format_args(args...)));
}
template <typename... Args>
static auto debug(Const<std::format_string<Args...>> fmt,
ForwardRef<Args>... args) -> void {
log_debug(std::vformat(fmt.get(), std::make_format_args(args...)));
}
template <typename... Args>
static auto info(Const<std::format_string<Args...>> fmt,
ForwardRef<Args>... args) -> void {
log_info(std::vformat(fmt.get(), std::make_format_args(args...)));
}
template <typename... Args>
static auto warn(Const<std::format_string<Args...>> fmt,
ForwardRef<Args>... args) -> void {
log_warn(std::vformat(fmt.get(), std::make_format_args(args...)));
}
template <typename... Args>
static auto error(Const<std::format_string<Args...>> fmt,
ForwardRef<Args>... args) -> void {
log_error(std::vformat(fmt.get(), std::make_format_args(args...)));
}
static auto flush_logs() -> void;
private:
#if IA_DISABLE_LOGGING > 0
static auto log_trace(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_warn(ForwardRef<String> msg) -> void { IA_UNUSED(msg); }
static auto log_error(ForwardRef<String> msg) -> void { IA_UNUSED(msg); }
#else
static auto log_trace(ForwardRef<String> msg) -> void {
if (m_log_level <= LogLevel::Trace)
log_internal(console::RESET, "TRACE", std::move(msg));
}
static auto log_debug(ForwardRef<String> msg) -> void {
if (m_log_level <= LogLevel::Debug)
log_internal(console::CYAN, "DEBUG", std::move(msg));
}
static auto log_info(ForwardRef<String> msg) -> void {
if (m_log_level <= LogLevel::Info)
log_internal(console::GREEN, "INFO", std::move(msg));
}
static auto log_warn(ForwardRef<String> msg) -> void {
if (m_log_level <= LogLevel::Warn)
log_internal(console::YELLOW, "WARN", std::move(msg));
}
static auto log_error(ForwardRef<String> msg) -> void {
if (m_log_level <= LogLevel::Error)
log_internal(console::RED, "ERROR", std::move(msg));
}
#endif
static auto log_internal(Const<const char *> prefix, Const<const char *> tag,
ForwardRef<String> msg) -> void;
private:
static Mut<LogLevel> m_log_level;
static Mut<std::ofstream> m_log_file;
static auto initialize() -> void;
static auto terminate() -> void;
friend void initialize();
friend void terminate();
};
} // namespace IACore

View File

@ -0,0 +1,135 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#if defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64)
#define IA_ARCH_X64 1
#elif defined(__aarch64__) || defined(_M_ARM64)
#define IA_ARCH_ARM64 1
#elif defined(__wasm__) || defined(__wasm32__) || defined(__wasm64__)
#define IA_ARCH_WASM 1
#else
#error "IACore: Unsupported Architecture."
#endif
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
#define IA_PLATFORM_WINDOWS 1
#elif __APPLE__
#include <TargetConditionals.h>
#define IA_PLATFORM_APPLE 1
#define IA_PLATFORM_UNIX 1
#elif __linux__
#define IA_PLATFORM_LINUX 1
#define IA_PLATFORM_UNIX 1
#elif __wasm__
#define IA_PLATFORM_WASM 1
#else
#error "IACore: Unsupported Platform."
#endif
#if IA_PLATFORM_WINDOWS
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#elif IA_PLATFORM_UNIX
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
#endif
#include <atomic>
#include <cmath>
#include <condition_variable>
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <iostream>
#include <limits>
#include <memory>
#include <mutex>
#include <thread>
#include <ankerl/unordered_dense.h>
#include <oxide/oxide.hpp>
namespace IACore {
using namespace Oxide;
// =============================================================================
// Build Environment & Constants
// =============================================================================
namespace Env {
using namespace Oxide::Env;
#if IA_PLATFORM_WINDOWS
constexpr Const<bool> IS_WINDOWS = true;
constexpr Const<bool> IS_UNIX = false;
#else
constexpr Const<bool> IS_WINDOWS = false;
constexpr Const<bool> IS_UNIX = true;
#endif
constexpr Const<usize> MAX_PATH_LEN = 4096;
} // namespace Env
// =============================================================================
// Data Structures & Aliases
// =============================================================================
template <typename K, typename V>
using HashMap = ankerl::unordered_dense::map<K, V>;
template <typename T> using HashSet = ankerl::unordered_dense::set<T>;
using Path = std::filesystem::path;
// =============================================================================
// Versioning
// =============================================================================
struct Version {
u32 major = 0;
u32 minor = 0;
u32 patch = 0;
[[nodiscard]] constexpr auto to_u64() const -> u64 {
return (static_cast<u64>(major) << 40) | (static_cast<u64>(minor) << 16) |
(static_cast<u64>(patch));
}
};
// =============================================================================
// Console Colors
// =============================================================================
namespace console {
constexpr Const<const char *> RESET = "\033[0m";
constexpr Const<const char *> RED = "\033[31m";
constexpr Const<const char *> GREEN = "\033[32m";
constexpr Const<const char *> YELLOW = "\033[33m";
constexpr Const<const char *> BLUE = "\033[34m";
constexpr Const<const char *> MAGENTA = "\033[35m";
constexpr Const<const char *> CYAN = "\033[36m";
} // namespace console
} // namespace IACore
#define IA_NODISCARD [[nodiscard]]
#define IA_UNUSED(v) (void)(v)

View File

@ -0,0 +1,52 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
#if IA_ARCH_X64
#ifdef _MSC_VER
#include <intrin.h>
#else
#include <immintrin.h>
#endif
#elif IA_ARCH_ARM64
#include <arm_acle.h>
#endif
namespace IACore {
class Platform {
public:
struct Capabilities {
Mut<bool> hardware_crc32 = false;
};
static auto check_cpu() -> bool;
#if IA_ARCH_X64
static auto cpuid(Const<i32> function, Const<i32> sub_function,
Mut<i32 *> out) -> void;
#endif
static auto get_architecture_name() -> const char *;
static auto get_operating_system_name() -> const char *;
static auto get_capabilities() -> Ref<Capabilities> { return s_capabilities; }
private:
static Mut<Capabilities> s_capabilities;
};
} // namespace IACore

View File

@ -0,0 +1,69 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
#if IA_PLATFORM_WINDOWS
using NativeProcessID = DWORD;
#elif IA_PLATFORM_UNIX
using NativeProcessID = pid_t;
#else
#error "This platform does not support IACore ProcessOps"
#endif
namespace IACore {
struct ProcessHandle {
Mut<std::atomic<NativeProcessID>> id{0};
Mut<std::atomic<bool>> is_running{false};
[[nodiscard]] auto is_active() const -> bool { return is_running && id != 0; }
private:
Mut<std::jthread> m_thread_handle;
friend class ProcessOps;
};
class ProcessOps {
public:
static auto get_current_process_id() -> NativeProcessID;
static auto spawn_process_sync(
Ref<String> command, Ref<String> args,
Const<std::function<void(Const<StringView>)>> on_output_line_callback)
-> Result<i32>;
static auto spawn_process_async(
Ref<String> command, Ref<String> args,
Const<std::function<void(Const<StringView>)>> on_output_line_callback,
Const<std::function<void(Const<Result<i32>>)>> on_finish_callback)
-> Result<Box<ProcessHandle>>;
static auto terminate_process(Ref<Box<ProcessHandle>> handle) -> void;
private:
static auto spawn_process_windows(
Ref<String> command, Ref<String> args,
Const<std::function<void(Const<StringView>)>> on_output_line_callback,
MutRef<std::atomic<NativeProcessID>> id) -> Result<i32>;
static auto spawn_process_posix(
Ref<String> command, Ref<String> args,
Const<std::function<void(Const<StringView>)>> on_output_line_callback,
MutRef<std::atomic<NativeProcessID>> id) -> Result<i32>;
};
} // namespace IACore

View File

@ -0,0 +1,280 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
#if defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
#endif
#include <hwy/highway.h>
#if defined(__clang__)
#pragma GCC diagnostic pop
#endif
namespace IACore {
namespace hn = hwy::HWY_NAMESPACE;
#if HWY_TARGET == HWY_SCALAR
#pragma message( \
"Warning: Configuration mismatch. IACore is being compiled for SCALAR SIMD (Slow)")
#endif
class alignas(16) IntVec4 {
public:
IntVec4() = default;
inline explicit IntVec4(Const<u32> s);
inline explicit IntVec4(Const<const u32 *> values);
inline explicit IntVec4(Const<u32> a, Const<u32> b, Const<u32> c,
Const<u32> d);
inline auto operator+(Ref<IntVec4> other) const -> IntVec4;
inline auto operator-(Ref<IntVec4> other) const -> IntVec4;
inline auto operator*(Ref<IntVec4> other) const -> IntVec4;
inline auto operator&(Ref<IntVec4> other) const -> IntVec4;
inline auto operator|(Ref<IntVec4> other) const -> IntVec4;
inline auto operator^(Ref<IntVec4> other) const -> IntVec4;
inline auto operator~() const -> IntVec4;
inline auto operator<<(Const<u32> amount) const -> IntVec4;
inline auto operator>>(Const<u32> amount) 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 clamp(Const<u32> min, Const<u32> max) const
-> IntVec4;
[[nodiscard]] inline auto mult_add(Ref<IntVec4> multiplier,
Ref<IntVec4> addend) const -> IntVec4;
inline auto store(Mut<u32 *> values) -> void;
static inline auto load(Const<const u32 *> values) -> IntVec4;
private:
using Tag = hn::FixedTag<u32, 4>;
Mut<hn::Vec<Tag>> m_data;
inline explicit IntVec4(Const<hn::Vec<Tag>> v) : m_data(v) {}
};
class alignas(16) FloatVec4 {
public:
FloatVec4() = default;
inline explicit FloatVec4(Const<f32> s);
inline explicit FloatVec4(Const<const f32 *> values);
inline explicit FloatVec4(Const<f32> a, Const<f32> b, Const<f32> c,
Const<f32> d);
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
-> FloatVec4;
[[nodiscard]] inline auto abs() const -> FloatVec4;
[[nodiscard]] inline auto sqrt() const -> FloatVec4;
[[nodiscard]] inline auto rsqrt() const -> FloatVec4;
[[nodiscard]] inline auto normalize() const -> FloatVec4;
[[nodiscard]] inline auto dot(Ref<FloatVec4> other) const -> f32;
[[nodiscard]] inline auto mult_add(Ref<FloatVec4> multiplier,
Ref<FloatVec4> addend) const -> FloatVec4;
inline auto store(Mut<f32 *> values) -> void;
static inline auto load(Const<const f32 *> values) -> FloatVec4;
private:
using Tag = hn::FixedTag<f32, 4>;
Mut<hn::Vec<Tag>> m_data;
inline explicit FloatVec4(Const<hn::Vec<Tag>> v) : m_data(v) {}
};
} // namespace IACore
namespace IACore {
IntVec4::IntVec4(Const<u32> s) {
Const<Tag> d;
m_data = hn::Set(d, s);
}
IntVec4::IntVec4(Const<const u32 *> values) {
Const<Tag> data;
m_data = hn::Load(data, values);
}
IntVec4::IntVec4(Const<u32> a, Const<u32> b, Const<u32> c, Const<u32> d) {
Const<Tag> data;
alignas(16) Mut<Array<u32, 4>> values = {a, b, c, d};
m_data = hn::Load(data, values.data());
}
auto IntVec4::operator+(Ref<IntVec4> other) const -> IntVec4 {
return IntVec4(hn::Add(m_data, other.m_data));
}
auto IntVec4::operator-(Ref<IntVec4> other) const -> IntVec4 {
return IntVec4(hn::Sub(m_data, other.m_data));
}
auto IntVec4::operator*(Ref<IntVec4> other) const -> IntVec4 {
return IntVec4(hn::Mul(m_data, other.m_data));
}
auto IntVec4::operator&(Ref<IntVec4> other) const -> IntVec4 {
return IntVec4(hn::And(m_data, other.m_data));
}
auto IntVec4::operator|(Ref<IntVec4> other) const -> IntVec4 {
return IntVec4(hn::Or(m_data, other.m_data));
}
auto IntVec4::operator^(Ref<IntVec4> other) const -> IntVec4 {
return IntVec4(hn::Xor(m_data, other.m_data));
}
auto IntVec4::operator~() const -> IntVec4 { return IntVec4(hn::Not(m_data)); }
auto IntVec4::operator<<(Const<u32> amount) const -> IntVec4 {
return IntVec4(hn::ShiftLeftSame(m_data, amount));
}
auto IntVec4::operator>>(Const<u32> amount) const -> IntVec4 {
return IntVec4(hn::ShiftRightSame(m_data, amount));
}
auto IntVec4::mult_add(Ref<IntVec4> multiplier, Ref<IntVec4> addend) const
-> IntVec4 {
return IntVec4(hn::MulAdd(m_data, multiplier.m_data, addend.m_data));
}
auto IntVec4::sat_add(Ref<IntVec4> other) const -> IntVec4 {
return IntVec4(hn::SaturatedAdd(m_data, other.m_data));
}
auto IntVec4::sat_sub(Ref<IntVec4> other) const -> IntVec4 {
return IntVec4(hn::SaturatedSub(m_data, other.m_data));
}
auto IntVec4::clamp(Const<u32> min, Const<u32> max) const -> IntVec4 {
Const<Tag> d;
Const<hn::Vec<Tag>> v_min = hn::Set(d, min);
Const<hn::Vec<Tag>> v_max = hn::Set(d, max);
return IntVec4(hn::Min(hn::Max(m_data, v_min), v_max));
}
auto IntVec4::store(Mut<u32 *> values) -> void {
Const<Tag> d;
hn::Store(m_data, d, values);
}
auto IntVec4::load(Const<const u32 *> values) -> IntVec4 {
Const<Tag> d;
return IntVec4(hn::Load(d, values));
}
} // namespace IACore
namespace IACore {
FloatVec4::FloatVec4(Const<f32> s) {
Const<Tag> d;
m_data = hn::Set(d, s);
}
FloatVec4::FloatVec4(Const<const f32 *> values) {
Const<Tag> d;
m_data = hn::Load(d, values);
}
FloatVec4::FloatVec4(Const<f32> a, Const<f32> b, Const<f32> c, Const<f32> d) {
Const<Tag> data;
alignas(16) Mut<Array<f32, 4>> temp = {a, b, c, d};
m_data = hn::Load(data, temp.data());
}
auto FloatVec4::operator+(Ref<FloatVec4> other) const -> FloatVec4 {
return FloatVec4(hn::Add(m_data, other.m_data));
}
auto FloatVec4::operator-(Ref<FloatVec4> other) const -> FloatVec4 {
return FloatVec4(hn::Sub(m_data, other.m_data));
}
auto FloatVec4::operator*(Ref<FloatVec4> other) const -> FloatVec4 {
return FloatVec4(hn::Mul(m_data, other.m_data));
}
auto FloatVec4::operator/(Ref<FloatVec4> other) const -> FloatVec4 {
return FloatVec4(hn::Div(m_data, other.m_data));
}
auto FloatVec4::mult_add(Ref<FloatVec4> multiplier, Ref<FloatVec4> addend) const
-> FloatVec4 {
return FloatVec4(hn::MulAdd(m_data, multiplier.m_data, addend.m_data));
}
auto FloatVec4::clamp(Const<f32> min, Const<f32> max) const -> FloatVec4 {
Const<Tag> d;
Const<hn::Vec<Tag>> v_min = hn::Set(d, min);
Const<hn::Vec<Tag>> v_max = hn::Set(d, max);
return FloatVec4(hn::Min(hn::Max(m_data, v_min), v_max));
}
auto FloatVec4::sqrt() const -> FloatVec4 {
return FloatVec4(hn::Sqrt(m_data));
}
auto FloatVec4::rsqrt() const -> FloatVec4 {
return FloatVec4(hn::ApproximateReciprocalSqrt(m_data));
}
auto FloatVec4::abs() const -> FloatVec4 { return FloatVec4(hn::Abs(m_data)); }
auto FloatVec4::dot(Ref<FloatVec4> other) const -> f32 {
Const<Tag> d;
Const<hn::Vec<Tag>> v_mul = hn::Mul(m_data, other.m_data);
return hn::ReduceSum(d, v_mul);
}
auto FloatVec4::normalize() const -> FloatVec4 {
Const<Tag> d;
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_inv_len = hn::ApproximateReciprocalSqrt(v_len_sq);
return FloatVec4(hn::Mul(m_data, v_inv_len));
}
auto FloatVec4::store(Mut<f32 *> values) -> void {
Const<Tag> d;
hn::Store(m_data, d, values);
}
auto FloatVec4::load(Const<const f32 *> values) -> FloatVec4 {
Const<Tag> d;
return FloatVec4(hn::Load(d, values));
}
} // namespace IACore

View File

@ -0,0 +1,116 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
#if IA_PLATFORM_WINDOWS
#include <afunix.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#elif IA_PLATFORM_UNIX
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif
#else
#error "IACore SocketOps is not supported on this platform."
#endif
namespace IACore {
#if IA_PLATFORM_WINDOWS
using SocketHandle = SOCKET;
#elif IA_PLATFORM_UNIX
using SocketHandle = i32;
#endif
class SocketOps {
public:
// SocketOps correctly handles multiple calls to initialize and terminate.
// Make sure every initialize call is paired with a corresponding terminate
// call.
static auto initialize() -> Result<void> {
s_init_count++;
if (s_init_count > 1) {
return {};
}
#if IA_PLATFORM_WINDOWS
Mut<WSADATA> wsa_data;
Const<i32> res = WSAStartup(MAKEWORD(2, 2), &wsa_data);
if (res != 0) {
s_init_count--;
return fail("WSAStartup failed with error: {}", res);
}
#endif
return {};
}
// SocketOps correctly handles multiple calls to initialize and terminate.
// Make sure every initialize call is paired with a corresponding terminate
// call.
static auto terminate() -> void {
s_init_count--;
if (s_init_count > 0) {
return;
}
#if IA_PLATFORM_WINDOWS
WSACleanup();
#endif
}
static auto is_initialized() -> bool { return s_init_count > 0; }
static auto is_port_available_tcp(Const<u16> port) -> bool {
return is_port_available(port, SOCK_STREAM);
}
static auto is_port_available_udp(Const<u16> port) -> bool {
return is_port_available(port, SOCK_DGRAM);
}
static auto is_would_block() -> bool;
static auto close(Const<SocketHandle> sock) -> void;
static auto listen(Const<SocketHandle> sock, Const<i32> queue_size = 5)
-> Result<void>;
static auto create_unix_socket() -> Result<SocketHandle>;
static auto bind_unix_socket(Const<SocketHandle> sock,
Const<const char *> path) -> Result<void>;
static auto connect_unix_socket(Const<SocketHandle> sock,
Const<const char *> path) -> Result<void>;
static auto unlink_file(Const<const char *> path) -> void {
#if IA_PLATFORM_WINDOWS
DeleteFileA(path);
#elif IA_PLATFORM_UNIX
unlink(path);
#endif
}
private:
static auto is_port_available(Const<u16> port, Const<i32> type) -> bool;
private:
static Mut<i32> s_init_count;
};
} // namespace IACore

View File

@ -0,0 +1,106 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
#include <algorithm>
#include <cstring>
namespace IACore {
class StreamReader {
public:
enum class StorageType {
NonOwning,
OwningMmap,
OwningVector,
};
static auto create_from_file(Ref<Path> path) -> Result<StreamReader>;
explicit StreamReader(ForwardRef<Vec<u8>> data);
explicit StreamReader(Const<Span<const u8>> data);
~StreamReader();
StreamReader(ForwardRef<StreamReader> other);
auto operator=(ForwardRef<StreamReader> other) -> MutRef<StreamReader>;
StreamReader(Ref<StreamReader>) = delete;
auto operator=(Ref<StreamReader>) -> MutRef<StreamReader> = delete;
auto read(Mut<void *> buffer, Const<usize> size) -> Result<void>;
template <typename T>
[[nodiscard("Check for EOF")]]
auto read() -> Result<T>;
auto skip(Const<usize> amount) -> void {
m_cursor = std::min(m_cursor + amount, m_data_size);
}
auto seek(Const<usize> pos) -> void {
m_cursor = (pos > m_data_size) ? m_data_size : pos;
}
[[nodiscard]] auto cursor() const -> usize { return m_cursor; }
[[nodiscard]] auto size() const -> usize { return m_data_size; }
[[nodiscard]] auto remaining() const -> usize {
return m_data_size - m_cursor;
}
[[nodiscard]] auto is_eof() const -> bool { return m_cursor >= m_data_size; }
private:
Mut<const u8 *> m_data = nullptr;
Mut<usize> m_cursor = 0;
Mut<usize> m_data_size = 0;
Mut<Vec<u8>> m_owning_vector;
Mut<StorageType> m_storage_type = StorageType::NonOwning;
};
inline auto StreamReader::read(Mut<void *> buffer, Const<usize> size)
-> Result<void> {
if (m_cursor + size > m_data_size) [[unlikely]] {
return fail("Unexpected EOF while reading");
}
std::memcpy(buffer, &m_data[m_cursor], size);
m_cursor += size;
return {};
}
template <typename T>
[[nodiscard("Check for EOF")]]
inline auto StreamReader::read() -> Result<T> {
static_assert(std::is_trivially_copyable_v<T>,
"T must be trivially copyable to read via memcpy");
constexpr Const<usize> SIZE = sizeof(T);
if (m_cursor + SIZE > m_data_size) [[unlikely]] {
return fail("Unexpected EOF while reading");
}
Mut<T> value;
std::memcpy(&value, &m_data[m_cursor], SIZE);
m_cursor += SIZE;
return value;
}
} // namespace IACore

View File

@ -0,0 +1,71 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
namespace IACore {
class StreamWriter {
public:
enum class StorageType {
NonOwning,
OwningFile,
OwningVector,
};
static auto create_from_file(Ref<Path> path) -> Result<StreamWriter>;
StreamWriter();
explicit StreamWriter(Const<Span<u8>> data);
StreamWriter(ForwardRef<StreamWriter> other);
auto operator=(ForwardRef<StreamWriter> other) -> MutRef<StreamWriter>;
StreamWriter(Ref<StreamWriter>) = delete;
auto operator=(Ref<StreamWriter>) -> MutRef<StreamWriter> = delete;
~StreamWriter();
auto write(Const<u8> byte, Const<usize> count) -> Result<void>;
auto write(Const<const void *> buffer, Const<usize> size) -> Result<void>;
template <typename T> auto write(Ref<T> value) -> Result<void>;
[[nodiscard]] auto data() const -> const u8 * { return m_buffer; }
[[nodiscard]] auto cursor() const -> usize { return m_cursor; }
auto flush() -> Result<void>;
private:
Mut<u8 *> m_buffer = nullptr;
Mut<usize> m_cursor = 0;
Mut<usize> m_capacity = 0;
Mut<Path> m_file_path;
Mut<Vec<u8>> m_owning_vector;
Mut<StorageType> m_storage_type = StorageType::OwningVector;
private:
auto flush_to_disk() -> Result<void>;
};
template <typename T>
inline auto StreamWriter::write(Ref<T> value) -> Result<void> {
return write(&value, sizeof(T));
}
} // namespace IACore

View File

@ -0,0 +1,26 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
namespace IACore {
class StringOps {
public:
static auto encode_base64(Const<Span<Const<u8>>> data) -> String;
static auto decode_base64(Ref<String> data) -> Vec<u8>;
};
} // namespace IACore

View File

@ -0,0 +1,107 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
#include <algorithm>
namespace IACore {
class Utils {
public:
static auto get_unix_time() -> u64;
static auto get_ticks_count() -> u64;
static auto get_seconds_count() -> f64;
static auto get_random() -> f32;
static auto get_random(Const<u64> max) -> u64;
static auto get_random(Const<i64> min, Const<i64> max) -> i64;
static auto sleep(Const<u64> milliseconds) -> void;
static auto binary_to_hex_string(Const<Span<Const<u8>>> data) -> String;
static auto hex_string_to_binary(Const<StringView> hex) -> Result<Vec<u8>>;
template <typename Range>
inline static auto sort(ForwardRef<Range> range) -> void {
std::ranges::sort(std::forward<Range>(range));
}
template <typename Range, typename T>
inline static auto binary_search_left(ForwardRef<Range> range, Ref<T> value)
-> auto {
return std::ranges::lower_bound(std::forward<Range>(range), value);
}
template <typename Range, typename T>
inline static auto binary_search_right(ForwardRef<Range> range, Ref<T> value)
-> auto {
return std::ranges::upper_bound(std::forward<Range>(range), value);
}
template <typename T>
inline static auto hash_combine(MutRef<u64> seed, Ref<T> v) -> void {
Mut<u64> h = 0;
if constexpr (std::is_constructible_v<StringView, T>) {
Const<StringView> sv(v);
Const<ankerl::unordered_dense::hash<StringView>> hasher;
h = hasher(sv);
} else {
Const<ankerl::unordered_dense::hash<T>> hasher;
h = hasher(v);
}
seed ^= h + 0x9e3779b97f4a7c15 + (seed << 6) + (seed >> 2);
}
template <typename... Args>
inline static auto compute_hash(Ref<Args>... args) -> u64 {
Mut<u64> seed = 0;
(hash_combine(seed, args), ...);
return seed;
}
template <typename T, typename... MemberPtrs>
inline static auto compute_hash_flat(Ref<T> obj, Const<MemberPtrs>... members)
-> u64 {
Mut<u64> seed = 0;
(hash_combine(seed, obj.*members), ...);
return seed;
}
};
} // namespace IACore
// -----------------------------------------------------------------------------
// MACRO: IA_MAKE_HASHABLE
//
// Injects the specialization for ankerl::unordered_dense::hash.
//
// Usage:
// struct Vector3 { float x, y, z; };
// IA_MAKE_HASHABLE(Vector3, &Vector3::x, &Vector3::y, &Vector3::z)
// -----------------------------------------------------------------------------
#define IA_MAKE_HASHABLE(Type, ...) \
template <> struct ankerl::unordered_dense::hash<Type> { \
using is_avalanching = void; \
IA_NODISCARD \
auto operator()(IACore::Ref<Type> v) const noexcept -> IACore::u64 { \
return IACore::Utils::compute_hash_flat(v, __VA_ARGS__); \
} \
};

View File

@ -0,0 +1,39 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2026 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <IACore/PCH.hpp>
#include <pugixml.hpp>
namespace IACore {
class XML {
public:
using Node = pugi::xml_node;
using Document = pugi::xml_document;
public:
static auto parse_from_string(Ref<String> data) -> Result<Document>;
static auto parse_from_file(Ref<Path> path) -> Result<Document>;
static auto serialize_to_string(Ref<Node> node, 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;
};
} // namespace IACore