@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/kernel-worker.js
CHANGED
|
@@ -252,6 +252,11 @@ function versionError(theirs) {
|
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
// src/fs/types.ts
|
|
255
|
+
function rangedOps(fs) {
|
|
256
|
+
const { pread, pwrite, truncate } = fs;
|
|
257
|
+
if (!pread || !pwrite || !truncate) return void 0;
|
|
258
|
+
return { pread: pread.bind(fs), pwrite: pwrite.bind(fs), truncate: truncate.bind(fs) };
|
|
259
|
+
}
|
|
255
260
|
function fsError(code, path) {
|
|
256
261
|
return Object.assign(new Error(`${code}: ${path}`), { code });
|
|
257
262
|
}
|
|
@@ -3654,7 +3659,8 @@ function withCommandDirs(fs, names) {
|
|
|
3654
3659
|
symlink: (target, path) => fs.symlink(target, path),
|
|
3655
3660
|
readlink: (path) => fs.readlink(path),
|
|
3656
3661
|
chmod: (path, mode) => fs.chmod(path, mode),
|
|
3657
|
-
utimes: (path, atime, mtime) => fs.utimes(path, atime, mtime)
|
|
3662
|
+
utimes: (path, atime, mtime) => fs.utimes(path, atime, mtime),
|
|
3663
|
+
...rangedOps(fs) ?? {}
|
|
3658
3664
|
};
|
|
3659
3665
|
}
|
|
3660
3666
|
|
|
@@ -3731,6 +3737,11 @@ var FsWatchers = class {
|
|
|
3731
3737
|
chmod: after(fs.chmod.bind(fs), (path) => [path]),
|
|
3732
3738
|
utimes: after(fs.utimes.bind(fs), (path) => [path])
|
|
3733
3739
|
};
|
|
3740
|
+
const ranged2 = rangedOps(fs);
|
|
3741
|
+
if (ranged2) {
|
|
3742
|
+
overrides.pwrite = after(ranged2.pwrite, (path) => [path]);
|
|
3743
|
+
overrides.truncate = after(ranged2.truncate, (path) => [path]);
|
|
3744
|
+
}
|
|
3734
3745
|
return new Proxy(fs, {
|
|
3735
3746
|
get(target, key, receiver) {
|
|
3736
3747
|
if (Object.hasOwn(overrides, key)) return overrides[key];
|
|
@@ -3925,7 +3936,7 @@ var ChildTable = class {
|
|
|
3925
3936
|
};
|
|
3926
3937
|
|
|
3927
3938
|
// src/mount/protocol.ts
|
|
3928
|
-
var DRIVER_PROTOCOL = [1,
|
|
3939
|
+
var DRIVER_PROTOCOL = [1, 1];
|
|
3929
3940
|
function driverVersionError(theirs) {
|
|
3930
3941
|
const major = Array.isArray(theirs) ? theirs[0] : void 0;
|
|
3931
3942
|
if (major === DRIVER_PROTOCOL[0]) return void 0;
|
|
@@ -4084,7 +4095,8 @@ function statOf(attr, path, mount) {
|
|
|
4084
4095
|
ino: attr.ino ?? inodeOf(path),
|
|
4085
4096
|
dev: mount.dev,
|
|
4086
4097
|
...readonly(mount) ? { readonly: true } : {},
|
|
4087
|
-
...mount.maxFile > 0 ? { maxFile: mount.maxFile } : {}
|
|
4098
|
+
...mount.maxFile > 0 ? { maxFile: mount.maxFile } : {},
|
|
4099
|
+
...mount.caps.ranges ? { ranged: true } : {}
|
|
4088
4100
|
};
|
|
4089
4101
|
}
|
|
4090
4102
|
function concat4(chunks, total) {
|
|
@@ -4097,6 +4109,21 @@ function concat4(chunks, total) {
|
|
|
4097
4109
|
}
|
|
4098
4110
|
return out;
|
|
4099
4111
|
}
|
|
4112
|
+
function resized(bytes, size) {
|
|
4113
|
+
if (size <= bytes.length) return bytes.subarray(0, size);
|
|
4114
|
+
const out = new Uint8Array(size);
|
|
4115
|
+
out.set(bytes);
|
|
4116
|
+
return out;
|
|
4117
|
+
}
|
|
4118
|
+
function handed(bytes, transfer, chunk2) {
|
|
4119
|
+
const whole = bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
|
|
4120
|
+
return transfer && whole && bytes.length <= chunk2 && bytes.buffer instanceof ArrayBuffer;
|
|
4121
|
+
}
|
|
4122
|
+
function spliced(bytes, offset, part) {
|
|
4123
|
+
const out = resized(bytes, Math.max(bytes.length, offset + part.length));
|
|
4124
|
+
out.set(part, offset);
|
|
4125
|
+
return out;
|
|
4126
|
+
}
|
|
4100
4127
|
var MountTable = class {
|
|
4101
4128
|
mounts = /* @__PURE__ */ new Map();
|
|
4102
4129
|
devices = 0;
|
|
@@ -4376,18 +4403,98 @@ var MountFs = class {
|
|
|
4376
4403
|
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
4377
4404
|
try {
|
|
4378
4405
|
for (let offset = 0; offset < bytes.length; offset += chunk2) {
|
|
4379
|
-
|
|
4380
|
-
|
|
4406
|
+
const part = bytes.slice(offset, offset + chunk2);
|
|
4407
|
+
await mount.conn.call({ op: "write", fh, offset, bytes: part }, [part.buffer]);
|
|
4408
|
+
}
|
|
4409
|
+
} finally {
|
|
4410
|
+
await mount.conn.call({ op: "release", fh });
|
|
4411
|
+
this.table.forget(mount, real);
|
|
4412
|
+
}
|
|
4413
|
+
}
|
|
4414
|
+
async pread(path, offset, length2) {
|
|
4415
|
+
const real = await this.follow(path);
|
|
4416
|
+
const found = this.at(real);
|
|
4417
|
+
if (!found) return (await this.base.readFileBuffer(real)).slice(offset, offset + length2);
|
|
4418
|
+
const { mount, rel: rel2 } = found;
|
|
4419
|
+
if ((await this.table.getattr(mount, real, rel2)).kind === "directory") {
|
|
4420
|
+
throw errnoError("EISDIR", real);
|
|
4421
|
+
}
|
|
4422
|
+
const fh = await this.open(mount, rel2, false);
|
|
4423
|
+
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
4424
|
+
const chunks = [];
|
|
4425
|
+
let total = 0;
|
|
4426
|
+
try {
|
|
4427
|
+
while (total < length2) {
|
|
4428
|
+
const bytes = await mount.conn.call({
|
|
4429
|
+
op: "read",
|
|
4381
4430
|
fh,
|
|
4382
|
-
offset,
|
|
4383
|
-
|
|
4431
|
+
offset: offset + total,
|
|
4432
|
+
size: Math.min(chunk2, length2 - total)
|
|
4384
4433
|
});
|
|
4434
|
+
if (bytes.length === 0) break;
|
|
4435
|
+
chunks.push(bytes);
|
|
4436
|
+
total += bytes.length;
|
|
4437
|
+
}
|
|
4438
|
+
} finally {
|
|
4439
|
+
await mount.conn.call({ op: "release", fh });
|
|
4440
|
+
}
|
|
4441
|
+
return concat4(chunks, total);
|
|
4442
|
+
}
|
|
4443
|
+
fits(mount, real, end) {
|
|
4444
|
+
if (mount.maxFile > 0 && end > mount.maxFile) {
|
|
4445
|
+
throw errnoError(
|
|
4446
|
+
"EFBIG",
|
|
4447
|
+
`${real} would be larger than this mount's maxfile (${mount.maxFile})`
|
|
4448
|
+
);
|
|
4449
|
+
}
|
|
4450
|
+
}
|
|
4451
|
+
async pwrite(path, offset, bytes, transfer = false) {
|
|
4452
|
+
const real = await this.follow(path, 0, true);
|
|
4453
|
+
const found = this.at(real);
|
|
4454
|
+
if (!found)
|
|
4455
|
+
return this.base.writeFile(
|
|
4456
|
+
real,
|
|
4457
|
+
spliced(await this.base.readFileBuffer(real), offset, bytes)
|
|
4458
|
+
);
|
|
4459
|
+
const { mount, rel: rel2 } = found;
|
|
4460
|
+
this.writable(mount, real);
|
|
4461
|
+
this.fits(mount, real, offset + bytes.length);
|
|
4462
|
+
this.table.forget(mount, real);
|
|
4463
|
+
const fh = await mount.conn.call({
|
|
4464
|
+
op: "open",
|
|
4465
|
+
path: rel2,
|
|
4466
|
+
write: true,
|
|
4467
|
+
create: true,
|
|
4468
|
+
truncate: false,
|
|
4469
|
+
exclusive: false
|
|
4470
|
+
});
|
|
4471
|
+
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
4472
|
+
try {
|
|
4473
|
+
for (let at = 0; at < bytes.length; at += chunk2) {
|
|
4474
|
+
const part = handed(bytes, transfer, chunk2) ? bytes : bytes.slice(at, at + chunk2);
|
|
4475
|
+
await mount.conn.call({ op: "write", fh, offset: offset + at, bytes: part }, [part.buffer]);
|
|
4385
4476
|
}
|
|
4386
4477
|
} finally {
|
|
4387
4478
|
await mount.conn.call({ op: "release", fh });
|
|
4388
4479
|
this.table.forget(mount, real);
|
|
4389
4480
|
}
|
|
4390
4481
|
}
|
|
4482
|
+
async truncate(path, size) {
|
|
4483
|
+
const real = await this.follow(path);
|
|
4484
|
+
const found = this.at(real);
|
|
4485
|
+
if (!found?.mount.caps.ranges) {
|
|
4486
|
+
return this.writeFile(real, resized(await this.readFileBuffer(real), size));
|
|
4487
|
+
}
|
|
4488
|
+
const { mount, rel: rel2 } = found;
|
|
4489
|
+
this.writable(mount, real);
|
|
4490
|
+
this.fits(mount, real, size);
|
|
4491
|
+
this.table.forget(mount, real);
|
|
4492
|
+
try {
|
|
4493
|
+
await mount.conn.call({ op: "setattr", path: rel2, size });
|
|
4494
|
+
} finally {
|
|
4495
|
+
this.table.forget(mount, real);
|
|
4496
|
+
}
|
|
4497
|
+
}
|
|
4391
4498
|
async exists(path) {
|
|
4392
4499
|
if (!this.at(path)) return this.base.exists(path);
|
|
4393
4500
|
try {
|
|
@@ -4571,7 +4678,8 @@ function statJson(s) {
|
|
|
4571
4678
|
ino: s.ino,
|
|
4572
4679
|
...s.dev !== void 0 ? { dev: s.dev } : {},
|
|
4573
4680
|
...s.readonly ? { readonly: true } : {},
|
|
4574
|
-
...s.maxFile ? { maxFile: s.maxFile } : {}
|
|
4681
|
+
...s.maxFile ? { maxFile: s.maxFile } : {},
|
|
4682
|
+
...s.ranged ? { ranged: true } : {}
|
|
4575
4683
|
};
|
|
4576
4684
|
}
|
|
4577
4685
|
async function readdirStat(fs, dir) {
|
|
@@ -4636,10 +4744,24 @@ async function run(entry, path, req) {
|
|
|
4636
4744
|
case "utimes":
|
|
4637
4745
|
await fs.utimes(path, new Date(req.atimeMs ?? 0), new Date(req.mtimeMs ?? 0));
|
|
4638
4746
|
return done;
|
|
4747
|
+
case "pread":
|
|
4748
|
+
case "pwrite":
|
|
4749
|
+
case "truncate":
|
|
4750
|
+
return ranged(fs, path, req);
|
|
4639
4751
|
default:
|
|
4640
4752
|
return { ok: false, errno: "EINVAL", message: `sync-fs: unknown op '${req.op}'` };
|
|
4641
4753
|
}
|
|
4642
4754
|
}
|
|
4755
|
+
async function ranged(fs, path, req) {
|
|
4756
|
+
const ops = rangedOps(fs);
|
|
4757
|
+
if (!ops) throw syncError("ENOSYS", path);
|
|
4758
|
+
const offset = req.offset ?? 0;
|
|
4759
|
+
if (req.op === "pread")
|
|
4760
|
+
return { ok: true, kind: "bytes", bytes: await ops.pread(path, offset, req.length ?? 0) };
|
|
4761
|
+
if (req.op === "pwrite") await ops.pwrite(path, offset, req.body ?? new Uint8Array(0), true);
|
|
4762
|
+
else await ops.truncate(path, req.length ?? 0);
|
|
4763
|
+
return done;
|
|
4764
|
+
}
|
|
4643
4765
|
async function dispatchSyncFs(req) {
|
|
4644
4766
|
const entry = resolveSyncFsToken(req.token);
|
|
4645
4767
|
if (!entry) return { ok: false, errno: "EACCES", message: "sync-fs: unknown or revoked token" };
|
|
@@ -4672,6 +4794,13 @@ var SAB_I_PUB = 7;
|
|
|
4672
4794
|
var SAB_I_SIGNALS = 8;
|
|
4673
4795
|
var SAB_I_TIMERS = 9;
|
|
4674
4796
|
var SAB_I_MEMORY = 10;
|
|
4797
|
+
var SAB_I_TOTAL_HI = 11;
|
|
4798
|
+
var SAB_I_OFFSET_HI = 12;
|
|
4799
|
+
var U32 = 2 ** 32;
|
|
4800
|
+
function storeU53(header2, lo, hi, value) {
|
|
4801
|
+
Atomics.store(header2, hi, Math.floor(value / U32));
|
|
4802
|
+
Atomics.store(header2, lo, value % U32 | 0);
|
|
4803
|
+
}
|
|
4675
4804
|
var SAB_STATE_READY = 2;
|
|
4676
4805
|
var SAB_STATUS_BYTES = 1;
|
|
4677
4806
|
var SAB_STATUS_JSON = 2;
|
|
@@ -4731,9 +4860,9 @@ function attachSyncSabResponder(port, sab, token, opts) {
|
|
|
4731
4860
|
const chunk2 = Math.min(window.byteLength, entry.payload.byteLength - offset);
|
|
4732
4861
|
if (chunk2 > 0) window.set(entry.payload.subarray(offset, offset + chunk2));
|
|
4733
4862
|
Atomics.store(header2, SAB_I_STATUS, entry.status);
|
|
4734
|
-
|
|
4863
|
+
storeU53(header2, SAB_I_TOTAL, SAB_I_TOTAL_HI, entry.payload.byteLength);
|
|
4735
4864
|
Atomics.store(header2, SAB_I_CHUNK, chunk2);
|
|
4736
|
-
|
|
4865
|
+
storeU53(header2, SAB_I_OFFSET, SAB_I_OFFSET_HI, offset);
|
|
4737
4866
|
Atomics.store(header2, SAB_I_SEQ, id);
|
|
4738
4867
|
Atomics.store(header2, SAB_I_STATE, SAB_STATE_READY);
|
|
4739
4868
|
Atomics.add(header2, SAB_I_PUB, 1);
|
|
@@ -5638,6 +5767,226 @@ async function accept(proc, fd, nonblock) {
|
|
|
5638
5767
|
return { fd: proc.fds.install(new OpenFile(socket), 3), peer: socket.peer };
|
|
5639
5768
|
}
|
|
5640
5769
|
|
|
5770
|
+
// src/fs/pages.ts
|
|
5771
|
+
var PAGE = 64 * 1024;
|
|
5772
|
+
var READAHEAD = 1024 * 1024;
|
|
5773
|
+
var CLEAN_LIMIT = 4 * 1024 * 1024;
|
|
5774
|
+
var WRITEBACK = 8 * 1024 * 1024;
|
|
5775
|
+
var RUN = 1024 * 1024;
|
|
5776
|
+
var Pages = class {
|
|
5777
|
+
length;
|
|
5778
|
+
stored;
|
|
5779
|
+
shrink;
|
|
5780
|
+
pages = /* @__PURE__ */ new Map();
|
|
5781
|
+
dirty = /* @__PURE__ */ new Set();
|
|
5782
|
+
free = [];
|
|
5783
|
+
pinned = false;
|
|
5784
|
+
constructor(size) {
|
|
5785
|
+
this.length = size;
|
|
5786
|
+
this.stored = size;
|
|
5787
|
+
}
|
|
5788
|
+
get dirtyBytes() {
|
|
5789
|
+
return this.dirty.size * PAGE;
|
|
5790
|
+
}
|
|
5791
|
+
get changed() {
|
|
5792
|
+
return this.dirty.size > 0 || this.shrink !== void 0 || this.length !== this.stored;
|
|
5793
|
+
}
|
|
5794
|
+
missing(at, length2) {
|
|
5795
|
+
this.evict();
|
|
5796
|
+
const end = Math.min(this.stored, at + length2, this.length);
|
|
5797
|
+
const runs = [];
|
|
5798
|
+
for (let page = Math.floor(at / PAGE); page * PAGE < end; page++) {
|
|
5799
|
+
if (this.pages.has(page)) continue;
|
|
5800
|
+
const last2 = runs[runs.length - 1];
|
|
5801
|
+
if (last2 && last2[0] + last2[1] === page * PAGE) last2[1] += PAGE;
|
|
5802
|
+
else runs.push([page * PAGE, PAGE]);
|
|
5803
|
+
}
|
|
5804
|
+
const last = runs[runs.length - 1];
|
|
5805
|
+
if (last) last[1] = Math.max(last[1], Math.min(READAHEAD, this.stored - last[0]));
|
|
5806
|
+
return runs.map(([start, n]) => [start, Math.min(n, this.stored - start)]);
|
|
5807
|
+
}
|
|
5808
|
+
partial(at, length2) {
|
|
5809
|
+
if (length2 === 0) return [];
|
|
5810
|
+
this.evict();
|
|
5811
|
+
const edges = /* @__PURE__ */ new Set([Math.floor(at / PAGE), Math.floor((at + length2 - 1) / PAGE)]);
|
|
5812
|
+
const runs = [];
|
|
5813
|
+
for (const page of edges) {
|
|
5814
|
+
const start = page * PAGE;
|
|
5815
|
+
const covered = at <= start && at + length2 >= start + PAGE;
|
|
5816
|
+
if (covered || this.pages.has(page) || start >= this.stored) continue;
|
|
5817
|
+
runs.push([start, Math.min(PAGE, this.stored - start)]);
|
|
5818
|
+
}
|
|
5819
|
+
return runs;
|
|
5820
|
+
}
|
|
5821
|
+
fill(start, bytes) {
|
|
5822
|
+
for (let at = 0; at < bytes.length; at += PAGE) {
|
|
5823
|
+
const page = (start + at) / PAGE;
|
|
5824
|
+
if (this.pages.has(page)) continue;
|
|
5825
|
+
const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
|
|
5826
|
+
const fresh = this.take(part.length);
|
|
5827
|
+
fresh.set(part);
|
|
5828
|
+
this.pages.set(page, fresh);
|
|
5829
|
+
}
|
|
5830
|
+
}
|
|
5831
|
+
read(at, length2) {
|
|
5832
|
+
const out = new Uint8Array(Math.max(0, Math.min(this.length, at + length2) - at));
|
|
5833
|
+
this.readInto(at, out);
|
|
5834
|
+
return out;
|
|
5835
|
+
}
|
|
5836
|
+
readInto(at, out) {
|
|
5837
|
+
const end = Math.min(this.length, at + out.length);
|
|
5838
|
+
for (let pos = at; pos < end; ) {
|
|
5839
|
+
const page = Math.floor(pos / PAGE);
|
|
5840
|
+
const from = pos - page * PAGE;
|
|
5841
|
+
const n = Math.min(PAGE - from, end - pos);
|
|
5842
|
+
const bytes = this.pages.get(page);
|
|
5843
|
+
if (bytes) out.set(bytes.subarray(from, from + n), pos - at);
|
|
5844
|
+
else out.fill(0, pos - at, pos - at + n);
|
|
5845
|
+
pos += n;
|
|
5846
|
+
}
|
|
5847
|
+
return Math.max(0, end - at);
|
|
5848
|
+
}
|
|
5849
|
+
write(at, bytes) {
|
|
5850
|
+
for (let done4 = 0; done4 < bytes.length; ) {
|
|
5851
|
+
const pos = at + done4;
|
|
5852
|
+
const page = Math.floor(pos / PAGE);
|
|
5853
|
+
const from = pos - page * PAGE;
|
|
5854
|
+
const n = Math.min(PAGE - from, bytes.length - done4);
|
|
5855
|
+
let target = this.pages.get(page);
|
|
5856
|
+
if (!target) {
|
|
5857
|
+
target = this.take(0);
|
|
5858
|
+
this.pages.set(page, target);
|
|
5859
|
+
}
|
|
5860
|
+
target.set(bytes.subarray(done4, done4 + n), from);
|
|
5861
|
+
this.dirty.add(page);
|
|
5862
|
+
done4 += n;
|
|
5863
|
+
}
|
|
5864
|
+
this.length = Math.max(this.length, at + bytes.length);
|
|
5865
|
+
}
|
|
5866
|
+
truncate(size) {
|
|
5867
|
+
if (size < this.stored) {
|
|
5868
|
+
this.stored = size;
|
|
5869
|
+
this.shrink = Math.min(this.shrink ?? size, size);
|
|
5870
|
+
}
|
|
5871
|
+
if (size < this.length) {
|
|
5872
|
+
const edge = Math.floor(size / PAGE);
|
|
5873
|
+
for (const page of [...this.pages.keys()]) {
|
|
5874
|
+
if (page > edge || page === edge && size === edge * PAGE) this.drop(page);
|
|
5875
|
+
}
|
|
5876
|
+
this.pages.get(edge)?.fill(0, size - edge * PAGE);
|
|
5877
|
+
}
|
|
5878
|
+
this.length = size;
|
|
5879
|
+
}
|
|
5880
|
+
plan() {
|
|
5881
|
+
const runs = [];
|
|
5882
|
+
const sorted = [...this.dirty].sort((a, b) => a - b);
|
|
5883
|
+
for (let i = 0; i < sorted.length; ) {
|
|
5884
|
+
let j = i + 1;
|
|
5885
|
+
const first = sorted[i];
|
|
5886
|
+
while (j < sorted.length && sorted[j] === first + (j - i) && sorted[j] * PAGE % RUN !== 0) {
|
|
5887
|
+
j++;
|
|
5888
|
+
}
|
|
5889
|
+
const start = first * PAGE;
|
|
5890
|
+
const end = Math.min(this.length, (first + (j - i)) * PAGE);
|
|
5891
|
+
const bytes = new Uint8Array(end - start);
|
|
5892
|
+
for (let k = i; k < j; k++) {
|
|
5893
|
+
const page = this.pages.get(sorted[k]);
|
|
5894
|
+
bytes.set(page.subarray(0, Math.min(PAGE, end - (k - i) * PAGE - start)), (k - i) * PAGE);
|
|
5895
|
+
}
|
|
5896
|
+
runs.push({ offset: start, bytes });
|
|
5897
|
+
i = j;
|
|
5898
|
+
}
|
|
5899
|
+
const written = Math.max(this.stored, ...runs.map((r) => r.offset + r.bytes.length));
|
|
5900
|
+
return {
|
|
5901
|
+
...this.shrink !== void 0 ? { shrink: this.shrink } : {},
|
|
5902
|
+
runs,
|
|
5903
|
+
...this.length > written ? { extend: this.length } : {}
|
|
5904
|
+
};
|
|
5905
|
+
}
|
|
5906
|
+
flushed() {
|
|
5907
|
+
this.dirty.clear();
|
|
5908
|
+
this.shrink = void 0;
|
|
5909
|
+
this.stored = this.length;
|
|
5910
|
+
this.evict();
|
|
5911
|
+
}
|
|
5912
|
+
pin() {
|
|
5913
|
+
this.pinned = true;
|
|
5914
|
+
}
|
|
5915
|
+
reset(size) {
|
|
5916
|
+
if (this.pinned) return;
|
|
5917
|
+
for (const page of [...this.pages.keys()]) if (!this.dirty.has(page)) this.drop(page);
|
|
5918
|
+
if (this.changed) return;
|
|
5919
|
+
this.length = size;
|
|
5920
|
+
this.stored = size;
|
|
5921
|
+
}
|
|
5922
|
+
evict() {
|
|
5923
|
+
if (this.pinned) return;
|
|
5924
|
+
let clean = (this.pages.size - this.dirty.size) * PAGE;
|
|
5925
|
+
for (const page of this.pages.keys()) {
|
|
5926
|
+
if (clean <= CLEAN_LIMIT) return;
|
|
5927
|
+
if (this.dirty.has(page)) continue;
|
|
5928
|
+
this.drop(page);
|
|
5929
|
+
clean -= PAGE;
|
|
5930
|
+
}
|
|
5931
|
+
}
|
|
5932
|
+
take(used) {
|
|
5933
|
+
const page = this.free.pop();
|
|
5934
|
+
if (!page) return new Uint8Array(PAGE);
|
|
5935
|
+
page.fill(0, used);
|
|
5936
|
+
return page;
|
|
5937
|
+
}
|
|
5938
|
+
drop(page) {
|
|
5939
|
+
const bytes = this.pages.get(page);
|
|
5940
|
+
this.pages.delete(page);
|
|
5941
|
+
this.dirty.delete(page);
|
|
5942
|
+
if (this.free.length < CLEAN_LIMIT / PAGE) this.free.push(bytes);
|
|
5943
|
+
}
|
|
5944
|
+
};
|
|
5945
|
+
|
|
5946
|
+
// src/fs/ranged.ts
|
|
5947
|
+
var AsyncRangedFile = class {
|
|
5948
|
+
pages;
|
|
5949
|
+
io;
|
|
5950
|
+
pinned = false;
|
|
5951
|
+
path;
|
|
5952
|
+
constructor(io, path, size) {
|
|
5953
|
+
this.io = io;
|
|
5954
|
+
this.path = path;
|
|
5955
|
+
this.pages = new Pages(size);
|
|
5956
|
+
}
|
|
5957
|
+
size() {
|
|
5958
|
+
return this.pages.length;
|
|
5959
|
+
}
|
|
5960
|
+
async fetch(runs) {
|
|
5961
|
+
for (const [start, n] of runs) this.pages.fill(start, await this.io.pread(this.path, start, n));
|
|
5962
|
+
}
|
|
5963
|
+
async read(at, length2) {
|
|
5964
|
+
await this.fetch(this.pages.missing(at, length2));
|
|
5965
|
+
return this.pages.read(at, length2);
|
|
5966
|
+
}
|
|
5967
|
+
async write(at, bytes) {
|
|
5968
|
+
await this.fetch(this.pages.partial(at, bytes.length));
|
|
5969
|
+
this.pages.write(at, bytes);
|
|
5970
|
+
if (!this.pinned && this.pages.dirtyBytes >= WRITEBACK) await this.flush();
|
|
5971
|
+
}
|
|
5972
|
+
truncate(size) {
|
|
5973
|
+
this.pages.truncate(size);
|
|
5974
|
+
}
|
|
5975
|
+
async flush() {
|
|
5976
|
+
if (this.pinned || !this.pages.changed) return;
|
|
5977
|
+
const plan = this.pages.plan();
|
|
5978
|
+
if (plan.shrink !== void 0) await this.io.truncate(this.path, plan.shrink);
|
|
5979
|
+
for (const run2 of plan.runs) await this.io.pwrite(this.path, run2.offset, run2.bytes, true);
|
|
5980
|
+
if (plan.extend !== void 0) await this.io.truncate(this.path, plan.extend);
|
|
5981
|
+
this.pages.flushed();
|
|
5982
|
+
}
|
|
5983
|
+
async pin() {
|
|
5984
|
+
this.pinned = true;
|
|
5985
|
+
this.pages.pin();
|
|
5986
|
+
await this.fetch(this.pages.missing(0, this.pages.length));
|
|
5987
|
+
}
|
|
5988
|
+
};
|
|
5989
|
+
|
|
5641
5990
|
// src/kernel/vfs-file.ts
|
|
5642
5991
|
var O_ACCMODE = 3;
|
|
5643
5992
|
var O_WRONLY = 1;
|
|
@@ -5657,6 +6006,7 @@ function within3(path, root) {
|
|
|
5657
6006
|
}
|
|
5658
6007
|
var VfsNode = class {
|
|
5659
6008
|
data;
|
|
6009
|
+
ranged;
|
|
5660
6010
|
length = 0;
|
|
5661
6011
|
dirty = false;
|
|
5662
6012
|
queue = Promise.resolve();
|
|
@@ -5687,6 +6037,23 @@ var VfsNode = class {
|
|
|
5687
6037
|
clearTimeout(this.writeBack);
|
|
5688
6038
|
this.writeBack = void 0;
|
|
5689
6039
|
this.data = void 0;
|
|
6040
|
+
this.ranged = void 0;
|
|
6041
|
+
}
|
|
6042
|
+
async prepare() {
|
|
6043
|
+
if (this.revoked) throw new KernelError("EIO");
|
|
6044
|
+
if (this.ranged || this.data) return this.ranged;
|
|
6045
|
+
const io = rangedOps(this.fs);
|
|
6046
|
+
const st = io && !this.orphaned ? await this.fs.stat?.(this.path).catch(() => void 0) : void 0;
|
|
6047
|
+
if (io && st?.ranged) {
|
|
6048
|
+
this.ranged = new AsyncRangedFile(io, this.path, st.size ?? 0);
|
|
6049
|
+
return this.ranged;
|
|
6050
|
+
}
|
|
6051
|
+
await this.load();
|
|
6052
|
+
return void 0;
|
|
6053
|
+
}
|
|
6054
|
+
async keep() {
|
|
6055
|
+
const ranged2 = await this.prepare();
|
|
6056
|
+
await ranged2?.pin();
|
|
5690
6057
|
}
|
|
5691
6058
|
async load() {
|
|
5692
6059
|
if (this.revoked) throw new KernelError("EIO");
|
|
@@ -5703,22 +6070,26 @@ var VfsNode = class {
|
|
|
5703
6070
|
return this.data;
|
|
5704
6071
|
}
|
|
5705
6072
|
replace(contents2) {
|
|
6073
|
+
this.ranged = void 0;
|
|
5706
6074
|
this.data = new Uint8Array(contents2);
|
|
5707
6075
|
this.length = this.data.length;
|
|
5708
6076
|
this.missing = false;
|
|
5709
6077
|
this.markDirty();
|
|
5710
6078
|
}
|
|
5711
6079
|
async materialize() {
|
|
5712
|
-
await this.
|
|
6080
|
+
if (await this.prepare()) return;
|
|
5713
6081
|
if (!this.missing || this.orphaned) return;
|
|
5714
6082
|
this.missing = false;
|
|
5715
6083
|
await this.fs.writeFile(this.path, this.data?.slice(0, this.length) ?? new Uint8Array(0));
|
|
6084
|
+
if (!this.dirty) this.data = void 0;
|
|
5716
6085
|
}
|
|
5717
6086
|
async size() {
|
|
5718
|
-
await this.
|
|
5719
|
-
return this.length;
|
|
6087
|
+
const ranged2 = await this.prepare();
|
|
6088
|
+
return ranged2 ? ranged2.size() : this.length;
|
|
5720
6089
|
}
|
|
5721
6090
|
async pread(max, at) {
|
|
6091
|
+
const ranged2 = await this.prepare();
|
|
6092
|
+
if (ranged2) return ranged2.read(at, max);
|
|
5722
6093
|
const bytes = await this.load();
|
|
5723
6094
|
const n = Math.max(0, Math.min(max, this.length - at));
|
|
5724
6095
|
return bytes.slice(at, at + n);
|
|
@@ -5741,12 +6112,18 @@ var VfsNode = class {
|
|
|
5741
6112
|
}
|
|
5742
6113
|
renamedTo(path) {
|
|
5743
6114
|
this.path = path;
|
|
6115
|
+
if (this.ranged) this.ranged.path = path;
|
|
5744
6116
|
this.cap = void 0;
|
|
5745
6117
|
}
|
|
5746
6118
|
async pwrite(bytes, at) {
|
|
5747
6119
|
if (bytes.length === 0) return 0;
|
|
5748
6120
|
await this.fits(at + bytes.length);
|
|
5749
|
-
await this.
|
|
6121
|
+
const ranged2 = await this.prepare();
|
|
6122
|
+
if (ranged2) {
|
|
6123
|
+
await ranged2.write(at, bytes);
|
|
6124
|
+
this.markDirty();
|
|
6125
|
+
return bytes.length;
|
|
6126
|
+
}
|
|
5750
6127
|
const buf = this.ensure(at + bytes.length);
|
|
5751
6128
|
if (at > this.length) buf.fill(0, this.length, at);
|
|
5752
6129
|
buf.set(bytes, at);
|
|
@@ -5756,7 +6133,12 @@ var VfsNode = class {
|
|
|
5756
6133
|
}
|
|
5757
6134
|
async truncate(size) {
|
|
5758
6135
|
await this.fits(size);
|
|
5759
|
-
await this.
|
|
6136
|
+
const ranged2 = await this.prepare();
|
|
6137
|
+
if (ranged2) {
|
|
6138
|
+
ranged2.truncate(size);
|
|
6139
|
+
this.markDirty();
|
|
6140
|
+
return;
|
|
6141
|
+
}
|
|
5760
6142
|
const buf = this.ensure(size);
|
|
5761
6143
|
if (size > this.length) buf.fill(0, this.length, size);
|
|
5762
6144
|
this.length = size;
|
|
@@ -5781,11 +6163,12 @@ var VfsNode = class {
|
|
|
5781
6163
|
}
|
|
5782
6164
|
async flush() {
|
|
5783
6165
|
if (this.revoked && this.dirty) throw new KernelError("EIO");
|
|
5784
|
-
if (!this.dirty || !this.data || this.
|
|
6166
|
+
if (!this.dirty || this.orphaned || !(this.data || this.ranged)) return;
|
|
5785
6167
|
this.dirty = false;
|
|
5786
6168
|
const started = performance.now();
|
|
5787
6169
|
try {
|
|
5788
|
-
|
|
6170
|
+
if (this.ranged) await this.ranged.flush();
|
|
6171
|
+
else await this.fs.writeFile(this.path, this.data.slice(0, this.length));
|
|
5789
6172
|
} catch (err) {
|
|
5790
6173
|
this.dirty = true;
|
|
5791
6174
|
throw err;
|
|
@@ -5832,7 +6215,7 @@ var VfsNodes = class {
|
|
|
5832
6215
|
}
|
|
5833
6216
|
async unlinking(path) {
|
|
5834
6217
|
const node = this.byPath.get(path);
|
|
5835
|
-
if (node) await node.serial(async () => void await Promise.all([node.
|
|
6218
|
+
if (node) await node.serial(async () => void await Promise.all([node.keep(), node.limit()]));
|
|
5836
6219
|
}
|
|
5837
6220
|
revoke(prefix) {
|
|
5838
6221
|
for (const [path, node] of this.byPath) {
|
|
@@ -6981,7 +7364,8 @@ async function dispatch(h, req) {
|
|
|
6981
7364
|
case "setattr":
|
|
6982
7365
|
return h.setattr?.(req.path, {
|
|
6983
7366
|
...req.mode !== void 0 ? { mode: req.mode } : {},
|
|
6984
|
-
...req.mtime !== void 0 ? { mtime: req.mtime } : {}
|
|
7367
|
+
...req.mtime !== void 0 ? { mtime: req.mtime } : {},
|
|
7368
|
+
...req.size !== void 0 ? { size: req.size } : {}
|
|
6985
7369
|
});
|
|
6986
7370
|
case "statfs":
|
|
6987
7371
|
return h.statfs?.() ?? null;
|
|
@@ -6989,7 +7373,7 @@ async function dispatch(h, req) {
|
|
|
6989
7373
|
throw fsError2("ENOSYS", `unknown driver op ${req.op}`);
|
|
6990
7374
|
}
|
|
6991
7375
|
}
|
|
6992
|
-
function serveFilesystem(port, handlers, capabilities = {}) {
|
|
7376
|
+
function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
|
|
6993
7377
|
let open = false;
|
|
6994
7378
|
const reply = (message2, transfer = []) => port.postMessage(message2, transfer);
|
|
6995
7379
|
async function hello(message2) {
|
|
@@ -7025,7 +7409,8 @@ function serveFilesystem(port, handlers, capabilities = {}) {
|
|
|
7025
7409
|
dispatch(handlers, data).then(
|
|
7026
7410
|
(result) => {
|
|
7027
7411
|
if (result instanceof Uint8Array) {
|
|
7028
|
-
const
|
|
7412
|
+
const whole = result.byteOffset === 0 && result.byteLength === result.buffer.byteLength;
|
|
7413
|
+
const bytes = options.owned && whole ? result : result.slice();
|
|
7029
7414
|
reply({ id: data.id, result: bytes }, [bytes.buffer]);
|
|
7030
7415
|
} else reply({ id: data.id, result: result ?? null });
|
|
7031
7416
|
},
|
|
@@ -7691,6 +8076,58 @@ async function openHostfs(spec, hook, fetch2, timing = {}) {
|
|
|
7691
8076
|
};
|
|
7692
8077
|
}
|
|
7693
8078
|
|
|
8079
|
+
// src/mount/chunked.ts
|
|
8080
|
+
var CHUNK2 = 1024 * 1024;
|
|
8081
|
+
var ChunkedBytes = class {
|
|
8082
|
+
chunks = [];
|
|
8083
|
+
size = 0;
|
|
8084
|
+
read(offset, length2) {
|
|
8085
|
+
const end = Math.min(this.size, offset + length2);
|
|
8086
|
+
if (end <= offset) return new Uint8Array(0);
|
|
8087
|
+
const out = new Uint8Array(end - offset);
|
|
8088
|
+
for (let at = offset; at < end; ) {
|
|
8089
|
+
const index = Math.floor(at / CHUNK2);
|
|
8090
|
+
const from = at - index * CHUNK2;
|
|
8091
|
+
const n = Math.min(CHUNK2 - from, end - at);
|
|
8092
|
+
const chunk2 = this.chunks[index];
|
|
8093
|
+
if (chunk2) out.set(chunk2.subarray(from, Math.min(from + n, chunk2.length)), at - offset);
|
|
8094
|
+
at += n;
|
|
8095
|
+
}
|
|
8096
|
+
return out;
|
|
8097
|
+
}
|
|
8098
|
+
write(offset, bytes, owned = false) {
|
|
8099
|
+
const whole = owned && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
|
|
8100
|
+
for (let done4 = 0; done4 < bytes.length; ) {
|
|
8101
|
+
const at = offset + done4;
|
|
8102
|
+
const index = Math.floor(at / CHUNK2);
|
|
8103
|
+
const from = at - index * CHUNK2;
|
|
8104
|
+
const n = Math.min(CHUNK2 - from, bytes.length - done4);
|
|
8105
|
+
if (whole && n === CHUNK2 && bytes.length === CHUNK2) this.chunks[index] = bytes;
|
|
8106
|
+
else this.room(index, from + n).set(bytes.subarray(done4, done4 + n), from);
|
|
8107
|
+
done4 += n;
|
|
8108
|
+
}
|
|
8109
|
+
this.size = Math.max(this.size, offset + bytes.length);
|
|
8110
|
+
}
|
|
8111
|
+
truncate(size) {
|
|
8112
|
+
if (size < this.size) {
|
|
8113
|
+
const keep = Math.ceil(size / CHUNK2);
|
|
8114
|
+
this.chunks.length = Math.min(this.chunks.length, keep);
|
|
8115
|
+
const tail = this.chunks[keep - 1];
|
|
8116
|
+
const from = size - (keep - 1) * CHUNK2;
|
|
8117
|
+
if (tail && from < tail.length) tail.fill(0, from);
|
|
8118
|
+
}
|
|
8119
|
+
this.size = size;
|
|
8120
|
+
}
|
|
8121
|
+
room(index, need) {
|
|
8122
|
+
const chunk2 = this.chunks[index];
|
|
8123
|
+
if (chunk2 && chunk2.length >= need) return chunk2;
|
|
8124
|
+
const grown = new Uint8Array(Math.min(CHUNK2, Math.max(need, (chunk2?.length ?? 0) * 2)));
|
|
8125
|
+
if (chunk2) grown.set(chunk2);
|
|
8126
|
+
this.chunks[index] = grown;
|
|
8127
|
+
return grown;
|
|
8128
|
+
}
|
|
8129
|
+
};
|
|
8130
|
+
|
|
7694
8131
|
// src/mount/tmpfs.ts
|
|
7695
8132
|
function tmpfs() {
|
|
7696
8133
|
let inodes = 0;
|
|
@@ -7699,7 +8136,7 @@ function tmpfs() {
|
|
|
7699
8136
|
ino: ++inodes,
|
|
7700
8137
|
mode,
|
|
7701
8138
|
mtime: Date.now(),
|
|
7702
|
-
data: new
|
|
8139
|
+
data: new ChunkedBytes(),
|
|
7703
8140
|
target: "",
|
|
7704
8141
|
children: /* @__PURE__ */ new Map()
|
|
7705
8142
|
});
|
|
@@ -7727,7 +8164,7 @@ function tmpfs() {
|
|
|
7727
8164
|
}
|
|
7728
8165
|
const attr = (node) => ({
|
|
7729
8166
|
kind: node.kind,
|
|
7730
|
-
size: node.kind === "file" ? node.data.
|
|
8167
|
+
size: node.kind === "file" ? node.data.size : node.kind === "symlink" ? node.target.length : 4096,
|
|
7731
8168
|
mtime: node.mtime,
|
|
7732
8169
|
mode: node.mode,
|
|
7733
8170
|
ino: node.ino
|
|
@@ -7762,32 +8199,25 @@ function tmpfs() {
|
|
|
7762
8199
|
},
|
|
7763
8200
|
async open(path, flags) {
|
|
7764
8201
|
const node = opened2(path, flags);
|
|
7765
|
-
|
|
7766
|
-
|
|
8202
|
+
if (flags.truncate && node.kind === "file") {
|
|
8203
|
+
node.data.truncate(0);
|
|
8204
|
+
node.mtime = Date.now();
|
|
8205
|
+
}
|
|
8206
|
+
handles.set(++nextFh, { node, write: flags.write });
|
|
7767
8207
|
return nextFh;
|
|
7768
8208
|
},
|
|
7769
8209
|
async read(fh, offset, size) {
|
|
7770
|
-
|
|
7771
|
-
return h.data.slice(offset, Math.min(h.length, offset + size));
|
|
8210
|
+
return handle(fh).node.data.read(offset, size);
|
|
7772
8211
|
},
|
|
7773
8212
|
async write(fh, offset, bytes) {
|
|
7774
8213
|
const h = handle(fh);
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
grown.set(h.data.subarray(0, h.length));
|
|
7779
|
-
h.data = grown;
|
|
7780
|
-
}
|
|
7781
|
-
h.data.set(bytes, offset);
|
|
7782
|
-
h.length = Math.max(h.length, end);
|
|
7783
|
-
h.dirty = true;
|
|
8214
|
+
if (!h.write) throw fsError2("EBADF", `handle ${fh} is read-only`);
|
|
8215
|
+
h.node.data.write(offset, bytes, true);
|
|
8216
|
+
h.node.mtime = Date.now();
|
|
7784
8217
|
},
|
|
7785
8218
|
async release(fh) {
|
|
7786
|
-
|
|
8219
|
+
handle(fh);
|
|
7787
8220
|
handles.delete(fh);
|
|
7788
|
-
if (!h.dirty) return;
|
|
7789
|
-
h.node.data = h.data.slice(0, h.length);
|
|
7790
|
-
h.node.mtime = Date.now();
|
|
7791
8221
|
},
|
|
7792
8222
|
async mkdir(path) {
|
|
7793
8223
|
const { dir, name: name2 } = parent(path);
|
|
@@ -7840,6 +8270,12 @@ function tmpfs() {
|
|
|
7840
8270
|
async setattr(path, change) {
|
|
7841
8271
|
const node = find(path);
|
|
7842
8272
|
if (change.mode !== void 0) node.mode = node.mode & ~4095 | change.mode & 4095;
|
|
8273
|
+
if (change.size !== void 0) {
|
|
8274
|
+
if (node.kind !== "file")
|
|
8275
|
+
throw fsError2(node.kind === "directory" ? "EISDIR" : "EINVAL", path);
|
|
8276
|
+
node.data.truncate(change.size);
|
|
8277
|
+
node.mtime = Date.now();
|
|
8278
|
+
}
|
|
7843
8279
|
if (change.mtime !== void 0) node.mtime = change.mtime;
|
|
7844
8280
|
},
|
|
7845
8281
|
async statfs() {
|
|
@@ -8430,7 +8866,12 @@ var Launcher = class {
|
|
|
8430
8866
|
}
|
|
8431
8867
|
if (type === "tmpfs") {
|
|
8432
8868
|
const { port1, port2 } = new MessageChannel();
|
|
8433
|
-
serveFilesystem(
|
|
8869
|
+
serveFilesystem(
|
|
8870
|
+
port2,
|
|
8871
|
+
tmpfs(),
|
|
8872
|
+
{ symlinks: true, chmod: true, ranges: true, attrTtl: 0 },
|
|
8873
|
+
{ owned: true }
|
|
8874
|
+
);
|
|
8434
8875
|
return {
|
|
8435
8876
|
port: port1,
|
|
8436
8877
|
dispose: () => {
|