using System; using System.Diagnostics; using System.Text; using Bitmask = System.UInt64; using u8 = System.Byte; using u16 = System.UInt16; using u32 = System.UInt32; namespace Community.CsharpSqlite { using sqlite3_int64 = System.Int64; using MemJournal = Sqlite3.sqlite3_file; public partial class Sqlite3 { /* ** 2007 August 22 ** ** 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. ** ************************************************************************* ** ** This file contains code use to implement an in-memory rollback journal. ** The in-memory rollback journal is used to journal transactions for ** ":memory:" databases and when the journal_mode=MEMORY pragma is used. ************************************************************************* ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart ** C#-SQLite is an independent reimplementation of the SQLite software library ** ** SQLITE_SOURCE_ID: 2010-12-07 20:14:09 a586a4deeb25330037a49df295b36aaf624d0f45 ** ************************************************************************* */ //#include "sqliteInt.h" /* Forward references to internal structures */ //typedef struct MemJournal MemJournal; //typedef struct FilePoint FilePoint; //typedef struct FileChunk FileChunk; /* Space to hold the rollback journal is allocated in increments of ** this many bytes. ** ** The size chosen is a little less than a power of two. That way, ** the FileChunk object will have a size that almost exactly fills ** a power-of-two allocation. This mimimizes wasted space in power-of-two ** memory allocators. */ //#define JOURNAL_CHUNKSIZE ((int)(1024-sizeof(FileChunk*))) const int JOURNAL_CHUNKSIZE = 4096; /* Macro to find the minimum of two numeric values. */ //#if !MIN //# define MIN(x,y) ((x)<(y)?(x):(y)) //#endif static int MIN( int x, int y ) { return ( x < y ) ? x : y; } static int MIN( int x, u32 y ) { return ( x < y ) ? x : (int)y; } /* ** The rollback journal is composed of a linked list of these structures. */ public class FileChunk { public FileChunk pNext; /* Next chunk in the journal */ public byte[] zChunk = new byte[JOURNAL_CHUNKSIZE]; /* Content of this chunk */ }; /* ** An instance of this object serves as a cursor into the rollback journal. ** The cursor can be either for reading or writing. */ public class FilePoint { public long iOffset; /* Offset from the beginning of the file */ public FileChunk pChunk; /* Specific chunk into which cursor points */ }; /* ** This subclass is a subclass of sqlite3_file. Each open memory-journal ** is an instance of this class. */ public partial class sqlite3_file { //public sqlite3_io_methods pMethods; /* Parent class. MUST BE FIRST */ public FileChunk pFirst; /* Head of in-memory chunk-list */ public FilePoint endpoint; /* Pointer to the end of the file */ public FilePoint readpoint; /* Pointer to the end of the last xRead() */ }; /* ** Read data from the in-memory journal file. This is the implementation ** of the sqlite3_vfs.xRead method. */ static int memjrnlRead( sqlite3_file pJfd, /* The journal file from which to read */ byte[] zBuf, /* Put the results here */ int iAmt, /* Number of bytes to read */ sqlite3_int64 iOfst /* Begin reading at this offset */ ) { MemJournal p = (MemJournal)pJfd; byte[] zOut = zBuf; int nRead = iAmt; int iChunkOffset; FileChunk pChunk; /* SQLite never tries to read past the end of a rollback journal file */ Debug.Assert( iOfst + iAmt <= p.endpoint.iOffset ); if ( p.readpoint.iOffset != iOfst || iOfst == 0 ) { int iOff = 0; for ( pChunk = p.pFirst; ALWAYS( pChunk != null ) && ( iOff + JOURNAL_CHUNKSIZE ) <= iOfst; pChunk = pChunk.pNext ) { iOff += JOURNAL_CHUNKSIZE; } } else { pChunk = p.readpoint.pChunk; } iChunkOffset = (int)( iOfst % JOURNAL_CHUNKSIZE ); int izOut = 0; do { int iSpace = JOURNAL_CHUNKSIZE - iChunkOffset; int nCopy = MIN( nRead, ( JOURNAL_CHUNKSIZE - iChunkOffset ) ); Buffer.BlockCopy( pChunk.zChunk, iChunkOffset, zOut, izOut, nCopy ); //memcpy( zOut, pChunk.zChunk[iChunkOffset], nCopy ); izOut += nCopy;// zOut += nCopy; nRead -= iSpace; iChunkOffset = 0; } while ( nRead >= 0 && ( pChunk = pChunk.pNext ) != null && nRead > 0 ); p.readpoint.iOffset = (int)( iOfst + iAmt ); p.readpoint.pChunk = pChunk; return SQLITE_OK; } /* ** Write data to the file. */ static int memjrnlWrite( sqlite3_file pJfd, /* The journal file into which to write */ byte[] zBuf, /* Take data to be written from here */ int iAmt, /* Number of bytes to write */ sqlite3_int64 iOfst /* Begin writing at this offset into the file */ ) { MemJournal p = (MemJournal)pJfd; int nWrite = iAmt; byte[] zWrite = zBuf; int izWrite = 0; /* An in-memory journal file should only ever be appended to. Random ** access writes are not required by sqlite. */ Debug.Assert( iOfst == p.endpoint.iOffset ); UNUSED_PARAMETER( iOfst ); while ( nWrite > 0 ) { FileChunk pChunk = p.endpoint.pChunk; int iChunkOffset = (int)( p.endpoint.iOffset % JOURNAL_CHUNKSIZE ); int iSpace = MIN( nWrite, JOURNAL_CHUNKSIZE - iChunkOffset ); if ( iChunkOffset == 0 ) { /* New chunk is required to extend the file. */ FileChunk pNew = new FileChunk();// sqlite3_malloc( sizeof( FileChunk ) ); if ( null == pNew ) { return SQLITE_IOERR_NOMEM; } pNew.pNext = null; if ( pChunk != null ) { Debug.Assert( p.pFirst != null ); pChunk.pNext = pNew; } else { Debug.Assert( null == p.pFirst ); p.pFirst = pNew; } p.endpoint.pChunk = pNew; } Buffer.BlockCopy( zWrite, izWrite, p.endpoint.pChunk.zChunk, iChunkOffset, iSpace ); //memcpy( &p.endpoint.pChunk.zChunk[iChunkOffset], zWrite, iSpace ); izWrite += iSpace;//zWrite += iSpace; nWrite -= iSpace; p.endpoint.iOffset += iSpace; } return SQLITE_OK; } /* ** Truncate the file. */ static int memjrnlTruncate( sqlite3_file pJfd, sqlite3_int64 size ) { MemJournal p = (MemJournal)pJfd; FileChunk pChunk; Debug.Assert( size == 0 ); UNUSED_PARAMETER( size ); pChunk = p.pFirst; while ( pChunk != null ) { ////FileChunk pTmp = pChunk; pChunk = pChunk.pNext; //sqlite3_free( ref pTmp ); } sqlite3MemJournalOpen( pJfd ); return SQLITE_OK; } /* ** Close the file. */ static int memjrnlClose( MemJournal pJfd ) { memjrnlTruncate( pJfd, 0 ); return SQLITE_OK; } /* ** Sync the file. ** ** Syncing an in-memory journal is a no-op. And, in fact, this routine ** is never called in a working implementation. This implementation ** exists purely as a contingency, in case some malfunction in some other ** part of SQLite causes Sync to be called by mistake. */ static int memjrnlSync( sqlite3_file NotUsed, int NotUsed2 ) { UNUSED_PARAMETER2( NotUsed, NotUsed2 ); return SQLITE_OK; } /* ** Query the size of the file in bytes. */ static int memjrnlFileSize( sqlite3_file pJfd, ref long pSize ) { MemJournal p = (MemJournal)pJfd; pSize = p.endpoint.iOffset; return SQLITE_OK; } /* ** Table of methods for MemJournal sqlite3_file object. */ static sqlite3_io_methods MemJournalMethods = new sqlite3_io_methods( 1, /* iVersion */ (dxClose)memjrnlClose, /* xClose */ (dxRead)memjrnlRead, /* xRead */ (dxWrite)memjrnlWrite, /* xWrite */ (dxTruncate)memjrnlTruncate, /* xTruncate */ (dxSync)memjrnlSync, /* xSync */ (dxFileSize)memjrnlFileSize, /* xFileSize */ null, /* xLock */ null, /* xUnlock */ null, /* xCheckReservedLock */ null, /* xFileControl */ null, /* xSectorSize */ null, /* xDeviceCharacteristics */ null, /* xShmMap */ null, /* xShmLock */ null, /* xShmBarrier */ null /* xShmUnlock */ ); /* ** Open a journal file. */ static void sqlite3MemJournalOpen( sqlite3_file pJfd ) { MemJournal p = (MemJournal)pJfd; //memset( p, 0, sqlite3MemJournalSize() ); p.pFirst = null; p.endpoint = new FilePoint(); p.readpoint = new FilePoint(); p.pMethods = MemJournalMethods;//(sqlite3_io_methods*)&MemJournalMethods; } /* ** Return true if the file-handle passed as an argument is ** an in-memory journal */ static bool sqlite3IsMemJournal( sqlite3_file pJfd ) { return pJfd.pMethods == MemJournalMethods; } /* ** Return the number of bytes required to store a MemJournal file descriptor. */ static int sqlite3MemJournalSize() { return 3096; // sizeof( MemJournal ); } } }