Files
cKit/String/c_LSD.c
T

79 lines
3.1 KiB
C
Raw Normal View History

2026-08-31 22:49:42 +08:00
#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;
}