Gzip源代码分析(四)

哈弗曼编码

首先说明一下涉及到的数据结构:

typedef struct ct_data {
    union {
        ush  freq;       /* frequency count */
        ush  code;       /* bit string */
    } fc;
    union {
        ush  dad;        /* father node in Huffman tree */
        ush  len;        /* length of bit string */
    } dl;
} ct_data;

ushunsigned short int类型)这是一个结构体里面套了两个联合体的数据结构。Gzip用这个ct_data类型定义了五棵树,其实就是数组:dyn_ltree[],dyn_dtree[],static_ltree[],static_dtree[],bl_tree[]。数组的下标表示待编码节点的值,freq字段表示该节点出现的频率,其余字段暂不介绍。

哈弗曼编码的输入

gzip中,哈弗曼编码可看作是LZ77的下一级压缩,但哈弗曼编码并不直接处理LZ77输出的第一手材料,而是做了一步映射。

dyn_dtree对应d_buf,其数据的取值范围在032K之间,gzip将这32K的范围映射到[0, 29]。因为树数组的下标代表节点的取值,所以dyn_dtree数组的长度可以定为30。要是不做映射,dyn_dtree数组的大小就需要32K之多,而且其中很多节点可能频率为0

dyn_ltree对应l_bufl_bufliterallength是混杂的,且两者的取值范围都是0255,完全重叠在一起,如果直接哈弗曼编码,解压时,除非参照flag_buf,否则无法区分。之前第二篇计算最小匹配长度时,曾假设(距离-长度)二元组在上下文中能自说明,不需要额外的描述符(flag_buf)。为了让假设成真,gziplength映射到[257285]这29个节点(用256表示block结束),所以解压时,值大于256的是length,小于256的是literal。所以,dyn_ltree数组需要285的大小存放literallength节点。

那么这个映射是怎么做的呢?这样多对一的映射,解压时又该怎么还原呢?

映射的方法就是将原数值的后几位比特(称为extra bit)截断,这样就映射到了比较小的值了。解压时,先解码映射后的小数值,被截断的比特紧随其后,直接拷贝就行了,具体要拷贝多少比特视小数值而定:

int extra_dbits[30] /* extra bits for each distance code */
   = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};

这是距离extra bit长度的定义;

int extra_lbits[29] /* extra bits for each length code */
   = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};

这是长度extra bit长度的定义。

你可能感兴趣的:(Gzip源代码分析(四))