雜記 › 資源分享 › 同一頁裡的三套雜湊實現:兩部公版 TXT 當字節語料,把 file-md5 的分塊賬與編碼變異逐字量一遍

同一頁裡的三套雜湊實現:兩部公版 TXT 當字節語料,把 file-md5 的分塊賬與編碼變異逐字量一遍

拿兩份資源頁下載的 TXT 當字節語料,把 file-md5 頁內自寫的三套雜湊與標準實現交叉核對 54 條全等,再逐筆量分塊賬、六組字節變異、大小捨入、行標識與進度幀序。

為什麼拿這一頁對賬

手上兩份站內資源頁下載的 TXT,一份 2840 字節、一份 4995 字節。打開雜湊工具頁才注意到,它自己的腳本裡就寫了三套雜湊實現,瀏覽器原生介面不可用時才回落。

這一頁自己寫了三套實現

被測頁是文件MD5計算,模板 456 行、18467 字節,頁內自寫 md5Bytes、sha1Hex、sha256Hex 三支。把三支逐字摘進純 Node,與 node:crypto 的 md5、sha1、sha256 在 18 個輸入上交叉核對,54 條比對全部相等、0 條不符。輸入含兩份 TXT 全文、各自首 64 與末 64 字節、中間 1000 字節切片、0 字節、1 字節與 8 條 RFC 向量。

  • 輸出碼位長恆為 32 / 40 / 64 字元,即 128 / 160 / 256 位。
  • 兩素材 MD5 前 8 位 ee965b6b 與 cd254166;SHA-1 前 8 位 f544f5af 與 a7b9c271;SHA-256 前 8 位 9d6a51bc 與 3c2f351d。
  • 把 hasSubtleCrypto() 打樁成假,頁內 SHA 支與 crypto.subtle 支在同一 buffer 上逐字元相同;MD5 從不進 calcSHA。
  • DOM 層另一口徑:headless Chrome 154 實測 hasSubtleCrypto() 為真。

頁內 MD5 的分塊賬

2840 字節按頁內補位口徑量出 44 個完整塊加 24 字節尾塊,含填充共 45 塊、2880 字節;4995 字節那份是 78 個完整塊加 3 字節尾塊,含填充 79 塊、5056 字節。0x80 分別寫下下標 2840 與 4995,長度域按小端寫兩個 32 位元字,低字是 22720 與 39960,高字都是 0。頁內 padLen 寫成 ((len+8)>>6)+1,標準寫法是 ceil((len+9)/64),在長度 0..2000 上逐個比對,分歧 0 處;觸發額外塊要 len%64 達到 56,兩份素材的尾塊 24 與 3 都不觸發。

2840 字節素材的分塊賬 完整塊 44 尾塊 24 字節 含填充 45 塊 · 共 2880 字節

圖 1:一部 TXT 被切成 64 字節一塊的賬。

改一個字節會怎樣

  • 翻下標 0、下標 1000、末字節各 1 位:字節數不變,MD5 的 32 個十六進位字元有 28 到 32 個改變;SHA-1 最多 40 個;SHA-256 改變 57 到 62 個。
  • 刪首字節 2840 變 2839;末尾加一個換行變 2841;加 BOM 變 2843,三個摘要都換。
  • LF 轉 CRLF 把兩檔案拉到 2926 與 5127 字節,增量 86 與 132 恰等於兩檔案的 LF 個數;反向轉回得到原摘要。
  • GBK 或 GB18030 重編碼未實測——Node 內建 TextEncoder 只會 UTF-8,沒有 GBK 編碼器。只實測了把 UTF-8 字節按 GBK 誤讀:一份得 1618 碼元含 18 個替換字元、編回 4375 字節;另一份 2693 碼元含 20 個替換字元、編回 7715 字節。

塊級中間態給出機理:複製一份同構實現、每塊末印出鏈接變數,2840 素材共 45 塊、46 個態——只改末塊內 1 字節,前面 45 個態逐字節相同;改首字節,只有初向量相同;改下標 1420 那個字節,前 23 個態相同。4995 素材是 79 塊、80 個態,對應三檔 79、1、40。

大小那一列的數怎麼捨入

formatSize 三檔閾值 1024、1048576、1073741824,前一檔直接帶 B,後兩檔分別用 toFixed(1) 與 toFixed(2):

