@poe-platform/safe-fs 0.1.731 → 0.1.733

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.
@@ -15,7 +15,7 @@ import { MemoryAllocation, MemoryLedger } from "./ledger.js";
15
15
  import { normalizeMemoryFileSystemLimits } from "./limits.js";
16
16
  export { defaultMemoryFileSystemLimits } from "./limits.js";
17
17
  class MemoryFileNode {
18
- constructor(mode, ino, now, byteLength, allocation, view) {
18
+ constructor(mode, ino, now, byteLength, allocation, view, sourceRef) {
19
19
  this.revision = 0;
20
20
  this.mode = mode;
21
21
  this.ino = ino;
@@ -28,6 +28,7 @@ class MemoryFileNode {
28
28
  this.byteLength = byteLength;
29
29
  this.allocation = allocation;
30
30
  this.view = view;
31
+ this.sourceRef = sourceRef;
31
32
  }
32
33
  get data() {
33
34
  return this.view ??= (this.byteLength === this.allocation.data.byteLength ? this.allocation.data : this.allocation.data.subarray(0, this.byteLength));
@@ -42,6 +43,7 @@ Object.assign(MemoryFileNode.prototype, {
42
43
  revision: 0,
43
44
  nlink: 1,
44
45
  references: 0,
46
+ sourceRef: undefined,
45
47
  });
46
48
  const EMPTY_ALLOC_BYTES = new Uint8Array(0);
47
49
  const SMALL_ALLOC_SLAB_SIZE = 8192;
@@ -54,6 +56,9 @@ DUMMY_POOL_FILE_NODE.atimeMs = DUMMY_POOL_FILE_NODE.mtimeMs = DUMMY_POOL_FILE_NO
54
56
  const defaultDateNow = Date.now;
55
57
  let fastWriteCachedNow = Date.now();
56
58
  let fastWriteNowTick = 0;
59
+ const lastFastMapMiss = {
60
+ map: undefined, key: "", slot: -1,
61
+ };
57
62
  class FastDirectoryEntriesMap {
58
63
  _table = new Int16Array(128).fill(-1);
59
64
  _mask = 127;
@@ -85,6 +90,72 @@ class FastDirectoryEntriesMap {
85
90
  slot = (slot + 1) & mask;
86
91
  }
87
92
  }
93
+ getForWrite(k) {
94
+ return this.getForWriteWithHash(k, this._hash(k));
95
+ }
96
+ getForWriteWithHash(k, h) {
97
+ let slot = h & this._mask;
98
+ if (this.size === 0) {
99
+ lastFastMapMiss.map = this;
100
+ lastFastMapMiss.key = k;
101
+ lastFastMapMiss.slot = slot;
102
+ return undefined;
103
+ }
104
+ const mask = this._mask;
105
+ const table = this._table;
106
+ const keys = this._keys;
107
+ let firstDeleted = -1;
108
+ while (true) {
109
+ const idx = table[slot];
110
+ if (idx === -1) {
111
+ lastFastMapMiss.map = this;
112
+ lastFastMapMiss.key = k;
113
+ lastFastMapMiss.slot = firstDeleted !== -1 ? firstDeleted : slot;
114
+ return undefined;
115
+ }
116
+ if (idx === -2) {
117
+ if (firstDeleted === -1)
118
+ firstDeleted = slot;
119
+ }
120
+ else if (keys[idx] === k) {
121
+ return this._vals[idx];
122
+ }
123
+ slot = (slot + 1) & mask;
124
+ }
125
+ }
126
+ getSubstr(s, start, end) {
127
+ if (this.size === 0)
128
+ return undefined;
129
+ const len = end - start;
130
+ let h = 5381;
131
+ for (let i = start; i < end; i++) {
132
+ h = (((h & 0x1fffff) * 33) ^ s.charCodeAt(i)) & 0x3fffffff;
133
+ }
134
+ let slot = h & this._mask;
135
+ const mask = this._mask;
136
+ const table = this._table;
137
+ const keys = this._keys;
138
+ while (true) {
139
+ const idx = table[slot];
140
+ if (idx === -1)
141
+ return undefined;
142
+ if (idx >= 0) {
143
+ const k = keys[idx];
144
+ if (k.length === len) {
145
+ let match = true;
146
+ for (let i = 0; i < len; i++) {
147
+ if (k.charCodeAt(i) !== s.charCodeAt(start + i)) {
148
+ match = false;
149
+ break;
150
+ }
151
+ }
152
+ if (match)
153
+ return this._vals[idx];
154
+ }
155
+ }
156
+ slot = (slot + 1) & mask;
157
+ }
158
+ }
88
159
  has(k) {
89
160
  return this.get(k) !== undefined;
90
161
  }
@@ -128,6 +199,15 @@ class FastDirectoryEntriesMap {
128
199
  this._next = writeIdx;
129
200
  }
130
201
  set(k, v) {
202
+ if (this === lastFastMapMiss.map && k === lastFastMapMiss.key && this._next < this._keys.length) {
203
+ const entryIdx = this._next++;
204
+ this._table[lastFastMapMiss.slot] = entryIdx;
205
+ this._keys[entryIdx] = k;
206
+ this._vals[entryIdx] = v;
207
+ this.size++;
208
+ lastFastMapMiss.map = undefined;
209
+ return this;
210
+ }
131
211
  if (this._next >= this._keys.length) {
132
212
  this._rebuild(this.size * 2 <= this._keys.length ? this._keys.length : this._keys.length * 2);
133
213
  }
@@ -238,6 +318,11 @@ const SHARED_POOL_CAPACITY = 256;
238
318
  const SHARED_DIR_POOL_CAPACITY = 64;
239
319
  const sharedAllocationPool = new Array(SHARED_POOL_CAPACITY).fill(DUMMY_POOL_ALLOCATION);
240
320
  let sharedAllocationPoolLen = 0;
321
+ const SHARED_LARGE_POOL_CAPACITY = 8;
322
+ const sharedLargeAllocationPool = new Array(SHARED_LARGE_POOL_CAPACITY).fill(DUMMY_POOL_ALLOCATION);
323
+ let sharedLargeAllocationPoolLen = 0;
324
+ const sharedMediumAllocationPool = new Array(SHARED_LARGE_POOL_CAPACITY).fill(DUMMY_POOL_ALLOCATION);
325
+ let sharedMediumAllocationPoolLen = 0;
241
326
  const sharedFileNodePool = new Array(SHARED_POOL_CAPACITY).fill(DUMMY_POOL_FILE_NODE);
242
327
  let sharedFileNodePoolLen = 0;
243
328
  const sharedDirectoryNodePool = new Array(SHARED_DIR_POOL_CAPACITY);
@@ -273,6 +358,18 @@ function replenishSharedMemoryPools() {
273
358
  while (sharedFileNodePoolLen < 112) {
274
359
  sharedFileNodePool[sharedFileNodePoolLen++] = new MemoryFileNode(0, 0, fastWriteCachedNow, 0, DUMMY_POOL_ALLOCATION, undefined);
275
360
  }
361
+ while (sharedLargeAllocationPoolLen < 4) {
362
+ DUMMY_POOL_LEDGER.reserve(65536, 0, "init", "/");
363
+ const alloc = new MemoryAllocation(new Uint8Array(65536), DUMMY_POOL_LEDGER);
364
+ alloc.release();
365
+ sharedLargeAllocationPool[sharedLargeAllocationPoolLen++] = alloc;
366
+ }
367
+ while (sharedMediumAllocationPoolLen < 4) {
368
+ DUMMY_POOL_LEDGER.reserve(16384, 0, "init", "/");
369
+ const alloc = new MemoryAllocation(new Uint8Array(16384), DUMMY_POOL_LEDGER);
370
+ alloc.release();
371
+ sharedMediumAllocationPool[sharedMediumAllocationPoolLen++] = alloc;
372
+ }
276
373
  while (sharedDirectoryNodePoolLen < 16) {
277
374
  sharedDirectoryNodePool[sharedDirectoryNodePoolLen++] = new MemoryDirectoryNode(0, 0, fastWriteCachedNow);
278
375
  }
@@ -309,10 +406,10 @@ class MemoryCache {
309
406
  directories = [];
310
407
  removedScratch = [];
311
408
  lastFastDirPrefix = "";
312
- lastFastDirNode;
409
+ lastFastDirNode = undefined;
313
410
  lastFastFilePath = "";
314
411
  lastFastFileName = "";
315
- lastFastFileNode;
412
+ lastFastFileNode = undefined;
316
413
  clearWrites() {
317
414
  this.lastFastDirPrefix = "";
318
415
  this.lastFastDirNode = undefined;
@@ -527,24 +624,31 @@ export class MemoryFileSystem {
527
624
  identityScope = Symbol();
528
625
  nextInode = 1;
529
626
  ledger;
627
+ _cache;
628
+ mutationTick = 0;
629
+ _owner;
530
630
  root;
531
631
  totalBytes = 0;
532
632
  symlinkCount = 0;
533
633
  constructor(options = {}) {
534
634
  this.ledger = new MemoryLedger(normalizeMemoryFileSystemLimits(options));
535
- if (this.ledger.hasInfiniteRetained && this.ledger.limits.maxFileBytes === Infinity) {
635
+ if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes) {
536
636
  replenishSharedMemoryPools();
537
637
  }
538
- memoryCaches.set(this.ledger, new MemoryCache());
638
+ const cache = new MemoryCache();
639
+ this._cache = cache;
640
+ memoryCaches.set(this.ledger, cache);
539
641
  this.ledger.reserve(0, 1, "mkdir", "/");
540
642
  this.root = this.directory(0o755);
541
643
  const root = this.root;
542
- ownedStores.set(this, {
644
+ const owner = {
543
645
  root,
544
646
  ledger: this.ledger,
545
647
  capabilities: this.capabilities,
546
648
  intact: () => this.root === root,
547
- });
649
+ };
650
+ this._owner = owner;
651
+ ownedStores.set(this, owner);
548
652
  registerMemoryAtomicView(this, {
549
653
  stat: (path) => {
550
654
  this.validatePath(path, "overlayAtomicView");
@@ -781,17 +885,24 @@ export class MemoryFileSystem {
781
885
  this.totalBytes -= node.byteLength;
782
886
  const alloc = node.allocation;
783
887
  node.view = undefined;
888
+ node.sourceRef = undefined;
784
889
  node.allocation = DUMMY_POOL_ALLOCATION;
785
890
  alloc.release();
786
891
  if (alloc.isReleased64()) {
787
892
  alloc.detachLedger(DUMMY_POOL_LEDGER);
788
- if (this.ledger.hasInfiniteRetained && this.ledger.limits.maxFileBytes === Infinity && sharedAllocationPoolLen < SHARED_POOL_CAPACITY) {
893
+ if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && sharedAllocationPoolLen < SHARED_POOL_CAPACITY) {
789
894
  sharedAllocationPool[sharedAllocationPoolLen++] = alloc;
790
895
  }
791
896
  else if (cache.allocations.length < 128) {
792
897
  cache.allocations.push(alloc);
793
898
  }
794
899
  }
900
+ else if (alloc.isReleased65536()) {
901
+ alloc.detachLedger(DUMMY_POOL_LEDGER);
902
+ if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && sharedLargeAllocationPoolLen < SHARED_LARGE_POOL_CAPACITY) {
903
+ sharedLargeAllocationPool[sharedLargeAllocationPoolLen++] = alloc;
904
+ }
905
+ }
795
906
  if (sharedFileNodePoolLen < SHARED_POOL_CAPACITY) {
796
907
  sharedFileNodePool[sharedFileNodePoolLen++] = node;
797
908
  }
@@ -829,6 +940,18 @@ export class MemoryFileSystem {
829
940
  node.view = length === allocation.data.byteLength ? allocation.data : undefined;
830
941
  node.allocation = allocation;
831
942
  previous.release();
943
+ if (previous.isReleased64()) {
944
+ previous.detachLedger(DUMMY_POOL_LEDGER);
945
+ if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && sharedAllocationPoolLen < SHARED_POOL_CAPACITY) {
946
+ sharedAllocationPool[sharedAllocationPoolLen++] = previous;
947
+ }
948
+ }
949
+ else if (previous.isReleased65536()) {
950
+ previous.detachLedger(DUMMY_POOL_LEDGER);
951
+ if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && sharedLargeAllocationPoolLen < SHARED_LARGE_POOL_CAPACITY) {
952
+ sharedLargeAllocationPool[sharedLargeAllocationPoolLen++] = previous;
953
+ }
954
+ }
832
955
  }
833
956
  admitSize(node, length, syscall, path) {
834
957
  this.ledger.fileSize(length, syscall, path);
@@ -861,6 +984,7 @@ export class MemoryFileSystem {
861
984
  this.fail("EINVAL", syscall, path);
862
985
  }
863
986
  changed(node, now = Date.now()) {
987
+ this.mutationTick = (this.mutationTick + 1) | 0;
864
988
  node.revision = node.revision < 1073741823 ? (node.revision + 1) | 0 : Math.min(Number.MAX_SAFE_INTEGER + 1, node.revision + 1);
865
989
  node.mtimeMs = node.ctimeMs = now;
866
990
  }
@@ -870,7 +994,7 @@ export class MemoryFileSystem {
870
994
  const fastDir = cache.lastFastDirNode;
871
995
  const fastPrefix = cache.lastFastDirPrefix;
872
996
  if (fastDir !== undefined && fastDir.nlink !== 0 && fastPrefix.length > 0 && path.startsWith(fastPrefix)) {
873
- const name = path.slice(fastPrefix.length);
997
+ const name = fastPrefix.length === 1 ? (path === _lastFileSlice1Path ? _lastFileSlice1Name : (_lastFileSlice1Path = path, _lastFileSlice1Name = path.slice(1))) : path.slice(fastPrefix.length);
874
998
  if (name.length > 0 && !name.includes("/")) {
875
999
  this.permission(fastDir, 1, syscall, path);
876
1000
  if (exceedsComponentByteLimit(name))
@@ -888,9 +1012,9 @@ export class MemoryFileSystem {
888
1012
  const slash = path.indexOf("/", start);
889
1013
  if (slash === -1) {
890
1014
  cache.clearWrites();
891
- cache.lastFastDirPrefix = path.slice(0, start);
1015
+ cache.lastFastDirPrefix = start === 1 ? "/" : path.slice(0, start);
892
1016
  cache.lastFastDirNode = current;
893
- const name = path.slice(start);
1017
+ const name = start === 1 ? (path === _lastFileSlice1Path ? _lastFileSlice1Name : (_lastFileSlice1Path = path, _lastFileSlice1Name = path.slice(1))) : path.slice(start);
894
1018
  if (exceedsComponentByteLimit(name))
895
1019
  this.fail("ENAMETOOLONG", syscall, path);
896
1020
  const node = current.entries.get(name);
@@ -898,7 +1022,7 @@ export class MemoryFileSystem {
898
1022
  this.fail("ENOENT", syscall, path);
899
1023
  return node;
900
1024
  }
901
- const name = path.slice(start, slash);
1025
+ const name = slicePathSegment(path, start, slash);
902
1026
  if (exceedsComponentByteLimit(name))
903
1027
  this.fail("ENAMETOOLONG", syscall, path);
904
1028
  const next = current.entries.get(name);
@@ -922,7 +1046,7 @@ export class MemoryFileSystem {
922
1046
  this.permission(current, 1, syscall, path);
923
1047
  const slash = path.indexOf("/", start);
924
1048
  if (slash === -1) {
925
- const name = path.slice(start);
1049
+ const name = slicePathSegment(path, start, path.length);
926
1050
  if (exceedsComponentByteLimit(name))
927
1051
  this.fail("ENAMETOOLONG", syscall, path);
928
1052
  let node = current.entries.get(name);
@@ -1043,6 +1167,20 @@ export class MemoryFileSystem {
1043
1167
  };
1044
1168
  }
1045
1169
  file(path, syscall) {
1170
+ if (this.symlinkCount === 0 && path.charCodeAt(0) === 47 && path.length > 1 && path.indexOf("/", 1) === -1 && !path.includes("\0")) {
1171
+ const name = path === _lastFileSlice1Path ? _lastFileSlice1Name : (_lastFileSlice1Path = path, _lastFileSlice1Name = path.slice(1));
1172
+ if (name !== "." && name !== "..") {
1173
+ this.permission(this.root, 1, syscall, path);
1174
+ if (exceedsComponentByteLimit(name))
1175
+ this.fail("ENAMETOOLONG", syscall, path);
1176
+ const node = this.root.entries.get(name);
1177
+ if (!node)
1178
+ this.fail("ENOENT", syscall, path);
1179
+ if (node.type !== "file")
1180
+ this.fail("EISDIR", syscall, path);
1181
+ return node;
1182
+ }
1183
+ }
1046
1184
  const cache = memoryCaches.get(this.ledger);
1047
1185
  if (this.symlinkCount === 0 && isCleanAbsolutePath(path)) {
1048
1186
  const fastDir = cache.lastFastDirNode;
@@ -1185,9 +1323,25 @@ export class MemoryFileSystem {
1185
1323
  if (length === 0) {
1186
1324
  return DUMMY_POOL_ALLOCATION;
1187
1325
  }
1326
+ if (length === 16384) {
1327
+ if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && sharedMediumAllocationPoolLen > 0) {
1328
+ const pooled = sharedMediumAllocationPool[--sharedMediumAllocationPoolLen];
1329
+ sharedMediumAllocationPool[sharedMediumAllocationPoolLen] = DUMMY_POOL_ALLOCATION;
1330
+ pooled.reuse(this.ledger);
1331
+ return pooled;
1332
+ }
1333
+ }
1334
+ if (length === 65536) {
1335
+ if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && sharedLargeAllocationPoolLen > 0) {
1336
+ const pooled = sharedLargeAllocationPool[--sharedLargeAllocationPoolLen];
1337
+ sharedLargeAllocationPool[sharedLargeAllocationPoolLen] = DUMMY_POOL_ALLOCATION;
1338
+ pooled.reuse(this.ledger);
1339
+ return pooled;
1340
+ }
1341
+ }
1188
1342
  if (length === 64) {
1189
1343
  let pooled;
1190
- if (this.ledger.hasInfiniteRetained && this.ledger.limits.maxFileBytes === Infinity && sharedAllocationPoolLen > 0) {
1344
+ if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && sharedAllocationPoolLen > 0) {
1191
1345
  pooled = sharedAllocationPool[--sharedAllocationPoolLen];
1192
1346
  sharedAllocationPool[sharedAllocationPoolLen] = DUMMY_POOL_ALLOCATION;
1193
1347
  }
@@ -1202,7 +1356,7 @@ export class MemoryFileSystem {
1202
1356
  try {
1203
1357
  // A shared slab's backing buffer exceeds the admitted slice capacity.
1204
1358
  // Finite file/storage ceilings require independently owned exact buffers.
1205
- if (length <= 64 && this.ledger.hasInfiniteRetained && this.ledger.limits.maxFileBytes === Infinity) {
1359
+ if (length <= 64 && this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes) {
1206
1360
  if (smallAllocOffset + length > SMALL_ALLOC_SLAB_SIZE) {
1207
1361
  smallAllocSlab = new Uint8Array(SMALL_ALLOC_SLAB_SIZE);
1208
1362
  smallAllocOffset = 0;
@@ -1334,9 +1488,10 @@ export class MemoryFileSystem {
1334
1488
  pooled.byteLength = length;
1335
1489
  pooled.view = view;
1336
1490
  pooled.allocation = allocation;
1491
+ pooled.sourceRef = data;
1337
1492
  return pooled;
1338
1493
  }
1339
- return new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view);
1494
+ return new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view, data);
1340
1495
  }, syscall, path);
1341
1496
  }
1342
1497
  catch (error) {
@@ -1371,27 +1526,37 @@ export class MemoryFileSystem {
1371
1526
  let allocation = node.allocation;
1372
1527
  if (length > allocation.data.byteLength) {
1373
1528
  this.ledger.check(length, 0, syscall, path);
1374
- const baseCap = curLen === 0 && length >= 8192 && length <= 16384 &&
1375
- this.ledger.limits.maxFileBytes >= 65536 &&
1376
- this.ledger.availableBytes >= 65536 &&
1377
- (this.ledger.limits.maxBytes === undefined || this.ledger.limits.maxBytes - this.totalBytes >= 65536)
1378
- ? 65536
1379
- : Math.max(length, curLen * 2, 64);
1380
- const capacity = Math.min(baseCap, this.ledger.limits.maxFileBytes, this.ledger.availableBytes);
1529
+ let capacity;
1530
+ if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && this.ledger.limits.maxBytes === undefined) {
1531
+ capacity = curLen === 0 && length >= 8192 && length <= 65536 ? (length <= 16384 ? 16384 : 65536) : Math.max(length, curLen * 2, 64);
1532
+ }
1533
+ else {
1534
+ const baseCap = curLen === 0 && length >= 8192 && length <= 65536 &&
1535
+ this.ledger.limits.maxFileBytes >= 65536 &&
1536
+ this.ledger.availableBytes >= 65536 &&
1537
+ (this.ledger.limits.maxBytes === undefined || this.ledger.limits.maxBytes - this.totalBytes >= 65536)
1538
+ ? 65536
1539
+ : Math.max(length, curLen * 2, 64);
1540
+ capacity = Math.min(baseCap, this.ledger.limits.maxFileBytes, this.ledger.availableBytes);
1541
+ }
1381
1542
  allocation = this.allocate(capacity, syscall, path);
1382
1543
  }
1383
1544
  try {
1384
1545
  const storage = allocation.data;
1385
1546
  if (allocation !== node.allocation && curLen > 0) {
1386
- storage.set(node.allocation.data.subarray(0, curLen));
1547
+ storage.set((node.view ?? node.allocation.data).subarray(0, curLen));
1387
1548
  }
1388
1549
  if (position > curLen)
1389
1550
  storage.fill(0, curLen, position);
1390
1551
  if (dataLen === data.byteLength) {
1391
1552
  storage.set(data, position);
1392
1553
  }
1554
+ else if (typeof data.copy === "function") {
1555
+ data.copy(storage, position, 0, dataLen);
1556
+ }
1393
1557
  else {
1394
- storage.set(data.subarray(0, dataLen), position);
1558
+ for (let i = 0; i < dataLen; i++)
1559
+ storage[position + i] = data[i];
1395
1560
  }
1396
1561
  }
1397
1562
  catch (error) {
@@ -1457,7 +1622,7 @@ export class MemoryFileSystem {
1457
1622
  }
1458
1623
  break;
1459
1624
  }
1460
- const seg = path.slice(start, slash);
1625
+ const seg = slicePathSegment(path, start, slash);
1461
1626
  if (exceedsComponentByteLimit(seg))
1462
1627
  this.fail("ENAMETOOLONG", syscall, path);
1463
1628
  const next = parent.entries.get(seg);
@@ -1469,7 +1634,7 @@ export class MemoryFileSystem {
1469
1634
  start = slash + 1;
1470
1635
  }
1471
1636
  }
1472
- const existing = parent.entries.get(name);
1637
+ const existing = parent.entries.getForWrite ? parent.entries.getForWrite(name) : parent.entries.get(name);
1473
1638
  if (existing) {
1474
1639
  if (existing.type !== "file")
1475
1640
  this.fail("EISDIR", syscall, path);
@@ -1492,7 +1657,7 @@ export class MemoryFileSystem {
1492
1657
  return;
1493
1658
  }
1494
1659
  if (!current) {
1495
- const capacity = length > 0 && length < 64 && this.ledger.hasInfiniteRetained && this.ledger.limits.maxFileBytes === Infinity ? 64 : length;
1660
+ const capacity = length > 0 && length < 64 && this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes ? 64 : length;
1496
1661
  const allocation = this.allocate(capacity, syscall, path);
1497
1662
  try {
1498
1663
  allocation.data.set(data);
@@ -1522,9 +1687,10 @@ export class MemoryFileSystem {
1522
1687
  node.byteLength = length;
1523
1688
  node.view = view;
1524
1689
  node.allocation = allocation;
1690
+ node.sourceRef = data;
1525
1691
  }
1526
1692
  else {
1527
- node = new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view);
1693
+ node = new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view, data);
1528
1694
  }
1529
1695
  const prevNlink = parent.cachedNlinkRev === parent.revision ? parent.cachedNlink : (parent.entries.size === 0 ? 2 : undefined);
1530
1696
  parent.entries.set(name, node);
@@ -1562,8 +1728,133 @@ export class MemoryFileSystem {
1562
1728
  throw error;
1563
1729
  }
1564
1730
  }
1565
- writeMemoryFileInDirFast(dirPrefix, name, data, append, mode) {
1566
- const cache = memoryCaches.get(this.ledger);
1731
+ writeMemoryFilesInDirBatchFast(firstFileName, dirPrefix, fileNames, nameHashes, payloads, totalNameBytes, payloadBytesSum, mode) {
1732
+ const batchCount = fileNames.length;
1733
+ const cache = this._cache;
1734
+ if (batchCount > 0 &&
1735
+ dirPrefix === cache.lastFastDirPrefix &&
1736
+ cache.lastFastDirNode !== undefined &&
1737
+ cache.lastFastDirNode.nlink !== 0 &&
1738
+ (cache.lastFastDirNode.mode & 0o300) === 0o300 &&
1739
+ this.ledger.hasInfiniteRetained &&
1740
+ this.ledger.hasInfiniteFileBytes &&
1741
+ this.ledger.limits.maxBytes === undefined &&
1742
+ sharedFileNodePoolLen >= batchCount) {
1743
+ const parent = cache.lastFastDirNode;
1744
+ const entries = parent.entries;
1745
+ if (entries.size === 1 &&
1746
+ entries._next === 1 &&
1747
+ entries._keys[0] === firstFileName &&
1748
+ 1 + batchCount <= entries._keys.length) {
1749
+ this.ledger.reserve(totalNameBytes, 2 * batchCount, "writeFile", fileNames[batchCount - 1]);
1750
+ this.totalBytes += payloadBytesSum;
1751
+ const now = fastWriteCachedNow;
1752
+ const fileMode = typeModes.file | mode;
1753
+ let ino = this.nextInode;
1754
+ this.nextInode = ino + batchCount;
1755
+ let nextIdx = 1;
1756
+ entries._next = 1 + batchCount;
1757
+ entries.size = 1 + batchCount;
1758
+ const table = entries._table;
1759
+ const mask = table.length - 1;
1760
+ const keys = entries._keys;
1761
+ const vals = entries._vals;
1762
+ for (let k = 0; k < batchCount; k++) {
1763
+ const name = fileNames[k];
1764
+ const data = payloads[k];
1765
+ const node = sharedFileNodePool[--sharedFileNodePoolLen];
1766
+ sharedFileNodePool[sharedFileNodePoolLen] = DUMMY_POOL_FILE_NODE;
1767
+ node.mode = fileMode;
1768
+ node.ino = ino++;
1769
+ if (node.nlink !== 1)
1770
+ node.nlink = 1;
1771
+ if (node.references !== 0)
1772
+ node.references = 0;
1773
+ if (node.revision !== 0)
1774
+ node.revision = 0;
1775
+ node.atimeMs = now;
1776
+ node.mtimeMs = now;
1777
+ node.ctimeMs = now;
1778
+ node.birthtimeMs = now;
1779
+ node.byteLength = data.byteLength;
1780
+ node.view = data;
1781
+ node.allocation = DUMMY_POOL_ALLOCATION;
1782
+ node.sourceRef = data;
1783
+ const entryIdx = nextIdx++;
1784
+ keys[entryIdx] = name;
1785
+ vals[entryIdx] = node;
1786
+ let slot = nameHashes[k] & mask;
1787
+ while (table[slot] !== -1)
1788
+ slot = (slot + 1) & mask;
1789
+ table[slot] = entryIdx;
1790
+ }
1791
+ this.mutationTick = (this.mutationTick + 1) | 0;
1792
+ parent.revision = parent.revision < 1073741000 ? (parent.revision + batchCount) | 0 : Math.min(Number.MAX_SAFE_INTEGER + 1, parent.revision + batchCount);
1793
+ parent.mtimeMs = parent.ctimeMs = now;
1794
+ cache.lastFastFilePath = "";
1795
+ cache.lastFastFileName = fileNames[batchCount - 1];
1796
+ cache.lastFastFileNode = vals[nextIdx - 1];
1797
+ return;
1798
+ }
1799
+ }
1800
+ for (let k = 0; k < batchCount; k++) {
1801
+ this.writeMemoryFileInDirFast(dirPrefix, fileNames[k], payloads[k], false, mode, nameHashes[k]);
1802
+ }
1803
+ }
1804
+ writeMemoryFileInDirFast(dirPrefix, name, data, append, mode, nameHash) {
1805
+ const cache = this._cache;
1806
+ if (!append &&
1807
+ dirPrefix === cache.lastFastDirPrefix &&
1808
+ cache.lastFastDirNode !== undefined &&
1809
+ cache.lastFastDirNode.nlink !== 0 &&
1810
+ (cache.lastFastDirNode.mode & 0o300) === 0o300 &&
1811
+ name.length <= 85 &&
1812
+ this.ledger.hasInfiniteRetained &&
1813
+ this.ledger.hasInfiniteFileBytes &&
1814
+ this.ledger.limits.maxBytes === undefined &&
1815
+ sharedFileNodePoolLen > 0) {
1816
+ const parent = cache.lastFastDirNode;
1817
+ const entries = parent.entries;
1818
+ const existing = nameHash !== undefined ? entries.getForWriteWithHash(name, nameHash) : entries.getForWrite(name);
1819
+ if (existing === undefined && entries._next < entries._keys.length) {
1820
+ const now = fastWriteCachedNow;
1821
+ const length = data.byteLength;
1822
+ const node = sharedFileNodePool[--sharedFileNodePoolLen];
1823
+ sharedFileNodePool[sharedFileNodePoolLen] = DUMMY_POOL_FILE_NODE;
1824
+ node.mode = typeModes.file | mode;
1825
+ node.ino = this.nextInode++;
1826
+ if (node.nlink !== 1)
1827
+ node.nlink = 1;
1828
+ if (node.references !== 0)
1829
+ node.references = 0;
1830
+ if (node.revision !== 0)
1831
+ node.revision = 0;
1832
+ node.atimeMs = now;
1833
+ node.mtimeMs = now;
1834
+ node.ctimeMs = now;
1835
+ node.birthtimeMs = now;
1836
+ node.byteLength = length;
1837
+ node.view = data;
1838
+ node.allocation = DUMMY_POOL_ALLOCATION;
1839
+ node.sourceRef = data;
1840
+ const entryIdx = entries._next++;
1841
+ entries._table[lastFastMapMiss.slot] = entryIdx;
1842
+ entries._keys[entryIdx] = name;
1843
+ entries._vals[entryIdx] = node;
1844
+ entries.size++;
1845
+ lastFastMapMiss.map = undefined;
1846
+ lastFastMapMiss.key = "";
1847
+ this.ledger.reserve(name.length * 2, 2, "writeFile", name);
1848
+ this.totalBytes += length;
1849
+ this.mutationTick = (this.mutationTick + 1) | 0;
1850
+ parent.revision = parent.revision < 1073741823 ? (parent.revision + 1) | 0 : Math.min(Number.MAX_SAFE_INTEGER + 1, parent.revision + 1);
1851
+ parent.mtimeMs = parent.ctimeMs = now;
1852
+ cache.lastFastFilePath = "";
1853
+ cache.lastFastFileName = name;
1854
+ cache.lastFastFileNode = node;
1855
+ return;
1856
+ }
1857
+ }
1567
1858
  const syscall = append ? "appendFile" : "writeFile";
1568
1859
  try {
1569
1860
  if (append &&
@@ -1598,7 +1889,7 @@ export class MemoryFileSystem {
1598
1889
  while (start < dirPrefix.length) {
1599
1890
  this.permission(parent, 1, syscall, name);
1600
1891
  const slash = dirPrefix.indexOf("/", start);
1601
- const seg = slash === -1 ? dirPrefix.slice(start) : dirPrefix.slice(start, slash);
1892
+ const seg = slash === -1 ? slicePathSegment(dirPrefix, start, dirPrefix.length) : slicePathSegment(dirPrefix, start, slash);
1602
1893
  if (exceedsComponentByteLimit(seg))
1603
1894
  this.fail("ENAMETOOLONG", syscall, name);
1604
1895
  const next = parent.entries.get(seg);
@@ -1619,7 +1910,7 @@ export class MemoryFileSystem {
1619
1910
  cache.lastFastDirNode = parent;
1620
1911
  }
1621
1912
  }
1622
- const existing = parent.entries.get(name);
1913
+ const existing = parent.entries.getForWrite ? parent.entries.getForWrite(name) : parent.entries.get(name);
1623
1914
  if (existing) {
1624
1915
  if (existing.type !== "file")
1625
1916
  this.fail("EISDIR", syscall, name);
@@ -1642,15 +1933,17 @@ export class MemoryFileSystem {
1642
1933
  return;
1643
1934
  }
1644
1935
  if (!current) {
1645
- const capacity = length > 0 && length < 64 && this.ledger.hasInfiniteRetained && this.ledger.limits.maxFileBytes === Infinity ? 64 : length;
1646
- const allocation = this.allocate(capacity, syscall, name);
1936
+ const zeroCopyConst = this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && this.ledger.limits.maxBytes === undefined;
1937
+ const capacity = zeroCopyConst ? 0 : (length > 0 && length < 64 && this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes ? 64 : length);
1938
+ const allocation = zeroCopyConst ? DUMMY_POOL_ALLOCATION : this.allocate(capacity, syscall, name);
1647
1939
  try {
1648
- allocation.data.set(data);
1940
+ if (!zeroCopyConst)
1941
+ allocation.data.set(data);
1649
1942
  this.ledger.reserve(nameBytes, 2, syscall, name);
1650
1943
  try {
1651
1944
  const now = fastWriteCachedNow;
1652
1945
  const fileMode = typeModes.file | mode;
1653
- const view = capacity === length ? allocation.data : undefined;
1946
+ const view = zeroCopyConst ? data : (capacity === length ? allocation.data : undefined);
1654
1947
  let node;
1655
1948
  if (sharedFileNodePoolLen > 0) {
1656
1949
  node = sharedFileNodePool[--sharedFileNodePoolLen];
@@ -1675,9 +1968,10 @@ export class MemoryFileSystem {
1675
1968
  node.byteLength = length;
1676
1969
  node.view = view;
1677
1970
  node.allocation = allocation;
1971
+ node.sourceRef = data;
1678
1972
  }
1679
1973
  else {
1680
- node = new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view);
1974
+ node = new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view, data);
1681
1975
  }
1682
1976
  const prevNlink = parent.cachedNlinkRev === parent.revision ? parent.cachedNlink : (parent.entries.size === 0 ? 2 : undefined);
1683
1977
  parent.entries.set(name, node);
@@ -1690,8 +1984,12 @@ export class MemoryFileSystem {
1690
1984
  cache.lastFastFilePath = "";
1691
1985
  cache.lastFastFileName = name;
1692
1986
  cache.lastFastFileNode = node;
1987
+ lastFastMapMiss.map = undefined;
1988
+ lastFastMapMiss.key = "";
1693
1989
  }
1694
1990
  catch (error) {
1991
+ lastFastMapMiss.map = undefined;
1992
+ lastFastMapMiss.key = "";
1695
1993
  this.ledger.release(nameBytes, 2);
1696
1994
  throw error;
1697
1995
  }
@@ -2169,6 +2467,45 @@ export class MemoryFileSystem {
2169
2467
  }
2170
2468
  return result.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
2171
2469
  }
2470
+ tryMkdirFastSync(path, recursive, mode) {
2471
+ try {
2472
+ this.validatePath(path, "mkdir");
2473
+ const resolvedMode = this.mode(mode, 0o777, "mkdir", path);
2474
+ if (recursive) {
2475
+ const node = this.resolve(path, "mkdir", { createDirectories: resolvedMode }).node;
2476
+ return node.type === "directory";
2477
+ }
2478
+ const location = this.resolve(path.replace(/\/+$/, "") || "/", "mkdir", { allowMissing: true, followFinal: false });
2479
+ if (location.node)
2480
+ return false;
2481
+ this.permission(location.parent, 3, "mkdir", path);
2482
+ this.addNode(location.parent, location.name, () => this.directory(resolvedMode), "mkdir", path);
2483
+ return true;
2484
+ }
2485
+ catch {
2486
+ return false;
2487
+ }
2488
+ }
2489
+ tryRmFastSync(path, recursive, force) {
2490
+ if (activeConditionalMutations.has(this.identityScope))
2491
+ return false;
2492
+ try {
2493
+ let location;
2494
+ try {
2495
+ location = this.entry(path, "rm", false);
2496
+ }
2497
+ catch (error) {
2498
+ if (force && error instanceof FsError && error.code === "ENOENT")
2499
+ return true;
2500
+ return false;
2501
+ }
2502
+ this.removeLocation(location, path, "rm", recursive);
2503
+ return true;
2504
+ }
2505
+ catch {
2506
+ return false;
2507
+ }
2508
+ }
2172
2509
  mkdir(path, options = {}) {
2173
2510
  try {
2174
2511
  options.signal?.throwIfAborted();
@@ -2767,6 +3104,32 @@ export class MemoryRedirectHandle {
2767
3104
  if (!this.append)
2768
3105
  this.position = pos + len;
2769
3106
  }
3107
+ writeImmutableSync(immutableData, signal) {
3108
+ signal?.throwIfAborted();
3109
+ if (this.closed)
3110
+ throw new FsError("EBADF", { syscall: "write", path: this.path });
3111
+ const len = immutableData.byteLength;
3112
+ if (len === 0)
3113
+ return;
3114
+ const mem = this.fs;
3115
+ if (!this.append &&
3116
+ this.position === 0 &&
3117
+ this.node.byteLength === 0 &&
3118
+ this.node.allocation === DUMMY_POOL_ALLOCATION &&
3119
+ mem.ledger.hasInfiniteRetained &&
3120
+ mem.ledger.hasInfiniteFileBytes &&
3121
+ mem.ledger.limits.maxBytes === undefined) {
3122
+ const now = Date.now === defaultDateNow ? fastWriteCachedNow : Date.now();
3123
+ this.node.byteLength = len;
3124
+ this.node.view = immutableData;
3125
+ this.node.sourceRef = immutableData;
3126
+ this.position = len;
3127
+ mem.totalBytes += len;
3128
+ mem.changed(this.node, now);
3129
+ return;
3130
+ }
3131
+ this.writeRangeSync(immutableData, len, signal);
3132
+ }
2770
3133
  writeSync(chunk, signal) {
2771
3134
  this.writeRangeSync(chunk, chunk.byteLength, signal);
2772
3135
  }
@@ -2780,20 +3143,30 @@ export class MemoryRedirectHandle {
2780
3143
  this.fs = undefined;
2781
3144
  this.node = DUMMY_POOL_FILE_NODE;
2782
3145
  this.path = "";
2783
- fs.releaseReference(node, path);
3146
+ if (node.nlink > 0 && node.references === 1) {
3147
+ node.references = 0;
3148
+ fs.ledger.release(path.length * 2, 1);
3149
+ }
3150
+ else {
3151
+ fs.releaseReference(node, path);
3152
+ }
2784
3153
  if (redirectHandlePool.handle === undefined) {
2785
3154
  redirectHandlePool.handle = this;
2786
3155
  }
2787
3156
  }
2788
3157
  }
2789
3158
  const redirectHandlePool = { handle: undefined };
3159
+ let _lastRedirectSlice1Path = "";
3160
+ let _lastRedirectSlice1Name = "";
3161
+ let _lastFileSlice1Path = "";
3162
+ let _lastFileSlice1Name = "";
2790
3163
  export function tryOpenMemoryRedirectHandleSync(filesystem, path, append, mode, signal) {
2791
3164
  const mem = filesystem;
2792
- const owner = ownedStores.get(mem);
3165
+ const owner = mem._owner ?? ownedStores.get(mem);
2793
3166
  if (!owner ||
2794
3167
  mem.symlinkCount !== 0 ||
2795
3168
  mem.capabilities !== owner.capabilities ||
2796
- activeConditionalMutations.has(mem.identityScope) ||
3169
+ (activeConditionalMutations.size > 0 && activeConditionalMutations.has(mem.identityScope)) ||
2797
3170
  !isStockMemoryMethods(mem, openRedirectFastMethodNames, false) ||
2798
3171
  !isCleanAbsolutePath(path) ||
2799
3172
  path === "/" ||
@@ -2813,12 +3186,14 @@ export function tryOpenMemoryRedirectHandleSync(filesystem, path, append, mode,
2813
3186
  memInternal.permission(parent, 1, "open", path);
2814
3187
  const slash = path.indexOf("/", start);
2815
3188
  if (slash === -1) {
2816
- name = path.slice(start);
3189
+ name = start === 1
3190
+ ? (path === _lastRedirectSlice1Path ? _lastRedirectSlice1Name : (_lastRedirectSlice1Path = path, _lastRedirectSlice1Name = path.slice(1)))
3191
+ : path.slice(start);
2817
3192
  if (exceedsComponentByteLimit(name))
2818
3193
  memInternal.fail("ENAMETOOLONG", "open", path);
2819
3194
  break;
2820
3195
  }
2821
- const seg = path.slice(start, slash);
3196
+ const seg = slicePathSegment(path, start, slash);
2822
3197
  if (exceedsComponentByteLimit(seg))
2823
3198
  memInternal.fail("ENAMETOOLONG", "open", path);
2824
3199
  const next = parent.entries.get(seg);
@@ -2829,7 +3204,7 @@ export function tryOpenMemoryRedirectHandleSync(filesystem, path, append, mode,
2829
3204
  parent = next;
2830
3205
  start = slash + 1;
2831
3206
  }
2832
- const existing = parent.entries.get(name);
3207
+ const existing = parent.entries.getForWrite(name);
2833
3208
  const ledger = memInternal.ledger;
2834
3209
  let node;
2835
3210
  if (existing !== undefined) {
@@ -2853,16 +3228,21 @@ export function tryOpenMemoryRedirectHandleSync(filesystem, path, append, mode,
2853
3228
  memInternal.permission(parent, 3, "open", path);
2854
3229
  const refBytes = path.length * 2;
2855
3230
  const nameBytes = name.length * 2;
2856
- ledger.reserve(refBytes, 1, "open", path);
2857
- try {
2858
- ledger.fileSize(0, "open", path);
2859
- ledger.reserve(nameBytes, 2, "open", path);
3231
+ if (ledger.hasInfiniteRetained && ledger.hasInfiniteFileBytes) {
3232
+ ledger.reserve(refBytes + nameBytes, 3, "open", path);
2860
3233
  }
2861
- catch (error) {
2862
- ledger.release(refBytes, 1);
2863
- throw error;
3234
+ else {
3235
+ ledger.reserve(refBytes, 1, "open", path);
3236
+ try {
3237
+ ledger.fileSize(0, "open", path);
3238
+ ledger.reserve(nameBytes, 2, "open", path);
3239
+ }
3240
+ catch (error) {
3241
+ ledger.release(refBytes, 1);
3242
+ throw error;
3243
+ }
2864
3244
  }
2865
- const cache = memoryCaches.get(ledger);
3245
+ const cache = mem._cache;
2866
3246
  cache.clearWrites();
2867
3247
  const now = Date.now === defaultDateNow ? ((++fastWriteNowTick & 63) === 0 ? (fastWriteCachedNow = Date.now()) : fastWriteCachedNow) : Date.now();
2868
3248
  const fileMode = typeModes.file | validMode;
@@ -2877,9 +3257,11 @@ export function tryOpenMemoryRedirectHandleSync(filesystem, path, append, mode,
2877
3257
  if (newNode) {
2878
3258
  newNode.mode = fileMode;
2879
3259
  newNode.ino = memInternal.nextInode++;
2880
- newNode.nlink = 1;
3260
+ if (newNode.nlink !== 1)
3261
+ newNode.nlink = 1;
2881
3262
  newNode.references = 1;
2882
- newNode.revision = 0;
3263
+ if (newNode.revision !== 0)
3264
+ newNode.revision = 0;
2883
3265
  newNode.atimeMs = now;
2884
3266
  newNode.mtimeMs = now;
2885
3267
  newNode.ctimeMs = now;
@@ -2893,7 +3275,20 @@ export function tryOpenMemoryRedirectHandleSync(filesystem, path, append, mode,
2893
3275
  newNode.references = 1;
2894
3276
  }
2895
3277
  const prevNlink = parent.cachedNlinkRev === parent.revision ? parent.cachedNlink : (parent.entries.size === 0 ? 2 : undefined);
2896
- parent.entries.set(name, newNode);
3278
+ const entries = parent.entries;
3279
+ if (lastFastMapMiss.map === entries && lastFastMapMiss.key === name && entries._next < entries._keys.length) {
3280
+ const entryIdx = entries._next++;
3281
+ entries._table[lastFastMapMiss.slot] = entryIdx;
3282
+ entries._keys[entryIdx] = name;
3283
+ entries._vals[entryIdx] = newNode;
3284
+ entries.size++;
3285
+ lastFastMapMiss.map = undefined;
3286
+ lastFastMapMiss.key = "";
3287
+ }
3288
+ else {
3289
+ entries.set(name, newNode);
3290
+ }
3291
+ mem.mutationTick = (mem.mutationTick + 1) | 0;
2897
3292
  parent.revision = parent.revision < 1073741823 ? (parent.revision + 1) | 0 : Math.min(Number.MAX_SAFE_INTEGER + 1, parent.revision + 1);
2898
3293
  parent.mtimeMs = parent.ctimeMs = now;
2899
3294
  if (prevNlink !== undefined) {
@@ -2973,14 +3368,14 @@ export function tryResolveMemoryDevicePath(filesystem, path, resizeCreate) {
2973
3368
  return undefined;
2974
3369
  const slash = path.indexOf("/", start);
2975
3370
  if (slash === -1) {
2976
- const name = path.slice(start);
3371
+ const name = slicePathSegment(path, start, path.length);
2977
3372
  if (exceedsComponentByteLimit(name))
2978
3373
  return undefined;
2979
3374
  if (resizeCreate === false && !current.entries.has(name))
2980
3375
  return undefined;
2981
3376
  return path;
2982
3377
  }
2983
- const name = path.slice(start, slash);
3378
+ const name = slicePathSegment(path, start, slash);
2984
3379
  if (exceedsComponentByteLimit(name))
2985
3380
  return undefined;
2986
3381
  const next = current.entries.get(name);
@@ -2990,7 +3385,7 @@ export function tryResolveMemoryDevicePath(filesystem, path, resizeCreate) {
2990
3385
  let remStart = slash + 1;
2991
3386
  while (true) {
2992
3387
  const nextSlash = path.indexOf("/", remStart);
2993
- const seg = nextSlash === -1 ? path.slice(remStart) : path.slice(remStart, nextSlash);
3388
+ const seg = nextSlash === -1 ? slicePathSegment(path, remStart, path.length) : slicePathSegment(path, remStart, nextSlash);
2994
3389
  if (exceedsComponentByteLimit(seg))
2995
3390
  return undefined;
2996
3391
  if (nextSlash === -1)
@@ -3004,9 +3399,19 @@ export function tryResolveMemoryDevicePath(filesystem, path, resizeCreate) {
3004
3399
  start = slash + 1;
3005
3400
  }
3006
3401
  }
3402
+ const cachedMethodValuesMap = new WeakMap();
3403
+ function getExpectedMethodValues(names) {
3404
+ let vals = cachedMethodValuesMap.get(names);
3405
+ if (!vals) {
3406
+ vals = names.map(name => memoryImplementation[name]?.value);
3407
+ cachedMethodValuesMap.set(names, vals);
3408
+ }
3409
+ return vals;
3410
+ }
3411
+ const hasOwn = Object.prototype.hasOwnProperty;
3007
3412
  function isStockMemoryMethods(mem, names, checkRootAccessor = true) {
3008
- const owner = ownedStores.get(mem);
3009
- if (!owner || Object.getPrototypeOf(mem) !== MemoryFileSystem.prototype)
3413
+ const owner = mem?._owner ?? ownedStores.get(mem);
3414
+ if (!owner || Object.getPrototypeOf(mem) !== MemoryFileSystem.prototype || ownedStores.get(mem) !== owner)
3010
3415
  return false;
3011
3416
  if (checkRootAccessor) {
3012
3417
  const rootDesc = Object.getOwnPropertyDescriptor(mem, "root");
@@ -3016,32 +3421,119 @@ function isStockMemoryMethods(mem, names, checkRootAccessor = true) {
3016
3421
  else if (mem.root !== owner.root) {
3017
3422
  return false;
3018
3423
  }
3424
+ const vals = getExpectedMethodValues(names);
3425
+ const proto = MemoryFileSystem.prototype;
3019
3426
  for (let i = 0; i < names.length; i++) {
3020
3427
  const name = names[i];
3021
- if (Object.prototype.hasOwnProperty.call(mem, name)) {
3428
+ const expected = vals[i];
3429
+ if (hasOwn.call(mem, name)) {
3022
3430
  const descriptor = Object.getOwnPropertyDescriptor(mem, name);
3023
- if (!descriptor || !("value" in descriptor) || descriptor.value !== memoryImplementation[name]?.value)
3431
+ if (!descriptor || !("value" in descriptor) || descriptor.value !== expected)
3024
3432
  return false;
3025
3433
  }
3026
- else if (MemoryFileSystem.prototype[name] !== memoryImplementation[name]?.value) {
3434
+ else if (proto[name] !== expected) {
3027
3435
  return false;
3028
3436
  }
3029
3437
  }
3030
3438
  return true;
3031
3439
  }
3032
- export function tryReadMemoryFileViewSync(filesystem, path, maxBytes, signal) {
3440
+ let lastReadViewData;
3441
+ let lastReadViewSourceRef;
3442
+ export function getLastReadMemoryFileSourceRef(view) {
3443
+ const ref = view === lastReadViewData ? lastReadViewSourceRef : undefined;
3444
+ lastReadViewData = undefined;
3445
+ lastReadViewSourceRef = undefined;
3446
+ return ref;
3447
+ }
3448
+ export function tryReadMemoryFileViewSync(filesystem, path, maxBytes, signal, captureSourceRef = false) {
3033
3449
  const mem = filesystem;
3034
- if (!ownedStores.has(mem) || !isStockMemoryMethods(mem, readFileFastMethodNames, false))
3450
+ if (mem._owner === undefined || !isStockMemoryMethods(mem, readFileFastMethodNames, false))
3035
3451
  return undefined;
3036
3452
  signal?.throwIfAborted();
3037
3453
  if (maxBytes !== undefined)
3038
3454
  mem.integer(maxBytes, "readFile", path);
3039
3455
  const node = mem.file(path, "readFile");
3040
3456
  mem.permission(node, 4, "readFile", path);
3041
- if (maxBytes !== undefined && node.data.byteLength > maxBytes)
3457
+ const data = node.data;
3458
+ if (maxBytes !== undefined && data.byteLength > maxBytes)
3042
3459
  mem.fail("EFBIG", "readFile", path);
3043
- node.atimeMs = Date.now();
3044
- return node.data;
3460
+ if (Date.now !== defaultDateNow)
3461
+ node.atimeMs = Date.now();
3462
+ else if ((++fastWriteNowTick & 63) === 0)
3463
+ node.atimeMs = fastWriteCachedNow = Date.now();
3464
+ if (captureSourceRef && node.revision === 0) {
3465
+ lastReadViewData = data;
3466
+ lastReadViewSourceRef = node.sourceRef;
3467
+ }
3468
+ return data;
3469
+ }
3470
+ let segCache0 = "";
3471
+ let segCache1 = "";
3472
+ let segCache2 = "";
3473
+ let segCache3 = "";
3474
+ let segCacheCursor = 0;
3475
+ function slicePathSegment(path, start, end) {
3476
+ const len = end - start;
3477
+ if (len <= 0)
3478
+ return "";
3479
+ if (len === 1)
3480
+ return String.fromCharCode(path.charCodeAt(start));
3481
+ if (len <= 16) {
3482
+ const c0 = path.charCodeAt(start);
3483
+ if (segCache0.length === len && segCache0.charCodeAt(0) === c0) {
3484
+ let ok = true;
3485
+ for (let i = 1; i < len; i++)
3486
+ if (path.charCodeAt(start + i) !== segCache0.charCodeAt(i)) {
3487
+ ok = false;
3488
+ break;
3489
+ }
3490
+ if (ok)
3491
+ return segCache0;
3492
+ }
3493
+ if (segCache1.length === len && segCache1.charCodeAt(0) === c0) {
3494
+ let ok = true;
3495
+ for (let i = 1; i < len; i++)
3496
+ if (path.charCodeAt(start + i) !== segCache1.charCodeAt(i)) {
3497
+ ok = false;
3498
+ break;
3499
+ }
3500
+ if (ok)
3501
+ return segCache1;
3502
+ }
3503
+ if (segCache2.length === len && segCache2.charCodeAt(0) === c0) {
3504
+ let ok = true;
3505
+ for (let i = 1; i < len; i++)
3506
+ if (path.charCodeAt(start + i) !== segCache2.charCodeAt(i)) {
3507
+ ok = false;
3508
+ break;
3509
+ }
3510
+ if (ok)
3511
+ return segCache2;
3512
+ }
3513
+ if (segCache3.length === len && segCache3.charCodeAt(0) === c0) {
3514
+ let ok = true;
3515
+ for (let i = 1; i < len; i++)
3516
+ if (path.charCodeAt(start + i) !== segCache3.charCodeAt(i)) {
3517
+ ok = false;
3518
+ break;
3519
+ }
3520
+ if (ok)
3521
+ return segCache3;
3522
+ }
3523
+ const sliced = path.slice(start, end);
3524
+ const slot = segCacheCursor;
3525
+ segCacheCursor = (slot + 1) & 3;
3526
+ if (slot === 0)
3527
+ segCache0 = sliced;
3528
+ else if (slot === 1)
3529
+ segCache1 = sliced;
3530
+ else if (slot === 2)
3531
+ segCache2 = sliced;
3532
+ else
3533
+ segCache3 = sliced;
3534
+ return sliced;
3535
+ }
3536
+ return path.slice(start, end);
3045
3537
  }
3046
3538
  export function tryGetMemoryDirectoryEntryNamesSync(filesystem, path) {
3047
3539
  const mem = filesystem;
@@ -3073,12 +3565,12 @@ export function tryGetMemoryDirectoryEntryNamesSync(filesystem, path) {
3073
3565
  return undefined;
3074
3566
  const slash = path.indexOf("/", start);
3075
3567
  if (slash === -1) {
3076
- const next = current.entries.get(path.slice(start));
3568
+ const next = current.entries.getSubstr(path, start, path.length);
3077
3569
  if (!next || next.type !== "directory" || ((next.mode >> 6) & 4) !== 4)
3078
3570
  return undefined;
3079
3571
  return next.entries;
3080
3572
  }
3081
- const next = current.entries.get(path.slice(start, slash));
3573
+ const next = current.entries.getSubstr(path, start, slash);
3082
3574
  if (!next || next.type !== "directory")
3083
3575
  return undefined;
3084
3576
  current = next;
@@ -3101,7 +3593,7 @@ export function tryWriteMemoryFileSync(filesystem, path, data, append, mode, sig
3101
3593
  mem.writeMemoryFileFast(path, data, append, mode);
3102
3594
  return true;
3103
3595
  }
3104
- export function tryMkdirMemorySync(filesystem, path, recursive, mode, signal) {
3596
+ export function tryMkdirMemorySync(filesystem, path, recursive, mode, signal, dirPrefixHint) {
3105
3597
  const mem = filesystem;
3106
3598
  const owner = ownedStores.get(mem);
3107
3599
  if (!owner ||
@@ -3121,7 +3613,7 @@ export function tryMkdirMemorySync(filesystem, path, recursive, mode, signal) {
3121
3613
  while (true) {
3122
3614
  mem.permission(current, 1, "mkdir", path);
3123
3615
  const slash = path.indexOf("/", start);
3124
- const seg = slash === -1 ? path.slice(start) : path.slice(start, slash);
3616
+ const seg = slash === -1 ? slicePathSegment(path, start, path.length) : slicePathSegment(path, start, slash);
3125
3617
  if (exceedsComponentByteLimit(seg))
3126
3618
  mem.fail("ENAMETOOLONG", "mkdir", path);
3127
3619
  let next = current.entries.get(seg);
@@ -3132,8 +3624,13 @@ export function tryMkdirMemorySync(filesystem, path, recursive, mode, signal) {
3132
3624
  else if (next.type !== "directory") {
3133
3625
  mem.fail(slash === -1 ? "EEXIST" : "ENOTDIR", "mkdir", path);
3134
3626
  }
3135
- if (slash === -1)
3627
+ if (slash === -1) {
3628
+ if (dirPrefixHint !== undefined) {
3629
+ mem._cache.lastFastDirPrefix = dirPrefixHint;
3630
+ mem._cache.lastFastDirNode = next;
3631
+ }
3136
3632
  break;
3633
+ }
3137
3634
  current = next;
3138
3635
  start = slash + 1;
3139
3636
  }
@@ -3168,7 +3665,7 @@ export function tryRmRfMemorySync(filesystem, path, signal) {
3168
3665
  mem.permission(current, 1, "rm", path);
3169
3666
  const slash = path.indexOf("/", start);
3170
3667
  if (slash === -1) {
3171
- targetName = path.slice(start);
3668
+ targetName = slicePathSegment(path, start, path.length);
3172
3669
  if (exceedsComponentByteLimit(targetName))
3173
3670
  mem.fail("ENAMETOOLONG", "rm", path);
3174
3671
  targetNode = current.entries.get(targetName);
@@ -3176,7 +3673,7 @@ export function tryRmRfMemorySync(filesystem, path, signal) {
3176
3673
  return true;
3177
3674
  break;
3178
3675
  }
3179
- const seg = path.slice(start, slash);
3676
+ const seg = slicePathSegment(path, start, slash);
3180
3677
  if (exceedsComponentByteLimit(seg))
3181
3678
  mem.fail("ENAMETOOLONG", "rm", path);
3182
3679
  const next = current.entries.get(seg);