From 37fddc0bdd72d48e759e041b317bd15a7935b7ed Mon Sep 17 00:00:00 2001 From: Chen Peng Date: Sun, 30 Aug 2026 03:52:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=80=E4=BA=9B=E5=9F=BA=E7=A1=80=E7=BB=84?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Foundation/c_SmartPtr.h | 55 +- Foundation/c_SmartPtr.t.c | 129 +++-- Foundation/c_SmartPtrVector.c | 3 +- Foundation/c_SmartPtrVector.h | 2 +- Foundation/c_SmartPtrVector.t.c | 4 +- Foundation/c_StrIndexKmp.c | 88 +++ Foundation/c_StrIndexKmp.h | 44 ++ Foundation/c_StrIndexKmp.t.c | 75 +++ Foundation/c_StringBuffer.c | 974 ++++++++++++++++++++++++++++++++ Foundation/c_StringBuffer.h | 138 +++++ Foundation/c_StringBuffer.t.c | 156 +++++ 11 files changed, 1595 insertions(+), 73 deletions(-) create mode 100644 Foundation/c_StrIndexKmp.c create mode 100644 Foundation/c_StrIndexKmp.h create mode 100644 Foundation/c_StrIndexKmp.t.c create mode 100644 Foundation/c_StringBuffer.c create mode 100644 Foundation/c_StringBuffer.h create mode 100644 Foundation/c_StringBuffer.t.c diff --git a/Foundation/c_SmartPtr.h b/Foundation/c_SmartPtr.h index 8cf3827..0ad0be3 100644 --- a/Foundation/c_SmartPtr.h +++ b/Foundation/c_SmartPtr.h @@ -9,10 +9,9 @@ #include #endif /*INCLUDED_C_ATOMIC_H*/ -#ifndef INCLUDED_C_MEMORY_H -#include -#endif /*INCLUDED_C_MEMORY_H*/ - +#ifndef INCLUDED_C_ALLOCATOR_H +#include +#endif /*INCLUDED_C_ALLOCATOR_H*/ /* ------------------------------------------------------------------------------------------------------------------ */ /* */ @@ -27,34 +26,25 @@ typedef struct { c_SmartPtrFreeFn_t free_fn; void* args; c_atomic_int_t* ref_count; + c_Allocator_t allocator; } c_SmartPtr_t; /* ------------------------------------------------------------------------------------------------------------------ */ /* */ -C_STATIC_FORCE_INLINE -c_SmartPtr_t c_SmartPtr_Make(void* ptr, const c_SmartPtrFreeFn_t free_fn, void* args) { - c_SmartPtr_t sptr = { .ptr = ptr, .free_fn = free_fn, .args = args, .ref_count = NULL }; - - if (ptr == NULL) return sptr; - - C_NEW(sptr.ref_count); - if (sptr.ref_count != NULL) { - c_atomic_init_int(sptr.ref_count, 1); - } - return sptr; -} C_STATIC_FORCE_INLINE -c_err_t c_SmartPtr_Init(c_SmartPtr_t* self, void* ptr, const c_SmartPtrFreeFn_t free_fn, void* args) { - if (ptr == NULL) return C_ERR_PARAM; +c_err_t c_SmartPtr_Init(c_SmartPtr_t* self, void* ptr, const c_SmartPtrFreeFn_t free_fn, void* args, c_Allocator_t* allocator) { + if (!self || ptr == NULL) return C_ERR_PARAM; + // 內置自我管理宣告 + self->allocator = (allocator != NULL) ? *allocator : c_DefaultAllocator; self->ptr = ptr; self->free_fn = free_fn; self->args = args; - self->ref_count = NULL; - C_NEW(self->ref_count); + // 從自己內嵌的多態記憶體管理器中為共享原子計數核申請空間,完全避免全域大堆抖動 + self->ref_count = (c_atomic_int_t*)c_Allocator_Alloc(&self->allocator, sizeof(c_atomic_int_t)); if (self->ref_count == NULL) { return C_ERR_NOMEM; } @@ -63,32 +53,53 @@ c_err_t c_SmartPtr_Init(c_SmartPtr_t* self, void* ptr, const c_SmartPtrFreeFn_t return C_ERR_OK; } + +C_STATIC_FORCE_INLINE +c_SmartPtr_t c_SmartPtr_Make(void* ptr, const c_SmartPtrFreeFn_t free_fn, void* args, c_Allocator_t* allocator) { + c_SmartPtr_t sptr = { .ptr = ptr, .free_fn = free_fn, .args = args, .ref_count = NULL, .allocator = c_DefaultAllocator }; + if (ptr == NULL) return sptr; + c_SmartPtr_Init(&sptr, ptr, free_fn, args, allocator); + return sptr; +} + C_STATIC_FORCE_INLINE void c_SmartPtr_Destroy(c_SmartPtr_t* self) { if (!self || !self->ref_count) return; + // 原子 FETCH_SUB 遞減計數。若返回 1,證明當前線程手握全宇宙最後一個所有權控制權 if (C_ATOMIC_FETCH_SUB(self->ref_count, 1) == 1) { + // ① 驅動用戶自定義的析構回呼,物理渡越火化業務物件 if (self->free_fn && self->ptr) { self->free_fn(self->ptr, self->args); } - C_FREE(self->ref_count); + // ② 【核心進化】:精準呼叫自身攜帶的多態管理器,火化回收共享計數核空間 + c_Allocator_Free(&self->allocator, self->ref_count); } + // 乾淨原位抹零(包括內建的 allocator 同步抹除),防止 Use-After-Free 野指针高危 memset(self, 0, sizeof(c_SmartPtr_t)); } C_STATIC_FORCE_INLINE c_err_t c_SmartPtr_Copy(c_SmartPtr_t* dest, const c_SmartPtr_t* src) { if (!dest || !src || dest == src) return C_ERR_PARAM; - if (!src->ptr || !src->ref_count) return C_ERR_PARAM; + if (!src->ptr || !src->ref_count) { + c_SmartPtr_Destroy(dest); + return C_ERR_OK; + } + + // ① 先安全解除接收器舊有的生命線 c_SmartPtr_Destroy(dest); + // ② 結構體拷貝:新克隆體直接承襲源頭的所有控制域,並【深度克隆繼承】其內嵌的多態分配器 dest->ptr = src->ptr; dest->free_fn = src->free_fn; dest->args = src->args; dest->ref_count = src->ref_count; + dest->allocator = src->allocator; // 分配器血脈賡續 + // ③ 原子級加一 C_ATOMIC_FETCH_ADD(dest->ref_count, 1); return C_ERR_OK; } diff --git a/Foundation/c_SmartPtr.t.c b/Foundation/c_SmartPtr.t.c index af339b4..04e8b41 100644 --- a/Foundation/c_SmartPtr.t.c +++ b/Foundation/c_SmartPtr.t.c @@ -1,57 +1,92 @@ #include "c_SmartPtr.h" #include #include +#include "c_Test.h" -// 自訂釋放函數:負責關閉檔案 -void close_file_callback(void* ptr, void* args) { - FILE* fp = (FILE*)ptr; - char* filename = (char*)args; +typedef struct { + int conn_fd; +} NetSession_t; - if (fp) { - printf("[SmartPtr] 引用計數歸零,自動關閉檔案: %s\n", filename); - fclose(fp); +static int g_session_free_call_count = 0; +static size_t g_buddy_pool_active_chunks = 0; // 審計夥伴系統記憶體池的活躍塊總數 + +// 自定義析構回呼 +static void session_release_handler(void* ptr, void* args) { + if (ptr) { + free(ptr); // 釋放業務物件 + g_session_free_call_count++; } } -int main() { - printf("--- 1. 建立資源 (Open File) ---\n"); - char* my_file = "test.txt"; - FILE* fp = fopen(my_file, "w"); - if (!fp) return 1; - - // 寫入一些測試資料 - fprintf(fp, "Hello Smart Pointer in C!"); - fflush(fp); // 確保資料進硬碟,但先不關閉檔案 - - // 使用智慧指標接管檔案控制權 - c_SmartPtr_t sptr_a = c_SmartPtr_Make(fp, close_file_callback, my_file); - printf("sptr_a 建立完成,目前引用計數: %d\n", c_SmartPtr_UseCount(&sptr_a)); - - // 建立另外兩個空白的智慧指標容器 - c_SmartPtr_t sptr_b = {0}; - c_SmartPtr_t sptr_c = {0}; - - printf("\n--- 2. 測試 Copy 語義 (共享檔案控制權) ---\n"); - c_SmartPtr_Copy(&sptr_b, &sptr_a); - printf("A 的計數: %d, B 的計數: %d\n", c_SmartPtr_UseCount(&sptr_a), c_SmartPtr_UseCount(&sptr_b)); - - printf("\n--- 3. 測試 Move 語義 (B 所有權轉移給 C) ---\n"); - c_SmartPtr_Move(&sptr_c, &sptr_b); - printf("Move 後 -> B 計數: %d (已空), C 計數: %d\n", c_SmartPtr_UseCount(&sptr_b), c_SmartPtr_UseCount(&sptr_c)); - - printf("\n--- 4. 開始依序銷毀指標物件 ---\n"); - printf("銷毀 sptr_a...\n"); - c_SmartPtr_Destroy(&sptr_a); // 計數 2 -> 1 - printf("sptr_a 銷毀後,C 的計數: %d\n", c_SmartPtr_UseCount(&sptr_c)); - - printf("銷毀 sptr_b (本身已空,無影響)...\n"); - c_SmartPtr_Destroy(&sptr_b); - - printf("銷毀 sptr_c...\n"); - // 計數 1 -> 0,自動觸發 close_file_callback 關閉檔案! - c_SmartPtr_Destroy(&sptr_c); - - printf("\n程式結束,所有資源安全回收。\n"); - return 0; +// 模擬夥伴系統多態分配器物理開闢 +static void* mock_buddy_alloc(size_t size, void* ctx) { + (void)ctx; g_buddy_pool_active_chunks++; return malloc(size); +} +static void mock_buddy_free(void* ptr, void* ctx) { + (void)ctx; if (ptr) g_buddy_pool_active_chunks--; free(ptr); } +TEST_CASE(test_fully_closed_smart_pointer_polymorphic_sandbox) { + g_session_free_call_count = 0; + g_buddy_pool_active_chunks = 0; + + // 1. 初始化你的自建多態記憶體池分配器 (夥伴系統物理底座) + c_Allocator_t buddy_pool = { + .alloc = mock_buddy_alloc, + .realloc = NULL, + .free = mock_buddy_free, + .dtor = NULL, + .ud = NULL + }; + + // 2. 在堆上構建一個真實的連線工作階段 + NetSession_t* raw_session = (NetSession_t*)malloc(sizeof(NetSession_t)); + raw_session->conn_fd = 8080; + + // 3. 宣告主智慧指標誕生,並掛載夥伴系統記憶體池 + c_SmartPtr_t sptr_master = c_SmartPtr_Make(raw_session, session_release_handler, NULL, &buddy_pool); + + // 斷言審判:此時夥伴系統內部的活躍物理塊計數精準等於 1 (即內嵌 allocator 幫我們開闢的 ref_count) + ASSERT_INT_EQ(1, g_buddy_pool_active_chunks); + ASSERT_INT_EQ(1, c_SmartPtr_UseCount(&sptr_master)); + + // 4. 驗證共享深度拷貝與分配器血脈克隆繼承 (Copy) + c_SmartPtr_t sptr_clone = {0}; + ASSERT_INT_EQ(C_ERR_OK, c_SmartPtr_Copy(&sptr_clone, &sptr_master)); + + // 兩者共享原子核,計數精準遞增為 2 + ASSERT_INT_EQ(2, c_SmartPtr_UseCount(&sptr_master)); + ASSERT_INT_EQ(2, c_SmartPtr_UseCount(&sptr_clone)); + + // 5. 【終極高能觀察點】:單體自主銷毀(Destroy) + // 外部直接呼叫極簡的 c_SmartPtr_Destroy,不需要傳入任何外部分配器! + c_SmartPtr_Destroy(&sptr_clone); // 克隆體釋放,計數精準回落至 1 + + // 帳目審查:克隆體被銷毀,但主體還活著,實體對象絕不能提前消亡,且夥伴系統計數依然完整保持為 1 + ASSERT_INT_EQ(1, c_SmartPtr_UseCount(&sptr_master)); + ASSERT_INT_EQ(0, g_session_free_call_count); + ASSERT_INT_EQ(1, g_buddy_pool_active_chunks); + + // 6. 終致命一擊:銷毀最後持有人 sptr_master + // 計數歸零,自主觸發託管的 session_release_handler 物理火化,並【自主】利用內嵌的 allocator 退還記憶體 + c_SmartPtr_Destroy(&sptr_master); + + // 7. 終極一致性雙向審判: + // A. 實體資源層:業務物件被精準自動火化解體,物理消亡數精準等於 1 + ASSERT_INT_EQ(1, g_session_free_call_count); + + // B. 多態記憶體池層:分散隨機開闢的原子計數核空間完全由智慧指標內部自主歸還! + // 夥伴系統記憶體池活躍塊計數 g_buddy_pool_active_chunks 完美且毫無滯留地歸零(0 洩漏,0 跑飛,100% 圓滿!) + ASSERT_INT_EQ_MSG(0, g_buddy_pool_active_chunks, "CRITICAL: Self-contained Smart Pointer leaked ref_count space inside buddy pool!"); +} + +int main(void) { + printf("\n"); + TEST_START(C_Ultimate_FullyClosed_SmartPtr_Tests); + RUN_TEST(test_fully_closed_smart_pointer_polymorphic_sandbox); + TEST_REPORT(); + RETURN_TEST_STATUS; +} + + + diff --git a/Foundation/c_SmartPtrVector.c b/Foundation/c_SmartPtrVector.c index 42141b3..ad14acf 100644 --- a/Foundation/c_SmartPtrVector.c +++ b/Foundation/c_SmartPtrVector.c @@ -47,7 +47,8 @@ void c_SmartPtrVector_Destroy(c_SmartPtrVector_t* vector) { * @brief 內部私有自動動態翻倍擴容函數 * @note 強異常安全性:採用移動語義平移控制權,即使分配失敗,老資料依舊原裝存活 */ -static bool c_SmartPtrVector_EnsureCapacity(c_SmartPtrVector_t* vector) { +C_STATIC_FORCE_INLINE +bool c_SmartPtrVector_EnsureCapacity(c_SmartPtrVector_t* vector) { if (vector->size < vector->capacity) return true; c_size_t new_capacity = vector->capacity * 2; diff --git a/Foundation/c_SmartPtrVector.h b/Foundation/c_SmartPtrVector.h index eb6f064..12df248 100644 --- a/Foundation/c_SmartPtrVector.h +++ b/Foundation/c_SmartPtrVector.h @@ -20,7 +20,7 @@ typedef struct { c_Allocator_t allocator; }c_SmartPtrVector_t; -#define C_SMART_PTR_VECTOR_INITIALIZER {NULL, 0, 0} +#define C_SMART_PTR_VECTOR_INITIALIZER {NULL, 0, 0, c_DefaultAllocator} /* ------------------------------------------------------------------------------------------------------------------ */ /* */ diff --git a/Foundation/c_SmartPtrVector.t.c b/Foundation/c_SmartPtrVector.t.c index 171d245..157035f 100644 --- a/Foundation/c_SmartPtrVector.t.c +++ b/Foundation/c_SmartPtrVector.t.c @@ -56,7 +56,7 @@ TEST_CASE(test_real_smart_ptr_vector_atomic_closure) { int free_args = 2026; // 自定義銷毀引數 // 使用你編寫的 c_SmartPtr_Make / Init 進行生命周期宣告 - c_SmartPtr_t master_sptr = c_SmartPtr_Make(raw_obj, test_resource_free, &free_args); + c_SmartPtr_t master_sptr = c_SmartPtr_Make(raw_obj, test_resource_free, &free_args, 0); // 剛出生,使用計數必須精準等於 1 ASSERT_INT_EQ(1, c_SmartPtr_UseCount(&master_sptr)); @@ -70,7 +70,7 @@ TEST_CASE(test_real_smart_ptr_vector_atomic_closure) { // 4. 建立第二具獨立智慧指標,迫使 Vector 翻倍自動擴容(2 -> 4) TestObject_t* raw_obj2 = (TestObject_t*)malloc(sizeof(TestObject_t)); raw_obj2->resource_id = 1122; - c_SmartPtr_t master_sptr2 = c_SmartPtr_Make(raw_obj2, test_resource_free, &free_args); + c_SmartPtr_t master_sptr2 = c_SmartPtr_Make(raw_obj2, test_resource_free, &free_args, 0); c_SmartPtrVector_PushBack(&vector, &master_sptr2); // 槽位 1 diff --git a/Foundation/c_StrIndexKmp.c b/Foundation/c_StrIndexKmp.c new file mode 100644 index 0000000..a4bb294 --- /dev/null +++ b/Foundation/c_StrIndexKmp.c @@ -0,0 +1,88 @@ +#include +#include + +/** + * @brief 私有辅助内部函数:为模式串动态解包并构建 KMP 高速失配跳转跳转表 + */ +C_STATIC_FORCE_INLINE +c_err_t c_Kmp_BuildNextTable(const uint8_t* p, c_size_t m, c_size_t* next) { + next[0] = 0; // KMP 起始基石 + c_size_t len = 0; // 前缀的核心长度 + c_size_t i = 1; + + while (i < m) { + if (p[i] == p[len]) { + len++; + next[i] = len; + i++; + } else { + if (len != 0) { + len = next[len - 1]; // 顺着失配位置向左回溯 + } else { + next[i] = 0; + i++; + } + } + } + return C_ERR_OK; +} + +c_size_t c_StrIndexKmpEx(const void* text, c_size_t text_len, + const void* pattern, c_size_t pattern_len, + c_size_t start_offset, c_Allocator_t* allocator) { + // 1. 强御级无效边界防御 + if (!text || !pattern) return C_KMP_NOT_FOUND; + + const uint8_t* t = (const uint8_t*)text; + const uint8_t* p = (const uint8_t*)pattern; + + // 如果传入 0 则自适应兼容裸字符串 + c_size_t n = (text_len == 0) ? (c_size_t)strlen((const char*)text) : text_len; + c_size_t m = (pattern_len == 0) ? (c_size_t)strlen((const char*)pattern) : pattern_len; + + if (m == 0 || n == 0 || start_offset >= n || m > (n - start_offset)) { + return C_KMP_NOT_FOUND; + } + + c_Allocator_t* alloc = (allocator != NULL) ? allocator : &c_DefaultAllocator; + + // 2. 【安全机制】:坚决不使用栈上的局部可变长数组(VLA),防止被大特征串顶爆引发 Stack Overflow + // 从内部绑定的专属多态分配器中动态申领物理块资源 + c_size_t* next = (c_size_t*)c_Allocator_Alloc(alloc, m * sizeof(c_size_t)); + if (!next) return C_KMP_NOT_FOUND; + + // 3. 构建 KMP 高速失配加速表 + c_Kmp_BuildNextTable(p, m, next); + + // 4. 开始执行双指针拉平线性单向匹配检索流 (无回溯 O(M+N)) + c_size_t i = start_offset; // 文本大字符串的推进指针 + c_size_t j = 0; // 模式串的局部指针 + + while (i < n) { + if (t[i] == p[j]) { + i++; + j++; + } + + if (j == m) { + // 完美踩中特征指纹,捕获命中! + c_size_t found_idx = i - j; + + // 强异常安全性物理火化:归还申领的跳转数组块,杜绝堆碎片跑飞 + c_Allocator_Free(alloc, next); + return found_idx; // 返回命中位置相对于首地址的绝对物理偏移量 + } + else if (i < n && t[i] != p[j]) { + // 发生失配抖动,KMP 的精髓:文本指针 i 绝对不后退,只原位等待,而模式串指针 j 顺着失配表向左高速跳转 + if (j != 0) { + j = next[j - 1]; + } else { + i++; + } + } + } + + // 全文扫描结束,未发生特征匹配,体面卸载资源退出 + c_Allocator_Free(alloc, next); + return C_KMP_NOT_FOUND; +} diff --git a/Foundation/c_StrIndexKmp.h b/Foundation/c_StrIndexKmp.h new file mode 100644 index 0000000..ec78a6e --- /dev/null +++ b/Foundation/c_StrIndexKmp.h @@ -0,0 +1,44 @@ +#ifndef INCLUDED_C_STRINDEXKMP_H +#define INCLUDED_C_STRINDEXKMP_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_ALLOCATOR_H +#include +#endif /*INCLUDED_C_ALLOCATOR_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define C_KMP_NOT_FOUND ((c_size_t)-1) + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * @brief 工业级 KMP 泛型字节/文本匹配检索器 (Knuth-Morris-Pratt Pattern Matcher) + * @param text 源文本大连续缓冲区指针 + * @param text_len 源文本缓冲区的实际物理字节长(传入 0 则内部自动按 strlen 计算) + * @param pattern 待查找的目标特征模式串指针 + * @param pattern_len 模式串的实际物理字节长(传入 0 则内部自动按 strlen 计算) + * @param start_offset 允许指定的接力检索起始虚拟偏移位置(0 代表从头开始检索) + * @param allocator 自定义多态分配器引用,若传入 NULL 则无缝降级为全局默认 c_DefaultAllocator + * @return c_size_t 匹配成功的首个相对物理偏移量,若检索失败则严格返回 C_KMP_NOT_FOUND + */ +c_size_t c_StrIndexKmpEx(const void* text, c_size_t text_len, + const void* pattern, c_size_t pattern_len, + c_size_t start_offset, c_Allocator_t* allocator); + + + +// 你给出的原厂极简标准库兼容版桥接接口 (支持直接对齐普通裸 C 字符串字符串) +C_STATIC_FORCE_INLINE +c_size_t c_StrIndexKmp(const char* text, const char* pattern) { + if (!text || !pattern) return C_KMP_NOT_FOUND; + // 自动桥接至全装全配置的多态安全内核中,降级为默认分配器 + return c_StrIndexKmpEx(text, 0, pattern, 0, 0, NULL); +} + +#endif /*INCLUDED_C_STRINDEXKMP_H*/ diff --git a/Foundation/c_StrIndexKmp.t.c b/Foundation/c_StrIndexKmp.t.c new file mode 100644 index 0000000..1bf9d6f --- /dev/null +++ b/Foundation/c_StrIndexKmp.t.c @@ -0,0 +1,75 @@ +#include "c_StrIndexKmp.h" +#include +#include +#include +#include "c_Test.h" + +static size_t g_kmp_alloc_tracker = 0; +static void* mock_kmp_alloc(size_t size, void* ctx) { + (void)ctx; g_kmp_alloc_tracker++; return malloc(size); +} +static void mock_kmp_free(void* ptr, void* ctx) { + (void)ctx; if (ptr) g_kmp_alloc_tracker--; free(ptr); +} + +TEST_CASE(test_kmp_string_pattern_matcher_closure) { + g_kmp_alloc_tracker = 0; + + c_Allocator_t kmp_pool = { + .alloc = mock_kmp_alloc, + .realloc = NULL, + .free = mock_kmp_free, + .dtor = NULL, + .ud = NULL + }; + + // ------------------------------------------------------------------------- + // 1. 验证常规的 C 风格字符串字符串匹配 + // ------------------------------------------------------------------------- + const char* txt1 = "ABABDABACDABABCABAB"; + const char* pat1 = "ABABCABAB"; + + // 调用内联包装 API + c_size_t idx1 = c_StrIndexKmp(txt1, pat1); + ASSERT_INT_EQ(10, (int)idx1); // 特征串应该在索引 10 的坑位被精准揪出来 + + // ------------------------------------------------------------------------- + // 2. 验证多态分配器自治与非阻塞流式“接力二次检索”能力 + // ------------------------------------------------------------------------- + // 模拟一段由于网络或串口 DMA 传输导致的高密度回绕大字节流 + // 里面包含两个相同的特定标志特征码 token "\r\n#OK" + const uint8_t binary_stream[] = {0x00, 0x11, '\r', '\n', '#', 'O', 'K', 0xFF, 0xAA, '\r', '\n', '#', 'O', 'K', 0x00}; + const uint8_t token_pattern[] = {'\r', '\n', '#', 'O', 'K'}; + + // 第一次检索:从 0 开始 + c_size_t match_1 = c_StrIndexKmpEx(binary_stream, sizeof(binary_stream), + token_pattern, sizeof(token_pattern), + 0, &kmp_pool); + ASSERT_INT_EQ(2, (int)match_1); // 成功在偏移 2 处捕获到第一个标志 + ASSERT_INT_EQ(0, g_kmp_alloc_tracker); // 证明内部的 Next 数组是完全在自建多态池中伸降分配的 + + // 第二次接力检索:利用第一次命中的位置后移一位(match_1 + 1)作为新起点,向后跨越式探测 + c_size_t match_2 = c_StrIndexKmpEx(binary_stream, sizeof(binary_stream), + token_pattern, sizeof(token_pattern), + match_1 + 1, &kmp_pool); + ASSERT_INT_EQ(9, (int)match_2); // 完美接力!在偏移 9 处成功挖出了第二个标志码 + + // ------------------------------------------------------------------------- + // 3. 极限下溢防御与销毁平衡断言 + // ------------------------------------------------------------------------- + // 检索完全不存在的污染码 + c_size_t match_fake = c_StrIndexKmpEx(binary_stream, sizeof(binary_stream), + "FAKE_TOKEN", 10, 0, &kmp_pool); + ASSERT_TRUE(match_fake == C_KMP_NOT_FOUND); + + // 终极资源账目判决:整个 KMP 解析链条结束后,内部申领的临时 Next 数组必须全部闭环 Free 清算归零! + ASSERT_INT_EQ_MSG(0, g_kmp_alloc_tracker, "CRITICAL: KMP internal Next-table leaked memory inside the allocator pool!"); +} + +int main(void) { + printf("\n"); + TEST_START(C_StringKmp_Advanced_Matcher_Tests); + RUN_TEST(test_kmp_string_pattern_matcher_closure); + TEST_REPORT(); + RETURN_TEST_STATUS; +} diff --git a/Foundation/c_StringBuffer.c b/Foundation/c_StringBuffer.c new file mode 100644 index 0000000..be8309b --- /dev/null +++ b/Foundation/c_StringBuffer.c @@ -0,0 +1,974 @@ +#include +#include +#include +#include + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define DEFAULT_INIT_CAPACITY 16 +#define GROWTH_FACTOR 2 + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +C_STATIC_FORCE_INLINE +c_err_t c_StringBuffer_EnsureCapacity(c_StringBuffer_t* self, c_size_t required_free_space) { + c_size_t current_free = self->capacity - self->size - 1; // 抛去隐式预留的 1 字节 \0 位 + if (current_free >= required_free_space) return C_ERR_OK; + + c_size_t new_capacity = self->capacity * GROWTH_FACTOR; + // 阶跃扩容保护,直到能完全装下需求空间 + while ((new_capacity - self->size - 1) < required_free_space) { + new_capacity *= GROWTH_FACTOR; + } + + return c_StringBuffer_Resize(self, new_capacity); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +c_err_t c_StringBuffer_Init(c_StringBuffer_t* self, c_size_t capacity, c_Allocator_t* allocator){ + if (!self) return C_ERR_PARAM; + + self->allocator = (allocator != NULL) ? *allocator : c_DefaultAllocator; + self->size = 0; + self->capacity = (capacity > 0) ? (capacity + 1) : DEFAULT_INIT_CAPACITY; + + // 内部多态自治申领空间 + self->buffer = (char*)c_Allocator_Alloc(&self->allocator, self->capacity); + if (!self->buffer) { + self->capacity = 0; + return C_ERR_NOMEM; + } + + self->buffer[0] = '\0'; // 初始置为空串 + return C_ERR_OK; +} + +void c_StringBuffer_Destroy(c_StringBuffer_t* self) { + if (!self) return; + if (self->buffer) { + c_Allocator_Free(&self->allocator, self->buffer); + self->buffer = NULL; + } + self->size = 0; + self->capacity = 0; +} + +c_err_t c_StringBuffer_Resize(c_StringBuffer_t* self, c_size_t new_capacity) { + if (!self || new_capacity == 0) return C_ERR_PARAM; + if (new_capacity == self->capacity) return C_ERR_OK; + + // 防止 new_capacity 算入 \0 的扩容本身越界 + if (new_capacity > (c_size_t)-1) return C_ERR_OUTOFBOUND; + + c_size_t old_bytes = self->capacity; + c_size_t new_bytes = new_capacity; + + // 对接你最新的 Realloc 包装,闭环支持伙伴系统在同一阶数(Order)内的 O(1) 原地续约 + void* new_buffer = c_Allocator_Realloc(&self->allocator, self->buffer, old_bytes, new_bytes); + if (!new_buffer) return C_ERR_NOMEM; // 扩容失败,老数据和老空间依然安全原装存活 + + self->buffer = (char*)new_buffer; + self->capacity = new_capacity; + + // 裁切防护:如果显式调小了容量,且当前已有的数据长度超过了新物理上限,强制进行数据阶段和 \0 封底 + if (self->size >= self->capacity) { + self->size = self->capacity - 1; + self->buffer[self->size] = '\0'; + } + + return C_ERR_OK; +} + + +c_err_t c_StringBuffer_Append(c_StringBuffer_t* self, const char* string, c_size_t length) { + if (!self || !self->buffer || !string || length == 0) return C_ERR_PARAM; + + c_err_t err = c_StringBuffer_EnsureCapacity(self, length); + if (err != C_ERR_OK) return err; + + memcpy(self->buffer + self->size, string, length); + self->size += length; + self->buffer[self->size] = '\0'; + + return C_ERR_OK; +} + +c_err_t c_StringBuffer_Prepend(c_StringBuffer_t* self, const char* string, c_size_t length) { + return c_StringBuffer_InsertAt(self, 0, string, length); +} + +c_err_t c_StringBuffer_InsertAt(c_StringBuffer_t* self, c_size_t index, const char* string, c_size_t length) { + if (!self || !self->buffer || !string || length == 0) return C_ERR_PARAM; + if (index > self->size) return C_ERR_OUTOFBOUND; + + c_err_t err = c_StringBuffer_EnsureCapacity(self, length); + if (err != C_ERR_OK) return err; + + // Shift memory to the right using memmove to prevent overlapping issues + memmove(self->buffer + index + length, self->buffer + index, self->size - index); + memcpy(self->buffer + index, string, length); + + self->size += length; + self->buffer[self->size] = '\0'; + + return C_ERR_OK; +} + +c_err_t c_StringBuffer_RemoveAt(c_StringBuffer_t* self, c_size_t index, c_size_t length) { + if (!self || !self->buffer) return C_ERR_PARAM; + if (index >= self->size) return C_ERR_OUTOFBOUND; + if (length ==0) return C_ERR_OK; + + // Clamp length if it attempts to read past the end of the current buffer + if (index + length > self->size) { + length = self->size - index; + } + + // Shift trailing memory to the left to close the character gap + memmove(self->buffer + index, self->buffer + index + length, self->size - (index + length)); + self->size -= length; + self->buffer[self->size] = '\0'; + + return C_ERR_OK; +} + +void c_StringBuffer_Clear(c_StringBuffer_t* self) { + if (!self || !self->buffer) return; + self->size = 0; + self->buffer[0] = '\0'; +} + +/* --- Explicit String-Wrapper Interfaces --- */ + +c_err_t c_StringBuffer_AppendStr(c_StringBuffer_t* self, const char* string) { + if (!string) return C_ERR_PARAM; + return c_StringBuffer_Append(self, string, strlen(string)); +} + +c_err_t c_StringBuffer_PrependStr(c_StringBuffer_t* self, const char* string) { + if (!string) return C_ERR_PARAM; + return c_StringBuffer_Prepend(self, string, strlen(string)); +} + +c_err_t c_StringBuffer_InsertStrAt(c_StringBuffer_t* self, const char* string, c_size_t index) { + if (!string) return C_ERR_PARAM; + return c_StringBuffer_InsertAt(self, index, string, strlen(string)); +} + +c_err_t c_StringBuffer_CopyTo(c_StringBuffer_t* self, c_size_t index, c_size_t length, char* buffer, c_size_t buffer_length) { + // 1. Guard against invalid pointers, empty destinations, or index out-of-bounds + if (!self || !self->buffer || !buffer || buffer_length == 0) { + return C_ERR_PARAM; + } + if (index > self->size) { + return C_ERR_OUTOFBOUND; + } + + // 2. Clamp requested copy length if it exceeds the remaining data payload bounds + if (index + length > self->size) { + length = self->size - index; + } + + // 3. Enforce destination buffer capacity threshold checks + // The requested segment requires at least (length + 1) bytes for safe null-termination + if (length >= buffer_length) { + return C_ERR_OUTOFBOUND; // Destination buffer is too small to store the segment safely + } + + // 4. Perform the raw memory copy if there are valid characters to process + if (length > 0) { + memcpy(buffer, self->buffer + index, length); + } + + // 5. Always apply a deterministic trailing null terminator + buffer[length] = '\0'; + + return C_ERR_OK; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +c_err_t c_StringBuffer_VPrintf(c_StringBuffer_t* self, const char* format, va_list args) { + if (!self || !format) return C_ERR_PARAM; + + // Make a copy of args to measure the required layout length safely + va_list args_copy; + va_copy(args_copy, args); + int formatted_len = vsnprintf(NULL, 0, format, args_copy); + va_end(args_copy); + + if (formatted_len < 0) return C_ERR_PARAM; + if (formatted_len == 0) return C_ERR_OK; + + c_size_t length = (c_size_t)formatted_len; + + c_err_t err = c_StringBuffer_EnsureCapacity(self, length); + if (err != C_ERR_OK) return err; + + // Use the original args list for writing directly into the structure block + vsnprintf(self->buffer + self->size, length + 1, format, args); + + self->size += length; + self->buffer[self->size] = '\0'; + + return C_ERR_OK; +} + +c_err_t c_StringBuffer_VPrintfAt(c_StringBuffer_t* self, c_size_t index, const char* format, va_list args) { + if (!self || !format) return C_ERR_PARAM; + if (index > self->size) return C_ERR_OUTOFBOUND; + + // Measure the length of the new formatted slice + va_list args_copy; + va_copy(args_copy, args); + int formatted_len = vsnprintf(NULL, 0, format, args_copy); + va_end(args_copy); + + if (formatted_len < 0) return C_ERR_PARAM; + if (formatted_len == 0) return C_ERR_OK; + + c_size_t length = (c_size_t)formatted_len; + + c_err_t err = c_StringBuffer_EnsureCapacity(self, length); + if (err != C_ERR_OK) return err; + + // Safely backup the target downstream character that will be stomped by vsnprintf's '\0' + char backup_char = '\0'; + if (index < self->size) { + backup_char = self->buffer[index]; + } + + // Shift the existing string buffer memory forward + memmove(self->buffer + index + length, self->buffer + index, self->size - index); + + // Render formatted string fragments safely into the newly allocated block gap + vsnprintf(self->buffer + index, length + 1, format, args); + + // Overwrite the accidental inner null-terminator using our clean structural backup + if (index < self->size) { + self->buffer[index + length] = backup_char; + } + + self->size += length; + self->buffer[self->size] = '\0'; + + return C_ERR_OK; +} + +c_err_t c_StringBuffer_Printf(c_StringBuffer_t* self, const char* format, ...) { + va_list args; + va_start(args, format); + c_err_t err = c_StringBuffer_VPrintf(self, format, args); + va_end(args); + return err; +} + +c_err_t c_StringBuffer_PrintfAt(c_StringBuffer_t* self, c_size_t index, const char* format, ...) { + va_list args; + va_start(args, format); + c_err_t err = c_StringBuffer_VPrintfAt(self, index, format, args); + va_end(args); + return err; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#include +#include + +c_err_t c_StringBuffer_AppendTimestamp(c_StringBuffer_t* self, const char* format, const struct tm* time_info) { + if (!self || !format || !time_info) return C_ERR_PARAM; + + // Start with a reasonable initial guess for max timestamp length. + // Most standard timestamps (%Y-%m-%d %H:%M:%S) fit in under 32 or 64 bytes. + c_size_t guess_space = 64; + c_err_t err; + + while (1) { + err = c_StringBuffer_EnsureCapacity(self, guess_space); + if (err != C_ERR_OK) return err; + + // strftime writes into the remaining available capacity space. + // self->capacity - self->size calculation leaves room for the null-terminator. + c_size_t max_write = self->capacity - self->size; + size_t written = strftime(self->buffer + self->size, max_write, format, time_info); + + // strftime returns 0 if the string didn't fit into the provided buffer size + if (written == 0) { + // Check if the pattern genuinely produces a 0-length output (like an empty format string "") + if (format[0] == '\0') { + return C_ERR_OK; + } + // Double the guess size space and try again + guess_space *= 2; + + // Put an upper bound sanity check to prevent infinite loops on broken formatting parameters + if (guess_space > 4096) { + return C_ERR_PARAM; + } + continue; + } + + // Success! Advance size tracking variable + self->size += (c_size_t)written; + // strftime automatically guarantees a null terminator at self->buffer[self->size] + break; + } + + return C_ERR_OK; +} + +c_err_t c_StringBuffer_AppendCurrentTimestamp(c_StringBuffer_t* self, const char* format, int use_utc) { + if (!self || !format) return C_ERR_PARAM; + + time_t raw_time = time(NULL); + if (raw_time == (time_t)-1) { + return C_ERR_PARAM; // Failed to retrieve system clock time + } + + struct tm time_struct; + struct tm* time_ptr; + + // Thread-safe structure assembly variants (fallback to standard if platform requires it) + if (use_utc) { +#if defined(_WIN32) || defined(_WIN64) + if (gmtime_s(&time_struct, &raw_time) != 0) return C_ERR_PARAM; + time_ptr = &time_struct; +#else + time_ptr = gmtime_r(&raw_time, &time_struct); +#endif + } else { +#if defined(_WIN32) || defined(_WIN64) + if (localtime_s(&time_struct, &raw_time) != 0) return C_ERR_PARAM; + time_ptr = &time_struct; +#else + time_ptr = localtime_r(&raw_time, &time_struct); +#endif + } + + if (!time_ptr) return C_ERR_PARAM; + + return c_StringBuffer_AppendTimestamp(self, format, time_ptr); +} + + +c_err_t c_StringBuffer_InsertTimestampAt(c_StringBuffer_t* self, c_size_t index, const char* format, const struct tm* time_info) { + if (!self || !format || !time_info) return C_ERR_PARAM; + if (index > self->size) return C_ERR_OUTOFBOUND; + + // Use a conservative local stack frame memory allocation. + // Standard timestamp strings comfortably fit within 128 bytes. + char temp_stack_buffer[128]; + char* target_buffer = temp_stack_buffer; + c_size_t allocated_size = sizeof(temp_stack_buffer); + c_size_t final_len = 0; + c_err_t result = C_ERR_OK; + + while (1) { + size_t written = strftime(target_buffer, allocated_size, format, time_info); + + if (written == 0) { + // Check if the format string pattern is intentionally empty "" + if (format[0] == '\0') { + final_len = 0; + break; + } + + // If the timestamp string didn't fit, scale up the workspace dynamically on the heap + c_size_t new_allocated_size = allocated_size * 2; + + // Loop sanity guard limit to prevent infinite allocations on bad layout configurations + if (new_allocated_size > 4096) { + if (target_buffer != temp_stack_buffer) { + c_Allocator_Free(&self->allocator, target_buffer); + } + return C_ERR_PARAM; + } + + char* new_buffer = (target_buffer == temp_stack_buffer) + ? (char*)c_Allocator_Alloc(&self->allocator, new_allocated_size) + : (char*)c_Allocator_Realloc(&self->allocator, target_buffer, allocated_size, new_allocated_size); + + if (!new_buffer) { + if (target_buffer != temp_stack_buffer) { + c_Allocator_Free(&self->allocator, target_buffer); + } + return C_ERR_NOMEM; + } + + // Copy data over if migrating from stack array block allocation initially + if (target_buffer == temp_stack_buffer) { + // No need to copy old data because strftime failed completely anyway + } + + target_buffer = new_buffer; + allocated_size = new_allocated_size; + continue; + } + + final_len = (c_size_t)written; + break; + } + + // Call your existing InsertAt implementation to open the gap and safely shift the array characters downstream + if (final_len > 0) { + result = c_StringBuffer_InsertAt(self, index, target_buffer, final_len); + } + + // Clean up heap space allocations if we outgrew the default 128-byte stack array footprint + if (target_buffer != temp_stack_buffer) { + c_Allocator_Free(&self->allocator, target_buffer); + } + + return result; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_index_t c_StringBuffer_IndexOfStr(c_StringBuffer_t* self, c_size_t start_index, const char* substr) { + if (!self || !self->buffer || !substr) return C_ERR_NOTFOUND; + if (start_index >= self->size) return C_ERR_NOTFOUND; + + // Utilize optimized standard strstr starting from our targeted index offset + char* match = strstr(self->buffer + start_index, substr); + if (!match) return C_ERR_NOTFOUND; + + return (c_index_t)(match - self->buffer); +} + +c_index_t c_StringBuffer_IndexOfChar(c_StringBuffer_t* self, c_size_t start_index, char target) { + if (!self || !self->buffer) return C_ERR_NOTFOUND; + if (start_index >= self->size) return C_ERR_NOTFOUND; + + // memchr is highly optimized by compilers using SIMD assembly operations under the hood + c_size_t search_len = self->size - start_index; + char* match = (char*)memchr(self->buffer + start_index, target, search_len); + if (!match) return C_ERR_NOTFOUND; + + return (c_index_t)(match - self->buffer); +} + +c_index_t c_StringBuffer_LastIndexOfStr(c_StringBuffer_t* self, c_size_t start_index, const char* substr) { + if (!self || !self->buffer || !substr) return C_ERR_NOTFOUND; + + c_size_t sub_len = strlen(substr); + if (sub_len == 0) return C_ERR_NOTFOUND; + + // Clamp start_index to structural string boundary maximums + c_size_t upper_bound = (start_index >= self->size) ? (self->size == 0 ? 0 : self->size - 1) : start_index; + if (upper_bound < sub_len - 1) return C_ERR_NOTFOUND; + + // Scan backwards sequentially to find the last occurrence match context + for (c_size_t i = upper_bound + 1 - sub_len; ; i--) { + if (strncmp(self->buffer + i, substr, sub_len) == 0) { + return (c_index_t)i; + } + if (i == 0) break; // Terminate condition for unsigned down-counting loops + } + + return C_ERR_NOTFOUND; +} + +c_index_t c_StringBuffer_LastIndexOfChar(c_StringBuffer_t* self, c_size_t start_index, char target) { + if (!self || !self->buffer || self->size == 0) return C_ERR_NOTFOUND; + + c_size_t upper_bound = (start_index >= self->size) ? (self->size - 1) : start_index; + + // Backwards structural loop checking character identities cleanly + for (c_size_t i = upper_bound; ; i--) { + if (self->buffer[i] == target) { + return (c_index_t)i; + } + if (i == 0) break; + } + + return C_ERR_NOTFOUND; +} + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +c_err_t c_StringBuffer_ReplaceStr(c_StringBuffer_t* self, const char* old_str, const char* new_str) { + if (!self || !old_str || !new_str) return C_ERR_PARAM; + + c_size_t old_len = strlen(old_str); + if (old_len == 0) return C_ERR_OK; // Replacing an empty string is a no-op + + c_size_t new_len = strlen(new_str); + + // Pass 1: Count total occurrences to evaluate memory requirements safely + c_size_t occurrences = 0; + const char* scan = self->buffer; + if (scan) { + while ((scan = strstr(scan, old_str)) != NULL) { + occurrences++; + scan += old_len; + } + } + + if (occurrences == 0) return C_ERR_OK; // No matches found + + // Calculate structural payload delta modifications + long long delta = (long long)new_len - (long long)old_len; + c_size_t final_size = self->size + (occurrences * delta); + + // Expand buffer layout upfront if the replacement string expands the footprint + if (delta > 0) { + c_err_t err = c_StringBuffer_EnsureCapacity(self, occurrences * delta); + if (err != C_ERR_OK) return err; + } + + // Pass 2: Apply the substitution matrix via pointer offsets + char* read_ptr = self->buffer; + char* write_ptr = self->buffer; + + // If the string expands, we must write from right-to-left to prevent stomping data. + // However, an easy and clean way to handle all deltas without complex memory logic + // is utilizing a temporary buffer, or shifting segments sequentially. + // Let's implement an in-place single-buffer scan-and-shift variant: + c_size_t current_index = 0; + while (current_index < self->size) { + char* match = strstr(self->buffer + current_index, old_str); + if (!match) break; + + c_index_t match_idx = (c_index_t)(match - self->buffer); + + if (delta != 0) { + // Shift the trailing data behind the old string block configuration + c_size_t tail_len = self->size - (match_idx + old_len); + memmove(self->buffer + match_idx + new_len, self->buffer + match_idx + old_len, tail_len); + } + + // Copy the replacement string elements into the target slot + if (new_len > 0) { + memcpy(self->buffer + match_idx, new_str, new_len); + } + + // Adjust tracking dimensions + self->size += delta; + current_index = match_idx + new_len; + } + + self->buffer[self->size] = '\0'; // Strictly enforce final null-termination + return C_ERR_OK; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#include +#include + +c_err_t c_StringBuffer_TrimLeft(c_StringBuffer_t* self) { + if (!self) return C_ERR_PARAM; + if (self->size == 0) return C_ERR_OK; + + c_size_t spaces = 0; + + // Scan forward to count leading whitespace characters + // isspace covers: ' ', '\t', '\n', '\v', '\f', '\r' + while (spaces < self->size && isspace((unsigned char)self->buffer[spaces])) { + spaces++; + } + + if (spaces == 0) return C_ERR_OK; // No leading whitespace found + + // Shift the remaining structural payload left to overwrite the whitespace + c_size_t remaining_bytes = self->size - spaces; + if (remaining_bytes > 0) { + memmove(self->buffer, self->buffer + spaces, remaining_bytes); + } + + self->size = remaining_bytes; + self->buffer[self->size] = '\0'; // Strictly enforce structural null-termination + + return C_ERR_OK; +} + +c_err_t c_StringBuffer_TrimRight(c_StringBuffer_t* self) { + if (!self) return C_ERR_PARAM; + if (self->size == 0) return C_ERR_OK; + + // Scan backwards from the tail using unsigned down-counting loop guard rails + c_size_t i = self->size; + while (i > 0 && isspace((unsigned char)self->buffer[i - 1])) { + i--; + } + + // Adjust structural sizes down directly without moving memory arrays + self->size = i; + if (self->buffer && self->capacity > 0) { + self->buffer[self->size] = '\0'; + } + + return C_ERR_OK; +} + +c_err_t c_StringBuffer_Trim(c_StringBuffer_t* self) { + if (!self) return C_ERR_PARAM; + + // Performance optimization: Clean up tail bytes first to minimize memory movement blocks + c_err_t err = c_StringBuffer_TrimRight(self); + if (err != C_ERR_OK) return err; + + return c_StringBuffer_TrimLeft(self); +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +c_err_t c_StringBuffer_ToLower(c_StringBuffer_t* self) { + if (!self || !self->buffer) return C_ERR_PARAM; + + for (c_size_t i = 0; i < self->size; i++) { + self->buffer[i] = (char)tolower((unsigned char)self->buffer[i]); + } + return C_ERR_OK; +} + +c_err_t c_StringBuffer_ToUpper(c_StringBuffer_t* self) { + if (!self || !self->buffer) return C_ERR_PARAM; + + for (c_size_t i = 0; i < self->size; i++) { + self->buffer[i] = (char)toupper((unsigned char)self->buffer[i]); + } + return C_ERR_OK; +} + + +c_err_t c_StringBuffer_Split(c_StringBuffer_t* self, const char* delimiter, c_StringBuffer_t** out_tokens, c_size_t* out_count) { + // 1. 严格的参数校验 + if (!self || !self->buffer || !delimiter || !out_tokens || !out_count) { + return C_ERR_PARAM; + } + + // 显式将输出重置,防止调用方读取未初始化的脏数据 + *out_tokens = NULL; + *out_count = 0; + + c_size_t delim_len = strlen(delimiter); + if (delim_len == 0) { + return C_ERR_PARAM; // 分隔符不能为空字符串 + } + + // 2. 第一轮扫描:计算一共会拆分出多少个 Token,以便一次性分配连续数组空间 + c_size_t token_count = 1; + const char* scan = self->buffer; + while ((scan = strstr(scan, delimiter)) != NULL) { + token_count++; + scan += delim_len; // 跳过当前分隔符继续匹配 + } + + // 3. 一次性分配容纳所有结构体的数组 + c_StringBuffer_t* tokens = (c_StringBuffer_t*)c_Allocator_Alloc(&self->allocator, token_count * sizeof(c_StringBuffer_t)); + if (!tokens) { + return C_ERR_NOMEM; + } + + // 预先清空结构体数组,使后续的防御性回滚清理更加安全 + for (c_size_t i = 0; i < token_count; i++) { + tokens[i].buffer = NULL; + tokens[i].capacity = 0; + tokens[i].size = 0; + } + + // 4. 第二轮扫描:精准切片并填充到独立的结构体中 + c_size_t current_token = 0; + c_size_t start_idx = 0; + + while (start_idx <= self->size) { + // 寻找下一个分隔符的位置 + char* match = strstr(self->buffer + start_idx, delimiter); + + // 计算当前 Token 的字节长度 + c_size_t token_len = match ? (c_size_t)(match - (self->buffer + start_idx)) : (self->size - start_idx); + + // 初始化子 StringBuffer(分配其内部的 char* 缓冲区) + c_err_t err = c_StringBuffer_Init(&tokens[current_token], token_len, &self->allocator); + if (err != C_ERR_OK) goto error_cleanup; + + // 如果长度大于 0,将片段内容追加拷贝进去 + if (token_len > 0) { + err = c_StringBuffer_Append(&tokens[current_token], self->buffer + start_idx, token_len); + if (err != C_ERR_OK) goto error_cleanup; + } + + current_token++; + if (!match) break; // 已处理完最后一个片段,退出循环 + + // 步进索引:当前片段长度 + 分隔符长度 + start_idx += token_len + delim_len; + } + + // 5. 成功赋值输出 + *out_tokens = tokens; + *out_count = token_count; + return C_ERR_OK; + +// 防御性垃圾回收:如果中途任何一个 Token 内存分配失败,完整回滚,绝不泄露 +error_cleanup: + for (c_size_t i = 0; i < token_count; i++) { + // c_StringBuffer_Destroy 内部有对 NULL 的安全校验 + c_StringBuffer_Destroy(&tokens[i]); + } + c_Allocator_Free(&self->allocator, tokens); + return C_ERR_NOMEM; +} + + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_StringBuffer_Join(c_StringBuffer_t* self, const c_StringBuffer_t tokens[], c_size_t count, const char* separator) { + if (!self || (!tokens && count > 0) || !separator) return C_ERR_PARAM; + + c_StringBuffer_Clear(self); + if (count == 0) return C_ERR_OK; + + c_size_t sep_len = strlen(separator); + c_size_t total_required_space = 0; + + // Pass 1: Compute exactly how much capacity is needed upfront to prevent intermediate reallocations + for (c_size_t i = 0; i < count; i++) { + total_required_space += tokens[i].size; + if (i < count - 1) { + total_required_space += sep_len; + } + } + + c_err_t err = c_StringBuffer_EnsureCapacity(self, total_required_space); + if (err != C_ERR_OK) return err; + + // Pass 2: Fast sequential data copying into the pre-sized buffer + for (c_size_t i = 0; i < count; i++) { + if (tokens[i].size > 0) { + memcpy(self->buffer + self->size, tokens[i].buffer, tokens[i].size); + self->size += tokens[i].size; + } + + if (i < count - 1 && sep_len > 0) { + memcpy(self->buffer + self->size, separator, sep_len); + self->size += sep_len; + } + } + + self->buffer[self->size] = '\0'; // Strictly enforce final null-termination + return C_ERR_OK; +} + +int c_StringBuffer_Equals(const c_StringBuffer_t* self, const char* string) { + if (!self || !string) return 0; + if (!self->buffer) return (string[0] == '\0'); + + // Optimization: Check sizing footprints first before comparing bytes + c_size_t str_len = strlen(string); + if (self->size != str_len) return 0; + + return (strcmp(self->buffer, string) == 0); +} + +int c_StringBuffer_EqualsIgnoreCase(const c_StringBuffer_t* self, const char* string) { + if (!self || !string) return 0; + if (!self->buffer) return (string[0] == '\0'); + + c_size_t str_len = strlen(string); + if (self->size != str_len) return 0; + + // Character-by-character validation mapped safely onto tolower limits + for (c_size_t i = 0; i < self->size; i++) { + if (tolower((unsigned char)self->buffer[i]) != tolower((unsigned char)string[i])) { + return 0; // Immediate mismatch exit + } + } + + return 1; // Content identities match perfectly +} + +int c_StringBuffer_Compare(const c_StringBuffer_t* self, const char* string) { + // Standardize null pointers to make safety deterministic + const char* s1 = (self && self->buffer) ? self->buffer : ""; + const char* s2 = string ? string : ""; + + return strcmp(s1, s2); +} + +c_err_t c_StringBuffer_Reverse(c_StringBuffer_t* self) { + if (!self) return C_ERR_PARAM; + if (self->size <= 1) return C_ERR_OK; // No-op if empty or single character + + c_size_t left = 0; + c_size_t right = self->size - 1; + + // Fast symmetric swap loop executing entirely in-place + while (left < right) { + char temp = self->buffer[left]; + self->buffer[left] = self->buffer[right]; + self->buffer[right] = temp; + + left++; + right--; + } + + // Maintain safety by preserving the existing null-terminator position + self->buffer[self->size] = '\0'; + return C_ERR_OK; +} + + +c_err_t c_StringBuffer_Substr(c_StringBuffer_t* self, c_size_t index, c_size_t length, c_StringBuffer_t* out_substring) { + if (!self || !out_substring) return C_ERR_PARAM; + + // Explicitly zero out the target structure descriptor up front to prevent undefined state access on failure + out_substring->buffer = NULL; + out_substring->capacity = 0; + out_substring->size = 0; + + if (index > self->size) return C_ERR_OUTOFBOUND; + + // Clamp the target length parameter dynamically if it exceeds the remaining data payload bounds + if (index + length > self->size) { + length = self->size - index; + } + + // Initialize the out string buffer with the exact exact footprint space required + c_err_t err = c_StringBuffer_Init(out_substring, length, &self->allocator); + if (err != C_ERR_OK) return err; + + if (length > 0) { + err = c_StringBuffer_Append(out_substring, self->buffer + index, length); + if (err != C_ERR_OK) { + c_StringBuffer_Destroy(out_substring); + return err; + } + } + + return C_ERR_OK; +} + +c_err_t c_StringBuffer_Slice(c_StringBuffer_t* self, c_size_t start_index, c_size_t end_index, c_StringBuffer_t* out_slice) { + if (!self || !out_slice) return C_ERR_PARAM; + + out_slice->buffer = NULL; + out_slice->capacity = 0; + out_slice->size = 0; + + if (start_index > self->size) return C_ERR_OUTOFBOUND; + + // Clamp end_index if it exceeds the structural size boundary limits + if (end_index > self->size) { + end_index = self->size; + } + + // If indices are out of order or equal, return an empty initialized string buffer instance safely + c_size_t length = (end_index > start_index) ? (end_index - start_index) : 0; + + c_err_t err = c_StringBuffer_Init(out_slice, length, &self->allocator); + if (err != C_ERR_OK) return err; + + if (length > 0) { + err = c_StringBuffer_Append(out_slice, self->buffer + start_index, length); + if (err != C_ERR_OK) { + c_StringBuffer_Destroy(out_slice); + return err; + } + } + + return C_ERR_OK; +} + + +c_err_t c_StringBuffer_strtoul(const c_StringBuffer_t* self, c_size_t start_index, int base, unsigned long* out_value, c_size_t* out_end_index) { + if (!self || !self->buffer || !out_value) return C_ERR_PARAM; + if (start_index >= self->size) return C_ERR_OUTOFBOUND; + + // Reset errno before executing standard parsing functions to isolate previous system actions + int current_errno = errno; + errno = 0; + + char* parse_end = NULL; + const char* start_ptr = self->buffer + start_index; + + unsigned long result = strtoul(start_ptr, &parse_end, base); + + // Error Validation Condition 1: Check for standard numerical overflow/underflow + if (errno == ERANGE) { + return C_ERR_OUTOFBOUND; // Numerical envelope exceeded bounds + } + + // Error Validation Condition 2: No structural digits could be parsed at all + if (parse_end == start_ptr) { + errno = current_errno; // Restore system errno + return C_ERR_PARAM; + } + + // Assign the computed scalar result out safely + *out_value = result; + + // Map pointer arithmetic distances back into the context of our indexing structural offset + if (out_end_index) { + *out_end_index = start_index + (c_size_t)(parse_end - start_ptr); + } + + errno = current_errno; // Restore system errno + return C_ERR_OK; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_StringBuffer_SetLength(c_StringBuffer_t* sb, c_size_t new_length) { + // 1. Core safety verification of structural parameters + if (!sb) { + return C_ERR_PARAM; + } + + // 2. Case A: Truncation step (new_length is within current memory boundaries) + if (new_length <= sb->size) { + sb->size = new_length; + if (sb->buffer && sb->size < sb->capacity) { + sb->buffer[sb->size] = '\0'; // Seal the new structural boundary line instantly + } + return C_ERR_OK; + } + + // 3. Case B: Buffer Expansion step (new_length exceeds current logical boundary size) + // Verify if we need to resize the dynamic backing heap array matrix + if (new_length >= sb->capacity) { + c_size_t new_capacity = sb->capacity == 0 ? 16 : sb->capacity * 2; + if (new_capacity <= new_length) { + new_capacity = new_length + 1; // Secure extra room for the trailing null-terminator + } + + c_size_t old_capacity = sb->capacity; + + // char* new_array = (char*)C_REALLOC(sb->buffer, new_capacity * sizeof(char)); + char* new_array = (char*)c_Allocator_Realloc(&sb->allocator, sb->buffer, sizeof(char) * old_capacity, sizeof(char) * new_capacity); + if (!new_array) { + return C_ERR_NOMEM; // Bubble up out-of-memory errors cleanly + } + sb->buffer = new_array; + sb->capacity = new_capacity; + } + + // 4. Zero-pad the newly appended logical spacing area segment + memset(sb->buffer + sb->size, 0, new_length - sb->size); + + // 5. Commit trailing metadata fields and place the final structural terminator string hook + sb->size = new_length; + sb->buffer[sb->size] = '\0'; + + return C_ERR_OK; +} diff --git a/Foundation/c_StringBuffer.h b/Foundation/c_StringBuffer.h new file mode 100644 index 0000000..24622a9 --- /dev/null +++ b/Foundation/c_StringBuffer.h @@ -0,0 +1,138 @@ +#ifndef INCLUDED_C_STRINGBUFFER_H +#define INCLUDED_C_STRINGBUFFER_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_STDARG_H +#define INCLUDED_STDARG_H +#include +#endif /*INCLUDED_STDARG_H*/ + +#ifndef INCLUDED_C_ALLOCATOR_H +#include +#endif /*INCLUDED_C_ALLOCATOR_H*/ + + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + char* buffer; + c_size_t capacity; + c_size_t size; + c_Allocator_t allocator; +}c_StringBuffer_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +C_STATIC_FORCE_INLINE +c_size_t c_StringBuffer_Size(c_StringBuffer_t* self) { + if (!self) return 0; + return self->size; +} + +C_STATIC_FORCE_INLINE +const char* c_StringBuffer_CStr(c_StringBuffer_t* self) { + if (!self) return NULL; + return self->buffer; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_StringBuffer_Init(c_StringBuffer_t* self, c_size_t capacity, c_Allocator_t* allocator); + +void c_StringBuffer_Destroy(c_StringBuffer_t* self); + +c_err_t c_StringBuffer_Resize(c_StringBuffer_t* self, c_size_t new_capacity); + +c_err_t c_StringBuffer_Append(c_StringBuffer_t* self, const char* string, c_size_t length); + +c_err_t c_StringBuffer_Prepend(c_StringBuffer_t* self, const char* string, c_size_t length); + +c_err_t c_StringBuffer_InsertAt(c_StringBuffer_t* self, c_size_t index, const char* string, c_size_t length); + +c_err_t c_StringBuffer_RemoveAt(c_StringBuffer_t* self, c_size_t index, c_size_t length); + +void c_StringBuffer_Clear(c_StringBuffer_t* self); + +c_err_t c_StringBuffer_AppendStr(c_StringBuffer_t* self, const char* string); +c_err_t c_StringBuffer_PrependStr(c_StringBuffer_t* self, const char* string); +c_err_t c_StringBuffer_InsertStrAt(c_StringBuffer_t* self, const char* string, c_size_t index); + +c_err_t c_StringBuffer_CopyTo(c_StringBuffer_t* self, c_size_t index, c_size_t length, char* buffer, c_size_t buffer_length); + +c_err_t c_StringBuffer_Printf(c_StringBuffer_t* self, const char* format, ...); +c_err_t c_StringBuffer_PrintfAt(c_StringBuffer_t* self, c_size_t index, const char* format, ...); + +c_err_t c_StringBuffer_VPrintf(c_StringBuffer_t* self, const char* format, va_list args); +c_err_t c_StringBuffer_VPrintfAt(c_StringBuffer_t* self, c_size_t index, const char* format, va_list args); + +c_err_t c_StringBuffer_AppendTimestamp(c_StringBuffer_t* self, const char* format, const struct tm* time_info); +c_err_t c_StringBuffer_AppendCurrentTimestamp(c_StringBuffer_t* self, const char* format, int use_utc); +c_err_t c_StringBuffer_InsertTimestampAt(c_StringBuffer_t* self, c_size_t index, const char* format, const struct tm* time_info); + +c_index_t c_StringBuffer_IndexOfStr(c_StringBuffer_t* self, c_size_t start_index, const char* substr); +c_index_t c_StringBuffer_IndexOfChar(c_StringBuffer_t* self, c_size_t start_index, char target); +c_index_t c_StringBuffer_LastIndexOfStr(c_StringBuffer_t* self, c_size_t start_index, const char* substr); +c_index_t c_StringBuffer_LastIndexOfChar(c_StringBuffer_t* self, c_size_t start_index, char target); + +c_err_t c_StringBuffer_ReplaceStr(c_StringBuffer_t* self, const char* old_str, const char* new_str); + +c_err_t c_StringBuffer_Trim(c_StringBuffer_t* self); +c_err_t c_StringBuffer_TrimLeft(c_StringBuffer_t* self); +c_err_t c_StringBuffer_TrimRight(c_StringBuffer_t* self); + +c_err_t c_StringBuffer_ToLower(c_StringBuffer_t* self); +c_err_t c_StringBuffer_ToUpper(c_StringBuffer_t* self); + +/* + * 重新设计的 Split 函数 + * @param self: 原始字符串缓冲区指针 + * @param delimiter: 分隔符字符串(不能为 NULL 或空字符串) + * @param out_tokens: 输出参数,用于接收分配的 c_StringBuffer_t 结构体数组指针 + * @param out_count: 输出参数,用于接收拆分出来的 Token 总数 + */ +c_err_t c_StringBuffer_Split(c_StringBuffer_t* self, const char* delimiter, c_StringBuffer_t** out_tokens, c_size_t* out_count); + +c_err_t c_StringBuffer_Join(c_StringBuffer_t* self, const c_StringBuffer_t tokens[], c_size_t count, const char* separator); + +int c_StringBuffer_Equals(const c_StringBuffer_t* self, const char* string); +int c_StringBuffer_EqualsIgnoreCase(const c_StringBuffer_t* self, const char* string); + +int c_StringBuffer_Compare(const c_StringBuffer_t* self, const char* string); +c_err_t c_StringBuffer_Reverse(c_StringBuffer_t* self); + +c_err_t c_StringBuffer_Substr(c_StringBuffer_t* self, c_size_t index, c_size_t length, c_StringBuffer_t* out_substring); +c_err_t c_StringBuffer_Slice(c_StringBuffer_t* self, c_size_t start_index, c_size_t end_index, c_StringBuffer_t* out_slice); + +/* + * Parses an unsigned long value from the buffer starting at a specific index. + * @param self: The string buffer instance. + * @param start_index: The index position to start scanning from. + * @param base: The number base system to parse (0, 2-36). + * @param out_value: Destination pointer for the parsed unsigned long. + * @param out_end_index: Optional destination pointer for the index of the first character after the number. + */ +c_err_t c_StringBuffer_strtoul(const c_StringBuffer_t* self, c_size_t start_index, int base, unsigned long* out_value, c_size_t* out_end_index); + + +/** + * Set the logical character length of the string buffer. + * If the new length is less than the current size, the string is truncated. + * If the new length is greater, the buffer expands and is zero-padded. + * + * @param sb Pointer to the active c_StringBuffer_t instance + * @param new_length The targeted logical length boundary to apply + * @return + * - C_ERR_OK if success + * - C_ERR_PARAM if parameters are invalid + * - C_ERR_NOMEM if expansion fails due to system exhaustion + */ +c_err_t c_StringBuffer_SetLength(c_StringBuffer_t* sb, c_size_t new_length); + +#endif /*INCLUDED_C_STRINGBUFFER_H*/ diff --git a/Foundation/c_StringBuffer.t.c b/Foundation/c_StringBuffer.t.c new file mode 100644 index 0000000..c7e001d --- /dev/null +++ b/Foundation/c_StringBuffer.t.c @@ -0,0 +1,156 @@ +#include "c_StringBuffer.h" +#include "c_Test.h" + +#include +#include + +// 模拟多态分配器的桩监控(用于审计自建内存池在频繁 Split 时的块变化) +static size_t g_str_pool_active_chunks = 0; +static void* mock_str_pool_alloc(size_t size, void* ctx) { + (void)ctx; g_str_pool_active_chunks++; return malloc(size); +} +static void mock_str_pool_free(void* ptr, void* ctx) { + (void)ctx; if (ptr) g_str_pool_active_chunks--; free(ptr); +} + +// 模拟的伪 realloc 桥接(桩实现) +static void* mock_str_pool_realloc(void* ptr, size_t old_size, size_t new_size, void* ctx) { + (void)ctx; (void)old_size; + return realloc(ptr, new_size); +} + +TEST_CASE(test_string_buffer_ultimate_matrix_flow) { + g_str_pool_active_chunks = 0; + + // 1. 初始化多态自治内存池(模拟伙伴系统或 Arena 物理底座) + c_Allocator_t str_pool = { + .alloc = mock_str_pool_alloc, + .realloc = mock_str_pool_realloc, + .free = mock_str_pool_free, + .dtor = NULL, + .ud = NULL + }; + + c_StringBuffer_t sb; + // 初始有效容量设为 4(底层物理分配 5 字节含 \0 预留位) + ASSERT_INT_EQ(C_ERR_OK, c_StringBuffer_Init(&sb, 4, &str_pool)); + ASSERT_INT_EQ(0, (int)c_StringBuffer_Size(&sb)); + ASSERT_INT_EQ(1, (int)g_str_pool_active_chunks); + ASSERT_TRUE(strcmp(c_StringBuffer_CStr(&sb), "") == 0); // 初始应为空串 + + // 2. 验证常规追加、前缀插入及重叠区域随机增删( memmove 移位安全) + ASSERT_INT_EQ(C_ERR_OK, c_StringBuffer_AppendStr(&sb, "WORLD")); + ASSERT_INT_EQ(C_ERR_OK, c_StringBuffer_PrependStr(&sb, "HELLO_")); // 此时: "HELLO_WORLD" + ASSERT_INT_EQ(11, (int)c_StringBuffer_Size(&sb)); + ASSERT_TRUE(c_StringBuffer_Equals(&sb, "HELLO_WORLD")); + + // 随机位置定点插入 + ASSERT_INT_EQ(C_ERR_OK, c_StringBuffer_InsertStrAt(&sb, "MY_", 6)); // 此时: "HELLO_MY_WORLD" + ASSERT_TRUE(c_StringBuffer_Equals(&sb, "HELLO_MY_WORLD")); + + // 随机位置定点删除 + ASSERT_INT_EQ(C_ERR_OK, c_StringBuffer_RemoveAt(&sb, 5, 4)); // 移去 "_MY_",此时: "HELLOWORLD" + ASSERT_TRUE(c_StringBuffer_Equals(&sb, "HELLOWORLD")); + + // 3. 验证试探型双向写入 Printf 格式化接口 + ASSERT_INT_EQ(C_ERR_OK, c_StringBuffer_PrintfAt(&sb, 5, "NEW_%s", "BUFFER")); // 此时: "HELLONEW_BUFFER" + ASSERT_TRUE(c_StringBuffer_Equals(&sb, "HELLONEW_BUFFERWORLD")); + + // 4. 验证高速特征指纹匹配(IndexOf / LastIndexOf) + c_StringBuffer_Clear(&sb); + c_StringBuffer_AppendStr(&sb, "TOKEN_A_TOKEN_B_TOKEN_A"); + + c_index_t idx_first = c_StringBuffer_IndexOfStr(&sb, 0, "TOKEN_A"); + c_index_t idx_last = c_StringBuffer_LastIndexOfStr(&sb, sb.size, "TOKEN_A"); + + ASSERT_INT_EQ(0, (int)idx_first); + ASSERT_INT_EQ(16, (int)idx_last); // 尾部反向查找成功 + ASSERT_INT_EQ(5, (int)c_StringBuffer_IndexOfChar(&sb, 0, '_')); + + // 全词全局高效替换 + ASSERT_INT_EQ(C_ERR_OK, c_StringBuffer_ReplaceStr(&sb, "TOKEN_A", "KEY")); // 此时: "KEY_TOKEN_B_KEY" + ASSERT_TRUE(c_StringBuffer_Equals(&sb, "KEY_TOKEN_B_KEY")); + + // 5. 验证全词高级文本规整(Trim家族)与大小写强转 + c_StringBuffer_Clear(&sb); + c_StringBuffer_AppendStr(&sb, " \t DATA_LOG \r\n "); + ASSERT_INT_EQ(C_ERR_OK, c_StringBuffer_Trim(&sb)); + ASSERT_TRUE(c_StringBuffer_Equals(&sb, "DATA_LOG")); + + c_StringBuffer_ToLower(&sb); + ASSERT_TRUE(c_StringBuffer_Equals(&sb, "data_log")); + c_StringBuffer_ToUpper(&sb); + ASSERT_TRUE(c_StringBuffer_Equals(&sb, "DATA_LOG")); + + // 6. 【核心大演进】:高能分包(Split)与重新粘合(Join)机制闭环审计 + c_StringBuffer_Clear(&sb); + c_StringBuffer_AppendStr(&sb, "Linux;RTOS;FreeRTOS;BareMetal"); + + c_StringBuffer_t* tokens = NULL; + c_size_t token_count = 0; + + // 执行精准多态切片拆分 + c_err_t split_err = c_StringBuffer_Split(&sb, ";", &tokens, &token_count); + ASSERT_INT_EQ(C_ERR_OK, split_err); + ASSERT_INT_EQ(4, (int)token_count); // 必须切出 4 个 Token + + // 校验切片内容的值复制隔离性及内嵌分配器继承血脉 + ASSERT_TRUE(c_StringBuffer_Equals(&tokens[0], "Linux")); + ASSERT_TRUE(c_StringBuffer_Equals(&tokens[3], "BareMetal")); + + // 将切片利用 Join 重新粘合为一个以逗号 "," 隔开的全新大串 + c_StringBuffer_t joined_sb; + c_StringBuffer_Init(&joined_sb, 32, &str_pool); + ASSERT_INT_EQ(C_ERR_OK, c_StringBuffer_Join(&joined_sb, tokens, token_count, ",")); + ASSERT_TRUE(c_StringBuffer_Equals(&joined_sb, "Linux,RTOS,FreeRTOS,BareMetal")); + + // 深度火化销毁切片资源沙盒 + for (c_size_t i = 0; i < token_count; i++) { + c_StringBuffer_Destroy(&tokens[i]); + } + c_Allocator_Free(&str_pool, tokens); + + // 7. 验证高级文本有限状态机数值解析提取 (strtoul 闭环重塑) + c_StringBuffer_Clear(&sb); + c_StringBuffer_AppendStr(&sb, " -0x7FFFFFFF_PADDING_DATA"); + unsigned long parsed_numeric = 0; + c_size_t end_parse_index = 0; + + c_err_t s2u_err = c_StringBuffer_strtoul(&sb, 0, 0, &parsed_numeric, &end_parse_index); + ASSERT_INT_EQ(C_ERR_OK, s2u_err); + // 0x7FFFFFFF 在取负后以补码形式转换为无符号形式 + ASSERT_TRUE(parsed_numeric == (unsigned long)(-0x7FFFFFFF)); + ASSERT_INT_EQ(14, (int)end_parse_index); // 精准停留在第一个非合法十六进制字符下划线 '_' 处 + + // 8. 验证契约容量扩容与零填充(SetLength) + c_StringBuffer_Clear(&sb); + c_StringBuffer_AppendStr(&sb, "XYZ"); + ASSERT_INT_EQ(C_ERR_OK, c_StringBuffer_SetLength(&sb, 55)); // 扩容并对齐零填充 + ASSERT_INT_EQ(55, (int)c_StringBuffer_Size(&sb)); + ASSERT_INT_EQ('\0', sb.buffer[3]); // 确认空白区域零填充就位 + ASSERT_INT_EQ('\0', sb.buffer[5]); // 确认隐式尾部安全封底 + + // 9. 验证子串与切片深度抽取复制(Substr / Slice) + c_StringBuffer_t slice_out; + ASSERT_INT_EQ(C_ERR_OK, c_StringBuffer_Slice(&joined_sb, 6, 10, &slice_out)); // 截取 "RTOS" + ASSERT_TRUE(c_StringBuffer_Equals(&slice_out, "RTOS")); + + // 彻底解体火化释放所有资源 + c_StringBuffer_Destroy(&sb); + c_StringBuffer_Destroy(&joined_sb); + c_StringBuffer_Destroy(&slice_out); + + // 终极一致性内存池大审判:全量大缓冲区底座、切片原子核卸载,分配块必须完美清算归零! + ASSERT_INT_EQ_MSG(0, (int)g_str_pool_active_chunks, "CRITICAL: c_StringBuffer_t leaked heap space inside the dynamic allocator pool!"); +} + +int main(void) { + printf("\n"); + TEST_START(C_StringBuffer_Ultimate_Matrix_Tests); + + // 驱动高防御动态字符串缓冲区全 API 功能流验证 + RUN_TEST(test_string_buffer_ultimate_matrix_flow); + + TEST_REPORT(); + RETURN_TEST_STATUS; +} \ No newline at end of file