上一章我们详细解析了Xdelta3编码生成的增量文件
的Header部分,这一章我们继续来解析增量文件
的Window部分。
在开始解析之前,要先介绍一个概念。我们将解码后生成的新文件
称为解码时的目标文件
,它本质上和编码时的目标文件
是一样的。
一般情况下,Xdelta3在编码目标文件
时并不是一次性对整个目标文件
进行编码,而是将目标文件
分割成多个大小相近的目标窗口
,将每个目标窗口
依次单独编码;当然也有例外,当目标文件
足够小,小到用一个预设的窗口的大小就足以进行编码时,就不会再分割了。(关于窗口选择算法后续也会另开一篇文章进行详细讲解)
我们本章要讲解的Window部分就是由这多个目标窗口
组成的。Xdelta3在解码时依次解码目标窗口
,每个目标窗口
中都编码了一系列的增量指令
,解码这些增量指令
后就能获得目标文件
中的某一段数据,将所有窗口全部解码完后就获得了完整的目标文件
。
介绍完基本概念后,我们先来看下Window部分的组成结构,然后再进行详细讲解:
Window部分
Window1
Win_Indicator - byte
|-[源数据的长度] - integer
|-[源数据的位置] - integer
该Window的增量指令部分
|-增量指令部分的长度 - integer
|-增量指令部分
|-该Window解码后的实际大小 - integer
|-Delta_Indicator - byte
|-data数组的长度L - integer
|-inst数组的长度M - integer
|-addr数组的长度N - integer
|-[adler32校验和] - integer
|-data数组 - L bytes
|-inst数组 - M bytes
|-addr数组 - N bytes
Window2
...
字节(byte)
)该字节
用于指示该目标窗口
的“源数据”
来源。这里说的“源数据”
不是编码时的源文件
,而是源窗口
中的数据。什么是源窗口
呢?还记得我们在讲解增量指令的那篇文章中说过一个约定吗:
假设源文件S的长度为x,目标文件T的长度为y,那么解码增量文件生成目标文件时的文件位置关系如下图所示:
由图可知,解码时生成的目标文件是紧接在源文件后面的,因此目标文件中的数据地址都是相对源文件偏移之后的地址。(增量压缩工具Xdelta3源码解析——增量指令)
其实这里的源文件
和目标文件
更准确的应该称为源窗口
和目标窗口
,每个Window部分都单独解码一个目标窗口
,而该目标窗口
所对应的源窗口
中的数据(即源数据
)并不一定就是源文件
。源数据
的来源取决于Win_Indicator字节中的标志位,该字节
使用最低的三个比特位作为标志位:
Win_Indicator字节:
7 6 5 4 3 2 1 0
+-+-+-+-+-+-+-+-+
| | | | | | | | |
+-+-+-+-+-+-+-+-+
^ ^ ^
| | |
| | +-- VCD_SOURCE
| +---- VCD_TARGET
+------ VCD_ADLER32
1
,则表示源数据
来自源文件
的一部分(当然也有可能是整个源文件
),并且Win_Indicator字节后紧跟两个整数(integer)
:源数据
的长度、在源文件
中的起始位置。1
,则表示源数据
来自目标文件
的一部分,该部分必须是包含在目标文件
已解码的数据中,并且Win_Indicator字节后紧跟两个整数(integer)
:源数据
的长度、在目标文件
中的起始位置。这两个标志位只能有一位为1
,但允许两位都为0
。若两个标志位都为0
,表示该目标窗口
没有对应的源数据
,则Win_Indicator字节后也不会存在两个整数(integer)
。这种情况可以说对该目标窗口
的增量操作实质上相当于压缩操作,解码操作相当于解压操作。
1
,则在编码该目标窗口
时将会计算该窗口目标数据
的校验和,以供解码时进行校验。该部分就是用来存储编码该目标窗口
时生成的增量指令
的,通过解码这些增量指令
,我们就可以获得对应的目标数据
。这个部分包含了许多小部分,我们来挨个介绍一下。
整数(integer)
)这个整数
用于表示该Window部分剩余的字节总数,也即该目标窗口
的增量指令
部分的长度。
整数(integer)
)这个整数
用于表示该目标窗口
解码后的实际大小,以便解码器预先分配存放解码后数据(即目标数据
)的存储空间。
字节(byte)
)该字节
使用最低的三个比特位作为标志位,用于指示被二次压缩的数据,其结构如下:
Delta_Indicator字节:
7 6 5 4 3 2 1 0
+-+-+-+-+-+-+-+-+
| | | | | | | | |
+-+-+-+-+-+-+-+-+
^ ^ ^
| | |
| | +-- VCD_DATACOMP
| +---- VCD_INSTCOMP
+------ VCD_ADDRCOMP
在解释每个标志位的作用前,我们需要先知道关于增量指令
的编码规则。不了解增量指令
的同学可以先看下我之前写的另一篇文章《增量压缩工具Xdelta3源码解析——增量指令》。
增量指令
的格式可以归纳为:指令类型 数据长度 数据
不同的是,COPY
指令的数据并不是直接给出具体内容,而是给的数据地址,因此我们将三种增量指令
中类型相近的数据分为三个不同的数组来存储:
data
:该数组用于存储ADD
指令和RUN
指令中的数据内容,由于这两个指令的数据都是直接给的具体内容,因此无论是ADD
指令的字符串还是RUN
指令的字符,都是按照编码时增量指令
的顺序以字符形式存储在该数组中。inst
:该数组用于存储增量指令
的类型
和数据长度
。这里说的指令类型
并不是COPY
、ADD
、RUN
三个中的一个,而是指令代码表中的下标,一个下标位置最多可以包含两条组合的增量指令
,并且由于代码表的下标值不超过255
,因此该指令类型
仅需一个字节
的大小即可。指令类型
后都有可能紧跟着至多两个数据长度
,为什么说是可能,因为在指令代码表中存在预设的数据长度
(即size
),只有当代码表中的size
为0时,该指令对应的size
才会被单独编码在inst
数组中。addr
:该数组用于存储COPY
指令中的数据地址
。至于为什么要将地址
单独编码,这涉及到地址缓存的问题,这个我后续会单独写一篇关于地址编码的解析,这里我们先忽略。知道了三个数组的定义之后,我们接着来看Delta_Indicator字节中的三个标志位,必须注意的是只有当Header
部分中的Hdr_Indicator
字节中的VCD_DECOMPRESS
标志位为1
时(表示启用二次压缩),这三个标志位才有意义:
1
表示data
数组被二次压缩,需要先将其解压后才能进行解码操作。1
表示inst
数组被二次压缩,需要先将其解压后才能进行解码操作。1
表示addr
数组被二次压缩,需要先将其解压后才能进行解码操作。整数(integer)
)在Delta_Indicator字节后紧跟着三个整数(integer)
,分别表示数组data
、inst
、addr
的长度(单位:字节)。
整数(integer)
)该整数
仅在Win_Indicator字节中的VCD_ADLER32
标志位为1
时才存在,当解码完该目标窗口
后,计算解码后得到的目标数据
的Adler-32校验和,并与该整数
进行比较,校验数据的正确性和完整性。
最后就是存储不同类型数据的三个数组data
、inst
、addr
了。每个Window部分的增量指令
都是独立的,即三个数组也都不相同。
解码时,解码器将会先读取inst
数组中的值(即指令代码表的下标),通过查表后获得指令,然后根据指令对应的size
值再判断是否需要再次读取inst
数组来获得size
值。接着根据指令的类型(COPY
/ADD
/RUN
),判断是从data
数组中直接获得数据,还是从addr
数组中获得数据地址,从而获得数据。获得数据后执行对应的指令操作生成目标数据
,直到解码完该目标窗口
的所有增量指令。
这就是解码一个目标窗口
的大致流程,更详细的解码过程我后续同样会单独写一篇文章并结合源码来讲解,感兴趣的请多多关注哦~
至此,我们就将整个增量文件
的结构解析完了,这一章的内容并不难,但了解增量文件
的结构有助于我们后续理解增量压缩的编码和解码过程。
下一章将结合源码详细讲解COPY
指令中的地址编码,我会慢慢把之前挖的坑补上的……如果喜欢可以点赞关注再走哦,本章内容有任何疑问也欢迎留言私信我~
《RFC3284》https://www.rfc-editor.org/rfc/pdfrfc/rfc3284.txt.pdf