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

32
.clang-format Normal file
View File

@ -0,0 +1,32 @@
---
Language: Cpp
Standard: c++20
BasedOnStyle: Microsoft
IndentWidth: 4
TabWidth: 4
UseTab: Never
AccessModifierOffset: -4
IndentPPDirectives: AfterHash
IndentRequiresClause: true
BreakBeforeConceptDeclarations: true
SpaceAfterTemplateKeyword: false
FixNamespaceComments: true
NamespaceIndentation: All
CompactNamespaces: true
AlignAfterOpenBracket: Align
AlignOperands: Align
AlignTrailingComments: true
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceBeforeAssignmentOperators: true
SpaceInEmptyParentheses: false
SpacesInAngles: false
SeparateDefinitionBlocks: Always
SortIncludes: false
---

78
.clang-tidy Normal file
View File

@ -0,0 +1,78 @@
Checks: >
-*,
readability-identifier-naming,
readability-const-return-type,
modernize-use-nodiscard,
modernize-use-override,
modernize-use-nullptr,
bugprone-use-after-move
CheckOptions:
# ----------------------------------------------------------------------------
# Types (PascalCase) - Matches Rust structs/enums
# ----------------------------------------------------------------------------
- key: readability-identifier-naming.ClassCase
value: PascalCase
- key: readability-identifier-naming.StructCase
value: PascalCase
- key: readability-identifier-naming.TypedefCase
value: PascalCase
- key: readability-identifier-naming.EnumCase
value: PascalCase
- key: readability-identifier-naming.TemplateParameterCase
value: PascalCase
# ----------------------------------------------------------------------------
# Functions & Methods (snake_case) - Matches Rust fn
# ----------------------------------------------------------------------------
- key: readability-identifier-naming.FunctionCase
value: lower_case
- key: readability-identifier-naming.MethodCase
value: lower_case
- key: readability-identifier-naming.ParameterCase
value: lower_case
# ----------------------------------------------------------------------------
# Variables (snake_case) - Matches Rust let
# ----------------------------------------------------------------------------
- key: readability-identifier-naming.VariableCase
value: lower_case
- key: readability-identifier-naming.LocalVariableCase
value: lower_case
# ----------------------------------------------------------------------------
# Members (m_snake_case for private, snake_case for public)
# ----------------------------------------------------------------------------
# Public struct members (like a Rust struct) -> x, y, width
- key: readability-identifier-naming.PublicMemberCase
value: lower_case
- key: readability-identifier-naming.StructMemberIgnoredRegexp
value: ^_[a-z0-9_]*$
# Private/Protected class members -> m_parser, m_count
- key: readability-identifier-naming.PrivateMemberCase
value: lower_case
- key: readability-identifier-naming.PrivateMemberPrefix
value: m_
- key: readability-identifier-naming.ProtectedMemberCase
value: lower_case
- key: readability-identifier-naming.ProtectedMemberPrefix
value: m_
# ----------------------------------------------------------------------------
# Constants (SCREAMING_SNAKE_CASE)
# ----------------------------------------------------------------------------
- key: readability-identifier-naming.GlobalConstantCase
value: UPPER_CASE
- key: readability-identifier-naming.ConstexprVariableCase
value: UPPER_CASE
- key: readability-identifier-naming.EnumConstantCase
value: PascalCase
# Note: Rust uses PascalCase for Enum Variants (Option::None).
# Change to UPPER_CASE if you prefer C-style enums.
# ----------------------------------------------------------------------------
# Macros (SCREAMING_SNAKE_CASE)
# ----------------------------------------------------------------------------
- key: readability-identifier-naming.MacroDefinitionCase
value: UPPER_CASE

3
.clangd Normal file
View File

@ -0,0 +1,3 @@
CompileFlags:
Add: [-Wno-missing-field-initializers, -Wno-missing-designated-field-initializers]

31
.github/workflows/ci.yaml vendored Normal file
View File

@ -0,0 +1,31 @@
name: CI
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build-linux-and-wasm:
runs-on: ubuntu-latest
permissions:
contents: read
packages: read
container:
image: ghcr.io/i-a-s/iabuild-env:latest
strategy:
fail-fast: false
matrix:
target: [x64-linux]
steps:
- uses: actions/checkout@v4
- name: Install OpenSSL v3 (System)
run: sudo dnf install -y openssl-devel
- name: Configure
run: cmake --preset ${{ matrix.target }}
- name: Build
run: cmake --build --preset ${{ matrix.target }} --config Release

54
.gitignore vendored Normal file
View File

@ -0,0 +1,54 @@
# ---> C++
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# ---> VisualStudioCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
[Ss]andbox/
.cache/
.local/
out/
[Bb]uild
/compile_commands.json

16
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug (Linux)",
"type": "lldb",
"request": "launch",
"program": "${command:cmake.launchTargetPath}",
"args": [
"dummy"
],
"cwd": "${workspaceFolder}/",
"preLaunchTask": "CMake Build"
}
]
}

9
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,9 @@
{
"editor.formatOnSave": true,
"C_Cpp.codeAnalysis.runAutomatically": "onSave",
"C_Cpp.codeAnalysis.clangTidy.enabled": true,
"C_Cpp.codeAnalysis.clangTidy.useBuildPath": false,
"C_Cpp.codeAnalysis.clangTidy.config": "",
"C_Cpp.clang_format_fallbackStyle": "Microsoft",
"cmake.copyCompileCommands": "${workspaceFolder}/compile_commands.json",
}

19
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,19 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "CMake Build",
"type": "cmake",
"command": "build",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "silent",
"panel": "shared"
},
"detail": "Builds the currently selected CMake Preset"
}
]
}

143
CMake/FindDeps.cmake Normal file
View File

@ -0,0 +1,143 @@
include(FetchContent)
find_package(OpenSSL 3.0.0 REQUIRED)
set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "Force static libs")
set(HWY_ENABLE_TESTS OFF CACHE BOOL "Disable Highway tests" FORCE)
set(HWY_ENABLE_EXAMPLES OFF CACHE BOOL "Disable Highway examples" FORCE)
set(HWY_ENABLE_CONTRIB OFF CACHE BOOL "Disable Highway contrib" FORCE)
set(HWY_ENABLE_INSTALL OFF CACHE BOOL "Disable Highway install rules" FORCE)
set(ZLIB_USE_STATIC_LIBS ON CACHE BOOL "" FORCE)
set(ZLIB_COMPAT ON CACHE BOOL "" FORCE)
set(ZLIB_ENABLE_TESTS OFF CACHE BOOL "" FORCE)
set(WITH_GZFILEOP ON CACHE BOOL "" FORCE)
FetchContent_Declare(
Oxide
GIT_REPOSITORY https://github.com/I-A-S/Oxide
GIT_TAG main
OVERRIDE_FIND_PACKAGE
)
FetchContent_Declare(
zlib
GIT_REPOSITORY https://github.com/zlib-ng/zlib-ng.git
GIT_TAG 2.1.6
OVERRIDE_FIND_PACKAGE
)
set(ZSTD_BUILD_PROGRAMS OFF CACHE BOOL "" FORCE)
set(ZSTD_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(ZSTD_BUILD_SHARED OFF CACHE BOOL "" FORCE)
set(ZSTD_BUILD_STATIC ON CACHE BOOL "" FORCE)
FetchContent_Declare(
zstd
GIT_REPOSITORY https://github.com/facebook/zstd.git
GIT_TAG v1.5.5
SOURCE_SUBDIR build/cmake
OVERRIDE_FIND_PACKAGE
)
FetchContent_Declare(
httplib
GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git
GIT_TAG v0.28.0
SYSTEM
EXCLUDE_FROM_ALL
)
FetchContent_Declare(
nlohmann_json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.12.0
SYSTEM
EXCLUDE_FROM_ALL
)
FetchContent_Declare(
glaze
GIT_REPOSITORY https://github.com/stephenberry/glaze.git
GIT_TAG v5.0.0
SYSTEM
EXCLUDE_FROM_ALL
)
FetchContent_Declare(
simdjson
GIT_REPOSITORY https://github.com/simdjson/simdjson.git
GIT_TAG v4.2.2
SYSTEM
EXCLUDE_FROM_ALL
)
FetchContent_Declare(
mimalloc
GIT_REPOSITORY https://github.com/microsoft/mimalloc.git
GIT_TAG v3.0.10
SYSTEM
EXCLUDE_FROM_ALL
PATCH_COMMAND ${CMAKE_COMMAND}
-DSOURCE_DIR=<SOURCE_DIR>
-P ${CMAKE_CURRENT_SOURCE_DIR}/CMake/PatchMimalloc.cmake
)
FetchContent_Declare(
unordered_dense
GIT_REPOSITORY https://github.com/martinus/unordered_dense.git
GIT_TAG v4.8.1
SYSTEM
EXCLUDE_FROM_ALL
)
FetchContent_Declare(
pugixml
GIT_REPOSITORY https://github.com/zeux/pugixml.git
GIT_TAG v1.15
SYSTEM
EXCLUDE_FROM_ALL
)
FetchContent_Declare(
highway
GIT_REPOSITORY https://github.com/google/highway.git
GIT_TAG 1.3.0
SYSTEM
)
set(MI_OVERRIDE ON CACHE BOOL "" FORCE)
set(MI_BUILD_STATIC ON CACHE BOOL "" FORCE)
set(MI_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(MI_BUILD_SHARED OFF CACHE BOOL "" FORCE)
set(MI_DEBUG OFF CACHE BOOL "" FORCE)
set(MI_SHOW_ERRORS OFF CACHE BOOL "" FORCE)
set(EXPECTED_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(HTTPLIB_REQUIRE_OPENSSL ON CACHE BOOL "" FORCE)
set(HTTPLIB_REQUIRE_ZLIB OFF CACHE BOOL "" FORCE)
set(HTTPLIB_NO_EXCEPTIONS ON CACHE BOOL "" FORCE)
set(HTTPLIB_COMPILE OFF CACHE BOOL "" FORCE)
set(HTTPLIB_TEST OFF CACHE BOOL "" FORCE)
set(HTTPLIB_EXAMPLE OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(zlib zstd)
target_include_directories(libzstd_static INTERFACE
$<BUILD_INTERFACE:${zstd_SOURCE_DIR}/lib>
)
if(NOT TARGET zstd::libzstd)
add_library(zstd::libzstd ALIAS libzstd_static)
endif()
FetchContent_MakeAvailable(Oxide httplib pugixml nlohmann_json glaze simdjson unordered_dense mimalloc highway)
if(NOT TARGET simdjson::simdjson)
add_library(simdjson::simdjson ALIAS simdjson)
endif()
target_compile_options(hwy PRIVATE -w)
target_compile_options(libzstd_static PRIVATE -w)

View File

@ -0,0 +1,29 @@
macro(iacore_setup_project)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
if(MSVC)
add_compile_options(/W4)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wno-c++98-compat -Wno-c++98-compat-pedantic)
endif()
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
add_compile_options(-Wall -Wextra -Wpedantic -Wno-language-extension-token)
endif()
if(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64")
set(IACORE_ARCH_X64 TRUE CACHE INTERNAL "")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64")
set(IACORE_ARCH_ARM64 TRUE CACHE INTERNAL "")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "wasm32|emscripten")
set(IACORE_ARCH_WASM TRUE CACHE INTERNAL "")
endif()
endmacro()

36
CMake/PatchMimalloc.cmake Normal file
View File

@ -0,0 +1,36 @@
# Get the file path from the command line argument
set(TARGET_FILE "${SOURCE_DIR}/src/prim/windows/prim.c")
# Read the file
file(READ "${TARGET_FILE}" FILE_CONTENT)
# Check if the patch is already applied
string(FIND "${FILE_CONTENT}" "struct _TEB* const teb" ALREADY_PATCHED)
if(ALREADY_PATCHED EQUAL -1)
message(STATUS "Patching mimalloc source: ${TARGET_FILE}")
# Perform the replacement
string(REPLACE
"_TEB* const teb = NtCurrentTeb()"
"struct _TEB* const teb = NtCurrentTeb()"
FILE_CONTENT
"${FILE_CONTENT}"
)
# Write the file back only if changes were made
file(WRITE "${TARGET_FILE}" "${FILE_CONTENT}")
else()
message(STATUS "mimalloc source is already patched. Skipping.")
endif()
# Patch mimalloc complaing about false positive alignment issues in libc loader
file(READ "${SOURCE_DIR}/CMakeLists.txt" MI_CMAKE_CONTENT)
string(REPLACE
"set(mi_debug_default ON)"
"set(mi_debug_default OFF)"
MI_CMAKE_CONTENT
"${MI_CMAKE_CONTENT}"
)
file(WRITE "${SOURCE_DIR}/CMakeLists.txt" "${MI_CMAKE_CONTENT}")
message(STATUS "Patched mimalloc: Forced MI_DEBUG default to OFF to silence alignment warnings.")

View File

@ -0,0 +1,21 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(triple aarch64-linux-gnu)
set(CMAKE_C_COMPILER_TARGET ${triple})
set(CMAKE_CXX_COMPILER_TARGET ${triple})
set(CMAKE_SYSROOT /usr/aarch64-linux-gnu/sys-root)
set(CMAKE_LIBRARY_ARCHITECTURE aarch64-linux-gnu)
string(APPEND CMAKE_C_FLAGS " -march=armv8-a+simd")
string(APPEND CMAKE_CXX_FLAGS " -march=armv8-a+simd")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
link_directories(${CMAKE_SYSROOT}/usr/lib64)
link_directories(${CMAKE_SYSROOT}/lib64)

View File

@ -0,0 +1,18 @@
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_C_COMPILER clang-cl)
set(CMAKE_CXX_COMPILER clang-cl)
set(CMAKE_RC_COMPILER llvm-rc)
set(CMAKE_LINKER lld-link)
set(triple arm64-pc-windows-msvc)
set(CMAKE_C_COMPILER_TARGET ${triple})
set(CMAKE_CXX_COMPILER_TARGET ${triple})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
string(APPEND CMAKE_C_FLAGS " /clang:--target=arm64-pc-windows-msvc")
string(APPEND CMAKE_CXX_FLAGS " /clang:--target=arm64-pc-windows-msvc")

View File

@ -0,0 +1,7 @@
set(CMAKE_SYSTEM_NAME Emscripten)
set(CMAKE_SYSTEM_PROCESSOR wasm32)
include("$ENV{EMSDK}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake")
string(APPEND CMAKE_C_FLAGS " -msimd128 -pthread")
string(APPEND CMAKE_CXX_FLAGS " -msimd128 -pthread")

View File

@ -0,0 +1,6 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR AMD64)
string(APPEND CMAKE_C_FLAGS " -mavx2 -mfma -mxsave -msse4.2")
string(APPEND CMAKE_CXX_FLAGS " -mavx2 -mfma -mxsave -msse4.2")

View File

@ -0,0 +1,14 @@
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR AMD64)
set(CMAKE_C_COMPILER clang-cl)
set(CMAKE_CXX_COMPILER clang-cl)
set(CMAKE_RC_COMPILER llvm-rc)
set(triple x86_64-pc-windows-msvc)
set(CMAKE_C_COMPILER_TARGET ${triple})
set(CMAKE_CXX_COMPILER_TARGET ${triple})
set(CMAKE_LINKER lld-link)
string(APPEND CMAKE_C_FLAGS " /arch:AVX2 /clang:-mfma")
string(APPEND CMAKE_CXX_FLAGS " /arch:AVX2 /clang:-mfma")

