【问题标题】:What is the maximum size of encoded dynamic Huffman tree as used by DEFLATE (zlib, gzip) format?DEFLATE(zlib,gzip)格式使用的编码动态霍夫曼树的最大大小是多少?
【发布时间】:2014-10-15 08:55:24
【问题描述】:

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

这是一个理论问题,可以更好地理解 DEFLATE 属性。但当然它也有实际应用,例如,“应该使用多大的缓冲区来保证解码 Huffman 树?”

【问题讨论】:

    标签: zlib huffman-code deflate


    【解决方案1】:

    动态块头长度的上限可以很容易地从您已经提供的参考中计算出来。从 RFC 1951 的第 3.2.7 节我们可以将这些位相加:

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

    (详情请参阅下面的计算说明。)

    实际上,您永远不会看到接近 286 字节的长度。更典型的长度是 60 到 90 个字节。

    这是来自 gzip 压缩的 linux 源代码分布的动态标头块长度的直方图,linux-3.1.6.tar.gz

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

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


    计算笔记:

    我故意不为符号 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
    

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

    【讨论】:

    • 感谢您的光临-我想知道是否有可能以某种方式召唤您回答这个问题 ;-)。很抱歉问题表述中的混淆 - 我问的不是 DEFLATE 使用的单个霍夫曼代码的最大长度,而是动态霍夫曼块开头编码的霍夫曼树表示的最大累积大小。
    • 感谢图表!我确实先做了自己的计算,如果没有人提供更好的答案,我打算将其发布为答案。但是有了它,我发现您的保守估计有点过于乐观了;-)。把我的计算贴出来,希望我们能找到更精确的界限。
    • 您能分享一下您是如何生成这些图表的吗?我很好奇您是自己编写代码来解析这些位,还是有一些实用程序可以免费提取此类元数据。
    • 我对@9​​87654321@的输出进行了后处理。所以是的,自从我编写 infgen 以来,我自己编写了代码,是的,infgen 是免费提供的。
    • 只是说,我认为计算应该包括块类型头的 1+2 位(如果有的话),因为它前面的单个位表示它是否是最后一个块。这三个位几乎是不可分割的。
    【解决方案2】:

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

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

    【讨论】:

    • 好点。这影响了缓冲区应该有多大的问题——这取决于您可能做出的一些实现选择。如果您限制表的长度,您可以稍微减少所需的缓冲区大小。
    【解决方案3】:

    我想详细说明上面马克的计算。希望它是可读的:

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

    与马克公式的区别:

    • 我使用 288 作为文字/长度字母大小(2 个代码不能用作符号,这就是 Mark 使用 286 的原因,但随后 2 个代码也不能用于距离,但他使用 32,而不是 30。我不明白什么时候应该计算无效代码以及什么时候不应该计算的约束,所以我保守一点,总是计算它们)。

    • 马克的公式计算每个代码长度最多 7 位,我的有 (7 + 7)。那是因为一些长度代码(特别是值 18)可以从流中读取 7 个额外的位。

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

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

    【讨论】:

    • 你说得对,应该是 288。我将编辑我的答案。我从 RFC 中的这一行得到 286:5 Bits: HLIT, # of Literal/Length codes - 257 (257 - 286),但该范围不正确。它应该是(257 - 288),因为 257+31 = 288。
    • 正如你所注意到的,你不应该在上限计算中包含额外的位,因为它们只能使标题更短,而不是更长。
    • 上限长度不可信,但标准允许。如果霍夫曼码是根据符号的频率动态计算的,那么是的,上限是不可能的。但是,RFC 中没有任何内容表明您必须这样做。例如,兼容的压缩器可以为动态标头设置一个恒定的预设霍夫曼代码,而标头信息可能碰巧只使用长代码。
    • 我已将这些 cmets 添加到答案中,尽管是四年后。
    猜你喜欢
    • 1970-01-01
    • 2013-06-28
    • 2012-05-15
    • 1970-01-01
    • 2019-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多