@ai-ecoverse/slicc-kernel 1.21.0 → 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -4
- package/dist/driver-worker.js +6 -4
- package/dist/driver-worker.js.map +2 -2
- package/dist/driver.js +6 -4
- package/dist/driver.js.map +2 -2
- package/dist/kernel-worker.js +482 -41
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-driver-worker.js +6 -4
- package/dist/node-driver-worker.js.map +2 -2
- package/dist/node-process-worker.js +396 -36
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +482 -41
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +396 -36
- package/dist/process-worker.js.map +4 -4
- package/package.json +1 -1
package/dist/node.js
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
import { MessageChannel as MessageChannel2, Worker } from "node:worker_threads";
|
|
3
3
|
|
|
4
4
|
// src/fs/types.ts
|
|
5
|
+
function rangedOps(fs) {
|
|
6
|
+
const { pread, pwrite, truncate } = fs;
|
|
7
|
+
if (!pread || !pwrite || !truncate) return void 0;
|
|
8
|
+
return { pread: pread.bind(fs), pwrite: pwrite.bind(fs), truncate: truncate.bind(fs) };
|
|
9
|
+
}
|
|
5
10
|
function fsError(code, path) {
|
|
6
11
|
return Object.assign(new Error(`${code}: ${path}`), { code });
|
|
7
12
|
}
|
|
@@ -2083,7 +2088,8 @@ function withCommandDirs(fs, names) {
|
|
|
2083
2088
|
symlink: (target, path) => fs.symlink(target, path),
|
|
2084
2089
|
readlink: (path) => fs.readlink(path),
|
|
2085
2090
|
chmod: (path, mode) => fs.chmod(path, mode),
|
|
2086
|
-
utimes: (path, atime, mtime) => fs.utimes(path, atime, mtime)
|
|
2091
|
+
utimes: (path, atime, mtime) => fs.utimes(path, atime, mtime),
|
|
2092
|
+
...rangedOps(fs) ?? {}
|
|
2087
2093
|
};
|
|
2088
2094
|
}
|
|
2089
2095
|
|
|
@@ -2160,6 +2166,11 @@ var FsWatchers = class {
|
|
|
2160
2166
|
chmod: after(fs.chmod.bind(fs), (path) => [path]),
|
|
2161
2167
|
utimes: after(fs.utimes.bind(fs), (path) => [path])
|
|
2162
2168
|
};
|
|
2169
|
+
const ranged2 = rangedOps(fs);
|
|
2170
|
+
if (ranged2) {
|
|
2171
|
+
overrides.pwrite = after(ranged2.pwrite, (path) => [path]);
|
|
2172
|
+
overrides.truncate = after(ranged2.truncate, (path) => [path]);
|
|
2173
|
+
}
|
|
2163
2174
|
return new Proxy(fs, {
|
|
2164
2175
|
get(target, key, receiver) {
|
|
2165
2176
|
if (Object.hasOwn(overrides, key)) return overrides[key];
|
|
@@ -2354,7 +2365,7 @@ var ChildTable = class {
|
|
|
2354
2365
|
};
|
|
2355
2366
|
|
|
2356
2367
|
// src/mount/protocol.ts
|
|
2357
|
-
var DRIVER_PROTOCOL = [1,
|
|
2368
|
+
var DRIVER_PROTOCOL = [1, 1];
|
|
2358
2369
|
function driverVersionError(theirs) {
|
|
2359
2370
|
const major = Array.isArray(theirs) ? theirs[0] : void 0;
|
|
2360
2371
|
if (major === DRIVER_PROTOCOL[0]) return void 0;
|
|
@@ -2513,7 +2524,8 @@ function statOf(attr, path, mount) {
|
|
|
2513
2524
|
ino: attr.ino ?? inodeOf(path),
|
|
2514
2525
|
dev: mount.dev,
|
|
2515
2526
|
...readonly(mount) ? { readonly: true } : {},
|
|
2516
|
-
...mount.maxFile > 0 ? { maxFile: mount.maxFile } : {}
|
|
2527
|
+
...mount.maxFile > 0 ? { maxFile: mount.maxFile } : {},
|
|
2528
|
+
...mount.caps.ranges ? { ranged: true } : {}
|
|
2517
2529
|
};
|
|
2518
2530
|
}
|
|
2519
2531
|
function concat2(chunks2, total) {
|
|
@@ -2526,6 +2538,21 @@ function concat2(chunks2, total) {
|
|
|
2526
2538
|
}
|
|
2527
2539
|
return out;
|
|
2528
2540
|
}
|
|
2541
|
+
function resized(bytes, size) {
|
|
2542
|
+
if (size <= bytes.length) return bytes.subarray(0, size);
|
|
2543
|
+
const out = new Uint8Array(size);
|
|
2544
|
+
out.set(bytes);
|
|
2545
|
+
return out;
|
|
2546
|
+
}
|
|
2547
|
+
function handed(bytes, transfer, chunk2) {
|
|
2548
|
+
const whole = bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
|
|
2549
|
+
return transfer && whole && bytes.length <= chunk2 && bytes.buffer instanceof ArrayBuffer;
|
|
2550
|
+
}
|
|
2551
|
+
function spliced(bytes, offset, part) {
|
|
2552
|
+
const out = resized(bytes, Math.max(bytes.length, offset + part.length));
|
|
2553
|
+
out.set(part, offset);
|
|
2554
|
+
return out;
|
|
2555
|
+
}
|
|
2529
2556
|
var MountTable = class {
|
|
2530
2557
|
mounts = /* @__PURE__ */ new Map();
|
|
2531
2558
|
devices = 0;
|
|
@@ -2805,18 +2832,98 @@ var MountFs = class {
|
|
|
2805
2832
|
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
2806
2833
|
try {
|
|
2807
2834
|
for (let offset = 0; offset < bytes.length; offset += chunk2) {
|
|
2808
|
-
|
|
2809
|
-
|
|
2835
|
+
const part = bytes.slice(offset, offset + chunk2);
|
|
2836
|
+
await mount.conn.call({ op: "write", fh, offset, bytes: part }, [part.buffer]);
|
|
2837
|
+
}
|
|
2838
|
+
} finally {
|
|
2839
|
+
await mount.conn.call({ op: "release", fh });
|
|
2840
|
+
this.table.forget(mount, real);
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
async pread(path, offset, length2) {
|
|
2844
|
+
const real = await this.follow(path);
|
|
2845
|
+
const found = this.at(real);
|
|
2846
|
+
if (!found) return (await this.base.readFileBuffer(real)).slice(offset, offset + length2);
|
|
2847
|
+
const { mount, rel: rel2 } = found;
|
|
2848
|
+
if ((await this.table.getattr(mount, real, rel2)).kind === "directory") {
|
|
2849
|
+
throw errnoError("EISDIR", real);
|
|
2850
|
+
}
|
|
2851
|
+
const fh = await this.open(mount, rel2, false);
|
|
2852
|
+
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
2853
|
+
const chunks2 = [];
|
|
2854
|
+
let total = 0;
|
|
2855
|
+
try {
|
|
2856
|
+
while (total < length2) {
|
|
2857
|
+
const bytes = await mount.conn.call({
|
|
2858
|
+
op: "read",
|
|
2810
2859
|
fh,
|
|
2811
|
-
offset,
|
|
2812
|
-
|
|
2860
|
+
offset: offset + total,
|
|
2861
|
+
size: Math.min(chunk2, length2 - total)
|
|
2813
2862
|
});
|
|
2863
|
+
if (bytes.length === 0) break;
|
|
2864
|
+
chunks2.push(bytes);
|
|
2865
|
+
total += bytes.length;
|
|
2866
|
+
}
|
|
2867
|
+
} finally {
|
|
2868
|
+
await mount.conn.call({ op: "release", fh });
|
|
2869
|
+
}
|
|
2870
|
+
return concat2(chunks2, total);
|
|
2871
|
+
}
|
|
2872
|
+
fits(mount, real, end) {
|
|
2873
|
+
if (mount.maxFile > 0 && end > mount.maxFile) {
|
|
2874
|
+
throw errnoError(
|
|
2875
|
+
"EFBIG",
|
|
2876
|
+
`${real} would be larger than this mount's maxfile (${mount.maxFile})`
|
|
2877
|
+
);
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
async pwrite(path, offset, bytes, transfer = false) {
|
|
2881
|
+
const real = await this.follow(path, 0, true);
|
|
2882
|
+
const found = this.at(real);
|
|
2883
|
+
if (!found)
|
|
2884
|
+
return this.base.writeFile(
|
|
2885
|
+
real,
|
|
2886
|
+
spliced(await this.base.readFileBuffer(real), offset, bytes)
|
|
2887
|
+
);
|
|
2888
|
+
const { mount, rel: rel2 } = found;
|
|
2889
|
+
this.writable(mount, real);
|
|
2890
|
+
this.fits(mount, real, offset + bytes.length);
|
|
2891
|
+
this.table.forget(mount, real);
|
|
2892
|
+
const fh = await mount.conn.call({
|
|
2893
|
+
op: "open",
|
|
2894
|
+
path: rel2,
|
|
2895
|
+
write: true,
|
|
2896
|
+
create: true,
|
|
2897
|
+
truncate: false,
|
|
2898
|
+
exclusive: false
|
|
2899
|
+
});
|
|
2900
|
+
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
2901
|
+
try {
|
|
2902
|
+
for (let at = 0; at < bytes.length; at += chunk2) {
|
|
2903
|
+
const part = handed(bytes, transfer, chunk2) ? bytes : bytes.slice(at, at + chunk2);
|
|
2904
|
+
await mount.conn.call({ op: "write", fh, offset: offset + at, bytes: part }, [part.buffer]);
|
|
2814
2905
|
}
|
|
2815
2906
|
} finally {
|
|
2816
2907
|
await mount.conn.call({ op: "release", fh });
|
|
2817
2908
|
this.table.forget(mount, real);
|
|
2818
2909
|
}
|
|
2819
2910
|
}
|
|
2911
|
+
async truncate(path, size) {
|
|
2912
|
+
const real = await this.follow(path);
|
|
2913
|
+
const found = this.at(real);
|
|
2914
|
+
if (!found?.mount.caps.ranges) {
|
|
2915
|
+
return this.writeFile(real, resized(await this.readFileBuffer(real), size));
|
|
2916
|
+
}
|
|
2917
|
+
const { mount, rel: rel2 } = found;
|
|
2918
|
+
this.writable(mount, real);
|
|
2919
|
+
this.fits(mount, real, size);
|
|
2920
|
+
this.table.forget(mount, real);
|
|
2921
|
+
try {
|
|
2922
|
+
await mount.conn.call({ op: "setattr", path: rel2, size });
|
|
2923
|
+
} finally {
|
|
2924
|
+
this.table.forget(mount, real);
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2820
2927
|
async exists(path) {
|
|
2821
2928
|
if (!this.at(path)) return this.base.exists(path);
|
|
2822
2929
|
try {
|
|
@@ -3000,7 +3107,8 @@ function statJson(s) {
|
|
|
3000
3107
|
ino: s.ino,
|
|
3001
3108
|
...s.dev !== void 0 ? { dev: s.dev } : {},
|
|
3002
3109
|
...s.readonly ? { readonly: true } : {},
|
|
3003
|
-
...s.maxFile ? { maxFile: s.maxFile } : {}
|
|
3110
|
+
...s.maxFile ? { maxFile: s.maxFile } : {},
|
|
3111
|
+
...s.ranged ? { ranged: true } : {}
|
|
3004
3112
|
};
|
|
3005
3113
|
}
|
|
3006
3114
|
async function readdirStat(fs, dir) {
|
|
@@ -3065,10 +3173,24 @@ async function run(entry, path, req) {
|
|
|
3065
3173
|
case "utimes":
|
|
3066
3174
|
await fs.utimes(path, new Date(req.atimeMs ?? 0), new Date(req.mtimeMs ?? 0));
|
|
3067
3175
|
return done;
|
|
3176
|
+
case "pread":
|
|
3177
|
+
case "pwrite":
|
|
3178
|
+
case "truncate":
|
|
3179
|
+
return ranged(fs, path, req);
|
|
3068
3180
|
default:
|
|
3069
3181
|
return { ok: false, errno: "EINVAL", message: `sync-fs: unknown op '${req.op}'` };
|
|
3070
3182
|
}
|
|
3071
3183
|
}
|
|
3184
|
+
async function ranged(fs, path, req) {
|
|
3185
|
+
const ops = rangedOps(fs);
|
|
3186
|
+
if (!ops) throw syncError("ENOSYS", path);
|
|
3187
|
+
const offset = req.offset ?? 0;
|
|
3188
|
+
if (req.op === "pread")
|
|
3189
|
+
return { ok: true, kind: "bytes", bytes: await ops.pread(path, offset, req.length ?? 0) };
|
|
3190
|
+
if (req.op === "pwrite") await ops.pwrite(path, offset, req.body ?? new Uint8Array(0), true);
|
|
3191
|
+
else await ops.truncate(path, req.length ?? 0);
|
|
3192
|
+
return done;
|
|
3193
|
+
}
|
|
3072
3194
|
async function dispatchSyncFs(req) {
|
|
3073
3195
|
const entry = resolveSyncFsToken(req.token);
|
|
3074
3196
|
if (!entry) return { ok: false, errno: "EACCES", message: "sync-fs: unknown or revoked token" };
|
|
@@ -3101,6 +3223,13 @@ var SAB_I_PUB = 7;
|
|
|
3101
3223
|
var SAB_I_SIGNALS = 8;
|
|
3102
3224
|
var SAB_I_TIMERS = 9;
|
|
3103
3225
|
var SAB_I_MEMORY = 10;
|
|
3226
|
+
var SAB_I_TOTAL_HI = 11;
|
|
3227
|
+
var SAB_I_OFFSET_HI = 12;
|
|
3228
|
+
var U32 = 2 ** 32;
|
|
3229
|
+
function storeU53(header3, lo, hi, value) {
|
|
3230
|
+
Atomics.store(header3, hi, Math.floor(value / U32));
|
|
3231
|
+
Atomics.store(header3, lo, value % U32 | 0);
|
|
3232
|
+
}
|
|
3104
3233
|
var SAB_STATE_READY = 2;
|
|
3105
3234
|
var SAB_STATUS_BYTES = 1;
|
|
3106
3235
|
var SAB_STATUS_JSON = 2;
|
|
@@ -3160,9 +3289,9 @@ function attachSyncSabResponder(port, sab, token, opts) {
|
|
|
3160
3289
|
const chunk2 = Math.min(window.byteLength, entry.payload.byteLength - offset);
|
|
3161
3290
|
if (chunk2 > 0) window.set(entry.payload.subarray(offset, offset + chunk2));
|
|
3162
3291
|
Atomics.store(header3, SAB_I_STATUS, entry.status);
|
|
3163
|
-
|
|
3292
|
+
storeU53(header3, SAB_I_TOTAL, SAB_I_TOTAL_HI, entry.payload.byteLength);
|
|
3164
3293
|
Atomics.store(header3, SAB_I_CHUNK, chunk2);
|
|
3165
|
-
|
|
3294
|
+
storeU53(header3, SAB_I_OFFSET, SAB_I_OFFSET_HI, offset);
|
|
3166
3295
|
Atomics.store(header3, SAB_I_SEQ, id);
|
|
3167
3296
|
Atomics.store(header3, SAB_I_STATE, SAB_STATE_READY);
|
|
3168
3297
|
Atomics.add(header3, SAB_I_PUB, 1);
|
|
@@ -5389,6 +5518,226 @@ async function accept(proc, fd, nonblock) {
|
|
|
5389
5518
|
return { fd: proc.fds.install(new OpenFile(socket), 3), peer: socket.peer };
|
|
5390
5519
|
}
|
|
5391
5520
|
|
|
5521
|
+
// src/fs/pages.ts
|
|
5522
|
+
var PAGE = 64 * 1024;
|
|
5523
|
+
var READAHEAD = 1024 * 1024;
|
|
5524
|
+
var CLEAN_LIMIT = 4 * 1024 * 1024;
|
|
5525
|
+
var WRITEBACK = 8 * 1024 * 1024;
|
|
5526
|
+
var RUN = 1024 * 1024;
|
|
5527
|
+
var Pages = class {
|
|
5528
|
+
length;
|
|
5529
|
+
stored;
|
|
5530
|
+
shrink;
|
|
5531
|
+
pages = /* @__PURE__ */ new Map();
|
|
5532
|
+
dirty = /* @__PURE__ */ new Set();
|
|
5533
|
+
free = [];
|
|
5534
|
+
pinned = false;
|
|
5535
|
+
constructor(size) {
|
|
5536
|
+
this.length = size;
|
|
5537
|
+
this.stored = size;
|
|
5538
|
+
}
|
|
5539
|
+
get dirtyBytes() {
|
|
5540
|
+
return this.dirty.size * PAGE;
|
|
5541
|
+
}
|
|
5542
|
+
get changed() {
|
|
5543
|
+
return this.dirty.size > 0 || this.shrink !== void 0 || this.length !== this.stored;
|
|
5544
|
+
}
|
|
5545
|
+
missing(at, length2) {
|
|
5546
|
+
this.evict();
|
|
5547
|
+
const end = Math.min(this.stored, at + length2, this.length);
|
|
5548
|
+
const runs = [];
|
|
5549
|
+
for (let page = Math.floor(at / PAGE); page * PAGE < end; page++) {
|
|
5550
|
+
if (this.pages.has(page)) continue;
|
|
5551
|
+
const last2 = runs[runs.length - 1];
|
|
5552
|
+
if (last2 && last2[0] + last2[1] === page * PAGE) last2[1] += PAGE;
|
|
5553
|
+
else runs.push([page * PAGE, PAGE]);
|
|
5554
|
+
}
|
|
5555
|
+
const last = runs[runs.length - 1];
|
|
5556
|
+
if (last) last[1] = Math.max(last[1], Math.min(READAHEAD, this.stored - last[0]));
|
|
5557
|
+
return runs.map(([start, n]) => [start, Math.min(n, this.stored - start)]);
|
|
5558
|
+
}
|
|
5559
|
+
partial(at, length2) {
|
|
5560
|
+
if (length2 === 0) return [];
|
|
5561
|
+
this.evict();
|
|
5562
|
+
const edges = /* @__PURE__ */ new Set([Math.floor(at / PAGE), Math.floor((at + length2 - 1) / PAGE)]);
|
|
5563
|
+
const runs = [];
|
|
5564
|
+
for (const page of edges) {
|
|
5565
|
+
const start = page * PAGE;
|
|
5566
|
+
const covered = at <= start && at + length2 >= start + PAGE;
|
|
5567
|
+
if (covered || this.pages.has(page) || start >= this.stored) continue;
|
|
5568
|
+
runs.push([start, Math.min(PAGE, this.stored - start)]);
|
|
5569
|
+
}
|
|
5570
|
+
return runs;
|
|
5571
|
+
}
|
|
5572
|
+
fill(start, bytes) {
|
|
5573
|
+
for (let at = 0; at < bytes.length; at += PAGE) {
|
|
5574
|
+
const page = (start + at) / PAGE;
|
|
5575
|
+
if (this.pages.has(page)) continue;
|
|
5576
|
+
const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
|
|
5577
|
+
const fresh = this.take(part.length);
|
|
5578
|
+
fresh.set(part);
|
|
5579
|
+
this.pages.set(page, fresh);
|
|
5580
|
+
}
|
|
5581
|
+
}
|
|
5582
|
+
read(at, length2) {
|
|
5583
|
+
const out = new Uint8Array(Math.max(0, Math.min(this.length, at + length2) - at));
|
|
5584
|
+
this.readInto(at, out);
|
|
5585
|
+
return out;
|
|
5586
|
+
}
|
|
5587
|
+
readInto(at, out) {
|
|
5588
|
+
const end = Math.min(this.length, at + out.length);
|
|
5589
|
+
for (let pos = at; pos < end; ) {
|
|
5590
|
+
const page = Math.floor(pos / PAGE);
|
|
5591
|
+
const from = pos - page * PAGE;
|
|
5592
|
+
const n = Math.min(PAGE - from, end - pos);
|
|
5593
|
+
const bytes = this.pages.get(page);
|
|
5594
|
+
if (bytes) out.set(bytes.subarray(from, from + n), pos - at);
|
|
5595
|
+
else out.fill(0, pos - at, pos - at + n);
|
|
5596
|
+
pos += n;
|
|
5597
|
+
}
|
|
5598
|
+
return Math.max(0, end - at);
|
|
5599
|
+
}
|
|
5600
|
+
write(at, bytes) {
|
|
5601
|
+
for (let done4 = 0; done4 < bytes.length; ) {
|
|
5602
|
+
const pos = at + done4;
|
|
5603
|
+
const page = Math.floor(pos / PAGE);
|
|
5604
|
+
const from = pos - page * PAGE;
|
|
5605
|
+
const n = Math.min(PAGE - from, bytes.length - done4);
|
|
5606
|
+
let target = this.pages.get(page);
|
|
5607
|
+
if (!target) {
|
|
5608
|
+
target = this.take(0);
|
|
5609
|
+
this.pages.set(page, target);
|
|
5610
|
+
}
|
|
5611
|
+
target.set(bytes.subarray(done4, done4 + n), from);
|
|
5612
|
+
this.dirty.add(page);
|
|
5613
|
+
done4 += n;
|
|
5614
|
+
}
|
|
5615
|
+
this.length = Math.max(this.length, at + bytes.length);
|
|
5616
|
+
}
|
|
5617
|
+
truncate(size) {
|
|
5618
|
+
if (size < this.stored) {
|
|
5619
|
+
this.stored = size;
|
|
5620
|
+
this.shrink = Math.min(this.shrink ?? size, size);
|
|
5621
|
+
}
|
|
5622
|
+
if (size < this.length) {
|
|
5623
|
+
const edge = Math.floor(size / PAGE);
|
|
5624
|
+
for (const page of [...this.pages.keys()]) {
|
|
5625
|
+
if (page > edge || page === edge && size === edge * PAGE) this.drop(page);
|
|
5626
|
+
}
|
|
5627
|
+
this.pages.get(edge)?.fill(0, size - edge * PAGE);
|
|
5628
|
+
}
|
|
5629
|
+
this.length = size;
|
|
5630
|
+
}
|
|
5631
|
+
plan() {
|
|
5632
|
+
const runs = [];
|
|
5633
|
+
const sorted = [...this.dirty].sort((a, b) => a - b);
|
|
5634
|
+
for (let i = 0; i < sorted.length; ) {
|
|
5635
|
+
let j = i + 1;
|
|
5636
|
+
const first = sorted[i];
|
|
5637
|
+
while (j < sorted.length && sorted[j] === first + (j - i) && sorted[j] * PAGE % RUN !== 0) {
|
|
5638
|
+
j++;
|
|
5639
|
+
}
|
|
5640
|
+
const start = first * PAGE;
|
|
5641
|
+
const end = Math.min(this.length, (first + (j - i)) * PAGE);
|
|
5642
|
+
const bytes = new Uint8Array(end - start);
|
|
5643
|
+
for (let k = i; k < j; k++) {
|
|
5644
|
+
const page = this.pages.get(sorted[k]);
|
|
5645
|
+
bytes.set(page.subarray(0, Math.min(PAGE, end - (k - i) * PAGE - start)), (k - i) * PAGE);
|
|
5646
|
+
}
|
|
5647
|
+
runs.push({ offset: start, bytes });
|
|
5648
|
+
i = j;
|
|
5649
|
+
}
|
|
5650
|
+
const written = Math.max(this.stored, ...runs.map((r) => r.offset + r.bytes.length));
|
|
5651
|
+
return {
|
|
5652
|
+
...this.shrink !== void 0 ? { shrink: this.shrink } : {},
|
|
5653
|
+
runs,
|
|
5654
|
+
...this.length > written ? { extend: this.length } : {}
|
|
5655
|
+
};
|
|
5656
|
+
}
|
|
5657
|
+
flushed() {
|
|
5658
|
+
this.dirty.clear();
|
|
5659
|
+
this.shrink = void 0;
|
|
5660
|
+
this.stored = this.length;
|
|
5661
|
+
this.evict();
|
|
5662
|
+
}
|
|
5663
|
+
pin() {
|
|
5664
|
+
this.pinned = true;
|
|
5665
|
+
}
|
|
5666
|
+
reset(size) {
|
|
5667
|
+
if (this.pinned) return;
|
|
5668
|
+
for (const page of [...this.pages.keys()]) if (!this.dirty.has(page)) this.drop(page);
|
|
5669
|
+
if (this.changed) return;
|
|
5670
|
+
this.length = size;
|
|
5671
|
+
this.stored = size;
|
|
5672
|
+
}
|
|
5673
|
+
evict() {
|
|
5674
|
+
if (this.pinned) return;
|
|
5675
|
+
let clean = (this.pages.size - this.dirty.size) * PAGE;
|
|
5676
|
+
for (const page of this.pages.keys()) {
|
|
5677
|
+
if (clean <= CLEAN_LIMIT) return;
|
|
5678
|
+
if (this.dirty.has(page)) continue;
|
|
5679
|
+
this.drop(page);
|
|
5680
|
+
clean -= PAGE;
|
|
5681
|
+
}
|
|
5682
|
+
}
|
|
5683
|
+
take(used) {
|
|
5684
|
+
const page = this.free.pop();
|
|
5685
|
+
if (!page) return new Uint8Array(PAGE);
|
|
5686
|
+
page.fill(0, used);
|
|
5687
|
+
return page;
|
|
5688
|
+
}
|
|
5689
|
+
drop(page) {
|
|
5690
|
+
const bytes = this.pages.get(page);
|
|
5691
|
+
this.pages.delete(page);
|
|
5692
|
+
this.dirty.delete(page);
|
|
5693
|
+
if (this.free.length < CLEAN_LIMIT / PAGE) this.free.push(bytes);
|
|
5694
|
+
}
|
|
5695
|
+
};
|
|
5696
|
+
|
|
5697
|
+
// src/fs/ranged.ts
|
|
5698
|
+
var AsyncRangedFile = class {
|
|
5699
|
+
pages;
|
|
5700
|
+
io;
|
|
5701
|
+
pinned = false;
|
|
5702
|
+
path;
|
|
5703
|
+
constructor(io, path, size) {
|
|
5704
|
+
this.io = io;
|
|
5705
|
+
this.path = path;
|
|
5706
|
+
this.pages = new Pages(size);
|
|
5707
|
+
}
|
|
5708
|
+
size() {
|
|
5709
|
+
return this.pages.length;
|
|
5710
|
+
}
|
|
5711
|
+
async fetch(runs) {
|
|
5712
|
+
for (const [start, n] of runs) this.pages.fill(start, await this.io.pread(this.path, start, n));
|
|
5713
|
+
}
|
|
5714
|
+
async read(at, length2) {
|
|
5715
|
+
await this.fetch(this.pages.missing(at, length2));
|
|
5716
|
+
return this.pages.read(at, length2);
|
|
5717
|
+
}
|
|
5718
|
+
async write(at, bytes) {
|
|
5719
|
+
await this.fetch(this.pages.partial(at, bytes.length));
|
|
5720
|
+
this.pages.write(at, bytes);
|
|
5721
|
+
if (!this.pinned && this.pages.dirtyBytes >= WRITEBACK) await this.flush();
|
|
5722
|
+
}
|
|
5723
|
+
truncate(size) {
|
|
5724
|
+
this.pages.truncate(size);
|
|
5725
|
+
}
|
|
5726
|
+
async flush() {
|
|
5727
|
+
if (this.pinned || !this.pages.changed) return;
|
|
5728
|
+
const plan = this.pages.plan();
|
|
5729
|
+
if (plan.shrink !== void 0) await this.io.truncate(this.path, plan.shrink);
|
|
5730
|
+
for (const run2 of plan.runs) await this.io.pwrite(this.path, run2.offset, run2.bytes, true);
|
|
5731
|
+
if (plan.extend !== void 0) await this.io.truncate(this.path, plan.extend);
|
|
5732
|
+
this.pages.flushed();
|
|
5733
|
+
}
|
|
5734
|
+
async pin() {
|
|
5735
|
+
this.pinned = true;
|
|
5736
|
+
this.pages.pin();
|
|
5737
|
+
await this.fetch(this.pages.missing(0, this.pages.length));
|
|
5738
|
+
}
|
|
5739
|
+
};
|
|
5740
|
+
|
|
5392
5741
|
// src/kernel/vfs-file.ts
|
|
5393
5742
|
var O_ACCMODE = 3;
|
|
5394
5743
|
var O_WRONLY = 1;
|
|
@@ -5408,6 +5757,7 @@ function within3(path, root) {
|
|
|
5408
5757
|
}
|
|
5409
5758
|
var VfsNode = class {
|
|
5410
5759
|
data;
|
|
5760
|
+
ranged;
|
|
5411
5761
|
length = 0;
|
|
5412
5762
|
dirty = false;
|
|
5413
5763
|
queue = Promise.resolve();
|
|
@@ -5438,6 +5788,23 @@ var VfsNode = class {
|
|
|
5438
5788
|
clearTimeout(this.writeBack);
|
|
5439
5789
|
this.writeBack = void 0;
|
|
5440
5790
|
this.data = void 0;
|
|
5791
|
+
this.ranged = void 0;
|
|
5792
|
+
}
|
|
5793
|
+
async prepare() {
|
|
5794
|
+
if (this.revoked) throw new KernelError("EIO");
|
|
5795
|
+
if (this.ranged || this.data) return this.ranged;
|
|
5796
|
+
const io = rangedOps(this.fs);
|
|
5797
|
+
const st = io && !this.orphaned ? await this.fs.stat?.(this.path).catch(() => void 0) : void 0;
|
|
5798
|
+
if (io && st?.ranged) {
|
|
5799
|
+
this.ranged = new AsyncRangedFile(io, this.path, st.size ?? 0);
|
|
5800
|
+
return this.ranged;
|
|
5801
|
+
}
|
|
5802
|
+
await this.load();
|
|
5803
|
+
return void 0;
|
|
5804
|
+
}
|
|
5805
|
+
async keep() {
|
|
5806
|
+
const ranged2 = await this.prepare();
|
|
5807
|
+
await ranged2?.pin();
|
|
5441
5808
|
}
|
|
5442
5809
|
async load() {
|
|
5443
5810
|
if (this.revoked) throw new KernelError("EIO");
|
|
@@ -5454,22 +5821,26 @@ var VfsNode = class {
|
|
|
5454
5821
|
return this.data;
|
|
5455
5822
|
}
|
|
5456
5823
|
replace(contents2) {
|
|
5824
|
+
this.ranged = void 0;
|
|
5457
5825
|
this.data = new Uint8Array(contents2);
|
|
5458
5826
|
this.length = this.data.length;
|
|
5459
5827
|
this.missing = false;
|
|
5460
5828
|
this.markDirty();
|
|
5461
5829
|
}
|
|
5462
5830
|
async materialize() {
|
|
5463
|
-
await this.
|
|
5831
|
+
if (await this.prepare()) return;
|
|
5464
5832
|
if (!this.missing || this.orphaned) return;
|
|
5465
5833
|
this.missing = false;
|
|
5466
5834
|
await this.fs.writeFile(this.path, this.data?.slice(0, this.length) ?? new Uint8Array(0));
|
|
5835
|
+
if (!this.dirty) this.data = void 0;
|
|
5467
5836
|
}
|
|
5468
5837
|
async size() {
|
|
5469
|
-
await this.
|
|
5470
|
-
return this.length;
|
|
5838
|
+
const ranged2 = await this.prepare();
|
|
5839
|
+
return ranged2 ? ranged2.size() : this.length;
|
|
5471
5840
|
}
|
|
5472
5841
|
async pread(max, at) {
|
|
5842
|
+
const ranged2 = await this.prepare();
|
|
5843
|
+
if (ranged2) return ranged2.read(at, max);
|
|
5473
5844
|
const bytes = await this.load();
|
|
5474
5845
|
const n = Math.max(0, Math.min(max, this.length - at));
|
|
5475
5846
|
return bytes.slice(at, at + n);
|
|
@@ -5492,12 +5863,18 @@ var VfsNode = class {
|
|
|
5492
5863
|
}
|
|
5493
5864
|
renamedTo(path) {
|
|
5494
5865
|
this.path = path;
|
|
5866
|
+
if (this.ranged) this.ranged.path = path;
|
|
5495
5867
|
this.cap = void 0;
|
|
5496
5868
|
}
|
|
5497
5869
|
async pwrite(bytes, at) {
|
|
5498
5870
|
if (bytes.length === 0) return 0;
|
|
5499
5871
|
await this.fits(at + bytes.length);
|
|
5500
|
-
await this.
|
|
5872
|
+
const ranged2 = await this.prepare();
|
|
5873
|
+
if (ranged2) {
|
|
5874
|
+
await ranged2.write(at, bytes);
|
|
5875
|
+
this.markDirty();
|
|
5876
|
+
return bytes.length;
|
|
5877
|
+
}
|
|
5501
5878
|
const buf = this.ensure(at + bytes.length);
|
|
5502
5879
|
if (at > this.length) buf.fill(0, this.length, at);
|
|
5503
5880
|
buf.set(bytes, at);
|
|
@@ -5507,7 +5884,12 @@ var VfsNode = class {
|
|
|
5507
5884
|
}
|
|
5508
5885
|
async truncate(size) {
|
|
5509
5886
|
await this.fits(size);
|
|
5510
|
-
await this.
|
|
5887
|
+
const ranged2 = await this.prepare();
|
|
5888
|
+
if (ranged2) {
|
|
5889
|
+
ranged2.truncate(size);
|
|
5890
|
+
this.markDirty();
|
|
5891
|
+
return;
|
|
5892
|
+
}
|
|
5511
5893
|
const buf = this.ensure(size);
|
|
5512
5894
|
if (size > this.length) buf.fill(0, this.length, size);
|
|
5513
5895
|
this.length = size;
|
|
@@ -5532,11 +5914,12 @@ var VfsNode = class {
|
|
|
5532
5914
|
}
|
|
5533
5915
|
async flush() {
|
|
5534
5916
|
if (this.revoked && this.dirty) throw new KernelError("EIO");
|
|
5535
|
-
if (!this.dirty || !this.data || this.
|
|
5917
|
+
if (!this.dirty || this.orphaned || !(this.data || this.ranged)) return;
|
|
5536
5918
|
this.dirty = false;
|
|
5537
5919
|
const started = performance.now();
|
|
5538
5920
|
try {
|
|
5539
|
-
|
|
5921
|
+
if (this.ranged) await this.ranged.flush();
|
|
5922
|
+
else await this.fs.writeFile(this.path, this.data.slice(0, this.length));
|
|
5540
5923
|
} catch (err) {
|
|
5541
5924
|
this.dirty = true;
|
|
5542
5925
|
throw err;
|
|
@@ -5583,7 +5966,7 @@ var VfsNodes = class {
|
|
|
5583
5966
|
}
|
|
5584
5967
|
async unlinking(path) {
|
|
5585
5968
|
const node = this.byPath.get(path);
|
|
5586
|
-
if (node) await node.serial(async () => void await Promise.all([node.
|
|
5969
|
+
if (node) await node.serial(async () => void await Promise.all([node.keep(), node.limit()]));
|
|
5587
5970
|
}
|
|
5588
5971
|
revoke(prefix) {
|
|
5589
5972
|
for (const [path, node] of this.byPath) {
|
|
@@ -6732,7 +7115,8 @@ async function dispatch(h, req) {
|
|
|
6732
7115
|
case "setattr":
|
|
6733
7116
|
return h.setattr?.(req.path, {
|
|
6734
7117
|
...req.mode !== void 0 ? { mode: req.mode } : {},
|
|
6735
|
-
...req.mtime !== void 0 ? { mtime: req.mtime } : {}
|
|
7118
|
+
...req.mtime !== void 0 ? { mtime: req.mtime } : {},
|
|
7119
|
+
...req.size !== void 0 ? { size: req.size } : {}
|
|
6736
7120
|
});
|
|
6737
7121
|
case "statfs":
|
|
6738
7122
|
return h.statfs?.() ?? null;
|
|
@@ -6740,7 +7124,7 @@ async function dispatch(h, req) {
|
|
|
6740
7124
|
throw fsError2("ENOSYS", `unknown driver op ${req.op}`);
|
|
6741
7125
|
}
|
|
6742
7126
|
}
|
|
6743
|
-
function serveFilesystem(port, handlers, capabilities = {}) {
|
|
7127
|
+
function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
|
|
6744
7128
|
let open = false;
|
|
6745
7129
|
const reply = (message2, transfer = []) => port.postMessage(message2, transfer);
|
|
6746
7130
|
async function hello(message2) {
|
|
@@ -6776,7 +7160,8 @@ function serveFilesystem(port, handlers, capabilities = {}) {
|
|
|
6776
7160
|
dispatch(handlers, data).then(
|
|
6777
7161
|
(result) => {
|
|
6778
7162
|
if (result instanceof Uint8Array) {
|
|
6779
|
-
const
|
|
7163
|
+
const whole = result.byteOffset === 0 && result.byteLength === result.buffer.byteLength;
|
|
7164
|
+
const bytes = options.owned && whole ? result : result.slice();
|
|
6780
7165
|
reply({ id: data.id, result: bytes }, [bytes.buffer]);
|
|
6781
7166
|
} else reply({ id: data.id, result: result ?? null });
|
|
6782
7167
|
},
|
|
@@ -7451,6 +7836,58 @@ function memoryMedia() {
|
|
|
7451
7836
|
};
|
|
7452
7837
|
}
|
|
7453
7838
|
|
|
7839
|
+
// src/mount/chunked.ts
|
|
7840
|
+
var CHUNK2 = 1024 * 1024;
|
|
7841
|
+
var ChunkedBytes = class {
|
|
7842
|
+
chunks = [];
|
|
7843
|
+
size = 0;
|
|
7844
|
+
read(offset, length2) {
|
|
7845
|
+
const end = Math.min(this.size, offset + length2);
|
|
7846
|
+
if (end <= offset) return new Uint8Array(0);
|
|
7847
|
+
const out = new Uint8Array(end - offset);
|
|
7848
|
+
for (let at = offset; at < end; ) {
|
|
7849
|
+
const index = Math.floor(at / CHUNK2);
|
|
7850
|
+
const from = at - index * CHUNK2;
|
|
7851
|
+
const n = Math.min(CHUNK2 - from, end - at);
|
|
7852
|
+
const chunk2 = this.chunks[index];
|
|
7853
|
+
if (chunk2) out.set(chunk2.subarray(from, Math.min(from + n, chunk2.length)), at - offset);
|
|
7854
|
+
at += n;
|
|
7855
|
+
}
|
|
7856
|
+
return out;
|
|
7857
|
+
}
|
|
7858
|
+
write(offset, bytes, owned = false) {
|
|
7859
|
+
const whole = owned && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
|
|
7860
|
+
for (let done4 = 0; done4 < bytes.length; ) {
|
|
7861
|
+
const at = offset + done4;
|
|
7862
|
+
const index = Math.floor(at / CHUNK2);
|
|
7863
|
+
const from = at - index * CHUNK2;
|
|
7864
|
+
const n = Math.min(CHUNK2 - from, bytes.length - done4);
|
|
7865
|
+
if (whole && n === CHUNK2 && bytes.length === CHUNK2) this.chunks[index] = bytes;
|
|
7866
|
+
else this.room(index, from + n).set(bytes.subarray(done4, done4 + n), from);
|
|
7867
|
+
done4 += n;
|
|
7868
|
+
}
|
|
7869
|
+
this.size = Math.max(this.size, offset + bytes.length);
|
|
7870
|
+
}
|
|
7871
|
+
truncate(size) {
|
|
7872
|
+
if (size < this.size) {
|
|
7873
|
+
const keep = Math.ceil(size / CHUNK2);
|
|
7874
|
+
this.chunks.length = Math.min(this.chunks.length, keep);
|
|
7875
|
+
const tail = this.chunks[keep - 1];
|
|
7876
|
+
const from = size - (keep - 1) * CHUNK2;
|
|
7877
|
+
if (tail && from < tail.length) tail.fill(0, from);
|
|
7878
|
+
}
|
|
7879
|
+
this.size = size;
|
|
7880
|
+
}
|
|
7881
|
+
room(index, need) {
|
|
7882
|
+
const chunk2 = this.chunks[index];
|
|
7883
|
+
if (chunk2 && chunk2.length >= need) return chunk2;
|
|
7884
|
+
const grown = new Uint8Array(Math.min(CHUNK2, Math.max(need, (chunk2?.length ?? 0) * 2)));
|
|
7885
|
+
if (chunk2) grown.set(chunk2);
|
|
7886
|
+
this.chunks[index] = grown;
|
|
7887
|
+
return grown;
|
|
7888
|
+
}
|
|
7889
|
+
};
|
|
7890
|
+
|
|
7454
7891
|
// src/mount/tmpfs.ts
|
|
7455
7892
|
function tmpfs() {
|
|
7456
7893
|
let inodes = 0;
|
|
@@ -7459,7 +7896,7 @@ function tmpfs() {
|
|
|
7459
7896
|
ino: ++inodes,
|
|
7460
7897
|
mode,
|
|
7461
7898
|
mtime: Date.now(),
|
|
7462
|
-
data: new
|
|
7899
|
+
data: new ChunkedBytes(),
|
|
7463
7900
|
target: "",
|
|
7464
7901
|
children: /* @__PURE__ */ new Map()
|
|
7465
7902
|
});
|
|
@@ -7487,7 +7924,7 @@ function tmpfs() {
|
|
|
7487
7924
|
}
|
|
7488
7925
|
const attr = (node) => ({
|
|
7489
7926
|
kind: node.kind,
|
|
7490
|
-
size: node.kind === "file" ? node.data.
|
|
7927
|
+
size: node.kind === "file" ? node.data.size : node.kind === "symlink" ? node.target.length : 4096,
|
|
7491
7928
|
mtime: node.mtime,
|
|
7492
7929
|
mode: node.mode,
|
|
7493
7930
|
ino: node.ino
|
|
@@ -7522,32 +7959,25 @@ function tmpfs() {
|
|
|
7522
7959
|
},
|
|
7523
7960
|
async open(path, flags) {
|
|
7524
7961
|
const node = opened2(path, flags);
|
|
7525
|
-
|
|
7526
|
-
|
|
7962
|
+
if (flags.truncate && node.kind === "file") {
|
|
7963
|
+
node.data.truncate(0);
|
|
7964
|
+
node.mtime = Date.now();
|
|
7965
|
+
}
|
|
7966
|
+
handles.set(++nextFh, { node, write: flags.write });
|
|
7527
7967
|
return nextFh;
|
|
7528
7968
|
},
|
|
7529
7969
|
async read(fh, offset, size) {
|
|
7530
|
-
|
|
7531
|
-
return h.data.slice(offset, Math.min(h.length, offset + size));
|
|
7970
|
+
return handle(fh).node.data.read(offset, size);
|
|
7532
7971
|
},
|
|
7533
7972
|
async write(fh, offset, bytes) {
|
|
7534
7973
|
const h = handle(fh);
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
grown.set(h.data.subarray(0, h.length));
|
|
7539
|
-
h.data = grown;
|
|
7540
|
-
}
|
|
7541
|
-
h.data.set(bytes, offset);
|
|
7542
|
-
h.length = Math.max(h.length, end);
|
|
7543
|
-
h.dirty = true;
|
|
7974
|
+
if (!h.write) throw fsError2("EBADF", `handle ${fh} is read-only`);
|
|
7975
|
+
h.node.data.write(offset, bytes, true);
|
|
7976
|
+
h.node.mtime = Date.now();
|
|
7544
7977
|
},
|
|
7545
7978
|
async release(fh) {
|
|
7546
|
-
|
|
7979
|
+
handle(fh);
|
|
7547
7980
|
handles.delete(fh);
|
|
7548
|
-
if (!h.dirty) return;
|
|
7549
|
-
h.node.data = h.data.slice(0, h.length);
|
|
7550
|
-
h.node.mtime = Date.now();
|
|
7551
7981
|
},
|
|
7552
7982
|
async mkdir(path) {
|
|
7553
7983
|
const { dir, name: name2 } = parent(path);
|
|
@@ -7600,6 +8030,12 @@ function tmpfs() {
|
|
|
7600
8030
|
async setattr(path, change) {
|
|
7601
8031
|
const node = find(path);
|
|
7602
8032
|
if (change.mode !== void 0) node.mode = node.mode & ~4095 | change.mode & 4095;
|
|
8033
|
+
if (change.size !== void 0) {
|
|
8034
|
+
if (node.kind !== "file")
|
|
8035
|
+
throw fsError2(node.kind === "directory" ? "EISDIR" : "EINVAL", path);
|
|
8036
|
+
node.data.truncate(change.size);
|
|
8037
|
+
node.mtime = Date.now();
|
|
8038
|
+
}
|
|
7603
8039
|
if (change.mtime !== void 0) node.mtime = change.mtime;
|
|
7604
8040
|
},
|
|
7605
8041
|
async statfs() {
|
|
@@ -8190,7 +8626,12 @@ var Launcher = class {
|
|
|
8190
8626
|
}
|
|
8191
8627
|
if (type === "tmpfs") {
|
|
8192
8628
|
const { port1, port2 } = new MessageChannel();
|
|
8193
|
-
serveFilesystem(
|
|
8629
|
+
serveFilesystem(
|
|
8630
|
+
port2,
|
|
8631
|
+
tmpfs(),
|
|
8632
|
+
{ symlinks: true, chmod: true, ranges: true, attrTtl: 0 },
|
|
8633
|
+
{ owned: true }
|
|
8634
|
+
);
|
|
8194
8635
|
return {
|
|
8195
8636
|
port: port1,
|
|
8196
8637
|
dispose: () => {
|