aboutsummaryrefslogtreecommitdiffhomepage
path: root/externals/zycore
diff options
context:
space:
mode:
authorMerryMage <[email protected]>2021-05-25 21:28:55 +0100
committerMerryMage <[email protected]>2021-05-25 21:28:58 +0100
commiteed33f255dc6890b284dc7800565d2b8bb3cf31d (patch)
tree5ca4b9eb990ed5ec74645a63199ae39e637844e4 /externals/zycore
parent343b21ff7b609a37a87a7d3e4bfd8298bea331ef (diff)
parent80d62f224900ab486a5bc5a6e80ce1e25a0e38e8 (diff)
downloaddynarmic-eed33f255dc6890b284dc7800565d2b8bb3cf31d.tar.gz
dynarmic-eed33f255dc6890b284dc7800565d2b8bb3cf31d.zip
externals: Add zycore
Merge commit '80d62f224900ab486a5bc5a6e80ce1e25a0e38e8' as 'externals/zycore'
Diffstat (limited to 'externals/zycore')
-rw-r--r--externals/zycore/.gitignore100
-rw-r--r--externals/zycore/CMakeLists.txt266
-rw-r--r--externals/zycore/CMakeLists.txt.in15
-rw-r--r--externals/zycore/LICENSE23
-rw-r--r--externals/zycore/README.md25
-rw-r--r--externals/zycore/cmake/zycore-config.cmake.in8
-rw-r--r--externals/zycore/examples/String.c192
-rw-r--r--externals/zycore/examples/Vector.c395
-rw-r--r--externals/zycore/include/Zycore/API/Memory.h134
-rw-r--r--externals/zycore/include/Zycore/API/Process.h67
-rw-r--r--externals/zycore/include/Zycore/API/Synchronization.h133
-rw-r--r--externals/zycore/include/Zycore/API/Terminal.h163
-rw-r--r--externals/zycore/include/Zycore/API/Thread.h244
-rw-r--r--externals/zycore/include/Zycore/Allocator.h143
-rw-r--r--externals/zycore/include/Zycore/ArgParse.h173
-rw-r--r--externals/zycore/include/Zycore/Bitset.h484
-rw-r--r--externals/zycore/include/Zycore/Comparison.h316
-rw-r--r--externals/zycore/include/Zycore/Defines.h443
-rw-r--r--externals/zycore/include/Zycore/Format.h286
-rw-r--r--externals/zycore/include/Zycore/LibC.h511
-rw-r--r--externals/zycore/include/Zycore/List.h574
-rw-r--r--externals/zycore/include/Zycore/Object.h84
-rw-r--r--externals/zycore/include/Zycore/Status.h287
-rw-r--r--externals/zycore/include/Zycore/String.h1012
-rw-r--r--externals/zycore/include/Zycore/Types.h195
-rw-r--r--externals/zycore/include/Zycore/Vector.h723
-rw-r--r--externals/zycore/include/Zycore/Zycore.h111
-rw-r--r--externals/zycore/resources/VersionInfo.rcbin0 -> 4500 bytes
-rw-r--r--externals/zycore/src/API/Memory.c128
-rw-r--r--externals/zycore/src/API/Process.c68
-rw-r--r--externals/zycore/src/API/Synchronization.c200
-rw-r--r--externals/zycore/src/API/Terminal.c156
-rw-r--r--externals/zycore/src/API/Thread.c194
-rw-r--r--externals/zycore/src/Allocator.c134
-rw-r--r--externals/zycore/src/ArgParse.c279
-rw-r--r--externals/zycore/src/Bitset.c670
-rw-r--r--externals/zycore/src/Format.c507
-rw-r--r--externals/zycore/src/List.c673
-rw-r--r--externals/zycore/src/String.c1098
-rw-r--r--externals/zycore/src/Vector.c848
-rw-r--r--externals/zycore/src/Zycore.c38
-rw-r--r--externals/zycore/tests/ArgParse.cpp320
-rw-r--r--externals/zycore/tests/String.cpp69
-rw-r--r--externals/zycore/tests/Vector.cpp505
44 files changed, 12994 insertions, 0 deletions
diff --git a/externals/zycore/.gitignore b/externals/zycore/.gitignore
new file mode 100644
index 00000000..8941d973
--- /dev/null
+++ b/externals/zycore/.gitignore
@@ -0,0 +1,100 @@
+# Created by https://www.gitignore.io/api/c,c++,cmake
+
+### C ###
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+
+
+### C++ ###
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+
+
+### CMake ###
+CMakeCache.txt
+CMakeFiles
+CMakeScripts
+Makefile
+cmake_install.cmake
+install_manifest.txt
+CTestTestfile.cmake
+
+
+# MacOS
+.DS_Store
+
+build*
+
+# MSVC
+.vs
+*.vcxproj.user
+*.suo
+*.sdf
+*.opensdf
+*.VC.db
+*.VC.opendb
+msvc/**/obj/
+msvc/**/bin/
+
+doc/html
+
+/.vscode
+/.idea
+/cmake-build-*
diff --git a/externals/zycore/CMakeLists.txt b/externals/zycore/CMakeLists.txt
new file mode 100644
index 00000000..ad01d650
--- /dev/null
+++ b/externals/zycore/CMakeLists.txt
@@ -0,0 +1,266 @@
+if (TARGET Zycore)
+ return()
+endif ()
+
+cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
+include(GenerateExportHeader)
+include(GNUInstallDirs)
+include(CMakePackageConfigHelpers)
+
+project(Zycore VERSION 1.0.0.0 LANGUAGES C CXX)
+
+# =============================================================================================== #
+# Overridable options #
+# =============================================================================================== #
+
+# Global configuration
+option(ZYAN_WHOLE_PROGRAM_OPTIMIZATION
+ "Enable whole program optimization (all targets)"
+ OFF)
+option(ZYAN_NO_LIBC
+ "Don't use any C standard library functions (for exotic build-envs like kernel drivers)"
+ OFF)
+option(ZYAN_DEV_MODE
+ "Enable developer mode (-Wall, -Werror, ...)"
+ OFF)
+
+# Build configuration
+option(ZYCORE_BUILD_SHARED_LIB
+ "Build shared library"
+ OFF)
+option(ZYCORE_BUILD_EXAMPLES
+ "Build examples"
+ OFF)
+option(ZYCORE_BUILD_TESTS
+ "Build tests"
+ OFF)
+
+# =============================================================================================== #
+# GoogleTest #
+# =============================================================================================== #
+
+# Download and unpack googletest
+if (ZYCORE_BUILD_TESTS)
+ if (NOT DEFINED ZYCORE_DOWNLOADED_GTEST)
+ configure_file("CMakeLists.txt.in" "${CMAKE_BINARY_DIR}/gtest/download/CMakeLists.txt")
+ execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
+ RESULT_VARIABLE result
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/gtest/download")
+ if (result)
+ message(FATAL_ERROR "CMake step for googletest failed: ${result}")
+ endif()
+ execute_process(COMMAND ${CMAKE_COMMAND} --build .
+ RESULT_VARIABLE result
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/gtest/download")
+ if (result)
+ message(FATAL_ERROR "Build step for googletest failed: ${result}")
+ endif()
+
+ set(ZYCORE_DOWNLOADED_GTEST TRUE CACHE BOOL "")
+ mark_as_advanced(ZYCORE_DOWNLOADED_GTEST)
+ endif ()
+
+ set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+
+ add_subdirectory("${CMAKE_BINARY_DIR}/gtest/src" "${CMAKE_BINARY_DIR}/gtest/build"
+ EXCLUDE_FROM_ALL)
+endif ()
+
+# =============================================================================================== #
+# Exported functions #
+# =============================================================================================== #
+
+function (zyan_set_common_flags target)
+ if (NOT MSVC)
+ target_compile_options("${target}" PRIVATE "-std=c99")
+ endif ()
+
+ if (ZYAN_DEV_MODE)
+ # If in developer mode, be pedantic.
+ if (MSVC)
+ target_compile_options("${target}" PUBLIC "/WX" "/W4")
+ else ()
+ target_compile_options("${target}" PUBLIC "-Wall" "-pedantic" "-Wextra" "-Werror")
+ endif ()
+ endif ()
+endfunction ()
+
+function (zyan_set_source_group target)
+ if (ZYAN_DEV_MODE)
+ if (((CMAKE_MAJOR_VERSION GREATER 3) OR (CMAKE_MAJOR_VERSION EQUAL 3)) AND
+ ((CMAKE_MINOR_VERSION GREATER 8) OR (CMAKE_MINOR_VERSION EQUAL 8)))
+ # Mirror directory structure in project files
+ get_property("TARGET_SOURCE_FILES" TARGET "${target}" PROPERTY SOURCES)
+ source_group(TREE "${CMAKE_CURRENT_LIST_DIR}" FILES ${TARGET_SOURCE_FILES})
+ endif ()
+ endif ()
+endfunction ()
+
+function (zyan_maybe_enable_wpo target)
+ if (ZYAN_WHOLE_PROGRAM_OPTIMIZATION AND MSVC)
+ set_target_properties("${target}" PROPERTIES COMPILE_FLAGS "/GL")
+ set_target_properties("${target}" PROPERTIES LINK_FLAGS_RELEASE "/LTCG")
+ endif ()
+endfunction ()
+
+function (zyan_maybe_enable_wpo_for_lib target)
+ if (ZYAN_WHOLE_PROGRAM_OPTIMIZATION AND MSVC)
+ set_target_properties("${target}" PROPERTIES COMPILE_FLAGS "/GL")
+ set_target_properties("${target}" PROPERTIES LINK_FLAGS_RELEASE "/LTCG")
+ set_target_properties("${target}" PROPERTIES STATIC_LIBRARY_FLAGS_RELEASE "/LTCG")
+ endif ()
+endfunction ()
+
+# =============================================================================================== #
+# Library configuration #
+# =============================================================================================== #
+
+if (ZYCORE_BUILD_SHARED_LIB)
+ add_library("Zycore" SHARED)
+else ()
+ add_library("Zycore" STATIC)
+endif ()
+
+set_target_properties("Zycore" PROPERTIES LINKER_LANGUAGE C)
+target_include_directories("Zycore"
+ PUBLIC "include" ${PROJECT_BINARY_DIR}
+ PRIVATE "src")
+target_compile_definitions("Zycore" PRIVATE "_CRT_SECURE_NO_WARNINGS" "ZYCORE_EXPORTS")
+zyan_set_common_flags("Zycore")
+zyan_maybe_enable_wpo_for_lib("Zycore")
+generate_export_header("Zycore" BASE_NAME "ZYCORE" EXPORT_FILE_NAME "ZycoreExportConfig.h")
+
+if (ZYAN_NO_LIBC)
+ target_compile_definitions("Zycore" PUBLIC "ZYAN_NO_LIBC")
+ if (UNIX)
+ set_target_properties("Zycore" PROPERTIES LINK_FLAGS "-nostdlib -nodefaultlibs")
+ endif ()
+endif ()
+
+target_sources("Zycore"
+ PRIVATE
+ # API
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/API/Memory.h"
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/API/Process.h"
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/API/Synchronization.h"
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/API/Terminal.h"
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/API/Thread.h"
+ # Common
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/Allocator.h"
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/ArgParse.h"
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/Bitset.h"
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/Comparison.h"
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/Defines.h"
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/Format.h"
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/LibC.h"
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/List.h"
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/Object.h"
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/Status.h"
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/String.h"
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/Types.h"
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/Vector.h"
+ "${CMAKE_CURRENT_LIST_DIR}/include/Zycore/Zycore.h"
+ # Common
+ "src/Allocator.c"
+ "src/ArgParse.c"
+ "src/Bitset.c"
+ "src/Format.c"
+ "src/List.c"
+ "src/String.c"
+ "src/Vector.c"
+ "src/Zycore.c")
+
+if (NOT ZYAN_NO_LIBC)
+ target_sources("Zycore"
+ PRIVATE
+ # API
+ "src/API/Memory.c"
+ "src/API/Process.c"
+ "src/API/Synchronization.c"
+ "src/API/Terminal.c"
+ "src/API/Thread.c")
+endif ()
+
+if (ZYCORE_BUILD_SHARED_LIB AND WIN32)
+ target_sources("Zycore" PRIVATE "resources/VersionInfo.rc")
+endif ()
+
+zyan_set_source_group("Zycore")
+
+if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT ZYAN_NO_LIBC)
+ target_compile_definitions("Zycore" PRIVATE "_GNU_SOURCE")
+ find_package(Threads REQUIRED)
+ target_link_libraries("Zycore" Threads::Threads)
+endif ()
+
+configure_package_config_file(cmake/zycore-config.cmake.in
+ "${CMAKE_CURRENT_BINARY_DIR}/zycore-config.cmake"
+ INSTALL_DESTINATION "${CMAKE_INSTALL_PREFIX}/cmake"
+)
+install(FILES
+ "${CMAKE_CURRENT_BINARY_DIR}/zycore-config.cmake"
+ DESTINATION "${CMAKE_INSTALL_PREFIX}/cmake"
+)
+
+install(TARGETS "Zycore"
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+install(FILES
+ "${PROJECT_BINARY_DIR}/ZycoreExportConfig.h"
+ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
+install(DIRECTORY "include/" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+
+# =============================================================================================== #
+# Developer mode #
+# =============================================================================================== #
+
+if (ZYAN_DEV_MODE)
+ set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+endif ()
+
+# =============================================================================================== #
+# Examples #
+# =============================================================================================== #
+
+if (ZYCORE_BUILD_EXAMPLES)
+ add_executable("String" "examples/String.c")
+ zyan_set_common_flags("String" "Zycore")
+ target_link_libraries("String" "Zycore")
+ set_target_properties("String" PROPERTIES FOLDER "Examples")
+ target_compile_definitions("String" PRIVATE "_CRT_SECURE_NO_WARNINGS")
+ zyan_maybe_enable_wpo("String")
+
+ add_executable("Vector" "examples/Vector.c")
+ zyan_set_common_flags("Vector" "Zycore")
+ target_link_libraries("Vector" "Zycore")
+ set_target_properties("Vector" PROPERTIES FOLDER "Examples")
+ target_compile_definitions("Vector" PRIVATE "_CRT_SECURE_NO_WARNINGS")
+ zyan_maybe_enable_wpo("Vector")
+endif ()
+
+# =============================================================================================== #
+# Tests #
+# =============================================================================================== #
+
+function (zyan_add_test test)
+ add_executable("Test${test}" "tests/${test}.cpp")
+
+ if (NOT MSVC)
+ target_compile_options("Test${test}" PRIVATE "-std=c++17")
+ endif ()
+
+ target_link_libraries("Test${test}" "Zycore")
+ target_link_libraries("Test${test}" "gtest")
+ set_target_properties("Test${test}" PROPERTIES FOLDER "Tests")
+ target_compile_definitions("Test${test}" PRIVATE "_CRT_SECURE_NO_WARNINGS")
+ zyan_maybe_enable_wpo("Test${test}")
+endfunction ()
+
+if (ZYCORE_BUILD_TESTS)
+ zyan_add_test("String")
+ zyan_add_test("Vector")
+ zyan_add_test("ArgParse")
+endif ()
+
+# =============================================================================================== #
diff --git a/externals/zycore/CMakeLists.txt.in b/externals/zycore/CMakeLists.txt.in
new file mode 100644
index 00000000..3333512f
--- /dev/null
+++ b/externals/zycore/CMakeLists.txt.in
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 2.8.2)
+
+project(googletest-download NONE)
+
+include(ExternalProject)
+ExternalProject_Add(googletest
+ GIT_REPOSITORY https://github.com/google/googletest.git
+ GIT_TAG ee3aa831172090fd5442820f215cb04ab6062756
+ SOURCE_DIR "${CMAKE_BINARY_DIR}/gtest/src"
+ BINARY_DIR "${CMAKE_BINARY_DIR}/gtest/build"
+ CONFIGURE_COMMAND ""
+ BUILD_COMMAND ""
+ INSTALL_COMMAND ""
+ TEST_COMMAND ""
+)
diff --git a/externals/zycore/LICENSE b/externals/zycore/LICENSE
new file mode 100644
index 00000000..4d9069ae
--- /dev/null
+++ b/externals/zycore/LICENSE
@@ -0,0 +1,23 @@
+The MIT License (MIT)
+
+Copyright (c) 2018-2020 Florian Bernd
+Copyright (c) 2018-2020 Joel Höner
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/externals/zycore/README.md b/externals/zycore/README.md
new file mode 100644
index 00000000..309a73b6
--- /dev/null
+++ b/externals/zycore/README.md
@@ -0,0 +1,25 @@
+# Zyan Core Library for C
+
+Internal library providing platform independent types, macros and a fallback for environments without LibC.
+
+## Features
+
+- Platform independent types
+ - Integer types (`ZyanU8`, `ZyanI32`, `ZyanUSize`, ...)
+ - `ZyanBool` (+ `ZYAN_FALSE`, `ZYAN_TRUE`)
+ - `ZYAN_NULL`
+- Macros
+ - Compiler/Platform/Architecture detection
+ - Asserts and static asserts
+ - Utils (`ARRAY_LENGTH`, `FALLTHROUGH`, `UNUSED`, ...)
+- Common types
+ - `ZyanBitset`
+ - `ZyanString`/`ZyanStringView`
+- Container types
+ - `ZyanVector`
+ - `ZyanList`
+- LibC abstraction (WiP)
+
+## License
+
+Zycore is licensed under the MIT license.
diff --git a/externals/zycore/cmake/zycore-config.cmake.in b/externals/zycore/cmake/zycore-config.cmake.in
new file mode 100644
index 00000000..400ab53a
--- /dev/null
+++ b/externals/zycore/cmake/zycore-config.cmake.in
@@ -0,0 +1,8 @@
+set(zycore_VERSION @PROJECT_VERSION@)
+
+@PACKAGE_INIT@
+
+set_and_check(zycore_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_INCLUDEDIR@")
+set_and_check(zycore_LIB_DIR "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_LIBDIR@")
+
+check_required_components(zycore)
diff --git a/externals/zycore/examples/String.c b/externals/zycore/examples/String.c
new file mode 100644
index 00000000..10ad84cc
--- /dev/null
+++ b/externals/zycore/examples/String.c
@@ -0,0 +1,192 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * Demonstrates the `String` implementation.
+ */
+
+#include <stdio.h>
+#include <Zycore/Allocator.h>
+#include <Zycore/Defines.h>
+#include <Zycore/LibC.h>
+#include <Zycore/String.h>
+#include <Zycore/Types.h>
+
+/* ============================================================================================== */
+/* Enums and types */
+/* ============================================================================================== */
+
+
+/* ============================================================================================== */
+/* Helper functions */
+/* ============================================================================================== */
+
+/* ============================================================================================== */
+/* Tests */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Basic tests */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Performs some basic test on the given `ZyanString` instance.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus PerformBasicTests(ZyanString* string)
+{
+ ZYAN_ASSERT(string);
+ ZYAN_UNUSED(string);
+
+
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/**
+ * Performs basic tests on a string that dynamically manages memory.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus TestDynamic(void)
+{
+ PerformBasicTests(ZYAN_NULL);
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/**
+ * Performs basic tests on a string that uses a static buffer.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus TestStatic(void)
+{
+ PerformBasicTests(ZYAN_NULL);
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Custom allocator */
+/* ---------------------------------------------------------------------------------------------- */
+
+//static ZyanStatus AllocatorAllocate(ZyanAllocator* allocator, void** p, ZyanUSize element_size,
+// ZyanUSize n)
+//{
+// ZYAN_ASSERT(allocator);
+// ZYAN_ASSERT(p);
+// ZYAN_ASSERT(element_size);
+// ZYAN_ASSERT(n);
+//
+// ZYAN_UNUSED(allocator);
+//
+// *p = ZYAN_MALLOC(element_size * n);
+// if (!*p)
+// {
+// return ZYAN_STATUS_NOT_ENOUGH_MEMORY;
+// }
+//
+// return ZYAN_STATUS_SUCCESS;
+//}
+//
+//static ZyanStatus AllocatorReallocate(ZyanAllocator* allocator, void** p, ZyanUSize element_size,
+// ZyanUSize n)
+//{
+// ZYAN_ASSERT(allocator);
+// ZYAN_ASSERT(p);
+// ZYAN_ASSERT(element_size);
+// ZYAN_ASSERT(n);
+//
+// ZYAN_UNUSED(allocator);
+//
+// void* const x = ZYAN_REALLOC(*p, element_size * n);
+// if (!x)
+// {
+// return ZYAN_STATUS_NOT_ENOUGH_MEMORY;
+// }
+// *p = x;
+//
+// return ZYAN_STATUS_SUCCESS;
+//}
+//
+//static ZyanStatus AllocatorDeallocate(ZyanAllocator* allocator, void* p, ZyanUSize element_size,
+// ZyanUSize n)
+//{
+// ZYAN_ASSERT(allocator);
+// ZYAN_ASSERT(p);
+// ZYAN_ASSERT(element_size);
+// ZYAN_ASSERT(n);
+//
+// ZYAN_UNUSED(allocator);
+// ZYAN_UNUSED(element_size);
+// ZYAN_UNUSED(n);
+//
+// ZYAN_FREE(p);
+//
+// return ZYAN_STATUS_SUCCESS;
+//}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Performs basic tests on a vector that dynamically manages memory using a custom
+ * allocator and modified growth-factor/shrink-threshold.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus TestAllocator(void)
+{
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Entry point */
+/* ============================================================================================== */
+
+int main()
+{
+ if (!ZYAN_SUCCESS(TestDynamic()))
+ {
+ return EXIT_FAILURE;
+ }
+ if (!ZYAN_SUCCESS(TestStatic()))
+ {
+ return EXIT_FAILURE;
+ }
+ if (!ZYAN_SUCCESS(TestAllocator()))
+ {
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+/* ============================================================================================== */
diff --git a/externals/zycore/examples/Vector.c b/externals/zycore/examples/Vector.c
new file mode 100644
index 00000000..090ad330
--- /dev/null
+++ b/externals/zycore/examples/Vector.c
@@ -0,0 +1,395 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * Demonstrates the `ZyanVector` implementation.
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <time.h>
+#include <Zycore/Allocator.h>
+#include <Zycore/Defines.h>
+#include <Zycore/LibC.h>
+#include <Zycore/Types.h>
+#include <Zycore/Vector.h>
+
+/* ============================================================================================== */
+/* Enums and types */
+/* ============================================================================================== */
+
+/**
+ * Defines the `TestStruct` struct that represents a single element in the vector.
+ */
+typedef struct TestStruct_
+{
+ ZyanU32 u32;
+ ZyanU64 u64;
+ float f;
+} TestStruct;
+
+/* ============================================================================================== */
+/* Helper functions */
+/* ============================================================================================== */
+
+/**
+ * Initializes the given `TestStruct` struct.
+ *
+ * @param data A pointer to the `TestStruct` struct.
+ * @param n The number to initialize the struct with.
+ */
+static void InitTestdata(TestStruct* data, ZyanU32 n)
+{
+ ZYAN_ASSERT(data);
+
+ data->u32 = n;
+ data->u64 = n;
+ data->f = (float)n;
+}
+
+/* ============================================================================================== */
+/* Tests */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Basic tests */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Performs some basic test on the given `ZyanVector` instance.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus PerformBasicTests(ZyanVector* vector)
+{
+ ZYAN_ASSERT(vector);
+
+ static TestStruct e_v;
+ static const TestStruct* e_p;
+
+ // Insert `20` elements. The vector automatically manages its size
+ for (ZyanU32 i = 0; i < 20; ++i)
+ {
+ InitTestdata(&e_v, i);
+ ZYAN_CHECK(ZyanVectorPushBack(vector, &e_v));
+ }
+
+ // Remove elements `#05..#09`
+ ZYAN_CHECK(ZyanVectorDeleteRange(vector, 5, 5));
+
+ // Insert a new element at index `#05`
+ InitTestdata(&e_v, 12345678);
+ ZYAN_CHECK(ZyanVectorInsert(vector, 5, &e_v));
+
+ // Change value of element `#15`
+ InitTestdata(&e_v, 87654321);
+ ZYAN_CHECK(ZyanVectorSet(vector, 10, &e_v));
+
+ // Print `u64` of all vector elements
+ ZyanUSize value;
+ ZYAN_CHECK(ZyanVectorGetSize(vector, &value));
+ puts("ELEMENTS");
+ for (ZyanUSize i = 0; i < value; ++i)
+ {
+ ZYAN_CHECK(ZyanVectorGetPointer(vector, i, (const void**)&e_p));
+ printf(" Element #%02" PRIuPTR ": %08" PRIu64 "\n", i, e_p->u64);
+ }
+
+ // Print infos
+ puts("INFO");
+ printf(" Size : %08" PRIuPTR "\n", value);
+ ZYAN_CHECK(ZyanVectorGetCapacity(vector, &value));
+ printf(" Capacity : %08" PRIuPTR "\n\n", value);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/**
+ * A dummy comparison function for the `TestStruct` that uses the `u32` field as key
+ * value.
+ *
+ * @param left A pointer to the first element.
+ * @param right A pointer to the second element.
+ *
+ * @return Returns values in the following range:
+ * `left == right -> result == 0`
+ * `left < right -> result < 0`
+ * `left > right -> result > 0`
+ */
+static ZyanI32 TestDataComparison(const TestStruct* left, const TestStruct* right)
+{
+ ZYAN_ASSERT(left);
+ ZYAN_ASSERT(right);
+
+ if (left->u32 < right->u32)
+ {
+ return -1;
+ }
+ if (left->u32 > right->u32)
+ {
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Tests the binary-search functionality of the given `ZyanVector` instance.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus PerformBinarySearchTest(ZyanVector* vector)
+{
+ ZYAN_ASSERT(vector);
+
+ static TestStruct e_v;
+ static const TestStruct* e_p;
+
+ ZyanUSize value;
+ ZYAN_CHECK(ZyanVectorGetCapacity(vector, &value));
+
+ // Create a sorted test vector
+ for (ZyanUSize i = 0; i < value; ++i)
+ {
+ const ZyanU32 n = rand() % 100;
+ InitTestdata(&e_v, n);
+
+ ZyanUSize found_index;
+ ZYAN_CHECK(ZyanVectorBinarySearch(vector, &e_v, &found_index,
+ (ZyanComparison)&TestDataComparison));
+ ZYAN_CHECK(ZyanVectorInsert(vector, found_index, &e_v));
+ }
+
+ // Print `u32` of all vector elements
+ ZYAN_CHECK(ZyanVectorGetSize(vector, &value));
+ puts("ELEMENTS");
+ for (ZyanUSize i = 0; i < value; ++i)
+ {
+ ZYAN_CHECK(ZyanVectorGetPointer(vector, i, (const void**)&e_p));
+ printf(" Element #%02" PRIuPTR ": %08" PRIu32 "\n", i, e_p->u32);
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/**
+ * Performs basic tests on a vector that dynamically manages memory.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus TestDynamic(void)
+{
+ // Initialize vector with a base capacity of `10` elements
+ ZyanVector vector;
+ ZYAN_CHECK(ZyanVectorInit(&vector, sizeof(TestStruct), 10, ZYAN_NULL));
+
+ ZYAN_CHECK(PerformBasicTests(&vector));
+ ZYAN_CHECK(ZyanVectorClear(&vector));
+ ZYAN_CHECK(ZyanVectorReserve(&vector, 20));
+ ZYAN_CHECK(PerformBinarySearchTest(&vector));
+
+ // Cleanup
+ return ZyanVectorDestroy(&vector);
+}
+
+/**
+ * Performs basic tests on a vector that uses a static buffer.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus TestStatic(void)
+{
+ static TestStruct buffer[20];
+
+ // Initialize vector to use a static buffer with a total capacity of `20` elements.
+ ZyanVector vector;
+ ZYAN_CHECK(ZyanVectorInitCustomBuffer(&vector, sizeof(TestStruct), buffer,
+ ZYAN_ARRAY_LENGTH(buffer), ZYAN_NULL));
+
+ // Compare elements
+ ZyanUSize size;
+ ZYAN_CHECK(ZyanVectorGetSize(&vector, &size));
+ for (ZyanUSize i = 0; i < size; ++i)
+ {
+ static TestStruct* element;
+ ZYAN_CHECK(ZyanVectorGetPointer(&vector, i, (const void**)&element));
+ if (element->u64 != buffer[i].u64)
+ {
+ return ZYAN_STATUS_INVALID_OPERATION;
+ }
+ }
+
+ ZYAN_CHECK(PerformBasicTests(&vector));
+ ZYAN_CHECK(ZyanVectorClear(&vector));
+ ZYAN_CHECK(PerformBinarySearchTest(&vector));
+
+ // Cleanup
+ return ZyanVectorDestroy(&vector);
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Custom allocator */
+/* ---------------------------------------------------------------------------------------------- */
+
+static ZyanStatus AllocatorAllocate(ZyanAllocator* allocator, void** p, ZyanUSize element_size,
+ ZyanUSize n)
+{
+ ZYAN_ASSERT(allocator);
+ ZYAN_ASSERT(p);
+ ZYAN_ASSERT(element_size);
+ ZYAN_ASSERT(n);
+
+ ZYAN_UNUSED(allocator);
+
+ *p = ZYAN_MALLOC(element_size * n);
+ if (!*p)
+ {
+ return ZYAN_STATUS_NOT_ENOUGH_MEMORY;
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus AllocatorReallocate(ZyanAllocator* allocator, void** p, ZyanUSize element_size,
+ ZyanUSize n)
+{
+ ZYAN_ASSERT(allocator);
+ ZYAN_ASSERT(p);
+ ZYAN_ASSERT(element_size);
+ ZYAN_ASSERT(n);
+
+ ZYAN_UNUSED(allocator);
+
+ void* const x = ZYAN_REALLOC(*p, element_size * n);
+ if (!x)
+ {
+ return ZYAN_STATUS_NOT_ENOUGH_MEMORY;
+ }
+ *p = x;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus AllocatorDeallocate(ZyanAllocator* allocator, void* p, ZyanUSize element_size,
+ ZyanUSize n)
+{
+ ZYAN_ASSERT(allocator);
+ ZYAN_ASSERT(p);
+ ZYAN_ASSERT(element_size);
+ ZYAN_ASSERT(n);
+
+ ZYAN_UNUSED(allocator);
+ ZYAN_UNUSED(element_size);
+ ZYAN_UNUSED(n);
+
+ ZYAN_FREE(p);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Performs basic tests on a vector that dynamically manages memory using a custom
+ * allocator and modified growth-factor/shrink-threshold.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus TestAllocator(void)
+{
+ ZyanAllocator allocator;
+ ZYAN_CHECK(ZyanAllocatorInit(&allocator, &AllocatorAllocate, &AllocatorReallocate,
+ &AllocatorDeallocate));
+
+ // Initialize vector with a base capacity of `10` elements. Growth-factor is set to 10 and
+ // dynamic shrinking is disabled
+ ZyanVector vector;
+ ZYAN_CHECK(ZyanVectorInitEx(&vector, sizeof(TestStruct), 5, ZYAN_NULL, &allocator,
+ 10.0f, 0.0f));
+
+ static TestStruct e_v;
+
+ // Insert `10` elements. The vector automatically manages its size
+ for (ZyanU32 i = 0; i < 10; ++i)
+ {
+ InitTestdata(&e_v, i);
+ ZYAN_CHECK(ZyanVectorPushBack(&vector, &e_v));
+ }
+
+ // Check capacity
+ ZyanUSize value;
+ ZYAN_CHECK(ZyanVectorGetCapacity(&vector, &value));
+ if (value != 60) // (5 + 1) * 10.0f
+ {
+ return ZYAN_STATUS_INVALID_OPERATION;
+ }
+
+ // Remove all elements
+ ZYAN_CHECK(ZyanVectorClear(&vector));
+
+ // Print infos
+ puts("INFO");
+ ZYAN_CHECK(ZyanVectorGetSize(&vector, &value));
+ printf(" Size : %08" PRIuPTR "\n", value);
+ ZYAN_CHECK(ZyanVectorGetCapacity(&vector, &value));
+ printf(" Capacity : %08" PRIuPTR "\n\n", value);
+
+ // Cleanup
+ return ZyanVectorDestroy(&vector);
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Entry point */
+/* ============================================================================================== */
+
+int main()
+{
+ time_t t;
+ srand((unsigned)time(&t));
+
+ if (!ZYAN_SUCCESS(TestDynamic()))
+ {
+ return EXIT_FAILURE;
+ }
+ if (!ZYAN_SUCCESS(TestStatic()))
+ {
+ return EXIT_FAILURE;
+ }
+ if (!ZYAN_SUCCESS(TestAllocator()))
+ {
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+/* ============================================================================================== */
diff --git a/externals/zycore/include/Zycore/API/Memory.h b/externals/zycore/include/Zycore/API/Memory.h
new file mode 100644
index 00000000..c5fa8a9b
--- /dev/null
+++ b/externals/zycore/include/Zycore/API/Memory.h
@@ -0,0 +1,134 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef ZYCORE_API_MEMORY_H
+#define ZYCORE_API_MEMORY_H
+
+#include <ZycoreExportConfig.h>
+#include <Zycore/Defines.h>
+#include <Zycore/Status.h>
+#include <Zycore/Types.h>
+
+#if defined(ZYAN_WINDOWS)
+# include <windows.h>
+#elif defined(ZYAN_POSIX)
+# include <sys/mman.h>
+#else
+# error "Unsupported platform detected"
+#endif
+
+/* ============================================================================================== */
+/* Enums and types */
+/* ============================================================================================== */
+
+/**
+ * Defines the `ZyanMemoryPageProtection` enum.
+ */
+typedef enum ZyanMemoryPageProtection_
+{
+#if defined(ZYAN_WINDOWS)
+
+ ZYAN_PAGE_READONLY = PAGE_READONLY,
+ ZYAN_PAGE_READWRITE = PAGE_READWRITE,
+ ZYAN_PAGE_EXECUTE = PAGE_EXECUTE,
+ ZYAN_PAGE_EXECUTE_READ = PAGE_EXECUTE_READ,
+ ZYAN_PAGE_EXECUTE_READWRITE = PAGE_EXECUTE_READWRITE
+
+#elif defined(ZYAN_POSIX)
+
+ ZYAN_PAGE_READONLY = PROT_READ,
+ ZYAN_PAGE_READWRITE = PROT_READ | PROT_WRITE,
+ ZYAN_PAGE_EXECUTE = PROT_EXEC,
+ ZYAN_PAGE_EXECUTE_READ = PROT_EXEC | PROT_READ,
+ ZYAN_PAGE_EXECUTE_READWRITE = PROT_EXEC | PROT_READ | PROT_WRITE
+
+#endif
+} ZyanMemoryPageProtection;
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* General */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Returns the system page size.
+ *
+ * @return The system page size.
+ */
+ZYCORE_EXPORT ZyanU32 ZyanMemoryGetSystemPageSize();
+
+/**
+ * Returns the system allocation granularity.
+ *
+ * The system allocation granularity specifies the minimum amount of bytes which can be allocated
+ * at a specific address by a single call of `ZyanMemoryVirtualAlloc`.
+ *
+ * This value is typically 64KiB on Windows systems and equal to the page size on most POSIX
+ * platforms.
+ *
+ * @return The system allocation granularity.
+ */
+ZYCORE_EXPORT ZyanU32 ZyanMemoryGetSystemAllocationGranularity();
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Memory management */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Changes the memory protection value of one or more pages.
+ *
+ * @param address The start address aligned to a page boundary.
+ * @param size The size.
+ * @param protection The new page protection value.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanMemoryVirtualProtect(void* address, ZyanUSize size,
+ ZyanMemoryPageProtection protection);
+
+/**
+ * Releases one or more memory pages starting at the given address.
+ *
+ * @param address The start address aligned to a page boundary.
+ * @param size The size.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanMemoryVirtualFree(void* address, ZyanUSize size);
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+
+#endif /* ZYCORE_API_MEMORY_H */
diff --git a/externals/zycore/include/Zycore/API/Process.h b/externals/zycore/include/Zycore/API/Process.h
new file mode 100644
index 00000000..0b6a5c6b
--- /dev/null
+++ b/externals/zycore/include/Zycore/API/Process.h
@@ -0,0 +1,67 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef ZYCORE_API_PROCESS_H
+#define ZYCORE_API_PROCESS_H
+
+#include <ZycoreExportConfig.h>
+#include <Zycore/Status.h>
+#include <Zycore/Types.h>
+
+/* ============================================================================================== */
+/* Enums and types */
+/* ============================================================================================== */
+
+
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* General */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * @brief Flushes the process instruction cache.
+ *
+ * @param address The address.
+ * @param size The size.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanProcessFlushInstructionCache(void* address, ZyanUSize size);
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+
+#endif /* ZYCORE_API_PROCESS_H */
diff --git a/externals/zycore/include/Zycore/API/Synchronization.h b/externals/zycore/include/Zycore/API/Synchronization.h
new file mode 100644
index 00000000..8414a44b
--- /dev/null
+++ b/externals/zycore/include/Zycore/API/Synchronization.h
@@ -0,0 +1,133 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef ZYCORE_API_SYNCHRONIZATION_H
+#define ZYCORE_API_SYNCHRONIZATION_H
+
+#ifndef ZYAN_NO_LIBC
+
+#include <ZycoreExportConfig.h>
+#include <Zycore/Defines.h>
+#include <Zycore/Status.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ============================================================================================== */
+/* Enums and types */
+/* ============================================================================================== */
+
+#if defined(ZYAN_POSIX)
+
+#include <pthread.h>
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Critical Section */
+/* ---------------------------------------------------------------------------------------------- */
+
+typedef pthread_mutex_t ZyanCriticalSection;
+
+/* ---------------------------------------------------------------------------------------------- */
+
+#elif defined(ZYAN_WINDOWS)
+
+#include <windows.h>
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Critical Section */
+/* ---------------------------------------------------------------------------------------------- */
+
+typedef CRITICAL_SECTION ZyanCriticalSection;
+
+/* ---------------------------------------------------------------------------------------------- */
+
+#else
+# error "Unsupported platform detected"
+#endif
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Critical Section */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Initializes a critical section.
+ *
+ * @param critical_section A pointer to the `ZyanCriticalSection` struct.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanCriticalSectionInitialize(ZyanCriticalSection* critical_section);
+
+/**
+ * Enters a critical section.
+ *
+ * @param critical_section A pointer to the `ZyanCriticalSection` struct.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanCriticalSectionEnter(ZyanCriticalSection* critical_section);
+
+/**
+ * Tries to enter a critical section.
+ *
+ * @param critical_section A pointer to the `ZyanCriticalSection` struct.
+ *
+ * @return Returns `ZYAN_TRUE` if the critical section was successfully entered or `ZYAN_FALSE`,
+ * if not.
+ */
+ZYCORE_EXPORT ZyanBool ZyanCriticalSectionTryEnter(ZyanCriticalSection* critical_section);
+
+/**
+ * Leaves a critical section.
+ *
+ * @param critical_section A pointer to the `ZyanCriticalSection` struct.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanCriticalSectionLeave(ZyanCriticalSection* critical_section);
+
+/**
+ * Deletes a critical section.
+ *
+ * @param critical_section A pointer to the `ZyanCriticalSection` struct.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanCriticalSectionDelete(ZyanCriticalSection* critical_section);
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZYAN_NO_LIBC */
+
+#endif /* ZYCORE_API_SYNCHRONIZATION_H */
diff --git a/externals/zycore/include/Zycore/API/Terminal.h b/externals/zycore/include/Zycore/API/Terminal.h
new file mode 100644
index 00000000..17dc384b
--- /dev/null
+++ b/externals/zycore/include/Zycore/API/Terminal.h
@@ -0,0 +1,163 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file Provides cross-platform terminal helper functions.
+ * @brief
+ */
+
+#ifndef ZYCORE_API_TERMINAL_H
+#define ZYCORE_API_TERMINAL_H
+
+#include <ZycoreExportConfig.h>
+#include <Zycore/LibC.h>
+#include <Zycore/Status.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ZYAN_NO_LIBC
+
+/* ============================================================================================== */
+/* VT100 CSI SGR sequences */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* General */
+/* ---------------------------------------------------------------------------------------------- */
+
+#define ZYAN_VT100SGR_RESET "\033[0m"
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Foreground colors */
+/* ---------------------------------------------------------------------------------------------- */
+
+#define ZYAN_VT100SGR_FG_DEFAULT "\033[39m"
+
+#define ZYAN_VT100SGR_FG_BLACK "\033[30m"
+#define ZYAN_VT100SGR_FG_RED "\033[31m"
+#define ZYAN_VT100SGR_FG_GREEN "\033[32m"
+#define ZYAN_VT100SGR_FG_YELLOW "\033[33m"
+#define ZYAN_VT100SGR_FG_BLUE "\033[34m"
+#define ZYAN_VT100SGR_FG_MAGENTA "\033[35m"
+#define ZYAN_VT100SGR_FG_CYAN "\033[36m"
+#define ZYAN_VT100SGR_FG_WHITE "\033[37m"
+#define ZYAN_VT100SGR_FG_BRIGHT_BLACK "\033[90m"
+#define ZYAN_VT100SGR_FG_BRIGHT_RED "\033[91m"
+#define ZYAN_VT100SGR_FG_BRIGHT_GREEN "\033[92m"
+#define ZYAN_VT100SGR_FG_BRIGHT_YELLOW "\033[93m"
+#define ZYAN_VT100SGR_FG_BRIGHT_BLUE "\033[94m"
+#define ZYAN_VT100SGR_FG_BRIGHT_MAGENTA "\033[95m"
+#define ZYAN_VT100SGR_FG_BRIGHT_CYAN "\033[96m"
+#define ZYAN_VT100SGR_FG_BRIGHT_WHITE "\033[97m"
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Background color */
+/* ---------------------------------------------------------------------------------------------- */
+
+#define ZYAN_VT100SGR_BG_DEFAULT "\033[49m"
+
+#define ZYAN_VT100SGR_BG_BLACK "\033[40m"
+#define ZYAN_VT100SGR_BG_RED "\033[41m"
+#define ZYAN_VT100SGR_BG_GREEN "\033[42m"
+#define ZYAN_VT100SGR_BG_YELLOW "\033[43m"
+#define ZYAN_VT100SGR_BG_BLUE "\033[44m"
+#define ZYAN_VT100SGR_BG_MAGENTA "\033[45m"
+#define ZYAN_VT100SGR_BG_CYAN "\033[46m"
+#define ZYAN_VT100SGR_BG_WHITE "\033[47m"
+#define ZYAN_VT100SGR_BG_BRIGHT_BLACK "\033[100m"
+#define ZYAN_VT100SGR_BG_BRIGHT_RED "\033[101m"
+#define ZYAN_VT100SGR_BG_BRIGHT_GREEN "\033[102m"
+#define ZYAN_VT100SGR_BG_BRIGHT_YELLOW "\033[103m"
+#define ZYAN_VT100SGR_BG_BRIGHT_BLUE "\033[104m"
+#define ZYAN_VT100SGR_BG_BRIGHT_MAGENTA "\033[105m"
+#define ZYAN_VT100SGR_BG_BRIGHT_CYAN "\033[106m"
+#define ZYAN_VT100SGR_BG_BRIGHT_WHITE "\033[107m"
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Enums and types */
+/* ============================================================================================== */
+
+/**
+ * Declares the `ZyanStandardStream` enum.
+ */
+typedef enum ZyanStandardStream_
+{
+ /**
+ * The default input stream.
+ */
+ ZYAN_STDSTREAM_IN,
+ /**
+ * The default output stream.
+ */
+ ZYAN_STDSTREAM_OUT,
+ /**
+ * The default error stream.
+ */
+ ZYAN_STDSTREAM_ERR
+} ZyanStandardStream;
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/**
+ * Enables VT100 ansi escape codes for the given stream.
+ *
+ * @param stream Either `ZYAN_STDSTREAM_OUT` or `ZYAN_STDSTREAM_ERR`.
+ *
+ * @return A zyan status code.
+ *
+ * This functions returns `ZYAN_STATUS_SUCCESS` on all non-Windows systems without performing any
+ * operations, assuming that VT100 is supported by default.
+ *
+ * On Windows systems, VT100 functionality is only supported on Windows 10 build 1607 (anniversary
+ * update) and later.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanTerminalEnableVT100(ZyanStandardStream stream);
+
+/**
+ * Checks, if the given standard stream reads from or writes to a terminal.
+ *
+ * @param stream The standard stream to check.
+ *
+ * @return `ZYAN_STATUS_TRUE`, if the stream is bound to a terminal, `ZYAN_STATUS_FALSE` if not,
+ * or another zyan status code if an error occured.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanTerminalIsTTY(ZyanStandardStream stream);
+
+/* ============================================================================================== */
+
+#endif // ZYAN_NO_LIBC
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZYCORE_API_TERMINAL_H */
diff --git a/externals/zycore/include/Zycore/API/Thread.h b/externals/zycore/include/Zycore/API/Thread.h
new file mode 100644
index 00000000..b1ec085e
--- /dev/null
+++ b/externals/zycore/include/Zycore/API/Thread.h
@@ -0,0 +1,244 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef ZYCORE_API_THREAD_H
+#define ZYCORE_API_THREAD_H
+
+#ifndef ZYAN_NO_LIBC
+
+#include <ZycoreExportConfig.h>
+#include <Zycore/Defines.h>
+#include <Zycore/Status.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ============================================================================================== */
+/* Enums and types */
+/* ============================================================================================== */
+
+#if defined(ZYAN_POSIX)
+
+#include <pthread.h>
+
+/* ---------------------------------------------------------------------------------------------- */
+/* General */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Defines the `ZyanThread` data-type.
+ */
+typedef pthread_t ZyanThread;
+
+/**
+ * Defines the `ZyanThreadId` data-type.
+ */
+typedef ZyanU64 ZyanThreadId;
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Thread Local Storage (TLS) */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Defines the `ZyanThreadTlsIndex` data-type.
+ */
+typedef pthread_key_t ZyanThreadTlsIndex;
+
+/**
+ * Defines the `ZyanThreadTlsCallback` function prototype.
+ */
+typedef void(*ZyanThreadTlsCallback)(void* data);
+
+/**
+ * Declares a Thread Local Storage (TLS) callback function.
+ *
+ * @param name The callback function name.
+ * @param param_type The callback data parameter type.
+ * @param param_name The callback data parameter name.
+ */
+#define ZYAN_THREAD_DECLARE_TLS_CALLBACK(name, param_type, param_name) \
+ void name(param_type* param_name)
+
+/* ---------------------------------------------------------------------------------------------- */
+
+#elif defined(ZYAN_WINDOWS)
+
+#include <windows.h>
+
+/* ---------------------------------------------------------------------------------------------- */
+/* General */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Defines the `ZyanThread` data-type.
+ */
+typedef HANDLE ZyanThread;
+
+/**
+ * Defines the `ZyanThreadId` data-type.
+ */
+typedef DWORD ZyanThreadId;
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Thread Local Storage (TLS) */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Defines the `ZyanThreadTlsIndex` data-type.
+ */
+typedef DWORD ZyanThreadTlsIndex;
+
+/**
+ * Defines the `ZyanThreadTlsCallback` function prototype.
+ */
+typedef PFLS_CALLBACK_FUNCTION ZyanThreadTlsCallback;
+
+/**
+ * Declares a Thread Local Storage (TLS) callback function.
+ *
+ * @param name The callback function name.
+ * @param param_type The callback data parameter type.
+ * @param param_name The callback data parameter name.
+ */
+#define ZYAN_THREAD_DECLARE_TLS_CALLBACK(name, param_type, param_name) \
+ VOID NTAPI name(param_type* param_name)
+
+/* ---------------------------------------------------------------------------------------------- */
+
+#else
+# error "Unsupported platform detected"
+#endif
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* General */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Returns the handle of the current thread.
+ *
+ * @param thread Receives the handle of the current thread.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanThreadGetCurrentThread(ZyanThread* thread);
+
+/**
+ * Returns the unique id of the current thread.
+ *
+ * @param thread_id Receives the unique id of the current thread.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanThreadGetCurrentThreadId(ZyanThreadId* thread_id);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Thread Local Storage (TLS) */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Allocates a new Thread Local Storage (TLS) slot.
+ *
+ * @param index Receives the TLS slot index.
+ * @param destructor A pointer to a destructor callback which is invoked to finalize the data
+ * in the TLS slot or `ZYAN_NULL`, if not needed.
+ *
+ * The maximum available number of TLS slots is implementation specific and different on each
+ * platform:
+ * - Windows
+ * - A total amount of 128 slots per process are guaranteed
+ * - POSIX
+ * - A total amount of 128 slots per process are guaranteed
+ * - Some systems guarantee larger amounts like e.g. 1024 slots per process
+ *
+ * Note that the invocation rules for the destructor callback are implementation specific and
+ * different on each platform:
+ * - Windows
+ * - The callback is invoked when a thread exits
+ * - The callback is invoked when the process exits
+ * - The callback is invoked when the TLS slot is released
+ * - POSIX
+ * - The callback is invoked when a thread exits and the stored value is not null
+ * - The callback is NOT invoked when the process exits
+ * - The callback is NOT invoked when the TLS slot is released
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanThreadTlsAlloc(ZyanThreadTlsIndex* index,
+ ZyanThreadTlsCallback destructor);
+
+/**
+ * Releases a Thread Local Storage (TLS) slot.
+ *
+ * @param index The TLS slot index.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanThreadTlsFree(ZyanThreadTlsIndex index);
+
+/**
+ * Returns the value inside the given Thread Local Storage (TLS) slot for the
+ * calling thread.
+ *
+ * @param index The TLS slot index.
+ * @param data Receives the value inside the given Thread Local Storage
+ * (TLS) slot for the calling thread.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanThreadTlsGetValue(ZyanThreadTlsIndex index, void** data);
+
+/**
+ * Set the value of the given Thread Local Storage (TLS) slot for the calling thread.
+ *
+ * @param index The TLS slot index.
+ * @param data The value to store inside the given Thread Local Storage (TLS) slot for the
+ * calling thread
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanThreadTlsSetValue(ZyanThreadTlsIndex index, void* data);
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZYAN_NO_LIBC */
+
+#endif /* ZYCORE_API_THREAD_H */
diff --git a/externals/zycore/include/Zycore/Allocator.h b/externals/zycore/include/Zycore/Allocator.h
new file mode 100644
index 00000000..64351719
--- /dev/null
+++ b/externals/zycore/include/Zycore/Allocator.h
@@ -0,0 +1,143 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef ZYCORE_ALLOCATOR_H
+#define ZYCORE_ALLOCATOR_H
+
+#include <ZycoreExportConfig.h>
+#include <Zycore/Status.h>
+#include <Zycore/Types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ============================================================================================== */
+/* Enums and types */
+/* ============================================================================================== */
+
+struct ZyanAllocator_;
+
+/**
+ * Defines the `ZyanAllocatorAllocate` function prototype.
+ *
+ * @param allocator A pointer to the `ZyanAllocator` instance.
+ * @param p Receives a pointer to the first memory block sufficient to hold an
+ * array of `n` elements with a size of `element_size`.
+ * @param element_size The size of a single element.
+ * @param n The number of elements to allocate storage for.
+ *
+ * @return A zyan status code.
+ *
+ * This prototype is used for the `allocate()` and `reallocate()` functions.
+ *
+ * The result of the `reallocate()` function is undefined, if `p` does not point to a memory block
+ * previously obtained by `(re-)allocate()`.
+ */
+typedef ZyanStatus (*ZyanAllocatorAllocate)(struct ZyanAllocator_* allocator, void** p,
+ ZyanUSize element_size, ZyanUSize n);
+
+/**
+ * Defines the `ZyanAllocatorDeallocate` function prototype.
+ *
+ * @param allocator A pointer to the `ZyanAllocator` instance.
+ * @param p The pointer obtained from `(re-)allocate()`.
+ * @param element_size The size of a single element.
+ * @param n The number of elements earlier passed to `(re-)allocate()`.
+ *
+ * @return A zyan status code.
+ */
+typedef ZyanStatus (*ZyanAllocatorDeallocate)(struct ZyanAllocator_* allocator, void* p,
+ ZyanUSize element_size, ZyanUSize n);
+
+/**
+ * Defines the `ZyanAllocator` struct.
+ *
+ * This is the base class for all custom allocator implementations.
+ *
+ * All fields in this struct should be considered as "private". Any changes may lead to unexpected
+ * behavior.
+ */
+typedef struct ZyanAllocator_
+{
+ /**
+ * The allocate function.
+ */
+ ZyanAllocatorAllocate allocate;
+ /**
+ * The reallocate function.
+ */
+ ZyanAllocatorAllocate reallocate;
+ /**
+ * The deallocate function.
+ */
+ ZyanAllocatorDeallocate deallocate;
+} ZyanAllocator;
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/**
+ * Initializes the given `ZyanAllocator` instance.
+ *
+ * @param allocator A pointer to the `ZyanAllocator` instance.
+ * @param allocate The allocate function.
+ * @param reallocate The reallocate function.
+ * @param deallocate The deallocate function.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanAllocatorInit(ZyanAllocator* allocator, ZyanAllocatorAllocate allocate,
+ ZyanAllocatorAllocate reallocate, ZyanAllocatorDeallocate deallocate);
+
+#ifndef ZYAN_NO_LIBC
+
+/**
+ * Returns the default `ZyanAllocator` instance.
+ *
+ * @return A pointer to the default `ZyanAllocator` instance.
+ *
+ * The default allocator uses the default memory manager to allocate memory on the heap.
+ *
+ * You should in no case modify the returned allocator instance to avoid unexpected behavior.
+ */
+ZYCORE_EXPORT ZYAN_REQUIRES_LIBC ZyanAllocator* ZyanAllocatorDefault(void);
+
+#endif // ZYAN_NO_LIBC
+
+/* ============================================================================================== */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZYCORE_ALLOCATOR_H */
diff --git a/externals/zycore/include/Zycore/ArgParse.h b/externals/zycore/include/Zycore/ArgParse.h
new file mode 100644
index 00000000..5d389cb6
--- /dev/null
+++ b/externals/zycore/include/Zycore/ArgParse.h
@@ -0,0 +1,173 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Joel Hoener
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * Implements command-line argument parsing.
+ */
+
+#ifndef ZYCORE_ARGPARSE_H
+#define ZYCORE_ARGPARSE_H
+
+#include <Zycore/Types.h>
+#include <Zycore/Status.h>
+#include <Zycore/Vector.h>
+#include <Zycore/String.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ============================================================================================== */
+/* Structs and other types */
+/* ============================================================================================== */
+
+/**
+ * Definition of a single argument.
+ */
+typedef struct ZyanArgParseDefinition_
+{
+ /**
+ * The argument name, e.g. `--help`.
+ *
+ * Must start with either one or two dashes. Single dash arguments must consist of a single
+ * character, (e.g. `-n`), double-dash arguments can be of arbitrary length.
+ */
+ const char* name;
+ /**
+ * Whether the argument is boolean or expects a value.
+ */
+ ZyanBool boolean;
+ /**
+ * Whether this argument is required (error if missing).
+ */
+ ZyanBool required;
+} ZyanArgParseDefinition;
+
+/**
+ * Configuration for argument parsing.
+ */
+typedef struct ZyanArgParseConfig_
+{
+ /**
+ * `argv` argument passed to `main` by LibC.
+ */
+ const char** argv;
+ /**
+ * `argc` argument passed to `main` by LibC.
+ */
+ ZyanUSize argc;
+ /**
+ * Minimum # of accepted unnamed / anonymous arguments.
+ */
+ ZyanUSize min_unnamed_args;
+ /**
+ * Maximum # of accepted unnamed / anonymous arguments.
+ */
+ ZyanUSize max_unnamed_args;
+ /**
+ * Argument definition array, or `ZYAN_NULL`.
+ *
+ * Expects a pointer to an array of `ZyanArgParseDefinition` instances. The array is
+ * terminated by setting the `.name` field of the last element to `ZYAN_NULL`. If no named
+ * arguments should be parsed, you can also set this to `ZYAN_NULL`.
+ */
+ ZyanArgParseDefinition* args;
+} ZyanArgParseConfig;
+
+/**
+ * Information about a parsed argument.
+ */
+typedef struct ZyanArgParseArg_
+{
+ /**
+ * Corresponding argument definition, or `ZYAN_NULL` for unnamed args.
+ *
+ * This pointer is borrowed from the `cfg` pointer passed to `ZyanArgParse`.
+ */
+ const ZyanArgParseDefinition* def;
+ /**
+ * Whether the argument has a value (is non-boolean).
+ */
+ ZyanBool has_value;
+ /**
+ * If `has_value == true`, then the argument value.
+ *
+ * This is a view into the `argv` string array passed to `ZyanArgParse` via the `cfg` argument.
+ */
+ ZyanStringView value;
+} ZyanArgParseArg;
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+#ifndef ZYAN_NO_LIBC
+
+/**
+ * Parse arguments according to a `ZyanArgParseConfig` definition.
+ *
+ * @param cfg Argument parser config to use.
+ * @param parsed Receives the parsed output. Vector of `ZyanArgParseArg`. Ownership is
+ * transferred to the user. Input is expected to be uninitialized. On error,
+ * the vector remains uninitialized.
+ * @param error_token On error, if it makes sense, receives the argument fragment causing the
+ * error. Optional, may be `ZYAN_NULL`. The pointer borrows into the `cfg`
+ * struct and doesn't have to be freed by the user.
+ *
+ * @return A `ZyanStatus` status determining whether the parsing succeeded.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanArgParse(const ZyanArgParseConfig *cfg, ZyanVector* parsed,
+ const char** error_token);
+
+#endif
+
+/**
+ * Parse arguments according to a `ZyanArgParseConfig` definition.
+ *
+ * This version allows specification of a custom memory allocator and thus supports no-libc.
+ *
+ * @param cfg Argument parser config to use.
+ * @param parsed Receives the parsed output. Vector of `ZyanArgParseArg`. Ownership is
+ * transferred to the user. Input is expected to be uninitialized. On error,
+ * the vector remains uninitialized.
+ * @param error_token On error, if it makes sense, receives the argument fragment causing the
+ * error. Optional, may be `ZYAN_NULL`. The pointer borrows into the `cfg`
+ * struct and doesn't have to be freed by the user.
+ * @param allocator The `ZyanAllocator` to be used for allocating the output vector's data.
+ *
+ * @return A `ZyanStatus` status determining whether the parsing succeeded.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanArgParseEx(const ZyanArgParseConfig *cfg, ZyanVector* parsed,
+ const char** error_token, ZyanAllocator* allocator);
+
+/* ============================================================================================== */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZYCORE_ARGPARSE_H */
diff --git a/externals/zycore/include/Zycore/Bitset.h b/externals/zycore/include/Zycore/Bitset.h
new file mode 100644
index 00000000..8c7eb1f3
--- /dev/null
+++ b/externals/zycore/include/Zycore/Bitset.h
@@ -0,0 +1,484 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * Implements the bitset class.
+ */
+
+#ifndef ZYCORE_BITSET_H
+#define ZYCORE_BITSET_H
+
+#include <ZycoreExportConfig.h>
+#include <Zycore/Allocator.h>
+#include <Zycore/Status.h>
+#include <Zycore/Types.h>
+#include <Zycore/Vector.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ============================================================================================== */
+/* Enums and types */
+/* ============================================================================================== */
+
+/**
+ * Defines the `ZyanVector` struct.
+ *
+ * All fields in this struct should be considered as "private". Any changes may lead to unexpected
+ * behavior.
+ */
+typedef struct ZyanBitset_
+{
+ /**
+ * The bitset size.
+ */
+ ZyanUSize size;
+ /**
+ * The bitset data.
+ */
+ ZyanVector bits;
+} ZyanBitset;
+
+/**
+ * Defines the `ZyanBitsetByteOperation` function prototype.
+ *
+ * @param v1 A pointer to the first byte. This value receives the result after performing the
+ * desired operation.
+ * @param v2 A pointer to the second byte.
+ *
+ * @return A zyan status code.
+ *
+ * This function is used to perform byte-wise operations on two `ZyanBitset` instances.
+ */
+typedef ZyanStatus (*ZyanBitsetByteOperation)(ZyanU8* v1, const ZyanU8* v2);
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Constructor and destructor */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYAN_NO_LIBC
+
+/**
+ * Initializes the given `ZyanBitset` instance.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ * @param count The initial amount of bits.
+ *
+ * @return A zyan status code.
+ *
+ * The space for the bitset is dynamically allocated by the default allocator using the default
+ * growth factor of `2.0f` and the default shrink threshold of `0.5f`.
+ */
+ZYCORE_EXPORT ZYAN_REQUIRES_LIBC ZyanStatus ZyanBitsetInit(ZyanBitset* bitset, ZyanUSize count);
+
+#endif // ZYAN_NO_LIBC
+
+/**
+ * Initializes the given `ZyanBitset` instance and sets a custom `allocator` and memory
+ * allocation/deallocation parameters.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ * @param count The initial amount of bits.
+ * @param allocator A pointer to a `ZyanAllocator` instance.
+ * @param growth_factor The growth factor (from `1.0f` to `x.xf`).
+ * @param shrink_threshold The shrink threshold (from `0.0f` to `1.0f`).
+ *
+ * @return A zyan status code.
+ *
+ * A growth factor of `1.0f` disables overallocation and a shrink threshold of `0.0f` disables
+ * dynamic shrinking.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetInitEx(ZyanBitset* bitset, ZyanUSize count,
+ ZyanAllocator* allocator, float growth_factor, float shrink_threshold);
+
+/**
+ * Initializes the given `ZyanBitset` instance and configures it to use a custom user
+ * defined buffer with a fixed size.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ * @param count The initial amount of bits.
+ * @param buffer A pointer to the buffer that is used as storage for the bits.
+ * @param capacity The maximum capacity (number of bytes) of the buffer.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetInitBuffer(ZyanBitset* bitset, ZyanUSize count, void* buffer,
+ ZyanUSize capacity);
+
+/**
+ * Destroys the given `ZyanBitset` instance.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetDestroy(ZyanBitset* bitset);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Logical operations */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Performs a byte-wise `operation` for every byte in the given `ZyanBitset` instances.
+ *
+ * @param destination A pointer to the `ZyanBitset` instance that is used as the first input and
+ * as the destination.
+ * @param source A pointer to the `ZyanBitset` instance that is used as the second input.
+ * @param operation A pointer to the function that performs the desired operation.
+ *
+ * @return A zyan status code.
+ *
+ * The `operation` callback is invoked once for every byte in the smallest of the `ZyanBitset`
+ * instances.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetPerformByteOperation(ZyanBitset* destination,
+ const ZyanBitset* source, ZyanBitsetByteOperation operation);
+
+/**
+ * Performs a logical `AND` operation on the given `ZyanBitset` instances.
+ *
+ * @param destination A pointer to the `ZyanBitset` instance that is used as the first input and
+ * as the destination.
+ * @param source A pointer to the `ZyanBitset` instance that is used as the second input.
+ *
+ * @return A zyan status code.
+ *
+ * If the destination bitmask contains more bits than the source one, the state of the remaining
+ * bits will be undefined.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetAND(ZyanBitset* destination, const ZyanBitset* source);
+
+/**
+ * Performs a logical `OR` operation on the given `ZyanBitset` instances.
+ *
+ * @param destination A pointer to the `ZyanBitset` instance that is used as the first input and
+ * as the destination.
+ * @param source A pointer to the `ZyanBitset` instance that is used as the second input.
+ *
+ * @return A zyan status code.
+ *
+ * If the destination bitmask contains more bits than the source one, the state of the remaining
+ * bits will be undefined.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetOR (ZyanBitset* destination, const ZyanBitset* source);
+
+/**
+ * Performs a logical `XOR` operation on the given `ZyanBitset` instances.
+ *
+ * @param destination A pointer to the `ZyanBitset` instance that is used as the first input and
+ * as the destination.
+ * @param source A pointer to the `ZyanBitset` instance that is used as the second input.
+ *
+ * @return A zyan status code.
+ *
+ * If the destination bitmask contains more bits than the source one, the state of the remaining
+ * bits will be undefined.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetXOR(ZyanBitset* destination, const ZyanBitset* source);
+
+/**
+ * Flips all bits of the given `ZyanBitset` instance.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetFlip(ZyanBitset* bitset);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Bit access */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Sets the bit at `index` of the given `ZyanBitset` instance to `1`.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ * @param index The bit index.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetSet(ZyanBitset* bitset, ZyanUSize index);
+
+/**
+ * Sets the bit at `index` of the given `ZyanBitset` instance to `0`.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ * @param index The bit index.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetReset(ZyanBitset* bitset, ZyanUSize index);
+
+/**
+ * Sets the bit at `index` of the given `ZyanBitset` instance to the specified `value`.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ * @param index The bit index.
+ * @param value The new value.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetAssign(ZyanBitset* bitset, ZyanUSize index, ZyanBool value);
+
+/**
+ * Toggles the bit at `index` of the given `ZyanBitset` instance.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ * @param index The bit index.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetToggle(ZyanBitset* bitset, ZyanUSize index);
+
+/**
+ * Returns the value of the bit at `index`.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ * @param index The bit index.
+ *
+ * @return `ZYAN_STATUS_TRUE`, if the bit is set or `ZYAN_STATUS_FALSE`, if not, Another zyan
+ * status code, if an error occured.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetTest(ZyanBitset* bitset, ZyanUSize index);
+
+/**
+ * Returns the value of the most significant bit.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ *
+ * @return `ZYAN_STATUS_TRUE`, if the bit is set or `ZYAN_STATUS_FALSE`, if not. Another zyan
+ * status code, if an error occured.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetTestMSB(ZyanBitset* bitset);
+
+/**
+ * Returns the value of the least significant bit.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ *
+ * @return `ZYAN_STATUS_TRUE`, if the bit is set or `ZYAN_STATUS_FALSE`, if not. Another zyan
+ * status code, if an error occured.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetTestLSB(ZyanBitset* bitset);
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Sets all bits of the given `ZyanBitset` instance to `1`.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetSetAll(ZyanBitset* bitset);
+
+/**
+ * Sets all bits of the given `ZyanBitset` instance to `0`.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetResetAll(ZyanBitset* bitset);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Size management */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Adds a new bit at the end of the bitset.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ * @param value The value of the new bit.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetPush(ZyanBitset* bitset, ZyanBool value);
+
+/**
+ * Removes the last bit of the bitset.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetPop(ZyanBitset* bitset);
+
+/**
+ * Deletes all bits of the given `ZyanBitset` instance.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetClear(ZyanBitset* bitset);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Memory management */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Changes the capacity of the given `ZyanBitset` instance.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ * @param count The new capacity (number of bits).
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetReserve(ZyanBitset* bitset, ZyanUSize count);
+
+/**
+ * Shrinks the capacity of the given bitset to match it's size.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetShrinkToFit(ZyanBitset* bitset);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Information */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Returns the current size of the bitset in bits.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ * @param size Receives the size of the bitset in bits.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetGetSize(const ZyanBitset* bitset, ZyanUSize* size);
+
+/**
+ * Returns the current capacity of the bitset in bits.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ * @param capacity Receives the size of the bitset in bits.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetGetCapacity(const ZyanBitset* bitset, ZyanUSize* capacity);
+
+/**
+ * Returns the current size of the bitset in bytes.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ * @param size Receives the size of the bitset in bytes.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetGetSizeBytes(const ZyanBitset* bitset, ZyanUSize* size);
+
+/**
+ * Returns the current capacity of the bitset in bytes.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ * @param capacity Receives the size of the bitset in bytes.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetGetCapacityBytes(const ZyanBitset* bitset, ZyanUSize* capacity);
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Returns the amount of bits set in the given bitset.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ * @param count Receives the amount of bits set in the given bitset.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetCount(const ZyanBitset* bitset, ZyanUSize* count);
+
+/**
+ * Checks, if all bits of the given bitset are set.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ *
+ * @return `ZYAN_STATUS_TRUE`, if all bits are set, `ZYAN_STATUS_FALSE`, if not. Another zyan
+ * status code, if an error occured.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetAll(const ZyanBitset* bitset);
+
+/**
+ * Checks, if at least one bit of the given bitset is set.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ *
+ * @return `ZYAN_STATUS_TRUE`, if at least one bit is set, `ZYAN_STATUS_FALSE`, if not. Another
+ * zyan status code, if an error occured.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetAny(const ZyanBitset* bitset);
+
+/**
+ * Checks, if none bits of the given bitset are set.
+ *
+ * @param bitset A pointer to the `ZyanBitset` instance.
+ *
+ * @return `ZYAN_STATUS_TRUE`, if none bits are set, `ZYAN_STATUS_FALSE`, if not. Another zyan
+ * status code, if an error occured.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanBitsetNone(const ZyanBitset* bitset);
+
+///* ---------------------------------------------------------------------------------------------- */
+//
+///**
+// * Returns a 32-bit unsigned integer representation of the data.
+// *
+// * @param bitset A pointer to the `ZyanBitset` instance.
+// * @param value Receives the 32-bit unsigned integer representation of the data.
+// *
+// * @return A zyan status code.
+// */
+//ZYCORE_EXPORT ZyanStatus ZyanBitsetToU32(const ZyanBitset* bitset, ZyanU32* value);
+//
+///**
+// * Returns a 64-bit unsigned integer representation of the data.
+// *
+// * @param bitset A pointer to the `ZyanBitset` instance.
+// * @param value Receives the 64-bit unsigned integer representation of the data.
+// *
+// * @return A zyan status code.
+// */
+//ZYCORE_EXPORT ZyanStatus ZyanBitsetToU64(const ZyanBitset* bitset, ZyanU64* value);
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZYCORE_BITSET_H */
diff --git a/externals/zycore/include/Zycore/Comparison.h b/externals/zycore/include/Zycore/Comparison.h
new file mode 100644
index 00000000..6d8b518b
--- /dev/null
+++ b/externals/zycore/include/Zycore/Comparison.h
@@ -0,0 +1,316 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * Defines prototypes of general-purpose comparison functions.
+ */
+
+#ifndef ZYCORE_COMPARISON_H
+#define ZYCORE_COMPARISON_H
+
+#include <Zycore/Defines.h>
+#include <Zycore/Types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ============================================================================================== */
+/* Enums and types */
+/* ============================================================================================== */
+
+/**
+ * Defines the `ZyanEqualityComparison` function prototype.
+ *
+ * @param left A pointer to the first element.
+ * @param right A pointer to the second element.
+ *
+ * @return This function should return `ZYAN_TRUE` if the `left` element equals the `right` one
+ * or `ZYAN_FALSE`, if not.
+ */
+typedef ZyanBool (*ZyanEqualityComparison)(const void* left, const void* right);
+
+/**
+ * Defines the `ZyanComparison` function prototype.
+ *
+ * @param left A pointer to the first element.
+ * @param right A pointer to the second element.
+ *
+ * @return This function should return values in the following range:
+ * `left == right -> result == 0`
+ * `left < right -> result < 0`
+ * `left > right -> result > 0`
+ */
+typedef ZyanI32 (*ZyanComparison)(const void* left, const void* right);
+
+/* ============================================================================================== */
+/* Macros */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Equality comparison functions */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Declares a generic equality comparison function for an integral data-type.
+ *
+ * @param name The name of the function.
+ * @param type The name of the integral data-type.
+ */
+#define ZYAN_DECLARE_EQUALITY_COMPARISON(name, type) \
+ ZyanBool name(const type* left, const type* right) \
+ { \
+ ZYAN_ASSERT(left); \
+ ZYAN_ASSERT(right); \
+ \
+ return (*left == *right) ? ZYAN_TRUE : ZYAN_FALSE; \
+ }
+
+/**
+ * Declares a generic equality comparison function that compares a single integral
+ * data-type field of a struct.
+ *
+ * @param name The name of the function.
+ * @param type The name of the integral data-type.
+ * @param field_name The name of the struct field.
+ */
+#define ZYAN_DECLARE_EQUALITY_COMPARISON_FOR_FIELD(name, type, field_name) \
+ ZyanBool name(const type* left, const type* right) \
+ { \
+ ZYAN_ASSERT(left); \
+ ZYAN_ASSERT(right); \
+ \
+ return (left->field_name == right->field_name) ? ZYAN_TRUE : ZYAN_FALSE; \
+ }
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Comparison functions */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Declares a generic comparison function for an integral data-type.
+ *
+ * @param name The name of the function.
+ * @param type The name of the integral data-type.
+ */
+#define ZYAN_DECLARE_COMPARISON(name, type) \
+ ZyanI32 name(const type* left, const type* right) \
+ { \
+ ZYAN_ASSERT(left); \
+ ZYAN_ASSERT(right); \
+ \
+ if (*left < *right) \
+ { \
+ return -1; \
+ } \
+ if (*left > *right) \
+ { \
+ return 1; \
+ } \
+ return 0; \
+ }
+
+/**
+ * Declares a generic comparison function that compares a single integral data-type field
+ * of a struct.
+ *
+ * @param name The name of the function.
+ * @param type The name of the integral data-type.
+ * @param field_name The name of the struct field.
+ */
+#define ZYAN_DECLARE_COMPARISON_FOR_FIELD(name, type, field_name) \
+ ZyanI32 name(const type* left, const type* right) \
+ { \
+ ZYAN_ASSERT(left); \
+ ZYAN_ASSERT(right); \
+ \
+ if (left->field_name < right->field_name) \
+ { \
+ return -1; \
+ } \
+ if (left->field_name > right->field_name) \
+ { \
+ return 1; \
+ } \
+ return 0; \
+ }
+
+ /* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Default equality comparison functions */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Defines a default equality comparison function for pointer values.
+ *
+ * @param left A pointer to the first value.
+ * @param right A pointer to the second value.
+ *
+ * @return Returns `ZYAN_TRUE` if the `left` value equals the `right` one or `ZYAN_FALSE`, if
+ * not.
+ */
+ZYAN_INLINE ZYAN_DECLARE_EQUALITY_COMPARISON(ZyanEqualsPointer, void* const)
+
+/**
+ * Defines a default equality comparison function for `ZyanBool` values.
+ *
+ * @param left A pointer to the first value.
+ * @param right A pointer to the second value.
+ *
+ * @return Returns `ZYAN_TRUE` if the `left` value equals the `right` one or `ZYAN_FALSE`, if
+ * not.
+ */
+ZYAN_INLINE ZYAN_DECLARE_EQUALITY_COMPARISON(ZyanEqualsBool, ZyanBool)
+
+/**
+ * Defines a default equality comparison function for 8-bit numeric values.
+ *
+ * @param left A pointer to the first value.
+ * @param right A pointer to the second value.
+ *
+ * @return Returns `ZYAN_TRUE` if the `left` value equals the `right` one or `ZYAN_FALSE`, if
+ * not.
+ */
+ZYAN_INLINE ZYAN_DECLARE_EQUALITY_COMPARISON(ZyanEqualsNumeric8, ZyanU8)
+
+/**
+ * Defines a default equality comparison function for 16-bit numeric values.
+ *
+ * @param left A pointer to the first value.
+ * @param right A pointer to the second value.
+ *
+ * @return Returns `ZYAN_TRUE` if the `left` value equals the `right` one or `ZYAN_FALSE`, if
+ * not.
+ */
+ZYAN_INLINE ZYAN_DECLARE_EQUALITY_COMPARISON(ZyanEqualsNumeric16, ZyanU16)
+
+/**
+ * Defines a default equality comparison function for 32-bit numeric values.
+ *
+ * @param left A pointer to the first value.
+ * @param right A pointer to the second value.
+ *
+ * @return Returns `ZYAN_TRUE` if the `left` value equals the `right` one or `ZYAN_FALSE`, if
+ * not.
+ */
+ZYAN_INLINE ZYAN_DECLARE_EQUALITY_COMPARISON(ZyanEqualsNumeric32, ZyanU32)
+
+/**
+ * Defines a default equality comparison function for 64-bit numeric values.
+ *
+ * @param left A pointer to the first value.
+ * @param right A pointer to the second value.
+ *
+ * @return Returns `ZYAN_TRUE` if the `left` value equals the `right` one or `ZYAN_FALSE`, if
+ * not.
+ */
+ZYAN_INLINE ZYAN_DECLARE_EQUALITY_COMPARISON(ZyanEqualsNumeric64, ZyanU64)
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Default comparison functions */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Defines a default comparison function for pointer values.
+ *
+ * @param left A pointer to the first value.
+ * @param right A pointer to the second value.
+ *
+ * @return Returns `0` if the `left` value equals the `right` one, `-1` if the `left` value is
+ * less than the `right` one, or `1` if the `left` value is greater than the `right` one.
+ */
+ZYAN_INLINE ZYAN_DECLARE_COMPARISON(ZyanComparePointer, void* const)
+
+/**
+ * Defines a default comparison function for `ZyanBool` values.
+ *
+ * @param left A pointer to the first value.
+ * @param right A pointer to the second value.
+ *
+ * @return Returns `0` if the `left` value equals the `right` one, `-1` if the `left` value is
+ * less than the `right` one, or `1` if the `left` value is greater than the `right` one.
+ */
+ZYAN_INLINE ZYAN_DECLARE_COMPARISON(ZyanCompareBool, ZyanBool)
+
+/**
+ * Defines a default comparison function for 8-bit numeric values.
+ *
+ * @param left A pointer to the first value.
+ * @param right A pointer to the second value.
+ *
+ * @return Returns `0` if the `left` value equals the `right` one, `-1` if the `left` value is
+ * less than the `right` one, or `1` if the `left` value is greater than the `right` one.
+ */
+ZYAN_INLINE ZYAN_DECLARE_COMPARISON(ZyanCompareNumeric8, ZyanU8)
+
+/**
+ * Defines a default comparison function for 16-bit numeric values.
+ *
+ * @param left A pointer to the first value.
+ * @param right A pointer to the second value.
+ *
+ * @return Returns `0` if the `left` value equals the `right` one, `-1` if the `left` value is
+ * less than the `right` one, or `1` if the `left` value is greater than the `right` one.
+ */
+ZYAN_INLINE ZYAN_DECLARE_COMPARISON(ZyanCompareNumeric16, ZyanU16)
+
+/**
+ * Defines a default comparison function for 32-bit numeric values.
+ *
+ * @param left A pointer to the first value.
+ * @param right A pointer to the second value.
+ *
+ * @return Returns `0` if the `left` value equals the `right` one, `-1` if the `left` value is
+ * less than the `right` one, or `1` if the `left` value is greater than the `right` one.
+ */
+ZYAN_INLINE ZYAN_DECLARE_COMPARISON(ZyanCompareNumeric32, ZyanU32)
+
+/**
+ * Defines a default comparison function for 64-bit numeric values.
+ *
+ * @param left A pointer to the first value.
+ * @param right A pointer to the second value.
+ *
+ * @return Returns `0` if the `left` value equals the `right` one, `-1` if the `left` value is
+ * less than the `right` one, or `1` if the `left` value is greater than the `right` one.
+ */
+ZYAN_INLINE ZYAN_DECLARE_COMPARISON(ZyanCompareNumeric64, ZyanU64)
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZYCORE_COMPARISON_H */
diff --git a/externals/zycore/include/Zycore/Defines.h b/externals/zycore/include/Zycore/Defines.h
new file mode 100644
index 00000000..65afbaa3
--- /dev/null
+++ b/externals/zycore/include/Zycore/Defines.h
@@ -0,0 +1,443 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd, Joel Hoener
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * General helper and platform detection macros.
+ */
+
+#ifndef ZYCORE_DEFINES_H
+#define ZYCORE_DEFINES_H
+
+/* ============================================================================================== */
+/* Meta macros */
+/* ============================================================================================== */
+
+/**
+ * Concatenates two values using the stringify operator (`##`).
+ *
+ * @param x The first value.
+ * @param y The second value.
+ *
+ * @return The combined string of the given values.
+ */
+#define ZYAN_MACRO_CONCAT(x, y) x ## y
+
+/**
+ * Concatenates two values using the stringify operator (`##`) and expands the value to
+ * be used in another macro.
+ *
+ * @param x The first value.
+ * @param y The second value.
+ *
+ * @return The combined string of the given values.
+ */
+#define ZYAN_MACRO_CONCAT_EXPAND(x, y) ZYAN_MACRO_CONCAT(x, y)
+
+/* ============================================================================================== */
+/* Compiler detection */
+/* ============================================================================================== */
+
+#if defined(__clang__)
+# define ZYAN_CLANG
+# define ZYAN_GNUC
+#elif defined(__ICC) || defined(__INTEL_COMPILER)
+# define ZYAN_ICC
+#elif defined(__GNUC__) || defined(__GNUG__)
+# define ZYAN_GCC
+# define ZYAN_GNUC
+#elif defined(_MSC_VER)
+# define ZYAN_MSVC
+#elif defined(__BORLANDC__)
+# define ZYAN_BORLAND
+#else
+# define ZYAN_UNKNOWN_COMPILER
+#endif
+
+/* ============================================================================================== */
+/* Platform detection */
+/* ============================================================================================== */
+
+#if defined(_WIN32)
+# define ZYAN_WINDOWS
+#elif defined(__EMSCRIPTEN__)
+# define ZYAN_EMSCRIPTEN
+#elif defined(__APPLE__)
+# define ZYAN_APPLE
+# define ZYAN_POSIX
+#elif defined(__linux)
+# define ZYAN_LINUX
+# define ZYAN_POSIX
+#elif defined(__FreeBSD__)
+# define ZYAN_FREEBSD
+# define ZYAN_POSIX
+#elif defined(sun) || defined(__sun)
+# define ZYAN_SOLARIS
+# define ZYAN_POSIX
+#elif defined(__unix)
+# define ZYAN_UNIX
+# define ZYAN_POSIX
+#elif defined(__posix)
+# define ZYAN_POSIX
+#else
+# define ZYAN_UNKNOWN_PLATFORM
+#endif
+
+/* ============================================================================================== */
+/* Kernel mode detection */
+/* ============================================================================================== */
+
+#if (defined(ZYAN_WINDOWS) && defined(_KERNEL_MODE)) || \
+ (defined(ZYAN_APPLE) && defined(KERNEL)) || \
+ (defined(ZYAN_LINUX) && defined(__KERNEL__)) || \
+ (defined(__FreeBSD_kernel__))
+# define ZYAN_KERNEL
+#else
+# define ZYAN_USER
+#endif
+
+/* ============================================================================================== */
+/* Architecture detection */
+/* ============================================================================================== */
+
+#if defined(_M_AMD64) || defined(__x86_64__)
+# define ZYAN_X64
+#elif defined(_M_IX86) || defined(__i386__)
+# define ZYAN_X86
+#elif defined(_M_ARM64) || defined(__aarch64__)
+# define ZYAN_AARCH64
+#elif defined(_M_ARM) || defined(_M_ARMT) || defined(__arm__) || defined(__thumb__)
+# define ZYAN_ARM
+#elif defined(__EMSCRIPTEN__)
+ // Nothing to do, `ZYAN_EMSCRIPTEN` is both platform and arch macro for this one.
+#else
+# error "Unsupported architecture detected"
+#endif
+
+/* ============================================================================================== */
+/* Debug/Release detection */
+/* ============================================================================================== */
+
+#if defined(ZYAN_MSVC) || defined(ZYAN_BORLAND)
+# ifdef _DEBUG
+# define ZYAN_DEBUG
+# else
+# define ZYAN_RELEASE
+# endif
+#elif defined(ZYAN_GNUC) || defined(ZYAN_ICC)
+# ifdef NDEBUG
+# define ZYAN_RELEASE
+# else
+# define ZYAN_DEBUG
+# endif
+#else
+# define ZYAN_RELEASE
+#endif
+
+/* ============================================================================================== */
+/* Misc compatibility macros */
+/* ============================================================================================== */
+
+#if defined(ZYAN_CLANG)
+# define ZYAN_NO_SANITIZE(what) __attribute__((no_sanitize(what)))
+#else
+# define ZYAN_NO_SANITIZE(what)
+#endif
+
+#if defined(ZYAN_MSVC) || defined(ZYAN_BORLAND)
+# define ZYAN_INLINE __inline
+#else
+# define ZYAN_INLINE static inline
+#endif
+
+/* ============================================================================================== */
+/* Debugging and optimization macros */
+/* ============================================================================================== */
+
+/**
+ * Runtime debug assertion.
+ */
+#if defined(ZYAN_NO_LIBC)
+# define ZYAN_ASSERT(condition) (void)(condition)
+#elif defined(ZYAN_WINDOWS) && defined(ZYAN_KERNEL)
+# include <wdm.h>
+# define ZYAN_ASSERT(condition) NT_ASSERT(condition)
+#else
+# include <assert.h>
+# define ZYAN_ASSERT(condition) assert(condition)
+#endif
+
+/**
+ * Compiler-time assertion.
+ */
+#if __STDC_VERSION__ >= 201112L && !defined(__cplusplus)
+# define ZYAN_STATIC_ASSERT(x) _Static_assert(x, #x)
+#elif (defined(__cplusplus) && __cplusplus >= 201103L) || \
+ (defined(__cplusplus) && defined (_MSC_VER) && (_MSC_VER >= 1600)) || \
+ (defined (_MSC_VER) && (_MSC_VER >= 1800))
+# define ZYAN_STATIC_ASSERT(x) static_assert(x, #x)
+#else
+# define ZYAN_STATIC_ASSERT(x) \
+ typedef int ZYAN_MACRO_CONCAT_EXPAND(ZYAN_SASSERT_, __COUNTER__) [(x) ? 1 : -1]
+#endif
+
+/**
+ * Marks the current code path as unreachable.
+ */
+#if defined(ZYAN_RELEASE)
+# if defined(ZYAN_CLANG) // GCC eagerly evals && RHS, we have to use nested ifs.
+# if __has_builtin(__builtin_unreachable)
+# define ZYAN_UNREACHABLE __builtin_unreachable()
+# else
+# define ZYAN_UNREACHABLE for(;;)
+# endif
+# elif defined(ZYAN_GCC) && ((__GNUC__ == 4 && __GNUC_MINOR__ > 4) || __GNUC__ > 4)
+# define ZYAN_UNREACHABLE __builtin_unreachable()
+# elif defined(ZYAN_ICC)
+# ifdef ZYAN_WINDOWS
+# include <stdlib.h> // "missing return statement" workaround
+# define ZYAN_UNREACHABLE __assume(0); (void)abort()
+# else
+# define ZYAN_UNREACHABLE __builtin_unreachable()
+# endif
+# elif defined(ZYAN_MSVC)
+# define ZYAN_UNREACHABLE __assume(0)
+# else
+# define ZYAN_UNREACHABLE for(;;)
+# endif
+#elif defined(ZYAN_NO_LIBC)
+# define ZYAN_UNREACHABLE for(;;)
+#elif defined(ZYAN_WINDOWS) && defined(ZYAN_KERNEL)
+# define ZYAN_UNREACHABLE { __fastfail(0); for(;;){} }
+#else
+# include <stdlib.h>
+# define ZYAN_UNREACHABLE { assert(0); abort(); }
+#endif
+
+/* ============================================================================================== */
+/* Utils */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* General purpose */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Marks the specified parameter as unused.
+ *
+ * @param x The name of the unused parameter.
+ */
+#define ZYAN_UNUSED(x) (void)(x)
+
+/**
+ * Intentional fallthrough.
+ */
+#if defined(ZYAN_GCC) && __GNUC__ >= 7
+# define ZYAN_FALLTHROUGH __attribute__((fallthrough))
+#else
+# define ZYAN_FALLTHROUGH
+#endif
+
+/**
+ * Declares a bitfield.
+ *
+ * @param x The size (in bits) of the bitfield.
+ */
+#define ZYAN_BITFIELD(x) : x
+
+/**
+ * Marks functions that require libc (cannot be used with `ZYAN_NO_LIBC`).
+ */
+#define ZYAN_REQUIRES_LIBC
+
+/**
+ * Decorator for `printf`-style functions.
+ *
+ * @param format_index The 1-based index of the format string parameter.
+ * @param first_to_check The 1-based index of the format arguments parameter.
+ */
+#if defined(__RESHARPER__)
+# define ZYAN_PRINTF_ATTR(format_index, first_to_check) \
+ [[gnu::format(printf, format_index, first_to_check)]]
+#elif defined(ZYAN_GCC)
+# define ZYAN_PRINTF_ATTR(format_index, first_to_check) \
+ __attribute__((format(printf, format_index, first_to_check)))
+#else
+# define ZYAN_PRINTF_ATTR(format_index, first_to_check)
+#endif
+
+/**
+ * Decorator for `wprintf`-style functions.
+ *
+ * @param format_index The 1-based index of the format string parameter.
+ * @param first_to_check The 1-based index of the format arguments parameter.
+ */
+#if defined(__RESHARPER__)
+# define ZYAN_WPRINTF_ATTR(format_index, first_to_check) \
+ [[rscpp::format(wprintf, format_index, first_to_check)]]
+#else
+# define ZYAN_WPRINTF_ATTR(format_index, first_to_check)
+#endif
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Arrays */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Returns the length (number of elements) of an array.
+ *
+ * @param a The name of the array.
+ *
+ * @return The number of elements of the given array.
+ */
+#define ZYAN_ARRAY_LENGTH(a) (sizeof(a) / sizeof((a)[0]))
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Arithmetic */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Returns the smaller value of `a` or `b`.
+ *
+ * @param a The first value.
+ * @param b The second value.
+ *
+ * @return The smaller value of `a` or `b`.
+ */
+#define ZYAN_MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+/**
+ * Returns the bigger value of `a` or `b`.
+ *
+ * @param a The first value.
+ * @param b The second value.
+ *
+ * @return The bigger value of `a` or `b`.
+ */
+#define ZYAN_MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+/**
+ * Returns the absolute value of `a`.
+ *
+ * @param a The value.
+ *
+ * @return The absolute value of `a`.
+ */
+#define ZYAN_ABS(a) (((a) < 0) ? -(a) : (a))
+
+/**
+ * Checks, if the given value is a power of 2.
+ *
+ * @param x The value.
+ *
+ * @return `ZYAN_TRUE`, if the given value is a power of 2 or `ZYAN_FALSE`, if not.
+ *
+ * Note that this macro always returns `ZYAN_TRUE` for `x == 0`.
+ */
+#define ZYAN_IS_POWER_OF_2(x) (((x) & ((x) - 1)) == 0)
+
+/**
+ * Checks, if the given value is properly aligned.
+ *
+ * Note that this macro only works for powers of 2.
+ */
+#define ZYAN_IS_ALIGNED_TO(x, align) (((x) & ((align) - 1)) == 0)
+
+/**
+ * Aligns the value to the nearest given alignment boundary (by rounding it up).
+ *
+ * @param x The value.
+ * @param align The desired alignment.
+ *
+ * @return The aligned value.
+ *
+ * Note that this macro only works for powers of 2.
+ */
+#define ZYAN_ALIGN_UP(x, align) (((x) + (align) - 1) & ~((align) - 1))
+
+/**
+ * Aligns the value to the nearest given alignment boundary (by rounding it down).
+ *
+ * @param x The value.
+ * @param align The desired alignment.
+ *
+ * @return The aligned value.
+ *
+ * Note that this macro only works for powers of 2.
+ */
+#define ZYAN_ALIGN_DOWN(x, align) (((x) - 1) & ~((align) - 1))
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Bit operations */
+/* ---------------------------------------------------------------------------------------------- */
+
+/*
+ * Checks, if the bit at index `b` is required to present the ordinal value `n`.
+ *
+ * @param n The ordinal value.
+ * @param b The bit index.
+ *
+ * @return `ZYAN_TRUE`, if the bit at index `b` is required to present the ordinal value `n` or
+ * `ZYAN_FALSE`, if not.
+ *
+ * Note that this macro always returns `ZYAN_FALSE` for `n == 0`.
+ */
+#define ZYAN_NEEDS_BIT(n, b) (((unsigned long)(n) >> (b)) > 0)
+
+/*
+ * Returns the number of bits required to represent the ordinal value `n`.
+ *
+ * @param n The ordinal value.
+ *
+ * @return The number of bits required to represent the ordinal value `n`.
+ *
+ * Note that this macro returns `0` for `n == 0`.
+ */
+#define ZYAN_BITS_TO_REPRESENT(n) \
+ ( \
+ ZYAN_NEEDS_BIT(n, 0) + ZYAN_NEEDS_BIT(n, 1) + \
+ ZYAN_NEEDS_BIT(n, 2) + ZYAN_NEEDS_BIT(n, 3) + \
+ ZYAN_NEEDS_BIT(n, 4) + ZYAN_NEEDS_BIT(n, 5) + \
+ ZYAN_NEEDS_BIT(n, 6) + ZYAN_NEEDS_BIT(n, 7) + \
+ ZYAN_NEEDS_BIT(n, 8) + ZYAN_NEEDS_BIT(n, 9) + \
+ ZYAN_NEEDS_BIT(n, 10) + ZYAN_NEEDS_BIT(n, 11) + \
+ ZYAN_NEEDS_BIT(n, 12) + ZYAN_NEEDS_BIT(n, 13) + \
+ ZYAN_NEEDS_BIT(n, 14) + ZYAN_NEEDS_BIT(n, 15) + \
+ ZYAN_NEEDS_BIT(n, 16) + ZYAN_NEEDS_BIT(n, 17) + \
+ ZYAN_NEEDS_BIT(n, 18) + ZYAN_NEEDS_BIT(n, 19) + \
+ ZYAN_NEEDS_BIT(n, 20) + ZYAN_NEEDS_BIT(n, 21) + \
+ ZYAN_NEEDS_BIT(n, 22) + ZYAN_NEEDS_BIT(n, 23) + \
+ ZYAN_NEEDS_BIT(n, 24) + ZYAN_NEEDS_BIT(n, 25) + \
+ ZYAN_NEEDS_BIT(n, 26) + ZYAN_NEEDS_BIT(n, 27) + \
+ ZYAN_NEEDS_BIT(n, 28) + ZYAN_NEEDS_BIT(n, 29) + \
+ ZYAN_NEEDS_BIT(n, 30) + ZYAN_NEEDS_BIT(n, 31) \
+ )
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+
+#endif /* ZYCORE_DEFINES_H */
diff --git a/externals/zycore/include/Zycore/Format.h b/externals/zycore/include/Zycore/Format.h
new file mode 100644
index 00000000..b0401e62
--- /dev/null
+++ b/externals/zycore/include/Zycore/Format.h
@@ -0,0 +1,286 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * Provides helper functions for performant number to string conversion.
+ */
+
+#ifndef ZYCORE_FORMAT_H
+#define ZYCORE_FORMAT_H
+
+#include <ZycoreExportConfig.h>
+#include <Zycore/Status.h>
+#include <Zycore/String.h>
+#include <Zycore/Types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Helpers */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Get the absolute value of a 64 bit int.
+ *
+ * @param x The value to process.
+ * @return The absolute, unsigned value.
+ *
+ * This gracefully deals with the special case of `x` being `INT_MAX`.
+ */
+ZYAN_INLINE ZyanU64 ZyanAbsI64(ZyanI64 x)
+{
+ // INT_MIN special case. Can't use the value directly because GCC thinks
+ // it's too big for an INT64 literal, however is perfectly happy to accept
+ // this expression. This is also hit INT64_MIN is defined in `stdint.h`.
+ if (x == (-0x7fffffffffffffff - 1))
+ {
+ return 0x8000000000000000u;
+ }
+
+ return (ZyanU64)(x < 0 ? -x : x);
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Insertion */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Inserts formatted text in the destination string at the given `index`.
+ *
+ * @param string The destination string.
+ * @param index The insert index.
+ * @param format The format string.
+ * @param ... The format arguments.
+ *
+ * @return A zyan status code.
+ *
+ * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
+ * `ZyanString` instance.
+ */
+ZYAN_PRINTF_ATTR(3, 4)
+ZYCORE_EXPORT ZyanStatus ZyanStringInsertFormat(ZyanString* string, ZyanUSize index,
+ const char* format, ...);
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Formats the given unsigned ordinal `value` to its decimal text-representation and
+ * inserts it to the `string`.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param index The insert index.
+ * @param value The value.
+ * @param padding_length Padds the converted value with leading zeros, if the number of chars is
+ * less than the `padding_length`.
+ *
+ * @return A zyan status code.
+ *
+ * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
+ * `ZyanString` instance.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringInsertDecU(ZyanString* string, ZyanUSize index, ZyanU64 value,
+ ZyanU8 padding_length);
+
+/**
+ * Formats the given signed ordinal `value` to its decimal text-representation and
+ * inserts it to the `string`.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param index The insert index.
+ * @param value The value.
+ * @param padding_length Padds the converted value with leading zeros, if the number of chars is
+ * less than the `padding_length`.
+ * @param force_sign Set `ZYAN_TRUE`, to force printing of the `+` sign for positive numbers.
+ * @param prefix The string to use as prefix or `ZYAN_NULL`, if not needed.
+ *
+ * @return A zyan status code.
+ *
+ * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
+ * `ZyanString` instance.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringInsertDecS(ZyanString* string, ZyanUSize index, ZyanI64 value,
+ ZyanU8 padding_length, ZyanBool force_sign, const ZyanString* prefix);
+
+/**
+ * Formats the given unsigned ordinal `value` to its hexadecimal text-representation and
+ * inserts it to the `string`.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param index The insert index.
+ * @param value The value.
+ * @param padding_length Padds the converted value with leading zeros, if the number of chars is
+ * less than the `padding_length`.
+ * @param uppercase Set `ZYAN_TRUE` to use uppercase letters ('A'-'F') instead of lowercase
+ * ones ('a'-'f').
+ *
+ * @return A zyan status code.
+ *
+ * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
+ * `ZyanString` instance.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringInsertHexU(ZyanString* string, ZyanUSize index, ZyanU64 value,
+ ZyanU8 padding_length, ZyanBool uppercase);
+
+/**
+ * Formats the given signed ordinal `value` to its hexadecimal text-representation and
+ * inserts it to the `string`.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param index The insert index.
+ * @param value The value.
+ * @param padding_length Padds the converted value with leading zeros, if the number of chars is
+ * less than the `padding_length`.
+ * @param uppercase Set `ZYAN_TRUE` to use uppercase letters ('A'-'F') instead of lowercase
+ * ones ('a'-'f').
+ * @param force_sign Set `ZYAN_TRUE`, to force printing of the `+` sign for positive numbers.
+ * @param prefix The string to use as prefix or `ZYAN_NULL`, if not needed.
+ *
+ * @return A zyan status code.
+ *
+ * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
+ * `ZyanString` instance.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringInsertHexS(ZyanString* string, ZyanUSize index, ZyanI64 value,
+ ZyanU8 padding_length, ZyanBool uppercase, ZyanBool force_sign, const ZyanString* prefix);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Appending */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYAN_NO_LIBC
+
+/**
+ * Appends formatted text to the destination string.
+ *
+ * @param string The destination string.
+ * @param format The format string.
+ * @param ... The format arguments.
+ *
+ * @return A zyan status code.
+ *
+ * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
+ * `ZyanString` instance.
+ */
+ZYAN_PRINTF_ATTR(2, 3)
+ZYCORE_EXPORT ZYAN_REQUIRES_LIBC ZyanStatus ZyanStringAppendFormat(
+ ZyanString* string, const char* format, ...);
+
+#endif // ZYAN_NO_LIBC
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Formats the given unsigned ordinal `value` to its decimal text-representation and
+ * appends it to the `string`.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param value The value.
+ * @param padding_length Padds the converted value with leading zeros, if the number of chars is
+ * less than the `padding_length`.
+ *
+ * @return A zyan status code.
+ *
+ * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
+ * `ZyanString` instance.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringAppendDecU(ZyanString* string, ZyanU64 value,
+ ZyanU8 padding_length);
+
+/**
+ * Formats the given signed ordinal `value` to its decimal text-representation and
+ * appends it to the `string`.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param value The value.
+ * @param padding_length Padds the converted value with leading zeros, if the number of chars is
+ * less than the `padding_length`.
+ * @param force_sign Set `ZYAN_TRUE`, to force printing of the `+` sign for positive numbers.
+ * @param prefix The string to use as prefix or `ZYAN_NULL`, if not needed.
+ *
+ * @return A zyan status code.
+ *
+ * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
+ * `ZyanString` instance.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringAppendDecS(ZyanString* string, ZyanI64 value,
+ ZyanU8 padding_length, ZyanBool force_sign, const ZyanStringView* prefix);
+
+/**
+ * Formats the given unsigned ordinal `value` to its hexadecimal text-representation and
+ * appends it to the `string`.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param value The value.
+ * @param padding_length Padds the converted value with leading zeros, if the number of chars is
+ * less than the `padding_length`.
+ * @param uppercase Set `ZYAN_TRUE` to use uppercase letters ('A'-'F') instead of lowercase
+ * ones ('a'-'f').
+ *
+ * @return A zyan status code.
+ *
+ * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
+ * `ZyanString` instance.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringAppendHexU(ZyanString* string, ZyanU64 value,
+ ZyanU8 padding_length, ZyanBool uppercase);
+
+/**
+ * Formats the given signed ordinal `value` to its hexadecimal text-representation and
+ * appends it to the `string`.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param value The value.
+ * @param padding_length Padds the converted value with leading zeros, if the number of chars is
+ * less than the `padding_length`.
+ * @param uppercase Set `ZYAN_TRUE` to use uppercase letters ('A'-'F') instead of lowercase
+ * ones ('a'-'f').
+ * @param force_sign Set `ZYAN_TRUE`, to force printing of the `+` sign for positive numbers.
+ * @param prefix The string to use as prefix or `ZYAN_NULL`, if not needed.
+ *
+ * @return A zyan status code.
+ *
+ * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
+ * `ZyanString` instance.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringAppendHexS(ZyanString* string, ZyanI64 value,
+ ZyanU8 padding_length, ZyanBool uppercase, ZyanBool force_sign, const ZyanStringView* prefix);
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ZYCORE_FORMAT_H
diff --git a/externals/zycore/include/Zycore/LibC.h b/externals/zycore/include/Zycore/LibC.h
new file mode 100644
index 00000000..cb0b2f32
--- /dev/null
+++ b/externals/zycore/include/Zycore/LibC.h
@@ -0,0 +1,511 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd, Joel Hoener
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * Provides a simple LibC abstraction and fallback routines.
+ */
+
+#ifndef ZYCORE_LIBC_H
+#define ZYCORE_LIBC_H
+
+#ifndef ZYAN_CUSTOM_LIBC
+
+// Include a custom LibC header and define `ZYAN_CUSTOM_LIBC` to provide your own LibC
+// replacement functions
+
+#ifndef ZYAN_NO_LIBC
+
+/* ============================================================================================== */
+/* LibC is available */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* errno.h */
+/* ---------------------------------------------------------------------------------------------- */
+
+#include <errno.h>
+
+#define ZYAN_ERRNO errno
+
+/* ---------------------------------------------------------------------------------------------- */
+/* stdarg.h */
+/* ---------------------------------------------------------------------------------------------- */
+
+#include <stdarg.h>
+
+/**
+ * Defines the `ZyanVAList` datatype.
+ */
+typedef va_list ZyanVAList;
+
+#define ZYAN_VA_START va_start
+#define ZYAN_VA_ARG va_arg
+#define ZYAN_VA_END va_end
+#define ZYAN_VA_COPY(dest, source) va_copy((dest), (source))
+
+/* ---------------------------------------------------------------------------------------------- */
+/* stdio.h */
+/* ---------------------------------------------------------------------------------------------- */
+
+#include <stdio.h>
+
+#define ZYAN_FPUTS fputs
+#define ZYAN_FPUTC fputc
+#define ZYAN_FPRINTF fprintf
+#define ZYAN_PRINTF printf
+#define ZYAN_PUTC putc
+#define ZYAN_PUTS puts
+#define ZYAN_SCANF scanf
+#define ZYAN_SSCANF sscanf
+#define ZYAN_VSNPRINTF vsnprintf
+
+/**
+ * Defines the `ZyanFile` datatype.
+ */
+typedef FILE ZyanFile;
+
+#define ZYAN_STDIN stdin
+#define ZYAN_STDOUT stdout
+#define ZYAN_STDERR stderr
+
+/* ---------------------------------------------------------------------------------------------- */
+/* stdlib.h */
+/* ---------------------------------------------------------------------------------------------- */
+
+#include <stdlib.h>
+#define ZYAN_CALLOC calloc
+#define ZYAN_FREE free
+#define ZYAN_MALLOC malloc
+#define ZYAN_REALLOC realloc
+
+/* ---------------------------------------------------------------------------------------------- */
+/* string.h */
+/* ---------------------------------------------------------------------------------------------- */
+
+#include <string.h>
+#define ZYAN_MEMCHR memchr
+#define ZYAN_MEMCMP memcmp
+#define ZYAN_MEMCPY memcpy
+#define ZYAN_MEMMOVE memmove
+#define ZYAN_MEMSET memset
+#define ZYAN_STRCAT strcat
+#define ZYAN_STRCHR strchr
+#define ZYAN_STRCMP strcmp
+#define ZYAN_STRCOLL strcoll
+#define ZYAN_STRCPY strcpy
+#define ZYAN_STRCSPN strcspn
+#define ZYAN_STRLEN strlen
+#define ZYAN_STRNCAT strncat
+#define ZYAN_STRNCMP strncmp
+#define ZYAN_STRNCPY strncpy
+#define ZYAN_STRPBRK strpbrk
+#define ZYAN_STRRCHR strrchr
+#define ZYAN_STRSPN strspn
+#define ZYAN_STRSTR strstr
+#define ZYAN_STRTOK strtok
+#define ZYAN_STRXFRM strxfrm
+
+/* ---------------------------------------------------------------------------------------------- */
+
+#else // if ZYAN_NO_LIBC
+
+/* ============================================================================================== */
+/* No LibC available, use our own functions */
+/* ============================================================================================== */
+
+#include <Zycore/Defines.h>
+#include <Zycore/Types.h>
+
+/*
+ * These implementations are by no means optimized and will be outperformed by pretty much any
+ * libc implementation out there. We do not aim towards providing competetive implementations here,
+ * but towards providing a last resort fallback for environments without a working libc.
+ */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* stdarg.h */
+/* ---------------------------------------------------------------------------------------------- */
+
+#if defined(ZYAN_MSVC) || defined(ZYAN_ICC)
+
+/**
+ * Defines the `ZyanVAList` datatype.
+ */
+typedef char* ZyanVAList;
+
+# define ZYAN_VA_START __crt_va_start
+# define ZYAN_VA_ARG __crt_va_arg
+# define ZYAN_VA_END __crt_va_end
+# define ZYAN_VA_COPY(destination, source) ((destination) = (source))
+
+#elif defined(ZYAN_GNUC)
+
+/**
+ * Defines the `ZyanVAList` datatype.
+ */
+typedef __builtin_va_list ZyanVAList;
+
+# define ZYAN_VA_START(v, l) __builtin_va_start(v, l)
+# define ZYAN_VA_END(v) __builtin_va_end(v)
+# define ZYAN_VA_ARG(v, l) __builtin_va_arg(v, l)
+# define ZYAN_VA_COPY(d, s) __builtin_va_copy(d, s)
+
+#else
+# error "Unsupported compiler for no-libc mode."
+#endif
+
+/* ---------------------------------------------------------------------------------------------- */
+/* stdio.h */
+/* ---------------------------------------------------------------------------------------------- */
+
+// ZYAN_INLINE int ZYAN_VSNPRINTF (char* const buffer, ZyanUSize const count,
+// char const* const format, ZyanVAList args)
+// {
+// // We cant provide a fallback implementation for this function
+// ZYAN_UNUSED(buffer);
+// ZYAN_UNUSED(count);
+// ZYAN_UNUSED(format);
+// ZYAN_UNUSED(args);
+// return ZYAN_NULL;
+// }
+
+/* ---------------------------------------------------------------------------------------------- */
+/* stdlib.h */
+/* ---------------------------------------------------------------------------------------------- */
+
+// ZYAN_INLINE void* ZYAN_CALLOC(ZyanUSize nitems, ZyanUSize size)
+// {
+// // We cant provide a fallback implementation for this function
+// ZYAN_UNUSED(nitems);
+// ZYAN_UNUSED(size);
+// return ZYAN_NULL;
+// }
+//
+// ZYAN_INLINE void ZYAN_FREE(void *p)
+// {
+// // We cant provide a fallback implementation for this function
+// ZYAN_UNUSED(p);
+// }
+//
+// ZYAN_INLINE void* ZYAN_MALLOC(ZyanUSize n)
+// {
+// // We cant provide a fallback implementation for this function
+// ZYAN_UNUSED(n);
+// return ZYAN_NULL;
+// }
+//
+// ZYAN_INLINE void* ZYAN_REALLOC(void* p, ZyanUSize n)
+// {
+// // We cant provide a fallback implementation for this function
+// ZYAN_UNUSED(p);
+// ZYAN_UNUSED(n);
+// return ZYAN_NULL;
+// }
+
+/* ---------------------------------------------------------------------------------------------- */
+/* string.h */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZYAN_INLINE void* ZYAN_MEMCHR(const void* str, int c, ZyanUSize n)
+{
+ const ZyanU8* p = (ZyanU8*)str;
+ while (n--)
+ {
+ if (*p != (ZyanU8)c)
+ {
+ p++;
+ } else
+ {
+ return (void*)p;
+ }
+ }
+ return 0;
+}
+
+ZYAN_INLINE int ZYAN_MEMCMP(const void* s1, const void* s2, ZyanUSize n)
+{
+ const ZyanU8* p1 = s1, *p2 = s2;
+ while (n--)
+ {
+ if (*p1 != *p2)
+ {
+ return *p1 - *p2;
+ }
+ p1++, p2++;
+ }
+ return 0;
+}
+
+ZYAN_INLINE void* ZYAN_MEMCPY(void* dst, const void* src, ZyanUSize n)
+{
+ volatile ZyanU8* dp = dst;
+ const ZyanU8* sp = src;
+ while (n--)
+ {
+ *dp++ = *sp++;
+ }
+ return dst;
+}
+
+ZYAN_INLINE void* ZYAN_MEMMOVE(void* dst, const void* src, ZyanUSize n)
+{
+ volatile ZyanU8* pd = dst;
+ const ZyanU8* ps = src;
+ if (ps < pd)
+ {
+ for (pd += n, ps += n; n--;)
+ {
+ *--pd = *--ps;
+ }
+ } else
+ {
+ while (n--)
+ {
+ *pd++ = *ps++;
+ }
+ }
+ return dst;
+}
+
+ZYAN_INLINE void* ZYAN_MEMSET(void* dst, int val, ZyanUSize n)
+{
+ volatile ZyanU8* p = dst;
+ while (n--)
+ {
+ *p++ = (unsigned char)val;
+ }
+ return dst;
+}
+
+ZYAN_INLINE char* ZYAN_STRCAT(char* dest, const char* src)
+{
+ char* ret = dest;
+ while (*dest)
+ {
+ dest++;
+ }
+ while ((*dest++ = *src++));
+ return ret;
+}
+
+ZYAN_INLINE char* ZYAN_STRCHR(const char* s, int c)
+{
+ while (*s != (char)c)
+ {
+ if (!*s++)
+ {
+ return 0;
+ }
+ }
+ return (char*)s;
+}
+
+ZYAN_INLINE int ZYAN_STRCMP(const char* s1, const char* s2)
+{
+ while (*s1 && (*s1 == *s2))
+ {
+ s1++, s2++;
+ }
+ return *(const ZyanU8*)s1 - *(const ZyanU8*)s2;
+}
+
+ZYAN_INLINE int ZYAN_STRCOLL(const char *s1, const char *s2)
+{
+ // TODO: Implement
+
+ ZYAN_UNUSED(s1);
+ ZYAN_UNUSED(s2);
+
+ return 0;
+}
+
+ZYAN_INLINE char* ZYAN_STRCPY(char* dest, const char* src)
+{
+ char* ret = dest;
+ while ((*dest++ = *src++));
+ return ret;
+}
+
+ZYAN_INLINE ZyanUSize ZYAN_STRCSPN(const char *s1, const char *s2)
+{
+ ZyanUSize ret = 0;
+ while (*s1)
+ {
+ if (ZYAN_STRCHR(s2, *s1))
+ {
+ return ret;
+ }
+ s1++, ret++;
+ }
+ return ret;
+}
+
+ZYAN_INLINE ZyanUSize ZYAN_STRLEN(const char* str)
+{
+ const char* p = str;
+ while (*str)
+ {
+ ++str;
+ }
+ return str - p;
+}
+
+ZYAN_INLINE char* ZYAN_STRNCAT(char* dest, const char* src, ZyanUSize n)
+{
+ char* ret = dest;
+ while (*dest)
+ {
+ dest++;
+ }
+ while (n--)
+ {
+ if (!(*dest++ = *src++))
+ {
+ return ret;
+ }
+ }
+ *dest = 0;
+ return ret;
+}
+
+ZYAN_INLINE int ZYAN_STRNCMP(const char* s1, const char* s2, ZyanUSize n)
+{
+ while (n--)
+ {
+ if (*s1++ != *s2++)
+ {
+ return *(unsigned char*)(s1 - 1) - *(unsigned char*)(s2 - 1);
+ }
+ }
+ return 0;
+}
+
+ZYAN_INLINE char* ZYAN_STRNCPY(char* dest, const char* src, ZyanUSize n)
+{
+ char* ret = dest;
+ do
+ {
+ if (!n--)
+ {
+ return ret;
+ }
+ } while ((*dest++ = *src++));
+ while (n--)
+ {
+ *dest++ = 0;
+ }
+ return ret;
+}
+
+ZYAN_INLINE char* ZYAN_STRPBRK(const char* s1, const char* s2)
+{
+ while (*s1)
+ {
+ if(ZYAN_STRCHR(s2, *s1++))
+ {
+ return (char*)--s1;
+ }
+ }
+ return 0;
+}
+
+ZYAN_INLINE char* ZYAN_STRRCHR(const char* s, int c)
+{
+ char* ret = 0;
+ do
+ {
+ if (*s == (char)c)
+ {
+ ret = (char*)s;
+ }
+ } while (*s++);
+ return ret;
+}
+
+ZYAN_INLINE ZyanUSize ZYAN_STRSPN(const char* s1, const char* s2)
+{
+ ZyanUSize ret = 0;
+ while (*s1 && ZYAN_STRCHR(s2, *s1++))
+ {
+ ret++;
+ }
+ return ret;
+}
+
+ZYAN_INLINE char* ZYAN_STRSTR(const char* s1, const char* s2)
+{
+ const ZyanUSize n = ZYAN_STRLEN(s2);
+ while (*s1)
+ {
+ if (!ZYAN_MEMCMP(s1++, s2, n))
+ {
+ return (char*)(s1 - 1);
+ }
+ }
+ return 0;
+}
+
+ZYAN_INLINE char* ZYAN_STRTOK(char* str, const char* delim)
+{
+ static char* p = 0;
+ if (str)
+ {
+ p = str;
+ } else
+ if (!p)
+ {
+ return 0;
+ }
+ str = p + ZYAN_STRSPN(p, delim);
+ p = str + ZYAN_STRCSPN(str, delim);
+ if (p == str)
+ {
+ return p = 0;
+ }
+ p = *p ? *p = 0, p + 1 : 0;
+ return str;
+}
+
+ZYAN_INLINE ZyanUSize ZYAN_STRXFRM(char* dest, const char* src, ZyanUSize n)
+{
+ const ZyanUSize n2 = ZYAN_STRLEN(src);
+ if (n > n2)
+ {
+ ZYAN_STRCPY(dest, src);
+ }
+ return n2;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+#endif
+
+#endif
+
+/* ============================================================================================== */
+
+#endif /* ZYCORE_LIBC_H */
diff --git a/externals/zycore/include/Zycore/List.h b/externals/zycore/include/Zycore/List.h
new file mode 100644
index 00000000..015a324d
--- /dev/null
+++ b/externals/zycore/include/Zycore/List.h
@@ -0,0 +1,574 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * Implements a doubly linked list.
+ */
+
+#ifndef ZYCORE_LIST_H
+#define ZYCORE_LIST_H
+
+#include <ZycoreExportConfig.h>
+#include <Zycore/Allocator.h>
+#include <Zycore/Object.h>
+#include <Zycore/Status.h>
+#include <Zycore/Types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ============================================================================================== */
+/* Enums and types */
+/* ============================================================================================== */
+
+/**
+ * Defines the `ZyanListNode` struct.
+ *
+ * All fields in this struct should be considered as "private". Any changes may lead to unexpected
+ * behavior.
+ */
+typedef struct ZyanListNode_
+{
+ /**
+ * A pointer to the previous list node.
+ */
+ struct ZyanListNode_* prev;
+ /**
+ * A pointer to the next list node.
+ */
+ struct ZyanListNode_* next;
+} ZyanListNode;
+
+/**
+ * Defines the `ZyanList` struct.
+ *
+ * All fields in this struct should be considered as "private". Any changes may lead to unexpected
+ * behavior.
+ */
+typedef struct ZyanList_
+{
+ /**
+ * The memory allocator.
+ */
+ ZyanAllocator* allocator;
+ /**
+ * The current number of elements in the list.
+ */
+ ZyanUSize size;
+ /**
+ * The size of a single element in bytes.
+ */
+ ZyanUSize element_size;
+ /**
+ * The element destructor callback.
+ */
+ ZyanMemberProcedure destructor;
+ /**
+ * The head node.
+ */
+ ZyanListNode* head;
+ /**
+ * The tail node.
+ */
+ ZyanListNode* tail;
+ /**
+ * The data buffer.
+ *
+ * Only used for instances created by `ZyanListInitCustomBuffer`.
+ */
+ void* buffer;
+ /**
+ * The data buffer capacity (number of bytes).
+ *
+ * Only used for instances created by `ZyanListInitCustomBuffer`.
+ */
+ ZyanUSize capacity;
+ /**
+ * The first unused node.
+ *
+ * When removing a node, the first-unused value is updated to point at the removed node and the
+ * next node of the removed node will be updated to point at the old first-unused node.
+ *
+ * When appending the memory of the first unused-node is recycled to store the new node. The
+ * value of the first-unused node is then updated to point at the reused nodes next node.
+ *
+ * If the first-unused value is `ZYAN_NULL`, any new node will be "allocated" behind the tail
+ * node (if there is enough space left in the fixed size buffer).
+ *
+ * Only used for instances created by `ZyanListInitCustomBuffer`.
+ */
+ ZyanListNode* first_unused;
+} ZyanList;
+
+/* ============================================================================================== */
+/* Macros */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* General */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Defines an uninitialized `ZyanList` instance.
+ */
+#define ZYAN_LIST_INITIALIZER \
+ { \
+ /* allocator */ ZYAN_NULL, \
+ /* size */ 0, \
+ /* element_size */ 0, \
+ /* head */ ZYAN_NULL, \
+ /* destructor */ ZYAN_NULL, \
+ /* tail */ ZYAN_NULL, \
+ /* buffer */ ZYAN_NULL, \
+ /* capacity */ 0, \
+ /* first_unused */ ZYAN_NULL \
+ }
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Helper macros */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Returns the data value of the given `node`.
+ *
+ * @param type The desired value type.
+ * @param node A pointer to the `ZyanListNode` struct.
+ *
+ * @result The data value of the given `node`.
+ *
+ * Note that this function is unsafe and might dereference a null-pointer.
+ */
+#ifdef __cplusplus
+#define ZYAN_LIST_GET(type, node) \
+ (*reinterpret_cast<const type*>(ZyanListGetNodeData(node)))
+#else
+#define ZYAN_LIST_GET(type, node) \
+ (*(const type*)ZyanListGetNodeData(node))
+#endif
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Constructor and destructor */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYAN_NO_LIBC
+
+/**
+ * Initializes the given `ZyanList` instance.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ * @param element_size The size of a single element in bytes.
+ * @param destructor A destructor callback that is invoked every time an item is deleted, or
+ * `ZYAN_NULL` if not needed.
+ *
+ * @return A zyan status code.
+ *
+ * The memory for the list elements is dynamically allocated by the default allocator.
+ *
+ * Finalization with `ZyanListDestroy` is required for all instances created by this function.
+ */
+ZYCORE_EXPORT ZYAN_REQUIRES_LIBC ZyanStatus ZyanListInit(ZyanList* list, ZyanUSize element_size,
+ ZyanMemberProcedure destructor);
+
+#endif // ZYAN_NO_LIBC
+
+/**
+ * Initializes the given `ZyanList` instance and sets a custom `allocator`.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ * @param element_size The size of a single element in bytes.
+ * @param destructor A destructor callback that is invoked every time an item is deleted, or
+ * `ZYAN_NULL` if not needed.
+ * @param allocator A pointer to a `ZyanAllocator` instance.
+ *
+ * @return A zyan status code.
+ *
+ * Finalization with `ZyanListDestroy` is required for all instances created by this function.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListInitEx(ZyanList* list, ZyanUSize element_size,
+ ZyanMemberProcedure destructor, ZyanAllocator* allocator);
+
+/**
+ * Initializes the given `ZyanList` instance and configures it to use a custom user
+ * defined buffer with a fixed size.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ * @param element_size The size of a single element in bytes.
+ * @param destructor A destructor callback that is invoked every time an item is deleted, or
+ * `ZYAN_NULL` if not needed.
+ * @param buffer A pointer to the buffer that is used as storage for the elements.
+ * @param capacity The maximum capacity (number of bytes) of the buffer including the
+ * space required for the list-nodes.
+ *
+ * @return A zyan status code.
+ *
+ * The buffer capacity required to store `n` elements of type `T` is be calculated by:
+ * `size = n * sizeof(ZyanListNode) + n * sizeof(T)`
+ *
+ * Finalization is not required for instances created by this function.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListInitCustomBuffer(ZyanList* list, ZyanUSize element_size,
+ ZyanMemberProcedure destructor, void* buffer, ZyanUSize capacity);
+
+/**
+ * Destroys the given `ZyanList` instance.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListDestroy(ZyanList* list);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Duplication */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYAN_NO_LIBC
+
+/**
+ * Initializes a new `ZyanList` instance by duplicating an existing list.
+ *
+ * @param destination A pointer to the (uninitialized) destination `ZyanList` instance.
+ * @param source A pointer to the source list.
+ *
+ * @return A zyan status code.
+ *
+ * The memory for the list is dynamically allocated by the default allocator.
+ *
+ * Finalization with `ZyanListDestroy` is required for all instances created by this function.
+ */
+ZYCORE_EXPORT ZYAN_REQUIRES_LIBC ZyanStatus ZyanListDuplicate(ZyanList* destination,
+ const ZyanList* source);
+
+#endif // ZYAN_NO_LIBC
+
+/**
+ * Initializes a new `ZyanList` instance by duplicating an existing list and sets a
+ * custom `allocator`.
+ *
+ * @param destination A pointer to the (uninitialized) destination `ZyanList` instance.
+ * @param source A pointer to the source list.
+ * @param allocator A pointer to a `ZyanAllocator` instance.
+ *
+ * @return A zyan status code.
+
+ * Finalization with `ZyanListDestroy` is required for all instances created by this function.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListDuplicateEx(ZyanList* destination, const ZyanList* source,
+ ZyanAllocator* allocator);
+
+/**
+ * Initializes a new `ZyanList` instance by duplicating an existing list and
+ * configures it to use a custom user defined buffer with a fixed size.
+ *
+ * @param destination A pointer to the (uninitialized) destination `ZyanList` instance.
+ * @param source A pointer to the source list.
+ * @param buffer A pointer to the buffer that is used as storage for the elements.
+ * @param capacity The maximum capacity (number of bytes) of the buffer including the
+ * space required for the list-nodes.
+
+ * This function will fail, if the capacity of the buffer is not sufficient
+ * to store all elements of the source list.
+ *
+ * @return A zyan status code.
+ *
+ * The buffer capacity required to store `n` elements of type `T` is be calculated by:
+ * `size = n * sizeof(ZyanListNode) + n * sizeof(T)`
+ *
+ * Finalization is not required for instances created by this function.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListDuplicateCustomBuffer(ZyanList* destination,
+ const ZyanList* source, void* buffer, ZyanUSize capacity);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Item access */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Returns a pointer to the first `ZyanListNode` struct of the given list.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ * @param node Receives a pointer to the first `ZyanListNode` struct of the list.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListGetHeadNode(const ZyanList* list, const ZyanListNode** node);
+
+/**
+ * Returns a pointer to the last `ZyanListNode` struct of the given list.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ * @param node Receives a pointer to the last `ZyanListNode` struct of the list.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListGetTailNode(const ZyanList* list, const ZyanListNode** node);
+
+/**
+ * Receives a pointer to the previous `ZyanListNode` struct linked to the passed one.
+ *
+ * @param node Receives a pointer to the previous `ZyanListNode` struct linked to the passed
+ * one.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListGetPrevNode(const ZyanListNode** node);
+
+/**
+ * Receives a pointer to the next `ZyanListNode` struct linked to the passed one.
+ *
+ * @param node Receives a pointer to the next `ZyanListNode` struct linked to the passed one.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListGetNextNode(const ZyanListNode** node);
+
+/**
+ * Returns a constant pointer to the data of the given `node`.
+ *
+ * @param node A pointer to the `ZyanListNode` struct.
+ *
+ * @return A constant pointer to the the data of the given `node` or `ZYAN_NULL`, if an error
+ * occured.
+ *
+ * Take a look at `ZyanListGetNodeDataEx`, if you need a function that returns a zyan status code.
+ */
+ZYCORE_EXPORT const void* ZyanListGetNodeData(const ZyanListNode* node);
+
+/**
+ * Returns a constant pointer to the data of the given `node`..
+ *
+ * @param node A pointer to the `ZyanListNode` struct.
+ * @param value Receives a constant pointer to the data of the given `node`.
+ *
+ * Take a look at `ZyanListGetNodeData`, if you need a function that directly returns a pointer.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListGetNodeDataEx(const ZyanListNode* node, const void** value);
+
+/**
+ * Returns a mutable pointer to the data of the given `node`.
+ *
+ * @param node A pointer to the `ZyanListNode` struct.
+ *
+ * @return A mutable pointer to the the data of the given `node` or `ZYAN_NULL`, if an error
+ * occured.
+ *
+ * Take a look at `ZyanListGetPointerMutableEx` instead, if you need a function that returns a
+ * zyan status code.
+ */
+ZYCORE_EXPORT void* ZyanListGetNodeDataMutable(const ZyanListNode* node);
+
+/**
+ * Returns a mutable pointer to the data of the given `node`..
+ *
+ * @param node A pointer to the `ZyanListNode` struct.
+ * @param value Receives a mutable pointer to the data of the given `node`.
+ *
+ * Take a look at `ZyanListGetNodeDataMutable`, if you need a function that directly returns a
+ * pointer.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListGetNodeDataMutableEx(const ZyanListNode* node, void** value);
+
+/**
+ * Assigns a new data value to the given `node`.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ * @param node A pointer to the `ZyanListNode` struct.
+ * @param value The value to assign.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListSetNodeData(const ZyanList* list, const ZyanListNode* node,
+ const void* value);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Insertion */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Adds a new `item` to the end of the list.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ * @param item A pointer to the item to add.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListPushBack(ZyanList* list, const void* item);
+
+/**
+ * Adds a new `item` to the beginning of the list.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ * @param item A pointer to the item to add.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListPushFront(ZyanList* list, const void* item);
+
+/**
+ * Constructs an `item` in-place at the end of the list.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ * @param item Receives a pointer to the new item.
+ * @param constructor The constructor callback or `ZYAN_NULL`. The new item will be in
+ * undefined state, if no constructor was passed.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListEmplaceBack(ZyanList* list, void** item,
+ ZyanMemberFunction constructor);
+
+/**
+ * Constructs an `item` in-place at the beginning of the list.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ * @param item Receives a pointer to the new item.
+ * @param constructor The constructor callback or `ZYAN_NULL`. The new item will be in
+ * undefined state, if no constructor was passed.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListEmplaceFront(ZyanList* list, void** item,
+ ZyanMemberFunction constructor);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Deletion */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Removes the last element of the list.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListPopBack(ZyanList* list);
+
+/**
+ * Removes the firstelement of the list.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListPopFront(ZyanList* list);
+
+/**
+ * Removes the given `node` from the list.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ * @param node A pointer to the `ZyanListNode` struct.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListRemove(ZyanList* list, const ZyanListNode* node);
+
+/**
+ * Removes multiple nodes from the list.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ * @param first A pointer to the first node.
+ * @param last A pointer to the last node.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListRemoveRange(ZyanList* list, const ZyanListNode* first,
+ const ZyanListNode* last);
+
+/**
+ * Erases all elements of the list.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListClear(ZyanList* list);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Searching */
+/* ---------------------------------------------------------------------------------------------- */
+
+// TODO:
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Memory management */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Resizes the given `ZyanList` instance.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ * @param size The new size of the list.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListResize(ZyanList* list, ZyanUSize size);
+
+/**
+ * Resizes the given `ZyanList` instance.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ * @param size The new size of the list.
+ * @param initializer A pointer to a value to be used as initializer for new items.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListResizeEx(ZyanList* list, ZyanUSize size, const void* initializer);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Information */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Returns the current size of the list.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ * @param size Receives the size of the list.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanListGetSize(const ZyanList* list, ZyanUSize* size);
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZYCORE_VECTOR_H */
diff --git a/externals/zycore/include/Zycore/Object.h b/externals/zycore/include/Zycore/Object.h
new file mode 100644
index 00000000..d015cef7
--- /dev/null
+++ b/externals/zycore/include/Zycore/Object.h
@@ -0,0 +1,84 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * Defines some generic object-related datatypes.
+ */
+
+#ifndef ZYCORE_OBJECT_H
+#define ZYCORE_OBJECT_H
+
+#include <Zycore/Status.h>
+#include <Zycore/Types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ============================================================================================== */
+/* Enums and types */
+/* ============================================================================================== */
+
+/**
+ * Defines the `ZyanMemberProcedure` function prototype.
+ *
+ * @param object A pointer to the object.
+ */
+typedef void (*ZyanMemberProcedure)(void* object);
+
+/**
+ * Defines the `ZyanConstMemberProcedure` function prototype.
+ *
+ * @param object A pointer to the object.
+ */
+typedef void (*ZyanConstMemberProcedure)(const void* object);
+
+/**
+ * Defines the `ZyanMemberFunction` function prototype.
+ *
+ * @param object A pointer to the object.
+ *
+ * @return A zyan status code.
+ */
+typedef ZyanStatus (*ZyanMemberFunction)(void* object);
+
+/**
+ * Defines the `ZyanConstMemberFunction` function prototype.
+ *
+ * @param object A pointer to the object.
+ *
+ * @return A zyan status code.
+ */
+typedef ZyanStatus (*ZyanConstMemberFunction)(const void* object);
+
+/* ============================================================================================== */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZYCORE_OBJECT_H */
diff --git a/externals/zycore/include/Zycore/Status.h b/externals/zycore/include/Zycore/Status.h
new file mode 100644
index 00000000..b0d7fdf5
--- /dev/null
+++ b/externals/zycore/include/Zycore/Status.h
@@ -0,0 +1,287 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zyan-C)
+
+ Original Author : Florian Bernd, Joel Hoener
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * Status code definitions and check macros.
+ */
+
+#ifndef ZYCORE_STATUS_H
+#define ZYCORE_STATUS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <Zycore/Types.h>
+
+/* ============================================================================================== */
+/* Enums and types */
+/* ============================================================================================== */
+
+/**
+ * Defines the `ZyanStatus` data type.
+ */
+typedef ZyanU32 ZyanStatus;
+
+/* ============================================================================================== */
+/* Macros */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Definition */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Defines a zyan status code.
+ *
+ * @param error `1`, if the status code signals an error or `0`, if not.
+ * @param module The module id.
+ * @param code The actual code.
+ *
+ * @return The zyan status code.
+ */
+#define ZYAN_MAKE_STATUS(error, module, code) \
+ (ZyanStatus)((((error) & 0x01u) << 31u) | (((module) & 0x7FFu) << 20u) | ((code) & 0xFFFFFu))
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Checks */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Checks if a zyan operation was successful.
+ *
+ * @param status The zyan status-code to check.
+ *
+ * @return `ZYAN_TRUE`, if the operation succeeded or `ZYAN_FALSE`, if not.
+ */
+#define ZYAN_SUCCESS(status) \
+ (!((status) & 0x80000000u))
+
+/**
+ * Checks if a zyan operation failed.
+ *
+ * @param status The zyan status-code to check.
+ *
+ * @return `ZYAN_TRUE`, if the operation failed or `ZYAN_FALSE`, if not.
+ */
+#define ZYAN_FAILED(status) \
+ ((status) & 0x80000000u)
+
+/**
+ * Checks if a zyan operation was successful and returns with the status-code, if not.
+ *
+ * @param status The zyan status-code to check.
+ */
+#define ZYAN_CHECK(status) \
+ do \
+ { \
+ const ZyanStatus status_047620348 = (status); \
+ if (!ZYAN_SUCCESS(status_047620348)) \
+ { \
+ return status_047620348; \
+ } \
+ } while (0)
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Information */
+/* ---------------------------------------------------------------------------------------------- */
+
+ /**
+ * Returns the module id of a zyan status-code.
+ *
+ * @param status The zyan status-code.
+ *
+ * @return The module id of the zyan status-code.
+ */
+#define ZYAN_STATUS_MODULE(status) \
+ (((status) >> 20) & 0x7FFu)
+
+ /**
+ * Returns the code of a zyan status-code.
+ *
+ * @param status The zyan status-code.
+ *
+ * @return The code of the zyan status-code.
+ */
+#define ZYAN_STATUS_CODE(status) \
+ ((status) & 0xFFFFFu)
+
+/* ============================================================================================== */
+/* Status codes */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Module IDs */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * The zycore generic module id.
+ */
+#define ZYAN_MODULE_ZYCORE 0x001u
+
+/**
+ * The zycore arg-parse submodule id.
+ */
+#define ZYAN_MODULE_ARGPARSE 0x003u
+
+/**
+ * The base module id for user-defined status codes.
+ */
+#define ZYAN_MODULE_USER 0x3FFu
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Status codes (general purpose) */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * The operation completed successfully.
+ */
+#define ZYAN_STATUS_SUCCESS \
+ ZYAN_MAKE_STATUS(0u, ZYAN_MODULE_ZYCORE, 0x00u)
+
+/**
+ * The operation failed with an generic error.
+ */
+#define ZYAN_STATUS_FAILED \
+ ZYAN_MAKE_STATUS(1u, ZYAN_MODULE_ZYCORE, 0x01u)
+
+/**
+ * The operation completed successfully and returned `ZYAN_TRUE`.
+ */
+#define ZYAN_STATUS_TRUE \
+ ZYAN_MAKE_STATUS(0u, ZYAN_MODULE_ZYCORE, 0x02u)
+
+/**
+ * The operation completed successfully and returned `ZYAN_FALSE`.
+ */
+#define ZYAN_STATUS_FALSE \
+ ZYAN_MAKE_STATUS(0u, ZYAN_MODULE_ZYCORE, 0x03u)
+
+/**
+ * An invalid argument was passed to a function.
+ */
+#define ZYAN_STATUS_INVALID_ARGUMENT \
+ ZYAN_MAKE_STATUS(1u, ZYAN_MODULE_ZYCORE, 0x04u)
+
+/**
+ * An attempt was made to perform an invalid operation.
+ */
+#define ZYAN_STATUS_INVALID_OPERATION \
+ ZYAN_MAKE_STATUS(1u, ZYAN_MODULE_ZYCORE, 0x05u)
+
+/**
+ * Insufficient privileges to perform the requested operation.
+ */
+#define ZYAN_STATUS_ACCESS_DENIED \
+ ZYAN_MAKE_STATUS(1u, ZYAN_MODULE_ZYCORE, 0x06u)
+
+/**
+ * The requested entity was not found.
+ */
+#define ZYAN_STATUS_NOT_FOUND \
+ ZYAN_MAKE_STATUS(1u, ZYAN_MODULE_ZYCORE, 0x07u)
+
+/**
+ * An index passed to a function was out of bounds.
+ */
+#define ZYAN_STATUS_OUT_OF_RANGE \
+ ZYAN_MAKE_STATUS(1u, ZYAN_MODULE_ZYCORE, 0x08u)
+
+/**
+ * A buffer passed to a function was too small to complete the requested operation.
+ */
+#define ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE \
+ ZYAN_MAKE_STATUS(1u, ZYAN_MODULE_ZYCORE, 0x09u)
+
+/**
+ * Insufficient memory to perform the operation.
+ */
+#define ZYAN_STATUS_NOT_ENOUGH_MEMORY \
+ ZYAN_MAKE_STATUS(1u, ZYAN_MODULE_ZYCORE, 0x0Au)
+
+/**
+ * An unknown error occurred during a system function call.
+ */
+#define ZYAN_STATUS_BAD_SYSTEMCALL \
+ ZYAN_MAKE_STATUS(1u, ZYAN_MODULE_ZYCORE, 0x0Bu)
+
+/**
+ * The process ran out of resources while performing an operation.
+ */
+#define ZYAN_STATUS_OUT_OF_RESOURCES \
+ ZYAN_MAKE_STATUS(1u, ZYAN_MODULE_ZYCORE, 0x0Cu)
+
+/**
+ * A dependency library was not found or does have an unexpected version number or
+ * feature-set.
+ */
+#define ZYAN_STATUS_MISSING_DEPENDENCY \
+ ZYAN_MAKE_STATUS(1u, ZYAN_MODULE_ZYCORE, 0x0Du)
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Status codes (arg parse) */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Argument was not expected.
+ */
+#define ZYAN_STATUS_ARG_NOT_UNDERSTOOD \
+ ZYAN_MAKE_STATUS(1u, ZYAN_MODULE_ARGPARSE, 0x00u)
+
+/**
+ * Too few arguments were provided.
+ */
+#define ZYAN_STATUS_TOO_FEW_ARGS \
+ ZYAN_MAKE_STATUS(1u, ZYAN_MODULE_ARGPARSE, 0x01u)
+
+/**
+ * Too many arguments were provided.
+ */
+#define ZYAN_STATUS_TOO_MANY_ARGS \
+ ZYAN_MAKE_STATUS(1u, ZYAN_MODULE_ARGPARSE, 0x02u)
+
+/**
+ * An argument that expected a value misses its value.
+ */
+#define ZYAN_STATUS_ARG_MISSES_VALUE \
+ ZYAN_MAKE_STATUS(1u, ZYAN_MODULE_ARGPARSE, 0x03u)
+
+/**
+* A required argument is missing.
+*/
+#define ZYAN_STATUS_REQUIRED_ARG_MISSING \
+ ZYAN_MAKE_STATUS(1u, ZYAN_MODULE_ARGPARSE, 0x04u)
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZYCORE_STATUS_H */
diff --git a/externals/zycore/include/Zycore/String.h b/externals/zycore/include/Zycore/String.h
new file mode 100644
index 00000000..c3157bc3
--- /dev/null
+++ b/externals/zycore/include/Zycore/String.h
@@ -0,0 +1,1012 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * Implements a string type.
+ */
+
+#ifndef ZYCORE_STRING_H
+#define ZYCORE_STRING_H
+
+#include <ZycoreExportConfig.h>
+#include <Zycore/Allocator.h>
+#include <Zycore/Status.h>
+#include <Zycore/Types.h>
+#include <Zycore/Vector.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ============================================================================================== */
+/* Constants */
+/* ============================================================================================== */
+
+/**
+ * The initial minimum capacity (number of characters) for all dynamically allocated
+ * string instances - not including the terminating '\0'-character.
+ */
+#define ZYAN_STRING_MIN_CAPACITY 32
+
+/**
+ * The default growth factor for all string instances.
+ */
+#define ZYAN_STRING_DEFAULT_GROWTH_FACTOR 2.00f
+
+/**
+ * The default shrink threshold for all string instances.
+ */
+#define ZYAN_STRING_DEFAULT_SHRINK_THRESHOLD 0.25f
+
+/* ============================================================================================== */
+/* Enums and types */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* String flags */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Defines the `ZyanStringFlags` datatype.
+ */
+typedef ZyanU8 ZyanStringFlags;
+
+/**
+ * The string uses a custom user-defined buffer with a fixed capacity.
+ */
+#define ZYAN_STRING_HAS_FIXED_CAPACITY 0x01 // (1 << 0)
+
+/* ---------------------------------------------------------------------------------------------- */
+/* String */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Defines the `ZyanString` struct.
+ *
+ * The `ZyanString` type is implemented as a size-prefixed string - which allows for a lot of
+ * performance optimizations.
+ * Nevertheless null-termination is guaranteed at all times to provide maximum compatibility with
+ * default C-style strings (use `ZyanStringGetData` to access the C-style string).
+ *
+ * All fields in this struct should be considered as "private". Any changes may lead to unexpected
+ * behavior.
+ */
+typedef struct ZyanString_
+{
+ /**
+ * String flags.
+ */
+ ZyanStringFlags flags;
+ /**
+ * The vector that contains the actual string.
+ */
+ ZyanVector vector;
+} ZyanString;
+
+/* ---------------------------------------------------------------------------------------------- */
+/* View */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Defines the `ZyanStringView` struct.
+ *
+ * The `ZyanStringView` type provides a view inside a string (`ZyanString` instances, null-
+ * terminated C-style strings, or even not-null-terminated custom strings). A view is immutable
+ * by design and can't be directly converted to a C-style string.
+ *
+ * Views might become invalid (e.g. pointing to invalid memory), if the underlying string gets
+ * destroyed or resized.
+ *
+ * The `ZYAN_STRING_TO_VIEW` macro can be used to cast a `ZyanString` to a `ZyanStringView` pointer
+ * without any runtime overhead.
+ * Casting a view to a normal string is not supported and will lead to unexpected behavior (use
+ * `ZyanStringDuplicate` to create a deep-copy instead).
+ *
+ * All fields in this struct should be considered as "private". Any changes may lead to unexpected
+ * behavior.
+ */
+typedef struct ZyanStringView_
+{
+ /**
+ * The string data.
+ *
+ * The view internally re-uses the normal string struct to allow casts without any runtime
+ * overhead.
+ */
+ ZyanString string;
+} ZyanStringView;
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Macros */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* General */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Defines an uninitialized `ZyanString` instance.
+ */
+#define ZYAN_STRING_INITIALIZER \
+ { \
+ /* flags */ 0, \
+ /* vector */ ZYAN_VECTOR_INITIALIZER \
+ }
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Helper macros */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Casts a `ZyanString` pointer to a constant `ZyanStringView` pointer.
+ */
+#define ZYAN_STRING_TO_VIEW(string) (const ZyanStringView*)(string)
+
+/**
+ * Defines a `ZyanStringView` struct that provides a view into a static C-style string.
+ *
+ * @param string The C-style string.
+ */
+#define ZYAN_DEFINE_STRING_VIEW(string) \
+ { \
+ /* string */ \
+ { \
+ /* flags */ 0, \
+ /* vector */ \
+ { \
+ /* allocator */ ZYAN_NULL, \
+ /* growth_factor */ 1.0f, \
+ /* shrink_threshold */ 0.0f, \
+ /* size */ sizeof(string), \
+ /* capacity */ sizeof(string), \
+ /* element_size */ sizeof(char), \
+ /* destructor */ ZYAN_NULL, \
+ /* data */ (char*)(string) \
+ } \
+ } \
+ }
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Constructor and destructor */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYAN_NO_LIBC
+
+/**
+ * Initializes the given `ZyanString` instance.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param capacity The initial capacity (number of characters).
+ *
+ * @return A zyan status code.
+ *
+ * The memory for the string is dynamically allocated by the default allocator using the default
+ * growth factor of `2.0f` and the default shrink threshold of `0.25f`.
+ *
+ * The allocated buffer will be at least one character larger than the given `capacity`, to reserve
+ * space for the terminating '\0'.
+ *
+ * Finalization with `ZyanStringDestroy` is required for all strings created by this function.
+ */
+ZYCORE_EXPORT ZYAN_REQUIRES_LIBC ZyanStatus ZyanStringInit(ZyanString* string, ZyanUSize capacity);
+
+#endif // ZYAN_NO_LIBC
+
+/**
+ * Initializes the given `ZyanString` instance and sets a custom `allocator` and memory
+ * allocation/deallocation parameters.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param capacity The initial capacity (number of characters).
+ * @param allocator A pointer to a `ZyanAllocator` instance.
+ * @param growth_factor The growth factor (from `1.0f` to `x.xf`).
+ * @param shrink_threshold The shrink threshold (from `0.0f` to `1.0f`).
+ *
+ * @return A zyan status code.
+ *
+ * A growth factor of `1.0f` disables overallocation and a shrink threshold of `0.0f` disables
+ * dynamic shrinking.
+ *
+ * The allocated buffer will be at least one character larger than the given `capacity`, to reserve
+ * space for the terminating '\0'.
+ *
+ * Finalization with `ZyanStringDestroy` is required for all strings created by this function.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringInitEx(ZyanString* string, ZyanUSize capacity,
+ ZyanAllocator* allocator, float growth_factor, float shrink_threshold);
+
+/**
+ * Initializes the given `ZyanString` instance and configures it to use a custom user
+ * defined buffer with a fixed size.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param buffer A pointer to the buffer that is used as storage for the string.
+ * @param capacity The maximum capacity (number of characters) of the buffer, including
+ * the terminating '\0'.
+ *
+ * @return A zyan status code.
+ *
+ * Finalization is not required for strings created by this function.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringInitCustomBuffer(ZyanString* string, char* buffer,
+ ZyanUSize capacity);
+
+/**
+ * Destroys the given `ZyanString` instance.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ *
+ * @return A zyan status code.
+ *
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringDestroy(ZyanString* string);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Duplication */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYAN_NO_LIBC
+
+/**
+ * Initializes a new `ZyanString` instance by duplicating an existing string.
+ *
+ * @param destination A pointer to the (uninitialized) destination `ZyanString` instance.
+ * @param source A pointer to the source string.
+ * @param capacity The initial capacity (number of characters).
+ *
+ * This value is automatically adjusted to the size of the source string, if
+ * a smaller value was passed.
+ *
+ * @return A zyan status code.
+ *
+ * The behavior of this function is undefined, if `source` is a view into the `destination`
+ * string or `destination` points to an already initialized `ZyanString` instance.
+ *
+ * The memory for the string is dynamically allocated by the default allocator using the default
+ * growth factor of `2.0f` and the default shrink threshold of `0.25f`.
+ *
+ * The allocated buffer will be at least one character larger than the given `capacity`, to reserve
+ * space for the terminating '\0'.
+ *
+ * Finalization with `ZyanStringDestroy` is required for all strings created by this function.
+ */
+ZYCORE_EXPORT ZYAN_REQUIRES_LIBC ZyanStatus ZyanStringDuplicate(ZyanString* destination,
+ const ZyanStringView* source, ZyanUSize capacity);
+
+#endif // ZYAN_NO_LIBC
+
+/**
+ * Initializes a new `ZyanString` instance by duplicating an existing string and sets a
+ * custom `allocator` and memory allocation/deallocation parameters.
+ *
+ * @param destination A pointer to the (uninitialized) destination `ZyanString` instance.
+ * @param source A pointer to the source string.
+ * @param capacity The initial capacity (number of characters).
+
+ * This value is automatically adjusted to the size of the source
+ * string, if a smaller value was passed.
+ * @param allocator A pointer to a `ZyanAllocator` instance.
+ * @param growth_factor The growth factor (from `1.0f` to `x.xf`).
+ * @param shrink_threshold The shrink threshold (from `0.0f` to `1.0f`).
+ *
+ * @return A zyan status code.
+ *
+ * The behavior of this function is undefined, if `source` is a view into the `destination`
+ * string or `destination` points to an already initialized `ZyanString` instance.
+ *
+ * A growth factor of `1.0f` disables overallocation and a shrink threshold of `0.0f` disables
+ * dynamic shrinking.
+ *
+ * The allocated buffer will be at least one character larger than the given `capacity`, to reserve
+ * space for the terminating '\0'.
+ *
+ * Finalization with `ZyanStringDestroy` is required for all strings created by this function.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringDuplicateEx(ZyanString* destination,
+ const ZyanStringView* source, ZyanUSize capacity, ZyanAllocator* allocator,
+ float growth_factor, float shrink_threshold);
+
+/**
+ * Initializes a new `ZyanString` instance by duplicating an existing string and
+ * configures it to use a custom user defined buffer with a fixed size.
+ *
+ * @param destination A pointer to the (uninitialized) destination `ZyanString` instance.
+ * @param source A pointer to the source string.
+ * @param buffer A pointer to the buffer that is used as storage for the string.
+ * @param capacity The maximum capacity (number of characters) of the buffer, including the
+ * terminating '\0'.
+
+ * This function will fail, if the capacity of the buffer is less or equal to
+ * the size of the source string.
+ *
+ * @return A zyan status code.
+ *
+ * The behavior of this function is undefined, if `source` is a view into the `destination`
+ * string or `destination` points to an already initialized `ZyanString` instance.
+ *
+ * Finalization is not required for strings created by this function.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringDuplicateCustomBuffer(ZyanString* destination,
+ const ZyanStringView* source, char* buffer, ZyanUSize capacity);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Concatenation */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYAN_NO_LIBC
+
+/**
+ * Initializes a new `ZyanString` instance by concatenating two existing strings.
+ *
+ * @param destination A pointer to the (uninitialized) destination `ZyanString` instance.
+ *
+ * This function will fail, if the destination `ZyanString` instance equals
+ * one of the source strings.
+ * @param s1 A pointer to the first source string.
+ * @param s2 A pointer to the second source string.
+ * @param capacity The initial capacity (number of characters).
+
+ * This value is automatically adjusted to the combined size of the source
+ * strings, if a smaller value was passed.
+ *
+ * @return A zyan status code.
+ *
+ * The behavior of this function is undefined, if `s1` or `s2` are views into the `destination`
+ * string or `destination` points to an already initialized `ZyanString` instance.
+ *
+ * The memory for the string is dynamically allocated by the default allocator using the default
+ * growth factor of `2.0f` and the default shrink threshold of `0.25f`.
+ *
+ * The allocated buffer will be at least one character larger than the given `capacity`, to reserve
+ * space for the terminating '\0'.
+ *
+ * Finalization with `ZyanStringDestroy` is required for all strings created by this function.
+ */
+ZYCORE_EXPORT ZYAN_REQUIRES_LIBC ZyanStatus ZyanStringConcat(ZyanString* destination,
+ const ZyanStringView* s1, const ZyanStringView* s2, ZyanUSize capacity);
+
+#endif // ZYAN_NO_LIBC
+
+/**
+ * Initializes a new `ZyanString` instance by concatenating two existing strings and sets
+ * a custom `allocator` and memory allocation/deallocation parameters.
+ *
+ * @param destination A pointer to the (uninitialized) destination `ZyanString` instance.
+ *
+ * This function will fail, if the destination `ZyanString` instance
+ * equals one of the source strings.
+ * @param s1 A pointer to the first source string.
+ * @param s2 A pointer to the second source string.
+ * @param capacity The initial capacity (number of characters).
+ *
+ * This value is automatically adjusted to the combined size of the
+ * source strings, if a smaller value was passed.
+ * @param allocator A pointer to a `ZyanAllocator` instance.
+ * @param growth_factor The growth factor (from `1.0f` to `x.xf`).
+ * @param shrink_threshold The shrink threshold (from `0.0f` to `1.0f`).
+ *
+ * @return A zyan status code.
+ *
+ * The behavior of this function is undefined, if `s1` or `s2` are views into the `destination`
+ * string or `destination` points to an already initialized `ZyanString` instance.
+ *
+ * A growth factor of `1.0f` disables overallocation and a shrink threshold of `0.0f` disables
+ * dynamic shrinking.
+ *
+ * The allocated buffer will be at least one character larger than the given `capacity`, to reserve
+ * space for the terminating '\0'.
+ *
+ * Finalization with `ZyanStringDestroy` is required for all strings created by this function.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringConcatEx(ZyanString* destination, const ZyanStringView* s1,
+ const ZyanStringView* s2, ZyanUSize capacity, ZyanAllocator* allocator, float growth_factor,
+ float shrink_threshold);
+
+/**
+ * Initializes a new `ZyanString` instance by concatenating two existing strings and
+ * configures it to use a custom user defined buffer with a fixed size.
+ *
+ * @param destination A pointer to the (uninitialized) destination `ZyanString` instance.
+ *
+ * This function will fail, if the destination `ZyanString` instance equals
+ * one of the source strings.
+ * @param s1 A pointer to the first source string.
+ * @param s2 A pointer to the second source string.
+ * @param buffer A pointer to the buffer that is used as storage for the string.
+ * @param capacity The maximum capacity (number of characters) of the buffer.
+ *
+ * This function will fail, if the capacity of the buffer is less or equal to
+ * the combined size of the source strings.
+ *
+ * @return A zyan status code.
+ *
+ * The behavior of this function is undefined, if `s1` or `s2` are views into the `destination`
+ * string or `destination` points to an already initialized `ZyanString` instance.
+ *
+ * Finalization is not required for strings created by this function.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringConcatCustomBuffer(ZyanString* destination,
+ const ZyanStringView* s1, const ZyanStringView* s2, char* buffer, ZyanUSize capacity);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Views */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Returns a view inside an existing view/string.
+ *
+ * @param view A pointer to the `ZyanStringView` instance.
+ * @param source A pointer to the source string.
+ *
+ * @return A zyan status code.
+ *
+ * The `ZYAN_STRING_TO_VEW` macro can be used to pass any `ZyanString` instance as value for the
+ * `source` string.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringViewInsideView(ZyanStringView* view,
+ const ZyanStringView* source);
+
+/**
+ * Returns a view inside an existing view/string starting from the given `index`.
+ *
+ * @param view A pointer to the `ZyanStringView` instance.
+ * @param source A pointer to the source string.
+ * @param index The start index.
+ * @param count The number of characters.
+ *
+ * @return A zyan status code.
+ *
+ * The `ZYAN_STRING_TO_VEW` macro can be used to pass any `ZyanString` instance as value for the
+ * `source` string.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringViewInsideViewEx(ZyanStringView* view,
+ const ZyanStringView* source, ZyanUSize index, ZyanUSize count);
+
+/**
+ * Returns a view inside a null-terminated C-style string.
+ *
+ * @param view A pointer to the `ZyanStringView` instance.
+ * @param string The C-style string.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringViewInsideBuffer(ZyanStringView* view, const char* string);
+
+/**
+ * Returns a view inside a character buffer with custom length.
+ *
+ * @param view A pointer to the `ZyanStringView` instance.
+ * @param buffer A pointer to the buffer containing the string characters.
+ * @param length The length of the string (number of characters).
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringViewInsideBufferEx(ZyanStringView* view, const char* buffer,
+ ZyanUSize length);
+
+/**
+ * Returns the size (number of characters) of the view.
+ *
+ * @param view A pointer to the `ZyanStringView` instance.
+ * @param size Receives the size (number of characters) of the view.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringViewGetSize(const ZyanStringView* view, ZyanUSize* size);
+
+/**
+ * Returns the C-style string of the given `ZyanString` instance.
+ *
+ * @warning The string is not guaranteed to be null terminated!
+ *
+ * @param string A pointer to the `ZyanStringView` instance.
+ * @param value Receives a pointer to the C-style string.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringViewGetData(const ZyanStringView* view, const char** buffer);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Character access */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Returns the character at the given `index`.
+ *
+ * @param string A pointer to the `ZyanStringView` instance.
+ * @param index The character index.
+ * @param value Receives the desired character of the string.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringGetChar(const ZyanStringView* string, ZyanUSize index,
+ char* value);
+
+/**
+ * Returns a pointer to the character at the given `index`.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param index The character index.
+ * @param value Receives a pointer to the desired character in the string.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringGetCharMutable(ZyanString* string, ZyanUSize index,
+ char** value);
+
+/**
+ * Assigns a new value to the character at the given `index`.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param index The character index.
+ * @param value The character to assign.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringSetChar(ZyanString* string, ZyanUSize index, char value);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Insertion */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Inserts the content of the source string in the destination string at the given `index`.
+ *
+ * @param destination The destination string.
+ * @param index The insert index.
+ * @param source The source string.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringInsert(ZyanString* destination, ZyanUSize index,
+ const ZyanStringView* source);
+
+/**
+ * Inserts `count` characters of the source string in the destination string at the given
+ * `index`.
+ *
+ * @param destination The destination string.
+ * @param destination_index The insert index.
+ * @param source The source string.
+ * @param source_index The index of the first character to be inserted from the source
+ * string.
+ * @param count The number of chars to insert from the source string.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringInsertEx(ZyanString* destination, ZyanUSize destination_index,
+ const ZyanStringView* source, ZyanUSize source_index, ZyanUSize count);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Appending */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Appends the content of the source string to the end of the destination string.
+ *
+ * @param destination The destination string.
+ * @param source The source string.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringAppend(ZyanString* destination, const ZyanStringView* source);
+
+/**
+ * Appends `count` characters of the source string to the end of the destination string.
+ *
+ * @param destination The destination string.
+ * @param source The source string.
+ * @param source_index The index of the first character to be appended from the source string.
+ * @param count The number of chars to append from the source string.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringAppendEx(ZyanString* destination, const ZyanStringView* source,
+ ZyanUSize source_index, ZyanUSize count);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Deletion */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Deletes characters from the given string, starting at `index`.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param index The index of the first character to delete.
+ * @param count The number of characters to delete.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringDelete(ZyanString* string, ZyanUSize index, ZyanUSize count);
+
+/**
+ * Deletes all remaining characters from the given string, starting at `index`.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param index The index of the first character to delete.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringTruncate(ZyanString* string, ZyanUSize index);
+
+/**
+ * Erases the given string.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringClear(ZyanString* string);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Searching */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Searches for the first occurrence of `needle` in the given `haystack` starting from the
+ * left.
+ *
+ * @param haystack The string to search in.
+ * @param needle The sub-string to search for.
+ * @param found_index A pointer to a variable that receives the index of the first occurrence of
+ * `needle`.
+ *
+ * @return `ZYAN_STATUS_TRUE`, if the needle was found, `ZYAN_STATUS_FALSE`, if not, or another
+ * zyan status code, if an error occured.
+ *
+ * The `found_index` is set to `-1`, if the needle was not found.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringLPos(const ZyanStringView* haystack,
+ const ZyanStringView* needle, ZyanISize* found_index);
+
+/**
+ * Searches for the first occurrence of `needle` in the given `haystack` starting from the
+ * left.
+ *
+ * @param haystack The string to search in.
+ * @param needle The sub-string to search for.
+ * @param found_index A pointer to a variable that receives the index of the first occurrence of
+ * `needle`.
+ * @param index The start index.
+ * @param count The maximum number of characters to iterate, beginning from the start
+ * `index`.
+ *
+ * @return `ZYAN_STATUS_TRUE`, if the needle was found, `ZYAN_STATUS_FALSE`, if not, or another
+ * zyan status code, if an error occured.
+ *
+ * The `found_index` is set to `-1`, if the needle was not found.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringLPosEx(const ZyanStringView* haystack,
+ const ZyanStringView* needle, ZyanISize* found_index, ZyanUSize index, ZyanUSize count);
+
+/**
+ * Performs a case-insensitive search for the first occurrence of `needle` in the given
+ * `haystack` starting from the left.
+ *
+ * @param haystack The string to search in.
+ * @param needle The sub-string to search for.
+ * @param found_index A pointer to a variable that receives the index of the first occurrence of
+ * `needle`.
+ *
+ * @return `ZYAN_STATUS_TRUE`, if the needle was found, `ZYAN_STATUS_FALSE`, if not, or another
+ * zyan status code, if an error occured.
+ *
+ * The `found_index` is set to `-1`, if the needle was not found.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringLPosI(const ZyanStringView* haystack,
+ const ZyanStringView* needle, ZyanISize* found_index);
+
+/**
+ * Performs a case-insensitive search for the first occurrence of `needle` in the given
+ * `haystack` starting from the left.
+ *
+ * @param haystack The string to search in.
+ * @param needle The sub-string to search for.
+ * @param found_index A pointer to a variable that receives the index of the first occurrence of
+ * `needle`.
+ * @param index The start index.
+ * @param count The maximum number of characters to iterate, beginning from the start
+ * `index`.
+ *
+ * @return `ZYAN_STATUS_TRUE`, if the needle was found, `ZYAN_STATUS_FALSE`, if not, or another
+ * zyan status code, if an error occured.
+ *
+ * The `found_index` is set to `-1`, if the needle was not found.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringLPosIEx(const ZyanStringView* haystack,
+ const ZyanStringView* needle, ZyanISize* found_index, ZyanUSize index, ZyanUSize count);
+
+/**
+ * Searches for the first occurrence of `needle` in the given `haystack` starting from the
+ * right.
+ *
+ * @param haystack The string to search in.
+ * @param needle The sub-string to search for.
+ * @param found_index A pointer to a variable that receives the index of the first occurrence of
+ * `needle`.
+ *
+ * @return `ZYAN_STATUS_TRUE`, if the needle was found, `ZYAN_STATUS_FALSE`, if not, or another
+ * zyan status code, if an error occured.
+ *
+ * The `found_index` is set to `-1`, if the needle was not found.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringRPos(const ZyanStringView* haystack,
+ const ZyanStringView* needle, ZyanISize* found_index);
+
+/**
+ * Searches for the first occurrence of `needle` in the given `haystack` starting from the
+ * right.
+ *
+ * @param haystack The string to search in.
+ * @param needle The sub-string to search for.
+ * @param found_index A pointer to a variable that receives the index of the first occurrence of
+ * `needle`.
+ * @param index The start index.
+ * @param count The maximum number of characters to iterate, beginning from the start
+ * `index`.
+ *
+ * @return `ZYAN_STATUS_TRUE`, if the needle was found, `ZYAN_STATUS_FALSE`, if not, or another
+ * zyan status code, if an error occured.
+ *
+ * The `found_index` is set to `-1`, if the needle was not found.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringRPosEx(const ZyanStringView* haystack,
+ const ZyanStringView* needle, ZyanISize* found_index, ZyanUSize index, ZyanUSize count);
+
+/**
+ * Performs a case-insensitive search for the first occurrence of `needle` in the given
+ * `haystack` starting from the right.
+ *
+ * @param haystack The string to search in.
+ * @param needle The sub-string to search for.
+ * @param found_index A pointer to a variable that receives the index of the first occurrence of
+ * `needle`.
+ *
+ * @return `ZYAN_STATUS_TRUE`, if the needle was found, `ZYAN_STATUS_FALSE`, if not, or another
+ * zyan status code, if an error occured.
+ *
+ * The `found_index` is set to `-1`, if the needle was not found.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringRPosI(const ZyanStringView* haystack,
+ const ZyanStringView* needle, ZyanISize* found_index);
+
+/**
+ * Performs a case-insensitive search for the first occurrence of `needle` in the given
+ * `haystack` starting from the right.
+ *
+ * @param haystack The string to search in.
+ * @param needle The sub-string to search for.
+ * @param found_index A pointer to a variable that receives the index of the first occurrence of
+ * `needle`.
+ * @param index The start index.
+ * @param count The maximum number of characters to iterate, beginning from the start
+ * `index`.
+ *
+ * @return `ZYAN_STATUS_TRUE`, if the needle was found, `ZYAN_STATUS_FALSE`, if not, or another
+ * zyan status code, if an error occured.
+ *
+ * The `found_index` is set to `-1`, if the needle was not found.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringRPosIEx(const ZyanStringView* haystack,
+ const ZyanStringView* needle, ZyanISize* found_index, ZyanUSize index, ZyanUSize count);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Comparing */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Compares two strings.
+ *
+ * @param s1 The first string
+ * @param s2 The second string.
+ * @param result Receives the comparison result.
+ *
+ * Values:
+ * - `result < 0` -> The first character that does not match has a lower value
+ * in `s1` than in `s2`.
+ * - `result == 0` -> The contents of both strings are equal.
+ * - `result > 0` -> The first character that does not match has a greater value
+ * in `s1` than in `s2`.
+ *
+ * @return `ZYAN_STATUS_TRUE`, if the strings are equal, `ZYAN_STATUS_FALSE`, if not, or another
+ * zyan status code, if an error occured.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringCompare(const ZyanStringView* s1, const ZyanStringView* s2,
+ ZyanI32* result);
+
+/**
+ * Performs a case-insensitive comparison of two strings.
+ *
+ * @param s1 The first string
+ * @param s2 The second string.
+ * @param result Receives the comparison result.
+ *
+ * Values:
+ * - `result < 0` -> The first character that does not match has a lower value
+ * in `s1` than in `s2`.
+ * - `result == 0` -> The contents of both strings are equal.
+ * - `result > 0` -> The first character that does not match has a greater value
+ * in `s1` than in `s2`.
+ *
+ * @return `ZYAN_STATUS_TRUE`, if the strings are equal, `ZYAN_STATUS_FALSE`, if not, or another
+ * zyan status code, if an error occured.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringCompareI(const ZyanStringView* s1, const ZyanStringView* s2,
+ ZyanI32* result);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Case conversion */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Converts the given string to lowercase letters.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ *
+ * @return A zyan status code.
+ *
+ * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
+ * `ZyanString` instance.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringToLowerCase(ZyanString* string);
+
+/**
+ * Converts `count` characters of the given string to lowercase letters.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param index The start index.
+ * @param count The number of characters to convert, beginning from the start `index`.
+ *
+ * @return A zyan status code.
+ *
+ * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
+ * `ZyanString` instance.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringToLowerCaseEx(ZyanString* string, ZyanUSize index,
+ ZyanUSize count);
+
+/**
+ * Converts the given string to uppercase letters.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ *
+ * @return A zyan status code.
+ *
+ * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
+ * `ZyanString` instance.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringToUpperCase(ZyanString* string);
+
+/**
+ * Converts `count` characters of the given string to uppercase letters.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param index The start index.
+ * @param count The number of characters to convert, beginning from the start `index`.
+ *
+ * @return A zyan status code.
+ *
+ * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
+ * `ZyanString` instance.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringToUpperCaseEx(ZyanString* string, ZyanUSize index,
+ ZyanUSize count);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Memory management */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Resizes the given `ZyanString` instance.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param size The new size of the string.
+ *
+ * @return A zyan status code.
+ *
+ * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
+ * `ZyanString` instance.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringResize(ZyanString* string, ZyanUSize size);
+
+/**
+ * Changes the capacity of the given `ZyanString` instance.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param capacity The new minimum capacity of the string.
+ *
+ * @return A zyan status code.
+ *
+ * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
+ * `ZyanString` instance.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringReserve(ZyanString* string, ZyanUSize capacity);
+
+/**
+ * Shrinks the capacity of the given string to match it's size.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ *
+ * @return A zyan status code.
+ *
+ * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
+ * `ZyanString` instance.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringShrinkToFit(ZyanString* string);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Information */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Returns the current capacity of the string.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param capacity Receives the size of the string.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringGetCapacity(const ZyanString* string, ZyanUSize* capacity);
+
+/**
+ * Returns the current size (number of characters) of the string (excluding the
+ * terminating zero character).
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param size Receives the size (number of characters) of the string.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringGetSize(const ZyanString* string, ZyanUSize* size);
+
+/**
+ * Returns the C-style string of the given `ZyanString` instance.
+ *
+ * @param string A pointer to the `ZyanString` instance.
+ * @param value Receives a pointer to the C-style string.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanStringGetData(const ZyanString* string, const char** value);
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ZYCORE_STRING_H
diff --git a/externals/zycore/include/Zycore/Types.h b/externals/zycore/include/Zycore/Types.h
new file mode 100644
index 00000000..74fe9056
--- /dev/null
+++ b/externals/zycore/include/Zycore/Types.h
@@ -0,0 +1,195 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zyan-C)
+
+ Original Author : Florian Bernd, Joel Hoener
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * Includes and defines some default data types.
+ */
+
+#ifndef ZYCORE_TYPES_H
+#define ZYCORE_TYPES_H
+
+#include <Zycore/Defines.h>
+
+/* ============================================================================================== */
+/* Integer types */
+/* ============================================================================================== */
+
+#if defined(ZYAN_NO_LIBC) || \
+ (defined(ZYAN_MSVC) && defined(ZYAN_KERNEL)) // The WDK LibC lacks stdint.h.
+ // No LibC mode, use compiler built-in types / macros.
+# if defined(ZYAN_MSVC) || defined(ZYAN_ICC)
+ typedef unsigned __int8 ZyanU8;
+ typedef unsigned __int16 ZyanU16;
+ typedef unsigned __int32 ZyanU32;
+ typedef unsigned __int64 ZyanU64;
+ typedef signed __int8 ZyanI8;
+ typedef signed __int16 ZyanI16;
+ typedef signed __int32 ZyanI32;
+ typedef signed __int64 ZyanI64;
+# if _WIN64
+ typedef ZyanU64 ZyanUSize;
+ typedef ZyanI64 ZyanISize;
+ typedef ZyanU64 ZyanUPointer;
+ typedef ZyanI64 ZyanIPointer;
+# else
+ typedef ZyanU32 ZyanUSize;
+ typedef ZyanI32 ZyanISize;
+ typedef ZyanU32 ZyanUPointer;
+ typedef ZyanI32 ZyanIPointer;
+# endif
+# elif defined(ZYAN_GNUC)
+ typedef __UINT8_TYPE__ ZyanU8;
+ typedef __UINT16_TYPE__ ZyanU16;
+ typedef __UINT32_TYPE__ ZyanU32;
+ typedef __UINT64_TYPE__ ZyanU64;
+ typedef __INT8_TYPE__ ZyanI8;
+ typedef __INT16_TYPE__ ZyanI16;
+ typedef __INT32_TYPE__ ZyanI32;
+ typedef __INT64_TYPE__ ZyanI64;
+ typedef __SIZE_TYPE__ ZyanUSize;
+ typedef __PTRDIFF_TYPE__ ZyanISize;
+ typedef __UINTPTR_TYPE__ ZyanUPointer;
+ typedef __INTPTR_TYPE__ ZyanIPointer;
+# else
+# error "Unsupported compiler for no-libc mode."
+# endif
+#else
+ // If is LibC present, we use stdint types.
+# include <stdint.h>
+# include <stddef.h>
+ typedef uint8_t ZyanU8;
+ typedef uint16_t ZyanU16;
+ typedef uint32_t ZyanU32;
+ typedef uint64_t ZyanU64;
+ typedef int8_t ZyanI8;
+ typedef int16_t ZyanI16;
+ typedef int32_t ZyanI32;
+ typedef int64_t ZyanI64;
+ typedef size_t ZyanUSize;
+ typedef ptrdiff_t ZyanISize;
+ typedef uintptr_t ZyanUPointer;
+ typedef intptr_t ZyanIPointer;
+#endif
+
+// Verify size assumptions.
+ZYAN_STATIC_ASSERT(sizeof(ZyanU8 ) == 1 );
+ZYAN_STATIC_ASSERT(sizeof(ZyanU16 ) == 2 );
+ZYAN_STATIC_ASSERT(sizeof(ZyanU32 ) == 4 );
+ZYAN_STATIC_ASSERT(sizeof(ZyanU64 ) == 8 );
+ZYAN_STATIC_ASSERT(sizeof(ZyanI8 ) == 1 );
+ZYAN_STATIC_ASSERT(sizeof(ZyanI16 ) == 2 );
+ZYAN_STATIC_ASSERT(sizeof(ZyanI32 ) == 4 );
+ZYAN_STATIC_ASSERT(sizeof(ZyanI64 ) == 8 );
+ZYAN_STATIC_ASSERT(sizeof(ZyanUSize ) == sizeof(void*)); // TODO: This one is incorrect!
+ZYAN_STATIC_ASSERT(sizeof(ZyanISize ) == sizeof(void*)); // TODO: This one is incorrect!
+ZYAN_STATIC_ASSERT(sizeof(ZyanUPointer) == sizeof(void*));
+ZYAN_STATIC_ASSERT(sizeof(ZyanIPointer) == sizeof(void*));
+
+// Verify signedness assumptions (relies on size checks above).
+ZYAN_STATIC_ASSERT((ZyanI8 )-1 >> 1 < (ZyanI8 )((ZyanU8 )-1 >> 1));
+ZYAN_STATIC_ASSERT((ZyanI16)-1 >> 1 < (ZyanI16)((ZyanU16)-1 >> 1));
+ZYAN_STATIC_ASSERT((ZyanI32)-1 >> 1 < (ZyanI32)((ZyanU32)-1 >> 1));
+ZYAN_STATIC_ASSERT((ZyanI64)-1 >> 1 < (ZyanI64)((ZyanU64)-1 >> 1));
+
+/* ============================================================================================== */
+/* Pointer */
+/* ============================================================================================== */
+
+/**
+ * Defines the `ZyanVoidPointer` data-type.
+ */
+typedef char* ZyanVoidPointer;
+
+/**
+ * Defines the `ZyanConstVoidPointer` data-type.
+ */
+typedef const void* ZyanConstVoidPointer;
+
+#define ZYAN_NULL ((void*)0)
+
+/* ============================================================================================== */
+/* Logic types */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Boolean */
+/* ---------------------------------------------------------------------------------------------- */
+
+#define ZYAN_FALSE 0
+#define ZYAN_TRUE 1
+
+/**
+ * Defines the `ZyanBool` data-type.
+ *
+ * Represents a default boolean data-type where `0` is interpreted as `false` and all other values
+ * as `true`.
+ */
+typedef ZyanU8 ZyanBool;
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Ternary */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Defines the `ZyanTernary` data-type.
+ *
+ * The `ZyanTernary` is a balanced ternary type that uses three truth values indicating `true`,
+ * `false` and an indeterminate third value.
+ */
+typedef ZyanI8 ZyanTernary;
+
+#define ZYAN_TERNARY_FALSE (-1)
+#define ZYAN_TERNARY_UNKNOWN 0x00
+#define ZYAN_TERNARY_TRUE 0x01
+
+/* ============================================================================================== */
+/* String types */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* C-style strings */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Defines the `ZyanCharPointer` data-type.
+ *
+ * This type is most often used to represent null-terminated strings aka. C-style strings.
+ */
+typedef char* ZyanCharPointer;
+
+/**
+ * Defines the `ZyanConstCharPointer` data-type.
+ *
+ * This type is most often used to represent null-terminated strings aka. C-style strings.
+ */
+typedef const char* ZyanConstCharPointer;
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+
+#endif /* ZYCORE_TYPES_H */
diff --git a/externals/zycore/include/Zycore/Vector.h b/externals/zycore/include/Zycore/Vector.h
new file mode 100644
index 00000000..47e728cc
--- /dev/null
+++ b/externals/zycore/include/Zycore/Vector.h
@@ -0,0 +1,723 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * Implements the vector container class.
+ */
+
+#ifndef ZYCORE_VECTOR_H
+#define ZYCORE_VECTOR_H
+
+#include <ZycoreExportConfig.h>
+#include <Zycore/Allocator.h>
+#include <Zycore/Comparison.h>
+#include <Zycore/Object.h>
+#include <Zycore/Status.h>
+#include <Zycore/Types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ============================================================================================== */
+/* Constants */
+/* ============================================================================================== */
+
+/**
+ * The initial minimum capacity (number of elements) for all dynamically allocated vector
+ * instances.
+ */
+#define ZYAN_VECTOR_MIN_CAPACITY 1
+
+/**
+ * The default growth factor for all vector instances.
+ */
+#define ZYAN_VECTOR_DEFAULT_GROWTH_FACTOR 2.00f
+
+/**
+ * The default shrink threshold for all vector instances.
+ */
+#define ZYAN_VECTOR_DEFAULT_SHRINK_THRESHOLD 0.25f
+
+/* ============================================================================================== */
+/* Enums and types */
+/* ============================================================================================== */
+
+/**
+ * Defines the `ZyanVector` struct.
+ *
+ * All fields in this struct should be considered as "private". Any changes may lead to unexpected
+ * behavior.
+ */
+typedef struct ZyanVector_
+{
+ /**
+ * The memory allocator.
+ */
+ ZyanAllocator* allocator;
+ /**
+ * The growth factor.
+ */
+ float growth_factor;
+ /**
+ * The shrink threshold.
+ */
+ float shrink_threshold;
+ /**
+ * The current number of elements in the vector.
+ */
+ ZyanUSize size;
+ /**
+ * The maximum capacity (number of elements).
+ */
+ ZyanUSize capacity;
+ /**
+ * The size of a single element in bytes.
+ */
+ ZyanUSize element_size;
+ /**
+ * The element destructor callback.
+ */
+ ZyanMemberProcedure destructor;
+ /**
+ * The data pointer.
+ */
+ void* data;
+} ZyanVector;
+
+/* ============================================================================================== */
+/* Macros */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* General */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Defines an uninitialized `ZyanVector` instance.
+ */
+#define ZYAN_VECTOR_INITIALIZER \
+ { \
+ /* allocator */ ZYAN_NULL, \
+ /* growth_factor */ 0.0f, \
+ /* shrink_threshold */ 0.0f, \
+ /* size */ 0, \
+ /* capacity */ 0, \
+ /* element_size */ 0, \
+ /* destructor */ ZYAN_NULL, \
+ /* data */ ZYAN_NULL \
+ }
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Helper macros */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Returns the value of the element at the given `index`.
+ *
+ * @param type The desired value type.
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param index The element index.
+ *
+ * @result The value of the desired element in the vector.
+ *
+ * Note that this function is unsafe and might dereference a null-pointer.
+ */
+#ifdef __cplusplus
+#define ZYAN_VECTOR_GET(type, vector, index) \
+ (*reinterpret_cast<const type*>(ZyanVectorGet(vector, index)))
+#else
+#define ZYAN_VECTOR_GET(type, vector, index) \
+ (*(const type*)ZyanVectorGet(vector, index))
+#endif
+
+/**
+ * Loops through all elements of the vector.
+ *
+ * @param type The desired value type.
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param item_name The name of the iterator item.
+ * @param body The body to execute for each item in the vector.
+ */
+#define ZYAN_VECTOR_FOREACH(type, vector, item_name, body) \
+ { \
+ const ZyanUSize ZYAN_MACRO_CONCAT_EXPAND(size_d50d3303, item_name) = (vector)->size; \
+ for (ZyanUSize ZYAN_MACRO_CONCAT_EXPAND(i_bfd62679, item_name) = 0; \
+ ZYAN_MACRO_CONCAT_EXPAND(i_bfd62679, item_name) < \
+ ZYAN_MACRO_CONCAT_EXPAND(size_d50d3303, item_name); \
+ ++ZYAN_MACRO_CONCAT_EXPAND(i_bfd62679, item_name)) \
+ { \
+ const type item_name = ZYAN_VECTOR_GET(type, vector, \
+ ZYAN_MACRO_CONCAT_EXPAND(i_bfd62679, item_name)); \
+ body \
+ } \
+ }
+
+/**
+ * Loops through all elements of the vector.
+ *
+ * @param type The desired value type.
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param item_name The name of the iterator item.
+ * @param body The body to execute for each item in the vector.
+ */
+#define ZYAN_VECTOR_FOREACH_MUTABLE(type, vector, item_name, body) \
+ { \
+ const ZyanUSize ZYAN_MACRO_CONCAT_EXPAND(size_d50d3303, item_name) = (vector)->size; \
+ for (ZyanUSize ZYAN_MACRO_CONCAT_EXPAND(i_bfd62679, item_name) = 0; \
+ ZYAN_MACRO_CONCAT_EXPAND(i_bfd62679, item_name) < \
+ ZYAN_MACRO_CONCAT_EXPAND(size_d50d3303, item_name); \
+ ++ZYAN_MACRO_CONCAT_EXPAND(i_bfd62679, item_name)) \
+ { \
+ type* const item_name = ZyanVectorGetMutable(vector, \
+ ZYAN_MACRO_CONCAT_EXPAND(i_bfd62679, item_name)); \
+ body \
+ } \
+ }
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Constructor and destructor */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYAN_NO_LIBC
+
+/**
+ * Initializes the given `ZyanVector` instance.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param element_size The size of a single element in bytes.
+ * @param capacity The initial capacity (number of elements).
+ * @param destructor A destructor callback that is invoked every time an item is deleted, or
+ * `ZYAN_NULL` if not needed.
+ *
+ * @return A zyan status code.
+ *
+ * The memory for the vector elements is dynamically allocated by the default allocator using the
+ * default growth factor of `2.0f` and the default shrink threshold of `0.25f`.
+ *
+ * Finalization with `ZyanVectorDestroy` is required for all instances created by this function.
+ */
+ZYCORE_EXPORT ZYAN_REQUIRES_LIBC ZyanStatus ZyanVectorInit(ZyanVector* vector,
+ ZyanUSize element_size, ZyanUSize capacity, ZyanMemberProcedure destructor);
+
+#endif // ZYAN_NO_LIBC
+
+/**
+ * Initializes the given `ZyanVector` instance and sets a custom `allocator` and memory
+ * allocation/deallocation parameters.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param element_size The size of a single element in bytes.
+ * @param capacity The initial capacity (number of elements).
+ * @param destructor A destructor callback that is invoked every time an item is deleted,
+ * or `ZYAN_NULL` if not needed.
+ * @param allocator A pointer to a `ZyanAllocator` instance.
+ * @param growth_factor The growth factor (from `1.0f` to `x.xf`).
+ * @param shrink_threshold The shrink threshold (from `0.0f` to `1.0f`).
+ *
+ * @return A zyan status code.
+ *
+ * A growth factor of `1.0f` disables overallocation and a shrink threshold of `0.0f` disables
+ * dynamic shrinking.
+ *
+ * Finalization with `ZyanVectorDestroy` is required for all instances created by this function.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorInitEx(ZyanVector* vector, ZyanUSize element_size,
+ ZyanUSize capacity, ZyanMemberProcedure destructor, ZyanAllocator* allocator,
+ float growth_factor, float shrink_threshold);
+
+/**
+ * Initializes the given `ZyanVector` instance and configures it to use a custom user
+ * defined buffer with a fixed size.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param element_size The size of a single element in bytes.
+ * @param buffer A pointer to the buffer that is used as storage for the elements.
+ * @param capacity The maximum capacity (number of elements) of the buffer.
+ * @param destructor A destructor callback that is invoked every time an item is deleted, or
+ * `ZYAN_NULL` if not needed.
+ *
+ * @return A zyan status code.
+ *
+ * Finalization is not required for instances created by this function.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorInitCustomBuffer(ZyanVector* vector, ZyanUSize element_size,
+ void* buffer, ZyanUSize capacity, ZyanMemberProcedure destructor);
+
+/**
+ * Destroys the given `ZyanVector` instance.
+ *
+ * @param vector A pointer to the `ZyanVector` instance..
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorDestroy(ZyanVector* vector);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Duplication */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYAN_NO_LIBC
+
+/**
+ * Initializes a new `ZyanVector` instance by duplicating an existing vector.
+ *
+ * @param destination A pointer to the (uninitialized) destination `ZyanVector` instance.
+ * @param source A pointer to the source vector.
+ * @param capacity The initial capacity (number of elements).
+ *
+ * This value is automatically adjusted to the size of the source vector, if
+ * a smaller value was passed.
+ *
+ * @return A zyan status code.
+ *
+ * The memory for the vector is dynamically allocated by the default allocator using the default
+ * growth factor of `2.0f` and the default shrink threshold of `0.25f`.
+ *
+ * Finalization with `ZyanVectorDestroy` is required for all instances created by this function.
+ */
+ZYCORE_EXPORT ZYAN_REQUIRES_LIBC ZyanStatus ZyanVectorDuplicate(ZyanVector* destination,
+ const ZyanVector* source, ZyanUSize capacity);
+
+#endif // ZYAN_NO_LIBC
+
+/**
+ * Initializes a new `ZyanVector` instance by duplicating an existing vector and sets a
+ * custom `allocator` and memory allocation/deallocation parameters.
+ *
+ * @param destination A pointer to the (uninitialized) destination `ZyanVector` instance.
+ * @param source A pointer to the source vector.
+ * @param capacity The initial capacity (number of elements).
+
+ * This value is automatically adjusted to the size of the source
+ * vector, if a smaller value was passed.
+ * @param allocator A pointer to a `ZyanAllocator` instance.
+ * @param growth_factor The growth factor (from `1.0f` to `x.xf`).
+ * @param shrink_threshold The shrink threshold (from `0.0f` to `1.0f`).
+ *
+ * @return A zyan status code.
+ *
+ * A growth factor of `1.0f` disables overallocation and a shrink threshold of `0.0f` disables
+ * dynamic shrinking.
+ *
+ * Finalization with `ZyanVectorDestroy` is required for all instances created by this function.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorDuplicateEx(ZyanVector* destination, const ZyanVector* source,
+ ZyanUSize capacity, ZyanAllocator* allocator, float growth_factor, float shrink_threshold);
+
+/**
+ * Initializes a new `ZyanVector` instance by duplicating an existing vector and
+ * configures it to use a custom user defined buffer with a fixed size.
+ *
+ * @param destination A pointer to the (uninitialized) destination `ZyanVector` instance.
+ * @param source A pointer to the source vector.
+ * @param buffer A pointer to the buffer that is used as storage for the elements.
+ * @param capacity The maximum capacity (number of elements) of the buffer.
+
+ * This function will fail, if the capacity of the buffer is less than the
+ * size of the source vector.
+ *
+ * @return A zyan status code.
+ *
+ * Finalization is not required for instances created by this function.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorDuplicateCustomBuffer(ZyanVector* destination,
+ const ZyanVector* source, void* buffer, ZyanUSize capacity);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Element access */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Returns a constant pointer to the element at the given `index`.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param index The element index.
+ *
+ * @return A constant pointer to the desired element in the vector or `ZYAN_NULL`, if an error
+ * occured.
+ *
+ * Note that the returned pointer might get invalid when the vector is resized by either a manual
+ * call to the memory-management functions or implicitly by inserting or removing elements.
+ *
+ * Take a look at `ZyanVectorGetPointer` instead, if you need a function that returns a zyan status
+ * code.
+ */
+ZYCORE_EXPORT const void* ZyanVectorGet(const ZyanVector* vector, ZyanUSize index);
+
+/**
+ * Returns a mutable pointer to the element at the given `index`.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param index The element index.
+ *
+ * @return A mutable pointer to the desired element in the vector or `ZYAN_NULL`, if an error
+ * occured.
+ *
+ * Note that the returned pointer might get invalid when the vector is resized by either a manual
+ * call to the memory-management functions or implicitly by inserting or removing elements.
+ *
+ * Take a look at `ZyanVectorGetPointerMutable` instead, if you need a function that returns a
+ * zyan status code.
+ */
+ZYCORE_EXPORT void* ZyanVectorGetMutable(const ZyanVector* vector, ZyanUSize index);
+
+/**
+ * Returns a constant pointer to the element at the given `index`.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param index The element index.
+ * @param value Receives a constant pointer to the desired element in the vector.
+ *
+ * Note that the returned pointer might get invalid when the vector is resized by either a manual
+ * call to the memory-management functions or implicitly by inserting or removing elements.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorGetPointer(const ZyanVector* vector, ZyanUSize index,
+ const void** value);
+
+/**
+ * Returns a mutable pointer to the element at the given `index`.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param index The element index.
+ * @param value Receives a mutable pointer to the desired element in the vector.
+ *
+ * Note that the returned pointer might get invalid when the vector is resized by either a manual
+ * call to the memory-management functions or implicitly by inserting or removing elements.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorGetPointerMutable(const ZyanVector* vector, ZyanUSize index,
+ void** value);
+
+/**
+ * Assigns a new value to the element at the given `index`.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param index The value index.
+ * @param value The value to assign.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorSet(ZyanVector* vector, ZyanUSize index,
+ const void* value);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Insertion */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Adds a new `element` to the end of the vector.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param element A pointer to the element to add.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorPushBack(ZyanVector* vector, const void* element);
+
+/**
+ * Inserts an `element` at the given `index` of the vector.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param index The insert index.
+ * @param element A pointer to the element to insert.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorInsert(ZyanVector* vector, ZyanUSize index,
+ const void* element);
+
+/**
+ * Inserts multiple `elements` at the given `index` of the vector.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param index The insert index.
+ * @param elements A pointer to the first element.
+ * @param count The number of elements to insert.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorInsertRange(ZyanVector* vector, ZyanUSize index,
+ const void* elements, ZyanUSize count);
+
+/**
+ * Constructs an `element` in-place at the end of the vector.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param element Receives a pointer to the new element.
+ * @param constructor The constructor callback or `ZYAN_NULL`. The new element will be in
+ * undefined state, if no constructor was passed.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorEmplace(ZyanVector* vector, void** element,
+ ZyanMemberFunction constructor);
+
+/**
+ * Constructs an `element` in-place and inserts it at the given `index` of the vector.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param index The insert index.
+ * @param element Receives a pointer to the new element.
+ * @param constructor The constructor callback or `ZYAN_NULL`. The new element will be in
+ * undefined state, if no constructor was passed.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorEmplaceEx(ZyanVector* vector, ZyanUSize index,
+ void** element, ZyanMemberFunction constructor);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Utils */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Swaps the element at `index_first` with the element at `index_second`.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param index_first The index of the first element.
+ * @param index_second The index of the second element.
+ *
+ * @return A zyan status code.
+ *
+ * This function requires the vector to have spare capacity for one temporary element. Call
+ * `ZyanVectorReserve` before this function to increase capacity, if needed.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorSwapElements(ZyanVector* vector, ZyanUSize index_first,
+ ZyanUSize index_second);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Deletion */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Deletes the element at the given `index` of the vector.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param index The element index.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorDelete(ZyanVector* vector, ZyanUSize index);
+
+/**
+ * Deletes multiple elements from the given vector, starting at `index`.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param index The index of the first element to delete.
+ * @param count The number of elements to delete.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorDeleteRange(ZyanVector* vector, ZyanUSize index,
+ ZyanUSize count);
+
+/**
+ * Removes the last element of the vector.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorPopBack(ZyanVector* vector);
+
+/**
+ * Erases all elements of the given vector.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorClear(ZyanVector* vector);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Searching */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Sequentially searches for the first occurrence of `element` in the given vector.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param element A pointer to the element to search for.
+ * @param found_index A pointer to a variable that receives the index of the found element.
+ * @param comparison The comparison function to use.
+ *
+ * @return `ZYAN_STATUS_TRUE` if the element was found, `ZYAN_STATUS_FALSE` if not or a generic
+ * zyan status code if an error occured.
+ *
+ * The `found_index` is set to `-1`, if the element was not found.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorFind(const ZyanVector* vector, const void* element,
+ ZyanISize* found_index, ZyanEqualityComparison comparison);
+
+/**
+ * Sequentially searches for the first occurrence of `element` in the given vector.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param element A pointer to the element to search for.
+ * @param found_index A pointer to a variable that receives the index of the found element.
+ * @param comparison The comparison function to use.
+ * @param index The start index.
+ * @param count The maximum number of elements to iterate, beginning from the start `index`.
+ *
+ * @return `ZYAN_STATUS_TRUE` if the element was found, `ZYAN_STATUS_FALSE` if not or a generic
+ * zyan status code if an error occured.
+ *
+ * The `found_index` is set to `-1`, if the element was not found.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorFindEx(const ZyanVector* vector, const void* element,
+ ZyanISize* found_index, ZyanEqualityComparison comparison, ZyanUSize index, ZyanUSize count);
+
+/**
+ * Searches for the first occurrence of `element` in the given vector using a binary-
+ * search algorithm.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param element A pointer to the element to search for.
+ * @param found_index A pointer to a variable that receives the index of the found element.
+ * @param comparison The comparison function to use.
+ *
+ * @return `ZYAN_STATUS_TRUE` if the element was found, `ZYAN_STATUS_FALSE` if not or a generic
+ * zyan status code if an error occured.
+ *
+ * If found, `found_index` contains the zero-based index of `element`. If not found, `found_index`
+ * contains the index of the first entry larger than `element`.
+ *
+ * This function requires all elements in the vector to be strictly ordered (sorted).
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorBinarySearch(const ZyanVector* vector, const void* element,
+ ZyanUSize* found_index, ZyanComparison comparison);
+
+/**
+ * Searches for the first occurrence of `element` in the given vector using a binary-
+ * search algorithm.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param element A pointer to the element to search for.
+ * @param found_index A pointer to a variable that receives the index of the found element.
+ * @param comparison The comparison function to use.
+ * @param index The start index.
+ * @param count The maximum number of elements to iterate, beginning from the start `index`.
+ *
+ * @return `ZYAN_STATUS_TRUE` if the element was found, `ZYAN_STATUS_FALSE` if not or a generic
+ * zyan status code if an error occured.
+ *
+ * If found, `found_index` contains the zero-based index of `element`. If not found, `found_index`
+ * contains the index of the first entry larger than `element`.
+ *
+ * This function requires all elements in the vector to be strictly ordered (sorted).
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorBinarySearchEx(const ZyanVector* vector, const void* element,
+ ZyanUSize* found_index, ZyanComparison comparison, ZyanUSize index, ZyanUSize count);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Memory management */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Resizes the given `ZyanVector` instance.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param size The new size of the vector.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorResize(ZyanVector* vector, ZyanUSize size);
+
+/**
+ * Resizes the given `ZyanVector` instance.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param size The new size of the vector.
+ * @param initializer A pointer to a value to be used as initializer for new items.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorResizeEx(ZyanVector* vector, ZyanUSize size,
+ const void* initializer);
+
+/**
+ * Changes the capacity of the given `ZyanVector` instance.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param capacity The new minimum capacity of the vector.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorReserve(ZyanVector* vector, ZyanUSize capacity);
+
+/**
+ * Shrinks the capacity of the given vector to match it's size.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorShrinkToFit(ZyanVector* vector);
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Information */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Returns the current capacity of the vector.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param capacity Receives the size of the vector.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorGetCapacity(const ZyanVector* vector, ZyanUSize* capacity);
+
+/**
+ * Returns the current size of the vector.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param size Receives the size of the vector.
+ *
+ * @return A zyan status code.
+ */
+ZYCORE_EXPORT ZyanStatus ZyanVectorGetSize(const ZyanVector* vector, ZyanUSize* size);
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZYCORE_VECTOR_H */
diff --git a/externals/zycore/include/Zycore/Zycore.h b/externals/zycore/include/Zycore/Zycore.h
new file mode 100644
index 00000000..e136acf5
--- /dev/null
+++ b/externals/zycore/include/Zycore/Zycore.h
@@ -0,0 +1,111 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * Master include file, including everything else.
+ */
+
+#ifndef ZYCORE_H
+#define ZYCORE_H
+
+#include <ZycoreExportConfig.h>
+#include <Zycore/Types.h>
+
+// TODO:
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ============================================================================================== */
+/* Macros */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Constants */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * A macro that defines the zycore version.
+ */
+#define ZYCORE_VERSION (ZyanU64)0x0001000000000000
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Helper macros */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Extracts the major-part of the zycore version.
+ *
+ * @param version The zycore version value
+ */
+#define ZYCORE_VERSION_MAJOR(version) (ZyanU16)((version & 0xFFFF000000000000) >> 48)
+
+/**
+ * Extracts the minor-part of the zycore version.
+ *
+ * @param version The zycore version value
+ */
+#define ZYCORE_VERSION_MINOR(version) (ZyanU16)((version & 0x0000FFFF00000000) >> 32)
+
+/**
+ * Extracts the patch-part of the zycore version.
+ *
+ * @param version The zycore version value
+ */
+#define ZYCORE_VERSION_PATCH(version) (ZyanU16)((version & 0x00000000FFFF0000) >> 16)
+
+/**
+ * Extracts the build-part of the zycore version.
+ *
+ * @param version The zycore version value
+ */
+#define ZYCORE_VERSION_BUILD(version) (ZyanU16)(version & 0x000000000000FFFF)
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/**
+ * Returns the zycore version.
+ *
+ * @return The zycore version.
+ *
+ * Use the macros provided in this file to extract the major, minor, patch and build part from the
+ * returned version value.
+ */
+ZYCORE_EXPORT ZyanU64 ZycoreGetVersion(void);
+
+/* ============================================================================================== */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZYCORE_H */
diff --git a/externals/zycore/resources/VersionInfo.rc b/externals/zycore/resources/VersionInfo.rc
new file mode 100644
index 00000000..ab8c4802
--- /dev/null
+++ b/externals/zycore/resources/VersionInfo.rc
Binary files differ
diff --git a/externals/zycore/src/API/Memory.c b/externals/zycore/src/API/Memory.c
new file mode 100644
index 00000000..490f5cdf
--- /dev/null
+++ b/externals/zycore/src/API/Memory.c
@@ -0,0 +1,128 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+#include <Zycore/API/Memory.h>
+
+#if defined(ZYAN_WINDOWS)
+
+#elif defined(ZYAN_POSIX)
+# include <unistd.h>
+#else
+# error "Unsupported platform detected"
+#endif
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* General */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanU32 ZyanMemoryGetSystemPageSize()
+{
+#if defined(ZYAN_WINDOWS)
+
+ SYSTEM_INFO system_info;
+ GetSystemInfo(&system_info);
+
+ return system_info.dwPageSize;
+
+#elif defined(ZYAN_POSIX)
+
+ return sysconf(_SC_PAGE_SIZE);
+
+#endif
+}
+
+ZyanU32 ZyanMemoryGetSystemAllocationGranularity()
+{
+#if defined(ZYAN_WINDOWS)
+
+ SYSTEM_INFO system_info;
+ GetSystemInfo(&system_info);
+
+ return system_info.dwAllocationGranularity;
+
+#elif defined(ZYAN_POSIX)
+
+ return sysconf(_SC_PAGE_SIZE);
+
+#endif
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Memory management */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanMemoryVirtualProtect(void* address, ZyanUSize size,
+ ZyanMemoryPageProtection protection)
+{
+#if defined(ZYAN_WINDOWS)
+
+ DWORD old;
+ if (!VirtualProtect(address, size, protection, &old))
+ {
+ return ZYAN_STATUS_BAD_SYSTEMCALL;
+ }
+
+#elif defined(ZYAN_POSIX)
+
+ if (mprotect(address, size, protection))
+ {
+ return ZYAN_STATUS_BAD_SYSTEMCALL;
+ }
+
+#endif
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanMemoryVirtualFree(void* address, ZyanUSize size)
+{
+#if defined(ZYAN_WINDOWS)
+
+ ZYAN_UNUSED(size);
+ if (!VirtualFree(address, 0, MEM_RELEASE))
+ {
+ return ZYAN_STATUS_BAD_SYSTEMCALL;
+ }
+
+#elif defined(ZYAN_POSIX)
+
+ if (munmap(address, size))
+ {
+ return ZYAN_STATUS_BAD_SYSTEMCALL;
+ }
+
+#endif
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
diff --git a/externals/zycore/src/API/Process.c b/externals/zycore/src/API/Process.c
new file mode 100644
index 00000000..dbda8d1c
--- /dev/null
+++ b/externals/zycore/src/API/Process.c
@@ -0,0 +1,68 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+#include <Zycore/Defines.h>
+#if defined(ZYAN_WINDOWS)
+# include <windows.h>
+#elif defined(ZYAN_POSIX)
+# include <sys/mman.h>
+#else
+# error "Unsupported platform detected"
+#endif
+#include <Zycore/API/Process.h>
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* General */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanProcessFlushInstructionCache(void* address, ZyanUSize size)
+{
+#if defined(ZYAN_WINDOWS)
+
+ if (!FlushInstructionCache(GetCurrentProcess(), address, size))
+ {
+ return ZYAN_STATUS_BAD_SYSTEMCALL;
+ }
+
+#elif defined(ZYAN_POSIX)
+
+ if (msync(address, size, MS_SYNC | MS_INVALIDATE))
+ {
+ return ZYAN_STATUS_BAD_SYSTEMCALL;
+ }
+
+#endif
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
diff --git a/externals/zycore/src/API/Synchronization.c b/externals/zycore/src/API/Synchronization.c
new file mode 100644
index 00000000..9be79af2
--- /dev/null
+++ b/externals/zycore/src/API/Synchronization.c
@@ -0,0 +1,200 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+#include <Zycore/API/Synchronization.h>
+
+/* ============================================================================================== */
+/* Internal functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* */
+/* ---------------------------------------------------------------------------------------------- */
+
+
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+#if defined(ZYAN_POSIX)
+
+#include <errno.h>
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Critical Section */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanCriticalSectionInitialize(ZyanCriticalSection* critical_section)
+{
+ pthread_mutexattr_t attribute;
+
+ int error = pthread_mutexattr_init(&attribute);
+ if (error != 0)
+ {
+ if (error == ENOMEM)
+ {
+ return ZYAN_STATUS_NOT_ENOUGH_MEMORY;
+ }
+ return ZYAN_STATUS_BAD_SYSTEMCALL;
+ }
+ pthread_mutexattr_settype(&attribute, PTHREAD_MUTEX_RECURSIVE);
+
+ error = pthread_mutex_init(critical_section, &attribute);
+ pthread_mutexattr_destroy(&attribute);
+ if (error != 0)
+ {
+ if (error == EAGAIN)
+ {
+ return ZYAN_STATUS_OUT_OF_RESOURCES;
+ }
+ if (error == ENOMEM)
+ {
+ return ZYAN_STATUS_NOT_ENOUGH_MEMORY;
+ }
+ if (error == EPERM)
+ {
+ return ZYAN_STATUS_ACCESS_DENIED;
+ }
+ if ((error == EBUSY) || (error == EINVAL))
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ return ZYAN_STATUS_BAD_SYSTEMCALL;
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanCriticalSectionEnter(ZyanCriticalSection* critical_section)
+{
+ const int error = pthread_mutex_lock(critical_section);
+ if (error != 0)
+ {
+ if (error == EINVAL)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (error == EAGAIN)
+ {
+ return ZYAN_STATUS_INVALID_OPERATION;
+ }
+ return ZYAN_STATUS_BAD_SYSTEMCALL;
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanBool ZyanCriticalSectionTryEnter(ZyanCriticalSection* critical_section)
+{
+ // No fine grained error handling for this one
+ return pthread_mutex_trylock(critical_section) ? ZYAN_FALSE : ZYAN_TRUE;
+}
+
+ZyanStatus ZyanCriticalSectionLeave(ZyanCriticalSection* critical_section)
+{
+ const int error = pthread_mutex_unlock(critical_section);
+ if (error != 0)
+ {
+ if (error == EINVAL)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (error == EPERM)
+ {
+ return ZYAN_STATUS_INVALID_OPERATION;
+ }
+ return ZYAN_STATUS_BAD_SYSTEMCALL;
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanCriticalSectionDelete(ZyanCriticalSection* critical_section)
+{
+ const int error = pthread_mutex_destroy(critical_section);
+ if (error != 0)
+ {
+ if ((error == EBUSY) || (error == EINVAL))
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ return ZYAN_STATUS_BAD_SYSTEMCALL;
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+#elif defined(ZYAN_WINDOWS)
+
+/* ---------------------------------------------------------------------------------------------- */
+/* General */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanCriticalSectionInitialize(ZyanCriticalSection* critical_section)
+{
+ InitializeCriticalSection(critical_section);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanCriticalSectionEnter(ZyanCriticalSection* critical_section)
+{
+ EnterCriticalSection(critical_section);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanBool ZyanCriticalSectionTryEnter(ZyanCriticalSection* critical_section)
+{
+ return TryEnterCriticalSection(critical_section) ? ZYAN_TRUE : ZYAN_FALSE;
+}
+
+ZyanStatus ZyanCriticalSectionLeave(ZyanCriticalSection* critical_section)
+{
+ LeaveCriticalSection(critical_section);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanCriticalSectionDelete(ZyanCriticalSection* critical_section)
+{
+ DeleteCriticalSection(critical_section);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+#else
+# error "Unsupported platform detected"
+#endif
+
+/* ============================================================================================== */
diff --git a/externals/zycore/src/API/Terminal.c b/externals/zycore/src/API/Terminal.c
new file mode 100644
index 00000000..376d8e12
--- /dev/null
+++ b/externals/zycore/src/API/Terminal.c
@@ -0,0 +1,156 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+#include <Zycore/API/Terminal.h>
+
+#if defined(ZYAN_POSIX)
+# include <unistd.h>
+#elif defined(ZYAN_WINDOWS)
+# include <windows.h>
+# include <io.h>
+#else
+# error "Unsupported platform detected"
+#endif
+
+// Provide fallback for old SDK versions
+#ifdef ZYAN_WINDOWS
+# ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+# define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+# endif
+#endif
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+ZyanStatus ZyanTerminalEnableVT100(ZyanStandardStream stream)
+{
+ if ((stream != ZYAN_STDSTREAM_OUT) && (stream != ZYAN_STDSTREAM_ERR))
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+#ifdef ZYAN_WINDOWS
+ // Get file descriptor
+ int file;
+ switch (stream)
+ {
+ case ZYAN_STDSTREAM_OUT:
+ file = _fileno(ZYAN_STDOUT);
+ break;
+ case ZYAN_STDSTREAM_ERR:
+ file = _fileno(ZYAN_STDERR);
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ if (file < 0)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ HANDLE const handle = (HANDLE)_get_osfhandle(file);
+ if (handle == INVALID_HANDLE_VALUE)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ DWORD mode;
+ if (!GetConsoleMode(handle, &mode))
+ {
+ // The given standard stream is not bound to a terminal
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+ if (!SetConsoleMode(handle, mode))
+ {
+ return ZYAN_STATUS_BAD_SYSTEMCALL;
+ }
+#endif
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanTerminalIsTTY(ZyanStandardStream stream)
+{
+ // Get file descriptor
+ int file;
+#ifdef ZYAN_WINDOWS
+ switch (stream)
+ {
+ case ZYAN_STDSTREAM_IN:
+ file = _fileno(ZYAN_STDIN);
+ break;
+ case ZYAN_STDSTREAM_OUT:
+ file = _fileno(ZYAN_STDOUT);
+ break;
+ case ZYAN_STDSTREAM_ERR:
+ file = _fileno(ZYAN_STDERR);
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ if (file < 0)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+#else
+ switch (stream)
+ {
+ case ZYAN_STDSTREAM_IN:
+ file = STDIN_FILENO;
+ break;
+ case ZYAN_STDSTREAM_OUT:
+ file = STDOUT_FILENO;
+ break;
+ case ZYAN_STDSTREAM_ERR:
+ file = STDERR_FILENO;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+#endif
+
+#ifdef ZYAN_WINDOWS
+ if (_isatty(file))
+#else
+ if ( isatty(file))
+#endif
+ {
+ return ZYAN_STATUS_TRUE;
+ }
+ if (ZYAN_ERRNO == EBADF)
+ {
+ // Invalid file descriptor
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ //ZYAN_ASSERT((errno == EINVAL) || (errno == ENOTTY));
+
+ return ZYAN_STATUS_FALSE;
+}
+
+/* ============================================================================================== */
diff --git a/externals/zycore/src/API/Thread.c b/externals/zycore/src/API/Thread.c
new file mode 100644
index 00000000..34158c3f
--- /dev/null
+++ b/externals/zycore/src/API/Thread.c
@@ -0,0 +1,194 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+#include <Zycore/API/Thread.h>
+
+/* ============================================================================================== */
+/* Internal functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* */
+/* ---------------------------------------------------------------------------------------------- */
+
+
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+#if defined(ZYAN_POSIX)
+
+#include <errno.h>
+
+/* ---------------------------------------------------------------------------------------------- */
+/* General */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanThreadGetCurrentThread(ZyanThread* thread)
+{
+ *thread = pthread_self();
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZYAN_STATIC_ASSERT(sizeof(ZyanThreadId) <= sizeof(ZyanU64));
+ZyanStatus ZyanThreadGetCurrentThreadId(ZyanThreadId* thread_id)
+{
+ // TODO: Use `pthread_getthreadid_np` on platforms where it is available
+
+ pthread_t ptid = pthread_self();
+ *thread_id = *(ZyanThreadId*)ptid;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Thread Local Storage */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanThreadTlsAlloc(ZyanThreadTlsIndex* index, ZyanThreadTlsCallback destructor)
+{
+ ZyanThreadTlsIndex value;
+ const int error = pthread_key_create(&value, destructor);
+ if (error != 0)
+ {
+ if (error == EAGAIN)
+ {
+ return ZYAN_STATUS_OUT_OF_RESOURCES;
+ }
+ if (error == ENOMEM)
+ {
+ return ZYAN_STATUS_NOT_ENOUGH_MEMORY;
+ }
+ return ZYAN_STATUS_BAD_SYSTEMCALL;
+ }
+
+ *index = value;
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanThreadTlsFree(ZyanThreadTlsIndex index)
+{
+ return !pthread_key_delete(index) ? ZYAN_STATUS_SUCCESS : ZYAN_STATUS_BAD_SYSTEMCALL;
+}
+
+ZyanStatus ZyanThreadTlsGetValue(ZyanThreadTlsIndex index, void** data)
+{
+ *data = pthread_getspecific(index);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanThreadTlsSetValue(ZyanThreadTlsIndex index, void* data)
+{
+ const int error = pthread_setspecific(index, data);
+ if (error != 0)
+ {
+ if (error == EINVAL)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ return ZYAN_STATUS_BAD_SYSTEMCALL;
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+#elif defined(ZYAN_WINDOWS)
+
+/* ---------------------------------------------------------------------------------------------- */
+/* General */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanThreadGetCurrentThread(ZyanThread* thread)
+{
+ *thread = GetCurrentThread();
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanThreadGetCurrentThreadId(ZyanThreadId* thread_id)
+{
+ *thread_id = GetCurrentThreadId();
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Thread Local Storage (TLS) */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanThreadTlsAlloc(ZyanThreadTlsIndex* index, ZyanThreadTlsCallback destructor)
+{
+ const ZyanThreadTlsIndex value = FlsAlloc(destructor);
+ if (value == FLS_OUT_OF_INDEXES)
+ {
+ return ZYAN_STATUS_OUT_OF_RESOURCES;
+ }
+
+ *index = value;
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanThreadTlsFree(ZyanThreadTlsIndex index)
+{
+ return FlsFree(index) ? ZYAN_STATUS_SUCCESS : ZYAN_STATUS_BAD_SYSTEMCALL;
+}
+
+ZyanStatus ZyanThreadTlsGetValue(ZyanThreadTlsIndex index, void** data)
+{
+ *data = FlsGetValue(index);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanThreadTlsSetValue(ZyanThreadTlsIndex index, void* data)
+{
+ if (!FlsSetValue(index, data))
+ {
+ const DWORD error = GetLastError();
+ if (error == ERROR_INVALID_PARAMETER)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ return ZYAN_STATUS_BAD_SYSTEMCALL;
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+#else
+# error "Unsupported platform detected"
+#endif
+
+/* ============================================================================================== */
diff --git a/externals/zycore/src/Allocator.c b/externals/zycore/src/Allocator.c
new file mode 100644
index 00000000..5fadf647
--- /dev/null
+++ b/externals/zycore/src/Allocator.c
@@ -0,0 +1,134 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+#include <Zycore/Allocator.h>
+#include <Zycore/LibC.h>
+
+/* ============================================================================================== */
+/* Internal functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Default allocator */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYAN_NO_LIBC
+
+static ZyanStatus ZyanAllocatorDefaultAllocate(ZyanAllocator* allocator, void** p,
+ ZyanUSize element_size, ZyanUSize n)
+{
+ ZYAN_ASSERT(allocator);
+ ZYAN_ASSERT(p);
+ ZYAN_ASSERT(element_size);
+ ZYAN_ASSERT(n);
+
+ ZYAN_UNUSED(allocator);
+
+ *p = ZYAN_MALLOC(element_size * n);
+ if (!*p)
+ {
+ return ZYAN_STATUS_NOT_ENOUGH_MEMORY;
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZyanAllocatorDefaultReallocate(ZyanAllocator* allocator, void** p,
+ ZyanUSize element_size, ZyanUSize n)
+{
+ ZYAN_ASSERT(allocator);
+ ZYAN_ASSERT(p);
+ ZYAN_ASSERT(element_size);
+ ZYAN_ASSERT(n);
+
+ ZYAN_UNUSED(allocator);
+
+ void* const x = ZYAN_REALLOC(*p, element_size * n);
+ if (!x)
+ {
+ return ZYAN_STATUS_NOT_ENOUGH_MEMORY;
+ }
+ *p = x;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZyanAllocatorDefaultDeallocate(ZyanAllocator* allocator, void* p,
+ ZyanUSize element_size, ZyanUSize n)
+{
+ ZYAN_ASSERT(allocator);
+ ZYAN_ASSERT(p);
+ ZYAN_ASSERT(element_size);
+ ZYAN_ASSERT(n);
+
+ ZYAN_UNUSED(allocator);
+ ZYAN_UNUSED(element_size);
+ ZYAN_UNUSED(n);
+
+ ZYAN_FREE(p);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+#endif // ZYAN_NO_LIBC
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+ZyanStatus ZyanAllocatorInit(ZyanAllocator* allocator, ZyanAllocatorAllocate allocate,
+ ZyanAllocatorAllocate reallocate, ZyanAllocatorDeallocate deallocate)
+{
+ if (!allocator || !allocate || !reallocate || !deallocate)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ allocator->allocate = allocate;
+ allocator->reallocate = reallocate;
+ allocator->deallocate = deallocate;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+#ifndef ZYAN_NO_LIBC
+
+ZyanAllocator* ZyanAllocatorDefault(void)
+{
+ static ZyanAllocator allocator =
+ {
+ &ZyanAllocatorDefaultAllocate,
+ &ZyanAllocatorDefaultReallocate,
+ &ZyanAllocatorDefaultDeallocate
+ };
+ return &allocator;
+}
+
+#endif
+
+/* ============================================================================================== */
diff --git a/externals/zycore/src/ArgParse.c b/externals/zycore/src/ArgParse.c
new file mode 100644
index 00000000..109cfc8a
--- /dev/null
+++ b/externals/zycore/src/ArgParse.c
@@ -0,0 +1,279 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Joel Hoener
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+#include <Zycore/ArgParse.h>
+#include <Zycore/LibC.h>
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+#ifndef ZYAN_NO_LIBC
+
+ZyanStatus ZyanArgParse(const ZyanArgParseConfig *cfg, ZyanVector* parsed,
+ const char** error_token)
+{
+ return ZyanArgParseEx(cfg, parsed, error_token, ZyanAllocatorDefault());
+}
+
+#endif
+
+ZyanStatus ZyanArgParseEx(const ZyanArgParseConfig *cfg, ZyanVector* parsed,
+ const char** error_token, ZyanAllocator* allocator)
+{
+# define ZYAN_ERR_TOK(tok) if (error_token) { *error_token = tok; }
+
+ ZYAN_ASSERT(cfg);
+ ZYAN_ASSERT(parsed);
+
+ // TODO: Once we have a decent hash map impl, refactor this to use it. The majority of for
+ // loops through the argument list could be avoided.
+
+ if (cfg->min_unnamed_args > cfg->max_unnamed_args)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ // Check argument syntax.
+ for (const ZyanArgParseDefinition* def = cfg->args; def && def->name; ++def)
+ {
+ // TODO: Duplicate check
+
+ if (!def->name)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZyanUSize arg_len = ZYAN_STRLEN(def->name);
+ if (arg_len < 2 || def->name[0] != '-')
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ // Single dash arguments only accept a single char name.
+ if (def->name[1] != '-' && arg_len != 2)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ }
+
+ // Initialize output vector.
+ ZYAN_CHECK(ZyanVectorInitEx(parsed, sizeof(ZyanArgParseArg), cfg->argc, ZYAN_NULL, allocator,
+ ZYAN_VECTOR_DEFAULT_GROWTH_FACTOR, ZYAN_VECTOR_DEFAULT_SHRINK_THRESHOLD));
+
+ ZyanStatus err;
+ ZyanBool accept_dash_args = ZYAN_TRUE;
+ ZyanUSize num_unnamed_args = 0;
+ for (ZyanUSize i = 1; i < cfg->argc; ++i)
+ {
+ const char* cur_arg = cfg->argv[i];
+ ZyanUSize arg_len = ZYAN_STRLEN(cfg->argv[i]);
+
+ // Double-dash argument?
+ if (accept_dash_args && arg_len >= 2 && ZYAN_MEMCMP(cur_arg, "--", 2) == 0)
+ {
+ // GNU style end of argument parsing.
+ if (arg_len == 2)
+ {
+ accept_dash_args = ZYAN_FALSE;
+ }
+ // Regular double-dash argument.
+ else
+ {
+ // Allocate parsed argument struct.
+ ZyanArgParseArg* parsed_arg;
+ ZYAN_CHECK(ZyanVectorEmplace(parsed, (void**)&parsed_arg, ZYAN_NULL));
+ ZYAN_MEMSET(parsed_arg, 0, sizeof(*parsed_arg));
+
+ // Find corresponding argument definition.
+ for (const ZyanArgParseDefinition* def = cfg->args; def && def->name; ++def)
+ {
+ if (ZYAN_STRCMP(def->name, cur_arg) == 0)
+ {
+ parsed_arg->def = def;
+ break;
+ }
+ }
+
+ // Search exhausted & argument not found. RIP.
+ if (!parsed_arg->def)
+ {
+ err = ZYAN_STATUS_ARG_NOT_UNDERSTOOD;
+ ZYAN_ERR_TOK(cur_arg);
+ goto failure;
+ }
+
+ // Does the argument expect a value? If yes, consume next token.
+ if (!parsed_arg->def->boolean)
+ {
+ if (i == cfg->argc - 1)
+ {
+ err = ZYAN_STATUS_ARG_MISSES_VALUE;
+ ZYAN_ERR_TOK(cur_arg);
+ goto failure;
+ }
+ parsed_arg->has_value = ZYAN_TRUE;
+ ZYAN_CHECK(ZyanStringViewInsideBuffer(&parsed_arg->value, cfg->argv[++i]));
+ }
+ }
+
+ // Continue parsing at next token.
+ continue;
+ }
+
+ // Single-dash argument?
+ // TODO: How to deal with just dashes? Current code treats it as unnamed arg.
+ if (accept_dash_args && arg_len > 1 && cur_arg[0] == '-')
+ {
+ // Iterate argument token chars until there are either no more chars left
+ // or we encounter a non-boolean argument, in which case we consume the
+ // remaining chars as its value.
+ for (const char* read_ptr = cur_arg + 1; *read_ptr; ++read_ptr)
+ {
+ // Allocate parsed argument struct.
+ ZyanArgParseArg* parsed_arg;
+ ZYAN_CHECK(ZyanVectorEmplace(parsed, (void**)&parsed_arg, ZYAN_NULL));
+ ZYAN_MEMSET(parsed_arg, 0, sizeof(*parsed_arg));
+
+ // Find corresponding argument definition.
+ for (const ZyanArgParseDefinition* def = cfg->args; def && def->name; ++def)
+ {
+ if (ZYAN_STRLEN(def->name) == 2 &&
+ def->name[0] == '-' &&
+ def->name[1] == *read_ptr)
+ {
+ parsed_arg->def = def;
+ break;
+ }
+ }
+
+ // Search exhausted, no match found?
+ if (!parsed_arg->def)
+ {
+ err = ZYAN_STATUS_ARG_NOT_UNDERSTOOD;
+ ZYAN_ERR_TOK(cur_arg);
+ goto failure;
+ }
+
+ // Requires value?
+ if (!parsed_arg->def->boolean)
+ {
+ // If there are chars left, consume them (e.g. `-n1000`).
+ if (read_ptr[1])
+ {
+ parsed_arg->has_value = ZYAN_TRUE;
+ ZYAN_CHECK(ZyanStringViewInsideBuffer(&parsed_arg->value, read_ptr + 1));
+ }
+ // If not, consume next token (e.g. `-n 1000`).
+ else
+ {
+ if (i == cfg->argc - 1)
+ {
+ err = ZYAN_STATUS_ARG_MISSES_VALUE;
+ ZYAN_ERR_TOK(cur_arg)
+ goto failure;
+ }
+
+ parsed_arg->has_value = ZYAN_TRUE;
+ ZYAN_CHECK(ZyanStringViewInsideBuffer(&parsed_arg->value, cfg->argv[++i]));
+ }
+
+ // Either way, continue with next argument.
+ goto continue_main_loop;
+ }
+ }
+ }
+
+ // Still here? We're looking at an unnamed argument.
+ ++num_unnamed_args;
+ if (num_unnamed_args > cfg->max_unnamed_args)
+ {
+ err = ZYAN_STATUS_TOO_MANY_ARGS;
+ ZYAN_ERR_TOK(cur_arg);
+ goto failure;
+ }
+
+ // Allocate parsed argument struct.
+ ZyanArgParseArg* parsed_arg;
+ ZYAN_CHECK(ZyanVectorEmplace(parsed, (void**)&parsed_arg, ZYAN_NULL));
+ ZYAN_MEMSET(parsed_arg, 0, sizeof(*parsed_arg));
+ parsed_arg->has_value = ZYAN_TRUE;
+ ZYAN_CHECK(ZyanStringViewInsideBuffer(&parsed_arg->value, cur_arg));
+
+ continue_main_loop:;
+ }
+
+ // All tokens processed. Do we have enough unnamed arguments?
+ if (num_unnamed_args < cfg->min_unnamed_args)
+ {
+ err = ZYAN_STATUS_TOO_FEW_ARGS;
+ // No sensible error token for this error type.
+ goto failure;
+ }
+
+ // Check whether all required arguments are present.
+ ZyanUSize num_parsed_args;
+ ZYAN_CHECK(ZyanVectorGetSize(parsed, &num_parsed_args));
+ for (const ZyanArgParseDefinition* def = cfg->args; def && def->name; ++def)
+ {
+ if (!def->required) continue;
+
+ ZyanBool arg_found = ZYAN_FALSE;
+ for (ZyanUSize i = 0; i < num_parsed_args; ++i)
+ {
+ const ZyanArgParseArg* arg = ZYAN_NULL;
+ ZYAN_CHECK(ZyanVectorGetPointer(parsed, i, (const void**)&arg));
+
+ // Skip unnamed args.
+ if (!arg->def) continue;
+
+ if (arg->def == def)
+ {
+ arg_found = ZYAN_TRUE;
+ break;
+ }
+ }
+
+ if (!arg_found)
+ {
+ err = ZYAN_STATUS_REQUIRED_ARG_MISSING;
+ ZYAN_ERR_TOK(def->name);
+ goto failure;
+ }
+ }
+
+ // Yay!
+ ZYAN_ERR_TOK(ZYAN_NULL);
+ return ZYAN_STATUS_SUCCESS;
+
+failure:
+ ZYAN_CHECK(ZyanVectorDestroy(parsed));
+ return err;
+
+# undef ZYAN_ERR_TOK
+}
+
+/* ============================================================================================== */
diff --git a/externals/zycore/src/Bitset.c b/externals/zycore/src/Bitset.c
new file mode 100644
index 00000000..289aa69d
--- /dev/null
+++ b/externals/zycore/src/Bitset.c
@@ -0,0 +1,670 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+#include <Zycore/Bitset.h>
+#include <Zycore/LibC.h>
+
+/* ============================================================================================== */
+/* Internal constants */
+/* ============================================================================================== */
+
+#define ZYAN_BITSET_GROWTH_FACTOR 2.00f
+#define ZYAN_BITSET_SHRINK_THRESHOLD 0.50f
+
+/* ============================================================================================== */
+/* Internal macros */
+/* ============================================================================================== */
+
+/**
+ * Computes the smallest integer value not less than `x`.
+ *
+ * @param x The value.
+ *
+ * @return The smallest integer value not less than `x`.
+ */
+#define ZYAN_BITSET_CEIL(x) \
+ (((x) == ((ZyanU32)(x))) ? (ZyanU32)(x) : ((ZyanU32)(x)) + 1)
+
+/**
+ * Converts bits to bytes.
+ *
+ * @param x The value in bits.
+ *
+ * @return The amount of bytes needed to fit `x` bits.
+ */
+#define ZYAN_BITSET_BITS_TO_BYTES(x) \
+ ZYAN_BITSET_CEIL((x) / 8.0f)
+
+/**
+ * Returns the offset of the given bit.
+ *
+ * @param index The bit index.
+ *
+ * @return The offset of the given bit.
+ */
+#define ZYAN_BITSET_BIT_OFFSET(index) \
+ (7 - ((index) % 8))
+
+/* ============================================================================================== */
+/* Internal functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Helper functions */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Initializes the given `vector` with `count` "zero"-bytes.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param count The number of bytes.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus ZyanBitsetInitVectorElements(ZyanVector* vector, ZyanUSize count)
+{
+ ZYAN_ASSERT(vector);
+
+ static const ZyanU8 zero = 0;
+ for (ZyanUSize i = 0; i < count; ++i)
+ {
+ ZYAN_CHECK(ZyanVectorPushBack(vector, &zero));
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Byte operations */
+/* ---------------------------------------------------------------------------------------------- */
+
+static ZyanStatus ZyanBitsetOperationAND(ZyanU8* b1, const ZyanU8* b2)
+{
+ *b1 &= *b2;
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZyanBitsetOperationOR (ZyanU8* b1, const ZyanU8* b2)
+{
+ *b1 |= *b2;
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZyanBitsetOperationXOR(ZyanU8* b1, const ZyanU8* b2)
+{
+ *b1 ^= *b2;
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Constructor and destructor */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYAN_NO_LIBC
+
+ZyanStatus ZyanBitsetInit(ZyanBitset* bitset, ZyanUSize count)
+{
+ return ZyanBitsetInitEx(bitset, count, ZyanAllocatorDefault(), ZYAN_BITSET_GROWTH_FACTOR,
+ ZYAN_BITSET_SHRINK_THRESHOLD);
+}
+
+#endif // ZYAN_NO_LIBC
+
+ZyanStatus ZyanBitsetInitEx(ZyanBitset* bitset, ZyanUSize count, ZyanAllocator* allocator,
+ float growth_factor, float shrink_threshold)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ const ZyanU32 bytes = ZYAN_BITSET_BITS_TO_BYTES(count);
+
+ bitset->size = count;
+ ZYAN_CHECK(ZyanVectorInitEx(&bitset->bits, sizeof(ZyanU8), bytes, ZYAN_NULL, allocator,
+ growth_factor, shrink_threshold));
+ ZYAN_CHECK(ZyanBitsetInitVectorElements(&bitset->bits, bytes));
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanBitsetInitBuffer(ZyanBitset* bitset, ZyanUSize count, void* buffer,
+ ZyanUSize capacity)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ const ZyanU32 bytes = ZYAN_BITSET_BITS_TO_BYTES(count);
+ if (capacity < bytes)
+ {
+ return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
+ }
+
+ bitset->size = count;
+ ZYAN_CHECK(ZyanVectorInitCustomBuffer(&bitset->bits, sizeof(ZyanU8), buffer, capacity,
+ ZYAN_NULL));
+ ZYAN_CHECK(ZyanBitsetInitVectorElements(&bitset->bits, bytes));
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanBitsetDestroy(ZyanBitset* bitset)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ZyanVectorDestroy(&bitset->bits);
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Logical operations */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanBitsetPerformByteOperation(ZyanBitset* destination, const ZyanBitset* source,
+ ZyanBitsetByteOperation operation)
+{
+ if (!destination || !source || !operation)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZyanUSize s1;
+ ZyanUSize s2;
+ ZYAN_CHECK(ZyanVectorGetSize(&destination->bits, &s1));
+ ZYAN_CHECK(ZyanVectorGetSize(&source->bits, &s2));
+
+ const ZyanUSize min = ZYAN_MIN(s1, s2);
+ for (ZyanUSize i = 0; i < min; ++i)
+ {
+ ZyanU8* v1;
+ const ZyanU8* v2;
+ ZYAN_CHECK(ZyanVectorGetPointerMutable(&destination->bits, i, (void**)&v1));
+ ZYAN_CHECK(ZyanVectorGetPointer(&source->bits, i, (const void**)&v2));
+
+ ZYAN_ASSERT(v1);
+ ZYAN_ASSERT(v2);
+
+ ZYAN_CHECK(operation(v1, v2));
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanBitsetAND(ZyanBitset* destination, const ZyanBitset* source)
+{
+ return ZyanBitsetPerformByteOperation(destination, source, ZyanBitsetOperationAND);
+}
+
+ZyanStatus ZyanBitsetOR (ZyanBitset* destination, const ZyanBitset* source)
+{
+ return ZyanBitsetPerformByteOperation(destination, source, ZyanBitsetOperationOR );
+}
+
+ZyanStatus ZyanBitsetXOR(ZyanBitset* destination, const ZyanBitset* source)
+{
+ return ZyanBitsetPerformByteOperation(destination, source, ZyanBitsetOperationXOR);
+}
+
+ZyanStatus ZyanBitsetFlip(ZyanBitset* bitset)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZyanUSize size;
+ ZYAN_CHECK(ZyanVectorGetSize(&bitset->bits, &size));
+ for (ZyanUSize i = 0; i < size; ++i)
+ {
+ ZyanU8* value;
+ ZYAN_CHECK(ZyanVectorGetPointerMutable(&bitset->bits, i, (void**)&value));
+ *value = ~(*value);
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Bit access */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanBitsetSet(ZyanBitset* bitset, ZyanUSize index)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (index >= bitset->size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ ZyanU8* value;
+ ZYAN_CHECK(ZyanVectorGetPointerMutable(&bitset->bits, index / 8, (void**)&value));
+
+ *value |= (1 << ZYAN_BITSET_BIT_OFFSET(index));
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanBitsetReset(ZyanBitset* bitset, ZyanUSize index)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (index >= bitset->size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ ZyanU8* value;
+ ZYAN_CHECK(ZyanVectorGetPointerMutable(&bitset->bits, index / 8, (void**)&value));
+ *value &= ~(1 << ZYAN_BITSET_BIT_OFFSET(index));
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanBitsetAssign(ZyanBitset* bitset, ZyanUSize index, ZyanBool value)
+{
+ if (value)
+ {
+ return ZyanBitsetSet(bitset, index);
+ }
+ return ZyanBitsetReset(bitset, index);
+}
+
+ZyanStatus ZyanBitsetToggle(ZyanBitset* bitset, ZyanUSize index)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (index >= bitset->size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ ZyanU8* value;
+ ZYAN_CHECK(ZyanVectorGetPointerMutable(&bitset->bits, index / 8, (void**)&value));
+ *value ^= (1 << ZYAN_BITSET_BIT_OFFSET(index));
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanBitsetTest(ZyanBitset* bitset, ZyanUSize index)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (index >= bitset->size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ const ZyanU8* value;
+ ZYAN_CHECK(ZyanVectorGetPointer(&bitset->bits, index / 8, (const void**)&value));
+ if ((*value & (1 << ZYAN_BITSET_BIT_OFFSET(index))) == 0)
+ {
+ return ZYAN_STATUS_FALSE;
+ }
+ return ZYAN_STATUS_TRUE;
+}
+
+ZyanStatus ZyanBitsetTestMSB(ZyanBitset* bitset)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ZyanBitsetTest(bitset, bitset->size - 1);
+}
+
+ZyanStatus ZyanBitsetTestLSB(ZyanBitset* bitset)
+{
+ return ZyanBitsetTest(bitset, 0);
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanBitsetSetAll(ZyanBitset* bitset)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZyanUSize size;
+ ZYAN_CHECK(ZyanVectorGetSize(&bitset->bits, &size));
+ for (ZyanUSize i = 0; i < size; ++i)
+ {
+ ZyanU8* value;
+ ZYAN_CHECK(ZyanVectorGetPointerMutable(&bitset->bits, i, (void**)&value));
+ *value = 0xFF;
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanBitsetResetAll(ZyanBitset* bitset)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZyanUSize size;
+ ZYAN_CHECK(ZyanVectorGetSize(&bitset->bits, &size));
+ for (ZyanUSize i = 0; i < size; ++i)
+ {
+ ZyanU8* value;
+ ZYAN_CHECK(ZyanVectorGetPointerMutable(&bitset->bits, i, (void**)&value));
+ *value = 0x00;
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Size management */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanBitsetPush(ZyanBitset* bitset, ZyanBool value)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ if ((bitset->size++ % 8) == 0)
+ {
+ static const ZyanU8 zero = 0;
+ ZYAN_CHECK(ZyanVectorPushBack(&bitset->bits, &zero));
+ }
+
+ return ZyanBitsetAssign(bitset, bitset->size - 1, value);
+}
+
+ZyanStatus ZyanBitsetPop(ZyanBitset* bitset)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ if ((--bitset->size % 8) == 0)
+ {
+ return ZyanVectorPopBack(&bitset->bits);
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanBitsetClear(ZyanBitset* bitset)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ bitset->size = 0;
+ return ZyanVectorClear(&bitset->bits);
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Memory management */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanBitsetReserve(ZyanBitset* bitset, ZyanUSize count)
+{
+ return ZyanVectorReserve(&bitset->bits, ZYAN_BITSET_BITS_TO_BYTES(count));
+}
+
+ZyanStatus ZyanBitsetShrinkToFit(ZyanBitset* bitset)
+{
+ return ZyanVectorShrinkToFit(&bitset->bits);
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Information */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanBitsetGetSize(const ZyanBitset* bitset, ZyanUSize* size)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ *size = bitset->size;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanBitsetGetCapacity(const ZyanBitset* bitset, ZyanUSize* capacity)
+{
+ ZYAN_CHECK(ZyanBitsetGetCapacityBytes(bitset, capacity));
+ *capacity *= 8;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanBitsetGetSizeBytes(const ZyanBitset* bitset, ZyanUSize* size)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ZyanVectorGetSize(&bitset->bits, size);
+}
+
+ZyanStatus ZyanBitsetGetCapacityBytes(const ZyanBitset* bitset, ZyanUSize* capacity)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ZyanVectorGetCapacity(&bitset->bits, capacity);
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanBitsetCount(const ZyanBitset* bitset, ZyanUSize* count)
+{
+ if (!bitset || !count)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ *count = 0;
+
+ ZyanUSize size;
+ ZYAN_CHECK(ZyanVectorGetSize(&bitset->bits, &size));
+ for (ZyanUSize i = 0; i < size; ++i)
+ {
+ ZyanU8* value;
+ ZYAN_CHECK(ZyanVectorGetPointer(&bitset->bits, i, (const void**)&value));
+
+ ZyanU8 popcnt = *value;
+ popcnt = (popcnt & 0x55) + ((popcnt >> 1) & 0x55);
+ popcnt = (popcnt & 0x33) + ((popcnt >> 2) & 0x33);
+ popcnt = (popcnt & 0x0F) + ((popcnt >> 4) & 0x0F);
+
+ *count += popcnt;
+ }
+
+ *count = ZYAN_MIN(*count, bitset->size);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanBitsetAll(const ZyanBitset* bitset)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZyanUSize size;
+ ZYAN_CHECK(ZyanVectorGetSize(&bitset->bits, &size));
+ for (ZyanUSize i = 0; i < size; ++i)
+ {
+ ZyanU8* value;
+ ZYAN_CHECK(ZyanVectorGetPointer(&bitset->bits, i, (const void**)&value));
+ if (i < (size - 1))
+ {
+ if (*value != 0xFF)
+ {
+ return ZYAN_STATUS_FALSE;
+ }
+ } else
+ {
+ const ZyanU8 mask = ~(8 - (bitset->size % 8));
+ if ((*value & mask) != mask)
+ {
+ return ZYAN_STATUS_FALSE;
+ }
+ }
+ }
+
+ return ZYAN_STATUS_TRUE;
+}
+
+ZyanStatus ZyanBitsetAny(const ZyanBitset* bitset)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZyanUSize size;
+ ZYAN_CHECK(ZyanVectorGetSize(&bitset->bits, &size));
+ for (ZyanUSize i = 0; i < size; ++i)
+ {
+ ZyanU8* value;
+ ZYAN_CHECK(ZyanVectorGetPointer(&bitset->bits, i, (const void**)&value));
+ if (i < (size - 1))
+ {
+ if (*value != 0x00)
+ {
+ return ZYAN_STATUS_TRUE;
+ }
+ } else
+ {
+ const ZyanU8 mask = ~(8 - (bitset->size % 8));
+ if ((*value & mask) != 0x00)
+ {
+ return ZYAN_STATUS_TRUE;
+ }
+ }
+ }
+
+ return ZYAN_STATUS_FALSE;
+}
+
+ZyanStatus ZyanBitsetNone(const ZyanBitset* bitset)
+{
+ if (!bitset)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZyanUSize size;
+ ZYAN_CHECK(ZyanVectorGetSize(&bitset->bits, &size));
+ for (ZyanUSize i = 0; i < size; ++i)
+ {
+ ZyanU8* value;
+ ZYAN_CHECK(ZyanVectorGetPointer(&bitset->bits, i, (const void**)&value));
+ if (i < (size - 1))
+ {
+ if (*value != 0x00)
+ {
+ return ZYAN_STATUS_FALSE;
+ }
+ } else
+ {
+ const ZyanU8 mask = ~(8 - (bitset->size % 8));
+ if ((*value & mask) != 0x00)
+ {
+ return ZYAN_STATUS_FALSE;
+ }
+ }
+ }
+
+ return ZYAN_STATUS_TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+//ZyanStatus ZyanBitsetToU32(const ZyanBitset* bitset, ZyanU32* value)
+//{
+// if (!bitset)
+// {
+// return ZYAN_STATUS_INVALID_ARGUMENT;
+// }
+// if (bitset->size > 32)
+// {
+// return ZYAN_STATUS_INVALID_OPERATION;
+// }
+//
+// // TODO:
+//
+// return ZYAN_STATUS_SUCCESS;
+//}
+//
+//ZyanStatus ZyanBitsetToU64(const ZyanBitset* bitset, ZyanU64* value)
+//{
+// if (!bitset)
+// {
+// return ZYAN_STATUS_INVALID_ARGUMENT;
+// }
+// if (bitset->size > 64)
+// {
+// return ZYAN_STATUS_INVALID_OPERATION;
+// }
+//
+// // TODO:
+//
+// return ZYAN_STATUS_SUCCESS;
+//}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
diff --git a/externals/zycore/src/Format.c b/externals/zycore/src/Format.c
new file mode 100644
index 00000000..d187d2a2
--- /dev/null
+++ b/externals/zycore/src/Format.c
@@ -0,0 +1,507 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+#include <Zycore/Format.h>
+#include <Zycore/LibC.h>
+
+/* ============================================================================================== */
+/* Constants */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Defines */
+/* ---------------------------------------------------------------------------------------------- */
+
+#define ZYCORE_MAXCHARS_DEC_32 10
+#define ZYCORE_MAXCHARS_DEC_64 20
+#define ZYCORE_MAXCHARS_HEX_32 8
+#define ZYCORE_MAXCHARS_HEX_64 16
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Lookup Tables */
+/* ---------------------------------------------------------------------------------------------- */
+
+static const char* const DECIMAL_LOOKUP =
+ "00010203040506070809"
+ "10111213141516171819"
+ "20212223242526272829"
+ "30313233343536373839"
+ "40414243444546474849"
+ "50515253545556575859"
+ "60616263646566676869"
+ "70717273747576777879"
+ "80818283848586878889"
+ "90919293949596979899";
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Static strings */
+/* ---------------------------------------------------------------------------------------------- */
+
+static const ZyanStringView STR_ADD = ZYAN_DEFINE_STRING_VIEW("+");
+static const ZyanStringView STR_SUB = ZYAN_DEFINE_STRING_VIEW("-");
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Internal macros */
+/* ============================================================================================== */
+
+/**
+ * Writes a terminating '\0' character at the end of the string data.
+ */
+#define ZYCORE_STRING_NULLTERMINATE(string) \
+ *(char*)((ZyanU8*)(string)->vector.data + (string)->vector.size - 1) = '\0';
+
+/* ============================================================================================== */
+/* Internal functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Decimal */
+/* ---------------------------------------------------------------------------------------------- */
+
+#if defined(ZYAN_X86) || defined(ZYAN_ARM) || defined(ZYAN_EMSCRIPTEN)
+ZyanStatus ZyanStringAppendDecU32(ZyanString* string, ZyanU32 value, ZyanU8 padding_length)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ char buffer[ZYCORE_MAXCHARS_DEC_32];
+ char *buffer_end = &buffer[ZYCORE_MAXCHARS_DEC_32];
+ char *buffer_write_pointer = buffer_end;
+ while (value >= 100)
+ {
+ const ZyanU32 value_old = value;
+ buffer_write_pointer -= 2;
+ value /= 100;
+ ZYAN_MEMCPY(buffer_write_pointer, &DECIMAL_LOOKUP[(value_old - (value * 100)) * 2], 2);
+ }
+ buffer_write_pointer -= 2;
+ ZYAN_MEMCPY(buffer_write_pointer, &DECIMAL_LOOKUP[value * 2], 2);
+
+ const ZyanUSize offset_odd = (ZyanUSize)(value < 10);
+ const ZyanUSize length_number = buffer_end - buffer_write_pointer - offset_odd;
+ const ZyanUSize length_total = ZYAN_MAX(length_number, padding_length);
+ const ZyanUSize length_target = string->vector.size;
+
+ if (string->vector.size + length_total > string->vector.capacity)
+ {
+ ZYAN_CHECK(ZyanStringResize(string, string->vector.size + length_total - 1));
+ }
+
+ ZyanUSize offset_write = 0;
+ if (padding_length > length_number)
+ {
+ offset_write = padding_length - length_number;
+ ZYAN_MEMSET((char*)string->vector.data + length_target - 1, '0', offset_write);
+ }
+
+ ZYAN_MEMCPY((char*)string->vector.data + length_target + offset_write - 1,
+ buffer_write_pointer + offset_odd, length_number);
+ string->vector.size = length_target + length_total;
+ ZYCORE_STRING_NULLTERMINATE(string);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+#endif
+
+ZyanStatus ZyanStringAppendDecU64(ZyanString* string, ZyanU64 value, ZyanU8 padding_length)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ char buffer[ZYCORE_MAXCHARS_DEC_64];
+ char *buffer_end = &buffer[ZYCORE_MAXCHARS_DEC_64];
+ char *buffer_write_pointer = buffer_end;
+ while (value >= 100)
+ {
+ const ZyanU64 value_old = value;
+ buffer_write_pointer -= 2;
+ value /= 100;
+ ZYAN_MEMCPY(buffer_write_pointer, &DECIMAL_LOOKUP[(value_old - (value * 100)) * 2], 2);
+ }
+ buffer_write_pointer -= 2;
+ ZYAN_MEMCPY(buffer_write_pointer, &DECIMAL_LOOKUP[value * 2], 2);
+
+ const ZyanUSize offset_odd = (ZyanUSize)(value < 10);
+ const ZyanUSize length_number = buffer_end - buffer_write_pointer - offset_odd;
+ const ZyanUSize length_total = ZYAN_MAX(length_number, padding_length);
+ const ZyanUSize length_target = string->vector.size;
+
+ if (string->vector.size + length_total > string->vector.capacity)
+ {
+ ZYAN_CHECK(ZyanStringResize(string, string->vector.size + length_total - 1));
+ }
+
+ ZyanUSize offset_write = 0;
+ if (padding_length > length_number)
+ {
+ offset_write = padding_length - length_number;
+ ZYAN_MEMSET((char*)string->vector.data + length_target - 1, '0', offset_write);
+ }
+
+ ZYAN_MEMCPY((char*)string->vector.data + length_target + offset_write - 1,
+ buffer_write_pointer + offset_odd, length_number);
+ string->vector.size = length_target + length_total;
+ ZYCORE_STRING_NULLTERMINATE(string);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Hexadecimal */
+/* ---------------------------------------------------------------------------------------------- */
+
+#if defined(ZYAN_X86) || defined(ZYAN_ARM) || defined(ZYAN_EMSCRIPTEN)
+ZyanStatus ZyanStringAppendHexU32(ZyanString* string, ZyanU32 value, ZyanU8 padding_length,
+ ZyanBool uppercase)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ const ZyanUSize len = string->vector.size;
+ ZyanUSize remaining = string->vector.capacity - string->vector.size;
+
+ if (remaining < (ZyanUSize)padding_length)
+ {
+ ZYAN_CHECK(ZyanStringResize(string, len + padding_length - 1));
+ remaining = padding_length;
+ }
+
+ if (!value)
+ {
+ const ZyanU8 n = (padding_length ? padding_length : 1);
+
+ if (remaining < (ZyanUSize)n)
+ {
+ ZYAN_CHECK(ZyanStringResize(string, string->vector.size + n - 1));
+ }
+
+ ZYAN_MEMSET((char*)string->vector.data + len - 1, '0', n);
+ string->vector.size = len + n;
+ ZYCORE_STRING_NULLTERMINATE(string);
+
+ return ZYAN_STATUS_SUCCESS;
+ }
+
+ ZyanU8 n = 0;
+ char* buffer = ZYAN_NULL;
+ for (ZyanI8 i = ZYCORE_MAXCHARS_HEX_32 - 1; i >= 0; --i)
+ {
+ const ZyanU8 v = (value >> i * 4) & 0x0F;
+ if (!n)
+ {
+ if (!v)
+ {
+ continue;
+ }
+ if (remaining <= (ZyanU8)i)
+ {
+ ZYAN_CHECK(ZyanStringResize(string, string->vector.size + i));
+ }
+ buffer = (char*)string->vector.data + len - 1;
+ if (padding_length > i)
+ {
+ n = padding_length - i - 1;
+ ZYAN_MEMSET(buffer, '0', n);
+ }
+ }
+ ZYAN_ASSERT(buffer);
+ if (uppercase)
+ {
+ buffer[n++] = "0123456789ABCDEF"[v];
+ } else
+ {
+ buffer[n++] = "0123456789abcdef"[v];
+ }
+ }
+ string->vector.size = len + n;
+ ZYCORE_STRING_NULLTERMINATE(string);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+#endif
+
+ZyanStatus ZyanStringAppendHexU64(ZyanString* string, ZyanU64 value, ZyanU8 padding_length,
+ ZyanBool uppercase)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ const ZyanUSize len = string->vector.size;
+ ZyanUSize remaining = string->vector.capacity - string->vector.size;
+
+ if (remaining < (ZyanUSize)padding_length)
+ {
+ ZYAN_CHECK(ZyanStringResize(string, len + padding_length - 1));
+ remaining = padding_length;
+ }
+
+ if (!value)
+ {
+ const ZyanU8 n = (padding_length ? padding_length : 1);
+
+ if (remaining < (ZyanUSize)n)
+ {
+ ZYAN_CHECK(ZyanStringResize(string, string->vector.size + n - 1));
+ }
+
+ ZYAN_MEMSET((char*)string->vector.data + len - 1, '0', n);
+ string->vector.size = len + n;
+ ZYCORE_STRING_NULLTERMINATE(string);
+
+ return ZYAN_STATUS_SUCCESS;
+ }
+
+ ZyanU8 n = 0;
+ char* buffer = ZYAN_NULL;
+ for (ZyanI8 i = ((value & 0xFFFFFFFF00000000) ?
+ ZYCORE_MAXCHARS_HEX_64 : ZYCORE_MAXCHARS_HEX_32) - 1; i >= 0; --i)
+ {
+ const ZyanU8 v = (value >> i * 4) & 0x0F;
+ if (!n)
+ {
+ if (!v)
+ {
+ continue;
+ }
+ if (remaining <= (ZyanU8)i)
+ {
+ ZYAN_CHECK(ZyanStringResize(string, string->vector.size + i));
+ }
+ buffer = (char*)string->vector.data + len - 1;
+ if (padding_length > i)
+ {
+ n = padding_length - i - 1;
+ ZYAN_MEMSET(buffer, '0', n);
+ }
+ }
+ ZYAN_ASSERT(buffer);
+ if (uppercase)
+ {
+ buffer[n++] = "0123456789ABCDEF"[v];
+ } else
+ {
+ buffer[n++] = "0123456789abcdef"[v];
+ }
+ }
+ string->vector.size = len + n;
+ ZYCORE_STRING_NULLTERMINATE(string);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Insertion */
+/* ---------------------------------------------------------------------------------------------- */
+
+//ZyanStatus ZyanStringInsertFormat(ZyanString* string, ZyanUSize index, const char* format, ...)
+//{
+//
+//}
+//
+///* ---------------------------------------------------------------------------------------------- */
+//
+//ZyanStatus ZyanStringInsertDecU(ZyanString* string, ZyanUSize index, ZyanU64 value,
+// ZyanUSize padding_length)
+//{
+//
+//}
+//
+//ZyanStatus ZyanStringInsertDecS(ZyanString* string, ZyanUSize index, ZyanI64 value,
+// ZyanUSize padding_length, ZyanBool force_sign, const ZyanString* prefix)
+//{
+//
+//}
+//
+//ZyanStatus ZyanStringInsertHexU(ZyanString* string, ZyanUSize index, ZyanU64 value,
+// ZyanUSize padding_length, ZyanBool uppercase)
+//{
+//
+//}
+//
+//ZyanStatus ZyanStringInsertHexS(ZyanString* string, ZyanUSize index, ZyanI64 value,
+// ZyanUSize padding_length, ZyanBool uppercase, ZyanBool force_sign, const ZyanString* prefix)
+//{
+//
+//}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Appending */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYAN_NO_LIBC
+
+ZyanStatus ZyanStringAppendFormat(ZyanString* string, const char* format, ...)
+{
+ if (!string || !format)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZyanVAList arglist;
+ ZYAN_VA_START(arglist, format);
+
+ const ZyanUSize len = string->vector.size;
+
+ ZyanI32 w = ZYAN_VSNPRINTF((char*)string->vector.data + len - 1,
+ string->vector.capacity - len + 1, format, arglist);
+ if (w < 0)
+ {
+ ZYAN_VA_END(arglist);
+ return ZYAN_STATUS_FAILED;
+ }
+ if (w <= (ZyanI32)(string->vector.capacity - len))
+ {
+ string->vector.size = len + w;
+
+ ZYAN_VA_END(arglist);
+ return ZYAN_STATUS_SUCCESS;
+ }
+
+ // The remaining capacity was not sufficent to fit the formatted string. Trying to resize ..
+ const ZyanStatus status = ZyanStringResize(string, string->vector.size + w - 1);
+ if (!ZYAN_SUCCESS(status))
+ {
+ ZYAN_VA_END(arglist);
+ return status;
+ }
+
+ w = ZYAN_VSNPRINTF((char*)string->vector.data + len - 1,
+ string->vector.capacity - string->vector.size + 1, format, arglist);
+ if (w < 0)
+ {
+ ZYAN_VA_END(arglist);
+ return ZYAN_STATUS_FAILED;
+ }
+ ZYAN_ASSERT(w <= (ZyanI32)(string->vector.capacity - string->vector.size));
+
+ ZYAN_VA_END(arglist);
+ return ZYAN_STATUS_SUCCESS;
+}
+
+#endif // ZYAN_NO_LIBC
+
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanStringAppendDecU(ZyanString* string, ZyanU64 value, ZyanU8 padding_length)
+{
+#if defined(ZYAN_X64) || defined(ZYAN_AARCH64)
+ return ZyanStringAppendDecU64(string, value, padding_length);
+#else
+ // Working with 64-bit values is slow on non 64-bit systems
+ if (value & 0xFFFFFFFF00000000)
+ {
+ return ZyanStringAppendDecU64(string, value, padding_length);
+ }
+ return ZyanStringAppendDecU32(string, (ZyanU32)value, padding_length);
+#endif
+}
+
+ZyanStatus ZyanStringAppendDecS(ZyanString* string, ZyanI64 value, ZyanU8 padding_length,
+ ZyanBool force_sign, const ZyanStringView* prefix)
+{
+ if (value < 0)
+ {
+ ZYAN_CHECK(ZyanStringAppend(string, &STR_SUB));
+ if (prefix)
+ {
+ ZYAN_CHECK(ZyanStringAppend(string, prefix));
+ }
+ return ZyanStringAppendDecU(string, ZyanAbsI64(value), padding_length);
+ }
+
+ if (force_sign)
+ {
+ ZYAN_ASSERT(value >= 0);
+ ZYAN_CHECK(ZyanStringAppend(string, &STR_ADD));
+ }
+
+ if (prefix)
+ {
+ ZYAN_CHECK(ZyanStringAppend(string, prefix));
+ }
+ return ZyanStringAppendDecU(string, value, padding_length);
+}
+
+ZyanStatus ZyanStringAppendHexU(ZyanString* string, ZyanU64 value, ZyanU8 padding_length,
+ ZyanBool uppercase)
+{
+#if defined(ZYAN_X64) || defined(ZYAN_AARCH64)
+ return ZyanStringAppendHexU64(string, value, padding_length, uppercase);
+#else
+ // Working with 64-bit values is slow on non 64-bit systems
+ if (value & 0xFFFFFFFF00000000)
+ {
+ return ZyanStringAppendHexU64(string, value, padding_length, uppercase);
+ }
+ return ZyanStringAppendHexU32(string, (ZyanU32)value, padding_length, uppercase);
+#endif
+}
+
+ZyanStatus ZyanStringAppendHexS(ZyanString* string, ZyanI64 value, ZyanU8 padding_length,
+ ZyanBool uppercase, ZyanBool force_sign, const ZyanStringView* prefix)
+{
+ if (value < 0)
+ {
+ ZYAN_CHECK(ZyanStringAppend(string, &STR_SUB));
+ if (prefix)
+ {
+ ZYAN_CHECK(ZyanStringAppend(string, prefix));
+ }
+ return ZyanStringAppendHexU(string, ZyanAbsI64(value), padding_length, uppercase);
+ }
+
+ if (force_sign)
+ {
+ ZYAN_ASSERT(value >= 0);
+ ZYAN_CHECK(ZyanStringAppend(string, &STR_ADD));
+ }
+
+ if (prefix)
+ {
+ ZYAN_CHECK(ZyanStringAppend(string, prefix));
+ }
+ return ZyanStringAppendHexU(string, value, padding_length, uppercase);
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
diff --git a/externals/zycore/src/List.c b/externals/zycore/src/List.c
new file mode 100644
index 00000000..e233da81
--- /dev/null
+++ b/externals/zycore/src/List.c
@@ -0,0 +1,673 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+#include <Zycore/LibC.h>
+#include <Zycore/List.h>
+
+/* ============================================================================================== */
+/* Internal macros */
+/* ============================================================================================== */
+
+/**
+ * Returns a pointer to the data of the given `node`.
+ *
+ * @param node A pointer to the `ZyanNodeData` struct.
+ *
+ * @return A pointer to the data of the given `node`.
+ */
+#define ZYCORE_LIST_GET_NODE_DATA(node) \
+ ((void*)(node + 1))
+
+/* ============================================================================================== */
+/* Internal functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Helper functions */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Allocates memory for a new list node.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ * @param node Receives a pointer to the new `ZyanListNode` struct.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus ZyanListAllocateNode(ZyanList* list, ZyanListNode** node)
+{
+ ZYAN_ASSERT(list);
+ ZYAN_ASSERT(node);
+
+ const ZyanBool is_dynamic = (list->allocator != ZYAN_NULL);
+ if (is_dynamic)
+ {
+ ZYAN_ASSERT(list->allocator->allocate);
+ ZYAN_CHECK(list->allocator->allocate(list->allocator, (void**)node,
+ sizeof(ZyanListNode) + list->element_size, 1));
+ } else
+ {
+ if (list->first_unused)
+ {
+ *node = list->first_unused;
+ list->first_unused = (*node)->next;
+ } else
+ {
+ const ZyanUSize size = list->size * (sizeof(ZyanListNode) + list->element_size);
+ if (size + (sizeof(ZyanListNode) + list->element_size) > list->capacity)
+ {
+ return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
+ }
+
+ *node = (ZyanListNode*)((ZyanU8*)list->buffer + size);
+ }
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/**
+ * Frees memory of a node.
+ *
+ * @param list A pointer to the `ZyanList` instance.
+ * @param node A pointer to the `ZyanListNode` struct.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus ZyanListDeallocateNode(ZyanList* list, ZyanListNode* node)
+{
+ ZYAN_ASSERT(list);
+ ZYAN_ASSERT(node);
+
+ const ZyanBool is_dynamic = (list->allocator != ZYAN_NULL);
+ if (is_dynamic)
+ {
+ ZYAN_ASSERT(list->allocator->deallocate);
+ ZYAN_CHECK(list->allocator->deallocate(list->allocator, (void*)node,
+ sizeof(ZyanListNode) + list->element_size, 1));
+ } else
+ {
+ node->next = list->first_unused;
+ list->first_unused = node;
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Constructor and destructor */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYAN_NO_LIBC
+
+ZYAN_REQUIRES_LIBC ZyanStatus ZyanListInit(ZyanList* list, ZyanUSize element_size,
+ ZyanMemberProcedure destructor)
+{
+ return ZyanListInitEx(list, element_size, destructor, ZyanAllocatorDefault());
+}
+
+#endif // ZYAN_NO_LIBC
+
+ZyanStatus ZyanListInitEx(ZyanList* list, ZyanUSize element_size, ZyanMemberProcedure destructor,
+ ZyanAllocator* allocator)
+{
+ if (!list || !element_size || !allocator)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ list->allocator = allocator;
+ list->size = 0;
+ list->element_size = element_size;
+ list->destructor = destructor;
+ list->head = ZYAN_NULL;
+ list->tail = ZYAN_NULL;
+ list->buffer = ZYAN_NULL;
+ list->capacity = 0;
+ list->first_unused = ZYAN_NULL;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanListInitCustomBuffer(ZyanList* list, ZyanUSize element_size,
+ ZyanMemberProcedure destructor, void* buffer, ZyanUSize capacity)
+{
+ if (!list || !element_size || !buffer || !capacity)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ list->allocator = ZYAN_NULL;
+ list->size = 0;
+ list->element_size = element_size;
+ list->destructor = destructor;
+ list->head = ZYAN_NULL;
+ list->tail = ZYAN_NULL;
+ list->buffer = buffer;
+ list->capacity = capacity;
+ list->first_unused = ZYAN_NULL;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanListDestroy(ZyanList* list)
+{
+ if (!list)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZYAN_ASSERT(list->element_size);
+
+ const ZyanBool is_dynamic = (list->allocator != ZYAN_NULL);
+ ZyanListNode* node = (is_dynamic || list->destructor) ? list->head : ZYAN_NULL;
+ while (node)
+ {
+ if (list->destructor)
+ {
+ list->destructor(ZYCORE_LIST_GET_NODE_DATA(node));
+ }
+
+ ZyanListNode* const next = node->next;
+
+ if (is_dynamic)
+ {
+ ZYAN_CHECK(list->allocator->deallocate(list->allocator, node,
+ sizeof(ZyanListNode) + list->element_size, 1));
+ }
+
+ node = next;
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Duplication */
+/* ---------------------------------------------------------------------------------------------- */
+
+
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Item access */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanListGetHeadNode(const ZyanList* list, const ZyanListNode** node)
+{
+ if (!list)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ *node = list->head;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanListGetTailNode(const ZyanList* list, const ZyanListNode** node)
+{
+ if (!list)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ *node = list->tail;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanListGetPrevNode(const ZyanListNode** node)
+{
+ if (!node || !*node)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ *node = (*node)->prev;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanListGetNextNode(const ZyanListNode** node)
+{
+ if (!node || !*node)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ *node = (*node)->next;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+const void* ZyanListGetNodeData(const ZyanListNode* node)
+{
+ if (!node)
+ {
+ return ZYAN_NULL;
+ }
+
+ return (const void*)ZYCORE_LIST_GET_NODE_DATA(node);
+}
+
+ZyanStatus ZyanListGetNodeDataEx(const ZyanListNode* node, const void** value)
+{
+ if (!node)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ *value = (const void*)ZYCORE_LIST_GET_NODE_DATA(node);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+void* ZyanListGetNodeDataMutable(const ZyanListNode* node)
+{
+ if (!node)
+ {
+ return ZYAN_NULL;
+ }
+
+ return ZYCORE_LIST_GET_NODE_DATA(node);
+}
+
+ZyanStatus ZyanListGetNodeDataMutableEx(const ZyanListNode* node, void** value)
+{
+ if (!node)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ *value = ZYCORE_LIST_GET_NODE_DATA(node);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanListSetNodeData(const ZyanList* list, const ZyanListNode* node, const void* value)
+{
+ if (!list || !node || !value)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (list->destructor)
+ {
+ list->destructor(ZYCORE_LIST_GET_NODE_DATA(node));
+ }
+
+ ZYAN_ASSERT(list->element_size);
+ ZYAN_MEMCPY(ZYCORE_LIST_GET_NODE_DATA(node), value, list->element_size);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Insertion */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanListPushBack(ZyanList* list, const void* item)
+{
+ if (!list || !item)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZyanListNode* node;
+ ZYAN_CHECK(ZyanListAllocateNode(list, &node));
+ node->prev = list->tail;
+ node->next = ZYAN_NULL;
+
+ ZYAN_MEMCPY(ZYCORE_LIST_GET_NODE_DATA(node), item, list->element_size);
+
+ if (!list->head)
+ {
+ list->head = node;
+ list->tail = node;
+ } else
+ {
+ list->tail->next = node;
+ list->tail = node;
+ }
+ ++list->size;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanListPushFront(ZyanList* list, const void* item)
+{
+ if (!list || !item)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZyanListNode* node;
+ ZYAN_CHECK(ZyanListAllocateNode(list, &node));
+ node->prev = ZYAN_NULL;
+ node->next = list->head;
+
+ ZYAN_MEMCPY(ZYCORE_LIST_GET_NODE_DATA(node), item, list->element_size);
+
+ if (!list->head)
+ {
+ list->head = node;
+ list->tail = node;
+ } else
+ {
+ list->head->prev= node;
+ list->head = node;
+ }
+ ++list->size;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanListEmplaceBack(ZyanList* list, void** item, ZyanMemberFunction constructor)
+{
+ if (!list || !item)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZyanListNode* node;
+ ZYAN_CHECK(ZyanListAllocateNode(list, &node));
+ node->prev = list->tail;
+ node->next = ZYAN_NULL;
+
+ *item = ZYCORE_LIST_GET_NODE_DATA(node);
+ if (constructor)
+ {
+ constructor(*item);
+ }
+
+ if (!list->head)
+ {
+ list->head = node;
+ list->tail = node;
+ } else
+ {
+ list->tail->next = node;
+ list->tail = node;
+ }
+ ++list->size;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanListEmplaceFront(ZyanList* list, void** item, ZyanMemberFunction constructor)
+{
+ if (!list || !item)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZyanListNode* node;
+ ZYAN_CHECK(ZyanListAllocateNode(list, &node));
+ node->prev = ZYAN_NULL;
+ node->next = list->head;
+
+ *item = ZYCORE_LIST_GET_NODE_DATA(node);
+ if (constructor)
+ {
+ constructor(*item);
+ }
+
+ if (!list->head)
+ {
+ list->head = node;
+ list->tail = node;
+ } else
+ {
+ list->head->prev= node;
+ list->head = node;
+ }
+ ++list->size;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Deletion */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanListPopBack(ZyanList* list)
+{
+ if (!list)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (!list->tail)
+ {
+ return ZYAN_STATUS_INVALID_OPERATION;
+ }
+
+ ZyanListNode* const node = list->tail;
+
+ if (list->destructor)
+ {
+ list->destructor(ZYCORE_LIST_GET_NODE_DATA(node));
+ }
+
+ list->tail = node->prev;
+ if (list->tail)
+ {
+ list->tail->next = ZYAN_NULL;
+ }
+ if (list->head == node)
+ {
+ list->head = list->tail;
+ }
+ --list->size;
+
+ return ZyanListDeallocateNode(list, node);
+}
+
+ZyanStatus ZyanListPopFront(ZyanList* list)
+{
+ if (!list)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (!list->head)
+ {
+ return ZYAN_STATUS_INVALID_OPERATION;
+ }
+
+ ZyanListNode* const node = list->head;
+
+ if (list->destructor)
+ {
+ list->destructor(ZYCORE_LIST_GET_NODE_DATA(node));
+ }
+
+ list->head = node->next;
+ if (list->head)
+ {
+ list->head->prev = ZYAN_NULL;
+ }
+ if (list->tail == node)
+ {
+ list->tail = list->head;
+ }
+ --list->size;
+
+ return ZyanListDeallocateNode(list, node);
+}
+
+ZyanStatus ZyanListRemove(ZyanList* list, const ZyanListNode* node)
+{
+ ZYAN_UNUSED(list);
+ ZYAN_UNUSED(node);
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanListRemoveRange(ZyanList* list, const ZyanListNode* first, const ZyanListNode* last)
+{
+ ZYAN_UNUSED(list);
+ ZYAN_UNUSED(first);
+ ZYAN_UNUSED(last);
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanListClear(ZyanList* list)
+{
+ return ZyanListResizeEx(list, 0, ZYAN_NULL);
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Searching */
+/* ---------------------------------------------------------------------------------------------- */
+
+
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Memory management */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanListResize(ZyanList* list, ZyanUSize size)
+{
+ return ZyanListResizeEx(list, size, ZYAN_NULL);
+}
+
+ZyanStatus ZyanListResizeEx(ZyanList* list, ZyanUSize size, const void* initializer)
+{
+ if (!list)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (size == list->size)
+ {
+ return ZYAN_STATUS_SUCCESS;
+ }
+
+ if (size == 0)
+ {
+ const ZyanBool is_dynamic = (list->allocator != ZYAN_NULL);
+ ZyanListNode* node = (is_dynamic || list->destructor) ? list->head : ZYAN_NULL;
+ while (node)
+ {
+ if (list->destructor)
+ {
+ list->destructor(ZYCORE_LIST_GET_NODE_DATA(node));
+ }
+
+ ZyanListNode* const next = node->next;
+
+ if (is_dynamic)
+ {
+ ZYAN_CHECK(list->allocator->deallocate(list->allocator, node,
+ sizeof(ZyanListNode) + list->element_size, 1));
+ }
+
+ node = next;
+ }
+
+ list->size = 0;
+ list->head = 0;
+ list->tail = 0;
+ list->first_unused = ZYAN_NULL;
+
+ return ZYAN_STATUS_SUCCESS;
+ }
+
+ if (size > list->size)
+ {
+ ZyanListNode* node;
+ for (ZyanUSize i = list->size; i < size; ++i)
+ {
+ ZYAN_CHECK(ZyanListAllocateNode(list, &node));
+ node->prev = list->tail;
+ node->next = ZYAN_NULL;
+
+ if (initializer)
+ {
+ ZYAN_MEMCPY(ZYCORE_LIST_GET_NODE_DATA(node), initializer, list->element_size);
+ }
+
+ if (!list->head)
+ {
+ list->head = node;
+ list->tail = node;
+ } else
+ {
+ list->tail->next = node;
+ list->tail = node;
+ }
+
+ // `ZyanListAllocateNode` needs the list size
+ ++list->size;
+ }
+ } else
+ {
+ for (ZyanUSize i = size; i < list->size; ++i)
+ {
+ ZyanListNode* const node = list->tail;
+
+ if (list->destructor)
+ {
+ list->destructor(ZYCORE_LIST_GET_NODE_DATA(node));
+ }
+
+ list->tail = node->prev;
+ if (list->tail)
+ {
+ list->tail->next = ZYAN_NULL;
+ }
+
+ ZYAN_CHECK(ZyanListDeallocateNode(list, node));
+ }
+
+ list->size = size;
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Information */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanListGetSize(const ZyanList* list, ZyanUSize* size)
+{
+ if (!list)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ *size = list->size;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
diff --git a/externals/zycore/src/String.c b/externals/zycore/src/String.c
new file mode 100644
index 00000000..feeb04d0
--- /dev/null
+++ b/externals/zycore/src/String.c
@@ -0,0 +1,1098 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+#include <Zycore/String.h>
+#include <Zycore/LibC.h>
+
+/* ============================================================================================== */
+/* Internal macros */
+/* ============================================================================================== */
+
+/**
+ * Writes a terminating '\0' character at the end of the string data.
+ */
+#define ZYCORE_STRING_NULLTERMINATE(string) \
+ *(char*)((ZyanU8*)(string)->vector.data + (string)->vector.size - 1) = '\0';
+
+/**
+ * Checks for a terminating '\0' character at the end of the string data.
+ */
+#define ZYCORE_STRING_ASSERT_NULLTERMINATION(string) \
+ ZYAN_ASSERT(*(char*)((ZyanU8*)(string)->vector.data + (string)->vector.size - 1) == '\0');
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Constructor and destructor */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYAN_NO_LIBC
+
+ZyanStatus ZyanStringInit(ZyanString* string, ZyanUSize capacity)
+{
+ return ZyanStringInitEx(string, capacity, ZyanAllocatorDefault(),
+ ZYAN_STRING_DEFAULT_GROWTH_FACTOR, ZYAN_STRING_DEFAULT_SHRINK_THRESHOLD);
+}
+
+#endif // ZYAN_NO_LIBC
+
+ZyanStatus ZyanStringInitEx(ZyanString* string, ZyanUSize capacity, ZyanAllocator* allocator,
+ float growth_factor, float shrink_threshold)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ string->flags = 0;
+ capacity = ZYAN_MAX(ZYAN_STRING_MIN_CAPACITY, capacity) + 1;
+ ZYAN_CHECK(ZyanVectorInitEx(&string->vector, sizeof(char), capacity, ZYAN_NULL, allocator,
+ growth_factor, shrink_threshold));
+ ZYAN_ASSERT(string->vector.capacity >= capacity);
+ // Some of the string code relies on `sizeof(char) == 1`
+ ZYAN_ASSERT(string->vector.element_size == 1);
+
+ *(char*)string->vector.data = '\0';
+ ++string->vector.size;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanStringInitCustomBuffer(ZyanString* string, char* buffer, ZyanUSize capacity)
+{
+ if (!string || !capacity)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ string->flags = ZYAN_STRING_HAS_FIXED_CAPACITY;
+ ZYAN_CHECK(ZyanVectorInitCustomBuffer(&string->vector, sizeof(char), (void*)buffer, capacity,
+ ZYAN_NULL));
+ ZYAN_ASSERT(string->vector.capacity == capacity);
+ // Some of the string code relies on `sizeof(char) == 1`
+ ZYAN_ASSERT(string->vector.element_size == 1);
+
+ *(char*)string->vector.data = '\0';
+ ++string->vector.size;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanStringDestroy(ZyanString* string)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (string->flags & ZYAN_STRING_HAS_FIXED_CAPACITY)
+ {
+ return ZYAN_STATUS_SUCCESS;
+ }
+
+ return ZyanVectorDestroy(&string->vector);
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Duplication */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYAN_NO_LIBC
+
+ZyanStatus ZyanStringDuplicate(ZyanString* destination, const ZyanStringView* source,
+ ZyanUSize capacity)
+{
+ return ZyanStringDuplicateEx(destination, source, capacity, ZyanAllocatorDefault(),
+ ZYAN_STRING_DEFAULT_GROWTH_FACTOR, ZYAN_STRING_DEFAULT_SHRINK_THRESHOLD);
+}
+
+#endif // ZYAN_NO_LIBC
+
+ZyanStatus ZyanStringDuplicateEx(ZyanString* destination, const ZyanStringView* source,
+ ZyanUSize capacity, ZyanAllocator* allocator, float growth_factor, float shrink_threshold)
+{
+ if (!source || !source->string.vector.size)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ const ZyanUSize len = source->string.vector.size;
+ capacity = ZYAN_MAX(capacity, len - 1);
+ ZYAN_CHECK(ZyanStringInitEx(destination, capacity, allocator, growth_factor, shrink_threshold));
+ ZYAN_ASSERT(destination->vector.capacity >= len);
+
+ ZYAN_MEMCPY(destination->vector.data, source->string.vector.data,
+ source->string.vector.size - 1);
+ destination->vector.size = len;
+ ZYCORE_STRING_NULLTERMINATE(destination);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanStringDuplicateCustomBuffer(ZyanString* destination, const ZyanStringView* source,
+ char* buffer, ZyanUSize capacity)
+{
+ if (!source || !source->string.vector.size)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ const ZyanUSize len = source->string.vector.size;
+ if (capacity < len)
+ {
+ return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
+ }
+
+ ZYAN_CHECK(ZyanStringInitCustomBuffer(destination, buffer, capacity));
+ ZYAN_ASSERT(destination->vector.capacity >= len);
+
+ ZYAN_MEMCPY(destination->vector.data, source->string.vector.data,
+ source->string.vector.size - 1);
+ destination->vector.size = len;
+ ZYCORE_STRING_NULLTERMINATE(destination);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Concatenation */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYAN_NO_LIBC
+
+ZyanStatus ZyanStringConcat(ZyanString* destination, const ZyanStringView* s1,
+ const ZyanStringView* s2, ZyanUSize capacity)
+{
+ return ZyanStringConcatEx(destination, s1, s2, capacity, ZyanAllocatorDefault(),
+ ZYAN_STRING_DEFAULT_GROWTH_FACTOR, ZYAN_STRING_DEFAULT_SHRINK_THRESHOLD);
+}
+
+#endif // ZYAN_NO_LIBC
+
+ZyanStatus ZyanStringConcatEx(ZyanString* destination, const ZyanStringView* s1,
+ const ZyanStringView* s2, ZyanUSize capacity, ZyanAllocator* allocator, float growth_factor,
+ float shrink_threshold)
+{
+ if (!s1 || !s2 || !s1->string.vector.size || !s2->string.vector.size)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ const ZyanUSize len = s1->string.vector.size + s2->string.vector.size - 1;
+ capacity = ZYAN_MAX(capacity, len - 1);
+ ZYAN_CHECK(ZyanStringInitEx(destination, capacity, allocator, growth_factor, shrink_threshold));
+ ZYAN_ASSERT(destination->vector.capacity >= len);
+
+ ZYAN_MEMCPY(destination->vector.data, s1->string.vector.data, s1->string.vector.size - 1);
+ ZYAN_MEMCPY((char*)destination->vector.data + s1->string.vector.size - 1,
+ s2->string.vector.data, s2->string.vector.size - 1);
+ destination->vector.size = len;
+ ZYCORE_STRING_NULLTERMINATE(destination);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanStringConcatCustomBuffer(ZyanString* destination, const ZyanStringView* s1,
+ const ZyanStringView* s2, char* buffer, ZyanUSize capacity)
+{
+ if (!s1 || !s2 || !s1->string.vector.size || !s2->string.vector.size)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ const ZyanUSize len = s1->string.vector.size + s2->string.vector.size - 1;
+ if (capacity < len)
+ {
+ return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
+ }
+
+ ZYAN_CHECK(ZyanStringInitCustomBuffer(destination, buffer, capacity));
+ ZYAN_ASSERT(destination->vector.capacity >= len);
+
+ ZYAN_MEMCPY(destination->vector.data, s1->string.vector.data, s1->string.vector.size - 1);
+ ZYAN_MEMCPY((char*)destination->vector.data + s1->string.vector.size - 1,
+ s2->string.vector.data, s2->string.vector.size - 1);
+ destination->vector.size = len;
+ ZYCORE_STRING_NULLTERMINATE(destination);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Views */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanStringViewInsideView(ZyanStringView* view, const ZyanStringView* source)
+{
+ if (!view || !source)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ view->string.vector.data = source->string.vector.data;
+ view->string.vector.size = source->string.vector.size;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanStringViewInsideViewEx(ZyanStringView* view, const ZyanStringView* source,
+ ZyanUSize index, ZyanUSize count)
+{
+ if (!view || !source)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (index + count >= source->string.vector.size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ view->string.vector.data = (void*)((char*)source->string.vector.data + index);
+ view->string.vector.size = count;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanStringViewInsideBuffer(ZyanStringView* view, const char* string)
+{
+ if (!view || !string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ view->string.vector.data = (void*)string;
+ view->string.vector.size = ZYAN_STRLEN(string) + 1;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanStringViewInsideBufferEx(ZyanStringView* view, const char* buffer, ZyanUSize length)
+{
+ if (!view || !buffer || !length)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ view->string.vector.data = (void*)buffer;
+ view->string.vector.size = length + 1;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanStringViewGetSize(const ZyanStringView* view, ZyanUSize* size)
+{
+ if (!view || !size)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZYAN_ASSERT(view->string.vector.size >= 1);
+ *size = view->string.vector.size - 1;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZYCORE_EXPORT ZyanStatus ZyanStringViewGetData(const ZyanStringView* view, const char** buffer)
+{
+ if (!view || !buffer)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ *buffer = view->string.vector.data;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Character access */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanStringGetChar(const ZyanStringView* string, ZyanUSize index, char* value)
+{
+ if (!string || !value)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ // Don't allow direct access to the terminating '\0' character
+ if (index + 1 >= string->string.vector.size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ const char* chr;
+ ZYAN_CHECK(ZyanVectorGetPointer(&string->string.vector, index, (const void**)&chr));
+ *value = *chr;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanStringGetCharMutable(ZyanString* string, ZyanUSize index, char** value)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ // Don't allow direct access to the terminating '\0' character
+ if (index + 1 >= string->vector.size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ return ZyanVectorGetPointerMutable(&string->vector, index, (void**)value);
+}
+
+ZyanStatus ZyanStringSetChar(ZyanString* string, ZyanUSize index, char value)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ // Don't allow direct access to the terminating '\0' character
+ if (index + 1 >= string->vector.size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ return ZyanVectorSet(&string->vector, index, (void*)&value);
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Insertion */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanStringInsert(ZyanString* destination, ZyanUSize index, const ZyanStringView* source)
+{
+ if (!destination || !source || !source->string.vector.size)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (index == destination->vector.size)
+ {
+ return ZyanStringAppend(destination, source);
+ }
+
+ // Don't allow insertion after the terminating '\0' character
+ if (index >= destination->vector.size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ ZYAN_CHECK(ZyanVectorInsertRange(&destination->vector, index, source->string.vector.data,
+ source->string.vector.size - 1));
+ ZYCORE_STRING_ASSERT_NULLTERMINATION(destination);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanStringInsertEx(ZyanString* destination, ZyanUSize destination_index,
+ const ZyanStringView* source, ZyanUSize source_index, ZyanUSize count)
+{
+ if (!destination || !source || !source->string.vector.size)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (destination_index == destination->vector.size)
+ {
+ return ZyanStringAppendEx(destination, source, source_index, count);
+ }
+
+ // Don't allow insertion after the terminating '\0' character
+ if (destination_index >= destination->vector.size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ // Don't allow access to the terminating '\0' character
+ if (source_index + count >= source->string.vector.size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ ZYAN_CHECK(ZyanVectorInsertRange(&destination->vector, destination_index,
+ (char*)source->string.vector.data + source_index, count));
+ ZYCORE_STRING_ASSERT_NULLTERMINATION(destination);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Appending */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanStringAppend(ZyanString* destination, const ZyanStringView* source)
+{
+ if (!destination || !source || !source->string.vector.size)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ const ZyanUSize len = destination->vector.size;
+ ZYAN_CHECK(ZyanVectorResize(&destination->vector, len + source->string.vector.size - 1));
+ ZYAN_MEMCPY((char*)destination->vector.data + len - 1, source->string.vector.data,
+ source->string.vector.size - 1);
+ ZYCORE_STRING_NULLTERMINATE(destination);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanStringAppendEx(ZyanString* destination, const ZyanStringView* source,
+ ZyanUSize source_index, ZyanUSize count)
+{
+ if (!destination || !source || !source->string.vector.size)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ // Don't allow access to the terminating '\0' character
+ if (source_index + count >= source->string.vector.size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ const ZyanUSize len = destination->vector.size;
+ ZYAN_CHECK(ZyanVectorResize(&destination->vector, len + count));
+ ZYAN_MEMCPY((char*)destination->vector.data + len - 1,
+ (const char*)source->string.vector.data + source_index, count);
+ ZYCORE_STRING_NULLTERMINATE(destination);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Deletion */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanStringDelete(ZyanString* string, ZyanUSize index, ZyanUSize count)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ // Don't allow removal of the terminating '\0' character
+ if (index + count >= string->vector.size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ ZYAN_CHECK(ZyanVectorDeleteRange(&string->vector, index, count));
+ ZYCORE_STRING_NULLTERMINATE(string);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanStringTruncate(ZyanString* string, ZyanUSize index)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ // Don't allow removal of the terminating '\0' character
+ if (index >= string->vector.size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ ZYAN_CHECK(ZyanVectorDeleteRange(&string->vector, index, string->vector.size - index - 1));
+ ZYCORE_STRING_NULLTERMINATE(string);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanStringClear(ZyanString* string)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZYAN_CHECK(ZyanVectorClear(&string->vector));
+ // `ZyanVector` guarantees a minimum capacity of 1 element/character
+ ZYAN_ASSERT(string->vector.capacity >= 1);
+
+ *(char*)string->vector.data = '\0';
+ string->vector.size++;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Searching */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanStringLPos(const ZyanStringView* haystack, const ZyanStringView* needle,
+ ZyanISize* found_index)
+{
+ if (!haystack)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ZyanStringLPosEx(haystack, needle, found_index, 0, haystack->string.vector.size - 1);
+}
+
+ZyanStatus ZyanStringLPosEx(const ZyanStringView* haystack, const ZyanStringView* needle,
+ ZyanISize* found_index, ZyanUSize index, ZyanUSize count)
+{
+ if (!haystack || !needle || !found_index)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ // Don't allow access to the terminating '\0' character
+ if (index + count >= haystack->string.vector.size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ if ((haystack->string.vector.size == 1) || (needle->string.vector.size == 1) ||
+ (haystack->string.vector.size < needle->string.vector.size))
+ {
+ *found_index = -1;
+ return ZYAN_STATUS_FALSE;
+ }
+
+ const char* s = (const char*)haystack->string.vector.data + index;
+ const char* b = (const char*)needle->string.vector.data;
+ for (; s + 1 < (const char*)haystack->string.vector.data + haystack->string.vector.size; ++s)
+ {
+ if (*s != *b)
+ {
+ continue;
+ }
+ const char* a = s;
+ for (;;)
+ {
+ if ((ZyanUSize)(a - (const char*)haystack->string.vector.data) > index + count)
+ {
+ *found_index = -1;
+ return ZYAN_STATUS_FALSE;
+ }
+ if (*b == 0)
+ {
+ *found_index = (ZyanISize)(s - (const char*)haystack->string.vector.data);
+ return ZYAN_STATUS_TRUE;
+ }
+ if (*a++ != *b++)
+ {
+ break;
+ }
+ }
+ b = (char*)needle->string.vector.data;
+ }
+
+ *found_index = -1;
+ return ZYAN_STATUS_FALSE;
+}
+
+ZyanStatus ZyanStringLPosI(const ZyanStringView* haystack, const ZyanStringView* needle,
+ ZyanISize* found_index)
+{
+ if (!haystack)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ZyanStringLPosIEx(haystack, needle, found_index, 0, haystack->string.vector.size - 1);
+}
+
+ZyanStatus ZyanStringLPosIEx(const ZyanStringView* haystack, const ZyanStringView* needle,
+ ZyanISize* found_index, ZyanUSize index, ZyanUSize count)
+{
+ // This solution assumes that characters are represented using ASCII representation, i.e.,
+ // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A',
+ // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively.
+
+ if (!haystack || !needle || !found_index)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ // Don't allow access to the terminating '\0' character
+ if (index + count >= haystack->string.vector.size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ if ((haystack->string.vector.size == 1) || (needle->string.vector.size == 1) ||
+ (haystack->string.vector.size < needle->string.vector.size))
+ {
+ *found_index = -1;
+ return ZYAN_STATUS_FALSE;
+ }
+
+ const char* s = (const char*)haystack->string.vector.data + index;
+ const char* b = (const char*)needle->string.vector.data;
+ for (; s + 1 < (const char*)haystack->string.vector.data + haystack->string.vector.size; ++s)
+ {
+ if ((*s != *b) && ((*s ^ 32) != *b))
+ {
+ continue;
+ }
+ const char* a = s;
+ for (;;)
+ {
+ if ((ZyanUSize)(a - (const char*)haystack->string.vector.data) > index + count)
+ {
+ *found_index = -1;
+ return ZYAN_STATUS_FALSE;
+ }
+ if (*b == 0)
+ {
+ *found_index = (ZyanISize)(s - (const char*)haystack->string.vector.data);
+ return ZYAN_STATUS_TRUE;
+ }
+ const char c1 = *a++;
+ const char c2 = *b++;
+ if ((c1 != c2) && ((c1 ^ 32) != c2))
+ {
+ break;
+ }
+ }
+ b = (char*)needle->string.vector.data;
+ }
+
+ *found_index = -1;
+ return ZYAN_STATUS_FALSE;
+}
+
+ZyanStatus ZyanStringRPos(const ZyanStringView* haystack, const ZyanStringView* needle,
+ ZyanISize* found_index)
+{
+ if (!haystack)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ZyanStringRPosEx(haystack, needle, found_index, haystack->string.vector.size - 1,
+ haystack->string.vector.size - 1);
+}
+
+ZyanStatus ZyanStringRPosEx(const ZyanStringView* haystack, const ZyanStringView* needle,
+ ZyanISize* found_index, ZyanUSize index, ZyanUSize count)
+{
+ if (!haystack || !needle || !found_index)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ // Don't allow access to the terminating '\0' character
+ if ((index >= haystack->string.vector.size) || (count > index))
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ if (!index || !count ||
+ (haystack->string.vector.size == 1) || (needle->string.vector.size == 1) ||
+ (haystack->string.vector.size < needle->string.vector.size))
+ {
+ *found_index = -1;
+ return ZYAN_STATUS_FALSE;
+ }
+
+ const char* s = (const char*)haystack->string.vector.data + index - 1;
+ const char* b = (const char*)needle->string.vector.data + needle->string.vector.size - 2;
+ for (; s >= (const char*)haystack->string.vector.data; --s)
+ {
+ if (*s != *b)
+ {
+ continue;
+ }
+ const char* a = s;
+ for (;;)
+ {
+ if (b < (const char*)needle->string.vector.data)
+ {
+ *found_index = (ZyanISize)(a - (const char*)haystack->string.vector.data + 1);
+ return ZYAN_STATUS_TRUE;
+ }
+ if (a < (const char*)haystack->string.vector.data + index - count)
+ {
+ *found_index = -1;
+ return ZYAN_STATUS_FALSE;
+ }
+ if (*a-- != *b--)
+ {
+ break;
+ }
+ }
+ b = (char*)needle->string.vector.data + needle->string.vector.size - 2;
+ }
+
+ *found_index = -1;
+ return ZYAN_STATUS_FALSE;
+}
+
+ZyanStatus ZyanStringRPosI(const ZyanStringView* haystack, const ZyanStringView* needle,
+ ZyanISize* found_index)
+{
+ if (!haystack)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ZyanStringRPosIEx(haystack, needle, found_index, haystack->string.vector.size - 1,
+ haystack->string.vector.size - 1);
+}
+
+ZyanStatus ZyanStringRPosIEx(const ZyanStringView* haystack, const ZyanStringView* needle,
+ ZyanISize* found_index, ZyanUSize index, ZyanUSize count)
+{
+ // This solution assumes that characters are represented using ASCII representation, i.e.,
+ // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A',
+ // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively.
+
+ if (!haystack || !needle || !found_index)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ // Don't allow access to the terminating '\0' character
+ if ((index >= haystack->string.vector.size) || (count > index))
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ if (!index || !count ||
+ (haystack->string.vector.size == 1) || (needle->string.vector.size == 1) ||
+ (haystack->string.vector.size < needle->string.vector.size))
+ {
+ *found_index = -1;
+ return ZYAN_STATUS_FALSE;
+ }
+
+ const char* s = (const char*)haystack->string.vector.data + index - 1;
+ const char* b = (const char*)needle->string.vector.data + needle->string.vector.size - 2;
+ for (; s >= (const char*)haystack->string.vector.data; --s)
+ {
+ if ((*s != *b) && ((*s ^ 32) != *b))
+ {
+ continue;
+ }
+ const char* a = s;
+ for (;;)
+ {
+ if (b < (const char*)needle->string.vector.data)
+ {
+ *found_index = (ZyanISize)(a - (const char*)haystack->string.vector.data + 1);
+ return ZYAN_STATUS_TRUE;
+ }
+ if (a < (const char*)haystack->string.vector.data + index - count)
+ {
+ *found_index = -1;
+ return ZYAN_STATUS_FALSE;
+ }
+ const char c1 = *a--;
+ const char c2 = *b--;
+ if ((c1 != c2) && ((c1 ^ 32) != c2))
+ {
+ break;
+ }
+ }
+ b = (char*)needle->string.vector.data + needle->string.vector.size - 2;
+ }
+
+ *found_index = -1;
+ return ZYAN_STATUS_FALSE;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Comparing */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanStringCompare(const ZyanStringView* s1, const ZyanStringView* s2, ZyanI32* result)
+{
+ if (!s1 || !s2)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (s1->string.vector.size < s2->string.vector.size)
+ {
+ *result = -1;
+ return ZYAN_STATUS_FALSE;
+ }
+ if (s1->string.vector.size > s2->string.vector.size)
+ {
+ *result = 1;
+ return ZYAN_STATUS_FALSE;
+ }
+
+ const char* const a = (char*)s1->string.vector.data;
+ const char* const b = (char*)s2->string.vector.data;
+ ZyanUSize i;
+ for (i = 0; (i + 1 < s1->string.vector.size) && (i + 1 < s2->string.vector.size); ++i)
+ {
+ if (a[i] == b[i])
+ {
+ continue;
+ }
+ break;
+ }
+
+ if (a[i] == b[i])
+ {
+ *result = 0;
+ return ZYAN_STATUS_TRUE;
+ }
+
+ if ((a[i] | 32) < (b[i] | 32))
+ {
+ *result = -1;
+ return ZYAN_STATUS_FALSE;
+ }
+
+ *result = 1;
+ return ZYAN_STATUS_FALSE;
+}
+
+ZyanStatus ZyanStringCompareI(const ZyanStringView* s1, const ZyanStringView* s2, ZyanI32* result)
+{
+ // This solution assumes that characters are represented using ASCII representation, i.e.,
+ // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A',
+ // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively.
+
+ if (!s1 || !s2)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (s1->string.vector.size < s2->string.vector.size)
+ {
+ *result = -1;
+ return ZYAN_STATUS_FALSE;
+ }
+ if (s1->string.vector.size > s2->string.vector.size)
+ {
+ *result = 1;
+ return ZYAN_STATUS_FALSE;
+ }
+
+ const char* const a = (char*)s1->string.vector.data;
+ const char* const b = (char*)s2->string.vector.data;
+ ZyanUSize i;
+ for (i = 0; (i + 1 < s1->string.vector.size) && (i + 1 < s2->string.vector.size); ++i)
+ {
+ if ((a[i] == b[i]) || ((a[i] ^ 32) == b[i]))
+ {
+ continue;
+ }
+ break;
+ }
+
+ if (a[i] == b[i])
+ {
+ *result = 0;
+ return ZYAN_STATUS_TRUE;
+ }
+
+ if ((a[i] | 32) < (b[i] | 32))
+ {
+ *result = -1;
+ return ZYAN_STATUS_FALSE;
+ }
+
+ *result = 1;
+ return ZYAN_STATUS_FALSE;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Case conversion */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanStringToLowerCase(ZyanString* string)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ZyanStringToLowerCaseEx(string, 0, string->vector.size - 1);
+}
+
+ZyanStatus ZyanStringToLowerCaseEx(ZyanString* string, ZyanUSize index, ZyanUSize count)
+{
+ // This solution assumes that characters are represented using ASCII representation, i.e.,
+ // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A',
+ // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively.
+
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ // Don't allow access to the terminating '\0' character
+ if (index + count >= string->vector.size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ char* s = (char*)string->vector.data + index;
+ for (ZyanUSize i = index; i < index + count; ++i)
+ {
+ const char c = *s;
+ if ((c >= 'A') && (c <= 'Z'))
+ {
+ *s = c | 32;
+ }
+ ++s;
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanStringToUpperCase(ZyanString* string)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ZyanStringToUpperCaseEx(string, 0, string->vector.size - 1);
+}
+
+ZyanStatus ZyanStringToUpperCaseEx(ZyanString* string, ZyanUSize index, ZyanUSize count)
+{
+ // This solution assumes that characters are represented using ASCII representation, i.e.,
+ // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A',
+ // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively.
+
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ // Don't allow access to the terminating '\0' character
+ if (index + count >= string->vector.size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ char* s = (char*)string->vector.data + index;
+ for (ZyanUSize i = index; i < index + count; ++i)
+ {
+ const char c = *s;
+ if ((c >= 'a') && (c <= 'z'))
+ {
+ *s = c & ~32;
+ }
+ ++s;
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Memory management */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanStringResize(ZyanString* string, ZyanUSize size)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZYAN_CHECK(ZyanVectorResize(&string->vector, size + 1));
+ ZYCORE_STRING_NULLTERMINATE(string);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanStringReserve(ZyanString* string, ZyanUSize capacity)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ZyanVectorReserve(&string->vector, capacity);
+}
+
+ZyanStatus ZyanStringShrinkToFit(ZyanString* string)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ZyanVectorShrinkToFit(&string->vector);
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Information */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanStringGetCapacity(const ZyanString* string, ZyanUSize* capacity)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZYAN_ASSERT(string->vector.capacity >= 1);
+ *capacity = string->vector.capacity - 1;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanStringGetSize(const ZyanString* string, ZyanUSize* size)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZYAN_ASSERT(string->vector.size >= 1);
+ *size = string->vector.size - 1;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanStringGetData(const ZyanString* string, const char** value)
+{
+ if (!string)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ *value = string->vector.data;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
diff --git a/externals/zycore/src/Vector.c b/externals/zycore/src/Vector.c
new file mode 100644
index 00000000..6b8d67c4
--- /dev/null
+++ b/externals/zycore/src/Vector.c
@@ -0,0 +1,848 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+#include <Zycore/LibC.h>
+#include <Zycore/Vector.h>
+
+/* ============================================================================================== */
+/* Internal macros */
+/* ============================================================================================== */
+
+/**
+ * Checks, if the passed vector should grow.
+ *
+ * @param size The desired size of the vector.
+ * @param capacity The current capacity of the vector.
+ *
+ * @return `ZYAN_TRUE`, if the vector should grow or `ZYAN_FALSE`, if not.
+ */
+#define ZYCORE_VECTOR_SHOULD_GROW(size, capacity) \
+ ((size) > (capacity))
+
+/**
+ * Checks, if the passed vector should shrink.
+ *
+ * @param size The desired size of the vector.
+ * @param capacity The current capacity of the vector.
+ * @param threshold The shrink threshold.
+ *
+ * @return `ZYAN_TRUE`, if the vector should shrink or `ZYAN_FALSE`, if not.
+ */
+#define ZYCORE_VECTOR_SHOULD_SHRINK(size, capacity, threshold) \
+ ((size) < (capacity) * (threshold))
+
+/**
+ * Returns the offset of the element at the given `index`.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param index The element index.
+ *
+ * @return The offset of the element at the given `index`.
+ */
+#define ZYCORE_VECTOR_OFFSET(vector, index) \
+ ((void*)((ZyanU8*)(vector)->data + ((index) * (vector)->element_size)))
+
+/* ============================================================================================== */
+/* Internal functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Helper functions */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Reallocates the internal buffer of the vector.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param capacity The new capacity.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus ZyanVectorReallocate(ZyanVector* vector, ZyanUSize capacity)
+{
+ ZYAN_ASSERT(vector);
+ ZYAN_ASSERT(vector->capacity >= ZYAN_VECTOR_MIN_CAPACITY);
+ ZYAN_ASSERT(vector->element_size);
+ ZYAN_ASSERT(vector->data);
+
+ if (!vector->allocator)
+ {
+ if (vector->capacity < capacity)
+ {
+ return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
+ }
+ return ZYAN_STATUS_SUCCESS;
+ }
+
+ ZYAN_ASSERT(vector->allocator);
+ ZYAN_ASSERT(vector->allocator->reallocate);
+
+ if (capacity < ZYAN_VECTOR_MIN_CAPACITY)
+ {
+ if (vector->capacity > ZYAN_VECTOR_MIN_CAPACITY)
+ {
+ capacity = ZYAN_VECTOR_MIN_CAPACITY;
+ } else
+ {
+ return ZYAN_STATUS_SUCCESS;
+ }
+ }
+
+ vector->capacity = capacity;
+ ZYAN_CHECK(vector->allocator->reallocate(vector->allocator, &vector->data,
+ vector->element_size, vector->capacity));
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/**
+ * Shifts all elements starting at the specified `index` by the amount of
+ * `count` to the left.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param index The start index.
+ * @param count The amount of shift operations.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus ZyanVectorShiftLeft(ZyanVector* vector, ZyanUSize index, ZyanUSize count)
+{
+ ZYAN_ASSERT(vector);
+ ZYAN_ASSERT(vector->element_size);
+ ZYAN_ASSERT(vector->data);
+ ZYAN_ASSERT(count > 0);
+ //ZYAN_ASSERT((ZyanISize)count - (ZyanISize)index + 1 >= 0);
+
+ void* const source = ZYCORE_VECTOR_OFFSET(vector, index + count);
+ void* const dest = ZYCORE_VECTOR_OFFSET(vector, index);
+ const ZyanUSize size = (vector->size - index - count) * vector->element_size;
+ ZYAN_MEMMOVE(dest, source, size);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/**
+ * Shifts all elements starting at the specified `index` by the amount of
+ * `count` to the right.
+ *
+ * @param vector A pointer to the `ZyanVector` instance.
+ * @param index The start index.
+ * @param count The amount of shift operations.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus ZyanVectorShiftRight(ZyanVector* vector, ZyanUSize index, ZyanUSize count)
+{
+ ZYAN_ASSERT(vector);
+ ZYAN_ASSERT(vector->element_size);
+ ZYAN_ASSERT(vector->data);
+ ZYAN_ASSERT(count > 0);
+ ZYAN_ASSERT(vector->size + count <= vector->capacity);
+
+ void* const source = ZYCORE_VECTOR_OFFSET(vector, index);
+ void* const dest = ZYCORE_VECTOR_OFFSET(vector, index + count);
+ const ZyanUSize size = (vector->size - index) * vector->element_size;
+ ZYAN_MEMMOVE(dest, source, size);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Constructor and destructor */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYAN_NO_LIBC
+
+ZyanStatus ZyanVectorInit(ZyanVector* vector, ZyanUSize element_size, ZyanUSize capacity,
+ ZyanMemberProcedure destructor)
+{
+ return ZyanVectorInitEx(vector, element_size, capacity, destructor, ZyanAllocatorDefault(),
+ ZYAN_VECTOR_DEFAULT_GROWTH_FACTOR, ZYAN_VECTOR_DEFAULT_SHRINK_THRESHOLD);
+}
+
+#endif // ZYAN_NO_LIBC
+
+ZyanStatus ZyanVectorInitEx(ZyanVector* vector, ZyanUSize element_size, ZyanUSize capacity,
+ ZyanMemberProcedure destructor, ZyanAllocator* allocator, float growth_factor,
+ float shrink_threshold)
+{
+ if (!vector || !element_size || !allocator || (growth_factor < 1.0f) ||
+ (shrink_threshold < 0.0f) || (shrink_threshold > 1.0f))
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZYAN_ASSERT(allocator->allocate);
+
+ vector->allocator = allocator;
+ vector->growth_factor = growth_factor;
+ vector->shrink_threshold = shrink_threshold;
+ vector->size = 0;
+ vector->capacity = ZYAN_MAX(ZYAN_VECTOR_MIN_CAPACITY, capacity);
+ vector->element_size = element_size;
+ vector->destructor = destructor;
+ vector->data = ZYAN_NULL;
+
+ return allocator->allocate(vector->allocator, &vector->data, vector->element_size,
+ vector->capacity);
+}
+
+ZyanStatus ZyanVectorInitCustomBuffer(ZyanVector* vector, ZyanUSize element_size,
+ void* buffer, ZyanUSize capacity, ZyanMemberProcedure destructor)
+{
+ if (!vector || !element_size || !buffer || !capacity)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ vector->allocator = ZYAN_NULL;
+ vector->growth_factor = 1.0f;
+ vector->shrink_threshold = 0.0f;
+ vector->size = 0;
+ vector->capacity = capacity;
+ vector->element_size = element_size;
+ vector->destructor = destructor;
+ vector->data = buffer;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanVectorDestroy(ZyanVector* vector)
+{
+ if (!vector)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZYAN_ASSERT(vector->element_size);
+ ZYAN_ASSERT(vector->data);
+
+ if (vector->destructor)
+ {
+ for (ZyanUSize i = 0; i < vector->size; ++i)
+ {
+ vector->destructor(ZYCORE_VECTOR_OFFSET(vector, i));
+ }
+ }
+
+ if (vector->allocator && vector->capacity)
+ {
+ ZYAN_ASSERT(vector->allocator->deallocate);
+ ZYAN_CHECK(vector->allocator->deallocate(vector->allocator, vector->data,
+ vector->element_size, vector->capacity));
+ }
+
+ vector->data = ZYAN_NULL;
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Duplication */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYAN_NO_LIBC
+
+ZyanStatus ZyanVectorDuplicate(ZyanVector* destination, const ZyanVector* source,
+ ZyanUSize capacity)
+{
+ return ZyanVectorDuplicateEx(destination, source, capacity, ZyanAllocatorDefault(),
+ ZYAN_VECTOR_DEFAULT_GROWTH_FACTOR, ZYAN_VECTOR_DEFAULT_SHRINK_THRESHOLD);
+}
+
+#endif // ZYAN_NO_LIBC
+
+ZyanStatus ZyanVectorDuplicateEx(ZyanVector* destination, const ZyanVector* source,
+ ZyanUSize capacity, ZyanAllocator* allocator, float growth_factor, float shrink_threshold)
+{
+ if (!source)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ const ZyanUSize len = source->size;
+
+ capacity = ZYAN_MAX(capacity, len);
+ ZYAN_CHECK(ZyanVectorInitEx(destination, source->element_size, capacity, source->destructor,
+ allocator, growth_factor, shrink_threshold));
+ ZYAN_ASSERT(destination->capacity >= len);
+
+ ZYAN_MEMCPY(destination->data, source->data, len * source->element_size);
+ destination->size = len;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanVectorDuplicateCustomBuffer(ZyanVector* destination, const ZyanVector* source,
+ void* buffer, ZyanUSize capacity)
+{
+ if (!source)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ const ZyanUSize len = source->size;
+
+ if (capacity < len)
+ {
+ return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
+ }
+
+ ZYAN_CHECK(ZyanVectorInitCustomBuffer(destination, source->element_size, buffer, capacity,
+ source->destructor));
+ ZYAN_ASSERT(destination->capacity >= len);
+
+ ZYAN_MEMCPY(destination->data, source->data, len * source->element_size);
+ destination->size = len;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Element access */
+/* ---------------------------------------------------------------------------------------------- */
+
+const void* ZyanVectorGet(const ZyanVector* vector, ZyanUSize index)
+{
+ if (!vector || (index >= vector->size))
+ {
+ return ZYAN_NULL;
+ }
+
+ ZYAN_ASSERT(vector->element_size);
+ ZYAN_ASSERT(vector->data);
+
+ return ZYCORE_VECTOR_OFFSET(vector, index);
+}
+
+void* ZyanVectorGetMutable(const ZyanVector* vector, ZyanUSize index)
+{
+ if (!vector || (index >= vector->size))
+ {
+ return ZYAN_NULL;
+ }
+
+ ZYAN_ASSERT(vector->element_size);
+ ZYAN_ASSERT(vector->data);
+
+ return ZYCORE_VECTOR_OFFSET(vector, index);
+}
+
+ZyanStatus ZyanVectorGetPointer(const ZyanVector* vector, ZyanUSize index, const void** value)
+{
+ if (!vector || !value)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (index >= vector->size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ ZYAN_ASSERT(vector->element_size);
+ ZYAN_ASSERT(vector->data);
+
+ *value = (const void*)ZYCORE_VECTOR_OFFSET(vector, index);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanVectorGetPointerMutable(const ZyanVector* vector, ZyanUSize index, void** value)
+{
+ if (!vector || !value)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (index >= vector->size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ ZYAN_ASSERT(vector->element_size);
+ ZYAN_ASSERT(vector->data);
+
+ *value = ZYCORE_VECTOR_OFFSET(vector, index);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanVectorSet(ZyanVector* vector, ZyanUSize index, const void* value)
+{
+ if (!vector || !value)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (index >= vector->size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ ZYAN_ASSERT(vector->element_size);
+ ZYAN_ASSERT(vector->data);
+
+ void* const offset = ZYCORE_VECTOR_OFFSET(vector, index);
+ if (vector->destructor)
+ {
+ vector->destructor(offset);
+ }
+ ZYAN_MEMCPY(offset, value, vector->element_size);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Insertion */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanVectorPushBack(ZyanVector* vector, const void* element)
+{
+ if (!vector || !element)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZYAN_ASSERT(vector->element_size);
+ ZYAN_ASSERT(vector->data);
+
+ if (ZYCORE_VECTOR_SHOULD_GROW(vector->size + 1, vector->capacity))
+ {
+ ZYAN_CHECK(ZyanVectorReallocate(vector,
+ ZYAN_MAX(1, (ZyanUSize)((vector->size + 1) * vector->growth_factor))));
+ }
+
+ void* const offset = ZYCORE_VECTOR_OFFSET(vector, vector->size);
+ ZYAN_MEMCPY(offset, element, vector->element_size);
+
+ ++vector->size;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanVectorInsert(ZyanVector* vector, ZyanUSize index, const void* element)
+{
+ return ZyanVectorInsertRange(vector, index, element, 1);
+}
+
+ZyanStatus ZyanVectorInsertRange(ZyanVector* vector, ZyanUSize index, const void* elements,
+ ZyanUSize count)
+{
+ if (!vector || !elements || !count)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (index > vector->size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ ZYAN_ASSERT(vector->element_size);
+ ZYAN_ASSERT(vector->data);
+
+ if (ZYCORE_VECTOR_SHOULD_GROW(vector->size + count, vector->capacity))
+ {
+ ZYAN_CHECK(ZyanVectorReallocate(vector,
+ ZYAN_MAX(1, (ZyanUSize)((vector->size + count) * vector->growth_factor))));
+ }
+
+ if (index < vector->size)
+ {
+ ZYAN_CHECK(ZyanVectorShiftRight(vector, index, count));
+ }
+
+ void* const offset = ZYCORE_VECTOR_OFFSET(vector, index);
+ ZYAN_MEMCPY(offset, elements, count * vector->element_size);
+ vector->size += count;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanVectorEmplace(ZyanVector* vector, void** element, ZyanMemberFunction constructor)
+{
+ if (!vector)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ZyanVectorEmplaceEx(vector, vector->size, element, constructor);
+}
+
+ZyanStatus ZyanVectorEmplaceEx(ZyanVector* vector, ZyanUSize index, void** element,
+ ZyanMemberFunction constructor)
+{
+ if (!vector)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (index > vector->size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ ZYAN_ASSERT(vector->element_size);
+ ZYAN_ASSERT(vector->data);
+
+ if (ZYCORE_VECTOR_SHOULD_GROW(vector->size + 1, vector->capacity))
+ {
+ ZYAN_CHECK(ZyanVectorReallocate(vector,
+ ZYAN_MAX(1, (ZyanUSize)((vector->size + 1) * vector->growth_factor))));
+ }
+
+ if (index < vector->size)
+ {
+ ZYAN_CHECK(ZyanVectorShiftRight(vector, index, 1));
+ }
+
+ *element = ZYCORE_VECTOR_OFFSET(vector, index);
+ if (constructor)
+ {
+ ZYAN_CHECK(constructor(*element));
+ }
+
+ ++vector->size;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Utils */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanVectorSwapElements(ZyanVector* vector, ZyanUSize index_first, ZyanUSize index_second)
+{
+ if (!vector)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if ((index_first >= vector->size) || (index_second >= vector->size))
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ if (vector->size == vector->capacity)
+ {
+ return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
+ }
+
+ ZYAN_ASSERT(vector->element_size);
+ ZYAN_ASSERT(vector->data);
+
+ ZyanU64* const t = ZYCORE_VECTOR_OFFSET(vector, vector->size);
+ ZyanU64* const a = ZYCORE_VECTOR_OFFSET(vector, index_first);
+ ZyanU64* const b = ZYCORE_VECTOR_OFFSET(vector, index_second);
+ ZYAN_MEMCPY(t, a, vector->element_size);
+ ZYAN_MEMCPY(a, b, vector->element_size);
+ ZYAN_MEMCPY(b, t, vector->element_size);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Deletion */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanVectorDelete(ZyanVector* vector, ZyanUSize index)
+{
+ return ZyanVectorDeleteRange(vector, index, 1);
+}
+
+ZyanStatus ZyanVectorDeleteRange(ZyanVector* vector, ZyanUSize index, ZyanUSize count)
+{
+ if (!vector || !count)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (index + count > vector->size)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ if (vector->destructor)
+ {
+ for (ZyanUSize i = index; i < index + count; ++i)
+ {
+ vector->destructor(ZYCORE_VECTOR_OFFSET(vector, i));
+ }
+ }
+
+ if (index + count < vector->size)
+ {
+ ZYAN_CHECK(ZyanVectorShiftLeft(vector, index, count));
+ }
+
+ vector->size -= count;
+ if (ZYCORE_VECTOR_SHOULD_SHRINK(vector->size, vector->capacity, vector->shrink_threshold))
+ {
+ return ZyanVectorReallocate(vector,
+ ZYAN_MAX(1, (ZyanUSize)(vector->size * vector->growth_factor)));
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanVectorPopBack(ZyanVector* vector)
+{
+ if (!vector)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (vector->size == 0)
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ if (vector->destructor)
+ {
+ vector->destructor(ZYCORE_VECTOR_OFFSET(vector, vector->size - 1));
+ }
+
+ --vector->size;
+ if (ZYCORE_VECTOR_SHOULD_SHRINK(vector->size, vector->capacity, vector->shrink_threshold))
+ {
+ return ZyanVectorReallocate(vector,
+ ZYAN_MAX(1, (ZyanUSize)(vector->size * vector->growth_factor)));
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanVectorClear(ZyanVector* vector)
+{
+ return ZyanVectorResizeEx(vector, 0, ZYAN_NULL);
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Searching */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanVectorFind(const ZyanVector* vector, const void* element, ZyanISize* found_index,
+ ZyanEqualityComparison comparison)
+{
+ if (!vector)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ZyanVectorFindEx(vector, element, found_index, comparison, 0, vector->size);
+}
+
+ZyanStatus ZyanVectorFindEx(const ZyanVector* vector, const void* element, ZyanISize* found_index,
+ ZyanEqualityComparison comparison, ZyanUSize index, ZyanUSize count)
+{
+ if (!vector)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if ((index + count > vector->size) || (index == vector->size))
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ if (!count)
+ {
+ *found_index = -1;
+ return ZYAN_STATUS_FALSE;
+ }
+
+ ZYAN_ASSERT(vector->element_size);
+ ZYAN_ASSERT(vector->data);
+
+ for (ZyanUSize i = index; i < index + count; ++i)
+ {
+ if (comparison(ZYCORE_VECTOR_OFFSET(vector, i), element))
+ {
+ *found_index = i;
+ return ZYAN_STATUS_TRUE;
+ }
+ }
+
+ *found_index = -1;
+ return ZYAN_STATUS_FALSE;
+}
+
+ZyanStatus ZyanVectorBinarySearch(const ZyanVector* vector, const void* element,
+ ZyanUSize* found_index, ZyanComparison comparison)
+{
+ if (!vector)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ZyanVectorBinarySearchEx(vector, element, found_index, comparison, 0, vector->size);
+}
+
+ZyanStatus ZyanVectorBinarySearchEx(const ZyanVector* vector, const void* element,
+ ZyanUSize* found_index, ZyanComparison comparison, ZyanUSize index, ZyanUSize count)
+{
+ if (!vector)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (((index >= vector->size) && (count > 0)) || (index + count > vector->size))
+ {
+ return ZYAN_STATUS_OUT_OF_RANGE;
+ }
+
+ if (!count)
+ {
+ *found_index = index;
+ return ZYAN_STATUS_FALSE;
+ }
+
+ ZYAN_ASSERT(vector->element_size);
+ ZYAN_ASSERT(vector->data);
+
+ ZyanStatus status = ZYAN_STATUS_FALSE;
+ ZyanISize l = index;
+ ZyanISize h = index + count - 1;
+ while (l <= h)
+ {
+ const ZyanUSize mid = l + ((h - l) >> 1);
+ const ZyanI32 cmp = comparison(ZYCORE_VECTOR_OFFSET(vector, mid), element);
+ if (cmp < 0)
+ {
+ l = mid + 1;
+ } else
+ {
+ h = mid - 1;
+ if (cmp == 0)
+ {
+ status = ZYAN_STATUS_TRUE;
+ }
+ }
+ }
+
+ *found_index = l;
+ return status;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Memory management */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanVectorResize(ZyanVector* vector, ZyanUSize size)
+{
+ return ZyanVectorResizeEx(vector, size, ZYAN_NULL);
+}
+
+ZyanStatus ZyanVectorResizeEx(ZyanVector* vector, ZyanUSize size, const void* initializer)
+{
+ if (!vector)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (size == vector->size)
+ {
+ return ZYAN_STATUS_SUCCESS;
+ }
+
+ if (vector->destructor && (size < vector->size))
+ {
+ for (ZyanUSize i = size; i < vector->size; ++i)
+ {
+ vector->destructor(ZYCORE_VECTOR_OFFSET(vector, i));
+ }
+ }
+
+ if (ZYCORE_VECTOR_SHOULD_GROW(size, vector->capacity) ||
+ ZYCORE_VECTOR_SHOULD_SHRINK(size, vector->capacity, vector->shrink_threshold))
+ {
+ ZYAN_CHECK(ZyanVectorReallocate(vector, (ZyanUSize)(size * vector->growth_factor)));
+ };
+
+ if (initializer && (size > vector->size))
+ {
+ for (ZyanUSize i = vector->size; i < size; ++i)
+ {
+ ZYAN_MEMCPY(ZYCORE_VECTOR_OFFSET(vector, i), initializer, vector->element_size);
+ }
+ }
+
+ vector->size = size;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanVectorReserve(ZyanVector* vector, ZyanUSize capacity)
+{
+ if (!vector)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (capacity > vector->capacity)
+ {
+ ZYAN_CHECK(ZyanVectorReallocate(vector, capacity));
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanVectorShrinkToFit(ZyanVector* vector)
+{
+ if (!vector)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ZyanVectorReallocate(vector, vector->size);
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Information */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZyanVectorGetCapacity(const ZyanVector* vector, ZyanUSize* capacity)
+{
+ if (!vector)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ *capacity = vector->capacity;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZyanVectorGetSize(const ZyanVector* vector, ZyanUSize* size)
+{
+ if (!vector)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ *size = vector->size;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
diff --git a/externals/zycore/src/Zycore.c b/externals/zycore/src/Zycore.c
new file mode 100644
index 00000000..9bbb2002
--- /dev/null
+++ b/externals/zycore/src/Zycore.c
@@ -0,0 +1,38 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+#include <Zycore/Zycore.h>
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+ZyanU64 ZycoreGetVersion(void)
+{
+ return ZYCORE_VERSION;
+}
+
+/* ============================================================================================== */
diff --git a/externals/zycore/tests/ArgParse.cpp b/externals/zycore/tests/ArgParse.cpp
new file mode 100644
index 00000000..612a5dd9
--- /dev/null
+++ b/externals/zycore/tests/ArgParse.cpp
@@ -0,0 +1,320 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Joel Hoener
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * @brief Tests the the arg parse implementation.
+ */
+
+#include <string_view>
+
+#include <gtest/gtest.h>
+#include <Zycore/ArgParse.h>
+#include <Zycore/LibC.h>
+
+/* ============================================================================================== */
+/* Helpers */
+/* ============================================================================================== */
+
+auto cvt_string_view(const ZyanStringView *sv)
+{
+ const char* buf;
+ if (ZYAN_FAILED(ZyanStringViewGetData(sv, &buf))) throw std::exception{};
+ ZyanUSize len;
+ if (ZYAN_FAILED(ZyanStringViewGetSize(sv, &len))) throw std::exception{};
+
+ return std::string_view{buf, len};
+}
+
+/* ============================================================================================== */
+/* Tests */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Unnamed args */
+/* ---------------------------------------------------------------------------------------------- */
+
+static auto UnnamedArgTest(ZyanU64 min, ZyanU64 max)
+{
+ const char* argv[]
+ {
+ "./test", "a", "xxx"
+ };
+
+ ZyanArgParseConfig cfg
+ {
+ argv, // argv
+ 3, // argc
+ min, // min_unnamed_args
+ max, // max_unnamed_args
+ nullptr // args
+ };
+
+ ZyanVector parsed;
+ const char* err_tok = nullptr;
+ ZYAN_MEMSET(&parsed, 0, sizeof(parsed));
+ auto status = ZyanArgParse(&cfg, &parsed, &err_tok);
+ return std::make_tuple(status, parsed, err_tok);
+}
+
+TEST(UnnamedArgs, TooFew)
+{
+ auto [status, parsed, err_tok] = UnnamedArgTest(5, 5);
+ ASSERT_EQ(status, ZYAN_STATUS_TOO_FEW_ARGS);
+ ASSERT_STREQ(err_tok, nullptr);
+}
+
+TEST(UnnamedArgs, TooMany)
+{
+ auto [status, parsed, err_tok] = UnnamedArgTest(1, 1);
+ ASSERT_EQ(status, ZYAN_STATUS_TOO_MANY_ARGS);
+ ASSERT_STREQ(err_tok, "xxx");
+}
+
+TEST(UnnamedArgs, PerfectFit)
+{
+ auto [status, parsed, err_tok] = UnnamedArgTest(2, 2);
+ ASSERT_TRUE(ZYAN_SUCCESS(status));
+
+ ZyanUSize size;
+ ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetSize(&parsed, &size)));
+ ASSERT_EQ(size, 2);
+
+ auto arg = (const ZyanArgParseArg*)ZyanVectorGet(&parsed, 0);
+ ASSERT_NE(arg, nullptr);
+ ASSERT_TRUE(arg->has_value);
+ ASSERT_EQ(cvt_string_view(&arg->value), "a");
+
+ arg = (const ZyanArgParseArg*)ZyanVectorGet(&parsed, 1);
+ ASSERT_NE(arg, nullptr);
+ ASSERT_TRUE(arg->has_value);
+ ASSERT_EQ(cvt_string_view(&arg->value), "xxx");
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Dash args */
+/* ---------------------------------------------------------------------------------------------- */
+
+TEST(DashArg, MixedBoolAndValueArgs)
+{
+ const char* argv[]
+ {
+ "./test", "-aio42", "-n", "xxx"
+ };
+
+ ZyanArgParseDefinition args[]
+ {
+ {"-o", ZYAN_FALSE, ZYAN_FALSE},
+ {"-a", ZYAN_TRUE, ZYAN_FALSE},
+ {"-n", ZYAN_FALSE, ZYAN_FALSE},
+ {"-i", ZYAN_TRUE, ZYAN_FALSE},
+ {nullptr, ZYAN_FALSE, ZYAN_FALSE}
+ };
+
+ ZyanArgParseConfig cfg
+ {
+ argv, // argv
+ 4, // argc
+ 0, // min_unnamed_args
+ 0, // max_unnamed_args
+ args // args
+ };
+
+ ZyanVector parsed;
+ ZYAN_MEMSET(&parsed, 0, sizeof(parsed));
+ auto status = ZyanArgParse(&cfg, &parsed, nullptr);
+ ASSERT_TRUE(ZYAN_SUCCESS(status));
+
+ ZyanUSize size;
+ ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetSize(&parsed, &size)));
+ ASSERT_EQ(size, 4);
+
+ const ZyanArgParseArg* arg;
+ ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 0, (const void**)&arg)));
+ ASSERT_STREQ(arg->def->name, "-a");
+ ASSERT_FALSE(arg->has_value);
+
+ ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 1, (const void**)&arg)));
+ ASSERT_STREQ(arg->def->name, "-i");
+ ASSERT_FALSE(arg->has_value);
+
+ ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 2, (const void**)&arg)));
+ ASSERT_STREQ(arg->def->name, "-o");
+ ASSERT_TRUE(arg->has_value);
+ ASSERT_EQ(cvt_string_view(&arg->value), "42");
+
+ ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 3, (const void**)&arg)));
+ ASSERT_STREQ(arg->def->name, "-n");
+ ASSERT_TRUE(arg->has_value);
+ ASSERT_EQ(cvt_string_view(&arg->value), "xxx");
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Double dash args */
+/* ---------------------------------------------------------------------------------------------- */
+
+TEST(DoubleDashArg, PerfectFit)
+{
+ const char* argv[]
+ {
+ "./test", "--help", "--stuff", "1337"
+ };
+
+ ZyanArgParseDefinition args[]
+ {
+ {"--help", ZYAN_TRUE, ZYAN_FALSE},
+ {"--stuff", ZYAN_FALSE, ZYAN_FALSE},
+ {nullptr, ZYAN_FALSE, ZYAN_FALSE}
+ };
+
+ ZyanArgParseConfig cfg
+ {
+ argv, // argv
+ 4, // argc
+ 0, // min_unnamed_args
+ 0, // max_unnamed_args
+ args // args
+ };
+
+ ZyanVector parsed;
+ ZYAN_MEMSET(&parsed, 0, sizeof(parsed));
+ auto status = ZyanArgParse(&cfg, &parsed, nullptr);
+ ASSERT_TRUE(ZYAN_SUCCESS(status));
+
+ ZyanUSize size;
+ ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetSize(&parsed, &size)));
+ ASSERT_EQ(size, 2);
+
+ const ZyanArgParseArg* arg;
+ ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 0, (const void**)&arg)));
+ ASSERT_STREQ(arg->def->name, "--help");
+ ASSERT_FALSE(arg->has_value);
+
+ ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 1, (const void**)&arg)));
+ ASSERT_STREQ(arg->def->name, "--stuff");
+ ASSERT_TRUE(arg->has_value);
+ ASSERT_EQ(cvt_string_view(&arg->value), "1337");
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Mixed */
+/* ---------------------------------------------------------------------------------------------- */
+
+TEST(MixedArgs, MissingRequiredArg)
+{
+ const char* argv[]
+ {
+ "./test", "blah.c", "woof.moo"
+ };
+
+ ZyanArgParseDefinition args[]
+ {
+ {"--feature-xyz", ZYAN_TRUE, ZYAN_FALSE},
+ {"-n", ZYAN_FALSE, ZYAN_TRUE},
+ {nullptr, ZYAN_FALSE, ZYAN_FALSE}
+ };
+
+ ZyanArgParseConfig cfg
+ {
+ argv, // argv
+ 3, // argc
+ 0, // min_unnamed_args
+ 100, // max_unnamed_args
+ args // args
+ };
+
+ ZyanVector parsed;
+ ZYAN_MEMSET(&parsed, 0, sizeof(parsed));
+ const char* err_tok = nullptr;
+ auto status = ZyanArgParse(&cfg, &parsed, &err_tok);
+ ASSERT_EQ(status, ZYAN_STATUS_REQUIRED_ARG_MISSING);
+ ASSERT_STREQ(err_tok, "-n");
+}
+
+TEST(MixedArgs, Stuff)
+{
+ const char* argv[]
+ {
+ "./test", "--feature-xyz", "-n5", "blah.c", "woof.moo"
+ };
+
+ ZyanArgParseDefinition args[]
+ {
+ {"--feature-xyz", ZYAN_TRUE, ZYAN_FALSE},
+ {"-n", ZYAN_FALSE, ZYAN_FALSE},
+ {nullptr, ZYAN_FALSE, ZYAN_FALSE}
+ };
+
+ ZyanArgParseConfig cfg
+ {
+ argv, // argv
+ 5, // argc
+ 0, // min_unnamed_args
+ 100, // max_unnamed_args
+ args // args
+ };
+
+ ZyanVector parsed;
+ ZYAN_MEMSET(&parsed, 0, sizeof(parsed));
+ auto status = ZyanArgParse(&cfg, &parsed, nullptr);
+ ASSERT_TRUE(ZYAN_SUCCESS(status));
+
+ ZyanUSize size;
+ ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetSize(&parsed, &size)));
+ ASSERT_EQ(size, 4);
+
+ const ZyanArgParseArg* arg;
+ ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 0, (const void**)&arg)));
+ ASSERT_STREQ(arg->def->name, "--feature-xyz");
+ ASSERT_FALSE(arg->has_value);
+
+ ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 1, (const void**)&arg)));
+ ASSERT_STREQ(arg->def->name, "-n");
+ ASSERT_TRUE(arg->has_value);
+ ASSERT_EQ(cvt_string_view(&arg->value), "5");
+
+ ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 2, (const void**)&arg)));
+ ASSERT_EQ(arg->def, nullptr);
+ ASSERT_TRUE(arg->has_value);
+ ASSERT_EQ(cvt_string_view(&arg->value), "blah.c");
+
+ ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 3, (const void**)&arg)));
+ ASSERT_EQ(arg->def, nullptr);
+ ASSERT_TRUE(arg->has_value);
+ ASSERT_EQ(cvt_string_view(&arg->value), "woof.moo");
+}
+
+/* ============================================================================================== */
+/* Entry point */
+/* ============================================================================================== */
+
+int main(int argc, char **argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+/* ============================================================================================== */
diff --git a/externals/zycore/tests/String.cpp b/externals/zycore/tests/String.cpp
new file mode 100644
index 00000000..3c00fb99
--- /dev/null
+++ b/externals/zycore/tests/String.cpp
@@ -0,0 +1,69 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * @brief Tests the `ZyanString` implementation.
+ */
+
+#include <gtest/gtest.h>
+#include <Zycore/String.h>
+
+/* ============================================================================================== */
+/* Enums and types */
+/* ============================================================================================== */
+
+
+
+/* ============================================================================================== */
+/* Helper functions */
+/* ============================================================================================== */
+
+
+
+/* ============================================================================================== */
+/* Tests */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* */
+/* ---------------------------------------------------------------------------------------------- */
+
+
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Entry point */
+/* ============================================================================================== */
+
+int main(int argc, char **argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+/* ============================================================================================== */
diff --git a/externals/zycore/tests/Vector.cpp b/externals/zycore/tests/Vector.cpp
new file mode 100644
index 00000000..ade6b09f
--- /dev/null
+++ b/externals/zycore/tests/Vector.cpp
@@ -0,0 +1,505 @@
+/***************************************************************************************************
+
+ Zyan Core Library (Zycore-C)
+
+ Original Author : Florian Bernd
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+/**
+ * @file
+ * @brief Tests the `ZyanVector` implementation.
+ */
+
+#include <time.h>
+#include <gtest/gtest.h>
+#include <Zycore/Comparison.h>
+#include <Zycore/Vector.h>
+
+/* ============================================================================================== */
+/* Fixtures */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* VectorTestBase */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * @brief Implements a fixture-class that provides an initialized `ZyanVector` instance for
+ * `ZyanU64` values.
+ */
+class VectorTestBase : public ::testing::TestWithParam<bool>
+{
+protected:
+ static const ZyanUSize m_test_size = 100;
+ ZyanBool m_has_fixed_capacity;
+ ZyanVector m_vector;
+ std::vector<ZyanU64> m_buffer;
+protected:
+ void SetUp() override
+ {
+ m_has_fixed_capacity = GetParam();
+
+ if (!m_has_fixed_capacity)
+ {
+ ASSERT_EQ(ZyanVectorInit(&m_vector, sizeof(ZyanU64), m_test_size,
+ reinterpret_cast<ZyanMemberProcedure>(ZYAN_NULL)), ZYAN_STATUS_SUCCESS);
+ } else
+ {
+ m_buffer.reserve(m_test_size);
+ ASSERT_EQ(ZyanVectorInitCustomBuffer(&m_vector, sizeof(ZyanU64), m_buffer.data(),
+ m_test_size, reinterpret_cast<ZyanMemberProcedure>(ZYAN_NULL)),
+ ZYAN_STATUS_SUCCESS);
+ }
+ }
+
+ void TearDown() override
+ {
+ EXPECT_EQ(ZyanVectorDestroy(&m_vector), ZYAN_STATUS_SUCCESS);
+ }
+};
+
+/* ---------------------------------------------------------------------------------------------- */
+/* VectorTestFilled */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * @brief Implements a fixture-class that provides an initialized `ZyanVector` instance which
+ * is filled with `ZyanU64` values from 0..100.
+ */
+class VectorTestFilled : public VectorTestBase
+{
+protected:
+ void SetUp() override
+ {
+ VectorTestBase::SetUp();
+
+ if (m_has_fixed_capacity)
+ {
+ m_buffer.resize(m_test_size);
+ }
+ for (ZyanU64 i = 0; i < m_test_size; ++i)
+ {
+ ASSERT_EQ(ZyanVectorPushBack(&m_vector, &i), ZYAN_STATUS_SUCCESS);
+ }
+ }
+};
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Helper functions */
+/* ============================================================================================== */
+
+/**
+ * @brief A dummy constructor for `ZyanU64` objects.
+ *
+ * @param object A pointer to the object.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus InitZyanU64(ZyanU64* object)
+{
+ *object = 1337;
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/**
+ * @brief A dummy destructor for `ZyanU16` objects.
+ *
+ * @param object A pointer to the object.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus FreeZyanU16(ZyanU16* object)
+{
+ *object = 0;
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ============================================================================================== */
+/* Tests */
+/* ============================================================================================== */
+
+TEST(VectorTest, InitBasic)
+{
+ ZyanVector vector;
+
+ ASSERT_EQ(ZyanVectorInit(&vector, sizeof(ZyanU64), 0,
+ reinterpret_cast<ZyanMemberProcedure>(ZYAN_NULL)), ZYAN_STATUS_SUCCESS);
+ EXPECT_EQ(vector.allocator, ZyanAllocatorDefault());
+ EXPECT_FLOAT_EQ(vector.growth_factor, ZYAN_VECTOR_DEFAULT_GROWTH_FACTOR);
+ EXPECT_FLOAT_EQ(vector.shrink_threshold, ZYAN_VECTOR_DEFAULT_SHRINK_THRESHOLD);
+ EXPECT_EQ(vector.size, static_cast<ZyanUSize>(0));
+ EXPECT_EQ(vector.capacity, static_cast<ZyanUSize>(ZYAN_VECTOR_MIN_CAPACITY));
+ EXPECT_EQ(vector.element_size, sizeof(ZyanU64));
+ EXPECT_NE(vector.data, ZYAN_NULL);
+ EXPECT_EQ(ZyanVectorDestroy(&vector), ZYAN_STATUS_SUCCESS);
+
+ // Custom capacity
+ EXPECT_EQ(ZyanVectorInit(&vector, sizeof(ZyanU16), 10,
+ reinterpret_cast<ZyanMemberProcedure>(ZYAN_NULL)), ZYAN_STATUS_SUCCESS);
+ EXPECT_EQ(vector.capacity, static_cast<ZyanUSize>(ZYAN_MAX(ZYAN_VECTOR_MIN_CAPACITY, 10)));
+ EXPECT_EQ(ZyanVectorDestroy(&vector), ZYAN_STATUS_SUCCESS);
+}
+
+TEST(VectorTest, InitAdvanced)
+{
+ ZyanVector vector;
+
+ ASSERT_EQ(ZyanVectorInitEx(&vector, sizeof(ZyanU16), 0,
+ reinterpret_cast<ZyanMemberProcedure>(ZYAN_NULL), ZyanAllocatorDefault(), 1.0f, 0.0f),
+ ZYAN_STATUS_SUCCESS);
+ EXPECT_EQ(vector.allocator, ZyanAllocatorDefault());
+ EXPECT_FLOAT_EQ(vector.growth_factor, 1.0f);
+ EXPECT_FLOAT_EQ(vector.shrink_threshold, 0.0f);
+ EXPECT_EQ(vector.size, static_cast<ZyanUSize>(0));
+ EXPECT_EQ(vector.capacity, static_cast<ZyanUSize>(ZYAN_VECTOR_MIN_CAPACITY));
+ EXPECT_EQ(vector.element_size, sizeof(ZyanU16));
+ EXPECT_NE(vector.data, ZYAN_NULL);
+ EXPECT_EQ(ZyanVectorDestroy(&vector), ZYAN_STATUS_SUCCESS);
+
+ // Custom capacity
+ EXPECT_EQ(ZyanVectorInitEx(&vector, sizeof(ZyanU16), 10,
+ reinterpret_cast<ZyanMemberProcedure>(ZYAN_NULL), ZyanAllocatorDefault(), 1.0f, 0.0f),
+ ZYAN_STATUS_SUCCESS);
+ EXPECT_EQ(vector.capacity, static_cast<ZyanUSize>(ZYAN_MAX(ZYAN_VECTOR_MIN_CAPACITY, 10)));
+ EXPECT_EQ(ZyanVectorDestroy(&vector), ZYAN_STATUS_SUCCESS);
+}
+
+TEST(VectorTest, InitCustomBuffer)
+{
+ ZyanVector vector;
+
+ ZyanU16 buffer[32];
+ EXPECT_EQ(ZyanVectorInitCustomBuffer(&vector, sizeof(ZyanU16), &buffer, 0,
+ reinterpret_cast<ZyanMemberProcedure>(ZYAN_NULL)), ZYAN_STATUS_INVALID_ARGUMENT);
+ ASSERT_EQ(ZyanVectorInitCustomBuffer(&vector, sizeof(ZyanU16), &buffer,
+ ZYAN_ARRAY_LENGTH(buffer), reinterpret_cast<ZyanMemberProcedure>(ZYAN_NULL)),
+ ZYAN_STATUS_SUCCESS);
+ EXPECT_EQ(vector.allocator, ZYAN_NULL);
+ EXPECT_FLOAT_EQ(vector.growth_factor, 1.0f);
+ EXPECT_FLOAT_EQ(vector.shrink_threshold, 0.0f);
+ EXPECT_EQ(vector.size, static_cast<ZyanUSize>(0));
+ EXPECT_EQ(vector.capacity, ZYAN_ARRAY_LENGTH(buffer));
+ EXPECT_EQ(vector.element_size, sizeof(ZyanU16));
+ EXPECT_EQ(vector.data, &buffer);
+ EXPECT_EQ(ZyanVectorDestroy(&vector), ZYAN_STATUS_SUCCESS);
+}
+
+TEST(VectorTest, Destructor)
+{
+ ZyanVector vector;
+
+ ZyanU16 buffer[16];
+ ASSERT_EQ(ZyanVectorInitCustomBuffer(&vector, sizeof(ZyanU16), &buffer,
+ ZYAN_ARRAY_LENGTH(buffer), reinterpret_cast<ZyanMemberProcedure>(&FreeZyanU16)),
+ ZYAN_STATUS_SUCCESS);
+
+ for (ZyanUSize i = 0; i < ZYAN_ARRAY_LENGTH(buffer); ++i)
+ {
+ const auto element = static_cast<ZyanU16>(i) + 0;
+ ASSERT_EQ(ZyanVectorPushBack(&vector, &element), ZYAN_STATUS_SUCCESS);
+ ASSERT_EQ(buffer[i], element);
+ }
+
+ ASSERT_EQ(ZyanVectorPopBack(&vector), ZYAN_STATUS_SUCCESS);
+ ASSERT_EQ(buffer[15], 0);
+
+ ASSERT_EQ(ZyanVectorDeleteRange(&vector, 12, 3), ZYAN_STATUS_SUCCESS);
+ ASSERT_EQ(buffer[12], 0);
+ ASSERT_EQ(buffer[13], 0);
+ ASSERT_EQ(buffer[14], 0);
+
+ ASSERT_EQ(ZyanVectorClear(&vector), ZYAN_STATUS_SUCCESS);
+ for (ZyanUSize i : buffer)
+ {
+ ASSERT_EQ(i, 0);
+ }
+
+ for (ZyanUSize i = 0; i < ZYAN_ARRAY_LENGTH(buffer); ++i)
+ {
+ const auto element = static_cast<ZyanU16>(i) + 1;
+ ASSERT_EQ(ZyanVectorPushBack(&vector, &element), ZYAN_STATUS_SUCCESS);
+ ASSERT_EQ(buffer[i], element);
+ }
+
+ EXPECT_EQ(ZyanVectorDestroy(&vector), ZYAN_STATUS_SUCCESS);
+ for (ZyanUSize i : buffer)
+ {
+ ASSERT_EQ(i, 0);
+ }
+}
+
+TEST_P(VectorTestFilled, ElementAccess)
+{
+ static const ZyanU64 element_in = 1337;
+ const ZyanU64* element_dummy;
+ ZyanU64* element_out_mut;
+
+ EXPECT_EQ(ZyanVectorSet(&m_vector, m_vector.size, &element_in),
+ ZYAN_STATUS_OUT_OF_RANGE);
+ EXPECT_EQ(ZyanVectorSet(&m_vector, m_vector.size - 1, &element_in),
+ ZYAN_STATUS_SUCCESS);
+
+ EXPECT_EQ(ZyanVectorGetPointer(&m_vector, m_vector.size,
+ reinterpret_cast<const void**>(&element_dummy)), ZYAN_STATUS_OUT_OF_RANGE);
+ EXPECT_EQ(ZYAN_VECTOR_GET(ZyanU64, &m_vector, m_vector.size - 1), element_in);
+
+ EXPECT_EQ(ZyanVectorGetPointerMutable(&m_vector, m_vector.size,
+ reinterpret_cast<void**>(&element_out_mut)), ZYAN_STATUS_OUT_OF_RANGE);
+ EXPECT_EQ(ZyanVectorGetPointerMutable(&m_vector, m_vector.size - 1,
+ reinterpret_cast<void**>(&element_out_mut)), ZYAN_STATUS_SUCCESS);
+ EXPECT_EQ(*element_out_mut, element_in);
+ *element_out_mut = 42;
+ EXPECT_EQ(ZYAN_VECTOR_GET(ZyanU64, &m_vector, m_vector.size - 1), 42);
+
+ if (m_has_fixed_capacity)
+ {
+ EXPECT_EQ(ZYAN_VECTOR_GET(ZyanU64, &m_vector, m_vector.size - 1),
+ m_buffer[m_vector.size - 1]);
+ }
+}
+
+TEST_P(VectorTestFilled, PushPop)
+{
+ static const ZyanU64 element_in = 1337;
+ const ZyanUSize size = m_vector.size;
+
+ if (!m_has_fixed_capacity)
+ {
+ EXPECT_EQ(ZyanVectorPushBack(&m_vector, &element_in), ZYAN_STATUS_SUCCESS);
+ EXPECT_EQ(m_vector.size, size + 1);
+ EXPECT_EQ(ZYAN_VECTOR_GET(ZyanU64, &m_vector, size), element_in);
+ EXPECT_EQ(ZyanVectorPopBack(&m_vector), ZYAN_STATUS_SUCCESS);
+ EXPECT_EQ(m_vector.size, size);
+ } else
+ {
+ EXPECT_EQ(ZyanVectorPushBack(&m_vector, &element_in), ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE);
+ EXPECT_EQ(m_vector.size, size);
+ EXPECT_EQ(ZyanVectorPopBack(&m_vector), ZYAN_STATUS_SUCCESS);
+ EXPECT_EQ(m_vector.size, size - 1);
+ EXPECT_EQ(ZyanVectorPushBack(&m_vector, &element_in), ZYAN_STATUS_SUCCESS);
+ EXPECT_EQ(m_vector.size, size);
+ EXPECT_EQ(ZYAN_VECTOR_GET(ZyanU64, &m_vector, size - 1), element_in);
+ }
+}
+
+TEST_P(VectorTestFilled, Insert)
+{
+ static const ZyanU64 elements[4] =
+ {
+ 1337, 1338, 1339, 1340
+ };
+ const ZyanUSize count = ZYAN_ARRAY_LENGTH(elements);
+
+ if (m_has_fixed_capacity)
+ {
+ const ZyanUSize size_temp = m_vector.size;
+ EXPECT_EQ(ZyanVectorInsertRange(&m_vector, size_temp / 2, &elements, count),
+ ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE);
+ EXPECT_EQ(ZyanVectorResize(&m_vector, size_temp - count), ZYAN_STATUS_SUCCESS);
+ EXPECT_EQ(m_vector.size, size_temp - count);
+ }
+
+ const ZyanUSize size = m_vector.size;
+ const ZyanUSize half = (size / 2);
+
+ EXPECT_EQ(ZyanVectorInsertRange(&m_vector, half, &elements, ZYAN_ARRAY_LENGTH(elements)),
+ ZYAN_STATUS_SUCCESS);
+ EXPECT_EQ(m_vector.size, size + count);
+ for (ZyanUSize i = 0; i < m_vector.size; ++i)
+ {
+ const ZyanU64 element_out = ZYAN_VECTOR_GET(ZyanU64, &m_vector, i);
+
+ if ((i >= half) && (i < half + count))
+ {
+ EXPECT_EQ(element_out, elements[i - half]);
+ } else
+ if (i < half)
+ {
+ EXPECT_EQ(element_out, i);
+ } else
+ {
+ EXPECT_EQ(element_out, i - count);
+ }
+ }
+}
+
+TEST_P(VectorTestFilled, Delete)
+{
+ EXPECT_EQ(ZyanVectorDeleteRange(&m_vector, m_vector.size, 1), ZYAN_STATUS_OUT_OF_RANGE);
+ EXPECT_EQ(ZyanVectorDeleteRange(&m_vector, 1, m_vector.size), ZYAN_STATUS_OUT_OF_RANGE);
+
+ const ZyanUSize size = m_vector.size;
+ const ZyanUSize half = (size / 2);
+ const ZyanUSize count = (half / 2);
+
+ EXPECT_EQ(ZyanVectorDeleteRange(&m_vector, half, count), ZYAN_STATUS_SUCCESS);
+ EXPECT_EQ(m_vector.size, size - count);
+ for (ZyanUSize i = 0; i < m_vector.size; ++i)
+ {
+ const ZyanU64 element_out = ZYAN_VECTOR_GET(ZyanU64, &m_vector, i);
+
+ if ((i >= half) && (i < half + count))
+ {
+ EXPECT_EQ(element_out, i + count);
+ } else
+ if (i < half)
+ {
+ EXPECT_EQ(element_out, i);
+ } else
+ {
+ EXPECT_EQ(element_out, i - count);
+ }
+ }
+}
+
+TEST_P(VectorTestFilled, Find)
+{
+ ZyanISize index;
+ ZyanU64 element_in = m_vector.size / 2;
+ EXPECT_EQ(ZyanVectorFind(&m_vector, &element_in, &index,
+ reinterpret_cast<ZyanEqualityComparison>(&ZyanEqualsNumeric64)), ZYAN_STATUS_TRUE);
+ EXPECT_EQ(static_cast<ZyanU64>(index), element_in);
+
+ element_in = 1337;
+ EXPECT_EQ(ZyanVectorFind(&m_vector, &element_in, &index,
+ reinterpret_cast<ZyanEqualityComparison>(&ZyanEqualsNumeric64)), ZYAN_STATUS_FALSE);
+ EXPECT_EQ(index, -1);
+
+ // Edge cases
+ EXPECT_EQ(ZyanVectorFindEx(&m_vector, &element_in, &index,
+ reinterpret_cast<ZyanEqualityComparison>(&ZyanEqualsNumeric64), 0, 0),
+ ZYAN_STATUS_FALSE);
+ EXPECT_EQ(ZyanVectorFindEx(&m_vector, &element_in, &index,
+ reinterpret_cast<ZyanEqualityComparison>(&ZyanEqualsNumeric64), 0, m_vector.size + 1),
+ ZYAN_STATUS_OUT_OF_RANGE);
+ EXPECT_EQ(ZyanVectorFindEx(&m_vector, &element_in, &index,
+ reinterpret_cast<ZyanEqualityComparison>(&ZyanEqualsNumeric64), 1, m_vector.size),
+ ZYAN_STATUS_OUT_OF_RANGE);
+}
+
+TEST_P(VectorTestBase, BinarySearch)
+{
+ EXPECT_EQ(ZyanVectorReserve(&m_vector, 100), ZYAN_STATUS_SUCCESS);
+ for (ZyanUSize i = 0; i < 100; ++i)
+ {
+ const ZyanU64 element = rand() % 100;
+
+ ZyanUSize index;
+ const ZyanStatus status = ZyanVectorBinarySearch(&m_vector, &element, &index,
+ reinterpret_cast<ZyanComparison>(&ZyanCompareNumeric64));
+ EXPECT_EQ(ZYAN_SUCCESS(status), ZYAN_TRUE);
+ EXPECT_EQ(ZyanVectorInsert(&m_vector, index, &element), ZYAN_STATUS_SUCCESS);
+ }
+ EXPECT_EQ(m_vector.size, static_cast<ZyanUSize>(100));
+
+ ZyanU64 element_out = ZYAN_VECTOR_GET(ZyanU64, &m_vector, 0);
+ for (ZyanUSize i = 1; i < m_vector.size; ++i)
+ {
+ const ZyanU64 value = element_out;
+ element_out = ZYAN_VECTOR_GET(ZyanU64, &m_vector, i);
+ EXPECT_GE(element_out, value);
+ }
+
+ // Edge cases
+ const ZyanU64 element_in = 1337;
+ ZyanUSize index;
+ EXPECT_EQ(ZyanVectorBinarySearchEx(&m_vector, &element_in, &index,
+ reinterpret_cast<ZyanComparison>(&ZyanCompareNumeric64), 0, 101),
+ ZYAN_STATUS_OUT_OF_RANGE);
+ EXPECT_EQ(ZyanVectorBinarySearchEx(&m_vector, &element_in, &index,
+ reinterpret_cast<ZyanComparison>(&ZyanCompareNumeric64), 1, 100),
+ ZYAN_STATUS_OUT_OF_RANGE);
+}
+
+TEST_P(VectorTestBase, Emplace)
+{
+ ZyanU64* element_new;
+
+ for (ZyanUSize i = 0; i < 10; ++i)
+ {
+ EXPECT_EQ(ZyanVectorEmplace(&m_vector, reinterpret_cast<void**>(&element_new),
+ reinterpret_cast<ZyanMemberFunction>(ZYAN_NULL)), ZYAN_STATUS_SUCCESS);
+ *element_new = i;
+ }
+ EXPECT_EQ(m_vector.size, static_cast<ZyanUSize>(10));
+
+ for (ZyanUSize i = 0; i < m_vector.size; ++i)
+ {
+ EXPECT_EQ(ZYAN_VECTOR_GET(ZyanU64, &m_vector, i), i);
+ }
+
+ EXPECT_EQ(ZyanVectorEmplaceEx(&m_vector, 5, reinterpret_cast<void**>(&element_new),
+ reinterpret_cast<ZyanMemberFunction>(&InitZyanU64)), ZYAN_STATUS_SUCCESS);
+ EXPECT_EQ(*element_new, 1337);
+ EXPECT_EQ(ZYAN_VECTOR_GET(ZyanU64, &m_vector, 5), 1337);
+}
+
+TEST_P(VectorTestFilled, SwapElements)
+{
+ EXPECT_EQ(m_vector.capacity, m_vector.size);
+
+ // Edge cases
+ EXPECT_EQ(ZyanVectorSwapElements(&m_vector, 0, m_vector.size), ZYAN_STATUS_OUT_OF_RANGE);
+ EXPECT_EQ(ZyanVectorSwapElements(&m_vector, m_vector.size, 0), ZYAN_STATUS_OUT_OF_RANGE);
+ EXPECT_EQ(ZyanVectorSwapElements(&m_vector, 0, m_vector.size - 1),
+ ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE);
+
+ // Free space for the temporary element
+ EXPECT_EQ(ZyanVectorPopBack(&m_vector), ZYAN_STATUS_SUCCESS);
+
+ // Retrieve element pointers
+ const ZyanU64* element_first;
+ EXPECT_EQ(ZyanVectorGetPointer(&m_vector, 0, reinterpret_cast<const void**>(&element_first)),
+ ZYAN_STATUS_SUCCESS);
+ const ZyanU64* element_second;
+ EXPECT_EQ(ZyanVectorGetPointer(&m_vector, m_vector.size - 1,
+ reinterpret_cast<const void**>(&element_second)), ZYAN_STATUS_SUCCESS);
+
+ const ZyanU64 values_before[2] = { *element_first, *element_second };
+ EXPECT_EQ(ZyanVectorSwapElements(&m_vector, 0, m_vector.size - 1), ZYAN_STATUS_SUCCESS);
+ const ZyanU64 values_after [2] = { *element_first, *element_second };
+
+ EXPECT_EQ(values_before[0], values_after[1]);
+ EXPECT_EQ(values_before[1], values_after[0]);
+}
+
+INSTANTIATE_TEST_SUITE_P(Param, VectorTestBase, ::testing::Values(false, true));
+INSTANTIATE_TEST_SUITE_P(Param, VectorTestFilled, ::testing::Values(false, true));
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Entry point */
+/* ============================================================================================== */
+
+int main(int argc, char **argv)
+{
+ time_t t;
+ srand(static_cast<unsigned>(time(&t)));
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+/* ============================================================================================== */