diff --git a/Foundation/c_Hex.c b/Foundation/c_Hex.c new file mode 100644 index 0000000..17f473d --- /dev/null +++ b/Foundation/c_Hex.c @@ -0,0 +1,278 @@ +#include "c_Hex.h" + +#include +#include +#include +#include + +/* -------------------------------------------------------------------------------------------------------------- */ +/* */ + +#define BYTE_AT(A, I) (*(((unsigned char*)(A))+(I))) + +/* ================================================================================ */ + + + +void c_Hex_DumpBin(const void* data, c_size_t size, int line_size){ + for(c_size_t i=0; i=0; d--){ + putchar((b & (1<= ' ' && ((unsigned char*)data)[i] <= '~') { + ascii[i % 16] = ((unsigned char*)data)[i]; + } else { + ascii[i % 16] = '.'; + } + if ((i+1) % 8 == 0 || i+1 == size) { + dump(" "); + if ((i+1) % 16 == 0) { + dump("| %s \n", ascii); + } else if (i+1 == size) { + ascii[(i+1) % 16] = '\0'; + if ((i+1) % 16 <= 8) { + dump(" "); + } + for (j = (i+1) % 16; j < 16; ++j) { + dump(" "); + } + dump("| %s \n", ascii); + } + } + } +} + +/* ================================================================================ */ +// +// static const uint8_t LOOKUP_TABLE_LOWER[] = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66}; +// static const uint8_t LOOKUP_TABLE_UPPER[] = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46}; +// +// char* c_Hex_BinToHex(const uint8_t *data +// , c_size_t data_size +// , const bool isUpperCase +// , const c_ByteOrder_t byteOrder) +// { +// +// assert(data); +// assert(data_size > 0); +// +// c_size_t i; +// const c_size_t buffer_size = (data_size << 1 ) + 1; +// +// char* buffer = C_ALLOC(buffer_size); +// if (!buffer) { +// return NULL; +// } +// assert(buffer); +// +// const uint8_t* lookup = isUpperCase?LOOKUP_TABLE_UPPER:LOOKUP_TABLE_LOWER; +// +// for(i =0; i> 4) & 0x0F]; +// buffer[(i<<1) + 1] = (char)lookup[(data[index] & 0x0F)]; +// } +// +// buffer[buffer_size] = '\0'; +// +// return buffer; +// } + +/* ================================================================================ */ +#if 0 +static const unsigned char TBL[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 58, 59, 60, 61, + 62, 63, 64, 10, 11, 12, 13, 14, 15, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 10, 11, 12, 13, 14, 15 +}; +static const unsigned char *LOOKUP = TBL - 48; + +uint8_t* c_Hex_HexToBin(const char* hex, const c_size_t hex_size, c_size_t* returnSize){ + uint8_t b1, b2; + + c_size_t bin_size = hex_size >> 1; + uint8_t* buffer = C_ALLOC(bin_size); + if (!buffer) { + return NULL; + } + assert(buffer); + + const char* in = hex; + const char* end = in + hex_size; + while(in < end){ + b1 = LOOKUP[(uint8_t)*(in++)]; + b2 = LOOKUP[(uint8_t)*(in++)]; + *(buffer) = ( b1 << 4) | b2; + buffer++; + } + + if (returnSize) { + *returnSize = bin_size; + } + + return buffer - bin_size; +} + +void c_Hex_Free(void* ptr) { + C_FREE(ptr); +} +#endif + + +/** + * @brief 内部私有:将单个十六进制字符转换为合规的 4 位无符号半字节(Nibble) + * + * 🌟【高吞吐无损硬容错】:完美兼容大小写,一旦遭遇非法杂质字符立刻返回 -1 触发拦截 + */ +C_STATIC_FORCE_INLINE +int c_Hex_CharToNibble(char c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + return -1; // 捕获到非法十六进制元素 +} + +/** + * @brief 工业级二进制安全:十六进制文本字符串逆向解析还原为真实二进制字节流 + * + * @param hex_str 待解析的标准十六进制文本字符串(以 '\0' 截止,如 "4A00FE") + * @param out_bin 由外部声明传入、用于承接导出的二进制字节流物理缓冲区首地址 + * @param bin_max_cap 外部缓冲区 out_bin 的最大物理插槽配额上限限制(防缓冲区溢出踩踏) + * @param out_size 🌟【物理边界锁】:成功转化后,通过此二级指针强行传出实际生成的【真实字节总个数 (N)】 + */ +c_err_t c_Hex_HexToBin(const char* hex_str, void* out_bin, c_size_t bin_max_cap, c_size_t* out_size) { + // 1. 前置毒入参非法强拦截 + if (!hex_str || !out_bin || !out_size || bin_max_cap == 0) { + return C_ERR_PARAM; + } + + c_size_t hex_len = strlen(hex_str); + if (hex_len == 0) { + *out_size = 0; + return C_ERR_EMPTY; + } + + // 🌟【硬核格式契约 1】:十六进制字符串每 2 个可显示字符拼接成 1 个完整字节。 + // 如果总字符长度为奇数,说明控制流发生非对称残缺,属于恶意或不合规输入,立刻原地熔断! + if ((hex_len & 1) != 0) { + *out_size = 0; + return C_ERR_PARAM; + } + + // 计算出本次还原所期望开辟生成的精确二进制字节体量 + c_size_t required_bin_size = hex_len >> 1; + + // 🌟【硬核格式契约 2】:前置上界高位拦截。 + // 若外部承接缓冲区的物理最大上限 capacity 根本吃不下这批数据,强行抛出越界异常,物理粉碎堆溢出! + if (required_bin_size > bin_max_cap) { + *out_size = 0; + return C_ERR_OUTOFBOUND; + } + + unsigned char* bin_buf = (unsigned char*)out_bin; + + // 2. 启动双字高频滑窗位移状态机 + for (c_size_t i = 0; i < required_bin_size; i++) { + // 每轮循环,密集抓取 2 个相邻的十六进制可显示字符 + char high_char = hex_str[i << 1]; + char low_char = hex_str[(i << 1) + 1]; + + int high_nibble = c_Hex_CharToNibble(high_char); + int low_nibble = c_Hex_CharToNibble(low_char); + + // 🌟【硬核格式契约 3】:如果沿途解包走查发现夹杂了任何非十六进制可打印杂质字符(如 G), + // 判定该报文已被严重污染,立刻启动自毁灭机制清空已生成数据,严肃报错退出! + if (high_nibble < 0 || low_nibble < 0) { + memset(out_bin, 0, bin_max_cap); // 洗净泄露 + *out_size = 0; + return C_ERR_PARAM; + } + + // 3. 高低位半字节(Nibble)按位执行物理高位填补拼接:(High << 4) | Low + bin_buf[i] = (unsigned char)((high_nibble << 4) | low_nibble); + } + + // 4. 锁定物理绝对边界传出,不依附任何文本 \0 截断 + *out_size = required_bin_size; + return C_ERR_OK; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * @brief 工业级二进制安全:二进制字节流正向解析还原为标准十六进制文本字符串 + * + * @param in_bin 待转化的二进制字节流物理缓冲区首地址(二进制安全,支持 \0 内置流) + * @param bin_size 二进制字节流的绝对有效物理字节总大小 (N) + * @param uppercase 控制开关:true 导出为大写十六进制字符串(如 "4A00FE");false 导出为小写(如 "4a00fe") + * @param out_hex_str 由外部声明传入、用于承接导出的十六进制可写文本缓冲区起始地址 + * @param hex_max_cap 外部文本缓冲区的最大物理插槽配额上限限制(防缓冲区溢出踩踏) + */ +c_err_t c_Hex_BinToHex(const void* in_bin, c_size_t bin_size, bool uppercase, char* out_hex_str, c_size_t hex_max_cap) { + // 1. 前置毒入参非法强拦截 + if (!in_bin || !out_hex_str || hex_max_cap == 0) { + return C_ERR_PARAM; + } + + if (bin_size == 0) { + if (hex_max_cap > 0) { + out_hex_str[0] = '\0'; + } + return C_ERR_EMPTY; + } + + // 🌟【硬核算术加固】:前置逆向除法与乘位溢出防御审计 + // 计算出本次还原所期望生成的文本矩阵绝对字节体量:N * 2 + 1 (含末尾 \0 终止符) + if (((c_size_t)-1 - 1) / 2 < bin_size) { + return C_ERR_NOMEM; + } + c_size_t required_hex_size = (bin_size << 1) + 1; + + // 🌟【硬核格式契约】:前置上界高位拦截。 + // 若外部承接缓冲区的物理最大上限 capacity 根本吃不下这批数据,强行抛出越界异常,物理粉碎堆溢出! + if (required_hex_size > hex_max_cap) { + return C_ERR_OUTOFBOUND; + } + + const unsigned char* bin_buf = (const unsigned char*)in_bin; + + // 弹性选择十六进制多态高速查找字母表 + const char* hex_digits = uppercase ? "0123456789ABCDEF" : "0123456789abcdef"; + + // 2. 启动单向滑窗转换状态机 + for (c_size_t i = 0; i < bin_size; i++) { + unsigned char byte_val = bin_buf[i]; + + // 分离高、低位半字节(Nibble)按位定位至字母表矩阵 + out_hex_str[i << 1] = hex_digits[(byte_val >> 4) & 0x0F]; // 提取高 4 位 + out_hex_str[(i << 1) + 1] = hex_digits[byte_val & 0x0F]; // 提取低 4 位 + } + + // 3. 完美格式化合拢:在尾端强制安全截断注入标准 C 风格 '\0' 终止符 + out_hex_str[bin_size << 1] = '\0'; + + return C_ERR_OK; +} + diff --git a/Foundation/c_Hex.h b/Foundation/c_Hex.h new file mode 100644 index 0000000..d20af91 --- /dev/null +++ b/Foundation/c_Hex.h @@ -0,0 +1,148 @@ +#ifndef INCLUDED_C_HEX_H +#define INCLUDED_C_HEX_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + + +/* -------------------------------------------------------------------------------------------------------------- */ +/* */ + + +void c_Hex_DumpBin(const void* data, c_size_t size, int line_size); + +void c_Hex_Dump(const void *data, c_size_t size); + +void c_Hex_DumpTo(const void *data, c_size_t size, void (*dump)(const char*, ...)); + +c_err_t c_Hex_HexToBin(const char* hex_str, void* out_bin, c_size_t bin_max_cap, c_size_t* out_size); + + +/** + * @brief 工业级二进制安全:二进制字节流正向解析还原为标准十六进制文本字符串 + * + * @param in_bin 待转化的二进制字节流物理缓冲区首地址(二进制安全,支持 \0 内置流) + * @param bin_size 二进制字节流的绝对有效物理字节总大小 (N) + * @param uppercase 控制开关:true 导出为大写十六进制字符串(如 "4A00FE");false 导出为小写(如 "4a00fe") + * @param out_hex_str 由外部声明传入、用于承接导出的十六进制可写文本缓冲区起始地址 + * @param hex_max_cap 外部文本缓冲区的最大物理插槽配额上限限制(防缓冲区溢出踩踏) + */ +c_err_t c_Hex_BinToHex(const void* in_bin, c_size_t bin_size, bool uppercase, char* out_hex_str, c_size_t hex_max_cap); + + +/* ================================================================================ */ +#define C_HEX_SET_UINT8(array, idx, u8v) ((array)[(idx)]=(u8v)&0xFF) + +#define C_HEX_SET_UINT16_BE(array, idx, u16v) \ +do{ \ + (array)[(idx)] = ((u16v) >> 8) & 0xFF; \ + (array)[(idx)+1] = ((u16v)) & 0xFF; \ +}while(0) + +#define C_HEX_SET_UINT16_LE(array, idx, u16v) \ +do{ \ + (array)[(idx)] = ((u16v)) & 0xFF; \ + (array)[(idx)+1] = ((u16v) >> 8) & 0xFF; \ +}while(0) + +#define C_HEX_SET_UINT24_BE(array, idx, u24v) \ +do{ \ + (array)[(idx)] = ((u24v) >> 16) & 0xFF; \ + (array)[(idx)+1] = ((u24v) >> 8) & 0xFF; \ + (array)[(idx)+2] = ((u24v)) & 0xFF; \ +}while(0) + +#define C_HEX_SET_UINT24_LE(array, idx, u24v) \ +do{ \ + (array)[(idx)] = ((u24v)) & 0xFF; \ + (array)[(idx)+1] = ((u24v) >> 8) & 0xFF; \ + (array)[(idx)+2] = ((u24v) >> 16) & 0xFF; \ +}while(0) + +#define C_HEX_SET_UINT32_BE(array, idx, u32v) \ +do{ \ + (array)[(idx)] = ((u32v) >> 24) & 0xFF; \ + (array)[(idx)+1] = ((u32v) >> 16) & 0xFF; \ + (array)[(idx)+2] = ((u32v) >> 8) & 0xFF; \ + (array)[(idx)+3] = ((u32v)) & 0xFF; \ +}while(0) + +#define C_HEX_SET_UINT32_LE(array, idx, u32v) \ +do{ \ + (array)[(idx)] = ((u32v)) & 0xFF; \ + (array)[(idx)+1] = ((u32v) >> 8) & 0xFF; \ + (array)[(idx)+2] = ((u32v) >> 16) & 0xFF; \ + (array)[(idx)+3] = ((u32v) >> 24) & 0xFF; \ +}while(0) + +#define C_HEX_SET_UINT64_BE(array, idx, u64v) \ +do{ \ + (array)[(idx)] = ((u64v) >> 56) & 0xFF; \ + (array)[(idx)+1] = ((u64v) >> 48) & 0xFF; \ + (array)[(idx)+2] = ((u64v) >> 40) & 0xFF; \ + (array)[(idx)+3] = ((u64v) >> 32) & 0xFF; \ + (array)[(idx)+4] = ((u64v) >> 24) & 0xFF; \ + (array)[(idx)+5] = ((u64v) >> 16) & 0xFF; \ + (array)[(idx)+6] = ((u64v) >> 8) & 0xFF; \ + (array)[(idx)+7] = ((u64v)) & 0xFF; \ +}while(0) + +#define C_HEX_SET_UINT64_LE(array, idx, u64v) \ +do{ \ + (array)[(idx)] = ((u64v)) & 0xFF; \ + (array)[(idx)+1] = ((u64v) >> 8) & 0xFF; \ + (array)[(idx)+2] = ((u64v) >> 16) & 0xFF; \ + (array)[(idx)+3] = ((u64v) >> 24) & 0xFF; \ + (array)[(idx)+4] = ((u64v) >> 32) & 0xFF; \ + (array)[(idx)+5] = ((u64v) >> 40) & 0xFF; \ + (array)[(idx)+6] = ((u64v) >> 48) & 0xFF; \ + (array)[(idx)+7] = ((u64v) >> 56) & 0xFF; \ +}while(0) + +#define C_HEX_GET_UINT8(array, idx) (((array)[(idx)])&0xFF) + +#define C_HEX_GET_UINT16_BE(array, idx) ((((array)[(idx)]&0xFF)<<8) | ((array)[(idx)+1] & 0xFF)) +#define C_HEX_GET_UINT16_LE(array, idx) (((array)[(idx)]&0xFF) | (((array)[(idx)+1] & 0xFF)<<8) ) + +#define C_HEX_GET_UINT24_BE(array, idx) ( (((array)[(idx)]&0xFF)<<16) | (((array)[(idx)+1]&0xFF)<<8) | ((array)[(idx)+2] & 0xFF) ) +#define C_HEX_GET_UINT24_LE(array, idx) ( ((array)[(idx)]&0xFF) | (((array)[(idx)+1]&0xFF)<<8) | (((array)[(idx)+2]&0xFF)<<16) ) + +#define C_HEX_GET_UINT32_BE(array, idx) ( \ + (((array)[(idx)]&0xFF)<<24) \ + | (((array)[(idx)+1]&0xFF)<<16) \ + | (((array)[(idx)+2]&0xFF)<<8) \ + | ((array)[(idx)+3] & 0xFF) ) + +#define C_HEX_GET_UINT32_LE(array, idx) ( \ + ((array)[(idx)]&0xFF) \ + | (((array)[(idx)+1]&0xFF)<<8) \ + | (((array)[(idx)+2]&0xFF)<<16) \ + | (((array)[(idx)+3]&0xFF)<<24) ) + + +#define C_HEX_GET_UINT64_BE(array, idx) ( \ + ((uint64_t)((array)[(idx)]&0xFF)<<56) \ + | ((uint64_t)((array)[(idx)+1]&0xFF)<<48) \ + | ((uint64_t)((array)[(idx)+2]&0xFF)<<40) \ + | ((uint64_t)((array)[(idx)+3]&0xFF)<<32) \ + | ((uint64_t)((array)[(idx)+4]&0xFF)<<24) \ + | ((uint64_t)((array)[(idx)+5]&0xFF)<<16) \ + | ((uint64_t)((array)[(idx)+6]&0xFF)<<8) \ + | ((uint64_t)(array)[(idx)+7] & 0xFF) ) + +#define C_HEX_GET_UINT64_LE(array, idx) ( \ + ((uint64_t)(array)[(idx)]&0xFF) \ + | ((uint64_t)((array)[(idx)+1]&0xFF)<<8) \ + | ((uint64_t)((array)[(idx)+2]&0xFF)<<16) \ + | ((uint64_t)((array)[(idx)+3]&0xFF)<<24) ) \ + | ((uint64_t)((array)[(idx)+4]&0xFF)<<32) ) \ + | ((uint64_t)((array)[(idx)+5]&0xFF)<<40) ) \ + | ((uint64_t)((array)[(idx)+6]&0xFF)<<48) ) \ + | ((uint64_t)((array)[(idx)+7]&0xFF)<<56) ) + + + + + +#endif /*INCLUDED_C_HEX_H*/ diff --git a/Foundation/c_Hex.t.c b/Foundation/c_Hex.t.c new file mode 100644 index 0000000..711779b --- /dev/null +++ b/Foundation/c_Hex.t.c @@ -0,0 +1,90 @@ +#include "c_Hex.h" +#include "c_Test.h" +#include +#include + +TEST_CASE(test_c_Hex_BinToHex_A) { + // 🌟【场景 A】:输入包含 0x00 中途截断干扰项的二进制数据密钥流 + // 预期转化为大写十六进制字符串后必须为:"4A00FE1B" + const unsigned char raw_bin_stream[] = { 0x4A, 0x00, 0xFE, 0x1B }; + c_size_t bin_len = sizeof(raw_bin_stream); + + char hex_output_buffer[16]; + + // 1. 验证大写形式正向导出 + c_err_t err = c_Hex_BinToHex(raw_bin_stream, bin_len, true, hex_output_buffer, 16); + ASSERT_INT_EQ(C_ERR_OK, err); + ASSERT_TRUE(strcmp("4A00FE1B", hex_output_buffer) == 0); // 完美跨越 00 截断并成功输出大写文本 + + // 2. 验证小写形式正向导出 + err = c_Hex_BinToHex(raw_bin_stream, bin_len, false, hex_output_buffer, 16); + ASSERT_INT_EQ(C_ERR_OK, err); + ASSERT_TRUE(strcmp("4a00fe1b", hex_output_buffer) == 0); // 成功输出小写文本 +} + +TEST_CASE(test_c_Hex_BinToHex_B) { + const unsigned char dummy_bin[] = { 0xAA, 0xBB, 0xCC }; + char dummy_hex_out[16]; + + // 🌟【场景 B】:外部承接缓冲区分配空间配额严重不足(仅给 5 字节,但 3 字节输入需要 3*2+1=7 字节空间) + // 预期控制流必须前置高位拦截,原地拒绝并抛出越界异常状态码 C_ERR_OUTOFBOUND,粉碎缓冲区踩踏溢出! + ASSERT_INT_EQ(C_ERR_OUTOFBOUND, c_Hex_BinToHex(dummy_bin, 3, true, dummy_hex_out, 5)); + + // 🌟【场景 C】:验证空仓异常状态码拦截返回 + ASSERT_INT_EQ(C_ERR_EMPTY, c_Hex_BinToHex(dummy_bin, 0, true, dummy_hex_out, 16)); + ASSERT_TRUE(dummy_hex_out[0] == '\0'); // 确保空输入时尾端完好写入空截止符 + + // 🌟【场景 D】:验证毒参数非合规强拦截线 + ASSERT_INT_EQ(C_ERR_PARAM, c_Hex_BinToHex(NULL, 5, true, dummy_hex_out, 16)); + ASSERT_INT_EQ(C_ERR_PARAM, c_Hex_BinToHex(dummy_bin, 5, true, NULL, 16)); +} + +TEST_CASE(test_c_Hex_HexToBin_A) { + // 🌟【场景 A】:输入一个大小写变长混合、且正中央故意包含 00 以便生成二进制 \0 数据的十六进制文本 + // 预期对应的物理二进制内容: 0x4A, 0x00, 0xFE, 0x1B + const char* mixed_hex_string = "4a00FE1b"; + + unsigned char bin_output_buffer[16]; + c_size_t actual_generated_size = 0; + + c_err_t err = c_Hex_HexToBin(mixed_hex_string, bin_output_buffer, 16, &actual_generated_size); + ASSERT_INT_EQ(C_ERR_OK, err); + + // 绝对物理长度必须精准卡死导出为 4 字节,不被 00 干扰斩断 + ASSERT_INT_EQ(4, (int)actual_generated_size); + + // 逐字节穿透核验真实的十六进制数字指纹内容 + ASSERT_INT_EQ(0x4A, bin_output_buffer[0]); + ASSERT_INT_EQ(0x00, bin_output_buffer[1]); // 验证 \0 无损存储 + ASSERT_INT_EQ(0xFE, bin_output_buffer[2]); + ASSERT_INT_EQ(0x1B, bin_output_buffer[3]); +} + +TEST_CASE(test_c_Hex_HexToBin_B) { + unsigned char dummy_buf[16]; + c_size_t dummy_size = 0; + + // 🌟【场景 B】:输入奇数位残缺字面量(长度 5),无法配对,预期必须爆发 C_ERR_PARAM 拦截 + ASSERT_INT_EQ(C_ERR_PARAM, c_Hex_HexToBin("A0F1B", dummy_buf, 16, &dummy_size)); + + // 🌟【场景 C】:注入了非十六进制字母表上限的污染杂质字符 'G',预期必须原地自毁熔断报错 + ASSERT_INT_EQ(C_ERR_PARAM, c_Hex_HexToBin("A0F1G2", dummy_buf, 16, &dummy_size)); + ASSERT_INT_EQ(0, (int)dummy_size); + + // 🌟【场景 D】:外部缓冲区承接上限过低(仅给 2 字节容量,但需要 3 字节),预期触发越界保护锁 + ASSERT_INT_EQ(C_ERR_OUTOFBOUND, c_Hex_HexToBin("4A00FE", dummy_buf, 2, &dummy_size)); +} + +// ========================================== +// 5. 主集成入口 +// ========================================== +int main(void) { + TEST_START(C_Hex_BinToHex_TestSuite); + RUN_TEST(test_c_Hex_BinToHex_A); + RUN_TEST(test_c_Hex_BinToHex_B); + + RUN_TEST(test_c_Hex_HexToBin_A); + RUN_TEST(test_c_Hex_HexToBin_B); + TEST_REPORT(); + return (g_test_registry.failed_count > 0 ? 1 : 0); +} \ No newline at end of file diff --git a/Foundation/c_StringBuffer.c b/Foundation/c_StringBuffer.c index be8309b..c937d39 100644 --- a/Foundation/c_StringBuffer.c +++ b/Foundation/c_StringBuffer.c @@ -145,6 +145,17 @@ void c_StringBuffer_Clear(c_StringBuffer_t* self) { /* --- Explicit String-Wrapper Interfaces --- */ +c_err_t c_StringBuffer_PopBack(c_StringBuffer_t* self) { + if (!self) return C_ERR_PARAM; + if (self->size == 0) return C_ERR_EMPTY; + return c_StringBuffer_RemoveAt(self, self->size-1, 1); +} + +c_err_t c_StringBuffer_AppendChar(c_StringBuffer_t* self, char ch) { + if (!self || !self->buffer) return C_ERR_PARAM; + return c_StringBuffer_Append(self, (char*)&ch, 1); +} + 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)); diff --git a/Foundation/c_StringBuffer.h b/Foundation/c_StringBuffer.h index 24622a9..01b4803 100644 --- a/Foundation/c_StringBuffer.h +++ b/Foundation/c_StringBuffer.h @@ -60,6 +60,7 @@ c_err_t c_StringBuffer_RemoveAt(c_StringBuffer_t* self, c_size_t index, c_size_t void c_StringBuffer_Clear(c_StringBuffer_t* self); +c_err_t c_StringBuffer_AppendChar(c_StringBuffer_t* self, char ch); 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); @@ -135,4 +136,7 @@ c_err_t c_StringBuffer_strtoul(const c_StringBuffer_t* self, c_size_t start_inde */ c_err_t c_StringBuffer_SetLength(c_StringBuffer_t* sb, c_size_t new_length); + +c_err_t c_StringBuffer_PopBack(c_StringBuffer_t* self); + #endif /*INCLUDED_C_STRINGBUFFER_H*/ diff --git a/Foundation/c_StringList.c b/Foundation/c_StringList.c index c61ed29..62fccee 100644 --- a/Foundation/c_StringList.c +++ b/Foundation/c_StringList.c @@ -1,6 +1,6 @@ #include -c_err_t c_StringList_Init(c_StringList* self, c_size_t capacity, c_Allocator_t* allocator) { +c_err_t c_StringList_Init(c_StringList_t* self, c_size_t capacity, c_Allocator_t* allocator) { if (!self) return C_ERR_PARAM; self->allocator = (allocator != NULL) ? *allocator : c_DefaultAllocator; @@ -19,7 +19,7 @@ c_err_t c_StringList_Init(c_StringList* self, c_size_t capacity, c_Allocator_t* return C_ERR_OK; } -void c_StringList_Clear(c_StringList* self) { +void c_StringList_Clear(c_StringList_t* self) { if (!self || !self->strings) return; // 【核心深清算】:必须先顺着槽位将当前持有的每一个独占字符串物理火化,退还给内置分配器 @@ -32,7 +32,7 @@ void c_StringList_Clear(c_StringList* self) { self->size = 0; } -void c_StringList_Destroy(c_StringList* self) { +void c_StringList_Destroy(c_StringList_t* self) { if (!self) return; if (self->strings) { @@ -48,7 +48,7 @@ void c_StringList_Destroy(c_StringList* self) { * @note 强异常安全性:若分配器因碎片满或爆仓返回 NULL,原有数据指针及老矩阵原样完整留存,绝不发生物理跑飞 */ C_STATIC_FORCE_INLINE -bool c_StringList_EnsureCapacity(c_StringList* self) { +bool c_StringList_EnsureCapacity(c_StringList_t* self) { if (self->size < self->capacity) return true; c_size_t old_bytes = self->capacity * sizeof(char*); @@ -73,7 +73,7 @@ bool c_StringList_EnsureCapacity(c_StringList* self) { // 2. 核心深度值复制增删控制操作 API // ================================================================================================================== -c_err_t c_StringList_InsertAt(c_StringList* self, c_size_t index, const char* str) { +c_err_t c_StringList_InsertAt(c_StringList_t* self, c_size_t index, const char* str) { if (!self || !str || index > self->size) return C_ERR_PARAM; if (!c_StringList_EnsureCapacity(self)) return C_ERR_NOMEM; @@ -93,11 +93,11 @@ c_err_t c_StringList_InsertAt(c_StringList* self, c_size_t index, const char* st return C_ERR_OK; } -c_err_t c_StringList_Add(c_StringList* self, const char* str) { +c_err_t c_StringList_Add(c_StringList_t* self, const char* str) { return c_StringList_InsertAt(self, self->size, str); // 末尾追加 } -c_err_t c_StringList_RemoveAt(c_StringList* self, c_size_t index) { +c_err_t c_StringList_RemoveAt(c_StringList_t* self, c_size_t index) { if (!self || index >= self->size || !self->strings) return C_ERR_PARAM; // ① 定点火化销毁当前槽位持有的独占堆字符串,归还空间 @@ -118,7 +118,7 @@ c_err_t c_StringList_RemoveAt(c_StringList* self, c_size_t index) { // 3. 高级功能:链表深克隆与高速去重 // ================================================================================================================== -c_err_t c_StringList_Clone(c_StringList* dest, const c_StringList* src) { +c_err_t c_StringList_Clone(c_StringList_t* dest, const c_StringList_t* src) { if (!dest || !src || dest == src || !src->strings) return C_ERR_PARAM; // 清算 dest 先前绑定的生命线 @@ -139,7 +139,7 @@ c_err_t c_StringList_Clone(c_StringList* dest, const c_StringList* src) { return C_ERR_OK; } -c_err_t c_StringList_Deduplicate(c_StringList* self) { +c_err_t c_StringList_Deduplicate(c_StringList_t* self) { if (!self || self->size <= 1 || !self->strings) return C_ERR_OK; // O(N^2) 经典原位原地重排去重流 diff --git a/Foundation/c_StringList.h b/Foundation/c_StringList.h index 5bf1c06..83ffc82 100644 --- a/Foundation/c_StringList.h +++ b/Foundation/c_StringList.h @@ -18,32 +18,32 @@ typedef struct { c_size_t size; // Current active string rows stored c_size_t capacity; // Max allocated capacity bounds of the internal pointer matrix c_Allocator_t allocator; -} c_StringList; +} c_StringList_t; /* ------------------------------------------------------------------------------------------------------------------ */ /* */ -c_err_t c_StringList_Init(c_StringList* self, c_size_t capacity, c_Allocator_t* allocator); -void c_StringList_Destroy(c_StringList* self); +c_err_t c_StringList_Init(c_StringList_t* self, c_size_t capacity, c_Allocator_t* allocator); +void c_StringList_Destroy(c_StringList_t* self); // 核心操作 API (安全深拷贝值复制模式) -c_err_t c_StringList_Add(c_StringList* self, const char* str); -c_err_t c_StringList_InsertAt(c_StringList* self, c_size_t index, const char* str); -c_err_t c_StringList_RemoveAt(c_StringList* self, c_size_t index); -void c_StringList_Clear(c_StringList* self); +c_err_t c_StringList_Add(c_StringList_t* self, const char* str); +c_err_t c_StringList_InsertAt(c_StringList_t* self, c_size_t index, const char* str); +c_err_t c_StringList_RemoveAt(c_StringList_t* self, c_size_t index); +void c_StringList_Clear(c_StringList_t* self); // 高级功能接口 -c_err_t c_StringList_Clone(c_StringList* dest, const c_StringList* src); -c_err_t c_StringList_Deduplicate(c_StringList* self); +c_err_t c_StringList_Clone(c_StringList_t* dest, const c_StringList_t* src); +c_err_t c_StringList_Deduplicate(c_StringList_t* self); // 极致高频内联只读窥探接口 C_STATIC_FORCE_INLINE -c_size_t c_StringList_Size(const c_StringList* self) { +c_size_t c_StringList_Size(const c_StringList_t* self) { return self ? self->size : 0; // O(1) 实时读取 } C_STATIC_FORCE_INLINE -const char* c_StringList_Get(const c_StringList* self, c_size_t index) { +const char* c_StringList_Get(const c_StringList_t* self, c_size_t index) { if (!self || index >= self->size || !self->strings) return NULL; return self->strings[index]; // 零拷贝原位返回 } diff --git a/Foundation/c_StringList.t.c b/Foundation/c_StringList.t.c index 66a9c81..6c4e03d 100644 --- a/Foundation/c_StringList.t.c +++ b/Foundation/c_StringList.t.c @@ -28,7 +28,7 @@ TEST_CASE(test_string_list_polymorphic_deep_copy_closure) { .ud = NULL }; - c_StringList list; + c_StringList_t list; // 初始化容量为 2 的特化字符串链表,并注入分配器 ASSERT_INT_EQ(C_ERR_OK, c_StringList_Init(&list, 2, &my_pool)); @@ -76,7 +76,7 @@ TEST_CASE(test_string_list_polymorphic_deep_copy_closure) { ASSERT_INT_EQ(3, (int)g_list_pool_active_chunks); // 重复行的物理资源被内置分配器在底层连根拔起 // 7. 验证克隆体深拷贝承袭能力 (Clone) - c_StringList clone_list; + c_StringList_t clone_list; c_StringList_Init(&clone_list, 1, &my_pool); // 给个小容量 1 ASSERT_INT_EQ(C_ERR_OK, c_StringList_Clone(&clone_list, &list)); diff --git a/String/c_AmericanFlag.c b/String/c_AmericanFlag.c new file mode 100644 index 0000000..a28004b --- /dev/null +++ b/String/c_AmericanFlag.c @@ -0,0 +1,311 @@ +#include +#include + +#define COMPONENT_AFS_CUTOFF 15 + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +/** + * @brief 内部原子物理接口:泛型就地连续内存块高效对调 + */ +C_STATIC_FORCE_INLINE +void c_AFS_InternalSwap(void* a, void* b, c_size_t size) { + if (a == b) return; + char* p1 = (char*)a; + char* p2 = (char*)b; + char temp_buf[256]; + c_size_t bytes_left = size; + while (bytes_left > 0) { + c_size_t chunk = (bytes_left < sizeof(temp_buf)) ? bytes_left : sizeof(temp_buf); + memcpy(&temp_buf, p1, chunk); + memcpy(p1, p2, chunk); + memcpy(p2, &temp_buf, chunk); + p1 += chunk; + p2 += chunk; + bytes_left -= chunk; + } +} + +/** + * @brief 带起始偏移量 d 的全景多级深度级联字典序比对器 + */ +C_STATIC_FORCE_INLINE +int c_AFS_CascadedCompare(const void* a, const void* b, c_size_t start_d, c_AFS_ExtractorFn extractor, void* args) { + c_size_t cur_d = start_d; + while (1) { + int char_a = extractor(a, cur_d, args); + int char_b = extractor(b, cur_d, args); + if (char_a == char_b) { + if (char_a == -1) return 0; + cur_d++; + continue; + } + return char_a - char_b; + } +} + +/** + * @brief 降级优化专线:泛型后缀级联插入排序 + */ +static void c_AFS_InsertionSort(char* base, c_size_t low, c_size_t high, c_size_t elem_size, c_AFS_ExtractorFn extractor, c_size_t d, void* args) { + for (c_size_t i = low + 1; i <= high; i++) { + c_size_t j = i; + char* item_i = base + (i * elem_size); + char v_buf[elem_size]; + memcpy(v_buf, item_i, elem_size); + + while (j > low) { + char* previous = base + ((j - 1) * elem_size); + char* current = base + (j * elem_size); + if (c_AFS_CascadedCompare(v_buf, previous, d, extractor, args) < 0) { + memcpy(current, previous, elem_size); + j--; + } else { + break; + } + } + memcpy(base + (j * elem_size), v_buf, elem_size); + } +} + +/** + * @brief 美国国旗排序核心递归分治控制状态机(完全体就地环置换版) + */ +static void c_AmericanFlagSort_Recursive(char* base, c_size_t low, c_size_t high, c_size_t elem_size, c_AFS_ExtractorFn extractor, c_size_t d, void* args) { + if (high <= low || high == (c_size_t)-1) { + return; + } + + if (high - low < COMPONENT_AFS_CUTOFF) { + c_AFS_InsertionSort(base, low, high, elem_size, extractor, d, args); + return; + } + + // 🌟【规范统一字符表】:终止符 -1 映射到桶 0,普通字节 0~255 映射到桶 1~256,总上限 257 + #define C_AFS_R 257 + c_size_t count[C_AFS_R]; + c_size_t head[C_AFS_R]; + c_size_t end[C_AFS_R]; // 🌟【右边界加固锁】:死死锁存每个桶的绝对开区间截止端点 + + memset(count, 0, sizeof(count)); + + // 步骤 1:频率扫描计数 + for (c_size_t i = low; i <= high; i++) { + int c = extractor(base + (i * elem_size), d, args); + count[c + 1]++; + } + + // 步骤 2:精准增量式计算每个桶在连续内存 [low, high] 中的物理起始与截止配额 + c_size_t current_offset = low; + for (int r = 0; r < C_AFS_R; r++) { + head[r] = current_offset; + current_offset += count[r]; + end[r] = current_offset; + } + + // 克隆一份绝对不会被置换扭转污染的初始边界快照 sub_bounds,专门供后续级联多路分治递归定位安全区 + c_size_t sub_bounds[C_AFS_R]; + memcpy(sub_bounds, head, sizeof(head)); + + // 步骤 3:🌟🌟🌟【美国国旗就地多路环置换绝对内核】🌟🌟🌟 + for (int r = 0; r < C_AFS_R; r++) { + while (head[r] < end[r]) { + // 每次进入循环体,必须动态基于当前的 head[r] 最新游标地址执行一维寻址定位 + char* curr_elem = base + (head[r] * elem_size); + + // 🌟【重新特征提取联锁】:动态计算出换进来的新元素的正确目标桶 + int c = extractor(curr_elem, d, args); + int c_slot = c + 1; + + // 情况 A:元素本就属于当前正在梳理的桶 r,无需置换,当前桶游标单调步进滑过 + if (c_slot == r) { + head[r]++; + } + // 情况 B:属于外部其他桶,且那个目标桶目前尚未满员(head < end 锁有效拦截) + else if (head[c_slot] < end[c_slot]) { + c_size_t dest_pos = head[c_slot]; + char* target_elem = base + (dest_pos * elem_size); + + // 执行就地泛型物理互换。当前 head[r] 游标指针在原地坚守守候! + // 在下一轮 while 循环中,由于前面的重提取联锁,会立刻对被对调过来的数据执行哈希哈希特征重提取 + c_AFS_InternalSwap(curr_elem, target_elem, elem_size); + head[c_slot]++; + } + // 情况 C:属于外部其他桶,但目标桶在前面已被前置填满(head >= end) + else { + // 说明由于大规模随机碰撞分配它不得不中途暂存,直接向前强行合拢,滑过处理 + head[r]++; + } + } + } + + // 步骤 4:级联多路分治深层递归(桶 0 对应的终止符 -1 属于完结节点,果断跳过不递归) + for (int r = 1; r < C_AFS_R; r++) { + c_size_t next_low = sub_bounds[r]; + c_size_t next_high = end[r] - 1; + + if (next_high > next_low && next_high != (c_size_t)-1) { + c_AmericanFlagSort_Recursive(base, next_low, next_high, elem_size, extractor, d + 1, args); + } + } + #undef C_AFS_R +} + +/** + * @brief 工业级纯就地、零动态堆内存损耗泛型美国国旗排序标准对外总线入口 + */ +c_err_t c_AmericanFlag_Sort(void* base, c_size_t num, c_size_t elem_size, c_AFS_ExtractorFn extractor, void* args) { + if (!base || elem_size == 0 || !extractor) { + return C_ERR_PARAM; + } + if (num < 2) { + return C_ERR_OK; // 零体安全放行 + } + + char* array_base = (char*)base; + c_AmericanFlagSort_Recursive(array_base, 0, num - 1, elem_size, extractor, 0, args); + return C_ERR_OK; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// 显式转储栈的最大深度上限(足以承载 1024 层深度的字符串级联下探,物理隔离栈溢出) +#define COMPONENT_NAFS_STACK_MAX 1024 + +typedef struct { + c_size_t low; // 当前待处理区间的左闭端点物理下标 + c_size_t high; // 当前待处理区间的右闭端点物理下标 + c_size_t d; // 当前执行分桶扫描的第 d 个无符号字节游标位置 +} c_NAFS_Frame_t; + +c_err_t c_AmericanFlag_NonRecSort(void* base, c_size_t num, c_size_t elem_size, c_AFS_ExtractorFn extractor, void* args, c_Allocator_t* allocator) { + if (!base || elem_size == 0 || !extractor) { + return C_ERR_PARAM; + } + if (num < 2) { + return C_ERR_OK; // 零体安全放行 + } + + char* array_base = (char*)base; + + // 🌟【非递归核心防线】:在栈空间建立显式帧管理矩阵,将弹跳开销控制在确定常量内 + c_ArrayStack_t stack; + c_ArrayStack_Init(&stack, sizeof(c_NAFS_Frame_t), 1024, allocator); + // c_NAFS_Frame_t stack[COMPONENT_NAFS_STACK_MAX]; + // c_size_t stack_size = 0; + + // 初始首帧压栈:区间为 [0, num-1],高位起始字节 d = 0 + // stack[stack_size].low = 0; + // stack[stack_size].high = num - 1; + // stack[stack_size].d = 0; + // stack_size++; + + c_NAFS_Frame_t frame; + frame.low = 0; + frame.high = num - 1; + frame.d = 0; + c_ArrayStack_Push(&stack, &frame); + + #define C_AFS_R 257 + c_size_t count[C_AFS_R]; + c_size_t head[C_AFS_R]; + c_size_t end[C_AFS_R]; + c_size_t sub_bounds[C_AFS_R]; + + + // 启动非递归主迭代滑窗 + while (!c_ArrayStack_IsEmpty(&stack)) { + // 出栈当前待处理的帧载体 + // stack_size--; + // c_size_t low = stack[stack_size].low; + // c_size_t high = stack[stack_size].high; + // c_size_t d = stack[stack_size].d; + + c_ArrayStack_Pop(&stack, &frame); + c_size_t low = frame.low; + c_size_t high = frame.high; + c_size_t d = frame.d; + + // 极限拦截 + if (high <= low || high == (c_size_t)-1) { + continue; + } + + // 优化点 1:小区间降级截断,直接走快速就地级联插入专线 + if (high - low < COMPONENT_AFS_CUTOFF) { + c_AFS_InsertionSort(array_base, low, high, elem_size, extractor, d, args); + continue; + } + + memset(count, 0, sizeof(count)); + + // 1. 频率扫描计数 + for (c_size_t i = low; i <= high; i++) { + int c = extractor(array_base + (i * elem_size), d, args); + count[c + 1]++; + } + + // 2. 精确增量式计算每个桶在当前闭区间内绝对物理起始与截止配额 + c_size_t current_offset = low; + for (int r = 0; r < C_AFS_R; r++) { + head[r] = current_offset; + current_offset += count[r]; + end[r] = current_offset; + } + + // 锁存绝对不变的初始边界快照 + memcpy(sub_bounds, head, sizeof(head)); + + // 3. 🌟🌟🌟【环置换无损对调核心状态机(Dynamic Remap Lock)】🌟🌟🌟 + for (int r = 0; r < C_AFS_R; r++) { + while (head[r] < end[r]) { + char* curr_elem = array_base + (head[r] * elem_size); + int c = extractor(curr_elem, d, args); + int c_slot = c + 1; + + if (c_slot == r) { + head[r]++; + } + else if (head[c_slot] < end[c_slot]) { + c_size_t dest_pos = head[c_slot]; + char* target_elem = array_base + (dest_pos * elem_size); + c_AFS_InternalSwap(curr_elem, target_elem, elem_size); + head[c_slot]++; + } + else { + head[r]++; + } + } + } + + // 4. 🌟【迭代变轨核心】:将后续的多路深度分治区间逆向压入显式栈中 + // 注意:桶 0 对应的终止符 -1 属于完结节点,彻底剔除不入栈 + for (int r = C_AFS_R - 1; r >= 1; r--) { + c_size_t next_low = sub_bounds[r]; + c_size_t next_high = end[r] - 1; + + if (next_high > next_low && next_high != (c_size_t)-1) { + // 状态检查:防止极限特种输入冲破显式栈容量 + // if (stack_size < COMPONENT_NAFS_STACK_MAX) { + // stack[stack_size].low = next_low; + // stack[stack_size].high = next_high; + // stack[stack_size].d = d + 1; // 字节游标单调向右推 + // stack_size++; + // } + frame.low = next_low; + frame.high = next_high; + frame.d = d+1; + c_ArrayStack_Push(&stack, &frame); + } + } + + } + + #undef C_AFS_R + c_ArrayStack_Destroy(&stack); + return C_ERR_OK; +} \ No newline at end of file diff --git a/String/c_AmericanFlag.h b/String/c_AmericanFlag.h new file mode 100644 index 0000000..1cef914 --- /dev/null +++ b/String/c_AmericanFlag.h @@ -0,0 +1,24 @@ +#ifndef INCLUDED_C_AMERICANFLAG_H +#define INCLUDED_C_AMERICANFLAG_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_ALLOCATOR_H +#include +#endif /*INCLUDED_C_ALLOCATOR_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef int (*c_AFS_ExtractorFn)(const void* elem, c_size_t d, void* args); + +c_err_t c_AmericanFlag_Sort(void* base, c_size_t num, c_size_t elem_size, c_AFS_ExtractorFn extractor, void* args); + + +c_err_t c_AmericanFlag_NonRecSort(void* base, c_size_t num, c_size_t elem_size, + c_AFS_ExtractorFn extractor, void* args, c_Allocator_t* allocator); + + +#endif /*INCLUDED_C_AMERICANFLAG_H*/ diff --git a/String/c_AmericanFlag.t.c b/String/c_AmericanFlag.t.c new file mode 100644 index 0000000..2b172e0 --- /dev/null +++ b/String/c_AmericanFlag.t.c @@ -0,0 +1,162 @@ +#include "c_AmericanFlag.h" +#include "c_Test.h" +#include +#include + +static int afs_string_extractor(const void* elem, c_size_t d, void* args) { + (void)args; + const char* str = *(const char* const*)elem; // 二级指针穿透锁字符 + c_size_t len = strlen(str); + if (d >= len) return -1; // 变长截止符 + return (int)((unsigned char)str[d]); +} + +TEST_CASE(test_c_AmericanFlagSort_大容量就地环置换压测) { + // 构造一个包含 18 个元素、大量前缀交织("shor", "shore", "short", "sh")的变长恶劣方阵 + const char* arr[] = { + "she", "sells", "seashells", "by", "the", + "sea", "shore", "the", "shells", "she", + "sea", "shore", "sit", "short", "shor", "a", + "sh", "z" + }; + c_size_t num = sizeof(arr) / sizeof(arr[0]); + + // 调用纯就地 MSD 排序,零动态辅助缓冲分配 + c_err_t err = c_AmericanFlag_Sort(arr, num, sizeof(char*), afs_string_extractor, NULL); + ASSERT_INT_EQ(C_ERR_OK, err); + + // 验证严格的全局字典序单调非减 + for (c_size_t i = 0; i < num - 1; i++) { + if (strcmp(arr[i], arr[i + 1]) > 0) { + printf(" " COLOR_RED "[FAIL] 就地置换错位捕获: arr[%d]='%s' 排在 arr[%d]='%s' 的前面!" COLOR_RESET "\n", + (int)i, arr[i], (int)(i + 1), arr[i + 1]); + ASSERT_TRUE(false); + return; + } + } + + // 校验极短终止符和极长串的拓扑归位状态 + ASSERT_TRUE(strcmp("a", arr[0]) == 0); + ASSERT_TRUE(strcmp("by", arr[1]) == 0); + ASSERT_TRUE(strcmp("sea", arr[2]) == 0); // 极短终止符前缀必须完美垫底最前端 + ASSERT_TRUE(strcmp("shells", arr[9]) == 0); // 极短终止符前缀必须完美垫底最前端 + ASSERT_TRUE(strcmp("shor", arr[10]) == 0); + ASSERT_TRUE(strcmp("shore", arr[11]) == 0); + ASSERT_TRUE(strcmp("z", arr[num - 1]) == 0); // 尾部大收拢正确 +} + +TEST_CASE(test_c_AmericanFlagSort_小样本Cutoff插入专线压测) { + // 🌟 故意精简至 10 个数据(小于阈值 15),且打乱顺序、包含相同前缀和变长终止符 + const char* arr_short[] = { + "shore", "she", "sells", "sea", "sh", + "shor", "sit", "short", "a", "sea" + }; + c_size_t num = sizeof(arr_short) / sizeof(arr_short[0]); + + // 调用入口:在第 0 字节探测时就因为总数 10 < 15 爆发降级,直接由 c_IP_MSD_InsertionSort 接管 + c_err_t err = c_AmericanFlag_Sort(arr_short, num, sizeof(char*), afs_string_extractor, NULL); + ASSERT_INT_EQ(C_ERR_OK, err); + + // 验证降级插入排序后的全局字典序是否完全单调非减 + for (c_size_t i = 0; i < num - 1; i++) { + if (strcmp(arr_short[i], arr_short[i + 1]) > 0) { + printf(" " COLOR_RED "[FAIL] 降级插入排序错位: arr_short[%d]='%s' 居然排在 arr_short[%d]='%s' 前面!" COLOR_RESET "\n", + (int)i, arr_short[i], (int)(i + 1), arr_short[i + 1]); + ASSERT_TRUE(false); + return; + } + } + + // 验证级联插入排序对短前缀终止符与后缀字典序的排序正确性 + ASSERT_TRUE(strcmp("a", arr_short[0]) == 0); + ASSERT_TRUE(strcmp("sea", arr_short[1]) == 0); + ASSERT_TRUE(strcmp("sea", arr_short[2]) == 0); // 稳定排序项紧凑排布 + ASSERT_TRUE(strcmp("sh", arr_short[4]) == 0); // 短前缀完美垫底 + ASSERT_TRUE(strcmp("shor", arr_short[6]) == 0); + ASSERT_TRUE(strcmp("shore", arr_short[7]) == 0); + ASSERT_TRUE(strcmp("short", arr_short[8]) == 0); + ASSERT_TRUE(strcmp("sit", arr_short[num-1]) == 0); +} + +TEST_CASE(test_c_AmericanFlagSort_NonRec_大容量显式置换) { + // 构造一个包含 18 个元素、大量前缀交织("shor", "shore", "short", "sh")的变长恶劣方阵 + const char* arr[] = { + "she", "sells", "seashells", "by", "the", + "sea", "shore", "the", "shells", "she", + "sea", "shore", "sit", "short", "shor", "a", + "sh", "z" + }; + c_size_t num = sizeof(arr) / sizeof(arr[0]); + + // 调用纯就地 MSD 排序,零动态辅助缓冲分配 + c_err_t err = c_AmericanFlag_NonRecSort(arr, num, sizeof(char*), afs_string_extractor, NULL, 0); + ASSERT_INT_EQ(C_ERR_OK, err); + + // 验证严格的全局字典序单调非减 + for (c_size_t i = 0; i < num - 1; i++) { + if (strcmp(arr[i], arr[i + 1]) > 0) { + printf(" " COLOR_RED "[FAIL] 就地置换错位捕获: arr[%d]='%s' 排在 arr[%d]='%s' 的前面!" COLOR_RESET "\n", + (int)i, arr[i], (int)(i + 1), arr[i + 1]); + ASSERT_TRUE(false); + return; + } + } + + // 校验极短终止符和极长串的拓扑归位状态 + ASSERT_TRUE(strcmp("a", arr[0]) == 0); + ASSERT_TRUE(strcmp("by", arr[1]) == 0); + ASSERT_TRUE(strcmp("sea", arr[2]) == 0); // 极短终止符前缀必须完美垫底最前端 + ASSERT_TRUE(strcmp("shells", arr[9]) == 0); // 极短终止符前缀必须完美垫底最前端 + ASSERT_TRUE(strcmp("shor", arr[10]) == 0); + ASSERT_TRUE(strcmp("shore", arr[11]) == 0); + ASSERT_TRUE(strcmp("z", arr[num - 1]) == 0); // 尾部大收拢正确 +} + +TEST_CASE(test_c_AmericanFlagSort_NonRec_小样本插入排序) { + // 🌟 故意精简至 10 个数据(小于阈值 15),且打乱顺序、包含相同前缀和变长终止符 + const char* arr_short[] = { + "shore", "she", "sells", "sea", "sh", + "shor", "sit", "short", "a", "sea" + }; + c_size_t num = sizeof(arr_short) / sizeof(arr_short[0]); + + // 调用入口:在第 0 字节探测时就因为总数 10 < 15 爆发降级,直接由 c_IP_MSD_InsertionSort 接管 + c_err_t err = c_AmericanFlag_NonRecSort(arr_short, num, sizeof(char*), afs_string_extractor, NULL, 0); + ASSERT_INT_EQ(C_ERR_OK, err); + + // 验证降级插入排序后的全局字典序是否完全单调非减 + for (c_size_t i = 0; i < num - 1; i++) { + if (strcmp(arr_short[i], arr_short[i + 1]) > 0) { + printf(" " COLOR_RED "[FAIL] 降级插入排序错位: arr_short[%d]='%s' 居然排在 arr_short[%d]='%s' 前面!" COLOR_RESET "\n", + (int)i, arr_short[i], (int)(i + 1), arr_short[i + 1]); + ASSERT_TRUE(false); + return; + } + } + + // 验证级联插入排序对短前缀终止符与后缀字典序的排序正确性 + ASSERT_TRUE(strcmp("a", arr_short[0]) == 0); + ASSERT_TRUE(strcmp("sea", arr_short[1]) == 0); + ASSERT_TRUE(strcmp("sea", arr_short[2]) == 0); // 稳定排序项紧凑排布 + ASSERT_TRUE(strcmp("sh", arr_short[4]) == 0); // 短前缀完美垫底 + ASSERT_TRUE(strcmp("shor", arr_short[6]) == 0); + ASSERT_TRUE(strcmp("shore", arr_short[7]) == 0); + ASSERT_TRUE(strcmp("short", arr_short[8]) == 0); + ASSERT_TRUE(strcmp("sit", arr_short[num-1]) == 0); +} + + +// ========================================== +// 5. 主集成入口 +// ========================================== +int main(void) { + TEST_START(C_AmericanFlagSort_ZeroAux_TestSuite); + RUN_TEST(test_c_AmericanFlagSort_大容量就地环置换压测); + RUN_TEST(test_c_AmericanFlagSort_小样本Cutoff插入专线压测); + + RUN_TEST(test_c_AmericanFlagSort_NonRec_大容量显式置换); + RUN_TEST(test_c_AmericanFlagSort_NonRec_小样本插入排序); + + TEST_REPORT(); + return (g_test_registry.failed_count > 0 ? 1 : 0); +} diff --git a/String/c_BoyerMoore.c b/String/c_BoyerMoore.c new file mode 100644 index 0000000..c4014a5 --- /dev/null +++ b/String/c_BoyerMoore.c @@ -0,0 +1,94 @@ +#include + +C_STATIC_FORCE_INLINE +c_int_t max(c_int_t a, c_int_t b) { + return a > b ? a : b; +} + +c_err_t c_BoyerMoore_Init(c_BoyerMoore_t* self, const char* pattern, c_size_t pattern_size, int R, c_Allocator_t* allocator) { + if (!self || !pattern || pattern_size==0 || R==0) return C_ERR_PARAM; + + // 前置逆向除法算术溢出防御审计 + if (((c_size_t)-1) / sizeof(c_int_t) < (c_size_t)R) return C_ERR_PARAM; + if (((c_size_t)-1) / sizeof(char) < pattern_size) return C_ERR_PARAM; + + self->R = R; + self->allocator = allocator?*allocator:c_DefaultAllocator; + + self->pattern = c_Allocator_Alloc(&self->allocator, sizeof(char) * pattern_size); + self->right = c_Allocator_Alloc(&self->allocator, sizeof(*self->right) * R); + + if (!self->pattern || !self->right) { + if (self->pattern) c_Allocator_Free(&self->allocator, self->pattern); + if (self->right) c_Allocator_Free(&self->allocator, self->right); + return C_ERR_NOMEM; + } + + memcpy(self->pattern, pattern, pattern_size); + self->pattern_size = pattern_size; + + for (int c=0; cright[c] = -1; + } + + // 若在当前模式串内部出现过,则记录其最右侧出现的绝对物理位置下标 + for (c_size_t i = 0; i < pattern_size; i++) { + unsigned char c = (unsigned char)self->pattern[i]; + + // 防御性高位拦截:若模式串内包含超出指定字母表 R 上限的越界字符,安全抛出异常拦截 + if ((int)c >= R) { + c_Allocator_Free(&self->allocator, self->pattern); + c_Allocator_Free(&self->allocator, self->right); + return C_ERR_OUTOFBOUND; + } + self->right[c] = (c_int_t)i; + } + + return C_ERR_OK; +} + +c_err_t c_BoyerMoore_Search(c_BoyerMoore_t* self, const char* text, c_size_t text_size, c_size_t* index) { + if (!self || !text || text_size==0 || !self->pattern || self->pattern_size==0) return C_ERR_PARAM; + + c_size_t m = self->pattern_size; + c_size_t n = text_size; + + // 区间饱和过滤:主文本字节宽度短于子串宽度,绝无命中可能,平滑拦截 + if (n < m) { + return C_ERR_NOTFOUND; + } + + c_size_t skip = 0; + for (c_size_t i=0; i<=n-m; i+=skip) { + skip = 0; + for (c_size_t j=m-1; j!=-1; j--) { + if (self->pattern[j]!=text[i+j]) { + skip = max(1, j-self->right[text[i+j]]); + break; + } + } + if (skip==0) { + if (index) { + *index = i; + return C_ERR_OK; + } + } + } + if (index) { + *index = n; + } + return C_ERR_NOTFOUND; +} + +void c_BoyerMoore_Destroy(c_BoyerMoore_t* self) { + if (!self) return; + if (self->pattern) { + c_Allocator_Free(&self->allocator, self->pattern); + self->pattern = NULL; + self->pattern_size = 0; + } + if (self->right) { + c_Allocator_Free(&self->allocator, self->right); + self->right = NULL; + } +} diff --git a/String/c_BoyerMoore.h b/String/c_BoyerMoore.h new file mode 100644 index 0000000..cdad136 --- /dev/null +++ b/String/c_BoyerMoore.h @@ -0,0 +1,50 @@ +#ifndef INCLUDED_C_BOYERMOORE_H +#define INCLUDED_C_BOYERMOORE_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_BM_R 256 + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +typedef struct { + int R; + c_int_t * right; + char* pattern; + c_size_t pattern_size; + c_Allocator_t allocator; // 统一的内联组合分配器实例 +} c_BoyerMoore_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_BoyerMoore_Init(c_BoyerMoore_t* self, const char* pattern, c_size_t pattern_size, int R, c_Allocator_t* allocator); + +void c_BoyerMoore_Destroy(c_BoyerMoore_t* self); + +c_err_t c_BoyerMoore_Search(c_BoyerMoore_t* self, const char* text, c_size_t text_size, c_size_t* index); + +C_STATIC_FORCE_INLINE +c_err_t c_BoyerMoore_InitStr(c_BoyerMoore_t* self, const char* pattern, c_Allocator_t* allocator) { + if (!self || !pattern) return C_ERR_PARAM; + return c_BoyerMoore_Init(self, pattern, strlen(pattern), 256, allocator); +} + +C_STATIC_FORCE_INLINE +c_err_t c_BoyerMoore_SearchStr(c_BoyerMoore_t* self, const char* text, c_size_t* index) { + if (!self || !text) return C_ERR_PARAM; + return c_BoyerMoore_Search(self, text, strlen(text), index); +} + + +#endif /*INCLUDED_C_BOYERMOORE_H*/ diff --git a/String/c_BoyerMoore.t.c b/String/c_BoyerMoore.t.c new file mode 100644 index 0000000..93f6ee9 --- /dev/null +++ b/String/c_BoyerMoore.t.c @@ -0,0 +1,63 @@ +#include "c_BoyerMoore.h" +#include "c_Test.h" +#include +#include + +TEST_CASE(test_c_BoyerMoore_Full) { + c_BoyerMoore_t bm; + + // 初始化子串模式匹配器,测试默认 Fallback 降级分配器 + c_err_t err = c_BoyerMoore_InitStr(&bm, "EXAMPLE", NULL); + ASSERT_INT_EQ(C_ERR_OK, err); + + // 1. 基础常规命中断言 + const char* text_normal = "HERE IS A SIMPLE EXAMPLE STR"; + c_size_t match_index = 0; + + err = c_BoyerMoore_SearchStr(&bm, text_normal, &match_index); + ASSERT_INT_EQ(C_ERR_OK, err); + ASSERT_INT_EQ(17, (int)match_index); // 精确断言子串在主文本中的绝对起始插槽位置为 17 + + // 2. 🌟【绝杀功能压测】:好后缀与坏字符最大化跨越式跳跃 + // 故意构造一个包含大量单调连续字符冲突、传统 KMP 极易发生高频无用回溯的排毒数据 + c_BoyerMoore_t bm_toxic; + c_BoyerMoore_InitStr(&bm_toxic, "ANPANMAN", &c_DefaultAllocator); + + const char* text_toxic = "ANPANPANPANANPANMAN_CORE_SYS"; // 子串深埋在中后段 + match_index = 0; + + err = c_BoyerMoore_SearchStr(&bm_toxic, text_toxic, &match_index); + ASSERT_INT_EQ(C_ERR_OK, err); + ASSERT_INT_EQ(11, (int)match_index); // 级联大跨度滑窗跳转定位成功! + + // 3. 边界拦截未命中核验 + const char* text_fake = "ANPAN_ANPAN_APN_SYSTEM"; + ASSERT_INT_EQ(C_ERR_NOTFOUND, c_BoyerMoore_SearchStr(&bm_toxic, text_fake, &match_index)); + + c_BoyerMoore_Destroy(&bm); + c_BoyerMoore_Destroy(&bm_toxic); +} + +TEST_CASE(test_c_BoyerMoore_Toxicity_Defenses) { + c_BoyerMoore_t local_bm; + c_BoyerMoore_InitStr(&local_bm, "A", NULL); + + c_size_t idx = 0; + // 4. 验证各类参数非法边界强拦截 + ASSERT_INT_EQ(C_ERR_PARAM, c_BoyerMoore_InitStr(NULL, NULL, NULL)); + ASSERT_INT_EQ(C_ERR_PARAM, c_BoyerMoore_SearchStr(NULL, "text", &idx)); + ASSERT_INT_EQ(C_ERR_PARAM, c_BoyerMoore_SearchStr(&local_bm, NULL, &idx)); + + c_BoyerMoore_Destroy(&local_bm); +} + +// ========================================== +// 5. 主集成入口 +// ========================================== +int main(void) { + TEST_START(C_BoyerMoore_HeuristicSearch_TestSuite); + RUN_TEST(test_c_BoyerMoore_Full); + RUN_TEST(test_c_BoyerMoore_Toxicity_Defenses); + TEST_REPORT(); + return (g_test_registry.failed_count > 0 ? 1 : 0); +} diff --git a/String/c_InplaceMSD.c b/String/c_InplaceMSD.c new file mode 100644 index 0000000..756f4b6 --- /dev/null +++ b/String/c_InplaceMSD.c @@ -0,0 +1,158 @@ +#include + +#define COMPONENT_IP_MSD_CUTOFF 15 + +/** + * @brief 内部原子物理接口:泛型就地连续内存块对调 + */ +C_STATIC_FORCE_INLINE +void c_IP_MSD_InternalSwap(void* a, void* b, c_size_t size) { + if (a == b) return; + char* p1 = (char*)a; + char* p2 = (char*)b; + char temp_buf[256]; + c_size_t bytes_left = size; + while (bytes_left > 0) { + c_size_t chunk = (bytes_left < sizeof(temp_buf)) ? bytes_left : sizeof(temp_buf); + memcpy(temp_buf, p1, chunk); + memcpy(p1, p2, chunk); + memcpy(p2, temp_buf, chunk); + p1 += chunk; + p2 += chunk; + bytes_left -= chunk; + } +} + +/** + * @brief 带起始偏移量 d 的全景多级深度级联字典序比对器 + */ +C_STATIC_FORCE_INLINE +int c_IP_MSD_CascadedCompare(const void* a, const void* b, c_size_t start_d, c_MSD_ExtractorFn extractor, void* args) { + c_size_t cur_d = start_d; + while (1) { + int char_a = extractor(a, cur_d, args); + int char_b = extractor(b, cur_d, args); + if (char_a == char_b) { + if (char_a == -1) return 0; // 双方完全全等至截止符 + cur_d++; + continue; + } + return char_a - char_b; // 终止符 -1 天然小于普通字节 + } +} + +/** + * @brief 局部自适应优化:泛型后缀级联插入排序 + */ +static void c_IP_MSD_InsertionSort(char* base, c_size_t low, c_size_t high, c_size_t elem_size, c_MSD_ExtractorFn extractor, c_size_t d, void* args) { + for (c_size_t i = low + 1; i <= high; i++) { + c_size_t j = i; + char* item_i = base + (i * elem_size); + char v_buf[elem_size]; + memcpy(v_buf, item_i, elem_size); + + while (j > low) { + char* previous = base + ((j - 1) * elem_size); + char* current = base + (j * elem_size); + if (c_IP_MSD_CascadedCompare(v_buf, previous, d, extractor, args) < 0) { + memcpy(current, previous, elem_size); + j--; + } else { + break; + } + } + memcpy(base + (j * elem_size), v_buf, elem_size); + } +} + +/** + * @brief 重新设计的就地 MSD 基数排序递归控制状态机(完全隔离式 American Flag) + */ +static void c_InPlace_MSD_Recursive(char* base, c_size_t low, c_size_t high, c_size_t elem_size, c_MSD_ExtractorFn extractor, c_size_t d, void* args) { + if (high <= low || high == (c_size_t)-1) { + return; + } + + if (high - low < COMPONENT_IP_MSD_CUTOFF) { + c_IP_MSD_InsertionSort(base, low, high, elem_size, extractor, d, args); + return; + } + + #define C_IP_R 257 + c_size_t count[C_IP_R]; + c_size_t head[C_IP_R]; + c_size_t end[C_IP_R]; + + memset(count, 0, sizeof(count)); + + // 步骤 1:频率计算(终止符 -1 映射至桶 0,普通字节 0~255 映射至桶 1~256) + for (c_size_t i = low; i <= high; i++) { + int c = extractor(base + (i * elem_size), d, args); + count[c + 1]++; + } + + // 步骤 2:🌟【编译加固核心】:摒弃错误的数组名直接代数赋值,采用符合 C 语法的增量对齐 + c_size_t current_offset = low; + for (int r = 0; r < C_IP_R; r++) { + head[r] = current_offset; + current_offset += count[r]; + end[r] = current_offset; // 精确卡死当前桶的物理右开边界 + } + + // 独立克隆一份绝对不变的初始边界快照,专门供后面分治变轨定位 + c_size_t sub_bounds[C_IP_R]; + memcpy(sub_bounds, head, sizeof(head)); + + // 步骤 3:环置换无损对调状态机 + for (int r = 0; r < C_IP_R; r++) { + while (head[r] < end[r]) { + char* curr_elem = base + (head[r] * elem_size); + + // 动态重新提取当前插槽换进来的新内容特征,消除残留 + int c = extractor(curr_elem, d, args); + int c_slot = c + 1; + + if (c_slot == r) { + head[r]++; // 合规归位,当前桶游标单调步进 + } + else if (head[c_slot] < end[c_slot]) { + c_size_t dest_pos = head[c_slot]; + char* target_elem = base + (dest_pos * elem_size); + + // 置换指针,且当前 head[r] 指针原地坚守,下一轮循环自动对其重审讯 + c_IP_MSD_InternalSwap(curr_elem, target_elem, elem_size); + head[c_slot]++; + } + else { + head[r]++; // 外部桶已满,强行作为游离多余项滑过 + } + } + } + + // 步骤 4:级联多路分治递归(桶 0 对应的终止符 -1 区间已经完全完结,彻底跳过不递归) + for (int r = 1; r < C_IP_R; r++) { + c_size_t next_low = sub_bounds[r]; + c_size_t next_high = end[r] - 1; + + if (next_high > next_low && next_high != (c_size_t)-1) { + c_InPlace_MSD_Recursive(base, next_low, next_high, elem_size, extractor, d + 1, args); + } + } + #undef C_IP_R +} + +/** + * @brief 工业级纯就地、零堆额外损耗泛型 MSD 基数排序统一外部入口 + */ +c_err_t c_InPlaceMSD_RadixSort(void* base, c_size_t num, c_size_t elem_size, c_MSD_ExtractorFn extractor, void* args) { + if (!base || elem_size == 0 || !extractor) { + return C_ERR_PARAM; + } + if (num < 2) { + return C_ERR_OK; // 零体安全放行 + } + + char* array_base = (char*)base; + c_InPlace_MSD_Recursive(array_base, 0, num - 1, elem_size, extractor, 0, args); + return C_ERR_OK; +} diff --git a/String/c_InplaceMSD.h b/String/c_InplaceMSD.h new file mode 100644 index 0000000..5811dac --- /dev/null +++ b/String/c_InplaceMSD.h @@ -0,0 +1,17 @@ +#ifndef INCLUDED_C_INPLACEMSD_H +#define INCLUDED_C_INPLACEMSD_H + +#ifndef INCLUDED_C_MSD_H +#include +#endif /*INCLUDED_C_MSD_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + + +c_err_t c_InPlaceMSD_RadixSort(void* base, c_size_t num, c_size_t elem_size, + c_MSD_ExtractorFn extractor, void* args) ; + +#endif /*INCLUDED_C_INPLACEMSD_H*/ diff --git a/String/c_InplaceMSD.t.c b/String/c_InplaceMSD.t.c new file mode 100644 index 0000000..265e03d --- /dev/null +++ b/String/c_InplaceMSD.t.c @@ -0,0 +1,93 @@ +#include "c_InplaceMSD.h" +#include "c_Test.h" + +#include +#include + +static int string_msd_extractor(const void* elem, c_size_t d, void* args) { + (void)args; + const char* str = *(const char* const*)elem; + c_size_t len = strlen(str); + if (d >= len) return -1; + return (int)((unsigned char)str[d]); +} + + +TEST_CASE(test_c_InPlace_MSD_RadixSort_VariableStrings) { + // 构造一个包含 18 个元素、大量前缀交织("shor", "shore", "short", "sh")的变长恶劣方阵 + const char* arr[] = { + "she", "sells", "seashells", "by", "the", + "sea", "shore", "the", "shells", "she", + "sea", "shore", "sit", "short", "shor", "a", + "sh", "z" + }; + c_size_t num = sizeof(arr) / sizeof(arr[0]); + + // 调用纯就地 MSD 排序,零动态辅助缓冲分配 + c_err_t err = c_InPlaceMSD_RadixSort(arr, num, sizeof(char*), string_msd_extractor, NULL); + ASSERT_INT_EQ(C_ERR_OK, err); + + // 验证严格的全局字典序单调非减 + for (c_size_t i = 0; i < num - 1; i++) { + if (strcmp(arr[i], arr[i + 1]) > 0) { + printf(" " COLOR_RED "[FAIL] 就地置换错位捕获: arr[%d]='%s' 排在 arr[%d]='%s' 的前面!" COLOR_RESET "\n", + (int)i, arr[i], (int)(i + 1), arr[i + 1]); + ASSERT_TRUE(false); + return; + } + } + + // 校验极短终止符和极长串的拓扑归位状态 + ASSERT_TRUE(strcmp("a", arr[0]) == 0); + ASSERT_TRUE(strcmp("by", arr[1]) == 0); + ASSERT_TRUE(strcmp("sea", arr[2]) == 0); // 极短终止符前缀必须完美垫底最前端 + ASSERT_TRUE(strcmp("shells", arr[9]) == 0); // 极短终止符前缀必须完美垫底最前端 + ASSERT_TRUE(strcmp("shor", arr[10]) == 0); + ASSERT_TRUE(strcmp("shore", arr[11]) == 0); + ASSERT_TRUE(strcmp("z", arr[num - 1]) == 0); // 尾部大收拢正确 +} + +// ========================================== +// 新增测试:强迫其在顶层就少于15个元素,精准覆盖降级插入排序专线 +// ========================================== +TEST_CASE(test_c_InPlaceMSD_RadixSort_DirectInsertionSortCutoff) { + // 🌟 故意精简至 10 个数据(小于阈值 15),且打乱顺序、包含相同前缀和变长终止符 + const char* arr_short[] = { + "shore", "she", "sells", "sea", "sh", + "shor", "sit", "short", "a", "sea" + }; + c_size_t num = sizeof(arr_short) / sizeof(arr_short[0]); + + // 调用入口:在第 0 字节探测时就因为总数 10 < 15 爆发降级,直接由 c_IP_MSD_InsertionSort 接管 + c_err_t err = c_InPlaceMSD_RadixSort(arr_short, num, sizeof(char*), string_msd_extractor, NULL); + ASSERT_INT_EQ(C_ERR_OK, err); + + // 验证降级插入排序后的全局字典序是否完全单调非减 + for (c_size_t i = 0; i < num - 1; i++) { + if (strcmp(arr_short[i], arr_short[i + 1]) > 0) { + printf(" " COLOR_RED "[FAIL] 降级插入排序错位: arr_short[%d]='%s' 居然排在 arr_short[%d]='%s' 前面!" COLOR_RESET "\n", + (int)i, arr_short[i], (int)(i + 1), arr_short[i + 1]); + ASSERT_TRUE(false); + return; + } + } + + // 验证级联插入排序对短前缀终止符与后缀字典序的排序正确性 + ASSERT_TRUE(strcmp("a", arr_short[0]) == 0); + ASSERT_TRUE(strcmp("sea", arr_short[1]) == 0); + ASSERT_TRUE(strcmp("sea", arr_short[2]) == 0); // 稳定排序项紧凑排布 + ASSERT_TRUE(strcmp("sh", arr_short[4]) == 0); // 短前缀完美垫底 + ASSERT_TRUE(strcmp("shor", arr_short[6]) == 0); + ASSERT_TRUE(strcmp("shore", arr_short[7]) == 0); + ASSERT_TRUE(strcmp("short", arr_short[8]) == 0); + ASSERT_TRUE(strcmp("sit", arr_short[num-1]) == 0); +} + + +int main(void) { + TEST_START(C_InPlace_MSD_RadixSort_TestSuite); + RUN_TEST(test_c_InPlace_MSD_RadixSort_VariableStrings); + RUN_TEST(test_c_InPlaceMSD_RadixSort_DirectInsertionSortCutoff); + TEST_REPORT(); + return (g_test_registry.failed_count > 0 ? 1 : 0); +} \ No newline at end of file diff --git a/String/c_LSD.c b/String/c_LSD.c new file mode 100644 index 0000000..8e3a765 --- /dev/null +++ b/String/c_LSD.c @@ -0,0 +1,78 @@ +#include + + + + +c_err_t c_LSD_RadixSort(void* base, c_size_t num, c_size_t elem_size, c_size_t w_bytes, c_LSD_ExtractorFn extractor, void* args, c_Allocator_t* allocator) { + // 边界拦截与防御性参数过滤 + if (!base || elem_size == 0 || w_bytes == 0 || !extractor) { + return C_ERR_PARAM; + } + if (num < 2) { + return C_ERR_OK; // 零个或单个元素无需排序,平滑退出 + } + + c_Allocator_t local_alloc; + if (allocator) { + local_alloc = *allocator; + } else { + local_alloc = c_DefaultAllocator; + } + + char* array_base = (char*)base; + + // 前置逆向除法整数溢出防御审计:阻止开辟影子辅助缓冲区时可能触发的无符号算术回绕 + if (((c_size_t)-1) / elem_size < num) { + return C_ERR_NOMEM; + } + + // 在堆上利用分配器托管开辟等大的一维扁平影子缓冲区(Auxiliary Array)用于稳定分配搬运 + char* aux = (char*)c_Allocator_Alloc(&local_alloc, num * elem_size); + if (!aux) { + return C_ERR_NOMEM; + } + + // 🌟【LSD 基数核心常数】:一个字节有 8 位,基数桶(R)的物理字母表总上限固定为 256 + #define C_LSD_R 256 + c_size_t count[C_LSD_R + 1]; + + // 从最低字节(d = 0)一路稳定向最高字节(d = w_bytes - 1)单调向前推进 + for (c_size_t d = 0; d < w_bytes; d++) { + + // 步骤 1:清空本轮的计数桶 + memset(count, 0, sizeof(count)); + + // 步骤 2:频率计数(Counting)。调用 extractor 虚函数提取对应的 8 位无符号整型键值 + for (c_size_t i = 0; i < num; i++) { + uint8_t bucket_key = extractor(array_base + (i * elem_size), d, args); + count[bucket_key + 1]++; // 错位计数,为下一步的前缀变换蓄力 + } + + // 步骤 3:前缀累计和转换(Prefix Sums)。将频率转化为影子缓冲区的精确起始物理下标 + for (c_size_t r = 0; r < C_LSD_R; r++) { + count[r + 1] += count[r]; + } + + // 步骤 4:无伤分配搬运(Data Distribution)。将数据深拷贝转储至影子缓冲区 aux 中 + // 严格遵循稳定排序原则,维持原同值键的物理相对顺序不动 + for (c_size_t i = 0; i < num; i++) { + uint8_t bucket_key = extractor(array_base + (i * elem_size), d, args); + + // 获取本元素在影子缓冲中安全分配的槽位索引,并递增计数指针位置 + c_size_t dest_idx = count[bucket_key]++; + + // 严密检查,阻止任何因为多态提取器越界导致的非线性越界踩踏 + if (dest_idx < num) { + memcpy(aux + (dest_idx * elem_size), array_base + (i * elem_size), elem_size); + } + } + + // 步骤 5:回写刷新(Copy Back)。将本轮基于第 d 字节排好序的影子结果完全回写覆盖回原数组 + memcpy(array_base, aux, num * elem_size); + } + + #undef C_LSD_R + // 销毁并原路回收影子缓冲,阻断任何悬空残留 + c_Allocator_Free(&local_alloc, aux); + return C_ERR_OK; +} diff --git a/String/c_LSD.h b/String/c_LSD.h new file mode 100644 index 0000000..518a35e --- /dev/null +++ b/String/c_LSD.h @@ -0,0 +1,42 @@ +#ifndef INCLUDED_C_LSD_H +#define INCLUDED_C_LSD_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_ALLOCATOR_H +#include +#endif /*INCLUDED_C_ALLOCATOR_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * @brief 泛型 LSD 专用字节密钥提取器函数指针 + * + * @param elem 指向当前泛型待提取对象的指针 + * @param d 当前从低向高扫描的第 d 个字节下标位置(0 代表最低位字节,W-1 代表最高位字节) + * @param args 自定义上下文参数指针 + * @return uint8_t 返回该泛型对象在第 d 个字节处的物理 8 位无符号整型键值 [0, 255] + */ +typedef uint8_t (*c_LSD_ExtractorFn)(const void* elem, c_size_t d, void* args); + + +/** + * @brief 工业级泛型低位优先(LSD)基数排序核心接口(分配器内联组合版) + * + * @param base 指向待排序连续数组首元素的指针 + * @param num 数组中元素的总个数 (N) + * @param elem_size 单个泛型元素对象占用的字节体量 (sizeof) + * @param w_bytes 该泛型对象的排序密钥的总定长字节宽度 (W) + * @param extractor 字节提取器回调虚操作函数指针 (不能为 NULL) + * @param args 自定义上下文参数指针 + * @param allocator 用户自制的分配器指针(传入 NULL 则自动降级调用 c_DefaultAllocator) + */ +c_err_t c_LSD_RadixSort(void* base, c_size_t num, c_size_t elem_size, c_size_t w_bytes, + c_LSD_ExtractorFn extractor, void* args, c_Allocator_t* allocator); + + +#endif /*INCLUDED_C_LSD_H*/ diff --git a/String/c_LSD.t.c b/String/c_LSD.t.c new file mode 100644 index 0000000..528d235 --- /dev/null +++ b/String/c_LSD.t.c @@ -0,0 +1,66 @@ +#include "c_LSD.h" +#include "c_Test.h" +#include +#include + +static uint8_t int32_lsd_extractor(const void* elem, c_size_t d, void* args) { + (void)args; + // 强制转换为无符号 32 位整型值进行物理提取 + uint32_t val = *(const uint32_t*)elem; + + // 🌟 核心:通过按位右移位位移 (d * 8) 并截断为 8 位,完美抽取对应第 d 个无符号字节 + return (uint8_t)((val >> (d << 3)) & 0xFF); +} + +TEST_CASE(test_c_LSD_RadixSort_IntLinearFlow) { + // 准备一组带有高频极值碰撞、乱序排布的 32 位整型数组 + uint32_t arr[] = { 54321, 12, 987654, 54321, 333, 8, 12, 1000000 }; + c_size_t num = sizeof(arr) / sizeof(arr[0]); + + // 调用 LSD 基数排序:单体大小为 sizeof(uint32_t),32位整型总定长密钥宽度为 4 字节 + // 测试将 allocator 传入 NULL,检验内部默认单例 c_DefaultAllocator 的自适应降级 Fallback 承接力 + c_err_t err = c_LSD_RadixSort(arr, num, sizeof(uint32_t), 4, int32_lsd_extractor, NULL, NULL); + ASSERT_INT_EQ(C_ERR_OK, err); + + // 验证全区间无损单调非减性排列 + for (c_size_t i = 0; i < num - 1; i++) { + if (arr[i] > arr[i + 1]) { + // 利用高精确断言对排序崩溃点实施快速定位亮红灯 + ASSERT_INT_EQ(arr[i + 1], arr[i]); + return; + } + } + + // 首、尾、重复值插槽下标精准校验 + ASSERT_INT_EQ(8, (int)arr[0]); + ASSERT_INT_EQ(12, (int)arr[1]); + ASSERT_INT_EQ(12, (int)arr[2]); // 稳定去重项紧凑排列 + ASSERT_INT_EQ(54321, (int)arr[4]); + ASSERT_INT_EQ(1000000, (int)arr[num - 1]); // 尾部必须合拢为绝对最大值 +} + +TEST_CASE(test_c_LSD_RadixSort_ParamConstraints) { + uint32_t single[] = { 999 }; + // 验证各类入参毒参数及极端单元素边界的前置拦截状态码返回值 + ASSERT_INT_EQ(C_ERR_PARAM, c_LSD_RadixSort(NULL, 10, sizeof(uint32_t), 4, int32_lsd_extractor, NULL, NULL)); + ASSERT_INT_EQ(C_ERR_PARAM, c_LSD_RadixSort(single, 1, 0, 4, int32_lsd_extractor, NULL, NULL)); + ASSERT_INT_EQ(C_ERR_PARAM, c_LSD_RadixSort(single, 1, sizeof(uint32_t), 4, NULL, NULL, NULL)); + + // 单体拦截线:单元素无需排序直接放行返回 C_ERR_OK + ASSERT_INT_EQ(C_ERR_OK, c_LSD_RadixSort(single, 1, sizeof(uint32_t), 4, int32_lsd_extractor, NULL, NULL)); +} + +// ========================================== +// 5. 主集成运行入口点 +// ========================================== +int main(void) { + TEST_START(C_LSD_RadixSort_PolymorphicLinear_TestSuite); + + RUN_TEST(test_c_LSD_RadixSort_IntLinearFlow); + RUN_TEST(test_c_LSD_RadixSort_ParamConstraints); + + TEST_REPORT(); + + return (g_test_registry.failed_count > 0 ? 1 : 0); +} + diff --git a/String/c_MSD.c b/String/c_MSD.c new file mode 100644 index 0000000..922dfc2 --- /dev/null +++ b/String/c_MSD.c @@ -0,0 +1,152 @@ +#include + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +// 小区间降级截断阈值,小于该元素个数的碎子树不再分配计数桶,直接转产插入排序提速 +#define COMPONENT_MSD_CUTOFF 15 + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +/** + * @brief 🌟【核心加固】:带起始偏移量 d 的全景多级深度级联字典序比对器 + * + * 专门用于在小区间降级时,完美承接、维系 MSD 的终止符权重与后缀字典序判定 + */ +static inline int c_MSD_CascadedCompare(const void* a, const void* b, c_size_t start_d, c_MSD_ExtractorFn extractor, void* args) { + c_size_t cur_d = start_d; + while (1) { + int char_a = extractor(a, cur_d, args); + int char_b = extractor(b, cur_d, args); + + // 如果完全相等 + if (char_a == char_b) { + // 如果双双遇到了终止符 -1,说明字符串完全全等,返回 0 + if (char_a == -1) { + return 0; + } + // 否则前缀一致,游标无伤向右推,继续深度对碰比对后缀 + cur_d++; + continue; + } + + // 胜负已分:严格遵循 MSD 契约,终止符 -1 是绝对的极小值 + return char_a - char_b; + } +} + +/** + * @brief 纠正后的局部内联优化泛型插入排序 + */ +static void c_MSD_InsertionSort(char* base, c_size_t low, c_size_t high, c_size_t elem_size, c_MSD_ExtractorFn extractor, c_size_t d, void* args) { + for (c_size_t i = low + 1; i <= high; i++) { + c_size_t j = i; + char* item_i = base + (i * elem_size); + + // 分配 256 字节的局部栈栈缓冲区承接影子副本,隔绝别名践踏 + // assert(elem_size<256); + char v_buf[elem_size]; + memcpy(v_buf, item_i, elem_size); + + while (j > low) { + char* previous = base + ((j - 1) * elem_size); + char* current = base + (j * elem_size); + + // 🌟【核心修正】:调用带偏移量 d 的深度级联比对器,对后缀执行完全的字典序倒装走查 + if (c_MSD_CascadedCompare(v_buf, previous, d, extractor, args) < 0) { + memcpy(current, previous, elem_size); + j--; + } else { + break; + } + } + memcpy(base + (j * elem_size), v_buf, elem_size); + } +} + +/** + * @brief MSD 高位优先基数排序分治递归核心状态机 + */ +static void c_MSD_RadixSortRecursive(char* base, char* aux, c_size_t low, c_size_t high, c_size_t elem_size, c_MSD_ExtractorFn extractor, c_size_t d, void* args) { + if (high <= low || high == (c_size_t)-1) { + return; + } + + // 优化点 1:大荡荡后的碎子树完美降级截断 + if (high - low < COMPONENT_MSD_CUTOFF) { + c_MSD_InsertionSort(base, low, high, elem_size, extractor, d, args); + return; + } + + #define C_MSD_R 256 + c_size_t count[C_MSD_R + 2]; + memset(count, 0, sizeof(count)); + + for (c_size_t i = low; i <= high; i++) { + int c = extractor(base + (i * elem_size), d, args); + count[c + 2]++; + } + + for (int r = 0; r < C_MSD_R + 1; r++) { + count[r + 1] += count[r]; + } + + for (c_size_t i = low; i <= high; i++) { + int c = extractor(base + (i * elem_size), d, args); + c_size_t dest_idx = count[c + 1]++; + memcpy(aux + (dest_idx * elem_size), base + (i * elem_size), elem_size); + } + + c_size_t range_len = high - low + 1; + memcpy(base + (low * elem_size), aux, range_len * elem_size); + + // 🌟【分治避让控制】:终止符 r = -1 映射在映射后的位置,其对应的区间元素已完全排好序, + // 必须被剔除、略过,不再触发后续的 d+1 右移推进! + for (int r = 0; r < C_MSD_R; r++) { + c_size_t next_low = low + count[r]; + if (count[r + 1] > count[r]) { + c_size_t next_high = low + count[r + 1] - 1; + if (next_high > next_low) { + c_MSD_RadixSortRecursive(base, aux, next_low, next_high, elem_size, extractor, d + 1, args); + } + } + } + #undef C_MSD_R +} + +/** + * @brief 变长泛型高位优先(MSD)基数排序对外标准入口 + */ +c_err_t c_MSD_RadixSort(void* base, c_size_t num, c_size_t elem_size, c_MSD_ExtractorFn extractor, void* args, c_Allocator_t* allocator) { + if (!base || elem_size == 0 || !extractor) { + return C_ERR_PARAM; + } + if (num < 2) { + return C_ERR_OK; + } + + c_Allocator_t local_alloc; + if (allocator) { + local_alloc = *allocator; + } else { + local_alloc = c_DefaultAllocator; + } + + char* array_base = (char*)base; + + if (((c_size_t)-1) / elem_size < num) { + return C_ERR_NOMEM; + } + + char* aux = (char*)c_Allocator_Alloc(&local_alloc, num * elem_size); + if (!aux) { + return C_ERR_NOMEM; + } + + c_MSD_RadixSortRecursive(array_base, aux, 0, num - 1, elem_size, extractor, 0, args); + + c_Allocator_Free(&local_alloc, aux); + return C_ERR_OK; +} diff --git a/String/c_MSD.h b/String/c_MSD.h new file mode 100644 index 0000000..172e7c1 --- /dev/null +++ b/String/c_MSD.h @@ -0,0 +1,33 @@ +#ifndef INCLUDED_C_MSD_H +#define INCLUDED_C_MSD_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_ALLOCATOR_H +#include +#endif /*INCLUDED_C_ALLOCATOR_H*/ + + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * @brief 泛型 MSD 专用字节密钥提取器函数指针 + * + * @param elem 指向当前泛型对象的指针 + * @param d 当前从高向低扫描的第 d 个字节下标(0 代表最高位字节/首字节) + * @param args 自定义上下文参数 + * @return int 返回该泛型对象在第 d 个字节处的 8 位键值。 + * 🌟【重要工业契约】:如果 d 已经超越了该变长元素的极限长度,必须强制返回 -1 充当哨兵终止符! + */ +typedef int (*c_MSD_ExtractorFn)(const void* elem, c_size_t d, void* args); + + +c_err_t c_MSD_RadixSort(void* base, c_size_t num, c_size_t elem_size, + c_MSD_ExtractorFn extractor, void* args, c_Allocator_t* allocator); + + +#endif /*INCLUDED_C_MSD_H*/ diff --git a/String/c_MSD.t.c b/String/c_MSD.t.c new file mode 100644 index 0000000..9f9de4c --- /dev/null +++ b/String/c_MSD.t.c @@ -0,0 +1,66 @@ +#include "c_MSD.h" +#include "c_Test.h" + +#include +#include + +static int string_msd_extractor(const void* elem, c_size_t d, void* args) { + (void)args; + // 穿透二级指针,锁定堆字符串实体 + const char* str = *(const char* const*)elem; + + c_size_t len = strlen(str); + if (d >= len) { + return -1; // 🌟【完美契合契约】:若当前寻位 d 超出其字符串物理长度,抛出终止哨兵 + } + return (int)((unsigned char)str[d]); // 返回对应无符号字符代码 +} + +TEST_CASE(test_c_MSD_RadixSort_VariableLengthStrings) { + // 声明一组长度严重参差不齐、带有相同前缀干扰项的变长变长堆字符串数组 + const char* arr[] = { "she", "sells", "seashells", "by", "the", "sea", "shore", "the", "shells", "she" }; + c_size_t num = sizeof(arr) / sizeof(arr[0]); + + // 激活 MSD 基数排序:单体大为指针 sizeof(char*),无须传入 w_bytes + c_err_t err = c_MSD_RadixSort(arr, num, sizeof(char*), string_msd_extractor, NULL, &c_DefaultAllocator); + ASSERT_INT_EQ(C_ERR_OK, err); + + // 验证全区间字典序单调非减排列状态 + for (c_size_t i = 0; i < num - 1; i++) { + if (strcmp(arr[i], arr[i + 1]) > 0) { + // 抛出数据乱序冲突拦截 + ASSERT_TRUE(false); + return; + } + } + + // 精确的字典升序插槽下标内容比对验证 + ASSERT_TRUE(strcmp("by", arr[0]) == 0); + ASSERT_TRUE(strcmp("sea", arr[1]) == 0); + ASSERT_TRUE(strcmp("seashells", arr[2]) == 0); + ASSERT_TRUE(strcmp("sells", arr[3]) == 0); + ASSERT_TRUE(strcmp("she", arr[4]) == 0); // 稳定去重项紧凑对齐 + ASSERT_TRUE(strcmp("the", arr[num - 1]) == 0); // 尾部合拢正确 +} + +TEST_CASE(test_c_MSD_RadixSort_Boundaries) { + const char* single[] = { "hello" }; + // 验证非法及单元素边界 + ASSERT_INT_EQ(C_ERR_PARAM, c_MSD_RadixSort(NULL, 5, sizeof(char*), string_msd_extractor, NULL, NULL)); + ASSERT_INT_EQ(C_ERR_PARAM, c_MSD_RadixSort((void*)single, 1, 0, string_msd_extractor, NULL, NULL)); + ASSERT_INT_EQ(C_ERR_OK, c_MSD_RadixSort((void*)single, 1, sizeof(char*), string_msd_extractor, NULL, NULL)); +} + +// ========================================== +// 5. 主集成入口 +// ========================================== +int main(void) { + TEST_START(C_MSD_RadixSort_PolymorphicVariable_TestSuite); + + RUN_TEST(test_c_MSD_RadixSort_VariableLengthStrings); + RUN_TEST(test_c_MSD_RadixSort_Boundaries); + + TEST_REPORT(); + + RETURN_TEST_STATUS; +} \ No newline at end of file diff --git a/String/c_Quick3string.c b/String/c_Quick3string.c new file mode 100644 index 0000000..62d9dae --- /dev/null +++ b/String/c_Quick3string.c @@ -0,0 +1,159 @@ +#include + +// 小区间自适应截断降级阈值(小于此体量的碎区间直接交付级联插入排序专线) +#define COMPONENT_Q3S_CUTOFF 15 + +/** + * @brief 内部原子物理接口:泛型就地连续内存块对调 + */ +C_STATIC_FORCE_INLINE +void c_Q3S_InternalSwap(void* a, void* b, c_size_t size) { + if (a == b) return; + char* p1 = (char*)a; + char* p2 = (char*)b; + char temp_buf[256]; + c_size_t bytes_left = size; + while (bytes_left > 0) { + c_size_t chunk = (bytes_left < sizeof(temp_buf)) ? bytes_left : sizeof(temp_buf); + memcpy(&temp_buf, p1, chunk); + memcpy(p1, p2, chunk); + memcpy(p2, &temp_buf, chunk); + p1 += chunk; + p2 += chunk; + bytes_left -= chunk; + } +} + +/** + * @brief 带起始偏移量 d 的全景多级深度级联字典序比对器 + */ +C_STATIC_FORCE_INLINE +int c_Q3S_CascadedCompare(const void* a, const void* b, c_size_t start_d, c_Q3S_ExtractorFn extractor, void* args) { + c_size_t cur_d = start_d; + while (1) { + int char_a = extractor(a, cur_d, args); + int char_b = extractor(b, cur_d, args); + if (char_a == char_b) { + if (char_a == -1) return 0; // 双方完全全等 + cur_d++; + continue; + } + return char_a - char_b; // 截止符 -1 属于绝对极小值 + } +} + +/** + * @brief 降级优化专线:泛型后缀级联插入排序 + */ +static void c_Q3S_InsertionSort(char* base, c_size_t low, c_size_t high, c_size_t elem_size, c_Q3S_ExtractorFn extractor, c_size_t d, void* args) { + for (c_size_t i = low + 1; i <= high; i++) { + c_size_t j = i; + char* item_i = base + (i * elem_size); + char v_buf[elem_size]; + memcpy(v_buf, item_i, elem_size); + + while (j > low) { + char* previous = base + ((j - 1) * elem_size); + char* current = base + (j * elem_size); + if (c_Q3S_CascadedCompare(v_buf, previous, d, extractor, args) < 0) { + memcpy(current, previous, elem_size); + j--; + } else { + break; + } + } + memcpy(base + (j * elem_size), v_buf, elem_size); + } +} + +/** + * @brief 三向字符串快速排序递归控制核心状态机(无符号全加固闭区间版) + */ +static void c_Quick3string_Recursive(char* base, c_size_t low, c_size_t high, c_size_t elem_size, c_Q3S_ExtractorFn extractor, c_size_t d, void* args) { + // 🌟【无符号下溢强拦截】:若当前区间上限发生下溢跨步或者区间非法,果断前置熔断破出 + if (high <= low || high == (c_size_t)-1) { + return; + } + + // 1. 小数组降级截断,彻底抹杀高维分治在稀疏碎片子树下的常数延迟 + if (high - low < COMPONENT_Q3S_CUTOFF) { + c_Q3S_InsertionSort(base, low, high, elem_size, extractor, d, args); + return; + } + + // 2. 选择当前区间的首元素作为三向切分的基准锚定点(Pivot) + char* pivot_item = base + (low * elem_size); + int v = extractor(pivot_item, d, args); + + // 🌟【双指针向内对碰状态机控制线】: + // lt 游标代表小于 v 区间的右开端点;gt 游标代表大于 v 区间的左闭端点;i 为当前探测移动指针 + c_size_t lt = low; + c_size_t i = low + 1; + c_size_t gt = high; + + // 3. 核心分治走查循环 + while (i <= gt && gt != (c_size_t)-1) { + char* curr_item = base + (i * elem_size); + int t = extractor(curr_item, d, args); + + if (t < v) { + // 情况 A:当前字符小于切分字符,与 lt 位置就地物理对调,两组游标同步推进 + c_Q3S_InternalSwap(base + (lt * elem_size), curr_item, elem_size); + lt++; + i++; + } + else if (t > v) { + // 情况 B:当前字符大于切分字符,与当前的 gt 尾部边缘插槽执行就地 Swap 对调 + c_Q3S_InternalSwap(curr_item, base + (gt * elem_size), elem_size); + + // 🌟 绝杀点:gt 减法单调向左逼近。为了防止其减到 0 之后发生回绕下溢, + // 增设了严格的高位联锁前置判定,若 gt 已经逼近最左端,强制使其归 `-1` 并斩断 while + if (gt == 0) { + gt = (c_size_t)-1; + } else { + gt--; + } + // 注意:此时探测指针 i 保持原地不动!换进来的新内容会在下一轮循环被重新审讯判定 + } + else { + // 情况 C:完全相等,无伤安全滑过,游标递增 + i++; + } + } + + // 4. 🌟🌟🌟【级联分治三向并行递归管线】🌟🌟🌟 + + // 区间 1:递归对左侧完全 [low, lt - 1] 的“小于区”执行排序。第 d 位字符保持不变 + if (lt > 0 && (lt - 1) > low) { + c_Quick3string_Recursive(base, low, lt - 1, elem_size, extractor, d, args); + } + + // 区间 2:递归对中央 [lt, gt] 的“等于区”执行排序! + // 核心精髓:由于这个子区间的所有元素在第 d 位字符上已经百分之百全等, + // 我们的字节提取器游标 d + 1 单调向右推前移,彻底略过已对齐前缀,执行深层后缀的对碰。 + // 特殊拦截:若当前切分出的基准字符本身就是终止哨兵 -1,说明这批等于区的字符串内容在物理上已经提前完结,果断斩断深层递归,不进入 d+1 + if (v >= 0 && gt != (c_size_t)-1 && gt >= lt) { + c_Quick3string_Recursive(base, lt, gt, elem_size, extractor, d + 1, args); + } + + // 区间 3:递归对右侧 [gt + 1, high] 的“大于区”执行排序。第 d 位字符保持不变 + if (gt != (c_size_t)-1 && high > (gt + 1)) { + c_Quick3string_Recursive(base, gt + 1, high, elem_size, extractor, d, args); + } +} + +/** + * @brief 工业级变长泛型三向字符串快速排序标准对外总线入口 + */ +c_err_t c_Quick3string(void* base, c_size_t num, c_size_t elem_size, c_Q3S_ExtractorFn extractor, void* args) { + if (!base || elem_size == 0 || !extractor) { + return C_ERR_PARAM; + } + if (num < 2) { + return C_ERR_OK; // 零体安全放行 + } + + char* array_base = (char*)base; + c_Quick3string_Recursive(array_base, 0, num - 1, elem_size, extractor, 0, args); + return C_ERR_OK; +} diff --git a/String/c_Quick3string.h b/String/c_Quick3string.h new file mode 100644 index 0000000..f7cea9b --- /dev/null +++ b/String/c_Quick3string.h @@ -0,0 +1,24 @@ +#ifndef INCLUDED_C_QUICK3STRING_H +#define INCLUDED_C_QUICK3STRING_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_ALLOCATOR_H +#include +#endif /*INCLUDED_C_ALLOCATOR_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + + +/** + * @brief 泛型多态对象变长字节密钥提取器函数指针 + */ +typedef int (*c_Q3S_ExtractorFn)(const void* elem, c_size_t d, void* args); + +c_err_t c_Quick3string(void* base, c_size_t num, c_size_t elem_size, c_Q3S_ExtractorFn extractor, void* args); + +#endif /*INCLUDED_C_QUICK3STRING_H*/ diff --git a/String/c_Quick3string.t.c b/String/c_Quick3string.t.c new file mode 100644 index 0000000..26e28e2 --- /dev/null +++ b/String/c_Quick3string.t.c @@ -0,0 +1,86 @@ +#include "c_Quick3string.h" +#include "c_Test.h" +#include +#include + +static int q3s_string_extractor(const void* elem, c_size_t d, void* args) { + (void)args; + const char* str = *(const char* const*)elem; // 二级指针穿透锁字符 + c_size_t len = strlen(str); + if (d >= len) return -1; // 截止返回终止符 + return (int)((unsigned char)str[d]); +} + + +TEST_CASE(test_c_Quick3string_大容量三向划分压测) { + // 🌟 18 个包含大量深度高度咬合前缀("shor", "shore", "short")和高频大量全等项的恶劣大方阵 + const char* arr[] = { + "she", "sells", "seashells", "by", "the", + "sea", "shore", "the", "shells", "she", + "sea", "shore", "sit", "short", "shor", "a", + "sh", "z" + }; + c_size_t num = sizeof(arr) / sizeof(arr[0]); + + // 触发三向快排主专线,单体大为 sizeof(char*) + c_err_t err = c_Quick3string(arr, num, sizeof(char*), q3s_string_extractor, NULL); + ASSERT_INT_EQ(C_ERR_OK, err); + + // 验证严格的全局字典序单调非减 + for (c_size_t i = 0; i < num - 1; i++) { + if (strcmp(arr[i], arr[i + 1]) > 0) { + printf(" " COLOR_RED "[FAIL] 三向对碰错位捕获: arr[%d]='%s' 居然排在 arr[%d]='%s' 的前面!" COLOR_RESET "\n", + (int)i, arr[i], (int)(i + 1), arr[i + 1]); + ASSERT_TRUE(false); + return; + } + } + + // 首尾下标插槽数据精准断言比对 + ASSERT_TRUE(strcmp("a", arr[0]) == 0); + ASSERT_TRUE(strcmp("by", arr[1]) == 0); + ASSERT_TRUE(strcmp("sea", arr[2]) == 0); // 极短终止符前缀必须完美垫底最前端 + ASSERT_TRUE(strcmp("shells", arr[9]) == 0); // 极短终止符前缀必须完美垫底最前端 + ASSERT_TRUE(strcmp("shor", arr[10]) == 0); + ASSERT_TRUE(strcmp("shore", arr[11]) == 0); + ASSERT_TRUE(strcmp("z", arr[num - 1]) == 0); // 尾部大收拢正确 +} + +TEST_CASE(test_c_Quick3string_小样本Cutoff插入专线压测) { + // 🌟 10 个数据(小于阈值 15),强迫顶层在第 0 字节时就前置下触降级机制,由内联插入排序完全接管 + const char* arr_short[] = { + "shore", "she", "sells", "sea", "sh", + "shor", "sit", "short", "a", "sea" + }; + c_size_t num = sizeof(arr_short) / sizeof(arr_short[0]); + + c_err_t err = c_Quick3string(arr_short, num, sizeof(char*), q3s_string_extractor, NULL); + ASSERT_INT_EQ(C_ERR_OK, err); + + // 验证降级后缀插入排序后的单调非减正确性 + for (c_size_t i = 0; i < num - 1; i++) { + if (strcmp(arr_short[i], arr_short[i + 1]) > 0) { + ASSERT_TRUE(false); + return; + } + } + ASSERT_TRUE(strcmp("a", arr_short[0]) == 0); + ASSERT_TRUE(strcmp("sea", arr_short[1]) == 0); + ASSERT_TRUE(strcmp("sea", arr_short[2]) == 0); // 稳定排序项紧凑排布 + ASSERT_TRUE(strcmp("sh", arr_short[4]) == 0); // 短前缀完美垫底 + ASSERT_TRUE(strcmp("shor", arr_short[6]) == 0); + ASSERT_TRUE(strcmp("shore", arr_short[7]) == 0); + ASSERT_TRUE(strcmp("short", arr_short[8]) == 0); + ASSERT_TRUE(strcmp("sit", arr_short[num-1]) == 0); +} + +// ========================================== +// 5. 主集成入口 +// ========================================== +int main(void) { + TEST_START(C_Quick3string_RigorousStandard_TestSuite); + RUN_TEST(test_c_Quick3string_大容量三向划分压测); + RUN_TEST(test_c_Quick3string_小样本Cutoff插入专线压测); + TEST_REPORT(); + RETURN_TEST_STATUS; +} \ No newline at end of file diff --git a/String/c_RabinKarp.c b/String/c_RabinKarp.c new file mode 100644 index 0000000..2713b65 --- /dev/null +++ b/String/c_RabinKarp.c @@ -0,0 +1,128 @@ +#include + +/** + * @brief 内部静态辅助:为变长文本流计算初始固定视窗的霍纳法则(Horner's rule)哈希指纹 + */ +static uint64_t c_RK_ComputeHash(const char* key, c_size_t m, uint64_t R, uint64_t Q) { + uint64_t h = 0; + for (c_size_t i = 0; i < m; i++) { + h = (R * h + (unsigned char)key[i]) % Q; + } + return h; +} + +/** + * @brief 就地初始化 Rabin-Karp 模式串匹配器 (前置计算模式指纹与最高位系数) + */ +c_err_t c_RabinKarp_Init(c_RabinKarp_t* self, const char* pattern, c_Allocator_t* allocator) { + if (!self || !pattern) { + return C_ERR_PARAM; + } + + self->allocator = allocator ? *allocator : c_DefaultAllocator; + self->m_len = strlen(pattern); + if (self->m_len == 0) { + return C_ERR_PARAM; + } + + // 1. 为模式串深拷贝开辟独立空间 + self->pattern = (char*)c_Allocator_Alloc(&self->allocator, (self->m_len + 1) * sizeof(char)); + if (!self->pattern) { + return C_ERR_NOMEM; + } + memcpy(self->pattern, pattern, self->m_len + 1); + + // 2. 配置商用数论高离散参数 + self->R_base = 256; // 扩展 ASCII 字符表基数 + self->Q_prime = 1000000007; // 经典防大规模冲突大素数 (10^9 + 7) + + c_size_t m = self->m_len; + uint64_t R = self->R_base; + uint64_t Q = self->Q_prime; + + // 3. 预先推演滚动最高位字符移除时所需要的物理权重乘子:RM = R^(M-1) % Q + self->RM = 1; + for (c_size_t i = 1; i <= m - 1; i++) { + self->RM = (R * self->RM) % Q; + } + + // 4. 计算出当前子串模式串的绝对初始数字指纹 + self->pattern_hash = c_RK_ComputeHash(self->pattern, m, R, Q); + + return C_ERR_OK; +} + +/** + * @brief 在主文本 text 中执行 Rabin-Karp 高性能滚动哈希检索(均摊时间复杂度 O(N)) + * + * @param text 待扫描检索的主文本大字节流(以 '\0' 截止) + * @param out_index 找到时,通过二级指针物理填充并回传匹配位置在主文本中的【起始插槽下标】 + * @return c_err_t 检索成功返回 C_ERR_OK,未找到匹配项返回 C_ERR_NOTFOUND + */ +c_err_t c_RabinKarp_Search(const c_RabinKarp_t* self, const char* text, c_size_t* out_index) { + if (!self || !text || !out_index) { + return C_ERR_PARAM; + } + + c_size_t n = strlen(text); + c_size_t m = self->m_len; + + if (n < m) return C_ERR_NOTFOUND; // 文本总长短于子串,安全拦截 + + uint64_t R = self->R_base; + uint64_t Q = self->Q_prime; + uint64_t RM = self->RM; + uint64_t pat_hash = self->pattern_hash; + + // 1. 利用霍纳法则瞬间计算出主文本中 [0, M-1] 初始前缀视窗的哈希值 + uint64_t txt_hash = c_RK_ComputeHash(text, m, R, Q); + + // 2. 拉斯维加斯验证机制:前置哈希完全等价时,启动全貌核验,粉碎极端哈希碰撞 + if ((txt_hash == pat_hash) && (strncmp(text, self->pattern, m) == 0)) { + *out_index = 0; + return C_ERR_OK; + } + + // 3. 🌟🌟🌟【Rabin 指纹极致滚动滑窗内核】🌟🌟🌟 + // 游标 i 指向当前滑窗即将【移出】的最高位字符,滑窗单调向右推移 + c_size_t limit = n - m; + for (c_size_t i = 0; i < limit; i++) { + + // A. 提取即将移出的高位字符与即将灌入的低位新字符特征 + uint64_t char_to_remove = (unsigned char)text[i]; + uint64_t char_to_add = (unsigned char)text[i + m]; + + // B. 滚动变换: + // (txt_hash + Q - (char_to_remove * RM) % Q) -> 🌟加 Q 防无符号减法算术下溢回绕! + // 随后乘 R 整体左移一格,最后加上低位新字符并整体取模,实现 O(1) 常数级哈希滑窗推演! + uint64_t high_remove_part = (char_to_remove * RM) % Q; + txt_hash = (txt_hash + Q - high_remove_part) % Q; + txt_hash = (txt_hash * R + char_to_add) % Q; + + // C. 拉斯维加斯联锁判定:计算当前最新滑窗位置处的物理偏移量 + c_size_t current_match_offset = i + 1; + if (txt_hash == pat_hash) { + if (strncmp(text + current_match_offset, self->pattern, m) == 0) { + *out_index = current_match_offset; // 精确传出匹配起点物理下标 + return C_ERR_OK; + } + } + } + + return C_ERR_NOTFOUND; +} + +/** + * @brief 反初始化:彻底注销并回收 Rabin-Karp 状态机内存空间 + */ +void c_RabinKarp_Destroy(c_RabinKarp_t* self) { + if (self) { + if (self->pattern) { + c_Allocator_Free(&self->allocator, self->pattern); + self->pattern = NULL; + } + self->m_len = 0; + self->pattern_hash = 0; + self->RM = 0; + } +} diff --git a/String/c_RabinKarp.h b/String/c_RabinKarp.h new file mode 100644 index 0000000..2d4e1ab --- /dev/null +++ b/String/c_RabinKarp.h @@ -0,0 +1,35 @@ +#ifndef INCLUDED_C_RABINKARP_H +#define INCLUDED_C_RABINKARP_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_ALLOCATOR_H +#include +#endif /*INCLUDED_C_ALLOCATOR_H*/ + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + + +typedef struct { + char* pattern; // 独立深拷贝转储的模式串(子串)载体 + c_size_t m_len; // 模式串的有效字符物理长度 (M) + uint64_t pattern_hash; // 模式串的数字指纹哈希码值 + uint64_t RM; // 滚动哈希最高位字符的位移系数权重乘子: R^(M-1) % Q + uint64_t Q_prime; // 滚动哈希专用的大素数取模基数 (Q) + uint64_t R_base; // 字母表物理基数 (常数项乘子,扩展 ASCII 固定为 256) + c_Allocator_t allocator; // 统一的内联组合分配器实例 +} c_RabinKarp_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_RabinKarp_Init(c_RabinKarp_t* self, const char* pattern, c_Allocator_t* allocator); +c_err_t c_RabinKarp_Search(const c_RabinKarp_t* self, const char* text, c_size_t* out_index); +void c_RabinKarp_Destroy(c_RabinKarp_t* self); + + +#endif /*INCLUDED_C_RABINKARP_H*/ diff --git a/String/c_RabinKarp.t.c b/String/c_RabinKarp.t.c new file mode 100644 index 0000000..e26bf75 --- /dev/null +++ b/String/c_RabinKarp.t.c @@ -0,0 +1,65 @@ +#include "c_RabinKarp.h" +#include "c_Test.h" +#include +#include + + +TEST_CASE(test_c_RabinKarp_Full) { + c_RabinKarp_t rk; + + // 初始化子串模式匹配器,测试默认 Fallback 降级分配器 + c_err_t err = c_RabinKarp_Init(&rk, "ABRACADABRA", NULL); + ASSERT_INT_EQ(C_ERR_OK, err); + + // 1. 验证常规居中匹配 + const char* text_normal = "IN A BIG ABRACADABRA TEXT STREAM"; + c_size_t match_index = 0; + + err = c_RabinKarp_Search(&rk, text_normal, &match_index); + ASSERT_INT_EQ(C_ERR_OK, err); + ASSERT_INT_EQ(9, (int)match_index); // 精确验证子串在主文本中的绝对起始物理插槽下标为 9 + + // 2. 🌟【拉斯维加斯深度压测】: + // 故意构造一个开头、中段充斥着大量高度相似哈希冲突干扰项、主子串深埋末尾的数据流 + c_RabinKarp_t rk_toxic; + c_RabinKarp_Init(&rk_toxic, "C_CORE", &c_DefaultAllocator); + + const char* text_toxic = "C_C0RE_A_C_C0RE_B_C_CORE_SYS"; + match_index = 0; + + err = c_RabinKarp_Search(&rk_toxic, text_toxic, &match_index); + ASSERT_INT_EQ(C_ERR_OK, err); + ASSERT_INT_EQ(18, (int)match_index); // 指纹滑动状态机过滤掉高全等欺骗,成功精确定位! + + // 3. 边界未命中核验 + const char* text_fake = "C_C0RE_A_C_C0RE_B_SYSTEM_ERR"; + ASSERT_INT_EQ(C_ERR_NOTFOUND, c_RabinKarp_Search(&rk_toxic, text_fake, &match_index)); + + c_RabinKarp_Destroy(&rk); + c_RabinKarp_Destroy(&rk_toxic); +} + +TEST_CASE(test_c_RabinKarp_Toxicity_Defenses) { + c_RabinKarp_t local_rk; + c_RabinKarp_Init(&local_rk, "A", NULL); + + c_size_t idx = 0; + // 4. 验证入参非法强拦截线 + ASSERT_INT_EQ(C_ERR_PARAM, c_RabinKarp_Init(NULL, NULL, NULL)); + ASSERT_INT_EQ(C_ERR_PARAM, c_RabinKarp_Search(NULL, "text", &idx)); + ASSERT_INT_EQ(C_ERR_PARAM, c_RabinKarp_Search(&local_rk, NULL, &idx)); + + c_RabinKarp_Destroy(&local_rk); +} + +// ========================================== +// 5. 主集成入口 +// ========================================== +int main(void) { + TEST_START(C_RabinKarp_RollingHash_TestSuite); + RUN_TEST(test_c_RabinKarp_Full); + RUN_TEST(test_c_RabinKarp_Toxicity_Defenses); + TEST_REPORT(); + return (g_test_registry.failed_count > 0 ? 1 : 0); +} + diff --git a/String/c_TrieST.c b/String/c_TrieST.c new file mode 100644 index 0000000..fad5262 --- /dev/null +++ b/String/c_TrieST.c @@ -0,0 +1,412 @@ +#include +#include + +/** + * @brief 内部私有:安全开辟并清空一个全新的 Trie 物理节点 + */ +C_STATIC_FORCE_INLINE +c_TrieNode* c_TrieST_CreateNode(c_Allocator_t* alloc) { + c_TrieNode* node = (c_TrieNode*)c_Allocator_Alloc(alloc, sizeof(c_TrieNode)); + if (!node) return NULL; + node->val = NULL; + memset(node->next, 0, sizeof(node->next)); // 将 256 路子链接全部初始化填充为干净的 NULL + return node; +} + +/** + * @brief 就地初始化多路单词查找树符号表 + * + * @param val_cp 多态值拷贝函数指针。若传入 NULL,则自动对 Value 降级执行原始物理地址浅拷贝托管 + * @param val_free 多态值销毁函数指针。若 Value 属于扁平内置类型无须释放,可传入 NULL + */ +c_err_t c_TrieST_Init(c_TrieST_t* self, + void* (*val_cp)(const void*, void*), + void (*val_free)(void*, void*), + void* val_arg, + c_Allocator_t* allocator) +{ + if (!self) { + return C_ERR_PARAM; + } + + if (allocator) { + self->allocator = *allocator; + } else { + self->allocator = c_DefaultAllocator; + } + + self->root = NULL; + self->size = 0; + self->val_cp = val_cp; + self->val_free = val_free; + self->val_arg = val_arg; + + return C_ERR_OK; +} + +/** + * @brief 内部递归插入状态机 + */ +static c_err_t c_TrieST_InternalPut(c_TrieST_t* self, c_TrieNode** node_ptr, const char* key, c_size_t d, const void* val, bool* is_new_key) { + // 递归基 A:若沿途下探到的物理链接为空,在此处延迟惰性加载开辟新控制头 + if (*node_ptr == NULL) { + *node_ptr = c_TrieST_CreateNode(&self->allocator); + if (*node_ptr == NULL) return C_ERR_NOMEM; + } + + c_TrieNode* curr = *node_ptr; + + // 递归基 B:已经完美推进匹配到了变长键的物理末尾截止字符 + if (key[d] == '\0') { + if (curr->val == NULL) { + *is_new_key = true; // 确定属于全新插入而非覆写 + } else { + *is_new_key = false; // 属于相同键更新覆写语义 + if (self->val_free) { + self->val_free(curr->val, self->val_arg); // 覆写前先摧毁老对象堆空间 + } + } + + // 执行值转储 + if (self->val_cp) { + curr->val = self->val_cp(val, self->val_arg); + } else { + curr->val = (void*)val; // 浅拷贝放行 + } + return C_ERR_OK; + } + + // 多路高速变轨寻址:利用当前的无符号字符物理代码直接作为 256 维数组下标切入 + unsigned char c = (unsigned char)key[d]; + return c_TrieST_InternalPut(self, &(curr->next[c]), key, d + 1, val, is_new_key); +} + +/** + * @brief 存入键值对(时间复杂度仅取决于变长键长 O(W),与总键数完全独立解耦) + * + * @param key 以 '\0' 结尾的标准变长字符串键 + * @param val 指向待注入 Value 对象的指针 + */ +c_err_t c_TrieST_Put(c_TrieST_t* self, const char* key, const void* val) { + if (!self || !key || !val) return C_ERR_PARAM; + + bool is_new = false; + c_err_t err = c_TrieST_InternalPut(self, &(self->root), key, 0, val, &is_new); + if (err == C_ERR_OK && is_new) { + self->size++; // 只有在全新键插入时才增加全局计数 + } + return err; +} + +/** + * @brief 内部下探滑窗辅助:根据指定字符串前缀寻找对应的 Trie 树局部根节点 + */ +static c_TrieNode* c_TrieST_SubtreeSearch(c_TrieNode* x, const char* key, c_size_t d) { + if (x == NULL) return NULL; + if (key[d] == '\0') return x; // 前缀完全匹配命中,给回当前局部根指针 + unsigned char c = (unsigned char)key[d]; + return c_TrieST_SubtreeSearch(x->next[c], key, d + 1); +} + +/** + * @brief 依据指定字符串键,精准提取读取其关联多态 Value 的原生物理堆地址 + * + * @return void* 找到时返回该值的原生指针;未找到或表为空时返回 NULL + */ +void* c_TrieST_Get(const c_TrieST_t* self, const char* key) { + if (!self || !key || self->size == 0) return NULL; + + c_TrieNode* x = c_TrieST_SubtreeSearch(self->root, key, 0); + if (x == NULL) return NULL; + return x->val; // 🌟【重要约束】:必须返回 val,若 val 为 NULL 说明虽然路径前缀存在但并非完整键 +} + +/** + * @brief 检查符号表内是否有效包含指定的字符串键 + */ +bool c_TrieST_Contains(const c_TrieST_t* self, const char* key) { + return c_TrieST_Get(self, key) != NULL; +} + +/** + * @brief 内部私有:安全走查某个 Trie 节点当前是否彻底沦为空仓空节点 + */ +C_STATIC_FORCE_INLINE +bool c_TrieST_HasChildren(const c_TrieNode* node) { + for (int r = 0; r < C_TRIE_R; r++) { + if (node->next[r] != NULL) return true; // 只要 256 路中有任一指针不为空,说明包含子代分支 + } + return false; +} + +/** + * @brief 内部级联递归惰性剪枝删除状态机 + */ +static c_TrieNode* c_TrieST_InternalDelete(c_TrieST_t* self, c_TrieNode* x, const char* key, c_size_t d, c_err_t* out_err) { + if (x == NULL) { + *out_err = C_ERR_NOTFOUND; + return NULL; + } + + if (key[d] == '\0') { + if (x->val != NULL) { + // 精确命中要斩断的目标完整键:调用用户自备析构函数原路瓦解清除 Value 堆空间 + if (self->val_free) { + self->val_free(x->val, self->val_arg); + } + x->val = NULL; + self->size--; // 递减总容量计数 + *out_err = C_ERR_OK; + } else { + *out_err = C_ERR_NOTFOUND; // 路径虽通,但该位置并无有效完整值键 + } + } else { + unsigned char c = (unsigned char)key[d]; + // 递归向下游层级探查推进 + x->next[c] = c_TrieST_InternalDelete(self, x->next[c], key, d + 1, out_err); + } + + // 🌟🌟🌟【自底向上绝对安全惰性剪枝防线(Lazy Pruning Countermeasure)】🌟🌟🌟 + // 退栈回溯时,如果发现当前节点的值已经被抹除置空 (val == NULL) + if (x->val == NULL) { + // 且进一步走查发现它下方的 256 路子代指针也已全部被断开变空 (无子孙驻留) + if (!c_TrieST_HasChildren(x)) { + // 说明此节点已彻底丧失路由及存储价值,果断对其执行物理垃圾回收,并向父层级返回 NULL 断开挂载! + c_Allocator_Free(&self->allocator, x); + return NULL; + } + } + + return x; // 沿途返回自身指针,维持上层树拓扑形态不破裂 +} + +/** + * @brief 根据指定变长字符串键彻底从树中斩断移除该符号,并自动自适应逆向触发级联剪枝释放 + */ +c_err_t c_TrieST_Delete(c_TrieST_t* self, const char* key) { + if (!self || !key) return C_ERR_PARAM; + if (self->size == 0) return C_ERR_EMPTY; + + c_err_t err = C_ERR_NOTFOUND; + self->root = c_TrieST_InternalDelete(self, self->root, key, 0, &err); + return err; +} + +/** + * @brief 内部后序深度递归遍历解构辅助 + */ +static void c_TrieST_InternalDestroy(c_Allocator_t* alloc, c_TrieNode* x, + void (*val_free)(void*, void*), void* val_arg) +{ + if (x == NULL) return; + + // 后序递归:先将 256 路子代网络彻底瓦解,最后再收拢解构自身 + for (int r = 0; r < C_TRIE_R; r++) { + c_TrieST_InternalDestroy(alloc, x->next[r], val_free, val_arg); + } + + if (x->val && val_free) { + val_free(x->val, val_arg); + } + c_Allocator_Free(alloc, x); +} + +/** + * @brief 符号表注销解构:全多路节点级联物理洗刷清除 + */ +void c_TrieST_Destroy(c_TrieST_t* self) { + if (self && self->root) { + c_TrieST_InternalDestroy(&self->allocator, self->root, self->val_free, self->val_arg); + self->root = NULL; + self->size = 0; + } +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * @brief 内部私有:下探滑窗寻找特定前缀所在的局部子树根节点 + */ +static c_TrieNode* c_TrieST_FindSubtreeRoot(c_TrieNode* x, const char* prefix, c_size_t d) { + if (x == NULL) return NULL; + if (prefix[d] == '\0') return x; + unsigned char c = (unsigned char)prefix[d]; + return c_TrieST_FindSubtreeRoot(x->next[c], prefix, d + 1); +} + +/** + * @brief 内部DFS辅助状态机:供 KeysWithPrefix 前缀联想收集路径使用 + */ +static c_err_t c_TrieST_CollectPrefixDFS(c_TrieNode* x, c_StringBuffer_t* path_buf, c_StringList_t* results) { + if (x == NULL) return C_ERR_OK; + + // 1. 若当前路径恰好构成一个有效独立键,从 StringBuffer 提取零拷贝只读 CStr 镜像,通过 StringList 值拷贝深拷贝压出 + if (x->val != NULL) { + c_err_t err = c_StringList_Add(results, c_StringBuffer_CStr(path_buf)); + if (err != C_ERR_OK) return err; + } + + // 2. 单调向全 256 路子代网络执行就地滑窗探测 + for (int r = 0; r < C_TRIE_R; r++) { + if (x->next[r] != NULL) { + // 🌟就地前推:将当前分支字符压入自适应缓冲区,零堆碎片开销 + c_err_t err = c_StringBuffer_AppendChar(path_buf, (char)r); + if (err != C_ERR_OK) return err; + + err = c_TrieST_CollectPrefixDFS(x->next[r], path_buf, results); + if (err != C_ERR_OK) return err; + + // 🌟就地回溯:探查完当前分支后,执行 PopBack 弹栈抹除,恢复当前层缓冲区形态,物理杜绝内存污染 + c_StringBuffer_RemoveAt(path_buf, path_buf->size-1, 1); + } + } + return C_ERR_OK; +} + +/** + * @brief 🌟 高阶总线 API 1:前缀模糊联想匹配检索 + */ +c_err_t c_TrieST_KeysWithPrefix(c_TrieST_t* self, const char* prefix, c_StringList_t* results) { + if (!self || !prefix || !results) return C_ERR_PARAM; + if (self->size == 0 || self->root == NULL) return C_ERR_OK; + + // 下探锁定局部子树根位置 + c_TrieNode* subtree_root = c_TrieST_FindSubtreeRoot(self->root, prefix, 0); + if (subtree_root == NULL) return C_ERR_OK; + + // 初始化独立的弹性路径缓冲区,前置装填好已匹配的公共前缀 + c_StringBuffer_t path_buffer; + c_size_t prefix_len = strlen(prefix); + c_err_t err = c_StringBuffer_Init(&path_buffer, prefix_len + 16, &self->allocator); + if (err != C_ERR_OK) return err; + + // for (c_size_t i = 0; i < prefix_len; i++) { + // c_StringBuffer_AppendChar(&path_buffer, prefix[i]); + // } + + c_StringBuffer_Append(&path_buffer, prefix, prefix_len); + + // 启动多路并行 DFS 状态机 + err = c_TrieST_CollectPrefixDFS(subtree_root, &path_buffer, results); + + c_StringBuffer_Destroy(&path_buffer); // 原子回收局部弹性路径链,常数级空间控制 + return err; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ + +static c_err_t c_TrieST_MatchPatternRecursive(c_TrieNode* x, const char* pattern, c_size_t d, c_StringBuffer_t* path_buf, + c_StringList_t* results) +{ + if (x == NULL) return C_ERR_OK; + + char current_pat = pattern[d]; + + // 递归基:模式串已步进到末尾截止位置 + if (current_pat == '\0') { + if (x->val != NULL) { + c_err_t err = c_StringList_Add(results, c_StringBuffer_CStr(path_buf)); + if (err != C_ERR_OK) return err; + } + return C_ERR_OK; + } + + // 🌟🌟🌟【通配符万能字符信道辐射状态机】🌟🌟🌟 + if (current_pat == '.') { + for (int r = 0; r < C_TRIE_R; r++) { + if (x->next[r] != NULL) { + c_StringBuffer_AppendChar(path_buf, (char)r); // 就地前推 + + c_err_t err = c_TrieST_MatchPatternRecursive(x->next[r], pattern, d + 1, path_buf, results); + if (err != C_ERR_OK) { + c_StringBuffer_PopBack(path_buf); + return err; + } + + c_StringBuffer_PopBack(path_buf); // 就地回溯恢复 + } + } + } else { + // 精确字符 O(1) 二级单路快速寻址 + unsigned char c = (unsigned char)current_pat; + if (x->next[c] != NULL) { + c_StringBuffer_AppendChar(path_buf, (char)c); + + c_err_t err = c_TrieST_MatchPatternRecursive(x->next[c], pattern, d + 1, path_buf, results); + if (err != C_ERR_OK) { + c_StringBuffer_PopBack(path_buf); + return err; + } + + c_StringBuffer_PopBack(path_buf); + } + } + + return C_ERR_OK; +} + +/** + * @brief 🌟 高阶总线 API 2:通配符正则全词模糊匹配接口(StringBuffer 加固版) + */ +c_err_t c_TrieST_KeysThatMatch(c_TrieST_t* self, const char* pattern, c_StringList_t* results) { + if (!self || !pattern || !results) return C_ERR_PARAM; + if (self->size == 0 || self->root == NULL) return C_ERR_OK; + + c_StringBuffer_t path_buffer; + c_err_t err = c_StringBuffer_Init(&path_buffer, strlen(pattern) + 8, &self->allocator); + if (err != C_ERR_OK) return err; + + err = c_TrieST_MatchPatternRecursive(self->root, pattern, 0, &path_buffer, results); + + c_StringBuffer_Destroy(&path_buffer); + return err; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ + +/** + * @brief 内部寻阻辅助:供 LongestPrefixOf 深度爬坡状态机使用 + */ +static void c_TrieST_FindLongestPrefixLength(c_TrieNode* x, const char* query, c_size_t d, c_size_t* out_longest_len) { + if (x == NULL) return; + if (x->val != NULL) { + *out_longest_len = d; // 沿途更新最大有效掩码位宽 + } + if (query[d] == '\0') return; + + unsigned char c = (unsigned char)query[d]; + c_TrieST_FindLongestPrefixLength(x->next[c], query, d + 1, out_longest_len); +} + +/** + * @brief 🌟 高阶总线 API 3:最长前缀分级掩码路由寻址(StringBuffer 加固版) + */ +c_err_t c_TrieST_LongestPrefixOf(c_TrieST_t* self, const char* query, c_StringList_t* results) { + if (!self || !query || !results) return C_ERR_PARAM; + if (self->size == 0 || self->root == NULL) return C_ERR_OK; + + c_size_t longest_match_length = 0; + c_TrieST_FindLongestPrefixLength(self->root, query, 0, &longest_match_length); + + if (longest_match_length == 0 && self->root->val == NULL) { + return C_ERR_NOTFOUND; + } + + // 利用 StringBuffer 直接截断装填该寻址段 + c_StringBuffer_t path_buffer; + c_err_t err = c_StringBuffer_Init(&path_buffer, longest_match_length + 4, &self->allocator); + if (err != C_ERR_OK) return err; + + // for (c_size_t i = 0; i < longest_match_length; i++) { + // c_StringBuffer_AppendChar(&path_buffer, query[i]); + // } + + c_StringBuffer_Append(&path_buffer, query, longest_match_length); + + err = c_StringList_Add(results, c_StringBuffer_CStr(&path_buffer)); + + c_StringBuffer_Destroy(&path_buffer); + return err; +} + diff --git a/String/c_TrieST.h b/String/c_TrieST.h new file mode 100644 index 0000000..d5f08ef --- /dev/null +++ b/String/c_TrieST.h @@ -0,0 +1,61 @@ +#ifndef INCLUDED_C_TRIEST_H +#define INCLUDED_C_TRIEST_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_ALLOCATOR_H +#include +#endif /*INCLUDED_C_ALLOCATOR_H*/ + +#ifndef INCLUDED_C_STRINGLIST_H +#include +#endif /*INCLUDED_C_STRINGLIST_H*/ + + + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define C_TRIE_R 256 + +/** + * @brief Trie 树内部多路物理节点封装 + */ +typedef struct c_TrieNode { + void* val; // 指向用户多态 Value 的物理堆地址 (若为 NULL 代表该前缀路径非完整键) + struct c_TrieNode* next[C_TRIE_R]; // 密集平铺的 256 路子节点二级指针控制控制总线 +} c_TrieNode; + +typedef struct { + c_TrieNode* root; // 查找树的根节点指针 + c_size_t size; // 当前符号表内有效驻留的完整键值对总个数 + void* (*val_cp)(const void* data, void* arg); // 值高级多态深拷贝虚函数 + void (*val_free)(void* data, void* arg); // 值高级多态解构自毁灭虚函数 + void* val_arg; // 多态虚函数伴生上下文参数 + c_Allocator_t allocator; // 内联组合分配器实例与自适应 Fallback 缺省机制 +} c_TrieST_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_TrieST_Init(c_TrieST_t* self, + void* (*val_cp)(const void*, void*), + void (*val_free)(void*, void*), void* val_arg, + c_Allocator_t* allocator); +void c_TrieST_Destroy(c_TrieST_t* self); + +c_err_t c_TrieST_Put(c_TrieST_t* self, const char* key, const void* val); + +void* c_TrieST_Get(const c_TrieST_t* self, const char* key); + +bool c_TrieST_Contains(const c_TrieST_t* self, const char* key); + +c_err_t c_TrieST_Delete(c_TrieST_t* self, const char* key); + +c_err_t c_TrieST_KeysWithPrefix(c_TrieST_t* self, const char* prefix, c_StringList_t* results); +c_err_t c_TrieST_KeysThatMatch(c_TrieST_t* self, const char* pattern, c_StringList_t* results); +c_err_t c_TrieST_LongestPrefixOf(c_TrieST_t* self, const char* query, c_StringList_t* results); + +#endif /*INCLUDED_C_TRIEST_H*/ diff --git a/String/c_TrieST.t.c b/String/c_TrieST.t.c new file mode 100644 index 0000000..b3240f5 --- /dev/null +++ b/String/c_TrieST.t.c @@ -0,0 +1,147 @@ +#include "c_TrieST.h" +#include "c_Test.h" +#include +#include + +typedef struct { + uint32_t gateway_id; + char protocol_tag[16]; +} RouteConfig_t; + +static void* test_trie_val_cp(const void* data, void* arg) { + (void)arg; + const RouteConfig_t* src = (const RouteConfig_t*)data; + RouteConfig_t* clone = (RouteConfig_t*)malloc(sizeof(RouteConfig_t)); // 模拟独立动态开辟 + if (clone) { + clone->gateway_id = src->gateway_id; + strcpy(clone->protocol_tag, src->protocol_tag); + } + return (void*)clone; +} + +static void test_trie_val_free(void* data, void* arg) { + (void)arg; + if (data) { + free(data); // 原路深层爆破销毁 + } +} + +TEST_CASE(test_c_TrieST_Extreme_O_W_CRUD) { + c_TrieST_t trie; + + // 初始化多态单词查找树,绑定虚操作,且传递 NULL 检验默认 Fallback 降级分配器机制 + c_err_t err = c_TrieST_Init(&trie, test_trie_val_cp, test_trie_val_free, NULL, NULL); + ASSERT_INT_EQ(C_ERR_OK, err); + + // 构建测试密集变长前缀树丛:"sea", "seashells", "she" + RouteConfig_t config_sea = { 101, "HTTP" }; + RouteConfig_t config_shells = { 202, "MQTT" }; + RouteConfig_t config_she = { 303, "gRPC" }; + + // 1. Put 基础写入断言 + ASSERT_INT_EQ(C_ERR_OK, c_TrieST_Put(&trie, "sea", &config_sea)); + ASSERT_INT_EQ(C_ERR_OK, c_TrieST_Put(&trie, "seashells", &config_shells)); + ASSERT_INT_EQ(C_ERR_OK, c_TrieST_Put(&trie, "she", &config_she)); + ASSERT_INT_EQ(3, (int)trie.size); + + // Contains 与符号模糊隔离断言 + ASSERT_TRUE(c_TrieST_Contains(&trie, "seashells")); + ASSERT_TRUE(!c_TrieST_Contains(&trie, "seash")); // 前缀虽通,但非完整独立有效键,必须返回 false + + // 2. Get 常数时间穿透读取与多态结构体字段验证 + void* fetched_ptr = c_TrieST_Get(&trie, "seashells"); + ASSERT_TRUE(fetched_ptr != NULL); + RouteConfig_t* match_conf = (RouteConfig_t*)fetched_ptr; + ASSERT_INT_EQ(202, (int)match_conf->gateway_id); + ASSERT_TRUE(strcmp("MQTT", match_conf->protocol_tag) == 0); + + // 3. Put 相同键下的覆写更新(Overwrite)特性核验 + RouteConfig_t config_update = { 202, "HTTP/3" }; + ASSERT_INT_EQ(C_ERR_OK, c_TrieST_Put(&trie, "seashells", &config_update)); + ASSERT_INT_EQ(3, (int)trie.size); // 全局大小强力守恒 + match_conf = (RouteConfig_t*)c_TrieST_Get(&trie, "seashells"); + ASSERT_TRUE(strcmp("HTTP/3", match_conf->protocol_tag) == 0); // 覆写完全奏效 + + // 4. Delete 惰性级联剪枝删除绝对安全验证 + // 斩断移出带有深度嵌套延伸子代路径的父节点 "sea" + ASSERT_INT_EQ(C_ERR_OK, c_TrieST_Delete(&trie, "sea")); + ASSERT_INT_EQ(2, (int)trie.size); + ASSERT_TRUE(c_TrieST_Get(&trie, "sea") == NULL); + + // 🌟【硬核级联完整性验证】:虽然 "sea" 节点对应的 val 被剥离, + // 但因为它的后方还延伸驻留着有效长字符串分支 "seashells", + // 逆向惰性剪枝控制流应当自适应保留后面的子孙拓扑!断言 "seashells" 必须依然可以被完美高并发检索! + ASSERT_TRUE(c_TrieST_Contains(&trie, "seashells")); + + // 5. 移出完全孤立的悬空分支节点 "she" + ASSERT_INT_EQ(C_ERR_OK, c_TrieST_Delete(&trie, "she")); + ASSERT_INT_EQ(1, (int)trie.size); + + // 6. 统一解构注销,原路物理洗刷洗净所有残存节点与多态对象空间 + c_TrieST_Destroy(&trie); +} + +TEST_CASE(test_c_TrieST_ParamConstraints) { + c_TrieST_t local_trie; + c_TrieST_Init(&local_trie, NULL, NULL, NULL, &c_DefaultAllocator); + + RouteConfig_t dummy = { 999, "RAW" }; + // 5. 验证各类极值边界及非法参数的强拦截 + ASSERT_INT_EQ(C_ERR_PARAM, c_TrieST_Init(NULL, NULL, NULL, NULL, NULL)); + ASSERT_INT_EQ(C_ERR_PARAM, c_TrieST_Put(NULL, "toxic", &dummy)); + ASSERT_INT_EQ(C_ERR_EMPTY, c_TrieST_Delete(&local_trie, "toxic")); // 空仓删除安全返回 C_ERR_EMPTY + + c_TrieST_Destroy(&local_trie); +} + +TEST_CASE(test_c_TrieST_StringBuffer_HighLevelPipeline) { + c_TrieST_t trie; + c_TrieST_Init(&trie, NULL, NULL, NULL, &c_DefaultAllocator); + + int dummy_v = 1; + c_TrieST_Put(&trie, "api/v1/user", &dummy_v); + c_TrieST_Put(&trie, "api/v1/user/profile", &dummy_v); + c_TrieST_Put(&trie, "api/v1/user/profile/avatar", &dummy_v); + c_TrieST_Put(&trie, "api/v2/auth", &dummy_v); + c_TrieST_Put(&trie, "app/v1/user", &dummy_v); + + c_StringList_t result_list; + c_StringList_Init(&result_list, 4, 0); + + // 1. KeysWithPrefix 断言(前缀拼写补全联想) + c_err_t err = c_TrieST_KeysWithPrefix(&trie, "api/v1/user", &result_list); + ASSERT_INT_EQ(C_ERR_OK, err); + ASSERT_INT_EQ(3, (int)c_StringList_Size(&result_list)); + ASSERT_TRUE(strcmp("api/v1/user", c_StringList_Get(&result_list, 0)) == 0); + ASSERT_TRUE(strcmp("api/v1/user/profile/avatar", c_StringList_Get(&result_list, 2)) == 0); + + // 2. KeysThatMatch 断言(通配符并发拦截) + c_StringList_Clear(&result_list); + err = c_TrieST_KeysThatMatch(&trie, "ap./v1/user", &result_list); + ASSERT_INT_EQ(C_ERR_OK, err); + ASSERT_INT_EQ(2, (int)c_StringList_Size(&result_list)); + ASSERT_TRUE(strcmp("api/v1/user", c_StringList_Get(&result_list, 0)) == 0); + + // 3. LongestPrefixOf 断言(最长分级掩码前缀寻址) + c_StringList_Clear(&result_list); + const char* network_url = "api/v1/user/profile/settings/security_token"; + err = c_TrieST_LongestPrefixOf(&trie, network_url, &result_list); + ASSERT_INT_EQ(C_ERR_OK, err); + ASSERT_INT_EQ(1, (int)c_StringList_Size(&result_list)); + ASSERT_TRUE(strcmp("api/v1/user/profile", c_StringList_Get(&result_list, 0)) == 0); + + c_StringList_Destroy(&result_list); + c_TrieST_Destroy(&trie); +} + +// ========================================== +// 5. 主集成入口 +// ========================================== +int main(void) { + TEST_START(C_TrieST_R_waySymbolTable_TestSuite); + RUN_TEST(test_c_TrieST_Extreme_O_W_CRUD); + RUN_TEST(test_c_TrieST_ParamConstraints); + RUN_TEST(test_c_TrieST_StringBuffer_HighLevelPipeline); + TEST_REPORT(); + return (g_test_registry.failed_count > 0 ? 1 : 0); +} \ No newline at end of file diff --git a/String/c_TrieSet.c b/String/c_TrieSet.c new file mode 100644 index 0000000..8b19ed0 --- /dev/null +++ b/String/c_TrieSet.c @@ -0,0 +1,364 @@ +#include +#include + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define c_StringBuffer_PushBack c_StringBuffer_AppendChar + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * @brief 内部私有:安全开辟并清空一个全新的 Trie SET 节点 + */ +C_STATIC_FORCE_INLINE +c_TrieSetNode* c_TrieSet_CreateNode(c_Allocator_t* alloc) { + c_TrieSetNode* node = (c_TrieSetNode*)c_Allocator_Alloc(alloc, sizeof(c_TrieSetNode)); + if (!node) return NULL; + node->is_key = false; + memset(node->next, 0, sizeof(node->next)); // 256路子链接初始化清零 + return node; +} + +/** + * @brief 就地初始化多路单词查找树集合 + */ +c_err_t c_TrieSet_Init(c_TrieSet_t* self, c_Allocator_t* allocator) { + if (!self) { + return C_ERR_PARAM; + } + + if (allocator) { + self->allocator = *allocator; + } else { + self->allocator = c_DefaultAllocator; + } + + self->root = NULL; + self->size = 0; + + return C_ERR_OK; +} + +/** + * @brief 内部递归注入状态机 + */ +static c_err_t c_TrieSet_InternalAdd(c_TrieSet_t* self, c_TrieSetNode** node_ptr, const char* key, c_size_t d, bool* is_new_inserted) { + // 递归基 A:若下探路径上的节点为空,延迟惰性加载开辟新控制头 + if (*node_ptr == NULL) { + *node_ptr = c_TrieSet_CreateNode(&self->allocator); + if (*node_ptr == NULL) return C_ERR_NOMEM; + } + + c_TrieSetNode* curr = *node_ptr; + + // 递归基 B:成功定位推进匹配到了字符串键的物理末尾截止字符 '\0' + if (key[d] == '\0') { + if (curr->is_key) { + // 🌟【强去重拦截】:元素已在集合中完好存在,果断扔回 C_ERR_EXIST 阻止向下流转 + *is_new_inserted = false; + return C_ERR_EXIST; + } + curr->is_key = true; + *is_new_inserted = true; + return C_ERR_OK; + } + + // 多路高速变轨寻址:利用字符原生无符号代码作为下标一键穿透 + unsigned char c = (unsigned char)key[d]; + return c_TrieSet_InternalAdd(self, &(curr->next[c]), key, d + 1, is_new_inserted); +} + +/** + * @brief 向集合中注入添加一个变长元素键(自动去重,时间复杂度绝对锁定为 O(W)) + */ +c_err_t c_TrieSet_Add(c_TrieSet_t* self, const char* key) { + if (!self || !key) return C_ERR_PARAM; + + bool is_new = false; + c_err_t err = c_TrieSet_InternalAdd(self, &(self->root), key, 0, &is_new); + if (err == C_ERR_OK && is_new) { + self->size++; // 仅在去重确认后递增全局计数 + } + return err; +} + +/** + * @brief 内部下探滑窗辅助:根据指定字符串前缀寻找树内对应的局部根节点 + */ +static c_TrieSetNode* c_TrieSet_SubtreeSearch(c_TrieSetNode* x, const char* key, c_size_t d) { + if (x == NULL) return NULL; + if (key[d] == '\0') return x; + unsigned char c = (unsigned char)key[d]; + return c_TrieSet_SubtreeSearch(x->next[c], key, d + 1); +} + +/** + * @brief 包含性检索判定(时间复杂度为完美的常数均摊 O(W)) + */ +bool c_TrieSet_Contains(const c_TrieSet_t* self, const char* key) { + if (!self || !key || self->size == 0) return false; + c_TrieSetNode* x = c_TrieSet_SubtreeSearch(self->root, key, 0); + return (x != NULL && x->is_key); +} + +/** + * @brief 内部私有:安全走查当前节点下方 256 路是否还有残存子代分支 + */ +C_STATIC_FORCE_INLINE +bool c_TrieSet_HasChildren(const c_TrieSetNode* node) { + for (int r = 0; r < C_TRIE_R; r++) { + if (node->next[r] != NULL) return true; + } + return false; +} + +/** + * @brief 内部级联递归惰性剪枝删除状态机 + */ +static c_TrieSetNode* c_TrieSet_InternalRemove(c_TrieSet_t* self, c_TrieSetNode* x, const char* key, c_size_t d, c_err_t* out_err) { + if (x == NULL) { + *out_err = C_ERR_NOTFOUND; + return NULL; + } + + if (key[d] == '\0') { + if (x->is_key) { + x->is_key = false; // 抹除完整键标识 + self->size--; + *out_err = C_ERR_OK; + } else { + *out_err = C_ERR_NOTFOUND; + } + } else { + unsigned char c = (unsigned char)key[d]; + x->next[c] = c_TrieSet_InternalRemove(self, x->next[c], key, d + 1, out_err); + } + + // 🌟🌟🌟【自底向上绝对安全惰性剪枝防线】🌟🌟🌟 + // 退栈回溯时,若发现当前节点的元素完结标记已被剥离,且下方已无任何子代网络常驻 + if (!x->is_key) { + if (!c_TrieSet_HasChildren(x)) { + // 果断对该多路节点实施就地物理垃圾回收,返回 NULL 断开父链接挂载 + c_Allocator_Free(&self->allocator, x); + return NULL; + } + } + + return x; +} + +/** + * @brief 从有序集合中精准剔除一个指定的变长字符串元素(触发自适应级联剪枝) + */ +c_err_t c_TrieSet_Remove(c_TrieSet_t* self, const char* key) { + if (!self || !key) return C_ERR_PARAM; + if (self->size == 0) return C_ERR_EMPTY; + + c_err_t err = C_ERR_NOTFOUND; + self->root = c_TrieSet_InternalRemove(self, self->root, key, 0, &err); + return err; +} + +/** + * @brief 后序深度递归遍历释放辅助 + */ +static void c_TrieSet_InternalDestroy(c_Allocator_t* alloc, c_TrieSetNode* x) { + if (x == NULL) return; + for (int r = 0; r < C_TRIE_R; r++) { + c_TrieSet_InternalDestroy(alloc, x->next[r]); + } + c_Allocator_Free(alloc, x); +} + +/** + * @brief 集合彻底反初始化注销销毁 + */ +void c_TrieSet_Destroy(c_TrieSet_t* self) { + if (self && self->root) { + c_TrieSet_InternalDestroy(&self->allocator, self->root); + self->root = NULL; + self->size = 0; + } +} + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +/** + * @brief 内部静态辅助:下探滑窗寻找特定前缀所在的局部子树根节点 + */ +static c_TrieSetNode* c_TrieSet_FindSubtreeRoot(c_TrieSetNode* x, const char* prefix, c_size_t d) { + if (x == NULL) return NULL; + if (prefix[d] == '\0') return x; + unsigned char c = (unsigned char)prefix[d]; + return c_TrieSet_FindSubtreeRoot(x->next[c], prefix, d + 1); +} + +/** + * @brief 内部DFS辅助:供 KeysWithPrefix 前缀联想使用(由 c_StringBuffer_t 驱动) + */ +static c_err_t c_TrieSet_CollectPrefixDFS(c_TrieSetNode* x, c_StringBuffer_t* path_buf, c_StringList_t* results) { + if (x == NULL) return C_ERR_OK; + + // 1. 若当前路径构成集合内一个有效的完整唯一元素 + if (x->is_key) { + c_err_t err = c_StringList_Add(results, c_StringBuffer_CStr(path_buf)); + if (err != C_ERR_OK) return err; + } + + // 2. 单调向全 256 路子代分支网络执行就地滑动探测 + for (int r = 0; r < C_TRIE_R; r++) { + if (x->next[r] != NULL) { + // 就地前推:将当前分支字符压入自适应缓冲区,零堆碎片开销 + c_err_t err = c_StringBuffer_PushBack(path_buf, (char)r); + if (err != C_ERR_OK) return err; + + err = c_TrieSet_CollectPrefixDFS(x->next[r], path_buf, results); + if (err != C_ERR_OK) return err; + + // 就地回溯:探查完当前分支后,执行 PopBack 弹栈抹除 + c_StringBuffer_PopBack(path_buf); + } + } + return C_ERR_OK; +} + +/** + * @brief 🌟 集合高阶接口 1:前缀模糊联想匹配检索 + */ +c_err_t c_TrieSet_KeysWithPrefix(c_TrieSet_t* self, const char* prefix, c_StringList_t* results) { + if (!self || !prefix || !results) return C_ERR_PARAM; + if (self->size == 0 || self->root == NULL) return C_ERR_OK; + + c_TrieSetNode* subtree_root = c_TrieSet_FindSubtreeRoot(self->root, prefix, 0); + if (subtree_root == NULL) return C_ERR_OK; // 前缀路径未走通,平滑返回空行 + + // 初始化独立的弹性路径缓冲区,前置装填好已匹配的公共前缀 + c_StringBuffer_t path_buffer; + c_size_t prefix_len = strlen(prefix); + c_err_t err = c_StringBuffer_Init(&path_buffer, prefix_len + 16, &self->allocator); + if (err != C_ERR_OK) return err; + + c_StringBuffer_Append(&path_buffer, prefix, prefix_len); + + // 启动多路并行 DFS 状态机 + err = c_TrieSet_CollectPrefixDFS(subtree_root, &path_buffer, results); + + c_StringBuffer_Destroy(&path_buffer); // 原子回收局部缓冲区 + return err; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ + +/** + * @brief 内部递归辅助:供 KeysThatMatch 通配符正则走查使用(由 c_StringBuffer_t 驱动) + */ +static c_err_t c_TrieSet_MatchPatternRecursive(c_TrieSetNode* x, const char* pattern, c_size_t d, c_StringBuffer_t* path_buf, c_StringList_t* results) { + if (x == NULL) return C_ERR_OK; + + char current_pat = pattern[d]; + + // 递归基:模式串已步进到末尾截止位置 + if (current_pat == '\0') { + if (x->is_key) { + c_err_t err = c_StringList_Add(results, c_StringBuffer_CStr(path_buf)); + if (err != C_ERR_OK) return err; + } + return C_ERR_OK; + } + + // 🌟🌟🌟【通配符万能字符信道辐射状态机】🌟🌟🌟 + if (current_pat == '.') { + for (int r = 0; r < C_TRIE_R; r++) { + if (x->next[r] != NULL) { + c_StringBuffer_PushBack(path_buf, (char)r); // 就地前推 + + c_err_t err = c_TrieSet_MatchPatternRecursive(x->next[r], pattern, d + 1, path_buf, results); + if (err != C_ERR_OK) { + c_StringBuffer_PopBack(path_buf); + return err; + } + + c_StringBuffer_PopBack(path_buf); // 就地回溯恢复 + } + } + } else { + // 精确字符 O(1) 二级单路快速寻址 + unsigned char c = (unsigned char)current_pat; + if (x->next[c] != NULL) { + c_StringBuffer_PushBack(path_buf, (char)c); + + c_err_t err = c_TrieSet_MatchPatternRecursive(x->next[c], pattern, d + 1, path_buf, results); + if (err != C_ERR_OK) { + c_StringBuffer_PopBack(path_buf); + return err; + } + + c_StringBuffer_PopBack(path_buf); + } + } + + return C_ERR_OK; +} + +/** + * @brief 🌟 集合高阶接口 2:通配符正则全词模糊匹配接口 + */ +c_err_t c_TrieSet_KeysThatMatch(c_TrieSet_t* self, const char* pattern, c_StringList_t* results) { + if (!self || !pattern || !results) return C_ERR_PARAM; + if (self->size == 0 || self->root == NULL) return C_ERR_OK; + + c_StringBuffer_t path_buffer; + c_err_t err = c_StringBuffer_Init(&path_buffer, strlen(pattern) + 8, &self->allocator); + if (err != C_ERR_OK) return err; + + err = c_TrieSet_MatchPatternRecursive(self->root, pattern, 0, &path_buffer, results); + + c_StringBuffer_Destroy(&path_buffer); + return err; +} + +/* ------------------------------------------------------------------------------------------------------------------ */ + +/** + * @brief 内部寻阻辅助:供 LongestPrefixOf 深度爬坡状态机使用 + */ +static void c_TrieSet_FindLongestPrefixLength(c_TrieSetNode* x, const char* query, c_size_t d, c_size_t* out_longest_len) { + if (x == NULL) return; + if (x->is_key) { + *out_longest_len = d; // 沿途更新最大有效掩码位宽 + } + if (query[d] == '\0') return; + + unsigned char c = (unsigned char)query[d]; + c_TrieSet_FindLongestPrefixLength(x->next[c], query, d + 1, out_longest_len); +} + +/** + * @brief 🌟 集合高阶接口 3:最长前缀分级掩码路由寻址 + */ +c_err_t c_TrieSet_LongestPrefixOf(c_TrieSet_t* self, const char* query, c_StringList_t* results) { + if (!self || !query || !results) return C_ERR_PARAM; + if (self->size == 0 || self->root == NULL) return C_ERR_OK; + + c_size_t longest_match_length = 0; + c_TrieSet_FindLongestPrefixLength(self->root, query, 0, &longest_match_length); + + if (longest_match_length == 0 && !self->root->is_key) { + return C_ERR_NOTFOUND; + } + + // 利用 StringBuffer 直接截断装填该寻址段 + c_StringBuffer_t path_buffer; + c_err_t err = c_StringBuffer_Init(&path_buffer, longest_match_length + 4, &self->allocator); + if (err != C_ERR_OK) return err; + + c_StringBuffer_Append(&path_buffer, query, longest_match_length); + + err = c_StringList_Add(results, c_StringBuffer_CStr(&path_buffer)); + + c_StringBuffer_Destroy(&path_buffer); + return err; +} diff --git a/String/c_TrieSet.h b/String/c_TrieSet.h new file mode 100644 index 0000000..ad1b42b --- /dev/null +++ b/String/c_TrieSet.h @@ -0,0 +1,55 @@ +#ifndef INCLUDED_C_TRIESET_H +#define INCLUDED_C_TRIESET_H + +#ifndef INCLUDED_C_TYPES_H +#include +#endif /*INCLUDED_C_TYPES_H*/ + +#ifndef INCLUDED_C_ALLOCATOR_H +#include +#endif /*INCLUDED_C_ALLOCATOR_H*/ + +#ifndef INCLUDED_C_STRINGLIST_H +#include +#endif /*INCLUDED_C_STRINGLIST_H*/ + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +#define C_TRIE_R 256 + +typedef struct c_TrieSetNode { + bool is_key; // 标志位:true 代表从根到此节点的路径构成集合内一个有效的唯一元素 + struct c_TrieSetNode* next[C_TRIE_R]; // 密集平铺的 256 路子节点二级指针控制控制总线 +} c_TrieSetNode; + +typedef struct { + c_TrieSetNode* root; // 集合树的根节点指针 + c_size_t size; // 当前集合内有效驻留的唯一元素总个数 + c_Allocator_t allocator; // 内联组合分配器实例与自适应 Fallback 缺省机制 +} c_TrieSet_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +/* */ + +c_err_t c_TrieSet_Init(c_TrieSet_t* self, c_Allocator_t* allocator); + +c_err_t c_TrieSet_Add(c_TrieSet_t* self, const char* key); + +bool c_TrieSet_Contains(const c_TrieSet_t* self, const char* key); + +c_err_t c_TrieSet_Remove(c_TrieSet_t* self, const char* key); + +void c_TrieSet_Destroy(c_TrieSet_t* self); + +C_STATIC_FORCE_INLINE +bool c_TrieSet_IsEmpty(const c_TrieSet_t* self) { + if (!self) return true; + return self->size==0; +} + +c_err_t c_TrieSet_KeysWithPrefix(c_TrieSet_t* self, const char* prefix, c_StringList_t* results); +c_err_t c_TrieSet_KeysThatMatch(c_TrieSet_t* self, const char* pattern, c_StringList_t* results); +c_err_t c_TrieSet_LongestPrefixOf(c_TrieSet_t* self, const char* query, c_StringList_t* results); + +#endif /*INCLUDED_C_TRIESET_H*/ diff --git a/String/c_TrieSet.t.c b/String/c_TrieSet.t.c new file mode 100644 index 0000000..6559f5c --- /dev/null +++ b/String/c_TrieSet.t.c @@ -0,0 +1,108 @@ +#include "c_TrieSet.h" +#include "c_Test.h" +#include +#include + +TEST_CASE(test_c_TrieSet_UniqueUnsignedFlow) { + c_TrieSet_t set; + + // 初始化变长集合,检验默认 Fallback 降级分配器播种 + c_err_t err = c_TrieSet_Init(&set, NULL); + ASSERT_INT_EQ(C_ERR_OK, err); + ASSERT_INT_EQ(0, (int)set.size); + + // 1. Add 基础添加与 Contains 判定 + ASSERT_INT_EQ(C_ERR_OK, c_TrieSet_Add(&set, "sea")); + ASSERT_INT_EQ(C_ERR_OK, c_TrieSet_Add(&set, "seashells")); + ASSERT_INT_EQ(C_ERR_OK, c_TrieSet_Add(&set, "she")); + ASSERT_INT_EQ(3, (int)set.size); + + ASSERT_TRUE(c_TrieSet_Contains(&set, "seashells")); + ASSERT_TRUE(!c_TrieSet_Contains(&set, "seash")); // 物理路径虽通但非有效键完结,必须卡死返回 false + + // 2. 🌟【绝杀点 1:去重防御】:尝试向集合再次强注已存在的同名元素 "sea" + // 控制流必须前置拦截,交回标准的 C_ERR_EXIST 异常状态,全局集合计数死守为 3 守恒不变! + ASSERT_INT_EQ(C_ERR_EXIST, c_TrieSet_Add(&set, "sea")); + ASSERT_INT_EQ(3, (int)set.size); + + // 3. 🌟【绝杀点 2:自底向上惰性剪枝】:移出带有嵌套延伸子代分支的枢纽根节点 "sea" + ASSERT_INT_EQ(C_ERR_OK, c_TrieSet_Remove(&set, "sea")); + ASSERT_INT_EQ(2, (int)set.size); + ASSERT_TRUE(!c_TrieSet_Contains(&set, "sea")); + + // 级联拓扑检查:因为 "sea" 的后方还衍生着独立有效元素 "seashells", + // 惰性剪枝控制流应当自适应切断 sea 处的标记,但必须完美完好保留长后缀的子孙分支! + ASSERT_TRUE(c_TrieSet_Contains(&set, "seashells")); + + // 4. 移出完全孤立的独苗元素 "she" + ASSERT_INT_EQ(C_ERR_OK, c_TrieSet_Remove(&set, "she")); + ASSERT_INT_EQ(1, (int)set.size); + + // 彻底解构注销 + c_TrieSet_Destroy(&set); +} + +TEST_CASE(test_c_TrieSet_Toxicity_Defenses) { + c_TrieSet_t local_set; + c_TrieSet_Init(&local_set, &c_DefaultAllocator); + + // 5. 验证极值边界及非法参数的强过滤拦截 + ASSERT_INT_EQ(C_ERR_PARAM, c_TrieSet_Init(NULL, NULL)); + ASSERT_INT_EQ(C_ERR_PARAM, c_TrieSet_Add(NULL, "toxic")); + ASSERT_INT_EQ(C_ERR_EMPTY, c_TrieSet_Remove(&local_set, "toxic")); // 空仓删除安全返回 C_ERR_EMPTY + + c_TrieSet_Destroy(&local_set); +} + +TEST_CASE(test_c_TrieSet_StringBuffer_Pipeline) { + c_TrieSet_t set; + c_TrieSet_Init(&set, &c_DefaultAllocator); + + // 密布注入去重键 + c_TrieSet_Add(&set, "api/v1/user"); + c_TrieSet_Add(&set, "api/v1/user/profile"); + c_TrieSet_Add(&set, "api/v1/user/profile/avatar"); + c_TrieSet_Add(&set, "api/v2/auth"); + c_TrieSet_Add(&set, "app/v1/user"); + + c_StringList_t result_list; + c_StringList_Init(&result_list, 4, &c_DefaultAllocator); + + // 1. KeysWithPrefix 前缀联想测试 + c_err_t err = c_TrieSet_KeysWithPrefix(&set, "api/v1/user", &result_list); + ASSERT_INT_EQ(C_ERR_OK, err); + ASSERT_INT_EQ(3, (int)c_StringList_Size(&result_list)); + ASSERT_TRUE(strcmp("api/v1/user", c_StringList_Get(&result_list, 0)) == 0); + ASSERT_TRUE(strcmp("api/v1/user/profile/avatar", c_StringList_Get(&result_list, 2)) == 0); + + // 2. KeysThatMatch 通配符正则走查 + c_StringList_Clear(&result_list); + err = c_TrieSet_KeysThatMatch(&set, "ap./v1/user", &result_list); + ASSERT_INT_EQ(C_ERR_OK, err); + ASSERT_INT_EQ(2, (int)c_StringList_Size(&result_list)); + ASSERT_TRUE(strcmp("api/v1/user", c_StringList_Get(&result_list, 0)) == 0); + + // 3. LongestPrefixOf 掩码最长前缀分级寻址 + c_StringList_Clear(&result_list); + const char* network_url = "api/v1/user/profile/settings/security_token"; + err = c_TrieSet_LongestPrefixOf(&set, network_url, &result_list); + ASSERT_INT_EQ(C_ERR_OK, err); + ASSERT_INT_EQ(1, (int)c_StringList_Size(&result_list)); + ASSERT_TRUE(strcmp("api/v1/user/profile", c_StringList_Get(&result_list, 0)) == 0); + + c_StringList_Destroy(&result_list); + c_TrieSet_Destroy(&set); +} + +// ========================================== +// 5. 主集成入口 +// ========================================== +int main(void) { + TEST_START(C_TrieSET_UniqueUnsigned_TestSuite); + RUN_TEST(test_c_TrieSet_UniqueUnsignedFlow); + RUN_TEST(test_c_TrieSet_Toxicity_Defenses); + + RUN_TEST(test_c_TrieSet_StringBuffer_Pipeline); + TEST_REPORT(); + return (g_test_registry.failed_count > 0 ? 1 : 0); +}