Zlib在C中解压缩具有未知压缩长度的字节

Posted

技术标签:

【中文标题】Zlib在C中解压缩具有未知压缩长度的字节【英文标题】:Zlib decompress bytes with unknown compressed length in C 【发布时间】:2020-10-18 02:55:57 【问题描述】:

我正在尝试在没有任何外部库的情况下编写自己的 png 阅读器。我需要使用 Zlib 来解压 png 的 IDAT 块。我已经设法在 python 中使用zlib.decompress() 做到这一点,我正试图在 C 中复制它。我正在阅读 zlib 的文档并找到 uncompress(),但是它需要一个我不知道的目标长度。

我可以将目标设置得比 png 可能的大得多,但这似乎是一种逃避,如果我有一张非常大的图片,这会破坏我的程序。但是,我发现了一个可以多次使用的函数inflate()。如果我能做到这一点,如果每次通话都需要,我可以realloc() 内存。但是我不太了解它的文档,也没有找到很多这类事情的例子。谁能提供一些代码或帮助我指出正确的方向?

【问题讨论】:

如您所料,zlib 函数是面向 stream 的。因此,您使用循环和 realloc 来扩展解压缩数据缓冲区的长度,一次一个块(例如)在每次 decompress [deflate] 调用之前,您调用 realloc 并增加长度。您在z_stream 结构中设置大小,当缓冲区已满[或没有更多输入数据] 时,函数将停止。您循环直到所有数据都被解压缩。 zlib.h 文件有相当完整的文档。例如,将源下载到使用zlib(例如gzip)的程序中,zlib源有示例 请注意,使用realloc 将需要每次复制整个现有缓冲区,因此不应过于频繁地调用它(我会说不超过一次 4096 字节)。如果你想要更好的性能,你应该增加这个数量,或者更好:根本不使用realloc,而是处理几个缓冲区。不过,我暂时不会担心这个。一旦工作正常,您可以随时进行优化。 @AndreasWenzel 有一个恒定的realloc() 频率是非常低效的,例如“每 4096 字节一次”。然后每个字节将被复制多次。相反,如果您将每个 realloc() 的内存大小 加倍,那么您可以证明这些字节平均只被复制一次。 @MarkAdler:是的,我同意自适应缓冲区大小比恒定缓冲区更好,特别是当文件大小可能很大时。我也同意您提出的将每次分配的缓冲区大小加倍的算法可能是最好的,因为该算法平均不会多次复制内存。 @MarkAdler:据我所知,最有效的解决方案是根本不调用realloc,而是使用指向固定长度内存缓冲区的指针的数组或链表.这样,可以根据需要分配新的缓冲区,并且不必复制内存。但是,如果程序员的任务需要在之后合并这些缓冲区,以便使用它们,那么所有内存缓冲区都必须复制一次。在这种情况下,我的解决方案与realloc 解决方案相比没有优势。 【参考方案1】:

确实知道目的地的长度。确切地。 PNG 标头信息告诉您有多少行、多少列以及每个像素有多少字节。将其全部相乘,每行添加一个字节进行过滤,您就会得到答案。

分配那个数量的内存,然后解压到那个。

请注意,可以有多个 IDAT 块,但它们组合在一起包含一个 zlib 流。

【讨论】:

嗯,这是一些有用的信息,我不知道你能得到这样的长度。而且您不是 Zlib 库的创建者吗?你是我的英雄之一,伙计。 =)

以上是关于Zlib在C中解压缩具有未知压缩长度的字节的主要内容,如果未能解决你的问题,请参考以下文章

解压缩具有可变长度的未知序列化格式

在C#中解压缩字节数组

如何在php中解压缩zip文件[重复]

如何在 .NET 中解压缩使用 Lempel-Ziv Haruyasu 算法压缩的字节?

如何在 ruby​​ 中解压缩大于 64 位的数字?

zlib 的“解压缩”是不是保留数据的原始字节序,还是进行字节序转换?