41
CMakeLists.txt Normal file
View File

@ -0,0 +1,41 @@
cmake_minimum_required(VERSION 3.28 FATAL_ERROR)
project(IACore LANGUAGES C CXX)
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(IACORE_IS_TOP_LEVEL ON)
else()
set(IACORE_IS_TOP_LEVEL OFF)
endif()
set(IACORE_ROOT "${CMAKE_CURRENT_LIST_DIR}" CACHE INTERNAL "")
set(IACORE_CMAKE_DIR "${IACORE_ROOT}/CMake" CACHE INTERNAL "")
list(APPEND CMAKE_MODULE_PATH "${IACORE_CMAKE_DIR}")
include(IAProjectConfig)
iacore_setup_project()
message(STATUS "[IACore] Detected Compiler: ${CMAKE_CXX_COMPILER_ID}")
if (MSVC AND NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
message(FATAL_ERROR
"\n-------------------------------------------------------------\n"
"CRITICAL ERROR: Unsupported Compiler (MSVC/cl.exe)\n"
"IACore requires GCC or Clang. On Windows, use 'Clang-cl' or MinGW.\n"
"-------------------------------------------------------------\n"
)
endif()
include(FindDeps)
option(IACore_BUILD_TESTS "Build unit tests" ${IACORE_IS_TOP_LEVEL})
add_subdirectory(Src)
if(IACore_BUILD_TESTS)
add_subdirectory(Tests)
endif()
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Sandbox")
# add_subdirectory(Sandbox)
endif()

78
CMakePresets.json Normal file
View File

@ -0,0 +1,78 @@
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 28
},
"configurePresets": [
{
"name": "iacore-base",
"hidden": true,
"generator": "Ninja Multi-Config",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"cacheVariables": {
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++",
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/CMake/Toolchains/${presetName}-clang.cmake"
}
},
{
"name": "x64-linux",
"displayName": "IACore Linux x64 (Clang)",
"inherits": "iacore-base",
"cacheVariables": {}
},
{
"name": "arm64-linux",
"displayName": "IACore Linux ARM64 (Clang Cross)",
"inherits": "iacore-base",
"cacheVariables": {}
},
{
"name": "x64-windows",
"displayName": "IACore Windows x64 (Clang)",
"inherits": "iacore-base",
"cacheVariables": {
"CMAKE_C_COMPILER": "clang-cl",
"CMAKE_CXX_COMPILER": "clang-cl"
}
},
{
"name": "arm64-windows",
"displayName": "IACore Windows ARM64 (Clang Cross)",
"inherits": "iacore-base",
"cacheVariables": {
"CMAKE_C_COMPILER": "clang-cl",
"CMAKE_CXX_COMPILER": "clang-cl"
}
},
{
"name": "wasm",
"displayName": "IACore WebAssembly (Clang)",
"inherits": "iacore-base",
"cacheVariables": {}
}
],
"buildPresets": [
{
"name": "x64-linux",
"configurePreset": "x64-linux"
},
{
"name": "arm64-linux",
"configurePreset": "arm64-linux"
},
{
"name": "x64-windows",
"configurePreset": "x64-windows"
},
{
"name": "arm64-windows",
"configurePreset": "arm64-windows"
},
{
"name": "wasm",
"configurePreset": "wasm"
}
]
}

37
Docs/BUILDING.md Normal file
View File

@ -0,0 +1,37 @@
## 🛠️ Building IACore
IACore uses **CMake Presets** to manage toolchains and cross-compilation. This ensures that the correct compilers (Clang) and flags (AVX2/SIMD) are used automatically.
### Prerequisites
* CMake 3.28+
* Ninja Build System
* Vcpkg (Environment variable `VCPKG_ROOT` must be set)
* Clang / Clang-CL
### Build Instructions
**1. Configure**
Select the preset for your target platform.
```bash
# List available presets
cmake --list-presets
# Configure for your platform (e.g., windows-x64, linux-arm64, wasm)
cmake --preset windows-x64
```
**2. Build**
```bash
cmake --build --preset windows-x64
```
### Available Presets
|Preset |Description |Toolchain |
|-------------|------------------------------|------------------------------------|
|windows-x64 |Windows (Clang-CL) |CMake/Toolchains/windows-x64.cmake |
|linux-x64 |Linux (Clang) |CMake/Toolchains/linux-x64.cmake |
|wasm |WebAssembly (Emscripten) |CMake/Toolchains/wasm.cmake |
|windows-arm64|Windows on ARM (Cross-compile)|CMake/Toolchains/windows-arm64.cmake|
|linux-arm64 |Linux on ARM (Cross-compile) |CMake/Toolchains/linux-arm64.cmake |

27
Docs/USING.md Normal file
View File

@ -0,0 +1,27 @@
## 🚀 Using IACore in a New Project
IACore provides a CMake macro `iacore_setup_project()`, which standardizes your build environment. This macro automatically:
* Enforces C++20 standard.
* Sets warning levels (-Wall -Wextra -Wpedantic for Clang/GCC, /W4 for MSVC/Clang-CL).
* Detects the target architecture (x64, ARM64, WASM) and sets internal cache variables.
* Suppresses C++98 compatibility warnings when using Clang on Windows.
Example CMakeLists.txt
```cmake
cmake_minimum_required(VERSION 3.28)
project(MyGame)
# Or you can use FetchContent
add_subdirectory(external/IACore)
## Apply IACore's standard project configuration
# This applies C++20 and strict warning flags globally to your targets.
iacore_setup_project()
# Define your target(s)
add_executable(MyGame src/main.cpp)
# Link IACore
target_link_libraries(MyGame PRIVATE IACore)
```

177
LICENSE Normal file
View File

@ -0,0 +1,177 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

182
README.md Normal file
View File

