String
This commit is contained in:
@@ -0,0 +1,278 @@
|
||||
#include "c_Hex.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------------------- */
|
||||
/* */
|
||||
|
||||
#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<size; i++){
|
||||
unsigned char b = BYTE_AT(data, i);
|
||||
for(int8_t d=7; d>=0; d--){
|
||||
putchar((b & (1<<d))?'1':'0');
|
||||
}
|
||||
if(((i+1)*8)%line_size==0 || (i+1)==size){
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void c_Hex_Dump(const void *data, c_size_t size) {
|
||||
c_Hex_DumpTo(data, size, (void (*)(const char *, ...)) printf);
|
||||
}
|
||||
|
||||
void c_Hex_DumpTo(const void *data, c_size_t size, void (*dump)(const char*, ...)) {
|
||||
char ascii[17];
|
||||
size_t i, j;
|
||||
ascii[16] = '\0';
|
||||
for (i = 0; i < size; ++i) {
|
||||
if (i % 16 == 0) {
|
||||
dump("%08"C_PRIx" ", i);
|
||||
}
|
||||
dump("%02X ", ((unsigned char*)data)[i]);
|
||||
if (((unsigned char*)data)[i] >= ' ' && ((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<data_size; i++){
|
||||
// const c_size_t index = (byteOrder == kByteOrder_BigEndian) ? i : (data_size - i - 1);
|
||||
// buffer[(i<<1)] = (char)lookup[(data[index] >> 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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
#ifndef INCLUDED_C_HEX_H
|
||||
#define INCLUDED_C_HEX_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#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*/
|
||||
@@ -0,0 +1,90 @@
|
||||
#include "c_Hex.h"
|
||||
#include "c_Test.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
@@ -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*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <c_StringList.h>
|
||||
|
||||
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) 经典原位原地重排去重流
|
||||
|
||||
+11
-11
@@ -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]; // 零拷贝原位返回
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
#include <c_AmericanFlag.h>
|
||||
#include <c_ArrayStack.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef INCLUDED_C_AMERICANFLAG_H
|
||||
#define INCLUDED_C_AMERICANFLAG_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#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*/
|
||||
@@ -0,0 +1,162 @@
|
||||
#include "c_AmericanFlag.h"
|
||||
#include "c_Test.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
#include <c_BoyerMoore.h>
|
||||
|
||||
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; c<R; c++) {
|
||||
self->right[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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef INCLUDED_C_BOYERMOORE_H
|
||||
#define INCLUDED_C_BOYERMOORE_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#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*/
|
||||
@@ -0,0 +1,63 @@
|
||||
#include "c_BoyerMoore.h"
|
||||
#include "c_Test.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
#include <c_InplaceMSD.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef INCLUDED_C_INPLACEMSD_H
|
||||
#define INCLUDED_C_INPLACEMSD_H
|
||||
|
||||
#ifndef INCLUDED_C_MSD_H
|
||||
#include <c_MSD.h>
|
||||
#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*/
|
||||
@@ -0,0 +1,93 @@
|
||||
#include "c_InplaceMSD.h"
|
||||
#include "c_Test.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#include <c_LSD.h>
|
||||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef INCLUDED_C_LSD_H
|
||||
#define INCLUDED_C_LSD_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#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*/
|
||||
@@ -0,0 +1,66 @@
|
||||
#include "c_LSD.h"
|
||||
#include "c_Test.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
#include <c_MSD.h>
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
// 小区间降级截断阈值,小于该元素个数的碎子树不再分配计数桶,直接转产插入排序提速
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef INCLUDED_C_MSD_H
|
||||
#define INCLUDED_C_MSD_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#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*/
|
||||
@@ -0,0 +1,66 @@
|
||||
#include "c_MSD.h"
|
||||
#include "c_Test.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
#include <c_Quick3string.h>
|
||||
|
||||
// 小区间自适应截断降级阈值(小于此体量的碎区间直接交付级联插入排序专线)
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef INCLUDED_C_QUICK3STRING_H
|
||||
#define INCLUDED_C_QUICK3STRING_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#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*/
|
||||
@@ -0,0 +1,86 @@
|
||||
#include "c_Quick3string.h"
|
||||
#include "c_Test.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
#include <c_RabinKarp.h>
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef INCLUDED_C_RABINKARP_H
|
||||
#define INCLUDED_C_RABINKARP_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#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*/
|
||||
@@ -0,0 +1,65 @@
|
||||
#include "c_RabinKarp.h"
|
||||
#include "c_Test.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,412 @@
|
||||
#include <c_TrieST.h>
|
||||
#include <c_StringBuffer.h>
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
#ifndef INCLUDED_C_TRIEST_H
|
||||
#define INCLUDED_C_TRIEST_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#endif /*INCLUDED_C_ALLOCATOR_H*/
|
||||
|
||||
#ifndef INCLUDED_C_STRINGLIST_H
|
||||
#include <c_StringList.h>
|
||||
#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*/
|
||||
@@ -0,0 +1,147 @@
|
||||
#include "c_TrieST.h"
|
||||
#include "c_Test.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
#include <c_TrieSet.h>
|
||||
#include <c_StringBuffer.h>
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef INCLUDED_C_TRIESET_H
|
||||
#define INCLUDED_C_TRIESET_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#endif /*INCLUDED_C_ALLOCATOR_H*/
|
||||
|
||||
#ifndef INCLUDED_C_STRINGLIST_H
|
||||
#include <c_StringList.h>
|
||||
#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*/
|
||||
@@ -0,0 +1,108 @@
|
||||
#include "c_TrieSet.h"
|
||||
#include "c_Test.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user