2019-02-19 18:39:16 +00:00
/*
* * 2019 - 02 - 19
* *
* * The author disclaims copyright to this source code . In place of
* * a legal notice , here is a blessing :
* *
* * May you do good and not evil .
* * May you find forgiveness for yourself and forgive others .
* * May you share freely , never taking more than you give .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
2019-03-02 20:09:35 +00:00
* * This SQLite extension implements the delta functions used by the RBU
* * extension . Three scalar functions and one table - valued function are
* * implemented here :
* *
* * delta_apply ( X , D ) - - apply delta D to file X and return the result
* * delta_create ( X , Y ) - - compute and return a delta that carries X into Y
* * delta_output_size ( D ) - - blob size in bytes output from applying delta D
* * delta_parse ( D ) - - returns rows describing delta D
* *
* * The delta format is the Fossil delta format , described in a comment
* * on the delete_create ( ) function implementation below , and also at
* *
* * https : //www.fossil-scm.org/fossil/doc/trunk/www/delta_format.wiki
* *
* * This delta format is used by the RBU extension , which is the main
* * reason that these routines are included in the extension library .
* * RBU does not use this extension directly . Rather , this extension is
* * provided as a convenience to developers who want to analyze RBU files
* * that contain deltas .
2019-02-19 18:39:16 +00:00
*/
# include <string.h>
# include <assert.h>
# include <stdlib.h>
# include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
2019-07-23 13:26:43 +00:00
# ifndef SQLITE_AMALGAMATION
2019-02-19 18:39:16 +00:00
/*
* * The " u32 " type must be an unsigned 32 - bit integer . Adjust this
*/
typedef unsigned int u32 ;
/*
* * Must be a 16 - bit value
*/
typedef short int s16 ;
typedef unsigned short int u16 ;
2019-07-23 13:26:43 +00:00
# endif /* SQLITE_AMALGAMATION */
2019-02-19 18:39:16 +00:00
/*
* * The width of a hash window in bytes . The algorithm only works if this
* * is a power of 2.
*/
# define NHASH 16
/*
* * The current state of the rolling hash .
* *
* * z [ ] holds the values that have been hashed . z [ ] is a circular buffer .
* * z [ i ] is the first entry and z [ ( i + NHASH - 1 ) % NHASH ] is the last entry of
* * the window .
* *
* * Hash . a is the sum of all elements of hash . z [ ] . Hash . b is a weighted
* * sum . Hash . b is z [ i ] * NHASH + z [ i + 1 ] * ( NHASH - 1 ) + . . . + z [ i + NHASH - 1 ] * 1.
* * ( Each index for z [ ] should be module NHASH , of course . The % NHASH operator
* * is omitted in the prior expression for brevity . )
*/
typedef struct hash hash ;
struct hash {
u16 a , b ; /* Hash values */
u16 i ; /* Start of the hash window */
char z [ NHASH ] ; /* The values that have been hashed */
} ;
/*
* * Initialize the rolling hash using the first NHASH characters of z [ ]
*/
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 ;
}
/*
* * Advance the rolling hash by a single character " c "
*/
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 ;
}
/*
* * Return a 32 - bit hash value
*/
static u32 hash_32bit ( hash * pHash ) {
return ( pHash - > a & 0xffff ) | ( ( ( u32 ) ( pHash - > b & 0xffff ) ) < < 16 ) ;
}
/*
* * Compute a hash on NHASH bytes .
* *
* * This routine is intended to be equivalent to :
* * hash h ;
* * hash_init ( & h , zInput ) ;
* * return hash_32bit ( & h ) ;
*/
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 ) ;
}
/*
* * Write an base - 64 integer into the given buffer .
*/
static void putInt ( unsigned int v , char * * pz ) {
static const char zDigits [ ] =
" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~ " ;
/* 123456789 123456789 123456789 123456789 123456789 123456789 123 */
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 ] ;
}
}
/*
* * Read bytes from * pz and convert them into a positive integer . When
* * finished , leave * pz pointing to the first character past the end of
* * the integer . The * pLen parameter holds the length of the string
* * in * pz and is decremented once for each character in the integer .
*/
2019-02-19 20:19:51 +00:00
static unsigned int deltaGetInt ( const char * * pz , int * pLen ) {
2019-02-19 18:39:16 +00:00
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 ;
}
/*
* * Return the number digits in the base - 64 representation of a positive integer
*/
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
/*
* * Compute a 32 - bit big - endian checksum on the N - byte buffer . If the
* * buffer is not a multiple of 4 bytes length , compute the sum that would
* * have occurred if the buffer was padded with zeros to the next multiple
* * of four bytes .
*/
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 ) ; /* Four-byte alignment */
if ( 0 = = * ( char * ) & byteOrderTest ) {
/* This is a big-endian machine */
while ( z < zEnd ) {
sum + = * ( unsigned * ) z ;
z + = 4 ;
}
} else {
/* A little-endian machine */
# 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 ;
}
/*
* * Create a new delta .
* *
* * The delta is written into a preallocated buffer , zDelta , which
* * should be at least 60 bytes longer than the target file , zOut .
* * The delta string will be NUL - terminated , but it might also contain
* * embedded NUL characters if either the zSrc or zOut files are
* * binary . This function returns the length of the delta string
* * in bytes , excluding the final NUL terminator character .
* *
* * Output Format :
* *
* * The delta begins with a base64 number followed by a newline . This
* * number is the number of bytes in the TARGET file . Thus , given a
* * delta file z , a program can compute the size of the output file
* * simply by reading the first line and decoding the base - 64 number
* * found there . The delta_output_size ( ) routine does exactly this .
* *
* * After the initial size number , the delta consists of a series of
* * literal text segments and commands to copy from the SOURCE file .
* * A copy command looks like this :
* *
* * NNN @ MMM ,
* *
* * where NNN is the number of bytes to be copied and MMM is the offset
* * into the source file of the first byte ( both base - 64 ) . If NNN is 0
* * it means copy the rest of the input file . Literal text is like this :
* *
* * NNN : TTTTT
* *
* * where NNN is the number of bytes of text ( base - 64 ) and TTTTT is the text .
* *
* * The last term is of the form
* *
* * NNN ;
* *
* * In this case , NNN is a 32 - bit bigendian checksum of the output file
* * that can be used to verify that the delta applied correctly . All
* * numbers are in base - 64.
* *
* * Pure text files generate a pure text delta . Binary files generate a
* * delta that may contain some binary data .
* *
* * Algorithm :
* *
* * The encoder first builds a hash table to help it find matching
* * patterns in the source file . 16 - byte chunks of the source file
* * sampled at evenly spaced intervals are used to populate the hash
* * table .
* *
* * Next we begin scanning the target file using a sliding 16 - byte
* * window . The hash of the 16 - byte window in the target is used to
* * search for a matching section in the source file . When a match
* * is found , a copy command is added to the delta . An effort is
* * made to extend the matching section to regions that come before
* * and after the 16 - byte hash window . A copy command is only issued
* * if the result would use less space that just quoting the text
* * literally . Literal text is added to the delta for sections that
* * do not match or which can not be encoded efficiently using copy
* * commands .
*/
static int delta_create (
const char * zSrc , /* The source or pattern file */
unsigned int lenSrc , /* Length of the source file */
const char * zOut , /* The target file */
unsigned int lenOut , /* Length of the target file */
char * zDelta /* Write the delta into this buffer */
) {
int i , base ;
char * zOrigDelta = zDelta ;
hash h ;
int nHash ; /* Number of hash table entries */
int * landmark ; /* Primary hash table */
int * collide ; /* Collision chain */
int lastRead = - 1 ; /* Last byte of zSrc read by a COPY command */
/* Add the target file size to the beginning of the delta
*/
putInt ( lenOut , & zDelta ) ;
* ( zDelta + + ) = ' \n ' ;
/* If the source file is very small, it means that we have no
* * chance of ever doing a copy command . Just output a single
* * literal segment for the entire target and exit .
*/
if ( lenSrc < = NHASH ) {
putInt ( lenOut , & zDelta ) ;
* ( zDelta + + ) = ' : ' ;
memcpy ( zDelta , zOut , lenOut ) ;
zDelta + = lenOut ;
putInt ( checksum ( zOut , lenOut ) , & zDelta ) ;
* ( zDelta + + ) = ' ; ' ;
return zDelta - zOrigDelta ;
}
/* Compute the hash table used to locate matching sections in the
* * source file .
*/
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 ;
}
/* Begin scanning the target file and generating copy commands and
* * literal sections of the delta .
*/
base = 0 ; /* We have already generated everything before zOut[base] */
while ( base + NHASH < lenOut ) {
int iSrc , iBlock ;
unsigned int bestCnt , bestOfst = 0 , bestLitsz = 0 ;
hash_init ( & h , & zOut [ base ] ) ;
i = 0 ; /* Trying to match a landmark against zOut[base+i] */
bestCnt = 0 ;
while ( 1 ) {
int hv ;
int limit = 250 ;
hv = hash_32bit ( & h ) % nHash ;
iBlock = landmark [ hv ] ;
while ( iBlock > = 0 & & ( limit - - ) > 0 ) {
/*
* * The hash window has identified a potential match against
* * landmark block iBlock . But we need to investigate further .
* *
* * Look for a region in zOut that matches zSrc . Anchor the search
* * at zSrc [ iSrc ] and zOut [ base + i ] . Do not include anything prior to
* * zOut [ base ] or after zOut [ outLen ] nor anything after zSrc [ srcLen ] .
* *
* * Set cnt equal to the length of the match and set ofst so that
* * zSrc [ ofst ] is the first element of the match . litsz is the number
* * of characters between zOut [ base ] and the beginning of the match .
* * sz will be the overhead ( in bytes ) needed to encode the copy
* * command . Only generate copy command if the overhead of the
* * copy command is less than the amount of literal text to be copied .
*/
int cnt , ofst , litsz ;
int j , k , x , y ;
int sz ;
int limitX ;
/* Beginning at iSrc, match forwards as far as we can. j counts
* * the number of characters that match */
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 ;
/* Beginning at iSrc-1, match backwards as far as we can. k counts
* * the number of characters that match */
for ( k = 1 ; k < iSrc & & k < = i ; k + + ) {
if ( zSrc [ iSrc - k ] ! = zOut [ base + i - k ] ) break ;
}
k - - ;
/* Compute the offset and size of the matching region */
ofst = iSrc - k ;
cnt = j + k + 1 ;
litsz = i - k ; /* Number of bytes of literal text before the copy */
/* sz will hold the number of bytes needed to encode the "insert"
* * command and the copy command , not counting the " insert " text */
sz = digit_count ( i - k ) + digit_count ( cnt ) + digit_count ( ofst ) + 3 ;
if ( cnt > = sz & & cnt > bestCnt ) {
/* Remember this match only if it is the best so far and it
* * does not increase the file size */
bestCnt = cnt ;
bestOfst = iSrc - k ;
bestLitsz = litsz ;
}
/* Check the next matching block */
iBlock = collide [ iBlock ] ;
}
/* We have a copy command that does not cause the delta to be larger
* * than a literal insert . So add the copy command to the delta .
*/
if ( bestCnt > 0 ) {
if ( bestLitsz > 0 ) {
/* Add an insert command before the copy */
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 we reach this point, it means no match is found so far */
if ( base + i + NHASH > = lenOut ) {
/* We have reached the end of the file and have not found any
* * matches . Do an " insert " for everything that does not match */
putInt ( lenOut - base , & zDelta ) ;
* ( zDelta + + ) = ' : ' ;
memcpy ( zDelta , & zOut [ base ] , lenOut - base ) ;
zDelta + = lenOut - base ;
base = lenOut ;
break ;
}
/* Advance the hash by one character. Keep looking for a match */
hash_next ( & h , zOut [ base + i + NHASH ] ) ;
i + + ;
}
}
/* Output a final "insert" record to get all the text at the end of
* * the file that does not match anything in the source file .
*/
if ( base < lenOut ) {
putInt ( lenOut - base , & zDelta ) ;
* ( zDelta + + ) = ' : ' ;
memcpy ( zDelta , & zOut [ base ] , lenOut - base ) ;
zDelta + = lenOut - base ;
}
/* Output the final checksum record. */
putInt ( checksum ( zOut , lenOut ) , & zDelta ) ;
* ( zDelta + + ) = ' ; ' ;
sqlite3_free ( collide ) ;
return zDelta - zOrigDelta ;
}
/*
* * Return the size ( in bytes ) of the output from applying
* * a delta .
* *
* * This routine is provided so that an procedure that is able
* * to call delta_apply ( ) can learn how much space is required
* * for the output and hence allocate nor more space that is really
* * needed .
*/
static int delta_output_size ( const char * zDelta , int lenDelta ) {
int size ;
2019-02-19 20:19:51 +00:00
size = deltaGetInt ( & zDelta , & lenDelta ) ;
2019-02-19 18:39:16 +00:00
if ( * zDelta ! = ' \n ' ) {
/* ERROR: size integer not terminated by "\n" */
return - 1 ;
}
return size ;
}
/*
* * Apply a delta .
* *
* * The output buffer should be big enough to hold the whole output
* * file and a NUL terminator at the end . The delta_output_size ( )
* * routine will determine this size for you .
* *
* * The delta string should be null - terminated . But the delta string
* * may contain embedded NUL characters ( if the input and output are
* * binary files ) so we also have to pass in the length of the delta in
* * the lenDelta parameter .
* *
* * This function returns the size of the output file in bytes ( excluding
* * the final NUL terminator character ) . Except , if the delta string is
* * malformed or intended for use with a source file other than zSrc ,
* * then this routine returns - 1.
* *
* * Refer to the delta_create ( ) documentation above for a description
* * of the delta file format .
*/
static int delta_apply (
const char * zSrc , /* The source or pattern file */
int lenSrc , /* Length of the source file */
const char * zDelta , /* Delta to apply to the pattern */
int lenDelta , /* Length of the delta */
char * zOut /* Write the output into this preallocated buffer */
) {
unsigned int limit ;
unsigned int total = 0 ;
# ifdef FOSSIL_ENABLE_DELTA_CKSUM_TEST
char * zOrigOut = zOut ;
# endif
2019-02-19 20:19:51 +00:00
limit = deltaGetInt ( & zDelta , & lenDelta ) ;
2019-02-19 18:39:16 +00:00
if ( * zDelta ! = ' \n ' ) {
/* ERROR: size integer not terminated by "\n" */
return - 1 ;
}
zDelta + + ; lenDelta - - ;
while ( * zDelta & & lenDelta > 0 ) {
unsigned int cnt , ofst ;
2019-02-19 20:19:51 +00:00
cnt = deltaGetInt ( & zDelta , & lenDelta ) ;
2019-02-19 18:39:16 +00:00
switch ( zDelta [ 0 ] ) {
case ' @ ' : {
zDelta + + ; lenDelta - - ;
2019-02-19 20:19:51 +00:00
ofst = deltaGetInt ( & zDelta , & lenDelta ) ;
2019-02-19 18:39:16 +00:00
if ( lenDelta > 0 & & zDelta [ 0 ] ! = ' , ' ) {
/* ERROR: copy command not terminated by ',' */
return - 1 ;
}
zDelta + + ; lenDelta - - ;
total + = cnt ;
if ( total > limit ) {
/* ERROR: copy exceeds output file size */
return - 1 ;
}
if ( ofst + cnt > lenSrc ) {
/* ERROR: copy extends past end of input */
return - 1 ;
}
memcpy ( zOut , & zSrc [ ofst ] , cnt ) ;
zOut + = cnt ;
break ;
}
case ' : ' : {
zDelta + + ; lenDelta - - ;
total + = cnt ;
if ( total > limit ) {
/* ERROR: insert command gives an output larger than predicted */
return - 1 ;
}
if ( cnt > lenDelta ) {
/* ERROR: insert count exceeds size of delta */
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 ) ) {
/* ERROR: bad checksum */
return - 1 ;
}
# endif
if ( total ! = limit ) {
/* ERROR: generated size does not match predicted size */
return - 1 ;
}
return total ;
}
default : {
/* ERROR: unknown delta operator */
return - 1 ;
}
}
}
/* ERROR: unterminated delta */
return - 1 ;
}
/*
2019-02-25 14:52:43 +00:00
* * SQL functions : delta_create ( X , Y )
2019-02-19 18:39:16 +00:00
* *
* * Return a delta for carrying X into Y .
*/
static void deltaCreateFunc (
sqlite3_context * context ,
int argc ,
sqlite3_value * * argv
) {
const char * aOrig ; int nOrig ; /* old blob */
const char * aNew ; int nNew ; /* new blob */
char * aOut ; int nOut ; /* output delta */
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 ) ;
}
}
}
/*
2019-02-25 14:52:43 +00:00
* * SQL functions : delta_apply ( X , D )
2019-02-19 18:39:16 +00:00
* *
* * Return the result of applying delta D to input X .
*/
static void deltaApplyFunc (
sqlite3_context * context ,
int argc ,
sqlite3_value * * argv
) {
const char * aOrig ; int nOrig ; /* The X input */
const char * aDelta ; int nDelta ; /* The input delta (D) */
char * aOut ; int nOut , nOut2 ; /* The output */
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 ] ) ;
/* Figure out the size of the output */
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 ) ;
}
}
}
/*
2019-02-25 14:52:43 +00:00
* * SQL functions : delta_output_size ( D )
2019-02-19 18:39:16 +00:00
* *
* * Return the size of the output that results from applying delta D .
*/
static void deltaOutputSizeFunc (
sqlite3_context * context ,
int argc ,
sqlite3_value * * argv
) {
const char * aDelta ; int nDelta ; /* The input delta (D) */
int nOut ; /* Size of output */
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 ] ) ;
/* Figure out the size of the output */
nOut = delta_output_size ( aDelta , nDelta ) ;
if ( nOut < 0 ) {
sqlite3_result_error ( context , " corrupt fossil delta " , - 1 ) ;
return ;
} else {
sqlite3_result_int ( context , nOut ) ;
}
}
2019-02-25 14:52:43 +00:00
/*****************************************************************************
* * Table - valued SQL function : delta_parse ( DELTA )
* *
* * Schema :
* *
* * CREATE TABLE delta_parse (
* * op TEXT ,
* * a1 INT ,
* * a2 ANY ,
* * delta HIDDEN BLOB
* * ) ;
* *
* * Given an input DELTA , this function parses the delta and returns
* * rows for each entry in the delta . The op column has one of the
* * values SIZE , COPY , INSERT , CHECKSUM , ERROR .
* *
* * Assuming no errors , the first row has op = ' SIZE ' . a1 is the size of
* * the output in bytes and a2 is NULL .
* *
* * After the initial SIZE row , there are zero or more ' COPY ' and / or ' INSERT '
* * rows . A COPY row means content is copied from the source into the
* * output . Column a1 is the number of bytes to copy and a2 is the offset
* * into source from which to begin copying . An INSERT row means to
* * insert text into the output stream . Column a1 is the number of bytes
* * to insert and column is a BLOB that contains the text to be inserted .
* *
* * The last row of a well - formed delta will have an op value of ' CHECKSUM ' .
* * The a1 column will be the value of the checksum and a2 will be NULL .
* *
* * If the input delta is not well - formed , then a row with an op value
* * of ' ERROR ' is returned . The a1 value of the ERROR row is the offset
* * into the delta where the error was encountered and a2 is NULL .
2019-02-19 20:19:51 +00:00
*/
typedef struct deltaparsevtab_vtab deltaparsevtab_vtab ;
typedef struct deltaparsevtab_cursor deltaparsevtab_cursor ;
struct deltaparsevtab_vtab {
sqlite3_vtab base ; /* Base class - must be first */
/* No additional information needed */
} ;
struct deltaparsevtab_cursor {
sqlite3_vtab_cursor base ; /* Base class - must be first */
char * aDelta ; /* The delta being parsed */
int nDelta ; /* Number of bytes in the delta */
int iCursor ; /* Current cursor location */
int eOp ; /* Name of current operator */
unsigned int a1 , a2 ; /* Arguments to current operator */
int iNext ; /* Next cursor value */
} ;
/* Operator names:
*/
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
/*
* * The deltaparsevtabConnect ( ) method is invoked to create a new
* * deltaparse virtual table .
* *
* * Think of this routine as the constructor for deltaparsevtab_vtab objects .
* *
* * All this routine needs to do is :
* *
* * ( 1 ) Allocate the deltaparsevtab_vtab object and initialize all fields .
* *
* * ( 2 ) Tell SQLite ( via the sqlite3_declare_vtab ( ) interface ) what the
* * result set of queries against the virtual table will look like .
*/
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) "
) ;
/* For convenience, define symbolic names for the index to each column. */
# 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 ) ) ;
2020-01-07 19:45:40 +00:00
sqlite3_vtab_config ( db , SQLITE_VTAB_INNOCUOUS ) ;
2019-02-19 20:19:51 +00:00
}
return rc ;
}
/*
* * This method is the destructor for deltaparsevtab_vtab objects .
*/
static int deltaparsevtabDisconnect ( sqlite3_vtab * pVtab ) {
deltaparsevtab_vtab * p = ( deltaparsevtab_vtab * ) pVtab ;
sqlite3_free ( p ) ;
return SQLITE_OK ;
}
/*
* * Constructor for a new deltaparsevtab_cursor object .
*/
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 ;
}
/*
* * Destructor for a deltaparsevtab_cursor .
*/
static int deltaparsevtabClose ( sqlite3_vtab_cursor * cur ) {
deltaparsevtab_cursor * pCur = ( deltaparsevtab_cursor * ) cur ;
2019-05-27 11:09:55 +00:00
sqlite3_free ( pCur - > aDelta ) ;
2019-02-19 20:19:51 +00:00
sqlite3_free ( pCur ) ;
return SQLITE_OK ;
}
/*
* * Advance a deltaparsevtab_cursor to its next row of output .
*/
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 ;
}
/*
* * Return values of columns for the row at which the deltaparsevtab_cursor
* * is currently pointing .
*/
static int deltaparsevtabColumn (
sqlite3_vtab_cursor * cur , /* The cursor */
sqlite3_context * ctx , /* First argument to sqlite3_result_...() */
int i /* Which column to return */
) {
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 ;
}
/*
* * Return the rowid for the current row . In this implementation , the
* * rowid is the same as the output value .
*/
static int deltaparsevtabRowid ( sqlite3_vtab_cursor * cur , sqlite_int64 * pRowid ) {
deltaparsevtab_cursor * pCur = ( deltaparsevtab_cursor * ) cur ;
* pRowid = pCur - > iCursor ;
return SQLITE_OK ;
}
/*
* * Return TRUE if the cursor has been moved off of the last
* * row of output .
*/
static int deltaparsevtabEof ( sqlite3_vtab_cursor * cur ) {
deltaparsevtab_cursor * pCur = ( deltaparsevtab_cursor * ) cur ;
return pCur - > eOp = = DELTAPARSE_OP_EOF ;
}
/*
* * This method is called to " rewind " the deltaparsevtab_cursor object back
* * to the first row of output . This method is always called at least
* * once prior to any call to deltaparsevtabColumn ( ) or deltaparsevtabRowid ( ) or
* * deltaparsevtabEof ( ) .
*/
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 ;
}
/*
* * SQLite will invoke this method one or more times while planning a query
* * that uses the virtual table . This routine needs to create
* * a query plan for each invocation and compute an estimated cost for that
* * plan .
*/
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 ;
2019-02-19 20:29:05 +00:00
return SQLITE_CONSTRAINT ;
2019-02-19 20:19:51 +00:00
}
/*
* * This following structure defines all the methods for the
* * virtual table .
*/
static sqlite3_module deltaparsevtabModule = {
/* iVersion */ 0 ,
/* xCreate */ 0 ,
/* xConnect */ deltaparsevtabConnect ,
/* xBestIndex */ deltaparsevtabBestIndex ,
/* xDisconnect */ deltaparsevtabDisconnect ,
/* xDestroy */ 0 ,
/* xOpen */ deltaparsevtabOpen ,
/* xClose */ deltaparsevtabClose ,
/* xFilter */ deltaparsevtabFilter ,
/* xNext */ deltaparsevtabNext ,
/* xEof */ deltaparsevtabEof ,
/* xColumn */ deltaparsevtabColumn ,
/* xRowid */ deltaparsevtabRowid ,
/* xUpdate */ 0 ,
/* xBegin */ 0 ,
/* xSync */ 0 ,
/* xCommit */ 0 ,
/* xRollback */ 0 ,
/* xFindMethod */ 0 ,
/* xRename */ 0 ,
/* xSavepoint */ 0 ,
/* xRelease */ 0 ,
/* xRollbackTo */ 0 ,
2023-10-06 12:51:05 +00:00
/* xShadowName */ 0 ,
/* xIntegrity */ 0
2019-02-19 20:19:51 +00:00
} ;
2019-02-19 18:39:16 +00:00
# ifdef _WIN32
__declspec ( dllexport )
# endif
int sqlite3_fossildelta_init (
sqlite3 * db ,
char * * pzErrMsg ,
const sqlite3_api_routines * pApi
) {
2020-01-09 23:07:21 +00:00
static const int enc = SQLITE_UTF8 | SQLITE_INNOCUOUS ;
2019-02-19 18:39:16 +00:00
int rc = SQLITE_OK ;
SQLITE_EXTENSION_INIT2 ( pApi ) ;
( void ) pzErrMsg ; /* Unused parameter */
2020-01-07 19:45:40 +00:00
rc = sqlite3_create_function ( db , " delta_create " , 2 , enc , 0 ,
2019-02-19 18:39:16 +00:00
deltaCreateFunc , 0 , 0 ) ;
if ( rc = = SQLITE_OK ) {
2020-01-07 19:45:40 +00:00
rc = sqlite3_create_function ( db , " delta_apply " , 2 , enc , 0 ,
2019-02-19 18:39:16 +00:00
deltaApplyFunc , 0 , 0 ) ;
}
if ( rc = = SQLITE_OK ) {
2020-01-07 19:45:40 +00:00
rc = sqlite3_create_function ( db , " delta_output_size " , 1 , enc , 0 ,
2019-02-19 18:39:16 +00:00
deltaOutputSizeFunc , 0 , 0 ) ;
}
2019-02-19 20:19:51 +00:00
if ( rc = = SQLITE_OK ) {
rc = sqlite3_create_module ( db , " delta_parse " , & deltaparsevtabModule , 0 ) ;
}
2019-02-19 18:39:16 +00:00
return rc ;
}