@ai-ecoverse/slicc-kernel 1.21.1 → 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 +6 -5
- 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 +473 -39
- 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 +387 -33
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +473 -39
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +387 -33
- 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" };
|
|
@@ -5645,6 +5767,226 @@ async function accept(proc, fd, nonblock) {
|
|
|
5645
5767
|
return { fd: proc.fds.install(new OpenFile(socket), 3), peer: socket.peer };
|
|
5646
5768
|
}
|
|
5647
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
|
+
|
|
5648
5990
|
// src/kernel/vfs-file.ts
|
|
5649
5991
|
var O_ACCMODE = 3;
|
|
5650
5992
|
var O_WRONLY = 1;
|
|
@@ -5664,6 +6006,7 @@ function within3(path, root) {
|
|
|
5664
6006
|
}
|
|
5665
6007
|
var VfsNode = class {
|
|
5666
6008
|
data;
|
|
6009
|
+
ranged;
|
|
5667
6010
|
length = 0;
|
|
5668
6011
|
dirty = false;
|
|
5669
6012
|
queue = Promise.resolve();
|
|
@@ -5694,6 +6037,23 @@ var VfsNode = class {
|
|
|
5694
6037
|
clearTimeout(this.writeBack);
|
|
5695
6038
|
this.writeBack = void 0;
|
|
5696
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();
|
|
5697
6057
|
}
|
|
5698
6058
|
async load() {
|
|
5699
6059
|
if (this.revoked) throw new KernelError("EIO");
|
|
@@ -5710,22 +6070,26 @@ var VfsNode = class {
|
|
|
5710
6070
|
return this.data;
|
|
5711
6071
|
}
|
|
5712
6072
|
replace(contents2) {
|
|
6073
|
+
this.ranged = void 0;
|
|
5713
6074
|
this.data = new Uint8Array(contents2);
|
|
5714
6075
|
this.length = this.data.length;
|
|
5715
6076
|
this.missing = false;
|
|
5716
6077
|
this.markDirty();
|
|
5717
6078
|
}
|
|
5718
6079
|
async materialize() {
|
|
5719
|
-
await this.
|
|
6080
|
+
if (await this.prepare()) return;
|
|
5720
6081
|
if (!this.missing || this.orphaned) return;
|
|
5721
6082
|
this.missing = false;
|
|
5722
6083
|
await this.fs.writeFile(this.path, this.data?.slice(0, this.length) ?? new Uint8Array(0));
|
|
6084
|
+
if (!this.dirty) this.data = void 0;
|
|
5723
6085
|
}
|
|
5724
6086
|
async size() {
|
|
5725
|
-
await this.
|
|
5726
|
-
return this.length;
|
|
6087
|
+
const ranged2 = await this.prepare();
|
|
6088
|
+
return ranged2 ? ranged2.size() : this.length;
|
|
5727
6089
|
}
|
|
5728
6090
|
async pread(max, at) {
|
|
6091
|
+
const ranged2 = await this.prepare();
|
|
6092
|
+
if (ranged2) return ranged2.read(at, max);
|
|
5729
6093
|
const bytes = await this.load();
|
|
5730
6094
|
const n = Math.max(0, Math.min(max, this.length - at));
|
|
5731
6095
|
return bytes.slice(at, at + n);
|
|
@@ -5748,12 +6112,18 @@ var VfsNode = class {
|
|
|
5748
6112
|
}
|
|
5749
6113
|
renamedTo(path) {
|
|
5750
6114
|
this.path = path;
|
|
6115
|
+
if (this.ranged) this.ranged.path = path;
|
|
5751
6116
|
this.cap = void 0;
|
|
5752
6117
|
}
|
|
5753
6118
|
async pwrite(bytes, at) {
|
|
5754
6119
|
if (bytes.length === 0) return 0;
|
|
5755
6120
|
await this.fits(at + bytes.length);
|
|
5756
|
-
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
|
+
}
|
|
5757
6127
|
const buf = this.ensure(at + bytes.length);
|
|
5758
6128
|
if (at > this.length) buf.fill(0, this.length, at);
|
|
5759
6129
|
buf.set(bytes, at);
|
|
@@ -5763,7 +6133,12 @@ var VfsNode = class {
|
|
|
5763
6133
|
}
|
|
5764
6134
|
async truncate(size) {
|
|
5765
6135
|
await this.fits(size);
|
|
5766
|
-
await this.
|
|
6136
|
+
const ranged2 = await this.prepare();
|
|
6137
|
+
if (ranged2) {
|
|
6138
|
+
ranged2.truncate(size);
|
|
6139
|
+
this.markDirty();
|
|
6140
|
+
return;
|
|
6141
|
+
}
|
|
5767
6142
|
const buf = this.ensure(size);
|
|
5768
6143
|
if (size > this.length) buf.fill(0, this.length, size);
|
|
5769
6144
|
this.length = size;
|
|
@@ -5788,11 +6163,12 @@ var VfsNode = class {
|
|
|
5788
6163
|
}
|
|
5789
6164
|
async flush() {
|
|
5790
6165
|
if (this.revoked && this.dirty) throw new KernelError("EIO");
|
|
5791
|
-
if (!this.dirty || !this.data || this.
|
|
6166
|
+
if (!this.dirty || this.orphaned || !(this.data || this.ranged)) return;
|
|
5792
6167
|
this.dirty = false;
|
|
5793
6168
|
const started = performance.now();
|
|
5794
6169
|
try {
|
|
5795
|
-
|
|
6170
|
+
if (this.ranged) await this.ranged.flush();
|
|
6171
|
+
else await this.fs.writeFile(this.path, this.data.slice(0, this.length));
|
|
5796
6172
|
} catch (err) {
|
|
5797
6173
|
this.dirty = true;
|
|
5798
6174
|
throw err;
|
|
@@ -5839,7 +6215,7 @@ var VfsNodes = class {
|
|
|
5839
6215
|
}
|
|
5840
6216
|
async unlinking(path) {
|
|
5841
6217
|
const node = this.byPath.get(path);
|
|
5842
|
-
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()]));
|
|
5843
6219
|
}
|
|
5844
6220
|
revoke(prefix) {
|
|
5845
6221
|
for (const [path, node] of this.byPath) {
|
|
@@ -6988,7 +7364,8 @@ async function dispatch(h, req) {
|
|
|
6988
7364
|
case "setattr":
|
|
6989
7365
|
return h.setattr?.(req.path, {
|
|
6990
7366
|
...req.mode !== void 0 ? { mode: req.mode } : {},
|
|
6991
|
-
...req.mtime !== void 0 ? { mtime: req.mtime } : {}
|
|
7367
|
+
...req.mtime !== void 0 ? { mtime: req.mtime } : {},
|
|
7368
|
+
...req.size !== void 0 ? { size: req.size } : {}
|
|
6992
7369
|
});
|
|
6993
7370
|
case "statfs":
|
|
6994
7371
|
return h.statfs?.() ?? null;
|
|
@@ -6996,7 +7373,7 @@ async function dispatch(h, req) {
|
|
|
6996
7373
|
throw fsError2("ENOSYS", `unknown driver op ${req.op}`);
|
|
6997
7374
|
}
|
|
6998
7375
|
}
|
|
6999
|
-
function serveFilesystem(port, handlers, capabilities = {}) {
|
|
7376
|
+
function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
|
|
7000
7377
|
let open = false;
|
|
7001
7378
|
const reply = (message2, transfer = []) => port.postMessage(message2, transfer);
|
|
7002
7379
|
async function hello(message2) {
|
|
@@ -7032,7 +7409,8 @@ function serveFilesystem(port, handlers, capabilities = {}) {
|
|
|
7032
7409
|
dispatch(handlers, data).then(
|
|
7033
7410
|
(result) => {
|
|
7034
7411
|
if (result instanceof Uint8Array) {
|
|
7035
|
-
const
|
|
7412
|
+
const whole = result.byteOffset === 0 && result.byteLength === result.buffer.byteLength;
|
|
7413
|
+
const bytes = options.owned && whole ? result : result.slice();
|
|
7036
7414
|
reply({ id: data.id, result: bytes }, [bytes.buffer]);
|
|
7037
7415
|
} else reply({ id: data.id, result: result ?? null });
|
|
7038
7416
|
},
|
|
@@ -7698,6 +8076,58 @@ async function openHostfs(spec, hook, fetch2, timing = {}) {
|
|
|
7698
8076
|
};
|
|
7699
8077
|
}
|
|
7700
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
|
+
|
|
7701
8131
|
// src/mount/tmpfs.ts
|
|
7702
8132
|
function tmpfs() {
|
|
7703
8133
|
let inodes = 0;
|
|
@@ -7706,7 +8136,7 @@ function tmpfs() {
|
|
|
7706
8136
|
ino: ++inodes,
|
|
7707
8137
|
mode,
|
|
7708
8138
|
mtime: Date.now(),
|
|
7709
|
-
data: new
|
|
8139
|
+
data: new ChunkedBytes(),
|
|
7710
8140
|
target: "",
|
|
7711
8141
|
children: /* @__PURE__ */ new Map()
|
|
7712
8142
|
});
|
|
@@ -7734,7 +8164,7 @@ function tmpfs() {
|
|
|
7734
8164
|
}
|
|
7735
8165
|
const attr = (node) => ({
|
|
7736
8166
|
kind: node.kind,
|
|
7737
|
-
size: node.kind === "file" ? node.data.
|
|
8167
|
+
size: node.kind === "file" ? node.data.size : node.kind === "symlink" ? node.target.length : 4096,
|
|
7738
8168
|
mtime: node.mtime,
|
|
7739
8169
|
mode: node.mode,
|
|
7740
8170
|
ino: node.ino
|
|
@@ -7769,32 +8199,25 @@ function tmpfs() {
|
|
|
7769
8199
|
},
|
|
7770
8200
|
async open(path, flags) {
|
|
7771
8201
|
const node = opened2(path, flags);
|
|
7772
|
-
|
|
7773
|
-
|
|
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 });
|
|
7774
8207
|
return nextFh;
|
|
7775
8208
|
},
|
|
7776
8209
|
async read(fh, offset, size) {
|
|
7777
|
-
|
|
7778
|
-
return h.data.slice(offset, Math.min(h.length, offset + size));
|
|
8210
|
+
return handle(fh).node.data.read(offset, size);
|
|
7779
8211
|
},
|
|
7780
8212
|
async write(fh, offset, bytes) {
|
|
7781
8213
|
const h = handle(fh);
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
grown.set(h.data.subarray(0, h.length));
|
|
7786
|
-
h.data = grown;
|
|
7787
|
-
}
|
|
7788
|
-
h.data.set(bytes, offset);
|
|
7789
|
-
h.length = Math.max(h.length, end);
|
|
7790
|
-
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();
|
|
7791
8217
|
},
|
|
7792
8218
|
async release(fh) {
|
|
7793
|
-
|
|
8219
|
+
handle(fh);
|
|
7794
8220
|
handles.delete(fh);
|
|
7795
|
-
if (!h.dirty) return;
|
|
7796
|
-
h.node.data = h.data.slice(0, h.length);
|
|
7797
|
-
h.node.mtime = Date.now();
|
|
7798
8221
|
},
|
|
7799
8222
|
async mkdir(path) {
|
|
7800
8223
|
const { dir, name: name2 } = parent(path);
|
|
@@ -7847,6 +8270,12 @@ function tmpfs() {
|
|
|
7847
8270
|
async setattr(path, change) {
|
|
7848
8271
|
const node = find(path);
|
|
7849
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
|
+
}
|
|
7850
8279
|
if (change.mtime !== void 0) node.mtime = change.mtime;
|
|
7851
8280
|
},
|
|
7852
8281
|
async statfs() {
|
|
@@ -8437,7 +8866,12 @@ var Launcher = class {
|
|
|
8437
8866
|
}
|
|
8438
8867
|
if (type === "tmpfs") {
|
|
8439
8868
|
const { port1, port2 } = new MessageChannel();
|
|
8440
|
-
serveFilesystem(
|
|
8869
|
+
serveFilesystem(
|
|
8870
|
+
port2,
|
|
8871
|
+
tmpfs(),
|
|
8872
|
+
{ symlinks: true, chmod: true, ranges: true, attrTtl: 0 },
|
|
8873
|
+
{ owned: true }
|
|
8874
|
+
);
|
|
8441
8875
|
return {
|
|
8442
8876
|
port: port1,
|
|
8443
8877
|
dispose: () => {
|