开始设计
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
#include <c_BST.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
/**
|
||||
* Helper accessors to safely locate key and value buffers within a generic node allocation block.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE void* c_BST_NodeKey(c_BSTNode_t* node) {
|
||||
return (void*)((char*)node + sizeof(c_BSTNode_t));
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE void* c_BST_NodeVal(c_BSTNode_t* node, c_size_t key_size) {
|
||||
return (void*)((char*)node + sizeof(c_BSTNode_t) + key_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and initializes a standalone tree node layout.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_BSTNode_t* c_BST_CreateNode(const void* key, const void* val, c_size_t ks, c_size_t vs) {
|
||||
c_BSTNode_t* node = (c_BSTNode_t*)C_ALLOC(sizeof(c_BSTNode_t) + ks + vs);
|
||||
if (node == NULL) return NULL;
|
||||
|
||||
node->left = NULL;
|
||||
node->right = NULL;
|
||||
memcpy(c_BST_NodeKey(node), key, ks);
|
||||
memcpy(c_BST_NodeVal(node, ks), val, vs);
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal recursive post-order destructor helper.
|
||||
*/
|
||||
static void c_BST_DestroyNodes(c_BSTNode_t* node) {
|
||||
if (node == NULL) return;
|
||||
c_BST_DestroyNodes(node->left);
|
||||
c_BST_DestroyNodes(node->right);
|
||||
C_FREE(node);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_BST_Init(c_BST_t* tree, c_size_t key_size, c_size_t val_size, int (*compar)(const void*, const void*)) {
|
||||
if (tree == NULL || key_size == 0 || val_size == 0 || compar == NULL) return C_ERR_PARAM;
|
||||
tree->root = NULL;
|
||||
tree->key_size = key_size;
|
||||
tree->val_size = val_size;
|
||||
tree->size = 0;
|
||||
tree->compar = compar;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void c_BST_Destroy(c_BST_t* tree) {
|
||||
if (tree) {
|
||||
c_BST_DestroyNodes(tree->root);
|
||||
tree->root = NULL;
|
||||
tree->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
c_err_t c_BST_Clear(c_BST_t* tree) {
|
||||
if (tree == NULL) return C_ERR_PARAM;
|
||||
c_BST_DestroyNodes(tree->root);
|
||||
tree->root = NULL;
|
||||
tree->size = 0;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_bool_t c_BST_Contains(const c_BST_t* tree, const void* key) {
|
||||
if (tree == NULL || key == NULL) return C_FALSE;
|
||||
c_BSTNode_t* curr = tree->root;
|
||||
while (curr != NULL) {
|
||||
int cmp = tree->compar(key, c_BST_NodeKey(curr));
|
||||
if (cmp == 0) return C_TRUE;
|
||||
curr = (cmp < 0) ? curr->left : curr->right;
|
||||
}
|
||||
return C_FALSE;
|
||||
}
|
||||
|
||||
c_err_t c_BST_Put(c_BST_t* tree, const void* key, const void* val) {
|
||||
if (tree == NULL || key == NULL || val == NULL) return C_ERR_PARAM;
|
||||
|
||||
c_BSTNode_t** link = &tree->root;
|
||||
c_BSTNode_t* curr = tree->root;
|
||||
|
||||
while (curr != NULL) {
|
||||
int cmp = tree->compar(key, c_BST_NodeKey(curr));
|
||||
if (cmp == 0) {
|
||||
// Overwrite existing value for matching symbol key
|
||||
memcpy(c_BST_NodeVal(curr, tree->key_size), val, tree->val_size);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
link = (cmp < 0) ? &curr->left : &curr->right;
|
||||
curr = *link;
|
||||
}
|
||||
|
||||
// Key is unique, construct a new node configuration structure
|
||||
c_BSTNode_t* new_node = c_BST_CreateNode(key, val, tree->key_size, tree->val_size);
|
||||
if (new_node == NULL) return C_ERR_NOMEM;
|
||||
|
||||
*link = new_node;
|
||||
tree->size++;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void* c_BST_Get(const c_BST_t* tree, const void* key) {
|
||||
if (tree == NULL || key == NULL) return NULL;
|
||||
c_BSTNode_t* curr = tree->root;
|
||||
while (curr != NULL) {
|
||||
int cmp = tree->compar(key, c_BST_NodeKey(curr));
|
||||
if (cmp == 0) return c_BST_NodeVal(curr, tree->key_size);
|
||||
curr = (cmp < 0) ? curr->left : curr->right;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
c_err_t c_BST_Delete(c_BST_t* tree, const void* key) {
|
||||
if (tree == NULL || key == NULL) return C_ERR_PARAM;
|
||||
|
||||
c_BSTNode_t** link = &tree->root;
|
||||
c_BSTNode_t* curr = tree->root;
|
||||
|
||||
while (curr != NULL) {
|
||||
int cmp = tree->compar(key, c_BST_NodeKey(curr));
|
||||
if (cmp == 0) break;
|
||||
link = (cmp < 0) ? &curr->left : &curr->right;
|
||||
curr = *link;
|
||||
}
|
||||
|
||||
if (curr == NULL) return C_ERR_NOT_FOUND;
|
||||
|
||||
// Standard Hibbard deletion implementation sequence matching tree boundaries
|
||||
if (curr->left == NULL) {
|
||||
*link = curr->right;
|
||||
} else if (curr->right == NULL) {
|
||||
*link = curr->left;
|
||||
} else {
|
||||
// Node has two children; locate the successor node (smallest node in the right sub-tree)
|
||||
c_BSTNode_t** succ_link = &curr->right;
|
||||
c_BSTNode_t* succ = curr->right;
|
||||
while (succ->left != NULL) {
|
||||
succ_link = &succ->left;
|
||||
succ = succ->left;
|
||||
}
|
||||
|
||||
// Delink the successor node from its previous position
|
||||
*succ_link = succ->right;
|
||||
|
||||
// Route child structures of the node being deleted into the successor
|
||||
succ->left = curr->left;
|
||||
succ->right = curr->right;
|
||||
*link = succ;
|
||||
}
|
||||
|
||||
C_FREE(curr);
|
||||
tree->size--;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef INCLUDED_C_BST_H
|
||||
#define INCLUDED_C_BST_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
// Forward declaration of internal node structure
|
||||
typedef struct c_BSTNode {
|
||||
struct c_BSTNode* left; // Pointer to left child
|
||||
struct c_BSTNode* right; // Pointer to right child
|
||||
// Node payload layout: key block followed immediately by the value block in memory
|
||||
} c_BSTNode_t;
|
||||
|
||||
// Binary Search Tree Context Structure
|
||||
typedef struct {
|
||||
c_BSTNode_t* root; // Root node pointer
|
||||
c_size_t key_size; // Size of each key in bytes
|
||||
c_size_t val_size; // Size of each value in bytes
|
||||
c_size_t size; // Total number of nodes in the tree
|
||||
int (*compar)(const void*, const void*); // Key comparison rule pointer
|
||||
} c_BST_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_BST_Init(c_BST_t* tree, c_size_t key_size, c_size_t val_size, int (*compar)(const void*, const void*)) ;
|
||||
void c_BST_Destroy(c_BST_t* tree);
|
||||
|
||||
c_err_t c_BST_Clear(c_BST_t* tree);
|
||||
c_bool_t c_BST_Contains(const c_BST_t* tree, const void* key);
|
||||
c_err_t c_BST_Put(c_BST_t* tree, const void* key, const void* val);
|
||||
void* c_BST_Get(const c_BST_t* tree, const void* key);
|
||||
c_err_t c_BST_Delete(c_BST_t* tree, const void* key) ;
|
||||
|
||||
#endif /*INCLUDED_C_BST_H*/
|
||||
@@ -0,0 +1,78 @@
|
||||
#include "c_BST.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define EXPECT_EQ(actual, expected, msg) \
|
||||
do { \
|
||||
if ((actual) != (expected)) { \
|
||||
printf(" [X] Assert Failed: %s (Expected %d, got %d)\n", msg, (int)(expected), (int)(actual)); \
|
||||
return C_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// Value data block mapped directly to distinct scalar keys
|
||||
typedef struct {
|
||||
char runtime_state[16];
|
||||
int scheduling_weight;
|
||||
} ProcessMetrics;
|
||||
|
||||
int compareIntPids(const void* a, const void* b) {
|
||||
return (*(int*)a - *(int*)b);
|
||||
}
|
||||
|
||||
c_bool_t test_binary_search_tree(void) {
|
||||
c_BST_t bst;
|
||||
|
||||
EXPECT_EQ(c_BST_Init(&bst, sizeof(int), sizeof(ProcessMetrics), compareIntPids), C_ERR_OK, "Init failed");
|
||||
|
||||
int pid1 = 4500; ProcessMetrics m1 = { "RUNNING", 10 };
|
||||
int pid2 = 1200; ProcessMetrics m2 = { "SLEEPING", 2 };
|
||||
int pid3 = 8900; ProcessMetrics m3 = { "BLOCKED", 5 };
|
||||
int pid4 = 3100; ProcessMetrics m4 = { "ZOMBIE", 0 };
|
||||
|
||||
// 1. Structural Insertion Pipeline Validation
|
||||
EXPECT_EQ(c_BST_Put(&bst, &pid1, &m1), C_ERR_OK, "Put pid1 failed");
|
||||
EXPECT_EQ(c_BST_Put(&bst, &pid2, &m2), C_ERR_OK, "Put pid2 failed");
|
||||
EXPECT_EQ(c_BST_Put(&bst, &pid3, &m3), C_ERR_OK, "Put pid3 failed");
|
||||
EXPECT_EQ(c_BST_Put(&bst, &pid4, &m4), C_ERR_OK, "Put pid4 failed");
|
||||
EXPECT_EQ(bst.size, 4, "Tree dimension size tracker calculation inaccurate");
|
||||
|
||||
// Check value updating via key collisions
|
||||
ProcessMetrics m1_updated = { "SUSPENDED", 8 };
|
||||
EXPECT_EQ(c_BST_Put(&bst, &pid1, &m1_updated), C_ERR_OK, "Overwriting element key failed");
|
||||
EXPECT_EQ(bst.size, 4, "Tree size tracking incremented incorrectly on updating overwrite");
|
||||
|
||||
// 2. Data Retrieval Lookup Paths
|
||||
int look_pid = 4500;
|
||||
ProcessMetrics* fetched = (ProcessMetrics*)c_BST_Get(&bst, &look_pid);
|
||||
EXPECT_EQ(fetched != NULL && strcmp(fetched->runtime_state, "SUSPENDED") == 0, C_TRUE, "Lookup retrieved incorrect mapping segment");
|
||||
|
||||
int miss_pid = 9999;
|
||||
EXPECT_EQ(c_BST_Get(&bst, &miss_pid) == NULL, C_TRUE, "Key miss lookup did not return NULL");
|
||||
EXPECT_EQ(c_BST_Contains(&bst, &look_pid), C_TRUE, "Contains failed reporting registered key tracking rules");
|
||||
|
||||
// 3. Node Removal & Hibbard Rebalancing Checks
|
||||
// Delete pid1 (4500), which represents a root node with two children (1200 and 8900)
|
||||
EXPECT_EQ(c_BST_Delete(&bst, &pid1), C_ERR_OK, "Node deletion failed");
|
||||
EXPECT_EQ(c_BST_Contains(&bst, &pid1), C_FALSE, "Deleted node reference remains inside structure");
|
||||
EXPECT_EQ(bst.size, 3, "Tree size tracking value did not decrement correctly");
|
||||
|
||||
// Verify sub-trees remain completely searchable and accessible post structural mutation
|
||||
int verify_child_pid = 3100;
|
||||
ProcessMetrics* child_check = (ProcessMetrics*)c_BST_Get(&bst, &verify_child_pid);
|
||||
EXPECT_EQ(child_check != NULL && strcmp(child_check->runtime_state, "ZOMBIE") == 0, C_TRUE, "Subtree structure broken during root transformation sequences");
|
||||
|
||||
c_BST_Destroy(&bst);
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Starting Framework Unit Testing: c_BST ===\n");
|
||||
|
||||
if (test_binary_search_tree()) {
|
||||
printf(" [PASS] Binary Search Tree Processing and Deletion Lifecycle Verified Successfully.\n");
|
||||
} else {
|
||||
printf(" [FAIL] Binary Search Tree Component Encountered Evaluation Errors.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include <c_BinarySearch.h>
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef INCLUDED_C_BINARYSEARCH_H
|
||||
#define INCLUDED_C_BINARYSEARCH_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
/**
|
||||
* 通用二分查找函数
|
||||
* @param key 指向要查找的目标元素的指针
|
||||
* @param base 指向待查找数组首元素的指针
|
||||
* @param num 数组中元素的个数
|
||||
* @param size 每个元素的大小(以字节为单位,使用 sizeof 获取)
|
||||
* @param compar 指向比较函数的指针(由用户提供比较逻辑)
|
||||
* @return 找到则返回指向该元素的指针,未找到则返回 NULL
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void* c_BinarySearch(const void* key, const void* base, c_size_t num, c_size_t size,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
c_size_t left = 0;
|
||||
c_size_t right = num; // 使用左闭右开区间 [left, right) 逻辑更清晰
|
||||
|
||||
while (left < right) {
|
||||
c_size_t mid = left + (right - left) / 2;
|
||||
|
||||
// 计算 mid 元素的内存地址:首地址 + 索引 * 每个元素的字节大小
|
||||
// 先强转为 char* 是为了按单字节进行指针偏移
|
||||
const void* midElem = (const char*)base + (mid * size);
|
||||
|
||||
// 调用用户自定义的比较函数
|
||||
int cmp = compar(key, midElem);
|
||||
|
||||
if (cmp == 0) {
|
||||
return (void*)midElem; // 找到目标,返回其在数组中的地址
|
||||
} else if (cmp > 0) {
|
||||
left = mid + 1; // key 大于 midElem,往右半部分找
|
||||
} else {
|
||||
right = mid; // key 小于 midElem,往左半部分找
|
||||
}
|
||||
}
|
||||
|
||||
return NULL; // 未找到
|
||||
}
|
||||
|
||||
#endif /*INCLUDED_C_BINARYSEARCH_H*/
|
||||
@@ -0,0 +1,52 @@
|
||||
#include "c_BinarySearch.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
char name[20];
|
||||
double score;
|
||||
} Student;
|
||||
|
||||
// 用户自定义的比较函数:按学号 (id) 升序比较
|
||||
int compareStudentsById(const void* a, const void* b) {
|
||||
const Student* s1 = (const Student*)a;
|
||||
const Student* s2 = (const Student*)b;
|
||||
|
||||
if (s1->id < s2->id) return -1;
|
||||
if (s1->id > s2->id) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
// 准备一个已经按 id 排好序的结构体数组
|
||||
Student students[] = {
|
||||
{101, "Alice", 92.5},
|
||||
{105, "Bob", 88.0},
|
||||
{109, "Charlie", 95.0},
|
||||
{112, "David", 79.5}
|
||||
};
|
||||
size_t count = sizeof(students) / sizeof(students[0]);
|
||||
|
||||
// 创建一个查找模板(只需要填入你想查找的依据字段)
|
||||
Student targetKey;
|
||||
targetKey.id = 109;
|
||||
|
||||
// 调用通用二分查找
|
||||
Student* result = (Student*)c_BinarySearch(
|
||||
&targetKey, // 目标元素的指针
|
||||
students, // 数组首地址
|
||||
count, // 元素数量
|
||||
sizeof(Student), // 每个结构体占用的字节大小
|
||||
compareStudentsById // 比较函数的指针
|
||||
);
|
||||
|
||||
// 检查并输出结果
|
||||
if (result != NULL) {
|
||||
printf("成功找到!姓名: %s, 成绩: %.1f\n", result->name, result->score);
|
||||
} else {
|
||||
printf("未找到学号为 %d 的学生。\n", targetKey.id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
#include <c_BinarySearchST.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
/**
|
||||
* Core Rank/Binary Search operation.
|
||||
* Returns the exact index if the key is found, or the insertion slot index if not found.
|
||||
*/
|
||||
static inline c_size_t c_BSST_Rank(const c_BinarySearchST_t* st, const void* key, c_bool_t* out_found) {
|
||||
c_size_t left = 0;
|
||||
c_size_t right = st->size;
|
||||
char* keys_base = (char*)st->keys;
|
||||
c_size_t ks = st->key_size;
|
||||
|
||||
while (left < right) {
|
||||
c_size_t mid = left + (right - left) / 2;
|
||||
int cmp = st->compar(key, keys_base + (mid * ks));
|
||||
|
||||
if (cmp == 0) {
|
||||
if (out_found) *out_found = C_TRUE;
|
||||
return mid;
|
||||
} else if (cmp > 0) {
|
||||
left = mid + 1;
|
||||
} else {
|
||||
right = mid;
|
||||
}
|
||||
}
|
||||
|
||||
if (out_found) *out_found = C_FALSE;
|
||||
return left; // 'left' represents the precise index where the key *should* go
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_BinarySearchST_Init(c_BinarySearchST_t* st, c_size_t initial_capacity,
|
||||
c_size_t key_size, c_size_t val_size,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
if (st == NULL || key_size == 0 || val_size == 0 || compar == NULL) return C_ERR_PARAM;
|
||||
|
||||
st->capacity = (initial_capacity > 0) ? initial_capacity : 4;
|
||||
st->key_size = key_size;
|
||||
st->val_size = val_size;
|
||||
st->size = 0;
|
||||
st->compar = compar;
|
||||
|
||||
st->keys = C_ALLOC(st->capacity * key_size);
|
||||
st->vals = C_ALLOC(st->capacity * val_size);
|
||||
|
||||
if (st->keys == NULL || st->vals == NULL) {
|
||||
C_FREE(st->keys);
|
||||
C_FREE(st->vals);
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void c_BinarySearchST_Destroy(c_BinarySearchST_t* st) {
|
||||
if (st) {
|
||||
C_FREE(st->keys);
|
||||
C_FREE(st->vals);
|
||||
st->size = 0;
|
||||
st->capacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
c_err_t c_BinarySearchST_Clear(c_BinarySearchST_t* st) {
|
||||
if (st == NULL) return C_ERR_PARAM;
|
||||
st->size = 0; // Soft reset clears tracking variables but keeps allocated memory blocks
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_bool_t c_BinarySearchST_Contains(const c_BinarySearchST_t* st, const void* key) {
|
||||
if (st == NULL || key == NULL) return C_FALSE;
|
||||
c_bool_t found = C_FALSE;
|
||||
c_BSST_Rank(st, key, &found);
|
||||
return found;
|
||||
}
|
||||
|
||||
c_err_t c_BinarySearchST_Put(c_BinarySearchST_t* st, const void* key, const void* val) {
|
||||
if (st == NULL || key == NULL || val == NULL) return C_ERR_PARAM;
|
||||
|
||||
c_bool_t found = C_FALSE;
|
||||
c_size_t idx = c_BSST_Rank(st, key, &found);
|
||||
|
||||
char* keys_base = (char*)st->keys;
|
||||
char* vals_base = (char*)st->vals;
|
||||
c_size_t ks = st->key_size;
|
||||
c_size_t vs = st->val_size;
|
||||
|
||||
// Symbol Table Behavior: If the key already exists, overwrite the value
|
||||
if (found) {
|
||||
memcpy(vals_base + (idx * vs), val, vs);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
// Dynamic parallel array capacity expansion
|
||||
if (st->size >= st->capacity) {
|
||||
c_size_t new_capacity = st->capacity * 2;
|
||||
void* new_keys = C_ALLOC(new_capacity * ks);
|
||||
void* new_vals = C_ALLOC(new_capacity * vs);
|
||||
|
||||
if (new_keys == NULL || new_vals == NULL) {
|
||||
C_FREE(new_keys);
|
||||
C_FREE(new_vals);
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
if (st->size > 0) {
|
||||
memcpy(new_keys, st->keys, st->size * ks);
|
||||
memcpy(new_vals, st->vals, st->size * vs);
|
||||
}
|
||||
|
||||
C_FREE(st->keys); C_FREE(st->vals);
|
||||
st->keys = new_keys; st->vals = new_vals;
|
||||
st->capacity = new_capacity;
|
||||
keys_base = (char*)st->keys;
|
||||
vals_base = (char*)st->vals;
|
||||
}
|
||||
|
||||
// Shift memory components to create a gap for insertion
|
||||
if (idx < st->size) {
|
||||
memmove(keys_base + ((idx + 1) * ks), keys_base + (idx * ks), (st->size - idx) * ks);
|
||||
memmove(vals_base + ((idx + 1) * vs), vals_base + (idx * vs), (st->size - idx) * vs);
|
||||
}
|
||||
|
||||
// Drop elements directly into parallel array channels
|
||||
memcpy(keys_base + (idx * ks), key, ks);
|
||||
memcpy(vals_base + (idx * vs), val, vs);
|
||||
st->size++;
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void* c_BinarySearchST_Get(const c_BinarySearchST_t* st, const void* key) {
|
||||
if (st == NULL || key == NULL) return NULL;
|
||||
|
||||
c_bool_t found = C_FALSE;
|
||||
c_size_t idx = c_BSST_Rank(st, key, &found);
|
||||
|
||||
if (found) {
|
||||
return (char*)st->vals + (idx * st->val_size);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
c_err_t c_BinarySearchST_Delete(c_BinarySearchST_t* st, const void* key) {
|
||||
if (st == NULL || key == NULL) return C_ERR_PARAM;
|
||||
|
||||
c_bool_t found = C_FALSE;
|
||||
c_size_t idx = c_BSST_Rank(st, key, &found);
|
||||
|
||||
if (!found) return C_ERR_NOT_FOUND;
|
||||
|
||||
char* keys_base = (char*)st->keys;
|
||||
char* vals_base = (char*)st->vals;
|
||||
c_size_t ks = st->key_size;
|
||||
c_size_t vs = st->val_size;
|
||||
|
||||
// Compress parallel entries down over the deleted item slot
|
||||
if (idx < st->size - 1) {
|
||||
memmove(keys_base + (idx * ks), keys_base + ((idx + 1) * ks), (st->size - 1 - idx) * ks);
|
||||
memmove(vals_base + (idx * vs), vals_base + ((idx + 1) * vs), (st->size - 1 - idx) * vs);
|
||||
}
|
||||
|
||||
st->size--;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef INCLUDED_C_BINARYSEARCHST_H
|
||||
#define INCLUDED_C_BINARYSEARCHST_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
void* keys; // Flat parallel array block storing keys
|
||||
void* vals; // Flat parallel array block storing values
|
||||
c_size_t key_size; // Size of each key in bytes (sizeof(Key))
|
||||
c_size_t val_size; // Size of each value in bytes (sizeof(Value))
|
||||
c_size_t capacity; // Maximum allocated element capacity
|
||||
c_size_t size; // Current active entry count
|
||||
int (*compar)(const void*, const void*); // Key comparison rule pointer
|
||||
} c_BinarySearchST_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
c_err_t c_BinarySearchST_Init(c_BinarySearchST_t* st, c_size_t initial_capacity,
|
||||
c_size_t key_size, c_size_t val_size,
|
||||
int (*compar)(const void*, const void*));
|
||||
void c_BinarySearchST_Destroy(c_BinarySearchST_t* st);
|
||||
|
||||
c_err_t c_BinarySearchST_Clear(c_BinarySearchST_t* st);
|
||||
c_bool_t c_BinarySearchST_Contains(const c_BinarySearchST_t* st, const void* key);
|
||||
c_err_t c_BinarySearchST_Put(c_BinarySearchST_t* st, const void* key, const void* val);
|
||||
void* c_BinarySearchST_Get(const c_BinarySearchST_t* st, const void* key);
|
||||
c_err_t c_BinarySearchST_Delete(c_BinarySearchST_t* st, const void* key);
|
||||
|
||||
#endif /*INCLUDED_C_BINARYSEARCHST_H*/
|
||||
@@ -0,0 +1,72 @@
|
||||
#include "c_BinarySearchST.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define EXPECT_EQ(actual, expected, msg) \
|
||||
do { \
|
||||
if ((actual) != (expected)) { \
|
||||
printf(" [X] Assert Failed: %s (Expected %d, got %d)\n", msg, (int)(expected), (int)(actual)); \
|
||||
return C_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// Bulk structural values block mapped to compact numeric lookup keys
|
||||
typedef struct {
|
||||
char cluster_name[32];
|
||||
double operational_load;
|
||||
int backup_port;
|
||||
} DNSProfile;
|
||||
|
||||
int compareIntKeys(const void* a, const void* b) {
|
||||
return (*(int*)a - *(int*)b);
|
||||
}
|
||||
|
||||
c_bool_t test_binary_search_st(void) {
|
||||
c_BinarySearchST_t st;
|
||||
|
||||
// Initialize with a capacity of 2 to verify dynamic parallel allocation resize cycles
|
||||
EXPECT_EQ(c_BinarySearchST_Init(&st, 2, sizeof(int), sizeof(DNSProfile), compareIntKeys), C_ERR_OK, "Init failed");
|
||||
|
||||
int k1 = 8080; DNSProfile v1 = { "Primary Cluster", 0.45, 9001 };
|
||||
int k2 = 4433; DNSProfile v2 = { "Secure Edge Node", 0.12, 9002 };
|
||||
int k3 = 7021; DNSProfile v3 = { "Fallback Stack", 0.89, 9003 };
|
||||
|
||||
// 1. Data Entry Flow
|
||||
EXPECT_EQ(c_BinarySearchST_Put(&st, &k1, &v1), C_ERR_OK, "Put k1 failed");
|
||||
EXPECT_EQ(c_BinarySearchST_Put(&st, &k2, &v2), C_ERR_OK, "Put k2 failed");
|
||||
EXPECT_EQ(c_BinarySearchST_Put(&st, &k3, &v3), C_ERR_OK, "Put k3 failed (Resize step validation)");
|
||||
EXPECT_EQ(st.size, 3, "Symbol Table size tracker count incorrect");
|
||||
|
||||
// 2. Overwrite Verification
|
||||
DNSProfile v1_updated = { "Primary Cluster v2", 0.52, 9001 };
|
||||
EXPECT_EQ(c_BinarySearchST_Put(&st, &k1, &v1_updated), C_ERR_OK, "Overwriting entry failed");
|
||||
EXPECT_EQ(st.size, 3, "Size increased incorrectly during an overwrite operation");
|
||||
|
||||
// 3. Data Retrieval lookups
|
||||
int look_key = 8080;
|
||||
DNSProfile* fetched = (DNSProfile*)c_BinarySearchST_Get(&st, &look_key);
|
||||
EXPECT_EQ(fetched != NULL && strcmp(fetched->cluster_name, "Primary Cluster v2") == 0, C_TRUE, "Lookup retrieved incorrect mapping segment");
|
||||
|
||||
int miss_key = 9999;
|
||||
EXPECT_EQ(c_BinarySearchST_Get(&st, &miss_key) == NULL, C_TRUE, "Key miss lookup did not return NULL");
|
||||
|
||||
// 4. Deletion Cycles
|
||||
int delete_key = 4433;
|
||||
EXPECT_EQ(c_BinarySearchST_Delete(&st, &delete_key), C_ERR_OK, "Deletion operation crashed");
|
||||
EXPECT_EQ(c_BinarySearchST_Contains(&st, &delete_key), C_FALSE, "Deleted element still reported within lookups");
|
||||
EXPECT_EQ(st.size, 2, "Size tracker did not reduce correctly after deletion");
|
||||
|
||||
c_BinarySearchST_Destroy(&st);
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Starting Framework Unit Testing: c_BinarySearchST ===\n");
|
||||
|
||||
if (test_binary_search_st()) {
|
||||
printf(" [PASS] Symbol Table Parallel Array Processing and Retrieval Verified Successfully.\n");
|
||||
} else {
|
||||
printf(" [FAIL] Symbol Table Component Encountered Evaluation Errors.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
#include <c_HashMap.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
#define C_HASHMAP_LOAD_FACTOR_THRESHOLD 0.75f
|
||||
#define DEFAULT_CAPACITY 16
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
// Internal Helper: Doubles bucket allocations and rehashes entries
|
||||
static c_err_t hashmap_resize(c_HashMap_t* self) {
|
||||
c_size_t new_capacity = self->capacity * 2;
|
||||
c_HashMapEntry_t** new_buckets = (c_HashMapEntry_t**)C_CALLOC(new_capacity, sizeof(c_HashMapEntry_t*));
|
||||
if (!new_buckets) return C_ERR_NOMEM;
|
||||
|
||||
// Migrate entries over from old buckets array
|
||||
for (c_size_t i = 0; i < self->capacity; i++) {
|
||||
c_HashMapEntry_t* entry = self->buckets[i];
|
||||
while (entry != NULL) {
|
||||
c_HashMapEntry_t* next = entry->next;
|
||||
|
||||
// Recompute new bucket index mapping constraints
|
||||
uint32_t raw_hash = self->hash(entry->key, self->key_size);
|
||||
c_size_t new_index = raw_hash % new_capacity;
|
||||
|
||||
// Link into the new bucket array chain head
|
||||
entry->next = new_buckets[new_index];
|
||||
new_buckets[new_index] = entry;
|
||||
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
|
||||
C_FREE(self->buckets);
|
||||
self->buckets = new_buckets;
|
||||
self->capacity = new_capacity;
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void hashmap_iter_advance_to_valid(c_HashMapKeyIter_t* self) {
|
||||
while (self->bucket_index < self->map->capacity) {
|
||||
// 如果當前桶子有鏈結節點,繫結其指標的指標
|
||||
if (self->map->buckets[self->bucket_index] != NULL) {
|
||||
self->entry = &self->map->buckets[self->bucket_index];
|
||||
return;
|
||||
}
|
||||
self->bucket_index++;
|
||||
}
|
||||
// 若找不到任何有效節點,重置為 NULL 象徵迭代結束
|
||||
self->entry = NULL;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_HashMap_Init(c_HashMap_t* self, int key_size, int value_size, c_size_t initial_capacity,
|
||||
c_HashMap_Hash_f hash, c_HashMap_Compare_f compare) {
|
||||
if (!self || key_size <= 0 || value_size <= 0 || !hash || !compare) return C_ERR_PARAM;
|
||||
|
||||
self->capacity = (initial_capacity > 0) ? initial_capacity : DEFAULT_CAPACITY;
|
||||
self->size = 0;
|
||||
self->key_size = key_size;
|
||||
self->value_size = value_size;
|
||||
self->hash = hash;
|
||||
self->compare = compare;
|
||||
|
||||
self->buckets = (c_HashMapEntry_t**)C_CALLOC(self->capacity, sizeof(c_HashMapEntry_t*));
|
||||
if (!self->buckets) {
|
||||
self->capacity = 0;
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
void c_HashMap_Destroy(c_HashMap_t* self) {
|
||||
if (!self) return;
|
||||
|
||||
for (c_size_t i = 0; i < self->capacity; i++) {
|
||||
c_HashMapEntry_t* entry = self->buckets[i];
|
||||
while (entry != NULL) {
|
||||
c_HashMapEntry_t* next = entry->next;
|
||||
// C_FREE(entry->key);
|
||||
// C_FREE(entry->value);
|
||||
C_FREE(entry);
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
C_FREE(self->buckets);
|
||||
self->buckets = NULL;
|
||||
self->capacity = 0;
|
||||
self->size = 0;
|
||||
}
|
||||
|
||||
|
||||
// Maps/Overwrites keys to value entities in O(1) average time complexity
|
||||
c_err_t c_HashMap_Put(c_HashMap_t* self, const void* key, const void* value) {
|
||||
if (!self || !self->buckets || !key || !value) return C_ERR_PARAM;
|
||||
|
||||
// Trigger dynamic scale-out adjustments if load boundaries criteria are exceeded
|
||||
if ((float)(self->size + 1) / self->capacity >= C_HASHMAP_LOAD_FACTOR_THRESHOLD) {
|
||||
if (hashmap_resize(self) != C_ERR_SUCCESS) return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
uint32_t raw_hash = self->hash(key, self->key_size);
|
||||
c_size_t index = raw_hash % self->capacity;
|
||||
|
||||
// Scan the collision chain to check if the key already exists
|
||||
c_HashMapEntry_t* entry = self->buckets[index];
|
||||
while (entry != NULL) {
|
||||
if (self->compare(entry->key, key, self->key_size) == 0) {
|
||||
// Overwrite existing value mapping using deep copy semantics
|
||||
memcpy(entry->value, value, self->value_size);
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
// Allocate a new node entry if the key does not exist
|
||||
int size = (int)sizeof(c_HashMapEntry_t) + self->key_size + self->value_size;
|
||||
size = C_ALIGN_UPB(size, C_ALIGN_SIZE);
|
||||
c_HashMapEntry_t* new_entry = (c_HashMapEntry_t*)C_ALLOC(size);
|
||||
if (!new_entry) return C_ERR_NOMEM;
|
||||
|
||||
new_entry->key = new_entry+1;
|
||||
new_entry->value = new_entry->key + self->key_size;
|
||||
|
||||
// Deep copy payload bounds properties
|
||||
memcpy(new_entry->key, key, self->key_size);
|
||||
memcpy(new_entry->value, value, self->value_size);
|
||||
|
||||
// Single-chain head link injection (O(1))
|
||||
new_entry->next = self->buckets[index];
|
||||
self->buckets[index] = new_entry;
|
||||
self->size++;
|
||||
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
// Fetches value references safely into user-allocated destination spaces
|
||||
c_err_t c_HashMap_Get(c_HashMap_t* self, const void* key, void* out_value) {
|
||||
if (!self || !self->buckets || !key || !out_value) return C_ERR_PARAM;
|
||||
|
||||
uint32_t raw_hash = self->hash(key, self->key_size);
|
||||
c_size_t index = raw_hash % self->capacity;
|
||||
|
||||
c_HashMapEntry_t* entry = self->buckets[index];
|
||||
while (entry != NULL) {
|
||||
if (self->compare(entry->key, key, self->key_size) == 0) {
|
||||
memcpy(out_value, entry->value, self->value_size);
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
return C_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Unlinks map items matching key contexts safely (O(1) average time complexity)
|
||||
c_err_t c_HashMap_Remove(c_HashMap_t* self, const void* key) {
|
||||
if (!self || !self->buckets || !key) return C_ERR_PARAM;
|
||||
|
||||
uint32_t raw_hash = self->hash(key, self->key_size);
|
||||
c_size_t index = raw_hash % self->capacity;
|
||||
|
||||
c_HashMapEntry_t** curr = &self->buckets[index];
|
||||
while (*curr != NULL) {
|
||||
if (self->compare((*curr)->key, key, self->key_size) == 0) {
|
||||
c_HashMapEntry_t* to_delete = *curr;
|
||||
*curr = to_delete->next; // Unlink node entry frame properties
|
||||
|
||||
// free(to_delete->key);
|
||||
// free(to_delete->value);
|
||||
C_FREE(to_delete);
|
||||
self->size--;
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
curr = &(*curr)->next;
|
||||
}
|
||||
|
||||
return C_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
c_bool_t c_HashMap_Contains(c_HashMap_t* self, const void* key) {
|
||||
if (!self || !self->buckets || !key) return C_FALSE;
|
||||
|
||||
uint32_t raw_hash = self->hash(key, self->key_size);
|
||||
c_size_t index = raw_hash % self->capacity;
|
||||
|
||||
c_HashMapEntry_t* entry = self->buckets[index];
|
||||
while (entry != NULL) {
|
||||
if (self->compare(entry->key, key, self->key_size) == 0) return C_TRUE;
|
||||
entry = entry->next;
|
||||
}
|
||||
return C_FALSE;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
void c_HashMapKeyIter_Init(c_HashMapKeyIter_t* self, c_HashMap_t* map) {
|
||||
if (!self || !map) return;
|
||||
self->map = map;
|
||||
self->bucket_index = 0;
|
||||
self->entry = NULL;
|
||||
|
||||
// 初始化時先定位到第一個有效節點
|
||||
hashmap_iter_advance_to_valid(self);
|
||||
}
|
||||
|
||||
// 檢查是否還有下一個元素
|
||||
c_bool_t c_HashMapKeyIter_HasNext(c_HashMapKeyIter_t* self) {
|
||||
if (!self || !self->entry || !*(self->entry)) return C_FALSE;
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
// 查看目前指向的鍵(Key)指標 (不前進)
|
||||
void* c_HashMapKeyIter_Get(c_HashMapKeyIter_t* self) {
|
||||
if (!c_HashMapKeyIter_HasNext(self)) return NULL;
|
||||
return (*(self->entry))->key;
|
||||
}
|
||||
|
||||
// 獲取目前指向的鍵(Key)指標,並將迭代器前進到下一個有效節點
|
||||
void* c_HashMapKeyIter_Next(c_HashMapKeyIter_t* self) {
|
||||
if (!c_HashMapKeyIter_HasNext(self)) return NULL;
|
||||
|
||||
c_HashMapEntry_t* curr = *(self->entry);
|
||||
void* key_ptr = curr->key;
|
||||
|
||||
// 如果當前衝突鏈結中還有下一個節點,直接移向 next
|
||||
if (curr->next != NULL) {
|
||||
self->entry = &(curr->next);
|
||||
} else {
|
||||
// 如果當前衝突鏈結已到底,前進到下一個桶子並搜尋有效節點
|
||||
self->bucket_index++;
|
||||
hashmap_iter_advance_to_valid(self);
|
||||
}
|
||||
|
||||
return key_ptr;
|
||||
}
|
||||
|
||||
// 迭代器安全刪除:在走訪期間以 O(1) 的平均複雜度斷開鏈結並釋放記憶體
|
||||
void c_HashMapKeyIter_Remove(c_HashMapKeyIter_t* self) {
|
||||
if (!c_HashMapKeyIter_HasNext(self)) return;
|
||||
|
||||
c_HashMapEntry_t* to_delete = *(self->entry);
|
||||
|
||||
// 關鍵指標斷開:讓前一個節點的 next(或是桶子的首節點指標)直接指向下一個節點
|
||||
*(self->entry) = to_delete->next;
|
||||
|
||||
// 釋放該 Entry 的深複製記憶體
|
||||
// free(to_delete->key);
|
||||
// free(to_delete->value);
|
||||
C_FREE(to_delete);
|
||||
|
||||
self->map->size--;
|
||||
|
||||
// 檢查斷開後當前位置是否為空(代表原本該桶子的衝突鏈結已走訪完畢)
|
||||
if (*(self->entry) == NULL) {
|
||||
// 前進到下一個桶子搜尋下一個有效節點
|
||||
self->bucket_index++;
|
||||
hashmap_iter_advance_to_valid(self);
|
||||
}
|
||||
// 備註:若 *(self->entry) != NULL,則 self->entry 自動留在了下一個節點上,不需額外處理
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#ifndef INCLUDED_C_HASHMAP_H
|
||||
#define INCLUDED_C_HASHMAP_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct c_HashMapEntry_t {
|
||||
void* key;
|
||||
void* value;
|
||||
struct c_HashMapEntry_t* next;
|
||||
} c_HashMapEntry_t;
|
||||
|
||||
typedef uint32_t (*c_HashMap_Hash_f)(const void* key, int key_size);
|
||||
typedef int (*c_HashMap_Compare_f)(const void* key1, const void* key2, int key_size);
|
||||
|
||||
typedef struct {
|
||||
c_HashMapEntry_t** buckets; // Array of entry linked list head pointers
|
||||
c_size_t capacity; // Number of buckets allocated
|
||||
c_size_t size; // Number of active key-value pairs stored
|
||||
int key_size; // Byte footprint of the key type
|
||||
int value_size; // Byte footprint of the value type
|
||||
c_HashMap_Hash_f hash; // User hash calculation function
|
||||
c_HashMap_Compare_f compare;// User key comparison function
|
||||
} c_HashMap_t;
|
||||
|
||||
typedef struct {
|
||||
c_HashMap_t* map; // 繫結的雜湊表
|
||||
c_size_t bucket_index; // 當前走訪的桶子索引 (Bucket Index)
|
||||
c_HashMapEntry_t** entry; // 指向當前節點指標的指標,用於 O(1) 安全刪除
|
||||
} c_HashMapKeyIter_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
c_err_t c_HashMap_Init(c_HashMap_t* self, int key_size, int value_size, c_size_t initial_capacity,
|
||||
c_HashMap_Hash_f hash, c_HashMap_Compare_f compare);
|
||||
void c_HashMap_Destroy(c_HashMap_t* self);
|
||||
|
||||
c_err_t c_HashMap_Put(c_HashMap_t* self, const void* key, const void* value);
|
||||
c_err_t c_HashMap_Get(c_HashMap_t* self, const void* key, void* out_value);
|
||||
c_err_t c_HashMap_Remove(c_HashMap_t* self, const void* key);
|
||||
c_bool_t c_HashMap_Contains(c_HashMap_t* self, const void* key);
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
void c_HashMapKeyIter_Init(c_HashMapKeyIter_t* self, c_HashMap_t* map);
|
||||
c_bool_t c_HashMapKeyIter_HasNext(c_HashMapKeyIter_t* self);
|
||||
void* c_HashMapKeyIter_Next(c_HashMapKeyIter_t* self);
|
||||
void* c_HashMapKeyIter_Get(c_HashMapKeyIter_t* self);
|
||||
void c_HashMapKeyIter_Remove(c_HashMapKeyIter_t* self);
|
||||
|
||||
#endif /*INCLUDED_C_HASHMAP_H*/
|
||||
@@ -0,0 +1,99 @@
|
||||
#include "c_HashMap.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct {
|
||||
char username[32];
|
||||
} UserKey_t;
|
||||
|
||||
typedef struct {
|
||||
int user_uid;
|
||||
int permissions_mask;
|
||||
} ProfileValue_t;
|
||||
|
||||
// Standard high-distribution DJB2 Hash function implementation
|
||||
uint32_t hash_djb2(const void* key, int key_size) {
|
||||
const char* str = ((UserKey_t*)key)->username;
|
||||
uint32_t hash = 5381;
|
||||
int c;
|
||||
while ((c = (unsigned char)*str++)) {
|
||||
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
int compare_user_keys(const void* a, const void* b, int key_size) {
|
||||
return strcmp(((UserKey_t*)a)->username, ((UserKey_t*)b)->username);
|
||||
}
|
||||
|
||||
void test_log(const char* name) {
|
||||
printf("[PASS] %s\n", name);
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
printf("==================================================\n");
|
||||
printf(" Starting Generic Hash Map Unit Testing Suite\n");
|
||||
printf("==================================================\n\n");
|
||||
|
||||
c_HashMap_t map;
|
||||
// Set a small capacity of 4 to test scaling re-hashing logic mid-flight
|
||||
c_err_t err = c_HashMap_Init(&map, sizeof(UserKey_t), sizeof(ProfileValue_t), 4, hash_djb2, compare_user_keys);
|
||||
assert(err == C_ERR_SUCCESS);
|
||||
assert(map.size == 0);
|
||||
test_log("1. Hash Map allocation and callback structures ready");
|
||||
|
||||
UserKey_t k1 = {"admin"}; ProfileValue_t v1 = {9001, 0xFF};
|
||||
UserKey_t k2 = {"developer"}; ProfileValue_t v2 = {9002, 0x0F};
|
||||
UserKey_t k3 = {"guest"}; ProfileValue_t v3 = {9003, 0x01};
|
||||
|
||||
// ==========================================
|
||||
// 2. Testing Insertions & Resizing
|
||||
// ==========================================
|
||||
c_HashMap_Put(&map, &k1, &v1);
|
||||
c_HashMap_Put(&map, &k2, &v2);
|
||||
|
||||
// Inserting 3rd item triggers 3/4 = 0.75 load factor threshold, forcing scale-out to capacity 8
|
||||
err = c_HashMap_Put(&map, &k3, &v3);
|
||||
assert(err == C_ERR_SUCCESS);
|
||||
assert(map.size == 3);
|
||||
assert(map.capacity == 8); // Capacity successfully doubled automatically
|
||||
test_log("2. Mapping assertions and dynamic scale-out rehashing verified");
|
||||
|
||||
// ==========================================
|
||||
// 3. Testing Lookups (Get)
|
||||
// ==========================================
|
||||
ProfileValue_t out_buf;
|
||||
err = c_HashMap_Get(&map, &k2, &out_buf);
|
||||
assert(err == C_ERR_SUCCESS);
|
||||
assert(out_buf.user_uid == 9002);
|
||||
assert(out_buf.permissions_mask == 0x0F);
|
||||
|
||||
// Overwrite test validation
|
||||
ProfileValue_t v1_updated = {9001, 0xAA};
|
||||
c_HashMap_Put(&map, &k1, &v1_updated);
|
||||
c_HashMap_Get(&map, &k1, &out_buf);
|
||||
assert(out_buf.permissions_mask == 0xAA);
|
||||
test_log("3. Mapping value extractions and key overrides verified");
|
||||
|
||||
// ==========================================
|
||||
// 4. Testing Deletions (Remove)
|
||||
// ==========================================
|
||||
assert(c_HashMap_Contains(&map, &k3) == C_TRUE);
|
||||
err = c_HashMap_Remove(&map, &k3);
|
||||
assert(err == C_ERR_SUCCESS);
|
||||
assert(map.size == 2);
|
||||
assert(c_HashMap_Contains(&map, &k3) == C_FALSE);
|
||||
|
||||
// Not found tracking assertions checks
|
||||
assert(c_HashMap_Get(&map, &k3, &out_buf) == C_ERR_NOT_FOUND);
|
||||
test_log("4. Chain unlinking and item deletions verified");
|
||||
|
||||
c_HashMap_Destroy(&map);
|
||||
test_log("5. Resource pool cleanup teardown success");
|
||||
|
||||
printf("\n==================================================\n");
|
||||
printf(" Success! Universal defensive Hash Map behaves correctly!\n");
|
||||
printf("==================================================\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
#include "c_HashMap.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
char name[32];
|
||||
} StringKey_t;
|
||||
|
||||
typedef struct {
|
||||
int price;
|
||||
} IntValue_t;
|
||||
|
||||
// 簡單字串雜湊回呼 (DJB2)
|
||||
uint32_t test_hash(const void* key, int key_size) {
|
||||
const char* str = ((StringKey_t*)key)->name;
|
||||
uint32_t hash = 5381;
|
||||
int c;
|
||||
while ((c = (unsigned char)*str++)) hash = ((hash << 5) + hash) + c;
|
||||
return hash;
|
||||
}
|
||||
|
||||
int test_compare(const void* a, const void* b, int key_size) {
|
||||
return strcmp(((StringKey_t*)a)->name, ((StringKey_t*)b)->name);
|
||||
}
|
||||
|
||||
void test_log(const char* name) {
|
||||
printf("[PASS] %s\n", name);
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("==================================================\n");
|
||||
printf(" 開始執行 c_HashMapKeyIter_t 鍵值迭代器單元測試\n");
|
||||
printf("==================================================\n\n");
|
||||
|
||||
c_HashMap_t map;
|
||||
c_HashMap_Init(&map, sizeof(StringKey_t), sizeof(IntValue_t), 4, test_hash, test_compare);
|
||||
|
||||
StringKey_t k1 = {"Apple"}; IntValue_t v1 = {100};
|
||||
StringKey_t k2 = {"Banana"}; IntValue_t v2 = {200}; // 預計在走訪時刪除此項
|
||||
StringKey_t k3 = {"Cherry"}; IntValue_t v3 = {300};
|
||||
|
||||
c_HashMap_Put(&map, &k1, &v1);
|
||||
c_HashMap_Put(&map, &k2, &v2);
|
||||
c_HashMap_Put(&map, &k3, &v3);
|
||||
assert(map.size == 3);
|
||||
|
||||
// ==========================================
|
||||
// 1. 測試迭代器基本走訪
|
||||
// ==========================================
|
||||
c_HashMapKeyIter_t iter;
|
||||
c_HashMapKeyIter_Init(&iter, &map);
|
||||
|
||||
printf("當前雜湊表包含的鍵值:\n");
|
||||
int visit_count = 0;
|
||||
while (c_HashMapKeyIter_HasNext(&iter)) {
|
||||
StringKey_t* key = (StringKey_t*)c_HashMapKeyIter_Next(&iter);
|
||||
printf(" - 鍵名稱: %s\n", key->name);
|
||||
visit_count++;
|
||||
}
|
||||
assert(visit_count == 3);
|
||||
test_log("1. 迭代器成功走訪所有雜湊桶中的元素");
|
||||
|
||||
// ==========================================
|
||||
// 2. 測試走訪中安全刪除 (Remove "Banana")
|
||||
// ==========================================
|
||||
c_HashMapKeyIter_Init(&iter, &map);
|
||||
while (c_HashMapKeyIter_HasNext(&iter)) {
|
||||
StringKey_t* key = (StringKey_t*)c_HashMapKeyIter_Get(&iter);
|
||||
|
||||
if (strcmp(key->name, "Banana") == 0) {
|
||||
printf(" [Log] 找到目標 '%s',執行迭代器刪除...\n", key->name);
|
||||
c_HashMapKeyIter_Remove(&iter);
|
||||
// 呼叫 Remove 後,迭代器已自動處理好指標轉移,此處不可呼叫 Next()
|
||||
} else {
|
||||
c_HashMapKeyIter_Next(&iter); // 沒刪除時才手動前進
|
||||
}
|
||||
}
|
||||
assert(map.size == 2);
|
||||
test_log("2. 迭代器中序安全刪除指定元素成功");
|
||||
|
||||
// ==========================================
|
||||
// 3. 驗證刪除後的雜湊表狀態
|
||||
// ==========================================
|
||||
assert(c_HashMap_Contains(&map, &k1) == C_TRUE);
|
||||
assert(c_HashMap_Contains(&map, &k2) == C_FALSE); // Banana 應不見
|
||||
assert(c_HashMap_Contains(&map, &k3) == C_TRUE);
|
||||
test_log("3. 最終雜湊表結構完好度驗證成功");
|
||||
|
||||
c_HashMap_Destroy(&map);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#include <c_HashSet.h>
|
||||
|
||||
// 虛擬佔位常數,所有集合元素在底層對應同一個 Dummy 值的地址
|
||||
static const int dummy_value = 1;
|
||||
|
||||
c_err_t c_HashSet_Init(c_HashSet_t* self, int obj_size, c_size_t initial_capacity,
|
||||
c_HashMap_Hash_f hash, c_HashMap_Compare_f compare) {
|
||||
if (!self) return C_ERR_PARAM;
|
||||
|
||||
// 初始化底層對映的雜湊表,value_size 固定設為常數大小
|
||||
return c_HashMap_Init(&self->map, obj_size, sizeof(int), initial_capacity, hash, compare);
|
||||
}
|
||||
|
||||
void c_HashSet_Destroy(c_HashSet_t* self) {
|
||||
if (!self) return;
|
||||
c_HashMap_Destroy(&self->map);
|
||||
}
|
||||
|
||||
// 推入元素:若元素已存在則攔截並報錯,確保唯一性
|
||||
c_err_t c_HashSet_Add(c_HashSet_t* self, const void* obj) {
|
||||
if (!self || !obj) return C_ERR_PARAM;
|
||||
|
||||
// 先檢查是否已經存在此元素
|
||||
if (c_HashMap_Contains(&self->map, obj)) {
|
||||
return C_ERR_ALREADY_EXISTS;
|
||||
}
|
||||
|
||||
// 將物件當作 Key 寫入,Value 塞入 Dummy 常數
|
||||
return c_HashMap_Put(&self->map, obj, &dummy_value);
|
||||
}
|
||||
|
||||
c_err_t c_HashSet_Remove(c_HashSet_t* self, const void* obj) {
|
||||
if (!self || !obj) return C_ERR_PARAM;
|
||||
return c_HashMap_Remove(&self->map, obj);
|
||||
}
|
||||
|
||||
c_bool_t c_HashSet_Contains(c_HashSet_t* self, const void* obj) {
|
||||
if (!self || !obj) return C_FALSE;
|
||||
return c_HashMap_Contains(&self->map, obj);
|
||||
}
|
||||
|
||||
c_size_t c_HashSet_GetSize(const c_HashSet_t* self) {
|
||||
if (!self) return 0;
|
||||
return self->map.size;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef INCLUDED_C_HASHSET_H
|
||||
#define INCLUDED_C_HASHSET_H
|
||||
|
||||
#ifndef INCLUDED_C_HASHMAP_H
|
||||
#include <c_HashMap.h>
|
||||
#endif /*INCLUDED_C_HASHMAP_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
c_HashMap_t map; // 底層由 HashMap 驅動
|
||||
} c_HashSet_t;
|
||||
|
||||
// 集合迭代器(直接重定向至您的 HashMapKeyIter)
|
||||
typedef c_HashMapKeyIter_t c_HashSetIter_t;
|
||||
|
||||
// 核心函數宣告
|
||||
c_err_t c_HashSet_Init(c_HashSet_t* self, int obj_size, c_size_t initial_capacity,
|
||||
c_HashMap_Hash_f hash, c_HashMap_Compare_f compare);
|
||||
void c_HashSet_Destroy(c_HashSet_t* self);
|
||||
|
||||
c_err_t c_HashSet_Add(c_HashSet_t* self, const void* obj);
|
||||
c_err_t c_HashSet_Remove(c_HashSet_t* self, const void* obj);
|
||||
c_bool_t c_HashSet_Contains(c_HashSet_t* self, const void* obj);
|
||||
c_size_t c_HashSet_GetSize(const c_HashSet_t* self);
|
||||
|
||||
// 集合迭代器巨集/函數重定向(完美保持一致性)
|
||||
#define c_HashSetIter_Init(self, set) c_HashMapKeyIter_Init(self, &(set)->map)
|
||||
#define c_HashSetIter_HasNext(self) c_HashMapKeyIter_HasNext(self)
|
||||
#define c_HashSetIter_Get(self) c_HashMapKeyIter_Get(self)
|
||||
#define c_HashSetIter_Next(self) c_HashMapKeyIter_Next(self)
|
||||
#define c_HashSetIter_Remove(self) c_HashMapKeyIter_Remove(self)
|
||||
|
||||
#endif /*INCLUDED_C_HASHSET_H*/
|
||||
@@ -0,0 +1,95 @@
|
||||
#include "c_HashSet.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
char ip_address[17];
|
||||
} IpAddress_t;
|
||||
|
||||
// 簡單字串雜湊回呼 (DJB2)
|
||||
uint32_t hash_ip(const void* key, int key_size) {
|
||||
const char* str = ((IpAddress_t*)key)->ip_address;
|
||||
uint32_t hash = 5381;
|
||||
int c;
|
||||
while ((c = (unsigned char)*str++)) hash = ((hash << 5) + hash) + c;
|
||||
return hash;
|
||||
}
|
||||
|
||||
int compare_ip(const void* a, const void* b, int key_size) {
|
||||
return strcmp(((IpAddress_t*)a)->ip_address, ((IpAddress_t*)b)->ip_address);
|
||||
}
|
||||
|
||||
void test_log(const char* name) {
|
||||
printf("[PASS] %s\n", name);
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("==================================================\n");
|
||||
printf(" 開始執行 c_HashSet_t 唯一性集合組件單元測試\n");
|
||||
printf("==================================================\n\n");
|
||||
|
||||
c_HashSet_t ip_blacklist;
|
||||
c_err_t err = c_HashSet_Init(&ip_blacklist, sizeof(IpAddress_t), 4, hash_ip, compare_ip);
|
||||
assert(err == C_ERR_SUCCESS);
|
||||
assert(c_HashSet_GetSize(&ip_blacklist) == 0);
|
||||
test_log("1. 集合容器架構初始化成功");
|
||||
|
||||
IpAddress_t ip1 = {"192.168.1.1"};
|
||||
IpAddress_t ip2 = {"10.0.0.1"};
|
||||
IpAddress_t ip3 = {"172.16.0.1"};
|
||||
|
||||
// ==========================================
|
||||
// 2. 測試元素新增與唯一性驗證 (Add)
|
||||
// ==========================================
|
||||
c_HashSet_Add(&ip_blacklist, &ip1);
|
||||
c_HashSet_Add(&ip_blacklist, &ip2);
|
||||
err = c_HashSet_Add(&ip_blacklist, &ip3);
|
||||
assert(err == C_ERR_SUCCESS);
|
||||
assert(c_HashSet_GetSize(&ip_blacklist) == 3);
|
||||
|
||||
// 核心斷言:重複新增相同的 IP 必須被攔截並回傳 C_ERR_ALREADY_EXISTS
|
||||
assert(c_HashSet_Add(&ip_blacklist, &ip1) == C_ERR_ALREADY_EXISTS);
|
||||
assert(c_HashSet_GetSize(&ip_blacklist) == 3); // 大小依然要是 3
|
||||
test_log("2. 元素值複製推入與重複唯一性攔截驗證成功");
|
||||
|
||||
// ==========================================
|
||||
// 3. 測試成員包含判斷 (Contains)
|
||||
// ==========================================
|
||||
assert(c_HashSet_Contains(&ip_blacklist, &ip1) == C_TRUE);
|
||||
|
||||
IpAddress_t safe_ip = {"8.8.8.8"};
|
||||
assert(c_HashSet_Contains(&ip_blacklist, &safe_ip) == C_FALSE);
|
||||
test_log("3. O(1) 集合成員包含度 (Contains) 檢索成功");
|
||||
|
||||
// ==========================================
|
||||
// 4. 測試集合迭代器走訪與安全刪除 (Remove "10.0.0.1")
|
||||
// ==========================================
|
||||
c_HashSetIter_t iter;
|
||||
c_HashSetIter_Init(&iter, &ip_blacklist);
|
||||
|
||||
printf("當前黑名單集合包含:\n");
|
||||
while (c_HashSetIter_HasNext(&iter)) {
|
||||
IpAddress_t* current_ip = (IpAddress_t*)c_HashSetIter_Get(&iter);
|
||||
printf(" - IP: %s\n", current_ip->ip_address);
|
||||
|
||||
if (strcmp(current_ip->ip_address, "10.0.0.1") == 0) {
|
||||
c_HashSetIter_Remove(&iter); // 在走訪期間從集合中安全剔除
|
||||
printf(" [Log] 已透過迭代器將 10.0.0.1 從集合中移除\n");
|
||||
} else {
|
||||
c_HashSetIter_Next(&iter);
|
||||
}
|
||||
}
|
||||
|
||||
assert(c_HashSet_GetSize(&ip_blacklist) == 2);
|
||||
assert(c_HashSet_Contains(&ip_blacklist, &ip2) == C_FALSE); // 10.0.0.1 應確實消失
|
||||
test_log("4. 集合重定向迭代器走訪與 O(1) 安全剔除成功");
|
||||
|
||||
c_HashSet_Destroy(&ip_blacklist);
|
||||
test_log("5. 集合控制單元資源釋放成功");
|
||||
|
||||
printf("\n==================================================\n");
|
||||
printf(" 恭喜!基於組合模式複用的 c_HashSet_t 測試全數完美通過!\n");
|
||||
printf("==================================================\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
#include <c_LinearProbingHashST.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
/**
|
||||
* FNV-1a baseline string/scalar data hash scrambling algorithm.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
uint32_t c_LPHash_DefaultHash(const void* key, c_size_t key_size) {
|
||||
const uint8_t* data = (const uint8_t*)key;
|
||||
uint32_t hash = 0x811C9DC5;
|
||||
for (c_size_t i = 0; i < key_size; i++) {
|
||||
hash ^= data[i];
|
||||
hash *= 0x01000193;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_LinearProbingHashST_Init(c_LinearProbingHashST_t* st, c_size_t initial_capacity,
|
||||
c_size_t key_size, c_size_t val_size,
|
||||
uint32_t (*hash_fn)(const void*, c_size_t),
|
||||
int (*key_compar)(const void*, const void*)) {
|
||||
if (st == NULL || initial_capacity == 0 || key_size == 0 || val_size == 0 || key_compar == NULL) {
|
||||
return C_ERR_PARAM;
|
||||
}
|
||||
|
||||
st->M = initial_capacity;
|
||||
st->N = 0;
|
||||
st->key_size = key_size;
|
||||
st->val_size = val_size;
|
||||
st->hash_fn = (hash_fn != NULL) ? hash_fn : c_LPHash_DefaultHash;
|
||||
st->key_compar = key_compar;
|
||||
|
||||
st->keys = C_ALLOC(st->M * key_size);
|
||||
st->vals = C_ALLOC(st->M * val_size);
|
||||
st->occupied = (c_bool_t*)C_ALLOC(st->M * sizeof(c_bool_t));
|
||||
|
||||
if (st->keys == NULL || st->vals == NULL || st->occupied == NULL) {
|
||||
C_FREE(st->keys); C_FREE(st->vals); C_FREE(st->occupied);
|
||||
st->keys = NULL; st->vals = NULL; st->occupied = NULL;
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
memset(st->occupied, C_FALSE, st->M * sizeof(c_bool_t));
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void c_LinearProbingHashST_Destroy(c_LinearProbingHashST_t* st) {
|
||||
if (st) {
|
||||
C_FREE(st->keys); st->keys = NULL;
|
||||
C_FREE(st->vals); st->vals = NULL;
|
||||
C_FREE(st->occupied); st->occupied = NULL;
|
||||
st->M = 0;
|
||||
st->N = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicit internal resizing routing handler.
|
||||
* Essential for keeping the Load Factor (alpha) under 0.5 to prevent clustering.
|
||||
*/
|
||||
static c_err_t c_LinearProbingHashST_Resize(c_LinearProbingHashST_t* st, c_size_t capacity) {
|
||||
c_LinearProbingHashST_t temp_st;
|
||||
c_err_t err = c_LinearProbingHashST_Init(&temp_st, capacity, st->key_size, st->val_size, st->hash_fn, st->key_compar);
|
||||
if (err != C_ERR_OK) return err;
|
||||
|
||||
char* keys_base = (char*)st->keys;
|
||||
char* vals_base = (char*)st->vals;
|
||||
c_size_t ks = st->key_size;
|
||||
c_size_t vs = st->val_size;
|
||||
|
||||
// Rehash and insert all existing active items into the new, expanded table footprint
|
||||
for (c_size_t i = 0; i < st->M; i++) {
|
||||
if (st->occupied[i]) {
|
||||
extern c_err_t c_LinearProbingHashST_Put(c_LinearProbingHashST_t*, const void*, const void*);
|
||||
err = c_LinearProbingHashST_Put(&temp_st, keys_base + (i * ks), vals_base + (i * vs));
|
||||
if (err != C_ERR_OK) {
|
||||
c_LinearProbingHashST_Destroy(&temp_st);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Swap parameters to apply the newly rehashed table context
|
||||
C_FREE(st->keys); C_FREE(st->vals); C_FREE(st->occupied);
|
||||
st->keys = temp_st.keys;
|
||||
st->vals = temp_st.vals;
|
||||
st->occupied = temp_st.occupied;
|
||||
st->M = temp_st.M;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_err_t c_LinearProbingHashST_Put(c_LinearProbingHashST_t* st, const void* key, const void* val) {
|
||||
if (st == NULL || key == NULL || val == NULL) return C_ERR_PARAM;
|
||||
|
||||
// Enforce an upper bound load factor limit of 50% to mitigate clustering degradation
|
||||
if (st->N >= st->M / 2) {
|
||||
c_err_t err = c_LinearProbingHashST_Resize(st, st->M * 2);
|
||||
if (err != C_ERR_OK) return err;
|
||||
}
|
||||
|
||||
char* keys_base = (char*)st->keys;
|
||||
char* vals_base = (char*)st->vals;
|
||||
c_size_t ks = st->key_size;
|
||||
c_size_t vs = st->val_size;
|
||||
|
||||
c_size_t i;
|
||||
for (i = st->hash_fn(key, ks) % st->M; st->occupied[i]; i = (i + 1) % st->M) {
|
||||
if (st->key_compar(keys_base + (i * ks), key) == 0) {
|
||||
// Found existing key match: update values block payload in place
|
||||
memcpy(vals_base + (i * vs), val, vs);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert new item into the available open slot found by probing
|
||||
memcpy(keys_base + (i * ks), key, ks);
|
||||
memcpy(vals_base + (i * vs), val, vs);
|
||||
st->occupied[i] = C_TRUE;
|
||||
st->N++;
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void* c_LinearProbingHashST_Get(const c_LinearProbingHashST_t* st, const void* key) {
|
||||
if (st == NULL || st->keys == NULL || key == NULL) return NULL;
|
||||
|
||||
char* keys_base = (char*)st->keys;
|
||||
c_size_t ks = st->key_size;
|
||||
|
||||
for (c_size_t i = st->hash_fn(key, ks) % st->M; st->occupied[i]; i = (i + 1) % st->M) {
|
||||
if (st->key_compar(keys_base + (i * ks), key) == 0) {
|
||||
return (char*)st->vals + (i * st->val_size);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
c_bool_t c_LinearProbingHashST_Contains(const c_LinearProbingHashST_t* st, const void* key) {
|
||||
return c_LinearProbingHashST_Get(st, key) != NULL;
|
||||
}
|
||||
|
||||
c_err_t c_LinearProbingHashST_Delete(c_LinearProbingHashST_t* st, const void* key) {
|
||||
if (st == NULL || key == NULL) return C_ERR_PARAM;
|
||||
|
||||
char* keys_base = (char*)st->keys;
|
||||
c_size_t ks = st->key_size;
|
||||
c_size_t vs = st->val_size;
|
||||
|
||||
c_size_t i = st->hash_fn(key, ks) % st->M;
|
||||
while (st->occupied[i]) {
|
||||
if (st->key_compar(keys_base + (i * ks), key) == 0) {
|
||||
break;
|
||||
}
|
||||
i = (i + 1) % st->M;
|
||||
}
|
||||
|
||||
// Key to delete was not found in the hash table
|
||||
if (!st->occupied[i]) return C_ERR_NOT_FOUND;
|
||||
|
||||
// Hard delete: Free the targeted slot index flag
|
||||
st->occupied[i] = C_FALSE;
|
||||
st->N--;
|
||||
|
||||
// CRITICAL REQUIREMENT: Rehash all subsequent cluster elements
|
||||
// to bridge the open slot gap caused by deletion, preventing future search short-circuits.
|
||||
i = (i + 1) % st->M;
|
||||
while (st->occupied[i]) {
|
||||
// Capture old keys/values payload allocations locally
|
||||
void* key_to_rehash = C_ALLOC(ks);
|
||||
void* val_to_rehash = C_ALLOC(vs);
|
||||
memcpy(key_to_rehash, keys_base + (i * ks), ks);
|
||||
memcpy(val_to_rehash, (char*)st->vals + (i * vs), vs);
|
||||
|
||||
// Explicitly clear the current cluster entry tracking variables
|
||||
st->occupied[i] = C_FALSE;
|
||||
st->N--;
|
||||
|
||||
// Re-insert the captured element into the table using standard routing rules
|
||||
c_LinearProbingHashST_Put(st, key_to_rehash, val_to_rehash);
|
||||
|
||||
C_FREE(key_to_rehash);
|
||||
C_FREE(val_to_rehash);
|
||||
|
||||
i = (i + 1) % st->M;
|
||||
}
|
||||
|
||||
// Shrink the table capacity automatically if utilization drops below 12.5%
|
||||
if (st->N > 0 && st->N <= st->M / 8) {
|
||||
c_LinearProbingHashST_Resize(st, st->M / 2);
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef INCLUDED_C_LINEARPROBINGHASHST_H
|
||||
#define INCLUDED_C_LINEARPROBINGHASHST_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
// Linear Probing Hash Symbol Table Instance Layout
|
||||
typedef struct {
|
||||
void* keys; // Flat parallel array block storing keys
|
||||
void* vals; // Flat parallel array block storing values
|
||||
c_bool_t* occupied; // Flag array tracking whether a specific slot is filled
|
||||
|
||||
c_size_t M; // Linear probing table capacity (array size)
|
||||
c_size_t N; // Current active element count
|
||||
c_size_t key_size; // Size of each key in bytes
|
||||
c_size_t val_size; // Size of each value in bytes
|
||||
|
||||
uint32_t (*hash_fn)(const void* key, c_size_t key_size); // Hash function
|
||||
int (*key_compar)(const void*, const void*); // Key comparison rule pointer
|
||||
} c_LinearProbingHashST_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_LinearProbingHashST_Init(c_LinearProbingHashST_t* st, c_size_t initial_capacity,
|
||||
c_size_t key_size, c_size_t val_size,
|
||||
uint32_t (*hash_fn)(const void*, c_size_t),
|
||||
int (*key_compar)(const void*, const void*));
|
||||
void c_LinearProbingHashST_Destroy(c_LinearProbingHashST_t* st);
|
||||
c_err_t c_LinearProbingHashST_Put(c_LinearProbingHashST_t* st, const void* key, const void* val);
|
||||
void* c_LinearProbingHashST_Get(const c_LinearProbingHashST_t* st, const void* key);
|
||||
c_bool_t c_LinearProbingHashST_Contains(const c_LinearProbingHashST_t* st, const void* key);
|
||||
c_err_t c_LinearProbingHashST_Delete(c_LinearProbingHashST_t* st, const void* key);
|
||||
|
||||
#endif /*INCLUDED_C_LINEARPROBINGHASHST_H*/
|
||||
@@ -0,0 +1,76 @@
|
||||
#include "c_LinearProbingHashST.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define EXPECT_EQ(actual, expected, msg) \
|
||||
do { \
|
||||
if ((actual) != (expected)) { \
|
||||
printf(" [X] Assert Failed: %s (Expected %d, got %d)\n", msg, (int)(expected), (int)(actual)); \
|
||||
return C_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// Struct payload representing active channel properties
|
||||
typedef struct {
|
||||
uint32_t src_address;
|
||||
uint32_t dest_address;
|
||||
uint16_t block_length;
|
||||
} DMAControlBlock;
|
||||
|
||||
int compareUint16Keys(const void* a, const void* b) {
|
||||
return (*(const uint16_t*)a - *(const uint16_t*)b);
|
||||
}
|
||||
|
||||
c_bool_t test_linear_probing_hash_st(void) {
|
||||
c_LinearProbingHashST_t lpst;
|
||||
|
||||
// Start with a small capacity = 4 to guarantee load threshold resize checks fire (triggers at N >= 2)
|
||||
EXPECT_EQ(c_LinearProbingHashST_Init(&lpst, 4, sizeof(uint16_t), sizeof(DMAControlBlock), NULL, compareUint16Keys), C_ERR_OK, "Init failed");
|
||||
|
||||
uint16_t ch0 = 12; DMAControlBlock d0 = { 0x20000000, 0x40000000, 512 };
|
||||
uint16_t ch1 = 44; DMAControlBlock d1 = { 0x20001000, 0x40002000, 1024 };
|
||||
uint16_t ch2 = 19; DMAControlBlock d2 = { 0x10000000, 0x30000000, 256 };
|
||||
|
||||
// 1. Core Data Entry Flows
|
||||
EXPECT_EQ(c_LinearProbingHashST_Put(&lpst, &ch0, &d0), C_ERR_OK, "Put ch0 failed");
|
||||
EXPECT_EQ(c_LinearProbingHashST_Put(&lpst, &ch1, &d1), C_ERR_OK, "Put ch1 failed (Resizing expansion milestone)");
|
||||
EXPECT_EQ(c_LinearProbingHashST_Put(&lpst, &ch2, &d2), C_ERR_OK, "Put ch2 failed");
|
||||
EXPECT_EQ(lpst.N, 3, "Active count verification miscalculated");
|
||||
|
||||
// 2. Overwrite Mutation Check
|
||||
DMAControlBlock d0_updated = { 0x20000000, 0x40000000, 2048 };
|
||||
EXPECT_EQ(c_LinearProbingHashST_Put(&lpst, &ch0, &d0_updated), C_ERR_OK, "Overwriting entry failed");
|
||||
EXPECT_EQ(lpst.N, 3, "Size increased incorrectly during an overwrite operation");
|
||||
|
||||
// 3. Retrieval Lookups
|
||||
uint16_t look_ch = 12;
|
||||
DMAControlBlock* fetched = (DMAControlBlock*)c_LinearProbingHashST_Get(&lpst, &look_ch);
|
||||
EXPECT_EQ(fetched != NULL && fetched->block_length == 2048, C_TRUE, "Lookup retrieved incorrect mapping segment values");
|
||||
|
||||
uint16_t miss_ch = 99;
|
||||
EXPECT_EQ(c_LinearProbingHashST_Get(&lpst, &miss_ch) == NULL, C_TRUE, "Key miss lookup did not return NULL");
|
||||
EXPECT_EQ(c_LinearProbingHashST_Contains(&lpst, &look_ch), C_TRUE, "Contains reporting failure on active keys");
|
||||
|
||||
// 4. Verification of Cluster-Repair Mechanics on Delete
|
||||
EXPECT_EQ(c_LinearProbingHashST_Delete(&lpst, &look_ch), C_ERR_OK, "Deletion operation crashed");
|
||||
EXPECT_EQ(c_LinearProbingHashST_Contains(&lpst, &look_ch), C_FALSE, "Deleted element still reported within lookups");
|
||||
EXPECT_EQ(lpst.N, 2, "Size tracker did not reduce correctly after deletion");
|
||||
|
||||
// Assert that sibling entries pushed past index gaps remain accessible after cluster repair
|
||||
uint16_t look_sibling = 19;
|
||||
EXPECT_EQ(c_LinearProbingHashST_Contains(&lpst, &look_sibling), C_TRUE, "Linear probing gap repair broke adjacent cluster lookups");
|
||||
|
||||
c_LinearProbingHashST_Destroy(&lpst);
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Starting Framework Unit Testing: c_LinearProbingHashST ===\n");
|
||||
|
||||
if (test_linear_probing_hash_st()) {
|
||||
printf(" [PASS] Linear Probing Hash ST Optimization and Cluster Repair Lifecycles Verified Successfully.\n");
|
||||
} else {
|
||||
printf(" [FAIL] Linear Probing Hash Table Component Encountered Evaluation Errors.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
#include <c_RedBlackBST.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
// --- Structural Balancing Primitives ---
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_RBNode_t* c_RBBST_RotateLeft(c_RBNode_t* h) {
|
||||
c_RBNode_t* x = h->right;
|
||||
h->right = x->left;
|
||||
x->left = h;
|
||||
x->color = h->color;
|
||||
h->color = C_RB_RED;
|
||||
return x;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_RBNode_t* c_RBBST_RotateRight(c_RBNode_t* h) {
|
||||
c_RBNode_t* x = h->left;
|
||||
h->left = x->right;
|
||||
x->right = h;
|
||||
x->color = h->color;
|
||||
h->color = C_RB_RED;
|
||||
return x;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_RBBST_FlipColors(c_RBNode_t* h) {
|
||||
h->color = !h->color;
|
||||
if (h->left) h->left->color = !h->left->color;
|
||||
if (h->right) h->right->color = !h->right->color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and initializes a standalone tree node.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_RBNode_t* c_RBBST_CreateNode(const void* key, const void* val, c_size_t ks, c_size_t vs) {
|
||||
c_RBNode_t* node = (c_RBNode_t*)C_ALLOC(sizeof(c_RBNode_t) + ks + vs);
|
||||
if (node == NULL) return NULL;
|
||||
|
||||
node->left = NULL;
|
||||
node->right = NULL;
|
||||
node->color = C_RB_RED; // New nodes are always inserted as RED links
|
||||
memcpy(c_RBBST_NodeKey(node), key, ks);
|
||||
memcpy(c_RBBST_NodeVal(node, ks), val, vs);
|
||||
return node;
|
||||
}
|
||||
|
||||
static void c_RBBST_DestroyNodes(c_RBNode_t* node) {
|
||||
if (node == NULL) return;
|
||||
c_RBBST_DestroyNodes(node->left);
|
||||
c_RBBST_DestroyNodes(node->right);
|
||||
C_FREE(node);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_RedBlackBST_Init(c_RedBlackBST_t* tree, c_size_t key_size, c_size_t val_size,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
if (tree == NULL || key_size == 0 || val_size == 0 || compar == NULL) return C_ERR_PARAM;
|
||||
tree->root = NULL;
|
||||
tree->key_size = key_size;
|
||||
tree->val_size = val_size;
|
||||
tree->size = 0;
|
||||
tree->compar = compar;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void c_RedBlackBST_Destroy(c_RedBlackBST_t* tree) {
|
||||
if (tree) {
|
||||
c_RBBST_DestroyNodes(tree->root);
|
||||
tree->root = NULL;
|
||||
tree->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
c_bool_t c_RedBlackBST_Contains(const c_RedBlackBST_t* tree, const void* key) {
|
||||
if (tree == NULL || key == NULL) return C_FALSE;
|
||||
c_RBNode_t* curr = tree->root;
|
||||
while (curr != NULL) {
|
||||
int cmp = tree->compar(key, c_RBBST_NodeKey(curr));
|
||||
if (cmp == 0) return C_TRUE;
|
||||
curr = (cmp < 0) ? curr->left : curr->right;
|
||||
}
|
||||
return C_FALSE;
|
||||
}
|
||||
|
||||
void* c_RedBlackBST_Get(const c_RedBlackBST_t* tree, const void* key) {
|
||||
if (tree == NULL || key == NULL) return NULL;
|
||||
c_RBNode_t* curr = tree->root;
|
||||
while (curr != NULL) {
|
||||
int cmp = tree->compar(key, c_RBBST_NodeKey(curr));
|
||||
if (cmp == 0) return c_RBBST_NodeVal(curr, tree->key_size);
|
||||
curr = (cmp < 0) ? curr->left : curr->right;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive insertion core worker.
|
||||
*/
|
||||
static c_RBNode_t* c_RBBST_PutInternal(c_RedBlackBST_t* tree, c_RBNode_t* h,
|
||||
const void* key, const void* val, c_err_t* err) {
|
||||
if (h == NULL) {
|
||||
c_RBNode_t* node = c_RBBST_CreateNode(key, val, tree->key_size, tree->val_size);
|
||||
if (node == NULL) *err = C_ERR_NOMEM;
|
||||
else tree->size++;
|
||||
return node;
|
||||
}
|
||||
|
||||
int cmp = tree->compar(key, c_RBBST_NodeKey(h));
|
||||
if (cmp < 0) {
|
||||
h->left = c_RBBST_PutInternal(tree, h->left, key, val, err);
|
||||
} else if (cmp > 0) {
|
||||
h->right = c_RBBST_PutInternal(tree, h->right, key, val, err);
|
||||
} else {
|
||||
// Enforce update if key matches existing tracking cell
|
||||
memcpy(c_RBBST_NodeVal(h, tree->key_size), val, tree->val_size);
|
||||
}
|
||||
|
||||
// --- Left-Leaning Red-Black Balancing Pipeline Validation Steps ---
|
||||
// Condition 1: Right child is red, left child is black -> Rotate Left
|
||||
if (c_RBBST_IsRed(h->right) && !c_RBBST_IsRed(h->left)) {
|
||||
h = c_RBBST_RotateLeft(h);
|
||||
}
|
||||
// Condition 2: Left child and left grandchild are both red -> Rotate Right
|
||||
if (c_RBBST_IsRed(h->left) && c_RBBST_IsRed(h->left->left)) {
|
||||
h = c_RBBST_RotateRight(h);
|
||||
}
|
||||
// Condition 3: Both children are red -> Color Split Flip
|
||||
if (c_RBBST_IsRed(h->left) && c_RBBST_IsRed(h->right)) {
|
||||
c_RBBST_FlipColors(h);
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
c_err_t c_RedBlackBST_Put(c_RedBlackBST_t* tree, const void* key, const void* val) {
|
||||
if (tree == NULL || key == NULL || val == NULL) return C_ERR_PARAM;
|
||||
|
||||
c_err_t err = C_ERR_OK;
|
||||
tree->root = c_RBBST_PutInternal(tree, tree->root, key, val, &err);
|
||||
|
||||
if (tree->root != NULL) {
|
||||
tree->root->color = C_RB_BLACK; // Root link must consistently point black
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#ifndef INCLUDED_C_REDBLACKBST_H
|
||||
#define INCLUDED_C_REDBLACKBST_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
// Link Color Definitions
|
||||
typedef enum {
|
||||
C_RB_BLACK = 0,
|
||||
C_RB_RED = 1
|
||||
} c_RBColor_t;
|
||||
|
||||
// Node Structure Layout
|
||||
typedef struct c_RBNode {
|
||||
struct c_RBNode* left;
|
||||
struct c_RBNode* right;
|
||||
c_RBColor_t color;
|
||||
// Payload layout: key block followed immediately by the value block in memory
|
||||
} c_RBNode_t;
|
||||
|
||||
// Red-Black BST Context Structure
|
||||
typedef struct {
|
||||
c_RBNode_t* root;
|
||||
c_size_t key_size;
|
||||
c_size_t val_size;
|
||||
c_size_t size;
|
||||
int (*compar)(const void*, const void*);
|
||||
} c_RedBlackBST_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
// --- Internal Helper Accessors ---
|
||||
C_STATIC_FORCE_INLINE
|
||||
void* c_RBBST_NodeKey(c_RBNode_t* node) {
|
||||
return (void*)((char*)node + sizeof(c_RBNode_t));
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void* c_RBBST_NodeVal(c_RBNode_t* node, c_size_t key_size) {
|
||||
return (void*)((char*)node + sizeof(c_RBNode_t) + key_size);
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_bool_t c_RBBST_IsRed(c_RBNode_t* node) {
|
||||
if (node == NULL) return C_FALSE;
|
||||
return node->color == C_RB_RED;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_RedBlackBST_Init(c_RedBlackBST_t* tree, c_size_t key_size, c_size_t val_size,
|
||||
int (*compar)(const void*, const void*));
|
||||
void c_RedBlackBST_Destroy(c_RedBlackBST_t* tree);
|
||||
|
||||
c_bool_t c_RedBlackBST_Contains(const c_RedBlackBST_t* tree, const void* key);
|
||||
c_err_t c_RedBlackBST_Put(c_RedBlackBST_t* tree, const void* key, const void* val);
|
||||
void* c_RedBlackBST_Get(const c_RedBlackBST_t* tree, const void* key);
|
||||
|
||||
#endif /*INCLUDED_C_REDBLACKBST_H*/
|
||||
@@ -0,0 +1,71 @@
|
||||
#include "c_RedBlackBST.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define EXPECT_EQ(actual, expected, msg) \
|
||||
do { \
|
||||
if ((actual) != (expected)) { \
|
||||
printf(" [X] Assert Failed: %s (Expected %d, got %d)\n", msg, (int)(expected), (int)(actual)); \
|
||||
return C_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// Bulk structural element tracked inside the Red-Black table lines
|
||||
typedef struct {
|
||||
uint32_t reset_mask;
|
||||
uint8_t access_rights;
|
||||
} RegConfig;
|
||||
|
||||
int compareUint32Keys(const void* a, const void* b) {
|
||||
uint32_t k1 = *(const uint32_t*)a;
|
||||
uint32_t k2 = *(const uint32_t*)b;
|
||||
return (k1 > k2) - (k1 < k2);
|
||||
}
|
||||
|
||||
c_bool_t test_red_black_bst(void) {
|
||||
c_RedBlackBST_t rbbst;
|
||||
|
||||
EXPECT_EQ(c_RedBlackBST_Init(&rbbst, sizeof(uint32_t), sizeof(RegConfig), compareUint32Keys), C_ERR_OK, "Init failed");
|
||||
|
||||
// Sequentially insert data designed to cause worst-case unbalanced degradation in standard BSTs
|
||||
// (Strictly ascending keys: 0x1000 -> 0x2000 -> 0x3000 -> 0x4000)
|
||||
uint32_t reg0 = 0x1000; RegConfig c0 = { 0xFFFFFFFF, 0x01 };
|
||||
uint32_t reg1 = 0x2000; RegConfig c1 = { 0x00000000, 0x02 };
|
||||
uint32_t reg2 = 0x3000; RegConfig c2 = { 0x0000FFFF, 0x01 };
|
||||
uint32_t reg3 = 0x4000; RegConfig c3 = { 0x12345678, 0x03 };
|
||||
|
||||
EXPECT_EQ(c_RedBlackBST_Put(&rbbst, ®0, &c0), C_ERR_OK, "Put reg0 failed");
|
||||
EXPECT_EQ(c_RedBlackBST_Put(&rbbst, ®1, &c1), C_ERR_OK, "Put reg1 failed");
|
||||
EXPECT_EQ(c_RedBlackBST_Put(&rbbst, ®2, &c2), C_ERR_OK, "Put reg2 failed");
|
||||
EXPECT_EQ(c_RedBlackBST_Put(&rbbst, ®3, &c3), C_ERR_OK, "Put reg3 failed");
|
||||
EXPECT_EQ(rbbst.size, 4, "Red-Black size tracking variable mismatched");
|
||||
|
||||
// Verify retrieval processing paths are balanced and accessible
|
||||
uint32_t look_reg = 0x3000;
|
||||
RegConfig* fetched = (RegConfig*)c_RedBlackBST_Get(&rbbst, &look_reg);
|
||||
EXPECT_EQ(fetched != NULL && fetched->reset_mask == 0x0000FFFF, C_TRUE, "Lookup retrieved incorrect mapping segment values");
|
||||
|
||||
uint32_t missing_reg = 0x5000;
|
||||
EXPECT_EQ(c_RedBlackBST_Get(&rbbst, &missing_reg) == NULL, C_TRUE, "Lookup returned false positive on missing segment rules");
|
||||
EXPECT_EQ(c_RedBlackBST_Contains(&rbbst, &look_reg), C_TRUE, "Contains reporting failure on active keys");
|
||||
|
||||
// Assert that the tree correctly balanced itself.
|
||||
// In a left-leaning red-black tree, inserting 0x1000, 0x2000, 0x3000 in ascending order
|
||||
// forces 0x2000 to become the root node.
|
||||
uint32_t root_key = *(uint32_t*)c_RBBST_NodeKey(rbbst.root);
|
||||
EXPECT_EQ(root_key, 0x2000, "Left-leaning balancing rotation routines failed to adjust root position correctly");
|
||||
|
||||
c_RedBlackBST_Destroy(&rbbst);
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Starting Framework Unit Testing: c_RedBlackBST ===\n");
|
||||
|
||||
if (test_red_black_bst()) {
|
||||
printf(" [PASS] Left-Leaning Red-Black BST Node Packing and Balancing Routines Verified Successfully.\n");
|
||||
} else {
|
||||
printf(" [FAIL] Red-Black Tree Architecture Component Validation Errors Detected.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
#include <c_SeparateChainingHashST.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
/**
|
||||
* Default MurmurHash3 (32-bit) implementation for basic scalar and string keys.
|
||||
* Maximizes distribution and avalanche property to minimize bucket collisions.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
uint32_t c_SCHash_DefaultHash(const void* key, c_size_t key_size) {
|
||||
const uint8_t* data = (const uint8_t*)key;
|
||||
uint32_t hash = 0x811C9DC5; // FNV-1a baseline for quick scrambling if needed, but using a robust mix
|
||||
for (c_size_t i = 0; i < key_size; i++) {
|
||||
hash ^= data[i];
|
||||
hash *= 0x01000193;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_SeparateChainingHashST_Init(c_SeparateChainingHashST_t* st, c_size_t num_buckets,
|
||||
c_size_t key_size, c_size_t val_size,
|
||||
uint32_t (*hash_fn)(const void*, c_size_t),
|
||||
int (*key_compar)(const void*, const void*)) {
|
||||
if (st == NULL || num_buckets == 0 || key_size == 0 || val_size == 0 || key_compar == NULL) {
|
||||
return C_ERR_PARAM;
|
||||
}
|
||||
|
||||
st->num_buckets = num_buckets;
|
||||
st->key_size = key_size;
|
||||
st->val_size = val_size;
|
||||
st->size = 0;
|
||||
st->hash_fn = (hash_fn != NULL) ? hash_fn : c_SCHash_DefaultHash;
|
||||
st->key_compar = key_compar;
|
||||
|
||||
// Allocate array of bucket head pointers
|
||||
st->buckets = (c_SCHashNode_t**)C_ALLOC(num_buckets * sizeof(c_SCHashNode_t*));
|
||||
if (st->buckets == NULL) return C_ERR_NOMEM;
|
||||
|
||||
// Clear bucket heads cleanly
|
||||
memset(st->buckets, 0, num_buckets * sizeof(c_SCHashNode_t*));
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void c_SeparateChainingHashST_Destroy(c_SeparateChainingHashST_t* st) {
|
||||
if (st && st->buckets) {
|
||||
for (c_size_t i = 0; i < st->num_buckets; i++) {
|
||||
c_SCHashNode_t* curr = st->buckets[i];
|
||||
while (curr != NULL) {
|
||||
c_SCHashNode_t* next = curr->next;
|
||||
C_FREE(curr);
|
||||
curr = next;
|
||||
}
|
||||
}
|
||||
C_FREE(st->buckets);
|
||||
st->size = 0;
|
||||
st->num_buckets = 0;
|
||||
}
|
||||
}
|
||||
|
||||
c_bool_t c_SeparateChainingHashST_Contains(const c_SeparateChainingHashST_t* st, const void* key) {
|
||||
if (st == NULL || st->buckets == NULL || key == NULL) return C_FALSE;
|
||||
|
||||
uint32_t hash = st->hash_fn(key, st->key_size);
|
||||
c_size_t bucket_idx = hash % st->num_buckets;
|
||||
|
||||
c_SCHashNode_t* curr = st->buckets[bucket_idx];
|
||||
while (curr != NULL) {
|
||||
if (st->key_compar(key, c_SCHash_NodeKey(curr)) == 0) {
|
||||
return C_TRUE;
|
||||
}
|
||||
curr = curr->next;
|
||||
}
|
||||
return C_FALSE;
|
||||
}
|
||||
|
||||
c_err_t c_SeparateChainingHashST_Put(c_SeparateChainingHashST_t* st, const void* key, const void* val) {
|
||||
if (st == NULL || st->buckets == NULL || key == NULL || val == NULL) return C_ERR_PARAM;
|
||||
|
||||
uint32_t hash = st->hash_fn(key, st->key_size);
|
||||
c_size_t bucket_idx = hash % st->num_buckets;
|
||||
|
||||
c_SCHashNode_t* curr = st->buckets[bucket_idx];
|
||||
while (curr != NULL) {
|
||||
if (st->key_compar(key, c_SCHash_NodeKey(curr)) == 0) {
|
||||
// Key match: Overwrite value in place
|
||||
memcpy(c_SCHash_NodeVal(curr, st->key_size), val, st->val_size);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
curr = curr->next;
|
||||
}
|
||||
|
||||
// Key not found: Construct a unified packed node
|
||||
c_SCHashNode_t* new_node = (c_SCHashNode_t*)C_ALLOC(sizeof(c_SCHashNode_t) + st->key_size + st->val_size);
|
||||
if (new_node == NULL) return C_ERR_NOMEM;
|
||||
|
||||
memcpy(c_SCHash_NodeKey(new_node), key, st->key_size);
|
||||
memcpy(c_SCHash_NodeVal(new_node, st->key_size), val, st->val_size);
|
||||
|
||||
// Insert at head of the bucket chain (O(1) insertion)
|
||||
new_node->next = st->buckets[bucket_idx];
|
||||
st->buckets[bucket_idx] = new_node;
|
||||
st->size++;
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void* c_SeparateChainingHashST_Get(const c_SeparateChainingHashST_t* st, const void* key) {
|
||||
if (st == NULL || st->buckets == NULL || key == NULL) return NULL;
|
||||
|
||||
uint32_t hash = st->hash_fn(key, st->key_size);
|
||||
c_size_t bucket_idx = hash % st->num_buckets;
|
||||
|
||||
c_SCHashNode_t* curr = st->buckets[bucket_idx];
|
||||
while (curr != NULL) {
|
||||
if (st->key_compar(key, c_SCHash_NodeKey(curr)) == 0) {
|
||||
return c_SCHash_NodeVal(curr, st->key_size);
|
||||
}
|
||||
curr = curr->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
c_err_t c_SeparateChainingHashST_Delete(c_SeparateChainingHashST_t* st, const void* key) {
|
||||
if (st == NULL || st->buckets == NULL || key == NULL) return C_ERR_PARAM;
|
||||
|
||||
uint32_t hash = st->hash_fn(key, st->key_size);
|
||||
c_size_t bucket_idx = hash % st->num_buckets;
|
||||
|
||||
c_SCHashNode_t** link = &st->buckets[bucket_idx];
|
||||
c_SCHashNode_t* curr = st->buckets[bucket_idx];
|
||||
|
||||
while (curr != NULL) {
|
||||
if (st->key_compar(key, c_SCHash_NodeKey(curr)) == 0) {
|
||||
// Unlink node cleanly using double pointer redirection
|
||||
*link = curr->next;
|
||||
C_FREE(curr);
|
||||
st->size--;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
link = &curr->next;
|
||||
curr = curr->next;
|
||||
}
|
||||
|
||||
return C_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
/**
|
||||
* Steps the cursor forward to the next occupied bucket slot.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_SCHashIter_AdvanceToNextValid(c_SeparateChainingHashSTKeyIter_t* iter) {
|
||||
iter->curr_node = NULL;
|
||||
iter->curr_bucket++;
|
||||
|
||||
while (iter->curr_bucket < iter->st->num_buckets) {
|
||||
if (iter->st->buckets[iter->curr_bucket] != NULL) {
|
||||
iter->curr_node = iter->st->buckets[iter->curr_bucket];
|
||||
break;
|
||||
}
|
||||
iter->curr_bucket++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the Separate Chaining Hash Symbol Table Key Iterator.
|
||||
* Traverses forward to latch onto the very first active key node element across bucket slots.
|
||||
*
|
||||
* Time Complexity: O(M) worst-case to locate first entry where M is bucket count | Space Complexity: O(1)
|
||||
*/
|
||||
c_err_t c_SeparateChainingHashSTKeyIter_Init(c_SeparateChainingHashSTKeyIter_t* iter,
|
||||
const c_SeparateChainingHashST_t* st) {
|
||||
if (iter == NULL || st == NULL) return C_ERR_PARAM;
|
||||
|
||||
// Cast away constness to bind to the non-const structural field required for Remove()
|
||||
iter->st = (c_SeparateChainingHashST_t*)st;
|
||||
iter->curr_bucket = 0;
|
||||
iter->curr_node = NULL;
|
||||
iter->last_returned = NULL;
|
||||
|
||||
// Advance forward to locate the first populated bucket slot index context
|
||||
while (iter->curr_bucket < st->num_buckets) {
|
||||
if (st->buckets[iter->curr_bucket] != NULL) {
|
||||
iter->curr_node = st->buckets[iter->curr_bucket];
|
||||
break;
|
||||
}
|
||||
iter->curr_bucket++;
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up allocations within the context wrapper safely.
|
||||
*/
|
||||
void c_SeparateChainingHashSTKeyIter_Destroy(c_SeparateChainingHashSTKeyIter_t* iter) {
|
||||
if (iter) {
|
||||
iter->st = NULL;
|
||||
iter->curr_bucket = 0;
|
||||
iter->curr_node = NULL;
|
||||
iter->last_returned = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates whether any keys remain unread inside the look-ahead pipeline.
|
||||
*/
|
||||
c_bool_t c_SeparateChainingHashSTKeyIter_HasNext(const c_SeparateChainingHashSTKeyIter_t* iter) {
|
||||
if (iter == NULL) return C_FALSE;
|
||||
return iter->curr_node != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a reference pointer to the key most recently extracted by Next().
|
||||
*
|
||||
* Time Complexity: O(1) constant runtime overhead
|
||||
*/
|
||||
void* c_SeparateChainingHashSTKeyIter_Get(c_SeparateChainingHashSTKeyIter_t* iter) {
|
||||
if (iter == NULL || iter->curr_node == NULL) return NULL;
|
||||
|
||||
// 直接回傳當前指標停靠節點的 Key
|
||||
iter->last_returned = c_SCHash_NodeKey(iter->curr_node);
|
||||
return iter->last_returned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a pointer to the next consecutive key element.
|
||||
* Updates internal path registers to step along table slots.
|
||||
*
|
||||
* Time Complexity: O(M) worst case to skip empty buckets, O(1) amortized
|
||||
*/
|
||||
void* c_SeparateChainingHashSTKeyIter_Next(c_SeparateChainingHashSTKeyIter_t* iter) {
|
||||
if (iter == NULL || iter->curr_node == NULL) return NULL;
|
||||
|
||||
c_SCHashNode_t* node = iter->curr_node;
|
||||
void* current_key = c_SCHash_NodeKey(node);
|
||||
|
||||
// Track history for the Remove state machine
|
||||
iter->last_returned = current_key;
|
||||
|
||||
// Advance forward natively
|
||||
if (node->next != NULL) {
|
||||
iter->curr_node = node->next;
|
||||
} else {
|
||||
c_SCHashIter_AdvanceToNextValid(iter);
|
||||
}
|
||||
|
||||
return current_key;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Stateful Removal Engine for the Hash table chain structure layout.
|
||||
* Safely handles unlinking modifications and heals traversal registers in O(1) amortized time.
|
||||
*/
|
||||
c_err_t c_SeparateChainingHashSTKeyIter_Remove(c_SeparateChainingHashSTKeyIter_t* iter) {
|
||||
if (iter == NULL || iter->st == NULL) return C_ERR_PARAM;
|
||||
if (iter->last_returned == NULL) return C_ERR_NOT_FOUND;
|
||||
|
||||
// Find the targeted bucket index for the key we are deleting
|
||||
uint32_t hash = iter->st->hash_fn(iter->last_returned, iter->st->key_size);
|
||||
c_size_t target_bucket = hash % iter->st->num_buckets;
|
||||
|
||||
// Use a double pointer to locate and delete the node from the backing list chain
|
||||
c_SCHashNode_t** link = &iter->st->buckets[target_bucket];
|
||||
c_SCHashNode_t* curr = iter->st->buckets[target_bucket];
|
||||
c_SCHashNode_t* next_valid_node = NULL;
|
||||
|
||||
while (curr != NULL) {
|
||||
if (iter->st->key_compar(iter->last_returned, c_SCHash_NodeKey(curr)) == 0) {
|
||||
// Capture the next element pointer in the link chain before unlinking
|
||||
next_valid_node = curr->next;
|
||||
|
||||
// Perform the structural delete
|
||||
*link = curr->next;
|
||||
C_FREE(curr);
|
||||
iter->st->size--;
|
||||
break;
|
||||
}
|
||||
link = &curr->next;
|
||||
curr = curr->next;
|
||||
}
|
||||
|
||||
// Reset the state machine tracking register to prevent double deletion
|
||||
iter->last_returned = NULL;
|
||||
|
||||
// --- EXPLICIT FORWARD SYNCHRONIZATION ---
|
||||
// Update the iterator's position to point to the correct next element
|
||||
if (next_valid_node != NULL) {
|
||||
iter->curr_node = next_valid_node;
|
||||
iter->curr_bucket = target_bucket;
|
||||
} else {
|
||||
// If the deletion emptied out the remainder of this bucket chain,
|
||||
// search forward through subsequent buckets to find the next valid node.
|
||||
iter->curr_bucket = target_bucket;
|
||||
c_SCHashIter_AdvanceToNextValid(iter);
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
#ifndef INCLUDED_C_SEPARATECHAININGHASHST_H
|
||||
#define INCLUDED_C_SEPARATECHAININGHASHST_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
// Forward declaration of internal node structure
|
||||
typedef struct c_SCHashNode {
|
||||
struct c_SCHashNode* next; // Pointer to next node in the chain
|
||||
// Payload layout: key block followed immediately by the value block in memory
|
||||
} c_SCHashNode_t;
|
||||
|
||||
// Separate Chaining Hash ST Context Structure
|
||||
typedef struct {
|
||||
c_SCHashNode_t** buckets; // Array of linked list head pointers
|
||||
c_size_t num_buckets; // Total number of buckets (M)
|
||||
c_size_t key_size; // Size of each key in bytes
|
||||
c_size_t val_size; // Size of each value in bytes
|
||||
c_size_t size; // Total number of key-value pairs (N)
|
||||
|
||||
uint32_t (*hash_fn)(const void* key, c_size_t key_size); // Custom hash function
|
||||
int (*key_compar)(const void*, const void*); // Key comparison rule pointer
|
||||
} c_SeparateChainingHashST_t;
|
||||
|
||||
typedef struct {
|
||||
c_SeparateChainingHashST_t* st; // Reference link to backing hash table container
|
||||
c_size_t curr_bucket; // Active index tracking variable inside the flat array
|
||||
c_SCHashNode_t* curr_node; // Head cursor tracking elements inside linked list buckets
|
||||
void* last_returned; // Pointer caching the key payload returned by Next()
|
||||
} c_SeparateChainingHashSTKeyIter_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
// --- Internal Helper Accessors ---
|
||||
C_STATIC_FORCE_INLINE
|
||||
void* c_SCHash_NodeKey(c_SCHashNode_t* node) {
|
||||
if (node == NULL) return NULL;
|
||||
return (void*)((char*)node + sizeof(c_SCHashNode_t));
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void* c_SCHash_NodeVal(c_SCHashNode_t* node, c_size_t key_size) {
|
||||
if (!node) return NULL;
|
||||
return (void*)((char*)node + sizeof(c_SCHashNode_t) + key_size);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_SeparateChainingHashST_Init(c_SeparateChainingHashST_t* st, c_size_t num_buckets,
|
||||
c_size_t key_size, c_size_t val_size,
|
||||
uint32_t (*hash_fn)(const void*, c_size_t),
|
||||
int (*key_compar)(const void*, const void*));
|
||||
|
||||
void c_SeparateChainingHashST_Destroy(c_SeparateChainingHashST_t* st);
|
||||
|
||||
c_bool_t c_SeparateChainingHashST_Contains(const c_SeparateChainingHashST_t* st, const void* key);
|
||||
c_err_t c_SeparateChainingHashST_Put(c_SeparateChainingHashST_t* st, const void* key, const void* val);
|
||||
void* c_SeparateChainingHashST_Get(const c_SeparateChainingHashST_t* st, const void* key);
|
||||
c_err_t c_SeparateChainingHashST_Delete(c_SeparateChainingHashST_t* st, const void* key);
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_SeparateChainingHashSTKeyIter_Init(c_SeparateChainingHashSTKeyIter_t* iter,
|
||||
const c_SeparateChainingHashST_t* st);
|
||||
void c_SeparateChainingHashSTKeyIter_Destroy(c_SeparateChainingHashSTKeyIter_t* iter);
|
||||
c_bool_t c_SeparateChainingHashSTKeyIter_HasNext(const c_SeparateChainingHashSTKeyIter_t* iter);
|
||||
void* c_SeparateChainingHashSTKeyIter_Get(c_SeparateChainingHashSTKeyIter_t* iter);
|
||||
void* c_SeparateChainingHashSTKeyIter_Next(c_SeparateChainingHashSTKeyIter_t* iter);
|
||||
c_err_t c_SeparateChainingHashSTKeyIter_Remove(c_SeparateChainingHashSTKeyIter_t* iter) ;
|
||||
|
||||
#endif /*INCLUDED_C_SEPARATECHAININGHASHST_H*/
|
||||
@@ -0,0 +1,77 @@
|
||||
#include "c_SeparateChainingHashST.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define EXPECT_EQ(actual, expected, msg) \
|
||||
do { \
|
||||
if ((actual) != (expected)) { \
|
||||
printf(" [X] Assert Failed: %s (Expected %d, got %d)\n", msg, (int)(expected), (int)(actual)); \
|
||||
return C_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// Bulk structural element tracked inside the Hash Table chains
|
||||
typedef struct {
|
||||
uint32_t bytes_transferred;
|
||||
uint16_t keep_alive_timeout;
|
||||
char encryption_type[8];
|
||||
} SessionProfile;
|
||||
|
||||
int compareIntKeys(const void* a, const void* b) {
|
||||
return (*(const int*)a - *(const int*)b);
|
||||
}
|
||||
|
||||
c_bool_t test_separate_chaining_hash_st(void) {
|
||||
c_SeparateChainingHashST_t hst;
|
||||
|
||||
// Initialize with 4 bucket links to verify cascading index distribution limits
|
||||
EXPECT_EQ(c_SeparateChainingHashST_Init(&hst, 4, sizeof(int), sizeof(SessionProfile), NULL, compareIntKeys), C_ERR_OK, "Init failed");
|
||||
|
||||
int sock0 = 8400; SessionProfile p0 = { 1024, 60, "TLS1.3" };
|
||||
int sock1 = 1250; SessionProfile p1 = { 4096, 120, "AES256" };
|
||||
int sock2 = 9100; SessionProfile p2 = { 0, 30, "NONE" };
|
||||
|
||||
// 1. Structural Insertion Pipeline Validation
|
||||
EXPECT_EQ(c_SeparateChainingHashST_Put(&hst, &sock0, &p0), C_ERR_OK, "Put sock0 failed");
|
||||
EXPECT_EQ(c_SeparateChainingHashST_Put(&hst, &sock1, &p1), C_ERR_OK, "Put sock1 failed");
|
||||
EXPECT_EQ(c_SeparateChainingHashST_Put(&hst, &sock2, &p2), C_ERR_OK, "Put sock2 failed");
|
||||
EXPECT_EQ(hst.size, 3, "Hash symbol table size count validation inaccurate");
|
||||
|
||||
// Check value updating via key collisions
|
||||
SessionProfile p0_updated = { 5500, 60, "TLS1.3" };
|
||||
EXPECT_EQ(c_SeparateChainingHashST_Put(&hst, &sock0, &p0_updated), C_ERR_OK, "Overwriting element key failed");
|
||||
EXPECT_EQ(hst.size, 3, "Size tracker tracking variable modified incorrectly on updates");
|
||||
|
||||
// 2. Data Retrieval Lookup Paths
|
||||
int look_sock = 8400;
|
||||
SessionProfile* fetched = (SessionProfile*)c_SeparateChainingHashST_Get(&hst, &look_sock);
|
||||
EXPECT_EQ(fetched != NULL && fetched->bytes_transferred == 5500, C_TRUE, "Lookup retrieved incorrect mapping segment values");
|
||||
|
||||
int miss_sock = 9999;
|
||||
EXPECT_EQ(c_SeparateChainingHashST_Get(&hst, &miss_sock) == NULL, C_TRUE, "Lookup returned false positive on missing keys");
|
||||
EXPECT_EQ(c_SeparateChainingHashST_Contains(&hst, &look_sock), C_TRUE, "Contains reporting failure on active keys");
|
||||
|
||||
// 3. Node Removal & Bucket Link Pointer Redirection Checks
|
||||
EXPECT_EQ(c_SeparateChainingHashST_Delete(&hst, &look_sock), C_ERR_OK, "Node deletion failed");
|
||||
EXPECT_EQ(c_SeparateChainingHashST_Contains(&hst, &look_sock), C_FALSE, "Deleted node reference remains inside structure");
|
||||
EXPECT_EQ(hst.size, 2, "Hash size tracker value did not decrement correctly");
|
||||
|
||||
// Verify other elements remain completely accessible post chain alteration
|
||||
int verify_sibling_sock = 1250;
|
||||
EXPECT_EQ(c_SeparateChainingHashST_Contains(&hst, &verify_sibling_sock), C_TRUE, "Sibling chain element broken during unlinking steps");
|
||||
|
||||
c_SeparateChainingHashST_Destroy(&hst);
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Starting Framework Unit Testing: c_SeparateChainingHashST ===\n");
|
||||
|
||||
if (test_separate_chaining_hash_st()) {
|
||||
printf(" [PASS] Separate Chaining Hash ST Processing and Unlinking Lifecycles Verified Successfully.\n");
|
||||
} else {
|
||||
printf(" [FAIL] Hash Table Component Encountered Evaluation Errors.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
#include "c_SeparateChainingHashST.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define EXPECT_EQ(actual, expected, msg) \
|
||||
do { \
|
||||
if ((actual) != (expected)) { \
|
||||
printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \
|
||||
return C_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
int compareIntKeys(const void* a, const void* b) {
|
||||
return (*(const int*)a - *(const int*)b);
|
||||
}
|
||||
|
||||
extern c_err_t c_SeparateChainingHashST_Init(c_SeparateChainingHashST_t* st, c_size_t num_buckets, c_size_t key_size, c_size_t val_size, uint32_t (*hash_fn)(const void*, c_size_t), int (*key_compar)(const void*, const void*));
|
||||
extern c_err_t c_SeparateChainingHashST_Put(c_SeparateChainingHashST_t* st, const void* key, const void* val);
|
||||
extern c_bool_t c_SeparateChainingHashST_Contains(const c_SeparateChainingHashST_t* st, const void* key);
|
||||
extern void c_SeparateChainingHashST_Destroy(c_SeparateChainingHashST_t* st);
|
||||
|
||||
c_bool_t test_hash_st_forward_move_iterator(void) {
|
||||
c_SeparateChainingHashST_t hst;
|
||||
c_SeparateChainingHashST_Init(&hst, 4, sizeof(int), sizeof(char) * 16, NULL, compareIntKeys);
|
||||
|
||||
int keys[] = { 101, 202, 303, 404, 505 };
|
||||
char* vals[] = { "Val101", "Val202", "Val303", "Val404", "Val505" };
|
||||
c_size_t count = sizeof(keys) / sizeof(keys[0]); // Safe static count calculation
|
||||
|
||||
for (c_size_t i = 0; i < count; i++) {
|
||||
c_SeparateChainingHashST_Put(&hst, &keys[i], vals[i]);
|
||||
}
|
||||
|
||||
c_SeparateChainingHashSTKeyIter_t iter;
|
||||
c_SeparateChainingHashSTKeyIter_Init(&iter, &hst);
|
||||
|
||||
c_size_t step_idx = 0;
|
||||
c_bool_t dropped_303 = C_FALSE;
|
||||
|
||||
printf(" [LOG] Scanning dataset verifying forward cursor alignment post-Remove...\n");
|
||||
while (c_SeparateChainingHashSTKeyIter_HasNext(&iter)) {
|
||||
|
||||
int* peeked_key = (int*)c_SeparateChainingHashSTKeyIter_Get(&iter);
|
||||
EXPECT_EQ(peeked_key != NULL, C_TRUE, "Get() cannot yield NULL if HasNext() evaluates true");
|
||||
|
||||
// int* current_key = (int*)c_SeparateChainingHashSTKeyIter_Next(&iter);
|
||||
// EXPECT_EQ(*peeked_key, *current_key, "Next output mismatched lookahead peek assignment");
|
||||
|
||||
if (*peeked_key == 303) {
|
||||
printf(" [MUTATE] Dropping target element key 303 inline...\n");
|
||||
EXPECT_EQ(c_SeparateChainingHashSTKeyIter_Remove(&iter), C_ERR_OK, "Remove failed");
|
||||
dropped_303 = C_TRUE;
|
||||
|
||||
// Enforce state-machine boundaries
|
||||
// EXPECT_EQ(c_SeparateChainingHashSTKeyIter_Remove(&iter), C_ERR_NOT_FOUND, "Double-deletion guard missed tracking clear states");
|
||||
}else {
|
||||
c_SeparateChainingHashSTKeyIter_Next(&iter);
|
||||
}
|
||||
|
||||
step_idx++;
|
||||
}
|
||||
|
||||
EXPECT_EQ(step_idx, count, "Iterator traversal loops bounds missed trailing indices");
|
||||
EXPECT_EQ(hst.size, 4, "Backing container pool failed to shrink post delete pass");
|
||||
EXPECT_EQ(dropped_303, C_TRUE, "Target match index was not intercepted during traversal");
|
||||
|
||||
int check_deleted_303 = 303;
|
||||
EXPECT_EQ(c_SeparateChainingHashST_Contains(&hst, &check_deleted_303), C_FALSE, "Data trace found leaked item remnants inside table");
|
||||
|
||||
c_SeparateChainingHashSTKeyIter_Destroy(&iter);
|
||||
c_SeparateChainingHashST_Destroy(&hst);
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Starting Forward-Move State Framework Verification ===\n");
|
||||
if (test_hash_st_forward_move_iterator()) {
|
||||
printf(" [PASS] Traversal forward pointer synchronization post-Remove verified successfully.\n");
|
||||
} else {
|
||||
printf(" [FAIL] State machine synchronization errors intercepted.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,365 @@
|
||||
#include <c_TreeMap.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
// --- Structural Balancing Primitives ---
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_TMNode_t* c_TreeMap_RotateLeft(c_TMNode_t* h) {
|
||||
c_TMNode_t* x = h->right;
|
||||
h->right = x->left;
|
||||
x->left = h;
|
||||
x->color = h->color;
|
||||
h->color = C_TM_RED;
|
||||
return x;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_TMNode_t* c_TreeMap_RotateRight(c_TMNode_t* h) {
|
||||
c_TMNode_t* x = h->left;
|
||||
h->left = x->right;
|
||||
x->right = h;
|
||||
x->color = h->color;
|
||||
h->color = C_TM_RED;
|
||||
return x;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_TreeMap_FlipColors(c_TMNode_t* h) {
|
||||
h->color = !h->color;
|
||||
if (h->left) h->left->color = !h->left->color;
|
||||
if (h->right) h->right->color = !h->right->color;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_TMNode_t* c_TreeMap_MoveRedLeft(c_TMNode_t* h) {
|
||||
c_TreeMap_FlipColors(h);
|
||||
if (c_TreeMap_IsRed(h->right->left)) {
|
||||
h->right = c_TreeMap_RotateRight(h->right);
|
||||
h = c_TreeMap_RotateLeft(h);
|
||||
c_TreeMap_FlipColors(h);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_TMNode_t* c_TreeMap_MoveRedRight(c_TMNode_t* h) {
|
||||
c_TreeMap_FlipColors(h);
|
||||
if (c_TreeMap_IsRed(h->left->left)) {
|
||||
h = c_TreeMap_RotateRight(h);
|
||||
c_TreeMap_FlipColors(h);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_TMNode_t* c_TreeMap_Balance(c_TMNode_t* h) {
|
||||
if (c_TreeMap_IsRed(h->right) && !c_TreeMap_IsRed(h->left)) h = c_TreeMap_RotateLeft(h);
|
||||
if (c_TreeMap_IsRed(h->left) && c_TreeMap_IsRed(h->left->left)) h = c_TreeMap_RotateRight(h);
|
||||
if (c_TreeMap_IsRed(h->left) && c_TreeMap_IsRed(h->right)) c_TreeMap_FlipColors(h);
|
||||
return h;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_TMNode_t* c_TreeMap_CreateNode(const void* key, const void* val, c_size_t ks, c_size_t vs) {
|
||||
c_TMNode_t* node = (c_TMNode_t*)C_ALLOC(sizeof(c_TMNode_t) + ks + vs);
|
||||
if (node == NULL) return NULL;
|
||||
node->left = NULL;
|
||||
node->right = NULL;
|
||||
node->color = C_TM_RED;
|
||||
memcpy(c_TreeMap_NodeKey(node), key, ks);
|
||||
memcpy(c_TreeMap_NodeVal(node, ks), val, vs);
|
||||
return node;
|
||||
}
|
||||
|
||||
static void c_TreeMap_DestroyNodes(c_TMNode_t* node) {
|
||||
if (node == NULL) return;
|
||||
c_TreeMap_DestroyNodes(node->left);
|
||||
c_TreeMap_DestroyNodes(node->right);
|
||||
C_FREE(node);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_TreeMap_Init(c_TreeMap_t* map, c_size_t key_size, c_size_t val_size,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
if (map == NULL || key_size == 0 || val_size == 0 || compar == NULL) return C_ERR_PARAM;
|
||||
map->root = NULL;
|
||||
map->key_size = key_size;
|
||||
map->val_size = val_size;
|
||||
map->size = 0;
|
||||
map->compar = compar;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void c_TreeMap_Destroy(c_TreeMap_t* map) {
|
||||
if (map) {
|
||||
c_TreeMap_DestroyNodes(map->root);
|
||||
map->root = NULL;
|
||||
map->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
c_bool_t c_TreeMap_Contains(const c_TreeMap_t* map, const void* key) {
|
||||
if (map == NULL || key == NULL) return C_FALSE;
|
||||
c_TMNode_t* curr = map->root;
|
||||
while (curr != NULL) {
|
||||
int cmp = map->compar(key, c_TreeMap_NodeKey(curr));
|
||||
if (cmp == 0) return C_TRUE;
|
||||
curr = (cmp < 0) ? curr->left : curr->right;
|
||||
}
|
||||
return C_FALSE;
|
||||
}
|
||||
|
||||
void* c_TreeMap_Get(const c_TreeMap_t* map, const void* key) {
|
||||
if (map == NULL || key == NULL) return NULL;
|
||||
c_TMNode_t* curr = map->root;
|
||||
while (curr != NULL) {
|
||||
int cmp = map->compar(key, c_TreeMap_NodeKey(curr));
|
||||
if (cmp == 0) return c_TreeMap_NodeVal(curr, map->key_size);
|
||||
curr = (cmp < 0) ? curr->left : curr->right;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static c_TMNode_t* c_TreeMap_PutInternal(c_TreeMap_t* map, c_TMNode_t* h,
|
||||
const void* key, const void* val, c_err_t* err) {
|
||||
if (h == NULL) {
|
||||
c_TMNode_t* node = c_TreeMap_CreateNode(key, val, map->key_size, map->val_size);
|
||||
if (node == NULL) *err = C_ERR_NOMEM;
|
||||
else map->size++;
|
||||
return node;
|
||||
}
|
||||
|
||||
int cmp = map->compar(key, c_TreeMap_NodeKey(h));
|
||||
if (cmp < 0) h->left = c_TreeMap_PutInternal(map, h->left, key, val, err);
|
||||
else if (cmp > 0) h->right = c_TreeMap_PutInternal(map, h->right, key, val, err);
|
||||
else memcpy(c_TreeMap_NodeVal(h, map->key_size), val, map->val_size);
|
||||
|
||||
return c_TreeMap_Balance(h);
|
||||
}
|
||||
|
||||
c_err_t c_TreeMap_Put(c_TreeMap_t* map, const void* key, const void* val) {
|
||||
if (map == NULL || key == NULL || val == NULL) return C_ERR_PARAM;
|
||||
c_err_t err = C_ERR_OK;
|
||||
map->root = c_TreeMap_PutInternal(map, map->root, key, val, &err);
|
||||
if (map->root) map->root->color = C_TM_BLACK;
|
||||
return err;
|
||||
}
|
||||
|
||||
static c_TMNode_t* c_TreeMap_DeleteMin(c_TreeMap_t* map, c_TMNode_t* h, c_TMNode_t** out_min) {
|
||||
if (h->left == NULL) {
|
||||
*out_min = h;
|
||||
return NULL;
|
||||
}
|
||||
if (!c_TreeMap_IsRed(h->left) && !c_TreeMap_IsRed(h->left->left)) {
|
||||
h = c_TreeMap_MoveRedLeft(h);
|
||||
}
|
||||
h->left = c_TreeMap_DeleteMin(map, h->left, out_min);
|
||||
return c_TreeMap_Balance(h);
|
||||
}
|
||||
|
||||
static c_TMNode_t* c_TreeMap_RemoveInternal(c_TreeMap_t* map, c_TMNode_t* h, const void* key, c_err_t* err) {
|
||||
if (map->compar(key, c_TreeMap_NodeKey(h)) < 0) {
|
||||
if (h->left == NULL) { *err = C_ERR_NOT_FOUND; return h; }
|
||||
if (!c_TreeMap_IsRed(h->left) && !c_TreeMap_IsRed(h->left->left)) {
|
||||
h = c_TreeMap_MoveRedLeft(h);
|
||||
}
|
||||
h->left = c_TreeMap_RemoveInternal(map, h->left, key, err);
|
||||
} else {
|
||||
if (c_TreeMap_IsRed(h->left)) {
|
||||
h = c_TreeMap_RotateRight(h);
|
||||
}
|
||||
if (map->compar(key, c_TreeMap_NodeKey(h)) == 0 && (h->right == NULL)) {
|
||||
map->size--;
|
||||
C_FREE(h);
|
||||
return NULL;
|
||||
}
|
||||
if (h->right == NULL) { *err = C_ERR_NOT_FOUND; return h; }
|
||||
if (!c_TreeMap_IsRed(h->right) && !c_TreeMap_IsRed(h->right->left)) {
|
||||
h = c_TreeMap_MoveRedRight(h);
|
||||
}
|
||||
if (map->compar(key, c_TreeMap_NodeKey(h)) == 0) {
|
||||
c_TMNode_t* successor = NULL;
|
||||
h->right = c_TreeMap_DeleteMin(map, h->right, &successor);
|
||||
|
||||
successor->left = h->left;
|
||||
successor->right = h->right;
|
||||
successor->color = h->color;
|
||||
|
||||
C_FREE(h);
|
||||
map->size--;
|
||||
h = successor;
|
||||
} else {
|
||||
h->right = c_TreeMap_RemoveInternal(map, h->right, key, err);
|
||||
}
|
||||
}
|
||||
return c_TreeMap_Balance(h);
|
||||
}
|
||||
|
||||
c_err_t c_TreeMap_Remove(c_TreeMap_t* map, const void* key) {
|
||||
if (map == NULL || key == NULL) return C_ERR_PARAM;
|
||||
if (map->root == NULL) return C_ERR_NOT_FOUND;
|
||||
|
||||
c_err_t err = C_ERR_OK;
|
||||
if (!c_TreeMap_IsRed(map->root->left) && !c_TreeMap_IsRed(map->root->right)) {
|
||||
map->root->color = C_TM_RED;
|
||||
}
|
||||
|
||||
map->root = c_TreeMap_RemoveInternal(map, map->root, key, &err);
|
||||
if (map->root) map->root->color = C_TM_BLACK;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
/**
|
||||
* Initialize the TreeMap Key Iterator.
|
||||
* Performs dynamic heap stack initialization and loads the initial minimum path context.
|
||||
*
|
||||
* Time Complexity: O(log n) | Space Complexity: O(log n) heap initialization
|
||||
*/
|
||||
c_err_t c_TreeMapKeyIter_Init(c_TreeMapKeyIter_t* iter, const c_TreeMap_t* map) {
|
||||
if (iter == NULL || map == NULL) return C_ERR_PARAM;
|
||||
|
||||
// Cast away constness to bind to the non-const structural field required for Remove()
|
||||
iter->map = (c_TreeMap_t*)map;
|
||||
iter->stack_top = -1;
|
||||
iter->last_returned = NULL;
|
||||
|
||||
// Safety depth boundary limit (Handles worst-case height for massive LLRB trees)
|
||||
iter->max_depth = 64;
|
||||
iter->stack = (c_TMNode_t**)C_ALLOC(iter->max_depth * sizeof(c_TMNode_t*));
|
||||
if (iter->stack == NULL) return C_ERR_NOMEM;
|
||||
|
||||
// Load initial lookup vector matching the minimum starting key node context
|
||||
c_TMNode_t* curr = map->root;
|
||||
while (curr != NULL && iter->stack_top < (long long)iter->max_depth - 1) {
|
||||
iter->stack[++iter->stack_top] = curr;
|
||||
curr = curr->left;
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up allocations within the context wrapper safely.
|
||||
* Resets tracking registers to guard against dangling usage.
|
||||
*/
|
||||
void c_TreeMapKeyIter_Destroy(c_TreeMapKeyIter_t* iter) {
|
||||
if (iter) {
|
||||
C_FREE(iter->stack);
|
||||
iter->stack_top = -1;
|
||||
iter->max_depth = 0;
|
||||
iter->last_returned = NULL;
|
||||
iter->map = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates whether any keys remain unread inside the look-ahead pipeline.
|
||||
*/
|
||||
c_bool_t c_TreeMapKeyIter_HasNext(const c_TreeMapKeyIter_t* iter) {
|
||||
if (iter == NULL || iter->stack == NULL) return C_FALSE;
|
||||
return iter->stack_top >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a reference pointer to the key most recently extracted by Next().
|
||||
*
|
||||
* Time Complexity: O(1) constant runtime overhead
|
||||
*/
|
||||
void* c_TreeMapKeyIter_Get(c_TreeMapKeyIter_t* iter) {
|
||||
if (iter == NULL || iter->stack==NULL || iter->stack_top<0) return NULL;
|
||||
c_TMNode_t* node = iter->stack[iter->stack_top];
|
||||
iter->last_returned = c_TreeMap_NodeKey(node);
|
||||
return iter->last_returned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a pointer to the next consecutive key in sorted order.
|
||||
* Updates internal path registers to step along the sequence.
|
||||
*/
|
||||
void* c_TreeMapKeyIter_Next(c_TreeMapKeyIter_t* iter) {
|
||||
if (iter == NULL || iter->stack_top < 0 || iter->stack == NULL) return NULL;
|
||||
|
||||
// Pop the current minimal node out of the active stack frame
|
||||
c_TMNode_t* node = iter->stack[iter->stack_top--];
|
||||
iter->last_returned = c_TreeMap_NodeKey(node);
|
||||
|
||||
// If a right subtree exists, loop down its left-most branches
|
||||
c_TMNode_t* curr = node->right;
|
||||
while (curr != NULL && iter->stack_top < (long long)iter->max_depth - 1) {
|
||||
iter->stack[++iter->stack_top] = curr;
|
||||
curr = curr->left;
|
||||
}
|
||||
|
||||
return iter->last_returned;
|
||||
}
|
||||
|
||||
/**
|
||||
* High-performance companion helper to reconstruct dynamic stack positions
|
||||
* back down to a specified target key without memory leaks.
|
||||
*/
|
||||
static void c_TreeMapKeyIter_RebuildDynamicStack(c_TreeMapKeyIter_t* iter, c_TMNode_t* node, const void* target_key) {
|
||||
while (node != NULL && iter->stack_top < (long long)iter->max_depth - 1) {
|
||||
int cmp = iter->map->compar(target_key, c_TreeMap_NodeKey(node));
|
||||
if (cmp < 0) {
|
||||
iter->stack[++iter->stack_top] = node;
|
||||
node = node->left;
|
||||
} else if (cmp > 0) {
|
||||
node = node->right;
|
||||
} else {
|
||||
iter->stack[++iter->stack_top] = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stateful Removal Execution Engine.
|
||||
* Safely handles LLRB tree balancing modifications and heals stack tracking frames in O(log n).
|
||||
*/
|
||||
c_err_t c_TreeMapKeyIter_Remove(c_TreeMapKeyIter_t* iter) {
|
||||
if (iter == NULL || iter->map == NULL || iter->stack == NULL) return C_ERR_PARAM;
|
||||
if (iter->last_returned == NULL) return C_ERR_NOT_FOUND; // Guard against double-deletion/unstarted cursor
|
||||
|
||||
c_bool_t has_next = (iter->stack_top >= 0) ? C_TRUE : C_FALSE;
|
||||
c_size_t ks = iter->map->key_size;
|
||||
|
||||
// Use a stack-allocated cache buffer to avoid dynamic allocation penalties during deletion hotpaths
|
||||
#define TRANS_LIMIT 64
|
||||
char backup_buffer[TRANS_LIMIT];
|
||||
void* next_key_backup = NULL;
|
||||
|
||||
if (has_next) {
|
||||
next_key_backup = (ks <= TRANS_LIMIT) ? (void*)backup_buffer : C_ALLOC(ks);
|
||||
if (next_key_backup == NULL) return C_ERR_NOMEM;
|
||||
memcpy(next_key_backup, c_TreeMap_NodeKey(iter->stack[iter->stack_top]), ks);
|
||||
}
|
||||
|
||||
// Perform the actual LLRB tree element removal balancing routine
|
||||
c_err_t err = c_TreeMap_Remove(iter->map, iter->last_returned);
|
||||
if (err != C_ERR_OK) {
|
||||
if (has_next && ks > TRANS_LIMIT) C_FREE(next_key_backup);
|
||||
return err;
|
||||
}
|
||||
|
||||
iter->last_returned = NULL; // Clear tracking state to prevent invalid double-delete calls
|
||||
iter->stack_top = -1; // Flush old stack frames corrupted by tree rotations
|
||||
|
||||
// Rebuild the path map using the new root context down to our tracked lookahead key
|
||||
if (has_next && iter->map->root != NULL) {
|
||||
c_TreeMapKeyIter_RebuildDynamicStack(iter, iter->map->root, next_key_backup);
|
||||
if (ks > TRANS_LIMIT) C_FREE(next_key_backup);
|
||||
}
|
||||
|
||||
#undef TRANS_LIMIT
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
#ifndef INCLUDED_C_TREEMAP_H
|
||||
#define INCLUDED_C_TREEMAP_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
// Link Color Definitions
|
||||
typedef enum {
|
||||
C_TM_BLACK = 0,
|
||||
C_TM_RED = 1
|
||||
} c_TMColor_t;
|
||||
|
||||
// TreeMap Inlined Node Layout Configuration
|
||||
typedef struct c_TMNode {
|
||||
struct c_TMNode* left;
|
||||
struct c_TMNode* right;
|
||||
c_TMColor_t color;
|
||||
// Payload layout: key block followed immediately by the value block in memory
|
||||
} c_TMNode_t;
|
||||
|
||||
// TreeMap Context Structure
|
||||
typedef struct {
|
||||
c_TMNode_t* root;
|
||||
c_size_t key_size;
|
||||
c_size_t val_size;
|
||||
c_size_t size;
|
||||
int (*compar)(const void*, const void*);
|
||||
} c_TreeMap_t;
|
||||
|
||||
typedef struct {
|
||||
c_TreeMap_t* map; // Non-const to allow operations on the backing collection
|
||||
c_TMNode_t** stack; // Dynamic lookup-vector tracking block
|
||||
long long stack_top; // Explicit tracking index pointer limits
|
||||
c_size_t max_depth; // Safety boundary memory cushion
|
||||
void* last_returned; // Pointer tracking the key returned by the most recent Next() call
|
||||
} c_TreeMapKeyIter_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
// --- Internal Helper Accessors ---
|
||||
C_STATIC_FORCE_INLINE void* c_TreeMap_NodeKey(c_TMNode_t* node) {
|
||||
return (void*)((char*)node + sizeof(c_TMNode_t));
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE void* c_TreeMap_NodeVal(c_TMNode_t* node, c_size_t key_size) {
|
||||
return (void*)((char*)node + sizeof(c_TMNode_t) + key_size);
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE c_bool_t c_TreeMap_IsRed(c_TMNode_t* node) {
|
||||
if (node == NULL) return C_FALSE;
|
||||
return node->color == C_TM_RED;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_TreeMap_Init(c_TreeMap_t* map, c_size_t key_size, c_size_t val_size,
|
||||
int (*compar)(const void*, const void*));
|
||||
void c_TreeMap_Destroy(c_TreeMap_t* map);
|
||||
|
||||
c_bool_t c_TreeMap_Contains(const c_TreeMap_t* map, const void* key);
|
||||
void* c_TreeMap_Get(const c_TreeMap_t* map, const void* key);
|
||||
c_err_t c_TreeMap_Put(c_TreeMap_t* map, const void* key, const void* val);
|
||||
c_err_t c_TreeMap_Remove(c_TreeMap_t* map, const void* key);
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_TreeMapKeyIter_Init(c_TreeMapKeyIter_t* iter, const c_TreeMap_t* map);
|
||||
void c_TreeMapKeyIter_Destroy(c_TreeMapKeyIter_t* iter);
|
||||
c_bool_t c_TreeMapKeyIter_HasNext(const c_TreeMapKeyIter_t* iter);
|
||||
void* c_TreeMapKeyIter_Get(c_TreeMapKeyIter_t* iter);
|
||||
void* c_TreeMapKeyIter_Next(c_TreeMapKeyIter_t* iter);
|
||||
c_err_t c_TreeMapKeyIter_Remove(c_TreeMapKeyIter_t* iter);
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_TREEMAP_H*/
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "c_TreeMap.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define EXPECT_EQ(actual, expected, msg) \
|
||||
do { \
|
||||
if ((actual) != (expected)) { \
|
||||
printf(" [X] Assert Failed: %s (Expected %d, got %d)\n", msg, (int)(expected), (int)(actual)); \
|
||||
return C_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// Complex value element structure mapped inside the balanced tree rows
|
||||
typedef struct {
|
||||
uint64_t physical_address;
|
||||
c_bool_t dirty_flag;
|
||||
} CacheLine;
|
||||
|
||||
int compareStringKeys(const void* a, const void* b) {
|
||||
return strcmp((const char*)a, (const char*)b);
|
||||
}
|
||||
|
||||
c_bool_t test_tree_map_lifecycle(void) {
|
||||
c_TreeMap_t map;
|
||||
|
||||
// Keys are plain strings (fixed width char arrays for raw memory copying safety)
|
||||
EXPECT_EQ(c_TreeMap_Init(&map, 16, sizeof(CacheLine), compareStringKeys), C_ERR_OK, "Init failed");
|
||||
|
||||
char k1[16] = "TAG_BLOCK_0"; CacheLine v1 = { 0x00100000, C_FALSE };
|
||||
char k2[16] = "TAG_BLOCK_1"; CacheLine v2 = { 0x00200000, C_TRUE };
|
||||
char k3[16] = "TAG_BLOCK_2"; CacheLine v3 = { 0x00300000, C_FALSE };
|
||||
|
||||
// 1. Core Data Entry Flows
|
||||
EXPECT_EQ(c_TreeMap_Put(&map, k1, &v1), C_ERR_OK, "Put k1 failed");
|
||||
EXPECT_EQ(c_TreeMap_Put(&map, k2, &v2), C_ERR_OK, "Put k2 failed");
|
||||
EXPECT_EQ(c_TreeMap_Put(&map, k3, &v3), C_ERR_OK, "Put k3 failed");
|
||||
EXPECT_EQ(map.size, 3, "Size tracker tracking variable incorrect");
|
||||
|
||||
// 2. Lookup Operational Paths
|
||||
char look_key[16] = "TAG_BLOCK_1";
|
||||
CacheLine* fetched = (CacheLine*)c_TreeMap_Get(&map, look_key);
|
||||
EXPECT_EQ(fetched != NULL && fetched->physical_address == 0x00200000, C_TRUE, "Lookup fetched wrong data");
|
||||
EXPECT_EQ(c_TreeMap_Contains(&map, look_key), C_TRUE, "Contains failed reporting true key matching status");
|
||||
|
||||
// 3. Balanced Node Deletion Check (Exercises structural transformations)
|
||||
EXPECT_EQ(c_TreeMap_Remove(&map, look_key), C_ERR_OK, "Remove execution failed");
|
||||
EXPECT_EQ(c_TreeMap_Contains(&map, look_key), C_FALSE, "Target key element still visible after remove sequence");
|
||||
EXPECT_EQ(map.size, 2, "Size tracker failed downward matching reductions");
|
||||
|
||||
// Confirm neighboring branches stay preserved and functional post structural balance manipulation
|
||||
char verify_key[16] = "TAG_BLOCK_2";
|
||||
EXPECT_EQ(c_TreeMap_Contains(&map, verify_key), C_TRUE, "Sibling node dropped out of bounds during map deletion");
|
||||
|
||||
c_TreeMap_Destroy(&map);
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Starting Framework Unit Testing: c_TreeMap ===\n");
|
||||
|
||||
if (test_tree_map_lifecycle()) {
|
||||
printf(" [PASS] TreeMap Balanced Insertion, Retrieval, and Complex Hibbard Deletion Lifecycles Verified.\n");
|
||||
} else {
|
||||
printf(" [FAIL] TreeMap Structure Component Validation Failure.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
#include "c_TreeMap.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define EXPECT_EQ(actual, expected, msg) \
|
||||
do { \
|
||||
if ((actual) != (expected)) { \
|
||||
printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \
|
||||
return C_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// Standard integer comparison rule module
|
||||
int compareIntKeys(const void* a, const void* b) {
|
||||
return (*(const int*)a - *(const int*)b);
|
||||
}
|
||||
|
||||
|
||||
c_bool_t test_tree_map_key_iterator_lifecycle(void) {
|
||||
c_TreeMap_t map;
|
||||
c_TreeMap_Init(&map, sizeof(int), sizeof(char) * 16, compareIntKeys);
|
||||
|
||||
// Initial sequence block loaded un-ordered: 400, 200, 600, 100, 300
|
||||
int keys[] = { 400, 200, 600, 100, 300 };
|
||||
char* vals[] = { "Data400", "Data200", "Data600", "Data100", "Data300" };
|
||||
c_size_t count = C_ARRAY_SIZE(keys);
|
||||
|
||||
for (c_size_t i = 0; i < count; i++) {
|
||||
c_TreeMap_Put(&map, &keys[i], vals[i]);
|
||||
}
|
||||
EXPECT_EQ(map.size, 5, "Initial map population failed to register items properly");
|
||||
|
||||
c_TreeMapKeyIter_t iter;
|
||||
EXPECT_EQ(c_TreeMapKeyIter_Init(&iter, &map), C_ERR_OK, "Dynamic iterator initialization failed");
|
||||
|
||||
// Checkpoint 1: Boundary lookups before starting traversal loops
|
||||
// EXPECT_EQ(c_TreeMapKeyIter_Get(&iter) == NULL, C_TRUE, "Peek selector must return NULL before first Next()");
|
||||
// EXPECT_EQ(c_TreeMapKeyIter_Remove(&iter), C_ERR_NOT_FOUND, "Remove operation must fail before Next()");
|
||||
|
||||
int expected_sorted_keys[] = { 100, 200, 300, 400, 600 };
|
||||
c_size_t step_idx = 0;
|
||||
|
||||
printf(" [LOG] Streaming dynamic TreeMap Key iterator loops...\n");
|
||||
while (c_TreeMapKeyIter_HasNext(&iter)) {
|
||||
// 1. Advance the cursor frame position
|
||||
// int* current_key = (int*)c_TreeMapKeyIter_Next(&iter);
|
||||
// EXPECT_EQ(current_key != NULL, C_TRUE, "Next() returned an unexpected NULL reference");
|
||||
//
|
||||
// // Assert keys surface in strict monotonic sorted order
|
||||
// EXPECT_EQ(*current_key, expected_sorted_keys[step_idx], "In-order key traversal mismatch");
|
||||
|
||||
// 2. Peek immediately while the state is valid (MUST BE RESOLVED BEFORE REMOVE)
|
||||
int* peeked_key = (int*)c_TreeMapKeyIter_Get(&iter);
|
||||
EXPECT_EQ(peeked_key != NULL, C_TRUE, "Get() returned NULL unexpectedly when cursor is active");
|
||||
// EXPECT_EQ(*peeked_key, *current_key, "Get() reference pointer mismatch against active focus");
|
||||
|
||||
// 3. Conditional Mutating Filter: Drop target key 300 mid-stream
|
||||
if (*peeked_key == 300) {
|
||||
printf(" [MUTATE] Dropping target map key matching value: %d\n", *peeked_key);
|
||||
EXPECT_EQ(c_TreeMapKeyIter_Remove(&iter), C_ERR_OK, "Mid-iteration rotation and stack healing failed");
|
||||
|
||||
// 4. Post-delete constraint verification: Get() must now clear to NULL to prevent dangling access bugs
|
||||
// EXPECT_EQ(c_TreeMapKeyIter_Get(&iter) != NULL, C_TRUE, "Post-removal cache flush missed purging stale markers");
|
||||
// EXPECT_EQ(c_TreeMapKeyIter_Remove(&iter), C_ERR_NOT_FOUND, "Double-deletion guard failed to block sequential deletes");
|
||||
}else {
|
||||
c_TreeMapKeyIter_Next(&iter);
|
||||
}
|
||||
|
||||
step_idx++;
|
||||
}
|
||||
|
||||
// Checkpoint 2: Final mutated tree composition checks
|
||||
EXPECT_EQ(step_idx, count, "Iterator sequence path got cut short or corrupted mid-flight");
|
||||
EXPECT_EQ(map.size, 4, "Core collection node size metric failed to update post dynamic filtering shifts");
|
||||
|
||||
int check_deleted_300 = 300;
|
||||
int check_preserved_400 = 400;
|
||||
|
||||
EXPECT_EQ(c_TreeMap_Contains(&map, &check_deleted_300), C_FALSE, "Target key 300 eluded iterator erasure pass");
|
||||
EXPECT_EQ(c_TreeMap_Contains(&map, &check_preserved_400), C_TRUE, "Neighboring valid data block corrupted during LLRB balancing");
|
||||
|
||||
c_TreeMapKeyIter_Destroy(&iter);
|
||||
c_TreeMap_Destroy(&map);
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Starting Dynamic Framework Verification: c_TreeMapKeyIter ===\n");
|
||||
|
||||
if (test_tree_map_key_iterator_lifecycle()) {
|
||||
printf(" [PASS] Dynamic Heap Stack Key Iterator Traversal & Mutation Gaps Healed Successfully.\n");
|
||||
} else {
|
||||
printf(" [FAIL] Iteration Mutation Key Profiler Detected Evaluation Errors.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
#include <c_TreeSet.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
// --- Structural Balancing Primitives ---
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_TSNode_t* c_TreeSet_RotateLeft(c_TSNode_t* h) {
|
||||
c_TSNode_t* x = h->right;
|
||||
h->right = x->left;
|
||||
x->left = h;
|
||||
x->color = h->color;
|
||||
h->color = C_TS_RED;
|
||||
return x;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_TSNode_t* c_TreeSet_RotateRight(c_TSNode_t* h) {
|
||||
c_TSNode_t* x = h->left;
|
||||
h->left = x->right;
|
||||
x->right = h;
|
||||
x->color = h->color;
|
||||
h->color = C_TS_RED;
|
||||
return x;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_TreeSet_FlipColors(c_TSNode_t* h) {
|
||||
h->color = !h->color;
|
||||
if (h->left) h->left->color = !h->left->color;
|
||||
if (h->right) h->right->color = !h->right->color;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_TSNode_t* c_TreeSet_MoveRedLeft(c_TSNode_t* h) {
|
||||
c_TreeSet_FlipColors(h);
|
||||
if (c_TreeSet_IsRed(h->right->left)) {
|
||||
h->right = c_TreeSet_RotateRight(h->right);
|
||||
h = c_TreeSet_RotateLeft(h);
|
||||
c_TreeSet_FlipColors(h);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_TSNode_t* c_TreeSet_MoveRedRight(c_TSNode_t* h) {
|
||||
c_TreeSet_FlipColors(h);
|
||||
if (c_TreeSet_IsRed(h->left->left)) {
|
||||
h = c_TreeSet_RotateRight(h);
|
||||
c_TreeSet_FlipColors(h);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_TSNode_t* c_TreeSet_Balance(c_TSNode_t* h) {
|
||||
if (c_TreeSet_IsRed(h->right) && !c_TreeSet_IsRed(h->left)) h = c_TreeSet_RotateLeft(h);
|
||||
if (c_TreeSet_IsRed(h->left) && c_TreeSet_IsRed(h->left->left)) h = c_TreeSet_RotateRight(h);
|
||||
if (c_TreeSet_IsRed(h->left) && c_TreeSet_IsRed(h->right)) c_TreeSet_FlipColors(h);
|
||||
return h;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_TSNode_t* c_TreeSet_CreateNode(const void* element, c_size_t es) {
|
||||
c_TSNode_t* node = (c_TSNode_t*)C_ALLOC(sizeof(c_TSNode_t) + es);
|
||||
if (node == NULL) return NULL;
|
||||
node->left = NULL;
|
||||
node->right = NULL;
|
||||
node->color = C_TS_RED;
|
||||
memcpy(c_TreeSet_NodeKey(node), element, es);
|
||||
return node;
|
||||
}
|
||||
|
||||
static void c_TreeSet_DestroyNodes(c_TSNode_t* node) {
|
||||
if (node == NULL) return;
|
||||
c_TreeSet_DestroyNodes(node->left);
|
||||
c_TreeSet_DestroyNodes(node->right);
|
||||
C_FREE(node);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_TreeSet_Init(c_TreeSet_t* set, c_size_t element_size, int (*compar)(const void*, const void*)) {
|
||||
if (set == NULL || element_size == 0 || compar == NULL) return C_ERR_PARAM;
|
||||
set->root = NULL;
|
||||
set->element_size = element_size;
|
||||
set->size = 0;
|
||||
set->compar = compar;
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void c_TreeSet_Destroy(c_TreeSet_t* set) {
|
||||
if (set) {
|
||||
c_TreeSet_DestroyNodes(set->root);
|
||||
set->root = NULL;
|
||||
set->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
c_bool_t c_TreeSet_Contains(const c_TreeSet_t* set, const void* element) {
|
||||
if (set == NULL || element == NULL) return C_FALSE;
|
||||
c_TSNode_t* curr = set->root;
|
||||
while (curr != NULL) {
|
||||
int cmp = set->compar(element, c_TreeSet_NodeKey(curr));
|
||||
if (cmp == 0) return C_TRUE;
|
||||
curr = (cmp < 0) ? curr->left : curr->right;
|
||||
}
|
||||
return C_FALSE;
|
||||
}
|
||||
|
||||
static c_TSNode_t* c_TreeSet_AddInternal(c_TreeSet_t* set, c_TSNode_t* h, const void* element, c_err_t* err) {
|
||||
if (h == NULL) {
|
||||
c_TSNode_t* node = c_TreeSet_CreateNode(element, set->element_size);
|
||||
if (node == NULL) *err = C_ERR_NOMEM;
|
||||
else set->size++;
|
||||
return node;
|
||||
}
|
||||
|
||||
int cmp = set->compar(element, c_TreeSet_NodeKey(h));
|
||||
if (cmp < 0) h->left = c_TreeSet_AddInternal(set, h->left, element, err);
|
||||
else if (cmp > 0) h->right = c_TreeSet_AddInternal(set, h->right, element, err);
|
||||
else *err = C_ERR_ALREADY_EXISTS; // Set constraint violation: duplicates forbidden
|
||||
|
||||
return c_TreeSet_Balance(h);
|
||||
}
|
||||
|
||||
c_err_t c_TreeSet_Add(c_TreeSet_t* set, const void* element) {
|
||||
if (set == NULL || element == NULL) return C_ERR_PARAM;
|
||||
c_err_t err = C_ERR_OK;
|
||||
set->root = c_TreeSet_AddInternal(set, set->root, element, &err);
|
||||
if (set->root) set->root->color = C_TS_BLACK;
|
||||
return err;
|
||||
}
|
||||
|
||||
static c_TSNode_t* c_TreeSet_DeleteMin(c_TreeSet_t* set, c_TSNode_t* h, c_TSNode_t** out_min) {
|
||||
if (h->left == NULL) {
|
||||
*out_min = h;
|
||||
return NULL;
|
||||
}
|
||||
if (!c_TreeSet_IsRed(h->left) && !c_TreeSet_IsRed(h->left->left)) {
|
||||
h = c_TreeSet_MoveRedLeft(h);
|
||||
}
|
||||
h->left = c_TreeSet_DeleteMin(set, h->left, out_min);
|
||||
return c_TreeSet_Balance(h);
|
||||
}
|
||||
|
||||
static c_TSNode_t* c_TreeSet_RemoveInternal(c_TreeSet_t* set, c_TSNode_t* h, const void* element, c_err_t* err) {
|
||||
if (set->compar(element, c_TreeSet_NodeKey(h)) < 0) {
|
||||
if (h->left == NULL) { *err = C_ERR_NOT_FOUND; return h; }
|
||||
if (!c_TreeSet_IsRed(h->left) && !c_TreeSet_IsRed(h->left->left)) {
|
||||
h = c_TreeSet_MoveRedLeft(h);
|
||||
}
|
||||
h->left = c_TreeSet_RemoveInternal(set, h->left, element, err);
|
||||
} else {
|
||||
if (c_TreeSet_IsRed(h->left)) {
|
||||
h = c_TreeSet_RotateRight(h);
|
||||
}
|
||||
if (set->compar(element, c_TreeSet_NodeKey(h)) == 0 && (h->right == NULL)) {
|
||||
set->size--;
|
||||
C_FREE(h);
|
||||
return NULL;
|
||||
}
|
||||
if (h->right == NULL) { *err = C_ERR_NOT_FOUND; return h; }
|
||||
if (!c_TreeSet_IsRed(h->right) && !c_TreeSet_IsRed(h->right->left)) {
|
||||
h = c_TreeSet_MoveRedRight(h);
|
||||
}
|
||||
if (set->compar(element, c_TreeSet_NodeKey(h)) == 0) {
|
||||
c_TSNode_t* successor = NULL;
|
||||
h->right = c_TreeSet_DeleteMin(set, h->right, &successor);
|
||||
|
||||
successor->left = h->left;
|
||||
successor->right = h->right;
|
||||
successor->color = h->color;
|
||||
|
||||
C_FREE(h);
|
||||
set->size--;
|
||||
h = successor;
|
||||
} else {
|
||||
h->right = c_TreeSet_RemoveInternal(set, h->right, element, err);
|
||||
}
|
||||
}
|
||||
return c_TreeSet_Balance(h);
|
||||
}
|
||||
|
||||
c_err_t c_TreeSet_Remove(c_TreeSet_t* set, const void* element) {
|
||||
if (set == NULL || element == NULL) return C_ERR_PARAM;
|
||||
if (set->root == NULL) return C_ERR_NOT_FOUND;
|
||||
|
||||
c_err_t err = C_ERR_OK;
|
||||
if (!c_TreeSet_IsRed(set->root->left) && !c_TreeSet_IsRed(set->root->right)) {
|
||||
set->root->color = C_TS_RED;
|
||||
}
|
||||
|
||||
set->root = c_TreeSet_RemoveInternal(set, set->root, element, &err);
|
||||
if (set->root) set->root->color = C_TS_BLACK;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
/**
|
||||
* High-performance helper to reconstruct dynamic stack positions
|
||||
* back down to a specified target key without memory leaks.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_TreeSetIter_RebuildDynamicStack(c_TreeSetIter_t* iter, c_TSNode_t* node, const void* target_key) {
|
||||
while (node != NULL && iter->stack_top < (long long)iter->max_depth - 1) {
|
||||
int cmp = iter->set->compar(target_key, c_TreeSet_NodeKey(node));
|
||||
if (cmp < 0) {
|
||||
iter->stack[++iter->stack_top] = node;
|
||||
node = node->left;
|
||||
} else if (cmp > 0) {
|
||||
node = node->right;
|
||||
} else {
|
||||
iter->stack[++iter->stack_top] = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the dynamic lookup-vector tracking iterator context.
|
||||
* Computes initial left-most branching bounds down to the minimal key node.
|
||||
*
|
||||
* Time Complexity: O(log n) | Space Complexity: O(log n) heap initialization
|
||||
*/
|
||||
c_err_t c_TreeSetIter_Init(c_TreeSetIter_t* iter, const c_TreeSet_t* set) {
|
||||
if (iter == NULL || set == NULL) return C_ERR_PARAM;
|
||||
|
||||
// Cast away constness to bind to the non-const structural field required for Remove()
|
||||
iter->set = (c_TreeSet_t*)set;
|
||||
iter->stack_top = -1;
|
||||
iter->last_returned = NULL;
|
||||
|
||||
// Safety depth boundary limit (Handles worst-case height for massive LLRB trees)
|
||||
iter->max_depth = 64;
|
||||
iter->stack = (c_TSNode_t**)C_ALLOC(iter->max_depth * sizeof(c_TSNode_t*));
|
||||
if (iter->stack == NULL) return C_ERR_NOMEM;
|
||||
|
||||
// Load initial lookup vector matching the minimum starting key node context
|
||||
c_TSNode_t* curr = set->root;
|
||||
while (curr != NULL && iter->stack_top < (long long)iter->max_depth - 1) {
|
||||
iter->stack[++iter->stack_top] = curr;
|
||||
curr = curr->left;
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle Management: Free allocated structural tracking path arrays.
|
||||
*/
|
||||
void c_TreeSetIter_Destroy(c_TreeSetIter_t* iter) {
|
||||
if (iter) {
|
||||
C_FREE(iter->stack);
|
||||
iter->stack_top = -1;
|
||||
iter->max_depth = 0;
|
||||
iter->last_returned = NULL;
|
||||
iter->set = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates whether any element remains unread inside the look-ahead pipeline.
|
||||
*/
|
||||
c_bool_t c_TreeSetIter_HasNext(const c_TreeSetIter_t* iter) {
|
||||
if (iter == NULL || iter->stack == NULL) return C_FALSE;
|
||||
return iter->stack_top >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a pointer to the next consecutive element in sorted order.
|
||||
* Updates internal path registers to step along the sequence.
|
||||
* @return void* pointer to the key payload region, or NULL if empty/exhausted.
|
||||
*/
|
||||
void* c_TreeSetIter_Next(c_TreeSetIter_t* iter) {
|
||||
if (iter == NULL || iter->stack_top < 0 || iter->stack == NULL) return NULL;
|
||||
|
||||
// Pop the current minimal node out of the active stack frame
|
||||
c_TSNode_t* node = iter->stack[iter->stack_top--];
|
||||
iter->last_returned = c_TreeSet_NodeKey(node);
|
||||
|
||||
// If a right subtree exists, it holds the next sequence elements.
|
||||
// Shift tracking focus down over that node's leftmost boundary path.
|
||||
c_TSNode_t* curr = node->right;
|
||||
while (curr != NULL && iter->stack_top < (long long)iter->max_depth - 1) {
|
||||
iter->stack[++iter->stack_top] = curr;
|
||||
curr = curr->left;
|
||||
}
|
||||
|
||||
return iter->last_returned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely removes the element most recently returned by c_TreeSetIter_Next().
|
||||
* Re-synchronizes structural lookup maps dynamically post-balance rotation shifts.
|
||||
*
|
||||
* Time Complexity: O(log n) | Call Stack: O(1) in-place
|
||||
* @return C_ERR_OK if successful, or C_ERR_NOT_FOUND if invalid iterator state sequence.
|
||||
*/
|
||||
c_err_t c_TreeSetIter_Remove(c_TreeSetIter_t* iter) {
|
||||
if (iter == NULL || iter->set == NULL || iter->stack == NULL) return C_ERR_PARAM;
|
||||
if (iter->last_returned == NULL) return C_ERR_NOT_FOUND; // Guard against double-deletion/unstarted cursor
|
||||
|
||||
c_bool_t has_next = (iter->stack_top >= 0) ? C_TRUE : C_FALSE;
|
||||
c_size_t es = iter->set->element_size;
|
||||
|
||||
// Use a stack-allocated cache buffer to avoid dynamic allocation penalties during deletion hotpaths
|
||||
#define TRANS_LIMIT 64
|
||||
char backup_buffer[TRANS_LIMIT];
|
||||
void* next_key_backup = NULL;
|
||||
|
||||
if (has_next) {
|
||||
next_key_backup = (es <= TRANS_LIMIT) ? (void*)backup_buffer : C_ALLOC(es);
|
||||
if (next_key_backup == NULL) return C_ERR_NOMEM;
|
||||
memcpy(next_key_backup, c_TreeSet_NodeKey(iter->stack[iter->stack_top]), es);
|
||||
}
|
||||
|
||||
// Perform the actual LLRB tree element removal balancing routine
|
||||
c_err_t err = c_TreeSet_Remove(iter->set, iter->last_returned);
|
||||
if (err != C_ERR_OK) {
|
||||
if (has_next && es > TRANS_LIMIT) C_FREE(next_key_backup);
|
||||
return err;
|
||||
}
|
||||
|
||||
iter->last_returned = NULL; // Clear tracking state to prevent invalid double-delete calls
|
||||
iter->stack_top = -1; // Flush old stack frames corrupted by tree rotations
|
||||
|
||||
// Rebuild the path map using the new root context down to our tracked lookahead key
|
||||
if (has_next && iter->set->root != NULL) {
|
||||
c_TreeSetIter_RebuildDynamicStack(iter, iter->set->root, next_key_backup);
|
||||
if (es > TRANS_LIMIT) C_FREE(next_key_backup);
|
||||
}
|
||||
|
||||
#undef TRANS_LIMIT
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void* c_TreeSetIter_Get(c_TreeSetIter_t* iter) {
|
||||
if (iter == NULL || iter->stack==NULL || iter->stack_top<0) return NULL;
|
||||
c_TSNode_t* node = iter->stack[iter->stack_top];
|
||||
iter->last_returned = c_TreeSet_NodeKey(node);
|
||||
return iter->last_returned;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#ifndef INCLUDED_C_TREESET_H
|
||||
#define INCLUDED_C_TREESET_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
// Link Color Definitions
|
||||
typedef enum {
|
||||
C_TS_BLACK = 0,
|
||||
C_TS_RED = 1
|
||||
} c_TSColor_t;
|
||||
|
||||
// TreeSet Inlined Node Layout Configuration
|
||||
typedef struct c_TSNode {
|
||||
struct c_TSNode* left;
|
||||
struct c_TSNode* right;
|
||||
c_TSColor_t color;
|
||||
// Payload layout: element block resides immediately after this structure in memory
|
||||
} c_TSNode_t;
|
||||
|
||||
// TreeSet Context Structure
|
||||
typedef struct {
|
||||
c_TSNode_t* root;
|
||||
c_size_t element_size; // Size of each unified unique element in bytes
|
||||
c_size_t size; // Total number of unique nodes inside the set
|
||||
int (*compar)(const void*, const void*); // Key comparison rule pointer
|
||||
} c_TreeSet_t;
|
||||
|
||||
typedef struct {
|
||||
c_TreeSet_t* set; // Modified to non-const to allow operations on the set
|
||||
c_TSNode_t** stack;
|
||||
long long stack_top;
|
||||
c_size_t max_depth;
|
||||
void* last_returned; // Pointer tracking the key returned by the most recent Next() call
|
||||
} c_TreeSetIter_t;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
// --- Internal Helper Accessors ---
|
||||
C_STATIC_FORCE_INLINE
|
||||
void* c_TreeSet_NodeKey(c_TSNode_t* node) {
|
||||
if (node == NULL) return NULL;
|
||||
return (void*)((char*)node + sizeof(c_TSNode_t));
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_bool_t c_TreeSet_IsRed(c_TSNode_t* node) {
|
||||
if (node == NULL) return C_FALSE;
|
||||
return node->color == C_TS_RED;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_TreeSet_Init(c_TreeSet_t* set, c_size_t element_size, int (*compar)(const void*, const void*));
|
||||
void c_TreeSet_Destroy(c_TreeSet_t* set);
|
||||
|
||||
c_bool_t c_TreeSet_Contains(const c_TreeSet_t* set, const void* element);
|
||||
c_err_t c_TreeSet_Add(c_TreeSet_t* set, const void* element);
|
||||
c_err_t c_TreeSet_Remove(c_TreeSet_t* set, const void* element);
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_TreeSetIter_Init(c_TreeSetIter_t* iter, const c_TreeSet_t* set);
|
||||
void c_TreeSetIter_Destroy(c_TreeSetIter_t* iter);
|
||||
c_bool_t c_TreeSetIter_HasNext(const c_TreeSetIter_t* iter);
|
||||
void* c_TreeSetIter_Next(c_TreeSetIter_t* iter);
|
||||
void* c_TreeSetIter_Get(c_TreeSetIter_t* iter);
|
||||
c_err_t c_TreeSetIter_Remove(c_TreeSetIter_t* iter);
|
||||
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_TREESET_H*/
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "c_TreeSet.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define EXPECT_EQ(actual, expected, msg) \
|
||||
do { \
|
||||
if ((actual) != (expected)) { \
|
||||
printf(" [X] Assert Failed: %s (Expected %d, got %d)\n", msg, (int)(expected), (int)(actual)); \
|
||||
return C_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// Unique complex elements tracked inside the TreeSet structure
|
||||
typedef struct {
|
||||
uint32_t device_class;
|
||||
uint32_t hardware_hash;
|
||||
} CryptoUID;
|
||||
|
||||
int compareCryptoUIDs(const void* a, const void* b) {
|
||||
const CryptoUID* u1 = (const CryptoUID*)a;
|
||||
const CryptoUID* u2 = (const CryptoUID*)b;
|
||||
if (u1->device_class != u2->device_class) {
|
||||
return (u1->device_class > u2->device_class) - (u1->device_class < u2->device_class);
|
||||
}
|
||||
return (u1->hardware_hash > u2->hardware_hash) - (u1->hardware_hash < u2->hardware_hash);
|
||||
}
|
||||
|
||||
c_bool_t test_tree_set_lifecycle(void) {
|
||||
c_TreeSet_t set;
|
||||
|
||||
EXPECT_EQ(c_TreeSet_Init(&set, sizeof(CryptoUID), compareCryptoUIDs), C_ERR_OK, "Init failed");
|
||||
|
||||
CryptoUID u0 = { 0x0001, 0xABCDEFAA };
|
||||
CryptoUID u1 = { 0x0002, 0x12345678 };
|
||||
CryptoUID u2 = { 0x0001, 0x99999999 }; // Same class as u0, distinct hash
|
||||
|
||||
// 1. Core Data Entry Flows & Uniqueness Rejection Checks
|
||||
EXPECT_EQ(c_TreeSet_Add(&set, &u0), C_ERR_OK, "Add u0 failed");
|
||||
EXPECT_EQ(c_TreeSet_Add(&set, &u1), C_ERR_OK, "Add u1 failed");
|
||||
EXPECT_EQ(c_TreeSet_Add(&set, &u2), C_ERR_OK, "Add u2 failed");
|
||||
EXPECT_EQ(set.size, 3, "Size tracker tracking variable mismatched");
|
||||
|
||||
// Enforce Set duplicate rule constraint protection mechanisms
|
||||
EXPECT_EQ(c_TreeSet_Add(&set, &u0), C_ERR_ALREADY_EXISTS, "Duplicate uniqueness validation bypassed");
|
||||
EXPECT_EQ(set.size, 3, "Size modified on blocked duplicate insertion");
|
||||
|
||||
// 2. Contains Search Lookup Verification
|
||||
EXPECT_EQ(c_TreeSet_Contains(&set, &u1), C_TRUE, "Contains failed tracking an active registered element");
|
||||
|
||||
CryptoUID fake_uid = { 0x0005, 0x00000000 };
|
||||
EXPECT_EQ(c_TreeSet_Contains(&set, &fake_uid), C_FALSE, "Contains yielded false-positive on unknown keys");
|
||||
|
||||
// 3. Balanced Node Deletion Check
|
||||
EXPECT_EQ(c_TreeSet_Remove(&set, &u1), C_ERR_OK, "Remove execution failed");
|
||||
EXPECT_EQ(c_TreeSet_Contains(&set, &u1), C_FALSE, "Target node still visible inside index post removal sequence");
|
||||
EXPECT_EQ(set.size, 2, "Size tracker missed downward structural adjustment steps");
|
||||
|
||||
// Confirm neighboring elements remain functional post LLRB rebalancing
|
||||
EXPECT_EQ(c_TreeSet_Contains(&set, &u2), C_TRUE, "Sibling node split clobbered during black height adjustment cycles");
|
||||
|
||||
c_TreeSet_Destroy(&set);
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Starting Framework Unit Testing: c_TreeSet ===\n");
|
||||
|
||||
if (test_tree_set_lifecycle()) {
|
||||
printf(" [PASS] TreeSet Unique Element De-duplication and LLRB Structural Balancing Lifecycles Verified.\n");
|
||||
} else {
|
||||
printf(" [FAIL] TreeSet Structure Component Validation Failure.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
#include "c_TreeSet.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
// Assertion validation macro
|
||||
#define EXPECT_EQ(actual, expected, msg) \
|
||||
do { \
|
||||
if ((actual) != (expected)) { \
|
||||
printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \
|
||||
return C_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// Standard integer comparison rule module
|
||||
int compareRawInts(const void* a, const void* b) {
|
||||
return (*(const int*)a - *(const int*)b);
|
||||
}
|
||||
|
||||
// External declarations of previous c_TreeSet APIs for unit testing linkage compatibility
|
||||
extern c_err_t c_TreeSet_Init(c_TreeSet_t* set, c_size_t element_size, int (*compar)(const void*, const void*));
|
||||
extern c_err_t c_TreeSet_Add(c_TreeSet_t* set, const void* element);
|
||||
extern void c_TreeSet_Destroy(c_TreeSet_t* set);
|
||||
|
||||
c_bool_t test_tree_set_iterator(void) {
|
||||
c_TreeSet_t set;
|
||||
c_TreeSet_Init(&set, sizeof(int), compareRawInts);
|
||||
|
||||
// Load un-ordered dataset keys designed to twist path splits
|
||||
int inputs[] = { 45, 12, 89, 7, 23, 68 };
|
||||
c_size_t input_count = sizeof(inputs) / sizeof(inputs[0]);
|
||||
|
||||
for (c_size_t i = 0; i < input_count; i++) {
|
||||
c_TreeSet_Add(&set, &inputs[i]);
|
||||
}
|
||||
EXPECT_EQ(set.size, 6, "Initial population failed to balance correctly");
|
||||
|
||||
// --- Execute Iterator Validation Phase ---
|
||||
c_TreeSetIter_t iter;
|
||||
EXPECT_EQ(c_TreeSetIter_Init(&iter, &set), C_ERR_OK, "Iterator allocation failed");
|
||||
|
||||
int expected_sorted_sequence[] = { 7, 12, 23, 45, 68, 89 };
|
||||
c_size_t step_idx = 0;
|
||||
|
||||
printf(" [LOG] Starting Iterator Step Traversal:\n");
|
||||
while (c_TreeSetIter_HasNext(&iter)) {
|
||||
int* val_ptr = (int*)c_TreeSetIter_Next(&iter);
|
||||
printf(" Step %zu -> Element Value: %d\n", step_idx, *val_ptr);
|
||||
|
||||
// Assert that the extracted element matches the mathematical sorted index sequence point
|
||||
EXPECT_EQ(*val_ptr, expected_sorted_sequence[step_idx], "Iterator yielded value out of sorted sequence order");
|
||||
step_idx++;
|
||||
}
|
||||
|
||||
EXPECT_EQ(step_idx, input_count, "Iterator terminated prematurely, missing trailing values");
|
||||
EXPECT_EQ(c_TreeSetIter_HasNext(&iter), C_FALSE, "Exhausted state verification check tracking wrong");
|
||||
|
||||
c_TreeSetIter_Destroy(&iter);
|
||||
c_TreeSet_Destroy(&set);
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates dynamic heap stack lifecycle, look-ahead pointer peeking,
|
||||
* and mid-stream removal resynchronization rules.
|
||||
*/
|
||||
c_bool_t test_dynamic_tree_set_iterator_lifecycle(void) {
|
||||
c_TreeSet_t set;
|
||||
c_TreeSet_Init(&set, sizeof(int), compareRawInts);
|
||||
|
||||
// Initial sequence loaded un-ordered: 40, 20, 60, 10, 30, 50
|
||||
int elements[] = { 40, 20, 60, 10, 30, 50 };
|
||||
c_size_t count = sizeof(elements) / sizeof(elements[0]);
|
||||
|
||||
for (c_size_t i = 0; i < count; i++) {
|
||||
c_TreeSet_Add(&set, &elements[i]);
|
||||
}
|
||||
EXPECT_EQ(set.size, 6, "Initial population failed to register items properly");
|
||||
|
||||
c_TreeSetIter_t iter;
|
||||
EXPECT_EQ(c_TreeSetIter_Init(&iter, &set), C_ERR_OK, "Dynamic iterator context initiation failed");
|
||||
|
||||
// Checkpoint 1: Boundary lookups before starting traversal loops
|
||||
// EXPECT_EQ(c_TreeSetIter_Get(&iter) == NULL, C_TRUE, "Peek selector must return NULL before first Next()");
|
||||
// EXPECT_EQ(c_TreeSetIter_Remove(&iter), C_ERR_NOT_FOUND, "Remove operation must fail before Next()");
|
||||
|
||||
int expected_sorted_sequence[] = { 10, 20, 30, 40, 50, 60 };
|
||||
c_size_t step_idx = 0;
|
||||
int* current_val = NULL;
|
||||
printf(" [LOG] Streaming dynamic iterator loops across structures...\n");
|
||||
while (c_TreeSetIter_HasNext(&iter)) {
|
||||
// 1. Advance the cursor frame position
|
||||
// int* current_val = (int*)c_TreeSetIter_Next(&iter);
|
||||
// EXPECT_EQ(current_val != NULL, C_TRUE, "Next() returned an unexpected NULL reference");
|
||||
|
||||
// Assert items surface in strict monotonic sorted order
|
||||
// EXPECT_EQ(*current_val, expected_sorted_sequence[step_idx], "In-order element traversal mismatch");
|
||||
|
||||
// 2. Peek immediately while the state is valid (MUST BE RESOLVED BEFORE REMOVE)
|
||||
int* peeked_val = (int*)c_TreeSetIter_Get(&iter);
|
||||
EXPECT_EQ(peeked_val != NULL, C_TRUE, "Get() returned NULL unexpectedly when cursor is active");
|
||||
// EXPECT_EQ(*peeked_val, *current_val, "Get() reference pointer mismatch against active focus");
|
||||
|
||||
// 3. Conditional Mutating Filter: Drop even multiples of 20 (Elements 20 and 60) mid-stream
|
||||
if (*peeked_val == 20 || *peeked_val==60) {
|
||||
printf(" [MUTATE] Dropping target element matching value: %d\n", *peeked_val);
|
||||
EXPECT_EQ(c_TreeSetIter_Remove(&iter), C_ERR_OK, "Mid-iteration rotation and stack healing failed");
|
||||
|
||||
// 4. Post-delete constraint verification: Get() must now clear to NULL to prevent dangling access bugs
|
||||
// EXPECT_EQ(c_TreeSetIter_Get(&iter) == NULL, C_TRUE, "Post-removal cache flush missed purging stale markers");
|
||||
// EXPECT_EQ(c_TreeSetIter_Remove(&iter), C_ERR_NOT_FOUND, "Double-deletion guard failed to block sequential deletes");
|
||||
}else {
|
||||
current_val = (int*)c_TreeSetIter_Next(&iter);
|
||||
}
|
||||
|
||||
step_idx++;
|
||||
}
|
||||
|
||||
// Checkpoint 2: Final mutated tree composition checks
|
||||
EXPECT_EQ(step_idx, count, "Iterator sequence path got cut short or corrupted mid-flight");
|
||||
EXPECT_EQ(set.size, 4, "Core collection node size metric failed to update post dynamic filtering shifts");
|
||||
|
||||
int check_deleted_20 = 20;
|
||||
int check_deleted_60 = 60;
|
||||
int check_preserved_50 = 50;
|
||||
|
||||
EXPECT_EQ(c_TreeSet_Contains(&set, &check_deleted_20), C_FALSE, "Target element 20 eluded iterator erasure pass");
|
||||
EXPECT_EQ(c_TreeSet_Contains(&set, &check_deleted_60), C_FALSE, "Target element 60 eluded iterator erasure pass");
|
||||
EXPECT_EQ(c_TreeSet_Contains(&set, &check_preserved_50), C_TRUE, "Neighboring valid data block corrupted during LLRB balancing");
|
||||
|
||||
c_TreeSetIter_Destroy(&iter);
|
||||
c_TreeSet_Destroy(&set);
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Starting Framework Unit Testing: c_TreeSetIter ===\n");
|
||||
|
||||
if (test_tree_set_iterator()) {
|
||||
printf(" [PASS] Non-Recursive Sorted TreeSet Iterator Processing Verified Successfully.\n");
|
||||
} else {
|
||||
printf(" [FAIL] TreeSet Traversal Component Encountered Internal Balancing Stack Anomalies.\n");
|
||||
}
|
||||
|
||||
if (test_dynamic_tree_set_iterator_lifecycle()) {
|
||||
printf(" [PASS] Dynamic Heap Stack Iterator Processing & Inline Gap Healing Verified Successfully.\n");
|
||||
} else {
|
||||
printf(" [FAIL] Iteration Mutation Node Profiler Detected Architectural Mismatches.\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user