Bsdiff差分算法讲解
https://blog.csdn.net/qazw9600/article/details/10811161
说明
- 解析基于算法库bsdiff-4.3.tar.gz。
- 算法文件组成是两个C文件:bsdiff.c(根据老版本和新版本生成补丁文件)和bspatch.c(根据老版本和补丁文件生成新版本)。
- 个人还未完全理解BSDiff算法,主要是后缀数组的实现未理解,暂时将后缀数组实现函数qsufsort和split当做黑盒,记录说明下其它操作。
解析
Line 230: qsufsort(I,V,old,oldsize);
- 调用qsufsort该函数生成后缀数组,old为老版本文件数据,oldsize为文件长度,V是辅助数组,使用完就free了,I保存了根据老版本文件数据生成的后缀数组,后缀数组就不解释,可在网上找后缀数组 罗穗骞的论文理解。
- 注意:I数组的大小是oldsize+1,I[0]的值为oldsize;可构造一些简单的数据对生成的I数组进行观察。
- 算法中实现后缀数组的算法,个人未理解,非常见的实现算法,是Jesper Larsson, Faster Suffix Sorting,虽然未理解,但是生成的结果是后缀数组,理解后缀数组的特性,将其当做黑盒,也能先理解后续操作。
while(scan<newsize) {for(scsc=scan+=len;scan<newsize;scan++) {len=search(I,old,oldsize,new+scan,newsize-scan,0,oldsize,&pos);for(;scsc<scan+len;scsc++)if((scsc+lastoffset<oldsize) &&(old[scsc+lastoffset] == new[scsc]))oldscore++;if(((len==oldscore) && (len!=0)) || (len>oldscore+8)) break;if((scan+lastoffset<oldsize) &&(old[scan+lastoffset] == new[scan]))oldscore--;};....
}
2.1 新版本文件和老版本文件都从数据开头开始,通过二分法,在整个后缀数组I中找到与新版本数据匹配最长的长度len和数组编号pos。
len=search(I,old,oldsize,new+scan,newsize-scan,0,oldsize,&pos);* search和matchlen函数略。
2.2 计算出当前偏移的old数据与new数据相同的字节个数,再与len比较。
for(;scsc<scan+len;scsc++)if((scsc+lastoffset<oldsize) &&(old[scsc+lastoffset] == new[scsc]))oldscore++;if(((len==oldscore) && (len!=0)) || (len>oldscore+8)) break;
2.2.2 如果相差大于8则退出for循环,下面则对这一段数据进行处理。
老版本程序:
printf("hello");
....
新版本程序:
printf("world");
....
- 更改位置的后续处理都一样,可以想象出编译出来的二进制数据也是相差不大的,只是由于插入新代码或者修改了部分代码,导致后续二进制数据在新老程序中的偏移不同。
- 退出for循环的条件就是找到这样的位置,少于8个字节的相差会被跳过,继续for循环。
while(scan<newsize) {....if((len!=oldscore) || (scan==newsize)) {s=0;Sf=0;lenf=0;for(i=0;(lastscan+i<scan)&&(lastpos+i<oldsize);) {if(old[lastpos+i]==new[lastscan+i]) s++;i++;if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; };};
,lenb=0;if(scan<newsize) {s=0;Sb=0;for(i=1;(scan>=lastscan+i)&&(pos>=i);i++) {if(old[pos-i]==new[scan-i]) s++;if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; };};};if(lastscan+lenf>scan-lenb) {overlap=(lastscan+lenf)-(scan-lenb);s=0;Ss=0;lens=0;for(i=0;i<overlap;i++) {if(new[lastscan+lenf-overlap+i]==old[lastpos+lenf-overlap+i]) s++; if(new[scan-lenb+i]== old[pos-lenb+i]) s--; if(s>Ss) { Ss=s; lens=i+1; }; };lenf+=lens-overlap; lenb-=lens;};for(i=0;i<lenf;i++)db[dblen+i]=new[lastscan+i]-old[lastpos+i];for(i=0;i<(scan-lenb)-(lastscan+lenf);i++)eb[eblen+i]=new[lastscan+lenf+i];dblen+=lenf;eblen+=(scan-lenb)-(lastscan+lenf);offtout(lenf,buf);BZ2_bzWrite(&bz2err, pfbz2, buf, 8); if (bz2err != BZ_OK)errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);offtout((scan-lenb)-(lastscan+lenf),buf); BZ2_bzWrite(&bz2err, pfbz2, buf, 8);if (bz2err != BZ_OK)errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);offtout((pos-lenb)-(lastpos+lenf),buf); BZ2_bzWrite(&bz2err, pfbz2, buf, 8);if (bz2err != BZ_OK)errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);lastscan=scan-lenb; lastpos=pos-lenb; lastoffset=pos-scan;};
};
s=0;Sf=0;lenf=0;
for(i=0;(lastscan+i<scan)&&(lastpos+i<oldsize);) {if(old[lastpos+i]==new[lastscan+i]) s++;i++;if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; };
};
if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; };
* Sf*2-lenf 和 s*2-i 都是等式: a*2 - b。
* Sf*2-lenf可以理解为 上一组s和i,s*2-i计算出的值。
* s*2 - i, i和s的增长步长都为1,也就是i走两步,s走一步就可以维持s*2 - i结果不变,
如果结果要增加,也就是s增加的频率要>50%,即后续增加的数据超过50%的数据需要是相等的。
lenb=0;
if(scan<newsize) {//printf("lastscan:%d, pos:%d\n", lastscan, pos);s=0;Sb=0;for(i=1;(scan>=lastscan+i)&&(pos>=i);i++) {if(old[pos-i]==new[scan-i]) s++;if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; };};
};
- 上面lenf的值即为diff string的长度,该段数据剩余部分即可认为是extra string,这样长度相减即可获得extra string的长度;但是保存的diff string是新老数据的相差值,可以更好的被压缩,而extra string保存的就是原始数据,压缩层度不高,为了减少extea string的长度,采取了将部分extra string与下一段数据近似相同的数据遗留下来,在下一段数据可以充当diff string。
- scan等于newsize,就没有下一段数据了,因此这里需要判断scan小于newsize。
if(lastscan+lenf>scan-lenb) {overlap=(lastscan+lenf)-(scan-lenb);s=0;Ss=0;lens=0;for(i=0;i<overlap;i++) {if(new[lastscan+lenf-overlap+i]==old[lastpos+lenf-overlap+i]) s++; if(new[scan-lenb+i]== old[pos-lenb+i]) s--; if(s>Ss) { Ss=s; lens=i+1; };};lenf+=lens-overlap; lenb-=lens;
};
3.4 将diff string和extra string保存到内存中,再将长度数据使用bzip压缩写入文件
for(i=0;i<lenf;i++)db[dblen+i]=new[lastscan+i]-old[lastpos+i]; //diff string
for(i=0;i<(scan-lenb)-(lastscan+lenf);i++)eb[eblen+i]=new[lastscan+lenf+i]; //extra stringdblen+=lenf;
eblen+=(scan-lenb)-(lastscan+lenf);//长度数据压缩写入文件
offtout(lenf,buf);
BZ2_bzWrite(&bz2err, pfbz2, buf, 8); //保存的是diff string 的长度
if (bz2err != BZ_OK)errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);offtout((scan-lenb)-(lastscan+lenf),buf); //保存的是extern string 的长度
BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
if (bz2err != BZ_OK)errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);offtout((pos-lenb)-(lastpos+lenf),buf); //保存的是old程序中的新偏移pos相对于上一个偏移lastpos的相差
BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
if (bz2err != BZ_OK)errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);//保存偏移
lastscan=scan-lenb;
lastpos=pos-lenb;
lastoffset=pos-scan;
说明
- diff string (上图的差异文件)和 extra string (上图的更新文件)如上图所示。
- bsdiff 算法循环中截取出的每段数据类似于上图中的数据,但是每段数据操作的新文件和老文件数据长度是一样的,和上图不一样。
Bsdiff差分算法讲解相关推荐
- bsdiff差分算法
bsdiff的基本原理 bsdiff是由Conlin Percival开源的一个优秀的差分算法,而且是跨平台的.在Android系统中所使用的imgdiff本质上就是bsdiff. bsdiff的依据 ...
- Xdelta3 bsdiff Courgette三种差分算法比较
今天介绍常用的三种差分算法,分别是Xdelta3 bsdiff Courgette. Xdelta3 官网地址: http://xdelta.org 源码地址:https://github.com/j ...
- Myers差分算法详解
一.简介 上一章节讲述了使用DiffUtil工具类计算两个集合的差异.具体核心代码是calculateDiff方法.即 计算差异 的方法. DiffUtil+AsyncListDiffer工具类原理讲 ...
- 边界填充算法讲解_边界填充算法
边界填充算法讲解 Boundary fill is the algorithm used frequently in computer graphics to fill a desired color ...
- 干货回顾丨机器学习笔记-----AP(affinity propagat)算法讲解及matlab实现
在统计和数据挖掘中,亲和传播(AP)是基于数据点之间"消息传递"概念的聚类算法.与诸如k-means或k-medoids的聚类算法不同,亲和传播不需要在运行算法之前确定或估计聚类的 ...
- Learning to Rank 中Listwise关于ListNet算法讲解及实现
[学习排序] Learning to Rank 中Listwise关于ListNet算法讲解及实现 版权声明:本文为博主原创文章,转载请注明CSDN博客源地址!共同学习, ...
- TF-IDF算法讲解
什么是 TF-IDF 算法? TF(全称TermFrequency),中文含义词频,简单理解就是关键词出现在网页当中的频次. IDF(全称InverseDocumentFrequency),中文含义逆 ...
- Java的算法讲解以及案例!
算法是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,java算法就是采用Java语言来实现解决某一问题的清晰指令. 算法的特征: 输入性:有零个或多个外部量作为算法的输入 输出性:算法产生 ...
- 单目标多目标 灰狼算法算法讲解
灰狼算法单目标&多目标算法讲解 引言 元启发式算法 概念 优势 特征 分类 1. 灰狼算法思想 参考文献 源代码下载 灰狼社会等级及捕猎过程 2. 单目标灰狼算法 包围猎物 追逐猎物 搜捕猎物 ...
最新文章
- 汇编程序设计与计算机体系结构软件工程师教程笔记:处理器、寄存器简介
- 每个设计师需知的40个设计素材站
- springmvc原理详解(手写springmvc)
- Spring框架之权限管理
- Java工作笔记-使用Maven创建多模块项目
- HDOJ 1285 确定比赛名次(拓扑排序)
- 微信小程序之 3d轮播(swiper来实现)
- 如何查看selenium的版本号
- android 音乐播放器----歌词在线下载
- Linux telnet命令
- Continue(Java)
- 实验4 数据库的连接查询
- C语言关键字详解(五)带你全面了解 volatile 关键字
- 论文笔记:DeepReID: Deep Filter Pairing Neural Network for Person Re-Identification
- Android Clipping
- 搜狗拼音输入法传统版 6.7i(0747) 去广告优化版
- iPhone11启动全线降价;瑞幸自曝22亿造假,股价暴跌75%;会议软件 Zoom 陷信任危机 | EA周报...
- starting to launch local task to process map join maximum memory =1029701632 的原因
- 比较两个字符串是否相等,strcmp wcscmp stricmp wcsicmp
- linux - 安装 telnet-server
热门文章
- 关于COM的Reg-Free(免注册)技术简介及实例讲解。
- Remote branch Develop not found in upstream origin
- 线段树线段树的创建线段树的查询单节点更新区间更新
- CentOS7编译安装libc++和libc++abi
- Linux设备中的并发控制
- 1296: [SCOI2009]粉刷匠
- 通过PHP自带的$_SERVER判断 手机访问网站自动跳转到手机版
- 绑定到异步的ObservableCollection
- ubuntu16.04安装FastDFS-5.08
- 打不开文件F:\vmware -副本\Ubuntu 16.vmdk:该虚拟机的磁盘已经由虚拟机或者快照使用