# 归档比较与多方言兼容

GNU 用独立名称表和偏移引用保存长名称，BSD 将名称嵌入成员，Darwin 还可能将对齐字节
计入成员数据大小。符号索引保存符号名到成员头部的绝对偏移，随着名称编码、索引宽度
或成员布局改变而变化。MoonAr 分开处理物理记录与逻辑成员，原始字节始终可观察。

## 兼容层的职责

1. 检查头部、数值与成员边界，保留特殊记录和原始字节。
2. 解析 GNU 引用与 BSD 内嵌名称，确定名称和数据范围。
3. 解码 GNU 32/64 位及 BSD/Darwin 32/64 位索引，检查字节序、计数、字符串和填充。
4. 将绝对偏移转换为普通成员索引，拒绝指向数据区、头部中间或特殊记录的引用。
5. 保留符号名及引用字段的源偏移，使诊断可以直接对应原文件。

BSD 字节序没有统一的显式标记。解析器分别验证两种解释，只接受唯一有效的结果，或
结果相同的中性解释。两种结构均有效但映射不同的归档返回 `AR010`；测试包含这样的
构造输入。空索引不会被强行标为某个字节序。

## 比较层的职责

物理比较包含名称表与符号表的原始字节，适合检查制品是否完全重现。
逻辑比较匹配普通成员，再将旧索引引用沿成员配对转换到新归档，比较符号到成员的关系。
同名符号的目标次序保留，避免漏报优先目标变化。

分组键直接使用成员类型与原始名称字节，配对和差异生成共享同一组出现序号。
两侧各只有一个同名成员时直接确定配对，再检查内容和元数据；无需先建立内容哈希桶。
不同的非 UTF-8 名称保持独立，普通成员与同名索引记录也不会混为一组。

重名组先用完整内容匹配，再处理剩余项。哈希只用于查找，碰撞后仍检查完整字节；
使用字节视图避免复制载荷，不构造二次空间的对齐矩阵。候选无法唯一识别时，结果
保留稳定配对并列出歧义，不能从相同内容推断历史身份。

## 可复现案例

| 输入变化 | 结果 | 验证位置 |
| --- | --- | --- |
| 相同长名称和数据改用 GNU/BSD 编码 | 字节不同，逻辑内容和映射相同 | `logical_wbtest.mbt`、独立工具脚本 |
| 两个不同内容的同名成员前插入新项 | 定位新增项，原载荷不误报修改 | 同上，覆盖四种工具布局 |
| 同名成员交换位置 | 顺序变化，载荷不变 | 同上 |
| 多个同名候选无法唯一识别 | 稳定配对和 `ambiguities` | `logical_wbtest.mbt` |
| 索引宽度或布局变化 | 原始索引字节不同，成员映射可保持相同 | 合成测试、LLVM 64 位索引 |
| 同名符号的目标次序交换 | `symbols_equal=false` | `logical_wbtest.mbt` |
| 符号指向无效头部 | `AR009` 和引用字段偏移 | 索引测试、真实归档变异 |
| 只改变时间戳 | 内容相同、元数据不同 | 真实 C 对象和示例 |
| Darwin 填充计入数据大小 | 保留实际提取字节 | LLVM 提取对照和回归测试 |

独立对照从本项目编写的文本和 C 源码生成归档，使用 GNU/LLVM 的成员列举、提取和
GNU nm 的归档索引或原始对象符号核对结果。旧版 GNU nm 不显示 BSD armap 时，使用
原始对象的已定义外部符号及其输入成员作为独立参照。

GNU/Darwin 64 位索引使用 LLVM 的 `SYM64_THRESHOLD` 测试开关生成小文件，避免数 GiB
填充。开关和布局依据见
[LLVM ArchiveWriter](https://github.com/llvm/llvm-project/blob/llvmorg-18.1.3/llvm/lib/Object/ArchiveWriter.cpp)。

## 判断边界

Darwin 数据内的填充与真实文件末尾换行不可无条件区分，逻辑比较保留它们。
Darwin 确定性重名归档可能调整时间戳，内容匹配后仍报告真实元数据变化。
对象内部定义、机器码及 ABI 不属于比较范围；索引结构有效不代表链接成功。
CLI 使用完整文件的内存输入，资源预算约束解析阶段；输出和副本也需要额外内存。

## thin 的直接内容比较

```sh
moonar check before/libthin.a --external
moonar diff before/libthin.a after/libthin.a --logical --json
moonar diff before/libthin.a packed.a --logical
```

每个 thin 输入以其自身目录解析引用，加载成员后与普通归档使用相同的逻辑配对规则。
名称、元数据、顺序和原索引映射均参与比较。差异序号仍对应原输入的物理记录。

两个 thin 容器可以完全相同，而所在目录的同名对象内容不同。此时 `bytes_equal=true`、
`content_equal=false`，退出码为 1。加载失败退出 2；缺失、大小变化、路径和累计预算均有诊断。
默认不允许读取目录之外的真实目标，需要时显式传入 `--allow-parent-paths`。

公共库可使用 `ThinArchive.snapshot(loader)` 与 `Archive.snapshot()` 捕获不可变比较输入。
thin 文件自身不保存旧对象；跨时间比较需要在各时间点分别捕获快照。
