DEFLATE(zlib,gzip)格式使用的编码动态霍夫曼树的最大大小是多少?

时间:2014-08-21 16:18:09

标签: zlib huffman-code deflate

使用https://www.ietf.org/rfc/rfc1951.txt的动态霍夫曼码(BTYPE = 10)的压缩描述了压缩期间使用的动态霍夫曼树的编码。在DEFLATE比特流中可能出现的这种编码霍夫曼树表示的最大大小(以位为单位)是多少?使用外部参考支持特定数字的额外点; - )。

这是理解DEFLATE属性的理论问题。但当然它有实际的应用,例如,“应该使用多大的缓冲区来保证解码霍夫曼树?”

3 个答案:

答案 0 :(得分:7)

动态块头的长度上限可以从您已经提供的引用中轻松计算出来。从RFC 1951,第3.2.7节我们可以将这些位加起来:

3 + 5 + 5 + 4 + 19 * 3 +(286 + 30)* 7 = 2286位= 285.75字节。

(详见下面的计算说明。)

在实践中,你永远不会看到一个长达286个字节。更典型的长度是60到90个字节。

以下是来自linux的gzip压缩源代码linux-3.1.6.tar.gz的动态标头块长度的直方图:

dynamic block length histogram

他们看起来都不一样。这是Archive.pax.gz(应用程序分发)的另一个:

another dynamic block length histogram

双峰形状可能是可执行文件与文本。可执行文件对所有文字字节值进行编码,从而产生更大的动态标头来描述所有这些值的代码。


计算说明:

我故意没有为符号16,17或18添加可能的额外位,因为使用任何这些代码(包括它们的额外位)会减少标头的长度,而不是增加它。 16符号将用9位替换21到42位,17符号用10位替换21到70位,18符号用14位替换77到966位(其中所有符号都假定为7位)

即使没有使用16,17和18,仍然有19个初始代码长度,因为它们是先存储的。

我将文字/长度代码长度限制为286,距离代码长度限制为30,因为兼容的充气机会拒绝超过该值的值。

2286是可能的最低上限,因为在deflate格式中没有约束将头构造为最佳。可以构造代码长度代码,例如,长度为4,5,8和9都是7位代码,然后只使用长度列表中的那些来构造完整的文字/长度和距离码。代码长度代码也必须完整,但这可以通过将较短的代码分配给未使用的长度来实现。

简而言之,可以构造一个长度为2286位的完全有效的动态块头。事实上,这是一个(有很多方法可以做到这一点):

ed fd 01 e0 38 70 1c 28 a7 fc 7e bf df ef f7 fb
fd 7e bf df ef f7 fb fd 7e bf df ef f7 fb fd 7e
bf df ef f7 fb fd 7e bf df ef f7 fb fd 7e bf df
ef f7 fb fd 7e bf df ef f7 fb fd 7e bf df ef f7
fb fd 7e bf df ef f7 fb fd 7e bf df ef f7 fb fd
7e bf df ef f7 fb fd 7e bf df ef f7 fb fd 7e bf
df ef f7 fb fd 7e bf df ef f7 fb fd 7e bf df ef
f7 fb fd 7e bf df ef f7 fb fd 7e bf df ef f7 fb
fd 7e bf df ef f7 fb fd 7e bf df ef f7 fb fd 7e
bf df ef f7 fb fd 7e bf df ef f7 fb fd 7e bf df
ef f7 fb fd 7e bf df ef f7 fb fd 7e bf df ef f7
fb fd 7e bf df ef f7 fb fd 7e bf df ef f7 fb fd
7e bf df ef f7 fb fd 7e bf df ef f7 fb fd 7e ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff f9 7c bf df ef f7 fb fd 7e bf df ef
f7 fb fd 7e bf df ef f7 fb fd 7e bf df ef 23

这是一个以十六进制表示的有效且完整的deflate流。它由一个动态块组成,标记为最后一个块,带有2286位动态头和9位块结束码,总共2295位,最接近287个字节。它解压缩到零字节,没有错误。

答案 1 :(得分:2)

答案是 5 + 5 + 4 + 19 * 3 +(286 * 7)+(30 * 7)= 2283位,不包括2位BTYPE字段。

Zlib将reject文字/长度符号286,287和距离符号30,31。因此,286和30是总和中的正确计数,而不是288和32。

答案 2 :(得分:1)

我想详细说明Mark上面的计算。希望它具有可读性:

2(块类型,本身不是霍夫曼树部分)+ 5(来自规范的HLIT)+5(HDIST)+4(HCLEN)+ 19(最大HCLEN)* 3(每个HCLEN的位数)+(288 + 32)*(7 + 7)= 4551位

与马克公式的差异:

  • 我使用288作为文字/长度字母大小(2个代码不能用作符号,这就是Mark使用286的原因,但是2个代码可以'也可以用于距离,但是他使用的是32,而不是30。当无效代码应该被计算时,我不会理解约束,所以当我没有时,我会保守并始终统计它们。)

  • 在Mark的公式中,每个代码长度最多7位,我的(7 + 7)。这是因为某些长度代码(具体值为18)可以从流中读取7个额外位。

然而,考虑到最后一点,读取额外7位的代码具有特殊语义 - 对数字(11+)长度代码重复零编码。换句话说,如果读取7 + 7代码,它将替换至少11 * 7的文字代码长度代码。因此,我对4551位的估计被夸大了,Mark的估计提供了更好的上限。

但它仍然保守 - 霍夫曼代码具有所有最大长度令人难以置信。如果分布均匀,霍夫曼的长度应该是平均的。 OTOH,显然有可能分配一个符号将霍夫曼长度为1,而其余符号将围绕最大长度徘徊。因此,确切的上限不应低于Mark给出的保守。所以,如果有人可以提供这个确切的上限,那就太酷了。否则,Mark的界限已经非常适合缓冲的实际应用。