據(jù)結(jié)構(gòu)詳解:Fitz 哈希表與自平衡二叉樹的實現(xiàn)與應(yīng)用)
圖形學(xué)圖像處理【免費下載鏈接】mupdfmupdf mirror項目地址https://gitcode.com/gh_mirrors/mu/mupdf點擊查看免費下載導(dǎo)讀本文聚焦 MuPDF 核心圖形庫 Fitz即fz前綴來源內(nèi)置的兩套通用數(shù)據(jù)結(jié)構(gòu)固定長度鍵哈希表fz_hash_table與字符串到值映射的自平衡二叉樹fz_treeAA-tree。這兩套結(jié)構(gòu)是 MuPDF 內(nèi)部文檔緩存store、PDF 資源索引、歸檔文件archive與 HTML 圖片去重等高頻場景的底層支撐。讀完本文你將掌握兩套結(jié)構(gòu)的全部公開 API、引用計數(shù)語義、擴(kuò)容/刪除等底層行為并能直接在基于 MuPDF 的 C 程序中正確使用它們。概述MuPDF 中的通用數(shù)據(jù)結(jié)構(gòu)MuPDF 的文檔渲染管線中充斥著大量鍵值查找需求按對象句柄查找緩存條目、按資源編號查找字體/顏色空間/圖像、按文件名查找歸檔內(nèi)條目、按圖片 id 去重等。與其為每種場景各寫一套Fitz 內(nèi)核提供了兩個通用容器fz_hash_table固定長度鍵fixed-length keys的通用哈希表見頭文件 include/mupdf/fitz/hash.hfz_tree將文本字符串映射到任意值的自平衡二叉樹AA-tree 實現(xiàn)見頭文件 include/mupdf/fitz/tree.h。兩者均定義于 Fitz 核心層通過 include/mupdf/fitz.h 隨核心庫一起暴露給上層模塊使用。哈希表fz_hash_table設(shè)計要點fz_hash_table是一個固定長度鍵的哈希表即所有鍵必須具有相同的字節(jié)長度創(chuàng)建時通過keylen參數(shù)指定。鍵與值均不參與引用計數(shù)not reference counted由調(diào)用方負(fù)責(zé)在插入/移除時手動維護(hù)引用計數(shù)。底層實現(xiàn)為開放尋址 線性探測open addressing with linear probing。與教科書實現(xiàn)不同該實現(xiàn)支持正確刪除條目而不會引發(fā)后續(xù)查找行為退化——源碼注釋明確強(qiáng)調(diào)這一點見 source/fitz/hash.c。刪除時通過do_removal將后續(xù)沖突條目向前回填backward shift從而保持探測鏈連續(xù)。哈希函數(shù)使用 CRC32C 校驗和hash()內(nèi)部直接調(diào)用fz_crc32c(0, s, len)見 source/fitz/hash.cCRC32C 的具體查表實現(xiàn)位于 source/fitz/crc32.c。創(chuàng)建與銷毀fz_hash_table *fz_new_hash_table(fz_context *ctx, int initial_size, int key_length, int lock, void (*drop_value)(fz_context *ctx, void *value)); void fz_drop_hash_table(fz_context *ctx, fz_hash_table *table);參數(shù)說明initial_size初始桶數(shù)量。表在約 80% 滿時會自動擴(kuò)容為原來的兩倍load size * 8 / 10觸發(fā)fz_resize_hash見 source/fitz/hash.c。因此初始值不需要精確預(yù)估只需合理即可。key_length每個鍵的字節(jié)長度。鍵過長時構(gòu)造函數(shù)會拋出FZ_ERROR_ARGUMENThash table key length too large單鍵上限為宏FZ_HASH_TABLE_KEY_LENGTH值為 48見 include/mupdf/fitz/hash.h。lock當(dāng)前應(yīng)傳0-1 亦可語義見下文。文檔明確警告?zhèn)髌渌魏沃刀紩?dǎo)致不可預(yù)測的行為。從源碼看見 source/fitz/hash.c該字段為-1時表示不加鎖非負(fù)時表示持有某個FZ_LOCK且各操作會調(diào)用fz_assert_lock_held校驗鎖狀態(tài)——普通用戶直接傳-1無鎖即可。注意fz_new_hash_table的lock參數(shù)是鎖的索引值0與-1的語義取決于 MuPDF 版本中的FZ_LOCK枚舉文檔以傳零為安全約定。drop_value僅用于銷毀整張表時釋放每個值插入/移除單個條目時不會調(diào)用它??蔀镹ULL。fz_drop_hash_table釋放表空間并對表中每個值調(diào)用drop_value若非 NULL后再釋放條目數(shù)組與表結(jié)構(gòu)本身見 source/fitz/hash.c。查找與插入void *fz_hash_find(fz_context *ctx, fz_hash_table *table, const void *key); void *fz_hash_insert(fz_context *ctx, fz_hash_table *table, const void *key, void *value);fz_hash_find返回鍵關(guān)聯(lián)的值未找到返回NULL。查找同樣使用 CRC32C 定位并線性探測見 source/fitz/hash.c。fz_hash_insert插入鍵值對。重復(fù)鍵不覆蓋舊值——若鍵已存在表保持不變并返回舊值指針若首次插入鍵被復(fù)制進(jìn)表內(nèi)、值所有權(quán)移交返回NULL。不進(jìn)行引用計數(shù)調(diào)用方需自行fz_keep_*值。擴(kuò)容檢查80% 負(fù)載閾值也在此函數(shù)入口執(zhí)行。刪除與遍歷void fz_hash_remove(fz_context *ctx, fz_hash_table *table, const void *key); void fz_hash_for_each(fz_context *ctx, fz_hash_table *table, void *state, void (*callback)(fz_context *ctx, void *state, void *key, int key_length, void *value));fz_hash_remove按鍵移除條目。不釋放值不引用計數(shù)調(diào)用方需自行釋放。若鍵不存在會打印一條警告assert: remove non-existent hash entry見 source/fitz/hash.c。fz_hash_for_each對表中每個鍵值對調(diào)用回調(diào)?;卣{(diào)簽名含key_length參數(shù)便于在固定長度鍵下安全讀取鍵內(nèi)容state為透傳的任意上下文指針。遍歷順序即桶數(shù)組順序與插入順序無關(guān)。頭文件中還提供了頭文件中未在文檔中單獨列出的fz_hash_filter遍歷并移除所有回調(diào)返回真的條目同樣不釋放值見 include/mupdf/fitz/hash.h 與 source/fitz/hash.c。典型用例MuPDF 內(nèi)部緩存與資源索引從源碼調(diào)用點可以看出這套哈希表承擔(dān)的核心職責(zé)文檔存儲緩存fz_store用fz_new_hash_table(ctx, 4096, sizeof(fz_store_hash), FZ_LOCK_ALLOC, NULL)建立緩存索引source/fitz/store.c并按需fz_hash_insert/fz_hash_remove維護(hù)條目source/fitz/store.c、source/fitz/store.c。PDF 資源表PDF 文檔的字體、顏色空間、圖像資源分別建表鍵為sizeof(*key)的結(jié)構(gòu)體值為 PDF 對象并使用pdf_drop_obj_as_void作為銷毀回調(diào)source/pdf/pdf-resources.c。顏色空間換算緩存colorspace.c中以n * sizeof(float)的浮點數(shù)組為鍵緩存 ICC 換算結(jié)果并使用fz_free釋放值source/fitz/colorspace.c顏色轉(zhuǎn)換的 lookup 表也以509為初始桶數(shù)建表source/fitz/colorspace.c。自平衡二叉樹fz_tree設(shè)計要點fz_tree是將文本字符串映射到值的自平衡二叉查找樹實現(xiàn)為AA-treeArne Andersson 樹每個節(jié)點帶level字段通過skew左傾修正與split層級提升兩種旋轉(zhuǎn)操作維持平衡見 source/fitz/tree.c。頭文件注釋將其概括為 AA-tree to look up things by strings見 include/mupdf/fitz/tree.h。關(guān)鍵語義查找使用strcmp做簡單指針等價比較source/fitz/tree.c插入時不復(fù)制鍵內(nèi)容也不復(fù)制值——節(jié)點僅保存key與value的指針源碼中key實際通過fz_strdup復(fù)制見 source/fitz/tree.c因此調(diào)用方無需為鍵的生命周期負(fù)責(zé)值則僅存指針由調(diào)用方保證存活。文檔層面以鍵和值僅作為指針被保存表述。無構(gòu)造函數(shù)根節(jié)點即樹fz_tree沒有構(gòu)造函數(shù)——不存在包含根的容器結(jié)構(gòu)。樹的根節(jié)點就是fz_tree*本身初始為空樹時直接使用NULL插入函數(shù)返回新的根節(jié)點因此調(diào)用方必須用返回值不斷更新根指針。fz_tree *tree NULL; tree fz_tree_insert(ctx, tree, A, my_a_obj); tree fz_tree_insert(ctx, tree, B, my_b_obj); tree fz_tree_insert(ctx, tree, C, my_c_obj); assert(fz_tree_lookup(ctx, tree, B) my_b_obj);注意插入后必須將返回值賦回根變量否則后續(xù)操作可能基于過期的根節(jié)點。同時不要插入重復(fù)鍵重復(fù)插入同一鍵時strcmp 0的節(jié)點會走右分支繼續(xù)插入可能產(chǎn)生語義不明的重復(fù)節(jié)點文檔明確要求避免。查找與銷毀void *fz_tree_lookup(fz_context *ctx, fz_tree *node, const char *key); void fz_drop_tree(fz_context *ctx, fz_tree *node, void (*dropfunc)(fz_context *ctx, void *value));fz_tree_lookup按字符串查找命中返回對應(yīng)值否則返回NULL。查找過程沿左右子樹下降見 source/fitz/tree.c。fz_drop_tree遞歸釋放整棵樹。先釋放左右子樹再釋放節(jié)點鍵副本并對每個值調(diào)用dropfunc可傳NULL跳過值的釋放最后釋放節(jié)點本身見 source/fitz/tree.c。典型用例內(nèi)存歸檔與 HTML 資源去重內(nèi)存歸檔tree archivefz_new_tree_archive直接以fz_tree為底層存儲將文件名映射到fz_bufferhas_entry/read_entry/open_entry均通過fz_tree_lookup實現(xiàn)添加條目時用fz_tree_insert更新根節(jié)點銷毀時用fz_drop_tree(ctx, tree, drop_tree_archive_entry)釋放每個 buffersource/fitz/archive.c。EPUB 元數(shù)據(jù)查重epub 文檔用fz_tree_lookup判斷條目是否已存在、以fz_tree_insert累積信息并以NULL作為 dropfunc 銷毀source/html/epub-doc.c。HTML 圖片去重HTML 解析器以圖片 id 為鍵、fz_image*為值建樹銷毀時以fz_drop_image作為 dropfunc 統(tǒng)一釋放source/html/html-parse.c、source/html/html-parse.c。哈希表 vs 二叉樹如何選擇維度fz_hash_tablefz_tree鍵類型任意固定長度字節(jié)keylen指定C 字符串strcmp比較結(jié)構(gòu)開放尋址線性探測哈希表自平衡 AA-tree查找復(fù)雜度平均 O(1)沖突時線性探測O(log n)重復(fù)鍵插入返回舊值、不覆蓋禁止插入重復(fù)鍵引用計數(shù)均不計數(shù)調(diào)用方自理均不計數(shù)調(diào)用方自理銷毀回調(diào)構(gòu)造函數(shù)傳入drop_value銷毀時傳入dropfunc擴(kuò)容80% 負(fù)載自動翻倍無容量概念動態(tài)插入典型用途文檔緩存 store、PDF 資源表、顏色換算緩存內(nèi)存歸檔、HTML/EPUB 資源去重兩條選擇建議鍵是定長二進(jìn)制結(jié)構(gòu)如對象句柄、struct或固定長度數(shù)組時優(yōu)先fz_hash_table鍵是文本名稱/路徑如文件名、圖片 id、URI且需要按字典序遍歷能力時優(yōu)先fz_tree。兩者均要求調(diào)用方管理值的引用計數(shù)插入前fz_keep_*、移除或銷毀時fz_drop_*或通過 drop 回調(diào)這是使用 MuPDF 容器最需要注意的約定??偨Y(jié)fz_hash_table與fz_tree是 MuPDF Fitz 內(nèi)核提供的兩個輕量通用容器前者以固定長度鍵 線性探測提供平均 O(1) 查找并支持安全的刪除與 80% 負(fù)載自動擴(kuò)容后者以 AA-tree 提供字符串鍵的 O(log n) 查找且無需獨立根結(jié)構(gòu)。二者的頭文件聲明見 include/mupdf/fitz/hash.h 與 include/mupdf/fitz/tree.h實現(xiàn)分別位于 source/fitz/hash.c 與 source/fitz/tree.c。在 MuPDF 的文檔緩存、PDF 資源索引、內(nèi)存歸檔與 HTML/EPUB 處理中它們承擔(dān)著高頻鍵值查找的職責(zé)理解其語義尤其是不引用計數(shù)與重復(fù)鍵行為是編寫正確 MuPDF 插件代碼的前提。贊分享圖形學(xué)圖像處理【免費下載鏈接】mupdfmupdf mirror項目地址https://gitcode.com/gh_mirrors/mu/mupdf點擊查看免費下載相關(guān)推薦MuPDF Fitz 通用數(shù)據(jù)結(jié)構(gòu)指南fz_hash_table 定長鍵哈希表與 fz_tree 字符串鍵自平衡二叉樹MuPDF Fitz 通用數(shù)據(jù)結(jié)構(gòu)指南fz_hash_table 定長鍵哈希表與 fz_tree 字符串鍵自平衡二叉樹 導(dǎo)讀 本文以 ext/mupdf/do桌面應(yīng)用文檔Hello Algorithm樹結(jié)構(gòu)二叉樹與平衡樹詳解Hello Algorithm樹結(jié)構(gòu)二叉樹與平衡樹詳解 引言為什么需要樹結(jié)構(gòu) 在日常編程中我們經(jīng)常需要處理具有層次關(guān)系的數(shù)據(jù)。想象一下文件系統(tǒng)、組織結(jié)構(gòu)教程文檔示例工程教育OpenClaw 中文社區(qū)版新手教程7 步 onboard 向?qū)牧闩渲媚愕?AI 助手附常見問題OpenClaw 中文社區(qū)版新手教程7 步 onboard 向?qū)牧闩渲媚愕?AI 助手附常見問題 OpenClaw 中文社區(qū)版openclaw cn人工智能AI Agent即時通訊后端本地部署語音上一篇Effect HttpApi 類型化響應(yīng)頭實戰(zhàn)指南WithHeaders、encodeToWithHeaders 與響應(yīng)頭覆蓋語義下一篇Open-Meteo 免費天氣 API 實戰(zhàn)Docker 一行命令跑通氣象數(shù)據(jù)服務(wù)創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考