#include "sqliteInt.h"
typedef struct MemJournal MemJournal;
typedef struct FilePoint FilePoint;
typedef struct FileChunk FileChunk;
struct FileChunk {
FileChunk *pNext;
u8 zChunk[8];
};
#define MEMJOURNAL_DFLT_FILECHUNKSIZE 1024
#define fileChunkSize(nChunkSize) (sizeof(FileChunk) + ((nChunkSize)-8))
struct FilePoint {
sqlite3_int64 iOffset;
FileChunk *pChunk;
};
struct MemJournal {
const sqlite3_io_methods *pMethod;
int nChunkSize;
int nSpill;
FileChunk *pFirst;
FilePoint endpoint;
FilePoint readpoint;
int flags;
sqlite3_vfs *pVfs;
const char *zJournal;
};
static int memjrnlRead(
sqlite3_file *pJfd,
void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
MemJournal *p = (MemJournal *)pJfd;
u8 *zOut = zBuf;
int nRead = iAmt;
int iChunkOffset;
FileChunk *pChunk;
if( (iAmt+iOfst)>p->endpoint.iOffset ){
return SQLITE_IOERR_SHORT_READ;
}
assert( p->readpoint.iOffset==0 || p->readpoint.pChunk!=0 );
if( p->readpoint.iOffset!=iOfst || iOfst==0 ){
sqlite3_int64 iOff = 0;
for(pChunk=p->pFirst;
ALWAYS(pChunk) && (iOff+p->nChunkSize)<=iOfst;
pChunk=pChunk->pNext
){
iOff += p->nChunkSize;
}
}else{
pChunk = p->readpoint.pChunk;
assert( pChunk!=0 );
}
iChunkOffset = (int)(iOfst%p->nChunkSize);
do {
int iSpace = p->nChunkSize - iChunkOffset;
int nCopy = MIN(nRead, (p->nChunkSize - iChunkOffset));
memcpy(zOut, (u8*)pChunk->zChunk + iChunkOffset, nCopy);
zOut += nCopy;
nRead -= iSpace;
iChunkOffset = 0;
} while( nRead>=0 && (pChunk=pChunk->pNext)!=0 && nRead>0 );
p->readpoint.iOffset = pChunk ? iOfst+iAmt : 0;
p->readpoint.pChunk = pChunk;
return SQLITE_OK;
}
static void memjrnlFreeChunks(FileChunk *pFirst){
FileChunk *pIter;
FileChunk *pNext;
for(pIter=pFirst; pIter; pIter=pNext){
pNext = pIter->pNext;
sqlite3_free(pIter);
}
}
static int memjrnlCreateFile(MemJournal *p){
int rc;
sqlite3_file *pReal = (sqlite3_file*)p;
MemJournal copy = *p;
memset(p, 0, sizeof(MemJournal));
rc = sqlite3OsOpen(copy.pVfs, copy.zJournal, pReal, copy.flags, 0);
if( rc==SQLITE_OK ){
int nChunk = copy.nChunkSize;
i64 iOff = 0;
FileChunk *pIter;
for(pIter=copy.pFirst; pIter; pIter=pIter->pNext){
if( iOff + nChunk > copy.endpoint.iOffset ){
nChunk = copy.endpoint.iOffset - iOff;
}
rc = sqlite3OsWrite(pReal, (u8*)pIter->zChunk, nChunk, iOff);
if( rc ) break;
iOff += nChunk;
}
if( rc==SQLITE_OK ){
memjrnlFreeChunks(copy.pFirst);
}
}
if( rc!=SQLITE_OK ){
sqlite3OsClose(pReal);
*p = copy;
}
return rc;
}
static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size);
static int memjrnlWrite(
sqlite3_file *pJfd,
const void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
MemJournal *p = (MemJournal *)pJfd;
int nWrite = iAmt;
u8 *zWrite = (u8 *)zBuf;
if( p->nSpill>0 && (iAmt+iOfst)>p->nSpill ){
int rc = memjrnlCreateFile(p);
if( rc==SQLITE_OK ){
rc = sqlite3OsWrite(pJfd, zBuf, iAmt, iOfst);
}
return rc;
}
else{
assert( iOfst<=p->endpoint.iOffset );
if( iOfst>0 && iOfst!=p->endpoint.iOffset ){
memjrnlTruncate(pJfd, iOfst);
}
if( iOfst==0 && p->pFirst ){
assert( p->nChunkSize>iAmt );
memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt);
}else{
while( nWrite>0 ){
FileChunk *pChunk = p->endpoint.pChunk;
int iChunkOffset = (int)(p->endpoint.iOffset%p->nChunkSize);
int iSpace = MIN(nWrite, p->nChunkSize - iChunkOffset);
assert( pChunk!=0 || iChunkOffset==0 );
if( iChunkOffset==0 ){
FileChunk *pNew = sqlite3_malloc(fileChunkSize(p->nChunkSize));
if( !pNew ){
return SQLITE_IOERR_NOMEM_BKPT;
}
pNew->pNext = 0;
if( pChunk ){
assert( p->pFirst );
pChunk->pNext = pNew;
}else{
assert( !p->pFirst );
p->pFirst = pNew;
}
pChunk = p->endpoint.pChunk = pNew;
}
assert( pChunk!=0 );
memcpy((u8*)pChunk->zChunk + iChunkOffset, zWrite, iSpace);
zWrite += iSpace;
nWrite -= iSpace;
p->endpoint.iOffset += iSpace;
}
}
}
return SQLITE_OK;
}
static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
MemJournal *p = (MemJournal *)pJfd;
assert( p->endpoint.pChunk==0 || p->endpoint.pChunk->pNext==0 );
if( size<p->endpoint.iOffset ){
FileChunk *pIter = 0;
if( size==0 ){
memjrnlFreeChunks(p->pFirst);
p->pFirst = 0;
}else{
i64 iOff = p->nChunkSize;
for(pIter=p->pFirst; ALWAYS(pIter) && iOff<size; pIter=pIter->pNext){
iOff += p->nChunkSize;
}
if( ALWAYS(pIter) ){
memjrnlFreeChunks(pIter->pNext);
pIter->pNext = 0;
}
}
p->endpoint.pChunk = pIter;
p->endpoint.iOffset = size;
p->readpoint.pChunk = 0;
p->readpoint.iOffset = 0;
}
return SQLITE_OK;
}
static int memjrnlClose(sqlite3_file *pJfd){
MemJournal *p = (MemJournal *)pJfd;
memjrnlFreeChunks(p->pFirst);
return SQLITE_OK;
}
static int memjrnlSync(sqlite3_file *pJfd, int flags){
UNUSED_PARAMETER2(pJfd, flags);
return SQLITE_OK;
}
static int memjrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){
MemJournal *p = (MemJournal *)pJfd;
*pSize = (sqlite_int64) p->endpoint.iOffset;
return SQLITE_OK;
}
static const struct sqlite3_io_methods MemJournalMethods = {
1,
memjrnlClose,
memjrnlRead,
memjrnlWrite,
memjrnlTruncate,
memjrnlSync,
memjrnlFileSize,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
};
int sqlite3JournalOpen(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_file *pJfd,
int flags,
int nSpill
){
MemJournal *p = (MemJournal*)pJfd;
assert( zName || nSpill<0 || (flags & SQLITE_OPEN_EXCLUSIVE) );
memset(p, 0, sizeof(MemJournal));
if( nSpill==0 ){
return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0);
}
if( nSpill>0 ){
p->nChunkSize = nSpill;
}else{
p->nChunkSize = 8 + MEMJOURNAL_DFLT_FILECHUNKSIZE - sizeof(FileChunk);
assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) );
}
pJfd->pMethods = (const sqlite3_io_methods*)&MemJournalMethods;
p->nSpill = nSpill;
p->flags = flags;
p->zJournal = zName;
p->pVfs = pVfs;
return SQLITE_OK;
}
void sqlite3MemJournalOpen(sqlite3_file *pJfd){
sqlite3JournalOpen(0, 0, pJfd, 0, -1);
}
#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|| defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
int sqlite3JournalCreate(sqlite3_file *pJfd){
int rc = SQLITE_OK;
MemJournal *p = (MemJournal*)pJfd;
if( pJfd->pMethods==&MemJournalMethods && (
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
p->nSpill>0
#else
NEVER(p->nSpill>0)
#endif
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|| (p->flags & SQLITE_OPEN_MAIN_JOURNAL)
#endif
)){
rc = memjrnlCreateFile(p);
}
return rc;
}
#endif
int sqlite3JournalIsInMemory(sqlite3_file *p){
return p->pMethods==&MemJournalMethods;
}
int sqlite3JournalSize(sqlite3_vfs *pVfs){
return MAX(pVfs->szOsFile, (int)sizeof(MemJournal));
}