从单独压缩的块创建 gzip 流

Creating a gzip stream from separately compressed chunks

我希望能够使用并发 CPU 线程生成 gzip (.gz) 文件。即,我将使用单独初始化的 z_stream 记录从输入文件中压缩单独的块。

在经典的单线程操作中,zlib 的 inflate() 函数应该可以读取生成的文件。

这可能吗?即使它需要定制的 zlib 代码?唯一的要求是当前存在的 zlib 的膨胀代码可以处理它。

更新

pigz 源代码演示了它是如何工作的。它使用一些复杂的优化来在块之间共享字典,从而保持最佳压缩率。如果使用更新的 zlib 版本,它会进一步处理位打包。

但是,我喜欢了解如何自己动手,保持简单,不使用 pigz 使用的优化。

虽然许多人认为源代码是最终文档 (Ed Post, anyone?),但我宁愿用通俗易懂的语言对其进行解释以避免误解。 (虽然文档实际上很好地描述了发生的事情,但他们并没有很好地解释自己需要做什么。)

通过浏览代码,到目前为止我明白了:

看来人们只是使用 deflate(..., Z_SYNC_FLUSH) 而不是 Z_FINISH 创建每个压缩块。然而, deflateEnd() 给出了一个错误,不确定是否可以忽略。并且需要手动计算所有块的最终校验和,尽管我想知道如何在最后添加校验和。还有一个相当复杂的 put_trailer() 函数用于编写 gzip header - 我想知道是否也可以通过 zlib 自己的代码处理简单的情况?

如有任何澄清,我们将不胜感激。

此外,我意识到我应该包括询问以同样的方式编写 zlib 流,以便将 multithreaded-compressed 文件写入 zip 存档。我怀疑,由于缺少更复杂的 gzip header.

,可能会有更多的简化

当然..

http://zlib.net/pigz/

A parallel implementation of gzip for modern multi-processor, multi-core machines

答案就在你的问题中。每个线程都有自己的 deflate 实例来生成原始压缩数据(参见 deflateInit2()),它压缩提供给它的数据块,以 Z_SYNC_FLUSH 而不是 Z_FINISH 结尾.除了最后一个数据块,它以 Z_FINISH 结尾。无论哪种方式,这都会在字节边界上结束每个压缩数据流。确保从 deflate() 中获取所有生成的数据。然后你可以连接所有的压缩数据流。 (以正确的顺序!)在其前面加上您自己生成的 gzip header。这样做很简单(参见 RFC 1952)。如果您不需要 header 中包含的任何附加信息(例如文件名、修改日期),它可以只是一个恒定的 10 字节序列。 gzip header 并不复杂。

您还可以计算同一线程或不同线程中每个未压缩块的 CRC-32,并使用 crc32_combine() 组合这些 CRC-32。 gzip 预告片需要它。

在写入所有压缩流后,以 Z_FINISH 结束的压缩流结束,您附加 gzip 尾部。所有这一切都是 four-byte CRC-32 和总未压缩长度的低四个字节,都是 little-endian 顺序。总共八个字节。

在每个线程中,您可以在完成每个块时使用 deflateEnd(),或者如果您要为更多块重用线程,请使用 deflateReset()。我在 pigz 中发现,在处理多个块时,让线程保持打开状态并在其中打开 deflate 个实例会更有效率。在关闭线程之前,只需确保对线程处理的最后一个块使用 deflateEnd()。是的,来自 deflateEnd() 的错误可以忽略。只需确保您 运行 deflate() 直到 avail_out 不为零以获取所有压缩数据。

这样做,每个线程都会压缩其块,而不引用任何其他未压缩的数据,在这种情况下,此类引用通常会在串行执行时提高压缩率。如果你想更高级,你可以为每个线程提供未压缩数据块进行压缩,前一个块的最后 32K 为压缩器提供历史记录。您使用 deflateSetDictionary().

执行此操作

更高级的是,您可以通过有时使用 Z_PARTIAL_FLUSH 直到到达字节边界来减少压缩流之间插入的字节数。有关详细信息,请参阅 pigz。

更高级但更慢的是,您可以在位级别而不是字节级别附加压缩流。这将需要将压缩流的每个字节移位两次以构建新的移位流。每八个前面的压缩流中至少有七个。这消除了压缩流之间插入的所有额外位。

可以用完全相同的方式生成 zlib 流,使用 adler32_combine() 作为校验和。

你关于 zlib 的问题暗示了一种混淆。 zip 格式不使用 zlib header 和预告片。 zip has its own structure,其中嵌入了原始压缩流。您也可以对那些原始压缩流使用上述方法。