modularize playground (prep for persistent/modular file handling)

This commit is contained in:
2024-12-07 16:58:10 -08:00
parent 421f5ea208
commit ba70845c09
11 changed files with 673 additions and 193 deletions

View File

@@ -4,7 +4,7 @@
- [x] SortedMap.newt issue in `where`
- [x] fix "insufficient patterns", wire in M or Either String
- [ ] Matching _,_ when Maybe is expected should be an error
- [ ] error for repeated names on LHS
- [ ] error for non-linear pattern
- [ ] typeclass dependencies
- need to flag internal functions to not search (or flag functions for search). I need to decide on syntax for this.
- don't search functions that are currently being defined. This is subtle... We do want to recurse in bind, we don't want to do that for the isEq function. Maybe something idris like.

View File

@@ -3,9 +3,8 @@ mkdir -p public
echo build monaco worker
esbuild --bundle node_modules/monaco-editor/esm/vs/editor/editor.worker.js > public/workerMain.js
echo build newt worker
esbuild src/worker.ts > public/worker.js
esbuild src/worker.ts --bundle --format=esm > public/worker.js
echo copy newt
cp ../build/exec/newt.js public
# cat ../build/exec/newt.js |grep -v '^#'>> public/worker.js
cp -r samples/* public
# esbuild --minify ../build/exec/newt.min.js > public/newt.js
cp -r static/* public
(cd samples && zip -r ../public/files.zip .)

254
playground/src/emul.ts Normal file
View File

@@ -0,0 +1,254 @@
import { ZipFile } from "./zipfile";
class Buffer extends DataView {
static alloc(n: number) {
return new Buffer(new Uint8Array(n).buffer);
}
indexOf(n: number) {
return new Uint8Array(this.buffer).indexOf(n);
}
get length() {
return this.byteLength;
}
slice(start: number, end: number) {
return new Buffer(this.buffer.slice(start, end));
}
readUInt8(i: number) {
return this.getUint8(i);
}
writeUInt8(val: number, i: number) {
this.setUint8(i, val);
}
write(value: string, start: number, len: number, enc: string) {
// console.log("write", value, start, len, enc);
let buf = new TextEncoder().encode(value);
let ss = 0;
let se = Math.min(len, buf.length);
let ts = start;
for (; ss < se; ss++, ts++) this.setInt8(ts, buf[ss]);
shim.process.__lasterr.errno = 0;
return se;
}
readDoubleLE(i: number) {
return this.getFloat64(i, true);
}
readInt32LE(i: number) {
return this.getInt32(i, true);
}
writeInt32LE(val: number, i: number) {
return this.setInt32(i, val, true);
}
copy(target: Buffer, ts: number, ss: number, se: number) {
for (; ss < se; ss++, ts++) target.setInt8(ts, this.getInt8(ss));
}
static concat(bufs: Buffer[]) {
let size = bufs.reduce((a, b) => (a += b.byteLength), 0);
let rval = Buffer.alloc(size);
let off = 0;
for (let buf of bufs) {
const view = new Int8Array(rval.buffer);
view.set(new Uint8Array(buf.buffer), off);
off += buf.byteLength;
}
return rval;
}
toString() {
return new TextDecoder().decode(this);
}
}
export interface Handle {
name: string;
mode: string;
pos: number;
buf: Uint8Array;
}
interface Process {
platform: string;
stdout: {
write(s: string): void;
};
argv: string[];
exit(_: number): void;
cwd(): string;
env: Record<string, string>;
__lasterr: { errno: number };
}
export interface NodeShim {
stdout: string;
archive?: ZipFile;
process: Process;
files: Record<string, Uint8Array>;
fds: Handle[];
tty: {
isatty(): number;
};
os: {
platform(): string;
};
fs: any;
}
export let shim: NodeShim = {
// these three and process are poked at externally
archive: undefined,
stdout: "",
files: {},
fds: [],
tty: {
isatty() {
return 0;
},
},
os: {
platform() {
return "linux";
},
},
fs: {
// TODO - Idris is doing readdir, we should implement that
opendirSync(name: string) {
let fd = shim.fds.findIndex((x) => !x);
if (fd < 0) fd = shim.fds.length;
console.log("openDir", name);
shim.process.__lasterr.errno = 0;
return fd;
},
mkdirSync(name: string) {
console.log("mkdir", name);
shim.process.__lasterr.errno = 0;
return 0;
},
openSync(name: string, mode: string) {
console.log("open", name, mode);
let te = new TextEncoder();
let fd = shim.fds.findIndex((x) => !x);
if (fd < 0) fd = shim.fds.length;
let buf: Uint8Array;
let pos = 0;
if (mode == "w") {
buf = new Uint8Array(0);
} else {
// TODO, we need to involve localStorage when the window does multiple files and persists
if (shim.files[name]) {
buf = shim.files[name];
} else if (shim.archive?.entries[name]) {
// keep a copy of the uncompressed version for speed
buf = shim.files[name] = shim.archive.getData(name);
} else {
shim.process.__lasterr.errno = 1;
throw new Error(`${name} not found`);
}
}
shim.process.__lasterr.errno = 0;
shim.fds[fd] = { buf, pos, mode, name };
// we'll mutate the pointer as stuff is read
return fd;
},
writeSync(fd: number, line: string | Buffer) {
try {
let handle = shim.fds[fd];
if (!handle) throw new Error(`bad fd ${fd}`);
let buf2: ArrayBuffer;
if (typeof line === "string") {
buf2 = new TextEncoder().encode(line);
let newbuf = new Uint8Array(handle.buf.byteLength + buf2.byteLength);
newbuf.set(new Uint8Array(handle.buf));
newbuf.set(new Uint8Array(buf2), handle.buf.byteLength);
handle.buf = newbuf;
shim.process.__lasterr.errno = 0;
} else if (line instanceof Buffer) {
let start = arguments[2];
let len = arguments[3];
buf2 = line.buffer.slice(start, start + len);
let newbuf = new Uint8Array(handle.buf.byteLength + buf2.byteLength);
newbuf.set(new Uint8Array(handle.buf));
newbuf.set(new Uint8Array(buf2), handle.buf.byteLength);
handle.buf = newbuf;
shim.process.__lasterr.errno = 0;
return len;
} else {
debugger;
throw new Error(`write ${typeof line} not implemented`);
}
} catch (e) {
debugger;
throw e;
}
},
chmodSync(fn: string, mode: number) {},
fstatSync(fd: number) {
let hand = shim.fds[fd];
return { size: hand.buf.byteLength };
},
readSync(fd: number, buf: Buffer, start: number, len: number) {
let hand = shim.fds[fd];
let avail = hand.buf.length - hand.pos;
let rd = Math.min(avail, len);
let src = hand.buf;
let dest = new Uint8Array(buf.buffer);
for (let i = 0; i < rd; i++) dest[start + i] = src[hand.pos++];
return rd;
},
closeSync(fd: number) {
let handle = shim.fds[fd];
// console.log("close", handle.name);
if (handle.mode == "w") {
shim.files[handle.name] = handle.buf;
}
delete shim.fds[fd];
},
},
process: {
platform: "linux",
argv: ["", ""],
stdout: {
// We'll want to replace this one
write(s) {
console.log("*", s);
shim.stdout += s;
},
},
exit(v: number) {
console.log("exit", v);
},
cwd() {
return "";
},
env: {
NO_COLOR: "true",
IDRIS2_CG: "javascript",
IDRIS2_PREFIX: "",
},
__lasterr: {
errno: 0,
},
// stdin: { fd: 0 },
},
};
// Spy on Idris' calls to see what we need to fill in
shim.fs = new Proxy(shim.fs, {
get(target, prop, receiver) {
if (prop in target) {
return (target as any)[prop];
}
let err = new Error(`IMPLEMENT fs.${String(prop)}`);
// idris support eats the exception
console.error(err);
throw err;
},
});
// we intercept require to return our fake node modules
declare global {
interface Window {
require: (x: string) => any;
process: Process;
}
}
const requireStub: any = (x: string) => (shim as any)[x];
self.require = requireStub;
self.process = shim.process;

View File

@@ -3,15 +3,8 @@ export {};
declare global {
// typescript doesn't know worker.ts is a worker
function importScripts(...scripts: string[]): void;
interface Process {
platform: string;
stdout: {
write(s: string): void;
};
argv: string[];
exit(_: number): void;
}
let files: Record<string, string>;
let process: Process;
// let files: Record<string, string>;
// let process: Process;
let newtMain: () => unknown;
}

View File

@@ -2,8 +2,18 @@ import { effect, signal } from "@preact/signals";
import { newtConfig, newtTokens } from "./monarch.ts";
import * as monaco from "monaco-editor";
import { useEffect, useRef, useState } from "preact/hooks";
import { h, render, VNode } from "preact";
import { h, render } from "preact";
import { ChangeEvent } from "preact/compat";
import { archive, preload } from "./preload.ts";
import { CompileReq, CompileRes } from "./types.ts";
// editor.(createModel / setModel / getModels) to switch files
// TODO - remember files and allow switching?
// static zip filesystem with user changes overlaid via localStorage
// download individual files (we could use the cheap compression from the pdflib or no compression to make zip download)
// would need way to reset/delete
interface FC {
file: string;
@@ -31,22 +41,27 @@ monaco.languages.registerDefinitionProvider("newt", {
if (!topData) return;
// HACK - we don't know our filename which was generated from `module` decl, so
// assume the last context entry is in our file.
let last = topData.context[topData.context.length-1]
let file = last.fc.file
let last = topData.context[topData.context.length - 1];
let file = last.fc.file;
const info = model.getWordAtPosition(position);
if (!info) return;
let entry = topData.context.find((entry) => entry.name === info.word);
// we can't switch files at the moment
if (!entry || entry.fc.file !== file) return
let lineNumber = entry.fc.line + 1
let column = entry.fc.col + 1
let word = model.getWordAtPosition({lineNumber, column})
let range = new monaco.Range(lineNumber, column, lineNumber, column)
if (!entry || entry.fc.file !== file) return;
let lineNumber = entry.fc.line + 1;
let column = entry.fc.col + 1;
let word = model.getWordAtPosition({ lineNumber, column });
let range = new monaco.Range(lineNumber, column, lineNumber, column);
if (word) {
range = new monaco.Range(lineNumber, word.startColumn, lineNumber, word.endColumn)
range = new monaco.Range(
lineNumber,
word.startColumn,
lineNumber,
word.endColumn
);
}
return { uri: model.uri,range}
return { uri: model.uri, range };
},
});
monaco.languages.registerHoverProvider("newt", {
@@ -68,7 +83,7 @@ monaco.languages.registerHoverProvider("newt", {
},
});
const newtWorker = new Worker("worker.js");
let postMessage = (msg: any) => newtWorker.postMessage(msg);
let postMessage = (msg: CompileReq) => newtWorker.postMessage(msg);
// Safari/MobileSafari have small stacks in webworkers.
if (navigator.vendor.includes("Apple")) {
@@ -117,20 +132,29 @@ function setOutput(output: string) {
state.output.value = output;
}
window.onmessage = (ev) => {
interface ConsoleList {
messages: string[]
}
interface ConsoleItem {
message: string
}
type WinMessage = CompileRes | ConsoleList | ConsoleItem
window.onmessage = (ev: MessageEvent<WinMessage>) => {
console.log("window got", ev.data);
if (ev.data.messages) state.messages.value = ev.data.messages;
if (ev.data.message) {
if ('messages' in ev.data) state.messages.value = ev.data.messages;
if ('message' in ev.data) {
state.messages.value = [...state.messages.value, ev.data.message];
}
// safari callback
if (ev.data.output !== undefined) {
if ('output' in ev.data) {
setOutput(ev.data.output);
state.javascript.value = ev.data.javascript;
}
};
newtWorker.onmessage = (ev) => {
newtWorker.onmessage = (ev: MessageEvent<CompileRes>) => {
setOutput(ev.data.output);
state.javascript.value = ev.data.javascript;
};
@@ -169,9 +193,10 @@ if (window.matchMedia) {
}
async function loadFile(fn: string) {
if (fn) {
const res = await fetch(fn);
const text = await res.text();
await preload;
let data = archive?.getData(fn);
if (data) {
let text = new TextDecoder().decode(data);
state.editor.value!.setValue(text);
} else {
state.editor.value!.setValue("module Main\n");

21
playground/src/preload.ts Normal file
View File

@@ -0,0 +1,21 @@
import {ZipFile} from './zipfile'
export let archive: ZipFile | undefined;
export let preload = (async function () {
// We pull down an archive of .ttc and support shim.files
try {
let res = await self.fetch("files.zip");
if (res.status === 200) {
let data = await res.arrayBuffer();
archive = new ZipFile(new Uint8Array(data));
let entries = archive.entries;
let count = Object.keys(entries).length;
console.log(`preloaded ${count} files`);
} else {
console.error(
`fetch of files.zip got status ${res.status}: ${res.statusText}`
);
}
} catch (e) {
console.error("preload failed", e);
}
})();

9
playground/src/types.ts Normal file
View File

@@ -0,0 +1,9 @@
export interface CompileReq {
src: string;
}
export interface CompileRes {
output: string
javascript: string
duration: number
}

View File

@@ -1,165 +1,39 @@
class Buffer extends ArrayBuffer {
static alloc(n: number) {
return new Buffer(n);
}
indexOf(n: number) {
let view = new Uint8Array(this);
return view.indexOf(n);
}
import { shim } from "./emul";
import { archive, preload } from "./preload";
import { CompileReq, CompileRes } from "./types";
static concat(bufs: Buffer[]) {
let size = bufs.reduce((a, b) => (a += b.byteLength), 0);
let rval = new Buffer(size);
let view = new Uint8Array(rval);
let off = 0;
for (let buf of bufs) {
view.set(new Uint8Array(buf), off);
off += buf.byteLength;
}
return rval;
}
toString() {
return new TextDecoder().decode(this);
}
}
let files: Record<string, string> = {};
interface Handle {
name: string;
mode: string;
pos: number;
buf: Buffer;
}
let fds: Handle[] = [];
let shim: any = {
os: {
platform() {
return "linux";
},
},
fs: {
openSync(name: string, mode: string) {
console.log("open", name, arguments);
let te = new TextEncoder();
let fd = fds.findIndex((x) => !x);
if (fd < 0) fd = fds.length;
let buf;
let pos = 0;
if (mode == "w") {
buf = new Buffer(0);
} else {
if (!files[name]) throw new Error(`${name} not found`);
buf = te.encode(files[name]);
}
fds[fd] = { buf, pos, mode, name };
// we'll mutate the pointer as stuff is read
return fd;
},
writeSync(fd: number, line: string) {
if (typeof line !== "string") throw new Error("not implemented");
let handle = fds[fd];
if (!handle) throw new Error(`bad fd ${fd}`)
let buf2 = new TextEncoder().encode(line);
handle.buf = Buffer.concat([handle.buf, buf2])
},
chmodSync(fn: string, mode: number) { },
readSync(fd: number, buf: Buffer, start: number, len: number) {
let hand = fds[fd];
let avail = hand.buf.byteLength - hand.pos;
let rd = Math.min(avail, len);
let src = new Uint8Array(hand.buf);
let dest = new Uint8Array(buf);
for (let i = 0; i < rd; i++) dest[start + i] = src[hand.pos++];
return rd;
},
closeSync(fd: number) {
let handle = fds[fd];
if (handle.mode == "w") {
files[handle.name] = new TextDecoder().decode(handle.buf);
}
delete fds[fd];
},
},
};
// Spy on Idris' calls to see what we need to fill in
shim.fs = new Proxy(shim.fs, {
get(target, prop, receiver) {
if (prop in target) {
return (target as any)[prop];
}
throw new Error(`implement fs.${String(prop)}`)
},
});
const process: Process = {
platform: "linux",
argv: ["", ""],
stdout: {
// We'll want to replace this one
write: console.log,
},
exit(v: number) {
console.log("exit", v);
},
// stdin: { fd: 0 },
};
const require = (x: string) => shim[x];
// Maybe the shim goes here and we append newt...
let stdout = ''
// We'll want to collect and put info in the monaco
process.stdout.write = (s) => {
stdout += s
};
// hack for now
const preload = [
"Prelude.newt",
"Web.newt",
"aoc2023/day1/eg.txt",
"aoc2023/day1/eg2.txt",
]
const handleMessage = async function (e) {
console.log('message for you sir', e.data)
for (let fn of preload) {
if (!files[fn]) {
console.log('preload', fn)
let res = await fetch(fn)
let text = await res.text()
files[fn] = text
}
}
let {src} = e.data
let module = 'Main'
let m = src.match(/module (\w+)/)
if (m) module = m[1]
let fn = `${module}.newt`
process.argv = ["", "", fn, "-o", "out.js", "--top"];
console.log("args", process.argv);
files[fn] = src;
files['out.js'] = 'No JS output';
stdout = ''
const start = +new Date()
const handleMessage = async function (ev: {data: CompileReq}) {
console.log("message", ev.data);
await preload;
shim.archive = archive;
let { src } = ev.data;
let module = "Main";
let m = src.match(/module (\w+)/);
if (m) module = m[1];
let fn = `${module}.newt`;
const outfile = "out.js";
shim.process.argv = ["", "", fn, "-o", outfile, "--top"];
console.log("Using args", shim.process.argv);
shim.files[fn] = new TextEncoder().encode(src);
shim.files[outfile] = new TextEncoder().encode("No JS output");
shim.stdout = "";
const start = +new Date();
try {
newtMain();
} catch (e) {
// make it clickable
console.error(e)
// make it visable
stdout += '\n' + String(e)
// make it clickable in console
console.error(e);
// make it visable on page
shim.stdout += "\n" + String(e);
}
let duration = +new Date() - start
console.log(`process ${fn} in ${duration} ms`)
let javascript = files['out.js']
let output = stdout
sendResponse({javascript, output, duration})
}
let sendResponse = postMessage
onmessage = handleMessage
let duration = +new Date() - start;
console.log(`shim.process ${fn} in ${duration} ms`);
let javascript = new TextDecoder().decode(shim.files[outfile]);
let output = shim.stdout;
sendResponse({ javascript, output, duration });
};
importScripts('newt.js')
// hooks for worker.html to override
let sendResponse : (_: CompileRes) => void = postMessage;
onmessage = handleMessage;
importScripts("newt.js");

305
playground/src/zipfile.ts Normal file
View File

@@ -0,0 +1,305 @@
// I wrote this inflate years ago, seems to work for zip
class BitReader {
pos = 0;
bits = 0;
acc = 0;
len: number;
data: Uint8Array;
constructor(data: Uint8Array) {
this.data = data;
this.len = data.length;
}
read(bits: number) {
while (this.bits < bits) {
if (this.pos >= this.len) throw "EOF";
this.acc |= this.data[this.pos++] << this.bits;
this.bits += 8;
}
let rval = this.acc & ((1 << bits) - 1);
this.acc >>= bits;
this.bits -= bits;
return rval;
}
read8() {
if (this.pos > this.len) throw "EOF";
// flush
if (this.bits > 0) {
this.bits = 0;
this.acc = 0;
}
return this.data[this.pos++];
}
read16() {
let rval = this.read8() * 256 + this.read8();
return rval;
}
}
class HuffDic {
limit: number[];
codes: number[];
base: number[];
constructor(lengths: number[]) {
let counts = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
let min = 0;
let max = 0;
for (let i = 0; i < lengths.length; i++) {
let len = lengths[i];
if (len != 0) {
if (len < min || min == 0) min = len;
if (len > max) max = len;
counts[len]++;
}
}
this.base = [];
this.limit = [
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
];
let code = 0;
let seq = 0;
let next_code = [];
for (let i = min; i <= max; i++) {
let n = counts[i];
next_code[i] = code;
this.base[i] = code - seq;
code += n;
seq += n;
this.limit[i] = code - 1;
code <<= 1;
}
this.codes = [];
for (let i = 0; i < lengths.length; i++) {
let n = lengths[i];
if (n != 0) {
code = next_code[n];
next_code[n]++;
if (!this.base[n]) this.base[n] = 0;
seq = code - this.base[n];
this.codes[seq] = i;
}
}
}
readSymbol(r: BitReader) {
let v = 0;
let l = 0;
let offset = 0;
for (let i = 1; i < this.limit.length; i++) {
v <<= 1;
v |= r.read(1);
let limit = this.limit[i];
if (v <= limit) {
return this.codes[v - this.base[i]];
}
}
throw "eHUFF";
}
}
let staticHuff: HuffDic;
let distHuff: HuffDic;
{
let tmp = [];
for (let i = 0; i < 144; i++) tmp[i] = 8;
for (let i = 144; i < 256; i++) tmp[i] = 9;
for (let i = 256; i < 280; i++) tmp[i] = 7;
for (let i = 280; i < 288; i++) tmp[i] = 8;
staticHuff = new HuffDic(tmp);
tmp = [];
for (let i = 0; i < 30; i++) {
tmp[i] = 5;
}
distHuff = new HuffDic(tmp);
}
function inflate(input: Uint8Array) {
let r = new BitReader(input);
let out = new Uint8Array(65536);
let pos = 0;
const push = (b: number) => {
if (pos + 10 > out.length) {
const tmp = new Uint8Array(out.length * 1.5);
tmp.set(out);
out = tmp;
}
out[pos++] = b;
};
let fin = 0;
while (!fin) {
fin = r.read(1);
let btype = r.read(2);
let huff2;
let huff3;
if (btype == 0) {
let len = r.read16();
let nlen = r.read16();
for (let i = 0; i < len; i++) push(r.read8());
} else if (btype == 1) {
// fixed huffman
huff2 = staticHuff;
huff3 = distHuff;
} else if (btype == 2) {
// dynamic huffman
let hlit = r.read(5) + 257;
let hdist = r.read(5) + 1;
let hclen = r.read(4) + 4;
let lengths: number[] = [];
for (let i = 0; i < 19; i++) lengths[i] = 0;
let xx = [
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
];
for (let i = 0; i < hclen; i++) {
let t = r.read(3);
lengths[xx[i]] = t;
}
let huff = new HuffDic(lengths);
lengths = [];
while (true) {
let k = huff.readSymbol(r);
if (k < 16) {
lengths.push(k);
} else if (k == 16) {
let count = r.read(2) + 3;
if (lengths.length == 0) throw new Error("no lengths?");
for (; count > 0; count--) lengths.push(lengths[lengths.length - 1]);
} else if (k == 17) {
let count = r.read(3) + 3;
for (; count > 0; count--) lengths.push(0);
} else if (k == 18) {
let count = r.read(7) + 11;
for (; count > 0; count--) lengths.push(0);
}
if (lengths.length >= hlit + hdist) break;
}
huff2 = new HuffDic(lengths.slice(0, hlit));
huff3 = new HuffDic(lengths.slice(hlit));
} else {
throw new Error("btype " + btype);
}
if (huff2) {
while (true) {
let k = huff2.readSymbol(r);
let len = 0;
let n = 0; // extra bits
if (k < 256) {
push(k);
continue;
} else if (k == 256) {
// End of block
break;
} else if (k < 265) {
len = k - 257 + 3;
n = 0;
} else if (k < 269) {
len = (k - 265) * 2 + 11;
n = 1;
} else if (k < 273) {
len = (k - 269) * 4 + 19;
n = 2;
} else if (k < 277) {
len = (k - 273) * 8 + 35;
n = 3;
} else if (k < 281) {
len = (k - 277) * 16 + 67;
n = 4;
} else if (k < 285) {
len = (k - 281) * 32 + 131;
n = 5;
} else {
len = 258;
n = 0;
}
if (n > 0) len += r.read(n);
// distance
if (r.pos > r.len) throw new Error("EOF");
let dist;
if (huff3) dist = huff3.readSymbol(r);
else dist = r.read(5);
if (dist < 4) {
dist++;
} else if (dist < 30) {
let db = (dist - 2) >> 1;
let extra = (dist & 1) << db;
extra |= r.read(db);
dist = (1 << (db + 1)) + 1 + extra;
} else {
throw new Error(`dist ${dist}`);
}
if (dist > pos) throw new Error(`dist ${dist} > pos ${pos}`);
let s: number = pos - dist;
for (let i = 0; i < len; i++) push(out[s + i]);
}
}
}
return out.slice(0, pos);
}
interface Entry {
size: number;
start: number;
end: number;
}
export class ZipFile {
data: Uint8Array;
entries: Record<string, Entry>;
constructor(data: Uint8Array) {
this.data = data;
this.entries = {};
let td = new TextDecoder();
let error = (msg: string) => {
throw new Error(`${msg} at ${pos}`);
};
let view = new DataView(data.buffer);
let pos = 0;
while (pos < view.byteLength) {
let sig = view.getUint32(pos, true);
if (sig == 0x02014b50) break;
if (sig != 0x04034b50) error(`bad sig ${sig.toString(16)}`);
let method = view.getUint16(pos + 8, true);
let csize = view.getUint32(pos + 18, true);
let size = view.getUint32(pos + 22, true);
let fnlen = view.getUint16(pos + 26, true);
let eflen = view.getUint16(pos + 28, true);
let fn = td.decode(data.slice(pos + 30, pos + 30 + fnlen));
if (size) {
let start = pos + 30 + fnlen + eflen;
let end = start + csize;
this.entries[fn] = { size, start, end };
}
pos = pos + 30 + fnlen + eflen + csize;
}
}
getData(name: string) {
let { start, end, size } = this.entries[name];
return inflate(new Uint8Array(this.data.slice(start, end)));
}
}