Files
cKit/Foundation/c_StrIndexKmp.c
T

89 lines
3.0 KiB
C
Raw Normal View History

2026-08-30 03:52:23 +08:00
#include <c_StrIndexKmp.h>
#include <c_Memory.h>
/**
* @brief 私有辅助内部函数:为模式串动态解包并构建 KMP 高速失配跳转跳转表
*/
C_STATIC_FORCE_INLINE
c_err_t c_Kmp_BuildNextTable(const uint8_t* p, c_size_t m, c_size_t* next) {
next[0] = 0; // KMP 起始基石
c_size_t len = 0; // 前缀的核心长度
c_size_t i = 1;
while (i < m) {
if (p[i] == p[len]) {
len++;
next[i] = len;
i++;
} else {
if (len != 0) {
len = next[len - 1]; // 顺着失配位置向左回溯
} else {
next[i] = 0;
i++;
}
}
}
return C_ERR_OK;
}
c_size_t c_StrIndexKmpEx(const void* text, c_size_t text_len,
const void* pattern, c_size_t pattern_len,
c_size_t start_offset, c_Allocator_t* allocator) {
// 1. 强御级无效边界防御
if (!text || !pattern) return C_KMP_NOT_FOUND;
const uint8_t* t = (const uint8_t*)text;
const uint8_t* p = (const uint8_t*)pattern;
// 如果传入 0 则自适应兼容裸字符串
c_size_t n = (text_len == 0) ? (c_size_t)strlen((const char*)text) : text_len;
c_size_t m = (pattern_len == 0) ? (c_size_t)strlen((const char*)pattern) : pattern_len;
if (m == 0 || n == 0 || start_offset >= n || m > (n - start_offset)) {
return C_KMP_NOT_FOUND;
}
c_Allocator_t* alloc = (allocator != NULL) ? allocator : &c_DefaultAllocator;
// 2. 【安全机制】:坚决不使用栈上的局部可变长数组(VLA),防止被大特征串顶爆引发 Stack Overflow
// 从内部绑定的专属多态分配器中动态申领物理块资源
c_size_t* next = (c_size_t*)c_Allocator_Alloc(alloc, m * sizeof(c_size_t));
if (!next) return C_KMP_NOT_FOUND;
// 3. 构建 KMP 高速失配加速表
c_Kmp_BuildNextTable(p, m, next);
// 4. 开始执行双指针拉平线性单向匹配检索流 (无回溯 O(M+N))
c_size_t i = start_offset; // 文本大字符串的推进指针
c_size_t j = 0; // 模式串的局部指针
while (i < n) {
if (t[i] == p[j]) {
i++;
j++;
}
if (j == m) {
// 完美踩中特征指纹,捕获命中!
c_size_t found_idx = i - j;
// 强异常安全性物理火化:归还申领的跳转数组块,杜绝堆碎片跑飞
c_Allocator_Free(alloc, next);
return found_idx; // 返回命中位置相对于首地址的绝对物理偏移量
}
else if (i < n && t[i] != p[j]) {
// 发生失配抖动,KMP 的精髓:文本指针 i 绝对不后退,只原位等待,而模式串指针 j 顺着失配表向左高速跳转
if (j != 0) {
j = next[j - 1];
} else {
i++;
}
}
}
// 全文扫描结束,未发生特征匹配,体面卸载资源退出
c_Allocator_Free(alloc, next);
return C_KMP_NOT_FOUND;
}