diff options
Diffstat (limited to 'ext/wasm/scratchpad-opfs-worker2.js')
-rw-r--r-- | ext/wasm/scratchpad-opfs-worker2.js | 494 |
1 files changed, 0 insertions, 494 deletions
diff --git a/ext/wasm/scratchpad-opfs-worker2.js b/ext/wasm/scratchpad-opfs-worker2.js deleted file mode 100644 index 47ace63de..000000000 --- a/ext/wasm/scratchpad-opfs-worker2.js +++ /dev/null @@ -1,494 +0,0 @@ -/* - 2022-05-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. - - *********************************************************************** - - An experiment for wasmfs/opfs. This file MUST be in the same dir as - the sqlite3.js emscripten module or that module won't be able to - resolve the relative URIs (importScript()'s relative URI handling - is, quite frankly, broken). -*/ -'use strict'; - -const toss = function(...args){throw new Error(args.join(' '))}; -/** - Posts a message in the form {type,data} unless passed more than 2 - args, in which case it posts {type, data:[arg1...argN]}. -*/ -const wMsg = function(type,data){ - postMessage({ - type, - data: arguments.length<3 - ? data - : Array.prototype.slice.call(arguments,1) - }); -}; - -const stdout = function(...args){ - wMsg('stdout',args); - console.log(...args); -}; -const stderr = function(...args){ - wMsg('stderr',args); - console.error(...args); -}; - -const log = console.log.bind(console); -const warn = console.warn.bind(console); -const error = console.error.bind(console); - - -const initOpfsBits = async function(sqlite3){ - if(!self.importScripts || !self.FileSystemFileHandle){ - //|| !self.FileSystemFileHandle.prototype.createSyncAccessHandle){ - // ^^^ sync API is not required with WASMFS/OPFS backend. - warn("OPFS is not available in this environment."); - return; - }else if(!sqlite3.capi.wasm.bigIntEnabled){ - error("OPFS requires BigInt support but sqlite3.capi.wasm.bigIntEnabled is false."); - return; - } - //warn('self.FileSystemFileHandle =',self.FileSystemFileHandle); - //warn('self.FileSystemFileHandle.prototype =',self.FileSystemFileHandle.prototype); - const capi = sqlite3.capi, - wasm = capi.wasm; - const sqlite3_vfs = capi.sqlite3_vfs - || toss("Missing sqlite3.capi.sqlite3_vfs object."); - const sqlite3_file = capi.sqlite3_file - || toss("Missing sqlite3.capi.sqlite3_file object."); - const sqlite3_io_methods = capi.sqlite3_io_methods - || toss("Missing sqlite3.capi.sqlite3_io_methods object."); - const StructBinder = sqlite3.StructBinder || toss("Missing sqlite3.StructBinder."); - const debug = console.debug.bind(console), - log = console.log.bind(console); - warn("UNDER CONSTRUCTION: setting up OPFS VFS..."); - - const pDVfs = capi.sqlite3_vfs_find(null)/*pointer to default VFS*/; - const dVfs = pDVfs - ? new sqlite3_vfs(pDVfs) - : null /* dVfs will be null when sqlite3 is built with - SQLITE_OS_OTHER. Though we cannot currently handle - that case, the hope is to eventually be able to. */; - const oVfs = new sqlite3_vfs(); - const oIom = new sqlite3_io_methods(); - oVfs.$iVersion = 2/*yes, two*/; - oVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; - oVfs.$mxPathname = 1024/*sure, why not?*/; - oVfs.$zName = wasm.allocCString("opfs"); - oVfs.ondispose = [ - '$zName', oVfs.$zName, - 'cleanup dVfs', ()=>(dVfs ? dVfs.dispose() : null) - ]; - if(dVfs){ - oVfs.$xSleep = dVfs.$xSleep; - oVfs.$xRandomness = dVfs.$xRandomness; - } - // All C-side memory of oVfs is zeroed out, but just to be explicit: - oVfs.$xDlOpen = oVfs.$xDlError = oVfs.$xDlSym = oVfs.$xDlClose = null; - - /** - Pedantic sidebar about oVfs.ondispose: the entries in that array - are items to clean up when oVfs.dispose() is called, but in this - environment it will never be called. The VFS instance simply - hangs around until the WASM module instance is cleaned up. We - "could" _hypothetically_ clean it up by "importing" an - sqlite3_os_end() impl into the wasm build, but the shutdown order - of the wasm engine and the JS one are undefined so there is no - guaranty that the oVfs instance would be available in one - environment or the other when sqlite3_os_end() is called (_if_ it - gets called at all in a wasm build, which is undefined). - */ - - /** - Installs a StructBinder-bound function pointer member of the - given name and function in the given StructType target object. - It creates a WASM proxy for the given function and arranges for - that proxy to be cleaned up when tgt.dispose() is called. Throws - on the slightest hint of error (e.g. tgt is-not-a StructType, - name does not map to a struct-bound member, etc.). - - Returns a proxy for this function which is bound to tgt and takes - 2 args (name,func). That function returns the same thing, - permitting calls to be chained. - - If called with only 1 arg, it has no side effects but returns a - func with the same signature as described above. - */ - const installMethod = function callee(tgt, name, func){ - if(!(tgt instanceof StructBinder.StructType)){ - toss("Usage error: target object is-not-a StructType."); - } - if(1===arguments.length){ - return (n,f)=>callee(tgt,n,f); - } - if(!callee.argcProxy){ - callee.argcProxy = function(func,sig){ - return function(...args){ - if(func.length!==arguments.length){ - toss("Argument mismatch. Native signature is:",sig); - } - return func.apply(this, args); - } - }; - callee.removeFuncList = function(){ - if(this.ondispose.__removeFuncList){ - this.ondispose.__removeFuncList.forEach( - (v,ndx)=>{ - if('number'===typeof v){ - try{wasm.uninstallFunction(v)} - catch(e){/*ignore*/} - } - /* else it's a descriptive label for the next number in - the list. */ - } - ); - delete this.ondispose.__removeFuncList; - } - }; - }/*static init*/ - const sigN = tgt.memberSignature(name); - if(sigN.length<2){ - toss("Member",name," is not a function pointer. Signature =",sigN); - } - const memKey = tgt.memberKey(name); - //log("installMethod",tgt, name, sigN); - const fProxy = 1 - // We can remove this proxy middle-man once the VFS is working - ? callee.argcProxy(func, sigN) - : func; - const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true)); - tgt[memKey] = pFunc; - if(!tgt.ondispose) tgt.ondispose = []; - if(!tgt.ondispose.__removeFuncList){ - tgt.ondispose.push('ondispose.__removeFuncList handler', - callee.removeFuncList); - tgt.ondispose.__removeFuncList = []; - } - tgt.ondispose.__removeFuncList.push(memKey, pFunc); - return (n,f)=>callee(tgt, n, f); - }/*installMethod*/; - - /** - Map of sqlite3_file pointers to OPFS handles. - */ - const __opfsHandles = Object.create(null); - - /** - Generates a random ASCII string len characters long, intended for - use as a temporary file name. - */ - const randomFilename = function f(len=16){ - if(!f._chars){ - f._chars = "abcdefghijklmnopqrstuvwxyz"+ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ - "012346789"; - f._n = f._chars.length; - } - const a = []; - let i = 0; - for( ; i < len; ++i){ - const ndx = Math.random() * (f._n * 64) % f._n | 0; - a[i] = f._chars[ndx]; - } - return a.join(''); - }; - - const rootDir = await navigator.storage.getDirectory(); - log("rootDir =",rootDir); - - //////////////////////////////////////////////////////////////////////// - // Set up OPFS VFS methods... - let inst = installMethod(oVfs); - inst('xOpen', function(pVfs, zName, pFile, flags, pOutFlags){ - const f = new sqlite3_file(pFile); - f.$pMethods = oIom.pointer; - __opfsHandles[pFile] = f; - f.opfsHandle = null /* TODO */; - if(flags & capi.SQLITE_OPEN_DELETEONCLOSE){ - f.deleteOnClose = true; - } - f.filename = zName ? wasm.cstringToJs(zName) : 'sqlite3-xOpen-'+randomFilename(); - error("OPFS sqlite3_vfs::xOpen is not yet full implemented."); - return capi.SQLITE_IOERR; - }) - ('xFullPathname', function(pVfs,zName,nOut,pOut){ - /* Until/unless we have some notion of "current dir" - in OPFS, simply copy zName to pOut... */ - const i = wasm.cstrncpy(pOut, zName, nOut); - return i<nOut ? 0 : capi.SQLITE_CANTOPEN - /*CANTOPEN is required by the docs but SQLITE_RANGE would be a closer match*/; - }) - ('xAccess', function(pVfs,zName,flags,pOut){ - error("OPFS sqlite3_vfs::xAccess is not yet implemented."); - let fileExists = 0; - switch(flags){ - case capi.SQLITE_ACCESS_EXISTS: break; - case capi.SQLITE_ACCESS_READWRITE: break; - case capi.SQLITE_ACCESS_READ/*docs say this is never used*/: - default: - error("Unexpected flags value for sqlite3_vfs::xAccess():",flags); - return capi.SQLITE_MISUSE; - } - wasm.setMemValue(pOut, fileExists, 'i32'); - return 0; - }) - ('xDelete', function(pVfs, zName, doSyncDir){ - error("OPFS sqlite3_vfs::xDelete is not yet implemented."); - // https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/modules/file_system_access/file_system_handle.idl - // ==> remove() - return capi.SQLITE_IOERR; - }) - ('xGetLastError', function(pVfs,nOut,pOut){ - debug("OPFS sqlite3_vfs::xGetLastError() has nothing sensible to return."); - return 0; - }) - ('xCurrentTime', function(pVfs,pOut){ - /* If it turns out that we need to adjust for timezone, see: - https://stackoverflow.com/a/11760121/1458521 */ - wasm.setMemValue(pOut, 2440587.5 + (new Date().getTime()/86400000), - 'double'); - return 0; - }) - ('xCurrentTimeInt64',function(pVfs,pOut){ - // TODO: confirm that this calculation is correct - wasm.setMemValue(pOut, (2440587.5 * 86400000) + new Date().getTime(), - 'i64'); - return 0; - }); - if(!oVfs.$xSleep){ - inst('xSleep', function(pVfs,ms){ - error("sqlite3_vfs::xSleep(",ms,") cannot be implemented from "+ - "JS and we have no default VFS to copy the impl from."); - return 0; - }); - } - if(!oVfs.$xRandomness){ - inst('xRandomness', function(pVfs, nOut, pOut){ - const heap = wasm.heap8u(); - let i = 0; - for(; i < nOut; ++i) heap[pOut + i] = (Math.random()*255000) & 0xFF; - return i; - }); - } - - //////////////////////////////////////////////////////////////////////// - // Set up OPFS sqlite3_io_methods... - inst = installMethod(oIom); - inst('xClose', async function(pFile){ - warn("xClose(",arguments,") uses await"); - const f = __opfsHandles[pFile]; - delete __opfsHandles[pFile]; - if(f.opfsHandle){ - await f.opfsHandle.close(); - if(f.deleteOnClose){ - // TODO - } - } - f.dispose(); - return 0; - }) - ('xRead', /*i(ppij)*/function(pFile,pDest,n,offset){ - /* int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst) */ - try { - const f = __opfsHandles[pFile]; - const heap = wasm.heap8u(); - const b = new Uint8Array(heap.buffer, pDest, n); - const nRead = f.opfsHandle.read(b, {at: offset}); - if(nRead<n){ - // MUST zero-fill short reads (per the docs) - heap.fill(0, dest + nRead, n - nRead); - } - return 0; - }catch(e){ - error("xRead(",arguments,") failed:",e); - return capi.SQLITE_IOERR_READ; - } - }) - ('xWrite', /*i(ppij)*/function(pFile,pSrc,n,offset){ - /* int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst) */ - try { - const f = __opfsHandles[pFile]; - const b = new Uint8Array(wasm.heap8u().buffer, pSrc, n); - const nOut = f.opfsHandle.write(b, {at: offset}); - if(nOut<n){ - error("xWrite(",arguments,") short write!"); - return capi.SQLITE_IOERR_WRITE; - } - return 0; - }catch(e){ - error("xWrite(",arguments,") failed:",e); - return capi.SQLITE_IOERR_WRITE; - } - }) - ('xTruncate', /*i(pj)*/async function(pFile,sz){ - /* int (*xTruncate)(sqlite3_file*, sqlite3_int64 size) */ - try{ - warn("xTruncate(",arguments,") uses await"); - const f = __opfsHandles[pFile]; - await f.opfsHandle.truncate(sz); - return 0; - } - catch(e){ - error("xTruncate(",arguments,") failed:",e); - return capi.SQLITE_IOERR_TRUNCATE; - } - }) - ('xSync', /*i(pi)*/async function(pFile,flags){ - /* int (*xSync)(sqlite3_file*, int flags) */ - try { - warn("xSync(",arguments,") uses await"); - const f = __opfsHandles[pFile]; - await f.opfsHandle.flush(); - return 0; - }catch(e){ - error("xSync(",arguments,") failed:",e); - return capi.SQLITE_IOERR_SYNC; - } - }) - ('xFileSize', /*i(pp)*/async function(pFile,pSz){ - /* int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize) */ - try { - warn("xFileSize(",arguments,") uses await"); - const f = __opfsHandles[pFile]; - const fsz = await f.opfsHandle.getSize(); - capi.wasm.setMemValue(pSz, fsz,'i64'); - return 0; - }catch(e){ - error("xFileSize(",arguments,") failed:",e); - return capi.SQLITE_IOERR_SEEK; - } - }) - ('xLock', /*i(pi)*/function(pFile,lockType){ - /* int (*xLock)(sqlite3_file*, int) */ - // Opening a handle locks it automatically. - warn("xLock(",arguments,") is a no-op"); - return 0; - }) - ('xUnlock', /*i(pi)*/function(pFile,lockType){ - /* int (*xUnlock)(sqlite3_file*, int) */ - // Opening a handle locks it automatically. - warn("xUnlock(",arguments,") is a no-op"); - return 0; - }) - ('xCheckReservedLock', /*i(pp)*/function(pFile,pOut){ - /* int (*xCheckReservedLock)(sqlite3_file*, int *pResOut) */ - // Exclusive lock is automatically acquired when opened - warn("xCheckReservedLock(",arguments,") is a no-op"); - wasm.setMemValue(pOut,1,'i32'); - return 0; - }) - ('xFileControl', /*i(pip)*/function(pFile,op,pArg){ - /* int (*xFileControl)(sqlite3_file*, int op, void *pArg) */ - debug("xFileControl(",arguments,") is a no-op"); - return capi.SQLITE_NOTFOUND; - }) - ('xDeviceCharacteristics',/*i(p)*/function(pFile){ - /* int (*xDeviceCharacteristics)(sqlite3_file*) */ - debug("xDeviceCharacteristics(",pFile,")"); - return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; - }); - // xSectorSize may be NULL - //('xSectorSize', function(pFile){ - // /* int (*xSectorSize)(sqlite3_file*) */ - // log("xSectorSize(",pFile,")"); - // return 4096 /* ==> SQLITE_DEFAULT_SECTOR_SIZE */; - //}) - - const rc = capi.sqlite3_vfs_register(oVfs.pointer, 0); - if(rc){ - oVfs.dispose(); - toss("sqlite3_vfs_register(OPFS) failed with rc",rc); - } - capi.sqlite3_vfs_register.addReference(oVfs, oIom); - warn("End of (very incomplete) OPFS setup.", oVfs); - //oVfs.dispose()/*only because we can't yet do anything with it*/; - -}/*initOpfsBits()*/; - -(async function(){ - importScripts('sqlite3.js'); - - const test1 = function(db){ - db.exec("create table if not exists t(a);") - .transaction(function(db){ - db.prepare("insert into t(a) values(?)") - .bind(new Date().getTime()) - .stepFinalize(); - stdout("Number of values in table t:", - db.selectValue("select count(*) from t")); - }); - }; - - const runTests = async function(Module){ - //stdout("Module",Module); - self._MODULE = Module /* this is only to facilitate testing from the console */; - const sqlite3 = Module.sqlite3, - capi = sqlite3.capi, - oo = sqlite3.oo1, - wasm = capi.wasm; - stdout("Loaded sqlite3:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); - - if(1){ - let errCount = 0; - [ - 'FileSystemHandle', 'FileSystemFileHandle', 'FileSystemDirectoryHandle', - 'FileSystemSyncAccessHandle' - ].forEach(function(n){ - const f = self[n]; - if(f){ - warn(n,f); - warn(n+'.prototype',f.prototype); - }else{ - stderr("MISSING",n); - ++errCount; - } - }); - if(errCount) return; - } - warn('self',self); - await initOpfsBits(sqlite3); - - if(1) return; - - let persistentDir; - if(1){ - persistentDir = ''; - }else{ - persistentDir = capi.sqlite3_web_persistent_dir(); - if(persistentDir){ - stderr("Persistent storage dir:",persistentDir); - }else{ - stderr("No persistent storage available."); - return; - } - } - const startTime = performance.now(); - let db; - try { - db = new oo.DB(persistentDir+'/foo.db'); - stdout("DB filename:",db.filename,db.fileName()); - const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>', - banner2 = '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'; - [ - test1 - ].forEach((f)=>{ - const n = performance.now(); - stdout(banner1,"Running",f.name+"()..."); - f(db, sqlite3, Module); - stdout(banner2,f.name+"() took ",(performance.now() - n),"ms"); - }); - }finally{ - if(db) db.close(); - } - stdout("Total test time:",(performance.now() - startTime),"ms"); - }; - - sqlite3InitModule(self.sqlite3TestModule).then(runTests); -})(); |