iw5-mod/deps/HDiffPatch/libHDiffPatch/HDiff/diff.cpp

1685 lines
77 KiB
C++

//diff.cpp
//
/*
The MIT License (MIT)
Copyright (c) 2012-2018 HouSisong
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include "diff.h"
#include <string.h> //strlen memcmp
#include <stdio.h> //fprintf
#include <algorithm> //std::max std::sort
#include <vector>
#include "private_diff/suffix_string.h"
#include "private_diff/bytes_rle.h"
#include "private_diff/compress_detect.h"
#include "private_diff/pack_uint.h"
#include "private_diff/mem_buf.h"
#include "../HPatch/patch.h"
#include "../HPatch/patch_private.h"
#include "private_diff/limit_mem_diff/covers.h"
#include "private_diff/limit_mem_diff/digest_matcher.h"
#include "private_diff/limit_mem_diff/stream_serialize.h"
#include "../../libParallel/parallel_import.h"
#if (_IS_USED_MULTITHREAD)
#include <thread> //if used vc++, need >= vc2012
#include <atomic>
#endif
using namespace hdiff_private;
static const char* kHDiffVersionType ="HDIFF13";
static const char* kHDiffSFVersionType="HDIFFSF20";
#define checki(value,info) { if (!(value)) { throw std::runtime_error(info); } }
#define check(value) checki(value,"check "#value" error!")
#if (_SSTRING_FAST_MATCH>0)
static const int kMinMatchLen = (_SSTRING_FAST_MATCH>kCoverMinMatchLen)?_SSTRING_FAST_MATCH:kCoverMinMatchLen;
#else
static const int kMinMatchLen = kCoverMinMatchLen; //最小搜寻相等长度。
#endif
static const int kMinMatchScore = 2; //最小搜寻覆盖收益.
namespace{
typedef unsigned char TByte;
typedef size_t TUInt;
typedef ptrdiff_t TInt;
static const int kMaxLinkSpaceLength=(1<<9)-1; //跨覆盖线合并时,允许合并的最远距离.
//覆盖线.
struct TOldCover {
TInt oldPos;
TInt newPos;
TInt length;
inline TOldCover():oldPos(0),newPos(0),length(0) { }
inline TOldCover(TInt _oldPos,TInt _newPos,TInt _length)
:oldPos(_oldPos),newPos(_newPos),length(_length) { }
inline TOldCover(const TOldCover& cover)
:oldPos(cover.oldPos),newPos(cover.newPos),length(cover.length) { }
inline bool isCanLink(const TOldCover& next)const{//覆盖线是否可以连接.
return isCollinear(next)&&(linkSpaceLength(next)<=kMaxLinkSpaceLength);
}
inline bool isCollinear(const TOldCover& next)const{//覆盖线是否在同一条直线上.
return cover_is_collinear(*this,next);
}
inline TInt linkSpaceLength(const TOldCover& next)const{//覆盖线间的间距.
return next.oldPos-(oldPos+length);
}
inline void Link(const TOldCover& next){//共线的2覆盖线合并链接.
assert(isCollinear(next));
assert(oldPos<=next.oldPos);
length = (next.oldPos-oldPos)+next.length;
}
};
struct TDiffData{
const TByte* newData;
const TByte* newData_end;
const TByte* oldData;
const TByte* oldData_end;
};
//查找相等的字符串长度.
template<TInt kMaxEqLenLimit>
static TInt getEqualLengthLimit(const TByte* x,const TByte* x_end,
const TByte* y,const TByte* y_end){
const TInt xLen=(TInt)(x_end-x);
const TInt yLen=(TInt)(y_end-y);
TInt maxEqLen=(xLen<yLen)?xLen:yLen;
if (kMaxEqLenLimit)
maxEqLen=(maxEqLen<=kMaxEqLenLimit)?maxEqLen:kMaxEqLenLimit;
for (TInt i=0; i<maxEqLen; ++i) {
if (x[i]!=y[i])
return i;
}
return maxEqLen;
}
#define getEqualLength(x,x_end,y,y_end) getEqualLengthLimit<0>(x,x_end,y,y_end)
struct TDiffLimit{
IDiffSearchCoverListener* listener;
size_t newPos;
size_t newEnd;
size_t recoverOldPos;
size_t recoverOldEnd;
TCompressDetect& nocover_detect;
TCompressDetect& cover_detect;
TOldCover lastCover_back;
int kMaxMatchDeep;
};
//得到最长的一个匹配长度和其位置.
static TInt getBestMatch(TInt* out_pos,const TSuffixString& sstring,
const TByte* newData,const TByte* newData_end,
TInt curNewPos,TDiffLimit* diffLimit=0,size_t* out_limitSkip=0){
TInt sai=sstring.lower_bound(newData,newData_end);
if (sai<0) return 0;
const TInt matchDeep = diffLimit?diffLimit->kMaxMatchDeep:2;
const TInt kLimitOldPos=(TInt)(diffLimit?diffLimit->recoverOldPos:0);
const TInt kLimitOldEnd=(TInt)(diffLimit?diffLimit->recoverOldEnd:sstring.SASize());
const TByte* src_begin=sstring.src_begin();
const TByte* src_end=sstring.src_end();
TInt bestLength= kMinMatchLen -1;
TInt bestOldPos=-1;
bool leftOk = false;
bool rightOk = false;
for (TInt mdi= 0; mdi< matchDeep; ++mdi) {
if (mdi&1){
if (rightOk) continue;
} else {
if (leftOk) continue;
}
TInt i = sai + (1-(mdi&1)*2) * ((mdi+1)/2);
if ((i<0)|(i>=(src_end-src_begin))) continue;
TInt curOldPos=sstring.SA(i);
TInt curLength;
#define kTryEqLenLimit (1024*1+17)
if (0==diffLimit){
curLength=getEqualLength(newData,newData_end,src_begin+curOldPos,src_end);
}else{
curLength=getEqualLengthLimit<kTryEqLenLimit>(newData,newData_end,src_begin+curOldPos,src_end);
if ((kLimitOldPos<=curOldPos)&&(curOldPos<kLimitOldEnd)){
if (curLength==kTryEqLenLimit)
*out_limitSkip=kTryEqLenLimit/2;
continue;
}
}
if (curLength>bestLength){
if (diffLimit){
hpatch_TCover cover={(size_t)curOldPos,(size_t)curNewPos,(size_t)curLength};
hpatch_StreamPos_t hitPos;
diffLimit->listener->limitCover(diffLimit->listener,&cover,&hitPos);
if (hitPos<=(size_t)bestLength)
continue;
if (hitPos==kTryEqLenLimit){
curLength=getEqualLength(newData,newData_end,src_begin+curOldPos,src_end);
hpatch_TCover cover={(size_t)curOldPos,(size_t)curNewPos,(size_t)curLength};
diffLimit->listener->limitCover(diffLimit->listener,&cover,&hitPos);
}
curLength=(TInt)hitPos;
}
bestLength = curLength;
bestOldPos= curOldPos;
}
if (mdi&1){
rightOk=true; if (leftOk) break;
} else {
leftOk=true; if (rightOk) break;
}
}
*out_pos=bestOldPos;
return bestLength;
}
//粗略估算覆盖线的控制数据成本;
inline static TInt getCoverCtrlCost(const TOldCover& cover,const TOldCover& lastCover){
static const int kUnLinkOtherScore=0;//0--2
return _getIntCost<TInt,TUInt>((TInt)(cover.oldPos-lastCover.oldPos))
+ _getUIntCost((TUInt)cover.length)
+ _getUIntCost((TUInt)(cover.newPos-lastCover.newPos)) + kUnLinkOtherScore;
}
//粗略估算 区域内当作覆盖时的可能存储成本.
inline static TInt getCoverCost(const TOldCover& cover,const TDiffData& diff){
return (TInt)getRegionRleCost(diff.newData+cover.newPos,cover.length,diff.oldData+cover.oldPos);
}
//尝试延长lastCover来完全代替matchCover;
static bool tryLinkExtend(TOldCover& lastCover,const TOldCover& matchCover,const TDiffData& diff,TDiffLimit* diffLimit){
if (lastCover.length<=0) return false;
const TInt linkSpaceLength=(matchCover.newPos-(lastCover.newPos+lastCover.length));
assert(linkSpaceLength>=0);
if (linkSpaceLength>kMaxLinkSpaceLength)
return false;
TInt linkOldPos=lastCover.oldPos+lastCover.length+linkSpaceLength;
if (linkOldPos+matchCover.length>(diff.oldData_end-diff.oldData))
return false;
const bool isCollinear=lastCover.isCollinear(matchCover);
if (diffLimit){
size_t cnewPos=lastCover.newPos+lastCover.length;
hpatch_TCover cover={(size_t)(lastCover.oldPos+lastCover.length),cnewPos,
(size_t)(linkSpaceLength+(isCollinear?0:matchCover.length))};
if (!diffLimit->listener->limitCover(diffLimit->listener,&cover,0))
return false;
}
if (isCollinear){//已经共线;
lastCover.Link(matchCover);
return true;
}
TInt matchCost=getCoverCtrlCost(matchCover,lastCover);
TInt lastLinkCost=(TInt)getRegionRleCost(diff.newData+matchCover.newPos,matchCover.length,diff.oldData+linkOldPos);
if (lastLinkCost>matchCost)
return false;
TInt len=lastCover.length+linkSpaceLength+(matchCover.length*2/3);//扩展大部分,剩下的可能扩展留给extend_cover.
len+=getEqualLength(diff.newData+lastCover.newPos+len,diff.newData_end,
diff.oldData+lastCover.oldPos+len,diff.oldData_end);
if (diffLimit){
TInt limitLen=diffLimit->newEnd-lastCover.newPos;
len=len<limitLen?len:limitLen;
TInt safeLen=lastCover.length+linkSpaceLength+matchCover.length;
if (len>safeLen){
hpatch_TCover cover={(size_t)(lastCover.oldPos+safeLen),(size_t)(lastCover.newPos+safeLen),
(size_t)(len-safeLen)};
hpatch_StreamPos_t hitPos;
diffLimit->listener->limitCover(diffLimit->listener,&cover,&hitPos);
len=(TInt)(safeLen+hitPos);
}
}
while ((len>0) && (diff.newData[lastCover.newPos+len-1]
!=diff.oldData[lastCover.oldPos+len-1])) {
--len;
}
lastCover.length=len;
return true;
}
//尝试设置lastCover为matchCover所在直线的延长线,实现共线(增加合并可能等);
static void tryCollinear(TOldCover& lastCover,const TOldCover& matchCover,const TDiffData& diff,TDiffLimit* diffLimit){
if (lastCover.length<=0) return;
if (lastCover.isCollinear(matchCover)) return; //已经共线;
TInt linkOldPos=matchCover.oldPos-(matchCover.newPos-lastCover.newPos);
if ((linkOldPos<0)||(linkOldPos+lastCover.length>(diff.oldData_end-diff.oldData)))
return;
if (diffLimit){
hpatch_TCover cover={ (size_t)linkOldPos,(size_t)lastCover.newPos,(size_t)lastCover.length};
if (!diffLimit->listener->limitCover(diffLimit->listener,&cover,0))
return;
}
TInt lastCost=getCoverCost(lastCover,diff);
TInt matchLinkCost=(TInt)getRegionRleCost(diff.newData+lastCover.newPos,lastCover.length,diff.oldData+linkOldPos);
if (lastCost>=matchLinkCost)
lastCover.oldPos=linkOldPos;
}
//寻找合适的覆盖线.
static void _search_cover(std::vector<TOldCover>& covers,const TDiffData& diff,
const TSuffixString& sstring,TDiffLimit* diffLimit,bool isCanExtendCover){
if (sstring.SASize()<=0) return;
TInt newPos=diffLimit?diffLimit->newPos:0;
const TInt newEnd=diffLimit?diffLimit->newEnd:(diff.newData_end-diff.newData);
if (newEnd-newPos<=kMinMatchLen) return;
const TInt maxSearchNewPos=newEnd-kMinMatchLen;
const size_t cover_begin=covers.size();
TOldCover lastCover(0,0,0);
while (newPos<=maxSearchNewPos) {
TInt matchOldPos=0;
size_t limitSkip=1;
TInt matchEqLength=getBestMatch(&matchOldPos,sstring,diff.newData+newPos,diff.newData+newEnd,newPos,
diffLimit,&limitSkip);
if (matchEqLength<kMinMatchLen){
newPos+=limitSkip;
continue;
}
TOldCover matchCover(matchOldPos,newPos,matchEqLength);
if (matchEqLength-getCoverCtrlCost(matchCover,lastCover)<kMinMatchScore){
++newPos;//下一个需要匹配的字符串(逐位置匹配速度会比较慢).
continue;
}//else matched
if (isCanExtendCover){
if (tryLinkExtend(lastCover,matchCover,diff,diffLimit)){//use link
if (covers.size()==cover_begin)
covers.push_back(lastCover);
else
covers.back()=lastCover;
}else{ //use match
if (covers.size()>cover_begin)//尝试共线;
tryCollinear(covers.back(),matchCover,diff,diffLimit);
covers.push_back(matchCover);
}
}else{
covers.push_back(matchCover);
}
lastCover=covers.back();
newPos=std::max(newPos+1,lastCover.newPos+lastCover.length);//选出的cover不允许重叠,这可能不是最优策略;
}
}
//选择合适的覆盖线,去掉不合适的.
static void _select_cover(std::vector<TOldCover>& covers,size_t cover_begin,const TDiffData& diff,int kMinSingleMatchScore,
TCompressDetect& nocover_detect,TCompressDetect& cover_detect,
TDiffLimit* diffLimit,bool isCanExtendCover){
TOldCover lastCover(0,0,0);
if (diffLimit)
lastCover=diffLimit->lastCover_back;
const size_t coverSize_old=covers.size();
size_t insertIndex=cover_begin;
for (size_t i=cover_begin;i<coverSize_old;++i){
if (covers[i].length<=0) continue;//处理已经del的.
bool isNeedSave=false;
bool isCanLink=false;
if (isCanExtendCover&&(!isNeedSave)){//向前合并可能.
if ((insertIndex>cover_begin)&&(covers[insertIndex-1].isCanLink(covers[i]))){
if (diffLimit){
const TOldCover& fc=covers[insertIndex-1];
hpatch_TCover cover={(size_t)(fc.oldPos+fc.length),(size_t)(fc.newPos+fc.length),
(size_t)fc.linkSpaceLength(covers[i])};
if (diffLimit->listener->limitCover(diffLimit->listener,&cover,0)){
isCanLink=true;
isNeedSave=true;
}
}else{
isCanLink=true;
isNeedSave=true;
}
}
}
if (isCanExtendCover&&(i+1<coverSize_old)){//查询向后合并可能link
for (size_t j=i+1;j<coverSize_old; ++j) {
if (!covers[i].isCanLink(covers[j])) break;
if (diffLimit){
const TOldCover& fc=covers[i];
hpatch_TCover cover={(size_t)(fc.oldPos+fc.length),(size_t)(fc.newPos+fc.length),
(size_t)fc.linkSpaceLength(covers[j])};
if (diffLimit->listener->limitCover(diffLimit->listener,&cover,0)){
covers[i].Link(covers[j]);
covers[j].length=0;//del
}else{
break;
}
}else{
covers[i].Link(covers[j]);
covers[j].length=0;//del
}
}
}
if (!isNeedSave){//单覆盖是否保留.
TInt noCoverCost=nocover_detect.cost(diff.newData+covers[i].newPos,covers[i].length);
TInt coverCost=cover_detect.cost(diff.newData+covers[i].newPos,covers[i].length,
diff.oldData+covers[i].oldPos);
TInt coverSorce=noCoverCost-coverCost-getCoverCtrlCost(covers[i],lastCover);
isNeedSave=(coverSorce>=kMinSingleMatchScore);
}
if (isNeedSave){
if (isCanLink){//link合并.
covers[insertIndex-1].Link(covers[i]);
cover_detect.add_chars(diff.newData+lastCover.newPos+lastCover.length,
covers[insertIndex-1].length-lastCover.length,
diff.oldData+lastCover.oldPos+lastCover.length);
}else{
covers[insertIndex++]=covers[i];
nocover_detect.add_chars(diff.newData+lastCover.newPos+lastCover.length,
covers[i].newPos-(lastCover.newPos+lastCover.length));
cover_detect.add_chars(diff.newData+covers[i].newPos,covers[i].length,
diff.oldData+covers[i].oldPos);
}
lastCover=covers[insertIndex-1];
}
}
covers.resize(insertIndex);
}
static void select_cover(std::vector<TOldCover>& covers,size_t cover_begin,const TDiffData& diff,
int kMinSingleMatchScore,TDiffLimit* diffLimit,bool isCanExtendCover){
if (diffLimit==0){
TCompressDetect nocover_detect;
TCompressDetect cover_detect;
_select_cover(covers,cover_begin,diff,kMinSingleMatchScore,nocover_detect,cover_detect,0,isCanExtendCover);
}else{
_select_cover(covers,cover_begin,diff,kMinSingleMatchScore,diffLimit->nocover_detect,
diffLimit->cover_detect,diffLimit,isCanExtendCover);
}
}
typedef size_t TFixedFloatSmooth; //定点数.
static const TFixedFloatSmooth kFixedFloatSmooth_base=1024;//定点数小数点位置.
//得到可以扩展位置的长度.
static TInt getCanExtendLength(TInt oldPos,TInt newPos,int inc,TInt newPos_min,TInt lastNewEnd,
const TDiffData& diff,const TFixedFloatSmooth kExtendMinSameRatio){
static const unsigned int kSmoothLength=4;
TFixedFloatSmooth curBestSameRatio=0;
TInt curBestLength=0;
TUInt curSameCount=0;
const TFixedFloatSmooth kLimitSameCount=(~(TFixedFloatSmooth)0)/kFixedFloatSmooth_base;
for (TUInt length=1; (oldPos>=0)&&(oldPos<(diff.oldData_end-diff.oldData))
&&(newPos>=newPos_min)&&(newPos<lastNewEnd); ++length,oldPos+=inc,newPos+=inc) {
if (diff.oldData[oldPos]==diff.newData[newPos]){
++curSameCount;
if (curSameCount>= kLimitSameCount) break; //for curSameCount*kFixedFloatSmooth_base
const TFixedFloatSmooth curSameRatio= (curSameCount*kFixedFloatSmooth_base)
/(length+kSmoothLength);
if (curSameRatio>=curBestSameRatio){
curBestSameRatio=curSameRatio;
curBestLength=length;
}
}
}
if ((curBestSameRatio<kExtendMinSameRatio)||(curBestLength<=2)){
curBestLength=0;
}
return curBestLength;
}
//尝试延长覆盖区域.
static void extend_cover(std::vector<TOldCover>& covers,size_t cover_begin,const TDiffData& diff,
const TFixedFloatSmooth kExtendMinSameRatio,TDiffLimit* diffLimit=0){
TInt lastNewEnd=diffLimit?diffLimit->newPos:0;
for (size_t i=cover_begin; i<covers.size(); ++i) {
TInt newPos_next;
if (i+1<covers.size())
newPos_next=covers[i+1].newPos;
else
newPos_next=diffLimit?diffLimit->newEnd:(TInt)(diff.newData_end-diff.newData);
TOldCover& curCover=covers[i];
if (diffLimit){
TInt limit_front=std::min(curCover.newPos-lastNewEnd,curCover.oldPos);
if (limit_front>0){
hpatch_TCover cover={(size_t)(curCover.oldPos-limit_front),
(size_t)(curCover.newPos-limit_front),(size_t)limit_front};
hpatch_StreamPos_t lenLimit;
diffLimit->listener->limitCover_front(diffLimit->listener,&cover,&lenLimit);
lastNewEnd=curCover.newPos-(TInt)lenLimit;
}
TInt limit_back=newPos_next-(curCover.newPos+curCover.length);
if ((curCover.oldPos+curCover.length)+limit_back>(diff.oldData_end-diff.oldData))
limit_back=(diff.oldData_end-diff.oldData)-(curCover.oldPos+curCover.length);
if (limit_back>0){
hpatch_TCover cover={(size_t)(curCover.oldPos+curCover.length),
(size_t)(curCover.newPos+curCover.length),(size_t)limit_back};
hpatch_StreamPos_t lenLimit;
diffLimit->listener->limitCover(diffLimit->listener,&cover,&lenLimit);
newPos_next=(curCover.newPos+curCover.length)+ (TInt)lenLimit;
}
}
//向前延伸.
TInt extendLength_front=getCanExtendLength(curCover.oldPos-1,curCover.newPos-1,
-1,lastNewEnd,newPos_next,diff,kExtendMinSameRatio);
if (extendLength_front>0){
curCover.oldPos-=extendLength_front;
curCover.newPos-=extendLength_front;
curCover.length+=extendLength_front;
}
//向后延伸.
TInt extendLength_back=getCanExtendLength(curCover.oldPos+curCover.length,
curCover.newPos+curCover.length,
1,lastNewEnd,newPos_next,diff,kExtendMinSameRatio);
if (extendLength_back>0){
curCover.length+=extendLength_back;
}
lastNewEnd=curCover.newPos+curCover.length;
}
}
template<class _TCover,class _TInt>
static void assert_cover_safe(const _TCover& cover,_TInt lastNewEnd,_TInt newSize,_TInt oldSize){
check(cover.length>0);
check(cover.newPos>=lastNewEnd);
check(cover.newPos<newSize);
check(cover.newPos+cover.length>0);
check(cover.newPos+cover.length<=newSize);
check(cover.oldPos>=0);
check(cover.oldPos<oldSize);
check(cover.oldPos+cover.length>0);
check(cover.oldPos+cover.length<=oldSize);
}
static void assert_covers_safe(const TCovers& covers,hpatch_StreamPos_t newSize,hpatch_StreamPos_t oldSize){
hpatch_StreamPos_t lastNewEnd=0;
for (size_t i=0;i<covers.coverCount();++i){
TCover cover;
covers.covers(i,&cover);
assert_cover_safe(cover,lastNewEnd,newSize,oldSize);
lastNewEnd=cover.newPos+cover.length;
}
}
static void assert_covers_safe(const std::vector<TOldCover>& covers,hpatch_StreamPos_t newSize,hpatch_StreamPos_t oldSize){
const TCovers _covers((void*)covers.data(),covers.size(),
sizeof(*covers.data())==sizeof(hpatch_TCover32));
assert_covers_safe(_covers,newSize,oldSize);
}
//diff结果序列化输出.
static void serialize_diff(const TDiffData& diff,const std::vector<TOldCover>& covers,std::vector<TByte>& out_diff){
const TUInt coverCount=(TUInt)covers.size();
std::vector<TByte> length_buf;
std::vector<TByte> inc_newPos_buf;
std::vector<TByte> inc_oldPos_buf;
{
TInt oldPosBack=0;
TInt lastNewEnd=0;
for (TUInt i=0; i<coverCount; ++i) {
packUInt(length_buf, (TUInt)covers[i].length);
assert(covers[i].newPos>=lastNewEnd);
packUInt(inc_newPos_buf,(TUInt)(covers[i].newPos-lastNewEnd)); //save inc_newPos
if (covers[i].oldPos>=oldPosBack){ //save inc_oldPos
packUIntWithTag(inc_oldPos_buf,(TUInt)(covers[i].oldPos-oldPosBack), 0, 1);
}else{
packUIntWithTag(inc_oldPos_buf,(TUInt)(oldPosBack-covers[i].oldPos), 1, 1);//sub safe
}
oldPosBack=covers[i].oldPos;
lastNewEnd=covers[i].newPos+covers[i].length;
}
}
const TCovers _covers((void*)covers.data(),covers.size(),
sizeof(*covers.data())==sizeof(hpatch_TCover32));
hpatch_TStreamInput _newDataStream;
mem_as_hStreamInput(&_newDataStream,diff.newData,diff.newData_end);
TNewDataDiffStream newDataDiffStream(_covers,&_newDataStream);
packUInt(out_diff, (TUInt)coverCount);
packUInt(out_diff, (TUInt)length_buf.size());
packUInt(out_diff, (TUInt)inc_newPos_buf.size());
packUInt(out_diff, (TUInt)inc_oldPos_buf.size());
packUInt(out_diff, newDataDiffStream.streamSize);
pushBack(out_diff,length_buf);
pushBack(out_diff,inc_newPos_buf);
pushBack(out_diff,inc_oldPos_buf);
pushBack(out_diff,&newDataDiffStream);
TNewDataSubDiffStream_mem newDataSubDiff(diff.newData,diff.newData_end,
diff.oldData,diff.oldData_end,_covers);
bytesRLE_save(out_diff,&newDataSubDiff,kRle_bestSize);
}
inline static void pushCompressCode(std::vector<TByte>& out_diff,
const std::vector<TByte>& compress_code,
const std::vector<TByte>& data){
if (compress_code.empty())
pushBack(out_diff,data);
else
pushBack(out_diff,compress_code);
}
inline static void pushCompressCode(std::vector<TByte>& out_diff,
const std::vector<TByte>& compress_code,
const hpatch_TStreamInput* data){
if (compress_code.empty())
pushBack(out_diff,data);
else
pushBack(out_diff,compress_code);
}
template<class T>
static void _outType(std::vector<TByte>& out_data,T* compressPlugin,const char* versionType=kHDiffVersionType){
//type version
pushCStr(out_data,versionType);
pushCStr(out_data,"&");
{//compressType
const char* compressType="";
if (compressPlugin)
compressType=compressPlugin->compressType();
size_t compressTypeLen=strlen(compressType);
check(compressTypeLen<=hpatch_kMaxPluginTypeLength);
check(0==strchr(compressType,'&'));
pushCStr(out_data,compressType);
}
const TByte _cstrEndTag='\0';//c string end tag
pushBack(out_data,&_cstrEndTag,(&_cstrEndTag)+1);
}
static void serialize_compressed_diff(const TDiffData& diff,std::vector<TOldCover>& covers,std::vector<TByte>& out_diff,
const hdiff_TCompress* compressPlugin){
const TUInt coverCount=(TUInt)covers.size();
std::vector<TByte> cover_buf;
{
TInt lastOldEnd=0;
TInt lastNewEnd=0;
for (TUInt i=0; i<coverCount; ++i) {
if (covers[i].oldPos>=lastOldEnd){ //save inc_oldPos
packUIntWithTag(cover_buf,(TUInt)(covers[i].oldPos-lastOldEnd), 0, 1);
}else{
packUIntWithTag(cover_buf,(TUInt)(lastOldEnd-covers[i].oldPos), 1, 1);//sub safe
}
assert(covers[i].newPos>=lastNewEnd);
packUInt(cover_buf,(TUInt)(covers[i].newPos-lastNewEnd)); //save inc_newPos
packUInt(cover_buf,(TUInt)covers[i].length);
lastOldEnd=covers[i].oldPos+covers[i].length;//! +length
lastNewEnd=covers[i].newPos+covers[i].length;
}
}
std::vector<TByte> rle_ctrlBuf;
std::vector<TByte> rle_codeBuf;
{
const TCovers _covers((void*)covers.data(),covers.size(),
sizeof(*covers.data())==sizeof(hpatch_TCover32));
TNewDataSubDiffStream_mem newDataSubDiff(diff.newData,diff.newData_end,
diff.oldData,diff.oldData_end,_covers);
bytesRLE_save(rle_ctrlBuf,rle_codeBuf,&newDataSubDiff,kRle_bestSize);
}
std::vector<TByte> compress_cover_buf;
std::vector<TByte> compress_rle_ctrlBuf;
std::vector<TByte> compress_rle_codeBuf;
std::vector<TByte> compress_newDataDiff;
do_compress(compress_cover_buf,cover_buf,compressPlugin);
do_compress(compress_rle_ctrlBuf,rle_ctrlBuf,compressPlugin);
do_compress(compress_rle_codeBuf,rle_codeBuf,compressPlugin);
const TCovers _covers((void*)covers.data(),covers.size(),
sizeof(*covers.data())==sizeof(hpatch_TCover32));
hpatch_TStreamInput _newDataStream;
mem_as_hStreamInput(&_newDataStream,diff.newData,diff.newData_end);
TNewDataDiffStream newDataDiffStream(_covers,&_newDataStream);
do_compress(compress_newDataDiff,&newDataDiffStream,compressPlugin);
_outType(out_diff,compressPlugin);
const TUInt newDataSize=(size_t)(diff.newData_end-diff.newData);
const TUInt oldDataSize=(size_t)(diff.oldData_end-diff.oldData);
packUInt(out_diff, newDataSize);
packUInt(out_diff, oldDataSize);
packUInt(out_diff, coverCount);
packUInt(out_diff, (TUInt)cover_buf.size());
packUInt(out_diff, (TUInt)compress_cover_buf.size());
packUInt(out_diff, (TUInt)rle_ctrlBuf.size());
packUInt(out_diff, (TUInt)compress_rle_ctrlBuf.size());
packUInt(out_diff, (TUInt)rle_codeBuf.size());
packUInt(out_diff, (TUInt)compress_rle_codeBuf.size());
packUInt(out_diff, newDataDiffStream.streamSize);
packUInt(out_diff, (TUInt)compress_newDataDiff.size());
pushCompressCode(out_diff,compress_cover_buf,cover_buf);
pushCompressCode(out_diff,compress_rle_ctrlBuf,rle_ctrlBuf);
pushCompressCode(out_diff,compress_rle_codeBuf,rle_codeBuf);
pushCompressCode(out_diff,compress_newDataDiff,&newDataDiffStream);
}
static void _dispose_cover(std::vector<TOldCover>& covers,size_t cover_begin,const TDiffData& diff,
int kMinSingleMatchScore,TDiffLimit* diffLimit,bool isCanExtendCover){
if (isCanExtendCover){
TFixedFloatSmooth kExtendMinSameRatio=kMinSingleMatchScore*36+254;
if (kExtendMinSameRatio<200) kExtendMinSameRatio=200;
if (kExtendMinSameRatio>800) kExtendMinSameRatio=800;
extend_cover(covers,cover_begin,diff,kExtendMinSameRatio,diffLimit);//先尝试扩展.
select_cover(covers,cover_begin,diff,kMinSingleMatchScore,diffLimit,isCanExtendCover);
extend_cover(covers,cover_begin,diff,kExtendMinSameRatio,diffLimit);//select_cover会删除一些覆盖线,所以重新扩展.
}else{
select_cover(covers,cover_begin,diff,kMinSingleMatchScore,diffLimit,isCanExtendCover);
}
}
static void search_and_dispose_cover(std::vector<TOldCover>& covers,const TDiffData& diff,
const TSuffixString& sstring,int kMinSingleMatchScore,
TDiffLimit* diffLimit,bool isCanExtendCover){
const size_t cover_begin=covers.size();
_search_cover(covers,diff,sstring,diffLimit,isCanExtendCover);
if (covers.size()>cover_begin)
_dispose_cover(covers,cover_begin,diff,kMinSingleMatchScore,diffLimit,isCanExtendCover);
}
#define first_search_and_dispose_cover(covers,diff,sstring,kMinSingleMatchScore,isCanExtendCover) search_and_dispose_cover(covers,diff,sstring,kMinSingleMatchScore,0,isCanExtendCover);
#if (_IS_USED_MULTITHREAD)
const size_t kPartPepeatSize=1024*2;
struct mt_data_t{
const TDiffData* diff;
const TSuffixString* sstring;
ICoverLinesListener* listener;
int kMinSingleMatchScore;
size_t workBlockSize;
bool isCanExtendCover;
std::atomic<size_t> workIndex;
bool nextBlock(hdiff_TRange* out_newRange){
const size_t kNewSize=(diff->newData_end-diff->newData);
if (listener)
return listener->next_search_block_MT(listener,out_newRange);
size_t i=workIndex++;
hpatch_StreamPos_t pos=i*(hpatch_StreamPos_t)workBlockSize;
if (pos<kNewSize){
out_newRange->beginPos=pos;
pos+=workBlockSize+kPartPepeatSize;
pos=(pos<=kNewSize)?pos:kNewSize;
out_newRange->endPos=pos;
return true;
}else{
return false;
}
}
};
static void _fsearch_and_dispose_cover_thread(std::vector<TOldCover>* _covers,mt_data_t* mt){
std::vector<TOldCover>& covers=*_covers;
const TDiffData& diff=*mt->diff;
hdiff_TRange newRange;
while (mt->nextBlock(&newRange)){
TDiffData diff_part=diff;
diff_part.newData=diff.newData+(size_t)newRange.beginPos;
diff_part.newData_end=diff.newData+(size_t)newRange.endPos;
size_t coverCountBack=covers.size();
first_search_and_dispose_cover(covers,diff_part,*mt->sstring,mt->kMinSingleMatchScore,mt->isCanExtendCover);
for (size_t i=coverCountBack;i<covers.size();++i)
covers[i].newPos+=(TInt)newRange.beginPos;
}
}
#endif
static void first_search_and_dispose_cover_MT(std::vector<TOldCover>& covers,const TDiffData& diff,
const TSuffixString& sstring,int kMinSingleMatchScore,
ICoverLinesListener* listener,size_t threadNum,bool isCanExtendCover){
#if (_IS_USED_MULTITHREAD)
const size_t kMinParallelSize=1024*1024*2;
const size_t kBestParallelSize=1024*1024*8;
size_t newSize=diff.newData_end-diff.newData;
if ((threadNum>1)&&(diff.oldData!=diff.oldData_end)&&(newSize>=kMinParallelSize)){
const size_t maxThreanNum=newSize/(kMinParallelSize/2);
threadNum=(threadNum<=maxThreanNum)?threadNum:maxThreanNum;
size_t workCount=(newSize+kBestParallelSize-1)/kBestParallelSize;
workCount=(threadNum>workCount)?threadNum:workCount;
const size_t threadCount=threadNum-1;
std::vector<std::thread> threads(threadCount);
std::vector<std::vector<TOldCover> > threadCovers(threadCount);
mt_data_t mt_data;
mt_data.diff=&diff;
mt_data.sstring=&sstring;
mt_data.listener=(listener&&listener->next_search_block_MT)?listener:0;
mt_data.kMinSingleMatchScore=kMinSingleMatchScore;
mt_data.workBlockSize=(newSize+workCount-1)/workCount;
mt_data.workIndex=0;
mt_data.isCanExtendCover=isCanExtendCover;
if (mt_data.listener&&listener->begin_search_block)
listener->begin_search_block(listener,newSize,mt_data.workBlockSize,kPartPepeatSize);
for (size_t i=0;i<threadCount;i++)
threads[i]=std::thread(_fsearch_and_dispose_cover_thread,&threadCovers[i],&mt_data);
_fsearch_and_dispose_cover_thread(&covers,&mt_data);
for (size_t i=0;i<threadCount;i++){
threads[i].join();
covers.insert(covers.end(),threadCovers[i].begin(),threadCovers[i].end());
{ std::vector<TOldCover> tmp; tmp.swap(threadCovers[i]); }
}
tm_collate_covers(covers);
}else
#endif
{
first_search_and_dispose_cover(covers,diff,sstring,kMinSingleMatchScore,isCanExtendCover);
}
}
static const hpatch_StreamPos_t _kNullCoverHitEndPos =hpatch_kNullStreamPos;
struct TDiffResearchCover:public IDiffResearchCover{
TDiffResearchCover(TDiffData& diff_,std::vector<TOldCover>& covers_,const TSuffixString& sstring_,
int kMinSingleMatchScore_,int kMaxMatchDeep_,bool _isCanExtendCover)
:diff(diff_), covers(covers_),sstring(sstring_),
kMinSingleMatchScore(kMinSingleMatchScore_),kMaxMatchDeep(kMaxMatchDeep_),
limitCoverIndex_back(~(size_t)0),limitCoverHitEndPos_back(_kNullCoverHitEndPos),
isCanExtendCover(_isCanExtendCover){ researchCover=_researchCover; }
void _researchRange(TDiffLimit* diffLimit){
search_and_dispose_cover(curCovers,diff,sstring,kMinSingleMatchScore,diffLimit,isCanExtendCover);
if (curCovers.empty()) return;
reCovers.insert(reCovers.end(),curCovers.begin(),curCovers.end());
curCovers.clear();
}
inline void endResearchCover(){
if (limitCoverHitEndPos_back!=_kNullCoverHitEndPos){
TOldCover& cover=covers[limitCoverIndex_back];
cover.oldPos+=(TInt)limitCoverHitEndPos_back;
cover.newPos+=(TInt)limitCoverHitEndPos_back;
cover.length-=(TInt)limitCoverHitEndPos_back;
limitCoverHitEndPos_back=_kNullCoverHitEndPos;
}
}
inline void doResearchCover(IDiffSearchCoverListener* listener,size_t limitCoverIndex,
hpatch_StreamPos_t endPosBack,hpatch_StreamPos_t hitPos,hpatch_StreamPos_t hitLen){
if (limitCoverIndex_back!=limitCoverIndex)
endResearchCover();
limitCoverIndex_back=limitCoverIndex;
limitCoverHitEndPos_back=hitPos+hitLen;
const TOldCover& cover=covers[limitCoverIndex];
TOldCover lastCover_back(0,0,0);
if (endPosBack<hitPos){
lastCover_back.oldPos=cover.oldPos+(TInt)endPosBack;
lastCover_back.newPos=cover.newPos+(TInt)endPosBack;
lastCover_back.length=(TInt)(hitPos-endPosBack);
reCovers.push_back(lastCover_back);
}else {
assert(endPosBack==hitPos);
if (limitCoverIndex>0)
lastCover_back=covers[limitCoverIndex-1];
if ((!reCovers.empty())&&(reCovers.back().newPos>lastCover_back.newPos))
lastCover_back=reCovers.back();
}
TDiffLimit diffLimit={listener,cover.newPos+(size_t)hitPos,cover.newPos+(size_t)(hitPos+hitLen),
cover.oldPos+(size_t)hitPos,cover.oldPos+(size_t)(hitPos+hitLen),
nocover_detect,cover_detect,lastCover_back,kMaxMatchDeep};
_researchRange(&diffLimit);
}
static void _researchCover(struct IDiffResearchCover* diffi,struct IDiffSearchCoverListener* listener,size_t limitCoverIndex,
hpatch_StreamPos_t endPosBack,hpatch_StreamPos_t hitPos,hpatch_StreamPos_t hitLen){
TDiffResearchCover* self=(TDiffResearchCover*)diffi;
self->doResearchCover(listener,limitCoverIndex,endPosBack,hitPos,hitLen);
}
void researchFinish(){
endResearchCover();
size_t insert=0;
for (size_t i=0;i<covers.size();++i){
if (covers[i].length>0)
covers[insert++]=covers[i];
}
covers.resize(insert);
covers.insert(covers.end(),reCovers.begin(),reCovers.end());
std::inplace_merge(covers.begin(),covers.begin()+insert,
covers.end(),cover_cmp_by_new_t<TOldCover>());
}
TDiffData& diff;
std::vector<TOldCover>& covers;
const TSuffixString& sstring;
int kMinSingleMatchScore;
int kMaxMatchDeep;
std::vector<TOldCover> reCovers;
std::vector<TOldCover> curCovers;
size_t limitCoverIndex_back;
hpatch_StreamPos_t limitCoverHitEndPos_back;
const bool isCanExtendCover;
TCompressDetect nocover_detect;
TCompressDetect cover_detect;
};
struct TDiffInsertCover:public IDiffInsertCover{
inline TDiffInsertCover(std::vector<TOldCover>& _covers)
:covers(_covers){
insertCover=_insertCover;
}
static void* _insertCover(IDiffInsertCover* diffi,const void* pInsertCovers,size_t insertCoverCount,bool insertIsCover32){
TDiffInsertCover* self=(TDiffInsertCover*)diffi;
return self->_insertCover(pInsertCovers,insertCoverCount,insertIsCover32);
}
void* _insertCover(const void* pInsertCovers,size_t insertCoverCount,bool insertIsCover32){
const bool isCover32=sizeof(*covers.data())==sizeof(hpatch_TCover32);
if (insertIsCover32==isCover32){
covers.insert(covers.end(),(const TOldCover*)pInsertCovers,
((const TOldCover*)pInsertCovers)+insertCoverCount);
}else{
size_t oldSize=covers.size();
covers.resize(oldSize +insertCoverCount);
for (size_t i=0;i<insertCoverCount;i++){
if (insertIsCover32){
const hpatch_TCover32& s=((const hpatch_TCover32*)pInsertCovers)[i];
covers[oldSize+i]=TOldCover((TInt)s.oldPos,(TInt)s.newPos,(TInt)s.length);
}else{
const hpatch_TCover& s=((const hpatch_TCover*)pInsertCovers)[i];
covers[oldSize+i]=TOldCover((TInt)s.oldPos,(TInt)s.newPos,(TInt)s.length);
}
}
}
return covers.data();
}
std::vector<TOldCover>& covers;
};
static void get_diff(const TByte* newData,const TByte* newData_end,
const TByte* oldData,const TByte* oldData_end,
TDiffData& out_diff,std::vector<TOldCover>& covers,
int kMinSingleMatchScore,
bool isUseBigCacheMatch,ICoverLinesListener* listener,
const TSuffixString* sstring,size_t threadNum,bool isCanExtendCover=true){
assert(newData<=newData_end);
assert(oldData<=oldData_end);
TDiffData& diff=out_diff;
diff.newData=newData;
diff.newData_end=newData_end;
diff.oldData=oldData;
diff.oldData_end=oldData_end;
const bool isCover32=sizeof(*covers.data())==sizeof(hpatch_TCover32);
if (!isCover32)
assert(sizeof(*covers.data())==sizeof(hpatch_TCover));
{
TSuffixString _sstring_default(isUseBigCacheMatch);
if (sstring==0){
_sstring_default.resetSuffixString(oldData,oldData_end,threadNum);
sstring=&_sstring_default;
}
first_search_and_dispose_cover_MT(covers,diff,*sstring,kMinSingleMatchScore,listener,threadNum,isCanExtendCover);
assert_covers_safe(covers,diff.newData_end-diff.newData,diff.oldData_end-diff.oldData);
if (listener&&listener->search_cover_limit&&
listener->search_cover_limit(listener,covers.data(),covers.size(),isCover32)){
TDiffResearchCover diffResearchCover(diff,covers,*sstring,kMinSingleMatchScore,
listener->get_max_match_deep?listener->get_max_match_deep(listener):kDefaultMaxMatchDeepForLimit,
isCanExtendCover);
listener->research_cover(listener,&diffResearchCover,covers.data(),covers.size(),isCover32);
diffResearchCover.researchFinish();
}
sstring=0;
_sstring_default.clear();
}
if (listener&&listener->insert_cover){
TDiffInsertCover diffInsertCover(covers);
hpatch_StreamPos_t newDataSize=(size_t)(diff.newData_end-diff.newData);
hpatch_StreamPos_t oldDataSize=(size_t)(diff.oldData_end-diff.oldData);
listener->insert_cover(listener,&diffInsertCover,covers.data(),covers.size(),isCover32,
&newDataSize,&oldDataSize);
diff.newData_end=diff.newData+(size_t)newDataSize;
diff.oldData_end=diff.oldData+(size_t)oldDataSize;
assert_covers_safe(covers,diff.newData_end-diff.newData,diff.oldData_end-diff.oldData);
}
if (listener&&listener->search_cover_finish){
hpatch_StreamPos_t newDataSize=(size_t)(diff.newData_end-diff.newData);
hpatch_StreamPos_t oldDataSize=(size_t)(diff.oldData_end-diff.oldData);
size_t newCoverCount=covers.size();
listener->search_cover_finish(listener,covers.data(),&newCoverCount,isCover32,
&newDataSize,&oldDataSize);
check(newCoverCount<=covers.size());
covers.resize(newCoverCount);
diff.newData_end=diff.newData+(size_t)newDataSize;
diff.oldData_end=diff.oldData+(size_t)oldDataSize;
}
if (listener){
assert_covers_safe(covers,diff.newData_end-diff.newData,diff.oldData_end-diff.oldData);
}
}
}//end namespace
void create_diff(const TByte* newData,const TByte* newData_end,
const TByte* oldData,const TByte* oldData_end,
std::vector<TByte>& out_diff,
int kMinSingleMatchScore,bool isUseBigCacheMatch,size_t threadNum){
TDiffData diff;
std::vector<TOldCover> covers;
get_diff(newData,newData_end,oldData,oldData_end,diff,covers,
kMinSingleMatchScore,isUseBigCacheMatch,0,0,threadNum);
serialize_diff(diff,covers,out_diff);
}
void create_compressed_diff(const TByte* newData,const TByte* newData_end,
const TByte* oldData,const TByte* oldData_end,
std::vector<TByte>& out_diff,const hdiff_TCompress* compressPlugin,
int kMinSingleMatchScore,bool isUseBigCacheMatch,
ICoverLinesListener* listener,size_t threadNum){
TDiffData diff;
std::vector<TOldCover> covers;
get_diff(newData,newData_end,oldData,oldData_end,diff,covers,
kMinSingleMatchScore,isUseBigCacheMatch,listener,0,threadNum);
serialize_compressed_diff(diff,covers,out_diff,compressPlugin);
}
void create_compressed_diff(const TByte* newData,const TByte* newData_end,
const TByte* oldData,const TByte* oldData_end,
const hpatch_TStreamOutput* out_diff,const hdiff_TCompress* compressPlugin,
int kMinSingleMatchScore,bool isUseBigCacheMatch,
ICoverLinesListener* listener,size_t threadNum){
std::vector<unsigned char> _out_diff;
create_compressed_diff(newData,newData_end,oldData,oldData_end,_out_diff,
compressPlugin,kMinSingleMatchScore,isUseBigCacheMatch,listener,threadNum);
checki(out_diff->write(out_diff,0,_out_diff.data(),_out_diff.data()+_out_diff.size()),"create_compressed_diff() out_diff->write");
}
static void serialize_single_compressed_diff(const hpatch_TStreamInput* newStream,const hpatch_TStreamInput* oldStream,
bool isZeroSubDiff,const TCovers& covers,const hpatch_TStreamOutput* out_diff,
const hdiff_TCompress* compressPlugin,size_t patchStepMemSize){
check(patchStepMemSize>=hpatch_kStreamCacheSize);
if (patchStepMemSize>newStream->streamSize){
patchStepMemSize=(size_t)newStream->streamSize;
if (patchStepMemSize<hpatch_kStreamCacheSize)
patchStepMemSize=hpatch_kStreamCacheSize;
}
TStepStream stepStream(newStream,oldStream,isZeroSubDiff,covers,patchStepMemSize);
TDiffStream outDiff(out_diff);
{//type
std::vector<TByte> out_type;
_outType(out_type,compressPlugin,kHDiffSFVersionType);
outDiff.pushBack(out_type.data(),out_type.size());
}
outDiff.packUInt(newStream->streamSize);
outDiff.packUInt(oldStream->streamSize);
outDiff.packUInt(stepStream.getCoverCount());
outDiff.packUInt(stepStream.getMaxStepMemSize());
outDiff.packUInt(stepStream.streamSize);
TPlaceholder compressed_sizePos=outDiff.packUInt_pos(compressPlugin?stepStream.streamSize:0);
outDiff.pushStream(&stepStream,compressPlugin,compressed_sizePos);
}
void create_single_compressed_diff(const TByte* newData,const TByte* newData_end,
const TByte* oldData,const TByte* oldData_end,
std::vector<unsigned char>& out_diff,
const hdiff_TCompress* compressPlugin,int kMinSingleMatchScore,
size_t patchStepMemSize,bool isUseBigCacheMatch,
ICoverLinesListener* listener,size_t threadNum){
TVectorAsStreamOutput outDiffStream(out_diff);
create_single_compressed_diff(newData,newData_end,oldData,oldData_end,&outDiffStream,
compressPlugin,kMinSingleMatchScore,patchStepMemSize,
isUseBigCacheMatch,listener,threadNum);
}
void create_single_compressed_diff(const TByte* newData,const TByte* newData_end,
const TByte* oldData,const TByte* oldData_end,
const hpatch_TStreamOutput* out_diff,
const hdiff_TCompress* compressPlugin,int kMinSingleMatchScore,
size_t patchStepMemSize,bool isUseBigCacheMatch,
ICoverLinesListener* listener,size_t threadNum){
TDiffData diff;
std::vector<TOldCover> covers;
get_diff(newData,newData_end,oldData,oldData_end,diff,covers,
kMinSingleMatchScore,isUseBigCacheMatch,listener,0,threadNum);
hpatch_TStreamInput _newStream;
hpatch_TStreamInput _oldStream;
mem_as_hStreamInput(&_newStream,diff.newData,diff.newData_end);
mem_as_hStreamInput(&_oldStream,diff.oldData,diff.oldData_end);
const TCovers _covers((void*)covers.data(),covers.size(),
sizeof(*covers.data())==sizeof(hpatch_TCover32));
serialize_single_compressed_diff(&_newStream,&_oldStream,false,_covers,
out_diff,compressPlugin,patchStepMemSize);
}
void create_single_compressed_diff_stream(const hpatch_TStreamInput* newData,
const hpatch_TStreamInput* oldData,
const hpatch_TStreamOutput* out_diff,
const hdiff_TCompress* compressPlugin,
size_t kMatchBlockSize,size_t patchStepMemSize,
const hdiff_TMTSets_s* mtsets){
TCoversBuf covers(newData->streamSize,oldData->streamSize);
get_match_covers_by_block(newData,oldData,&covers,kMatchBlockSize,mtsets);
serialize_single_compressed_diff(newData,oldData,true,covers,
out_diff,compressPlugin,patchStepMemSize);
}
bool check_diff(const TByte* newData,const TByte* newData_end,
const TByte* oldData,const TByte* oldData_end,
const TByte* diff,const TByte* diff_end){
hpatch_TStreamInput newStream;
hpatch_TStreamInput oldStream;
hpatch_TStreamInput diffStream;
mem_as_hStreamInput(&newStream,newData,newData_end);
mem_as_hStreamInput(&oldStream,oldData,oldData_end);
mem_as_hStreamInput(&diffStream,diff,diff_end);
return check_diff(&newStream,&oldStream,&diffStream);
}
bool check_diff(const hpatch_TStreamInput* newData,
const hpatch_TStreamInput* oldData,
const hpatch_TStreamInput* diff){
const size_t kACacheBufSize=hdiff_kFileIOBufBestSize;
TAutoMem _cache(kACacheBufSize*(1+8));
_TCheckOutNewDataStream out_newData(newData,_cache.data(),kACacheBufSize);
_test_rt(patch_stream_with_cache(&out_newData,oldData,diff,
_cache.data()+kACacheBufSize,_cache.data_end()));
_test_rt(out_newData.isWriteFinish());
return true;
}
bool check_compressed_diff(const TByte* newData,const TByte* newData_end,
const TByte* oldData,const TByte* oldData_end,
const TByte* diff,const TByte* diff_end,
hpatch_TDecompress* decompressPlugin){
hpatch_TStreamInput newStream;
hpatch_TStreamInput oldStream;
hpatch_TStreamInput diffStream;
mem_as_hStreamInput(&newStream,newData,newData_end);
mem_as_hStreamInput(&oldStream,oldData,oldData_end);
mem_as_hStreamInput(&diffStream,diff,diff_end);
return check_compressed_diff(&newStream,&oldStream,&diffStream,decompressPlugin);
}
bool check_compressed_diff(const hpatch_TStreamInput* newData,
const hpatch_TStreamInput* oldData,
const hpatch_TStreamInput* compressed_diff,
hpatch_TDecompress* decompressPlugin){
const size_t kACacheBufSize=hdiff_kFileIOBufBestSize;
TAutoMem _cache(kACacheBufSize*(1+6));
_TCheckOutNewDataStream out_newData(newData,_cache.data(),kACacheBufSize);
_test_rt(patch_decompress_with_cache(&out_newData,oldData,compressed_diff,decompressPlugin,
_cache.data()+kACacheBufSize,_cache.data_end()));
_test_rt(out_newData.isWriteFinish());
return true;
}
bool check_single_compressed_diff(const TByte* newData,const TByte* newData_end,
const TByte* oldData,const TByte* oldData_end,
const TByte* diff,const TByte* diff_end,
hpatch_TDecompress* decompressPlugin){
hpatch_TStreamInput newStream;
hpatch_TStreamInput oldStream;
hpatch_TStreamInput diffStream;
mem_as_hStreamInput(&newStream,newData,newData_end);
mem_as_hStreamInput(&oldStream,oldData,oldData_end);
mem_as_hStreamInput(&diffStream,diff,diff_end);
return check_single_compressed_diff(&newStream,&oldStream,&diffStream,decompressPlugin);
}
bool check_single_compressed_diff(const hpatch_TStreamInput* newData,
const TByte* oldData,const TByte* oldData_end,
const hpatch_TStreamInput* diff,
hpatch_TDecompress* decompressPlugin){
hpatch_TStreamInput oldStream;
mem_as_hStreamInput(&oldStream,oldData,oldData_end);
return check_single_compressed_diff(newData,&oldStream,diff,decompressPlugin);
}
static hpatch_BOOL _check_single_onDiffInfo(struct sspatch_listener_t* listener,
const hpatch_singleCompressedDiffInfo* info,
hpatch_TDecompress** out_decompressPlugin,
unsigned char** out_temp_cache,
unsigned char** out_temp_cacheEnd){
size_t memSize=(size_t)(info->stepMemSize+hdiff_kFileIOBufBestSize*3);
*out_temp_cache=(unsigned char*)malloc(memSize);
*out_temp_cacheEnd=(*out_temp_cache)+memSize;
*out_decompressPlugin=(info->compressType[0]=='\0')?0:(hpatch_TDecompress*)listener->import;
return hpatch_TRUE;
}
static void _check_single_onPatchFinish(struct sspatch_listener_t* listener,
unsigned char* temp_cache, unsigned char* temp_cacheEnd){
if (temp_cache) free(temp_cache);
}
bool check_single_compressed_diff(const hpatch_TStreamInput* newData,
const hpatch_TStreamInput* oldData,
const hpatch_TStreamInput* diff,
hpatch_TDecompress* decompressPlugin){
sspatch_listener_t listener={0};
listener.import=decompressPlugin;
listener.onDiffInfo=_check_single_onDiffInfo;
listener.onPatchFinish=_check_single_onPatchFinish;
const size_t kACacheBufSize=hdiff_kFileIOBufBestSize;
TAutoMem _cache(kACacheBufSize*1);
_TCheckOutNewDataStream out_newData(newData,_cache.data(),kACacheBufSize);
_test_rt(patch_single_stream(&listener,&out_newData,oldData,diff,0,0));
_test_rt(out_newData.isWriteFinish());
return true;
}
//for test
void __hdiff_private__create_compressed_diff(const TByte* newData,const TByte* newData_end,
const TByte* oldData,const TByte* oldData_end,
std::vector<TByte>& out_diff,
const hdiff_TCompress* compressPlugin,int kMinSingleMatchScore,
const TSuffixString* sstring){
TDiffData diff;
std::vector<TOldCover> covers;
get_diff(newData,newData_end,oldData,oldData_end,diff,covers,
kMinSingleMatchScore,false,0,sstring,1);
serialize_compressed_diff(diff,covers,out_diff,compressPlugin);
}
//======================
void get_match_covers_by_block(const hpatch_TStreamInput* newData,const hpatch_TStreamInput* oldData,
hpatch_TOutputCovers* out_covers,size_t kMatchBlockSize,const hdiff_TMTSets_s* mtsets){
assert(out_covers->push_cover!=0);
TDigestMatcher matcher(oldData,newData,kMatchBlockSize,mtsets?*mtsets:hdiff_TMTSets_s_kEmpty);
matcher.search_cover(out_covers);
//todo: + extend_cover_stream ?
}
void get_match_covers_by_block(const unsigned char* newData,const unsigned char* newData_end,
const unsigned char* oldData,const unsigned char* oldData_end,
hpatch_TOutputCovers* out_covers,size_t kMatchBlockSize,size_t threadNum){
hdiff_TStreamInput oldData_stream;
mem_as_hStreamInput(&oldData_stream,oldData,oldData_end);
hdiff_TStreamInput newData_stream;
mem_as_hStreamInput(&newData_stream,newData,newData_end);
hdiff_TMTSets_s mtsets={threadNum,threadNum,true,true,false};
get_match_covers_by_block(&newData_stream,&oldData_stream,out_covers,kMatchBlockSize,&mtsets);
}
void get_match_covers_by_sstring(const unsigned char* newData,const unsigned char* newData_end,
const unsigned char* oldData,const unsigned char* oldData_end,
std::vector<hpatch_TCover_sz>& out_covers,int kMinSingleMatchScore,
bool isUseBigCacheMatch,ICoverLinesListener* listener,
size_t threadNum,bool isCanExtendCover){
TDiffData diff;
std::vector<TOldCover> covers;
assert(sizeof(TOldCover)==sizeof(hpatch_TCover_sz));
{ std::vector<hpatch_TCover_sz> tmp; tmp.swap(out_covers); }
get_diff(newData,newData_end,oldData,oldData_end,diff,covers,
kMinSingleMatchScore,isUseBigCacheMatch,listener,0,threadNum,isCanExtendCover);
void* pcovers=&covers;
out_covers.swap(*(std::vector<hpatch_TCover_sz>*)pcovers);
}
void get_match_covers_by_sstring(const unsigned char* newData,const unsigned char* newData_end,
const unsigned char* oldData,const unsigned char* oldData_end,
hpatch_TOutputCovers* out_covers,int kMinSingleMatchScore,
bool isUseBigCacheMatch,ICoverLinesListener* listener,
size_t threadNum,bool isCanExtendCover){
std::vector<hpatch_TCover_sz> covers;
get_match_covers_by_sstring(newData,newData_end,oldData,oldData_end,covers,
kMinSingleMatchScore,isUseBigCacheMatch,listener,threadNum,isCanExtendCover);
const hpatch_TCover_sz* pcovers=covers.data();
for (size_t i=0;i<covers.size();++i,++pcovers){
if (sizeof(*pcovers)==sizeof(hpatch_TCover)){
out_covers->push_cover(out_covers,(const hpatch_TCover*)pcovers);
}else{
hpatch_TCover cover;
cover.oldPos=pcovers->oldPos;
cover.newPos=pcovers->newPos;
cover.length=pcovers->length;
out_covers->push_cover(out_covers,&cover);
}
}
}
static void stream_serialize_compressed_diff(const hpatch_TStreamInput* newData,
hpatch_StreamPos_t oldDataSize,
const hpatch_TStreamOutput* out_diff,
const hdiff_TCompress* compressPlugin,
const TCovers& covers){
std::vector<TByte> rle_ctrlBuf;
std::vector<TByte> rle_codeBuf;
{//empty rle //todo: suport rle data
if (newData->streamSize>0)
packUIntWithTag(rle_ctrlBuf,newData->streamSize-1,kByteRleType_rle0,kByteRleType_bit);
assert(rle_codeBuf.empty());
}
TDiffStream outDiff(out_diff);
{//type
std::vector<TByte> out_type;
_outType(out_type,compressPlugin);
outDiff.pushBack(out_type.data(),out_type.size());
}
outDiff.packUInt(newData->streamSize);
outDiff.packUInt(oldDataSize);
outDiff.packUInt(covers.coverCount());
const hpatch_StreamPos_t cover_buf_size=TCoversStream::getDataSize(covers);
outDiff.packUInt(cover_buf_size);
TPlaceholder compress_cover_buf_sizePos=
outDiff.packUInt_pos(compressPlugin?cover_buf_size:0); //compress_cover_buf size
outDiff.packUInt(rle_ctrlBuf.size());//rle_ctrlBuf size
outDiff.packUInt(0);//compress_rle_ctrlBuf size
outDiff.packUInt(rle_codeBuf.size());//rle_codeBuf size
outDiff.packUInt(0);//compress_rle_codeBuf size
const hpatch_StreamPos_t newDataDiff_size=
TNewDataDiffStream::getDataSize(covers,newData->streamSize);
outDiff.packUInt(newDataDiff_size);
TPlaceholder compress_newDataDiff_sizePos=
outDiff.packUInt_pos(compressPlugin?newDataDiff_size:0); //compress_newDataDiff size
{//save covers
TCoversStream cover_buf(covers,cover_buf_size);
outDiff.pushStream(&cover_buf,compressPlugin,compress_cover_buf_sizePos);
}
{//rle
outDiff.pushBack(rle_ctrlBuf.data(),rle_ctrlBuf.size());
outDiff.pushBack(rle_codeBuf.data(),rle_codeBuf.size());
}
{//save newDataDiff
TNewDataDiffStream newDataDiff(covers,newData,newDataDiff_size);
outDiff.pushStream(&newDataDiff,compressPlugin,compress_newDataDiff_sizePos);
}
}
void create_compressed_diff_stream(const hpatch_TStreamInput* newData,
const hpatch_TStreamInput* oldData,
const hpatch_TStreamOutput* out_diff,
const hdiff_TCompress* compressPlugin,
size_t kMatchBlockSize,const hdiff_TMTSets_s* mtsets){
TCoversBuf covers(newData->streamSize,oldData->streamSize);
get_match_covers_by_block(newData,oldData,&covers,kMatchBlockSize,mtsets);
stream_serialize_compressed_diff(newData,oldData->streamSize,out_diff,compressPlugin,covers);
}
void resave_compressed_diff(const hpatch_TStreamInput* in_diff,
hpatch_TDecompress* decompressPlugin,
const hpatch_TStreamOutput* out_diff,
const hdiff_TCompress* compressPlugin,
hpatch_StreamPos_t out_diff_curPos){
_THDiffzHead head;
hpatch_compressedDiffInfo diffInfo;
assert(in_diff!=0);
assert(in_diff->read!=0);
assert(out_diff!=0);
assert(out_diff->write!=0);
{//read head
checki(read_diffz_head(&diffInfo,&head,in_diff),
"resave_compressed_diff() read_diffz_head() error!");
checki((decompressPlugin!=0)||(diffInfo.compressedCount<=0),
"resave_compressed_diff() decompressPlugin null error!");
if ((decompressPlugin)&&(diffInfo.compressedCount>0)){
checki(decompressPlugin->is_can_open(diffInfo.compressType),
"resave_compressed_diff() decompressPlugin cannot open compressed data error!");
}
}
TDiffStream outDiff(out_diff,out_diff_curPos);
{//type
std::vector<TByte> out_type;
_outType(out_type,compressPlugin);
outDiff.pushBack(out_type.data(),out_type.size());
}
{//copy other
TStreamClip clip(in_diff,head.typesEndPos,head.compressSizeBeginPos);
outDiff.pushStream(&clip);
}
outDiff.packUInt(head.cover_buf_size);
TPlaceholder compress_cover_buf_sizePos=
outDiff.packUInt_pos(compressPlugin?head.cover_buf_size:0);//compress_cover_buf size
outDiff.packUInt(head.rle_ctrlBuf_size);//rle_ctrlBuf size
TPlaceholder compress_rle_ctrlBuf_sizePos=
outDiff.packUInt_pos(compressPlugin?head.rle_ctrlBuf_size:0);//compress_rle_ctrlBuf size
outDiff.packUInt(head.rle_codeBuf_size);//rle_codeBuf size
TPlaceholder compress_rle_codeBuf_sizePos=
outDiff.packUInt_pos(compressPlugin?head.rle_codeBuf_size:0);//compress_rle_codeBuf size
outDiff.packUInt(head.newDataDiff_size);
TPlaceholder compress_newDataDiff_sizePos=
outDiff.packUInt_pos(compressPlugin?head.newDataDiff_size:0);//compress_newDataDiff size
{//save covers
TStreamClip clip(in_diff,head.headEndPos,head.coverEndPos,
(head.compress_cover_buf_size>0)?decompressPlugin:0,head.cover_buf_size);
outDiff.pushStream(&clip,compressPlugin,compress_cover_buf_sizePos);
}
hpatch_StreamPos_t diffPos0=head.coverEndPos;
{//save rle ctrl
bool isCompressed=(head.compress_rle_ctrlBuf_size>0);
hpatch_StreamPos_t bufSize=isCompressed?head.compress_rle_ctrlBuf_size:head.rle_ctrlBuf_size;
TStreamClip clip(in_diff,diffPos0,diffPos0+bufSize,
isCompressed?decompressPlugin:0,head.rle_ctrlBuf_size);
outDiff.pushStream(&clip,compressPlugin,compress_rle_ctrlBuf_sizePos);
diffPos0+=bufSize;
}
{//save rle code
bool isCompressed=(head.compress_rle_codeBuf_size>0);
hpatch_StreamPos_t bufSize=isCompressed?head.compress_rle_codeBuf_size:head.rle_codeBuf_size;
TStreamClip clip(in_diff,diffPos0,diffPos0+bufSize,
isCompressed?decompressPlugin:0,head.rle_codeBuf_size);
outDiff.pushStream(&clip,compressPlugin,compress_rle_codeBuf_sizePos);
diffPos0+=bufSize;
}
{//save newDataDiff
bool isCompressed=(head.compress_newDataDiff_size>0);
hpatch_StreamPos_t bufSize=isCompressed?head.compress_newDataDiff_size:head.newDataDiff_size;
TStreamClip clip(in_diff,diffPos0,diffPos0+bufSize,
isCompressed?decompressPlugin:0,head.newDataDiff_size);
outDiff.pushStream(&clip,compressPlugin,compress_newDataDiff_sizePos);
diffPos0+=bufSize;
}
}
void resave_single_compressed_diff(const hpatch_TStreamInput* in_diff,
hpatch_TDecompress* decompressPlugin,
const hpatch_TStreamOutput* out_diff,
const hdiff_TCompress* compressPlugin,
const hpatch_singleCompressedDiffInfo* diffInfo,
hpatch_StreamPos_t in_diff_curPos,
hpatch_StreamPos_t out_diff_curPos){
hpatch_singleCompressedDiffInfo _diffInfo;
if (diffInfo==0){
checki(getSingleCompressedDiffInfo(&_diffInfo,in_diff,in_diff_curPos),
"getSingleCompressedDiffInfo() return fail!");
diffInfo=&_diffInfo;
}
const bool isCompressed=(diffInfo->compressedSize>0);
if (isCompressed){ //check
checki(diffInfo->compressedSize+(in_diff_curPos+diffInfo->diffDataPos)==in_diff->streamSize,
"resave_single_compressed_diff() diffInfo error!");
checki((decompressPlugin!=0)&&(decompressPlugin->is_can_open(diffInfo->compressType)),
"resave_single_compressed_diff() decompressPlugin error!");
}
TDiffStream outDiff(out_diff,out_diff_curPos);
{//type & head
std::vector<TByte> outBuf;
_outType(outBuf, compressPlugin,kHDiffSFVersionType);
packUInt(outBuf, diffInfo->newDataSize);
packUInt(outBuf, diffInfo->oldDataSize);
packUInt(outBuf, diffInfo->coverCount);
packUInt(outBuf, diffInfo->stepMemSize);
packUInt(outBuf, diffInfo->uncompressedSize);
outDiff.pushBack(outBuf.data(),outBuf.size());
//no compressedSize
}
{//save single stream data
TStreamClip clip(in_diff,diffInfo->diffDataPos+in_diff_curPos,in_diff->streamSize,
isCompressed?decompressPlugin:0,diffInfo->uncompressedSize);
TPlaceholder compressedSize_pos=outDiff.packUInt_pos(compressPlugin?diffInfo->uncompressedSize:0);
outDiff.pushStream(&clip,compressPlugin,compressedSize_pos);
}
}
//----------------------------------------------------------------------------------------------------
#include "diff_for_hpatch_lite.h"
#include "../HPatchLite/hpatch_lite.h"
namespace{
static const char* kHPatchLite_versionType="hI";
static const hpi_byte kHPatchLite_versionCode=1;
static inline void hpi_packUInt(std::vector<TByte>& buf,TUInt v){
check(v==(hpi_pos_t)v);
packUInt(buf,v);
}
static inline void hpi_packUIntWithTag(std::vector<TByte>& buf,TUInt v,TByte tag,TByte bit){
check(v==(hpi_pos_t)v);
packUIntWithTag(buf,v,tag,bit);
}
static inline TByte hpi_getSavedSizeBytes(TUInt size){
check(size==(hpi_pos_t)size);
TByte bytes=0;
while (size>0){
++bytes;
size>>=8;
}
return bytes;
}
static inline void hpi_saveSize(std::vector<TByte>& buf,TUInt size){
check(size==(hpi_pos_t)size);
while (size>0){
buf.push_back((TByte)size);
size>>=8;
}
}
static bool _getIs0(const TByte* data,size_t length){
for (size_t i=0; i<length; ++i) {
if (data[i]==0)
continue;
else
return false;
}
return true;
}
static void _getSubDiff(std::vector<TByte>& subDiff,const TDiffData& diff,const TOldCover& cover){
subDiff.resize(cover.length);
const TByte* pnew=diff.newData+cover.newPos;
const TByte* pold=diff.oldData+cover.oldPos;
for (size_t i=0;i<subDiff.size();++i)
subDiff[i]=pnew[i]-pold[i];
}
static void serialize_lite_diff(const TDiffData& diff,const std::vector<TOldCover>& covers,
std::vector<TByte>& out_diff,const hdiffi_TCompress* compressPlugin){
const TUInt coverCount=(TUInt)covers.size();
std::vector<TByte> subDiff;
std::vector<TByte> buf;
hpi_packUInt(buf,coverCount);
const TUInt newSize=(TUInt)(diff.newData_end-diff.newData);
{
TUInt lastOldEnd=0;
TUInt lastNewEnd=0;
for (TUInt i=0; i<coverCount; ++i) {
const TOldCover& cover=covers[i];
hpi_packUInt(buf, cover.length);
_getSubDiff(subDiff,diff,cover);
const TByte isNullSubDiff=_getIs0(subDiff.data(),cover.length)?1:0;
if ((TUInt)cover.oldPos>=lastOldEnd){ //save inc_oldPos
hpi_packUIntWithTag(buf,(TUInt)(cover.oldPos-lastOldEnd), 0+isNullSubDiff*2,2);
}else{
hpi_packUIntWithTag(buf,(TUInt)(lastOldEnd-cover.oldPos), 1+isNullSubDiff*2,2);
}
TUInt backNewLen=cover.newPos-lastNewEnd;
assert(backNewLen>=0);
hpi_packUInt(buf,(TUInt)backNewLen); //save inc_newPos
if (backNewLen>0){
const TByte* newDataDiff=diff.newData+lastNewEnd;
pushBack(buf,newDataDiff,newDataDiff+backNewLen);
}
if (!isNullSubDiff){
pushBack(buf,subDiff.data(),subDiff.data()+cover.length);
}
lastOldEnd=cover.oldPos+cover.length;
lastNewEnd=cover.newPos+cover.length;
}
TUInt backNewLen=(newSize-lastNewEnd);
check(backNewLen==0);
}
std::vector<TByte> compress_buf;
do_compress(compress_buf,buf,compressPlugin->compress);
out_diff.push_back(kHPatchLite_versionType[0]);
out_diff.push_back(kHPatchLite_versionType[1]);
out_diff.push_back(compress_buf.empty()?hpi_compressType_no:compressPlugin->compress_type);
TUInt savedUncompressSize=compress_buf.empty()?0:buf.size();
out_diff.push_back((kHPatchLite_versionCode<<6)|
(hpi_getSavedSizeBytes(newSize))|
(hpi_getSavedSizeBytes(savedUncompressSize)<<3));
hpi_saveSize(out_diff,newSize);
hpi_saveSize(out_diff,savedUncompressSize);
pushBack(out_diff,compress_buf.empty()?buf:compress_buf);
}
} //end namespace
void create_lite_diff(const unsigned char* newData,const unsigned char* newData_end,
const unsigned char* oldData,const unsigned char* oldData_end,
std::vector<hpi_byte>& out_lite_diff,const hdiffi_TCompress* compressPlugin,
int kMinSingleMatchScore,bool isUseBigCacheMatch,size_t threadNum){
static const int _kMatchScore_optim4bin=6;
TDiffData diff;
std::vector<TOldCover> covers;
get_diff(newData,newData_end,oldData,oldData_end,diff,covers,
kMinSingleMatchScore-_kMatchScore_optim4bin,isUseBigCacheMatch,0,0,threadNum);
size_t oldPosEnd=0;
size_t newPosEnd=0;
if (!covers.empty()){
const TOldCover& c=covers.back();
oldPosEnd=c.oldPos+c.length;
newPosEnd=c.newPos+c.length;
}
const size_t newSize=newData_end-newData;
if (newPosEnd<newSize)
covers.push_back(TOldCover(oldPosEnd,newSize,0));
serialize_lite_diff(diff,covers,out_lite_diff,compressPlugin);
}
namespace{
struct TPatchiListener:public hpatchi_listener_t{
hpatch_decompressHandle decompresser;
hpatch_TDecompress* decompressPlugin;
inline TPatchiListener():decompresser(0){}
inline ~TPatchiListener(){ if (decompresser) decompressPlugin->close(decompressPlugin,decompresser); }
const hpi_byte* diffData_cur;
const hpi_byte* diffData_end;
hpatch_TStreamInput diffStream;
hpi_pos_t uncompressSize;
const hpi_byte* newData_cur;
const hpi_byte* newData_end;
const hpi_byte* oldData;
const hpi_byte* oldData_end;
static hpi_BOOL _read_diff(hpi_TInputStreamHandle inputStream,hpi_byte* out_data,hpi_size_t* data_size){
TPatchiListener& self=*(TPatchiListener*)inputStream;
const hpi_byte* cur=self.diffData_cur;
size_t d_size=self.diffData_end-cur;
size_t r_size=*data_size;
if (r_size>d_size){
r_size=d_size;
*data_size=(hpi_size_t)r_size;
}
memcpy(out_data,cur,r_size);
self.diffData_cur=cur+r_size;
return hpi_TRUE;
}
static hpi_BOOL _read_diff_dec(hpi_TInputStreamHandle inputStream,hpi_byte* out_data,hpi_size_t* data_size){
TPatchiListener& self=*(TPatchiListener*)inputStream;
hpi_size_t r_size=*data_size;
if (r_size>self.uncompressSize){
r_size=(hpi_size_t)self.uncompressSize;
*data_size=(hpi_size_t)self.uncompressSize;
}
if (!self.decompressPlugin->decompress_part(self.decompresser,out_data,out_data+r_size))
return hpi_FALSE;
self.uncompressSize-=r_size;
return hpi_TRUE;
}
static hpi_BOOL _write_new(struct hpatchi_listener_t* listener,const hpi_byte* data,hpi_size_t data_size){
TPatchiListener& self=*(TPatchiListener*)listener;
if (data_size>(size_t)(self.newData_end-self.newData_cur))
return hpi_FALSE;
if (0!=memcmp(self.newData_cur,data,data_size))
return hpi_FALSE;
self.newData_cur+=data_size;
return hpi_TRUE;
}
static hpi_BOOL _read_old(struct hpatchi_listener_t* listener,hpi_pos_t read_from_pos,hpi_byte* out_data,hpi_size_t data_size){
TPatchiListener& self=*(TPatchiListener*)listener;
size_t dsize=self.oldData_end-self.oldData;
if ((read_from_pos>dsize)|(data_size>(size_t)(dsize-read_from_pos))) return hpi_FALSE;
memcpy(out_data,self.oldData+(size_t)read_from_pos,data_size);
return hpi_TRUE;
}
};
}// end namespace
bool check_lite_diff_open(const hpi_byte* lite_diff,const hpi_byte* lite_diff_end,
hpi_compressType* out_compress_type){
TPatchiListener listener;
listener.diffData_cur=lite_diff;
listener.diffData_end=lite_diff_end;
hpi_pos_t saved_newSize;
hpi_pos_t saved_uncompressSize;
if (!hpatch_lite_open(&listener,listener._read_diff,out_compress_type,&saved_newSize,
&saved_uncompressSize)) return false;
return true;
}
bool check_lite_diff(const hpi_byte* newData,const hpi_byte* newData_end,
const hpi_byte* oldData,const hpi_byte* oldData_end,
const hpi_byte* lite_diff,const hpi_byte* lite_diff_end,
hpatch_TDecompress* decompressPlugin){
TPatchiListener listener;
listener.diffData_cur=lite_diff;
listener.diffData_end=lite_diff_end;
hpi_compressType _compress_type;
hpi_pos_t saved_newSize;
hpi_pos_t saved_uncompressSize;
if (!hpatch_lite_open(&listener,listener._read_diff,&_compress_type,&saved_newSize,
&saved_uncompressSize)) return false;
if (saved_newSize!=(size_t)(newData_end-newData)) return false;
listener.diff_data=&listener;
listener.decompressPlugin=(_compress_type!=hpi_compressType_no)?decompressPlugin:0;
if (listener.decompressPlugin){
listener.uncompressSize=saved_uncompressSize;
mem_as_hStreamInput(&listener.diffStream,listener.diffData_cur,lite_diff_end);
listener.decompresser=decompressPlugin->open(decompressPlugin,saved_uncompressSize,&listener.diffStream,
0,(size_t)(lite_diff_end-listener.diffData_cur));
if (listener.decompresser==0) return false;
listener.read_diff=listener._read_diff_dec;
}else{
listener.read_diff=listener._read_diff;
}
listener.newData_cur=newData;
listener.newData_end=newData_end;
listener.write_new=listener._write_new;
listener.oldData=oldData;
listener.oldData_end=oldData_end;
listener.read_old=listener._read_old;
const size_t kACacheBufSize=1024*32;
hdiff_private::TAutoMem _cache(kACacheBufSize);
if (!hpatch_lite_patch(&listener,saved_newSize,_cache.data(),(hpi_size_t)_cache.size()))
return false;
return listener.newData_cur==listener.newData_end;
}