您的位置:首页 > 其它

[差量更新系列2]Xdelta3原理学习笔记

2016-04-24 12:08 190 查看
[差量更新系列2]Xdelta3原理学习笔记
Xdelta3是一种优秀的、被广泛使用的差量更新算法,它在操作上既有对新文件(targetfile)和旧文件(sourcefile)的差分(differencing)又有对产生的patch包进行压缩(compression),我们将产生patch包的过程统称为加密(encoding),而将合成新文件的过程统称为解密(decoding)。Xdelta3和经典的压缩算法LZ’77一样,也是将source file划分成一个个不相交而又连续的window,然后进行encoding和decoding。
设target file的大小为n,source file的大小为m,window的大小为w。由于Xdelta3在合成target file所消耗的时间为O(n),所消耗的内存大小为O(w),所以该压缩算法很适合被移植到手机中。
Xdelta3在产生patch包的时候,主要依靠三个方面:
1.采用Vcdiff格式编码,节省patch包字节;
2.从target file和source file的内容上做差分;
3.压缩patch包。
 
Vcdiff格式编码
这是一种基于字节的编码,可移植性强。采用128进制,用一个字节的低七位表示数字,最高位标记编码是否完成,如果没有编完,编码将扩展到下一个字节。如:
 
123456789
 
表示为128进制为:
 
58,111,26,21
 
在机器中表示如下:

 

               +-------------------------------------------------------+

                 | 10111010 | 11101111 |10011010 | 00010101 |

               +-------------------------------------------------------+

                  MSB+58  MSB+111 MSB+26  0+21

 

这里,原本要用9个字节,变成了4个字节。

 

Target file和Sourcefile的差分             

我们用S表示sourcefile的字符串,长度为S_len,T表示targetfile的字符串。差分用到add,run,copy三种命令。Xdelta3中,差分的copy命令不但可以在S与T之间执行,还可以在T已被解压的部分和未被解压的部分执行。

 

    ADD   x,s: 将一个长度为x的字符串s拷贝到T的当前位置。

    COPY  x,y: 当y<S_len时,从S[y]拷贝x个字符到T的当前位置;

                当y≥S_len时,从T[y-S_len]拷贝x个字符到T的当前位置。

    RUN   x,z:在T的当前位置加x个字符z。

 

我们给出例子如下:

    Source file  a b c d e f g h i j kl m n o p

     Target file  a b c d w x y z e f g h e f g h e f g h e f g h z z z z

 

       COPY  4, 0

       ADD   4, w x y z

       COPY  4, 4

       COPY  12, 24

       RUN  4, z

 

对于第四条命令,由于S的长度为16, 拷贝指针c指向T中的位置24-16=8,T当前位置的指针p指向位置12,执行T[p++]=T[c++]12次。

 

压缩Patch包

由上一部分可知,patch包中的内容可以由一系列add,run,copy命令表示。为了使patch包更小,这些命令必须被更有效的压缩。为了使压缩效率更高,Xdelta3将patch中的内容分了三部分,数据部分(DATA)、指令部分(INSTRUCTION)以及地址部分(ADDRESS),分别放在三个不同的数组,基于Vcdiff编码压缩:

 

DATA       :ADD 和 RUN 命令中附带的数据,如字符串s和字符z(存放在数组data

               中);

INSTRUCTION:一系列ADD、COPY、RUN命令本身和它们附带表示size的数字x(命令

               存在数组inst中,x存在inst或code table中);

ADDRESS   :COPY命令中附带的地址y(存放在数组addr中)。

 

DATA部分简单地按字节压缩、按顺序存放在数组data中即可,当执行的指令需要用到这个参数时,根据Vcdiff的编码原则,按顺序读取一个或多个字节,就可以获得该指令对应的data,如RUN中的字符和ADD中的字符串。

ADDRESS部分为COPY命令提供地址参数,和DATA部分的原理类似,但其在压缩和解压过程中,用到了cache以便加速,其中解压过程的cache会自动与压缩过程的cache同步。near 和same两种不同的cache对已两种不同的压缩方式。

INSTRUCTION部分的压缩较为复杂。由于剥离了存放在data和addr中的数据,现在我们可以用元组(inst, size, mode)来表示一条命令。Inst表示指令是ADD、RUN或COPY;size 就是命令中携带的x,其值为0的时候表示它被放在inst数组中,像上面提到的DATA、ADDRESS两个部分一样独立编码,不为0时被放在code table中可被直接读出;mode表示地址编码的压缩方式,当inst的值为COPY时,这个值才有效,其余的时候这个值都为0。

为了压缩b部分,需要在内存里保存一张code table,这个table的每个index对应了两个操作

 

       +-----------------------------------------------------+
          | inst1 | size1 | mode1 | inst2 | size2 | mode2 |
          +----------------------------------------------------+
 

