diff options
Diffstat (limited to 'ext/pdo_sqlite/sqlite/src/util.c')
-rw-r--r-- | ext/pdo_sqlite/sqlite/src/util.c | 968 |
1 files changed, 725 insertions, 243 deletions
diff --git a/ext/pdo_sqlite/sqlite/src/util.c b/ext/pdo_sqlite/sqlite/src/util.c index de716d982..5ca9ec408 100644 --- a/ext/pdo_sqlite/sqlite/src/util.c +++ b/ext/pdo_sqlite/sqlite/src/util.c @@ -17,37 +17,173 @@ ** $Id$ */ #include "sqliteInt.h" +#include "os.h" #include <stdarg.h> #include <ctype.h> -#if SQLITE_MEMDEBUG>2 && defined(__GLIBC__) -#include <execinfo.h> -void print_stack_trace(){ - void *bt[30]; - int i; - int n = backtrace(bt, 30); +/* +** MALLOC WRAPPER ARCHITECTURE +** +** The sqlite code accesses dynamic memory allocation/deallocation by invoking +** the following six APIs (which may be implemented as macros). +** +** sqlite3Malloc() +** sqlite3MallocRaw() +** sqlite3Realloc() +** sqlite3ReallocOrFree() +** sqlite3Free() +** sqlite3AllocSize() +** +** The function sqlite3FreeX performs the same task as sqlite3Free and is +** guaranteed to be a real function. The same holds for sqlite3MallocX +** +** The above APIs are implemented in terms of the functions provided in the +** operating-system interface. The OS interface is never accessed directly +** by code outside of this file. +** +** sqlite3OsMalloc() +** sqlite3OsRealloc() +** sqlite3OsFree() +** sqlite3OsAllocationSize() +** +** Functions sqlite3MallocRaw() and sqlite3Realloc() may invoke +** sqlite3_release_memory() if a call to sqlite3OsMalloc() or +** sqlite3OsRealloc() fails (or if the soft-heap-limit for the thread is +** exceeded). Function sqlite3Malloc() usually invokes +** sqlite3MallocRaw(). +** +** MALLOC TEST WRAPPER ARCHITECTURE +** +** The test wrapper provides extra test facilities to ensure the library +** does not leak memory and handles the failure of the underlying OS level +** allocation system correctly. It is only present if the library is +** compiled with the SQLITE_MEMDEBUG macro set. +** +** * Guardposts to detect overwrites. +** * Ability to cause a specific Malloc() or Realloc() to fail. +** * Audit outstanding memory allocations (i.e check for leaks). +*/ + +#define MAX(x,y) ((x)>(y)?(x):(y)) - fprintf(stderr, "STACK: "); - for(i=0; i<n;i++){ - fprintf(stderr, "%p ", bt[i]); +#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) && !defined(SQLITE_OMIT_DISKIO) +/* +** Set the soft heap-size limit for the current thread. Passing a negative +** value indicates no limit. +*/ +void sqlite3_soft_heap_limit(int n){ + ThreadData *pTd = sqlite3ThreadData(); + if( pTd ){ + pTd->nSoftHeapLimit = n; } - fprintf(stderr, "\n"); + sqlite3ReleaseThreadData(); +} + +/* +** Release memory held by SQLite instances created by the current thread. +*/ +int sqlite3_release_memory(int n){ + return sqlite3pager_release_memory(n); } #else -#define print_stack_trace() +/* If SQLITE_ENABLE_MEMORY_MANAGEMENT is not defined, then define a version +** of sqlite3_release_memory() to be used by other code in this file. +** This is done for no better reason than to reduce the number of +** pre-processor #ifndef statements. +*/ +#define sqlite3_release_memory(x) 0 /* 0 == no memory freed */ +#endif + +#ifdef SQLITE_MEMDEBUG +/*-------------------------------------------------------------------------- +** Begin code for memory allocation system test layer. +** +** Memory debugging is turned on by defining the SQLITE_MEMDEBUG macro. +** +** SQLITE_MEMDEBUG==1 -> Fence-posting only (thread safe) +** SQLITE_MEMDEBUG==2 -> Fence-posting + linked list of allocations (not ts) +** SQLITE_MEMDEBUG==3 -> Above + backtraces (not thread safe, req. glibc) +*/ + +/* Figure out whether or not to store backtrace() information for each malloc. +** The backtrace() function is only used if SQLITE_MEMDEBUG is set to 2 or +** greater and glibc is in use. If we don't want to use backtrace(), then just +** define it as an empty macro and set the amount of space reserved to 0. +*/ +#if defined(__GLIBC__) && SQLITE_MEMDEBUG>2 + extern int backtrace(void **, int); + #define TESTALLOC_STACKSIZE 128 + #define TESTALLOC_STACKFRAMES ((TESTALLOC_STACKSIZE-8)/sizeof(void*)) +#else + #define backtrace(x, y) + #define TESTALLOC_STACKSIZE 0 + #define TESTALLOC_STACKFRAMES 0 #endif /* -** If malloc() ever fails, this global variable gets set to 1. -** This causes the library to abort and never again function. +** Number of 32-bit guard words. This should probably be a multiple of +** 2 since on 64-bit machines we want the value returned by sqliteMalloc() +** to be 8-byte aligned. */ -int sqlite3_malloc_failed = 0; +#ifndef TESTALLOC_NGUARD +# define TESTALLOC_NGUARD 2 +#endif /* -** If SQLITE_MEMDEBUG is defined, then use versions of malloc() and -** free() that track memory usage and check for buffer overruns. +** Size reserved for storing file-name along with each malloc()ed blob. */ -#ifdef SQLITE_MEMDEBUG +#define TESTALLOC_FILESIZE 64 + +/* +** Size reserved for storing the user string. Each time a Malloc() or Realloc() +** call succeeds, up to TESTALLOC_USERSIZE bytes of the string pointed to by +** sqlite3_malloc_id are stored along with the other test system metadata. +*/ +#define TESTALLOC_USERSIZE 64 +const char *sqlite3_malloc_id = 0; + +/* +** Blocks used by the test layer have the following format: +** +** <sizeof(void *) pNext pointer> +** <sizeof(void *) pPrev pointer> +** <TESTALLOC_NGUARD 32-bit guard words> +** <The application level allocation> +** <TESTALLOC_NGUARD 32-bit guard words> +** <32-bit line number> +** <TESTALLOC_FILESIZE bytes containing null-terminated file name> +** <TESTALLOC_STACKSIZE bytes of backtrace() output> +*/ + +#define TESTALLOC_OFFSET_GUARD1(p) (sizeof(void *) * 2) +#define TESTALLOC_OFFSET_DATA(p) ( \ + TESTALLOC_OFFSET_GUARD1(p) + sizeof(u32) * TESTALLOC_NGUARD \ +) +#define TESTALLOC_OFFSET_GUARD2(p) ( \ + TESTALLOC_OFFSET_DATA(p) + sqlite3OsAllocationSize(p) - TESTALLOC_OVERHEAD \ +) +#define TESTALLOC_OFFSET_LINENUMBER(p) ( \ + TESTALLOC_OFFSET_GUARD2(p) + sizeof(u32) * TESTALLOC_NGUARD \ +) +#define TESTALLOC_OFFSET_FILENAME(p) ( \ + TESTALLOC_OFFSET_LINENUMBER(p) + sizeof(u32) \ +) +#define TESTALLOC_OFFSET_USER(p) ( \ + TESTALLOC_OFFSET_FILENAME(p) + TESTALLOC_FILESIZE \ +) +#define TESTALLOC_OFFSET_STACK(p) ( \ + TESTALLOC_OFFSET_USER(p) + TESTALLOC_USERSIZE + 8 - \ + (TESTALLOC_OFFSET_USER(p) % 8) \ +) + +#define TESTALLOC_OVERHEAD ( \ + sizeof(void *)*2 + /* pPrev and pNext pointers */ \ + TESTALLOC_NGUARD*sizeof(u32)*2 + /* Guard words */ \ + sizeof(u32) + TESTALLOC_FILESIZE + /* File and line number */ \ + TESTALLOC_USERSIZE + /* User string */ \ + TESTALLOC_STACKSIZE /* backtrace() stack */ \ +) + /* ** For keeping track of the number of mallocs and frees. This @@ -58,35 +194,31 @@ int sqlite3_malloc_failed = 0; */ int sqlite3_nMalloc; /* Number of sqliteMalloc() calls */ int sqlite3_nFree; /* Number of sqliteFree() calls */ -int sqlite3_memUsed; /* Total memory obtained from malloc */ -int sqlite3_memMax; /* Mem usage high-water mark */ +int sqlite3_memUsed; /* TODO Total memory obtained from malloc */ +int sqlite3_memMax; /* TODO Mem usage high-water mark */ int sqlite3_iMallocFail; /* Fail sqliteMalloc() after this many calls */ int sqlite3_iMallocReset = -1; /* When iMallocFail reaches 0, set to this */ -#if SQLITE_MEMDEBUG>1 -static int memcnt = 0; -#endif -/* -** Number of 32-bit guard words. This should probably be a multiple of -** 2 since on 64-bit machines we want the value returned by sqliteMalloc() -** to be 8-byte aligned. -*/ -#define N_GUARD 2 +void *sqlite3_pFirst = 0; /* Pointer to linked list of allocations */ +int sqlite3_nMaxAlloc = 0; /* High water mark of ThreadData.nAlloc */ +int sqlite3_mallocDisallowed = 0; /* assert() in sqlite3Malloc() if set */ +int sqlite3_isFail = 0; /* True if all malloc calls should fail */ +const char *sqlite3_zFile = 0; /* Filename to associate debug info with */ +int sqlite3_iLine = 0; /* Line number for debug info */ /* ** Check for a simulated memory allocation failure. Return true if ** the failure should be simulated. Return false to proceed as normal. */ -static int simulatedMallocFailure(int n, char *zFile, int line){ +int sqlite3TestMallocFail(){ + if( sqlite3_isFail ){ + return 1; + } if( sqlite3_iMallocFail>=0 ){ sqlite3_iMallocFail--; if( sqlite3_iMallocFail==0 ){ - sqlite3_malloc_failed++; -#if SQLITE_MEMDEBUG>1 - fprintf(stderr,"**** failed to allocate %d bytes at %s:%d\n", - n, zFile,line); -#endif sqlite3_iMallocFail = sqlite3_iMallocReset; + sqlite3_isFail = 1; return 1; } } @@ -94,290 +226,516 @@ static int simulatedMallocFailure(int n, char *zFile, int line){ } /* -** Allocate new memory and set it to zero. Return NULL if -** no memory is available. +** The argument is a pointer returned by sqlite3OsMalloc() or xRealloc(). +** assert() that the first and last (TESTALLOC_NGUARD*4) bytes are set to the +** values set by the applyGuards() function. */ -void *sqlite3Malloc_(int n, int bZero, char *zFile, int line){ - void *p; - int *pi; - int i, k; - if( n==0 ){ - return 0; +static void checkGuards(u32 *p) +{ + int i; + char *zAlloc = (char *)p; + char *z; + + /* First set of guard words */ + z = &zAlloc[TESTALLOC_OFFSET_GUARD1(p)]; + for(i=0; i<TESTALLOC_NGUARD; i++){ + assert(((u32 *)z)[i]==0xdead1122); } - if( simulatedMallocFailure(n, zFile, line) ){ - return 0; + + /* Second set of guard words */ + z = &zAlloc[TESTALLOC_OFFSET_GUARD2(p)]; + for(i=0; i<TESTALLOC_NGUARD; i++){ + u32 guard = 0; + memcpy(&guard, &z[i*sizeof(u32)], sizeof(u32)); + assert(guard==0xdead3344); } - sqlite3_memUsed += n; - if( sqlite3_memMax<sqlite3_memUsed ) sqlite3_memMax = sqlite3_memUsed; - k = (n+sizeof(int)-1)/sizeof(int); - pi = malloc( (N_GUARD*2+1+k)*sizeof(int)); - if( pi==0 ){ - if( n>0 ) sqlite3_malloc_failed++; - return 0; +} + +/* +** The argument is a pointer returned by sqlite3OsMalloc() or Realloc(). The +** first and last (TESTALLOC_NGUARD*4) bytes are set to known values for use as +** guard-posts. +*/ +static void applyGuards(u32 *p) +{ + int i; + char *z; + char *zAlloc = (char *)p; + + /* First set of guard words */ + z = &zAlloc[TESTALLOC_OFFSET_GUARD1(p)]; + for(i=0; i<TESTALLOC_NGUARD; i++){ + ((u32 *)z)[i] = 0xdead1122; } - sqlite3_nMalloc++; - for(i=0; i<N_GUARD; i++) pi[i] = 0xdead1122; - pi[N_GUARD] = n; - for(i=0; i<N_GUARD; i++) pi[k+1+N_GUARD+i] = 0xdead3344; - p = &pi[N_GUARD+1]; - memset(p, bZero==0, n); -#if SQLITE_MEMDEBUG>1 - print_stack_trace(); - fprintf(stderr,"%06d malloc %d bytes at 0x%x from %s:%d\n", - ++memcnt, n, (int)p, zFile,line); -#endif - return p; + + /* Second set of guard words */ + z = &zAlloc[TESTALLOC_OFFSET_GUARD2(p)]; + for(i=0; i<TESTALLOC_NGUARD; i++){ + static const int guard = 0xdead3344; + memcpy(&z[i*sizeof(u32)], &guard, sizeof(u32)); + } + + /* Line number */ + z = &((char *)z)[TESTALLOC_NGUARD*sizeof(u32)]; /* Guard words */ + z = &zAlloc[TESTALLOC_OFFSET_LINENUMBER(p)]; + memcpy(z, &sqlite3_iLine, sizeof(u32)); + + /* File name */ + z = &zAlloc[TESTALLOC_OFFSET_FILENAME(p)]; + strncpy(z, sqlite3_zFile, TESTALLOC_FILESIZE); + z[TESTALLOC_FILESIZE - 1] = '\0'; + + /* User string */ + z = &zAlloc[TESTALLOC_OFFSET_USER(p)]; + z[0] = 0; + if( sqlite3_malloc_id ){ + strncpy(z, sqlite3_malloc_id, TESTALLOC_USERSIZE); + z[TESTALLOC_USERSIZE-1] = 0; + } + + /* backtrace() stack */ + z = &zAlloc[TESTALLOC_OFFSET_STACK(p)]; + backtrace((void **)z, TESTALLOC_STACKFRAMES); + + /* Sanity check to make sure checkGuards() is working */ + checkGuards(p); } /* -** This version of malloc is always a real function, never a macro +** The argument is a malloc()ed pointer as returned by the test-wrapper. +** Return a pointer to the Os level allocation. */ -void *sqlite3MallocX(int n){ - return sqlite3Malloc_(n, 0, __FILE__, __LINE__); +static void *getOsPointer(void *p) +{ + char *z = (char *)p; + return (void *)(&z[-1 * TESTALLOC_OFFSET_DATA(p)]); } + +#if SQLITE_MEMDEBUG>1 /* -** Check to see if the given pointer was obtained from sqliteMalloc() -** and is able to hold at least N bytes. Raise an exception if this -** is not the case. -** -** This routine is used for testing purposes only. +** The argument points to an Os level allocation. Link it into the threads list +** of allocations. */ -void sqlite3CheckMemory(void *p, int N){ - int *pi = p; - int n, i, k; - pi -= N_GUARD+1; - for(i=0; i<N_GUARD; i++){ - assert( pi[i]==0xdead1122 ); - } - n = pi[N_GUARD]; - assert( N>=0 && N<n ); - k = (n+sizeof(int)-1)/sizeof(int); - for(i=0; i<N_GUARD; i++){ - assert( pi[k+N_GUARD+1+i]==0xdead3344 ); +static void linkAlloc(void *p){ + void **pp = (void **)p; + pp[0] = 0; + pp[1] = sqlite3_pFirst; + if( sqlite3_pFirst ){ + ((void **)sqlite3_pFirst)[0] = p; } + sqlite3_pFirst = p; } /* -** Free memory previously obtained from sqliteMalloc() +** The argument points to an Os level allocation. Unlinke it from the threads +** list of allocations. */ -void sqlite3Free_(void *p, char *zFile, int line){ - if( p ){ - int *pi, i, k, n; - pi = p; - pi -= N_GUARD+1; - sqlite3_nFree++; - for(i=0; i<N_GUARD; i++){ - if( pi[i]!=0xdead1122 ){ - fprintf(stderr,"Low-end memory corruption at 0x%x\n", (int)p); - return; - } +static void unlinkAlloc(void *p) +{ + void **pp = (void **)p; + if( p==sqlite3_pFirst ){ + assert(!pp[0]); + assert(!pp[1] || ((void **)(pp[1]))[0]==p); + sqlite3_pFirst = pp[1]; + if( sqlite3_pFirst ){ + ((void **)sqlite3_pFirst)[0] = 0; } - n = pi[N_GUARD]; - sqlite3_memUsed -= n; - k = (n+sizeof(int)-1)/sizeof(int); - for(i=0; i<N_GUARD; i++){ - if( pi[k+N_GUARD+1+i]!=0xdead3344 ){ - fprintf(stderr,"High-end memory corruption at 0x%x\n", (int)p); - return; - } + }else{ + void **pprev = pp[0]; + void **pnext = pp[1]; + assert(pprev); + assert(pprev[1]==p); + pprev[1] = (void *)pnext; + if( pnext ){ + assert(pnext[0]==p); + pnext[0] = (void *)pprev; } - memset(pi, 0xff, (k+N_GUARD*2+1)*sizeof(int)); -#if SQLITE_MEMDEBUG>1 - fprintf(stderr,"%06d free %d bytes at 0x%x from %s:%d\n", - ++memcnt, n, (int)p, zFile,line); -#endif - free(pi); } } /* -** Resize a prior allocation. If p==0, then this routine -** works just like sqliteMalloc(). If n==0, then this routine -** works just like sqliteFree(). +** Pointer p is a pointer to an OS level allocation that has just been +** realloc()ed. Set the list pointers that point to this entry to it's new +** location. */ -void *sqlite3Realloc_(void *oldP, int n, char *zFile, int line){ - int *oldPi, *pi, i, k, oldN, oldK; - void *p; - if( oldP==0 ){ - return sqlite3Malloc_(n,1,zFile,line); - } - if( n==0 ){ - sqlite3Free_(oldP,zFile,line); - return 0; - } - if( simulatedMallocFailure(n, zFile, line) ){ - return 0; +static void relinkAlloc(void *p) +{ + void **pp = (void **)p; + if( pp[0] ){ + ((void **)(pp[0]))[1] = p; + }else{ + sqlite3_pFirst = p; } - oldPi = oldP; - oldPi -= N_GUARD+1; - if( oldPi[0]!=0xdead1122 ){ - fprintf(stderr,"Low-end memory corruption in realloc at 0x%x\n", (int)oldP); - return 0; + if( pp[1] ){ + ((void **)(pp[1]))[0] = p; } - oldN = oldPi[N_GUARD]; - sqlite3_memUsed -= oldN; - oldK = (oldN+sizeof(int)-1)/sizeof(int); - for(i=0; i<N_GUARD; i++){ - if( oldPi[oldK+N_GUARD+1+i]!=0xdead3344 ){ - fprintf(stderr,"High-end memory corruption in realloc at 0x%x\n", - (int)oldP); - return 0; +} +#else +#define linkAlloc(x) +#define relinkAlloc(x) +#define unlinkAlloc(x) +#endif + +/* +** This function sets the result of the Tcl interpreter passed as an argument +** to a list containing an entry for each currently outstanding call made to +** sqliteMalloc and friends by the current thread. Each list entry is itself a +** list, consisting of the following (in order): +** +** * The number of bytes allocated +** * The __FILE__ macro at the time of the sqliteMalloc() call. +** * The __LINE__ macro ... +** * The value of the sqlite3_malloc_id variable ... +** * The output of backtrace() (if available) ... +** +** Todo: We could have a version of this function that outputs to stdout, +** to debug memory leaks when Tcl is not available. +*/ +#if defined(TCLSH) && defined(SQLITE_DEBUG) && SQLITE_MEMDEBUG>1 +#include <tcl.h> +int sqlite3OutstandingMallocs(Tcl_Interp *interp){ + void *p; + Tcl_Obj *pRes = Tcl_NewObj(); + Tcl_IncrRefCount(pRes); + + + for(p=sqlite3_pFirst; p; p=((void **)p)[1]){ + Tcl_Obj *pEntry = Tcl_NewObj(); + Tcl_Obj *pStack = Tcl_NewObj(); + char *z; + u32 iLine; + int nBytes = sqlite3OsAllocationSize(p) - TESTALLOC_OVERHEAD; + char *zAlloc = (char *)p; + int i; + + Tcl_ListObjAppendElement(0, pEntry, Tcl_NewIntObj(nBytes)); + + z = &zAlloc[TESTALLOC_OFFSET_FILENAME(p)]; + Tcl_ListObjAppendElement(0, pEntry, Tcl_NewStringObj(z, -1)); + + z = &zAlloc[TESTALLOC_OFFSET_LINENUMBER(p)]; + memcpy(&iLine, z, sizeof(u32)); + Tcl_ListObjAppendElement(0, pEntry, Tcl_NewIntObj(iLine)); + + z = &zAlloc[TESTALLOC_OFFSET_USER(p)]; + Tcl_ListObjAppendElement(0, pEntry, Tcl_NewStringObj(z, -1)); + + z = &zAlloc[TESTALLOC_OFFSET_STACK(p)]; + for(i=0; i<TESTALLOC_STACKFRAMES; i++){ + char zHex[128]; + sprintf(zHex, "%p", ((void **)z)[i]); + Tcl_ListObjAppendElement(0, pStack, Tcl_NewStringObj(zHex, -1)); } + + Tcl_ListObjAppendElement(0, pEntry, pStack); + Tcl_ListObjAppendElement(0, pRes, pEntry); } - k = (n + sizeof(int) - 1)/sizeof(int); - pi = malloc( (k+N_GUARD*2+1)*sizeof(int) ); - if( pi==0 ){ - if( n>0 ) sqlite3_malloc_failed++; - return 0; - } - for(i=0; i<N_GUARD; i++) pi[i] = 0xdead1122; - pi[N_GUARD] = n; - sqlite3_memUsed += n; - if( sqlite3_memMax<sqlite3_memUsed ) sqlite3_memMax = sqlite3_memUsed; - for(i=0; i<N_GUARD; i++) pi[k+N_GUARD+1+i] = 0xdead3344; - p = &pi[N_GUARD+1]; - memcpy(p, oldP, n>oldN ? oldN : n); - if( n>oldN ){ - memset(&((char*)p)[oldN], 0x55, n-oldN); - } - memset(oldPi, 0xab, (oldK+N_GUARD+2)*sizeof(int)); - free(oldPi); -#if SQLITE_MEMDEBUG>1 - print_stack_trace(); - fprintf(stderr,"%06d realloc %d to %d bytes at 0x%x to 0x%x at %s:%d\n", - ++memcnt, oldN, n, (int)oldP, (int)p, zFile, line); -#endif - return p; + + Tcl_ResetResult(interp); + Tcl_SetObjResult(interp, pRes); + Tcl_DecrRefCount(pRes); + return TCL_OK; } +#endif /* -** Make a copy of a string in memory obtained from sqliteMalloc() +** This is the test layer's wrapper around sqlite3OsMalloc(). */ -char *sqlite3StrDup_(const char *z, char *zFile, int line){ - char *zNew; - if( z==0 ) return 0; - zNew = sqlite3Malloc_(strlen(z)+1, 0, zFile, line); - if( zNew ) strcpy(zNew, z); - return zNew; +static void * OSMALLOC(int n){ + sqlite3OsEnterMutex(); +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT + sqlite3_nMaxAlloc = + MAX(sqlite3_nMaxAlloc, sqlite3ThreadDataReadOnly()->nAlloc); +#endif + assert( !sqlite3_mallocDisallowed ); + if( !sqlite3TestMallocFail() ){ + u32 *p; + p = (u32 *)sqlite3OsMalloc(n + TESTALLOC_OVERHEAD); + assert(p); + sqlite3_nMalloc++; + applyGuards(p); + linkAlloc(p); + sqlite3OsLeaveMutex(); + return (void *)(&p[TESTALLOC_NGUARD + 2*sizeof(void *)/sizeof(u32)]); + } + sqlite3OsLeaveMutex(); + return 0; } -char *sqlite3StrNDup_(const char *z, int n, char *zFile, int line){ - char *zNew; - if( z==0 ) return 0; - zNew = sqlite3Malloc_(n+1, 0, zFile, line); - if( zNew ){ - memcpy(zNew, z, n); - zNew[n] = 0; + +static int OSSIZEOF(void *p){ + if( p ){ + u32 *pOs = (u32 *)getOsPointer(p); + return sqlite3OsAllocationSize(pOs) - TESTALLOC_OVERHEAD; } - return zNew; + return 0; } /* -** A version of sqliteFree that is always a function, not a macro. +** This is the test layer's wrapper around sqlite3OsFree(). The argument is a +** pointer to the space allocated for the application to use. */ -void sqlite3FreeX(void *p){ - sqliteFree(p); +static void OSFREE(void *pFree){ + u32 *p; /* Pointer to the OS-layer allocation */ + sqlite3OsEnterMutex(); + p = (u32 *)getOsPointer(pFree); + checkGuards(p); + unlinkAlloc(p); + memset(pFree, 0x55, OSSIZEOF(pFree)); + sqlite3OsFree(p); + sqlite3_nFree++; + sqlite3OsLeaveMutex(); } -#endif /* SQLITE_MEMDEBUG */ /* -** The following versions of malloc() and free() are for use in a -** normal build. +** This is the test layer's wrapper around sqlite3OsRealloc(). */ -#if !defined(SQLITE_MEMDEBUG) +static void * OSREALLOC(void *pRealloc, int n){ +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT + sqlite3_nMaxAlloc = + MAX(sqlite3_nMaxAlloc, sqlite3ThreadDataReadOnly()->nAlloc); +#endif + assert( !sqlite3_mallocDisallowed ); + if( !sqlite3TestMallocFail() ){ + u32 *p = (u32 *)getOsPointer(pRealloc); + checkGuards(p); + p = sqlite3OsRealloc(p, n + TESTALLOC_OVERHEAD); + applyGuards(p); + relinkAlloc(p); + return (void *)(&p[TESTALLOC_NGUARD + 2*sizeof(void *)/sizeof(u32)]); + } + return 0; +} +static void OSMALLOC_FAILED(){ + sqlite3_isFail = 0; +} + +#else +/* Define macros to call the sqlite3OsXXX interface directly if +** the SQLITE_MEMDEBUG macro is not defined. +*/ +#define OSMALLOC(x) sqlite3OsMalloc(x) +#define OSREALLOC(x,y) sqlite3OsRealloc(x,y) +#define OSFREE(x) sqlite3OsFree(x) +#define OSSIZEOF(x) sqlite3OsAllocationSize(x) +#define OSMALLOC_FAILED() + +#endif /* SQLITE_MEMDEBUG */ /* -** Allocate new memory and set it to zero. Return NULL if -** no memory is available. See also sqliteMallocRaw(). +** End code for memory allocation system test layer. +**--------------------------------------------------------------------------*/ + +/* +** This routine is called when we are about to allocate n additional bytes +** of memory. If the new allocation will put is over the soft allocation +** limit, then invoke sqlite3_release_memory() to try to release some +** memory before continuing with the allocation. +** +** This routine also makes sure that the thread-specific-data (TSD) has +** be allocated. If it has not and can not be allocated, then return +** false. The updateMemoryUsedCount() routine below will deallocate +** the TSD if it ought to be. +** +** If SQLITE_ENABLE_MEMORY_MANAGEMENT is not defined, this routine is +** a no-op +*/ +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT +static int enforceSoftLimit(int n){ + ThreadData *pTsd = sqlite3ThreadData(); + if( pTsd==0 ){ + return 0; + } + assert( pTsd->nAlloc>=0 ); + if( n>0 && pTsd->nSoftHeapLimit>0 ){ + while( pTsd->nAlloc+n>pTsd->nSoftHeapLimit && sqlite3_release_memory(n) ){} + } + return 1; +} +#else +# define enforceSoftLimit(X) 1 +#endif + +/* +** Update the count of total outstanding memory that is held in +** thread-specific-data (TSD). If after this update the TSD is +** no longer being used, then deallocate it. +** +** If SQLITE_ENABLE_MEMORY_MANAGEMENT is not defined, this routine is +** a no-op */ -void *sqlite3Malloc(int n){ - void *p; - if( n==0 ) return 0; - if( (p = malloc(n))==0 ){ - if( n>0 ) sqlite3_malloc_failed++; - }else{ - memset(p, 0, n); +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT +static void updateMemoryUsedCount(int n){ + ThreadData *pTsd = sqlite3ThreadData(); + if( pTsd ){ + pTsd->nAlloc += n; + assert( pTsd->nAlloc>=0 ); + if( pTsd->nAlloc==0 && pTsd->nSoftHeapLimit==0 ){ + sqlite3ReleaseThreadData(); + } } - return p; } +#else +#define updateMemoryUsedCount(x) /* no-op */ +#endif /* -** Allocate new memory but do not set it to zero. Return NULL if -** no memory is available. See also sqliteMalloc(). +** Allocate and return N bytes of uninitialised memory by calling +** sqlite3OsMalloc(). If the Malloc() call fails, attempt to free memory +** by calling sqlite3_release_memory(). */ -void *sqlite3MallocRaw(int n){ - void *p; - if( n==0 ) return 0; - if( (p = malloc(n))==0 ){ - if( n>0 ) sqlite3_malloc_failed++; +void *sqlite3MallocRaw(int n, int doMemManage){ + void *p = 0; + if( n>0 && !sqlite3MallocFailed() && (!doMemManage || enforceSoftLimit(n)) ){ + while( (p = OSMALLOC(n))==0 && sqlite3_release_memory(n) ){} + if( !p ){ + sqlite3FailedMalloc(); + OSMALLOC_FAILED(); + }else if( doMemManage ){ + updateMemoryUsedCount(OSSIZEOF(p)); + } } return p; } /* -** Free memory previously obtained from sqliteMalloc() +** Resize the allocation at p to n bytes by calling sqlite3OsRealloc(). The +** pointer to the new allocation is returned. If the Realloc() call fails, +** attempt to free memory by calling sqlite3_release_memory(). +*/ +void *sqlite3Realloc(void *p, int n){ + if( sqlite3MallocFailed() ){ + return 0; + } + + if( !p ){ + return sqlite3Malloc(n, 1); + }else{ + void *np = 0; +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT + int origSize = OSSIZEOF(p); +#endif + if( enforceSoftLimit(n - origSize) ){ + while( (np = OSREALLOC(p, n))==0 && sqlite3_release_memory(n) ){} + if( !np ){ + sqlite3FailedMalloc(); + OSMALLOC_FAILED(); + }else{ + updateMemoryUsedCount(OSSIZEOF(np) - origSize); + } + } + return np; + } +} + +/* +** Free the memory pointed to by p. p must be either a NULL pointer or a +** value returned by a previous call to sqlite3Malloc() or sqlite3Realloc(). */ void sqlite3FreeX(void *p){ if( p ){ - free(p); + updateMemoryUsedCount(0 - OSSIZEOF(p)); + OSFREE(p); } } /* -** Resize a prior allocation. If p==0, then this routine -** works just like sqliteMalloc(). If n==0, then this routine -** works just like sqliteFree(). +** A version of sqliteMalloc() that is always a function, not a macro. +** Currently, this is used only to alloc to allocate the parser engine. */ -void *sqlite3Realloc(void *p, int n){ - void *p2; - if( p==0 ){ - return sqliteMalloc(n); +void *sqlite3MallocX(int n){ + return sqliteMalloc(n); +} + +/* +** sqlite3Malloc +** sqlite3ReallocOrFree +** +** These two are implemented as wrappers around sqlite3MallocRaw(), +** sqlite3Realloc() and sqlite3Free(). +*/ +void *sqlite3Malloc(int n, int doMemManage){ + void *p = sqlite3MallocRaw(n, doMemManage); + if( p ){ + memset(p, 0, n); } - if( n==0 ){ - sqliteFree(p); - return 0; + return p; +} +void sqlite3ReallocOrFree(void **pp, int n){ + void *p = sqlite3Realloc(*pp, n); + if( !p ){ + sqlite3FreeX(*pp); } - p2 = realloc(p, n); - if( p2==0 ){ - if( n>0 ) sqlite3_malloc_failed++; + *pp = p; +} + +/* +** sqlite3ThreadSafeMalloc() and sqlite3ThreadSafeFree() are used in those +** rare scenarios where sqlite may allocate memory in one thread and free +** it in another. They are exactly the same as sqlite3Malloc() and +** sqlite3Free() except that: +** +** * The allocated memory is not included in any calculations with +** respect to the soft-heap-limit, and +** +** * sqlite3ThreadSafeMalloc() must be matched with ThreadSafeFree(), +** not sqlite3Free(). Calling sqlite3Free() on memory obtained from +** ThreadSafeMalloc() will cause an error somewhere down the line. +*/ +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT +void *sqlite3ThreadSafeMalloc(int n){ + (void)ENTER_MALLOC; + return sqlite3Malloc(n, 0); +} +void sqlite3ThreadSafeFree(void *p){ + (void)ENTER_MALLOC; + if( p ){ + OSFREE(p); } - return p2; } +#endif + /* -** Make a copy of a string in memory obtained from sqliteMalloc() +** Return the number of bytes allocated at location p. p must be either +** a NULL pointer (in which case 0 is returned) or a pointer returned by +** sqlite3Malloc(), sqlite3Realloc() or sqlite3ReallocOrFree(). +** +** The number of bytes allocated does not include any overhead inserted by +** any malloc() wrapper functions that may be called. So the value returned +** is the number of bytes that were available to SQLite using pointer p, +** regardless of how much memory was actually allocated. +*/ +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT +int sqlite3AllocSize(void *p){ + return OSSIZEOF(p); +} +#endif + +/* +** Make a copy of a string in memory obtained from sqliteMalloc(). These +** functions call sqlite3MallocRaw() directly instead of sqliteMalloc(). This +** is because when memory debugging is turned on, these two functions are +** called via macros that record the current file and line number in the +** ThreadData structure. */ char *sqlite3StrDup(const char *z){ char *zNew; if( z==0 ) return 0; - zNew = sqliteMallocRaw(strlen(z)+1); + zNew = sqlite3MallocRaw(strlen(z)+1, 1); if( zNew ) strcpy(zNew, z); return zNew; } char *sqlite3StrNDup(const char *z, int n){ char *zNew; if( z==0 ) return 0; - zNew = sqliteMallocRaw(n+1); + zNew = sqlite3MallocRaw(n+1, 1); if( zNew ){ memcpy(zNew, z, n); zNew[n] = 0; } return zNew; } -#endif /* !defined(SQLITE_MEMDEBUG) */ - -/* -** Reallocate a buffer to a different size. This is similar to -** sqliteRealloc() except that if the allocation fails the buffer -** is freed. -*/ -void sqlite3ReallocOrFree(void **ppBuf, int newSize){ - void *pNew = sqliteRealloc(*ppBuf, newSize); - if( pNew==0 ){ - sqliteFree(*ppBuf); - } - *ppBuf = pNew; -} /* ** Create a string from the 2nd and subsequent arguments (up to the @@ -411,11 +769,6 @@ void sqlite3SetString(char **pz, ...){ zResult += strlen(zResult); } va_end(ap); -#ifdef SQLITE_MEMDEBUG -#if SQLITE_MEMDEBUG>1 - fprintf(stderr,"string at 0x%x is %s\n", (int)*pz, *pz); -#endif -#endif } /* @@ -440,7 +793,7 @@ void sqlite3SetString(char **pz, ...){ ** to NULL. */ void sqlite3Error(sqlite3 *db, int err_code, const char *zFormat, ...){ - if( db && (db->pErr || (db->pErr = sqlite3ValueNew())) ){ + if( db && (db->pErr || (db->pErr = sqlite3ValueNew())!=0) ){ db->errCode = err_code; if( zFormat ){ char *z; @@ -482,6 +835,15 @@ void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){ } /* +** Clear the error message in pParse, if any +*/ +void sqlite3ErrorClear(Parse *pParse){ + sqliteFree(pParse->zErrMsg); + pParse->zErrMsg = 0; + pParse->nErr = 0; +} + +/* ** Convert an SQL-style quoted string into a normal string by removing ** the quote characters. The conversion is done in-place. If the ** input does not begin with a quote character, then this routine @@ -522,6 +884,7 @@ void sqlite3Dequote(char *z){ ** lower-case character. */ const unsigned char sqlite3UpperToLower[] = { +#ifdef SQLITE_ASCII 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 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, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, @@ -537,6 +900,25 @@ const unsigned char sqlite3UpperToLower[] = { 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233, 234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251, 252,253,254,255 +#endif +#ifdef SQLITE_EBCDIC + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 0x */ + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 1x */ + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 2x */ + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 3x */ + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 4x */ + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 5x */ + 96, 97, 66, 67, 68, 69, 70, 71, 72, 73,106,107,108,109,110,111, /* 6x */ + 112, 81, 82, 83, 84, 85, 86, 87, 88, 89,122,123,124,125,126,127, /* 7x */ + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 8x */ + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,156,159, /* 9x */ + 160,161,162,163,164,165,166,167,168,169,170,171,140,141,142,175, /* Ax */ + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, /* Bx */ + 192,129,130,131,132,133,134,135,136,137,202,203,204,205,206,207, /* Cx */ + 208,145,146,147,148,149,150,151,152,153,218,219,220,221,222,223, /* Dx */ + 224,225,162,163,164,165,166,167,168,169,232,203,204,205,206,207, /* Ex */ + 239,240,241,242,243,244,245,246,247,248,249,219,220,221,222,255, /* Fx */ +#endif }; #define UpperToLower sqlite3UpperToLower @@ -606,9 +988,11 @@ int sqlite3IsNumber(const char *z, int *realnum, u8 enc){ ** for SQL. So this routine always uses "." regardless of locale. */ int sqlite3AtoF(const char *z, double *pResult){ +#ifndef SQLITE_OMIT_FLOATING_POINT int sign = 1; const char *zBegin = z; LONGDOUBLE_TYPE v1 = 0.0; + while( isspace(*z) ) z++; if( *z=='-' ){ sign = -1; z++; @@ -656,6 +1040,9 @@ int sqlite3AtoF(const char *z, double *pResult){ } *pResult = sign<0 ? -v1 : v1; return z - zBegin; +#else + return sqlite3atoi64(z, pResult); +#endif /* SQLITE_OMIT_FLOATING_POINT */ } /* @@ -673,6 +1060,7 @@ int sqlite3atoi64(const char *zNum, i64 *pNum){ i64 v = 0; int neg; int i, c; + while( isspace(*zNum) ) zNum++; if( *zNum=='-' ){ neg = 1; zNum++; @@ -763,7 +1151,7 @@ int sqlite3SafetyOn(sqlite3 *db){ return 0; }else if( db->magic==SQLITE_MAGIC_BUSY ){ db->magic = SQLITE_MAGIC_ERROR; - db->flags |= SQLITE_Interrupt; + db->u1.isInterrupted = 1; } return 1; } @@ -779,7 +1167,7 @@ int sqlite3SafetyOff(sqlite3 *db){ return 0; }else if( db->magic==SQLITE_MAGIC_OPEN ){ db->magic = SQLITE_MAGIC_ERROR; - db->flags |= SQLITE_Interrupt; + db->u1.isInterrupted = 1; } return 1; } @@ -969,8 +1357,10 @@ void *sqlite3HexToBlob(const char *z){ if( n%2 ) return 0; zBlob = (char *)sqliteMalloc(n/2); - for(i=0; i<n; i+=2){ - zBlob[i/2] = (hexToInt(z[i])<<4) | hexToInt(z[i+1]); + if( zBlob ){ + for(i=0; i<n; i+=2){ + zBlob[i/2] = (hexToInt(z[i])<<4) | hexToInt(z[i+1]); + } } return zBlob; } @@ -1003,3 +1393,95 @@ void *sqlite3TextToPtr(const char *z){ return p; } #endif + +/* +** Return a pointer to the ThreadData associated with the calling thread. +*/ +ThreadData *sqlite3ThreadData(){ + ThreadData *p = (ThreadData*)sqlite3OsThreadSpecificData(1); + if( !p ){ + sqlite3FailedMalloc(); + } + return p; +} + +/* +** Return a pointer to the ThreadData associated with the calling thread. +** If no ThreadData has been allocated to this thread yet, return a pointer +** to a substitute ThreadData structure that is all zeros. +*/ +const ThreadData *sqlite3ThreadDataReadOnly(){ + static const ThreadData zeroData = {0}; /* Initializer to silence warnings + ** from broken compilers */ + const ThreadData *pTd = sqlite3OsThreadSpecificData(0); + return pTd ? pTd : &zeroData; +} + +/* +** Check to see if the ThreadData for this thread is all zero. If it +** is, then deallocate it. +*/ +void sqlite3ReleaseThreadData(){ + sqlite3OsThreadSpecificData(-1); +} + +/* +** This function must be called before exiting any API function (i.e. +** returning control to the user) that has called sqlite3Malloc or +** sqlite3Realloc. +** +** The returned value is normally a copy of the second argument to this +** function. However, if a malloc() failure has occured since the previous +** invocation SQLITE_NOMEM is returned instead. +** +** If the first argument, db, is not NULL and a malloc() error has occured, +** then the connection error-code (the value returned by sqlite3_errcode()) +** is set to SQLITE_NOMEM. +*/ +static int mallocHasFailed = 0; +int sqlite3ApiExit(sqlite3* db, int rc){ + if( sqlite3MallocFailed() ){ + mallocHasFailed = 0; + sqlite3OsLeaveMutex(); + sqlite3Error(db, SQLITE_NOMEM, 0); + rc = SQLITE_NOMEM; + } + return rc; +} + +/* +** Return true is a malloc has failed in this thread since the last call +** to sqlite3ApiExit(), or false otherwise. +*/ +int sqlite3MallocFailed(){ + return (mallocHasFailed && sqlite3OsInMutex(1)); +} + +/* +** Set the "malloc has failed" condition to true for this thread. +*/ +void sqlite3FailedMalloc(){ + sqlite3OsEnterMutex(); + assert( mallocHasFailed==0 ); + mallocHasFailed = 1; +} + +#ifdef SQLITE_MEMDEBUG +/* +** This function sets a flag in the thread-specific-data structure that will +** cause an assert to fail if sqliteMalloc() or sqliteRealloc() is called. +*/ +void sqlite3MallocDisallow(){ + assert( sqlite3_mallocDisallowed>=0 ); + sqlite3_mallocDisallowed++; +} + +/* +** This function clears the flag set in the thread-specific-data structure set +** by sqlite3MallocDisallow(). +*/ +void sqlite3MallocAllow(){ + assert( sqlite3_mallocDisallowed>0 ); + sqlite3_mallocDisallowed--; +} +#endif |