Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 27 additions & 9 deletions src/browser/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export interface Pack {
offsets: Map<string, { image: string; at: number }>;
}

const utf8 = new TextDecoder();

// Pack layout: repeated [u16 path length][path][u32 size][bytes]. In the boot
// pack, "/index.prefix" holds prefix-coded "d path" and "f path size
// [image:offset] [pack]" lines. A file with an offset is read in ranges from
Expand All @@ -24,11 +26,10 @@ export interface Pack {
export function parsePack(buf: ArrayBuffer): Pack {
const dv = new DataView(buf);
const u8 = new Uint8Array(buf);
const dec = new TextDecoder();
const files = new Map<string, Uint8Array>();
for (let p = 0; p < buf.byteLength;) {
const n = dv.getUint16(p);
const path = dec.decode(u8.subarray(p + 2, p + 2 + n));
const path = utf8.decode(u8.subarray(p + 2, p + 2 + n));
p += 2 + n;
const size = dv.getUint32(p);
files.set(path, u8.subarray(p + 4, p + 4 + size));
Expand All @@ -38,7 +39,7 @@ export function parsePack(buf: ArrayBuffer): Pack {
const raw = files.get("/index.prefix") ?? files.get("/index.txt");
if (raw) {
pack.index = new PackIndex(pack);
addIndex(pack, dec.decode(raw), files.has("/index.prefix"));
addIndex(pack, utf8.decode(raw), files.has("/index.prefix"));
files.delete("/index.txt");
files.delete("/index.prefix");
for (const [p, data] of files) {
Expand All @@ -56,6 +57,9 @@ export function parsePack(buf: ArrayBuffer): Pack {
export class PackIndex extends Map<string, number> {
readonly modules = new Map<string, Uint8Array>();
private readonly pack: Pack;
// directory -> names in it, built for the entries there were at the time
private dirs: Map<string, string[]> | null = null;
private dirsSize = 0;

constructor(pack: Pack) {
super([["/", -1]]);
Expand All @@ -72,6 +76,24 @@ export class PackIndex extends Map<string, number> {
return super.has(path);
}

// The names in each directory. Entries are only ever added, so a change in
// size means new ones.
children(): Map<string, string[]> {
if (this.dirs && this.dirsSize === this.size) return this.dirs;
const dirs = new Map<string, string[]>();
for (const p of this.keys()) {
if (p === "/") continue;
const i = p.lastIndexOf("/");
const parent = i === 0 ? "/" : p.slice(0, i);
let names = dirs.get(parent);
if (!names) dirs.set(parent, (names = []));
names.push(p.slice(i + 1));
}
this.dirs = dirs;
this.dirsSize = this.size;
return dirs;
}

// Parses the lines of the module path is in. The module directories
// themselves are in the main index.
load(path: string) {
Expand All @@ -82,7 +104,7 @@ export class PackIndex extends Map<string, number> {
const raw = this.modules.get(module);
if (!raw) return;
this.modules.delete(module);
addIndex(this.pack, new TextDecoder().decode(raw), true);
addIndex(this.pack, utf8.decode(raw), true);
}
}

Expand Down Expand Up @@ -297,9 +319,5 @@ export function listIndex(idx: PackIndex, path: string): string[] | null {
if (idx.get(path) !== -1) return null;
const prefix = path === "/" ? "/" : path + "/";
idx.load(prefix);
const out: string[] = [];
for (const p of idx.keys()) {
if (p.startsWith(prefix) && p.length > prefix.length && !p.slice(prefix.length).includes("/")) out.push(p.slice(prefix.length));
}
return out;
return [...(idx.children().get(path) ?? [])];
}
15 changes: 8 additions & 7 deletions src/jvm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ export class JVM {
private natives = new Map<string, NativeFn>();
private interned = new Map<string, JObject>();
readonly prims = new Map<string, JClass>();
private slots = new Map<string, number>();
// owner -> field name -> slot; two lookups of literal strings beat building a key
private slots = new Map<string, Map<string, number>>();
readonly threads = new Set<JThread>();
private runQueue: JThread[] = [];
private queued = new Set<JThread>();
Expand Down Expand Up @@ -716,14 +717,14 @@ export class JVM {
// ---- fields by name ------------------------------------------------------

slotOf(owner: string, name: string): number {
const key = owner + "." + name;
let s = this.slots.get(key);
let byName = this.slots.get(owner);
if (!byName) this.slots.set(owner, (byName = new Map()));
let s = byName.get(name);
if (s === undefined) {
const c = this.loadBootOrFail(owner);
const f = c.findField(name);
if (!f) throw new Error(`no field ${key}`);
const f = this.loadBootOrFail(owner).findField(name);
if (!f) throw new Error(`no field ${owner}.${name}`);
s = f.slot;
this.slots.set(key, s);
byName.set(name, s);
}
return s;
}
Expand Down
6 changes: 2 additions & 4 deletions src/natives/nio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,8 @@ function fillStat(t: JThread, attrs: JObject, st: Stat, writable: boolean, ino:
vm.setField(attrs, A, "st_uid", 1000);
vm.setField(attrs, A, "st_gid", 1000);
vm.setField(attrs, A, "st_size", BigInt(size));
for (const k of ["atime", "mtime", "ctime"]) {
vm.setField(attrs, A, `st_${k}_sec`, sec);
vm.setField(attrs, A, `st_${k}_nsec`, nsec);
}
for (const k of ["st_atime_sec", "st_mtime_sec", "st_ctime_sec"]) vm.setField(attrs, A, k, sec);
for (const k of ["st_atime_nsec", "st_mtime_nsec", "st_ctime_nsec"]) vm.setField(attrs, A, k, nsec);
}

// open mount table handles: whether the one entry has been read
Expand Down
Loading