前言
通过系列的前面几篇文章,我们对Xdelta3的使用、增量指令
以及增量文件
都有了详细的了解,从本章开始,我们将通过结合源码来深入讲解整个增量压缩的编码和解码过程。
文章中贴源码的部分,我大多数情况下会以注释的形式进行解释,以便更准确地定位代码位置。
概念介绍
在之前讲解增量文件
Window部分的时候,我们说COPY
指令的数据地址被单独存放在addr
数组中。
然而,为了最大程度上压缩增量文件的大小,ADDR
数组中的元素大多数情况下都不是数据的实际地址。因为Base-128编码方式的特点,在编码数据地址的时候,我们期望能用少量的字节数(编码后)来表示一个地址,甚至于仅使用一个字节的低7位来表示,以避免额外的内存开销,这就需要用到我们今天要介绍的地址编码技术。
不知道大家还记不记得在讲解增量指令的那篇文章中,我们介绍过在指令代码表中每条指令都是通过一个三元组(inst,size,mode)
来表示。
而这三元组中的mode
就是地址编码的类型,因此该值也只有在COPY
指令中才有意义。
地址编码总共分为4类,每一类对应的mode
值也不同,在开始讲解之前,我们先定义几个名词:
data
:当前COPY
指令要拷贝的数据addr
:拷贝数据data
的起始地址here
:当前COPY
指令的位置(即目标窗口
的当前输入位置)en_addr
:addr
通过地址编码后实际存入ADDR
数组中的值
-
VCD_SELF(mode = 0)
当addr
的值小于128,或不满足其他地址缓存类型的情况下,使用该类型,ADDR
数组中存放的是addr
本身的数值(en_addr = addr
)。 -
VCD_HERE(mode = 1)
使用here
作为参照地址,计算相对于addr
的偏移值(here
-addr
)。当偏移值小于128,或偏移值小于addr
且不满足NEAR和SAME缓存的情况下,使用该类型。ADDR
数组中存放的是偏移值(en_addr = here - addr
)。 -
NEAR缓存(mode = [2, 5])
NEAR缓存使用前s_near
条COPY
指令的地址addr
作为参照地址,计算当前指令的addr
相对于NEAR缓存中的addr
的偏移值(addr
-near_addr
)。当偏移值小于128,或偏移值小于前两种地址编码后的结果且不满足SAME缓存的情况下,使用该类型,ADDR
数组中存放的是偏移值(en_addr = addr - near_addr
)。
s_near
是一个预设的整数值,用来表示NEAR缓存的数量。在VCDIFF的默认指令代码表中s_near
的值为4,因此mode
的取值范围在2~5
之间,用户也可以通过自定义代码表来修改这个值。
-
SAME缓存(mode = [6, 8])
每次编码一条COPY
指令时,其地址addr
都将被保存至SAME缓存中,每次新编码一个地址时,都会检索SAME缓存,若其中有与之相等的addr
值,则该地址被编码为SAME缓存中的索引值,该索引就对应addr
值。
SAME缓存总共可以存储s_same*256
个不重复的地址,s_same
是一个预设的整数值,用来表示SAME缓存区域的数量,在VCDIFF的默认指令代码表中s_same
的值为3,因此mode
的取值范围在6~8
之间,用户也可以通过自定义代码表来修改这个值。每块区域最多可以含有256个地址,因此一块区域内的索引值仅需一个字节长度即可表示。但实际上这s_same
块区域的索引值是连续的,也就是说,在存值取值时,SAME缓存所能取到最大索引值为(s_same*256)-1
。
SAME缓存采用哈希表的方式存储addr
,其哈希函数为:i_same = addr % (s_same * 256)
,i_same
为存放addr
的索引值,ADDR
数组中存放的是索引值i_same
。
实例演示
这么说可能有点抽象,我们照例还是通过例子来进一步理解,假设某个将要编码的目标窗口
和其对应的源窗口
如下:
如果全部使用COPY
指令编码该目标窗口的前33个字节,则将生成以下六条COPY
指令(指令格式为COPY(size, addr)
)。我们将依次解析每条COPY
指令的地址编码:
- COPY(4, 4306)
此时here
=10000
,指令的addr
=4306
,使用here
作为参照地址的偏移值(here
-addr
)=5694
>4306
,NEAR和SAME缓存都为空,因此只能选择VCD_SELF
类型编码地址(mode
=0
),en_addr
=4306
并存入ADDR
数组中,同时将addr
存入NEAR和SAME缓存,其中SAME缓存的索引值i_same
=4306
%(3
*256
)=376
。
- COPY(7, 4399)
此时here
=10004
,指令的addr
=4399
,使用here
作为参照地址的偏移值(here
-addr
)=5605
>4399
,NEAR缓存中的最小偏移值(4399
-4306
)为93
,小于128
,因此选择NEAR缓存类型编码地址(4306
在NEAR缓存中的索引值为0
,所以mode
=2
),en_addr
=93
并存入ADDR
数组中,同时将addr
存入NEAR和SAME缓存,其中SAME缓存的索引值i_same
=4399
%(3
*256
)=559
。
- COPY(3, 9909)
此时here
=10011
,指令的addr
=9909
,使用here
作为参照地址的偏移值(here
-addr
)=102
<128
,因此选择VCD_HERE
类型编码地址(mode
=1
),en_addr
=102
并存入ADDR
数组中,同时将addr
存入NEAR和SAME缓存,其中SAME缓存的索引值i_same
=9909
%(3
*256
)=693
。
- COPY(9, 8888)
此时here
=10014
,指令的addr
=8888
,使用here
作为参照地址的偏移值(h