@ -0,0 +1,182 @@
# IACore (Independent Architecture Core)
<div align="center">
<img src="logo.svg" alt="IACore Logo" width="400"/>
<br/>
<img src="https://img.shields.io/badge/license-apache_v2-blue.svg" alt="License"/>
<img src="https://img.shields.io/badge/standard-C%2B%2B20-yellow.svg" alt="C++ Standard"/>
<img src="https://img.shields.io/badge/platform-Windows%20%7C%20Linux-lightgrey.svg" alt="Platform"/>
<p>
<b>A High-Performance Foundation for Modern C++ 20 Applications.</b>
</p>
</div>
> [!NOTE]
> This branch presents the latest version of IACore (v1.2), which is a major rewrite of the previous API. If you're looking for the previous API instead, please find it in the `_legay_api` branch!
## 📖 Description
IACore is a high-performance C++20 foundation library bundling essential systems (IPC, Logging, Networking, Compression, and Async Scheduling) into a single, coherent API.
IACore strictly follows the coding style and philosophy of [Oxide](https://github.com/I-A-S/Oxide).
Originally developed as the internal core for IASoft (PVT) LTD., it is now open-source to provide a standardized bedrock for C++ applications where performance matters.
## ✨ Features
* **🚀 High-Performance IPC:** Shared-Memory Ring Buffers with wait-free SPSC synchronization.
* **🌐 Networking:** Integrated HTTP/HTTPS client (wrapper around `cpp-httplib` with automatic Zlib/Gzip handling).
* **🧵 Async Scheduler:** A job system with high/normal priority queues.
* **💾 File I/O:** Memory-mapped file operations and optimized binary stream readers/writers.
* **📦 Compression:** Unified API for Zlib, Gzip, and Zstd.
* **📜 Logging:** Thread-safe, colored console and disk logging.
* **⚡ Modern C++:** Heavily utilizes modern C++20 concepts, `std::span`, and `Oxide` for error handling.
## 💡 Usage Examples
### 1. IPC (Manager & Node)
IACore provides a manager/node architecture using shared memory.
#### Manager:
```C++
#include <IACore/IPC.hpp>
// Spawns a child process and connects via Shared Memory
auto nodeID = manager.SpawnNode("MyChildNodeExe");
manager.WaitTillNodeIsOnline(*nodeID);
// Send data with zero-copy overhead
String msg = "Hello Node";
manager.SendPacket(*nodeID, 100, {(PCUINT8)msg.data(), msg.size()});
```
#### Node:
```C++
#include <IACore/IPC.hpp>
class Node : public IACore::IPC_Node {
public:
void OnSignal(uint8_t signal) override {
// Handle signals
}
void OnPacket(uint16_t packetID, std::span<const uint8_t> payload) override {
// Handle packets
}
};
int main(int argc, char* argv[]) {
// The connection string is passed as the first argument by the Manager
if (argc < 2) return -1;
Node node;
// Connect back to the manager via Shared Memory
if (!node.Connect(argv[1])) return -1;
while(true) {
node.Update();
}
return 0;
}
```
### 2. Async Jobs
```C++
#include <IACore/AsyncOps.hpp>
// Initialize worker threads (hardware_concurrency - 2)
IACore::AsyncOps::InitializeScheduler();
// Schedule a task
IACore::AsyncOps::Schedule *mySchedule = new IACore::AsyncOps::Schedule();
IACore::AsyncOps::ScheduleTask([](auto workerID) {
printf("Running on worker %d\n", workerID);
}, 0, mySchedule);
// Wait for completion
IACore::AsyncOps::WaitForScheduleCompletion(mySchedule);
```
### 3. HTTP Request
```C++
#include <IACore/HttpClient.hpp>
IACore::HttpClient client("https://api.example.com");
auto res = client.JsonGet<MyResponseStruct>("/data", {});
if (res) {
std::cout << "Data: " << res->value << "\n";
} else {
std::cerr << "Error: " << res.error() << "\n";
}
```
## 🛠️ Integration
IACore is built with CMake. You can include it in your project via `FetchContent` or by adding it as a subdirectory.
**Note:** On Windows, you must have **VCPKG** installed and the `VCPKG_ROOT` environment variable set, for OpenSSL support.
### CMake Example
```cmake
cmake_minimum_required(VERSION 3.28)
project(MyGame)
# Or you can use FetchContent
add_subdirectory(external/IACore)
# Apply IACore's standard project configuration
# This applies C++20 and strict warning flags globally to your targets.
iacore_setup_project()
# Define your targets
add_executable(MyGame src/main.cpp)
# Link IACore
target_link_libraries(MyGame PRIVATE IACore)
```
## 🤝 Contributing
We welcome contributions from the community!
### What we accept immediately:
* **📚 Documentation:** Improvements to comments, the README, or external docs.
* **🧪 Tests:** New unit tests (in `Tests/`) to improve coverage or reproduce bugs.
* **💡 Examples:** New usage examples or sample projects.
* **🐛 Bug Reports:** detailed issues describing reproduction steps are highly valued.
### Core Library Policy (`Src/` Directory)
Currently, **we are not accepting Pull Requests that modify the core source code (`Src/`)**.
If you find a critical bug in `Src/`, please open an **Issue** rather than a PR, and the core team will implement a fix ASAP.
## 📦 Dependencies
IACore manages its own dependencies via CMake's FetchContent. You do not need to install these manually:
* **JSON:** `glaze`
* **SIMD:** `google-highway`
* **Networking:** `cpp-httplib`
* **Compression:** `zlib-ng` & `zstd`
* **Utilities:** `tl-expected` & `unordered_dense`
**Note:** Following dependencies are not directly used by IACore, but bundles them (+ helper wrappers) for user convenience: `nlohmann_json`, `simdjson`, `pugixml`
## ⚖️ License
This project is licensed under the Apache License Version 2.0.
## 🤖 Use of Generative AI at IASoft
While we never let Generative AI to make architecural/design decisions, and we hand-write almost all of the implementations, we do use Generative AI (Google Gemini) for the following and *(ONLY following)* tasks:
1) **Controlled Repititive Boilerplate Generation:** Each and **every single line of AI generated code** is **manually reviewed** one-by-one for hallucinations and logic errors. Trust, but verify.
2) **Concept Research and Development:** Design pattern comparisions, cost-benefit analysis and as a second pair of eyes to evalute and critique our design decisions.
3) **Documentation:** Repititive method doc strings (parameter, return value descriptions) are done mostly using Generative AI.
4) **Code Review:** Automated logic checking and static analysis on top of deterministic tools.

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

4
Tests/CMakeLists.txt Normal file
View File

@ -0,0 +1,4 @@
add_subdirectory(Subjects/)
add_subdirectory(Unit/)
add_subdirectory(Regression/)

View File

View File

@ -0,0 +1 @@
add_executable(LongProcess LongProcess/Main.cpp)

View File

@ -0,0 +1,12 @@
#include <thread>
#include <iostream>
int main(int, char **)
{
std::cout << "Started!\n";
std::cout.flush();
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "Ended!\n";
std::cout.flush();
return 100;
}

160
Tests/Unit/AsyncOps.cpp Normal file
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.
#include <IACore/AsyncOps.hpp>
#include <IACore/IATest.hpp>
#include <chrono>
#include <thread>
using namespace IACore;
struct SchedulerGuard {
SchedulerGuard(u8 worker_count = 2) {
(void)AsyncOps::initialize_scheduler(worker_count);
}
~SchedulerGuard() { AsyncOps::terminate_scheduler(); }
};
IAT_BEGIN_BLOCK(Core, AsyncOps)
auto test_initialization() -> bool {
AsyncOps::terminate_scheduler();
const auto res = AsyncOps::initialize_scheduler(4);
IAT_CHECK(res.has_value());
IAT_CHECK_EQ(AsyncOps::get_worker_count(), static_cast<u16>(4));
AsyncOps::terminate_scheduler();
const auto res2 = AsyncOps::initialize_scheduler(1);
IAT_CHECK(res2.has_value());
IAT_CHECK_EQ(AsyncOps::get_worker_count(), static_cast<u16>(1));
AsyncOps::terminate_scheduler();
return true;
}
auto test_basic_execution() -> bool {
SchedulerGuard guard(2);
AsyncOps::Schedule schedule;
std::atomic<i32> run_count{0};
AsyncOps::schedule_task([&](AsyncOps::WorkerId) { run_count++; }, 0,
&schedule);
AsyncOps::wait_for_schedule_completion(&schedule);
IAT_CHECK_EQ(run_count.load(), 1);
return true;
}
auto test_concurrency() -> bool {
SchedulerGuard guard(4);
AsyncOps::Schedule schedule;
std::atomic<i32> run_count{0};
const i32 total_tasks = 100;
for (i32 i = 0; i < total_tasks; ++i) {
AsyncOps::schedule_task(
[&](AsyncOps::WorkerId) {
std::this_thread::sleep_for(std::chrono::microseconds(10));
run_count++;
},
0, &schedule);
}
AsyncOps::wait_for_schedule_completion(&schedule);
IAT_CHECK_EQ(run_count.load(), total_tasks);
return true;
}
auto test_priorities() -> bool {
SchedulerGuard guard(2);
AsyncOps::Schedule schedule;
std::atomic<i32> high_priority_ran{0};
std::atomic<i32> normal_priority_ran{0};
AsyncOps::schedule_task([&](AsyncOps::WorkerId) { high_priority_ran++; }, 0,
&schedule, AsyncOps::Priority::High);
AsyncOps::schedule_task([&](AsyncOps::WorkerId) { normal_priority_ran++; }, 0,
&schedule, AsyncOps::Priority::Normal);
AsyncOps::wait_for_schedule_completion(&schedule);
IAT_CHECK_EQ(high_priority_ran.load(), 1);
IAT_CHECK_EQ(normal_priority_ran.load(), 1);
return true;
}
auto test_run_task_fire_and_forget() -> bool {
SchedulerGuard guard(2);
std::atomic<bool> executed{false};
AsyncOps::run_task([&]() { executed = true; });
for (int i = 0; i < 100; ++i) {
if (executed.load())
break;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
IAT_CHECK(executed.load());
return true;
}
auto test_cancellation_safety() -> bool {
SchedulerGuard guard(2);
AsyncOps::cancel_tasks_of_tag(999);
AsyncOps::Schedule schedule;
std::atomic<i32> counter{0};
AsyncOps::schedule_task([&](AsyncOps::WorkerId) { counter++; }, 10,
&schedule);
AsyncOps::wait_for_schedule_completion(&schedule);
IAT_CHECK_EQ(counter.load(), 1);
AsyncOps::cancel_tasks_of_tag(10);
return true;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_initialization);
IAT_ADD_TEST(test_basic_execution);
IAT_ADD_TEST(test_concurrency);
IAT_ADD_TEST(test_priorities);
IAT_ADD_TEST(test_run_task_fire_and_forget);
IAT_ADD_TEST(test_cancellation_safety);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, AsyncOps)

103
Tests/Unit/CLI.cpp Normal file
View File

@ -0,0 +1,103 @@
// 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>
#include <IACore/IATest.hpp>
using namespace IACore;
IAT_BEGIN_BLOCK(Core, CLI)
auto test_basic_traversal() -> bool {
const Vec<String> args = {"ignored", "one", "two", "three"};
CLIParser parser(args);
IAT_CHECK(parser.remaining());
IAT_CHECK_EQ(String(parser.next()), "one");
IAT_CHECK(parser.remaining());
IAT_CHECK_EQ(String(parser.next()), "two");
IAT_CHECK(parser.remaining());
IAT_CHECK_EQ(String(parser.next()), "three");
IAT_CHECK_NOT(parser.remaining());
IAT_CHECK_EQ(String(parser.next()), "");
return true;
}
auto test_peek() -> bool {
const Vec<String> args = {"ignored", "peek_val", "next_val"};
CLIParser parser(args);
IAT_CHECK_EQ(String(parser.peek()), "peek_val");
IAT_CHECK(parser.remaining());
IAT_CHECK_EQ(String(parser.next()), "peek_val");
IAT_CHECK_EQ(String(parser.peek()), "next_val");
IAT_CHECK_EQ(String(parser.next()), "next_val");
IAT_CHECK_NOT(parser.remaining());
return true;
}
auto test_consume() -> bool {
const Vec<String> args = {"ignored", "-v", "--output", "file.txt"};
CLIParser parser(args);
IAT_CHECK_NOT(parser.consume("-x"));
IAT_CHECK_EQ(String(parser.peek()), "-v");
IAT_CHECK(parser.consume("-v"));
IAT_CHECK_EQ(String(parser.peek()), "--output");
IAT_CHECK(parser.consume("--output"));
IAT_CHECK_EQ(String(parser.next()), "file.txt");
IAT_CHECK_NOT(parser.remaining());
return true;
}
auto test_empty() -> bool {
const Vec<String> args = {};
CLIParser parser(args);
IAT_CHECK_NOT(parser.remaining());
IAT_CHECK_EQ(String(parser.peek()), "");
IAT_CHECK_EQ(String(parser.next()), "");
IAT_CHECK_NOT(parser.consume("-help"));
return true;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_basic_traversal);
IAT_ADD_TEST(test_peek);
IAT_ADD_TEST(test_consume);
IAT_ADD_TEST(test_empty);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, CLI)

36
Tests/Unit/CMakeLists.txt Normal file
View File

