【问题标题】:Why is a different zlib window bits value required for extraction, compared with compression?与压缩相比,为什么提取需要不同的 zlib 窗口位值?
【发布时间】: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_LEVEL1,以提供最佳速度。然后我在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+1631 用于窗口位,8 用于内存级别,Z_DEFAULT_STRATEGY 用于策略。

以前的inflateInit() 调用不起作用;相反,我必须使用inflateInit2() 并指定修改后的窗口位值:

int ret = inflateInit2(this->z_stream_ptr, ZLIB_TEST_COMPRESSION_WINDOW_BITS + 16);

在这种情况下,窗口位值不是31 调用中的deflateInit2(),而是15+3247

如果我使用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]

这里,-3Z_DATA_ERROR 相同。

根据文档,使用31deflateInit2() 应该写一个gzip 标题和尾部。因此,接下来的inflateInit2() 调用中的31 应该能够提取标头信息。

为什么修改后的值47 有效,而31 无效?

我的测试项目与zlib 站点上的示例代码大部分相似,但提取/膨胀代码除外,它一次膨胀一个z_stream 块并解析输出以查找换行符。

只有在请求提取数据的新缓冲区时才运行 inflate() 有什么特别之处吗?比如在 inflate() 调用之间缺少标头信息——而不是像在 @ 中那样一次性运行整个提取过程987654367@示例代码?

我更大的调试问题是寻找一种可靠的方法来仅在请求时提取一大块zlib-压缩数据,这样我就可以一次提取一行数据,而不是获取整个提取的文件。我处理 zlib 格式参数的方式似乎让我很困惑,但我不知道为什么或如何解决这个问题。

【问题讨论】:

    标签: c++ c zlib compression


    【解决方案1】:

    deflateInit()inflateInit(),以及 deflateInit2()inflateInit2()windowBits 在 0..15 中都处理 zlib 包装的放气数据。 (见RFC 1950RFC 1951。)

    deflateInit2()inflateInit2() 在 -1..-15 中带有负值 windowBits 处理没有标题或尾部的原始放气数据。 deflateInit2()inflateInit2() 与 16..31 中的 windowBits,即 16 添加到 0..15,处理 gzip 包装的放气数据 (RFC 1952)。 inflateInit2() 和 32..47 中的 windowBits(32 添加到 0..15)将自动检测 gzip 或 zlib 标头(但不是原始 deflate 数据),并相应地解压缩。

    为什么修改后的值 47 有效,而 31 无效?

    31 确实有效。我没有尝试查看您的代码来调试它。

    只有在新的时候才运行 inflate() 有什么特别的 要求提取数据的缓冲区——比如标题信息 在 inflate() 调用之间缺失——而不是运行整个 一次提取,就像在 zlib 示例代码中一样?

    我不明白你在这里问什么。也许一个更明确的例子会有所帮助。 inflate() 的全部意义在于一次解压一个块。

    【讨论】:

    • 很遗憾,31 不起作用。我所要做的就是调整那个值,重新编译并运行第二个测试来确认。
    • 那你的程序有bug,假设你同时使用31进行压缩和解压。
    • 不幸的是,除了以不同方式处理输出缓冲区之外,它与压缩/提取例程没有任何关系(或者不应该,它只是字节的缓冲区),我正在使用来自@的示例代码987654338@ 对数据进行压缩和膨胀的站点,并调整窗口位以查看哪些设置有效,哪些设置无效。
    • 我只是引用 zlib 文档所说的应该有效的内容。
    • 如果我有一些关于究竟要调试什么的指示,也许这将有助于解决问题?我使用zlib inflate 例程的方式与文档中描述的方式相同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-03
    • 1970-01-01
    • 2020-07-21
    • 1970-01-01
    • 2014-06-29
    • 1970-01-01
    相关资源
    最近更新 更多