# 设计原理与工程取舍

本文由 AI 辅助整理，供源码审阅和理解。接口的精确约定见 [API.md](API.md)。

## 1. 为什么选择离线制作与校验

发布开源软件、交付数据集或检查离线镜像时，制作者需要从实际文件生成标准元数据，接收者需要用实际内容核验，发现错误后定位文件和字节范围。SwarmCrate 完成这两条链路；网络连接、下载调度和做种属于另外的系统。CLI 自己读取文件并由 MoonBit 实现元数据与哈希布局，不调用其他种子制作程序。

## 2. v1、v2 与混合格式如何不同

v1 把文件按元数据顺序拼成一条字节流，使用 SHA-1 分片，因此一个分片可能跨两个或更多文件。报告必须保留所有真实贡献范围。

v2 为每个文件建立独立的 SHA-256 Merkle 树，叶子覆盖 16 KiB 数据，最后一个真实块可短于 16 KiB。树平衡所需的缺失叶子是 32 个零字节的哈希值，不是“零填充数据块的 SHA-256”。多片文件的 piece layer 必须先验证能重建文件根，才能信任它来逐片核验。

混合格式同时保留两套标识，并通过 v1 虚拟补零使非空文件按片边界对齐。v1 的虚拟补零是真实参与 SHA-1 的零字节，与 v2 的零哈希叶子用途不同。解析时要核对两套文件路径、顺序、长度和对齐，避免同一份元数据描述不同载荷。

0.4.0 也能读取独立 v1 的 BEP 47 补零条目。解析器分别维护真实文件索引和含补零的存储顺序：前者用于磁盘与回调，后者用于 SHA-1 片边界。补零只能填满已有的未满分片，路径可省略，相同补零路径可重复，始终由内存中的零字节提供。无需在磁盘创建 `.pad` 文件。普通 v1 文件仍可跨片相连，因此局部核验继续计算实际所需的邻居，而不假设所有文件都已对齐。

## 3. 一遍数据如何产生两套哈希

创建器先确定性排序，但回调文件索引仍映射到调用方输入数组。读取同一文件时偏移只向前增加，每次最多 1 MiB 且不跨片边界。读取块中的字节同时进入 SHA-1 片缓冲和 SHA-256 的 16 KiB 叶子计算。一个非末尾文件结束后，SHA-1 缓冲按需要补零；v2 只处理真实数据与自身的零哈希规则。

一次真实字节读取并不意味着哈希计算只做一次：两套算法都仍执行。它主要降低读取量与调用开销。0.3.0 首先实现创建的单遍读取；0.5.0 将混合核验也改为单遍：在同一次回调返回的数据上计算 SHA-256 叶子并填充 SHA-1 片缓冲。SHA-1 的短片补零根据已核对布局的实际存储长度处理，SHA-256 仍遵循独立的 Merkle 规则，两套预期哈希必须分别比较。

每个混合格式真实文件按片对齐，因此两种算法可共享文件内的片边界。读取失败同时使该片的两套检查不可读，之后的片继续检查，不为第二种算法重新读取失败片。诊断保持先 SHA-1、再 SHA-256 的既有顺序；有界暂存 SHA-256 失败详情，随着 SHA-1 失败增加丢弃无法进入最终上限的尾部详情。无需缓存整个文件或第二套实际分片层。独立对照测试验证稳定输入、确定性错误、详情截断和两种元数据哈希各自损坏时的行为。

目录索引减少构树时对兄弟节点的反复查找，编码时仍需对字典键按原始字节排序。`info` 标识依赖标准字节序，不能为了构树效率随意改变它。

## 4. 自动分片为什么是策略而非新格式

分片过小会产生更多哈希与元数据，过大则损坏定位更粗，并增加混合格式的补零空间。默认策略从 16 KiB 的二次幂逐步选择，目标约 4,096 片，最大 16 MiB。v1 使用拼接字节数，v2/混合格式使用各文件向上取整后的片数。

如果非空文件本来就多于目标，不能强行追逐不可能达到的目标，因此将有效目标提高到非空文件数。目标在最大尺寸处是软限制，400,000 片仍是硬上限。数值长度使用 Int64，实际 Node 文件偏移不得超过 `2^53-1`。库的历史默认 256 KiB 保持不变；CLI 明确使用自动策略，用户也可显式固定参数。

## 5. 报告中的通过到底覆盖什么

不指定选择时检查全部列出的文件，包括零长度文件。指定文件时，v1 还可能需要共享分片中相邻文件的部分字节；不会递归检查这些邻居的其他分片。报告列出 `selected_files`、`required_files`、检查数、跳过数和 `complete`。局部成功标记为 PARTIAL PASS；要证明全部载荷通过，必须同时满足 `ok` 与 `complete`。

无法读取的数据不能当作可信零数据。v2 读取异常只让当前片不可读，之后的片仍检查。有界详情不会截断汇总计数。混合报告的检查数包含两种哈希，不能将其直接解释为不同物理分片的数量。

## 6. 安全性、可复现性与依赖

0.11.0 为创建提供独立进度类型和检查点。先预检，再冻结调用方路径和分发地址数组，保持原始读取索引；读取、编码前及编码验证后均可取消，取消或错误不返回种子。创建读错直接中止，不能借用核验的失败分片恢复语义。

