Skip to content

Commit

Permalink
Merge pull request #4 from hughfenghen/feat/compatible-safari
Browse files Browse the repository at this point in the history
Feat/compatible safari
  • Loading branch information
hughfenghen committed Apr 16, 2024
2 parents b8d9886 + 1d5e9ca commit 0f0f71e
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 87 deletions.
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),
});
}
};

0 comments on commit 0f0f71e

Please sign in to comment.