#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#ifndef SQLITE_AMALGAMATION
typedef unsigned int u32;
typedef short int s16;
typedef unsigned short int u16;
#endif
#define NHASH 16
typedef struct hash hash;
struct hash {
u16 a, b;
u16 i;
char z[NHASH];
};
static void hash_init(hash *pHash, const char *z){
u16 a, b, i;
a = b = z[0];
for(i=1; i<NHASH; i++){
a += z[i];
b += a;
}
memcpy(pHash->z, z, NHASH);
pHash->a = a & 0xffff;
pHash->b = b & 0xffff;
pHash->i = 0;
}
static void hash_next(hash *pHash, int c){
u16 old = pHash->z[pHash->i];
pHash->z[pHash->i] = c;
pHash->i = (pHash->i+1)&(NHASH-1);
pHash->a = pHash->a - old + c;
pHash->b = pHash->b - NHASH*old + pHash->a;
}
static u32 hash_32bit(hash *pHash){
return (pHash->a & 0xffff) | (((u32)(pHash->b & 0xffff))<<16);
}
static u32 hash_once(const char *z){
u16 a, b, i;
a = b = z[0];
for(i=1; i<NHASH; i++){
a += z[i];
b += a;
}
return a | (((u32)b)<<16);
}
static void putInt(unsigned int v, char **pz){
static const char zDigits[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~";
int i, j;
char zBuf[20];
if( v==0 ){
*(*pz)++ = '0';
return;
}
for(i=0; v>0; i++, v>>=6){
zBuf[i] = zDigits[v&0x3f];
}
for(j=i-1; j>=0; j--){
*(*pz)++ = zBuf[j];
}
}
static unsigned int deltaGetInt(const char **pz, int *pLen){
static const signed char zValue[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
-1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, 36,
-1, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, -1, -1, -1, 63, -1,
};
unsigned int v = 0;
int c;
unsigned char *z = (unsigned char*)*pz;
unsigned char *zStart = z;
while( (c = zValue[0x7f&*(z++)])>=0 ){
v = (v<<6) + c;
}
z--;
*pLen -= z - zStart;
*pz = (char*)z;
return v;
}
static int digit_count(int v){
unsigned int i, x;
for(i=1, x=64; v>=x; i++, x <<= 6){}
return i;
}
#ifdef __GNUC__
# define GCC_VERSION (__GNUC__*1000000+__GNUC_MINOR__*1000+__GNUC_PATCHLEVEL__)
#else
# define GCC_VERSION 0
#endif
static unsigned int checksum(const char *zIn, size_t N){
static const int byteOrderTest = 1;
const unsigned char *z = (const unsigned char *)zIn;
const unsigned char *zEnd = (const unsigned char*)&zIn[N&~3];
unsigned sum = 0;
assert( (z - (const unsigned char*)0)%4==0 );
if( 0==*(char*)&byteOrderTest ){
while( z<zEnd ){
sum += *(unsigned*)z;
z += 4;
}
}else{
#if GCC_VERSION>=4003000
while( z<zEnd ){
sum += __builtin_bswap32(*(unsigned*)z);
z += 4;
}
#elif defined(_MSC_VER) && _MSC_VER>=1300
while( z<zEnd ){
sum += _byteswap_ulong(*(unsigned*)z);
z += 4;
}
#else
unsigned sum0 = 0;
unsigned sum1 = 0;
unsigned sum2 = 0;
while(N >= 16){
sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]);
sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]);
sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]);
sum += ((unsigned)z[3] + z[7] + z[11]+ z[15]);
z += 16;
N -= 16;
}
while(N >= 4){
sum0 += z[0];
sum1 += z[1];
sum2 += z[2];
sum += z[3];
z += 4;
N -= 4;
}
sum += (sum2 << 8) + (sum1 << 16) + (sum0 << 24);
#endif
}
switch(N&3){
case 3: sum += (z[2] << 8);
case 2: sum += (z[1] << 16);
case 1: sum += (z[0] << 24);
default: ;
}
return sum;
}
static int delta_create(
const char *zSrc,
unsigned int lenSrc,
const char *zOut,
unsigned int lenOut,
char *zDelta
){
int i, base;
char *zOrigDelta = zDelta;
hash h;
int nHash;
int *landmark;
int *collide;
int lastRead = -1;
putInt(lenOut, &zDelta);
*(zDelta++) = '\n';
if( lenSrc<=NHASH ){
putInt(lenOut, &zDelta);
*(zDelta++) = ':';
memcpy(zDelta, zOut, lenOut);
zDelta += lenOut;
putInt(checksum(zOut, lenOut), &zDelta);
*(zDelta++) = ';';
return zDelta - zOrigDelta;
}
nHash = lenSrc/NHASH;
collide = sqlite3_malloc64( (sqlite3_int64)nHash*2*sizeof(int) );
memset(collide, -1, nHash*2*sizeof(int));
landmark = &collide[nHash];
for(i=0; i<lenSrc-NHASH; i+=NHASH){
int hv = hash_once(&zSrc[i]) % nHash;
collide[i/NHASH] = landmark[hv];
landmark[hv] = i/NHASH;
}
base = 0;
while( base+NHASH<lenOut ){
int iSrc, iBlock;
unsigned int bestCnt, bestOfst=0, bestLitsz=0;
hash_init(&h, &zOut[base]);
i = 0;
bestCnt = 0;
while( 1 ){
int hv;
int limit = 250;
hv = hash_32bit(&h) % nHash;
iBlock = landmark[hv];
while( iBlock>=0 && (limit--)>0 ){
int cnt, ofst, litsz;
int j, k, x, y;
int sz;
int limitX;
iSrc = iBlock*NHASH;
y = base+i;
limitX = ( lenSrc-iSrc <= lenOut-y ) ? lenSrc : iSrc + lenOut - y;
for(x=iSrc; x<limitX; x++, y++){
if( zSrc[x]!=zOut[y] ) break;
}
j = x - iSrc - 1;
for(k=1; k<iSrc && k<=i; k++){
if( zSrc[iSrc-k]!=zOut[base+i-k] ) break;
}
k--;
ofst = iSrc-k;
cnt = j+k+1;
litsz = i-k;
sz = digit_count(i-k)+digit_count(cnt)+digit_count(ofst)+3;
if( cnt>=sz && cnt>bestCnt ){
bestCnt = cnt;
bestOfst = iSrc-k;
bestLitsz = litsz;
}
iBlock = collide[iBlock];
}
if( bestCnt>0 ){
if( bestLitsz>0 ){
putInt(bestLitsz,&zDelta);
*(zDelta++) = ':';
memcpy(zDelta, &zOut[base], bestLitsz);
zDelta += bestLitsz;
base += bestLitsz;
}
base += bestCnt;
putInt(bestCnt, &zDelta);
*(zDelta++) = '@';
putInt(bestOfst, &zDelta);
*(zDelta++) = ',';
if( bestOfst + bestCnt -1 > lastRead ){
lastRead = bestOfst + bestCnt - 1;
}
bestCnt = 0;
break;
}
if( base+i+NHASH>=lenOut ){
putInt(lenOut-base, &zDelta);
*(zDelta++) = ':';
memcpy(zDelta, &zOut[base], lenOut-base);
zDelta += lenOut-base;
base = lenOut;
break;
}
hash_next(&h, zOut[base+i+NHASH]);
i++;
}
}
if( base<lenOut ){
putInt(lenOut-base, &zDelta);
*(zDelta++) = ':';
memcpy(zDelta, &zOut[base], lenOut-base);
zDelta += lenOut-base;
}
putInt(checksum(zOut, lenOut), &zDelta);
*(zDelta++) = ';';
sqlite3_free(collide);
return zDelta - zOrigDelta;
}
static int delta_output_size(const char *zDelta, int lenDelta){
int size;
size = deltaGetInt(&zDelta, &lenDelta);
if( *zDelta!='\n' ){
return -1;
}
return size;
}
static int delta_apply(
const char *zSrc,
int lenSrc,
const char *zDelta,
int lenDelta,
char *zOut
){
unsigned int limit;
unsigned int total = 0;
#ifdef FOSSIL_ENABLE_DELTA_CKSUM_TEST
char *zOrigOut = zOut;
#endif
limit = deltaGetInt(&zDelta, &lenDelta);
if( *zDelta!='\n' ){
return -1;
}
zDelta++; lenDelta--;
while( *zDelta && lenDelta>0 ){
unsigned int cnt, ofst;
cnt = deltaGetInt(&zDelta, &lenDelta);
switch( zDelta[0] ){
case '@': {
zDelta++; lenDelta--;
ofst = deltaGetInt(&zDelta, &lenDelta);
if( lenDelta>0 && zDelta[0]!=',' ){
return -1;
}
zDelta++; lenDelta--;
total += cnt;
if( total>limit ){
return -1;
}
if( ofst+cnt > lenSrc ){
return -1;
}
memcpy(zOut, &zSrc[ofst], cnt);
zOut += cnt;
break;
}
case ':': {
zDelta++; lenDelta--;
total += cnt;
if( total>limit ){
return -1;
}
if( cnt>lenDelta ){
return -1;
}
memcpy(zOut, zDelta, cnt);
zOut += cnt;
zDelta += cnt;
lenDelta -= cnt;
break;
}
case ';': {
zDelta++; lenDelta--;
zOut[0] = 0;
#ifdef FOSSIL_ENABLE_DELTA_CKSUM_TEST
if( cnt!=checksum(zOrigOut, total) ){
return -1;
}
#endif
if( total!=limit ){
return -1;
}
return total;
}
default: {
return -1;
}
}
}
return -1;
}
static void deltaCreateFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *aOrig; int nOrig;
const char *aNew; int nNew;
char *aOut; int nOut;
assert( argc==2 );
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
if( sqlite3_value_type(argv[1])==SQLITE_NULL ) return;
nOrig = sqlite3_value_bytes(argv[0]);
aOrig = (const char*)sqlite3_value_blob(argv[0]);
nNew = sqlite3_value_bytes(argv[1]);
aNew = (const char*)sqlite3_value_blob(argv[1]);
aOut = sqlite3_malloc64(nNew+70);
if( aOut==0 ){
sqlite3_result_error_nomem(context);
}else{
nOut = delta_create(aOrig, nOrig, aNew, nNew, aOut);
if( nOut<0 ){
sqlite3_free(aOut);
sqlite3_result_error(context, "cannot create fossil delta", -1);
}else{
sqlite3_result_blob(context, aOut, nOut, sqlite3_free);
}
}
}
static void deltaApplyFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *aOrig; int nOrig;
const char *aDelta; int nDelta;
char *aOut; int nOut, nOut2;
assert( argc==2 );
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
if( sqlite3_value_type(argv[1])==SQLITE_NULL ) return;
nOrig = sqlite3_value_bytes(argv[0]);
aOrig = (const char*)sqlite3_value_blob(argv[0]);
nDelta = sqlite3_value_bytes(argv[1]);
aDelta = (const char*)sqlite3_value_blob(argv[1]);
nOut = delta_output_size(aDelta, nDelta);
if( nOut<0 ){
sqlite3_result_error(context, "corrupt fossil delta", -1);
return;
}
aOut = sqlite3_malloc64((sqlite3_int64)nOut+1);
if( aOut==0 ){
sqlite3_result_error_nomem(context);
}else{
nOut2 = delta_apply(aOrig, nOrig, aDelta, nDelta, aOut);
if( nOut2!=nOut ){
sqlite3_free(aOut);
sqlite3_result_error(context, "corrupt fossil delta", -1);
}else{
sqlite3_result_blob(context, aOut, nOut, sqlite3_free);
}
}
}
static void deltaOutputSizeFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *aDelta; int nDelta;
int nOut;
assert( argc==1 );
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
nDelta = sqlite3_value_bytes(argv[0]);
aDelta = (const char*)sqlite3_value_blob(argv[0]);
nOut = delta_output_size(aDelta, nDelta);
if( nOut<0 ){
sqlite3_result_error(context, "corrupt fossil delta", -1);
return;
}else{
sqlite3_result_int(context, nOut);
}
}
typedef struct deltaparsevtab_vtab deltaparsevtab_vtab;
typedef struct deltaparsevtab_cursor deltaparsevtab_cursor;
struct deltaparsevtab_vtab {
sqlite3_vtab base;
};
struct deltaparsevtab_cursor {
sqlite3_vtab_cursor base;
char *aDelta;
int nDelta;
int iCursor;
int eOp;
unsigned int a1, a2;
int iNext;
};
static const char *azOp[] = {
"SIZE", "COPY", "INSERT", "CHECKSUM", "ERROR", "EOF"
};
#define DELTAPARSE_OP_SIZE 0
#define DELTAPARSE_OP_COPY 1
#define DELTAPARSE_OP_INSERT 2
#define DELTAPARSE_OP_CHECKSUM 3
#define DELTAPARSE_OP_ERROR 4
#define DELTAPARSE_OP_EOF 5
static int deltaparsevtabConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
deltaparsevtab_vtab *pNew;
int rc;
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(op,a1,a2,delta HIDDEN)"
);
#define DELTAPARSEVTAB_OP 0
#define DELTAPARSEVTAB_A1 1
#define DELTAPARSEVTAB_A2 2
#define DELTAPARSEVTAB_DELTA 3
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
}
return rc;
}
static int deltaparsevtabDisconnect(sqlite3_vtab *pVtab){
deltaparsevtab_vtab *p = (deltaparsevtab_vtab*)pVtab;
sqlite3_free(p);
return SQLITE_OK;
}
static int deltaparsevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
deltaparsevtab_cursor *pCur;
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
return SQLITE_OK;
}
static int deltaparsevtabClose(sqlite3_vtab_cursor *cur){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
sqlite3_free(pCur->aDelta);
sqlite3_free(pCur);
return SQLITE_OK;
}
static int deltaparsevtabNext(sqlite3_vtab_cursor *cur){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
const char *z;
int i = 0;
pCur->iCursor = pCur->iNext;
z = pCur->aDelta + pCur->iCursor;
pCur->a1 = deltaGetInt(&z, &i);
switch( z[0] ){
case '@': {
z++;
pCur->a2 = deltaGetInt(&z, &i);
pCur->eOp = DELTAPARSE_OP_COPY;
pCur->iNext = (int)(&z[1] - pCur->aDelta);
break;
}
case ':': {
z++;
pCur->a2 = (unsigned int)(z - pCur->aDelta);
pCur->eOp = DELTAPARSE_OP_INSERT;
pCur->iNext = (int)(&z[pCur->a1] - pCur->aDelta);
break;
}
case ';': {
pCur->eOp = DELTAPARSE_OP_CHECKSUM;
pCur->iNext = pCur->nDelta;
break;
}
default: {
if( pCur->iNext==pCur->nDelta ){
pCur->eOp = DELTAPARSE_OP_EOF;
}else{
pCur->eOp = DELTAPARSE_OP_ERROR;
pCur->iNext = pCur->nDelta;
}
break;
}
}
return SQLITE_OK;
}
static int deltaparsevtabColumn(
sqlite3_vtab_cursor *cur,
sqlite3_context *ctx,
int i
){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
switch( i ){
case DELTAPARSEVTAB_OP: {
sqlite3_result_text(ctx, azOp[pCur->eOp], -1, SQLITE_STATIC);
break;
}
case DELTAPARSEVTAB_A1: {
sqlite3_result_int(ctx, pCur->a1);
break;
}
case DELTAPARSEVTAB_A2: {
if( pCur->eOp==DELTAPARSE_OP_COPY ){
sqlite3_result_int(ctx, pCur->a2);
}else if( pCur->eOp==DELTAPARSE_OP_INSERT ){
sqlite3_result_blob(ctx, pCur->aDelta+pCur->a2, pCur->a1,
SQLITE_TRANSIENT);
}
break;
}
case DELTAPARSEVTAB_DELTA: {
sqlite3_result_blob(ctx, pCur->aDelta, pCur->nDelta, SQLITE_TRANSIENT);
break;
}
}
return SQLITE_OK;
}
static int deltaparsevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
*pRowid = pCur->iCursor;
return SQLITE_OK;
}
static int deltaparsevtabEof(sqlite3_vtab_cursor *cur){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
return pCur->eOp==DELTAPARSE_OP_EOF;
}
static int deltaparsevtabFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor *)pVtabCursor;
const char *a;
int i = 0;
pCur->eOp = DELTAPARSE_OP_ERROR;
if( idxNum!=1 ){
return SQLITE_OK;
}
pCur->nDelta = sqlite3_value_bytes(argv[0]);
a = (const char*)sqlite3_value_blob(argv[0]);
if( pCur->nDelta==0 || a==0 ){
return SQLITE_OK;
}
pCur->aDelta = sqlite3_malloc64( pCur->nDelta+1 );
if( pCur->aDelta==0 ){
pCur->nDelta = 0;
return SQLITE_NOMEM;
}
memcpy(pCur->aDelta, a, pCur->nDelta);
pCur->aDelta[pCur->nDelta] = 0;
a = pCur->aDelta;
pCur->eOp = DELTAPARSE_OP_SIZE;
pCur->a1 = deltaGetInt(&a, &i);
if( a[0]!='\n' ){
pCur->eOp = DELTAPARSE_OP_ERROR;
pCur->a1 = pCur->a2 = 0;
pCur->iNext = pCur->nDelta;
return SQLITE_OK;
}
a++;
pCur->iNext = (unsigned int)(a - pCur->aDelta);
return SQLITE_OK;
}
static int deltaparsevtabBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int i;
for(i=0; i<pIdxInfo->nConstraint; i++){
if( pIdxInfo->aConstraint[i].iColumn != DELTAPARSEVTAB_DELTA ) continue;
if( pIdxInfo->aConstraint[i].usable==0 ) continue;
if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
pIdxInfo->aConstraintUsage[i].omit = 1;
pIdxInfo->estimatedCost = (double)1;
pIdxInfo->estimatedRows = 10;
pIdxInfo->idxNum = 1;
return SQLITE_OK;
}
pIdxInfo->idxNum = 0;
pIdxInfo->estimatedCost = (double)0x7fffffff;
pIdxInfo->estimatedRows = 0x7fffffff;
return SQLITE_CONSTRAINT;
}
static sqlite3_module deltaparsevtabModule = {
0,
0,
deltaparsevtabConnect,
deltaparsevtabBestIndex,
deltaparsevtabDisconnect,
0,
deltaparsevtabOpen,
deltaparsevtabClose,
deltaparsevtabFilter,
deltaparsevtabNext,
deltaparsevtabEof,
deltaparsevtabColumn,
deltaparsevtabRowid,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
};
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_fossildelta_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
static const int enc = SQLITE_UTF8|SQLITE_INNOCUOUS;
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg;
rc = sqlite3_create_function(db, "delta_create", 2, enc, 0,
deltaCreateFunc, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "delta_apply", 2, enc, 0,
deltaApplyFunc, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "delta_output_size", 1, enc, 0,
deltaOutputSizeFunc, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_module(db, "delta_parse", &deltaparsevtabModule, 0);
}
return rc;
}