{
  "lead": "SHA-256 把任意长度的数据压缩成一个 256 位（64 个十六进制字符）的「指纹」。输入哪怕只改变 1 个比特，输出也会面目全非；而且无法从指纹反推原文，也几乎不可能找到两份指纹相同的不同数据。下载文件后核对校验值、Git 的对象 ID、比特币挖矿用的都是这类哈希函数。这里的 SHA-256 完全按照 FIPS 180-4 标准用 Rust 手写，文件只在你的浏览器里计算，不会上传。",
  "formulas": [
    [
      "H<sub>i</sub> = H<sub>i−1</sub> + compress(H<sub>i−1</sub>, M<sub>i</sub>)",
      "Merkle–Damgård 结构：逐块压缩，状态向前传递"
    ]
  ],
  "terms": [
    [
      "分块与填充",
      "消息被切成 512 位（64 字节）的块。最后补一个 <code>0x80</code> 字节、若干个 0，再以大端序写入原始消息的比特长度（64 位），使总长度正好是 64 字节的整数倍。如果最后一块剩余空间不够放下长度字段，就要多补一整块，测试里 448 位的 NIST 向量正是这种情况。"
    ],
    [
      "消息扩展",
      "每个 64 字节的块先拆成 16 个 32 位字，再用移位、循环右移和异或扩展成 64 个字 <code>W₀…W₆₃</code>。"
    ],
    [
      "64 轮压缩",
      "8 个 32 位工作变量 a…h 经过 64 轮混合，每一轮用到选择函数 Ch、多数函数 Maj、两组循环右移组合 Σ₀/Σ₁，以及一个轮常数 <code>Kᵢ</code>（前 64 个素数立方根的小数部分）。所有加法都按 2³² 取模，在 Rust 里写成 <code>wrapping_add</code>。"
    ],
    [
      "流式计算",
      "哈希状态只有 8 个字加一个不满 64 字节的缓冲区，所以可以一块一块地喂数据：<code>sha256_init</code> → 多次 <code>sha256_update</code> → <code>sha256_finish</code>。无论文件多大，内存占用都只取决于每次读取的块大小（4 MB）。"
    ]
  ],
  "flow": [
    [
      "js",
      "文本模式：每次输入都用 <code>TextEncoder</code> 把文字转成 UTF-8 字节，调用一次性函数 <code>sha256_hex()</code>。"
    ],
    [
      "js",
      "文件模式：用 <code>File.slice()</code> 每次读取 4 MB，转成 <code>Uint8Array</code>。"
    ],
    [
      "rust",
      "每块调用 <code>sha256_update(块)</code>，Rust 在全局状态中累积压缩结果；全部读完后调用 <code>sha256_finish()</code>，完成填充并返回十六进制字符串。"
    ],
    [
      "js",
      "JS 再调用浏览器内置的 <code>crypto.subtle.digest('SHA-256', …)</code> 计算一遍，比较两者的结果和速度。"
    ]
  ],
  "flow_notes": [
    "「生成并计算」会用 xorshift 生成确定性的伪随机数据，每次运行的哈希值都相同，方便在没有大文件时测试。"
  ],
  "perf": [
    "参考数据（一台 Linux 测试机，Chromium 152，256 MB 测试数据）：WASM 约 300 MB/s，Web Crypto 约 1,380 MB/s。",
    "Web Crypto 快 4 倍多并不奇怪：它调用的是浏览器内置的原生加密库，还能用上 CPU 专门的 SHA 硬件指令（x86 的 SHA-NI、ARM 的 SHA2 扩展）；WASM 目前没有这类指令，只能用普通的整数运算。",
    "那为什么还要在 WASM 里实现？Web Crypto 只提供少数几种标准算法，而且只能一次性处理整个缓冲区，没有流式接口。需要其他哈希算法（如 BLAKE3、xxHash），或者要分块处理超大文件时，WASM 是更灵活的选择。",
    "Rust 实现中，完整的 64 字节块直接从输入切片读取（<code>chunks_exact</code>），不经过缓冲区复制；只有块边界上不满 64 字节的部分才进入内部缓冲。"
  ],
  "src": [
    [
      "crates/tools/sha256/src/lib.rs",
      ""
    ],
    [
      "www/sha256/index.js",
      ""
    ]
  ]
}
