LZ4 字典压缩与随机访问解压:dictionaryRandomAccess 示例深度解析 LZ4 字典压缩与随机访问解压dictionaryRandomAccess 示例深度解析【免费下载链接】lz4Extremely Fast Compression algorithm项目地址: https://gitcode.com/GitHub_Trending/lz/lz4dictionaryRandomAccess是 LZ4 官方仓库 examples/ 目录下的核心教学示例它同时演示了两项实用技术基于字典Dictionary的流式压缩以及面向压缩块的随机访问Random Access解压。本文以 examples/dictionaryRandomAccess.md 为主线结合 examples/dictionaryRandomAccess.c 的完整实现、lib/lz4.h 的流式 API 文档与 tests/test-lz4-dict.sh 的字典测试讲解文件格式、压缩/解压流程、命令行用法与底层原理帮助读者掌握“对同质化文件既能保持压缩比、又能任意定位读取”的自定义 LZ4 块流方案。示例定位它不是 lz4frame而是一个教学用的自定义格式先明确边界该示例的输出文件不是lz4frame 格式也不保证跨平台可读。原文档在开头就强调“Please note that the output file is not compatible with lz4frame and is platform dependent.”examples/dictionaryRandomAccess.md。这意味着它不使用 lib/lz4frame.c 提供的帧封装magic、块头、内容大小、校验和等自描述元数据它直接以裸块raw block方式写入压缩数据并自己设计了一套极简的“魔数 块区 跳表”容器布局跳表中的偏移量、块计数均以本机int4 字节直接fwrite/fread没有字节序转换因此换平台大端/小端或换int宽度即不可读。也正因如此它非常适合用来学习LZ4 Block 级 API 与流式 API 的协作方式而不是用于生产存储。与它同目录的 examples/streaming_api_basics.md、examples/blockStreaming_doubleBuffer.md 分别介绍了流式 API 基础与双缓冲模型而本文示例的独特价值在于**“字典 独立块 跳表”**三者的组合。核心设计字典做历史、块做边界、跳表做索引原文档明确指出本示例的两个技术点Dictionary based compression for homogeneous files对同质化文件如日志、结构化记录、代码片段集合等使用字典压缩Random access to compressed blocks对压缩块进行随机访问。实现思路examples/dictionaryRandomAccess.c 中test_compress()L57-L105是从字典文件中读出 1 KiB 内容将其作为每个块的压缩历史。压缩每个块之前调用LZ4_loadDict()把字典装载进流状态——由于LZ4_loadDict()本身会触发一次 reset见 lib/lz4.h 中“LZ4_loadDict() triggers a reset, so any previous data will be forgotten”因此块与块之间互不依赖只共同依赖字典。这样既保证了任意一块都能脱离其他块独立解压随机访问的前提又借助字典提供了可复用的匹配历史维持了压缩比。原文档给出的压缩流程示意图Dictionary | v --------- | Block#1 | -------- | v {Out#1} Dictionary | v --------- | Block#2 | -------- | v {Out#2}可以看到每个块都以同一份字典作为“前方的历史窗口”彼此互不相干。这与 examples/blockStreaming_doubleBuffer.md 中块之间依赖前一数据块的连续流式压缩有本质区别——后者块间有依赖、压缩比更高但无法随机访问前者块间无依赖、可随机访问且仍保留字典带来的压缩收益。文件格式魔数 压缩块 跳表整个文件布局由原文档给出--------------- ---------------------- --------------- | TEST | Block#1 | ... | Block#N | 4 | Offset#1 | ... | Offset#N | N1 | --------------- ---------------------- ---------------各字段含义如下结合源码逐项确认字段内容说明TEST4 字节魔数源码中定义为const char kTestMagic[] { T, E, S, T }examples/dictionaryRandomAccess.cBlock#1 .. Block#NN 个压缩块每块对应BLOCK_BYTES1024字节未压缩数据最后一块可能不足 1024 字节Offset#1 .. Offset#N共 N1 个跳表每个 4 字节整数记录“写完某个块之后”累计写入的总字节数含魔数N1最后 4 字节块数存储的是“偏移量个数”即块数 N 1关于跳表原文档的表述是“If there are N blocks, then just before the last 4 bytes is N1 4 byte integers containing the offsets at the beginning and end of each block. Let Offset#K be the total number of bytes written after writing out Block#Kincludingthe magic bytes for simplicity.” 结合源码examples/dictionaryRandomAccess.c可进一步明确偏移数组的生成规则offsets[0] sizeof(kTestMagic)即 4——写完整块魔数后、写第一个块之前的位置每写完第 k 个块offsets[k] offsets[k-1] cmpBytes因此第 k 个压缩块的大小可由offsets[k1] - offsets[k]直接算出文件尾部依次写入offsets[0..N]共 N1 个整数再写入计数N1。解压端正是利用这一性质来定位块numOffsets读到的最后 4 字节等于 N1向前跳过4 × (numOffsets1)字节即可定位跳表起点examples/dictionaryRandomAccess.c。关于魔数源码注释坦诚指出“This is not a great magic number because it is a common word in ASCII. However, it is important to have some versioning system in your format.”——即示例刻意选择TEST以强调自定义格式必须包含版本标识这一工程要点读者在生产设计中应选用更不易误判的魔数。压缩端实现每块重载字典逐块产出独立块test_compress()examples/dictionaryRandomAccess.c的完整流程在栈上声明LZ4_stream_t并用LZ4_initStream()初始化LZ4_initStream()是 v1.9.0 提供的栈上流上下文初始化入口见 lib/lz4.h写入魔数TEST并把offsets[0]置为 4循环读取输入文件每次最多BLOCK_BYTES1024字节读到 0 字节即结束每次循环先调用LZ4_loadDict(lz4Stream, dict, dictSize)重载字典这会重置流状态从而切断与前一块数据的依赖见 lib/lz4.h用LZ4_compress_fast_continue()压缩当前块输出缓冲为LZ4_COMPRESSBOUND(BLOCK_BYTES)大小的栈数组acceleration 参数取 1examples/dictionaryRandomAccess.c把压缩块写入输出文件并更新offsets数组全部块写完后再写跳表与块数。LZ4_COMPRESSBOUND的定义在 lib/lz4.h#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) ((isize)/255) 16)对BLOCK_BYTES 1024上界为1024 1024/255 16 1044字节LZ4_compress_fast_continue()在此容量下保证压缩必然成功见 lib/lz4.h 中 “If dstCapacity LZ4_compressBound(srcSize), compression is guaranteed to succeed”。LZ4_compress_fast_continue()的块语义在这里尤其关键lib/lz4.h“Each invocation to LZ4_compress_fast_continue() generates a new block. Each block has precise boundaries. Each block must be decompressed separately.” 正是这种“一次调用一个独立块”的边界模型配合每次调用前重载字典才让示例的随机访问成为可能。需要说明的是LZ4_compress_fast_continue()在一般流式用法中要求“前 64KB 源数据保持可用”Note 2而本例通过每次LZ4_loadDict()重置状态绕过了对前一数据块的依赖。两个防御性检查值得留意压缩失败cmpBytes 0时以退出码 1 终止块数超过MAX_BLOCKS1024时以退出码 2 终止。由于每块 1 KiB该示例整体能处理的输入上限约为 1 MiB——这是示例为简单实现而设置的硬限制examples/dictionaryRandomAccess.c 中注释 “For simplicity of implementation”。解压端实现只解压目标区间跨越的块test_decompress()examples/dictionaryRandomAccess.c演示随机访问的全部步骤与原文档“How the decompression works”一节一一对应参数换算offset、length是相对未压缩输入文件的字节区间。currentBlock offset / BLOCK_BYTESendBlock ((offset length - 1) / BLOCK_BYTES) 1即目标区间跨越的所有块length 0时直接返回L116-L123校验魔数读 4 字节并与kTestMagic比对不一致退出码 2L126-L131读取跳表seek到文件末尾-4处读出numOffsets若numOffsets endBlock跳表不完整/文件损坏退出码 3再seek到-4*(numOffsets1)处读入前endBlock1个偏移量L134-L145定位首块seek到offsets[currentBlock]并将offset折算为块内偏移offset % BLOCK_BYTESL147-L148逐块解压并裁剪对每个目标块cmpBytes offsets[currentBlock1] - offsets[currentBlock]得出压缩块大小读出后调用LZ4_setStreamDecode()装载字典、LZ4_decompress_safe_continue()解压进 1024 字节的decBuf再用MIN(length, decBytes - offset)裁剪出需要的部分写入输出随后offset 0、length - blockLength进入下一块L151-L174。解压端的状态管理值得注意LZ4_streamDecode_t在栈上声明解压每个块前都调用LZ4_setStreamDecode(lz4StreamDecode, dict, dictSize)重设字典返回 1 表示成功见 lib/lz4.h。LZ4_decompress_safe_continue()一次只接受一个完整的块其返回值是实际解压字节数负数表示输入损坏或目标缓冲不足lib/lz4.h示例中decBuf容量恰为BLOCK_BYTES与压缩端块大小严格对应这也是“同步模式”流式解码成立的前提。从算法角度看endBlock只读取跨越目标区间的块因此随机访问的代价正比于目标区间的大小而非整个文件的大小——这正是“随机访问”二字的含义它不需要解压第 1 块到第 N 块的全部数据。主程序与命令行用法main()examples/dictionaryRandomAccess.c要求 4 个命令行参数Usage: program input dictionary offset length各参数含义参数含义示例值来自 examples/Makefileinput待压缩的原始文件路径Makefile/.gitignoredictionary字典文件路径程序只读取前DICTIONARY_BYTES1024 字节同上用自身做字典offset希望读取的字节区间起点相对未压缩输入1100/0length希望读取的字节区间长度1400/32程序依次执行三个阶段压缩input → input.lz4s-1024后缀中的1024是BLOCK_BYTES的取值文件名命名见 examples/dictionaryRandomAccess.c解压input.lz4s-1024 → input.lz4s-1024.dec只产出[offset, offsetlength)区间对应的数据校验将原始文件从offset处 seek 后与.dec文件逐块memcmp比对compare()examples/dictionaryRandomAccess.c一致输出verify : OK否则输出verify : NG。运行示例仓库根目录下构建后执行cd examples make ./dictionaryRandomAccess Makefile Makefile 1100 1400 ./dictionaryRandomAccess .gitignore .gitignore 0 32其中第一条会压缩Makefile随后只解压第 1100 到 2499 字节的区间并与原文件校验第二条则验证从文件头开始的 32 字节。这两条命令也被 examples/Makefile 的make test目标自动执行属于官方回归验证的一部分。为什么可行LZ4 字典机制的底层原理要理解本示例为何成立需要知道 LZ4 块格式的压缩模型。根据 doc/lz4_Block_format.mdLZ4 是 LZ77 型、面向字节的固定编码格式压缩核心是“在过去 64KB 窗口内检测重复数据”匹配通过offset2 字节、小端与matchlength编码。它没有熵编码后端也没有帧层——帧层由外部系统负责这正是本示例自行设计容器布局的依据。在 lib/lz4.h 中LZ4_loadDict()的文档进一步说明了字典机制的关键约束“The same dictionary will have to be loaded on decompression side for successful decoding.”压缩与解压必须使用同一份字典字典在压缩/解压期间必须保持可访问且不被修改“Loading a size of 0 is allowed, and is the same as reset.”加载 0 字节等同于重置“note: only the last 64 KB are loaded”无论传入多大字典只有最后 64KB 会被装载进历史窗口——这与 LZ4 的 64KB 匹配窗口doc/lz4_Block_format.md 中 offset 最大 65535一致。本示例字典固定为 1 KiB远小于窗口上限因此 1024 字节全部生效。这一“只用最后 64KB”的语义在仓库测试中也有验证tests/test-lz4-dict.sh 遍历了 0、1、4、128、32767、65536、131073 等多种字典大小用不同大小的字典压缩同一数据均能正确解压且测试同时断言使用字典的压缩结果小于不使用字典的结果“Test Passed: dictionary is effective.”证明字典在小数据压缩中的收益是真实可测的。回到本示例LZ4_loadDict()每次调用都触发 reset 并装载同一份字典因此每块在逻辑上等价于“以字典为前置历史的独立块”。解压端LZ4_setStreamDecode()以同样方式装载字典LZ4_decompress_safe_continue()就能在字典窗口内解析出全部匹配引用。字典同时扮演了“共同的历史前缀”既维系压缩比又让块之间彻底解耦——这就是随机访问得以实现的全部秘密。限制与改进方向作为教学示例dictionaryRandomAccess存在若干明确限制理解这些限制有助于在生产场景中设计替代方案格式非标准、平台相关int直接落盘、无字节序转换、无校验和跨平台/跨进程不可移植生产环境应优先使用 lib/lz4frame.h 的帧格式或自行加入字节序规范化、版本字段与校验规模上限BLOCK_BYTES、DICTIONARY_BYTES、MAX_BLOCKS均为编译期常量1 KiB / 1 KiB / 1024 块最大覆盖约 1 MiB 输入块大小固定意味着无法按内容自适应切分字典依赖外部文件解压必须拿到与压缩时完全一致的字典文件含相同前缀丢失字典则全部数据不可恢复压缩比受限于字典字典仅 1 KiB若数据中存在字典覆盖不到的重复模式压缩比会打折扣。文档级建议lib/lz4.h指出当对字典效率拿不准时可考虑使用 Zstandard 的 Dictionary Builder 生成更优字典。若要在生产环境中落地“字典 随机访问”可将示例中的三要素独立块、64KB 窗口语义、跳表索引与 lz4frame 的块元数据、CRC 校验、端序转换结合并按需引入变长块与更大的偏移表。延伸阅读examples/dictionaryRandomAccess.md本文对应的原始文档压缩/解压流程与文件布局图examples/dictionaryRandomAccess.c完整可编译实现examples/streaming_api_basics.mdLZ4 流式 API 基础理解continue系列函数的前提examples/blockStreaming_doubleBuffer.md 与 examples/blockStreaming_lineByLine.md块间有依赖的连续流式方案对比lib/lz4.hLZ4_loadDict、LZ4_compress_fast_continue、LZ4_setStreamDecode、LZ4_decompress_safe_continue、LZ4_COMPRESSBOUND的权威说明doc/lz4_Block_format.mdLZ4 块格式规范token、字面量、offset、matchlength 与 64KB 窗口tests/test-lz4-dict.shCLI 层字典压缩的有效性与 64KB 尾部语义回归测试examples/Makefilemake构建与make test中该示例的自动化调用。【免费下载链接】lz4Extremely Fast Compression algorithm项目地址: https://gitcode.com/GitHub_Trending/lz/lz4创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考