杂记 › 资源分享 › 同一页里的三套哈希实现:两部公版 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