You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
628 lines
17 KiB
628 lines
17 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// plBSDiffBuffer - A utility class for writing and applying a difference |
|
// buffer--i.e. a buffer containing a series of modifications |
|
// that will modify an old data buffer to match a new one. |
|
// It's a useful utility class when doing binary file |
|
// patching, for example, as you can write out the changes |
|
// to this class, get back a data buffer suitable for writing, |
|
// then use this class again later to reconstruct the new buffer. |
|
// |
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
#include "hsTypes.h" |
|
#include "plBSDiffBuffer.h" |
|
#include "hsUtils.h" |
|
#include "hsStream.h" |
|
|
|
#define plBSDiffBuffer_MIN(x,y) (((x)<(y)) ? (x) : (y)) |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
//// Constructor/Destructors ///////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
//// Creation Constructor //////////////////////////////////////////////////// |
|
// Use this constructor when creating a new diff buffer. Pass in the length |
|
// of the final new buffer. You don't need to pass in the length of the old |
|
// buffer, but if you do, it'll help this class do some internal space |
|
// optimization. |
|
|
|
plBSDiffBuffer::plBSDiffBuffer( UInt32 newLength, UInt32 oldLength ) |
|
: fNewLength( newLength ) |
|
, fPatchLength( 0 ) |
|
{ |
|
fPatchBuffer = nil; |
|
fWriting = true; |
|
} |
|
|
|
//// Application Constructor ///////////////////////////////////////////////// |
|
// Use this constructor when taking a diff buffer and applying it to an old |
|
// buffer. Pass in a pointer to the diff buffer and its length. The buffer |
|
// will be copied, so you don't need to keep it around after you construct |
|
// this object. |
|
|
|
plBSDiffBuffer::plBSDiffBuffer( void *buffer, UInt32 length ) |
|
: fNewLength( 0 ) |
|
, fPatchLength( 0 ) |
|
, fPatchBuffer( nil ) |
|
, fWriting( false ) |
|
{ |
|
if (!buffer || length < 32) |
|
hsAssert(false, "Corrupt Patch Buffer!"); |
|
|
|
if((fPatchBuffer=TRACKED_NEW unsigned char[length+1])!=nil) |
|
{ |
|
fPatchLength = length; |
|
memcpy(fPatchBuffer, buffer, fPatchLength); |
|
} |
|
} |
|
|
|
plBSDiffBuffer::~plBSDiffBuffer() |
|
{ |
|
if ( fPatchBuffer ) |
|
delete[] fPatchBuffer; |
|
} |
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
//// Creation/Write Functions //////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
//// Diff ///////////////////////////////////////////////////////////////////// |
|
// Diff() creates the diff buffer from the new and old. |
|
UInt32 plBSDiffBuffer::Diff( UInt32 oldLength, void *oldBuffer, UInt32 newLength, void *newBuffer ) |
|
{ |
|
hsAssert( fWriting, "Trying to Add() to a difference buffer that's reading" ); |
|
|
|
Int32 *I,*V; |
|
|
|
Int32 scan,pos,len; |
|
Int32 lastscan,lastpos,lastoffset; |
|
Int32 oldscore,scsc; |
|
|
|
Int32 s,Sf,lenf,Sb,lenb; |
|
Int32 overlap,Ss,lens; |
|
Int32 i; |
|
|
|
UInt32 cblen,dblen,eblen; |
|
unsigned char *oldbuf, *newbuf; |
|
unsigned char *cb,*db,*eb; |
|
|
|
unsigned char header[32]; |
|
|
|
if (oldBuffer == nil || |
|
oldLength < 0 || |
|
newBuffer == nil || |
|
newLength < 0) |
|
{ |
|
return (-1); |
|
} |
|
|
|
oldbuf = (unsigned char *)oldBuffer; |
|
newbuf = (unsigned char *)newBuffer; |
|
|
|
if(((I=TRACKED_NEW Int32[oldLength+1])==nil) || |
|
((V=TRACKED_NEW Int32[oldLength+1])==nil)) |
|
{ |
|
delete[] I; |
|
delete[] V; |
|
return fPatchLength; |
|
} |
|
|
|
IQSuffixSort(I,V,oldbuf,oldLength); |
|
|
|
delete[] V; |
|
|
|
/* |
|
* These could probably be smaller, especially cb. |
|
*/ |
|
if(((cb=TRACKED_NEW unsigned char[newLength+1])==nil) || |
|
((db=TRACKED_NEW unsigned char[newLength+1])==nil) || |
|
((eb=TRACKED_NEW unsigned char[newLength+1])==nil)) |
|
{ |
|
delete[] I; |
|
delete[] cb; |
|
delete[] db; |
|
delete[] eb; |
|
return fPatchLength; |
|
} |
|
cblen=0; |
|
dblen=0; |
|
eblen=0; |
|
|
|
/* Header is |
|
0 8 "BSDIFF40" |
|
8 8 length of bzip2ed ctrl block |
|
16 8 length of bzip2ed diff block |
|
24 8 length of new file */ |
|
/* File is |
|
0 32 Header |
|
32 ?? Bzip2ed ctrl block |
|
?? ?? Bzip2ed diff block |
|
?? ?? Bzip2ed extra block */ |
|
memcpy(header,"BSDIFF40",8); |
|
memset(header+8,0,24); |
|
|
|
scan=0;len=0; |
|
lastscan=0;lastpos=0;lastoffset=0; |
|
while(scan<newLength) { |
|
oldscore=0; |
|
|
|
for(scsc=scan+=len;scan<newLength;scan++) { |
|
len=ISearch(I,oldbuf,oldLength,newbuf+scan,newLength-scan, |
|
0,oldLength,&pos); |
|
|
|
for(;scsc<scan+len;scsc++) |
|
if((scsc+lastoffset<oldLength) && |
|
(oldbuf[scsc+lastoffset] == newbuf[scsc])) |
|
oldscore++; |
|
|
|
if(((len==oldscore) && (len!=0)) || |
|
(len>oldscore+8)) break; |
|
|
|
if((scan+lastoffset<oldLength) && |
|
(oldbuf[scan+lastoffset] == newbuf[scan])) |
|
oldscore--; |
|
}; |
|
|
|
if((len!=oldscore) || (scan==newLength)) { |
|
s=0;Sf=0;lenf=0; |
|
for(i=0;(lastscan+i<scan)&&(lastpos+i<oldLength);) { |
|
if(oldbuf[lastpos+i]==newbuf[lastscan+i]) s++; |
|
i++; |
|
if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; }; |
|
}; |
|
|
|
lenb=0; |
|
if(scan<newLength) { |
|
s=0;Sb=0; |
|
for(i=1;(scan>=lastscan+i)&&(pos>=i);i++) { |
|
if(oldbuf[pos-i]==newbuf[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(newbuf[lastscan+lenf-overlap+i]== |
|
oldbuf[lastpos+lenf-overlap+i]) s++; |
|
if(newbuf[scan-lenb+i]== |
|
oldbuf[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]=newbuf[lastscan+i]-oldbuf[lastpos+i]; |
|
for(i=0;i<(scan-lenb)-(lastscan+lenf);i++) |
|
eb[eblen+i]=newbuf[lastscan+lenf+i]; |
|
|
|
dblen+=lenf; |
|
eblen+=(scan-lenb)-(lastscan+lenf); |
|
|
|
IWriteUnsignedInt8(lenf,cb+cblen); |
|
IWriteUnsignedInt8((scan-lenb)-(lastscan+lenf),cb+cblen+8); |
|
IWriteUnsignedInt8((pos-lenb)-(lastpos+lenf),cb+cblen+16); |
|
cblen+=24; |
|
|
|
lastscan=scan-lenb; |
|
lastpos=pos-lenb; |
|
lastoffset=pos-scan; |
|
}; |
|
}; |
|
|
|
IWriteUnsignedInt8(cblen,header+8); |
|
IWriteUnsignedInt8(dblen,header+16); |
|
IWriteUnsignedInt8(newLength,header+24); |
|
|
|
fPatchLength = 32 + cblen + dblen + eblen; |
|
fPatchBuffer = nil; |
|
if((fPatchBuffer=TRACKED_NEW unsigned char[fPatchLength])!=nil) |
|
{ |
|
memcpy(fPatchBuffer,header,32); |
|
memcpy(fPatchBuffer+32,cb,cblen); |
|
memcpy(fPatchBuffer+32+cblen,db,dblen); |
|
memcpy(fPatchBuffer+32+cblen+dblen,eb,eblen); |
|
} |
|
else |
|
{ |
|
fPatchLength = 0; |
|
} |
|
|
|
delete[] cb; |
|
delete[] db; |
|
delete[] eb; |
|
delete[] I; |
|
|
|
return fPatchLength; |
|
} |
|
|
|
//// GetBuffer /////////////////////////////////////////////////////////////// |
|
// GetBuffer() will copy the diff stream into a new buffer and return it. |
|
// You are responsible for freeing the buffer. Call this once you're done |
|
// adding ops and want the raw data to write out somewhere. Note: this |
|
// function will rewind the diff stream, so once you call it, you can't do |
|
// anything else on the object. |
|
|
|
void plBSDiffBuffer::GetBuffer( UInt32 &length, void *&bufferPtr ) |
|
{ |
|
hsAssert( fWriting, "Trying to GetBuffer() on a difference buffer that's reading" ); |
|
|
|
if (fPatchBuffer && fPatchLength) |
|
{ |
|
length = fPatchLength; |
|
if (bufferPtr = (void *)TRACKED_NEW unsigned char[ length ]) |
|
memcpy(bufferPtr, fPatchBuffer, length); |
|
} |
|
else |
|
{ |
|
length = 0; |
|
bufferPtr = nil; |
|
} |
|
} |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
//// Application Functions /////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
#define hsAssertAndBreak( cond, msg ) { if( cond ) { hsAssert( false, msg ); break; } } |
|
|
|
// Patch() will take this diff buffer and apply it to the given old buffer, |
|
// allocating and producing a new buffer. You are responsible for freeing the new buffer. |
|
UInt32 plBSDiffBuffer::Patch( UInt32 oldLength, void *oldBuffer, UInt32 &newLength, void *&newBuffer ) |
|
{ |
|
hsAssert( !fWriting, "Trying to Patch() a difference buffer that's writing" ); |
|
|
|
unsigned char *ctrlpipe, *ctrlend; |
|
unsigned char *diffpipe, *diffend; |
|
unsigned char *extrapipe; |
|
UInt32 ctrllen,datalen; |
|
int version=0; |
|
unsigned char *newpos, *newend; |
|
unsigned char *oldpos, *oldend; |
|
unsigned char *patchend; |
|
UInt32 ctrl[3]; |
|
UInt32 i; |
|
|
|
if (oldBuffer == nil || |
|
oldLength < 0 || |
|
newBuffer != nil || |
|
newLength != 0 || |
|
fPatchBuffer == nil || |
|
fPatchLength < 32) |
|
{ |
|
return (-1); |
|
} |
|
|
|
patchend = fPatchBuffer + fPatchLength; |
|
/* |
|
Ok, this is going to be messy. There are two different patch |
|
formats which we need to support. |
|
|
|
The old format (pre-4.0) is: |
|
0 8 "QSUFDIFF" or "BSDIFF30" |
|
8 8 X |
|
16 8 Y |
|
24 8 sizeof(newfile) |
|
32 X bzip2(control block) |
|
32+X Y bzip2(data block) |
|
with control block a set of pairs (x,y) meaning "seek forward |
|
in oldfile by y bytes, and add the next x bytes to x bytes from |
|
the data block". |
|
|
|
The new format (4.0) is: |
|
0 8 "BSDIFF40" |
|
8 8 X |
|
16 8 Y |
|
24 8 sizeof(newfile) |
|
32 X bzip2(control block) |
|
32+X Y bzip2(diff block) |
|
32+X+Y ??? bzip2(extra block) |
|
with control block a set of triples (x,y,z) meaning "add x bytes |
|
from oldfile to x bytes from the diff block; copy y bytes from the |
|
extra block; seek forwards in oldfile by z bytes". |
|
*/ |
|
|
|
if(memcmp(fPatchBuffer,"QSUFDIFF",8)==0) version=1; |
|
if(memcmp(fPatchBuffer,"BSDIFF30",8)==0) version=1; |
|
if(memcmp(fPatchBuffer,"BSDIFF40",8)==0) version=2; |
|
|
|
if(!version) return (-1); |
|
|
|
ctrllen=IReadUnsignedInt8(fPatchBuffer+8); |
|
datalen=IReadUnsignedInt8(fPatchBuffer+16); |
|
newLength=IReadUnsignedInt8(fPatchBuffer+24); |
|
if((ctrllen<0) || (datalen<0) || (newLength<0) || |
|
((version==1) && (32+ctrllen+datalen!=fPatchLength))) |
|
return (-1); |
|
|
|
ctrlpipe=fPatchBuffer+32; |
|
ctrlend =ctrlpipe+ctrllen; |
|
diffpipe=ctrlend; |
|
diffend = diffpipe + datalen; |
|
if(version==2) { |
|
extrapipe=diffpipe+datalen; |
|
}; |
|
|
|
if((newBuffer=(void *)TRACKED_NEW unsigned char[newLength+1])==nil) return(-1); |
|
newpos = (unsigned char *)newBuffer; |
|
newend = (unsigned char *)newBuffer + newLength; |
|
oldpos = (unsigned char *)oldBuffer; |
|
oldend = (unsigned char *)oldBuffer + oldLength; |
|
while(newpos<newend) { |
|
for(i=0;i<=version;i++) { |
|
if(ctrlend-ctrlpipe < 8) return (-1); |
|
ctrl[i]=IReadUnsignedInt8(ctrlpipe); |
|
ctrlpipe += 8; |
|
}; |
|
|
|
if(version==1) oldpos+=ctrl[1]; |
|
|
|
ISafeMemcpy(newpos, diffpipe, ctrl[0], newend, diffend); |
|
diffpipe += ctrl[0]; |
|
|
|
for(i=0;i<ctrl[0];i++) |
|
if(oldpos<oldend) |
|
{ |
|
*newpos += *oldpos; |
|
newpos++; |
|
oldpos++; |
|
} |
|
|
|
if(version==2) { |
|
ISafeMemcpy(newpos,extrapipe,ctrl[1],newend,patchend); |
|
extrapipe += ctrl[1]; |
|
newpos+=ctrl[1]; |
|
oldpos+=ctrl[2]; |
|
}; |
|
}; |
|
|
|
if((ctrlpipe != ctrlend) || |
|
(diffpipe != diffend) || |
|
((version==2) && (extrapipe != patchend))) |
|
{ |
|
return (-1); |
|
} |
|
|
|
if(newpos != newend) |
|
{ |
|
delete[] newBuffer; |
|
newLength = 0; |
|
} |
|
|
|
return newLength; |
|
} |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
//// Private Helper Functions /////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
// Reads in an 8-byte unsigned integer least significant byte first and returns a |
|
// UInt32. We'll ignore the top four bytes. |
|
UInt32 plBSDiffBuffer::IReadUnsignedInt8(unsigned char *buf) |
|
{ |
|
UInt32 y; |
|
|
|
y=buf[7]&0x7F; |
|
y=y*256;y+=buf[6]; |
|
y=y*256;y+=buf[5]; |
|
y=y*256;y+=buf[4]; |
|
y=y*256;y+=buf[3]; |
|
y=y*256;y+=buf[2]; |
|
y=y*256;y+=buf[1]; |
|
y=y*256;y+=buf[0]; |
|
|
|
// casting added so compiler doesn't warn about negating an unsigned value |
|
Int32 signedY = (Int32)y; |
|
if(buf[7]&0x80) signedY=-signedY; |
|
y = (UInt32)signedY; |
|
|
|
return y; |
|
} |
|
|
|
void plBSDiffBuffer::IWriteUnsignedInt8(UInt32 x,unsigned char *buf) |
|
{ |
|
UInt32 y; |
|
Int32 signedY, signedX = (Int32)x; |
|
|
|
// casting added so compiler doesn't warn about negating an unsigned value |
|
if(x<0) signedY=-signedX; else signedY=signedX; |
|
y = (UInt32)signedY; |
|
|
|
buf[0]=(unsigned char)(y%256);y-=buf[0]; |
|
y=y/256;buf[1]=(unsigned char)(y%256);y-=buf[1]; |
|
y=y/256;buf[2]=(unsigned char)(y%256);y-=buf[2]; |
|
y=y/256;buf[3]=(unsigned char)(y%256);y-=buf[3]; |
|
y=y/256;buf[4]=(unsigned char)(y%256);y-=buf[4]; |
|
y=y/256;buf[5]=(unsigned char)(y%256);y-=buf[5]; |
|
y=y/256;buf[6]=(unsigned char)(y%256);y-=buf[6]; |
|
y=y/256;buf[7]=(unsigned char)(y%256); |
|
|
|
if(x<0) buf[7]|=0x80; |
|
} |
|
|
|
/* |
|
* This function will ensure that no boundary errors occur. It also increments *src |
|
* by nbytes when done. |
|
*/ |
|
void plBSDiffBuffer::ISafeMemcpy(unsigned char *dest, unsigned char *src, size_t nbytes, |
|
unsigned char *destend, unsigned char *srcend) |
|
{ |
|
if((destend - dest < nbytes) || |
|
(srcend - src < nbytes)) |
|
{ |
|
hsAssert(false,"Corrupt patch\n"); |
|
} |
|
memcpy(dest,src,nbytes); |
|
} |
|
|
|
void plBSDiffBuffer::ISplit(Int32 *I,Int32 *V,UInt32 start,UInt32 len,UInt32 h) |
|
{ |
|
UInt32 i,j,k,x,tmp,jj,kk; |
|
|
|
if(len<16) { |
|
for(k=start;k<start+len;k+=j) { |
|
j=1;x=V[I[k]+h]; |
|
for(i=1;k+i<start+len;i++) { |
|
if(V[I[k+i]+h]<x) { |
|
x=V[I[k+i]+h]; |
|
j=0; |
|
}; |
|
if(V[I[k+i]+h]==x) { |
|
tmp=I[k+j];I[k+j]=I[k+i];I[k+i]=tmp; |
|
j++; |
|
}; |
|
}; |
|
for(i=0;i<j;i++) V[I[k+i]]=k+j-1; |
|
if(j==1) I[k]=-1; |
|
}; |
|
return; |
|
}; |
|
|
|
x=V[I[start+len/2]+h]; |
|
jj=0;kk=0; |
|
for(i=start;i<start+len;i++) { |
|
if(V[I[i]+h]<x) jj++; |
|
if(V[I[i]+h]==x) kk++; |
|
}; |
|
jj+=start;kk+=jj; |
|
|
|
i=start;j=0;k=0; |
|
while(i<jj) { |
|
if(V[I[i]+h]<x) { |
|
i++; |
|
} else if(V[I[i]+h]==x) { |
|
tmp=I[i];I[i]=I[jj+j];I[jj+j]=tmp; |
|
j++; |
|
} else { |
|
tmp=I[i];I[i]=I[kk+k];I[kk+k]=tmp; |
|
k++; |
|
}; |
|
}; |
|
|
|
while(jj+j<kk) { |
|
if(V[I[jj+j]+h]==x) { |
|
j++; |
|
} else { |
|
tmp=I[jj+j];I[jj+j]=I[kk+k];I[kk+k]=tmp; |
|
k++; |
|
}; |
|
}; |
|
|
|
if(jj>start) ISplit(I,V,start,jj-start,h); |
|
|
|
for(i=0;i<kk-jj;i++) V[I[jj+i]]=kk-1; |
|
if(jj==kk-1) I[jj]=-1; |
|
|
|
if(start+len>kk) ISplit(I,V,kk,start+len-kk,h); |
|
} |
|
|
|
void plBSDiffBuffer::IQSuffixSort(Int32 *I,Int32 *V,unsigned char *old,UInt32 oldsize) |
|
{ |
|
UInt32 buckets[256]; |
|
UInt32 i,h,len; |
|
|
|
for(i=0;i<256;i++) buckets[i]=0; |
|
for(i=0;i<oldsize;i++) buckets[old[i]]++; |
|
for(i=1;i<256;i++) buckets[i]+=buckets[i-1]; |
|
for(i=255;i>0;i--) buckets[i]=buckets[i-1]; |
|
buckets[0]=0; |
|
|
|
for(i=0;i<oldsize;i++) I[++buckets[old[i]]]=i; |
|
I[0]=oldsize; |
|
for(i=0;i<oldsize;i++) V[i]=buckets[old[i]]; |
|
V[oldsize]=0; |
|
for(i=1;i<256;i++) if(buckets[i]==buckets[i-1]+1) I[buckets[i]]=-1; |
|
I[0]=-1; |
|
|
|
// oldsize converted to Int32 to stop warning about negating unsigned numbers |
|
for(h=1;I[0]!=-(Int32)(oldsize+1);h+=h) { |
|
len=0; |
|
for(i=0;i<oldsize+1;) { |
|
if(I[i]<0) { |
|
len-=I[i]; |
|
i-=I[i]; |
|
} else { |
|
// len converted to Int32 to stop the warning about negating an unsigned number |
|
if(len) I[i-len]=-(Int32)len; |
|
len=V[I[i]]+1-i; |
|
ISplit(I,V,i,len,h); |
|
i+=len; |
|
len=0; |
|
}; |
|
}; |
|
// len converted to Int32 to stop the warning about negating an unsigned number |
|
if(len) I[i-len]=-(Int32)len; |
|
}; |
|
|
|
for(i=0;i<oldsize+1;i++) I[V[i]]=i; |
|
} |
|
|
|
UInt32 plBSDiffBuffer::IMatchLen( unsigned char *oldBuffer, UInt32 oldLength, |
|
unsigned char *newBuffer, UInt32 newLength) |
|
{ |
|
UInt32 i; |
|
|
|
for(i=0;(i<oldLength)&&(i<newLength);i++) |
|
if(oldBuffer[i]!=newBuffer[i]) break; |
|
|
|
return i; |
|
} |
|
|
|
UInt32 plBSDiffBuffer::ISearch( Int32 *I, |
|
unsigned char *oldBuffer, UInt32 oldLength, |
|
unsigned char *newBuffer, UInt32 newLength, |
|
UInt32 st, UInt32 en, Int32 *pos) |
|
{ |
|
UInt32 x,y; |
|
|
|
if(en-st<2) { |
|
x=IMatchLen(oldBuffer+I[st],oldLength-I[st],newBuffer,newLength); |
|
y=IMatchLen(oldBuffer+I[en],oldLength-I[en],newBuffer,newLength); |
|
|
|
if(x>y) { |
|
*pos=I[st]; |
|
return x; |
|
} else { |
|
*pos=I[en]; |
|
return y; |
|
} |
|
}; |
|
|
|
x=st+(en-st)/2; |
|
if(memcmp(oldBuffer+I[x],newBuffer,plBSDiffBuffer_MIN(oldLength-I[x],newLength))<0) { |
|
return ISearch(I,oldBuffer,oldLength,newBuffer,newLength,x,en,pos); |
|
} else { |
|
return ISearch(I,oldBuffer,oldLength,newBuffer,newLength,st,x,pos); |
|
}; |
|
}
|
|
|