diff options
author | drh <drh@noemail.net> | 2018-02-09 23:25:14 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2018-02-09 23:25:14 +0000 |
commit | f313952097725eaf25f80b3431035d19bb1c5c72 (patch) | |
tree | 05cd6ce45ac920fb0310144f11d202cab3cb4288 /src/func.c | |
parent | 510fea885e5b4841a57d13c21958ce650029f408 (diff) | |
download | sqlite-f313952097725eaf25f80b3431035d19bb1c5c72.tar.gz sqlite-f313952097725eaf25f80b3431035d19bb1c5c72.zip |
Improve the performance of the built-in REPLACE() function in cases where
it does many substitutions that make the string larger. OSSFuzz is reporting
intermittant timeouts when running a test where it does a REPLACE() on a
930KB random blob. Perhaps this enhancement will fix that.
FossilOrigin-Name: fab2c2b07b5d3cd851db3e6f5c8a44155e32b0df22905ea33412b153b825a928
Diffstat (limited to 'src/func.c')
-rw-r--r-- | src/func.c | 43 |
1 files changed, 26 insertions, 17 deletions
diff --git a/src/func.c b/src/func.c index 4e80535e8..399f6dba3 100644 --- a/src/func.c +++ b/src/func.c @@ -1199,6 +1199,8 @@ static void replaceFunc( i64 nOut; /* Maximum size of zOut */ int loopLimit; /* Last zStr[] that might match zPattern[] */ int i, j; /* Loop counters */ + unsigned cntExpand; /* Number zOut expansions */ + sqlite3 *db = sqlite3_context_db_handle(context); assert( argc==3 ); UNUSED_PARAMETER(argc); @@ -1230,33 +1232,40 @@ static void replaceFunc( return; } loopLimit = nStr - nPattern; + cntExpand = 0; for(i=j=0; i<=loopLimit; i++){ if( zStr[i]!=zPattern[0] || memcmp(&zStr[i], zPattern, nPattern) ){ zOut[j++] = zStr[i]; }else{ - u8 *zOld; - sqlite3 *db = sqlite3_context_db_handle(context); - nOut += nRep - nPattern; - testcase( nOut-1==db->aLimit[SQLITE_LIMIT_LENGTH] ); - testcase( nOut-2==db->aLimit[SQLITE_LIMIT_LENGTH] ); - if( nOut-1>db->aLimit[SQLITE_LIMIT_LENGTH] ){ - sqlite3_result_error_toobig(context); - sqlite3_free(zOut); - return; - } - zOld = zOut; - zOut = sqlite3_realloc64(zOut, (int)nOut); - if( zOut==0 ){ - sqlite3_result_error_nomem(context); - sqlite3_free(zOld); - return; + if( nRep>nPattern ){ + nOut += nRep - nPattern; + if( nOut-1>db->aLimit[SQLITE_LIMIT_LENGTH] ){ + sqlite3_result_error_toobig(context); + sqlite3_free(zOut); + return; + } + testcase( nOut-1==db->aLimit[SQLITE_LIMIT_LENGTH] ); + testcase( nOut-2==db->aLimit[SQLITE_LIMIT_LENGTH] ); + cntExpand++; + if( (cntExpand&(cntExpand-1))==0 ){ + /* Grow the size of the output buffer only on substitutions + ** whose index is a power of two: 1, 2, 4, 8, 16, 32, ... */ + u8 *zOld; + zOld = zOut; + zOut = sqlite3_realloc64(zOut, (int)nOut + (nOut - nStr - 1)); + if( zOut==0 ){ + sqlite3_result_error_nomem(context); + sqlite3_free(zOld); + return; + } + } } memcpy(&zOut[j], zRep, nRep); j += nRep; i += nPattern-1; } } - assert( j+nStr-i+1==nOut ); + assert( j+nStr-i+1<=nOut ); memcpy(&zOut[j], &zStr[i], nStr-i); j += nStr - i; assert( j<=nOut ); |