延續 Day 12 的 mongory_array,筆者今天帶讀者把 mongory_table 的設計一次講清楚:bucket 與雜湊、碰撞(separate chaining)如何處理、何時 rehash、以及對外 API(get/set/del/each_pair)與慣用的 keys 輔助
mongory_value*)的雜湊表hash = hash*33 + c)對應 hash_string
mongory_array(直接復用自己的東西)hash(key) % capacity 對 key 算出 hash 值後對 bucket 的數量(capacity)取餘數,以確定不會超出 bucket lengthmongory_table_node* 的 linked nodesset):
value
mongory_string_cpy 複製一份進 pool,避免外部字串生命週期問題get):沿 bucket 串列比對字串,命中即回傳值,否則 NULL
del):沿 bucket 串列步進,從鏈上移除節點(頭節點或中間)count > capacity * 0.75 時觸發next_prime(capacity * 2) 找出下一個質數,以計算新容量mongory_array_resize 擴容,節點記憶體不搬家,只改鏈結pool->error,表仍可運作,但效能可能退化(維持高 load factor)mongory_table_new(pool):建立表,內部建立 bucket 陣列並填 NULL
table->set(table, key, value):新增或更新table->get(table, key):查詢,未命中回 NULL
table->del(table, key):刪除,回傳是否成功table->each(table, acc, callback):逐對(key, value)走訪,callback(key, value, acc),回傳 false 可提前終止mongory_table_merge(table, other):以 other 的鍵值覆寫/補齊 table插入、查詢、刪除與列舉:
#include <mongory-core/foundations/memory_pool.h>
#include <mongory-core/foundations/table.h>
#include <mongory-core/foundations/value.h>
#include <stdio.h>
static bool print_pair(char *key, mongory_value *value, void *acc) {
mongory_memory_pool *pool = (mongory_memory_pool *)acc;
char *vs = value->to_str(value, pool);
printf("%s => %s\n", key, vs);
return true;
}
int main() {
mongory_init();
mongory_memory_pool *pool = mongory_memory_pool_new();
mongory_table *t = mongory_table_new(pool);
t->set(t, "name", mongory_value_wrap_str(pool, "Jack"));
t->set(t, "age", mongory_value_wrap_int(pool, 18));
t->set(t, "score", mongory_value_wrap_int(pool, 90));
// 更新
t->set(t, "score", mongory_value_wrap_int(pool, 95));
// 查詢
mongory_value *age = t->get(t, "age");
printf("age=%lld\n", age->data.i64);
// 列舉
t->each(t, pool, print_pair);
// 刪除
t->del(t, "name");
mongory_memory_pool_free(pool);
mongory_cleanup();
return 0;
}
set 會複製 key 到 pool,外部修改原字串不影響表內 keypool_free 時一次回收table->each 走訪即可輸出(例如轉 JSON)mongory_value->to_str 可協助把值轉可讀字串get/set/del 為 O(1),最壞落到 O(n)(單 bucket 長鏈)pool->error 被寫入mongory_table 以簡潔穩健的策略處理碰撞與擴容,並與記憶體池天然貼合,讓上層 Matchers(特別是 Field 與條件文件解析)可以無痛地依 key 存取