Search / Sort

This commit is contained in:
2026-08-31 11:50:04 +08:00
parent 0cb98557da
commit 109e8af3d5
10 changed files with 818 additions and 219 deletions
+61 -5
View File
@@ -75,9 +75,8 @@ c_err_t c_ArrayList_Resize(c_ArrayList_t* self, c_size_t new_capacity) {
c_err_t c_ArrayList_Add(c_ArrayList_t* self, const void* item) {
if (!self || !item) return C_ERR_PARAM;
if (self->size>=self->capacity) {
c_err_t err = c_ArrayList_Resize(self, (self->capacity==0)?4:(self->capacity<<1));
if (err!=C_ERR_OK) return err;
if (self->size >= self->capacity) {
return C_ERR_FULL;
}
char* target = (char*)self->array + (self->size * self->item_size);
memcpy(target, item, self->item_size);
@@ -106,8 +105,11 @@ c_err_t c_ArrayList_Read(const c_ArrayList_t* self, c_size_t index, void* out_it
return C_ERR_OK;
}
c_err_t c_ArrayList_Remove(c_ArrayList_t* self, c_size_t index, void* out_item) {
if (!self || index>=self->size) return C_ERR_PARAM;
c_err_t c_ArrayList_RemoveAt(c_ArrayList_t* self, c_size_t index, void* out_item) {
if (!self) return C_ERR_PARAM;
if (self->size == 0) return C_ERR_EMPTY;
if (index >= self->size) return C_ERR_OUTOFBOUND;
char* target = (char*)self->array + (index * self->item_size);
if (out_item) {
@@ -124,3 +126,57 @@ c_err_t c_ArrayList_Remove(c_ArrayList_t* self, c_size_t index, void* out_item)
return C_ERR_OK;
}
c_err_t c_ArrayList_Set(const c_ArrayList_t* self, c_size_t index, const void* item) {
if (!self || !item) return C_ERR_PARAM;
if (index >= self->size) return C_ERR_OUTOFBOUND;
char* target = (char*)self->array + (index * self->item_size);
memcpy(target, item, self->item_size);
return C_ERR_OK;
}
c_err_t c_ArrayList_PopBack(c_ArrayList_t* self, void* out_item) {
if (!self ) return C_ERR_PARAM;
if (self->size==0) return C_ERR_EMPTY;
self->size--;
if (out_item) {
char* target = (char*)self->array + (self->size) * self->item_size;
memcpy(out_item, target, self->item_size);
}
return C_ERR_OK;
}
c_err_t c_ArrayList_PopBackResize(c_ArrayList_t* self, void* out_item) {
if (!self ) return C_ERR_PARAM;
if (self->size==0) return C_ERR_EMPTY;
self->size--;
if (out_item) {
char* target = (char*)self->array + (self->size) * self->item_size;
memcpy(out_item, target, self->item_size);
}
// 🌟【弹性内存缩容锁】:若当前存量过低 (N <= Capacity/4) 且长于基本配额,主动腰斩退还内存给系统
if (self->size > 0 && self->size <= (self->capacity >> 2)) {
c_ArrayList_Resize(self, self->capacity >> 1);
}
return C_ERR_OK;
}
c_err_t c_ArrayList_ResizeAdd(c_ArrayList_t* self, const void* item) {
if (!self || !item) return C_ERR_PARAM;
if (self->size >= self->capacity) {
c_err_t err = c_ArrayList_Resize(self, (self->capacity==0)?4:(self->capacity<<1));
if (err!=C_ERR_OK) return err;
}
char* target = (char*)self->array + (self->size * self->item_size);
memcpy(target, item, self->item_size);
self->size++;
return C_ERR_OK;
}
+10 -1
View File
@@ -37,7 +37,15 @@ void* c_ArrayList_Get(const c_ArrayList_t* self, c_size_t index);
c_err_t c_ArrayList_Read(const c_ArrayList_t* self, c_size_t index, void* out_item);
c_err_t c_ArrayList_Remove(c_ArrayList_t* self, c_size_t index, void* out_item);
c_err_t c_ArrayList_Set(const c_ArrayList_t* self, c_size_t index, const void* item);
c_err_t c_ArrayList_RemoveAt(c_ArrayList_t* self, c_size_t index, void* out_item);
c_err_t c_ArrayList_PopBack(c_ArrayList_t* self, void* out_item) ;
c_err_t c_ArrayList_PopBackResize(c_ArrayList_t* self, void* out_item);
c_err_t c_ArrayList_ResizeAdd(c_ArrayList_t* self, const void* item);
C_STATIC_FORCE_INLINE
c_size_t c_ArrayList_Size(const c_ArrayList_t* self) {
@@ -45,4 +53,5 @@ c_size_t c_ArrayList_Size(const c_ArrayList_t* self) {
return self->size;
}
#endif /*INCLUDED_C_ARRAYLIST_H*/
+89 -212
View File
@@ -7,246 +7,123 @@
/* */
typedef struct {
size_t active_allocations;
size_t total_alloc_bytes;
size_t total_free_bytes;
size_t realloc_calls;
size_t realloc_in_place_count; // 记录有多少次 Realloc 实现了就地复用(模拟伙伴系统)
} TestMemoryTracker_t;
uint32_t uid;
int data_block;
} UserPayload_t;
static TestMemoryTracker_t g_tracker = {0};
static size_t mock_buddy_power_of_two(size_t size) {
if (size == 0) return 0;
size_t p = 1;
while (p < size) p <<= 1;
return p;
}
static void* test_alloc(size_t size, void* ctx) {
TestMemoryTracker_t* tracker = (TestMemoryTracker_t*)ctx;
if (tracker) {
tracker->active_allocations++;
tracker->total_alloc_bytes += size;
}
return malloc(size);
}
static void test_free(void* ptr, void* ctx) {
TestMemoryTracker_t* tracker = (TestMemoryTracker_t*)ctx;
if (ptr && tracker) {
tracker->active_allocations--;
}
free(ptr);
}
static void* test_realloc(void* ptr, size_t old_size, size_t new_size, void* ctx) {
TestMemoryTracker_t* tracker = (TestMemoryTracker_t*)ctx;
if (tracker) tracker->realloc_calls++;
if (new_size == 0) {
test_free(ptr, ctx);
if (tracker) tracker->total_free_bytes += old_size;
return NULL;
}
if (!ptr) {
return test_alloc(new_size, ctx);
}
// 【模拟伙伴系统核心逻辑】:如果新旧大小落在同一个 2 的幂阶数区间,则直接原地返回,零搬迁!
size_t old_buddy = mock_buddy_power_of_two(old_size);
size_t new_buddy = mock_buddy_power_of_two(new_size);
if (old_buddy == new_buddy && old_buddy != 0) {
if (tracker) tracker->realloc_in_place_count++;
return ptr;
}
// 阶数改变,模拟搬迁
void* new_ptr = malloc(new_size);
if (!new_ptr) return NULL;
size_t copy_size = (old_size < new_size) ? old_size : new_size;
memcpy(new_ptr, ptr, copy_size);
free(ptr);
if (tracker) {
tracker->total_alloc_bytes += new_size;
tracker->total_free_bytes += old_size;
}
return new_ptr;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
// 供 RUN_TEST_FIXTURE 使用的 Setup 和 Teardown 钩子
static void custom_allocator_setup(void) {
memset(&g_tracker, 0, sizeof(TestMemoryTracker_t));
}
static void custom_allocator_teardown(void) {
// 每次测试结束,严格确保没有发生内存泄漏
// 注意:不能在此处直接调用带有返回的断言,因为破坏了测试函数的封装,仅在内部测试中做二次校验或由测试用例本身断言。
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
typedef struct {
int node_id;
float threshold;
char name[16];
} SensorNode_t;
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
// 测试一:基础生命周期与边界防御测试
TEST_CASE(test_lifecycle_and_defense) {
TEST_CASE(test_c_ArrayList_CRUD) {
c_ArrayList_t list;
// 初始化物理插槽总上限被死死限制为 2 的轻量级列表
c_err_t err = c_ArrayList_Init(&list, sizeof(UserPayload_t), 2, &c_DefaultAllocator);
ASSERT_INT_EQ(C_ERR_OK, err);
ASSERT_INT_EQ(0, (int)c_ArrayList_Size(&list)); // 验证内联函数 c_ArrayList_Size 是否正确返回当前 0
ASSERT_INT_EQ(0, (int)list.size);
// 防御测试:无效参数传入
ASSERT_INT_EQ_MSG(C_ERR_PARAM, c_ArrayList_Init(NULL, sizeof(int), 4, NULL), "Should return C_ERR_PARAM when self is NULL");
ASSERT_INT_EQ_MSG(C_ERR_PARAM, c_ArrayList_Init(&list, 0, 4, NULL), "Should return C_ERR_PARAM when item_size is 0");
ASSERT_INT_EQ(0, c_ArrayList_Size(NULL));
UserPayload_t p1 = { 8001, 10 };
UserPayload_t p2 = { 8002, 20 };
UserPayload_t p3 = { 8003, 30 };
// 正常原地初始化
c_err_t err = c_ArrayList_Init(&list, sizeof(int), 5, NULL);
ASSERT_INT_EQ(C_SUCCESS, err);
ASSERT_INT_EQ(0, c_ArrayList_Size(&list));
ASSERT_INT_EQ(5, list.capacity);
ASSERT_PTR_NOT_NULL(list.array);
// 1. 在容量空间未达到天花板前,轻量 Add 应当平滑写入
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_Add(&list, &p1));
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_Add(&list, &p2));
ASSERT_INT_EQ(2, (int)list.size);
// 销毁幂等性与清除检查
c_ArrayList_Destroy(&list);
ASSERT_TRUE(list.array == NULL);
ASSERT_INT_EQ(0, list.capacity);
ASSERT_INT_EQ(0, list.size);
// 🌟【确定性轻量策略绝杀断言 1】:当塞入第 3 个数据导致容量溢出时,
// 轻量版 Add 必须无条件前置拦截、拒绝隐式重分配,严肃返回状态码 C_ERR_OUTOFBOUND
ASSERT_INT_EQ(C_ERR_FULL, c_ArrayList_Add(&list, &p3));
ASSERT_INT_EQ(2, (int)list.size); // 大小纹丝不动
c_ArrayList_Destroy(&list); // 重复销毁不应崩溃
}
// 2. 验证 Read 与 Get 的读取差异
UserPayload_t read_buf;
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_Read(&list, 1, &read_buf));
ASSERT_INT_EQ(8002, (int)read_buf.uid); // 副本安全导出
// 测试二:泛型值复制存储、安全 Read/Get 访问测试
TEST_CASE(test_value_copy_and_access) {
c_ArrayList_t list;
c_ArrayList_Init(&list, sizeof(SensorNode_t), 2, NULL);
// 通过 Get 拿到内部指针直接进行原地篡改
UserPayload_t* p_raw_ptr = (UserPayload_t*)c_ArrayList_Get(&list, 1);
ASSERT_TRUE(p_raw_ptr != NULL);
p_raw_ptr->data_block = 9999; // 原地篡改内部物理缓冲区
SensorNode_t node1 = { .node_id = 101, .threshold = 45.2f, .name = "Temp01" };
SensorNode_t node2 = { .node_id = 102, .threshold = 12.8f, .name = "Humid02" };
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_Read(&list, 1, &read_buf));
ASSERT_INT_EQ(9999, read_buf.data_block); // 指针篡改成功核回
// 写入测试
ASSERT_INT_EQ(C_SUCCESS, c_ArrayList_Add(&list, &node1));
ASSERT_INT_EQ(C_SUCCESS, c_ArrayList_Add(&list, &node2));
ASSERT_INT_EQ(2, c_ArrayList_Size(&list));
// 🌟【确定性轻量策略绝杀断言 2】:使用轻量版 PopBack 弹出
UserPayload_t pop_buf;
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_PopBack(&list, &pop_buf));
ASSERT_INT_EQ(8002, (int)pop_buf.uid);
ASSERT_INT_EQ(1, (int)list.size);
// 强隔离隔离性检查:修改外部临时变量,内部数据不应被污染
node1.node_id = 999;
// 1. 测试 c_ArrayList_Read 拷出副本能力
SensorNode_t read_buffer;
ASSERT_INT_EQ(C_SUCCESS, c_ArrayList_Read(&list, 0, &read_buffer));
ASSERT_INT_EQ(101, read_buffer.node_id); // 应该依旧是原值 101
ASSERT_DOUBLE_EQ_MSG(45.2f, read_buffer.threshold, "Float precision checking");
ASSERT_TRUE(strcmp(read_buffer.name, "Temp01") == 0);
// 2. 测试 c_ArrayList_Get 直接指针读取能力
SensorNode_t* direct_ptr = (SensorNode_t*)c_ArrayList_Get(&list, 1);
ASSERT_PTR_NOT_NULL(direct_ptr);
ASSERT_INT_EQ(102, direct_ptr->node_id);
// 3. 越界保护检查
ASSERT_INT_EQ(C_ERR_PARAM, c_ArrayList_Read(&list, 2, &read_buffer));
ASSERT_TRUE(c_ArrayList_Get(&list, 5) == NULL);
// 尽管存量已经萎缩到总容量的 1/4 以下,轻量版 PopBack 也决不能引发缩容,容量必须卡死留守在 2!
ASSERT_INT_EQ(2, (int)list.capacity);
c_ArrayList_Destroy(&list);
}
// 测试三:高危内存移动(memmove)及 Remove 位移正确性测试
TEST_CASE(test_element_removal_and_shifting) {
TEST_CASE(test_c_ArrayList_Resize) {
c_ArrayList_t list;
c_ArrayList_Init(&list, sizeof(int), 5, NULL);
c_ArrayList_Init(&list, sizeof(UserPayload_t), 2, NULL); // 降级测试
int values[] = {10, 20, 30, 40, 50};
for(int i = 0; i < 5; i++) {
c_ArrayList_Add(&list, &values[i]);
}
UserPayload_t p1 = { 9001, 100 };
UserPayload_t p2 = { 9002, 200 };
UserPayload_t p3 = { 9003, 300 }; // 会强迫弹性版触发自动倍增
// 移除中间的数字 30 (索引 2) 并精准接住拷出值
int removed_val = 0;
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_Remove(&list, 2, &removed_val));
ASSERT_INT_EQ(30, removed_val);
ASSERT_INT_EQ(4, c_ArrayList_Size(&list));
// 1. 验证自适应变轨 ResizeAdd
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_ResizeAdd(&list, &p1));
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_ResizeAdd(&list, &p2));
// 极其严苛地检测后面所有元素的向前位移是否对齐正确
ASSERT_INT_EQ(10, *(int*)c_ArrayList_Get(&list, 0));
ASSERT_INT_EQ(20, *(int*)c_ArrayList_Get(&list, 1));
ASSERT_INT_EQ(40, *(int*)c_ArrayList_Get(&list, 2)); // 40 顶替了 30 的位置
ASSERT_INT_EQ(50, *(int*)c_ArrayList_Get(&list, 3)); // 50 顶替了 40 的位置
// 绝杀:第 3 个数据会自适应变轨,将容量从 2 直接倍增拓宽至 4
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_ResizeAdd(&list, &p3));
ASSERT_INT_EQ(3, (int)list.size);
ASSERT_INT_EQ(4, (int)list.capacity);
// 验证静默删除(out_item 为 NULL
ASSERT_INT_EQ(C_SUCCESS, c_ArrayList_Remove(&list, 0, NULL));
ASSERT_INT_EQ(20, *(int*)c_ArrayList_Get(&list, 0)); // 20 变成了头元素
// 2. 验证中段中段摘除平移覆盖
UserPayload_t remove_verify;
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_RemoveAt(&list, 1, &remove_verify)); // 拿掉 1 号位的 P2
ASSERT_INT_EQ(9002, (int)remove_verify.uid);
ASSERT_INT_EQ(2, (int)list.size);
// 搬运平移断言:原先在 2 号位的 P3(9003) 应该已经被向前挪移顶替到了 1 号位
UserPayload_t read_verify;
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_Read(&list, 1, &read_verify));
ASSERT_INT_EQ(9003, (int)read_verify.uid);
// 3. 验证弹性版 PopBackResize 与常数缩容锁
UserPayload_t pop_resize_verify;
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_PopBackResize(&list, &pop_resize_verify));
ASSERT_INT_EQ(9003, (int)pop_resize_verify.uid);
ASSERT_INT_EQ(1, (int)list.size);
// 🌟【核心弹性缩容断言】:此时 N <= Capacity/4 (1 <= 4/4) 且大于配额上限
// 自适应版 PopBackResize 必须自动通过常数缩容锁将物理存储空间腰斩,重新高效卡回容量 2!
ASSERT_INT_EQ(2, (int)list.capacity);
c_ArrayList_Destroy(&list);
}
// 测试四:挂载自定义分配器,并验证伙伴系统(Buddy System)的 O(1) 就地复用优化
TEST_CASE(test_buddy_system_allocator_integration) {
c_Allocator_t buddy_allocator = {
.alloc = test_alloc,
.realloc = test_realloc,
.free = test_free,
.ud = &g_tracker
};
TEST_CASE(test_c_ArrayList_Toxicity_Defenses) {
c_ArrayList_t local_list;
c_ArrayList_Init(&local_list, sizeof(int), 4, NULL);
c_ArrayList_t list;
// 单个元素 8 字节,初始容量 2。整个缓冲区 = 2 * 8 = 16 字节
c_ArrayList_Init(&list, 8, 2, &buddy_allocator);
int dummy = 0;
// 验证各类入参毒参数及空仓边界的状态码返回线
ASSERT_INT_EQ(C_ERR_PARAM, c_ArrayList_Init(NULL, sizeof(int), 4, NULL));
ASSERT_INT_EQ(C_ERR_EMPTY, c_ArrayList_PopBack(&local_list, &dummy)); // 拦截 C_ERR_EMPTY
ASSERT_INT_EQ(C_ERR_OUTOFBOUND, c_ArrayList_Set(&local_list, 999, &dummy)); // 越界拦截
ASSERT_TRUE(c_ArrayList_Get(&local_list, 0) == NULL);
// 验证 active_allocations 计数 (1控制头由调用者在栈分配,因此分配器内只有 1 个内部数据缓冲区 array)
ASSERT_INT_EQ(1, g_tracker.active_allocations);
// 1. 显式调整容量从 2 -> 3
// 旧大小 16 字节 (2^4),新大小 24 字节 (向上对齐到 2^5 = 32),阶数改变,模拟真实搬迁
ASSERT_INT_EQ(C_SUCCESS, c_ArrayList_Resize(&list, 3));
ASSERT_INT_EQ(0, g_tracker.realloc_in_place_count); // 跨越了幂次墙,没有就地复用
// 2. 深度契合点:再次调整容量从 3 -> 4
// 旧大小 24 字节 (2^5 范围内),新大小 32 字节 (刚好跨入 2^5 满额边界)
// 【期望结果】:落在同一阶数内,c_Buddy_Realloc 应当直接 O(1) 返回原指针!
ASSERT_INT_EQ(C_SUCCESS, c_ArrayList_Resize(&list, 4));
ASSERT_INT_EQ(1, g_tracker.realloc_in_place_count); // 完美!命中伙伴系统就地复用优化次数 1 次
// 3. 极限截断缩小到 0
ASSERT_INT_EQ(C_SUCCESS, c_ArrayList_Resize(&list, 0));
ASSERT_INT_EQ(0, c_ArrayList_Size(&list));
ASSERT_INT_EQ(0, list.capacity);
ASSERT_TRUE(list.array == NULL);
c_ArrayList_Destroy(&list);
// 验证测试环境有没有发生任何内存泄漏
ASSERT_INT_EQ_MSG(0, g_tracker.active_allocations, "Memory leak detected inside allocator!");
c_ArrayList_Destroy(&local_list);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
// ==========================================
// 5. 主集成入口
// ==========================================
int main(void) {
TEST_START(C_ArrayList_DoubleStrategy_TestSuite);
int main(int argc, char** argv){
TEST_START(C_ArrayList_Module_Tests);
// 运行常规测试
RUN_TEST(test_lifecycle_and_defense);
RUN_TEST(test_value_copy_and_access);
RUN_TEST(test_element_removal_and_shifting);
// 使用 Fixture 模式运行涉及自定义状态追踪的伙伴系统测试
RUN_TEST_FIXTURE(test_buddy_system_allocator_integration, custom_allocator_setup, custom_allocator_teardown);
RUN_TEST(test_c_ArrayList_CRUD);
RUN_TEST(test_c_ArrayList_Resize);
RUN_TEST(test_c_ArrayList_Toxicity_Defenses);
TEST_REPORT();
RETURN_TEST_STATUS;
}
return (g_test_registry.failed_count > 0 ? 1 : 0);
}
+230
View File
@@ -0,0 +1,230 @@
#include <c_LinearProbingHashST.h>
/**
* @brief 内部多项式滚动去偏差哈希映射机
*/
C_STATIC_FORCE_INLINE
c_size_t c_LP_HashEngine(const void* key, c_size_t key_size) {
const unsigned char* bytes = (const unsigned char*)key;
c_size_t hash = 0;
for (c_size_t i = 0; i < key_size; i++) {
hash = 31 * hash + bytes[i];
}
return hash;
}
/**
* @brief 就地初始化线性探测哈希表(开辟密集扁平存储空间)
*/
c_err_t c_LinearProbingHashST_Init(c_LinearProbingHashST_t* self, c_size_t initial_capacity, c_size_t key_size, c_size_t val_size, c_SortCompare_t key_cmp, void* args, c_Allocator_t* allocator) {
if (!self || initial_capacity == 0 || key_size == 0 || val_size == 0 || !key_cmp) {
return C_ERR_PARAM;
}
if (allocator) {
self->allocator = *allocator;
} else {
self->allocator = c_DefaultAllocator;
}
c_size_t cap = initial_capacity;
// 前置无符号整数乘法算术溢出防御审计
if (((c_size_t)-1) / key_size < cap) return C_ERR_NOMEM;
if (((c_size_t)-1) / val_size < cap) return C_ERR_NOMEM;
if (((c_size_t)-1) / sizeof(bool) < 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);
self->occupied = (bool*)c_Allocator_Alloc(&self->allocator, cap * sizeof(bool));
if (!self->keys || !self->vals || !self->occupied) {
if (self->keys) c_Allocator_Free(&self->allocator, self->keys);
if (self->vals) c_Allocator_Free(&self->allocator, self->vals);
if (self->occupied) c_Allocator_Free(&self->allocator, self->occupied);
return C_ERR_NOMEM;
}
memset(self->occupied, 0, cap * sizeof(bool)); // 清空标志位
self->m_capacity = cap;
self->size = 0;
self->key_size = key_size;
self->val_size = val_size;
self->key_cmp = key_cmp;
self->args = args;
return C_ERR_OK;
}
/**
* @brief 内部自适应动态扩容变轨接口(开放寻址法要求 N/M <= 0.5 时查找常数常数最低)
*/
c_err_t c_LinearProbingHashST_Resize(c_LinearProbingHashST_t* self, c_size_t new_capacity) {
c_LinearProbingHashST_t temp_st;
c_err_t err = c_LinearProbingHashST_Init(&temp_st, new_capacity, self->key_size, self->val_size, self->key_cmp, self->args, &self->allocator);
if (err != C_ERR_OK) return err;
// 🌟 核心:将老哈希表内所有已占用的插槽元素物理搬运重散列到新表
for (c_size_t i = 0; i < self->m_capacity; i++) {
if (self->occupied[i]) {
void* k_src = self->keys + (i * self->key_size);
void* v_src = self->vals + (i * self->val_size);
// 外部前置接口复用
c_LinearProbingHashST_Put(&temp_st, k_src, v_src);
}
}
// 释放老表的内部存储载体
c_Allocator_Free(&self->allocator, self->keys);
c_Allocator_Free(&self->allocator, self->vals);
c_Allocator_Free(&self->allocator, self->occupied);
// 将重组平衡后的 temp_st 的物理外壳平移接管给 self
self->keys = temp_st.keys;
self->vals = temp_st.vals;
self->occupied = temp_st.occupied;
self->m_capacity = temp_st.m_capacity;
return C_ERR_OK;
}
/**
* @brief 存入键值对(开放寻址常数项均摊 O(1) 吞吐效率)
*/
c_err_t c_LinearProbingHashST_Put(c_LinearProbingHashST_t* self, const void* key, const void* val) {
if (!self || !key || !val) return C_ERR_PARAM;
// 1. 如果当前的负载因子过高(存储量已经达到总插槽容量的一半 N >= M/2),启动两倍弹性大扩容
if (self->size >= (self->m_capacity >> 1)) {
if (c_LinearProbingHashST_Resize(self, self->m_capacity << 1) != C_ERR_OK) {
return C_ERR_NOMEM;
}
}
c_size_t i;
c_size_t M = self->m_capacity;
c_size_t ks = self->key_size;
// 2. 核心线性探测滑窗:从哈希初始基准插槽位置开始,顺序向后查找
for (i = c_LP_HashEngine(key, ks) % M; self->occupied[i]; i = (i + 1) % M) {
if (self->key_cmp(key, self->keys + (i * ks), self->args) == 0) {
// Key 已存在:触发覆写(Overwrite)更新 Value 语义,平滑返回
memcpy(self->vals + (i * self->val_size), val, self->val_size);
return C_ERR_OK;
}
}
// 3. 探查到首个空闲插槽(occupied[i] == false):深拷贝落脚安家
memcpy(self->keys + (i * ks), key, ks);
memcpy(self->vals + (i * self->val_size), val, self->val_size);
self->occupied[i] = true;
self->size++;
return C_ERR_OK;
}
/**
* @brief 依据指定 Key 精准存取读取 Value(均摊常数时间复杂度 O(1))
*/
c_err_t c_LinearProbingHashST_Get(const c_LinearProbingHashST_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 M = self->m_capacity;
c_size_t ks = self->key_size;
// 探测碰撞线,遇到空插槽时强行终止探查(说明目标必定不存在)
for (c_size_t i = c_LP_HashEngine(key, ks) % M; self->occupied[i]; i = (i + 1) % M) {
if (self->key_cmp(key, self->keys + (i * ks), 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_LinearProbingHashST_Contains(const c_LinearProbingHashST_t* self, const void* key) {
if (!self || !key || self->size == 0) return false;
c_size_t M = self->m_capacity;
c_size_t ks = self->key_size;
for (c_size_t i = c_LP_HashEngine(key, ks) % M; self->occupied[i]; i = (i + 1) % M) {
if (self->key_cmp(key, self->keys + (i * ks), self->args) == 0) {
return true;
}
}
return false;
}
/**
* @brief 精准斩断移出键值对(商用级硬核 Cluster 级联右移重散列机制)
*/
c_err_t c_LinearProbingHashST_Delete(c_LinearProbingHashST_t* self, const void* key) {
if (!self || !key) return C_ERR_PARAM;
if (self->size == 0) return C_ERR_EMPTY;
c_size_t M = self->m_capacity;
c_size_t ks = self->key_size;
c_size_t vs = self->val_size;
// 1. 前置走查:如果在表中根本找不到这个键,直接拦截
if (!c_LinearProbingHashST_Contains(self, key)) {
return C_ERR_NOTFOUND;
}
// 2. 寻找目标键当前所在的精确物理下标插槽位置 i
c_size_t i = c_LP_HashEngine(key, ks) % M;
while (self->key_cmp(key, self->keys + (i * ks), self->args) != 0) {
i = (i + 1) % M;
}
// 3. 物理抹除该点数据
self->occupied[i] = false;
self->size--;
// 4. 🌟【级联重散列绝杀内核】:对当前被斩断点 i 后方的整个连续碰撞簇(Cluster)的所有节点执行物理挪移
i = (i + 1) % M;
while (self->occupied[i]) {
// 先把处于位置 i 的键和值独立备份转储出来
char* k_to_redo = self->keys + (i * ks);
char* v_to_redo = self->vals + (i * vs);
char k_buf[self->key_size];
char v_buf[self->val_size];
// 256字节足够吃下常规基本和结构体对象
memcpy(k_buf, k_to_redo, (ks < sizeof(k_buf)) ? ks : sizeof(k_buf));
memcpy(v_buf, v_to_redo, (vs < sizeof(v_buf)) ? vs : sizeof(v_buf));
// 斩断原位置的占用状态,递减临时计数
self->occupied[i] = false;
self->size--;
// 重新调用 Put 将由于前面断裂被波及的孤立节点重新安全的散列到正确的落脚槽中
c_LinearProbingHashST_Put(self, k_buf, v_buf);
i = (i + 1) % M; // 向右单调递增逼近取模
}
// 5. 内存缩容防御锁(若当前有效存量已经极度萎缩 N <= M/8 且容量大于最低基数时,自动释放多余空间翻倍缩容)
if (self->size > 0 && self->size <= (self->m_capacity >> 3) && self->m_capacity > 8) {
c_LinearProbingHashST_Resize(self, self->m_capacity >> 1);
}
return C_ERR_OK;
}
/**
* @brief 释放线性开放寻址哈希表资源
*/
void c_LinearProbingHashST_Destroy(c_LinearProbingHashST_t* self) {
if (self) {
if (self->keys) c_Allocator_Free(&self->allocator, self->keys);
if (self->vals) c_Allocator_Free(&self->allocator, self->vals);
if (self->occupied) c_Allocator_Free(&self->allocator, self->occupied);
self->keys = NULL; self->vals = NULL; self->occupied = NULL;
self->size = 0; self->m_capacity = 0;
}
}
+56
View File
@@ -0,0 +1,56 @@
#ifndef INCLUDED_C_LINEARPROBINGHASHST_H
#define INCLUDED_C_LINEARPROBINGHASHST_H
#ifndef INCLUDED_C_TYPES_H
#include <c_Types.h>
#endif /*INCLUDED_C_TYPES_H*/
#ifndef INCLUDED_C_SORTCOMPARE_H
#include <c_SortCompare.h>
#endif /*INCLUDED_C_SORTCOMPARE_H*/
#ifndef INCLUDED_C_ALLOCATOR_H
#include <c_Allocator.h>
#endif /*INCLUDED_C_ALLOCATOR_H*/
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
typedef struct {
char* keys; // 密集排布的键连续字节数组载体
char* vals; // 与键数组物理下标一一对应的值连续字节数组载体
bool* occupied; // 标志位布尔数组:occupied[i] 为 true 代表该插槽当前已存入有效数据
c_size_t m_capacity; // 当前哈希表大连续数组的物理插槽总容量 (M)
c_size_t size; // 当前哈希表内实际驻留的有效键值对总个数 (N)
c_size_t key_size; // 单个键对象占用的物理字节大小 (sizeof)
c_size_t val_size; // 单个值对象占用的物理字节大小 (sizeof)
c_SortCompare_t key_cmp;// 键对象专用的全等回调比对器
void* args; // 自定义上下文参数指针
c_Allocator_t allocator;// 内联组合分配器实例与自适应 Fallback 缺省
} c_LinearProbingHashST_t;
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
c_err_t c_LinearProbingHashST_Init(c_LinearProbingHashST_t* self, c_size_t initial_capacity,
c_size_t key_size, c_size_t val_size, c_SortCompare_t key_cmp, void* args, c_Allocator_t* allocator);
c_err_t c_LinearProbingHashST_Resize(c_LinearProbingHashST_t* self, c_size_t new_capacity);
c_err_t c_LinearProbingHashST_Put(c_LinearProbingHashST_t* self, const void* key, const void* val);
c_err_t c_LinearProbingHashST_Get(const c_LinearProbingHashST_t* self, const void* key, void* out_val);
bool c_LinearProbingHashST_Contains(const c_LinearProbingHashST_t* self, const void* key);
c_err_t c_LinearProbingHashST_Delete(c_LinearProbingHashST_t* self, const void* key);
void c_LinearProbingHashST_Destroy(c_LinearProbingHashST_t* self);
#endif /*INCLUDED_C_LINEARPROBINGHASHST_H*/
+73
View File
@@ -0,0 +1,73 @@
#include "c_LinearProbingHashST.h"
#include "c_Test.h"
#include <stdlib.h>
#include <stdio.h>
static int lp_compare_chars(const void* a, const void* b, void* args) {
(void)args;
char char1 = *(const char*)a;
char char2 = *(const char*)b;
return char1 - char2;
}
TEST_CASE(test_c_LinearProbingHashST_ClusterDeleteFlow) {
c_LinearProbingHashST_t lp_st;
// 初始化物理容量仅为 4 的密集探测哈希表,强迫其触发高频扩容与线性碰撞
c_err_t err = c_LinearProbingHashST_Init(&lp_st, 4, sizeof(char), sizeof(int), lp_compare_chars, NULL, &c_DefaultAllocator);
ASSERT_INT_EQ(C_ERR_OK, err);
char key_A = 'A'; int val_A = 100;
char key_B = 'B'; int val_B = 200;
char key_C = 'C'; int val_C = 300;
// 1. Put 添加行为断言
ASSERT_INT_EQ(C_ERR_OK, c_LinearProbingHashST_Put(&lp_st, &key_A, &val_A));
ASSERT_INT_EQ(C_ERR_OK, c_LinearProbingHashST_Put(&lp_st, &key_B, &val_B));
ASSERT_INT_EQ(C_ERR_OK, c_LinearProbingHashST_Put(&lp_st, &key_C, &val_C));
ASSERT_INT_EQ(3, (int)lp_st.size);
// 2. Overwrite 相同键覆写更新测试
int update_val_B = 999;
ASSERT_INT_EQ(C_ERR_OK, c_LinearProbingHashST_Put(&lp_st, &key_B, &update_val_B));
int get_verify = 0;
ASSERT_INT_EQ(C_ERR_OK, c_LinearProbingHashST_Get(&lp_st, &key_B, &get_verify));
ASSERT_INT_EQ(999, get_verify);
// 3. 🌟【硬核断言】:摘除处于连续碰撞探测中央、扮演“桥梁”角色的元素 'B'
// 修复后的代码由于触发了级联 Cluster 重新散列,'C' 节点会被安全的重新插入,探测绝不断裂!
ASSERT_INT_EQ(C_ERR_OK, c_LinearProbingHashST_Delete(&lp_st, &key_B));
ASSERT_INT_EQ(2, (int)lp_st.size);
ASSERT_TRUE(!c_LinearProbingHashST_Contains(&lp_st, &key_B));
// 4. 终极断言:桥梁断裂后,后方的 C 元素依然必须完好无损、可以被常数级 Get 命中!
ASSERT_INT_EQ(C_ERR_OK, c_LinearProbingHashST_Get(&lp_st, &key_C, &get_verify));
ASSERT_INT_EQ(300, get_verify);
c_LinearProbingHashST_Destroy(&lp_st);
}
TEST_CASE(test_c_LinearProbingHashST_ParamConstraints) {
c_LinearProbingHashST_t local_lp;
c_LinearProbingHashST_Init(&local_lp, 8, sizeof(char), sizeof(int), lp_compare_chars, NULL, NULL);
char k = 'Z'; int v = 55;
// 5. 验证拦截机制
ASSERT_INT_EQ(C_ERR_PARAM, c_LinearProbingHashST_Init(NULL, 8, sizeof(char), sizeof(int), lp_compare_chars, NULL, NULL));
ASSERT_INT_EQ(C_ERR_PARAM, c_LinearProbingHashST_Put(NULL, &k, &v));
ASSERT_INT_EQ(C_ERR_EMPTY, c_LinearProbingHashST_Delete(&local_lp, &k)); // 空仓删除安全抛出 C_ERR_EMPTY
c_LinearProbingHashST_Destroy(&local_lp);
}
// ==========================================
// 5. 主集成入口
// ==========================================
int main(void) {
TEST_START(C_LinearProbingHashST_Isolated_TestSuite);
RUN_TEST(test_c_LinearProbingHashST_ClusterDeleteFlow);
RUN_TEST(test_c_LinearProbingHashST_ParamConstraints);
TEST_REPORT();
return (g_test_registry.failed_count > 0 ? 1 : 0);
}
+1 -1
View File
@@ -71,7 +71,7 @@ static c_err_t c_RedBlackBST_InternalPut(c_RedBlackBST_t* self, c_RBBSTNode_t**
// 递归基:开辟挂载新节点,新生成的链接默认为极其活跃的【红链接】
if (curr == NULL) {
c_RBBSTNode_t* new_node = (c_RBBSTNode_t*)c_Allocator_Alloc(&self->allocator, sizeof(c_RBBSTNode_t));
c_RBBSTNode_t* new_node = (c_RBBSTNode_t*)c_Allocator_Alloc(&self->allocator, sizeof(*new_node));
void* new_key = c_Allocator_Alloc(&self->allocator, self->key_size);
void* new_val = c_Allocator_Alloc(&self->allocator, self->val_size);
+150
View File
@@ -0,0 +1,150 @@
#include <c_SeparateChainingHashST.h>
/**
* @brief 工业级无符号泛型去偏差多项式哈希映射机
*
* 采用经典常数乘子 31 逐字节滚动翻滚,完美离散任何变长扁平结构体或内置基础类型
*/
C_STATIC_FORCE_INLINE
c_size_t c_SeparateChainingHashST_HashEngine(const void* key, c_size_t key_size) {
const unsigned char* bytes = (const unsigned char*)key;
c_size_t hash = 0;
for (c_size_t i = 0; i < key_size; i++) {
hash = 31 * hash + bytes[i];
}
return hash;
}
/**
* @brief 内部辅助:通过哈希码安全计算对应的无符号桶插槽物理下标位置
*/
C_STATIC_FORCE_INLINE
c_size_t c_SeparateChainingHashST_GetBucketSlot(const c_SeparateChainingHashST_t* self, const void* key) {
c_size_t code = c_SeparateChainingHashST_HashEngine(key, self->key_size);
// 纯无符号按位安全取模,物理屏蔽符号位回绕风险
return code % self->m_buckets;
}
/**
* @brief 就地初始化拉链法哈希映射表
*
* @param initial_buckets 哈希桶(拉链容量)的总基数 M。建议传入质数(如 97, 997, 8191)以获得最佳离散度
*/
c_err_t c_SeparateChainingHashST_Init(c_SeparateChainingHashST_t* self, c_size_t initial_buckets, c_size_t key_size, c_size_t val_size, c_SortCompare_t key_cmp, void* args, c_Allocator_t* allocator) {
if (!self || initial_buckets == 0 || key_size == 0 || val_size == 0 || !key_cmp) {
return C_ERR_PARAM;
}
if (allocator) {
self->allocator = *allocator;
} else {
self->allocator = c_DefaultAllocator;
}
self->m_buckets = initial_buckets;
self->size = 0;
self->key_size = key_size;
self->val_size = val_size;
self->key_cmp = key_cmp;
self->args = args;
// 前置无符号乘法整数溢出防御审计
if (((c_size_t)-1) / sizeof(c_SeqSearchST_t) < initial_buckets) {
return C_ERR_NOMEM;
}
// 一次性静态分配 M 个桶的符号表控制头空间
self->buckets = (c_SeqSearchST_t*)c_Allocator_Alloc(&self->allocator, initial_buckets * sizeof(c_SeqSearchST_t));
if (!self->buckets) {
return C_ERR_NOMEM;
}
// 逐个串联并初始化每个桶内部的单链表控制流,强力绑定统一的组合分配器
for (c_size_t i = 0; i < initial_buckets; i++) {
c_SeqSearchST_Init(&(self->buckets[i]), key_size, val_size, key_cmp, args, &self->allocator);
}
return C_ERR_OK;
}
/**
* @brief 存入键值对(均摊常数项时间复杂度 O(1))
*
* 如果键已存在于特定拉链桶中则覆写更新;若不存在则头插法压入新节点并刷新全局计数
*/
c_err_t c_SeparateChainingHashST_Put(c_SeparateChainingHashST_t* self, const void* key, const void* val) {
if (!self || !key || !val) return C_ERR_PARAM;
// 1. 利用哈希映射机瞬间定位到目标桶
c_size_t slot = c_SeparateChainingHashST_GetBucketSlot(self, key);
c_SeqSearchST_t* bucket_st = &(self->buckets[slot]);
// 2. 顺序探查该拉链。前置提取原拉链大小,用来判别本次操作是“新增”还是“修改覆写”
c_size_t old_bucket_size = bucket_st->size;
c_err_t err = c_SeqSearchST_Put(bucket_st, key, val);
if (err == C_ERR_OK) {
// 如果引发了当前单链桶节点的空间膨胀,说明是新键插入,递增全局计数
if (bucket_st->size > old_bucket_size) {
self->size++;
}
}
return err;
}
/**
* @brief 依据指定 Key 精准存取读取关联的 Value(均摊常数时间复杂度 O(1))
*/
c_err_t c_SeparateChainingHashST_Get(const c_SeparateChainingHashST_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 slot = c_SeparateChainingHashST_GetBucketSlot(self, key);
// 穿透调用单向顺序表的 Get 接口
return c_SeqSearchST_Get(&(self->buckets[slot]), key, out_val);
}
/**
* @brief 检查哈希表内是否有效包含指定的 Key
*/
bool c_SeparateChainingHashST_Contains(const c_SeparateChainingHashST_t* self, const void* key) {
if (!self || !key || self->size == 0) return false;
c_size_t slot = c_SeparateChainingHashST_GetBucketSlot(self, key);
return c_SeqSearchST_Contains(&(self->buckets[slot]), key);
}
/**
* @brief 从指定的哈希桶拉链中斩断并彻底移出其关联的符号对
*/
c_err_t c_SeparateChainingHashST_Delete(c_SeparateChainingHashST_t* self, const void* key) {
if (!self || !key) return C_ERR_PARAM;
if (self->size == 0) return C_ERR_EMPTY;
c_size_t slot = c_SeparateChainingHashST_GetBucketSlot(self, key);
c_SeqSearchST_t* bucket_st = &(self->buckets[slot]);
c_err_t err = c_SeqSearchST_Delete(bucket_st, key);
if (err == C_ERR_OK) {
self->size--; // 递减总容量计数
}
return err;
}
/**
* @brief 反初始化:级联彻底清理销毁全部哈希桶拉链,原路逆向回收堆空间
*/
void c_SeparateChainingHashST_Destroy(c_SeparateChainingHashST_t* self) {
if (!self || !self->buckets) return;
// 1. 迫使每个哈希拉链桶先链式释放其内部的单链物理节点
for (c_size_t i = 0; i < self->m_buckets; i++) {
c_SeqSearchST_Destroy(&(self->buckets[i]));
}
// 2. 回收哈希桶外壳控制头数组本身
c_Allocator_Free(&self->allocator, self->buckets);
self->buckets = NULL;
self->size = 0;
self->m_buckets = 0;
}
+46
View File
@@ -0,0 +1,46 @@
#ifndef INCLUDED_C_SEPARATECHAININGHASHST_H
#define INCLUDED_C_SEPARATECHAININGHASHST_H
#ifndef INCLUDED_C_TYPES_H
#include <c_Types.h>
#endif /*INCLUDED_C_TYPES_H*/
#ifndef INCLUDED_C_SEQSEARCHST_H
#include <c_SeqSearchST.h>
#endif /*INCLUDED_C_SEQSEARCHST_H*/
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
typedef struct {
c_size_t m_buckets; // 哈希表内的桶(拉链数量)总基数 (M)
c_size_t size; // 当前整个哈希映射表内有效驻留的键值对总数 (N)
c_SeqSearchST_t* buckets; // 密集排布的拉链桶符号表动态数组:buckets[0 ... M-1]
c_size_t key_size; // 键对象的字节大小 (sizeof)
c_size_t val_size; // 值对象的字节大小 (sizeof)
c_SortCompare_t key_cmp; // 键对象的全等判定器
void* args; // 自定义上下文
c_Allocator_t allocator; // 内联组合分配器实例与自适应 Fallback 缺省
} c_SeparateChainingHashST_t;
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
c_err_t c_SeparateChainingHashST_Init(c_SeparateChainingHashST_t* self, c_size_t initial_buckets,
c_size_t key_size, c_size_t val_size, c_SortCompare_t key_cmp, void* args, c_Allocator_t* allocator);
c_err_t c_SeparateChainingHashST_Put(c_SeparateChainingHashST_t* self, const void* key, const void* val);
c_err_t c_SeparateChainingHashST_Get(const c_SeparateChainingHashST_t* self, const void* key, void* out_val);
bool c_SeparateChainingHashST_Contains(const c_SeparateChainingHashST_t* self, const void* key);
c_err_t c_SeparateChainingHashST_Delete(c_SeparateChainingHashST_t* self, const void* key);
void c_SeparateChainingHashST_Destroy(c_SeparateChainingHashST_t* self);
#endif /*INCLUDED_C_SEPARATECHAININGHASHST_H*/
+102
View File
@@ -0,0 +1,102 @@
#include "c_SeparateChainingHashST.h"
#include "c_Test.h"
#include <stdlib.h>
#include <stdio.h>
static int hash_compare_chars(const void* a, const void* b, void* args) {
(void)args;
char char1 = *(const char*)a;
char char2 = *(const char*)b;
return char1 - char2;
}
TEST_CASE(test_c_SeparateChainingHashST_DestroyFlow) {
c_SeparateChainingHashST_t hash_st;
c_err_t err = c_SeparateChainingHashST_Init(&hash_st, 5, sizeof(char), sizeof(int), hash_compare_chars, NULL, NULL);
ASSERT_INT_EQ(C_ERR_OK, err);
char key_P = 'P'; int val_P = 90;
char key_H = 'H'; int val_H = 80;
ASSERT_INT_EQ(C_ERR_OK, c_SeparateChainingHashST_Put(&hash_st, &key_P, &val_P));
ASSERT_INT_EQ(C_ERR_OK, c_SeparateChainingHashST_Put(&hash_st, &key_H, &val_H));
ASSERT_INT_EQ(2, (int)hash_st.size);
int get_result = 0;
ASSERT_INT_EQ(C_ERR_OK, c_SeparateChainingHashST_Get(&hash_st, &key_H, &get_result));
ASSERT_INT_EQ(80, get_result);
// 🌟 核心重命名释放链条走查
c_SeparateChainingHashST_Destroy(&hash_st);
ASSERT_TRUE(hash_st.buckets == NULL);
ASSERT_INT_EQ(0, (int)hash_st.size);
}
TEST_CASE(test_c_SeparateChainingHashST_O1_StandardFlow) {
c_SeparateChainingHashST_t hash_st;
// 初始化具有 5 个拉链桶的泛型拉链法哈希表 (M=5),测试默认分配器 Fallback 降级机制
c_err_t err = c_SeparateChainingHashST_Init(&hash_st, 5, sizeof(char), sizeof(int), hash_compare_chars, NULL, NULL);
ASSERT_INT_EQ(C_ERR_OK, err);
char key_P = 'P'; int val_P = 90;
char key_H = 'H'; int val_H = 80;
char key_Q = 'Q'; int val_Q = 70;
// 1. Put 基础常数级高速录入验证
ASSERT_INT_EQ(C_ERR_OK, c_SeparateChainingHashST_Put(&hash_st, &key_P, &val_P));
ASSERT_INT_EQ(C_ERR_OK, c_SeparateChainingHashST_Put(&hash_st, &key_H, &val_H));
ASSERT_INT_EQ(C_ERR_OK, c_SeparateChainingHashST_Put(&hash_st, &key_Q, &val_Q));
ASSERT_INT_EQ(3, (int)hash_st.size);
// 状态包含判定
ASSERT_TRUE(c_SeparateChainingHashST_Contains(&hash_st, &key_H));
char key_NotExit = 'X';
ASSERT_TRUE(!c_SeparateChainingHashST_Contains(&hash_st, &key_NotExit));
// 2. Get 常数级读取断言
int get_result = 0;
ASSERT_INT_EQ(C_ERR_OK, c_SeparateChainingHashST_Get(&hash_st, &key_H, &get_result));
ASSERT_INT_EQ(80, get_result); // 精确提取
// 3. Put 相同键覆写更新(Overwrite)特性核验
int overwrite_val_H = 9999;
ASSERT_INT_EQ(C_ERR_OK, c_SeparateChainingHashST_Put(&hash_st, &key_H, &overwrite_val_H));
ASSERT_INT_EQ(3, (int)hash_st.size); // 覆写后,全局哈希大小必须守恒,依旧为 3
ASSERT_INT_EQ(C_ERR_OK, c_SeparateChainingHashST_Get(&hash_st, &key_H, &get_result));
ASSERT_INT_EQ(9999, get_result);
// 4. Delete 常数级拉链断开删除测试
ASSERT_INT_EQ(C_ERR_OK, c_SeparateChainingHashST_Delete(&hash_st, &key_H));
ASSERT_INT_EQ(2, (int)hash_st.size); // 递减确凿
ASSERT_INT_EQ(C_ERR_NOTFOUND, c_SeparateChainingHashST_Get(&hash_st, &key_H, &get_result));
// 彻底级联反初始化释放桶空间
c_SeparateChainingHashST_Destroy(&hash_st);
}
TEST_CASE(test_c_SeparateChainingHashST_ToxicityDefenses) {
c_SeparateChainingHashST_t local_map;
c_SeparateChainingHashST_Init(&local_map, 4, sizeof(char), sizeof(int), hash_compare_chars, NULL, &c_DefaultAllocator);
char k = 'K'; int v = 11;
// 5. 验证关键入参异常状态码强拦截
ASSERT_INT_EQ(C_ERR_PARAM, c_SeparateChainingHashST_Init(NULL, 5, sizeof(char), sizeof(int), hash_compare_chars, NULL, NULL));
ASSERT_INT_EQ(C_ERR_PARAM, c_SeparateChainingHashST_Put(NULL, &k, &v));
ASSERT_INT_EQ(C_ERR_PARAM, c_SeparateChainingHashST_Delete(NULL, &k));
ASSERT_INT_EQ(C_ERR_EMPTY, c_SeparateChainingHashST_Delete(&local_map, &k)); // 空仓删除安全拦截抛出 C_ERR_EMPTY
c_SeparateChainingHashST_Destroy(&local_map);
}
int main(void) {
TEST_START(C_SeparateChainingHashST_DestroyRename_TestSuite);
RUN_TEST(test_c_SeparateChainingHashST_O1_StandardFlow);
RUN_TEST(test_c_SeparateChainingHashST_ToxicityDefenses);
RUN_TEST(test_c_SeparateChainingHashST_DestroyFlow);
TEST_REPORT();
RETURN_TEST_STATUS;
}