]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
OPFS VFS: further internal refactoring towards experimenting with a new comms model.
authorstephan <stephan@noemail.net>
Tue, 20 Sep 2022 03:31:02 +0000 (03:31 +0000)
committerstephan <stephan@noemail.net>
Tue, 20 Sep 2022 03:31:02 +0000 (03:31 +0000)
FossilOrigin-Name: 5ca412ced24b4e3af5f467e710a597ed440badf7b8335346aade11d3cad3d1a1

ext/wasm/api/sqlite3-api-opfs.js
ext/wasm/sqlite3-opfs-async-proxy.js
manifest
manifest.uuid

index ffc7669a7f4585bdefeaf4e6b3a10bf2db5e8012..c6b38fa93ee0e87dbae8afb84f387b8fe46198f3 100644 (file)
@@ -128,7 +128,7 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
       // failure is, e.g., that the remote script is 404.
       promiseReject(new Error("Loading OPFS async Worker failed for unknown reasons."));
     };
-    const wMsg = (type,payload)=>W.postMessage({type,payload});
+    const wMsg = (type,args)=>W.postMessage({type,args});
     /**
        Generic utilities for working with OPFS. This will get filled out
        by the Promise setup and, on success, installed as sqlite3.opfs.
@@ -186,36 +186,48 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
        require a bit of acrobatics but should be feasible.
     */
     const state = Object.create(null);
+    state.littleEndian = true;
     state.verbose = options.verbose;
     state.fileBufferSize =
       1024 * 64 /* size of aFileHandle.sab. 64k = max sqlite3 page
                    size. */;
-    state.sabOffsetS11n = state.fileBufferSize;
+    state.sabS11nOffset = state.fileBufferSize;
+    state.sabS11nSize = 2048;
     state.sabIO = new SharedArrayBuffer(
       state.fileBufferSize
-        + 4096/* arg/result serialization */
-        + 8 /* to be removed - porting crutch */
+        + state.sabS11nSize/* arg/result serialization block */
     );
-    state.fbInt64Offset =
-      state.sabIO.byteLength - 8 /*spot in fileHandle.sab to store an int64 result.
-                                  to be removed. Porting crutch. */;
     state.opIds = Object.create(null);
