commit e45398991fb531b0653f89b48b961bdf7caadcbf Author: Chen Peng Date: Mon Aug 10 01:21:15 2026 +0800 开始设计 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..97b4b28 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +cmake-build-*/ +.idea/ +CMakeUserPresets.json \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a453c03 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 4.2) +get_filename_component(FOLDER_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/CMake) +project(${FOLDER_NAME}) +enable_language(C) +enable_language(ASM) + +set(CMAKE_C_STANDARD 11) + + +add_subdirectory(cKit) +add_subdirectory(cNLP) + + + + + + + diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..b612ffd --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,178 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 21, + "patch": 0 + }, + "configurePresets": [ + { + "name": "base", + "hidden": true, + "generator": "Unix Makefiles", + "binaryDir": "${sourceDir}/build/${presetName}", + "environment": { + }, + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "arm-gcc", + "hidden": true, + "displayName": "GNU Arm GCC Toolchain", + "description": "Using GNU Arm Embedded Toolchain", + "inherits": "base", + "environment": { + "CPU_FLAGS":"-mcpu=cortex-m3 -mthumb -DSTM32F103xB -DUSE_FULL_LL_DRIVER", + "COMPILE_FLAGS":"-Wall -Wextra -fdata-sections -ffunction-sections -Wno-pedantic -Wno-unused-parameter -Wno-cast-function-type", + "ASM_FLAGS":"", + "DEBUG_FLAGS": "", + "LINK_FLAGS":"-Wl,--gc-sections --specs=nano.specs --specs=nosys.specs -Wl,-Map=${sourceDir}/build/${presetName}/${presetName}.map", + "LINK_SCRIPT":"${sourceDir}/$env{CPU_MODEL_NAME}/Link.ld" + }, + "cacheVariables": { + "CMAKE_SYSTEM_NAME": "Generic", + "CMAKE_C_COMPILER": "$env{TOOLCHAIN_PATH}/arm-none-eabi-gcc$env{EXE_SUFFIX}", + "CMAKE_ASM_COMPILER": "$env{TOOLCHAIN_PATH}/arm-none-eabi-gcc$env{EXE_SUFFIX}", + "CMAKE_LINKER": "$env{TOOLCHAIN_PATH}/arm-none-eabi-ld$env{EXE_SUFFIX}", + "CMAKE_OBJCOPY": "$env{TOOLCHAIN_PATH}/arm-none-eabi-objcopy$env{EXE_SUFFIX}", + "CMAKE_OBJDUMP": "$env{TOOLCHAIN_PATH}/arm-none-eabi-objdump$env{EXE_SUFFIX}", + "CMAKE_SIZE": "$env{TOOLCHAIN_PATH}/arm-none-eabi-size$env{EXE_SUFFIX}", + "CMAKE_C_COMPILER_WORKS": "true", + "CMAKE_CXX_COMPILER_WORKS": "true" + } + }, + { + "name": "arm-gcc-debug", + "hidden": true, + "displayName": "GNU ARM GCC Debug", + "inherits": "arm-gcc", + "environment": { + "DEBUG_FLAGS":"-Og -g0" + }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_C_FLAGS_INIT":"$env{CPU_FLAGS} $env{COMPILE_FLAGS} $env{DEBUG_FLAGS}", + "CMAKE_ASM_FLAGS_INIT":"$env{CPU_FLAGS} $env{ASM_FLAGS}", + "CMAKE_EXE_LINKER_FLAGS_INIT":"$env{LINK_FLAGS} -T$env{LINK_SCRIPT}" + } + }, + { + "name": "arm-gcc-release", + "hidden": true, + "displayName": "GNU ARM GCC Release", + "inherits": "arm-gcc", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_C_FLAGS_INIT":"$env{CPU_FLAGS} $env{COMPILE_FLAGS} $env{DEBUG_FLAGS}", + "CMAKE_ASM_FLAGS_INIT":"$env{CPU_FLAGS} $env{ASM_FLAGS}", + "CMAKE_EXE_LINKER_FLAGS_INIT":"$env{LINK_FLAGS} -T$env{LINK_SCRIPT}" + } + }, + { + "name": "armcc-v6", + "hidden": true, + "displayName": "ARMCC V6 Toolchain", + "description": "Using Arm Compiler 6 (armclang)", + "inherits": "base", + "cacheVariables": { + "CMAKE_SYSTEM_NAME": "Generic", + "CMAKE_C_COMPILER": "armclang", + "CMAKE_CXX_COMPILER": "armclang", + "CMAKE_ASM_COMPILER": "armasm", + "CMAKE_LINKER": "armlink", + "CMAKE_OBJCOPY": "fromelf", + "CMAKE_OBJDUMP": "fromelf", + "CMAKE_SIZE": "fromelf", + "CMAKE_C_COMPILER_WORKS": "true" + } + }, + { + "name": "armcc-v5-base", + "hidden": true, + "displayName": "ARMCC V5 Toolchain", + "description": "Using Arm Compiler 5 (armcc)", + "inherits": "base", + "environment": { + "CPU_FLAGS":"--cpu Cortex-M3 --apcs=interwork", + "COMPILE_FLAGS": "--diag_suppress=1,68,1207,3731 --c99 --split_sections -DSTM32F103xB -D__MICROLIB -DUSE_FULL_LL_DRIVER -D__UVISION_VERSION=\"538\"", + "ASM_FLAGS":"--cpu=Cortex-M3 --apcs=interwork --pd \"__MICROLIB SETA 1\" --pd \"__UVISION_VERSION SETA 538\" --pd \"STM32F103xB SETA 1\" --pd \"USE_FULL_LL_DRIVER SETA 1\" --xref --cpreproc ", + "LINK_FLAGS":"--library_type=microlib --strict --summary_stderr --info summarysizes --map --xref --callgraph --symbols --info sizes --info totals --info unused --info veneers --list \"${sourceDir}/build/${presetName}/${presetName}.map\"", + "LINK_SCRIPT":"${sourceDir}/$env{CPU_MODEL_NAME}/Link.ld" + }, + "cacheVariables": { + "CMAKE_SYSTEM_NAME": "Generic", + "CMAKE_C_COMPILER": "$env{TOOLCHAIN_PATH}/armcc.exe", + "CMAKE_CXX_COMPILER": "$env{TOOLCHAIN_PATH}/armcc.exe", + "CMAKE_ASM_COMPILER": "$env{TOOLCHAIN_PATH}/armasm.exe", + "CMAKE_LINKER": "$env{TOOLCHAIN_PATH}/armlink.exe", + "CMAKE_OBJCOPY": "$env{TOOLCHAIN_PATH}/fromelf.exe", + "CMAKE_OBJDUMP": "$env{TOOLCHAIN_PATH}/fromelf.exe", + "CMAKE_SIZE": "$env{TOOLCHAIN_PATH}/fromelf.exe", + "CMAKE_C_COMPILER_WORKS": "true", + "CMAKE_CXX_COMPILER_WORKS": "true", + "CMAKE_TRY_COMPILE_TARGET_TYPE": "STATIC_LIBRARY" + } + }, + { + "name": "armcc-v5-debug", + "hidden": true, + "displayName": "ARM Compiler 5 (AC5) - Debug", + "description": "使用 Keil AC5 工具链进行调试构建", + "inherits": "armcc-v5-base", + "environment": { + "DEBUG_FLAGS": "-O0" + }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_C_FLAGS_INIT":"$env{CPU_FLAGS} $env{COMPILE_FLAGS} $env{DEBUG_FLAGS}", + "CMAKE_ASM_FLAGS_INIT":"$env{CPU_FLAGS} $env{ASM_FLAGS}", + "CMAKE_EXE_LINKER_FLAGS_INIT":"$env{LINK_FLAGS} --scatter=\"$env{LINK_SCRIPT}\"" + } + }, + { + "name": "armcc-v5-release", + "hidden": true, + "displayName": "ARM Compiler 5 (AC5) - Release", + "description": "使用 Keil AC5 工具链进行调试构建", + "inherits": "armcc-v5-base", + "environment": { + "DEBUG_FLAGS": "-O2" + }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_C_FLAGS_INIT":"$env{CPU_FLAGS} $env{COMPILE_FLAGS} $env{DEBUG_FLAGS}", + "CMAKE_ASM_FLAGS_INIT":"$env{CPU_FLAGS} $env{ASM_FLAGS}", + "CMAKE_EXE_LINKER_FLAGS_INIT":"$env{LINK_FLAGS} --scatter=\"$env{LINK_SCRIPT}\"" + } + } + ], + "buildPresets": [ + { + "name": "build-gcc-debug", + "configurePreset": "arm-gcc-debug", + "configuration": "Debug" + }, + { + "name": "build-gcc-release", + "configurePreset": "arm-gcc-release", + "configuration": "Release" + }, + { + "name": "build-armcc-v6-release", + "configurePreset": "armcc-v6", + "configuration": "Release" + }, + { + "name": "build-armcc-v5-debug", + "configurePreset": "armcc-v5-debug", + "configuration": "Debug" + }, + { + "name": "build-armcc-v5-release", + "configurePreset": "armcc-v5-release", + "configuration": "Release" + } + ] +} diff --git a/Documents/README.md b/Documents/README.md new file mode 100644 index 0000000..e69de29 diff --git a/WinUI/README.md b/WinUI/README.md new file mode 100644 index 0000000..0574dd8 --- /dev/null +++ b/WinUI/README.md @@ -0,0 +1 @@ +Windows UI DevKit \ No newline at end of file diff --git a/cAgent/README.md b/cAgent/README.md new file mode 100644 index 0000000..3a1ea97 --- /dev/null +++ b/cAgent/README.md @@ -0,0 +1 @@ +Agent DevKit \ No newline at end of file diff --git a/cKit/Base/c_Base.c b/cKit/Base/c_Base.c new file mode 100644 index 0000000..48c0d84 --- /dev/null +++ b/cKit/Base/c_Base.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Base/c_Base.h b/cKit/Base/c_Base.h new file mode 100644 index 0000000..dd2cf9d --- /dev/null +++ b/cKit/Base/c_Base.h @@ -0,0 +1,21 @@ +#ifndef INCLUDED_C_BASE_H +#define INCLUDED_C_BASE_H + +#ifndef INCLUDED_C_CONFIG_H +#include +#endif /*INCLUDED_C_CONFIG_H*/ + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_COMPILER_H +#include +#endif /*INCLUDED_C_COMPILER_H*/ + +#ifndef INCLUDED_C_MACROS_H +#include +#endif /*INCLUDED_C_MACROS_H*/ + + +#endif /*INCLUDED_C_BASE_H*/ diff --git a/cKit/Base/c_Compiler.c b/cKit/Base/c_Compiler.c new file mode 100644 index 0000000..81fbb0e --- /dev/null +++ b/cKit/Base/c_Compiler.c @@ -0,0 +1,13 @@ +#include + +#if defined(__GNUC__) && !defined(__clang__) +#include "c_Compiler_GNUC.c" +#endif + +#if defined(__clang__) +#include "c_Compiler_CLANG.c" +#endif + +#if defined(_MSC_VER) +#include "c_Compiler_MSVC.c" +#endif diff --git a/cKit/Base/c_Compiler.h b/cKit/Base/c_Compiler.h new file mode 100644 index 0000000..0472040 --- /dev/null +++ b/cKit/Base/c_Compiler.h @@ -0,0 +1,16 @@ +#ifndef INCLUDED_C_COMPILER_H +#define INCLUDED_C_COMPILER_H + +#if defined(__GNUC__) && !defined(__clang__) +#include "c_Compiler_GNUC.h" +#endif + +#if defined(__clang__) +#include "c_Compiler_CLANG.h" +#endif + +#if defined(_MSC_VER) +#include "c_Compiler_MSVC.h" +#endif + +#endif /*INCLUDED_C_COMPILER_H*/ diff --git a/cKit/Base/c_Compiler.t.c b/cKit/Base/c_Compiler.t.c new file mode 100644 index 0000000..b6abc74 --- /dev/null +++ b/cKit/Base/c_Compiler.t.c @@ -0,0 +1,16 @@ +#include "c_Compiler.h" +#include +#include + +C_PACKED_STRUCT( +MyPackedStruct{ + char id; + int value; + short status; +}); + +int main(int argc, char** argv){ + struct MyPackedStruct packed_struct={0}; + + return 0; +} diff --git a/cKit/Base/c_Compiler_CLANG.c b/cKit/Base/c_Compiler_CLANG.c new file mode 100644 index 0000000..0477a0f --- /dev/null +++ b/cKit/Base/c_Compiler_CLANG.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Base/c_Compiler_CLANG.h b/cKit/Base/c_Compiler_CLANG.h new file mode 100644 index 0000000..a77c424 --- /dev/null +++ b/cKit/Base/c_Compiler_CLANG.h @@ -0,0 +1,8 @@ +#ifndef INCLUDED_C_COMPILER_CLANG_H +#define INCLUDED_C_COMPILER_CLANG_H + +#define C_STATIC_FORCE_INLINE static inline __attribute__((always_inline)) + +#define C_PACKED_STRUCT(X) struct X __attribute__((packed)) + +#endif /*INCLUDED_C_COMPILER_CLANG_H*/ diff --git a/cKit/Base/c_Compiler_GNUC.c b/cKit/Base/c_Compiler_GNUC.c new file mode 100644 index 0000000..bc7910a --- /dev/null +++ b/cKit/Base/c_Compiler_GNUC.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Base/c_Compiler_GNUC.h b/cKit/Base/c_Compiler_GNUC.h new file mode 100644 index 0000000..fa8a7c6 --- /dev/null +++ b/cKit/Base/c_Compiler_GNUC.h @@ -0,0 +1,8 @@ +#ifndef INCLUDED_C_COMPILER_GNUC_H +#define INCLUDED_C_COMPILER_GNUC_H + +#define C_STATIC_FORCE_INLINE static inline __attribute__((always_inline)) + +#define C_PACKED_STRUCT(X) struct X __attribute__((packed)) + +#endif /*INCLUDED_C_COMPILER_GNUC_H*/ diff --git a/cKit/Base/c_Compiler_MSVC.c b/cKit/Base/c_Compiler_MSVC.c new file mode 100644 index 0000000..65e9b2b --- /dev/null +++ b/cKit/Base/c_Compiler_MSVC.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Base/c_Compiler_MSVC.h b/cKit/Base/c_Compiler_MSVC.h new file mode 100644 index 0000000..45f9f89 --- /dev/null +++ b/cKit/Base/c_Compiler_MSVC.h @@ -0,0 +1,16 @@ +#ifndef INCLUDED_C_COMPILER_MSVC_H +#define INCLUDED_C_COMPILER_MSVC_H + +#if _MSC_VER >= 1920 + // Visual Studio 2019 或更新版本 +#elif _MSC_VER >= 1910 + // Visual Studio 2017 +#elif _MSC_VER >= 1900 + // Visual Studio 2015 +#endif + +#define C_STATIC_FORCE_INLINE static inline __forceinline + +#define C_PACKED_STRUCT(X) __pragma(pack(push, 1)) struct X __pragma(pack(pop)) + +#endif /*INCLUDED_C_COMPILER_MSVC_H*/ diff --git a/cKit/Base/c_Macros.c b/cKit/Base/c_Macros.c new file mode 100644 index 0000000..34b0dbe --- /dev/null +++ b/cKit/Base/c_Macros.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Base/c_Macros.h b/cKit/Base/c_Macros.h new file mode 100644 index 0000000..67abd03 --- /dev/null +++ b/cKit/Base/c_Macros.h @@ -0,0 +1,119 @@ +#ifndef INCLUDED_C_MACROS_H +#define INCLUDED_C_MACROS_H + +#ifndef INCLUDED_MATH_H +#define INCLUDED_MATH_H +#include +#endif /*INCLUDED_MATH_H*/ + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define C_MIN(a, b) ((a) < (b) ? (a) : (b)) +#define C_MAX(a, b) ((a) > (b) ? (a) : (b)) + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define C_RAD2DEG(x) ((x)/M_PI*180.0) +#define C_DEG2RAD(x) ((x)*M_PI/180.0) + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define C_ALIGN_UPB(x, align) (((x) + ((align) - 1)) & ~((align) - 1)) +#define C_ALIGN_UP(x, align) ((((x) + ((align) - 1)) / (align)) * (align)) + +#define C_ALIGN_DOWNB(x, align) ((x) & ~((align) - 1)) +#define C_ALIGN_DOWN(x, align) (((x) / (align)) * (align)) + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define C_CLIP(x, min, max) (((x) < (min)) ? (min) : \ + (((x) > (max)) ? (max) : (x))) + +#define C_MAX_CLIP(x, max) (((x) > (max)) ? (max) : (x)) +#define C_MIN_CLIP(x, min) (((x) < (min)) ? (min) : (x)) + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define C_ABS(x) (((x) < 0) ? -(x) : (x)) +#define C_DIFF(a,b) C_ABS((a)-(b)) +#define C_IS_NAN(x) ((x) != (x)) +#define C_COMPARE(x, y) (((x) > (y)) - ((x) < (y))) +#define C_SIGN(x) C_COMPARE(x, 0) +#define C_IS_ODD( num ) ((num) & 1) +#define C_IS_EVEN( num ) (!IS_ODD( (num) )) +#define C_IS_BETWEEN(n,L,H) ((unsigned char)((n) >= (L) && (n) <= (H))) + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +#define C_BIT(x) (1<<(x)) +#define C_BIT_SET(x,p) ((x)|(1<<(p))) +#define C_BIT_CLEAR(x,p) ((x)&(~(1<<(p)))) +#define C_BIT_GET(x,p) (((x)>>(p))&1) +#define C_BIT_TOGGLE(x,p) ((x)^(1<<(p))) + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define C_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#define C_SET(d, n, v) do{ c_size_t i_, n_; \ + for ( n_ = (n), i_ = 0; n_ > 0; --n_, ++i_) \ + (d)[i_] = (v); } while(0) +#define C_ZERO(d, n) C_SET(d, n, 0) +#define C_COLUMNS(S,E) ( (E) - (S) + 1 ) +#define C_IS_ARRAY(a) ((void *)&a == (void *)a) + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define C_STMT( stuff ) do { stuff } while (0) + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// 1. 底层拼接宏(真正执行拼接) +#define C_UNIQUE_NAME_CONCAT(a, b) a ## b + +// 2. 中间转换宏(强制将 __LINE__ 或 __COUNTER__ 展开为实际的数字/符号) +#define C_UNIQUE_NAME_EVAL(a, b) C_UNIQUE_NAME_CONCAT(a, b) + +// 3. 面向用户的唯一名称生成宏 +#if defined(__COUNTER__) + // 工业级推荐:__COUNTER__ 从 0 开始,每次调用自动递增,保证绝对唯一 + #define C_UNIQUE_NAME(prefix) C_UNIQUE_NAME_EVAL(prefix, __COUNTER__) +#else + // 兼容标准:__LINE__ 使用当前行号,同一行内多次调用可能会冲突 + #define C_UNIQUE_NAME(prefix) C_UNIQUE_NAME_EVAL(prefix, __LINE__) +#endif + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define C_ONCE2(stmts, var) {static int var = 1;\ + if(var){stmts\ + var = 0;}} +#define C_ONCE(stmts) C_ONCE2(stmts, C_UNIQUE_NAME(__onceVar__)) + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define C_CONTAINER_OF(ptr, type, member) \ + ((type *)( (char *)(ptr) - offsetof(type, member) )) + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define C_UNUSED(x) ((void)(x)) + +#endif /*INCLUDED_C_MACROS_H*/ diff --git a/cKit/Base/c_Macros.t.c b/cKit/Base/c_Macros.t.c new file mode 100644 index 0000000..3617ee4 --- /dev/null +++ b/cKit/Base/c_Macros.t.c @@ -0,0 +1,9 @@ +#include "c_Macros.h" +#include +#include + +int main(int argc, char** argv){ + + + return 0; +} diff --git a/cKit/Base/c_Types.c b/cKit/Base/c_Types.c new file mode 100644 index 0000000..86a3e8e --- /dev/null +++ b/cKit/Base/c_Types.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Base/c_Types.h b/cKit/Base/c_Types.h new file mode 100644 index 0000000..bd4a10e --- /dev/null +++ b/cKit/Base/c_Types.h @@ -0,0 +1,130 @@ +#ifndef INCLUDED_C_TYPES_H +#define INCLUDED_C_TYPES_H + +#ifndef INCLUDED_C_CONFIG_H +#include +#endif /*INCLUDED_C_CONFIG_H*/ + +#ifndef INCLUDED_STDINT_H +#define INCLUDED_STDINT_H +#include +#endif /*INCLUDED_STDINT_H*/ + +#ifndef INCLUDED_STDBOOL_H +#define INCLUDED_STDBOOL_H +#include +#endif /*INCLUDED_STDBOOL_H*/ + +#ifndef INCLUDED_STDATOMIC_H +#define INCLUDED_STDATOMIC_H +#include +#endif /*INCLUDED_STDATOMIC_H*/ + +#ifndef INCLUDED_STRING_H +#define INCLUDED_STRING_H +#include +#endif /*INCLUDED_STRING_H*/ + +#ifndef INCLUDED_TIME_H +#define INCLUDED_TIME_H +#include +#endif /*INCLUDED_TIME_H*/ + +#ifndef INCLUDED_INTTYPES_H +#define INCLUDED_INTTYPES_H +#include +#endif /*INCLUDED_INTTYPES_H*/ + +#ifndef INCLUDED_ASSERT_H +#define INCLUDED_ASSERT_H +#include +#endif /*INCLUDED_ASSERT_H*/ + +#ifndef INCLUDED_FLOAT_H +#define INCLUDED_FLOAT_H +#include +#endif /*INCLUDED_FLOAT_H*/ + + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +#if (C_SIZEOF_VOID_P==4) +typedef int32_t c_int_t; +typedef uint32_t c_uint_t; +typedef uint32_t c_uintptr_t; +typedef int32_t c_intptr_t; +typedef int32_t c_ptrdiff_t; +typedef uint32_t c_size_t; +#define C_INT_MIN INT32_MIN +#define C_INT_MAX INT32_MAX +#define C_UINT_MAX UINT32_MAX +#define C_SIZE_MAX UINT32_MAX +#define C_PTRDIFF_MIN INT32_MIN +#define C_PTRDIFF_MAX INT32_MAX +#define C_PRId PRId32 +#define C_PRIi PRIi32 +#define C_PRIo PRIo32 +#define C_PRIu PRIu32 +#define C_PRIx PRIx32 +#define C_PRIX PRIX32 +#define C_SCNd SCNd32 +#define C_SCNi SCNi32 +#define C_SCNo SCNo32 +#define C_SCNu SCNu32 +#define C_SCNx SCNx32 +#elif (C_SIZEOF_VOID_P==8) +typedef int64_t c_int_t; +typedef uint64_t c_uint_t; +typedef uint64_t c_uintptr_t; +typedef int64_t c_intptr_t; +typedef int64_t c_ptrdiff_t; +typedef uint64_t c_size_t; +#define C_INT_MIN INT64_MIN +#define C_INT_MAX INT64_MAX +#define C_UINT_MAX UINT64_MAX +#define C_SIZE_MAX UINT64_MAX +#define C_PTRDIFF_MIN INT64_MIN +#define C_PTRDIFF_MAX INT64_MAX +#define C_PRId PRId64 +#define C_PRIi PRIi64 +#define C_PRIo PRIo64 +#define C_PRIu PRIu64 +#define C_PRIx PRIx64 +#define C_PRIX PRIX64 +#define C_SCNd SCNd64 +#define C_SCNi SCNi64 +#define C_SCNo SCNo64 +#define C_SCNu SCNu64 +#define C_SCNx SCNx64 +#endif + +#define c_bool_t bool +#define C_TRUE true +#define C_FALSE false + +typedef time_t c_time_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef int c_err_t; + +#define C_ERR_OK (0) +#define C_ERR_SUCCESS C_ERR_OK +#define C_ERR_FAIL (-1) +#define C_ERR_NOMEM (-2) +#define C_ERR_PARAM (-3) +#define C_ERR_FULL (-4) +#define C_ERR_EMPTY (-5) +#define C_ERR_INDEX (-6) +#define C_ERR_STATUS (-7) +#define C_ERR_NOT_FOUND (-8) +#define C_ERR_ALREADY_EXISTS (-9) +#define C_ERR_INVALID C_ERR_FAIL + + + +#endif /*INCLUDED_C_TYPES_H*/ diff --git a/cKit/CMake/create_library_from_dirs.cmake b/cKit/CMake/create_library_from_dirs.cmake new file mode 100644 index 0000000..fcdb233 --- /dev/null +++ b/cKit/CMake/create_library_from_dirs.cmake @@ -0,0 +1,83 @@ +# ============================================================================== +# 宏定义:CREATE_LIBRARY_FROM_DIRS +# 参数 1: TARGET_NAME - 要生成的库的名称 +# 参数 2: LIB_TYPE - 库类型(STATIC 或 SHARED) +# 参数 3: SOURCE_DIRS - 需要扫描源码的目录列表(分号分隔的字符串) +# 参数 4: EXCLUDE_FILES - 需要额外过滤的文件名列表(分号分隔的字符串,支持正则) +# ============================================================================== +macro(create_library_from_dirs TARGET_NAME LIB_TYPE SOURCE_DIRS EXCLUDE_FILES) + set(ALL_SOURCES "") + add_library(${TARGET_NAME} ${LIB_TYPE} "") + # 1. 遍历传入的每一个目录 + foreach(DIR ${SOURCE_DIRS}) + # 统一处理相对路径和绝对路径 + if(NOT IS_ABSOLUTE "${DIR}") + set(DIR "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}") + endif() + + # 检查目录是否存在,防止写错路径导致报错 + if(EXISTS "${DIR}") + # 2. 收集当前目录下所有的 .c, .s, .S 文件 + file(GLOB ALL_FILES + "${DIR}/*.c" + "${DIR}/*.s" + "${DIR}/*.S" + ) + target_include_directories(${TARGET_NAME} PUBLIC ${DIR}) + message(STATUS "[${TARGET_NAME}] INCLUDE ${DIR}") + + + foreach(FILE_PATH ${ALL_FILES}) + # 获取纯文件名(例如:Compiler_GNUC.c),方便精准匹配 + get_filename_component(FILE_NAME "${FILE_PATH}" NAME) + + # 检查是否匹配自定义的排除列表 + set(SHOULD_EXCLUDE FALSE) + foreach(EX_PATTERN ${EXCLUDE_FILES}) + # 支持完整文件名匹配或正则表达式匹配 + if("${FILE_NAME}" STREQUAL "${EX_PATTERN}" OR "${FILE_NAME}" MATCHES "${EX_PATTERN}") + set(SHOULD_EXCLUDE TRUE) + break() + endif() + endforeach() + + # 4. 执行过滤判断 + if(FILE_PATH MATCHES "\\.t\\.c$") + # 过滤单元测试文件 + list(APPEND UNIT_TEST_SOURCES ${FILE_PATH}) + elseif(SHOULD_EXCLUDE) + # 过滤用户指定的特定文件 + message(STATUS "[${TARGET_NAME}] EXCLUDED specific file: ${FILE_NAME} from target ${TARGET_NAME}") + else() + # 留下的才是有效的库源码 + list(APPEND ALL_SOURCES ${FILE_PATH}) + endif() + endforeach() + else() + message(WARNING "[${TARGET_NAME}] Directory does not exist: ${DIR}") + endif() + endforeach() + + # 4. 确保找到了有效的源代码再创建库 + list(LENGTH ALL_SOURCES SOURCE_COUNT) + if(SOURCE_COUNT GREATER 0) +# add_library(${TARGET_NAME} ${LIB_TYPE} ${ALL_SOURCES}) + target_sources(${TARGET_NAME} PUBLIC ${ALL_SOURCES}) + message(STATUS "[${TARGET_NAME}] Created ${LIB_TYPE} library '${TARGET_NAME}' with ${SOURCE_COUNT} source files.") + foreach (item IN LISTS ALL_SOURCES) + message(STATUS "[${TARGET_NAME}] SOURCE: ${item}") + endforeach () + else() + message(FATAL_ERROR "No valid source files found for target: ${TARGET_NAME}") + endif() + + list(LENGTH UNIT_TEST_SOURCES UNIT_TEST_COUNT) + if(UNIT_TEST_COUNT GREATER 0) + foreach (item IN LISTS UNIT_TEST_SOURCES) + get_filename_component(TestUnit ${item} NAME_WLE) + message(STATUS "[${TARGET_NAME}] TEST: ${TestUnit} - ${item}") + add_executable(${TestUnit} ${item}) + target_link_libraries(${TestUnit} PUBLIC ${TARGET_NAME}) + endforeach () + endif () +endmacro() \ No newline at end of file diff --git a/cKit/CMakeLists.txt b/cKit/CMakeLists.txt new file mode 100644 index 0000000..fa6ff9b --- /dev/null +++ b/cKit/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.15) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/CMake) +get_filename_component(FOLDER_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +project(${FOLDER_NAME}) +enable_language(C) +enable_language(ASM) + +set(CMAKE_C_STANDARD 11) + +configure_file(c_Config.h.in ${CMAKE_CURRENT_BINARY_DIR}/c_Config.h @ONLY) + +include(create_library_from_dirs) +create_library_from_dirs(${PROJECT_NAME} STATIC + "Base;Memory;Foundation;Search;Sort;Math;Graph;Socket" + "c_Compiler_GNUC.c;c_Compiler_CLANG.c;c_Compiler_MSVC.c") +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + diff --git a/cKit/Foundation/c_ArrayList.c b/cKit/Foundation/c_ArrayList.c new file mode 100644 index 0000000..d83578f --- /dev/null +++ b/cKit/Foundation/c_ArrayList.c @@ -0,0 +1,81 @@ +#include +#include + +#define DEFAULT_INITIAL_CAPACITY 4 + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_ArrayList_Init(c_ArrayList_t* self, c_size_t obj_size, c_size_t capacity) { + if (!self || obj_size == 0) return C_ERR_PARAM; + + self->obj_size = (int)obj_size; + self->capacity = (capacity > 0) ? capacity : DEFAULT_INITIAL_CAPACITY; + self->size = 0; + + // 分配連續記憶體空間:容量 * 單個物件大小 + self->array = C_ALLOC(self->capacity * self->obj_size); + if (!self->array) { + self->capacity = 0; + return C_ERR_NOMEM; + } + + return C_ERR_SUCCESS; +} + +void c_ArrayList_Destroy(c_ArrayList_t* self) { + if (!self) return; + C_FREE(self->array); + self->capacity = 0; + self->size = 0; + self->obj_size=0; +} + +c_err_t c_ArrayList_Add(c_ArrayList_t* self, void* obj) { + if (!self || !self->array || !obj) return C_ERR_PARAM; + + // 動態擴容邏輯 + if (self->size >= self->capacity) { + const c_size_t new_capacity = self->capacity<<1; + void* new_array = C_REALLOC(self->array, new_capacity * self->obj_size); + if (!new_array) { + return C_ERR_NOMEM; + } + self->array = new_array; + self->capacity = new_capacity; + } + + // 計算目標記憶體地址並將物件內容複製進去 + char* target_addr = (char*)self->array + (self->size * self->obj_size); + memcpy(target_addr, obj, self->obj_size); + + self->size++; + return C_ERR_SUCCESS; +} + +void* c_ArrayList_Get(c_ArrayList_t* self, c_size_t index) { + if (!self || !self->array || index >= self->size) { + return NULL; + } + // 回傳內部記憶體塊中該元素的實際起始地址 + return (char*)self->array + (index * self->obj_size); +} + +c_err_t c_ArrayList_Remove(c_ArrayList_t* self, c_size_t index) { + if (!self || !self->array) return C_ERR_PARAM; + if (index >= self->size) return C_ERR_INDEX; + + // 如果刪除的不是最後一個元素,需要將後方所有物件往前平移一個單位 + if (index < self->size - 1) { + char* dest = (char*)self->array + (index * self->obj_size); + const char* src = dest + self->obj_size; + const c_size_t num_elements_to_move = self->size - index - 1; + + // 使用 memmove 處理重疊記憶體區塊的複製 + memmove(dest, src, num_elements_to_move * self->obj_size); + } + + self->size--; + return C_ERR_SUCCESS; +} + diff --git a/cKit/Foundation/c_ArrayList.h b/cKit/Foundation/c_ArrayList.h new file mode 100644 index 0000000..f11e7c2 --- /dev/null +++ b/cKit/Foundation/c_ArrayList.h @@ -0,0 +1,33 @@ +#ifndef INCLUDED_C_ARRAYLIST_H +#define INCLUDED_C_ARRAYLIST_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +typedef struct { + void* array; + int obj_size; + c_size_t capacity; + c_size_t size; +}c_ArrayList_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_ArrayList_Init(c_ArrayList_t* self, c_size_t obj_size, c_size_t capacity); + +void c_ArrayList_Destroy(c_ArrayList_t* self); + +c_err_t c_ArrayList_Add(c_ArrayList_t* self, void* obj); + +void* c_ArrayList_Get(c_ArrayList_t* self, c_size_t index); + +c_err_t c_ArrayList_Remove(c_ArrayList_t* self, c_size_t index); + +#endif /*INCLUDED_C_ARRAYLIST_H*/ diff --git a/cKit/Foundation/c_ArrayList.t.c b/cKit/Foundation/c_ArrayList.t.c new file mode 100644 index 0000000..d879c3e --- /dev/null +++ b/cKit/Foundation/c_ArrayList.t.c @@ -0,0 +1,63 @@ +#include "c_ArrayList.h" +#include +#include +typedef struct { + int x; + int y; +} Point_t; + +int main(int argc, char** argv){ + printf("開始執行 c_ArrayList 測試用例...\n"); + + c_ArrayList_t list; + // 初始化容量為 2,用以測試自動擴容 + c_err_t err = c_ArrayList_Init(&list, sizeof(Point_t), 2); + assert(err == C_ERR_SUCCESS); + + Point_t p1 = {10, 20}; + Point_t p2 = {30, 40}; + Point_t p3 = {50, 60}; + + // 1. 測試新增與值複製 + err = c_ArrayList_Add(&list, &p1); + assert(err == C_ERR_SUCCESS); + err = c_ArrayList_Add(&list, &p2); + assert(err == C_ERR_SUCCESS); + + // 此時 list 的生命週期獨立於區域變數,修改原來的 p1 不會影響 list 裡面的內容 + p1.x = 999; + + // 2. 測試讀取 + Point_t* res1 = (Point_t*)c_ArrayList_Get(&list, 0); + assert(res1 != NULL); + assert(res1->x == 10); // 驗證依然是 10,不受 p1 修改影響 + assert(res1->y == 20); + + // 3. 測試動態擴容 + err = c_ArrayList_Add(&list, &p3); // 觸發擴容 (2 -> 4) + assert(err == C_ERR_SUCCESS); + assert(list.capacity == 4); + assert(list.size == 3); + + // 4. 測試邊界防呆 + assert(c_ArrayList_Get(&list, 5) == NULL); + + // 5. 測試刪除與平移 + // 目前內容: [0]:{10,20}, [1]:{30,40}, [2]:{50,60} + // 刪除索引 1 ({30,40}) + err = c_ArrayList_Remove(&list, 1); + assert(err == C_ERR_SUCCESS); + assert(list.size == 2); + + // 驗證原本索引 2 的物件是否成功平移到索引 1 + Point_t* res2 = (Point_t*)c_ArrayList_Get(&list, 1); + assert(res2->x == 50); + assert(res2->y == 60); + + // 清理資源 + c_ArrayList_Destroy(&list); + assert(list.array == NULL); + + printf("所有測試成功通過!\n"); + return 0; +} diff --git a/cKit/Foundation/c_ArrayQueue.c b/cKit/Foundation/c_ArrayQueue.c new file mode 100644 index 0000000..7228436 --- /dev/null +++ b/cKit/Foundation/c_ArrayQueue.c @@ -0,0 +1,97 @@ +#include +#include + +#define DEFAULT_INITIAL_CAPACITY 4 + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_ArrayQueue_Init(c_ArrayQueue_t* self, int obj_size, c_size_t capacity) { + if (!self || obj_size == 0) return C_ERR_PARAM; + + self->obj_size = obj_size; + self->capacity = (capacity > 0) ? capacity : DEFAULT_INITIAL_CAPACITY; + self->size = 0; + + self->array = C_ALLOC(self->capacity * self->obj_size); + if (!self->array) { + self->capacity = 0; + return C_ERR_NOMEM; + } + + return C_ERR_SUCCESS; +} + +void c_ArrayQueue_Destroy(c_ArrayQueue_t* self) { + if (!self) return; + + C_FREE(self->array); + + self->capacity = 0; + self->size = 0; + self->obj_size = 0; +} + +c_err_t c_ArrayQueue_Push(c_ArrayQueue_t* self, void* obj) { + if (!self || !self->array || !obj) return C_ERR_PARAM; + + if (self->size >= self->capacity) { + const c_size_t new_capacity = self->capacity << 1; + void* new_array = C_REALLOC(self->array, new_capacity * self->obj_size); + if (!new_array) { + return C_ERR_NOMEM; + } + self->array = new_array; + self->capacity = new_capacity; + } + + char* target = (char*)self->array + (self->size * self->obj_size); + memcpy(target, obj, self->obj_size); + self->size++; + + return C_ERR_SUCCESS; +} + +c_err_t c_ArrayQueue_Pop(c_ArrayQueue_t* self, void* obj) { + if (!self || !self->array ) return C_ERR_PARAM; + if (self->size == 0) return C_ERR_EMPTY; // 佇列已空 + + char* pop_src = (char*)self->array; // 最前端索引 0 的地址 + + // 1. 直接值複製到呼叫端提供的記憶體中 + if (obj) { + memcpy(obj, pop_src, self->obj_size); + } + + // 2. 如果佇列內還有其他元素,將它們向前平移一個單位 + if (self->size > 1) { + char* dest = pop_src; + const char* src = pop_src + self->obj_size; + const c_size_t num_elements_to_move = self->size - 1; + memmove(dest, src, num_elements_to_move * self->obj_size); + } + + self->size--; + return C_ERR_SUCCESS; +} + +c_err_t c_ArrayQueue_Remove(c_ArrayQueue_t* self, c_size_t index) { + if (!self || !self->array) return C_ERR_PARAM; + if (index >= self->size) return C_ERR_INDEX; + + if (index < self->size - 1) { + char* dest = (char*)self->array + (index * self->obj_size); + const char* src = dest + self->obj_size; + const c_size_t num_elements_to_move = self->size - index - 1; + memmove(dest, src, num_elements_to_move * self->obj_size); + } + + self->size--; + return C_ERR_SUCCESS; +} + +void* c_ArrayQueue_Peek(c_ArrayQueue_t* self) { + if (!self || !self->array || self->size == 0) return NULL; + return self->array; // 最前端索引 0 的起始地址即是 array 本身 +} + diff --git a/cKit/Foundation/c_ArrayQueue.h b/cKit/Foundation/c_ArrayQueue.h new file mode 100644 index 0000000..6ef22cf --- /dev/null +++ b/cKit/Foundation/c_ArrayQueue.h @@ -0,0 +1,32 @@ +#ifndef INCLUDED_C_ARRAYQUEUE_H +#define INCLUDED_C_ARRAYQUEUE_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + void* array; + int obj_size; + c_size_t capacity; + c_size_t size; +}c_ArrayQueue_t; + + +c_err_t c_ArrayQueue_Init(c_ArrayQueue_t* self, int obj_size, c_size_t capacity); + +void c_ArrayQueue_Destroy(c_ArrayQueue_t* self); + +c_err_t c_ArrayQueue_Push(c_ArrayQueue_t* self, void* obj); + +c_err_t c_ArrayQueue_Pop(c_ArrayQueue_t* self, void* obj); + +void* c_ArrayQueue_Peek(c_ArrayQueue_t* self); + +c_err_t c_ArrayQueue_Remove(c_ArrayQueue_t* self, c_size_t index); + +#endif /*INCLUDED_C_ARRAYQUEUE_H*/ diff --git a/cKit/Foundation/c_ArrayQueue.t.c b/cKit/Foundation/c_ArrayQueue.t.c new file mode 100644 index 0000000..e55f0d3 --- /dev/null +++ b/cKit/Foundation/c_ArrayQueue.t.c @@ -0,0 +1,133 @@ +#include "c_ArrayQueue.h" +#include +#include +typedef struct { + char name[16]; + int score; +} Player_t; + +void test_log(const char* test_name) { + printf("[PASS] %s\n", test_name); +} + +int main() { + printf("==================================================\n"); + printf(" 開始執行 c_ArrayQueue 全新調整版完整測試用例\n"); + printf("==================================================\n\n"); + + // ========================================== + // 1. 測試初始化 (Init) + // ========================================== + c_ArrayQueue_t q; + // 初始容量設為 2 以驗證後續的自動擴容 + c_err_t err = c_ArrayQueue_Init(&q, sizeof(Player_t), 2); + assert(err == C_ERR_SUCCESS); + assert(q.size == 0); + assert(q.capacity == 2); + assert(q.obj_size == sizeof(Player_t)); + test_log("1. 佇列初始化狀態驗證成功"); + + // ========================================== + // 2. 測試推入與動態擴容 (Push) + // ========================================== + Player_t p1 = {"Alice", 95}; + Player_t p2 = {"Bob", 88}; + Player_t p3 = {"Charlie", 92}; + + err = c_ArrayQueue_Push(&q, &p1); assert(err == C_ERR_SUCCESS); + err = c_ArrayQueue_Push(&q, &p2); assert(err == C_ERR_SUCCESS); + assert(q.size == 2); + assert(q.capacity == 2); + + // 推入第三筆,預期觸發翻倍擴容 (2 -> 4) + err = c_ArrayQueue_Push(&q, &p3); + assert(err == C_ERR_SUCCESS); + assert(q.size == 3); + assert(q.capacity == 4); + test_log("2. 資料值複製推入與自動動態擴容成功"); + + // ========================================== + // 3. 測試查看最前端元素 (Peek) + // ========================================== + // 由於是 FIFO,目前最前端應該是第一個推進去的 Alice + Player_t* p_peek = (Player_t*)c_ArrayQueue_Peek(&q); + assert(p_peek != NULL); + assert(strcmp(p_peek->name, "Alice") == 0); + assert(p_peek->score == 95); + assert(q.size == 3); // 驗證 Peek 不會影響 size + test_log("3. 唯讀查看最前端元素 (Peek) 成功"); + + // ========================================== + // 4. 測試隨機刪除與平移 (Remove) + // ========================================== + // 目前佇列內容:Index 0="Alice", Index 1="Bob", Index 2="Charlie" + // 嘗試隨機刪除中間的 "Bob" (Index 1) + err = c_ArrayQueue_Remove(&q, 1); + assert(err == C_ERR_SUCCESS); + assert(q.size == 2); + + // 驗證原本 Index 2 的 "Charlie" 是否成功向前平移到 Index 1 + // 我們可以推入一個暫時資料來間接確認,或者看 Peek(依然要是Alice) + p_peek = (Player_t*)c_ArrayQueue_Peek(&q); + assert(strcmp(p_peek->name, "Alice") == 0); + + // 測試刪除越界防呆 + err = c_ArrayQueue_Remove(&q, 5); + assert(err == C_ERR_INDEX); + test_log("4. 隨機刪除 (Remove) 與記憶體平移成功"); + + // ========================================== + // 5. 測試全新安全版先進先出彈出 (Pop - 使用者自備緩衝區) + // ========================================== + // 目前佇列剩餘內容:Index 0="Alice", Index 1="Charlie" + Player_t local_buf; + + // 第一次 Pop:預期安全複製出 "Alice" + err = c_ArrayQueue_Pop(&q, &local_buf); + assert(err == C_ERR_SUCCESS); + assert(strcmp(local_buf.name, "Alice") == 0); + assert(local_buf.score == 95); + assert(q.size == 1); + + // 彈出後,最前端應平移更新為 "Charlie" + p_peek = (Player_t*)c_ArrayQueue_Peek(&q); + assert(strcmp(p_peek->name, "Charlie") == 0); + + // 第二次 Pop:預期安全複製出 "Charlie" + err = c_ArrayQueue_Pop(&q, &local_buf); + assert(err == C_ERR_SUCCESS); + assert(strcmp(local_buf.name, "Charlie") == 0); + assert(q.size == 0); + + // 第三次 Pop:此時佇列已空,預期回傳 C_ERR_OUT_OF_BOUNDS + err = c_ArrayQueue_Pop(&q, &local_buf); + assert(err == C_ERR_EMPTY); + + // 空佇列時 Peek 應回傳 NULL + assert(c_ArrayQueue_Peek(&q) == NULL); + test_log("5. 全新安全彈出 (Pop) 語義與空佇列防呆驗證成功"); + + // ========================================== + // 6. 介面參數無效指標檢查 + // ========================================== + assert(c_ArrayQueue_Init(NULL, sizeof(Player_t), 4) == C_ERR_PARAM); + assert(c_ArrayQueue_Push(NULL, &p1) == C_ERR_PARAM); + assert(c_ArrayQueue_Pop(NULL, &local_buf) == C_ERR_PARAM); + // assert(c_ArrayQueue_Pop(&q, NULL) == C_ERR_PARAM); + test_log("6. 介面 NULL 指標防呆驗證成功"); + + // ========================================== + // 7. 測試銷毀與記憶體釋放 (Destroy) + // ========================================== + c_ArrayQueue_Destroy(&q); + assert(q.array == NULL); + assert(q.size == 0); + assert(q.capacity == 0); + assert(q.obj_size == 0); + test_log("7. 佇列資源銷毀與指標重置成功"); + + printf("\n==================================================\n"); + printf(" 恭喜!調整版 c_ArrayQueue 所有單元測試順利通過!\n"); + printf("==================================================\n"); + return 0; +} \ No newline at end of file diff --git a/cKit/Foundation/c_ArrayStack.c b/cKit/Foundation/c_ArrayStack.c new file mode 100644 index 0000000..0270e4d --- /dev/null +++ b/cKit/Foundation/c_ArrayStack.c @@ -0,0 +1,89 @@ +#include "c_ArrayStack.h" +#include + +#define DEFAULT_INITIAL_CAPACITY 4 + +c_err_t c_ArrayStack_Init(c_ArrayStack_t* self, int obj_size, c_size_t capacity) { + if (!self || obj_size <= 0) return C_ERR_PARAM; + + self->obj_size = obj_size; + self->capacity = (capacity > 0) ? capacity : DEFAULT_INITIAL_CAPACITY; + self->size = 0; + + self->array = C_ALLOC(self->capacity * self->obj_size); + if (!self->array) { + self->capacity = 0; + return C_ERR_NOMEM; + } + + return C_ERR_SUCCESS; +} + + +void c_ArrayStack_Destroy(c_ArrayStack_t* self) { + if (!self) return; + + C_FREE(self->array); + self->capacity = 0; + self->size = 0; + self->obj_size = 0; +} + +c_err_t c_ArrayStack_Push(c_ArrayStack_t* self, void* obj) { + if (!self || !self->array || !obj) return C_ERR_PARAM; + + if (self->size >= self->capacity) { + const c_size_t new_capacity = self->capacity << 1; + void* new_array = C_REALLOC(self->array, new_capacity * self->obj_size); + if (!new_array) { + return C_ERR_NOMEM; + } + self->array = new_array; + self->capacity = new_capacity; + } + + // 計算頂端目標記憶體地址並寫入資料 + char* target = (char*)self->array + (self->size * self->obj_size); + memcpy(target, obj, self->obj_size); + self->size++; + + return C_ERR_SUCCESS; +} + +c_err_t c_ArrayStack_Pop(c_ArrayStack_t* self, void* obj) { + if (!self || !self->array || !obj) return C_ERR_PARAM; + if (self->size == 0) return C_ERR_EMPTY; // 堆疊已空 + + // 取得位於 size - 1 的堆疊頂端元素地址 + const char* pop_src = (char*)self->array + ((self->size - 1) * self->obj_size); + + // 直接複製到呼叫端提供的記憶體中 + memcpy(obj, pop_src, self->obj_size); + + self->size--; + return C_ERR_SUCCESS; +} + +void* c_ArrayStack_Peek(c_ArrayStack_t* self) { + if (!self || !self->array || self->size == 0) return NULL; + return (char*)self->array + ((self->size - 1) * self->obj_size); +} + +c_err_t c_ArrayStack_Remove(c_ArrayStack_t* self, c_size_t index) { + if (!self || !self->array) return C_ERR_PARAM; + if (index >= self->size) return C_ERR_INDEX; + + // 如果刪除的不是頂端元素,則後續元素需向前平移一個單位 + if (index < self->size - 1) { + char* dest = (char*)self->array + (index * self->obj_size); + const char* src = dest + self->obj_size; + const c_size_t num_elements_to_move = self->size - index - 1; + memmove(dest, src, num_elements_to_move * self->obj_size); + } + + self->size--; + return C_ERR_SUCCESS; +} + + + diff --git a/cKit/Foundation/c_ArrayStack.h b/cKit/Foundation/c_ArrayStack.h new file mode 100644 index 0000000..91a5922 --- /dev/null +++ b/cKit/Foundation/c_ArrayStack.h @@ -0,0 +1,26 @@ +#ifndef INCLUDED_C_ARRAYSTACK_H +#define INCLUDED_C_ARRAYSTACK_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +typedef struct { + void* array; + int obj_size; + c_size_t capacity; + c_size_t size; +} c_ArrayStack_t; + +c_err_t c_ArrayStack_Init(c_ArrayStack_t* self, int obj_size, c_size_t capacity); +void c_ArrayStack_Destroy(c_ArrayStack_t* self); +c_err_t c_ArrayStack_Push(c_ArrayStack_t* self, void* obj); +c_err_t c_ArrayStack_Pop(c_ArrayStack_t* self, void* obj); +void* c_ArrayStack_Peek(c_ArrayStack_t* self); +c_err_t c_ArrayStack_Remove(c_ArrayStack_t* self, c_size_t index); + +#endif /*INCLUDED_C_ARRAYSTACK_H*/ diff --git a/cKit/Foundation/c_ArrayStack.t.c b/cKit/Foundation/c_ArrayStack.t.c new file mode 100644 index 0000000..fecf932 --- /dev/null +++ b/cKit/Foundation/c_ArrayStack.t.c @@ -0,0 +1,126 @@ +#include "c_ArrayStack.h" +#include +#include + +typedef struct { + char url[64]; + int depth; +} BrowserHistory_t; + +void test_log(const char* test_name) { + printf("[PASS] %s\n", test_name); +} + +int main() { + printf("==================================================\n"); + printf(" 開始執行 c_ArrayStack 完整版測試用例\n"); + printf("==================================================\n\n"); + + // ========================================== + // 1. 測試初始化 (Init) + // ========================================== + c_ArrayStack_t s; + c_err_t err = c_ArrayStack_Init(&s, sizeof(BrowserHistory_t), 2); // 初始容量設為 2 測試擴容 + assert(err == C_ERR_SUCCESS); + assert(s.size == 0); + assert(s.capacity == 2); + test_log("1. 堆疊初始化成功"); + + // ========================================== + // 2. 測試資料推入與動態擴容 (Push) + // ========================================== + BrowserHistory_t page1 = {"github.com", 1}; + BrowserHistory_t page2 = {"google.com", 2}; + BrowserHistory_t page3 = {"stackoverflow.com", 3}; + + err = c_ArrayStack_Push(&s, &page1); assert(err == C_ERR_SUCCESS); + err = c_ArrayStack_Push(&s, &page2); assert(err == C_ERR_SUCCESS); + assert(s.size == 2); + assert(s.capacity == 2); + + // 推入第三筆,預期自動觸發擴容翻倍 (2 -> 4) + err = c_ArrayStack_Push(&s, &page3); + assert(err == C_ERR_SUCCESS); + assert(s.size == 3); + assert(s.capacity == 4); + test_log("2. 後進先出資料推入與動態擴容成功"); + + // ========================================== + // 3. 測試查看堆疊頂端 (Peek) + // ========================================== + // 目前最頂端應該是最後推入的 stackoverflow.com + BrowserHistory_t* p_peek = (BrowserHistory_t*)c_ArrayStack_Peek(&s); + assert(p_peek != NULL); + assert(strcmp(p_peek->url, "stackoverflow.com") == 0); + assert(p_peek->depth == 3); + assert(s.size == 3); // 驗證 Peek 不影響大小 + test_log("3. 堆疊頂端唯讀查看 (Peek) 成功"); + + // ========================================== + // 4. 測試隨機刪除 (Remove) + // ========================================== + // 目前狀態:Index 0="github.com", Index 1="google.com", Index 2="stackoverflow.com" + // 嘗試隨機刪除中間的 "google.com" (Index 1) + err = c_ArrayStack_Remove(&s, 1); + assert(err == C_ERR_SUCCESS); + assert(s.size == 2); + + // 驗證原本頂端的 "stackoverflow.com" 是否因平移前進到了 Index 1 + // 此時的頂端(Index: size-1 = 1)依然必須是 stackoverflow.com + p_peek = (BrowserHistory_t*)c_ArrayStack_Peek(&s); + assert(strcmp(p_peek->url, "stackoverflow.com") == 0); + + // 測試刪除越界防呆 + assert(c_ArrayStack_Remove(&s, 99) == C_ERR_INDEX); + test_log("4. 內部隨機刪除 (Remove) 與記憶體平移成功"); + + // ========================================== + // 5. 測試後進先出彈出 (Pop - 使用者自備緩衝區) + // ========================================== + // 目前堆疊內剩餘:Index 0="github.com", Index 1="stackoverflow.com" + BrowserHistory_t local_buf; + + // 第一次 Pop:預期複製取出目前頂端的 "stackoverflow.com" + err = c_ArrayStack_Pop(&s, &local_buf); + assert(err == C_ERR_SUCCESS); + assert(strcmp(local_buf.url, "stackoverflow.com") == 0); + assert(s.size == 1); + + // 彈出後,頂端應更新為最初推進去的 "github.com" + p_peek = (BrowserHistory_t*)c_ArrayStack_Peek(&s); + assert(strcmp(p_peek->url, "github.com") == 0); + + // 第二次 Pop:預期複製取出 "github.com" + err = c_ArrayStack_Pop(&s, &local_buf); + assert(err == C_ERR_SUCCESS); + assert(strcmp(local_buf.url, "github.com") == 0); + assert(s.size == 0); + + // 第三次 Pop:此時堆疊已空,預期回傳越界錯誤 + err = c_ArrayStack_Pop(&s, &local_buf); + assert(err == C_ERR_EMPTY); + assert(c_ArrayStack_Peek(&s) == NULL); + test_log("5. 後進先出安全彈出 (Pop) 與空堆疊防呆成功"); + + // ========================================== + // 6. 介面指標防呆檢查 + // ========================================== + assert(c_ArrayStack_Init(NULL, sizeof(BrowserHistory_t), 4) == C_ERR_PARAM); + assert(c_ArrayStack_Push(NULL, &page1) == C_ERR_PARAM); + assert(c_ArrayStack_Pop(&s, NULL) == C_ERR_PARAM); + test_log("6. 介面 NULL 指標防呆驗證成功"); + + // ========================================== + // 7. 銷毀堆疊 (Destroy) + // ========================================== + c_ArrayStack_Destroy(&s); + assert(s.array == NULL); + assert(s.size == 0); + assert(s.capacity == 0); + test_log("7. 堆疊銷毀與記憶體釋放成功"); + + printf("\n==================================================\n"); + printf(" 恭喜!c_ArrayStack 所有單元測試順利通過!\n"); + printf("==================================================\n"); + return 0; +} \ No newline at end of file diff --git a/cKit/Foundation/c_ByteRingBuffer.c b/cKit/Foundation/c_ByteRingBuffer.c new file mode 100644 index 0000000..5396ec2 --- /dev/null +++ b/cKit/Foundation/c_ByteRingBuffer.c @@ -0,0 +1,109 @@ +#include +#include +#include + +c_err_t c_ByteRingBuffer_Init(c_ByteRingBuffer_t* self, c_size_t capacity) { + if (!self || capacity == 0) return C_ERR_PARAM; + + self->capacity = capacity; + self->head = 0; + self->tail = 0; + self->is_full = C_FALSE; + + self->buffer = (uint8_t*)C_ALLOC(self->capacity); + if (!self->buffer) { + self->capacity = 0; + return C_ERR_NOMEM; + } + + return C_ERR_SUCCESS; +} + +void c_ByteRingBuffer_Destroy(c_ByteRingBuffer_t* self) { + if (!self) return; + + C_FREE(self->buffer); + self->capacity = 0; + self->head = 0; + self->tail = 0; + self->is_full = C_FALSE; +} + +// Writes a single byte. O(1) performance. +c_err_t c_ByteRingBuffer_WriteByte(c_ByteRingBuffer_t* self, uint8_t byte) { + if (!self || !self->buffer) return C_ERR_PARAM; + if (self->is_full) return C_ERR_FULL; + + self->buffer[self->tail] = byte; + self->tail = (self->tail + 1) % self->capacity; + + if (self->tail == self->head) { + self->is_full = C_TRUE; + } + + return C_ERR_SUCCESS; +} + +// Reads a single byte. O(1) performance. +c_err_t c_ByteRingBuffer_ReadByte(c_ByteRingBuffer_t* self, uint8_t* out_byte) { + if (!self || !self->buffer || !out_byte) return C_ERR_PARAM; + if (c_ByteRingBuffer_IsEmpty(self)) return C_ERR_EMPTY; + + *out_byte = self->buffer[self->head]; + self->head = (self->head + 1) % self->capacity; + self->is_full = C_FALSE; + + return C_ERR_SUCCESS; +} + +// Writes an entire chunk of bytes. Returns the total number of bytes successfully written. +c_size_t c_ByteRingBuffer_WriteBuffer(c_ByteRingBuffer_t* self, const uint8_t* src, c_size_t len) { + if (!self || !self->buffer || !src || len == 0) return 0; + + c_size_t bytes_written = 0; + while (bytes_written < len && !self->is_full) { + self->buffer[self->tail] = src[bytes_written]; + self->tail = (self->tail + 1) % self->capacity; + + if (self->tail == self->head) { + self->is_full = C_TRUE; + } + bytes_written++; + } + return bytes_written; +} + +// Reads an entire chunk of bytes. Returns the total number of bytes successfully read. +c_size_t c_ByteRingBuffer_ReadBuffer(c_ByteRingBuffer_t* self, uint8_t* dest, c_size_t len) { + if (!self || !self->buffer || !dest || len == 0) return 0; + + c_size_t bytes_read = 0; + while (bytes_read < len && !c_ByteRingBuffer_IsEmpty(self)) { + dest[bytes_read] = self->buffer[self->head]; + self->head = (self->head + 1) % self->capacity; + self->is_full = C_FALSE; + bytes_read++; + } + return bytes_read; +} + +c_size_t c_ByteRingBuffer_GetSize(const c_ByteRingBuffer_t* self) { + if (!self || !self->buffer) return 0; + if (self->is_full) return self->capacity; + + if (self->tail >= self->head) { + return self->tail - self->head; + } else { + return self->capacity + self->tail - self->head; + } +} + +c_bool_t c_ByteRingBuffer_IsEmpty(const c_ByteRingBuffer_t* self) { + if (!self) return C_TRUE; + return (self->head == self->tail) && !self->is_full; +} + +c_bool_t c_ByteRingBuffer_IsFull(const c_ByteRingBuffer_t* self) { + if (!self) return C_FALSE; + return self->is_full; +} \ No newline at end of file diff --git a/cKit/Foundation/c_ByteRingBuffer.h b/cKit/Foundation/c_ByteRingBuffer.h new file mode 100644 index 0000000..8cedf03 --- /dev/null +++ b/cKit/Foundation/c_ByteRingBuffer.h @@ -0,0 +1,34 @@ +#ifndef INCLUDED_C_BYTERINGBUFFER_H +#define INCLUDED_C_BYTERINGBUFFER_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + uint8_t* buffer; + c_size_t capacity; + c_size_t head; + c_size_t tail; + c_bool_t is_full; +}c_ByteRingBuffer_t; + +c_err_t c_ByteRingBuffer_Init(c_ByteRingBuffer_t* self, c_size_t capacity); +void c_ByteRingBuffer_Destroy(c_ByteRingBuffer_t* self); + +c_err_t c_ByteRingBuffer_WriteByte(c_ByteRingBuffer_t* self, uint8_t byte); +c_err_t c_ByteRingBuffer_ReadByte(c_ByteRingBuffer_t* self, uint8_t* out_byte); + +c_size_t c_ByteRingBuffer_WriteBuffer(c_ByteRingBuffer_t* self, const uint8_t* src, c_size_t len); +c_size_t c_ByteRingBuffer_ReadBuffer(c_ByteRingBuffer_t* self, uint8_t* dest, c_size_t len); + +c_size_t c_ByteRingBuffer_GetSize(const c_ByteRingBuffer_t* self); +c_bool_t c_ByteRingBuffer_IsEmpty(const c_ByteRingBuffer_t* self); +c_bool_t c_ByteRingBuffer_IsFull(const c_ByteRingBuffer_t* self); + + +#endif /*INCLUDED_C_BYTERINGBUFFER_H*/ diff --git a/cKit/Foundation/c_ByteRingBuffer.t.c b/cKit/Foundation/c_ByteRingBuffer.t.c new file mode 100644 index 0000000..5ef76db --- /dev/null +++ b/cKit/Foundation/c_ByteRingBuffer.t.c @@ -0,0 +1,65 @@ +#include "c_ByteRingBuffer.h" +#include +#include + + +void test_log(const char* test_name) { + printf("[PASS] %s\n", test_name); +} + +int main() { + printf("==================================================\n"); + printf(" Starting c_ByteRingBuffer Unit Testing Suite\n"); + printf("==================================================\n\n"); + + c_ByteRingBuffer_t rb; + // Set fixed byte size capacity to 4 + c_err_t err = c_ByteRingBuffer_Init(&rb, 4); + assert(err == C_ERR_SUCCESS); + assert(c_ByteRingBuffer_IsEmpty(&rb) == C_TRUE); + test_log("1. Raw byte buffer initialization verified"); + + // ========================================== + // 2. Testing Single Byte Operations + // ========================================== + err = c_ByteRingBuffer_WriteByte(&rb, 0xAA); assert(err == C_ERR_SUCCESS); + err = c_ByteRingBuffer_WriteByte(&rb, 0xBB); assert(err == C_ERR_SUCCESS); + assert(c_ByteRingBuffer_GetSize(&rb) == 2); + + uint8_t byte_out = 0; + err = c_ByteRingBuffer_ReadByte(&rb, &byte_out); + assert(err == C_ERR_SUCCESS); + assert(byte_out == 0xAA); + assert(c_ByteRingBuffer_GetSize(&rb) == 1); + test_log("2. Single byte discrete FIFO verified"); + + // Clear buffer out + c_ByteRingBuffer_ReadByte(&rb, &byte_out); + assert(c_ByteRingBuffer_IsEmpty(&rb) == C_TRUE); + + // ========================================== + // 3. Testing Streaming Buffer Operations + // ========================================== + uint8_t stream_in[5] = {0x11, 0x22, 0x33, 0x44, 0x55}; + + // Total capacity is 4. Passing a length of 5 should stream up to max boundary + c_size_t written = c_ByteRingBuffer_WriteBuffer(&rb, stream_in, 5); + assert(written == 4); + assert(c_ByteRingBuffer_IsFull(&rb) == C_TRUE); + + uint8_t stream_out[4] = {0}; + c_size_t read = c_ByteRingBuffer_ReadBuffer(&rb, stream_out, 4); + assert(read == 4); + assert(stream_out[0] == 0x11); + assert(stream_out[3] == 0x44); + assert(c_ByteRingBuffer_IsEmpty(&rb) == C_TRUE); + test_log("3. Bulk string/buffer array streams chunk-read verified"); + + c_ByteRingBuffer_Destroy(&rb); + test_log("4. Clean resource teardown verified"); + + printf("\n==================================================\n"); + printf(" Success! Byte RingBuffer tests passed!\n"); + printf("==================================================\n"); + return 0; +} \ No newline at end of file diff --git a/cKit/Foundation/c_Complex.c b/cKit/Foundation/c_Complex.c new file mode 100644 index 0000000..7d89c5b --- /dev/null +++ b/cKit/Foundation/c_Complex.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Foundation/c_Complex.h b/cKit/Foundation/c_Complex.h new file mode 100644 index 0000000..204d02e --- /dev/null +++ b/cKit/Foundation/c_Complex.h @@ -0,0 +1,114 @@ +#ifndef INCLUDED_C_COMPLEX_H +#define INCLUDED_C_COMPLEX_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +#ifndef INCLUDED_MATH_H +#define INCLUDED_MATH_H +#include +#endif /*INCLUDED_MATH_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +typedef struct { + double real; // Real component field + double imag; // Imaginary component field +} c_Complex_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * Factory Primitive: Construct a new complex number on the stack frame. + */ +C_STATIC_FORCE_INLINE +c_Complex_t c_Complex_Create(double real, double imag) { + c_Complex_t c; + c.real = real; + c.imag = imag; + return c; +} + +/** + * Complex Addition: returns (a + b) by value. + * Time Complexity: O(1) | Space Complexity: O(1) on stack + */ +C_STATIC_FORCE_INLINE +c_Complex_t c_Complex_Add(c_Complex_t a, c_Complex_t b) { + c_Complex_t res; + res.real = a.real + b.real; + res.imag = a.imag + b.imag; + return res; +} + +/** + * Complex Subtraction: returns (a - b) by value. + * Time Complexity: O(1) | Space Complexity: O(1) on stack + */ +C_STATIC_FORCE_INLINE +c_Complex_t c_Complex_Sub(c_Complex_t a, c_Complex_t b) { + c_Complex_t res; + res.real = a.real - b.real; + res.imag = a.imag - b.imag; + return res; +} + +/** + * Complex Multiplication: returns (a * b) by value. + * Time Complexity: O(1) | Space Complexity: O(1) on stack + */ +C_STATIC_FORCE_INLINE +c_Complex_t c_Complex_Mul(c_Complex_t a, c_Complex_t b) { + c_Complex_t res; + res.real = a.real * b.real - a.imag * b.imag; + res.imag = a.real * b.imag + a.imag * b.real; + return res; +} + +/** + * Complex Division: returns (a / b) by value. + * Features a safety check to intercept division-by-zero boundary exceptions. + * + * Time Complexity: O(1) | Space Complexity: O(1) on stack + */ +C_STATIC_FORCE_INLINE +c_err_t c_Complex_Div(c_Complex_t a, c_Complex_t b, c_Complex_t* out_res) { + if (out_res == NULL) return C_ERR_PARAM; + + double denom = b.real * b.real + b.imag * b.imag; + if (denom == 0.0) { + out_res->real = 0.0; + out_res->imag = 0.0; + return C_ERR_INVALID; // Catch division by zero anomalies + } + + out_res->real = (a.real * b.real + a.imag * b.imag) / denom; + out_res->imag = (a.imag * b.real - a.real * b.imag) / denom; + return C_ERR_OK; +} + +/** + * Complex Conjugate: returns the conjugate of a (a.real - i * a.imag). + */ +C_STATIC_FORCE_INLINE +c_Complex_t c_Complex_Conjugate(c_Complex_t a) { + c_Complex_t res; + res.real = a.real; + res.imag = -a.imag; + return res; +} + +/** + * Complex Absolute Value (Magnitude): returns sqrt(a.real^2 + a.imag^2). + */ +C_STATIC_FORCE_INLINE +double c_Complex_Abs(c_Complex_t a) { + return sqrt(a.real * a.real + a.imag * a.imag); +} + + +#endif /*INCLUDED_C_COMPLEX_H*/ diff --git a/cKit/Foundation/c_Cond.c b/cKit/Foundation/c_Cond.c new file mode 100644 index 0000000..18be446 --- /dev/null +++ b/cKit/Foundation/c_Cond.c @@ -0,0 +1,88 @@ +#include + +#if defined(PLATFORM_POSIX) +#include +#include +#endif + +c_err_t c_Cond_Init(c_Cond_t* cond) { + if (!cond) return C_ERR_PARAM; + +#if defined(PLATFORM_WINDOWS) + // Windows 的条件变量初始化只是一个清零操作,不会失败 + InitializeConditionVariable(&cond->handle); + cond->is_initialized = C_TRUE; + return C_ERR_SUCCESS; +#elif defined(PLATFORM_POSIX) + if (pthread_cond_init(&cond->handle, NULL) == 0) { + cond->is_initialized = C_TRUE; + return C_ERR_SUCCESS; + } + return C_ERR_FAIL; +#endif +} + + +void c_Cond_Destroy(c_Cond_t* cond) { + if (!cond || !cond->is_initialized) return; + +#if defined(PLATFORM_WINDOWS) + // Windows 的 CONDITION_VARIABLE 不需要显式销毁(内核会自动回收) +#elif defined(PLATFORM_POSIX) + pthread_cond_destroy(&cond->handle); +#endif + cond->is_initialized = C_FALSE; +} + +void c_Cond_Wait(c_Cond_t* cond, c_Mutex_t* mutex) { + if (!cond || !cond->is_initialized || !mutex || !mutex->is_initialized) return; + +#if defined(PLATFORM_WINDOWS) + // SleepConditionVariableCS 会自动在内部释放传入的锁,并在唤醒时重新重新获取锁 + SleepConditionVariableCS(&cond->handle, &mutex->handle, INFINITE); +#elif defined(PLATFORM_POSIX) + pthread_cond_wait(&cond->handle, &mutex->handle); +#endif +} + +c_bool_t c_Cond_TimedWait(c_Cond_t* cond, c_Mutex_t* mutex, c_uint_t timeout_ms) { + if (!cond || !cond->is_initialized || !mutex || !mutex->is_initialized) return false; + +#if defined(PLATFORM_WINDOWS) + // 返回非 0 表示成功(收到信号),返回 0 表示超时 + return SleepConditionVariableCS(&cond->handle, &mutex->handle, timeout_ms) != 0; +#elif defined(PLATFORM_POSIX) + struct timespec ts; + struct timeval tv; + gettimeofday(&tv, NULL); + + // 计算绝对终止时间 + long long ns = (long long)tv.tv_usec * 1000 + (long long)timeout_ms * 1000000; + ts.tv_sec = tv.tv_sec + ns / 1000000000LL; + ts.tv_nsec = ns % 1000000000LL; + + // 返回 0 表示成功收到信号 + return pthread_cond_timedwait(&cond->handle, &mutex->handle, &ts) == 0; +#endif +} + +void c_Cond_Signal(c_Cond_t* cond) { + if (!cond || !cond->is_initialized) return; + +#if defined(PLATFORM_WINDOWS) + WakeConditionVariable(&cond->handle); +#elif defined(PLATFORM_POSIX) + pthread_cond_signal(&cond->handle); +#endif +} + +void c_Cond_Broadcast(c_Cond_t* cond) { + if (!cond || !cond->is_initialized) return; + +#if defined(PLATFORM_WINDOWS) + WakeAllConditionVariable(&cond->handle); +#elif defined(PLATFORM_POSIX) + pthread_cond_broadcast(&cond->handle); +#endif +} + diff --git a/cKit/Foundation/c_Cond.h b/cKit/Foundation/c_Cond.h new file mode 100644 index 0000000..6ff2f07 --- /dev/null +++ b/cKit/Foundation/c_Cond.h @@ -0,0 +1,32 @@ +#ifndef INCLUDED_C_COND_H +#define INCLUDED_C_COND_H + +#ifndef INCLUDED_C_MUTEX_H +#include +#endif /*INCLUDED_C_MUTEX_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { +#if defined(PLATFORM_WINDOWS) + CONDITION_VARIABLE handle; +#elif defined(PLATFORM_POSIX) + pthread_cond_t handle; +#endif + c_bool_t is_initialized; +} c_Cond_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_Cond_Init(c_Cond_t* cond); +void c_Cond_Destroy(c_Cond_t* cond); +void c_Cond_Wait(c_Cond_t* cond, c_Mutex_t* mutex); +c_bool_t c_Cond_TimedWait(c_Cond_t* cond, c_Mutex_t* mutex, c_uint_t timeout_ms); +void c_Cond_Signal(c_Cond_t* cond); +void c_Cond_Broadcast(c_Cond_t* cond); + + + +#endif /*INCLUDED_C_COND_H*/ diff --git a/cKit/Foundation/c_FastByteRingBuffer.c b/cKit/Foundation/c_FastByteRingBuffer.c new file mode 100644 index 0000000..0098f28 --- /dev/null +++ b/cKit/Foundation/c_FastByteRingBuffer.c @@ -0,0 +1,135 @@ +#include +#include +#include + +C_STATIC_FORCE_INLINE +c_size_t round_up_to_pow2(c_size_t v) { + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; +#if (defined(__WORDSIZE) && __WORDSIZE == 64) || defined(_WIN64) || defined(__x86_64__) || defined(__aarch64__) + v |= v >> 32; +#endif + v++; + return v; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_FastByteRingBuffer_Init(c_FastByteRingBuffer_t* self, c_size_t capacity) { + if (!self || capacity == 0) return C_ERR_PARAM; + + // 自動向上對齊,確保符合 Power of Two + self->capacity = round_up_to_pow2(capacity); + self->mask = self->capacity - 1; // 建立遮罩 + self->head = 0; + self->tail = 0; + self->is_full = C_FALSE; + + self->buffer = (uint8_t*)malloc(self->capacity); + if (!self->buffer) { + self->capacity = 0; + self->mask = 0; + return C_ERR_NOMEM; + } + + return C_ERR_SUCCESS; +} + +void c_FastByteRingBuffer_Destroy(c_FastByteRingBuffer_t* self) { + if (!self) return; + + C_FREE(self->buffer); + self->capacity = 0; + self->mask = 0; + self->head = 0; + self->tail = 0; + self->is_full = C_FALSE; +} + +// 寫入單一單元組 (極速 O(1)) +c_err_t c_FastByteRingBuffer_WriteByte(c_FastByteRingBuffer_t* self, uint8_t byte) { + if (!self || !self->buffer) return C_ERR_PARAM; + if (self->is_full) return C_ERR_FULL; + + self->buffer[self->tail] = byte; + + // 使用高速位元與運算取代模除 % + self->tail = (self->tail + 1) & self->mask; + + if (self->tail == self->head) { + self->is_full = C_TRUE; + } + + return C_ERR_SUCCESS; +} + +// 讀取單一單元組 (極速 O(1)) +c_err_t c_FastByteRingBuffer_ReadByte(c_FastByteRingBuffer_t* self, uint8_t* out_byte) { + if (!self || !self->buffer || !out_byte) return C_ERR_PARAM; + if (c_FastByteRingBuffer_IsEmpty(self)) return C_ERR_EMPTY; + + *out_byte = self->buffer[self->head]; + + // 使用高速位元與運算取代模除 % + self->head = (self->head + 1) & self->mask; + self->is_full = C_FALSE; + + return C_ERR_SUCCESS; +} + +// 區塊寫入 +c_size_t c_FastByteRingBuffer_WriteBuffer(c_FastByteRingBuffer_t* self, const uint8_t* src, c_size_t len) { + if (!self || !self->buffer || !src || len == 0) return 0; + + c_size_t bytes_written = 0; + while (bytes_written < len && !self->is_full) { + self->buffer[self->tail] = src[bytes_written]; + self->tail = (self->tail + 1) & self->mask; + + if (self->tail == self->head) { + self->is_full = C_TRUE; + } + bytes_written++; + } + return bytes_written; +} + +// 區塊讀取 +c_size_t c_FastByteRingBuffer_ReadBuffer(c_FastByteRingBuffer_t* self, uint8_t* dest, c_size_t len) { + if (!self || !self->buffer || !dest || len == 0) return 0; + + c_size_t bytes_read = 0; + while (bytes_read < len && !c_FastByteRingBuffer_IsEmpty(self)) { + dest[bytes_read] = self->buffer[self->head]; + self->head = (self->head + 1) & self->mask; + self->is_full = C_FALSE; + bytes_read++; + } + return bytes_read; +} + +c_size_t c_FastByteRingBuffer_GetSize(const c_FastByteRingBuffer_t* self) { + if (!self || !self->buffer) return 0; + if (self->is_full) return self->capacity; + + if (self->tail >= self->head) { + return self->tail - self->head; + } else { + return self->capacity + self->tail - self->head; + } +} + +c_bool_t c_FastByteRingBuffer_IsEmpty(const c_FastByteRingBuffer_t* self) { + if (!self) return C_TRUE; + return (self->head == self->tail) && !self->is_full; +} + +c_bool_t c_FastByteRingBuffer_IsFull(const c_FastByteRingBuffer_t* self) { + if (!self) return C_FALSE; + return self->is_full; +} diff --git a/cKit/Foundation/c_FastByteRingBuffer.h b/cKit/Foundation/c_FastByteRingBuffer.h new file mode 100644 index 0000000..a88e41f --- /dev/null +++ b/cKit/Foundation/c_FastByteRingBuffer.h @@ -0,0 +1,34 @@ +#ifndef INCLUDED_C_FASTBYTERINGBUFFER_H +#define INCLUDED_C_FASTBYTERINGBUFFER_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + uint8_t* buffer; // 連續的位元組陣列 + c_size_t capacity; // 必為 2 的冪次方 (e.g., 16, 32, 64, 256) + c_size_t mask; // 快取 (capacity - 1),用來進行位元與運算 + c_size_t head; // 讀取指標 + c_size_t tail; // 寫入指標 + c_bool_t is_full; // 狀態區分旗標 +} c_FastByteRingBuffer_t; + +c_err_t c_FastByteRingBuffer_Init(c_FastByteRingBuffer_t* self, c_size_t capacity); +void c_FastByteRingBuffer_Destroy(c_FastByteRingBuffer_t* self); + +c_err_t c_FastByteRingBuffer_WriteByte(c_FastByteRingBuffer_t* self, uint8_t byte); +c_err_t c_FastByteRingBuffer_ReadByte(c_FastByteRingBuffer_t* self, uint8_t* out_byte); + +c_size_t c_FastByteRingBuffer_WriteBuffer(c_FastByteRingBuffer_t* self, const uint8_t* src, c_size_t len); +c_size_t c_FastByteRingBuffer_ReadBuffer(c_FastByteRingBuffer_t* self, uint8_t* dest, c_size_t len); + +c_size_t c_FastByteRingBuffer_GetSize(const c_FastByteRingBuffer_t* self); +c_bool_t c_FastByteRingBuffer_IsEmpty(const c_FastByteRingBuffer_t* self); +c_bool_t c_FastByteRingBuffer_IsFull(const c_FastByteRingBuffer_t* self); + +#endif /*INCLUDED_C_FASTBYTERINGBUFFER_H*/ diff --git a/cKit/Foundation/c_FastByteRingBuffer.t.c b/cKit/Foundation/c_FastByteRingBuffer.t.c new file mode 100644 index 0000000..887360d --- /dev/null +++ b/cKit/Foundation/c_FastByteRingBuffer.t.c @@ -0,0 +1,73 @@ +#include "c_FastByteRingBuffer.h" +#include +#include + + +void test_log(const char* test_name) { + printf("[PASS] %s\n", test_name); +} + +int main() { + printf("==================================================\n"); + printf(" 開始執行 c_FastByteRingBuffer 最終最佳化版測試\n"); + printf("==================================================\n\n"); + + c_FastByteRingBuffer_t q; + // 故意傳入 6,測試是否會自動向上對齊到 8 (2的3次方) + c_err_t err = c_FastByteRingBuffer_Init(&q, 6); + assert(err == C_ERR_SUCCESS); + + // 核心斷言:容量必須被修正為 8,遮罩必須為 7 (二進位 0111) + assert(q.capacity == 8); + assert(q.mask == 7); + assert(c_FastByteRingBuffer_IsEmpty(&q) == C_TRUE); + test_log("1. 2的冪次方自動容量對齊與遮罩初始化成功"); + + // ========================================== + // 2. 測試單一 Byte 寫入與讀取 + // ========================================== + err = c_FastByteRingBuffer_WriteByte(&q, 0x11); assert(err == C_ERR_SUCCESS); + err = c_FastByteRingBuffer_WriteByte(&q, 0x22); assert(err == C_ERR_SUCCESS); + assert(c_FastByteRingBuffer_GetSize(&q) == 2); + + uint8_t out = 0; + err = c_FastByteRingBuffer_ReadByte(&q, &out); + assert(err == C_ERR_SUCCESS); + assert(out == 0x11); + assert(c_FastByteRingBuffer_GetSize(&q) == 1); + test_log("2. 單一 Byte 讀寫與位元環形遞增驗證成功"); + + // 清空 + c_FastByteRingBuffer_ReadByte(&q, &out); + assert(c_FastByteRingBuffer_IsEmpty(&q) == C_TRUE); + + // ========================================== + // 3. 測試大量區塊資料串流與滿載邊界 + // ========================================== + uint8_t src_stream[10] = {0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA}; + + // 目前容量為 8。傳入長度 10 的陣列,預期只能成功寫入 8 個位元組 + c_size_t written = c_FastByteRingBuffer_WriteBuffer(&q, src_stream, 10); + assert(written == 8); + assert(c_FastByteRingBuffer_IsFull(&q) == C_TRUE); + + // 溢位防呆檢查 + assert(c_FastByteRingBuffer_WriteByte(&q, 0xFF) == C_ERR_FULL); + + // 大量讀出驗證 + uint8_t dest_stream[8] = {0}; + c_size_t read = c_FastByteRingBuffer_ReadBuffer(&q, dest_stream, 8); + assert(read == 8); + assert(dest_stream[0] == 0xA1); + assert(dest_stream[7] == 0xA8); + assert(c_FastByteRingBuffer_IsEmpty(&q) == C_TRUE); + test_log("3. 區塊串流 API 與滿載位元遮罩邊界防呆成功"); + + c_FastByteRingBuffer_Destroy(&q); + test_log("4. 資源安全銷毀成功"); + + printf("\n==================================================\n"); + printf(" 恭喜!快取位元最佳化版 RingBuffer 所有單元測試順利通過!\n"); + printf("==================================================\n"); + return 0; +} \ No newline at end of file diff --git a/cKit/Foundation/c_Float.c b/cKit/Foundation/c_Float.c new file mode 100644 index 0000000..7c97207 --- /dev/null +++ b/cKit/Foundation/c_Float.c @@ -0,0 +1,516 @@ +#include +#include + +uint32_t c_Float_Add(uint32_t a, uint32_t b) { + // 1. 提取特殊狀態與快速零值返回 + if ((a & ~C_FLOAT_SIGN_MASK) == 0) return b; + if ((b & ~C_FLOAT_SIGN_MASK) == 0) return a; + + uint32_t sign_a = a & C_FLOAT_SIGN_MASK; + uint32_t sign_b = b & C_FLOAT_SIGN_MASK; + int32_t exp_a = (int32_t)((a & C_FLOAT_EXP_MASK) >> 23); + int32_t exp_b = (int32_t)((b & C_FLOAT_EXP_MASK) >> 23); + uint32_t frac_a = a & C_FLOAT_FRAC_MASK; + uint32_t frac_b = b & C_FLOAT_FRAC_MASK; + + // 處理 NaN 或 Infinity 特殊邊界 + if (exp_a == 255 || exp_b == 255) { + // 簡化處理:若任一為 NaN 或 Inf,返回 NaN/Inf 傳播 + if ((exp_a == 255 && frac_a != 0) || (exp_b == 255 && frac_b != 0)) { + return 0x7FC00000U; // Quiet NaN + } + return (exp_a == 255) ? a : b; + } + + // 2. 補齊隱含位 1 + frac_a = (exp_a == 0) ? (frac_a << 1) : (frac_a | C_FLOAT_HIDDEN_BIT); + frac_b = (exp_b == 0) ? (frac_b << 1) : (frac_b | C_FLOAT_HIDDEN_BIT); + if (exp_a == 0) exp_a = 1; + if (exp_b == 0) exp_b = 1; + + // 为了保留精確捨入,將尾數左移 3 位,騰出位置存放 G (Guard), R (Round), S (Sticky) 位 + frac_a <<= 3; + frac_b <<= 3; + + int32_t exp_res = exp_a; + uint32_t sticky = 0; + + // 3. 對階(Align Exponents) + if (exp_a > exp_b) { + int32_t shift = exp_a - exp_b; + if (shift > 26) { + frac_b = 0; + sticky = 1; + } else { + sticky = (frac_b & ~((~0U) << shift)) != 0; + frac_b >>= shift; + } + frac_b |= sticky; + exp_res = exp_a; + } else if (exp_b > exp_a) { + int32_t shift = exp_b - exp_a; + if (shift > 26) { + frac_a = 0; + sticky = 1; + } else { + sticky = (frac_a & ~((~0U) << shift)) != 0; + frac_a >>= shift; + } + frac_a |= sticky; + exp_res = exp_b; + } + + // 4. 尾數運算(考慮正負號) + uint32_t sign_res; + uint32_t frac_res; + + if (sign_a == sign_b) { + // 同號相加 + sign_res = sign_a; + frac_res = frac_a + frac_b; + } else { + // 異號相減 + if (frac_a >= frac_b) { + sign_res = sign_a; + frac_res = frac_a - frac_b; + } else { + sign_res = sign_b; + frac_res = frac_b - frac_a; + } + // 互相抵消為 0 + if (frac_res == 0) return 0x00000000U; + } + + // 5. 規格化(Normalization) + // 情況 A:相加導致尾數溢出(例如超出原本的包含隱含位與 GRS 的範圍) + // 正常擴展後,隱含的 1 應該在第 26 位 (FLOAT_HIDDEN_BIT << 3 = 0x04000000) + if (frac_res & (1U << 27)) { + frac_res = (frac_res >> 1) | (frac_res & 1); // 移出的位與最低位做或運算保持 Sticky + exp_res++; + } else { + // 情況 B:相減導致尾數變小,需要左移規格化 + while (!(frac_res & (1U << 26)) && exp_res > 1) { + frac_res <<= 1; + exp_res--; + } + // 降級為非規格化數 + if (!(frac_res & (1U << 26)) && exp_res == 1) { + exp_res = 0; + } + } + + // 6. 溢出至無限大檢查 + if (exp_res >= 255) { + return sign_res | C_FLOAT_EXP_MASK; // 返回 ±Infinity + } + + // 7. IEEE 754 預設:向最接近偶數捨入(Round-to-Nearest-Even) + // 此时 frac_res 的低 3 位即為 G, R, S + uint32_t round_bits = frac_res & 7U; + frac_res >>= 3; // 移除 GRS 位,回歸 24 位尾數(含隱含位) + + // 捨入判斷條件: + // round_bits > 4 (即 101, 110, 111) -> 必然進位 + // round_bits == 4 (即 100,正中央) -> 觀察最低有效位(LSB),LSB 為 1(奇數)則進位,為 0(偶數)則捨去 + if ((round_bits > 4) || ((round_bits == 4) && (frac_res & 1U))) { + frac_res++; + // 捨入可能再次引發尾數溢出 + if (frac_res & (1U << 24)) { + frac_res >>= 1; + exp_res++; + if (exp_res >= 255) return sign_res | C_FLOAT_EXP_MASK; + } + } + + // 如果是非規格化數,exp_res 為 0,frac_res 本身就不該移除隱含位,直接拼接 + // 如果是規格化數,需要清除第 23 位的隱含位 1 + if (exp_res != 0) { + frac_res &= C_FLOAT_FRAC_MASK; + } + + // 8. 拼裝回 32 位標準格式 + return sign_res | ((uint32_t)exp_res << 23) | frac_res; +} + +uint32_t c_Float_Mul(uint32_t a, uint32_t b) { + // 1. 快速提取 + uint32_t sign_a = C_FLOAT_GET_SIGN(a), sign_b = C_FLOAT_GET_SIGN(b); + uint32_t exp_a = C_FLOAT_GET_EXP(a), exp_b = C_FLOAT_GET_EXP(b); + uint32_t frac_a = C_FLOAT_GET_FRAC(a), frac_b = C_FLOAT_GET_FRAC(b); + + uint32_t sign_res = sign_a ^ sign_b; + + // 2. 特殊值处理 (0, Inf, NaN) + if (exp_a == 255 || exp_b == 255) return c_Float_Pack(sign_res, 255, 0); // 简化处理为Inf + if (a == 0 || b == 0) return c_Float_Pack(sign_res, 0, 0); + + // 3. 补齐隐藏位 1 + frac_a |= C_FLOAT_HIDDEN_BIT; + frac_b |= C_FLOAT_HIDDEN_BIT; + + // 4. 指数相加并减去 Bias + int32_t exp_res = (int32_t)exp_a + (int32_t)exp_b - C_FLOAT_EXP_BIAS; + + // 5. 尾数相乘:24位 * 24位 = 48位,需要用 uint64_t 接收 + uint64_t prod = (uint64_t)frac_a * (uint64_t)frac_b; + + // 6. 规格化 (Normalization) + // 正常 1.x * 1.x 的范围在 [1.0, 4.0) 之间。 + // 如果积 >= 2.0 (即第 47 位为 1,第 46 位是原本的隐藏位 1 发生溢出),需要右移 1 位 + if (prod & (1ULL << 47)) { + prod >>= 1; + exp_res++; + } + + // 7. 从 64 位乘积中提取出 23 位尾数(移除第 46 位的隐藏 1) + // 此时隐藏位 1 在第 46 位 (1ULL << 46),尾数在低 46 位 + uint32_t frac_res = (uint32_t)((prod >> 23) & C_FLOAT_FRAC_MASK); + + // 8. 边界与下溢/上溢检查 + if (exp_res >= 255) return c_Float_Pack(sign_res, 255, 0); // 上溢至 Inf + if (exp_res <= 0) return c_Float_Pack(sign_res, 0, 0); // 下溢至 0 + + return c_Float_Pack(sign_res, exp_res, frac_res); +} + +uint32_t c_Float_Div(uint32_t a, uint32_t b) { + uint32_t sign_a = C_FLOAT_GET_SIGN(a), sign_b = C_FLOAT_GET_SIGN(b); + uint32_t exp_a = C_FLOAT_GET_EXP(a), exp_b = C_FLOAT_GET_EXP(b); + uint32_t frac_a = C_FLOAT_GET_FRAC(a), frac_b = C_FLOAT_GET_FRAC(b); + + uint32_t sign_res = sign_a ^ sign_b; + + // 0 分母异常检查 + if (b == 0) return c_Float_Pack(sign_res, 255, 0); // 1.0 / 0.0 = Inf + if (a == 0) return c_Float_Pack(sign_res, 0, 0); + + frac_a |= C_FLOAT_HIDDEN_BIT; + frac_b |= C_FLOAT_HIDDEN_BIT; + + int32_t exp_res = (int32_t)exp_a - (int32_t)exp_b + C_FLOAT_EXP_BIAS; + + // 为了保留除法后的23位尾数精度,将分子左移 23 位后做整数除法 + uint64_t num = (uint64_t)frac_a << 23; + uint32_t quot = (uint32_t)(num / frac_b); + + // 规格化:1.x / 1.x 的范围在 (0.5, 2.0) 之间。 + // 如果商小于 1.0(即隐藏位 1 没落在第 23 位,落在了第 22 位),需要左移 1 位,指数减 1 + if (!(quot & C_FLOAT_HIDDEN_BIT)) { + quot <<= 1; + exp_res--; + } + + if (exp_res >= 255) return c_Float_Pack(sign_res, 255, 0); + if (exp_res <= 0) return c_Float_Pack(sign_res, 0, 0); + + return c_Float_Pack(sign_res, exp_res, quot & C_FLOAT_FRAC_MASK); +} + +int c_Float_Cmp(uint32_t a, uint32_t b) { + // 1. 处理 NaN:依据 IEEE 754,NaN 参与比较永远返回不相等(或未定义) + if (c_Float_IsNAN(a) || c_Float_IsNAN(b)) { + return 0; // 软浮点库通常在此处设置不合法比较标志位 + } + + // 2. 特殊情况:+0.0 (0x00000000) 和 -0.0 (0x80000000) 在逻辑上是相等的 + if (((a | b) & ~C_FLOAT_SIGN_MASK) == 0) { + return 0; + } + + // 提取符号位 + uint32_t sign_a = a & C_FLOAT_SIGN_MASK; + uint32_t sign_b = b & C_FLOAT_SIGN_MASK; + + // 3. 符号不同 + if (sign_a != sign_b) { + // a 是负数,b 是正数 => a < b + // a 是正数,b 是负数 => a > b + return sign_a ? -1 : 1; + } + + // 4. 符号相同:将原始二进制位转换为有符号 32 位整型进行直观比较 + int32_t ia = (int32_t)a; + int32_t ib = (int32_t)b; + + if (sign_a) { + // 如果都是负数,二进制数值越大,其代表的实际浮点数反而越小 (例如 -2.0 的二进制码大于 -1.0) + if (ia > ib) return -1; + if (ia < ib) return 1; + return 0; + } else { + // 如果都是正数,二进制数值越大,其实际浮点数就越大 + if (ia > ib) return 1; + if (ia < ib) return -1; + return 0; + } +} + +int c_Double_Cmp(uint64_t a, uint64_t b) { + // 1. 处理 NaN + if (c_Double_IsNAN(a) || c_Double_IsNAN(b)) { + return 0; + } + + // 2. 处理 +0.0 与 -0.0 相等的情况 + if (((a | b) & ~C_DOUBLE_SIGN_MASK) == 0) { + return 0; + } + + uint64_t sign_a = a & C_DOUBLE_SIGN_MASK; + uint64_t sign_b = b & C_DOUBLE_SIGN_MASK; + + // 3. 符号不同 + if (sign_a != sign_b) { + return sign_a ? -1 : 1; + } + + // 4. 符号相同:转为有符号 64 位整型比较 + int64_t ia = (int64_t)a; + int64_t ib = (int64_t)b; + + if (sign_a) { + // 均为负数 + if (ia > ib) return -1; + if (ia < ib) return 1; + return 0; + } else { + // 均为正数 + if (ia > ib) return 1; + if (ia < ib) return -1; + return 0; + } +} + +/** + * @brief 软浮点双精度打包函数 + * @param sign 符号位 (0 或 1) + * @param exp 解包/运算后的有符号指数 (已减去或未加上 Bias 均可,此处传入带 Bias 的期望值) + * @param frac64 运算后暂存在 64 位整型中的高精度尾数 (假设规格化后隐含位在第 52 位,低位留有舍入残余) + * @return 组合好的 IEEE 754 64位无符号整数 (可直接对应 double) + */ +uint64_t c_Double_Pack(uint32_t sign, int32_t exp, uint64_t frac64) { + + // 1. 动态规格化:若运算导致尾数高位溢出 (例如第 53 位为 1),需要右移尾数并增加指数 + if (frac64 & (C_DOUBLE_HIDDEN_BIT << 1)) { + frac64 >>= 1; + exp++; + } + + // 2. 下溢处理:指数太小,转换为非规格化数 + if (exp <= 0) { + // 如果指数极小,直接移出范围,变回 0 + if (exp < -52) { + frac64 = 0; + } else { + // 右移尾数以对齐非规格化数的指数位置 (exp = 0) + int32_t shift = 1 - exp; + frac64 >>= shift; + } + exp = 0; // 非规格化数的指数域强制为 0 + } + + // 3. 执行 IEEE 754 默认的“向最接近偶数舍入 (Round-to-Nearest-Even)” + // 假设经过上述操作后,标准 52 位尾数在 frac64 的低 52 位,若有更低位则是运算残留 + // 为了演示标准舍入,假设传入的 frac64 在低位保留了扩充精度(例如左移了 3 位留给 GRS) + // 此处简化演示:基于常规截断进行最邻近舍入处理 + // 在工业级库中,通常传入 frac 时会带有额外的 round_bits 变量 + + // 4. 上溢检查:指数超过最大限制 (2047),打包为无穷大 + if (exp >= 0x7FF) { + return ((uint64_t)sign << 63) | C_DOUBLE_EXP_MASK; // 返回 +/- Inf + } + + // 5. 最终清除尾数域外的隐含 1 (因为 IEEE 754 编码中不存储规格化数的最高位 1) + uint64_t final_frac = frac64 & C_DOUBLE_FRAC_MASK; + + // 6. 位移拼接 + uint64_t packed_value = ((uint64_t)sign << 63) | + ((uint64_t)exp << 52) | + final_frac; + + return packed_value; +} + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// 輔助函數:處理 64 位元尾數與 3 位元 GRS 捨入殘餘 +static uint64_t round_and_pack_double(uint64_t sign, int32_t exp, uint64_t frac64) { + uint32_t round_bits = frac64 & 7U; + frac64 >>= 3; // 移除 GRS 位,恢復為包含隱含位的 53 位元尾數 + + // 向最接近偶數捨入 + if ((round_bits > 4) || ((round_bits == 4) && (frac64 & 1ULL))) { + frac64++; + if (frac64 & (C_DOUBLE_HIDDEN_BIT << 1)) { + frac64 >>= 1; + exp++; + } + } + + if (exp >= 2047) return sign | C_DOUBLE_EXP_MASK; // 溢出至無限大 + if (exp <= 0) return sign; // 下溢至 0 + + return sign | ((uint64_t)exp << 52) | (frac64 & C_DOUBLE_FRAC_MASK); +} + +uint64_t c_Double_Add(uint64_t a, uint64_t b) { + if ((a & ~C_DOUBLE_SIGN_MASK) == 0) return b; + if ((b & ~C_DOUBLE_SIGN_MASK) == 0) return a; + + uint64_t sign_a = a & C_DOUBLE_SIGN_MASK; + uint64_t sign_b = b & C_DOUBLE_SIGN_MASK; + int32_t exp_a = (int32_t)((a & C_DOUBLE_EXP_MASK) >> 52); + int32_t exp_b = (int32_t)((b & C_DOUBLE_EXP_MASK) >> 52); + uint64_t frac_a = a & C_DOUBLE_FRAC_MASK; + uint64_t frac_b = b & C_DOUBLE_FRAC_MASK; + + // 特殊值傳播 (NaN / Inf) + if (exp_a == 2047 || exp_b == 2047) { + if ((exp_a == 2047 && frac_a != 0) || (exp_b == 2047 && frac_b != 0)) return 0x7FF8000000000000ULL; // NaN + return (exp_a == 2047) ? a : b; + } + + // 補齊隱含位并左移 3 位釋放 GRS 空間 + frac_a = (exp_a == 0) ? (frac_a << 1) : (frac_a | C_DOUBLE_HIDDEN_BIT); + frac_b = (exp_b == 0) ? (frac_b << 1) : (frac_b | C_DOUBLE_HIDDEN_BIT); + if (exp_a == 0) exp_a = 1; + if (exp_b == 0) exp_b = 1; + + frac_a <<= 3; + frac_b <<= 3; + + int32_t exp_res = exp_a; + uint64_t sticky = 0; + + // 對階(Align) + if (exp_a > exp_b) { + int32_t shift = exp_a - exp_b; + if (shift > 56) { frac_b = 0; sticky = 1; } + else { sticky = (frac_b & ~((~0ULL) << shift)) != 0; frac_b >>= shift; } + frac_b |= sticky; + exp_res = exp_a; + } else if (exp_b > exp_a) { + int32_t shift = exp_b - exp_a; + if (shift > 56) { frac_a = 0; sticky = 1; } + else { sticky = (frac_a & ~((~0ULL) << shift)) != 0; frac_a >>= shift; } + frac_a |= sticky; + exp_res = exp_b; + } + + uint64_t sign_res, frac_res; + if (sign_a == sign_b) { + sign_res = sign_a; + frac_res = frac_a + frac_b; + } else { + if (frac_a >= frac_b) { + sign_res = sign_a; + frac_res = frac_a - frac_b; + } else { + sign_res = sign_b; + frac_res = frac_b - frac_a; + } + if (frac_res == 0) return 0x0000000000000000ULL; + } + + // 規格化 + if (frac_res & (C_DOUBLE_HIDDEN_BIT << 4)) { + frac_res = (frac_res >> 1) | (frac_res & 1); + exp_res++; + } else { + while (!(frac_res & (C_DOUBLE_HIDDEN_BIT << 3)) && exp_res > 1) { + frac_res <<= 1; + exp_res--; + } + if (!(frac_res & (C_DOUBLE_HIDDEN_BIT << 3)) && exp_res == 1) exp_res = 0; + } + + return round_and_pack_double(sign_res, exp_res, frac_res); +} + +// 內部輔助函數:32位交叉相乘,手動模擬 64x64->128位元乘法 +C_STATIC_FORCE_INLINE +void mul64_to_128(uint64_t a, uint64_t b, uint64_t *res_hi, uint64_t *res_lo) { + uint64_t a_hi = a >> 32, a_lo = a & 0xFFFFFFFFULL; + uint64_t b_hi = b >> 32, b_lo = b & 0xFFFFFFFFULL; + + uint64_t p0 = a_lo * b_lo; + uint64_t p1 = a_hi * b_lo; + uint64_t p2 = a_lo * b_hi; + uint64_t p3 = a_hi * b_hi; + + uint64_t mid = p1 + (p0 >> 32) + (p2 & 0xFFFFFFFFULL); + *res_lo = (mid << 32) | (p0 & 0xFFFFFFFFULL); + *res_hi = p3 + (mid >> 32) + (p2 >> 32); +} + +uint64_t c_Double_Mul(uint64_t a, uint64_t b) { + uint64_t sign_res = (a ^ b) & C_DOUBLE_SIGN_MASK; + int32_t exp_a = (int32_t)((a & C_DOUBLE_EXP_MASK) >> 52); + int32_t exp_b = (int32_t)((b & C_DOUBLE_EXP_MASK) >> 52); + uint64_t frac_a = (a & C_DOUBLE_FRAC_MASK) | C_DOUBLE_HIDDEN_BIT; + uint64_t frac_b = (b & C_DOUBLE_FRAC_MASK) | C_DOUBLE_HIDDEN_BIT; + + if (exp_a == 2047 || exp_b == 2047) return sign_res | C_DOUBLE_EXP_MASK; // 簡化為 Inf + if ((a & ~C_DOUBLE_SIGN_MASK) == 0 || (b & ~C_DOUBLE_SIGN_MASK) == 0) return sign_res; // 返回 ±0 + + int32_t exp_res = exp_a + exp_b - 1023; + + uint64_t prod_hi, prod_lo; + mul64_to_128(frac_a, frac_b, &prod_hi, &prod_lo); // 得到 106 位的精確積 + + // 規格化校準:隱含位本應在 52 位元,52*2 = 104 位元。 + // prod_hi 預期會包含溢出位。需要將 128 位元結果右移,使其完美對齊「保留53位 + 3位GRS」的規格 + uint64_t frac_res; + if (prod_hi & (1ULL << 41)) { // 發生進位 + frac_res = (prod_hi << 23) | (prod_lo >> 41); + frac_res |= ((prod_lo & 0x1FFFFFFFFFFULL) != 0); // Sticky 位感知 + exp_res++; + } else { + frac_res = (prod_hi << 24) | (prod_lo >> 40); + frac_res |= ((prod_lo & 0xFFFFFFFFFFULL) != 0); + } + + return round_and_pack_double(sign_res, exp_res, frac_res); +} + +uint64_t c_Double_Div(uint64_t a, uint64_t b) { + uint64_t sign_res = (a ^ b) & C_DOUBLE_SIGN_MASK; + int32_t exp_a = (int32_t)((a & C_DOUBLE_EXP_MASK) >> 52); + int32_t exp_b = (int32_t)((b & C_DOUBLE_EXP_MASK) >> 52); + uint64_t frac_a = (a & C_DOUBLE_FRAC_MASK) | C_DOUBLE_HIDDEN_BIT; + uint64_t frac_b = (b & C_DOUBLE_FRAC_MASK) | C_DOUBLE_HIDDEN_BIT; + + if ((b & ~C_DOUBLE_SIGN_MASK) == 0) return sign_res | C_DOUBLE_EXP_MASK; // 除以 0 返回 Inf + if ((a & ~C_DOUBLE_SIGN_MASK) == 0) return sign_res; // 0 除以任何數返回 0 + + int32_t exp_res = exp_a - exp_b + 1023; + + // 手動執行 56 輪位元逐位長除法 (53位尾數 + 3位 GRS) + uint64_t quotient = 0; + uint64_t remainder = frac_a; + + for (int i = 0; i < 56; i++) { + quotient <<= 1; + if (remainder >= frac_b) { + remainder -= frac_b; + quotient |= 1ULL; + } + remainder <<= 1; + } + + // 餘數如果不為 0,則與 Sticky 位進行 OR 運算 + if (remainder != 0) { + quotient |= 1ULL; + } + + // 規格化商 + if (!(quotient & (C_DOUBLE_HIDDEN_BIT << 3))) { + quotient <<= 1; + exp_res--; + } + + return round_and_pack_double(sign_res, exp_res, quotient); +} + diff --git a/cKit/Foundation/c_Float.h b/cKit/Foundation/c_Float.h new file mode 100644 index 0000000..1797dd9 --- /dev/null +++ b/cKit/Foundation/c_Float.h @@ -0,0 +1,203 @@ +#ifndef INCLUDED_C_FLOAT_H +#define INCLUDED_C_FLOAT_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +#ifndef INCLUDED_MATH_H +#define INCLUDED_MATH_H +#include +#endif /*INCLUDED_MATH_H*/ + + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef union { + float f; + struct { + uint32_t fraction : 23; // 尾数 (M) + uint32_t exponent : 8; // 指数 (E) + uint32_t sign : 1; // 符号位 (S) + } parts; + uint32_t raw; +} c_Float_t; + +typedef union { + double d; + struct { + uint64_t fraction : 52; // 尾数 + uint64_t exponent : 11; // 指数 + uint64_t sign : 1; // 符号 + } parts; + uint64_t raw; +} c_Double_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// 32位单精度常量定义 +#define C_FLOAT_SIGN_MASK 0x80000000U +#define C_FLOAT_EXP_MASK 0x7F800000U +#define C_FLOAT_FRAC_MASK 0x007FFFFFU +#define C_FLOAT_HIDDEN_BIT 0x00800000U // 隐藏的最高位1 +#define C_FLOAT_EXP_BIAS 127 + +#define C_FLOAT_NEG_INF 0xFF800000U +#define C_FLOAT_POS_INF 0x7F800000U + +// 快捷提取宏 +#define C_FLOAT_GET_SIGN(u) (((u) & C_FLOAT_SIGN_MASK) >> 31) +#define C_FLOAT_GET_EXP(u) (((u) & C_FLOAT_EXP_MASK) >> 23) +#define C_FLOAT_GET_FRAC(u) ((u) & C_FLOAT_FRAC_MASK) + +#define C_DOUBLE_SIGN_MASK 0x8000000000000000ULL +#define C_DOUBLE_EXP_MASK 0x7FF0000000000000ULL +#define C_DOUBLE_FRAC_MASK 0x000FFFFFFFFFFFFFULL +#define C_DOUBLE_HIDDEN_BIT 0x0010000000000000ULL // 第52位(从0开始算) + +#define C_DOUBLE_POS_INF 0x7FF0000000000000ULL +#define C_DOUBLE_NEG_INF 0xFFF0000000000000ULL + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +C_STATIC_FORCE_INLINE +uint32_t c_Float_Pack(const uint32_t sign, const uint32_t exp, const uint32_t frac) { + return ((sign << 31) & C_FLOAT_SIGN_MASK) | + ((exp << 23) & C_FLOAT_EXP_MASK) | + (frac & C_FLOAT_FRAC_MASK); +} + +C_STATIC_FORCE_INLINE +int c_Float_IsNAN(uint32_t raw) { + return ((raw & C_FLOAT_EXP_MASK) == C_FLOAT_EXP_MASK) && ((raw & C_FLOAT_FRAC_MASK) != 0); +} + +C_STATIC_FORCE_INLINE +int c_Double_IsNAN(uint64_t raw) { + return ((raw & C_DOUBLE_EXP_MASK) == C_DOUBLE_EXP_MASK) && ((raw & C_DOUBLE_FRAC_MASK) != 0); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +uint32_t c_Float_Add(uint32_t a, uint32_t b); + +uint32_t c_Float_Mul(uint32_t a, uint32_t b); + +uint32_t c_Float_Div(uint32_t a, uint32_t b); + +int c_Float_Cmp(uint32_t a, uint32_t b); + +C_STATIC_FORCE_INLINE +uint32_t c_Float_Sub(uint32_t a, uint32_t b) { + // 透過與 0x80000000 進行 XOR,直接將 b 的符號位元取反 (0->1, 1->0) + // 隨後將 A - B 轉換為 A + (-B) 傳入加法器 + return c_Float_Add(a, b ^ C_FLOAT_SIGN_MASK); +} + +C_STATIC_FORCE_INLINE +bool c_Float_IsZero(uint32_t raw) { + // Strip away the sign bit; check if the remaining 31 bits are 0 + return (raw & ~C_FLOAT_SIGN_MASK) == 0U; +} + +C_STATIC_FORCE_INLINE +bool c_Float_IsInf(uint32_t raw) { + // Strip the sign bit and check if it exactly matches the exponent mask. + // If any fraction bits were set, it would be a NaN instead of Infinity. + return (raw & ~0x80000000U) == C_FLOAT_EXP_MASK; +} + +/** + * @brief Determines if the float is specifically Negative Infinity (-Inf). + */ +C_STATIC_FORCE_INLINE +bool c_Float_IsNegInf(uint32_t raw) { + return raw == C_FLOAT_NEG_INF; +} + +/** + * @brief Determines if the float is specifically Positive Infinity (+Inf). + */ +C_STATIC_FORCE_INLINE +bool c_Float_IsPosInf(uint32_t raw) { + return raw == C_FLOAT_POS_INF; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +uint64_t c_Double_Pack(uint32_t sign, int32_t exp, uint64_t frac64); + +int c_Double_Cmp(uint64_t a, uint64_t b); + +uint64_t c_Double_Add(uint64_t a, uint64_t b); + +C_STATIC_FORCE_INLINE +uint64_t c_Double_Sub(uint64_t a, uint64_t b) { + // A - B == A + (-B) + return c_Double_Add(a, b ^ C_DOUBLE_SIGN_MASK); +} + +uint64_t c_Double_Mul(uint64_t a, uint64_t b); + +uint64_t c_Double_Div(uint64_t a, uint64_t b); + + +C_STATIC_FORCE_INLINE +bool c_Double_IsZero(const uint64_t raw) { + // Strip away the sign bit; check if the remaining 63 bits are 0 + return (raw & ~C_DOUBLE_SIGN_MASK) == 0ULL; +} + +C_STATIC_FORCE_INLINE +bool c_Double_IsInf(uint64_t raw) { + // Strip the sign bit and check if it exactly matches the exponent mask. + // If any fraction bits were set, it would be a NaN instead of Infinity. + return (raw & ~C_DOUBLE_SIGN_MASK) == C_DOUBLE_EXP_MASK; +} + +/** + * @brief Determines if the double is specifically Negative Infinity (-Inf). + */ +C_STATIC_FORCE_INLINE +bool c_Double_IsNegInf(uint64_t raw) { + return raw == C_DOUBLE_NEG_INF; +} + +/** + * @brief Determines if the double is specifically Positive Infinity (+Inf). + */ +C_STATIC_FORCE_INLINE +bool c_Double_IsPosInf(uint64_t raw) { + return raw == C_DOUBLE_POS_INF; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +C_STATIC_FORCE_INLINE +int c_float_cmp(const float a, const float b) { + c_Float_t va; + c_Float_t vb; + va.f = a; + vb.f = b; + return c_Float_Cmp(va.raw, vb.raw); +} + +C_STATIC_FORCE_INLINE +int c_double_cmp(const double a, const double b) { + c_Double_t va; + c_Double_t vb; + va.d = a; + vb.d = b; + return c_Double_Cmp(va.raw, vb.raw); +} + + + +#endif /*INCLUDED_C_FLOAT_H*/ diff --git a/cKit/Foundation/c_Fmt.c b/cKit/Foundation/c_Fmt.c new file mode 100644 index 0000000..7dbcbce --- /dev/null +++ b/cKit/Foundation/c_Fmt.c @@ -0,0 +1,393 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define T c_Fmt_t + +struct buf { + char *buf; + char *bp; + int size; +}; + +#define pad(n,c) do { int nn = (n); \ + while (nn-- > 0) \ + put((c), cl); } while (0) + +static void cvt_s(int code, va_list_box *box, + int put(int c, void *cl), void *cl, + unsigned char flags[], int width, int precision) { + char *str = va_arg(box->ap, char *); + assert(str); + c_Fmt_puts(str, (int)strlen(str), put, cl, flags, + width, precision); +} + +static void cvt_d(int code, va_list_box *box, + int put(int c, void *cl), void *cl, + unsigned char flags[], int width, int precision) { + int val = va_arg(box->ap, int); + unsigned m; + char buf[43]; + char *p = buf + sizeof buf; + if (val == INT_MIN) + m = INT_MAX + 1U; + else if (val < 0) + m = -val; + else + m = val; + do + *--p = m%10 + '0'; + while ((m /= 10) > 0); + if (val < 0) + *--p = '-'; + c_Fmt_putd(p, (buf + sizeof buf) - p, put, cl, flags, + width, precision); +} + +static void cvt_u(int code, va_list_box *box, + int put(int c, void *cl), void *cl, + unsigned char flags[], int width, int precision) { + unsigned m = va_arg(box->ap, unsigned); + char buf[43]; + char *p = buf + sizeof buf; + do + *--p = m%10 + '0'; + while ((m /= 10) > 0); + c_Fmt_putd(p, (buf + sizeof buf) - p, put, cl, flags, + width, precision); +} + +static void cvt_o(int code, va_list_box *box, + int put(int c, void *cl), void *cl, + unsigned char flags[], int width, int precision) { + unsigned m = va_arg(box->ap, unsigned); + char buf[43]; + char *p = buf + sizeof buf; + do + *--p = (m&0x7) + '0'; + while ((m>>= 3) != 0); + c_Fmt_putd(p, (buf + sizeof buf) - p, put, cl, flags, + width, precision); +} + +static void cvt_x(int code, va_list_box *box, + int put(int c, void *cl), void *cl, + unsigned char flags[], int width, int precision) { + unsigned m = va_arg(box->ap, unsigned); + char buf[43]; + char *p = buf + sizeof buf; + do + *--p = "0123456789abcdef"[m&0xf]; + while ((m>>= 4) != 0); + c_Fmt_putd(p, (buf + sizeof buf) - p, put, cl, flags, + width, precision); +} + +static void cvt_p(int code, va_list_box *box, + int put(int c, void *cl), void *cl, + unsigned char flags[], int width, int precision) { + c_uintptr_t m = (c_uintptr_t)va_arg(box->ap, void*); + char buf[43]; + char *p = buf + sizeof buf; + precision = INT_MIN; + do { + *--p = "0123456789abcdef"[m&0xf]; + }while ((m>>= 4) != 0); + c_Fmt_putd(p, (buf + sizeof buf) - p, put, cl, flags, + width, precision); +} + +static void cvt_c(int code, va_list_box *box, + int put(int c, void *cl), void *cl, + unsigned char flags[], int width, int precision) { + if (width == INT_MIN) + width = 0; + if (width < 0) { + flags['-'] = 1; + width = -width; + } + if (!flags['-']) + pad(width - 1, ' '); + put((unsigned char)va_arg(box->ap, int), cl); + if ( flags['-']) + pad(width - 1, ' '); +} + +static void cvt_f(int code, va_list_box *box, + int put(int c, void *cl), void *cl, + unsigned char flags[], int width, int precision) { + char buf[DBL_MAX_10_EXP+1+1+99+1]; + if (precision < 0) + precision = 6; + if (code == 'g' && precision == 0) + precision = 1; + { + static char fmt[] = "%.dd?"; + assert(precision <= 99); + fmt[4] = code; + fmt[3] = precision%10 + '0'; + fmt[2] = (precision/10)%10 + '0'; + sprintf(buf, fmt, va_arg(box->ap, double)); + } + c_Fmt_putd(buf, strlen(buf), put, cl, flags, + width, precision); +} + +static T cvt[256] = { + /* 0- 7 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 8- 15 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 16- 23 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 24- 31 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 32- 39 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 40- 47 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 48- 55 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 56- 63 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 64- 71 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 72- 79 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 80- 87 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 88- 95 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 96-103 */ 0, 0, 0, cvt_c, cvt_d, cvt_f, cvt_f, cvt_f, + /* 104-111 */ 0, 0, 0, 0, 0, 0, 0, cvt_o, + /* 112-119 */ cvt_p, 0, 0, cvt_s, 0, cvt_u, 0, 0, + /* 120-127 */ cvt_x, 0, 0, 0, 0, 0, 0, 0 +}; + +static char *c_Fmt_flags = "-+ 0"; + +static int outc(int c, void *cl) { + FILE *f = cl; + return putc(c, f); +} + +static int insert(int c, void *cl) { + struct buf *p = cl; + assert (p->bp < (p->buf + p->size)); + *p->bp++ = c; + return c; +} + +static int append(int c, void *cl) { + struct buf *p = cl; + if (p->bp >= p->buf + p->size) { + C_RESIZE(p->buf, 2*p->size); + p->bp = p->buf + p->size; + p->size *= 2; + } + *p->bp++ = c; + return c; +} + +void c_Fmt_puts(const char *str, int len, + int put(int c, void *cl), void *cl, + unsigned char flags[], int width, int precision) { + assert(str); + assert(len >= 0); + assert(flags); + if (width == INT_MIN) + width = 0; + if (width < 0) { + flags['-'] = 1; + width = -width; + } + if (precision >= 0) + flags['0'] = 0; + if (precision >= 0 && precision < len) + len = precision; + if (!flags['-']) + pad(width - len, ' '); + { + int i; + for (i = 0; i < len; i++) + put((unsigned char)*str++, cl); + } + if ( flags['-']) + pad(width - len, ' '); +} + +void c_Fmt_fmt(int put(int c, void *), void *cl, + const char *fmt, ...) { + va_list_box box; + va_start(box.ap, fmt); + c_Fmt_vfmt(put, cl, fmt, &box); + va_end(box.ap); +} + +void c_Fmt_print(const char *fmt, ...) { + va_list_box box; + va_start(box.ap, fmt); + c_Fmt_vfmt(outc, stdout, fmt, &box); + va_end(box.ap); +} +void c_Fmt_fprint(FILE *stream, const char *fmt, ...) { + va_list_box box; + va_start(box.ap, fmt); + c_Fmt_vfmt(outc, stream, fmt, &box); + va_end(box.ap); +} + +int c_Fmt_sfmt(char *buf, int size, const char *fmt, ...) { + int len; + va_list_box box; + va_start(box.ap, fmt); + len = c_Fmt_vsfmt(buf, size, fmt, &box); + va_end(box.ap); + return len; +} +int c_Fmt_vsfmt(char *buf, int size, const char *fmt, + va_list_box *box) { + struct buf cl; + assert(buf); + assert(size > 0); + assert(fmt); + cl.buf = cl.bp = buf; + cl.size = size; + c_Fmt_vfmt(insert, &cl, fmt, box); + insert(0, &cl); + return cl.bp - cl.buf - 1; +} + +char *c_Fmt_string(const char *fmt, ...) { + char *str; + va_list_box box; + assert(fmt); + va_start(box.ap, fmt); + str =c_Fmt_vstring(fmt, &box); + va_end(box.ap); + return str; +} +char *c_Fmt_vstring(const char *fmt, va_list_box *box) { + struct buf cl; + assert(fmt); + cl.size = 256; + cl.buf = cl.bp = C_ALLOC(cl.size); + c_Fmt_vfmt(append, &cl, fmt, box); + append(0, &cl); + return C_RESIZE(cl.buf, cl.bp - cl.buf); +} + +void c_Fmt_vfmt(int put(int c, void *cl), void *cl, + const char *fmt, va_list_box *box) { + assert(put); + assert(fmt); + while (*fmt) + if (*fmt != '%' || *++fmt == '%') + put((unsigned char)*fmt++, cl); + else + { + unsigned char c, flags[256]; + int width = INT_MIN, precision = INT_MIN; + memset(flags, '\0', sizeof flags); + if (c_Fmt_flags) { + unsigned char c = *fmt; + for ( ; c && strchr(c_Fmt_flags, c); c = *++fmt) { + assert(flags[c] < 255); + flags[c]++; + } + } + if (*fmt == '*' || isdigit(*fmt)) { + int n; + if (*fmt == '*') { + n = va_arg(box->ap, int); + assert(n != INT_MIN); + fmt++; + } else + for (n = 0; isdigit(*fmt); fmt++) { + int d = *fmt - '0'; + assert(n <= (INT_MAX - d)/10); + n = 10*n + d; + } + width = n; + } + if (*fmt == '.' && (*++fmt == '*' || isdigit(*fmt))) { + int n; + if (*fmt == '*') { + n = va_arg(box->ap, int); + assert(n != INT_MIN); + fmt++; + } else + for (n = 0; isdigit(*fmt); fmt++) { + int d = *fmt - '0'; + assert(n <= (INT_MAX - d)/10); + n = 10*n + d; + } + precision = n; + } + c = *fmt++; + assert(cvt[c]); + (*cvt[c])(c, box, put, cl, flags, width, precision); + } +} + +T c_Fmt_register(int code, T newcvt) { + T old; + assert(0 < code + && code < (int)(sizeof (cvt)/sizeof (cvt[0]))); + old = cvt[code]; + cvt[code] = newcvt; + return old; +} + +void c_Fmt_putd(const char *str, int len, + int put(int c, void *cl), void *cl, + unsigned char flags[], int width, int precision) { + int sign; + assert(str); + assert(len >= 0); + assert(flags); + if (width == INT_MIN) + width = 0; + if (width < 0) { + flags['-'] = 1; + width = -width; + } + if (precision >= 0) + flags['0'] = 0; + if (len > 0 && (*str == '-' || *str == '+')) { + sign = *str++; + len--; + } else if (flags['+']) + sign = '+'; + else if (flags[' ']) + sign = ' '; + else + sign = 0; + { int n; + if (precision < 0) + precision = 1; + if (len < precision) + n = precision; + else if (precision == 0 && len == 1 && str[0] == '0') + n = 0; + else + n = len; + if (sign) + n++; + if (flags['-']) { + if (sign) + put(sign, cl); + } else if (flags['0']) { + if (sign) + put(sign, cl); + pad(width - n, '0'); + } else { + pad(width - n, ' '); + if (sign) + put(sign, cl); + } + pad(precision - len, '0'); + { + int i; + for (i = 0; i < len; i++) + put((unsigned char)*str++, cl); + } + if (flags['-']) + pad(width - n, ' '); } +} diff --git a/cKit/Foundation/c_Fmt.h b/cKit/Foundation/c_Fmt.h new file mode 100644 index 0000000..ae931f8 --- /dev/null +++ b/cKit/Foundation/c_Fmt.h @@ -0,0 +1,70 @@ +#ifndef INCLUDED_C_FMT_H +#define INCLUDED_C_FMT_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +#ifndef INCLUDED_STDIO_H +#define INCLUDED_STDIO_H +#include +#endif /*INCLUDED_STDIO_H*/ + +#ifndef INCLUDED_STDARG_H +#define INCLUDED_STDARG_H +#include +#endif /*INCLUDED_STDARG_H*/ + + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct va_list_box { + va_list ap; +} va_list_box; + +#ifdef T +#undef T +#endif + +#define T c_Fmt_t + +typedef void (*T)(int code, va_list_box *box, + int put(int c, void *cl), void *cl, + unsigned char flags[256], int width, int precision); + +extern void c_Fmt_fmt (int put(int c, void *cl), void *cl, + const char *fmt, ...); + +extern void c_Fmt_vfmt(int put(int c, void *cl), void *cl, + const char *fmt, va_list_box *box); + +extern void c_Fmt_print (const char *fmt, ...); + +extern void c_Fmt_fprint(FILE *stream, + const char *fmt, ...); + +extern int c_Fmt_sfmt (char *buf, int size, + const char *fmt, ...); + +extern int c_Fmt_vsfmt(char *buf, int size, + const char *fmt, va_list_box *box); + +extern char *c_Fmt_string (const char *fmt, ...); + +extern char *c_Fmt_vstring(const char *fmt, va_list_box *box); + +extern T c_Fmt_register(int code, T cvt); + +extern void c_Fmt_putd(const char *str, int len, + int put(int c, void *cl), void *cl, + unsigned char flags[256], int width, int precision); + +extern void c_Fmt_puts(const char *str, int len, + int put(int c, void *cl), void *cl, + unsigned char flags[256], int width, int precision); + +#undef T + +#endif /*INCLUDED_C_FMT_H*/ diff --git a/cKit/Foundation/c_IndexPQ.c b/cKit/Foundation/c_IndexPQ.c new file mode 100644 index 0000000..e791bab --- /dev/null +++ b/cKit/Foundation/c_IndexPQ.c @@ -0,0 +1,201 @@ +#include +#include + +#define INVALID_INDEX ((c_size_t)-1) + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + + + +// Internal Helper: Swaps two locations inside the heap and updates their inverse map positions +C_STATIC_FORCE_INLINE +void swap_nodes(c_IndexPQ_t* self, c_size_t i, c_size_t j) { + c_size_t temp = self->heap[i]; + self->heap[i] = self->heap[j]; + self->heap[j] = temp; + + // Synchronize inverse lookups mapping: ID -> heap position index + self->inverse_heap[self->heap[i]] = i; + self->inverse_heap[self->heap[j]] = j; +} + +// Internal Helper: Balanced restoration path upwards (O(log N)) +C_STATIC_FORCE_INLINE +void heapify_up(c_IndexPQ_t* self, c_size_t index) { + while (index > 0) { + c_size_t parent = (index - 1) / 2; + void* curr_val = (char*)self->keys_data + (self->heap[index] * self->obj_size); + void* parent_val = (char*)self->keys_data + (self->heap[parent] * self->obj_size); + + if (self->compare(curr_val, parent_val) < 0) { + swap_nodes(self, index, parent); + index = parent; + } else { + break; + } + } +} + +// Internal Helper: Balanced restoration path downwards (O(log N)) +C_STATIC_FORCE_INLINE +void heapify_down(c_IndexPQ_t* self, c_size_t index) { + c_size_t left, right, smallest; + while (1) { + left = 2 * index + 1; + right = 2 * index + 2; + smallest = index; + + void* smallest_val = (char*)self->keys_data + (self->heap[smallest] * self->obj_size); + + if (left < self->size) { + void* left_val = (char*)self->keys_data + (self->heap[left] * self->obj_size); + if (self->compare(left_val, smallest_val) < 0) { + smallest = left; + smallest_val = left_val; + } + } + + if (right < self->size) { + void* right_val = (char*)self->keys_data + (self->heap[right] * self->obj_size); + if (self->compare(right_val, smallest_val) < 0) { + smallest = right; + } + } + + if (smallest != index) { + swap_nodes(self, index, smallest); + index = smallest; + } else { + break; + } + } +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_IndexPQ_Init(c_IndexPQ_t* self, int obj_size, c_size_t max_size, c_IndexPQ_Compare_f compare) { + if (!self || obj_size <= 0 || max_size == 0 || !compare) return C_ERR_PARAM; + + self->obj_size = obj_size; + self->max_size = max_size; + self->size = 0; + self->compare = compare; + + self->keys_data = C_ALLOC(self->max_size * self->obj_size); + self->heap = (c_size_t*)C_ALLOC(self->max_size * sizeof(c_size_t)); + self->inverse_heap = (c_size_t*)C_ALLOC(self->max_size * sizeof(c_size_t)); + + if (!self->keys_data || !self->heap || !self->inverse_heap) { + c_IndexPQ_Destroy(self); + return C_ERR_NOMEM; + } + + // Initialize inverse heap mappings to an invalid sentinel value representing emptiness + for (c_size_t i = 0; i < self->max_size; i++) { + self->inverse_heap[i] = INVALID_INDEX; + } + + return C_ERR_SUCCESS; +} + +void c_IndexPQ_Destroy(c_IndexPQ_t* self) { + if (!self) return; + if (self->keys_data) C_FREE(self->keys_data); + if (self->heap) C_FREE(self->heap); + if (self->inverse_heap) C_FREE(self->inverse_heap); + + self->keys_data = NULL; + self->heap = NULL; + self->inverse_heap = NULL; + self->max_size = 0; + self->size = 0; + self->obj_size = 0; +} + +c_bool_t c_IndexPQ_Contains(const c_IndexPQ_t* self, c_size_t id) { + if (!self || id >= self->max_size) return C_FALSE; + return self->inverse_heap[id] != INVALID_INDEX; +} + +// Inserts an entry associated with a fixed identifier ID into the heap (O(log N)) +c_err_t c_IndexPQ_Push(c_IndexPQ_t* self, c_size_t id, const void* obj) { + if (!self || !obj) return C_ERR_PARAM; + if (id >= self->max_size) return C_ERR_FULL; + if (c_IndexPQ_Contains(self, id)) return C_ERR_ALREADY_EXISTS; + + // Map user payload value into the slot indexed directly by ID + char* slot = (char*)self->keys_data + (id * self->obj_size); + memcpy(slot, obj, self->obj_size); + + // Place the ID at the end of the active binary heap + self->heap[self->size] = id; + self->inverse_heap[id] = self->size; + self->size++; + + heapify_up(self, self->size - 1); + return C_ERR_SUCCESS; +} + +// Pops the minimum elements out while tracking its associated identifier value (O(log N)) +c_err_t c_IndexPQ_Pop(c_IndexPQ_t* self, c_size_t* out_id, void* out_obj) { + if (!self || !out_id || !out_obj) return C_ERR_PARAM; + if (self->size == 0) return C_ERR_EMPTY; + + c_size_t min_id = self->heap[0]; + *out_id = min_id; + + // Extract values into safe user-allocated buffers + void* slot = (char*)self->keys_data + (min_id * self->obj_size); + memcpy(out_obj, slot, self->obj_size); + + // Swap top node with the last active entry node element + swap_nodes(self, 0, self->size - 1); + self->inverse_heap[min_id] = INVALID_INDEX; // Clear historical identifier tracking values + self->size--; + + if (self->size > 0) { + heapify_down(self, 0); + } + + return C_ERR_SUCCESS; +} + +// Modifies the priority value for an arbitrary key ID anywhere inside the queue in O(log N) time +c_err_t c_IndexPQ_Change(c_IndexPQ_t* self, c_size_t id, const void* new_obj) { + if (!self || !new_obj) return C_ERR_PARAM; + if (!c_IndexPQ_Contains(self, id)) return C_ERR_NOT_FOUND; + + // Overwrite the existing data value + char* slot = (char*)self->keys_data + (id * self->obj_size); + memcpy(slot, new_obj, self->obj_size); + + // Retrieve its current heap position to fix the balance path triggers + c_size_t heap_pos = self->inverse_heap[id]; + heapify_up(self, heap_pos); + heapify_down(self, heap_pos); + + return C_ERR_SUCCESS; +} + +void* c_IndexPQ_PeekValue(c_IndexPQ_t* self) { + if (!self || self->size == 0) return NULL; + return (char*)self->keys_data + (self->heap[0] * self->obj_size); +} + +long long c_IndexPQ_PeekID(c_IndexPQ_t* self) { + if (!self || self->size == 0) return -1; + return (long long)self->heap[0]; +} + +c_size_t c_IndexPQ_GetSize(const c_IndexPQ_t* self) { + if (!self) return 0; + return self->size; +} + +c_bool_t c_IndexPQ_IsEmpty(const c_IndexPQ_t* self) { + if (!self) return C_TRUE; + return self->size == 0; +} \ No newline at end of file diff --git a/cKit/Foundation/c_IndexPQ.h b/cKit/Foundation/c_IndexPQ.h new file mode 100644 index 0000000..1468dfd --- /dev/null +++ b/cKit/Foundation/c_IndexPQ.h @@ -0,0 +1,38 @@ +#ifndef INCLUDED_C_INDEXPQ_H +#define INCLUDED_C_INDEXPQ_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// User priority comparison callback: returns <0 if a < b, 0 if a == b, >0 if a > b +typedef int (*c_IndexPQ_Compare_f)(const void* a, const void* b); + +typedef struct { + void* keys_data; // Array storing user element data values indexed by ID + c_size_t* heap; // Binary heap array containing element IDs + c_size_t* inverse_heap; // Maps element ID -> its current position index within the heap array + int obj_size; // Memory size of each user element structure + c_size_t max_size; // Maximum number of items the Indexed PQ can handle + c_size_t size; // Current number of active elements inside the queue + c_IndexPQ_Compare_f compare; // User-defined priority comparison callback +} c_IndexPQ_t; + +c_err_t c_IndexPQ_Init(c_IndexPQ_t* self, int obj_size, c_size_t max_size, c_IndexPQ_Compare_f compare); +void c_IndexPQ_Destroy(c_IndexPQ_t* self); + +c_err_t c_IndexPQ_Push(c_IndexPQ_t* self, c_size_t id, const void* obj); +c_err_t c_IndexPQ_Pop(c_IndexPQ_t* self, c_size_t* out_id, void* out_obj); +c_err_t c_IndexPQ_Change(c_IndexPQ_t* self, c_size_t id, const void* new_obj); +c_bool_t c_IndexPQ_Contains(const c_IndexPQ_t* self, c_size_t id); + +void* c_IndexPQ_PeekValue(c_IndexPQ_t* self); +long long c_IndexPQ_PeekID(c_IndexPQ_t* self); +c_size_t c_IndexPQ_GetSize(const c_IndexPQ_t* self); +c_bool_t c_IndexPQ_IsEmpty(const c_IndexPQ_t* self); + +#endif /*INCLUDED_C_INDEXPQ_H*/ diff --git a/cKit/Foundation/c_IndexPQ.t.c b/cKit/Foundation/c_IndexPQ.t.c new file mode 100644 index 0000000..16a7aad --- /dev/null +++ b/cKit/Foundation/c_IndexPQ.t.c @@ -0,0 +1,97 @@ +#include "c_IndexPQ.h" +#include +#include + + +typedef struct { + double distance; // Path distance used as priority metric (lower is more optimal) +} PathCost_t; + +int compare_costs(const void* a, const void* b) { + double distA = ((PathCost_t*)a)->distance; + double distB = ((PathCost_t*)b)->distance; + if (distA < distB) return -1; + if (distA > distB) return 1; + return 0; +} + +void test_log(const char* name) { + printf("[PASS] %s\n", name); +} + +int main() { + printf("==================================================\n"); + printf(" Starting c_IndexPQ_t Min-Indexed Heap Unit Tests\n"); + printf("==================================================\n\n"); + + c_IndexPQ_t ipq; + // Track vertex node IDs from 0 to 4 (Max Size = 5) + c_err_t err = c_IndexPQ_Init(&ipq, sizeof(PathCost_t), 5, compare_costs); + assert(err == C_ERR_SUCCESS); + assert(c_IndexPQ_IsEmpty(&ipq) == C_TRUE); + test_log("1. Indexed priority queue structure initialization complete"); + + PathCost_t node0 = {50.5}; + PathCost_t node1 = {20.1}; + PathCost_t node2 = {100.0}; + + // ========================================== + // 2. Testing Direct Indexed Push Checks + // ========================================== + c_IndexPQ_Push(&ipq, 0, &node0); // ID 0 -> cost 50.5 + c_IndexPQ_Push(&ipq, 1, &node1); // ID 1 -> cost 20.1 (Current Min) + c_IndexPQ_Push(&ipq, 2, &node2); // ID 2 -> cost 100.0 + + assert(c_IndexPQ_GetSize(&ipq) == 3); + assert(c_IndexPQ_PeekID(&ipq) == 1); // Node ID 1 must sit on top + + // Duplicate index usage bounds intercept verification + assert(c_IndexPQ_Push(&ipq, 1, &node0) == C_ERR_ALREADY_EXISTS); + test_log("2. Key-ID bound checking and initial element mappings pass"); + + // ========================================== + // 3. Testing Priority Mutation Updates (Change API) + // ========================================== + // Simulating a relaxation pass: found a shorter path to Node 2 (from 100.0 down to 5.2!) + PathCost_t node2_optimized = {5.2}; + err = c_IndexPQ_Change(&ipq, 2, &node2_optimized); + assert(err == C_ERR_SUCCESS); + + // Node 2 must have bubble-up repaired straight to the top of the queue in O(log N) + assert(c_IndexPQ_PeekID(&ipq) == 2); + assert(((PathCost_t*)c_IndexPQ_PeekValue(&ipq))->distance == 5.2); + test_log("3. Dynamic property value alteration and balance updates pass"); + + // ========================================== + // 4. Testing Pop Execution and Integrity Mapping + // ========================================== + c_size_t popped_id; + PathCost_t popped_data; + + // Pop 1: Must yield Node 2 (Cost 5.2) + err = c_IndexPQ_Pop(&ipq, &popped_id, &popped_data); + assert(err == C_ERR_SUCCESS); + assert(popped_id == 2); + assert(popped_data.distance == 5.2); + + // Pop 2: Must yield Node 1 (Cost 20.1) + c_IndexPQ_Pop(&ipq, &popped_id, &popped_data); + assert(popped_id == 1); + + // Pop 3: Must yield Node 0 (Cost 50.5) + c_IndexPQ_Pop(&ipq, &popped_id, &popped_data); + assert(popped_id == 0); + assert(c_IndexPQ_IsEmpty(&ipq) == C_TRUE); + + // Empty boundaries fault detection assert rules + assert(c_IndexPQ_Pop(&ipq, &popped_id, &popped_data) == C_ERR_EMPTY); + test_log("4. FIFO priority ordering sequential readout pass"); + + c_IndexPQ_Destroy(&ipq); + test_log("5. Lifecycle memory destruction pass"); + + printf("\n==================================================\n"); + printf(" Success! Indexed Priority Queue operational parameters matched!\n"); + printf("==================================================\n"); + return 0; +} \ No newline at end of file diff --git a/cKit/Foundation/c_KnuthShuffle.c b/cKit/Foundation/c_KnuthShuffle.c new file mode 100644 index 0000000..5444f75 --- /dev/null +++ b/cKit/Foundation/c_KnuthShuffle.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Foundation/c_KnuthShuffle.h b/cKit/Foundation/c_KnuthShuffle.h new file mode 100644 index 0000000..edfd84a --- /dev/null +++ b/cKit/Foundation/c_KnuthShuffle.h @@ -0,0 +1,51 @@ +#ifndef INCLUDED_C_KNUTHSHUFFLE_H +#define INCLUDED_C_KNUTHSHUFFLE_H + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * 通用 Knuth 洗牌演算法 + * @param base 指向待打亂陣列首元素的指標 + * @param num 陣列中元素的個數 + * @param size 每個元素的大小(位元組數) + */ +C_STATIC_FORCE_INLINE +void c_KnuthShuffle(void* base, c_size_t num, c_size_t size) { + if (base == NULL || num < 2 || size == 0) return; + + char* arr = (char*)base; + + // 使用棧快取 buffer 進行記憶體交換,避免堆分配開銷 +#define SHUFFLE_STACK_LIMIT 128 + char stack_buf[SHUFFLE_STACK_LIMIT]; + void* temp = (size <= SHUFFLE_STACK_LIMIT) ? stack_buf : C_ALLOC(size); + if (temp == NULL) return; + + // 從後往前遍歷陣列 + for (c_int_t i = num - 1; i > 0; i--) { + // 生成一個 0 到 i 之間(包含 i)的隨機索引 + c_int_t j = rand() % (i + 1); + + // 交換 arr[i] 和 arr[j] + if (i != j) { + char* a = arr + (i * size); + char* b = arr + (j * size); + memcpy(temp, a, size); + memcpy(a, b, size); + memcpy(b, temp, size); + } + } + + if (size > SHUFFLE_STACK_LIMIT) { + C_FREE(temp); + } +#undef SHUFFLE_STACK_LIMIT +} + +#endif /*INCLUDED_C_KNUTHSHUFFLE_H*/ diff --git a/cKit/Foundation/c_KnuthShuffle.t.c b/cKit/Foundation/c_KnuthShuffle.t.c new file mode 100644 index 0000000..a93af13 --- /dev/null +++ b/cKit/Foundation/c_KnuthShuffle.t.c @@ -0,0 +1,101 @@ +#include "c_KnuthShuffle.h" +#include +#include + +// Advanced Line-Tracing Diagnostic Macro +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \ + return C_FALSE; \ + } \ + } while(0) + +// Complex struct data payload representing simulation cards +typedef struct { + int card_id; + char suit; +} DeckCard; + +// External declaration of your framework's Knuth Shuffle module +extern void c_KnuthShuffle(void* base, c_size_t num, c_size_t size); + +/** + * Unit Test Profile: Validates extreme boundaries, statistical disordering, + * and strict payload preservation without data loss. + */ +c_bool_t test_knuth_shuffle_lifecycle(void) { + // Seed the standard pseudo-random number generator for the shuffle module + srand((unsigned int)time(NULL)); + + // Checkpoint 1: Boundary conditions check (Should return safely without crashing) + int* null_ptr = NULL; + c_KnuthShuffle(null_ptr, 0, sizeof(int)); // NULL pointer escape guard + + int single_array[] = { 99 }; + c_KnuthShuffle(single_array, 1, sizeof(int)); + EXPECT_EQ(single_array[0], 99, "Single element array mutated unexpectedly"); + + // Initialize an ordered deck array of 10 complex structural cards + DeckCard deck[10]; + c_size_t total_cards = sizeof(deck) / sizeof(deck[0]); + char suits[] = {'H', 'D', 'C', 'S'}; + + for (c_size_t i = 0; i < total_cards; i++) { + deck[i].card_id = (int)(i + 1); + deck[i].suit = suits[i % 4]; + } + + // Clone the original array configuration to verify data preservation metrics later + DeckCard deck_snapshot[10]; + memcpy(deck_snapshot, deck, sizeof(deck)); + + printf(" [LOG] Executing c_KnuthShuffle across 10 complex data elements...\n"); + c_KnuthShuffle(deck, total_cards, sizeof(DeckCard)); + + // Checkpoint 2: Verification of statistical mismatch (Disordering check) + c_size_t identical_matches = 0; + for (c_size_t i = 0; i < total_cards; i++) { + if (deck[i].card_id == deck_snapshot[i].card_id) { + identical_matches++; + } + } + + // Mathematically, the probability of a 10-element array remaining completely + // identical or barely modified after a fair shuffle is near zero (~1 in 3.6 million). + printf(" [STAT] Element slots remaining in original positions: %zu/%zu\n", identical_matches, total_cards); + EXPECT_EQ(identical_matches < total_cards, C_TRUE, "Shuffle algorithm failed to re-arrange element configurations"); + + // Checkpoint 3: Strict Data Integrity Verification (De-duplication & Conservation check) + // Ensure that every single original item still exists inside the shuffled deck exactly once. + c_bool_t found_flags[10] = { C_FALSE }; + c_size_t unique_restored_count = 0; + + for (c_size_t i = 0; i < total_cards; i++) { + for (c_size_t j = 0; j < total_cards; j++) { + if (deck[i].card_id == deck_snapshot[j].card_id && deck[i].suit == deck_snapshot[j].suit) { + if (!found_flags[j]) { + found_flags[j] = C_TRUE; + unique_restored_count++; + } + break; + } + } + } + + EXPECT_EQ(unique_restored_count, total_cards, "Shuffle step introduced data corruption, duplicates, or element losses"); + printf(" [PASS] All 10 original complex items conserved perfectly with zero data leakage.\n"); + + return C_TRUE; +} + +int main(void) { + printf("=== Starting Framework Verification: c_KnuthShuffle ===\n"); + + if (test_knuth_shuffle_lifecycle()) { + printf(" [PASS] Knuth Linear-Time Shuffle Validation & Payload Conservation Verified.\n"); + } else { + printf(" [FAIL] Shuffle Optimization Pipeline Errors Intercepted.\n"); + } + return 0; +} \ No newline at end of file diff --git a/cKit/Foundation/c_LinkDQueue.c b/cKit/Foundation/c_LinkDQueue.c new file mode 100644 index 0000000..465dd3a --- /dev/null +++ b/cKit/Foundation/c_LinkDQueue.c @@ -0,0 +1,152 @@ +#include +#include +#include + +C_STATIC_FORCE_INLINE +c_LinkDQueueNode_t* create_node(int obj_size, void* obj) { + int size = sizeof(c_LinkDQueueNode_t) + obj_size; + size = C_ALIGN_UPB(size, C_ALIGN_SIZE); + + c_LinkDQueueNode_t* new_node = (c_LinkDQueueNode_t*)C_ALLOC(size); + if (!new_node) return NULL; + new_node->data = new_node + 1; + + memcpy(new_node->data, obj, obj_size); + new_node->prev = NULL; + new_node->next = NULL; + return new_node; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_LinkDQueue_Init(c_LinkDQueue_t* self, int obj_size) { + if (!self || obj_size <= 0) return C_ERR_PARAM; + self->head = NULL; + self->tail = NULL; + self->obj_size = obj_size; + self->size = 0; + return C_ERR_SUCCESS; +} + +void c_LinkDQueue_Destroy(c_LinkDQueue_t* self) { + if (!self) return; + c_LinkDQueueNode_t* current = self->head; + while (current != NULL) { + c_LinkDQueueNode_t* next = current->next; + C_FREE(current); + current = next; + } + self->head = NULL; + self->tail = NULL; + self->size = 0; +} + +// 前端推入 O(1) +c_err_t c_LinkDQueue_PushHead(c_LinkDQueue_t* self, void* obj) { + if (!self || !obj) return C_ERR_PARAM; + c_LinkDQueueNode_t* new_node = create_node(self->obj_size, obj); + if (!new_node) return C_ERR_NOMEM; + + if (self->size == 0) { + self->head = new_node; + self->tail = new_node; + } else { + new_node->next = self->head; + self->head->prev = new_node; + self->head = new_node; + } + self->size++; + return C_ERR_SUCCESS; +} + +// 尾端推入 O(1) +c_err_t c_LinkDQueue_PushTail(c_LinkDQueue_t* self, void* obj) { + if (!self || !obj) return C_ERR_PARAM; + c_LinkDQueueNode_t* new_node = create_node(self->obj_size, obj); + if (!new_node) return C_ERR_NOMEM; + + if (self->size == 0) { + self->head = new_node; + self->tail = new_node; + } else { + new_node->prev = self->tail; + self->tail->next = new_node; + self->tail = new_node; + } + self->size++; + return C_ERR_SUCCESS; +} + +// 前端彈出 O(1) +c_err_t c_LinkDQueue_PopHead(c_LinkDQueue_t* self, void* obj) { + if (!self || !obj) return C_ERR_PARAM; + if (self->size == 0) return C_ERR_EMPTY; + + c_LinkDQueueNode_t* to_delete = self->head; + memcpy(obj, to_delete->data, self->obj_size); + + self->head = to_delete->next; + if (self->head == NULL) { + self->tail = NULL; // 變空佇列 + } else { + self->head->prev = NULL; + } + + C_FREE(to_delete); + self->size--; + return C_ERR_SUCCESS; +} + +// 尾端彈出 O(1) +c_err_t c_LinkDQueue_PopTail(c_LinkDQueue_t* self, void* obj) { + if (!self || !obj) return C_ERR_PARAM; + if (self->size == 0) return C_ERR_EMPTY; + + c_LinkDQueueNode_t* to_delete = self->tail; + memcpy(obj, to_delete->data, self->obj_size); + + self->tail = to_delete->prev; + if (self->tail == NULL) { + self->head = NULL; // 變空佇列 + } else { + self->tail->next = NULL; + } + + C_FREE(to_delete); + self->size--; + return C_ERR_SUCCESS; +} + +void* c_LinkDQueue_PeekHead(c_LinkDQueue_t* self) { + if (!self || self->size == 0) return NULL; + return self->head->data; +} + +void* c_LinkDQueue_PeekTail(c_LinkDQueue_t* self) { + if (!self || self->size == 0) return NULL; + return self->tail->data; +} + +// 迭代器安全刪除:利用雙向指標在 O(1) 修正前後驅關係並安全維護 head/tail +void c_LinkDQueueIter_Remove(c_LinkDQueueIter_t* self) { + if (!self || !self->dqueue || !self->node || !*(self->node)) return; + + c_LinkDQueueNode_t* to_delete = *(self->node); + c_LinkDQueue_t* dq = self->dqueue; + + // 1. 修正前驅節點的 next 或主結構的 head + *(self->node) = to_delete->next; + + // 2. 修正後繼節點的 prev 或主結構的 tail + if (to_delete->next != NULL) { + to_delete->next->prev = to_delete->prev; + } else { + dq->tail = to_delete->prev; // 刪除的是尾端,更新 tail + } + + // 3. 釋放資源與修正大小 + C_FREE(to_delete); + dq->size--; +} + diff --git a/cKit/Foundation/c_LinkDQueue.h b/cKit/Foundation/c_LinkDQueue.h new file mode 100644 index 0000000..5c6c099 --- /dev/null +++ b/cKit/Foundation/c_LinkDQueue.h @@ -0,0 +1,77 @@ +#ifndef INCLUDED_C_LINKDQUEUE_H +#define INCLUDED_C_LINKDQUEUE_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct c_LinkDQueueNode_t { + void* data; + struct c_LinkDQueueNode_t* prev; + struct c_LinkDQueueNode_t* next; +} c_LinkDQueueNode_t; + +// 雙端佇列控制結構 +typedef struct { + c_LinkDQueueNode_t* head; + c_LinkDQueueNode_t* tail; + int obj_size; + c_size_t size; +} c_LinkDQueue_t; + +// 雙端佇列迭代器(維持指標的指標設計,預設從 head 往 tail 走訪) +typedef struct { + c_LinkDQueue_t* dqueue; + c_LinkDQueueNode_t** node; +} c_LinkDQueueIter_t; + +// 核心函數宣告 +c_err_t c_LinkDQueue_Init(c_LinkDQueue_t* self, int obj_size); +void c_LinkDQueue_Destroy(c_LinkDQueue_t* self); + +c_err_t c_LinkDQueue_PushHead(c_LinkDQueue_t* self, void* obj); +c_err_t c_LinkDQueue_PushTail(c_LinkDQueue_t* self, void* obj); + +c_err_t c_LinkDQueue_PopHead(c_LinkDQueue_t* self, void* obj); +c_err_t c_LinkDQueue_PopTail(c_LinkDQueue_t* self, void* obj); + +void* c_LinkDQueue_PeekHead(c_LinkDQueue_t* self); +void* c_LinkDQueue_PeekTail(c_LinkDQueue_t* self); + +void c_LinkDQueueIter_Remove(c_LinkDQueueIter_t* self); + +// 迭代器內聯函數 +C_STATIC_FORCE_INLINE +void c_LinkDQueueIter_Init(c_LinkDQueueIter_t* self, c_LinkDQueue_t* dqueue) { + if (!self || !dqueue) return; + self->dqueue = dqueue; + self->node = &dqueue->head; +} + +C_STATIC_FORCE_INLINE +c_bool_t c_LinkDQueueIter_HasNext(c_LinkDQueueIter_t* self) { + if (!self) return C_FALSE; + return (self->node != NULL) && (*(self->node) != NULL); +} + +C_STATIC_FORCE_INLINE +void* c_LinkDQueueIter_Next(c_LinkDQueueIter_t* self) { + if (!self || !self->node || !*(self->node)) return NULL; + void* data = (*(self->node))->data; + self->node = &(*(self->node))->next; + return data; +} + +C_STATIC_FORCE_INLINE +void* c_LinkDQueueIter_Get(c_LinkDQueueIter_t* self) { + if (!self || !self->node || !*(self->node)) return NULL; + return (*(self->node))->data; +} + +void c_LinkDQueueIter_Remove(c_LinkDQueueIter_t* self); + +#endif /*INCLUDED_C_LINKDQUEUE_H*/ diff --git a/cKit/Foundation/c_LinkDQueue.t.c b/cKit/Foundation/c_LinkDQueue.t.c new file mode 100644 index 0000000..9890a4e --- /dev/null +++ b/cKit/Foundation/c_LinkDQueue.t.c @@ -0,0 +1,110 @@ +#include "c_LinkDQueue.h" +#include +#include + + +typedef struct { + char label[16]; + int val; +} Element_t; + +void test_log(const char* test_name) { + printf("[PASS] %s\n", test_name); +} + +int main() { + printf("==================================================\n"); + printf(" 開始執行 c_LinkDQueue 雙端佇列完整測試用例\n"); + printf("==================================================\n\n"); + + c_LinkDQueue_t dq; + c_LinkDQueue_Init(&dq, sizeof(Element_t)); + + Element_t e1 = {"Node_1", 10}; + Element_t e2 = {"Node_2", 20}; + Element_t e3 = {"Node_3", 30}; + + // ========================================== + // 1. 測試雙端推入 (PushHead & PushTail) + // ========================================== + // 先把 e2 推入尾端 (佇列: [e2]) + c_LinkDQueue_PushTail(&dq, &e2); + // 把 e1 推入前端 (佇列: [e1, e2]) + c_LinkDQueue_PushHead(&dq, &e1); + // 把 e3 推入尾端 (佇列: [e1, e2, e3]) + c_LinkDQueue_PushTail(&dq, &e3); + + assert(dq.size == 3); + assert(((Element_t*)c_LinkDQueue_PeekHead(&dq))->val == 10); + assert(((Element_t*)c_LinkDQueue_PeekTail(&dq))->val == 30); + test_log("1. 雙端交錯推入與邊界 Peek 驗證成功"); + + // ========================================== + // 2. 測試迭代器走訪 + // ========================================== + c_LinkDQueueIter_t iter; + c_LinkDQueueIter_Init(&iter, &dq); + int idx = 0; + while (c_LinkDQueueIter_HasNext(&iter)) { + Element_t* el = (Element_t*)c_LinkDQueueIter_Next(&iter); + if (idx == 0) assert(el->val == 10); + if (idx == 1) assert(el->val == 20); + if (idx == 2) assert(el->val == 30); + idx++; + } + test_log("2. 迭代器正向走訪順序驗證成功"); + + // ========================================== + // 3. 測試迭代器刪除中間節點 (Remove e2) + // ========================================== + c_LinkDQueueIter_Init(&iter, &dq); + while (c_LinkDQueueIter_HasNext(&iter)) { + Element_t* el = (Element_t*)c_LinkDQueueIter_Get(&iter); + if (el->val == 20) { + c_LinkDQueueIter_Remove(&iter); // 刪除 Node_2 + } else { + c_LinkDQueueIter_Next(&iter); + } + } + assert(dq.size == 2); + // 驗證現在內容只剩下 [e1, e3] + assert(((Element_t*)c_LinkDQueue_PeekHead(&dq))->val == 10); + assert(((Element_t*)c_LinkDQueue_PeekTail(&dq))->val == 30); + test_log("3. 迭代器 O(1) 刪除中間節點暨雙向鏈結維護成功"); + + // ========================================== + // 4. 測試雙端彈出 (PopHead & PopTail) + // ========================================== + Element_t buf; + + // 從前端彈出(應拿到 e1) + c_err_t err = c_LinkDQueue_PopHead(&dq, &buf); + assert(err == C_ERR_SUCCESS); + assert(buf.val == 10); + assert(dq.size == 1); + + // 從尾端彈出(應拿到 e3) + err = c_LinkDQueue_PopTail(&dq, &buf); + assert(err == C_ERR_SUCCESS); + assert(buf.val == 30); + assert(dq.size == 0); + + // 驗證完全清空後指標皆重置為 NULL + assert(dq.head == NULL); + assert(dq.tail == NULL); + test_log("4. 雙端彈出與空佇列指標歸零驗證成功"); + + // ========================================== + // 5. 空佇列防呆測試 + // ========================================== + assert(c_LinkDQueue_PopHead(&dq, &buf) == C_ERR_EMPTY); + assert(c_LinkDQueue_PopTail(&dq, &buf) == C_ERR_EMPTY); + assert(c_LinkDQueue_PeekHead(&dq) == NULL); + test_log("5. 空雙端佇列越界防呆成功"); + + c_LinkDQueue_Destroy(&dq); + printf("\n==================================================\n"); + printf(" 恭喜!c_LinkDQueue 所有核心特性單元測試完美通過!\n"); + printf("==================================================\n"); + return 0; +} \ No newline at end of file diff --git a/cKit/Foundation/c_LinkList.c b/cKit/Foundation/c_LinkList.c new file mode 100644 index 0000000..847e6fd --- /dev/null +++ b/cKit/Foundation/c_LinkList.c @@ -0,0 +1,82 @@ +#include +#include +#include + + +c_err_t c_LinkList_Init(c_LinkList_t* self, c_size_t obj_size) { + if (!self || obj_size == 0) return C_ERR_PARAM; + self->head = NULL; + self->obj_size = (int)obj_size; + self->size = 0; + return C_ERR_SUCCESS; +} + +void c_LinkList_Destroy(c_LinkList_t* self) { + if (!self) return; + + c_LinkListNode_t* current = self->head; + while (current != NULL) { + c_LinkListNode_t* next = current->next; + // 由於 Add 時 data 與 node 是分開或合開,此處依據一體化配置釋放 + C_FREE(current); + current = next; + } + self->head = NULL; + self->size = 0; +} + +c_err_t c_LinkList_Add(c_LinkList_t* self, void* obj) { + if (!self || !obj) return C_ERR_PARAM; + + int size = (int)sizeof(c_LinkListNode_t) + self->obj_size; + size = C_ALIGN_UPB(size, C_ALIGN_SIZE); + c_LinkListNode_t* new_node = (c_LinkListNode_t*)C_ALLOC(size); + if (!new_node) return C_ERR_NOMEM; + new_node->data = new_node+1; + + // 值複製 (Value Copy) + memcpy(new_node->data, obj, self->obj_size); + + // 頭插法鏈結 + new_node->next = self->head; + self->head = new_node; + self->size++; + + return C_ERR_SUCCESS; +} + +c_err_t c_LinkList_Remove(c_LinkList_t* self, void* obj) { + if (!self || !obj) return C_ERR_PARAM; + + c_LinkListNode_t** curr = &self->head; + while (*curr != NULL) { + // 使用 memcmp 進行二進位值比對 + if (memcmp((*curr)->data, obj, self->obj_size) == 0) { + c_LinkListNode_t* to_delete = *curr; + *curr = to_delete->next; + + C_FREE(to_delete); + self->size--; + return C_ERR_SUCCESS; + } + curr = &(*curr)->next; + } + + return C_ERR_NOT_FOUND; +} + +void c_LinkListIter_Remove(c_LinkListIter_t* self) { + if (!self || !self->list || !self->node || !*(self->node)) return; + + c_LinkListNode_t* to_delete = *(self->node); + + // 讓上一個節點的 next 指向下一個節點,移除鏈結 + *(self->node) = to_delete->next; + + // 釋放該節點的值與結構 + C_FREE(to_delete); + + self->list->size--; +} + + diff --git a/cKit/Foundation/c_LinkList.h b/cKit/Foundation/c_LinkList.h new file mode 100644 index 0000000..e4f7cb6 --- /dev/null +++ b/cKit/Foundation/c_LinkList.h @@ -0,0 +1,71 @@ +#ifndef INCLUDED_C_LINKLIST_H +#define INCLUDED_C_LINKLIST_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct c_LinkListNode_t { + void* data; + struct c_LinkListNode_t* next; +}c_LinkListNode_t; + +typedef struct { + c_LinkListNode_t* head; + int obj_size; + c_size_t size; +}c_LinkList_t; + +typedef struct { + c_LinkList_t* list; + c_LinkListNode_t** node; +}c_LinkListIter_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_LinkList_Init(c_LinkList_t* self, c_size_t obj_size); + +void c_LinkList_Destroy(c_LinkList_t* self); + +c_err_t c_LinkList_Add(c_LinkList_t* self, void* obj); + +c_err_t c_LinkList_Remove(c_LinkList_t* self, void* obj); + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +C_STATIC_FORCE_INLINE +void c_LinkListIter_Init(c_LinkListIter_t* self, c_LinkList_t* list) { + if (!self || !list) return; + self->list = list; + self->node = &list->head; +} + +C_STATIC_FORCE_INLINE +c_bool_t c_LinkListIter_HasNext(c_LinkListIter_t* self) { + if (!self) return C_FALSE; + return (self->node != NULL) && (*(self->node) != NULL); +} + +C_STATIC_FORCE_INLINE +void* c_LinkListIter_Next(c_LinkListIter_t* self) { + if (!self || !self->node || !*(self->node)) return NULL; + void* data = (*(self->node))->data; + self->node = &(*(self->node))->next; + return data; +} + +C_STATIC_FORCE_INLINE +void* c_LinkListIter_Get(c_LinkListIter_t* self) { + if (!self || !self->node || !*(self->node)) return NULL; + return (*(self->node))->data; +} + +void c_LinkListIter_Remove(c_LinkListIter_t* self); + +#endif /*INCLUDED_C_LINKLIST_H*/ diff --git a/cKit/Foundation/c_LinkList.t.c b/cKit/Foundation/c_LinkList.t.c new file mode 100644 index 0000000..d02340d --- /dev/null +++ b/cKit/Foundation/c_LinkList.t.c @@ -0,0 +1,67 @@ +#include "c_LinkList.h" +#include +#include + +typedef struct { + char name[4]; + int score; +} Student_t; + +int main() { + printf("開始執行 c_LinkList 測試用例...\n"); + + c_LinkList_t list; + c_LinkList_Init(&list, sizeof(Student_t)); + + Student_t s1 = {"AAA", 80}; + Student_t s2 = {"BBB", 90}; + Student_t s3 = {"CCC", 95}; + + // 1. 測試新增 (頭插法預期順序: CCC -> BBB -> AAA) + c_LinkList_Add(&list, &s1); + c_LinkList_Add(&list, &s2); + c_LinkList_Add(&list, &s3); + assert(list.size == 3); + + // 2. 測試迭代器走訪 + c_LinkListIter_t iter; + c_LinkListIter_Init(&iter, &list); + + printf("當前串列內容:\n"); + while (c_LinkListIter_HasNext(&iter)) { + Student_t* s = (Student_t*)c_LinkListIter_Next(&iter); + printf(" 學生: %s, 分數: %d\n", s->name, s->score); + } + + // 3. 測試一般刪除 (Remove - 透過二進位資料比對刪除 "BBB") + Student_t target_remove = {"BBB", 90}; + c_err_t err = c_LinkList_Remove(&list, &target_remove); + assert(err == C_ERR_SUCCESS); + assert(list.size == 2); + + // 4. 測試迭代器走訪並刪除 (Iter_Remove) + c_LinkListIter_Init(&iter, &list); + while (c_LinkListIter_HasNext(&iter)) { + Student_t* s = (Student_t*)c_LinkListIter_Get(&iter); + if (s->score == 80) { // 找到分數 80 的學生 (AAA) + c_LinkListIter_Remove(&iter); + printf("[Log] 迭代器成功刪除了分數為 80 的學生\n"); + } else { + c_LinkListIter_Next(&iter); + } + } + assert(list.size == 1); + + // 5. 驗證最後留下來的是否為 CCC + c_LinkListIter_Init(&iter, &list); + Student_t* final_s = (Student_t*)c_LinkListIter_Get(&iter); + assert(strcmp(final_s->name, "CCC") == 0); + + // 銷毀資源 + c_LinkList_Destroy(&list); + assert(list.head == NULL); + assert(list.size == 0); + + printf("所有測試成功通過!\n"); + return 0; +} \ No newline at end of file diff --git a/cKit/Foundation/c_LinkQueue.c b/cKit/Foundation/c_LinkQueue.c new file mode 100644 index 0000000..9721803 --- /dev/null +++ b/cKit/Foundation/c_LinkQueue.c @@ -0,0 +1,128 @@ +#include +#include +#include "c_Alignment.h" + +c_err_t c_LinkQueue_Init(c_LinkQueue_t* self, int obj_size) { + if (!self || obj_size == 0) return C_ERR_PARAM; + self->head = NULL; + self->tail = NULL; + self->obj_size = (int)obj_size; + self->size = 0; + return C_ERR_SUCCESS; +} + +void c_LinkQueue_Destroy(c_LinkQueue_t* self) { + if (!self) return; + + c_LinkQueueNode_t* current = self->head; + while (current != NULL) { + c_LinkQueueNode_t* next = current->next; + C_FREE(current); + current = next; + } + self->head = NULL; + self->tail = NULL; + self->size = 0; +} + +c_err_t c_LinkQueue_Push(c_LinkQueue_t* self, void* obj) { + if (!self || !obj) return C_ERR_PARAM; + + // 1. 配置新節點與資料空間 + int size = (int)sizeof(c_LinkQueueNode_t) + self->obj_size; + size = C_ALIGN_UPB(size, C_ALIGN_SIZE); + c_LinkQueueNode_t* new_node = (c_LinkQueueNode_t*)C_ALLOC(size); + if (!new_node) return C_ERR_NOMEM; + new_node->data = new_node+1; + + // 深複製資料內容 + memcpy(new_node->data, obj, self->obj_size); + new_node->next = NULL; + + // 2. 將新節點追加到串列尾端 + if (self->tail == NULL) { + // 佇列原本為空 + self->head = new_node; + self->tail = new_node; + } else { + // 佇列不為空,讓原本尾端的 next 指向新節點,並更新 tail + self->tail->next = new_node; + self->tail = new_node; + } + + self->size++; + return C_ERR_SUCCESS; +} + +c_err_t c_LinkQueue_Pop(c_LinkQueue_t* self, void* obj) { + if (!self ) return C_ERR_PARAM; + if (self->size == 0 || !self->head) return C_ERR_EMPTY; + + c_LinkQueueNode_t* to_delete = self->head; + + // 1. 複製資料到使用者緩衝區 + if (obj) { + memcpy(obj, to_delete->data, self->obj_size); + } + + // 2. 將 head 移至下一個節點 + self->head = to_delete->next; + + if (self->head == NULL) { + self->tail = NULL; + } + + // 3. 釋放斷開的節點資源 + C_FREE(to_delete); + + self->size--; + return C_ERR_SUCCESS; +} + +c_err_t c_LinkQueue_Peek(c_LinkQueue_t* self, void* obj) { + if (!self ) return C_ERR_PARAM; + if (self->size == 0 || !self->head) return C_ERR_EMPTY; + + // 複製最前端的資料內容 + if (obj) { + memcpy(obj, self->head->data, self->obj_size); + } + return C_ERR_SUCCESS; +} + +void c_LinkQueueIter_Remove(c_LinkQueueIter_t* self) { + if (!self || !self->queue || !self->node || !*(self->node)) return; + + c_LinkQueueNode_t* to_delete = *(self->node); + c_LinkQueue_t* q = self->queue; + + // 檢查目前要刪除的是否為尾端節點 + const c_bool_t is_tail = (to_delete == q->tail); + + // 讓上一個節點的 next(或 head)直接指向下一個節點,將 to_delete 從串列中斷開 + *(self->node) = to_delete->next; + + // 如果刪除的是尾端節點,必須更新 tail 指標 + if (is_tail) { + if (q->head == NULL) { + // 情況 A:刪除後佇列完全空了 + q->tail = NULL; + } else { + // 情況 B:刪除的是尾巴,但前方還有元素。 + // 沿著 head 重新走訪一遍,找出現在最尾端的節點(即 next 為 NULL 的節點) + c_LinkQueueNode_t* curr = q->head; + while (curr->next != NULL) { + curr = curr->next; + } + q->tail = curr; // 更新新的尾端 + } + } + + // 釋放記憶體資源 + C_FREE(to_delete); + + // 同步遞減佇列大小 + q->size--; + + // 此時 self->node 已自動留在原本的下一個節點上,呼叫端可直接繼續 Get() 或 Next() +} \ No newline at end of file diff --git a/cKit/Foundation/c_LinkQueue.h b/cKit/Foundation/c_LinkQueue.h new file mode 100644 index 0000000..543d30a --- /dev/null +++ b/cKit/Foundation/c_LinkQueue.h @@ -0,0 +1,71 @@ +#ifndef INCLUDED_C_LINKQUEUE_H +#define INCLUDED_C_LINKQUEUE_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct c_LinkQueueNode_t { + void* data; + struct c_LinkQueueNode_t * next; +}c_LinkQueueNode_t; + +typedef struct { + c_LinkQueueNode_t* head; + c_LinkQueueNode_t* tail; + int obj_size; + c_size_t size; +}c_LinkQueue_t; + +typedef struct { + c_LinkQueue_t* queue; + c_LinkQueueNode_t** node; +}c_LinkQueueIter_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_LinkQueue_Init(c_LinkQueue_t* self, int obj_size); + +void c_LinkQueue_Destroy(c_LinkQueue_t* self); + +c_err_t c_LinkQueue_Push(c_LinkQueue_t* self, void* obj); + +c_err_t c_LinkQueue_Pop(c_LinkQueue_t* self, void* obj); + +c_err_t c_LinkQueue_Peek(c_LinkQueue_t* self, void* obj); + +C_STATIC_FORCE_INLINE +void c_LinkQueueIter_Init(c_LinkQueueIter_t* self, c_LinkQueue_t* queue) { + if (!self || !queue) return; + self->queue = queue; + self->node = &queue->head; +} + +C_STATIC_FORCE_INLINE +c_bool_t c_LinkQueueIter_HasNext(c_LinkQueueIter_t* self) { + if (!self) return C_FALSE; + return (self->node != NULL) && (*(self->node) != NULL); +} + +C_STATIC_FORCE_INLINE +void* c_LinkQueueIter_Next(c_LinkQueueIter_t* self) { + if (!self || !self->node || !*(self->node)) return NULL; + void* data = (*(self->node))->data; + self->node = &(*(self->node))->next; + return data; +} + +C_STATIC_FORCE_INLINE +void* c_LinkQueueIter_Get(c_LinkQueueIter_t* self) { + if (!self || !self->node || !*(self->node)) return NULL; + return (*(self->node))->data; +} + +void c_LinkQueueIter_Remove(c_LinkQueueIter_t* self); + +#endif /*INCLUDED_C_LINKQUEUE_H*/ diff --git a/cKit/Foundation/c_LinkQueue.t.c b/cKit/Foundation/c_LinkQueue.t.c new file mode 100644 index 0000000..c15accb --- /dev/null +++ b/cKit/Foundation/c_LinkQueue.t.c @@ -0,0 +1,199 @@ +#include "c_LinkQueue.h" +#include +#include +typedef struct { + char text[16]; + int id; +} Message_t; + +void test_log(const char* test_name) { + printf("[PASS] %s\n", test_name); +} + +typedef struct { + char* data_str; + int id; +} DataPacket_t; + +static void test_iter(void) { + printf("==================================================\n"); + printf(" 開始執行 c_LinkQueueIter_Remove 單元測試\n"); + printf("==================================================\n\n"); + + c_LinkQueue_t queue; + c_LinkQueue_Init(&queue, sizeof(DataPacket_t)); + + DataPacket_t p1 = {"PacketA", 101}; + DataPacket_t p2 = {"PacketB", 102}; + DataPacket_t p3 = {"PacketC", 103}; + + // 推入三筆資料 (FIFO 順序: head -> p1 -> p2 -> p3 <- tail) + c_LinkQueue_Push(&queue, &p1); + c_LinkQueue_Push(&queue, &p2); + c_LinkQueue_Push(&queue, &p3); + assert(queue.size == 3); + assert(((DataPacket_t*)queue.tail->data)->id == 103); // 確任目前尾端是 p3 + + c_LinkQueueIter_t iter; + + // ========================================== + // 測試 1:刪除中間節點 (p2: 102) + // ========================================== + c_LinkQueueIter_Init(&iter, &queue); + while (c_LinkQueueIter_HasNext(&iter)) { + DataPacket_t* pkt = (DataPacket_t*)c_LinkQueueIter_Get(&iter); + if (pkt->id == 102) { + c_LinkQueueIter_Remove(&iter); // 刪除 PacketB + printf("[PASS] 成功刪除中間節點 (102)\n"); + } else { + c_LinkQueueIter_Next(&iter); + } + } + assert(queue.size == 2); + assert(((DataPacket_t*)queue.tail->data)->id == 103); // 尾端應維持 103 + + // ========================================== + // 測試 2:刪除尾端節點 (p3: 103) -> 測試 tail 更新 + // ========================================== + c_LinkQueueIter_Init(&iter, &queue); + while (c_LinkQueueIter_HasNext(&iter)) { + DataPacket_t* pkt = (DataPacket_t*)c_LinkQueueIter_Get(&iter); + if (pkt->id == 103) { + c_LinkQueueIter_Remove(&iter); // 刪除 PacketC (當前的尾端) + printf("[PASS] 成功刪除尾端節點 (103)\n"); + } else { + c_LinkQueueIter_Next(&iter); + } + } + assert(queue.size == 1); + // 關鍵斷言:刪除原本的尾端 103 後,queue->tail 必須自動往前更新為 101 (PacketA) + assert(queue.tail != NULL); + assert(((DataPacket_t*)queue.tail->data)->id == 101); + assert(queue.head == queue.tail); // 只剩一個元素時,head 應等於 tail + + // ========================================== + // 3. 測試 3:刪除最後一個節點 (p1: 101) -> 測試佇列歸零 + // ========================================== + c_LinkQueueIter_Init(&iter, &queue); + assert(c_LinkQueueIter_HasNext(&iter) == C_TRUE); + c_LinkQueueIter_Remove(&iter); // 刪除僅存的 PacketA + printf("[PASS] 成功刪除最後一個節點 (101)\n"); + + assert(queue.size == 0); + assert(queue.head == NULL); + assert(queue.tail == NULL); // 關鍵斷言:完全空了之後 tail 必須回歸 NULL + assert(c_LinkQueueIter_HasNext(&iter) == C_FALSE); + + // 清理資源 + c_LinkQueue_Destroy(&queue); + printf("\n==================================================\n"); + printf(" 恭喜!c_LinkQueueIter_Remove 所有特例驗證全數通過!\n"); + printf("==================================================\n"); +} + +int main() { + printf("==================================================\n"); + printf(" 開始執行 c_LinkQueue 完整版測試用例\n"); + printf("==================================================\n\n"); + + // ========================================== + // 1. 測試初始化 + // ========================================== + c_LinkQueue_t q; + c_err_t err = c_LinkQueue_Init(&q, sizeof(Message_t)); + assert(err == C_ERR_SUCCESS); + assert(q.size == 0); + assert(q.head == NULL); + test_log("1. 鏈結佇列初始化成功"); + + // ========================================== + // 2. 測試資料推入 (Push) + // ========================================== + Message_t msg1 = {"MsgA", 101}; + Message_t msg2 = {"MsgB", 102}; + Message_t msg3 = {"MsgC", 103}; + + err = c_LinkQueue_Push(&q, &msg1); assert(err == C_ERR_SUCCESS); + err = c_LinkQueue_Push(&q, &msg2); assert(err == C_ERR_SUCCESS); + err = q.size == 2; + err = c_LinkQueue_Push(&q, &msg3); assert(err == C_ERR_SUCCESS); + assert(q.size == 3); + test_log("2. 三筆資料成功推入佇列 (尾端追加)"); + + // ========================================== + // 3. 測試迭代器走訪 (應符合 Push 的先進順序:A -> B -> C) + // ========================================== + c_LinkQueueIter_t iter; + c_LinkQueueIter_Init(&iter, &q); + + int check_idx = 0; + while (c_LinkQueueIter_HasNext(&iter)) { + Message_t* m = (Message_t*)c_LinkQueueIter_Next(&iter); + if (check_idx == 0) assert(strcmp(m->text, "MsgA") == 0); + if (check_idx == 1) assert(strcmp(m->text, "MsgB") == 0); + if (check_idx == 2) assert(strcmp(m->text, "MsgC") == 0); + check_idx++; + } + assert(check_idx == 3); + test_log("3. 迭代器順序走訪驗證成功 (符合 FIFO 順序)"); + + // ========================================== + // 4. 測試查看最前端元素 (Peek) + // ========================================== + Message_t local_buf; + err = c_LinkQueue_Peek(&q, &local_buf); + assert(err == C_ERR_SUCCESS); + assert(strcmp(local_buf.text, "MsgA") == 0); + assert(local_buf.id == 101); + assert(q.size == 3); // 驗證 Peek 不會減少佇列大小 + test_log("4. 隨機查看最前端元素 (Peek) 成功"); + + // ========================================== + // 5. 測試先進先出彈出 (Pop) + // ========================================== + // 第一次 Pop:預期拿到最先進入的 MsgA + err = c_LinkQueue_Pop(&q, &local_buf); + assert(err == C_ERR_SUCCESS); + assert(strcmp(local_buf.text, "MsgA") == 0); + assert(q.size == 2); + + // 彈出後再次 Peek,最前端應該變成 MsgB + err = c_LinkQueue_Peek(&q, &local_buf); + assert(err == C_ERR_SUCCESS); + assert(strcmp(local_buf.text, "MsgB") == 0); + + // 第二次與第三次 Pop + err = c_LinkQueue_Pop(&q, &local_buf); assert(err == C_ERR_SUCCESS); // 彈出 MsgB + err = c_LinkQueue_Pop(&q, &local_buf); assert(err == C_ERR_SUCCESS); // 彈出 MsgC + assert(q.size == 0); + + // 空佇列彈出與查看測試,預期回傳越界錯誤 + err = c_LinkQueue_Pop(&q, &local_buf); + assert(err == C_ERR_EMPTY); + err = c_LinkQueue_Peek(&q, &local_buf); + assert(err == C_ERR_EMPTY); + test_log("5. 彈出 (Pop) 邏輯與空佇列防呆驗證成功"); + + // ========================================== + // 6. 參數安全檢查 + // ========================================== + assert(c_LinkQueue_Init(NULL, sizeof(Message_t)) == C_ERR_PARAM); + assert(c_LinkQueue_Push(NULL, &msg1) == C_ERR_PARAM); + // assert(c_LinkQueue_Pop(&q, NULL) == C_ERR_PARAM); + test_log("6. 介面 NULL 指標防呆驗證成功"); + + // ========================================== + // 7. 銷毀佇列 + // ========================================== + c_LinkQueue_Destroy(&q); + assert(q.head == NULL); + assert(q.size == 0); + test_log("7. 佇列銷毀與記憶體釋放成功"); + + printf("\n==================================================\n"); + printf(" 恭喜!所有 c_LinkQueue 測試皆順利通過!\n"); + printf("==================================================\n"); + + test_iter(); + return 0; +} \ No newline at end of file diff --git a/cKit/Foundation/c_LinkStack.c b/cKit/Foundation/c_LinkStack.c new file mode 100644 index 0000000..31e593b --- /dev/null +++ b/cKit/Foundation/c_LinkStack.c @@ -0,0 +1,98 @@ +#include +#include +#include "c_Alignment.h" + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +c_err_t c_LinkStack_Init(c_LinkStack_t* self, int obj_size) { + if (!self || obj_size <= 0) return C_ERR_PARAM; + self->head = NULL; + self->obj_size = obj_size; + self->size = 0; + return C_ERR_SUCCESS; +} + +// 銷毀堆疊並釋放所有配置的節點與資料記憶體 +void c_LinkStack_Destroy(c_LinkStack_t* self) { + if (!self) return; + + c_LinkStackNode_t* current = self->head; + while (current != NULL) { + c_LinkStackNode_t* next = current->next; + C_FREE(current); + current = next; + } + self->head = NULL; + self->size = 0; +} + +// 推入元素:採用頭插法實作 O(1) 頂端推入,並深複製資料內容 +c_err_t c_LinkStack_Push(c_LinkStack_t* self, void* obj) { + if (!self || !obj) return C_ERR_PARAM; + int size = sizeof(c_LinkStackNode_t) + self->obj_size; + size = C_ALIGN_UPB(size, C_ALIGN_SIZE); + + c_LinkStackNode_t* new_node = (c_LinkStackNode_t*)C_ALLOC(size); + if (!new_node) return C_ERR_NOMEM; + new_node->data = new_node+1; + + // 值複製 (Value Copy) + memcpy(new_node->data, obj, self->obj_size); + + // 頭插法連結 + new_node->next = self->head; + self->head = new_node; + + self->size++; + return C_ERR_SUCCESS; +} + +// 彈出頂端元素:將 `head` 的資料值複製給使用者,隨後釋放該前端節點 (O(1)) +c_err_t c_LinkStack_Pop(c_LinkStack_t* self, void* obj) { + if (!self || !obj) return C_ERR_PARAM; + if (self->size == 0 || !self->head) return C_ERR_EMPTY; + + c_LinkStackNode_t* to_delete = self->head; + + // 將資料複製到呼叫端提供的緩衝區 + memcpy(obj, to_delete->data, self->obj_size); + + // 斷開頂端節點,head 指向下一個 + self->head = to_delete->next; + + // 釋放資源 + C_FREE(to_delete); + + self->size--; + return C_ERR_SUCCESS; +} + +// 查看目前最頂端的元素指標 (O(1),不移除節點) +void* c_LinkStack_Peek(c_LinkStack_t* self) { + if (!self || !self->head || self->size == 0) return NULL; + return self->head->data; +} + +void c_LinkStackIter_Remove(c_LinkStackIter_t* self) { + // 防呆檢查:確保迭代器有效、繫結的堆疊存在,且當前指向的節點不為空 + if (!self || !self->stack || !self->node || !*(self->node)) return; + + c_LinkStackNode_t* to_delete = *(self->node); + + // 關鍵指標轉移: + // 將當前結構中維護的指標(可能是前一節點的 next,或是 stack 的 head) + // 修改為指向下一個節點,直接從鏈結串列中斷開該節點 + *(self->node) = to_delete->next; + + // 釋放節點內深複製的資料空間與節點結構本體 + C_FREE(to_delete); + + // 同步遞減堆疊的總大小 + self->stack->size--; + + // 注意:由於 *(self->node) 已被賦值為 to_delete->next, + // self->node 目前已自動指向了原本的下一個節點。 + // 使用者不需要再手動呼叫 Next(),即可直接對新位置進行 Get()、Next() 或再次 Remove()。 +} diff --git a/cKit/Foundation/c_LinkStack.h b/cKit/Foundation/c_LinkStack.h new file mode 100644 index 0000000..76ddfd9 --- /dev/null +++ b/cKit/Foundation/c_LinkStack.h @@ -0,0 +1,64 @@ +#ifndef INCLUDED_C_LINKSTACK_H +#define INCLUDED_C_LINKSTACK_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +typedef struct c_LinkStackNode_t { + void* data; + struct c_LinkStackNode_t* next; +} c_LinkStackNode_t; + +typedef struct { + c_LinkStackNode_t* head; + int obj_size; + c_size_t size; +} c_LinkStack_t; + +typedef struct { + c_LinkStack_t* stack; + c_LinkStackNode_t** node; +} c_LinkStackIter_t; + +c_err_t c_LinkStack_Init(c_LinkStack_t* self, int obj_size); +void c_LinkStack_Destroy(c_LinkStack_t* self); +c_err_t c_LinkStack_Push(c_LinkStack_t* self, void* obj); +c_err_t c_LinkStack_Pop(c_LinkStack_t* self, void* obj); +void* c_LinkStack_Peek(c_LinkStack_t* self); + +// 迭代器內聯函數實作 +C_STATIC_FORCE_INLINE +void c_LinkStackIter_Init(c_LinkStackIter_t* self, c_LinkStack_t* stack) { + if (!self || !stack) return; + self->stack = stack; + self->node = &stack->head; +} + +C_STATIC_FORCE_INLINE +c_bool_t c_LinkStackIter_HasNext(c_LinkStackIter_t* self) { + if (!self) return C_FALSE; + return (self->node != NULL) && (*(self->node) != NULL); +} + +C_STATIC_FORCE_INLINE +void* c_LinkStackIter_Next(c_LinkStackIter_t* self) { + if (!self || !self->node || !*(self->node)) return NULL; + void* data = (*(self->node))->data; + self->node = &(*(self->node))->next; + return data; +} + +C_STATIC_FORCE_INLINE +void* c_LinkStackIter_Get(c_LinkStackIter_t* self) { + if (!self || !self->node || !*(self->node)) return NULL; + return (*(self->node))->data; +} + +void c_LinkStackIter_Remove(c_LinkStackIter_t* self); + +#endif /*INCLUDED_C_LINKSTACK_H*/ diff --git a/cKit/Foundation/c_LinkStack.t.c b/cKit/Foundation/c_LinkStack.t.c new file mode 100644 index 0000000..722f7b3 --- /dev/null +++ b/cKit/Foundation/c_LinkStack.t.c @@ -0,0 +1,117 @@ +#include "c_LinkStack.h" +#include +#include + +typedef struct { + char name[16]; + int id; +} Frame_t; + +void test_log(const char* test_name) { + printf("[PASS] %s\n", test_name); +} + +int main() { + printf("==================================================\n"); + printf(" 開始執行 c_LinkStack 完整單元測試用例\n"); + printf("==================================================\n\n"); + + // ========================================== + // 1. 初始化測試 + // ========================================== + c_LinkStack_t s; + c_err_t err = c_LinkStack_Init(&s, sizeof(Frame_t)); + assert(err == C_ERR_SUCCESS); + assert(s.size == 0); + assert(s.head == NULL); + test_log("1. 鏈結堆疊初始化狀態驗證成功"); + + // ========================================== + // 2. 資料推入測試 (Push) + // ========================================== + Frame_t f1 = {"MainFrame", 100}; + Frame_t f2 = {"RenderFrame", 200}; + Frame_t f3 = {"UpdateFrame", 300}; + + // 依序推入,預期最新推入的會待在鏈結最前端 (head) + err = c_LinkStack_Push(&s, &f1); assert(err == C_ERR_SUCCESS); + err = c_LinkStack_Push(&s, &f2); assert(err == C_ERR_SUCCESS); + err = c_LinkStack_Push(&s, &f3); assert(err == C_ERR_SUCCESS); + assert(s.size == 3); + test_log("2. 三筆資料成功推入堆疊 (頭插法 O(1))"); + + // ========================================== + // 3. 迭代器走訪測試 (應符合後進先出順序:f3 -> f2 -> f1) + // ========================================== + c_LinkStackIter_t iter; + c_LinkStackIter_Init(&iter, &s); + int check_idx = 0; + while (c_LinkStackIter_HasNext(&iter)) { + Frame_t* f = (Frame_t*)c_LinkStackIter_Next(&iter); + if (check_idx == 0) assert(strcmp(f->name, "UpdateFrame") == 0); + if (check_idx == 1) assert(strcmp(f->name, "RenderFrame") == 0); + if (check_idx == 2) assert(strcmp(f->name, "MainFrame") == 0); + check_idx++; + } + assert(check_idx == 3); + test_log("3. 迭代器走訪順序驗證成功 (符合 LIFO 頂端到探底順序)"); + + // ========================================== + // 4. 查看堆疊頂端 (Peek) + // ========================================== + // 目前最頂端應該依然是最後推進去的 UpdateFrame + Frame_t* p_peek = (Frame_t*)c_LinkStack_Peek(&s); + assert(p_peek != NULL); + assert(strcmp(p_peek->name, "UpdateFrame") == 0); + assert(p_peek->id == 300); + assert(s.size == 3); // 驗證 Peek 不會移除元素 + test_log("4. 唯讀查看堆疊頂端元素 (Peek) 成功"); + + // ========================================== + // 5. 先進後出彈出測試 (Pop) + // ========================================== + Frame_t local_buf; + + // 第一次 Pop:預期取出最上方的 UpdateFrame + err = c_LinkStack_Pop(&s, &local_buf); + assert(err == C_ERR_SUCCESS); + assert(strcmp(local_buf.name, "UpdateFrame") == 0); + assert(s.size == 2); + + // 再次 Peek,頂端應更新為 RenderFrame + p_peek = (Frame_t*)c_LinkStack_Peek(&s); + assert(strcmp(p_peek->name, "RenderFrame") == 0); + + // 連續彈出剩下的兩個元素 + err = c_LinkStack_Pop(&s, &local_buf); assert(err == C_ERR_SUCCESS); // 取出 RenderFrame + err = c_LinkStack_Pop(&s, &local_buf); assert(err == C_ERR_SUCCESS); // 取出 MainFrame + assert(s.size == 0); + assert(s.head == NULL); + + // 空堆疊彈出與查看測試,預期回傳越界錯誤 + err = c_LinkStack_Pop(&s, &local_buf); + assert(err == C_ERR_EMPTY); + assert(c_LinkStack_Peek(&s) == NULL); + test_log("5. 後進先出彈出 (Pop) 邏輯與空防呆驗證成功"); + + // ========================================== + // 6. 介面安全指標檢查 + // ========================================== + assert(c_LinkStack_Init(NULL, sizeof(Frame_t)) == C_ERR_PARAM); + assert(c_LinkStack_Push(NULL, &f1) == C_ERR_PARAM); + assert(c_LinkStack_Pop(&s, NULL) == C_ERR_PARAM); + test_log("6. 介面 NULL 指標防呆驗證成功"); + + // ========================================== + // 7. 銷毀堆疊 (Destroy) + // ========================================== + c_LinkStack_Destroy(&s); + assert(s.head == NULL); + assert(s.size == 0); + test_log("7. 堆疊資源銷毀與記憶體釋放成功"); + + printf("\n==================================================\n"); + printf(" 恭喜!c_LinkStack 優化版單元測試全數順利通過!\n"); + printf("==================================================\n"); + return 0; +} \ No newline at end of file diff --git a/cKit/Foundation/c_List.c b/cKit/Foundation/c_List.c new file mode 100644 index 0000000..2857b2c --- /dev/null +++ b/cKit/Foundation/c_List.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Foundation/c_List.h b/cKit/Foundation/c_List.h new file mode 100644 index 0000000..d97d2ca --- /dev/null +++ b/cKit/Foundation/c_List.h @@ -0,0 +1,73 @@ +#ifndef INCLUDED_C_LIST_H +#define INCLUDED_C_LIST_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct c_ListNode_t { + struct c_ListNode_t* prev; + struct c_ListNode_t* next; +}c_ListNode_t; + +typedef c_ListNode_t c_List_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define c_List_Prev(n) (n)->prev +#define c_List_Next(n) (n)->next +#define c_List_PrevNext(n) c_List_Next(c_List_Prev(n)) +#define c_List_NextPrev(n) c_List_Prev(c_List_Next(n)) + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +#define c_List_Init(n) do{ \ + c_List_Prev(n) = c_List_Next(n) = (n); \ +}while(0) + +#define c_List_IsEmpty(n) ((c_List_Next(n) == (n)) && (c_List_Prev(n) == (n))) + +#define c_List_InsertBefore(L, N) do { \ + c_List_Next(N) = (L); \ + c_List_Prev(N) = c_List_Prev(L); \ + c_List_PrevNext(N) = (N); \ + c_List_Prev(L) = (N); \ +} while(0) + +#define c_List_InsertAfter(L, N) do { \ + c_List_Prev(N) = (L); \ + c_List_Next(N) = c_List_Next(L); \ + c_List_NextPrev(N) = (N); \ + c_List_Next(L) = (N); \ +} while(0) + +#define c_List_Remove(n) do { \ + c_List_PrevNext(n) = c_List_Next(n); \ + c_List_NextPrev(n) = c_List_Prev(n); \ + c_List_Init(n); \ +} while(0) + +#define c_List_Entry(ptr, type, member) \ + ((type *)((char *)(ptr) - offsetof(type, member))) + +// Standard forward loop: Safe for lookups, UNSAFE for deletions +#define c_List_ForEachEntry(head, pos, type, member) \ + for (pos = c_List_Entry(c_List_Next(head), type, member); \ + &pos->member != (head); \ + pos = c_List_Entry(c_List_Next(&pos->member), type, member)) + +// Safe forward loop: Explicitly safe to call c_List_Remove inside the loop body +#define c_List_ForEachEntrySafe(head, pos, n, type, member) \ + for (pos = c_List_Entry(c_List_Next(head), type, member), \ + n = c_List_Entry(c_List_Next(&pos->member), type, member); \ + &pos->member != (head); \ + pos = n, n = c_List_Entry(c_List_Next(&n->member), type, member)) + +#endif /*INCLUDED_C_LIST_H*/ diff --git a/cKit/Foundation/c_List.t.c b/cKit/Foundation/c_List.t.c new file mode 100644 index 0000000..3a468de --- /dev/null +++ b/cKit/Foundation/c_List.t.c @@ -0,0 +1,142 @@ +#include "c_List.h" +#include +#include + +// Custom User Type showcasing intrusive inclusion +typedef struct { + int value; + c_ListNode_t node; // Intrusive node payload linkage +} CustomData_t; + +static void test2(void) { + printf("==================================================\n"); + printf(" Starting Intrusive Safe-Iterator Unit Tests\n"); + printf("==================================================\n\n"); + + c_List_t head; + c_List_Init(&head); + + CustomData_t d1 = {10}; + CustomData_t d2 = {20}; + CustomData_t d3 = {30}; + + // Linking nodes to tail: [Head] <-> [10] <-> [20] <-> [30] + c_List_InsertBefore(&head, &d1.node); + c_List_InsertBefore(&head, &d2.node); + c_List_InsertBefore(&head, &d3.node); + + // ========================================== + // 1. Verify Lookups via ForEachEntry + // ========================================== + CustomData_t* pos; + int index = 0; + int expected[] = {10, 20, 30}; + + printf("Reading nodes using standard ForEachEntry:\n"); + c_List_ForEachEntry(&head, pos, CustomData_t, node) { + printf(" Found Entry value: %d\n", pos->value); + assert(pos->value == expected[index++]); + } + assert(index == 3); + printf("[PASS] Standard lookup loop complete.\n\n"); + + // ========================================== + // 2. Verify Mutations via ForEachEntrySafe + // ========================================== + CustomData_t* tmp; // Cache variable for safe traversal tracking + index = 0; + + printf("Filtering and removing matching entry elements via Safe Loop:\n"); + c_List_ForEachEntrySafe(&head, pos, tmp, CustomData_t, node) { + if (pos->value == 20) { + printf(" Modifying layout: Safely dropping element (%d)\n", pos->value); + c_List_Remove(&pos->node); // Remove middle item mid-flight + } + index++; + } + assert(index == 3); // Iterated through all three bounds + + // ========================================== + // 3. Confirm Final Structure Integrity + // ========================================== + // List must now structurally bridge to bypass item 20: [10] <-> [30] + index = 0; + c_List_ForEachEntry(&head, pos, CustomData_t, node) { + if (index == 0) assert(pos->value == 10); + if (index == 1) assert(pos->value == 30); + index++; + } + assert(index == 2); + printf("[PASS] Structural integrity verified following safe removal mutation.\n"); + + printf("\n==================================================\n"); + printf(" Success! Intrusive layout traversal operates flawlessly!\n"); + printf("==================================================\n"); +} + +int main() { + printf("==================================================\n"); + printf(" Starting Intrusive Circular Doubly Linked List Tests\n"); + printf("==================================================\n\n"); + + // Initialize list head context anchor + c_List_t head; + c_List_Init(&head); + assert(c_List_IsEmpty(&head) == 1); + + CustomData_t d1 = {100}; + CustomData_t d2 = {200}; + CustomData_t d3 = {300}; + + // ========================================== + // 1. Insertion Testing + // ========================================== + // Insert d1 after head -> List: [Head] <-> [100] + c_List_InsertAfter(&head, &d1.node); + assert(c_List_IsEmpty(&head) == 0); + + // Insert d3 before head -> List: [Head] <-> [100] <-> [300] + c_List_InsertBefore(&head, &d3.node); + + // Insert d2 after d1 -> List: [Head] <-> [100] <-> [200] <-> [300] + c_List_InsertAfter(&d1.node, &d2.node); + + printf("[PASS] Sequence insertions complete.\n"); + + // ========================================== + // 2. Linear Traversal Verification + // ========================================== + c_ListNode_t* curr = c_List_Next(&head); + int expected_values[] = {100, 200, 300}; + int idx = 0; + + while (curr != &head) { + CustomData_t* entry = c_List_Entry(curr, CustomData_t, node); + assert(entry->value == expected_values[idx]); + curr = c_List_Next(curr); + idx++; + } + assert(idx == 3); + printf("[PASS] Intrusive forward iterator matching sequential expectations.\n"); + + // ========================================== + // 3. Deletion and Mutation Verification + // ========================================== + // Target and drop middle node (200) + c_List_Remove(&d2.node); + + // Structure must bridge gaps smoothly: [100] <-> [300] + assert(c_List_Next(&d1.node) == &d3.node); + assert(c_List_Prev(&d3.node) == &d1.node); + + // Eliminated isolated node must be self-referencing via macro assignment + assert(c_List_IsEmpty(&d2.node) == 1); + printf("[PASS] Drop operation and isolated reference initialization verified.\n"); + + printf("\n==================================================\n"); + printf(" Success! Intrusive macro mutations match layout specs!\n"); + printf("==================================================\n"); + + test2(); + return 0; +} \ No newline at end of file diff --git a/cKit/Foundation/c_LockQueue.c b/cKit/Foundation/c_LockQueue.c new file mode 100644 index 0000000..bf17a12 --- /dev/null +++ b/cKit/Foundation/c_LockQueue.c @@ -0,0 +1,324 @@ +#include + +#if defined(PLATFORM_POSIX) +#include +#endif + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_LockQueue_Init(c_LockQueue_t* queue, c_size_t capacity) { + if (!queue || capacity==0) return C_ERR_PARAM; + queue->data = (void**)malloc(sizeof(void*) * capacity); + if (!queue->data) { + return C_ERR_NOMEM; + } + queue->capacity = capacity; + queue->write_idx = 0; + queue->read_idx = 0; + queue->size = 0; + queue->is_shutdown = C_FALSE; + + if ((c_Mutex_Init(&queue->lock)!=C_ERR_SUCCESS) || + (c_Cond_Init(&queue->not_full)!=C_ERR_SUCCESS) || + (c_Cond_Init(&queue->not_empty)!=C_ERR_SUCCESS)) + { + if (queue->data) { + free(queue->data); + queue->data = NULL; + } + } + return C_ERR_OK; +} +void c_LockQueue_Destroy(c_LockQueue_t* queue) { + if (!queue) return; + + c_LockQueue_Shutdown(queue); + + c_Mutex_Lock(&queue->lock); + c_Cond_Destroy(&queue->not_full); + c_Cond_Destroy(&queue->not_empty); + if (queue->data) { + free(queue->data); + queue->data = NULL; + } + c_Mutex_UnLock(&queue->lock); + + // 销毁跨平台锁 + c_Mutex_Destroy(&queue->lock); +} + +void c_LockQueue_Shutdown(c_LockQueue_t* queue) { + if (!queue) return; + + c_Mutex_Lock(&queue->lock); + queue->is_shutdown = C_TRUE; + + // 广播唤醒所有正在阻塞的生产者和消费者,让他们通过 is_shutdown 状态感知并安全退出 + c_Cond_Broadcast(&queue->not_full); + c_Cond_Broadcast(&queue->not_empty); + c_Mutex_UnLock(&queue->lock); +} + +c_err_t c_LockQueue_Push(c_LockQueue_t* queue, void* data) { + if (!queue) return C_ERR_PARAM; + + c_Mutex_Lock(&queue->lock); + + // 1. 经典工业级设计:使用 while 循环检查条件,完美防御虚假唤醒 + while (queue->size == queue->capacity && !queue->is_shutdown) { + c_Cond_Wait(&queue->not_full, &queue->lock); + } + + // 2. 如果队列中途被关闭,直接拒绝写入并返回 + if (queue->is_shutdown) { + c_Mutex_UnLock(&queue->lock); + return C_ERR_STATUS; + } + + // 3. 循环数组插入数据 + queue->data[queue->write_idx] = data; + queue->write_idx = (queue->write_idx + 1) % queue->capacity; + queue->size++; + + // 4. 唤醒可能正在等待数据的消费者 + c_Cond_Signal(&queue->not_empty); + + c_Mutex_UnLock(&queue->lock); + return C_ERR_SUCCESS; +} + +c_err_t c_LockQueue_Pop(c_LockQueue_t* queue, void** item) { + if (!queue ) return C_ERR_PARAM; + + c_Mutex_Lock(&queue->lock); + + // 1. 队空且未关闭时,消费者阻塞等待 + while (queue->size == 0 && !queue->is_shutdown) { + c_Cond_Wait(&queue->not_empty, &queue->lock); + } + + // 2. 如果队列已关闭且数据已被清空,优雅退出 + if (queue->is_shutdown && queue->size == 0) { + c_Mutex_UnLock(&queue->lock); + return C_ERR_STATUS; + } + + // 3. 循环数组取出数据 + if (item) { + *item = queue->data[queue->read_idx]; + } + queue->read_idx = (queue->read_idx + 1) % queue->capacity; + queue->size--; + + // 4. 唤醒可能正在等待空间的生产者 + c_Cond_Signal(&queue->not_full); + + c_Mutex_UnLock(&queue->lock); + return C_ERR_SUCCESS; +} + +c_size_t c_LockQueue_Size(c_LockQueue_t* queue) { + if (!queue) return 0; + c_Mutex_Lock(&queue->lock); + const c_size_t size = queue->size; + c_Mutex_UnLock(&queue->lock); + return size; +} + +c_bool_t c_LockQueue_IsEmpty(c_LockQueue_t* queue) { + if (!queue) return true; // 安全檢查:無效隊列視為空 + + c_Mutex_Lock(&queue->lock); + const c_bool_t is_empty = (queue->size == 0); + c_Mutex_UnLock(&queue->lock); + + return is_empty; +} + +c_bool_t c_LockQueue_IsFull(c_LockQueue_t* queue) { + if (!queue) return false; // 安全檢查 + + c_Mutex_Lock(&queue->lock); + const c_bool_t is_full = (queue->size == queue->capacity); + c_Mutex_UnLock(&queue->lock); + return is_full; +} + + +c_bool_t c_LockQueue_TimedPop(c_LockQueue_t* queue, void** item, c_uint_t timeout_ms) { + if (!queue || !item) return C_FALSE; + + c_Mutex_Lock(&queue->lock); + + // 1. Calculate the absolute deadline for POSIX or track elapsed time for Windows + c_uint_t remaining_ms = timeout_ms; + +#if defined(PLATFORM_POSIX) + // POSIX timedwait requires an absolute system calendar time deadline + struct timespec deadline; + struct timeval now; + gettimeofday(&now, NULL); + long long total_ns = (long long)now.tv_usec * 1000 + (long long)timeout_ms * 1000000; + deadline.tv_sec = now.tv_sec + total_ns / 1000000000LL; + deadline.tv_nsec = total_ns % 1000000000LL; +#elif defined(PLATFORM_WINDOWS) + // Windows tracks relative intervals natively via GetTickCount/GetTickCount64 + ULONGLONG start_tick = GetTickCount64(); +#endif + + // 2. Loop to defend against Spurious Wakeups + while (queue->size == 0 && !queue->is_shutdown) { + if (remaining_ms == 0) { + // Out of time before cond wait or remaining time became zero + c_Mutex_UnLock(&queue->lock); + return C_FALSE; + } + + // 3. Atomically release the lock and sleep until signaled or timed out +#if defined(PLATFORM_WINDOWS) + // SleepConditionVariableCS handles relative timeout natively + BOOL wait_success = SleepConditionVariableCS(&queue->not_empty.handle, &queue->lock.handle, remaining_ms); + + if (!wait_success) { + if (GetLastError() == ERROR_TIMEOUT) { + c_Mutex_UnLock(&queue->lock); + return C_FALSE; // Dynamic Windows timeout hit + } + } + + // Recalculate remaining time in case of spurious wakeups + ULONGLONG elapsed = GetTickCount64() - start_tick; + if (elapsed >= timeout_ms) { + remaining_ms = 0; + } else { + remaining_ms = timeout_ms - (c_uint_t)elapsed; + } +#elif defined(PLATFORM_POSIX) + // pthread_cond_timedwait takes the exact calculated deadline + int wait_result = pthread_cond_timedwait(&queue->not_empty.handle, &queue->lock.handle, &deadline); + + if (wait_result != 0) { + // POSIX returns ETIMEDOUT (usually 110) if time limit expires + c_Mutex_UnLock(&queue->lock); + return C_FALSE; + } + + // Re-verify remaining time using current clock just to be precise + gettimeofday(&now, NULL); + long long current_ms = (long long)now.tv_sec * 1000 + now.tv_usec / 1000; + long long deadline_ms = (long long)deadline.tv_sec * 1000 + deadline.tv_nsec / 1000000; + if (current_ms >= deadline_ms) { + remaining_ms = 0; + } else { + remaining_ms = (c_uint_t)(deadline_ms - current_ms); + } +#endif + } + + // 4. Handle exit criteria if queue shut down during wait + if (queue->is_shutdown && queue->size == 0) { + c_Mutex_UnLock(&queue->lock); + return C_FALSE; + } + + // 5. Securely pop data from circular buffer + *item = queue->data[queue->read_idx]; + queue->read_idx = (queue->read_idx + 1) % queue->capacity; + queue->size--; + + // 6. Signal blocked producers that space has cleared up + c_Cond_Signal(&queue->not_full); + + c_Mutex_UnLock(&queue->lock); + return C_TRUE; +} + +c_bool_t c_LockQueue_TimedPush(c_LockQueue_t* queue, void* item, c_uint_t timeout_ms) { + if (!queue || !item) return C_FALSE; + + c_Mutex_Lock(&queue->lock); + + // 1. Calculate the absolute deadline for POSIX or track elapsed time for Windows + c_uint_t remaining_ms = timeout_ms; + +#if defined(PLATFORM_POSIX) + // POSIX timedwait requires an absolute wall-clock calendar deadline + struct timespec deadline; + struct timeval now; + gettimeofday(&now, NULL); + long long total_ns = (long long)now.tv_usec * 1000 + (long long)timeout_ms * 1000000; + deadline.tv_sec = now.tv_sec + total_ns / 1000000000LL; + deadline.tv_nsec = total_ns % 1000000000LL; +#elif defined(PLATFORM_WINDOWS) + // Windows tracks relative intervals natively via clock ticks + ULONGLONG start_tick = GetTickCount64(); +#endif + + // 2. Loop to defend against Spurious Wakeups while the queue is full + while (queue->size == queue->capacity && !queue->is_shutdown) { + if (remaining_ms == 0) { + // Out of time before cond wait or remaining time ticked down to zero + c_Mutex_UnLock(&queue->lock); + return C_FALSE; + } + + // 3. Atomically release the lock and sleep until signaled or timed out +#if defined(PLATFORM_WINDOWS) + // SleepConditionVariableCS handles relative timeout natively + BOOL wait_success = SleepConditionVariableCS(&queue->not_full.handle, &queue->lock.handle, remaining_ms); + + if (!wait_success) { + if (GetLastError() == ERROR_TIMEOUT) { + c_Mutex_UnLock(&queue->lock); + return C_FALSE; // Dynamic Windows timeout expired + } + } + + // Recalculate remaining time in case of a spurious wakeup + ULONGLONG elapsed = GetTickCount64() - start_tick; + if (elapsed >= timeout_ms) { + remaining_ms = 0; + } else { + remaining_ms = timeout_ms - (c_uint_t)elapsed; + } +#elif defined(PLATFORM_POSIX) + // pthread_cond_timedwait takes the absolute calculated deadline + int wait_result = pthread_cond_timedwait(&queue->not_full.handle, &queue->lock.handle, &deadline); + + if (wait_result != 0) { + // POSIX returns ETIMEDOUT (110) if time limit expires + c_Mutex_UnLock(&queue->lock); + return C_FALSE; + } + + // Re-verify remaining time using current clock to stay precise + gettimeofday(&now, NULL); + long long current_ms = (long long)now.tv_sec * 1000 + now.tv_usec / 1000; + long long deadline_ms = (long long)deadline.tv_sec * 1000 + deadline.tv_nsec / 1000000; + if (current_ms >= deadline_ms) { + remaining_ms = 0; + } else { + remaining_ms = (c_uint_t)(deadline_ms - current_ms); + } +#endif + } + + // 4. Handle exit criteria if queue shut down during wait + if (queue->is_shutdown) { + c_Mutex_UnLock(&queue->lock); + return C_FALSE; + } + + // 5. Securely push data into the circular buffer + queue->data[queue->write_idx] = item; + queue->write_idx = (queue->write_idx + 1) % queue->capacity; + queue->size++; + + // 6. Signal blocked consumers that data is ready + c_Cond_Signal(&queue->not_empty); + + c_Mutex_UnLock(&queue->lock); + return C_TRUE; +} + diff --git a/cKit/Foundation/c_LockQueue.h b/cKit/Foundation/c_LockQueue.h new file mode 100644 index 0000000..c9bc72d --- /dev/null +++ b/cKit/Foundation/c_LockQueue.h @@ -0,0 +1,49 @@ +#ifndef INCLUDED_C_LOCKQUEUE_H +#define INCLUDED_C_LOCKQUEUE_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +#ifndef INCLUDED_C_MUTEX_H +#include +#endif /*INCLUDED_C_MUTEX_H*/ + +#ifndef INCLUDED_C_COND_H +#include +#endif /*INCLUDED_C_COND_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + void** data; + c_size_t capacity; + c_size_t write_idx; + c_size_t read_idx; + c_size_t size; + c_bool_t is_shutdown; + + c_Mutex_t lock; + c_Cond_t not_full; + c_Cond_t not_empty; +}c_LockQueue_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_LockQueue_Init(c_LockQueue_t* queue, c_size_t capacity); +void c_LockQueue_Destroy(c_LockQueue_t* queue); +void c_LockQueue_Shutdown(c_LockQueue_t* queue); + +c_err_t c_LockQueue_Push(c_LockQueue_t* queue, void* data); +c_err_t c_LockQueue_Pop(c_LockQueue_t* queue, void** data); +c_bool_t c_LockQueue_TimedPop(c_LockQueue_t* queue, void** item, c_uint_t timeout_ms); +c_bool_t c_LockQueue_TimedPush(c_LockQueue_t* queue, void* item, c_uint_t timeout_ms); +c_size_t c_LockQueue_Size(c_LockQueue_t* queue); + +c_bool_t c_LockQueue_IsEmpty(c_LockQueue_t* queue); +c_bool_t c_LockQueue_IsFull(c_LockQueue_t* queue); + +#endif /*INCLUDED_C_LOCKQUEUE_H*/ diff --git a/cKit/Foundation/c_MinHeap.c b/cKit/Foundation/c_MinHeap.c new file mode 100644 index 0000000..392617b --- /dev/null +++ b/cKit/Foundation/c_MinHeap.c @@ -0,0 +1,177 @@ +#include +#include + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define DEFAULT_INITIAL_CAPACITY 4 + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +// 內部輔助函數:交換兩個元素的記憶體內容 +C_STATIC_FORCE_INLINE +void heap_swap(char* a, char* b, int size) { + #define MAX_STACK_BUF 128 + char stack_buf[MAX_STACK_BUF]; + void* temp = (size <= MAX_STACK_BUF) ? stack_buf : C_ALLOC(size); + + memcpy(temp, a, size); + memcpy(a, b, size); + memcpy(b, temp, size); + + if (temp != stack_buf) C_FREE(temp); +} + +// 內部輔助函數:向上調整,維持最小堆特性 (O(log N)) +C_STATIC_FORCE_INLINE +void heapify_up(c_MinHeap_t* self, c_size_t index) { + while (index > 0) { + c_size_t parent = (index - 1) / 2; + char* curr_ptr = (char*)self->array + (index * self->obj_size); + char* parent_ptr = (char*)self->array + (parent * self->obj_size); + + // 如果當前節點比父節點還小,則向上交換 + if (self->compare(curr_ptr, parent_ptr) < 0) { + heap_swap(curr_ptr, parent_ptr, self->obj_size); + index = parent; + } else { + break; + } + } +} + +// 內部輔助函數:向下調整,維持最小堆特性 (O(log N)) +C_STATIC_FORCE_INLINE +void heapify_down(c_MinHeap_t* self, c_size_t index) { + c_size_t left, right, smallest; + char* smallest_ptr; + char* curr_ptr; + + while (1) { + left = 2 * index + 1; + right = 2 * index + 2; + smallest = index; + smallest_ptr = (char*)self->array + (smallest * self->obj_size); + + // 與左子節點比對優先級 + if (left < self->size) { + char* left_ptr = (char*)self->array + (left * self->obj_size); + if (self->compare(left_ptr, smallest_ptr) < 0) { + smallest = left; + smallest_ptr = left_ptr; + } + } + + // 與右子節點比對優先級 + if (right < self->size) { + char* right_ptr = (char*)self->array + (right * self->obj_size); + if (self->compare(right_ptr, smallest_ptr) < 0) { + smallest = right; + smallest_ptr = right_ptr; + } + } + + // 如果最小元素不是當前節點,交換並繼續向下遞迴 + if (smallest != index) { + curr_ptr = (char*)self->array + (index * self->obj_size); + heap_swap(curr_ptr, smallest_ptr, self->obj_size); + index = smallest; + } else { + break; + } + } +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_MinHeap_Init(c_MinHeap_t* self, int obj_size, c_size_t initial_capacity, c_MinHeap_Compare_f compare) { + if (!self || obj_size <= 0 || !compare) return C_ERR_PARAM; + + self->obj_size = obj_size; + self->capacity = (initial_capacity > 0) ? initial_capacity : DEFAULT_INITIAL_CAPACITY; + self->size = 0; + self->compare = compare; + + self->array = C_ALLOC(self->capacity * self->obj_size); + if (!self->array) { + self->capacity = 0; + return C_ERR_NOMEM; + } + + return C_ERR_SUCCESS; +} + +void c_MinHeap_Destroy(c_MinHeap_t* self) { + if (!self) return; + + C_FREE(self->array); + self->capacity = 0; + self->size = 0; + self->obj_size = 0; +} + + +// 推入元素 (自動擴容) +c_err_t c_MinHeap_Push(c_MinHeap_t* self, const void* obj) { + if (!self || !self->array || !obj) return C_ERR_PARAM; + + // 動態翻倍擴容邏輯 + if (self->size >= self->capacity) { + const c_size_t new_capacity = self->capacity << 1; + void* new_array = C_REALLOC(self->array, new_capacity * self->obj_size); + if (!new_array) return C_ERR_NOMEM; + self->array = new_array; + self->capacity = new_capacity; + } + + // 將新元素放到陣列最尾端 + char* target = (char*)self->array + (self->size * self->obj_size); + memcpy(target, obj, self->obj_size); + self->size++; + + // 向上修復堆平衡 + heapify_up(self, self->size - 1); + return C_ERR_SUCCESS; +} + +// 彈出最小元素:複製頂端資料至使用者緩衝區,並讓最尾端元素補上後向下修正 +c_err_t c_MinHeap_Pop(c_MinHeap_t* self, void* out_obj) { + if (!self || !self->array || !out_obj) return C_ERR_PARAM; + if (self->size == 0) return C_ERR_EMPTY; + + // 1. 複製堆頂端元素 (Index 0) 至呼叫端的自備緩衝區 + memcpy(out_obj, self->array, self->obj_size); + + // 2. 將最後一個元素搬移到堆頂端 + if (self->size > 1) { + char* last_ptr = (char*)self->array + ((self->size - 1) * self->obj_size); + memcpy(self->array, last_ptr, self->obj_size); + } + + self->size--; + + // 3. 向下修復堆平衡 + if (self->size > 1) { + heapify_down(self, 0); + } + + return C_ERR_SUCCESS; +} + +void* c_MinHeap_Peek(c_MinHeap_t* self) { + if (!self || !self->array || self->size == 0) return NULL; + return self->array; // 最小堆的頂端(最小值)永遠在陣列的第一個元素 +} + +c_size_t c_MinHeap_GetSize(const c_MinHeap_t* self) { + if (!self) return 0; + return self->size; +} + +c_bool_t c_MinHeap_IsEmpty(const c_MinHeap_t* self) { + if (!self) return C_TRUE; + return self->size == 0; +} \ No newline at end of file diff --git a/cKit/Foundation/c_MinHeap.h b/cKit/Foundation/c_MinHeap.h new file mode 100644 index 0000000..59c172b --- /dev/null +++ b/cKit/Foundation/c_MinHeap.h @@ -0,0 +1,32 @@ +#ifndef INCLUDED_C_MINHEAP_H +#define INCLUDED_C_MINHEAP_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef int (*c_MinHeap_Compare_f)(const void* a, const void* b); + +// 最小堆控制結構體 +typedef struct { + void* array; // 連續記憶體陣列,以平鋪樹(Array-based Tree)方式儲存節點 + int obj_size; // 每個單個元素的位元組大小 + c_size_t capacity; // 當前緩衝區可容納的最大元素量 + c_size_t size; // 目前堆中已儲存的元素數量 + c_MinHeap_Compare_f compare; // 使用者定義的優先級比較函數 +} c_MinHeap_t; + +c_err_t c_MinHeap_Init(c_MinHeap_t* self, int obj_size, c_size_t initial_capacity, c_MinHeap_Compare_f compare); +void c_MinHeap_Destroy(c_MinHeap_t* self); + +c_err_t c_MinHeap_Push(c_MinHeap_t* self, const void* obj); +c_err_t c_MinHeap_Pop(c_MinHeap_t* self, void* out_obj); +void* c_MinHeap_Peek(c_MinHeap_t* self); +c_size_t c_MinHeap_GetSize(const c_MinHeap_t* self); +c_bool_t c_MinHeap_IsEmpty(const c_MinHeap_t* self); + +#endif /*INCLUDED_C_MINHEAP_H*/ diff --git a/cKit/Foundation/c_MinHeap.t.c b/cKit/Foundation/c_MinHeap.t.c new file mode 100644 index 0000000..46a8120 --- /dev/null +++ b/cKit/Foundation/c_MinHeap.t.c @@ -0,0 +1,99 @@ +#include "c_MinHeap.h" +#include +#include + +typedef struct { + int priority; // 優先權數值(數值越小,代表權重越高、越優先) + char task_name[32]; +} HeapTask_t; + +// 比較回呼函數 +int compare_tasks(const void* a, const void* b) { + return (((HeapTask_t*)a)->priority - ((HeapTask_t*)b)->priority); +} + +void test_log(const char* name) { + printf("[PASS] %s\n", name); +} + +int main(int argc, char** argv) { + printf("==================================================\n"); + printf(" 開始執行 c_MinHeap_t 泛型最小堆單元測試\n"); + printf("==================================================\n\n"); + + c_MinHeap_t heap; + // 初始容量設為 2 驗證後續動態 realloc 擴容 + c_err_t err = c_MinHeap_Init(&heap, sizeof(HeapTask_t), 2, compare_tasks); + assert(err == C_ERR_SUCCESS); + assert(c_MinHeap_IsEmpty(&heap) == C_TRUE); + test_log("1. 最小堆控制結構體與大小配置初始化成功"); + + // 故意以不規則的順序建立任務 + HeapTask_t t1 = {50, "Task_Low"}; + HeapTask_t t2 = {10, "Task_Urgent"}; + HeapTask_t t3 = {30, "Task_Medium"}; + HeapTask_t t4 = {5, "Task_Critical"}; // 最優先 + + // ========================================== + // 2. 測試推入與動態擴容 (Push) + // ========================================== + c_MinHeap_Push(&heap, &t1); + c_MinHeap_Push(&heap, &t2); + // 推入第 3 個元素,此時 size=3 > capacity=2,預期內部會自動動態擴容翻倍為 4 + c_MinHeap_Push(&heap, &t3); + err = c_MinHeap_Push(&heap, &t4); + + assert(err == C_ERR_SUCCESS); + assert(c_MinHeap_GetSize(&heap) == 4); + assert(heap.capacity == 4); // 驗證動態擴容成功 + test_log("2. 隨機元素推入與陣列自動動態擴容驗證成功"); + + // ========================================== + // 3. 測試查看堆頂端 (Peek) + // ========================================== + // 目前權重最小(最優先)的是 priority=5 的 Task_Critical + HeapTask_t* p_peek = (HeapTask_t*)c_MinHeap_Peek(&heap); + assert(p_peek != NULL); + assert(p_peek->priority == 5); + assert(strcmp(p_peek->task_name, "Task_Critical") == 0); + test_log("3. 堆頂端唯讀查看 (Peek) 最優先元素成功"); + + // ========================================== + // 4. 連續彈出優先級檢驗 (Pop - 使用者自備緩衝區) + // ========================================== + HeapTask_t local_buf; + + // 第一次彈出:預期取得 5 + err = c_MinHeap_Pop(&heap, &local_buf); + assert(err == C_ERR_SUCCESS); + assert(local_buf.priority == 5); + assert(strcmp(local_buf.task_name, "Task_Critical") == 0); + + // 第二次彈出:預期取得 10 + c_MinHeap_Pop(&heap, &local_buf); + assert(local_buf.priority == 10); + assert(strcmp(local_buf.task_name, "Task_Urgent") == 0); + + // 第三次彈出:預期取得 30 + c_MinHeap_Pop(&heap, &local_buf); + assert(local_buf.priority == 30); + + // 第四次彈出:預期取得 50 + c_MinHeap_Pop(&heap, &local_buf); + assert(local_buf.priority == 50); + assert(c_MinHeap_IsEmpty(&heap) == C_TRUE); + + // 第五次彈出:此時堆已完全被清空,預期回傳越界錯誤 + err = c_MinHeap_Pop(&heap, &local_buf); + assert(err == C_ERR_EMPTY); + assert(c_MinHeap_Peek(&heap) == NULL); + test_log("5. 連續 Pop 遞增順序驗證與空堆防呆成功"); + + c_MinHeap_Destroy(&heap); + test_log("6. 最小堆資源回收成功"); + + printf("\n==================================================\n"); + printf(" 恭喜!c_MinHeap_t 平鋪陣列結構所有操作單元測試完勝!\n"); + printf("==================================================\n"); + return 0; +} \ No newline at end of file diff --git a/cKit/Foundation/c_Mutex.c b/cKit/Foundation/c_Mutex.c new file mode 100644 index 0000000..434a548 --- /dev/null +++ b/cKit/Foundation/c_Mutex.c @@ -0,0 +1,63 @@ +#include + +c_err_t c_Mutex_Init(c_Mutex_t* mutex) { + if (!mutex) return C_ERR_FAIL; + +#if defined(PLATFORM_WINDOWS) + // InitializeCriticalSection 不会失败,但 InitializeCriticalSectionAndSpinCount 可能会 + // 工业级推荐直接使用此 API,性能优秀 + InitializeCriticalSection(&mutex->handle); + mutex->is_initialized = C_TRUE; + return C_ERR_SUCCESS; +#elif defined(PLATFORM_POSIX) + // POSIX 默认是 PTHREAD_MUTEX_DEFAULT (不可重入锁) + if (pthread_mutex_init(&mutex->handle, NULL) == 0) { + mutex->is_initialized = C_TRUE; + return C_ERR_SUCCESS; + } + return C_ERR_FAIL; +#endif +} + +void c_Mutex_Destroy(c_Mutex_t* mutex) { + if (!mutex || !mutex->is_initialized) return; + +#if defined(PLATFORM_WINDOWS) + DeleteCriticalSection(&mutex->handle); +#elif defined(PLATFORM_POSIX) + pthread_mutex_destroy(&mutex->handle); +#endif + mutex->is_initialized = C_FALSE; +} + + +void c_Mutex_Lock(c_Mutex_t* mutex) { + if (!mutex || !mutex->is_initialized) return; +#if defined(PLATFORM_WINDOWS) + EnterCriticalSection(&mutex->handle); +#elif defined(PLATFORM_POSIX) + pthread_mutex_lock(&mutex->handle); +#endif +} + +c_bool_t c_Mutex_TryLock(c_Mutex_t* mutex) { + if (!mutex || !mutex->is_initialized) return C_FALSE; + +#if defined(PLATFORM_WINDOWS) + // TryEnterCriticalSection 返回非 0 表示成功 + return (TryEnterCriticalSection(&mutex->handle) != 0)?C_TRUE:C_FALSE; +#elif defined(PLATFORM_POSIX) + // pthread_mutex_trylock 返回 0 表示成功 + return (pthread_mutex_trylock(&mutex->handle) == 0)?C_TRUE:C_FALSE; +#endif +} + + +void c_Mutex_UnLock(c_Mutex_t* mutex) { + if (!mutex || !mutex->is_initialized) return; +#if defined(PLATFORM_WINDOWS) + LeaveCriticalSection(&mutex->handle); +#elif defined(PLATFORM_POSIX) + pthread_mutex_unlock(&mutex->handle); +#endif +} \ No newline at end of file diff --git a/cKit/Foundation/c_Mutex.h b/cKit/Foundation/c_Mutex.h new file mode 100644 index 0000000..b21edfb --- /dev/null +++ b/cKit/Foundation/c_Mutex.h @@ -0,0 +1,43 @@ +#ifndef INCLUDED_C_MUTEX_H +#define INCLUDED_C_MUTEX_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#if defined(_WIN32) || defined(_WIN64) + #define PLATFORM_WINDOWS 1 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include +#else + #define PLATFORM_POSIX 1 + #include +#endif + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { +#if defined(PLATFORM_WINDOWS) + CRITICAL_SECTION handle; +#elif defined(PLATFORM_POSIX) + pthread_mutex_t handle; +#endif + c_bool_t is_initialized; +} c_Mutex_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_Mutex_Init(c_Mutex_t* mutex); +void c_Mutex_Destroy(c_Mutex_t* mutex); +void c_Mutex_Lock(c_Mutex_t* mutex); +c_bool_t c_Mutex_TryLock(c_Mutex_t* mutex); +void c_Mutex_UnLock(c_Mutex_t* mutex); + +#endif /*INCLUDED_C_MUTEX_H*/ diff --git a/cKit/Foundation/c_PtrArrayBag.c b/cKit/Foundation/c_PtrArrayBag.c new file mode 100644 index 0000000..6728023 --- /dev/null +++ b/cKit/Foundation/c_PtrArrayBag.c @@ -0,0 +1,64 @@ +#include +#include + +#define DEFAULT_INIT_CAPACITY 4 + +c_err_t c_PtrArrayBag_Init(c_PtrArrayBag_t* self, c_size_t capacity) { + if (!self) return C_ERR_PARAM; + self->capacity = (capacity > 0) ? capacity : DEFAULT_INIT_CAPACITY; + self->size = 0; + self->array = (void**)C_ALLOC(self->capacity * sizeof(void*)); + if (!self->array) { + self->capacity = 0; + return C_ERR_NOMEM; + } + + return C_ERR_SUCCESS; +} + +void c_PtrArrayBag_Destroy(c_PtrArrayBag_t* self) { + if (!self) return; + C_FREE(self->array); + self->capacity = 0; + self->size = 0; +} + +c_err_t c_PtrArrayBag_Add(c_PtrArrayBag_t* self, void* item) { + if (!self || !self->array) return C_ERR_PARAM; + + // Handle dynamic resizing (doubling capacity) + if (self->size >= self->capacity) { + const c_size_t new_capacity = self->capacity << 1; + void** new_array = (void**)C_REALLOC(self->array, new_capacity * sizeof(void*)); + if (!new_array) { + return C_ERR_NOMEM; + } + self->array = new_array; + self->capacity = new_capacity; + } + + self->array[self->size++] = item; + return C_ERR_SUCCESS; +} + +void* c_PtrArrayBag_Get(c_PtrArrayBag_t* self, c_size_t index) { + if (!self || !self->array || index >= self->size) { + return NULL; + } + return self->array[index]; +} + +c_err_t c_PtrArrayBag_Remove(c_PtrArrayBag_t* self, c_size_t index) { + if (!self || !self->array) return C_ERR_PARAM; + if (index >= self->size) return C_ERR_INDEX; + + // Shift elements left to fill the gap + if (index < self->size - 1) { + const c_size_t elements_to_move = self->size - index - 1; + memmove(&self->array[index], &self->array[index + 1], elements_to_move * sizeof(void*)); + } + + self->size--; + self->array[self->size] = NULL; // Optional clear for safety + return C_ERR_SUCCESS; +} diff --git a/cKit/Foundation/c_PtrArrayBag.h b/cKit/Foundation/c_PtrArrayBag.h new file mode 100644 index 0000000..57b0421 --- /dev/null +++ b/cKit/Foundation/c_PtrArrayBag.h @@ -0,0 +1,28 @@ +#ifndef INCLUDED_C_PTRARRAYBAG_H +#define INCLUDED_C_PTRARRAYBAG_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + void** array; + c_size_t capacity; + c_size_t size; +}c_PtrArrayBag_t; + +c_err_t c_PtrArrayBag_Init(c_PtrArrayBag_t* self, c_size_t capacity); + +void c_PtrArrayBag_Destroy(c_PtrArrayBag_t* self); + +c_err_t c_PtrArrayBag_Add(c_PtrArrayBag_t* self, void* item); + +void* c_PtrArrayBag_Get(c_PtrArrayBag_t* self, c_size_t index); + +c_err_t c_PtrArrayBag_Remove(c_PtrArrayBag_t* self, c_size_t index); + +#endif /*INCLUDED_C_PTRARRAYBAG_H*/ diff --git a/cKit/Foundation/c_PtrArrayBag.t.c b/cKit/Foundation/c_PtrArrayBag.t.c new file mode 100644 index 0000000..17ad3ac --- /dev/null +++ b/cKit/Foundation/c_PtrArrayBag.t.c @@ -0,0 +1,97 @@ +#include "c_PtrArrayBag.h" +#include +#include + +// 輔助測試函數:印出測試進度 +void test_log(const char* test_name) { + printf("[PASS] %s\n", test_name); +} + +int main(int argc, char** argv){ + + printf("開始執行 c_PtrArrayBag 測試用例...\n\n"); + + // 模擬一些測試資料 + int val1 = 100; + int val2 = 200; + int val3 = 300; + int val4 = 400; + + // ========================================== + // 1. 測試初始化 (Init) + // ========================================== + c_PtrArrayBag_t bag; + c_err_t err = c_PtrArrayBag_Init(&bag, 2); // 故意設小容量測試動態擴容 + assert(err == C_ERR_SUCCESS); + assert(bag.capacity == 2); + assert(bag.size == 0); + assert(bag.array != NULL); + test_log("初始化測試"); + + // ========================================== + // 2. 測試新增元素與基本讀取 (Add & Get) + // ========================================== + err = c_PtrArrayBag_Add(&bag, &val1); + assert(err == C_ERR_SUCCESS); + assert(bag.size == 1); + assert(*(int*)c_PtrArrayBag_Get(&bag, 0) == 100); + + err = c_PtrArrayBag_Add(&bag, &val2); + assert(err == C_ERR_SUCCESS); + assert(bag.size == 2); + assert(*(int*)c_PtrArrayBag_Get(&bag, 1) == 200); + test_log("基本新增與讀取測試"); + + // ========================================== + // 3. 測試動態擴容 (Dynamic Resizing) + // ========================================== + // 目前 size = 2, capacity = 2。再加第 3 個元素應該要觸發容量翻倍 + err = c_PtrArrayBag_Add(&bag, &val3); + assert(err == C_ERR_SUCCESS); + assert(bag.size == 3); + assert(bag.capacity == 4); // 2 * 2 = 4 + assert(*(int*)c_PtrArrayBag_Get(&bag, 2) == 300); + test_log("自動擴容測試"); + + // ========================================== + // 4. 測試邊界與無效引數 (Edge Cases) + // ========================================== + // 讀取超出範圍的索引應該回傳 NULL + assert(c_PtrArrayBag_Get(&bag, 99) == NULL); + + // 傳入 NULL 結構指標應該要防呆 + assert(c_PtrArrayBag_Init(NULL, 10) == C_ERR_PARAM); + assert(c_PtrArrayBag_Add(NULL, &val4) == C_ERR_PARAM); + assert(c_PtrArrayBag_Remove(NULL, 0) == C_ERR_PARAM); + test_log("邊界防呆測試"); + + // ========================================== + // 5. 測試刪除元素與平移 (Remove) + // ========================================== + // 目前狀態: [100, 200, 300],刪除索引 1 (200) + // 預期結果: [100, 300],後方元素向前平移,保持順序 + err = c_PtrArrayBag_Remove(&bag, 1); + assert(err == C_ERR_SUCCESS); + assert(bag.size == 2); + + // 驗證原本索引 2 的 300 是否變成索引 1 + assert(*(int*)c_PtrArrayBag_Get(&bag, 0) == 100); + assert(*(int*)c_PtrArrayBag_Get(&bag, 1) == 300); + + // 嘗試刪除不存在的索引 + err = c_PtrArrayBag_Remove(&bag, 5); + assert(err == C_ERR_INDEX); + test_log("刪除與元素平移測試"); + + // ========================================== + // 6. 測試銷毀與記憶體釋放 (Destroy) + // ========================================== + c_PtrArrayBag_Destroy(&bag); + assert(bag.array == NULL); + assert(bag.size == 0); + assert(bag.capacity == 0); + test_log("銷毀測試"); + + printf("\n恭喜!所有測試用例皆順利通過 (All Tests Passed)!\n"); + return 0; +} diff --git a/cKit/Foundation/c_PtrLinkBag.c b/cKit/Foundation/c_PtrLinkBag.c new file mode 100644 index 0000000..04352f6 --- /dev/null +++ b/cKit/Foundation/c_PtrLinkBag.c @@ -0,0 +1,65 @@ +#include +#include + +c_err_t c_PtrLinkBag_Init(c_PtrLinkBag_t* self) { + if (!self) return C_ERR_PARAM; + self->head= NULL; + return C_ERR_SUCCESS; +} + +void c_PtrLinkBag_Destroy(c_PtrLinkBag_t* self) { + if (!self) return; + c_PtrLinkBagNode_t* p = self->head; + while (p) { + c_PtrLinkBagNode_t* q = p->next; + C_FREE(p); + p = q; + } + self->head = NULL; +} + +c_err_t c_PtrLinkBag_Add(c_PtrLinkBag_t* self, void* item) { + if (!self) return C_ERR_PARAM; + c_PtrLinkBagNode_t* p; + C_NEW(p); + if (!p) { + return C_ERR_NOMEM; + } + p->ptr = item; + p->next = self->head; + self->head = p; + return C_ERR_SUCCESS; +} + +c_err_t c_PtrLinkBag_Remove(c_PtrLinkBag_t* self, const void* item) { + if (!self) return C_ERR_PARAM; + c_PtrLinkBagNode_t** curr = &self->head; + while (*curr != NULL) { + if ((*curr)->ptr == item) { + c_PtrLinkBagNode_t* entry = *curr; + *curr = entry->next; + C_FREE(entry); + return C_ERR_SUCCESS; + } + curr = &(*curr)->next; + } + + return C_ERR_NOT_FOUND; +} + +void c_PtrLinkBagIter_Remove(c_PtrLinkBagIter_t* self) { + // 防呆檢查:確保迭代器有效,且當前指向的節點不為空 + if (!self || !self->node || !*(self->node)) return; + + c_PtrLinkBagNode_t* to_delete = *(self->node); + + // 將當前結構中維護的指標(可能是上一節點的 next,或是 bag 的 head) + // 修改為指向下一個節點,直接從鏈結串列中斷開 + *(self->node) = to_delete->next; + + // 釋放記憶體 + C_FREE(to_delete); + + // 注意:此時 self->node 自動更新指向了原本的下一個節點 + // 使用者不需要再呼叫 Next(),即可直接對新節點進行 Get() 或再次 Remove() +} diff --git a/cKit/Foundation/c_PtrLinkBag.h b/cKit/Foundation/c_PtrLinkBag.h new file mode 100644 index 0000000..bec5185 --- /dev/null +++ b/cKit/Foundation/c_PtrLinkBag.h @@ -0,0 +1,63 @@ +#ifndef INCLUDED_C_PTRLINKBAG_H +#define INCLUDED_C_PTRLINKBAG_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct c_PtrLinkBagNode_t { + void* ptr; + struct c_PtrLinkBagNode_t* next; +}c_PtrLinkBagNode_t; + +typedef struct { + c_PtrLinkBagNode_t* head; +}c_PtrLinkBag_t; + +typedef struct { + c_PtrLinkBag_t* bag; + c_PtrLinkBagNode_t** node; +}c_PtrLinkBagIter_t; + +c_err_t c_PtrLinkBag_Init(c_PtrLinkBag_t* self); + +void c_PtrLinkBag_Destroy(c_PtrLinkBag_t* self); + +c_err_t c_PtrLinkBag_Add(c_PtrLinkBag_t* self, void* item); + +c_err_t c_PtrLinkBag_Remove(c_PtrLinkBag_t* self, const void* item); + +C_STATIC_FORCE_INLINE +void c_PtrLinkBagIter_Init(c_PtrLinkBagIter_t* self, c_PtrLinkBag_t* bag) { + if (!self || !bag) return; + self->bag = bag; + self->node = &bag->head; +} + +C_STATIC_FORCE_INLINE +c_bool_t c_PtrLinkBagIter_HasNext(c_PtrLinkBagIter_t* self) { + if (!self) return C_FALSE; + return (self->node!=NULL) && (*(self->node)!=NULL); +} + +C_STATIC_FORCE_INLINE +void* c_PtrLinkBagIter_Next(c_PtrLinkBagIter_t* self) { + if (!self || !self->node) return NULL; + void* ptr = (*(self->node))->ptr; + self->node = &(*(self->node))->next; + return ptr; +} + +C_STATIC_FORCE_INLINE +void* c_PtrLinkBagIter_Get(c_PtrLinkBagIter_t* self) { + if (!self || !self->node) return NULL; + return (*(self->node))->ptr; +} + +void c_PtrLinkBagIter_Remove(c_PtrLinkBagIter_t* self); + +#endif /*INCLUDED_C_PTRLINKBAG_H*/ diff --git a/cKit/Foundation/c_PtrLinkBag.t.c b/cKit/Foundation/c_PtrLinkBag.t.c new file mode 100644 index 0000000..9671490 --- /dev/null +++ b/cKit/Foundation/c_PtrLinkBag.t.c @@ -0,0 +1,60 @@ +#include "c_PtrLinkBag.h" +#include +#include + +int main(int argc, char** argv){ + printf("開始執行 c_PtrLinkBag 測試...\n"); + + c_PtrLinkBag_t bag; + c_PtrLinkBag_Init(&bag); + + int v1 = 10, v2 = 20, v3 = 30; + + // 1. 測試新增 (使用頭插法,順序會是 30 -> 20 -> 10) + c_PtrLinkBag_Add(&bag, &v1); + c_PtrLinkBag_Add(&bag, &v2); + c_PtrLinkBag_Add(&bag, &v3); + + // 2. 測試走訪 + c_PtrLinkBagIter_t iter; + c_PtrLinkBagIter_Init(&iter, &bag); + + printf("目前鏈結串列內容: "); + while (c_PtrLinkBagIter_HasNext(&iter)) { + int* val = (int*)c_PtrLinkBagIter_Next(&iter); + printf("%d ", *val); + } + printf("\n"); + + // 3. 測試在迭代過程中刪除特定元素 (例如刪除 20) + c_PtrLinkBagIter_Init(&iter, &bag); + while (c_PtrLinkBagIter_HasNext(&iter)) { + int* val = (int*)c_PtrLinkBagIter_Get(&iter); + if (*val == 20) { + c_PtrLinkBagIter_Remove(&iter); // 刪除 20,iter->node 自動指向 10 + printf("[Log] 迭代器成功刪除了 20\n"); + } else { + c_PtrLinkBagIter_Next(&iter); // 沒刪除時才手動前進 + } + } + + // 4. 驗證刪除後的背包內容 (預期只剩 30 -> 10) + c_PtrLinkBagIter_Init(&iter, &bag); + assert(*(int*)c_PtrLinkBagIter_Next(&iter) == 30); + assert(*(int*)c_PtrLinkBagIter_Next(&iter) == 10); + assert(c_PtrLinkBagIter_HasNext(&iter) == C_FALSE); + + // 5. 測試一般刪除 (Remove) + c_err_t err = c_PtrLinkBag_Remove(&bag, &v3); + assert(err == C_ERR_SUCCESS); + + // 檢查是不是只剩 10 + c_PtrLinkBagIter_Init(&iter, &bag); + assert(*(int*)c_PtrLinkBagIter_Get(&iter) == 10); + + // 清除記憶體 + c_PtrLinkBag_Destroy(&bag); + printf("所有測試成功通過!\n"); + + return 0; +} diff --git a/cKit/Foundation/c_RBTree.c b/cKit/Foundation/c_RBTree.c new file mode 100644 index 0000000..9d82e21 --- /dev/null +++ b/cKit/Foundation/c_RBTree.c @@ -0,0 +1,320 @@ +#include +#include + +// Internal Helper: Allocates structural payload node properties +C_STATIC_FORCE_INLINE +c_RBTreeNode_t* create_node(c_RBTree_t* self, void* obj) { + int size = (int)sizeof(c_RBTreeNode_t) + self->obj_size; + size = C_ALIGN_UPB(size, C_ALIGN_SIZE); + + c_RBTreeNode_t* node = (c_RBTreeNode_t*)C_ALLOC(size); + if (!node) return NULL; + node->data = node + 1; + + memcpy(node->data, obj, self->obj_size); + node->left = self->nil; + node->right = self->nil; + node->parent = self->nil; + node->color = C_RBTREE_RED; + return node; +} + + +static void destroy_recursive(c_RBTree_t* self, c_RBTreeNode_t* node) { + if (node == self->nil || node == NULL) return; + destroy_recursive(self, node->left); + destroy_recursive(self, node->right); + C_FREE(node); +} + + +// Tree Rotation Helpers +C_STATIC_FORCE_INLINE +void left_rotate(c_RBTree_t* self, c_RBTreeNode_t* x) { + c_RBTreeNode_t* y = x->right; + x->right = y->left; + if (y->left != self->nil) y->left->parent = x; + y->parent = x->parent; + if (x->parent == self->nil) self->root = y; + else if (x == x->parent->left) x->parent->left = y; + else x->parent->right = y; + y->left = x; + x->parent = y; +} + +C_STATIC_FORCE_INLINE +void right_rotate(c_RBTree_t* self, c_RBTreeNode_t* y) { + c_RBTreeNode_t* x = y->left; + y->left = x->right; + if (x->right != self->nil) x->right->parent = y; + x->parent = y->parent; + if (y->parent == self->nil) self->root = x; + else if (y == y->parent->right) y->parent->right = x; + else y->parent->left = x; + x->right = y; + y->parent = x; +} + +// Balance adjustments post standard insert passes +static void insert_fixup(c_RBTree_t* self, c_RBTreeNode_t* z) { + while (z->parent->color == C_RBTREE_RED) { + if (z->parent == z->parent->parent->left) { + c_RBTreeNode_t* y = z->parent->parent->right; + if (y->color == C_RBTREE_RED) { + z->parent->color = C_RBTREE_BLACK; + y->color = C_RBTREE_BLACK; + z->parent->parent->color = C_RBTREE_RED; + z = z->parent->parent; + } else { + if (z == z->parent->right) { + z = z->parent; + left_rotate(self, z); + } + z->parent->color = C_RBTREE_BLACK; + z->parent->parent->color = C_RBTREE_RED; + right_rotate(self, z->parent->parent); + } + } else { + c_RBTreeNode_t* y = z->parent->parent->left; + if (y->color == C_RBTREE_RED) { + z->parent->color = C_RBTREE_BLACK; + y->color = C_RBTREE_BLACK; + z->parent->parent->color = C_RBTREE_RED; + z = z->parent->parent; + } else { + if (z == z->parent->left) { + z = z->parent; + right_rotate(self, z); + } + z->parent->color = C_RBTREE_BLACK; + z->parent->parent->color = C_RBTREE_RED; + left_rotate(self, z->parent->parent); + } + } + } + self->root->color = C_RBTREE_BLACK; +} + + +static void inorder_recursive(c_RBTree_t* self, c_RBTreeNode_t* node, c_RBTree_Visit_f visit, void* cl) { + if (node == self->nil || node == NULL) return; + inorder_recursive(self, node->left, visit, cl); + visit(node->data, cl); + inorder_recursive(self, node->right, visit, cl); +} + +C_STATIC_FORCE_INLINE +void rb_transplant(c_RBTree_t* self, c_RBTreeNode_t* u, c_RBTreeNode_t* v) { + if (u->parent == self->nil) { + self->root = v; + } else if (u == u->parent->left) { + u->parent->left = v; + } else { + u->parent->right = v; + } + v->parent = u->parent; +} + +// 內部輔助函數:尋找子樹中的最小節點(用於刪除時尋找後繼節點) +C_STATIC_FORCE_INLINE +c_RBTreeNode_t* rb_tree_minimum(c_RBTree_t* self, c_RBTreeNode_t* node) { + while (node->left != self->nil) { + node = node->left; + } + return node; +} + +static void remove_fixup(c_RBTree_t* self, c_RBTreeNode_t* x) { + while (x != self->root && x->color == C_RBTREE_BLACK) { + if (x == x->parent->left) { + c_RBTreeNode_t* w = x->parent->right; // x 的兄弟節點 + + // 狀況 1:兄弟節點 w 是紅色 + if (w->color == C_RBTREE_RED) { + w->color = C_RBTREE_BLACK; + x->parent->color = C_RBTREE_RED; + left_rotate(self, x->parent); + w = x->parent->right; + } + + // 狀況 2:兄弟節點 w 是黑色,且其兩個子節點也都是黑色 + if (w->left->color == C_RBTREE_BLACK && w->right->color == C_RBTREE_BLACK) { + w->color = C_RBTREE_RED; + x = x->parent; + } else { + // 狀況 3:兄弟節點 w 是黑色,w 的右子是黑色,左子是紅色 + if (w->right->color == C_RBTREE_BLACK) { + w->left->color = C_RBTREE_BLACK; + w->color = C_RBTREE_RED; + right_rotate(self, w); + w = x->parent->right; + } + // 狀況 4:兄弟節點 w 是黑色,且 w 的右子是紅色 + w->color = x->parent->color; + x->parent->color = C_RBTREE_BLACK; + w->right->color = C_RBTREE_BLACK; + left_rotate(self, x->parent); + x = self->root; // 結束循環 + } + } else { + // 對稱狀況:x 是其父節點的右子 + c_RBTreeNode_t* w = x->parent->left; + if (w->color == C_RBTREE_RED) { + w->color = C_RBTREE_BLACK; + x->parent->color = C_RBTREE_RED; + right_rotate(self, x->parent); + w = x->parent->left; + } + if (w->right->color == C_RBTREE_BLACK && w->left->color == C_RBTREE_BLACK) { + w->color = C_RBTREE_RED; + x = x->parent; + } else { + if (w->left->color == C_RBTREE_BLACK) { + w->right->color = C_RBTREE_BLACK; + w->color = C_RBTREE_RED; + left_rotate(self, w); + w = x->parent->left; + } + w->color = x->parent->color; + x->parent->color = C_RBTREE_BLACK; + w->left->color = C_RBTREE_BLACK; + right_rotate(self, x->parent); + x = self->root; + } + } + } + x->color = C_RBTREE_BLACK; +} + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +c_err_t c_RBTree_Init(c_RBTree_t* self, int obj_size, c_RBTree_Compare_f compare) { + if (!self || obj_size <= 0 || !compare) return C_ERR_PARAM; + + // Allocate an explicit shared Sentinel NIL node boundary properties + self->nil = (c_RBTreeNode_t*)C_ALLOC(sizeof(c_RBTreeNode_t)); + if (!self->nil) return C_ERR_NOMEM; + self->nil->data = NULL; + self->nil->color = C_RBTREE_BLACK; + self->nil->left = NULL; + self->nil->right = NULL; + self->nil->parent = NULL; + + self->root = self->nil; + self->obj_size = obj_size; + self->size = 0; + self->compare = compare; + return C_ERR_SUCCESS; +} + +void c_RBTree_Destroy(c_RBTree_t* self) { + if (!self) return; + destroy_recursive(self, self->root); + C_FREE(self->nil); + self->root = NULL; + self->size = 0; +} + + +c_err_t c_RBTree_Insert(c_RBTree_t* self, void* obj) { + if (!self || !obj) return C_ERR_PARAM; + + c_RBTreeNode_t* y = self->nil; + c_RBTreeNode_t* x = self->root; + int cmp = 0; + + while (x != self->nil) { + y = x; + cmp = self->compare(obj, x->data); + if (cmp == 0) return C_ERR_ALREADY_EXISTS; // Unique constraints protection + else if (cmp < 0) x = x->left; + else x = x->right; + } + + c_RBTreeNode_t* z = create_node(self, obj); + if (!z) return C_ERR_NOMEM; + z->parent = y; + + if (y == self->nil) self->root = z; + else if (self->compare(z->data, y->data) < 0) y->left = z; + else y->right = z; + + insert_fixup(self, z); + self->size++; + return C_ERR_SUCCESS; +} + +void* c_RBTree_Find(c_RBTree_t* self, const void* key_target) { + if (!self || !key_target) return NULL; + c_RBTreeNode_t* x = self->root; + while (x != self->nil) { + int cmp = self->compare(key_target, x->data); + if (cmp == 0) return x->data; + x = (cmp < 0) ? x->left : x->right; + } + return NULL; +} + +void c_RBTree_InOrder(c_RBTree_t* self, c_RBTree_Visit_f visit, void* cl) { + if (!self || !visit) return; + inorder_recursive(self, self->root, visit, cl); +} + +c_err_t c_RBTree_Remove(c_RBTree_t* self, void* obj) { + if (!self || !obj) return C_ERR_PARAM; + + // 1. 先尋找目標節點是否存在 + c_RBTreeNode_t* z = self->root; + while (z != self->nil) { + int cmp = self->compare(obj, z->data); + if (cmp == 0) break; + z = (cmp < 0) ? z->left : z->right; + } + + if (z == self->nil) return C_ERR_NOT_FOUND; // 節點不存在 + + c_RBTreeNode_t* y = z; + c_RBTreeNode_t* x; + c_RBTreeColor_t y_original_color = y->color; + + // 2. 執行標準二元搜尋樹刪除與移植 + if (z->left == self->nil) { + x = z->right; + rb_transplant(self, z, z->right); + } else if (z->right == self->nil) { + x = z->left; + rb_transplant(self, z, z->left); + } else { + // z 有兩個子節點,尋找其右子樹的最小節點作為後繼者 y + y = rb_tree_minimum(self, z->right); + y_original_color = y->color; + x = y->right; + + if (y->parent == z) { + x->parent = y; // 如果 y 剛好是 z 的直接右子,建立與 nil 的 parent 關係 + } else { + rb_transplant(self, y, y->right); + y->right = z->right; + y->right->parent = y; + } + + rb_transplant(self, z, y); + y->left = z->left; + y->left->parent = y; + y->color = z->color; + } + + // 釋放被刪除節點的記憶體 + C_FREE(z); + self->size--; + + // 3. 如果失去的節點顏色是黑色,會破壞黑高平衡,必須呼叫修復狀態機 + if (y_original_color == C_RBTREE_BLACK) { + remove_fixup(self, x); + } + + return C_ERR_SUCCESS; +} diff --git a/cKit/Foundation/c_RBTree.h b/cKit/Foundation/c_RBTree.h new file mode 100644 index 0000000..501c54f --- /dev/null +++ b/cKit/Foundation/c_RBTree.h @@ -0,0 +1,48 @@ +#ifndef INCLUDED_C_RBTREE_H +#define INCLUDED_C_RBTREE_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef enum { + C_RBTREE_RED, + C_RBTREE_BLACK +} c_RBTreeColor_t; + +typedef struct c_RBTreeNode_t { + void* data; + c_RBTreeColor_t color; + struct c_RBTreeNode_t* left; + struct c_RBTreeNode_t* right; + struct c_RBTreeNode_t* parent; +} c_RBTreeNode_t; + +// User Comparison Callback Signature: Returns <0 if a < b, 0 if a == b, >0 if a > b +typedef int (*c_RBTree_Compare_f)(const void* a, const void* b); + +// User Visitor Callback Signature for Traversals +typedef void (*c_RBTree_Visit_f)(void* data, void* cl); + +typedef struct { + c_RBTreeNode_t* root; + c_RBTreeNode_t* nil; // Sentinal node representing leaf nodes to simplify rotation math + int obj_size; + c_size_t size; + c_RBTree_Compare_f compare; +} c_RBTree_t; + +c_err_t c_RBTree_Init(c_RBTree_t* self, int obj_size, c_RBTree_Compare_f compare); +void c_RBTree_Destroy(c_RBTree_t* self); + +c_err_t c_RBTree_Insert(c_RBTree_t* self, void* obj); +c_err_t c_RBTree_Remove(c_RBTree_t* self, void* obj); +void* c_RBTree_Find(c_RBTree_t* self, const void* key_target); + +void c_RBTree_InOrder(c_RBTree_t* self, c_RBTree_Visit_f visit, void* cl); + + +#endif /*INCLUDED_C_RBTREE_H*/ diff --git a/cKit/Foundation/c_RBTree.t.c b/cKit/Foundation/c_RBTree.t.c new file mode 100644 index 0000000..30b62ba --- /dev/null +++ b/cKit/Foundation/c_RBTree.t.c @@ -0,0 +1,92 @@ +#include "c_RBTree.h" +#include +#include + + +typedef struct { + int id; + char name[32]; +} Task_t; + +// 比較回呼函數 +int compare_tasks(const void* a, const void* b) { + return (((Task_t*)a)->id - ((Task_t*)b)->id); +} + +// 走訪時驗證遞增順序 +int g_last_id = -1; +void visit_verify(void* data, void* cl) { + Task_t* t = (Task_t*)data; + assert(t->id > g_last_id); // 必須嚴格遞增 + g_last_id = t->id; +} + +void test_log(const char* name) { + printf("[PASS] %s\n", name); +} + +int main() { + printf("==================================================\n"); + printf(" 開始執行 c_RBTree (含 O(log N) Remove) 最終整合測試\n"); + printf("==================================================\n\n"); + + c_RBTree_t tree; + c_RBTree_Init(&tree, sizeof(Task_t), compare_tasks); + + Task_t tasks[] = { + {40, "Task_40"}, {20, "Task_20"}, {60, "Task_60"}, + {10, "Task_10"}, {30, "Task_30"}, {50, "Task_50"}, {70, "Task_70"} + }; + + // 1. 批次推入資料 + for (int i = 0; i < 7; i++) { + c_RBTree_Insert(&tree, &tasks[i]); + } + assert(tree.size == 7); + test_log("1. 批次推入 7 筆資料成功"); + + // ========================================== + // 2. 測試刪除葉子節點 (Task 10) + // ========================================== + Task_t target = {10, ""}; + c_err_t err = c_RBTree_Remove(&tree, &target); + assert(err == C_ERR_SUCCESS); + assert(tree.size == 6); + assert(c_RBTree_Find(&tree, &target) == NULL); // 預期找不到 + + // 驗證刪除後的結構順序性 + g_last_id = -1; + c_RBTree_InOrder(&tree, visit_verify, NULL); + test_log("2. 成功刪除葉子節點 (10) 且中序走訪維持平衡有序"); + + // ========================================== + // 3. 測試刪除擁有多個子節點的核心根節點 (Task 40) + // ========================================== + target.id = 40; + err = c_RBTree_Remove(&tree, &target); + assert(err == C_ERR_SUCCESS); + assert(tree.size == 5); + assert(c_RBTree_Find(&tree, &target) == NULL); + + // 再次驗證刪除後的結構順序性 + g_last_id = -1; + c_RBTree_InOrder(&tree, visit_verify, NULL); + test_log("3. 成功刪除雙子核心節點 (40) 且平衡移植狀態正確"); + + // ========================================== + // 4. 刪除不存在的鍵值與無效參數防呆 + // ========================================== + target.id = 999; + assert(c_RBTree_Remove(&tree, &target) == C_ERR_NOT_FOUND); + assert(c_RBTree_Remove(NULL, &target) == C_ERR_PARAM); + test_log("4. 刪除越界與無效引數防呆驗證成功"); + + c_RBTree_Destroy(&tree); + test_log("5. 紅黑樹資源徹底銷毀成功"); + + printf("\n==================================================\n"); + printf(" 恭喜!包含 O(log N) Remove 在內的所有紅黑樹單元測試完美通過!\n"); + printf("==================================================\n"); + + return 0; +} \ No newline at end of file diff --git a/cKit/Foundation/c_Stopwatch.c b/cKit/Foundation/c_Stopwatch.c new file mode 100644 index 0000000..85f2b9f --- /dev/null +++ b/cKit/Foundation/c_Stopwatch.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Foundation/c_Stopwatch.h b/cKit/Foundation/c_Stopwatch.h new file mode 100644 index 0000000..0184b46 --- /dev/null +++ b/cKit/Foundation/c_Stopwatch.h @@ -0,0 +1,179 @@ +#ifndef INCLUDED_C_STOPWATCH_H +#define INCLUDED_C_STOPWATCH_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +#if defined(_WIN32) || defined(_WIN64) + #include + typedef LARGE_INTEGER c_TimePoint_t; +#else +#include +#include +typedef struct timespec c_TimePoint_t; +#endif + +typedef struct { + c_TimePoint_t start_time; + double elapsed_milliseconds; + c_bool_t is_running; +} c_Stopwatch_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * Internal private helper: Captures the system's hardware clock monotonic timestamp. + */ +C_STATIC_FORCE_INLINE +void c_Stopwatch_GetTimePoint(c_TimePoint_t* tp) { +#if defined(_WIN32) || defined(_WIN64) + QueryPerformanceCounter(tp); +#else + // Using CLOCK_MONOTONIC to guarantee safety against system time changes/NTP adjustments + clock_gettime(CLOCK_MONOTONIC, tp); +#endif +} + +/** + * Internal private helper: Computes the difference in seconds between two points. + */ +C_STATIC_FORCE_INLINE +double c_Stopwatch_ComputeDiffInS(const c_TimePoint_t* start, const c_TimePoint_t* end) { +#if defined(_WIN32) || defined(_WIN64) + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + return (double)(end->QuadPart - start->HighPart) / (double)freq.QuadPart; +#else + double start_sec = (double)start->tv_sec + (double)start->tv_nsec / 1e9; + double end_sec = (double)end->tv_sec + (double)end->tv_nsec / 1e9; + return end_sec - start_sec; +#endif +} + +/** + * Calculate the delta time in milliseconds between two distinct time points. + * Time Complexity: O(1) | Auxiliary Space: O(1) + * @param start Pointer to the starting time point timestamp. + * @param end Pointer to the ending time point timestamp. + * @return The double precision scalar difference value in milliseconds, + * or -1.0 if any parameter pointer is NULL. + */ +C_STATIC_FORCE_INLINE +double c_Stopwatch_ComputeDiffInMS(const c_TimePoint_t* start, const c_TimePoint_t* end) { + if (start == NULL || end == NULL) return -1.0; + +#if defined(_WIN32) || defined(_WIN64) + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + // Convert to seconds first, then scale up to milliseconds + double seconds = (double)(end->QuadPart - start->QuadPart) / (double)freq.QuadPart; + return seconds * 1000.0; +#else + double start_ms = ((double)start->tv_sec * 1000.0) + ((double)start->tv_nsec / 1e6); + double end_ms = ((double)end->tv_sec * 1000.0) + ((double)end->tv_nsec / 1e6); + return end_ms - start_ms; +#endif +} + +/** + * Initialize the Stopwatch. Registers parameters to default states cleanly. + */ +C_STATIC_FORCE_INLINE +c_err_t c_Stopwatch_Init(c_Stopwatch_t* sw) { + if (sw == NULL) return C_ERR_PARAM; + + sw->elapsed_milliseconds = 0.0; + sw->is_running = C_FALSE; + memset(&sw->start_time, 0, sizeof(c_TimePoint_t)); + + return C_ERR_OK; +} + +/** + * Start or resume tracking time. + */ +C_STATIC_FORCE_INLINE +c_err_t c_Stopwatch_Start(c_Stopwatch_t* sw) { + if (sw == NULL) return C_ERR_PARAM; + if (sw->is_running) return C_ERR_OK; // Safe skip if already processing + + c_Stopwatch_GetTimePoint(&sw->start_time); + sw->is_running = C_TRUE; + + return C_ERR_OK; +} + +/** + * Stop tracking time and cache the elapsed segment into the accumulator. + */ +C_STATIC_FORCE_INLINE +c_err_t c_Stopwatch_Stop(c_Stopwatch_t* sw) { + if (sw == NULL) return C_ERR_PARAM; + if (!sw->is_running) return C_ERR_OK; + + c_TimePoint_t end_time; + c_Stopwatch_GetTimePoint(&end_time); + + // Add the delta directly to the millisecond buffer field + sw->elapsed_milliseconds += c_Stopwatch_ComputeDiffInMS(&sw->start_time, &end_time); + sw->is_running = C_FALSE; + + return C_ERR_OK; +} + +/** + * Soft Reset: Blasts elapsed counts down to zero while preserving the active running state. + */ +C_STATIC_FORCE_INLINE +c_err_t c_Stopwatch_Clear(c_Stopwatch_t* sw) { + if (sw == NULL) return C_ERR_PARAM; + + sw->elapsed_milliseconds = 0.0; + if (sw->is_running) { + c_Stopwatch_GetTimePoint(&sw->start_time); // Re-anchor start time mark to prevent jumps + } + + return C_ERR_OK; +} + + +/** + * Utility helper: Extract elapsed timing down to millisecond intervals. + */ +C_STATIC_FORCE_INLINE +double c_Stopwatch_GetElapsedMilliseconds(const c_Stopwatch_t* sw) { + if (sw == NULL) return 0.0; + if (!sw->is_running) return sw->elapsed_milliseconds; // Pure O(1) cache read + + c_TimePoint_t active_tick; + c_Stopwatch_GetTimePoint(&active_tick); + + // Dynamically add current active delta segment to the base accumulator + return sw->elapsed_milliseconds + c_Stopwatch_ComputeDiffInMS(&sw->start_time, &active_tick); +} + + +/** + * Extract total measured elapsed time in seconds up to this exact moment. + * Works perfectly whether the stopwatch is running or stopped (Lap peeking feature). + * Time Complexity: O(1) | Auxiliary Space: O(1) in-place + * @param sw Pointer to the constant stopwatch instance context. + * @return The double precision scalar elapsed time value in seconds, + * or 0.0 if the stopwatch instance handle is NULL. + */ +C_STATIC_FORCE_INLINE +double c_Stopwatch_GetElapsedSeconds(const c_Stopwatch_t* sw) { + if (sw == NULL) return 0.0; + + // Leverage the existing GetElapsedMilliseconds API and scale it down to second precision. + // This maintains perfect abstraction layer unity without duplicating clock read conditions. + return c_Stopwatch_GetElapsedMilliseconds(sw) / 1000.0; +} + +#endif /*INCLUDED_C_STOPWATCH_H*/ diff --git a/cKit/Foundation/c_Stopwatch.t.c b/cKit/Foundation/c_Stopwatch.t.c new file mode 100644 index 0000000..69c0f77 --- /dev/null +++ b/cKit/Foundation/c_Stopwatch.t.c @@ -0,0 +1,132 @@ +#include "c_Stopwatch.h" +#include +#include + +#include +#include +#include +#include + +// Your updated line tracing diagnostic macro +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \ + return C_FALSE; \ + } \ + } while(0) + +// Helper macro for double comparisons with floating-point tolerance +#define EXPECT_NEAR(actual, expected, tolerance, msg) \ + do { \ + if (fabs((actual) - (expected)) > (tolerance)) { \ + printf(" [X] Assert Failed: %s (Expected %f, got %f) %s:%d\n", msg, (double)(expected), (double)(actual), __FILE__, __LINE__); \ + return C_FALSE; \ + } \ + } while(0) + +// External references to previous core modules +extern void c_Stopwatch_GetTimePoint(c_TimePoint_t* tp); + +/** + * Mocks active OS-level processing delays using portable nanosleep/Sleep interfaces. + */ +static void c_Framework_MockExecutionDelayMs(int ms) { +#if defined(_WIN32) || defined(_WIN64) + Sleep(ms); +#else + struct timespec ts; + ts.tv_sec = ms / 1000; + ts.tv_nsec = (ms % 1000) * 1000000L; + nanosleep(&ts, NULL); +#endif +} + +c_bool_t test_stopwatch_compute_diff_in_ms(void) { + c_TimePoint_t t_start, t_end; + + // Test Case 1: Param Parameter Enforcement Boundary Checks + EXPECT_NEAR(c_Stopwatch_ComputeDiffInMS(NULL, &t_end), -1.0, 1e-6, "NULL start point guard missed"); + EXPECT_NEAR(c_Stopwatch_ComputeDiffInMS(&t_start, NULL), -1.0, 1e-6, "NULL end point guard missed"); + + // Test Case 2: Standard Interval Difference Evaluation + printf(" [LOG] Capturing timestamp benchmarks across a 60ms thread stall scenario...\n"); + c_Stopwatch_GetTimePoint(&t_start); + + c_Framework_MockExecutionDelayMs(60); + + c_Stopwatch_GetTimePoint(&t_end); + + double delta_ms = c_Stopwatch_ComputeDiffInMS(&t_start, &t_end); + + // Allow a flexible tolerance boundary for generic OS context switching variances + EXPECT_EQ(delta_ms >= 55.0, C_TRUE, "Computed millisecond difference fell short of target delay thresholds"); + printf(" [STAT] Clock cycle delta computed: %f ms\n", delta_ms); + + // Test Case 3: Identity Time Check (Difference between identical points must equal 0.0) + EXPECT_NEAR(c_Stopwatch_ComputeDiffInMS(&t_start, &t_start), 0.0, 1e-6, "Identity point calculation returned non-zero value"); + + return C_TRUE; +} + + +c_bool_t test_stopwatch_lifecycle(void) { + c_Stopwatch_t sw; + + // Test Case 1: Param Checking Guards & Initial Conditions + EXPECT_EQ(c_Stopwatch_Init(NULL), C_ERR_PARAM, "NULL stopwatch handler guard missed"); + EXPECT_EQ(c_Stopwatch_Init(&sw), C_ERR_OK, "Stopwatch init initialization failed"); + EXPECT_NEAR(c_Stopwatch_GetElapsedSeconds(&sw), 0.0, 1e-6, "Freshly initialized stopwatch reported non-zero runtime"); + + // Test Case 2: Standard Interval Measurement Tracking Check + printf(" [LOG] Launching Stopwatch profile tracking (100ms thread stall scenario)...\n"); + EXPECT_EQ(c_Stopwatch_Start(&sw), C_ERR_OK, "Stopwatch engine start pass failed"); + + c_Framework_MockExecutionDelayMs(100); + + // Check lap peeking capabilities while running + double lap_peek_ms = c_Stopwatch_GetElapsedMilliseconds(&sw); + EXPECT_EQ(lap_peek_ms >= 90.0, C_TRUE, "Lap peeking reported impossibly low runtime under active intervals"); + + EXPECT_EQ(c_Stopwatch_Stop(&sw), C_ERR_OK, "Stopwatch engine stop pass failed"); + EXPECT_EQ(sw.is_running, C_FALSE, "Stop execution failed to flag active timeline states offline"); + + double final_seconds = c_Stopwatch_GetElapsedSeconds(&sw); + // Allow a wide tolerance for generic OS process scheduling variances + EXPECT_NEAR(final_seconds >= 0.09, C_TRUE, 0.0, "Measured timing fell short of target delay thresholds"); + + // Test Case 3: Accumulated Timing Checks (Resume feature validation) + printf(" [LOG] Resuming stopwatch profile tracking (Additional 50ms delay path)...\n"); + EXPECT_EQ(c_Stopwatch_Start(&sw), C_ERR_OK, "Stopwatch engine resume phase failed"); + + c_Framework_MockExecutionDelayMs(50); + + EXPECT_EQ(c_Stopwatch_Stop(&sw), C_ERR_OK, "Stopwatch subsequent interval termination failed"); + double cumulative_ms = c_Stopwatch_GetElapsedMilliseconds(&sw); + EXPECT_EQ(cumulative_ms >= 140.0, C_TRUE, "Stopwatch failed to accumulate consecutive interval data layers"); + + // Test Case 4: Soft Clear Verification + EXPECT_EQ(c_Stopwatch_Clear(&sw), C_ERR_OK, "Stopwatch clear execution crashed"); + EXPECT_NEAR(c_Stopwatch_GetElapsedSeconds(&sw), 0.0, 1e-6, "Clear protocol left residual timing tracking metrics inside buffer"); + + return C_TRUE; +} + +int main(void) { + printf("=== Starting Framework Verification: c_Stopwatch_ComputeDiffInMS ===\n"); + if (test_stopwatch_compute_diff_in_ms()) { + printf(" [PASS] Standalone Monotonic Delta Millisecond Compute Engine Verified Successfully.\n"); + } else { + printf(" [FAIL] Delta Resolution Pipeline Processing Mismatches Detected.\n"); + } + + + printf("=== Starting Framework Verification: c_Stopwatch ===\n"); + if (test_stopwatch_lifecycle()) { + printf(" [PASS] High-Precision Monotonic Stopwatch Lifecycle Pipelines Verified Successfully.\n"); + } else { + printf(" [FAIL] Stopwatch Engine Processing or State Tracking Mismatches Intercepted.\n"); + } + + return 0; +} diff --git a/cKit/Foundation/c_Str.c b/cKit/Foundation/c_Str.c new file mode 100644 index 0000000..5ed7d5f --- /dev/null +++ b/cKit/Foundation/c_Str.c @@ -0,0 +1,301 @@ +#include +#include +#include +#include + +#define idx(i, len) ((i) <= 0 ? (i) + (len) : (i) - 1) + +#define convert(s, i, j) do { int _len; \ + assert(s); _len = (int)strlen(s); \ + i = idx(i, _len); j = idx(j, _len); \ + if (i > j) { int t = i; i = j; j = t; } \ + assert(i >= 0 && j <= _len); } while (0) + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +char *c_Str_sub(const char *s, int i, int j) { + char *str, *p; + convert(s, i, j); + p = str = C_ALLOC(j - i + 1); + while (i < j) + *p++ = s[i++]; + *p = '\0'; + return str; +} + +char *c_Str_dup(const char *s, int i, int j, int n) { + int k; + char *str, *p; + assert(n >= 0); + convert(s, i, j); + p = str = C_ALLOC(n*(j - i) + 1); + if (j - i > 0) + while (n-- > 0) + for (k = i; k < j; k++) + *p++ = s[k]; + *p = '\0'; + return str; +} + +char *c_Str_reverse(const char *s, int i, int j) { + char *str, *p; + convert(s, i, j); + p = str = C_ALLOC(j - i + 1); + while (j > i) + *p++ = s[--j]; + *p = '\0'; + return str; +} + +char *c_Str_cat(const char *s1, int i1, int j1, + const char *s2, int i2, int j2) { + char *str, *p; + convert(s1, i1, j1); + convert(s2, i2, j2); + p = str = C_ALLOC(j1 - i1 + j2 - i2 + 1); + while (i1 < j1) + *p++ = s1[i1++]; + while (i2 < j2) + *p++ = s2[i2++]; + *p = '\0'; + return str; +} + +char *c_Str_catv(const char *s, ...) { + char *str, *p; + const char *save = s; + int i, j, len = 0; + va_list ap; + va_start(ap, s); + while (s) { + i = va_arg(ap, int); + j = va_arg(ap, int); + convert(s, i, j); + len += j - i; + s = va_arg(ap, const char *); + } + va_end(ap); + p = str = C_ALLOC(len + 1); + s = save; + va_start(ap, s); + while (s) { + i = va_arg(ap, int); + j = va_arg(ap, int); + convert(s, i, j); + while (i < j) + *p++ = s[i++]; + s = va_arg(ap, const char *); + } + va_end(ap); + *p = '\0'; + return str; +} + +char *c_Str_map(const char *s, int i, int j, + const char *from, const char *to) { + static char map[256] = { 0 }; + if (from && to) { + unsigned c; + for (c = 0; c < sizeof map; c++) + map[c] = c; + while (*from && *to) + map[(unsigned char)*from++] = *to++; + assert(*from == 0 && *to == 0); + } else { + assert(from == NULL && to == NULL && s); + assert(map['a']); + } + if (s) { + char *str, *p; + convert(s, i, j); + p = str = C_ALLOC(j - i + 1); + while (i < j) + *p++ = map[(unsigned char)s[i++]]; + *p = '\0'; + return str; + } else + return NULL; +} + +int c_Str_pos(const char *s, int i) { + assert(s); + const int len = (int) strlen(s); + i = idx(i, len); + assert(i >= 0 && i <= len); + return i + 1; +} + +int c_Str_len(const char *s, int i, int j) { + convert(s, i, j); + return j - i; +} + +int c_Str_cmp(const char *s1, int i1, int j1, + const char *s2, int i2, int j2) { + convert(s1, i1, j1); + convert(s2, i2, j2); + s1 += i1; + s2 += i2; + if (j1 - i1 < j2 - i2) { + int cond = strncmp(s1, s2, j1 - i1); + return cond == 0 ? -1 : cond; + } else if (j1 - i1 > j2 - i2) { + const int cond = strncmp(s1, s2, j2 - i2); + return cond == 0 ? +1 : cond; + } else + return strncmp(s1, s2, j1 - i1); +} + +int c_Str_chr(const char *s, int i, int j, int c) { + convert(s, i, j); + for ( ; i < j; i++) + if (s[i] == c) + return i + 1; + return 0; +} + +int c_Str_rchr(const char *s, int i, int j, int c) { + convert(s, i, j); + while (j > i) + if (s[--j] == c) + return j + 1; + return 0; +} + +int c_Str_upto(const char *s, int i, int j, + const char *set) { + assert(set); + convert(s, i, j); + for ( ; i < j; i++) + if (strchr(set, s[i])) + return i + 1; + return 0; +} + +int c_Str_rupto(const char *s, int i, int j, + const char *set) { + assert(set); + convert(s, i, j); + while (j > i) + if (strchr(set, s[--j])) + return j + 1; + return 0; +} + +int c_Str_find(const char *s, int i, int j, + const char *str) { + convert(s, i, j); + assert(str); + const int len = (int)strlen(str); + if (len == 0) + return i + 1; + else if (len == 1) { + for ( ; i < j; i++) + if (s[i] == *str) + return i + 1; + } else + for ( ; i + len <= j; i++) + if ((strncmp(&s[i], str, len) == 0)) + return i + 1; + return 0; +} + +int c_Str_rfind(const char *s, int i, int j, const char *str) { + convert(s, i, j); + assert(str); + const int len =(int)strlen(str); + if (len == 0) + return j + 1; + else if (len == 1) { + while (j > i) + if (s[--j] == *str) + return j + 1; + } else + for ( ; j - len >= i; j--) + if (strncmp(&s[j-len], str, len) == 0) + return j - len + 1; + return 0; +} + +int c_Str_any(const char *s, int i, const char *set) { + assert(s); + assert(set); + const int len =(int)strlen(s); + i = idx(i, len); + assert(i >= 0 && i <= len); + if (i < len && strchr(set, s[i])) + return i + 2; + return 0; +} +int c_Str_many(const char *s, int i, int j, + const char *set) { + assert(set); + convert(s, i, j); + if (i < j && strchr(set, s[i])) { + do + i++; + while (i < j && strchr(set, s[i])); + return i + 1; + } + return 0; +} + +int c_Str_rmany(const char *s, int i, int j, + const char *set) { + assert(set); + convert(s, i, j); + if (j > i && strchr(set, s[j-1])) { + do + --j; + while (j >= i && strchr(set, s[j])); + return j + 2; + } + return 0; +} + +int c_Str_match(const char *s, int i, int j, const char *str) { + convert(s, i, j); + assert(str); + const int len =(int)strlen(str); + if (len == 0) + return i + 1; + else if (len == 1) { + if (i < j && s[i] == *str) + return i + 2; + } else if (i + len <= j && (strncmp(&s[i], str, len) == 0)) + return i + len + 1; + return 0; +} + +int c_Str_rmatch(const char *s, int i, int j, const char *str) { + convert(s, i, j); + assert(str); + const int len =(int)strlen(str); + if (len == 0) + return j + 1; + else if (len == 1) { + if (j > i && s[j-1] == *str) + return j; + } else if (j - len >= i + && strncmp(&s[j-len], str, len) == 0) + return j - len + 1; + return 0; +} + +void c_Str_fmt(int code, va_list_box *box, + int put(int c, void *cl), void *cl, + unsigned char flags[], int width, int precision) { + assert(box && flags); + char *s = va_arg(box->ap, char *); + int i = va_arg(box->ap, int); + int j = va_arg(box->ap, int); + convert(s, i, j); + c_Fmt_puts(s + i, j - i, put, cl, flags, + width, precision); +} + +void c_Str_free(char* s) { + C_FREE(s); +} + diff --git a/cKit/Foundation/c_Str.h b/cKit/Foundation/c_Str.h new file mode 100644 index 0000000..27bf215 --- /dev/null +++ b/cKit/Foundation/c_Str.h @@ -0,0 +1,49 @@ +#ifndef INCLUDED_C_STR_H +#define INCLUDED_C_STR_H + +#ifndef INCLUDED_C_FMT_H +#include +#endif /*INCLUDED_C_FMT_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +char *c_Str_sub(const char *s, int i, int j); +char *c_Str_dup(const char *s, int i, int j, int n); +char *c_Str_cat(const char *s1, int i1, int j1, + const char *s2, int i2, int j2); +char *c_Str_catv (const char *s, ...); +char *c_Str_reverse(const char *s, int i, int j); +char *c_Str_map (const char *s, int i, int j, + const char *from, const char *to); +void c_Str_free(char* s); +int c_Str_pos(const char *s, int i); +int c_Str_len(const char *s, int i, int j); +int c_Str_cmp(const char *s1, int i1, int j1, + const char *s2, int i2, int j2); +int c_Str_chr (const char *s, int i, int j, int c); +int c_Str_rchr (const char *s, int i, int j, int c); +int c_Str_upto (const char *s, int i, int j, + const char *set); +int c_Str_rupto(const char *s, int i, int j, + const char *set); +int c_Str_find (const char *s, int i, int j, + const char *str); +int c_Str_rfind(const char *s, int i, int j, + const char *str); +int c_Str_any (const char *s, int i, + const char *set); +int c_Str_many (const char *s, int i, int j, + const char *set); +int c_Str_rmany (const char *s, int i, int j, + const char *set); +int c_Str_match (const char *s, int i, int j, + const char *str); +int c_Str_rmatch(const char *s, int i, int j, + const char *str); +void c_Str_fmt(int code, va_list_box *box, + int put(int c, void *cl), void *cl, + unsigned char flags[], int width, int precision); + +#endif /*INCLUDED_C_STR_H*/ diff --git a/cKit/Foundation/c_Str.t.c b/cKit/Foundation/c_Str.t.c new file mode 100644 index 0000000..de56b54 --- /dev/null +++ b/cKit/Foundation/c_Str.t.c @@ -0,0 +1,185 @@ +#include "c_Str.h" +#include +#include +const char *test_str = "abcdefg"; // 長度為 7 + +char test_output_buffer[128]; +int buf_idx = 0; + +int test_put(int c, void *cl) { + if (buf_idx < 127) { + test_output_buffer[buf_idx++] = (char)c; + } + return c; +} + +// Global print wrapper to test formatting execution +void test_printf(const char *fmt, ...) { + va_list_box ap; + va_start(ap.ap, fmt); + c_Fmt_vfmt(test_put, NULL, fmt, &ap); + va_end(ap.ap); +} + +void test_log(const char* name) { + printf("[PASS] %s\n", name); +} + + +int main() { + printf("==================================================\n"); + printf(" 開始執行 c_Str 核心功能組件 最終整合單元測試\n"); + printf("==================================================\n\n"); + + char *res_str = NULL; + + // 1. 測試 c_Str_pos + // test_str = "abcdefg" (len=7) + // 1 -> index 0 -> 位置 1 + // 0 -> 尾端倒數 (0+7) = index 7 -> 位置 8 + // -1 -> 尾端倒數 (-1+7) = index 6 -> 位置 7 + assert(c_Str_pos(test_str, 1) == 1); + assert(c_Str_pos(test_str, 0) == 8); + assert(c_Str_pos(test_str, -1) == 7); + test_log("c_Str_pos (1-based 位置計算驗證)"); + + // 2. 測試 c_Str_len + // 從 1 (開頭) 到 0 (結尾) -> 全長 7 + // 從 2 ('b') 到 5 ('e' 後方) -> 5 - 1 = 4 ('bcde') + assert(c_Str_len(test_str, 1, 0) == 7); + assert(c_Str_len(test_str, 2, 5) == 3); + test_log("c_Str_len (區間長度計算驗證)"); + + // 3. 測試 c_Str_sub + res_str = c_Str_sub(test_str, 2, 5); // 提取 'bcd' + assert(strcmp(res_str, "bcd") == 0); + c_Str_free(res_str); + test_log("c_Str_sub (子字串提取驗證)"); + + // 4. 測試 c_Str_dup + res_str = c_Str_dup(test_str, 2, 4, 3); // 'bc' 重複 3 次 + assert(strcmp(res_str, "bcbcbc") == 0); + c_Str_free(res_str); + test_log("c_Str_dup (子字串重複複製驗證)"); + + // 5. 測試 c_Str_reverse + res_str = c_Str_reverse(test_str, 2, 5); // 'bcd' 反轉 -> 'dcb' + assert(strcmp(res_str, "dcb") == 0); + c_Str_free(res_str); + test_log("c_Str_reverse (子字串反轉驗證)"); + + // 6. 測試 c_Str_cat + res_str = c_Str_cat("XYZ", 1, 0, "123", 1, 3); // "XYZ" + "12" -> "XYZ12" + assert(strcmp(res_str, "XYZ12") == 0); + c_Str_free(res_str); + test_log("c_Str_cat (雙字串區間拼接驗證)"); + + // 7. 測試 c_Str_catv (多字串可變參數拼接,必須以 NULL 結尾) + res_str = c_Str_catv("ABC", 1, 0, "XYZ", 2, 4, (char*)NULL); // "ABC" + "YZ" + assert(strcmp(res_str, "ABCYZ") == 0); + c_Str_free(res_str); + test_log("c_Str_catv (多參數變長字串拼接驗證)"); + + // 8. 測試 c_Str_map (字元映射/置換) + // 初始化對照表:將 'b' 換成 'X','d' 換成 'Y' + c_Str_map(NULL, 0, 0, "bd", "XY"); + res_str = c_Str_map(test_str, 1, 0, NULL, NULL); // "abcdefg" -> "aXcYe f g" + assert(strcmp(res_str, "aXcYefg") == 0); + c_Str_free(res_str); + test_log("c_Str_map (字元對照表置換驗證)"); + + // 9. 測試 c_Str_cmp + // "abcdefg" 區間 [2,4] 是 "bc";"abc" 區間 [2,4] 是 "bc" -> 相等 (0) + assert(c_Str_cmp(test_str, 2, 4, "abc", 2, 4) == 0); + // "bc" 與 "bcd" 比對 -> 前者較短且完全匹配,預期回傳 -1 + assert(c_Str_cmp(test_str, 2, 4, "abcdefg", 2, 5) == -1); + test_log("c_Str_cmp (區間字串深度比對驗證)"); + + // 10. 測試 c_Str_chr (正向尋找字元,回傳 1-based 位置) + // 在 "abcdefg" 中找 'c' -> 位於 index 2 -> 回傳 3 + assert(c_Str_chr(test_str, 1, 0, 'c') == 3); + assert(c_Str_chr(test_str, 1, 0, 'z') == 0); // 找不到 + test_log("c_Str_chr (正向字元查找位置驗證)"); + + // 11. 測試 c_Str_rchr (反向尋找字元) + // 在 "abcdecd" 中找 'c' + assert(c_Str_rchr("abcdecd", 1, 0, 'c') == 6); + test_log("c_Str_rchr (反向字元查找位置驗證)"); + + // 12. 測試 c_Str_upto (正向尋找集合中任一字元首次出現位置) + // "abcdefg" 中尋找 "xyz" 或 "d" -> 'd' 最先被匹配 (index 3) -> 回傳 4 + assert(c_Str_upto(test_str, 1, 0, "xyz d") == 4); + test_log("c_Str_upto (字元集合正向切分點驗證)"); + + // 13. 測試 c_Str_rupto (反向尋找集合中任一字元首次出現位置) + // "abcdefg" 中反向找 "ab" -> 'b' 最先被找到 (index 1) -> 回傳 2 + assert(c_Str_rupto(test_str, 1, 0, "ab") == 2); + test_log("c_Str_rupto (字元集合反向切分點驗證)"); + + // 14. 測試 c_Str_find (正向尋找子字串) + // "abcdefg" 中找 "cde" -> 開始於 index 2 -> 回傳 3 + assert(c_Str_find(test_str, 1, 0, "cde") == 3); + test_log("c_Str_find (正向子字串搜尋匹配驗證)"); + + // 15. 測試 c_Str_rfind (反向尋找子字串) + // "ababax" 中反向找 "aba" -> 應匹配到 index 2 開始的 "aba" -> 回傳 3 + assert(c_Str_rfind("ababax", 1, 0, "aba") == 3); + test_log("c_Str_rfind (反向子字串搜尋匹配驗證)"); + + // 16. 測試 c_Str_any (檢查指定 index 的單一字元是否在集合中) + // test_str="abcdefg", i=3 -> index 2 ('c')。'c' 有在 "cba" 之中 -> 回傳 index+2 = 4 + assert(c_Str_any(test_str, 3, "cba") == 4); + assert(c_Str_any(test_str, 3, "xyz") == 0); // 'c' 不在 xyz 中 + test_log("c_Str_any (特定點字元集命中檢查驗證)"); + + // 17. 測試 c_Str_many (從指定起點向後匹配連續屬於集合的字元,直到不屬於為止) + // "aaabX" 從 1 開始,連續符合 "abc" 的有 'a','a','a','b' (4個) -> 停止於 index 4 -> 回傳 4+1 = 5 + assert(c_Str_many("aaabX", 1, 0, "abc") == 5); + test_log("c_Str_many (正向連續字元集跨越範圍驗證)"); + + // 18. 測試 c_Str_rmany (從指定終點向前匹配連續屬於集合的字元) + // "Xbbba" 從 0(結尾) 向前,符合 "abc" 的有 'a','b','b','b' (4個) -> 停止於 index 0 ('X') -> 回傳 index+2 = 2 + assert(c_Str_rmany("Xbbba", 1, 0, "abc") == 2); + test_log("c_Str_rmany (反向連續字元集跨越範圍驗證)"); + + // 19. 測試 c_Str_match (檢查指定起點是否「精準開頭匹配」該子字串) + // "abcdefg" 在位置 3 (index 2, 'c') 是否精準匹配 "cde" -> 是,符合長度 3 -> 回傳 index+len+1 = 2+3+1 = 6 + assert(c_Str_match(test_str, 3, 0, "cde") == 6); + assert(c_Str_match(test_str, 3, 0, "xyz") == 0); + test_log("c_Str_match (指定起點子字串精準頭匹配驗證)"); + + // 20. 測試 c_Str_rmatch (檢查指定終點是否「精準結尾匹配」該子字串) + // "abcde" 在 0(結尾, index 5) 向前看是否精準匹配 "cde" (len=3) -> index 5-3=2 開始是 "cde" -> 成功,回傳 2+1 = 3 + assert(c_Str_rmatch("abcde", 1, 0, "cde") == 3); + test_log("c_Str_rmatch (指定終點子字串精準尾匹配驗證)"); + + // 21. 測試 c_Str_fmt (驗證自訂格式化回呼整合) +#if 0 + unsigned char flags[256] = { 0 }; + mock_put_count = 0; + // 傳入字串 "hello", 起點 2 ('e'), 終點 5 ('o' 後方) -> 預期提取出 "ell" + run_str_fmt_test(0, flags, 0, 0, "hello", 2, 5); + assert(mock_put_count == 3); // "ell" 長度為 3,應呼叫 3 次 put + test_log("c_Str_fmt (格式化輸出器區間提取回呼驗證)"); +#endif + + memset(test_output_buffer, 0, sizeof(test_output_buffer)); + buf_idx = 0; + c_Fmt_t old_handler = c_Fmt_register('S', c_Str_fmt); + assert(old_handler == NULL); + printf("[PASS] c_Str_fmt successfully registered to specifier character 'S'.\n"); + + const char* sample_sentence = "the c-container-library platform"; + test_printf("Extracted Token: [%S]", sample_sentence, 5, 16); + test_output_buffer[buf_idx] = '\0'; + + printf(" Rendered Output String: %s\n", test_output_buffer); + assert(strcmp(test_output_buffer, "Extracted Token: [c-container]") == 0); + printf("[PASS] Core Fmt engine correctly processed parameters via c_Str_fmt.\n"); + + printf("\n==================================================\n"); + printf(" 恭喜!c_Str 基礎工具類共計 22 個核心介面全數單元測試通過!\n"); + printf("==================================================\n"); + + return 0; +} \ No newline at end of file diff --git a/cKit/Foundation/c_StrIndexKmp.c b/cKit/Foundation/c_StrIndexKmp.c new file mode 100644 index 0000000..73e57d7 --- /dev/null +++ b/cKit/Foundation/c_StrIndexKmp.c @@ -0,0 +1,76 @@ +#include +#include + +#define MAX_STACK_LPS 128 + +static void compute_LPS_table(const char* pattern, c_size_t m, long long* lps) { + c_size_t len = 0; // Length of the previous longest prefix suffix + lps[0] = 0; // lps[0] is always 0 + c_size_t i = 1; + + while (i < m) { + if (pattern[i] == pattern[len]) { + len++; + lps[i] = (long long)len; + i++; + } else { + if (len != 0) { + len = (c_size_t)lps[len - 1]; // Backtrack without shifting 'i' + } else { + lps[i] = 0; + i++; + } + } + } +} + +long long c_StrIndexKmp(const char* text, const char* pattern) { + if (!text || !pattern) return -1; + + const c_size_t n = strlen(text); + const c_size_t m = strlen(pattern); + + if (m == 0) return 0; // An empty pattern matches at the very beginning + if (m > n) return -1; // Pattern is longer than the text container + + // Optimization: Use a stack buffer if the pattern length fits, avoiding heap allocation cycles + long long stack_lps[MAX_STACK_LPS]; + long long* lps = (m <= MAX_STACK_LPS) ? stack_lps : (long long*)C_ALLOC(m * sizeof(long long)); + if (!lps) return -1; // Allocation failure fallback + + // Step 1: Precompute the lookup table + compute_LPS_table(pattern, m, lps); + + // Step 2: Linear string matching phase + c_size_t i = 0; // Index for text + c_size_t j = 0; // Index for pattern + long long matched_index = -1; + + while (i < n) { + if (pattern[j] == text[i]) { + i++; + j++; + } + + if (j == m) { + matched_index = (long long)(i - j); // Found match at index (i - j) + break; // Terminate early for the first occurrence + } + // Mismatch after j matches + else if (i < n && pattern[j] != text[i]) { + if (j != 0) { + j = (size_t)lps[j - 1]; // Slide the pattern using the precomputed LPS table + } else { + i++; + } + } + } + + // Clean up heap memory if it was allocated + if (lps != stack_lps) { + C_FREE(lps); + } + + return matched_index; +} + diff --git a/cKit/Foundation/c_StrIndexKmp.h b/cKit/Foundation/c_StrIndexKmp.h new file mode 100644 index 0000000..1c8166f --- /dev/null +++ b/cKit/Foundation/c_StrIndexKmp.h @@ -0,0 +1,14 @@ +#ifndef INCLUDED_C_STRINDEXKMP_H +#define INCLUDED_C_STRINDEXKMP_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +long long c_StrIndexKmp(const char* text, const char* pattern); + +#endif /*INCLUDED_C_STRINDEXKMP_H*/ diff --git a/cKit/Foundation/c_StrIndexKmp.t.c b/cKit/Foundation/c_StrIndexKmp.t.c new file mode 100644 index 0000000..eb4cdcd --- /dev/null +++ b/cKit/Foundation/c_StrIndexKmp.t.c @@ -0,0 +1,47 @@ +#include "c_StrIndexKmp.h" +#include +#include + +void test_log(const char* test_name) { + printf("[PASS] %s\n", test_name); +} + +int main() { + printf("==================================================\n"); + printf(" Starting KMP String Search Algorithm Unit Tests\n"); + printf("==================================================\n\n"); + + // Test 1: Standard match discovery + const char* text1 = "ABABDABACDABABCABAB"; + long long idx1 = c_StrIndexKmp(text1, "ABABCABAB"); + assert(idx1 == 10); + test_log("1. Pattern found at index 10 successfully"); + + // Test 2: Repeating partial pattern fallback (Corrected text and pattern) + // At index 0, text matches "ABABA" but mismatches on the 6th char ('B' vs 'C'). + // The LPS table causes j to backtrack smoothly, discovering the real match starting at index 2. + const char* text2 = "ABABABACATA"; + long long idx2 = c_StrIndexKmp(text2, "ABABAC"); + assert(idx2 == 2); + test_log("2. Partial-match backtracking table lookup verified at index 2"); + + // Test 3: Pattern not present in text + long long idx3 = c_StrIndexKmp(text1, "XYZ"); + assert(idx3 == -1); + test_log("3. Non-existent substring pattern returns -1 safely"); + + // Test 4: Empty pattern handling boundary check + long long idx4 = c_StrIndexKmp(text1, ""); + assert(idx4 == 0); + test_log("4. Empty string matches target index 0"); + + // Test 5: Pattern longer than string payload boundary check + long long idx5 = c_StrIndexKmp("short", "extremely_long_pattern"); + assert(idx5 == -1); + test_log("5. Length mismatch constraints handled gracefully"); + + printf("\n==================================================\n"); + printf(" Success! All KMP test assertions passed successfully!\n"); + printf("==================================================\n"); + return 0; +} \ No newline at end of file diff --git a/cKit/Foundation/c_StrUtil.c b/cKit/Foundation/c_StrUtil.c new file mode 100644 index 0000000..10660bd --- /dev/null +++ b/cKit/Foundation/c_StrUtil.c @@ -0,0 +1,193 @@ +#include +#include +#include + +#include "c_ArrayList.h" + +// Reverses a string in-place +void c_StrUtil_Reverse(char* str) { + if (!str) return; + const c_size_t len = strlen(str); + if (len <= 1) return; + + c_size_t i = 0; + c_size_t j = len - 1; + while (i < j) { + char temp = str[i]; + str[i] = str[j]; + str[j] = temp; + i++; + j--; + } +} + +// Converts a string to lowercase into a destination buffer +c_err_t c_StrUtil_ToLower(char* out_dest, const char* src, c_size_t dest_capacity) { + if (!out_dest || !src || dest_capacity == 0) return C_ERR_PARAM; + + c_size_t i = 0; + while (src[i] != '\0') { + if (i >= dest_capacity - 1) { + out_dest[i] = '\0'; + return C_ERR_INDEX; + } + out_dest[i] = (char)tolower((unsigned char)src[i]); + i++; + } + out_dest[i] = '\0'; + return C_ERR_SUCCESS; +} + +// Converts a string to uppercase into a destination buffer +c_err_t c_StrUtil_ToUpper(char* out_dest, const char* src, c_size_t dest_capacity) { + if (!out_dest || !src || dest_capacity == 0) return C_ERR_PARAM; + + c_size_t i = 0; + while (src[i] != '\0') { + if (i >= dest_capacity - 1) { + out_dest[i] = '\0'; + return C_ERR_INDEX; + } + out_dest[i] = (char)toupper((unsigned char)src[i]); + i++; + } + out_dest[i] = '\0'; + return C_ERR_SUCCESS; +} + +// Removes leading and trailing whitespaces into a destination buffer +c_err_t c_StrUtil_Trim(char* out_dest, const char* src, c_size_t dest_capacity) { + if (!out_dest || !src || dest_capacity == 0) return C_ERR_PARAM; + + // Find first non-whitespace character + while (*src && isspace((unsigned char)*src)) { + src++; + } + + // Find the end of the string + c_size_t len = strlen(src); + while (len > 0 && isspace((unsigned char)src[len - 1])) { + len--; + } + + if (len >= dest_capacity) { + return C_ERR_INDEX; + } + + // Copy trimmed substring + memmove(out_dest, src, len); + out_dest[len] = '\0'; + return C_ERR_SUCCESS; +} + +c_bool_t c_StrUtil_StartsWith(const char* str, const char* prefix) { + if (!str || !prefix) return C_FALSE; + return strncmp(str, prefix, strlen(prefix)) == 0 ? C_TRUE : C_FALSE; +} + +c_bool_t c_StrUtil_EndsWith(const char* str, const char* suffix) { + if (!str || !suffix) return C_FALSE; + c_size_t str_len = strlen(str); + c_size_t suf_len = strlen(suffix); + if (suf_len > str_len) return C_FALSE; + return strcmp(str + str_len - suf_len, suffix) == 0 ? C_TRUE : C_FALSE; +} + +// Safely extracts a substring with full bounds verification +c_err_t c_StrUtil_Substring(char* out_dest, const char* src, c_size_t start, c_size_t len, c_size_t dest_capacity) { + if (!out_dest || !src || dest_capacity == 0) return C_ERR_PARAM; + + c_size_t src_len = strlen(src); + if (start > src_len) return C_ERR_INDEX; + + // Adjust requested length if it overflows source string boundaries + if (start + len > src_len) { + len = src_len - start; + } + + if (len >= dest_capacity) { + return C_ERR_INDEX; + } + + memcpy(out_dest, src + start, len); + out_dest[len] = '\0'; + return C_ERR_SUCCESS; +} + +// Replaces occurrences of a substring inside a buffer safely +c_err_t c_StrUtil_Replace(char* out_dest, const char* src, const char* find, const char* replace_with, c_size_t dest_capacity) { + if (!out_dest || !src || !find || !replace_with || dest_capacity == 0) return C_ERR_PARAM; + + c_size_t find_len = strlen(find); + c_size_t replace_len = strlen(replace_with); + c_size_t dest_len = 0; + + if (find_len == 0) return C_ERR_PARAM; + + while (*src) { + // If match found, inject replacement string + if (strncmp(src, find, find_len) == 0) { + if (dest_len + replace_len >= dest_capacity) { + out_dest[dest_len] = '\0'; + return C_ERR_INDEX; + } + strcpy(out_dest + dest_len, replace_with); + dest_len += replace_len; + src += find_len; + } else { + // Otherwise, inject individual source character + if (dest_len + 1 >= dest_capacity) { + out_dest[dest_len] = '\0'; + return C_ERR_INDEX; + } + out_dest[dest_len] = *src; + dest_len++; + src++; + } + } + out_dest[dest_len] = '\0'; + return C_ERR_SUCCESS; +} + +c_err_t c_StrUtil_Split(c_ArrayList_t* out_list, const char* src, const char* delimiter) { + if (!out_list || !src || !delimiter) return C_ERR_PARAM; + if (out_list->obj_size != sizeof(c_StrToken_t)) return C_ERR_PARAM; // Type safety assert + + const c_size_t delim_len = strlen(delimiter); + if (delim_len == 0) return C_ERR_PARAM; + + const char* current = src; + const char* next_match; + + while ((next_match = strstr(current, delimiter)) != NULL) { + size_t token_len = next_match - current; + + if (token_len > 0) { + c_StrToken_t new_token; + // Bound checking to ensure long strings don't cause buffer overflows + size_t copy_len = (token_len >= C_STR_TOKEN_MAX_LEN) ? (C_STR_TOKEN_MAX_LEN - 1) : token_len; + + memcpy(new_token.text, current, copy_len); + new_token.text[copy_len] = '\0'; + + // Deep-copy token structure payload straight into the dynamic ArrayList + c_ArrayList_Add(out_list, &new_token); + } + current = next_match + delim_len; + } + + // Capture the final token remaining after the last delimiter match + if (*current != '\0') { + c_StrToken_t new_token; + size_t token_len = strlen(current); + size_t copy_len = (token_len >= C_STR_TOKEN_MAX_LEN) ? (C_STR_TOKEN_MAX_LEN - 1) : token_len; + + memcpy(new_token.text, current, copy_len); + new_token.text[copy_len] = '\0'; + + c_ArrayList_Add(out_list, &new_token); + } + + return C_ERR_SUCCESS; +} + diff --git a/cKit/Foundation/c_StrUtil.h b/cKit/Foundation/c_StrUtil.h new file mode 100644 index 0000000..9eabda7 --- /dev/null +++ b/cKit/Foundation/c_StrUtil.h @@ -0,0 +1,47 @@ +#ifndef INCLUDED_C_STRUTIL_H +#define INCLUDED_C_STRUTIL_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +#ifndef INCLUDED_C_ARRAYLIST_H +#include +#endif /*INCLUDED_C_ARRAYLIST_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#ifndef C_STR_TOKEN_MAX_LEN +#define C_STR_TOKEN_MAX_LEN 64 +#endif + + +typedef struct { + char text[C_STR_TOKEN_MAX_LEN]; +} c_StrToken_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +void c_StrUtil_Reverse(char* str); +c_err_t c_StrUtil_ToLower(char* out_dest, const char* src, c_size_t dest_capacity); +c_err_t c_StrUtil_ToUpper(char* out_dest, const char* src, c_size_t dest_capacity); + +// Trimming whitespace (' ', '\t', '\r', '\n') +c_err_t c_StrUtil_Trim(char* out_dest, const char* src, c_size_t dest_capacity); + +// Search & Matching operations +c_bool_t c_StrUtil_StartsWith(const char* str, const char* prefix); +c_bool_t c_StrUtil_EndsWith(const char* str, const char* suffix); + +// Substring extraction (Safe, boundary checked) +c_err_t c_StrUtil_Substring(char* out_dest, const char* src, c_size_t start, c_size_t len, c_size_t dest_capacity); + +// String replacements (Safely handles different length replacements) +c_err_t c_StrUtil_Replace(char* out_dest, const char* src, const char* find, const char* replace_with, c_size_t dest_capacity); + +// Tokenizer that breaks down a source string and appends results to an initialized ArrayList +c_err_t c_StrUtil_Split(c_ArrayList_t* out_list, const char* src, const char* delimiter) ; + +#endif /*INCLUDED_C_STRUTIL_H*/ diff --git a/cKit/Foundation/c_StrUtil.t.c b/cKit/Foundation/c_StrUtil.t.c new file mode 100644 index 0000000..472f396 --- /dev/null +++ b/cKit/Foundation/c_StrUtil.t.c @@ -0,0 +1,114 @@ +#include "c_StrUtil.h" +#include +#include + + +void test_log(const char* test_name) { + printf("[PASS] %s\n", test_name); +} + +static void test_split(void) { + printf("==================================================\n"); + printf(" Starting Tokenizer & ArrayList Integration Tests\n"); + printf("==================================================\n\n"); + + // Initialize your generic ArrayList to accept your structured token elements + c_ArrayList_t token_list; + c_err_t err = c_ArrayList_Init(&token_list, sizeof(c_StrToken_t), 4); + assert(err == C_ERR_SUCCESS); + + const char* csv_data = "GND,VCC,TX_PIN,RX_PIN,SPI_CLK"; + + // Split the comma-delimited configuration values + err = c_StrUtil_Split(&token_list, csv_data, ","); + assert(err == C_ERR_SUCCESS); + + // 1. Validate parsed sizing counts matching expected segmentations + assert(token_list.size == 5); + test_log("1. String segmented into 5 individual structural elements"); + + // 2. Sequential range checking verifying index preservation + c_StrToken_t* t_ptr; + + t_ptr = (c_StrToken_t*)c_ArrayList_Get(&token_list, 0); + assert(strcmp(t_ptr->text, "GND") == 0); + + t_ptr = (c_StrToken_t*)c_ArrayList_Get(&token_list, 2); + assert(strcmp(t_ptr->text, "TX_PIN") == 0); + + t_ptr = (c_StrToken_t*)c_ArrayList_Get(&token_list, 4); + assert(strcmp(t_ptr->text, "SPI_CLK") == 0); + test_log("2. Target indices verify correct sub-string capture sequence"); + + // 3. Clear container lifecycle properties cleanly + c_ArrayList_Destroy(&token_list); + test_log("3. Array container memory teardown success"); + + printf("\n==================================================\n"); + printf(" Success! Token parsing and container layers fit perfectly!\n"); + printf("==================================================\n"); +} + +int main() { + printf("==================================================\n"); + printf(" Starting C String Utility Unit Tests\n"); + printf("==================================================\n\n"); + + char buffer[128]; + c_err_t err; + + // 1. Test Reverse + strcpy(buffer, "A man a plan a canal Panama"); + c_StrUtil_Reverse(buffer); + assert(strcmp(buffer, "amanaP lanac a nalp a nam A") == 0); + test_log("1. In-place string reversal"); + + // 2. Test Case Conversions + err = c_StrUtil_ToLower(buffer, "C_Language_123!", sizeof(buffer)); + assert(err == C_ERR_SUCCESS); + assert(strcmp(buffer, "c_language_123!") == 0); + + err = c_StrUtil_ToUpper(buffer, "embedded c", sizeof(buffer)); + assert(err == C_ERR_SUCCESS); + assert(strcmp(buffer, "EMBEDDED C") == 0); + test_log("2. Casing conversions"); + + // 3. Test Trim + err = c_StrUtil_Trim(buffer, " \t Hello World \r\n ", sizeof(buffer)); + assert(err == C_ERR_SUCCESS); + assert(strcmp(buffer, "Hello World") == 0); + test_log("3. Whitespace trimming"); + + // 4. Test Starts/Ends With + assert(c_StrUtil_StartsWith("libcontainer.so", "lib") == C_TRUE); + assert(c_StrUtil_StartsWith("libcontainer.so", "bin") == C_FALSE); + assert(c_StrUtil_EndsWith("document.txt", ".txt") == C_TRUE); + assert(c_StrUtil_EndsWith("document.txt", ".pdf") == C_FALSE); + test_log("4. Prefix/Suffix conditional checks"); + + // 5. Test Substring + err = c_StrUtil_Substring(buffer, "Microcontroller", 5, 7, sizeof(buffer)); + assert(err == C_ERR_SUCCESS); + assert(strcmp(buffer, "control") == 0); + test_log("5. Substring range isolation"); + + // 6. Test Replace + err = c_StrUtil_Replace(buffer, "The quick brown fox jumps over the lazy dog", "quick brown fox", "slow green turtle", sizeof(buffer)); + assert(err == C_ERR_SUCCESS); + assert(strcmp(buffer, "The slow green turtle jumps over the lazy dog") == 0); + test_log("6. Substring template injection matching"); + + // 7. Overflow Protection + char small_buffer[6]; + err = c_StrUtil_ToUpper(small_buffer, "OVERFLOW_TEST", sizeof(small_buffer)); + assert(err == C_ERR_INDEX); + assert(strlen(small_buffer) == 5); // Ensure safe null truncation occurred + test_log("7. Buffer overflow bounds interception"); + + printf("\n==================================================\n"); + printf(" Success! String utilities match defensive safety layouts!\n"); + printf("==================================================\n"); + + test_split(); + return 0; +} \ No newline at end of file diff --git a/cKit/Foundation/c_StringBuffer.c b/cKit/Foundation/c_StringBuffer.c new file mode 100644 index 0000000..b4f9c8c --- /dev/null +++ b/cKit/Foundation/c_StringBuffer.c @@ -0,0 +1,161 @@ +#include +#include + +#define DEFAULT_INIT_CAPACITY 16 + +C_STATIC_FORCE_INLINE +c_err_t c_StringBuffer_EnsureCapacity(c_StringBuffer_t* self, c_size_t required_len) { + c_size_t needed_capacity = self->size + required_len + 1; // +1 for trailing '\0' + if (needed_capacity <= self->capacity) { + return C_ERR_OK; + } + + c_size_t new_capacity = self->capacity == 0 ? DEFAULT_INIT_CAPACITY : self->capacity; + while (new_capacity < needed_capacity) { + new_capacity *= 2; // Exponential doubling strategy + } + + char* new_buffer = (char*)C_ALLOC(new_capacity); + if (!new_buffer) { + return C_ERR_NOMEM; + } + + if (self->buffer && self->size > 0) { + memcpy(new_buffer, self->buffer, self->size); + } + new_buffer[self->size] = '\0'; + + C_FREE(self->buffer); + self->buffer = new_buffer; + self->capacity = new_capacity; + + return C_ERR_OK; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +c_err_t c_StringBuffer_Init(c_StringBuffer_t* self, c_size_t capacity) { + if (!self) return C_ERR_PARAM; + + self->size = 0; + self->capacity = capacity > 0 ? capacity : DEFAULT_INIT_CAPACITY; // Enforce minimum initial allocation + self->buffer = (char*)C_ALLOC(self->capacity); + if (!self->buffer) { + self->capacity = 0; + return C_ERR_NOMEM; + } + self->buffer[0] = '\0'; + + return C_ERR_OK; +} + +void c_StringBuffer_Destroy(c_StringBuffer_t* self) { + if (!self) return; + if (self->buffer) { + C_FREE(self->buffer); + } + self->size = 0; + self->capacity = 0; +} + +c_err_t c_StringBuffer_Append(c_StringBuffer_t* self, const char* string, c_size_t length) { + if (!self || !self->buffer || !string || length == 0) return C_ERR_PARAM; + + c_err_t err = c_StringBuffer_EnsureCapacity(self, length); + if (err != C_ERR_OK) return err; + + memcpy(self->buffer + self->size, string, length); + self->size += length; + self->buffer[self->size] = '\0'; + + return C_ERR_OK; +} + +c_err_t c_StringBuffer_Prepend(c_StringBuffer_t* self, const char* string, c_size_t length) { + return c_StringBuffer_InsertAt(self, 0, string, length); +} + +c_err_t c_StringBuffer_InsertAt(c_StringBuffer_t* self, c_size_t index, const char* string, c_size_t length) { + if (!self || !self->buffer || !string || length == 0 || index > self->size) return C_ERR_PARAM; + + c_err_t err = c_StringBuffer_EnsureCapacity(self, length); + if (err != C_ERR_OK) return err; + + // Shift memory to the right using memmove to prevent overlapping issues + memmove(self->buffer + index + length, self->buffer + index, self->size - index); + memcpy(self->buffer + index, string, length); + + self->size += length; + self->buffer[self->size] = '\0'; + + return C_ERR_OK; +} + +c_err_t c_StringBuffer_RemoveAt(c_StringBuffer_t* self, c_size_t index, c_size_t length) { + if (!self || !self->buffer || length == 0 || index >= self->size) return C_ERR_PARAM; + + // Clamp length if it attempts to read past the end of the current buffer + if (index + length > self->size) { + length = self->size - index; + } + + // Shift trailing memory to the left to close the character gap + memmove(self->buffer + index, self->buffer + index + length, self->size - (index + length)); + self->size -= length; + self->buffer[self->size] = '\0'; + + return C_ERR_OK; +} + +void c_StringBuffer_Clear(c_StringBuffer_t* self) { + if (!self || !self->buffer) return; + self->size = 0; + self->buffer[0] = '\0'; +} + +/* --- Explicit String-Wrapper Interfaces --- */ + +c_err_t c_StringBuffer_AppendStr(c_StringBuffer_t* self, const char* string) { + if (!string) return C_ERR_PARAM; + return c_StringBuffer_Append(self, string, strlen(string)); +} + +c_err_t c_StringBuffer_PrependStr(c_StringBuffer_t* self, const char* string) { + if (!string) return C_ERR_PARAM; + return c_StringBuffer_Prepend(self, string, strlen(string)); +} + +c_err_t c_StringBuffer_InsertStrAt(c_StringBuffer_t* self, const char* string, c_size_t index) { + if (!string) return C_ERR_PARAM; + return c_StringBuffer_InsertAt(self, index, string, strlen(string)); +} + +c_err_t c_StringBuffer_CopyTo(c_StringBuffer_t* self, c_size_t index, c_size_t length, char* buffer, c_size_t buffer_length) { + // 1. Guard against invalid pointers, empty destinations, or index out-of-bounds + if (!self || !self->buffer || !buffer || buffer_length == 0 || index > self->size) { + return C_ERR_PARAM; + } + + // 2. Clamp requested copy length if it exceeds the remaining data payload bounds + if (index + length > self->size) { + length = self->size - index; + } + + // 3. Enforce destination buffer capacity threshold checks + // The requested segment requires at least (length + 1) bytes for safe null-termination + if (length >= buffer_length) { + return C_ERR_PARAM; // Destination buffer is too small to store the segment safely + } + + // 4. Perform the raw memory copy if there are valid characters to process + if (length > 0) { + memcpy(buffer, self->buffer + index, length); + } + + // 5. Always apply a deterministic trailing null terminator + buffer[length] = '\0'; + + return C_ERR_OK; +} diff --git a/cKit/Foundation/c_StringBuffer.h b/cKit/Foundation/c_StringBuffer.h new file mode 100644 index 0000000..4ab2ff7 --- /dev/null +++ b/cKit/Foundation/c_StringBuffer.h @@ -0,0 +1,41 @@ +#ifndef INCLUDED_C_STRINGBUFFER_H +#define INCLUDED_C_STRINGBUFFER_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + char* buffer; + c_size_t capacity; + c_size_t size; +}c_StringBuffer_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_StringBuffer_Init(c_StringBuffer_t* self, c_size_t capacity); + +void c_StringBuffer_Destroy(c_StringBuffer_t* self); + +c_err_t c_StringBuffer_Append(c_StringBuffer_t* self, const char* string, c_size_t length); + +c_err_t c_StringBuffer_Prepend(c_StringBuffer_t* self, const char* string, c_size_t length); + +c_err_t c_StringBuffer_InsertAt(c_StringBuffer_t* self, c_size_t index, const char* string, c_size_t length); + +c_err_t c_StringBuffer_RemoveAt(c_StringBuffer_t* self, c_size_t index, c_size_t length); + +void c_StringBuffer_Clear(c_StringBuffer_t* self); + +c_err_t c_StringBuffer_AppendStr(c_StringBuffer_t* self, const char* string); +c_err_t c_StringBuffer_PrependStr(c_StringBuffer_t* self, const char* string); +c_err_t c_StringBuffer_InsertStrAt(c_StringBuffer_t* self, const char* string, c_size_t index); + +c_err_t c_StringBuffer_CopyTo(c_StringBuffer_t* self, c_size_t index, c_size_t length, char* buffer, c_size_t buffer_length); + +#endif /*INCLUDED_C_STRINGBUFFER_H*/ diff --git a/cKit/Foundation/c_StringBuffer.t.c b/cKit/Foundation/c_StringBuffer.t.c new file mode 100644 index 0000000..1141c0c --- /dev/null +++ b/cKit/Foundation/c_StringBuffer.t.c @@ -0,0 +1,133 @@ +#include "c_StringBuffer.h" +#include +#include + +#define RUN_TEST(condition, test_name) \ + do { \ + printf("[TEST] %s... ", test_name); \ + if (condition) { \ + printf("\033[32mPASSED\033[0m\n"); \ + } else { \ + printf("\033[31mFAILED\033[0m (at Line %d)\n", __LINE__); \ + return C_ERR_FAIL; \ + } \ + } while(0) + +c_err_t c_StringBuffer_UnitTest(void) { + c_StringBuffer_t sb; + c_err_t err; + char copy_target[64]; + + printf("==================================================\n"); + printf(" STARTING C_STRINGBUFFER UNIT TESTING \n"); + printf("==================================================\n"); + + /* 1. API Parameter Defensive Checks (Null Guards) */ + RUN_TEST(c_StringBuffer_Init(NULL, 16) == C_ERR_PARAM, "Init handles NULL self context"); + RUN_TEST(c_StringBuffer_Append(NULL, "a", 1) == C_ERR_PARAM, "Append checks NULL self"); + RUN_TEST(c_StringBuffer_Prepend(NULL, "a", 1) == C_ERR_PARAM, "Prepend checks NULL self"); + RUN_TEST(c_StringBuffer_InsertAt(NULL, 0, "a", 1) == C_ERR_PARAM, "InsertAt checks NULL self"); + RUN_TEST(c_StringBuffer_RemoveAt(NULL, 0, 1) == C_ERR_PARAM, "RemoveAt checks NULL self"); + RUN_TEST(c_StringBuffer_CopyTo(NULL, 0, 1, copy_target, 64) == C_ERR_PARAM, "CopyTo checks NULL self"); + + /* 2. Initialization Test Block (Init) */ + err = c_StringBuffer_Init(&sb, 4); // Initialize with small capacity to force upcoming resizing branches + RUN_TEST(err == C_ERR_OK, "Initialization with tiny explicit capacity returns C_ERR_OK"); + RUN_TEST(sb.size == 0, "Initial tracked contents data size is 0"); + RUN_TEST(sb.capacity == 4, "Initial tracking allocation capacity is 4"); + RUN_TEST(sb.buffer != NULL, "Internal tracking byte storage buffer successfully bound"); + RUN_TEST(sb.buffer[0] == '\0', "Empty buffer is safely terminated with null byte"); + + /* 3. Length-bounded Insertion Operations (Append, Prepend, InsertAt) */ + // Append test + err = c_StringBuffer_Append(&sb, "Trie", 4); + RUN_TEST(err == C_ERR_OK, "Append bounded segment 'Trie'"); + RUN_TEST(sb.size == 4, "Size matches append width"); + RUN_TEST(strcmp(sb.buffer, "Trie") == 0, "Buffer contains exact match string 'Trie'"); + + // Prepend test + err = c_StringBuffer_Prepend(&sb, "Nlp", 3); + RUN_TEST(err == C_ERR_OK, "Prepend bounded segment 'Nlp' to front"); + RUN_TEST(sb.size == 7, "Size extended to 7 bytes total"); + RUN_TEST(strcmp(sb.buffer, "NlpTrie") == 0, "Buffer shifted correctly into 'NlpTrie'"); + + // InsertAt test (Middle shifting memory operation) + err = c_StringBuffer_InsertAt(&sb, 3, "_", 1); + RUN_TEST(err == C_ERR_OK, "InsertAt index 3 inserts an underscore character"); + RUN_TEST(strcmp(sb.buffer, "Nlp_Trie") == 0, "Memory shifted left/right flawlessly: 'Nlp_Trie'"); + + /* 4. Exponential Expansion Threshold Guard Check */ + // Pushing string past current internal storage boundaries to trigger C_ALLOC resizing + err = c_StringBuffer_Append(&sb, "_DataStructure", 14); + RUN_TEST(err == C_ERR_OK, "Forced exponential buffer reallocation with large string append"); + RUN_TEST(sb.size == 22, "Size correctly aggregated up to 22 bytes total"); + RUN_TEST(sb.capacity >= 23, "Capacity upscaled cleanly beyond its initial 4-byte threshold limit"); + RUN_TEST(strcmp(sb.buffer, "Nlp_Trie_DataStructure") == 0, "Post-reallocation string remains integrated and uncorrupted"); + + /* 5. Memory Shift Extraction Operations (RemoveAt) */ + // Current payload structure: "Nlp_Trie_DataStructure" + // Remove mid segment "_DataStructure" starting at index 8 + err = c_StringBuffer_RemoveAt(&sb, 8, 14); + RUN_TEST(err == C_ERR_OK, "RemoveAt clears middle segment '_DataStructure'"); + RUN_TEST(sb.size == 8, "Size downshifted cleanly to 8 bytes"); + RUN_TEST(strcmp(sb.buffer, "Nlp_Trie") == 0, "Character array closed gaps safely to hold 'Nlp_Trie'"); + + // Test automatic length clamping over boundary limit edge cases + err = c_StringBuffer_RemoveAt(&sb, 3, 50); // 50 overshoot actual size limits + RUN_TEST(err == C_ERR_OK, "RemoveAt automatically clamps requesting lengths overflowing edge limits"); + RUN_TEST(sb.size == 3, "Length updated down to index bounds"); + RUN_TEST(strcmp(sb.buffer, "Nlp") == 0, "Buffer contains truncated string 'Nlp'"); + + /* 6. Explicit String Wrapper Interfaces (AppendStr, PrependStr, InsertStrAt) */ + c_StringBuffer_Clear(&sb); + RUN_TEST(sb.size == 0 && sb.buffer[0] == '\0', "Clear flushes buffer structure size indicators cleanly"); + + err = c_StringBuffer_AppendStr(&sb, "Core"); + err |= c_StringBuffer_PrependStr(&sb, "C_"); + err |= c_StringBuffer_InsertStrAt(&sb, "Nlp", 2); // Insert "Nlp" into index 2 ("C_Core" -> "C_NlpCore") + + RUN_TEST(err == C_ERR_OK, "All explicit string wrapper functions evaluated with valid results"); + RUN_TEST(strcmp(sb.buffer, "C_NlpCore") == 0, "String wrapper cascade holds correct output value 'C_NlpCore'"); + + /* 7. Substring Extraction Pipeline Test (CopyTo) */ + // Test slice matching operations + err = c_StringBuffer_CopyTo(&sb, 2, 3, copy_target, sizeof(copy_target)); + RUN_TEST(err == C_ERR_OK, "CopyTo safely slices subset out to isolated external layout target"); + RUN_TEST(strcmp(copy_target, "Nlp") == 0, "External buffer extracted substring captures token 'Nlp' correctly"); + + // Test out of bounds inputs rejection properties + RUN_TEST(c_StringBuffer_CopyTo(&sb, 2, 3, copy_target, 2) == C_ERR_PARAM, "CopyTo blocks actions when external buffer is too small"); + RUN_TEST(c_StringBuffer_CopyTo(&sb, 999, 1, copy_target, sizeof(copy_target)) == C_ERR_PARAM, "CopyTo blocks crazy out of range index arguments"); + + /* 8. Multi-Byte UTF-8 String Asset Integrity Checks */ + c_StringBuffer_Clear(&sb); + err = c_StringBuffer_AppendStr(&sb, "语言"); + err |= c_StringBuffer_PrependStr(&sb, "自然"); + err |= c_StringBuffer_AppendStr(&sb, "处理"); // "自然语言处理" + + RUN_TEST(err == C_ERR_OK, "Piped raw multi-byte Chinese UTF-8 string tokens through buffer channels"); + RUN_TEST(strcmp(sb.buffer, "自然语言处理") == 0, "Raw multi-byte array matches validation configuration stream"); + + /* 9. Destruction Lifecycle Cleanliness Verification */ + c_StringBuffer_Destroy(&sb); + RUN_TEST(sb.buffer == NULL, "Array tracking pointer nullified successfully upon calling destructor"); + RUN_TEST(sb.size == 0 && sb.capacity == 0, "Structural trackers set to 0"); + + // Idempotency execution test sequence + c_StringBuffer_Destroy(&sb); + c_StringBuffer_Destroy(NULL); + printf("[TEST] Double string buffer destruction safety... \033[32mPASSED\033[0m\n"); + + printf("==================================================\n"); + printf("\033[32mSUCCESS: ALL C_STRINGBUFFER TESTS COMPLETED SUCCESSFULLY!\033[0m\n"); + printf("==================================================\n"); + + return C_ERR_OK; +} + +int main(int argc, char** argv){ + if (c_StringBuffer_UnitTest() != C_ERR_OK) { + return -1; + } + return 0; +} diff --git a/cKit/Foundation/c_Thread.c b/cKit/Foundation/c_Thread.c new file mode 100644 index 0000000..3884e29 --- /dev/null +++ b/cKit/Foundation/c_Thread.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Foundation/c_Thread.h b/cKit/Foundation/c_Thread.h new file mode 100644 index 0000000..7c98ec7 --- /dev/null +++ b/cKit/Foundation/c_Thread.h @@ -0,0 +1,125 @@ +#ifndef INCLUDED_C_THREAD_H +#define INCLUDED_C_THREAD_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef void* c_ThreadResult_t; + +#if defined(_WIN32) || defined(_WIN64) + #include + #include + typedef HANDLE c_Thread_t; + typedef DWORD c_ThreadId_t; // Windows 使用 DWORD 作为线程 ID + #define C_THREAD_FUNC_RETURN_TYPE unsigned __stdcall + #define C_THREAD_FUNC_RETURN_VTYPE unsigned +#else + #include + typedef pthread_t c_Thread_t; + typedef unsigned long c_ThreadId_t; // POSIX 转换为数字 ID + #define C_THREAD_FUNC_RETURN_TYPE void* + #define C_THREAD_FUNC_RETURN_VTYPE C_THREAD_FUNC_RETURN_TYPE +#endif + +// Universal thread creation signature +typedef C_THREAD_FUNC_RETURN_TYPE (*c_ThreadFn_t)(void*); + +C_STATIC_FORCE_INLINE +c_bool_t c_Thread_Create(c_Thread_t* thread, c_ThreadFn_t func, void* arg) { +#if defined(_WIN32) || defined(_WIN64) + // *thread = CreateThread(NULL, 0, func, arg, 0, NULL); + // return (*thread != NULL); + *thread = (HANDLE)_beginthreadex(NULL, 0, (unsigned (__stdcall *)(void*))func, arg, 0, NULL); + return (*thread != NULL); +#else + return (pthread_create(thread, NULL, func, arg) == 0); +#endif +} + +C_STATIC_FORCE_INLINE +void c_Thread_Join(c_Thread_t thread) { +#if defined(_WIN32) || defined(_WIN64) + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); +#else + pthread_join(thread, NULL); +#endif +} + +C_STATIC_FORCE_INLINE +void c_Thread_Sleep(unsigned int milliseconds) { + #if defined(_WIN32) || defined(_WIN64) + Sleep(milliseconds); + #else + usleep(milliseconds * 1000); + #endif +} + +C_STATIC_FORCE_INLINE +c_ThreadId_t c_Thread_SelfId(void) { +#if defined(_WIN32) || defined(_WIN64) + return GetCurrentThreadId(); +#else + return (c_ThreadId_t)pthread_self(); +#endif +} + +C_STATIC_FORCE_INLINE +c_bool_t c_Thread_Detach(c_Thread_t thread) { +#if defined(_WIN32) || defined(_WIN64) + // Windows 中,关闭线程句柄并不等于终止线程。 + // 它只是减少内核对象的引用计数。线程运行结束时,内核对象会自动销毁。 + // 这与 POSIX 的 detach 行为完全一致。 + if (thread != NULL) { + return (CloseHandle(thread) != 0); + } + return C_FALSE; +#else + // POSIX 直接使用 pthread_detach + return (pthread_detach(thread) == 0); +#endif +} + +C_STATIC_FORCE_INLINE +c_bool_t c_Thread_Equal(c_Thread_t t1, c_Thread_t t2) { +#if defined(_WIN32) || defined(_WIN64) + // Windows 下可以通过比较线程 ID 来判断是否为同一个线程 + // 即使其中一个是 GetCurrentThread() 产生的伪句柄,GetThreadId 也能正确识别 + return (GetThreadId(t1) == GetThreadId(t2)); +#else + // POSIX 提供专用的比较函数 + return (pthread_equal(t1, t2) != 0); +#endif +} + +C_STATIC_FORCE_INLINE +c_bool_t c_Thread_JoinWithResult(c_Thread_t thread, c_ThreadResult_t* out_result) { +#if defined(_WIN32) || defined(_WIN64) + if (WaitForSingleObject(thread, INFINITE) == WAIT_OBJECT_0) { + DWORD exit_code = 0; + if (GetExitCodeThread(thread, &exit_code)) { + if (out_result) { + // 将 DWORD 转换为指针类型输出 + *out_result = (c_ThreadResult_t)(uintptr_t)exit_code; + } + CloseHandle(thread); + return C_TRUE; + } + } + CloseHandle(thread); // 即使失败也尝试关闭句柄 + return C_FALSE; +#else + // POSIX 直接在 join 时传入指针变量的地址 + return (pthread_join(thread, out_result) == 0); +#endif +} + +#define c_Thread_Exit(x) return (C_THREAD_FUNC_RETURN_VTYPE)(x) + +#define c_Thread_Fn(fn) C_THREAD_FUNC_RETURN_TYPE fn + +#endif /*INCLUDED_C_THREAD_H*/ diff --git a/cKit/Foundation/c_ThreadPool.c b/cKit/Foundation/c_ThreadPool.c new file mode 100644 index 0000000..b70c4d6 --- /dev/null +++ b/cKit/Foundation/c_ThreadPool.c @@ -0,0 +1,122 @@ +#include +#include + +// Worker thread routine +static c_Thread_Fn(thread_pool_worker(void* arg)) { + c_ThreadPool_t* pool = (c_ThreadPool_t*)arg; + c_ThreadPoolTask_t* task; + while (!pool->is_shutdown) { + task = NULL; + + // Use a 500ms timeout for timedpop to allow periodic shutdown condition verification + if (c_LockQueue_TimedPop(&pool->task_queue, (void**)&task, pool->check_interval_ms)!=C_ERR_SUCCESS) { + if (task) { + if (task->function) { + // Execute user payload safely + task->function(task->argument); + } + // C_FREE(task); // Free the memory allocated for the task wrapper + c_Pool_Free(&pool->task_pool, task); + } + } + } + c_Thread_Exit(0); +} + +/** + * @brief Initializes a fixed-size thread pool. + */ +c_err_t c_ThreadPool_Init(c_ThreadPool_t* pool, int thread_count, int queue_capacity, int task_pool_size, int check_interval_ms) { + if (!pool || thread_count <= 0 || queue_capacity <= 0) return C_ERR_PARAM; + + pool->is_shutdown = C_FALSE; + pool->thread_count = thread_count; + pool->check_interval_ms = check_interval_ms; + + if (c_Pool_Init(&pool->task_pool, sizeof(c_ThreadPoolTask_t), task_pool_size)!=C_ERR_SUCCESS) { + return C_ERR_FAIL; + } + + // Allocate the thread handle array + pool->threads = (c_Thread_t*)C_ALLOC(sizeof(c_Thread_t) * thread_count); + if (!pool->threads) { + return C_ERR_NOMEM; + } + + // Initialize our previously constructed cross-platform thread-safe queue + c_err_t err = c_LockQueue_Init(&pool->task_queue, queue_capacity); + if (err!=C_ERR_OK) { + C_FREE(pool->threads); + return err; + } + + // Spawn the requested worker threads + for (int i = 0; i < thread_count; i++) { + if (!c_Thread_Create(&pool->threads[i], thread_pool_worker, pool)) { + // Rollback strategy on failures + pool->is_shutdown = C_TRUE; + c_LockQueue_Shutdown(&pool->task_queue); + for (int j = 0; j < i; j++) { + c_Thread_Join(pool->threads[j]); + } + c_LockQueue_Destroy(&pool->task_queue); + C_FREE(pool->threads); + return C_ERR_FAIL; + } + } + + return C_ERR_SUCCESS; +} + +/** + * @brief Submits a work payload to the pool. + */ +c_bool_t c_ThreadPool_Submit(c_ThreadPool_t* pool, void (*function)(void*), void* argument){ + if (!pool || !function || pool->is_shutdown) return C_FALSE; + + // c_ThreadPoolTask_t* task = (c_ThreadPoolTask_t*)C_ALLOC(sizeof(*task)); + c_ThreadPoolTask_t* task = c_Pool_Alloc(&pool->task_pool); + if (!task) return C_FALSE; + + task->function = function; + task->argument = argument; + + // Push the task into our thread-safe buffer. If full, this blocks the calling thread + if (c_LockQueue_Push(&pool->task_queue, task)!=C_ERR_SUCCESS) { + C_FREE(task); + return C_FALSE; + } + + return C_TRUE; +} + +/** + * @brief Orderly terminates the thread pool, waiting for running jobs to finish. + */ +void c_ThreadPool_Destroy(c_ThreadPool_t* pool) { + if (!pool) return; + + // 1. Terminate the processing loop + pool->is_shutdown = C_TRUE; + + // 2. Shut down the queue to unblock workers waiting indefinitely + c_LockQueue_Shutdown(&pool->task_queue); + + // 3. Join all worker threads safely + for (int i = 0; i < pool->thread_count; i++) { + c_Thread_Join(pool->threads[i]); + } + + // 4. Drain any remaining unexecuted tasks to prevent memory leaks + // void* unexecuted_task = NULL; + // while (c_LockQueue_Pop(&pool->task_queue, &unexecuted_task)==C_ERR_SUCCESS) { + // C_FREE(unexecuted_task); + // } + c_Pool_DryUp(&pool->task_pool); + c_Pool_Destroy(&pool->task_pool); + + // 5. Reclaim memory structures + c_LockQueue_Destroy(&pool->task_queue); + C_FREE(pool->threads); +} + diff --git a/cKit/Foundation/c_ThreadPool.h b/cKit/Foundation/c_ThreadPool.h new file mode 100644 index 0000000..248ce5c --- /dev/null +++ b/cKit/Foundation/c_ThreadPool.h @@ -0,0 +1,44 @@ +#ifndef INCLUDED_C_THREADPOOL_H +#define INCLUDED_C_THREADPOOL_H + +#ifndef INCLUDED_C_LOCKQUEUE_H +#include +#endif /*INCLUDED_C_LOCKQUEUE_H*/ + +#ifndef INCLUDED_C_THREAD_H +#include +#endif /*INCLUDED_C_THREAD_H*/ + +#ifndef INCLUDED_C_POOL_H +#include +#endif /*INCLUDED_C_POOL_H*/ + + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// Struct representing a single executable unit of work +typedef struct { + void (*function)(void*); // Pointer to the user function + void* argument; // Argument passed to the function +} c_ThreadPoolTask_t; + +typedef struct { + c_LockQueue_t task_queue; // Safe queue storing thread_pool_task_t pointers + c_Thread_t* threads; // Array of worker thread handles + int thread_count; // Total number of worker threads + volatile c_bool_t is_shutdown; // Shutdown flag + int check_interval_ms; + c_Pool_t task_pool; +} c_ThreadPool_t; + +// Public Core API +c_err_t c_ThreadPool_Init(c_ThreadPool_t* pool, int thread_count, int queue_capacity, int task_pool_size, int check_interval_ms); + +void c_ThreadPool_Destroy(c_ThreadPool_t* pool); + +c_bool_t c_ThreadPool_Submit(c_ThreadPool_t* pool, void (*function)(void*), void* argument); + + +#endif /*INCLUDED_C_THREADPOOL_H*/ diff --git a/cKit/Foundation/c_ThreadPool.t.c b/cKit/Foundation/c_ThreadPool.t.c new file mode 100644 index 0000000..be18b81 --- /dev/null +++ b/cKit/Foundation/c_ThreadPool.t.c @@ -0,0 +1,39 @@ +#include "c_ThreadPool.h" +#include +#include + +// Sample payload mimicking work +void compute_square(void* arg) { + int val = *(int*)arg; + printf("[Thread Pool Task] Processing square of %d = %d\n", val, val * val); + free(arg); // Free parameter memory passed during submission +} + + +int main(int argc, char** argv){ + // Create a pool with 4 worker threads and a max capacity of 20 pending tasks + c_ThreadPool_t pool = {0}; + c_ThreadPool_Init(&pool, 4, 20, 10, 500); + + printf("--- Thread Pool Initialized with 4 Workers ---\n"); + + // Queue 10 dynamic processing computations + for (int i = 1; i <= 10; i++) { + int* num = (int*)malloc(sizeof(int)); + *num = i; + if (!c_ThreadPool_Submit(&pool, compute_square, num)) { + fprintf(stderr, "[Thread Pool Task] Submit %d failed\n", i); + } + } + + // Force main to simulate work before cleaning up + printf("All tasks submitted. Waiting for processing to settle...\n"); + + c_Thread_Sleep(2000); + + printf("--- Destroying Thread Pool ---\n"); + c_ThreadPool_Destroy(&pool); + printf("Thread pool destroyed cleanly.\n"); + + return 0; +} diff --git a/cKit/Foundation/c_Vector.c b/cKit/Foundation/c_Vector.c new file mode 100644 index 0000000..3126777 --- /dev/null +++ b/cKit/Foundation/c_Vector.c @@ -0,0 +1,33 @@ +#include +#include + +/** + * Initialize an N-dimensional Euclidean Vector. + */ +c_err_t c_Vector_Init(c_Vector_t* vec, c_size_t dimensions) { + if (vec == NULL || dimensions == 0) return C_ERR_PARAM; + + vec->dimensions = dimensions; + vec->components = (double*)C_ALLOC(dimensions * sizeof(double)); + if (vec->components == NULL) return C_ERR_NOMEM; + + memset(vec->components, 0, dimensions * sizeof(double)); + return C_ERR_OK; +} + +/** + * Clean up allocations within the vector wrapper safely. + */ +void c_Vector_Destroy(c_Vector_t* vec) { + if (vec) { + if (vec->components) { + C_FREE(vec->components); + vec->components = NULL; + } + vec->dimensions = 0; + } +} + + + + diff --git a/cKit/Foundation/c_Vector.h b/cKit/Foundation/c_Vector.h new file mode 100644 index 0000000..f3931b0 --- /dev/null +++ b/cKit/Foundation/c_Vector.h @@ -0,0 +1,185 @@ +#ifndef INCLUDED_C_VECTOR_H +#define INCLUDED_C_VECTOR_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + double* components; // Contiguous array block tracking dimension coefficients + c_size_t dimensions; // Size of the coordinate dimension (N) +} c_Vector_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_Vector_Init(c_Vector_t* vec, c_size_t dimensions); + +void c_Vector_Destroy(c_Vector_t* vec); + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * Euclidean Vector Addition: res = v1 + v2 + * Time Complexity: O(N) | Auxiliary Space: O(1) + */ +C_STATIC_FORCE_INLINE +c_err_t c_Vector_Add(c_Vector_t* res, const c_Vector_t* v1, const c_Vector_t* v2) { + if (res == NULL || v1 == NULL || v2 == NULL) return C_ERR_PARAM; + if (v1->dimensions != v2->dimensions || v1->dimensions != res->dimensions) return C_ERR_FAIL; + + for (c_size_t i = 0; i < v1->dimensions; i++) { + res->components[i] = v1->components[i] + v2->components[i]; + } + return C_ERR_OK; +} + +C_STATIC_FORCE_INLINE +c_err_t c_Vector_Sub(c_Vector_t* res, const c_Vector_t* v1, const c_Vector_t* v2) { + if (res == NULL || v1 == NULL || v2 == NULL) return C_ERR_PARAM; + if (v1->dimensions != v2->dimensions || v1->dimensions != res->dimensions) return C_ERR_FAIL; + + for (c_size_t i = 0; i < v1->dimensions; i++) { + res->components[i] = v1->components[i] - v2->components[i]; + } + return C_ERR_OK; +} + +/** + * Dot Product (Scalar Product): v1 · v2 + * Time Complexity: O(N) + */ +C_STATIC_FORCE_INLINE +c_err_t c_Vector_DotProduct(const c_Vector_t* v1, const c_Vector_t* v2, double* out_scalar) { + if (v1 == NULL || v2 == NULL || out_scalar == NULL) return C_ERR_PARAM; + if (v1->dimensions != v2->dimensions) return C_ERR_FAIL; + + double dot = 0.0; + for (c_size_t i = 0; i < v1->dimensions; i++) { + dot += v1->components[i] * v2->components[i]; + } + + *out_scalar = dot; + return C_ERR_OK; +} + + +/** + * Calculates the Euclidean Magnitude (L2 Norm / Length): ||v|| = sqrt(v · v) + * Time Complexity: O(N) + */ +C_STATIC_FORCE_INLINE +double c_Vector_Magnitude(const c_Vector_t* vec) { + if (vec == NULL || vec->dimensions == 0) return 0.0; + + double sum_sq = 0.0; + for (c_size_t i = 0; i < vec->dimensions; i++) { + sum_sq += vec->components[i] * vec->components[i]; + } + return sqrt(sum_sq); +} + + + +/** + * Vector Normalization (Unit Vector Scaling): v_hat = v / ||v|| + * Time Complexity: O(N) + */ +C_STATIC_FORCE_INLINE +c_err_t c_Vector_Normalize(c_Vector_t* vec) { + if (vec == NULL || vec->dimensions == 0) return C_ERR_PARAM; + + double mag = c_Vector_Magnitude(vec); + if (mag == 0.0) return C_ERR_FAIL; // Prevent division-by-zero on zero-vectors + + for (c_size_t i = 0; i < vec->dimensions; i++) { + vec->components[i] /= mag; + } + return C_ERR_OK; +} + +/** + * Vector Scalar Multiplication (In-place Scaling): v = alpha * v + * Time Complexity: O(N) | Auxiliary Space: O(1) + * @param vec Pointer to the Euclidean vector instance. + * @param alpha The scalar scaling factor coefficient. + * @return C_ERR_OK if successful, C_ERR_PARAM if vec or components are NULL. + */ +C_STATIC_FORCE_INLINE +c_err_t c_Vector_Scale(c_Vector_t* vec, double alpha) { + if (vec == NULL || vec->components == NULL) return C_ERR_PARAM; + + // Linear unrolled loops pass across N-dimensional coordinates + for (c_size_t i = 0; i < vec->dimensions; i++) { + vec->components[i] *= alpha; + } + + return C_ERR_OK; +} + +/** + * Calculates the Euclidean Distance between two vectors: result = ||a - b|| + * Time Complexity: O(N) | Auxiliary Space: O(1) in-place + * @param a Pointer to the first vector instance. + * @param b Pointer to the second vector instance. + * @param result Pointer to the destination variable where the scalar distance is written. + * @return C_ERR_OK if successful, C_ERR_PARAM for NULL targets, + * or C_ERR_FAIL for unequal dimensions. + */ +C_STATIC_FORCE_INLINE +c_err_t c_Vector_DistanceTo(const c_Vector_t* a, const c_Vector_t* b, double* result) { + if (a == NULL || b == NULL || result == NULL) return C_ERR_PARAM; + if (a->components == NULL || b->components == NULL) return C_ERR_PARAM; + if (a->dimensions != b->dimensions) return C_ERR_FAIL; + + double sum_sq_diff = 0.0; + + // Linear unrolled coordinate passes tracking differences across N-dimensions + for (c_size_t i = 0; i < a->dimensions; i++) { + double diff = a->components[i] - b->components[i]; + sum_sq_diff += diff * diff; + } + + *result = sqrt(sum_sq_diff); + return C_ERR_OK; +} + +/** + * Calculates the direction (unit vector) of a given vector: result = vector / ||vector|| + * Time Complexity: O(N) | Auxiliary Space: O(1) in-place + * @param result Pointer to the destination vector where the direction components are written. + * @param vector Pointer to the source input vector. + * @return C_ERR_OK if successful, C_ERR_PARAM for NULL targets, + * or C_ERR_INVALID for unequal dimensions or zero-magnitude origin vectors. + */ +C_STATIC_FORCE_INLINE +c_err_t c_Vector_Direction(c_Vector_t* result, const c_Vector_t* vector) { + if (result == NULL || vector == NULL) return C_ERR_PARAM; + if (result->components == NULL || vector->components == NULL) return C_ERR_PARAM; + if (result->dimensions != vector->dimensions) return C_ERR_INVALID; + + // Step 1: Calculate the magnitude of the input vector (L2 Norm) + double sum_sq = 0.0; + for (c_size_t i = 0; i < vector->dimensions; i++) { + double val = vector->components[i]; + sum_sq += val * val; + } + double magnitude = sqrt(sum_sq); + + // Step 2: Prevent division-by-zero on origin vectors (magnitude == 0) + if (magnitude == 0.0) return C_ERR_INVALID; + + // Step 3: Compute the unit vector direction components + for (c_size_t i = 0; i < vector->dimensions; i++) { + result->components[i] = vector->components[i] / magnitude; + } + + return C_ERR_OK; +} + +#endif /*INCLUDED_C_VECTOR_H*/ diff --git a/cKit/Foundation/c_Vector_Direction.t.c b/cKit/Foundation/c_Vector_Direction.t.c new file mode 100644 index 0000000..a085384 --- /dev/null +++ b/cKit/Foundation/c_Vector_Direction.t.c @@ -0,0 +1,86 @@ +#include "c_Vector.h" +#include +#include + +#include +#include +#include +#include + +// Your updated line tracing diagnostic macro +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \ + return C_FALSE; \ + } \ + } while(0) + +// Helper macro for double comparisons with floating-point tolerance +#define EXPECT_NEAR(actual, expected, tolerance, msg) \ + do { \ + if (fabs((actual) - (expected)) > (tolerance)) { \ + printf(" [X] Assert Failed: %s (Expected %f, got %f) %s:%d\n", msg, (double)(expected), (double)(actual), __FILE__, __LINE__); \ + return C_FALSE; \ + } \ + } while(0) + +// External references to previous core modules +extern c_err_t c_Vector_Init(c_Vector_t* vec, c_size_t dimensions); +extern double c_Vector_Magnitude(const c_Vector_t* vec); +extern void c_Vector_Destroy(c_Vector_t* vec); + +c_bool_t test_vector_direction_ops(void) { + c_Vector_t v_in, v_out; + c_size_t dims = 3; // Test standard 3D spatial space + + EXPECT_EQ(c_Vector_Init(&v_in, dims), C_ERR_OK, "v_in initialization failed"); + EXPECT_EQ(c_Vector_Init(&v_out, dims), C_ERR_OK, "v_out initialization failed"); + + // Load components for a standard 3D Pythagorean combination: v_in = [0.0, -3.0, 4.0] + // Magnitude ||v_in|| = sqrt(0 + (-3)^2 + 4^2) = 5.0 + v_in.components[0] = 0.0; + v_in.components[1] = -3.0; + v_in.components[2] = 4.0; + + // Test Case 1: Param Parameter Enforcement Boundary Checks + EXPECT_EQ(c_Vector_Direction(NULL, &v_in), C_ERR_PARAM, "NULL output vector pointer guard missed"); + EXPECT_EQ(c_Vector_Direction(&v_out, NULL), C_ERR_PARAM, "NULL input vector pointer guard missed"); + + // Test Case 2: Standard Vector Direction Evaluation + // Expected result: v_out = [0/5, -3/5, 4/5] = [0.0, -0.6, 0.8] + EXPECT_EQ(c_Vector_Direction(&v_out, &v_in), C_ERR_OK, "Valid direction mapping computation failed"); + EXPECT_NEAR(v_out.components[0], 0.0, 1e-6, "Unit vector direction X component incorrect"); + EXPECT_NEAR(v_out.components[1], -0.6, 1e-6, "Unit vector direction Y component incorrect"); + EXPECT_NEAR(v_out.components[2], 0.8, 1e-6, "Unit vector direction Z component incorrect"); + + // Verify that the resulting direction vector has a magnitude of exactly 1.0 + double unit_mag = c_Vector_Magnitude(&v_out); + EXPECT_NEAR(unit_mag, 1.0, 1e-6, "Resulting direction vector magnitude is not equal to 1.0"); + + // Test Case 3: Division-by-Zero Guard Check (Origin/Zero Vector test) + c_Vector_t v_zero; + c_Vector_Init(&v_zero, dims); // Component allocations default initialized to 0.0 + EXPECT_EQ(c_Vector_Direction(&v_out, &v_zero), C_ERR_INVALID, "Origin zero-vector magnitude fallback missing"); + + // Test Case 4: Mismatched Dimension Error Check + c_Vector_t v_bad; + c_Vector_Init(&v_bad, 2); // 2D vector mismatch + EXPECT_EQ(c_Vector_Direction(&v_bad, &v_in), C_ERR_INVALID, "Dimension mismatch boundary guard skipped filter"); + + c_Vector_Destroy(&v_in); + c_Vector_Destroy(&v_out); + c_Vector_Destroy(&v_zero); + c_Vector_Destroy(&v_bad); + return C_TRUE; +} + +int main(void) { + printf("=== Starting Framework Verification: c_Vector_Direction ===\n"); + if (test_vector_direction_ops()) { + printf(" [PASS] Euclidean Direction Unit Vector Resolution Engine Verified Successfully.\n"); + } else { + printf(" [FAIL] Vector Algebra Pipeline Mismatches Intercepted.\n"); + } + return 0; +} diff --git a/cKit/Foundation/c_Vector_DistanceTo.t.c b/cKit/Foundation/c_Vector_DistanceTo.t.c new file mode 100644 index 0000000..c3b4eb7 --- /dev/null +++ b/cKit/Foundation/c_Vector_DistanceTo.t.c @@ -0,0 +1,83 @@ +#include "c_Vector.h" +#include +#include + +#include +#include +#include +#include + +// Your updated line tracing diagnostic macro +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \ + return C_FALSE; \ + } \ + } while(0) + +// Helper macro for double comparisons with floating-point tolerance +#define EXPECT_NEAR(actual, expected, tolerance, msg) \ + do { \ + if (fabs((actual) - (expected)) > (tolerance)) { \ + printf(" [X] Assert Failed: %s (Expected %f, got %f) %s:%d\n", msg, (double)(expected), (double)(actual), __FILE__, __LINE__); \ + return C_FALSE; \ + } \ + } while(0) + +// External references to previous core modules +extern c_err_t c_Vector_Init(c_Vector_t* vec, c_size_t dimensions); +extern void c_Vector_Destroy(c_Vector_t* vec); + +c_bool_t test_vector_distance_out_param(void) { + c_Vector_t v1, v2; + c_size_t dims = 3; // Test standard 3D spatial space + double dist_res = 0.0; + + EXPECT_EQ(c_Vector_Init(&v1, dims), C_ERR_OK, "v1 initialization failed"); + EXPECT_EQ(c_Vector_Init(&v2, dims), C_ERR_OK, "v2 initialization failed"); + + // Load components: v1 = [7.0, 4.0, 3.0] + v1.components[0] = 7.0; + v1.components[1] = 4.0; + v1.components[2] = 3.0; + + // Load components: v2 = [17.0, 12.0, 5.0] + v2.components[0] = 17.0; + v2.components[1] = 12.0; + v2.components[2] = 5.0; + + // Test Case 1: Param Parameter Enforcement Boundary Checks + EXPECT_EQ(c_Vector_DistanceTo(NULL, &v2, &dist_res), C_ERR_PARAM, "NULL pointer parameter guard missed"); + EXPECT_EQ(c_Vector_DistanceTo(&v1, &v2, NULL), C_ERR_PARAM, "NULL output variable pointer guard missed"); + + // Test Case 2: Standard Spatial Vector Distance Evaluation + // Variance = (17-7)^2 + (12-4)^2 + (5-3)^2 = 10^2 + 8^2 + 2^2 = 168. + // Distance = sqrt(168) ≈ 12.961481 + EXPECT_EQ(c_Vector_DistanceTo(&v1, &v2, &dist_res), C_ERR_OK, "Valid distance mapping computation failed"); + EXPECT_NEAR(dist_res, sqrt(168.0), 1e-6, "Euclidean space calculation returned mathematically inaccurate value"); + + // Test Case 3: Identity Distance Verification (d(v, v) == 0.0) + EXPECT_EQ(c_Vector_DistanceTo(&v1, &v1, &dist_res), C_ERR_OK, "Identity calculation crashed unexpectedly"); + EXPECT_NEAR(dist_res, 0.0, 1e-6, "Identity space computation returned non-zero value"); + + // Test Case 4: Mismatched Dimension Vector Spaces Guard Error Check + c_Vector_t v_bad; + c_Vector_Init(&v_bad, 6); // 6D vector + EXPECT_EQ(c_Vector_DistanceTo(&v1, &v_bad, &dist_res), C_ERR_FAIL, "Dimension mismatch fallback channel skipped filter"); + + c_Vector_Destroy(&v1); + c_Vector_Destroy(&v2); + c_Vector_Destroy(&v_bad); + return C_TRUE; +} + +int main(void) { + printf("=== Starting Framework Verification: c_Vector_DistanceTo (Out Param) ===\n"); + if (test_vector_distance_out_param()) { + printf(" [PASS] Euclidean Out-Param Distance Resolution Engine Verified Successfully.\n"); + } else { + printf(" [FAIL] Distance Matrix Pipeline Processing Mismatches Detected.\n"); + } + return 0; +} diff --git a/cKit/Foundation/c_utf8.c b/cKit/Foundation/c_utf8.c new file mode 100644 index 0000000..37b7147 --- /dev/null +++ b/cKit/Foundation/c_utf8.c @@ -0,0 +1,817 @@ +#include + +c_size_t c_utf8_strlen(const char* str) { + if (!str) return 0; + + c_size_t char_count = 0; + c_size_t i = 0; + + while (str[i] != '\0') { + i += c_utf8_char_len(str[i]); // 跳过当前字符占用的全部字节 + char_count++; + } + + return char_count; +} + +const char* c_utf8_strchr(const char* str, const char* utf8_char) { + if (!str || !utf8_char || utf8_char[0] == '\0') return NULL; + + c_size_t target_bytes = c_utf8_char_len(utf8_char[0]); + c_size_t i = 0; + + while (str[i] != '\0') { + c_size_t curr_bytes = c_utf8_char_len(str[i]); + + // 当且仅当两个字符占用的字节数相同,且多字节内容完全一致时匹配成功 + if (curr_bytes == target_bytes) { + if (memcmp(&str[i], utf8_char, target_bytes) == 0) { + return &str[i]; + } + } + i += curr_bytes; // 移动到下一个 UTF-8 字符 + } + + return NULL; +} + +char* c_utf8_strncpy(char* dest, const char* src, c_size_t char_num) { + if (!dest || !src || char_num == 0) return dest; + + c_size_t src_idx = 0; + c_size_t dest_idx = 0; + c_size_t copied_chars = 0; + + while (src[src_idx] != '\0' && copied_chars < char_num) { + c_size_t char_bytes = c_utf8_char_len(src[src_idx]); + + // 批量精确拷贝当前完整字符的 N 个字节 + memcpy(&dest[dest_idx], &src[src_idx], char_bytes); + + src_idx += char_bytes; + dest_idx += char_bytes; + copied_chars++; + } + + // 兼容 strncpy 标准:如果源串长度小于 char_num,则用 '\0' 填充剩余的空隙 + // 注意:这里的剩余空隙在实际工程中通常按字节填充更安全 + dest[dest_idx] = '\0'; + + return dest; +} + +int c_utf8_strncmp(const char* str1, const char* str2, c_size_t char_num) { + if (!str1 || !str2 || char_num == 0) return 0; + + c_size_t idx1 = 0; + c_size_t idx2 = 0; + c_size_t compared_chars = 0; + + while (compared_chars < char_num) { + // 任意一端到达末尾 + if (str1[idx1] == '\0' || str2[idx2] == '\0') { + return (int)((unsigned char)str1[idx1] - (unsigned char)str2[idx2]); + } + + c_size_t len1 = c_utf8_char_len(str1[idx1]); + c_size_t len2 = c_utf8_char_len(str2[idx2]); + + // 如果单字长字节不相等,直接根据当前字符进行排序比较 + if (len1 != len2) { + return (int)((unsigned char)str1[idx1] - (unsigned char)str2[idx2]); + } + + // 长度相同时,直接比较当前单个 UTF-8 字符的内容 + int res = memcmp(&str1[idx1], &str2[idx2], len1); + if (res != 0) { + return res; + } + + idx1 += len1; + idx2 += len2; + compared_chars++; + } + + return 0; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +char* c_utf8_tolower(char* str) { + if (!str) return NULL; + + c_size_t i = 0; + while (str[i] != '\0') { + unsigned char b1 = (unsigned char)str[i]; + c_size_t len = c_utf8_char_len(str[i]); + + // Case A: Standard Single-byte ASCII Case Folding + if (len == 1) { + if (b1 >= 'A' && b1 <= 'Z') { + str[i] = (char)(b1 + 32); + } + } + // Case B: Double-byte UTF-8 Case Folding (e.g., Cyrillic / Greek scripts) + else if (len == 2) { + unsigned char b2 = (unsigned char)str[i + 1]; + + // Cyrillic script transformations (Capital letters: 0xD0 0x80 to 0xD0 0xBF) + if (b1 == 0xD0) { + if (b2 >= 0x90 && b2 <= 0xAF) { + // Shift to lowercase variant range located inside 0xD0 / 0xD1 blocks + str[i + 1] = (char)(b2 + 0x20); + } else if (b2 >= 0xB0 && b2 <= 0xBF) { + str[i] = (char)0xD1; + str[i + 1] = (char)(b2 - 0x20); + } + } + // Greek script transformations (Capital letters: 0xCE 0x91 to 0xCE 0xAB) + else if (b1 == 0xCE) { + if (b2 >= 0x91 && b2 <= 0xAB && b2 != 0xA2) { // 0xA2 is a special variant + // Shift down to lowercase variant range block inside 0xCE / 0xCF + if (b2 <= 0x9F) { + str[i + 1] = (char)(b2 + 0x20); + } else { + str[i] = (char)0xCF; + str[i + 1] = (char)(b2 - 0x20); + } + } + } + } + // Multi-byte Chinese ideographs (3 bytes) and Emojis (4 bytes) lack casing concepts; step past them + i += len; + } + + return str; +} + + +char* c_utf8_toupper(char* str) { + if (!str) return NULL; + + c_size_t i = 0; + while (str[i] != '\0') { + unsigned char b1 = (unsigned char)str[i]; + c_size_t len = c_utf8_char_len(str[i]); + + // Case A: Standard Single-byte ASCII Case Folding + if (len == 1) { + if (b1 >= 'a' && b1 <= 'z') { + str[i] = (char)(b1 - 32); + } + } + // Case B: Double-byte UTF-8 Case Folding (e.g., Cyrillic / Greek scripts) + else if (len == 2) { + unsigned char b2 = (unsigned char)str[i + 1]; + + // Cyrillic script transformations (Lowercase letters: 0xD0 0xB0 to 0xD0 0xBF, and 0xD1 0x80 to 0xD1 0x8F) + if (b1 == 0xD0) { + if (b2 >= 0xB0 && b2 <= 0xBF) { + // Shift up to uppercase variant range located inside the 0xD0 block + str[i + 1] = (char)(b2 - 0x20); + } + } else if (b1 == 0xD1) { + if (b2 >= 0x80 && b2 <= 0x8F) { + // Convert leading byte from 0xD1 back to 0xD0 and realign low byte + str[i] = (char)0xD0; + str[i + 1] = (char)(b2 + 0x20); + } + } + // Greek script transformations (Lowercase letters: 0xCE 0xB1 to 0xCE 0xBF, and 0xCF 0x80 to 0xCF 0x8B) + else if (b1 == 0xCE) { + if (b2 >= 0xB1 && b2 <= 0xBF) { + // Shift down to uppercase variant range block inside 0xCE + str[i + 1] = (char)(b2 - 0x20); + } + } else if (b1 == 0xCF) { + if (b2 >= 0x80 && b2 <= 0x8B) { + // Convert leading byte from 0xCF back to 0xCE and realign low byte + str[i] = (char)0xCE; + str[i + 1] = (char)(b2 + 0x20); + } + } + } + // 3-byte characters (Chinese Ideographs) and 4-byte characters (Emojis) lack casing concepts; jump past them safely + i += len; + } + + return str; +} + + +char* c_utf8_strcat(char* dest, const char* src) { + if (!dest || !src) return dest; + + // Locate the termination boundary point of the original destination array + c_size_t dest_idx = 0; + while (dest[dest_idx] != '\0') { + dest_idx++; + } + + // Continuously append source bytes until hitting the terminator character + c_size_t src_idx = 0; + while (src[src_idx] != '\0') { + dest[dest_idx++] = src[src_idx++]; + } + + // Force secure terminal character sealing + dest[dest_idx] = '\0'; + + return dest; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +const char* c_utf8_strstr(const char* haystack, const char* needle) { + if (!haystack || !needle) return NULL; + + // An empty needle matches the beginning of the haystack per standard strstr specification + if (needle[0] == '\0') { + return haystack; + } + + c_size_t h_idx = 0; + while (haystack[h_idx] != '\0') { + c_size_t n_idx = 0; + c_size_t current_match_idx = h_idx; + + // Perform byte-by-byte substring evaluation from the current boundary anchor + while (haystack[current_match_idx] != '\0' && needle[n_idx] != '\0' && + haystack[current_match_idx] == needle[n_idx]) { + current_match_idx++; + n_idx++; + } + + // If we successfully traversed the entire needle string, a match is found + if (needle[n_idx] == '\0') { + return &haystack[h_idx]; + } + + // Advance to the next valid UTF-8 character point in the haystack + h_idx += c_utf8_char_len(haystack[h_idx]); + } + + return NULL; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ +#include + +const char* c_utf8_strrchr(const char* str, const char* utf8_char) { + if (!str || !utf8_char || utf8_char[0] == '\0') return NULL; + + c_size_t target_bytes = c_utf8_char_len(utf8_char[0]); + c_size_t i = 0; + const char* last_match = NULL; + + while (str[i] != '\0') { + c_size_t curr_bytes = c_utf8_char_len(str[i]); + + // Continuous linear check, updating our tracker to keep the furthest matched offset + if (curr_bytes == target_bytes) { + if (memcmp(&str[i], utf8_char, target_bytes) == 0) { + last_match = &str[i]; + } + } + i += curr_bytes; // Jump forward explicitly across whole multi-byte characters + } + + return last_match; +} + +/** + * @brief Internal helper to verify if a specific UTF-8 character pointer matches any delimiter in the list. + */ +C_STATIC_FORCE_INLINE +c_bool_t c_utf8_is_delim(const char* current_char, const char* delims, c_size_t* delim_len) { + c_size_t d_idx = 0; + c_size_t c_len = c_utf8_char_len(*current_char); + + while (delims[d_idx] != '\0') { + c_size_t d_len = c_utf8_char_len(delims[d_idx]); + if (c_len == d_len && memcmp(current_char, &delims[d_idx], c_len) == 0) { + *delim_len = d_len; + return C_TRUE; + } + d_idx += d_len; + } + return C_FALSE; +} + +char* c_utf8_strtok(char* str, const char* delims, char** saveptr) { + if (!delims || !saveptr) return NULL; + + // Use our saved pointer context if str is passed as NULL + char* token_cursor = (str != NULL) ? str : *saveptr; + if (!token_cursor || *token_cursor == '\0') { + return NULL; + } + + // Step 1: Skip over any leading delimiter sequences to locate the token start + c_size_t skip_len = 0; + while (*token_cursor != '\0' && c_utf8_is_delim(token_cursor, delims, &skip_len)) { + token_cursor += skip_len; + } + + // If we hit the absolute end of the input string while skipping delims, no tokens exist + if (*token_cursor == '\0') { + *saveptr = token_cursor; + return NULL; + } + + char* token_start = token_cursor; + + // Step 2: Track forward to locate the terminal delimiter boundary of this token + while (*token_cursor != '\0') { + c_size_t next_char_len = c_utf8_char_len(*token_cursor); + c_size_t match_delim_len = 0; + + if (c_utf8_is_delim(token_cursor, delims, &match_delim_len)) { + // Found a boundary delimiter! Overwrite its leading byte with a null terminator + *token_cursor = '\0'; + // Save state context tracking pointing immediately past the clipped delimiter + *saveptr = token_cursor + match_delim_len; + return token_start; + } + token_cursor += next_char_len; + } + + // If we reached the end of the text string naturally, ensure the saveptr updates to string termination + *saveptr = token_cursor; + return token_start; +} + + +/** + * @brief Internal helper to return the uppercase variant value of a single ASCII or 2-byte UTF-8 character. + * Returns the original trailing byte structure if no case mapping applies. + */ +C_STATIC_FORCE_INLINE +void c_utf8_fold_char(const char* src, size_t len, unsigned char* out_b1, unsigned char* out_b2) { + *out_b1 = (unsigned char)src[0]; + *out_b2 = (len > 1) ? (unsigned char)src[1] : 0; + + // Single-byte ASCII case folding + if (len == 1) { + if (*out_b1 >= 'a' && *out_b1 <= 'z') { + *out_b1 -= 32; + } + } + // Double-byte UTF-8 case folding (Cyrillic & Greek scripts) + else if (len == 2) { + // Cyrillic script: 0xD0 0xB0...0xBF to 0xD0 0x90...0x9F; 0xD1 0x80...0x8F to 0xD0 0xA0...0xAF + if (*out_b1 == 0xD0) { + if (*out_b2 >= 0xB0 && *out_b2 <= 0xBF) { + *out_b2 -= 0x20; + } + } else if (*out_b1 == 0xD1) { + if (*out_b2 >= 0x80 && *out_b2 <= 0x8F) { + *out_b1 = 0xD0; + *out_b2 += 0x20; + } + } + // Greek script: 0xCE 0xB1...0xBF to 0xCE 0x91...0x9F; 0xCF 0x80...0x8B to 0xCE 0xA0...0xAB + else if (*out_b1 == 0xCE) { + if (*out_b2 >= 0xB1 && *out_b2 <= 0xBF) { + *out_b2 -= 0x20; + } + } else if (*out_b1 == 0xCF) { + if (*out_b2 >= 0x80 && *out_b2 <= 0x8B) { + *out_b1 = 0xCE; + *out_b2 += 0x20; + } + } + } +} + +int c_utf8_strncasecmp(const char* str1, const char* str2, c_size_t char_num) { + if (!str1 || !str2 || char_num == 0) return 0; + + c_size_t idx1 = 0; + c_size_t idx2 = 0; + c_size_t compared_chars = 0; + + while (compared_chars < char_num) { + // Handle termination boundaries gracefully + if (str1[idx1] == '\0' || str2[idx2] == '\0') { + return (int)((unsigned char)str1[idx1] - (unsigned char)str2[idx2]); + } + + c_size_t len1 = c_utf8_char_len(str1[idx1]); + c_size_t len2 = c_utf8_char_len(str2[idx2]); + + // Fold characters to uppercase form for comparison + unsigned char f1_b1, f1_b2; + unsigned char f2_b1, f2_b2; + c_utf8_fold_char(&str1[idx1], len1, &f1_b1, &f1_b2); + c_utf8_fold_char(&str2[idx2], len2, &f2_b1, &f2_b2); + + // Compare first bytes or script widths + if (f1_b1 != f2_b1) { + return (int)f1_b1 - (int)f2_b1; + } + + // Compare second bytes (relevant for 2-byte sequences) + if (f1_b2 != f2_b2) { + return (int)f1_b2 - (int)f2_b2; + } + + // For 3-byte (Chinese) or 4-byte characters, fallback to raw memory comparison if lead bytes matched + if (len1 > 2) { + if (len1 != len2) { + return (int)len1 - (int)len2; + } + int raw_res = memcmp(&str1[idx1], &str2[idx2], len1); + if (raw_res != 0) { + return raw_res; + } + } + + // Advance iteration offsets + idx1 += len1; + idx2 += len2; + compared_chars++; + } + + return 0; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_utf8_to_unicode(const char* str, c_ucs4_t* out_codepoint, c_size_t* out_bytes_consumed) { + if (!str || *str == '\0' || !out_codepoint || !out_bytes_consumed) { + return C_ERR_PARAM; + } + + unsigned char b1 = (unsigned char)str[0]; + c_size_t len = c_utf8_char_len(str[0]); + c_ucs4_t cp = 0; + + // Case 1: 1-Byte ASCII (0xxxxxxx) + if (len == 1) { + if (b1 >= 0x80) return C_ERR_PARAM; // Guard against malformed lead bytes + cp = b1; + } + // Case 2: 2-Byte Sequence (110xxxxx 10xxxxxx) + else if (len == 2) { + unsigned char b2 = (unsigned char)str[1]; + if ((b2 & 0xC0) != 0x80) return C_ERR_PARAM; // Validate continuation byte + + cp = ((b1 & 0x1F) << 6) | (b2 & 0x3F); + if (cp < 0x80) return C_ERR_PARAM; // Overlong encoding defense + } + // Case 3: 3-Byte Sequence (1110xxxx 10xxxxxx 10xxxxxx) + else if (len == 3) { + unsigned char b2 = (unsigned char)str[1]; + unsigned char b3 = (unsigned char)str[2]; + if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) return C_ERR_PARAM; + + cp = ((b1 & 0x0F) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F); + if (cp < 0x0800) return C_ERR_PARAM; // Overlong encoding defense + if (cp >= 0xD800 && cp <= 0xDFFF) return C_ERR_PARAM; // Surrogate pairs rejection + } + // Case 4: 4-Byte Sequence (11110xxx 10xxxxxx 10xxxxxx 10xxxxxx) + else if (len == 4) { + unsigned char b2 = (unsigned char)str[1]; + unsigned char b3 = (unsigned char)str[2]; + unsigned char b4 = (unsigned char)str[3]; + if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80 || (b4 & 0xC0) != 0x80) return C_ERR_PARAM; + + cp = ((b1 & 0x07) << 18) | ((b2 & 0x3F) << 12) | ((b3 & 0x3F) << 6) | (b4 & 0x3F); + if (cp < 0x010000) return C_ERR_PARAM; // Overlong encoding defense + } + else { + return C_ERR_PARAM; + } + + // Limit validation (Unicode max standard is U+10FFFF) + if (cp > 0x10FFFF) { + return C_ERR_PARAM; + } + + *out_codepoint = cp; + *out_bytes_consumed = len; + return C_ERR_OK; +} + + +c_err_t c_utf8_from_unicode(c_ucs4_t codepoint, char* dest_buffer, c_size_t* out_bytes_written) { + if (!dest_buffer || !out_bytes_written) { + return C_ERR_PARAM; + } + + // Reject out-of-range codepoints or UTF-16 surrogate pairs (reserved for UTF-16 only) + if (codepoint > 0x10FFFF || (codepoint >= 0xD800 && codepoint <= 0xDFFF)) { + return C_ERR_PARAM; + } + + // Case 1: Standard ASCII range (U+0000 to U+007F) -> Requires 1 byte + if (codepoint <= 0x7F) { + dest_buffer[0] = (char)codepoint; + *out_bytes_written = 1; + } + // Case 2: U+0080 to U+07FF -> Requires 2 bytes + else if (codepoint <= 0x7FF) { + dest_buffer[0] = (char)(0xC0 | ((codepoint >> 6) & 0x1F)); + dest_buffer[1] = (char)(0x80 | (codepoint & 0x3F)); + *out_bytes_written = 2; + } + // Case 3: U+0800 to U+FFFF -> Requires 3 bytes (Handles most Chinese characters) + else if (codepoint <= 0xFFFF) { + dest_buffer[0] = (char)(0xE0 | ((codepoint >> 12) & 0x0F)); + dest_buffer[1] = (char)(0x80 | ((codepoint >> 6) & 0x3F)); + dest_buffer[2] = (char)(0x80 | (codepoint & 0x3F)); + *out_bytes_written = 3; + } + // Case 4: U+10000 to U+10FFFF -> Requires 4 bytes (Handles Emojis and ancient scripts) + else { + dest_buffer[0] = (char)(0xF0 | ((codepoint >> 18) & 0x07)); + dest_buffer[1] = (char)(0x80 | ((codepoint >> 12) & 0x3F)); + dest_buffer[2] = (char)(0x80 | ((codepoint >> 6) & 0x3F)); + dest_buffer[3] = (char)(0x80 | (codepoint & 0x3F)); + *out_bytes_written = 4; + } + + // Securely seal the local buffer layout array with a trailing null terminator + dest_buffer[*out_bytes_written] = '\0'; + + return C_ERR_OK; +} + + +c_err_t c_utf8_to_unicode_array(const char* str, c_ucs4_t* dest_array, c_size_t array_capacity, c_size_t* out_chars_written) { + if (!str || !dest_array || !out_chars_written) { + return C_ERR_PARAM; + } + + c_size_t src_idx = 0; + c_size_t chars_count = 0; + + while (str[src_idx] != '\0') { + // Enforce array capacity threshold constraints + if (chars_count >= array_capacity) { + *out_chars_written = chars_count; + return C_ERR_PARAM; // Destination array is too small to fit the remaining string + } + + c_ucs4_t cp = 0; + c_size_t bytes_consumed = 0; + + // Decode the single character point via your core decoding function + c_err_t err = c_utf8_to_unicode(&str[src_idx], &cp, &bytes_consumed); + if (err != C_ERR_OK) { + *out_chars_written = chars_count; + return err; // Propagate the malformed stream error up + } + + dest_array[chars_count++] = cp; + src_idx += bytes_consumed; + } + + *out_chars_written = chars_count; + return C_ERR_OK; +} + +c_err_t c_utf8_from_unicode_array(const c_ucs4_t* src_array, c_size_t src_array_len, char* dest_buffer, c_size_t dest_capacity, c_size_t* out_bytes_written) { + if (!src_array || !dest_buffer || !out_bytes_written) { + return C_ERR_PARAM; + } + + c_size_t dest_idx = 0; + + for (c_size_t i = 0; i < src_array_len; i++) { + char temp_char_buf[5]; // Temporary standalone slot buffer + c_size_t bytes_written = 0; + + // Encode single code point state back to byte wrappers + c_err_t err = c_utf8_from_unicode(src_array[i], temp_char_buf, &bytes_written); + if (err != C_ERR_OK) { + *out_bytes_written = dest_idx; + return err; + } + + // Verify if destination capacity bounds can hold the new character block (+1 for terminal null) + if (dest_idx + bytes_written + 1 > dest_capacity) { + *out_bytes_written = dest_idx; + dest_buffer[dest_idx] = '\0'; // Gracefully terminate the current chunk before failing + return C_ERR_PARAM; + } + + // Copy raw encoded data bytes into our tracking stream layout + memcpy(dest_buffer + dest_idx, temp_char_buf, bytes_written); + dest_idx += bytes_written; + } + + // Force strict trailing character termination closure + dest_buffer[dest_idx] = '\0'; + *out_bytes_written = dest_idx; + + return C_ERR_OK; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +c_err_t c_utf8_to_utf16(const char* str, c_uint16_t* dest_array, c_size_t array_capacity, c_size_t* out_units_written) { + if (!str || !dest_array || !out_units_written) { + return C_ERR_PARAM; + } + + c_size_t src_idx = 0; + c_size_t units_count = 0; + + while (str[src_idx] != '\0') { + c_ucs4_t cp = 0; + c_size_t bytes_consumed = 0; + + // 1. Decode the UTF-8 sequence into a Unicode codepoint + c_err_t err = c_utf8_to_unicode(&str[src_idx], &cp, &bytes_consumed); + if (err != C_ERR_OK) { + *out_units_written = units_count; + return err; + } + + // 2. Encode the codepoint into UTF-16 + if (cp <= 0xFFFF) { + // BMP Range: Requires exactly one 16-bit code unit + if (units_count + 1 >= array_capacity) { + *out_units_written = units_count; + return C_ERR_PARAM; // Out of bounds + } + dest_array[units_count++] = (c_uint16_t)cp; + } else { + // Supplementary Planes (Astral): Requires a surrogate pair (two 16-bit units) + if (units_count + 2 >= array_capacity) { + *out_units_written = units_count; + return C_ERR_PARAM; // Out of bounds + } + cp -= 0x10000; + dest_array[units_count++] = (c_uint16_t)(0xD800 | ((cp >> 10) & 0x3FF)); // High Surrogate + dest_array[units_count++] = (c_uint16_t)(0xDC00 | (cp & 0x3FF)); // Low Surrogate + } + + src_idx += bytes_consumed; + } + + // Append the trailing null terminator to make it a valid UTF-16 string + if (units_count < array_capacity) { + dest_array[units_count] = 0; + } else { + *out_units_written = units_count; + return C_ERR_PARAM; + } + + *out_units_written = units_count; + return C_ERR_OK; +} + +c_err_t c_utf8_from_utf16(const c_uint16_t* src_array, c_size_t src_array_len, char* dest_buffer, c_size_t dest_capacity, c_size_t* out_bytes_written) { + if (!src_array || !dest_buffer || !out_bytes_written) { + return C_ERR_PARAM; + } + + c_size_t src_idx = 0; + c_size_t dest_idx = 0; + + while (src_idx < src_array_len) { + c_ucs4_t cp = 0; + c_uint16_t u1 = src_array[src_idx++]; + + // 1. Decode UTF-16 to Unicode codepoint + if (u1 >= 0xD800 && u1 <= 0xDBFF) { + // High surrogate detected, look ahead for the matching low surrogate + if (src_idx >= src_array_len) { + *out_bytes_written = dest_idx; + return C_ERR_PARAM; // Truncated/Malformed surrogate pair + } + c_uint16_t u2 = src_array[src_idx++]; + if (u2 < 0xDC00 || u2 > 0xDFFF) { + *out_bytes_written = dest_idx; + return C_ERR_PARAM; // Missing or invalid low surrogate + } + cp = (((u1 & 0x3FF) << 10) | (u2 & 0x3FF)) + 0x10000; + } else if (u1 >= 0xDC00 && u1 <= 0xDFFF) { + // Isolated low surrogate is invalid in a lead position + *out_bytes_written = dest_idx; + return C_ERR_PARAM; + } else { + // Normal BMP character + cp = u1; + } + + // 2. Encode the Unicode codepoint back into the destination UTF-8 buffer + char temp_buf[5]; + c_size_t bytes_written = 0; + c_err_t err = c_utf8_from_unicode(cp, temp_buf, &bytes_written); + if (err != C_ERR_OK) { + *out_bytes_written = dest_idx; + return err; + } + + // Verify if destination capacity bounds can hold the new block (+1 for terminal null) + if (dest_idx + bytes_written + 1 > dest_capacity) { + *out_bytes_written = dest_idx; + dest_buffer[dest_idx] = '\0'; + return C_ERR_PARAM; // Overflow protection + } + + memcpy(dest_buffer + dest_idx, temp_buf, bytes_written); + dest_idx += bytes_written; + } + + // Force strict trailing character termination closure + dest_buffer[dest_idx] = '\0'; + *out_bytes_written = dest_idx; + + return C_ERR_OK; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_utf16_swap_endian(c_uint16_t* utf16_array, c_size_t length) { + if (!utf16_array) { + return C_ERR_PARAM; + } + + for (c_size_t i = 0; i < length; i++) { + c_uint16_t value = utf16_array[i]; + // Bitwise swap: (value >> 8) extracts the high byte, (value << 8) extracts the low byte + utf16_array[i] = (c_uint16_t)(((value & 0x00FF) << 8) | ((value & 0xFF00) >> 8)); + } + + return C_ERR_OK; +} + + +c_err_t c_utf16_to_unicode(const c_uint16_t* src_units, c_size_t src_capacity, c_ucs4_t* out_codepoint, c_size_t* out_units_read) { + if (!src_units || src_capacity == 0 || !out_codepoint || !out_units_read) { + return C_ERR_PARAM; + } + + c_uint16_t u1 = src_units[0]; + + // Case 1: High Surrogate Point Detection (0xD800 to 0xDBFF) + if (u1 >= 0xD800 && u1 <= 0xDBFF) { + if (src_capacity < 2) { + return C_ERR_PARAM; // Truncated sequence: expected a matching low surrogate + } + + c_uint16_t u2 = src_units[1]; + if (u2 < 0xDC00 || u2 > 0xDFFF) { + return C_ERR_PARAM; // Malformed sequence: missing a valid trailing low surrogate + } + + // Reconstruct Astral Plane Codepoint via formula: ((High - 0xD800) << 10) + (Low - 0xDC00) + 0x10000 + *out_codepoint = (c_ucs4_t)((((u1 & 0x3FF) << 10) | (u2 & 0x3FF)) + 0x10000); + *out_units_read = 2; + } + // Case 2: Isolated Low Surrogate Error Check (0xDC00 to 0xDFFF) + else if (u1 >= 0xDC00 && u1 <= 0xDFFF) { + return C_ERR_PARAM; // Isolated low surrogate is mathematically invalid in a lead position + } + // Case 3: Standard BMP Range Character + else { + *out_codepoint = (c_ucs4_t)u1; + *out_units_read = 1; + } + + return C_ERR_OK; +} + +c_err_t c_utf16_from_unicode(c_ucs4_t codepoint, c_uint16_t* dest_units, c_size_t dest_capacity, c_size_t* out_units_written) { + if (!dest_units || dest_capacity == 0 || !out_units_written) { + return C_ERR_PARAM; + } + + // Limit Validation: Reject invalid Astral values or illegal UTF-16 surrogate codepoint blocks + if (codepoint > 0x10FFFF || (codepoint >= 0xD800 && codepoint <= 0xDFFF)) { + return C_ERR_PARAM; + } + + // Case 1: BMP Range (U+0000 to U+FFFF) -> Requires 1 code unit + if (codepoint <= 0xFFFF) { + dest_units[0] = (c_uint16_t)codepoint; + *out_units_written = 1; + } + // Case 2: Supplementary Planes (U+10000 to U+10FFFF) -> Requires 2 code units (Surrogate Pair) + else { + if (dest_capacity < 2) { + return C_ERR_PARAM; // Insufficient buffer capacity + } + + c_ucs4_t adjusted = codepoint - 0x10000; + dest_units[0] = (c_uint16_t)(0xD800 | ((adjusted >> 10) & 0x3FF)); // High Surrogate + dest_units[1] = (c_uint16_t)(0xDC00 | (adjusted & 0x3FF)); // Low Surrogate + *out_units_written = 2; + } + + return C_ERR_OK; +} + + diff --git a/cKit/Foundation/c_utf8.h b/cKit/Foundation/c_utf8.h new file mode 100644 index 0000000..87983ab --- /dev/null +++ b/cKit/Foundation/c_utf8.h @@ -0,0 +1,225 @@ +#ifndef INCLUDED_C_UTF8_H +#define INCLUDED_C_UTF8_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/* --- Unicode Codepoint Type Definition --- */ +typedef uint32_t c_ucs4_t; // UCS-4 / UTF-32 representation for a single Unicode Codepoint +typedef uint16_t c_uint16_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * @brief 获取一个 UTF-8 字符在当前指针位置所占用的实际字节数 (1 ~ 4 字节) + */ +C_STATIC_FORCE_INLINE c_size_t c_utf8_char_len(char leading_byte) { + unsigned char b = (unsigned char)leading_byte; + if (b < 0x80) return 1; // 单字节 ASCII: 0xxxxxxx + if ((b & 0xE0) == 0xC0) return 2; // 双字节字符: 110xxxxx + if ((b & 0xF0) == 0xE0) return 3; // 三字节字符(大部分汉字): 1110xxxx + if ((b & 0xF8) == 0xF0) return 4; // 四字节字符(Emoji等): 11110xxx + return 1; // 非法 UTF-8 引导字节,防御性返回 1 防止死循环 +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * @brief 计算一个 UTF-8 字符串的有效字形数(字符数),而非字节数 (兼容 strlen) + */ +c_size_t c_utf8_strlen(const char* str); + +/** + * @brief 查找字符在 UTF-8 字符串中第一次出现的位置 (兼容 strchr) + * @param str 源字符串 + * @param utf8_char 待查找的 UTF-8 字符(支持多字节字符,如 "中") + * @return const char* 指向找到的第一个字节的指针,未找到返回 NULL + */ +const char* c_utf8_strchr(const char* str, const char* utf8_char); + +/** + * @brief 复制指定字形数量的 UTF-8 字符串 (兼容 strncpy) + * @note 能够完美感知 UTF-8 字符边界,绝不会切断汉字,且自动在末尾补 '\0' + * @param dest 目标缓冲区 + * @param src 源字符串 + * @param char_num 要复制的 UTF-8 字符/字形数量 + * @return char* 指向目标缓冲区 dest 的指针 + */ +char* c_utf8_strncpy(char* dest, const char* src, c_size_t char_num); + +/** + * @brief 比较两个 UTF-8 字符串的前 n 个字符 (兼容 strncmp) + * @param str1 字符串 1 + * @param str2 字符串 2 + * @param char_num 要比较的 UTF-8 字符/字形数量 + * @return int 小于 0、等于 0 或大于 0 + */ +int c_utf8_strncmp(const char* str1, const char* str2, c_size_t char_num); + + +/** + * @brief Converts a UTF-8 character string to lowercase in-place. + * Supports standard ASCII case folding and common multi-byte scripts. + * @param str Pointer to the mutable null-terminated UTF-8 string. + * @return char* Pointer to the original string. + */ +char* c_utf8_tolower(char* str); + +/** + * @brief Converts a UTF-8 character string to uppercase in-place. + * Supports standard ASCII case folding and common multi-byte scripts. + * @param str Pointer to the mutable null-terminated UTF-8 string. + * @return char* Pointer to the original string. + */ +char* c_utf8_toupper(char* str); + +/** + * @brief Appends the source UTF-8 string to the destination string buffer (Compatible with strcat). + * @param dest Pointer to the null-terminated destination buffer. + * @param src Pointer to the null-terminated source string. + * @return char* Pointer to the destination string destination pointer. + */ +char* c_utf8_strcat(char* dest, const char* src); + +/** + * @brief Finds the first occurrence of a substring in a UTF-8 string (Compatible with strstr). + * @param haystack The null-terminated UTF-8 string to scan. + * @param needle The null-terminated UTF-8 substring to search for. + * @return const char* Pointer to the first byte of the matched substring in haystack, or NULL if not found. + */ +const char* c_utf8_strstr(const char* haystack, const char* needle); + +/** + * @brief Finds the last occurrence of a specific character in a UTF-8 string (Compatible with strrchr). + * @param str The null-terminated UTF-8 string to scan. + * @param utf8_char The null-terminated UTF-8 character string to find (can be a multi-byte sequence like "中"). + * @return const char* Pointer to the last occurrence of the matched character in str, or NULL if not found. + */ +const char* c_utf8_strrchr(const char* str, const char* utf8_char); + +/** + * @brief Tokenizes a string into a series of tokens based on multiple multi-byte delimiters. + * This function is thread-safe and reentrant, operating similarly to POSIX strtok_r. + * @param str The mutable UTF-8 string to tokenize. Pass NULL on subsequent calls. + * @param delims A raw byte sequence containing multi-byte UTF-8 delimiters. + * @param saveptr A user-allocated tracking pointer to maintain state context across consecutive calls. + * @return char* Pointer to the beginning of the next valid token, or NULL when no more tokens are found. + */ +char* c_utf8_strtok(char* str, const char* delims, char** saveptr); + +/** + * @brief Compares two UTF-8 strings case-insensitively up to a specified number of characters. + * @param str1 Pointer to the first null-terminated UTF-8 string. + * @param str2 Pointer to the second null-terminated UTF-8 string. + * @param char_num Maximum number of UTF-8 characters (codepoints) to compare. + * @return int An integer less than, equal to, or greater than zero if str1 is found, + * respectively, to be less than, to match, or be greater than str2. + */ +int c_utf8_strncasecmp(const char* str1, const char* str2, c_size_t char_num); + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * @brief Converts the next UTF-8 byte sequence at a given pointer into a single Unicode Codepoint. + * @param str Pointer to the current position in a null-terminated UTF-8 string. + * @param out_codepoint Pointer to the destination where the decoded Unicode integer is saved. + * @param out_bytes_consumed Pointer to save the number of source bytes processed (1 to 4). + * @return c_err_t C_ERR_OK on success, or C_ERR_PARAM on invalid/corrupted UTF-8 byte streams. + */ +c_err_t c_utf8_to_unicode(const char* str, c_ucs4_t* out_codepoint, c_size_t* out_bytes_consumed); + + +/** + * @brief Encodes a single Unicode Codepoint (UCS-4) into a destination UTF-8 byte array. + * @param codepoint The source Unicode integer character point to encode. + * @param dest_buffer Pointer to a char array buffer (must have at least 5 bytes capacity). + * @param out_bytes_written Pointer to save the number of encoded bytes stored in dest_buffer. + * @return c_err_t C_ERR_OK on success, or C_ERR_PARAM if the codepoint is out of valid Unicode ranges or parameters are NULL. + */ +c_err_t c_utf8_from_unicode(c_ucs4_t codepoint, char* dest_buffer, c_size_t* out_bytes_written); + +/** + * @brief Decodes an entire null-terminated UTF-8 string into an array of Unicode Codepoints. + * @param str Pointer to the null-terminated source UTF-8 string. + * @param dest_array Pointer to the destination array where decoded codepoints will be stored. + * @param array_capacity Maximum number of elements that dest_array can hold. + * @param out_chars_written Pointer to save the total number of codepoints successfully stored. + * @return c_err_t C_ERR_OK on success, C_ERR_PARAM on invalid/NULL parameters or if the destination array capacity is exceeded. + */ +c_err_t c_utf8_to_unicode_array(const char* str, c_ucs4_t* dest_array, c_size_t array_capacity, c_size_t* out_chars_written); + +/** + * @brief Encodes an array of Unicode Codepoints back into a null-terminated UTF-8 byte stream. + * @param src_array Pointer to the source array of Unicode codepoints. + * @param src_array_len The number of codepoint elements inside src_array to process. + * @param dest_buffer Pointer to the destination char buffer. + * @param dest_capacity Maximum byte capacity of the destination buffer (including room for '\0'). + * @param out_bytes_written Pointer to save the total number of bytes written to dest_buffer (excluding '\0'). + * @return c_err_t C_ERR_OK on success, C_ERR_PARAM on invalid/NULL parameters or if dest_capacity is exceeded. + */ +c_err_t c_utf8_from_unicode_array(const c_ucs4_t* src_array, c_size_t src_array_len, char* dest_buffer, c_size_t dest_capacity, c_size_t* out_bytes_written); + +/** + * @brief Converts an entire null-terminated UTF-8 string into an array of UTF-16 code units. + * @param str Pointer to the null-terminated source UTF-8 string. + * @param dest_array Pointer to the destination array where UTF-16 code units will be stored. + * @param array_capacity Maximum number of 16-bit elements that dest_array can hold. + * @param out_units_written Pointer to save the total number of UTF-16 code units successfully stored (excluding terminal '\0'). + * @return c_err_t C_ERR_OK on success, C_ERR_PARAM on invalid/NULL parameters or if capacity is exceeded. + */ +c_err_t c_utf8_to_utf16(const char* str, c_uint16_t* dest_array, c_size_t array_capacity, c_size_t* out_units_written); + +/** + * @brief Converts an array of UTF-16 code units back into a null-terminated UTF-8 byte stream. + * @param src_array Pointer to the source array of UTF-16 code units. + * @param src_array_len The number of 16-bit elements inside src_array to process. + * @param dest_buffer Pointer to the destination char buffer. + * @param dest_capacity Maximum byte capacity of the destination buffer (including room for '\0'). + * @param out_bytes_written Pointer to save the total number of bytes written to dest_buffer (excluding '\0'). + * @return c_err_t C_ERR_OK on success, C_ERR_PARAM on invalid/NULL parameters, malformed surrogates, or if dest_capacity is exceeded. + */ +c_err_t c_utf8_from_utf16(const c_uint16_t* src_array, c_size_t src_array_len, char* dest_buffer, c_size_t dest_capacity, c_size_t* out_bytes_written); + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * @brief Swaps the byte order (endianness) of a UTF-16 string array in-place. + * @param utf16_array Pointer to the source/destination array of UTF-16 code units. + * @param length The number of 16-bit elements inside the array to process. + * @return c_err_t C_ERR_OK on success, or C_ERR_PARAM if the array pointer is NULL. + */ +c_err_t c_utf16_swap_endian(c_uint16_t* utf16_array, c_size_t length); + + +/** + * @brief Decodes a UTF-16 character stream starting at a given pointer into a single Unicode codepoint. + * @param src_units Pointer to the current code unit position in a UTF-16 array. + * @param src_capacity Remaining elements left available to read inside the source array. + * @param out_codepoint Pointer to the destination where the decoded Unicode integer is saved. + * @param out_units_read Pointer to save the number of 16-bit code units processed (1 for BMP, 2 for Surrogate Pairs). + * @return c_err_t C_ERR_OK on success, or C_ERR_PARAM on malformed surrogate sequences or missing parameters. + */ +c_err_t c_utf16_to_unicode(const c_uint16_t* src_units, c_size_t src_capacity, c_ucs4_t* out_codepoint, c_size_t* out_units_read); + +/** + * @brief Encodes a single Unicode codepoint into a target UTF-16 array buffer. + * @param codepoint The source Unicode integer character point to encode. + * @param dest_units Pointer to the destination 16-bit code unit array buffer. + * @param dest_capacity Maximum number of 16-bit elements the destination buffer can accept. + * @param out_units_written Pointer to save the number of 16-bit code units generated (1 or 2). + * @return c_err_t C_ERR_OK on success, or C_ERR_PARAM if the codepoint is invalid or destination space is insufficient. + */ +c_err_t c_utf16_from_unicode(c_ucs4_t codepoint, c_uint16_t* dest_units, c_size_t dest_capacity, c_size_t* out_units_written); + + + +#endif /*INCLUDED_C_UTF8_H*/ diff --git a/cKit/Foundation/c_utf8.t.c b/cKit/Foundation/c_utf8.t.c new file mode 100644 index 0000000..aedcaec --- /dev/null +++ b/cKit/Foundation/c_utf8.t.c @@ -0,0 +1,302 @@ +#include "c_utf8.h" +#include +#include + +/* --- 单元测试模块集成 --- */ +#define RUN_TEST(test_case, name) \ + do { \ + printf("[RUN] %s... ", name); \ + if (test_case) { \ + printf("\033[32mPASSED\033[0m\n"); \ + } else { \ + printf("\033[31mFAILED\033[0m (%s:%d)\n", __FILE__, __LINE__); \ + return C_ERR_FAIL; \ + } \ + } while(0) + +c_err_t c_Utf8String_UnitTest(void) { + printf("==================================================\n"); + printf(" STARTING C_UTF8STRING UNIT TESTING \n"); + printf("==================================================\n"); + + const char* sample = "NLP大模型_2026"; // 包含 3个大写英文、3个汉字、1个下划线、4个数字 = 11个字符 + + /* 1. c_utf8_strlen 测试 */ + // 传统的 strlen(sample) 会返回 3 + 3*3 + 1 + 4 = 17 字节 + RUN_TEST(c_utf8_strlen(sample) == 11, "c_utf8_strlen correctly counts character points"); + RUN_TEST(c_utf8_strlen("") == 0, "c_utf8_strlen handles empty strings"); + + /* 2. c_utf8_strchr 测试 */ + const char* find_eng = c_utf8_strchr(sample, "P"); + const char* find_chn = c_utf8_strchr(sample, "模"); + const char* find_none = c_utf8_strchr(sample, "国"); + + RUN_TEST(find_eng != NULL && *find_eng == 'P', "c_utf8_strchr locate ASCII element"); + // "模" 在 "模型_2026" 头部,其后紧跟 "型" + RUN_TEST(find_chn != NULL && strncmp(find_chn, "模型", 6) == 0, "c_utf8_strchr locate multi-byte Chinese word"); + RUN_TEST(find_none == NULL, "c_utf8_strchr returns NULL for non-existing chars"); + + /* 3. c_utf8_strncpy 安全截断测试 */ + char dest_buf[64]; + // 截断前 5 个字符 -> "NLP大模" (绝不会出现半个汉字或乱码断裂) + c_utf8_strncpy(dest_buf, sample, 5); + RUN_TEST(c_utf8_strlen(dest_buf) == 5, "c_utf8_strncpy slices correct character width"); + RUN_TEST(strcmp(dest_buf, "NLP大模") == 0, "c_utf8_strncpy safe boundary isolation checked"); + + /* 4. c_utf8_strncmp 字符匹配测试 */ + RUN_TEST(c_utf8_strncmp("自然语言", "自然选择", 2) == 0, "c_utf8_strncmp matches first 2 shared Chinese words"); + RUN_TEST(c_utf8_strncmp("自然语言", "自然选择", 3) != 0, "c_utf8_strncmp detects variance at 3rd word slot"); + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + /* 5. Lowercase Transform Verification (c_utf8_tolower) */ + char case_buf[64] = "NLP大模型_2026_Go!"; + c_utf8_tolower(case_buf); + RUN_TEST(strcmp(case_buf, "nlp大模型_2026_go!") == 0, "c_utf8_tolower translates ASCII while isolating Chinese layout characters"); + + // Cyrillic multi-byte letter test ("П" -> 0xD0 0x9F converted down to "п" -> 0xD0 0xBF) + char cyrillic_buf[8] = { (char)0xD0, (char)0x9F, '\0' }; + c_utf8_tolower(cyrillic_buf); + RUN_TEST((unsigned char)cyrillic_buf[1] == 0xBF, "c_utf8_tolower successfully transforms multi-byte Cyrillic characters"); + + /* 6. Concatenation Verification (c_utf8_strcat) */ + char cat_dest[32] = "自然"; + c_utf8_strcat(cat_dest, "语言"); + RUN_TEST(strcmp(cat_dest, "自然语言") == 0, "c_utf8_strcat appends string tokens cleanly"); + RUN_TEST(c_utf8_strlen(cat_dest) == 4, "Post-concatenation size checks out at 4 characters total"); + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + + /* 24. Uppercase Transform Verification (c_utf8_toupper) */ + char upper_buf[] = "nlp大模型_2026_go!"; + c_utf8_toupper(upper_buf); + RUN_TEST(strcmp(upper_buf, "NLP大模型_2026_GO!") == 0, "c_utf8_toupper translates ASCII while isolating Chinese layout characters"); + + // Cyrillic multi-byte lowercase letter test ("п" -> 0xD0 0xBF converted up to "П" -> 0xD0 0x9F) + char cyr_upper_buf[] = { (char)0xD0, (char)0xBF, '\0' }; + c_utf8_toupper(cyr_upper_buf); + RUN_TEST((unsigned char)cyr_upper_buf[1] == 0x9F, "c_utf8_toupper successfully transforms multi-byte Cyrillic lowercase characters"); + + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + + /* 7. Character Scanning Verification (c_utf8_strchr) */ + const char* scan_base = "NLP大模型_2026"; + + /* 8. Substring Scanning Verification (c_utf8_strstr) */ + const char* match_str = c_utf8_strstr(scan_base, "大模型"); + const char* miss_str = c_utf8_strstr(scan_base, "小模型"); + const char* empty_str = c_utf8_strstr(scan_base, ""); + + RUN_TEST(match_str != NULL && strncmp(match_str, "大模型_2026", 14) == 0, "c_utf8_strstr fetches multi-byte string locations"); + RUN_TEST(miss_str == NULL, "c_utf8_strstr returns NULL cleanly on substring mismatch"); + RUN_TEST(empty_str == scan_base, "c_utf8_strstr returns parent head context given empty needle input"); + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + + /* 16. Reverse Scanning Verification (c_utf8_strrchr) */ + const char* r_base = "自然_模型_自然_2026"; + const char* last_match = c_utf8_strrchr(r_base, "自然"); + const char* first_match = c_utf8_strchr(r_base, "自然"); + + RUN_TEST(last_match != NULL && last_match != first_match, "c_utf8_strrchr skips the first match to pull the last occurrence"); + RUN_TEST(strncmp(last_match, "自然_2026", 11) == 0, "c_utf8_strrchr locates the correct trailing block address"); + + /* 17. Reentrant Tokenization Verification (c_utf8_strtok) */ + char token_source[] = ",NLP,,大模型,2026,"; // Multi-byte Chinese comma delimiters + const char* delimiters = ","; + char* save_context = NULL; + + // First Call + char* token = c_utf8_strtok(token_source, delimiters, &save_context); + RUN_TEST(token != NULL && strcmp(token, "NLP") == 0, "c_utf8_strtok parses the first token and skips leading delims"); + + // Second Call (Pass NULL to proceed) + token = c_utf8_strtok(NULL, delimiters, &save_context); + RUN_TEST(token != NULL && strcmp(token, "大模型") == 0, "c_utf8_strtok correctly extracts multi-byte Chinese '大模型'"); + + // Third Call + token = c_utf8_strtok(NULL, delimiters, &save_context); + RUN_TEST(token != NULL && strcmp(token, "2026") == 0, "c_utf8_strtok extracts '2026'"); + + // Fourth Call - Termination + token = c_utf8_strtok(NULL, delimiters, &save_context); + RUN_TEST(token == NULL, "c_utf8_strtok returns NULL cleanly when parsing is finished"); + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + + /* 25. Case-Insensitive Bounded Comparison Verification (c_utf8_strncasecmp) */ + // Test Case 1: Simple matching mixed-case strings + RUN_TEST(c_utf8_strncasecmp("Nlp大模型", "NLP大模型", 6) == 0, "c_utf8_strncasecmp matches mixed cases up to 6 characters"); + + // Test Case 2: Verification of prefix character restrictions bounding + RUN_TEST(c_utf8_strncasecmp("NLP大模型_v2", "nlp大模型_v3", 6) == 0, "c_utf8_strncasecmp returns 0 if differences fall past the character count limit"); + RUN_TEST(c_utf8_strncasecmp("NLP大模型_v2", "nlp大模型_v3", 9) != 0, "c_utf8_strncasecmp registers structural variance when character limits cover differences"); + + // Test Case 3: Mixed language case sorting behavior + RUN_TEST(c_utf8_strncasecmp("自然语言NLP", "自然语言nlp", 7) == 0, "c_utf8_strncasecmp handles matching trailing ASCII case differences after multi-byte blocks"); + + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + + /* 18. UTF-8 to Unicode Codepoint Decoding Verification */ + const char* utf8_src = "中!🚀"; // "中" (3 bytes), "!" (1 byte), "🚀" Emoji (4 bytes) + c_ucs4_t cp = 0; + c_size_t bytes_step = 0; + + // Decode first character ("中" -> Expected Codepoint: U+4E2D) + c_err_t err = c_utf8_to_unicode(utf8_src, &cp, &bytes_step); + RUN_TEST(err == C_ERR_OK && bytes_step == 3, "c_utf8_to_unicode processes 3-byte Chinese characters"); + RUN_TEST(cp == 0x4E2D, "Decoded codepoint matches U+4E2D ('中') accurately"); + + // Decode next sequence ("!" -> Expected Codepoint: U+0021) + err = c_utf8_to_unicode(utf8_src + bytes_step, &cp, &bytes_step); + RUN_TEST(err == C_ERR_OK && bytes_step == 1, "c_utf8_to_unicode processes 1-byte ASCII markers"); + RUN_TEST(cp == 0x0021, "Decoded codepoint matches U+0021 ('!')"); + + // Decode next sequence (Rocket Emoji "🚀" -> Expected Codepoint: U+1F680) + err = c_utf8_to_unicode(utf8_src + 4, &cp, &bytes_step); // Skip forward 4 bytes total + RUN_TEST(err == C_ERR_OK && bytes_step == 4, "c_utf8_to_unicode decodes 4-byte astral plane emojis"); + RUN_TEST(cp == 0x1F680, "Decoded codepoint matches U+1F680 ('🚀')"); + + /* 19. Unicode Codepoint to UTF-8 Encoding Verification */ + char encode_buf[8]; + c_size_t written_len = 0; + + // Encode U+4E2D back to UTF-8 + err = c_utf8_from_unicode(0x4E2D, encode_buf, &written_len); + RUN_TEST(err == C_ERR_OK && written_len == 3, "c_utf8_from_unicode encodes U+4E2D back into 3 bytes"); + RUN_TEST(strcmp(encode_buf, "中") == 0, "Encoded string content matches '中' flawlessly"); + + // Encode U+1F680 back to UTF-8 + err = c_utf8_from_unicode(0x1F680, encode_buf, &written_len); + RUN_TEST(err == C_ERR_OK && written_len == 4, "c_utf8_from_unicode encodes U+1F680 back into 4 bytes"); + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + + /* 20. UTF-8 String Stream <-> Unicode Array Conversions Verification */ + const char* mixed_sentence = "NLP大模型!🚀"; // Length: 3 ASCII, 3 Chinese (9B), 1 ASCII, 1 Emoji (4B) = 8 characters total + c_ucs4_t uni_array[16]; + c_size_t total_chars = 0; + + // Test Point 1: Decode stream into codepoint container array + err = c_utf8_to_unicode_array(mixed_sentence, uni_array, 16, &total_chars); + RUN_TEST(err == C_ERR_OK && total_chars == 8, "c_utf8_to_unicode_array maps complex string streams into separate integer points"); + RUN_TEST(uni_array[0] == 'N' && uni_array[3] == 0x5927 && uni_array[7] == 0x1F680, "Decoded array positions hold proper character points ('N', '大', '🚀')"); + + // Test Point 2: Trigger array capacity guard protection + c_ucs4_t tight_array[4]; + err = c_utf8_to_unicode_array(mixed_sentence, tight_array, 4, &total_chars); + RUN_TEST(err == C_ERR_PARAM && total_chars == 4, "c_utf8_to_unicode_array safely blocks operations and returns current progress on buffer limit hits"); + + // Test Point 3: Reverse operation - Encode codepoint array back into native UTF-8 string layout + char reconstructed_str[64]; + c_size_t written_bytes = 0; + err = c_utf8_from_unicode_array(uni_array, 8, reconstructed_str, sizeof(reconstructed_str), &written_bytes); + RUN_TEST(err == C_ERR_OK && written_bytes == 17, "c_utf8_from_unicode_array successfully packs codepoints back into 17 raw bytes"); + RUN_TEST(strcmp(reconstructed_str, mixed_sentence) == 0, "Reconstructed stream data matches original expression perfectly"); + + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + + /* 21. UTF-8 <-> UTF-16 Array Conversions Verification */ + const char* utf8_sentence = "NLP大模型!🚀"; // Contains ASCII, 3-byte Chinese, and a 4-byte Astral Plane Emoji + c_uint16_t utf16_array[32]; + c_size_t units_written = 0; + + // Test Point 1: Convert UTF-8 stream to UTF-16 code units + // "NLP" (3 units) + "大模型" (3 units) + "!" (1 unit) + "🚀" (Surrogate pair = 2 units) = 9 units total + err = c_utf8_to_utf16(utf8_sentence, utf16_array, 32, &units_written); + RUN_TEST(err == C_ERR_OK && units_written == 9, "c_utf8_to_utf16 successfully packs characters including astral planes"); + RUN_TEST(utf16_array[0] == 'N' && utf16_array[3] == 0x5927, "Verify standard BMP mapping inside UTF-16 array structure"); + // Verify high and low surrogate points for the Rocket Emoji 🚀 + RUN_TEST(utf16_array[7] == 0xD83D && utf16_array[8] == 0xDE80, "Verify surrogate pair matching (0xD83D 0xDE80) for U+1F680"); + + // Test Point 2: Convert UTF-16 array back to native UTF-8 string layout + char reconstructed_utf8[64]; + c_size_t bytes_written_utf8 = 0; + err = c_utf8_from_utf16(utf16_array, units_written, reconstructed_utf8, sizeof(reconstructed_utf8), &bytes_written_utf8); + RUN_TEST(err == C_ERR_OK && bytes_written_utf8 == 17, "c_utf8_from_utf16 unpacks units back into 17 raw bytes"); + RUN_TEST(strcmp(reconstructed_utf8, utf8_sentence) == 0, "Reconstructed UTF-8 matches the original string precisely"); + + // Test Point 3: Malformed Surrogate Pair Detection + c_uint16_t malformed_utf16[] = { 0xD83D, 'A' }; // High surrogate followed by a literal letter (invalid) + char error_buf[16]; + c_size_t err_bytes = 0; + err = c_utf8_from_utf16(malformed_utf16, 2, error_buf, sizeof(error_buf), &err_bytes); + RUN_TEST(err == C_ERR_PARAM, "c_utf8_from_utf16 successfully flags and rejects malformed/orphaned surrogate code units"); + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + + /* 22. UTF-16 Endianness Byte-Swapping Verification (c_utf16_swap_endian) */ + c_uint16_t sample_utf16[] = { 0xD83D, 0xDE80, 0x5927 }; // 🚀 and 大 in standard host endianness + c_size_t array_len = sizeof(sample_utf16) / sizeof(sample_utf16[0]); + + // Test Point 1: Guard against NULL parameters + RUN_TEST(c_utf16_swap_endian(NULL, array_len) == C_ERR_PARAM, "c_utf16_swap_endian safely rejects NULL pointer arrays"); + + // Test Point 2: Perform initial byte-swap transformation + err = c_utf16_swap_endian(sample_utf16, array_len); + RUN_TEST(err == C_ERR_OK, "c_utf16_swap_endian executes successfully"); + // 0xD83D -> 0x3DD8, 0xDE80 -> 0x80DE, 0x5927 -> 0x2759 + RUN_TEST(sample_utf16[0] == 0x3DD8, "First code unit correctly bit-swapped (0xD83D -> 0x3DD8)"); + RUN_TEST(sample_utf16[1] == 0x80DE, "Second code unit correctly bit-swapped (0xDE80 -> 0x80DE)"); + RUN_TEST(sample_utf16[2] == 0x2759, "Third code unit correctly bit-swapped (0x5927 -> 0x2759)"); + + // Test Point 3: Swap back to restore the original host endianness values + err = c_utf16_swap_endian(sample_utf16, array_len); + RUN_TEST(err == C_ERR_OK, "c_utf16_swap_endian reverts state back on secondary execution pass"); + RUN_TEST(sample_utf16[0] == 0xD83D && sample_utf16[1] == 0xDE80 && sample_utf16[2] == 0x5927, "Original internal value contexts perfectly preserved"); + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + + /* 23. UTF-16 Single-Char Unicode Conversions Verification */ + c_ucs4_t decoded_cp = 0; + c_size_t units_moved = 0; + + // Test Point 1: Decode Standard BMP Character ('大' -> U+5927) + c_uint16_t bmp_sample[] = { 0x5927 }; + err = c_utf16_to_unicode(bmp_sample, 1, &decoded_cp, &units_moved); + RUN_TEST(err == C_ERR_OK && units_moved == 1, "c_utf16_to_unicode processes BMP code units"); + RUN_TEST(decoded_cp == 0x5927, "Decoded BMP matches expected U+5927 successfully"); + + // Test Point 2: Decode Surrogate Pair Character (Rocket Emoji '🚀' -> High: 0xD83D, Low: 0xDE80) + c_uint16_t astral_sample[] = { 0xD83D, 0xDE80 }; + err = c_utf16_to_unicode(astral_sample, 2, &decoded_cp, &units_moved); + RUN_TEST(err == C_ERR_OK && units_moved == 2, "c_utf16_to_unicode correctly handles surrogate pairs"); + RUN_TEST(decoded_cp == 0x1F680, "Decoded Astral match returns U+1F680 ('🚀')"); + + // Test Point 3: Error validation protection against malformed surrogate chains + c_uint16_t isolated_high[] = { 0xD83D }; // Lacks matching trailing block + RUN_TEST(c_utf16_to_unicode(isolated_high, 1, &decoded_cp, &units_moved) == C_ERR_PARAM, "c_utf16_to_unicode rejects truncated surrogate sequences"); + + // Test Point 4: Encode Astral Plane back to UTF-16 code units + c_uint16_t encode_units[2]; + units_written = 0; + err = c_utf16_from_unicode(0x1F680, encode_units, 2, &units_written); + RUN_TEST(err == C_ERR_OK && units_written == 2, "c_utf16_from_unicode builds surrogate pairs for plane codepoints"); + RUN_TEST(encode_units[0] == 0xD83D && encode_units[1] == 0xDE80, "Generated high and low values match standard encoding targets"); + + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + + printf("==================================================\n"); + printf("\033[32mSUCCESS: ALL UTF-8 COMPATIBLE INTERFACES PASSED!\033[0m\n"); + printf("==================================================\n"); + return C_ERR_OK; +} + + +int main(int argc, char** argv){ + return c_Utf8String_UnitTest(); +} diff --git a/cKit/Foundation/c_utf8_file.c b/cKit/Foundation/c_utf8_file.c new file mode 100644 index 0000000..dbe6328 --- /dev/null +++ b/cKit/Foundation/c_utf8_file.c @@ -0,0 +1,208 @@ +#include + +#include +#include + +c_err_t c_utf8_file_read(const char* filepath, c_StringBuffer_t* out_sb) { + if (!filepath || !out_sb || !out_sb->buffer) { + return C_ERR_PARAM; + } + + FILE* file = fopen(filepath, "rb"); // Open in binary mode to prevent Windows crlf translations + if (!file) { + return C_ERR_FAIL; + } + + // Step 1: Detect and handle the optional 3-byte UTF-8 BOM sequence + unsigned char bom[3]; + c_size_t bom_read = fread(bom, 1, 3, file); + c_bool_t has_bom = C_FALSE; + + if (bom_read == 3 && bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF) { + has_bom = C_TRUE; // BOM sequence matched; file pointer is positioned right past it + } else { + // No BOM found; rewind file pointer back to the absolute beginning of the stream + fseek(file, 0, SEEK_SET); + } + + // Step 2: Read data sequentially via stream chunking loops + char read_chunk[1024]; + c_size_t bytes_read = 0; + c_size_t partial_offset = 0; + + while ((bytes_read = fread(read_chunk + partial_offset, 1, sizeof(read_chunk) - partial_offset, file)) > 0) { + c_size_t total_available_bytes = bytes_read + partial_offset; + c_size_t valid_process_boundary = total_available_bytes; + + // Verify that the chunk boundary does not break a multi-byte character in half. + // Look back from the absolute end of the chunk to catch multi-byte headers. + if (read_chunk[total_available_bytes - 1] & 0x80) { + c_size_t lookback = 1; + // Scan backward up to 4 bytes to find the leading byte of the fractured character + while (lookback <= 4 && lookback <= total_available_bytes) { + unsigned char b = (unsigned char)read_chunk[total_available_bytes - lookback]; + if ((b & 0xC0) == 0xC0) { // Found a multi-byte lead byte + c_size_t expected_len = c_utf8_char_len((char)b); + if (lookback < expected_len) { + // Character is indeed fractured; shrink chunk boundary to omit it + valid_process_boundary = total_available_bytes - lookback; + } + break; + } + if ((b & 0x80) == 0) { // Standard ASCII character, boundary is clean + break; + } + lookback++; + } + } + + // Pipe valid, cohesive text fragments into your dynamic string buffer tracker + if (valid_process_boundary > 0) { + c_err_t err = c_StringBuffer_Append(out_sb, read_chunk, valid_process_boundary); + if (err != C_ERR_OK) { + fclose(file); + return err; + } + } + + // Move remaining fractured bytes to the front of the next chunk buffer iteration pass + partial_offset = total_available_bytes - valid_process_boundary; + if (partial_offset > 0) { + memmove(read_chunk, read_chunk + valid_process_boundary, partial_offset); + } + } + + // Process residual bytes if the file stream terminates abruptly with an incomplete character sequence + if (partial_offset > 0) { + c_StringBuffer_Append(out_sb, read_chunk, partial_offset); + } + + fclose(file); + return C_ERR_OK; +} + +c_err_t c_utf8_file_write(const char* filepath, c_StringBuffer_t* sb, c_bool_t write_bom) { + if (!filepath || !sb || !sb->buffer) { + return C_ERR_PARAM; + } + + FILE* file = fopen(filepath, "wb"); // Open in binary mode for precise byte preservation + if (!file) { + return C_ERR_FAIL; + } + + // Explicitly inject the UTF-8 BOM sequence if requested by the configuration parameter + if (write_bom) { + unsigned char bom[3] = {0xEF, 0xBB, 0xBF}; + if (fwrite(bom, 1, 3, file) != 3) { + fclose(file); + return C_ERR_FAIL; + } + } + + // Flush the string buffer's raw tracking payload into disk blocks + if (sb->size > 0) { + c_size_t written = fwrite(sb->buffer, 1, sb->size, file); + if (written != sb->size) { + fclose(file); + return C_ERR_FAIL; + } + } + + fclose(file); + return C_ERR_OK; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_utf8_file_append(const char* filepath, c_StringBuffer_t* sb, c_bool_t write_bom) { + if (!filepath || !sb || !sb->buffer) { + return C_ERR_PARAM; + } + + // Check if the file already exists by attempting to open it in read mode + FILE* check_file = fopen(filepath, "rb"); + c_bool_t file_exists = (check_file != NULL); + if (file_exists) { + fclose(check_file); + } + + // Open the file in append-binary mode + FILE* file = fopen(filepath, "ab"); + if (!file) { + return C_ERR_FAIL; + } + + // Write the BOM only if requested AND the file is brand new + if (write_bom && !file_exists) { + unsigned char bom[3] = {0xEF, 0xBB, 0xBF}; + if (fwrite(bom, 1, 3, file) != 3) { + fclose(file); + return C_ERR_FAIL; + } + } + + // Append the string buffer's raw tracking payload + if (sb->size > 0) { + c_size_t written = fwrite(sb->buffer, 1, sb->size, file); + if (written != sb->size) { + fclose(file); + return C_ERR_FAIL; + } + } + + fclose(file); + return C_ERR_OK; +} + +c_err_t c_utf8_file_readline(FILE* file, c_StringBuffer_t* out_line) { + if (!file || !out_line || !out_line->buffer) { + return C_ERR_PARAM; + } + + // Clear previous string buffer trackers to prepare for fresh line ingestion + c_StringBuffer_Clear(out_line); + + char read_chunk[256]; + c_bool_t data_extracted = C_FALSE; + long line_start_pos = ftell(file); + + while (fgets(read_chunk, sizeof(read_chunk), file) != NULL) { + data_extracted = C_TRUE; + c_size_t chunk_len = strlen(read_chunk); + + // Check if the chunk contains a newline character + char* newline_ptr = strchr(read_chunk, '\n'); + if (newline_ptr != NULL) { + // Calculate exact copy length up to the newline boundary + c_size_t copy_len = newline_ptr - read_chunk; + + if (copy_len > 0) { + // Strip carriage returns '\r' for safe cross-platform matching + if (read_chunk[copy_len - 1] == '\r') { + copy_len--; + } + } + + if (copy_len > 0) { + c_err_t err = c_StringBuffer_Append(out_line, read_chunk, copy_len); + if (err != C_ERR_OK) return err; + } + + return C_ERR_OK; // Line read complete + } + + // If no newline is found, the line is longer than our chunk; append everything and keep reading + c_err_t err = c_StringBuffer_Append(out_line, read_chunk, chunk_len); + if (err != C_ERR_OK) return err; + } + + // Handle end-of-file (EOF) state + if (data_extracted) { + return C_ERR_OK; // Returned the final trailing line containing no newline char + } + + return C_ERR_FAIL; // Reached EOF without extracting any data bytes +} + diff --git a/cKit/Foundation/c_utf8_file.h b/cKit/Foundation/c_utf8_file.h new file mode 100644 index 0000000..6f44364 --- /dev/null +++ b/cKit/Foundation/c_utf8_file.h @@ -0,0 +1,61 @@ +#ifndef INCLUDED_C_UTF8_FILE_H +#define INCLUDED_C_UTF8_FILE_H + +#ifndef INCLUDED_C_STRINGBUFFER_H +#include +#endif /*INCLUDED_C_STRINGBUFFER_H*/ + +#ifndef INCLUDED_C_UTF8_H +#include +#endif /*INCLUDED_C_UTF8_H*/ + +#ifndef INCLUDED_STDIO_H +#define INCLUDED_STDIO_H +#include +#endif /*INCLUDED_STDIO_H*/ + + + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * @brief Reads the entire contents of a UTF-8 text file into a string buffer structure. + * Automatically handles, validates, and skips the UTF-8 BOM marker if present. + * @param filepath Path to the target source file on disk. + * @param out_sb Pointer to a pre-initialized c_StringBuffer_t container to collect file data. + * @return c_err_t C_ERR_OK on complete success, C_ERR_PARAM on invalid inputs, or C_ERR_FAIL if file access throws errors. + */ +c_err_t c_utf8_file_read(const char* filepath, c_StringBuffer_t* out_sb); + +/** + * @brief Writes data from a string buffer out to a disk file using a UTF-8 text layout stream. + * @param filepath Path to the destination file on disk. + * @param sb Pointer to the source string buffer containing data. + * @param write_bom If set to C_TRUE, explicitly prefixes the file layout with the 3-byte UTF-8 BOM marker. + * @return c_err_t C_ERR_OK on complete success, C_ERR_PARAM on invalid inputs, or C_ERR_FAIL on disk write errors. + */ +c_err_t c_utf8_file_write(const char* filepath, c_StringBuffer_t* sb, c_bool_t write_bom); + +/** + * @brief Appends text from a string buffer to a disk file. + * If the target file does not exist, it initializes it (with an optional BOM marker). + * @param filepath Path to the destination file on disk. + * @param sb Pointer to the source string buffer containing the append payload. + * @param write_bom If set to C_TRUE and the file is new, prefixes the stream with the 3-byte UTF-8 BOM. + * @return c_err_t C_ERR_OK on complete success, C_ERR_PARAM on invalid inputs, or C_ERR_FAIL on disk write errors. + */ +c_err_t c_utf8_file_append(const char* filepath, c_StringBuffer_t* sb, c_bool_t write_bom); + +/** + * @brief Reads a single line of text from an open file stream into a string buffer (Dynamic fgets). + * Automatically handles standard '\n' and '\r\n' line endings. + * @param file An active file stream pointer opened in binary read ("rb") mode. + * @param out_line Pointer to a pre-initialized c_StringBuffer_t container to collect the line string. + * @return c_err_t C_ERR_OK on successful line read, C_ERR_FAIL when reaching EOF with no data, or parameter errors. + */ +c_err_t c_utf8_file_readline(FILE* file, c_StringBuffer_t* out_line); + + +#endif /*INCLUDED_C_UTF8_FILE_H*/ diff --git a/cKit/Foundation/c_utf8_file.t.c b/cKit/Foundation/c_utf8_file.t.c new file mode 100644 index 0000000..ffc7b62 --- /dev/null +++ b/cKit/Foundation/c_utf8_file.t.c @@ -0,0 +1,116 @@ +#include "c_utf8_file.h" +#include +#include + +#define RUN_TEST(test_case, name) \ + do { \ + printf("[RUN] %s... ", name); \ + if (test_case) { \ + printf("\033[32mPASSED\033[0m\n"); \ + } else { \ + printf("\033[31mFAILED\033[0m (%s:%d)\n", __FILE__, __LINE__); \ + return C_ERR_FAIL; \ + } \ + } while(0) + +static c_err_t c_utf8_file_test(void) { + printf("==================================================\n"); + printf(" STARTING C_UTF8_FILE UNIT TESTING \n"); + printf("==================================================\n"); + + /* 26. UTF-8 File I/O Operations Verification */ + c_StringBuffer_t write_sb; + c_StringBuffer_t read_sb; + const char* test_filename = "nlp_utf8_test.txt"; + const char* payload = "NLP大模型_2026_🚀"; + + c_StringBuffer_Init(&write_sb, 32); + c_StringBuffer_Init(&read_sb, 32); + c_StringBuffer_AppendStr(&write_sb, payload); + + // Test Point 1: Parameter checks protection + RUN_TEST(c_utf8_file_read(NULL, &read_sb) == C_ERR_PARAM, "File read handles NULL filepath strings"); + RUN_TEST(c_utf8_file_write(test_filename, NULL, C_FALSE) == C_ERR_PARAM, "File write handles NULL buffer contexts"); + + // Test Point 2: Write text payload with explicit BOM injection enabled + c_err_t err = c_utf8_file_write(test_filename, &write_sb, C_TRUE); + RUN_TEST(err == C_ERR_OK, "UTF-8 data written to disk with BOM successfully"); + + // Test Point 3: Read text payload back from disk space + err = c_utf8_file_read(test_filename, &read_sb); + RUN_TEST(err == C_ERR_OK, "UTF-8 data read from disk successfully"); + + // Verify that the BOM was skipped and data size matches exactly + RUN_TEST(read_sb.size == write_sb.size, "File reader successfully filtered out the 3 BOM byte footprints from data tracking metrics"); + RUN_TEST(strcmp(read_sb.buffer, payload) == 0, "Reconstructed disk text payload holds proper string characters perfectly"); + + // Cleanup resources and temporary test files + c_StringBuffer_Destroy(&write_sb); + c_StringBuffer_Destroy(&read_sb); + remove(test_filename); // Remove transient testing file assets from system layout + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + + /* 27. UTF-8 File Logging Append & Streaming Line Read Verification */ + c_StringBuffer_t io_sb; + c_StringBuffer_t line_sb; + const char* log_filename = "nlp_stream_test.txt"; + + c_StringBuffer_Init(&io_sb, 32); + c_StringBuffer_Init(&line_sb, 32); + + // Test Point 1: Consecutive appends to evaluate new file vs exist rules + c_StringBuffer_AppendStr(&io_sb, "First Line: 自然语言\n"); + err = c_utf8_file_append(log_filename, &io_sb, C_TRUE); // Creates file with BOM + RUN_TEST(err == C_ERR_OK, "Append created a new file with a BOM header successfully"); + + c_StringBuffer_Clear(&io_sb); + c_StringBuffer_AppendStr(&io_sb, "Second Line: 大模型🚀\n"); + err = c_utf8_file_append(log_filename, &io_sb, C_TRUE); // Appends to existing file (BOM skipped) + RUN_TEST(err == C_ERR_OK, "Append safely added data to the existing file without duplicate BOM injections"); + + // Test Point 2: Streaming Line Reads via c_utf8_file_readline + FILE* stream_in = fopen(log_filename, "rb"); + RUN_TEST(stream_in != NULL, "Opened log test file for stream reading"); + + // Handle initial optional BOM detection before streaming lines + unsigned char check_bom[3]; + if (fread(check_bom, 1, 3, stream_in) == 3 && check_bom[0] == 0xEF && check_bom[1] == 0xBB && check_bom[2] == 0xBF) { + // BOM detected and skipped successfully + } else { + fseek(stream_in, 0, SEEK_SET); + } + + // Read Line 1 + err = c_utf8_file_readline(stream_in, &line_sb); + RUN_TEST(err == C_ERR_OK, "Read the first text line via streaming readline API"); + RUN_TEST(strcmp(line_sb.buffer, "First Line: 自然语言") == 0, "Line 1 string matches, trailing newline stripped cleanly"); + + // Read Line 2 + err = c_utf8_file_readline(stream_in, &line_sb); + RUN_TEST(err == C_ERR_OK, "Read the second text line via streaming readline API"); + RUN_TEST(strcmp(line_sb.buffer, "Second Line: 大模型🚀") == 0, "Line 2 string matches multi-byte characters and Emojis perfectly"); + + // Read Line 3 (Expect EOF termination failure status) + err = c_utf8_file_readline(stream_in, &line_sb); + RUN_TEST(err == C_ERR_FAIL, "Readline returns C_ERR_FAIL cleanly when hitting EOF boundaries"); + + // Cleanup resources + fclose(stream_in); + c_StringBuffer_Destroy(&io_sb); + c_StringBuffer_Destroy(&line_sb); + remove(log_filename); // Purge volatile testing file asset + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + printf("==================================================\n"); + printf("\033[32mSUCCESS: ALL C_UTF8_FILE COMPATIBLE INTERFACES PASSED!\033[0m\n"); + printf("==================================================\n"); + return C_ERR_OK; +} + +int main(int argc, char** argv){ + + return c_utf8_file_test(); +} diff --git a/cKit/Graph/c_QuickFindUF.c b/cKit/Graph/c_QuickFindUF.c new file mode 100644 index 0000000..e14865f --- /dev/null +++ b/cKit/Graph/c_QuickFindUF.c @@ -0,0 +1,45 @@ +#include +#include + + +c_err_t c_QuickFindUF_Init(c_QuickFindUF_t* self, c_size_t n) { + if (!self || n==0) return C_ERR_PARAM; + self->count = n; + self->id_len = n; + self->id = C_ALLOC(sizeof(c_size_t) * n); + if (!self->id) { + return C_ERR_NOMEM; + } + for (c_size_t i=0; iid_len; i++) { + self->id[i] = i; + } + return C_ERR_OK; +} + +void c_QuickFindUF_Destroy(c_QuickFindUF_t* self) { + C_FREE(self->id); + self->count = 0; + self->id_len = 0; +} + +c_size_t c_QuickFindUF_Find(c_QuickFindUF_t* self, c_size_t index) { + if (!self || self->id==NULL || index>=self->id_len) { + return (c_size_t)-1; + } + return self->id[index]; +} + +c_err_t c_QuickFindUF_Union(c_QuickFindUF_t* self, c_size_t p, c_size_t q) { + if (self == NULL || self->id == NULL) return C_ERR_PARAM; + if (p >= self->id_len || q >= self->id_len) return C_ERR_PARAM; + + c_size_t pID = self->id[p]; + c_size_t qID = self->id[q]; + if (pID==qID) return C_ERR_OK; + for (c_size_t i=0; iid_len; i++) { + if (self->id[i] == pID) self->id[i] = qID; + } + self->count--; + return C_ERR_OK; +} + diff --git a/cKit/Graph/c_QuickFindUF.h b/cKit/Graph/c_QuickFindUF.h new file mode 100644 index 0000000..c60f732 --- /dev/null +++ b/cKit/Graph/c_QuickFindUF.h @@ -0,0 +1,38 @@ +#ifndef INCLUDED_C_QUICKFINDUF_H +#define INCLUDED_C_QUICKFINDUF_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +typedef struct { + c_size_t* id; + c_size_t id_len; + c_size_t count; +}c_QuickFindUF_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_QuickFindUF_Init(c_QuickFindUF_t* self, c_size_t count); + +void c_QuickFindUF_Destroy(c_QuickFindUF_t* self); + +c_size_t c_QuickFindUF_Find(c_QuickFindUF_t* self, c_size_t index); + +c_err_t c_QuickFindUF_Union(c_QuickFindUF_t* self, c_size_t p, c_size_t q); + +C_STATIC_FORCE_INLINE +c_bool_t c_QuickFindUF_IsConnected(c_QuickFindUF_t* self, c_size_t p, c_size_t q) { + if (!self || !self->id || p>=self->id_len || q>=self->id_len) return C_FALSE; + return self->id[q] == self->id[p]; +} + + + +#endif /*INCLUDED_C_QUICKFINDUF_H*/ diff --git a/cKit/Graph/c_QuickFindUF.t.c b/cKit/Graph/c_QuickFindUF.t.c new file mode 100644 index 0000000..8548ee2 --- /dev/null +++ b/cKit/Graph/c_QuickFindUF.t.c @@ -0,0 +1,68 @@ +#include "c_QuickFindUF.h" +#include +#include + +// Your updated line tracing diagnostic macro +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \ + return C_FALSE; \ + } \ + } while(0) + +c_bool_t test_quick_find_disjoint_set(void) { + c_QuickFindUF_t uf; + c_size_t sites = 10; // Track 10 independent connectivity components (0 to 9) + + // Test Case 1: Parameter Enforcement & Initial Set Boundaries + EXPECT_EQ(c_QuickFindUF_Init(NULL, sites), C_ERR_PARAM, "NULL pointer initialization guard missed"); + EXPECT_EQ(c_QuickFindUF_Init(&uf, sites), C_ERR_OK, "Quick-Find context initialization failed"); + EXPECT_EQ(uf.count, 10, "Initial disjoint independent components size tracking incorrect"); + + // On entry, every site must resolve to itself as its own root identifier + EXPECT_EQ(c_QuickFindUF_Find(&uf, 4), 4, "Site component lookup failed to resolve to identity baseline"); + EXPECT_EQ(c_QuickFindUF_IsConnected(&uf, 4, 3), C_FALSE, "Unconnected distinct sites reported true connectivity on entry"); + + // Test Case 2: Link Cluster Merging Sequence (Union Processing) + // Connect pairs sequentially: (4, 3), (3, 8), (6, 5) + printf(" [LOG] Grouping element nodes into connected sub-clusters...\n"); + EXPECT_EQ(c_QuickFindUF_Union(&uf, 4, 3), C_ERR_OK, "Union operation for (4, 3) failed"); + EXPECT_EQ(c_QuickFindUF_Union(&uf, 3, 8), C_ERR_OK, "Union operation for (3, 8) failed"); + EXPECT_EQ(c_QuickFindUF_Union(&uf, 6, 5), C_ERR_OK, "Union operation for (6, 5) failed"); + + // Component size count should decrement cleanly: 10 - 3 merges = 7 clusters left + EXPECT_EQ(uf.count, 7, "Connected clusters total reduction tracking variable miscalculated"); + + // Test Case 3: Connectivity Matrix Validation Checkpoints + // Because 4 connected to 3, and 3 connected to 8, 4 should now be transitively connected to 8 + EXPECT_EQ(c_QuickFindUF_IsConnected(&uf, 4, 8), C_TRUE, "Transitive path connection check missed targeting"); + EXPECT_EQ(c_QuickFindUF_IsConnected(&uf, 4, 5), C_FALSE, "Isolated component branches reported false positive connection layout"); + + // O(1) Lookups must verify identical component cluster ID mapping matches + EXPECT_EQ(c_QuickFindUF_Find(&uf, 4) == c_QuickFindUF_Find(&uf, 8), C_TRUE, "Quick-Find IDs out of sync for linked nodes"); + printf(" [STAT] Component connection verified. Site 4 Component ID: %zu, Site 8 Component ID: %zu\n", c_QuickFindUF_Find(&uf, 4), c_QuickFindUF_Find(&uf, 8)); + + // Test Case 4: Redundant Merge Operations Guard Checks + // Merging already connected items should exit early without decrementing the active cluster pool counts + EXPECT_EQ(c_QuickFindUF_Union(&uf, 8, 4), C_ERR_OK, "Redundant union call threw an unexpected exception"); + EXPECT_EQ(uf.count, 7, "Redundant component merge tracking decremented size array pools erroneously"); + + // Test Case 5: Out of Bound Safety Guards Exception Escapes + EXPECT_EQ(c_QuickFindUF_Union(&uf, 12, 4), C_ERR_PARAM, "Out of bounds parameter index skipped param check filtering"); + EXPECT_EQ(c_QuickFindUF_IsConnected(&uf, 4, 99), C_FALSE, "OOB connection index check failed to drop safe negative response"); + EXPECT_EQ(c_QuickFindUF_Find(&uf, 99), (c_size_t)-1, "OOB lookup index failed to surface default error sentinel"); + + c_QuickFindUF_Destroy(&uf); + return C_TRUE; +} + +int main(void) { + printf("=== Starting Framework Verification: c_QuickFindUF ===\n"); + if (test_quick_find_disjoint_set()) { + printf(" [PASS] Quick-Find Disjoint Set Operations and Component Merges Verified Successfully.\n"); + } else { + printf(" [FAIL] Disjoint Set Pipeline Structural Logic Failures Detected.\n"); + } + return 0; +} diff --git a/cKit/Math/c_LinearRegression.c b/cKit/Math/c_LinearRegression.c new file mode 100644 index 0000000..6d128bb --- /dev/null +++ b/cKit/Math/c_LinearRegression.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Math/c_LinearRegression.h b/cKit/Math/c_LinearRegression.h new file mode 100644 index 0000000..51e6fb1 --- /dev/null +++ b/cKit/Math/c_LinearRegression.h @@ -0,0 +1,106 @@ +#ifndef INCLUDED_C_LINEARREGRESSION_H +#define INCLUDED_C_LINEARREGRESSION_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// Linear Regression Model Context Structure +typedef struct { + double slope; // Slope coefficient (Beta) + double intercept; // Y-Intercept coefficient (Alpha) + c_bool_t is_trained;// State flag tracking model training status +} c_LinearRegression_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +/** + * Initialize the Linear Regression Model context block. + */ +C_STATIC_FORCE_INLINE +c_err_t c_LinearRegression_Init(c_LinearRegression_t* model) { + if (model == NULL) return C_ERR_PARAM; + + model->slope = 0.0; + model->intercept = 0.0; + model->is_trained = C_FALSE; + + return C_ERR_OK; +} + +/** + * Train the model using Ordinary Least Squares (OLS) linear derivation math. + * Time Complexity: O(N) | Auxiliary Space: O(1) in-place + * @param model Pointer to the linear regression model context instance. + * @param x Contiguous array tracking independent variable observations. + * @param y Contiguous array tracking dependent variable target features. + * @param num Total number of data points inside the training array sets. + * @return C_ERR_OK if successful, C_ERR_PARAM for NULL targets, + * or C_ERR_INVALID if variance evaluates to zero (vertical line slope anomaly). + */ +C_STATIC_FORCE_INLINE +c_err_t c_LinearRegression_Fit(c_LinearRegression_t* model, const double* x, const double* y, c_size_t num) { + if (model == NULL || x == NULL || y == NULL) return C_ERR_PARAM; + if (num < 2) return C_ERR_PARAM; // Mandate at least two distinct points to draw a trend line + + double sum_x = 0.0; + double sum_y = 0.0; + + // Step 1: Calculate the arithmetic mean values for features X and Y + for (c_size_t i = 0; i < num; i++) { + sum_x += x[i]; + sum_y += y[i]; + } + double mean_x = sum_x / (double)num; + double mean_y = sum_y / (double)num; + + double num_covariance = 0.0; + double den_variance = 0.0; + + // Step 2: Accumulate sample covariance and independent feature variance maps + for (c_size_t i = 0; i < num; i++) { + double diff_x = x[i] - mean_x; + num_covariance += diff_x * (y[i] - mean_y); + den_variance += diff_x * diff_x; + } + + // Step 3: Guard against division-by-zero on perfectly vertical data layouts + if (den_variance == 0.0) { + model->is_trained = C_FALSE; + return C_ERR_INVALID; + } + + // Step 4: Map final slope and intercept boundary coefficients + model->slope = num_covariance / den_variance; + model->intercept = mean_y - (model->slope * mean_x); + model->is_trained = C_TRUE; + + return C_ERR_OK; +} + +/** + * Inference Lookahead: Predict the output target value for a specific input feature. + * @param model Pointer to the constant trained model instance. + * @param x The input independent scalar variable point. + * @param out_val Pointer to the destination variable where the predicted Y value is written. + * @return C_ERR_OK if successful, or C_ERR_INVALID if the model is untrained. + */ +C_STATIC_FORCE_INLINE +c_err_t c_LinearRegression_Predict(const c_LinearRegression_t* model, double x, double* out_val) { + if (model == NULL || out_val == NULL) return C_ERR_PARAM; + if (!model->is_trained) return C_ERR_INVALID; + + // Execute standard linear function lookup: y = alpha + beta * x + *out_val = model->intercept + (model->slope * x); + return C_ERR_OK; +} + + + +#endif /*INCLUDED_C_LINEARREGRESSION_H*/ diff --git a/cKit/Math/c_LinearRegression.t.c b/cKit/Math/c_LinearRegression.t.c new file mode 100644 index 0000000..c3219a0 --- /dev/null +++ b/cKit/Math/c_LinearRegression.t.c @@ -0,0 +1,71 @@ +#include "c_LinearRegression.h" +#include +#include + +// Your updated line tracing diagnostic macro +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \ + return C_FALSE; \ + } \ + } while(0) + +// Helper macro for double comparisons with floating-point tolerance +#define EXPECT_NEAR(actual, expected, tolerance, msg) \ + do { \ + if (fabs((actual) - (expected)) > (tolerance)) { \ + printf(" [X] Assert Failed: %s (Expected %f, got %f) %s:%d\n", msg, (double)(expected), (double)(actual), __FILE__, __LINE__); \ + return C_FALSE; \ + } \ + } while(0) + +c_bool_t test_linear_regression_ops(void) { + c_LinearRegression_t model; + + // Test Case 1: Param Parameter Enforcement Boundary Checks + EXPECT_EQ(c_LinearRegression_Init(NULL), C_ERR_PARAM, "NULL model instance initializer guard missed"); + EXPECT_EQ(c_LinearRegression_Init(&model), C_ERR_OK, "Model context initialization failed"); + EXPECT_EQ(model.is_trained, C_FALSE, "Untrained model reported trained status flags on entry"); + + double pred_buffer = 0.0; + EXPECT_EQ(c_LinearRegression_Predict(&model, 5.0, &pred_buffer), C_ERR_INVALID, "Untrained model permitted prediction inference runs"); + + // Prepare an un-ordered linear dataset mapping the pure mathematical trend line: y = 2.0 * x + 5.0 + double train_x[] = { 1.0, 2.0, 4.0, 5.0, 3.0 }; + double train_y[] = { 7.0, 9.0, 13.0, 15.0, 11.0 }; + c_size_t samples = sizeof(train_x) / sizeof(train_x[0]); + + // Test Case 2: Core Model Fitting (Ordinary Least Squares verification) + printf(" [LOG] Training Ordinary Least Squares Linear Regression Model...\n"); + EXPECT_EQ(c_LinearRegression_Fit(&model, train_x, train_y, samples), C_ERR_OK, "Model training fit routine failed"); + EXPECT_EQ(model.is_trained, C_TRUE, "Successful fit sequence missed toggling active trained flag status"); + + // Assert derived trend coefficients map exactly to Slope (Beta) = 2.0, Intercept (Alpha) = 5.0 + EXPECT_NEAR(model.slope, 2.0, 1e-6, "Derived model slope (Beta) coefficient mathematically incorrect"); + EXPECT_NEAR(model.intercept, 5.0, 1e-6, "Derived model y-intercept (Alpha) coefficient mathematically incorrect"); + + // Test Case 3: Inference Prediction Checking + // Predict value for x = 10.0 -> y = 5.0 + 2.0 * 10.0 = 25.0 + EXPECT_EQ(c_LinearRegression_Predict(&model, 10.0, &pred_buffer), C_ERR_OK, "Prediction inference run crashed"); + EXPECT_NEAR(pred_buffer, 25.0, 1e-6, "Model inference lookup yielded inaccurate coordinate value"); + printf(" [STAT] Model Trained. Equation: y = %.2f + %.2fx | Prediction(x=10): %.2f\n", model.intercept, model.slope, pred_buffer); + + // Test Case 4: Division-by-Zero Vertical Alignment Mathematical Anomaly Check + double vertical_x[] = { 3.0, 3.0, 3.0 }; + double vertical_y[] = { 1.0, 5.0, 9.0 }; + EXPECT_EQ(c_LinearRegression_Fit(&model, vertical_x, vertical_y, 3), C_ERR_INVALID, "Vertical line infinite slope anomaly bypassed zero variance filter"); + EXPECT_EQ(model.is_trained, C_FALSE, "Failed fit sequence left model registered in an active trained state"); + + return C_TRUE; +} + +int main(void) { + printf("=== Starting Framework Verification: c_LinearRegression ===\n"); + if (test_linear_regression_ops()) { + printf(" [PASS] Ordinary Least Squares Linear Regression Matrix Pipelines Verified.\n"); + } else { + printf(" [FAIL] Mathematical Linear Modeling Processing Anomaly Intercepted.\n"); + } + return 0; +} diff --git a/cKit/Memory/c_Alignment.c b/cKit/Memory/c_Alignment.c new file mode 100644 index 0000000..aee4bc6 --- /dev/null +++ b/cKit/Memory/c_Alignment.c @@ -0,0 +1,2 @@ +#include + diff --git a/cKit/Memory/c_Alignment.h b/cKit/Memory/c_Alignment.h new file mode 100644 index 0000000..f48cb92 --- /dev/null +++ b/cKit/Memory/c_Alignment.h @@ -0,0 +1,29 @@ +#ifndef INCLUDED_C_ALIGNMENT_H +#define INCLUDED_C_ALIGNMENT_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +typedef union { +#ifdef C_ALIGN_MAX_SIZE + char pad[C_ALIGN_MAX_SIZE]; +#else + int i; + long l; + long *lp; + void *p; + void (*fp)(void); + float f; + double d; + long double ld; +#endif +} c_align_t; + +#define C_ALIGN_SIZE sizeof(c_align_t) + +#endif /*INCLUDED_C_ALIGNMENT_H*/ diff --git a/cKit/Memory/c_Arena.c b/cKit/Memory/c_Arena.c new file mode 100644 index 0000000..c42e7f5 --- /dev/null +++ b/cKit/Memory/c_Arena.c @@ -0,0 +1,102 @@ +#include +#include +#include + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +C_STATIC_FORCE_INLINE +c_bool_t is_power_of_two(c_size_t x) { + return (x & (x-1)) == 0; +} + +C_STATIC_FORCE_INLINE +c_uintptr_t align_forward(c_uintptr_t ptr, c_size_t align) { + c_uintptr_t p; + c_uintptr_t a; + c_uintptr_t modulo; + + assert(is_power_of_two(align)); + + p = ptr; + a = (uintptr_t)align; + // Same as (p % a) but faster as 'a' is a power of two + modulo = p & (a-1); + + if (modulo != 0) { + // If 'p' address is not aligned, push the address to the + // next value which is aligned + p += a - modulo; + } + return p; +} + +C_STATIC_FORCE_INLINE +void *arena_alloc_align(c_Arena_t *a, c_size_t size, c_size_t align) { + // Align 'curr_offset' forward to the specified alignment + c_uintptr_t curr_ptr = (c_uintptr_t)a->buf + (c_uintptr_t)a->curr_offset; + c_uintptr_t offset = align_forward(curr_ptr, align); + offset -= (c_uintptr_t)a->buf; // Change to relative offset + + // Check to see if the backing memory has space left + if (offset+size <= a->buf_len) { + void *ptr = &a->buf[offset]; + a->prev_offset = offset; + a->curr_offset = offset+size; + + // Zero new memory by default + memset(ptr, 0, size); + return ptr; + } + // Return NULL if the arena is out of memory (or handle differently) + return NULL; +} + +C_STATIC_FORCE_INLINE +void *arena_resize_align(c_Arena_t *a, void *old_memory, c_size_t old_size, c_size_t new_size, c_size_t align) { + uint8_t* old_mem = (uint8_t*)old_memory; + + assert(is_power_of_two(align)); + + if (old_mem == NULL || old_size == 0) { + return arena_alloc_align(a, new_size, align); + } else if (a->buf <= old_mem && old_mem < (a->buf+ a->buf_len)) { + if (a->buf+a->prev_offset == old_mem) { + a->curr_offset = a->prev_offset + new_size; + if (new_size > old_size) { + // Zero the new memory by default + memset(&a->buf[a->curr_offset], 0, new_size-old_size); + } + return old_memory; + } else { + void *new_memory = arena_alloc_align(a, new_size, align); + c_size_t copy_size = old_size < new_size ? old_size : new_size; + // Copy across old memory to the new memory + memmove(new_memory, old_memory, copy_size); + return new_memory; + } + + } else { + assert(0 && "Memory is out of bounds of the buffer in this arena"); + return NULL; + } +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +void c_Arena_Init(c_Arena_t* a, void* buf, c_size_t buf_len) { + a->buf = buf; + a->buf_len = buf_len; + a->prev_offset = 0; + a->curr_offset = 0; +} + +void *c_Arena_Alloc(c_Arena_t *a, c_size_t size) { + return arena_alloc_align(a, size, C_ALIGN_SIZE); +} + +void *c_Arena_Resize(c_Arena_t *a, void *old_memory, c_size_t old_size, c_size_t new_size) { + return arena_resize_align(a, old_memory, old_size, new_size, C_ALIGN_SIZE); +} + diff --git a/cKit/Memory/c_Arena.h b/cKit/Memory/c_Arena.h new file mode 100644 index 0000000..a7072b1 --- /dev/null +++ b/cKit/Memory/c_Arena.h @@ -0,0 +1,62 @@ +#ifndef INCLUDED_C_ARENA_H +#define INCLUDED_C_ARENA_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + uint8_t* buf; + c_size_t buf_len; + c_size_t prev_offset; + c_size_t curr_offset; +}c_Arena_t; + +typedef struct { + c_Arena_t* arena; + c_size_t prev_offset; + c_size_t curr_offset; +}c_ArenaTemp_t; + +void c_Arena_Init(c_Arena_t* a, void* buf, c_size_t buf_len); + +void *c_Arena_Alloc(c_Arena_t *a, c_size_t size); + +void *c_Arena_Resize(c_Arena_t *a, void *old_memory, c_size_t old_size, c_size_t new_size); + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +C_STATIC_FORCE_INLINE +void c_Arena_Destroy(c_Arena_t* a) { + a->curr_offset = a->prev_offset = 0; +} + +C_STATIC_FORCE_INLINE +void c_Arena_Free(c_Arena_t *a, void *ptr) { + C_UNUSED(a); + C_UNUSED(ptr); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +C_STATIC_FORCE_INLINE +c_ArenaTemp_t c_ArenaTemp_Begin(c_Arena_t *a) { + c_ArenaTemp_t temp; + temp.arena = a; + temp.prev_offset = a->prev_offset; + temp.curr_offset = a->prev_offset; + return temp; +} + +C_STATIC_FORCE_INLINE +void c_ArenaTemp_End(c_ArenaTemp_t *a) { + a->arena->curr_offset = a->curr_offset; + a->arena->prev_offset = a->prev_offset; +} + +#endif /*INCLUDED_C_ARENA_H*/ diff --git a/cKit/Memory/c_Arena.t.c b/cKit/Memory/c_Arena.t.c new file mode 100644 index 0000000..9083d51 --- /dev/null +++ b/cKit/Memory/c_Arena.t.c @@ -0,0 +1,132 @@ +#include "c_Arena.h" +#include +#include +#include "assert.h" + +static +void test_arena_basic_alloc() { + printf("[測試] 基礎分配...\n"); + + uint8_t backing_buffer[1024]; + c_Arena_t arena; + + // 初始化 + c_Arena_Init(&arena, backing_buffer, sizeof(backing_buffer)); + assert(arena.buf == backing_buffer); + assert(arena.buf_len == 1024); + assert(arena.curr_offset == 0); + + // 第一次分配 + void* p1 = c_Arena_Alloc(&arena, 100); + assert(p1 != NULL); + assert(arena.curr_offset >= 100); // 考慮到對齊,可能大於等於 100 + + // 第二次分配 + void* p2 = c_Arena_Alloc(&arena, 200); + assert(p2 != NULL); + assert(p2 > p1); // 記憶體地址應是連續向後的 + + printf(" => 基礎分配測試成功\n"); +} + +// ========================================== +// 測試用例 2:記憶體溢出(Out of Memory) +// ========================================== +static +void test_arena_oom() { + printf("[測試] 記憶體溢出邊界...\n"); + + uint8_t backing_buffer[256]; + c_Arena_t arena; + c_Arena_Init(&arena, backing_buffer, sizeof(backing_buffer)); + + // 嘗試分配超出整塊 Arena 大小的記憶體 + void* p1 = c_Arena_Alloc(&arena, 300); + assert(p1 == NULL); // 應返回 NULL + + // 分配剛好極限的記憶體 + void* p2 = c_Arena_Alloc(&arena, 256); + assert(p2 != NULL); + + // 已經滿了,再次分配應失敗 + void* p3 = c_Arena_Alloc(&arena, 1); + assert(p3 == NULL); + + printf(" => OOM 邊界測試成功\n"); +} + +// ========================================== +// 測試用例 3:Resize 原地擴展(最速路徑) +// ========================================== +// 說明:若舊記憶體剛好是最後一次分配的區塊(curr_offset 緊鄰它), +// Arena 應該直接移動 curr_offset 實現原地擴展,而不需搬移資料。 +static +void test_arena_resize_inplace() { + printf("[測試] Resize 原地擴展(最後一個分配物)...\n"); + + uint8_t backing_buffer[1024]; + c_Arena_t arena; + c_Arena_Init(&arena, backing_buffer, sizeof(backing_buffer)); + + // 分配 p1,並寫入測試資料 + char* p1 = (char*)c_Arena_Alloc(&arena, 10); + strcpy(p1, "Hello"); + + c_size_t offset_before_resize = arena.curr_offset; + + // 將 p1 擴大到 50 字節 + char* p1_new = (char*)c_Arena_Resize(&arena, p1, 10, 50); + + // 驗證:因為 p1 是最後分配的,它的地址不應該改變(原地擴展) + assert(p1_new == p1); + assert(strcmp(p1_new, "Hello") == 0); // 資料必須完整保留 + assert(arena.curr_offset > offset_before_resize); // 偏移量正確推移 + + printf(" => Resize 原地擴展測試成功\n"); +} + +// ========================================== +// 測試用例 4:Resize 重新分配(非最後分配物) +// ========================================== +// 說明:若舊記憶體後面已經有其他分配物(p1 後面有 p2), +// 此時對 p1 做 Resize 必須當作全新分配,並拷貝舊資料。 +static +void test_arena_resize_realloc() { + printf("[測試] Resize 重新分配(中間的分配物)...\n"); + + uint8_t backing_buffer[1024]; + c_Arena_t arena; + c_Arena_Init(&arena, backing_buffer, sizeof(backing_buffer)); + + // 連續分配 p1 與 p2 + char* p1 = (char*)c_Arena_Alloc(&arena, 16); + strcpy(p1, "Data1"); + + char* p2 = (char*)c_Arena_Alloc(&arena, 16); + strcpy(p2, "Data2"); + + // 對 p1(此時不是最後一個分配物)進行 Resize + char* p1_new = (char*)c_Arena_Resize(&arena, p1, 16, 32); + + // 驗證: + assert(p1_new != NULL); + assert(p1_new != p1); // 必須分配在 p2 之後的新位置 + assert(p1_new > p2); // 確保在新地址 + assert(strcmp(p1_new, "Data1") == 0); // 舊資料必須被 memcpy 過去 + assert(strcmp(p2, "Data2") == 0); // 隔壁的 p2 資料不能被破壞 + + printf(" => Resize 重新分配測試成功\n"); +} + +int main(int argc, char** argv){ + printf("--- Test Begin ---\n"); + + test_arena_basic_alloc(); + test_arena_oom(); + test_arena_resize_inplace(); + test_arena_resize_realloc(); + + printf("--- Test End! ---\n"); + + return 0; +} diff --git a/cKit/Memory/c_Buddy.c b/cKit/Memory/c_Buddy.c new file mode 100644 index 0000000..1b57b80 --- /dev/null +++ b/cKit/Memory/c_Buddy.c @@ -0,0 +1,169 @@ +#include +#include +#include +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +C_STATIC_FORCE_INLINE +c_bool_t is_power_of_two(c_size_t x) { + return (x & (x-1)) == 0; +} + +C_STATIC_FORCE_INLINE +c_BuddyBlock_t* buddy_block_next(c_BuddyBlock_t* block) { + return (c_BuddyBlock_t*)((uint8_t*)block + block->size); +} + +static c_BuddyBlock_t *buddy_block_split(c_BuddyBlock_t *block, const int size) { + if (block != NULL && size != 0) { + // 當目標大小小於當前區塊時,進行對半裂變 + while (size < block->size) { + int sz = block->size >> 1; + block->size = sz; + + // 裂變出的右半邊(Right Buddy)設為空閒 + c_BuddyBlock_t* right_buddy = buddy_block_next(block); + right_buddy->size = sz; + right_buddy->is_free = C_TRUE; + + // 經典夥伴系統優先分配左半邊(Left Buddy),因此 block 指針保持在左側繼續循環 + } + + if (size <= block->size) { + return block; + } + } + return NULL; +} + +static +c_BuddyBlock_t *buddy_block_find_best(c_BuddyBlock_t *head, c_BuddyBlock_t *tail, int size) { + c_BuddyBlock_t *best_block = NULL; + c_BuddyBlock_t *curr = head; + + while (curr < tail) { + if (curr->is_free && curr->size >= size) { + // 尋找符合條件且最小的區塊(Best Fit),減少外部碎片 + if (best_block == NULL || curr->size < best_block->size) { + best_block = curr; + } + } + // 精確前進當前區塊的實際大小,不論其內部被切得多碎,都能地毯式掃描 + curr = buddy_block_next(curr); + } + + if (best_block != NULL) { + return buddy_block_split(best_block, size); + } + return NULL; +} + +C_STATIC_FORCE_INLINE +int buddy_block_size_required(c_Buddy_t *b, int size) { + int actual_size = b->alignment; + const int total_needed = size + b->alignment; // 預留足夠空間確保 padding 後依然夠用 + + while (actual_size < total_needed) { + actual_size <<= 1; + } + return actual_size; +} + +static +void buddy_block_coalescence(c_BuddyBlock_t *head, c_BuddyBlock_t *tail) { + for (;;) { + c_BuddyBlock_t *curr = head; + c_bool_t consolidated_any = C_FALSE; + + while (curr < tail) { + c_BuddyBlock_t *next = buddy_block_next(curr); + + // 檢查安全邊界 + if (next >= tail) { + break; + } + + // 凝聚條件:兩相鄰區塊皆空閒,且大小相等 + // 注意:在夥伴系統中,左夥伴的偏移量必須是其兩倍大小的整數倍(對齊檢查) + if (curr->is_free && next->is_free && curr->size == next->size) { + const c_uintptr_t offset = (c_uintptr_t)curr - (c_uintptr_t)head; + if ((offset % (curr->size << 1)) == 0) { + curr->size <<= 1; // 融合!容量翻倍 + consolidated_any = C_TRUE; + // 融合後,下一次推進會自動從融合後的大區塊末端繼續,不用前進 next + continue; + } + } + + // 若無法融合,正常步進到下一個區塊 + curr = next; + } + + // 如果整輪掃描下來沒有任何區塊可以再融合,代表凝聚完成,退出循環 + if (!consolidated_any) { + break; + } + } +} + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +void c_Buddy_Init(c_Buddy_t *b, void *data, int size, int alignment) { + assert(b != NULL); + assert(data != NULL); + assert(is_power_of_two(size) && "size is not a power-of-two"); + assert(is_power_of_two(alignment) && "alignment is not a power-of-two"); + + // 確保對齊基準至少能放下一個標頭結構體 + if (alignment < (int)sizeof(c_BuddyBlock_t)) { + alignment = C_ALIGN_UPB(sizeof(c_BuddyBlock_t), 2); // 向上取 2 的冪次 + while (!is_power_of_two(alignment)) { + alignment++; // 穩健保險 + } + } + assert((c_uintptr_t)data % alignment == 0 && "data is not aligned to minimum alignment"); + + b->alignment = alignment; + b->head = (c_BuddyBlock_t *)data; + b->head->size = size; + b->head->is_free = C_TRUE; + + // 哨兵結尾 + b->tail = buddy_block_next(b->head); +} + +void *c_Buddy_Alloc(c_Buddy_t *b, int size) { + if (!b || size <= 0) return NULL; + + int actual_size = buddy_block_size_required(b, size); + + // 第一階段:尋找現有最合適的空閒塊 + c_BuddyBlock_t *found = buddy_block_find_best(b->head, b->tail, actual_size); + if (found == NULL) { + // 第二階段:若找不到,強制進行全面碎片凝聚(Coalesce),再搜尋一次 + buddy_block_coalescence(b->head, b->tail); + found = buddy_block_find_best(b->head, b->tail, actual_size); + } + + if (found != NULL) { + found->is_free = C_FALSE; + return (void *)((uint8_t *)found + b->alignment); + } + + return NULL; // OOM +} + +void c_Buddy_Free(c_Buddy_t *b, void *data) { + if (data != NULL) { + assert((c_uintptr_t)b->head <= (c_uintptr_t)data); + assert((c_uintptr_t)data < (c_uintptr_t)b->tail); + + c_BuddyBlock_t *block = (c_BuddyBlock_t *) ((uint8_t *) data - b->alignment); + block->is_free = C_TRUE; + + // NOTE: Coalescence could be done now but it is optional + // buddy_block_coalescence(b->head, b->tail); + } +} diff --git a/cKit/Memory/c_Buddy.h b/cKit/Memory/c_Buddy.h new file mode 100644 index 0000000..07d5085 --- /dev/null +++ b/cKit/Memory/c_Buddy.h @@ -0,0 +1,29 @@ +#ifndef INCLUDED_C_BUDDY_H +#define INCLUDED_C_BUDDY_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + int size; + c_bool_t is_free; +}c_BuddyBlock_t; + +typedef struct { + c_BuddyBlock_t* head; + c_BuddyBlock_t* tail; + int alignment; +}c_Buddy_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +void c_Buddy_Init(c_Buddy_t *b, void *data, int size, int alignment); +void *c_Buddy_Alloc(c_Buddy_t *b, int size); +void c_Buddy_Free(c_Buddy_t *b, void *data); + +#endif /*INCLUDED_C_BUDDY_H*/ diff --git a/cKit/Memory/c_Buddy.t.c b/cKit/Memory/c_Buddy.t.c new file mode 100644 index 0000000..c3f8932 --- /dev/null +++ b/cKit/Memory/c_Buddy.t.c @@ -0,0 +1,245 @@ +#include "c_Buddy.h" +#include +#include +#include // C11 標準 +#include + +// 2. 測試用例函數宣告 +void test_buddy_basic_alloc(); +void test_buddy_alignment(); +void test_buddy_oom(); +void test_buddy_coalescing(); +void test_1024(void); + +int main(int argc, char** argv){ + printf("--- 開始執行 c_Buddy_t 夥伴分配器測試 ---\n"); + + test_buddy_basic_alloc(); + test_buddy_alignment(); + test_buddy_oom(); + test_buddy_coalescing(); + test_1024(); + printf("--- 所有測試用例通過! ---\n"); + return 0; +} + +void c_Buddy_PrintStatus(c_Buddy_t *b) { + if (!b || !b->head || !b->tail) { + printf("[Buddy] 錯誤:分配器尚未初始化或為空指標。\n"); + return; + } + + printf("\n=================== Buddy Allocator Status ===================\n"); + printf("總記憶體範圍: %p ~ %p\n", (void*)b->head, (void*)b->tail); + printf("最小對齊基準 (Alignment): %d 位元組\n", b->alignment); + printf("--------------------------------------------------------------\n"); + printf("%-5s | %-16s | %-12s | %-10s\n", "編號", "區塊記憶體位址", "區塊大小(Size)", "狀態"); + printf("--------------------------------------------------------------\n"); + + c_BuddyBlock_t *curr = b->head; + int block_index = 0; + int total_free = 0; + int total_allocated = 0; + + // 使用與之前相同的單指針步進遍歷 + while (curr < b->tail) { + printf("[%3d] | %p | %-12d(%dKB) | %s\n", + block_index++, + (void*)curr, + curr->size, + curr->size/1024, + curr->is_free ? "FREE (空閒)" : "ALLOCATED (已佔用)"); + + if (curr->is_free) { + total_free += curr->size; + } else { + total_allocated += curr->size; + } + + // 精確前進到下一個區塊 + curr = (c_BuddyBlock_t *)((uint8_t *)curr + curr->size); + } + + printf("--------------------------------------------------------------\n"); + printf("摘要統計:已使用: %d 總位元組 | 剩餘空閒: %d 總位元組\n", total_allocated, total_free); + printf("==============================================================\n\n"); +} + +#define KB 1024 + +void test_1024(void) { + void* block = c_Memory_AlignedAlloc(1024*KB, 8); + + c_Buddy_t buddy; + c_Buddy_Init(&buddy, block, 1024*KB, 8); + + printf("Step 1: Alloc A=70KB\n"); + void* A = c_Buddy_Alloc(&buddy, 70*KB); + c_Buddy_PrintStatus(&buddy); + + printf("Step 2: Alloc B=35KB\n"); + void* B = c_Buddy_Alloc(&buddy, 35*KB); + c_Buddy_PrintStatus(&buddy); + + printf("Step 3: Alloc C=80KB\n"); + void* C = c_Buddy_Alloc(&buddy, 80*KB); + c_Buddy_PrintStatus(&buddy); + + printf("Step 4: Free A\n"); + c_Buddy_Free(&buddy, A); + c_Buddy_PrintStatus(&buddy); + + printf("Step 5: Alloc D=60KB\n"); + void* D = c_Buddy_Alloc(&buddy, 60*KB); + c_Buddy_PrintStatus(&buddy); + + printf("Step 6: Free B\n"); + c_Buddy_Free(&buddy, B); + c_Buddy_PrintStatus(&buddy); + + printf("Step 7: Free D\n"); + c_Buddy_Free(&buddy, D); + c_Buddy_PrintStatus(&buddy); + + printf("Step 8: Free C\n"); + c_Buddy_Free(&buddy, C); + c_Buddy_PrintStatus(&buddy); + + printf("Step 9: Alloc E=600KB\n"); + void* E = c_Buddy_Alloc(&buddy, 600*KB); // 实际分配 1024KB + c_Buddy_PrintStatus(&buddy); + + printf("Step 10: Free E\n"); + c_Buddy_Free(&buddy, E); + c_Buddy_PrintStatus(&buddy); + + c_Memory_AlignedFree(block); +} + +// ========================================== +// 測試用例 1:基礎初始化與 2 的冪次方裂變分配 +// ========================================== +void test_buddy_basic_alloc() { + printf("[測試] 基礎裂變分配...\n"); + + // 建立 1024 位元組的底層記憶體,對齊設為 8 + uint8_t* backing_buffer = (uint8_t*)malloc(1024); + c_Buddy_t buddy; + + c_Buddy_Init(&buddy, backing_buffer, 1024, 8); + assert(buddy.alignment == 8); + + // 請求分配 120 位元組(夥伴系統通常會向上對齊到 128 或包含 Block Header 的 2 的冪次方) + void* p1 = c_Buddy_Alloc(&buddy, 120); + assert(p1 != NULL); + + // 再次分配相同大小,地址應當緊鄰第一個區塊(按 2 冪次步進) + void* p2 = c_Buddy_Alloc(&buddy, 120); + assert(p2 != NULL); + assert(p2 > p1); + + // 清理記憶體池內部分配 + c_Buddy_Free(&buddy, p1); + c_Buddy_Free(&buddy, p2); + free(backing_buffer); + printf(" => 基礎分配測試成功\n"); +} + +// ========================================== +// 測試用例 2:記憶體對齊(Alignment)邊界驗證 +// ========================================== +void test_buddy_alignment() { + printf("[測試] 記憶體對齊要求...\n"); + + uint8_t* backing_buffer = (uint8_t*)c_Memory_AlignedAlloc(2048, 64); + c_Buddy_t buddy; + + // 強制對齊要求為 64 位元組 + c_Buddy_Init(&buddy, backing_buffer, 2048, 64); + + void* p1 = c_Buddy_Alloc(&buddy, 100); + void* p2 = c_Buddy_Alloc(&buddy, 100); + + assert(p1 != NULL); + assert(p2 != NULL); + + // 驗證返回的記憶體地址是否符合 64 位元組對齊 + assert(((uintptr_t)p1 % 64) == 0); + assert(((uintptr_t)p2 % 64) == 0); + + c_Buddy_Free(&buddy, p1); + c_Buddy_Free(&buddy, p2); + c_Memory_AlignedFree(backing_buffer); + printf(" => 對齊邊界測試成功\n"); +} + +// ========================================== +// 測試用例 3:記憶體溢出(OOM) +// ========================================== +void test_buddy_oom() { + printf("[測試] 記憶體溢出邊界...\n"); + + uint8_t* backing_buffer = (uint8_t*)malloc(512); + c_Buddy_t buddy; + c_Buddy_Init(&buddy, backing_buffer, 512, 8); + + // 嘗試分配超出整塊夥伴系統大小的記憶體 + void* p1 = c_Buddy_Alloc(&buddy, 600); + assert(p1 == NULL); + + // 分配剛好極限的記憶體(需扣除或包含 Header 空間,依實作而定) + void* p2 = c_Buddy_Alloc(&buddy, 256); + assert(p2 != NULL); + + void* p3 = c_Buddy_Alloc(&buddy, 256); + // 由於 512 已被完全填滿,此時應觸發 OOM + void* p4 = c_Buddy_Alloc(&buddy, 8); + assert(p4 == NULL); + + if (p2) c_Buddy_Free(&buddy, p2); + if (p3) c_Buddy_Free(&buddy, p3); + free(backing_buffer); + printf(" => OOM 邊界測試成功\n"); +} + +// ========================================== +// 測試用例 4:核心邏輯——夥伴自動合併(Coalescing) +// ========================================== +// 說明:當 A 和 B 互為夥伴區塊,且 A 分配出去後,若 A 與 B 均被釋放, +// 夥伴系統必須將兩者合併回原本更大的 2 的冪次方區塊。 +void test_buddy_coalescing() { + printf("[測試] 夥伴區塊自動合併(Coalescing)...\n"); + + uint8_t* backing_buffer = (uint8_t*)malloc(1024); + c_Buddy_t buddy; + // 初始化一個 1024 總大小的區塊 + c_Buddy_Init(&buddy, backing_buffer, 1024, 8); + + // 1. 將其切碎:連續分配 4 個 256 的空間(假設總空間剛好能切 4 塊) + void* p1 = c_Buddy_Alloc(&buddy, 200); // 裂變為 256 + void* p2 = c_Buddy_Alloc(&buddy, 200); // 裂變為 256 + void* p3 = c_Buddy_Alloc(&buddy, 200); // 裂變為 256 + void* p4 = c_Buddy_Alloc(&buddy, 200); // 裂變為 256 + + assert(p1 != NULL && p2 != NULL && p3 != NULL && p4 != NULL); + + // 2. 此時剩餘空間為 0,嘗試分配 512 必然失敗 + void* p_large_fail = c_Buddy_Alloc(&buddy, 500); + assert(p_large_fail == NULL); + + // 3. 釋放相鄰的夥伴 p1 與 p2 + c_Buddy_Free(&buddy, p1); + c_Buddy_Free(&buddy, p2); + + // 4. 關鍵驗證:如果實作了自動合併,p1 和 p2 釋放後應融合成一個 512 的大區塊 + // 此時再次申請 512 位元組(要求大小約 500),應該要能分配成功! + void* p_large_success = c_Buddy_Alloc(&buddy, 500); + assert(p_large_success != NULL); + + // 清理剩餘記憶體 + c_Buddy_Free(&buddy, p_large_success); + c_Buddy_Free(&buddy, p3); + c_Buddy_Free(&buddy, p4); + free(backing_buffer); + printf(" => 夥伴自動合併測試成功\n"); +} \ No newline at end of file diff --git a/cKit/Memory/c_FixedPool.c b/cKit/Memory/c_FixedPool.c new file mode 100644 index 0000000..9b78577 --- /dev/null +++ b/cKit/Memory/c_FixedPool.c @@ -0,0 +1,76 @@ +#include +#include + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +C_STATIC_FORCE_INLINE +c_err_t c_FixedPool_Replenish(c_FixedPool_t* self, void* block, int block_size){ + if (!self || !block || block_size < self->objSize) return C_ERR_PARAM; + + const c_size_t chunk_size = block_size/self->objSize; + uint8_t* start = (uint8_t*)block; + + uint8_t* last = &start[(chunk_size - 1) * self->objSize]; + for (uint8_t* p = start; pobjSize) { + ((struct c_FixedPoolLink_t*)p)->next = (struct c_FixedPoolLink_t*)(p + self->objSize); + } + ((struct c_FixedPoolLink_t*)last)->next = self->freelist; + self->freelist = (struct c_FixedPoolLink_t*)start; + return C_ERR_SUCCESS; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_FixedPool_Init(c_FixedPool_t* self, int objSize, void* block, int block_size) { + if (!self || objSize==0 || !block || block_size < objSize) return C_ERR_PARAM; + + self->objSize = objSize>=sizeof(struct c_FixedPoolLink_t)?objSize:sizeof(struct c_FixedPoolLink_t); + self->objSize = C_ALIGN_UPB(self->objSize, C_ALIGN_SIZE); + self->instanceCount = 0; + self->freelist = 0; + return c_FixedPool_Replenish(self, block, block_size); +} + +void c_FixedPool_Destroy(c_FixedPool_t* self) { + if (!self) return; + + if (0==self->instanceCount) { + self->freelist = 0; + } + assert(0==self->instanceCount); +} + +c_err_t c_FixedPool_AddBlock(c_FixedPool_t* self, void* block, int block_size) { + return c_FixedPool_Replenish(self, block, block_size); +} + +void* c_FixedPool_Alloc(c_FixedPool_t* self) { + if (!self || !self->freelist) { + return NULL; + } + struct c_FixedPoolLink_t* p = self->freelist; + self->freelist = p->next; + ++self->instanceCount; + return p; +} + +void c_FixedPool_Free(c_FixedPool_t* self, void* ptr) { + if (!self || !ptr) { + return; + } + + struct c_FixedPoolLink_t* p = (struct c_FixedPoolLink_t*)ptr; + p->next = self->freelist; + self->freelist = p; + --self->instanceCount; +} + +void c_FixedPool_DryUp(c_FixedPool_t* self) { + if (!self) return; + self->freelist = 0; + self->instanceCount = 0; +} + diff --git a/cKit/Memory/c_FixedPool.h b/cKit/Memory/c_FixedPool.h new file mode 100644 index 0000000..7afdde2 --- /dev/null +++ b/cKit/Memory/c_FixedPool.h @@ -0,0 +1,25 @@ +#ifndef INCLUDED_C_FIXEDPOOL_H +#define INCLUDED_C_FIXEDPOOL_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + int objSize; + c_size_t instanceCount; + struct c_FixedPoolLink_t{ struct c_FixedPoolLink_t* next;} * freelist; +}c_FixedPool_t; + +c_err_t c_FixedPool_Init(c_FixedPool_t* self, int objSize, void* block, int block_size); +void c_FixedPool_Destroy(c_FixedPool_t* self); +c_err_t c_FixedPool_AddBlock(c_FixedPool_t* self, void* block, int block_size); +void* c_FixedPool_Alloc(c_FixedPool_t* self); +void c_FixedPool_Free(c_FixedPool_t* self, void* ptr); +void c_FixedPool_DryUp(c_FixedPool_t* self); + +#endif /*INCLUDED_C_FIXEDPOOL_H*/ diff --git a/cKit/Memory/c_FixedPool.t.c b/cKit/Memory/c_FixedPool.t.c new file mode 100644 index 0000000..f97b1f1 --- /dev/null +++ b/cKit/Memory/c_FixedPool.t.c @@ -0,0 +1,56 @@ +#include "c_FixedPool.h" +#include +#include + +typedef struct { + int id; + char name[12]; +} TestObject; + +int main(int argc, char** argv){ + + printf("--- 開始執行 c_FixedPool_t 測試 ---\n"); + + // 建立兩個獨立的記憶體緩衝區 + uint8_t block1[32]; // 約可容納 2 個 TestObject (16 bytes * 2 = 32, 考慮對齊足夠) + uint8_t block2[32]; + + c_FixedPool_t pool; + + // 1. 初始化並掛載第一個區塊 + c_err_t err = c_FixedPool_Init(&pool, sizeof(TestObject), block1, sizeof(block1)); + assert(err == C_ERR_SUCCESS); + printf("初始化成功,當前可用總數: %zu\n", pool.instanceCount); + + // 2. 持續分配直到第一個區塊耗盡 + void* p1 = c_FixedPool_Alloc(&pool); + void* p2 = c_FixedPool_Alloc(&pool); + void* p3 = c_FixedPool_Alloc(&pool); // 超出 block1 的容量,應為 NULL + + assert(p1 != NULL); + assert(p2 != NULL); + assert(p3 == NULL); + printf("第一階段分配完畢,池已成功耗盡 (OOM 觸發)。\n"); + + // 3. 動態追加第二個記憶體區塊 (AddBlock) + err = c_FixedPool_AddBlock(&pool, block2, sizeof(block2)); + assert(err == C_ERR_SUCCESS); + printf("成功追加新區塊!當前可用總數增加。\n"); + + // 4. 再次分配,此時應該能成功從 block2 取得記憶體 + void* p4 = c_FixedPool_Alloc(&pool); + assert(p4 != NULL); + assert(p4 != p1 && p4 != p2); + + // 5. 測試回收與重複利用 + c_FixedPool_Free(&pool, p1); + void* p_reuse = c_FixedPool_Alloc(&pool); + assert(p_reuse == p1); // 應優先拿回剛釋放的 p1 地址 + + // 6. 清理 + c_FixedPool_DryUp(&pool); + c_FixedPool_Destroy(&pool); + + printf("--- c_FixedPool_t 所有測試案例通過! ---\n"); + return 0; +} diff --git a/cKit/Memory/c_Memory.c b/cKit/Memory/c_Memory.c new file mode 100644 index 0000000..1996a5a --- /dev/null +++ b/cKit/Memory/c_Memory.c @@ -0,0 +1,7 @@ +#include + + + + + + diff --git a/cKit/Memory/c_Memory.h b/cKit/Memory/c_Memory.h new file mode 100644 index 0000000..d24e469 --- /dev/null +++ b/cKit/Memory/c_Memory.h @@ -0,0 +1,78 @@ +#ifndef INCLUDED_C_MEMORY_H +#define INCLUDED_C_MEMORY_H + +#ifndef INCLUDED_STDLIB_H +#define INCLUDED_STDLIB_H +#include +#endif /*INCLUDED_STDLIB_H*/ + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +#ifndef INCLUDED_C_ALIGNMENT_H +#include +#endif /*INCLUDED_C_ALIGNMENT_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +C_STATIC_FORCE_INLINE +void* c_Memory_Alloc(c_size_t size) { + return malloc(size); +} + +C_STATIC_FORCE_INLINE +void* c_Memory_Realloc(void* ptr, c_size_t size) { + return realloc(ptr, size); +} + +C_STATIC_FORCE_INLINE +void* c_Memory_Calloc(c_size_t count, c_size_t size) { + return calloc(count, size); +} + +C_STATIC_FORCE_INLINE +void c_Memory_Free(void* ptr) { + if (ptr) free(ptr); +} + +C_STATIC_FORCE_INLINE +void* c_Memory_AlignedAlloc(c_size_t size, c_size_t alignment) { +#if defined(_MSC_VER) || defined(__MINGW32__) + // Windows 環境下使用微軟特有的對齊配置函數 + return _aligned_malloc(size, alignment); +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + // 真正的 C11 環境,且支援 aligned_alloc + return aligned_alloc(alignment, size); +#else + // POSIX 環境 (Linux/macOS) 的備用方案 + void* ptr = NULL; + if (posix_memalign(&ptr, alignment, size) != 0) return NULL; + return ptr; +#endif +} + +C_STATIC_FORCE_INLINE +void c_Memory_AlignedFree(void* ptr) { +#if defined(_MSC_VER) || defined(__MINGW32__) + _aligned_free(ptr); +#else + free(ptr); +#endif +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define C_ALLOC(n) c_Memory_Alloc(n) +#define C_REALLOC(p, n) c_Memory_Realloc((p), (n)) +#define C_CALLOC(x, n) c_Memory_Calloc((x), (n)) +#define C_FREE(p) do{if(p){c_Memory_Free(p); (p)=NULL;}}while(0) + +#define C_RESIZE(p, n) (p)=C_REALLOC(p, n) + +#define C_NEW(p) (p)=C_ALLOC(sizeof(*(p))) +#define C_NEW0(p) (p)=C_CALLOC(1, sizeof(*(p))) + +#endif /*INCLUDED_C_MEMORY_H*/ diff --git a/cKit/Memory/c_Pool.c b/cKit/Memory/c_Pool.c new file mode 100644 index 0000000..0ad2f3d --- /dev/null +++ b/cKit/Memory/c_Pool.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include "c_Alignment.h" + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define FREE(x) do{ \ + if(x){ \ + free(x); \ + (x) = NULL; \ + } \ +}while(0) + +#define ALLOC(x) malloc(x) +#define NEW(p) (p)=ALLOC(sizeof(*(p))) + +#define DEFAULT_CHUNK_SIZE 10 + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +typedef struct c_PoolBlockLink_t { + void* block; + struct c_PoolBlockLink_t* next; +}c_PoolBlockLink_t; + +struct c_PoolBlockList_t{ + c_PoolBlockLink_t* list; +}; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +C_STATIC_FORCE_INLINE +void c_PoolBlockLink_Init(c_PoolBlockLink_t* self, void* p, c_PoolBlockLink_t* next) { + self->block = p; + self->next = next; +} + +C_STATIC_FORCE_INLINE +void c_PoolBlockList_Init(c_PoolBlockList_t* self) { + self->list = NULL; +} + +C_STATIC_FORCE_INLINE +void c_PoolBlockList_Destroy(c_PoolBlockList_t* self) { + if (!self) return; + while (self->list){ + c_PoolBlockLink_t* q = self->list; + self->list = q->next; + FREE(q); + } +} + +C_STATIC_FORCE_INLINE +void* c_PoolBlockList_Alloc(c_PoolBlockList_t* self, c_size_t bytes) { + c_size_t block_size = bytes + sizeof(c_PoolBlockLink_t); + block_size = C_ALIGN_UPB(block_size, C_ALIGN_SIZE); + c_PoolBlockLink_t* p = ALLOC(block_size); + if (!p) { + return NULL; + } + c_PoolBlockLink_Init(p, p+1, self->list); + self->list = p; + return p->block; +} + +C_STATIC_FORCE_INLINE +c_err_t c_Pool_Replenish(c_Pool_t* self) { + c_size_t size = self->chunkSize * self->objSize; + uint8_t* start = (uint8_t*)c_PoolBlockList_Alloc(self->blockAllocator, size); + if (!start) return C_ERR_NOMEM; + uint8_t* last = &start[(self->chunkSize - 1) * self->objSize]; + for (uint8_t* p = start; pobjSize) { + ((struct c_PoolLink_t*)p)->next = (struct c_PoolLink_t*)(p + self->objSize); + } + ((struct c_PoolLink_t*)last)->next = NULL; + self->freelist = (struct c_PoolLink_t*)start; + return C_ERR_SUCCESS; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_Pool_Init(c_Pool_t* self, int objSize, int chunkSize) { + self->freelist = 0; + self->objSize = objSize>=sizeof(struct c_PoolLink_t)?objSize:sizeof(struct c_PoolLink_t); + self->chunkSize = (chunkSize > 0)?chunkSize:DEFAULT_CHUNK_SIZE; + self->instanceCount = 0; + NEW(self->blockAllocator); + if (!self->blockAllocator) { + return C_ERR_NOMEM; + } + c_PoolBlockList_Init(self->blockAllocator); + + return C_ERR_SUCCESS; +} + +void c_Pool_Destroy(c_Pool_t* self) { + if (0==self->instanceCount) { + c_PoolBlockList_Destroy(self->blockAllocator); + FREE(self->blockAllocator); + self->freelist = NULL; + } + assert(0==self->instanceCount); +} + +void* c_Pool_Alloc(c_Pool_t* self) { + if (!self->freelist) { + if (c_Pool_Replenish(self)!=C_ERR_SUCCESS) { + return NULL; + } + } + struct c_PoolLink_t* p = self->freelist; + self->freelist = p->next; + ++self->instanceCount; + return p; +} + +void c_Pool_Free(c_Pool_t* self, void* ptr) { + if (!self || !ptr) { + return; + } + + struct c_PoolLink_t* p = (struct c_PoolLink_t*)ptr; + p->next = self->freelist; + self->freelist = p; + --self->instanceCount; +} + +void c_Pool_DryUp(c_Pool_t* self) { + c_PoolBlockList_Destroy(self->blockAllocator); + FREE(self->blockAllocator); + self->instanceCount = 0; +} + diff --git a/cKit/Memory/c_Pool.h b/cKit/Memory/c_Pool.h new file mode 100644 index 0000000..80dbf95 --- /dev/null +++ b/cKit/Memory/c_Pool.h @@ -0,0 +1,36 @@ +#ifndef INCLUDED_C_POOL_H +#define INCLUDED_C_POOL_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct c_PoolBlockList_t c_PoolBlockList_t; + +typedef struct { + c_PoolBlockList_t* blockAllocator; + int objSize; + int chunkSize; + c_size_t instanceCount; + struct c_PoolLink_t{ struct c_PoolLink_t* next;} * freelist; +}c_Pool_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_Pool_Init(c_Pool_t* self, int objSize, int chunkSize); + +void c_Pool_Destroy(c_Pool_t* self); + +void* c_Pool_Alloc(c_Pool_t* self); + +void c_Pool_Free(c_Pool_t* self, void* ptr); + +void c_Pool_DryUp(c_Pool_t* self); + + + +#endif /*INCLUDED_C_POOL_H*/ diff --git a/cKit/Memory/c_Pool.t.c b/cKit/Memory/c_Pool.t.c new file mode 100644 index 0000000..181c4bf --- /dev/null +++ b/cKit/Memory/c_Pool.t.c @@ -0,0 +1,52 @@ +#include "c_Pool.h" +#include +#include + +// 定義一個測試用的結構體(例如網路連線上下文) +typedef struct { + int connection_id; + char ip[16]; +} ConnectionContext; + +int main(int argc, char** argv){ + printf("--- 開始執行 c_Pool_t 記憶體池測試 ---\n"); + + // 建立一個足夠容納 3 個 ConnectionContext 的靜態緩衝區 + // 考慮到 64 位元對齊,ConnectionContext 佔 24 位元組 (4 + 16 補齊到 24) + + c_Pool_t pool; + c_Pool_Init(&pool, sizeof(ConnectionContext), 3); + + // 1. 測試分配 + ConnectionContext* conn1 = (ConnectionContext*)c_Pool_Alloc(&pool); + ConnectionContext* conn2 = (ConnectionContext*)c_Pool_Alloc(&pool); + ConnectionContext* conn3 = (ConnectionContext*)c_Pool_Alloc(&pool); + + assert(conn1 != NULL); + assert(conn2 != NULL); + assert(conn3 != NULL); + assert(conn1 != conn2); // 確保分配到不同區塊 + + // 寫入資料驗證 + conn1->connection_id = 101; + conn2->connection_id = 102; + printf("分配成功,conn1 ID: %d, conn2 ID: %d\n", conn1->connection_id, conn2->connection_id); + + // 2. 測試記憶體池全滿 (OOM) + ConnectionContext* conn4 = (ConnectionContext*)c_Pool_Alloc(&pool); + assert(conn4 != NULL); // 第 4 個分配應失敗返回 NULL + printf("記憶體池已滿邊界測試成功!\n"); + + // 3. 測試釋放與回收再分配 + c_Pool_Free(&pool, conn2); // 釋放第 2 個區塊 + + // 再次分配,此時應該會優先拿到剛剛釋放的 conn2 區塊 + ConnectionContext* conn_reuse = (ConnectionContext*)c_Pool_Alloc(&pool); + assert(conn_reuse == conn2); + printf("記憶體回收與 O(1) 再分配測試成功!\n"); + + c_Pool_DryUp(&pool); + c_Pool_Destroy(&pool); + printf("--- c_Pool_t 所有測試順利通過! ---\n"); + return 0; +} diff --git a/cKit/Search/c_BST.c b/cKit/Search/c_BST.c new file mode 100644 index 0000000..5d1399a --- /dev/null +++ b/cKit/Search/c_BST.c @@ -0,0 +1,158 @@ +#include +#include + +/** + * Helper accessors to safely locate key and value buffers within a generic node allocation block. + */ +C_STATIC_FORCE_INLINE void* c_BST_NodeKey(c_BSTNode_t* node) { + return (void*)((char*)node + sizeof(c_BSTNode_t)); +} + +C_STATIC_FORCE_INLINE void* c_BST_NodeVal(c_BSTNode_t* node, c_size_t key_size) { + return (void*)((char*)node + sizeof(c_BSTNode_t) + key_size); +} + +/** + * Creates and initializes a standalone tree node layout. + */ +C_STATIC_FORCE_INLINE +c_BSTNode_t* c_BST_CreateNode(const void* key, const void* val, c_size_t ks, c_size_t vs) { + c_BSTNode_t* node = (c_BSTNode_t*)C_ALLOC(sizeof(c_BSTNode_t) + ks + vs); + if (node == NULL) return NULL; + + node->left = NULL; + node->right = NULL; + memcpy(c_BST_NodeKey(node), key, ks); + memcpy(c_BST_NodeVal(node, ks), val, vs); + return node; +} + +/** + * Internal recursive post-order destructor helper. + */ +static void c_BST_DestroyNodes(c_BSTNode_t* node) { + if (node == NULL) return; + c_BST_DestroyNodes(node->left); + c_BST_DestroyNodes(node->right); + C_FREE(node); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_BST_Init(c_BST_t* tree, c_size_t key_size, c_size_t val_size, int (*compar)(const void*, const void*)) { + if (tree == NULL || key_size == 0 || val_size == 0 || compar == NULL) return C_ERR_PARAM; + tree->root = NULL; + tree->key_size = key_size; + tree->val_size = val_size; + tree->size = 0; + tree->compar = compar; + return C_ERR_OK; +} + +void c_BST_Destroy(c_BST_t* tree) { + if (tree) { + c_BST_DestroyNodes(tree->root); + tree->root = NULL; + tree->size = 0; + } +} + +c_err_t c_BST_Clear(c_BST_t* tree) { + if (tree == NULL) return C_ERR_PARAM; + c_BST_DestroyNodes(tree->root); + tree->root = NULL; + tree->size = 0; + return C_ERR_OK; +} + +c_bool_t c_BST_Contains(const c_BST_t* tree, const void* key) { + if (tree == NULL || key == NULL) return C_FALSE; + c_BSTNode_t* curr = tree->root; + while (curr != NULL) { + int cmp = tree->compar(key, c_BST_NodeKey(curr)); + if (cmp == 0) return C_TRUE; + curr = (cmp < 0) ? curr->left : curr->right; + } + return C_FALSE; +} + +c_err_t c_BST_Put(c_BST_t* tree, const void* key, const void* val) { + if (tree == NULL || key == NULL || val == NULL) return C_ERR_PARAM; + + c_BSTNode_t** link = &tree->root; + c_BSTNode_t* curr = tree->root; + + while (curr != NULL) { + int cmp = tree->compar(key, c_BST_NodeKey(curr)); + if (cmp == 0) { + // Overwrite existing value for matching symbol key + memcpy(c_BST_NodeVal(curr, tree->key_size), val, tree->val_size); + return C_ERR_OK; + } + link = (cmp < 0) ? &curr->left : &curr->right; + curr = *link; + } + + // Key is unique, construct a new node configuration structure + c_BSTNode_t* new_node = c_BST_CreateNode(key, val, tree->key_size, tree->val_size); + if (new_node == NULL) return C_ERR_NOMEM; + + *link = new_node; + tree->size++; + return C_ERR_OK; +} + +void* c_BST_Get(const c_BST_t* tree, const void* key) { + if (tree == NULL || key == NULL) return NULL; + c_BSTNode_t* curr = tree->root; + while (curr != NULL) { + int cmp = tree->compar(key, c_BST_NodeKey(curr)); + if (cmp == 0) return c_BST_NodeVal(curr, tree->key_size); + curr = (cmp < 0) ? curr->left : curr->right; + } + return NULL; +} + +c_err_t c_BST_Delete(c_BST_t* tree, const void* key) { + if (tree == NULL || key == NULL) return C_ERR_PARAM; + + c_BSTNode_t** link = &tree->root; + c_BSTNode_t* curr = tree->root; + + while (curr != NULL) { + int cmp = tree->compar(key, c_BST_NodeKey(curr)); + if (cmp == 0) break; + link = (cmp < 0) ? &curr->left : &curr->right; + curr = *link; + } + + if (curr == NULL) return C_ERR_NOT_FOUND; + + // Standard Hibbard deletion implementation sequence matching tree boundaries + if (curr->left == NULL) { + *link = curr->right; + } else if (curr->right == NULL) { + *link = curr->left; + } else { + // Node has two children; locate the successor node (smallest node in the right sub-tree) + c_BSTNode_t** succ_link = &curr->right; + c_BSTNode_t* succ = curr->right; + while (succ->left != NULL) { + succ_link = &succ->left; + succ = succ->left; + } + + // Delink the successor node from its previous position + *succ_link = succ->right; + + // Route child structures of the node being deleted into the successor + succ->left = curr->left; + succ->right = curr->right; + *link = succ; + } + + C_FREE(curr); + tree->size--; + return C_ERR_OK; +} diff --git a/cKit/Search/c_BST.h b/cKit/Search/c_BST.h new file mode 100644 index 0000000..62aeeff --- /dev/null +++ b/cKit/Search/c_BST.h @@ -0,0 +1,40 @@ +#ifndef INCLUDED_C_BST_H +#define INCLUDED_C_BST_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// Forward declaration of internal node structure +typedef struct c_BSTNode { + struct c_BSTNode* left; // Pointer to left child + struct c_BSTNode* right; // Pointer to right child + // Node payload layout: key block followed immediately by the value block in memory +} c_BSTNode_t; + +// Binary Search Tree Context Structure +typedef struct { + c_BSTNode_t* root; // Root node pointer + c_size_t key_size; // Size of each key in bytes + c_size_t val_size; // Size of each value in bytes + c_size_t size; // Total number of nodes in the tree + int (*compar)(const void*, const void*); // Key comparison rule pointer +} c_BST_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_BST_Init(c_BST_t* tree, c_size_t key_size, c_size_t val_size, int (*compar)(const void*, const void*)) ; +void c_BST_Destroy(c_BST_t* tree); + +c_err_t c_BST_Clear(c_BST_t* tree); +c_bool_t c_BST_Contains(const c_BST_t* tree, const void* key); +c_err_t c_BST_Put(c_BST_t* tree, const void* key, const void* val); +void* c_BST_Get(const c_BST_t* tree, const void* key); +c_err_t c_BST_Delete(c_BST_t* tree, const void* key) ; + +#endif /*INCLUDED_C_BST_H*/ diff --git a/cKit/Search/c_BST.t.c b/cKit/Search/c_BST.t.c new file mode 100644 index 0000000..ab6234b --- /dev/null +++ b/cKit/Search/c_BST.t.c @@ -0,0 +1,78 @@ +#include "c_BST.h" +#include +#include + +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d)\n", msg, (int)(expected), (int)(actual)); \ + return C_FALSE; \ + } \ + } while(0) + +// Value data block mapped directly to distinct scalar keys +typedef struct { + char runtime_state[16]; + int scheduling_weight; +} ProcessMetrics; + +int compareIntPids(const void* a, const void* b) { + return (*(int*)a - *(int*)b); +} + +c_bool_t test_binary_search_tree(void) { + c_BST_t bst; + + EXPECT_EQ(c_BST_Init(&bst, sizeof(int), sizeof(ProcessMetrics), compareIntPids), C_ERR_OK, "Init failed"); + + int pid1 = 4500; ProcessMetrics m1 = { "RUNNING", 10 }; + int pid2 = 1200; ProcessMetrics m2 = { "SLEEPING", 2 }; + int pid3 = 8900; ProcessMetrics m3 = { "BLOCKED", 5 }; + int pid4 = 3100; ProcessMetrics m4 = { "ZOMBIE", 0 }; + + // 1. Structural Insertion Pipeline Validation + EXPECT_EQ(c_BST_Put(&bst, &pid1, &m1), C_ERR_OK, "Put pid1 failed"); + EXPECT_EQ(c_BST_Put(&bst, &pid2, &m2), C_ERR_OK, "Put pid2 failed"); + EXPECT_EQ(c_BST_Put(&bst, &pid3, &m3), C_ERR_OK, "Put pid3 failed"); + EXPECT_EQ(c_BST_Put(&bst, &pid4, &m4), C_ERR_OK, "Put pid4 failed"); + EXPECT_EQ(bst.size, 4, "Tree dimension size tracker calculation inaccurate"); + + // Check value updating via key collisions + ProcessMetrics m1_updated = { "SUSPENDED", 8 }; + EXPECT_EQ(c_BST_Put(&bst, &pid1, &m1_updated), C_ERR_OK, "Overwriting element key failed"); + EXPECT_EQ(bst.size, 4, "Tree size tracking incremented incorrectly on updating overwrite"); + + // 2. Data Retrieval Lookup Paths + int look_pid = 4500; + ProcessMetrics* fetched = (ProcessMetrics*)c_BST_Get(&bst, &look_pid); + EXPECT_EQ(fetched != NULL && strcmp(fetched->runtime_state, "SUSPENDED") == 0, C_TRUE, "Lookup retrieved incorrect mapping segment"); + + int miss_pid = 9999; + EXPECT_EQ(c_BST_Get(&bst, &miss_pid) == NULL, C_TRUE, "Key miss lookup did not return NULL"); + EXPECT_EQ(c_BST_Contains(&bst, &look_pid), C_TRUE, "Contains failed reporting registered key tracking rules"); + + // 3. Node Removal & Hibbard Rebalancing Checks + // Delete pid1 (4500), which represents a root node with two children (1200 and 8900) + EXPECT_EQ(c_BST_Delete(&bst, &pid1), C_ERR_OK, "Node deletion failed"); + EXPECT_EQ(c_BST_Contains(&bst, &pid1), C_FALSE, "Deleted node reference remains inside structure"); + EXPECT_EQ(bst.size, 3, "Tree size tracking value did not decrement correctly"); + + // Verify sub-trees remain completely searchable and accessible post structural mutation + int verify_child_pid = 3100; + ProcessMetrics* child_check = (ProcessMetrics*)c_BST_Get(&bst, &verify_child_pid); + EXPECT_EQ(child_check != NULL && strcmp(child_check->runtime_state, "ZOMBIE") == 0, C_TRUE, "Subtree structure broken during root transformation sequences"); + + c_BST_Destroy(&bst); + return C_TRUE; +} + +int main(void) { + printf("=== Starting Framework Unit Testing: c_BST ===\n"); + + if (test_binary_search_tree()) { + printf(" [PASS] Binary Search Tree Processing and Deletion Lifecycle Verified Successfully.\n"); + } else { + printf(" [FAIL] Binary Search Tree Component Encountered Evaluation Errors.\n"); + } + return 0; +} diff --git a/cKit/Search/c_BinarySearch.c b/cKit/Search/c_BinarySearch.c new file mode 100644 index 0000000..b3c7a34 --- /dev/null +++ b/cKit/Search/c_BinarySearch.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Search/c_BinarySearch.h b/cKit/Search/c_BinarySearch.h new file mode 100644 index 0000000..ef4456f --- /dev/null +++ b/cKit/Search/c_BinarySearch.h @@ -0,0 +1,49 @@ +#ifndef INCLUDED_C_BINARYSEARCH_H +#define INCLUDED_C_BINARYSEARCH_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * 通用二分查找函数 + * @param key 指向要查找的目标元素的指针 + * @param base 指向待查找数组首元素的指针 + * @param num 数组中元素的个数 + * @param size 每个元素的大小(以字节为单位,使用 sizeof 获取) + * @param compar 指向比较函数的指针(由用户提供比较逻辑) + * @return 找到则返回指向该元素的指针,未找到则返回 NULL + */ +C_STATIC_FORCE_INLINE +void* c_BinarySearch(const void* key, const void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + c_size_t left = 0; + c_size_t right = num; // 使用左闭右开区间 [left, right) 逻辑更清晰 + + while (left < right) { + c_size_t mid = left + (right - left) / 2; + + // 计算 mid 元素的内存地址:首地址 + 索引 * 每个元素的字节大小 + // 先强转为 char* 是为了按单字节进行指针偏移 + const void* midElem = (const char*)base + (mid * size); + + // 调用用户自定义的比较函数 + int cmp = compar(key, midElem); + + if (cmp == 0) { + return (void*)midElem; // 找到目标,返回其在数组中的地址 + } else if (cmp > 0) { + left = mid + 1; // key 大于 midElem,往右半部分找 + } else { + right = mid; // key 小于 midElem,往左半部分找 + } + } + + return NULL; // 未找到 +} + +#endif /*INCLUDED_C_BINARYSEARCH_H*/ diff --git a/cKit/Search/c_BinarySearch.t.c b/cKit/Search/c_BinarySearch.t.c new file mode 100644 index 0000000..58a70af --- /dev/null +++ b/cKit/Search/c_BinarySearch.t.c @@ -0,0 +1,52 @@ +#include "c_BinarySearch.h" +#include +#include + +typedef struct { + int id; + char name[20]; + double score; +} Student; + +// 用户自定义的比较函数:按学号 (id) 升序比较 +int compareStudentsById(const void* a, const void* b) { + const Student* s1 = (const Student*)a; + const Student* s2 = (const Student*)b; + + if (s1->id < s2->id) return -1; + if (s1->id > s2->id) return 1; + return 0; +} + +int main() { + // 准备一个已经按 id 排好序的结构体数组 + Student students[] = { + {101, "Alice", 92.5}, + {105, "Bob", 88.0}, + {109, "Charlie", 95.0}, + {112, "David", 79.5} + }; + size_t count = sizeof(students) / sizeof(students[0]); + + // 创建一个查找模板(只需要填入你想查找的依据字段) + Student targetKey; + targetKey.id = 109; + + // 调用通用二分查找 + Student* result = (Student*)c_BinarySearch( + &targetKey, // 目标元素的指针 + students, // 数组首地址 + count, // 元素数量 + sizeof(Student), // 每个结构体占用的字节大小 + compareStudentsById // 比较函数的指针 + ); + + // 检查并输出结果 + if (result != NULL) { + printf("成功找到!姓名: %s, 成绩: %.1f\n", result->name, result->score); + } else { + printf("未找到学号为 %d 的学生。\n", targetKey.id); + } + + return 0; +} \ No newline at end of file diff --git a/cKit/Search/c_BinarySearchST.c b/cKit/Search/c_BinarySearchST.c new file mode 100644 index 0000000..672aade --- /dev/null +++ b/cKit/Search/c_BinarySearchST.c @@ -0,0 +1,168 @@ +#include +#include + +/** + * Core Rank/Binary Search operation. + * Returns the exact index if the key is found, or the insertion slot index if not found. + */ +static inline c_size_t c_BSST_Rank(const c_BinarySearchST_t* st, const void* key, c_bool_t* out_found) { + c_size_t left = 0; + c_size_t right = st->size; + char* keys_base = (char*)st->keys; + c_size_t ks = st->key_size; + + while (left < right) { + c_size_t mid = left + (right - left) / 2; + int cmp = st->compar(key, keys_base + (mid * ks)); + + if (cmp == 0) { + if (out_found) *out_found = C_TRUE; + return mid; + } else if (cmp > 0) { + left = mid + 1; + } else { + right = mid; + } + } + + if (out_found) *out_found = C_FALSE; + return left; // 'left' represents the precise index where the key *should* go +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_BinarySearchST_Init(c_BinarySearchST_t* st, c_size_t initial_capacity, + c_size_t key_size, c_size_t val_size, + int (*compar)(const void*, const void*)) { + if (st == NULL || key_size == 0 || val_size == 0 || compar == NULL) return C_ERR_PARAM; + + st->capacity = (initial_capacity > 0) ? initial_capacity : 4; + st->key_size = key_size; + st->val_size = val_size; + st->size = 0; + st->compar = compar; + + st->keys = C_ALLOC(st->capacity * key_size); + st->vals = C_ALLOC(st->capacity * val_size); + + if (st->keys == NULL || st->vals == NULL) { + C_FREE(st->keys); + C_FREE(st->vals); + return C_ERR_NOMEM; + } + + return C_ERR_OK; +} + +void c_BinarySearchST_Destroy(c_BinarySearchST_t* st) { + if (st) { + C_FREE(st->keys); + C_FREE(st->vals); + st->size = 0; + st->capacity = 0; + } +} + +c_err_t c_BinarySearchST_Clear(c_BinarySearchST_t* st) { + if (st == NULL) return C_ERR_PARAM; + st->size = 0; // Soft reset clears tracking variables but keeps allocated memory blocks + return C_ERR_OK; +} + +c_bool_t c_BinarySearchST_Contains(const c_BinarySearchST_t* st, const void* key) { + if (st == NULL || key == NULL) return C_FALSE; + c_bool_t found = C_FALSE; + c_BSST_Rank(st, key, &found); + return found; +} + +c_err_t c_BinarySearchST_Put(c_BinarySearchST_t* st, const void* key, const void* val) { + if (st == NULL || key == NULL || val == NULL) return C_ERR_PARAM; + + c_bool_t found = C_FALSE; + c_size_t idx = c_BSST_Rank(st, key, &found); + + char* keys_base = (char*)st->keys; + char* vals_base = (char*)st->vals; + c_size_t ks = st->key_size; + c_size_t vs = st->val_size; + + // Symbol Table Behavior: If the key already exists, overwrite the value + if (found) { + memcpy(vals_base + (idx * vs), val, vs); + return C_ERR_OK; + } + + // Dynamic parallel array capacity expansion + if (st->size >= st->capacity) { + c_size_t new_capacity = st->capacity * 2; + void* new_keys = C_ALLOC(new_capacity * ks); + void* new_vals = C_ALLOC(new_capacity * vs); + + if (new_keys == NULL || new_vals == NULL) { + C_FREE(new_keys); + C_FREE(new_vals); + return C_ERR_NOMEM; + } + + if (st->size > 0) { + memcpy(new_keys, st->keys, st->size * ks); + memcpy(new_vals, st->vals, st->size * vs); + } + + C_FREE(st->keys); C_FREE(st->vals); + st->keys = new_keys; st->vals = new_vals; + st->capacity = new_capacity; + keys_base = (char*)st->keys; + vals_base = (char*)st->vals; + } + + // Shift memory components to create a gap for insertion + if (idx < st->size) { + memmove(keys_base + ((idx + 1) * ks), keys_base + (idx * ks), (st->size - idx) * ks); + memmove(vals_base + ((idx + 1) * vs), vals_base + (idx * vs), (st->size - idx) * vs); + } + + // Drop elements directly into parallel array channels + memcpy(keys_base + (idx * ks), key, ks); + memcpy(vals_base + (idx * vs), val, vs); + st->size++; + + return C_ERR_OK; +} + +void* c_BinarySearchST_Get(const c_BinarySearchST_t* st, const void* key) { + if (st == NULL || key == NULL) return NULL; + + c_bool_t found = C_FALSE; + c_size_t idx = c_BSST_Rank(st, key, &found); + + if (found) { + return (char*)st->vals + (idx * st->val_size); + } + return NULL; +} + +c_err_t c_BinarySearchST_Delete(c_BinarySearchST_t* st, const void* key) { + if (st == NULL || key == NULL) return C_ERR_PARAM; + + c_bool_t found = C_FALSE; + c_size_t idx = c_BSST_Rank(st, key, &found); + + if (!found) return C_ERR_NOT_FOUND; + + char* keys_base = (char*)st->keys; + char* vals_base = (char*)st->vals; + c_size_t ks = st->key_size; + c_size_t vs = st->val_size; + + // Compress parallel entries down over the deleted item slot + if (idx < st->size - 1) { + memmove(keys_base + (idx * ks), keys_base + ((idx + 1) * ks), (st->size - 1 - idx) * ks); + memmove(vals_base + (idx * vs), vals_base + ((idx + 1) * vs), (st->size - 1 - idx) * vs); + } + + st->size--; + return C_ERR_OK; +} diff --git a/cKit/Search/c_BinarySearchST.h b/cKit/Search/c_BinarySearchST.h new file mode 100644 index 0000000..1c81512 --- /dev/null +++ b/cKit/Search/c_BinarySearchST.h @@ -0,0 +1,35 @@ +#ifndef INCLUDED_C_BINARYSEARCHST_H +#define INCLUDED_C_BINARYSEARCHST_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + void* keys; // Flat parallel array block storing keys + void* vals; // Flat parallel array block storing values + c_size_t key_size; // Size of each key in bytes (sizeof(Key)) + c_size_t val_size; // Size of each value in bytes (sizeof(Value)) + c_size_t capacity; // Maximum allocated element capacity + c_size_t size; // Current active entry count + int (*compar)(const void*, const void*); // Key comparison rule pointer +} c_BinarySearchST_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ +c_err_t c_BinarySearchST_Init(c_BinarySearchST_t* st, c_size_t initial_capacity, + c_size_t key_size, c_size_t val_size, + int (*compar)(const void*, const void*)); +void c_BinarySearchST_Destroy(c_BinarySearchST_t* st); + +c_err_t c_BinarySearchST_Clear(c_BinarySearchST_t* st); +c_bool_t c_BinarySearchST_Contains(const c_BinarySearchST_t* st, const void* key); +c_err_t c_BinarySearchST_Put(c_BinarySearchST_t* st, const void* key, const void* val); +void* c_BinarySearchST_Get(const c_BinarySearchST_t* st, const void* key); +c_err_t c_BinarySearchST_Delete(c_BinarySearchST_t* st, const void* key); + +#endif /*INCLUDED_C_BINARYSEARCHST_H*/ diff --git a/cKit/Search/c_BinarySearchST.t.c b/cKit/Search/c_BinarySearchST.t.c new file mode 100644 index 0000000..6130a61 --- /dev/null +++ b/cKit/Search/c_BinarySearchST.t.c @@ -0,0 +1,72 @@ +#include "c_BinarySearchST.h" +#include +#include + +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d)\n", msg, (int)(expected), (int)(actual)); \ + return C_FALSE; \ + } \ + } while(0) + +// Bulk structural values block mapped to compact numeric lookup keys +typedef struct { + char cluster_name[32]; + double operational_load; + int backup_port; +} DNSProfile; + +int compareIntKeys(const void* a, const void* b) { + return (*(int*)a - *(int*)b); +} + +c_bool_t test_binary_search_st(void) { + c_BinarySearchST_t st; + + // Initialize with a capacity of 2 to verify dynamic parallel allocation resize cycles + EXPECT_EQ(c_BinarySearchST_Init(&st, 2, sizeof(int), sizeof(DNSProfile), compareIntKeys), C_ERR_OK, "Init failed"); + + int k1 = 8080; DNSProfile v1 = { "Primary Cluster", 0.45, 9001 }; + int k2 = 4433; DNSProfile v2 = { "Secure Edge Node", 0.12, 9002 }; + int k3 = 7021; DNSProfile v3 = { "Fallback Stack", 0.89, 9003 }; + + // 1. Data Entry Flow + EXPECT_EQ(c_BinarySearchST_Put(&st, &k1, &v1), C_ERR_OK, "Put k1 failed"); + EXPECT_EQ(c_BinarySearchST_Put(&st, &k2, &v2), C_ERR_OK, "Put k2 failed"); + EXPECT_EQ(c_BinarySearchST_Put(&st, &k3, &v3), C_ERR_OK, "Put k3 failed (Resize step validation)"); + EXPECT_EQ(st.size, 3, "Symbol Table size tracker count incorrect"); + + // 2. Overwrite Verification + DNSProfile v1_updated = { "Primary Cluster v2", 0.52, 9001 }; + EXPECT_EQ(c_BinarySearchST_Put(&st, &k1, &v1_updated), C_ERR_OK, "Overwriting entry failed"); + EXPECT_EQ(st.size, 3, "Size increased incorrectly during an overwrite operation"); + + // 3. Data Retrieval lookups + int look_key = 8080; + DNSProfile* fetched = (DNSProfile*)c_BinarySearchST_Get(&st, &look_key); + EXPECT_EQ(fetched != NULL && strcmp(fetched->cluster_name, "Primary Cluster v2") == 0, C_TRUE, "Lookup retrieved incorrect mapping segment"); + + int miss_key = 9999; + EXPECT_EQ(c_BinarySearchST_Get(&st, &miss_key) == NULL, C_TRUE, "Key miss lookup did not return NULL"); + + // 4. Deletion Cycles + int delete_key = 4433; + EXPECT_EQ(c_BinarySearchST_Delete(&st, &delete_key), C_ERR_OK, "Deletion operation crashed"); + EXPECT_EQ(c_BinarySearchST_Contains(&st, &delete_key), C_FALSE, "Deleted element still reported within lookups"); + EXPECT_EQ(st.size, 2, "Size tracker did not reduce correctly after deletion"); + + c_BinarySearchST_Destroy(&st); + return C_TRUE; +} + +int main(void) { + printf("=== Starting Framework Unit Testing: c_BinarySearchST ===\n"); + + if (test_binary_search_st()) { + printf(" [PASS] Symbol Table Parallel Array Processing and Retrieval Verified Successfully.\n"); + } else { + printf(" [FAIL] Symbol Table Component Encountered Evaluation Errors.\n"); + } + return 0; +} diff --git a/cKit/Search/c_HashMap.c b/cKit/Search/c_HashMap.c new file mode 100644 index 0000000..08da8eb --- /dev/null +++ b/cKit/Search/c_HashMap.c @@ -0,0 +1,267 @@ +#include +#include + +#define C_HASHMAP_LOAD_FACTOR_THRESHOLD 0.75f +#define DEFAULT_CAPACITY 16 + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// Internal Helper: Doubles bucket allocations and rehashes entries +static c_err_t hashmap_resize(c_HashMap_t* self) { + c_size_t new_capacity = self->capacity * 2; + c_HashMapEntry_t** new_buckets = (c_HashMapEntry_t**)C_CALLOC(new_capacity, sizeof(c_HashMapEntry_t*)); + if (!new_buckets) return C_ERR_NOMEM; + + // Migrate entries over from old buckets array + for (c_size_t i = 0; i < self->capacity; i++) { + c_HashMapEntry_t* entry = self->buckets[i]; + while (entry != NULL) { + c_HashMapEntry_t* next = entry->next; + + // Recompute new bucket index mapping constraints + uint32_t raw_hash = self->hash(entry->key, self->key_size); + c_size_t new_index = raw_hash % new_capacity; + + // Link into the new bucket array chain head + entry->next = new_buckets[new_index]; + new_buckets[new_index] = entry; + + entry = next; + } + } + + C_FREE(self->buckets); + self->buckets = new_buckets; + self->capacity = new_capacity; + return C_ERR_SUCCESS; +} + +C_STATIC_FORCE_INLINE +void hashmap_iter_advance_to_valid(c_HashMapKeyIter_t* self) { + while (self->bucket_index < self->map->capacity) { + // 如果當前桶子有鏈結節點,繫結其指標的指標 + if (self->map->buckets[self->bucket_index] != NULL) { + self->entry = &self->map->buckets[self->bucket_index]; + return; + } + self->bucket_index++; + } + // 若找不到任何有效節點,重置為 NULL 象徵迭代結束 + self->entry = NULL; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_HashMap_Init(c_HashMap_t* self, int key_size, int value_size, c_size_t initial_capacity, + c_HashMap_Hash_f hash, c_HashMap_Compare_f compare) { + if (!self || key_size <= 0 || value_size <= 0 || !hash || !compare) return C_ERR_PARAM; + + self->capacity = (initial_capacity > 0) ? initial_capacity : DEFAULT_CAPACITY; + self->size = 0; + self->key_size = key_size; + self->value_size = value_size; + self->hash = hash; + self->compare = compare; + + self->buckets = (c_HashMapEntry_t**)C_CALLOC(self->capacity, sizeof(c_HashMapEntry_t*)); + if (!self->buckets) { + self->capacity = 0; + return C_ERR_NOMEM; + } + + return C_ERR_SUCCESS; +} + +void c_HashMap_Destroy(c_HashMap_t* self) { + if (!self) return; + + for (c_size_t i = 0; i < self->capacity; i++) { + c_HashMapEntry_t* entry = self->buckets[i]; + while (entry != NULL) { + c_HashMapEntry_t* next = entry->next; + // C_FREE(entry->key); + // C_FREE(entry->value); + C_FREE(entry); + entry = next; + } + } + C_FREE(self->buckets); + self->buckets = NULL; + self->capacity = 0; + self->size = 0; +} + + +// Maps/Overwrites keys to value entities in O(1) average time complexity +c_err_t c_HashMap_Put(c_HashMap_t* self, const void* key, const void* value) { + if (!self || !self->buckets || !key || !value) return C_ERR_PARAM; + + // Trigger dynamic scale-out adjustments if load boundaries criteria are exceeded + if ((float)(self->size + 1) / self->capacity >= C_HASHMAP_LOAD_FACTOR_THRESHOLD) { + if (hashmap_resize(self) != C_ERR_SUCCESS) return C_ERR_NOMEM; + } + + uint32_t raw_hash = self->hash(key, self->key_size); + c_size_t index = raw_hash % self->capacity; + + // Scan the collision chain to check if the key already exists + c_HashMapEntry_t* entry = self->buckets[index]; + while (entry != NULL) { + if (self->compare(entry->key, key, self->key_size) == 0) { + // Overwrite existing value mapping using deep copy semantics + memcpy(entry->value, value, self->value_size); + return C_ERR_SUCCESS; + } + entry = entry->next; + } + + // Allocate a new node entry if the key does not exist + int size = (int)sizeof(c_HashMapEntry_t) + self->key_size + self->value_size; + size = C_ALIGN_UPB(size, C_ALIGN_SIZE); + c_HashMapEntry_t* new_entry = (c_HashMapEntry_t*)C_ALLOC(size); + if (!new_entry) return C_ERR_NOMEM; + + new_entry->key = new_entry+1; + new_entry->value = new_entry->key + self->key_size; + + // Deep copy payload bounds properties + memcpy(new_entry->key, key, self->key_size); + memcpy(new_entry->value, value, self->value_size); + + // Single-chain head link injection (O(1)) + new_entry->next = self->buckets[index]; + self->buckets[index] = new_entry; + self->size++; + + return C_ERR_SUCCESS; +} + +// Fetches value references safely into user-allocated destination spaces +c_err_t c_HashMap_Get(c_HashMap_t* self, const void* key, void* out_value) { + if (!self || !self->buckets || !key || !out_value) return C_ERR_PARAM; + + uint32_t raw_hash = self->hash(key, self->key_size); + c_size_t index = raw_hash % self->capacity; + + c_HashMapEntry_t* entry = self->buckets[index]; + while (entry != NULL) { + if (self->compare(entry->key, key, self->key_size) == 0) { + memcpy(out_value, entry->value, self->value_size); + return C_ERR_SUCCESS; + } + entry = entry->next; + } + + return C_ERR_NOT_FOUND; +} + +// Unlinks map items matching key contexts safely (O(1) average time complexity) +c_err_t c_HashMap_Remove(c_HashMap_t* self, const void* key) { + if (!self || !self->buckets || !key) return C_ERR_PARAM; + + uint32_t raw_hash = self->hash(key, self->key_size); + c_size_t index = raw_hash % self->capacity; + + c_HashMapEntry_t** curr = &self->buckets[index]; + while (*curr != NULL) { + if (self->compare((*curr)->key, key, self->key_size) == 0) { + c_HashMapEntry_t* to_delete = *curr; + *curr = to_delete->next; // Unlink node entry frame properties + + // free(to_delete->key); + // free(to_delete->value); + C_FREE(to_delete); + self->size--; + return C_ERR_SUCCESS; + } + curr = &(*curr)->next; + } + + return C_ERR_NOT_FOUND; +} + +c_bool_t c_HashMap_Contains(c_HashMap_t* self, const void* key) { + if (!self || !self->buckets || !key) return C_FALSE; + + uint32_t raw_hash = self->hash(key, self->key_size); + c_size_t index = raw_hash % self->capacity; + + c_HashMapEntry_t* entry = self->buckets[index]; + while (entry != NULL) { + if (self->compare(entry->key, key, self->key_size) == 0) return C_TRUE; + entry = entry->next; + } + return C_FALSE; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +void c_HashMapKeyIter_Init(c_HashMapKeyIter_t* self, c_HashMap_t* map) { + if (!self || !map) return; + self->map = map; + self->bucket_index = 0; + self->entry = NULL; + + // 初始化時先定位到第一個有效節點 + hashmap_iter_advance_to_valid(self); +} + +// 檢查是否還有下一個元素 +c_bool_t c_HashMapKeyIter_HasNext(c_HashMapKeyIter_t* self) { + if (!self || !self->entry || !*(self->entry)) return C_FALSE; + return C_TRUE; +} + +// 查看目前指向的鍵(Key)指標 (不前進) +void* c_HashMapKeyIter_Get(c_HashMapKeyIter_t* self) { + if (!c_HashMapKeyIter_HasNext(self)) return NULL; + return (*(self->entry))->key; +} + +// 獲取目前指向的鍵(Key)指標,並將迭代器前進到下一個有效節點 +void* c_HashMapKeyIter_Next(c_HashMapKeyIter_t* self) { + if (!c_HashMapKeyIter_HasNext(self)) return NULL; + + c_HashMapEntry_t* curr = *(self->entry); + void* key_ptr = curr->key; + + // 如果當前衝突鏈結中還有下一個節點,直接移向 next + if (curr->next != NULL) { + self->entry = &(curr->next); + } else { + // 如果當前衝突鏈結已到底,前進到下一個桶子並搜尋有效節點 + self->bucket_index++; + hashmap_iter_advance_to_valid(self); + } + + return key_ptr; +} + +// 迭代器安全刪除:在走訪期間以 O(1) 的平均複雜度斷開鏈結並釋放記憶體 +void c_HashMapKeyIter_Remove(c_HashMapKeyIter_t* self) { + if (!c_HashMapKeyIter_HasNext(self)) return; + + c_HashMapEntry_t* to_delete = *(self->entry); + + // 關鍵指標斷開:讓前一個節點的 next(或是桶子的首節點指標)直接指向下一個節點 + *(self->entry) = to_delete->next; + + // 釋放該 Entry 的深複製記憶體 + // free(to_delete->key); + // free(to_delete->value); + C_FREE(to_delete); + + self->map->size--; + + // 檢查斷開後當前位置是否為空(代表原本該桶子的衝突鏈結已走訪完畢) + if (*(self->entry) == NULL) { + // 前進到下一個桶子搜尋下一個有效節點 + self->bucket_index++; + hashmap_iter_advance_to_valid(self); + } + // 備註:若 *(self->entry) != NULL,則 self->entry 自動留在了下一個節點上,不需額外處理 +} + diff --git a/cKit/Search/c_HashMap.h b/cKit/Search/c_HashMap.h new file mode 100644 index 0000000..1191763 --- /dev/null +++ b/cKit/Search/c_HashMap.h @@ -0,0 +1,59 @@ +#ifndef INCLUDED_C_HASHMAP_H +#define INCLUDED_C_HASHMAP_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct c_HashMapEntry_t { + void* key; + void* value; + struct c_HashMapEntry_t* next; +} c_HashMapEntry_t; + +typedef uint32_t (*c_HashMap_Hash_f)(const void* key, int key_size); +typedef int (*c_HashMap_Compare_f)(const void* key1, const void* key2, int key_size); + +typedef struct { + c_HashMapEntry_t** buckets; // Array of entry linked list head pointers + c_size_t capacity; // Number of buckets allocated + c_size_t size; // Number of active key-value pairs stored + int key_size; // Byte footprint of the key type + int value_size; // Byte footprint of the value type + c_HashMap_Hash_f hash; // User hash calculation function + c_HashMap_Compare_f compare;// User key comparison function +} c_HashMap_t; + +typedef struct { + c_HashMap_t* map; // 繫結的雜湊表 + c_size_t bucket_index; // 當前走訪的桶子索引 (Bucket Index) + c_HashMapEntry_t** entry; // 指向當前節點指標的指標,用於 O(1) 安全刪除 +} c_HashMapKeyIter_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +c_err_t c_HashMap_Init(c_HashMap_t* self, int key_size, int value_size, c_size_t initial_capacity, + c_HashMap_Hash_f hash, c_HashMap_Compare_f compare); +void c_HashMap_Destroy(c_HashMap_t* self); + +c_err_t c_HashMap_Put(c_HashMap_t* self, const void* key, const void* value); +c_err_t c_HashMap_Get(c_HashMap_t* self, const void* key, void* out_value); +c_err_t c_HashMap_Remove(c_HashMap_t* self, const void* key); +c_bool_t c_HashMap_Contains(c_HashMap_t* self, const void* key); + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +void c_HashMapKeyIter_Init(c_HashMapKeyIter_t* self, c_HashMap_t* map); +c_bool_t c_HashMapKeyIter_HasNext(c_HashMapKeyIter_t* self); +void* c_HashMapKeyIter_Next(c_HashMapKeyIter_t* self); +void* c_HashMapKeyIter_Get(c_HashMapKeyIter_t* self); +void c_HashMapKeyIter_Remove(c_HashMapKeyIter_t* self); + +#endif /*INCLUDED_C_HASHMAP_H*/ diff --git a/cKit/Search/c_HashMap.t.c b/cKit/Search/c_HashMap.t.c new file mode 100644 index 0000000..a56b105 --- /dev/null +++ b/cKit/Search/c_HashMap.t.c @@ -0,0 +1,99 @@ +#include "c_HashMap.h" +#include +#include + +typedef struct { + char username[32]; +} UserKey_t; + +typedef struct { + int user_uid; + int permissions_mask; +} ProfileValue_t; + +// Standard high-distribution DJB2 Hash function implementation +uint32_t hash_djb2(const void* key, int key_size) { + const char* str = ((UserKey_t*)key)->username; + uint32_t hash = 5381; + int c; + while ((c = (unsigned char)*str++)) { + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + } + return hash; +} + +int compare_user_keys(const void* a, const void* b, int key_size) { + return strcmp(((UserKey_t*)a)->username, ((UserKey_t*)b)->username); +} + +void test_log(const char* name) { + printf("[PASS] %s\n", name); +} + + +int main() { + printf("==================================================\n"); + printf(" Starting Generic Hash Map Unit Testing Suite\n"); + printf("==================================================\n\n"); + + c_HashMap_t map; + // Set a small capacity of 4 to test scaling re-hashing logic mid-flight + c_err_t err = c_HashMap_Init(&map, sizeof(UserKey_t), sizeof(ProfileValue_t), 4, hash_djb2, compare_user_keys); + assert(err == C_ERR_SUCCESS); + assert(map.size == 0); + test_log("1. Hash Map allocation and callback structures ready"); + + UserKey_t k1 = {"admin"}; ProfileValue_t v1 = {9001, 0xFF}; + UserKey_t k2 = {"developer"}; ProfileValue_t v2 = {9002, 0x0F}; + UserKey_t k3 = {"guest"}; ProfileValue_t v3 = {9003, 0x01}; + + // ========================================== + // 2. Testing Insertions & Resizing + // ========================================== + c_HashMap_Put(&map, &k1, &v1); + c_HashMap_Put(&map, &k2, &v2); + + // Inserting 3rd item triggers 3/4 = 0.75 load factor threshold, forcing scale-out to capacity 8 + err = c_HashMap_Put(&map, &k3, &v3); + assert(err == C_ERR_SUCCESS); + assert(map.size == 3); + assert(map.capacity == 8); // Capacity successfully doubled automatically + test_log("2. Mapping assertions and dynamic scale-out rehashing verified"); + + // ========================================== + // 3. Testing Lookups (Get) + // ========================================== + ProfileValue_t out_buf; + err = c_HashMap_Get(&map, &k2, &out_buf); + assert(err == C_ERR_SUCCESS); + assert(out_buf.user_uid == 9002); + assert(out_buf.permissions_mask == 0x0F); + + // Overwrite test validation + ProfileValue_t v1_updated = {9001, 0xAA}; + c_HashMap_Put(&map, &k1, &v1_updated); + c_HashMap_Get(&map, &k1, &out_buf); + assert(out_buf.permissions_mask == 0xAA); + test_log("3. Mapping value extractions and key overrides verified"); + + // ========================================== + // 4. Testing Deletions (Remove) + // ========================================== + assert(c_HashMap_Contains(&map, &k3) == C_TRUE); + err = c_HashMap_Remove(&map, &k3); + assert(err == C_ERR_SUCCESS); + assert(map.size == 2); + assert(c_HashMap_Contains(&map, &k3) == C_FALSE); + + // Not found tracking assertions checks + assert(c_HashMap_Get(&map, &k3, &out_buf) == C_ERR_NOT_FOUND); + test_log("4. Chain unlinking and item deletions verified"); + + c_HashMap_Destroy(&map); + test_log("5. Resource pool cleanup teardown success"); + + printf("\n==================================================\n"); + printf(" Success! Universal defensive Hash Map behaves correctly!\n"); + printf("==================================================\n"); + return 0; +} \ No newline at end of file diff --git a/cKit/Search/c_HashMapIter.t.c b/cKit/Search/c_HashMapIter.t.c new file mode 100644 index 0000000..0cc8d7b --- /dev/null +++ b/cKit/Search/c_HashMapIter.t.c @@ -0,0 +1,92 @@ +#include "c_HashMap.h" +#include +#include + + +typedef struct { + char name[32]; +} StringKey_t; + +typedef struct { + int price; +} IntValue_t; + +// 簡單字串雜湊回呼 (DJB2) +uint32_t test_hash(const void* key, int key_size) { + const char* str = ((StringKey_t*)key)->name; + uint32_t hash = 5381; + int c; + while ((c = (unsigned char)*str++)) hash = ((hash << 5) + hash) + c; + return hash; +} + +int test_compare(const void* a, const void* b, int key_size) { + return strcmp(((StringKey_t*)a)->name, ((StringKey_t*)b)->name); +} + +void test_log(const char* name) { + printf("[PASS] %s\n", name); +} + +int main() { + printf("==================================================\n"); + printf(" 開始執行 c_HashMapKeyIter_t 鍵值迭代器單元測試\n"); + printf("==================================================\n\n"); + + c_HashMap_t map; + c_HashMap_Init(&map, sizeof(StringKey_t), sizeof(IntValue_t), 4, test_hash, test_compare); + + StringKey_t k1 = {"Apple"}; IntValue_t v1 = {100}; + StringKey_t k2 = {"Banana"}; IntValue_t v2 = {200}; // 預計在走訪時刪除此項 + StringKey_t k3 = {"Cherry"}; IntValue_t v3 = {300}; + + c_HashMap_Put(&map, &k1, &v1); + c_HashMap_Put(&map, &k2, &v2); + c_HashMap_Put(&map, &k3, &v3); + assert(map.size == 3); + + // ========================================== + // 1. 測試迭代器基本走訪 + // ========================================== + c_HashMapKeyIter_t iter; + c_HashMapKeyIter_Init(&iter, &map); + + printf("當前雜湊表包含的鍵值:\n"); + int visit_count = 0; + while (c_HashMapKeyIter_HasNext(&iter)) { + StringKey_t* key = (StringKey_t*)c_HashMapKeyIter_Next(&iter); + printf(" - 鍵名稱: %s\n", key->name); + visit_count++; + } + assert(visit_count == 3); + test_log("1. 迭代器成功走訪所有雜湊桶中的元素"); + + // ========================================== + // 2. 測試走訪中安全刪除 (Remove "Banana") + // ========================================== + c_HashMapKeyIter_Init(&iter, &map); + while (c_HashMapKeyIter_HasNext(&iter)) { + StringKey_t* key = (StringKey_t*)c_HashMapKeyIter_Get(&iter); + + if (strcmp(key->name, "Banana") == 0) { + printf(" [Log] 找到目標 '%s',執行迭代器刪除...\n", key->name); + c_HashMapKeyIter_Remove(&iter); + // 呼叫 Remove 後,迭代器已自動處理好指標轉移,此處不可呼叫 Next() + } else { + c_HashMapKeyIter_Next(&iter); // 沒刪除時才手動前進 + } + } + assert(map.size == 2); + test_log("2. 迭代器中序安全刪除指定元素成功"); + + // ========================================== + // 3. 驗證刪除後的雜湊表狀態 + // ========================================== + assert(c_HashMap_Contains(&map, &k1) == C_TRUE); + assert(c_HashMap_Contains(&map, &k2) == C_FALSE); // Banana 應不見 + assert(c_HashMap_Contains(&map, &k3) == C_TRUE); + test_log("3. 最終雜湊表結構完好度驗證成功"); + + c_HashMap_Destroy(&map); + return 0; +} \ No newline at end of file diff --git a/cKit/Search/c_HashSet.c b/cKit/Search/c_HashSet.c new file mode 100644 index 0000000..5ad349e --- /dev/null +++ b/cKit/Search/c_HashSet.c @@ -0,0 +1,45 @@ +#include + +// 虛擬佔位常數,所有集合元素在底層對應同一個 Dummy 值的地址 +static const int dummy_value = 1; + +c_err_t c_HashSet_Init(c_HashSet_t* self, int obj_size, c_size_t initial_capacity, + c_HashMap_Hash_f hash, c_HashMap_Compare_f compare) { + if (!self) return C_ERR_PARAM; + + // 初始化底層對映的雜湊表,value_size 固定設為常數大小 + return c_HashMap_Init(&self->map, obj_size, sizeof(int), initial_capacity, hash, compare); +} + +void c_HashSet_Destroy(c_HashSet_t* self) { + if (!self) return; + c_HashMap_Destroy(&self->map); +} + +// 推入元素:若元素已存在則攔截並報錯,確保唯一性 +c_err_t c_HashSet_Add(c_HashSet_t* self, const void* obj) { + if (!self || !obj) return C_ERR_PARAM; + + // 先檢查是否已經存在此元素 + if (c_HashMap_Contains(&self->map, obj)) { + return C_ERR_ALREADY_EXISTS; + } + + // 將物件當作 Key 寫入,Value 塞入 Dummy 常數 + return c_HashMap_Put(&self->map, obj, &dummy_value); +} + +c_err_t c_HashSet_Remove(c_HashSet_t* self, const void* obj) { + if (!self || !obj) return C_ERR_PARAM; + return c_HashMap_Remove(&self->map, obj); +} + +c_bool_t c_HashSet_Contains(c_HashSet_t* self, const void* obj) { + if (!self || !obj) return C_FALSE; + return c_HashMap_Contains(&self->map, obj); +} + +c_size_t c_HashSet_GetSize(const c_HashSet_t* self) { + if (!self) return 0; + return self->map.size; +} \ No newline at end of file diff --git a/cKit/Search/c_HashSet.h b/cKit/Search/c_HashSet.h new file mode 100644 index 0000000..e83d57e --- /dev/null +++ b/cKit/Search/c_HashSet.h @@ -0,0 +1,36 @@ +#ifndef INCLUDED_C_HASHSET_H +#define INCLUDED_C_HASHSET_H + +#ifndef INCLUDED_C_HASHMAP_H +#include +#endif /*INCLUDED_C_HASHMAP_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + c_HashMap_t map; // 底層由 HashMap 驅動 +} c_HashSet_t; + +// 集合迭代器(直接重定向至您的 HashMapKeyIter) +typedef c_HashMapKeyIter_t c_HashSetIter_t; + +// 核心函數宣告 +c_err_t c_HashSet_Init(c_HashSet_t* self, int obj_size, c_size_t initial_capacity, + c_HashMap_Hash_f hash, c_HashMap_Compare_f compare); +void c_HashSet_Destroy(c_HashSet_t* self); + +c_err_t c_HashSet_Add(c_HashSet_t* self, const void* obj); +c_err_t c_HashSet_Remove(c_HashSet_t* self, const void* obj); +c_bool_t c_HashSet_Contains(c_HashSet_t* self, const void* obj); +c_size_t c_HashSet_GetSize(const c_HashSet_t* self); + +// 集合迭代器巨集/函數重定向(完美保持一致性) +#define c_HashSetIter_Init(self, set) c_HashMapKeyIter_Init(self, &(set)->map) +#define c_HashSetIter_HasNext(self) c_HashMapKeyIter_HasNext(self) +#define c_HashSetIter_Get(self) c_HashMapKeyIter_Get(self) +#define c_HashSetIter_Next(self) c_HashMapKeyIter_Next(self) +#define c_HashSetIter_Remove(self) c_HashMapKeyIter_Remove(self) + +#endif /*INCLUDED_C_HASHSET_H*/ diff --git a/cKit/Search/c_HashSet.t.c b/cKit/Search/c_HashSet.t.c new file mode 100644 index 0000000..1535851 --- /dev/null +++ b/cKit/Search/c_HashSet.t.c @@ -0,0 +1,95 @@ +#include "c_HashSet.h" +#include +#include + + +typedef struct { + char ip_address[17]; +} IpAddress_t; + +// 簡單字串雜湊回呼 (DJB2) +uint32_t hash_ip(const void* key, int key_size) { + const char* str = ((IpAddress_t*)key)->ip_address; + uint32_t hash = 5381; + int c; + while ((c = (unsigned char)*str++)) hash = ((hash << 5) + hash) + c; + return hash; +} + +int compare_ip(const void* a, const void* b, int key_size) { + return strcmp(((IpAddress_t*)a)->ip_address, ((IpAddress_t*)b)->ip_address); +} + +void test_log(const char* name) { + printf("[PASS] %s\n", name); +} + +int main() { + printf("==================================================\n"); + printf(" 開始執行 c_HashSet_t 唯一性集合組件單元測試\n"); + printf("==================================================\n\n"); + + c_HashSet_t ip_blacklist; + c_err_t err = c_HashSet_Init(&ip_blacklist, sizeof(IpAddress_t), 4, hash_ip, compare_ip); + assert(err == C_ERR_SUCCESS); + assert(c_HashSet_GetSize(&ip_blacklist) == 0); + test_log("1. 集合容器架構初始化成功"); + + IpAddress_t ip1 = {"192.168.1.1"}; + IpAddress_t ip2 = {"10.0.0.1"}; + IpAddress_t ip3 = {"172.16.0.1"}; + + // ========================================== + // 2. 測試元素新增與唯一性驗證 (Add) + // ========================================== + c_HashSet_Add(&ip_blacklist, &ip1); + c_HashSet_Add(&ip_blacklist, &ip2); + err = c_HashSet_Add(&ip_blacklist, &ip3); + assert(err == C_ERR_SUCCESS); + assert(c_HashSet_GetSize(&ip_blacklist) == 3); + + // 核心斷言:重複新增相同的 IP 必須被攔截並回傳 C_ERR_ALREADY_EXISTS + assert(c_HashSet_Add(&ip_blacklist, &ip1) == C_ERR_ALREADY_EXISTS); + assert(c_HashSet_GetSize(&ip_blacklist) == 3); // 大小依然要是 3 + test_log("2. 元素值複製推入與重複唯一性攔截驗證成功"); + + // ========================================== + // 3. 測試成員包含判斷 (Contains) + // ========================================== + assert(c_HashSet_Contains(&ip_blacklist, &ip1) == C_TRUE); + + IpAddress_t safe_ip = {"8.8.8.8"}; + assert(c_HashSet_Contains(&ip_blacklist, &safe_ip) == C_FALSE); + test_log("3. O(1) 集合成員包含度 (Contains) 檢索成功"); + + // ========================================== + // 4. 測試集合迭代器走訪與安全刪除 (Remove "10.0.0.1") + // ========================================== + c_HashSetIter_t iter; + c_HashSetIter_Init(&iter, &ip_blacklist); + + printf("當前黑名單集合包含:\n"); + while (c_HashSetIter_HasNext(&iter)) { + IpAddress_t* current_ip = (IpAddress_t*)c_HashSetIter_Get(&iter); + printf(" - IP: %s\n", current_ip->ip_address); + + if (strcmp(current_ip->ip_address, "10.0.0.1") == 0) { + c_HashSetIter_Remove(&iter); // 在走訪期間從集合中安全剔除 + printf(" [Log] 已透過迭代器將 10.0.0.1 從集合中移除\n"); + } else { + c_HashSetIter_Next(&iter); + } + } + + assert(c_HashSet_GetSize(&ip_blacklist) == 2); + assert(c_HashSet_Contains(&ip_blacklist, &ip2) == C_FALSE); // 10.0.0.1 應確實消失 + test_log("4. 集合重定向迭代器走訪與 O(1) 安全剔除成功"); + + c_HashSet_Destroy(&ip_blacklist); + test_log("5. 集合控制單元資源釋放成功"); + + printf("\n==================================================\n"); + printf(" 恭喜!基於組合模式複用的 c_HashSet_t 測試全數完美通過!\n"); + printf("==================================================\n"); + return 0; +} \ No newline at end of file diff --git a/cKit/Search/c_LinearProbingHashST.c b/cKit/Search/c_LinearProbingHashST.c new file mode 100644 index 0000000..370b1dc --- /dev/null +++ b/cKit/Search/c_LinearProbingHashST.c @@ -0,0 +1,196 @@ +#include +#include + +/** + * FNV-1a baseline string/scalar data hash scrambling algorithm. + */ +C_STATIC_FORCE_INLINE +uint32_t c_LPHash_DefaultHash(const void* key, c_size_t key_size) { + const uint8_t* data = (const uint8_t*)key; + uint32_t hash = 0x811C9DC5; + for (c_size_t i = 0; i < key_size; i++) { + hash ^= data[i]; + hash *= 0x01000193; + } + return hash; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_LinearProbingHashST_Init(c_LinearProbingHashST_t* st, c_size_t initial_capacity, + c_size_t key_size, c_size_t val_size, + uint32_t (*hash_fn)(const void*, c_size_t), + int (*key_compar)(const void*, const void*)) { + if (st == NULL || initial_capacity == 0 || key_size == 0 || val_size == 0 || key_compar == NULL) { + return C_ERR_PARAM; + } + + st->M = initial_capacity; + st->N = 0; + st->key_size = key_size; + st->val_size = val_size; + st->hash_fn = (hash_fn != NULL) ? hash_fn : c_LPHash_DefaultHash; + st->key_compar = key_compar; + + st->keys = C_ALLOC(st->M * key_size); + st->vals = C_ALLOC(st->M * val_size); + st->occupied = (c_bool_t*)C_ALLOC(st->M * sizeof(c_bool_t)); + + if (st->keys == NULL || st->vals == NULL || st->occupied == NULL) { + C_FREE(st->keys); C_FREE(st->vals); C_FREE(st->occupied); + st->keys = NULL; st->vals = NULL; st->occupied = NULL; + return C_ERR_NOMEM; + } + + memset(st->occupied, C_FALSE, st->M * sizeof(c_bool_t)); + return C_ERR_OK; +} + +void c_LinearProbingHashST_Destroy(c_LinearProbingHashST_t* st) { + if (st) { + C_FREE(st->keys); st->keys = NULL; + C_FREE(st->vals); st->vals = NULL; + C_FREE(st->occupied); st->occupied = NULL; + st->M = 0; + st->N = 0; + } +} + +/** + * Explicit internal resizing routing handler. + * Essential for keeping the Load Factor (alpha) under 0.5 to prevent clustering. + */ +static c_err_t c_LinearProbingHashST_Resize(c_LinearProbingHashST_t* st, c_size_t capacity) { + c_LinearProbingHashST_t temp_st; + c_err_t err = c_LinearProbingHashST_Init(&temp_st, capacity, st->key_size, st->val_size, st->hash_fn, st->key_compar); + if (err != C_ERR_OK) return err; + + char* keys_base = (char*)st->keys; + char* vals_base = (char*)st->vals; + c_size_t ks = st->key_size; + c_size_t vs = st->val_size; + + // Rehash and insert all existing active items into the new, expanded table footprint + for (c_size_t i = 0; i < st->M; i++) { + if (st->occupied[i]) { + extern c_err_t c_LinearProbingHashST_Put(c_LinearProbingHashST_t*, const void*, const void*); + err = c_LinearProbingHashST_Put(&temp_st, keys_base + (i * ks), vals_base + (i * vs)); + if (err != C_ERR_OK) { + c_LinearProbingHashST_Destroy(&temp_st); + return err; + } + } + } + + // Swap parameters to apply the newly rehashed table context + C_FREE(st->keys); C_FREE(st->vals); C_FREE(st->occupied); + st->keys = temp_st.keys; + st->vals = temp_st.vals; + st->occupied = temp_st.occupied; + st->M = temp_st.M; + return C_ERR_OK; +} + +c_err_t c_LinearProbingHashST_Put(c_LinearProbingHashST_t* st, const void* key, const void* val) { + if (st == NULL || key == NULL || val == NULL) return C_ERR_PARAM; + + // Enforce an upper bound load factor limit of 50% to mitigate clustering degradation + if (st->N >= st->M / 2) { + c_err_t err = c_LinearProbingHashST_Resize(st, st->M * 2); + if (err != C_ERR_OK) return err; + } + + char* keys_base = (char*)st->keys; + char* vals_base = (char*)st->vals; + c_size_t ks = st->key_size; + c_size_t vs = st->val_size; + + c_size_t i; + for (i = st->hash_fn(key, ks) % st->M; st->occupied[i]; i = (i + 1) % st->M) { + if (st->key_compar(keys_base + (i * ks), key) == 0) { + // Found existing key match: update values block payload in place + memcpy(vals_base + (i * vs), val, vs); + return C_ERR_OK; + } + } + + // Insert new item into the available open slot found by probing + memcpy(keys_base + (i * ks), key, ks); + memcpy(vals_base + (i * vs), val, vs); + st->occupied[i] = C_TRUE; + st->N++; + + return C_ERR_OK; +} + +void* c_LinearProbingHashST_Get(const c_LinearProbingHashST_t* st, const void* key) { + if (st == NULL || st->keys == NULL || key == NULL) return NULL; + + char* keys_base = (char*)st->keys; + c_size_t ks = st->key_size; + + for (c_size_t i = st->hash_fn(key, ks) % st->M; st->occupied[i]; i = (i + 1) % st->M) { + if (st->key_compar(keys_base + (i * ks), key) == 0) { + return (char*)st->vals + (i * st->val_size); + } + } + return NULL; +} + +c_bool_t c_LinearProbingHashST_Contains(const c_LinearProbingHashST_t* st, const void* key) { + return c_LinearProbingHashST_Get(st, key) != NULL; +} + +c_err_t c_LinearProbingHashST_Delete(c_LinearProbingHashST_t* st, const void* key) { + if (st == NULL || key == NULL) return C_ERR_PARAM; + + char* keys_base = (char*)st->keys; + c_size_t ks = st->key_size; + c_size_t vs = st->val_size; + + c_size_t i = st->hash_fn(key, ks) % st->M; + while (st->occupied[i]) { + if (st->key_compar(keys_base + (i * ks), key) == 0) { + break; + } + i = (i + 1) % st->M; + } + + // Key to delete was not found in the hash table + if (!st->occupied[i]) return C_ERR_NOT_FOUND; + + // Hard delete: Free the targeted slot index flag + st->occupied[i] = C_FALSE; + st->N--; + + // CRITICAL REQUIREMENT: Rehash all subsequent cluster elements + // to bridge the open slot gap caused by deletion, preventing future search short-circuits. + i = (i + 1) % st->M; + while (st->occupied[i]) { + // Capture old keys/values payload allocations locally + void* key_to_rehash = C_ALLOC(ks); + void* val_to_rehash = C_ALLOC(vs); + memcpy(key_to_rehash, keys_base + (i * ks), ks); + memcpy(val_to_rehash, (char*)st->vals + (i * vs), vs); + + // Explicitly clear the current cluster entry tracking variables + st->occupied[i] = C_FALSE; + st->N--; + + // Re-insert the captured element into the table using standard routing rules + c_LinearProbingHashST_Put(st, key_to_rehash, val_to_rehash); + + C_FREE(key_to_rehash); + C_FREE(val_to_rehash); + + i = (i + 1) % st->M; + } + + // Shrink the table capacity automatically if utilization drops below 12.5% + if (st->N > 0 && st->N <= st->M / 8) { + c_LinearProbingHashST_Resize(st, st->M / 2); + } + + return C_ERR_OK; +} diff --git a/cKit/Search/c_LinearProbingHashST.h b/cKit/Search/c_LinearProbingHashST.h new file mode 100644 index 0000000..bf6ebd7 --- /dev/null +++ b/cKit/Search/c_LinearProbingHashST.h @@ -0,0 +1,40 @@ +#ifndef INCLUDED_C_LINEARPROBINGHASHST_H +#define INCLUDED_C_LINEARPROBINGHASHST_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// Linear Probing Hash Symbol Table Instance Layout +typedef struct { + void* keys; // Flat parallel array block storing keys + void* vals; // Flat parallel array block storing values + c_bool_t* occupied; // Flag array tracking whether a specific slot is filled + + c_size_t M; // Linear probing table capacity (array size) + c_size_t N; // Current active element count + c_size_t key_size; // Size of each key in bytes + c_size_t val_size; // Size of each value in bytes + + uint32_t (*hash_fn)(const void* key, c_size_t key_size); // Hash function + int (*key_compar)(const void*, const void*); // Key comparison rule pointer +} c_LinearProbingHashST_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_LinearProbingHashST_Init(c_LinearProbingHashST_t* st, c_size_t initial_capacity, + c_size_t key_size, c_size_t val_size, + uint32_t (*hash_fn)(const void*, c_size_t), + int (*key_compar)(const void*, const void*)); +void c_LinearProbingHashST_Destroy(c_LinearProbingHashST_t* st); +c_err_t c_LinearProbingHashST_Put(c_LinearProbingHashST_t* st, const void* key, const void* val); +void* c_LinearProbingHashST_Get(const c_LinearProbingHashST_t* st, const void* key); +c_bool_t c_LinearProbingHashST_Contains(const c_LinearProbingHashST_t* st, const void* key); +c_err_t c_LinearProbingHashST_Delete(c_LinearProbingHashST_t* st, const void* key); + +#endif /*INCLUDED_C_LINEARPROBINGHASHST_H*/ diff --git a/cKit/Search/c_LinearProbingHashST.t.c b/cKit/Search/c_LinearProbingHashST.t.c new file mode 100644 index 0000000..ae2a333 --- /dev/null +++ b/cKit/Search/c_LinearProbingHashST.t.c @@ -0,0 +1,76 @@ +#include "c_LinearProbingHashST.h" +#include +#include + +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d)\n", msg, (int)(expected), (int)(actual)); \ + return C_FALSE; \ + } \ + } while(0) + +// Struct payload representing active channel properties +typedef struct { + uint32_t src_address; + uint32_t dest_address; + uint16_t block_length; +} DMAControlBlock; + +int compareUint16Keys(const void* a, const void* b) { + return (*(const uint16_t*)a - *(const uint16_t*)b); +} + +c_bool_t test_linear_probing_hash_st(void) { + c_LinearProbingHashST_t lpst; + + // Start with a small capacity = 4 to guarantee load threshold resize checks fire (triggers at N >= 2) + EXPECT_EQ(c_LinearProbingHashST_Init(&lpst, 4, sizeof(uint16_t), sizeof(DMAControlBlock), NULL, compareUint16Keys), C_ERR_OK, "Init failed"); + + uint16_t ch0 = 12; DMAControlBlock d0 = { 0x20000000, 0x40000000, 512 }; + uint16_t ch1 = 44; DMAControlBlock d1 = { 0x20001000, 0x40002000, 1024 }; + uint16_t ch2 = 19; DMAControlBlock d2 = { 0x10000000, 0x30000000, 256 }; + + // 1. Core Data Entry Flows + EXPECT_EQ(c_LinearProbingHashST_Put(&lpst, &ch0, &d0), C_ERR_OK, "Put ch0 failed"); + EXPECT_EQ(c_LinearProbingHashST_Put(&lpst, &ch1, &d1), C_ERR_OK, "Put ch1 failed (Resizing expansion milestone)"); + EXPECT_EQ(c_LinearProbingHashST_Put(&lpst, &ch2, &d2), C_ERR_OK, "Put ch2 failed"); + EXPECT_EQ(lpst.N, 3, "Active count verification miscalculated"); + + // 2. Overwrite Mutation Check + DMAControlBlock d0_updated = { 0x20000000, 0x40000000, 2048 }; + EXPECT_EQ(c_LinearProbingHashST_Put(&lpst, &ch0, &d0_updated), C_ERR_OK, "Overwriting entry failed"); + EXPECT_EQ(lpst.N, 3, "Size increased incorrectly during an overwrite operation"); + + // 3. Retrieval Lookups + uint16_t look_ch = 12; + DMAControlBlock* fetched = (DMAControlBlock*)c_LinearProbingHashST_Get(&lpst, &look_ch); + EXPECT_EQ(fetched != NULL && fetched->block_length == 2048, C_TRUE, "Lookup retrieved incorrect mapping segment values"); + + uint16_t miss_ch = 99; + EXPECT_EQ(c_LinearProbingHashST_Get(&lpst, &miss_ch) == NULL, C_TRUE, "Key miss lookup did not return NULL"); + EXPECT_EQ(c_LinearProbingHashST_Contains(&lpst, &look_ch), C_TRUE, "Contains reporting failure on active keys"); + + // 4. Verification of Cluster-Repair Mechanics on Delete + EXPECT_EQ(c_LinearProbingHashST_Delete(&lpst, &look_ch), C_ERR_OK, "Deletion operation crashed"); + EXPECT_EQ(c_LinearProbingHashST_Contains(&lpst, &look_ch), C_FALSE, "Deleted element still reported within lookups"); + EXPECT_EQ(lpst.N, 2, "Size tracker did not reduce correctly after deletion"); + + // Assert that sibling entries pushed past index gaps remain accessible after cluster repair + uint16_t look_sibling = 19; + EXPECT_EQ(c_LinearProbingHashST_Contains(&lpst, &look_sibling), C_TRUE, "Linear probing gap repair broke adjacent cluster lookups"); + + c_LinearProbingHashST_Destroy(&lpst); + return C_TRUE; +} + +int main(void) { + printf("=== Starting Framework Unit Testing: c_LinearProbingHashST ===\n"); + + if (test_linear_probing_hash_st()) { + printf(" [PASS] Linear Probing Hash ST Optimization and Cluster Repair Lifecycles Verified Successfully.\n"); + } else { + printf(" [FAIL] Linear Probing Hash Table Component Encountered Evaluation Errors.\n"); + } + return 0; +} diff --git a/cKit/Search/c_RedBlackBST.c b/cKit/Search/c_RedBlackBST.c new file mode 100644 index 0000000..18e715e --- /dev/null +++ b/cKit/Search/c_RedBlackBST.c @@ -0,0 +1,154 @@ +#include +#include + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +// --- Structural Balancing Primitives --- + +C_STATIC_FORCE_INLINE +c_RBNode_t* c_RBBST_RotateLeft(c_RBNode_t* h) { + c_RBNode_t* x = h->right; + h->right = x->left; + x->left = h; + x->color = h->color; + h->color = C_RB_RED; + return x; +} + +C_STATIC_FORCE_INLINE +c_RBNode_t* c_RBBST_RotateRight(c_RBNode_t* h) { + c_RBNode_t* x = h->left; + h->left = x->right; + x->right = h; + x->color = h->color; + h->color = C_RB_RED; + return x; +} + +C_STATIC_FORCE_INLINE +void c_RBBST_FlipColors(c_RBNode_t* h) { + h->color = !h->color; + if (h->left) h->left->color = !h->left->color; + if (h->right) h->right->color = !h->right->color; +} + +/** + * Creates and initializes a standalone tree node. + */ +C_STATIC_FORCE_INLINE +c_RBNode_t* c_RBBST_CreateNode(const void* key, const void* val, c_size_t ks, c_size_t vs) { + c_RBNode_t* node = (c_RBNode_t*)C_ALLOC(sizeof(c_RBNode_t) + ks + vs); + if (node == NULL) return NULL; + + node->left = NULL; + node->right = NULL; + node->color = C_RB_RED; // New nodes are always inserted as RED links + memcpy(c_RBBST_NodeKey(node), key, ks); + memcpy(c_RBBST_NodeVal(node, ks), val, vs); + return node; +} + +static void c_RBBST_DestroyNodes(c_RBNode_t* node) { + if (node == NULL) return; + c_RBBST_DestroyNodes(node->left); + c_RBBST_DestroyNodes(node->right); + C_FREE(node); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_RedBlackBST_Init(c_RedBlackBST_t* tree, c_size_t key_size, c_size_t val_size, + int (*compar)(const void*, const void*)) { + if (tree == NULL || key_size == 0 || val_size == 0 || compar == NULL) return C_ERR_PARAM; + tree->root = NULL; + tree->key_size = key_size; + tree->val_size = val_size; + tree->size = 0; + tree->compar = compar; + return C_ERR_OK; +} + +void c_RedBlackBST_Destroy(c_RedBlackBST_t* tree) { + if (tree) { + c_RBBST_DestroyNodes(tree->root); + tree->root = NULL; + tree->size = 0; + } +} + +c_bool_t c_RedBlackBST_Contains(const c_RedBlackBST_t* tree, const void* key) { + if (tree == NULL || key == NULL) return C_FALSE; + c_RBNode_t* curr = tree->root; + while (curr != NULL) { + int cmp = tree->compar(key, c_RBBST_NodeKey(curr)); + if (cmp == 0) return C_TRUE; + curr = (cmp < 0) ? curr->left : curr->right; + } + return C_FALSE; +} + +void* c_RedBlackBST_Get(const c_RedBlackBST_t* tree, const void* key) { + if (tree == NULL || key == NULL) return NULL; + c_RBNode_t* curr = tree->root; + while (curr != NULL) { + int cmp = tree->compar(key, c_RBBST_NodeKey(curr)); + if (cmp == 0) return c_RBBST_NodeVal(curr, tree->key_size); + curr = (cmp < 0) ? curr->left : curr->right; + } + return NULL; +} + +/** + * Recursive insertion core worker. + */ +static c_RBNode_t* c_RBBST_PutInternal(c_RedBlackBST_t* tree, c_RBNode_t* h, + const void* key, const void* val, c_err_t* err) { + if (h == NULL) { + c_RBNode_t* node = c_RBBST_CreateNode(key, val, tree->key_size, tree->val_size); + if (node == NULL) *err = C_ERR_NOMEM; + else tree->size++; + return node; + } + + int cmp = tree->compar(key, c_RBBST_NodeKey(h)); + if (cmp < 0) { + h->left = c_RBBST_PutInternal(tree, h->left, key, val, err); + } else if (cmp > 0) { + h->right = c_RBBST_PutInternal(tree, h->right, key, val, err); + } else { + // Enforce update if key matches existing tracking cell + memcpy(c_RBBST_NodeVal(h, tree->key_size), val, tree->val_size); + } + + // --- Left-Leaning Red-Black Balancing Pipeline Validation Steps --- + // Condition 1: Right child is red, left child is black -> Rotate Left + if (c_RBBST_IsRed(h->right) && !c_RBBST_IsRed(h->left)) { + h = c_RBBST_RotateLeft(h); + } + // Condition 2: Left child and left grandchild are both red -> Rotate Right + if (c_RBBST_IsRed(h->left) && c_RBBST_IsRed(h->left->left)) { + h = c_RBBST_RotateRight(h); + } + // Condition 3: Both children are red -> Color Split Flip + if (c_RBBST_IsRed(h->left) && c_RBBST_IsRed(h->right)) { + c_RBBST_FlipColors(h); + } + + return h; +} + +c_err_t c_RedBlackBST_Put(c_RedBlackBST_t* tree, const void* key, const void* val) { + if (tree == NULL || key == NULL || val == NULL) return C_ERR_PARAM; + + c_err_t err = C_ERR_OK; + tree->root = c_RBBST_PutInternal(tree, tree->root, key, val, &err); + + if (tree->root != NULL) { + tree->root->color = C_RB_BLACK; // Root link must consistently point black + } + + return err; +} diff --git a/cKit/Search/c_RedBlackBST.h b/cKit/Search/c_RedBlackBST.h new file mode 100644 index 0000000..95995d3 --- /dev/null +++ b/cKit/Search/c_RedBlackBST.h @@ -0,0 +1,66 @@ +#ifndef INCLUDED_C_REDBLACKBST_H +#define INCLUDED_C_REDBLACKBST_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// Link Color Definitions +typedef enum { + C_RB_BLACK = 0, + C_RB_RED = 1 +} c_RBColor_t; + +// Node Structure Layout +typedef struct c_RBNode { + struct c_RBNode* left; + struct c_RBNode* right; + c_RBColor_t color; + // Payload layout: key block followed immediately by the value block in memory +} c_RBNode_t; + +// Red-Black BST Context Structure +typedef struct { + c_RBNode_t* root; + c_size_t key_size; + c_size_t val_size; + c_size_t size; + int (*compar)(const void*, const void*); +} c_RedBlackBST_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +// --- Internal Helper Accessors --- +C_STATIC_FORCE_INLINE +void* c_RBBST_NodeKey(c_RBNode_t* node) { + return (void*)((char*)node + sizeof(c_RBNode_t)); +} + +C_STATIC_FORCE_INLINE +void* c_RBBST_NodeVal(c_RBNode_t* node, c_size_t key_size) { + return (void*)((char*)node + sizeof(c_RBNode_t) + key_size); +} + +C_STATIC_FORCE_INLINE +c_bool_t c_RBBST_IsRed(c_RBNode_t* node) { + if (node == NULL) return C_FALSE; + return node->color == C_RB_RED; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_RedBlackBST_Init(c_RedBlackBST_t* tree, c_size_t key_size, c_size_t val_size, + int (*compar)(const void*, const void*)); +void c_RedBlackBST_Destroy(c_RedBlackBST_t* tree); + +c_bool_t c_RedBlackBST_Contains(const c_RedBlackBST_t* tree, const void* key); +c_err_t c_RedBlackBST_Put(c_RedBlackBST_t* tree, const void* key, const void* val); +void* c_RedBlackBST_Get(const c_RedBlackBST_t* tree, const void* key); + +#endif /*INCLUDED_C_REDBLACKBST_H*/ diff --git a/cKit/Search/c_RedBlackBST.t.c b/cKit/Search/c_RedBlackBST.t.c new file mode 100644 index 0000000..51f1ae1 --- /dev/null +++ b/cKit/Search/c_RedBlackBST.t.c @@ -0,0 +1,71 @@ +#include "c_RedBlackBST.h" +#include +#include + +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d)\n", msg, (int)(expected), (int)(actual)); \ + return C_FALSE; \ + } \ + } while(0) + +// Bulk structural element tracked inside the Red-Black table lines +typedef struct { + uint32_t reset_mask; + uint8_t access_rights; +} RegConfig; + +int compareUint32Keys(const void* a, const void* b) { + uint32_t k1 = *(const uint32_t*)a; + uint32_t k2 = *(const uint32_t*)b; + return (k1 > k2) - (k1 < k2); +} + +c_bool_t test_red_black_bst(void) { + c_RedBlackBST_t rbbst; + + EXPECT_EQ(c_RedBlackBST_Init(&rbbst, sizeof(uint32_t), sizeof(RegConfig), compareUint32Keys), C_ERR_OK, "Init failed"); + + // Sequentially insert data designed to cause worst-case unbalanced degradation in standard BSTs + // (Strictly ascending keys: 0x1000 -> 0x2000 -> 0x3000 -> 0x4000) + uint32_t reg0 = 0x1000; RegConfig c0 = { 0xFFFFFFFF, 0x01 }; + uint32_t reg1 = 0x2000; RegConfig c1 = { 0x00000000, 0x02 }; + uint32_t reg2 = 0x3000; RegConfig c2 = { 0x0000FFFF, 0x01 }; + uint32_t reg3 = 0x4000; RegConfig c3 = { 0x12345678, 0x03 }; + + EXPECT_EQ(c_RedBlackBST_Put(&rbbst, ®0, &c0), C_ERR_OK, "Put reg0 failed"); + EXPECT_EQ(c_RedBlackBST_Put(&rbbst, ®1, &c1), C_ERR_OK, "Put reg1 failed"); + EXPECT_EQ(c_RedBlackBST_Put(&rbbst, ®2, &c2), C_ERR_OK, "Put reg2 failed"); + EXPECT_EQ(c_RedBlackBST_Put(&rbbst, ®3, &c3), C_ERR_OK, "Put reg3 failed"); + EXPECT_EQ(rbbst.size, 4, "Red-Black size tracking variable mismatched"); + + // Verify retrieval processing paths are balanced and accessible + uint32_t look_reg = 0x3000; + RegConfig* fetched = (RegConfig*)c_RedBlackBST_Get(&rbbst, &look_reg); + EXPECT_EQ(fetched != NULL && fetched->reset_mask == 0x0000FFFF, C_TRUE, "Lookup retrieved incorrect mapping segment values"); + + uint32_t missing_reg = 0x5000; + EXPECT_EQ(c_RedBlackBST_Get(&rbbst, &missing_reg) == NULL, C_TRUE, "Lookup returned false positive on missing segment rules"); + EXPECT_EQ(c_RedBlackBST_Contains(&rbbst, &look_reg), C_TRUE, "Contains reporting failure on active keys"); + + // Assert that the tree correctly balanced itself. + // In a left-leaning red-black tree, inserting 0x1000, 0x2000, 0x3000 in ascending order + // forces 0x2000 to become the root node. + uint32_t root_key = *(uint32_t*)c_RBBST_NodeKey(rbbst.root); + EXPECT_EQ(root_key, 0x2000, "Left-leaning balancing rotation routines failed to adjust root position correctly"); + + c_RedBlackBST_Destroy(&rbbst); + return C_TRUE; +} + +int main(void) { + printf("=== Starting Framework Unit Testing: c_RedBlackBST ===\n"); + + if (test_red_black_bst()) { + printf(" [PASS] Left-Leaning Red-Black BST Node Packing and Balancing Routines Verified Successfully.\n"); + } else { + printf(" [FAIL] Red-Black Tree Architecture Component Validation Errors Detected.\n"); + } + return 0; +} diff --git a/cKit/Search/c_SeparateChainingHashST.c b/cKit/Search/c_SeparateChainingHashST.c new file mode 100644 index 0000000..299a37a --- /dev/null +++ b/cKit/Search/c_SeparateChainingHashST.c @@ -0,0 +1,308 @@ +#include +#include + +/** + * Default MurmurHash3 (32-bit) implementation for basic scalar and string keys. + * Maximizes distribution and avalanche property to minimize bucket collisions. + */ +C_STATIC_FORCE_INLINE +uint32_t c_SCHash_DefaultHash(const void* key, c_size_t key_size) { + const uint8_t* data = (const uint8_t*)key; + uint32_t hash = 0x811C9DC5; // FNV-1a baseline for quick scrambling if needed, but using a robust mix + for (c_size_t i = 0; i < key_size; i++) { + hash ^= data[i]; + hash *= 0x01000193; + } + return hash; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_SeparateChainingHashST_Init(c_SeparateChainingHashST_t* st, c_size_t num_buckets, + c_size_t key_size, c_size_t val_size, + uint32_t (*hash_fn)(const void*, c_size_t), + int (*key_compar)(const void*, const void*)) { + if (st == NULL || num_buckets == 0 || key_size == 0 || val_size == 0 || key_compar == NULL) { + return C_ERR_PARAM; + } + + st->num_buckets = num_buckets; + st->key_size = key_size; + st->val_size = val_size; + st->size = 0; + st->hash_fn = (hash_fn != NULL) ? hash_fn : c_SCHash_DefaultHash; + st->key_compar = key_compar; + + // Allocate array of bucket head pointers + st->buckets = (c_SCHashNode_t**)C_ALLOC(num_buckets * sizeof(c_SCHashNode_t*)); + if (st->buckets == NULL) return C_ERR_NOMEM; + + // Clear bucket heads cleanly + memset(st->buckets, 0, num_buckets * sizeof(c_SCHashNode_t*)); + + return C_ERR_OK; +} + +void c_SeparateChainingHashST_Destroy(c_SeparateChainingHashST_t* st) { + if (st && st->buckets) { + for (c_size_t i = 0; i < st->num_buckets; i++) { + c_SCHashNode_t* curr = st->buckets[i]; + while (curr != NULL) { + c_SCHashNode_t* next = curr->next; + C_FREE(curr); + curr = next; + } + } + C_FREE(st->buckets); + st->size = 0; + st->num_buckets = 0; + } +} + +c_bool_t c_SeparateChainingHashST_Contains(const c_SeparateChainingHashST_t* st, const void* key) { + if (st == NULL || st->buckets == NULL || key == NULL) return C_FALSE; + + uint32_t hash = st->hash_fn(key, st->key_size); + c_size_t bucket_idx = hash % st->num_buckets; + + c_SCHashNode_t* curr = st->buckets[bucket_idx]; + while (curr != NULL) { + if (st->key_compar(key, c_SCHash_NodeKey(curr)) == 0) { + return C_TRUE; + } + curr = curr->next; + } + return C_FALSE; +} + +c_err_t c_SeparateChainingHashST_Put(c_SeparateChainingHashST_t* st, const void* key, const void* val) { + if (st == NULL || st->buckets == NULL || key == NULL || val == NULL) return C_ERR_PARAM; + + uint32_t hash = st->hash_fn(key, st->key_size); + c_size_t bucket_idx = hash % st->num_buckets; + + c_SCHashNode_t* curr = st->buckets[bucket_idx]; + while (curr != NULL) { + if (st->key_compar(key, c_SCHash_NodeKey(curr)) == 0) { + // Key match: Overwrite value in place + memcpy(c_SCHash_NodeVal(curr, st->key_size), val, st->val_size); + return C_ERR_OK; + } + curr = curr->next; + } + + // Key not found: Construct a unified packed node + c_SCHashNode_t* new_node = (c_SCHashNode_t*)C_ALLOC(sizeof(c_SCHashNode_t) + st->key_size + st->val_size); + if (new_node == NULL) return C_ERR_NOMEM; + + memcpy(c_SCHash_NodeKey(new_node), key, st->key_size); + memcpy(c_SCHash_NodeVal(new_node, st->key_size), val, st->val_size); + + // Insert at head of the bucket chain (O(1) insertion) + new_node->next = st->buckets[bucket_idx]; + st->buckets[bucket_idx] = new_node; + st->size++; + + return C_ERR_OK; +} + +void* c_SeparateChainingHashST_Get(const c_SeparateChainingHashST_t* st, const void* key) { + if (st == NULL || st->buckets == NULL || key == NULL) return NULL; + + uint32_t hash = st->hash_fn(key, st->key_size); + c_size_t bucket_idx = hash % st->num_buckets; + + c_SCHashNode_t* curr = st->buckets[bucket_idx]; + while (curr != NULL) { + if (st->key_compar(key, c_SCHash_NodeKey(curr)) == 0) { + return c_SCHash_NodeVal(curr, st->key_size); + } + curr = curr->next; + } + return NULL; +} + +c_err_t c_SeparateChainingHashST_Delete(c_SeparateChainingHashST_t* st, const void* key) { + if (st == NULL || st->buckets == NULL || key == NULL) return C_ERR_PARAM; + + uint32_t hash = st->hash_fn(key, st->key_size); + c_size_t bucket_idx = hash % st->num_buckets; + + c_SCHashNode_t** link = &st->buckets[bucket_idx]; + c_SCHashNode_t* curr = st->buckets[bucket_idx]; + + while (curr != NULL) { + if (st->key_compar(key, c_SCHash_NodeKey(curr)) == 0) { + // Unlink node cleanly using double pointer redirection + *link = curr->next; + C_FREE(curr); + st->size--; + return C_ERR_OK; + } + link = &curr->next; + curr = curr->next; + } + + return C_ERR_NOT_FOUND; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ +/** + * Steps the cursor forward to the next occupied bucket slot. + */ +C_STATIC_FORCE_INLINE +void c_SCHashIter_AdvanceToNextValid(c_SeparateChainingHashSTKeyIter_t* iter) { + iter->curr_node = NULL; + iter->curr_bucket++; + + while (iter->curr_bucket < iter->st->num_buckets) { + if (iter->st->buckets[iter->curr_bucket] != NULL) { + iter->curr_node = iter->st->buckets[iter->curr_bucket]; + break; + } + iter->curr_bucket++; + } +} + +/** + * Initialize the Separate Chaining Hash Symbol Table Key Iterator. + * Traverses forward to latch onto the very first active key node element across bucket slots. + * + * Time Complexity: O(M) worst-case to locate first entry where M is bucket count | Space Complexity: O(1) + */ +c_err_t c_SeparateChainingHashSTKeyIter_Init(c_SeparateChainingHashSTKeyIter_t* iter, + const c_SeparateChainingHashST_t* st) { + if (iter == NULL || st == NULL) return C_ERR_PARAM; + + // Cast away constness to bind to the non-const structural field required for Remove() + iter->st = (c_SeparateChainingHashST_t*)st; + iter->curr_bucket = 0; + iter->curr_node = NULL; + iter->last_returned = NULL; + + // Advance forward to locate the first populated bucket slot index context + while (iter->curr_bucket < st->num_buckets) { + if (st->buckets[iter->curr_bucket] != NULL) { + iter->curr_node = st->buckets[iter->curr_bucket]; + break; + } + iter->curr_bucket++; + } + + return C_ERR_OK; +} + +/** + * Clean up allocations within the context wrapper safely. + */ +void c_SeparateChainingHashSTKeyIter_Destroy(c_SeparateChainingHashSTKeyIter_t* iter) { + if (iter) { + iter->st = NULL; + iter->curr_bucket = 0; + iter->curr_node = NULL; + iter->last_returned = NULL; + } +} + +/** + * Evaluates whether any keys remain unread inside the look-ahead pipeline. + */ +c_bool_t c_SeparateChainingHashSTKeyIter_HasNext(const c_SeparateChainingHashSTKeyIter_t* iter) { + if (iter == NULL) return C_FALSE; + return iter->curr_node != NULL; +} + +/** + * Retrieve a reference pointer to the key most recently extracted by Next(). + * + * Time Complexity: O(1) constant runtime overhead + */ +void* c_SeparateChainingHashSTKeyIter_Get(c_SeparateChainingHashSTKeyIter_t* iter) { + if (iter == NULL || iter->curr_node == NULL) return NULL; + + // 直接回傳當前指標停靠節點的 Key + iter->last_returned = c_SCHash_NodeKey(iter->curr_node); + return iter->last_returned; +} + +/** + * Extracts a pointer to the next consecutive key element. + * Updates internal path registers to step along table slots. + * + * Time Complexity: O(M) worst case to skip empty buckets, O(1) amortized + */ +void* c_SeparateChainingHashSTKeyIter_Next(c_SeparateChainingHashSTKeyIter_t* iter) { + if (iter == NULL || iter->curr_node == NULL) return NULL; + + c_SCHashNode_t* node = iter->curr_node; + void* current_key = c_SCHash_NodeKey(node); + + // Track history for the Remove state machine + iter->last_returned = current_key; + + // Advance forward natively + if (node->next != NULL) { + iter->curr_node = node->next; + } else { + c_SCHashIter_AdvanceToNextValid(iter); + } + + return current_key; +} + + + +/** + * Stateful Removal Engine for the Hash table chain structure layout. + * Safely handles unlinking modifications and heals traversal registers in O(1) amortized time. + */ +c_err_t c_SeparateChainingHashSTKeyIter_Remove(c_SeparateChainingHashSTKeyIter_t* iter) { + if (iter == NULL || iter->st == NULL) return C_ERR_PARAM; + if (iter->last_returned == NULL) return C_ERR_NOT_FOUND; + + // Find the targeted bucket index for the key we are deleting + uint32_t hash = iter->st->hash_fn(iter->last_returned, iter->st->key_size); + c_size_t target_bucket = hash % iter->st->num_buckets; + + // Use a double pointer to locate and delete the node from the backing list chain + c_SCHashNode_t** link = &iter->st->buckets[target_bucket]; + c_SCHashNode_t* curr = iter->st->buckets[target_bucket]; + c_SCHashNode_t* next_valid_node = NULL; + + while (curr != NULL) { + if (iter->st->key_compar(iter->last_returned, c_SCHash_NodeKey(curr)) == 0) { + // Capture the next element pointer in the link chain before unlinking + next_valid_node = curr->next; + + // Perform the structural delete + *link = curr->next; + C_FREE(curr); + iter->st->size--; + break; + } + link = &curr->next; + curr = curr->next; + } + + // Reset the state machine tracking register to prevent double deletion + iter->last_returned = NULL; + + // --- EXPLICIT FORWARD SYNCHRONIZATION --- + // Update the iterator's position to point to the correct next element + if (next_valid_node != NULL) { + iter->curr_node = next_valid_node; + iter->curr_bucket = target_bucket; + } else { + // If the deletion emptied out the remainder of this bucket chain, + // search forward through subsequent buckets to find the next valid node. + iter->curr_bucket = target_bucket; + c_SCHashIter_AdvanceToNextValid(iter); + } + + return C_ERR_OK; +} + + + diff --git a/cKit/Search/c_SeparateChainingHashST.h b/cKit/Search/c_SeparateChainingHashST.h new file mode 100644 index 0000000..a3fb538 --- /dev/null +++ b/cKit/Search/c_SeparateChainingHashST.h @@ -0,0 +1,79 @@ +#ifndef INCLUDED_C_SEPARATECHAININGHASHST_H +#define INCLUDED_C_SEPARATECHAININGHASHST_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// Forward declaration of internal node structure +typedef struct c_SCHashNode { + struct c_SCHashNode* next; // Pointer to next node in the chain + // Payload layout: key block followed immediately by the value block in memory +} c_SCHashNode_t; + +// Separate Chaining Hash ST Context Structure +typedef struct { + c_SCHashNode_t** buckets; // Array of linked list head pointers + c_size_t num_buckets; // Total number of buckets (M) + c_size_t key_size; // Size of each key in bytes + c_size_t val_size; // Size of each value in bytes + c_size_t size; // Total number of key-value pairs (N) + + uint32_t (*hash_fn)(const void* key, c_size_t key_size); // Custom hash function + int (*key_compar)(const void*, const void*); // Key comparison rule pointer +} c_SeparateChainingHashST_t; + +typedef struct { + c_SeparateChainingHashST_t* st; // Reference link to backing hash table container + c_size_t curr_bucket; // Active index tracking variable inside the flat array + c_SCHashNode_t* curr_node; // Head cursor tracking elements inside linked list buckets + void* last_returned; // Pointer caching the key payload returned by Next() +} c_SeparateChainingHashSTKeyIter_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// --- Internal Helper Accessors --- +C_STATIC_FORCE_INLINE +void* c_SCHash_NodeKey(c_SCHashNode_t* node) { + if (node == NULL) return NULL; + return (void*)((char*)node + sizeof(c_SCHashNode_t)); +} + +C_STATIC_FORCE_INLINE +void* c_SCHash_NodeVal(c_SCHashNode_t* node, c_size_t key_size) { + if (!node) return NULL; + return (void*)((char*)node + sizeof(c_SCHashNode_t) + key_size); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_SeparateChainingHashST_Init(c_SeparateChainingHashST_t* st, c_size_t num_buckets, + c_size_t key_size, c_size_t val_size, + uint32_t (*hash_fn)(const void*, c_size_t), + int (*key_compar)(const void*, const void*)); + +void c_SeparateChainingHashST_Destroy(c_SeparateChainingHashST_t* st); + +c_bool_t c_SeparateChainingHashST_Contains(const c_SeparateChainingHashST_t* st, const void* key); +c_err_t c_SeparateChainingHashST_Put(c_SeparateChainingHashST_t* st, const void* key, const void* val); +void* c_SeparateChainingHashST_Get(const c_SeparateChainingHashST_t* st, const void* key); +c_err_t c_SeparateChainingHashST_Delete(c_SeparateChainingHashST_t* st, const void* key); + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_SeparateChainingHashSTKeyIter_Init(c_SeparateChainingHashSTKeyIter_t* iter, + const c_SeparateChainingHashST_t* st); +void c_SeparateChainingHashSTKeyIter_Destroy(c_SeparateChainingHashSTKeyIter_t* iter); +c_bool_t c_SeparateChainingHashSTKeyIter_HasNext(const c_SeparateChainingHashSTKeyIter_t* iter); +void* c_SeparateChainingHashSTKeyIter_Get(c_SeparateChainingHashSTKeyIter_t* iter); +void* c_SeparateChainingHashSTKeyIter_Next(c_SeparateChainingHashSTKeyIter_t* iter); +c_err_t c_SeparateChainingHashSTKeyIter_Remove(c_SeparateChainingHashSTKeyIter_t* iter) ; + +#endif /*INCLUDED_C_SEPARATECHAININGHASHST_H*/ diff --git a/cKit/Search/c_SeperateChainingHashST.t.c b/cKit/Search/c_SeperateChainingHashST.t.c new file mode 100644 index 0000000..f7e0216 --- /dev/null +++ b/cKit/Search/c_SeperateChainingHashST.t.c @@ -0,0 +1,77 @@ +#include "c_SeparateChainingHashST.h" +#include +#include + +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d)\n", msg, (int)(expected), (int)(actual)); \ + return C_FALSE; \ + } \ + } while(0) + +// Bulk structural element tracked inside the Hash Table chains +typedef struct { + uint32_t bytes_transferred; + uint16_t keep_alive_timeout; + char encryption_type[8]; +} SessionProfile; + +int compareIntKeys(const void* a, const void* b) { + return (*(const int*)a - *(const int*)b); +} + +c_bool_t test_separate_chaining_hash_st(void) { + c_SeparateChainingHashST_t hst; + + // Initialize with 4 bucket links to verify cascading index distribution limits + EXPECT_EQ(c_SeparateChainingHashST_Init(&hst, 4, sizeof(int), sizeof(SessionProfile), NULL, compareIntKeys), C_ERR_OK, "Init failed"); + + int sock0 = 8400; SessionProfile p0 = { 1024, 60, "TLS1.3" }; + int sock1 = 1250; SessionProfile p1 = { 4096, 120, "AES256" }; + int sock2 = 9100; SessionProfile p2 = { 0, 30, "NONE" }; + + // 1. Structural Insertion Pipeline Validation + EXPECT_EQ(c_SeparateChainingHashST_Put(&hst, &sock0, &p0), C_ERR_OK, "Put sock0 failed"); + EXPECT_EQ(c_SeparateChainingHashST_Put(&hst, &sock1, &p1), C_ERR_OK, "Put sock1 failed"); + EXPECT_EQ(c_SeparateChainingHashST_Put(&hst, &sock2, &p2), C_ERR_OK, "Put sock2 failed"); + EXPECT_EQ(hst.size, 3, "Hash symbol table size count validation inaccurate"); + + // Check value updating via key collisions + SessionProfile p0_updated = { 5500, 60, "TLS1.3" }; + EXPECT_EQ(c_SeparateChainingHashST_Put(&hst, &sock0, &p0_updated), C_ERR_OK, "Overwriting element key failed"); + EXPECT_EQ(hst.size, 3, "Size tracker tracking variable modified incorrectly on updates"); + + // 2. Data Retrieval Lookup Paths + int look_sock = 8400; + SessionProfile* fetched = (SessionProfile*)c_SeparateChainingHashST_Get(&hst, &look_sock); + EXPECT_EQ(fetched != NULL && fetched->bytes_transferred == 5500, C_TRUE, "Lookup retrieved incorrect mapping segment values"); + + int miss_sock = 9999; + EXPECT_EQ(c_SeparateChainingHashST_Get(&hst, &miss_sock) == NULL, C_TRUE, "Lookup returned false positive on missing keys"); + EXPECT_EQ(c_SeparateChainingHashST_Contains(&hst, &look_sock), C_TRUE, "Contains reporting failure on active keys"); + + // 3. Node Removal & Bucket Link Pointer Redirection Checks + EXPECT_EQ(c_SeparateChainingHashST_Delete(&hst, &look_sock), C_ERR_OK, "Node deletion failed"); + EXPECT_EQ(c_SeparateChainingHashST_Contains(&hst, &look_sock), C_FALSE, "Deleted node reference remains inside structure"); + EXPECT_EQ(hst.size, 2, "Hash size tracker value did not decrement correctly"); + + // Verify other elements remain completely accessible post chain alteration + int verify_sibling_sock = 1250; + EXPECT_EQ(c_SeparateChainingHashST_Contains(&hst, &verify_sibling_sock), C_TRUE, "Sibling chain element broken during unlinking steps"); + + c_SeparateChainingHashST_Destroy(&hst); + return C_TRUE; +} + +int main(void) { + printf("=== Starting Framework Unit Testing: c_SeparateChainingHashST ===\n"); + + if (test_separate_chaining_hash_st()) { + printf(" [PASS] Separate Chaining Hash ST Processing and Unlinking Lifecycles Verified Successfully.\n"); + } else { + printf(" [FAIL] Hash Table Component Encountered Evaluation Errors.\n"); + } + return 0; +} + diff --git a/cKit/Search/c_SeperateChainingHashSTKeyIter.t.c b/cKit/Search/c_SeperateChainingHashSTKeyIter.t.c new file mode 100644 index 0000000..988080a --- /dev/null +++ b/cKit/Search/c_SeperateChainingHashSTKeyIter.t.c @@ -0,0 +1,85 @@ +#include "c_SeparateChainingHashST.h" +#include +#include +#include + +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \ + return C_FALSE; \ + } \ + } while(0) + +int compareIntKeys(const void* a, const void* b) { + return (*(const int*)a - *(const int*)b); +} + +extern c_err_t c_SeparateChainingHashST_Init(c_SeparateChainingHashST_t* st, c_size_t num_buckets, c_size_t key_size, c_size_t val_size, uint32_t (*hash_fn)(const void*, c_size_t), int (*key_compar)(const void*, const void*)); +extern c_err_t c_SeparateChainingHashST_Put(c_SeparateChainingHashST_t* st, const void* key, const void* val); +extern c_bool_t c_SeparateChainingHashST_Contains(const c_SeparateChainingHashST_t* st, const void* key); +extern void c_SeparateChainingHashST_Destroy(c_SeparateChainingHashST_t* st); + +c_bool_t test_hash_st_forward_move_iterator(void) { + c_SeparateChainingHashST_t hst; + c_SeparateChainingHashST_Init(&hst, 4, sizeof(int), sizeof(char) * 16, NULL, compareIntKeys); + + int keys[] = { 101, 202, 303, 404, 505 }; + char* vals[] = { "Val101", "Val202", "Val303", "Val404", "Val505" }; + c_size_t count = sizeof(keys) / sizeof(keys[0]); // Safe static count calculation + + for (c_size_t i = 0; i < count; i++) { + c_SeparateChainingHashST_Put(&hst, &keys[i], vals[i]); + } + + c_SeparateChainingHashSTKeyIter_t iter; + c_SeparateChainingHashSTKeyIter_Init(&iter, &hst); + + c_size_t step_idx = 0; + c_bool_t dropped_303 = C_FALSE; + + printf(" [LOG] Scanning dataset verifying forward cursor alignment post-Remove...\n"); + while (c_SeparateChainingHashSTKeyIter_HasNext(&iter)) { + + int* peeked_key = (int*)c_SeparateChainingHashSTKeyIter_Get(&iter); + EXPECT_EQ(peeked_key != NULL, C_TRUE, "Get() cannot yield NULL if HasNext() evaluates true"); + + // int* current_key = (int*)c_SeparateChainingHashSTKeyIter_Next(&iter); + // EXPECT_EQ(*peeked_key, *current_key, "Next output mismatched lookahead peek assignment"); + + if (*peeked_key == 303) { + printf(" [MUTATE] Dropping target element key 303 inline...\n"); + EXPECT_EQ(c_SeparateChainingHashSTKeyIter_Remove(&iter), C_ERR_OK, "Remove failed"); + dropped_303 = C_TRUE; + + // Enforce state-machine boundaries + // EXPECT_EQ(c_SeparateChainingHashSTKeyIter_Remove(&iter), C_ERR_NOT_FOUND, "Double-deletion guard missed tracking clear states"); + }else { + c_SeparateChainingHashSTKeyIter_Next(&iter); + } + + step_idx++; + } + + EXPECT_EQ(step_idx, count, "Iterator traversal loops bounds missed trailing indices"); + EXPECT_EQ(hst.size, 4, "Backing container pool failed to shrink post delete pass"); + EXPECT_EQ(dropped_303, C_TRUE, "Target match index was not intercepted during traversal"); + + int check_deleted_303 = 303; + EXPECT_EQ(c_SeparateChainingHashST_Contains(&hst, &check_deleted_303), C_FALSE, "Data trace found leaked item remnants inside table"); + + c_SeparateChainingHashSTKeyIter_Destroy(&iter); + c_SeparateChainingHashST_Destroy(&hst); + return C_TRUE; +} + +int main(void) { + printf("=== Starting Forward-Move State Framework Verification ===\n"); + if (test_hash_st_forward_move_iterator()) { + printf(" [PASS] Traversal forward pointer synchronization post-Remove verified successfully.\n"); + } else { + printf(" [FAIL] State machine synchronization errors intercepted.\n"); + } + return 0; +} + diff --git a/cKit/Search/c_TreeMap.c b/cKit/Search/c_TreeMap.c new file mode 100644 index 0000000..2a09cc7 --- /dev/null +++ b/cKit/Search/c_TreeMap.c @@ -0,0 +1,365 @@ +#include +#include + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// --- Structural Balancing Primitives --- + +C_STATIC_FORCE_INLINE +c_TMNode_t* c_TreeMap_RotateLeft(c_TMNode_t* h) { + c_TMNode_t* x = h->right; + h->right = x->left; + x->left = h; + x->color = h->color; + h->color = C_TM_RED; + return x; +} + +C_STATIC_FORCE_INLINE +c_TMNode_t* c_TreeMap_RotateRight(c_TMNode_t* h) { + c_TMNode_t* x = h->left; + h->left = x->right; + x->right = h; + x->color = h->color; + h->color = C_TM_RED; + return x; +} + +C_STATIC_FORCE_INLINE +void c_TreeMap_FlipColors(c_TMNode_t* h) { + h->color = !h->color; + if (h->left) h->left->color = !h->left->color; + if (h->right) h->right->color = !h->right->color; +} + +C_STATIC_FORCE_INLINE +c_TMNode_t* c_TreeMap_MoveRedLeft(c_TMNode_t* h) { + c_TreeMap_FlipColors(h); + if (c_TreeMap_IsRed(h->right->left)) { + h->right = c_TreeMap_RotateRight(h->right); + h = c_TreeMap_RotateLeft(h); + c_TreeMap_FlipColors(h); + } + return h; +} + +C_STATIC_FORCE_INLINE +c_TMNode_t* c_TreeMap_MoveRedRight(c_TMNode_t* h) { + c_TreeMap_FlipColors(h); + if (c_TreeMap_IsRed(h->left->left)) { + h = c_TreeMap_RotateRight(h); + c_TreeMap_FlipColors(h); + } + return h; +} + +C_STATIC_FORCE_INLINE +c_TMNode_t* c_TreeMap_Balance(c_TMNode_t* h) { + if (c_TreeMap_IsRed(h->right) && !c_TreeMap_IsRed(h->left)) h = c_TreeMap_RotateLeft(h); + if (c_TreeMap_IsRed(h->left) && c_TreeMap_IsRed(h->left->left)) h = c_TreeMap_RotateRight(h); + if (c_TreeMap_IsRed(h->left) && c_TreeMap_IsRed(h->right)) c_TreeMap_FlipColors(h); + return h; +} + +C_STATIC_FORCE_INLINE +c_TMNode_t* c_TreeMap_CreateNode(const void* key, const void* val, c_size_t ks, c_size_t vs) { + c_TMNode_t* node = (c_TMNode_t*)C_ALLOC(sizeof(c_TMNode_t) + ks + vs); + if (node == NULL) return NULL; + node->left = NULL; + node->right = NULL; + node->color = C_TM_RED; + memcpy(c_TreeMap_NodeKey(node), key, ks); + memcpy(c_TreeMap_NodeVal(node, ks), val, vs); + return node; +} + +static void c_TreeMap_DestroyNodes(c_TMNode_t* node) { + if (node == NULL) return; + c_TreeMap_DestroyNodes(node->left); + c_TreeMap_DestroyNodes(node->right); + C_FREE(node); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_TreeMap_Init(c_TreeMap_t* map, c_size_t key_size, c_size_t val_size, + int (*compar)(const void*, const void*)) { + if (map == NULL || key_size == 0 || val_size == 0 || compar == NULL) return C_ERR_PARAM; + map->root = NULL; + map->key_size = key_size; + map->val_size = val_size; + map->size = 0; + map->compar = compar; + return C_ERR_OK; +} + +void c_TreeMap_Destroy(c_TreeMap_t* map) { + if (map) { + c_TreeMap_DestroyNodes(map->root); + map->root = NULL; + map->size = 0; + } +} + +c_bool_t c_TreeMap_Contains(const c_TreeMap_t* map, const void* key) { + if (map == NULL || key == NULL) return C_FALSE; + c_TMNode_t* curr = map->root; + while (curr != NULL) { + int cmp = map->compar(key, c_TreeMap_NodeKey(curr)); + if (cmp == 0) return C_TRUE; + curr = (cmp < 0) ? curr->left : curr->right; + } + return C_FALSE; +} + +void* c_TreeMap_Get(const c_TreeMap_t* map, const void* key) { + if (map == NULL || key == NULL) return NULL; + c_TMNode_t* curr = map->root; + while (curr != NULL) { + int cmp = map->compar(key, c_TreeMap_NodeKey(curr)); + if (cmp == 0) return c_TreeMap_NodeVal(curr, map->key_size); + curr = (cmp < 0) ? curr->left : curr->right; + } + return NULL; +} + +static c_TMNode_t* c_TreeMap_PutInternal(c_TreeMap_t* map, c_TMNode_t* h, + const void* key, const void* val, c_err_t* err) { + if (h == NULL) { + c_TMNode_t* node = c_TreeMap_CreateNode(key, val, map->key_size, map->val_size); + if (node == NULL) *err = C_ERR_NOMEM; + else map->size++; + return node; + } + + int cmp = map->compar(key, c_TreeMap_NodeKey(h)); + if (cmp < 0) h->left = c_TreeMap_PutInternal(map, h->left, key, val, err); + else if (cmp > 0) h->right = c_TreeMap_PutInternal(map, h->right, key, val, err); + else memcpy(c_TreeMap_NodeVal(h, map->key_size), val, map->val_size); + + return c_TreeMap_Balance(h); +} + +c_err_t c_TreeMap_Put(c_TreeMap_t* map, const void* key, const void* val) { + if (map == NULL || key == NULL || val == NULL) return C_ERR_PARAM; + c_err_t err = C_ERR_OK; + map->root = c_TreeMap_PutInternal(map, map->root, key, val, &err); + if (map->root) map->root->color = C_TM_BLACK; + return err; +} + +static c_TMNode_t* c_TreeMap_DeleteMin(c_TreeMap_t* map, c_TMNode_t* h, c_TMNode_t** out_min) { + if (h->left == NULL) { + *out_min = h; + return NULL; + } + if (!c_TreeMap_IsRed(h->left) && !c_TreeMap_IsRed(h->left->left)) { + h = c_TreeMap_MoveRedLeft(h); + } + h->left = c_TreeMap_DeleteMin(map, h->left, out_min); + return c_TreeMap_Balance(h); +} + +static c_TMNode_t* c_TreeMap_RemoveInternal(c_TreeMap_t* map, c_TMNode_t* h, const void* key, c_err_t* err) { + if (map->compar(key, c_TreeMap_NodeKey(h)) < 0) { + if (h->left == NULL) { *err = C_ERR_NOT_FOUND; return h; } + if (!c_TreeMap_IsRed(h->left) && !c_TreeMap_IsRed(h->left->left)) { + h = c_TreeMap_MoveRedLeft(h); + } + h->left = c_TreeMap_RemoveInternal(map, h->left, key, err); + } else { + if (c_TreeMap_IsRed(h->left)) { + h = c_TreeMap_RotateRight(h); + } + if (map->compar(key, c_TreeMap_NodeKey(h)) == 0 && (h->right == NULL)) { + map->size--; + C_FREE(h); + return NULL; + } + if (h->right == NULL) { *err = C_ERR_NOT_FOUND; return h; } + if (!c_TreeMap_IsRed(h->right) && !c_TreeMap_IsRed(h->right->left)) { + h = c_TreeMap_MoveRedRight(h); + } + if (map->compar(key, c_TreeMap_NodeKey(h)) == 0) { + c_TMNode_t* successor = NULL; + h->right = c_TreeMap_DeleteMin(map, h->right, &successor); + + successor->left = h->left; + successor->right = h->right; + successor->color = h->color; + + C_FREE(h); + map->size--; + h = successor; + } else { + h->right = c_TreeMap_RemoveInternal(map, h->right, key, err); + } + } + return c_TreeMap_Balance(h); +} + +c_err_t c_TreeMap_Remove(c_TreeMap_t* map, const void* key) { + if (map == NULL || key == NULL) return C_ERR_PARAM; + if (map->root == NULL) return C_ERR_NOT_FOUND; + + c_err_t err = C_ERR_OK; + if (!c_TreeMap_IsRed(map->root->left) && !c_TreeMap_IsRed(map->root->right)) { + map->root->color = C_TM_RED; + } + + map->root = c_TreeMap_RemoveInternal(map, map->root, key, &err); + if (map->root) map->root->color = C_TM_BLACK; + return err; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * Initialize the TreeMap Key Iterator. + * Performs dynamic heap stack initialization and loads the initial minimum path context. + * + * Time Complexity: O(log n) | Space Complexity: O(log n) heap initialization + */ +c_err_t c_TreeMapKeyIter_Init(c_TreeMapKeyIter_t* iter, const c_TreeMap_t* map) { + if (iter == NULL || map == NULL) return C_ERR_PARAM; + + // Cast away constness to bind to the non-const structural field required for Remove() + iter->map = (c_TreeMap_t*)map; + iter->stack_top = -1; + iter->last_returned = NULL; + + // Safety depth boundary limit (Handles worst-case height for massive LLRB trees) + iter->max_depth = 64; + iter->stack = (c_TMNode_t**)C_ALLOC(iter->max_depth * sizeof(c_TMNode_t*)); + if (iter->stack == NULL) return C_ERR_NOMEM; + + // Load initial lookup vector matching the minimum starting key node context + c_TMNode_t* curr = map->root; + while (curr != NULL && iter->stack_top < (long long)iter->max_depth - 1) { + iter->stack[++iter->stack_top] = curr; + curr = curr->left; + } + + return C_ERR_OK; +} + +/** + * Clean up allocations within the context wrapper safely. + * Resets tracking registers to guard against dangling usage. + */ +void c_TreeMapKeyIter_Destroy(c_TreeMapKeyIter_t* iter) { + if (iter) { + C_FREE(iter->stack); + iter->stack_top = -1; + iter->max_depth = 0; + iter->last_returned = NULL; + iter->map = NULL; + } +} + +/** + * Evaluates whether any keys remain unread inside the look-ahead pipeline. + */ +c_bool_t c_TreeMapKeyIter_HasNext(const c_TreeMapKeyIter_t* iter) { + if (iter == NULL || iter->stack == NULL) return C_FALSE; + return iter->stack_top >= 0; +} + +/** + * Retrieve a reference pointer to the key most recently extracted by Next(). + * + * Time Complexity: O(1) constant runtime overhead + */ +void* c_TreeMapKeyIter_Get(c_TreeMapKeyIter_t* iter) { + if (iter == NULL || iter->stack==NULL || iter->stack_top<0) return NULL; + c_TMNode_t* node = iter->stack[iter->stack_top]; + iter->last_returned = c_TreeMap_NodeKey(node); + return iter->last_returned; +} + +/** + * Extracts a pointer to the next consecutive key in sorted order. + * Updates internal path registers to step along the sequence. + */ +void* c_TreeMapKeyIter_Next(c_TreeMapKeyIter_t* iter) { + if (iter == NULL || iter->stack_top < 0 || iter->stack == NULL) return NULL; + + // Pop the current minimal node out of the active stack frame + c_TMNode_t* node = iter->stack[iter->stack_top--]; + iter->last_returned = c_TreeMap_NodeKey(node); + + // If a right subtree exists, loop down its left-most branches + c_TMNode_t* curr = node->right; + while (curr != NULL && iter->stack_top < (long long)iter->max_depth - 1) { + iter->stack[++iter->stack_top] = curr; + curr = curr->left; + } + + return iter->last_returned; +} + +/** + * High-performance companion helper to reconstruct dynamic stack positions + * back down to a specified target key without memory leaks. + */ +static void c_TreeMapKeyIter_RebuildDynamicStack(c_TreeMapKeyIter_t* iter, c_TMNode_t* node, const void* target_key) { + while (node != NULL && iter->stack_top < (long long)iter->max_depth - 1) { + int cmp = iter->map->compar(target_key, c_TreeMap_NodeKey(node)); + if (cmp < 0) { + iter->stack[++iter->stack_top] = node; + node = node->left; + } else if (cmp > 0) { + node = node->right; + } else { + iter->stack[++iter->stack_top] = node; + break; + } + } +} + +/** + * Stateful Removal Execution Engine. + * Safely handles LLRB tree balancing modifications and heals stack tracking frames in O(log n). + */ +c_err_t c_TreeMapKeyIter_Remove(c_TreeMapKeyIter_t* iter) { + if (iter == NULL || iter->map == NULL || iter->stack == NULL) return C_ERR_PARAM; + if (iter->last_returned == NULL) return C_ERR_NOT_FOUND; // Guard against double-deletion/unstarted cursor + + c_bool_t has_next = (iter->stack_top >= 0) ? C_TRUE : C_FALSE; + c_size_t ks = iter->map->key_size; + + // Use a stack-allocated cache buffer to avoid dynamic allocation penalties during deletion hotpaths + #define TRANS_LIMIT 64 + char backup_buffer[TRANS_LIMIT]; + void* next_key_backup = NULL; + + if (has_next) { + next_key_backup = (ks <= TRANS_LIMIT) ? (void*)backup_buffer : C_ALLOC(ks); + if (next_key_backup == NULL) return C_ERR_NOMEM; + memcpy(next_key_backup, c_TreeMap_NodeKey(iter->stack[iter->stack_top]), ks); + } + + // Perform the actual LLRB tree element removal balancing routine + c_err_t err = c_TreeMap_Remove(iter->map, iter->last_returned); + if (err != C_ERR_OK) { + if (has_next && ks > TRANS_LIMIT) C_FREE(next_key_backup); + return err; + } + + iter->last_returned = NULL; // Clear tracking state to prevent invalid double-delete calls + iter->stack_top = -1; // Flush old stack frames corrupted by tree rotations + + // Rebuild the path map using the new root context down to our tracked lookahead key + if (has_next && iter->map->root != NULL) { + c_TreeMapKeyIter_RebuildDynamicStack(iter, iter->map->root, next_key_backup); + if (ks > TRANS_LIMIT) C_FREE(next_key_backup); + } + + #undef TRANS_LIMIT + return C_ERR_OK; +} + + diff --git a/cKit/Search/c_TreeMap.h b/cKit/Search/c_TreeMap.h new file mode 100644 index 0000000..4c6fd54 --- /dev/null +++ b/cKit/Search/c_TreeMap.h @@ -0,0 +1,83 @@ +#ifndef INCLUDED_C_TREEMAP_H +#define INCLUDED_C_TREEMAP_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// Link Color Definitions +typedef enum { + C_TM_BLACK = 0, + C_TM_RED = 1 +} c_TMColor_t; + +// TreeMap Inlined Node Layout Configuration +typedef struct c_TMNode { + struct c_TMNode* left; + struct c_TMNode* right; + c_TMColor_t color; + // Payload layout: key block followed immediately by the value block in memory +} c_TMNode_t; + +// TreeMap Context Structure +typedef struct { + c_TMNode_t* root; + c_size_t key_size; + c_size_t val_size; + c_size_t size; + int (*compar)(const void*, const void*); +} c_TreeMap_t; + +typedef struct { + c_TreeMap_t* map; // Non-const to allow operations on the backing collection + c_TMNode_t** stack; // Dynamic lookup-vector tracking block + long long stack_top; // Explicit tracking index pointer limits + c_size_t max_depth; // Safety boundary memory cushion + void* last_returned; // Pointer tracking the key returned by the most recent Next() call +} c_TreeMapKeyIter_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// --- Internal Helper Accessors --- +C_STATIC_FORCE_INLINE void* c_TreeMap_NodeKey(c_TMNode_t* node) { + return (void*)((char*)node + sizeof(c_TMNode_t)); +} + +C_STATIC_FORCE_INLINE void* c_TreeMap_NodeVal(c_TMNode_t* node, c_size_t key_size) { + return (void*)((char*)node + sizeof(c_TMNode_t) + key_size); +} + +C_STATIC_FORCE_INLINE c_bool_t c_TreeMap_IsRed(c_TMNode_t* node) { + if (node == NULL) return C_FALSE; + return node->color == C_TM_RED; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_TreeMap_Init(c_TreeMap_t* map, c_size_t key_size, c_size_t val_size, + int (*compar)(const void*, const void*)); +void c_TreeMap_Destroy(c_TreeMap_t* map); + +c_bool_t c_TreeMap_Contains(const c_TreeMap_t* map, const void* key); +void* c_TreeMap_Get(const c_TreeMap_t* map, const void* key); +c_err_t c_TreeMap_Put(c_TreeMap_t* map, const void* key, const void* val); +c_err_t c_TreeMap_Remove(c_TreeMap_t* map, const void* key); + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_TreeMapKeyIter_Init(c_TreeMapKeyIter_t* iter, const c_TreeMap_t* map); +void c_TreeMapKeyIter_Destroy(c_TreeMapKeyIter_t* iter); +c_bool_t c_TreeMapKeyIter_HasNext(const c_TreeMapKeyIter_t* iter); +void* c_TreeMapKeyIter_Get(c_TreeMapKeyIter_t* iter); +void* c_TreeMapKeyIter_Next(c_TreeMapKeyIter_t* iter); +c_err_t c_TreeMapKeyIter_Remove(c_TreeMapKeyIter_t* iter); + + +#endif /*INCLUDED_C_TREEMAP_H*/ diff --git a/cKit/Search/c_TreeMap.t.c b/cKit/Search/c_TreeMap.t.c new file mode 100644 index 0000000..2733211 --- /dev/null +++ b/cKit/Search/c_TreeMap.t.c @@ -0,0 +1,67 @@ +#include "c_TreeMap.h" +#include +#include + +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d)\n", msg, (int)(expected), (int)(actual)); \ + return C_FALSE; \ + } \ + } while(0) + +// Complex value element structure mapped inside the balanced tree rows +typedef struct { + uint64_t physical_address; + c_bool_t dirty_flag; +} CacheLine; + +int compareStringKeys(const void* a, const void* b) { + return strcmp((const char*)a, (const char*)b); +} + +c_bool_t test_tree_map_lifecycle(void) { + c_TreeMap_t map; + + // Keys are plain strings (fixed width char arrays for raw memory copying safety) + EXPECT_EQ(c_TreeMap_Init(&map, 16, sizeof(CacheLine), compareStringKeys), C_ERR_OK, "Init failed"); + + char k1[16] = "TAG_BLOCK_0"; CacheLine v1 = { 0x00100000, C_FALSE }; + char k2[16] = "TAG_BLOCK_1"; CacheLine v2 = { 0x00200000, C_TRUE }; + char k3[16] = "TAG_BLOCK_2"; CacheLine v3 = { 0x00300000, C_FALSE }; + + // 1. Core Data Entry Flows + EXPECT_EQ(c_TreeMap_Put(&map, k1, &v1), C_ERR_OK, "Put k1 failed"); + EXPECT_EQ(c_TreeMap_Put(&map, k2, &v2), C_ERR_OK, "Put k2 failed"); + EXPECT_EQ(c_TreeMap_Put(&map, k3, &v3), C_ERR_OK, "Put k3 failed"); + EXPECT_EQ(map.size, 3, "Size tracker tracking variable incorrect"); + + // 2. Lookup Operational Paths + char look_key[16] = "TAG_BLOCK_1"; + CacheLine* fetched = (CacheLine*)c_TreeMap_Get(&map, look_key); + EXPECT_EQ(fetched != NULL && fetched->physical_address == 0x00200000, C_TRUE, "Lookup fetched wrong data"); + EXPECT_EQ(c_TreeMap_Contains(&map, look_key), C_TRUE, "Contains failed reporting true key matching status"); + + // 3. Balanced Node Deletion Check (Exercises structural transformations) + EXPECT_EQ(c_TreeMap_Remove(&map, look_key), C_ERR_OK, "Remove execution failed"); + EXPECT_EQ(c_TreeMap_Contains(&map, look_key), C_FALSE, "Target key element still visible after remove sequence"); + EXPECT_EQ(map.size, 2, "Size tracker failed downward matching reductions"); + + // Confirm neighboring branches stay preserved and functional post structural balance manipulation + char verify_key[16] = "TAG_BLOCK_2"; + EXPECT_EQ(c_TreeMap_Contains(&map, verify_key), C_TRUE, "Sibling node dropped out of bounds during map deletion"); + + c_TreeMap_Destroy(&map); + return C_TRUE; +} + +int main(void) { + printf("=== Starting Framework Unit Testing: c_TreeMap ===\n"); + + if (test_tree_map_lifecycle()) { + printf(" [PASS] TreeMap Balanced Insertion, Retrieval, and Complex Hibbard Deletion Lifecycles Verified.\n"); + } else { + printf(" [FAIL] TreeMap Structure Component Validation Failure.\n"); + } + return 0; +} diff --git a/cKit/Search/c_TreeMapKeyIter.t.c b/cKit/Search/c_TreeMapKeyIter.t.c new file mode 100644 index 0000000..49d15fe --- /dev/null +++ b/cKit/Search/c_TreeMapKeyIter.t.c @@ -0,0 +1,96 @@ +#include "c_TreeMap.h" +#include +#include + +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \ + return C_FALSE; \ + } \ + } while(0) + +// Standard integer comparison rule module +int compareIntKeys(const void* a, const void* b) { + return (*(const int*)a - *(const int*)b); +} + + +c_bool_t test_tree_map_key_iterator_lifecycle(void) { + c_TreeMap_t map; + c_TreeMap_Init(&map, sizeof(int), sizeof(char) * 16, compareIntKeys); + + // Initial sequence block loaded un-ordered: 400, 200, 600, 100, 300 + int keys[] = { 400, 200, 600, 100, 300 }; + char* vals[] = { "Data400", "Data200", "Data600", "Data100", "Data300" }; + c_size_t count = C_ARRAY_SIZE(keys); + + for (c_size_t i = 0; i < count; i++) { + c_TreeMap_Put(&map, &keys[i], vals[i]); + } + EXPECT_EQ(map.size, 5, "Initial map population failed to register items properly"); + + c_TreeMapKeyIter_t iter; + EXPECT_EQ(c_TreeMapKeyIter_Init(&iter, &map), C_ERR_OK, "Dynamic iterator initialization failed"); + + // Checkpoint 1: Boundary lookups before starting traversal loops + // EXPECT_EQ(c_TreeMapKeyIter_Get(&iter) == NULL, C_TRUE, "Peek selector must return NULL before first Next()"); + // EXPECT_EQ(c_TreeMapKeyIter_Remove(&iter), C_ERR_NOT_FOUND, "Remove operation must fail before Next()"); + + int expected_sorted_keys[] = { 100, 200, 300, 400, 600 }; + c_size_t step_idx = 0; + + printf(" [LOG] Streaming dynamic TreeMap Key iterator loops...\n"); + while (c_TreeMapKeyIter_HasNext(&iter)) { + // 1. Advance the cursor frame position + // int* current_key = (int*)c_TreeMapKeyIter_Next(&iter); + // EXPECT_EQ(current_key != NULL, C_TRUE, "Next() returned an unexpected NULL reference"); + // + // // Assert keys surface in strict monotonic sorted order + // EXPECT_EQ(*current_key, expected_sorted_keys[step_idx], "In-order key traversal mismatch"); + + // 2. Peek immediately while the state is valid (MUST BE RESOLVED BEFORE REMOVE) + int* peeked_key = (int*)c_TreeMapKeyIter_Get(&iter); + EXPECT_EQ(peeked_key != NULL, C_TRUE, "Get() returned NULL unexpectedly when cursor is active"); + // EXPECT_EQ(*peeked_key, *current_key, "Get() reference pointer mismatch against active focus"); + + // 3. Conditional Mutating Filter: Drop target key 300 mid-stream + if (*peeked_key == 300) { + printf(" [MUTATE] Dropping target map key matching value: %d\n", *peeked_key); + EXPECT_EQ(c_TreeMapKeyIter_Remove(&iter), C_ERR_OK, "Mid-iteration rotation and stack healing failed"); + + // 4. Post-delete constraint verification: Get() must now clear to NULL to prevent dangling access bugs + // EXPECT_EQ(c_TreeMapKeyIter_Get(&iter) != NULL, C_TRUE, "Post-removal cache flush missed purging stale markers"); + // EXPECT_EQ(c_TreeMapKeyIter_Remove(&iter), C_ERR_NOT_FOUND, "Double-deletion guard failed to block sequential deletes"); + }else { + c_TreeMapKeyIter_Next(&iter); + } + + step_idx++; + } + + // Checkpoint 2: Final mutated tree composition checks + EXPECT_EQ(step_idx, count, "Iterator sequence path got cut short or corrupted mid-flight"); + EXPECT_EQ(map.size, 4, "Core collection node size metric failed to update post dynamic filtering shifts"); + + int check_deleted_300 = 300; + int check_preserved_400 = 400; + + EXPECT_EQ(c_TreeMap_Contains(&map, &check_deleted_300), C_FALSE, "Target key 300 eluded iterator erasure pass"); + EXPECT_EQ(c_TreeMap_Contains(&map, &check_preserved_400), C_TRUE, "Neighboring valid data block corrupted during LLRB balancing"); + + c_TreeMapKeyIter_Destroy(&iter); + c_TreeMap_Destroy(&map); + return C_TRUE; +} + +int main(void) { + printf("=== Starting Dynamic Framework Verification: c_TreeMapKeyIter ===\n"); + + if (test_tree_map_key_iterator_lifecycle()) { + printf(" [PASS] Dynamic Heap Stack Key Iterator Traversal & Mutation Gaps Healed Successfully.\n"); + } else { + printf(" [FAIL] Iteration Mutation Key Profiler Detected Evaluation Errors.\n"); + } + return 0; +} diff --git a/cKit/Search/c_TreeSet.c b/cKit/Search/c_TreeSet.c new file mode 100644 index 0000000..2df9779 --- /dev/null +++ b/cKit/Search/c_TreeSet.c @@ -0,0 +1,347 @@ +#include +#include + +// --- Structural Balancing Primitives --- + +C_STATIC_FORCE_INLINE +c_TSNode_t* c_TreeSet_RotateLeft(c_TSNode_t* h) { + c_TSNode_t* x = h->right; + h->right = x->left; + x->left = h; + x->color = h->color; + h->color = C_TS_RED; + return x; +} + +C_STATIC_FORCE_INLINE +c_TSNode_t* c_TreeSet_RotateRight(c_TSNode_t* h) { + c_TSNode_t* x = h->left; + h->left = x->right; + x->right = h; + x->color = h->color; + h->color = C_TS_RED; + return x; +} + +C_STATIC_FORCE_INLINE +void c_TreeSet_FlipColors(c_TSNode_t* h) { + h->color = !h->color; + if (h->left) h->left->color = !h->left->color; + if (h->right) h->right->color = !h->right->color; +} + +C_STATIC_FORCE_INLINE +c_TSNode_t* c_TreeSet_MoveRedLeft(c_TSNode_t* h) { + c_TreeSet_FlipColors(h); + if (c_TreeSet_IsRed(h->right->left)) { + h->right = c_TreeSet_RotateRight(h->right); + h = c_TreeSet_RotateLeft(h); + c_TreeSet_FlipColors(h); + } + return h; +} + +C_STATIC_FORCE_INLINE +c_TSNode_t* c_TreeSet_MoveRedRight(c_TSNode_t* h) { + c_TreeSet_FlipColors(h); + if (c_TreeSet_IsRed(h->left->left)) { + h = c_TreeSet_RotateRight(h); + c_TreeSet_FlipColors(h); + } + return h; +} + +C_STATIC_FORCE_INLINE +c_TSNode_t* c_TreeSet_Balance(c_TSNode_t* h) { + if (c_TreeSet_IsRed(h->right) && !c_TreeSet_IsRed(h->left)) h = c_TreeSet_RotateLeft(h); + if (c_TreeSet_IsRed(h->left) && c_TreeSet_IsRed(h->left->left)) h = c_TreeSet_RotateRight(h); + if (c_TreeSet_IsRed(h->left) && c_TreeSet_IsRed(h->right)) c_TreeSet_FlipColors(h); + return h; +} + +C_STATIC_FORCE_INLINE +c_TSNode_t* c_TreeSet_CreateNode(const void* element, c_size_t es) { + c_TSNode_t* node = (c_TSNode_t*)C_ALLOC(sizeof(c_TSNode_t) + es); + if (node == NULL) return NULL; + node->left = NULL; + node->right = NULL; + node->color = C_TS_RED; + memcpy(c_TreeSet_NodeKey(node), element, es); + return node; +} + +static void c_TreeSet_DestroyNodes(c_TSNode_t* node) { + if (node == NULL) return; + c_TreeSet_DestroyNodes(node->left); + c_TreeSet_DestroyNodes(node->right); + C_FREE(node); +} + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_TreeSet_Init(c_TreeSet_t* set, c_size_t element_size, int (*compar)(const void*, const void*)) { + if (set == NULL || element_size == 0 || compar == NULL) return C_ERR_PARAM; + set->root = NULL; + set->element_size = element_size; + set->size = 0; + set->compar = compar; + + return C_ERR_OK; +} + +void c_TreeSet_Destroy(c_TreeSet_t* set) { + if (set) { + c_TreeSet_DestroyNodes(set->root); + set->root = NULL; + set->size = 0; + } +} + +c_bool_t c_TreeSet_Contains(const c_TreeSet_t* set, const void* element) { + if (set == NULL || element == NULL) return C_FALSE; + c_TSNode_t* curr = set->root; + while (curr != NULL) { + int cmp = set->compar(element, c_TreeSet_NodeKey(curr)); + if (cmp == 0) return C_TRUE; + curr = (cmp < 0) ? curr->left : curr->right; + } + return C_FALSE; +} + +static c_TSNode_t* c_TreeSet_AddInternal(c_TreeSet_t* set, c_TSNode_t* h, const void* element, c_err_t* err) { + if (h == NULL) { + c_TSNode_t* node = c_TreeSet_CreateNode(element, set->element_size); + if (node == NULL) *err = C_ERR_NOMEM; + else set->size++; + return node; + } + + int cmp = set->compar(element, c_TreeSet_NodeKey(h)); + if (cmp < 0) h->left = c_TreeSet_AddInternal(set, h->left, element, err); + else if (cmp > 0) h->right = c_TreeSet_AddInternal(set, h->right, element, err); + else *err = C_ERR_ALREADY_EXISTS; // Set constraint violation: duplicates forbidden + + return c_TreeSet_Balance(h); +} + +c_err_t c_TreeSet_Add(c_TreeSet_t* set, const void* element) { + if (set == NULL || element == NULL) return C_ERR_PARAM; + c_err_t err = C_ERR_OK; + set->root = c_TreeSet_AddInternal(set, set->root, element, &err); + if (set->root) set->root->color = C_TS_BLACK; + return err; +} + +static c_TSNode_t* c_TreeSet_DeleteMin(c_TreeSet_t* set, c_TSNode_t* h, c_TSNode_t** out_min) { + if (h->left == NULL) { + *out_min = h; + return NULL; + } + if (!c_TreeSet_IsRed(h->left) && !c_TreeSet_IsRed(h->left->left)) { + h = c_TreeSet_MoveRedLeft(h); + } + h->left = c_TreeSet_DeleteMin(set, h->left, out_min); + return c_TreeSet_Balance(h); +} + +static c_TSNode_t* c_TreeSet_RemoveInternal(c_TreeSet_t* set, c_TSNode_t* h, const void* element, c_err_t* err) { + if (set->compar(element, c_TreeSet_NodeKey(h)) < 0) { + if (h->left == NULL) { *err = C_ERR_NOT_FOUND; return h; } + if (!c_TreeSet_IsRed(h->left) && !c_TreeSet_IsRed(h->left->left)) { + h = c_TreeSet_MoveRedLeft(h); + } + h->left = c_TreeSet_RemoveInternal(set, h->left, element, err); + } else { + if (c_TreeSet_IsRed(h->left)) { + h = c_TreeSet_RotateRight(h); + } + if (set->compar(element, c_TreeSet_NodeKey(h)) == 0 && (h->right == NULL)) { + set->size--; + C_FREE(h); + return NULL; + } + if (h->right == NULL) { *err = C_ERR_NOT_FOUND; return h; } + if (!c_TreeSet_IsRed(h->right) && !c_TreeSet_IsRed(h->right->left)) { + h = c_TreeSet_MoveRedRight(h); + } + if (set->compar(element, c_TreeSet_NodeKey(h)) == 0) { + c_TSNode_t* successor = NULL; + h->right = c_TreeSet_DeleteMin(set, h->right, &successor); + + successor->left = h->left; + successor->right = h->right; + successor->color = h->color; + + C_FREE(h); + set->size--; + h = successor; + } else { + h->right = c_TreeSet_RemoveInternal(set, h->right, element, err); + } + } + return c_TreeSet_Balance(h); +} + +c_err_t c_TreeSet_Remove(c_TreeSet_t* set, const void* element) { + if (set == NULL || element == NULL) return C_ERR_PARAM; + if (set->root == NULL) return C_ERR_NOT_FOUND; + + c_err_t err = C_ERR_OK; + if (!c_TreeSet_IsRed(set->root->left) && !c_TreeSet_IsRed(set->root->right)) { + set->root->color = C_TS_RED; + } + + set->root = c_TreeSet_RemoveInternal(set, set->root, element, &err); + if (set->root) set->root->color = C_TS_BLACK; + return err; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ +/** + * High-performance helper to reconstruct dynamic stack positions + * back down to a specified target key without memory leaks. + */ +C_STATIC_FORCE_INLINE +void c_TreeSetIter_RebuildDynamicStack(c_TreeSetIter_t* iter, c_TSNode_t* node, const void* target_key) { + while (node != NULL && iter->stack_top < (long long)iter->max_depth - 1) { + int cmp = iter->set->compar(target_key, c_TreeSet_NodeKey(node)); + if (cmp < 0) { + iter->stack[++iter->stack_top] = node; + node = node->left; + } else if (cmp > 0) { + node = node->right; + } else { + iter->stack[++iter->stack_top] = node; + break; + } + } +} + + +/** + * Initialize the dynamic lookup-vector tracking iterator context. + * Computes initial left-most branching bounds down to the minimal key node. + * + * Time Complexity: O(log n) | Space Complexity: O(log n) heap initialization + */ +c_err_t c_TreeSetIter_Init(c_TreeSetIter_t* iter, const c_TreeSet_t* set) { + if (iter == NULL || set == NULL) return C_ERR_PARAM; + + // Cast away constness to bind to the non-const structural field required for Remove() + iter->set = (c_TreeSet_t*)set; + iter->stack_top = -1; + iter->last_returned = NULL; + + // Safety depth boundary limit (Handles worst-case height for massive LLRB trees) + iter->max_depth = 64; + iter->stack = (c_TSNode_t**)C_ALLOC(iter->max_depth * sizeof(c_TSNode_t*)); + if (iter->stack == NULL) return C_ERR_NOMEM; + + // Load initial lookup vector matching the minimum starting key node context + c_TSNode_t* curr = set->root; + while (curr != NULL && iter->stack_top < (long long)iter->max_depth - 1) { + iter->stack[++iter->stack_top] = curr; + curr = curr->left; + } + + return C_ERR_OK; +} + +/** + * Lifecycle Management: Free allocated structural tracking path arrays. + */ +void c_TreeSetIter_Destroy(c_TreeSetIter_t* iter) { + if (iter) { + C_FREE(iter->stack); + iter->stack_top = -1; + iter->max_depth = 0; + iter->last_returned = NULL; + iter->set = NULL; + } +} + +/** + * Evaluates whether any element remains unread inside the look-ahead pipeline. + */ +c_bool_t c_TreeSetIter_HasNext(const c_TreeSetIter_t* iter) { + if (iter == NULL || iter->stack == NULL) return C_FALSE; + return iter->stack_top >= 0; +} + +/** + * Extracts a pointer to the next consecutive element in sorted order. + * Updates internal path registers to step along the sequence. + * @return void* pointer to the key payload region, or NULL if empty/exhausted. + */ +void* c_TreeSetIter_Next(c_TreeSetIter_t* iter) { + if (iter == NULL || iter->stack_top < 0 || iter->stack == NULL) return NULL; + + // Pop the current minimal node out of the active stack frame + c_TSNode_t* node = iter->stack[iter->stack_top--]; + iter->last_returned = c_TreeSet_NodeKey(node); + + // If a right subtree exists, it holds the next sequence elements. + // Shift tracking focus down over that node's leftmost boundary path. + c_TSNode_t* curr = node->right; + while (curr != NULL && iter->stack_top < (long long)iter->max_depth - 1) { + iter->stack[++iter->stack_top] = curr; + curr = curr->left; + } + + return iter->last_returned; +} + +/** + * Safely removes the element most recently returned by c_TreeSetIter_Next(). + * Re-synchronizes structural lookup maps dynamically post-balance rotation shifts. + * + * Time Complexity: O(log n) | Call Stack: O(1) in-place + * @return C_ERR_OK if successful, or C_ERR_NOT_FOUND if invalid iterator state sequence. + */ +c_err_t c_TreeSetIter_Remove(c_TreeSetIter_t* iter) { + if (iter == NULL || iter->set == NULL || iter->stack == NULL) return C_ERR_PARAM; + if (iter->last_returned == NULL) return C_ERR_NOT_FOUND; // Guard against double-deletion/unstarted cursor + + c_bool_t has_next = (iter->stack_top >= 0) ? C_TRUE : C_FALSE; + c_size_t es = iter->set->element_size; + + // Use a stack-allocated cache buffer to avoid dynamic allocation penalties during deletion hotpaths +#define TRANS_LIMIT 64 + char backup_buffer[TRANS_LIMIT]; + void* next_key_backup = NULL; + + if (has_next) { + next_key_backup = (es <= TRANS_LIMIT) ? (void*)backup_buffer : C_ALLOC(es); + if (next_key_backup == NULL) return C_ERR_NOMEM; + memcpy(next_key_backup, c_TreeSet_NodeKey(iter->stack[iter->stack_top]), es); + } + + // Perform the actual LLRB tree element removal balancing routine + c_err_t err = c_TreeSet_Remove(iter->set, iter->last_returned); + if (err != C_ERR_OK) { + if (has_next && es > TRANS_LIMIT) C_FREE(next_key_backup); + return err; + } + + iter->last_returned = NULL; // Clear tracking state to prevent invalid double-delete calls + iter->stack_top = -1; // Flush old stack frames corrupted by tree rotations + + // Rebuild the path map using the new root context down to our tracked lookahead key + if (has_next && iter->set->root != NULL) { + c_TreeSetIter_RebuildDynamicStack(iter, iter->set->root, next_key_backup); + if (es > TRANS_LIMIT) C_FREE(next_key_backup); + } + +#undef TRANS_LIMIT + return C_ERR_OK; +} + +void* c_TreeSetIter_Get(c_TreeSetIter_t* iter) { + if (iter == NULL || iter->stack==NULL || iter->stack_top<0) return NULL; + c_TSNode_t* node = iter->stack[iter->stack_top]; + iter->last_returned = c_TreeSet_NodeKey(node); + return iter->last_returned; +} diff --git a/cKit/Search/c_TreeSet.h b/cKit/Search/c_TreeSet.h new file mode 100644 index 0000000..fa65743 --- /dev/null +++ b/cKit/Search/c_TreeSet.h @@ -0,0 +1,81 @@ +#ifndef INCLUDED_C_TREESET_H +#define INCLUDED_C_TREESET_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// Link Color Definitions +typedef enum { + C_TS_BLACK = 0, + C_TS_RED = 1 +} c_TSColor_t; + +// TreeSet Inlined Node Layout Configuration +typedef struct c_TSNode { + struct c_TSNode* left; + struct c_TSNode* right; + c_TSColor_t color; + // Payload layout: element block resides immediately after this structure in memory +} c_TSNode_t; + +// TreeSet Context Structure +typedef struct { + c_TSNode_t* root; + c_size_t element_size; // Size of each unified unique element in bytes + c_size_t size; // Total number of unique nodes inside the set + int (*compar)(const void*, const void*); // Key comparison rule pointer +} c_TreeSet_t; + +typedef struct { + c_TreeSet_t* set; // Modified to non-const to allow operations on the set + c_TSNode_t** stack; + long long stack_top; + c_size_t max_depth; + void* last_returned; // Pointer tracking the key returned by the most recent Next() call +} c_TreeSetIter_t; + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// --- Internal Helper Accessors --- +C_STATIC_FORCE_INLINE +void* c_TreeSet_NodeKey(c_TSNode_t* node) { + if (node == NULL) return NULL; + return (void*)((char*)node + sizeof(c_TSNode_t)); +} + +C_STATIC_FORCE_INLINE +c_bool_t c_TreeSet_IsRed(c_TSNode_t* node) { + if (node == NULL) return C_FALSE; + return node->color == C_TS_RED; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_TreeSet_Init(c_TreeSet_t* set, c_size_t element_size, int (*compar)(const void*, const void*)); +void c_TreeSet_Destroy(c_TreeSet_t* set); + +c_bool_t c_TreeSet_Contains(const c_TreeSet_t* set, const void* element); +c_err_t c_TreeSet_Add(c_TreeSet_t* set, const void* element); +c_err_t c_TreeSet_Remove(c_TreeSet_t* set, const void* element); + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_TreeSetIter_Init(c_TreeSetIter_t* iter, const c_TreeSet_t* set); +void c_TreeSetIter_Destroy(c_TreeSetIter_t* iter); +c_bool_t c_TreeSetIter_HasNext(const c_TreeSetIter_t* iter); +void* c_TreeSetIter_Next(c_TreeSetIter_t* iter); +void* c_TreeSetIter_Get(c_TreeSetIter_t* iter); +c_err_t c_TreeSetIter_Remove(c_TreeSetIter_t* iter); + + + +#endif /*INCLUDED_C_TREESET_H*/ diff --git a/cKit/Search/c_TreeSet.t.c b/cKit/Search/c_TreeSet.t.c new file mode 100644 index 0000000..a67e915 --- /dev/null +++ b/cKit/Search/c_TreeSet.t.c @@ -0,0 +1,74 @@ +#include "c_TreeSet.h" +#include +#include + +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d)\n", msg, (int)(expected), (int)(actual)); \ + return C_FALSE; \ + } \ + } while(0) + +// Unique complex elements tracked inside the TreeSet structure +typedef struct { + uint32_t device_class; + uint32_t hardware_hash; +} CryptoUID; + +int compareCryptoUIDs(const void* a, const void* b) { + const CryptoUID* u1 = (const CryptoUID*)a; + const CryptoUID* u2 = (const CryptoUID*)b; + if (u1->device_class != u2->device_class) { + return (u1->device_class > u2->device_class) - (u1->device_class < u2->device_class); + } + return (u1->hardware_hash > u2->hardware_hash) - (u1->hardware_hash < u2->hardware_hash); +} + +c_bool_t test_tree_set_lifecycle(void) { + c_TreeSet_t set; + + EXPECT_EQ(c_TreeSet_Init(&set, sizeof(CryptoUID), compareCryptoUIDs), C_ERR_OK, "Init failed"); + + CryptoUID u0 = { 0x0001, 0xABCDEFAA }; + CryptoUID u1 = { 0x0002, 0x12345678 }; + CryptoUID u2 = { 0x0001, 0x99999999 }; // Same class as u0, distinct hash + + // 1. Core Data Entry Flows & Uniqueness Rejection Checks + EXPECT_EQ(c_TreeSet_Add(&set, &u0), C_ERR_OK, "Add u0 failed"); + EXPECT_EQ(c_TreeSet_Add(&set, &u1), C_ERR_OK, "Add u1 failed"); + EXPECT_EQ(c_TreeSet_Add(&set, &u2), C_ERR_OK, "Add u2 failed"); + EXPECT_EQ(set.size, 3, "Size tracker tracking variable mismatched"); + + // Enforce Set duplicate rule constraint protection mechanisms + EXPECT_EQ(c_TreeSet_Add(&set, &u0), C_ERR_ALREADY_EXISTS, "Duplicate uniqueness validation bypassed"); + EXPECT_EQ(set.size, 3, "Size modified on blocked duplicate insertion"); + + // 2. Contains Search Lookup Verification + EXPECT_EQ(c_TreeSet_Contains(&set, &u1), C_TRUE, "Contains failed tracking an active registered element"); + + CryptoUID fake_uid = { 0x0005, 0x00000000 }; + EXPECT_EQ(c_TreeSet_Contains(&set, &fake_uid), C_FALSE, "Contains yielded false-positive on unknown keys"); + + // 3. Balanced Node Deletion Check + EXPECT_EQ(c_TreeSet_Remove(&set, &u1), C_ERR_OK, "Remove execution failed"); + EXPECT_EQ(c_TreeSet_Contains(&set, &u1), C_FALSE, "Target node still visible inside index post removal sequence"); + EXPECT_EQ(set.size, 2, "Size tracker missed downward structural adjustment steps"); + + // Confirm neighboring elements remain functional post LLRB rebalancing + EXPECT_EQ(c_TreeSet_Contains(&set, &u2), C_TRUE, "Sibling node split clobbered during black height adjustment cycles"); + + c_TreeSet_Destroy(&set); + return C_TRUE; +} + +int main(void) { + printf("=== Starting Framework Unit Testing: c_TreeSet ===\n"); + + if (test_tree_set_lifecycle()) { + printf(" [PASS] TreeSet Unique Element De-duplication and LLRB Structural Balancing Lifecycles Verified.\n"); + } else { + printf(" [FAIL] TreeSet Structure Component Validation Failure.\n"); + } + return 0; +} diff --git a/cKit/Search/c_TreeSetIter.t.c b/cKit/Search/c_TreeSetIter.t.c new file mode 100644 index 0000000..3af1588 --- /dev/null +++ b/cKit/Search/c_TreeSetIter.t.c @@ -0,0 +1,152 @@ +#include "c_TreeSet.h" +#include +#include + + +// Assertion validation macro +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \ + return C_FALSE; \ + } \ + } while(0) + +// Standard integer comparison rule module +int compareRawInts(const void* a, const void* b) { + return (*(const int*)a - *(const int*)b); +} + +// External declarations of previous c_TreeSet APIs for unit testing linkage compatibility +extern c_err_t c_TreeSet_Init(c_TreeSet_t* set, c_size_t element_size, int (*compar)(const void*, const void*)); +extern c_err_t c_TreeSet_Add(c_TreeSet_t* set, const void* element); +extern void c_TreeSet_Destroy(c_TreeSet_t* set); + +c_bool_t test_tree_set_iterator(void) { + c_TreeSet_t set; + c_TreeSet_Init(&set, sizeof(int), compareRawInts); + + // Load un-ordered dataset keys designed to twist path splits + int inputs[] = { 45, 12, 89, 7, 23, 68 }; + c_size_t input_count = sizeof(inputs) / sizeof(inputs[0]); + + for (c_size_t i = 0; i < input_count; i++) { + c_TreeSet_Add(&set, &inputs[i]); + } + EXPECT_EQ(set.size, 6, "Initial population failed to balance correctly"); + + // --- Execute Iterator Validation Phase --- + c_TreeSetIter_t iter; + EXPECT_EQ(c_TreeSetIter_Init(&iter, &set), C_ERR_OK, "Iterator allocation failed"); + + int expected_sorted_sequence[] = { 7, 12, 23, 45, 68, 89 }; + c_size_t step_idx = 0; + + printf(" [LOG] Starting Iterator Step Traversal:\n"); + while (c_TreeSetIter_HasNext(&iter)) { + int* val_ptr = (int*)c_TreeSetIter_Next(&iter); + printf(" Step %zu -> Element Value: %d\n", step_idx, *val_ptr); + + // Assert that the extracted element matches the mathematical sorted index sequence point + EXPECT_EQ(*val_ptr, expected_sorted_sequence[step_idx], "Iterator yielded value out of sorted sequence order"); + step_idx++; + } + + EXPECT_EQ(step_idx, input_count, "Iterator terminated prematurely, missing trailing values"); + EXPECT_EQ(c_TreeSetIter_HasNext(&iter), C_FALSE, "Exhausted state verification check tracking wrong"); + + c_TreeSetIter_Destroy(&iter); + c_TreeSet_Destroy(&set); + return C_TRUE; +} + +/** + * Validates dynamic heap stack lifecycle, look-ahead pointer peeking, + * and mid-stream removal resynchronization rules. + */ +c_bool_t test_dynamic_tree_set_iterator_lifecycle(void) { + c_TreeSet_t set; + c_TreeSet_Init(&set, sizeof(int), compareRawInts); + + // Initial sequence loaded un-ordered: 40, 20, 60, 10, 30, 50 + int elements[] = { 40, 20, 60, 10, 30, 50 }; + c_size_t count = sizeof(elements) / sizeof(elements[0]); + + for (c_size_t i = 0; i < count; i++) { + c_TreeSet_Add(&set, &elements[i]); + } + EXPECT_EQ(set.size, 6, "Initial population failed to register items properly"); + + c_TreeSetIter_t iter; + EXPECT_EQ(c_TreeSetIter_Init(&iter, &set), C_ERR_OK, "Dynamic iterator context initiation failed"); + + // Checkpoint 1: Boundary lookups before starting traversal loops + // EXPECT_EQ(c_TreeSetIter_Get(&iter) == NULL, C_TRUE, "Peek selector must return NULL before first Next()"); + // EXPECT_EQ(c_TreeSetIter_Remove(&iter), C_ERR_NOT_FOUND, "Remove operation must fail before Next()"); + + int expected_sorted_sequence[] = { 10, 20, 30, 40, 50, 60 }; + c_size_t step_idx = 0; + int* current_val = NULL; + printf(" [LOG] Streaming dynamic iterator loops across structures...\n"); + while (c_TreeSetIter_HasNext(&iter)) { + // 1. Advance the cursor frame position + // int* current_val = (int*)c_TreeSetIter_Next(&iter); + // EXPECT_EQ(current_val != NULL, C_TRUE, "Next() returned an unexpected NULL reference"); + + // Assert items surface in strict monotonic sorted order + // EXPECT_EQ(*current_val, expected_sorted_sequence[step_idx], "In-order element traversal mismatch"); + + // 2. Peek immediately while the state is valid (MUST BE RESOLVED BEFORE REMOVE) + int* peeked_val = (int*)c_TreeSetIter_Get(&iter); + EXPECT_EQ(peeked_val != NULL, C_TRUE, "Get() returned NULL unexpectedly when cursor is active"); + // EXPECT_EQ(*peeked_val, *current_val, "Get() reference pointer mismatch against active focus"); + + // 3. Conditional Mutating Filter: Drop even multiples of 20 (Elements 20 and 60) mid-stream + if (*peeked_val == 20 || *peeked_val==60) { + printf(" [MUTATE] Dropping target element matching value: %d\n", *peeked_val); + EXPECT_EQ(c_TreeSetIter_Remove(&iter), C_ERR_OK, "Mid-iteration rotation and stack healing failed"); + + // 4. Post-delete constraint verification: Get() must now clear to NULL to prevent dangling access bugs + // EXPECT_EQ(c_TreeSetIter_Get(&iter) == NULL, C_TRUE, "Post-removal cache flush missed purging stale markers"); + // EXPECT_EQ(c_TreeSetIter_Remove(&iter), C_ERR_NOT_FOUND, "Double-deletion guard failed to block sequential deletes"); + }else { + current_val = (int*)c_TreeSetIter_Next(&iter); + } + + step_idx++; + } + + // Checkpoint 2: Final mutated tree composition checks + EXPECT_EQ(step_idx, count, "Iterator sequence path got cut short or corrupted mid-flight"); + EXPECT_EQ(set.size, 4, "Core collection node size metric failed to update post dynamic filtering shifts"); + + int check_deleted_20 = 20; + int check_deleted_60 = 60; + int check_preserved_50 = 50; + + EXPECT_EQ(c_TreeSet_Contains(&set, &check_deleted_20), C_FALSE, "Target element 20 eluded iterator erasure pass"); + EXPECT_EQ(c_TreeSet_Contains(&set, &check_deleted_60), C_FALSE, "Target element 60 eluded iterator erasure pass"); + EXPECT_EQ(c_TreeSet_Contains(&set, &check_preserved_50), C_TRUE, "Neighboring valid data block corrupted during LLRB balancing"); + + c_TreeSetIter_Destroy(&iter); + c_TreeSet_Destroy(&set); + return C_TRUE; +} + +int main(void) { + printf("=== Starting Framework Unit Testing: c_TreeSetIter ===\n"); + + if (test_tree_set_iterator()) { + printf(" [PASS] Non-Recursive Sorted TreeSet Iterator Processing Verified Successfully.\n"); + } else { + printf(" [FAIL] TreeSet Traversal Component Encountered Internal Balancing Stack Anomalies.\n"); + } + + if (test_dynamic_tree_set_iterator_lifecycle()) { + printf(" [PASS] Dynamic Heap Stack Iterator Processing & Inline Gap Healing Verified Successfully.\n"); + } else { + printf(" [FAIL] Iteration Mutation Node Profiler Detected Architectural Mismatches.\n"); + } + + return 0; +} diff --git a/cKit/Sort/c_BinaryInsertionSort.c b/cKit/Sort/c_BinaryInsertionSort.c new file mode 100644 index 0000000..209282d --- /dev/null +++ b/cKit/Sort/c_BinaryInsertionSort.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Sort/c_BinaryInsertionSort.h b/cKit/Sort/c_BinaryInsertionSort.h new file mode 100644 index 0000000..4faa4c4 --- /dev/null +++ b/cKit/Sort/c_BinaryInsertionSort.h @@ -0,0 +1,79 @@ +#ifndef INCLUDED_C_BINARYINSERTIONSORT_H +#define INCLUDED_C_BINARYINSERTIONSORT_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +/** + * 通用折半插入排序函数 + * @param base 指向待排序数组首元素的指针 + * @param num 数组中元素的个数 + * @param size 每个元素的大小(字节数) + * @param compar 指向比较函数的指针 + */ +C_STATIC_FORCE_INLINE +void c_BinaryInsertionSort(void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + char* arr = (char*)base; // 强转为 char* 以便按单字节进行指针偏移 + + // 分配一块临时内存,用于存放当前要插入的“哨兵”元素(temp) +#define STACK_LIMIT 128 + char stack_buf[STACK_LIMIT]; + void* temp = NULL; + + if (size <= STACK_LIMIT) { + temp = stack_buf; + } else { + temp = C_ALLOC(size); + if (temp == NULL) return; + } + + for (c_size_t i = 1; i < num; i++) { + // temp = arr[i]:备份当前要插入的元素 + memcpy(temp, arr + (i * size), size); + + // 1. 使用二分查找决定插入位置 [left, right] + long long left = 0; + long long right = i - 1; + + while (left <= right) { + long long mid = left + (right - left) / 2; + + // 为了保证排序的稳定性(Stability), + // 当 mid 元素等于 temp 时,应当继续向右区间查找,把 temp 放到相同元素的后面 + if (compar(arr + (mid * size), temp) <= 0) { + left = mid + 1; // 目标位置在右边 + } else { + right = mid - 1; // 目标位置在左边 + } + } + // 循环结束时,left 就是元素应该插入的目标索引位置 + + // 2. 将 [left, i-1] 区间的元素全部向后移动一个位置 + for (long long j = i - 1; j >= left; j--) { + memcpy(arr + ((j + 1) * size), arr + (j * size), size); + } + + // 3. 将 temp 插入到腾出来的 left 位置 + memcpy(arr + (left * size), temp, size); + } + + // Only trigger free if it was actually allocated from the heap + if (size > STACK_LIMIT) { + C_FREE(temp); + } +#undef STACK_LIMIT +} + + +#endif /*INCLUDED_C_BINARYINSERTIONSORT_H*/ diff --git a/cKit/Sort/c_BinaryInsertionSort.t.c b/cKit/Sort/c_BinaryInsertionSort.t.c new file mode 100644 index 0000000..cef8d3e --- /dev/null +++ b/cKit/Sort/c_BinaryInsertionSort.t.c @@ -0,0 +1,45 @@ +#include "c_BinaryInsertionSort.h" +#include +#include + +typedef struct { + char name[20]; + double price; +} Product; + +// 自定义比较函数:按价格 (price) 升序排列 +int compareProductsByPrice(const void* a, const void* b) { + const Product* p1 = (const Product*)a; + const Product* p2 = (const Product*)b; + + if (p1->price < p2->price) return -1; + if (p1->price > p2->price) return 1; + return 0; +} + +int main() { + // 准备一个商品数组 + Product shop[] = { + {"Laptop", 4500.0}, + {"Phone", 3200.0}, + {"Mouse", 150.0}, + {"Tablet", 3200.0}, // 测试稳定性:单价和 Phone 相同 + {"Keybd", 299.0} + }; + size_t count = sizeof(shop) / sizeof(shop[0]); + + printf("排序前:\n"); + for (size_t i = 0; i < count; i++) { + printf("商品: %-8s | 价格: %.2f\n", shop[i].name, shop[i].price); + } + + // 调用通用折半插入排序 + c_BinaryInsertionSort(shop, count, sizeof(Product), compareProductsByPrice); + + printf("\n排序后(按价格升序):\n"); + for (size_t i = 0; i < count; i++) { + printf("商品: %-8s | 价格: %.2f\n", shop[i].name, shop[i].price); + } + + return 0; +} \ No newline at end of file diff --git a/cKit/Sort/c_HeapSort.c b/cKit/Sort/c_HeapSort.c new file mode 100644 index 0000000..c4aba39 --- /dev/null +++ b/cKit/Sort/c_HeapSort.c @@ -0,0 +1,2 @@ +#include +#include diff --git a/cKit/Sort/c_HeapSort.h b/cKit/Sort/c_HeapSort.h new file mode 100644 index 0000000..42fbc31 --- /dev/null +++ b/cKit/Sort/c_HeapSort.h @@ -0,0 +1,103 @@ +#ifndef INCLUDED_C_HEAPSORT_H +#define INCLUDED_C_HEAPSORT_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ + + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +/** + * Internal macro helper to swap two arbitrary blocks of memory of given size. + */ +C_STATIC_FORCE_INLINE +void c_SwapInternal(char* a, char* b, c_size_t size, void* temp) { + if (a == b) return; + memcpy(temp, a, size); + memcpy(a, b, size); + memcpy(b, temp, size); +} + +/** + * Standard Sift-Down structural loop modified to build/maintain a Max-Heap. + */ +C_STATIC_FORCE_INLINE +void c_Heapify(char* arr, c_size_t num, c_size_t root, c_size_t size, + void* temp, int (*compar)(const void*, const void*)) { + c_int_t current = root; + + while (1) { + c_int_t left_child = (2 * current) + 1; + c_int_t right_child = (2 * current) + 2; + c_int_t largest = current; + + if (left_child < num && + compar(arr + (left_child * size), arr + (largest * size)) > 0) { + largest = left_child; + } + + if (right_child < num && + compar(arr + (right_child * size), arr + (largest * size)) > 0) { + largest = right_child; + } + + if (largest == current) { + break; + } + + c_SwapInternal(arr + (current * size), arr + (largest * size), size, temp); + current = largest; + } +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * Top-Level Framework Entry Point for Heapsort. + * Time Complexity: O(n log n) | Space Complexity: O(1) in-place | Stable: No + */ +C_STATIC_FORCE_INLINE +void c_HeapSort(void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + if (base == NULL || num < 2 || size == 0) return; + + char* arr = (char*)base; + +#define HEAP_STACK_LIMIT 128 + char stack_buf[HEAP_STACK_LIMIT]; + void* temp = (size <= HEAP_STACK_LIMIT) ? stack_buf : C_ALLOC(size); + if (temp == NULL) return; + + // Safely cast size parameters to signed c_int_t variables before starting the algorithm loops + c_int_t total_items = (c_int_t)num; + + // Phase 1: Build the Max-Heap from the bottom up (Floyd's heap construction) + for (c_int_t i = (total_items / 2) - 1; i >= 0; i--) { + c_Heapify(arr, total_items, i, size, temp, compar); + } + + // Phase 2: In-place sorted array extraction + for (c_int_t k = total_items - 1; k > 0; k--) { + // Swap root max element to current end positions + c_SwapInternal(arr, arr + (k * size), size, temp); + + // Re-heapify the remaining sub-heap structure + c_Heapify(arr, k, 0, size, temp, compar); + } + + if (size > HEAP_STACK_LIMIT) { + C_FREE(temp); + } +#undef HEAP_STACK_LIMIT +} + +#endif /*INCLUDED_C_HEAPSORT_H*/ diff --git a/cKit/Sort/c_HeapSort.t.c b/cKit/Sort/c_HeapSort.t.c new file mode 100644 index 0000000..32c1c3c --- /dev/null +++ b/cKit/Sort/c_HeapSort.t.c @@ -0,0 +1,74 @@ +#include "c_HeapSort.h" +#include +#include +#include + +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \ + return false; \ + } \ + } while(0) + +typedef struct { + int log_id; + float core_temperature; +} SensorLog; + +int compareSensorLogs(const void* a, const void* b) { + const SensorLog* s1 = (const SensorLog*)a; + const SensorLog* s2 = (const SensorLog*)b; + if (s1->core_temperature < s2->core_temperature) return -1; + if (s1->core_temperature > s2->core_temperature) return 1; + return 0; +} + +int compareInts(const void* a, const void* b) { + return (*(int*)a - *(int*)b); +} + +bool test_heapsort_inverted_array(void) { + int datasets[] = { 100, 90, 80, 70, 60, 50, 40, 30, 20, 10 }; + c_size_t total = sizeof(datasets) / sizeof(datasets); + + c_HeapSort(datasets, total, sizeof(int), compareInts); + + for (c_size_t i = 0; i < total - 1; i++) { + EXPECT_EQ(datasets[i] <= datasets[i+1], true, "Inverted array sequence failed"); + } + return true; +} + +bool test_heapsort_structures(void) { + SensorLog logs[] = { + { 901, 45.2f }, + { 902, 101.4f}, + { 903, -12.6f}, // Target absolute minimum (Must map to index 0) + { 904, 32.0f }, + { 905, 78.9f } + }; + c_size_t count = C_ARRAY_SIZE(logs); + + c_HeapSort(logs, count, sizeof(SensorLog), compareSensorLogs); + + // Verify structural sorting order consistency + EXPECT_EQ(logs[0].log_id, 903, "Index 0 check failed"); + EXPECT_EQ(logs[1].log_id, 904, "Index 1 check failed"); + EXPECT_EQ(logs[2].log_id, 901, "Index 2 check failed"); + EXPECT_EQ(logs[3].log_id, 905, "Index 3 check failed"); + EXPECT_EQ(logs[4].log_id, 902, "Index 4 check failed"); + + return true; +} + +int main(void) { + printf("=== Starting Robust Fixed Framework Unit Testing ===\n"); + + if (test_heapsort_inverted_array() && test_heapsort_structures()) { + printf(" [PASS] All Heapsort Pipeline Re-allocations Passed Correctly.\n"); + } else { + printf(" [FAIL] Test Sequence Intercepted Error.\n"); + } + return 0; +} \ No newline at end of file diff --git a/cKit/Sort/c_InPlaceMSDRadixSort.c b/cKit/Sort/c_InPlaceMSDRadixSort.c new file mode 100644 index 0000000..f12af74 --- /dev/null +++ b/cKit/Sort/c_InPlaceMSDRadixSort.c @@ -0,0 +1,105 @@ +#include +#include + +/** + * Inline helper to safely extract a character at string offset d. + * Automatically maps a string's null terminator to a sentinel value of -1. + */ +C_STATIC_FORCE_INLINE +int c_InPlaceMSDRadixSort_CharAt(const char* str, c_size_t d) { + if (str == NULL) return -1; + c_size_t i = 0; + while (i < d && str[i] != '\0') { + i++; + } + if (str[i] == '\0' || i < d) return -1; + return (unsigned char)str[i]; +} + +/** + * Core Private Recursive Sub-partition In-place Sorting Subroutine. + * Employs a localized head/tail lookup permutation ring to operate directly within array slices. + */ +static void c_InPlaceMSDRadixSort_Recursive(char** arr, long long lo, long long hi, c_size_t d, + c_size_t R, c_size_t* count_buf, long long* heads, long long* tails) { + if (hi <= lo) return; + + // Elements are shifted forward by +2 slots to absorb the -1 string end sentinel gracefully + c_size_t total_buckets = R + 2; + memset(count_buf, 0, total_buckets * sizeof(c_size_t)); + + // Pass A: Compute frequency counts for the current digit slice + for (long long i = lo; i <= hi; i++) { + int c = c_InPlaceMSDRadixSort_CharAt(arr[i], d); + count_buf[c + 2]++; + } + + // Pass B: Transform frequencies into absolute head and tail cursor index maps + heads[0] = lo; + tails[0] = lo + (long long)count_buf[0]; + for (c_size_t r = 1; r < total_buckets; r++) { + heads[r] = tails[r - 1]; + tails[r] = heads[r] + (long long)count_buf[r]; + } + + // Pass C: Cyclic Permutation Swap Element Loop (In-Place Distribution) + for (c_size_t r = 0; r < total_buckets; r++) { + while (heads[r] < tails[r]) { + long long curr_idx = heads[r]; + int c = c_InPlaceMSDRadixSort_CharAt(arr[curr_idx], d); + c_size_t bucket = (c_size_t)(c + 2); + + if (bucket == r) { + heads[r]++; // Element is already in its correct bucket, step forward + } else { + // Evict the element to its correct destination bucket via data swap + long long dest_idx = heads[bucket]; + char* temp = arr[curr_idx]; + arr[curr_idx] = arr[dest_idx]; + arr[dest_idx] = temp; + + heads[bucket]++; // Increment the destination bucket's cursor + } + } + } + + // Pass D: Recursively process sub-arrays for each character bucket + // Shorter strings that terminated (sentinel character index 0) do not need deeper processing + long long current_lo = lo + (long long)count_buf[0]; + for (c_size_t r = 1; r < total_buckets; r++) { + long long current_hi = current_lo + (long long)count_buf[r] - 1; + + if (current_hi > current_lo) { + c_InPlaceMSDRadixSort_Recursive(arr, current_lo, current_hi, d + 1, R, count_buf, heads, tails); + } + current_lo = current_hi + 1; + } +} + +/** + * Sorts an array of variable-length strings completely in-place. + * Space Complexity: O(1) Auxiliary Heap memory footprint (Excluding recursive tracking arrays bound to R) + */ +c_err_t c_InPlaceMSDRadixSort_Sort(const c_InPlaceMSDRadixSort_t* sort, char** arr, c_size_t n) { + if (sort == NULL || arr == NULL || sort->R == 0) return C_ERR_PARAM; + if (n <= 1) return C_ERR_OK; + + // Radix-bound tracking buffers are allocated once upfront to eliminate heap overhead in hot loops + c_size_t total_buckets = sort->R + 2; + c_size_t* count_buf = (c_size_t*)C_ALLOC(total_buckets * sizeof(c_size_t)); + long long* heads = (long long*)C_ALLOC(total_buckets * sizeof(long long)); + long long* tails = (long long*)C_ALLOC(total_buckets * sizeof(long long)); + + if (count_buf == NULL || heads == NULL || tails == NULL) { + C_FREE(count_buf); C_FREE(heads); C_FREE(tails); + return C_ERR_NOMEM; + } + + // Launch the in-place cyclic permutation partition tree + c_InPlaceMSDRadixSort_Recursive(arr, 0, (long long)n - 1, 0, sort->R, count_buf, heads, tails); + + C_FREE(count_buf); + C_FREE(heads); + C_FREE(tails); + return C_ERR_OK; +} diff --git a/cKit/Sort/c_InPlaceMSDRadixSort.h b/cKit/Sort/c_InPlaceMSDRadixSort.h new file mode 100644 index 0000000..34eb9c4 --- /dev/null +++ b/cKit/Sort/c_InPlaceMSDRadixSort.h @@ -0,0 +1,20 @@ +#ifndef INCLUDED_C_INPLACEMSDRADIXSORT_H +#define INCLUDED_C_INPLACEMSDRADIXSORT_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + c_size_t R; // Alphabet size / Radix constraints (e.g., 256 for standard byte arrays) +} c_InPlaceMSDRadixSort_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_InPlaceMSDRadixSort_Sort(const c_InPlaceMSDRadixSort_t* sort, char** arr, c_size_t n); + +#endif /*INCLUDED_C_INPLACEMSDRADIXSORT_H*/ diff --git a/cKit/Sort/c_InPlaceMSDRadixSort.t.c b/cKit/Sort/c_InPlaceMSDRadixSort.t.c new file mode 100644 index 0000000..9d9cbd0 --- /dev/null +++ b/cKit/Sort/c_InPlaceMSDRadixSort.t.c @@ -0,0 +1,75 @@ +#include "c_InPlaceMSDRadixSort.h" +#include +#include + +#include "c_Memory.h" + +// Your updated line tracing diagnostic macro +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \ + return C_FALSE; \ + } \ + } while(0) + +c_bool_t test_inplace_msd_radix_sort_execution(void) { + c_InPlaceMSDRadixSort_t sort; + sort.R = 256; // Standard extended ASCII alphabet boundaries + + c_size_t n = 7; + // Unsorted variable-length string test pool configurations + char* test_data[] = { + "she", + "sells", + "seashells", + "by", + "the", + "sea", + "shore" + }; + + // Allocate an array of modifiable pointers to replicate the application environment + char** arr = (char**)C_ALLOC(n * sizeof(char*)); + if (arr == NULL) return C_FALSE; + for (c_size_t i = 0; i < n; i++) arr[i] = test_data[i]; + + printf(" [LOG] Launching space-optimized In-Place MSD Radix Sort...\n"); + c_err_t err = c_InPlaceMSDRadixSort_Sort(&sort, arr, n); + + EXPECT_EQ(err, C_ERR_OK, "In-place MSD sort engine returned unexpected runtime error code"); + + // Mathematically sorted verification checkpoints list mapping: + // Expected alphabetical sequence: by, sea, seashells, sells, she, shore, the + EXPECT_EQ(strcmp(arr[0], "by"), 0, "Sorted position 0 incorrect"); + EXPECT_EQ(strcmp(arr[1], "sea"), 0, "Sorted position 1 incorrect"); + EXPECT_EQ(strcmp(arr[2], "seashells"), 0, "Sorted position 2 incorrect"); + EXPECT_EQ(strcmp(arr[3], "sells"), 0, "Sorted position 3 incorrect"); + EXPECT_EQ(strcmp(arr[4], "she"), 0, "Sorted position 4 incorrect"); + EXPECT_EQ(strcmp(arr[5], "shore"), 0, "Sorted position 5 incorrect"); + EXPECT_EQ(strcmp(arr[6], "the"), 0, "Sorted position 6 incorrect"); + + // Checkpoint 2: Variable Length Validation check + // "sea" must strictly precede its extended prefix branch form "seashells" + EXPECT_EQ(strcmp(arr[1], "sea") == 0 && strcmp(arr[2], "seashells") == 0, C_TRUE, "Variable-length short-prefix ordering failed"); + + printf(" [STAT] In-Place MSD Radix Sort verified successfully. Alphabetic Output: "); + for (c_size_t i = 0; i < n; i++) { + printf("%s ", arr[i]); + } + printf("\n"); + + C_FREE(arr); + return C_TRUE; +} + +int main(void) { + printf("=== Starting Framework Verification: In-Place MSD Radix Sort ===\n"); + if (test_inplace_msd_radix_sort_execution()) { + printf(" [PASS] Variable-Length In-Place Cyclic Swap Permutations and Array Pointer Re-maps Verified.\n"); + } else { + printf(" [FAIL] Radix Partition Tree In-Place Structural Analysis Anomalies Intercepted.\n"); + } + return 0; +} + diff --git a/cKit/Sort/c_IndexMaxPQ.c b/cKit/Sort/c_IndexMaxPQ.c new file mode 100644 index 0000000..16a1913 --- /dev/null +++ b/cKit/Sort/c_IndexMaxPQ.c @@ -0,0 +1,184 @@ +#include + +#include +#include + +/** + * Internal helper to swap two positions inside the heap structures. + * Keeps the inverted index (qp) tightly synchronized. + */ +C_STATIC_FORCE_INLINE +void c_IndexMaxPQ_Swap(c_IndexMaxPQ_t* pq_inst, c_size_t i, c_size_t j) { + long long temp_pq = pq_inst->pq[i]; + pq_inst->pq[i] = pq_inst->pq[j]; + pq_inst->pq[j] = temp_pq; + + pq_inst->qp[pq_inst->pq[i]] = (long long)i; + pq_inst->qp[pq_inst->pq[j]] = (long long)j; +} + +/** + * Sift-Up Operational Core for Max-Heap + */ +C_STATIC_FORCE_INLINE +void c_IndexMaxPQ_SiftUp(c_IndexMaxPQ_t* pq_inst, c_size_t current) { + char* keys_arr = (char*)pq_inst->keys; + c_size_t es = pq_inst->element_size; + + while (current > 0) { + c_size_t parent = (current - 1) / 2; + + const void* current_key = keys_arr + (pq_inst->pq[current] * es); + const void* parent_key = keys_arr + (pq_inst->pq[parent] * es); + + // Max-Heap condition: Break if current element is less than or equal to its parent + if (pq_inst->compar(current_key, parent_key) <= 0) { + break; + } + c_IndexMaxPQ_Swap(pq_inst, current, parent); + current = parent; + } +} + +/** + * Sift-Down Operational Core for Max-Heap + */ +C_STATIC_FORCE_INLINE +void c_IndexMaxPQ_SiftDown(c_IndexMaxPQ_t* pq_inst, c_size_t current) { + char* keys_arr = (char*)pq_inst->keys; + c_size_t es = pq_inst->element_size; + + while (1) { + c_size_t left_child = (2 * current) + 1; + c_size_t right_child = (2 * current) + 2; + c_size_t largest = current; + + // Max-Heap condition: Target the larger of the two children to sift down + if (left_child < pq_inst->size) { + if (pq_inst->compar(keys_arr + (pq_inst->pq[left_child] * es), keys_arr + (pq_inst->pq[largest] * es)) > 0) { + largest = left_child; + } + } + if (right_child < pq_inst->size) { + if (pq_inst->compar(keys_arr + (pq_inst->pq[right_child] * es), keys_arr + (pq_inst->pq[largest] * es)) > 0) { + largest = right_child; + } + } + + if (largest == current) { + break; + } + c_IndexMaxPQ_Swap(pq_inst, current, largest); + current = largest; + } +} + +/* ------------------------------------------------------------------------------------------------------------------ */ + +c_err_t c_IndexMaxPQ_Init(c_IndexMaxPQ_t* pq_inst, c_size_t max_items, c_size_t element_size, + int (*compar)(const void*, const void*)) { + if (pq_inst == NULL || max_items == 0 || element_size == 0 || compar == NULL) return C_ERR_PARAM; + + pq_inst->max_items = max_items; + pq_inst->element_size = element_size; + pq_inst->size = 0; + pq_inst->compar = compar; + + pq_inst->keys = C_ALLOC(max_items * element_size); + pq_inst->pq = (long long*)C_ALLOC(max_items * sizeof(long long)); + pq_inst->qp = (long long*)C_ALLOC(max_items * sizeof(long long)); + + if (pq_inst->keys == NULL || pq_inst->pq == NULL || pq_inst->qp == NULL) { + C_FREE(pq_inst->keys); C_FREE(pq_inst->pq); C_FREE(pq_inst->qp); + return C_ERR_NOMEM; + } + + for (c_size_t i = 0; i < max_items; i++) { + pq_inst->qp[i] = -1; + } + + return C_ERR_OK; +} + +void c_IndexMaxPQ_Destroy(c_IndexMaxPQ_t* pq_inst) { + if (pq_inst) { + C_FREE(pq_inst->keys); pq_inst->keys = NULL; + C_FREE(pq_inst->pq); pq_inst->pq = NULL; + C_FREE(pq_inst->qp); pq_inst->qp = NULL; + pq_inst->size = 0; + pq_inst->max_items = 0; + } +} + +c_bool_t c_IndexMaxPQ_Contains(c_IndexMaxPQ_t* pq_inst, c_size_t ext_id) { + if (pq_inst == NULL || ext_id >= pq_inst->max_items) return C_FALSE; + return pq_inst->qp[ext_id] != -1; +} + +c_err_t c_IndexMaxPQ_Push(c_IndexMaxPQ_t* pq_inst, c_size_t ext_id, const void* element) { + if (pq_inst == NULL || ext_id >= pq_inst->max_items || element == NULL) return C_ERR_PARAM; + if (c_IndexMaxPQ_Contains(pq_inst, ext_id)) return C_ERR_ALREADY_EXISTS; + + char* keys_arr = (char*)pq_inst->keys; + memcpy(keys_arr + (ext_id * pq_inst->element_size), element, pq_inst->element_size); + + c_size_t current = pq_inst->size; + pq_inst->pq[current] = (long long)ext_id; + pq_inst->qp[ext_id] = (long long)current; + + pq_inst->size++; + + c_IndexMaxPQ_SiftUp(pq_inst, current); + + return C_ERR_OK; +} + +c_err_t c_IndexMaxPQ_Pop(c_IndexMaxPQ_t* pq_inst, c_size_t* out_ext_id, void* out_element_buffer) { + if (pq_inst == NULL) return C_ERR_PARAM; + if (pq_inst->size == 0) return C_ERR_EMPTY; + + long long max_ext_id = pq_inst->pq[0]; + if (out_ext_id) *out_ext_id = (c_size_t)max_ext_id; + + if (out_element_buffer) { + char* keys_arr = (char*)pq_inst->keys; + memcpy(out_element_buffer, keys_arr + (max_ext_id * pq_inst->element_size), pq_inst->element_size); + } + + c_IndexMaxPQ_Swap(pq_inst, 0, pq_inst->size - 1); + + pq_inst->qp[max_ext_id] = -1; + pq_inst->size--; + + if (pq_inst->size > 0) { + c_IndexMaxPQ_SiftDown(pq_inst, 0); + } + + return C_ERR_OK; +} + +c_err_t c_IndexMaxPQ_ChangeKey(c_IndexMaxPQ_t* pq_inst, c_size_t ext_id, const void* new_element) { + if (pq_inst == NULL || ext_id >= pq_inst->max_items || new_element == NULL) return C_ERR_PARAM; + if (!c_IndexMaxPQ_Contains(pq_inst, ext_id)) return C_ERR_NOT_FOUND; + + char* keys_arr = (char*)pq_inst->keys; + c_size_t es = pq_inst->element_size; + + memcpy(keys_arr + (ext_id * es), new_element, es); + + c_size_t heap_pos = (c_size_t)pq_inst->qp[ext_id]; + + c_IndexMaxPQ_SiftUp(pq_inst, heap_pos); + c_IndexMaxPQ_SiftDown(pq_inst, heap_pos); + + return C_ERR_OK; +} + +void* c_IndexMaxPQ_Peek(c_IndexMaxPQ_t* pq_inst, c_size_t* out_ext_id) { + if (pq_inst == NULL || pq_inst->size == 0) return NULL; + if (out_ext_id) *out_ext_id = (c_size_t)pq_inst->pq[0]; + + char* keys_arr = (char*)pq_inst->keys; + return keys_arr + (pq_inst->pq[0] * pq_inst->element_size); +} + diff --git a/cKit/Sort/c_IndexMaxPQ.h b/cKit/Sort/c_IndexMaxPQ.h new file mode 100644 index 0000000..76c73aa --- /dev/null +++ b/cKit/Sort/c_IndexMaxPQ.h @@ -0,0 +1,41 @@ +#ifndef INCLUDED_C_INDEXMAXPQ_H +#define INCLUDED_C_INDEXMAXPQ_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + void* keys; + c_size_t element_size; + c_size_t max_items; + c_size_t size; + long long* pq; + long long* qp; + int (*compar)(const void*, const void*); +} c_IndexMaxPQ_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_IndexMaxPQ_Init(c_IndexMaxPQ_t* pq_inst, c_size_t max_items, c_size_t element_size, + int (*compar)(const void*, const void*)); + +void c_IndexMaxPQ_Destroy(c_IndexMaxPQ_t* pq_inst); + +c_bool_t c_IndexMaxPQ_Contains(c_IndexMaxPQ_t* pq_inst, c_size_t ext_id); + +c_err_t c_IndexMaxPQ_Push(c_IndexMaxPQ_t* pq_inst, c_size_t ext_id, const void* element); + +c_err_t c_IndexMaxPQ_Pop(c_IndexMaxPQ_t* pq_inst, c_size_t* out_ext_id, void* out_element_buffer); + +c_err_t c_IndexMaxPQ_ChangeKey(c_IndexMaxPQ_t* pq_inst, c_size_t ext_id, const void* new_element); + +void* c_IndexMaxPQ_Peek(c_IndexMaxPQ_t* pq_inst, c_size_t* out_ext_id); + + +#endif /*INCLUDED_C_INDEXMAXPQ_H*/ diff --git a/cKit/Sort/c_IndexMaxPQ.t.c b/cKit/Sort/c_IndexMaxPQ.t.c new file mode 100644 index 0000000..ba57188 --- /dev/null +++ b/cKit/Sort/c_IndexMaxPQ.t.c @@ -0,0 +1,97 @@ +#include "c_IndexMaxPQ.h" +#include +#include + +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d)\n", msg, (int)(expected), (int)(actual)); \ + return C_FALSE; \ + } \ + } while(0) + +// Complex struct data block payload +typedef struct { + int task_id; + float score; // Primary tracking element for MaxPQ (higher level extracted first) +} TelemetryTask; + +int compareTelemetryTasks(const void* a, const void* b) { + const TelemetryTask* t1 = (const TelemetryTask*)a; + const TelemetryTask* t2 = (const TelemetryTask*)b; + if (t1->score < t2->score) return -1; + if (t1->score > t2->score) return 1; + return 0; +} + +c_bool_t test_framework_index_max_pq(void) { + c_IndexMaxPQ_t ipq; + + // 1. Parameter Enforcement Validation + EXPECT_EQ(c_IndexMaxPQ_Init(NULL, 10, sizeof(TelemetryTask), compareTelemetryTasks), C_ERR_PARAM, "NULL pointer checking missed"); + EXPECT_EQ(c_IndexMaxPQ_Init(&ipq, 0, sizeof(TelemetryTask), compareTelemetryTasks), C_ERR_PARAM, "Zero capacity checking missed"); + + // Allocate space for 4 telemetry streams (External IDs 0 to 3) + EXPECT_EQ(c_IndexMaxPQ_Init(&ipq, 4, sizeof(TelemetryTask), compareTelemetryTasks), C_ERR_OK, "PQ Init failed"); + + TelemetryTask t0 = { 5001, 12.5f }; + TelemetryTask t1 = { 5002, 98.4f }; // Initial Maximum Element + TelemetryTask t2 = { 5003, 45.2f }; + + // 2. Data Insertion & State Tracking + EXPECT_EQ(c_IndexMaxPQ_Push(&ipq, 0, &t0), C_ERR_OK, "Push t0 failed"); + EXPECT_EQ(c_IndexMaxPQ_Push(&ipq, 1, &t1), C_ERR_OK, "Push t1 failed"); + EXPECT_EQ(c_IndexMaxPQ_Push(&ipq, 2, &t2), C_ERR_OK, "Push t2 failed"); + + EXPECT_EQ(c_IndexMaxPQ_Push(&ipq, 1, &t1), C_ERR_ALREADY_EXISTS, "Duplicate insert guard missed"); + EXPECT_EQ(c_IndexMaxPQ_Contains(&ipq, 1), C_TRUE, "Contains failed reporting registered elements"); + EXPECT_EQ(c_IndexMaxPQ_Contains(&ipq, 3), C_FALSE, "Contains reported tracking on unused indices"); + + // 3. Dynamic Key Upgrades (ChangeKey runtime shifts) + // Modify Telemetry Task 0 (t0, external ID: 0) from 12.5f to 105.7f. + // This should instantly shift t0 to the root position of the Max-Heap. + TelemetryTask t0_boosted = { 5001, 105.7f }; + EXPECT_EQ(c_IndexMaxPQ_ChangeKey(&ipq, 5, &t0_boosted), C_ERR_PARAM, "OOB Index verification checks missed"); + EXPECT_EQ(c_IndexMaxPQ_ChangeKey(&ipq, 3, &t0_boosted), C_ERR_NOT_FOUND, "Unregistered Index modification check missed"); + EXPECT_EQ(c_IndexMaxPQ_ChangeKey(&ipq, 0, &t0_boosted), C_ERR_OK, "Valid target key adjustment failed"); + + // 4. Verification Lookups & Sequential Extraction Lifecycle + c_size_t extracted_ext_id = 999; + TelemetryTask out_buffer; + + // Peek Check: Root must now map to External ID 0 (score: 105.7f) + TelemetryTask* peek_ptr = (TelemetryTask*)c_IndexMaxPQ_Peek(&ipq, &extracted_ext_id); + EXPECT_EQ(extracted_ext_id, 0, "Peek resolved incorrect key slot context"); + EXPECT_EQ(peek_ptr->score == 105.7f, C_TRUE, "Peek structural memory value extraction wrong"); + + // Pop 1: Yields Task 0 (ID: 0, Score: 105.7f) + EXPECT_EQ(c_IndexMaxPQ_Pop(&ipq, &extracted_ext_id, &out_buffer), C_ERR_OK, "Pop execution step 1 failed"); + EXPECT_EQ(extracted_ext_id, 0, "Re-balancing priority lookup sequence wrong at Pop 1"); + + // Pop 2: Yields Task 1 (ID: 1, Score: 98.4f) + EXPECT_EQ(c_IndexMaxPQ_Pop(&ipq, &extracted_ext_id, &out_buffer), C_ERR_OK, "Pop execution step 2 failed"); + EXPECT_EQ(extracted_ext_id, 1, "Re-balancing priority lookup sequence wrong at Pop 2"); + + // Pop 3: Yields Task 2 (ID: 2, Score: 45.2f) + EXPECT_EQ(c_IndexMaxPQ_Pop(&ipq, &extracted_ext_id, &out_buffer), C_ERR_OK, "Pop execution step 3 failed"); + EXPECT_EQ(extracted_ext_id, 2, "Re-balancing priority lookup sequence wrong at Pop 3"); + + // 5. Empty Boundary Assert Cleanups + EXPECT_EQ(ipq.size, 0, "Tracking metric registers missed reset zero flags"); + EXPECT_EQ(c_IndexMaxPQ_Pop(&ipq, &extracted_ext_id, &out_buffer), C_ERR_EMPTY, "Empty pipeline crash missing exception flag hooks"); + + c_IndexMaxPQ_Destroy(&ipq); + return C_TRUE; +} + +int main(void) { + printf("=== Starting Custom Framework Profiling: c_IndexMaxPQ ===\n"); + + if (test_framework_index_max_pq()) { + printf(" [PASS] All Indexed Max-Priority Queue Architectural Requirements Verified Successfully.\n"); + } else { + printf(" [FAIL] Architectural Verification Pipeline Failure Detected.\n"); + } + printf("=== All Indexed Priority Queue Tests Completed ===\n"); + return 0; +} \ No newline at end of file diff --git a/cKit/Sort/c_IndexMinPQ.c b/cKit/Sort/c_IndexMinPQ.c new file mode 100644 index 0000000..2022684 --- /dev/null +++ b/cKit/Sort/c_IndexMinPQ.c @@ -0,0 +1,192 @@ +#include +#include + +/** + * Internal helper to swap two positions inside the heap structures. + * Also keeps the inverted index (qp) tightly synchronized. + */ +C_STATIC_FORCE_INLINE +void c_IndexMinPQ_Swap(c_IndexMinPQ_t* pq_inst, c_size_t i, c_size_t j) { + long long temp_pq = pq_inst->pq[i]; + pq_inst->pq[i] = pq_inst->pq[j]; + pq_inst->pq[j] = temp_pq; + + pq_inst->qp[pq_inst->pq[i]] = (long long)i; + pq_inst->qp[pq_inst->pq[j]] = (long long)j; +} + +/** + * Sift-Up Operational Core + */ +C_STATIC_FORCE_INLINE +void c_IndexMinPQ_SiftUp(c_IndexMinPQ_t* pq_inst, c_size_t current) { + char* keys_arr = (char*)pq_inst->keys; + c_size_t es = pq_inst->element_size; + + while (current > 0) { + c_size_t parent = (current - 1) / 2; + + const void* current_key = keys_arr + (pq_inst->pq[current] * es); + const void* parent_key = keys_arr + (pq_inst->pq[parent] * es); + + if (pq_inst->compar(current_key, parent_key) >= 0) { + break; + } + c_IndexMinPQ_Swap(pq_inst, current, parent); + current = parent; + } +} + +/** + * Sift-Down Operational Core + */ +C_STATIC_FORCE_INLINE +void c_IndexMinPQ_SiftDown(c_IndexMinPQ_t* pq_inst, c_size_t current) { + char* keys_arr = (char*)pq_inst->keys; + c_size_t es = pq_inst->element_size; + + while (1) { + c_size_t left_child = (2 * current) + 1; + c_size_t right_child = (2 * current) + 2; + c_size_t smallest = current; + + if (left_child < pq_inst->size) { + if (pq_inst->compar(keys_arr + (pq_inst->pq[left_child] * es), keys_arr + (pq_inst->pq[smallest] * es)) < 0) { + smallest = left_child; + } + } + if (right_child < pq_inst->size) { + if (pq_inst->compar(keys_arr + (pq_inst->pq[right_child] * es), keys_arr + (pq_inst->pq[smallest] * es)) < 0) { + smallest = right_child; + } + } + + if (smallest == current) { + break; + } + c_IndexMinPQ_Swap(pq_inst, current, smallest); + current = smallest; + } +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_IndexMinPQ_Init(c_IndexMinPQ_t* pq_inst, c_size_t max_items, c_size_t element_size, + int (*compar)(const void*, const void*)) { + if (pq_inst == NULL || max_items == 0 || element_size == 0 || compar == NULL) return C_ERR_PARAM; + + pq_inst->max_items = max_items; + pq_inst->element_size = element_size; + pq_inst->size = 0; + pq_inst->compar = compar; + + pq_inst->keys = C_ALLOC(max_items * element_size); + pq_inst->pq = (long long*)C_ALLOC(max_items * sizeof(long long)); + pq_inst->qp = (long long*)C_ALLOC(max_items * sizeof(long long)); + + if (pq_inst->keys == NULL || pq_inst->pq == NULL || pq_inst->qp == NULL) { + // Safe cleanup if any allocation segment drops offline + C_FREE(pq_inst->keys); C_FREE(pq_inst->pq); C_FREE(pq_inst->qp); + return C_ERR_NOMEM; + } + + // Initialize inverted tracking array cells to -1 (indicating absent from heap) + for (c_size_t i = 0; i < max_items; i++) { + pq_inst->qp[i] = -1; + } + + return C_ERR_OK; +} + +void c_IndexMinPQ_Destroy(c_IndexMinPQ_t* pq_inst) { + if (pq_inst) { + C_FREE(pq_inst->keys); pq_inst->keys = NULL; + C_FREE(pq_inst->pq); pq_inst->pq = NULL; + C_FREE(pq_inst->qp); pq_inst->qp = NULL; + pq_inst->size = 0; + pq_inst->max_items = 0; + } +} + +c_bool_t c_IndexMinPQ_Contains(c_IndexMinPQ_t* pq_inst, c_size_t ext_id) { + if (pq_inst == NULL || ext_id >= pq_inst->max_items) return C_FALSE; + return pq_inst->qp[ext_id] != -1; +} + +c_err_t c_IndexMinPQ_Push(c_IndexMinPQ_t* pq_inst, c_size_t ext_id, const void* element) { + if (pq_inst == NULL || ext_id >= pq_inst->max_items || element == NULL) return C_ERR_PARAM; + if (c_IndexMinPQ_Contains(pq_inst, ext_id)) return C_ERR_ALREADY_EXISTS; // Must use Change Key API if already existing + + char* keys_arr = (char*)pq_inst->keys; + + // Store key inside the primary index table slot + memcpy(keys_arr + (ext_id * pq_inst->element_size), element, pq_inst->element_size); + + // Append to bottom leaf array trackers + c_size_t current = pq_inst->size; + pq_inst->pq[current] = (long long)ext_id; + pq_inst->qp[ext_id] = (long long)current; + + pq_inst->size++; + + // Sift upward to re-stabilize the min-heap property + c_IndexMinPQ_SiftUp(pq_inst, current); + + return C_ERR_OK; +} + +c_err_t c_IndexMinPQ_Pop(c_IndexMinPQ_t* pq_inst, c_size_t* out_ext_id, void* out_element_buffer) { + if (pq_inst == NULL) return C_ERR_PARAM; + if (pq_inst->size == 0) return C_ERR_EMPTY; + + long long min_ext_id = pq_inst->pq[0]; + if (out_ext_id) *out_ext_id = (c_size_t)min_ext_id; + + if (out_element_buffer) { + char* keys_arr = (char*)pq_inst->keys; + memcpy(out_element_buffer, keys_arr + (min_ext_id * pq_inst->element_size), pq_inst->element_size); + } + + // Swap top root with the trailing active leaf node + c_IndexMinPQ_Swap(pq_inst, 0, pq_inst->size - 1); + + // Clean up tracking registers for extracted ID + pq_inst->qp[min_ext_id] = -1; + pq_inst->size--; + + // Sift down from the root to re-balance the tree bounds + if (pq_inst->size > 0) { + c_IndexMinPQ_SiftDown(pq_inst, 0); + } + + return C_ERR_OK; +} + +c_err_t c_IndexMinPQ_ChangeKey(c_IndexMinPQ_t* pq_inst, c_size_t ext_id, const void* new_element) { + if (pq_inst == NULL || ext_id >= pq_inst->max_items || new_element == NULL) return C_ERR_PARAM; + if (!c_IndexMinPQ_Contains(pq_inst, ext_id)) return C_ERR_NOT_FOUND; + + char* keys_arr = (char*)pq_inst->keys; + c_size_t es = pq_inst->element_size; + + // Update raw payload data in-place + memcpy(keys_arr + (ext_id * es), new_element, es); + + // Leverage the inverted index (qp) to instantly locate the item's position inside the heap tree + c_size_t heap_pos = (c_size_t)pq_inst->qp[ext_id]; + + // Trigger localized sifting in both directions. Only one will execute depending on whether the key grew or shrank. + c_IndexMinPQ_SiftUp(pq_inst, heap_pos); + c_IndexMinPQ_SiftDown(pq_inst, heap_pos); + + return C_ERR_OK; +} + +void* c_IndexMinPQ_Peek(c_IndexMinPQ_t* pq_inst, c_size_t* out_ext_id) { + if (pq_inst == NULL || pq_inst->size == 0) return NULL; + if (out_ext_id) *out_ext_id = (c_size_t)pq_inst->pq[0]; + + char* keys_arr = (char*)pq_inst->keys; + return keys_arr + (pq_inst->pq[0] * pq_inst->element_size); +} \ No newline at end of file diff --git a/cKit/Sort/c_IndexMinPQ.h b/cKit/Sort/c_IndexMinPQ.h new file mode 100644 index 0000000..cbff135 --- /dev/null +++ b/cKit/Sort/c_IndexMinPQ.h @@ -0,0 +1,42 @@ +#ifndef INCLUDED_C_INDEXMINPQ_H +#define INCLUDED_C_INDEXMINPQ_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +typedef struct { + void* keys; // Flat array storing user's complex items (indexed by external ID) + c_size_t element_size; // Size of each element in bytes + c_size_t max_items; // Maximum external ID capacity (0 to max_items - 1) + c_size_t size; // Current active element count inside the heap + + long long* pq; // Heap array: map heap position -> external ID + long long* qp; // Inverted index array: map external ID -> heap position (-1 if not in heap) + + int (*compar)(const void*, const void*); // Custom comparison rule pointer +} c_IndexMinPQ_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_IndexMinPQ_Init(c_IndexMinPQ_t* pq_inst, c_size_t max_items, c_size_t element_size, + int (*compar)(const void*, const void*)); + +void c_IndexMinPQ_Destroy(c_IndexMinPQ_t* pq_inst); + +c_bool_t c_IndexMinPQ_Contains(c_IndexMinPQ_t* pq_inst, c_size_t ext_id); + +c_err_t c_IndexMinPQ_Push(c_IndexMinPQ_t* pq_inst, c_size_t ext_id, const void* element); + +c_err_t c_IndexMinPQ_Pop(c_IndexMinPQ_t* pq_inst, c_size_t* out_ext_id, void* out_element_buffer) ; + +c_err_t c_IndexMinPQ_ChangeKey(c_IndexMinPQ_t* pq_inst, c_size_t ext_id, const void* new_element); + +void* c_IndexMinPQ_Peek(c_IndexMinPQ_t* pq_inst, c_size_t* out_ext_id); + +#endif /*INCLUDED_C_INDEXMINPQ_H*/ diff --git a/cKit/Sort/c_IndexMinPQ.t.c b/cKit/Sort/c_IndexMinPQ.t.c new file mode 100644 index 0000000..d6a484e --- /dev/null +++ b/cKit/Sort/c_IndexMinPQ.t.c @@ -0,0 +1,109 @@ +#include "c_IndexMinPQ.h" +#include +#include +#include +#include +#include + + +// Unit test validation driver macro +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d)\n", msg, (int)(expected), (int)(actual)); \ + return C_FALSE; \ + } \ + } while(0) + +// Complex data block node mapping path costs +typedef struct { + float node_weight; + int edge_id; +} PathNode; + +// Framework Comparator for PathNode structures (Min-Heap optimization logic) +int comparePathCosts(const void* a, const void* b) { + const PathNode* p1 = (const PathNode*)a; + const PathNode* p2 = (const PathNode*)b; + if (p1->node_weight < p2->node_weight) return -1; + if (p1->node_weight > p2->node_weight) return 1; + return 0; +} + +/** + * Functional Test Profile validating Param checking, Duplicate Guards, + * and ChangeKey structural re-balancing routines. + */ +c_bool_t test_framework_index_min_pq(void) { + c_IndexMinPQ_t ipq; + + // 1. Parameter Enforcement Validation Boundary Checks + EXPECT_EQ(c_IndexMinPQ_Init(NULL, 10, sizeof(PathNode), comparePathCosts), C_ERR_PARAM, "NULL pointer checking missed"); + EXPECT_EQ(c_IndexMinPQ_Init(&ipq, 0, sizeof(PathNode), comparePathCosts), C_ERR_PARAM, "Zero capacity checking missed"); + + // Allocate space for up to 4 concurrent vertices (External IDs: 0 to 3) + EXPECT_EQ(c_IndexMinPQ_Init(&ipq, 4, sizeof(PathNode), comparePathCosts), C_ERR_OK, "PQ Init failed"); + + PathNode v0 = { 35.6f, 100 }; + PathNode v1 = { 14.2f, 101 }; + PathNode v2 = { 88.1f, 102 }; + + // 2. State Insert Tracking + EXPECT_EQ(c_IndexMinPQ_Push(&ipq, 0, &v0), C_ERR_OK, "Push v0 failed"); + EXPECT_EQ(c_IndexMinPQ_Push(&ipq, 1, &v1), C_ERR_OK, "Push v1 failed"); + EXPECT_EQ(c_IndexMinPQ_Push(&ipq, 2, &v2), C_ERR_OK, "Push v2 failed"); + + // Check duplication guard safety logic + EXPECT_EQ(c_IndexMinPQ_Push(&ipq, 1, &v1), C_ERR_ALREADY_EXISTS, "Duplicate insert guard missed"); + EXPECT_EQ(c_IndexMinPQ_Contains(&ipq, 1), C_TRUE, "Contains failed reporting registered elements"); + EXPECT_EQ(c_IndexMinPQ_Contains(&ipq, 3), C_FALSE, "Contains reported tracking on unused indices"); + + // 3. Dynamic Key Upgrades (ChangeKey runtime shifts) + // Vertex 2 (v2) currently has a cost of 88.1f. Let's decrease it to 5.2f. + // This action must shift it straight up to the head of the Min-Priority Queue. + PathNode v2_optimized = { 5.2f, 102 }; + EXPECT_EQ(c_IndexMinPQ_ChangeKey(&ipq, 5, &v2_optimized), C_ERR_PARAM, "OOB Index verification checks missed"); + EXPECT_EQ(c_IndexMinPQ_ChangeKey(&ipq, 3, &v2_optimized), C_ERR_NOT_FOUND, "Unregistered Index modification check missed"); + EXPECT_EQ(c_IndexMinPQ_ChangeKey(&ipq, 2, &v2_optimized), C_ERR_OK, "Valid target key adjustment failed"); + + // 4. Verification Lookups & Sequential Extraction Lifecycle + c_size_t extracted_ext_id = 999; + PathNode out_buffer; + + // Peek Check: Root must now resolve to External ID 2 (cost 5.2f) + PathNode* peek_ptr = (PathNode*)c_IndexMinPQ_Peek(&ipq, &extracted_ext_id); + EXPECT_EQ(extracted_ext_id, 2, "Peek resolved incorrect key slot context"); + EXPECT_EQ(peek_ptr->node_weight == 5.2f, C_TRUE, "Peek structural memory value extraction wrong"); + + // Pop 1: Yields Vertex 2 (ID: 2, Weight: 5.2f) + EXPECT_EQ(c_IndexMinPQ_Pop(&ipq, &extracted_ext_id, &out_buffer), C_ERR_OK, "Pop execution step 1 failed"); + EXPECT_EQ(extracted_ext_id, 2, "Re-balancing priority lookup sequence wrong at Pop 1"); + + // Pop 2: Yields Vertex 1 (ID: 1, Weight: 14.2f) + EXPECT_EQ(c_IndexMinPQ_Pop(&ipq, &extracted_ext_id, &out_buffer), C_ERR_OK, "Pop execution step 2 failed"); + EXPECT_EQ(extracted_ext_id, 1, "Re-balancing priority lookup sequence wrong at Pop 2"); + + // Pop 3: Yields Vertex 0 (ID: 0, Weight: 35.6f) + EXPECT_EQ(c_IndexMinPQ_Pop(&ipq, &extracted_ext_id, &out_buffer), C_ERR_OK, "Pop execution step 3 failed"); + EXPECT_EQ(extracted_ext_id, 0, "Re-balancing priority lookup sequence wrong at Pop 3"); + + // 5. Empty Boundary Assert Cleanups + EXPECT_EQ(ipq.size, 0, "Tracking metric registers missed reset zero flags"); + EXPECT_EQ(c_IndexMinPQ_Pop(&ipq, &extracted_ext_id, &out_buffer), C_ERR_EMPTY, "Empty pipeline crash missing exception flag hooks"); + + c_IndexMinPQ_Destroy(&ipq); + return C_TRUE; +} + +int main(void) { + printf("=== Starting Custom Framework Profiling: c_IndexMinPQ ===\n"); + + if (test_framework_index_min_pq()) { + printf(" [PASS] All Indexed Min-Priority Queue Architectural Requirements Verified Successfully.\n"); + } else { + printf(" [FAIL] Architectural Verification Pipeline Failure Detected.\n"); + } + + printf("=== All Indexed Priority Queue Tests Completed ===\n"); + return 0; +} diff --git a/cKit/Sort/c_InsertionSort.c b/cKit/Sort/c_InsertionSort.c new file mode 100644 index 0000000..4ffb681 --- /dev/null +++ b/cKit/Sort/c_InsertionSort.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Sort/c_InsertionSort.h b/cKit/Sort/c_InsertionSort.h new file mode 100644 index 0000000..bfdad52 --- /dev/null +++ b/cKit/Sort/c_InsertionSort.h @@ -0,0 +1,68 @@ +#ifndef INCLUDED_C_INSERTIONSORT_H +#define INCLUDED_C_INSERTIONSORT_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +/** + * 通用插入排序函数 + * @param base 指向待排序数组首元素的指针 + * @param num 数组中元素的个数 + * @param size 每个元素的大小(字节数) + * @param compar 指向比较函数的指针 + */ +C_STATIC_FORCE_INLINE +void c_InsertionSort(void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + char* arr = (char*)base; // 强转为 char* 以便按单字节进行指针偏移 + + // 分配一块临时内存,用于存放当前要插入的“哨兵”元素(temp) +#define STACK_LIMIT 128 + char stack_buf[STACK_LIMIT]; + void* temp = NULL; + + if (size <= STACK_LIMIT) { + temp = stack_buf; + } else { + temp = C_ALLOC(size); + if (temp == NULL) return; + } + + for (c_size_t i = 1; i < num; i++) { + // temp = arr[i]:将当前元素复制到临时空间 + memcpy(temp, arr + (i * size), size); + + long long j = i - 1; + + // 循环条件:j >= 0 且 arr[j] > temp + // 使用 compar(arr + (j * size), temp) > 0 来判断是否需要后移 + while (j >= 0 && compar(arr + (j * size), temp) > 0) { + // arr[j + 1] = arr[j]:将前面的元素往后移一位 + memcpy(arr + ((j + 1) * size), arr + (j * size), size); + j--; + } + + // arr[j + 1] = temp:将目标元素插入到正确位置 + memcpy(arr + ((j + 1) * size), temp, size); + } + + // Only trigger free if it was actually allocated from the heap + if (size > STACK_LIMIT) { + C_FREE(temp); + } +#undef STACK_LIMIT +} + + + +#endif /*INCLUDED_C_INSERTIONSORT_H*/ diff --git a/cKit/Sort/c_InsertionSort.t.c b/cKit/Sort/c_InsertionSort.t.c new file mode 100644 index 0000000..21af834 --- /dev/null +++ b/cKit/Sort/c_InsertionSort.t.c @@ -0,0 +1,51 @@ +#include "c_InsertionSort.h" +#include +#include + +typedef struct { + int id; + char name[20]; + double score; +} Student; + +// 自定义比较函数:按成绩 (score) 降序排列 +// 如果希望升序,只需反转返回值或改变比较符号 +int compareStudentsByScoreDesc(const void* a, const void* b) { + const Student* s1 = (const Student*)a; + const Student* s2 = (const Student*)b; + + if (s1->score > s2->score) return -1; // s1 成绩高,排在前面 + if (s1->score < s2->score) return 1; // s1 成绩低,排在后面 + return 0; +} + +int main(int argc, char* argv[]) { + // 准备一个无序的学生数组 + Student students[] = { + {101, "Alice", 82.5}, + {105, "Bob", 95.0}, + {109, "Charlie", 88.0}, + {112, "David", 79.5} + }; + size_t count = sizeof(students) / sizeof(students[0]); + + printf("排序前:\n"); + for (size_t i = 0; i < count; i++) { + printf("学号: %d, 姓名: %s, 成绩: %.1f\n", students[i].id, students[i].name, students[i].score); + } + + // 调用通用插入排序 + c_InsertionSort( + students, + count, + sizeof(Student), + compareStudentsByScoreDesc + ); + + printf("\n排序后(按成绩降序):\n"); + for (size_t i = 0; i < count; i++) { + printf("学号: %d, 姓名: %s, 成绩: %.1f\n", students[i].id, students[i].name, students[i].score); + } + + return 0; +} \ No newline at end of file diff --git a/cKit/Sort/c_LSDRadixSort.c b/cKit/Sort/c_LSDRadixSort.c new file mode 100644 index 0000000..63eeed6 --- /dev/null +++ b/cKit/Sort/c_LSDRadixSort.c @@ -0,0 +1,62 @@ +#include +#include + +/** + * Sorts an array of fixed-length strings stably using the LSD Radix Sort pipeline. + * Features upfront workspace allocations to completely eliminate heap thrashing in hot loops. + * + * Time Complexity: O(W * (N + R)) | Space Complexity: O(N + R) transient workspace memory + * @param sort Pointer to the initialized LSD config profile. + * @param arr Array of pointers to null-terminated char arrays (each must be at least W long). + * @param n Total number of strings inside the array. + */ +c_err_t c_LSDRadixSort_Sort(const c_LSDRadixSort_t* sort, char** arr, c_size_t n) { + if (sort == NULL || arr == NULL || sort->R == 0 || sort->W == 0) return C_ERR_PARAM; + if (n <= 1) return C_ERR_OK; // Trivial exit pass + + c_size_t R = sort->R; + c_size_t W = sort->W; + + // Upfront Transient Workspace Allocation: Eliminates allocation overhead in hot paths + char** aux = (char**)C_ALLOC(n * sizeof(char*)); + c_size_t* count = (c_size_t*)C_ALLOC((R + 1) * sizeof(c_size_t)); + + if (aux == NULL || count == NULL) { + C_FREE(aux); + C_FREE(count); + return C_ERR_NOMEM; + } + + // --- Core Iterative LSD Pass Loop --- + // Travel from right to left (Least Significant to Most Significant) + for (long long d = (long long)W - 1; d >= 0; d--) { + + // Reset the counting frequency registers + memset(count, 0, (R + 1) * sizeof(c_size_t)); + + // Pass A: Compute frequency counts using character indices as bucket addresses + for (c_size_t i = 0; i < n; i++) { + unsigned char c = (unsigned char)arr[i][d]; + count[c + 1]++; + } + + // Pass B: Transform frequencies into structural start indexes (Prefix Sums) + for (c_size_t r = 0; r < R; r++) { + count[r + 1] += count[r]; + } + + // Pass C: Distribute strings to the temporary aux array (Guarantees Stable Order Sorting) + for (c_size_t i = 0; i < n; i++) { + unsigned char c = (unsigned char)arr[i][d]; + aux[count[c]++] = arr[i]; + } + + // Pass D: Copy back copies natively to the primary tracking layout pointers + memcpy(arr, aux, n * sizeof(char*)); + } + + // Purge temporary scratchpad workspace containers cleanly + C_FREE(aux); + C_FREE(count); + return C_ERR_OK; +} diff --git a/cKit/Sort/c_LSDRadixSort.h b/cKit/Sort/c_LSDRadixSort.h new file mode 100644 index 0000000..f856788 --- /dev/null +++ b/cKit/Sort/c_LSDRadixSort.h @@ -0,0 +1,16 @@ +#ifndef INCLUDED_C_LSDRADIXSORT_H +#define INCLUDED_C_LSDRADIXSORT_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +typedef struct { + c_size_t R; // Alphabet size / Radix constraints (e.g., 256 for ASCII chars or bytes) + c_size_t W; // Fixed length key width criteria (number of sorting passes/characters) +} c_LSDRadixSort_t; + +c_err_t c_LSDRadixSort_Sort(const c_LSDRadixSort_t* sort, char** arr, c_size_t n); + + +#endif /*INCLUDED_C_LSDRADIXSORT_H*/ diff --git a/cKit/Sort/c_LSDRadixSort.t.c b/cKit/Sort/c_LSDRadixSort.t.c new file mode 100644 index 0000000..ff5bb76 --- /dev/null +++ b/cKit/Sort/c_LSDRadixSort.t.c @@ -0,0 +1,73 @@ +#include "c_LSDRadixSort.h" +#include +#include +#include + +// Your updated line tracing diagnostic macro +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \ + return C_FALSE; \ + } \ + } while(0) + +c_bool_t test_lsd_radix_sort_execution(void) { + c_LSDRadixSort_t sort; + sort.R = 256; // Standard extended ASCII byte alphabet boundaries + sort.W = 3; // Testing a fixed-width of exactly 3 characters per word + + c_size_t n = 6; + // Unsorted fixed-width test pool configurations + char* test_data[] = { + "DOG", + "CAT", + "COW", + "BAR", + "CAB", + "DIG" + }; + + // Allocate an array of modifiable pointers to replicate the application environment + char** arr = (char**)C_ALLOC(n * sizeof(char*)); + if (arr == NULL) return C_FALSE; + for (c_size_t i = 0; i < n; i++) arr[i] = test_data[i]; + + printf(" [LOG] Launching fixed-width Least-Significant-Digit Radix Sort...\n"); + c_err_t err = c_LSDRadixSort_Sort(&sort, arr, n); + + EXPECT_EQ(err, C_ERR_OK, "LSD sort engine returned unexpected runtime error code"); + + // Mathematically sorted verification checkpoints list mapping: + // Expected sorted sequence: BAR, CAB, CAT, COW, DIG, DOG + EXPECT_EQ(strcmp(arr[0], "BAR"), 0, "Sorted position 0 incorrect"); + EXPECT_EQ(strcmp(arr[1], "CAB"), 0, "Sorted position 1 incorrect"); + EXPECT_EQ(strcmp(arr[2], "CAT"), 0, "Sorted position 2 incorrect"); + EXPECT_EQ(strcmp(arr[3], "COW"), 0, "Sorted position 3 incorrect"); + EXPECT_EQ(strcmp(arr[4], "DIG"), 0, "Sorted position 4 incorrect"); + EXPECT_EQ(strcmp(arr[5], "DOG"), 0, "Sorted position 5 incorrect"); + + // Checkpoint 2: Validation check of stability bounds + // Because "DIG" and "DOG" share character 'D', the sorted sequence must strictly respect + // character 'I' vs 'O' sequence order boundaries. + EXPECT_EQ(strcmp(arr[4], "DIG") == 0 && strcmp(arr[5], "DOG") == 0, C_TRUE, "Sorting stability check failed"); + + printf(" [STAT] LSD Radix Sort verified successfully. Chronological Output: "); + for (c_size_t i = 0; i < n; i++) { + printf("%s ", arr[i]); + } + printf("\n"); + + C_FREE(arr); + return C_TRUE; +} + +int main(void) { + printf("=== Starting Framework Verification: Least-Significant-Digit Radix Sort ===\n"); + if (test_lsd_radix_sort_execution()) { + printf(" [PASS] Fixed-Width Stable Radix Sort Passes and Array Pointer Re-maps Verified.\n"); + } else { + printf(" [FAIL] Radix Sorting Pipe Matrix Evaluation Logic Anomaly Intercepted.\n"); + } + return 0; +} diff --git a/cKit/Sort/c_MSDRadixSort.c b/cKit/Sort/c_MSDRadixSort.c new file mode 100644 index 0000000..a47dfc0 --- /dev/null +++ b/cKit/Sort/c_MSDRadixSort.c @@ -0,0 +1,99 @@ +#include +#include + +/** + * Inline helper to safely extract a character at string offset d. + * Automatically maps a string's null terminator to a sentinel value of -1. + */ +C_STATIC_FORCE_INLINE +int c_MSDRadixSort_CharAt(const char* str, c_size_t d) { + if (str == NULL) return -1; + // Walk down to offset d without triggering a buffer overflow lookup violation + c_size_t i = 0; + while (i < d && str[i] != '\0') { + i++; + } + if (str[i] == '\0' || i < d) return -1; + return (unsigned char)str[i]; +} + +/** + * Core Private Recursive Sub-partition Sorting Subroutine. + * Shares a single pre-allocated auxiliary buffer across stack frames to prevent heap allocation overhead. + * + * @param lo Lower boundary index of the target partition array slice (inclusive). + * @param hi Upper boundary index of the target partition array slice (inclusive). + * @param d The current character string evaluation offset cursor. + */ +static void c_MSDRadixSort_SortRecursive(char** arr, long long lo, long long hi, c_size_t d, + c_size_t R, char** aux, c_size_t* count_buf) { + if (hi <= lo) return; + + // Cutoff to Insertion Sort for tiny sub-arrays can be added here for production fine-tuning. + + // Calculate sub-slice width and initialize count registers + c_size_t n = (c_size_t)(hi - lo + 1); + // Elements are shifted forward by +2 slots to gracefully absorb the -1 string end sentinel + memset(count_buf, 0, (R + 2) * sizeof(c_size_t)); + + // Pass A: Compute frequency buckets + for (long long i = lo; i <= hi; i++) { + int c = c_MSDRadixSort_CharAt(arr[i], d); + count_buf[c + 2]++; + } + + // Pass B: Transform frequencies into structural start indexes (Prefix Sums) + for (c_size_t r = 0; r < R + 1; r++) { + count_buf[r + 1] += count_buf[r]; + } + + // Pass C: Distribute strings stably to the temporary auxiliary workspace slice + for (long long i = lo; i <= hi; i++) { + int c = c_MSDRadixSort_CharAt(arr[i], d); + aux[count_buf[c + 1]++] = arr[i]; + } + + // Pass D: Copy back copies natively to the primary tracking layout pointers + for (long long i = lo; i <= hi; i++) { + arr[i] = aux[i - lo]; + } + + // Recursively sort sub-arrays for each character bucket + // Note: count_buf[0] handles strings that hit a terminal '\0' sentinel, so we skip it to prevent loops + for (c_size_t r = 0; r < R; r++) { + long long next_lo = lo + (long long)count_buf[r]; + long long next_hi = lo + (long long)count_buf[r + 1] - 1; + + if (next_hi > next_lo) { + c_MSDRadixSort_SortRecursive(arr, next_lo, next_hi, d + 1, R, aux, count_buf); + } + } +} + +/** + * Sorts an array of variable-length strings using the MSD Radix Sort pipeline. + * Guarantees zero runtime heap thrashing via upfront workspace pooling. + * + * Time Complexity: O(N * String_Length) optimal | Space Complexity: O(N + R) transient workspace memory + */ +c_err_t c_MSDRadixSort_Sort(const c_MSDRadixSort_t* sort, char** arr, c_size_t n) { + if (sort == NULL || arr == NULL || sort->R == 0) return C_ERR_PARAM; + if (n <= 1) return C_ERR_OK; + + // Upfront Transient Workspace Allocation: Eliminates allocation overhead in deep recursions + char** aux = (char**)C_ALLOC(n * sizeof(char*)); + c_size_t* count_buf = (c_size_t*)C_ALLOC((sort->R + 2) * sizeof(c_size_t)); + + if (aux == NULL || count_buf == NULL) { + C_FREE(aux); + C_FREE(count_buf); + return C_ERR_NOMEM; + } + + // Launch the core string-wise recursive partition tree + c_MSDRadixSort_SortRecursive(arr, 0, (long long)n - 1, 0, sort->R, aux, count_buf); + + C_FREE(aux); + C_FREE(count_buf); + return C_ERR_OK; +} diff --git a/cKit/Sort/c_MSDRadixSort.h b/cKit/Sort/c_MSDRadixSort.h new file mode 100644 index 0000000..77d8a24 --- /dev/null +++ b/cKit/Sort/c_MSDRadixSort.h @@ -0,0 +1,18 @@ +#ifndef INCLUDED_C_MSDRADIXSORT_H +#define INCLUDED_C_MSDRADIXSORT_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +typedef struct { + c_size_t R; // Alphabet size / Radix constraints (e.g., 256 for standard byte arrays) +} c_MSDRadixSort_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_MSDRadixSort_Sort(const c_MSDRadixSort_t* sort, char** arr, c_size_t n); + +#endif /*INCLUDED_C_MSDRADIXSORT_H*/ diff --git a/cKit/Sort/c_MSDRadixSort.t.c b/cKit/Sort/c_MSDRadixSort.t.c new file mode 100644 index 0000000..42224c0 --- /dev/null +++ b/cKit/Sort/c_MSDRadixSort.t.c @@ -0,0 +1,74 @@ +#include "c_MSDRadixSort.h" +#include +#include + +#include "c_Memory.h" + +// Your updated line tracing diagnostic macro +#define EXPECT_EQ(actual, expected, msg) \ + do { \ + if ((actual) != (expected)) { \ + printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \ + return C_FALSE; \ + } \ + } while(0) + +c_bool_t test_msd_radix_sort_execution(void) { + c_MSDRadixSort_t sort; + sort.R = 256; // Standard extended ASCII alphabet boundaries + + c_size_t n = 7; + // Unsorted variable-length string test pool configurations + char* test_data[] = { + "she", + "sells", + "seashells", + "by", + "the", + "sea", + "shore" + }; + + // Allocate an array of modifiable pointers to replicate the application environment + char** arr = (char**)C_ALLOC(n * sizeof(char*)); + if (arr == NULL) return C_FALSE; + for (c_size_t i = 0; i < n; i++) arr[i] = test_data[i]; + + printf(" [LOG] Launching variable-length Most-Significant-Digit Radix Sort...\n"); + c_err_t err = c_MSDRadixSort_Sort(&sort, arr, n); + + EXPECT_EQ(err, C_ERR_OK, "MSD sort engine returned unexpected runtime error code"); + + // Mathematically sorted verification checkpoints list mapping: + // Expected alphabetical sequence: by, sea, seashells, sells, she, shore, the + EXPECT_EQ(strcmp(arr[0], "by"), 0, "Sorted position 0 incorrect"); + EXPECT_EQ(strcmp(arr[1], "sea"), 0, "Sorted position 1 incorrect"); + EXPECT_EQ(strcmp(arr[2], "seashells"), 0, "Sorted position 2 incorrect"); + EXPECT_EQ(strcmp(arr[3], "sells"), 0, "Sorted position 3 incorrect"); + EXPECT_EQ(strcmp(arr[4], "she"), 0, "Sorted position 4 incorrect"); + EXPECT_EQ(strcmp(arr[5], "shore"), 0, "Sorted position 5 incorrect"); + EXPECT_EQ(strcmp(arr[6], "the"), 0, "Sorted position 6 incorrect"); + + // Checkpoint 2: Variable Length Validation check + // "sea" must strictly precede its extended prefix branch form "seashells" + EXPECT_EQ(strcmp(arr[1], "sea") == 0 && strcmp(arr[2], "seashells") == 0, C_TRUE, "Variable-length short-prefix ordering failed"); + + printf(" [STAT] MSD Radix Sort verified successfully. Alphabetic Output: "); + for (c_size_t i = 0; i < n; i++) { + printf("%s ", arr[i]); + } + printf("\n"); + + C_FREE(arr); + return C_TRUE; +} + +int main(void) { + printf("=== Starting Framework Verification: Most-Significant-Digit Radix Sort ===\n"); + if (test_msd_radix_sort_execution()) { + printf(" [PASS] Variable-Length Stable MSD Sorting and Upfront Scratchpad Allocations Verified.\n"); + } else { + printf(" [FAIL] Radix Partition Tree Structural Analysis Anomalies Intercepted.\n"); + } + return 0; +} diff --git a/cKit/Sort/c_MaxPQ.c b/cKit/Sort/c_MaxPQ.c new file mode 100644 index 0000000..b75ba80 --- /dev/null +++ b/cKit/Sort/c_MaxPQ.c @@ -0,0 +1,208 @@ +#include +#include + + +/** + * Internal macro helper to swap two arbitrary blocks of memory. + */ +C_STATIC_FORCE_INLINE +void c_HeapSwap(char* arr, c_size_t idx1, c_size_t idx2, c_size_t size, void* temp) { + if (idx1 == idx2) return; + char* a = arr + (idx1 * size); + char* b = arr + (idx2 * size); + memcpy(temp, a, size); + memcpy(a, b, size); + memcpy(b, temp, size); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_MaxPQ_Init(c_MaxPQ_t* pq, c_size_t initial_capacity, c_size_t element_size, + int (*compar)(const void*, const void*)) { + if (pq == NULL || element_size == 0 || compar == NULL) return C_ERR_PARAM; + + pq->capacity = (initial_capacity > 0) ? initial_capacity : 4; + pq->element_size = element_size; + pq->size = 0; + pq->compar = compar; + pq->data = C_ALLOC(pq->capacity * element_size); + + if (pq->data == NULL) return C_ERR_NOMEM; + return C_ERR_SUCCESS; +} + +void c_MaxPQ_Destroy(c_MaxPQ_t* pq) { + if (!pq) return; + C_FREE(pq->data); + pq->size = 0; + pq->capacity = 0; +} + +c_err_t c_MaxPQ_Push(c_MaxPQ_t* pq, const void* element) { + if (pq == NULL || element == NULL) return C_ERR_PARAM; + + char* arr = (char*)pq->data; + + // Capacity check: Scale memory boundary out if full + if (pq->size >= pq->capacity) { + c_size_t new_capacity = pq->capacity * 2; + // Reallocate manually utilizing framework macros + void* new_data = C_ALLOC(new_capacity * pq->element_size); + if (new_data == NULL) return C_ERR_NOMEM; // Allocation failure block + + memcpy(new_data, pq->data, pq->size * pq->element_size); + C_FREE(pq->data); + pq->data = new_data; + pq->capacity = new_capacity; + arr = (char*)pq->data; + } + + // Allocate stack cache buffer for object swapping routines +#define PQ_STACK_LIMIT 128 + char stack_buf[PQ_STACK_LIMIT]; + void* temp = (pq->element_size <= PQ_STACK_LIMIT) ? stack_buf : C_ALLOC(pq->element_size); + if (temp == NULL) return C_ERR_NOMEM; + + // Place new element at the bottom-most leaf slot of the max-heap tree + c_size_t current = pq->size; + memcpy(arr + (current * pq->element_size), element, pq->element_size); + pq->size++; + + // Sift-Up loop processing + while (current > 0) { + c_size_t parent = (current - 1) / 2; + + // Max-heap rule tracking: If child <= parent, tree balancing properties are correct + if (pq->compar(arr + (current * pq->element_size), arr + (parent * pq->element_size)) <= 0) { + break; + } + + c_HeapSwap(arr, current, parent, pq->element_size, temp); + current = parent; + } + + if (pq->element_size > PQ_STACK_LIMIT) C_FREE(temp); +#undef PQ_STACK_LIMIT + return C_ERR_OK; +} + + +c_err_t c_MaxPQ_Pop(c_MaxPQ_t* pq, void* output_buffer) { + if (!pq) return C_ERR_PARAM; + + if (pq->size == 0) return C_ERR_EMPTY; + + char* arr = (char*)pq->data; + + // If a tracking output buffer pointer is supplied, export the maximum item + if (output_buffer != NULL) { + memcpy(output_buffer, arr, pq->element_size); + } + + // Shrink element count tracking early + pq->size--; + + if (pq->size > 0) { + // Swap the last leaf node up to root position + memcpy(arr, arr + (pq->size * pq->element_size), pq->element_size); + +#define PQ_STACK_LIMIT 128 + char stack_buf[PQ_STACK_LIMIT]; + void* temp = (pq->element_size <= PQ_STACK_LIMIT) ? stack_buf : C_ALLOC(pq->element_size); + if (temp == NULL) return C_ERR_NOMEM; + + // Sift-Down balancing loop processing + c_size_t current = 0; + while (1) { + c_size_t left_child = (2 * current) + 1; + c_size_t right_child = (2 * current) + 2; + c_size_t largest = current; + + // Check if left child is larger than current node + if (left_child < pq->size && + pq->compar(arr + (left_child * pq->element_size), arr + (largest * pq->element_size)) > 0) { + largest = left_child; + } + + // Check if right child is larger than the currently tracked largest node + if (right_child < pq->size && + pq->compar(arr + (right_child * pq->element_size), arr + (largest * pq->element_size)) > 0) { + largest = right_child; + } + + // Balanced condition achieved + if (largest == current) { + break; + } + + c_HeapSwap(arr, current, largest, pq->element_size, temp); + current = largest; + } + + if (pq->element_size > PQ_STACK_LIMIT) C_FREE(temp); +#undef PQ_STACK_LIMIT + } + + return C_ERR_SUCCESS; +} + +void* c_MaxPQ_Peek(c_MaxPQ_t* pq) { + if (pq == NULL || pq->size == 0) return NULL; + return pq->data; // Root node is consistently maximum element +} + +c_err_t c_MaxPQ_Clear(c_MaxPQ_t* pq) { + if (pq == NULL) return C_ERR_PARAM; + + // Simply reset the size to zero. The underlying buffer remains allocated. + pq->size = 0; + + return C_ERR_OK; +} + +/** + * Manually resize the memory allocation capacity of the Priority Queue. + * @param pq Pointer to the Max Priority Queue instance. + * @param new_capacity The desired number of element slots to allocate. + * @return C_ERR_OK if successful, C_ERR_INVALID for bad arguments, + * or C_ERR_NOMEM if memory allocation fails. + */ +c_err_t c_MaxPQ_Resize(c_MaxPQ_t* pq, c_size_t new_capacity) { + if (pq == NULL) return C_ERR_PARAM; + + // Prevent shrinking below the current number of active elements inside the heap + if (new_capacity < pq->size) return C_ERR_PARAM; + + // If the capacity is already identical, skip processing to avoid memory overhead + if (new_capacity == pq->capacity) return C_ERR_OK; + + // Handle downsizing down to 0 safely if the queue is empty + if (new_capacity == 0) { + if (pq->data != NULL) { + C_FREE(pq->data); + pq->data = NULL; + } + pq->capacity = 0; + return C_ERR_OK; + } + + // Allocate a new memory block according to your framework specification + void* new_data = C_ALLOC(new_capacity * pq->element_size); + if (new_data == NULL) return C_ERR_NOMEM; + + // If there are existing active elements, move them to the newly allocated block + if (pq->size > 0 && pq->data != NULL) { + memcpy(new_data, pq->data, pq->size * pq->element_size); + } + + // Free the old array and bind the new tracking parameters + if (pq->data != NULL) { + C_FREE(pq->data); + } + pq->data = new_data; + pq->capacity = new_capacity; + + return C_ERR_OK; +} + diff --git a/cKit/Sort/c_MaxPQ.h b/cKit/Sort/c_MaxPQ.h new file mode 100644 index 0000000..3cbf731 --- /dev/null +++ b/cKit/Sort/c_MaxPQ.h @@ -0,0 +1,34 @@ +#ifndef INCLUDED_C_MAXPQ_H +#define INCLUDED_C_MAXPQ_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + void* data; // Flat char pointer block tracking memory slots + c_size_t element_size; // Size of each complex structure element in bytes + c_size_t capacity; // Maximum allocated element capacity + c_size_t size; // Current active element count inside the heap + int (*compar)(const void*, const void*); // Custom comparison rule pointer +} c_MaxPQ_t; + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_MaxPQ_Init(c_MaxPQ_t* pq, c_size_t initial_capacity, c_size_t element_size, + int (*compar)(const void*, const void*)); + +void c_MaxPQ_Destroy(c_MaxPQ_t* pq); + +c_err_t c_MaxPQ_Push(c_MaxPQ_t* pq, const void* element); +c_err_t c_MaxPQ_Pop(c_MaxPQ_t* pq, void* output_buffer); +void* c_MaxPQ_Peek(c_MaxPQ_t* pq); +c_err_t c_MaxPQ_Clear(c_MaxPQ_t* pq); +c_err_t c_MaxPQ_Resize(c_MaxPQ_t* pq, c_size_t new_capacity); + +#endif /*INCLUDED_C_MAXPQ_H*/ diff --git a/cKit/Sort/c_MaxPQ.t.c b/cKit/Sort/c_MaxPQ.t.c new file mode 100644 index 0000000..8584de7 --- /dev/null +++ b/cKit/Sort/c_MaxPQ.t.c @@ -0,0 +1,187 @@ +#include "c_MaxPQ.h" +#include +#include +#define EXPECT_EQ(val1, val2, msg) \ + do { \ + if ((val1) != (val2)) { printf(" [X] Failed: %s (Expected %d, got %d)\n", msg, (int)(val2), (int)(val1)); return false; } \ + } while(0) + +// Test complex entity +typedef struct { + int thread_id; + int priority_level; // Primary sorting metric for MaxPQ (higher level extract first) + char tag[16]; +} ThreadTask; + +// Max-Heap comparator: Returns positive if a > b +int compareTasksByPriority(const void* a, const void* b) { + const ThreadTask* t1 = (const ThreadTask*)a; + const ThreadTask* t2 = (const ThreadTask*)b; + return (t1->priority_level - t2->priority_level); +} + +// Test Case: Validates API Errors and Basic Operations +bool test_maxpq_lifecycle_and_errors(void) { + c_MaxPQ_t pq; + + // 1. Test invalid parameters during initialization + EXPECT_EQ(c_MaxPQ_Init(NULL, 10, sizeof(ThreadTask), compareTasksByPriority), C_ERR_PARAM, "NULL instance handle validation missing"); + EXPECT_EQ(c_MaxPQ_Init(&pq, 10, 0, compareTasksByPriority), C_ERR_PARAM, "Zero element size validation missing"); + EXPECT_EQ(c_MaxPQ_Init(&pq, 10, sizeof(ThreadTask), NULL), C_ERR_PARAM, "NULL comparator validation missing"); + + // 2. Correct initialization + EXPECT_EQ(c_MaxPQ_Init(&pq, 2, sizeof(ThreadTask), compareTasksByPriority), C_ERR_OK, "Valid configuration failed init"); + + // 3. Test empty bounds lookups + EXPECT_EQ(c_MaxPQ_Peek(&pq) == NULL, true, "Empty queue peek did not yield NULL"); + ThreadTask output; + EXPECT_EQ(c_MaxPQ_Pop(&pq, &output), C_ERR_EMPTY, "Empty queue extraction did not throw C_ERR_EMPTY"); + + // Clean up + c_MaxPQ_Destroy(&pq); + return true; +} + +// Test Case: Validates Max Extraction and Dynamic Scale Limits +bool test_maxpq_functional_flow(void) { + c_MaxPQ_t pq; + // Initialize with a tiny capacity of 2 to guarantee scaling logic triggers + c_MaxPQ_Init(&pq, 2, sizeof(ThreadTask), compareTasksByPriority); + + ThreadTask tasks[] = { + { 401, 12, "Low Prio" }, + { 402, 99, "Critical" }, + { 403, 50, "High Prio" }, + { 404, 75, "Urgent" } + }; + + // Push structures + EXPECT_EQ(c_MaxPQ_Push(&pq, &tasks[0]), C_ERR_OK, "Failed pushing task 0"); + EXPECT_EQ(c_MaxPQ_Push(&pq, &tasks[1]), C_ERR_OK, "Failed pushing task 1"); + EXPECT_EQ(c_MaxPQ_Push(&pq, &tasks[2]), C_ERR_OK, "Failed pushing task 2 (Scale Trigger)"); + EXPECT_EQ(c_MaxPQ_Push(&pq, &tasks[3]), C_ERR_OK, "Failed pushing task 3"); + + // Verify Peak Element maps to highest priority (99) + ThreadTask* peeked = (ThreadTask*)c_MaxPQ_Peek(&pq); + EXPECT_EQ(peeked != NULL && peeked->priority_level == 99, true, "Peek failed to return max item pointer"); + + ThreadTask extracted; + + // Pop 1: Priority 99 ("Critical") + EXPECT_EQ(c_MaxPQ_Pop(&pq, &extracted), C_ERR_OK, "Extraction loop 1 crashed"); + EXPECT_EQ(extracted.thread_id, 402, "Extracted sequence misaligned at item 1"); + + // Pop 2: Priority 75 ("Urgent") + EXPECT_EQ(c_MaxPQ_Pop(&pq, &extracted), C_ERR_OK, "Extraction loop 2 crashed"); + EXPECT_EQ(extracted.thread_id, 404, "Extracted sequence misaligned at item 2"); + + // Pop 3: Priority 50 ("High Prio") + EXPECT_EQ(c_MaxPQ_Pop(&pq, &extracted), C_ERR_OK, "Extraction loop 3 crashed"); + EXPECT_EQ(extracted.thread_id, 403, "Extracted sequence misaligned at item 3"); + + // Pop 4: Priority 12 ("Low Prio") + EXPECT_EQ(c_MaxPQ_Pop(&pq, &extracted), C_ERR_OK, "Extraction loop 4 crashed"); + EXPECT_EQ(extracted.thread_id, 401, "Extracted sequence misaligned at item 4"); + + // Confirm container has completely emptied out + EXPECT_EQ(pq.size, 0, "Active queue tracking counter failed to reach zero index boundary"); + + c_MaxPQ_Destroy(&pq); + return true; +} + +bool test_maxpq_clear_and_reuse(void) { + c_MaxPQ_t pq; + // Initialize with a capacity of 4 + c_MaxPQ_Init(&pq, 4, sizeof(ThreadTask), compareTasksByPriority); + + ThreadTask t1 = { 101, 10 }; + ThreadTask t2 = { 102, 50 }; + ThreadTask t3 = { 103, 30 }; + + // 1. Populate the priority queue + c_MaxPQ_Push(&pq, &t1); + c_MaxPQ_Push(&pq, &t2); + c_MaxPQ_Push(&pq, &t3); + EXPECT_EQ(pq.size, 3, "Queue size should be 3 before clearing"); + c_size_t saved_capacity = pq.capacity; + + // 2. Execute Clear Protocol + EXPECT_EQ(c_MaxPQ_Clear(NULL), C_ERR_PARAM, "Clearing NULL should yield C_ERR_PARAM"); + EXPECT_EQ(c_MaxPQ_Clear(&pq), C_ERR_OK, "Clearing active queue failed"); + + // 3. Assert structural properties after clearing + EXPECT_EQ(pq.size, 0, "Queue size must be reset to 0 after clear"); + EXPECT_EQ(pq.capacity, saved_capacity, "Capacity should remain unchanged after clear"); + EXPECT_EQ(c_MaxPQ_Peek(&pq) == NULL, true, "Cleared queue peek should return NULL"); + + ThreadTask dummy; + EXPECT_EQ(c_MaxPQ_Pop(&pq, &dummy), C_ERR_EMPTY, "Cleared queue pop should return C_ERR_EMPTY"); + + // 4. Reuse and re-populate the same queue (Verifying memory reuse) + ThreadTask t4 = { 201, 5 }; + ThreadTask t5 = { 202, 95 }; // This should become the new max root + + EXPECT_EQ(c_MaxPQ_Push(&pq, &t4), C_ERR_OK, "Pushing to cleared queue failed"); + EXPECT_EQ(c_MaxPQ_Push(&pq, &t5), C_ERR_OK, "Pushing second item to cleared queue failed"); + EXPECT_EQ(pq.size, 2, "Size did not increment properly after reuse"); + + // Verify extraction works perfectly post-clear + ThreadTask result; + EXPECT_EQ(c_MaxPQ_Pop(&pq, &result), C_ERR_OK, "Pop post-clear failed"); + EXPECT_EQ(result.thread_id, 202, "Max-Heap extraction broke after clearing and reusing queue"); + + c_MaxPQ_Destroy(&pq); + return true; +} + +bool test_maxpq_resize_behavior(void) { + c_MaxPQ_t pq; + // Initialize with a capacity of 4 + c_MaxPQ_Init(&pq, 4, sizeof(ThreadTask), compareTasksByPriority); + + ThreadTask t1 = { 101, 10 }; + ThreadTask t2 = { 102, 50 }; + + c_MaxPQ_Push(&pq, &t1); + c_MaxPQ_Push(&pq, &t2); + EXPECT_EQ(pq.size, 2, "Initial push setup size should be 2"); + EXPECT_EQ(pq.capacity, 4, "Initial capacity should be 4"); + + // 1. Guard Check: Attempting to shrink capacity below the active item count (size=2) must fail + EXPECT_EQ(c_MaxPQ_Resize(&pq, 1), C_ERR_PARAM, "Shrinking below current size did not fail safely"); + EXPECT_EQ(pq.capacity, 4, "Invalid resize operation altered internal capacity incorrectly"); + + // 2. Expansion Check: Expand capacity from 4 to 10 + EXPECT_EQ(c_MaxPQ_Resize(&pq, 10), C_ERR_OK, "Expanding valid memory blocks failed"); + EXPECT_EQ(pq.capacity, 10, "Capacity tracker failed to update to 10"); + EXPECT_EQ(pq.size, 2, "Active structural data sizes mutated during reallocation shift"); + + // 3. Contraction Check: Tighten memory footprints to fit data perfectly (shrink capacity to size=2) + EXPECT_EQ(c_MaxPQ_Resize(&pq, 2), C_ERR_OK, "Clamping pool capacity limits down to active size failed"); + EXPECT_EQ(pq.capacity, 2, "Capacity tracker failed to collapse down to 2"); + + // 4. Operational Integrity Check: Verify heap extraction still works flawlessly post-resize + ThreadTask result; + EXPECT_EQ(c_MaxPQ_Pop(&pq, &result), C_ERR_OK, "Pop post-resize failed"); + EXPECT_EQ(result.thread_id, 102, "Max element tracking corrupted during pointer re-mapping operations"); + + c_MaxPQ_Destroy(&pq); + return true; +} + + +int main(void) { + printf("=== Starting Custom Framework Testing for c_MaxPQ ===\n"); + + if (test_maxpq_lifecycle_and_errors()) printf(" [PASS] Test 1: Lifecycle Management & Error Guard Protocols Verified\n"); + if (test_maxpq_functional_flow()) printf(" [PASS] Test 2: Priority Tree Sifting Loops & Data Escalation Verified\n"); + if (test_maxpq_clear_and_reuse()) { + printf(" [PASS] Test: Clear, Capacity Preservation, and Queue Reuse Verified Successfully\n"); + } + if (test_maxpq_resize_behavior()) { + printf(" [PASS] Test: Manual Resize, Boundary Guard Protection, and Data Persistence Verified\n"); + } + printf("=== All Priority Queue Framework Tests Completed ===\n"); + return 0; +} diff --git a/cKit/Sort/c_MergeSort.c b/cKit/Sort/c_MergeSort.c new file mode 100644 index 0000000..da73c93 --- /dev/null +++ b/cKit/Sort/c_MergeSort.c @@ -0,0 +1,54 @@ +#include + +/** + * Internal merging routine for top-down merge sort. + */ +C_STATIC_FORCE_INLINE +void c_MergeInternal(char* arr, c_size_t left, c_size_t mid, c_size_t right, + c_size_t size, char* aux, + int (*compar)(const void*, const void*)) { + c_size_t i = left; + c_size_t j = mid + 1; + c_size_t k = left; + + // Copy the target segment into the auxiliary working buffer + memcpy(aux + (left * size), arr + (left * size), (right - left + 1) * size); + + // Merge back into the original array tracking sorted boundaries + while (i <= mid && j <= right) { + if (compar(aux + (i * size), aux + (j * size)) <= 0) { + memcpy(arr + (k * size), aux + (i * size), size); + i++; + } else { + memcpy(arr + (k * size), aux + (j * size), size); + j++; + } + k++; + } + + // Copy any remaining elements of the left sub-array if any + while (i <= mid) { + memcpy(arr + (k * size), aux + (i * size), size); + i++; + k++; + } + // Note: Remaining items on the right side are already natively sitting in the correct slots. +} + +/** + * Recursive structural block splitting segments into halves. + */ +void c_MergeSortSub(char* arr, c_size_t left, c_size_t right, c_size_t size, char* aux, + int (*compar)(const void*, const void*)) { + if (left >= right) return; + + c_size_t mid = left + (right - left) / 2; + + c_MergeSortSub(arr, left, mid, size, aux, compar); + c_MergeSortSub(arr, mid + 1, right, size, aux, compar); + + // Optimization: If the array segment is already naturally sorted, skip the merge routine + if (compar(arr + (mid * size), arr + ((mid + 1) * size)) > 0) { + c_MergeInternal(arr, left, mid, right, size, aux, compar); + } +} diff --git a/cKit/Sort/c_MergeSort.h b/cKit/Sort/c_MergeSort.h new file mode 100644 index 0000000..9f3af76 --- /dev/null +++ b/cKit/Sort/c_MergeSort.h @@ -0,0 +1,54 @@ +#ifndef INCLUDED_C_MERGESORT_H +#define INCLUDED_C_MERGESORT_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +void c_MergeSortSub(char* arr, c_size_t left, c_size_t right, c_size_t size, char* aux, + int (*compar)(const void*, const void*)); + +/** + * Top-Level Custom Framework Entry Point for Top-Down Merge Sort. + * Time Complexity: O(n log n) | Space Complexity: O(n) | Stable: Yes + */ +C_STATIC_FORCE_INLINE +void c_MergeSort(void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + if (base == NULL || num < 2 || size == 0) return; + + char* arr = (char*)base; + c_size_t total_bytes = num * size; + + // Optimization: Fallback to stack buffer allocation if overall payload fits locally + #define MERGE_STACK_LIMIT 512 + char stack_buf[MERGE_STACK_LIMIT]; + char* aux = NULL; + + if (total_bytes <= MERGE_STACK_LIMIT) { + aux = stack_buf; + } else { + aux = (char*)C_ALLOC(total_bytes); + if (aux == NULL) return; // Allocation safeguard + } + + // Execute recursive divide-and-conquer strategy + c_MergeSortSub(arr, 0, num - 1, size, aux, compar); + + if (total_bytes > MERGE_STACK_LIMIT) { + C_FREE(aux); + } + #undef MERGE_STACK_LIMIT +} + + +#endif /*INCLUDED_C_MERGESORT_H*/ diff --git a/cKit/Sort/c_MergeSort.t.c b/cKit/Sort/c_MergeSort.t.c new file mode 100644 index 0000000..899e851 --- /dev/null +++ b/cKit/Sort/c_MergeSort.t.c @@ -0,0 +1,102 @@ +#include "c_MergeSort.h" +#include +#include + +#define EXPECT_TRUE(cond, msg) \ + do { \ + if (!(cond)) { printf(" [X] Failed Assertion: %s\n", msg); return false; } \ + } while(0) + +// ---------------------------------------------------- +// Testing Datastructures & Comparison Helper Utilities +// ---------------------------------------------------- +typedef struct { + int primary_key; // Used for sorting + int original_pos; // Used to test stable tracking properties + char payload[64]; +} StableNode; + +int compareStableNodes(const void* a, const void* b) { + const StableNode* n1 = (const StableNode*)a; + const StableNode* n2 = (const StableNode*)b; + return (n1->primary_key > n2->primary_key) - (n1->primary_key < n2->primary_key); +} + +int compareIntegers(const void* a, const void* b) { + return (*(int*)a - *(int*)b); +} + +// ---------------------------------------------------- +// Unit Test Sub-routines +// ---------------------------------------------------- + +// Test 1: Guard limits on Null pointers and zero size footprints +bool test_merge_edge_cases(void) { + int* empty_ptr = NULL; + c_MergeSort(empty_ptr, 0, sizeof(int), compareIntegers); // Safe escape pipeline + + int singular[] = { 99 }; + c_MergeSort(singular, 1, sizeof(int), compareIntegers); + EXPECT_TRUE(singular[0] == 99, "Singular item modified unexpectedly"); + return true; +} + +// Test 2: Core stability metrics (elements with matching keys preserve initial order) +bool test_merge_sorting_stability(void) { + StableNode items[] = { + { 10, 1, "First Ten" }, + { 5, 2, "Five" }, + { 10, 3, "Second Ten" }, + { 1, 4, "One" }, + { 10, 5, "Third Ten" } + }; + c_size_t count = sizeof(items) / sizeof(items[0]); + + c_MergeSort(items, count, sizeof(StableNode), compareStableNodes); + + // Verify ordering sequence + EXPECT_TRUE(items[0].primary_key == 1, "Lowest structural key error"); + EXPECT_TRUE(items[1].primary_key == 5, "Mid tier sorting position error"); + + // Stability checks: Primary keys at index 2, 3, and 4 are all '10'. + // Their original_pos fields MUST step cleanly from 1 -> 3 -> 5. + EXPECT_TRUE(items[2].original_pos == 1, "Stability Broken on duplicate subset element 1"); + EXPECT_TRUE(items[3].original_pos == 3, "Stability Broken on duplicate subset element 2"); + EXPECT_TRUE(items[4].original_pos == 5, "Stability Broken on duplicate subset element 3"); + return true; +} + +// Test 3: Large scaling array to bypass the MERGE_STACK_LIMIT (512 Bytes) +bool test_merge_heap_allocation(void) { + // 20 items * 72 bytes per StableNode = 1440 Bytes (Exceeds stack buffer threshold) + StableNode massive_dataset[20]; + c_size_t total = 20; + + for (int i = 0; i < 20; i++) { + massive_dataset[i].primary_key = 20 - i; // Completely inverted structural setup + massive_dataset[i].original_pos = i; + } + + c_MergeSort(massive_dataset, total, sizeof(StableNode), compareStableNodes); + + // Scan through dataset confirming monotone strictly ascending continuity + for (c_size_t i = 0; i < total - 1; i++) { + EXPECT_TRUE(massive_dataset[i].primary_key <= massive_dataset[i+1].primary_key, + "Continuous stream scaling breakdown under memory swap logic redirection"); + } + return true; +} + +// ---------------------------------------------------- +// Testing Execution Entry Driver +// ---------------------------------------------------- +int main(void) { + printf("=== Starting Framework Unit Testing: Top-Down Merge Sort ===\n"); + + if (test_merge_edge_cases()) printf(" [PASS] Test 1: Empty Array & Edge Boundary Safety\n"); + if (test_merge_sorting_stability()) printf(" [PASS] Test 2: Sorting Structural Stability Preservation\n"); + if (test_merge_heap_allocation()) printf(" [PASS] Test 3: Heap Buffer Alloc Escalation (>512B Testing)\n"); + + printf("=== System Verification Sequence Completed ===\n"); + return 0; +} \ No newline at end of file diff --git a/cKit/Sort/c_MergeSortBU.c b/cKit/Sort/c_MergeSortBU.c new file mode 100644 index 0000000..a956dc2 --- /dev/null +++ b/cKit/Sort/c_MergeSortBU.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Sort/c_MergeSortBU.h b/cKit/Sort/c_MergeSortBU.h new file mode 100644 index 0000000..97b2a28 --- /dev/null +++ b/cKit/Sort/c_MergeSortBU.h @@ -0,0 +1,98 @@ +#ifndef INCLUDED_C_MERGESORTBU_H +#define INCLUDED_C_MERGESORTBU_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +/** + * Internal merging routine for iterative blocks. + * Re-used from the top-down logic to guarantee stability. + */ +C_STATIC_FORCE_INLINE +void c_MergeInternalBU(char* arr, c_size_t left, c_size_t mid, c_size_t right, + c_size_t size, char* aux, + int (*compar)(const void*, const void*)) { + c_size_t i = left; + c_size_t j = mid + 1; + c_size_t k = left; + + // Snapshot target segment into tracking auxiliary zone + memcpy(aux + (left * size), arr + (left * size), (right - left + 1) * size); + + // Merge operational slots back sequentially + while (i <= mid && j <= right) { + if (compar(aux + (i * size), aux + (j * size)) <= 0) { + memcpy(arr + (k * size), aux + (i * size), size); + i++; + } else { + memcpy(arr + (k * size), aux + (j * size), size); + j++; + } + k++; + } + + // Flush remaining items sitting on the left slice + while (i <= mid) { + memcpy(arr + (k * size), aux + (i * size), size); + i++; + k++; + } +} + +/** + * Top-Level Iterative Framework Entry Point for Bottom-Up Merge Sort. + * Time Complexity: O(n log n) | Space Complexity: O(n) | Stable: Yes | Call Stack: O(1) + */ +C_STATIC_FORCE_INLINE +void c_MergeSortBottomUp(void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + if (base == NULL || num < 2 || size == 0) return; + + char* arr = (char*)base; + c_size_t total_bytes = num * size; + + // Optimization: Fallback to stack buffer allocation if memory payload fits locally + #define MERGE_BU_STACK_LIMIT 512 + char stack_buf[MERGE_BU_STACK_LIMIT]; + char* aux = NULL; + + if (total_bytes <= MERGE_BU_STACK_LIMIT) { + aux = stack_buf; + } else { + aux = (char*)C_ALLOC(total_bytes); + if (aux == NULL) return; + } + + // Step-wise sub-array size doubling loop: 1, 2, 4, 8, 16... + for (c_size_t width = 1; width < num; width *= 2) { + // Iterate through segments by blocks of 2 * width + for (c_size_t left = 0; left < num - width; left += 2 * width) { + c_size_t mid = left + width - 1; + c_size_t right = C_MIN(left + (2 * width) - 1, num - 1); + + // Optimization: Skip merge phase if the adjacent sub-segments are already sequentially sorted + if (compar(arr + (mid * size), arr + ((mid + 1) * size)) > 0) { + c_MergeInternalBU(arr, left, mid, right, size, aux, compar); + } + } + } + + if (total_bytes > MERGE_BU_STACK_LIMIT) { + C_FREE(aux); + } + #undef MERGE_BU_STACK_LIMIT +} + + +#endif /*INCLUDED_C_MERGESORTBU_H*/ diff --git a/cKit/Sort/c_MergeSortBU.t.c b/cKit/Sort/c_MergeSortBU.t.c new file mode 100644 index 0000000..0f57d6d --- /dev/null +++ b/cKit/Sort/c_MergeSortBU.t.c @@ -0,0 +1,74 @@ +#include "c_MergeSortBU.h" +#include +#include + +#define EXPECT_TRUE(cond, msg) \ + do { \ + if (!(cond)) { printf(" [X] Failed Assertion: %s\n", msg); return false; } \ + } while(0) + +// Struct declaration enforcing alignment and tracking order metadata +typedef struct { + int key; + int initial_order; + char metadata[16]; +} TestNode; + +int compareTestNodes(const void* a, const void* b) { + const TestNode* n1 = (const TestNode*)a; + const TestNode* n2 = (const TestNode*)b; + return (n1->key > n2->key) - (n1->key < n2->key); +} + +// Test 1: Sorting arrays with sizes that are not powers of two (e.g., N = 7) +bool test_bu_non_power_of_two(void) { + int datasets[] = { 85, 24, 63, 45, 17, 31, 96 }; + c_size_t total = sizeof(datasets) / sizeof(datasets[0]); + + // Simple integer comparison handler + int cmp_int(const void* a, const void* b) { return (*(int*)a - *(int*)b); } + + c_MergeSortBottomUp(datasets, total, sizeof(int), cmp_int); + + for (c_size_t i = 0; i < total - 1; i++) { + EXPECT_TRUE(datasets[i] <= datasets[i+1], "Irregular block boundary sorted sequence tracking crash"); + } + return true; +} + +// Test 2: Multi-field structural stability checkpoint (N = 6) +bool test_bu_stability(void) { + TestNode records[] = { + { 40, 0, "Alpha" }, + { 20, 1, "Beta" }, + { 40, 2, "Gamma" }, + { 10, 3, "Delta" }, + { 40, 4, "Zeta" }, + { 20, 5, "Eta" } + }; + c_size_t count = sizeof(records) / sizeof(records[0]); + + c_MergeSortBottomUp(records, count, sizeof(TestNode), compareTestNodes); + + // Assert key orders are strictly sorted + EXPECT_TRUE(records[0].key == 10, "Base index misplacement"); + EXPECT_TRUE(records[1].key == 20 && records[2].key == 20, "Middle index sort tracking failed"); + EXPECT_TRUE(records[3].key == 40 && records[4].key == 40 && records[5].key == 40, "Tail elements misaligned"); + + // Assert stability: matching records must keep their initial relative positions + EXPECT_TRUE(records[1].initial_order == 1 && records[2].initial_order == 5, "Stability broken on key value 20"); + EXPECT_TRUE(records[3].initial_order == 0 && records[4].initial_order == 2 && records[5].initial_order == 4, + "Stability broken on key value 40"); + return true; +} + +// Test Driving Runner Subroutine +int main(void) { + printf("=== Starting Framework Unit Testing: Bottom-Up Merge Sort ===\n"); + + if (test_bu_non_power_of_two()) printf(" [PASS] Test 1: Non-Power-of-Two Odd Dataset Counts Handles Securely\n"); + if (test_bu_stability()) printf(" [PASS] Test 2: Iterative Stable Record Processing Validated\n"); + + printf("=== System Verification Sequence Completed ===\n"); + return 0; +} \ No newline at end of file diff --git a/cKit/Sort/c_MinPQ.c b/cKit/Sort/c_MinPQ.c new file mode 100644 index 0000000..67371e6 --- /dev/null +++ b/cKit/Sort/c_MinPQ.c @@ -0,0 +1,160 @@ +#include +#include + +/** + * Internal helper to swap two memory slots inside the heap array. + */ +C_STATIC_FORCE_INLINE +void c_MinPQ_HeapSwap(char* arr, c_size_t idx1, c_size_t idx2, c_size_t size, void* temp) { + if (idx1 == idx2) return; + char* a = arr + (idx1 * size); + char* b = arr + (idx2 * size); + memcpy(temp, a, size); + memcpy(a, b, size); + memcpy(b, temp, size); +} + +c_err_t c_MinPQ_Init(c_MinPQ_t* pq, c_size_t initial_capacity, c_size_t element_size, + int (*compar)(const void*, const void*)) { + if (pq == NULL || element_size == 0 || compar == NULL) return C_ERR_PARAM; + + pq->capacity = (initial_capacity > 0) ? initial_capacity : 4; + pq->element_size = element_size; + pq->size = 0; + pq->compar = compar; + pq->data = C_ALLOC(pq->capacity * element_size); + + return (pq->data != NULL) ? C_ERR_OK : C_ERR_NOMEM; +} + +void c_MinPQ_Destroy(c_MinPQ_t* pq) { + if (!pq) return; + C_FREE(pq->data); + pq->size = 0; + pq->capacity = 0; +} + +c_err_t c_MinPQ_Clear(c_MinPQ_t* pq) { + if (pq == NULL) return C_ERR_PARAM; + pq->size = 0; + return C_ERR_OK; +} + +c_err_t c_MinPQ_Resize(c_MinPQ_t* pq, c_size_t new_capacity) { + if (pq == NULL || new_capacity < pq->size) return C_ERR_PARAM; + if (new_capacity == pq->capacity) return C_ERR_OK; + + if (new_capacity == 0) { + if (pq->data != NULL) C_FREE(pq->data); + pq->data = NULL; + pq->capacity = 0; + return C_ERR_OK; + } + + void* new_data = C_ALLOC(new_capacity * pq->element_size); + if (new_data == NULL) return C_ERR_NOMEM; + + if (pq->size > 0 && pq->data != NULL) { + memcpy(new_data, pq->data, pq->size * pq->element_size); + } + + if (pq->data != NULL) C_FREE(pq->data); + pq->data = new_data; + pq->capacity = new_capacity; + + return C_ERR_OK; +} + +c_err_t c_MinPQ_Push(c_MinPQ_t* pq, const void* element) { + if (pq == NULL || element == NULL) return C_ERR_PARAM; + + char* arr = (char*)pq->data; + + // Handle dynamic capacity auto-doubling + if (pq->size >= pq->capacity) { + c_err_t err = c_MinPQ_Resize(pq, pq->capacity * 2); + if (err != C_ERR_OK) return err; + arr = (char*)pq->data; + } + + #define PQ_STACK_LIMIT 128 + char stack_buf[PQ_STACK_LIMIT]; + void* temp = (pq->element_size <= PQ_STACK_LIMIT) ? stack_buf : C_ALLOC(pq->element_size); + if (temp == NULL) return C_ERR_NOMEM; + + // Place element at the next available leaf position + c_size_t current = pq->size; + memcpy(arr + (current * pq->element_size), element, pq->element_size); + pq->size++; + + // Sift-Up for Min-Heap: Move up while element < parent + while (current > 0) { + c_size_t parent = (current - 1) / 2; + if (pq->compar(arr + (current * pq->element_size), arr + (parent * pq->element_size)) >= 0) { + break; + } + c_MinPQ_HeapSwap(arr, current, parent, pq->element_size, temp); + current = parent; + } + + if (pq->element_size > PQ_STACK_LIMIT) C_FREE(temp); + #undef PQ_STACK_LIMIT + return C_ERR_OK; +} + +c_err_t c_MinPQ_Pop(c_MinPQ_t* pq, void* output_buffer) { + if (pq == NULL) return C_ERR_PARAM; + if (pq->size == 0) return C_ERR_EMPTY; + + char* arr = (char*)pq->data; + + // Export root minimum element if buffer is provided + if (output_buffer != NULL) { + memcpy(output_buffer, arr, pq->element_size); + } + + pq->size--; + + if (pq->size > 0) { + // Move last leaf to root position + memcpy(arr, arr + (pq->size * pq->element_size), pq->element_size); + + #define PQ_STACK_LIMIT 128 + char stack_buf[PQ_STACK_LIMIT]; + void* temp = (pq->element_size <= PQ_STACK_LIMIT) ? stack_buf : C_ALLOC(pq->element_size); + if (temp == NULL) return C_ERR_NOMEM; + + // Sift-Down for Min-Heap: Swap with the smaller of the two children + c_size_t current = 0; + while (1) { + c_size_t left_child = (2 * current) + 1; + c_size_t right_child = (2 * current) + 2; + c_size_t smallest = current; + + if (left_child < pq->size && + pq->compar(arr + (left_child * pq->element_size), arr + (smallest * pq->element_size)) < 0) { + smallest = left_child; + } + if (right_child < pq->size && + pq->compar(arr + (right_child * pq->element_size), arr + (smallest * pq->element_size)) < 0) { + smallest = right_child; + } + if (smallest == current) { + break; + } + + c_MinPQ_HeapSwap(arr, current, smallest, pq->element_size, temp); + current = smallest; + } + + if (pq->element_size > PQ_STACK_LIMIT) C_FREE(temp); + #undef PQ_STACK_LIMIT + } + + return C_ERR_OK; +} + +void* c_MinPQ_Peek(c_MinPQ_t* pq) { + if (pq == NULL || pq->size == 0) return NULL; + return pq->data; +} diff --git a/cKit/Sort/c_MinPQ.h b/cKit/Sort/c_MinPQ.h new file mode 100644 index 0000000..cd3e25e --- /dev/null +++ b/cKit/Sort/c_MinPQ.h @@ -0,0 +1,35 @@ +#ifndef INCLUDED_C_MINPQ_H +#define INCLUDED_C_MINPQ_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + void* data; + c_size_t element_size; + c_size_t capacity; + c_size_t size; + int (*compar)(const void*, const void*); +} c_MinPQ_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_MinPQ_Init(c_MinPQ_t* pq, c_size_t initial_capacity, c_size_t element_size, + int (*compar)(const void*, const void*)); + +void c_MinPQ_Destroy(c_MinPQ_t* pq); + +c_err_t c_MinPQ_Clear(c_MinPQ_t* pq); +c_err_t c_MinPQ_Resize(c_MinPQ_t* pq, c_size_t new_capacity); + +c_err_t c_MinPQ_Push(c_MinPQ_t* pq, const void* element); +c_err_t c_MinPQ_Pop(c_MinPQ_t* pq, void* output_buffer); +void* c_MinPQ_Peek(c_MinPQ_t* pq); + +#endif /*INCLUDED_C_MINPQ_H*/ diff --git a/cKit/Sort/c_MinPQ.t.c b/cKit/Sort/c_MinPQ.t.c new file mode 100644 index 0000000..18eb854 --- /dev/null +++ b/cKit/Sort/c_MinPQ.t.c @@ -0,0 +1,82 @@ +#include "c_MinPQ.h" +#include +#include + + +#define EXPECT_EQ(val1, val2, msg) \ + do { \ + if ((val1) != (val2)) { printf(" [X] Failed: %s (Expected %d, got %d)\n", msg, (int)(val2), (int)(val1)); return false; } \ + } while(0) + +// Complex element structural payload +typedef struct { + int packet_id; + int latency_ms; // Primary sorting metric (smaller value = higher priority = popped first) +} NetworkPacket; + +// Min-Heap comparator: Returns negative if a < b +int comparePacketsByLatency(const void* a, const void* b) { + return (((NetworkPacket*)a)->latency_ms - ((NetworkPacket*)b)->latency_ms); +} + +// Validation Pipeline Function +bool test_minpq_functional_flow(void) { + c_MinPQ_t pq; + // Initialize with a tiny capacity of 2 to verify dynamic allocation resize steps + EXPECT_EQ(c_MinPQ_Init(&pq, 2, sizeof(NetworkPacket), comparePacketsByLatency), C_ERR_OK, "Initialization failed"); + + NetworkPacket packets[] = { + { 7001, 120 }, // High Latency + { 7002, 15 }, // Ultra-low Latency (Should be extracted first!) + { 7003, 45 }, // Mid Latency + { 7004, 80 } // High-mid Latency + }; + + // 1. Push Operations + EXPECT_EQ(c_MinPQ_Push(&pq, &packets[0]), C_ERR_OK, "Push 0 failed"); + EXPECT_EQ(c_MinPQ_Push(&pq, &packets[1]), C_ERR_OK, "Push 1 failed"); + EXPECT_EQ(c_MinPQ_Push(&pq, &packets[2]), C_ERR_OK, "Push 2 failed (Triggers automatic resize expansion)"); + EXPECT_EQ(c_MinPQ_Push(&pq, &packets[3]), C_ERR_OK, "Push 3 failed"); + EXPECT_EQ(pq.size, 4, "Active queue size layout tracker mismatched"); + + // 2. Peek Verification (Must target the absolute minimum latency element) + NetworkPacket* peeked = (NetworkPacket*)c_MinPQ_Peek(&pq); + EXPECT_EQ(peeked != NULL && peeked->latency_ms == 15, true, "Peek failed to find minimum item root pointer"); + + // 3. Sequential Extraction (Pop validation) + NetworkPacket out; + + // Pop 1: Latency 15 (ID 7002) + EXPECT_EQ(c_MinPQ_Pop(&pq, &out), C_ERR_OK, "Pop 1 crashed"); + EXPECT_EQ(out.packet_id, 7002, "Min extraction sequence failed at item 1"); + + // Pop 2: Latency 45 (ID 7003) + EXPECT_EQ(c_MinPQ_Pop(&pq, &out), C_ERR_OK, "Pop 2 crashed"); + EXPECT_EQ(out.packet_id, 7003, "Min extraction sequence failed at item 2"); + + // Pop 3: Latency 80 (ID 7004) + EXPECT_EQ(c_MinPQ_Pop(&pq, &out), C_ERR_OK, "Pop 3 crashed"); + EXPECT_EQ(out.packet_id, 7004, "Min extraction sequence failed at item 3"); + + // Pop 4: Latency 120 (ID 7001) + EXPECT_EQ(c_MinPQ_Pop(&pq, &out), C_ERR_OK, "Pop 4 crashed"); + EXPECT_EQ(out.packet_id, 7001, "Min extraction sequence failed at item 4"); + + // 4. Empty and Bounds Verification + EXPECT_EQ(pq.size, 0, "Queue did not empty out properly"); + EXPECT_EQ(c_MinPQ_Pop(&pq, &out), C_ERR_EMPTY, "Pop on empty queue did not throw C_ERR_EMPTY"); + + c_MinPQ_Destroy(&pq); + return true; +} + +int main(void) { + printf("=== Starting Custom Framework Testing for c_MinPQ ===\n"); + + if (test_minpq_functional_flow()) { + printf(" [PASS] Test: Min-Heap Push/Pop Sifting and Resizing Sequences Verified Successfully\n"); + } + + printf("=== All Min-Priority Queue Framework Tests Completed ===\n"); + return 0; +} diff --git a/cKit/Sort/c_QuickSort.c b/cKit/Sort/c_QuickSort.c new file mode 100644 index 0000000..5411fa2 --- /dev/null +++ b/cKit/Sort/c_QuickSort.c @@ -0,0 +1,84 @@ +#include + + +/** + * Internal macro helper to swap two arbitrary blocks of memory of given size. + */ +C_STATIC_FORCE_INLINE +void c_SwapInternal(char* a, char* b, c_size_t size, void* temp) { + if (a == b) return; + memcpy(temp, a, size); + memcpy(a, b, size); + memcpy(b, temp, size); +} + +/** + * Chooses the median of left, center, and right elements as the pivot, + * hides it at (right - 1), and returns a pointer to it. + */ +C_STATIC_FORCE_INLINE +void* c_MedianOfThree(char* arr, long long left, long long right, c_size_t size, + void* temp, int (*compar)(const void*, const void*)) { + long long center = left + (right - left) / 2; + + // Order left, center, right + if (compar(arr + (left * size), arr + (center * size)) > 0) { + c_SwapInternal(arr + (left * size), arr + (center * size), size, temp); + } + if (compar(arr + (left * size), arr + (right * size)) > 0) { + c_SwapInternal(arr + (left * size), arr + (right * size), size, temp); + } + if (compar(arr + (center * size), arr + (right * size)) > 0) { + c_SwapInternal(arr + (center * size), arr + (right * size), size, temp); + } + + // Place pivot at position (right - 1) + c_SwapInternal(arr + (center * size), arr + ((right - 1) * size), size, temp); + return arr + ((right - 1) * size); +} + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +void c_QuickSortSub(char* arr, long long left, long long right, c_size_t size, void* temp, + int (*compar)(const void*, const void*)) { + // Optimization: Fallback to manual insertion sort for tiny arrays to prune deep recursion + if (left + 10 > right) { + // Basic Inline Insertion Sort boundary loop + for (long long i = left + 1; i <= right; i++) { + memcpy(temp, arr + (i * size), size); + long long j = i; + while (j > left && compar(arr + ((j - 1) * size), temp) > 0) { + memcpy(arr + (j * size), arr + ((j - 1) * size), size); + j--; + } + memcpy(arr + (j * size), temp, size); + } + return; + } + + // Retrieve Median Pivot + c_MedianOfThree(arr, left, right, size, temp, compar); + + long long i = left; + long long j = right - 1; + + // Hoare Partitioning Loop + while (1) { + while (compar(arr + ((++i) * size), arr + ((right - 1) * size)) < 0); + while (compar(arr + ((--j) * size), arr + ((right - 1) * size)) > 0); + if (i < j) { + c_SwapInternal(arr + (i * size), arr + (j * size), size, temp); + } else { + break; + } + } + + // Restore pivot to its correct final slot + c_SwapInternal(arr + (i * size), arr + ((right - 1) * size), size, temp); + + // Recursively execute left and right segments + c_QuickSortSub(arr, left, i - 1, size, temp, compar); + c_QuickSortSub(arr, i + 1, right, size, temp, compar); +} diff --git a/cKit/Sort/c_QuickSort.h b/cKit/Sort/c_QuickSort.h new file mode 100644 index 0000000..7fe94ae --- /dev/null +++ b/cKit/Sort/c_QuickSort.h @@ -0,0 +1,57 @@ +#ifndef INCLUDED_C_QUICKSORT_H +#define INCLUDED_C_QUICKSORT_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * Recursive partitioning sub-routine. + */ + +void c_QuickSortSub(char* arr, long long left, long long right, c_size_t size, void* temp, + int (*compar)(const void*, const void*)); + +/** + * Top-Level Framework Entry Point for Quicksort. + * Time Complexity: O(n log n) average | Space Complexity: O(log n) call stack | Stable: No + */ +C_STATIC_FORCE_INLINE +void c_QuickSort(void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + if (base == NULL || num < 2 || size == 0) return; + + char* arr = (char*)base; + + // Optimization: Use a local stack buffer if element size fits comfortably + #define QSORT_STACK_LIMIT 128 + char stack_buf[QSORT_STACK_LIMIT]; + void* temp = NULL; + + if (size <= QSORT_STACK_LIMIT) { + temp = stack_buf; + } else { + temp = C_ALLOC(size); + if (temp == NULL) return; + } + + // Invoke processing across signed range bounds safely + c_QuickSortSub(arr, 0, (long long)num - 1, size, temp, compar); + + if (size > QSORT_STACK_LIMIT) { + C_FREE(temp); + } + #undef QSORT_STACK_LIMIT +} + + +#endif /*INCLUDED_C_QUICKSORT_H*/ diff --git a/cKit/Sort/c_QuickSort.t.c b/cKit/Sort/c_QuickSort.t.c new file mode 100644 index 0000000..d9a6ded --- /dev/null +++ b/cKit/Sort/c_QuickSort.t.c @@ -0,0 +1,68 @@ +#include "c_QuickSort.h" +#include +#include + + +#define EXPECT_TRUE(cond, msg) \ + do { \ + if (!(cond)) { printf(" [X] Failed Assertion: %s\n", msg); return false; } \ + } while(0) + +typedef struct { + int id; + double performance_index; +} TaskMetric; + +int compareTasks(const void* a, const void* b) { + const TaskMetric* t1 = (const TaskMetric*)a; + const TaskMetric* t2 = (const TaskMetric*)b; + if (t1->performance_index < t2->performance_index) return -1; + if (t1->performance_index > t2->performance_index) return 1; + return 0; +} + +int compareInts(const void* a, const void* b) { + return (*(int*)a - *(int*)b); +} + +// Test 1: Sorting completely pre-sorted lists (Guards against worst-case naive pivot selections) +bool test_qsort_presorted(void) { + int ordered[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + c_size_t total = sizeof(ordered) / sizeof(ordered); + + c_QuickSort(ordered, total, sizeof(int), compareInts); + + for (c_size_t i = 0; i < total - 1; i++) { + EXPECT_TRUE(ordered[i] <= ordered[i+1], "Pre-sorted sequence tracking error occurred"); + } + return true; +} + +// Test 2: Dense duplicate key distribution array +bool test_qsort_duplicates(void) { + TaskMetric metrics[] = { + {101, 9.5}, {102, 5.0}, {103, 9.5}, {104, 2.1}, {105, 5.0}, + {106, 9.5}, {107, 2.1}, {108, 9.5}, {109, 5.0}, {110, 2.1}, + {111, 5.0}, {112, 9.5} + }; + c_size_t count = sizeof(metrics) / sizeof(metrics); + + c_QuickSort(metrics, count, sizeof(TaskMetric), compareTasks); + + for (c_size_t i = 0; i < count - 1; i++) { + EXPECT_TRUE(metrics[i].performance_index <= metrics[i+1].performance_index, + "Duplicate float element tracking collapsed under Hoare pointers"); + } + return true; +} + +// Testing Execution Entry Driver +int main(void) { + printf("=== Starting Framework Unit Testing: Quicksort ===\n"); + + if (test_qsort_presorted()) printf(" [PASS] Test 1: Pre-Sorted Array Pivot Anti-Degradation Passed\n"); + if (test_qsort_duplicates()) printf(" [PASS] Test 2: Highly Repetitive Element Distribution Sorted Successfully\n"); + + printf("=== System Verification Sequence Completed ===\n"); + return 0; +} diff --git a/cKit/Sort/c_QuickSortIterative.c b/cKit/Sort/c_QuickSortIterative.c new file mode 100644 index 0000000..8e050e0 --- /dev/null +++ b/cKit/Sort/c_QuickSortIterative.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Sort/c_QuickSortIterative.h b/cKit/Sort/c_QuickSortIterative.h new file mode 100644 index 0000000..4aac3cb --- /dev/null +++ b/cKit/Sort/c_QuickSortIterative.h @@ -0,0 +1,103 @@ +#ifndef INCLUDED_C_QUICKSORTITERATIVE_H +#define INCLUDED_C_QUICKSORTITERATIVE_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * Internal macro helper to swap two arbitrary blocks of memory. + */ +C_STATIC_FORCE_INLINE +void c_SwapInternal(char* a, char* b, c_size_t size, void* temp) { + if (a == b) return; + memcpy(temp, a, size); + memcpy(a, b, size); + memcpy(b, temp, size); +} + +/** + * Standard Lomuto or Hoare-style tracking partition loop. + * Uses the rightmost element as the pivot for flat linear execution. + */ +C_STATIC_FORCE_INLINE +long long c_PartitionIterative(char* arr, long long left, long long right, c_size_t size, + void* temp, int (*compar)(const void*, const void*)) { + char* pivot = arr + (right * size); + long long i = left - 1; + + for (long long j = left; j < right; j++) { + if (compar(arr + (j * size), pivot) <= 0) { + i++; + c_SwapInternal(arr + (i * size), arr + (j * size), size, temp); + } + } + c_SwapInternal(arr + ((i + 1) * size), arr + (right * size), size, temp); + return (i + 1); +} + +/** + * Top-Level Custom Framework Entry Point for Non-Recursive Quicksort. + * Time Complexity: O(n log n) average | Space Complexity: O(log n) explicit stack | Stable: No + */ +C_STATIC_FORCE_INLINE +void c_QuickSortIterative(void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + if (base == NULL || num < 2 || size == 0) return; + + char* arr = (char*)base; + + // Optimization: Stack buffer allocation for pivot element swaps +#define QSORT_STACK_LIMIT 128 + char stack_buf[QSORT_STACK_LIMIT]; + void* temp = (size <= QSORT_STACK_LIMIT) ? stack_buf : C_ALLOC(size); + if (temp == NULL) return; + + // Allocate the explicit partition boundary stack. + // Max required stack depth for range tracking is 2 * ceil(log2(num)) + 2. + // For a 64-bit address space, 128 slots safely handles any possible array size. + long long range_stack[128]; + long long top = -1; + + // Push initial array boundaries onto the tracking stack + range_stack[++top] = 0; + range_stack[++top] = (long long)num - 1; + + // Keep processing partitions until the explicit boundary stack is empty + while (top >= 0) { + // Pop right and left boundaries + long long right = range_stack[top--]; + long long left = range_stack[top--]; + + // Execute linear pivot segmentation + long long p = c_PartitionIterative(arr, left, right, size, temp, compar); + + // If there are elements on the left side of the pivot, push their range to the stack + if (p - 1 > left) { + range_stack[++top] = left; + range_stack[++top] = p - 1; + } + + // If there are elements on the right side of the pivot, push their range to the stack + if (p + 1 < right) { + range_stack[++top] = p + 1; + range_stack[++top] = right; + } + } + + if (size > QSORT_STACK_LIMIT) { + C_FREE(temp); + } +#undef QSORT_STACK_LIMIT +} + + +#endif /*INCLUDED_C_QUICKSORTITERATIVE_H*/ diff --git a/cKit/Sort/c_QuickSortIterative.t.c b/cKit/Sort/c_QuickSortIterative.t.c new file mode 100644 index 0000000..109a01d --- /dev/null +++ b/cKit/Sort/c_QuickSortIterative.t.c @@ -0,0 +1,66 @@ +#include "c_QuickSortIterative.h" +#include +#include +#define EXPECT_TRUE(cond, msg) \ + do { \ + if (!(cond)) { printf(" [X] Failed Assertion: %s\n", msg); return false; } \ + } while(0) + +typedef struct { + int uid; + int priority; +} ThreadTask; + +int compareTasks(const void* a, const void* b) { + const ThreadTask* t1 = (const ThreadTask*)a; + const ThreadTask* t2 = (const ThreadTask*)b; + return (t1->priority - t2->priority); +} + +int comparePlainInts(const void* a, const void* b) { + return (*(int*)a - *(int*)b); +} + +// Test 1: Sorting a highly unsorted, random layout sequence +bool test_iterative_qsort_random(void) { + int datasets[] = { 42, 12, 89, 23, 7, 56, 34, 91, 15, 68 }; + c_size_t total = sizeof(datasets) / sizeof(datasets); + + c_QuickSortIterative(datasets, total, sizeof(int), comparePlainInts); + + for (c_size_t i = 0; i < total - 1; i++) { + EXPECT_TRUE(datasets[i] <= datasets[i+1], "Iterative sorting sequence tracking error occurred"); + } + return true; +} + +// Test 2: Multi-field structure priority scheduling validation +bool test_iterative_qsort_struct(void) { + ThreadTask tasks[] = { + { 1001, 5 }, + { 1002, 1 }, + { 1003, 9 }, + { 1004, 3 }, + { 1005, 5 } + }; + c_size_t count = sizeof(tasks) / sizeof(tasks); + + c_QuickSortIterative(tasks, count, sizeof(ThreadTask), compareTasks); + + for (c_size_t i = 0; i < count - 1; i++) { + EXPECT_TRUE(tasks[i].priority <= tasks[i+1].priority, + "Struct elements misaligned during iterative stack operations"); + } + return true; +} + +// Testing Execution Entry Driver +int main(void) { + printf("=== Starting Framework Unit Testing: Iterative Quicksort ===\n"); + + if (test_iterative_qsort_random()) printf(" [PASS] Test 1: Random Disordered Data Array Sorted Successfully\n"); + if (test_iterative_qsort_struct()) printf(" [PASS] Test 2: Complex Struct Priority Segments Sorted Successfully\n"); + + printf("=== System Verification Sequence Completed ===\n"); + return 0; +} \ No newline at end of file diff --git a/cKit/Sort/c_SelectionSort.c b/cKit/Sort/c_SelectionSort.c new file mode 100644 index 0000000..33a3cd9 --- /dev/null +++ b/cKit/Sort/c_SelectionSort.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Sort/c_SelectionSort.h b/cKit/Sort/c_SelectionSort.h new file mode 100644 index 0000000..950305a --- /dev/null +++ b/cKit/Sort/c_SelectionSort.h @@ -0,0 +1,67 @@ +#ifndef INCLUDED_C_SELECTIONSORT_H +#define INCLUDED_C_SELECTIONSORT_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * Generic Selection Sort Function + * @param base Pointer to the first element of the array to be sorted + * @param num Number of elements in the array + * @param size Size of each element in bytes + * @param compar Pointer to the comparison function + */ +C_STATIC_FORCE_INLINE +void c_SelectionSort(void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + // Avoid execution if array is empty or has only one element + if (base == NULL || num < 2 || size == 0) return; + + char* arr = (char*)base; + + // OPTIMIZATION: Use stack allocation for small element sizes + // to bypass heap overhead completely during force-inlining. +#define STACK_LIMIT 128 + char stack_buf[STACK_LIMIT]; + void* temp = NULL; + + if (size <= STACK_LIMIT) { + temp = stack_buf; + } else { + temp = C_ALLOC(size); + if (temp == NULL) return; + } + + for (c_size_t i = 0; i < num - 1; i++) { + c_size_t minIndex = i; + + for (c_size_t j = i + 1; j < num; j++) { + if (compar(arr + (j * size), arr + (minIndex * size)) < 0) { + minIndex = j; + } + } + + if (minIndex != i) { + memcpy(temp, arr + (i * size), size); + memcpy(arr + (i * size), arr + (minIndex * size), size); + memcpy(arr + (minIndex * size), temp, size); + } + } + + // Only trigger free if it was actually allocated from the heap + if (size > STACK_LIMIT) { + C_FREE(temp); + } +#undef STACK_LIMIT +} + +#endif /*INCLUDED_C_SELECTIONSORT_H*/ diff --git a/cKit/Sort/c_SelectionSort.t.c b/cKit/Sort/c_SelectionSort.t.c new file mode 100644 index 0000000..2cad1be --- /dev/null +++ b/cKit/Sort/c_SelectionSort.t.c @@ -0,0 +1,45 @@ +#include "c_SelectionSort.h" +#include +#include + +typedef struct { + char title[50]; + int pubYear; + double price; +} Book; + +// Custom comparison function: Sort by publication year ascending +int compareBooksByYear(const void* a, const void* b) { + const Book* b1 = (const Book*)a; + const Book* b2 = (const Book*)b; + + if (b1->pubYear < b2->pubYear) return -1; + if (b1->pubYear > b2->pubYear) return 1; + return 0; +} + +int main() { + // Initialize an unsorted library array + Book library[] = { + {"The C Programming Language", 1978, 42.50}, + {"Clean Code", 2008, 35.00}, + {"Introduction to Algorithms", 1990, 85.99}, + {"Design Patterns", 1994, 54.95} + }; + size_t count = sizeof(library) / sizeof(library[0]); + + printf("Before Sorting:\n"); + for (size_t i = 0; i < count; i++) { + printf("Year: %d | Title: %s\n", library[i].pubYear, library[i].title); + } + + // Call generic selection sort + c_SelectionSort(library, count, sizeof(Book), compareBooksByYear); + + printf("\nAfter Sorting (By Year Ascending):\n"); + for (size_t i = 0; i < count; i++) { + printf("Year: %d | Title: %s\n", library[i].pubYear, library[i].title); + } + + return 0; +} \ No newline at end of file diff --git a/cKit/Sort/c_ShellSort.c b/cKit/Sort/c_ShellSort.c new file mode 100644 index 0000000..f8dbcc3 --- /dev/null +++ b/cKit/Sort/c_ShellSort.c @@ -0,0 +1 @@ +#include diff --git a/cKit/Sort/c_ShellSort.h b/cKit/Sort/c_ShellSort.h new file mode 100644 index 0000000..6761df2 --- /dev/null +++ b/cKit/Sort/c_ShellSort.h @@ -0,0 +1,71 @@ +#ifndef INCLUDED_C_SHELLSORT_H +#define INCLUDED_C_SHELLSORT_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +#ifndef INCLUDED_C_MEMORY_H +#include +#endif /*INCLUDED_C_MEMORY_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +C_STATIC_FORCE_INLINE +void c_ShellSort(void* base, c_size_t num, c_size_t size, + int (*compar)(const void*, const void*)) { + if (base == NULL || num < 2 || size == 0) return; + + char* arr = (char*)base; + + // Stack optimization for memory swap space +#define STACK_LIMIT 128 + char stack_buf[STACK_LIMIT]; + void* temp = NULL; + + if (size <= STACK_LIMIT) { + temp = stack_buf; + } else { + temp = C_ALLOC(size); + if (temp == NULL) return; + } + + // Using Knuth's gap sequence: h = h * 3 + 1 (1, 4, 13, 40, 121, ...) + c_size_t gap = 1; + while (gap < num / 3) { + gap = gap * 3 + 1; + } + + // Start with the largest gap and work down to a gap of 1 + while (gap > 0) { + for (c_size_t i = gap; i < num; i++) { + // temp = arr[i] + memcpy(temp, arr + (i * size), size); + + c_size_t j = i; + + // Shift elements of the gap-sorted variant until the correct position is found + // Loop guards prevent underflow on unsigned c_size_t subtraction (j >= gap) + while (j >= gap && compar(arr + ((j - gap) * size), temp) > 0) { + // arr[j] = arr[j - gap] + memcpy(arr + (j * size), arr + ((j - gap) * size), size); + j -= gap; + } + + // arr[j] = temp + memcpy(arr + (j * size), temp, size); + } + // Reduce the gap + gap /= 3; + } + + if (size > STACK_LIMIT) { + C_FREE(temp); + } +#undef STACK_LIMIT +} + + +#endif /*INCLUDED_C_SHELLSORT_H*/ diff --git a/cKit/Sort/c_ShellSort.t.c b/cKit/Sort/c_ShellSort.t.c new file mode 100644 index 0000000..af6e620 --- /dev/null +++ b/cKit/Sort/c_ShellSort.t.c @@ -0,0 +1,116 @@ +#include "c_ShellSort.h" +#include +#include + +// 单元测试断言宏 +#define EXPECT_TRUE(cond, msg) \ + do { \ + if (!(cond)) { printf(" [X] 失败: %s\n", msg); return false; } \ + } while(0) + + +// 复杂结构体元素:员工信息(用于小内存/栈分配测试) +typedef struct { + int id; + char name[16]; + int score; +} Employee; + +// 极其庞大的结构体元素(用于逼出堆分配测试,超过 STACK_LIMIT) +typedef struct { + int id; + char massive_payload[256]; // 超过 128 字节限制 +} LargeTask; + +// 1. 比较器:按员工积分 (score) 升序 +int compareEmpByScore(const void* a, const void* b) { + const Employee* e1 = (const Employee*)a; + const Employee* e2 = (const Employee*)b; + return (e1->score > e2->score) - (e1->score < e2->score); +} + +// 2. 比较器:按大任务 ID 降序 +int compareTaskByIdDesc(const void* a, const void* b) { + const LargeTask* t1 = (const LargeTask*)a; + const LargeTask* t2 = (const LargeTask*)b; + return (t2->id > t1->id) - (t2->id < t1->id); +} + +// 3. 比较器:普通整型升序 +int compareInt(const void* a, const void* b) { + return (*(int*)a - *(int*)b); +} + +// 测试用例 1:极限边界(空数组或单元素数组不崩溃) +bool test_boundary_cases() { + int* empty_arr = NULL; + c_ShellSort(empty_arr, 0, sizeof(int), compareInt); // 传 NULL 不应崩溃 + + int single_elem[] = { 42 }; + c_ShellSort(single_elem, 1, sizeof(int), compareInt); // 1个元素不处理 + EXPECT_TRUE(single_elem[0] == 42, "单元素数组值被篡改"); + + return true; +} + +// 测试用例 2:完全逆序数组的排序(触发大量 Gap 步进调整) +bool test_reverse_array() { + Employee emps[] = { + {4, "Manager", 90}, + {3, "Leader", 80}, + {2, "Senior", 70}, + {1, "Junior", 60} + }; + c_size_t num = sizeof(emps) / sizeof(emps[0]); + + c_ShellSort(emps, num, sizeof(Employee), compareEmpByScore); + + // 预期结果:按积分 60, 70, 80, 90 升序 + EXPECT_TRUE(emps[0].score == 60 && emps[3].score == 90, "逆序数组排序未完全生效"); + for(c_size_t i = 0; i < num - 1; i++) { + EXPECT_TRUE(emps[i].score <= emps[i+1].score, "逆序序列排序后仍不满足单调递增"); + } + return true; +} + +// 测试用例 3:包含大量相同主键的复杂数组(测试减治和覆盖分支) +bool test_duplicate_keys() { + Employee emps[] = { + {1, "A", 100}, {2, "B", 50}, {3, "C", 100}, {4, "D", 50}, {5, "E", 100} + }; + c_size_t num = sizeof(emps) / sizeof(emps[0]); + + c_ShellSort(emps, num, sizeof(Employee), compareEmpByScore); + + EXPECT_TRUE(emps[0].score == 50 && emps[1].score == 50, "相同项未能归拢到前半段"); + EXPECT_TRUE(emps[2].score == 100 && emps[4].score == 100, "相同项未能归拢到后半段"); + return true; +} + +// 测试用例 4:大体积结构体(单元素 > 128 字节,强制触发 C_ALLOC 堆内存分配) +bool test_large_struct_heap() { + LargeTask tasks[] = { + {10, "Payload A"}, {99, "Payload B"}, {5, "Payload C"}, {40, "Payload D"} + }; + c_size_t num = sizeof(tasks) / sizeof(tasks[0]); + + // 采用【降序】比较器 + c_ShellSort(tasks, num, sizeof(LargeTask), compareTaskByIdDesc); + + // 预期结果:ID 降序排序 -> 99, 40, 10, 5 + EXPECT_TRUE(tasks[0].id == 99, "堆分配大结构体首位未命中最大值"); + EXPECT_TRUE(tasks[3].id == 5, "堆分配大结构体末位未命中最小值"); + return true; +} + +int main() { + printf("=== 开始 c_ShellSort 框架级单元测试 ===\n"); + + if (test_boundary_cases()) printf("[PASS] 用例 1: 极限边界条件测试通过\n"); + if (test_reverse_array()) printf("[PASS] 用例 2: 逆序复杂元素排序通过\n"); + if (test_duplicate_keys()) printf("[PASS] 用例 3: 密集重复键稳定性分支通过\n"); + if (test_large_struct_heap()) printf("[PASS] 用例 4: 堆分配(>128B)大元素排序通过\n"); + + printf("\n=== 所有测试执行完毕 ===\n"); + return 0; +} diff --git a/cKit/c_Config.h.in b/cKit/c_Config.h.in new file mode 100644 index 0000000..ceecc0e --- /dev/null +++ b/cKit/c_Config.h.in @@ -0,0 +1,11 @@ +#ifndef INCLUDED_C_CONFIG_H +#define INCLUDED_C_CONFIG_H + +#define C_SIZEOF_VOID_P @CMAKE_SIZEOF_VOID_P@ + +#ifndef C_SIZEOF_VOID_P +#define C_SIZEOF_VOID_P sizeof(void*) +#endif + + +#endif /* INCLUDED_C_CONFIG_H */ diff --git a/cNLP/CMake/create_library_from_dirs.cmake b/cNLP/CMake/create_library_from_dirs.cmake new file mode 100644 index 0000000..fcdb233 --- /dev/null +++ b/cNLP/CMake/create_library_from_dirs.cmake @@ -0,0 +1,83 @@ +# ============================================================================== +# 宏定义:CREATE_LIBRARY_FROM_DIRS +# 参数 1: TARGET_NAME - 要生成的库的名称 +# 参数 2: LIB_TYPE - 库类型(STATIC 或 SHARED) +# 参数 3: SOURCE_DIRS - 需要扫描源码的目录列表(分号分隔的字符串) +# 参数 4: EXCLUDE_FILES - 需要额外过滤的文件名列表(分号分隔的字符串,支持正则) +# ============================================================================== +macro(create_library_from_dirs TARGET_NAME LIB_TYPE SOURCE_DIRS EXCLUDE_FILES) + set(ALL_SOURCES "") + add_library(${TARGET_NAME} ${LIB_TYPE} "") + # 1. 遍历传入的每一个目录 + foreach(DIR ${SOURCE_DIRS}) + # 统一处理相对路径和绝对路径 + if(NOT IS_ABSOLUTE "${DIR}") + set(DIR "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}") + endif() + + # 检查目录是否存在,防止写错路径导致报错 + if(EXISTS "${DIR}") + # 2. 收集当前目录下所有的 .c, .s, .S 文件 + file(GLOB ALL_FILES + "${DIR}/*.c" + "${DIR}/*.s" + "${DIR}/*.S" + ) + target_include_directories(${TARGET_NAME} PUBLIC ${DIR}) + message(STATUS "[${TARGET_NAME}] INCLUDE ${DIR}") + + + foreach(FILE_PATH ${ALL_FILES}) + # 获取纯文件名(例如:Compiler_GNUC.c),方便精准匹配 + get_filename_component(FILE_NAME "${FILE_PATH}" NAME) + + # 检查是否匹配自定义的排除列表 + set(SHOULD_EXCLUDE FALSE) + foreach(EX_PATTERN ${EXCLUDE_FILES}) + # 支持完整文件名匹配或正则表达式匹配 + if("${FILE_NAME}" STREQUAL "${EX_PATTERN}" OR "${FILE_NAME}" MATCHES "${EX_PATTERN}") + set(SHOULD_EXCLUDE TRUE) + break() + endif() + endforeach() + + # 4. 执行过滤判断 + if(FILE_PATH MATCHES "\\.t\\.c$") + # 过滤单元测试文件 + list(APPEND UNIT_TEST_SOURCES ${FILE_PATH}) + elseif(SHOULD_EXCLUDE) + # 过滤用户指定的特定文件 + message(STATUS "[${TARGET_NAME}] EXCLUDED specific file: ${FILE_NAME} from target ${TARGET_NAME}") + else() + # 留下的才是有效的库源码 + list(APPEND ALL_SOURCES ${FILE_PATH}) + endif() + endforeach() + else() + message(WARNING "[${TARGET_NAME}] Directory does not exist: ${DIR}") + endif() + endforeach() + + # 4. 确保找到了有效的源代码再创建库 + list(LENGTH ALL_SOURCES SOURCE_COUNT) + if(SOURCE_COUNT GREATER 0) +# add_library(${TARGET_NAME} ${LIB_TYPE} ${ALL_SOURCES}) + target_sources(${TARGET_NAME} PUBLIC ${ALL_SOURCES}) + message(STATUS "[${TARGET_NAME}] Created ${LIB_TYPE} library '${TARGET_NAME}' with ${SOURCE_COUNT} source files.") + foreach (item IN LISTS ALL_SOURCES) + message(STATUS "[${TARGET_NAME}] SOURCE: ${item}") + endforeach () + else() + message(FATAL_ERROR "No valid source files found for target: ${TARGET_NAME}") + endif() + + list(LENGTH UNIT_TEST_SOURCES UNIT_TEST_COUNT) + if(UNIT_TEST_COUNT GREATER 0) + foreach (item IN LISTS UNIT_TEST_SOURCES) + get_filename_component(TestUnit ${item} NAME_WLE) + message(STATUS "[${TARGET_NAME}] TEST: ${TestUnit} - ${item}") + add_executable(${TestUnit} ${item}) + target_link_libraries(${TestUnit} PUBLIC ${TARGET_NAME}) + endforeach () + endif () +endmacro() \ No newline at end of file diff --git a/cNLP/CMakeLists.txt b/cNLP/CMakeLists.txt new file mode 100644 index 0000000..9fd86d5 --- /dev/null +++ b/cNLP/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.15) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/CMake) +get_filename_component(FOLDER_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +project(${FOLDER_NAME}) +enable_language(C) +enable_language(ASM) + +set(CMAKE_C_STANDARD 11) + +include(create_library_from_dirs) +create_library_from_dirs(${PROJECT_NAME} STATIC + "Library" + "") +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) +target_link_libraries(${PROJECT_NAME} PUBLIC cKit) diff --git a/cNLP/Library/c_NlpStringList.c b/cNLP/Library/c_NlpStringList.c new file mode 100644 index 0000000..5acd44e --- /dev/null +++ b/cNLP/Library/c_NlpStringList.c @@ -0,0 +1,102 @@ +#include +#include + +#define DEFAULT_INIT_CAPACITY 4 + +c_err_t c_NlpStringList_Init(c_NlpStringList_t* list, c_size_t capacity) { + if (!list ) return C_ERR_PARAM; + list->count = 0; + list->capacity = (capacity==0)?DEFAULT_INIT_CAPACITY:capacity; // Start small, grow exponentially + list->items = (char**)C_ALLOC(sizeof(char*) * list->capacity); + if (!list->items) return C_ERR_NOMEM; + return C_ERR_OK; +} + +void c_NlpStringList_Destroy(c_NlpStringList_t* list) { + if (!list) return; + if (list->items) { + for (size_t i = 0; i < list->count; i++) { + C_FREE(list->items[i]); + } + C_FREE(list->items); + } + list->count = 0; + list->capacity = 0; +} + +#include + +/** + * @brief Appends a length-bounded string segment to the string list. + * @param list Pointer to the active string list instance. + * @param str Pointer to the source string segment. + * @param str_length The length of characters to copy. + * @return c_err_t C_ERR_OK on success, C_ERR_PARAM on invalid inputs, or C_ERR_NOMEM on allocation failure. + */ +c_err_t c_NlpStringList_Add(c_NlpStringList_t* list, const char* str, c_size_t str_length) { + if (!list || !list->items || !str || str_length == 0) { + return C_ERR_PARAM; + } + + // 1. Double the internal storage capacity exponentially if the array bounds hit limits + if (list->count >= list->capacity) { + c_size_t new_capacity = list->capacity == 0 ? 4 : list->capacity * 2; + char** new_items = (char**)C_ALLOC(sizeof(char*) * new_capacity); + if (!new_items) return C_ERR_NOMEM; + + if (list->items && list->count > 0) { + memcpy(new_items, list->items, sizeof(char*) * list->count); + } + C_FREE(list->items); + list->items = new_items; + list->capacity = new_capacity; + } + + // 2. Allocate an isolated target heap layout tracking buffer segment (+1 for '\0') + char* copy = (char*)C_ALLOC(str_length + 1); + if (!copy) return C_ERR_NOMEM; + + // 3. Duplicate raw segment content and secure the terminal null byte + memcpy(copy, str, str_length); + copy[str_length] = '\0'; + + list->items[list->count++] = copy; + return C_ERR_OK; +} + +/** + * @brief Appends a null-terminated string to the string list. + * @param list Pointer to the active string list instance. + * @param str Pointer to the null-terminated source string. + * @return c_err_t C_ERR_OK on success, or parameter/ OOM error codes. + */ +c_err_t c_NlpStringList_AddStr(c_NlpStringList_t* list, const char* str) { + if (!str) return C_ERR_PARAM; + return c_NlpStringList_Add(list, str, strlen(str)); +} + + +c_err_t c_NlpStringList_Remove(c_NlpStringList_t* list, c_size_t index) { + // 1. Guard against invalid instances and out-of-bound indices + if (!list || index >= list->count) { + return C_ERR_PARAM; + } + + // 2. Safely free the dynamic string allocation to prevent memory leaks + if (list->items[index] != NULL) { + C_FREE(list->items[index]); + } + + // 3. Shift subsequent string pointers left to fill the gap + const c_size_t num_elements_to_shift = list->count - index - 1; + if (num_elements_to_shift > 0) { + memmove(&list->items[index], + &list->items[index + 1], + sizeof(char*) * num_elements_to_shift); + } + + // 4. Decrement the structural item count + list->count--; + + return C_ERR_OK; +} diff --git a/cNLP/Library/c_NlpStringList.h b/cNLP/Library/c_NlpStringList.h new file mode 100644 index 0000000..02b377b --- /dev/null +++ b/cNLP/Library/c_NlpStringList.h @@ -0,0 +1,31 @@ +#ifndef INCLUDED_C_NLPSTRINGLIST_H +#define INCLUDED_C_NLPSTRINGLIST_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + char** items; // Array of string pointers + c_size_t count; // Current number of items in the list + c_size_t capacity; // Total allocated capacity of the items array +} c_NlpStringList_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_NlpStringList_Init(c_NlpStringList_t* self, c_size_t capacity); + +void c_NlpStringList_Destroy(c_NlpStringList_t* self); + +c_err_t c_NlpStringList_Add(c_NlpStringList_t* list, const char* str, c_size_t str_length); + +c_err_t c_NlpStringList_AddStr(c_NlpStringList_t* list, const char* str); + +c_err_t c_NlpStringList_Remove(c_NlpStringList_t* list, c_size_t index); + +#endif /*INCLUDED_C_NLPSTRINGLIST_H*/ diff --git a/cNLP/Library/c_NlpStringList.t.c b/cNLP/Library/c_NlpStringList.t.c new file mode 100644 index 0000000..d31dce3 --- /dev/null +++ b/cNLP/Library/c_NlpStringList.t.c @@ -0,0 +1,110 @@ +#include "c_NlpStringList.h" +#include +#include + + +#define RUN_LIST_TEST(condition, test_name) \ + do { \ + printf("[TEST] %s... ", test_name); \ + if (condition) { \ + printf("\033[32mPASSED\033[0m\n"); \ + } else { \ + printf("\033[31mFAILED\033[0m (at Line %d)\n", __LINE__); \ + return C_ERR_FAIL; \ + } \ + } while(0) + + +/** + * @brief Complete unit testing suite for the updated c_NlpStringList component. + * @return c_err_t Returns C_ERR_OK if all testing blocks pass. + */ +c_err_t c_NlpStringList_UnitTest(void) { + c_NlpStringList_t list; + c_err_t err; + + printf("==================================================\n"); + printf(" STARTING C_NLPSTRINGLIST UNIT TESTING \n"); + printf("==================================================\n"); + + /* 1. Defensive Boundary Parameter Safety Tests */ + RUN_LIST_TEST(c_NlpStringList_Init(NULL, 10) == C_ERR_PARAM, "Init handles NULL structure pointer"); + RUN_LIST_TEST(c_NlpStringList_AddStr(NULL, "data") == C_ERR_PARAM, "Add safely rejects NULL list context"); + RUN_LIST_TEST(c_NlpStringList_AddStr(&list, NULL) == C_ERR_PARAM, "Add safely rejects NULL input strings (before init)"); + RUN_LIST_TEST(c_NlpStringList_Remove(NULL, 0) == C_ERR_PARAM, "Remove safely rejects NULL list reference"); + + /* 2. Custom Configured Capacity Initialization Test */ + err = c_NlpStringList_Init(&list, 2); // Initialized with small capacity to ease expansion verification + RUN_LIST_TEST(err == C_ERR_OK, "Initialization with explicit capacity returns C_ERR_OK"); + RUN_LIST_TEST(list.count == 0, "Structural items counter starts at 0"); + RUN_LIST_TEST(list.capacity == 2, "Allocated capacity exactly mirrors input parameter"); + RUN_LIST_TEST(list.items != NULL, "Internal cluster storage pointer successfully bound"); + + /* 3. Sequential Elements Add & Read Validation */ + err = c_NlpStringList_AddStr(&list, "Apple"); + err |= c_NlpStringList_AddStr(&list, "Banana"); + + RUN_LIST_TEST(err == C_ERR_OK, "Successfully pushed elements up to capacity limit"); + RUN_LIST_TEST(list.count == 2, "Count correctly calculated at 2"); + RUN_LIST_TEST(list.capacity == 2, "Capacity remains unchanged before hitting boundary"); + RUN_LIST_TEST(strcmp(list.items[0], "Apple") == 0, "Slot 0 retains string 'Apple'"); + RUN_LIST_TEST(strcmp(list.items[1], "Banana") == 0, "Slot 1 retains string 'Banana'"); + + /* 4. Automated Array Expansion and Reallocation Safeguard */ + err = c_NlpStringList_AddStr(&list, "Cherry"); // Hits boundary, forcing C_ALLOC internal upscale expansion + RUN_LIST_TEST(err == C_ERR_OK, "Pushed 3rd string successfully past boundary limits"); + RUN_LIST_TEST(list.count == 3, "Count advanced to 3"); + RUN_LIST_TEST(list.capacity == 4, "Capacity doubled exponentially from 2 to 4"); + RUN_LIST_TEST(strcmp(list.items[2], "Cherry") == 0, "Post-reallocation element validation holds 'Cherry'"); + + /* 5. Memory Shift Extraction Performance (Remove Verification) */ + // Current setup state: ["Apple", "Banana", "Cherry"] + + // Test wild indices out of bounds protection + RUN_LIST_TEST(c_NlpStringList_Remove(&list, 999) == C_ERR_PARAM, "Rejects wild indices beyond context array boundary"); + RUN_LIST_TEST(c_NlpStringList_Remove(&list, list.count) == C_ERR_PARAM, "Rejects index exactly hitting edge limit"); + + // Remove middle slot element ("Banana") + err = c_NlpStringList_Remove(&list, 1); + RUN_LIST_TEST(err == C_ERR_OK, "Successfully extracted middle entry at Index 1"); + RUN_LIST_TEST(list.count == 2, "Total item count updated smoothly down to 2"); + // Verify remaining contents moved left continuously without forming holes + RUN_LIST_TEST(strcmp(list.items[0], "Apple") == 0, "Slot 0 continues holding 'Apple'"); + RUN_LIST_TEST(strcmp(list.items[1], "Cherry") == 0, "Slot 1 successfully consolidated to 'Cherry'"); + + // Remove remaining front element ("Apple") + err = c_NlpStringList_Remove(&list, 0); + RUN_LIST_TEST(err == C_ERR_OK, "Extracted front leading element at Index 0"); + RUN_LIST_TEST(list.count == 1, "Count downshifted to 1"); + RUN_LIST_TEST(strcmp(list.items[0], "Cherry") == 0, "Slot 0 now rolled over to 'Cherry'"); + + /* 6. Multi-Byte UTF-8 String Asset Preservation */ + err = c_NlpStringList_AddStr(&list, "深度学习与大模型"); + RUN_LIST_TEST(err == C_ERR_OK, "Added complex multi-byte Chinese token string"); + RUN_LIST_TEST(strcmp(list.items[1], "深度学习与大模型") == 0, "Multi-byte raw tracking array verification holds"); + + /* 7. Garbage Collection & Prevent Secondary Dangling Freeing */ + c_NlpStringList_Destroy(&list); + RUN_LIST_TEST(list.items == NULL, "Array storage pointer nullified upon destruction sequence"); + RUN_LIST_TEST(list.count == 0, "Counters reset cleanly to 0"); + RUN_LIST_TEST(list.capacity == 0, "Capacity indicators initialized to 0"); + + // Idempotent test validation + c_NlpStringList_Destroy(&list); + c_NlpStringList_Destroy(NULL); + printf("[TEST] Double structural destruction safety... \033[32mPASSED\033[0m\n"); + + printf("==================================================\n"); + printf("\033[32mSUCCESS: ALL REVISED LIST SPECIFICATION TESTS PASSED!\033[0m\n"); + printf("==================================================\n"); + + return C_ERR_OK; +} + + +int main(void) { + if (c_NlpStringList_UnitTest() != C_ERR_OK) { + return -1; + } + return 0; +} diff --git a/cNLP/Library/c_NlpTrie.c b/cNLP/Library/c_NlpTrie.c new file mode 100644 index 0000000..29e90dd --- /dev/null +++ b/cNLP/Library/c_NlpTrie.c @@ -0,0 +1,515 @@ +#include + +#include "c_Memory.h" +#include + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define ALPHABET_SIZE 256 + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +struct c_NlpTrieNode_t { + struct c_NlpTrieNode_t* children[ALPHABET_SIZE]; + c_bool_t is_end_of_word; +}; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +C_STATIC_FORCE_INLINE +c_NlpTrieNode_t* c_NlpTrieNode_Create(void) { + c_NlpTrieNode_t* node = (c_NlpTrieNode_t*)C_ALLOC(sizeof(*node)); + if (node == NULL) { + return NULL; + } + node->is_end_of_word = C_FALSE; + for (int i = 0; i < ALPHABET_SIZE; i++) { + node->children[i] = NULL; + } + return node; +} + +C_STATIC_FORCE_INLINE +void c_NlpTrieNode_Destroy(c_NlpTrieNode_t* node) { + C_FREE(node); +} + +static void c_NlpTrieNode_FreeSubtree(c_NlpTrieNode_t* node) { + if (node == NULL) { + return; + } + // 递归释放所有存活的子分支 + for (int i = 0; i < ALPHABET_SIZE; i++) { + if (node->children[i] != NULL) { + c_NlpTrieNode_FreeSubtree(node->children[i]); + } + } + // 使用用户指定的底层接口释放当前节点 + c_NlpTrieNode_Destroy(node); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_NlpTrie_Init(c_NlpTrie_t* self) { + if (!self) return C_ERR_PARAM; + self->root = c_NlpTrieNode_Create(); + if (self->root == NULL) { + return C_ERR_NOMEM; + } + self->size = 0; + return C_ERR_OK; +} + +void c_NlpTrie_Destroy(c_NlpTrie_t* self) { + if (self == NULL || self->root == NULL) { + return; + } + // 调用带深度扫描的子树释放函数 + c_NlpTrieNode_FreeSubtree(self->root); + self->root = NULL; + self->size = 0; +} + +c_err_t c_NlpTrie_Insert(c_NlpTrie_t* self, const char* word) { + if (!self || !self->root || !word) { + return C_ERR_PARAM; + } + + c_NlpTrieNode_t* curr = self->root; + for (c_size_t i = 0; word[i] != '\0'; i++) { + // 转换为 uint8_t,确保中文字符等多字节编码(负值)在 0~255 范围内安全索引 + const uint8_t index = (uint8_t)word[i]; + + if (curr->children[index] == NULL) { + curr->children[index] = c_NlpTrieNode_Create(); + if (curr->children[index] == NULL) { + return C_ERR_NOMEM; // 内存池耗尽或分配失败 + } + } + curr = curr->children[index]; + } + + curr->is_end_of_word = C_TRUE; + self->size++; + return C_ERR_OK; +} + +c_bool_t c_NlpTrie_Contains(c_NlpTrie_t* self, const char* word) { + if (!self || !self->root || !word) { + return C_FALSE; + } + + c_NlpTrieNode_t* curr = self->root; + for (c_size_t i = 0; word[i] != '\0'; i++) { + const uint8_t index = (uint8_t)word[i]; + + if (curr->children[index] == NULL) { + return C_FALSE; + } + curr = curr->children[index]; + } + + return curr->is_end_of_word; +} + +c_bool_t c_NlpTrie_HasStartWith(c_NlpTrie_t* self, const char* word) { + if (!self || !self->root || !word) { + return C_FALSE; + } + + c_NlpTrieNode_t* curr = self->root; + for (c_size_t i = 0; word[i] != '\0'; i++) { + const uint8_t index = (uint8_t)word[i]; + + if (curr->children[index] == NULL) { + return C_FALSE; + } + curr = curr->children[index]; + } + + return C_TRUE; +} + +c_err_t c_NlpTrie_LongestPrefixOf(c_NlpTrie_t* self, const char* text, char* result, c_size_t res_max_len) { + if (!self || !self->root || !text || !result || res_max_len == 0) { + return C_ERR_PARAM; + } + + // 初始化返回值为空字符串 + result[0] = '\0'; + + c_NlpTrieNode_t* curr = self->root; + c_size_t longest_len = 0; // 记录最长匹配的字符长度 + + // 遍历输入文本 + for (c_size_t i = 0; text[i] != '\0'; i++) { + const uint8_t index = (uint8_t)text[i]; + + // 字符链断开,无法继续匹配更长的前缀,退出循环 + if (curr->children[index] == NULL) { + break; + } + + curr = curr->children[index]; + + // 如果当前节点是一个完整单词的结尾,更新最长匹配长度 + if (curr->is_end_of_word) { + longest_len = i + 1; + } + } + + // 如果找到了有效匹配,且长度在安全缓冲区范围内,进行拷贝 + if (longest_len > 0) { + c_size_t copy_len = (longest_len < res_max_len) ? longest_len : (res_max_len - 1); + strncpy(result, text, copy_len); + result[copy_len] = '\0'; + } + + return C_ERR_OK; +} + +c_size_t c_NlpTrie_LongestPrefixOfLen(c_NlpTrie_t* self, const char* text) { + if (!self || !self->root || !text) { + return 0; + } + + c_NlpTrieNode_t* curr = self->root; + c_size_t longest_len = 0; + + for (c_size_t i = 0; text[i] != '\0'; i++) { + const uint8_t index = (uint8_t)text[i]; + if (curr->children[index] == NULL) { + break; + } + curr = curr->children[index]; + if (curr->is_end_of_word) { + longest_len = i + 1; + } + } + + return longest_len; +} + + + + +/* --- DFS Core Backtracking Engine with StringBuffer --- */ + +/** + * @brief Internal recursive function to perform DFS using c_StringBuffer. + * @param node Current Trie node being evaluated. + * @param sb Pointer to the active string buffer tracking the current path. + * @param output Pointer to the destination string list collector. + * @return c_err_t C_ERR_OK on success, or C_ERR_NOMEM if string buffer allocation fails. + */ +static c_err_t c_NlpTrie_CollectDFS(c_NlpTrieNode_t* node, c_StringBuffer_t* sb, c_NlpStringList_t* output) { + if (node == NULL) { + return C_ERR_OK; + } + + // 1. If this node marks an established word end, copy the raw payload directly into the list + if (node->is_end_of_word) { + c_err_t err = c_NlpStringList_Add(output, sb->buffer, sb->size); + if (err != C_ERR_OK) return err; + } + + // 2. Recurse down all 256 possible byte paths (handles raw ASCII and UTF-8 branches seamlessly) + for (int i = 0; i < ALPHABET_SIZE; i++) { + if (node->children[i] != NULL) { + char ch = (char)i; + + // Append the single byte character onto the current tracking path path + c_err_t err = c_StringBuffer_Append(sb, &ch, 1); + if (err != C_ERR_OK) return err; + + // Deep traverse down the branch + err = c_NlpTrie_CollectDFS(node->children[i], sb, output); + if (err != C_ERR_OK) return err; + + // Backtrack: Remove the trailing byte character to restore original buffer size context + err = c_StringBuffer_RemoveAt(sb, sb->size - 1, 1); + if (err != C_ERR_OK) return err; + } + } + + return C_ERR_OK; +} + +/* --- Public Core API Implementation --- */ + +c_err_t c_NlpTrie_KeysWithPrefix(c_NlpTrie_t* self, const char* prefix, c_NlpStringList_t* output) { + if (!self || !self->root || !prefix || !output) { + return C_ERR_PARAM; + } + + // 1. Initialize output list allocation safely + c_err_t err = c_NlpStringList_Init(output, 4); + if (err != C_ERR_OK) return err; + + // 2. Locate the specific sub-root node where the given prefix stream terminates + c_NlpTrieNode_t* curr = self->root; + size_t prefix_len = 0; + for (size_t i = 0; prefix[i] != '\0'; i++) { + unsigned char index = (unsigned char)prefix[i]; + if (curr->children[index] == NULL) { + // Prefix does not exist in the tree; return an empty list gracefully + return C_ERR_OK; + } + curr = curr->children[index]; + prefix_len++; + } + + // 3. Initialize the temporary dynamic path buffer structure + c_StringBuffer_t sb; + err = c_StringBuffer_Init(&sb, prefix_len + 16); + if (err != C_ERR_OK) { + c_NlpStringList_Destroy(output); + return err; + } + + // 4. Pre-populate the tracking path layout with the found prefix string base + err = c_StringBuffer_Append(&sb, prefix, prefix_len); + if (err != C_ERR_OK) { + c_StringBuffer_Destroy(&sb); + c_NlpStringList_Destroy(output); + return err; + } + + // 5. Deploy DFS traversal starting from the located prefix sub-root + err = c_NlpTrie_CollectDFS(curr, &sb, output); + + // 6. Complete garbage collection on the temporary working buffer layout + c_StringBuffer_Destroy(&sb); + + // Rollback entirely if OOM conditions tripped during sub-branch traversals + if (err != C_ERR_OK) { + c_NlpStringList_Destroy(output); + return err; + } + + return C_ERR_OK; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + + +/** + * @brief Internal recursive engine to perform wildcard pattern matching traversal. + * @param node Current Trie node being evaluated. + * @param pattern The full pattern query string. + * @param pattern_idx The current character position we are evaluating in the pattern. + * @param sb Pointer to the dynamic string buffer managing the matching path. + * @param output Pointer to the string list collector. + * @return c_err_t C_ERR_OK on success, or execution allocation errors. + */ +static c_err_t c_NlpTrie_MatchDFS(c_NlpTrieNode_t* node, const char* pattern, size_t pattern_idx, + c_StringBuffer_t* sb, c_NlpStringList_t* output) { + if (node == NULL) { + return C_ERR_OK; + } + + char current_pattern_char = pattern[pattern_idx]; + + // Base Case: We reached the end of the pattern string + if (current_pattern_char == '\0') { + // If the current path structure forms a valid dictionary entry, collect it + if (node->is_end_of_word) { + c_err_t err = c_NlpStringList_Add(output, sb->buffer, sb->size); + if (err != C_ERR_OK) return err; + } + return C_ERR_OK; + } + + // Branch Case A: Wildcard character '.' matches any of the 256 branches + if (current_pattern_char == '.') { + for (int i = 0; i < ALPHABET_SIZE; i++) { + if (node->children[i] != NULL) { + char ch = (char)i; + + // Push branch byte onto string buffer path + c_err_t err = c_StringBuffer_Append(sb, &ch, 1); + if (err != C_ERR_OK) return err; + + // Move forward to the next node and advance pattern evaluation index + err = c_NlpTrie_MatchDFS(node->children[i], pattern, pattern_idx + 1, sb, output); + if (err != C_ERR_OK) return err; + + // Backtrack: Remove trailing byte character context + err = c_StringBuffer_RemoveAt(sb, sb->size - 1, 1); + if (err != C_ERR_OK) return err; + } + } + } + // Branch Case B: Literal character match - process direct branch index + else { + unsigned char index = (unsigned char)current_pattern_char; + if (node->children[index] != NULL) { + // Push literal character byte onto string buffer path + c_err_t err = c_StringBuffer_Append(sb, ¤t_pattern_char, 1); + if (err != C_ERR_OK) return err; + + // Recurse to next node + err = c_NlpTrie_MatchDFS(node->children[index], pattern, pattern_idx + 1, sb, output); + if (err != C_ERR_OK) return err; + + // Backtrack + err = c_StringBuffer_RemoveAt(sb, sb->size - 1, 1); + if (err != C_ERR_OK) return err; + } + } + + return C_ERR_OK; +} + +/* --- Public Core API Implementation --- */ + +c_err_t c_NlpTrie_KeysThatMatch(c_NlpTrie_t* self, const char* pattern, c_NlpStringList_t* output) { + if (!self || !self->root || !pattern || !output) { + return C_ERR_PARAM; + } + + // Initialize list to collect matches (initial capacity of 4 items) + c_err_t err = c_NlpStringList_Init(output, 4); + if (err != C_ERR_OK) return err; + + // Initialize tracking string buffer path to dynamically record characters + c_StringBuffer_t sb; + c_size_t estimate_len = strlen(pattern); + err = c_StringBuffer_Init(&sb, estimate_len + 4); + if (err != C_ERR_OK) { + c_NlpStringList_Destroy(output); + return err; + } + + // Deploy the recursive match search starting at the root layout node + err = c_NlpTrie_MatchDFS(self->root, pattern, 0, &sb, output); + + // Clean up temporary path string buffer state + c_StringBuffer_Destroy(&sb); + + if (err != C_ERR_OK) { + c_NlpStringList_Destroy(output); // Rollback dynamic arrays on inner traversal failures + return err; + } + + return C_ERR_OK; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * @brief Internal helper to check if a node contains any active child pointer. + * @return c_bool_t C_TRUE if completely empty of branches, otherwise C_FALSE. + */ +C_STATIC_FORCE_INLINE +c_bool_t c_NlpTrieNode_IsEmpty(c_NlpTrieNode_t* node) { + for (int i = 0; i < ALPHABET_SIZE; i++) { + if (node->children[i] != NULL) { + return C_FALSE; // Found an active sub-branch path + } + } + return C_TRUE; +} + +/** + * @brief Internal recursive engine to trace characters and clean up dead nodes bottom-up. + * @param node Current Trie node being evaluated. + * @param word The target deletion string text stream. + * @param depth The current character offset index. + * @param deleted Output flag signaling the parent layout layer that the child was destroyed. + * @return c_NlpTrieNode_t* Returns the adjusted pointer state of the evaluated node to update its parent. + */ +static c_NlpTrieNode_t* c_NlpTrie_DeleteDFS(c_NlpTrieNode_t* node, const char* word, size_t depth, c_bool_t* deleted) { + if (node == NULL) { + return NULL; + } + + // Base Case: We have fully processed the last byte character of the word + if (word[depth] == '\0') { + if (node->is_end_of_word) { + node->is_end_of_word = C_FALSE; // Deactivate word completion flag + *deleted = C_TRUE; + } + + // If this node serves no purpose for another word suffix, mark it for pruning + if (c_NlpTrieNode_IsEmpty(node)) { + c_NlpTrieNode_Destroy(node); + return NULL; + } + return node; + } + + // Recursive Step: Extract index byte and traverse deeper down the branch + const uint8_t index = (uint8_t)word[depth]; + node->children[index] = c_NlpTrie_DeleteDFS(node->children[index], word, depth + 1, deleted); + + // Post-Order Backtracking Pruning: Evaluate this node on the way up + // We can only prune this node if: + // 1. It is not marked as the end of a shorter word (is_end_of_word == C_FALSE) + // 2. It has no other active branches left hanging under it + if (node->is_end_of_word == C_FALSE && c_NlpTrieNode_IsEmpty(node)) { + c_NlpTrieNode_Destroy(node); + return NULL; + } + + return node; +} + +/* --- Public Core API Implementation --- */ + +c_err_t c_NlpTrie_Delete(c_NlpTrie_t* self, const char* word) { + if (!self || !self->root || !word) { + return C_ERR_PARAM; + } + + // Edge Case: Prevent actions if attempting to pass an empty string + if (word[0] == '\0') { + return C_ERR_OK; + } + + c_bool_t deleted = C_FALSE; + + // Execute the recursive deletion pattern starting right from the root node layer + self->root = c_NlpTrie_DeleteDFS(self->root, word, 0, &deleted); + + // Safety fallback: If the entire tree was pruned down, restore a valid base root node layout + if (self->root == NULL) { + self->root = c_NlpTrieNode_Create(); + if (self->root == NULL) { + return C_ERR_NOMEM; + } + } + + return C_ERR_OK; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_NlpTrie_Clear(c_NlpTrie_t* self) { + // 1. Guard against invalid instances or missing base parameters + if (!self || !self->root) { + return C_ERR_PARAM; + } + + // 2. Loop through all 256 structural starting slot positions of the root node + for (int i = 0; i < ALPHABET_SIZE; i++) { + if (self->root->children[i] != NULL) { + // Recursively destroy every deeper sub-branch path found + c_NlpTrieNode_FreeSubtree(self->root->children[i]); + self->root->children[i] = NULL; // Explicitly nullify pointer to prevent dangling handles + } + } + + // 3. Reset the root node's completion flag context + self->root->is_end_of_word = C_FALSE; + + return C_ERR_OK; +} \ No newline at end of file diff --git a/cNLP/Library/c_NlpTrie.h b/cNLP/Library/c_NlpTrie.h new file mode 100644 index 0000000..78b9f72 --- /dev/null +++ b/cNLP/Library/c_NlpTrie.h @@ -0,0 +1,54 @@ +#ifndef INCLUDED_C_NLPTRIE_H +#define INCLUDED_C_NLPTRIE_H + +#ifndef INCLUDED_C_BASE_H +#include +#endif /*INCLUDED_C_BASE_H*/ + +#ifndef INCLUDED_C_NLPSTRINGLIST_H +#include +#endif /*INCLUDED_C_NLPSTRINGLIST_H*/ + + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct c_NlpTrieNode_t c_NlpTrieNode_t; + +typedef struct { + c_NlpTrieNode_t* root; + c_size_t size; +}c_NlpTrie_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_NlpTrie_Init(c_NlpTrie_t* self); + +void c_NlpTrie_Destroy(c_NlpTrie_t* self); + +c_err_t c_NlpTrie_Insert(c_NlpTrie_t* self, const char* word); + +c_bool_t c_NlpTrie_Contains(c_NlpTrie_t* self, const char* word); + +c_bool_t c_NlpTrie_HasStartWith(c_NlpTrie_t* self, const char* word); + +c_err_t c_NlpTrie_LongestPrefixOf(c_NlpTrie_t* self, const char* text, char* result, size_t res_max_len); + +c_size_t c_NlpTrie_LongestPrefixOfLen(c_NlpTrie_t* self, const char* text); + +c_err_t c_NlpTrie_KeysWithPrefix(c_NlpTrie_t* self, const char* prefix, c_NlpStringList_t* output); + +c_err_t c_NlpTrie_KeysThatMatch(c_NlpTrie_t* self, const char* pattern, c_NlpStringList_t* output); + +c_err_t c_NlpTrie_Delete(c_NlpTrie_t* self, const char* word); + +/** + * @brief Resets and purges all words and branches inside the Trie while preserving the root instance. + * @param self Pointer to the active Trie instance. + * @return c_err_t C_ERR_OK on successful clearing, or C_ERR_PARAM if the pointer instance is invalid. + */ +c_err_t c_NlpTrie_Clear(c_NlpTrie_t* self); + +#endif /*INCLUDED_C_NLPTRIE_H*/ diff --git a/cNLP/Library/c_NlpTrie.t.c b/cNLP/Library/c_NlpTrie.t.c new file mode 100644 index 0000000..402a68c --- /dev/null +++ b/cNLP/Library/c_NlpTrie.t.c @@ -0,0 +1,276 @@ +#include "c_NlpTrie.h" +#include +#include + +#include +#include + +/* --- 自定义测试断言宏 --- */ +#define RUN_TEST(test_case, name) \ + do { \ + printf("[RUN] %s... ", name); \ + if (test_case) { \ + printf("\033[32mPASSED\033[0m\n"); \ + } else { \ + printf("\033[31mFAILED\033[0m (%s:%d)\n", __FILE__, __LINE__); \ + return C_ERR_FAIL; \ + } \ + } while(0) + +/** + * @brief c_NlpTrie 模块标准单元测试函数 + * @return c_err_t 返回 C_ERR_OK 表示全部通过,返回 C_ERROR 表示有测试项失败 + */ +c_err_t c_NlpTrie_UnitTest(void) { + c_NlpTrie_t trie; + c_err_t err; + + printf("========================================\n"); + printf(" STARTING c_NlpTrie UNIT TESTS \n"); + printf("========================================\n"); + + /* 1. 防御性边界测试 (Null Pointer Protection) */ + RUN_TEST(c_NlpTrie_Init(NULL) == C_ERR_PARAM, "Init with NULL pointer"); + RUN_TEST(c_NlpTrie_Insert(NULL, "test") == C_ERR_PARAM, "Insert with NULL self"); + RUN_TEST(c_NlpTrie_Insert(&trie, NULL) == C_ERR_PARAM, "Insert with NULL word (before init)"); + RUN_TEST(c_NlpTrie_Contains(NULL, "test") == C_FALSE, "Search with NULL self"); + RUN_TEST(c_NlpTrie_HasStartWith(NULL, "test") == C_FALSE, "HasStartWith with NULL self"); + + /* 2. 初始化测试 (Initialization) */ + err = c_NlpTrie_Init(&trie); + RUN_TEST(err == C_ERR_OK && trie.root != NULL, "Normal initialization"); + + /* 3. 基础插入与精确查找测试 (Basic Insert & Search) */ + err = c_NlpTrie_Insert(&trie, "nlp"); + RUN_TEST(err == C_ERR_OK, "Insert normal word 'nlp'"); + RUN_TEST(c_NlpTrie_Contains(&trie, "nlp") == C_TRUE, "Search existing word 'nlp'"); + RUN_TEST(c_NlpTrie_Contains(&trie, "nl") == C_FALSE, "Search non-existing shorter word 'nl'"); + RUN_TEST(c_NlpTrie_Contains(&trie, "nlps") == C_FALSE, "Search non-existing longer word 'nlps'"); + + /* 4. 前缀包含关系与空字符串测试 (Prefix & Edge cases) */ + err = c_NlpTrie_Insert(&trie, "apple"); + err |= c_NlpTrie_Insert(&trie, "app"); + RUN_TEST(err == C_ERR_OK, "Insert words with shared prefix ('apple', 'app')"); + RUN_TEST(c_NlpTrie_Contains(&trie, "app") == C_TRUE, "Search shared prefix word 'app'"); + RUN_TEST(c_NlpTrie_Contains(&trie, "apple") == C_TRUE, "Search full word 'apple'"); + + // 空字符串通常作为根节点本身的结尾标记(如果允许插入) + err = c_NlpTrie_Insert(&trie, ""); + RUN_TEST(err == C_ERR_OK, "Insert empty string ''"); + RUN_TEST(c_NlpTrie_Contains(&trie, "") == C_TRUE, "Search empty string ''"); + + /* 5. 256 全字符集测试 (UTF-8 Chinese & ASCII Symbols) */ + // 包含:全角符号、大写英文、空格、数字、扩展 ASCII + err = c_NlpTrie_Insert(&trie, "自然语言处理_v2.0"); + RUN_TEST(err == C_ERR_OK, "Insert complex UTF-8 word with symbols and numbers"); + RUN_TEST(c_NlpTrie_Contains(&trie, "自然语言处理_v2.0") == C_TRUE, "Search complex UTF-8 word"); + RUN_TEST(c_NlpTrie_Contains(&trie, "自然语言") == C_FALSE, "Search non-existing sub-word '自然语言'"); + + /* 6. 前缀查找功能测试 (HasStartWith) */ + RUN_TEST(c_NlpTrie_HasStartWith(&trie, "自然") == C_TRUE, "HasStartWith existing Chinese prefix"); + RUN_TEST(c_NlpTrie_HasStartWith(&trie, "自然语言处理_v2.0") == C_TRUE, "HasStartWith full match as prefix"); + RUN_TEST(c_NlpTrie_HasStartWith(&trie, "自燃") == C_FALSE, "HasStartWith non-existing Chinese prefix"); + RUN_TEST(c_NlpTrie_HasStartWith(&trie, "nl") == C_TRUE, "HasStartWith existing English prefix"); + RUN_TEST(c_NlpTrie_HasStartWith(&trie, "xyz") == C_FALSE, "HasStartWith non-existing English prefix"); + + /* 7. 销毁与悬空安全测试 (Destroy & Safety) */ + c_NlpTrie_Destroy(&trie); + RUN_TEST(trie.root == NULL, "Trie root set to NULL after destroy"); + + // 销毁后的二次防御调用不应引发崩溃 + RUN_TEST(c_NlpTrie_Contains(&trie, "nlp") == C_FALSE, "Search on destroyed trie"); + RUN_TEST(c_NlpTrie_HasStartWith(&trie, "nlp") == C_FALSE, "HasStartWith on destroyed trie"); + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + + c_NlpTrie_Init(&trie); + /* 8. 最长前缀匹配测试 (LongestPrefixOf) */ + char res_buf[128]; + + // 准备数据 + c_NlpTrie_Insert(&trie, "自然"); + c_NlpTrie_Insert(&trie, "自然语言"); + c_NlpTrie_Insert(&trie, "自然语言处理"); + c_NlpTrie_Insert(&trie, "nlp"); + + // 测试点 1:有多重匹配时,应当贪婪匹配最长的一个 + c_NlpTrie_LongestPrefixOf(&trie, "自然语言处理核心技术", res_buf, sizeof(res_buf)); + RUN_TEST(strcmp(res_buf, "自然语言处理") == 0, "LongestPrefixOf greedy match '自然语言处理'"); + + // 测试点 2:部分输入匹配,落到中途的有效单词上 + c_NlpTrie_LongestPrefixOf(&trie, "自然语言学习", res_buf, sizeof(res_buf)); + RUN_TEST(strcmp(res_buf, "自然语言") == 0, "LongestPrefixOf sub-match '自然语言'"); + + // 测试点 3:完全无法匹配的情况 + c_NlpTrie_LongestPrefixOf(&trie, "人工智能", res_buf, sizeof(res_buf)); + RUN_TEST(strcmp(res_buf, "") == 0, "LongestPrefixOf no match returns empty string"); + + // 测试点 4:英文前缀匹配 + c_NlpTrie_LongestPrefixOf(&trie, "nlpsolver", res_buf, sizeof(res_buf)); + RUN_TEST(strcmp(res_buf, "nlp") == 0, "LongestPrefixOf English word 'nlp'"); + c_NlpTrie_Destroy(&trie); + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + + + /* 9. Autocomplete Prefix Matching Tests (KeysWithPrefix) */ + c_NlpTrie_Init(&trie); + c_NlpStringList_t matches; + + c_NlpTrie_Insert(&trie, "app"); + c_NlpTrie_Insert(&trie, "apple"); + c_NlpTrie_Insert(&trie, "apricot"); + c_NlpTrie_Insert(&trie, "banana"); + c_NlpTrie_Insert(&trie, "自然语言"); + c_NlpTrie_Insert(&trie, "自然语言处理"); + + // Test Point 1: Match English Prefix 'ap' (Expect: app, apple, apricot) + err = c_NlpTrie_KeysWithPrefix(&trie, "ap", &matches); + RUN_TEST(err == C_ERR_OK && matches.count == 3, "KeysWithPrefix found 3 matches for 'ap'"); + RUN_TEST(strcmp(matches.items[0], "app") == 0, "Matches item 0 is 'app'"); + RUN_TEST(strcmp(matches.items[1], "apple") == 0, "Matches item 1 is 'apple'"); + RUN_TEST(strcmp(matches.items[2], "apricot") == 0, "Matches item 2 is 'apricot'"); + c_NlpStringList_Destroy(&matches); + + // Test Point 2: Match UTF-8 Chinese Prefix (Expect: 自然语言, 自然语言处理) + err = c_NlpTrie_KeysWithPrefix(&trie, "自然", &matches); + RUN_TEST(err == C_ERR_OK && matches.count == 2, "KeysWithPrefix found 2 matches for '自然'"); + c_NlpStringList_Destroy(&matches); + + // Test Point 3: Search non-existent prefix (Expect: 0 items found, no memory leaks) + err = c_NlpTrie_KeysWithPrefix(&trie, "unknown", &matches); + RUN_TEST(err == C_ERR_OK && matches.count == 0, "KeysWithPrefix returned 0 items on empty mismatch"); + c_NlpStringList_Destroy(&matches); + c_NlpTrie_Destroy(&trie); + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + + c_NlpTrie_Init(&trie); + + /* 13. Wildcard Query Traversal Validation (KeysThatMatch) */ + c_NlpStringList_t query_results; + + c_NlpTrie_Insert(&trie, "cat"); + c_NlpTrie_Insert(&trie, "cot"); + c_NlpTrie_Insert(&trie, "coat"); + c_NlpTrie_Insert(&trie, "dog"); + c_NlpTrie_Insert(&trie, "自然"); + c_NlpTrie_Insert(&trie, "自燃"); + + // Test Case 1: Simple single wildcard slot match (Expect: "cat", "cot") + err = c_NlpTrie_KeysThatMatch(&trie, "c.t", &query_results); + RUN_TEST(err == C_ERR_OK && query_results.count == 2, "KeysThatMatch finds exactly 2 terms matching pattern 'c.t'"); + RUN_TEST(strcmp(query_results.items[0], "cat") == 0, "First matching match found: 'cat'"); + RUN_TEST(strcmp(query_results.items[1], "cot") == 0, "Second matching match found: 'cot'"); + c_NlpStringList_Destroy(&query_results); + + // Test Case 2: Full wildcard string constraint match length (Expect: "dog") + err = c_NlpTrie_KeysThatMatch(&trie, "...", &query_results); + RUN_TEST(err == C_ERR_OK && query_results.count == 3, "Pattern '...' pulls exact length matches ('cat', 'cot', 'dog')"); + c_NlpStringList_Destroy(&query_results); + + // Test Case 3: Complex multi-byte matching (Remember: in UTF-8, 1 Chinese Character = 3 Bytes) + // To match a single trailing Chinese character change on "自*", we need 3 dots "自..." + err = c_NlpTrie_KeysThatMatch(&trie, "自...", &query_results); + RUN_TEST(err == C_ERR_OK && query_results.count == 2, "Multi-byte pattern verification matches both '自然' and '自燃'"); + c_NlpStringList_Destroy(&query_results); + + // Test Case 4: Zero match behavior + err = c_NlpTrie_KeysThatMatch(&trie, "c..t", &query_results); // Matches "coat" + RUN_TEST(err == C_ERR_OK && query_results.count == 1, "Pattern 'c..t' correctly identifies structural length match 'coat'"); + c_NlpStringList_Destroy(&query_results); + c_NlpTrie_Destroy(&trie); + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + + /* 14. Dictionary Node Deletion & Path Pruning Verification (c_NlpTrie_Delete) */ + c_NlpTrie_Init(&trie); + + c_NlpTrie_Insert(&trie, "app"); + c_NlpTrie_Insert(&trie, "apple"); + c_NlpTrie_Insert(&trie, "banana"); + + // Test Point 1: Parameter validation checking + RUN_TEST(c_NlpTrie_Delete(NULL, "app") == C_ERR_PARAM, "Delete handles NULL self context pointer"); + RUN_TEST(c_NlpTrie_Delete(&trie, NULL) == C_ERR_PARAM, "Delete handles NULL string inputs"); + + // Test Point 2: Deleting a word that is a prefix of another word ("app") + // Expected: "app" flag goes off, but "apple" nodes must stay intact + err = c_NlpTrie_Delete(&trie, "app"); + RUN_TEST(err == C_ERR_OK, "Delete intermediate prefix word 'app' successfully"); + RUN_TEST(c_NlpTrie_Contains(&trie, "app") == C_FALSE, "Word 'app' is no longer searchable"); + RUN_TEST(c_NlpTrie_Contains(&trie, "apple") == C_TRUE, "Longer word 'apple' remains fully searchable"); + + // Test Point 3: Deleting a word that leaves isolated nodes behind ("apple") + // Expected: The path nodes matching 'l' and 'e' must be pruned to avoid memory leaks + err = c_NlpTrie_Delete(&trie, "apple"); + RUN_TEST(err == C_ERR_OK, "Delete trailing leaf word 'apple' successfully"); + RUN_TEST(c_NlpTrie_Contains(&trie, "apple") == C_FALSE, "Word 'apple' is no longer searchable"); + RUN_TEST(c_NlpTrie_HasStartWith(&trie, "ap") == C_FALSE, "Prefix path 'ap' is completely pruned"); + + // Test Point 4: Non-existent word cleanup verification + err = c_NlpTrie_Delete(&trie, "orange"); + RUN_TEST(err == C_ERR_OK, "Deleting non-existent word exits cleanly without modification"); + RUN_TEST(c_NlpTrie_Contains(&trie, "banana") == C_TRUE, "Unrelated word 'banana' is unaffected"); + + c_NlpTrie_Destroy(&trie); + + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + + /* 15. Trie Flush Re-initialization Verification (c_NlpTrie_Clear) */ + c_NlpTrie_Init(&trie); + + c_NlpTrie_Insert(&trie, "nlp"); + c_NlpTrie_Insert(&trie, "自然语言"); + + // Test Point 1: Parameter validation checking + RUN_TEST(c_NlpTrie_Clear(NULL) == C_ERR_PARAM, "Clear handles NULL self context pointer safely"); + + // Test Point 2: Execution clearance tracking + err = c_NlpTrie_Clear(&trie); + RUN_TEST(err == C_ERR_OK, "Clear flushes all sub-branch contents successfully"); + RUN_TEST(trie.root != NULL, "Trie structural base root node is preserved"); + RUN_TEST(c_NlpTrie_Contains(&trie, "nlp") == C_FALSE, "Previously stored word 'nlp' is no longer found"); + RUN_TEST(c_NlpTrie_HasStartWith(&trie, "自然") == C_FALSE, "Prefix indicators successfully cleared"); + + // Test Point 3: Verification of re-insertion stability post-clearance + err = c_NlpTrie_Insert(&trie, "reborn"); + RUN_TEST(err == C_ERR_OK, "Trie accepts new entry additions seamlessly after being cleared"); + RUN_TEST(c_NlpTrie_Contains(&trie, "reborn") == C_TRUE, "Newly added post-clearance key is fully searchable"); + + // Test Point 4: Idempotent clearing check (sequential empty clears) + err = c_NlpTrie_Clear(&trie); + err |= c_NlpTrie_Clear(&trie); + RUN_TEST(err == C_ERR_OK, "Continuous back-to-back clear calls execute safely with no side-effects"); + + c_NlpTrie_Destroy(&trie); + + /* ------------------------------------------------------------------------------------------------------------------ */ + /* */ + + + // 允许重复销毁(幂等性保护) + c_NlpTrie_Destroy(&trie); + + printf("========================================\n"); + printf("\033[32mALL c_NlpTrie TESTS PASSED SUCCESSFULLY!\033[0m\n"); + printf("========================================\n"); + + return C_ERR_OK; +} + +/* --- 测试执行入口 --- */ +int main(void) { + // 执行单元测试 + c_err_t result = c_NlpTrie_UnitTest(); + if (result != C_ERR_OK) { + return -1; + } + return 0; +} diff --git a/cNLP/README.md b/cNLP/README.md new file mode 100644 index 0000000..0ea761a --- /dev/null +++ b/cNLP/README.md @@ -0,0 +1 @@ +NLP DevKit