diff options
author | drh <drh@noemail.net> | 2001-01-13 14:34:05 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2001-01-13 14:34:05 +0000 |
commit | ae85dc8b0b06ebbf86bcad4e4d7119a5741fbcec (patch) | |
tree | dc15f520521376992b889e96c8a79a3c22a976ef /src | |
parent | 993c2a7c8f48ab2ac61a7eb63d8a3b30d6a937c5 (diff) | |
download | sqlite-ae85dc8b0b06ebbf86bcad4e4d7119a5741fbcec.tar.gz sqlite-ae85dc8b0b06ebbf86bcad4e4d7119a5741fbcec.zip |
Changes to the DBBE. Moving toward having many more
backend driver choices. (CVS 176)
FossilOrigin-Name: c0730217a04323a1a73d125e3e7da32bcc8d58fc
Diffstat (limited to 'src')
-rw-r--r-- | src/dbbe.c | 89 | ||||
-rw-r--r-- | src/dbbe.h | 31 | ||||
-rw-r--r-- | src/dbbegdbm.c | 221 | ||||
-rw-r--r-- | src/dbbemem.c | 210 | ||||
-rw-r--r-- | src/main.c | 4 | ||||
-rw-r--r-- | src/random.c | 127 | ||||
-rw-r--r-- | src/sqliteInt.h | 7 | ||||
-rw-r--r-- | src/vdbe.c | 89 |
8 files changed, 362 insertions, 416 deletions
diff --git a/src/dbbe.c b/src/dbbe.c index 43948c1e1..907aa7380 100644 --- a/src/dbbe.c +++ b/src/dbbe.c @@ -30,7 +30,7 @@ ** relatively simple to convert to a different database such ** as NDBM, SDBM, or BerkeleyDB. ** -** $Id: dbbe.c,v 1.21 2000/10/19 14:10:09 drh Exp $ +** $Id: dbbe.c,v 1.22 2001/01/13 14:34:06 drh Exp $ */ #include "sqliteInt.h" @@ -62,3 +62,90 @@ Dbbe *sqliteDbbeOpen( } return sqliteGdbmOpen(zName, writeFlag, createFlag, pzErrMsg); } + +/* +** Open a temporary file. The file should be deleted when closed. +** +** Note that we can't use the old Unix trick of opening the file +** and then immediately unlinking the file. That works great +** under Unix, but fails when we try to port to Windows. +*/ +int sqliteDbbeOpenTempFile(Dbbe *pBe, FILE **ppFile){ + char *zFile; /* Full name of the temporary file */ + char zBuf[50]; /* Base name of the temporary file */ + int i; /* Loop counter */ + int limit; /* Prevent an infinite loop */ + int rc = SQLITE_OK; /* Value returned by this function */ + char *zDir; /* Directory to hold the file */ + + for(i=0; i<pBe->nTemp; i++){ + if( pBe->apTemp[i]==0 ) break; + } + if( i>=pBe->nTemp ){ + pBe->nTemp++; + pBe->apTemp = sqliteRealloc(pBe->apTemp, pBe->nTemp*sizeof(FILE*) ); + pBe->azTemp = sqliteRealloc(pBe->azTemp, pBe->nTemp*sizeof(char*) ); + } + if( pBe->apTemp==0 ){ + *ppFile = 0; + return SQLITE_NOMEM; + } + limit = 4; + zFile = 0; + zDir = pBe->zDir; + if( zDir==0 ){ + zDir = "./"; + } + do{ + sqliteRandomName(zBuf, "/_temp_file_"); + sqliteFree(zFile); + zFile = 0; + sqliteSetString(&zFile, zDir, zBuf, 0); + }while( access(zFile,0)==0 && limit-- >= 0 ); + *ppFile = pBe->apTemp[i] = fopen(zFile, "w+"); + if( pBe->apTemp[i]==0 ){ + rc = SQLITE_ERROR; + sqliteFree(zFile); + pBe->azTemp[i] = 0; + }else{ + pBe->azTemp[i] = zFile; + } + return rc; +} + +/* +** Close a temporary file opened using sqliteGdbmOpenTempFile() +*/ +void sqliteDbbeCloseTempFile(Dbbe *pBe, FILE *f){ + int i; + for(i=0; i<pBe->nTemp; i++){ + if( pBe->apTemp[i]==f ){ + unlink(pBe->azTemp[i]); + sqliteFree(pBe->azTemp[i]); + pBe->apTemp[i] = 0; + pBe->azTemp[i] = 0; + break; + } + } + fclose(f); +} + +/* +** Close all temporary files that happen to still be open. +** This routine is called when the database is being closed. +*/ +void sqliteDbbeCloseAllTempFiles(Dbbe *pBe){ + int i; + for(i=0; i<pBe->nTemp; i++){ + if( pBe->apTemp[i]!=0 ){ + unlink(pBe->azTemp[i]); + fclose(pBe->apTemp[i]); + sqliteFree(pBe->azTemp[i]); + pBe->apTemp[i] = 0; + pBe->azTemp[i] = 0; + break; + } + } + sqliteFree(pBe->azTemp); + sqliteFree(pBe->apTemp); +} diff --git a/src/dbbe.h b/src/dbbe.h index 7c54a77b6..7d524cb03 100644 --- a/src/dbbe.h +++ b/src/dbbe.h @@ -28,7 +28,7 @@ ** This library was originally designed to support the following ** backends: GDBM, NDBM, SDBM, Berkeley DB. ** -** $Id: dbbe.h,v 1.8 2000/10/19 01:49:02 drh Exp $ +** $Id: dbbe.h,v 1.9 2001/01/13 14:34:06 drh Exp $ */ #ifndef _SQLITE_DBBE_H_ #define _SQLITE_DBBE_H_ @@ -53,7 +53,7 @@ */ typedef struct Dbbe Dbbe; typedef struct DbbeCursor DbbeCursor; - +typedef struct DbbeMethods DbbeMethods; /* ** Open a complete database. @@ -65,10 +65,13 @@ typedef struct DbbeCursor DbbeCursor; Dbbe *sqliteDbbeOpen(const char *zName, int write, int create, char **pzErr); /* -** This is the structure returned by sqliteDbbeOpen(). It contains pointers -** to all access routines for the database backend. +** Each of the various SQLite backends defines a set of methods for +** accessing the database. Pointers to the methods are contained in +** an instance of the following structure. A pointer to a static instance +** of this structure is assigned to the Dbbe structure that sqlileDbbeOpen +** returns. */ -struct Dbbe { +struct DbbeMethods { /* Close the whole database. */ void (*Close)(Dbbe*); @@ -149,4 +152,22 @@ struct Dbbe { void (*CloseTempFile)(Dbbe *, FILE *); }; +/* +** This is the structure returned by sqliteDbbeOpen(). It contains +** information common to all the different backend drivers. +** +** The information in this structure (with the exception the method +** pointers in the Dbbe.x field) is intended to be visible to +** the backend drivers only. Users should not access or modify +** this structure in any way other than the read the method pointers +** in Dbbe.x. +*/ +struct Dbbe { + struct DbbeMethods *x; /* Backend-specific methods for database access */ + char *zDir; /* The directory containing the database file(s) */ + int nTemp; /* Number of temporary files created */ + FILE **apTemp; /* Space to hold temporary file pointers */ + char **azTemp; /* Names of the temporary files */ +}; + #endif /* defined(_SQLITE_DBBE_H_) */ diff --git a/src/dbbegdbm.c b/src/dbbegdbm.c index 1e2702028..6c2659a80 100644 --- a/src/dbbegdbm.c +++ b/src/dbbegdbm.c @@ -30,7 +30,7 @@ ** relatively simple to convert to a different database such ** as NDBM, SDBM, or BerkeleyDB. ** -** $Id: dbbegdbm.c,v 1.1 2000/10/19 01:49:02 drh Exp $ +** $Id: dbbegdbm.c,v 1.2 2001/01/13 14:34:06 drh Exp $ */ #include "sqliteInt.h" #include <gdbm.h> @@ -58,33 +58,14 @@ struct BeFile { }; /* -** The following structure holds the current state of the RC4 algorithm. -** We use RC4 as a random number generator. Each call to RC4 gives -** a random 8-bit number. -** -** Nothing in this file or anywhere else in SQLite does any kind of -** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random -** number generator) not as an encryption device. -*/ -struct rc4 { - int i, j; - int s[256]; -}; - -/* ** The following structure contains all information used by GDBM ** database driver. This is a subclass of the Dbbe structure. */ typedef struct Dbbex Dbbex; struct Dbbex { Dbbe dbbe; /* The base class */ - char *zDir; /* The directory containing the database */ int write; /* True for write permission */ BeFile *pOpen; /* List of open files */ - int nTemp; /* Number of temporary files created */ - FILE **apTemp; /* Space to hold temporary file pointers */ - char **azTemp; /* Names of the temporary files */ - struct rc4 rc4; /* The random number generator */ }; /* @@ -105,41 +86,12 @@ struct DbbeCursor { }; /* -** Initialize the RC4 PRNG. "seed" is a pointer to some random -** data used to initialize the PRNG. -*/ -static void rc4init(struct rc4 *p, char *seed, int seedlen){ - int i; - char k[256]; - p->j = 0; - p->i = 0; - for(i=0; i<256; i++){ - p->s[i] = i; - k[i] = seed[i%seedlen]; - } - for(i=0; i<256; i++){ - int t; - p->j = (p->j + p->s[i] + k[i]) & 0xff; - t = p->s[p->j]; - p->s[p->j] = p->s[i]; - p->s[i] = t; - } -} - -/* -** Get a single 8-bit random value from the RC4 PRNG. +** */ -static int rc4byte(struct rc4 *p){ - int t; - p->i = (p->i + 1) & 0xff; - p->j = (p->j + p->s[p->i]) & 0xff; - t = p->s[p->i]; - p->s[p->i] = p->s[p->j]; - p->s[p->j] = t; - t = p->s[p->i] + p->s[p->j]; - return t & 0xff; -} - +struct DbbeList { + FILE *pOut; + Dbbe *pDbbe; +}; /* ** The "mkdir()" function only takes one argument under Windows. */ @@ -165,18 +117,7 @@ static void sqliteGdbmClose(Dbbe *pDbbe){ memset(pFile, 0, sizeof(*pFile)); sqliteFree(pFile); } - for(i=0; i<pBe->nTemp; i++){ - if( pBe->apTemp[i]!=0 ){ - unlink(pBe->azTemp[i]); - fclose(pBe->apTemp[i]); - sqliteFree(pBe->azTemp[i]); - pBe->apTemp[i] = 0; - pBe->azTemp[i] = 0; - break; - } - } - sqliteFree(pBe->azTemp); - sqliteFree(pBe->apTemp); + sqliteDbbeCloseAllTempFiles(pDbbe); memset(pBe, 0, sizeof(*pBe)); sqliteFree(pBe); } @@ -190,9 +131,9 @@ static void sqliteGdbmClose(Dbbe *pDbbe){ static char *sqliteFileOfTable(Dbbex *pBe, const char *zTable){ char *zFile = 0; int i; - sqliteSetString(&zFile, pBe->zDir, "/", zTable, ".tbl", 0); + sqliteSetString(&zFile, pBe->dbbe.zDir, "/", zTable, ".tbl", 0); if( zFile==0 ) return 0; - for(i=strlen(pBe->zDir)+1; zFile[i]; i++){ + for(i=strlen(pBe->dbbe.zDir)+1; zFile[i]; i++){ int c = zFile[i]; if( isupper(c) ){ zFile[i] = tolower(c); @@ -204,27 +145,6 @@ static char *sqliteFileOfTable(Dbbex *pBe, const char *zTable){ } /* -** Generate a random filename with the given prefix. The new filename -** is written into zBuf[]. The calling function must insure that -** zBuf[] is big enough to hold the prefix plus 20 or so extra -** characters. -** -** Very random names are chosen so that the chance of a -** collision with an existing filename is very very small. -*/ -static void randomName(struct rc4 *pRc4, char *zBuf, char *zPrefix){ - int i, j; - static const char zRandomChars[] = "abcdefghijklmnopqrstuvwxyz0123456789"; - strcpy(zBuf, zPrefix); - j = strlen(zBuf); - for(i=0; i<15; i++){ - int c = rc4byte(pRc4) % (sizeof(zRandomChars) - 1); - zBuf[j++] = zRandomChars[c]; - } - zBuf[j] = 0; -} - -/* ** Open a new table cursor. Write a pointer to the corresponding ** DbbeCursor structure into *ppCursr. Return an integer success ** code: @@ -295,13 +215,11 @@ static int sqliteGdbmOpenCursor( } }else{ int limit; - struct rc4 *pRc4; char zRandom[50]; - pRc4 = &pBe->rc4; zFile = 0; limit = 5; do { - randomName(&pBe->rc4, zRandom, "_temp_table_"); + sqliteRandomName(zRandom, "_temp_table_"); sqliteFree(zFile); zFile = sqliteFileOfTable(pBe, zRandom); pFile->dbf = gdbm_open(zFile, 0, rw_mask, mode, 0); @@ -577,15 +495,10 @@ static int sqliteGdbmNew(DbbeCursor *pCursr){ datum key; int go = 1; int i; - struct rc4 *pRc4; if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return 1; - pRc4 = &pCursr->pBe->rc4; while( go ){ - iKey = 0; - for(i=0; i<4; i++){ - iKey = (iKey<<8) + rc4byte(pRc4); - } + iKey = sqliteRandomInteger(); if( iKey==0 ) continue; key.dptr = (char*)&iKey; key.dsize = 4; @@ -631,68 +544,31 @@ static int sqliteGdbmDelete(DbbeCursor *pCursr, int nKey, char *pKey){ } /* -** Open a temporary file. The file should be deleted when closed. -** -** Note that we can't use the old Unix trick of opening the file -** and then immediately unlinking the file. That works great -** under Unix, but fails when we try to port to Windows. +** This variable contains pointers to all of the access methods +** used to implement the GDBM backend. */ -static int sqliteGdbmOpenTempFile(Dbbe *pDbbe, FILE **ppFile){ - char *zFile; /* Full name of the temporary file */ - char zBuf[50]; /* Base name of the temporary file */ - int i; /* Loop counter */ - int limit; /* Prevent an infinite loop */ - int rc = SQLITE_OK; /* Value returned by this function */ - Dbbex *pBe = (Dbbex*)pDbbe; - - for(i=0; i<pBe->nTemp; i++){ - if( pBe->apTemp[i]==0 ) break; - } - if( i>=pBe->nTemp ){ - pBe->nTemp++; - pBe->apTemp = sqliteRealloc(pBe->apTemp, pBe->nTemp*sizeof(FILE*) ); - pBe->azTemp = sqliteRealloc(pBe->azTemp, pBe->nTemp*sizeof(char*) ); - } - if( pBe->apTemp==0 ){ - *ppFile = 0; - return SQLITE_NOMEM; - } - limit = 4; - zFile = 0; - do{ - randomName(&pBe->rc4, zBuf, "/_temp_file_"); - sqliteFree(zFile); - zFile = 0; - sqliteSetString(&zFile, pBe->zDir, zBuf, 0); - }while( access(zFile,0)==0 && limit-- >= 0 ); - *ppFile = pBe->apTemp[i] = fopen(zFile, "w+"); - if( pBe->apTemp[i]==0 ){ - rc = SQLITE_ERROR; - sqliteFree(zFile); - pBe->azTemp[i] = 0; - }else{ - pBe->azTemp[i] = zFile; - } - return rc; -} - -/* -** Close a temporary file opened using sqliteGdbmOpenTempFile() -*/ -static void sqliteGdbmCloseTempFile(Dbbe *pDbbe, FILE *f){ - int i; - Dbbex *pBe = (Dbbex*)pDbbe; - for(i=0; i<pBe->nTemp; i++){ - if( pBe->apTemp[i]==f ){ - unlink(pBe->azTemp[i]); - sqliteFree(pBe->azTemp[i]); - pBe->apTemp[i] = 0; - pBe->azTemp[i] = 0; - break; - } - } - fclose(f); -} +static struct DbbeMethods gdbmMethods = { + /* n Close */ sqliteGdbmClose, + /* OpenCursor */ sqliteGdbmOpenCursor, + /* DropTable */ sqliteGdbmDropTable, + /* ReorganizeTable */ sqliteGdbmReorganizeTable, + /* CloseCursor */ sqliteGdbmCloseCursor, + /* Fetch */ sqliteGdbmFetch, + /* Test */ sqliteGdbmTest, + /* CopyKey */ sqliteGdbmCopyKey, + /* CopyData */ sqliteGdbmCopyData, + /* ReadKey */ sqliteGdbmReadKey, + /* ReadData */ sqliteGdbmReadData, + /* KeyLength */ sqliteGdbmKeyLength, + /* DataLength */ sqliteGdbmDataLength, + /* NextKey */ sqliteGdbmNextKey, + /* Rewind */ sqliteGdbmRewind, + /* New */ sqliteGdbmNew, + /* Put */ sqliteGdbmPut, + /* Delete */ sqliteGdbmDelete, + /* OpenTempFile */ sqliteDbbeOpenTempFile, + /* CloseTempFile */ sqliteDbbeCloseTempFile +}; /* @@ -746,31 +622,10 @@ Dbbe *sqliteGdbmOpen( sqliteSetString(pzErrMsg, "out of memory", 0); return 0; } - pNew->dbbe.Close = sqliteGdbmClose; - pNew->dbbe.OpenCursor = sqliteGdbmOpenCursor; - pNew->dbbe.DropTable = sqliteGdbmDropTable; - pNew->dbbe.ReorganizeTable = sqliteGdbmReorganizeTable; - pNew->dbbe.CloseCursor = sqliteGdbmCloseCursor; - pNew->dbbe.Fetch = sqliteGdbmFetch; - pNew->dbbe.Test = sqliteGdbmTest; - pNew->dbbe.CopyKey = sqliteGdbmCopyKey; - pNew->dbbe.CopyData = sqliteGdbmCopyData; - pNew->dbbe.ReadKey = sqliteGdbmReadKey; - pNew->dbbe.ReadData = sqliteGdbmReadData; - pNew->dbbe.KeyLength = sqliteGdbmKeyLength; - pNew->dbbe.DataLength = sqliteGdbmDataLength; - pNew->dbbe.NextKey = sqliteGdbmNextKey; - pNew->dbbe.Rewind = sqliteGdbmRewind; - pNew->dbbe.New = sqliteGdbmNew; - pNew->dbbe.Put = sqliteGdbmPut; - pNew->dbbe.Delete = sqliteGdbmDelete; - pNew->dbbe.OpenTempFile = sqliteGdbmOpenTempFile; - pNew->dbbe.CloseTempFile = sqliteGdbmCloseTempFile; - pNew->zDir = (char*)&pNew[1]; - strcpy(pNew->zDir, zName); + pNew->dbbe.x = &gdbmMethods; + pNew->dbbe.zDir = (char*)&pNew[1]; + strcpy(pNew->dbbe.zDir, zName); pNew->write = writeFlag; pNew->pOpen = 0; - time(&statbuf.st_ctime); - rc4init(&pNew->rc4, (char*)&statbuf, sizeof(statbuf)); return &pNew->dbbe; } diff --git a/src/dbbemem.c b/src/dbbemem.c index c8118055e..89b4aeed1 100644 --- a/src/dbbemem.c +++ b/src/dbbemem.c @@ -28,7 +28,7 @@ ** ** This file uses an in-memory hash table as the database backend. ** -** $Id: dbbemem.c,v 1.5 2000/12/10 18:23:50 drh Exp $ +** $Id: dbbemem.c,v 1.6 2001/01/13 14:34:06 drh Exp $ */ #include "sqliteInt.h" #include <sys/stat.h> @@ -343,20 +343,6 @@ struct MTable { }; /* -** The following structure holds the current state of the RC4 algorithm. -** We use RC4 as a random number generator. Each call to RC4 gives -** a random 8-bit number. -** -** Nothing in this file or anywhere else in SQLite does any kind of -** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random -** number generator) not as an encryption device. -*/ -struct rc4 { - int i, j; - int s[256]; -}; - -/* ** The following structure contains all information used by GDBM ** database driver. This is a subclass of the Dbbe structure. */ @@ -364,10 +350,6 @@ typedef struct Dbbex Dbbex; struct Dbbex { Dbbe dbbe; /* The base class */ Array tables; /* All tables of the database */ - int nTemp; /* Number of temporary files created */ - FILE **apTemp; /* Space to hold temporary file pointers */ - char **azTemp; /* Names of the temporary files */ - struct rc4 rc4; /* The random number generator */ }; /* @@ -386,42 +368,6 @@ struct DbbeCursor { }; /* -** Initialize the RC4 PRNG. "seed" is a pointer to some random -** data used to initialize the PRNG. -*/ -static void rc4init(struct rc4 *p, char *seed, int seedlen){ - int i; - char k[256]; - p->j = 0; - p->i = 0; - for(i=0; i<256; i++){ - p->s[i] = i; - k[i] = seed[i%seedlen]; - } - for(i=0; i<256; i++){ - int t; - p->j = (p->j + p->s[i] + k[i]) & 0xff; - t = p->s[p->j]; - p->s[p->j] = p->s[i]; - p->s[i] = t; - } -} - -/* -** Get a single 8-bit random value from the RC4 PRNG. -*/ -static int rc4byte(struct rc4 *p){ - int t; - p->i = (p->i + 1) & 0xff; - p->j = (p->j + p->s[p->i]) & 0xff; - t = p->s[p->i]; - p->s[p->i] = p->s[p->j]; - p->s[p->j] = t; - t = p->s[p->i] + p->s[p->j]; - return t & 0xff; -} - -/* ** Forward declaration */ static void sqliteMemCloseCursor(DbbeCursor *pCursr); @@ -453,44 +399,12 @@ static void sqliteMemClose(Dbbe *pDbbe){ deleteMTable(pTble); } ArrayClear(&pBe->tables); - for(i=0; i<pBe->nTemp; i++){ - if( pBe->apTemp[i]!=0 ){ - unlink(pBe->azTemp[i]); - fclose(pBe->apTemp[i]); - sqliteFree(pBe->azTemp[i]); - pBe->apTemp[i] = 0; - pBe->azTemp[i] = 0; - break; - } - } - sqliteFree(pBe->azTemp); - sqliteFree(pBe->apTemp); + sqliteDbbeCloseAllTempFiles(pDbbe); memset(pBe, 0, sizeof(*pBe)); sqliteFree(pBe); } /* -** Generate a random filename with the given prefix. The new filename -** is written into zBuf[]. The calling function must insure that -** zBuf[] is big enough to hold the prefix plus 20 or so extra -** characters. -** -** Very random names are chosen so that the chance of a -** collision with an existing filename is very very small. -*/ -static void randomName(struct rc4 *pRc4, char *zBuf, char *zPrefix){ - int i, j; - static const char zRandomChars[] = "abcdefghijklmnopqrstuvwxyz0123456789"; - strcpy(zBuf, zPrefix); - j = strlen(zBuf); - for(i=0; i<15; i++){ - int c = rc4byte(pRc4) % (sizeof(zRandomChars) - 1); - zBuf[j++] = zRandomChars[c]; - } - zBuf[j] = 0; -} - -/* ** Translate the name of an SQL table (or index) into its ** canonical name. ** @@ -752,14 +666,9 @@ static int sqliteMemNew(DbbeCursor *pCursr){ Datum key; int go = 1; int i; - struct rc4 *pRc4; - pRc4 = &pCursr->pBe->rc4; while( go ){ - iKey = 0; - for(i=0; i<4; i++){ - iKey = (iKey<<8) + rc4byte(pRc4); - } + iKey = sqliteRandomInteger(); if( iKey==0 ) continue; key.p = (char*)&iKey; key.n = 4; @@ -804,69 +713,31 @@ static int sqliteMemDelete(DbbeCursor *pCursr, int nKey, char *pKey){ } /* -** Open a temporary file. The file should be deleted when closed. -** -** Note that we can't use the old Unix trick of opening the file -** and then immediately unlinking the file. That works great -** under Unix, but fails when we try to port to Windows. -*/ -static int sqliteMemOpenTempFile(Dbbe *pDbbe, FILE **ppTble){ - char *zName; /* Full name of the temporary file */ - char zBuf[50]; /* Base name of the temporary file */ - int i; /* Loop counter */ - int limit; /* Prevent an infinite loop */ - int rc = SQLITE_OK; /* Value returned by this function */ - Dbbex *pBe = (Dbbex*)pDbbe; - - for(i=0; i<pBe->nTemp; i++){ - if( pBe->apTemp[i]==0 ) break; - } - if( i>=pBe->nTemp ){ - pBe->nTemp++; - pBe->apTemp = sqliteRealloc(pBe->apTemp, pBe->nTemp*sizeof(FILE*) ); - pBe->azTemp = sqliteRealloc(pBe->azTemp, pBe->nTemp*sizeof(char*) ); - } - if( pBe->apTemp==0 ){ - *ppTble = 0; - return SQLITE_NOMEM; - } - limit = 4; - zName = 0; - do{ - randomName(&pBe->rc4, zBuf, "/tmp/_temp_file_"); - sqliteFree(zName); - zName = 0; - sqliteSetString(&zName, zBuf, 0); - }while( access(zName,0)==0 && limit-- >= 0 ); - *ppTble = pBe->apTemp[i] = fopen(zName, "w+"); - if( pBe->apTemp[i]==0 ){ - rc = SQLITE_ERROR; - sqliteFree(zName); - pBe->azTemp[i] = 0; - }else{ - pBe->azTemp[i] = zName; - } - return rc; -} - -/* -** Close a temporary file opened using sqliteMemOpenTempFile() -*/ -static void sqliteMemCloseTempFile(Dbbe *pDbbe, FILE *f){ - int i; - Dbbex *pBe = (Dbbex*)pDbbe; - for(i=0; i<pBe->nTemp; i++){ - if( pBe->apTemp[i]==f ){ - unlink(pBe->azTemp[i]); - sqliteFree(pBe->azTemp[i]); - pBe->apTemp[i] = 0; - pBe->azTemp[i] = 0; - break; - } - } - fclose(f); -} - +** This variable contains pointers to all of the access methods +** used to implement the MEMORY backend. +*/ +static struct DbbeMethods memoryMethods = { + /* n Close */ sqliteMemClose, + /* OpenCursor */ sqliteMemOpenCursor, + /* DropTable */ sqliteMemDropTable, + /* ReorganizeTable */ sqliteMemReorganizeTable, + /* CloseCursor */ sqliteMemCloseCursor, + /* Fetch */ sqliteMemFetch, + /* Test */ sqliteMemTest, + /* CopyKey */ sqliteMemCopyKey, + /* CopyData */ sqliteMemCopyData, + /* ReadKey */ sqliteMemReadKey, + /* ReadData */ sqliteMemReadData, + /* KeyLength */ sqliteMemKeyLength, + /* DataLength */ sqliteMemDataLength, + /* NextKey */ sqliteMemNextKey, + /* Rewind */ sqliteMemRewind, + /* New */ sqliteMemNew, + /* Put */ sqliteMemPut, + /* Delete */ sqliteMemDelete, + /* OpenTempFile */ sqliteDbbeOpenTempFile, + /* CloseTempFile */ sqliteDbbeCloseTempFile +}; /* ** This routine opens a new database. For the GDBM driver @@ -884,7 +755,6 @@ Dbbe *sqliteMemOpen( char **pzErrMsg /* Write error messages (if any) here */ ){ Dbbex *pNew; - long now; pNew = sqliteMalloc( sizeof(*pNew) ); if( pNew==0 ){ @@ -892,27 +762,7 @@ Dbbe *sqliteMemOpen( return 0; } ArrayInit(&pNew->tables); - pNew->dbbe.Close = sqliteMemClose; - pNew->dbbe.OpenCursor = sqliteMemOpenCursor; - pNew->dbbe.DropTable = sqliteMemDropTable; - pNew->dbbe.ReorganizeTable = sqliteMemReorganizeTable; - pNew->dbbe.CloseCursor = sqliteMemCloseCursor; - pNew->dbbe.Fetch = sqliteMemFetch; - pNew->dbbe.Test = sqliteMemTest; - pNew->dbbe.CopyKey = sqliteMemCopyKey; - pNew->dbbe.CopyData = sqliteMemCopyData; - pNew->dbbe.ReadKey = sqliteMemReadKey; - pNew->dbbe.ReadData = sqliteMemReadData; - pNew->dbbe.KeyLength = sqliteMemKeyLength; - pNew->dbbe.DataLength = sqliteMemDataLength; - pNew->dbbe.NextKey = sqliteMemNextKey; - pNew->dbbe.Rewind = sqliteMemRewind; - pNew->dbbe.New = sqliteMemNew; - pNew->dbbe.Put = sqliteMemPut; - pNew->dbbe.Delete = sqliteMemDelete; - pNew->dbbe.OpenTempFile = sqliteMemOpenTempFile; - pNew->dbbe.CloseTempFile = sqliteMemCloseTempFile; - time(&now); - rc4init(&pNew->rc4, (char*)&now, sizeof(now)); + pNew->dbbe.x = &memoryMethods; + pNew->dbbe.zDir = 0; return &pNew->dbbe; } diff --git a/src/main.c b/src/main.c index 0a2117708..a707b6722 100644 --- a/src/main.c +++ b/src/main.c @@ -26,7 +26,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.22 2000/12/10 18:23:50 drh Exp $ +** $Id: main.c,v 1.23 2001/01/13 14:34:06 drh Exp $ */ #include "sqliteInt.h" @@ -239,7 +239,7 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){ */ void sqlite_close(sqlite *db){ int i; - db->pBe->Close(db->pBe); + db->pBe->x->Close(db->pBe); for(i=0; i<N_HASH; i++){ Table *pNext, *pList = db->apTblHash[i]; db->apTblHash[i] = 0; diff --git a/src/random.c b/src/random.c new file mode 100644 index 000000000..726fefee4 --- /dev/null +++ b/src/random.c @@ -0,0 +1,127 @@ +/* +** Copyright (c) 2000 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public +** License as published by the Free Software Foundation; either +** version 2 of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public +** License along with this library; if not, write to the +** Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +************************************************************************* +** This file contains code to implement a pseudo-random number +** generator (PRNG) for SQLite. +** +** Random numbers are used by some of the database backends in order +** to generate random integer keys for tables or random filenames. +** +** $Id: random.c,v 1.1 2001/01/13 14:34:07 drh Exp $ +*/ +#include "sqliteInt.h" + + +/* +** Get a single 8-bit random value from the RC4 PRNG. +*/ +int sqliteRandomByte(void){ + int t; + + /* + ** The following structure holds the current state of the RC4 algorithm. + ** We use RC4 as a random number generator. Each call to RC4 gives + ** a random 8-bit number. + ** + ** Nothing in this file or anywhere else in SQLite does any kind of + ** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random + ** number generator) not as an encryption device. + */ + static struct { + int isInit; + int i, j; + int s[256]; + } prng_state; + + /* Initialize the state of the random number generator once, + ** the first time this routine is called. The seed value does + ** not need to contain a lot of randomness since we are not + ** trying to do secure encryption or anything like that... + */ + if( !prng_state.isInit ){ + int i; + static char seed[] = " sqlite random seed"; + char k[256]; + time((time_t*)seed); + prng_state.j = 0; + prng_state.i = 0; + for(i=0; i<256; i++){ + prng_state.s[i] = i; + k[i] = seed[i%sizeof(seed)]; + } + for(i=0; i<256; i++){ + int t; + prng_state.j = (prng_state.j + prng_state.s[i] + k[i]) & 0xff; + t = prng_state.s[prng_state.j]; + prng_state.s[prng_state.j] = prng_state.s[i]; + prng_state.s[i] = t; + } + prng_state.isInit = 1; + } + + /* Generate and return single random byte + */ + prng_state.i = (prng_state.i + 1) & 0xff; + prng_state.j = (prng_state.j + prng_state.s[prng_state.i]) & 0xff; + t = prng_state.s[prng_state.i]; + prng_state.s[prng_state.i] = prng_state.s[prng_state.j]; + prng_state.s[prng_state.j] = t; + t = prng_state.s[prng_state.i] + prng_state.s[prng_state.j]; + return t & 0xff; +} + +/* +** Return a random 32-bit integer. The integer is generated by making +** 4 calls to sqliteRandomByte(). +*/ +int sqliteRandomInteger(void){ + int r; + int i; + r = sqliteRandomByte(); + for(i=1; i<4; i++){ + r = (r<<8) + sqliteRandomByte(); + } + return r; +} + + +/* +** Generate a random filename with the given prefix. The new filename +** is written into zBuf[]. The calling function must insure that +** zBuf[] is big enough to hold the prefix plus 20 or so extra +** characters. +** +** Very random names are chosen so that the chance of a +** collision with an existing filename is very very small. +*/ +void sqliteRandomName(char *zBuf, char *zPrefix){ + int i, j; + static const char zRandomChars[] = "abcdefghijklmnopqrstuvwxyz0123456789"; + strcpy(zBuf, zPrefix); + j = strlen(zBuf); + for(i=0; i<15; i++){ + int c = sqliteRandomByte() % (sizeof(zRandomChars) - 1); + zBuf[j++] = zRandomChars[c]; + } + zBuf[j] = 0; +} diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 5b777ef51..b6e602102 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -23,7 +23,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.33 2000/12/10 18:23:51 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.34 2001/01/13 14:34:07 drh Exp $ */ #include "sqlite.h" #include "dbbe.h" @@ -419,3 +419,8 @@ void sqliteExprResolveInSelect(Parse*, Expr*); int sqliteExprAnalyzeAggregates(Parse*, Expr*); void sqliteParseInfoReset(Parse*); Vdbe *sqliteGetVdbe(Parse*); +int sqliteRandomByte(void); +int sqliteRandomInteger(void); +void sqliteRandomName(char*,char*); +int sqliteDbbeOpenTempFile(Dbbe*, FILE**); +void sqliteDbbeCloseTempFile(Dbbe*, FILE*); diff --git a/src/vdbe.c b/src/vdbe.c index bfdb65b79..749f1e039 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -41,7 +41,7 @@ ** But other routines are also provided to help in building up ** a program instruction by instruction. ** -** $Id: vdbe.c,v 1.49 2000/12/10 18:35:20 drh Exp $ +** $Id: vdbe.c,v 1.50 2001/01/13 14:34:07 drh Exp $ */ #include "sqliteInt.h" #include <unistd.h> @@ -696,7 +696,7 @@ static void Cleanup(Vdbe *p){ p->azColName = 0; for(i=0; i<p->nCursor; i++){ if( p->aCsr[i].pCursor ){ - p->pBe->CloseCursor(p->aCsr[i].pCursor); + p->pBe->x->CloseCursor(p->aCsr[i].pCursor); p->aCsr[i].pCursor = 0; } } @@ -713,7 +713,7 @@ static void Cleanup(Vdbe *p){ p->nMem = 0; for(i=0; i<p->nList; i++){ if( p->apList[i] ){ - p->pBe->CloseTempFile(p->pBe, p->apList[i]); + p->pBe->x->CloseTempFile(p->pBe, p->apList[i]); p->apList[i] = 0; } } @@ -953,6 +953,7 @@ int sqliteVdbeExec( Op *pOp; /* Current operation */ int rc; /* Value to return */ Dbbe *pBe = p->pBe; /* The backend driver */ + DbbeMethods *pBex = pBe->x; /* The backend driver methods */ sqlite *db = p->db; /* The database */ char **zStack; Stack *aStack; @@ -1809,10 +1810,10 @@ int sqliteVdbeExec( for(j=p->nCursor; j<=i; j++) p->aCsr[j].pCursor = 0; p->nCursor = i+1; }else if( p->aCsr[i].pCursor ){ - pBe->CloseCursor(p->aCsr[i].pCursor); + pBex->CloseCursor(p->aCsr[i].pCursor); } do { - rc = pBe->OpenCursor(pBe,pOp->p3,pOp->p2,&p->aCsr[i].pCursor); + rc = pBex->OpenCursor(pBe,pOp->p3,pOp->p2,&p->aCsr[i].pCursor); switch( rc ){ case SQLITE_BUSY: { if( xBusy==0 || (*xBusy)(pBusyArg, pOp->p3, ++busy)==0 ){ @@ -1853,7 +1854,7 @@ int sqliteVdbeExec( case OP_Close: { int i = pOp->p1; if( i>=0 && i<p->nCursor && p->aCsr[i].pCursor ){ - pBe->CloseCursor(p->aCsr[i].pCursor); + pBex->CloseCursor(p->aCsr[i].pCursor); p->aCsr[i].pCursor = 0; } break; @@ -1871,11 +1872,11 @@ int sqliteVdbeExec( VERIFY( if( tos<0 ) goto not_enough_stack; ) if( i>=0 && i<p->nCursor && p->aCsr[i].pCursor ){ if( aStack[tos].flags & STK_Int ){ - pBe->Fetch(p->aCsr[i].pCursor, sizeof(int), + pBex->Fetch(p->aCsr[i].pCursor, sizeof(int), (char*)&aStack[tos].i); }else{ if( Stringify(p, tos) ) goto no_mem; - pBe->Fetch(p->aCsr[i].pCursor, aStack[tos].n, + pBex->Fetch(p->aCsr[i].pCursor, aStack[tos].n, zStack[tos]); } p->nFetch++; @@ -1937,11 +1938,11 @@ int sqliteVdbeExec( VERIFY( if( tos<0 ) goto not_enough_stack; ) if( VERIFY( i>=0 && i<p->nCursor && ) p->aCsr[i].pCursor ){ if( aStack[tos].flags & STK_Int ){ - alreadyExists = pBe->Test(p->aCsr[i].pCursor, sizeof(int), + alreadyExists = pBex->Test(p->aCsr[i].pCursor, sizeof(int), (char*)&aStack[tos].i); }else{ if( Stringify(p, tos) ) goto no_mem; - alreadyExists = pBe->Test(p->aCsr[i].pCursor,aStack[tos].n, + alreadyExists = pBex->Test(p->aCsr[i].pCursor,aStack[tos].n, zStack[tos]); } } @@ -1967,7 +1968,7 @@ int sqliteVdbeExec( if( VERIFY( i<0 || i>=p->nCursor || ) p->aCsr[i].pCursor==0 ){ v = 0; }else{ - v = pBe->New(p->aCsr[i].pCursor); + v = pBex->New(p->aCsr[i].pCursor); } VERIFY( NeedStack(p, p->tos+1); ) p->tos++; @@ -2000,7 +2001,7 @@ int sqliteVdbeExec( nKey = sizeof(int); zKey = (char*)&aStack[nos].i; } - pBe->Put(p->aCsr[i].pCursor, nKey, zKey, + pBex->Put(p->aCsr[i].pCursor, nKey, zKey, aStack[tos].n, zStack[tos]); } POPSTACK; @@ -2028,7 +2029,7 @@ int sqliteVdbeExec( nKey = aStack[tos].n; zKey = zStack[tos]; } - pBe->Delete(p->aCsr[i].pCursor, nKey, zKey); + pBex->Delete(p->aCsr[i].pCursor, nKey, zKey); } POPSTACK; break; @@ -2082,29 +2083,29 @@ int sqliteVdbeExec( VERIFY( if( NeedStack(p, tos) ) goto no_mem; ) if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ if( p->aCsr[i].keyAsData ){ - amt = pBe->KeyLength(pCrsr); + amt = pBex->KeyLength(pCrsr); if( amt<=sizeof(int)*(p2+1) ){ aStack[tos].flags = STK_Null; break; } - pAddr = (int*)pBe->ReadKey(pCrsr, sizeof(int)*p2); + pAddr = (int*)pBex->ReadKey(pCrsr, sizeof(int)*p2); if( *pAddr==0 ){ aStack[tos].flags = STK_Null; break; } - z = pBe->ReadKey(pCrsr, *pAddr); + z = pBex->ReadKey(pCrsr, *pAddr); }else{ - amt = pBe->DataLength(pCrsr); + amt = pBex->DataLength(pCrsr); if( amt<=sizeof(int)*(p2+1) ){ aStack[tos].flags = STK_Null; break; } - pAddr = (int*)pBe->ReadData(pCrsr, sizeof(int)*p2); + pAddr = (int*)pBex->ReadData(pCrsr, sizeof(int)*p2); if( *pAddr==0 ){ aStack[tos].flags = STK_Null; break; } - z = pBe->ReadData(pCrsr, *pAddr); + z = pBex->ReadData(pCrsr, *pAddr); } zStack[tos] = z; aStack[tos].n = strlen(z) + 1; @@ -2127,11 +2128,11 @@ int sqliteVdbeExec( VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ - char *z = pBe->ReadKey(pCrsr, 0); + char *z = pBex->ReadKey(pCrsr, 0); if( p->aCsr[i].keyAsData ){ zStack[tos] = z; aStack[tos].flags = STK_Str; - aStack[tos].n = pBe->KeyLength(pCrsr); + aStack[tos].n = pBex->KeyLength(pCrsr); }else{ memcpy(&aStack[tos].i, z, sizeof(int)); aStack[tos].flags = STK_Int; @@ -2148,7 +2149,7 @@ int sqliteVdbeExec( case OP_Rewind: { int i = pOp->p1; if( VERIFY( i>=0 && i<p->nCursor && ) p->aCsr[i].pCursor!=0 ){ - pBe->Rewind(p->aCsr[i].pCursor); + pBex->Rewind(p->aCsr[i].pCursor); } break; } @@ -2161,7 +2162,7 @@ int sqliteVdbeExec( case OP_Next: { int i = pOp->p1; if( VERIFY( i>=0 && i<p->nCursor && ) p->aCsr[i].pCursor!=0 ){ - if( pBe->NextKey(p->aCsr[i].pCursor)==0 ){ + if( pBex->NextKey(p->aCsr[i].pCursor)==0 ){ pc = pOp->p2 - 1; }else{ p->nFetch++; @@ -2211,8 +2212,8 @@ int sqliteVdbeExec( int *aIdx; int nIdx; int j, k; - nIdx = pBe->DataLength(pCrsr)/sizeof(int); - aIdx = (int*)pBe->ReadData(pCrsr, 0); + nIdx = pBex->DataLength(pCrsr)/sizeof(int); + aIdx = (int*)pBex->ReadData(pCrsr, 0); if( nIdx>1 ){ k = *(aIdx++); if( k>nIdx-1 ) k = nIdx-1; @@ -2257,10 +2258,10 @@ int sqliteVdbeExec( Integerify(p, nos); newVal = aStack[nos].i; if( Stringify(p, tos) ) goto no_mem; - r = pBe->Fetch(pCrsr, aStack[tos].n, zStack[tos]); + r = pBex->Fetch(pCrsr, aStack[tos].n, zStack[tos]); if( r==0 ){ /* Create a new record for this index */ - pBe->Put(pCrsr, aStack[tos].n, zStack[tos], + pBex->Put(pCrsr, aStack[tos].n, zStack[tos], sizeof(int), (char*)&newVal); }else{ /* Extend the existing record */ @@ -2268,32 +2269,32 @@ int sqliteVdbeExec( int *aIdx; int k; - nIdx = pBe->DataLength(pCrsr)/sizeof(int); + nIdx = pBex->DataLength(pCrsr)/sizeof(int); if( nIdx==1 ){ aIdx = sqliteMalloc( sizeof(int)*4 ); if( aIdx==0 ) goto no_mem; aIdx[0] = 2; - pBe->CopyData(pCrsr, 0, sizeof(int), (char*)&aIdx[1]); + pBex->CopyData(pCrsr, 0, sizeof(int), (char*)&aIdx[1]); aIdx[2] = newVal; - pBe->Put(pCrsr, aStack[tos].n, zStack[tos], + pBex->Put(pCrsr, aStack[tos].n, zStack[tos], sizeof(int)*4, (char*)aIdx); sqliteFree(aIdx); }else{ - aIdx = (int*)pBe->ReadData(pCrsr, 0); + aIdx = (int*)pBex->ReadData(pCrsr, 0); k = aIdx[0]; if( k<nIdx-1 ){ aIdx[k+1] = newVal; aIdx[0]++; - pBe->Put(pCrsr, aStack[tos].n, zStack[tos], + pBex->Put(pCrsr, aStack[tos].n, zStack[tos], sizeof(int)*nIdx, (char*)aIdx); }else{ nIdx *= 2; aIdx = sqliteMalloc( sizeof(int)*nIdx ); if( aIdx==0 ) goto no_mem; - pBe->CopyData(pCrsr, 0, sizeof(int)*(k+1), (char*)aIdx); + pBex->CopyData(pCrsr, 0, sizeof(int)*(k+1), (char*)aIdx); aIdx[k+1] = newVal; aIdx[0]++; - pBe->Put(pCrsr, aStack[tos].n, zStack[tos], + pBex->Put(pCrsr, aStack[tos].n, zStack[tos], sizeof(int)*nIdx, (char*)aIdx); sqliteFree(aIdx); } @@ -2333,12 +2334,12 @@ int sqliteVdbeExec( Integerify(p, nos); oldVal = aStack[nos].i; if( Stringify(p, tos) ) goto no_mem; - r = pBe->Fetch(pCrsr, aStack[tos].n, zStack[tos]); + r = pBex->Fetch(pCrsr, aStack[tos].n, zStack[tos]); if( r==0 ) break; - nIdx = pBe->DataLength(pCrsr)/sizeof(int); - aIdx = (int*)pBe->ReadData(pCrsr, 0); + nIdx = pBex->DataLength(pCrsr)/sizeof(int); + aIdx = (int*)pBex->ReadData(pCrsr, 0); if( (nIdx==1 && aIdx[0]==oldVal) || (aIdx[0]==1 && aIdx[1]==oldVal) ){ - pBe->Delete(pCrsr, aStack[tos].n, zStack[tos]); + pBex->Delete(pCrsr, aStack[tos].n, zStack[tos]); }else{ k = aIdx[0]; for(j=1; j<=k && aIdx[j]!=oldVal; j++){} @@ -2349,7 +2350,7 @@ int sqliteVdbeExec( if( aIdx[0]*3 + 1 < nIdx ){ nIdx /= 2; } - pBe->Put(pCrsr, aStack[tos].n, zStack[tos], + pBex->Put(pCrsr, aStack[tos].n, zStack[tos], sizeof(int)*nIdx, (char*)aIdx); } } @@ -2365,7 +2366,7 @@ int sqliteVdbeExec( ** from the disk. */ case OP_Destroy: { - pBe->DropTable(pBe, pOp->p3); + pBex->DropTable(pBe, pOp->p3); break; } @@ -2374,7 +2375,7 @@ int sqliteVdbeExec( ** Compress, optimize, and tidy up the GDBM file named by P3. */ case OP_Reorganize: { - pBe->ReorganizeTable(pBe, pOp->p3); + pBex->ReorganizeTable(pBe, pOp->p3); break; } @@ -2396,9 +2397,9 @@ int sqliteVdbeExec( for(j=p->nList; j<=i; j++) p->apList[j] = 0; p->nList = i+1; }else if( p->apList[i] ){ - pBe->CloseTempFile(pBe, p->apList[i]); + pBex->CloseTempFile(pBe, p->apList[i]); } - rc = pBe->OpenTempFile(pBe, &p->apList[i]); + rc = pBex->OpenTempFile(pBe, &p->apList[i]); if( rc!=SQLITE_OK ){ sqliteSetString(pzErrMsg, "unable to open a temporary file", 0); } @@ -2468,7 +2469,7 @@ int sqliteVdbeExec( int i = pOp->p1; VERIFY( if( i<0 ) goto bad_instruction; ) if( VERIFY( i<p->nList && ) p->apList[i]!=0 ){ - pBe->CloseTempFile(pBe, p->apList[i]); + pBex->CloseTempFile(pBe, p->apList[i]); p->apList[i] = 0; } break; |