重构
This commit is contained in:
@@ -1,308 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user