一次压缩任务,从调用者提供的字节开始。窗口与匹配生成符号,编码器把符号写成比特,解码器沿相反方向恢复字节;缓冲区可能在任意一次调用后更换,分配也可能失败。
证明沿着这些实际对象前进:先精读一个组件的算法、布局和优化,再说明它需要建立的关系,以及关系怎样交给下一个组件。每个主题的最后一章集中讲证明思路,可以与前面的算法互相跳转。
压缩与解压的全局地图
先看每个组件负责什么,再进入算法或证明。悬停、聚焦或点选节点,查看它的输入、输出和阅读入口。
窗口与匹配
重复片段可以用“回退距离、复制长度”描述。压缩器保留近期历史,寻找可以复用的内容;滑动窗口限制保留的历史范围,哈希链减少需要逐字节比较的候选。
- 输入
- 原始输入与近期历史
- 得到的结果
- 字面量和长度/距离对
Huffman 编码构造
符号的出现次数不同。给常见符号更短的码字,可以降低编码成本;解码器还必须知道使用了哪套编码,因此也需要描述码长。
- 输入
- 符号及其出现次数
- 得到的结果
- 码长、码字与树描述
块与压缩输出
码字长短不一,应用接收的却是整字节。这个组件选择块的表示,把编码接成字节,暂存尚未交付的部分,再随调用者提供空间逐步交付。
- 输入
- 符号、码字与输出空间
- 得到的结果
- 压缩块与待交付字节
位输入、解码表与复制
解压器识别块和编码表,把比特还原成字面量与匹配。字面量直接成为字节;匹配则从历史逐字节复制,重新产生原来的内容。
- 输入
- 压缩比特与可用历史
- 得到的结果
- 恢复出的原文字节
流式状态与封装
应用常把大任务拆成多批输入和输出。输入暂时不够,或本轮输出空间用完时,流状态记住处理到哪里;下一次换了缓冲区仍能继续。zlib 与 gzip 的头尾信息也沿同一个任务处理。
- 输入
- 分批输入与有限输出空间
- 得到的结果
- 可暂停、可接续的状态
状态操作与资源生命周期
窗口和内部状态需要分配,也可能在中途分配失败。初始化、重置、复制和结束操作共同决定哪些内容可以保留、哪些必须清理。
- 输入
- 状态块、窗口和其他资源
- 得到的结果
- 合法活状态与精确清理
校验和与组合
zlib 与 gzip 封装记录未压缩数据的校验值。解压时重新计算并比较,帮助发现损坏。Adler 与 CRC 用很小的状态累积这些信息,也能跨缓冲区继续。
- 输入
- 未压缩的数据
- 得到的结果
- 校验值与比对结果
应用接口与外部交互
应用需要交出输入、接收输出,并处理返回结果。缓冲区、回调和文件接口提供不同的使用方式,底层仍然复用压缩与解压引擎。
- 输入
- 调用请求与应用数据
- 得到的结果
- 交付的数据与返回结果
| 任务中的对象 | 论证连接 |
|---|---|
| 输入与符号 | 匹配产生的符号展开为已接受的输入。 |
| 编码与解码 | 实际输出满足独立格式关系,解码产生相同字节。 |
| 调用前后状态 | 每个合法返回留下可接续的状态,精确记录消费与交付。 |
| 活的内存与资源 | 访问合法、无关对象保持不变,分配与释放精确配对。 |
窗口与匹配 ↗
重复片段的长度与距离从哪里来,以及为什么只需保留近期字节。
Huffman 编码构造 ↗
让常见符号占更少比特,并让解码端重建同一套编码。
块与压缩输出 ↗
把选好的符号写成压缩块,并安全交给调用者。
位输入、解码表与复制 ↗
读取压缩字节、识别符号,并恢复出原来的数据。
流式状态与封装 ↗
理解同一任务为什么需要多次调用,以及暂停后怎样继续。
状态操作与资源生命周期 ↗
开始、复用、复制和结束任务,同时处理分配失败。
校验和与组合 ↗
在分批处理时核对数据,并保留支持序列合并的信息。
应用接口与外部交互 ↗
为完整缓冲区、回调和 gzip 文件选择合适的调用方式。
命题、实现与接受范围
章节分别标明算法解读、局部已给出的结论和仍需组合的义务。完整命题、调用者前提与 Lean 正文可以继续查阅;项目进度页统一记录接受范围与剩余工作。
完整目标与调用序列 ↗翻译与模型边界 ↗项目进度 ↗