开始设计
This commit is contained in:
@@ -0,0 +1,267 @@
|
||||
#include <c_HashMap.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
#define C_HASHMAP_LOAD_FACTOR_THRESHOLD 0.75f
|
||||
#define DEFAULT_CAPACITY 16
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
// Internal Helper: Doubles bucket allocations and rehashes entries
|
||||
static c_err_t hashmap_resize(c_HashMap_t* self) {
|
||||
c_size_t new_capacity = self->capacity * 2;
|
||||
c_HashMapEntry_t** new_buckets = (c_HashMapEntry_t**)C_CALLOC(new_capacity, sizeof(c_HashMapEntry_t*));
|
||||
if (!new_buckets) return C_ERR_NOMEM;
|
||||
|
||||
// Migrate entries over from old buckets array
|
||||
for (c_size_t i = 0; i < self->capacity; i++) {
|
||||
c_HashMapEntry_t* entry = self->buckets[i];
|
||||
while (entry != NULL) {
|
||||
c_HashMapEntry_t* next = entry->next;
|
||||
|
||||
// Recompute new bucket index mapping constraints
|
||||
uint32_t raw_hash = self->hash(entry->key, self->key_size);
|
||||
c_size_t new_index = raw_hash % new_capacity;
|
||||
|
||||
// Link into the new bucket array chain head
|
||||
entry->next = new_buckets[new_index];
|
||||
new_buckets[new_index] = entry;
|
||||
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
|
||||
C_FREE(self->buckets);
|
||||
self->buckets = new_buckets;
|
||||
self->capacity = new_capacity;
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void hashmap_iter_advance_to_valid(c_HashMapKeyIter_t* self) {
|
||||
while (self->bucket_index < self->map->capacity) {
|
||||
// 如果當前桶子有鏈結節點,繫結其指標的指標
|
||||
if (self->map->buckets[self->bucket_index] != NULL) {
|
||||
self->entry = &self->map->buckets[self->bucket_index];
|
||||
return;
|
||||
}
|
||||
self->bucket_index++;
|
||||
}
|
||||
// 若找不到任何有效節點,重置為 NULL 象徵迭代結束
|
||||
self->entry = NULL;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_HashMap_Init(c_HashMap_t* self, int key_size, int value_size, c_size_t initial_capacity,
|
||||
c_HashMap_Hash_f hash, c_HashMap_Compare_f compare) {
|
||||
if (!self || key_size <= 0 || value_size <= 0 || !hash || !compare) return C_ERR_PARAM;
|
||||
|
||||
self->capacity = (initial_capacity > 0) ? initial_capacity : DEFAULT_CAPACITY;
|
||||
self->size = 0;
|
||||
self->key_size = key_size;
|
||||
self->value_size = value_size;
|
||||
self->hash = hash;
|
||||
self->compare = compare;
|
||||
|
||||
self->buckets = (c_HashMapEntry_t**)C_CALLOC(self->capacity, sizeof(c_HashMapEntry_t*));
|
||||
if (!self->buckets) {
|
||||
self->capacity = 0;
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
void c_HashMap_Destroy(c_HashMap_t* self) {
|
||||
if (!self) return;
|
||||
|
||||
for (c_size_t i = 0; i < self->capacity; i++) {
|
||||
c_HashMapEntry_t* entry = self->buckets[i];
|
||||
while (entry != NULL) {
|
||||
c_HashMapEntry_t* next = entry->next;
|
||||
// C_FREE(entry->key);
|
||||
// C_FREE(entry->value);
|
||||
C_FREE(entry);
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
C_FREE(self->buckets);
|
||||
self->buckets = NULL;
|
||||
self->capacity = 0;
|
||||
self->size = 0;
|
||||
}
|
||||
|
||||
|
||||
// Maps/Overwrites keys to value entities in O(1) average time complexity
|
||||
c_err_t c_HashMap_Put(c_HashMap_t* self, const void* key, const void* value) {
|
||||
if (!self || !self->buckets || !key || !value) return C_ERR_PARAM;
|
||||
|
||||
// Trigger dynamic scale-out adjustments if load boundaries criteria are exceeded
|
||||
if ((float)(self->size + 1) / self->capacity >= C_HASHMAP_LOAD_FACTOR_THRESHOLD) {
|
||||
if (hashmap_resize(self) != C_ERR_SUCCESS) return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
uint32_t raw_hash = self->hash(key, self->key_size);
|
||||
c_size_t index = raw_hash % self->capacity;
|
||||
|
||||
// Scan the collision chain to check if the key already exists
|
||||
c_HashMapEntry_t* entry = self->buckets[index];
|
||||
while (entry != NULL) {
|
||||
if (self->compare(entry->key, key, self->key_size) == 0) {
|
||||
// Overwrite existing value mapping using deep copy semantics
|
||||
memcpy(entry->value, value, self->value_size);
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
// Allocate a new node entry if the key does not exist
|
||||
int size = (int)sizeof(c_HashMapEntry_t) + self->key_size + self->value_size;
|
||||
size = C_ALIGN_UPB(size, C_ALIGN_SIZE);
|
||||
c_HashMapEntry_t* new_entry = (c_HashMapEntry_t*)C_ALLOC(size);
|
||||
if (!new_entry) return C_ERR_NOMEM;
|
||||
|
||||
new_entry->key = new_entry+1;
|
||||
new_entry->value = new_entry->key + self->key_size;
|
||||
|
||||
// Deep copy payload bounds properties
|
||||
memcpy(new_entry->key, key, self->key_size);
|
||||
memcpy(new_entry->value, value, self->value_size);
|
||||
|
||||
// Single-chain head link injection (O(1))
|
||||
new_entry->next = self->buckets[index];
|
||||
self->buckets[index] = new_entry;
|
||||
self->size++;
|
||||
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
// Fetches value references safely into user-allocated destination spaces
|
||||
c_err_t c_HashMap_Get(c_HashMap_t* self, const void* key, void* out_value) {
|
||||
if (!self || !self->buckets || !key || !out_value) return C_ERR_PARAM;
|
||||
|
||||
uint32_t raw_hash = self->hash(key, self->key_size);
|
||||
c_size_t index = raw_hash % self->capacity;
|
||||
|
||||
c_HashMapEntry_t* entry = self->buckets[index];
|
||||
while (entry != NULL) {
|
||||
if (self->compare(entry->key, key, self->key_size) == 0) {
|
||||
memcpy(out_value, entry->value, self->value_size);
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
return C_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Unlinks map items matching key contexts safely (O(1) average time complexity)
|
||||
c_err_t c_HashMap_Remove(c_HashMap_t* self, const void* key) {
|
||||
if (!self || !self->buckets || !key) return C_ERR_PARAM;
|
||||
|
||||
uint32_t raw_hash = self->hash(key, self->key_size);
|
||||
c_size_t index = raw_hash % self->capacity;
|
||||
|
||||
c_HashMapEntry_t** curr = &self->buckets[index];
|
||||
while (*curr != NULL) {
|
||||
if (self->compare((*curr)->key, key, self->key_size) == 0) {
|
||||
c_HashMapEntry_t* to_delete = *curr;
|
||||
*curr = to_delete->next; // Unlink node entry frame properties
|
||||
|
||||
// free(to_delete->key);
|
||||
// free(to_delete->value);
|
||||
C_FREE(to_delete);
|
||||
self->size--;
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
curr = &(*curr)->next;
|
||||
}
|
||||
|
||||
return C_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
c_bool_t c_HashMap_Contains(c_HashMap_t* self, const void* key) {
|
||||
if (!self || !self->buckets || !key) return C_FALSE;
|
||||
|
||||
uint32_t raw_hash = self->hash(key, self->key_size);
|
||||
c_size_t index = raw_hash % self->capacity;
|
||||
|
||||
c_HashMapEntry_t* entry = self->buckets[index];
|
||||
while (entry != NULL) {
|
||||
if (self->compare(entry->key, key, self->key_size) == 0) return C_TRUE;
|
||||
entry = entry->next;
|
||||
}
|
||||
return C_FALSE;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
void c_HashMapKeyIter_Init(c_HashMapKeyIter_t* self, c_HashMap_t* map) {
|
||||
if (!self || !map) return;
|
||||
self->map = map;
|
||||
self->bucket_index = 0;
|
||||
self->entry = NULL;
|
||||
|
||||
// 初始化時先定位到第一個有效節點
|
||||
hashmap_iter_advance_to_valid(self);
|
||||
}
|
||||
|
||||
// 檢查是否還有下一個元素
|
||||
c_bool_t c_HashMapKeyIter_HasNext(c_HashMapKeyIter_t* self) {
|
||||
if (!self || !self->entry || !*(self->entry)) return C_FALSE;
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
// 查看目前指向的鍵(Key)指標 (不前進)
|
||||
void* c_HashMapKeyIter_Get(c_HashMapKeyIter_t* self) {
|
||||
if (!c_HashMapKeyIter_HasNext(self)) return NULL;
|
||||
return (*(self->entry))->key;
|
||||
}
|
||||
|
||||
// 獲取目前指向的鍵(Key)指標,並將迭代器前進到下一個有效節點
|
||||
void* c_HashMapKeyIter_Next(c_HashMapKeyIter_t* self) {
|
||||
if (!c_HashMapKeyIter_HasNext(self)) return NULL;
|
||||
|
||||
c_HashMapEntry_t* curr = *(self->entry);
|
||||
void* key_ptr = curr->key;
|
||||
|
||||
// 如果當前衝突鏈結中還有下一個節點,直接移向 next
|
||||
if (curr->next != NULL) {
|
||||
self->entry = &(curr->next);
|
||||
} else {
|
||||
// 如果當前衝突鏈結已到底,前進到下一個桶子並搜尋有效節點
|
||||
self->bucket_index++;
|
||||
hashmap_iter_advance_to_valid(self);
|
||||
}
|
||||
|
||||
return key_ptr;
|
||||
}
|
||||
|
||||
// 迭代器安全刪除:在走訪期間以 O(1) 的平均複雜度斷開鏈結並釋放記憶體
|
||||
void c_HashMapKeyIter_Remove(c_HashMapKeyIter_t* self) {
|
||||
if (!c_HashMapKeyIter_HasNext(self)) return;
|
||||
|
||||
c_HashMapEntry_t* to_delete = *(self->entry);
|
||||
|
||||
// 關鍵指標斷開:讓前一個節點的 next(或是桶子的首節點指標)直接指向下一個節點
|
||||
*(self->entry) = to_delete->next;
|
||||
|
||||
// 釋放該 Entry 的深複製記憶體
|
||||
// free(to_delete->key);
|
||||
// free(to_delete->value);
|
||||
C_FREE(to_delete);
|
||||
|
||||
self->map->size--;
|
||||
|
||||
// 檢查斷開後當前位置是否為空(代表原本該桶子的衝突鏈結已走訪完畢)
|
||||
if (*(self->entry) == NULL) {
|
||||
// 前進到下一個桶子搜尋下一個有效節點
|
||||
self->bucket_index++;
|
||||
hashmap_iter_advance_to_valid(self);
|
||||
}
|
||||
// 備註:若 *(self->entry) != NULL,則 self->entry 自動留在了下一個節點上,不需額外處理
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user