LZ4 的核心解压循环 按照 [Token][字面量溢出][原文][Offset][匹配溢出] 的顺序读取,并还原出原始数据。 这五个部分构成了 LZ4 压缩格式中每一个“序列”Sequence的完整二进制结构。解压器就是严格按照这个固定顺序像流水线一样依次读取并还原数据的。它们各自的作用如下1. Token令牌- 是什么每个序列的第一个字节是整个序列的“控制头”。- 干嘛用它被劈成两半使用。高 4 位记录字面量长度低 4 位记录匹配长度。- 核心意义它是解压器的“导航仪”告诉解压器接下来该读多少字面量、该回溯多远复制多少匹配数据。2. 字面量溢出Literal Length Overflow- 是什么0 个或多个额外字节仅当 Token 高 4 位等于 15 时才存在。- 干嘛用因为 Token 只有 4 bit最多只能表示 15。如果实际字面量长度 ≥ 15就需要这些额外字节来“接力”表达超出的部分遇到 0xFF 继续累加直到读到 0xFF 的字节为止。- 核心意义让 4 bit 的空间能表达任意大的字面量长度短字面量零开销长字面量按需扩展。3. 原文Literals / 字面量数据- 是什么一段未经压缩的原始字节长度由前两步确定。- 干嘛用直接拷贝到输出缓冲区。这些数据在历史窗口中找不到匹配无法被压缩只能原样保留。- 核心意义保证无损还原。压缩不是魔法不能匹配的数据必须忠实记录。4. Offset偏移量 / 回溯距离- 是什么固定的 2 字节小端序整数紧跟在字面量数据之后。- 干嘛用告诉解压器“去已经解压出来的数据中往回倒退多少个字节开始复制”。例如 Offset9 表示从当前位置往前数 9 个字节处开始拷贝。- 核心意义这是 LZ4 实现压缩的核心机制——用 2 字节的地址引用代替一大段重复数据。注意最后一个序列可能没有 Offset只有字面量解压完字面量后若已达目标长度就直接结束。5. 匹配溢出Match Length Overflow- 是什么0 个或多个额外字节仅当 Token 低 4 位等于 15 时才存在。- 干嘛用与字面量溢出原理相同。Token 低 4 位最多表示 15加上最小匹配长度 4即 Token 直接能表达的最大匹配长度为 19。超过 19 的部分通过这些额外字节编码。- 核心意义让短匹配绝大多数情况只需 Token 一个字节就能表达超长匹配才付出额外字节代价。 一张图看整体关系┌─────────┬──────────────┬──────────┬─────────┬──────────────┐│ Token │ 字面量溢出 │ 原文 │ Offset │ 匹配溢出 ││ (1 byte) │ (0~N bytes) │ (L bytes)│(2 bytes)│ (0~N bytes) │├─────────┼──────────────┴──────────┴─────────┴──────────────┤│ 高4位:字面量长度 ││ 低4位:匹配长度 │└─────────────────────────────────────────────────────────────┘↑ 决定第2部分是否存在及长度 ↑ 决定第5部分是否存在及长度 为什么是这个顺序这个顺序是精心设计的目的是让解压器能够单遍、流式、无需回溯地完成解压1. 先读 Token → 立刻知道接下来要处理什么2. 再读字面量溢出 → 确定要拷贝多少原文3. 拷贝原文 → 输出缓冲区有了新数据4. 读 Offset → 知道去哪里找重复数据5. 读匹配溢出 → 确定要复制多长的重复数据6. 执行复制 → 完成本序列回到步骤 1 处理下一个序列每一步都只依赖前面已读取的信息不需要预扫描或缓存整个块这就是 LZ4 解压速度极快的根本原因。- Offset决定了复制的起点从已解压数据中往回倒退多少个字节开始拷贝。- 匹配溢出决定了复制的长度当 Token 低4位不够用时补充表达要拷贝多少字节。为了让你更直观地确认我们把这两者在解压代码中的对应关系再对齐一下 Offset 从哪开始复制// 读取2字节小端序偏移量final int matchDec (compressed.readByte() 0xFF) | ((compressed.readByte() 0xFF) 8);// 计算复制起点当前位置 - 偏移量int ref dOff - matchDec;⚠️ 注意Offset 是相对于当前写入位置的回溯距离不是绝对地址。matchDec 1 表示从上一个刚写入的字节开始复制matchDec 9 表示往前数9个字节。 匹配溢出 拷贝多少的补充部分// Token低4位提供基础长度0~15 → 实际4~19int matchLen token 0x0F;// 如果基础长度达到上限15说明还有额外长度需要读取if (matchLen 0x0F) {int len;while ((len compressed.readByte()) (byte) 0xFF) {matchLen 0xFF; // 每读到0xFF累加255}matchLen len 0xFF; // 加上终止字节}// 最终复制长度 基础值 溢出值 最小匹配4matchLen MIN_MATCH; 两者如何协作完成一次复制已解压数据: [A B C D E F G H I J K L M N O P]↑ dOff(当前位置)Offset 6 → 起点 dOff - 6 指向 KmatchLen 5 → 从 K 开始复制 5 个字节: K L M N O结果: [A B C D E F G H I J K L M N O P K L M N O] 一个容易混淆的点匹配溢出本身不是完整的拷贝长度它只是完整长度的补充部分。完整的拷贝长度由三部分组成组成部分 来源 作用基础值 Token 低4位 (0~15) 短匹配零开销表达溢出值 匹配溢出字节 (0~N bytes) 仅在基础值15时存在MIN_MATCH 常量 4 因为小于4字节的匹配不值得压缩所以准确地说Token 低4位 匹配溢出 4 总共拷贝多少字节。你的理解方向完全没问题只是记住“匹配溢出”是增量而非总量就可以了。