@ -0,0 +1,36 @@
set(TEST_SOURCES
AsyncOps.cpp
CLI.cpp
DataOps.cpp
Environment.cpp
FileOps.cpp
IPC.cpp
JSON.cpp
Logger.cpp
Main.cpp
Platform.cpp
ProcessOps.cpp
RingBuffer.cpp
SocketOps.cpp
StreamReader.cpp
StreamWriter.cpp
StringOps.cpp
Utils.cpp
XML.cpp
SIMD/IntVec4.cpp
SIMD/FloatVec4.cpp
)
add_executable(IACore_Test_Suite ${TEST_SOURCES})
target_compile_options(IACore_Test_Suite PRIVATE -fexceptions)
set_target_properties(IACore_Test_Suite PROPERTIES USE_EXCEPTIONS ON)
target_link_libraries(IACore_Test_Suite PRIVATE IACore)
add_custom_command(TARGET IACore_Test_Suite POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:LongProcess>
$<TARGET_FILE_DIR:IACore_Test_Suite>/LongProcess${CMAKE_EXECUTABLE_SUFFIX}
)

108
Tests/Unit/DataOps.cpp Normal file
View File

@ -0,0 +1,108 @@
// 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/IATest.hpp>
using namespace IACore;
IAT_BEGIN_BLOCK(Core, DataOps)
auto test_crc32() -> bool {
{
const String s = "123456789";
const Span<const u8> span(reinterpret_cast<const u8 *>(s.data()), s.size());
const u32 result = DataOps::crc32(span);
IAT_CHECK_EQ(result, 0xE3069283);
}
{
const u32 result = DataOps::crc32({});
IAT_CHECK_EQ(result, 0U);
}
{
Vec<u8> buffer(33);
for (usize i = 1; i < 33; ++i) {
buffer[i] = static_cast<u8>(i);
}
Vec<u8> ref_data(32);
for (usize i = 0; i < 32; ++i) {
ref_data[i] = static_cast<u8>(i + 1);
}
const u32 hash_ref =
DataOps::crc32(Span<const u8>(ref_data.data(), ref_data.size()));
const u32 hash_unaligned =
DataOps::crc32(Span<const u8>(buffer.data() + 1, 32));
IAT_CHECK_EQ(hash_ref, hash_unaligned);
}
return true;
}
auto test_hash_xxhash() -> bool {
{
const String s = "123456789";
const u32 result = DataOps::hash_xxhash(s);
IAT_CHECK_EQ(result, 0x937bad67);
}
{
const String s = "The quick brown fox jumps over the lazy dog";
const u32 result = DataOps::hash_xxhash(s);
IAT_CHECK_EQ(result, 0xE85EA4DE);
}
{
const String s = "Test";
const u32 r1 = DataOps::hash_xxhash(s);
const u32 r2 = DataOps::hash_xxhash(
Span<const u8>(reinterpret_cast<const u8 *>(s.data()), s.size()));
IAT_CHECK_EQ(r1, r2);
}
return true;
}
auto test_hash_fnv1a() -> bool {
{
const String s = "123456789";
const u32 result = DataOps::hash_fnv1a(
Span<const u8>(reinterpret_cast<const u8 *>(s.data()), s.size()));
IAT_CHECK_EQ(result, 0xbb86b11c);
}
{
const u32 result = DataOps::hash_fnv1a(Span<const u8>{});
IAT_CHECK_EQ(result, 0x811C9DC5);
}
return true;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_crc32);
IAT_ADD_TEST(test_hash_fnv1a);
IAT_ADD_TEST(test_hash_xxhash);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, DataOps)

127
Tests/Unit/Environment.cpp Normal file
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.
#include <IACore/Environment.hpp>
#include <IACore/IATest.hpp>
using namespace IACore;
static constexpr const char *TEST_KEY = "IA_TEST_ENV_VAR_12345";
static constexpr const char *TEST_VAL = "Hello World";
IAT_BEGIN_BLOCK(Core, Environment)
auto test_basic_cycle() -> bool {
(void)Environment::unset(TEST_KEY);
IAT_CHECK_NOT(Environment::exists(TEST_KEY));
const auto set_res = Environment::set(TEST_KEY, TEST_VAL);
IAT_CHECK(set_res.has_value());
IAT_CHECK(Environment::exists(TEST_KEY));
const auto opt = Environment::find(TEST_KEY);
IAT_CHECK(opt.has_value());
IAT_CHECK_EQ(*opt, String(TEST_VAL));
const String val = Environment::get(TEST_KEY);
IAT_CHECK_EQ(val, String(TEST_VAL));
(void)Environment::unset(TEST_KEY);
return true;
}
auto test_overwrite() -> bool {
(void)Environment::set(TEST_KEY, "ValueA");
IAT_CHECK_EQ(Environment::get(TEST_KEY), String("ValueA"));
(void)Environment::set(TEST_KEY, "ValueB");
IAT_CHECK_EQ(Environment::get(TEST_KEY), String("ValueB"));
(void)Environment::unset(TEST_KEY);
return true;
}
auto test_unset() -> bool {
(void)Environment::set(TEST_KEY, "To Be Deleted");
IAT_CHECK(Environment::exists(TEST_KEY));
const auto unset_res = Environment::unset(TEST_KEY);
IAT_CHECK(unset_res.has_value());
IAT_CHECK_NOT(Environment::exists(TEST_KEY));
const auto opt = Environment::find(TEST_KEY);
IAT_CHECK_NOT(opt.has_value());
return true;
}
auto test_defaults() -> bool {
const char *ghost_key = "IA_THIS_KEY_DOES_NOT_EXIST";
(void)Environment::unset(ghost_key);
const String empty = Environment::get(ghost_key);
IAT_CHECK(empty.empty());
const String fallback = Environment::get(ghost_key, "MyDefault");
IAT_CHECK_EQ(fallback, String("MyDefault"));
return true;
}
auto test_empty_value() -> bool {
(void)Environment::set(TEST_KEY, "");
#if IA_PLATFORM_WINDOWS
#else
IAT_CHECK(Environment::exists(TEST_KEY));
const auto opt = Environment::find(TEST_KEY);
IAT_CHECK(opt.has_value());
IAT_CHECK(opt->empty());
#endif
(void)Environment::unset(TEST_KEY);
IAT_CHECK_NOT(Environment::exists(TEST_KEY));
return true;
}
auto test_bad_input() -> bool {
const auto res = Environment::set("", "Value");
IAT_CHECK_NOT(res.has_value());
const auto res_unset = Environment::unset("");
IAT_CHECK_NOT(res_unset.has_value());
return true;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_basic_cycle);
IAT_ADD_TEST(test_overwrite);
IAT_ADD_TEST(test_unset);
IAT_ADD_TEST(test_defaults);
IAT_ADD_TEST(test_empty_value);
IAT_ADD_TEST(test_bad_input);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, Environment)

156
Tests/Unit/FileOps.cpp Normal file
View File

@ -0,0 +1,156 @@
// 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/IATest.hpp>
using namespace IACore;
IAT_BEGIN_BLOCK(Core, FileOps)
void cleanup_file(const Path &path) {
std::error_code ec;
if (std::filesystem::exists(path, ec)) {
std::filesystem::remove(path, ec);
}
}
auto test_text_io() -> bool {
const Path path = "iatest_fileops_text.txt";
const String content = "Hello IACore FileOps!\nLine 2";
const auto write_res = FileOps::write_text_file(path, content, true);
IAT_CHECK(write_res.has_value());
IAT_CHECK_EQ(*write_res, content.size());
const auto read_res = FileOps::read_text_file(path);
IAT_CHECK(read_res.has_value());
IAT_CHECK_EQ(*read_res, content);
cleanup_file(path);
return true;
}
auto test_binary_io() -> bool {
const Path path = "iatest_fileops_bin.bin";
const Vec<u8> content = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF};
const auto write_res = FileOps::write_binary_file(path, content, true);
IAT_CHECK(write_res.has_value());
IAT_CHECK_EQ(*write_res, content.size());
const auto read_res = FileOps::read_binary_file(path);
IAT_CHECK(read_res.has_value());
IAT_CHECK_EQ(read_res->size(), content.size());
for (usize i = 0; i < content.size(); ++i) {
IAT_CHECK_EQ((*read_res)[i], content[i]);
}
cleanup_file(path);
return true;
}
auto test_file_mapping() -> bool {
const Path path = "iatest_fileops_map.txt";
const String content = "MappedContent";
(void)FileOps::write_text_file(path, content, true);
usize size = 0;
const auto map_res = FileOps::map_file(path, size);
IAT_CHECK(map_res.has_value());
IAT_CHECK_EQ(size, content.size());
const u8 *ptr = *map_res;
IAT_CHECK(ptr != nullptr);
String read_back(reinterpret_cast<const char *>(ptr), size);
IAT_CHECK_EQ(read_back, content);
FileOps::unmap_file(ptr);
cleanup_file(path);
return true;
}
auto test_shared_memory() -> bool {
const String shm_name = "iatest_shm_block";
const usize shm_size = 4096;
auto owner_res = FileOps::map_shared_memory(shm_name, shm_size, true);
IAT_CHECK(owner_res.has_value());
u8 *owner_ptr = *owner_res;
std::memset(owner_ptr, 0, shm_size);
const String msg = "Shared Memory Message";
std::memcpy(owner_ptr, msg.data(), msg.size());
auto client_res = FileOps::map_shared_memory(shm_name, shm_size, false);
IAT_CHECK(client_res.has_value());
u8 *client_ptr = *client_res;
String read_msg(reinterpret_cast<const char *>(client_ptr), msg.size());
IAT_CHECK_EQ(read_msg, msg);
FileOps::unmap_file(owner_ptr);
FileOps::unmap_file(client_ptr);
FileOps::unlink_shared_memory(shm_name);
return true;
}
auto test_stream_integration() -> bool {
const Path path = "iatest_fileops_stream.bin";
cleanup_file(path);
{
auto writer_res = FileOps::stream_to_file(path, true);
IAT_CHECK(writer_res.has_value());
auto &writer = *writer_res;
(void)writer.write<u32>(0x12345678);
(void)writer.write<u8>(0xFF);
}
{
auto reader_res = FileOps::stream_from_file(path);
IAT_CHECK(reader_res.has_value());
auto &reader = *reader_res;
auto val_u32 = reader.read<u32>();
IAT_CHECK(val_u32.has_value());
IAT_CHECK_EQ(*val_u32, 0x12345678u);
auto val_u8 = reader.read<u8>();
IAT_CHECK(val_u8.has_value());
IAT_CHECK_EQ(*val_u8, 0xFF);
}
cleanup_file(path);
return true;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_text_io);
IAT_ADD_TEST(test_binary_io);
IAT_ADD_TEST(test_file_mapping);
IAT_ADD_TEST(test_shared_memory);
IAT_ADD_TEST(test_stream_integration);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, FileOps)

130
Tests/Unit/IPC.cpp Normal file
View File

