1/*
2** 2008 October 7
3**
4** The author disclaims copyright to this source code.  In place of
5** a legal notice, here is a blessing:
6**
7**    May you do good and not evil.
8**    May you find forgiveness for yourself and forgive others.
9**    May you share freely, never taking more than you give.
10**
11*************************************************************************
12**
13** This file contains code use to implement an in-memory rollback journal.
14** The in-memory rollback journal is used to journal transactions for
15** ":memory:" databases and when the journal_mode=MEMORY pragma is used.
16*/
17#include "sqliteInt.h"
18
19/* Forward references to internal structures */
20typedef struct MemJournal MemJournal;
21typedef struct FilePoint FilePoint;
22typedef struct FileChunk FileChunk;
23
24/* Space to hold the rollback journal is allocated in increments of
25** this many bytes.
26**
27** The size chosen is a little less than a power of two.  That way,
28** the FileChunk object will have a size that almost exactly fills
29** a power-of-two allocation.  This mimimizes wasted space in power-of-two
30** memory allocators.
31*/
32#define JOURNAL_CHUNKSIZE ((int)(1024-sizeof(FileChunk*)))
33
34/* Macro to find the minimum of two numeric values.
35*/
36#ifndef MIN
37# define MIN(x,y) ((x)<(y)?(x):(y))
38#endif
39
40/*
41** The rollback journal is composed of a linked list of these structures.
42*/
43struct FileChunk {
44  FileChunk *pNext;               /* Next chunk in the journal */
45  u8 zChunk[JOURNAL_CHUNKSIZE];   /* Content of this chunk */
46};
47
48/*
49** An instance of this object serves as a cursor into the rollback journal.
50** The cursor can be either for reading or writing.
51*/
52struct FilePoint {
53  sqlite3_int64 iOffset;          /* Offset from the beginning of the file */
54  FileChunk *pChunk;              /* Specific chunk into which cursor points */
55};
56
57/*
58** This subclass is a subclass of sqlite3_file.  Each open memory-journal
59** is an instance of this class.
60*/
61struct MemJournal {
62  sqlite3_io_methods *pMethod;    /* Parent class. MUST BE FIRST */
63  FileChunk *pFirst;              /* Head of in-memory chunk-list */
64  FilePoint endpoint;             /* Pointer to the end of the file */
65  FilePoint readpoint;            /* Pointer to the end of the last xRead() */
66};
67
68/*
69** Read data from the in-memory journal file.  This is the implementation
70** of the sqlite3_vfs.xRead method.
71*/
72static int memjrnlRead(
73  sqlite3_file *pJfd,    /* The journal file from which to read */
74  void *zBuf,            /* Put the results here */
75  int iAmt,              /* Number of bytes to read */
76  sqlite_int64 iOfst     /* Begin reading at this offset */
77){
78  MemJournal *p = (MemJournal *)pJfd;
79  u8 *zOut = zBuf;
80  int nRead = iAmt;
81  int iChunkOffset;
82  FileChunk *pChunk;
83
84  /* SQLite never tries to read past the end of a rollback journal file */
85  assert( iOfst+iAmt<=p->endpoint.iOffset );
86
87  if( p->readpoint.iOffset!=iOfst || iOfst==0 ){
88    sqlite3_int64 iOff = 0;
89    for(pChunk=p->pFirst;
90        ALWAYS(pChunk) && (iOff+JOURNAL_CHUNKSIZE)<=iOfst;
91        pChunk=pChunk->pNext
92    ){
93      iOff += JOURNAL_CHUNKSIZE;
94    }
95  }else{
96    pChunk = p->readpoint.pChunk;
97  }
98
99  iChunkOffset = (int)(iOfst%JOURNAL_CHUNKSIZE);
100  do {
101    int iSpace = JOURNAL_CHUNKSIZE - iChunkOffset;
102    int nCopy = MIN(nRead, (JOURNAL_CHUNKSIZE - iChunkOffset));
103    memcpy(zOut, &pChunk->zChunk[iChunkOffset], nCopy);
104    zOut += nCopy;
105    nRead -= iSpace;
106    iChunkOffset = 0;
107  } while( nRead>=0 && (pChunk=pChunk->pNext)!=0 && nRead>0 );
108  p->readpoint.iOffset = iOfst+iAmt;
109  p->readpoint.pChunk = pChunk;
110
111  return SQLITE_OK;
112}
113
114/*
115** Write data to the file.
116*/
117static int memjrnlWrite(
118  sqlite3_file *pJfd,    /* The journal file into which to write */
119  const void *zBuf,      /* Take data to be written from here */
120  int iAmt,              /* Number of bytes to write */
121  sqlite_int64 iOfst     /* Begin writing at this offset into the file */
122){
123  MemJournal *p = (MemJournal *)pJfd;
124  int nWrite = iAmt;
125  u8 *zWrite = (u8 *)zBuf;
126
127  /* An in-memory journal file should only ever be appended to. Random
128  ** access writes are not required by sqlite.
129  */
130  assert( iOfst==p->endpoint.iOffset );
131  UNUSED_PARAMETER(iOfst);
132
133  while( nWrite>0 ){
134    FileChunk *pChunk = p->endpoint.pChunk;
135    int iChunkOffset = (int)(p->endpoint.iOffset%JOURNAL_CHUNKSIZE);
136    int iSpace = MIN(nWrite, JOURNAL_CHUNKSIZE - iChunkOffset);
137
138    if( iChunkOffset==0 ){
139      /* New chunk is required to extend the file. */
140      FileChunk *pNew = sqlite3_malloc(sizeof(FileChunk));
141      if( !pNew ){
142        return SQLITE_IOERR_NOMEM;
143      }
144      pNew->pNext = 0;
145      if( pChunk ){
146        assert( p->pFirst );
147        pChunk->pNext = pNew;
148      }else{
149        assert( !p->pFirst );
150        p->pFirst = pNew;
151      }
152      p->endpoint.pChunk = pNew;
153    }
154
155    memcpy(&p->endpoint.pChunk->zChunk[iChunkOffset], zWrite, iSpace);
156    zWrite += iSpace;
157    nWrite -= iSpace;
158    p->endpoint.iOffset += iSpace;
159  }
160
161  return SQLITE_OK;
162}
163
164/*
165** Truncate the file.
166*/
167static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
168  MemJournal *p = (MemJournal *)pJfd;
169  FileChunk *pChunk;
170  assert(size==0);
171  UNUSED_PARAMETER(size);
172  pChunk = p->pFirst;
173  while( pChunk ){
174    FileChunk *pTmp = pChunk;
175    pChunk = pChunk->pNext;
176    sqlite3_free(pTmp);
177  }
178  sqlite3MemJournalOpen(pJfd);
179  return SQLITE_OK;
180}
181
182/*
183** Close the file.
184*/
185static int memjrnlClose(sqlite3_file *pJfd){
186  memjrnlTruncate(pJfd, 0);
187  return SQLITE_OK;
188}
189
190
191/*
192** Sync the file.
193**
194** Syncing an in-memory journal is a no-op.  And, in fact, this routine
195** is never called in a working implementation.  This implementation
196** exists purely as a contingency, in case some malfunction in some other
197** part of SQLite causes Sync to be called by mistake.
198*/
199static int memjrnlSync(sqlite3_file *NotUsed, int NotUsed2){
200  UNUSED_PARAMETER2(NotUsed, NotUsed2);
201  return SQLITE_OK;
202}
203
204/*
205** Query the size of the file in bytes.
206*/
207static int memjrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){
208  MemJournal *p = (MemJournal *)pJfd;
209  *pSize = (sqlite_int64) p->endpoint.iOffset;
210  return SQLITE_OK;
211}
212
213/*
214** Table of methods for MemJournal sqlite3_file object.
215*/
216static const struct sqlite3_io_methods MemJournalMethods = {
217  1,                /* iVersion */
218  memjrnlClose,     /* xClose */
219  memjrnlRead,      /* xRead */
220  memjrnlWrite,     /* xWrite */
221  memjrnlTruncate,  /* xTruncate */
222  memjrnlSync,      /* xSync */
223  memjrnlFileSize,  /* xFileSize */
224  0,                /* xLock */
225  0,                /* xUnlock */
226  0,                /* xCheckReservedLock */
227  0,                /* xFileControl */
228  0,                /* xSectorSize */
229  0,                /* xDeviceCharacteristics */
230  0,                /* xShmMap */
231  0,                /* xShmLock */
232  0,                /* xShmBarrier */
233  0                 /* xShmUnlock */
234};
235
236/*
237** Open a journal file.
238*/
239void sqlite3MemJournalOpen(sqlite3_file *pJfd){
240  MemJournal *p = (MemJournal *)pJfd;
241  assert( EIGHT_BYTE_ALIGNMENT(p) );
242  memset(p, 0, sqlite3MemJournalSize());
243  p->pMethod = (sqlite3_io_methods*)&MemJournalMethods;
244}
245
246/*
247** Return true if the file-handle passed as an argument is
248** an in-memory journal
249*/
250int sqlite3IsMemJournal(sqlite3_file *pJfd){
251  return pJfd->pMethods==&MemJournalMethods;
252}
253
254/*
255** Return the number of bytes required to store a MemJournal file descriptor.
256*/
257int sqlite3MemJournalSize(void){
258  return sizeof(MemJournal);
259}
260