Search/Sort

This commit is contained in:
2026-08-29 01:58:00 +08:00
parent 8e95904cc4
commit a0758766c5
56 changed files with 5425 additions and 0 deletions
+158
View File
@@ -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_NOTFOUND;
// 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;
}
+40
View File
@@ -0,0 +1,40 @@
#ifndef INCLUDED_C_BST_H
#define INCLUDED_C_BST_H
#ifndef INCLUDED_C_TYPES_H
#include <c_Types.h>
#endif /*INCLUDED_C_TYPES_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*/
+1
View File
@@ -0,0 +1 @@
#include <c_BinarySearch.h>
+49
View File
@@ -0,0 +1,49 @@
#ifndef INCLUDED_C_BINARYSEARCH_H
#define INCLUDED_C_BINARYSEARCH_H
#ifndef INCLUDED_C_TYPES_H
#include <c_Types.h>
#endif /*INCLUDED_C_TYPES_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*/
+168
View File
@@ -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_NOTFOUND;
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;
}
+35
View File
@@ -0,0 +1,35 @@
#ifndef INCLUDED_C_BINARYSEARCHST_H
#define INCLUDED_C_BINARYSEARCHST_H
#ifndef INCLUDED_C_TYPES_H
#include <c_Types.h>
#endif /*INCLUDED_C_TYPES_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*/
+269
View File
@@ -0,0 +1,269 @@
#include <c_HashMap.h>
#include <c_Memory.h>
#include "c_Macros.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_OK;
}
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_OK;
}
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_OK) 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_OK;
}
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_OK;
}
// 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_OK;
}
entry = entry->next;
}
return C_ERR_NOTFOUND;
}
// 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_OK;
}
curr = &(*curr)->next;
}
return C_ERR_NOTFOUND;
}
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 自動留在了下一個節點上,不需額外處理
}
+59
View File
@@ -0,0 +1,59 @@
#ifndef INCLUDED_C_HASHMAP_H
#define INCLUDED_C_HASHMAP_H
#ifndef INCLUDED_C_TYPES_H
#include <c_Types.h>
#endif /*INCLUDED_C_TYPES_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*/
+45
View File
@@ -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;
}
+36
View File
@@ -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*/
+196
View File
@@ -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_NOTFOUND;
// 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;
}
+40
View File
@@ -0,0 +1,40 @@
#ifndef INCLUDED_C_LINEARPROBINGHASHST_H
#define INCLUDED_C_LINEARPROBINGHASHST_H
#ifndef INCLUDED_C_TYPES_H
#include <c_Types.h>
#endif /*INCLUDED_C_TYPES_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*/
+154
View File
@@ -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;
}
+66
View File
@@ -0,0 +1,66 @@
#ifndef INCLUDED_C_REDBLACKBST_H
#define INCLUDED_C_REDBLACKBST_H
#ifndef INCLUDED_C_TYPES_H
#include <c_Types.h>
#endif /*INCLUDED_C_TYPES_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*/
+308
View File
@@ -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_NOTFOUND;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
/**
* 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_NOTFOUND;
// 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;
}
+78
View File
@@ -0,0 +1,78 @@
#ifndef INCLUDED_C_SEPARATECHAININGHASHST_H
#define INCLUDED_C_SEPARATECHAININGHASHST_H
#ifndef INCLUDED_C_TYPES_H
#include <c_Types.h>
#endif /*INCLUDED_C_TYPES_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*/
+312
View File
@@ -0,0 +1,312 @@
#include <c_TST.h>
#include <c_Memory.h>
#include <c_StringBuffer.h>
#include <c_ArrayStack.h>
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
/* Internal constructor helper to build an isolated TST node capsule */
static c_TSTNode_t* c_TSTNode_Create(char c) {
c_TSTNode_t* node = (c_TSTNode_t*)C_CALLOC(1, sizeof(c_TSTNode_t));
if (node) {
node->c = c;
}
return node;
}
/* Internal destructor helper to clear TST nodes non-recursively using an explicit heap stack */
static void c_TSTNode_DestroyRecursive(c_TSTNode_t* root) {
if (!root) return;
c_ArrayStack_t node_stack;
c_ArrayStack_Init(&node_stack, sizeof(c_TSTNode_t*), 256);
c_ArrayStack_Push(&node_stack, &root);
while (!c_ArrayStack_IsEmpty(&node_stack)) {
c_TSTNode_t* curr = 0;
c_ArrayStack_Pop(&node_stack, &curr);
c_bool_t advanced = C_FALSE;
// Push children onto the cleanup stack frame and clear links to avoid cycles
if (curr->left) {
c_ArrayStack_Push(&node_stack, &curr->left);
curr->left = NULL;
advanced = C_TRUE;
} else if (curr->mid) {
c_ArrayStack_Push(&node_stack, &curr->mid);
curr->mid = NULL;
advanced = C_TRUE;
} else if (curr->right) {
c_ArrayStack_Push(&node_stack, &curr->right);
curr->right = NULL;
advanced = C_TRUE;
}
if (advanced == C_FALSE) {
C_FREE(curr);
}
}
c_ArrayStack_Destroy(&node_stack);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
c_err_t c_TST_Init(c_TST_t* self) {
if (!self) return C_ERR_PARAM;
self->root = NULL;
self->size = 0;
return C_ERR_OK;
}
void c_TST_Destroy(c_TST_t* self) {
if (!self) return;
c_TSTNode_DestroyRecursive(self->root);
self->root = NULL;
self->size = 0;
}
/* Internal recursive worker to support clean TST node creation and value insertions */
static c_TSTNode_t* c_TST_PutWorker(c_TSTNode_t* x, const char* key, c_size_t d, void* value, c_bool_t* is_new, c_err_t* err) {
char c = key[d];
if (!x) {
x = c_TSTNode_Create(c);
if (!x) {
*err = C_ERR_NOMEM;
return NULL;
}
}
if (c < x->c) {
x->left = c_TST_PutWorker(x->left, key, d, value, is_new, err);
} else if (c > x->c) {
x->right = c_TST_PutWorker(x->right, key, d, value, is_new, err);
} else if (d < strlen(key) - 1) {
x->mid = c_TST_PutWorker(x->mid, key, d + 1, value, is_new, err);
} else {
if (x->value == NULL) {
*is_new = C_TRUE;
}
x->value = value;
}
return x;
}
c_err_t c_TST_Put(c_TST_t* self, const char* key, void* value) {
if (!self || !key || strlen(key) == 0 || !value) return C_ERR_PARAM;
c_bool_t is_new = C_FALSE;
c_err_t err = C_ERR_OK;
self->root = c_TST_PutWorker(self->root, key, 0, value, &is_new, &err);
if (err == C_ERR_OK && is_new == C_TRUE) {
self->size++;
}
return err;
}
void* c_TST_Get(c_TST_t* self, const char* key) {
if (!self || !key || strlen(key) == 0 || !self->root) return NULL;
c_TSTNode_t* curr = self->root;
c_size_t d = 0;
c_size_t len = strlen(key);
while (curr) {
char c = key[d];
if (c < curr->c) {
curr = curr->left;
} else if (c > curr->c) {
curr = curr->right;
} else if (d < len - 1) {
curr = curr->mid;
d++;
} else {
return curr->value;
}
}
return NULL;
}
c_bool_t c_TST_Contains(c_TST_t* self, const char* key) {
return (c_TST_Get(self, key) != NULL) ? C_TRUE : C_FALSE;
}
/* Internal prefix traversal worker */
static void c_TST_CollectWorker(c_TSTNode_t* x, c_StringBuffer_t* sb, c_size_t depth, c_StringList* result) {
if (!x) return;
// Explore smaller alphabetical character trees leftward (keeps current string prefix length unchanged)
c_TST_CollectWorker(x->left, sb, depth, result);
// Append the matching node character token directly to the string builder
c_StringBuffer_Append(sb, &(x->c), 1);
if (x->value != NULL) {
c_StringList_Append(result, sb->buffer);
}
// Continue crawling down matching children on the middle branch
c_TST_CollectWorker(x->mid, sb, depth + 1, result);
// Backtrack step: restore parent character layout configuration length boundaries
c_StringBuffer_SetLength(sb, depth);
// Explore larger alphabetical character trees rightward
c_TST_CollectWorker(x->right, sb, depth, result);
}
c_err_t c_TST_KeysWithPrefix(c_TST_t* self, const char* prefix, c_StringList* result) {
if (!self || !prefix || !result || !self->root) return C_ERR_PARAM;
c_TSTNode_t* curr = self->root;
c_size_t d = 0;
c_size_t len = strlen(prefix);
// Navigate to the end node matching the prefix string character rules
while (curr) {
char c = prefix[d];
if (c < curr->c) {
curr = curr->left;
} else if (c > curr->c) {
curr = curr->right;
} else if (d < len - 1) {
curr = curr->mid;
d++;
} else {
break; // Prefix matched up to curr node boundaries
}
}
if (!curr) return C_ERR_OK; // Prefix not found safely yields 0 matches
c_StringBuffer_t sb;
if (c_StringBuffer_Init(&sb, 256) != C_ERR_OK) return C_ERR_NOMEM;
// Seed our builder with the matching prefix handle string tokens
c_StringBuffer_Append(&sb, prefix, len);
// If the prefix node boundary itself holds an active value, record it first
if (curr->value != NULL) {
c_StringList_Append(result, prefix);
}
// Crawl down the middle branch to extract all matching multi-character children variations
c_TST_CollectWorker(curr->mid, &sb, len, result);
c_StringBuffer_Destroy(&sb);
return C_ERR_OK;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
/* Internal recursive wildcard collector worker */
static void c_TST_MatchWorker(c_TSTNode_t* x, c_StringBuffer_t* sb, const char* pattern, c_size_t d, c_StringList* result) {
if (!x) return;
char c = pattern[d];
c_size_t len = strlen(pattern);
// Explore smaller characters leftward if the pattern permits or if it's a wildcard
if (c == '.' || c < x->c) {
c_TST_MatchWorker(x->left, sb, pattern, d, result);
}
// Process current node character matching boundaries
if (c == '.' || c == x->c) {
// Append the current split character token onto your string buffer builder stack
c_StringBuffer_Append(sb, &(x->c), 1);
// Terminal case: If we have reached the final character index of the pattern layout string
if (d == len - 1) {
if (x->value != NULL) {
c_StringList_Append(result, sb->buffer);
}
} else {
// Advance deeper down the middle branch to explore matching multi-character continuations
c_TST_MatchWorker(x->mid, sb, pattern, d + 1, result);
}
// Backtracking unwinding step: reset logical buffer length configuration framework
c_StringBuffer_SetLength(sb, d);
}
// Explore larger characters rightward if the pattern permits or if it's a wildcard
if (c == '.' || c > x->c) {
c_TST_MatchWorker(x->right, sb, pattern, d, result);
}
}
/**
* Gather all keys currently matching a specific wildcard pattern string inside the TST
*/
c_err_t c_TST_KeysThatMatch(c_TST_t* self, const char* pattern, c_StringList* result) {
if (!self || !pattern || strlen(pattern) == 0 || !result || !self->root) {
return C_ERR_PARAM;
}
c_StringBuffer_t sb;
if (c_StringBuffer_Init(&sb, 256) != C_ERR_OK) {
return C_ERR_NOMEM;
}
// Start crawling the ternary search tree using our shared string buffer accumulator
c_TST_MatchWorker(self->root, &sb, pattern, 0, result);
c_StringBuffer_Destroy(&sb);
return C_ERR_OK;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
char* c_TST_LongestPrefixOf(c_TST_t* self, const char* query) {
if (!self || !query || !self->root) {
return NULL;
}
c_TSTNode_t* curr = self->root;
c_size_t query_len = strlen(query);
c_size_t longest_match_len = 0;
c_bool_t match_found = C_FALSE;
c_size_t d = 0;
// Run an iterative O(L) scan across TST split character tokens
while (curr && d < query_len) {
char c = query[d];
if (c < curr->c) {
curr = curr->left; // Step left: current character is smaller
} else if (c > curr->c) {
curr = curr->right; // Step right: current character is larger
} else {
// Character matches curr->c exactly! Check if this marks a complete key
if (curr->value != NULL) {
longest_match_len = d + 1;
match_found = C_TRUE;
}
curr = curr->mid; // Advance down the middle branch
d++; // Advance to next character in query string
}
}
// Allocate an isolated heap buffer to hold the output string copy
c_size_t output_bytes = match_found ? (longest_match_len + 1) : 1;
char* result_str = (char*)C_ALLOC(output_bytes);
if (!result_str) {
return NULL;
}
if (match_found == C_TRUE) {
memcpy(result_str, query, longest_match_len);
result_str[longest_match_len] = '\0';
} else {
result_str[0] = '\0'; // Return a clean empty string if no prefix matches
}
return result_str;
}
+77
View File
@@ -0,0 +1,77 @@
#ifndef INCLUDED_C_TST_H
#define INCLUDED_C_TST_H
#ifndef INCLUDED_C_TYPES_H
#include <c_Types.h>
#endif /*INCLUDED_C_TYPES_H*/
#ifndef INCLUDED_C_STRINGLIST_H
#include <c_StringList.h>
#endif /*INCLUDED_C_STRINGLIST_H*/
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
typedef struct c_TSTNode {
char c; // The single character split character token for this node
void* value; // Generic client value pointer associated with a complete key string
struct c_TSTNode* left; // Left branch pointer: character is smaller (<)
struct c_TSTNode* mid; // Middle branch pointer: character matches (==)
struct c_TSTNode* right; // Right branch pointer: character is larger (>)
} c_TSTNode_t;
typedef struct {
c_TSTNode_t* root; // Reference root pointer of the TST structure capsule
c_size_t size; // Total count of distinct key-value pairs stored inside the TST
} c_TST_t;
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
c_err_t c_TST_Init(c_TST_t* self);
void c_TST_Destroy(c_TST_t* self);
/**
* Insert or update a string key mapped to a generic value pointer inside the table
*/
c_err_t c_TST_Put(c_TST_t* self, const char* key, void* value);
/**
* Retrieve the generic client value pointer mapped to a string key
* @return The stored value address, or NULL if the key does not exist
*/
void* c_TST_Get(c_TST_t* self, const char* key);
/**
* Check if the TST contains a matching entry for a specific string key
*/
c_bool_t c_TST_Contains(c_TST_t* self, const char* key);
/**
* Gather all keys currently matching a specific character prefix layout string
* @param result An initialized c_StringList container to append the extracted string records
*/
c_err_t c_TST_KeysWithPrefix(c_TST_t* self, const char* prefix, c_StringList* result);
/**
* Gather all keys currently matching a specific wildcard pattern string (where '.' matches any character)
* @param pattern String pattern containing characters and '.' wildcards
* @param result An initialized c_StringList container to append the extracted string records
*/
c_err_t c_TST_KeysThatMatch(c_TST_t* self, const char* pattern, c_StringList* result);
/**
* Find the longest key registered in the TST that is a prefix of the query string.
* For example, if "a", "app", and "apple" are in the TST, LongestPrefixOf("applepie") returns "apple".
*
* @param query The source text string to analyze
* @return
* - A dynamically allocated copy of the longest matching prefix string (managed via C_ALLOC, caller frees)
* - An empty string copy "" if no prefix is matched
* - NULL if system parameters are invalid
*/
char* c_TST_LongestPrefixOf(c_TST_t* self, const char* query);
#endif /*INCLUDED_C_TST_H*/
+365
View File
@@ -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_FAIL; 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_FAIL; 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_NOTFOUND;
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_FAIL; // 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;
}
+82
View File
@@ -0,0 +1,82 @@
#ifndef INCLUDED_C_TREEMAP_H
#define INCLUDED_C_TREEMAP_H
#ifndef INCLUDED_C_TYPES_H
#include <c_Types.h>
#endif /*INCLUDED_C_TYPES_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*/
+347
View File
@@ -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_FAIL; 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_FAIL; 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_FAIL;
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_INVALID 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_FAIL; // 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;
}
+80
View File
@@ -0,0 +1,80 @@
#ifndef INCLUDED_C_TREESET_H
#define INCLUDED_C_TREESET_H
#ifndef INCLUDED_C_TYPES_H
#include <c_Types.h>
#endif /*INCLUDED_C_TYPES_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*/
+320
View File
@@ -0,0 +1,320 @@
#include <c_Trie.h>
#include <c_Memory.h>
#include <c_ArrayStack.h>
#include "c_StringBuffer.h"
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
/* Internal constructor helper to build an isolated trie node capsule */
C_STATIC_FORCE_INLINE
c_TrieNode_t* c_TrieNode_Create(void) {
c_TrieNode_t* node = (c_TrieNode_t*)C_CALLOC(1, sizeof(c_TrieNode_t));
return node; // C_CALLOC initializes all children nodes inside next[] to NULL
}
/* Internal destructor helper to clear trie nodes post-order non-recursively using an explicit stack */
static void c_TrieNode_DestroyRecursive(c_TrieNode_t* root) {
if (!root) return;
// Explicit tree-cleanup stack configuration limits space constraints safely
c_ArrayStack_t node_stack;
c_ArrayStack_Init(&node_stack, sizeof(c_TrieNode_t*), 4);
c_ArrayStack_Push(&node_stack, &root);
while (!c_ArrayStack_IsEmpty(&node_stack)) {
c_TrieNode_t* curr = 0;
c_err_t err = c_ArrayStack_Pop(&node_stack, &curr);
c_bool_t has_children = C_FALSE;
for (int i = 0; i < C_TRIE_R; i++) {
if (curr->next[i]) {
c_ArrayStack_Push(&node_stack, &curr->next[i]);
curr->next[i] = NULL; // Break loop linkage to track post-order cleanup processing
has_children = C_TRUE;
break;
}
}
if (has_children == C_FALSE) {
C_FREE(curr);
}
}
c_ArrayStack_Destroy(&node_stack);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
c_err_t c_Trie_Init(c_Trie_t* self) {
if (!self) return C_ERR_PARAM;
self->root = c_TrieNode_Create();
self->size = 0;
return self->root ? C_ERR_OK : C_ERR_NOMEM;
}
void c_Trie_Destroy(c_Trie_t* self) {
if (!self) return;
c_TrieNode_DestroyRecursive(self->root);
self->root = NULL;
self->size = 0;
}
c_err_t c_Trie_Put(c_Trie_t* self, const char* key, void* value) {
if (!self || !key) return C_ERR_PARAM;
if (!self->root) {
self->root = c_TrieNode_Create();
if (!self->root) return C_ERR_NOMEM;
}
c_TrieNode_t* curr = self->root;
c_size_t len = strlen(key);
for (c_size_t i = 0; i < len; i++) {
unsigned char c = (unsigned char)key[i];
if (!curr->next[c]) {
curr->next[c] = c_TrieNode_Create();
if (!curr->next[c]) return C_ERR_NOMEM;
}
curr = curr->next[c];
}
if (curr->value == NULL && value != NULL) {
self->size++;
} else if (curr->value != NULL && value == NULL) {
self->size--;
}
curr->value = value;
return C_ERR_OK;
}
void* c_Trie_Get(c_Trie_t* self, const char* key) {
if (!self || !key || !self->root) return NULL;
c_TrieNode_t* curr = self->root;
c_size_t len = strlen(key);
for (c_size_t i = 0; i < len; i++) {
unsigned char c = (unsigned char)key[i];
curr = curr->next[c];
if (!curr) return NULL;
}
return curr->value;
}
c_bool_t c_Trie_Contains(c_Trie_t* self, const char* key) {
return (c_Trie_Get(self, key) != NULL) ? C_TRUE : C_FALSE;
}
/* Internal recursive worker to support automated character branch purging on node deletions */
static c_TrieNode_t* c_Trie_DeleteWorker(c_TrieNode_t* x, const char* key, c_size_t d, c_bool_t* out_deleted, c_size_t* size_ref) {
if (!x) return NULL;
if (d == strlen(key)) {
if (x->value != NULL) {
x->value = NULL;
(*size_ref)--;
*out_deleted = C_TRUE;
}
} else {
unsigned char c = (unsigned char)key[d];
x->next[c] = c_Trie_DeleteWorker(x->next[c], key, d + 1, out_deleted, size_ref);
}
// Clean up empty nodes dynamically: if this node holds a value or has other sub-branches, preserve it
if (x->value != NULL) return x;
for (int c = 0; c < C_TRIE_R; c++) {
if (x->next[c] != NULL) return x;
}
// Completely orphaned branch slot achieved; purge memory to prevent layout leaks
C_FREE(x);
return NULL;
}
c_err_t c_Trie_Delete(c_Trie_t* self, const char* key) {
if (!self || !key || !self->root) return C_ERR_PARAM;
c_bool_t deleted = C_FALSE;
self->root = c_Trie_DeleteWorker(self->root, key, 0, &deleted, &(self->size));
return deleted ? C_ERR_OK : C_ERR_PARAM;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
static void c_Trie_CollectWorker(c_TrieNode_t* x, c_StringBuffer_t* sb, c_size_t depth, c_StringList* result) {
if (!x) return;
// 1. If an active data payload value exists, export a snapshot copy directly to your StringList
if (x->value != NULL) {
// Retrieve the current continuous null-terminated string handle from the buffer wrapper
// const char* completed_string = c_StringBuffer_CStr(sb);
c_StringList_Append(result, sb->buffer);
}
// 2. Iterate sequentially through all possible child alphabet pathways
for (int c = 0; c < C_TRIE_R; c++) {
if (x->next[c]) {
// Append the edge character representation directly onto your builder stack frame
char character_token = (char)c;
c_StringBuffer_Append(sb, &character_token, 1);
// Descend recursively down to explore child nodes
c_Trie_CollectWorker(x->next[c], sb, depth + 1, result);
/*
* 🛡️ BACKTRACKING STRING INVARIANT:
* When winding back up a call frame stack, we must pop/truncate the last character
* from your string buffer to restore the parent prefix context state cleanly.
*/
c_StringBuffer_SetLength(sb, depth);
}
}
}
c_err_t c_Trie_KeysWithPrefix(c_Trie_t* self, const char* prefix, c_StringList* result) {
if (!self || !prefix || !result) return C_ERR_PARAM;
c_TrieNode_t* curr = self->root;
c_size_t len = strlen(prefix);
for (c_size_t i = 0; i < len; i++) {
unsigned char c = (unsigned char)prefix[i];
curr = curr->next[c];
if (!curr) return C_ERR_OK;
}
c_StringBuffer_t sb;
if (c_StringBuffer_Init(&sb, len+256) != C_ERR_OK) {
return C_ERR_NOMEM;
}
c_StringBuffer_Append(&sb, (const char*)prefix, len);
c_Trie_CollectWorker(curr, &sb, len, result);
c_StringBuffer_Destroy(&sb);
return C_ERR_OK;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
/* Internal recursive matching collector worker */
static void c_Trie_MatchWorker(c_TrieNode_t* x, c_StringBuffer_t* sb, const char* pattern, c_size_t depth, c_StringList* result) {
if (!x) return;
c_size_t pattern_len = strlen(pattern);
// Invariant Guard: If we have reached the pattern length, check for an active terminal value payload
if (depth == pattern_len) {
if (x->value != NULL) {
c_StringList_Append(result, sb->buffer);
}
return;
}
unsigned char c = (unsigned char)pattern[depth];
// Case A: The active cursor encounters the dot wildcard character ('.')
if (c == '.') {
for (int next_char = 0; next_char < C_TRIE_R; next_char++) {
if (x->next[next_char]) {
char token = (char)next_char;
c_StringBuffer_Append(sb, &token, 1);
c_Trie_MatchWorker(x->next[next_char], sb, pattern, depth + 1, result);
// Backtracking unwinding step: reset logical buffer length context
c_StringBuffer_SetLength(sb, depth);
}
}
}
// Case B: Explicit character absolute matching step
else {
if (x->next[c]) {
char token = (char)c;
c_StringBuffer_Append(sb, &token, 1);
c_Trie_MatchWorker(x->next[c], sb, pattern, depth + 1, result);
// Backtracking unwinding step: reset logical buffer length context
c_StringBuffer_SetLength(sb, depth);
}
}
}
/**
* Gather all keys currently matching a specific wildcard pattern string
*/
c_err_t c_Trie_KeysThatMatch(c_Trie_t* self, const char* pattern, c_StringList* result) {
if (!self || !pattern || !result || !self->root) {
return C_ERR_PARAM;
}
c_StringBuffer_t sb;
if (c_StringBuffer_Init(&sb, 256) != C_ERR_OK) {
return C_ERR_NOMEM;
}
// Start crawling the trie from root utilizing our string buffer builder
c_Trie_MatchWorker(self->root, &sb, pattern, 0, result);
c_StringBuffer_Destroy(&sb);
return C_ERR_OK;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
/**
* Find the longest key registered in the Trie that is a prefix of the query string.
*/
char* c_Trie_LongestPrefixOf(c_Trie_t* self, const char* query) {
if (!self || !query || !self->root) {
return NULL;
}
c_TrieNode_t* curr = self->root;
c_size_t query_len = strlen(query);
c_size_t longest_match_len = 0;
c_bool_t match_found = C_FALSE;
// Iterate through characters of the query string sequentially
for (c_size_t i = 0; i < query_len; i++) {
unsigned char c = (unsigned char)query[i];
curr = curr->next[c];
// If the path breaks, stop the search
if (!curr) {
break;
}
// If this intermediate node marks a complete registered key, record its length
if (curr->value != NULL) {
longest_match_len = i + 1;
match_found = C_TRUE;
}
}
// Allocate an isolated heap buffer to hold the output copy string
c_size_t output_bytes = match_found ? (longest_match_len + 1) : 1;
char* result_str = (char*)C_ALLOC(output_bytes);
if (!result_str) {
return NULL;
}
if (match_found == C_TRUE) {
memcpy(result_str, query, longest_match_len);
result_str[longest_match_len] = '\0';
} else {
result_str[0] = '\0'; // Return a clean empty string if no prefix matches
}
return result_str;
}
+82
View File
@@ -0,0 +1,82 @@
#ifndef INCLUDED_C_TRIE_H
#define INCLUDED_C_TRIE_H
#ifndef INCLUDED_C_TYPES_H
#include <c_Types.h>
#endif /*INCLUDED_C_TYPES_H*/
#ifndef INCLUDED_C_STRINGLIST_H
#include <c_StringList.h>
#endif /*INCLUDED_C_STRINGLIST_H*/
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
#define C_TRIE_R 256
typedef struct c_TrieNode {
void* value; // Generic client value pointer associated with a complete key string
struct c_TrieNode* next[C_TRIE_R]; // Flat array of child node pointers mapping to character offsets
} c_TrieNode_t;
typedef struct {
c_TrieNode_t* root; // Reference root pointer of the trie structure capsule
c_size_t size; // Total count of distinct key-value pairs stored inside the trie
} c_Trie_t;
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
c_err_t c_Trie_Init(c_Trie_t* self);
void c_Trie_Destroy(c_Trie_t* self);
/**
* Insert or update a string key mapped to a generic value pointer inside the table
*/
c_err_t c_Trie_Put(c_Trie_t* self, const char* key, void* value);
/**
* Retrieve the generic client value pointer mapped to a string key
* @return The stored value address, or NULL if the key does not exist
*/
void* c_Trie_Get(c_Trie_t* self, const char* key);
/**
* Check if the trie contains a matching entry for a specific string key
*/
c_bool_t c_Trie_Contains(c_Trie_t* self, const char* key);
/**
* Remove a key-value mapping from the trie table. Cleans up orphaned down-stream nodes automatically.
*/
c_err_t c_Trie_Delete(c_Trie_t* self, const char* key);
/**
* Gather all keys currently matching a specific character prefix layout string
* @param result An initialized c_StringList container to append the extracted string records
*/
c_err_t c_Trie_KeysWithPrefix(c_Trie_t* self, const char* prefix, c_StringList* result);
/**
* Gather all keys currently matching a specific wildcard pattern string (where '.' matches any character)
* @param pattern String pattern containing characters and '.' wildcards
* @param result An initialized c_StringList container to append the extracted string records
*/
c_err_t c_Trie_KeysThatMatch(c_Trie_t* self, const char* pattern, c_StringList* result);
/**
* Find the longest key registered in the Trie that is a prefix of the query string.
* For example, if "a", "app", and "apple" are in the Trie, LongestPrefixOf("applepie") returns "apple".
*
* @param query The source text string to analyze
* @return
* - A dynamically allocated copy of the longest matching prefix string (managed via C_ALLOC, caller frees)
* - An empty string copy "" if no prefix is matched
* - NULL if system parameters are invalid
*/
char* c_Trie_LongestPrefixOf(c_Trie_t* self, const char* query);
#endif /*INCLUDED_C_TRIE_H*/