Forked SQLite deno WASM lib
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"imports": {
|
||||
"../build/vfs.js": "../browser/vfs.js"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { DB } from "../src/db.ts";
|
||||
import { loadFile, writeFile } from "./vfs.js";
|
||||
import { compile, instantiateBrowser } from "../build/sqlite.js";
|
||||
|
||||
export { SqliteError } from "../src/error.ts";
|
||||
export { Status } from "../src/constants.ts";
|
||||
|
||||
const hasCompiled = compile();
|
||||
|
||||
/**
|
||||
* Opens a database with the given name. If `file` is
|
||||
* not provided or `:memory:`, an in-memory database
|
||||
* is returned which will not persist after the database
|
||||
* is closed.
|
||||
*/
|
||||
export async function open(file?: string): Promise<DB> {
|
||||
if (file != null && file !== ":memory:") await loadFile(file);
|
||||
await hasCompiled;
|
||||
await instantiateBrowser();
|
||||
return new DB(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite a given file with arbitrary data. This can be used
|
||||
* to import a database which can later be opened.
|
||||
*/
|
||||
export async function write(file: string, data: Uint8Array): Promise<void> {
|
||||
await writeFile(file, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the data currently stored for a given file. This can be used
|
||||
* to export a database which has been created or modified.
|
||||
*/
|
||||
export async function read(file: string): Promise<Uint8Array | null> {
|
||||
const buffer = await loadFile(file);
|
||||
return buffer?.toUint8Array()?.slice();
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
import { getStr } from "../src/wasm.ts";
|
||||
|
||||
const DB_NAME = "sqlitevfs";
|
||||
const LOADED_FILES = new Map();
|
||||
const OPEN_FILES = new Map();
|
||||
|
||||
function nextRid() {
|
||||
const rid = (nextRid?.LAST_RID ?? 0) + 1;
|
||||
nextRid.LAST_RID = rid;
|
||||
return rid;
|
||||
}
|
||||
|
||||
function getOpenFile(rid) {
|
||||
if (!OPEN_FILES.has(rid)) {
|
||||
throw new Error(`Resource ID ${rid} does not exist.`);
|
||||
}
|
||||
return OPEN_FILES.get(rid);
|
||||
}
|
||||
|
||||
const MIN_GROW_BYTES = 2048;
|
||||
const MAX_GROW_BYTES = 65536;
|
||||
|
||||
class Buffer {
|
||||
constructor(data) {
|
||||
this._data = data ?? new Uint8Array();
|
||||
this._size = this._data.length;
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
read(offset, buffer) {
|
||||
if (offset >= this._size) return 0;
|
||||
const toCopy = this._data.subarray(
|
||||
offset,
|
||||
Math.min(this._size, offset + buffer.length),
|
||||
);
|
||||
buffer.set(toCopy);
|
||||
return toCopy.length;
|
||||
}
|
||||
|
||||
reserve(capacity) {
|
||||
if (this._data.length >= capacity) return;
|
||||
const neededBytes = capacity - this._data.length;
|
||||
const growBy = Math.min(
|
||||
MAX_GROW_BYTES,
|
||||
Math.max(MIN_GROW_BYTES, this._data.length),
|
||||
);
|
||||
const newArray = new Uint8Array(
|
||||
this._data.length + Math.max(growBy, neededBytes),
|
||||
);
|
||||
newArray.set(this._data);
|
||||
this._data = newArray;
|
||||
}
|
||||
|
||||
write(offset, buffer) {
|
||||
this.reserve(offset + buffer.length);
|
||||
this._data.set(buffer, offset);
|
||||
this._size = Math.max(this._size, offset + buffer.length);
|
||||
return buffer.length;
|
||||
}
|
||||
|
||||
truncate(size) {
|
||||
this._size = size;
|
||||
}
|
||||
|
||||
toUint8Array() {
|
||||
return this._data.subarray(0, this._size);
|
||||
}
|
||||
}
|
||||
|
||||
const indexedDB = window.indexedDB || window.mozIndexedDB ||
|
||||
window.webkitIndexedDB || window.msIndexedDB || window.shimIndexedDB;
|
||||
|
||||
// Web browser indexedDB database
|
||||
const database = new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(DB_NAME, 1);
|
||||
request.onupgradeneeded = () =>
|
||||
request.result.createObjectStore("files", { keyPath: "name" });
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
|
||||
export async function loadFile(fileName) {
|
||||
const db = await database;
|
||||
const file = await new Promise((resolve, reject) => {
|
||||
const store = db.transaction("files", "readonly").objectStore("files");
|
||||
const request = store.get(fileName);
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
if (file != null && !LOADED_FILES.has(fileName)) {
|
||||
const buffer = new Buffer(file.data);
|
||||
LOADED_FILES.set(fileName, buffer);
|
||||
return buffer;
|
||||
} else if (LOADED_FILES.has(fileName)) {
|
||||
return LOADED_FILES.get(fileName);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function syncFile(fileName, data) {
|
||||
const db = await database;
|
||||
await new Promise((resolve, reject) => {
|
||||
const store = db.transaction("files", "readwrite").objectStore("files");
|
||||
const request = store.put({ name: fileName, data });
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
async function deleteFile(fileName) {
|
||||
const db = await database;
|
||||
await new Promise((resolve, reject) => {
|
||||
const store = db.transaction("files", "readwrite").objectStore("files");
|
||||
const request = store.delete(fileName);
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
export async function writeFile(fileName, data) {
|
||||
await syncFile(fileName, data);
|
||||
if (LOADED_FILES.has(fileName)) {
|
||||
const buffer = LOADED_FILES.get(fileName);
|
||||
buffer.truncate(0);
|
||||
buffer.write(0, data);
|
||||
}
|
||||
}
|
||||
|
||||
// Closure to return an environment that links
|
||||
// the current wasm context. This is a modified
|
||||
// version, suitable for use within browsers.
|
||||
export default function env(inst) {
|
||||
const env = {
|
||||
js_print: (str_ptr) => {
|
||||
const text = getStr(inst.exports, str_ptr);
|
||||
console.log(text[text.length - 1] === "\n" ? text.slice(0, -1) : text);
|
||||
},
|
||||
js_open: (path_ptr, mode, _flags) => {
|
||||
if (mode === 1 /* temp file */) {
|
||||
const rid = nextRid();
|
||||
OPEN_FILES.set(rid, { path: null, buffer: new Buffer() });
|
||||
return rid;
|
||||
} else if (mode === 0 /* regular file */) {
|
||||
const path = getStr(inst.exports, path_ptr);
|
||||
const buffer = LOADED_FILES.get(path) ?? new Buffer();
|
||||
if (!LOADED_FILES.has(path)) LOADED_FILES.set(path, buffer);
|
||||
const rid = nextRid();
|
||||
OPEN_FILES.set(rid, { path, buffer });
|
||||
return rid;
|
||||
}
|
||||
},
|
||||
js_close: (rid) => {
|
||||
OPEN_FILES.delete(rid);
|
||||
},
|
||||
js_delete: (path_ptr) => {
|
||||
const path = getStr(inst.exports, path_ptr);
|
||||
LOADED_FILES.delete(path);
|
||||
deleteFile(path);
|
||||
},
|
||||
js_read: (rid, buffer_ptr, offset, amount) => {
|
||||
const buffer = new Uint8Array(
|
||||
inst.exports.memory.buffer,
|
||||
buffer_ptr,
|
||||
amount,
|
||||
);
|
||||
const file = getOpenFile(rid);
|
||||
return file.buffer.read(offset, buffer);
|
||||
},
|
||||
js_write: (rid, buffer_ptr, offset, amount) => {
|
||||
const buffer = new Uint8Array(
|
||||
inst.exports.memory.buffer,
|
||||
buffer_ptr,
|
||||
amount,
|
||||
);
|
||||
const file = getOpenFile(rid);
|
||||
return file.buffer.write(offset, buffer);
|
||||
},
|
||||
js_truncate: (rid, size) => {
|
||||
getOpenFile(rid).buffer.truncate(size);
|
||||
},
|
||||
js_sync: (rid) => {
|
||||
const file = getOpenFile(rid);
|
||||
if (file.path != null) syncFile(file.path, file.buffer.toUint8Array());
|
||||
},
|
||||
js_size: (rid) => {
|
||||
return getOpenFile(rid).buffer.size;
|
||||
},
|
||||
js_lock: (_rid, _exclusive) => {},
|
||||
js_unlock: (_rid) => {},
|
||||
js_time: () => {
|
||||
return Date.now();
|
||||
},
|
||||
js_timezone: () => {
|
||||
return (new Date()).getTimezoneOffset();
|
||||
},
|
||||
js_exists: (path_ptr) => {
|
||||
const path = getStr(inst.exports, path_ptr);
|
||||
return LOADED_FILES.has(path) ? 1 : 0;
|
||||
},
|
||||
js_access: (_path_ptr) => 1,
|
||||
};
|
||||
|
||||
return { env };
|
||||
}
|
||||
Reference in New Issue
Block a user