+    state.rcIds = Object.create(null);
     const metrics = Object.create(null);
     {
       let i = 0;
+      state.opIds.whichOp = i++;
       state.opIds.nothing = i++;
       state.opIds.xAccess = i++;
+      state.rcIds.xAccess = i++;
       state.opIds.xClose = i++;
+      state.rcIds.xClose = i++;
       state.opIds.xDelete = i++;
+      state.rcIds.xDelete = i++;
       state.opIds.xDeleteNoWait = i++;
+      state.rcIds.xDeleteNoWait = i++;
       state.opIds.xFileSize = i++;
+      state.rcIds.xFileSize = i++;
       state.opIds.xOpen = i++;
+      state.rcIds.xOpen = i++;
       state.opIds.xRead = i++;
+      state.rcIds.xRead = i++;
       state.opIds.xSleep = i++;
+      state.rcIds.xSleep = i++;
       state.opIds.xSync = i++;
+      state.rcIds.xSync = i++;
       state.opIds.xTruncate = i++;
+      state.rcIds.xTruncate = i++;
       state.opIds.xWrite = i++;
+      state.rcIds.xWrite = i++;
       state.opIds.mkdir = i++;
+      state.rcIds.mkdir = i++;
       state.sabOP = new SharedArrayBuffer(i * 4/*sizeof int32*/);
       state.opIds.xFileControl = state.opIds.xSync /* special case */;
       opfsUtil.metrics.reset();
@@ -247,7 +259,7 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
        must be a single object or primitive value, depending on the
        given operation's signature in the async API counterpart.
     */
-    const opRun = (op,args)=>{
+    const opRun = (op,...args)=>{
       const t = performance.now();
       Atomics.store(state.sabOPView, state.opIds[op], -1);
       wMsg(op, args);
@@ -256,6 +268,44 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
       return Atomics.load(state.sabOPView, state.opIds[op]);
     };
 
+    const initS11n = ()=>{
+      // Achtung: this code is 100% duplicated in the other half of this proxy!
+      if(state.s11n) return state.s11n;
+      const jsonDecoder = new TextDecoder(),
+            jsonEncoder = new TextEncoder('utf-8'),
+            viewSz = new DataView(state.sabIO, state.sabS11nOffset, 4),
+            viewJson = new Uint8Array(state.sabIO, state.sabS11nOffset+4, state.sabS11nSize-4);
+      state.s11n = Object.create(null);
+      /**
+         Returns an array of the state serialized by the most recent
+         serialize() operation (here or in the counterpart thread), or
+         null if the serialization buffer is empty.
+      */
+      state.s11n.deserialize = function(){
+        const sz = viewSz.getInt32(0, state.littleEndian);
+        const json = sz ? jsonDecoder.decode(
+          viewJson.slice(0, sz)
+          /* slice() (copy) needed, instead of subarray() (reference),
+             because TextDecoder throws if asked to decode from an
+             SAB. */
+        ) : null;
+        return JSON.parse(json);
+      }
+      /**
+         Serializes all arguments to the shared buffer for consumption
+         by the counterpart thread. This impl currently uses JSON for
+         serialization for simplicy of implementation, but if that
+         proves imperformant then a lower-level approach will be
+         created.
+      */
+      state.s11n.serialize = function(...args){
+        const json = jsonEncoder.encode(JSON.stringify(args));
+        viewSz.setInt32(0, json.byteLength, state.littleEndian);
+        viewJson.set(json);
+      };
+      return state.s11n;
+    };
+
     /**
        Generates a random ASCII string len characters long, intended for
        use as a temporary file name.
@@ -425,7 +475,7 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
       xFileControl: function(pFile, opId, pArg){
         mTimeStart('xFileControl');
         const rc = (capi.SQLITE_FCNTL_SYNC===opId)
-              ? opRun('xSync', {fid:pFile, flags:0})
+              ? opRun('xSync', pFile, 0)
               : capi.SQLITE_NOTFOUND;
         mTimeEnd();
         return rc;
@@ -434,10 +484,8 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
         mTimeStart('xFileSize');
         const rc = opRun('xFileSize', pFile);
         if(!isWorkerErrCode(rc)){
-          wasm.setMemValue(
-            pSz64, __openFiles[pFile].sabViewFileSize.getBigInt64(0,true),
-            'i64'
-          );
+          const sz = state.s11n.deserialize()[0];
+          wasm.setMemValue(pSz64, BigInt(sz), 'i64');
         }
         mTimeEnd();
         return rc;
@@ -447,14 +495,13 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
         //warn("xLock(",arguments,") is a no-op");
         return 0;
       },
-      xRead: function(pFile,pDest,n,offset){
+      xRead: function(pFile,pDest,n,offset64){
         /* int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst) */
         mTimeStart('xRead');
         const f = __openFiles[pFile];
         let rc;
         try {
-          // FIXME(?): block until we finish copying the xRead result buffer. How?
-          rc = opRun('xRead',{fid:pFile, n, offset});
+          rc = opRun('xRead',pFile, n, Number(offset64));
           if(0===rc || capi.SQLITE_IOERR_SHORT_READ===rc){
             // set() seems to be the fastest way to copy this...
             wasm.heap8u().set(f.sabView.subarray(0, n), pDest);
@@ -468,11 +515,11 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
       },
       xSync: function(pFile,flags){
         ++metrics.xSync.count;
-        return 0; // impl'd in xFileControl(). opRun('xSync', {fid:pFile, flags});
+        return 0; // impl'd in xFileControl()
       },
       xTruncate: function(pFile,sz64){
         mTimeStart('xTruncate');
-        const rc = opRun('xTruncate', {fid:pFile, size: sz64});
+        const rc = opRun('xTruncate', pFile, Number(sz64));
         mTimeEnd();
         return rc;
       },
@@ -481,15 +528,14 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
         //warn("xUnlock(",arguments,") is a no-op");
         return 0;
       },
-      xWrite: function(pFile,pSrc,n,offset){
+      xWrite: function(pFile,pSrc,n,offset64){
         /* int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst) */
         mTimeStart('xWrite');
         const f = __openFiles[pFile];
         let rc;
         try {
-          // FIXME(?): block from here until we finish the xWrite. How?
           f.sabView.set(wasm.heap8u().subarray(pSrc, pSrc+n));
-          rc = opRun('xWrite',{fid:pFile, n, offset});
+          rc = opRun('xWrite', pFile, n, Number(offset64));
         }catch(e){
           error("xWrite(",arguments,") failed:",e,f);
           rc = capi.SQLITE_IOERR_WRITE;
@@ -527,7 +573,7 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
       },
       xDelete: function(pVfs, zName, doSyncDir){
         mTimeStart('xDelete');
-        opRun('xDelete', {filename: wasm.cstringToJs(zName), syncDir: doSyncDir});
+        opRun('xDelete', wasm.cstringToJs(zName), doSyncDir, false);
         /* We're ignoring errors because we cannot yet differentiate
            between harmless and non-harmless failures. */
         mTimeEnd();
@@ -577,24 +623,22 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
         }else if('number'===typeof zName){
           zName = wasm.cstringToJs(zName);
         }
-        const args = Object.create(null);
-        args.fid = pFile;
-        args.filename = zName;
-        args.sab = new SharedArrayBuffer(state.fileBufferSize);
-        args.flags = flags;
-        const rc = opRun('xOpen', args);
+        const fh = Object.create(null);
+        fh.fid = pFile;
+        fh.filename = zName;
+        fh.sab = new SharedArrayBuffer(state.fileBufferSize);
+        fh.flags = flags;
+        const rc = opRun('xOpen', pFile, zName, flags);
         if(!rc){
           /* Recall that sqlite3_vfs::xClose() will be called, even on
              error, unless pFile->pMethods is NULL. */
-          if(args.readOnly){
+          if(fh.readOnly){
             wasm.setMemValue(pOutFlags, capi.SQLITE_OPEN_READONLY, 'i32');
           }
-          __openFiles[pFile] = args;
-          args.sabView = new Uint8Array(state.sabIO, 0, state.fileBufferSize);
-          args.sabViewFileSize = new DataView(state.sabIO, state.fbInt64Offset, 8);
-          args.sq3File = new sqlite3_file(pFile);
-          args.sq3File.$pMethods = opfsIoMethods.pointer;
-          args.ba = new Uint8Array(args.sab);
+          __openFiles[pFile] = fh;
+          fh.sabView = state.sabFileBufView;
+          fh.sq3File = new sqlite3_file(pFile);
+          fh.sq3File.$pMethods = opfsIoMethods.pointer;
         }
         mTimeEnd();
         return rc;
@@ -640,13 +684,17 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
        but cannot report the nature of the failure.
     */
     opfsUtil.deleteEntry = function(fsEntryName,recursive=false){
-      return 0===opRun('xDelete', {filename:fsEntryName, recursive});
+      return 0===opRun('xDelete', fsEntryName, 0, recursive);
     };
     /**
-       Exactly like deleteEntry() but runs asynchronously.
+       Exactly like deleteEntry() but runs asynchronously. This is a
+       "fire and forget" operation: it does not return a promise
+       because the counterpart operation happens in another thread and
+       waiting on that result in a Promise would block the OPFS VFS
+       from acting until it completed.
     */
-    opfsUtil.deleteEntryAsync = async function(fsEntryName,recursive=false){
-      wMsg('xDeleteNoWait', {filename: fsEntryName, recursive});
+    opfsUtil.deleteEntryAsync = function(fsEntryName,recursive=false){
+      wMsg('xDeleteNoWait', [fsEntryName, 0, recursive]);
     };
     /**
        Synchronously creates the given directory name, recursively, in
@@ -753,11 +801,11 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
     W.onmessage = function({data}){
       //log("Worker.onmessage:",data);
       switch(data.type){
-          case 'loaded':
+          case 'opfs-async-loaded':
             /*Pass our config and shared state on to the async worker.*/
-            wMsg('init',state);
+            wMsg('opfs-async-init',state);
             break;
-          case 'inited':{
+          case 'opfs-async-inited':{
             /*Indicates that the async partner has received the 'init',
               so we now know that the state object is no longer subject to
               being copied by a pending postMessage() call.*/
@@ -772,7 +820,9 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
               }
               capi.sqlite3_vfs_register.addReference(opfsVfs, opfsIoMethods);
               state.sabOPView = new Int32Array(state.sabOP);
-              state.sabFileBufView = new Uint8Array(state.sabFileBufView, 0, state.fileBufferSize);
+              state.sabFileBufView = new Uint8Array(state.sabIO, 0, state.fileBufferSize);
+              state.sabS11nView = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize);
+              initS11n();
               if(options.sanityChecks){
                 warn("Running sanity checks because of opfs-sanity-check URL arg...");
                 sanityCheck();
index a1ed4362d3abcdc7b5215387e490767fecfc9ffa..19d71512c95d121ea31f715494b71cf753ae0d25 100644 (file)
@@ -220,7 +220,7 @@ const vfsAsyncImpls = {
     storeAndNotify('xDelete', rc);
     mTimeEnd();
   },
-  xDeleteNoWait: async function({filename, syncDir, recursive = false}){
+  xDeleteNoWait: async function(filename, syncDir = 0, recursive = false){
     /* The syncDir flag is, for purposes of the VFS API's semantics,
        ignored here. However, if it has the value 0x1234 then: after
        deleting the given file, recursively try to delete any empty
@@ -261,10 +261,7 @@ const vfsAsyncImpls = {
     let sz;
     try{
       sz = await fh.accessHandle.getSize();
-      if(!fh.sabViewFileSize){
-        fh.sabViewFileSize = new DataView(state.sabIO,state.fbInt64Offset,8);
-      }
-      fh.sabViewFileSize.setBigInt64(0, BigInt(sz), true);
+      state.s11n.serialize(Number(sz));
       sz = 0;
     }catch(e){
       error("xFileSize():",e, fh);
@@ -273,11 +270,7 @@ const vfsAsyncImpls = {
     storeAndNotify('xFileSize', sz);
     mTimeEnd();
   },
-  xOpen: async function({
-    fid/*sqlite3_file pointer*/,
-    filename,
-    flags
-  }){
+  xOpen: async function(fid/*sqlite3_file pointer*/, filename, flags){
     const opName = 'xOpen';
     mTimeStart(opName);
     log(opName+"(",arguments[0],")");
@@ -316,7 +309,7 @@ const vfsAsyncImpls = {
     }
     mTimeEnd();
   },
-  xRead: async function({fid,n,offset}){
+  xRead: async function(fid,n,offset){
     mTimeStart('xRead');
     log("xRead(",arguments[0],")");
     let rc = 0;
@@ -337,7 +330,7 @@ const vfsAsyncImpls = {
     storeAndNotify('xRead',rc);
     mTimeEnd();
   },
-  xSync: async function({fid,flags/*ignored*/}){
+  xSync: async function(fid,flags/*ignored*/){
     mTimeStart('xSync');
     log("xSync(",arguments[0],")");
     const fh = __openFiles[fid];
@@ -345,14 +338,14 @@ const vfsAsyncImpls = {
     storeAndNotify('xSync',0);
     mTimeEnd();
   },
-  xTruncate: async function({fid,size}){
+  xTruncate: async function(fid,size){
     mTimeStart('xTruncate');
     log("xTruncate(",arguments[0],")");
     let rc = 0;
     const fh = __openFiles[fid];
     try{
       affirmNotRO('xTruncate', fh);
-      await fh.accessHandle.truncate(Number(size));
+      await fh.accessHandle.truncate(size);
     }catch(e){
       error("xTruncate():",e,fh);
       rc = state.sq3Codes.SQLITE_IOERR_TRUNCATE;
@@ -360,7 +353,7 @@ const vfsAsyncImpls = {
     storeAndNotify('xTruncate',rc);
     mTimeEnd();
   },
-  xWrite: async function({fid,n,offset}){
+  xWrite: async function(fid,n,offset){
     mTimeStart('xWrite');
     log("xWrite(",arguments[0],")");
     let rc;
@@ -380,6 +373,70 @@ const vfsAsyncImpls = {
   }
 };
 
+
+const initS11n = ()=>{
+  // Achtung: this code is 100% duplicated in the other half of this proxy!
+  if(state.s11n) return state.s11n;
+  const jsonDecoder = new TextDecoder(),
+        jsonEncoder = new TextEncoder('utf-8'),
+        viewSz = new DataView(state.sabIO, state.sabS11nOffset, 4),
+        viewJson = new Uint8Array(state.sabIO, state.sabS11nOffset+4, state.sabS11nSize-4);
+  state.s11n = Object.create(null);
+  /**
+     Returns an array of the state serialized by the most recent
+     serialize() operation (here or in the counterpart thread), or
+     null if the serialization buffer is empty.
+  */
+  state.s11n.deserialize = function(){
+    const sz = viewSz.getInt32(0, state.littleEndian);
+    const json = sz ? jsonDecoder.decode(
+      viewJson.slice(0, sz)
+      /* slice() (copy) needed, instead of subarray() (reference),
+         because TextDecoder throws if asked to decode from an
+         SAB. */
+    ) : null;
+    return JSON.parse(json);
+  }
+  /**
+     Serializes all arguments to the shared buffer for consumption
+     by the counterpart thread. This impl currently uses JSON for
+     serialization for simplicy of implementation, but if that
+     proves imperformant then a lower-level approach will be
+     created.
+  */
+  state.s11n.serialize = function(...args){
+    const json = jsonEncoder.encode(JSON.stringify(args));
+    viewSz.setInt32(0, json.byteLength, state.littleEndian);
+    viewJson.set(json);
+  };
+  return state.s11n;
+};
+
+const waitLoop = function(){
+  const opHandlers = Object.create(null);
+  for(let k of Object.keys(state.opIds)){
+    const o = Object.create(null);
+    opHandlers[state.opIds[k]] = o;
+    o.key = k;
+  }
+  const sabOP = state.sabOP;
+  for(;;){
+    try {
+      Atomics.store(sabOP, state.opIds.whichOp, 0);
+      Atomic.wait(sabOP, state.opIds.whichOp);
+      const opId = Atomics.load(sabOP, state.opIds.whichOp);
+      const hnd = opHandlers[opId] ?? toss("No waitLoop handler for whichOp #",opId);
+      const args = state.s11n.deserialize();
+      log("whichOp =",opId,hnd,args);
+      const rc = 0/*TODO: run op*/;
+      Atomics.store(sabOP, state.rcIds[hnd.key], rc);
+      Atomics.notify(sabOP, state.rcIds[hnd.key]);
+    }catch(e){
+      error('in waitLoop():',e.message);
+    }
+  }
+};
+
 navigator.storage.getDirectory().then(function(d){
   const wMsg = (type)=>postMessage({type});
   state.rootDir = d;
@@ -387,33 +444,38 @@ navigator.storage.getDirectory().then(function(d){
   self.onmessage = async function({data}){
     log("self.onmessage()",data);
     switch(data.type){
-        case 'init':{
+        case 'opfs-async-init':{
           /* Receive shared state from synchronous partner */
-          const opt = data.payload;
+          const opt = data.args;
+          state.littleEndian = opt.littleEndian;
           state.verbose = opt.verbose ?? 2;
           state.fileBufferSize = opt.fileBufferSize;
-          state.fbInt64Offset = opt.fbInt64Offset;
+          state.sabS11nOffset = opt.sabS11nOffset;
+          state.sabS11nSize = opt.sabS11nSize;
           state.sabOP = opt.sabOP;
           state.sabOPView = new Int32Array(state.sabOP);
           state.sabIO = opt.sabIO;
           state.sabFileBufView = new Uint8Array(state.sabIO, 0, state.fileBufferSize);
+          state.sabS11nView = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize);
           state.opIds = opt.opIds;
+          state.rcIds = opt.rcIds;
           state.sq3Codes = opt.sq3Codes;
           Object.keys(vfsAsyncImpls).forEach((k)=>{
             if(!Number.isFinite(state.opIds[k])){
               toss("Maintenance required: missing state.opIds[",k,"]");
             }
           });
+          initS11n();
           metrics.reset();
           log("init state",state);
-          wMsg('inited');
+          wMsg('opfs-async-inited');
           break;
         }
         default:{
           let err;
           const m = vfsAsyncImpls[data.type] || toss("Unknown message type:",data.type);
           try {
-            await m(data.payload).catch((e)=>err=e);
+            await m(...data.args).catch((e)=>err=e);
           }catch(e){
             err = e;
           }
@@ -425,5 +487,5 @@ navigator.storage.getDirectory().then(function(d){
         }
     }
   };
-  wMsg('loaded');
+  wMsg('opfs-async-loaded');
 }).catch((e)=>error(e));
index 00518b64c82905d2714172b62beba17f938b865c..13980df6d3dc5d9c1b65eb233e52ae30df445985 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C OPFS\sVFS:\smoved\si/o\sbuffer\sfrom\sper-file\sto\sthe\sVFS,\sand\srelated\srefactoring,\sin\sprep\sfor\sexperimentation\swith\sa\snew\sinter-worker\scomms\smodel.
-D 2022-09-20T01:28:47.162
+C OPFS\sVFS:\sfurther\sinternal\srefactoring\stowards\sexperimenting\swith\sa\snew\scomms\smodel.
+D 2022-09-20T03:31:02.139
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -484,7 +484,7 @@ F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a
 F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77
 F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30
 F ext/wasm/api/sqlite3-api-oo1.js 2d13dddf0d2b4168a9249f124134d37924331e5b55e05dba18b6d661fbeefe48
-F ext/wasm/api/sqlite3-api-opfs.js f8027fb4af1c24fcfad31889f8a5ccfa3b96d6e812d3495b13833bef57034046
+F ext/wasm/api/sqlite3-api-opfs.js 351459d571166ff4cebaccd6b8aad2b0fe5eac54a8c777ba52c31c931a3eb2e2
 F ext/wasm/api/sqlite3-api-prologue.js 0d2639387b94c30f492d4aea6e44fb7b16720808678464559458fd2ae3759655
 F ext/wasm/api/sqlite3-api-worker1.js ee4cf149cbacb63d06b536674f822aa5088b7e022cdffc69f1f36cebe2f9fea0
 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
@@ -519,7 +519,7 @@ F ext/wasm/speedtest1.html 512addeb3c27c94901178b7bcbde83a6f95c093f9ebe16a2959a0
 F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
 F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0
 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5
-F ext/wasm/sqlite3-opfs-async-proxy.js 7ebc36915cd61bd4f067a4823307f4d4eb2678a173aaae470c534e8fe9cda650
+F ext/wasm/sqlite3-opfs-async-proxy.js 462081970a6a46d9b2c386474aacad2d81e6629bb554d6cad5c58515f08c8a38
 F ext/wasm/sqlite3-worker1-promiser.js 4fd0465688a28a75f1d4ee4406540ba494f49844e3cad0670d0437a001943365
 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e
 F ext/wasm/test-opfs-vfs.html eb69dda21eb414b8f5e3f7c1cc0f774103cc9c0f87b2d28a33419e778abfbab5
@@ -2026,8 +2026,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P d1f1fe6f1c60640f7770dfb9245c459a09b8d24ec2ddf664dff77c810bd51f96
-R e5a1ff53ebe902985ab8e87bba2c71fb
+P d4d63e4580ad8d497310608175308c03c517e051d7865cb66aa0b10356612d7d
+R 7fef73d3d6085edd0d87db08b80a36c1
 U stephan
-Z d05962442428c5dae722ab00dca47e48
+Z d9dc6ab88cc2471cfc1f140af8fec063
 # Remove this line to create a well-formed Fossil manifest.
index 8d4f713497cedb50ab892059b0c7621b010ebb3a..b2152187c4f2689f4f2530c09cd155a87013b4ab 100644 (file)
@@ -1 +1 @@
-d4d63e4580ad8d497310608175308c03c517e051d7865cb66aa0b10356612d7d
\ No newline at end of file
+5ca412ced24b4e3af5f467e710a597ed440badf7b8335346aade11d3cad3d1a1
\ No newline at end of file