Files
cAI/cKit/Search/c_SeparateChainingHashST.c
T

309 lines
10 KiB
C
Raw Normal View History

2026-08-10 01:21:15 +08:00
#include <c_SeparateChainingHashST.h>
#include <c_Memory.h>
/**
* Default MurmurHash3 (32-bit) implementation for basic scalar and string keys.
* Maximizes distribution and avalanche property to minimize bucket collisions.
*/
C_STATIC_FORCE_INLINE
uint32_t c_SCHash_DefaultHash(const void* key, c_size_t key_size) {
const uint8_t* data = (const uint8_t*)key;
uint32_t hash = 0x811C9DC5; // FNV-1a baseline for quick scrambling if needed, but using a robust mix
for (c_size_t i = 0; i < key_size; i++) {
hash ^= data[i];
hash *= 0x01000193;
}
return hash;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
c_err_t c_SeparateChainingHashST_Init(c_SeparateChainingHashST_t* st, c_size_t num_buckets,
c_size_t key_size, c_size_t val_size,
uint32_t (*hash_fn)(const void*, c_size_t),
int (*key_compar)(const void*, const void*)) {
if (st == NULL || num_buckets == 0 || key_size == 0 || val_size == 0 || key_compar == NULL) {
return C_ERR_PARAM;
}
st->num_buckets = num_buckets;
st->key_size = key_size;
st->val_size = val_size;
st->size = 0;
st->hash_fn = (hash_fn != NULL) ? hash_fn : c_SCHash_DefaultHash;
st->key_compar = key_compar;
// Allocate array of bucket head pointers
st->buckets = (c_SCHashNode_t**)C_ALLOC(num_buckets * sizeof(c_SCHashNode_t*));
if (st->buckets == NULL) return C_ERR_NOMEM;
// Clear bucket heads cleanly
memset(st->buckets, 0, num_buckets * sizeof(c_SCHashNode_t*));
return C_ERR_OK;
}
void c_SeparateChainingHashST_Destroy(c_SeparateChainingHashST_t* st) {
if (st && st->buckets) {
for (c_size_t i = 0; i < st->num_buckets; i++) {
c_SCHashNode_t* curr = st->buckets[i];
while (curr != NULL) {
c_SCHashNode_t* next = curr->next;
C_FREE(curr);
curr = next;
}
}
C_FREE(st->buckets);
st->size = 0;
st->num_buckets = 0;
}
}
c_bool_t c_SeparateChainingHashST_Contains(const c_SeparateChainingHashST_t* st, const void* key) {
if (st == NULL || st->buckets == NULL || key == NULL) return C_FALSE;
uint32_t hash = st->hash_fn(key, st->key_size);
c_size_t bucket_idx = hash % st->num_buckets;
c_SCHashNode_t* curr = st->buckets[bucket_idx];
while (curr != NULL) {
if (st->key_compar(key, c_SCHash_NodeKey(curr)) == 0) {
return C_TRUE;
}
curr = curr->next;
}
return C_FALSE;
}
c_err_t c_SeparateChainingHashST_Put(c_SeparateChainingHashST_t* st, const void* key, const void* val) {
if (st == NULL || st->buckets == NULL || key == NULL || val == NULL) return C_ERR_PARAM;
uint32_t hash = st->hash_fn(key, st->key_size);
c_size_t bucket_idx = hash % st->num_buckets;
c_SCHashNode_t* curr = st->buckets[bucket_idx];
while (curr != NULL) {
if (st->key_compar(key, c_SCHash_NodeKey(curr)) == 0) {
// Key match: Overwrite value in place
memcpy(c_SCHash_NodeVal(curr, st->key_size), val, st->val_size);
return C_ERR_OK;
}
curr = curr->next;
}
// Key not found: Construct a unified packed node
c_SCHashNode_t* new_node = (c_SCHashNode_t*)C_ALLOC(sizeof(c_SCHashNode_t) + st->key_size + st->val_size);
if (new_node == NULL) return C_ERR_NOMEM;
memcpy(c_SCHash_NodeKey(new_node), key, st->key_size);
memcpy(c_SCHash_NodeVal(new_node, st->key_size), val, st->val_size);
// Insert at head of the bucket chain (O(1) insertion)
new_node->next = st->buckets[bucket_idx];
st->buckets[bucket_idx] = new_node;
st->size++;
return C_ERR_OK;
}
void* c_SeparateChainingHashST_Get(const c_SeparateChainingHashST_t* st, const void* key) {
if (st == NULL || st->buckets == NULL || key == NULL) return NULL;
uint32_t hash = st->hash_fn(key, st->key_size);
c_size_t bucket_idx = hash % st->num_buckets;
c_SCHashNode_t* curr = st->buckets[bucket_idx];
while (curr != NULL) {
if (st->key_compar(key, c_SCHash_NodeKey(curr)) == 0) {
return c_SCHash_NodeVal(curr, st->key_size);
}
curr = curr->next;
}
return NULL;
}
c_err_t c_SeparateChainingHashST_Delete(c_SeparateChainingHashST_t* st, const void* key) {
if (st == NULL || st->buckets == NULL || key == NULL) return C_ERR_PARAM;
uint32_t hash = st->hash_fn(key, st->key_size);
c_size_t bucket_idx = hash % st->num_buckets;
c_SCHashNode_t** link = &st->buckets[bucket_idx];
c_SCHashNode_t* curr = st->buckets[bucket_idx];
while (curr != NULL) {
if (st->key_compar(key, c_SCHash_NodeKey(curr)) == 0) {
// Unlink node cleanly using double pointer redirection
*link = curr->next;
C_FREE(curr);
st->size--;
return C_ERR_OK;
}
link = &curr->next;
curr = curr->next;
}
return C_ERR_NOT_FOUND;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
/**
* Steps the cursor forward to the next occupied bucket slot.
*/
C_STATIC_FORCE_INLINE
void c_SCHashIter_AdvanceToNextValid(c_SeparateChainingHashSTKeyIter_t* iter) {
iter->curr_node = NULL;
iter->curr_bucket++;
while (iter->curr_bucket < iter->st->num_buckets) {
if (iter->st->buckets[iter->curr_bucket] != NULL) {
iter->curr_node = iter->st->buckets[iter->curr_bucket];
break;
}
iter->curr_bucket++;
}
}
/**
* Initialize the Separate Chaining Hash Symbol Table Key Iterator.
* Traverses forward to latch onto the very first active key node element across bucket slots.
*
* Time Complexity: O(M) worst-case to locate first entry where M is bucket count | Space Complexity: O(1)
*/
c_err_t c_SeparateChainingHashSTKeyIter_Init(c_SeparateChainingHashSTKeyIter_t* iter,
const c_SeparateChainingHashST_t* st) {
if (iter == NULL || st == NULL) return C_ERR_PARAM;
// Cast away constness to bind to the non-const structural field required for Remove()
iter->st = (c_SeparateChainingHashST_t*)st;
iter->curr_bucket = 0;
iter->curr_node = NULL;
iter->last_returned = NULL;
// Advance forward to locate the first populated bucket slot index context
while (iter->curr_bucket < st->num_buckets) {
if (st->buckets[iter->curr_bucket] != NULL) {
iter->curr_node = st->buckets[iter->curr_bucket];
break;
}
iter->curr_bucket++;
}
return C_ERR_OK;
}
/**
* Clean up allocations within the context wrapper safely.
*/
void c_SeparateChainingHashSTKeyIter_Destroy(c_SeparateChainingHashSTKeyIter_t* iter) {
if (iter) {
iter->st = NULL;
iter->curr_bucket = 0;
iter->curr_node = NULL;
iter->last_returned = NULL;
}
}
/**
* Evaluates whether any keys remain unread inside the look-ahead pipeline.
*/
c_bool_t c_SeparateChainingHashSTKeyIter_HasNext(const c_SeparateChainingHashSTKeyIter_t* iter) {
if (iter == NULL) return C_FALSE;
return iter->curr_node != NULL;
}
/**
* Retrieve a reference pointer to the key most recently extracted by Next().
*
* Time Complexity: O(1) constant runtime overhead
*/
void* c_SeparateChainingHashSTKeyIter_Get(c_SeparateChainingHashSTKeyIter_t* iter) {
if (iter == NULL || iter->curr_node == NULL) return NULL;
// 直接回傳當前指標停靠節點的 Key
iter->last_returned = c_SCHash_NodeKey(iter->curr_node);
return iter->last_returned;
}
/**
* Extracts a pointer to the next consecutive key element.
* Updates internal path registers to step along table slots.
*
* Time Complexity: O(M) worst case to skip empty buckets, O(1) amortized
*/
void* c_SeparateChainingHashSTKeyIter_Next(c_SeparateChainingHashSTKeyIter_t* iter) {
if (iter == NULL || iter->curr_node == NULL) return NULL;
c_SCHashNode_t* node = iter->curr_node;
void* current_key = c_SCHash_NodeKey(node);
// Track history for the Remove state machine
iter->last_returned = current_key;
// Advance forward natively
if (node->next != NULL) {
iter->curr_node = node->next;
} else {
c_SCHashIter_AdvanceToNextValid(iter);
}
return current_key;
}
/**
* Stateful Removal Engine for the Hash table chain structure layout.
* Safely handles unlinking modifications and heals traversal registers in O(1) amortized time.
*/
c_err_t c_SeparateChainingHashSTKeyIter_Remove(c_SeparateChainingHashSTKeyIter_t* iter) {
if (iter == NULL || iter->st == NULL) return C_ERR_PARAM;
if (iter->last_returned == NULL) return C_ERR_NOT_FOUND;
// Find the targeted bucket index for the key we are deleting
uint32_t hash = iter->st->hash_fn(iter->last_returned, iter->st->key_size);
c_size_t target_bucket = hash % iter->st->num_buckets;
// Use a double pointer to locate and delete the node from the backing list chain
c_SCHashNode_t** link = &iter->st->buckets[target_bucket];
c_SCHashNode_t* curr = iter->st->buckets[target_bucket];
c_SCHashNode_t* next_valid_node = NULL;
while (curr != NULL) {
if (iter->st->key_compar(iter->last_returned, c_SCHash_NodeKey(curr)) == 0) {
// Capture the next element pointer in the link chain before unlinking
next_valid_node = curr->next;
// Perform the structural delete
*link = curr->next;
C_FREE(curr);
iter->st->size--;
break;
}
link = &curr->next;
curr = curr->next;
}
// Reset the state machine tracking register to prevent double deletion
iter->last_returned = NULL;
// --- EXPLICIT FORWARD SYNCHRONIZATION ---
// Update the iterator's position to point to the correct next element
if (next_valid_node != NULL) {
iter->curr_node = next_valid_node;
iter->curr_bucket = target_bucket;
} else {
// If the deletion emptied out the remainder of this bucket chain,
// search forward through subsequent buckets to find the next valid node.
iter->curr_bucket = target_bucket;
c_SCHashIter_AdvanceToNextValid(iter);
}
return C_ERR_OK;
}