增量压缩工具Xdelta3源码解析——增量文件(Window部分)

前言

上一章我们详细解析了Xdelta3编码生成的增量文件的Header部分,这一章我们继续来解析增量文件的Window部分。

介绍

在开始解析之前,要先介绍一个概念。我们将解码后生成的新文件称为解码时的目标文件,它本质上和编码时的目标文件是一样的。

一般情况下,Xdelta3在编码目标文件时并不是一次性对整个目标文件进行编码,而是将目标文件分割成多个大小相近的目标窗口,将每个目标窗口依次单独编码;当然也有例外,当目标文件足够小,小到用一个预设的窗口的大小就足以进行编码时,就不会再分割了。(关于窗口选择算法后续也会另开一篇文章进行详细讲解)

我们本章要讲解的Window部分就是由这多个目标窗口组成的。Xdelta3在解码时依次解码目标窗口,每个目标窗口中都编码了一系列的增量指令,解码这些增量指令后就能获得目标文件中的某一段数据,将所有窗口全部解码完后就获得了完整的目标文件

增量文件Window部分解析

介绍完基本概念后,我们先来看下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
	...

Win_Indicator(字节(byte)

字节用于指示该目标窗口“源数据”来源。这里说的“源数据”不是编码时的源文件,而是源窗口中的数据。什么是源窗口呢?还记得我们在讲解增量指令的那篇文章中说过一个约定吗:

假设源文件S的长度为x,目标文件T的长度为y,那么解码增量文件生成目标文件时的文件位置关系如下图所示:
增量压缩工具Xdelta3源码解析——增量文件(Window部分)_第1张图片
由图可知,解码时生成的目标文件是紧接在源文件后面的,因此目标文件中的数据地址都是相对源文件偏移之后的地址。(增量压缩工具Xdelta3源码解析——增量指令)

其实这里的源文件目标文件更准确的应该称为源窗口目标窗口,每个Window部分都单独解码一个目标窗口,而该目标窗口所对应的源窗口中的数据(即源数据)并不一定就是源文件源数据的来源取决于Win_Indicator字节中的标志位,该字节使用最低的三个比特位作为标志位:

Win_Indicator字节:
		 7 6 5 4 3 2 1 0
		+-+-+-+-+-+-+-+-+
		| | | | | | | | |
		+-+-+-+-+-+-+-+-+
		           ^ ^ ^
		           | | |
		           | | +-- VCD_SOURCE
		           | +---- VCD_TARGET
		           +------ VCD_ADLER32
  • VCD_SOURCE(第0位)
    若该标志位为1,则表示源数据来自源文件的一部分(当然也有可能是整个源文件),并且Win_Indicator字节后紧跟两个整数(integer)源数据的长度、在源文件中的起始位置。
  • VCD_TARGET(第1位)
    若该标志位为1,则表示源数据来自目标文件的一部分,该部分必须是包含在目标文件已解码的数据中,并且Win_Indicator字节后紧跟两个整数(integer)源数据的长度、在目标文件中的起始位置。

这两个标志位只能有一位为1,但允许两位都为0。若两个标志位都为0,表示该目标窗口没有对应的源数据,则Win_Indicator字节后也不会存在两个整数(integer)。这种情况可以说对该目标窗口的增量操作实质上相当于压缩操作,解码操作相当于解压操作。

  • VCD_ADLER32(第2位)
    该标志位指示是否启用Adler-32校验和,若该标志位为1,则在编码该目标窗口时将会计算该窗口目标数据的校验和,以供解码时进行校验。

该Window的增量指令部分

该部分就是用来存储编码该目标窗口时生成的增量指令的,通过解码这些增量指令,我们就可以获得对应的目标数据。这个部分包含了许多小部分,我们来挨个介绍一下。

增量指令部分的长度(整数(integer)

这个整数用于表示该Window部分剩余的字节总数,也即该目标窗口增量指令部分的长度。

该Window解码后的实际大小(整数(integer)

这个整数用于表示该目标窗口解码后的实际大小,以便解码器预先分配存放解码后数据(即目标数据)的存储空间。

Delta_Indicator(字节(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:该数组用于存储增量指令类型数据长度。这里说的指令类型并不是COPYADDRUN三个中的一个,而是指令代码表中的下标,一个下标位置最多可以包含两条组合的增量指令,并且由于代码表的下标值不超过255,因此该指令类型仅需一个字节的大小即可。
    另外在该数组中,每个指令类型后都有可能紧跟着至多两个数据长度,为什么说是可能,因为在指令代码表中存在预设的数据长度(即size),只有当代码表中的size为0时,该指令对应的size才会被单独编码在inst数组中。
  • addr:该数组用于存储COPY指令中的数据地址。至于为什么要将地址单独编码,这涉及到地址缓存的问题,这个我后续会单独写一篇关于地址编码的解析,这里我们先忽略。

知道了三个数组的定义之后,我们接着来看Delta_Indicator字节中的三个标志位,必须注意的是只有当Header部分中的Hdr_Indicator字节中的VCD_DECOMPRESS标志位为1时(表示启用二次压缩),这三个标志位才有意义:

  • VCD_DATACOMP(第0位)
    该标志位为1表示data数组被二次压缩,需要先将其解压后才能进行解码操作。
  • VCD_INSTCOMP(第1位)
    该标志位为1表示inst数组被二次压缩,需要先将其解压后才能进行解码操作。
  • VCD_ADDRCOMP(第2位)
    该标志位为1表示addr数组被二次压缩,需要先将其解压后才能进行解码操作。
三个数组的长度(整数(integer)

在Delta_Indicator字节后紧跟着三个整数(integer),分别表示数组datainstaddr的长度(单位:字节)。

adler32校验和(整数(integer)

整数仅在Win_Indicator字节中的VCD_ADLER32标志位为1时才存在,当解码完该目标窗口后,计算解码后得到的目标数据的Adler-32校验和,并与该整数进行比较,校验数据的正确性和完整性。

三个数组

最后就是存储不同类型数据的三个数组datainstaddr了。每个Window部分的增量指令都是独立的,即三个数组也都不相同。

解码时,解码器将会先读取inst数组中的值(即指令代码表的下标),通过查表后获得指令,然后根据指令对应的size值再判断是否需要再次读取inst数组来获得size值。接着根据指令的类型(COPY/ADD/RUN),判断是从data数组中直接获得数据,还是从addr数组中获得数据地址,从而获得数据。获得数据后执行对应的指令操作生成目标数据,直到解码完该目标窗口的所有增量指令。

这就是解码一个目标窗口的大致流程,更详细的解码过程我后续同样会单独写一篇文章并结合源码来讲解,感兴趣的请多多关注哦~

本章小结

至此,我们就将整个增量文件的结构解析完了,这一章的内容并不难,但了解增量文件的结构有助于我们后续理解增量压缩的编码和解码过程。

下一章将结合源码详细讲解COPY指令中的地址编码,我会慢慢把之前挖的坑补上的……如果喜欢可以点赞关注再走哦,本章内容有任何疑问也欢迎留言私信我~

参考文献

《RFC3284》https://www.rfc-editor.org/rfc/pdfrfc/rfc3284.txt.pdf

你可能感兴趣的:(xdelta3学习笔记,c语言,c++)