如何找到 DEFLATE 块的结尾

How to find the end of a DEFLATE block

出于自学目的,我正在尝试创建一个程序,将 png 文件转换为 RGBA 值数组。但是,我在解码使用 deflate 格式使用 zlib 编码的 IDAT 部分时遇到问题。我遇到的问题是我不知道如何找到压缩块的末尾。文档中唯一显示长度的地方只是针对解压缩的块,但是对于具有默认霍夫曼表的块和提供的霍夫曼表,似乎没有办法找出块的结束位置。我应该如何找到 deflate 格式的块的结尾。

根据 PNG Deflate/Inflate Compression 文档

The compressed data within the zlib datastream is stored as a series of blocks, each of which can represent raw (uncompressed) data, LZ77-compressed data encoded with fixed Huffman codes, or LZ77-compressed data encoded with custom Huffman codes. A marker bit in the final block identifies it as the last block, allowing the decoder to recognize the end of the compressed datastream. Further details on the compression algorithm and the encoding are given in the deflate specification [RFC-1951].

...

In a PNG file, the concatenation of the contents of all the IDAT chunks makes up a zlib datastream as specified above. This datastream decompresses to filtered image data as described elsewhere in this document.

It is important to emphasize that the boundaries between IDAT chunks are arbitrary and can fall anywhere in the zlib datastream. There is not necessarily any correlation between IDAT chunk boundaries and deflate block boundaries or any other feature of the zlib data. For example, it is entirely possible for the terminating zlib check value to be split across IDAT chunks.*"

并且根据 RFC 1951:“DEFLATE 压缩数据格式规范版本 1.3”:

A compressed data set consists of a series of blocks, corresponding to successive blocks of input data. The block sizes are arbitrary, except that non-compressible blocks are limited to 65,535 bytes.

Each block is compressed using a combination of the LZ77 algorithm and Huffman coding. The Huffman trees for each block are independent of those for previous or subsequent blocks; the LZ77 algorithm may use a reference to a duplicated string occurring in a previous block, up to 32K input bytes before.

Each block consists of two parts: a pair of Huffman code trees that describe the representation of the compressed data part, and a compressed data part. (The Huffman trees themselves are compressed using Huffman encoding.) The compressed data consists of a series of elements of two types: literal bytes (of strings that have not been detected as duplicated within the previous 32K input bytes), and pointers to duplicated strings, where a pointer is represented as a pair <length, backward distance>. The representation used in the "deflate" format limits distances to 32K bytes and lengths to 258 bytes, but does not limit the size of a block, except for uncompressible blocks, which are limited as noted above.

Each type of value (literals, distances, and lengths) in the compressed data is represented using a Huffman code, using one code tree for literals and lengths and a separate code tree for distances. The code trees for each block appear in a compact form just before the compressed data for that block.

因此,要确定给定块的结尾,您必须解析块的霍夫曼代码以了解压缩数据中每个元素的位置和类型,然后您可以根据需要处理每个元素,直到您找到块中最后一个元素的结尾。第 3.2 节详细介绍了如何格式化压缩块的技术细节,特别是 Section 3.2.3:

3.2.3. Details of block format

Each block of compressed data begins with 3 header bits containing the following data:

first bit       BFINAL
next 2 bits     BTYPE

Note that the header bits do not necessarily begin on a byte boundary, since a block does not necessarily occupy an integral number of bytes.

BFINAL is set if and only if this is the last block of the data set.

BTYPE specifies how the data are compressed, as follows:

00 - no compression
01 - compressed with fixed Huffman codes
10 - compressed with dynamic Huffman codes
11 - reserved (error)

The only difference between the two compressed cases is how the Huffman codes for the literal/length and distance alphabets are defined.

In all cases, the decoding algorithm for the actual data is as follows:

do
    read block header from input stream.
    if stored with no compression
        skip any remaining bits in current partially
            processed byte
        read LEN and NLEN (see next section)
        copy LEN bytes of data to output
    otherwise
        if compressed with dynamic Huffman codes
            read representation of code trees (see
                subsection below)
        loop (until end of block code recognized)
            decode literal/length value from input stream
            if value < 256
                copy value (literal byte) to output stream
            otherwise
                if value = end of block (256)
                    break from loop
                otherwise (value = 257..285)
                    decode distance from input stream

                    move backwards distance bytes in the output
                    stream, and copy length bytes from this
                    position to the output stream.
        end loop
while not last block

Note that a duplicated string reference may refer to a string in a previous block; i.e., the backward distance may cross one or more block boundaries. However a distance cannot refer past the beginning of the output stream. (An application using a preset dictionary might discard part of the output stream; a distance can refer to that part of the output stream anyway) Note also that the referenced string may overlap the current position; for example, if the last 2 bytes decoded have values X and Y, a string reference with <length = 5, distance = 2> adds X,Y,X,Y,X to the output stream.