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.
645 lines
17 KiB
645 lines
17 KiB
4 years ago
|
/*==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/>.
|
||
|
|
||
|
Additional permissions under GNU GPL version 3 section 7
|
||
|
|
||
|
If you modify this Program, or any covered work, by linking or
|
||
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||
|
(or a modified version of those libraries),
|
||
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||
|
licensors of this Program grant you additional
|
||
|
permission to convey the resulting work. Corresponding Source for a
|
||
|
non-source form of such a combination shall include the source code for
|
||
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||
|
work.
|
||
|
|
||
|
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);
|
||
|
};
|
||
|
}
|