193 lines
7.4 KiB
C
193 lines
7.4 KiB
C
#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;
|
||
}
|
||
}
|