默认的code table可以满足现今绝大多数的情况下的需求,如下:

 

 

     TYPE      SIZE     MODE   TYPE     SIZE     MODE  INDEX

    -----------------------------------------------------------------------------------------------------

    1.  RUN         0     0      NOOP       0       0        0

    2.  ADD    0, [1,17]     0     NOOP       0        0     [1,18]

    3.  COPY  0, [4,18]     0      NOOP      0        0     [19,34]

    4.  COPY   0, [4,18]    1      NOOP       0       0     [35,50]

    5.  COPY   0, [4,18]    2      NOOP       0       0     [51,66]

    6.  COPY   0, [4,18]    3      NOOP       0       0     [67,82]

    7.  COPY   0, [4,18]    4      NOOP       0       0     [83,98]

     8. COPY   0, [4,18]     5     NOOP       0        0    [99,114]

    9.  COPY   0, [4,18]    6      NOOP       0       0    [115,130]

   10.  COPY   0, [4,18]    7      NOOP       0       0    [131,146]

   11.  COPY   0, [4,18]    8      NOOP       0       0    [147,162]

   12.  ADD      [1,4]     0      COPY    [4,6]       0    [163,174]

   13.  ADD       [1,4]    0      COPY     [4,6]      1    [175,186]

   14.  ADD       [1,4]    0      COPY     [4,6]      2    [187,198]

   15.  ADD       [1,4]    0      COPY     [4,6]      3    [199,210]

   16.  ADD       [1,4]    0      COPY     [4,6]      4    [211,222]

   17.  ADD       [1,4]    0      COPY     [4,6]      5    [223,234]

   18.  ADD       [1,4]    0      COPY       4       6    [235,238]

   19.  ADD       [1,4]    0      COPY       4       7    [239,242]

   20.  ADD       [1,4]    0      COPY       4       8    [243,246]

   21.  COPY        4    [0,8]    ADD        1       0    [247,255]

------------------------------------------------------------------------------------------------------

 

其中NOOP表示无任何操作。Index的值覆盖0~255,可以用一个字节表示。有了code table,我们编码过程中,可以将指令写成(index, [size1],[size2])形式放在数组inst中。 

比如我们给出index=19,那么我们在code table中查找,第3行的INDEX中包含了19,这一行对应了两个连续的命令,COPY和NOOP。COPY的对应的SIZE部分包含了0,4~18这16个值,其中0与19对应,即这条命令的size被独立压缩编码,我们需要从inst数组中读出size1。如果是index=165,它对应第12行。第12行有两个操作,两个SIZE应该以第一个SIZE的取值范围做外层循环,第二个SIZE的范围做内层循环,组合展开如下:

 

 TYPE     SIZE     MODE    TYPE    SIZE     MODE     INDEX

------------------------------------------------------------------------------------------------------

ADD        1       0        COPY       4        0         163

ADD        1        0       COPY       5         0         164   

ADD        1       0        COPY       6        0         165

ADD        2       0        COPY       4        0         166

ADD        2       0        COPY       5        0         167

ADD        2       0        COPY       6        0         168

ADD        3       0        COPY       4        0         169

 TYPE      SIZE     MODE   TYPE     SIZE     MODE    INDEX

 -----------------------------------------------------------------------------------------------------

  ADD       3        0        COPY       5        0         170

  ADD       3        0        COPY       6        0         171

  ADD       4        0        COPY       4        0         172

  ADD        4       0        COPY       5        0         173

  ADD       4        0        COPY       6        0         174

 ------------------------------------------------------------------------------------------------------

 

我们可以将165解密成两个连续的操作:1.在T[p]处添加一个字符,该字符以Vcdiff编码的方式存放在data[data_p],p++,data_p++;2.拷贝6个字节到T[p...p+5]处,p=p+6,被拷贝文件的地址以Vcdiff编码+mode 0的方式编码存放在addr[addr_p]。

   

总结

  Xdelta3使用基于字节的Vcdiff编码,可以在一种机器上encoding而在另一种机器上decoding,且encoding与decoding的过程相对独立,可移植性强。Patch包被分为三个独立的部分,便于使用不同的技术来分别改进它们的编码、压缩,并且支持二次压缩。Decoding的时间与内存消耗为线性。而encoding的过程中,最主要的部分为每次寻找最大匹配串的string matching过程,这个过程可采用hash,suffix trees,fast string matching等技术来优化,但其时间内存消耗对大文件而言仍不可接受,使用windows划分这一技术有效的解决了问题。window的大小是一个重要的参数,window越大,最长字符串匹配的结果越精确,产生的patch可能越小,而时间内存消耗则变大;window越小,则反之。

 
更多内容,参考译文:

The VCDIFF Generic Differencing and Compression Data Format

http://tools.ietf.org/html/rfc3284
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: