UNPKG

taglib-wasm

Version:

TagLib-Wasm is the universal tagging library for TypeScript/JavaScript platforms: Browsers, Node.js, Deno, Bun, Cloudflare Workers, and Electron apps

203 lines (202 loc) 6.84 kB
import { joinPath as join } from "../utils/path.js"; const WASI_ESUCCESS = 0; const WASI_EBADF = 8; const WASI_EINVAL = 28; const WASI_ENOENT = 44; const WASI_ENOTCAPABLE = 76; const OFLAGS_CREAT = 1; const OFLAGS_TRUNC = 8; const RIGHTS_FD_WRITE = 1n << 6n; function createWasiImports(memory, config) { const fds = /* @__PURE__ */ new Map(); let nextFd = 3; for (const [virtualPath, realPath] of Object.entries(config.preopens)) { fds.set(nextFd, { type: "preopen", realPath, virtualPath }); nextFd++; } function getMemory() { return { u8: new Uint8Array(memory.buffer), dv: new DataView(memory.buffer) }; } function resolvePath(dirFd, pathPtr, pathLen) { const dir = fds.get(dirFd); if (dir?.type !== "preopen") return null; const { u8 } = getMemory(); const relPath = new TextDecoder().decode( u8.slice(pathPtr, pathPtr + pathLen) ); const normalized = relPath.replaceAll("\\", "/"); const segments = normalized.split("/"); if (segments.includes("..") || normalized.startsWith("/")) return null; return join(dir.realPath, normalized); } return { args_get: (_argv, _buf) => WASI_ESUCCESS, args_sizes_get: (argcPtr, bufSzPtr) => { const { dv } = getMemory(); dv.setUint32(argcPtr, 0, true); dv.setUint32(bufSzPtr, 0, true); return WASI_ESUCCESS; }, fd_close: (fd) => { const entry = fds.get(fd); if (!entry) return WASI_EBADF; if (entry.type === "file") { try { entry.file.close(); } catch { } } fds.delete(fd); return WASI_ESUCCESS; }, fd_fdstat_get: (fd, buf) => { const entry = fds.get(fd); if (!entry) return WASI_EBADF; const { dv } = getMemory(); const fileType = entry.type === "preopen" ? 3 : 4; dv.setUint8(buf, fileType); dv.setUint16(buf + 2, 0, true); dv.setBigUint64(buf + 8, 0xFFFFFFFFFFFFFFFFn, true); dv.setBigUint64(buf + 16, 0xFFFFFFFFFFFFFFFFn, true); return WASI_ESUCCESS; }, fd_fdstat_set_flags: (_fd, _flags) => WASI_ESUCCESS, fd_filestat_set_size: (fd, size) => { const entry = fds.get(fd); if (entry?.type !== "file") return WASI_EBADF; try { entry.file.truncateSync(Number(size)); return WASI_ESUCCESS; } catch { return WASI_EINVAL; } }, fd_prestat_get: (fd, buf) => { const entry = fds.get(fd); if (entry?.type !== "preopen") return WASI_EBADF; const { dv } = getMemory(); const pathBytes = new TextEncoder().encode(entry.virtualPath); dv.setUint32(buf, 0, true); dv.setUint32(buf + 4, pathBytes.length, true); return WASI_ESUCCESS; }, fd_prestat_dir_name: (fd, pathPtr, pathLen) => { const entry = fds.get(fd); if (entry?.type !== "preopen") return WASI_EBADF; const { u8 } = getMemory(); const pathBytes = new TextEncoder().encode(entry.virtualPath); u8.set(pathBytes.subarray(0, pathLen), pathPtr); return WASI_ESUCCESS; }, fd_read: (fd, iovsPtr, iovsLen, nreadPtr) => { const entry = fds.get(fd); if (entry?.type !== "file") return WASI_EBADF; const { u8, dv } = getMemory(); let totalRead = 0; for (let i = 0; i < iovsLen; i++) { const bufPtr = dv.getUint32(iovsPtr + i * 8, true); const bufLen = dv.getUint32(iovsPtr + i * 8 + 4, true); const target = u8.subarray(bufPtr, bufPtr + bufLen); const n = entry.file.readSync(target); if (n === null) break; totalRead += n; if (n < bufLen) break; } dv.setUint32(nreadPtr, totalRead, true); return WASI_ESUCCESS; }, fd_seek: (fd, offset, whence, newoffsetPtr) => { const entry = fds.get(fd); if (entry?.type !== "file") return WASI_EBADF; try { const newPos = entry.file.seekSync(Number(offset), whence); const { dv } = getMemory(); dv.setBigInt64(newoffsetPtr, BigInt(newPos), true); return WASI_ESUCCESS; } catch { return WASI_EINVAL; } }, fd_write: (fd, iovsPtr, iovsLen, nwrittenPtr) => { const { u8, dv } = getMemory(); let totalWritten = 0; for (let i = 0; i < iovsLen; i++) { const bufPtr = dv.getUint32(iovsPtr + i * 8, true); const bufLen = dv.getUint32(iovsPtr + i * 8 + 4, true); const data = u8.subarray(bufPtr, bufPtr + bufLen); if (fd === 1) { config.stdout?.(data); totalWritten += bufLen; } else if (fd === 2) { config.stderr?.(data); totalWritten += bufLen; } else { const entry = fds.get(fd); if (entry?.type !== "file") return WASI_EBADF; totalWritten += entry.file.writeSync(data); } } dv.setUint32(nwrittenPtr, totalWritten, true); return WASI_ESUCCESS; }, path_open: (dirFd, _dirflags, pathPtr, pathLen, oflags, _rightsBase, _rightsInheriting, _fdflags, openedFdPtr) => { const realPath = resolvePath(dirFd, pathPtr, pathLen); if (!realPath) return WASI_ENOTCAPABLE; try { const wantWrite = (_rightsBase & RIGHTS_FD_WRITE) !== 0n || (oflags & (OFLAGS_CREAT | OFLAGS_TRUNC)) !== 0; const options = { read: true, write: wantWrite, create: (oflags & OFLAGS_CREAT) !== 0, truncate: (oflags & OFLAGS_TRUNC) !== 0 }; const file = config.fs.openSync(realPath, options); const fd = nextFd++; fds.set(fd, { type: "file", file, path: realPath }); const { dv } = getMemory(); dv.setUint32(openedFdPtr, fd, true); return WASI_ESUCCESS; } catch (e) { if (config.fs.isNotFoundError(e)) return WASI_ENOENT; return WASI_EINVAL; } }, environ_get: (_environ, _buf) => WASI_ESUCCESS, environ_sizes_get: (countPtr, bufSzPtr) => { const { dv } = getMemory(); dv.setUint32(countPtr, 0, true); dv.setUint32(bufSzPtr, 0, true); return WASI_ESUCCESS; }, clock_time_get: (_id, _precision, timePtr) => { const { dv } = getMemory(); dv.setBigUint64(timePtr, BigInt(Date.now()) * 1000000n, true); return WASI_ESUCCESS; }, random_get: (bufPtr, bufLen) => { const buf = new Uint8Array(memory.buffer, bufPtr, bufLen); crypto.getRandomValues(buf); return WASI_ESUCCESS; }, proc_exit: (code) => { throw new Error(`WASI proc_exit called: code ${code}`); }, [Symbol.dispose]: () => { for (const [fd, entry] of fds) { if (entry.type === "file") { try { entry.file.close(); } catch { } } fds.delete(fd); } } }; } export { createWasiImports };