10544 lines
362 KiB
JavaScript
10544 lines
362 KiB
JavaScript
/* @preserve
|
|
**
|
|
** LICENSE for the sqlite3 WebAssembly/JavaScript APIs.
|
|
**
|
|
** This bundle (typically released as sqlite3.js or sqlite3.mjs)
|
|
** is an amalgamation of JavaScript source code from two projects:
|
|
**
|
|
** 1) https://emscripten.org: the Emscripten "glue code" is covered by
|
|
** the terms of the MIT license and University of Illinois/NCSA
|
|
** Open Source License, as described at:
|
|
**
|
|
** https://emscripten.org/docs/introducing_emscripten/emscripten_license.html
|
|
**
|
|
** 2) https://sqlite.org: all code and documentation labeled as being
|
|
** from this source are released under the same terms as the sqlite3
|
|
** C library:
|
|
**
|
|
** 2022-10-16
|
|
**
|
|
** 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.
|
|
*/
|
|
/* @preserve
|
|
** This code was built from sqlite3 version...
|
|
**
|
|
** SQLITE_VERSION "3.52.0"
|
|
** SQLITE_VERSION_NUMBER 3052000
|
|
** SQLITE_SOURCE_ID "2026-01-30 06:37:34 407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e"
|
|
**
|
|
** Emscripten SDK: 5.0.0
|
|
*/
|
|
async function sqlite3InitModule(moduleArg = {}) {
|
|
var moduleRtn;
|
|
var Module = moduleArg;
|
|
var ENVIRONMENT_IS_WEB = !!globalThis.window;
|
|
var ENVIRONMENT_IS_WORKER = !!globalThis.WorkerGlobalScope;
|
|
globalThis.process?.versions?.node && globalThis.process?.type;
|
|
(function(Module) {
|
|
const sIMS = globalThis.sqlite3InitModuleState || Object.assign(Object.create(null), { debugModule: function() {
|
|
console.warn("globalThis.sqlite3InitModuleState is missing", arguments);
|
|
} });
|
|
delete globalThis.sqlite3InitModuleState;
|
|
sIMS.debugModule("pre-js.js sqlite3InitModuleState =", sIMS);
|
|
Module["locateFile"] = function(path, prefix) {
|
|
if (this.emscriptenLocateFile instanceof Function) return this.emscriptenLocateFile(path, prefix);
|
|
return new URL(path, import.meta.url).href;
|
|
}.bind(sIMS);
|
|
Module["instantiateWasm"] = function callee(imports, onSuccess) {
|
|
if (this.emscriptenInstantiateWasm instanceof Function) return this.emscriptenInstantiateWasm(imports, onSuccess);
|
|
const sims = this;
|
|
const uri = Module.locateFile(sims.wasmFilename, "undefined" === typeof scriptDirectory ? "" : scriptDirectory);
|
|
sims.debugModule("instantiateWasm() uri =", uri, "sIMS =", this);
|
|
const wfetch = () => fetch(uri, { credentials: "same-origin" });
|
|
const finalThen = (arg) => {
|
|
arg.imports = imports;
|
|
sims.instantiateWasm = arg;
|
|
onSuccess(arg.instance, arg.module);
|
|
};
|
|
return (WebAssembly.instantiateStreaming ? async () => WebAssembly.instantiateStreaming(wfetch(), imports).then(finalThen) : async () => wfetch().then((response) => response.arrayBuffer()).then((bytes) => WebAssembly.instantiate(bytes, imports)).then(finalThen))();
|
|
}.bind(sIMS);
|
|
})(Module);
|
|
var thisProgram = "./this.program";
|
|
var _scriptName = import.meta.url;
|
|
var scriptDirectory = "";
|
|
function locateFile(path) {
|
|
if (Module["locateFile"]) return Module["locateFile"](path, scriptDirectory);
|
|
return scriptDirectory + path;
|
|
}
|
|
var readAsync, readBinary;
|
|
if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
|
|
try {
|
|
scriptDirectory = new URL(".", _scriptName).href;
|
|
} catch {}
|
|
if (ENVIRONMENT_IS_WORKER) readBinary = (url) => {
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.open("GET", url, false);
|
|
xhr.responseType = "arraybuffer";
|
|
xhr.send(null);
|
|
return new Uint8Array(xhr.response);
|
|
};
|
|
readAsync = async (url) => {
|
|
var response = await fetch(url, { credentials: "same-origin" });
|
|
if (response.ok) return response.arrayBuffer();
|
|
throw new Error(response.status + " : " + response.url);
|
|
};
|
|
}
|
|
var out = console.log.bind(console);
|
|
var err = console.error.bind(console);
|
|
var wasmBinary;
|
|
var ABORT = false, readyPromiseResolve, readyPromiseReject, HEAP8, HEAPU8, HEAP16, HEAP32, HEAPU32, HEAP64;
|
|
var runtimeInitialized = false;
|
|
function updateMemoryViews() {
|
|
var b = wasmMemory.buffer;
|
|
HEAP8 = new Int8Array(b);
|
|
HEAP16 = new Int16Array(b);
|
|
HEAPU8 = new Uint8Array(b);
|
|
new Uint16Array(b);
|
|
HEAP32 = new Int32Array(b);
|
|
HEAPU32 = new Uint32Array(b);
|
|
new Float32Array(b);
|
|
new Float64Array(b);
|
|
HEAP64 = new BigInt64Array(b);
|
|
new BigUint64Array(b);
|
|
}
|
|
function initMemory() {
|
|
if (Module["wasmMemory"]) wasmMemory = Module["wasmMemory"];
|
|
else {
|
|
var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 8388608;
|
|
wasmMemory = new WebAssembly.Memory({
|
|
"initial": INITIAL_MEMORY / 65536,
|
|
"maximum": 32768
|
|
});
|
|
}
|
|
updateMemoryViews();
|
|
}
|
|
function preRun() {
|
|
if (Module["preRun"]) {
|
|
if (typeof Module["preRun"] == "function") Module["preRun"] = [Module["preRun"]];
|
|
while (Module["preRun"].length) addOnPreRun(Module["preRun"].shift());
|
|
}
|
|
callRuntimeCallbacks(onPreRuns);
|
|
}
|
|
function initRuntime() {
|
|
runtimeInitialized = true;
|
|
if (!Module["noFSInit"] && !FS.initialized) FS.init();
|
|
TTY.init();
|
|
wasmExports["__wasm_call_ctors"]();
|
|
FS.ignorePermissions = false;
|
|
}
|
|
function postRun() {
|
|
if (Module["postRun"]) {
|
|
if (typeof Module["postRun"] == "function") Module["postRun"] = [Module["postRun"]];
|
|
while (Module["postRun"].length) addOnPostRun(Module["postRun"].shift());
|
|
}
|
|
callRuntimeCallbacks(onPostRuns);
|
|
}
|
|
function abort(what) {
|
|
Module["onAbort"]?.(what);
|
|
what = "Aborted(" + what + ")";
|
|
err(what);
|
|
ABORT = true;
|
|
what += ". Build with -sASSERTIONS for more info.";
|
|
var e = new WebAssembly.RuntimeError(what);
|
|
readyPromiseReject?.(e);
|
|
throw e;
|
|
}
|
|
var wasmBinaryFile;
|
|
function findWasmBinary() {
|
|
if (Module["locateFile"]) return locateFile("sqlite3.wasm");
|
|
return new URL("sqlite3.wasm", import.meta.url).href;
|
|
}
|
|
function getBinarySync(file) {
|
|
if (file == wasmBinaryFile && wasmBinary) return new Uint8Array(wasmBinary);
|
|
if (readBinary) return readBinary(file);
|
|
throw "both async and sync fetching of the wasm failed";
|
|
}
|
|
async function getWasmBinary(binaryFile) {
|
|
if (!wasmBinary) try {
|
|
var response = await readAsync(binaryFile);
|
|
return new Uint8Array(response);
|
|
} catch {}
|
|
return getBinarySync(binaryFile);
|
|
}
|
|
async function instantiateArrayBuffer(binaryFile, imports) {
|
|
try {
|
|
var binary = await getWasmBinary(binaryFile);
|
|
return await WebAssembly.instantiate(binary, imports);
|
|
} catch (reason) {
|
|
err(`failed to asynchronously prepare wasm: ${reason}`);
|
|
abort(reason);
|
|
}
|
|
}
|
|
async function instantiateAsync(binary, binaryFile, imports) {
|
|
if (!binary) try {
|
|
var response = fetch(binaryFile, { credentials: "same-origin" });
|
|
return await WebAssembly.instantiateStreaming(response, imports);
|
|
} catch (reason) {
|
|
err(`wasm streaming compile failed: ${reason}`);
|
|
err("falling back to ArrayBuffer instantiation");
|
|
}
|
|
return instantiateArrayBuffer(binaryFile, imports);
|
|
}
|
|
function getWasmImports() {
|
|
return {
|
|
"env": wasmImports,
|
|
"wasi_snapshot_preview1": wasmImports
|
|
};
|
|
}
|
|
async function createWasm() {
|
|
function receiveInstance(instance, module) {
|
|
wasmExports = instance.exports;
|
|
assignWasmExports(wasmExports);
|
|
return wasmExports;
|
|
}
|
|
function receiveInstantiationResult(result) {
|
|
return receiveInstance(result["instance"]);
|
|
}
|
|
var info = getWasmImports();
|
|
if (Module["instantiateWasm"]) return new Promise((resolve, reject) => {
|
|
Module["instantiateWasm"](info, (inst, mod) => {
|
|
resolve(receiveInstance(inst, mod));
|
|
});
|
|
});
|
|
wasmBinaryFile ??= findWasmBinary();
|
|
return receiveInstantiationResult(await instantiateAsync(wasmBinary, wasmBinaryFile, info));
|
|
}
|
|
var callRuntimeCallbacks = (callbacks) => {
|
|
while (callbacks.length > 0) callbacks.shift()(Module);
|
|
};
|
|
var onPostRuns = [];
|
|
var addOnPostRun = (cb) => onPostRuns.push(cb);
|
|
var onPreRuns = [];
|
|
var addOnPreRun = (cb) => onPreRuns.push(cb);
|
|
var wasmMemory;
|
|
var PATH = {
|
|
isAbs: (path) => path.charAt(0) === "/",
|
|
splitPath: (filename) => {
|
|
return /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(filename).slice(1);
|
|
},
|
|
normalizeArray: (parts, allowAboveRoot) => {
|
|
var up = 0;
|
|
for (var i = parts.length - 1; i >= 0; i--) {
|
|
var last = parts[i];
|
|
if (last === ".") parts.splice(i, 1);
|
|
else if (last === "..") {
|
|
parts.splice(i, 1);
|
|
up++;
|
|
} else if (up) {
|
|
parts.splice(i, 1);
|
|
up--;
|
|
}
|
|
}
|
|
if (allowAboveRoot) for (; up; up--) parts.unshift("..");
|
|
return parts;
|
|
},
|
|
normalize: (path) => {
|
|
var isAbsolute = PATH.isAbs(path), trailingSlash = path.slice(-1) === "/";
|
|
path = PATH.normalizeArray(path.split("/").filter((p) => !!p), !isAbsolute).join("/");
|
|
if (!path && !isAbsolute) path = ".";
|
|
if (path && trailingSlash) path += "/";
|
|
return (isAbsolute ? "/" : "") + path;
|
|
},
|
|
dirname: (path) => {
|
|
var result = PATH.splitPath(path), root = result[0], dir = result[1];
|
|
if (!root && !dir) return ".";
|
|
if (dir) dir = dir.slice(0, -1);
|
|
return root + dir;
|
|
},
|
|
basename: (path) => path && path.match(/([^\/]+|\/)\/*$/)[1],
|
|
join: (...paths) => PATH.normalize(paths.join("/")),
|
|
join2: (l, r) => PATH.normalize(l + "/" + r)
|
|
};
|
|
var initRandomFill = () => {
|
|
return (view) => crypto.getRandomValues(view);
|
|
};
|
|
var randomFill = (view) => {
|
|
(randomFill = initRandomFill())(view);
|
|
};
|
|
var PATH_FS = {
|
|
resolve: (...args) => {
|
|
var resolvedPath = "", resolvedAbsolute = false;
|
|
for (var i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) {
|
|
var path = i >= 0 ? args[i] : FS.cwd();
|
|
if (typeof path != "string") throw new TypeError("Arguments to path.resolve must be strings");
|
|
else if (!path) return "";
|
|
resolvedPath = path + "/" + resolvedPath;
|
|
resolvedAbsolute = PATH.isAbs(path);
|
|
}
|
|
resolvedPath = PATH.normalizeArray(resolvedPath.split("/").filter((p) => !!p), !resolvedAbsolute).join("/");
|
|
return (resolvedAbsolute ? "/" : "") + resolvedPath || ".";
|
|
},
|
|
relative: (from, to) => {
|
|
from = PATH_FS.resolve(from).slice(1);
|
|
to = PATH_FS.resolve(to).slice(1);
|
|
function trim(arr) {
|
|
var start = 0;
|
|
for (; start < arr.length; start++) if (arr[start] !== "") break;
|
|
var end = arr.length - 1;
|
|
for (; end >= 0; end--) if (arr[end] !== "") break;
|
|
if (start > end) return [];
|
|
return arr.slice(start, end - start + 1);
|
|
}
|
|
var fromParts = trim(from.split("/"));
|
|
var toParts = trim(to.split("/"));
|
|
var length = Math.min(fromParts.length, toParts.length);
|
|
var samePartsLength = length;
|
|
for (var i = 0; i < length; i++) if (fromParts[i] !== toParts[i]) {
|
|
samePartsLength = i;
|
|
break;
|
|
}
|
|
var outputParts = [];
|
|
for (var i = samePartsLength; i < fromParts.length; i++) outputParts.push("..");
|
|
outputParts = outputParts.concat(toParts.slice(samePartsLength));
|
|
return outputParts.join("/");
|
|
}
|
|
};
|
|
var UTF8Decoder = new TextDecoder();
|
|
var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => {
|
|
var maxIdx = idx + maxBytesToRead;
|
|
if (ignoreNul) return maxIdx;
|
|
while (heapOrArray[idx] && !(idx >= maxIdx)) ++idx;
|
|
return idx;
|
|
};
|
|
var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead, ignoreNul) => {
|
|
var endPtr = findStringEnd(heapOrArray, idx, maxBytesToRead, ignoreNul);
|
|
return UTF8Decoder.decode(heapOrArray.buffer ? heapOrArray.subarray(idx, endPtr) : new Uint8Array(heapOrArray.slice(idx, endPtr)));
|
|
};
|
|
var FS_stdin_getChar_buffer = [];
|
|
var lengthBytesUTF8 = (str) => {
|
|
var len = 0;
|
|
for (var i = 0; i < str.length; ++i) {
|
|
var c = str.charCodeAt(i);
|
|
if (c <= 127) len++;
|
|
else if (c <= 2047) len += 2;
|
|
else if (c >= 55296 && c <= 57343) {
|
|
len += 4;
|
|
++i;
|
|
} else len += 3;
|
|
}
|
|
return len;
|
|
};
|
|
var stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => {
|
|
if (!(maxBytesToWrite > 0)) return 0;
|
|
var startIdx = outIdx;
|
|
var endIdx = outIdx + maxBytesToWrite - 1;
|
|
for (var i = 0; i < str.length; ++i) {
|
|
var u = str.codePointAt(i);
|
|
if (u <= 127) {
|
|
if (outIdx >= endIdx) break;
|
|
heap[outIdx++] = u;
|
|
} else if (u <= 2047) {
|
|
if (outIdx + 1 >= endIdx) break;
|
|
heap[outIdx++] = 192 | u >> 6;
|
|
heap[outIdx++] = 128 | u & 63;
|
|
} else if (u <= 65535) {
|
|
if (outIdx + 2 >= endIdx) break;
|
|
heap[outIdx++] = 224 | u >> 12;
|
|
heap[outIdx++] = 128 | u >> 6 & 63;
|
|
heap[outIdx++] = 128 | u & 63;
|
|
} else {
|
|
if (outIdx + 3 >= endIdx) break;
|
|
heap[outIdx++] = 240 | u >> 18;
|
|
heap[outIdx++] = 128 | u >> 12 & 63;
|
|
heap[outIdx++] = 128 | u >> 6 & 63;
|
|
heap[outIdx++] = 128 | u & 63;
|
|
i++;
|
|
}
|
|
}
|
|
heap[outIdx] = 0;
|
|
return outIdx - startIdx;
|
|
};
|
|
var intArrayFromString = (stringy, dontAddNull, length) => {
|
|
var len = length > 0 ? length : lengthBytesUTF8(stringy) + 1;
|
|
var u8array = new Array(len);
|
|
var numBytesWritten = stringToUTF8Array(stringy, u8array, 0, u8array.length);
|
|
if (dontAddNull) u8array.length = numBytesWritten;
|
|
return u8array;
|
|
};
|
|
var FS_stdin_getChar = () => {
|
|
if (!FS_stdin_getChar_buffer.length) {
|
|
var result = null;
|
|
if (globalThis.window?.prompt) {
|
|
result = window.prompt("Input: ");
|
|
if (result !== null) result += "\n";
|
|
}
|
|
if (!result) return null;
|
|
FS_stdin_getChar_buffer = intArrayFromString(result, true);
|
|
}
|
|
return FS_stdin_getChar_buffer.shift();
|
|
};
|
|
var TTY = {
|
|
ttys: [],
|
|
init() {},
|
|
shutdown() {},
|
|
register(dev, ops) {
|
|
TTY.ttys[dev] = {
|
|
input: [],
|
|
output: [],
|
|
ops
|
|
};
|
|
FS.registerDevice(dev, TTY.stream_ops);
|
|
},
|
|
stream_ops: {
|
|
open(stream) {
|
|
var tty = TTY.ttys[stream.node.rdev];
|
|
if (!tty) throw new FS.ErrnoError(43);
|
|
stream.tty = tty;
|
|
stream.seekable = false;
|
|
},
|
|
close(stream) {
|
|
stream.tty.ops.fsync(stream.tty);
|
|
},
|
|
fsync(stream) {
|
|
stream.tty.ops.fsync(stream.tty);
|
|
},
|
|
read(stream, buffer, offset, length, pos) {
|
|
if (!stream.tty || !stream.tty.ops.get_char) throw new FS.ErrnoError(60);
|
|
var bytesRead = 0;
|
|
for (var i = 0; i < length; i++) {
|
|
var result;
|
|
try {
|
|
result = stream.tty.ops.get_char(stream.tty);
|
|
} catch (e) {
|
|
throw new FS.ErrnoError(29);
|
|
}
|
|
if (result === void 0 && bytesRead === 0) throw new FS.ErrnoError(6);
|
|
if (result === null || result === void 0) break;
|
|
bytesRead++;
|
|
buffer[offset + i] = result;
|
|
}
|
|
if (bytesRead) stream.node.atime = Date.now();
|
|
return bytesRead;
|
|
},
|
|
write(stream, buffer, offset, length, pos) {
|
|
if (!stream.tty || !stream.tty.ops.put_char) throw new FS.ErrnoError(60);
|
|
try {
|
|
for (var i = 0; i < length; i++) stream.tty.ops.put_char(stream.tty, buffer[offset + i]);
|
|
} catch (e) {
|
|
throw new FS.ErrnoError(29);
|
|
}
|
|
if (length) stream.node.mtime = stream.node.ctime = Date.now();
|
|
return i;
|
|
}
|
|
},
|
|
default_tty_ops: {
|
|
get_char(tty) {
|
|
return FS_stdin_getChar();
|
|
},
|
|
put_char(tty, val) {
|
|
if (val === null || val === 10) {
|
|
out(UTF8ArrayToString(tty.output));
|
|
tty.output = [];
|
|
} else if (val != 0) tty.output.push(val);
|
|
},
|
|
fsync(tty) {
|
|
if (tty.output?.length > 0) {
|
|
out(UTF8ArrayToString(tty.output));
|
|
tty.output = [];
|
|
}
|
|
},
|
|
ioctl_tcgets(tty) {
|
|
return {
|
|
c_iflag: 25856,
|
|
c_oflag: 5,
|
|
c_cflag: 191,
|
|
c_lflag: 35387,
|
|
c_cc: [
|
|
3,
|
|
28,
|
|
127,
|
|
21,
|
|
4,
|
|
0,
|
|
1,
|
|
0,
|
|
17,
|
|
19,
|
|
26,
|
|
0,
|
|
18,
|
|
15,
|
|
23,
|
|
22,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0
|
|
]
|
|
};
|
|
},
|
|
ioctl_tcsets(tty, optional_actions, data) {
|
|
return 0;
|
|
},
|
|
ioctl_tiocgwinsz(tty) {
|
|
return [24, 80];
|
|
}
|
|
},
|
|
default_tty1_ops: {
|
|
put_char(tty, val) {
|
|
if (val === null || val === 10) {
|
|
err(UTF8ArrayToString(tty.output));
|
|
tty.output = [];
|
|
} else if (val != 0) tty.output.push(val);
|
|
},
|
|
fsync(tty) {
|
|
if (tty.output?.length > 0) {
|
|
err(UTF8ArrayToString(tty.output));
|
|
tty.output = [];
|
|
}
|
|
}
|
|
}
|
|
};
|
|
var zeroMemory = (ptr, size) => HEAPU8.fill(0, ptr, ptr + size);
|
|
var alignMemory = (size, alignment) => {
|
|
return Math.ceil(size / alignment) * alignment;
|
|
};
|
|
var mmapAlloc = (size) => {
|
|
size = alignMemory(size, 65536);
|
|
var ptr = _emscripten_builtin_memalign(65536, size);
|
|
if (ptr) zeroMemory(ptr, size);
|
|
return ptr;
|
|
};
|
|
var MEMFS = {
|
|
ops_table: null,
|
|
mount(mount) {
|
|
return MEMFS.createNode(null, "/", 16895, 0);
|
|
},
|
|
createNode(parent, name, mode, dev) {
|
|
if (FS.isBlkdev(mode) || FS.isFIFO(mode)) throw new FS.ErrnoError(63);
|
|
MEMFS.ops_table ||= {
|
|
dir: {
|
|
node: {
|
|
getattr: MEMFS.node_ops.getattr,
|
|
setattr: MEMFS.node_ops.setattr,
|
|
lookup: MEMFS.node_ops.lookup,
|
|
mknod: MEMFS.node_ops.mknod,
|
|
rename: MEMFS.node_ops.rename,
|
|
unlink: MEMFS.node_ops.unlink,
|
|
rmdir: MEMFS.node_ops.rmdir,
|
|
readdir: MEMFS.node_ops.readdir,
|
|
symlink: MEMFS.node_ops.symlink
|
|
},
|
|
stream: { llseek: MEMFS.stream_ops.llseek }
|
|
},
|
|
file: {
|
|
node: {
|
|
getattr: MEMFS.node_ops.getattr,
|
|
setattr: MEMFS.node_ops.setattr
|
|
},
|
|
stream: {
|
|
llseek: MEMFS.stream_ops.llseek,
|
|
read: MEMFS.stream_ops.read,
|
|
write: MEMFS.stream_ops.write,
|
|
mmap: MEMFS.stream_ops.mmap,
|
|
msync: MEMFS.stream_ops.msync
|
|
}
|
|
},
|
|
link: {
|
|
node: {
|
|
getattr: MEMFS.node_ops.getattr,
|
|
setattr: MEMFS.node_ops.setattr,
|
|
readlink: MEMFS.node_ops.readlink
|
|
},
|
|
stream: {}
|
|
},
|
|
chrdev: {
|
|
node: {
|
|
getattr: MEMFS.node_ops.getattr,
|
|
setattr: MEMFS.node_ops.setattr
|
|
},
|
|
stream: FS.chrdev_stream_ops
|
|
}
|
|
};
|
|
var node = FS.createNode(parent, name, mode, dev);
|
|
if (FS.isDir(node.mode)) {
|
|
node.node_ops = MEMFS.ops_table.dir.node;
|
|
node.stream_ops = MEMFS.ops_table.dir.stream;
|
|
node.contents = {};
|
|
} else if (FS.isFile(node.mode)) {
|
|
node.node_ops = MEMFS.ops_table.file.node;
|
|
node.stream_ops = MEMFS.ops_table.file.stream;
|
|
node.usedBytes = 0;
|
|
node.contents = null;
|
|
} else if (FS.isLink(node.mode)) {
|
|
node.node_ops = MEMFS.ops_table.link.node;
|
|
node.stream_ops = MEMFS.ops_table.link.stream;
|
|
} else if (FS.isChrdev(node.mode)) {
|
|
node.node_ops = MEMFS.ops_table.chrdev.node;
|
|
node.stream_ops = MEMFS.ops_table.chrdev.stream;
|
|
}
|
|
node.atime = node.mtime = node.ctime = Date.now();
|
|
if (parent) {
|
|
parent.contents[name] = node;
|
|
parent.atime = parent.mtime = parent.ctime = node.atime;
|
|
}
|
|
return node;
|
|
},
|
|
getFileDataAsTypedArray(node) {
|
|
if (!node.contents) return new Uint8Array(0);
|
|
if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes);
|
|
return new Uint8Array(node.contents);
|
|
},
|
|
expandFileStorage(node, newCapacity) {
|
|
var prevCapacity = node.contents ? node.contents.length : 0;
|
|
if (prevCapacity >= newCapacity) return;
|
|
newCapacity = Math.max(newCapacity, prevCapacity * (prevCapacity < 1024 * 1024 ? 2 : 1.125) >>> 0);
|
|
if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256);
|
|
var oldContents = node.contents;
|
|
node.contents = new Uint8Array(newCapacity);
|
|
if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0);
|
|
},
|
|
resizeFileStorage(node, newSize) {
|
|
if (node.usedBytes == newSize) return;
|
|
if (newSize == 0) {
|
|
node.contents = null;
|
|
node.usedBytes = 0;
|
|
} else {
|
|
var oldContents = node.contents;
|
|
node.contents = new Uint8Array(newSize);
|
|
if (oldContents) node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes)));
|
|
node.usedBytes = newSize;
|
|
}
|
|
},
|
|
node_ops: {
|
|
getattr(node) {
|
|
var attr = {};
|
|
attr.dev = FS.isChrdev(node.mode) ? node.id : 1;
|
|
attr.ino = node.id;
|
|
attr.mode = node.mode;
|
|
attr.nlink = 1;
|
|
attr.uid = 0;
|
|
attr.gid = 0;
|
|
attr.rdev = node.rdev;
|
|
if (FS.isDir(node.mode)) attr.size = 4096;
|
|
else if (FS.isFile(node.mode)) attr.size = node.usedBytes;
|
|
else if (FS.isLink(node.mode)) attr.size = node.link.length;
|
|
else attr.size = 0;
|
|
attr.atime = new Date(node.atime);
|
|
attr.mtime = new Date(node.mtime);
|
|
attr.ctime = new Date(node.ctime);
|
|
attr.blksize = 4096;
|
|
attr.blocks = Math.ceil(attr.size / attr.blksize);
|
|
return attr;
|
|
},
|
|
setattr(node, attr) {
|
|
for (const key of [
|
|
"mode",
|
|
"atime",
|
|
"mtime",
|
|
"ctime"
|
|
]) if (attr[key] != null) node[key] = attr[key];
|
|
if (attr.size !== void 0) MEMFS.resizeFileStorage(node, attr.size);
|
|
},
|
|
lookup(parent, name) {
|
|
if (!MEMFS.doesNotExistError) {
|
|
MEMFS.doesNotExistError = new FS.ErrnoError(44);
|
|
MEMFS.doesNotExistError.stack = "<generic error, no stack>";
|
|
}
|
|
throw MEMFS.doesNotExistError;
|
|
},
|
|
mknod(parent, name, mode, dev) {
|
|
return MEMFS.createNode(parent, name, mode, dev);
|
|
},
|
|
rename(old_node, new_dir, new_name) {
|
|
var new_node;
|
|
try {
|
|
new_node = FS.lookupNode(new_dir, new_name);
|
|
} catch (e) {}
|
|
if (new_node) {
|
|
if (FS.isDir(old_node.mode)) for (var i in new_node.contents) throw new FS.ErrnoError(55);
|
|
FS.hashRemoveNode(new_node);
|
|
}
|
|
delete old_node.parent.contents[old_node.name];
|
|
new_dir.contents[new_name] = old_node;
|
|
old_node.name = new_name;
|
|
new_dir.ctime = new_dir.mtime = old_node.parent.ctime = old_node.parent.mtime = Date.now();
|
|
},
|
|
unlink(parent, name) {
|
|
delete parent.contents[name];
|
|
parent.ctime = parent.mtime = Date.now();
|
|
},
|
|
rmdir(parent, name) {
|
|
for (var i in FS.lookupNode(parent, name).contents) throw new FS.ErrnoError(55);
|
|
delete parent.contents[name];
|
|
parent.ctime = parent.mtime = Date.now();
|
|
},
|
|
readdir(node) {
|
|
return [
|
|
".",
|
|
"..",
|
|
...Object.keys(node.contents)
|
|
];
|
|
},
|
|
symlink(parent, newname, oldpath) {
|
|
var node = MEMFS.createNode(parent, newname, 41471, 0);
|
|
node.link = oldpath;
|
|
return node;
|
|
},
|
|
readlink(node) {
|
|
if (!FS.isLink(node.mode)) throw new FS.ErrnoError(28);
|
|
return node.link;
|
|
}
|
|
},
|
|
stream_ops: {
|
|
read(stream, buffer, offset, length, position) {
|
|
var contents = stream.node.contents;
|
|
if (position >= stream.node.usedBytes) return 0;
|
|
var size = Math.min(stream.node.usedBytes - position, length);
|
|
if (size > 8 && contents.subarray) buffer.set(contents.subarray(position, position + size), offset);
|
|
else for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i];
|
|
return size;
|
|
},
|
|
write(stream, buffer, offset, length, position, canOwn) {
|
|
if (buffer.buffer === HEAP8.buffer) canOwn = false;
|
|
if (!length) return 0;
|
|
var node = stream.node;
|
|
node.mtime = node.ctime = Date.now();
|
|
if (buffer.subarray && (!node.contents || node.contents.subarray)) {
|
|
if (canOwn) {
|
|
node.contents = buffer.subarray(offset, offset + length);
|
|
node.usedBytes = length;
|
|
return length;
|
|
} else if (node.usedBytes === 0 && position === 0) {
|
|
node.contents = buffer.slice(offset, offset + length);
|
|
node.usedBytes = length;
|
|
return length;
|
|
} else if (position + length <= node.usedBytes) {
|
|
node.contents.set(buffer.subarray(offset, offset + length), position);
|
|
return length;
|
|
}
|
|
}
|
|
MEMFS.expandFileStorage(node, position + length);
|
|
if (node.contents.subarray && buffer.subarray) node.contents.set(buffer.subarray(offset, offset + length), position);
|
|
else for (var i = 0; i < length; i++) node.contents[position + i] = buffer[offset + i];
|
|
node.usedBytes = Math.max(node.usedBytes, position + length);
|
|
return length;
|
|
},
|
|
llseek(stream, offset, whence) {
|
|
var position = offset;
|
|
if (whence === 1) position += stream.position;
|
|
else if (whence === 2) {
|
|
if (FS.isFile(stream.node.mode)) position += stream.node.usedBytes;
|
|
}
|
|
if (position < 0) throw new FS.ErrnoError(28);
|
|
return position;
|
|
},
|
|
mmap(stream, length, position, prot, flags) {
|
|
if (!FS.isFile(stream.node.mode)) throw new FS.ErrnoError(43);
|
|
var ptr;
|
|
var allocated;
|
|
var contents = stream.node.contents;
|
|
if (!(flags & 2) && contents && contents.buffer === HEAP8.buffer) {
|
|
allocated = false;
|
|
ptr = contents.byteOffset;
|
|
} else {
|
|
allocated = true;
|
|
ptr = mmapAlloc(length);
|
|
if (!ptr) throw new FS.ErrnoError(48);
|
|
if (contents) {
|
|
if (position > 0 || position + length < contents.length) if (contents.subarray) contents = contents.subarray(position, position + length);
|
|
else contents = Array.prototype.slice.call(contents, position, position + length);
|
|
HEAP8.set(contents, ptr);
|
|
}
|
|
}
|
|
return {
|
|
ptr,
|
|
allocated
|
|
};
|
|
},
|
|
msync(stream, buffer, offset, length, mmapFlags) {
|
|
MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false);
|
|
return 0;
|
|
}
|
|
}
|
|
};
|
|
var FS_modeStringToFlags = (str) => {
|
|
var flags = {
|
|
"r": 0,
|
|
"r+": 2,
|
|
"w": 577,
|
|
"w+": 578,
|
|
"a": 1089,
|
|
"a+": 1090
|
|
}[str];
|
|
if (typeof flags == "undefined") throw new Error(`Unknown file open mode: ${str}`);
|
|
return flags;
|
|
};
|
|
var FS_getMode = (canRead, canWrite) => {
|
|
var mode = 0;
|
|
if (canRead) mode |= 365;
|
|
if (canWrite) mode |= 146;
|
|
return mode;
|
|
};
|
|
var asyncLoad = async (url) => {
|
|
var arrayBuffer = await readAsync(url);
|
|
return new Uint8Array(arrayBuffer);
|
|
};
|
|
var FS_createDataFile = (...args) => FS.createDataFile(...args);
|
|
var getUniqueRunDependency = (id) => {
|
|
return id;
|
|
};
|
|
var runDependencies = 0;
|
|
var dependenciesFulfilled = null;
|
|
var removeRunDependency = (id) => {
|
|
runDependencies--;
|
|
Module["monitorRunDependencies"]?.(runDependencies);
|
|
if (runDependencies == 0) {
|
|
if (dependenciesFulfilled) {
|
|
var callback = dependenciesFulfilled;
|
|
dependenciesFulfilled = null;
|
|
callback();
|
|
}
|
|
}
|
|
};
|
|
var addRunDependency = (id) => {
|
|
runDependencies++;
|
|
Module["monitorRunDependencies"]?.(runDependencies);
|
|
};
|
|
var preloadPlugins = [];
|
|
var FS_handledByPreloadPlugin = async (byteArray, fullname) => {
|
|
if (typeof Browser != "undefined") Browser.init();
|
|
for (var plugin of preloadPlugins) if (plugin["canHandle"](fullname)) return plugin["handle"](byteArray, fullname);
|
|
return byteArray;
|
|
};
|
|
var FS_preloadFile = async (parent, name, url, canRead, canWrite, dontCreateFile, canOwn, preFinish) => {
|
|
var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent;
|
|
var dep = getUniqueRunDependency(`cp ${fullname}`);
|
|
addRunDependency(dep);
|
|
try {
|
|
var byteArray = url;
|
|
if (typeof url == "string") byteArray = await asyncLoad(url);
|
|
byteArray = await FS_handledByPreloadPlugin(byteArray, fullname);
|
|
preFinish?.();
|
|
if (!dontCreateFile) FS_createDataFile(parent, name, byteArray, canRead, canWrite, canOwn);
|
|
} finally {
|
|
removeRunDependency(dep);
|
|
}
|
|
};
|
|
var FS_createPreloadedFile = (parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish) => {
|
|
FS_preloadFile(parent, name, url, canRead, canWrite, dontCreateFile, canOwn, preFinish).then(onload).catch(onerror);
|
|
};
|
|
var FS = {
|
|
root: null,
|
|
mounts: [],
|
|
devices: {},
|
|
streams: [],
|
|
nextInode: 1,
|
|
nameTable: null,
|
|
currentPath: "/",
|
|
initialized: false,
|
|
ignorePermissions: true,
|
|
filesystems: null,
|
|
syncFSRequests: 0,
|
|
readFiles: {},
|
|
ErrnoError: class {
|
|
name = "ErrnoError";
|
|
constructor(errno) {
|
|
this.errno = errno;
|
|
}
|
|
},
|
|
FSStream: class {
|
|
shared = {};
|
|
get object() {
|
|
return this.node;
|
|
}
|
|
set object(val) {
|
|
this.node = val;
|
|
}
|
|
get isRead() {
|
|
return (this.flags & 2097155) !== 1;
|
|
}
|
|
get isWrite() {
|
|
return (this.flags & 2097155) !== 0;
|
|
}
|
|
get isAppend() {
|
|
return this.flags & 1024;
|
|
}
|
|
get flags() {
|
|
return this.shared.flags;
|
|
}
|
|
set flags(val) {
|
|
this.shared.flags = val;
|
|
}
|
|
get position() {
|
|
return this.shared.position;
|
|
}
|
|
set position(val) {
|
|
this.shared.position = val;
|
|
}
|
|
},
|
|
FSNode: class {
|
|
node_ops = {};
|
|
stream_ops = {};
|
|
readMode = 365;
|
|
writeMode = 146;
|
|
mounted = null;
|
|
constructor(parent, name, mode, rdev) {
|
|
if (!parent) parent = this;
|
|
this.parent = parent;
|
|
this.mount = parent.mount;
|
|
this.id = FS.nextInode++;
|
|
this.name = name;
|
|
this.mode = mode;
|
|
this.rdev = rdev;
|
|
this.atime = this.mtime = this.ctime = Date.now();
|
|
}
|
|
get read() {
|
|
return (this.mode & this.readMode) === this.readMode;
|
|
}
|
|
set read(val) {
|
|
val ? this.mode |= this.readMode : this.mode &= ~this.readMode;
|
|
}
|
|
get write() {
|
|
return (this.mode & this.writeMode) === this.writeMode;
|
|
}
|
|
set write(val) {
|
|
val ? this.mode |= this.writeMode : this.mode &= ~this.writeMode;
|
|
}
|
|
get isFolder() {
|
|
return FS.isDir(this.mode);
|
|
}
|
|
get isDevice() {
|
|
return FS.isChrdev(this.mode);
|
|
}
|
|
},
|
|
lookupPath(path, opts = {}) {
|
|
if (!path) throw new FS.ErrnoError(44);
|
|
opts.follow_mount ??= true;
|
|
if (!PATH.isAbs(path)) path = FS.cwd() + "/" + path;
|
|
linkloop: for (var nlinks = 0; nlinks < 40; nlinks++) {
|
|
var parts = path.split("/").filter((p) => !!p);
|
|
var current = FS.root;
|
|
var current_path = "/";
|
|
for (var i = 0; i < parts.length; i++) {
|
|
var islast = i === parts.length - 1;
|
|
if (islast && opts.parent) break;
|
|
if (parts[i] === ".") continue;
|
|
if (parts[i] === "..") {
|
|
current_path = PATH.dirname(current_path);
|
|
if (FS.isRoot(current)) {
|
|
path = current_path + "/" + parts.slice(i + 1).join("/");
|
|
nlinks--;
|
|
continue linkloop;
|
|
} else current = current.parent;
|
|
continue;
|
|
}
|
|
current_path = PATH.join2(current_path, parts[i]);
|
|
try {
|
|
current = FS.lookupNode(current, parts[i]);
|
|
} catch (e) {
|
|
if (e?.errno === 44 && islast && opts.noent_okay) return { path: current_path };
|
|
throw e;
|
|
}
|
|
if (FS.isMountpoint(current) && (!islast || opts.follow_mount)) current = current.mounted.root;
|
|
if (FS.isLink(current.mode) && (!islast || opts.follow)) {
|
|
if (!current.node_ops.readlink) throw new FS.ErrnoError(52);
|
|
var link = current.node_ops.readlink(current);
|
|
if (!PATH.isAbs(link)) link = PATH.dirname(current_path) + "/" + link;
|
|
path = link + "/" + parts.slice(i + 1).join("/");
|
|
continue linkloop;
|
|
}
|
|
}
|
|
return {
|
|
path: current_path,
|
|
node: current
|
|
};
|
|
}
|
|
throw new FS.ErrnoError(32);
|
|
},
|
|
getPath(node) {
|
|
var path;
|
|
while (true) {
|
|
if (FS.isRoot(node)) {
|
|
var mount = node.mount.mountpoint;
|
|
if (!path) return mount;
|
|
return mount[mount.length - 1] !== "/" ? `${mount}/${path}` : mount + path;
|
|
}
|
|
path = path ? `${node.name}/${path}` : node.name;
|
|
node = node.parent;
|
|
}
|
|
},
|
|
hashName(parentid, name) {
|
|
var hash = 0;
|
|
for (var i = 0; i < name.length; i++) hash = (hash << 5) - hash + name.charCodeAt(i) | 0;
|
|
return (parentid + hash >>> 0) % FS.nameTable.length;
|
|
},
|
|
hashAddNode(node) {
|
|
var hash = FS.hashName(node.parent.id, node.name);
|
|
node.name_next = FS.nameTable[hash];
|
|
FS.nameTable[hash] = node;
|
|
},
|
|
hashRemoveNode(node) {
|
|
var hash = FS.hashName(node.parent.id, node.name);
|
|
if (FS.nameTable[hash] === node) FS.nameTable[hash] = node.name_next;
|
|
else {
|
|
var current = FS.nameTable[hash];
|
|
while (current) {
|
|
if (current.name_next === node) {
|
|
current.name_next = node.name_next;
|
|
break;
|
|
}
|
|
current = current.name_next;
|
|
}
|
|
}
|
|
},
|
|
lookupNode(parent, name) {
|
|
var errCode = FS.mayLookup(parent);
|
|
if (errCode) throw new FS.ErrnoError(errCode);
|
|
var hash = FS.hashName(parent.id, name);
|
|
for (var node = FS.nameTable[hash]; node; node = node.name_next) {
|
|
var nodeName = node.name;
|
|
if (node.parent.id === parent.id && nodeName === name) return node;
|
|
}
|
|
return FS.lookup(parent, name);
|
|
},
|
|
createNode(parent, name, mode, rdev) {
|
|
var node = new FS.FSNode(parent, name, mode, rdev);
|
|
FS.hashAddNode(node);
|
|
return node;
|
|
},
|
|
destroyNode(node) {
|
|
FS.hashRemoveNode(node);
|
|
},
|
|
isRoot(node) {
|
|
return node === node.parent;
|
|
},
|
|
isMountpoint(node) {
|
|
return !!node.mounted;
|
|
},
|
|
isFile(mode) {
|
|
return (mode & 61440) === 32768;
|
|
},
|
|
isDir(mode) {
|
|
return (mode & 61440) === 16384;
|
|
},
|
|
isLink(mode) {
|
|
return (mode & 61440) === 40960;
|
|
},
|
|
isChrdev(mode) {
|
|
return (mode & 61440) === 8192;
|
|
},
|
|
isBlkdev(mode) {
|
|
return (mode & 61440) === 24576;
|
|
},
|
|
isFIFO(mode) {
|
|
return (mode & 61440) === 4096;
|
|
},
|
|
isSocket(mode) {
|
|
return (mode & 49152) === 49152;
|
|
},
|
|
flagsToPermissionString(flag) {
|
|
var perms = [
|
|
"r",
|
|
"w",
|
|
"rw"
|
|
][flag & 3];
|
|
if (flag & 512) perms += "w";
|
|
return perms;
|
|
},
|
|
nodePermissions(node, perms) {
|
|
if (FS.ignorePermissions) return 0;
|
|
if (perms.includes("r") && !(node.mode & 292)) return 2;
|
|
else if (perms.includes("w") && !(node.mode & 146)) return 2;
|
|
else if (perms.includes("x") && !(node.mode & 73)) return 2;
|
|
return 0;
|
|
},
|
|
mayLookup(dir) {
|
|
if (!FS.isDir(dir.mode)) return 54;
|
|
var errCode = FS.nodePermissions(dir, "x");
|
|
if (errCode) return errCode;
|
|
if (!dir.node_ops.lookup) return 2;
|
|
return 0;
|
|
},
|
|
mayCreate(dir, name) {
|
|
if (!FS.isDir(dir.mode)) return 54;
|
|
try {
|
|
FS.lookupNode(dir, name);
|
|
return 20;
|
|
} catch (e) {}
|
|
return FS.nodePermissions(dir, "wx");
|
|
},
|
|
mayDelete(dir, name, isdir) {
|
|
var node;
|
|
try {
|
|
node = FS.lookupNode(dir, name);
|
|
} catch (e) {
|
|
return e.errno;
|
|
}
|
|
var errCode = FS.nodePermissions(dir, "wx");
|
|
if (errCode) return errCode;
|
|
if (isdir) {
|
|
if (!FS.isDir(node.mode)) return 54;
|
|
if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) return 10;
|
|
} else if (FS.isDir(node.mode)) return 31;
|
|
return 0;
|
|
},
|
|
mayOpen(node, flags) {
|
|
if (!node) return 44;
|
|
if (FS.isLink(node.mode)) return 32;
|
|
else if (FS.isDir(node.mode)) {
|
|
if (FS.flagsToPermissionString(flags) !== "r" || flags & 576) return 31;
|
|
}
|
|
return FS.nodePermissions(node, FS.flagsToPermissionString(flags));
|
|
},
|
|
checkOpExists(op, err) {
|
|
if (!op) throw new FS.ErrnoError(err);
|
|
return op;
|
|
},
|
|
MAX_OPEN_FDS: 4096,
|
|
nextfd() {
|
|
for (var fd = 0; fd <= FS.MAX_OPEN_FDS; fd++) if (!FS.streams[fd]) return fd;
|
|
throw new FS.ErrnoError(33);
|
|
},
|
|
getStreamChecked(fd) {
|
|
var stream = FS.getStream(fd);
|
|
if (!stream) throw new FS.ErrnoError(8);
|
|
return stream;
|
|
},
|
|
getStream: (fd) => FS.streams[fd],
|
|
createStream(stream, fd = -1) {
|
|
stream = Object.assign(new FS.FSStream(), stream);
|
|
if (fd == -1) fd = FS.nextfd();
|
|
stream.fd = fd;
|
|
FS.streams[fd] = stream;
|
|
return stream;
|
|
},
|
|
closeStream(fd) {
|
|
FS.streams[fd] = null;
|
|
},
|
|
dupStream(origStream, fd = -1) {
|
|
var stream = FS.createStream(origStream, fd);
|
|
stream.stream_ops?.dup?.(stream);
|
|
return stream;
|
|
},
|
|
doSetAttr(stream, node, attr) {
|
|
var setattr = stream?.stream_ops.setattr;
|
|
var arg = setattr ? stream : node;
|
|
setattr ??= node.node_ops.setattr;
|
|
FS.checkOpExists(setattr, 63);
|
|
setattr(arg, attr);
|
|
},
|
|
chrdev_stream_ops: {
|
|
open(stream) {
|
|
stream.stream_ops = FS.getDevice(stream.node.rdev).stream_ops;
|
|
stream.stream_ops.open?.(stream);
|
|
},
|
|
llseek() {
|
|
throw new FS.ErrnoError(70);
|
|
}
|
|
},
|
|
major: (dev) => dev >> 8,
|
|
minor: (dev) => dev & 255,
|
|
makedev: (ma, mi) => ma << 8 | mi,
|
|
registerDevice(dev, ops) {
|
|
FS.devices[dev] = { stream_ops: ops };
|
|
},
|
|
getDevice: (dev) => FS.devices[dev],
|
|
getMounts(mount) {
|
|
var mounts = [];
|
|
var check = [mount];
|
|
while (check.length) {
|
|
var m = check.pop();
|
|
mounts.push(m);
|
|
check.push(...m.mounts);
|
|
}
|
|
return mounts;
|
|
},
|
|
syncfs(populate, callback) {
|
|
if (typeof populate == "function") {
|
|
callback = populate;
|
|
populate = false;
|
|
}
|
|
FS.syncFSRequests++;
|
|
if (FS.syncFSRequests > 1) err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`);
|
|
var mounts = FS.getMounts(FS.root.mount);
|
|
var completed = 0;
|
|
function doCallback(errCode) {
|
|
FS.syncFSRequests--;
|
|
return callback(errCode);
|
|
}
|
|
function done(errCode) {
|
|
if (errCode) {
|
|
if (!done.errored) {
|
|
done.errored = true;
|
|
return doCallback(errCode);
|
|
}
|
|
return;
|
|
}
|
|
if (++completed >= mounts.length) doCallback(null);
|
|
}
|
|
for (var mount of mounts) if (mount.type.syncfs) mount.type.syncfs(mount, populate, done);
|
|
else done(null);
|
|
},
|
|
mount(type, opts, mountpoint) {
|
|
var root = mountpoint === "/";
|
|
var pseudo = !mountpoint;
|
|
var node;
|
|
if (root && FS.root) throw new FS.ErrnoError(10);
|
|
else if (!root && !pseudo) {
|
|
var lookup = FS.lookupPath(mountpoint, { follow_mount: false });
|
|
mountpoint = lookup.path;
|
|
node = lookup.node;
|
|
if (FS.isMountpoint(node)) throw new FS.ErrnoError(10);
|
|
if (!FS.isDir(node.mode)) throw new FS.ErrnoError(54);
|
|
}
|
|
var mount = {
|
|
type,
|
|
opts,
|
|
mountpoint,
|
|
mounts: []
|
|
};
|
|
var mountRoot = type.mount(mount);
|
|
mountRoot.mount = mount;
|
|
mount.root = mountRoot;
|
|
if (root) FS.root = mountRoot;
|
|
else if (node) {
|
|
node.mounted = mount;
|
|
if (node.mount) node.mount.mounts.push(mount);
|
|
}
|
|
return mountRoot;
|
|
},
|
|
unmount(mountpoint) {
|
|
var lookup = FS.lookupPath(mountpoint, { follow_mount: false });
|
|
if (!FS.isMountpoint(lookup.node)) throw new FS.ErrnoError(28);
|
|
var node = lookup.node;
|
|
var mount = node.mounted;
|
|
var mounts = FS.getMounts(mount);
|
|
for (var [hash, current] of Object.entries(FS.nameTable)) while (current) {
|
|
var next = current.name_next;
|
|
if (mounts.includes(current.mount)) FS.destroyNode(current);
|
|
current = next;
|
|
}
|
|
node.mounted = null;
|
|
var idx = node.mount.mounts.indexOf(mount);
|
|
node.mount.mounts.splice(idx, 1);
|
|
},
|
|
lookup(parent, name) {
|
|
return parent.node_ops.lookup(parent, name);
|
|
},
|
|
mknod(path, mode, dev) {
|
|
var parent = FS.lookupPath(path, { parent: true }).node;
|
|
var name = PATH.basename(path);
|
|
if (!name) throw new FS.ErrnoError(28);
|
|
if (name === "." || name === "..") throw new FS.ErrnoError(20);
|
|
var errCode = FS.mayCreate(parent, name);
|
|
if (errCode) throw new FS.ErrnoError(errCode);
|
|
if (!parent.node_ops.mknod) throw new FS.ErrnoError(63);
|
|
return parent.node_ops.mknod(parent, name, mode, dev);
|
|
},
|
|
statfs(path) {
|
|
return FS.statfsNode(FS.lookupPath(path, { follow: true }).node);
|
|
},
|
|
statfsStream(stream) {
|
|
return FS.statfsNode(stream.node);
|
|
},
|
|
statfsNode(node) {
|
|
var rtn = {
|
|
bsize: 4096,
|
|
frsize: 4096,
|
|
blocks: 1e6,
|
|
bfree: 5e5,
|
|
bavail: 5e5,
|
|
files: FS.nextInode,
|
|
ffree: FS.nextInode - 1,
|
|
fsid: 42,
|
|
flags: 2,
|
|
namelen: 255
|
|
};
|
|
if (node.node_ops.statfs) Object.assign(rtn, node.node_ops.statfs(node.mount.opts.root));
|
|
return rtn;
|
|
},
|
|
create(path, mode = 438) {
|
|
mode &= 4095;
|
|
mode |= 32768;
|
|
return FS.mknod(path, mode, 0);
|
|
},
|
|
mkdir(path, mode = 511) {
|
|
mode &= 1023;
|
|
mode |= 16384;
|
|
return FS.mknod(path, mode, 0);
|
|
},
|
|
mkdirTree(path, mode) {
|
|
var dirs = path.split("/");
|
|
var d = "";
|
|
for (var dir of dirs) {
|
|
if (!dir) continue;
|
|
if (d || PATH.isAbs(path)) d += "/";
|
|
d += dir;
|
|
try {
|
|
FS.mkdir(d, mode);
|
|
} catch (e) {
|
|
if (e.errno != 20) throw e;
|
|
}
|
|
}
|
|
},
|
|
mkdev(path, mode, dev) {
|
|
if (typeof dev == "undefined") {
|
|
dev = mode;
|
|
mode = 438;
|
|
}
|
|
mode |= 8192;
|
|
return FS.mknod(path, mode, dev);
|
|
},
|
|
symlink(oldpath, newpath) {
|
|
if (!PATH_FS.resolve(oldpath)) throw new FS.ErrnoError(44);
|
|
var parent = FS.lookupPath(newpath, { parent: true }).node;
|
|
if (!parent) throw new FS.ErrnoError(44);
|
|
var newname = PATH.basename(newpath);
|
|
var errCode = FS.mayCreate(parent, newname);
|
|
if (errCode) throw new FS.ErrnoError(errCode);
|
|
if (!parent.node_ops.symlink) throw new FS.ErrnoError(63);
|
|
return parent.node_ops.symlink(parent, newname, oldpath);
|
|
},
|
|
rename(old_path, new_path) {
|
|
var old_dirname = PATH.dirname(old_path);
|
|
var new_dirname = PATH.dirname(new_path);
|
|
var old_name = PATH.basename(old_path);
|
|
var new_name = PATH.basename(new_path);
|
|
var lookup = FS.lookupPath(old_path, { parent: true }), old_dir = lookup.node, new_dir;
|
|
lookup = FS.lookupPath(new_path, { parent: true });
|
|
new_dir = lookup.node;
|
|
if (!old_dir || !new_dir) throw new FS.ErrnoError(44);
|
|
if (old_dir.mount !== new_dir.mount) throw new FS.ErrnoError(75);
|
|
var old_node = FS.lookupNode(old_dir, old_name);
|
|
var relative = PATH_FS.relative(old_path, new_dirname);
|
|
if (relative.charAt(0) !== ".") throw new FS.ErrnoError(28);
|
|
relative = PATH_FS.relative(new_path, old_dirname);
|
|
if (relative.charAt(0) !== ".") throw new FS.ErrnoError(55);
|
|
var new_node;
|
|
try {
|
|
new_node = FS.lookupNode(new_dir, new_name);
|
|
} catch (e) {}
|
|
if (old_node === new_node) return;
|
|
var isdir = FS.isDir(old_node.mode);
|
|
var errCode = FS.mayDelete(old_dir, old_name, isdir);
|
|
if (errCode) throw new FS.ErrnoError(errCode);
|
|
errCode = new_node ? FS.mayDelete(new_dir, new_name, isdir) : FS.mayCreate(new_dir, new_name);
|
|
if (errCode) throw new FS.ErrnoError(errCode);
|
|
if (!old_dir.node_ops.rename) throw new FS.ErrnoError(63);
|
|
if (FS.isMountpoint(old_node) || new_node && FS.isMountpoint(new_node)) throw new FS.ErrnoError(10);
|
|
if (new_dir !== old_dir) {
|
|
errCode = FS.nodePermissions(old_dir, "w");
|
|
if (errCode) throw new FS.ErrnoError(errCode);
|
|
}
|
|
FS.hashRemoveNode(old_node);
|
|
try {
|
|
old_dir.node_ops.rename(old_node, new_dir, new_name);
|
|
old_node.parent = new_dir;
|
|
} catch (e) {
|
|
throw e;
|
|
} finally {
|
|
FS.hashAddNode(old_node);
|
|
}
|
|
},
|
|
rmdir(path) {
|
|
var parent = FS.lookupPath(path, { parent: true }).node;
|
|
var name = PATH.basename(path);
|
|
var node = FS.lookupNode(parent, name);
|
|
var errCode = FS.mayDelete(parent, name, true);
|
|
if (errCode) throw new FS.ErrnoError(errCode);
|
|
if (!parent.node_ops.rmdir) throw new FS.ErrnoError(63);
|
|
if (FS.isMountpoint(node)) throw new FS.ErrnoError(10);
|
|
parent.node_ops.rmdir(parent, name);
|
|
FS.destroyNode(node);
|
|
},
|
|
readdir(path) {
|
|
var node = FS.lookupPath(path, { follow: true }).node;
|
|
return FS.checkOpExists(node.node_ops.readdir, 54)(node);
|
|
},
|
|
unlink(path) {
|
|
var parent = FS.lookupPath(path, { parent: true }).node;
|
|
if (!parent) throw new FS.ErrnoError(44);
|
|
var name = PATH.basename(path);
|
|
var node = FS.lookupNode(parent, name);
|
|
var errCode = FS.mayDelete(parent, name, false);
|
|
if (errCode) throw new FS.ErrnoError(errCode);
|
|
if (!parent.node_ops.unlink) throw new FS.ErrnoError(63);
|
|
if (FS.isMountpoint(node)) throw new FS.ErrnoError(10);
|
|
parent.node_ops.unlink(parent, name);
|
|
FS.destroyNode(node);
|
|
},
|
|
readlink(path) {
|
|
var link = FS.lookupPath(path).node;
|
|
if (!link) throw new FS.ErrnoError(44);
|
|
if (!link.node_ops.readlink) throw new FS.ErrnoError(28);
|
|
return link.node_ops.readlink(link);
|
|
},
|
|
stat(path, dontFollow) {
|
|
var node = FS.lookupPath(path, { follow: !dontFollow }).node;
|
|
return FS.checkOpExists(node.node_ops.getattr, 63)(node);
|
|
},
|
|
fstat(fd) {
|
|
var stream = FS.getStreamChecked(fd);
|
|
var node = stream.node;
|
|
var getattr = stream.stream_ops.getattr;
|
|
var arg = getattr ? stream : node;
|
|
getattr ??= node.node_ops.getattr;
|
|
FS.checkOpExists(getattr, 63);
|
|
return getattr(arg);
|
|
},
|
|
lstat(path) {
|
|
return FS.stat(path, true);
|
|
},
|
|
doChmod(stream, node, mode, dontFollow) {
|
|
FS.doSetAttr(stream, node, {
|
|
mode: mode & 4095 | node.mode & -4096,
|
|
ctime: Date.now(),
|
|
dontFollow
|
|
});
|
|
},
|
|
chmod(path, mode, dontFollow) {
|
|
var node;
|
|
if (typeof path == "string") node = FS.lookupPath(path, { follow: !dontFollow }).node;
|
|
else node = path;
|
|
FS.doChmod(null, node, mode, dontFollow);
|
|
},
|
|
lchmod(path, mode) {
|
|
FS.chmod(path, mode, true);
|
|
},
|
|
fchmod(fd, mode) {
|
|
var stream = FS.getStreamChecked(fd);
|
|
FS.doChmod(stream, stream.node, mode, false);
|
|
},
|
|
doChown(stream, node, dontFollow) {
|
|
FS.doSetAttr(stream, node, {
|
|
timestamp: Date.now(),
|
|
dontFollow
|
|
});
|
|
},
|
|
chown(path, uid, gid, dontFollow) {
|
|
var node;
|
|
if (typeof path == "string") node = FS.lookupPath(path, { follow: !dontFollow }).node;
|
|
else node = path;
|
|
FS.doChown(null, node, dontFollow);
|
|
},
|
|
lchown(path, uid, gid) {
|
|
FS.chown(path, uid, gid, true);
|
|
},
|
|
fchown(fd, uid, gid) {
|
|
var stream = FS.getStreamChecked(fd);
|
|
FS.doChown(stream, stream.node, false);
|
|
},
|
|
doTruncate(stream, node, len) {
|
|
if (FS.isDir(node.mode)) throw new FS.ErrnoError(31);
|
|
if (!FS.isFile(node.mode)) throw new FS.ErrnoError(28);
|
|
var errCode = FS.nodePermissions(node, "w");
|
|
if (errCode) throw new FS.ErrnoError(errCode);
|
|
FS.doSetAttr(stream, node, {
|
|
size: len,
|
|
timestamp: Date.now()
|
|
});
|
|
},
|
|
truncate(path, len) {
|
|
if (len < 0) throw new FS.ErrnoError(28);
|
|
var node;
|
|
if (typeof path == "string") node = FS.lookupPath(path, { follow: true }).node;
|
|
else node = path;
|
|
FS.doTruncate(null, node, len);
|
|
},
|
|
ftruncate(fd, len) {
|
|
var stream = FS.getStreamChecked(fd);
|
|
if (len < 0 || (stream.flags & 2097155) === 0) throw new FS.ErrnoError(28);
|
|
FS.doTruncate(stream, stream.node, len);
|
|
},
|
|
utime(path, atime, mtime) {
|
|
var node = FS.lookupPath(path, { follow: true }).node;
|
|
FS.checkOpExists(node.node_ops.setattr, 63)(node, {
|
|
atime,
|
|
mtime
|
|
});
|
|
},
|
|
open(path, flags, mode = 438) {
|
|
if (path === "") throw new FS.ErrnoError(44);
|
|
flags = typeof flags == "string" ? FS_modeStringToFlags(flags) : flags;
|
|
if (flags & 64) mode = mode & 4095 | 32768;
|
|
else mode = 0;
|
|
var node;
|
|
var isDirPath;
|
|
if (typeof path == "object") node = path;
|
|
else {
|
|
isDirPath = path.endsWith("/");
|
|
var lookup = FS.lookupPath(path, {
|
|
follow: !(flags & 131072),
|
|
noent_okay: true
|
|
});
|
|
node = lookup.node;
|
|
path = lookup.path;
|
|
}
|
|
var created = false;
|
|
if (flags & 64) if (node) {
|
|
if (flags & 128) throw new FS.ErrnoError(20);
|
|
} else if (isDirPath) throw new FS.ErrnoError(31);
|
|
else {
|
|
node = FS.mknod(path, mode | 511, 0);
|
|
created = true;
|
|
}
|
|
if (!node) throw new FS.ErrnoError(44);
|
|
if (FS.isChrdev(node.mode)) flags &= -513;
|
|
if (flags & 65536 && !FS.isDir(node.mode)) throw new FS.ErrnoError(54);
|
|
if (!created) {
|
|
var errCode = FS.mayOpen(node, flags);
|
|
if (errCode) throw new FS.ErrnoError(errCode);
|
|
}
|
|
if (flags & 512 && !created) FS.truncate(node, 0);
|
|
flags &= -131713;
|
|
var stream = FS.createStream({
|
|
node,
|
|
path: FS.getPath(node),
|
|
flags,
|
|
seekable: true,
|
|
position: 0,
|
|
stream_ops: node.stream_ops,
|
|
ungotten: [],
|
|
error: false
|
|
});
|
|
if (stream.stream_ops.open) stream.stream_ops.open(stream);
|
|
if (created) FS.chmod(node, mode & 511);
|
|
if (Module["logReadFiles"] && !(flags & 1)) {
|
|
if (!(path in FS.readFiles)) FS.readFiles[path] = 1;
|
|
}
|
|
return stream;
|
|
},
|
|
close(stream) {
|
|
if (FS.isClosed(stream)) throw new FS.ErrnoError(8);
|
|
if (stream.getdents) stream.getdents = null;
|
|
try {
|
|
if (stream.stream_ops.close) stream.stream_ops.close(stream);
|
|
} catch (e) {
|
|
throw e;
|
|
} finally {
|
|
FS.closeStream(stream.fd);
|
|
}
|
|
stream.fd = null;
|
|
},
|
|
isClosed(stream) {
|
|
return stream.fd === null;
|
|
},
|
|
llseek(stream, offset, whence) {
|
|
if (FS.isClosed(stream)) throw new FS.ErrnoError(8);
|
|
if (!stream.seekable || !stream.stream_ops.llseek) throw new FS.ErrnoError(70);
|
|
if (whence != 0 && whence != 1 && whence != 2) throw new FS.ErrnoError(28);
|
|
stream.position = stream.stream_ops.llseek(stream, offset, whence);
|
|
stream.ungotten = [];
|
|
return stream.position;
|
|
},
|
|
read(stream, buffer, offset, length, position) {
|
|
if (length < 0 || position < 0) throw new FS.ErrnoError(28);
|
|
if (FS.isClosed(stream)) throw new FS.ErrnoError(8);
|
|
if ((stream.flags & 2097155) === 1) throw new FS.ErrnoError(8);
|
|
if (FS.isDir(stream.node.mode)) throw new FS.ErrnoError(31);
|
|
if (!stream.stream_ops.read) throw new FS.ErrnoError(28);
|
|
var seeking = typeof position != "undefined";
|
|
if (!seeking) position = stream.position;
|
|
else if (!stream.seekable) throw new FS.ErrnoError(70);
|
|
var bytesRead = stream.stream_ops.read(stream, buffer, offset, length, position);
|
|
if (!seeking) stream.position += bytesRead;
|
|
return bytesRead;
|
|
},
|
|
write(stream, buffer, offset, length, position, canOwn) {
|
|
if (length < 0 || position < 0) throw new FS.ErrnoError(28);
|
|
if (FS.isClosed(stream)) throw new FS.ErrnoError(8);
|
|
if ((stream.flags & 2097155) === 0) throw new FS.ErrnoError(8);
|
|
if (FS.isDir(stream.node.mode)) throw new FS.ErrnoError(31);
|
|
if (!stream.stream_ops.write) throw new FS.ErrnoError(28);
|
|
if (stream.seekable && stream.flags & 1024) FS.llseek(stream, 0, 2);
|
|
var seeking = typeof position != "undefined";
|
|
if (!seeking) position = stream.position;
|
|
else if (!stream.seekable) throw new FS.ErrnoError(70);
|
|
var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn);
|
|
if (!seeking) stream.position += bytesWritten;
|
|
return bytesWritten;
|
|
},
|
|
mmap(stream, length, position, prot, flags) {
|
|
if ((prot & 2) !== 0 && (flags & 2) === 0 && (stream.flags & 2097155) !== 2) throw new FS.ErrnoError(2);
|
|
if ((stream.flags & 2097155) === 1) throw new FS.ErrnoError(2);
|
|
if (!stream.stream_ops.mmap) throw new FS.ErrnoError(43);
|
|
if (!length) throw new FS.ErrnoError(28);
|
|
return stream.stream_ops.mmap(stream, length, position, prot, flags);
|
|
},
|
|
msync(stream, buffer, offset, length, mmapFlags) {
|
|
if (!stream.stream_ops.msync) return 0;
|
|
return stream.stream_ops.msync(stream, buffer, offset, length, mmapFlags);
|
|
},
|
|
ioctl(stream, cmd, arg) {
|
|
if (!stream.stream_ops.ioctl) throw new FS.ErrnoError(59);
|
|
return stream.stream_ops.ioctl(stream, cmd, arg);
|
|
},
|
|
readFile(path, opts = {}) {
|
|
opts.flags = opts.flags || 0;
|
|
opts.encoding = opts.encoding || "binary";
|
|
if (opts.encoding !== "utf8" && opts.encoding !== "binary") abort(`Invalid encoding type "${opts.encoding}"`);
|
|
var stream = FS.open(path, opts.flags);
|
|
var length = FS.stat(path).size;
|
|
var buf = new Uint8Array(length);
|
|
FS.read(stream, buf, 0, length, 0);
|
|
if (opts.encoding === "utf8") buf = UTF8ArrayToString(buf);
|
|
FS.close(stream);
|
|
return buf;
|
|
},
|
|
writeFile(path, data, opts = {}) {
|
|
opts.flags = opts.flags || 577;
|
|
var stream = FS.open(path, opts.flags, opts.mode);
|
|
if (typeof data == "string") data = new Uint8Array(intArrayFromString(data, true));
|
|
if (ArrayBuffer.isView(data)) FS.write(stream, data, 0, data.byteLength, void 0, opts.canOwn);
|
|
else abort("Unsupported data type");
|
|
FS.close(stream);
|
|
},
|
|
cwd: () => FS.currentPath,
|
|
chdir(path) {
|
|
var lookup = FS.lookupPath(path, { follow: true });
|
|
if (lookup.node === null) throw new FS.ErrnoError(44);
|
|
if (!FS.isDir(lookup.node.mode)) throw new FS.ErrnoError(54);
|
|
var errCode = FS.nodePermissions(lookup.node, "x");
|
|
if (errCode) throw new FS.ErrnoError(errCode);
|
|
FS.currentPath = lookup.path;
|
|
},
|
|
createDefaultDirectories() {
|
|
FS.mkdir("/tmp");
|
|
FS.mkdir("/home");
|
|
FS.mkdir("/home/web_user");
|
|
},
|
|
createDefaultDevices() {
|
|
FS.mkdir("/dev");
|
|
FS.registerDevice(FS.makedev(1, 3), {
|
|
read: () => 0,
|
|
write: (stream, buffer, offset, length, pos) => length,
|
|
llseek: () => 0
|
|
});
|
|
FS.mkdev("/dev/null", FS.makedev(1, 3));
|
|
TTY.register(FS.makedev(5, 0), TTY.default_tty_ops);
|
|
TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops);
|
|
FS.mkdev("/dev/tty", FS.makedev(5, 0));
|
|
FS.mkdev("/dev/tty1", FS.makedev(6, 0));
|
|
var randomBuffer = new Uint8Array(1024), randomLeft = 0;
|
|
var randomByte = () => {
|
|
if (randomLeft === 0) {
|
|
randomFill(randomBuffer);
|
|
randomLeft = randomBuffer.byteLength;
|
|
}
|
|
return randomBuffer[--randomLeft];
|
|
};
|
|
FS.createDevice("/dev", "random", randomByte);
|
|
FS.createDevice("/dev", "urandom", randomByte);
|
|
FS.mkdir("/dev/shm");
|
|
FS.mkdir("/dev/shm/tmp");
|
|
},
|
|
createSpecialDirectories() {
|
|
FS.mkdir("/proc");
|
|
var proc_self = FS.mkdir("/proc/self");
|
|
FS.mkdir("/proc/self/fd");
|
|
FS.mount({ mount() {
|
|
var node = FS.createNode(proc_self, "fd", 16895, 73);
|
|
node.stream_ops = { llseek: MEMFS.stream_ops.llseek };
|
|
node.node_ops = {
|
|
lookup(parent, name) {
|
|
var fd = +name;
|
|
var stream = FS.getStreamChecked(fd);
|
|
var ret = {
|
|
parent: null,
|
|
mount: { mountpoint: "fake" },
|
|
node_ops: { readlink: () => stream.path },
|
|
id: fd + 1
|
|
};
|
|
ret.parent = ret;
|
|
return ret;
|
|
},
|
|
readdir() {
|
|
return Array.from(FS.streams.entries()).filter(([k, v]) => v).map(([k, v]) => k.toString());
|
|
}
|
|
};
|
|
return node;
|
|
} }, {}, "/proc/self/fd");
|
|
},
|
|
createStandardStreams(input, output, error) {
|
|
if (input) FS.createDevice("/dev", "stdin", input);
|
|
else FS.symlink("/dev/tty", "/dev/stdin");
|
|
if (output) FS.createDevice("/dev", "stdout", null, output);
|
|
else FS.symlink("/dev/tty", "/dev/stdout");
|
|
if (error) FS.createDevice("/dev", "stderr", null, error);
|
|
else FS.symlink("/dev/tty1", "/dev/stderr");
|
|
FS.open("/dev/stdin", 0);
|
|
FS.open("/dev/stdout", 1);
|
|
FS.open("/dev/stderr", 1);
|
|
},
|
|
staticInit() {
|
|
FS.nameTable = new Array(4096);
|
|
FS.mount(MEMFS, {}, "/");
|
|
FS.createDefaultDirectories();
|
|
FS.createDefaultDevices();
|
|
FS.createSpecialDirectories();
|
|
FS.filesystems = { "MEMFS": MEMFS };
|
|
},
|
|
init(input, output, error) {
|
|
FS.initialized = true;
|
|
input ??= Module["stdin"];
|
|
output ??= Module["stdout"];
|
|
error ??= Module["stderr"];
|
|
FS.createStandardStreams(input, output, error);
|
|
},
|
|
quit() {
|
|
FS.initialized = false;
|
|
for (var stream of FS.streams) if (stream) FS.close(stream);
|
|
},
|
|
findObject(path, dontResolveLastLink) {
|
|
var ret = FS.analyzePath(path, dontResolveLastLink);
|
|
if (!ret.exists) return null;
|
|
return ret.object;
|
|
},
|
|
analyzePath(path, dontResolveLastLink) {
|
|
try {
|
|
var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink });
|
|
path = lookup.path;
|
|
} catch (e) {}
|
|
var ret = {
|
|
isRoot: false,
|
|
exists: false,
|
|
error: 0,
|
|
name: null,
|
|
path: null,
|
|
object: null,
|
|
parentExists: false,
|
|
parentPath: null,
|
|
parentObject: null
|
|
};
|
|
try {
|
|
var lookup = FS.lookupPath(path, { parent: true });
|
|
ret.parentExists = true;
|
|
ret.parentPath = lookup.path;
|
|
ret.parentObject = lookup.node;
|
|
ret.name = PATH.basename(path);
|
|
lookup = FS.lookupPath(path, { follow: !dontResolveLastLink });
|
|
ret.exists = true;
|
|
ret.path = lookup.path;
|
|
ret.object = lookup.node;
|
|
ret.name = lookup.node.name;
|
|
ret.isRoot = lookup.path === "/";
|
|
} catch (e) {
|
|
ret.error = e.errno;
|
|
}
|
|
return ret;
|
|
},
|
|
createPath(parent, path, canRead, canWrite) {
|
|
parent = typeof parent == "string" ? parent : FS.getPath(parent);
|
|
var parts = path.split("/").reverse();
|
|
while (parts.length) {
|
|
var part = parts.pop();
|
|
if (!part) continue;
|
|
var current = PATH.join2(parent, part);
|
|
try {
|
|
FS.mkdir(current);
|
|
} catch (e) {
|
|
if (e.errno != 20) throw e;
|
|
}
|
|
parent = current;
|
|
}
|
|
return current;
|
|
},
|
|
createFile(parent, name, properties, canRead, canWrite) {
|
|
var path = PATH.join2(typeof parent == "string" ? parent : FS.getPath(parent), name);
|
|
var mode = FS_getMode(canRead, canWrite);
|
|
return FS.create(path, mode);
|
|
},
|
|
createDataFile(parent, name, data, canRead, canWrite, canOwn) {
|
|
var path = name;
|
|
if (parent) {
|
|
parent = typeof parent == "string" ? parent : FS.getPath(parent);
|
|
path = name ? PATH.join2(parent, name) : parent;
|
|
}
|
|
var mode = FS_getMode(canRead, canWrite);
|
|
var node = FS.create(path, mode);
|
|
if (data) {
|
|
if (typeof data == "string") {
|
|
var arr = new Array(data.length);
|
|
for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i);
|
|
data = arr;
|
|
}
|
|
FS.chmod(node, mode | 146);
|
|
var stream = FS.open(node, 577);
|
|
FS.write(stream, data, 0, data.length, 0, canOwn);
|
|
FS.close(stream);
|
|
FS.chmod(node, mode);
|
|
}
|
|
},
|
|
createDevice(parent, name, input, output) {
|
|
var path = PATH.join2(typeof parent == "string" ? parent : FS.getPath(parent), name);
|
|
var mode = FS_getMode(!!input, !!output);
|
|
FS.createDevice.major ??= 64;
|
|
var dev = FS.makedev(FS.createDevice.major++, 0);
|
|
FS.registerDevice(dev, {
|
|
open(stream) {
|
|
stream.seekable = false;
|
|
},
|
|
close(stream) {
|
|
if (output?.buffer?.length) output(10);
|
|
},
|
|
read(stream, buffer, offset, length, pos) {
|
|
var bytesRead = 0;
|
|
for (var i = 0; i < length; i++) {
|
|
var result;
|
|
try {
|
|
result = input();
|
|
} catch (e) {
|
|
throw new FS.ErrnoError(29);
|
|
}
|
|
if (result === void 0 && bytesRead === 0) throw new FS.ErrnoError(6);
|
|
if (result === null || result === void 0) break;
|
|
bytesRead++;
|
|
buffer[offset + i] = result;
|
|
}
|
|
if (bytesRead) stream.node.atime = Date.now();
|
|
return bytesRead;
|
|
},
|
|
write(stream, buffer, offset, length, pos) {
|
|
for (var i = 0; i < length; i++) try {
|
|
output(buffer[offset + i]);
|
|
} catch (e) {
|
|
throw new FS.ErrnoError(29);
|
|
}
|
|
if (length) stream.node.mtime = stream.node.ctime = Date.now();
|
|
return i;
|
|
}
|
|
});
|
|
return FS.mkdev(path, mode, dev);
|
|
},
|
|
forceLoadFile(obj) {
|
|
if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true;
|
|
if (globalThis.XMLHttpRequest) abort("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.");
|
|
else try {
|
|
obj.contents = readBinary(obj.url);
|
|
} catch (e) {
|
|
throw new FS.ErrnoError(29);
|
|
}
|
|
},
|
|
createLazyFile(parent, name, url, canRead, canWrite) {
|
|
class LazyUint8Array {
|
|
lengthKnown = false;
|
|
chunks = [];
|
|
get(idx) {
|
|
if (idx > this.length - 1 || idx < 0) return;
|
|
var chunkOffset = idx % this.chunkSize;
|
|
var chunkNum = idx / this.chunkSize | 0;
|
|
return this.getter(chunkNum)[chunkOffset];
|
|
}
|
|
setDataGetter(getter) {
|
|
this.getter = getter;
|
|
}
|
|
cacheLength() {
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.open("HEAD", url, false);
|
|
xhr.send(null);
|
|
if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) abort("Couldn't load " + url + ". Status: " + xhr.status);
|
|
var datalength = Number(xhr.getResponseHeader("Content-length"));
|
|
var header;
|
|
var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes";
|
|
var usesGzip = (header = xhr.getResponseHeader("Content-Encoding")) && header === "gzip";
|
|
var chunkSize = 1024 * 1024;
|
|
if (!hasByteServing) chunkSize = datalength;
|
|
var doXHR = (from, to) => {
|
|
if (from > to) abort("invalid range (" + from + ", " + to + ") or no bytes requested!");
|
|
if (to > datalength - 1) abort("only " + datalength + " bytes available! programmer error!");
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.open("GET", url, false);
|
|
if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to);
|
|
xhr.responseType = "arraybuffer";
|
|
if (xhr.overrideMimeType) xhr.overrideMimeType("text/plain; charset=x-user-defined");
|
|
xhr.send(null);
|
|
if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) abort("Couldn't load " + url + ". Status: " + xhr.status);
|
|
if (xhr.response !== void 0) return new Uint8Array(xhr.response || []);
|
|
return intArrayFromString(xhr.responseText || "", true);
|
|
};
|
|
var lazyArray = this;
|
|
lazyArray.setDataGetter((chunkNum) => {
|
|
var start = chunkNum * chunkSize;
|
|
var end = (chunkNum + 1) * chunkSize - 1;
|
|
end = Math.min(end, datalength - 1);
|
|
if (typeof lazyArray.chunks[chunkNum] == "undefined") lazyArray.chunks[chunkNum] = doXHR(start, end);
|
|
if (typeof lazyArray.chunks[chunkNum] == "undefined") abort("doXHR failed!");
|
|
return lazyArray.chunks[chunkNum];
|
|
});
|
|
if (usesGzip || !datalength) {
|
|
chunkSize = datalength = 1;
|
|
datalength = this.getter(0).length;
|
|
chunkSize = datalength;
|
|
out("LazyFiles on gzip forces download of the whole file when length is accessed");
|
|
}
|
|
this._length = datalength;
|
|
this._chunkSize = chunkSize;
|
|
this.lengthKnown = true;
|
|
}
|
|
get length() {
|
|
if (!this.lengthKnown) this.cacheLength();
|
|
return this._length;
|
|
}
|
|
get chunkSize() {
|
|
if (!this.lengthKnown) this.cacheLength();
|
|
return this._chunkSize;
|
|
}
|
|
}
|
|
if (globalThis.XMLHttpRequest) {
|
|
if (!ENVIRONMENT_IS_WORKER) abort("Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc");
|
|
var properties = {
|
|
isDevice: false,
|
|
contents: new LazyUint8Array()
|
|
};
|
|
} else var properties = {
|
|
isDevice: false,
|
|
url
|
|
};
|
|
var node = FS.createFile(parent, name, properties, canRead, canWrite);
|
|
if (properties.contents) node.contents = properties.contents;
|
|
else if (properties.url) {
|
|
node.contents = null;
|
|
node.url = properties.url;
|
|
}
|
|
Object.defineProperties(node, { usedBytes: { get: function() {
|
|
return this.contents.length;
|
|
} } });
|
|
var stream_ops = {};
|
|
for (const [key, fn] of Object.entries(node.stream_ops)) stream_ops[key] = (...args) => {
|
|
FS.forceLoadFile(node);
|
|
return fn(...args);
|
|
};
|
|
function writeChunks(stream, buffer, offset, length, position) {
|
|
var contents = stream.node.contents;
|
|
if (position >= contents.length) return 0;
|
|
var size = Math.min(contents.length - position, length);
|
|
if (contents.slice) for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i];
|
|
else for (var i = 0; i < size; i++) buffer[offset + i] = contents.get(position + i);
|
|
return size;
|
|
}
|
|
stream_ops.read = (stream, buffer, offset, length, position) => {
|
|
FS.forceLoadFile(node);
|
|
return writeChunks(stream, buffer, offset, length, position);
|
|
};
|
|
stream_ops.mmap = (stream, length, position, prot, flags) => {
|
|
FS.forceLoadFile(node);
|
|
var ptr = mmapAlloc(length);
|
|
if (!ptr) throw new FS.ErrnoError(48);
|
|
writeChunks(stream, HEAP8, ptr, length, position);
|
|
return {
|
|
ptr,
|
|
allocated: true
|
|
};
|
|
};
|
|
node.stream_ops = stream_ops;
|
|
return node;
|
|
}
|
|
};
|
|
var UTF8ToString = (ptr, maxBytesToRead, ignoreNul) => {
|
|
if (!ptr) return "";
|
|
var end = findStringEnd(HEAPU8, ptr, maxBytesToRead, ignoreNul);
|
|
return UTF8Decoder.decode(HEAPU8.subarray(ptr, end));
|
|
};
|
|
var SYSCALLS = {
|
|
calculateAt(dirfd, path, allowEmpty) {
|
|
if (PATH.isAbs(path)) return path;
|
|
var dir;
|
|
if (dirfd === -100) dir = FS.cwd();
|
|
else dir = SYSCALLS.getStreamFromFD(dirfd).path;
|
|
if (path.length == 0) {
|
|
if (!allowEmpty) throw new FS.ErrnoError(44);
|
|
return dir;
|
|
}
|
|
return dir + "/" + path;
|
|
},
|
|
writeStat(buf, stat) {
|
|
HEAPU32[buf >> 2] = stat.dev;
|
|
HEAPU32[buf + 4 >> 2] = stat.mode;
|
|
HEAPU32[buf + 8 >> 2] = stat.nlink;
|
|
HEAPU32[buf + 12 >> 2] = stat.uid;
|
|
HEAPU32[buf + 16 >> 2] = stat.gid;
|
|
HEAPU32[buf + 20 >> 2] = stat.rdev;
|
|
HEAP64[buf + 24 >> 3] = BigInt(stat.size);
|
|
HEAP32[buf + 32 >> 2] = 4096;
|
|
HEAP32[buf + 36 >> 2] = stat.blocks;
|
|
var atime = stat.atime.getTime();
|
|
var mtime = stat.mtime.getTime();
|
|
var ctime = stat.ctime.getTime();
|
|
HEAP64[buf + 40 >> 3] = BigInt(Math.floor(atime / 1e3));
|
|
HEAPU32[buf + 48 >> 2] = atime % 1e3 * 1e3 * 1e3;
|
|
HEAP64[buf + 56 >> 3] = BigInt(Math.floor(mtime / 1e3));
|
|
HEAPU32[buf + 64 >> 2] = mtime % 1e3 * 1e3 * 1e3;
|
|
HEAP64[buf + 72 >> 3] = BigInt(Math.floor(ctime / 1e3));
|
|
HEAPU32[buf + 80 >> 2] = ctime % 1e3 * 1e3 * 1e3;
|
|
HEAP64[buf + 88 >> 3] = BigInt(stat.ino);
|
|
return 0;
|
|
},
|
|
writeStatFs(buf, stats) {
|
|
HEAPU32[buf + 4 >> 2] = stats.bsize;
|
|
HEAPU32[buf + 60 >> 2] = stats.bsize;
|
|
HEAP64[buf + 8 >> 3] = BigInt(stats.blocks);
|
|
HEAP64[buf + 16 >> 3] = BigInt(stats.bfree);
|
|
HEAP64[buf + 24 >> 3] = BigInt(stats.bavail);
|
|
HEAP64[buf + 32 >> 3] = BigInt(stats.files);
|
|
HEAP64[buf + 40 >> 3] = BigInt(stats.ffree);
|
|
HEAPU32[buf + 48 >> 2] = stats.fsid;
|
|
HEAPU32[buf + 64 >> 2] = stats.flags;
|
|
HEAPU32[buf + 56 >> 2] = stats.namelen;
|
|
},
|
|
doMsync(addr, stream, len, flags, offset) {
|
|
if (!FS.isFile(stream.node.mode)) throw new FS.ErrnoError(43);
|
|
if (flags & 2) return 0;
|
|
var buffer = HEAPU8.slice(addr, addr + len);
|
|
FS.msync(stream, buffer, offset, len, flags);
|
|
},
|
|
getStreamFromFD(fd) {
|
|
return FS.getStreamChecked(fd);
|
|
},
|
|
varargs: void 0,
|
|
getStr(ptr) {
|
|
return UTF8ToString(ptr);
|
|
}
|
|
};
|
|
function ___syscall_chmod(path, mode) {
|
|
try {
|
|
path = SYSCALLS.getStr(path);
|
|
FS.chmod(path, mode);
|
|
return 0;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
function ___syscall_faccessat(dirfd, path, amode, flags) {
|
|
try {
|
|
path = SYSCALLS.getStr(path);
|
|
path = SYSCALLS.calculateAt(dirfd, path);
|
|
if (amode & -8) return -28;
|
|
var node = FS.lookupPath(path, { follow: true }).node;
|
|
if (!node) return -44;
|
|
var perms = "";
|
|
if (amode & 4) perms += "r";
|
|
if (amode & 2) perms += "w";
|
|
if (amode & 1) perms += "x";
|
|
if (perms && FS.nodePermissions(node, perms)) return -2;
|
|
return 0;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
function ___syscall_fchmod(fd, mode) {
|
|
try {
|
|
FS.fchmod(fd, mode);
|
|
return 0;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
function ___syscall_fchown32(fd, owner, group) {
|
|
try {
|
|
FS.fchown(fd, owner, group);
|
|
return 0;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
var syscallGetVarargI = () => {
|
|
var ret = HEAP32[+SYSCALLS.varargs >> 2];
|
|
SYSCALLS.varargs += 4;
|
|
return ret;
|
|
};
|
|
var syscallGetVarargP = syscallGetVarargI;
|
|
function ___syscall_fcntl64(fd, cmd, varargs) {
|
|
SYSCALLS.varargs = varargs;
|
|
try {
|
|
var stream = SYSCALLS.getStreamFromFD(fd);
|
|
switch (cmd) {
|
|
case 0:
|
|
var arg = syscallGetVarargI();
|
|
if (arg < 0) return -28;
|
|
while (FS.streams[arg]) arg++;
|
|
return FS.dupStream(stream, arg).fd;
|
|
case 1:
|
|
case 2: return 0;
|
|
case 3: return stream.flags;
|
|
case 4:
|
|
var arg = syscallGetVarargI();
|
|
stream.flags |= arg;
|
|
return 0;
|
|
case 12:
|
|
var arg = syscallGetVarargP();
|
|
var offset = 0;
|
|
HEAP16[arg + offset >> 1] = 2;
|
|
return 0;
|
|
case 13:
|
|
case 14: return 0;
|
|
}
|
|
return -28;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
function ___syscall_fstat64(fd, buf) {
|
|
try {
|
|
return SYSCALLS.writeStat(buf, FS.fstat(fd));
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
var INT53_MAX = 9007199254740992;
|
|
var INT53_MIN = -9007199254740992;
|
|
var bigintToI53Checked = (num) => num < INT53_MIN || num > INT53_MAX ? NaN : Number(num);
|
|
function ___syscall_ftruncate64(fd, length) {
|
|
length = bigintToI53Checked(length);
|
|
try {
|
|
if (isNaN(length)) return -61;
|
|
FS.ftruncate(fd, length);
|
|
return 0;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
var stringToUTF8 = (str, outPtr, maxBytesToWrite) => {
|
|
return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite);
|
|
};
|
|
function ___syscall_getcwd(buf, size) {
|
|
try {
|
|
if (size === 0) return -28;
|
|
var cwd = FS.cwd();
|
|
var cwdLengthInBytes = lengthBytesUTF8(cwd) + 1;
|
|
if (size < cwdLengthInBytes) return -68;
|
|
stringToUTF8(cwd, buf, size);
|
|
return cwdLengthInBytes;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
function ___syscall_ioctl(fd, op, varargs) {
|
|
SYSCALLS.varargs = varargs;
|
|
try {
|
|
var stream = SYSCALLS.getStreamFromFD(fd);
|
|
switch (op) {
|
|
case 21509:
|
|
if (!stream.tty) return -59;
|
|
return 0;
|
|
case 21505:
|
|
if (!stream.tty) return -59;
|
|
if (stream.tty.ops.ioctl_tcgets) {
|
|
var termios = stream.tty.ops.ioctl_tcgets(stream);
|
|
var argp = syscallGetVarargP();
|
|
HEAP32[argp >> 2] = termios.c_iflag || 0;
|
|
HEAP32[argp + 4 >> 2] = termios.c_oflag || 0;
|
|
HEAP32[argp + 8 >> 2] = termios.c_cflag || 0;
|
|
HEAP32[argp + 12 >> 2] = termios.c_lflag || 0;
|
|
for (var i = 0; i < 32; i++) HEAP8[argp + i + 17] = termios.c_cc[i] || 0;
|
|
return 0;
|
|
}
|
|
return 0;
|
|
case 21510:
|
|
case 21511:
|
|
case 21512:
|
|
if (!stream.tty) return -59;
|
|
return 0;
|
|
case 21506:
|
|
case 21507:
|
|
case 21508:
|
|
if (!stream.tty) return -59;
|
|
if (stream.tty.ops.ioctl_tcsets) {
|
|
var argp = syscallGetVarargP();
|
|
var c_iflag = HEAP32[argp >> 2];
|
|
var c_oflag = HEAP32[argp + 4 >> 2];
|
|
var c_cflag = HEAP32[argp + 8 >> 2];
|
|
var c_lflag = HEAP32[argp + 12 >> 2];
|
|
var c_cc = [];
|
|
for (var i = 0; i < 32; i++) c_cc.push(HEAP8[argp + i + 17]);
|
|
return stream.tty.ops.ioctl_tcsets(stream.tty, op, {
|
|
c_iflag,
|
|
c_oflag,
|
|
c_cflag,
|
|
c_lflag,
|
|
c_cc
|
|
});
|
|
}
|
|
return 0;
|
|
case 21519:
|
|
if (!stream.tty) return -59;
|
|
var argp = syscallGetVarargP();
|
|
HEAP32[argp >> 2] = 0;
|
|
return 0;
|
|
case 21520:
|
|
if (!stream.tty) return -59;
|
|
return -28;
|
|
case 21537:
|
|
case 21531:
|
|
var argp = syscallGetVarargP();
|
|
return FS.ioctl(stream, op, argp);
|
|
case 21523:
|
|
if (!stream.tty) return -59;
|
|
if (stream.tty.ops.ioctl_tiocgwinsz) {
|
|
var winsize = stream.tty.ops.ioctl_tiocgwinsz(stream.tty);
|
|
var argp = syscallGetVarargP();
|
|
HEAP16[argp >> 1] = winsize[0];
|
|
HEAP16[argp + 2 >> 1] = winsize[1];
|
|
}
|
|
return 0;
|
|
case 21524:
|
|
if (!stream.tty) return -59;
|
|
return 0;
|
|
case 21515:
|
|
if (!stream.tty) return -59;
|
|
return 0;
|
|
default: return -28;
|
|
}
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
function ___syscall_lstat64(path, buf) {
|
|
try {
|
|
path = SYSCALLS.getStr(path);
|
|
return SYSCALLS.writeStat(buf, FS.lstat(path));
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
function ___syscall_mkdirat(dirfd, path, mode) {
|
|
try {
|
|
path = SYSCALLS.getStr(path);
|
|
path = SYSCALLS.calculateAt(dirfd, path);
|
|
FS.mkdir(path, mode, 0);
|
|
return 0;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
function ___syscall_newfstatat(dirfd, path, buf, flags) {
|
|
try {
|
|
path = SYSCALLS.getStr(path);
|
|
var nofollow = flags & 256;
|
|
var allowEmpty = flags & 4096;
|
|
flags = flags & -6401;
|
|
path = SYSCALLS.calculateAt(dirfd, path, allowEmpty);
|
|
return SYSCALLS.writeStat(buf, nofollow ? FS.lstat(path) : FS.stat(path));
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
function ___syscall_openat(dirfd, path, flags, varargs) {
|
|
SYSCALLS.varargs = varargs;
|
|
try {
|
|
path = SYSCALLS.getStr(path);
|
|
path = SYSCALLS.calculateAt(dirfd, path);
|
|
var mode = varargs ? syscallGetVarargI() : 0;
|
|
return FS.open(path, flags, mode).fd;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
function ___syscall_readlinkat(dirfd, path, buf, bufsize) {
|
|
try {
|
|
path = SYSCALLS.getStr(path);
|
|
path = SYSCALLS.calculateAt(dirfd, path);
|
|
if (bufsize <= 0) return -28;
|
|
var ret = FS.readlink(path);
|
|
var len = Math.min(bufsize, lengthBytesUTF8(ret));
|
|
var endChar = HEAP8[buf + len];
|
|
stringToUTF8(ret, buf, bufsize + 1);
|
|
HEAP8[buf + len] = endChar;
|
|
return len;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
function ___syscall_rmdir(path) {
|
|
try {
|
|
path = SYSCALLS.getStr(path);
|
|
FS.rmdir(path);
|
|
return 0;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
function ___syscall_stat64(path, buf) {
|
|
try {
|
|
path = SYSCALLS.getStr(path);
|
|
return SYSCALLS.writeStat(buf, FS.stat(path));
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
function ___syscall_unlinkat(dirfd, path, flags) {
|
|
try {
|
|
path = SYSCALLS.getStr(path);
|
|
path = SYSCALLS.calculateAt(dirfd, path);
|
|
if (!flags) FS.unlink(path);
|
|
else if (flags === 512) FS.rmdir(path);
|
|
else return -28;
|
|
return 0;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
var readI53FromI64 = (ptr) => {
|
|
return HEAPU32[ptr >> 2] + HEAP32[ptr + 4 >> 2] * 4294967296;
|
|
};
|
|
function ___syscall_utimensat(dirfd, path, times, flags) {
|
|
try {
|
|
path = SYSCALLS.getStr(path);
|
|
path = SYSCALLS.calculateAt(dirfd, path, true);
|
|
var now = Date.now(), atime, mtime;
|
|
if (!times) {
|
|
atime = now;
|
|
mtime = now;
|
|
} else {
|
|
var seconds = readI53FromI64(times);
|
|
var nanoseconds = HEAP32[times + 8 >> 2];
|
|
if (nanoseconds == 1073741823) atime = now;
|
|
else if (nanoseconds == 1073741822) atime = null;
|
|
else atime = seconds * 1e3 + nanoseconds / (1e3 * 1e3);
|
|
times += 16;
|
|
seconds = readI53FromI64(times);
|
|
nanoseconds = HEAP32[times + 8 >> 2];
|
|
if (nanoseconds == 1073741823) mtime = now;
|
|
else if (nanoseconds == 1073741822) mtime = null;
|
|
else mtime = seconds * 1e3 + nanoseconds / (1e3 * 1e3);
|
|
}
|
|
if ((mtime ?? atime) !== null) FS.utime(path, atime, mtime);
|
|
return 0;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
var isLeapYear = (year) => year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
var MONTH_DAYS_LEAP_CUMULATIVE = [
|
|
0,
|
|
31,
|
|
60,
|
|
91,
|
|
121,
|
|
152,
|
|
182,
|
|
213,
|
|
244,
|
|
274,
|
|
305,
|
|
335
|
|
];
|
|
var MONTH_DAYS_REGULAR_CUMULATIVE = [
|
|
0,
|
|
31,
|
|
59,
|
|
90,
|
|
120,
|
|
151,
|
|
181,
|
|
212,
|
|
243,
|
|
273,
|
|
304,
|
|
334
|
|
];
|
|
var ydayFromDate = (date) => {
|
|
return (isLeapYear(date.getFullYear()) ? MONTH_DAYS_LEAP_CUMULATIVE : MONTH_DAYS_REGULAR_CUMULATIVE)[date.getMonth()] + date.getDate() - 1;
|
|
};
|
|
function __localtime_js(time, tmPtr) {
|
|
time = bigintToI53Checked(time);
|
|
var date = /* @__PURE__ */ new Date(time * 1e3);
|
|
HEAP32[tmPtr >> 2] = date.getSeconds();
|
|
HEAP32[tmPtr + 4 >> 2] = date.getMinutes();
|
|
HEAP32[tmPtr + 8 >> 2] = date.getHours();
|
|
HEAP32[tmPtr + 12 >> 2] = date.getDate();
|
|
HEAP32[tmPtr + 16 >> 2] = date.getMonth();
|
|
HEAP32[tmPtr + 20 >> 2] = date.getFullYear() - 1900;
|
|
HEAP32[tmPtr + 24 >> 2] = date.getDay();
|
|
var yday = ydayFromDate(date) | 0;
|
|
HEAP32[tmPtr + 28 >> 2] = yday;
|
|
HEAP32[tmPtr + 36 >> 2] = -(date.getTimezoneOffset() * 60);
|
|
var start = new Date(date.getFullYear(), 0, 1);
|
|
var summerOffset = new Date(date.getFullYear(), 6, 1).getTimezoneOffset();
|
|
var winterOffset = start.getTimezoneOffset();
|
|
var dst = (summerOffset != winterOffset && date.getTimezoneOffset() == Math.min(winterOffset, summerOffset)) | 0;
|
|
HEAP32[tmPtr + 32 >> 2] = dst;
|
|
}
|
|
function __mmap_js(len, prot, flags, fd, offset, allocated, addr) {
|
|
offset = bigintToI53Checked(offset);
|
|
try {
|
|
var stream = SYSCALLS.getStreamFromFD(fd);
|
|
var res = FS.mmap(stream, len, offset, prot, flags);
|
|
var ptr = res.ptr;
|
|
HEAP32[allocated >> 2] = res.allocated;
|
|
HEAPU32[addr >> 2] = ptr;
|
|
return 0;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
function __munmap_js(addr, len, prot, flags, fd, offset) {
|
|
offset = bigintToI53Checked(offset);
|
|
try {
|
|
var stream = SYSCALLS.getStreamFromFD(fd);
|
|
if (prot & 2) SYSCALLS.doMsync(addr, stream, len, flags, offset);
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return -e.errno;
|
|
}
|
|
}
|
|
var __tzset_js = (timezone, daylight, std_name, dst_name) => {
|
|
var currentYear = (/* @__PURE__ */ new Date()).getFullYear();
|
|
var winter = new Date(currentYear, 0, 1);
|
|
var summer = new Date(currentYear, 6, 1);
|
|
var winterOffset = winter.getTimezoneOffset();
|
|
var summerOffset = summer.getTimezoneOffset();
|
|
var stdTimezoneOffset = Math.max(winterOffset, summerOffset);
|
|
HEAPU32[timezone >> 2] = stdTimezoneOffset * 60;
|
|
HEAP32[daylight >> 2] = Number(winterOffset != summerOffset);
|
|
var extractZone = (timezoneOffset) => {
|
|
var sign = timezoneOffset >= 0 ? "-" : "+";
|
|
var absOffset = Math.abs(timezoneOffset);
|
|
return `UTC${sign}${String(Math.floor(absOffset / 60)).padStart(2, "0")}${String(absOffset % 60).padStart(2, "0")}`;
|
|
};
|
|
var winterName = extractZone(winterOffset);
|
|
var summerName = extractZone(summerOffset);
|
|
if (summerOffset < winterOffset) {
|
|
stringToUTF8(winterName, std_name, 17);
|
|
stringToUTF8(summerName, dst_name, 17);
|
|
} else {
|
|
stringToUTF8(winterName, dst_name, 17);
|
|
stringToUTF8(summerName, std_name, 17);
|
|
}
|
|
};
|
|
var _emscripten_get_now = () => performance.now();
|
|
var _emscripten_date_now = () => Date.now();
|
|
var nowIsMonotonic = 1;
|
|
var checkWasiClock = (clock_id) => clock_id >= 0 && clock_id <= 3;
|
|
function _clock_time_get(clk_id, ignored_precision, ptime) {
|
|
ignored_precision = bigintToI53Checked(ignored_precision);
|
|
if (!checkWasiClock(clk_id)) return 28;
|
|
var now;
|
|
if (clk_id === 0) now = _emscripten_date_now();
|
|
else if (nowIsMonotonic) now = _emscripten_get_now();
|
|
else return 52;
|
|
var nsec = Math.round(now * 1e3 * 1e3);
|
|
HEAP64[ptime >> 3] = BigInt(nsec);
|
|
return 0;
|
|
}
|
|
var getHeapMax = () => 2147483648;
|
|
var _emscripten_get_heap_max = () => getHeapMax();
|
|
var growMemory = (size) => {
|
|
var pages = (size - wasmMemory.buffer.byteLength + 65535) / 65536 | 0;
|
|
try {
|
|
wasmMemory.grow(pages);
|
|
updateMemoryViews();
|
|
return 1;
|
|
} catch (e) {}
|
|
};
|
|
var _emscripten_resize_heap = (requestedSize) => {
|
|
var oldSize = HEAPU8.length;
|
|
requestedSize >>>= 0;
|
|
var maxHeapSize = getHeapMax();
|
|
if (requestedSize > maxHeapSize) return false;
|
|
for (var cutDown = 1; cutDown <= 4; cutDown *= 2) {
|
|
var overGrownHeapSize = oldSize * (1 + .2 / cutDown);
|
|
overGrownHeapSize = Math.min(overGrownHeapSize, requestedSize + 100663296);
|
|
if (growMemory(Math.min(maxHeapSize, alignMemory(Math.max(requestedSize, overGrownHeapSize), 65536)))) return true;
|
|
}
|
|
return false;
|
|
};
|
|
var ENV = {};
|
|
var getExecutableName = () => thisProgram || "./this.program";
|
|
var getEnvStrings = () => {
|
|
if (!getEnvStrings.strings) {
|
|
var lang = (globalThis.navigator?.language ?? "C").replace("-", "_") + ".UTF-8";
|
|
var env = {
|
|
"USER": "web_user",
|
|
"LOGNAME": "web_user",
|
|
"PATH": "/",
|
|
"PWD": "/",
|
|
"HOME": "/home/web_user",
|
|
"LANG": lang,
|
|
"_": getExecutableName()
|
|
};
|
|
for (var x in ENV) if (ENV[x] === void 0) delete env[x];
|
|
else env[x] = ENV[x];
|
|
var strings = [];
|
|
for (var x in env) strings.push(`${x}=${env[x]}`);
|
|
getEnvStrings.strings = strings;
|
|
}
|
|
return getEnvStrings.strings;
|
|
};
|
|
var _environ_get = (__environ, environ_buf) => {
|
|
var bufSize = 0;
|
|
var envp = 0;
|
|
for (var string of getEnvStrings()) {
|
|
var ptr = environ_buf + bufSize;
|
|
HEAPU32[__environ + envp >> 2] = ptr;
|
|
bufSize += stringToUTF8(string, ptr, Infinity) + 1;
|
|
envp += 4;
|
|
}
|
|
return 0;
|
|
};
|
|
var _environ_sizes_get = (penviron_count, penviron_buf_size) => {
|
|
var strings = getEnvStrings();
|
|
HEAPU32[penviron_count >> 2] = strings.length;
|
|
var bufSize = 0;
|
|
for (var string of strings) bufSize += lengthBytesUTF8(string) + 1;
|
|
HEAPU32[penviron_buf_size >> 2] = bufSize;
|
|
return 0;
|
|
};
|
|
function _fd_close(fd) {
|
|
try {
|
|
var stream = SYSCALLS.getStreamFromFD(fd);
|
|
FS.close(stream);
|
|
return 0;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return e.errno;
|
|
}
|
|
}
|
|
function _fd_fdstat_get(fd, pbuf) {
|
|
try {
|
|
var rightsBase = 0;
|
|
var rightsInheriting = 0;
|
|
var flags = 0;
|
|
var stream = SYSCALLS.getStreamFromFD(fd);
|
|
var type = stream.tty ? 2 : FS.isDir(stream.mode) ? 3 : FS.isLink(stream.mode) ? 7 : 4;
|
|
HEAP8[pbuf] = type;
|
|
HEAP16[pbuf + 2 >> 1] = flags;
|
|
HEAP64[pbuf + 8 >> 3] = BigInt(rightsBase);
|
|
HEAP64[pbuf + 16 >> 3] = BigInt(rightsInheriting);
|
|
return 0;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return e.errno;
|
|
}
|
|
}
|
|
var doReadv = (stream, iov, iovcnt, offset) => {
|
|
var ret = 0;
|
|
for (var i = 0; i < iovcnt; i++) {
|
|
var ptr = HEAPU32[iov >> 2];
|
|
var len = HEAPU32[iov + 4 >> 2];
|
|
iov += 8;
|
|
var curr = FS.read(stream, HEAP8, ptr, len, offset);
|
|
if (curr < 0) return -1;
|
|
ret += curr;
|
|
if (curr < len) break;
|
|
if (typeof offset != "undefined") offset += curr;
|
|
}
|
|
return ret;
|
|
};
|
|
function _fd_read(fd, iov, iovcnt, pnum) {
|
|
try {
|
|
var num = doReadv(SYSCALLS.getStreamFromFD(fd), iov, iovcnt);
|
|
HEAPU32[pnum >> 2] = num;
|
|
return 0;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return e.errno;
|
|
}
|
|
}
|
|
function _fd_seek(fd, offset, whence, newOffset) {
|
|
offset = bigintToI53Checked(offset);
|
|
try {
|
|
if (isNaN(offset)) return 61;
|
|
var stream = SYSCALLS.getStreamFromFD(fd);
|
|
FS.llseek(stream, offset, whence);
|
|
HEAP64[newOffset >> 3] = BigInt(stream.position);
|
|
if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null;
|
|
return 0;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return e.errno;
|
|
}
|
|
}
|
|
function _fd_sync(fd) {
|
|
try {
|
|
var stream = SYSCALLS.getStreamFromFD(fd);
|
|
return stream.stream_ops?.fsync?.(stream);
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return e.errno;
|
|
}
|
|
}
|
|
var doWritev = (stream, iov, iovcnt, offset) => {
|
|
var ret = 0;
|
|
for (var i = 0; i < iovcnt; i++) {
|
|
var ptr = HEAPU32[iov >> 2];
|
|
var len = HEAPU32[iov + 4 >> 2];
|
|
iov += 8;
|
|
var curr = FS.write(stream, HEAP8, ptr, len, offset);
|
|
if (curr < 0) return -1;
|
|
ret += curr;
|
|
if (curr < len) break;
|
|
if (typeof offset != "undefined") offset += curr;
|
|
}
|
|
return ret;
|
|
};
|
|
function _fd_write(fd, iov, iovcnt, pnum) {
|
|
try {
|
|
var num = doWritev(SYSCALLS.getStreamFromFD(fd), iov, iovcnt);
|
|
HEAPU32[pnum >> 2] = num;
|
|
return 0;
|
|
} catch (e) {
|
|
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e;
|
|
return e.errno;
|
|
}
|
|
}
|
|
FS.createPreloadedFile = FS_createPreloadedFile;
|
|
FS.preloadFile = FS_preloadFile;
|
|
FS.staticInit();
|
|
initMemory();
|
|
if (Module["noExitRuntime"]) Module["noExitRuntime"];
|
|
if (Module["preloadPlugins"]) preloadPlugins = Module["preloadPlugins"];
|
|
if (Module["print"]) out = Module["print"];
|
|
if (Module["printErr"]) err = Module["printErr"];
|
|
if (Module["wasmBinary"]) wasmBinary = Module["wasmBinary"];
|
|
if (Module["arguments"]) Module["arguments"];
|
|
if (Module["thisProgram"]) thisProgram = Module["thisProgram"];
|
|
if (Module["preInit"]) {
|
|
if (typeof Module["preInit"] == "function") Module["preInit"] = [Module["preInit"]];
|
|
while (Module["preInit"].length > 0) Module["preInit"].shift()();
|
|
}
|
|
Module["wasmMemory"] = wasmMemory;
|
|
var _emscripten_builtin_memalign;
|
|
function assignWasmExports(wasmExports) {
|
|
Module["_sqlite3_status64"] = wasmExports["sqlite3_status64"];
|
|
Module["_sqlite3_status"] = wasmExports["sqlite3_status"];
|
|
Module["_sqlite3_db_status64"] = wasmExports["sqlite3_db_status64"];
|
|
Module["_sqlite3_msize"] = wasmExports["sqlite3_msize"];
|
|
Module["_sqlite3_db_status"] = wasmExports["sqlite3_db_status"];
|
|
Module["_sqlite3_vfs_find"] = wasmExports["sqlite3_vfs_find"];
|
|
Module["_sqlite3_initialize"] = wasmExports["sqlite3_initialize"];
|
|
Module["_sqlite3_malloc"] = wasmExports["sqlite3_malloc"];
|
|
Module["_sqlite3_free"] = wasmExports["sqlite3_free"];
|
|
Module["_sqlite3_vfs_register"] = wasmExports["sqlite3_vfs_register"];
|
|
Module["_sqlite3_vfs_unregister"] = wasmExports["sqlite3_vfs_unregister"];
|
|
Module["_sqlite3_malloc64"] = wasmExports["sqlite3_malloc64"];
|
|
Module["_sqlite3_realloc"] = wasmExports["sqlite3_realloc"];
|
|
Module["_sqlite3_realloc64"] = wasmExports["sqlite3_realloc64"];
|
|
Module["_sqlite3_value_text"] = wasmExports["sqlite3_value_text"];
|
|
Module["_sqlite3_randomness"] = wasmExports["sqlite3_randomness"];
|
|
Module["_sqlite3_stricmp"] = wasmExports["sqlite3_stricmp"];
|
|
Module["_sqlite3_strnicmp"] = wasmExports["sqlite3_strnicmp"];
|
|
Module["_sqlite3_uri_parameter"] = wasmExports["sqlite3_uri_parameter"];
|
|
Module["_sqlite3_uri_boolean"] = wasmExports["sqlite3_uri_boolean"];
|
|
Module["_sqlite3_serialize"] = wasmExports["sqlite3_serialize"];
|
|
Module["_sqlite3_prepare_v2"] = wasmExports["sqlite3_prepare_v2"];
|
|
Module["_sqlite3_step"] = wasmExports["sqlite3_step"];
|
|
Module["_sqlite3_column_int64"] = wasmExports["sqlite3_column_int64"];
|
|
Module["_sqlite3_reset"] = wasmExports["sqlite3_reset"];
|
|
Module["_sqlite3_exec"] = wasmExports["sqlite3_exec"];
|
|
Module["_sqlite3_column_int"] = wasmExports["sqlite3_column_int"];
|
|
Module["_sqlite3_finalize"] = wasmExports["sqlite3_finalize"];
|
|
Module["_sqlite3_file_control"] = wasmExports["sqlite3_file_control"];
|
|
Module["_sqlite3_column_name"] = wasmExports["sqlite3_column_name"];
|
|
Module["_sqlite3_column_text"] = wasmExports["sqlite3_column_text"];
|
|
Module["_sqlite3_column_type"] = wasmExports["sqlite3_column_type"];
|
|
Module["_sqlite3_errmsg"] = wasmExports["sqlite3_errmsg"];
|
|
Module["_sqlite3_deserialize"] = wasmExports["sqlite3_deserialize"];
|
|
Module["_sqlite3_clear_bindings"] = wasmExports["sqlite3_clear_bindings"];
|
|
Module["_sqlite3_value_blob"] = wasmExports["sqlite3_value_blob"];
|
|
Module["_sqlite3_value_bytes"] = wasmExports["sqlite3_value_bytes"];
|
|
Module["_sqlite3_value_double"] = wasmExports["sqlite3_value_double"];
|
|
Module["_sqlite3_value_int"] = wasmExports["sqlite3_value_int"];
|
|
Module["_sqlite3_value_int64"] = wasmExports["sqlite3_value_int64"];
|
|
Module["_sqlite3_value_subtype"] = wasmExports["sqlite3_value_subtype"];
|
|
Module["_sqlite3_value_pointer"] = wasmExports["sqlite3_value_pointer"];
|
|
Module["_sqlite3_value_type"] = wasmExports["sqlite3_value_type"];
|
|
Module["_sqlite3_value_nochange"] = wasmExports["sqlite3_value_nochange"];
|
|
Module["_sqlite3_value_frombind"] = wasmExports["sqlite3_value_frombind"];
|
|
Module["_sqlite3_value_dup"] = wasmExports["sqlite3_value_dup"];
|
|
Module["_sqlite3_value_free"] = wasmExports["sqlite3_value_free"];
|
|
Module["_sqlite3_result_blob"] = wasmExports["sqlite3_result_blob"];
|
|
Module["_sqlite3_result_error_toobig"] = wasmExports["sqlite3_result_error_toobig"];
|
|
Module["_sqlite3_result_error_nomem"] = wasmExports["sqlite3_result_error_nomem"];
|
|
Module["_sqlite3_result_double"] = wasmExports["sqlite3_result_double"];
|
|
Module["_sqlite3_result_error"] = wasmExports["sqlite3_result_error"];
|
|
Module["_sqlite3_result_int"] = wasmExports["sqlite3_result_int"];
|
|
Module["_sqlite3_result_int64"] = wasmExports["sqlite3_result_int64"];
|
|
Module["_sqlite3_result_null"] = wasmExports["sqlite3_result_null"];
|
|
Module["_sqlite3_result_pointer"] = wasmExports["sqlite3_result_pointer"];
|
|
Module["_sqlite3_result_subtype"] = wasmExports["sqlite3_result_subtype"];
|
|
Module["_sqlite3_result_text"] = wasmExports["sqlite3_result_text"];
|
|
Module["_sqlite3_result_zeroblob"] = wasmExports["sqlite3_result_zeroblob"];
|
|
Module["_sqlite3_result_zeroblob64"] = wasmExports["sqlite3_result_zeroblob64"];
|
|
Module["_sqlite3_result_error_code"] = wasmExports["sqlite3_result_error_code"];
|
|
Module["_sqlite3_user_data"] = wasmExports["sqlite3_user_data"];
|
|
Module["_sqlite3_context_db_handle"] = wasmExports["sqlite3_context_db_handle"];
|
|
Module["_sqlite3_vtab_nochange"] = wasmExports["sqlite3_vtab_nochange"];
|
|
Module["_sqlite3_vtab_in_first"] = wasmExports["sqlite3_vtab_in_first"];
|
|
Module["_sqlite3_vtab_in_next"] = wasmExports["sqlite3_vtab_in_next"];
|
|
Module["_sqlite3_aggregate_context"] = wasmExports["sqlite3_aggregate_context"];
|
|
Module["_sqlite3_get_auxdata"] = wasmExports["sqlite3_get_auxdata"];
|
|
Module["_sqlite3_set_auxdata"] = wasmExports["sqlite3_set_auxdata"];
|
|
Module["_sqlite3_column_count"] = wasmExports["sqlite3_column_count"];
|
|
Module["_sqlite3_data_count"] = wasmExports["sqlite3_data_count"];
|
|
Module["_sqlite3_column_blob"] = wasmExports["sqlite3_column_blob"];
|
|
Module["_sqlite3_column_bytes"] = wasmExports["sqlite3_column_bytes"];
|
|
Module["_sqlite3_column_double"] = wasmExports["sqlite3_column_double"];
|
|
Module["_sqlite3_column_value"] = wasmExports["sqlite3_column_value"];
|
|
Module["_sqlite3_column_decltype"] = wasmExports["sqlite3_column_decltype"];
|
|
Module["_sqlite3_column_database_name"] = wasmExports["sqlite3_column_database_name"];
|
|
Module["_sqlite3_column_table_name"] = wasmExports["sqlite3_column_table_name"];
|
|
Module["_sqlite3_column_origin_name"] = wasmExports["sqlite3_column_origin_name"];
|
|
Module["_sqlite3_bind_blob"] = wasmExports["sqlite3_bind_blob"];
|
|
Module["_sqlite3_bind_double"] = wasmExports["sqlite3_bind_double"];
|
|
Module["_sqlite3_bind_int"] = wasmExports["sqlite3_bind_int"];
|
|
Module["_sqlite3_bind_int64"] = wasmExports["sqlite3_bind_int64"];
|
|
Module["_sqlite3_bind_null"] = wasmExports["sqlite3_bind_null"];
|
|
Module["_sqlite3_bind_pointer"] = wasmExports["sqlite3_bind_pointer"];
|
|
Module["_sqlite3_bind_text"] = wasmExports["sqlite3_bind_text"];
|
|
Module["_sqlite3_bind_parameter_count"] = wasmExports["sqlite3_bind_parameter_count"];
|
|
Module["_sqlite3_bind_parameter_name"] = wasmExports["sqlite3_bind_parameter_name"];
|
|
Module["_sqlite3_bind_parameter_index"] = wasmExports["sqlite3_bind_parameter_index"];
|
|
Module["_sqlite3_db_handle"] = wasmExports["sqlite3_db_handle"];
|
|
Module["_sqlite3_stmt_readonly"] = wasmExports["sqlite3_stmt_readonly"];
|
|
Module["_sqlite3_stmt_isexplain"] = wasmExports["sqlite3_stmt_isexplain"];
|
|
Module["_sqlite3_stmt_explain"] = wasmExports["sqlite3_stmt_explain"];
|
|
Module["_sqlite3_stmt_busy"] = wasmExports["sqlite3_stmt_busy"];
|
|
Module["_sqlite3_next_stmt"] = wasmExports["sqlite3_next_stmt"];
|
|
Module["_sqlite3_stmt_status"] = wasmExports["sqlite3_stmt_status"];
|
|
Module["_sqlite3_sql"] = wasmExports["sqlite3_sql"];
|
|
Module["_sqlite3_expanded_sql"] = wasmExports["sqlite3_expanded_sql"];
|
|
Module["_sqlite3_preupdate_old"] = wasmExports["sqlite3_preupdate_old"];
|
|
Module["_sqlite3_preupdate_count"] = wasmExports["sqlite3_preupdate_count"];
|
|
Module["_sqlite3_preupdate_depth"] = wasmExports["sqlite3_preupdate_depth"];
|
|
Module["_sqlite3_preupdate_blobwrite"] = wasmExports["sqlite3_preupdate_blobwrite"];
|
|
Module["_sqlite3_preupdate_new"] = wasmExports["sqlite3_preupdate_new"];
|
|
Module["_sqlite3_value_numeric_type"] = wasmExports["sqlite3_value_numeric_type"];
|
|
Module["_sqlite3_set_authorizer"] = wasmExports["sqlite3_set_authorizer"];
|
|
Module["_sqlite3_strglob"] = wasmExports["sqlite3_strglob"];
|
|
Module["_sqlite3_strlike"] = wasmExports["sqlite3_strlike"];
|
|
Module["_sqlite3_auto_extension"] = wasmExports["sqlite3_auto_extension"];
|
|
Module["_sqlite3_cancel_auto_extension"] = wasmExports["sqlite3_cancel_auto_extension"];
|
|
Module["_sqlite3_reset_auto_extension"] = wasmExports["sqlite3_reset_auto_extension"];
|
|
Module["_sqlite3_prepare_v3"] = wasmExports["sqlite3_prepare_v3"];
|
|
Module["_sqlite3_create_module"] = wasmExports["sqlite3_create_module"];
|
|
Module["_sqlite3_create_module_v2"] = wasmExports["sqlite3_create_module_v2"];
|
|
Module["_sqlite3_drop_modules"] = wasmExports["sqlite3_drop_modules"];
|
|
Module["_sqlite3_declare_vtab"] = wasmExports["sqlite3_declare_vtab"];
|
|
Module["_sqlite3_vtab_on_conflict"] = wasmExports["sqlite3_vtab_on_conflict"];
|
|
Module["_sqlite3_vtab_collation"] = wasmExports["sqlite3_vtab_collation"];
|
|
Module["_sqlite3_vtab_in"] = wasmExports["sqlite3_vtab_in"];
|
|
Module["_sqlite3_vtab_rhs_value"] = wasmExports["sqlite3_vtab_rhs_value"];
|
|
Module["_sqlite3_vtab_distinct"] = wasmExports["sqlite3_vtab_distinct"];
|
|
Module["_sqlite3_keyword_name"] = wasmExports["sqlite3_keyword_name"];
|
|
Module["_sqlite3_keyword_count"] = wasmExports["sqlite3_keyword_count"];
|
|
Module["_sqlite3_keyword_check"] = wasmExports["sqlite3_keyword_check"];
|
|
Module["_sqlite3_complete"] = wasmExports["sqlite3_complete"];
|
|
Module["_sqlite3_libversion"] = wasmExports["sqlite3_libversion"];
|
|
Module["_sqlite3_libversion_number"] = wasmExports["sqlite3_libversion_number"];
|
|
Module["_sqlite3_shutdown"] = wasmExports["sqlite3_shutdown"];
|
|
Module["_sqlite3_last_insert_rowid"] = wasmExports["sqlite3_last_insert_rowid"];
|
|
Module["_sqlite3_set_last_insert_rowid"] = wasmExports["sqlite3_set_last_insert_rowid"];
|
|
Module["_sqlite3_changes64"] = wasmExports["sqlite3_changes64"];
|
|
Module["_sqlite3_changes"] = wasmExports["sqlite3_changes"];
|
|
Module["_sqlite3_total_changes64"] = wasmExports["sqlite3_total_changes64"];
|
|
Module["_sqlite3_total_changes"] = wasmExports["sqlite3_total_changes"];
|
|
Module["_sqlite3_txn_state"] = wasmExports["sqlite3_txn_state"];
|
|
Module["_sqlite3_close_v2"] = wasmExports["sqlite3_close_v2"];
|
|
Module["_sqlite3_busy_handler"] = wasmExports["sqlite3_busy_handler"];
|
|
Module["_sqlite3_progress_handler"] = wasmExports["sqlite3_progress_handler"];
|
|
Module["_sqlite3_busy_timeout"] = wasmExports["sqlite3_busy_timeout"];
|
|
Module["_sqlite3_interrupt"] = wasmExports["sqlite3_interrupt"];
|
|
Module["_sqlite3_is_interrupted"] = wasmExports["sqlite3_is_interrupted"];
|
|
Module["_sqlite3_create_function"] = wasmExports["sqlite3_create_function"];
|
|
Module["_sqlite3_create_function_v2"] = wasmExports["sqlite3_create_function_v2"];
|
|
Module["_sqlite3_create_window_function"] = wasmExports["sqlite3_create_window_function"];
|
|
Module["_sqlite3_overload_function"] = wasmExports["sqlite3_overload_function"];
|
|
Module["_sqlite3_trace_v2"] = wasmExports["sqlite3_trace_v2"];
|
|
Module["_sqlite3_commit_hook"] = wasmExports["sqlite3_commit_hook"];
|
|
Module["_sqlite3_update_hook"] = wasmExports["sqlite3_update_hook"];
|
|
Module["_sqlite3_rollback_hook"] = wasmExports["sqlite3_rollback_hook"];
|
|
Module["_sqlite3_preupdate_hook"] = wasmExports["sqlite3_preupdate_hook"];
|
|
Module["_sqlite3_set_errmsg"] = wasmExports["sqlite3_set_errmsg"];
|
|
Module["_sqlite3_error_offset"] = wasmExports["sqlite3_error_offset"];
|
|
Module["_sqlite3_errcode"] = wasmExports["sqlite3_errcode"];
|
|
Module["_sqlite3_extended_errcode"] = wasmExports["sqlite3_extended_errcode"];
|
|
Module["_sqlite3_errstr"] = wasmExports["sqlite3_errstr"];
|
|
Module["_sqlite3_limit"] = wasmExports["sqlite3_limit"];
|
|
Module["_sqlite3_open"] = wasmExports["sqlite3_open"];
|
|
Module["_sqlite3_open_v2"] = wasmExports["sqlite3_open_v2"];
|
|
Module["_sqlite3_create_collation"] = wasmExports["sqlite3_create_collation"];
|
|
Module["_sqlite3_create_collation_v2"] = wasmExports["sqlite3_create_collation_v2"];
|
|
Module["_sqlite3_collation_needed"] = wasmExports["sqlite3_collation_needed"];
|
|
Module["_sqlite3_get_autocommit"] = wasmExports["sqlite3_get_autocommit"];
|
|
Module["_sqlite3_table_column_metadata"] = wasmExports["sqlite3_table_column_metadata"];
|
|
Module["_sqlite3_extended_result_codes"] = wasmExports["sqlite3_extended_result_codes"];
|
|
Module["_sqlite3_uri_key"] = wasmExports["sqlite3_uri_key"];
|
|
Module["_sqlite3_uri_int64"] = wasmExports["sqlite3_uri_int64"];
|
|
Module["_sqlite3_db_name"] = wasmExports["sqlite3_db_name"];
|
|
Module["_sqlite3_db_filename"] = wasmExports["sqlite3_db_filename"];
|
|
Module["_sqlite3_db_readonly"] = wasmExports["sqlite3_db_readonly"];
|
|
Module["_sqlite3_compileoption_used"] = wasmExports["sqlite3_compileoption_used"];
|
|
Module["_sqlite3_compileoption_get"] = wasmExports["sqlite3_compileoption_get"];
|
|
Module["_sqlite3session_diff"] = wasmExports["sqlite3session_diff"];
|
|
Module["_sqlite3session_attach"] = wasmExports["sqlite3session_attach"];
|
|
Module["_sqlite3session_create"] = wasmExports["sqlite3session_create"];
|
|
Module["_sqlite3session_delete"] = wasmExports["sqlite3session_delete"];
|
|
Module["_sqlite3session_table_filter"] = wasmExports["sqlite3session_table_filter"];
|
|
Module["_sqlite3session_changeset"] = wasmExports["sqlite3session_changeset"];
|
|
Module["_sqlite3session_changeset_strm"] = wasmExports["sqlite3session_changeset_strm"];
|
|
Module["_sqlite3session_patchset_strm"] = wasmExports["sqlite3session_patchset_strm"];
|
|
Module["_sqlite3session_patchset"] = wasmExports["sqlite3session_patchset"];
|
|
Module["_sqlite3session_enable"] = wasmExports["sqlite3session_enable"];
|
|
Module["_sqlite3session_indirect"] = wasmExports["sqlite3session_indirect"];
|
|
Module["_sqlite3session_isempty"] = wasmExports["sqlite3session_isempty"];
|
|
Module["_sqlite3session_memory_used"] = wasmExports["sqlite3session_memory_used"];
|
|
Module["_sqlite3session_object_config"] = wasmExports["sqlite3session_object_config"];
|
|
Module["_sqlite3session_changeset_size"] = wasmExports["sqlite3session_changeset_size"];
|
|
Module["_sqlite3changeset_start"] = wasmExports["sqlite3changeset_start"];
|
|
Module["_sqlite3changeset_start_v2"] = wasmExports["sqlite3changeset_start_v2"];
|
|
Module["_sqlite3changeset_start_strm"] = wasmExports["sqlite3changeset_start_strm"];
|
|
Module["_sqlite3changeset_start_v2_strm"] = wasmExports["sqlite3changeset_start_v2_strm"];
|
|
Module["_sqlite3changeset_next"] = wasmExports["sqlite3changeset_next"];
|
|
Module["_sqlite3changeset_op"] = wasmExports["sqlite3changeset_op"];
|
|
Module["_sqlite3changeset_pk"] = wasmExports["sqlite3changeset_pk"];
|
|
Module["_sqlite3changeset_old"] = wasmExports["sqlite3changeset_old"];
|
|
Module["_sqlite3changeset_new"] = wasmExports["sqlite3changeset_new"];
|
|
Module["_sqlite3changeset_conflict"] = wasmExports["sqlite3changeset_conflict"];
|
|
Module["_sqlite3changeset_fk_conflicts"] = wasmExports["sqlite3changeset_fk_conflicts"];
|
|
Module["_sqlite3changeset_finalize"] = wasmExports["sqlite3changeset_finalize"];
|
|
Module["_sqlite3changeset_invert"] = wasmExports["sqlite3changeset_invert"];
|
|
Module["_sqlite3changeset_invert_strm"] = wasmExports["sqlite3changeset_invert_strm"];
|
|
Module["_sqlite3changeset_apply_v2"] = wasmExports["sqlite3changeset_apply_v2"];
|
|
Module["_sqlite3changeset_apply_v3"] = wasmExports["sqlite3changeset_apply_v3"];
|
|
Module["_sqlite3changeset_apply"] = wasmExports["sqlite3changeset_apply"];
|
|
Module["_sqlite3changeset_apply_v3_strm"] = wasmExports["sqlite3changeset_apply_v3_strm"];
|
|
Module["_sqlite3changeset_apply_v2_strm"] = wasmExports["sqlite3changeset_apply_v2_strm"];
|
|
Module["_sqlite3changeset_apply_strm"] = wasmExports["sqlite3changeset_apply_strm"];
|
|
Module["_sqlite3changegroup_new"] = wasmExports["sqlite3changegroup_new"];
|
|
Module["_sqlite3changegroup_add"] = wasmExports["sqlite3changegroup_add"];
|
|
Module["_sqlite3changegroup_output"] = wasmExports["sqlite3changegroup_output"];
|
|
Module["_sqlite3changegroup_add_strm"] = wasmExports["sqlite3changegroup_add_strm"];
|
|
Module["_sqlite3changegroup_output_strm"] = wasmExports["sqlite3changegroup_output_strm"];
|
|
Module["_sqlite3changegroup_delete"] = wasmExports["sqlite3changegroup_delete"];
|
|
Module["_sqlite3changeset_concat"] = wasmExports["sqlite3changeset_concat"];
|
|
Module["_sqlite3changeset_concat_strm"] = wasmExports["sqlite3changeset_concat_strm"];
|
|
Module["_sqlite3session_config"] = wasmExports["sqlite3session_config"];
|
|
Module["_sqlite3_sourceid"] = wasmExports["sqlite3_sourceid"];
|
|
Module["_sqlite3__wasm_pstack_ptr"] = wasmExports["sqlite3__wasm_pstack_ptr"];
|
|
Module["_sqlite3__wasm_pstack_restore"] = wasmExports["sqlite3__wasm_pstack_restore"];
|
|
Module["_sqlite3__wasm_pstack_alloc"] = wasmExports["sqlite3__wasm_pstack_alloc"];
|
|
Module["_sqlite3__wasm_pstack_remaining"] = wasmExports["sqlite3__wasm_pstack_remaining"];
|
|
Module["_sqlite3__wasm_pstack_quota"] = wasmExports["sqlite3__wasm_pstack_quota"];
|
|
Module["_sqlite3__wasm_test_struct"] = wasmExports["sqlite3__wasm_test_struct"];
|
|
Module["_sqlite3__wasm_enum_json"] = wasmExports["sqlite3__wasm_enum_json"];
|
|
Module["_sqlite3__wasm_vfs_unlink"] = wasmExports["sqlite3__wasm_vfs_unlink"];
|
|
Module["_sqlite3__wasm_db_vfs"] = wasmExports["sqlite3__wasm_db_vfs"];
|
|
Module["_sqlite3__wasm_db_reset"] = wasmExports["sqlite3__wasm_db_reset"];
|
|
Module["_sqlite3__wasm_db_export_chunked"] = wasmExports["sqlite3__wasm_db_export_chunked"];
|
|
Module["_sqlite3__wasm_db_serialize"] = wasmExports["sqlite3__wasm_db_serialize"];
|
|
Module["_sqlite3__wasm_vfs_create_file"] = wasmExports["sqlite3__wasm_vfs_create_file"];
|
|
Module["_sqlite3__wasm_posix_create_file"] = wasmExports["sqlite3__wasm_posix_create_file"];
|
|
Module["_sqlite3__wasm_kvvfsMakeKey"] = wasmExports["sqlite3__wasm_kvvfsMakeKey"];
|
|
Module["_sqlite3__wasm_kvvfs_methods"] = wasmExports["sqlite3__wasm_kvvfs_methods"];
|
|
Module["_sqlite3__wasm_vtab_config"] = wasmExports["sqlite3__wasm_vtab_config"];
|
|
Module["_sqlite3__wasm_db_config_ip"] = wasmExports["sqlite3__wasm_db_config_ip"];
|
|
Module["_sqlite3__wasm_db_config_pii"] = wasmExports["sqlite3__wasm_db_config_pii"];
|
|
Module["_sqlite3__wasm_db_config_s"] = wasmExports["sqlite3__wasm_db_config_s"];
|
|
Module["_sqlite3__wasm_config_i"] = wasmExports["sqlite3__wasm_config_i"];
|
|
Module["_sqlite3__wasm_config_ii"] = wasmExports["sqlite3__wasm_config_ii"];
|
|
Module["_sqlite3__wasm_config_j"] = wasmExports["sqlite3__wasm_config_j"];
|
|
Module["_sqlite3__wasm_qfmt_token"] = wasmExports["sqlite3__wasm_qfmt_token"];
|
|
Module["_sqlite3__wasm_kvvfs_decode"] = wasmExports["sqlite3__wasm_kvvfs_decode"];
|
|
Module["_sqlite3__wasm_kvvfs_encode"] = wasmExports["sqlite3__wasm_kvvfs_encode"];
|
|
Module["_sqlite3__wasm_init_wasmfs"] = wasmExports["sqlite3__wasm_init_wasmfs"];
|
|
Module["_sqlite3__wasm_test_intptr"] = wasmExports["sqlite3__wasm_test_intptr"];
|
|
Module["_sqlite3__wasm_test_voidptr"] = wasmExports["sqlite3__wasm_test_voidptr"];
|
|
Module["_sqlite3__wasm_test_int64_max"] = wasmExports["sqlite3__wasm_test_int64_max"];
|
|
Module["_sqlite3__wasm_test_int64_min"] = wasmExports["sqlite3__wasm_test_int64_min"];
|
|
Module["_sqlite3__wasm_test_int64_times2"] = wasmExports["sqlite3__wasm_test_int64_times2"];
|
|
Module["_sqlite3__wasm_test_int64_minmax"] = wasmExports["sqlite3__wasm_test_int64_minmax"];
|
|
Module["_sqlite3__wasm_test_int64ptr"] = wasmExports["sqlite3__wasm_test_int64ptr"];
|
|
Module["_sqlite3__wasm_test_stack_overflow"] = wasmExports["sqlite3__wasm_test_stack_overflow"];
|
|
Module["_sqlite3__wasm_test_str_hello"] = wasmExports["sqlite3__wasm_test_str_hello"];
|
|
Module["_sqlite3__wasm_SQLTester_strglob"] = wasmExports["sqlite3__wasm_SQLTester_strglob"];
|
|
Module["_malloc"] = wasmExports["malloc"];
|
|
Module["_free"] = wasmExports["free"];
|
|
Module["_realloc"] = wasmExports["realloc"];
|
|
_emscripten_builtin_memalign = wasmExports["emscripten_builtin_memalign"];
|
|
wasmExports["_emscripten_stack_restore"];
|
|
wasmExports["_emscripten_stack_alloc"];
|
|
wasmExports["emscripten_stack_get_current"];
|
|
wasmExports["__indirect_function_table"];
|
|
}
|
|
var wasmImports = {
|
|
__syscall_chmod: ___syscall_chmod,
|
|
__syscall_faccessat: ___syscall_faccessat,
|
|
__syscall_fchmod: ___syscall_fchmod,
|
|
__syscall_fchown32: ___syscall_fchown32,
|
|
__syscall_fcntl64: ___syscall_fcntl64,
|
|
__syscall_fstat64: ___syscall_fstat64,
|
|
__syscall_ftruncate64: ___syscall_ftruncate64,
|
|
__syscall_getcwd: ___syscall_getcwd,
|
|
__syscall_ioctl: ___syscall_ioctl,
|
|
__syscall_lstat64: ___syscall_lstat64,
|
|
__syscall_mkdirat: ___syscall_mkdirat,
|
|
__syscall_newfstatat: ___syscall_newfstatat,
|
|
__syscall_openat: ___syscall_openat,
|
|
__syscall_readlinkat: ___syscall_readlinkat,
|
|
__syscall_rmdir: ___syscall_rmdir,
|
|
__syscall_stat64: ___syscall_stat64,
|
|
__syscall_unlinkat: ___syscall_unlinkat,
|
|
__syscall_utimensat: ___syscall_utimensat,
|
|
_localtime_js: __localtime_js,
|
|
_mmap_js: __mmap_js,
|
|
_munmap_js: __munmap_js,
|
|
_tzset_js: __tzset_js,
|
|
clock_time_get: _clock_time_get,
|
|
emscripten_date_now: _emscripten_date_now,
|
|
emscripten_get_heap_max: _emscripten_get_heap_max,
|
|
emscripten_get_now: _emscripten_get_now,
|
|
emscripten_resize_heap: _emscripten_resize_heap,
|
|
environ_get: _environ_get,
|
|
environ_sizes_get: _environ_sizes_get,
|
|
fd_close: _fd_close,
|
|
fd_fdstat_get: _fd_fdstat_get,
|
|
fd_read: _fd_read,
|
|
fd_seek: _fd_seek,
|
|
fd_sync: _fd_sync,
|
|
fd_write: _fd_write,
|
|
memory: wasmMemory
|
|
};
|
|
function run() {
|
|
if (runDependencies > 0) {
|
|
dependenciesFulfilled = run;
|
|
return;
|
|
}
|
|
preRun();
|
|
if (runDependencies > 0) {
|
|
dependenciesFulfilled = run;
|
|
return;
|
|
}
|
|
function doRun() {
|
|
Module["calledRun"] = true;
|
|
if (ABORT) return;
|
|
initRuntime();
|
|
readyPromiseResolve?.(Module);
|
|
Module["onRuntimeInitialized"]?.();
|
|
postRun();
|
|
}
|
|
if (Module["setStatus"]) {
|
|
Module["setStatus"]("Running...");
|
|
setTimeout(() => {
|
|
setTimeout(() => Module["setStatus"](""), 1);
|
|
doRun();
|
|
}, 1);
|
|
} else doRun();
|
|
}
|
|
var wasmExports = await createWasm();
|
|
run();
|
|
Module.runSQLite3PostLoadInit = async function(sqlite3InitScriptInfo, EmscriptenModule, sqlite3IsUnderTest) {
|
|
"use strict";
|
|
delete EmscriptenModule.runSQLite3PostLoadInit;
|
|
globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(apiConfig = globalThis.sqlite3ApiConfig || sqlite3ApiBootstrap.defaultConfig) {
|
|
if (sqlite3ApiBootstrap.sqlite3) {
|
|
(sqlite3ApiBootstrap.sqlite3.config || console).warn("sqlite3ApiBootstrap() called multiple times.", "Config and external initializers are ignored on calls after the first.");
|
|
return sqlite3ApiBootstrap.sqlite3;
|
|
}
|
|
const config = Object.assign(Object.create(null), {
|
|
exports: void 0,
|
|
memory: void 0,
|
|
bigIntEnabled: !!globalThis.BigInt64Array,
|
|
debug: console.debug.bind(console),
|
|
warn: console.warn.bind(console),
|
|
error: console.error.bind(console),
|
|
log: console.log.bind(console),
|
|
wasmfsOpfsDir: "/opfs",
|
|
useStdAlloc: false
|
|
}, apiConfig || {});
|
|
Object.assign(config, {
|
|
allocExportName: config.useStdAlloc ? "malloc" : "sqlite3_malloc",
|
|
deallocExportName: config.useStdAlloc ? "free" : "sqlite3_free",
|
|
reallocExportName: config.useStdAlloc ? "realloc" : "sqlite3_realloc"
|
|
});
|
|
[
|
|
"exports",
|
|
"memory",
|
|
"functionTable",
|
|
"wasmfsOpfsDir"
|
|
].forEach((k) => {
|
|
if ("function" === typeof config[k]) config[k] = config[k]();
|
|
});
|
|
const capi = Object.create(null);
|
|
const wasm = Object.create(null);
|
|
const __rcStr = (rc) => {
|
|
return capi.sqlite3_js_rc_str && capi.sqlite3_js_rc_str(rc) || "Unknown result code #" + rc;
|
|
};
|
|
const isInt32 = (n) => "number" === typeof n && n === (n | 0) && n <= 2147483647 && n >= -2147483648;
|
|
class SQLite3Error extends Error {
|
|
constructor(...args) {
|
|
let rc;
|
|
if (args.length) if (isInt32(args[0])) {
|
|
rc = args[0];
|
|
if (1 === args.length) super(__rcStr(args[0]));
|
|
else {
|
|
const rcStr = __rcStr(rc);
|
|
if ("object" === typeof args[1]) super(rcStr, args[1]);
|
|
else {
|
|
args[0] = rcStr + ":";
|
|
super(args.join(" "));
|
|
}
|
|
}
|
|
} else if (2 === args.length && "object" === typeof args[1]) super(...args);
|
|
else super(args.join(" "));
|
|
this.resultCode = rc || capi.SQLITE_ERROR;
|
|
this.name = "SQLite3Error";
|
|
}
|
|
}
|
|
SQLite3Error.toss = (...args) => {
|
|
throw new SQLite3Error(...args);
|
|
};
|
|
const toss3 = SQLite3Error.toss;
|
|
if (config.wasmfsOpfsDir && !/^\/[^/]+$/.test(config.wasmfsOpfsDir)) toss3("config.wasmfsOpfsDir must be falsy or in the form '/dir-name'.");
|
|
const bigIntFits64 = function f(b) {
|
|
if (!f._max) {
|
|
f._max = BigInt("0x7fffffffffffffff");
|
|
f._min = ~f._max;
|
|
}
|
|
return b >= f._min && b <= f._max;
|
|
};
|
|
const bigIntFits32 = (b) => b >= -2147483647n - 1n && b <= 2147483647n;
|
|
const bigIntFitsDouble = function f(b) {
|
|
if (!f._min) {
|
|
f._min = Number.MIN_SAFE_INTEGER;
|
|
f._max = Number.MAX_SAFE_INTEGER;
|
|
}
|
|
return b >= f._min && b <= f._max;
|
|
};
|
|
const isTypedArray = (v) => {
|
|
return v && v.constructor && isInt32(v.constructor.BYTES_PER_ELEMENT) ? v : false;
|
|
};
|
|
const isBindableTypedArray = (v) => v && (v instanceof Uint8Array || v instanceof Int8Array || v instanceof ArrayBuffer);
|
|
const isSQLableTypedArray = (v) => v && (v instanceof Uint8Array || v instanceof Int8Array || v instanceof ArrayBuffer);
|
|
const affirmBindableTypedArray = (v) => isBindableTypedArray(v) || toss3("Value is not of a supported TypedArray type.");
|
|
const flexibleString = function(v) {
|
|
if (isSQLableTypedArray(v)) return wasm.typedArrayToString(v instanceof ArrayBuffer ? new Uint8Array(v) : v, 0, v.length);
|
|
else if (Array.isArray(v)) return v.join("");
|
|
else if (wasm.isPtr(v)) v = wasm.cstrToJs(v);
|
|
return v;
|
|
};
|
|
class WasmAllocError extends Error {
|
|
constructor(...args) {
|
|
if (2 === args.length && "object" === typeof args[1]) super(...args);
|
|
else if (args.length) super(args.join(" "));
|
|
else super("Allocation failed.");
|
|
this.resultCode = capi.SQLITE_NOMEM;
|
|
this.name = "WasmAllocError";
|
|
}
|
|
}
|
|
WasmAllocError.toss = (...args) => {
|
|
throw new WasmAllocError(...args);
|
|
};
|
|
Object.assign(capi, {
|
|
sqlite3_bind_blob: void 0,
|
|
sqlite3_bind_text: void 0,
|
|
sqlite3_create_function_v2: (pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, xDestroy) => {},
|
|
sqlite3_create_function: (pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal) => {},
|
|
sqlite3_create_window_function: (pDb, funcName, nArg, eTextRep, pApp, xStep, xFinal, xValue, xInverse, xDestroy) => {},
|
|
sqlite3_prepare_v3: (dbPtr, sql, sqlByteLen, prepFlags, stmtPtrPtr, strPtrPtr) => {},
|
|
sqlite3_prepare_v2: (dbPtr, sql, sqlByteLen, stmtPtrPtr, strPtrPtr) => {},
|
|
sqlite3_exec: (pDb, sql, callback, pVoid, pErrMsg) => {},
|
|
sqlite3_randomness: (n, outPtr) => {}
|
|
});
|
|
const util = {
|
|
affirmBindableTypedArray,
|
|
flexibleString,
|
|
bigIntFits32,
|
|
bigIntFits64,
|
|
bigIntFitsDouble,
|
|
isBindableTypedArray,
|
|
isInt32,
|
|
isSQLableTypedArray,
|
|
isTypedArray,
|
|
isUIThread: () => globalThis.window === globalThis && !!globalThis.document,
|
|
toss: function(...args) {
|
|
throw new Error(args.join(" "));
|
|
},
|
|
toss3,
|
|
typedArrayPart: wasm.typedArrayPart,
|
|
assert: function(arg, msg) {
|
|
if (!arg) util.toss("Assertion failed:", msg);
|
|
},
|
|
affirmDbHeader: function(bytes) {
|
|
if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes);
|
|
const header = "SQLite format 3";
|
|
if (15 > bytes.byteLength) toss3("Input does not contain an SQLite3 database header.");
|
|
for (let i = 0; i < 15; ++i) if (header.charCodeAt(i) !== bytes[i]) toss3("Input does not contain an SQLite3 database header.");
|
|
},
|
|
affirmIsDb: function(bytes) {
|
|
if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes);
|
|
const n = bytes.byteLength;
|
|
if (n < 512 || n % 512 !== 0) toss3("Byte array size", n, "is invalid for an SQLite3 db.");
|
|
util.affirmDbHeader(bytes);
|
|
}
|
|
};
|
|
Object.assign(wasm, {
|
|
exports: config.exports || toss3("Missing API config.exports (WASM module exports)."),
|
|
memory: config.memory || config.exports["memory"] || toss3("API config object requires a WebAssembly.Memory object", "in either config.exports.memory (exported)", "or config.memory (imported)."),
|
|
pointerSize: "number" === typeof config.exports.sqlite3_libversion() ? 4 : 8,
|
|
bigIntEnabled: !!config.bigIntEnabled,
|
|
functionTable: config.functionTable,
|
|
alloc: void 0,
|
|
realloc: void 0,
|
|
dealloc: void 0
|
|
});
|
|
wasm.allocFromTypedArray = function(srcTypedArray) {
|
|
if (srcTypedArray instanceof ArrayBuffer) srcTypedArray = new Uint8Array(srcTypedArray);
|
|
affirmBindableTypedArray(srcTypedArray);
|
|
const pRet = wasm.alloc(srcTypedArray.byteLength || 1);
|
|
wasm.heapForSize(srcTypedArray.constructor).set(srcTypedArray.byteLength ? srcTypedArray : [0], Number(pRet));
|
|
return pRet;
|
|
};
|
|
{
|
|
const keyAlloc = config.allocExportName, keyDealloc = config.deallocExportName, keyRealloc = config.reallocExportName;
|
|
for (const key of [
|
|
keyAlloc,
|
|
keyDealloc,
|
|
keyRealloc
|
|
]) if (!(wasm.exports[key] instanceof Function)) toss3("Missing required exports[", key, "] function.");
|
|
wasm.alloc = function f(n) {
|
|
return f.impl(n) || WasmAllocError.toss("Failed to allocate", n, " bytes.");
|
|
};
|
|
wasm.alloc.impl = wasm.exports[keyAlloc];
|
|
wasm.realloc = function f(m, n) {
|
|
const m2 = f.impl(wasm.ptr.coerce(m), n);
|
|
return n ? m2 || WasmAllocError.toss("Failed to reallocate", n, " bytes.") : wasm.ptr.null;
|
|
};
|
|
wasm.realloc.impl = wasm.exports[keyRealloc];
|
|
wasm.dealloc = function f(m) {
|
|
f.impl(wasm.ptr.coerce(m));
|
|
};
|
|
wasm.dealloc.impl = wasm.exports[keyDealloc];
|
|
}
|
|
wasm.compileOptionUsed = function f(optName) {
|
|
if (!arguments.length) {
|
|
if (f._result) return f._result;
|
|
else if (!f._opt) {
|
|
f._rx = /^([^=]+)=(.+)/;
|
|
f._rxInt = /^-?\d+$/;
|
|
f._opt = function(opt, rv) {
|
|
const m = f._rx.exec(opt);
|
|
rv[0] = m ? m[1] : opt;
|
|
rv[1] = m ? f._rxInt.test(m[2]) ? +m[2] : m[2] : true;
|
|
};
|
|
}
|
|
const rc = Object.create(null), ov = [0, 0];
|
|
let i = 0, k;
|
|
while (k = capi.sqlite3_compileoption_get(i++)) {
|
|
f._opt(k, ov);
|
|
rc[ov[0]] = ov[1];
|
|
}
|
|
return f._result = rc;
|
|
} else if (Array.isArray(optName)) {
|
|
const rc = Object.create(null);
|
|
optName.forEach((v) => {
|
|
rc[v] = capi.sqlite3_compileoption_used(v);
|
|
});
|
|
return rc;
|
|
} else if ("object" === typeof optName) {
|
|
Object.keys(optName).forEach((k) => {
|
|
optName[k] = capi.sqlite3_compileoption_used(k);
|
|
});
|
|
return optName;
|
|
}
|
|
return "string" === typeof optName ? !!capi.sqlite3_compileoption_used(optName) : false;
|
|
};
|
|
wasm.pstack = Object.assign(Object.create(null), {
|
|
restore: wasm.exports.sqlite3__wasm_pstack_restore,
|
|
alloc: function(n) {
|
|
if ("string" === typeof n && !(n = wasm.sizeofIR(n))) WasmAllocError.toss("Invalid value for pstack.alloc(", arguments[0], ")");
|
|
return wasm.exports.sqlite3__wasm_pstack_alloc(n) || WasmAllocError.toss("Could not allocate", n, "bytes from the pstack.");
|
|
},
|
|
allocChunks: function(n, sz) {
|
|
if ("string" === typeof sz && !(sz = wasm.sizeofIR(sz))) WasmAllocError.toss("Invalid size value for allocChunks(", arguments[1], ")");
|
|
const mem = wasm.pstack.alloc(n * sz);
|
|
const rc = [mem];
|
|
let i = 1, offset = sz;
|
|
for (; i < n; ++i, offset += sz) rc.push(wasm.ptr.add(mem, offset));
|
|
return rc;
|
|
},
|
|
allocPtr: (n = 1, safePtrSize = true) => {
|
|
return 1 === n ? wasm.pstack.alloc(safePtrSize ? 8 : wasm.ptr.size) : wasm.pstack.allocChunks(n, safePtrSize ? 8 : wasm.ptr.size);
|
|
},
|
|
call: function(f) {
|
|
const stackPos = wasm.pstack.pointer;
|
|
try {
|
|
return f(sqlite3);
|
|
} finally {
|
|
wasm.pstack.restore(stackPos);
|
|
}
|
|
}
|
|
});
|
|
Object.defineProperties(wasm.pstack, {
|
|
pointer: {
|
|
configurable: false,
|
|
iterable: true,
|
|
writeable: false,
|
|
get: wasm.exports.sqlite3__wasm_pstack_ptr
|
|
},
|
|
quota: {
|
|
configurable: false,
|
|
iterable: true,
|
|
writeable: false,
|
|
get: wasm.exports.sqlite3__wasm_pstack_quota
|
|
},
|
|
remaining: {
|
|
configurable: false,
|
|
iterable: true,
|
|
writeable: false,
|
|
get: wasm.exports.sqlite3__wasm_pstack_remaining
|
|
}
|
|
});
|
|
capi.sqlite3_randomness = (...args) => {
|
|
if (1 === args.length && util.isTypedArray(args[0]) && 1 === args[0].BYTES_PER_ELEMENT) {
|
|
const ta = args[0];
|
|
if (0 === ta.byteLength) {
|
|
wasm.exports.sqlite3_randomness(0, wasm.ptr.null);
|
|
return ta;
|
|
}
|
|
const stack = wasm.pstack.pointer;
|
|
try {
|
|
let n = ta.byteLength, offset = 0;
|
|
const r = wasm.exports.sqlite3_randomness;
|
|
const heap = wasm.heap8u();
|
|
const nAlloc = n < 512 ? n : 512;
|
|
const ptr = wasm.pstack.alloc(nAlloc);
|
|
do {
|
|
const j = n > nAlloc ? nAlloc : n;
|
|
r(j, ptr);
|
|
ta.set(wasm.typedArrayPart(heap, ptr, wasm.ptr.add(ptr, j)), offset);
|
|
n -= j;
|
|
offset += j;
|
|
} while (n > 0);
|
|
} catch (e) {
|
|
config.error("Highly unexpected (and ignored!) exception in sqlite3_randomness():", e);
|
|
} finally {
|
|
wasm.pstack.restore(stack);
|
|
}
|
|
return ta;
|
|
}
|
|
wasm.exports.sqlite3_randomness(...args);
|
|
};
|
|
capi.sqlite3_wasmfs_opfs_dir = function() {
|
|
if (void 0 !== this.dir) return this.dir;
|
|
const pdir = config.wasmfsOpfsDir;
|
|
if (!pdir || !globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !wasm.exports.sqlite3__wasm_init_wasmfs) return this.dir = "";
|
|
try {
|
|
if (pdir && 0 === wasm.xCallWrapped("sqlite3__wasm_init_wasmfs", "i32", ["string"], pdir)) return this.dir = pdir;
|
|
else return this.dir = "";
|
|
} catch (e) {
|
|
return this.dir = "";
|
|
}
|
|
}.bind(Object.create(null));
|
|
capi.sqlite3_wasmfs_filename_is_persistent = function(name) {
|
|
const p = capi.sqlite3_wasmfs_opfs_dir();
|
|
return p && name ? name.startsWith(p + "/") : false;
|
|
};
|
|
capi.sqlite3_js_db_uses_vfs = function(pDb, vfsName, dbName = 0) {
|
|
try {
|
|
const pK = capi.sqlite3_vfs_find(vfsName);
|
|
if (!pK) return false;
|
|
else if (!pDb) return pK === capi.sqlite3_vfs_find(0) ? pK : false;
|
|
else return pK === capi.sqlite3_js_db_vfs(pDb, dbName) ? pK : false;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
};
|
|
capi.sqlite3_js_vfs_list = function() {
|
|
const rc = [];
|
|
let pVfs = capi.sqlite3_vfs_find(wasm.ptr.null);
|
|
while (pVfs) {
|
|
const oVfs = new capi.sqlite3_vfs(pVfs);
|
|
rc.push(wasm.cstrToJs(oVfs.$zName));
|
|
pVfs = oVfs.$pNext;
|
|
oVfs.dispose();
|
|
}
|
|
return rc;
|
|
};
|
|
capi.sqlite3_js_db_export = function(pDb, schema = 0) {
|
|
pDb = wasm.xWrap.testConvertArg("sqlite3*", pDb);
|
|
if (!pDb) toss3("Invalid sqlite3* argument.");
|
|
if (!wasm.bigIntEnabled) toss3("BigInt support is not enabled.");
|
|
const scope = wasm.scopedAllocPush();
|
|
let pOut;
|
|
try {
|
|
const pSize = wasm.scopedAlloc(8 + wasm.ptr.size);
|
|
const ppOut = wasm.ptr.add(pSize, 8);
|
|
const zSchema = schema ? wasm.isPtr(schema) ? schema : wasm.scopedAllocCString("" + schema) : wasm.ptr.null;
|
|
let rc = wasm.exports.sqlite3__wasm_db_serialize(pDb, zSchema, ppOut, pSize, 0);
|
|
if (rc) toss3("Database serialization failed with code", sqlite3.capi.sqlite3_js_rc_str(rc));
|
|
pOut = wasm.peekPtr(ppOut);
|
|
const nOut = wasm.peek(pSize, "i64");
|
|
rc = nOut ? wasm.heap8u().slice(Number(pOut), Number(pOut) + Number(nOut)) : new Uint8Array();
|
|
return rc;
|
|
} finally {
|
|
if (pOut) wasm.exports.sqlite3_free(pOut);
|
|
wasm.scopedAllocPop(scope);
|
|
}
|
|
};
|
|
capi.sqlite3_js_db_vfs = (dbPointer, dbName = wasm.ptr.null) => util.sqlite3__wasm_db_vfs(dbPointer, dbName);
|
|
capi.sqlite3_js_aggregate_context = (pCtx, n) => {
|
|
return capi.sqlite3_aggregate_context(pCtx, n) || (n ? WasmAllocError.toss("Cannot allocate", n, "bytes for sqlite3_aggregate_context()") : 0);
|
|
};
|
|
capi.sqlite3_js_posix_create_file = function(filename, data, dataLen) {
|
|
let pData;
|
|
if (data && wasm.isPtr(data)) pData = data;
|
|
else if (data instanceof ArrayBuffer || data instanceof Uint8Array) {
|
|
pData = wasm.allocFromTypedArray(data);
|
|
if (arguments.length < 3 || !util.isInt32(dataLen) || dataLen < 0) dataLen = data.byteLength;
|
|
} else SQLite3Error.toss("Invalid 2nd argument for sqlite3_js_posix_create_file().");
|
|
try {
|
|
if (!util.isInt32(dataLen) || dataLen < 0) SQLite3Error.toss("Invalid 3rd argument for sqlite3_js_posix_create_file().");
|
|
const rc = util.sqlite3__wasm_posix_create_file(filename, pData, dataLen);
|
|
if (rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code", capi.sqlite3_js_rc_str(rc));
|
|
} finally {
|
|
if (pData && pData !== data) wasm.dealloc(pData);
|
|
}
|
|
};
|
|
capi.sqlite3_js_vfs_create_file = function(vfs, filename, data, dataLen) {
|
|
config.warn("sqlite3_js_vfs_create_file() is deprecated and", "should be avoided because it can lead to C-level crashes.", "See its documentation for alternatives.");
|
|
let pData;
|
|
if (data) if (wasm.isPtr(data)) pData = data;
|
|
else {
|
|
if (data instanceof ArrayBuffer) data = new Uint8Array(data);
|
|
if (data instanceof Uint8Array) {
|
|
pData = wasm.allocFromTypedArray(data);
|
|
if (arguments.length < 4 || !util.isInt32(dataLen) || dataLen < 0) dataLen = data.byteLength;
|
|
} else SQLite3Error.toss("Invalid 3rd argument type for sqlite3_js_vfs_create_file().");
|
|
}
|
|
else pData = 0;
|
|
if (!util.isInt32(dataLen) || dataLen < 0) {
|
|
if (pData && pData !== data) wasm.dealloc(pData);
|
|
SQLite3Error.toss("Invalid 4th argument for sqlite3_js_vfs_create_file().");
|
|
}
|
|
try {
|
|
const rc = util.sqlite3__wasm_vfs_create_file(vfs, filename, pData, dataLen);
|
|
if (rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code", capi.sqlite3_js_rc_str(rc));
|
|
} finally {
|
|
if (pData && pData !== data) wasm.dealloc(pData);
|
|
}
|
|
};
|
|
capi.sqlite3_js_sql_to_string = (sql) => {
|
|
if ("string" === typeof sql) return sql;
|
|
const x = flexibleString(v);
|
|
return x === v ? void 0 : x;
|
|
};
|
|
capi.sqlite3_db_config = function(pDb, op, ...args) {
|
|
switch (op) {
|
|
case capi.SQLITE_DBCONFIG_ENABLE_FKEY:
|
|
case capi.SQLITE_DBCONFIG_ENABLE_TRIGGER:
|
|
case capi.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER:
|
|
case capi.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION:
|
|
case capi.SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE:
|
|
case capi.SQLITE_DBCONFIG_ENABLE_QPSG:
|
|
case capi.SQLITE_DBCONFIG_TRIGGER_EQP:
|
|
case capi.SQLITE_DBCONFIG_RESET_DATABASE:
|
|
case capi.SQLITE_DBCONFIG_DEFENSIVE:
|
|
case capi.SQLITE_DBCONFIG_WRITABLE_SCHEMA:
|
|
case capi.SQLITE_DBCONFIG_LEGACY_ALTER_TABLE:
|
|
case capi.SQLITE_DBCONFIG_DQS_DML:
|
|
case capi.SQLITE_DBCONFIG_DQS_DDL:
|
|
case capi.SQLITE_DBCONFIG_ENABLE_VIEW:
|
|
case capi.SQLITE_DBCONFIG_LEGACY_FILE_FORMAT:
|
|
case capi.SQLITE_DBCONFIG_TRUSTED_SCHEMA:
|
|
case capi.SQLITE_DBCONFIG_STMT_SCANSTATUS:
|
|
case capi.SQLITE_DBCONFIG_REVERSE_SCANORDER:
|
|
case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE:
|
|
case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE:
|
|
case capi.SQLITE_DBCONFIG_ENABLE_COMMENTS:
|
|
if (!this.ip) this.ip = wasm.xWrap("sqlite3__wasm_db_config_ip", "int", [
|
|
"sqlite3*",
|
|
"int",
|
|
"int",
|
|
"*"
|
|
]);
|
|
return this.ip(pDb, op, args[0], args[1] || 0);
|
|
case capi.SQLITE_DBCONFIG_LOOKASIDE:
|
|
if (!this.pii) this.pii = wasm.xWrap("sqlite3__wasm_db_config_pii", "int", [
|
|
"sqlite3*",
|
|
"int",
|
|
"*",
|
|
"int",
|
|
"int"
|
|
]);
|
|
return this.pii(pDb, op, args[0], args[1], args[2]);
|
|
case capi.SQLITE_DBCONFIG_MAINDBNAME:
|
|
if (!this.s) this.s = wasm.xWrap("sqlite3__wasm_db_config_s", "int", [
|
|
"sqlite3*",
|
|
"int",
|
|
"string:static"
|
|
]);
|
|
return this.s(pDb, op, args[0]);
|
|
default: return capi.SQLITE_MISUSE;
|
|
}
|
|
}.bind(Object.create(null));
|
|
capi.sqlite3_value_to_js = function(pVal, throwIfCannotConvert = true) {
|
|
let arg;
|
|
const valType = capi.sqlite3_value_type(pVal);
|
|
switch (valType) {
|
|
case capi.SQLITE_INTEGER:
|
|
if (wasm.bigIntEnabled) {
|
|
arg = capi.sqlite3_value_int64(pVal);
|
|
if (util.bigIntFitsDouble(arg)) arg = Number(arg);
|
|
} else arg = capi.sqlite3_value_double(pVal);
|
|
break;
|
|
case capi.SQLITE_FLOAT:
|
|
arg = capi.sqlite3_value_double(pVal);
|
|
break;
|
|
case capi.SQLITE_TEXT:
|
|
arg = capi.sqlite3_value_text(pVal);
|
|
break;
|
|
case capi.SQLITE_BLOB: {
|
|
const n = capi.sqlite3_value_bytes(pVal);
|
|
const pBlob = capi.sqlite3_value_blob(pVal);
|
|
if (n && !pBlob) sqlite3.WasmAllocError.toss("Cannot allocate memory for blob argument of", n, "byte(s)");
|
|
arg = n ? wasm.heap8u().slice(Number(pBlob), Number(pBlob) + Number(n)) : null;
|
|
break;
|
|
}
|
|
case capi.SQLITE_NULL:
|
|
arg = null;
|
|
break;
|
|
default:
|
|
if (throwIfCannotConvert) toss3(capi.SQLITE_MISMATCH, "Unhandled sqlite3_value_type():", valType);
|
|
arg = void 0;
|
|
}
|
|
return arg;
|
|
};
|
|
capi.sqlite3_values_to_js = function(argc, pArgv, throwIfCannotConvert = true) {
|
|
let i;
|
|
const tgt = [];
|
|
for (i = 0; i < argc; ++i) tgt.push(capi.sqlite3_value_to_js(wasm.peekPtr(wasm.ptr.add(pArgv, wasm.ptr.size * i)), throwIfCannotConvert));
|
|
return tgt;
|
|
};
|
|
capi.sqlite3_result_error_js = function(pCtx, e) {
|
|
if (e instanceof WasmAllocError) capi.sqlite3_result_error_nomem(pCtx);
|
|
else capi.sqlite3_result_error(pCtx, "" + e, -1);
|
|
};
|
|
capi.sqlite3_result_js = function(pCtx, val) {
|
|
if (val instanceof Error) {
|
|
capi.sqlite3_result_error_js(pCtx, val);
|
|
return;
|
|
}
|
|
try {
|
|
switch (typeof val) {
|
|
case "undefined": break;
|
|
case "boolean":
|
|
capi.sqlite3_result_int(pCtx, val ? 1 : 0);
|
|
break;
|
|
case "bigint":
|
|
if (util.bigIntFits32(val)) capi.sqlite3_result_int(pCtx, Number(val));
|
|
else if (util.bigIntFitsDouble(val)) capi.sqlite3_result_double(pCtx, Number(val));
|
|
else if (wasm.bigIntEnabled) if (util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val);
|
|
else toss3("BigInt value", val.toString(), "is too BigInt for int64.");
|
|
else toss3("BigInt value", val.toString(), "is too BigInt.");
|
|
break;
|
|
case "number": {
|
|
let f;
|
|
if (util.isInt32(val)) f = capi.sqlite3_result_int;
|
|
else if (wasm.bigIntEnabled && Number.isInteger(val) && util.bigIntFits64(BigInt(val))) f = capi.sqlite3_result_int64;
|
|
else f = capi.sqlite3_result_double;
|
|
f(pCtx, val);
|
|
break;
|
|
}
|
|
case "string": {
|
|
const [p, n] = wasm.allocCString(val, true);
|
|
capi.sqlite3_result_text(pCtx, p, n, capi.SQLITE_WASM_DEALLOC);
|
|
break;
|
|
}
|
|
case "object": if (null === val) {
|
|
capi.sqlite3_result_null(pCtx);
|
|
break;
|
|
} else if (util.isBindableTypedArray(val)) {
|
|
const pBlob = wasm.allocFromTypedArray(val);
|
|
capi.sqlite3_result_blob(pCtx, pBlob, val.byteLength, capi.SQLITE_WASM_DEALLOC);
|
|
break;
|
|
}
|
|
default: toss3("Don't not how to handle this UDF result value:", typeof val, val);
|
|
}
|
|
} catch (e) {
|
|
capi.sqlite3_result_error_js(pCtx, e);
|
|
}
|
|
};
|
|
capi.sqlite3_column_js = function(pStmt, iCol, throwIfCannotConvert = true) {
|
|
const v = capi.sqlite3_column_value(pStmt, iCol);
|
|
return 0 === v ? void 0 : capi.sqlite3_value_to_js(v, throwIfCannotConvert);
|
|
};
|
|
{
|
|
const __newOldValue = function(pObj, iCol, impl) {
|
|
impl = capi[impl];
|
|
if (!this.ptr) this.ptr = wasm.allocPtr();
|
|
else wasm.pokePtr(this.ptr, 0);
|
|
const rc = impl(pObj, iCol, this.ptr);
|
|
if (rc) return SQLite3Error.toss(rc, arguments[2] + "() failed with code " + rc);
|
|
const pv = wasm.peekPtr(this.ptr);
|
|
return pv ? capi.sqlite3_value_to_js(pv, true) : void 0;
|
|
}.bind(Object.create(null));
|
|
capi.sqlite3_preupdate_new_js = (pDb, iCol) => __newOldValue(pDb, iCol, "sqlite3_preupdate_new");
|
|
capi.sqlite3_preupdate_old_js = (pDb, iCol) => __newOldValue(pDb, iCol, "sqlite3_preupdate_old");
|
|
capi.sqlite3changeset_new_js = (pChangesetIter, iCol) => __newOldValue(pChangesetIter, iCol, "sqlite3changeset_new");
|
|
capi.sqlite3changeset_old_js = (pChangesetIter, iCol) => __newOldValue(pChangesetIter, iCol, "sqlite3changeset_old");
|
|
}
|
|
const sqlite3 = {
|
|
WasmAllocError,
|
|
SQLite3Error,
|
|
capi,
|
|
util,
|
|
wasm,
|
|
config,
|
|
version: Object.create(null),
|
|
client: void 0,
|
|
asyncPostInit: async function ff() {
|
|
if (ff.isReady instanceof Promise) return ff.isReady;
|
|
let lia = this.initializersAsync;
|
|
delete this.initializersAsync;
|
|
const postInit = async () => {
|
|
if (!sqlite3.__isUnderTest) {
|
|
delete sqlite3.util;
|
|
delete sqlite3.StructBinder;
|
|
}
|
|
return sqlite3;
|
|
};
|
|
const catcher = (e) => {
|
|
config.error("an async sqlite3 initializer failed:", e);
|
|
throw e;
|
|
};
|
|
if (!lia || !lia.length) return ff.isReady = postInit().catch(catcher);
|
|
lia = lia.map((f) => {
|
|
return f instanceof Function ? async (x) => f(sqlite3) : f;
|
|
});
|
|
lia.push(postInit);
|
|
let p = Promise.resolve(sqlite3);
|
|
while (lia.length) p = p.then(lia.shift());
|
|
return ff.isReady = p.catch(catcher);
|
|
}.bind(sqlite3ApiBootstrap),
|
|
scriptInfo: void 0
|
|
};
|
|
if ("undefined" !== typeof sqlite3IsUnderTest) sqlite3.__isUnderTest = !!sqlite3IsUnderTest;
|
|
try {
|
|
sqlite3ApiBootstrap.initializers.forEach((f) => {
|
|
f(sqlite3);
|
|
});
|
|
} catch (e) {
|
|
console.error("sqlite3 bootstrap initializer threw:", e);
|
|
throw e;
|
|
}
|
|
delete sqlite3ApiBootstrap.initializers;
|
|
sqlite3ApiBootstrap.sqlite3 = sqlite3;
|
|
if ("undefined" !== typeof sqlite3InitScriptInfo) {
|
|
sqlite3InitScriptInfo.debugModule("sqlite3ApiBootstrap() complete", sqlite3);
|
|
sqlite3.scriptInfo = sqlite3InitScriptInfo;
|
|
}
|
|
if (sqlite3.__isUnderTest) {
|
|
if ("undefined" !== typeof EmscriptenModule) sqlite3.config.emscripten = EmscriptenModule;
|
|
const iw = sqlite3.scriptInfo?.instantiateWasm;
|
|
if (iw) {
|
|
sqlite3.wasm.module = iw.module;
|
|
sqlite3.wasm.instance = iw.instance;
|
|
sqlite3.wasm.imports = iw.imports;
|
|
}
|
|
}
|
|
delete globalThis.sqlite3ApiConfig;
|
|
delete globalThis.sqlite3ApiBootstrap;
|
|
delete sqlite3ApiBootstrap.defaultConfig;
|
|
return sqlite3.asyncPostInit().then((s) => {
|
|
if ("undefined" !== typeof sqlite3InitScriptInfo) sqlite3InitScriptInfo.debugModule("sqlite3.asyncPostInit() complete", s);
|
|
delete s.asyncPostInit;
|
|
delete s.scriptInfo;
|
|
delete s.emscripten;
|
|
return s;
|
|
});
|
|
};
|
|
globalThis.sqlite3ApiBootstrap.initializers = [];
|
|
globalThis.sqlite3ApiBootstrap.initializersAsync = [];
|
|
globalThis.sqlite3ApiBootstrap.defaultConfig = Object.create(null);
|
|
globalThis.sqlite3ApiBootstrap.sqlite3 = void 0;
|
|
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) {
|
|
sqlite3.version = {
|
|
"libVersion": "3.52.0",
|
|
"libVersionNumber": 3052e3,
|
|
"sourceId": "2026-01-30 06:37:34 407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e",
|
|
"downloadVersion": 352e4,
|
|
"scm": {
|
|
"sha3-256": "407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e",
|
|
"branch": "trunk",
|
|
"tags": "",
|
|
"datetime": "2026-01-30T06:37:34.096Z"
|
|
}
|
|
};
|
|
});
|
|
globalThis.WhWasmUtilInstaller = function WhWasmUtilInstaller(target) {
|
|
"use strict";
|
|
if (void 0 === target.bigIntEnabled) target.bigIntEnabled = !!globalThis["BigInt64Array"];
|
|
const toss = (...args) => {
|
|
throw new Error(args.join(" "));
|
|
};
|
|
if (!target.pointerSize && !target.pointerIR && target.alloc && target.dealloc) {
|
|
const ptr = target.alloc(1);
|
|
target.pointerSize = "bigint" === typeof ptr ? 8 : 4;
|
|
target.dealloc(ptr);
|
|
}
|
|
if (target.pointerSize && !target.pointerIR) target.pointerIR = 4 === target.pointerSize ? "i32" : "i64";
|
|
const __ptrIR = target.pointerIR ??= "i32";
|
|
const __ptrSize = target.pointerSize ??= "i32" === __ptrIR ? 4 : "i64" === __ptrIR ? 8 : 0;
|
|
delete target.pointerSize;
|
|
delete target.pointerIR;
|
|
if ("i32" !== __ptrIR && "i64" !== __ptrIR) toss("Invalid pointerIR:", __ptrIR);
|
|
else if (8 !== __ptrSize && 4 !== __ptrSize) toss("Invalid pointerSize:", __ptrSize);
|
|
const __BigInt = target.bigIntEnabled ? (v) => BigInt(v || 0) : (v) => toss("BigInt support is disabled in this build.");
|
|
const __Number = (v) => Number(v || 0);
|
|
const __asPtrType = 4 === __ptrSize ? __Number : __BigInt;
|
|
const __NullPtr = __asPtrType(0);
|
|
const __ptrAdd = function(...args) {
|
|
let rc = __asPtrType(0);
|
|
for (const v of args) rc += __asPtrType(v);
|
|
return rc;
|
|
};
|
|
{
|
|
const __ptr = Object.create(null);
|
|
Object.defineProperty(target, "ptr", {
|
|
enumerable: true,
|
|
get: () => __ptr,
|
|
set: () => toss("The ptr property is read-only.")
|
|
});
|
|
(function f(name, val) {
|
|
Object.defineProperty(__ptr, name, {
|
|
enumerable: true,
|
|
get: () => val,
|
|
set: () => toss("ptr[" + name + "] is read-only.")
|
|
});
|
|
return f;
|
|
})("null", __NullPtr)("size", __ptrSize)("ir", __ptrIR)("coerce", __asPtrType)("add", __ptrAdd)("addn", 4 === __ptrIR ? __ptrAdd : (...args) => Number(__ptrAdd(...args)));
|
|
}
|
|
if (!target.exports) Object.defineProperty(target, "exports", {
|
|
enumerable: true,
|
|
configurable: true,
|
|
get: () => target.instance?.exports
|
|
});
|
|
const cache = Object.create(null);
|
|
cache.heapSize = 0;
|
|
cache.memory = null;
|
|
cache.freeFuncIndexes = [];
|
|
cache.scopedAlloc = [];
|
|
cache.scopedAlloc.pushPtr = (ptr) => {
|
|
cache.scopedAlloc[cache.scopedAlloc.length - 1].push(ptr);
|
|
return ptr;
|
|
};
|
|
cache.utf8Decoder = new TextDecoder();
|
|
cache.utf8Encoder = new TextEncoder("utf-8");
|
|
target.sizeofIR = (n) => {
|
|
switch (n) {
|
|
case "i8": return 1;
|
|
case "i16": return 2;
|
|
case "i32":
|
|
case "f32":
|
|
case "float": return 4;
|
|
case "i64":
|
|
case "f64":
|
|
case "double": return 8;
|
|
case "*": return __ptrSize;
|
|
default: return ("" + n).endsWith("*") ? __ptrSize : void 0;
|
|
}
|
|
};
|
|
const heapWrappers = function() {
|
|
if (!cache.memory) cache.memory = target.memory instanceof WebAssembly.Memory ? target.memory : target.exports.memory;
|
|
else if (cache.heapSize === cache.memory.buffer.byteLength) return cache;
|
|
const b = cache.memory.buffer;
|
|
cache.HEAP8 = new Int8Array(b);
|
|
cache.HEAP8U = new Uint8Array(b);
|
|
cache.HEAP16 = new Int16Array(b);
|
|
cache.HEAP16U = new Uint16Array(b);
|
|
cache.HEAP32 = new Int32Array(b);
|
|
cache.HEAP32U = new Uint32Array(b);
|
|
cache.HEAP32F = new Float32Array(b);
|
|
cache.HEAP64F = new Float64Array(b);
|
|
if (target.bigIntEnabled) if ("undefined" !== typeof BigInt64Array) {
|
|
cache.HEAP64 = new BigInt64Array(b);
|
|
cache.HEAP64U = new BigUint64Array(b);
|
|
} else toss("BigInt support is enabled, but the BigInt64Array type is missing.");
|
|
cache.heapSize = b.byteLength;
|
|
return cache;
|
|
};
|
|
target.heap8 = () => heapWrappers().HEAP8;
|
|
target.heap8u = () => heapWrappers().HEAP8U;
|
|
target.heap16 = () => heapWrappers().HEAP16;
|
|
target.heap16u = () => heapWrappers().HEAP16U;
|
|
target.heap32 = () => heapWrappers().HEAP32;
|
|
target.heap32u = () => heapWrappers().HEAP32U;
|
|
target.heapForSize = function(n, unsigned = true) {
|
|
const c = cache.memory && cache.heapSize === cache.memory.buffer.byteLength ? cache : heapWrappers();
|
|
switch (n) {
|
|
case Int8Array: return c.HEAP8;
|
|
case Uint8Array: return c.HEAP8U;
|
|
case Int16Array: return c.HEAP16;
|
|
case Uint16Array: return c.HEAP16U;
|
|
case Int32Array: return c.HEAP32;
|
|
case Uint32Array: return c.HEAP32U;
|
|
case 8: return unsigned ? c.HEAP8U : c.HEAP8;
|
|
case 16: return unsigned ? c.HEAP16U : c.HEAP16;
|
|
case 32: return unsigned ? c.HEAP32U : c.HEAP32;
|
|
case 64:
|
|
if (c.HEAP64) return unsigned ? c.HEAP64U : c.HEAP64;
|
|
break;
|
|
default: if (target.bigIntEnabled) {
|
|
if (n === globalThis["BigUint64Array"]) return c.HEAP64U;
|
|
else if (n === globalThis["BigInt64Array"]) return c.HEAP64;
|
|
break;
|
|
}
|
|
}
|
|
toss("Invalid heapForSize() size: expecting 8, 16, 32,", "or (if BigInt is enabled) 64.");
|
|
};
|
|
const __funcTable = target.functionTable;
|
|
delete target.functionTable;
|
|
target.functionTable = __funcTable ? () => __funcTable : () => target.exports.__indirect_function_table;
|
|
target.functionEntry = function(fptr) {
|
|
const ft = target.functionTable();
|
|
return fptr < ft.length ? ft.get(__asPtrType(fptr)) : void 0;
|
|
};
|
|
target.jsFuncToWasm = function f(func, sig) {
|
|
if (!f._) f._ = {
|
|
sigTypes: Object.assign(Object.create(null), {
|
|
i: "i32",
|
|
p: __ptrIR,
|
|
P: __ptrIR,
|
|
s: __ptrIR,
|
|
j: "i64",
|
|
f: "f32",
|
|
d: "f64"
|
|
}),
|
|
typeCodes: Object.assign(Object.create(null), {
|
|
f64: 124,
|
|
f32: 125,
|
|
i64: 126,
|
|
i32: 127
|
|
}),
|
|
uleb128Encode: (tgt, method, n) => {
|
|
if (n < 128) tgt[method](n);
|
|
else tgt[method](n % 128 | 128, n >> 7);
|
|
},
|
|
rxJSig: /^(\w)\((\w*)\)$/,
|
|
sigParams: (sig) => {
|
|
const m = f._.rxJSig.exec(sig);
|
|
return m ? m[2] : sig.substr(1);
|
|
},
|
|
letterType: (x) => f._.sigTypes[x] || toss("Invalid signature letter:", x),
|
|
pushSigType: (dest, letter) => dest.push(f._.typeCodes[f._.letterType(letter)])
|
|
};
|
|
if ("string" === typeof func) {
|
|
const x = sig;
|
|
sig = func;
|
|
func = x;
|
|
}
|
|
const _ = f._;
|
|
const sigParams = _.sigParams(sig);
|
|
const wasmCode = [1, 96];
|
|
_.uleb128Encode(wasmCode, "push", sigParams.length);
|
|
for (const x of sigParams) _.pushSigType(wasmCode, x);
|
|
if ("v" === sig[0]) wasmCode.push(0);
|
|
else {
|
|
wasmCode.push(1);
|
|
_.pushSigType(wasmCode, sig[0]);
|
|
}
|
|
_.uleb128Encode(wasmCode, "unshift", wasmCode.length);
|
|
wasmCode.unshift(0, 97, 115, 109, 1, 0, 0, 0, 1);
|
|
wasmCode.push(2, 7, 1, 1, 101, 1, 102, 0, 0, 7, 5, 1, 1, 102, 0, 0);
|
|
return new WebAssembly.Instance(new WebAssembly.Module(new Uint8Array(wasmCode)), { e: { f: func } }).exports["f"];
|
|
};
|
|
const __installFunction = function f(func, sig, scoped) {
|
|
if (scoped && !cache.scopedAlloc.length) toss("No scopedAllocPush() scope is active.");
|
|
if ("string" === typeof func) {
|
|
const x = sig;
|
|
sig = func;
|
|
func = x;
|
|
}
|
|
if ("string" !== typeof sig || !(func instanceof Function)) toss("Invalid arguments: expecting (function,signature) or (signature,function).");
|
|
const ft = target.functionTable();
|
|
const oldLen = __asPtrType(ft.length);
|
|
let ptr;
|
|
while (ptr = cache.freeFuncIndexes.pop()) if (ft.get(ptr)) {
|
|
ptr = null;
|
|
continue;
|
|
} else break;
|
|
if (!ptr) {
|
|
ptr = __asPtrType(oldLen);
|
|
ft.grow(__asPtrType(1));
|
|
}
|
|
try {
|
|
ft.set(ptr, func);
|
|
if (scoped) cache.scopedAlloc.pushPtr(ptr);
|
|
return ptr;
|
|
} catch (e) {
|
|
if (!(e instanceof TypeError)) {
|
|
if (ptr === oldLen) cache.freeFuncIndexes.push(oldLen);
|
|
throw e;
|
|
}
|
|
}
|
|
try {
|
|
const fptr = target.jsFuncToWasm(func, sig);
|
|
ft.set(ptr, fptr);
|
|
if (scoped) cache.scopedAlloc.pushPtr(ptr);
|
|
} catch (e) {
|
|
if (ptr === oldLen) cache.freeFuncIndexes.push(oldLen);
|
|
throw e;
|
|
}
|
|
return ptr;
|
|
};
|
|
target.installFunction = (func, sig) => __installFunction(func, sig, false);
|
|
target.scopedInstallFunction = (func, sig) => __installFunction(func, sig, true);
|
|
target.uninstallFunction = function(ptr) {
|
|
if (!ptr && __NullPtr !== ptr) return void 0;
|
|
const ft = target.functionTable();
|
|
cache.freeFuncIndexes.push(ptr);
|
|
const rc = ft.get(ptr);
|
|
ft.set(ptr, null);
|
|
return rc;
|
|
};
|
|
target.peek = function f(ptr, type = "i8") {
|
|
if (type.endsWith("*")) type = __ptrIR;
|
|
const c = cache.memory && cache.heapSize === cache.memory.buffer.byteLength ? cache : heapWrappers();
|
|
const list = Array.isArray(ptr) ? [] : void 0;
|
|
let rc;
|
|
do {
|
|
if (list) ptr = arguments[0].shift();
|
|
switch (type) {
|
|
case "i1":
|
|
case "i8":
|
|
rc = c.HEAP8[Number(ptr) >> 0];
|
|
break;
|
|
case "i16":
|
|
rc = c.HEAP16[Number(ptr) >> 1];
|
|
break;
|
|
case "i32":
|
|
rc = c.HEAP32[Number(ptr) >> 2];
|
|
break;
|
|
case "float":
|
|
case "f32":
|
|
rc = c.HEAP32F[Number(ptr) >> 2];
|
|
break;
|
|
case "double":
|
|
case "f64":
|
|
rc = Number(c.HEAP64F[Number(ptr) >> 3]);
|
|
break;
|
|
case "i64": if (c.HEAP64) {
|
|
rc = __BigInt(c.HEAP64[Number(ptr) >> 3]);
|
|
break;
|
|
}
|
|
default: toss("Invalid type for peek():", type);
|
|
}
|
|
if (list) list.push(rc);
|
|
} while (list && arguments[0].length);
|
|
return list || rc;
|
|
};
|
|
target.poke = function(ptr, value, type = "i8") {
|
|
if (type.endsWith("*")) type = __ptrIR;
|
|
const c = cache.memory && cache.heapSize === cache.memory.buffer.byteLength ? cache : heapWrappers();
|
|
for (const p of Array.isArray(ptr) ? ptr : [ptr]) switch (type) {
|
|
case "i1":
|
|
case "i8":
|
|
c.HEAP8[Number(p) >> 0] = value;
|
|
continue;
|
|
case "i16":
|
|
c.HEAP16[Number(p) >> 1] = value;
|
|
continue;
|
|
case "i32":
|
|
c.HEAP32[Number(p) >> 2] = value;
|
|
continue;
|
|
case "float":
|
|
case "f32":
|
|
c.HEAP32F[Number(p) >> 2] = value;
|
|
continue;
|
|
case "double":
|
|
case "f64":
|
|
c.HEAP64F[Number(p) >> 3] = value;
|
|
continue;
|
|
case "i64": if (c.HEAP64) {
|
|
c.HEAP64[Number(p) >> 3] = __BigInt(value);
|
|
continue;
|
|
}
|
|
default: toss("Invalid type for poke(): " + type);
|
|
}
|
|
return this;
|
|
};
|
|
target.peekPtr = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, __ptrIR);
|
|
target.pokePtr = (ptr, value = 0) => target.poke(ptr, value, __ptrIR);
|
|
target.peek8 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i8");
|
|
target.poke8 = (ptr, value) => target.poke(ptr, value, "i8");
|
|
target.peek16 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i16");
|
|
target.poke16 = (ptr, value) => target.poke(ptr, value, "i16");
|
|
target.peek32 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i32");
|
|
target.poke32 = (ptr, value) => target.poke(ptr, value, "i32");
|
|
target.peek64 = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "i64");
|
|
target.poke64 = (ptr, value) => target.poke(ptr, value, "i64");
|
|
target.peek32f = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "f32");
|
|
target.poke32f = (ptr, value) => target.poke(ptr, value, "f32");
|
|
target.peek64f = (...ptr) => target.peek(1 === ptr.length ? ptr[0] : ptr, "f64");
|
|
target.poke64f = (ptr, value) => target.poke(ptr, value, "f64");
|
|
target.getMemValue = target.peek;
|
|
target.getPtrValue = target.peekPtr;
|
|
target.setMemValue = target.poke;
|
|
target.setPtrValue = target.pokePtr;
|
|
target.isPtr32 = (ptr) => "number" === typeof ptr && ptr >= 0 && ptr === (ptr | 0);
|
|
target.isPtr64 = (ptr) => "bigint" === typeof ptr ? ptr >= 0 : target.isPtr32(ptr);
|
|
target.isPtr = 4 === __ptrSize ? target.isPtr32 : target.isPtr64;
|
|
target.cstrlen = function(ptr) {
|
|
if (!ptr || !target.isPtr(ptr)) return null;
|
|
ptr = Number(ptr);
|
|
const h = heapWrappers().HEAP8U;
|
|
let pos = ptr;
|
|
for (; h[pos] !== 0; ++pos);
|
|
return pos - ptr;
|
|
};
|
|
const __SAB = "undefined" === typeof SharedArrayBuffer ? function() {} : SharedArrayBuffer;
|
|
const isSharedTypedArray = (aTypedArray) => aTypedArray.buffer instanceof __SAB;
|
|
target.isSharedTypedArray = isSharedTypedArray;
|
|
const typedArrayPart = (aTypedArray, begin, end) => {
|
|
if (8 === __ptrSize) {
|
|
if ("bigint" === typeof begin) begin = Number(begin);
|
|
if ("bigint" === typeof end) end = Number(end);
|
|
}
|
|
return isSharedTypedArray(aTypedArray) ? aTypedArray.slice(begin, end) : aTypedArray.subarray(begin, end);
|
|
};
|
|
target.typedArrayPart = typedArrayPart;
|
|
const typedArrayToString = (typedArray, begin, end) => cache.utf8Decoder.decode(typedArrayPart(typedArray, begin, end));
|
|
target.typedArrayToString = typedArrayToString;
|
|
target.cstrToJs = function(ptr) {
|
|
const n = target.cstrlen(ptr);
|
|
return n ? typedArrayToString(heapWrappers().HEAP8U, Number(ptr), Number(ptr) + n) : null === n ? n : "";
|
|
};
|
|
target.jstrlen = function(str) {
|
|
if ("string" !== typeof str) return null;
|
|
const n = str.length;
|
|
let len = 0;
|
|
for (let i = 0; i < n; ++i) {
|
|
let u = str.charCodeAt(i);
|
|
if (u >= 55296 && u <= 57343) u = 65536 + ((u & 1023) << 10) | str.charCodeAt(++i) & 1023;
|
|
if (u <= 127) ++len;
|
|
else if (u <= 2047) len += 2;
|
|
else if (u <= 65535) len += 3;
|
|
else len += 4;
|
|
}
|
|
return len;
|
|
};
|
|
target.jstrcpy = function(jstr, tgt, offset = 0, maxBytes = -1, addNul = true) {
|
|
if (!tgt || !(tgt instanceof Int8Array) && !(tgt instanceof Uint8Array)) toss("jstrcpy() target must be an Int8Array or Uint8Array.");
|
|
maxBytes = Number(maxBytes);
|
|
offset = Number(offset);
|
|
if (maxBytes < 0) maxBytes = tgt.length - offset;
|
|
if (!(maxBytes > 0) || !(offset >= 0)) return 0;
|
|
let i = 0, max = jstr.length;
|
|
const begin = offset, end = offset + maxBytes - (addNul ? 1 : 0);
|
|
for (; i < max && offset < end; ++i) {
|
|
let u = jstr.charCodeAt(i);
|
|
if (u >= 55296 && u <= 57343) u = 65536 + ((u & 1023) << 10) | jstr.charCodeAt(++i) & 1023;
|
|
if (u <= 127) {
|
|
if (offset >= end) break;
|
|
tgt[offset++] = u;
|
|
} else if (u <= 2047) {
|
|
if (offset + 1 >= end) break;
|
|
tgt[offset++] = 192 | u >> 6;
|
|
tgt[offset++] = 128 | u & 63;
|
|
} else if (u <= 65535) {
|
|
if (offset + 2 >= end) break;
|
|
tgt[offset++] = 224 | u >> 12;
|
|
tgt[offset++] = 128 | u >> 6 & 63;
|
|
tgt[offset++] = 128 | u & 63;
|
|
} else {
|
|
if (offset + 3 >= end) break;
|
|
tgt[offset++] = 240 | u >> 18;
|
|
tgt[offset++] = 128 | u >> 12 & 63;
|
|
tgt[offset++] = 128 | u >> 6 & 63;
|
|
tgt[offset++] = 128 | u & 63;
|
|
}
|
|
}
|
|
if (addNul) tgt[offset++] = 0;
|
|
return offset - begin;
|
|
};
|
|
target.cstrncpy = function(tgtPtr, srcPtr, n) {
|
|
if (!tgtPtr || !srcPtr) toss("cstrncpy() does not accept NULL strings.");
|
|
if (n < 0) n = target.cstrlen(strPtr) + 1;
|
|
else if (!(n > 0)) return 0;
|
|
const heap = target.heap8u();
|
|
let i = 0, ch;
|
|
const tgtNumber = Number(tgtPtr), srcNumber = Number(srcPtr);
|
|
for (; i < n && (ch = heap[srcNumber + i]); ++i) heap[tgtNumber + i] = ch;
|
|
if (i < n) heap[tgtNumber + i++] = 0;
|
|
return i;
|
|
};
|
|
target.jstrToUintArray = (str, addNul = false) => {
|
|
return cache.utf8Encoder.encode(addNul ? str + "\0" : str);
|
|
};
|
|
const __affirmAlloc = (obj, funcName) => {
|
|
if (!(obj.alloc instanceof Function) || !(obj.dealloc instanceof Function)) toss("Object is missing alloc() and/or dealloc() function(s)", "required by", funcName + "().");
|
|
};
|
|
const __allocCStr = function(jstr, returnWithLength, allocator, funcName) {
|
|
__affirmAlloc(target, funcName);
|
|
if ("string" !== typeof jstr) return null;
|
|
const u = cache.utf8Encoder.encode(jstr), ptr = allocator(u.length + 1);
|
|
let toFree = ptr;
|
|
try {
|
|
const heap = heapWrappers().HEAP8U;
|
|
heap.set(u, Number(ptr));
|
|
heap[__ptrAdd(ptr, u.length)] = 0;
|
|
toFree = __NullPtr;
|
|
return returnWithLength ? [ptr, u.length] : ptr;
|
|
} finally {
|
|
if (toFree) target.dealloc(toFree);
|
|
}
|
|
};
|
|
target.allocCString = (jstr, returnWithLength = false) => __allocCStr(jstr, returnWithLength, target.alloc, "allocCString()");
|
|
target.scopedAllocPush = function() {
|
|
__affirmAlloc(target, "scopedAllocPush");
|
|
const a = [];
|
|
cache.scopedAlloc.push(a);
|
|
return a;
|
|
};
|
|
target.scopedAllocPop = function(state) {
|
|
__affirmAlloc(target, "scopedAllocPop");
|
|
const n = arguments.length ? cache.scopedAlloc.indexOf(state) : cache.scopedAlloc.length - 1;
|
|
if (n < 0) toss("Invalid state object for scopedAllocPop().");
|
|
if (0 === arguments.length) state = cache.scopedAlloc[n];
|
|
cache.scopedAlloc.splice(n, 1);
|
|
for (let p; p = state.pop();) if (target.functionEntry(p)) target.uninstallFunction(p);
|
|
else target.dealloc(p);
|
|
};
|
|
target.scopedAlloc = function(n) {
|
|
if (!cache.scopedAlloc.length) toss("No scopedAllocPush() scope is active.");
|
|
const p = __asPtrType(target.alloc(n));
|
|
return cache.scopedAlloc.pushPtr(p);
|
|
};
|
|
Object.defineProperty(target.scopedAlloc, "level", {
|
|
configurable: false,
|
|
enumerable: false,
|
|
get: () => cache.scopedAlloc.length,
|
|
set: () => toss("The 'active' property is read-only.")
|
|
});
|
|
target.scopedAllocCString = (jstr, returnWithLength = false) => __allocCStr(jstr, returnWithLength, target.scopedAlloc, "scopedAllocCString()");
|
|
const __allocMainArgv = function(isScoped, list) {
|
|
const pList = target[isScoped ? "scopedAlloc" : "alloc"]((list.length + 1) * target.ptr.size);
|
|
let i = 0;
|
|
list.forEach((e) => {
|
|
target.pokePtr(__ptrAdd(pList, target.ptr.size * i++), target[isScoped ? "scopedAllocCString" : "allocCString"]("" + e));
|
|
});
|
|
target.pokePtr(__ptrAdd(pList, target.ptr.size * i), 0);
|
|
return pList;
|
|
};
|
|
target.scopedAllocMainArgv = (list) => __allocMainArgv(true, list);
|
|
target.allocMainArgv = (list) => __allocMainArgv(false, list);
|
|
target.cArgvToJs = (argc, pArgv) => {
|
|
const list = [];
|
|
for (let i = 0; i < argc; ++i) {
|
|
const arg = target.peekPtr(__ptrAdd(pArgv, target.ptr.size * i));
|
|
list.push(arg ? target.cstrToJs(arg) : null);
|
|
}
|
|
return list;
|
|
};
|
|
target.scopedAllocCall = function(func) {
|
|
target.scopedAllocPush();
|
|
try {
|
|
return func();
|
|
} finally {
|
|
target.scopedAllocPop();
|
|
}
|
|
};
|
|
const __allocPtr = function(howMany, safePtrSize, method) {
|
|
__affirmAlloc(target, method);
|
|
const pIr = safePtrSize ? "i64" : __ptrIR;
|
|
let m = target[method](howMany * (safePtrSize ? 8 : __ptrSize));
|
|
target.poke(m, 0, pIr);
|
|
if (1 === howMany) return m;
|
|
const a = [m];
|
|
for (let i = 1; i < howMany; ++i) {
|
|
m = __ptrAdd(m, safePtrSize ? 8 : __ptrSize);
|
|
a[i] = m;
|
|
target.poke(m, 0, pIr);
|
|
}
|
|
return a;
|
|
};
|
|
target.allocPtr = (howMany = 1, safePtrSize = true) => __allocPtr(howMany, safePtrSize, "alloc");
|
|
target.scopedAllocPtr = (howMany = 1, safePtrSize = true) => __allocPtr(howMany, safePtrSize, "scopedAlloc");
|
|
target.xGet = function(name) {
|
|
return target.exports[name] || toss("Cannot find exported symbol:", name);
|
|
};
|
|
const __argcMismatch = (f, n) => toss(f + "() requires", n, "argument(s).");
|
|
target.xCall = function(fname, ...args) {
|
|
const f = fname instanceof Function ? fname : target.xGet(fname);
|
|
if (!(f instanceof Function)) toss("Exported symbol", fname, "is not a function.");
|
|
if (f.length !== args.length) __argcMismatch(f === fname ? f.name : fname, f.length);
|
|
return 2 === arguments.length && Array.isArray(arguments[1]) ? f.apply(null, arguments[1]) : f.apply(null, args);
|
|
};
|
|
cache.xWrap = Object.create(null);
|
|
cache.xWrap.convert = Object.create(null);
|
|
cache.xWrap.convert.arg = /* @__PURE__ */ new Map();
|
|
cache.xWrap.convert.result = /* @__PURE__ */ new Map();
|
|
const xArg = cache.xWrap.convert.arg, xResult = cache.xWrap.convert.result;
|
|
const __xArgPtr = __asPtrType;
|
|
xArg.set("i64", __BigInt).set("i32", (i) => i | 0).set("i16", (i) => (i | 0) & 65535).set("i8", (i) => (i | 0) & 255).set("f32", (i) => Number(i).valueOf()).set("float", xArg.get("f32")).set("f64", xArg.get("f32")).set("double", xArg.get("f64")).set("int", xArg.get("i32")).set("null", (i) => i).set(null, xArg.get("null")).set("**", __xArgPtr).set("*", __xArgPtr);
|
|
xResult.set("*", __xArgPtr).set("pointer", __xArgPtr).set("number", (v) => Number(v)).set("void", (v) => void 0).set(void 0, xResult.get("void")).set("null", (v) => v).set(null, xResult.get("null"));
|
|
for (const t of [
|
|
"i8",
|
|
"i16",
|
|
"i32",
|
|
"i64",
|
|
"int",
|
|
"f32",
|
|
"float",
|
|
"f64",
|
|
"double"
|
|
]) {
|
|
xArg.set(t + "*", __xArgPtr);
|
|
xResult.set(t + "*", __xArgPtr);
|
|
xResult.set(t, xArg.get(t) || toss("Maintenance required: missing arg converter for", t));
|
|
}
|
|
const __xArgString = (v) => {
|
|
return "string" === typeof v ? target.scopedAllocCString(v) : __asPtrType(v);
|
|
};
|
|
xArg.set("string", __xArgString).set("utf8", __xArgString);
|
|
xResult.set("string", (i) => target.cstrToJs(i)).set("utf8", xResult.get("string")).set("string:dealloc", (i) => {
|
|
try {
|
|
return i ? target.cstrToJs(i) : null;
|
|
} finally {
|
|
target.dealloc(i);
|
|
}
|
|
}).set("utf8:dealloc", xResult.get("string:dealloc")).set("json", (i) => JSON.parse(target.cstrToJs(i))).set("json:dealloc", (i) => {
|
|
try {
|
|
return i ? JSON.parse(target.cstrToJs(i)) : null;
|
|
} finally {
|
|
target.dealloc(i);
|
|
}
|
|
});
|
|
const AbstractArgAdapter = class {
|
|
constructor(opt) {
|
|
this.name = opt.name || "unnamed adapter";
|
|
}
|
|
convertArg(v, argv, argIndex) {
|
|
toss("AbstractArgAdapter must be subclassed.");
|
|
}
|
|
};
|
|
xArg.FuncPtrAdapter = class FuncPtrAdapter extends AbstractArgAdapter {
|
|
constructor(opt) {
|
|
super(opt);
|
|
if (xArg.FuncPtrAdapter.warnOnUse) console.warn("xArg.FuncPtrAdapter is an internal-only API", "and is not intended to be invoked from", "client-level code. Invoked with:", opt);
|
|
this.name = opt.name || "unnamed";
|
|
this.signature = opt.signature;
|
|
if (opt.contextKey instanceof Function) {
|
|
this.contextKey = opt.contextKey;
|
|
if (!opt.bindScope) opt.bindScope = "context";
|
|
}
|
|
this.bindScope = opt.bindScope || toss("FuncPtrAdapter options requires a bindScope (explicit or implied).");
|
|
if (FuncPtrAdapter.bindScopes.indexOf(opt.bindScope) < 0) toss("Invalid options.bindScope (" + opt.bindMod + ") for FuncPtrAdapter. Expecting one of: (" + FuncPtrAdapter.bindScopes.join(", ") + ")");
|
|
this.isTransient = "transient" === this.bindScope;
|
|
this.isContext = "context" === this.bindScope;
|
|
this.isPermanent = "permanent" === this.bindScope;
|
|
this.singleton = "singleton" === this.bindScope ? [] : void 0;
|
|
this.callProxy = opt.callProxy instanceof Function ? opt.callProxy : void 0;
|
|
}
|
|
contextKey(argv, argIndex) {
|
|
return this;
|
|
}
|
|
contextMap(key) {
|
|
const cm = this.__cmap || (this.__cmap = /* @__PURE__ */ new Map());
|
|
let rc = cm.get(key);
|
|
if (void 0 === rc) cm.set(key, rc = []);
|
|
return rc;
|
|
}
|
|
convertArg(v, argv, argIndex) {
|
|
let pair = this.singleton;
|
|
if (!pair && this.isContext) pair = this.contextMap(this.contextKey(argv, argIndex));
|
|
if (pair && 2 === pair.length && pair[0] === v) return pair[1];
|
|
if (v instanceof Function) {
|
|
if (this.callProxy) v = this.callProxy(v);
|
|
const fp = __installFunction(v, this.signature, this.isTransient);
|
|
if (FuncPtrAdapter.debugFuncInstall) FuncPtrAdapter.debugOut("FuncPtrAdapter installed", this, this.contextKey(argv, argIndex), "@" + fp, v);
|
|
if (pair) {
|
|
if (pair[1]) {
|
|
if (FuncPtrAdapter.debugFuncInstall) FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this, this.contextKey(argv, argIndex), "@" + pair[1], v);
|
|
try {
|
|
cache.scopedAlloc.pushPtr(pair[1]);
|
|
} catch (e) {}
|
|
}
|
|
pair[0] = arguments[0] || __NullPtr;
|
|
pair[1] = fp;
|
|
}
|
|
return fp;
|
|
} else if (target.isPtr(v) || null === v || void 0 === v) {
|
|
if (pair && pair[1] && pair[1] !== v) {
|
|
if (FuncPtrAdapter.debugFuncInstall) FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this, this.contextKey(argv, argIndex), "@" + pair[1], v);
|
|
try {
|
|
cache.scopedAlloc.pushPtr(pair[1]);
|
|
} catch (e) {}
|
|
pair[0] = pair[1] = v || __NullPtr;
|
|
}
|
|
return v || __NullPtr;
|
|
} else throw new TypeError("Invalid FuncPtrAdapter argument type. Expecting a function pointer or a " + (this.name ? this.name + " " : "") + "function matching signature " + this.signature + ".");
|
|
}
|
|
};
|
|
xArg.FuncPtrAdapter.warnOnUse = false;
|
|
xArg.FuncPtrAdapter.debugFuncInstall = false;
|
|
xArg.FuncPtrAdapter.debugOut = console.debug.bind(console);
|
|
xArg.FuncPtrAdapter.bindScopes = [
|
|
"transient",
|
|
"context",
|
|
"singleton",
|
|
"permanent"
|
|
];
|
|
const __xArgAdapterCheck = (t) => xArg.get(t) || toss("Argument adapter not found:", t);
|
|
const __xResultAdapterCheck = (t) => xResult.get(t) || toss("Result adapter not found:", t);
|
|
cache.xWrap.convertArg = (t, ...args) => __xArgAdapterCheck(t)(...args);
|
|
cache.xWrap.convertArgNoCheck = (t, ...args) => xArg.get(t)(...args);
|
|
cache.xWrap.convertResult = (t, v) => null === t ? v : t ? __xResultAdapterCheck(t)(v) : void 0;
|
|
cache.xWrap.convertResultNoCheck = (t, v) => null === t ? v : t ? xResult.get(t)(v) : void 0;
|
|
target.xWrap = function callee(fArg, resultType, ...argTypes) {
|
|
if (3 === arguments.length && Array.isArray(arguments[2])) argTypes = arguments[2];
|
|
if (target.isPtr(fArg)) fArg = target.functionEntry(fArg) || toss("Function pointer not found in WASM function table.");
|
|
const fIsFunc = fArg instanceof Function;
|
|
const xf = fIsFunc ? fArg : target.xGet(fArg);
|
|
if (fIsFunc) fArg = xf.name || "unnamed function";
|
|
if (argTypes.length !== xf.length) __argcMismatch(fArg, xf.length);
|
|
if (0 === xf.length && (null === resultType || "null" === resultType)) return xf;
|
|
__xResultAdapterCheck(resultType);
|
|
for (const t of argTypes) if (t instanceof AbstractArgAdapter) xArg.set(t, (...args) => t.convertArg(...args));
|
|
else __xArgAdapterCheck(t);
|
|
const cxw = cache.xWrap;
|
|
if (0 === xf.length) return (...args) => args.length ? __argcMismatch(fArg, xf.length) : cxw.convertResult(resultType, xf.call(null));
|
|
return function(...args) {
|
|
if (args.length !== xf.length) __argcMismatch(fArg, xf.length);
|
|
const scope = target.scopedAllocPush();
|
|
try {
|
|
let i = 0;
|
|
if (callee.debug) console.debug("xWrap() preparing: resultType ", resultType, "xf", xf, "argTypes", argTypes, "args", args);
|
|
for (; i < args.length; ++i) args[i] = cxw.convertArgNoCheck(argTypes[i], args[i], args, i);
|
|
if (callee.debug) console.debug("xWrap() calling: resultType ", resultType, "xf", xf, "argTypes", argTypes, "args", args);
|
|
return cxw.convertResultNoCheck(resultType, xf.apply(null, args));
|
|
} finally {
|
|
target.scopedAllocPop(scope);
|
|
}
|
|
};
|
|
};
|
|
const __xAdapter = function(func, argc, typeName, adapter, modeName, xcvPart) {
|
|
if ("string" === typeof typeName) {
|
|
if (1 === argc) return xcvPart.get(typeName);
|
|
else if (2 === argc) {
|
|
if (!adapter) {
|
|
xcvPart.delete(typeName);
|
|
return func;
|
|
} else if (!(adapter instanceof Function)) toss(modeName, "requires a function argument.");
|
|
xcvPart.set(typeName, adapter);
|
|
return func;
|
|
}
|
|
}
|
|
toss("Invalid arguments to", modeName);
|
|
};
|
|
target.xWrap.resultAdapter = function f(typeName, adapter) {
|
|
return __xAdapter(f, arguments.length, typeName, adapter, "resultAdapter()", xResult);
|
|
};
|
|
target.xWrap.argAdapter = function f(typeName, adapter) {
|
|
return __xAdapter(f, arguments.length, typeName, adapter, "argAdapter()", xArg);
|
|
};
|
|
target.xWrap.FuncPtrAdapter = xArg.FuncPtrAdapter;
|
|
target.xCallWrapped = function(fArg, resultType, argTypes, ...args) {
|
|
if (Array.isArray(arguments[3])) args = arguments[3];
|
|
return target.xWrap(fArg, resultType, argTypes || []).apply(null, args || []);
|
|
};
|
|
target.xWrap.testConvertArg = cache.xWrap.convertArg;
|
|
target.xWrap.testConvertResult = cache.xWrap.convertResult;
|
|
return target;
|
|
};
|
|
globalThis.WhWasmUtilInstaller.yawl = function yawl(config) {
|
|
"use strict";
|
|
const wfetch = () => fetch(config.uri, { credentials: "same-origin" });
|
|
const wui = this;
|
|
const finalThen = function(arg) {
|
|
if (config.wasmUtilTarget) {
|
|
const toss = (...args) => {
|
|
throw new Error(args.join(" "));
|
|
};
|
|
const tgt = config.wasmUtilTarget;
|
|
tgt.module = arg.module;
|
|
tgt.instance = arg.instance;
|
|
if (!tgt.instance.exports.memory) tgt.memory = config?.imports?.env?.memory || toss("Missing 'memory' object!");
|
|
if (!tgt.alloc && arg.instance.exports.malloc) {
|
|
const exports = arg.instance.exports;
|
|
tgt.alloc = function(n) {
|
|
return exports.malloc(n) || toss("Allocation of", n, "bytes failed.");
|
|
};
|
|
tgt.dealloc = function(m) {
|
|
m && exports.free(m);
|
|
};
|
|
}
|
|
wui(tgt);
|
|
}
|
|
arg.config = config;
|
|
if (config.onload) config.onload(arg);
|
|
return arg;
|
|
};
|
|
return WebAssembly.instantiateStreaming ? () => WebAssembly.instantiateStreaming(wfetch(), config.imports || {}).then(finalThen) : () => wfetch().then((response) => response.arrayBuffer()).then((bytes) => WebAssembly.instantiate(bytes, config.imports || {})).then(finalThen);
|
|
}.bind(globalThis.WhWasmUtilInstaller);
|
|
globalThis.Jaccwabyt = function StructBinderFactory(config) {
|
|
"use strict";
|
|
const toss = (...args) => {
|
|
throw new Error(args.join(" "));
|
|
};
|
|
{
|
|
let h = config.heap;
|
|
if (h instanceof WebAssembly.Memory) h = function() {
|
|
return new Uint8Array(this.buffer);
|
|
}.bind(h);
|
|
else if (!(h instanceof Function)) toss("config.heap must be WebAssembly.Memory instance or", "a function which returns one.");
|
|
config.heap = h;
|
|
}
|
|
["alloc", "dealloc"].forEach(function(k) {
|
|
config[k] instanceof Function || toss("Config option '" + k + "' must be a function.");
|
|
});
|
|
const SBF = StructBinderFactory, heap = config.heap, alloc = config.alloc, dealloc = config.dealloc;
|
|
config.realloc;
|
|
const log = config.log || console.debug.bind(console), memberPrefix = config.memberPrefix || "", memberSuffix = config.memberSuffix || "", BigInt = globalThis["BigInt"], BigInt64Array = globalThis["BigInt64Array"], bigIntEnabled = config.bigIntEnabled ?? !!BigInt64Array;
|
|
let ptr;
|
|
const ptrSize = config.pointerSize || config.ptrSize || ("bigint" === typeof (ptr = alloc(1)) ? 8 : 4);
|
|
const ptrIR = config.pointerIR || config.ptrIR || (4 === ptrSize ? "i32" : "i64");
|
|
if (ptr) {
|
|
dealloc(ptr);
|
|
ptr = void 0;
|
|
}
|
|
if (ptrSize !== 4 && ptrSize !== 8) toss("Invalid pointer size:", ptrSize);
|
|
if (ptrIR !== "i32" && ptrIR !== "i64") toss("Invalid pointer representation:", ptrIR);
|
|
const __BigInt = bigIntEnabled && BigInt ? (v) => BigInt(v || 0) : (v) => toss("BigInt support is disabled in this build.");
|
|
const __asPtrType = "i32" == ptrIR ? Number : __BigInt;
|
|
const __NullPtr = __asPtrType(0);
|
|
const __ptrAdd = function(...args) {
|
|
let rc = __NullPtr;
|
|
for (let i = 0; i < args.length; ++i) rc += __asPtrType(args[i]);
|
|
return rc;
|
|
};
|
|
const __ptrAddSelf = function(...args) {
|
|
return __ptrAdd(this.pointer, ...args);
|
|
};
|
|
if (!SBF.debugFlags) {
|
|
SBF.__makeDebugFlags = function(deriveFrom = null) {
|
|
if (deriveFrom && deriveFrom.__flags) deriveFrom = deriveFrom.__flags;
|
|
const f = function f(flags) {
|
|
if (0 === arguments.length) return f.__flags;
|
|
if (flags < 0) {
|
|
delete f.__flags.getter;
|
|
delete f.__flags.setter;
|
|
delete f.__flags.alloc;
|
|
delete f.__flags.dealloc;
|
|
} else {
|
|
f.__flags.getter = 0 !== (1 & flags);
|
|
f.__flags.setter = 0 !== (2 & flags);
|
|
f.__flags.alloc = 0 !== (4 & flags);
|
|
f.__flags.dealloc = 0 !== (8 & flags);
|
|
}
|
|
return f._flags;
|
|
};
|
|
Object.defineProperty(f, "__flags", {
|
|
iterable: false,
|
|
writable: false,
|
|
value: Object.create(deriveFrom)
|
|
});
|
|
if (!deriveFrom) f(0);
|
|
return f;
|
|
};
|
|
SBF.debugFlags = SBF.__makeDebugFlags();
|
|
}
|
|
const isLittleEndian = true;
|
|
const isFuncSig = (s) => "(" === s[1];
|
|
const isPtrSig = (s) => "p" === s || "P" === s || "s" === s;
|
|
const isAutoPtrSig = (s) => "P" === s;
|
|
const sigLetter = (s) => s ? isFuncSig(s) ? "p" : s[0] : void 0;
|
|
const sigIR = function(s) {
|
|
switch (sigLetter(s)) {
|
|
case "c":
|
|
case "C": return "i8";
|
|
case "i": return "i32";
|
|
case "p":
|
|
case "P":
|
|
case "s": return ptrIR;
|
|
case "j": return "i64";
|
|
case "f": return "float";
|
|
case "d": return "double";
|
|
}
|
|
toss("Unhandled signature IR:", s);
|
|
};
|
|
const sigSize = function(s) {
|
|
switch (sigLetter(s)) {
|
|
case "c":
|
|
case "C": return 1;
|
|
case "i": return 4;
|
|
case "p":
|
|
case "P":
|
|
case "s": return ptrSize;
|
|
case "j": return 8;
|
|
case "f": return 4;
|
|
case "d": return 8;
|
|
}
|
|
toss("Unhandled signature sizeof:", s);
|
|
};
|
|
const affirmBigIntArray = BigInt64Array ? () => true : () => toss("BigInt64Array is not available.");
|
|
const sigDVGetter = function(s) {
|
|
switch (sigLetter(s)) {
|
|
case "p":
|
|
case "P":
|
|
case "s":
|
|
switch (ptrSize) {
|
|
case 4: return "getInt32";
|
|
case 8: return affirmBigIntArray() && "getBigInt64";
|
|
}
|
|
break;
|
|
case "i": return "getInt32";
|
|
case "c": return "getInt8";
|
|
case "C": return "getUint8";
|
|
case "j": return affirmBigIntArray() && "getBigInt64";
|
|
case "f": return "getFloat32";
|
|
case "d": return "getFloat64";
|
|
}
|
|
toss("Unhandled DataView getter for signature:", s);
|
|
};
|
|
const sigDVSetter = function(s) {
|
|
switch (sigLetter(s)) {
|
|
case "p":
|
|
case "P":
|
|
case "s":
|
|
switch (ptrSize) {
|
|
case 4: return "setInt32";
|
|
case 8: return affirmBigIntArray() && "setBigInt64";
|
|
}
|
|
break;
|
|
case "i": return "setInt32";
|
|
case "c": return "setInt8";
|
|
case "C": return "setUint8";
|
|
case "j": return affirmBigIntArray() && "setBigInt64";
|
|
case "f": return "setFloat32";
|
|
case "d": return "setFloat64";
|
|
}
|
|
toss("Unhandled DataView setter for signature:", s);
|
|
};
|
|
const sigDVSetWrapper = function(s) {
|
|
switch (sigLetter(s)) {
|
|
case "i":
|
|
case "f":
|
|
case "c":
|
|
case "C":
|
|
case "d": return Number;
|
|
case "j": return __BigInt;
|
|
case "p":
|
|
case "P":
|
|
case "s":
|
|
switch (ptrSize) {
|
|
case 4: return Number;
|
|
case 8: return __BigInt;
|
|
}
|
|
break;
|
|
}
|
|
toss("Unhandled DataView set wrapper for signature:", s);
|
|
};
|
|
const sPropName = (s, k) => s + "::" + k;
|
|
const __propThrowOnSet = function(structName, propName) {
|
|
return () => toss(sPropName(structName, propName), "is read-only.");
|
|
};
|
|
const getInstanceHandle = function f(obj, create = true) {
|
|
let ii = f.map.get(obj);
|
|
if (!ii && create) f.map.set(obj, ii = f.create(obj));
|
|
return ii;
|
|
};
|
|
getInstanceHandle.map = /* @__PURE__ */ new WeakMap();
|
|
getInstanceHandle.create = (forObj) => {
|
|
return Object.assign(Object.create(null), {
|
|
o: forObj,
|
|
p: void 0,
|
|
ownsPointer: false,
|
|
zod: false,
|
|
xb: 0
|
|
});
|
|
};
|
|
const rmInstanceHandle = (obj) => getInstanceHandle.map.delete(obj);
|
|
const __isPtr32 = (ptr) => "number" === typeof ptr && ptr === (ptr | 0) && ptr >= 0;
|
|
const __isPtr64 = (ptr) => "bigint" === typeof ptr && ptr >= 0 || __isPtr32(ptr);
|
|
const __isPtr = 4 === ptrSize ? __isPtr32 : __isPtr64;
|
|
const __isNonNullPtr = (v) => __isPtr(v) && v > 0;
|
|
const __freeStruct = function(ctor, obj, m) {
|
|
const ii = getInstanceHandle(obj, false);
|
|
if (!ii) return;
|
|
rmInstanceHandle(obj);
|
|
if (!m && !(m = ii.p)) {
|
|
console.warn("Cannot(?) happen: __freeStruct() found no instanceInfo");
|
|
return;
|
|
}
|
|
if (Array.isArray(obj.ondispose)) {
|
|
let x;
|
|
while (x = obj.ondispose.pop()) try {
|
|
if (x instanceof Function) x.call(obj);
|
|
else if (x instanceof StructType) x.dispose();
|
|
else if (__isPtr(x)) dealloc(x);
|
|
} catch (e) {
|
|
console.warn("ondispose() for", ctor.structName, "@", m, "threw. NOT propagating it.", e);
|
|
}
|
|
} else if (obj.ondispose instanceof Function) try {
|
|
obj.ondispose();
|
|
} catch (e) {
|
|
console.warn("ondispose() for", ctor.structName, "@", m, "threw. NOT propagating it.", e);
|
|
}
|
|
delete obj.ondispose;
|
|
if (ctor.debugFlags.__flags.dealloc) log("debug.dealloc:", ii.ownsPointer ? "" : "EXTERNAL", ctor.structName, "instance:", ctor.structInfo.sizeof, "bytes @" + m);
|
|
if (ii.ownsPointer) {
|
|
if (ii.zod || ctor.structInfo.zeroOnDispose) heap().fill(0, Number(m), Number(m) + ctor.structInfo.sizeof + ii.xb);
|
|
dealloc(m);
|
|
}
|
|
};
|
|
const rop0 = () => {
|
|
return {
|
|
configurable: false,
|
|
writable: false,
|
|
iterable: false
|
|
};
|
|
};
|
|
const rop = (v) => {
|
|
return {
|
|
...rop0(),
|
|
value: v
|
|
};
|
|
};
|
|
const __allocStruct = function f(ctor, obj, xm) {
|
|
let opt;
|
|
const checkPtr = (ptr) => {
|
|
__isNonNullPtr(ptr) || toss("Invalid pointer value", arguments[0], "for", ctor.structName, "constructor.");
|
|
};
|
|
if (arguments.length >= 3) if (xm && "object" === typeof xm) {
|
|
opt = xm;
|
|
xm = opt?.wrap;
|
|
} else {
|
|
checkPtr(xm);
|
|
opt = { wrap: xm };
|
|
}
|
|
else opt = {};
|
|
const fill = !xm;
|
|
let nAlloc = 0;
|
|
let ownsPointer = false;
|
|
if (xm) {
|
|
checkPtr(xm);
|
|
ownsPointer = !!opt?.takeOwnership;
|
|
} else {
|
|
const nX = opt?.extraBytes ?? 0;
|
|
if (nX < 0 || nX !== (nX | 0)) toss("Invalid extraBytes value:", opt?.extraBytes);
|
|
nAlloc = ctor.structInfo.sizeof + nX;
|
|
xm = alloc(nAlloc) || toss("Allocation of", ctor.structName, "structure failed.");
|
|
ownsPointer = true;
|
|
}
|
|
try {
|
|
if (opt?.debugFlags) obj.debugFlags(opt.debugFlags);
|
|
if (ctor.debugFlags.__flags.alloc) log("debug.alloc:", fill ? "" : "EXTERNAL", ctor.structName, "instance:", ctor.structInfo.sizeof, "bytes @" + xm);
|
|
if (fill) heap().fill(0, Number(xm), Number(xm) + nAlloc);
|
|
const ii = getInstanceHandle(obj);
|
|
ii.p = xm;
|
|
ii.ownsPointer = ownsPointer;
|
|
ii.xb = nAlloc ? nAlloc - ctor.structInfo.sizeof : 0;
|
|
ii.zod = !!opt?.zeroOnDispose;
|
|
if (opt?.ondispose && opt.ondispose !== xm) obj.addOnDispose(opt.ondispose);
|
|
} catch (e) {
|
|
__freeStruct(ctor, obj, xm);
|
|
throw e;
|
|
}
|
|
};
|
|
const looksLikeASig = function f(sig) {
|
|
f.rxSig1 ??= /^[ipPsjfdcC]$/;
|
|
f.rxSig2 ??= /^[vipPsjfdcC]\([ipPsjfdcC]*\)$/;
|
|
return f.rxSig1.test(sig) || f.rxSig2.test(sig);
|
|
};
|
|
const __adaptorsFor = function(who) {
|
|
let x = this.get(who);
|
|
if (!x) {
|
|
x = [
|
|
Object.create(null),
|
|
Object.create(null),
|
|
Object.create(null)
|
|
];
|
|
this.set(who, x);
|
|
}
|
|
return x;
|
|
}.bind(/* @__PURE__ */ new WeakMap());
|
|
const __adaptor = function(who, which, key, proxy) {
|
|
const a = __adaptorsFor(who)[which];
|
|
if (3 === arguments.length) return a[key];
|
|
if (proxy) return a[key] = proxy;
|
|
return delete a[key];
|
|
};
|
|
const __adaptGet = function(key, ...args) {
|
|
return __adaptor(this, 0, key, ...args);
|
|
};
|
|
const __affirmNotASig = function(ctx, key) {
|
|
looksLikeASig(key) && toss(ctx, "(", key, ") collides with a data type signature.");
|
|
};
|
|
const __adaptSet = function(key, ...args) {
|
|
__affirmNotASig("Setter adaptor", key);
|
|
return __adaptor(this, 1, key, ...args);
|
|
};
|
|
const __adaptStruct = function(key, ...args) {
|
|
__affirmNotASig("Struct adaptor", key);
|
|
return __adaptor(this, 2, key, ...args);
|
|
};
|
|
const __adaptStruct2 = function(who, key) {
|
|
const si = "string" === typeof key ? __adaptor(who, 2, key) : key;
|
|
if ("object" !== typeof si) toss("Invalid struct mapping object. Arg =", key, JSON.stringify(si));
|
|
return si;
|
|
};
|
|
const __memberKey = (k) => memberPrefix + k + memberSuffix;
|
|
const __memberKeyProp = rop(__memberKey);
|
|
const __lookupMember = function(structInfo, memberName, tossIfNotFound = true) {
|
|
let m = structInfo.members[memberName];
|
|
if (!m && (memberPrefix || memberSuffix)) {
|
|
for (const v of Object.values(structInfo.members)) if (v.key === memberName) {
|
|
m = v;
|
|
break;
|
|
}
|
|
if (!m && tossIfNotFound) toss(sPropName(structInfo.name || structInfo.structName, memberName), "is not a mapped struct member.");
|
|
}
|
|
return m;
|
|
};
|
|
const __memberSignature = function f(obj, memberName, emscriptenFormat = false) {
|
|
if (!f._) f._ = (x) => x.replace(/[^vipPsjrdcC]/g, "").replace(/[pPscC]/g, "i");
|
|
const m = __lookupMember(obj.structInfo, memberName, true);
|
|
return emscriptenFormat ? f._(m.signature) : m.signature;
|
|
};
|
|
const __structMemberKeys = rop(function() {
|
|
const a = [];
|
|
for (const k of Object.keys(this.structInfo.members)) a.push(this.memberKey(k));
|
|
return a;
|
|
});
|
|
const __utf8Decoder = new TextDecoder("utf-8");
|
|
const __utf8Encoder = new TextEncoder();
|
|
const __SAB = "undefined" === typeof SharedArrayBuffer ? function() {} : SharedArrayBuffer;
|
|
const __utf8Decode = function(arrayBuffer, begin, end) {
|
|
if (8 === ptrSize) {
|
|
begin = Number(begin);
|
|
end = Number(end);
|
|
}
|
|
return __utf8Decoder.decode(arrayBuffer.buffer instanceof __SAB ? arrayBuffer.slice(begin, end) : arrayBuffer.subarray(begin, end));
|
|
};
|
|
const __memberIsString = function(obj, memberName, tossIfNotFound = false) {
|
|
const m = __lookupMember(obj.structInfo, memberName, tossIfNotFound);
|
|
return m && 1 === m.signature.length && "s" === m.signature[0] ? m : false;
|
|
};
|
|
const __affirmCStringSignature = function(member) {
|
|
if ("s" === member.signature) return;
|
|
toss("Invalid member type signature for C-string value:", JSON.stringify(member));
|
|
};
|
|
const __memberToJsString = function f(obj, memberName) {
|
|
const m = __lookupMember(obj.structInfo, memberName, true);
|
|
__affirmCStringSignature(m);
|
|
const addr = obj[m.key];
|
|
if (!addr) return null;
|
|
let pos = addr;
|
|
const mem = heap();
|
|
for (; mem[pos] !== 0; ++pos);
|
|
return addr === pos ? "" : __utf8Decode(mem, addr, pos);
|
|
};
|
|
const __addOnDispose = function(obj, ...v) {
|
|
if (obj.ondispose) {
|
|
if (!Array.isArray(obj.ondispose)) obj.ondispose = [obj.ondispose];
|
|
} else obj.ondispose = [];
|
|
obj.ondispose.push(...v);
|
|
};
|
|
const __allocCString = function(str) {
|
|
const u = __utf8Encoder.encode(str);
|
|
const mem = alloc(u.length + 1);
|
|
if (!mem) toss("Allocation error while duplicating string:", str);
|
|
const h = heap();
|
|
h.set(u, Number(mem));
|
|
h[__ptrAdd(mem, u.length)] = 0;
|
|
return mem;
|
|
};
|
|
const __setMemberCString = function(obj, memberName, str) {
|
|
const m = __lookupMember(obj.structInfo, memberName, true);
|
|
__affirmCStringSignature(m);
|
|
const mem = __allocCString(str);
|
|
obj[m.key] = mem;
|
|
__addOnDispose(obj, mem);
|
|
return obj;
|
|
};
|
|
const StructType = function StructType(structName, structInfo) {
|
|
if (arguments[2] !== rop) toss("Do not call the StructType constructor", "from client-level code.");
|
|
Object.defineProperties(this, {
|
|
structName: rop(structName),
|
|
structInfo: rop(structInfo)
|
|
});
|
|
};
|
|
StructType.prototype = Object.create(null, {
|
|
dispose: rop(function() {
|
|
__freeStruct(this.constructor, this);
|
|
}),
|
|
lookupMember: rop(function(memberName, tossIfNotFound = true) {
|
|
return __lookupMember(this.structInfo, memberName, tossIfNotFound);
|
|
}),
|
|
memberToJsString: rop(function(memberName) {
|
|
return __memberToJsString(this, memberName);
|
|
}),
|
|
memberIsString: rop(function(memberName, tossIfNotFound = true) {
|
|
return __memberIsString(this, memberName, tossIfNotFound);
|
|
}),
|
|
memberKey: __memberKeyProp,
|
|
memberKeys: __structMemberKeys,
|
|
memberSignature: rop(function(memberName, emscriptenFormat = false) {
|
|
return __memberSignature(this, memberName, emscriptenFormat);
|
|
}),
|
|
memoryDump: rop(function() {
|
|
const p = this.pointer;
|
|
return p ? new Uint8Array(heap().slice(Number(p), Number(p) + this.structInfo.sizeof)) : null;
|
|
}),
|
|
extraBytes: {
|
|
configurable: false,
|
|
enumerable: false,
|
|
get: function() {
|
|
return getInstanceHandle(this, false)?.xb ?? 0;
|
|
}
|
|
},
|
|
zeroOnDispose: {
|
|
configurable: false,
|
|
enumerable: false,
|
|
get: function() {
|
|
return getInstanceHandle(this, false)?.zod ?? !!this.structInfo.zeroOnDispose;
|
|
}
|
|
},
|
|
pointer: {
|
|
configurable: false,
|
|
enumerable: false,
|
|
get: function() {
|
|
return getInstanceHandle(this, false)?.p;
|
|
},
|
|
set: () => toss("Cannot assign the 'pointer' property of a struct.")
|
|
},
|
|
setMemberCString: rop(function(memberName, str) {
|
|
return __setMemberCString(this, memberName, str);
|
|
})
|
|
});
|
|
Object.assign(StructType.prototype, { addOnDispose: function(...v) {
|
|
__addOnDispose(this, ...v);
|
|
return this;
|
|
} });
|
|
Object.defineProperties(StructType, {
|
|
allocCString: rop(__allocCString),
|
|
isA: rop((v) => v instanceof StructType),
|
|
hasExternalPointer: rop((v) => {
|
|
const ii = getInstanceHandle(v, false);
|
|
return !!(ii?.p && !ii?.ownsPointer);
|
|
}),
|
|
memberKey: __memberKeyProp
|
|
});
|
|
const memberGetterProxy = function(si) {
|
|
return si.get || (si.adaptGet ? StructBinder.adaptGet(si.adaptGet) : void 0);
|
|
};
|
|
const memberSetterProxy = function(si) {
|
|
return si.set || (si.adaptSet ? StructBinder.adaptSet(si.adaptSet) : void 0);
|
|
};
|
|
const makeMemberStructWrapper = function callee(ctor, name, si) {
|
|
const __innerStructs = callee.innerStructs ??= /* @__PURE__ */ new Map();
|
|
const key = ctor.memberKey(name);
|
|
if (void 0 !== si.signature) toss("'signature' cannot be used on an embedded struct (", ctor.structName, ".", key, ").");
|
|
if (memberSetterProxy(si)) toss("'set' and 'adaptSet' are not permitted for nested struct members.");
|
|
si.structName ??= ctor.structName + "::" + name;
|
|
si.key = key;
|
|
si.name = name;
|
|
si.constructor = this.call(this, si.structName, si);
|
|
const getterProxy = memberGetterProxy(si);
|
|
const prop = Object.assign(Object.create(null), {
|
|
configurable: false,
|
|
enumerable: false,
|
|
set: __propThrowOnSet(ctor.structName, key),
|
|
get: function() {
|
|
const dbg = this.debugFlags.__flags;
|
|
const p = this.pointer;
|
|
const k = p + "." + key;
|
|
let s = __innerStructs.get(k);
|
|
if (dbg.getter) log("debug.getter: k =", k);
|
|
if (!s) {
|
|
s = new si.constructor(__ptrAdd(p, si.offset));
|
|
__innerStructs.set(k, s);
|
|
this.addOnDispose(() => s.dispose());
|
|
s.addOnDispose(() => __innerStructs.delete(k));
|
|
}
|
|
if (getterProxy) s = getterProxy.apply(this, [s, key]);
|
|
if (dbg.getter) log("debug.getter: result =", s);
|
|
return s;
|
|
}
|
|
});
|
|
Object.defineProperty(ctor.prototype, key, prop);
|
|
};
|
|
const makeMemberWrapper = function f(ctor, name, si) {
|
|
si = __adaptStruct2(this, si);
|
|
if (si.members) return makeMemberStructWrapper.call(this, ctor, name, si);
|
|
if (!f.cache) {
|
|
f.cache = {
|
|
getters: {},
|
|
setters: {},
|
|
sw: {}
|
|
};
|
|
const a = [
|
|
"i",
|
|
"c",
|
|
"C",
|
|
"p",
|
|
"P",
|
|
"s",
|
|
"f",
|
|
"d",
|
|
"v()"
|
|
];
|
|
if (bigIntEnabled) a.push("j");
|
|
a.forEach(function(v) {
|
|
f.cache.getters[v] = sigDVGetter(v);
|
|
f.cache.setters[v] = sigDVSetter(v);
|
|
f.cache.sw[v] = sigDVSetWrapper(v);
|
|
});
|
|
f.sigCheck = function(obj, name, key, sig) {
|
|
if (Object.prototype.hasOwnProperty.call(obj, key)) toss(obj.structName, "already has a property named", key + ".");
|
|
looksLikeASig(sig) || toss("Malformed signature for", sPropName(obj.structName, name) + ":", sig);
|
|
};
|
|
}
|
|
const key = ctor.memberKey(name);
|
|
f.sigCheck(ctor.prototype, name, key, si.signature);
|
|
si.key = key;
|
|
si.name = name;
|
|
const sigGlyph = sigLetter(si.signature);
|
|
const xPropName = sPropName(ctor.structName, key);
|
|
const dbg = ctor.debugFlags.__flags;
|
|
const getterProxy = memberGetterProxy(si);
|
|
const prop = Object.create(null);
|
|
prop.configurable = false;
|
|
prop.enumerable = false;
|
|
prop.get = function() {
|
|
if (dbg.getter) log("debug.getter:", f.cache.getters[sigGlyph], "for", sigIR(sigGlyph), xPropName, "@", this.pointer, "+", si.offset, "sz", si.sizeof);
|
|
let rc = new DataView(heap().buffer, Number(this.pointer) + si.offset, si.sizeof)[f.cache.getters[sigGlyph]](0, isLittleEndian);
|
|
if (getterProxy) rc = getterProxy.apply(this, [key, rc]);
|
|
if (dbg.getter) log("debug.getter:", xPropName, "result =", rc);
|
|
return rc;
|
|
};
|
|
if (si.readOnly) prop.set = __propThrowOnSet(ctor.prototype.structName, key);
|
|
else {
|
|
const setterProxy = memberSetterProxy(si);
|
|
prop.set = function(v) {
|
|
if (dbg.setter) log("debug.setter:", f.cache.setters[sigGlyph], "for", sigIR(sigGlyph), xPropName, "@", this.pointer, "+", si.offset, "sz", si.sizeof, v);
|
|
if (!this.pointer) toss("Cannot set native property on a disposed", this.structName, "instance.");
|
|
if (setterProxy) v = setterProxy.apply(this, [key, v]);
|
|
if (null === v || void 0 === v) v = __NullPtr;
|
|
else if (isPtrSig(si.signature) && !__isPtr(v)) if (isAutoPtrSig(si.signature) && v instanceof StructType) {
|
|
v = v.pointer || __NullPtr;
|
|
if (dbg.setter) log("debug.setter:", xPropName, "resolved to", v);
|
|
} else toss("Invalid value for pointer-type", xPropName + ".");
|
|
new DataView(heap().buffer, Number(this.pointer) + si.offset, si.sizeof)[f.cache.setters[sigGlyph]](0, f.cache.sw[sigGlyph](v), isLittleEndian);
|
|
};
|
|
}
|
|
Object.defineProperty(ctor.prototype, key, prop);
|
|
};
|
|
const StructBinderImpl = function StructBinderImpl(structName, si, opt = Object.create(null)) {
|
|
const StructCtor = function StructCtor(arg) {
|
|
if (!(this instanceof StructCtor)) toss("The", structName, "constructor may only be called via 'new'.");
|
|
__allocStruct(StructCtor, this, ...arguments);
|
|
};
|
|
const self = this;
|
|
const ads = (x) => {
|
|
return "string" === typeof x && looksLikeASig(x) ? { signature: x } : __adaptStruct2(self, x);
|
|
};
|
|
if (1 === arguments.length) {
|
|
si = ads(structName);
|
|
structName = si.structName || si.name;
|
|
} else if (2 === arguments.length) {
|
|
si = ads(si);
|
|
si.name ??= structName;
|
|
} else si = ads(si);
|
|
structName ??= si.structName;
|
|
structName ??= opt.structName;
|
|
if (!structName) toss("One of 'name' or 'structName' are required.");
|
|
if (si.adapt) {
|
|
Object.keys(si.adapt.struct || {}).forEach((k) => {
|
|
__adaptStruct.call(StructBinderImpl, k, si.adapt.struct[k]);
|
|
});
|
|
Object.keys(si.adapt.set || {}).forEach((k) => {
|
|
__adaptSet.call(StructBinderImpl, k, si.adapt.set[k]);
|
|
});
|
|
Object.keys(si.adapt.get || {}).forEach((k) => {
|
|
__adaptGet.call(StructBinderImpl, k, si.adapt.get[k]);
|
|
});
|
|
}
|
|
if (!si.members && !si.sizeof) si.sizeof = sigSize(si.signature);
|
|
const debugFlags = rop(SBF.__makeDebugFlags(StructBinder.debugFlags));
|
|
Object.defineProperties(StructCtor, {
|
|
debugFlags,
|
|
isA: rop((v) => v instanceof StructCtor),
|
|
memberKey: __memberKeyProp,
|
|
memberKeys: __structMemberKeys,
|
|
structInfo: rop(si),
|
|
structName: rop(structName),
|
|
ptrAdd: rop(__ptrAdd)
|
|
});
|
|
StructCtor.prototype = new StructType(structName, si, rop);
|
|
Object.defineProperties(StructCtor.prototype, {
|
|
debugFlags,
|
|
constructor: rop(StructCtor),
|
|
ptrAdd: rop(__ptrAddSelf)
|
|
});
|
|
let lastMember = false;
|
|
let offset = 0;
|
|
const autoCalc = !!si.autoCalcSizeOffset;
|
|
if (!autoCalc) {
|
|
if (!si.sizeof) toss(structName, "description is missing its sizeof property.");
|
|
si.offset ??= 0;
|
|
} else si.offset ??= 0;
|
|
Object.keys(si.members || {}).forEach((k) => {
|
|
let m = ads(si.members[k]);
|
|
if (!m.members && !m.sizeof) {
|
|
m.sizeof = sigSize(m.signature);
|
|
if (!m.sizeof) toss(sPropName(structName, k), "is missing a sizeof property.", m);
|
|
}
|
|
if (void 0 === m.offset) if (autoCalc) m.offset = offset;
|
|
else toss(sPropName(structName, k), "is missing its offset.", JSON.stringify(m));
|
|
si.members[k] = m;
|
|
if (!lastMember || lastMember.offset < m.offset) lastMember = m;
|
|
const oldAutoCalc = !!m.autoCalc;
|
|
if (autoCalc) m.autoCalcSizeOffset = true;
|
|
makeMemberWrapper.call(self, StructCtor, k, m);
|
|
if (oldAutoCalc) m.autoCalcSizeOffset = true;
|
|
else delete m.autoCalcSizeOffset;
|
|
offset += m.sizeof;
|
|
});
|
|
if (!lastMember) toss("No member property descriptions found.");
|
|
if (!si.sizeof) si.sizeof = offset;
|
|
if (si.sizeof === 1) si.signature === "c" || si.signature === "C" || toss("Unexpected sizeof==1 member", sPropName(structName, k), "with signature", si.signature);
|
|
else {
|
|
if (0 !== si.sizeof % 4) {
|
|
console.warn("Invalid struct member description", si);
|
|
toss(structName, "sizeof is not aligned. sizeof=" + si.sizeof);
|
|
}
|
|
if (0 !== si.offset % 4) {
|
|
console.warn("Invalid struct member description", si);
|
|
toss(structName, "offset is not aligned. offset=" + si.offset);
|
|
}
|
|
}
|
|
if (si.sizeof < offset) {
|
|
console.warn("Suspect struct description:", si, "offset =", offset);
|
|
toss("Mismatch in the calculated vs. the provided sizeof/offset info.", "Expected sizeof", offset, "but got", si.sizeof, "for", si);
|
|
}
|
|
delete si.autoCalcSizeOffset;
|
|
return StructCtor;
|
|
};
|
|
const StructBinder = function StructBinder(structName, structInfo) {
|
|
return 1 == arguments.length ? StructBinderImpl.call(StructBinder, structName) : StructBinderImpl.call(StructBinder, structName, structInfo);
|
|
};
|
|
StructBinder.StructType = StructType;
|
|
StructBinder.config = config;
|
|
StructBinder.allocCString = __allocCString;
|
|
StructBinder.adaptGet = __adaptGet;
|
|
StructBinder.adaptSet = __adaptSet;
|
|
StructBinder.adaptStruct = __adaptStruct;
|
|
StructBinder.ptrAdd = __ptrAdd;
|
|
if (!StructBinder.debugFlags) StructBinder.debugFlags = SBF.__makeDebugFlags(SBF.debugFlags);
|
|
return StructBinder;
|
|
};
|
|
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) {
|
|
"use strict";
|
|
const toss = (...args) => {
|
|
throw new Error(args.join(" "));
|
|
};
|
|
const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util;
|
|
globalThis.WhWasmUtilInstaller(wasm);
|
|
delete globalThis.WhWasmUtilInstaller;
|
|
const bindingSignatures = {
|
|
core: [
|
|
[
|
|
"sqlite3_aggregate_context",
|
|
"void*",
|
|
"sqlite3_context*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_bind_double",
|
|
"int",
|
|
"sqlite3_stmt*",
|
|
"int",
|
|
"f64"
|
|
],
|
|
[
|
|
"sqlite3_bind_int",
|
|
"int",
|
|
"sqlite3_stmt*",
|
|
"int",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_bind_null",
|
|
void 0,
|
|
"sqlite3_stmt*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_bind_parameter_count",
|
|
"int",
|
|
"sqlite3_stmt*"
|
|
],
|
|
[
|
|
"sqlite3_bind_parameter_index",
|
|
"int",
|
|
"sqlite3_stmt*",
|
|
"string"
|
|
],
|
|
[
|
|
"sqlite3_bind_parameter_name",
|
|
"string",
|
|
"sqlite3_stmt*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_bind_pointer",
|
|
"int",
|
|
"sqlite3_stmt*",
|
|
"int",
|
|
"*",
|
|
"string:static",
|
|
"*"
|
|
],
|
|
[
|
|
"sqlite3_busy_handler",
|
|
"int",
|
|
[
|
|
"sqlite3*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
signature: "i(pi)",
|
|
contextKey: (argv, argIndex) => argv[0]
|
|
}),
|
|
"*"
|
|
]
|
|
],
|
|
[
|
|
"sqlite3_busy_timeout",
|
|
"int",
|
|
"sqlite3*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_changes",
|
|
"int",
|
|
"sqlite3*"
|
|
],
|
|
[
|
|
"sqlite3_clear_bindings",
|
|
"int",
|
|
"sqlite3_stmt*"
|
|
],
|
|
[
|
|
"sqlite3_collation_needed",
|
|
"int",
|
|
"sqlite3*",
|
|
"*",
|
|
"*"
|
|
],
|
|
[
|
|
"sqlite3_column_blob",
|
|
"*",
|
|
"sqlite3_stmt*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_column_bytes",
|
|
"int",
|
|
"sqlite3_stmt*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_column_count",
|
|
"int",
|
|
"sqlite3_stmt*"
|
|
],
|
|
[
|
|
"sqlite3_column_decltype",
|
|
"string",
|
|
"sqlite3_stmt*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_column_double",
|
|
"f64",
|
|
"sqlite3_stmt*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_column_int",
|
|
"int",
|
|
"sqlite3_stmt*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_column_name",
|
|
"string",
|
|
"sqlite3_stmt*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_column_type",
|
|
"int",
|
|
"sqlite3_stmt*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_column_value",
|
|
"sqlite3_value*",
|
|
"sqlite3_stmt*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_commit_hook",
|
|
"void*",
|
|
[
|
|
"sqlite3*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "sqlite3_commit_hook",
|
|
signature: "i(p)",
|
|
contextKey: (argv) => argv[0]
|
|
}),
|
|
"*"
|
|
]
|
|
],
|
|
[
|
|
"sqlite3_compileoption_get",
|
|
"string",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_compileoption_used",
|
|
"int",
|
|
"string"
|
|
],
|
|
[
|
|
"sqlite3_complete",
|
|
"int",
|
|
"string:flexible"
|
|
],
|
|
[
|
|
"sqlite3_context_db_handle",
|
|
"sqlite3*",
|
|
"sqlite3_context*"
|
|
],
|
|
[
|
|
"sqlite3_data_count",
|
|
"int",
|
|
"sqlite3_stmt*"
|
|
],
|
|
[
|
|
"sqlite3_db_filename",
|
|
"string",
|
|
"sqlite3*",
|
|
"string"
|
|
],
|
|
[
|
|
"sqlite3_db_handle",
|
|
"sqlite3*",
|
|
"sqlite3_stmt*"
|
|
],
|
|
[
|
|
"sqlite3_db_name",
|
|
"string",
|
|
"sqlite3*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_db_readonly",
|
|
"int",
|
|
"sqlite3*",
|
|
"string"
|
|
],
|
|
[
|
|
"sqlite3_db_status",
|
|
"int",
|
|
"sqlite3*",
|
|
"int",
|
|
"*",
|
|
"*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_errcode",
|
|
"int",
|
|
"sqlite3*"
|
|
],
|
|
[
|
|
"sqlite3_errmsg",
|
|
"string",
|
|
"sqlite3*"
|
|
],
|
|
[
|
|
"sqlite3_error_offset",
|
|
"int",
|
|
"sqlite3*"
|
|
],
|
|
[
|
|
"sqlite3_errstr",
|
|
"string",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_exec",
|
|
"int",
|
|
[
|
|
"sqlite3*",
|
|
"string:flexible",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
signature: "i(pipp)",
|
|
bindScope: "transient",
|
|
callProxy: (callback) => {
|
|
let aNames;
|
|
return (pVoid, nCols, pColVals, pColNames) => {
|
|
try {
|
|
const aVals = wasm.cArgvToJs(nCols, pColVals);
|
|
if (!aNames) aNames = wasm.cArgvToJs(nCols, pColNames);
|
|
return callback(aVals, aNames) | 0;
|
|
} catch (e) {
|
|
return e.resultCode || capi.SQLITE_ERROR;
|
|
}
|
|
};
|
|
}
|
|
}),
|
|
"*",
|
|
"**"
|
|
]
|
|
],
|
|
[
|
|
"sqlite3_expanded_sql",
|
|
"string",
|
|
"sqlite3_stmt*"
|
|
],
|
|
[
|
|
"sqlite3_extended_errcode",
|
|
"int",
|
|
"sqlite3*"
|
|
],
|
|
[
|
|
"sqlite3_extended_result_codes",
|
|
"int",
|
|
"sqlite3*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_file_control",
|
|
"int",
|
|
"sqlite3*",
|
|
"string",
|
|
"int",
|
|
"*"
|
|
],
|
|
[
|
|
"sqlite3_finalize",
|
|
"int",
|
|
"sqlite3_stmt*"
|
|
],
|
|
[
|
|
"sqlite3_free",
|
|
void 0,
|
|
"*"
|
|
],
|
|
[
|
|
"sqlite3_get_autocommit",
|
|
"int",
|
|
"sqlite3*"
|
|
],
|
|
[
|
|
"sqlite3_get_auxdata",
|
|
"*",
|
|
"sqlite3_context*",
|
|
"int"
|
|
],
|
|
["sqlite3_initialize", void 0],
|
|
[
|
|
"sqlite3_interrupt",
|
|
void 0,
|
|
"sqlite3*"
|
|
],
|
|
[
|
|
"sqlite3_is_interrupted",
|
|
"int",
|
|
"sqlite3*"
|
|
],
|
|
["sqlite3_keyword_count", "int"],
|
|
[
|
|
"sqlite3_keyword_name",
|
|
"int",
|
|
[
|
|
"int",
|
|
"**",
|
|
"*"
|
|
]
|
|
],
|
|
[
|
|
"sqlite3_keyword_check",
|
|
"int",
|
|
["string", "int"]
|
|
],
|
|
["sqlite3_libversion", "string"],
|
|
["sqlite3_libversion_number", "int"],
|
|
[
|
|
"sqlite3_limit",
|
|
"int",
|
|
[
|
|
"sqlite3*",
|
|
"int",
|
|
"int"
|
|
]
|
|
],
|
|
[
|
|
"sqlite3_malloc",
|
|
"*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_next_stmt",
|
|
"sqlite3_stmt*",
|
|
["sqlite3*", "sqlite3_stmt*"]
|
|
],
|
|
[
|
|
"sqlite3_open",
|
|
"int",
|
|
"string",
|
|
"*"
|
|
],
|
|
[
|
|
"sqlite3_open_v2",
|
|
"int",
|
|
"string",
|
|
"*",
|
|
"int",
|
|
"string"
|
|
],
|
|
[
|
|
"sqlite3_realloc",
|
|
"*",
|
|
"*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_reset",
|
|
"int",
|
|
"sqlite3_stmt*"
|
|
],
|
|
[
|
|
"sqlite3_result_blob",
|
|
void 0,
|
|
"sqlite3_context*",
|
|
"*",
|
|
"int",
|
|
"*"
|
|
],
|
|
[
|
|
"sqlite3_result_double",
|
|
void 0,
|
|
"sqlite3_context*",
|
|
"f64"
|
|
],
|
|
[
|
|
"sqlite3_result_error",
|
|
void 0,
|
|
"sqlite3_context*",
|
|
"string",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_result_error_code",
|
|
void 0,
|
|
"sqlite3_context*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_result_error_nomem",
|
|
void 0,
|
|
"sqlite3_context*"
|
|
],
|
|
[
|
|
"sqlite3_result_error_toobig",
|
|
void 0,
|
|
"sqlite3_context*"
|
|
],
|
|
[
|
|
"sqlite3_result_int",
|
|
void 0,
|
|
"sqlite3_context*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_result_null",
|
|
void 0,
|
|
"sqlite3_context*"
|
|
],
|
|
[
|
|
"sqlite3_result_pointer",
|
|
void 0,
|
|
"sqlite3_context*",
|
|
"*",
|
|
"string:static",
|
|
"*"
|
|
],
|
|
[
|
|
"sqlite3_result_subtype",
|
|
void 0,
|
|
"sqlite3_value*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_result_text",
|
|
void 0,
|
|
"sqlite3_context*",
|
|
"string",
|
|
"int",
|
|
"*"
|
|
],
|
|
[
|
|
"sqlite3_result_zeroblob",
|
|
void 0,
|
|
"sqlite3_context*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_rollback_hook",
|
|
"void*",
|
|
[
|
|
"sqlite3*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "sqlite3_rollback_hook",
|
|
signature: "v(p)",
|
|
contextKey: (argv) => argv[0]
|
|
}),
|
|
"*"
|
|
]
|
|
],
|
|
[
|
|
"sqlite3_set_auxdata",
|
|
void 0,
|
|
[
|
|
"sqlite3_context*",
|
|
"int",
|
|
"*",
|
|
"*"
|
|
]
|
|
],
|
|
[
|
|
"sqlite3_set_errmsg",
|
|
"int",
|
|
"sqlite3*",
|
|
"int",
|
|
"string"
|
|
],
|
|
["sqlite3_shutdown", void 0],
|
|
["sqlite3_sourceid", "string"],
|
|
[
|
|
"sqlite3_sql",
|
|
"string",
|
|
"sqlite3_stmt*"
|
|
],
|
|
[
|
|
"sqlite3_status",
|
|
"int",
|
|
"int",
|
|
"*",
|
|
"*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_step",
|
|
"int",
|
|
"sqlite3_stmt*"
|
|
],
|
|
[
|
|
"sqlite3_stmt_busy",
|
|
"int",
|
|
"sqlite3_stmt*"
|
|
],
|
|
[
|
|
"sqlite3_stmt_readonly",
|
|
"int",
|
|
"sqlite3_stmt*"
|
|
],
|
|
[
|
|
"sqlite3_stmt_status",
|
|
"int",
|
|
"sqlite3_stmt*",
|
|
"int",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_strglob",
|
|
"int",
|
|
"string",
|
|
"string"
|
|
],
|
|
[
|
|
"sqlite3_stricmp",
|
|
"int",
|
|
"string",
|
|
"string"
|
|
],
|
|
[
|
|
"sqlite3_strlike",
|
|
"int",
|
|
"string",
|
|
"string",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_strnicmp",
|
|
"int",
|
|
"string",
|
|
"string",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_table_column_metadata",
|
|
"int",
|
|
"sqlite3*",
|
|
"string",
|
|
"string",
|
|
"string",
|
|
"**",
|
|
"**",
|
|
"*",
|
|
"*",
|
|
"*"
|
|
],
|
|
[
|
|
"sqlite3_total_changes",
|
|
"int",
|
|
"sqlite3*"
|
|
],
|
|
[
|
|
"sqlite3_trace_v2",
|
|
"int",
|
|
[
|
|
"sqlite3*",
|
|
"int",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "sqlite3_trace_v2::callback",
|
|
signature: "i(ippp)",
|
|
contextKey: (argv, argIndex) => argv[0]
|
|
}),
|
|
"*"
|
|
]
|
|
],
|
|
[
|
|
"sqlite3_txn_state",
|
|
"int",
|
|
["sqlite3*", "string"]
|
|
],
|
|
[
|
|
"sqlite3_uri_boolean",
|
|
"int",
|
|
"sqlite3_filename",
|
|
"string",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_uri_key",
|
|
"string",
|
|
"sqlite3_filename",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_uri_parameter",
|
|
"string",
|
|
"sqlite3_filename",
|
|
"string"
|
|
],
|
|
[
|
|
"sqlite3_user_data",
|
|
"void*",
|
|
"sqlite3_context*"
|
|
],
|
|
[
|
|
"sqlite3_value_blob",
|
|
"*",
|
|
"sqlite3_value*"
|
|
],
|
|
[
|
|
"sqlite3_value_bytes",
|
|
"int",
|
|
"sqlite3_value*"
|
|
],
|
|
[
|
|
"sqlite3_value_double",
|
|
"f64",
|
|
"sqlite3_value*"
|
|
],
|
|
[
|
|
"sqlite3_value_dup",
|
|
"sqlite3_value*",
|
|
"sqlite3_value*"
|
|
],
|
|
[
|
|
"sqlite3_value_free",
|
|
void 0,
|
|
"sqlite3_value*"
|
|
],
|
|
[
|
|
"sqlite3_value_frombind",
|
|
"int",
|
|
"sqlite3_value*"
|
|
],
|
|
[
|
|
"sqlite3_value_int",
|
|
"int",
|
|
"sqlite3_value*"
|
|
],
|
|
[
|
|
"sqlite3_value_nochange",
|
|
"int",
|
|
"sqlite3_value*"
|
|
],
|
|
[
|
|
"sqlite3_value_numeric_type",
|
|
"int",
|
|
"sqlite3_value*"
|
|
],
|
|
[
|
|
"sqlite3_value_pointer",
|
|
"*",
|
|
"sqlite3_value*",
|
|
"string:static"
|
|
],
|
|
[
|
|
"sqlite3_value_subtype",
|
|
"int",
|
|
"sqlite3_value*"
|
|
],
|
|
[
|
|
"sqlite3_value_type",
|
|
"int",
|
|
"sqlite3_value*"
|
|
],
|
|
[
|
|
"sqlite3_vfs_find",
|
|
"*",
|
|
"string"
|
|
],
|
|
[
|
|
"sqlite3_vfs_register",
|
|
"int",
|
|
"sqlite3_vfs*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_vfs_unregister",
|
|
"int",
|
|
"sqlite3_vfs*"
|
|
]
|
|
],
|
|
int64: [
|
|
[
|
|
"sqlite3_bind_int64",
|
|
"int",
|
|
[
|
|
"sqlite3_stmt*",
|
|
"int",
|
|
"i64"
|
|
]
|
|
],
|
|
[
|
|
"sqlite3_changes64",
|
|
"i64",
|
|
["sqlite3*"]
|
|
],
|
|
[
|
|
"sqlite3_column_int64",
|
|
"i64",
|
|
["sqlite3_stmt*", "int"]
|
|
],
|
|
[
|
|
"sqlite3_deserialize",
|
|
"int",
|
|
"sqlite3*",
|
|
"string",
|
|
"*",
|
|
"i64",
|
|
"i64",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_last_insert_rowid",
|
|
"i64",
|
|
["sqlite3*"]
|
|
],
|
|
[
|
|
"sqlite3_malloc64",
|
|
"*",
|
|
"i64"
|
|
],
|
|
[
|
|
"sqlite3_msize",
|
|
"i64",
|
|
"*"
|
|
],
|
|
[
|
|
"sqlite3_overload_function",
|
|
"int",
|
|
[
|
|
"sqlite3*",
|
|
"string",
|
|
"int"
|
|
]
|
|
],
|
|
[
|
|
"sqlite3_realloc64",
|
|
"*",
|
|
"*",
|
|
"i64"
|
|
],
|
|
[
|
|
"sqlite3_result_int64",
|
|
void 0,
|
|
"*",
|
|
"i64"
|
|
],
|
|
[
|
|
"sqlite3_result_zeroblob64",
|
|
"int",
|
|
"*",
|
|
"i64"
|
|
],
|
|
[
|
|
"sqlite3_serialize",
|
|
"*",
|
|
"sqlite3*",
|
|
"string",
|
|
"*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_set_last_insert_rowid",
|
|
void 0,
|
|
["sqlite3*", "i64"]
|
|
],
|
|
[
|
|
"sqlite3_status64",
|
|
"int",
|
|
"int",
|
|
"*",
|
|
"*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_db_status64",
|
|
"int",
|
|
"sqlite3*",
|
|
"int",
|
|
"*",
|
|
"*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3_total_changes64",
|
|
"i64",
|
|
["sqlite3*"]
|
|
],
|
|
[
|
|
"sqlite3_update_hook",
|
|
"*",
|
|
[
|
|
"sqlite3*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "sqlite3_update_hook::callback",
|
|
signature: "v(pippj)",
|
|
contextKey: (argv) => argv[0],
|
|
callProxy: (callback) => {
|
|
return (p, op, z0, z1, rowid) => {
|
|
callback(p, op, wasm.cstrToJs(z0), wasm.cstrToJs(z1), rowid);
|
|
};
|
|
}
|
|
}),
|
|
"*"
|
|
]
|
|
],
|
|
[
|
|
"sqlite3_uri_int64",
|
|
"i64",
|
|
[
|
|
"sqlite3_filename",
|
|
"string",
|
|
"i64"
|
|
]
|
|
],
|
|
[
|
|
"sqlite3_value_int64",
|
|
"i64",
|
|
"sqlite3_value*"
|
|
]
|
|
],
|
|
wasmInternal: [
|
|
[
|
|
"sqlite3__wasm_db_reset",
|
|
"int",
|
|
"sqlite3*"
|
|
],
|
|
[
|
|
"sqlite3__wasm_db_vfs",
|
|
"sqlite3_vfs*",
|
|
"sqlite3*",
|
|
"string"
|
|
],
|
|
[
|
|
"sqlite3__wasm_vfs_create_file",
|
|
"int",
|
|
"sqlite3_vfs*",
|
|
"string",
|
|
"*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3__wasm_posix_create_file",
|
|
"int",
|
|
"string",
|
|
"*",
|
|
"int"
|
|
],
|
|
[
|
|
"sqlite3__wasm_vfs_unlink",
|
|
"int",
|
|
"sqlite3_vfs*",
|
|
"string"
|
|
],
|
|
[
|
|
"sqlite3__wasm_qfmt_token",
|
|
"string:dealloc",
|
|
"string",
|
|
"int"
|
|
]
|
|
]
|
|
};
|
|
if (!!wasm.exports.sqlite3_progress_handler) bindingSignatures.core.push([
|
|
"sqlite3_progress_handler",
|
|
void 0,
|
|
[
|
|
"sqlite3*",
|
|
"int",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xProgressHandler",
|
|
signature: "i(p)",
|
|
bindScope: "context",
|
|
contextKey: (argv, argIndex) => argv[0]
|
|
}),
|
|
"*"
|
|
]
|
|
]);
|
|
if (!!wasm.exports.sqlite3_stmt_explain) bindingSignatures.core.push([
|
|
"sqlite3_stmt_explain",
|
|
"int",
|
|
"sqlite3_stmt*",
|
|
"int"
|
|
], [
|
|
"sqlite3_stmt_isexplain",
|
|
"int",
|
|
"sqlite3_stmt*"
|
|
]);
|
|
if (!!wasm.exports.sqlite3_set_authorizer) bindingSignatures.core.push([
|
|
"sqlite3_set_authorizer",
|
|
"int",
|
|
[
|
|
"sqlite3*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "sqlite3_set_authorizer::xAuth",
|
|
signature: "i(pissss)",
|
|
contextKey: (argv, argIndex) => argv[0],
|
|
callProxy: (callback) => {
|
|
return (pV, iCode, s0, s1, s2, s3) => {
|
|
try {
|
|
s0 = s0 && wasm.cstrToJs(s0);
|
|
s1 = s1 && wasm.cstrToJs(s1);
|
|
s2 = s2 && wasm.cstrToJs(s2);
|
|
s3 = s3 && wasm.cstrToJs(s3);
|
|
return callback(pV, iCode, s0, s1, s2, s3) | 0;
|
|
} catch (e) {
|
|
return e.resultCode || capi.SQLITE_ERROR;
|
|
}
|
|
};
|
|
}
|
|
}),
|
|
"*"
|
|
]
|
|
]);
|
|
if (!!wasm.exports.sqlite3_column_origin_name) bindingSignatures.core.push([
|
|
"sqlite3_column_database_name",
|
|
"string",
|
|
"sqlite3_stmt*",
|
|
"int"
|
|
], [
|
|
"sqlite3_column_origin_name",
|
|
"string",
|
|
"sqlite3_stmt*",
|
|
"int"
|
|
], [
|
|
"sqlite3_column_table_name",
|
|
"string",
|
|
"sqlite3_stmt*",
|
|
"int"
|
|
]);
|
|
if (wasm.bigIntEnabled && !!wasm.exports.sqlite3_declare_vtab) bindingSignatures.int64.push([
|
|
"sqlite3_create_module",
|
|
"int",
|
|
[
|
|
"sqlite3*",
|
|
"string",
|
|
"sqlite3_module*",
|
|
"*"
|
|
]
|
|
], [
|
|
"sqlite3_create_module_v2",
|
|
"int",
|
|
[
|
|
"sqlite3*",
|
|
"string",
|
|
"sqlite3_module*",
|
|
"*",
|
|
"*"
|
|
]
|
|
], [
|
|
"sqlite3_declare_vtab",
|
|
"int",
|
|
["sqlite3*", "string:flexible"]
|
|
], [
|
|
"sqlite3_drop_modules",
|
|
"int",
|
|
["sqlite3*", "**"]
|
|
], [
|
|
"sqlite3_vtab_collation",
|
|
"string",
|
|
"sqlite3_index_info*",
|
|
"int"
|
|
], [
|
|
"sqlite3_vtab_distinct",
|
|
"int",
|
|
"sqlite3_index_info*"
|
|
], [
|
|
"sqlite3_vtab_in",
|
|
"int",
|
|
"sqlite3_index_info*",
|
|
"int",
|
|
"int"
|
|
], [
|
|
"sqlite3_vtab_in_first",
|
|
"int",
|
|
"sqlite3_value*",
|
|
"**"
|
|
], [
|
|
"sqlite3_vtab_in_next",
|
|
"int",
|
|
"sqlite3_value*",
|
|
"**"
|
|
], [
|
|
"sqlite3_vtab_nochange",
|
|
"int",
|
|
"sqlite3_context*"
|
|
], [
|
|
"sqlite3_vtab_on_conflict",
|
|
"int",
|
|
"sqlite3*"
|
|
], [
|
|
"sqlite3_vtab_rhs_value",
|
|
"int",
|
|
"sqlite3_index_info*",
|
|
"int",
|
|
"**"
|
|
]);
|
|
if (wasm.bigIntEnabled && !!wasm.exports.sqlite3_preupdate_hook) bindingSignatures.int64.push([
|
|
"sqlite3_preupdate_blobwrite",
|
|
"int",
|
|
"sqlite3*"
|
|
], [
|
|
"sqlite3_preupdate_count",
|
|
"int",
|
|
"sqlite3*"
|
|
], [
|
|
"sqlite3_preupdate_depth",
|
|
"int",
|
|
"sqlite3*"
|
|
], [
|
|
"sqlite3_preupdate_hook",
|
|
"*",
|
|
[
|
|
"sqlite3*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "sqlite3_preupdate_hook",
|
|
signature: "v(ppippjj)",
|
|
contextKey: (argv) => argv[0],
|
|
callProxy: (callback) => {
|
|
return (p, db, op, zDb, zTbl, iKey1, iKey2) => {
|
|
callback(p, db, op, wasm.cstrToJs(zDb), wasm.cstrToJs(zTbl), iKey1, iKey2);
|
|
};
|
|
}
|
|
}),
|
|
"*"
|
|
]
|
|
], [
|
|
"sqlite3_preupdate_new",
|
|
"int",
|
|
[
|
|
"sqlite3*",
|
|
"int",
|
|
"**"
|
|
]
|
|
], [
|
|
"sqlite3_preupdate_old",
|
|
"int",
|
|
[
|
|
"sqlite3*",
|
|
"int",
|
|
"**"
|
|
]
|
|
]);
|
|
if (wasm.bigIntEnabled && !!wasm.exports.sqlite3changegroup_add && !!wasm.exports.sqlite3session_create && !!wasm.exports.sqlite3_preupdate_hook) {
|
|
const __ipsProxy = {
|
|
signature: "i(ps)",
|
|
callProxy: (callback) => {
|
|
return (p, s) => {
|
|
try {
|
|
return callback(p, wasm.cstrToJs(s)) | 0;
|
|
} catch (e) {
|
|
return e.resultCode || capi.SQLITE_ERROR;
|
|
}
|
|
};
|
|
}
|
|
};
|
|
bindingSignatures.int64.push([
|
|
"sqlite3changegroup_add",
|
|
"int",
|
|
[
|
|
"sqlite3_changegroup*",
|
|
"int",
|
|
"void*"
|
|
]
|
|
], [
|
|
"sqlite3changegroup_add_strm",
|
|
"int",
|
|
[
|
|
"sqlite3_changegroup*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xInput",
|
|
signature: "i(ppp)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*"
|
|
]
|
|
], [
|
|
"sqlite3changegroup_delete",
|
|
void 0,
|
|
["sqlite3_changegroup*"]
|
|
], [
|
|
"sqlite3changegroup_new",
|
|
"int",
|
|
["**"]
|
|
], [
|
|
"sqlite3changegroup_output",
|
|
"int",
|
|
[
|
|
"sqlite3_changegroup*",
|
|
"int*",
|
|
"**"
|
|
]
|
|
], [
|
|
"sqlite3changegroup_output_strm",
|
|
"int",
|
|
[
|
|
"sqlite3_changegroup*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xOutput",
|
|
signature: "i(ppi)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*"
|
|
]
|
|
], [
|
|
"sqlite3changeset_apply",
|
|
"int",
|
|
[
|
|
"sqlite3*",
|
|
"int",
|
|
"void*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xFilter",
|
|
bindScope: "transient",
|
|
...__ipsProxy
|
|
}),
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xConflict",
|
|
signature: "i(pip)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*"
|
|
]
|
|
], [
|
|
"sqlite3changeset_apply_strm",
|
|
"int",
|
|
[
|
|
"sqlite3*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xInput",
|
|
signature: "i(ppp)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xFilter",
|
|
bindScope: "transient",
|
|
...__ipsProxy
|
|
}),
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xConflict",
|
|
signature: "i(pip)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*"
|
|
]
|
|
], [
|
|
"sqlite3changeset_apply_v2",
|
|
"int",
|
|
[
|
|
"sqlite3*",
|
|
"int",
|
|
"void*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xFilter",
|
|
bindScope: "transient",
|
|
...__ipsProxy
|
|
}),
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xConflict",
|
|
signature: "i(pip)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*",
|
|
"**",
|
|
"int*",
|
|
"int"
|
|
]
|
|
], [
|
|
"sqlite3changeset_apply_v2_strm",
|
|
"int",
|
|
[
|
|
"sqlite3*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xInput",
|
|
signature: "i(ppp)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xFilter",
|
|
bindScope: "transient",
|
|
...__ipsProxy
|
|
}),
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xConflict",
|
|
signature: "i(pip)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*",
|
|
"**",
|
|
"int*",
|
|
"int"
|
|
]
|
|
], [
|
|
"sqlite3changeset_apply_v3",
|
|
"int",
|
|
[
|
|
"sqlite3*",
|
|
"int",
|
|
"void*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xFilter",
|
|
signature: "i(pp)",
|
|
bindScope: "transient"
|
|
}),
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xConflict",
|
|
signature: "i(pip)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*",
|
|
"**",
|
|
"int*",
|
|
"int"
|
|
]
|
|
], [
|
|
"sqlite3changeset_apply_v3_strm",
|
|
"int",
|
|
[
|
|
"sqlite3*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xInput",
|
|
signature: "i(ppp)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xFilter",
|
|
signature: "i(pp)",
|
|
bindScope: "transient"
|
|
}),
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xConflict",
|
|
signature: "i(pip)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*",
|
|
"**",
|
|
"int*",
|
|
"int"
|
|
]
|
|
], [
|
|
"sqlite3changeset_concat",
|
|
"int",
|
|
[
|
|
"int",
|
|
"void*",
|
|
"int",
|
|
"void*",
|
|
"int*",
|
|
"**"
|
|
]
|
|
], [
|
|
"sqlite3changeset_concat_strm",
|
|
"int",
|
|
[
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xInputA",
|
|
signature: "i(ppp)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xInputB",
|
|
signature: "i(ppp)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xOutput",
|
|
signature: "i(ppi)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*"
|
|
]
|
|
], [
|
|
"sqlite3changeset_conflict",
|
|
"int",
|
|
[
|
|
"sqlite3_changeset_iter*",
|
|
"int",
|
|
"**"
|
|
]
|
|
], [
|
|
"sqlite3changeset_finalize",
|
|
"int",
|
|
["sqlite3_changeset_iter*"]
|
|
], [
|
|
"sqlite3changeset_fk_conflicts",
|
|
"int",
|
|
["sqlite3_changeset_iter*", "int*"]
|
|
], [
|
|
"sqlite3changeset_invert",
|
|
"int",
|
|
[
|
|
"int",
|
|
"void*",
|
|
"int*",
|
|
"**"
|
|
]
|
|
], [
|
|
"sqlite3changeset_invert_strm",
|
|
"int",
|
|
[
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xInput",
|
|
signature: "i(ppp)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xOutput",
|
|
signature: "i(ppi)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*"
|
|
]
|
|
], [
|
|
"sqlite3changeset_new",
|
|
"int",
|
|
[
|
|
"sqlite3_changeset_iter*",
|
|
"int",
|
|
"**"
|
|
]
|
|
], [
|
|
"sqlite3changeset_next",
|
|
"int",
|
|
["sqlite3_changeset_iter*"]
|
|
], [
|
|
"sqlite3changeset_old",
|
|
"int",
|
|
[
|
|
"sqlite3_changeset_iter*",
|
|
"int",
|
|
"**"
|
|
]
|
|
], [
|
|
"sqlite3changeset_op",
|
|
"int",
|
|
[
|
|
"sqlite3_changeset_iter*",
|
|
"**",
|
|
"int*",
|
|
"int*",
|
|
"int*"
|
|
]
|
|
], [
|
|
"sqlite3changeset_pk",
|
|
"int",
|
|
[
|
|
"sqlite3_changeset_iter*",
|
|
"**",
|
|
"int*"
|
|
]
|
|
], [
|
|
"sqlite3changeset_start",
|
|
"int",
|
|
[
|
|
"**",
|
|
"int",
|
|
"*"
|
|
]
|
|
], [
|
|
"sqlite3changeset_start_strm",
|
|
"int",
|
|
[
|
|
"**",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xInput",
|
|
signature: "i(ppp)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*"
|
|
]
|
|
], [
|
|
"sqlite3changeset_start_v2",
|
|
"int",
|
|
[
|
|
"**",
|
|
"int",
|
|
"*",
|
|
"int"
|
|
]
|
|
], [
|
|
"sqlite3changeset_start_v2_strm",
|
|
"int",
|
|
[
|
|
"**",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xInput",
|
|
signature: "i(ppp)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*",
|
|
"int"
|
|
]
|
|
], [
|
|
"sqlite3session_attach",
|
|
"int",
|
|
["sqlite3_session*", "string"]
|
|
], [
|
|
"sqlite3session_changeset",
|
|
"int",
|
|
[
|
|
"sqlite3_session*",
|
|
"int*",
|
|
"**"
|
|
]
|
|
], [
|
|
"sqlite3session_changeset_size",
|
|
"i64",
|
|
["sqlite3_session*"]
|
|
], [
|
|
"sqlite3session_changeset_strm",
|
|
"int",
|
|
[
|
|
"sqlite3_session*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xOutput",
|
|
signature: "i(ppp)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*"
|
|
]
|
|
], [
|
|
"sqlite3session_config",
|
|
"int",
|
|
["int", "void*"]
|
|
], [
|
|
"sqlite3session_create",
|
|
"int",
|
|
[
|
|
"sqlite3*",
|
|
"string",
|
|
"**"
|
|
]
|
|
], [
|
|
"sqlite3session_diff",
|
|
"int",
|
|
[
|
|
"sqlite3_session*",
|
|
"string",
|
|
"string",
|
|
"**"
|
|
]
|
|
], [
|
|
"sqlite3session_enable",
|
|
"int",
|
|
["sqlite3_session*", "int"]
|
|
], [
|
|
"sqlite3session_indirect",
|
|
"int",
|
|
["sqlite3_session*", "int"]
|
|
], [
|
|
"sqlite3session_isempty",
|
|
"int",
|
|
["sqlite3_session*"]
|
|
], [
|
|
"sqlite3session_memory_used",
|
|
"i64",
|
|
["sqlite3_session*"]
|
|
], [
|
|
"sqlite3session_object_config",
|
|
"int",
|
|
[
|
|
"sqlite3_session*",
|
|
"int",
|
|
"void*"
|
|
]
|
|
], [
|
|
"sqlite3session_patchset",
|
|
"int",
|
|
[
|
|
"sqlite3_session*",
|
|
"*",
|
|
"**"
|
|
]
|
|
], [
|
|
"sqlite3session_patchset_strm",
|
|
"int",
|
|
[
|
|
"sqlite3_session*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xOutput",
|
|
signature: "i(ppp)",
|
|
bindScope: "transient"
|
|
}),
|
|
"void*"
|
|
]
|
|
], [
|
|
"sqlite3session_table_filter",
|
|
void 0,
|
|
[
|
|
"sqlite3_session*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xFilter",
|
|
...__ipsProxy,
|
|
contextKey: (argv, argIndex) => argv[0]
|
|
}),
|
|
"*"
|
|
]
|
|
]);
|
|
}
|
|
sqlite3.StructBinder = globalThis.Jaccwabyt({
|
|
heap: wasm.heap8u,
|
|
alloc: wasm.alloc,
|
|
dealloc: wasm.dealloc,
|
|
bigIntEnabled: wasm.bigIntEnabled,
|
|
pointerIR: wasm.ptr.ir,
|
|
memberPrefix: "$"
|
|
});
|
|
delete globalThis.Jaccwabyt;
|
|
{
|
|
const __xString = wasm.xWrap.argAdapter("string");
|
|
wasm.xWrap.argAdapter("string:flexible", (v) => __xString(util.flexibleString(v)));
|
|
wasm.xWrap.argAdapter("string:static", function(v) {
|
|
if (wasm.isPtr(v)) return v;
|
|
v = "" + v;
|
|
return this[v] || (this[v] = wasm.allocCString(v));
|
|
}.bind(Object.create(null)));
|
|
const __xArgPtr = wasm.xWrap.argAdapter("*");
|
|
const nilType = function() {};
|
|
wasm.xWrap.argAdapter("sqlite3_filename", __xArgPtr)("sqlite3_context*", __xArgPtr)("sqlite3_value*", __xArgPtr)("void*", __xArgPtr)("sqlite3_changegroup*", __xArgPtr)("sqlite3_changeset_iter*", __xArgPtr)("sqlite3_session*", __xArgPtr)("sqlite3_stmt*", (v) => __xArgPtr(v instanceof (sqlite3?.oo1?.Stmt || nilType) ? v.pointer : v))("sqlite3*", (v) => __xArgPtr(v instanceof (sqlite3?.oo1?.DB || nilType) ? v.pointer : v))("sqlite3_vfs*", (v) => {
|
|
if ("string" === typeof v) return capi.sqlite3_vfs_find(v) || sqlite3.SQLite3Error.toss(capi.SQLITE_NOTFOUND, "Unknown sqlite3_vfs name:", v);
|
|
return __xArgPtr(v instanceof (capi.sqlite3_vfs || nilType) ? v.pointer : v);
|
|
});
|
|
if (wasm.exports.sqlite3_declare_vtab) wasm.xWrap.argAdapter("sqlite3_index_info*", (v) => __xArgPtr(v instanceof (capi.sqlite3_index_info || nilType) ? v.pointer : v))("sqlite3_module*", (v) => __xArgPtr(v instanceof (capi.sqlite3_module || nilType) ? v.pointer : v));
|
|
const __xRcPtr = wasm.xWrap.resultAdapter("*");
|
|
wasm.xWrap.resultAdapter("sqlite3*", __xRcPtr)("sqlite3_context*", __xRcPtr)("sqlite3_stmt*", __xRcPtr)("sqlite3_value*", __xRcPtr)("sqlite3_vfs*", __xRcPtr)("void*", __xRcPtr);
|
|
for (const e of bindingSignatures.core) capi[e[0]] = wasm.xWrap.apply(null, e);
|
|
for (const e of bindingSignatures.wasmInternal) util[e[0]] = wasm.xWrap.apply(null, e);
|
|
for (const e of bindingSignatures.int64) capi[e[0]] = wasm.bigIntEnabled ? wasm.xWrap.apply(null, e) : () => toss(e[0] + "() is unavailable due to lack", "of BigInt support in this build.");
|
|
delete bindingSignatures.core;
|
|
delete bindingSignatures.int64;
|
|
delete bindingSignatures.wasmInternal;
|
|
util.sqlite3__wasm_db_error = function(pDb, resultCode, message) {
|
|
if (!pDb) return capi.SQLITE_MISUSE;
|
|
if (resultCode instanceof sqlite3.WasmAllocError) {
|
|
resultCode = capi.SQLITE_NOMEM;
|
|
message = 0;
|
|
} else if (resultCode instanceof Error) {
|
|
message = message || "" + resultCode;
|
|
resultCode = resultCode.resultCode || capi.SQLITE_ERROR;
|
|
}
|
|
return capi.sqlite3_set_errmsg(pDb, resultCode, message) || resultCode;
|
|
};
|
|
}
|
|
{
|
|
const cJson = wasm.xCall("sqlite3__wasm_enum_json");
|
|
if (!cJson) toss("Maintenance required: increase sqlite3__wasm_enum_json()'s", "static buffer size!");
|
|
wasm.ctype = JSON.parse(wasm.cstrToJs(cJson));
|
|
const defineGroups = [
|
|
"access",
|
|
"authorizer",
|
|
"blobFinalizers",
|
|
"changeset",
|
|
"config",
|
|
"dataTypes",
|
|
"dbConfig",
|
|
"dbStatus",
|
|
"encodings",
|
|
"fcntl",
|
|
"flock",
|
|
"ioCap",
|
|
"limits",
|
|
"openFlags",
|
|
"prepareFlags",
|
|
"resultCodes",
|
|
"sqlite3Status",
|
|
"stmtStatus",
|
|
"syncFlags",
|
|
"trace",
|
|
"txnState",
|
|
"udfFlags",
|
|
"version"
|
|
];
|
|
if (wasm.bigIntEnabled) defineGroups.push("serialize", "session", "vtab");
|
|
for (const t of defineGroups) for (const e of Object.entries(wasm.ctype[t])) capi[e[0]] = e[1];
|
|
if (!wasm.functionEntry(capi.SQLITE_WASM_DEALLOC)) toss("Internal error: cannot resolve exported function", "entry SQLITE_WASM_DEALLOC (==" + capi.SQLITE_WASM_DEALLOC + ").");
|
|
const __rcMap = Object.create(null);
|
|
for (const e of Object.entries(wasm.ctype["resultCodes"])) __rcMap[e[1]] = e[0];
|
|
capi.sqlite3_js_rc_str = (rc) => __rcMap[rc];
|
|
const notThese = Object.assign(Object.create(null), {
|
|
WasmTestStruct: true,
|
|
sqlite3_index_info: !wasm.bigIntEnabled,
|
|
sqlite3_index_constraint: !wasm.bigIntEnabled,
|
|
sqlite3_index_orderby: !wasm.bigIntEnabled,
|
|
sqlite3_index_constraint_usage: !wasm.bigIntEnabled
|
|
});
|
|
for (const s of wasm.ctype.structs) if (!notThese[s.name]) capi[s.name] = sqlite3.StructBinder(s);
|
|
if (capi.sqlite3_index_info) {
|
|
for (const k of [
|
|
"sqlite3_index_constraint",
|
|
"sqlite3_index_orderby",
|
|
"sqlite3_index_constraint_usage"
|
|
]) {
|
|
capi.sqlite3_index_info[k] = capi[k];
|
|
delete capi[k];
|
|
}
|
|
capi.sqlite3_vtab_config = wasm.xWrap("sqlite3__wasm_vtab_config", "int", [
|
|
"sqlite3*",
|
|
"int",
|
|
"int"
|
|
]);
|
|
}
|
|
}
|
|
const __dbArgcMismatch = (pDb, f, n) => {
|
|
return util.sqlite3__wasm_db_error(pDb, capi.SQLITE_MISUSE, f + "() requires " + n + " argument" + (1 === n ? "" : "s") + ".");
|
|
};
|
|
const __errEncoding = (pDb) => {
|
|
return util.sqlite3__wasm_db_error(pDb, capi.SQLITE_FORMAT, "SQLITE_UTF8 is the only supported encoding.");
|
|
};
|
|
const __argPDb = (pDb) => wasm.xWrap.argAdapter("sqlite3*")(pDb);
|
|
const __argStr = (str) => wasm.isPtr(str) ? wasm.cstrToJs(str) : str;
|
|
const __dbCleanupMap = function(pDb, mode) {
|
|
pDb = __argPDb(pDb);
|
|
let m = this.dbMap.get(pDb);
|
|
if (!mode) {
|
|
this.dbMap.delete(pDb);
|
|
return m;
|
|
} else if (!m && mode > 0) this.dbMap.set(pDb, m = Object.create(null));
|
|
return m;
|
|
}.bind(Object.assign(Object.create(null), { dbMap: /* @__PURE__ */ new Map() }));
|
|
__dbCleanupMap.addCollation = function(pDb, name) {
|
|
const m = __dbCleanupMap(pDb, 1);
|
|
if (!m.collation) m.collation = /* @__PURE__ */ new Set();
|
|
m.collation.add(__argStr(name).toLowerCase());
|
|
};
|
|
__dbCleanupMap._addUDF = function(pDb, name, arity, map) {
|
|
name = __argStr(name).toLowerCase();
|
|
let u = map.get(name);
|
|
if (!u) map.set(name, u = /* @__PURE__ */ new Set());
|
|
u.add(arity < 0 ? -1 : arity);
|
|
};
|
|
__dbCleanupMap.addFunction = function(pDb, name, arity) {
|
|
const m = __dbCleanupMap(pDb, 1);
|
|
if (!m.udf) m.udf = /* @__PURE__ */ new Map();
|
|
this._addUDF(pDb, name, arity, m.udf);
|
|
};
|
|
if (wasm.exports.sqlite3_create_window_function) __dbCleanupMap.addWindowFunc = function(pDb, name, arity) {
|
|
const m = __dbCleanupMap(pDb, 1);
|
|
if (!m.wudf) m.wudf = /* @__PURE__ */ new Map();
|
|
this._addUDF(pDb, name, arity, m.wudf);
|
|
};
|
|
__dbCleanupMap.cleanup = function(pDb) {
|
|
pDb = __argPDb(pDb);
|
|
for (const obj of [
|
|
["sqlite3_busy_handler", 3],
|
|
["sqlite3_commit_hook", 3],
|
|
["sqlite3_preupdate_hook", 3],
|
|
["sqlite3_progress_handler", 4],
|
|
["sqlite3_rollback_hook", 3],
|
|
["sqlite3_set_authorizer", 3],
|
|
["sqlite3_trace_v2", 4],
|
|
["sqlite3_update_hook", 3]
|
|
]) {
|
|
const [name, arity] = obj;
|
|
if (!wasm.exports[name]) continue;
|
|
const closeArgs = [pDb];
|
|
closeArgs.length = arity;
|
|
try {
|
|
capi[name](...closeArgs);
|
|
} catch (e) {
|
|
sqlite3.config.warn("close-time call of", name + "(", closeArgs, ") threw:", e);
|
|
}
|
|
}
|
|
const m = __dbCleanupMap(pDb, 0);
|
|
if (!m) return;
|
|
if (m.collation) {
|
|
for (const name of m.collation) try {
|
|
capi.sqlite3_create_collation_v2(pDb, name, capi.SQLITE_UTF8, 0, 0, 0);
|
|
} catch (e) {}
|
|
delete m.collation;
|
|
}
|
|
let i;
|
|
for (i = 0; i < 2; ++i) {
|
|
const fmap = i ? m.wudf : m.udf;
|
|
if (!fmap) continue;
|
|
const func = i ? capi.sqlite3_create_window_function : capi.sqlite3_create_function_v2;
|
|
for (const e of fmap) {
|
|
const name = e[0], arities = e[1];
|
|
const fargs = [
|
|
pDb,
|
|
name,
|
|
0,
|
|
capi.SQLITE_UTF8,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0
|
|
];
|
|
if (i) fargs.push(0);
|
|
for (const arity of arities) try {
|
|
fargs[2] = arity;
|
|
func.apply(null, fargs);
|
|
} catch (e) {}
|
|
arities.clear();
|
|
}
|
|
fmap.clear();
|
|
}
|
|
delete m.udf;
|
|
delete m.wudf;
|
|
};
|
|
{
|
|
const __sqlite3CloseV2 = wasm.xWrap("sqlite3_close_v2", "int", "sqlite3*");
|
|
capi.sqlite3_close_v2 = function(pDb) {
|
|
if (1 !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_close_v2", 1);
|
|
if (pDb) try {
|
|
__dbCleanupMap.cleanup(pDb);
|
|
} catch (e) {}
|
|
return __sqlite3CloseV2(pDb);
|
|
};
|
|
}
|
|
if (capi.sqlite3session_create) {
|
|
const __sqlite3SessionDelete = wasm.xWrap("sqlite3session_delete", void 0, ["sqlite3_session*"]);
|
|
capi.sqlite3session_delete = function(pSession) {
|
|
if (1 !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3session_delete", 1);
|
|
else if (pSession) capi.sqlite3session_table_filter(pSession, 0, 0);
|
|
__sqlite3SessionDelete(pSession);
|
|
};
|
|
}
|
|
{
|
|
const contextKey = (argv, argIndex) => {
|
|
return "argv[" + argIndex + "]:" + argv[0] + ":" + wasm.cstrToJs(argv[1]).toLowerCase();
|
|
};
|
|
const __sqlite3CreateCollationV2 = wasm.xWrap("sqlite3_create_collation_v2", "int", [
|
|
"sqlite3*",
|
|
"string",
|
|
"int",
|
|
"*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xCompare",
|
|
signature: "i(pipip)",
|
|
contextKey
|
|
}),
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xDestroy",
|
|
signature: "v(p)",
|
|
contextKey
|
|
})
|
|
]);
|
|
capi.sqlite3_create_collation_v2 = function(pDb, zName, eTextRep, pArg, xCompare, xDestroy) {
|
|
if (6 !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_create_collation_v2", 6);
|
|
else if (0 === (eTextRep & 15)) eTextRep |= capi.SQLITE_UTF8;
|
|
else if (capi.SQLITE_UTF8 !== (eTextRep & 15)) return __errEncoding(pDb);
|
|
try {
|
|
const rc = __sqlite3CreateCollationV2(pDb, zName, eTextRep, pArg, xCompare, xDestroy);
|
|
if (0 === rc && xCompare instanceof Function) __dbCleanupMap.addCollation(pDb, zName);
|
|
return rc;
|
|
} catch (e) {
|
|
return util.sqlite3__wasm_db_error(pDb, e);
|
|
}
|
|
};
|
|
capi.sqlite3_create_collation = (pDb, zName, eTextRep, pArg, xCompare) => {
|
|
return 5 === arguments.length ? capi.sqlite3_create_collation_v2(pDb, zName, eTextRep, pArg, xCompare, 0) : __dbArgcMismatch(pDb, "sqlite3_create_collation", 5);
|
|
};
|
|
}
|
|
{
|
|
const contextKey = function(argv, argIndex) {
|
|
return argv[0] + ":" + (argv[2] < 0 ? -1 : argv[2]) + ":" + argIndex + ":" + wasm.cstrToJs(argv[1]).toLowerCase();
|
|
};
|
|
const __cfProxy = Object.assign(Object.create(null), {
|
|
xInverseAndStep: {
|
|
signature: "v(pip)",
|
|
contextKey,
|
|
callProxy: (callback) => {
|
|
return (pCtx, argc, pArgv) => {
|
|
try {
|
|
callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv));
|
|
} catch (e) {
|
|
capi.sqlite3_result_error_js(pCtx, e);
|
|
}
|
|
};
|
|
}
|
|
},
|
|
xFinalAndValue: {
|
|
signature: "v(p)",
|
|
contextKey,
|
|
callProxy: (callback) => {
|
|
return (pCtx) => {
|
|
try {
|
|
capi.sqlite3_result_js(pCtx, callback(pCtx));
|
|
} catch (e) {
|
|
capi.sqlite3_result_error_js(pCtx, e);
|
|
}
|
|
};
|
|
}
|
|
},
|
|
xFunc: {
|
|
signature: "v(pip)",
|
|
contextKey,
|
|
callProxy: (callback) => {
|
|
return (pCtx, argc, pArgv) => {
|
|
try {
|
|
capi.sqlite3_result_js(pCtx, callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv)));
|
|
} catch (e) {
|
|
capi.sqlite3_result_error_js(pCtx, e);
|
|
}
|
|
};
|
|
}
|
|
},
|
|
xDestroy: {
|
|
signature: "v(p)",
|
|
contextKey,
|
|
callProxy: (callback) => {
|
|
return (pVoid) => {
|
|
try {
|
|
callback(pVoid);
|
|
} catch (e) {
|
|
console.error("UDF xDestroy method threw:", e);
|
|
}
|
|
};
|
|
}
|
|
}
|
|
});
|
|
const __sqlite3CreateFunction = wasm.xWrap("sqlite3_create_function_v2", "int", [
|
|
"sqlite3*",
|
|
"string",
|
|
"int",
|
|
"int",
|
|
"*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xFunc",
|
|
...__cfProxy.xFunc
|
|
}),
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xStep",
|
|
...__cfProxy.xInverseAndStep
|
|
}),
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xFinal",
|
|
...__cfProxy.xFinalAndValue
|
|
}),
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xDestroy",
|
|
...__cfProxy.xDestroy
|
|
})
|
|
]);
|
|
const __sqlite3CreateWindowFunction = wasm.exports.sqlite3_create_window_function ? wasm.xWrap("sqlite3_create_window_function", "int", [
|
|
"sqlite3*",
|
|
"string",
|
|
"int",
|
|
"int",
|
|
"*",
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xStep",
|
|
...__cfProxy.xInverseAndStep
|
|
}),
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xFinal",
|
|
...__cfProxy.xFinalAndValue
|
|
}),
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xValue",
|
|
...__cfProxy.xFinalAndValue
|
|
}),
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xInverse",
|
|
...__cfProxy.xInverseAndStep
|
|
}),
|
|
new wasm.xWrap.FuncPtrAdapter({
|
|
name: "xDestroy",
|
|
...__cfProxy.xDestroy
|
|
})
|
|
]) : void 0;
|
|
capi.sqlite3_create_function_v2 = function f(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, xDestroy) {
|
|
if (f.length !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_create_function_v2", f.length);
|
|
else if (0 === (eTextRep & 15)) eTextRep |= capi.SQLITE_UTF8;
|
|
else if (capi.SQLITE_UTF8 !== (eTextRep & 15)) return __errEncoding(pDb);
|
|
try {
|
|
const rc = __sqlite3CreateFunction(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, xDestroy);
|
|
if (0 === rc && (xFunc instanceof Function || xStep instanceof Function || xFinal instanceof Function || xDestroy instanceof Function)) __dbCleanupMap.addFunction(pDb, funcName, nArg);
|
|
return rc;
|
|
} catch (e) {
|
|
console.error("sqlite3_create_function_v2() setup threw:", e);
|
|
return util.sqlite3__wasm_db_error(pDb, e, "Creation of UDF threw: " + e);
|
|
}
|
|
};
|
|
capi.sqlite3_create_function = function f(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal) {
|
|
return f.length === arguments.length ? capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, 0) : __dbArgcMismatch(pDb, "sqlite3_create_function", f.length);
|
|
};
|
|
if (__sqlite3CreateWindowFunction) capi.sqlite3_create_window_function = function f(pDb, funcName, nArg, eTextRep, pApp, xStep, xFinal, xValue, xInverse, xDestroy) {
|
|
if (f.length !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_create_window_function", f.length);
|
|
else if (0 === (eTextRep & 15)) eTextRep |= capi.SQLITE_UTF8;
|
|
else if (capi.SQLITE_UTF8 !== (eTextRep & 15)) return __errEncoding(pDb);
|
|
try {
|
|
const rc = __sqlite3CreateWindowFunction(pDb, funcName, nArg, eTextRep, pApp, xStep, xFinal, xValue, xInverse, xDestroy);
|
|
if (0 === rc && (xStep instanceof Function || xFinal instanceof Function || xValue instanceof Function || xInverse instanceof Function || xDestroy instanceof Function)) __dbCleanupMap.addWindowFunc(pDb, funcName, nArg);
|
|
return rc;
|
|
} catch (e) {
|
|
console.error("sqlite3_create_window_function() setup threw:", e);
|
|
return util.sqlite3__wasm_db_error(pDb, e, "Creation of UDF threw: " + e);
|
|
}
|
|
};
|
|
else delete capi.sqlite3_create_window_function;
|
|
capi.sqlite3_create_function_v2.udfSetResult = capi.sqlite3_create_function.udfSetResult = capi.sqlite3_result_js;
|
|
if (capi.sqlite3_create_window_function) capi.sqlite3_create_window_function.udfSetResult = capi.sqlite3_result_js;
|
|
capi.sqlite3_create_function_v2.udfConvertArgs = capi.sqlite3_create_function.udfConvertArgs = capi.sqlite3_values_to_js;
|
|
if (capi.sqlite3_create_window_function) capi.sqlite3_create_window_function.udfConvertArgs = capi.sqlite3_values_to_js;
|
|
capi.sqlite3_create_function_v2.udfSetError = capi.sqlite3_create_function.udfSetError = capi.sqlite3_result_error_js;
|
|
if (capi.sqlite3_create_window_function) capi.sqlite3_create_window_function.udfSetError = capi.sqlite3_result_error_js;
|
|
}
|
|
{
|
|
const __flexiString = (v, n) => {
|
|
if ("string" === typeof v) n = -1;
|
|
else if (util.isSQLableTypedArray(v)) {
|
|
n = v.byteLength;
|
|
v = wasm.typedArrayToString(v instanceof ArrayBuffer ? new Uint8Array(v) : v);
|
|
} else if (Array.isArray(v)) {
|
|
v = v.join("");
|
|
n = -1;
|
|
}
|
|
return [v, n];
|
|
};
|
|
const __prepare = {
|
|
basic: wasm.xWrap("sqlite3_prepare_v3", "int", [
|
|
"sqlite3*",
|
|
"string",
|
|
"int",
|
|
"int",
|
|
"**",
|
|
"**"
|
|
]),
|
|
full: wasm.xWrap("sqlite3_prepare_v3", "int", [
|
|
"sqlite3*",
|
|
"*",
|
|
"int",
|
|
"int",
|
|
"**",
|
|
"**"
|
|
])
|
|
};
|
|
capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail) {
|
|
if (f.length !== arguments.length) return __dbArgcMismatch(pDb, "sqlite3_prepare_v3", f.length);
|
|
const [xSql, xSqlLen] = __flexiString(sql, Number(sqlLen));
|
|
switch (typeof xSql) {
|
|
case "string": return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null);
|
|
case typeof wasm.ptr.null: return __prepare.full(pDb, wasm.ptr.coerce(xSql), xSqlLen, prepFlags, ppStmt, pzTail);
|
|
default: return util.sqlite3__wasm_db_error(pDb, capi.SQLITE_MISUSE, "Invalid SQL argument type for sqlite3_prepare_v2/v3(). typeof=" + typeof xSql);
|
|
}
|
|
};
|
|
capi.sqlite3_prepare_v2 = function f(pDb, sql, sqlLen, ppStmt, pzTail) {
|
|
return f.length === arguments.length ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail) : __dbArgcMismatch(pDb, "sqlite3_prepare_v2", f.length);
|
|
};
|
|
}
|
|
{
|
|
const __bindText = wasm.xWrap("sqlite3_bind_text", "int", [
|
|
"sqlite3_stmt*",
|
|
"int",
|
|
"string",
|
|
"int",
|
|
"*"
|
|
]);
|
|
const __bindBlob = wasm.xWrap("sqlite3_bind_blob", "int", [
|
|
"sqlite3_stmt*",
|
|
"int",
|
|
"*",
|
|
"int",
|
|
"*"
|
|
]);
|
|
capi.sqlite3_bind_text = function f(pStmt, iCol, text, nText, xDestroy) {
|
|
if (f.length !== arguments.length) return __dbArgcMismatch(capi.sqlite3_db_handle(pStmt), "sqlite3_bind_text", f.length);
|
|
else if (wasm.isPtr(text) || null === text) return __bindText(pStmt, iCol, text, nText, xDestroy);
|
|
else if (text instanceof ArrayBuffer) text = new Uint8Array(text);
|
|
else if (Array.isArray(pMem)) text = pMem.join("");
|
|
let p, n;
|
|
try {
|
|
if (util.isSQLableTypedArray(text)) {
|
|
p = wasm.allocFromTypedArray(text);
|
|
n = text.byteLength;
|
|
} else if ("string" === typeof text) [p, n] = wasm.allocCString(text);
|
|
else return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), capi.SQLITE_MISUSE, "Invalid 3rd argument type for sqlite3_bind_text().");
|
|
return __bindText(pStmt, iCol, p, n, capi.SQLITE_WASM_DEALLOC);
|
|
} catch (e) {
|
|
wasm.dealloc(p);
|
|
return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), e);
|
|
}
|
|
};
|
|
capi.sqlite3_bind_blob = function f(pStmt, iCol, pMem, nMem, xDestroy) {
|
|
if (f.length !== arguments.length) return __dbArgcMismatch(capi.sqlite3_db_handle(pStmt), "sqlite3_bind_blob", f.length);
|
|
else if (wasm.isPtr(pMem) || null === pMem) return __bindBlob(pStmt, iCol, pMem, nMem, xDestroy);
|
|
else if (pMem instanceof ArrayBuffer) pMem = new Uint8Array(pMem);
|
|
else if (Array.isArray(pMem)) pMem = pMem.join("");
|
|
let p, n;
|
|
try {
|
|
if (util.isBindableTypedArray(pMem)) {
|
|
p = wasm.allocFromTypedArray(pMem);
|
|
n = nMem >= 0 ? nMem : pMem.byteLength;
|
|
} else if ("string" === typeof pMem) [p, n] = wasm.allocCString(pMem);
|
|
else return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), capi.SQLITE_MISUSE, "Invalid 3rd argument type for sqlite3_bind_blob().");
|
|
return __bindBlob(pStmt, iCol, p, n, capi.SQLITE_WASM_DEALLOC);
|
|
} catch (e) {
|
|
wasm.dealloc(p);
|
|
return util.sqlite3__wasm_db_error(capi.sqlite3_db_handle(pStmt), e);
|
|
}
|
|
};
|
|
}
|
|
if (!capi.sqlite3_column_text) {
|
|
const argStmt = wasm.xWrap.argAdapter("sqlite3_stmt*"), argInt = wasm.xWrap.argAdapter("int"), argValue = wasm.xWrap.argAdapter("sqlite3_value*"), newStr = (cstr, n) => wasm.typedArrayToString(wasm.heap8u(), Number(cstr), Number(cstr) + n);
|
|
capi.sqlite3_column_text = function(stmt, colIndex) {
|
|
const a0 = argStmt(stmt), a1 = argInt(colIndex);
|
|
const cstr = wasm.exports.sqlite3_column_text(a0, a1);
|
|
return cstr ? newStr(cstr, wasm.exports.sqlite3_column_bytes(a0, a1)) : null;
|
|
};
|
|
capi.sqlite3_value_text = function(val) {
|
|
const a0 = argValue(val);
|
|
const cstr = wasm.exports.sqlite3_value_text(a0);
|
|
return cstr ? newStr(cstr, wasm.exports.sqlite3_value_bytes(a0)) : null;
|
|
};
|
|
}
|
|
capi.sqlite3_config = function(op, ...args) {
|
|
if (arguments.length < 2) return capi.SQLITE_MISUSE;
|
|
switch (op) {
|
|
case capi.SQLITE_CONFIG_COVERING_INDEX_SCAN:
|
|
case capi.SQLITE_CONFIG_MEMSTATUS:
|
|
case capi.SQLITE_CONFIG_SMALL_MALLOC:
|
|
case capi.SQLITE_CONFIG_SORTERREF_SIZE:
|
|
case capi.SQLITE_CONFIG_STMTJRNL_SPILL:
|
|
case capi.SQLITE_CONFIG_URI: return wasm.exports.sqlite3__wasm_config_i(op, args[0]);
|
|
case capi.SQLITE_CONFIG_LOOKASIDE: return wasm.exports.sqlite3__wasm_config_ii(op, args[0], args[1]);
|
|
case capi.SQLITE_CONFIG_MEMDB_MAXSIZE: return wasm.exports.sqlite3__wasm_config_j(op, args[0]);
|
|
case capi.SQLITE_CONFIG_GETMALLOC:
|
|
case capi.SQLITE_CONFIG_GETMUTEX:
|
|
case capi.SQLITE_CONFIG_GETPCACHE2:
|
|
case capi.SQLITE_CONFIG_GETPCACHE:
|
|
case capi.SQLITE_CONFIG_HEAP:
|
|
case capi.SQLITE_CONFIG_LOG:
|
|
case capi.SQLITE_CONFIG_MALLOC:
|
|
case capi.SQLITE_CONFIG_MMAP_SIZE:
|
|
case capi.SQLITE_CONFIG_MULTITHREAD:
|
|
case capi.SQLITE_CONFIG_MUTEX:
|
|
case capi.SQLITE_CONFIG_PAGECACHE:
|
|
case capi.SQLITE_CONFIG_PCACHE2:
|
|
case capi.SQLITE_CONFIG_PCACHE:
|
|
case capi.SQLITE_CONFIG_PCACHE_HDRSZ:
|
|
case capi.SQLITE_CONFIG_PMASZ:
|
|
case capi.SQLITE_CONFIG_SERIALIZED:
|
|
case capi.SQLITE_CONFIG_SINGLETHREAD:
|
|
case capi.SQLITE_CONFIG_SQLLOG:
|
|
case capi.SQLITE_CONFIG_WIN32_HEAPSIZE:
|
|
default: return capi.SQLITE_NOTFOUND;
|
|
}
|
|
};
|
|
{
|
|
const __autoExtFptr = /* @__PURE__ */ new Set();
|
|
capi.sqlite3_auto_extension = function(fPtr) {
|
|
if (fPtr instanceof Function) fPtr = wasm.installFunction("i(ppp)", fPtr);
|
|
else if (1 !== arguments.length || !wasm.isPtr(fPtr)) return capi.SQLITE_MISUSE;
|
|
const rc = wasm.exports.sqlite3_auto_extension(fPtr);
|
|
if (fPtr !== arguments[0]) if (0 === rc) __autoExtFptr.add(fPtr);
|
|
else wasm.uninstallFunction(fPtr);
|
|
return rc;
|
|
};
|
|
capi.sqlite3_cancel_auto_extension = function(fPtr) {
|
|
if (!fPtr || 1 !== arguments.length || !wasm.isPtr(fPtr)) return 0;
|
|
return wasm.exports.sqlite3_cancel_auto_extension(fPtr);
|
|
};
|
|
capi.sqlite3_reset_auto_extension = function() {
|
|
wasm.exports.sqlite3_reset_auto_extension();
|
|
for (const fp of __autoExtFptr) wasm.uninstallFunction(fp);
|
|
__autoExtFptr.clear();
|
|
};
|
|
}
|
|
wasm.xWrap.FuncPtrAdapter.warnOnUse = true;
|
|
const StructBinder = sqlite3.StructBinder;
|
|
const installMethod = function callee(tgt, name, func, applyArgcCheck = callee.installMethodArgcCheck) {
|
|
if (!(tgt instanceof StructBinder.StructType)) toss("Usage error: target object is-not-a StructType.");
|
|
else if (!(func instanceof Function) && !wasm.isPtr(func)) toss("Usage error: expecting a Function or WASM pointer to one.");
|
|
if (1 === arguments.length) return (n, f) => callee(tgt, n, f, applyArgcCheck);
|
|
if (!callee.argcProxy) {
|
|
callee.argcProxy = function(tgt, funcName, func, sig) {
|
|
return function(...args) {
|
|
if (func.length !== arguments.length) toss("Argument mismatch for", tgt.structInfo.name + "::" + funcName + ": Native signature is:", sig);
|
|
return func.apply(this, args);
|
|
};
|
|
};
|
|
callee.removeFuncList = function() {
|
|
if (this.ondispose.__removeFuncList) {
|
|
this.ondispose.__removeFuncList.forEach((v, ndx) => {
|
|
if (wasm.isPtr(v)) try {
|
|
wasm.uninstallFunction(v);
|
|
} catch (e) {}
|
|
});
|
|
delete this.ondispose.__removeFuncList;
|
|
}
|
|
};
|
|
}
|
|
const sigN = tgt.memberSignature(name);
|
|
if (sigN.length < 2) toss("Member", name, "does not have a function pointer signature:", sigN);
|
|
const memKey = tgt.memberKey(name);
|
|
const fProxy = applyArgcCheck && !wasm.isPtr(func) ? callee.argcProxy(tgt, memKey, func, sigN) : func;
|
|
if (wasm.isPtr(fProxy)) {
|
|
if (fProxy && !wasm.functionEntry(fProxy)) toss("Pointer", fProxy, "is not a WASM function table entry.");
|
|
tgt[memKey] = fProxy;
|
|
} else {
|
|
const pFunc = wasm.installFunction(fProxy, sigN);
|
|
tgt[memKey] = pFunc;
|
|
if (!tgt.ondispose || !tgt.ondispose.__removeFuncList) {
|
|
tgt.addOnDispose("ondispose.__removeFuncList handler", callee.removeFuncList);
|
|
tgt.ondispose.__removeFuncList = [];
|
|
}
|
|
tgt.ondispose.__removeFuncList.push(memKey, pFunc);
|
|
}
|
|
return (n, f) => callee(tgt, n, f, applyArgcCheck);
|
|
};
|
|
installMethod.installMethodArgcCheck = false;
|
|
const installMethods = function(structInstance, methods, applyArgcCheck = installMethod.installMethodArgcCheck) {
|
|
const seen = /* @__PURE__ */ new Map();
|
|
for (const k of Object.keys(methods)) {
|
|
const m = methods[k];
|
|
const prior = seen.get(m);
|
|
if (prior) {
|
|
const mkey = structInstance.memberKey(k);
|
|
structInstance[mkey] = structInstance[structInstance.memberKey(prior)];
|
|
} else {
|
|
installMethod(structInstance, k, m, applyArgcCheck);
|
|
seen.set(m, k);
|
|
}
|
|
}
|
|
return structInstance;
|
|
};
|
|
StructBinder.StructType.prototype.installMethod = function callee(name, func, applyArgcCheck = installMethod.installMethodArgcCheck) {
|
|
return arguments.length < 3 && name && "object" === typeof name ? installMethods(this, ...arguments) : installMethod(this, ...arguments);
|
|
};
|
|
StructBinder.StructType.prototype.installMethods = function(methods, applyArgcCheck = installMethod.installMethodArgcCheck) {
|
|
return installMethods(this, methods, applyArgcCheck);
|
|
};
|
|
});
|
|
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) {
|
|
const toss3 = (...args) => {
|
|
throw new sqlite3.SQLite3Error(...args);
|
|
};
|
|
const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util;
|
|
const outWrapper = function(f) {
|
|
return (...args) => f("sqlite3.oo1:", ...args);
|
|
};
|
|
sqlite3.__isUnderTest ? outWrapper(console.debug.bind(console)) : outWrapper(sqlite3.config.debug);
|
|
sqlite3.__isUnderTest ? outWrapper(console.warn.bind(console)) : outWrapper(sqlite3.config.warn);
|
|
sqlite3.__isUnderTest ? outWrapper(console.error.bind(console)) : outWrapper(sqlite3.config.error);
|
|
const __ptrMap = /* @__PURE__ */ new WeakMap();
|
|
const __doesNotOwnHandle = /* @__PURE__ */ new Set();
|
|
const __stmtMap = /* @__PURE__ */ new WeakMap();
|
|
const getOwnOption = (opts, p, dflt) => {
|
|
const d = Object.getOwnPropertyDescriptor(opts, p);
|
|
return d ? d.value : dflt;
|
|
};
|
|
const checkSqlite3Rc = function(dbPtr, sqliteResultCode) {
|
|
if (sqliteResultCode) {
|
|
if (dbPtr instanceof DB) dbPtr = dbPtr.pointer;
|
|
toss3(sqliteResultCode, "sqlite3 result code", sqliteResultCode + ":", dbPtr ? capi.sqlite3_errmsg(dbPtr) : capi.sqlite3_errstr(sqliteResultCode));
|
|
}
|
|
return arguments[0];
|
|
};
|
|
const __dbTraceToConsole = wasm.installFunction("i(ippp)", function(t, c, p, x) {
|
|
if (capi.SQLITE_TRACE_STMT === t) console.log("SQL TRACE #" + ++this.counter, "via sqlite3@" + c + "[" + capi.sqlite3_db_filename(c, null) + "]", wasm.cstrToJs(x));
|
|
}.bind({ counter: 0 }));
|
|
const __vfsPostOpenCallback = Object.create(null);
|
|
const dbCtorHelper = function ctor(...args) {
|
|
const opt = ctor.normalizeArgs(...args);
|
|
let pDb;
|
|
if (pDb = opt["sqlite3*"]) {
|
|
if (!opt["sqlite3*:takeOwnership"]) __doesNotOwnHandle.add(this);
|
|
this.filename = capi.sqlite3_db_filename(pDb, "main");
|
|
} else {
|
|
let fn = opt.filename, vfsName = opt.vfs, flagsStr = opt.flags;
|
|
if ("string" !== typeof fn && !wasm.isPtr(fn) || "string" !== typeof flagsStr || vfsName && "string" !== typeof vfsName && !wasm.isPtr(vfsName)) {
|
|
sqlite3.config.error("Invalid DB ctor args", opt, arguments);
|
|
toss3("Invalid arguments for DB constructor:", arguments, "opts:", opt);
|
|
}
|
|
let oflags = 0;
|
|
if (flagsStr.indexOf("c") >= 0) oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE;
|
|
if (flagsStr.indexOf("w") >= 0) oflags |= capi.SQLITE_OPEN_READWRITE;
|
|
if (0 === oflags) oflags |= capi.SQLITE_OPEN_READONLY;
|
|
oflags |= capi.SQLITE_OPEN_EXRESCODE;
|
|
const stack = wasm.pstack.pointer;
|
|
try {
|
|
const pPtr = wasm.pstack.allocPtr();
|
|
let rc = capi.sqlite3_open_v2(fn, pPtr, oflags, vfsName || wasm.ptr.null);
|
|
pDb = wasm.peekPtr(pPtr);
|
|
checkSqlite3Rc(pDb, rc);
|
|
capi.sqlite3_extended_result_codes(pDb, 1);
|
|
if (flagsStr.indexOf("t") >= 0) capi.sqlite3_trace_v2(pDb, capi.SQLITE_TRACE_STMT, __dbTraceToConsole, pDb);
|
|
} catch (e) {
|
|
if (pDb) capi.sqlite3_close_v2(pDb);
|
|
throw e;
|
|
} finally {
|
|
wasm.pstack.restore(stack);
|
|
}
|
|
this.filename = wasm.isPtr(fn) ? wasm.cstrToJs(fn) : fn;
|
|
}
|
|
__ptrMap.set(this, pDb);
|
|
__stmtMap.set(this, Object.create(null));
|
|
if (!opt["sqlite3*"]) try {
|
|
const postInitSql = __vfsPostOpenCallback[capi.sqlite3_js_db_vfs(pDb) || toss3("Internal error: cannot get VFS for new db handle.")];
|
|
if (postInitSql) if (postInitSql instanceof Function) postInitSql(this, sqlite3);
|
|
else checkSqlite3Rc(pDb, capi.sqlite3_exec(pDb, postInitSql, 0, 0, 0));
|
|
} catch (e) {
|
|
this.close();
|
|
throw e;
|
|
}
|
|
};
|
|
dbCtorHelper.setVfsPostOpenCallback = function(pVfs, callback) {
|
|
if (!(callback instanceof Function)) toss3("dbCtorHelper.setVfsPostOpenCallback() should not be used with a non-function argument.", arguments);
|
|
__vfsPostOpenCallback[pVfs] = callback;
|
|
};
|
|
dbCtorHelper.normalizeArgs = function(filename = ":memory:", flags = "c", vfs = null) {
|
|
const arg = {};
|
|
if (1 === arguments.length && arguments[0] && "object" === typeof arguments[0]) {
|
|
Object.assign(arg, arguments[0]);
|
|
if (void 0 === arg.flags) arg.flags = "c";
|
|
if (void 0 === arg.vfs) arg.vfs = null;
|
|
if (void 0 === arg.filename) arg.filename = ":memory:";
|
|
} else {
|
|
arg.filename = filename;
|
|
arg.flags = flags;
|
|
arg.vfs = vfs;
|
|
}
|
|
return arg;
|
|
};
|
|
const DB = function(...args) {
|
|
dbCtorHelper.apply(this, args);
|
|
};
|
|
DB.dbCtorHelper = dbCtorHelper;
|
|
const BindTypes = {
|
|
null: 1,
|
|
number: 2,
|
|
string: 3,
|
|
boolean: 4,
|
|
blob: 5
|
|
};
|
|
if (wasm.bigIntEnabled) BindTypes.bigint = BindTypes.number;
|
|
const Stmt = function() {
|
|
if (BindTypes !== arguments[2]) toss3(capi.SQLITE_MISUSE, "Do not call the Stmt constructor directly. Use DB.prepare().");
|
|
this.db = arguments[0];
|
|
__ptrMap.set(this, arguments[1]);
|
|
if (arguments.length > 3 && !arguments[3]) __doesNotOwnHandle.add(this);
|
|
};
|
|
const affirmDbOpen = function(db) {
|
|
if (!db.pointer) toss3("DB has been closed.");
|
|
return db;
|
|
};
|
|
const affirmColIndex = function(stmt, ndx) {
|
|
if (ndx !== (ndx | 0) || ndx < 0 || ndx >= stmt.columnCount) toss3("Column index", ndx, "is out of range.");
|
|
return stmt;
|
|
};
|
|
const parseExecArgs = function(db, args) {
|
|
const out = Object.create(null);
|
|
out.opt = Object.create(null);
|
|
switch (args.length) {
|
|
case 1:
|
|
if ("string" === typeof args[0] || util.isSQLableTypedArray(args[0])) out.sql = args[0];
|
|
else if (Array.isArray(args[0])) out.sql = args[0];
|
|
else if (args[0] && "object" === typeof args[0]) {
|
|
out.opt = args[0];
|
|
out.sql = out.opt.sql;
|
|
}
|
|
break;
|
|
case 2:
|
|
out.sql = args[0];
|
|
out.opt = args[1];
|
|
break;
|
|
default: toss3("Invalid argument count for exec().");
|
|
}
|
|
out.sql = util.flexibleString(out.sql);
|
|
if ("string" !== typeof out.sql) toss3("Missing SQL argument or unsupported SQL value type.");
|
|
const opt = out.opt;
|
|
switch (opt.returnValue) {
|
|
case "resultRows":
|
|
if (!opt.resultRows) opt.resultRows = [];
|
|
out.returnVal = () => opt.resultRows;
|
|
break;
|
|
case "saveSql":
|
|
if (!opt.saveSql) opt.saveSql = [];
|
|
out.returnVal = () => opt.saveSql;
|
|
break;
|
|
case void 0:
|
|
case "this":
|
|
out.returnVal = () => db;
|
|
break;
|
|
default: toss3("Invalid returnValue value:", opt.returnValue);
|
|
}
|
|
if (!opt.callback && !opt.returnValue && void 0 !== opt.rowMode) {
|
|
if (!opt.resultRows) opt.resultRows = [];
|
|
out.returnVal = () => opt.resultRows;
|
|
}
|
|
if (opt.callback || opt.resultRows) switch (void 0 === opt.rowMode ? "array" : opt.rowMode) {
|
|
case "object":
|
|
out.cbArg = (stmt, cache) => {
|
|
if (!cache.columnNames) cache.columnNames = stmt.getColumnNames([]);
|
|
const row = stmt.get([]);
|
|
const rv = Object.create(null);
|
|
for (const i in cache.columnNames) rv[cache.columnNames[i]] = row[i];
|
|
return rv;
|
|
};
|
|
break;
|
|
case "array":
|
|
out.cbArg = (stmt) => stmt.get([]);
|
|
break;
|
|
case "stmt":
|
|
if (Array.isArray(opt.resultRows)) toss3("exec(): invalid rowMode for a resultRows array: must", "be one of 'array', 'object',", "a result column number, or column name reference.");
|
|
out.cbArg = (stmt) => stmt;
|
|
break;
|
|
default:
|
|
if (util.isInt32(opt.rowMode)) {
|
|
out.cbArg = (stmt) => stmt.get(opt.rowMode);
|
|
break;
|
|
} else if ("string" === typeof opt.rowMode && opt.rowMode.length > 1 && "$" === opt.rowMode[0]) {
|
|
const $colName = opt.rowMode.substr(1);
|
|
out.cbArg = (stmt) => {
|
|
const rc = stmt.get(Object.create(null))[$colName];
|
|
return void 0 === rc ? toss3(capi.SQLITE_NOTFOUND, "exec(): unknown result column:", $colName) : rc;
|
|
};
|
|
break;
|
|
}
|
|
toss3("Invalid rowMode:", opt.rowMode);
|
|
}
|
|
return out;
|
|
};
|
|
const __selectFirstRow = (db, sql, bind, ...getArgs) => {
|
|
const stmt = db.prepare(sql);
|
|
try {
|
|
const rc = stmt.bind(bind).step() ? stmt.get(...getArgs) : void 0;
|
|
stmt.reset();
|
|
return rc;
|
|
} finally {
|
|
stmt.finalize();
|
|
}
|
|
};
|
|
const __selectAll = (db, sql, bind, rowMode) => db.exec({
|
|
sql,
|
|
bind,
|
|
rowMode,
|
|
returnValue: "resultRows"
|
|
});
|
|
DB.checkRc = (db, resultCode) => checkSqlite3Rc(db, resultCode);
|
|
DB.prototype = {
|
|
isOpen: function() {
|
|
return !!this.pointer;
|
|
},
|
|
affirmOpen: function() {
|
|
return affirmDbOpen(this);
|
|
},
|
|
close: function() {
|
|
const pDb = this.pointer;
|
|
if (pDb) {
|
|
if (this.onclose && this.onclose.before instanceof Function) try {
|
|
this.onclose.before(this);
|
|
} catch (e) {}
|
|
Object.keys(__stmtMap.get(this)).forEach((k, s) => {
|
|
if (s && s.pointer) try {
|
|
s.finalize();
|
|
} catch (e) {}
|
|
});
|
|
__ptrMap.delete(this);
|
|
__stmtMap.delete(this);
|
|
if (!__doesNotOwnHandle.delete(this)) capi.sqlite3_close_v2(pDb);
|
|
if (this.onclose && this.onclose.after instanceof Function) try {
|
|
this.onclose.after(this);
|
|
} catch (e) {}
|
|
delete this.filename;
|
|
}
|
|
},
|
|
changes: function(total = false, sixtyFour = false) {
|
|
const p = affirmDbOpen(this).pointer;
|
|
if (total) return sixtyFour ? capi.sqlite3_total_changes64(p) : capi.sqlite3_total_changes(p);
|
|
else return sixtyFour ? capi.sqlite3_changes64(p) : capi.sqlite3_changes(p);
|
|
},
|
|
dbFilename: function(dbName = "main") {
|
|
return capi.sqlite3_db_filename(affirmDbOpen(this).pointer, dbName);
|
|
},
|
|
dbName: function(dbNumber = 0) {
|
|
return capi.sqlite3_db_name(affirmDbOpen(this).pointer, dbNumber);
|
|
},
|
|
dbVfsName: function(dbName = 0) {
|
|
let rc;
|
|
const pVfs = capi.sqlite3_js_db_vfs(affirmDbOpen(this).pointer, dbName);
|
|
if (pVfs) {
|
|
const v = new capi.sqlite3_vfs(pVfs);
|
|
try {
|
|
rc = wasm.cstrToJs(v.$zName);
|
|
} finally {
|
|
v.dispose();
|
|
}
|
|
}
|
|
return rc;
|
|
},
|
|
prepare: function(sql) {
|
|
affirmDbOpen(this);
|
|
const stack = wasm.pstack.pointer;
|
|
let ppStmt, pStmt;
|
|
try {
|
|
ppStmt = wasm.pstack.alloc(8);
|
|
DB.checkRc(this, capi.sqlite3_prepare_v2(this.pointer, sql, -1, ppStmt, null));
|
|
pStmt = wasm.peekPtr(ppStmt);
|
|
} finally {
|
|
wasm.pstack.restore(stack);
|
|
}
|
|
if (!pStmt) toss3("Cannot prepare empty SQL.");
|
|
const stmt = new Stmt(this, pStmt, BindTypes);
|
|
__stmtMap.get(this)[pStmt] = stmt;
|
|
return stmt;
|
|
},
|
|
exec: function() {
|
|
affirmDbOpen(this);
|
|
const arg = parseExecArgs(this, arguments);
|
|
if (!arg.sql) return toss3("exec() requires an SQL string.");
|
|
const opt = arg.opt;
|
|
const callback = opt.callback;
|
|
const resultRows = Array.isArray(opt.resultRows) ? opt.resultRows : void 0;
|
|
let stmt;
|
|
let bind = opt.bind;
|
|
let evalFirstResult = !!(arg.cbArg || opt.columnNames || resultRows);
|
|
const stack = wasm.scopedAllocPush();
|
|
const saveSql = Array.isArray(opt.saveSql) ? opt.saveSql : void 0;
|
|
try {
|
|
const isTA = util.isSQLableTypedArray(arg.sql);
|
|
let sqlByteLen = isTA ? arg.sql.byteLength : wasm.jstrlen(arg.sql);
|
|
const ppStmt = wasm.scopedAlloc(2 * wasm.ptr.size + (sqlByteLen + 1));
|
|
const pzTail = wasm.ptr.add(ppStmt, wasm.ptr.size);
|
|
let pSql = wasm.ptr.add(pzTail, wasm.ptr.size);
|
|
const pSqlEnd = wasm.ptr.add(pSql, sqlByteLen);
|
|
if (isTA) wasm.heap8().set(arg.sql, pSql);
|
|
else wasm.jstrcpy(arg.sql, wasm.heap8(), pSql, sqlByteLen, false);
|
|
wasm.poke8(wasm.ptr.add(pSql, sqlByteLen), 0);
|
|
while (pSql && wasm.peek8(pSql)) {
|
|
wasm.pokePtr([ppStmt, pzTail], 0);
|
|
DB.checkRc(this, capi.sqlite3_prepare_v3(this.pointer, pSql, sqlByteLen, 0, ppStmt, pzTail));
|
|
const pStmt = wasm.peekPtr(ppStmt);
|
|
pSql = wasm.peekPtr(pzTail);
|
|
sqlByteLen = Number(wasm.ptr.add(pSqlEnd, -pSql));
|
|
if (!pStmt) continue;
|
|
if (saveSql) saveSql.push(capi.sqlite3_sql(pStmt).trim());
|
|
stmt = new Stmt(this, pStmt, BindTypes);
|
|
if (bind && stmt.parameterCount) {
|
|
stmt.bind(bind);
|
|
bind = null;
|
|
}
|
|
if (evalFirstResult && stmt.columnCount) {
|
|
let gotColNames = Array.isArray(opt.columnNames) ? 0 : 1;
|
|
evalFirstResult = false;
|
|
if (arg.cbArg || resultRows) {
|
|
const cbArgCache = Object.create(null);
|
|
for (; stmt.step(); __execLock.delete(stmt)) {
|
|
if (0 === gotColNames++) stmt.getColumnNames(cbArgCache.columnNames = opt.columnNames || []);
|
|
__execLock.add(stmt);
|
|
const row = arg.cbArg(stmt, cbArgCache);
|
|
if (resultRows) resultRows.push(row);
|
|
if (callback && false === callback.call(opt, row, stmt)) break;
|
|
}
|
|
__execLock.delete(stmt);
|
|
}
|
|
if (0 === gotColNames) stmt.getColumnNames(opt.columnNames);
|
|
} else stmt.step();
|
|
stmt.reset().finalize();
|
|
stmt = null;
|
|
}
|
|
} finally {
|
|
if (stmt) {
|
|
__execLock.delete(stmt);
|
|
stmt.finalize();
|
|
}
|
|
wasm.scopedAllocPop(stack);
|
|
}
|
|
return arg.returnVal();
|
|
},
|
|
createFunction: function f(name, xFunc, opt) {
|
|
const isFunc = (f) => f instanceof Function;
|
|
switch (arguments.length) {
|
|
case 1:
|
|
opt = name;
|
|
name = opt.name;
|
|
xFunc = opt.xFunc || 0;
|
|
break;
|
|
case 2:
|
|
if (!isFunc(xFunc)) {
|
|
opt = xFunc;
|
|
xFunc = opt.xFunc || 0;
|
|
}
|
|
break;
|
|
case 3: break;
|
|
default: break;
|
|
}
|
|
if (!opt) opt = {};
|
|
if ("string" !== typeof name) toss3("Invalid arguments: missing function name.");
|
|
let xStep = opt.xStep || 0;
|
|
let xFinal = opt.xFinal || 0;
|
|
const xValue = opt.xValue || 0;
|
|
const xInverse = opt.xInverse || 0;
|
|
let isWindow = void 0;
|
|
if (isFunc(xFunc)) {
|
|
isWindow = false;
|
|
if (isFunc(xStep) || isFunc(xFinal)) toss3("Ambiguous arguments: scalar or aggregate?");
|
|
xStep = xFinal = null;
|
|
} else if (isFunc(xStep)) {
|
|
if (!isFunc(xFinal)) toss3("Missing xFinal() callback for aggregate or window UDF.");
|
|
xFunc = null;
|
|
} else if (isFunc(xFinal)) toss3("Missing xStep() callback for aggregate or window UDF.");
|
|
else toss3("Missing function-type properties.");
|
|
if (false === isWindow) {
|
|
if (isFunc(xValue) || isFunc(xInverse)) toss3("xValue and xInverse are not permitted for non-window UDFs.");
|
|
} else if (isFunc(xValue)) {
|
|
if (!isFunc(xInverse)) toss3("xInverse must be provided if xValue is.");
|
|
isWindow = true;
|
|
} else if (isFunc(xInverse)) toss3("xValue must be provided if xInverse is.");
|
|
const pApp = opt.pApp;
|
|
if (void 0 !== pApp && null !== pApp && !wasm.isPtr(pApp)) toss3("Invalid value for pApp property. Must be a legal WASM pointer value.");
|
|
const xDestroy = opt.xDestroy || 0;
|
|
if (xDestroy && !isFunc(xDestroy)) toss3("xDestroy property must be a function.");
|
|
let fFlags = 0;
|
|
if (getOwnOption(opt, "deterministic")) fFlags |= capi.SQLITE_DETERMINISTIC;
|
|
if (getOwnOption(opt, "directOnly")) fFlags |= capi.SQLITE_DIRECTONLY;
|
|
if (getOwnOption(opt, "innocuous")) fFlags |= capi.SQLITE_INNOCUOUS;
|
|
name = name.toLowerCase();
|
|
const xArity = xFunc || xStep;
|
|
const arity = getOwnOption(opt, "arity");
|
|
const arityArg = "number" === typeof arity ? arity : xArity.length ? xArity.length - 1 : 0;
|
|
let rc;
|
|
if (isWindow) rc = capi.sqlite3_create_window_function(this.pointer, name, arityArg, capi.SQLITE_UTF8 | fFlags, pApp || 0, xStep, xFinal, xValue, xInverse, xDestroy);
|
|
else rc = capi.sqlite3_create_function_v2(this.pointer, name, arityArg, capi.SQLITE_UTF8 | fFlags, pApp || 0, xFunc, xStep, xFinal, xDestroy);
|
|
DB.checkRc(this, rc);
|
|
return this;
|
|
},
|
|
selectValue: function(sql, bind, asType) {
|
|
return __selectFirstRow(this, sql, bind, 0, asType);
|
|
},
|
|
selectValues: function(sql, bind, asType) {
|
|
const stmt = this.prepare(sql), rc = [];
|
|
try {
|
|
stmt.bind(bind);
|
|
while (stmt.step()) rc.push(stmt.get(0, asType));
|
|
stmt.reset();
|
|
} finally {
|
|
stmt.finalize();
|
|
}
|
|
return rc;
|
|
},
|
|
selectArray: function(sql, bind) {
|
|
return __selectFirstRow(this, sql, bind, []);
|
|
},
|
|
selectObject: function(sql, bind) {
|
|
return __selectFirstRow(this, sql, bind, {});
|
|
},
|
|
selectArrays: function(sql, bind) {
|
|
return __selectAll(this, sql, bind, "array");
|
|
},
|
|
selectObjects: function(sql, bind) {
|
|
return __selectAll(this, sql, bind, "object");
|
|
},
|
|
openStatementCount: function() {
|
|
return this.pointer ? Object.keys(__stmtMap.get(this)).length : 0;
|
|
},
|
|
transaction: function(callback) {
|
|
let opener = "BEGIN";
|
|
if (arguments.length > 1) {
|
|
if (/[^a-zA-Z]/.test(arguments[0])) toss3(capi.SQLITE_MISUSE, "Invalid argument for BEGIN qualifier.");
|
|
opener += " " + arguments[0];
|
|
callback = arguments[1];
|
|
}
|
|
affirmDbOpen(this).exec(opener);
|
|
try {
|
|
const rc = callback(this);
|
|
this.exec("COMMIT");
|
|
return rc;
|
|
} catch (e) {
|
|
this.exec("ROLLBACK");
|
|
throw e;
|
|
}
|
|
},
|
|
savepoint: function(callback) {
|
|
affirmDbOpen(this).exec("SAVEPOINT oo1");
|
|
try {
|
|
const rc = callback(this);
|
|
this.exec("RELEASE oo1");
|
|
return rc;
|
|
} catch (e) {
|
|
this.exec("ROLLBACK to SAVEPOINT oo1; RELEASE SAVEPOINT oo1");
|
|
throw e;
|
|
}
|
|
},
|
|
checkRc: function(resultCode) {
|
|
return checkSqlite3Rc(this, resultCode);
|
|
}
|
|
};
|
|
DB.wrapHandle = function(pDb, takeOwnership = false) {
|
|
if (!pDb || !wasm.isPtr(pDb)) throw new sqlite3.SQLite3Error(capi.SQLITE_MISUSE, "Argument must be a WASM sqlite3 pointer");
|
|
return new DB({
|
|
"sqlite3*": pDb,
|
|
"sqlite3*:takeOwnership": !!takeOwnership
|
|
});
|
|
};
|
|
const affirmStmtOpen = function(stmt) {
|
|
if (!stmt.pointer) toss3("Stmt has been closed.");
|
|
return stmt;
|
|
};
|
|
const isSupportedBindType = function(v) {
|
|
let t = BindTypes[null === v || void 0 === v ? "null" : typeof v];
|
|
switch (t) {
|
|
case BindTypes.boolean:
|
|
case BindTypes.null:
|
|
case BindTypes.number:
|
|
case BindTypes.string: return t;
|
|
case BindTypes.bigint: return wasm.bigIntEnabled ? t : void 0;
|
|
default: return util.isBindableTypedArray(v) ? BindTypes.blob : void 0;
|
|
}
|
|
};
|
|
const affirmSupportedBindType = function(v) {
|
|
return isSupportedBindType(v) || toss3("Unsupported bind() argument type:", typeof v);
|
|
};
|
|
const affirmParamIndex = function(stmt, key) {
|
|
const n = "number" === typeof key ? key : capi.sqlite3_bind_parameter_index(stmt.pointer, key);
|
|
if (0 === n || !util.isInt32(n)) toss3("Invalid bind() parameter name: " + key);
|
|
else if (n < 1 || n > stmt.parameterCount) toss3("Bind index", key, "is out of range.");
|
|
return n;
|
|
};
|
|
const __execLock = /* @__PURE__ */ new Set();
|
|
const __stmtMayGet = /* @__PURE__ */ new Set();
|
|
const affirmNotLockedByExec = function(stmt, currentOpName) {
|
|
if (__execLock.has(stmt)) toss3("Operation is illegal when statement is locked:", currentOpName);
|
|
return stmt;
|
|
};
|
|
const bindOne = function f(stmt, ndx, bindType, val) {
|
|
affirmNotLockedByExec(affirmStmtOpen(stmt), "bind()");
|
|
if (!f._) {
|
|
f._tooBigInt = (v) => toss3("BigInt value is too big to store without precision loss:", v);
|
|
f._ = { string: function(stmt, ndx, val, asBlob) {
|
|
const [pStr, n] = wasm.allocCString(val, true);
|
|
return (asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text)(stmt.pointer, ndx, pStr, n, capi.SQLITE_WASM_DEALLOC);
|
|
} };
|
|
}
|
|
affirmSupportedBindType(val);
|
|
ndx = affirmParamIndex(stmt, ndx);
|
|
let rc = 0;
|
|
switch (null === val || void 0 === val ? BindTypes.null : bindType) {
|
|
case BindTypes.null:
|
|
rc = capi.sqlite3_bind_null(stmt.pointer, ndx);
|
|
break;
|
|
case BindTypes.string:
|
|
rc = f._.string(stmt, ndx, val, false);
|
|
break;
|
|
case BindTypes.number: {
|
|
let m;
|
|
if (util.isInt32(val)) m = capi.sqlite3_bind_int;
|
|
else if ("bigint" === typeof val) if (!util.bigIntFits64(val)) f._tooBigInt(val);
|
|
else if (wasm.bigIntEnabled) m = capi.sqlite3_bind_int64;
|
|
else if (util.bigIntFitsDouble(val)) {
|
|
val = Number(val);
|
|
m = capi.sqlite3_bind_double;
|
|
} else f._tooBigInt(val);
|
|
else {
|
|
val = Number(val);
|
|
if (wasm.bigIntEnabled && Number.isInteger(val)) m = capi.sqlite3_bind_int64;
|
|
else m = capi.sqlite3_bind_double;
|
|
}
|
|
rc = m(stmt.pointer, ndx, val);
|
|
break;
|
|
}
|
|
case BindTypes.boolean:
|
|
rc = capi.sqlite3_bind_int(stmt.pointer, ndx, val ? 1 : 0);
|
|
break;
|
|
case BindTypes.blob: {
|
|
if ("string" === typeof val) {
|
|
rc = f._.string(stmt, ndx, val, true);
|
|
break;
|
|
} else if (val instanceof ArrayBuffer) val = new Uint8Array(val);
|
|
else if (!util.isBindableTypedArray(val)) toss3("Binding a value as a blob requires", "that it be a string, Uint8Array, Int8Array, or ArrayBuffer.");
|
|
const pBlob = wasm.alloc(val.byteLength || 1);
|
|
wasm.heap8().set(val.byteLength ? val : [0], Number(pBlob));
|
|
rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, capi.SQLITE_WASM_DEALLOC);
|
|
break;
|
|
}
|
|
default:
|
|
sqlite3.config.warn("Unsupported bind() argument type:", val);
|
|
toss3("Unsupported bind() argument type: " + typeof val);
|
|
}
|
|
if (rc) DB.checkRc(stmt.db.pointer, rc);
|
|
return stmt;
|
|
};
|
|
Stmt.prototype = {
|
|
finalize: function() {
|
|
const ptr = this.pointer;
|
|
if (ptr) {
|
|
affirmNotLockedByExec(this, "finalize()");
|
|
const rc = __doesNotOwnHandle.delete(this) ? 0 : capi.sqlite3_finalize(ptr);
|
|
delete __stmtMap.get(this.db)[ptr];
|
|
__ptrMap.delete(this);
|
|
__execLock.delete(this);
|
|
__stmtMayGet.delete(this);
|
|
delete this.parameterCount;
|
|
delete this.db;
|
|
return rc;
|
|
}
|
|
},
|
|
clearBindings: function() {
|
|
affirmNotLockedByExec(affirmStmtOpen(this), "clearBindings()");
|
|
capi.sqlite3_clear_bindings(this.pointer);
|
|
__stmtMayGet.delete(this);
|
|
return this;
|
|
},
|
|
reset: function(alsoClearBinds) {
|
|
affirmNotLockedByExec(this, "reset()");
|
|
if (alsoClearBinds) this.clearBindings();
|
|
const rc = capi.sqlite3_reset(affirmStmtOpen(this).pointer);
|
|
__stmtMayGet.delete(this);
|
|
checkSqlite3Rc(this.db, rc);
|
|
return this;
|
|
},
|
|
bind: function() {
|
|
affirmStmtOpen(this);
|
|
let ndx, arg;
|
|
switch (arguments.length) {
|
|
case 1:
|
|
ndx = 1;
|
|
arg = arguments[0];
|
|
break;
|
|
case 2:
|
|
ndx = arguments[0];
|
|
arg = arguments[1];
|
|
break;
|
|
default: toss3("Invalid bind() arguments.");
|
|
}
|
|
if (void 0 === arg) return this;
|
|
else if (!this.parameterCount) toss3("This statement has no bindable parameters.");
|
|
__stmtMayGet.delete(this);
|
|
if (null === arg) return bindOne(this, ndx, BindTypes.null, arg);
|
|
else if (Array.isArray(arg)) {
|
|
if (1 !== arguments.length) toss3("When binding an array, an index argument is not permitted.");
|
|
arg.forEach((v, i) => bindOne(this, i + 1, affirmSupportedBindType(v), v));
|
|
return this;
|
|
} else if (arg instanceof ArrayBuffer) arg = new Uint8Array(arg);
|
|
if ("object" === typeof arg && !util.isBindableTypedArray(arg)) {
|
|
if (1 !== arguments.length) toss3("When binding an object, an index argument is not permitted.");
|
|
Object.keys(arg).forEach((k) => bindOne(this, k, affirmSupportedBindType(arg[k]), arg[k]));
|
|
return this;
|
|
} else return bindOne(this, ndx, affirmSupportedBindType(arg), arg);
|
|
toss3("Should not reach this point.");
|
|
},
|
|
bindAsBlob: function(ndx, arg) {
|
|
affirmStmtOpen(this);
|
|
if (1 === arguments.length) {
|
|
arg = ndx;
|
|
ndx = 1;
|
|
}
|
|
const t = affirmSupportedBindType(arg);
|
|
if (BindTypes.string !== t && BindTypes.blob !== t && BindTypes.null !== t) toss3("Invalid value type for bindAsBlob()");
|
|
return bindOne(this, ndx, BindTypes.blob, arg);
|
|
},
|
|
step: function() {
|
|
affirmNotLockedByExec(this, "step()");
|
|
const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer);
|
|
switch (rc) {
|
|
case capi.SQLITE_DONE:
|
|
__stmtMayGet.delete(this);
|
|
return false;
|
|
case capi.SQLITE_ROW:
|
|
__stmtMayGet.add(this);
|
|
return true;
|
|
default:
|
|
__stmtMayGet.delete(this);
|
|
sqlite3.config.warn("sqlite3_step() rc=", rc, capi.sqlite3_js_rc_str(rc), "SQL =", capi.sqlite3_sql(this.pointer));
|
|
DB.checkRc(this.db.pointer, rc);
|
|
}
|
|
},
|
|
stepReset: function() {
|
|
this.step();
|
|
return this.reset();
|
|
},
|
|
stepFinalize: function() {
|
|
try {
|
|
const rc = this.step();
|
|
this.reset();
|
|
return rc;
|
|
} finally {
|
|
try {
|
|
this.finalize();
|
|
} catch (e) {}
|
|
}
|
|
},
|
|
get: function(ndx, asType) {
|
|
if (!__stmtMayGet.has(affirmStmtOpen(this))) toss3("Stmt.step() has not (recently) returned true.");
|
|
if (Array.isArray(ndx)) {
|
|
let i = 0;
|
|
const n = this.columnCount;
|
|
while (i < n) ndx[i] = this.get(i++);
|
|
return ndx;
|
|
} else if (ndx && "object" === typeof ndx) {
|
|
let i = 0;
|
|
const n = this.columnCount;
|
|
while (i < n) ndx[capi.sqlite3_column_name(this.pointer, i)] = this.get(i++);
|
|
return ndx;
|
|
}
|
|
affirmColIndex(this, ndx);
|
|
switch (void 0 === asType ? capi.sqlite3_column_type(this.pointer, ndx) : asType) {
|
|
case capi.SQLITE_NULL: return null;
|
|
case capi.SQLITE_INTEGER: if (wasm.bigIntEnabled) {
|
|
const rc = capi.sqlite3_column_int64(this.pointer, ndx);
|
|
if (rc >= Number.MIN_SAFE_INTEGER && rc <= Number.MAX_SAFE_INTEGER) return Number(rc).valueOf();
|
|
return rc;
|
|
} else {
|
|
const rc = capi.sqlite3_column_double(this.pointer, ndx);
|
|
if (rc > Number.MAX_SAFE_INTEGER || rc < Number.MIN_SAFE_INTEGER) toss3("Integer is out of range for JS integer range: " + rc);
|
|
return util.isInt32(rc) ? rc | 0 : rc;
|
|
}
|
|
case capi.SQLITE_FLOAT: return capi.sqlite3_column_double(this.pointer, ndx);
|
|
case capi.SQLITE_TEXT: return capi.sqlite3_column_text(this.pointer, ndx);
|
|
case capi.SQLITE_BLOB: {
|
|
const n = capi.sqlite3_column_bytes(this.pointer, ndx), ptr = capi.sqlite3_column_blob(this.pointer, ndx), rc = new Uint8Array(n);
|
|
if (n) {
|
|
rc.set(wasm.heap8u().slice(Number(ptr), Number(ptr) + n), 0);
|
|
if (this.db._blobXfer instanceof Array) this.db._blobXfer.push(rc.buffer);
|
|
}
|
|
return rc;
|
|
}
|
|
default: toss3("Don't know how to translate", "type of result column #" + ndx + ".");
|
|
}
|
|
toss3("Not reached.");
|
|
},
|
|
getInt: function(ndx) {
|
|
return this.get(ndx, capi.SQLITE_INTEGER);
|
|
},
|
|
getFloat: function(ndx) {
|
|
return this.get(ndx, capi.SQLITE_FLOAT);
|
|
},
|
|
getString: function(ndx) {
|
|
return this.get(ndx, capi.SQLITE_TEXT);
|
|
},
|
|
getBlob: function(ndx) {
|
|
return this.get(ndx, capi.SQLITE_BLOB);
|
|
},
|
|
getJSON: function(ndx) {
|
|
const s = this.get(ndx, capi.SQLITE_STRING);
|
|
return null === s ? s : JSON.parse(s);
|
|
},
|
|
getColumnName: function(ndx) {
|
|
return capi.sqlite3_column_name(affirmColIndex(affirmStmtOpen(this), ndx).pointer, ndx);
|
|
},
|
|
getColumnNames: function(tgt = []) {
|
|
affirmColIndex(affirmStmtOpen(this), 0);
|
|
const n = this.columnCount;
|
|
for (let i = 0; i < n; ++i) tgt.push(capi.sqlite3_column_name(this.pointer, i));
|
|
return tgt;
|
|
},
|
|
getParamIndex: function(name) {
|
|
return affirmStmtOpen(this).parameterCount ? capi.sqlite3_bind_parameter_index(this.pointer, name) : void 0;
|
|
},
|
|
getParamName: function(ndx) {
|
|
return affirmStmtOpen(this).parameterCount ? capi.sqlite3_bind_parameter_name(this.pointer, ndx) : void 0;
|
|
},
|
|
isBusy: function() {
|
|
return 0 !== capi.sqlite3_stmt_busy(affirmStmtOpen(this));
|
|
},
|
|
isReadOnly: function() {
|
|
return 0 !== capi.sqlite3_stmt_readonly(affirmStmtOpen(this));
|
|
}
|
|
};
|
|
{
|
|
const prop = {
|
|
enumerable: true,
|
|
get: function() {
|
|
return __ptrMap.get(this);
|
|
},
|
|
set: () => toss3("The pointer property is read-only.")
|
|
};
|
|
Object.defineProperty(Stmt.prototype, "pointer", prop);
|
|
Object.defineProperty(DB.prototype, "pointer", prop);
|
|
}
|
|
Object.defineProperty(Stmt.prototype, "columnCount", {
|
|
enumerable: false,
|
|
get: function() {
|
|
return capi.sqlite3_column_count(this.pointer);
|
|
},
|
|
set: () => toss3("The columnCount property is read-only.")
|
|
});
|
|
Object.defineProperty(Stmt.prototype, "parameterCount", {
|
|
enumerable: false,
|
|
get: function() {
|
|
return capi.sqlite3_bind_parameter_count(this.pointer);
|
|
},
|
|
set: () => toss3("The parameterCount property is read-only.")
|
|
});
|
|
Stmt.wrapHandle = function(oo1db, pStmt, takeOwnership = false) {
|
|
if (!(oo1db instanceof DB) || !oo1db.pointer) throw new sqlite3.SQLite3Error(sqlite3.SQLITE_MISUSE, "First argument must be an opened sqlite3.oo1.DB instance");
|
|
if (!pStmt || !wasm.isPtr(pStmt)) throw new sqlite3.SQLite3Error(sqlite3.SQLITE_MISUSE, "Second argument must be a WASM sqlite3_stmt pointer");
|
|
return new Stmt(oo1db, pStmt, BindTypes, !!takeOwnership);
|
|
};
|
|
sqlite3.oo1 = {
|
|
DB,
|
|
Stmt
|
|
};
|
|
});
|
|
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) {
|
|
const util = sqlite3.util;
|
|
sqlite3.initWorker1API = function() {
|
|
"use strict";
|
|
const toss = (...args) => {
|
|
throw new Error(args.join(" "));
|
|
};
|
|
if (!(globalThis.WorkerGlobalScope instanceof Function)) toss("initWorker1API() must be run from a Worker thread.");
|
|
const sqlite3 = this.sqlite3 || toss("Missing this.sqlite3 object.");
|
|
const DB = sqlite3.oo1.DB;
|
|
const getDbId = function(db) {
|
|
let id = wState.idMap.get(db);
|
|
if (id) return id;
|
|
id = "db#" + ++wState.idSeq + ":" + Math.floor(Math.random() * 1e8) + ":" + Math.floor(Math.random() * 1e8);
|
|
wState.idMap.set(db, id);
|
|
return id;
|
|
};
|
|
const wState = {
|
|
dbList: [],
|
|
idSeq: 0,
|
|
idMap: /* @__PURE__ */ new WeakMap(),
|
|
xfer: [],
|
|
open: function(opt) {
|
|
const db = new DB(opt);
|
|
this.dbs[getDbId(db)] = db;
|
|
if (this.dbList.indexOf(db) < 0) this.dbList.push(db);
|
|
return db;
|
|
},
|
|
close: function(db, alsoUnlink) {
|
|
if (db) {
|
|
delete this.dbs[getDbId(db)];
|
|
const filename = db.filename;
|
|
const pVfs = util.sqlite3__wasm_db_vfs(db.pointer, 0);
|
|
db.close();
|
|
const ddNdx = this.dbList.indexOf(db);
|
|
if (ddNdx >= 0) this.dbList.splice(ddNdx, 1);
|
|
if (alsoUnlink && filename && pVfs) util.sqlite3__wasm_vfs_unlink(pVfs, filename);
|
|
}
|
|
},
|
|
post: function(msg, xferList) {
|
|
if (xferList && xferList.length) {
|
|
globalThis.postMessage(msg, Array.from(xferList));
|
|
xferList.length = 0;
|
|
} else globalThis.postMessage(msg);
|
|
},
|
|
dbs: Object.create(null),
|
|
getDb: function(id, require = true) {
|
|
return this.dbs[id] || (require ? toss("Unknown (or closed) DB ID:", id) : void 0);
|
|
}
|
|
};
|
|
const affirmDbOpen = function(db = wState.dbList[0]) {
|
|
return db && db.pointer ? db : toss("DB is not opened.");
|
|
};
|
|
const getMsgDb = function(msgData, affirmExists = true) {
|
|
const db = wState.getDb(msgData.dbId, false) || wState.dbList[0];
|
|
return affirmExists ? affirmDbOpen(db) : db;
|
|
};
|
|
const getDefaultDbId = function() {
|
|
return wState.dbList[0] && getDbId(wState.dbList[0]);
|
|
};
|
|
const wMsgHandler = {
|
|
open: function(ev) {
|
|
const oargs = Object.create(null), args = ev.args || Object.create(null);
|
|
if (args.simulateError) toss("Throwing because of simulateError flag.");
|
|
const rc = Object.create(null);
|
|
oargs.vfs = args.vfs;
|
|
oargs.filename = args.filename || "";
|
|
const db = wState.open(oargs);
|
|
rc.filename = db.filename;
|
|
rc.persistent = !!sqlite3.capi.sqlite3_js_db_uses_vfs(db.pointer, "opfs");
|
|
rc.dbId = getDbId(db);
|
|
rc.vfs = db.dbVfsName();
|
|
return rc;
|
|
},
|
|
close: function(ev) {
|
|
const db = getMsgDb(ev, false);
|
|
const response = { filename: db && db.filename };
|
|
if (db) {
|
|
const doUnlink = ev.args && "object" === typeof ev.args ? !!ev.args.unlink : false;
|
|
wState.close(db, doUnlink);
|
|
}
|
|
return response;
|
|
},
|
|
exec: function(ev) {
|
|
const rc = "string" === typeof ev.args ? { sql: ev.args } : ev.args || Object.create(null);
|
|
if ("stmt" === rc.rowMode) toss("Invalid rowMode for 'exec': stmt mode", "does not work in the Worker API.");
|
|
else if (!rc.sql) toss("'exec' requires input SQL.");
|
|
const db = getMsgDb(ev);
|
|
if (rc.callback || Array.isArray(rc.resultRows)) db._blobXfer = wState.xfer;
|
|
const theCallback = rc.callback;
|
|
let rowNumber = 0;
|
|
const hadColNames = !!rc.columnNames;
|
|
if ("string" === typeof theCallback) {
|
|
if (!hadColNames) rc.columnNames = [];
|
|
rc.callback = function(row, stmt) {
|
|
wState.post({
|
|
type: theCallback,
|
|
columnNames: rc.columnNames,
|
|
rowNumber: ++rowNumber,
|
|
row
|
|
}, wState.xfer);
|
|
};
|
|
}
|
|
try {
|
|
const changeCount = !!rc.countChanges ? db.changes(true, 64 === rc.countChanges) : void 0;
|
|
db.exec(rc);
|
|
if (void 0 !== changeCount) rc.changeCount = db.changes(true, 64 === rc.countChanges) - changeCount;
|
|
const lastInsertRowId = !!rc.lastInsertRowId ? sqlite3.capi.sqlite3_last_insert_rowid(db) : void 0;
|
|
if (void 0 !== lastInsertRowId) rc.lastInsertRowId = lastInsertRowId;
|
|
if (rc.callback instanceof Function) {
|
|
rc.callback = theCallback;
|
|
wState.post({
|
|
type: theCallback,
|
|
columnNames: rc.columnNames,
|
|
rowNumber: null,
|
|
row: void 0
|
|
});
|
|
}
|
|
} finally {
|
|
delete db._blobXfer;
|
|
if (rc.callback) rc.callback = theCallback;
|
|
}
|
|
return rc;
|
|
},
|
|
"config-get": function() {
|
|
const rc = Object.create(null), src = sqlite3.config;
|
|
["bigIntEnabled"].forEach(function(k) {
|
|
if (Object.getOwnPropertyDescriptor(src, k)) rc[k] = src[k];
|
|
});
|
|
rc.version = sqlite3.version;
|
|
rc.vfsList = sqlite3.capi.sqlite3_js_vfs_list();
|
|
return rc;
|
|
},
|
|
export: function(ev) {
|
|
const db = getMsgDb(ev);
|
|
const response = {
|
|
byteArray: sqlite3.capi.sqlite3_js_db_export(db.pointer),
|
|
filename: db.filename,
|
|
mimetype: "application/x-sqlite3"
|
|
};
|
|
wState.xfer.push(response.byteArray.buffer);
|
|
return response;
|
|
},
|
|
toss: function(ev) {
|
|
toss("Testing worker exception");
|
|
}
|
|
};
|
|
globalThis.onmessage = async function(ev) {
|
|
ev = ev.data;
|
|
let result, dbId = ev.dbId, evType = ev.type;
|
|
const arrivalTime = performance.now();
|
|
try {
|
|
if (wMsgHandler.hasOwnProperty(evType) && wMsgHandler[evType] instanceof Function) result = await wMsgHandler[evType](ev);
|
|
else toss("Unknown db worker message type:", ev.type);
|
|
} catch (err) {
|
|
evType = "error";
|
|
result = {
|
|
operation: ev.type,
|
|
message: err.message,
|
|
errorClass: err.name,
|
|
input: ev
|
|
};
|
|
if (err.stack) result.stack = "string" === typeof err.stack ? err.stack.split(/\n\s*/) : err.stack;
|
|
}
|
|
if (!dbId) dbId = result.dbId || getDefaultDbId();
|
|
wState.post({
|
|
type: evType,
|
|
dbId,
|
|
messageId: ev.messageId,
|
|
workerReceivedTime: arrivalTime,
|
|
workerRespondTime: performance.now(),
|
|
departureTime: ev.departureTime,
|
|
result
|
|
}, wState.xfer);
|
|
};
|
|
globalThis.postMessage({
|
|
type: "sqlite3-api",
|
|
result: "worker1-ready"
|
|
});
|
|
}.bind({ sqlite3 });
|
|
});
|
|
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) {
|
|
const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3;
|
|
const vfs = Object.create(null);
|
|
sqlite3.vfs = vfs;
|
|
capi.sqlite3_vfs.prototype.registerVfs = function(asDefault = false) {
|
|
if (!(this instanceof sqlite3.capi.sqlite3_vfs)) toss("Expecting a sqlite3_vfs-type argument.");
|
|
const rc = capi.sqlite3_vfs_register(this, asDefault ? 1 : 0);
|
|
if (rc) toss("sqlite3_vfs_register(", this, ") failed with rc", rc);
|
|
if (this.pointer !== capi.sqlite3_vfs_find(this.$zName)) toss("BUG: sqlite3_vfs_find(vfs.$zName) failed for just-installed VFS", this);
|
|
return this;
|
|
};
|
|
vfs.installVfs = function(opt) {
|
|
let count = 0;
|
|
const propList = ["io", "vfs"];
|
|
for (const key of propList) {
|
|
const o = opt[key];
|
|
if (o) {
|
|
++count;
|
|
o.struct.installMethods(o.methods, !!o.applyArgcCheck);
|
|
if ("vfs" === key) {
|
|
if (!o.struct.$zName && "string" === typeof o.name) o.struct.addOnDispose(o.struct.$zName = wasm.allocCString(o.name));
|
|
o.struct.registerVfs(!!o.asDefault);
|
|
}
|
|
}
|
|
}
|
|
if (!count) toss("Misuse: installVfs() options object requires at least", "one of:", propList);
|
|
return this;
|
|
};
|
|
});
|
|
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) {
|
|
if (!sqlite3.wasm.exports.sqlite3_declare_vtab) return;
|
|
const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3;
|
|
const vtab = Object.create(null);
|
|
sqlite3.vtab = vtab;
|
|
const sii = capi.sqlite3_index_info;
|
|
sii.prototype.nthConstraint = function(n, asPtr = false) {
|
|
if (n < 0 || n >= this.$nConstraint) return false;
|
|
const ptr = wasm.ptr.add(this.$aConstraint, sii.sqlite3_index_constraint.structInfo.sizeof * n);
|
|
return asPtr ? ptr : new sii.sqlite3_index_constraint(ptr);
|
|
};
|
|
sii.prototype.nthConstraintUsage = function(n, asPtr = false) {
|
|
if (n < 0 || n >= this.$nConstraint) return false;
|
|
const ptr = wasm.ptr.add(this.$aConstraintUsage, sii.sqlite3_index_constraint_usage.structInfo.sizeof * n);
|
|
return asPtr ? ptr : new sii.sqlite3_index_constraint_usage(ptr);
|
|
};
|
|
sii.prototype.nthOrderBy = function(n, asPtr = false) {
|
|
if (n < 0 || n >= this.$nOrderBy) return false;
|
|
const ptr = wasm.ptr.add(this.$aOrderBy, sii.sqlite3_index_orderby.structInfo.sizeof * n);
|
|
return asPtr ? ptr : new sii.sqlite3_index_orderby(ptr);
|
|
};
|
|
const __xWrapFactory = function(methodName, StructType) {
|
|
return function(ptr, removeMapping = false) {
|
|
if (0 === arguments.length) ptr = new StructType();
|
|
if (ptr instanceof StructType) {
|
|
this.set(ptr.pointer, ptr);
|
|
return ptr;
|
|
} else if (!wasm.isPtr(ptr)) sqlite3.SQLite3Error.toss("Invalid argument to", methodName + "()");
|
|
let rc = this.get(ptr);
|
|
if (removeMapping) this.delete(ptr);
|
|
return rc;
|
|
}.bind(/* @__PURE__ */ new Map());
|
|
};
|
|
const StructPtrMapper = function(name, StructType) {
|
|
const __xWrap = __xWrapFactory(name, StructType);
|
|
return Object.assign(Object.create(null), {
|
|
StructType,
|
|
create: (ppOut) => {
|
|
const rc = __xWrap();
|
|
wasm.pokePtr(ppOut, rc.pointer);
|
|
return rc;
|
|
},
|
|
get: (pCObj) => __xWrap(pCObj),
|
|
unget: (pCObj) => __xWrap(pCObj, true),
|
|
dispose: (pCObj) => __xWrap(pCObj, true)?.dispose?.()
|
|
});
|
|
};
|
|
vtab.xVtab = StructPtrMapper("xVtab", capi.sqlite3_vtab);
|
|
vtab.xCursor = StructPtrMapper("xCursor", capi.sqlite3_vtab_cursor);
|
|
vtab.xIndexInfo = (pIdxInfo) => new capi.sqlite3_index_info(pIdxInfo);
|
|
vtab.xError = function f(methodName, err, defaultRc) {
|
|
if (f.errorReporter instanceof Function) try {
|
|
f.errorReporter("sqlite3_module::" + methodName + "(): " + err.message);
|
|
} catch (e) {}
|
|
let rc;
|
|
if (err instanceof sqlite3.WasmAllocError) rc = capi.SQLITE_NOMEM;
|
|
else if (arguments.length > 2) rc = defaultRc;
|
|
else if (err instanceof sqlite3.SQLite3Error) rc = err.resultCode;
|
|
return rc || capi.SQLITE_ERROR;
|
|
};
|
|
vtab.xError.errorReporter = sqlite3.config.error.bind(sqlite3.config);
|
|
vtab.xRowid = (ppRowid64, value) => wasm.poke(ppRowid64, value, "i64");
|
|
vtab.setupModule = function(opt) {
|
|
let createdMod = false;
|
|
const mod = this instanceof capi.sqlite3_module ? this : opt.struct || (createdMod = new capi.sqlite3_module());
|
|
try {
|
|
const methods = opt.methods || toss("Missing 'methods' object.");
|
|
for (const e of Object.entries({
|
|
xConnect: "xCreate",
|
|
xDisconnect: "xDestroy"
|
|
})) {
|
|
const k = e[0], v = e[1];
|
|
if (true === methods[k]) methods[k] = methods[v];
|
|
else if (true === methods[v]) methods[v] = methods[k];
|
|
}
|
|
if (opt.catchExceptions) {
|
|
const fwrap = function(methodName, func) {
|
|
if (["xConnect", "xCreate"].indexOf(methodName) >= 0) return function(pDb, pAux, argc, argv, ppVtab, pzErr) {
|
|
try {
|
|
return func(...arguments) || 0;
|
|
} catch (e) {
|
|
if (!(e instanceof sqlite3.WasmAllocError)) {
|
|
wasm.dealloc(wasm.peekPtr(pzErr));
|
|
wasm.pokePtr(pzErr, wasm.allocCString(e.message));
|
|
}
|
|
return vtab.xError(methodName, e);
|
|
}
|
|
};
|
|
else return function(...args) {
|
|
try {
|
|
return func(...args) || 0;
|
|
} catch (e) {
|
|
return vtab.xError(methodName, e);
|
|
}
|
|
};
|
|
};
|
|
const mnames = [
|
|
"xCreate",
|
|
"xConnect",
|
|
"xBestIndex",
|
|
"xDisconnect",
|
|
"xDestroy",
|
|
"xOpen",
|
|
"xClose",
|
|
"xFilter",
|
|
"xNext",
|
|
"xEof",
|
|
"xColumn",
|
|
"xRowid",
|
|
"xUpdate",
|
|
"xBegin",
|
|
"xSync",
|
|
"xCommit",
|
|
"xRollback",
|
|
"xFindFunction",
|
|
"xRename",
|
|
"xSavepoint",
|
|
"xRelease",
|
|
"xRollbackTo",
|
|
"xShadowName"
|
|
];
|
|
const remethods = Object.create(null);
|
|
for (const k of mnames) {
|
|
const m = methods[k];
|
|
if (!(m instanceof Function)) continue;
|
|
else if ("xConnect" === k && methods.xCreate === m) remethods[k] = methods.xCreate;
|
|
else if ("xCreate" === k && methods.xConnect === m) remethods[k] = methods.xConnect;
|
|
else remethods[k] = fwrap(k, m);
|
|
}
|
|
mod.installMethods(remethods, false);
|
|
} else mod.installMethods(methods, !!opt.applyArgcCheck);
|
|
if (0 === mod.$iVersion) {
|
|
let v;
|
|
if ("number" === typeof opt.iVersion) v = opt.iVersion;
|
|
else if (mod.$xIntegrity) v = 4;
|
|
else if (mod.$xShadowName) v = 3;
|
|
else if (mod.$xSavePoint || mod.$xRelease || mod.$xRollbackTo) v = 2;
|
|
else v = 1;
|
|
mod.$iVersion = v;
|
|
}
|
|
} catch (e) {
|
|
if (createdMod) createdMod.dispose();
|
|
throw e;
|
|
}
|
|
return mod;
|
|
};
|
|
capi.sqlite3_module.prototype.setupModule = function(opt) {
|
|
return vtab.setupModule.call(this, opt);
|
|
};
|
|
});
|
|
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) {
|
|
"use strict";
|
|
const capi = sqlite3.capi, sqlite3_kvvfs_methods = capi.sqlite3_kvvfs_methods, KVVfsFile = capi.KVVfsFile, pKvvfs = sqlite3.capi.sqlite3_vfs_find("kvvfs");
|
|
delete capi.sqlite3_kvvfs_methods;
|
|
delete capi.KVVfsFile;
|
|
if (!pKvvfs) return;
|
|
const util = sqlite3.util, wasm = sqlite3.wasm, toss3 = util.toss3, hop = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
|
|
const kvvfsMethods = new sqlite3_kvvfs_methods(wasm.exports.sqlite3__wasm_kvvfs_methods());
|
|
util.assert(32 <= kvvfsMethods.$nKeySize, "unexpected kvvfsMethods.$nKeySize: " + kvvfsMethods.$nKeySize);
|
|
const cache = Object.assign(Object.create(null), {
|
|
rxJournalSuffix: /-journal$/,
|
|
zKeyJrnl: wasm.allocCString("jrnl"),
|
|
zKeySz: wasm.allocCString("sz"),
|
|
keySize: kvvfsMethods.$nKeySize,
|
|
buffer: Object.assign(Object.create(null), {
|
|
n: kvvfsMethods.$nBufferSize,
|
|
pool: Object.create(null)
|
|
})
|
|
});
|
|
cache.memBuffer = (id = 0) => cache.buffer.pool[id] ??= wasm.alloc(cache.buffer.n);
|
|
cache.memBufferFree = (id) => {
|
|
const b = cache.buffer.pool[id];
|
|
if (b) {
|
|
wasm.dealloc(b);
|
|
delete cache.buffer.pool[id];
|
|
}
|
|
};
|
|
const noop = () => {};
|
|
const debug = sqlite3.__isUnderTest ? (...args) => sqlite3.config.debug("kvvfs:", ...args) : noop;
|
|
const warn = (...args) => sqlite3.config.warn("kvvfs:", ...args);
|
|
const error = (...args) => sqlite3.config.error("kvvfs:", ...args);
|
|
class KVVfsStorage {
|
|
#map;
|
|
#keys;
|
|
#getKeys() {
|
|
return this.#keys ??= Object.keys(this.#map);
|
|
}
|
|
constructor() {
|
|
this.clear();
|
|
}
|
|
key(n) {
|
|
const k = this.#getKeys();
|
|
return n < k.length ? k[n] : null;
|
|
}
|
|
getItem(k) {
|
|
return this.#map[k] ?? null;
|
|
}
|
|
setItem(k, v) {
|
|
if (!hop(this.#map, k)) this.#keys = null;
|
|
this.#map[k] = "" + v;
|
|
}
|
|
removeItem(k) {
|
|
if (delete this.#map[k]) this.#keys = null;
|
|
}
|
|
clear() {
|
|
this.#map = Object.create(null);
|
|
this.#keys = null;
|
|
}
|
|
get length() {
|
|
return this.#getKeys().length;
|
|
}
|
|
}
|
|
const kvvfsIsPersistentName = (v) => "local" === v || "session" === v;
|
|
const kvvfsKeyPrefix = (v) => kvvfsIsPersistentName(v) ? "kvvfs-" + v + "-" : "";
|
|
const validateStorageName = function(n, mayBeJournal = false) {
|
|
if (kvvfsIsPersistentName(n)) return;
|
|
const len = new Blob([n]).size;
|
|
if (!len) toss3(capi.SQLITE_MISUSE, "Empty name is not permitted.");
|
|
let maxLen = cache.keySize - 1;
|
|
if (cache.rxJournalSuffix.test(n)) {
|
|
if (!mayBeJournal) toss3(capi.SQLITE_MISUSE, "Storage names may not have a '-journal' suffix.");
|
|
} else if (["-wal", "-shm"].filter((v) => n.endsWith(v)).length) toss3(capi.SQLITE_MISUSE, "Storage names may not have a -wal or -shm suffix.");
|
|
else maxLen -= 8;
|
|
if (len > maxLen) toss3(capi.SQLITE_RANGE, "Storage name is too long. Limit =", maxLen);
|
|
let i;
|
|
for (i = 0; i < len; ++i) {
|
|
const ch = n.codePointAt(i);
|
|
if (ch < 32) toss3(capi.SQLITE_RANGE, "Illegal character (" + ch + "d) in storage name:", n);
|
|
}
|
|
};
|
|
const newStorageObj = (name, storage = void 0) => Object.assign(Object.create(null), {
|
|
jzClass: name,
|
|
refc: 1,
|
|
deleteAtRefc0: false,
|
|
storage: storage || new KVVfsStorage(),
|
|
keyPrefix: kvvfsKeyPrefix(name),
|
|
files: [],
|
|
listeners: void 0
|
|
});
|
|
const kvvfs = sqlite3.kvvfs = Object.create(null);
|
|
if (sqlite3.__isUnderTest) kvvfs.log = Object.assign(Object.create(null), {
|
|
xOpen: false,
|
|
xClose: false,
|
|
xWrite: false,
|
|
xRead: false,
|
|
xSync: false,
|
|
xAccess: false,
|
|
xFileControl: false,
|
|
xRcrdRead: false,
|
|
xRcrdWrite: false,
|
|
xRcrdDelete: false
|
|
});
|
|
const deleteStorage = function(store) {
|
|
const other = cache.rxJournalSuffix.test(store.jzClass) ? store.jzClass.replace(cache.rxJournalSuffix, "") : store.jzClass + "-journal";
|
|
kvvfs?.log?.xClose && debug("cleaning up storage handles [", store.jzClass, other, "]", store);
|
|
delete cache.storagePool[store.jzClass];
|
|
delete cache.storagePool[other];
|
|
if (!sqlite3.__isUnderTest) {
|
|
delete store.storage;
|
|
delete store.refc;
|
|
}
|
|
};
|
|
const installStorageAndJournal = (store) => cache.storagePool[store.jzClass] = cache.storagePool[store.jzClass + "-journal"] = store;
|
|
const nameOfThisThreadStorage = ".";
|
|
cache.storagePool = Object.assign(Object.create(null), { [nameOfThisThreadStorage]: newStorageObj(nameOfThisThreadStorage) });
|
|
if (globalThis.Storage) {
|
|
if (globalThis.localStorage instanceof globalThis.Storage) cache.storagePool.local = newStorageObj("local", globalThis.localStorage);
|
|
if (globalThis.sessionStorage instanceof globalThis.Storage) cache.storagePool.session = newStorageObj("session", globalThis.sessionStorage);
|
|
}
|
|
cache.builtinStorageNames = Object.keys(cache.storagePool);
|
|
const isBuiltinName = (n) => cache.builtinStorageNames.indexOf(n) > -1;
|
|
for (const k of Object.keys(cache.storagePool)) {
|
|
const orig = cache.storagePool[k];
|
|
cache.storagePool[k + "-journal"] = orig;
|
|
}
|
|
cache.setError = (e = void 0, dfltErrCode = capi.SQLITE_ERROR) => {
|
|
if (e) {
|
|
cache.lastError = e;
|
|
return e.resultCode | 0 || dfltErrCode;
|
|
}
|
|
delete cache.lastError;
|
|
return 0;
|
|
};
|
|
cache.popError = () => {
|
|
const e = cache.lastError;
|
|
delete cache.lastError;
|
|
return e;
|
|
};
|
|
const catchForNotify = (e) => {
|
|
warn("kvvfs.listener handler threw:", e);
|
|
};
|
|
const kvvfsDecode = wasm.exports.sqlite3__wasm_kvvfs_decode;
|
|
const kvvfsEncode = wasm.exports.sqlite3__wasm_kvvfs_encode;
|
|
const notifyListeners = async function(eventName, store, ...args) {
|
|
try {
|
|
if (store.keyPrefix && args[0]) args[0] = args[0].replace(store.keyPrefix, "");
|
|
let u8enc, z0, z1, wcache;
|
|
for (const ear of store.listeners) {
|
|
const ev = Object.create(null);
|
|
ev.storageName = store.jzClass;
|
|
ev.type = eventName;
|
|
ear.decodePages;
|
|
const f = ear.events[eventName];
|
|
if (f) {
|
|
if (!ear.includeJournal && args[0] === "jrnl") continue;
|
|
if ("write" === eventName && ear.decodePages && +args[0] > 0) {
|
|
ev.data = [args[0]];
|
|
if (wcache?.[args[0]]) {
|
|
ev.data[1] = wcache[args[0]];
|
|
continue;
|
|
}
|
|
u8enc ??= new TextEncoder("utf-8");
|
|
z0 ??= cache.memBuffer(10);
|
|
z1 ??= cache.memBuffer(11);
|
|
const u = u8enc.encode(args[1]);
|
|
const heap = wasm.heap8u();
|
|
heap.set(u, Number(z0));
|
|
heap[wasm.ptr.addn(z0, u.length)] = 0;
|
|
const rc = kvvfsDecode(z0, z1, cache.buffer.n);
|
|
if (rc > 0) {
|
|
wcache ??= Object.create(null);
|
|
wcache[args[0]] = ev.data[1] = heap.slice(Number(z1), wasm.ptr.addn(z1, rc));
|
|
} else continue;
|
|
} else ev.data = args.length ? args.length === 1 ? args[0] : args : void 0;
|
|
try {
|
|
f(ev)?.catch?.(catchForNotify);
|
|
} catch (e) {
|
|
warn("notifyListeners [", store.jzClass, "]", eventName, e);
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
catchForNotify(e);
|
|
}
|
|
};
|
|
const storageForZClass = (zClass) => "string" === typeof zClass ? cache.storagePool[zClass] : cache.storagePool[wasm.cstrToJs(zClass)];
|
|
const kvvfsMakeKey = wasm.exports.sqlite3__wasm_kvvfsMakeKey;
|
|
const zKeyForStorage = (store, zClass, zKey) => {
|
|
return zClass && store.keyPrefix ? kvvfsMakeKey(zClass, zKey) : zKey;
|
|
};
|
|
const jsKeyForStorage = (store, zClass, zKey) => wasm.cstrToJs(zKeyForStorage(store, zClass, zKey));
|
|
const storageGetDbSize = (store) => +store.storage.getItem(store.keyPrefix + "sz");
|
|
const pFileHandles = /* @__PURE__ */ new Map();
|
|
const originalMethods = {
|
|
vfs: Object.create(null),
|
|
ioDb: Object.create(null),
|
|
ioJrnl: Object.create(null)
|
|
};
|
|
const pVfs = new capi.sqlite3_vfs(kvvfsMethods.$pVfs);
|
|
const pIoDb = new capi.sqlite3_io_methods(kvvfsMethods.$pIoDb);
|
|
const pIoJrnl = new capi.sqlite3_io_methods(kvvfsMethods.$pIoJrnl);
|
|
const recordHandler = Object.create(null);
|
|
const kvvfsInternal = Object.assign(Object.create(null), {
|
|
pFileHandles,
|
|
cache,
|
|
storageForZClass,
|
|
KVVfsStorage,
|
|
disablePageSizeChange: true
|
|
});
|
|
if (kvvfs.log) kvvfs.internal = kvvfsInternal;
|
|
const methodOverrides = {
|
|
recordHandler: {
|
|
xRcrdRead: (zClass, zKey, zBuf, nBuf) => {
|
|
try {
|
|
const jzClass = wasm.cstrToJs(zClass);
|
|
const store = storageForZClass(jzClass);
|
|
if (!store) return -1;
|
|
const jXKey = jsKeyForStorage(store, zClass, zKey);
|
|
kvvfs?.log?.xRcrdRead && warn("xRcrdRead", jzClass, jXKey, nBuf, store);
|
|
const jV = store.storage.getItem(jXKey);
|
|
if (null === jV) return -1;
|
|
const nV = jV.length;
|
|
if (nBuf <= 0) return nV;
|
|
else if (1 === nBuf) {
|
|
wasm.poke(zBuf, 0);
|
|
return nV;
|
|
}
|
|
if (nBuf + 1 < nV) toss3(capi.SQLITE_RANGE, "xRcrdRead()", jzClass, jXKey, "input buffer is too small: need", nV, "but have", nBuf);
|
|
const zV = cache.memBuffer(0);
|
|
const heap = wasm.heap8();
|
|
let i;
|
|
for (i = 0; i < nV; ++i) heap[wasm.ptr.add(zV, i)] = jV.codePointAt(i) & 255;
|
|
heap.copyWithin(Number(zBuf), Number(zV), wasm.ptr.addn(zV, i));
|
|
heap[wasm.ptr.add(zBuf, nV)] = 0;
|
|
return nBuf;
|
|
} catch (e) {
|
|
error("kvrecordRead()", e);
|
|
cache.setError(e);
|
|
return -2;
|
|
}
|
|
},
|
|
xRcrdWrite: (zClass, zKey, zData) => {
|
|
try {
|
|
const store = storageForZClass(zClass);
|
|
const jxKey = jsKeyForStorage(store, zClass, zKey);
|
|
const jData = wasm.cstrToJs(zData);
|
|
kvvfs?.log?.xRcrdWrite && warn("xRcrdWrite", jxKey, store);
|
|
store.storage.setItem(jxKey, jData);
|
|
store.listeners && notifyListeners("write", store, jxKey, jData);
|
|
return 0;
|
|
} catch (e) {
|
|
error("kvrecordWrite()", e);
|
|
return cache.setError(e, capi.SQLITE_IOERR);
|
|
}
|
|
},
|
|
xRcrdDelete: (zClass, zKey) => {
|
|
try {
|
|
const store = storageForZClass(zClass);
|
|
const jxKey = jsKeyForStorage(store, zClass, zKey);
|
|
kvvfs?.log?.xRcrdDelete && warn("xRcrdDelete", jxKey, store);
|
|
store.storage.removeItem(jxKey);
|
|
store.listeners && notifyListeners("delete", store, jxKey);
|
|
return 0;
|
|
} catch (e) {
|
|
error("kvrecordDelete()", e);
|
|
return cache.setError(e, capi.SQLITE_IOERR);
|
|
}
|
|
}
|
|
},
|
|
vfs: {
|
|
xOpen: function(pProtoVfs, zName, pProtoFile, flags, pOutFlags) {
|
|
cache.popError();
|
|
let zToFree;
|
|
try {
|
|
if (!zName) {
|
|
zToFree = wasm.allocCString("" + pProtoFile + "." + (Math.random() * 1e5 | 0));
|
|
zName = zToFree;
|
|
}
|
|
const jzClass = wasm.cstrToJs(zName);
|
|
kvvfs?.log?.xOpen && debug("xOpen", jzClass, "flags =", flags);
|
|
validateStorageName(jzClass, true);
|
|
if (flags & (capi.SQLITE_OPEN_MAIN_DB | capi.SQLITE_OPEN_TEMP_DB | capi.SQLITE_OPEN_TRANSIENT_DB) && cache.rxJournalSuffix.test(jzClass)) toss3(capi.SQLITE_ERROR, "DB files may not have a '-journal' suffix.");
|
|
let s = storageForZClass(jzClass);
|
|
if (!s && !(flags & capi.SQLITE_OPEN_CREATE)) toss3(capi.SQLITE_ERROR, "Storage not found:", jzClass);
|
|
const rc = originalMethods.vfs.xOpen(pProtoVfs, zName, pProtoFile, flags, pOutFlags);
|
|
if (rc) return rc;
|
|
let deleteAt0 = !!(capi.SQLITE_OPEN_DELETEONCLOSE & flags);
|
|
if (wasm.isPtr(arguments[1])) {
|
|
if (capi.sqlite3_uri_boolean(zName, "delete-on-close", 0)) deleteAt0 = true;
|
|
}
|
|
const f = new KVVfsFile(pProtoFile);
|
|
util.assert(f.$zClass, "Missing f.$zClass");
|
|
f.addOnDispose(zToFree);
|
|
zToFree = void 0;
|
|
if (s) {
|
|
++s.refc;
|
|
s.files.push(f);
|
|
wasm.poke32(pOutFlags, flags);
|
|
} else {
|
|
wasm.poke32(pOutFlags, flags | capi.SQLITE_OPEN_CREATE);
|
|
util.assert(!f.$isJournal, "Opening a journal before its db? " + jzClass);
|
|
const nm = jzClass.replace(cache.rxJournalSuffix, "");
|
|
s = newStorageObj(nm);
|
|
installStorageAndJournal(s);
|
|
s.files.push(f);
|
|
s.deleteAtRefc0 = deleteAt0;
|
|
kvvfs?.log?.xOpen && debug("xOpen installed storage handle [", nm, nm + "-journal", "]", s);
|
|
}
|
|
pFileHandles.set(pProtoFile, {
|
|
store: s,
|
|
file: f,
|
|
jzClass
|
|
});
|
|
s.listeners && notifyListeners("open", s, s.files.length);
|
|
return 0;
|
|
} catch (e) {
|
|
warn("xOpen:", e);
|
|
return cache.setError(e);
|
|
} finally {
|
|
zToFree && wasm.dealloc(zToFree);
|
|
}
|
|
},
|
|
xDelete: function(pVfs, zName, iSyncFlag) {
|
|
cache.popError();
|
|
try {
|
|
const jzName = wasm.cstrToJs(zName);
|
|
if (cache.rxJournalSuffix.test(jzName)) recordHandler.xRcrdDelete(zName, cache.zKeyJrnl);
|
|
return 0;
|
|
} catch (e) {
|
|
warn("xDelete", e);
|
|
return cache.setError(e);
|
|
}
|
|
},
|
|
xAccess: function(pProtoVfs, zPath, flags, pResOut) {
|
|
cache.popError();
|
|
try {
|
|
const s = storageForZClass(zPath);
|
|
const jzPath = s?.jzClass || wasm.cstrToJs(zPath);
|
|
if (kvvfs?.log?.xAccess) debug("xAccess", jzPath, "flags =", flags, "*pResOut =", wasm.peek32(pResOut), "store =", s);
|
|
if (!s) try {
|
|
validateStorageName(jzPath);
|
|
} catch (e) {
|
|
wasm.poke32(pResOut, 0);
|
|
return 0;
|
|
}
|
|
if (s) {
|
|
const key = s.keyPrefix + (cache.rxJournalSuffix.test(jzPath) ? "jrnl" : "1");
|
|
const res = s.storage.getItem(key) ? 0 : 1;
|
|
wasm.poke32(pResOut, res);
|
|
} else wasm.poke32(pResOut, 0);
|
|
return 0;
|
|
} catch (e) {
|
|
error("xAccess", e);
|
|
return cache.setError(e);
|
|
}
|
|
},
|
|
xRandomness: function(pVfs, nOut, pOut) {
|
|
const heap = wasm.heap8u();
|
|
let i = 0;
|
|
const npOut = Number(pOut);
|
|
for (; i < nOut; ++i) heap[npOut + i] = Math.random() * 255e3 & 255;
|
|
return nOut;
|
|
},
|
|
xGetLastError: function(pVfs, nOut, pOut) {
|
|
const e = cache.popError();
|
|
debug("xGetLastError", e);
|
|
if (e) {
|
|
const scope = wasm.scopedAllocPush();
|
|
try {
|
|
const [cMsg, n] = wasm.scopedAllocCString(e.message, true);
|
|
wasm.cstrncpy(pOut, cMsg, nOut);
|
|
if (n > nOut) wasm.poke8(wasm.ptr.add(pOut, nOut, -1), 0);
|
|
debug("set xGetLastError", e.message);
|
|
return e.resultCode | 0 || capi.SQLITE_IOERR;
|
|
} catch (e) {
|
|
return capi.SQLITE_NOMEM;
|
|
} finally {
|
|
wasm.scopedAllocPop(scope);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
},
|
|
ioDb: {
|
|
xClose: function(pFile) {
|
|
cache.popError();
|
|
try {
|
|
const h = pFileHandles.get(pFile);
|
|
kvvfs?.log?.xClose && debug("xClose", pFile, h);
|
|
if (h) {
|
|
pFileHandles.delete(pFile);
|
|
const s = h.store;
|
|
s.files = s.files.filter((v) => v !== h.file);
|
|
if (--s.refc <= 0 && s.deleteAtRefc0) deleteStorage(s);
|
|
originalMethods.ioDb.xClose(pFile);
|
|
h.file.dispose();
|
|
s.listeners && notifyListeners("close", s, s.files.length);
|
|
}
|
|
return 0;
|
|
} catch (e) {
|
|
error("xClose", e);
|
|
return cache.setError(e);
|
|
}
|
|
},
|
|
xFileControl: function(pFile, opId, pArg) {
|
|
cache.popError();
|
|
try {
|
|
const h = pFileHandles.get(pFile);
|
|
util.assert(h, "Missing KVVfsFile handle");
|
|
kvvfs?.log?.xFileControl && debug("xFileControl", h, "op =", opId);
|
|
if (opId === capi.SQLITE_FCNTL_PRAGMA && kvvfsInternal.disablePageSizeChange) {
|
|
const zName = wasm.peekPtr(wasm.ptr.add(pArg, wasm.ptr.size));
|
|
if ("page_size" === wasm.cstrToJs(zName)) {
|
|
kvvfs?.log?.xFileControl && debug("xFileControl pragma", wasm.cstrToJs(zName));
|
|
const zVal = wasm.peekPtr(wasm.ptr.add(pArg, 2 * wasm.ptr.size));
|
|
if (zVal) {
|
|
kvvfs?.log?.xFileControl && warn("xFileControl pragma", h, "NOT setting page size to", wasm.cstrToJs(zVal));
|
|
h.file.$szPage = -1;
|
|
return 0;
|
|
} else if (h.file.$szPage > 0) {
|
|
kvvfs?.log?.xFileControl && warn("xFileControl", h, "getting page size", h.file.$szPage);
|
|
wasm.pokePtr(pArg, wasm.allocCString("" + h.file.$szPage));
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
const rc = originalMethods.ioDb.xFileControl(pFile, opId, pArg);
|
|
if (0 == rc && capi.SQLITE_FCNTL_SYNC === opId) h.store.listeners && notifyListeners("sync", h.store, false);
|
|
return rc;
|
|
} catch (e) {
|
|
error("xFileControl", e);
|
|
return cache.setError(e);
|
|
}
|
|
},
|
|
xSync: function(pFile, flags) {
|
|
cache.popError();
|
|
try {
|
|
const h = pFileHandles.get(pFile);
|
|
kvvfs?.log?.xSync && debug("xSync", h);
|
|
util.assert(h, "Missing KVVfsFile handle");
|
|
const rc = originalMethods.ioDb.xSync(pFile, flags);
|
|
if (0 == rc && h.store.listeners) notifyListeners("sync", h.store, true);
|
|
return rc;
|
|
} catch (e) {
|
|
error("xSync", e);
|
|
return cache.setError(e);
|
|
}
|
|
},
|
|
xRead: function(pFile, pTgt, n, iOff64) {
|
|
cache.popError();
|
|
try {
|
|
if (kvvfs?.log?.xRead) {
|
|
const h = pFileHandles.get(pFile);
|
|
util.assert(h, "Missing KVVfsFile handle");
|
|
debug("xRead", n, iOff64, h);
|
|
}
|
|
return originalMethods.ioDb.xRead(pFile, pTgt, n, iOff64);
|
|
} catch (e) {
|
|
error("xRead", e);
|
|
return cache.setError(e);
|
|
}
|
|
},
|
|
xWrite: function(pFile, pSrc, n, iOff64) {
|
|
cache.popError();
|
|
try {
|
|
if (kvvfs?.log?.xWrite) {
|
|
const h = pFileHandles.get(pFile);
|
|
util.assert(h, "Missing KVVfsFile handle");
|
|
debug("xWrite", n, iOff64, h);
|
|
}
|
|
return originalMethods.ioDb.xWrite(pFile, pSrc, n, iOff64);
|
|
} catch (e) {
|
|
error("xWrite", e);
|
|
return cache.setError(e);
|
|
}
|
|
}
|
|
},
|
|
ioJrnl: { xClose: true }
|
|
};
|
|
debug("pVfs and friends", pVfs, pIoDb, pIoJrnl, kvvfsMethods, capi.sqlite3_file.structInfo, KVVfsFile.structInfo);
|
|
try {
|
|
util.assert(cache.buffer.n > 1024 * 129, "Heap buffer is not large enough");
|
|
for (const e of Object.entries(methodOverrides.recordHandler)) {
|
|
const k = e[0], f = e[1];
|
|
recordHandler[k] = f;
|
|
kvvfsMethods[kvvfsMethods.memberKey(k)] = wasm.installFunction(kvvfsMethods.memberSignature(k), f);
|
|
}
|
|
for (const e of Object.entries(methodOverrides.vfs)) {
|
|
const k = e[0], f = e[1], km = pVfs.memberKey(k), member = pVfs.structInfo.members[k] || util.toss("Missing pVfs.structInfo[", k, "]");
|
|
originalMethods.vfs[k] = wasm.functionEntry(pVfs[km]);
|
|
pVfs[km] = wasm.installFunction(member.signature, f);
|
|
}
|
|
for (const e of Object.entries(methodOverrides.ioDb)) {
|
|
const k = e[0], f = e[1], km = pIoDb.memberKey(k);
|
|
originalMethods.ioDb[k] = wasm.functionEntry(pIoDb[km]) || util.toss("Missing native pIoDb[", km, "]");
|
|
pIoDb[km] = wasm.installFunction(pIoDb.memberSignature(k), f);
|
|
}
|
|
for (const e of Object.entries(methodOverrides.ioJrnl)) {
|
|
const k = e[0], f = e[1], km = pIoJrnl.memberKey(k);
|
|
originalMethods.ioJrnl[k] = wasm.functionEntry(pIoJrnl[km]) || util.toss("Missing native pIoJrnl[", km, "]");
|
|
if (true === f) pIoJrnl[km] = pIoDb[km] || util.toss("Missing copied pIoDb[", km, "]");
|
|
else pIoJrnl[km] = wasm.installFunction(pIoJrnl.memberSignature(k), f);
|
|
}
|
|
} finally {
|
|
kvvfsMethods.dispose();
|
|
pVfs.dispose();
|
|
pIoDb.dispose();
|
|
pIoJrnl.dispose();
|
|
}
|
|
const sqlite3_js_kvvfs_clear = function callee(which) {
|
|
if ("" === which) return callee("local") + callee("session");
|
|
const store = storageForZClass(which);
|
|
if (!store) return 0;
|
|
if (store.files.length) if (globalThis.localStorage === store.storage || globalThis.sessionStorage === store.storage) {} else toss3(capi.SQLITE_ACCESS, "Cannot clear in-use database storage.");
|
|
const s = store.storage;
|
|
const toRm = [];
|
|
let i, n = s.length;
|
|
for (i = 0; i < n; ++i) {
|
|
const k = s.key(i);
|
|
if (!store.keyPrefix || k.startsWith(store.keyPrefix)) toRm.push(k);
|
|
}
|
|
toRm.forEach((kk) => s.removeItem(kk));
|
|
return toRm.length;
|
|
};
|
|
const sqlite3_js_kvvfs_size = function callee(which) {
|
|
if ("" === which) return callee("local") + callee("session");
|
|
const store = storageForZClass(which);
|
|
if (!store) return 0;
|
|
const s = store.storage;
|
|
let i, sz = 0;
|
|
for (i = 0; i < s.length; ++i) {
|
|
const k = s.key(i);
|
|
if (!store.keyPrefix || k.startsWith(store.keyPrefix)) {
|
|
sz += k.length;
|
|
sz += s.getItem(k).length;
|
|
}
|
|
}
|
|
return sz * 2;
|
|
};
|
|
const sqlite3_js_kvvfs_export = function callee(...args) {
|
|
let opt;
|
|
if (1 === args.length && "object" === typeof args[0]) opt = args[0];
|
|
else if (args.length) opt = Object.assign(Object.create(null), { name: args[0] });
|
|
const store = opt ? storageForZClass(opt.name) : null;
|
|
if (!store) toss3(capi.SQLITE_NOTFOUND, "There is no kvvfs storage named", opt?.name);
|
|
const s = store.storage;
|
|
const rc = Object.assign(Object.create(null), {
|
|
name: store.jzClass,
|
|
timestamp: Date.now(),
|
|
pages: []
|
|
});
|
|
const pages = Object.create(null);
|
|
const keyPrefix = store.keyPrefix;
|
|
const rxTail = keyPrefix ? /^kvvfs-[^-]+-(\w+)/ : void 0;
|
|
let i = 0, n = s.length;
|
|
for (; i < n; ++i) {
|
|
const k = s.key(i);
|
|
if (!keyPrefix || k.startsWith(keyPrefix)) {
|
|
let kk = (keyPrefix ? rxTail.exec(k) : void 0)?.[1] ?? k;
|
|
switch (kk) {
|
|
case "jrnl":
|
|
if (opt.includeJournal) rc.journal = s.getItem(k);
|
|
break;
|
|
case "sz":
|
|
rc.size = +s.getItem(k);
|
|
break;
|
|
default:
|
|
kk = +kk;
|
|
if (!util.isInt32(kk) || kk <= 0) toss3(capi.SQLITE_RANGE, "Malformed kvvfs key: " + k);
|
|
if (opt.decodePages) {
|
|
const spg = s.getItem(k), n = spg.length, z = cache.memBuffer(0), zDec = cache.memBuffer(1), heap = wasm.heap8u();
|
|
let i = 0;
|
|
for (; i < n; ++i) heap[wasm.ptr.add(z, i)] = spg.codePointAt(i) & 255;
|
|
heap[wasm.ptr.add(z, i)] = 0;
|
|
const nDec = kvvfsDecode(z, zDec, cache.buffer.n);
|
|
pages[kk] = heap.slice(Number(zDec), wasm.ptr.addn(zDec, nDec));
|
|
} else pages[kk] = s.getItem(k);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (opt.decodePages) cache.memBufferFree(1);
|
|
Object.keys(pages).map((v) => +v).sort().forEach((v) => rc.pages.push(pages[v]));
|
|
return rc;
|
|
};
|
|
const sqlite3_js_kvvfs_import = function(exp, overwrite = false) {
|
|
if (!exp?.timestamp || !exp.name || void 0 === exp.size || !Array.isArray(exp.pages)) toss3(capi.SQLITE_MISUSE, "Malformed export object.");
|
|
else if (!exp.size || exp.size !== (exp.size | 0) || exp.size >= 2147483647) toss3(capi.SQLITE_RANGE, "Invalid db size: " + exp.size);
|
|
validateStorageName(exp.name);
|
|
let store = storageForZClass(exp.name);
|
|
const isNew = !store;
|
|
if (store) {
|
|
if (!overwrite) toss3(capi.SQLITE_ACCESS, "Storage '" + exp.name + "' already exists and", "overwrite was not specified.");
|
|
else if (!store.files || !store.jzClass) toss3(capi.SQLITE_ERROR, "Internal storage object", exp.name, "seems to be malformed.");
|
|
else if (store.files.length) toss3(capi.SQLITE_IOERR_ACCESS, "Cannot import db storage while it is in use.");
|
|
sqlite3_js_kvvfs_clear(exp.name);
|
|
} else store = newStorageObj(exp.name);
|
|
const keyPrefix = kvvfsKeyPrefix(exp.name);
|
|
let zEnc;
|
|
try {
|
|
const s = store.storage;
|
|
s.setItem(keyPrefix + "sz", exp.size);
|
|
if (exp.journal) s.setItem(keyPrefix + "jrnl", exp.journal);
|
|
if (exp.pages[0] instanceof Uint8Array) exp.pages.forEach((u, ndx) => {
|
|
const n = u.length;
|
|
zEnc ??= cache.memBuffer(1);
|
|
const zBin = cache.memBuffer(0), heap = wasm.heap8u();
|
|
heap.set(u, Number(zBin));
|
|
heap[wasm.ptr.addn(zBin, n)] = 0;
|
|
const rc = kvvfsEncode(zBin, n, zEnc);
|
|
util.assert(rc < cache.buffer.n, "Impossibly long output - possibly smashed the heap");
|
|
util.assert(0 === wasm.peek8(wasm.ptr.add(zEnc, rc)), "Expecting NUL-terminated encoded output");
|
|
const jenc = wasm.cstrToJs(zEnc);
|
|
s.setItem(keyPrefix + (ndx + 1), jenc);
|
|
});
|
|
else if (exp.pages[0]) exp.pages.forEach((v, ndx) => s.setItem(keyPrefix + (ndx + 1), v));
|
|
if (isNew) installStorageAndJournal(store);
|
|
} catch {
|
|
if (!isNew) try {
|
|
sqlite3_js_kvvfs_clear(exp.name);
|
|
} catch (ee) {}
|
|
} finally {
|
|
if (zEnc) cache.memBufferFree(1);
|
|
}
|
|
return this;
|
|
};
|
|
const sqlite3_js_kvvfs_reserve = function(name) {
|
|
let store = storageForZClass(name);
|
|
if (store) {
|
|
++store.refc;
|
|
return;
|
|
}
|
|
validateStorageName(name);
|
|
installStorageAndJournal(newStorageObj(name));
|
|
};
|
|
const sqlite3_js_kvvfs_unlink = function(name) {
|
|
const store = storageForZClass(name);
|
|
if (!store || kvvfsIsPersistentName(store.jzClass) || isBuiltinName(store.jzClass) || cache.rxJournalSuffix.test(name)) return false;
|
|
if (store.refc > store.files.length || 0 === store.files.length) {
|
|
if (--store.refc <= 0) deleteStorage(store);
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
const sqlite3_js_kvvfs_listen = function(opt) {
|
|
if (!opt || "object" !== typeof opt) toss3(capi.SQLITE_MISUSE, "Expecting a listener object.");
|
|
let store = storageForZClass(opt.storage);
|
|
if (!store) if (opt.storage && opt.reserve) {
|
|
sqlite3_js_kvvfs_reserve(opt.storage);
|
|
store = storageForZClass(opt.storage);
|
|
util.assert(store, "Unexpectedly cannot fetch reserved storage " + opt.storage);
|
|
} else toss3(capi.SQLITE_NOTFOUND, "No such storage:", opt.storage);
|
|
if (opt.events) (store.listeners ??= []).push(opt);
|
|
};
|
|
const sqlite3_js_kvvfs_unlisten = function(opt) {
|
|
const store = storageForZClass(opt?.storage);
|
|
if (store?.listeners && opt.events) {
|
|
const n = store.listeners.length;
|
|
store.listeners = store.listeners.filter((v) => v !== opt);
|
|
const rc = n > store.listeners.length;
|
|
if (!store.listeners.length) store.listeners = void 0;
|
|
return rc;
|
|
}
|
|
return false;
|
|
};
|
|
sqlite3.kvvfs.reserve = sqlite3_js_kvvfs_reserve;
|
|
sqlite3.kvvfs.import = sqlite3_js_kvvfs_import;
|
|
sqlite3.kvvfs.export = sqlite3_js_kvvfs_export;
|
|
sqlite3.kvvfs.unlink = sqlite3_js_kvvfs_unlink;
|
|
sqlite3.kvvfs.listen = sqlite3_js_kvvfs_listen;
|
|
sqlite3.kvvfs.unlisten = sqlite3_js_kvvfs_unlisten;
|
|
sqlite3.kvvfs.exists = (name) => !!storageForZClass(name);
|
|
sqlite3.kvvfs.estimateSize = sqlite3_js_kvvfs_size;
|
|
sqlite3.kvvfs.clear = sqlite3_js_kvvfs_clear;
|
|
if (globalThis.Storage) {
|
|
capi.sqlite3_js_kvvfs_size = (which = "") => sqlite3_js_kvvfs_size(which);
|
|
capi.sqlite3_js_kvvfs_clear = (which = "") => sqlite3_js_kvvfs_clear(which);
|
|
}
|
|
if (sqlite3.oo1?.DB) {
|
|
const DB = sqlite3.oo1.DB;
|
|
sqlite3.oo1.JsStorageDb = function(storageName = sqlite3.oo1.JsStorageDb.defaultStorageName) {
|
|
const opt = DB.dbCtorHelper.normalizeArgs(...arguments);
|
|
opt.vfs = "kvvfs";
|
|
switch (opt.filename) {
|
|
case ":sessionStorage:":
|
|
opt.filename = "session";
|
|
break;
|
|
case ":localStorage:":
|
|
opt.filename = "local";
|
|
break;
|
|
}
|
|
const m = /(file:(\/\/)?)([^?]+)/.exec(opt.filename);
|
|
validateStorageName(m ? m[3] : opt.filename);
|
|
DB.dbCtorHelper.call(this, opt);
|
|
};
|
|
sqlite3.oo1.JsStorageDb.defaultStorageName = cache.storagePool.session ? "session" : nameOfThisThreadStorage;
|
|
const jdb = sqlite3.oo1.JsStorageDb;
|
|
jdb.prototype = Object.create(DB.prototype);
|
|
jdb.clearStorage = sqlite3_js_kvvfs_clear;
|
|
jdb.prototype.clearStorage = function() {
|
|
return jdb.clearStorage(this.affirmOpen().dbFilename(), true);
|
|
};
|
|
jdb.storageSize = sqlite3_js_kvvfs_size;
|
|
jdb.prototype.storageSize = function() {
|
|
return jdb.storageSize(this.affirmOpen().dbFilename(), true);
|
|
};
|
|
}
|
|
if (sqlite3.__isUnderTest && sqlite3.vtab) {
|
|
const cols = Object.assign(Object.create(null), {
|
|
rowid: { type: "INTEGER" },
|
|
name: { type: "TEXT" },
|
|
nRef: { type: "INTEGER" },
|
|
nOpen: { type: "INTEGER" },
|
|
isTransient: { type: "INTEGER" },
|
|
dbSize: { type: "INTEGER" }
|
|
});
|
|
Object.keys(cols).forEach((v, i) => cols[v].colId = i);
|
|
const VT = sqlite3.vtab;
|
|
const ProtoCursor = Object.assign(Object.create(null), { row: function() {
|
|
return cache.storagePool[this.names[this.rowid]];
|
|
} });
|
|
Object.assign(Object.create(ProtoCursor), {
|
|
rowid: 0,
|
|
names: Object.keys(cache.storagePool).filter((v) => !cache.rxJournalSuffix.test(v))
|
|
});
|
|
const cursorState = function(cursor, reset) {
|
|
const o = cursor instanceof capi.sqlite3_vtab_cursor ? cursor : VT.xCursor.get(cursor);
|
|
if (reset || !o.vTabState) o.vTabState = Object.assign(Object.create(ProtoCursor), {
|
|
rowid: 0,
|
|
names: Object.keys(cache.storagePool).filter((v) => !cache.rxJournalSuffix.test(v))
|
|
});
|
|
return o.vTabState;
|
|
};
|
|
const dbg = () => {};
|
|
const theModule = function f() {
|
|
return f.mod ??= new sqlite3.capi.sqlite3_module().setupModule({
|
|
catchExceptions: true,
|
|
methods: {
|
|
xConnect: function(pDb, pAux, argc, argv, ppVtab, pzErr) {
|
|
dbg("xConnect");
|
|
try {
|
|
const xcol = [];
|
|
Object.keys(cols).forEach((k) => {
|
|
xcol.push(k + " " + cols[k].type);
|
|
});
|
|
const rc = capi.sqlite3_declare_vtab(pDb, "CREATE TABLE ignored(" + xcol.join(",") + ")");
|
|
if (0 === rc) {
|
|
const t = VT.xVtab.create(ppVtab);
|
|
util.assert(t === VT.xVtab.get(wasm.peekPtr(ppVtab)), "output pointer check failed");
|
|
}
|
|
return rc;
|
|
} catch (e) {
|
|
return VT.xErrror("xConnect", e, capi.SQLITE_ERROR);
|
|
}
|
|
},
|
|
xCreate: wasm.ptr.null,
|
|
xDisconnect: function(pVtab) {
|
|
dbg("xDisconnect", ...arguments);
|
|
VT.xVtab.dispose(pVtab);
|
|
return 0;
|
|
},
|
|
xOpen: function(pVtab, ppCursor) {
|
|
dbg("xOpen", ...arguments);
|
|
VT.xCursor.create(ppCursor);
|
|
return 0;
|
|
},
|
|
xClose: function(pCursor) {
|
|
dbg("xClose", ...arguments);
|
|
const c = VT.xCursor.unget(pCursor);
|
|
delete c.vTabState;
|
|
c.dispose();
|
|
return 0;
|
|
},
|
|
xNext: function(pCursor) {
|
|
dbg("xNext", ...arguments);
|
|
const c = VT.xCursor.get(pCursor);
|
|
++cursorState(c).rowid;
|
|
return 0;
|
|
},
|
|
xColumn: function(pCursor, pCtx, iCol) {
|
|
dbg("xColumn", ...arguments);
|
|
const st = cursorState(pCursor);
|
|
const store = st.row();
|
|
util.assert(store, "Unexpected xColumn call");
|
|
switch (iCol) {
|
|
case cols.rowid.colId:
|
|
capi.sqlite3_result_int(pCtx, st.rowid);
|
|
break;
|
|
case cols.name.colId:
|
|
capi.sqlite3_result_text(pCtx, store.jzClass, -1, capi.SQLITE_TRANSIENT);
|
|
break;
|
|
case cols.nRef.colId:
|
|
capi.sqlite3_result_int(pCtx, store.refc);
|
|
break;
|
|
case cols.nOpen.colId:
|
|
capi.sqlite3_result_int(pCtx, store.files.length);
|
|
break;
|
|
case cols.isTransient.colId:
|
|
capi.sqlite3_result_int(pCtx, !!store.deleteAtRefc0);
|
|
break;
|
|
case cols.dbSize.colId:
|
|
capi.sqlite3_result_int(pCtx, storageGetDbSize(store));
|
|
break;
|
|
default:
|
|
capi.sqlite3_result_error(pCtx, "Invalid column id: " + iCol);
|
|
return capi.SQLITE_RANGE;
|
|
}
|
|
return 0;
|
|
},
|
|
xRowid: function(pCursor, ppRowid64) {
|
|
dbg("xRowid", ...arguments);
|
|
const st = cursorState(pCursor);
|
|
VT.xRowid(ppRowid64, st.rowid);
|
|
return 0;
|
|
},
|
|
xEof: function(pCursor) {
|
|
const st = cursorState(pCursor);
|
|
dbg("xEof?=" + !st.row(), ...arguments);
|
|
return !st.row();
|
|
},
|
|
xFilter: function(pCursor, idxNum, idxCStr, argc, argv) {
|
|
dbg("xFilter", ...arguments);
|
|
cursorState(pCursor, true);
|
|
return 0;
|
|
},
|
|
xBestIndex: function(pVtab, pIdxInfo) {
|
|
dbg("xBestIndex", ...arguments);
|
|
const pii = new capi.sqlite3_index_info(pIdxInfo);
|
|
pii.$estimatedRows = cache.storagePool.size;
|
|
pii.$estimatedCost = 1;
|
|
pii.dispose();
|
|
return 0;
|
|
}
|
|
}
|
|
});
|
|
};
|
|
sqlite3.kvvfs.create_module = function(pDb, name = "sqlite_kvvfs") {
|
|
return capi.sqlite3_create_module(pDb, name, theModule(), wasm.ptr.null);
|
|
};
|
|
}
|
|
});
|
|
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) {
|
|
const installOpfsVfs = function callee(options) {
|
|
if (!globalThis.SharedArrayBuffer || !globalThis.Atomics) return Promise.reject(/* @__PURE__ */ new Error("Cannot install OPFS: Missing SharedArrayBuffer and/or Atomics. The server must emit the COOP/COEP response headers to enable those. See https://sqlite.org/wasm/doc/trunk/persistence.md#coop-coep"));
|
|
else if ("undefined" === typeof WorkerGlobalScope) return Promise.reject(/* @__PURE__ */ new Error("The OPFS sqlite3_vfs cannot run in the main thread because it requires Atomics.wait()."));
|
|
else if (!globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || !navigator?.storage?.getDirectory) return Promise.reject(/* @__PURE__ */ new Error("Missing required OPFS APIs."));
|
|
if (!options || "object" !== typeof options) options = Object.create(null);
|
|
const urlParams = new URL(globalThis.location.href).searchParams;
|
|
if (urlParams.has("opfs-disable")) return Promise.resolve(sqlite3);
|
|
if (void 0 === options.verbose) options.verbose = urlParams.has("opfs-verbose") ? +urlParams.get("opfs-verbose") || 2 : 1;
|
|
if (void 0 === options.sanityChecks) options.sanityChecks = urlParams.has("opfs-sanity-check");
|
|
if (void 0 === options.proxyUri) options.proxyUri = callee.defaultProxyUri;
|
|
if ("function" === typeof options.proxyUri) options.proxyUri = options.proxyUri();
|
|
return new Promise(function(promiseResolve_, promiseReject_) {
|
|
const loggers = [
|
|
sqlite3.config.error,
|
|
sqlite3.config.warn,
|
|
sqlite3.config.log
|
|
];
|
|
const logImpl = (level, ...args) => {
|
|
if (options.verbose > level) loggers[level]("OPFS syncer:", ...args);
|
|
};
|
|
const log = (...args) => logImpl(2, ...args);
|
|
const warn = (...args) => logImpl(1, ...args);
|
|
const error = (...args) => logImpl(0, ...args);
|
|
const toss = sqlite3.util.toss;
|
|
const capi = sqlite3.capi;
|
|
const util = sqlite3.util;
|
|
const wasm = sqlite3.wasm;
|
|
const sqlite3_vfs = capi.sqlite3_vfs;
|
|
const sqlite3_file = capi.sqlite3_file;
|
|
const sqlite3_io_methods = capi.sqlite3_io_methods;
|
|
const opfsUtil = Object.create(null);
|
|
const thisThreadHasOPFS = () => {
|
|
return globalThis.FileSystemHandle && globalThis.FileSystemDirectoryHandle && globalThis.FileSystemFileHandle && globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle && navigator?.storage?.getDirectory;
|
|
};
|
|
opfsUtil.metrics = {
|
|
dump: function() {
|
|
let k, n = 0, t = 0, w = 0;
|
|
for (k in state.opIds) {
|
|
const m = metrics[k];
|
|
n += m.count;
|
|
t += m.time;
|
|
w += m.wait;
|
|
m.avgTime = m.count && m.time ? m.time / m.count : 0;
|
|
m.avgWait = m.count && m.wait ? m.wait / m.count : 0;
|
|
}
|
|
sqlite3.config.log(globalThis.location.href, "metrics for", globalThis.location.href, ":", metrics, "\nTotal of", n, "op(s) for", t, "ms (incl. " + w + " ms of waiting on the async side)");
|
|
sqlite3.config.log("Serialization metrics:", metrics.s11n);
|
|
W.postMessage({ type: "opfs-async-metrics" });
|
|
},
|
|
reset: function() {
|
|
let k;
|
|
const r = (m) => m.count = m.time = m.wait = 0;
|
|
for (k in state.opIds) r(metrics[k] = Object.create(null));
|
|
let s = metrics.s11n = Object.create(null);
|
|
s = s.serialize = Object.create(null);
|
|
s.count = s.time = 0;
|
|
s = metrics.s11n.deserialize = Object.create(null);
|
|
s.count = s.time = 0;
|
|
}
|
|
};
|
|
const opfsIoMethods = new sqlite3_io_methods();
|
|
const opfsVfs = new sqlite3_vfs().addOnDispose(() => opfsIoMethods.dispose());
|
|
let promiseWasRejected = void 0;
|
|
const promiseReject = (err) => {
|
|
promiseWasRejected = true;
|
|
opfsVfs.dispose();
|
|
return promiseReject_(err);
|
|
};
|
|
const promiseResolve = () => {
|
|
promiseWasRejected = false;
|
|
return promiseResolve_(sqlite3);
|
|
};
|
|
const W = new Worker(new URL(options.proxyUri, import.meta.url));
|
|
setTimeout(() => {
|
|
if (void 0 === promiseWasRejected) promiseReject(/* @__PURE__ */ new Error("Timeout while waiting for OPFS async proxy worker."));
|
|
}, 4e3);
|
|
W._originalOnError = W.onerror;
|
|
W.onerror = function(err) {
|
|
error("Error initializing OPFS asyncer:", err);
|
|
promiseReject(/* @__PURE__ */ new Error("Loading OPFS async Worker failed for unknown reasons."));
|
|
};
|
|
const pDVfs = capi.sqlite3_vfs_find(null);
|
|
const dVfs = pDVfs ? new sqlite3_vfs(pDVfs) : null;
|
|
opfsIoMethods.$iVersion = 1;
|
|
opfsVfs.$iVersion = 2;
|
|
opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof;
|
|
opfsVfs.$mxPathname = 1024;
|
|
opfsVfs.$zName = wasm.allocCString("opfs");
|
|
opfsVfs.$xDlOpen = opfsVfs.$xDlError = opfsVfs.$xDlSym = opfsVfs.$xDlClose = null;
|
|
opfsVfs.addOnDispose("$zName", opfsVfs.$zName, "cleanup default VFS wrapper", () => dVfs ? dVfs.dispose() : null);
|
|
const state = Object.create(null);
|
|
state.verbose = options.verbose;
|
|
state.littleEndian = (() => {
|
|
const buffer = /* @__PURE__ */ new ArrayBuffer(2);
|
|
new DataView(buffer).setInt16(0, 256, true);
|
|
return new Int16Array(buffer)[0] === 256;
|
|
})();
|
|
state.asyncIdleWaitTime = 150;
|
|
state.asyncS11nExceptions = 1;
|
|
state.fileBufferSize = 1024 * 64;
|
|
state.sabS11nOffset = state.fileBufferSize;
|
|
state.sabS11nSize = opfsVfs.$mxPathname * 2;
|
|
state.sabIO = new SharedArrayBuffer(state.fileBufferSize + state.sabS11nSize);
|
|
state.opIds = Object.create(null);
|
|
const metrics = Object.create(null);
|
|
{
|
|
let i = 0;
|
|
state.opIds.whichOp = i++;
|
|
state.opIds.rc = i++;
|
|
state.opIds.xAccess = i++;
|
|
state.opIds.xClose = i++;
|
|
state.opIds.xDelete = i++;
|
|
state.opIds.xDeleteNoWait = i++;
|
|
state.opIds.xFileSize = i++;
|
|
state.opIds.xLock = i++;
|
|
state.opIds.xOpen = i++;
|
|
state.opIds.xRead = i++;
|
|
state.opIds.xSleep = i++;
|
|
state.opIds.xSync = i++;
|
|
state.opIds.xTruncate = i++;
|
|
state.opIds.xUnlock = i++;
|
|
state.opIds.xWrite = i++;
|
|
state.opIds.mkdir = i++;
|
|
state.opIds["opfs-async-metrics"] = i++;
|
|
state.opIds["opfs-async-shutdown"] = i++;
|
|
state.opIds.retry = i++;
|
|
state.sabOP = new SharedArrayBuffer(i * 4);
|
|
opfsUtil.metrics.reset();
|
|
}
|
|
state.sq3Codes = Object.create(null);
|
|
[
|
|
"SQLITE_ACCESS_EXISTS",
|
|
"SQLITE_ACCESS_READWRITE",
|
|
"SQLITE_BUSY",
|
|
"SQLITE_CANTOPEN",
|
|
"SQLITE_ERROR",
|
|
"SQLITE_IOERR",
|
|
"SQLITE_IOERR_ACCESS",
|
|
"SQLITE_IOERR_CLOSE",
|
|
"SQLITE_IOERR_DELETE",
|
|
"SQLITE_IOERR_FSYNC",
|
|
"SQLITE_IOERR_LOCK",
|
|
"SQLITE_IOERR_READ",
|
|
"SQLITE_IOERR_SHORT_READ",
|
|
"SQLITE_IOERR_TRUNCATE",
|
|
"SQLITE_IOERR_UNLOCK",
|
|
"SQLITE_IOERR_WRITE",
|
|
"SQLITE_LOCK_EXCLUSIVE",
|
|
"SQLITE_LOCK_NONE",
|
|
"SQLITE_LOCK_PENDING",
|
|
"SQLITE_LOCK_RESERVED",
|
|
"SQLITE_LOCK_SHARED",
|
|
"SQLITE_LOCKED",
|
|
"SQLITE_MISUSE",
|
|
"SQLITE_NOTFOUND",
|
|
"SQLITE_OPEN_CREATE",
|
|
"SQLITE_OPEN_DELETEONCLOSE",
|
|
"SQLITE_OPEN_MAIN_DB",
|
|
"SQLITE_OPEN_READONLY"
|
|
].forEach((k) => {
|
|
if (void 0 === (state.sq3Codes[k] = capi[k])) toss("Maintenance required: not found:", k);
|
|
});
|
|
state.opfsFlags = Object.assign(Object.create(null), {
|
|
OPFS_UNLOCK_ASAP: 1,
|
|
OPFS_UNLINK_BEFORE_OPEN: 2,
|
|
defaultUnlockAsap: false
|
|
});
|
|
const opRun = (op, ...args) => {
|
|
const opNdx = state.opIds[op] || toss("Invalid op ID:", op);
|
|
state.s11n.serialize(...args);
|
|
Atomics.store(state.sabOPView, state.opIds.rc, -1);
|
|
Atomics.store(state.sabOPView, state.opIds.whichOp, opNdx);
|
|
Atomics.notify(state.sabOPView, state.opIds.whichOp);
|
|
const t = performance.now();
|
|
while ("not-equal" !== Atomics.wait(state.sabOPView, state.opIds.rc, -1));
|
|
const rc = Atomics.load(state.sabOPView, state.opIds.rc);
|
|
metrics[op].wait += performance.now() - t;
|
|
if (rc && state.asyncS11nExceptions) {
|
|
const err = state.s11n.deserialize();
|
|
if (err) error(op + "() async error:", ...err);
|
|
}
|
|
return rc;
|
|
};
|
|
opfsUtil.debug = {
|
|
asyncShutdown: () => {
|
|
warn("Shutting down OPFS async listener. The OPFS VFS will no longer work.");
|
|
opRun("opfs-async-shutdown");
|
|
},
|
|
asyncRestart: () => {
|
|
warn("Attempting to restart OPFS VFS async listener. Might work, might not.");
|
|
W.postMessage({ type: "opfs-async-restart" });
|
|
}
|
|
};
|
|
const initS11n = () => {
|
|
if (state.s11n) return state.s11n;
|
|
const textDecoder = new TextDecoder(), textEncoder = new TextEncoder("utf-8"), viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize), viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize);
|
|
state.s11n = Object.create(null);
|
|
const TypeIds = Object.create(null);
|
|
TypeIds.number = {
|
|
id: 1,
|
|
size: 8,
|
|
getter: "getFloat64",
|
|
setter: "setFloat64"
|
|
};
|
|
TypeIds.bigint = {
|
|
id: 2,
|
|
size: 8,
|
|
getter: "getBigInt64",
|
|
setter: "setBigInt64"
|
|
};
|
|
TypeIds.boolean = {
|
|
id: 3,
|
|
size: 4,
|
|
getter: "getInt32",
|
|
setter: "setInt32"
|
|
};
|
|
TypeIds.string = { id: 4 };
|
|
const getTypeId = (v) => TypeIds[typeof v] || toss("Maintenance required: this value type cannot be serialized.", v);
|
|
const getTypeIdById = (tid) => {
|
|
switch (tid) {
|
|
case TypeIds.number.id: return TypeIds.number;
|
|
case TypeIds.bigint.id: return TypeIds.bigint;
|
|
case TypeIds.boolean.id: return TypeIds.boolean;
|
|
case TypeIds.string.id: return TypeIds.string;
|
|
default: toss("Invalid type ID:", tid);
|
|
}
|
|
};
|
|
state.s11n.deserialize = function(clear = false) {
|
|
++metrics.s11n.deserialize.count;
|
|
const t = performance.now();
|
|
const argc = viewU8[0];
|
|
const rc = argc ? [] : null;
|
|
if (argc) {
|
|
const typeIds = [];
|
|
let offset = 1, i, n, v;
|
|
for (i = 0; i < argc; ++i, ++offset) typeIds.push(getTypeIdById(viewU8[offset]));
|
|
for (i = 0; i < argc; ++i) {
|
|
const t = typeIds[i];
|
|
if (t.getter) {
|
|
v = viewDV[t.getter](offset, state.littleEndian);
|
|
offset += t.size;
|
|
} else {
|
|
n = viewDV.getInt32(offset, state.littleEndian);
|
|
offset += 4;
|
|
v = textDecoder.decode(viewU8.slice(offset, offset + n));
|
|
offset += n;
|
|
}
|
|
rc.push(v);
|
|
}
|
|
}
|
|
if (clear) viewU8[0] = 0;
|
|
metrics.s11n.deserialize.time += performance.now() - t;
|
|
return rc;
|
|
};
|
|
state.s11n.serialize = function(...args) {
|
|
const t = performance.now();
|
|
++metrics.s11n.serialize.count;
|
|
if (args.length) {
|
|
const typeIds = [];
|
|
let i = 0, offset = 1;
|
|
viewU8[0] = args.length & 255;
|
|
for (; i < args.length; ++i, ++offset) {
|
|
typeIds.push(getTypeId(args[i]));
|
|
viewU8[offset] = typeIds[i].id;
|
|
}
|
|
for (i = 0; i < args.length; ++i) {
|
|
const t = typeIds[i];
|
|
if (t.setter) {
|
|
viewDV[t.setter](offset, args[i], state.littleEndian);
|
|
offset += t.size;
|
|
} else {
|
|
const s = textEncoder.encode(args[i]);
|
|
viewDV.setInt32(offset, s.byteLength, state.littleEndian);
|
|
offset += 4;
|
|
viewU8.set(s, offset);
|
|
offset += s.byteLength;
|
|
}
|
|
}
|
|
} else viewU8[0] = 0;
|
|
metrics.s11n.serialize.time += performance.now() - t;
|
|
};
|
|
return state.s11n;
|
|
};
|
|
const randomFilename = function f(len = 16) {
|
|
if (!f._chars) {
|
|
f._chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012346789";
|
|
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 __openFiles = Object.create(null);
|
|
const opTimer = Object.create(null);
|
|
opTimer.op = void 0;
|
|
opTimer.start = void 0;
|
|
const mTimeStart = (op) => {
|
|
opTimer.start = performance.now();
|
|
opTimer.op = op;
|
|
++metrics[op].count;
|
|
};
|
|
const mTimeEnd = () => metrics[opTimer.op].time += performance.now() - opTimer.start;
|
|
const ioSyncWrappers = {
|
|
xCheckReservedLock: function(pFile, pOut) {
|
|
wasm.poke(pOut, 0, "i32");
|
|
return 0;
|
|
},
|
|
xClose: function(pFile) {
|
|
mTimeStart("xClose");
|
|
let rc = 0;
|
|
const f = __openFiles[pFile];
|
|
if (f) {
|
|
delete __openFiles[pFile];
|
|
rc = opRun("xClose", pFile);
|
|
if (f.sq3File) f.sq3File.dispose();
|
|
}
|
|
mTimeEnd();
|
|
return rc;
|
|
},
|
|
xDeviceCharacteristics: function(pFile) {
|
|
return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
|
|
},
|
|
xFileControl: function(pFile, opId, pArg) {
|
|
return capi.SQLITE_NOTFOUND;
|
|
},
|
|
xFileSize: function(pFile, pSz64) {
|
|
mTimeStart("xFileSize");
|
|
let rc = opRun("xFileSize", pFile);
|
|
if (0 == rc) try {
|
|
const sz = state.s11n.deserialize()[0];
|
|
wasm.poke(pSz64, sz, "i64");
|
|
} catch (e) {
|
|
error("Unexpected error reading xFileSize() result:", e);
|
|
rc = state.sq3Codes.SQLITE_IOERR;
|
|
}
|
|
mTimeEnd();
|
|
return rc;
|
|
},
|
|
xLock: function(pFile, lockType) {
|
|
mTimeStart("xLock");
|
|
const f = __openFiles[pFile];
|
|
let rc = 0;
|
|
if (!f.lockType) {
|
|
rc = opRun("xLock", pFile, lockType);
|
|
if (0 === rc) f.lockType = lockType;
|
|
} else f.lockType = lockType;
|
|
mTimeEnd();
|
|
return rc;
|
|
},
|
|
xRead: function(pFile, pDest, n, offset64) {
|
|
mTimeStart("xRead");
|
|
const f = __openFiles[pFile];
|
|
let rc;
|
|
try {
|
|
rc = opRun("xRead", pFile, n, Number(offset64));
|
|
if (0 === rc || capi.SQLITE_IOERR_SHORT_READ === rc) wasm.heap8u().set(f.sabView.subarray(0, n), Number(pDest));
|
|
} catch (e) {
|
|
error("xRead(", arguments, ") failed:", e, f);
|
|
rc = capi.SQLITE_IOERR_READ;
|
|
}
|
|
mTimeEnd();
|
|
return rc;
|
|
},
|
|
xSync: function(pFile, flags) {
|
|
mTimeStart("xSync");
|
|
++metrics.xSync.count;
|
|
const rc = opRun("xSync", pFile, flags);
|
|
mTimeEnd();
|
|
return rc;
|
|
},
|
|
xTruncate: function(pFile, sz64) {
|
|
mTimeStart("xTruncate");
|
|
const rc = opRun("xTruncate", pFile, Number(sz64));
|
|
mTimeEnd();
|
|
return rc;
|
|
},
|
|
xUnlock: function(pFile, lockType) {
|
|
mTimeStart("xUnlock");
|
|
const f = __openFiles[pFile];
|
|
let rc = 0;
|
|
if (capi.SQLITE_LOCK_NONE === lockType && f.lockType) rc = opRun("xUnlock", pFile, lockType);
|
|
if (0 === rc) f.lockType = lockType;
|
|
mTimeEnd();
|
|
return rc;
|
|
},
|
|
xWrite: function(pFile, pSrc, n, offset64) {
|
|
mTimeStart("xWrite");
|
|
const f = __openFiles[pFile];
|
|
let rc;
|
|
try {
|
|
f.sabView.set(wasm.heap8u().subarray(Number(pSrc), Number(pSrc) + n));
|
|
rc = opRun("xWrite", pFile, n, Number(offset64));
|
|
} catch (e) {
|
|
error("xWrite(", arguments, ") failed:", e, f);
|
|
rc = capi.SQLITE_IOERR_WRITE;
|
|
}
|
|
mTimeEnd();
|
|
return rc;
|
|
}
|
|
};
|
|
const vfsSyncWrappers = {
|
|
xAccess: function(pVfs, zName, flags, pOut) {
|
|
mTimeStart("xAccess");
|
|
const rc = opRun("xAccess", wasm.cstrToJs(zName));
|
|
wasm.poke(pOut, rc ? 0 : 1, "i32");
|
|
mTimeEnd();
|
|
return 0;
|
|
},
|
|
xCurrentTime: function(pVfs, pOut) {
|
|
wasm.poke(pOut, 2440587.5 + (/* @__PURE__ */ new Date()).getTime() / 864e5, "double");
|
|
return 0;
|
|
},
|
|
xCurrentTimeInt64: function(pVfs, pOut) {
|
|
wasm.poke(pOut, 2440587.5 * 864e5 + (/* @__PURE__ */ new Date()).getTime(), "i64");
|
|
return 0;
|
|
},
|
|
xDelete: function(pVfs, zName, doSyncDir) {
|
|
mTimeStart("xDelete");
|
|
const rc = opRun("xDelete", wasm.cstrToJs(zName), doSyncDir, false);
|
|
mTimeEnd();
|
|
return rc;
|
|
},
|
|
xFullPathname: function(pVfs, zName, nOut, pOut) {
|
|
return wasm.cstrncpy(pOut, zName, nOut) < nOut ? 0 : capi.SQLITE_CANTOPEN;
|
|
},
|
|
xGetLastError: function(pVfs, nOut, pOut) {
|
|
warn("OPFS xGetLastError() has nothing sensible to return.");
|
|
return 0;
|
|
},
|
|
xOpen: function f(pVfs, zName, pFile, flags, pOutFlags) {
|
|
mTimeStart("xOpen");
|
|
let opfsFlags = 0;
|
|
if (0 === zName) zName = randomFilename();
|
|
else if (wasm.isPtr(zName)) {
|
|
if (capi.sqlite3_uri_boolean(zName, "opfs-unlock-asap", 0)) opfsFlags |= state.opfsFlags.OPFS_UNLOCK_ASAP;
|
|
if (capi.sqlite3_uri_boolean(zName, "delete-before-open", 0)) opfsFlags |= state.opfsFlags.OPFS_UNLINK_BEFORE_OPEN;
|
|
zName = wasm.cstrToJs(zName);
|
|
}
|
|
const fh = Object.create(null);
|
|
fh.fid = pFile;
|
|
fh.filename = zName;
|
|
fh.sab = new SharedArrayBuffer(state.fileBufferSize);
|
|
fh.flags = flags;
|
|
fh.readOnly = !(capi.SQLITE_OPEN_CREATE & flags) && !!(flags & capi.SQLITE_OPEN_READONLY);
|
|
const rc = opRun("xOpen", pFile, zName, flags, opfsFlags);
|
|
if (!rc) {
|
|
if (fh.readOnly) wasm.poke(pOutFlags, capi.SQLITE_OPEN_READONLY, "i32");
|
|
__openFiles[pFile] = fh;
|
|
fh.sabView = state.sabFileBufView;
|
|
fh.sq3File = new sqlite3_file(pFile);
|
|
fh.sq3File.$pMethods = opfsIoMethods.pointer;
|
|
fh.lockType = capi.SQLITE_LOCK_NONE;
|
|
}
|
|
mTimeEnd();
|
|
return rc;
|
|
}
|
|
};
|
|
if (dVfs) {
|
|
opfsVfs.$xRandomness = dVfs.$xRandomness;
|
|
opfsVfs.$xSleep = dVfs.$xSleep;
|
|
}
|
|
if (!opfsVfs.$xRandomness) vfsSyncWrappers.xRandomness = function(pVfs, nOut, pOut) {
|
|
const heap = wasm.heap8u();
|
|
let i = 0;
|
|
const npOut = Number(pOut);
|
|
for (; i < nOut; ++i) heap[npOut + i] = Math.random() * 255e3 & 255;
|
|
return i;
|
|
};
|
|
if (!opfsVfs.$xSleep) vfsSyncWrappers.xSleep = function(pVfs, ms) {
|
|
Atomics.wait(state.sabOPView, state.opIds.xSleep, 0, ms);
|
|
return 0;
|
|
};
|
|
opfsUtil.getResolvedPath = function(filename, splitIt) {
|
|
const p = new URL(filename, "file://irrelevant").pathname;
|
|
return splitIt ? p.split("/").filter((v) => !!v) : p;
|
|
};
|
|
opfsUtil.getDirForFilename = async function f(absFilename, createDirs = false) {
|
|
const path = opfsUtil.getResolvedPath(absFilename, true);
|
|
const filename = path.pop();
|
|
let dh = opfsUtil.rootDirectory;
|
|
for (const dirName of path) if (dirName) dh = await dh.getDirectoryHandle(dirName, { create: !!createDirs });
|
|
return [dh, filename];
|
|
};
|
|
opfsUtil.mkdir = async function(absDirName) {
|
|
try {
|
|
await opfsUtil.getDirForFilename(absDirName + "/filepart", true);
|
|
return true;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
};
|
|
opfsUtil.entryExists = async function(fsEntryName) {
|
|
try {
|
|
const [dh, fn] = await opfsUtil.getDirForFilename(fsEntryName);
|
|
await dh.getFileHandle(fn);
|
|
return true;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
};
|
|
opfsUtil.randomFilename = randomFilename;
|
|
opfsUtil.treeList = async function() {
|
|
const doDir = async function callee(dirHandle, tgt) {
|
|
tgt.name = dirHandle.name;
|
|
tgt.dirs = [];
|
|
tgt.files = [];
|
|
for await (const handle of dirHandle.values()) if ("directory" === handle.kind) {
|
|
const subDir = Object.create(null);
|
|
tgt.dirs.push(subDir);
|
|
await callee(handle, subDir);
|
|
} else tgt.files.push(handle.name);
|
|
};
|
|
const root = Object.create(null);
|
|
await doDir(opfsUtil.rootDirectory, root);
|
|
return root;
|
|
};
|
|
opfsUtil.rmfr = async function() {
|
|
const dir = opfsUtil.rootDirectory, opt = { recurse: true };
|
|
for await (const handle of dir.values()) dir.removeEntry(handle.name, opt);
|
|
};
|
|
opfsUtil.unlink = async function(fsEntryName, recursive = false, throwOnError = false) {
|
|
try {
|
|
const [hDir, filenamePart] = await opfsUtil.getDirForFilename(fsEntryName, false);
|
|
await hDir.removeEntry(filenamePart, { recursive });
|
|
return true;
|
|
} catch (e) {
|
|
if (throwOnError) throw new Error("unlink(", arguments[0], ") failed: " + e.message, { cause: e });
|
|
return false;
|
|
}
|
|
};
|
|
opfsUtil.traverse = async function(opt) {
|
|
const defaultOpt = {
|
|
recursive: true,
|
|
directory: opfsUtil.rootDirectory
|
|
};
|
|
if ("function" === typeof opt) opt = { callback: opt };
|
|
opt = Object.assign(defaultOpt, opt || {});
|
|
(async function callee(dirHandle, depth) {
|
|
for await (const handle of dirHandle.values()) if (false === opt.callback(handle, dirHandle, depth)) return false;
|
|
else if (opt.recursive && "directory" === handle.kind) {
|
|
if (false === await callee(handle, depth + 1)) break;
|
|
}
|
|
})(opt.directory, 0);
|
|
};
|
|
const importDbChunked = async function(filename, callback) {
|
|
const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true);
|
|
let sah = await (await hDir.getFileHandle(fnamePart, { create: true })).createSyncAccessHandle(), nWrote = 0, chunk, checkedHeader = false;
|
|
try {
|
|
sah.truncate(0);
|
|
while (void 0 !== (chunk = await callback())) {
|
|
if (chunk instanceof ArrayBuffer) chunk = new Uint8Array(chunk);
|
|
if (!checkedHeader && 0 === nWrote && chunk.byteLength >= 15) {
|
|
util.affirmDbHeader(chunk);
|
|
checkedHeader = true;
|
|
}
|
|
sah.write(chunk, { at: nWrote });
|
|
nWrote += chunk.byteLength;
|
|
}
|
|
if (nWrote < 512 || 0 !== nWrote % 512) toss("Input size", nWrote, "is not correct for an SQLite database.");
|
|
if (!checkedHeader) {
|
|
const header = new Uint8Array(20);
|
|
sah.read(header, { at: 0 });
|
|
util.affirmDbHeader(header);
|
|
}
|
|
sah.write(new Uint8Array([1, 1]), { at: 18 });
|
|
return nWrote;
|
|
} catch (e) {
|
|
await sah.close();
|
|
sah = void 0;
|
|
await hDir.removeEntry(fnamePart).catch(() => {});
|
|
throw e;
|
|
} finally {
|
|
if (sah) await sah.close();
|
|
}
|
|
};
|
|
opfsUtil.importDb = async function(filename, bytes) {
|
|
if (bytes instanceof Function) return importDbChunked(filename, bytes);
|
|
if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes);
|
|
util.affirmIsDb(bytes);
|
|
const n = bytes.byteLength;
|
|
const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true);
|
|
let sah, nWrote = 0;
|
|
try {
|
|
sah = await (await hDir.getFileHandle(fnamePart, { create: true })).createSyncAccessHandle();
|
|
sah.truncate(0);
|
|
nWrote = sah.write(bytes, { at: 0 });
|
|
if (nWrote != n) toss("Expected to write " + n + " bytes but wrote " + nWrote + ".");
|
|
sah.write(new Uint8Array([1, 1]), { at: 18 });
|
|
return nWrote;
|
|
} catch (e) {
|
|
if (sah) {
|
|
await sah.close();
|
|
sah = void 0;
|
|
}
|
|
await hDir.removeEntry(fnamePart).catch(() => {});
|
|
throw e;
|
|
} finally {
|
|
if (sah) await sah.close();
|
|
}
|
|
};
|
|
if (sqlite3.oo1) {
|
|
const OpfsDb = function(...args) {
|
|
const opt = sqlite3.oo1.DB.dbCtorHelper.normalizeArgs(...args);
|
|
opt.vfs = opfsVfs.$zName;
|
|
sqlite3.oo1.DB.dbCtorHelper.call(this, opt);
|
|
};
|
|
OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype);
|
|
sqlite3.oo1.OpfsDb = OpfsDb;
|
|
OpfsDb.importDb = opfsUtil.importDb;
|
|
sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenCallback(opfsVfs.pointer, function(oo1Db, sqlite3) {
|
|
sqlite3.capi.sqlite3_busy_timeout(oo1Db, 1e4);
|
|
});
|
|
}
|
|
const sanityCheck = function() {
|
|
const scope = wasm.scopedAllocPush();
|
|
const sq3File = new sqlite3_file();
|
|
try {
|
|
const fid = sq3File.pointer;
|
|
const openFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE | capi.SQLITE_OPEN_MAIN_DB;
|
|
const pOut = wasm.scopedAlloc(8);
|
|
const dbFile = "/sanity/check/file" + randomFilename(8);
|
|
const zDbFile = wasm.scopedAllocCString(dbFile);
|
|
let rc;
|
|
state.s11n.serialize("This is ä string.");
|
|
rc = state.s11n.deserialize();
|
|
log("deserialize() says:", rc);
|
|
if ("This is ä string." !== rc[0]) toss("String d13n error.");
|
|
vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut);
|
|
rc = wasm.peek(pOut, "i32");
|
|
log("xAccess(", dbFile, ") exists ?=", rc);
|
|
rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, zDbFile, fid, openFlags, pOut);
|
|
log("open rc =", rc, "state.sabOPView[xOpen] =", state.sabOPView[state.opIds.xOpen]);
|
|
if (0 !== rc) {
|
|
error("open failed with code", rc);
|
|
return;
|
|
}
|
|
vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut);
|
|
rc = wasm.peek(pOut, "i32");
|
|
if (!rc) toss("xAccess() failed to detect file.");
|
|
rc = ioSyncWrappers.xSync(sq3File.pointer, 0);
|
|
if (rc) toss("sync failed w/ rc", rc);
|
|
rc = ioSyncWrappers.xTruncate(sq3File.pointer, 1024);
|
|
if (rc) toss("truncate failed w/ rc", rc);
|
|
wasm.poke(pOut, 0, "i64");
|
|
rc = ioSyncWrappers.xFileSize(sq3File.pointer, pOut);
|
|
if (rc) toss("xFileSize failed w/ rc", rc);
|
|
log("xFileSize says:", wasm.peek(pOut, "i64"));
|
|
rc = ioSyncWrappers.xWrite(sq3File.pointer, zDbFile, 10, 1);
|
|
if (rc) toss("xWrite() failed!");
|
|
const readBuf = wasm.scopedAlloc(16);
|
|
rc = ioSyncWrappers.xRead(sq3File.pointer, readBuf, 6, 2);
|
|
wasm.poke(readBuf + 6, 0);
|
|
let jRead = wasm.cstrToJs(readBuf);
|
|
log("xRead() got:", jRead);
|
|
if ("sanity" !== jRead) toss("Unexpected xRead() value.");
|
|
if (vfsSyncWrappers.xSleep) {
|
|
log("xSleep()ing before close()ing...");
|
|
vfsSyncWrappers.xSleep(opfsVfs.pointer, 2e3);
|
|
log("waking up from xSleep()");
|
|
}
|
|
rc = ioSyncWrappers.xClose(fid);
|
|
log("xClose rc =", rc, "sabOPView =", state.sabOPView);
|
|
log("Deleting file:", dbFile);
|
|
vfsSyncWrappers.xDelete(opfsVfs.pointer, zDbFile, 4660);
|
|
vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut);
|
|
rc = wasm.peek(pOut, "i32");
|
|
if (rc) toss("Expecting 0 from xAccess(", dbFile, ") after xDelete().");
|
|
warn("End of OPFS sanity checks.");
|
|
} finally {
|
|
sq3File.dispose();
|
|
wasm.scopedAllocPop(scope);
|
|
}
|
|
};
|
|
W.onmessage = function({ data }) {
|
|
switch (data.type) {
|
|
case "opfs-unavailable":
|
|
promiseReject(new Error(data.payload.join(" ")));
|
|
break;
|
|
case "opfs-async-loaded":
|
|
W.postMessage({
|
|
type: "opfs-async-init",
|
|
args: state
|
|
});
|
|
break;
|
|
case "opfs-async-inited":
|
|
if (true === promiseWasRejected) break;
|
|
try {
|
|
sqlite3.vfs.installVfs({
|
|
io: {
|
|
struct: opfsIoMethods,
|
|
methods: ioSyncWrappers
|
|
},
|
|
vfs: {
|
|
struct: opfsVfs,
|
|
methods: vfsSyncWrappers
|
|
}
|
|
});
|
|
state.sabOPView = new Int32Array(state.sabOP);
|
|
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();
|
|
}
|
|
if (thisThreadHasOPFS()) navigator.storage.getDirectory().then((d) => {
|
|
W.onerror = W._originalOnError;
|
|
delete W._originalOnError;
|
|
sqlite3.opfs = opfsUtil;
|
|
opfsUtil.rootDirectory = d;
|
|
log("End of OPFS sqlite3_vfs setup.", opfsVfs);
|
|
promiseResolve();
|
|
}).catch(promiseReject);
|
|
else promiseResolve();
|
|
} catch (e) {
|
|
error(e);
|
|
promiseReject(e);
|
|
}
|
|
break;
|
|
default: {
|
|
const errMsg = "Unexpected message from the OPFS async worker: " + JSON.stringify(data);
|
|
error(errMsg);
|
|
promiseReject(new Error(errMsg));
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
});
|
|
};
|
|
installOpfsVfs.defaultProxyUri = "sqlite3-opfs-async-proxy.js";
|
|
globalThis.sqlite3ApiBootstrap.initializersAsync.push(async (sqlite3) => {
|
|
try {
|
|
let proxyJs = installOpfsVfs.defaultProxyUri;
|
|
if (sqlite3?.scriptInfo?.sqlite3Dir) installOpfsVfs.defaultProxyUri = sqlite3.scriptInfo.sqlite3Dir + proxyJs;
|
|
return installOpfsVfs().catch((e) => {
|
|
sqlite3.config.warn("Ignoring inability to install OPFS sqlite3_vfs:", e.message);
|
|
});
|
|
} catch (e) {
|
|
sqlite3.config.error("installOpfsVfs() exception:", e);
|
|
return Promise.reject(e);
|
|
}
|
|
});
|
|
});
|
|
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3) {
|
|
"use strict";
|
|
const toss = sqlite3.util.toss;
|
|
const toss3 = sqlite3.util.toss3;
|
|
const initPromises = Object.create(null);
|
|
const capi = sqlite3.capi;
|
|
const util = sqlite3.util;
|
|
const wasm = sqlite3.wasm;
|
|
const SECTOR_SIZE = 4096;
|
|
const HEADER_MAX_PATH_SIZE = 512;
|
|
const HEADER_FLAGS_SIZE = 4;
|
|
const HEADER_DIGEST_SIZE = 8;
|
|
const HEADER_CORPUS_SIZE = HEADER_MAX_PATH_SIZE + HEADER_FLAGS_SIZE;
|
|
const HEADER_OFFSET_FLAGS = HEADER_MAX_PATH_SIZE;
|
|
const HEADER_OFFSET_DIGEST = HEADER_CORPUS_SIZE;
|
|
const HEADER_OFFSET_DATA = SECTOR_SIZE;
|
|
const PERSISTENT_FILE_TYPES = capi.SQLITE_OPEN_MAIN_DB | capi.SQLITE_OPEN_MAIN_JOURNAL | capi.SQLITE_OPEN_SUPER_JOURNAL | capi.SQLITE_OPEN_WAL;
|
|
const FLAG_COMPUTE_DIGEST_V2 = capi.SQLITE_OPEN_MEMORY;
|
|
const OPAQUE_DIR_NAME = ".opaque";
|
|
const getRandomName = () => Math.random().toString(36).slice(2);
|
|
const textDecoder = new TextDecoder();
|
|
const textEncoder = new TextEncoder();
|
|
const optionDefaults = Object.assign(Object.create(null), {
|
|
name: "opfs-sahpool",
|
|
directory: void 0,
|
|
initialCapacity: 6,
|
|
clearOnInit: false,
|
|
verbosity: 2,
|
|
forceReinitIfPreviouslyFailed: false
|
|
});
|
|
const loggers = [
|
|
sqlite3.config.error,
|
|
sqlite3.config.warn,
|
|
sqlite3.config.log
|
|
];
|
|
sqlite3.config.log;
|
|
const warn = sqlite3.config.warn;
|
|
sqlite3.config.error;
|
|
const __mapVfsToPool = /* @__PURE__ */ new Map();
|
|
const getPoolForVfs = (pVfs) => __mapVfsToPool.get(pVfs);
|
|
const setPoolForVfs = (pVfs, pool) => {
|
|
if (pool) __mapVfsToPool.set(pVfs, pool);
|
|
else __mapVfsToPool.delete(pVfs);
|
|
};
|
|
const __mapSqlite3File = /* @__PURE__ */ new Map();
|
|
const getPoolForPFile = (pFile) => __mapSqlite3File.get(pFile);
|
|
const setPoolForPFile = (pFile, pool) => {
|
|
if (pool) __mapSqlite3File.set(pFile, pool);
|
|
else __mapSqlite3File.delete(pFile);
|
|
};
|
|
const ioMethods = {
|
|
xCheckReservedLock: function(pFile, pOut) {
|
|
const pool = getPoolForPFile(pFile);
|
|
pool.log("xCheckReservedLock");
|
|
pool.storeErr();
|
|
wasm.poke32(pOut, 1);
|
|
return 0;
|
|
},
|
|
xClose: function(pFile) {
|
|
const pool = getPoolForPFile(pFile);
|
|
pool.storeErr();
|
|
const file = pool.getOFileForS3File(pFile);
|
|
if (file) try {
|
|
pool.log(`xClose ${file.path}`);
|
|
pool.mapS3FileToOFile(pFile, false);
|
|
file.sah.flush();
|
|
if (file.flags & capi.SQLITE_OPEN_DELETEONCLOSE) pool.deletePath(file.path);
|
|
} catch (e) {
|
|
return pool.storeErr(e, capi.SQLITE_IOERR);
|
|
}
|
|
return 0;
|
|
},
|
|
xDeviceCharacteristics: function(pFile) {
|
|
return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
|
|
},
|
|
xFileControl: function(pFile, opId, pArg) {
|
|
return capi.SQLITE_NOTFOUND;
|
|
},
|
|
xFileSize: function(pFile, pSz64) {
|
|
const pool = getPoolForPFile(pFile);
|
|
pool.log(`xFileSize`);
|
|
const size = pool.getOFileForS3File(pFile).sah.getSize() - HEADER_OFFSET_DATA;
|
|
wasm.poke64(pSz64, BigInt(size));
|
|
return 0;
|
|
},
|
|
xLock: function(pFile, lockType) {
|
|
const pool = getPoolForPFile(pFile);
|
|
pool.log(`xLock ${lockType}`);
|
|
pool.storeErr();
|
|
const file = pool.getOFileForS3File(pFile);
|
|
file.lockType = lockType;
|
|
return 0;
|
|
},
|
|
xRead: function(pFile, pDest, n, offset64) {
|
|
const pool = getPoolForPFile(pFile);
|
|
pool.storeErr();
|
|
const file = pool.getOFileForS3File(pFile);
|
|
pool.log(`xRead ${file.path} ${n} @ ${offset64}`);
|
|
try {
|
|
const nRead = file.sah.read(wasm.heap8u().subarray(Number(pDest), Number(pDest) + n), { at: HEADER_OFFSET_DATA + Number(offset64) });
|
|
if (nRead < n) {
|
|
wasm.heap8u().fill(0, Number(pDest) + nRead, Number(pDest) + n);
|
|
return capi.SQLITE_IOERR_SHORT_READ;
|
|
}
|
|
return 0;
|
|
} catch (e) {
|
|
return pool.storeErr(e, capi.SQLITE_IOERR);
|
|
}
|
|
},
|
|
xSectorSize: function(pFile) {
|
|
return SECTOR_SIZE;
|
|
},
|
|
xSync: function(pFile, flags) {
|
|
const pool = getPoolForPFile(pFile);
|
|
pool.log(`xSync ${flags}`);
|
|
pool.storeErr();
|
|
const file = pool.getOFileForS3File(pFile);
|
|
try {
|
|
file.sah.flush();
|
|
return 0;
|
|
} catch (e) {
|
|
return pool.storeErr(e, capi.SQLITE_IOERR);
|
|
}
|
|
},
|
|
xTruncate: function(pFile, sz64) {
|
|
const pool = getPoolForPFile(pFile);
|
|
pool.log(`xTruncate ${sz64}`);
|
|
pool.storeErr();
|
|
const file = pool.getOFileForS3File(pFile);
|
|
try {
|
|
file.sah.truncate(HEADER_OFFSET_DATA + Number(sz64));
|
|
return 0;
|
|
} catch (e) {
|
|
return pool.storeErr(e, capi.SQLITE_IOERR);
|
|
}
|
|
},
|
|
xUnlock: function(pFile, lockType) {
|
|
const pool = getPoolForPFile(pFile);
|
|
pool.log("xUnlock");
|
|
const file = pool.getOFileForS3File(pFile);
|
|
file.lockType = lockType;
|
|
return 0;
|
|
},
|
|
xWrite: function(pFile, pSrc, n, offset64) {
|
|
const pool = getPoolForPFile(pFile);
|
|
pool.storeErr();
|
|
const file = pool.getOFileForS3File(pFile);
|
|
pool.log(`xWrite ${file.path} ${n} ${offset64}`);
|
|
try {
|
|
return n === file.sah.write(wasm.heap8u().subarray(Number(pSrc), Number(pSrc) + n), { at: HEADER_OFFSET_DATA + Number(offset64) }) ? 0 : toss("Unknown write() failure.");
|
|
} catch (e) {
|
|
return pool.storeErr(e, capi.SQLITE_IOERR);
|
|
}
|
|
}
|
|
};
|
|
const opfsIoMethods = new capi.sqlite3_io_methods();
|
|
opfsIoMethods.$iVersion = 1;
|
|
sqlite3.vfs.installVfs({ io: {
|
|
struct: opfsIoMethods,
|
|
methods: ioMethods
|
|
} });
|
|
const vfsMethods = {
|
|
xAccess: function(pVfs, zName, flags, pOut) {
|
|
const pool = getPoolForVfs(pVfs);
|
|
pool.storeErr();
|
|
try {
|
|
const name = pool.getPath(zName);
|
|
wasm.poke32(pOut, pool.hasFilename(name) ? 1 : 0);
|
|
} catch (e) {
|
|
wasm.poke32(pOut, 0);
|
|
}
|
|
return 0;
|
|
},
|
|
xCurrentTime: function(pVfs, pOut) {
|
|
wasm.poke(pOut, 2440587.5 + (/* @__PURE__ */ new Date()).getTime() / 864e5, "double");
|
|
return 0;
|
|
},
|
|
xCurrentTimeInt64: function(pVfs, pOut) {
|
|
wasm.poke(pOut, 2440587.5 * 864e5 + (/* @__PURE__ */ new Date()).getTime(), "i64");
|
|
return 0;
|
|
},
|
|
xDelete: function(pVfs, zName, doSyncDir) {
|
|
const pool = getPoolForVfs(pVfs);
|
|
pool.log(`xDelete ${wasm.cstrToJs(zName)}`);
|
|
pool.storeErr();
|
|
try {
|
|
pool.deletePath(pool.getPath(zName));
|
|
return 0;
|
|
} catch (e) {
|
|
pool.storeErr(e);
|
|
return capi.SQLITE_IOERR_DELETE;
|
|
}
|
|
},
|
|
xFullPathname: function(pVfs, zName, nOut, pOut) {
|
|
return wasm.cstrncpy(pOut, zName, nOut) < nOut ? 0 : capi.SQLITE_CANTOPEN;
|
|
},
|
|
xGetLastError: function(pVfs, nOut, pOut) {
|
|
const pool = getPoolForVfs(pVfs);
|
|
const e = pool.popErr();
|
|
pool.log(`xGetLastError ${nOut} e =`, e);
|
|
if (e) {
|
|
const scope = wasm.scopedAllocPush();
|
|
try {
|
|
const [cMsg, n] = wasm.scopedAllocCString(e.message, true);
|
|
wasm.cstrncpy(pOut, cMsg, nOut);
|
|
if (n > nOut) wasm.poke8(wasm.ptr.add(pOut, nOut, -1), 0);
|
|
} catch (e) {
|
|
return capi.SQLITE_NOMEM;
|
|
} finally {
|
|
wasm.scopedAllocPop(scope);
|
|
}
|
|
}
|
|
return e ? e.sqlite3Rc || capi.SQLITE_IOERR : 0;
|
|
},
|
|
xOpen: function f(pVfs, zName, pFile, flags, pOutFlags) {
|
|
const pool = getPoolForVfs(pVfs);
|
|
try {
|
|
flags &= ~FLAG_COMPUTE_DIGEST_V2;
|
|
pool.log(`xOpen ${wasm.cstrToJs(zName)} ${flags}`);
|
|
const path = zName && wasm.peek8(zName) ? pool.getPath(zName) : getRandomName();
|
|
let sah = pool.getSAHForPath(path);
|
|
if (!sah && flags & capi.SQLITE_OPEN_CREATE) if (pool.getFileCount() < pool.getCapacity()) {
|
|
sah = pool.nextAvailableSAH();
|
|
pool.setAssociatedPath(sah, path, flags);
|
|
} else toss("SAH pool is full. Cannot create file", path);
|
|
if (!sah) toss("file not found:", path);
|
|
const file = {
|
|
path,
|
|
flags,
|
|
sah
|
|
};
|
|
pool.mapS3FileToOFile(pFile, file);
|
|
file.lockType = capi.SQLITE_LOCK_NONE;
|
|
const sq3File = new capi.sqlite3_file(pFile);
|
|
sq3File.$pMethods = opfsIoMethods.pointer;
|
|
sq3File.dispose();
|
|
wasm.poke32(pOutFlags, flags);
|
|
return 0;
|
|
} catch (e) {
|
|
pool.storeErr(e);
|
|
return capi.SQLITE_CANTOPEN;
|
|
}
|
|
}
|
|
};
|
|
const createOpfsVfs = function(vfsName) {
|
|
if (sqlite3.capi.sqlite3_vfs_find(vfsName)) toss3("VFS name is already registered:", vfsName);
|
|
const opfsVfs = new capi.sqlite3_vfs();
|
|
const pDVfs = capi.sqlite3_vfs_find(null);
|
|
const dVfs = pDVfs ? new capi.sqlite3_vfs(pDVfs) : null;
|
|
opfsVfs.$iVersion = 2;
|
|
opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof;
|
|
opfsVfs.$mxPathname = HEADER_MAX_PATH_SIZE;
|
|
opfsVfs.addOnDispose(opfsVfs.$zName = wasm.allocCString(vfsName), () => setPoolForVfs(opfsVfs.pointer, 0));
|
|
if (dVfs) {
|
|
opfsVfs.$xRandomness = dVfs.$xRandomness;
|
|
opfsVfs.$xSleep = dVfs.$xSleep;
|
|
dVfs.dispose();
|
|
}
|
|
if (!opfsVfs.$xRandomness && !vfsMethods.xRandomness) vfsMethods.xRandomness = function(pVfs, nOut, pOut) {
|
|
const heap = wasm.heap8u();
|
|
let i = 0;
|
|
const npOut = Number(pOut);
|
|
for (; i < nOut; ++i) heap[npOut + i] = Math.random() * 255e3 & 255;
|
|
return i;
|
|
};
|
|
if (!opfsVfs.$xSleep && !vfsMethods.xSleep) vfsMethods.xSleep = (pVfs, ms) => 0;
|
|
sqlite3.vfs.installVfs({ vfs: {
|
|
struct: opfsVfs,
|
|
methods: vfsMethods
|
|
} });
|
|
return opfsVfs;
|
|
};
|
|
class OpfsSAHPool {
|
|
vfsDir;
|
|
#dhVfsRoot;
|
|
#dhOpaque;
|
|
#dhVfsParent;
|
|
#mapSAHToName = /* @__PURE__ */ new Map();
|
|
#mapFilenameToSAH = /* @__PURE__ */ new Map();
|
|
#availableSAH = /* @__PURE__ */ new Set();
|
|
#mapS3FileToOFile_ = /* @__PURE__ */ new Map();
|
|
#apBody = new Uint8Array(HEADER_CORPUS_SIZE);
|
|
#dvBody;
|
|
#cVfs;
|
|
#verbosity;
|
|
constructor(options = Object.create(null)) {
|
|
this.#verbosity = options.verbosity ?? optionDefaults.verbosity;
|
|
this.vfsName = options.name || optionDefaults.name;
|
|
this.#cVfs = createOpfsVfs(this.vfsName);
|
|
setPoolForVfs(this.#cVfs.pointer, this);
|
|
this.vfsDir = options.directory || "." + this.vfsName;
|
|
this.#dvBody = new DataView(this.#apBody.buffer, this.#apBody.byteOffset);
|
|
this.isReady = this.reset(!!(options.clearOnInit ?? optionDefaults.clearOnInit)).then(() => {
|
|
if (this.$error) throw this.$error;
|
|
return this.getCapacity() ? Promise.resolve(void 0) : this.addCapacity(options.initialCapacity || optionDefaults.initialCapacity);
|
|
});
|
|
}
|
|
#logImpl(level, ...args) {
|
|
if (this.#verbosity > level) loggers[level](this.vfsName + ":", ...args);
|
|
}
|
|
log(...args) {
|
|
this.#logImpl(2, ...args);
|
|
}
|
|
warn(...args) {
|
|
this.#logImpl(1, ...args);
|
|
}
|
|
error(...args) {
|
|
this.#logImpl(0, ...args);
|
|
}
|
|
getVfs() {
|
|
return this.#cVfs;
|
|
}
|
|
getCapacity() {
|
|
return this.#mapSAHToName.size;
|
|
}
|
|
getFileCount() {
|
|
return this.#mapFilenameToSAH.size;
|
|
}
|
|
getFileNames() {
|
|
const rc = [];
|
|
for (const n of this.#mapFilenameToSAH.keys()) rc.push(n);
|
|
return rc;
|
|
}
|
|
async addCapacity(n) {
|
|
for (let i = 0; i < n; ++i) {
|
|
const name = getRandomName();
|
|
const ah = await (await this.#dhOpaque.getFileHandle(name, { create: true })).createSyncAccessHandle();
|
|
this.#mapSAHToName.set(ah, name);
|
|
this.setAssociatedPath(ah, "", 0);
|
|
}
|
|
return this.getCapacity();
|
|
}
|
|
async reduceCapacity(n) {
|
|
let nRm = 0;
|
|
for (const ah of Array.from(this.#availableSAH)) {
|
|
if (nRm === n || this.getFileCount() === this.getCapacity()) break;
|
|
const name = this.#mapSAHToName.get(ah);
|
|
ah.close();
|
|
await this.#dhOpaque.removeEntry(name);
|
|
this.#mapSAHToName.delete(ah);
|
|
this.#availableSAH.delete(ah);
|
|
++nRm;
|
|
}
|
|
return nRm;
|
|
}
|
|
releaseAccessHandles() {
|
|
for (const ah of this.#mapSAHToName.keys()) ah.close();
|
|
this.#mapSAHToName.clear();
|
|
this.#mapFilenameToSAH.clear();
|
|
this.#availableSAH.clear();
|
|
}
|
|
async acquireAccessHandles(clearFiles = false) {
|
|
const files = [];
|
|
for await (const [name, h] of this.#dhOpaque) if ("file" === h.kind) files.push([name, h]);
|
|
return Promise.all(files.map(async ([name, h]) => {
|
|
try {
|
|
const ah = await h.createSyncAccessHandle();
|
|
this.#mapSAHToName.set(ah, name);
|
|
if (clearFiles) {
|
|
ah.truncate(HEADER_OFFSET_DATA);
|
|
this.setAssociatedPath(ah, "", 0);
|
|
} else {
|
|
const path = this.getAssociatedPath(ah);
|
|
if (path) this.#mapFilenameToSAH.set(path, ah);
|
|
else this.#availableSAH.add(ah);
|
|
}
|
|
} catch (e) {
|
|
this.storeErr(e);
|
|
this.releaseAccessHandles();
|
|
throw e;
|
|
}
|
|
}));
|
|
}
|
|
getAssociatedPath(sah) {
|
|
sah.read(this.#apBody, { at: 0 });
|
|
const flags = this.#dvBody.getUint32(HEADER_OFFSET_FLAGS);
|
|
if (this.#apBody[0] && (flags & capi.SQLITE_OPEN_DELETEONCLOSE || (flags & PERSISTENT_FILE_TYPES) === 0)) {
|
|
warn(`Removing file with unexpected flags ${flags.toString(16)}`, this.#apBody);
|
|
this.setAssociatedPath(sah, "", 0);
|
|
return "";
|
|
}
|
|
const fileDigest = new Uint32Array(HEADER_DIGEST_SIZE / 4);
|
|
sah.read(fileDigest, { at: HEADER_OFFSET_DIGEST });
|
|
const compDigest = this.computeDigest(this.#apBody, flags);
|
|
if (fileDigest.every((v, i) => v === compDigest[i])) {
|
|
const pathBytes = this.#apBody.findIndex((v) => 0 === v);
|
|
if (0 === pathBytes) sah.truncate(HEADER_OFFSET_DATA);
|
|
return pathBytes ? textDecoder.decode(this.#apBody.subarray(0, pathBytes)) : "";
|
|
} else {
|
|
warn("Disassociating file with bad digest.");
|
|
this.setAssociatedPath(sah, "", 0);
|
|
return "";
|
|
}
|
|
}
|
|
setAssociatedPath(sah, path, flags) {
|
|
const enc = textEncoder.encodeInto(path, this.#apBody);
|
|
if (HEADER_MAX_PATH_SIZE <= enc.written + 1) toss("Path too long:", path);
|
|
if (path && flags) flags |= FLAG_COMPUTE_DIGEST_V2;
|
|
this.#apBody.fill(0, enc.written, HEADER_MAX_PATH_SIZE);
|
|
this.#dvBody.setUint32(HEADER_OFFSET_FLAGS, flags);
|
|
const digest = this.computeDigest(this.#apBody, flags);
|
|
sah.write(this.#apBody, { at: 0 });
|
|
sah.write(digest, { at: HEADER_OFFSET_DIGEST });
|
|
sah.flush();
|
|
if (path) {
|
|
this.#mapFilenameToSAH.set(path, sah);
|
|
this.#availableSAH.delete(sah);
|
|
} else {
|
|
sah.truncate(HEADER_OFFSET_DATA);
|
|
this.#availableSAH.add(sah);
|
|
}
|
|
}
|
|
computeDigest(byteArray, fileFlags) {
|
|
if (fileFlags & FLAG_COMPUTE_DIGEST_V2) {
|
|
let h1 = 3735928559;
|
|
let h2 = 1103547991;
|
|
for (const v of byteArray) {
|
|
h1 = Math.imul(h1 ^ v, 2654435761);
|
|
h2 = Math.imul(h2 ^ v, 104729);
|
|
}
|
|
return new Uint32Array([h1 >>> 0, h2 >>> 0]);
|
|
} else return new Uint32Array([0, 0]);
|
|
}
|
|
async reset(clearFiles) {
|
|
await this.isReady;
|
|
let h = await navigator.storage.getDirectory(), prev;
|
|
for (const d of this.vfsDir.split("/")) if (d) {
|
|
prev = h;
|
|
h = await h.getDirectoryHandle(d, { create: true });
|
|
}
|
|
this.#dhVfsRoot = h;
|
|
this.#dhVfsParent = prev;
|
|
this.#dhOpaque = await this.#dhVfsRoot.getDirectoryHandle(OPAQUE_DIR_NAME, { create: true });
|
|
this.releaseAccessHandles();
|
|
return this.acquireAccessHandles(clearFiles);
|
|
}
|
|
getPath(arg) {
|
|
if (wasm.isPtr(arg)) arg = wasm.cstrToJs(arg);
|
|
return (arg instanceof URL ? arg : new URL(arg, "file://localhost/")).pathname;
|
|
}
|
|
deletePath(path) {
|
|
const sah = this.#mapFilenameToSAH.get(path);
|
|
if (sah) {
|
|
this.#mapFilenameToSAH.delete(path);
|
|
this.setAssociatedPath(sah, "", 0);
|
|
}
|
|
return !!sah;
|
|
}
|
|
storeErr(e, code) {
|
|
if (e) {
|
|
e.sqlite3Rc = code || capi.SQLITE_IOERR;
|
|
this.error(e);
|
|
}
|
|
this.$error = e;
|
|
return code;
|
|
}
|
|
popErr() {
|
|
const rc = this.$error;
|
|
this.$error = void 0;
|
|
return rc;
|
|
}
|
|
nextAvailableSAH() {
|
|
const [rc] = this.#availableSAH.keys();
|
|
return rc;
|
|
}
|
|
getOFileForS3File(pFile) {
|
|
return this.#mapS3FileToOFile_.get(pFile);
|
|
}
|
|
mapS3FileToOFile(pFile, file) {
|
|
if (file) {
|
|
this.#mapS3FileToOFile_.set(pFile, file);
|
|
setPoolForPFile(pFile, this);
|
|
} else {
|
|
this.#mapS3FileToOFile_.delete(pFile);
|
|
setPoolForPFile(pFile, false);
|
|
}
|
|
}
|
|
hasFilename(name) {
|
|
return this.#mapFilenameToSAH.has(name);
|
|
}
|
|
getSAHForPath(path) {
|
|
return this.#mapFilenameToSAH.get(path);
|
|
}
|
|
async removeVfs() {
|
|
if (!this.#cVfs.pointer || !this.#dhOpaque) return false;
|
|
capi.sqlite3_vfs_unregister(this.#cVfs.pointer);
|
|
this.#cVfs.dispose();
|
|
delete initPromises[this.vfsName];
|
|
try {
|
|
this.releaseAccessHandles();
|
|
await this.#dhVfsRoot.removeEntry(OPAQUE_DIR_NAME, { recursive: true });
|
|
this.#dhOpaque = void 0;
|
|
await this.#dhVfsParent.removeEntry(this.#dhVfsRoot.name, { recursive: true });
|
|
this.#dhVfsRoot = this.#dhVfsParent = void 0;
|
|
} catch (e) {
|
|
sqlite3.config.error(this.vfsName, "removeVfs() failed with no recovery strategy:", e);
|
|
}
|
|
return true;
|
|
}
|
|
pauseVfs() {
|
|
if (this.#mapS3FileToOFile_.size > 0) sqlite3.SQLite3Error.toss(capi.SQLITE_MISUSE, "Cannot pause VFS", this.vfsName, "because it has opened files.");
|
|
if (this.#mapSAHToName.size > 0) {
|
|
capi.sqlite3_vfs_unregister(this.vfsName);
|
|
this.releaseAccessHandles();
|
|
}
|
|
return this;
|
|
}
|
|
isPaused() {
|
|
return 0 === this.#mapSAHToName.size;
|
|
}
|
|
async unpauseVfs() {
|
|
if (0 === this.#mapSAHToName.size) return this.acquireAccessHandles(false).then(() => capi.sqlite3_vfs_register(this.#cVfs, 0), this);
|
|
return this;
|
|
}
|
|
//! Documented elsewhere in this file.
|
|
exportFile(name) {
|
|
const sah = this.#mapFilenameToSAH.get(name) || toss("File not found:", name);
|
|
const n = sah.getSize() - HEADER_OFFSET_DATA;
|
|
const b = new Uint8Array(n > 0 ? n : 0);
|
|
if (n > 0) {
|
|
const nRead = sah.read(b, { at: HEADER_OFFSET_DATA });
|
|
if (nRead != n) toss("Expected to read " + n + " bytes but read " + nRead + ".");
|
|
}
|
|
return b;
|
|
}
|
|
//! Impl for importDb() when its 2nd arg is a function.
|
|
async importDbChunked(name, callback) {
|
|
const sah = this.#mapFilenameToSAH.get(name) || this.nextAvailableSAH() || toss("No available handles to import to.");
|
|
sah.truncate(0);
|
|
let nWrote = 0, chunk, checkedHeader = false;
|
|
try {
|
|
while (void 0 !== (chunk = await callback())) {
|
|
if (chunk instanceof ArrayBuffer) chunk = new Uint8Array(chunk);
|
|
if (!checkedHeader && 0 === nWrote && chunk.byteLength >= 15) {
|
|
util.affirmDbHeader(chunk);
|
|
checkedHeader = true;
|
|
}
|
|
sah.write(chunk, { at: HEADER_OFFSET_DATA + nWrote });
|
|
nWrote += chunk.byteLength;
|
|
}
|
|
if (nWrote < 512 || 0 !== nWrote % 512) toss("Input size", nWrote, "is not correct for an SQLite database.");
|
|
if (!checkedHeader) {
|
|
const header = new Uint8Array(20);
|
|
sah.read(header, { at: 0 });
|
|
util.affirmDbHeader(header);
|
|
}
|
|
sah.write(new Uint8Array([1, 1]), { at: HEADER_OFFSET_DATA + 18 });
|
|
} catch (e) {
|
|
this.setAssociatedPath(sah, "", 0);
|
|
throw e;
|
|
}
|
|
this.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB);
|
|
return nWrote;
|
|
}
|
|
//! Documented elsewhere in this file.
|
|
importDb(name, bytes) {
|
|
if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes);
|
|
else if (bytes instanceof Function) return this.importDbChunked(name, bytes);
|
|
const sah = this.#mapFilenameToSAH.get(name) || this.nextAvailableSAH() || toss("No available handles to import to.");
|
|
const n = bytes.byteLength;
|
|
if (n < 512 || n % 512 != 0) toss("Byte array size is invalid for an SQLite db.");
|
|
const header = "SQLite format 3";
|
|
for (let i = 0; i < 15; ++i) if (header.charCodeAt(i) !== bytes[i]) toss("Input does not contain an SQLite database header.");
|
|
const nWrote = sah.write(bytes, { at: HEADER_OFFSET_DATA });
|
|
if (nWrote != n) {
|
|
this.setAssociatedPath(sah, "", 0);
|
|
toss("Expected to write " + n + " bytes but wrote " + nWrote + ".");
|
|
} else {
|
|
sah.write(new Uint8Array([1, 1]), { at: HEADER_OFFSET_DATA + 18 });
|
|
this.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB);
|
|
}
|
|
return nWrote;
|
|
}
|
|
}
|
|
class OpfsSAHPoolUtil {
|
|
#p;
|
|
constructor(sahPool) {
|
|
this.#p = sahPool;
|
|
this.vfsName = sahPool.vfsName;
|
|
}
|
|
async addCapacity(n) {
|
|
return this.#p.addCapacity(n);
|
|
}
|
|
async reduceCapacity(n) {
|
|
return this.#p.reduceCapacity(n);
|
|
}
|
|
getCapacity() {
|
|
return this.#p.getCapacity(this.#p);
|
|
}
|
|
getFileCount() {
|
|
return this.#p.getFileCount();
|
|
}
|
|
getFileNames() {
|
|
return this.#p.getFileNames();
|
|
}
|
|
async reserveMinimumCapacity(min) {
|
|
const c = this.#p.getCapacity();
|
|
return c < min ? this.#p.addCapacity(min - c) : c;
|
|
}
|
|
exportFile(name) {
|
|
return this.#p.exportFile(name);
|
|
}
|
|
importDb(name, bytes) {
|
|
return this.#p.importDb(name, bytes);
|
|
}
|
|
async wipeFiles() {
|
|
return this.#p.reset(true);
|
|
}
|
|
unlink(filename) {
|
|
return this.#p.deletePath(filename);
|
|
}
|
|
async removeVfs() {
|
|
return this.#p.removeVfs();
|
|
}
|
|
pauseVfs() {
|
|
this.#p.pauseVfs();
|
|
return this;
|
|
}
|
|
async unpauseVfs() {
|
|
return this.#p.unpauseVfs().then(() => this);
|
|
}
|
|
isPaused() {
|
|
return this.#p.isPaused();
|
|
}
|
|
}
|
|
const apiVersionCheck = async () => {
|
|
const dh = await navigator.storage.getDirectory();
|
|
const fn = ".opfs-sahpool-sync-check-" + getRandomName();
|
|
const close = (await (await dh.getFileHandle(fn, { create: true })).createSyncAccessHandle()).close();
|
|
await close;
|
|
await dh.removeEntry(fn);
|
|
if (close?.then) toss("The local OPFS API is too old for opfs-sahpool:", "it has an async FileSystemSyncAccessHandle.close() method.");
|
|
return true;
|
|
};
|
|
sqlite3.installOpfsSAHPoolVfs = async function(options = Object.create(null)) {
|
|
options = Object.assign(Object.create(null), optionDefaults, options || {});
|
|
const vfsName = options.name;
|
|
if (options.$testThrowPhase1) throw options.$testThrowPhase1;
|
|
if (initPromises[vfsName]) try {
|
|
return await initPromises[vfsName];
|
|
} catch (e) {
|
|
if (options.forceReinitIfPreviouslyFailed) delete initPromises[vfsName];
|
|
else throw e;
|
|
}
|
|
if (!globalThis.FileSystemHandle || !globalThis.FileSystemDirectoryHandle || !globalThis.FileSystemFileHandle || !globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle || !navigator?.storage?.getDirectory) return initPromises[vfsName] = Promise.reject(/* @__PURE__ */ new Error("Missing required OPFS APIs."));
|
|
return initPromises[vfsName] = apiVersionCheck().then(async function() {
|
|
if (options.$testThrowPhase2) throw options.$testThrowPhase2;
|
|
const thePool = new OpfsSAHPool(options);
|
|
return thePool.isReady.then(async () => {
|
|
const poolUtil = new OpfsSAHPoolUtil(thePool);
|
|
if (sqlite3.oo1) {
|
|
const oo1 = sqlite3.oo1;
|
|
const theVfs = thePool.getVfs();
|
|
const OpfsSAHPoolDb = function(...args) {
|
|
const opt = oo1.DB.dbCtorHelper.normalizeArgs(...args);
|
|
opt.vfs = theVfs.$zName;
|
|
oo1.DB.dbCtorHelper.call(this, opt);
|
|
};
|
|
OpfsSAHPoolDb.prototype = Object.create(oo1.DB.prototype);
|
|
poolUtil.OpfsSAHPoolDb = OpfsSAHPoolDb;
|
|
}
|
|
thePool.log("VFS initialized.");
|
|
return poolUtil;
|
|
}).catch(async (e) => {
|
|
await thePool.removeVfs().catch(() => {});
|
|
throw e;
|
|
});
|
|
}).catch((err) => {
|
|
return initPromises[vfsName] = Promise.reject(err);
|
|
});
|
|
};
|
|
});
|
|
try {
|
|
const bootstrapConfig = Object.assign(Object.create(null), {
|
|
memory: "undefined" !== typeof wasmMemory ? wasmMemory : EmscriptenModule["wasmMemory"],
|
|
exports: "undefined" !== typeof wasmExports ? wasmExports : Object.prototype.hasOwnProperty.call(EmscriptenModule, "wasmExports") ? EmscriptenModule["wasmExports"] : EmscriptenModule["asm"]
|
|
}, globalThis.sqlite3ApiBootstrap.defaultConfig, globalThis.sqlite3ApiConfig || {});
|
|
sqlite3InitScriptInfo.debugModule("Bootstrapping lib config", bootstrapConfig);
|
|
const p = globalThis.sqlite3ApiBootstrap(bootstrapConfig);
|
|
delete globalThis.sqlite3ApiBootstrap;
|
|
return p;
|
|
} catch (e) {
|
|
console.error("sqlite3ApiBootstrap() error:", e);
|
|
throw e;
|
|
}
|
|
};
|
|
if (runtimeInitialized) moduleRtn = Module;
|
|
else moduleRtn = new Promise((resolve, reject) => {
|
|
readyPromiseResolve = resolve;
|
|
readyPromiseReject = reject;
|
|
});
|
|
return moduleRtn;
|
|
}
|
|
sqlite3InitModule = (function() {
|
|
const originalInit = sqlite3InitModule;
|
|
if (!originalInit) throw new Error("Expecting sqlite3InitModule to be defined by the Emscripten build.");
|
|
const sIMS = globalThis.sqlite3InitModuleState = Object.assign(Object.create(null), {
|
|
moduleScript: globalThis?.document?.currentScript,
|
|
isWorker: "undefined" !== typeof WorkerGlobalScope,
|
|
location: globalThis.location,
|
|
urlParams: globalThis?.location?.href ? new URL(globalThis.location.href).searchParams : new URLSearchParams(),
|
|
wasmFilename: "sqlite3.wasm"
|
|
});
|
|
sIMS.debugModule = sIMS.urlParams.has("sqlite3.debugModule") ? (...args) => console.warn("sqlite3.debugModule:", ...args) : () => {};
|
|
if (sIMS.urlParams.has("sqlite3.dir")) sIMS.sqlite3Dir = sIMS.urlParams.get("sqlite3.dir") + "/";
|
|
else if (sIMS.moduleScript) {
|
|
const li = sIMS.moduleScript.src.split("/");
|
|
li.pop();
|
|
sIMS.sqlite3Dir = li.join("/") + "/";
|
|
}
|
|
const sIM = globalThis.sqlite3InitModule = function ff(...args) {
|
|
sIMS.emscriptenLocateFile = args[0]?.locateFile;
|
|
sIMS.emscriptenInstantiateWasm = args[0]?.instantiateWasm;
|
|
return originalInit(...args).then((EmscriptenModule) => {
|
|
sIMS.debugModule("sqlite3InitModule() sIMS =", sIMS);
|
|
sIMS.debugModule("sqlite3InitModule() EmscriptenModule =", EmscriptenModule);
|
|
const s = EmscriptenModule.runSQLite3PostLoadInit(sIMS, EmscriptenModule, !!ff.__isUnderTest);
|
|
sIMS.debugModule("sqlite3InitModule() sqlite3 =", s);
|
|
return s;
|
|
}).catch((e) => {
|
|
console.error("Exception loading sqlite3 module:", e);
|
|
throw e;
|
|
});
|
|
};
|
|
sIM.ready = originalInit.ready;
|
|
if (sIMS.moduleScript) {
|
|
let src = sIMS.moduleScript.src.split("/");
|
|
src.pop();
|
|
sIMS.scriptDir = src.join("/") + "/";
|
|
}
|
|
sIMS.debugModule("extern-post-js.c-pp.js sqlite3InitModuleState =", sIMS);
|
|
return sIM;
|
|
})();
|
|
sqlite3InitModule().then((sqlite3) => sqlite3.initWorker1API());
|
|
export {};
|