79 lines
3.1 KiB
C
79 lines
3.1 KiB
C
#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;
|
|
}
|