summaryrefslogtreecommitdiffstats
path: root/ext/misc/memvfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/misc/memvfs.c')
-rw-r--r--ext/misc/memvfs.c575
1 files changed, 575 insertions, 0 deletions
diff --git a/ext/misc/memvfs.c b/ext/misc/memvfs.c
new file mode 100644
index 0000000..83fc946
--- /dev/null
+++ b/ext/misc/memvfs.c
@@ -0,0 +1,575 @@
+/*
+** 2016-09-07
+**
+** 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 is an in-memory VFS implementation. The application supplies
+** a chunk of memory to hold the database file.
+**
+** Because there is place to store a rollback or wal journal, the database
+** must use one of journal_mode=MEMORY or journal_mode=NONE.
+**
+** USAGE:
+**
+** sqlite3_open_v2("file:/whatever?ptr=0xf05538&sz=14336&max=65536", &db,
+** SQLITE_OPEN_READWRITE | SQLITE_OPEN_URI,
+** "memvfs");
+**
+** These are the query parameters:
+**
+** ptr= The address of the memory buffer that holds the database.
+**
+** sz= The current size the database file
+**
+** maxsz= The maximum size of the database. In other words, the
+** amount of space allocated for the ptr= buffer.
+**
+** freeonclose= If true, then sqlite3_free() is called on the ptr=
+** value when the connection closes.
+**
+** The ptr= and sz= query parameters are required. If maxsz= is omitted,
+** then it defaults to the sz= value. Parameter values can be in either
+** decimal or hexadecimal. The filename in the URI is ignored.
+*/
+#include <sqlite3ext.h>
+SQLITE_EXTENSION_INIT1
+#include <string.h>
+#include <assert.h>
+
+
+/*
+** Forward declaration of objects used by this utility
+*/
+typedef struct sqlite3_vfs MemVfs;
+typedef struct MemFile MemFile;
+
+/* Access to a lower-level VFS that (might) implement dynamic loading,
+** access to randomness, etc.
+*/
+#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
+
+/* An open file */
+struct MemFile {
+ sqlite3_file base; /* IO methods */
+ sqlite3_int64 sz; /* Size of the file */
+ sqlite3_int64 szMax; /* Space allocated to aData */
+ unsigned char *aData; /* content of the file */
+ int bFreeOnClose; /* Invoke sqlite3_free() on aData at close */
+};
+
+/*
+** Methods for MemFile
+*/
+static int memClose(sqlite3_file*);
+static int memRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+static int memWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
+static int memTruncate(sqlite3_file*, sqlite3_int64 size);
+static int memSync(sqlite3_file*, int flags);
+static int memFileSize(sqlite3_file*, sqlite3_int64 *pSize);
+static int memLock(sqlite3_file*, int);
+static int memUnlock(sqlite3_file*, int);
+static int memCheckReservedLock(sqlite3_file*, int *pResOut);
+static int memFileControl(sqlite3_file*, int op, void *pArg);
+static int memSectorSize(sqlite3_file*);
+static int memDeviceCharacteristics(sqlite3_file*);
+static int memShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
+static int memShmLock(sqlite3_file*, int offset, int n, int flags);
+static void memShmBarrier(sqlite3_file*);
+static int memShmUnmap(sqlite3_file*, int deleteFlag);
+static int memFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
+static int memUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
+
+/*
+** Methods for MemVfs
+*/
+static int memOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
+static int memDelete(sqlite3_vfs*, const char *zName, int syncDir);
+static int memAccess(sqlite3_vfs*, const char *zName, int flags, int *);
+static int memFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
+static void *memDlOpen(sqlite3_vfs*, const char *zFilename);
+static void memDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
+static void (*memDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
+static void memDlClose(sqlite3_vfs*, void*);
+static int memRandomness(sqlite3_vfs*, int nByte, char *zOut);
+static int memSleep(sqlite3_vfs*, int microseconds);
+static int memCurrentTime(sqlite3_vfs*, double*);
+static int memGetLastError(sqlite3_vfs*, int, char *);
+static int memCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
+
+static sqlite3_vfs mem_vfs = {
+ 2, /* iVersion */
+ 0, /* szOsFile (set when registered) */
+ 1024, /* mxPathname */
+ 0, /* pNext */
+ "memvfs", /* zName */
+ 0, /* pAppData (set when registered) */
+ memOpen, /* xOpen */
+ memDelete, /* xDelete */
+ memAccess, /* xAccess */
+ memFullPathname, /* xFullPathname */
+ memDlOpen, /* xDlOpen */
+ memDlError, /* xDlError */
+ memDlSym, /* xDlSym */
+ memDlClose, /* xDlClose */
+ memRandomness, /* xRandomness */
+ memSleep, /* xSleep */
+ memCurrentTime, /* xCurrentTime */
+ memGetLastError, /* xGetLastError */
+ memCurrentTimeInt64 /* xCurrentTimeInt64 */
+};
+
+static const sqlite3_io_methods mem_io_methods = {
+ 3, /* iVersion */
+ memClose, /* xClose */
+ memRead, /* xRead */
+ memWrite, /* xWrite */
+ memTruncate, /* xTruncate */
+ memSync, /* xSync */
+ memFileSize, /* xFileSize */
+ memLock, /* xLock */
+ memUnlock, /* xUnlock */
+ memCheckReservedLock, /* xCheckReservedLock */
+ memFileControl, /* xFileControl */
+ memSectorSize, /* xSectorSize */
+ memDeviceCharacteristics, /* xDeviceCharacteristics */
+ memShmMap, /* xShmMap */
+ memShmLock, /* xShmLock */
+ memShmBarrier, /* xShmBarrier */
+ memShmUnmap, /* xShmUnmap */
+ memFetch, /* xFetch */
+ memUnfetch /* xUnfetch */
+};
+
+
+
+/*
+** Close an mem-file.
+**
+** The pData pointer is owned by the application, so there is nothing
+** to free.
+*/
+static int memClose(sqlite3_file *pFile){
+ MemFile *p = (MemFile *)pFile;
+ if( p->bFreeOnClose ) sqlite3_free(p->aData);
+ return SQLITE_OK;
+}
+
+/*
+** Read data from an mem-file.
+*/
+static int memRead(
+ sqlite3_file *pFile,
+ void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ MemFile *p = (MemFile *)pFile;
+ memcpy(zBuf, p->aData+iOfst, iAmt);
+ return SQLITE_OK;
+}
+
+/*
+** Write data to an mem-file.
+*/
+static int memWrite(
+ sqlite3_file *pFile,
+ const void *z,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ MemFile *p = (MemFile *)pFile;
+ if( iOfst+iAmt>p->sz ){
+ if( iOfst+iAmt>p->szMax ) return SQLITE_FULL;
+ if( iOfst>p->sz ) memset(p->aData+p->sz, 0, iOfst-p->sz);
+ p->sz = iOfst+iAmt;
+ }
+ memcpy(p->aData+iOfst, z, iAmt);
+ return SQLITE_OK;
+}
+
+/*
+** Truncate an mem-file.
+*/
+static int memTruncate(sqlite3_file *pFile, sqlite_int64 size){
+ MemFile *p = (MemFile *)pFile;
+ if( size>p->sz ){
+ if( size>p->szMax ) return SQLITE_FULL;
+ memset(p->aData+p->sz, 0, size-p->sz);
+ }
+ p->sz = size;
+ return SQLITE_OK;
+}
+
+/*
+** Sync an mem-file.
+*/
+static int memSync(sqlite3_file *pFile, int flags){
+ return SQLITE_OK;
+}
+
+/*
+** Return the current file-size of an mem-file.
+*/
+static int memFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
+ MemFile *p = (MemFile *)pFile;
+ *pSize = p->sz;
+ return SQLITE_OK;
+}
+
+/*
+** Lock an mem-file.
+*/
+static int memLock(sqlite3_file *pFile, int eLock){
+ return SQLITE_OK;
+}
+
+/*
+** Unlock an mem-file.
+*/
+static int memUnlock(sqlite3_file *pFile, int eLock){
+ return SQLITE_OK;
+}
+
+/*
+** Check if another file-handle holds a RESERVED lock on an mem-file.
+*/
+static int memCheckReservedLock(sqlite3_file *pFile, int *pResOut){
+ *pResOut = 0;
+ return SQLITE_OK;
+}
+
+/*
+** File control method. For custom operations on an mem-file.
+*/
+static int memFileControl(sqlite3_file *pFile, int op, void *pArg){
+ MemFile *p = (MemFile *)pFile;
+ int rc = SQLITE_NOTFOUND;
+ if( op==SQLITE_FCNTL_VFSNAME ){
+ *(char**)pArg = sqlite3_mprintf("mem(%p,%lld)", p->aData, p->sz);
+ rc = SQLITE_OK;
+ }
+ return rc;
+}
+
+/*
+** Return the sector-size in bytes for an mem-file.
+*/
+static int memSectorSize(sqlite3_file *pFile){
+ return 1024;
+}
+
+/*
+** Return the device characteristic flags supported by an mem-file.
+*/
+static int memDeviceCharacteristics(sqlite3_file *pFile){
+ return SQLITE_IOCAP_ATOMIC |
+ SQLITE_IOCAP_POWERSAFE_OVERWRITE |
+ SQLITE_IOCAP_SAFE_APPEND |
+ SQLITE_IOCAP_SEQUENTIAL;
+}
+
+/* Create a shared memory file mapping */
+static int memShmMap(
+ sqlite3_file *pFile,
+ int iPg,
+ int pgsz,
+ int bExtend,
+ void volatile **pp
+){
+ return SQLITE_IOERR_SHMMAP;
+}
+
+/* Perform locking on a shared-memory segment */
+static int memShmLock(sqlite3_file *pFile, int offset, int n, int flags){
+ return SQLITE_IOERR_SHMLOCK;
+}
+
+/* Memory barrier operation on shared memory */
+static void memShmBarrier(sqlite3_file *pFile){
+ return;
+}
+
+/* Unmap a shared memory segment */
+static int memShmUnmap(sqlite3_file *pFile, int deleteFlag){
+ return SQLITE_OK;
+}
+
+/* Fetch a page of a memory-mapped file */
+static int memFetch(
+ sqlite3_file *pFile,
+ sqlite3_int64 iOfst,
+ int iAmt,
+ void **pp
+){
+ MemFile *p = (MemFile *)pFile;
+ *pp = (void*)(p->aData + iOfst);
+ return SQLITE_OK;
+}
+
+/* Release a memory-mapped page */
+static int memUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
+ return SQLITE_OK;
+}
+
+/*
+** Open an mem file handle.
+*/
+static int memOpen(
+ sqlite3_vfs *pVfs,
+ const char *zName,
+ sqlite3_file *pFile,
+ int flags,
+ int *pOutFlags
+){
+ MemFile *p = (MemFile*)pFile;
+ memset(p, 0, sizeof(*p));
+ if( (flags & SQLITE_OPEN_MAIN_DB)==0 ) return SQLITE_CANTOPEN;
+ p->aData = (unsigned char*)sqlite3_uri_int64(zName,"ptr",0);
+ if( p->aData==0 ) return SQLITE_CANTOPEN;
+ p->sz = sqlite3_uri_int64(zName,"sz",0);
+ if( p->sz<0 ) return SQLITE_CANTOPEN;
+ p->szMax = sqlite3_uri_int64(zName,"max",p->sz);
+ if( p->szMax<p->sz ) return SQLITE_CANTOPEN;
+ p->bFreeOnClose = sqlite3_uri_boolean(zName,"freeonclose",0);
+ pFile->pMethods = &mem_io_methods;
+ return SQLITE_OK;
+}
+
+/*
+** Delete the file located at zPath. If the dirSync argument is true,
+** ensure the file-system modifications are synced to disk before
+** returning.
+*/
+static int memDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+ return SQLITE_IOERR_DELETE;
+}
+
+/*
+** Test for access permissions. Return true if the requested permission
+** is available, or false otherwise.
+*/
+static int memAccess(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int flags,
+ int *pResOut
+){
+ *pResOut = 0;
+ return SQLITE_OK;
+}
+
+/*
+** Populate buffer zOut with the full canonical pathname corresponding
+** to the pathname in zPath. zOut is guaranteed to point to a buffer
+** of at least (INST_MAX_PATHNAME+1) bytes.
+*/
+static int memFullPathname(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int nOut,
+ char *zOut
+){
+ sqlite3_snprintf(nOut, zOut, "%s", zPath);
+ return SQLITE_OK;
+}
+
+/*
+** Open the dynamic library located at zPath and return a handle.
+*/
+static void *memDlOpen(sqlite3_vfs *pVfs, const char *zPath){
+ return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
+}
+
+/*
+** Populate the buffer zErrMsg (size nByte bytes) with a human readable
+** utf-8 string describing the most recent error encountered associated
+** with dynamic libraries.
+*/
+static void memDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
+ ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
+}
+
+/*
+** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
+*/
+static void (*memDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
+ return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
+}
+
+/*
+** Close the dynamic library handle pHandle.
+*/
+static void memDlClose(sqlite3_vfs *pVfs, void *pHandle){
+ ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
+}
+
+/*
+** Populate the buffer pointed to by zBufOut with nByte bytes of
+** random data.
+*/
+static int memRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
+ return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
+}
+
+/*
+** Sleep for nMicro microseconds. Return the number of microseconds
+** actually slept.
+*/
+static int memSleep(sqlite3_vfs *pVfs, int nMicro){
+ return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro);
+}
+
+/*
+** Return the current time as a Julian Day number in *pTimeOut.
+*/
+static int memCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
+ return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
+}
+
+static int memGetLastError(sqlite3_vfs *pVfs, int a, char *b){
+ return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
+}
+static int memCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
+ return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
+}
+
+#ifdef MEMVFS_TEST
+/*
+** memvfs_from_file(FILENAME, MAXSIZE)
+**
+** This an SQL function used to help in testing the memvfs VFS. The
+** function reads the content of a file into memory and then returns
+** a URI that can be handed to ATTACH to attach the memory buffer as
+** a database. Example:
+**
+** ATTACH memvfs_from_file('test.db',1048576) AS inmem;
+**
+** The optional MAXSIZE argument gives the size of the memory allocation
+** used to hold the database. If omitted, it defaults to the size of the
+** file on disk.
+*/
+#include <stdio.h>
+static void memvfsFromFileFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ unsigned char *p;
+ sqlite3_int64 sz;
+ sqlite3_int64 szMax;
+ FILE *in;
+ const char *zFilename = (const char*)sqlite3_value_text(argv[0]);
+ char *zUri;
+
+ if( zFilename==0 ) return;
+ in = fopen(zFilename, "rb");
+ if( in==0 ) return;
+ fseek(in, 0, SEEK_END);
+ szMax = sz = ftell(in);
+ rewind(in);
+ if( argc>=2 ){
+ szMax = sqlite3_value_int64(argv[1]);
+ if( szMax<sz ) szMax = sz;
+ }
+ p = sqlite3_malloc64( szMax );
+ if( p==0 ){
+ fclose(in);
+ sqlite3_result_error_nomem(context);
+ return;
+ }
+ fread(p, sz, 1, in);
+ fclose(in);
+ zUri = sqlite3_mprintf(
+ "file:/mem?vfs=memvfs&ptr=%lld&sz=%lld&max=%lld&freeonclose=1",
+ (sqlite3_int64)p, sz, szMax);
+ sqlite3_result_text(context, zUri, -1, sqlite3_free);
+}
+#endif /* MEMVFS_TEST */
+
+#ifdef MEMVFS_TEST
+/*
+** memvfs_to_file(SCHEMA, FILENAME)
+**
+** The schema identified by SCHEMA must be a memvfs database. Write
+** the content of this database into FILENAME.
+*/
+static void memvfsToFileFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ MemFile *p = 0;
+ FILE *out;
+ int rc;
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ sqlite3_vfs *pVfs = 0;
+ const char *zSchema = (const char*)sqlite3_value_text(argv[0]);
+ const char *zFilename = (const char*)sqlite3_value_text(argv[1]);
+
+ if( zFilename==0 ) return;
+ out = fopen(zFilename, "wb");
+ if( out==0 ) return;
+ rc = sqlite3_file_control(db, zSchema, SQLITE_FCNTL_VFS_POINTER, &pVfs);
+ if( rc || pVfs==0 ) return;
+ if( strcmp(pVfs->zName,"memvfs")!=0 ) return;
+ rc = sqlite3_file_control(db, zSchema, SQLITE_FCNTL_FILE_POINTER, &p);
+ if( rc ) return;
+ fwrite(p->aData, 1, (size_t)p->sz, out);
+ fclose(out);
+}
+#endif /* MEMVFS_TEST */
+
+#ifdef MEMVFS_TEST
+/* Called for each new database connection */
+static int memvfsRegister(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const struct sqlite3_api_routines *pThunk
+){
+ sqlite3_create_function(db, "memvfs_from_file", 1, SQLITE_UTF8, 0,
+ memvfsFromFileFunc, 0, 0);
+ sqlite3_create_function(db, "memvfs_from_file", 2, SQLITE_UTF8, 0,
+ memvfsFromFileFunc, 0, 0);
+ sqlite3_create_function(db, "memvfs_to_file", 2, SQLITE_UTF8, 0,
+ memvfsToFileFunc, 0, 0);
+ return SQLITE_OK;
+}
+#endif /* MEMVFS_TEST */
+
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+/*
+** This routine is called when the extension is loaded.
+** Register the new VFS.
+*/
+int sqlite3_memvfs_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ int rc = SQLITE_OK;
+ SQLITE_EXTENSION_INIT2(pApi);
+ mem_vfs.pAppData = sqlite3_vfs_find(0);
+ if( mem_vfs.pAppData==0 ) return SQLITE_ERROR;
+ mem_vfs.szOsFile = sizeof(MemFile);
+ rc = sqlite3_vfs_register(&mem_vfs, 1);
+#ifdef MEMVFS_TEST
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_auto_extension((void(*)(void))memvfsRegister);
+ }
+ if( rc==SQLITE_OK ){
+ rc = memvfsRegister(db, pzErrMsg, pApi);
+ }
+#endif
+ if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
+ return rc;
+}