輸入字節列印
10231023 B
10241.0 KB
10485751024.0 KB
10485761.00 MB
10737418231024.00 MB
10737418241.00 GB

KB 檔內部,4095 印出 4.0 KB、5119 印出 5.0 KB;3583 與 3584 都印出 3.5 KB。表格每行大小列與統計區總大小調的是同一個函數,差在輸入是逐檔案還是求和:6 個 1075 字節的檔案逐行 1.0 KB、合計 6450 字節印出 6.3 KB;2 個 1126 字節逐行 1.1 KB、合計 2252 印出 2.2 KB。兩部素材同時在表內,列顯示 2.8 KB 與 4.9 KB,總大小 7835 字節顯示 7.7 KB。

一行一個檔案,重複的兩個都算

行標識由 Date.now() 加底線再加隨機段拼成,實測完整 id 長 20。把 Date.now 打樁成同一毫秒、隨機段用真值,同毫秒連加 10、100、1000 個,重複條數都是 0,隨機段長度分佈全是 6 位;隨機段也打樁成同一個值時,5 次入隊只得 1 個 id。真實時鐘代入 2000 次,隨機段全是 6 位;短尾只能用樁值代入:0.5 得 1 位、0 得空字串使整個 id 只剩 14 字元、極小值得 000000。

DOM 層實測:同一檔案添加兩次得到兩行同摘要;3 個檔案進表,ids distinct 為 3、重複標紅行 2,統計區四行是「檔案總數:3、總大小:9 B、重複檔案:2、重複群組:1」。頁面只標紅計數,不刪行也不匯出。

進度條那 50% 是哪裡來的

computing 幀的 MD5 格被換成 width:50% 的進度條,另兩格顯示「計算中...」。50% 與實際計算量無關,代碼裡這一幀就是這樣。整場探針 57 幀,done 行與 pending 行同幀並存。這組來自 headless Chrome 154 探針頁,餵的是合成字節,與上面純 Node 層兩套口徑不混。

兩份 TXT 的字節賬

語料都是從資源頁下載來的:百家姓那份 2840 字節、UTF-16 碼元 1104、碼點 1104,按 LF 切段 87,非空 84、空 3,無 CRLF、無 BOM,末字節是換行,非 ASCII 字節 2604;弟子規那份 4995 字節、碼元與碼點都是 1783,切段 133、非空 109、空 24,同樣無 CRLF 無 BOM,非 ASCII 字節 4818。兩份碼元數與碼點數相等,非 ASCII 字節都是 3 的整數倍:三位元組字元 868 與 1606 個,U+4E00 到 U+9FFF 區間的漢字 716 與 1240 個,其餘 152 與 366 個。摘要只認這條字節流,不認行——87 段還是 133 段,落到分塊賬上只是 2840 與 4995 這兩個分母。

常見問題

Q:只是換了編碼形式,為什麼摘要整個變了?

摘要函數讀的是字節序列:加 BOM 讓字節數從 2840 到 2843、LF 轉 CRLF 到 2926,輸入已不是同一串,三個摘要跟著全換。

Q:同一檔案的兩行,摘要為什麼一樣?

頁面不按內容去重:每次入隊各佔一行、各算各的摘要,兩行自然相同;統計只把它記成重複檔案 2、重複群組 1,標紅按 MD5 歸組。

Q:這麼小的檔案,怎麼也有一格 50%?

50% 是 computing 幀的佔位寫法,與檔案大小無關;探針裡每個檔案都走三幀,那一幀的 MD5 格在 DOM 裡就是這樣。

📎 相關文章

《聲律啟蒙》《笠翁對韻》整檔餵進文本對比頁:公共 154 行、新增 665 刪除 401,而統計只給兩個數 2026-11-08 《笠翁對韻》量出 21 個轉不出拼音的字:8026 鍵拼音表實測命中 7160、多音 232 次 2026-11-07 千字文有一千個不重字嗎:字數統計實測正文 996、真字 1000、去重 995 2026-11-05 120 回的「紅樓夢」整本貼進文本頻率統計,榜首為什麼是 59260 次的逗號 2026-11-04 SOS 只有 9 個音,整句字母表要 104 個音:摩斯碼的節奏怎麼算出來 2026-10-21