20260829 Redesign
This commit is contained in:
@@ -66,3 +66,10 @@ Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
# CMake Used Defined Presets
|
||||
CMakeUserPresets.json
|
||||
|
||||
#Clion
|
||||
.idea/
|
||||
cmake-build-*/
|
||||
build/
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#include <c_Compiler.h>
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef INCLUDED_C_COMPILER_H
|
||||
#define INCLUDED_C_COMPILER_H
|
||||
|
||||
#ifndef INCLUDED_C_PLATFORM_H
|
||||
#include <c_Platform.h>
|
||||
#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*/
|
||||
@@ -0,0 +1 @@
|
||||
#include <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*/
|
||||
@@ -0,0 +1 @@
|
||||
#include <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*/
|
||||
@@ -0,0 +1 @@
|
||||
#include <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*/
|
||||
@@ -0,0 +1 @@
|
||||
#include <c_Macros.h>
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
#ifndef INCLUDED_C_MACROS_H
|
||||
#define INCLUDED_C_MACROS_H
|
||||
|
||||
#ifndef INCLUDED_MATH_H
|
||||
#define INCLUDED_MATH_H
|
||||
#include <math.h>
|
||||
#endif /*INCLUDED_MATH_H*/
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#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*/
|
||||
@@ -0,0 +1 @@
|
||||
#include <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*/
|
||||
@@ -0,0 +1,29 @@
|
||||
#include "c_Platform.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include <c_Test.h>
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
#ifndef INCLUDED_C_TEST_H
|
||||
#define INCLUDED_C_TEST_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include <time.h>
|
||||
#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*/
|
||||
@@ -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()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include <c_Types.h>
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#define INCLUDED_C_TYPES_H
|
||||
|
||||
#ifndef INCLUDED_C_CONFIG_H
|
||||
#include <c_Config.h>
|
||||
#endif /*INCLUDED_C_CONFIG_H*/
|
||||
|
||||
#ifndef INCLUDED_STDINT_H
|
||||
#define INCLUDED_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif /*INCLUDED_STDINT_H*/
|
||||
|
||||
#ifndef INCLUDED_STDBOOL_H
|
||||
#define INCLUDED_STDBOOL_H
|
||||
#include <stdbool.h>
|
||||
#endif /*INCLUDED_STDBOOL_H*/
|
||||
|
||||
#ifndef INCLUDED_STRING_H
|
||||
#define INCLUDED_STRING_H
|
||||
#include <string.h>
|
||||
#endif /*INCLUDED_STRING_H*/
|
||||
|
||||
#ifndef INCLUDED_TIME_H
|
||||
#define INCLUDED_TIME_H
|
||||
#include <time.h>
|
||||
#endif /*INCLUDED_TIME_H*/
|
||||
|
||||
#ifndef INCLUDED_INTTYPES_H
|
||||
#define INCLUDED_INTTYPES_H
|
||||
#include <inttypes.h>
|
||||
#endif /*INCLUDED_INTTYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_FLOAT_H
|
||||
#define INCLUDED_FLOAT_H
|
||||
#include <float.h>
|
||||
#endif /*INCLUDED_FLOAT_H*/
|
||||
|
||||
#ifndef INCLUDED_C_PLATFORM_H
|
||||
#include <c_Platform.h>
|
||||
#endif /*INCLUDED_C_PLATFORM_H*/
|
||||
|
||||
|
||||
#ifndef INCLUDED_C_COMPILER_H
|
||||
#include <c_Compiler.h>
|
||||
#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*/
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char** argv){
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -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()
|
||||
@@ -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" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include <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 <float.h>
|
||||
#endif /*INCLUDED_FLOAT_H*/
|
||||
|
||||
#ifndef INCLUDED_STDINT_H
|
||||
#define INCLUDED_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif /*INCLUDED_STDINT_H*/
|
||||
|
||||
#ifndef INCLUDED_MATH_H
|
||||
#define INCLUDED_MATH_H
|
||||
#include <math.h>
|
||||
#endif /*INCLUDED_MATH_H*/
|
||||
|
||||
#ifndef INCLUDED_C_COMPILER_H
|
||||
#include <c_Compiler.h>
|
||||
#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*/
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "c_float.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include <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*/
|
||||
@@ -0,0 +1,92 @@
|
||||
#include <c_Arena.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
#ifndef INCLUDED_C_ARENA_H
|
||||
#define INCLUDED_C_ARENA_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALIGNMENT_H
|
||||
#include <c_Alignment.h>
|
||||
#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*/
|
||||
@@ -0,0 +1,142 @@
|
||||
#include "c_Arena.h"
|
||||
#include <c_Test.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
#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()
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
#include <c_Buddy.h>
|
||||
#include <c_Alignment.h>
|
||||
#include <c_Memory.h>
|
||||
#include <c_Macros.h>
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef INCLUDED_C_BUDDY_H
|
||||
#define INCLUDED_C_BUDDY_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#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*/
|
||||
@@ -0,0 +1,245 @@
|
||||
#include "c_Buddy.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdalign.h> // C11 標準
|
||||
#include <c_Memory.h>
|
||||
|
||||
// 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");
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#include <c_FixedPool.h>
|
||||
#include <c_Alignment.h>
|
||||
#include <c_Macros.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
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; p<last; p+=self->objSize) {
|
||||
((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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef INCLUDED_C_FIXEDPOOL_H
|
||||
#define INCLUDED_C_FIXEDPOOL_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#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*/
|
||||
@@ -0,0 +1,96 @@
|
||||
#include "c_FixedPool.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
#include <c_Memory.h>
|
||||
|
||||
#if C_IS_OS_WINDOWS
|
||||
#include <malloc.h> /* Windows 下的 _aligned_malloc 需要此头文件 */
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <stdio.h>
|
||||
#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
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef INCLUDED_C_MEMORY_H
|
||||
#define INCLUDED_C_MEMORY_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALIGNMENT_H
|
||||
#include <c_Alignment.h>
|
||||
#endif /*INCLUDED_C_ALIGNMENT_H*/
|
||||
|
||||
#ifndef INCLUDED_ASSERT_H
|
||||
#define INCLUDED_ASSERT_H
|
||||
#include <assert.h>
|
||||
#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*/
|
||||
@@ -0,0 +1,76 @@
|
||||
#include "c_Memory.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#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()
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
#include <c_Pool.h>
|
||||
#include <assert.h>
|
||||
#include <c_Memory.h>
|
||||
#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; p<last; p+=self->objSize) {
|
||||
((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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef INCLUDED_C_POOL_H
|
||||
#define INCLUDED_C_POOL_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#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*/
|
||||
@@ -0,0 +1,87 @@
|
||||
#include "c_Pool.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
||||
@@ -1,3 +1,25 @@
|
||||
# cKit
|
||||
|
||||
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
|
||||
```
|
||||
@@ -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 */
|
||||
Reference in New Issue
Block a user