【发布时间】:2015-03-12 07:19:12
【问题描述】:
我正在尝试调试一些使用zlib 1.2.8 的代码的问题。问题是这个较大的项目可以制作档案,但在尝试提取档案时遇到Z_DATA_ERROR 标头问题。
为此,我用 C++ 编写了一个小测试程序,它压缩(“deflates”)指定的常规文件,将压缩数据写入第二个常规文件,然后提取(“膨胀”)到第三个常规文件,一次一行。然后我diff 第一个和第三个文件以确保我得到相同的字节。
作为参考,这个测试项目位于:https://github.com/alexpreynolds/zlib-test,并在 Clang 下编译(也应该在 GNU GCC 下编译)。
我更大的问题是如何在我的大型项目中正确处理标题数据。
在我的第一个测试场景中,我可以使用以下代码设置压缩机制:
z_error = deflateInit(this->z_stream_ptr, ZLIB_TEST_COMPRESSION_LEVEL);
这里,ZLIB_TEST_COMPRESSION_LEVEL 是 1,以提供最佳速度。然后我在z_stream 指针上运行deflate(),直到压缩后没有任何剩余。
要提取这些字节,我可以使用inflateInit():
int ret = inflateInit(this->z_stream_ptr);
那么在这种情况下,标题格式是什么?
在我的第二个测试场景中,我这样设置放气机制:
z_error = deflateInit2(this->z_stream_ptr,
ZLIB_TEST_COMPRESSION_LEVEL,
ZLIB_TEST_COMPRESSION_METHOD,
ZLIB_TEST_COMPRESSION_WINDOW_BITS,
ZLIB_TEST_COMPRESSION_MEM_LEVEL,
ZLIB_TEST_COMPRESSION_STRATEGY);
这些 deflate 常量分别是,1 用于级别,Z_DEFLATED 用于方法,15+16 或 31 用于窗口位,8 用于内存级别,Z_DEFAULT_STRATEGY 用于策略。
以前的inflateInit() 调用不起作用;相反,我必须使用inflateInit2() 并指定修改后的窗口位值:
int ret = inflateInit2(this->z_stream_ptr, ZLIB_TEST_COMPRESSION_WINDOW_BITS + 16);
在这种情况下,窗口位值不是31 调用中的deflateInit2(),而是15+32 或47。
如果我使用31(或除47 之外的任何其他值),那么我会在随后的inflate() 调用中得到Z_DATA_ERROR。也就是说,如果我对inflateInit2() 调用使用相同的窗口位:
int ret = inflateInit2(this->z_stream_ptr, ZLIB_TEST_COMPRESSION_WINDOW_BITS);
然后我在尝试inflate() 时收到以下错误:
Error: inflate to stream failed [-3]
这里,-3 与 Z_DATA_ERROR 相同。
根据文档,使用31 和deflateInit2() 应该写一个gzip 标题和尾部。因此,接下来的inflateInit2() 调用中的31 应该能够提取标头信息。
为什么修改后的值47 有效,而31 无效?
我的测试项目与zlib 站点上的示例代码大部分相似,但提取/膨胀代码除外,它一次膨胀一个z_stream 块并解析输出以查找换行符。
只有在请求提取数据的新缓冲区时才运行 inflate() 有什么特别之处吗?比如在 inflate() 调用之间缺少标头信息——而不是像在 @ 中那样一次性运行整个提取过程987654367@示例代码?
我更大的调试问题是寻找一种可靠的方法来仅在请求时提取一大块zlib-压缩数据,这样我就可以一次提取一行数据,而不是获取整个提取的文件。我处理 zlib 格式参数的方式似乎让我很困惑,但我不知道为什么或如何解决这个问题。
【问题讨论】:
标签: c++ c zlib compression