#include #include /** * 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; }