diff --git a/.gitignore b/.gitignore index e93af23..4807c50 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,10 @@ Module.symvers Mkfile.old dkms.conf +# CMake Used Defined Presets +CMakeUserPresets.json + +#Clion +.idea/ +cmake-build-*/ +build/ diff --git a/Base/c_Compiler.c b/Base/c_Compiler.c new file mode 100644 index 0000000..a84117e --- /dev/null +++ b/Base/c_Compiler.c @@ -0,0 +1,9 @@ +#include + +#if (C_IS_COMPILER_GCC) + #include "c_Compiler_GCC.cx" +#elif (C_IS_COMPILER_CLANG) + #include "c_Compiler_CLANG.cx" +#elif (C_IS_COMPILER_MSVC) + #include "c_Compiler_MSVC.cx" +#endif diff --git a/Base/c_Compiler.h b/Base/c_Compiler.h new file mode 100644 index 0000000..c99cb5c --- /dev/null +++ b/Base/c_Compiler.h @@ -0,0 +1,45 @@ +#ifndef INCLUDED_C_COMPILER_H +#define INCLUDED_C_COMPILER_H + +#ifndef INCLUDED_C_PLATFORM_H +#include +#endif /*INCLUDED_C_PLATFORM_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* C_PACKED 举例 */ + +/* +C_PACKED_STRUCT_START +struct MyHeader { + char id; + int length; +} C_PACKED; +C_PACKED_STRUCT_END + */ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#if (C_IS_COMPILER_GCC) + #include "c_Compiler_GCC.h" +#elif (C_IS_COMPILER_CLANG) + #include "c_Compiler_CLANG.h" +#elif (C_IS_COMPILER_MSVC) + #include "c_Compiler_MSVC.h" +#else + #define C_INLINE inline + #define C_NOINLINE + #define C_STATIC_FORCE_INLINE static C_INLINE + #define C_PACKED_STRUCT_START + #define C_PACKED_STRUCT_END + #define C_PACKED + #define C_ALIGN(bytes) + #define C_RESTRICT restrict + #define C_LIKELY(x) (x) + #define C_UNLIKELY(x) (x) +#endif + +#define C_UNUSED(x) ((void)(x)) + +#endif /*INCLUDED_C_COMPILER_H*/ diff --git a/Base/c_Compiler_CLANG.cx b/Base/c_Compiler_CLANG.cx new file mode 100644 index 0000000..0477a0f --- /dev/null +++ b/Base/c_Compiler_CLANG.cx @@ -0,0 +1 @@ +#include diff --git a/Base/c_Compiler_CLANG.h b/Base/c_Compiler_CLANG.h new file mode 100644 index 0000000..25f3e96 --- /dev/null +++ b/Base/c_Compiler_CLANG.h @@ -0,0 +1,17 @@ +#ifndef INCLUDED_C_COMPILER_CLANG_H +#define INCLUDED_C_COMPILER_CLANG_H + +#define C_INLINE inline __attribute__((always_inline)) +#define C_NOINLINE __attribute__((noinline)) + +#define C_STATIC_FORCE_INLINE static C_INLINE + +#define C_PACKED_STRUCT_START +#define C_PACKED_STRUCT_END +#define C_PACKED __attribute__((packed)) + +#define C_ALIGN(bytes) __attribute__((aligned(bytes))) + +#define C_RESTRICT __restrict__ + +#endif /*INCLUDED_C_COMPILER_CLANG_H*/ diff --git a/Base/c_Compiler_GCC.cx b/Base/c_Compiler_GCC.cx new file mode 100644 index 0000000..350b40d --- /dev/null +++ b/Base/c_Compiler_GCC.cx @@ -0,0 +1 @@ +#include diff --git a/Base/c_Compiler_GCC.h b/Base/c_Compiler_GCC.h new file mode 100644 index 0000000..6ec9568 --- /dev/null +++ b/Base/c_Compiler_GCC.h @@ -0,0 +1,21 @@ +#ifndef INCLUDED_C_COMPILER_GCC_H +#define INCLUDED_C_COMPILER_GCC_H + +#define C_INLINE inline __attribute__((always_inline)) +#define C_NOINLINE __attribute__((noinline)) + +#define C_STATIC_FORCE_INLINE static C_INLINE + +#define C_PACKED_STRUCT_START +#define C_PACKED_STRUCT_END +#define C_PACKED __attribute__((packed)) + +#define C_ALIGN(bytes) __attribute__((aligned(bytes))) + +#define C_RESTRICT __restrict__ + +/* 用于 Search / Sort 等高频判断分支,提示 CPU 哪条分支概率更高 */ +#define C_LIKELY(x) __builtin_expect(!!(x), 1) +#define C_UNLIKELY(x) __builtin_expect(!!(x), 0) + +#endif /*INCLUDED_C_COMPILER_GCC_H*/ diff --git a/Base/c_Compiler_MSVC.cx b/Base/c_Compiler_MSVC.cx new file mode 100644 index 0000000..65e9b2b --- /dev/null +++ b/Base/c_Compiler_MSVC.cx @@ -0,0 +1 @@ +#include diff --git a/Base/c_Compiler_MSVC.h b/Base/c_Compiler_MSVC.h new file mode 100644 index 0000000..9204eed --- /dev/null +++ b/Base/c_Compiler_MSVC.h @@ -0,0 +1,22 @@ +#ifndef INCLUDED_C_COMPILER_MSVC_H +#define INCLUDED_C_COMPILER_MSVC_H + +#define C_INLINE __forceinline +#define C_NOINLINE __declspec(noinline) + +#define C_STATIC_FORCE_INLINE static C_INLINE + +#define C_PACKED_STRUCT_START __pragma(pack(push, 1)) +#define C_PACKED_STRUCT_END __pragma(pack(pop)) +#define C_PACKED /* MSVC 不支持通过单行后缀修饰变量 */ + +#define C_ALIGN(bytes) __declspec(align(bytes)) + +/* 通知编译器两个指针指向的内存绝不重叠,用于大幅提升数学库(Math)的向量化效率 */ +#define C_RESTRICT __restrict + +/* 用于 Search / Sort 等高频判断分支,提示 CPU 哪条分支概率更高 */ +#define C_LIKELY(x) (x) +#define C_UNLIKELY(x) (x) + +#endif /*INCLUDED_C_COMPILER_MSVC_H*/ diff --git a/Base/c_Macros.c b/Base/c_Macros.c new file mode 100644 index 0000000..34b0dbe --- /dev/null +++ b/Base/c_Macros.c @@ -0,0 +1 @@ +#include diff --git a/Base/c_Macros.h b/Base/c_Macros.h new file mode 100644 index 0000000..67abd03 --- /dev/null +++ b/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/Base/c_Platform.c b/Base/c_Platform.c new file mode 100644 index 0000000..3883c12 --- /dev/null +++ b/Base/c_Platform.c @@ -0,0 +1 @@ +#include diff --git a/Base/c_Platform.h b/Base/c_Platform.h new file mode 100644 index 0000000..4f7604e --- /dev/null +++ b/Base/c_Platform.h @@ -0,0 +1,131 @@ +#ifndef INCLUDED_C_PLATFORM_H +#define INCLUDED_C_PLATFORM_H + +/* ============================================================================== + * 🎯 1. 编译器类型常量(严格以 C_ 开头) + * ============================================================================== */ +#define C_COMPILER_TYPE_UNKNOWN 0 +#define C_COMPILER_TYPE_GCC 1 +#define C_COMPILER_TYPE_CLANG 2 +#define C_COMPILER_TYPE_MSVC 3 + +/* 初始化默认值 */ +#define C_CURRENT_COMPILER_TYPE C_COMPILER_TYPE_UNKNOWN +#define C_CURRENT_COMPILER_NAME "Unknown Compiler" +#define C_CURRENT_COMPILER_VERSION 0 + +/* 核心阻断:Clang 具有欺骗性(会同时定义 __GNUC__ 或 _MSC_VER),必须优先判定 */ +#if defined(__clang__) + #undef C_CURRENT_COMPILER_TYPE + #undef C_CURRENT_COMPILER_NAME + #define C_CURRENT_COMPILER_TYPE C_COMPILER_TYPE_CLANG + #define C_CURRENT_COMPILER_NAME "Clang" + + #undef C_CURRENT_COMPILER_VERSION + #define C_CURRENT_COMPILER_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) + +#elif defined(__GNUC__) || defined(__GNUG__) + #undef C_CURRENT_COMPILER_TYPE + #undef C_CURRENT_COMPILER_NAME + #define C_CURRENT_COMPILER_TYPE C_COMPILER_TYPE_GCC + #define C_CURRENT_COMPILER_NAME "GCC" + + #undef C_CURRENT_COMPILER_VERSION + #if defined(__GNUC_PATCHLEVEL__) + #define C_CURRENT_COMPILER_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) + #else + #define C_CURRENT_COMPILER_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) + #endif + +#elif defined(_MSC_VER) + #undef C_CURRENT_COMPILER_TYPE + #undef C_CURRENT_COMPILER_NAME + #define C_CURRENT_COMPILER_TYPE C_COMPILER_TYPE_MSVC + #define C_CURRENT_COMPILER_NAME "Microsoft Visual C++" + + #undef C_CURRENT_COMPILER_VERSION + #if defined(_MSC_FULL_VER) + #define C_CURRENT_COMPILER_VERSION (_MSC_FULL_VER / 1000) + #else + #define C_CURRENT_COMPILER_VERSION (_MSC_VER * 100) + #endif +#endif + + +/* ============================================================================== + * 🎯 2. 快捷判定辅助宏(严格以 C_ 开头,0 或 1 显式映射) + * ============================================================================== */ +#if C_CURRENT_COMPILER_TYPE == C_COMPILER_TYPE_GCC + #define C_IS_COMPILER_GCC 1 +#else + #define C_IS_COMPILER_GCC 0 +#endif + +#if C_CURRENT_COMPILER_TYPE == C_COMPILER_TYPE_CLANG + #define C_IS_COMPILER_CLANG 1 +#else + #define C_IS_COMPILER_CLANG 0 +#endif + +#if C_CURRENT_COMPILER_TYPE == C_COMPILER_TYPE_MSVC + #define C_IS_COMPILER_MSVC 1 +#else + #define C_IS_COMPILER_MSVC 0 +#endif + + +/* ============================================================================== + * 🎯 3. 操作系统环境识别矩阵(严格以 C_ 开头,无缝对接跨平台需求) + * ============================================================================== */ +#define C_OS_TYPE_UNKNOWN 0 +#define C_OS_TYPE_WINDOWS 1 +#define C_OS_TYPE_LINUX 2 +#define C_OS_TYPE_MACOS 3 +#define C_OS_TYPE_FREEBSD 4 + +#define C_CURRENT_OS_TYPE C_OS_TYPE_UNKNOWN +#define C_CURRENT_OS_NAME "Unknown OS" + +#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) + #undef C_CURRENT_OS_TYPE + #undef C_CURRENT_OS_NAME + #define C_CURRENT_OS_TYPE C_OS_TYPE_WINDOWS + #define C_CURRENT_OS_NAME "Windows" + +#elif defined(__APPLE__) && defined(__MACH__) + #undef C_CURRENT_OS_TYPE + #undef C_CURRENT_OS_NAME + #define C_CURRENT_OS_TYPE C_OS_TYPE_MACOS + #define C_CURRENT_OS_NAME "macOS" + +#elif defined(__linux__) || defined(__linux) + #undef C_CURRENT_OS_TYPE + #undef C_CURRENT_OS_NAME + #define C_CURRENT_OS_TYPE C_OS_TYPE_LINUX + #define C_CURRENT_OS_NAME "Linux" + +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + #undef C_CURRENT_OS_TYPE + #undef C_CURRENT_OS_NAME + #define C_CURRENT_OS_TYPE C_OS_TYPE_FREEBSD + #define C_CURRENT_OS_NAME "FreeBSD" +#endif + +/* 操作系统快捷判定 */ +#define C_IS_OS_WINDOWS (C_CURRENT_OS_TYPE == C_OS_TYPE_WINDOWS) +#define C_IS_OS_LINUX (C_CURRENT_OS_TYPE == C_OS_TYPE_LINUX) +#define C_IS_OS_MACOS (C_CURRENT_OS_TYPE == C_OS_TYPE_MACOS) +#define C_IS_OS_FREEBSD (C_CURRENT_OS_TYPE == C_OS_TYPE_FREEBSD) + + +/* ============================================================================== + * 🎯 4. 特化环境:MinGW / MSYS2 环境检测(严格以 C_ 开头) + * ============================================================================== */ +#if defined(__MINGW32__) || defined(__MINGW64__) + #define C_ENVIRONMENT_IS_MINGW 1 +#else + #define C_ENVIRONMENT_IS_MINGW 0 +#endif + + +#endif /*INCLUDED_C_PLATFORM_H*/ diff --git a/Base/c_Platform.t.c b/Base/c_Platform.t.c new file mode 100644 index 0000000..15b99de --- /dev/null +++ b/Base/c_Platform.t.c @@ -0,0 +1,29 @@ +#include "c_Platform.h" +#include +#include + +int main(int argc, char** argv){ + // 1. 打印基础架构诊断信息 + printf("操作系统: %s, 编译器: %s (版本: %d)\n", + C_CURRENT_OS_NAME, C_CURRENT_COMPILER_NAME, C_CURRENT_COMPILER_VERSION); + + // 2. 结合操作系统与编译器的复合拦截 +#if C_IS_OS_WINDOWS + if (C_ENVIRONMENT_IS_MINGW) { + printf("正在您的本地环境 (Windows + MSYS2 MinGW) 下高效执行...\n"); + } +#endif + + // 3. 多平台特化高性能代码分支 +#if C_IS_OS_LINUX && C_IS_COMPILER_GCC + // Linux 下使用 GCC 的特化高性能系统调用 + printf("启用 Linux Epoll & GCC 优化算法\n"); +#elif C_IS_OS_MACOS || C_IS_OS_FREEBSD + // macOS 和 FreeBSD 属于 BSD 谱系,可以使用类似 kqueue 的特性 + printf("启用 BSD 谱系特化内核优化\n"); +#else + printf("启用跨平台标准通用算法\n"); +#endif + + return 0; +} diff --git a/Base/c_Test.c b/Base/c_Test.c new file mode 100644 index 0000000..1d7789a --- /dev/null +++ b/Base/c_Test.c @@ -0,0 +1 @@ +#include diff --git a/Base/c_Test.h b/Base/c_Test.h new file mode 100644 index 0000000..d4dd02d --- /dev/null +++ b/Base/c_Test.h @@ -0,0 +1,145 @@ +#ifndef INCLUDED_C_TEST_H +#define INCLUDED_C_TEST_H + +#include +#include +#include +#include +#include +#include "c_Compiler.h" + +/* ============================================================================== + * 🎯 1. 测试上下文状态追踪 + * ============================================================================== */ +static int c_test_suite_failed = 0; /* 当前套件是否有失败的断言 */ +static int c_test_case_failed = 0; /* 当前正在运行的测试用例是否有失败 */ +static int c_test_total_assertions = 0; /* 总断言数 */ +static int c_test_passed_assertions = 0; /* 成功的断言数 */ +static int c_test_cases_run = 0; /* 运行的用例数 */ +static int c_test_cases_passed = 0; /* 成功的用例数 */ +static double c_test_suite_total_ms = 0.0; /* 整个套件的总耗时 */ + +/* ============================================================================== + * 🎯 2. 核心断言宏矩阵(严格以 C_ASSERT_ 开头) + * ============================================================================== */ + +/* 统一的断言失败内部打印函数 */ +C_STATIC_FORCE_INLINE +void c_test_print_fail(const char* file, int line, const char* expr, const char* msg) { + c_test_suite_failed = 1; + c_test_case_failed = 1; + printf(" [FAIL] %s:%d -> 断言失败: (%s) ", file, line, expr); + if (msg && strlen(msg) > 0) { + printf("| 说明: %s", msg); + } + printf("\n"); +} + +/* 基础布尔断言 */ +#define C_ASSERT(expr, msg) do { \ + c_test_total_assertions++; \ + if (C_LIKELY(expr)) { \ + c_test_passed_assertions++; \ + } else { \ + c_test_print_fail(__FILE__, __LINE__, #expr, msg); \ + } \ +} while(0) + +/* 整型相等断言 */ +#define C_ASSERT_INT_EQ(actual, expected, msg) do { \ + c_test_total_assertions++; \ + long long act = (long long)(actual); \ + long long exp = (long long)(expected); \ + if (C_LIKELY(act == exp)) { \ + c_test_passed_assertions++; \ + } else { \ + char buf[256]; \ + snprintf(buf, sizeof(buf), "期望值: %lld, 实际值: %lld | %s", exp, act, msg); \ + c_test_print_fail(__FILE__, __LINE__, #actual " == " #expected, buf); \ + } \ +} while(0) + +/* 字符串相等断言 */ +#define C_ASSERT_STR_EQ(actual, expected, msg) do { \ + c_test_total_assertions++; \ + const char* act = (const char*)(actual); \ + const char* exp = (const char*)(expected); \ + if (C_LIKELY(act && exp && strcmp(act, exp) == 0)) { \ + c_test_passed_assertions++; \ + } else { \ + char buf[256]; \ + snprintf(buf, sizeof(buf), "期望: \"%s\", 实际: \"%s\" | %s", exp ? exp : "NULL", act ? act : "NULL", msg); \ + c_test_print_fail(__FILE__, __LINE__, "strcmp(" #actual ", " #expected ") == 0", buf); \ + } \ +} while(0) + +/* 浮点数安全相等断言(内置之前讨论的 IEEE 754 Epsilon 比较) */ +#define C_ASSERT_DOUBLE_EQ(actual, expected, msg) do { \ + c_test_total_assertions++; \ + double act = (double)(actual); \ + double exp = (double)(expected); \ + int is_eq = 0; \ + if (isnan(act) && isnan(exp) || (fabs(act - exp) < DBL_EPSILON) ) { is_eq = 1; } \ + if (C_LIKELY(is_eq)) { \ + c_test_passed_assertions++; \ + } else { \ + char buf[256]; \ + snprintf(buf, sizeof(buf), "期望: %.32f, 实际: %.32f (误差 > DBL_EPSILON[%.32f]) | %s", exp, act, DBL_EPSILON, msg); \ + c_test_print_fail(__FILE__, __LINE__, "fabs(" #actual " - " #expected ") < DBL_EPSILON", buf); \ + } \ +} while(0) + + +/* ============================================================================== + * 🎯 3. 测试套件生命周期控制宏 + * ============================================================================== */ + +/* 初始化一个测试套件文件 */ +#define C_TEST_SUITE_BEGIN(name) \ + printf("==================================================\n"); \ + printf("🧪 运行测试套件: %s [%s环境]\n", #name, C_CURRENT_OS_NAME); \ + printf("==================================================\n"); + +/* 结束测试套件并返回标准状态码给 CMake CTest */ +#define C_TEST_SUITE_END() \ + printf("\n--------------------------------------------------------------------\n"); \ + printf("📊 测试结果统计:\n"); \ + printf(" 测试用例数: %d 运行, %d 通过, %d 失败\n", \ + c_test_cases_run, c_test_cases_passed, c_test_cases_run - c_test_cases_passed); \ + printf(" 底层断言数: %d 总计, %d 成功, %d 失败\n", \ + c_test_total_assertions, c_test_passed_assertions, c_test_total_assertions - c_test_passed_assertions); \ + printf(" 套件总耗时: %.3f ms\n", c_test_suite_total_ms); \ + if (c_test_suite_failed) { \ + printf("🚨 结论 : [ FAILURE ] 有测试未通过!\n"); \ + printf("====================================================================\n"); \ + return 1; \ + } \ + printf("🎉 结论 : [ SUCCESS ] 全套单元测试完美通过!\n"); \ + printf("====================================================================\n"); \ + return 0; + +/* 声明一个测试用例块 */ +#define C_TEST_CASE(name) static void name(void) + +#define C_RUN_TEST_CASE(name) do { \ + c_test_cases_run++; \ + c_test_case_failed = 0; \ + printf(" ▶ 运行测试用例: %-30s ... ", #name); \ + fflush(stdout); \ + struct timespec c_start_time, c_end_time; \ + timespec_get(&c_start_time, TIME_UTC); \ + name(); \ + timespec_get(&c_end_time, TIME_UTC); \ + double c_elapsed_ms = (double)(c_end_time.tv_sec - c_start_time.tv_sec) * 1000.0 + \ + (double)(c_end_time.tv_nsec - c_start_time.tv_nsec) / 1000000.0; \ + c_test_suite_total_ms += c_elapsed_ms; \ + if (c_test_case_failed) { \ + printf("[ ❌ 失败 ] (耗时: %8.3f ms)\n", c_elapsed_ms); \ + } else { \ + c_test_cases_passed++; \ + printf("[ ✨ 通过 ] (耗时: %8.3f ms)\n", c_elapsed_ms); \ + } \ +} while(0) + + +#endif /*INCLUDED_C_TEST_H*/ diff --git a/Base/c_Test.t.c b/Base/c_Test.t.c new file mode 100644 index 0000000..3b473e3 --- /dev/null +++ b/Base/c_Test.t.c @@ -0,0 +1,39 @@ +#include "c_Test.h" +#include "c_Compiler.h" + +// Math/matrix.t.c +#include "c_Test.h" + +void heavy_calculation(void) { + double sum = 0.0; + for (int i = 0; i < 5000000; ++i) { + sum += sin((double)i) * cos((double)i); + } + // 随便加一个没意义的断言防止被编译器完全优化掉 + C_ASSERT(sum != 12345.6, "密集数学计算检验"); +} + +// ------------------------------------------------------------------------------ +// 🛠️ 步骤 A: 编写独立的测试用例函数 (C_TEST_CASE) +// ------------------------------------------------------------------------------ + +C_TEST_CASE(test_quick_operations) { + int a = 1, b = 1; + C_ASSERT_INT_EQ(a, b, "极速操作验证"); +} + +C_TEST_CASE(test_heavy_workload) { + heavy_calculation(); +} + +// ------------------------------------------------------------------------------ +// 🚀 步骤 B: 在主执行块中注册并依次运行它们 +// ------------------------------------------------------------------------------ +int main(int argc, char** argv) { + C_TEST_SUITE_BEGIN(MathMatrixFunctionSuite) + + C_RUN_TEST_CASE(test_quick_operations); + C_RUN_TEST_CASE(test_heavy_workload); + + C_TEST_SUITE_END() +} diff --git a/Base/c_Types.c b/Base/c_Types.c new file mode 100644 index 0000000..86a3e8e --- /dev/null +++ b/Base/c_Types.c @@ -0,0 +1 @@ +#include diff --git a/Base/c_Types.h b/Base/c_Types.h new file mode 100644 index 0000000..8ea8253 --- /dev/null +++ b/Base/c_Types.h @@ -0,0 +1,122 @@ +#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_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_FLOAT_H +#define INCLUDED_FLOAT_H +#include +#endif /*INCLUDED_FLOAT_H*/ + +#ifndef INCLUDED_C_PLATFORM_H +#include +#endif /*INCLUDED_C_PLATFORM_H*/ + + +#ifndef INCLUDED_C_COMPILER_H +#include +#endif /*INCLUDED_C_COMPILER_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* TYPES */ + +#if (C_SIZEOF_VOID_P==8) + typedef int64_t c_int_t; + typedef uint64_t c_uint_t; + typedef uint64_t c_size_t; + typedef int64_t c_intptr_t; + typedef uint64_t c_uintptr_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_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 + +#if (C_SIZEOF_VOID_P==4) + typedef int32_t c_int_t; + typedef uint32_t c_uint_t; + typedef uint32_t c_size_t; + typedef int32_t c_intptr_t; + typedef uint32_t c_uintptr_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_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 +#endif + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* ERRORS */ + +typedef int c_err_t; + +#define C_ERR_OK 0 +#define C_ERR_FAIL (-1) +#define C_ERR_NOTFOUND (-2) +#define C_ERR_NOMEM (-3) +#define C_ERR_NOTIMPLEMENTED (-4) +#define C_ERR_PARAM (-5) +#define C_ERR_OUTOFBOUND (-6) +#define C_ERR_EMPTY (-7) +#define C_ERR_FULL (-8) + +#define C_SUCCESS C_ERR_OK + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define c_bool_t bool +#define C_TRUE true +#define C_FALSE false + + +#endif /*INCLUDED_C_TYPES_H*/ diff --git a/Base/c_Types.t.c b/Base/c_Types.t.c new file mode 100644 index 0000000..b03a5f0 --- /dev/null +++ b/Base/c_Types.t.c @@ -0,0 +1,8 @@ +#include +#include + +int main(int argc, char** argv){ + + + return 0; +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c233e31 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.20) + +get_filename_component(MY_PROJECT_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +project(${MY_PROJECT_NAME} C ASM) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) + +# ============================================================================== +# 🎯 宏:add_project_modules_with_tests +# ============================================================================== +macro(add_project_modules_with_tests OUT_APP_SOURCES OUT_TEST_FILES_LIST) + foreach(MODULE_DIR ${ARGN}) + if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_DIR}") + message(STATUS "[Module Found]: ${MODULE_DIR}") + + # 自动将该目录加入全局头文件包含路径 + include_directories("${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_DIR}") + + # 1. 搜集该目录下所有的 C/C++ 源文件 + file(GLOB ALL_MODULE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_DIR}/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_DIR}/*.cc" + "${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_DIR}/*.c" + "${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_DIR}/*.s" + "${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_DIR}/*.S" + ) + + # 2. 分离出测试文件 (*.t.cpp 或 *.t.c) + foreach(FILE_PATH ${ALL_MODULE_FILES}) + # 匹配以 .t.cpp, .t.cc, .t.c 结尾的文件 + if(FILE_PATH MATCHES "\\.t\\.(cpp|cc|c)$") + list(APPEND ${OUT_TEST_FILES_LIST} ${FILE_PATH}) + message("\tTestUnit: ${FILE_PATH}") + else() + # 剩下的才是主程序的源代码 + list(APPEND ${OUT_APP_SOURCES} ${FILE_PATH}) + message("\tSource: ${FILE_PATH}") + endif() + endforeach() + + else() + message(WARNING "[Module Warning]: Directory '${MODULE_DIR}' does not exist!") + endif() + endforeach() +endmacro() + +# ============================================================================== +# 🚀 使用宏收集目标目录 +# ============================================================================== +# 初始化一个空变量用于存放所有源码 +set(SOURCES "") +set(TEST_SOURCES "") + +configure_file(c_Config.h.in c_Config.h @ONLY) + +# 调用宏:第一个参数是接收源码的变量,后面跟上您所有的子目录 +add_project_modules_with_tests(SOURCES TEST_SOURCES + Base + Foundation + Graph + Math + Memory + Search + Sort + String +) + +#foreach (item ${SOURCES}) +# message(STATUS "[${MY_PROJECT_NAME}] SOURCE: ${item}") +#endforeach () +add_library(${MY_PROJECT_NAME} ${SOURCES}) +target_include_directories(${MY_PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + +# ============================================================================== +# 🧪 自动生成并注册单元测试 +# ============================================================================== +foreach(TEST_FILE ${TEST_SOURCES}) + # 获取文件名(不含路径),例如 "C:/project/Math/matrix.t.cpp" -> "matrix.t.cpp" + get_filename_component(TEST_NAME_WE ${TEST_FILE} NAME_WE) + # 构造测试可执行文件的名称,例如 "test_matrix" + set(TEST_EXE_NAME "test_${TEST_NAME_WE}") + + # 编译此测试程序:测试程序 = 测试源文件 + 主程序除main外的所有依赖 + # 注意:如果您的主程序包含 main.cpp,建议将 main.cpp 从 ${APP_SOURCES} 中剥离, + # 或者在这里只链接需要的源文件,防止出现多个 main 函数冲突。 +# add_executable(${TEST_EXE_NAME} ${TEST_FILE} ${APP_SOURCES}) + add_executable(${TEST_EXE_NAME} ${TEST_FILE}) + target_link_libraries(${TEST_EXE_NAME} PRIVATE ${MY_PROJECT_NAME}) + + # 将其注册到 CMake 的 ctest 测试集中 + add_test(NAME ${TEST_EXE_NAME} COMMAND ${TEST_EXE_NAME}) + + message(STATUS "[Test Registered]: ${TEST_EXE_NAME}") +endforeach() diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..f310991 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,102 @@ +{ + "version": 3, + "configurePresets": [ + { + "name": "base-preset", + "hidden": true, + "description": "所有预设的基类", + "binaryDir": "${sourceDir}/build/${presetName}", + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "ninja-base", + "hidden": true, + "inherits": "base-preset", + "generator": "Ninja" + }, + { + "name": "linux-gcc-debug", + "displayName": "Linux (GCC Debug)", + "description": "适用于 Linux 系统的 GCC 调试配置", + "inherits": "ninja-base", + "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Linux" }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_C_COMPILER": "gcc" + } + }, + { + "name": "linux-clang-release", + "displayName": "Linux (Clang Release)", + "description": "适用于 Linux 系统的 Clang 发行配置", + "inherits": "ninja-base", + "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Linux" }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_C_COMPILER": "clang" + } + }, + { + "name": "windows-msvc-debug", + "displayName": "Windows (MSVC 2022 Debug)", + "description": "适用于 Windows 的 Visual Studio 2022 调试", + "inherits": "base-preset", + "generator": "Visual Studio 17 2022", + "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" }, + "architecture": "x64", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "windows-clang-debug", + "displayName": "Windows (ClangCL Debug)", + "description": "在 Windows 上使用 MSVC 工具链下的 Clang", + "inherits": "base-preset", + "generator": "Visual Studio 17 2022", + "toolset": "ClangCL", + "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" }, + "architecture": "x64" + }, + { + "name": "macos-clang-debug", + "displayName": "macOS (AppleClang Debug)", + "description": "适用于 macOS 的自带 Clang 调试配置", + "inherits": "ninja-base", + "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Darwin" }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "freebsd-clang-debug", + "displayName": "FreeBSD (Clang Debug)", + "description": "适用于 FreeBSD 的系统默认 Clang 配置", + "inherits": "ninja-base", + "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "FreeBSD" }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_C_COMPILER": "clang" + } + }, + { + "name": "windows-mingw-debug", + "displayName": "Windows (MinGW GCC Debug)", + "description": "适用于 Windows 系统的 MinGW GCC 调试配置", + "inherits": "ninja-base", + "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_C_COMPILER": "gcc" + } + } + ], + "buildPresets": [ + { "name": "build-linux-gcc", "configurePreset": "linux-gcc-debug" }, + { "name": "build-windows-msvc", "configurePreset": "windows-msvc-debug" }, + { "name": "build-macos-clang", "configurePreset": "macos-clang-debug" }, + { "name": "build-freebsd-clang", "configurePreset": "freebsd-clang-debug" } + ] +} diff --git a/Foundation/c_float.c b/Foundation/c_float.c new file mode 100644 index 0000000..e0b99ac --- /dev/null +++ b/Foundation/c_float.c @@ -0,0 +1 @@ +#include diff --git a/Foundation/c_float.h b/Foundation/c_float.h new file mode 100644 index 0000000..360e7d6 --- /dev/null +++ b/Foundation/c_float.h @@ -0,0 +1,164 @@ +#ifndef INCLUDED_C_FLOAT_H +#define INCLUDED_C_FLOAT_H + +#ifndef INCLUDED_FLOAT_H +#define INCLUDED_FLOAT_H +#include +#endif /*INCLUDED_FLOAT_H*/ + +#ifndef INCLUDED_STDINT_H +#define INCLUDED_STDINT_H +#include +#endif /*INCLUDED_STDINT_H*/ + +#ifndef INCLUDED_MATH_H +#define INCLUDED_MATH_H +#include +#endif /*INCLUDED_MATH_H*/ + +#ifndef INCLUDED_C_COMPILER_H +#include +#endif /*INCLUDED_C_COMPILER_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef union { + float f; + uint32_t u; + struct { + uint32_t sign: 1; + uint32_t exponent: 8; + uint32_t mantissa: 23; + }IEEE754; +}c_Float_t; + +typedef union { + double d; + uint64_t u; + struct { + uint64_t sign: 1; + uint64_t exponent: 11; + uint64_t mantissa: 52; + }; +}c_Double_t; + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +C_STATIC_FORCE_INLINE +int c_float_is_eq(const float a, const float b) { + return fabsf(a - b) < FLT_EPSILON; +} + +C_STATIC_FORCE_INLINE +int c_float_is_gt(const float a, const float b) { + return (a - b) > FLT_EPSILON; +} + +C_STATIC_FORCE_INLINE +int c_float_is_ge(const float a, const float b) { + return (a - b) >= -FLT_EPSILON; +} + +C_STATIC_FORCE_INLINE +int c_float_cmp_safe(const float fa, const float fb) { + int nan_a = isnan(fa); + int nan_b = isnan(fb); + + if (C_UNLIKELY(nan_a || nan_b)) { + if (nan_a && nan_b) return 0; + if (nan_a) return 1; + return -1; + } + + if (fa < fb) return -1; + if (fa > fb) return 1; + return 0; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* double */ + +C_STATIC_FORCE_INLINE +int c_double_is_eq(const double a, const double b) { + return fabs(a - b) < DBL_EPSILON; +} + +C_STATIC_FORCE_INLINE +int c_double_is_gt(const double a, const double b) { + return (a - b) > DBL_EPSILON; +} + +C_STATIC_FORCE_INLINE +int c_double_is_ge(const double a, const double b) { + return (a - b) >= -DBL_EPSILON; +} + +C_STATIC_FORCE_INLINE +int c_double_cmp_safe(const double da, const double db) { + + int nan_a = isnan(da); + int nan_b = isnan(db); + + /* --- 1. 处理 NaN 的极端分支 --- */ + /* 采用 C_UNLIKELY 优化,因为大部分待排数据中 NaN 是极少数,提示 CPU 预读正常分支 */ + if (C_UNLIKELY(nan_a || nan_b)) { + if (nan_a && nan_b) return 0; /* 两个都是 NaN,视为相等 */ + if (nan_a) return 1; /* a 是 NaN,b 是正常数,视为 a > b(NaN排到最后) */ + return -1; /* a 是正常数,b 是 NaN,视为 a < b */ + } + + /* --- 2. 正常数值的三向比较分支 --- */ + if (da < db) return -1; + if (da > db) return 1; + return 0; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +C_STATIC_FORCE_INLINE +int c_float_ptr_cmp_safe(const void* a, const void* b) { + float fa = *(const float*)a; + float fb = *(const float*)b; + + int nan_a = isnan(fa); + int nan_b = isnan(fb); + + if (C_UNLIKELY(nan_a || nan_b)) { + if (nan_a && nan_b) return 0; + if (nan_a) return 1; + return -1; + } + + if (fa < fb) return -1; + if (fa > fb) return 1; + return 0; +} + +C_STATIC_FORCE_INLINE +int c_double_ptr_cmp_safe(const void* a, const void* b) { + double da = *(const double*)a; + double db = *(const double*)b; + + int nan_a = isnan(da); + int nan_b = isnan(db); + + /* --- 1. 处理 NaN 的极端分支 --- */ + /* 采用 C_UNLIKELY 优化,因为大部分待排数据中 NaN 是极少数,提示 CPU 预读正常分支 */ + if (C_UNLIKELY(nan_a || nan_b)) { + if (nan_a && nan_b) return 0; /* 两个都是 NaN,视为相等 */ + if (nan_a) return 1; /* a 是 NaN,b 是正常数,视为 a > b(NaN排到最后) */ + return -1; /* a 是正常数,b 是 NaN,视为 a < b */ + } + + /* --- 2. 正常数值的三向比较分支 --- */ + if (da < db) return -1; + if (da > db) return 1; + return 0; +} + +#endif /*INCLUDED_C_FLOAT_H*/ diff --git a/Foundation/c_float.t.c b/Foundation/c_float.t.c new file mode 100644 index 0000000..c92abfc --- /dev/null +++ b/Foundation/c_float.t.c @@ -0,0 +1,23 @@ +#include "c_float.h" +#include +#include + +static void c_Double_print(c_Double_t value) { + uint64_t sign = value.sign; + uint64_t exponent = value.exponent; + uint64_t fraction = value.mantissa; + printf("数值: %.15f\n", value.d); + printf("整体十六进制: 0x%016llX\n", (unsigned long long)value.u); + printf("符号位 (Sign): %llu (%s)\n", (unsigned long long)sign, sign ? "负" : "正"); + printf("指数位 (Exponent 原始值): %llu (实际 2 阶: %d)\n", + (unsigned long long)exponent, (int)exponent - 1023); + printf("尾数位 (Fraction 16进制): 0x%013llX\n", (unsigned long long)fraction); + printf("---------------------------------------\n"); +} + +int main(int argc, char** argv){ + c_Double_t d={.d=1.0f}; + c_Double_print(d); + + return 0; +} diff --git a/Memory/c_Alignment.c b/Memory/c_Alignment.c new file mode 100644 index 0000000..07d63c5 --- /dev/null +++ b/Memory/c_Alignment.c @@ -0,0 +1 @@ +#include diff --git a/Memory/c_Alignment.h b/Memory/c_Alignment.h new file mode 100644 index 0000000..36edd07 --- /dev/null +++ b/Memory/c_Alignment.h @@ -0,0 +1,21 @@ +#ifndef INCLUDED_C_ALIGNMENT_H +#define INCLUDED_C_ALIGNMENT_H + +typedef union { +#ifdef C_ALIGN_MAX_BYTES + char pad[C_ALIGN_MAX_BYTES]; +#else + int i; + long l; + long *lp; + void *p; + void (*fp)(void); + float f; + double d; + long double ld; +#endif +}c_Alignment_t ; + +#define C_ALIGN_SIZE sizeof(c_Alignment_t) + +#endif /*INCLUDED_C_ALIGNMENT_H*/ diff --git a/Memory/c_Arena.c b/Memory/c_Arena.c new file mode 100644 index 0000000..9a19f34 --- /dev/null +++ b/Memory/c_Arena.c @@ -0,0 +1,92 @@ +#include +#include + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +C_STATIC_FORCE_INLINE +bool is_power_of_two(uintptr_t x) { + return (x & (x-1)) == 0; +} + +C_STATIC_FORCE_INLINE +uintptr_t align_forward(uintptr_t ptr, size_t align) { + uintptr_t p, a, 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, size_t size, size_t align) { + // Align 'curr_offset' forward to the specified alignment + uintptr_t curr_ptr = (uintptr_t)a->buf + (uintptr_t)a->curr_offset; + uintptr_t offset = align_forward(curr_ptr, align); + offset -= (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, size_t old_size, size_t new_size, size_t align) { + unsigned char *old_mem = (unsigned char *)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); + 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_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, size_t old_size, size_t new_size) { + return arena_resize_align(a, old_memory, old_size, new_size, C_ALIGN_SIZE); +} diff --git a/Memory/c_Arena.h b/Memory/c_Arena.h new file mode 100644 index 0000000..e670f4f --- /dev/null +++ b/Memory/c_Arena.h @@ -0,0 +1,76 @@ +#ifndef INCLUDED_C_ARENA_H +#define INCLUDED_C_ARENA_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_ALIGNMENT_H +#include +#endif /*INCLUDED_C_ALIGNMENT_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct c_Arena_s c_Arena_t; +struct c_Arena_s { + unsigned char *buf; + c_size_t buf_len; + c_size_t prev_offset; // This will be useful for later on + c_size_t curr_offset; +}; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +C_STATIC_FORCE_INLINE +void c_Arena_Init(c_Arena_t *a, void *backing_buffer, size_t backing_buffer_length) { + a->buf = (unsigned char *)backing_buffer; + a->buf_len = backing_buffer_length; + a->curr_offset = 0; + a->prev_offset = 0; +} + +C_STATIC_FORCE_INLINE +void c_Arena_Destroy(c_Arena_t *a) { + a->curr_offset = 0; + a->prev_offset = 0; +} + +C_STATIC_FORCE_INLINE +void c_Arena_Free(c_Arena_t *a, void *ptr) { + C_UNUSED(a); + C_UNUSED(ptr); +} + +void *c_Arena_Alloc(c_Arena_t *a, c_size_t size); + +void *c_Arena_Resize(c_Arena_t *a, void *old_memory, size_t old_size, size_t new_size); + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct c_ArenaTemp_s c_ArenaTemp_t; +struct c_ArenaTemp_s { + c_Arena_t *arena; + size_t prev_offset; + size_t curr_offset; +}; + +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->curr_offset; + return temp; +} + +C_STATIC_FORCE_INLINE +void c_ArenaTemp_End(c_ArenaTemp_t temp) { + temp.arena->prev_offset = temp.prev_offset; + temp.arena->curr_offset = temp.curr_offset; +} + +#endif /*INCLUDED_C_ARENA_H*/ diff --git a/Memory/c_Arena.t.c b/Memory/c_Arena.t.c new file mode 100644 index 0000000..18b811e --- /dev/null +++ b/Memory/c_Arena.t.c @@ -0,0 +1,142 @@ +#include "c_Arena.h" +#include +#include + +#define C_ASSERT_ALIGNED_TO(ptr, align) \ + C_ASSERT(((uintptr_t)(ptr) % (align)) == 0, "Arena 分配出的地址不符合对齐边界") + +/* ============================================================================== + * 🧪 1. 测试 Arena 基础线性流水线分配 (c_Arena_Init / c_Arena_Alloc) + * ============================================================================== */ +C_TEST_CASE(test_arena_basic_bump_allocation) +{ + /* 1. 向传统堆借一块 1024 字节的大对齐沙盒作为 Arena 的后备支撑 */ + size_t backing_size = 1024; + void* backing_buffer = C_ALLOC(backing_size); + C_ASSERT(backing_buffer != NULL, "Backing buffer 申请成功"); + + c_Arena_t arena; + c_Arena_Init(&arena, backing_buffer, backing_size); + + /* 验证初始状态 */ + C_ASSERT_INT_EQ(arena.curr_offset, 0, "初始偏移量必须为 0"); + C_ASSERT_INT_EQ(arena.buf_len, backing_size, "沙盒边界容量对齐正确"); + + /* 2. 连续切分三块不同大小的内存 */ + int* arr1 = (int*)c_Arena_Alloc(&arena, 5 * sizeof(int)); /* 20 字节 */ + double* val1 = (double*)c_Arena_Alloc(&arena, sizeof(double)); /* 8 字节 */ + char* str1 = (char*)c_Arena_Alloc(&arena, 7); /* 7 字节 */ + + C_ASSERT(arr1 != NULL, "分配 arr1 成功"); + C_ASSERT(val1 != NULL, "分配 val1 成功"); + C_ASSERT(str1 != NULL, "分配 str1 成功"); + + /* 3. 核心物理对齐验证:检查 Bump 出来的每一块地址是否都顺应了 C_ALIGN_SIZE (16字节) 最大自然对齐 */ + C_ASSERT_ALIGNED_TO(arr1, C_ALIGN_SIZE); + C_ASSERT_ALIGNED_TO(val1, C_ALIGN_SIZE); + C_ASSERT_ALIGNED_TO(str1, C_ALIGN_SIZE); + + /* 验证 c_Arena_Free 为安全的无操作空函数 */ + c_Arena_Free(&arena, arr1); + + /* 销毁并解绑外部堆 */ + c_Arena_Destroy(&arena); + C_FREE(backing_buffer); +} + + +/* ============================================================================== + * 🧪 2. 测试快照保存与一键局部内存回滚 (c_ArenaTemp_Begin / End) + * ============================================================================== */ +C_TEST_CASE(test_arena_snapshot_scratch_rollback) +{ + size_t backing_size = 512; + void* backing_buffer = C_ALLOC(backing_size); + + c_Arena_t arena; + c_Arena_Init(&arena, backing_buffer, backing_size); + + /* 1. 先在常驻区分配一点持久数据 */ + int* persistent_data = (int*)c_Arena_Alloc(&arena, sizeof(int)); + *persistent_data = 8888; + size_t persistent_offset = arena.curr_offset; + + /* 2. 📸 拍下当前的物理快照 (保存当前划线位置) */ + c_ArenaTemp_t scratchpad = c_ArenaTemp_Begin(&arena); + C_ASSERT_INT_EQ(scratchpad.curr_offset, persistent_offset, "快照必须精准锚定当前偏移量"); + + /* 3. 在这个局部临时沙盒里疯狂申请内存进行高频计算 */ + for (int i = 0; i < 10; ++i) { + void* temp_chunk = c_Arena_Alloc(&arena, 32); + C_UNUSED(temp_chunk); + } + C_ASSERT(arena.curr_offset > persistent_offset, "临时分配导致划线指针一路向后推进"); + + /* 4. 🔄 结束局部计算,执行终极快照回滚 */ + c_ArenaTemp_End(scratchpad); + + /* 断言:所有的临时内存被逻辑上“全量粉碎”,划线指针瞬间完好无损地缩回到持久化线之后! */ + C_ASSERT_INT_EQ(arena.curr_offset, persistent_offset, "一键回滚后,curr_offset 必须退回到最初的快照起点!"); + C_ASSERT_INT_EQ(*persistent_data, 8888, "常驻区的数据绝不能受到回滚机制的破坏"); + + c_Arena_Destroy(&arena); + C_FREE(backing_buffer); +} + + +/* ============================================================================== + * 🧪 3. 测试 `prev_offset` 驱动的高能原地重分配优化 (c_Arena_Resize) + * ============================================================================== */ +C_TEST_CASE(test_arena_inplace_resize_optimization) +{ + size_t backing_size = 512; + void* backing_buffer = C_ALLOC(backing_size); + + c_Arena_t arena; + c_Arena_Init(&arena, backing_buffer, backing_size); + + /* 1. 申请第一个隔离块,切断最底部的零线 */ + c_Arena_Alloc(&arena, 16); + + /* 2. 申请目标数组:当前它处于整个沙盒的最顶端线(其偏移记录在 prev_offset 中) */ + int* array = (int*)c_Arena_Alloc(&arena, 4 * sizeof(int)); /* 申请 16 字节 */ + uintptr_t original_address = (uintptr_t)array; + + array[0] = 100; + array[3] = 400; + + /* 3. 🎯 触发原地扩容:将其扩大到原先的 4 倍大小 (64字节) */ + int* resized_array = (int*)c_Arena_Resize(&arena, array, 4 * sizeof(int), 16 * sizeof(int)); + uintptr_t new_address = (uintptr_t)resized_array; + + /* 🌟 核心断言:由于其属于上一次的最上方分配,Resize 逻辑应当直接划线, + 其返回的虚拟内存物理地址必须和原来完全一模一样,实现了零拷贝原地扩容! */ + C_ASSERT_INT_EQ(new_address, original_address, "Arena 对最后一次分配的块进行 Resize 时,必须触发零拷贝原地扩容优化!"); + C_ASSERT_INT_EQ(resized_array[0], 100, "扩容后元素 0 的数据保持完好"); + C_ASSERT_INT_EQ(resized_array[3], 400, "扩容后元素 3 的数据保持完好"); + + /* 4. 反向边界测试:如果在中间安插了新的分配,阻断了顶端线 */ + c_Arena_Alloc(&arena, 16); /* 抢占了顶端线 */ + + /* 此时再次对原本的 array 执行 Resize,由于其已经不是最新的分配,原地优化将会失效 */ + int* copy_moved_array = (int*)c_Arena_Resize(&arena, resized_array, 16 * sizeof(int), 32 * sizeof(int)); + uintptr_t moved_address = (uintptr_t)copy_moved_array; + + /* 断言:此时原地优化被安全拦截,退化为分配新空间+数据迁移,地址应当发生改变 */ + C_ASSERT(moved_address != original_address, "被阻断的块执行 Resize 时,应当安全退化为新空间搬运迁移"); + C_ASSERT_INT_EQ(copy_moved_array[0], 100, "搬运迁移后数据依然保持完整性"); + + c_Arena_Destroy(&arena); + C_FREE(backing_buffer); +} + + +int main(int argc, char** argv) { + C_TEST_SUITE_BEGIN(MemoryArenaCoreTestSuite) + + C_RUN_TEST_CASE(test_arena_basic_bump_allocation); + C_RUN_TEST_CASE(test_arena_snapshot_scratch_rollback); + C_RUN_TEST_CASE(test_arena_inplace_resize_optimization); + + C_TEST_SUITE_END() +} \ No newline at end of file diff --git a/Memory/c_Buddy.c b/Memory/c_Buddy.c new file mode 100644 index 0000000..3eaad81 --- /dev/null +++ b/Memory/c_Buddy.c @@ -0,0 +1,171 @@ +#include +#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/Memory/c_Buddy.h b/Memory/c_Buddy.h new file mode 100644 index 0000000..2ac28da --- /dev/null +++ b/Memory/c_Buddy.h @@ -0,0 +1,30 @@ +#ifndef INCLUDED_C_BUDDY_H +#define INCLUDED_C_BUDDY_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_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/Memory/c_Buddy.t.c b/Memory/c_Buddy.t.c new file mode 100644 index 0000000..368fb03 --- /dev/null +++ b/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_Aligned_Alloc(1024*KB, 8, __FILE__, __LINE__); + + 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_Aligned_Free(block, __FILE__, __LINE__); +} + +// ========================================== +// 測試用例 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_ALIGNED_ALLOC(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_ALIGNED_FREE(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/Memory/c_FixedPool.c b/Memory/c_FixedPool.c new file mode 100644 index 0000000..bf5b7d3 --- /dev/null +++ b/Memory/c_FixedPool.c @@ -0,0 +1,79 @@ +#include +#include +#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_OK; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +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; + assert(self->instanceCount >= 0); +} + +void c_FixedPool_DryUp(c_FixedPool_t* self) { + if (!self) return; + self->freelist = 0; + self->instanceCount = 0; +} + diff --git a/Memory/c_FixedPool.h b/Memory/c_FixedPool.h new file mode 100644 index 0000000..a08bb26 --- /dev/null +++ b/Memory/c_FixedPool.h @@ -0,0 +1,26 @@ +#ifndef INCLUDED_C_FIXEDPOOL_H +#define INCLUDED_C_FIXEDPOOL_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + int objSize; + c_int_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/Memory/c_FixedPool.t.c b/Memory/c_FixedPool.t.c new file mode 100644 index 0000000..acb8fe6 --- /dev/null +++ b/Memory/c_FixedPool.t.c @@ -0,0 +1,96 @@ +#include "c_FixedPool.h" +#include +#include +#include "c_Test.h" +#include "c_Memory.h" + +/* ============================================================================== + * 🧪 1. 测试静态“零堆分配”初始化与边界耗尽拦截 + * ============================================================================== */ +C_TEST_CASE(test_fixed_pool_zero_heap_allocation) +{ + /* 1. 模拟在外部开辟一块 64 字节的纯裸静态缓冲区(预期能刚好容纳 2 个 32 字节网格) */ + int static_buffer_size = 64; + void* my_external_static_buffer = C_ALLOC(static_buffer_size); + + c_FixedPool_t pool; + /* 🛠️ 核心调用:在初始化时直接绑定外部给定的裸空间,内部绝对不发生任何系统级 malloc */ + c_err_t err = c_FixedPool_Init(&pool, 24, my_external_static_buffer, static_buffer_size); + + C_ASSERT_INT_EQ(err, C_SUCCESS, "静态内存池绑定初始化必须返回 C_OK"); + C_ASSERT_INT_EQ(pool.objSize, 32, "对象尺寸自动向上休整为最大自然对齐边界(16字节倍数)"); + + /* 2. 连续分发出仅有的 2 个格子 */ + void* node_1 = c_FixedPool_Alloc(&pool); + void* node_2 = c_FixedPool_Alloc(&pool); + C_ASSERT(node_1 != NULL && node_2 != NULL, "静态网格正常分配通过"); + C_ASSERT_INT_EQ(pool.instanceCount, 2, "当前贷出活跃数为 2"); + + /* 3. 🚨 边界触碰:由于外部给的空间已用尽,且本池严禁私自动态扩容,再次 Alloc 必须安全返回 NULL */ + void* overflow_node = c_FixedPool_Alloc(&pool); + C_ASSERT(overflow_node == NULL, "静态内存池耗尽后,再次 Alloc 必须严格拦截并安全回传 NULL"); + + // 清理测试用的外部大模拟区 + c_FixedPool_DryUp(&pool); + c_FixedPool_Destroy(&pool); + C_FREE(my_external_static_buffer); +} + + +/* ============================================================================== + * 🧪 2. 测试中途外部喂入新页补充弹药 (c_FixedPool_AddBlock) + * ============================================================================== */ +C_TEST_CASE(test_fixed_pool_external_ammunition_supply) +{ + /* 准备初始极微小外部缓冲区(仅容纳 1 个对象) */ + int buf_size_1 = 32; + void* ext_buf_1 = C_ALLOC(buf_size_1); + + c_FixedPool_t pool; + c_FixedPool_Init(&pool, 32, ext_buf_1, buf_size_1); + + /* 掏空这仅有的一个额度 */ + void* active_ptr = c_FixedPool_Alloc(&pool); + C_ASSERT(active_ptr != NULL, "初始第一发额度分配成功"); + C_ASSERT(c_FixedPool_Alloc(&pool) == NULL, "此时池子已瞬间干瘪"); + + /* 🛠️ 核心调用:外部在中途遇到短缺时,手动申请一块新静态大页,通过 AddBlock 喂给池子 */ + int buf_size_2 = 96; /* 预期能额外提供 3 个全新格子 */ + void* ext_buf_2 = C_ALLOC(buf_size_2); + + c_err_t add_err = c_FixedPool_AddBlock(&pool, ext_buf_2, buf_size_2); + C_ASSERT_INT_EQ(add_err, C_ERR_OK, "手动注入新页弹药必须返回 C_OK"); + + /* 4. 再次检验:池子应当立刻恢复发牌能力,连续顺畅吐出 3 个新对象 */ + void* extra_node_1 = c_FixedPool_Alloc(&pool); + void* extra_node_2 = c_FixedPool_Alloc(&pool); + void* extra_node_3 = c_FixedPool_Alloc(&pool); + + C_ASSERT(extra_node_1 != NULL && extra_node_2 != NULL && extra_node_3 != NULL, "补给新弹药后,FixedPool 必须成功复活"); + C_ASSERT_INT_EQ(pool.instanceCount, 4, "当前总计贷出 4 个活跃对象"); + + /* 5. 验证 DryUp 强行重置计数 */ + c_FixedPool_DryUp(&pool); + C_ASSERT_INT_EQ(pool.instanceCount, 0, "执行 DryUp 后,贷出追踪计数必须瞬间斩断强制复位为 0"); + + /* 清理现场 */ + // c_FixedPool_Free(&pool, extra_node_3); /* 侵入式回收依然畅通 */ + // c_FixedPool_DryUp(&pool); + c_FixedPool_Destroy(&pool); + C_FREE(ext_buf_1); + C_FREE(ext_buf_2); +} + +/* ============================================================================== + * 🚀 测试套件统一注册调度主函数 + * ============================================================================== */ + + +int main(int argc, char** argv){ + C_TEST_SUITE_BEGIN(MemoryFixedPoolCoreTestSuite) + C_RUN_TEST_CASE(test_fixed_pool_zero_heap_allocation); + C_RUN_TEST_CASE(test_fixed_pool_external_ammunition_supply); + C_TEST_SUITE_END() + + return 0; +} diff --git a/Memory/c_Memory.c b/Memory/c_Memory.c new file mode 100644 index 0000000..226cd10 --- /dev/null +++ b/Memory/c_Memory.c @@ -0,0 +1,410 @@ +#include + +#if C_IS_OS_WINDOWS +#include /* Windows 下的 _aligned_malloc 需要此头文件 */ +#endif + +#ifndef NDEBUG +#include +#endif + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#if defined(C_MEMORY_USE_LOCAL_SYSTEM) + +void* c_Memory_Alloc(c_size_t nBytes, const char* file, int line) { + return malloc(nBytes); +} +void* c_Memory_Calloc(c_size_t nCount, c_size_t nBytes, const char* file, int line) { + return calloc(nCount, nBytes); +} +void* c_Memory_Realloc(void* ptr, c_size_t nBytes, const char* file, int line) { + return realloc(ptr, nBytes); +} + +void c_Memory_Free(void* ptr, const char* file, int line) { + if (!ptr) return; + free(ptr); +} + +void* c_Memory_Aligned_Alloc(c_size_t alignment, c_size_t nBytes, const char* file, int line) { + /* 1. 统一防御边界:凡是申请 0 字节的,全生命周期内统一斩断并返回 NULL */ + if (C_UNLIKELY(nBytes == 0)) { + return NULL; + } + + /* 2. 硬件级严苛校验:对齐边界必须大于零,且必须是 2 的有限幂 (如 8, 16, 32, 64) + 利用经典的位运算 (align & (align - 1)) == 0 进行快速断言 */ + if (C_UNLIKELY(alignment == 0 || (alignment & (alignment - 1)) != 0)) { + return NULL; + } + + /* 3. 系统级约束规避:对齐边界绝对不能小于当前平台的指针位宽 (32位系统下为4,64位系统下为8) */ + if (alignment < sizeof(void*)) { + alignment = sizeof(void*); + } + + void* raw_ptr = NULL; + + /* ============================================================================== + * 🚀 跨平台多路物理分支隔离分化 + * ============================================================================== */ +#if C_IS_OS_WINDOWS + /* 无论您本地使用的是 MSVC 还是 MSYS2 MinGW,在 Windows 上均走微软的原生高速对齐堆接口 */ + raw_ptr = _aligned_malloc(nBytes, alignment); + +#elif C_IS_OS_LINUX || C_IS_OS_FREEBSD || C_IS_OS_MACOS + /* 在标准的类 Unix (POSIX) 谱系下,调用 posix_memalign + 注意:它的设计是传入指针的指针,成功时返回 0,失败时返回错误码 */ + int result = posix_memalign(&raw_ptr, alignment, nBytes); + if (C_UNLIKELY(result != 0)) { + raw_ptr = NULL; /* 强制拦截,确保失败时指针处于绝对纯净的 NULL 状态 */ + } + +#else + /* 终极未知平台兜底:普通的 malloc 在 64 位系统下天然具备 16 字节对齐能力,但无法支持更高对齐 */ + /* 如果项目未来被移植到某些不支持 posix_memalign 的极端微内核或嵌入式系统,将在此激活降级 */ + raw_ptr = malloc(nBytes); +#endif + + return raw_ptr; +} + +void* c_Memory_Aligned_Calloc(c_size_t alignment, c_size_t num, c_size_t size, const char* file, int line) { + c_size_t total_bytes = num * size; + if (total_bytes == 0) return NULL; + + // 直接复用我们写好的跨平台对齐分配器 + void* ptr = c_Memory_Aligned_Alloc(alignment, total_bytes, file, line); + if (C_LIKELY(ptr)) { + // 强制物理内存清零,保证安全性 + memset(ptr, 0, total_bytes); + } + return ptr; +} + +void* c_Memory_Aligned_Realloc(size_t alignment, void* ptr, size_t new_size, const char* file, int line) { + if (!ptr) return c_Memory_Aligned_Alloc(alignment, new_size, file, line); + if (new_size == 0) { + c_Memory_Aligned_Free(ptr, file, line); + return NULL; + } + + if (alignment < sizeof(void*)) alignment = sizeof(void*); + void* new_ptr = NULL; + +#if C_IS_OS_WINDOWS + new_ptr = _aligned_realloc(ptr, new_size, alignment); +#else + new_ptr = c_Memory_Aligned_Alloc(alignment, new_size, file, line); + if (C_LIKELY(new_ptr)) { + memcpy(new_ptr, ptr, new_size); + c_Memory_Aligned_Free(ptr, file, line); + } +#endif + return new_ptr; +} + +void c_Memory_Aligned_Free(void* ptr, const char* file, int line) { +#if C_IS_OS_WINDOWS + /* 🚨 微软大坑警告:Windows 下通过 _aligned_malloc 出来的内存, + 必须、绝对、强制使用对应的 _aligned_free 释放!混用普通 free 会直接引发蓝屏或堆破坏崩溃 */ + _aligned_free(ptr); +#else + /* 在 POSIX (Linux, macOS, FreeBSD) 下,posix_memalign 出来的地址本质上只是常规堆的延伸, + 可以直接且必须使用标准的 free() 进行释放 */ + free(ptr); +#endif +} + +#else + +#if !defined(NDEBUG) && defined(C_MEMORY_CHECK_ENABLE) + +C_PACKED_STRUCT_START +typedef struct c_MemoryNode_t { + void* address; + size_t size; + const char* file; + int line; + struct c_MemoryNode_t* next; +} C_PACKED c_MemoryNode_t; +C_PACKED_STRUCT_END + +static c_MemoryNode_t* g_tracker_head = NULL; +static size_t g_active_allocations = 0; +static size_t g_total_allocated_bytes = 0; + +C_STATIC_FORCE_INLINE +int c_tracker_exists(void* ptr) { + c_MemoryNode_t* curr = g_tracker_head; + while (curr) { + if (curr->address == ptr) return 1; + curr = curr->next; + } + return 0; +} + +/* 内部辅助:将分配记录加入追踪器 */ +C_STATIC_FORCE_INLINE +void c_tracker_add(void* ptr, size_t size, const char* file, int line) { + if (!ptr) return; + c_MemoryNode_t* node = (c_MemoryNode_t*)malloc(sizeof(c_MemoryNode_t)); + if (C_UNLIKELY(!node)) return; /* 防御极端内存耗尽 */ + + node->address = ptr; + node->size = size; + node->file = file; + node->line = line; + node->next = g_tracker_head; + g_tracker_head = node; + + g_active_allocations++; + g_total_allocated_bytes += size; +} + +/* 内部辅助:从追踪器中移除释放的记录 */ +C_STATIC_FORCE_INLINE +void c_tracker_remove(void* ptr) { + if (!ptr) return; + c_MemoryNode_t* curr = g_tracker_head; + c_MemoryNode_t* prev = NULL; + + while (curr) { + if (curr->address == ptr) { + if (prev) { prev->next = curr->next; } + else { g_tracker_head = curr->next; } + + g_total_allocated_bytes -= curr->size; + g_active_allocations--; + free(curr); + return; + } + prev = curr; + curr = curr->next; + } +} + + +/* 内部辅助:根据旧指针反查其原始分配大小(用于 POSIX 链下手动迁移数据) */ +C_STATIC_FORCE_INLINE +c_size_t c_tracker_get_size(void* ptr) { + c_MemoryNode_t* curr = g_tracker_head; + while (curr) { + if (curr->address == ptr) return curr->size; + curr = curr->next; + } + return 0; +} + +C_STATIC_FORCE_INLINE +void c_tracker_update(void* old_ptr, void* new_ptr, size_t new_size, const char* file, int line) { + if (!old_ptr) { + c_tracker_add(new_ptr, new_size, file, line); + return; + } + + c_MemoryNode_t* curr = g_tracker_head; + while (curr) { + if (curr->address == old_ptr) { + // 先扣除旧的全局字节计数,加上新的 + g_total_allocated_bytes -= curr->size; + g_total_allocated_bytes += new_size; + + // 更新节点内部物理信息 + curr->address = new_ptr; + curr->size = new_size; + curr->file = file; + curr->line = line; + return; + } + curr = curr->next; + } + + /* 防御边界:如果追踪器里没找到旧指针(例如用户混用了外部指针),则强制当作新指针加入 */ + c_tracker_add(new_ptr, new_size, file, line); +} + +#endif + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +void* c_Memory_Aligned_Alloc(c_size_t alignment, c_size_t size, const char* file, int line) { + if (size == 0) return NULL; + void* raw_ptr = NULL; + + /* 确保对齐边界合法(必须是2的幂且大于指针大小) */ + if (alignment < sizeof(void*)) alignment = sizeof(void*); + +#if C_IS_OS_WINDOWS + /* 无论是 MSVC 还是您的 MSYS2 MinGW,在 Windows 上均调用此原生安全接口 */ + raw_ptr = _aligned_malloc(size, alignment); +#elif C_IS_OS_MACOS + /* macOS 原生 malloc 在满足边界时已经高度对齐,或者使用 posix_memalign */ + if (posix_memalign(&raw_ptr, alignment, size) != 0) { raw_ptr = NULL; } +#elif C_IS_OS_LINUX || C_IS_OS_FREEBSD + /* Linux 和 FreeBSD 的标准 POSIX 对齐接口 */ + if (posix_memalign(&raw_ptr, alignment, size) != 0) { raw_ptr = NULL; } +#else + /* 终极兜底降级方案:常规分配(不对齐) */ + raw_ptr = malloc(size); +#endif + +#if !defined(NDEBUG) && defined(C_MEMORY_CHECK_ENABLE) + /* 注入追踪器 */ + if (C_LIKELY(raw_ptr)) { + c_tracker_add(raw_ptr, size, file, line); + } +#endif + + return raw_ptr; +} + +void c_Memory_Aligned_Free(void* ptr, const char* file, int line) { + if (!ptr) return; + + C_UNUSED(file); + C_UNUSED(line); + +#if !defined(NDEBUG) && defined(C_MEMORY_CHECK_ENABLE) + /* 从日志中移除 */ + c_tracker_remove(ptr); +#endif + + +#if C_IS_OS_WINDOWS + _aligned_free(ptr); /* Windows 的对齐内存必须用配套的 free 释放 */ +#else + free(ptr); /* POSIX (Linux/Mac/FreeBSD) 的 posix_memalign 直接用常规 free 即可 */ +#endif +} + +void* c_Memory_Aligned_Calloc(c_size_t alignment, c_size_t num, c_size_t size, const char* file, int line) { + c_size_t total_bytes = num * size; + if (total_bytes == 0) return NULL; + + // 直接复用我们写好的跨平台对齐分配器 + void* ptr = c_Memory_Aligned_Alloc(alignment, total_bytes, file, line); + if (C_LIKELY(ptr)) { + // 强制物理内存清零,保证安全性 + memset(ptr, 0, total_bytes); + } + return ptr; +} + +void* c_Memory_Aligned_Realloc(size_t alignment, void* ptr, size_t new_size, const char* file, int line) { +#if !(!defined(NDEBUG) && defined(C_MEMORY_CHECK_ENABLE)) && defined(C_MEMORY_CHECK_ENABLE) + /* Debug 模式下的野指针前置拦截 */ + if (ptr != NULL) { + if (C_UNLIKELY(!c_tracker_exists(ptr))) { + fprintf(stderr, "🚨 [CRITICAL ERROR] %s:%d 试图用未知野指针调用 Aligned_Realloc: %p!\n", file, line, ptr); + assert(0 && "c_Memory_Aligned_Realloc Fail: Wild pointer!"); + } + } +#endif + + if (!ptr) return c_Memory_Aligned_Alloc(alignment, new_size, file, line); + if (new_size == 0) { + c_Memory_Aligned_Free(ptr, file, line); + return NULL; + } + + if (alignment < sizeof(void*)) alignment = sizeof(void*); + void* new_ptr = NULL; + +#if !(!defined(NDEBUG) && defined(C_MEMORY_CHECK_ENABLE)) && !C_IS_OS_WINDOWS + /* Debug 模式下非 Windows 系统的“严格毒化迁移” */ + c_size_t old_size = c_tracker_get_size(ptr); + new_ptr = c_Memory_Aligned_Alloc(alignment, new_size, file, line); + if (C_LIKELY(new_ptr)) { + c_size_t copy_size = (old_size < new_size) ? old_size : new_size; + if (copy_size > 0) memcpy(new_ptr, ptr, copy_size); + memset(ptr, 0xDD, old_size); /* 毒化 */ + c_Memory_Aligned_Free(ptr, file, line); + c_tracker_update(NULL, new_ptr, new_size, file, line); + } +#else + /* Release 生产环境或 Windows 环境下的原生高性能逻辑 */ +#if C_IS_OS_WINDOWS + new_ptr = _aligned_realloc(ptr, new_size, alignment); +#else + new_ptr = c_Memory_Aligned_Alloc(alignment, new_size, file, line); + if (C_LIKELY(new_ptr)) { + memcpy(new_ptr, ptr, new_size); + c_Memory_Aligned_Free(ptr, file, line); + } +#endif + +#if !defined(NDEBUG) && defined(C_MEMORY_CHECK_ENABLE) + if (C_LIKELY(new_ptr)) { + c_tracker_update(ptr, new_ptr, new_size, file, line); + } +#endif +#endif + +#if !defined(NDEBUG) && defined(C_MEMORY_CHECK_ENABLE) + if (C_UNLIKELY(!new_ptr)) { + fprintf(stderr, "🚨 [OOM CRITICAL] %s:%d 内存耗尽,无法重分配 %lu 字节!\n", file, line, (unsigned long)new_size); + assert(0 && "Out of Memory!"); + } +#endif + + return new_ptr; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +void* c_Memory_Alloc(c_size_t nBytes, const char* file, int line) { + return c_Memory_Aligned_Alloc(C_ALIGN_SIZE, nBytes, file, line); +} + +void* c_Memory_Calloc(c_size_t nCount, c_size_t nBytes, const char* file, int line) { + return c_Memory_Aligned_Calloc(C_ALIGN_SIZE, nCount, nBytes, file, line); +} + +void* c_Memory_Realloc(void* ptr, c_size_t nBytes, const char* file, int line) { + return c_Memory_Aligned_Realloc(C_ALIGN_SIZE, ptr, nBytes, file, line); +} + +void c_Memory_Free(void* ptr, const char* file, int line) { + c_Memory_Aligned_Free(ptr, file, line); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#if !defined(NDEBUG) && defined(C_MEMORY_CHECK_ENABLE) +c_size_t c_memory_leak_report(void) { + if (g_active_allocations == 0) { + return 0; + } + + printf("\n🚨 [MEMORY AUDIT] 检测到内存泄漏! 未释放的泄漏列表如下:\n"); + c_MemoryNode_t* curr = g_tracker_head; + while (curr) { + printf(" -> 泄漏地址: %p | 大小: %lu 字节 | 位置: %s (第 %d 行)\n", + curr->address, (unsigned long)curr->size, curr->file, curr->line); + curr = curr->next; + } + return g_total_allocated_bytes; +} + +void c_memory_tracker_reset(void) { + c_MemoryNode_t* curr = g_tracker_head; + while (curr) { + c_MemoryNode_t* next = curr->next; + free(curr); + curr = next; + } + g_tracker_head = NULL; + g_active_allocations = 0; + g_total_allocated_bytes = 0; +} +#endif + +#endif diff --git a/Memory/c_Memory.h b/Memory/c_Memory.h new file mode 100644 index 0000000..ee0574e --- /dev/null +++ b/Memory/c_Memory.h @@ -0,0 +1,55 @@ +#ifndef INCLUDED_C_MEMORY_H +#define INCLUDED_C_MEMORY_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_ALIGNMENT_H +#include +#endif /*INCLUDED_C_ALIGNMENT_H*/ + +#ifndef INCLUDED_ASSERT_H +#define INCLUDED_ASSERT_H +#include +#endif /*INCLUDED_ASSERT_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +void* c_Memory_Alloc(c_size_t nBytes, const char* file, int line); +void* c_Memory_Calloc(c_size_t nCount, c_size_t nBytes, const char* file, int line); +void* c_Memory_Realloc(void* ptr, c_size_t nBytes, const char* file, int line); +void c_Memory_Free(void* ptr, const char* file, int line); + +void* c_Memory_Aligned_Alloc(c_size_t alignment, c_size_t nBytes, const char* file, int line); +void* c_Memory_Aligned_Calloc(c_size_t alignment, c_size_t num, c_size_t size, const char* file, int line); +void* c_Memory_Aligned_Realloc(size_t alignment, void* ptr, size_t new_size, const char* file, int line); +void c_Memory_Aligned_Free(void* ptr, const char* file, int line); + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#if !defined(NDEBUG) && defined(C_MEMORY_CHECK_ENABLE) +c_size_t c_memory_leak_report(void); +void c_memory_tracker_reset(void); +#endif + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define C_ALLOC(nbytes) c_Memory_Alloc(nbytes, __FILE__, __LINE__) +#define C_CALLOC(count, nbytes) c_Memory_Calloc(count, nbytes, __FILE__, __LINE__) +#define C_REALLOC(ptr, nbytes) c_Memory_Realloc(ptr, nbytes, __FILE__, __LINE__) +#define C_FREE(p) (c_Memory_Free((p), __FILE__, __LINE__), (p)=0) + +#define C_NEW(p) ((p) = C_ALLOC(sizeof(*(p)))) +#define C_NEW0(p) ((p)=C_CALLOC(1, sizeof(*(p)))) +#define C_RESIZE(p, nbytes) ((p)=C_REALLOC((p), (nbytes))) + +#define C_ALIGNED_ALLOC(align, nbytes) c_Memory_Aligned_Alloc((align), (nbytes), __FILE__, __LINE__) +#define C_ALIGNED_FREE(p) (c_Memory_Aligned_Free((p), __FILE__, __LINE__), (p)=0) + +#endif /*INCLUDED_C_MEMORY_H*/ diff --git a/Memory/c_Memory.t.c b/Memory/c_Memory.t.c new file mode 100644 index 0000000..7c8524e --- /dev/null +++ b/Memory/c_Memory.t.c @@ -0,0 +1,76 @@ +#include "c_Memory.h" +#include +#include +#include "c_Test.h" + +#define C_ASSERT_ALIGNED_TO(ptr, align) \ + C_ASSERT(((uintptr_t)(ptr) % (align)) == 0, "内存物理地址必须符合指定的字节对齐边界") + + +C_TEST_CASE(test_natural_alignment_and_macro_flows) +{ + /* 测试 A: 验证普通分配是否完美顺应您设计的最大自然对齐 (16 字节) */ + int* natural_arr = (int*)C_ALLOC(32 * sizeof(int)); + C_ASSERT(natural_arr != NULL, "分配不能为 NULL"); + + /* 🎯 绝杀:现在的默认分配将百分之百通过您的 C_ALIGN_SIZE 自然边界断言! */ + C_ASSERT_ALIGNED_TO(natural_arr, C_ALIGN_SIZE); + + C_FREE(natural_arr); +} + +C_TEST_CASE(test_hardware_cacheline_alignment) +{ + /* 测试 B: 显式强制申请 64 字节对齐(用于后续 Math 模块的 AVX 向量化) */ + /* 修正:申请大小(256字节)必须是 64 字节对齐边界的整数倍 */ + double* simd_vector = (double*)c_Memory_Aligned_Alloc(64, 32 * sizeof(double), __FILE__, __LINE__); + C_ASSERT(simd_vector != NULL, "强行显式对齐分配不能为 NULL"); + + /* 验证底层跨平台 _aligned_malloc 或 posix_memalign 强行开辟 64 字节边界成功 */ + C_ASSERT_ALIGNED_TO(simd_vector, 64); + + c_Memory_Aligned_Free(simd_vector, __FILE__, __LINE__); +} + +C_TEST_CASE(test_resize_integrity_with_natural_bounds) +{ + /* 测试 C: 测试您定义的 C_RESIZE 动态增减流 */ + double* dynamic_data = (double*)C_ALLOC(2 * sizeof(double)); /* 初始分配 16 字节 */ + C_ASSERT(dynamic_data != NULL, "初始分配成功"); + + dynamic_data[0] = 3.14159265; + dynamic_data[1] = 2.71828182; + + /* 扩容到 16 个 double (128 字节,依然是 C_ALIGN_SIZE 的倍数) */ + C_RESIZE(dynamic_data, 16 * sizeof(double)); + C_ASSERT(dynamic_data != NULL, "C_RESIZE 扩容成功"); + C_ASSERT_ALIGNED_TO(dynamic_data, C_ALIGN_SIZE); + + /* 数据完整性校验 */ + C_ASSERT_DOUBLE_EQ(dynamic_data[0], 3.14159265, "重分配后元素0完好无损"); + C_ASSERT_DOUBLE_EQ(dynamic_data[1], 2.71828182, "重分配后元素1完好无损"); + + C_FREE(dynamic_data); +} + + +/* ============================================================================== + * 🚀 测试用例统一调度主函数 + * ============================================================================== */ +int main(int argc, char** argv) { + C_TEST_SUITE_BEGIN(CrossPlatformMemoryTestSuite) + + /* 依次注入并运行函数式测试用例 */ + C_RUN_TEST_CASE(test_natural_alignment_and_macro_flows); + C_RUN_TEST_CASE(test_hardware_cacheline_alignment); + C_RUN_TEST_CASE(test_resize_integrity_with_natural_bounds); + + /* 终极防线:测试套件自身的闭环审计 */ +#if !defined(NDEBUG) && defined(C_MEMORY_CHECK_ENABLE) + printf("\n[Final Guard] 正在对全套件本身执行终极内存合规复盘..."); + c_size_t self_leaks = c_memory_leak_report(); + C_ASSERT_INT_EQ(self_leaks, 0, "确保本测试文件自身没有任何漏掉 C_FREE 的内存尾巴"); +#endif + + C_TEST_SUITE_END() +} \ No newline at end of file diff --git a/Memory/c_Pool.c b/Memory/c_Pool.c new file mode 100644 index 0000000..798e0b7 --- /dev/null +++ b/Memory/c_Pool.c @@ -0,0 +1,161 @@ +#include +#include +#include +#include "c_Alignment.h" +#include "c_Macros.h" + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#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; + C_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 = C_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) { + if (!self || !self->blockAllocator) { + return C_ERR_PARAM; + } + 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_OK; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +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; + C_NEW(self->blockAllocator); + if (!self->blockAllocator) { + return C_ERR_NOMEM; + } + c_PoolBlockList_Init(self->blockAllocator); + + return C_ERR_OK; +} + +void c_Pool_Destroy(c_Pool_t* self) { + if (0==self->instanceCount) { + c_PoolBlockList_Destroy(self->blockAllocator); + C_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_OK) { + 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) { +#if 0 + c_PoolBlockList_Destroy(self->blockAllocator); + C_FREE(self->blockAllocator); + self->freelist = NULL; + self->instanceCount = 0; +#endif + + + if (!self) { + return; + } + + /* 强制洗净 freelist 自由网 */ + self->freelist = NULL; + self->instanceCount = 0; + + if (!self->blockAllocator) { + return; + } + + /* 🛠️ 链表反向全量闪放:将所有内部动态追加入驻的 Block 页一键逻辑全量清零, + 这样所有的格子会被强制瞬间退回可用状态,而无需反复 free */ + c_PoolBlockLink_t* curr = self->blockAllocator->list; + while (curr) { + unsigned char* byte_ptr = (unsigned char*)curr->block; + for (int i = 0; i < self->chunkSize; ++i) { + struct c_PoolLink_t* link = (struct c_PoolLink_t*)&byte_ptr[i * self->objSize]; + link->next = self->freelist; + self->freelist = link; + } + curr = curr->next; + } +} diff --git a/Memory/c_Pool.h b/Memory/c_Pool.h new file mode 100644 index 0000000..f92b0f8 --- /dev/null +++ b/Memory/c_Pool.h @@ -0,0 +1,35 @@ +#ifndef INCLUDED_C_POOL_H +#define INCLUDED_C_POOL_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_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/Memory/c_Pool.t.c b/Memory/c_Pool.t.c new file mode 100644 index 0000000..8163d68 --- /dev/null +++ b/Memory/c_Pool.t.c @@ -0,0 +1,87 @@ +#include "c_Pool.h" +#include +#include +#include "c_Test.h" +#include "c_Memory.h" + +#define C_ASSERT_ALIGNED_TO(ptr, align) \ + C_ASSERT(((uintptr_t)(ptr) % (align)) == 0, "Pool分配出的块地址不符合对齐边界") + + +/* ============================================================================== + * 🧪 1. 测试全自动“无感自愈扩容”核心行为流 (c_Pool_Alloc) + * ============================================================================== */ +C_TEST_CASE(test_pool_auto_healing_expansion) +{ + c_Pool_t pool; + /* 初始化:每个块 24 字节,每页(ChunkSize)只装载极小的 2 个块 */ + c_err_t err = c_Pool_Init(&pool, 24, 2); + C_ASSERT_INT_EQ(err, C_ERR_OK, "弹性自愈内存池初始化必须返回 C_OK"); + C_ASSERT_INT_EQ(pool.instanceCount, 0, "初始活跃贷出对象数必须为 0"); + + /* 1. 连续掏空第一页的 2 个默认格子 */ + void* block_1 = c_Pool_Alloc(&pool); + void* block_2 = c_Pool_Alloc(&pool); + C_ASSERT(block_1 != NULL && block_2 != NULL, "第一页的额度分配顺利通过"); + C_ASSERT_INT_EQ(pool.instanceCount, 2, "此时贷出计数必须动态变更为 2"); + + /* 2. 🎯 核心高能看点:在上一版设计中,此时再 Alloc 会直接返回 NULL。 + 但在这一代高级设计下,池子会在内部发现 freelist 为空,从而自动追加串联一个新 Page! */ + void* block_3_auto_healed = c_Pool_Alloc(&pool); + + /* 断言:分配绝对不能为 NULL,上层代码完全没有感知到任何耗尽异常! */ + C_ASSERT(block_3_auto_healed != NULL, "耗尽时追加申请,分配器必须完成全自动无感自愈扩容!"); + C_ASSERT_INT_EQ(pool.instanceCount, 3, "贷出计数动态累加为 3"); + + /* 规范回收其中两个 */ + c_Pool_Free(&pool, block_2); + c_Pool_Free(&pool, block_3_auto_healed); + C_ASSERT_INT_EQ(pool.instanceCount, 1, "主动还回 2 个对象后,instanceCount 必须平滑退回 1"); + + c_Pool_DryUp(&pool); + c_Pool_Destroy(&pool); +} + + +/* ============================================================================== + * 🧪 2. 测试 `c_Pool_DryUp` 强制全量洗净一键清零机制 + * ============================================================================== */ +C_TEST_CASE(test_pool_dry_up_force_reset) +{ + c_Pool_t pool; + c_Pool_Init(&pool, sizeof(double), 5); /* 每页 5 个对象 */ + + /* 连续狂点分配 12 次,这会迫使池子在内部自动开辟 3 个 Page 页(5 + 5 + 5 = 15) */ + for (int i = 0; i < 12; ++i) { + void* ptr = c_Pool_Alloc(&pool); + C_UNUSED(ptr); + } + C_ASSERT_INT_EQ(pool.instanceCount, 12, "12 个活跃对象驻留在外"); + + /* 🛠️ 执行高能一键强制回收 */ + c_Pool_DryUp(&pool); + + /* 断言:执行一键 DryUp 后,无论刚才贷出了多少,活跃计数瞬间全量归零! */ + C_ASSERT_INT_EQ(pool.instanceCount, 0, "执行 DryUp 后,活跃贷出计数必须强行全量斩断复位为 0"); + + /* 此时,池子内部历史上申请的 3 个物理大页并没有被销毁(不释放传统堆,防止高频申请时的性能抖动), + 但所有的 15 个空闲格子已经被全部强行重新串联回 freelist,可以直接重新欢快地发牌复用! */ + void* fresh_ptr = c_Pool_Alloc(&pool); + C_ASSERT(fresh_ptr != NULL, "DryUp 后,内存池可以在零传统堆开销下立刻无缝重新投入使用"); + + c_Pool_Free(&pool, fresh_ptr); + c_Pool_Destroy(&pool); +} + +/* ============================================================================== + * 🚀 测试套件统一注册调度主函数 + * ============================================================================== */ + +int main(int argc, char** argv){ + C_TEST_SUITE_BEGIN(MemoryPoolAutoHealingTestSuite) + C_RUN_TEST_CASE(test_pool_auto_healing_expansion); + C_RUN_TEST_CASE(test_pool_dry_up_force_reset); + C_TEST_SUITE_END() + + return 0; +} diff --git a/README.md b/README.md index fc0e666..870266a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,25 @@ # cKit -C Development Kit \ No newline at end of file +C Development Kit + +# 安装 MSYS2 UCRT64 环境 + +```shell +# 1. 安装基础工具链(GCC 编译器、GDB 调试器等) +pacman -S --needed mingw-w64-ucrt-x86_64-toolchain + +# 2. 安装现代构建工具 Ninja(性能远超传统的 make) +pacman -S --needed mingw-w64-ucrt-x86_64-ninja + +# 3. 安装 CMake(作为 IDE 备用或终端独立调用) +pacman -S --needed mingw-w64-ucrt-x86_64-cmake +``` + +# 构建 +```shell +# 以 UCRT64 预设进行配置 +cmake --preset msys2-ucrt64 + +# 一键编译 +cmake --build --preset build-windows-mingw +``` \ No newline at end of file diff --git a/c_Config.h.in b/c_Config.h.in new file mode 100644 index 0000000..432755b --- /dev/null +++ b/c_Config.h.in @@ -0,0 +1,9 @@ +#ifndef INCLUDED_C_CONFIG_H +#define INCLUDED_C_CONFIG_H + +#define C_SIZEOF_VOID_P @CMAKE_SIZEOF_VOID_P@ + +#cmakedefine C_MEMORY_USE_LOCAL_SYSTEM @C_MEMORY_USE_LOCAL_SYSTEM@ +#cmakedefine C_MEMORY_CHECK_ENABLE @C_MEMORY_CHECK_ENABLE@ + +#endif /* INCLUDED_C_CONFIG_H */