受控命令在写入前再次征询观察者，并在其返回后重新核对扫描快照。只有通过这些门槛才启动原子发布：同父目录暂存、完整写入/fsync/关闭后以硬链接创建新目标，避免中断时暴露半成品目标或覆盖竞争者。此新增 API 要求硬链接支持，无支持时失败且不回退；旧写入接口不变。提交前中断可能残留暂存数据，提交后中断目标已完整，不能声称崩溃后没有临时文件或断电持久性。可复现故障和约定见[创建控制](creation-control.md)。

0.10.0 以独立控制通道为核验 SDK 增加进度和协作取消。观察者的取消/错误先保留在私有状态中；读取或哈希层捕获异常后复查此状态，从而阻止取消变成普通不可读分片后继续执行。真实读错仍使用原有分片恢复语义，不能仅凭错误代码字符串判断取消来源。混合格式共享控制计数，两个哈希比较分别计数，载荷仍只读一遍。

进度事件仅含标量快照，不暴露可修改的内部范围数组。最终检查点位于不可变凭据构造之前，此处取消也不返回完整结果。宿主消费方在 API 返回和源文件复查后才独占保存，取消不留下完整凭据；即使检查数达到总数，仍需等待返回并检查 `ok` / 范围。该机制是同步检查点，不抢占阻塞 I/O 或 JS 事件循环，创建/异步宿主适配留待后续，见 [SDK 控制契约](sdk-control.md)。

0.9.0 将保留的分片失败按真实文件分组，先对每类范围取并集，再对哈希不一致、不可读影响及缺失内容取并集。排序后合并相邻或重叠区间，避免混合格式两套哈希与不同原因重复计算同一物理字节。长文件的额外尾部单列，不与期望内容混算；空文件缺失保留问题而不生成虚构区间。v1 共享失败分片只能指明参与范围，不能判断哪个文件实际坏了或造成读取失败。

诊断与同次凭据绑定，新增导出和 HTML 分组不改变旧核验标准输出。截断标记会传播到每一行：未记录问题不代表通过，字节数和文件数可能只覆盖保留详情。`--detail-limit` 限制保存详情，不限制检查过程与全量计数。短文件因大小不符不尝试读取已有前缀，因此不可读影响可与缺失尾部重叠；并集避免重复。输出均经过原有路径及读后一致性检查，载荷不会自动修复。详见[按文件诊断](file-diagnostics.md)。

0.8.0 为核验增加同次生成的不可变凭据。工厂从同一份解析结果和核验流程捕获精确种子 SHA-256、infohash、真实文件映射、所需文件的观测大小和完整报告，避免任意报告与另一份元数据拼接。大小映射在回调前复制，结果在返回前序列化，因此外部数组后续修改不会改写快照。`swarmcrate.verify.v2` 保持兼容，独立凭据使用 `swarmcrate.receipt.v1`。完整/局部通过和失败明确分开，文件映射及 v1 邻居依赖均不冒充全量认证。

完整种子摘要还包括 `info` 之外的字节，所以仅改 Tracker 也需产生新凭据。它用于对应精确输入，不认证发布者、不提供数字签名或未来有效性保证。CLI 在载荷读取前检查报告目标，防止覆盖已有文件或把缺失载荷创建为报告；复查所需源文件后依次独占写入凭据与 HTML。两个输出不是原子事务，先写出的完整文件可在后续 I/O 失败时保留。字段和使用边界见[核验凭据说明](verification-receipts.md)。

发布前规划复用创建参数与资源校验：v1 按拼接后的逻辑字节数向上取整，v2 按每个文件分别向上取整，混合格式同时统计两套。虚拟补零取决于规范路径序中哪个文件最后出现，因此通过线性查找最后路径来计算，不必为规划再排序整个集合。最后的空文件也会影响此前非空文件的尾部补零。v2 分片层在哈希后才能根据相同根去重，所以规划只给出去重前的上限，不把它当作完整元数据大小或实际占盘量。该过程不读取载荷，也不能产生内容标识。

元数据解析有长度、深度、节点、文件和片数限制。路径采用安全子集，拒绝遍历、设备名和冲突；文件适配器拒绝符号链接与特殊文件。输出以独占方式新建。源文件身份、大小和时间信息在读取周围及完成前复查，检测普通并发修改，但不构成原子快照。

0.6.0 将源目录清单也纳入创建前的复查。只看已知文件无法发现新文件，特别是原本被忽略的空目录后来出现内容。扫描时保存每个目录的身份与直接子项名称，创建完成前复查文件和这些目录；目录名称按排序后的清单比较，枚举顺序变化不算内容变化。空目录仍不进入种子，但会参与一致性检查。节点遍历采用跨目录共享的 200,000 条目预算，OS 枚举按剩余预算逐项读取，超限即停止并关闭句柄。它能缩小普通编辑导致遗漏内容的窗口，但仍是顺序检查，无法消除最后一次检查与输出写入之间的并发变化。

分发地址编辑保留原始 `info` 字节，所以修改 Tracker 或 Web Seed 不改变内容标识。SHA-1/SHA-256 来自固定版本的官方 MoonBit crypto 包；本项目的实现工作是标准编码、数据模型、布局、校验流程与工具链集成。哈希只能证明与给定元数据一致，不能验证发布者身份。

运行时依赖和测试用第三方比较工具分别见 [THIRD_PARTY.md](../THIRD_PARTY.md)。用户材料与凭据不属于仓库或发布包。理解这些边界比只记住代码行数或测试数量更有助于审查项目。
