| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include <string.h> |
| #include <assert.h> |
| #include <stdlib.h> |
| #include "sqlite3ext.h" |
| SQLITE_EXTENSION_INIT1 |
|
|
| #ifndef SQLITE_AMALGAMATION |
| |
| |
| |
| |
| typedef unsigned int u32; |
| typedef sqlite3_uint64 u64; |
|
|
| |
| |
| |
| 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( (u64)ofst+(u64)cnt > (u64)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; |
| if( pCur->iCursor >= pCur->nDelta ){ |
| pCur->eOp = DELTAPARSE_OP_ERROR; |
| pCur->iNext = pCur->nDelta; |
| return SQLITE_OK; |
| } |
| z = pCur->aDelta + pCur->iCursor; |
| pCur->a1 = deltaGetInt(&z, &i); |
| switch( z[0] ){ |
| case '@': { |
| z++; |
| if( pCur->iNext>=pCur->nDelta ){ |
| pCur->eOp = DELTAPARSE_OP_ERROR; |
| pCur->iNext = pCur->nDelta; |
| break; |
| } |
| 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 ){ |
| if( pCur->a2 + pCur->a1 > pCur->nDelta ){ |
| sqlite3_result_zeroblob(ctx, pCur->a1); |
| }else{ |
| 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 || pCur->iCursor>=pCur->nDelta; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| 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, |
| 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; |
| } |
|
|