@ -0,0 +1,130 @@
// 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/IATest.hpp>
#include <IACore/IPC.hpp>
using namespace IACore;
IAT_BEGIN_BLOCK(Core, IPC)
auto test_layout_constraints() -> bool {
IAT_CHECK_EQ(alignof(IpcSharedMemoryLayout), static_cast<usize>(64));
IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, meta), static_cast<usize>(0));
IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, moni_control),
static_cast<usize>(64));
IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, mino_control),
static_cast<usize>(192));
IAT_CHECK_EQ(offsetof(IpcSharedMemoryLayout, moni_data_offset),
static_cast<usize>(320));
IAT_CHECK_EQ(sizeof(IpcSharedMemoryLayout) % 64, static_cast<usize>(0));
return true;
}
auto test_manual_shm_ringbuffer() -> bool {
const String shm_name = "IA_TEST_IPC_LAYOUT_CHECK";
const usize shm_size = 16 * 1024;
FileOps::unlink_shared_memory(shm_name);
auto map_res = FileOps::map_shared_memory(shm_name, shm_size, true);
IAT_CHECK(map_res.has_value());
u8 *base_ptr = *map_res;
auto *layout = reinterpret_cast<IpcSharedMemoryLayout *>(base_ptr);
layout->meta.magic = 0xDEADBEEF;
layout->meta.version = 1;
layout->meta.total_size = shm_size;
const usize header_size = IpcSharedMemoryLayout::get_header_size();
const usize data_available = shm_size - header_size;
const usize half_data = data_available / 2;
layout->moni_data_offset = header_size;
layout->moni_data_size = half_data;
layout->mino_data_offset = header_size + half_data;
layout->mino_data_size = half_data;
Span<u8> moni_data_span(base_ptr + layout->moni_data_offset,
static_cast<usize>(layout->moni_data_size));
auto moni_res =
RingBufferView::create(&layout->moni_control, moni_data_span, true);
IAT_CHECK(moni_res.has_value());
auto moni = std::move(*moni_res);
Span<u8> mino_data_span(base_ptr + layout->mino_data_offset,
static_cast<usize>(layout->mino_data_size));
auto mino_res =
RingBufferView::create(&layout->mino_control, mino_data_span, true);
IAT_CHECK(mino_res.has_value());
auto _ = std::move(*mino_res);
String msg = "IPC_TEST_MESSAGE";
IAT_CHECK(
moni.push(100, Span<const u8>(reinterpret_cast<const u8 *>(msg.data()),
msg.size()))
.has_value());
auto moni_reader_res =
RingBufferView::create(&layout->moni_control, moni_data_span, false);
IAT_CHECK(moni_reader_res.has_value());
auto moni_reader = std::move(*moni_reader_res);
RingBufferView::PacketHeader header;
u8 buffer[128];
auto pop_res = moni_reader.pop(header, Span<u8>(buffer, 128));
IAT_CHECK(pop_res.has_value());
IAT_CHECK(pop_res->has_value());
IAT_CHECK_EQ(header.id, static_cast<u16>(100));
String received((char *)buffer, *pop_res.value());
IAT_CHECK_EQ(received, msg);
FileOps::unmap_file(base_ptr);
FileOps::unlink_shared_memory(shm_name);
return true;
}
class TestManager : public IpcManager {
public:
void on_signal(NativeProcessID, u8) override {}
void on_packet(NativeProcessID, u16, Span<const u8>) override {}
};
auto test_manager_instantiation() -> bool {
TestManager mgr;
return true;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_layout_constraints);
IAT_ADD_TEST(test_manual_shm_ringbuffer);
IAT_ADD_TEST(test_manager_instantiation);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, IPC)

164
Tests/Unit/JSON.cpp Normal file
View File

@ -0,0 +1,164 @@
// 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/IATest.hpp>
#include <IACore/JSON.hpp>
using namespace IACore;
struct UserProfile {
String username;
u32 id;
bool is_active;
Vec<String> roles;
bool operator==(const UserProfile &other) const {
return username == other.username && id == other.id &&
is_active == other.is_active && roles == other.roles;
}
};
IAT_BEGIN_BLOCK(Core, JSON)
auto test_dynamic_parse() -> bool {
const String json_text = R"({
"string": "Hello World",
"int": 42,
"float": 3.14159,
"bool": true,
"array": [10, 20, 30],
"object": { "key": "value" }
})";
auto res = Json::parse(json_text);
IAT_CHECK(res.has_value());
const auto &j = *res;
IAT_CHECK(j["string"].is_string());
IAT_CHECK_EQ(j["string"].get<String>(), String("Hello World"));
IAT_CHECK(j["int"].is_number_integer());
IAT_CHECK_EQ(j["int"].get<i32>(), 42);
IAT_CHECK(j["float"].is_number_float());
IAT_CHECK_APPROX(j["float"].get<f32>(), 3.14159f);
IAT_CHECK(j["bool"].is_boolean());
IAT_CHECK_EQ(j["bool"].get<bool>(), true);
IAT_CHECK(j["array"].is_array());
IAT_CHECK_EQ(j["array"].size(), 3u);
IAT_CHECK_EQ(j["array"][0].get<i32>(), 10);
IAT_CHECK(j["object"].is_object());
IAT_CHECK_EQ(j["object"]["key"].get<String>(), String("value"));
return true;
}
auto test_dynamic_encode() -> bool {
nlohmann::json j;
j["name"] = "IACore";
j["version"] = 2;
const String encoded = Json::encode(j);
IAT_CHECK(encoded.find("IACore") != String::npos);
IAT_CHECK(encoded.find("version") != String::npos);
IAT_CHECK(encoded.find("2") != String::npos);
return true;
}
auto test_parse_invalid() -> bool {
const String bad_json = "{ key: value }";
auto res = Json::parse(bad_json);
IAT_CHECK_NOT(res.has_value());
return true;
}
auto test_struct_round_trip() -> bool {
UserProfile original{.username = "test_user",
.id = 12345,
.is_active = true,
.roles = {"admin", "editor"}};
auto encode_res = Json::encode_struct(original);
IAT_CHECK(encode_res.has_value());
String json_str = *encode_res;
IAT_CHECK(json_str.find("test_user") != String::npos);
IAT_CHECK(json_str.find("roles") != String::npos);
auto decode_res = Json::parse_to_struct<UserProfile>(json_str);
IAT_CHECK(decode_res.has_value());
UserProfile decoded = *decode_res;
IAT_CHECK(decoded == original);
return true;
}
auto test_struct_parse_error() -> bool {
const String malformed = "{ broken_json: ";
auto res = Json::parse_to_struct<UserProfile>(malformed);
IAT_CHECK_NOT(res.has_value());
return true;
}
auto test_read_only() -> bool {
const String json_text = R"({
"id": 999,
"name": "Simd",
"scores": [1.1, 2.2]
})";
auto res = Json::parse_read_only(json_text);
IAT_CHECK(res.has_value());
auto &doc = *res;
simdjson::dom::element root = doc.root();
u64 id = 0;
auto err_id = root["id"].get(id);
IAT_CHECK(!err_id);
IAT_CHECK_EQ(id, 999ULL);
std::string_view name;
auto err_name = root["name"].get(name);
IAT_CHECK(!err_name);
IAT_CHECK_EQ(String(name), String("Simd"));
simdjson::dom::array scores;
auto err_arr = root["scores"].get(scores);
IAT_CHECK(!err_arr);
IAT_CHECK_EQ(scores.size(), 2u);
return true;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_dynamic_parse);
IAT_ADD_TEST(test_dynamic_encode);
IAT_ADD_TEST(test_parse_invalid);
IAT_ADD_TEST(test_struct_round_trip);
IAT_ADD_TEST(test_struct_parse_error);
IAT_ADD_TEST(test_read_only);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, JSON)

118
Tests/Unit/Logger.cpp Normal file
View File

@ -0,0 +1,118 @@
// 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/IATest.hpp>
#include <IACore/Logger.hpp>
using namespace IACore;
IAT_BEGIN_BLOCK(Core, Logger)
static constexpr const char *LOG_FILE = "iacore_test_log.txt";
auto test_file_logging() -> bool {
const auto res = Logger::enable_logging_to_disk(LOG_FILE);
IAT_CHECK(res.has_value());
Logger::set_log_level(Logger::LogLevel::Trace);
const String msg_info = "Test_Info_Msg_123";
const String msg_err = "Test_Error_Msg_456";
const String msg_warn = "Test_Warn_Msg_789";
Logger::info("{}", msg_info);
Logger::error("{}", msg_err);
Logger::warn("{}", msg_warn);
Logger::flush_logs();
auto read_res = FileOps::read_text_file(LOG_FILE);
if (!read_res) {
std::cout << console::YELLOW << " Warning: Could not read log file ("
<< read_res.error() << "). Skipping verification.\n"
<< console::RESET;
return true;
}
const String content = *read_res;
IAT_CHECK(content.find(msg_info) != String::npos);
IAT_CHECK(content.find(msg_err) != String::npos);
IAT_CHECK(content.find(msg_warn) != String::npos);
IAT_CHECK(content.find("INFO") != String::npos);
IAT_CHECK(content.find("ERROR") != String::npos);
IAT_CHECK(content.find("WARN") != String::npos);
return true;
}
auto test_log_levels() -> bool {
Logger::set_log_level(Logger::LogLevel::Warn);
const String unique_info = "Hidden_Info_Msg";
const String unique_warn = "Visible_Warn_Msg";
Logger::info("{}", unique_info);
Logger::warn("{}", unique_warn);
Logger::flush_logs();
auto read_res = FileOps::read_text_file(LOG_FILE);
if (!read_res) {
return true;
}
const String content = *read_res;
IAT_CHECK(content.find(unique_info) == String::npos);
IAT_CHECK(content.find(unique_warn) != String::npos);
return true;
}
auto test_formatting() -> bool {
Logger::set_log_level(Logger::LogLevel::Info);
const String name = "IACore";
const i32 version = 99;
Logger::info("System {} online v{}", name, version);
Logger::flush_logs();
auto read_res = FileOps::read_text_file(LOG_FILE);
if (!read_res) {
return true;
}
const String content = *read_res;
IAT_CHECK(content.find("System IACore online v99") != String::npos);
return true;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_file_logging);
IAT_ADD_TEST(test_log_levels);
IAT_ADD_TEST(test_formatting);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, Logger)

41
Tests/Unit/Main.cpp Normal file
View File

@ -0,0 +1,41 @@
// 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/IATest.hpp>
#include <IACore/SocketOps.hpp>
using namespace IACore;
IACORE_MAIN() {
(void)args;
OX_TRY_PURE(SocketOps::initialize());
std::cout
<< console::GREEN
<< "\n===============================================================\n";
std::cout << " IACore (Independent Architecture Core) - Unit Test Suite\n";
std::cout
<< "===============================================================\n"
<< console::RESET << "\n";
Const<i32> result = Test::TestRegistry::run_all();
SocketOps::terminate();
return result;
}

119
Tests/Unit/Platform.cpp Normal file
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.
#include <IACore/IATest.hpp>
#include <IACore/Platform.hpp>
#include <cstring>
using namespace IACore;
IAT_BEGIN_BLOCK(Core, Platform)
auto test_os_name() -> bool {
const char *os_name = Platform::get_operating_system_name();
IAT_CHECK(os_name != nullptr);
const String os(os_name);
IAT_CHECK(!os.empty());
#if IA_PLATFORM_WINDOWS
IAT_CHECK_EQ(os, String("Windows"));
#elif IA_PLATFORM_LINUX
IAT_CHECK_EQ(os, String("Linux"));
#elif IA_PLATFORM_APPLE
IAT_CHECK_EQ(os, String("MacOS"));
#elif IA_PLATFORM_WASM
IAT_CHECK_EQ(os, String("WebAssembly"));
#endif
return true;
}
auto test_arch_name() -> bool {
const char *arch_name = Platform::get_architecture_name();
IAT_CHECK(arch_name != nullptr);
const String arch(arch_name);
IAT_CHECK(!arch.empty());
#if IA_ARCH_X64
IAT_CHECK_EQ(arch, String("x86_64"));
#elif IA_ARCH_ARM64
IAT_CHECK_EQ(arch, String("ARM64"));
#elif IA_ARCH_WASM
IAT_CHECK_EQ(arch, String("WASM"));
#endif
return true;
}
auto test_capabilities() -> bool {
const bool check_result = Platform::check_cpu();
IAT_CHECK(check_result);
const auto &caps = Platform::get_capabilities();
volatile bool has_crc = caps.hardware_crc32;
(void)has_crc;
return true;
}
#if IA_ARCH_X64
auto test_cpuid() -> bool {
i32 regs[4] = {0};
Platform::cpuid(0, 0, regs);
IAT_CHECK(regs[0] >= 0);
char vendor[13];
std::memset(vendor, 0, 13);
std::memcpy(vendor, &regs[1], 4);
std::memcpy(vendor + 4, &regs[3], 4);
std::memcpy(vendor + 8, &regs[2], 4);
vendor[12] = '\0';
const String vendor_str(vendor);
IAT_CHECK(!vendor_str.empty());
bool is_known =
(vendor_str == "GenuineIntel" || vendor_str == "AuthenticAMD" ||
vendor_str == "KVMKVMKVM" || vendor_str == "Microsoft Hv" ||
vendor_str == "VBoxVBoxVBox");
if (!is_known) {
std::cout << " [Info] Unknown CPU Vendor: " << vendor_str << "\n";
}
return true;
}
#endif
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_os_name);
IAT_ADD_TEST(test_arch_name);
IAT_ADD_TEST(test_capabilities);
#if IA_ARCH_X64
IAT_ADD_TEST(test_cpuid);
#endif
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, Platform)

