nginx中的哈夫曼编码算法-解码[上]

nginx中的哈夫曼编码算法-编码
nginx中的哈夫曼编码算法-解码[上]
nginx中的哈夫曼编码算法-解码[中]
nginx中的哈夫曼编码算法-解码[下]

1. 引言

  在[《nginx中的哈夫曼编码算法》](https://blog.csdn.net/bluestn/article/details/138095355)中,我们介绍了nginx采用查表的方法来实现的哈夫曼编码对http2 hpack进行压缩的功能,其编码的实现原理还是比较简单的。然而,上山容易下山难,nginx中实现的快速哈夫曼解码算法在理解上相对于编码算法有一些难度的。今天我们来聊一聊nginx是如何来实现快速哈夫曼解码的。
  为什么要增加快速这个形容词呢?因为在学习哈夫曼原理的时候,书本上介绍的是采用构建哈夫曼树的方式,通过一边读取输入流中的比特,一边在哈夫曼树中不断游走的方式来实现的解码方式,虽然这种方式比较容易理解,但是其解码效率是不那么理想的。而nginx采用构建状态转移矩阵,在解码时不是每次读取一个比特,而是每次一次性读取输入流中的4个比特,通过跟随状态转移矩阵不断更新状态,来实现快速解码,解码效率得到大幅提升。
  而且nginx在状态转移矩阵也进行了优化,能够在解码的过程中,对于多读取的比特不进行回退就能够正确解码,相比于需要回退的算法能够在性能上表现更加优秀。
  本文分三部分进行讲解,首先介绍nginx实现的哈夫曼解码算法中的状态转移矩阵的构造及利用状态转移矩阵如何进行解码的原理;接着我们结合nginx的源码来详细分析nginx的解码源码的实现原理;最后,介绍快速哈夫曼解码算法的最核心的内容,就是如何来构造状态转移矩阵。

  在深入阅读本文之前,大家也可以参考我之前发表的《采用状态转移矩阵方式的快速哈夫曼解码算法》,预先了解用状态转移表进行哈夫曼解码的原理。

2. 哈夫曼解码状态转移矩阵

  在nginx的哈夫曼解码的相关代码里面,首先它定义了一个状态转移矩阵,如下:

  

typedef struct {
    u_char  next;
    u_char  emit;
    u_char  sym;
    u_char  ending;

} ngx_http_huff_decode_code_t;


static ngx_http_huff_decode_code_t  ngx_http_huff_decode_codes[256][16] = {
......
}

  先了解释一下ngx_http_huff_decode_code_t结构体的每个字段的含义。

  • next: 下一个状态的状态id
  • emit: 是否可以输出sym(被解码出来的字符)
  • sym: 如果 emit=1,那么sym表示被解码出来的字符
  • ending: 本状态是否可以进入解码完成状态

 emsp;在看看ngx_http_huff_decode_codes,它是一个二维数组,第一维有256个记录,表示256个状态,第二纬有16个元素,表示在当前的状态下面,后面读取到新的4个bit(总共16种排序方式,包括:0000,0001,0010,0011,0100,0101,0110,0111,1000,1001,1010,1011,1100,1101,1110,1111)后,如何进行处理,包括是输出解码得到的字符,还是只是进行状态转移,疑惑是既要输出解码得到的字符又要进行状态转移。

  所以这里第二维中的每个元素可以看成是状态转移图中的转移弧,并且每个状态都有16条状态转移弧。

  在这个状态转移矩阵中,ngx_http_huff_decode_codes的第零条记录被规定为起始状态,解码的时候从状态零开始,不断重复读进4个bit,然后根据当前状态下对应的转移弧来进行处理,直到解码出所有的字符。

  举个例子,譬如,0a0a9bc\xf8的哈夫曼编码为 00 c0 37 e3 27 ff ff eb,按照nginx定义的状态转移矩阵,人工进行解码,将过程总结成一个表格,如下:

当前状态输入
(二进制)
下一状态输出字符结束标记
000004-0
4000030x30(0)0
3110020x61(a)0
2000010x30(0)0
0001100x61(a)0
0011119-0
191110230x39(9)0
23001100x62(b)0
000107-0
70111560x63©1
56111174-1
74111188-1
88111195-1
951111169-1
1691110208-1
208101100xf81

  需要特别说明的是上述表格的”结束标记“,结束标记表示当前状态下遇到特定的4个比特的输入后,转移到新的状态时,这个新的状态可能是结束态。

  如果输入流所有的比特都读取完毕了以后,但是当前的解码状态不是”结束状态“,那么认为当前的哈夫曼码流是有问题的,所以解码器根据这个条件来识别待解码的码流可能损坏。

在解码的过程中,还有一种是当前状态下面,输入的新的4个比特后,对应的转移弧还是转移到当前状态,在nginx中这种是用来表示当前状态不可能碰到这种组合的比特,也用来表示当前的输入码流可能已经损坏。

3. 源码分析

4. 如何构造状态转移表

<未完待续>

  • 15
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农心语

您的鼓励是我写作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值