在RFC-1951中,有一种简单的算法可以从代码长度列表中恢复霍夫曼树,如下所述:
1) Count the number of codes for each code length. Let
bl_count[N] be the number of codes of length N, N >= 1.
2) Find the numerical value of the smallest code for each
code length:
code = 0;
bl_count[0] = 0;
for (bits = 1; bits <= MAX_BITS; bits++) {
code = (code + bl_count[bits-1]) << 1;
next_code[bits] = code;
}
3) Assign numerical values to all codes, using consecutive
values for all codes of the same length with the base
values determined at step 2. Codes that are never used
(which have a bit length of zero) must not be assigned a
value.
for (n = 0; n <= max_code; n++) {
len = tree[n].Len;
if (len != 0) {
tree[n].Code = next_code[len];
next_code[len]++;
}
但算法中没有任何数据一致性检查。另一方面很明显长度列表可能无效。由于4位编码的长度值不能无效,但是,例如,对于某些代码长度,可以编码的代码可以多于可编码的代码。
提供数据验证的最小检查集是什么?或者由于某些原因我不需要这样的检查?
答案 0 :(得分:1)
我认为检查next_code[len]
不会溢出其各自的位就足够了。因此,在tree[n].Code = next_code[len];
之后,您可以执行以下检查:
if (tree[n].Code & ((1<<len)-1) == 0)
print(Error)
如果tree[n].Code & ((1<<len)-1)
达到0,则意味着有更多长度为len
的代码,因此长度列表中有错误。
另一方面,如果为树的每个符号分配了一个有效(唯一)代码,那么您已经创建了一个正确的霍夫曼树。
编辑:我突然意识到:您可以在第一步结束时进行相同的检查:您只需检查所有bl_count[N] <= 2^N - SUM((2^j)*bl_count[N-j])
和所有1<=j<=N
的{{1}} (如果二叉树在级别N >=1
中有bl_count[N-1]
个叶子,那么它在级别N-1
中不能超过2^N - 2*bl_count[N-1]
个叶子,级别0是根目录。“ p>
这可以保证您创建的代码是前缀代码,但不保证它与原始创建者的代码相同。例如,如果长度列表无法以您仍然可以创建有效前缀代码的方式进行,则无法证明这是霍夫曼代码,只是因为您不知道每个符号出现的频率。
答案 1 :(得分:1)
zlib检查代码长度列表是否完整,即它是否耗尽所有位模式,并且它不会溢出位模式。允许的一个例外是当存在长度为1的单个符号时,在这种情况下允许代码不完整(位0表示符号,1位未定义)。
这有助于zlib以更高的概率拒绝随机,损坏或不正确编码的数据,并且在流中更早。这是一种不同于此处另一个答案所建议的稳健性,在这里您可以允许不完整的代码,并且只有在压缩数据中遇到未定义的代码时才会返回错误。
要计算完整性,请先从代码k=1
中的位数和可能的代码数n=2
开始。有两种可能的一位代码。您从n
减去长度为1的代码数n -= a[k]
。然后,您递增k
以查看两位代码,然后加倍n
。减去两位代码的数量。完成后,n
应为零。如果在任何时候n
变为负数,则可以在那里停止,因为您有一组无效的代码长度。如果您完成n
的时间大于零,那么您的代码就会不完整。
答案 2 :(得分:0)
您需要确保没有输入会导致您的代码执行非法或未定义的行为,例如索引数组末尾,因为此类非法输入可能会用于攻击您的代码。
在我看来,你应该尝试尽可能优雅地处理非法但非危险的输入,以便与其他人编写的程序互操作,这些程序可能以不同于你的方式解释规范,或者制作了只有一个似是而非的解释的小错误。这是鲁棒性原则 - 您可以从http://en.wikipedia.org/wiki/Robustness_principle开始讨论这个问题。