213
Tests/Unit/ProcessOps.cpp Normal file
View File

@ -0,0 +1,213 @@
// 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/IATest.hpp>
#include <IACore/ProcessOps.hpp>
using namespace IACore;
#if IA_PLATFORM_WINDOWS
#define CMD_ECHO_EXE "cmd.exe"
#define CMD_ARG_PREFIX "/c echo"
#define NULL_DEVICE "NUL"
#else
#define CMD_ECHO_EXE "/bin/echo"
#define CMD_ARG_PREFIX ""
#define NULL_DEVICE "/dev/null"
#endif
IAT_BEGIN_BLOCK(Core, ProcessOps)
auto test_basic_run() -> bool {
String captured;
const auto result =
ProcessOps::spawn_process_sync(CMD_ECHO_EXE, CMD_ARG_PREFIX " HelloIA",
[&](StringView line) { captured = line; });
IAT_CHECK(result.has_value());
IAT_CHECK_EQ(*result, 0);
IAT_CHECK(captured.find("HelloIA") != String::npos);
return true;
}
auto test_arguments() -> bool {
Vec<String> lines;
String args = String(CMD_ARG_PREFIX) + " one two";
if (!args.empty() && args[0] == ' ') {
args.erase(0, 1);
}
const auto result =
ProcessOps::spawn_process_sync(CMD_ECHO_EXE, args, [&](StringView line) {
lines.push_back(String(line));
});
IAT_CHECK_EQ(*result, 0);
IAT_CHECK(lines.size() > 0);
IAT_CHECK(lines[0].find("one two") != String::npos);
return true;
}
auto test_exit_codes() -> bool {
String cmd;
String arg;
#if IA_PLATFORM_WINDOWS
cmd = "cmd.exe";
arg = "/c exit 42";
#else
cmd = "/bin/sh";
arg = "-c \"exit 42\"";
#endif
const auto result =
ProcessOps::spawn_process_sync(cmd, arg, [](StringView) {});
IAT_CHECK(result.has_value());
IAT_CHECK_EQ(*result, 42);
return true;
}
auto test_missing_exe() -> bool {
const auto result =
ProcessOps::spawn_process_sync("sdflkjghsdflkjg", "", [](StringView) {});
#if IA_PLATFORM_WINDOWS
IAT_CHECK_NOT(result.has_value());
#else
IAT_CHECK(result.has_value());
IAT_CHECK_EQ(*result, 127);
#endif
return true;
}
auto test_large_output() -> bool {
String massive_string;
massive_string.reserve(5000);
for (i32 i = 0; i < 500; ++i) {
massive_string += "1234567890";
}
String cmd;
String arg;
#if IA_PLATFORM_WINDOWS
cmd = "cmd.exe";
arg = "/c echo " + massive_string;
#else
cmd = "/bin/echo";
arg = massive_string;
#endif
String captured;
const auto result = ProcessOps::spawn_process_sync(
cmd, arg, [&](StringView line) { captured += line; });
IAT_CHECK(result.has_value());
IAT_CHECK_EQ(*result, 0);
IAT_CHECK_EQ(captured.length(), massive_string.length());
return true;
}
auto test_multi_line() -> bool {
String cmd;
String arg;
#if IA_PLATFORM_WINDOWS
cmd = "cmd.exe";
arg = "/c \"echo LineA && echo LineB\"";
#else
cmd = "/bin/sh";
arg = "-c \"echo LineA; echo LineB\"";
#endif
i32 line_count = 0;
bool found_a = false;
bool found_b = false;
const auto res =
ProcessOps::spawn_process_sync(cmd, arg, [&](StringView line) {
line_count++;
if (line.find("LineA") != String::npos) {
found_a = true;
}
if (line.find("LineB") != String::npos) {
found_b = true;
}
});
IAT_CHECK(res.has_value());
IAT_CHECK(found_a);
IAT_CHECK(found_b);
IAT_CHECK(line_count >= 2);
return true;
}
auto test_complex_arguments() -> bool {
const String complex_args =
"-DDEFINED_MSG=\\\"Hello World\\\" -v path/to/file";
const String cmd = CMD_ECHO_EXE;
String final_args;
#if IA_PLATFORM_WINDOWS
final_args = "/c echo " + complex_args;
#else
final_args = complex_args;
#endif
String captured;
const auto result = ProcessOps::spawn_process_sync(
cmd, final_args, [&](StringView line) { captured += line; });
IAT_CHECK(result.has_value());
IAT_CHECK_EQ(*result, 0);
IAT_CHECK(captured.find("Hello World") != String::npos);
return true;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_basic_run);
IAT_ADD_TEST(test_arguments);
IAT_ADD_TEST(test_exit_codes);
IAT_ADD_TEST(test_missing_exe);
IAT_ADD_TEST(test_large_output);
IAT_ADD_TEST(test_multi_line);
IAT_ADD_TEST(test_complex_arguments);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, ProcessOps)

107
Tests/Unit/RingBuffer.cpp Normal file
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.
#include <IACore/ADT/RingBuffer.hpp>
#include <IACore/IATest.hpp>
using namespace IACore;
IAT_BEGIN_BLOCK(Core, RingBuffer)
auto test_push_pop() -> bool {
Vec<u8> memory(sizeof(RingBufferView::ControlBlock) + 1024);
auto producer_res = RingBufferView::create(Span<u8>(memory), true);
IAT_CHECK(producer_res.has_value());
auto producer = std::move(*producer_res);
auto consumer_res = RingBufferView::create(Span<u8>(memory), false);
IAT_CHECK(consumer_res.has_value());
auto consumer = std::move(*consumer_res);
String msg = "Hello RingBuffer";
const auto push_res = producer.push(
1, Span<const u8>(reinterpret_cast<const u8 *>(msg.data()), msg.size()));
IAT_CHECK(push_res.has_value());
RingBufferView::PacketHeader header;
u8 read_buf[128];
const auto pop_res = consumer.pop(header, Span<u8>(read_buf, 128));
IAT_CHECK(pop_res.has_value());
const auto bytes_read_opt = *pop_res;
IAT_CHECK(bytes_read_opt.has_value());
const usize bytes_read = *bytes_read_opt;
IAT_CHECK_EQ(header.id, static_cast<u16>(1));
IAT_CHECK_EQ(bytes_read, static_cast<usize>(msg.size()));
String read_msg(reinterpret_cast<char *>(read_buf), bytes_read);
IAT_CHECK_EQ(read_msg, msg);
return true;
}
auto test_wrap_around() -> bool {
Vec<u8> memory(sizeof(RingBufferView::ControlBlock) + 100);
auto rb_res = RingBufferView::create(Span<u8>(memory), true);
IAT_CHECK(rb_res.has_value());
auto rb = std::move(*rb_res);
Vec<u8> junk(80, 0xFF);
const auto push1 = rb.push(1, junk);
IAT_CHECK(push1.has_value());
RingBufferView::PacketHeader header;
u8 out_buf[100];
const auto pop1 = rb.pop(header, out_buf);
IAT_CHECK(pop1.has_value());
IAT_CHECK(pop1->has_value());
Vec<u8> wrap_data(40, 0xAA);
const auto push2 = rb.push(2, wrap_data);
IAT_CHECK(push2.has_value());
const auto pop2 = rb.pop(header, out_buf);
IAT_CHECK(pop2.has_value());
IAT_CHECK(pop2->has_value());
const usize pop_size = *pop2.value();
IAT_CHECK_EQ(pop_size, static_cast<usize>(40));
bool match = true;
for (usize i = 0; i < 40; i++) {
if (out_buf[i] != 0xAA) {
match = false;
}
}
IAT_CHECK(match);
return true;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_push_pop);
IAT_ADD_TEST(test_wrap_around);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, RingBuffer)

View File

@ -0,0 +1,102 @@
// 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/IATest.hpp>
#include <IACore/SIMD.hpp>
using namespace IACore;
IAT_BEGIN_BLOCK(Core, FloatVec4)
auto test_float_arithmetic() -> bool {
FloatVec4 v1(10.0f, 20.0f, 30.0f, 40.0f);
FloatVec4 v2(2.0f, 4.0f, 5.0f, 8.0f);
alignas(16) f32 res[4];
(v1 / v2).store(res);
IAT_CHECK_APPROX(res[0], 5.0f);
IAT_CHECK_APPROX(res[3], 5.0f);
(v1 * v2).store(res);
IAT_CHECK_APPROX(res[0], 20.0f);
(v1 + v2).store(res);
IAT_CHECK_APPROX(res[0], 12.0f);
return true;
}
auto test_math_helpers() -> bool {
alignas(16) f32 res[4];
FloatVec4 v_sq(4.0f, 9.0f, 16.0f, 25.0f);
v_sq.sqrt().store(res);
IAT_CHECK_APPROX(res[0], 2.0f);
IAT_CHECK_APPROX(res[3], 5.0f);
FloatVec4 v_neg(-1.0f, -5.0f, 10.0f, -0.0f);
v_neg.abs().store(res);
IAT_CHECK_APPROX(res[0], 1.0f);
IAT_CHECK_APPROX(res[2], 10.0f);
FloatVec4 v_clamp(-100.0f, 0.0f, 50.0f, 200.0f);
v_clamp.clamp(0.0f, 100.0f).store(res);
IAT_CHECK_APPROX(res[0], 0.0f);
IAT_CHECK_APPROX(res[2], 50.0f);
IAT_CHECK_APPROX(res[3], 100.0f);
return true;
}
auto test_approx_math() -> bool {
alignas(16) f32 res[4];
FloatVec4 v(16.0f, 25.0f, 100.0f, 1.0f);
v.rsqrt().store(res);
IAT_CHECK_APPROX(res[0], 0.25f);
IAT_CHECK_APPROX(res[2], 0.1f);
return true;
}
auto test_linear_algebra() -> bool {
FloatVec4 v1(1.0f, 2.0f, 3.0f, 4.0f);
FloatVec4 v2(1.0f, 0.0f, 1.0f, 0.0f);
f32 dot = v1.dot(v2);
IAT_CHECK_APPROX(dot, 4.0f);
FloatVec4 v_norm(10.0f, 0.0f, 0.0f, 0.0f);
alignas(16) f32 res[4];
v_norm.normalize().store(res);
IAT_CHECK_APPROX(res[0], 1.0f);
IAT_CHECK_APPROX(res[1], 0.0f);
return true;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_float_arithmetic);
IAT_ADD_TEST(test_math_helpers);
IAT_ADD_TEST(test_approx_math);
IAT_ADD_TEST(test_linear_algebra);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, FloatVec4)

144
Tests/Unit/SIMD/IntVec4.cpp Normal file
View File

