Files
cKit/Search/c_BinarySearchST.c
T

193 lines
7.4 KiB
C
Raw Normal View History

2026-08-30 22:24:45 +08:00
#include <c_BinarySearchST.h>
/**
* @brief 核心内部操作:基于无符号安全的左闭右开二分 Rank 检索
*
* @return c_size_t 返回小于指定 key 的键的总个数。该位置就是精确命中点或完美插入点
*/
static c_size_t c_BinarySearchST_Rank(const c_BinarySearchST_t* self, const void* key) {
c_size_t low = 0;
c_size_t high = self->size; // 右边界设为 size(开区间),这样 high 永远不会因 mid-1 下溢
while (low < high) { // 🌟 开区间控制条件为 low < high,彻底杜绝死循环
c_size_t mid = low + ((high - low) >> 1);
char* mid_key = self->keys + (mid * self->key_size);
int cmp_res = self->cmp(key, mid_key, self->args);
if (cmp_res < 0) {
high = mid; // 🌟 目标比 mid 小,安全收缩右开边界,完全斩断减法下溢
} else if (cmp_res > 0) {
low = mid + 1; // 目标比 mid 大,安全收缩左闭边界
} else {
return mid; // 精确命中,返回对应的物理下标位置
}
}
return low; // 未命中,返回当前最精准的插入点插槽索引
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
c_err_t c_BinarySearchST_Init(c_BinarySearchST_t* self, c_size_t initial_capacity, c_size_t key_size, c_size_t val_size, c_SortCompare_t cmp, void* args, c_Allocator_t* allocator) {
if (!self || key_size == 0 || val_size == 0 || !cmp) {
return C_ERR_PARAM;
}
// 自适应分配器降级缺省安全播种
if (allocator) {
self->allocator = *allocator;
} else {
self->allocator = c_DefaultAllocator;
}
c_size_t cap = (initial_capacity > 0) ? initial_capacity : 4;
// 前置逆向除法溢出安全审计
if (((c_size_t)-1) / key_size < cap) return C_ERR_NOMEM;
if (((c_size_t)-1) / val_size < cap) return C_ERR_NOMEM;
self->keys = (char*)c_Allocator_Alloc(&self->allocator, cap * key_size);
self->vals = (char*)c_Allocator_Alloc(&self->allocator, cap * val_size);
if (!self->keys || !self->vals) {
if (self->keys) c_Allocator_Free(&self->allocator, self->keys);
if (self->vals) c_Allocator_Free(&self->allocator, self->vals);
return C_ERR_NOMEM;
}
self->capacity = cap;
self->size = 0;
self->key_size = key_size;
self->val_size = val_size;
self->cmp = cmp;
self->args = args;
return C_ERR_OK;
}
/**
* @brief 根据指定的 Key 检索关联的 Value 值(时间复杂度 O(log N)
*/
c_err_t c_BinarySearchST_Get(const c_BinarySearchST_t* self, const void* key, void* out_val) {
if (!self || !key || !out_val) return C_ERR_PARAM;
if (self->size == 0) return C_ERR_EMPTY;
c_size_t i = c_BinarySearchST_Rank(self, key);
// 如果检索出的位置合法,且对应的键与其等价,说明精确命中
if (i < self->size && self->cmp(key, self->keys + (i * self->key_size), self->args) == 0) {
memcpy(out_val, self->vals + (i * self->val_size), self->val_size);
return C_ERR_OK;
}
return C_ERR_NOTFOUND;
}
/**
* @brief 检查符号表内是否包含指定的 Key 键值
*/
bool c_BinarySearchST_Contains(const c_BinarySearchST_t* self, const void* key) {
if (!self || !key || self->size == 0) return false;
c_size_t i = c_BinarySearchST_Rank(self, key);
return (i < self->size && self->cmp(key, self->keys + (i * self->key_size), self->args) == 0);
}
/**
* @brief 存入键值对。若已存在则覆写更新;若不存在则对后方数据滑窗后移,插入并维持有序性
*/
c_err_t c_BinarySearchST_Put(c_BinarySearchST_t* self, const void* key, const void* val) {
if (!self || !key || !val) return C_ERR_PARAM;
c_size_t i = c_BinarySearchST_Rank(self, key);
// 1. 如果键值已经存在,直接更新它的值(覆写语义)
if (i < self->size && self->cmp(key, self->keys + (i * self->key_size), self->args) == 0) {
memcpy(self->vals + (i * self->val_size), val, self->val_size);
return C_ERR_OK;
}
// 2. 键值不存在,准备执行新元素插入。前置判定自适应弹性动态扩容
if (self->size >= self->capacity) {
c_size_t old_cap = self->capacity;
// 算术乘法整数溢出除法拦截防护
if (((c_size_t)-1) / self->key_size < (old_cap << 1)) return C_ERR_NOMEM;
if (((c_size_t)-1) / self->val_size < (old_cap << 1)) return C_ERR_NOMEM;
c_size_t new_cap = old_cap << 1;
c_size_t old_key_bytes = old_cap * self->key_size;
c_size_t new_key_bytes = new_cap * self->key_size;
c_size_t old_val_bytes = old_cap * self->val_size;
c_size_t new_val_bytes = new_cap * self->val_size;
char* new_keys = (char*)c_Allocator_Realloc(&self->allocator, self->keys, old_key_bytes, new_key_bytes);
char* new_vals = (char*)c_Allocator_Realloc(&self->allocator, self->vals, old_val_bytes, new_val_bytes);
if (!new_keys || !new_vals) {
// 部分成功安全回退
if (new_keys) self->keys = new_keys;
if (new_vals) self->vals = new_vals;
return C_ERR_NOMEM;
}
self->keys = new_keys;
self->vals = new_vals;
self->capacity = new_cap;
}
// 3. 核心数据平移:将区间 [i, size-1] 内的所有键值对数据,单向全部后移一位给新插入留出位置
// 控制条件为 j > i。即便 i 为最左侧的 0,j 最小减到 1 就会强制退出,彻底封死无符号自减下溢
for (c_size_t j = self->size; j > i; j--) {
memcpy(self->keys + (j * self->key_size), self->keys + ((j - 1) * self->key_size), self->key_size);
memcpy(self->vals + (j * self->val_size), self->vals + ((j - 1) * self->val_size), self->val_size);
}
// 4. 将新元素深拷贝写入空出来的有序安全插槽中
memcpy(self->keys + (i * self->key_size), key, self->key_size);
memcpy(self->vals + (i * self->val_size), val, self->val_size);
self->size++;
return C_ERR_OK;
}
/**
* @brief 根据指定 Key 彻底从有序表中斩断移出指定对,并对其后方数据滑窗前移覆盖
*/
c_err_t c_BinarySearchST_Delete(c_BinarySearchST_t* self, const void* key) {
if (!self || !key) return C_ERR_PARAM;
if (self->size == 0) return C_ERR_EMPTY;
c_size_t i = c_BinarySearchST_Rank(self, key);
// 键值未找到拦截
if (i >= self->size || self->cmp(key, self->keys + (i * self->key_size), self->args) != 0) {
return C_ERR_NOTFOUND;
}
// 核心数据平移:将区间 [i+1, size-1] 内的元素统一向前推进覆盖一位
// 纯单调递增控制,num 边界有效拦截
c_size_t limit = self->size - 1;
for (c_size_t j = i; j < limit; j++) {
memcpy(self->keys + (j * self->key_size), self->keys + ((j + 1) * self->key_size), self->key_size);
memcpy(self->vals + (j * self->val_size), self->vals + ((j + 1) * self->val_size), self->val_size);
}
self->size--;
return C_ERR_OK;
}
/**
* @brief 有序表反初始化数据销毁
*/
void c_BinarySearchST_Destroy(c_BinarySearchST_t* self) {
if (self) {
if (self->keys) c_Allocator_Free(&self->allocator, self->keys);
if (self->vals) c_Allocator_Free(&self->allocator, self->vals);
self->keys = NULL;
self->vals = NULL;
self->size = 0;
self->capacity = 0;
}
}