Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/compatible safari #4

Merged
merged 2 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 16 additions & 9 deletions demo/benchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,17 @@ const writeData = Array(1000)

let startTime = performance.now();
let testFileHandle = await root.getFileHandle(fileName, { create: true });
const writer1 = await testFileHandle.createWritable();
for (const d of writeData) {
await writer1.write(d);
if (testFileHandle.createWritable == null) {
updateCost('built-in-writer-cost', true);
} else {
const writer1 = await testFileHandle.createWritable();
for (const d of writeData) {
await writer1.write(d);
}
updateCost('built-in-writer-cost');
await writer1.truncate(0);
await writer1.close();
}
updateCost('built-in-writer-cost');

startTime = performance.now();
let tx = db.transaction('data', 'readwrite');
Expand All @@ -37,9 +43,6 @@ for (const d of writeData) {
await tx.done;
updateCost('indexeddb-write-cost');

await writer1.truncate(0);
await writer1.close();

startTime = performance.now();
const writer2 = await file(fileName).createWriter();
for (const d of writeData) {
Expand Down Expand Up @@ -97,8 +100,12 @@ updateCost('opfs-tools-read-all-cost');

getElById('status').remove();

function updateCost(id: string) {
getElById(id).textContent = `${~~(performance.now() - startTime)}ms`;
function updateCost(id: string, unsupported = false) {
if (unsupported) {
getElById(id).textContent = 'unsupported';
} else {
getElById(id).textContent = `${~~(performance.now() - startTime)}ms`;
}
}

// clear
Expand Down
77 changes: 5 additions & 72 deletions src/access-worker.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
interface FileSystemSyncAccessHandle {
read: (container: ArrayBuffer, opts: { at: number }) => number;
write: (data: ArrayBuffer | ArrayBufferView, opts?: { at: number }) => number;
flush: () => void;
close: () => void;
truncate: (newSize: number) => void;
getSize: () => number;
}
import { FileSystemSyncAccessHandle } from './common';
import OPFSWorker from './opfs-worker?worker&inline';

type Async<F> = F extends (...args: infer Params) => infer R
? (...args: Params) => Promise<R>
Expand All @@ -21,11 +15,10 @@ export type OPFSWorkerAccessHandle = {
};

export async function createOPFSAccess(
filePath: string,
fileHandle: FileSystemFileHandle
filePath: string
): Promise<OPFSWorkerAccessHandle> {
const postMsg = getWorkerMsger();
await postMsg('register', { filePath, fileHandle });
await postMsg('register', { filePath });
return {
read: async (offset, size) =>
(await postMsg('read', {
Expand Down Expand Up @@ -78,9 +71,7 @@ function getWorkerMsger() {
}

function create() {
const blob = new Blob([`(${opfsWorkerSetupStr})()`]);
const url = URL.createObjectURL(blob);
const worker = new Worker(url);
const worker = new OPFSWorker();

let cbId = 0;
let cbFns: Record<number, { resolve: Function; reject: Function }> = {};
Expand Down Expand Up @@ -126,61 +117,3 @@ function getWorkerMsger() {
};
}
}

const opfsWorkerSetupStr = ((): void => {
const fileAccesserMap: Record<string, FileSystemSyncAccessHandle> = {};

self.onmessage = async (e) => {
const { evtType, args } = e.data;

let accessHandle = fileAccesserMap[args.filePath];

try {
let returnVal;
const trans: Transferable[] = [];
if (evtType === 'register') {
accessHandle = await args.fileHandle.createSyncAccessHandle();
fileAccesserMap[args.filePath] = accessHandle;
} else if (evtType === 'close') {
accessHandle.close();
delete fileAccesserMap[args.filePath];
} else if (evtType === 'truncate') {
accessHandle.truncate(args.newSize);
} else if (evtType === 'write') {
const { data, opts } = e.data.args;
returnVal = accessHandle.write(data, opts);
} else if (evtType === 'read') {
const { offset, size } = e.data.args;
const buf = new ArrayBuffer(size);
const readLen = accessHandle.read(buf, { at: offset });
returnVal =
readLen === size
? buf
: // @ts-expect-error transfer support by chrome 114
buf.transfer?.(readLen) ?? buf.slice(0, readLen);
trans.push(returnVal);
} else if (evtType === 'getSize') {
returnVal = accessHandle.getSize();
} else if (evtType === 'flush') {
accessHandle.flush();
}

self.postMessage(
{
evtType: 'callback',
cbId: e.data.cbId,
returnVal,
},
// @ts-expect-error
trans
);
} catch (error) {
const err = error as Error;
self.postMessage({
evtType: 'throwError',
cbId: e.data.cbId,
errMsg: err.name + ': ' + err.message + '\n' + JSON.stringify(e.data),
});
}
};
}).toString();
9 changes: 9 additions & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
export interface FileSystemSyncAccessHandle {
read: (container: ArrayBuffer, opts: { at: number }) => number;
write: (data: ArrayBuffer | ArrayBufferView, opts?: { at: number }) => number;
flush: () => void;
close: () => void;
truncate: (newSize: number) => void;
getSize: () => number;
}

export function parsePath(path: string) {
if (path === '/') return { parent: null, name: '' };

Expand Down
7 changes: 1 addition & 6 deletions src/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,7 @@ export class OPFSFileWrap {

return (accPromise = new Promise(async (resolve, reject) => {
try {
const fh = (await getFSHandle(this.#path, {
create: true,
isFile: true,
})) as FileSystemFileHandle;

const accHandle = await createOPFSAccess(this.#path, fh);
const accHandle = await createOPFSAccess(this.#path);
resolve([
accHandle,
async () => {
Expand Down
63 changes: 63 additions & 0 deletions src/opfs-worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { FileSystemSyncAccessHandle, getFSHandle } from './common';

const fileAccesserMap: Record<string, FileSystemSyncAccessHandle> = {};

self.onmessage = async (e) => {
const { evtType, args } = e.data;

let accessHandle = fileAccesserMap[args.filePath];

try {
let returnVal;
const trans: Transferable[] = [];
if (evtType === 'register') {
const fh =
args.fileHandle ??
((await getFSHandle(args.filePath, {
create: true,
isFile: true,
})) as FileSystemFileHandle);
accessHandle = await fh.createSyncAccessHandle();
fileAccesserMap[args.filePath] = accessHandle;
} else if (evtType === 'close') {
accessHandle.close();
delete fileAccesserMap[args.filePath];
} else if (evtType === 'truncate') {
accessHandle.truncate(args.newSize);
} else if (evtType === 'write') {
const { data, opts } = e.data.args;
returnVal = accessHandle.write(data, opts);
} else if (evtType === 'read') {
const { offset, size } = e.data.args;
const buf = new ArrayBuffer(size);
const readLen = accessHandle.read(buf, { at: offset });
returnVal =
readLen === size
? buf
: // @ts-expect-error transfer support by chrome 114
buf.transfer?.(readLen) ?? buf.slice(0, readLen);
trans.push(returnVal);
} else if (evtType === 'getSize') {
returnVal = accessHandle.getSize();
} else if (evtType === 'flush') {
accessHandle.flush();
}

self.postMessage(
{
evtType: 'callback',
cbId: e.data.cbId,
returnVal,
},
// @ts-expect-error
trans
);
} catch (error) {
const err = error as Error;
self.postMessage({
evtType: 'throwError',
cbId: e.data.cbId,
errMsg: err.name + ': ' + err.message + '\n' + JSON.stringify(e.data),
});
}
};