@ -0,0 +1,144 @@
// 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/IATest.hpp>
#include <IACore/SIMD.hpp>
using namespace IACore;
IAT_BEGIN_BLOCK(Core, IntVec4)
auto test_constructors() -> bool {
IntVec4 v_broadcast(10);
alignas(16) u32 store_buf[4];
v_broadcast.store(store_buf);
IAT_CHECK_EQ(store_buf[0], 10U);
IAT_CHECK_EQ(store_buf[3], 10U);
IntVec4 v_comp(1, 2, 3, 4);
v_comp.store(store_buf);
IAT_CHECK_EQ(store_buf[0], 1U);
IAT_CHECK_EQ(store_buf[3], 4U);
alignas(16) u32 src_buf[4] = {100, 200, 300, 400};
IntVec4 v_load = IntVec4::load(src_buf);
v_load.store(store_buf);
IAT_CHECK_EQ(store_buf[1], 200U);
return true;
}
auto test_arithmetic() -> bool {
const IntVec4 v1(10, 20, 30, 40);
const IntVec4 v2(1, 2, 3, 4);
IntVec4 v_add = v1 + v2;
alignas(16) u32 res[4];
v_add.store(res);
IAT_CHECK_EQ(res[0], 11U);
IAT_CHECK_EQ(res[3], 44U);
IntVec4 v_sub = v1 - v2;
v_sub.store(res);
IAT_CHECK_EQ(res[0], 9U);
IntVec4 v_mul = v1 * v2;
v_mul.store(res);
IAT_CHECK_EQ(res[0], 10U);
IAT_CHECK_EQ(res[2], 90U);
IAT_CHECK_EQ(res[3], 160U);
return true;
}
auto test_bitwise() -> bool {
const IntVec4 v_all_ones(0xFFFFFFFF);
const IntVec4 v_zero((u32)0);
const IntVec4 v_pattern(0xAAAAAAAA);
alignas(16) u32 res[4];
(v_all_ones & v_pattern).store(res);
IAT_CHECK_EQ(res[0], 0xAAAAAAAAU);
(v_zero | v_pattern).store(res);
IAT_CHECK_EQ(res[0], 0xAAAAAAAAU);
(v_all_ones ^ v_pattern).store(res);
IAT_CHECK_EQ(res[0], 0x55555555U);
(~v_pattern).store(res);
IAT_CHECK_EQ(res[0], 0x55555555U);
const IntVec4 v_shift(1);
(v_shift << 1).store(res);
IAT_CHECK_EQ(res[0], 2U);
const IntVec4 v_shift_right(4);
(v_shift_right >> 1).store(res);
IAT_CHECK_EQ(res[0], 2U);
return true;
}
auto test_saturation() -> bool {
const u32 max = 0xFFFFFFFF;
const IntVec4 v_high(max - 10);
const IntVec4 v_add(20);
alignas(16) u32 res[4];
v_high.sat_add(v_add).store(res);
IAT_CHECK_EQ(res[0], max);
const IntVec4 v_low(10);
const IntVec4 v_sub(20);
v_low.sat_sub(v_sub).store(res);
IAT_CHECK_EQ(res[0], 0U);
return true;
}
auto test_advanced_ops() -> bool {
const IntVec4 v(0, 50, 100, 150);
alignas(16) u32 res[4];
v.clamp(40, 110).store(res);
IAT_CHECK_EQ(res[0], 40U);
IAT_CHECK_EQ(res[1], 50U);
IAT_CHECK_EQ(res[2], 100U);
IAT_CHECK_EQ(res[3], 110U);
const IntVec4 a(2);
const IntVec4 b(10);
const IntVec4 c(5);
a.mult_add(b, c).store(res);
IAT_CHECK_EQ(res[0], 25U);
return true;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_constructors);
IAT_ADD_TEST(test_arithmetic);
IAT_ADD_TEST(test_bitwise);
IAT_ADD_TEST(test_saturation);
IAT_ADD_TEST(test_advanced_ops);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, IntVec4)

107
Tests/Unit/SocketOps.cpp Normal file
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.
#include <IACore/IATest.hpp>
#include <IACore/SocketOps.hpp>
using namespace IACore;
IAT_BEGIN_BLOCK(Core, SocketOps)
auto test_initialization() -> bool {
IAT_CHECK(SocketOps::is_initialized());
const auto res = SocketOps::initialize();
IAT_CHECK(res.has_value());
SocketOps::terminate();
IAT_CHECK(SocketOps::is_initialized());
return true;
}
auto test_port_availability() -> bool {
const u16 port = 54321;
(void)SocketOps::is_port_available_tcp(port);
(void)SocketOps::is_port_available_udp(port);
return true;
}
auto test_unix_socket_lifecycle() -> bool {
const String socket_path = "iatest_ipc.sock";
SocketOps::unlink_file(socket_path.c_str());
auto server_res = SocketOps::create_unix_socket();
IAT_CHECK(server_res.has_value());
SocketHandle server = *server_res;
auto bind_res = SocketOps::bind_unix_socket(server, socket_path.c_str());
if (!bind_res) {
SocketOps::close(server);
return false;
}
auto listen_res = SocketOps::listen(server);
IAT_CHECK(listen_res.has_value());
auto client_res = SocketOps::create_unix_socket();
IAT_CHECK(client_res.has_value());
SocketHandle client = *client_res;
auto connect_res =
SocketOps::connect_unix_socket(client, socket_path.c_str());
IAT_CHECK(connect_res.has_value());
SocketOps::close(client);
SocketOps::close(server);
SocketOps::unlink_file(socket_path.c_str());
return true;
}
auto test_unix_socket_errors() -> bool {
const String socket_path = "iatest_missing.sock";
SocketOps::unlink_file(socket_path.c_str());
auto client_res = SocketOps::create_unix_socket();
IAT_CHECK(client_res.has_value());
SocketHandle client = *client_res;
auto connect_res =
SocketOps::connect_unix_socket(client, socket_path.c_str());
IAT_CHECK_NOT(connect_res.has_value());
SocketOps::close(client);
return true;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_initialization);
IAT_ADD_TEST(test_port_availability);
IAT_ADD_TEST(test_unix_socket_lifecycle);
IAT_ADD_TEST(test_unix_socket_errors);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, SocketOps)

137
Tests/Unit/StreamReader.cpp Normal file
View File

@ -0,0 +1,137 @@
// 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/IATest.hpp>
#include <IACore/StreamReader.hpp>
using namespace IACore;
IAT_BEGIN_BLOCK(Core, StreamReader)
auto test_read_uint8() -> bool {
u8 data[] = {0xAA, 0xBB, 0xCC};
StreamReader reader(data);
auto val1 = reader.read<u8>();
IAT_CHECK(val1.has_value());
IAT_CHECK_EQ(*val1, 0xAA);
IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(1));
auto val2 = reader.read<u8>();
IAT_CHECK(val2.has_value());
IAT_CHECK_EQ(*val2, 0xBB);
return true;
}
auto test_read_multi_byte() -> bool {
u8 data[] = {0x01, 0x02, 0x03, 0x04};
StreamReader reader(data);
auto val = reader.read<u32>();
IAT_CHECK(val.has_value());
IAT_CHECK_EQ(*val, static_cast<u32>(0x04030201));
IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(4));
IAT_CHECK(reader.is_eof());
return true;
}
auto test_read_float() -> bool {
const f32 pi = 3.14159f;
u8 data[4];
std::memcpy(data, &pi, 4);
StreamReader reader(data);
auto val = reader.read<f32>();
IAT_CHECK(val.has_value());
IAT_CHECK_APPROX(*val, pi);
return true;
}
auto test_read_buffer() -> bool {
u8 src[] = {1, 2, 3, 4, 5};
u8 dst[3] = {0};
StreamReader reader(src);
const auto res = reader.read(dst, 3);
IAT_CHECK(res.has_value());
IAT_CHECK_EQ(dst[0], 1);
IAT_CHECK_EQ(dst[1], 2);
IAT_CHECK_EQ(dst[2], 3);
IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(3));
return true;
}
auto test_navigation() -> bool {
u8 data[10] = {0};
StreamReader reader(data);
IAT_CHECK_EQ(reader.remaining(), static_cast<usize>(10));
reader.skip(5);
IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(5));
IAT_CHECK_EQ(reader.remaining(), static_cast<usize>(5));
reader.skip(100);
IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(10));
IAT_CHECK(reader.is_eof());
reader.seek(2);
IAT_CHECK_EQ(reader.cursor(), static_cast<usize>(2));
IAT_CHECK_EQ(reader.remaining(), static_cast<usize>(8));
IAT_CHECK_NOT(reader.is_eof());
return true;
}
auto test_boundary_checks() -> bool {
u8 data[] = {0x00, 0x00};
StreamReader reader(data);
(void)reader.read<u16>();
IAT_CHECK(reader.is_eof());
auto val = reader.read<u8>();
IAT_CHECK_NOT(val.has_value());
u8 buf[1];
auto batch = reader.read(buf, 1);
IAT_CHECK_NOT(batch.has_value());
return true;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_read_uint8);
IAT_ADD_TEST(test_read_multi_byte);
IAT_ADD_TEST(test_read_float);
IAT_ADD_TEST(test_read_buffer);
IAT_ADD_TEST(test_navigation);
IAT_ADD_TEST(test_boundary_checks);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, StreamReader)

116
Tests/Unit/StreamWriter.cpp Normal file
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.
#include <IACore/FileOps.hpp>
#include <IACore/IATest.hpp>
#include <IACore/StreamWriter.hpp>
using namespace IACore;
IAT_BEGIN_BLOCK(Core, StreamWriter)
auto test_memory_writer() -> bool {
StreamWriter writer;
IAT_CHECK(writer.write(static_cast<u8>(0xAA), 1).has_value());
const u32 val = 0x12345678;
IAT_CHECK(writer.write(val).has_value());
IAT_CHECK_EQ(writer.cursor(), static_cast<usize>(1 + 4));
const u8 *ptr = writer.data();
IAT_CHECK_EQ(ptr[0], 0xAA);
IAT_CHECK_EQ(ptr[1], 0x78);
IAT_CHECK_EQ(ptr[4], 0x12);
return true;
}
auto test_fixed_buffer() -> bool {
u8 buffer[4] = {0};
StreamWriter writer(Span<u8>(buffer, 4));
IAT_CHECK(writer.write(static_cast<u8>(0xFF), 2).has_value());
IAT_CHECK_EQ(writer.cursor(), static_cast<usize>(2));
IAT_CHECK(writer.write(static_cast<u8>(0xEE), 2).has_value());
IAT_CHECK_EQ(writer.cursor(), static_cast<usize>(4));
const auto res = writer.write(static_cast<u8>(0x00), 1);
IAT_CHECK_NOT(res.has_value());
IAT_CHECK_EQ(buffer[0], 0xFF);
IAT_CHECK_EQ(buffer[1], 0xFF);
IAT_CHECK_EQ(buffer[2], 0xEE);
IAT_CHECK_EQ(buffer[3], 0xEE);
return true;
}
auto test_file_writer() -> bool {
const Path path = "test_stream_writer.bin";
if (std::filesystem::exists(path)) {
std::filesystem::remove(path);
}
{
auto res = StreamWriter::create_from_file(path);
IAT_CHECK(res.has_value());
StreamWriter writer = std::move(*res);
const String hello = "Hello World";
IAT_CHECK(writer.write(hello.data(), hello.size()).has_value());
IAT_CHECK(writer.flush().has_value());
}
auto read_res = FileOps::read_binary_file(path);
IAT_CHECK(read_res.has_value());
const String read_str(reinterpret_cast<const char *>(read_res->data()),
read_res->size());
IAT_CHECK_EQ(read_str, String("Hello World"));
std::filesystem::remove(path);
return true;
}
auto test_primitives() -> bool {
StreamWriter writer;
const f32 f = 1.5f;
const u64 big = 0xDEADBEEFCAFEBABE;
IAT_CHECK(writer.write(f).has_value());
IAT_CHECK(writer.write(big).has_value());
IAT_CHECK_EQ(writer.cursor(), sizeof(f32) + sizeof(u64));
return true;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_memory_writer);
IAT_ADD_TEST(test_fixed_buffer);
IAT_ADD_TEST(test_file_writer);
IAT_ADD_TEST(test_primitives);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, StreamWriter)

