Files
cKit/Search/c_BinarySearchST.c
T
2026-08-30 22:24:45 +08:00

193 lines
7.4 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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;
}
}