111
Tests/Unit/StringOps.cpp Normal file
View File

@ -0,0 +1,111 @@
// 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/IATest.hpp>
#include <IACore/StringOps.hpp>
using namespace IACore;
IAT_BEGIN_BLOCK(Core, StringOps)
auto test_base64_encode() -> bool {
{
const String s = "Hello World";
const Span<const u8> data(reinterpret_cast<const u8 *>(s.data()), s.size());
const String encoded = StringOps::encode_base64(data);
IAT_CHECK_EQ(encoded, String("SGVsbG8gV29ybGQ="));
}
{
const String s = "M";
const Span<const u8> data(reinterpret_cast<const u8 *>(s.data()), s.size());
const String encoded = StringOps::encode_base64(data);
IAT_CHECK_EQ(encoded, String("TQ=="));
}
{
const String s = "Ma";
const Span<const u8> data(reinterpret_cast<const u8 *>(s.data()), s.size());
const String encoded = StringOps::encode_base64(data);
IAT_CHECK_EQ(encoded, String("TWE="));
}
{
const String s = "Man";
const Span<const u8> data(reinterpret_cast<const u8 *>(s.data()), s.size());
const String encoded = StringOps::encode_base64(data);
IAT_CHECK_EQ(encoded, String("TWFu"));
}
{
const String encoded = StringOps::encode_base64({});
IAT_CHECK(encoded.empty());
}
return true;
}
auto test_base64_decode() -> bool {
{
const String encoded = "SGVsbG8gV29ybGQ=";
const Vec<u8> decoded = StringOps::decode_base64(encoded);
const String result(reinterpret_cast<const char *>(decoded.data()),
decoded.size());
IAT_CHECK_EQ(result, String("Hello World"));
}
{
const Vec<u8> decoded = StringOps::decode_base64("");
IAT_CHECK(decoded.empty());
}
return true;
}
auto test_base64_round_trip() -> bool {
Vec<u8> original;
original.reserve(256);
for (usize i = 0; i < 256; ++i) {
original.push_back(static_cast<u8>(i));
}
const String encoded = StringOps::encode_base64(original);
const Vec<u8> decoded = StringOps::decode_base64(encoded);
IAT_CHECK_EQ(original.size(), decoded.size());
bool match = true;
for (usize i = 0; i < original.size(); ++i) {
if (original[i] != decoded[i]) {
match = false;
break;
}
}
IAT_CHECK(match);
return true;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_base64_encode);
IAT_ADD_TEST(test_base64_decode);
IAT_ADD_TEST(test_base64_round_trip);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, StringOps)

162
Tests/Unit/Utils.cpp Normal file
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/IATest.hpp>
#include <IACore/Utils.hpp>
using namespace IACore;
struct TestVec3 {
f32 x, y, z;
bool operator==(const TestVec3 &other) const {
return x == other.x && y == other.y && z == other.z;
}
};
IA_MAKE_HASHABLE(TestVec3, &TestVec3::x, &TestVec3::y, &TestVec3::z);
IAT_BEGIN_BLOCK(Core, Utils)
auto test_hex_conversion() -> bool {
u8 bin[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF};
String hex = Utils::binary_to_hex_string(bin);
IAT_CHECK_EQ(hex, String("DEADBEEF00FF"));
auto res_upper = Utils::hex_string_to_binary("DEADBEEF00FF");
IAT_CHECK(res_upper.has_value());
IAT_CHECK_EQ(res_upper->size(), static_cast<usize>(6));
IAT_CHECK_EQ((*res_upper)[0], 0xDE);
IAT_CHECK_EQ((*res_upper)[5], 0xFF);
auto res_lower = Utils::hex_string_to_binary("deadbeef00ff");
IAT_CHECK(res_lower.has_value());
IAT_CHECK_EQ((*res_lower)[0], 0xDE);
Vec<u8> original = {1, 2, 3, 4, 5};
String s = Utils::binary_to_hex_string(original);
auto back = Utils::hex_string_to_binary(s);
IAT_CHECK(back.has_value());
IAT_CHECK_EQ(original.size(), back->size());
IAT_CHECK_EQ(original[2], (*back)[2]);
return true;
}
auto test_hex_errors() -> bool {
auto odd = Utils::hex_string_to_binary("ABC");
IAT_CHECK_NOT(odd.has_value());
auto invalid = Utils::hex_string_to_binary("ZZTOP");
IAT_CHECK_NOT(invalid.has_value());
auto empty = Utils::hex_string_to_binary("");
IAT_CHECK(empty.has_value());
IAT_CHECK_EQ(empty->size(), static_cast<usize>(0));
return true;
}
auto test_sort() -> bool {
Vec<i32> nums = {5, 1, 4, 2, 3};
Utils::sort(nums);
IAT_CHECK_EQ(nums[0], 1);
IAT_CHECK_EQ(nums[1], 2);
IAT_CHECK_EQ(nums[2], 3);
IAT_CHECK_EQ(nums[3], 4);
IAT_CHECK_EQ(nums[4], 5);
return true;
}
auto test_binary_search() -> bool {
Vec<i32> nums = {10, 20, 20, 20, 30};
auto it_left = Utils::binary_search_left(nums, 20);
IAT_CHECK(it_left != nums.end());
IAT_CHECK_EQ(*it_left, 20);
IAT_CHECK_EQ(std::distance(nums.begin(), it_left), 1);
auto it_right = Utils::binary_search_right(nums, 20);
IAT_CHECK(it_right != nums.end());
IAT_CHECK_EQ(*it_right, 30);
IAT_CHECK_EQ(std::distance(nums.begin(), it_right), 4);
auto it_fail = Utils::binary_search_left(nums, 99);
IAT_CHECK(it_fail == nums.end());
return true;
}
auto test_hash_basics() -> bool {
u64 h1 = Utils::compute_hash(10, 20.5f, "Hello");
u64 h2 = Utils::compute_hash(10, 20.5f, "Hello");
u64 h3 = Utils::compute_hash(10, 20.5f, "World");
IAT_CHECK_EQ(h1, h2);
IAT_CHECK_NEQ(h1, h3);
u64 order_a = Utils::compute_hash(1, 2);
u64 order_b = Utils::compute_hash(2, 1);
IAT_CHECK_NEQ(order_a, order_b);
return true;
}
auto test_hash_macro() -> bool {
TestVec3 v1{1.0f, 2.0f, 3.0f};
TestVec3 v2{1.0f, 2.0f, 3.0f};
TestVec3 v3{1.0f, 2.0f, 4.0f};
ankerl::unordered_dense::hash<TestVec3> hasher;
u64 h1 = hasher(v1);
u64 h2 = hasher(v2);
u64 h3 = hasher(v3);
IAT_CHECK_EQ(h1, h2);
IAT_CHECK_NEQ(h1, h3);
u64 h_manual = 0;
Utils::hash_combine(h_manual, v1);
u64 h_wrapper = Utils::compute_hash(v1);
IAT_CHECK_EQ(h_manual, h_wrapper);
IAT_CHECK_NEQ(h1, h_wrapper);
return true;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_hex_conversion);
IAT_ADD_TEST(test_hex_errors);
IAT_ADD_TEST(test_sort);
IAT_ADD_TEST(test_binary_search);
IAT_ADD_TEST(test_hash_basics);
IAT_ADD_TEST(test_hash_macro);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, Utils)

112
Tests/Unit/XML.cpp Normal file
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/FileOps.hpp>
#include <IACore/IATest.hpp>
#include <IACore/XML.hpp>
using namespace IACore;
IAT_BEGIN_BLOCK(Core, XML)
auto test_parse_string() -> bool {
const String xml_content = R"(
<root>
<item id="1">Value1</item>
<item id="2">Value2</item>
</root>
)";
auto res = XML::parse_from_string(xml_content);
IAT_CHECK(res.has_value());
auto &doc = *res;
auto root = doc.child("root");
IAT_CHECK(root);
auto item1 = root.find_child_by_attribute("item", "id", "1");
IAT_CHECK(item1);
IAT_CHECK_EQ(String(item1.child_value()), String("Value1"));
auto item2 = root.find_child_by_attribute("item", "id", "2");
IAT_CHECK(item2);
IAT_CHECK_EQ(String(item2.child_value()), String("Value2"));
return true;
}
auto test_parse_error() -> bool {
const String invalid_xml = "<root><unclosed>";
auto res = XML::parse_from_string(invalid_xml);
IAT_CHECK_NOT(res.has_value());
return true;
}
auto test_serialize() -> bool {
const String xml_content = "<root><node>Text</node></root>";
auto res = XML::parse_from_string(xml_content);
IAT_CHECK(res.has_value());
String output = XML::serialize_to_string(*res);
IAT_CHECK(output.find("<root>") != String::npos);
IAT_CHECK(output.find("<node>Text</node>") != String::npos);
return true;
}
auto test_escape() -> bool {
const String raw = "< & > \" '";
const String escaped = XML::escape_xml_string(raw);
IAT_CHECK(escaped.find("&lt;") != String::npos);
IAT_CHECK(escaped.find("&amp;") != String::npos);
IAT_CHECK(escaped.find("&gt;") != String::npos);
IAT_CHECK(escaped.find("&quot;") != String::npos);
IAT_CHECK(escaped.find("&apos;") != String::npos);
return true;
}
auto test_file_io() -> bool {
const Path path = "test_temp_xml_doc.xml";
const String content = "<config><ver>1.0</ver></config>";
auto write_res = FileOps::write_text_file(path, content, true);
IAT_CHECK(write_res.has_value());
auto parse_res = XML::parse_from_file(path);
IAT_CHECK(parse_res.has_value());
auto &doc = *parse_res;
IAT_CHECK_EQ(String(doc.child("config").child("ver").child_value()),
String("1.0"));
std::filesystem::remove(path);
return true;
}
IAT_BEGIN_TEST_LIST()
IAT_ADD_TEST(test_parse_string);
IAT_ADD_TEST(test_parse_error);
IAT_ADD_TEST(test_serialize);
IAT_ADD_TEST(test_escape);
IAT_ADD_TEST(test_file_io);
IAT_END_TEST_LIST()
IAT_END_BLOCK()
IAT_REGISTER_ENTRY(Core, XML)

23
logo.svg Normal file
View File

@ -0,0 +1,23 @@
<svg width="400" height="120" viewBox="0 0 400 120" fill="none" xmlns="http://www.w3.org/2000/svg">
<style>
.text { font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-weight: bold; }
.secondary { font-weight: 300; letter-spacing: 2px; }
</style>
<g transform="translate(20, 10)">
<path d="M50 5 L93.3 30 V80 L50 105 L6.7 80 V30 L50 5 Z" stroke="#3b82f6" stroke-width="4"
fill="none" />
<rect x="42" y="25" width="16" height="60" rx="2" fill="#3b82f6" />
<circle cx="20" cy="55" r="4" fill="#60a5fa" />
<circle cx="80" cy="55" r="4" fill="#60a5fa" />
<path d="M24 55 H42" stroke="#60a5fa" stroke-width="2" />
<path d="M58 55 H76" stroke="#60a5fa" stroke-width="2" />
</g>
<text x="120" y="65" fill="#e2e8f0" font-size="52" class="text">IACore</text>
<text x="122" y="95" fill="#94a3b8" font-size="14" class="secondary">INDEPENDENT ARCHITECTURE</text>
</svg>

After

Width:  |  Height:  |  Size: 979 B