@poe-platform/safe-fs 0.1.730 → 0.1.732

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,16 +15,20 @@ 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
+ this.revision = 0;
19
20
  this.mode = mode;
20
21
  this.ino = ino;
22
+ this.nlink = 1;
21
23
  this.atimeMs = now;
22
24
  this.mtimeMs = now;
23
25
  this.ctimeMs = now;
24
26
  this.birthtimeMs = now;
27
+ this.references = 0;
25
28
  this.byteLength = byteLength;
26
29
  this.allocation = allocation;
27
30
  this.view = view;
31
+ this.sourceRef = sourceRef;
28
32
  }
29
33
  get data() {
30
34
  return this.view ??= (this.byteLength === this.allocation.data.byteLength ? this.allocation.data : this.allocation.data.subarray(0, this.byteLength));
@@ -39,6 +43,7 @@ Object.assign(MemoryFileNode.prototype, {
39
43
  revision: 0,
40
44
  nlink: 1,
41
45
  references: 0,
46
+ sourceRef: undefined,
42
47
  });
43
48
  const EMPTY_ALLOC_BYTES = new Uint8Array(0);
44
49
  const SMALL_ALLOC_SLAB_SIZE = 8192;
@@ -46,19 +51,299 @@ let smallAllocSlab = new Uint8Array(SMALL_ALLOC_SLAB_SIZE);
46
51
  let smallAllocOffset = 0;
47
52
  const DUMMY_POOL_LEDGER = new MemoryLedger(normalizeMemoryFileSystemLimits({}));
48
53
  const DUMMY_POOL_ALLOCATION = new MemoryAllocation(EMPTY_ALLOC_BYTES, DUMMY_POOL_LEDGER);
49
- const sharedAllocationPool = [];
50
- const sharedFileNodePool = [];
51
- const sharedDirectoryNodePool = [];
52
- const sharedRemovedScratch = [];
54
+ const DUMMY_POOL_FILE_NODE = new MemoryFileNode(0, 0, null, 0, DUMMY_POOL_ALLOCATION, DUMMY_POOL_ALLOCATION.data);
55
+ DUMMY_POOL_FILE_NODE.atimeMs = DUMMY_POOL_FILE_NODE.mtimeMs = DUMMY_POOL_FILE_NODE.ctimeMs = DUMMY_POOL_FILE_NODE.birthtimeMs = 1700000000000;
56
+ const defaultDateNow = Date.now;
57
+ let fastWriteCachedNow = Date.now();
58
+ let fastWriteNowTick = 0;
59
+ const lastFastMapMiss = {
60
+ map: undefined, key: "", slot: -1,
61
+ };
62
+ class FastDirectoryEntriesMap {
63
+ _table = new Int16Array(128).fill(-1);
64
+ _mask = 127;
65
+ _keys = new Array(64).fill("");
66
+ _vals = new Array(64).fill(DUMMY_POOL_FILE_NODE);
67
+ _next = 0;
68
+ size = 0;
69
+ [Symbol.toStringTag] = "Map";
70
+ _hash(k) {
71
+ let h = 5381;
72
+ for (let i = 0; i < k.length; i++) {
73
+ h = (((h & 0x1fffff) * 33) ^ k.charCodeAt(i)) & 0x3fffffff;
74
+ }
75
+ return h & this._mask;
76
+ }
77
+ get(k) {
78
+ if (this.size === 0)
79
+ return undefined;
80
+ let slot = this._hash(k);
81
+ const mask = this._mask;
82
+ const table = this._table;
83
+ const keys = this._keys;
84
+ while (true) {
85
+ const idx = table[slot];
86
+ if (idx === -1)
87
+ return undefined;
88
+ if (idx >= 0 && keys[idx] === k)
89
+ return this._vals[idx];
90
+ slot = (slot + 1) & mask;
91
+ }
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
+ }
159
+ has(k) {
160
+ return this.get(k) !== undefined;
161
+ }
162
+ _rebuild(newCap) {
163
+ const newTableLen = newCap * 2;
164
+ const newMask = newTableLen - 1;
165
+ const newTable = newCap <= 16384 ? new Int16Array(newTableLen).fill(-1) : new Int32Array(newTableLen).fill(-1);
166
+ const oldKeys = this._keys;
167
+ const oldVals = this._vals;
168
+ const oldNext = this._next;
169
+ const newKeys = newCap === oldKeys.length ? oldKeys : new Array(newCap).fill("");
170
+ const newVals = newCap === oldVals.length ? oldVals : new Array(newCap).fill(DUMMY_POOL_FILE_NODE);
171
+ let writeIdx = 0;
172
+ for (let i = 0; i < oldNext; i++) {
173
+ const v = oldVals[i];
174
+ if (v === DUMMY_POOL_FILE_NODE)
175
+ continue;
176
+ const k = oldKeys[i];
177
+ newKeys[writeIdx] = k;
178
+ newVals[writeIdx] = v;
179
+ let h = 5381;
180
+ for (let c = 0; c < k.length; c++) {
181
+ h = (((h & 0x1fffff) * 33) ^ k.charCodeAt(c)) & 0x3fffffff;
182
+ }
183
+ let slot = h & newMask;
184
+ while (newTable[slot] !== -1) {
185
+ slot = (slot + 1) & newMask;
186
+ }
187
+ newTable[slot] = writeIdx++;
188
+ }
189
+ if (newKeys === oldKeys) {
190
+ for (let i = writeIdx; i < oldNext; i++) {
191
+ newKeys[i] = "";
192
+ newVals[i] = DUMMY_POOL_FILE_NODE;
193
+ }
194
+ }
195
+ this._table = newTable;
196
+ this._mask = newMask;
197
+ this._keys = newKeys;
198
+ this._vals = newVals;
199
+ this._next = writeIdx;
200
+ }
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
+ }
211
+ if (this._next >= this._keys.length) {
212
+ this._rebuild(this.size * 2 <= this._keys.length ? this._keys.length : this._keys.length * 2);
213
+ }
214
+ let slot = this._hash(k);
215
+ const mask = this._mask;
216
+ const table = this._table;
217
+ const keys = this._keys;
218
+ let firstDeleted = -1;
219
+ while (true) {
220
+ const idx = table[slot];
221
+ if (idx === -1) {
222
+ const targetSlot = firstDeleted !== -1 ? firstDeleted : slot;
223
+ const entryIdx = this._next++;
224
+ table[targetSlot] = entryIdx;
225
+ keys[entryIdx] = k;
226
+ this._vals[entryIdx] = v;
227
+ this.size++;
228
+ return this;
229
+ }
230
+ if (idx === -2) {
231
+ if (firstDeleted === -1)
232
+ firstDeleted = slot;
233
+ }
234
+ else if (keys[idx] === k) {
235
+ this._vals[idx] = v;
236
+ return this;
237
+ }
238
+ slot = (slot + 1) & mask;
239
+ }
240
+ }
241
+ delete(k) {
242
+ if (this.size === 0)
243
+ return false;
244
+ let slot = this._hash(k);
245
+ const mask = this._mask;
246
+ const table = this._table;
247
+ const keys = this._keys;
248
+ while (true) {
249
+ const idx = table[slot];
250
+ if (idx === -1)
251
+ return false;
252
+ if (idx >= 0 && keys[idx] === k) {
253
+ if (this.size === 1) {
254
+ this.clear();
255
+ return true;
256
+ }
257
+ table[slot] = -2;
258
+ keys[idx] = "";
259
+ this._vals[idx] = DUMMY_POOL_FILE_NODE;
260
+ if (idx === this._next - 1)
261
+ this._next--;
262
+ this.size--;
263
+ return true;
264
+ }
265
+ slot = (slot + 1) & mask;
266
+ }
267
+ }
268
+ clear() {
269
+ if (this._next > 0) {
270
+ this._table.fill(-1);
271
+ for (let i = 0; i < this._next; i++) {
272
+ this._keys[i] = "";
273
+ this._vals[i] = DUMMY_POOL_FILE_NODE;
274
+ }
275
+ this._next = 0;
276
+ this.size = 0;
277
+ }
278
+ }
279
+ forEach(callbackfn, thisArg) {
280
+ const next = this._next;
281
+ const keys = this._keys;
282
+ const vals = this._vals;
283
+ for (let i = 0; i < next; i++) {
284
+ const v = vals[i];
285
+ if (v !== DUMMY_POOL_FILE_NODE) {
286
+ if (thisArg !== undefined)
287
+ callbackfn.call(thisArg, v, keys[i], this);
288
+ else
289
+ callbackfn(v, keys[i], this);
290
+ }
291
+ }
292
+ }
293
+ *keys() {
294
+ for (let i = 0; i < this._next; i++) {
295
+ if (this._vals[i] !== DUMMY_POOL_FILE_NODE)
296
+ yield this._keys[i];
297
+ }
298
+ }
299
+ *values() {
300
+ for (let i = 0; i < this._next; i++) {
301
+ const v = this._vals[i];
302
+ if (v !== DUMMY_POOL_FILE_NODE)
303
+ yield v;
304
+ }
305
+ }
306
+ *entries() {
307
+ for (let i = 0; i < this._next; i++) {
308
+ const v = this._vals[i];
309
+ if (v !== DUMMY_POOL_FILE_NODE)
310
+ yield [this._keys[i], v];
311
+ }
312
+ }
313
+ [Symbol.iterator]() {
314
+ return this.entries();
315
+ }
316
+ }
317
+ const SHARED_POOL_CAPACITY = 256;
318
+ const SHARED_DIR_POOL_CAPACITY = 64;
319
+ const sharedAllocationPool = new Array(SHARED_POOL_CAPACITY).fill(DUMMY_POOL_ALLOCATION);
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;
326
+ const sharedFileNodePool = new Array(SHARED_POOL_CAPACITY).fill(DUMMY_POOL_FILE_NODE);
327
+ let sharedFileNodePoolLen = 0;
328
+ const sharedDirectoryNodePool = new Array(SHARED_DIR_POOL_CAPACITY);
329
+ let sharedDirectoryNodePoolLen = 0;
330
+ const sharedRemovedScratch = new Array(SHARED_POOL_CAPACITY).fill(DUMMY_POOL_FILE_NODE);
331
+ let sharedRemovedLen = 0;
53
332
  let sharedRemovedKeyChars = 0;
54
333
  let sharedRemovedKeyEntries = 0;
55
334
  function collectRemovedChild(child, name) {
56
- sharedRemovedScratch.push(child);
335
+ if (sharedRemovedLen < sharedRemovedScratch.length) {
336
+ sharedRemovedScratch[sharedRemovedLen++] = child;
337
+ }
338
+ else {
339
+ sharedRemovedScratch.push(child);
340
+ sharedRemovedLen++;
341
+ }
57
342
  sharedRemovedKeyChars += name.length;
58
343
  sharedRemovedKeyEntries++;
59
344
  }
60
345
  function replenishSharedMemoryPools() {
61
- while (sharedAllocationPool.length < 112) {
346
+ while (sharedAllocationPoolLen < 112) {
62
347
  if (smallAllocOffset + 64 > SMALL_ALLOC_SLAB_SIZE) {
63
348
  smallAllocSlab = new Uint8Array(SMALL_ALLOC_SLAB_SIZE);
64
349
  smallAllocOffset = 0;
@@ -68,24 +353,41 @@ function replenishSharedMemoryPools() {
68
353
  DUMMY_POOL_LEDGER.reserve(64, 0, "init", "/");
69
354
  const alloc = new MemoryAllocation(slice, DUMMY_POOL_LEDGER);
70
355
  alloc.release();
71
- sharedAllocationPool.push(alloc);
356
+ sharedAllocationPool[sharedAllocationPoolLen++] = alloc;
357
+ }
358
+ while (sharedFileNodePoolLen < 112) {
359
+ sharedFileNodePool[sharedFileNodePoolLen++] = new MemoryFileNode(0, 0, fastWriteCachedNow, 0, DUMMY_POOL_ALLOCATION, undefined);
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;
72
366
  }
73
- while (sharedFileNodePool.length < 112) {
74
- sharedFileNodePool.push(new MemoryFileNode(0, 0, 0, 0, DUMMY_POOL_ALLOCATION, undefined));
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;
75
372
  }
76
- while (sharedDirectoryNodePool.length < 16) {
77
- sharedDirectoryNodePool.push(new MemoryDirectoryNode(0, 0, 0));
373
+ while (sharedDirectoryNodePoolLen < 16) {
374
+ sharedDirectoryNodePool[sharedDirectoryNodePoolLen++] = new MemoryDirectoryNode(0, 0, fastWriteCachedNow);
78
375
  }
79
376
  }
80
377
  class MemoryDirectoryNode {
81
378
  constructor(mode, ino, now) {
379
+ this.revision = 0;
82
380
  this.mode = mode;
83
381
  this.ino = ino;
382
+ this.nlink = 1;
383
+ this.references = 0;
84
384
  this.atimeMs = now;
85
385
  this.mtimeMs = now;
86
386
  this.ctimeMs = now;
87
387
  this.birthtimeMs = now;
88
- this.entries = new Map();
388
+ this.entries = new FastDirectoryEntriesMap();
389
+ this.cachedNlink = 2;
390
+ this.cachedNlinkRev = 0;
89
391
  }
90
392
  }
91
393
  Object.assign(MemoryDirectoryNode.prototype, {
@@ -96,16 +398,18 @@ Object.assign(MemoryDirectoryNode.prototype, {
96
398
  cachedNlink: 2,
97
399
  cachedNlinkRev: 0,
98
400
  });
401
+ const DUMMY_POOL_DIR_NODE = new MemoryDirectoryNode(0, 0, null);
402
+ DUMMY_POOL_DIR_NODE.atimeMs = DUMMY_POOL_DIR_NODE.mtimeMs = DUMMY_POOL_DIR_NODE.ctimeMs = DUMMY_POOL_DIR_NODE.birthtimeMs = 1700000000000;
99
403
  class MemoryCache {
100
404
  allocations = [];
101
405
  files = [];
102
406
  directories = [];
103
407
  removedScratch = [];
104
408
  lastFastDirPrefix = "";
105
- lastFastDirNode;
409
+ lastFastDirNode = undefined;
106
410
  lastFastFilePath = "";
107
411
  lastFastFileName = "";
108
- lastFastFileNode;
412
+ lastFastFileNode = undefined;
109
413
  clearWrites() {
110
414
  this.lastFastDirPrefix = "";
111
415
  this.lastFastDirNode = undefined;
@@ -117,6 +421,9 @@ class MemoryCache {
117
421
  const typeModes = { file: 0o100000, directory: 0o040000, symlink: 0o120000 };
118
422
  const emptyResolveOptions = Object.freeze({});
119
423
  const noFollowResolveOptions = Object.freeze({ followFinal: false });
424
+ const sharedMkdirResolveOptions = { createDirectories: 0o777 };
425
+ const sharedAllowMissingNoFollowOptions = Object.freeze({ allowMissing: true, followFinal: false });
426
+ const sharedFastLocation = { node: undefined, parent: DUMMY_POOL_DIR_NODE, name: "", path: "" };
120
427
  const resolvedVoid = Promise.resolve();
121
428
  const preferredIoBlockSize = 64 * 1024;
122
429
  const ext4HtreeEof64 = (1n << 63n) - 1n;
@@ -317,24 +624,31 @@ export class MemoryFileSystem {
317
624
  identityScope = Symbol();
318
625
  nextInode = 1;
319
626
  ledger;
627
+ _cache;
628
+ mutationTick = 0;
629
+ _owner;
320
630
  root;
321
631
  totalBytes = 0;
322
632
  symlinkCount = 0;
323
633
  constructor(options = {}) {
324
634
  this.ledger = new MemoryLedger(normalizeMemoryFileSystemLimits(options));
325
- if (this.ledger.hasInfiniteRetained && this.ledger.limits.maxFileBytes === Infinity) {
635
+ if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes) {
326
636
  replenishSharedMemoryPools();
327
637
  }
328
- memoryCaches.set(this.ledger, new MemoryCache());
638
+ const cache = new MemoryCache();
639
+ this._cache = cache;
640
+ memoryCaches.set(this.ledger, cache);
329
641
  this.ledger.reserve(0, 1, "mkdir", "/");
330
642
  this.root = this.directory(0o755);
331
643
  const root = this.root;
332
- ownedStores.set(this, {
644
+ const owner = {
333
645
  root,
334
646
  ledger: this.ledger,
335
647
  capabilities: this.capabilities,
336
648
  intact: () => this.root === root,
337
- });
649
+ };
650
+ this._owner = owner;
651
+ ownedStores.set(this, owner);
338
652
  registerMemoryAtomicView(this, {
339
653
  stat: (path) => {
340
654
  this.validatePath(path, "overlayAtomicView");
@@ -496,11 +810,17 @@ export class MemoryFileSystem {
496
810
  atimeMs: now, mtimeMs: now, ctimeMs: now, birthtimeMs: now,
497
811
  };
498
812
  }
499
- directory(mode) {
500
- const now = Date.now();
813
+ directory(mode, now = Date.now()) {
501
814
  const fullMode = typeModes.directory | mode;
502
815
  const ino = this.nextInode++;
503
- const pooled = sharedDirectoryNodePool.pop() ?? memoryCaches.get(this.ledger)?.directories.pop();
816
+ let pooled;
817
+ if (sharedDirectoryNodePoolLen > 0) {
818
+ pooled = sharedDirectoryNodePool[--sharedDirectoryNodePoolLen];
819
+ sharedDirectoryNodePool[sharedDirectoryNodePoolLen] = DUMMY_POOL_DIR_NODE;
820
+ }
821
+ else {
822
+ pooled = memoryCaches.get(this.ledger)?.directories.pop();
823
+ }
504
824
  if (pooled) {
505
825
  pooled.mode = fullMode;
506
826
  pooled.ino = ino;
@@ -517,13 +837,13 @@ export class MemoryFileSystem {
517
837
  }
518
838
  return new MemoryDirectoryNode(fullMode, ino, now);
519
839
  }
520
- addDirectoryNode(parent, name, mode, syscall, path) {
840
+ addDirectoryNode(parent, name, mode, syscall, path, now = Date.now()) {
521
841
  const bytes = name.length * 2;
522
842
  this.ledger.reserve(bytes, 2, syscall, path);
523
843
  try {
524
- const node = this.directory(mode);
844
+ const node = this.directory(mode, now);
525
845
  parent.entries.set(name, node);
526
- this.changed(parent);
846
+ this.changed(parent, now);
527
847
  return node;
528
848
  }
529
849
  catch (error) {
@@ -565,19 +885,26 @@ export class MemoryFileSystem {
565
885
  this.totalBytes -= node.byteLength;
566
886
  const alloc = node.allocation;
567
887
  node.view = undefined;
888
+ node.sourceRef = undefined;
568
889
  node.allocation = DUMMY_POOL_ALLOCATION;
569
890
  alloc.release();
570
891
  if (alloc.isReleased64()) {
571
892
  alloc.detachLedger(DUMMY_POOL_LEDGER);
572
- if (this.ledger.hasInfiniteRetained && this.ledger.limits.maxFileBytes === Infinity && sharedAllocationPool.length < 128) {
573
- sharedAllocationPool.push(alloc);
893
+ if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && sharedAllocationPoolLen < SHARED_POOL_CAPACITY) {
894
+ sharedAllocationPool[sharedAllocationPoolLen++] = alloc;
574
895
  }
575
896
  else if (cache.allocations.length < 128) {
576
897
  cache.allocations.push(alloc);
577
898
  }
578
899
  }
579
- if (sharedFileNodePool.length < 128) {
580
- sharedFileNodePool.push(node);
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
+ }
906
+ if (sharedFileNodePoolLen < SHARED_POOL_CAPACITY) {
907
+ sharedFileNodePool[sharedFileNodePoolLen++] = node;
581
908
  }
582
909
  else if (cache.files.length < 128) {
583
910
  cache.files.push(node);
@@ -592,8 +919,8 @@ export class MemoryFileSystem {
592
919
  cache.lastFastDirNode = undefined;
593
920
  }
594
921
  if (node.entries.size === 0) {
595
- if (sharedDirectoryNodePool.length < 32) {
596
- sharedDirectoryNodePool.push(node);
922
+ if (sharedDirectoryNodePoolLen < SHARED_DIR_POOL_CAPACITY) {
923
+ sharedDirectoryNodePool[sharedDirectoryNodePoolLen++] = node;
597
924
  }
598
925
  else if (cache.directories.length < 32) {
599
926
  cache.directories.push(node);
@@ -613,6 +940,18 @@ export class MemoryFileSystem {
613
940
  node.view = length === allocation.data.byteLength ? allocation.data : undefined;
614
941
  node.allocation = allocation;
615
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
+ }
616
955
  }
617
956
  admitSize(node, length, syscall, path) {
618
957
  this.ledger.fileSize(length, syscall, path);
@@ -645,6 +984,7 @@ export class MemoryFileSystem {
645
984
  this.fail("EINVAL", syscall, path);
646
985
  }
647
986
  changed(node, now = Date.now()) {
987
+ this.mutationTick = (this.mutationTick + 1) | 0;
648
988
  node.revision = node.revision < 1073741823 ? (node.revision + 1) | 0 : Math.min(Number.MAX_SAFE_INTEGER + 1, node.revision + 1);
649
989
  node.mtimeMs = node.ctimeMs = now;
650
990
  }
@@ -654,7 +994,7 @@ export class MemoryFileSystem {
654
994
  const fastDir = cache.lastFastDirNode;
655
995
  const fastPrefix = cache.lastFastDirPrefix;
656
996
  if (fastDir !== undefined && fastDir.nlink !== 0 && fastPrefix.length > 0 && path.startsWith(fastPrefix)) {
657
- 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);
658
998
  if (name.length > 0 && !name.includes("/")) {
659
999
  this.permission(fastDir, 1, syscall, path);
660
1000
  if (exceedsComponentByteLimit(name))
@@ -672,9 +1012,9 @@ export class MemoryFileSystem {
672
1012
  const slash = path.indexOf("/", start);
673
1013
  if (slash === -1) {
674
1014
  cache.clearWrites();
675
- cache.lastFastDirPrefix = path.slice(0, start);
1015
+ cache.lastFastDirPrefix = start === 1 ? "/" : path.slice(0, start);
676
1016
  cache.lastFastDirNode = current;
677
- const name = path.slice(start);
1017
+ const name = start === 1 ? (path === _lastFileSlice1Path ? _lastFileSlice1Name : (_lastFileSlice1Path = path, _lastFileSlice1Name = path.slice(1))) : path.slice(start);
678
1018
  if (exceedsComponentByteLimit(name))
679
1019
  this.fail("ENAMETOOLONG", syscall, path);
680
1020
  const node = current.entries.get(name);
@@ -682,7 +1022,7 @@ export class MemoryFileSystem {
682
1022
  this.fail("ENOENT", syscall, path);
683
1023
  return node;
684
1024
  }
685
- const name = path.slice(start, slash);
1025
+ const name = slicePathSegment(path, start, slash);
686
1026
  if (exceedsComponentByteLimit(name))
687
1027
  this.fail("ENAMETOOLONG", syscall, path);
688
1028
  const next = current.entries.get(name);
@@ -706,7 +1046,7 @@ export class MemoryFileSystem {
706
1046
  this.permission(current, 1, syscall, path);
707
1047
  const slash = path.indexOf("/", start);
708
1048
  if (slash === -1) {
709
- const name = path.slice(start);
1049
+ const name = slicePathSegment(path, start, path.length);
710
1050
  if (exceedsComponentByteLimit(name))
711
1051
  this.fail("ENAMETOOLONG", syscall, path);
712
1052
  let node = current.entries.get(name);
@@ -827,6 +1167,20 @@ export class MemoryFileSystem {
827
1167
  };
828
1168
  }
829
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
+ }
830
1184
  const cache = memoryCaches.get(this.ledger);
831
1185
  if (this.symlinkCount === 0 && isCleanAbsolutePath(path)) {
832
1186
  const fastDir = cache.lastFastDirNode;
@@ -967,12 +1321,33 @@ export class MemoryFileSystem {
967
1321
  this.ledger.fileSize(length, syscall, path);
968
1322
  this.ledger.reserve(length, 0, syscall, path);
969
1323
  if (length === 0) {
970
- return new MemoryAllocation(EMPTY_ALLOC_BYTES, this.ledger);
1324
+ return DUMMY_POOL_ALLOCATION;
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
+ }
971
1341
  }
972
1342
  if (length === 64) {
973
- const pooled = (this.ledger.hasInfiniteRetained && this.ledger.limits.maxFileBytes === Infinity
974
- ? sharedAllocationPool.pop()
975
- : undefined) ?? memoryCaches.get(this.ledger).allocations.pop();
1343
+ let pooled;
1344
+ if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes && sharedAllocationPoolLen > 0) {
1345
+ pooled = sharedAllocationPool[--sharedAllocationPoolLen];
1346
+ sharedAllocationPool[sharedAllocationPoolLen] = DUMMY_POOL_ALLOCATION;
1347
+ }
1348
+ else {
1349
+ pooled = memoryCaches.get(this.ledger).allocations.pop();
1350
+ }
976
1351
  if (pooled) {
977
1352
  pooled.reuse(this.ledger);
978
1353
  return pooled;
@@ -981,7 +1356,7 @@ export class MemoryFileSystem {
981
1356
  try {
982
1357
  // A shared slab's backing buffer exceeds the admitted slice capacity.
983
1358
  // Finite file/storage ceilings require independently owned exact buffers.
984
- if (length <= 64 && this.ledger.hasInfiniteRetained && this.ledger.limits.maxFileBytes === Infinity) {
1359
+ if (length <= 64 && this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes) {
985
1360
  if (smallAllocOffset + length > SMALL_ALLOC_SLAB_SIZE) {
986
1361
  smallAllocSlab = new Uint8Array(SMALL_ALLOC_SLAB_SIZE);
987
1362
  smallAllocOffset = 0;
@@ -1092,7 +1467,14 @@ export class MemoryFileSystem {
1092
1467
  const now = Date.now();
1093
1468
  const fileMode = typeModes.file | target.mode;
1094
1469
  const view = capacity === length ? allocation.data : undefined;
1095
- const pooled = sharedFileNodePool.pop() ?? memoryCaches.get(this.ledger).files.pop();
1470
+ let pooled;
1471
+ if (sharedFileNodePoolLen > 0) {
1472
+ pooled = sharedFileNodePool[--sharedFileNodePoolLen];
1473
+ sharedFileNodePool[sharedFileNodePoolLen] = DUMMY_POOL_FILE_NODE;
1474
+ }
1475
+ else {
1476
+ pooled = memoryCaches.get(this.ledger).files.pop();
1477
+ }
1096
1478
  if (pooled) {
1097
1479
  pooled.mode = fileMode;
1098
1480
  pooled.ino = this.nextInode++;
@@ -1106,9 +1488,10 @@ export class MemoryFileSystem {
1106
1488
  pooled.byteLength = length;
1107
1489
  pooled.view = view;
1108
1490
  pooled.allocation = allocation;
1491
+ pooled.sourceRef = data;
1109
1492
  return pooled;
1110
1493
  }
1111
- return new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view);
1494
+ return new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view, data);
1112
1495
  }, syscall, path);
1113
1496
  }
1114
1497
  catch (error) {
@@ -1128,11 +1511,14 @@ export class MemoryFileSystem {
1128
1511
  }
1129
1512
  }
1130
1513
  writeAt(node, data, position, syscall, path) {
1131
- if (data.byteLength === 0) {
1132
- this.changed(node);
1514
+ this.writeAtRange(node, data, data.byteLength, position, syscall, path, Date.now());
1515
+ }
1516
+ writeAtRange(node, data, dataLen, position, syscall, path, now) {
1517
+ if (dataLen === 0) {
1518
+ this.changed(node, now);
1133
1519
  return;
1134
1520
  }
1135
- const end = position + data.byteLength;
1521
+ const end = position + dataLen;
1136
1522
  this.ledger.fileSize(end, syscall, path);
1137
1523
  const curLen = node.byteLength;
1138
1524
  const length = Math.max(curLen, end);
@@ -1140,22 +1526,38 @@ export class MemoryFileSystem {
1140
1526
  let allocation = node.allocation;
1141
1527
  if (length > allocation.data.byteLength) {
1142
1528
  this.ledger.check(length, 0, syscall, path);
1143
- const baseCap = curLen === 0 && length >= 8192 && length <= 16384 &&
1144
- this.ledger.limits.maxFileBytes >= 65536 &&
1145
- this.ledger.availableBytes >= 65536 &&
1146
- (this.ledger.limits.maxBytes === undefined || this.ledger.limits.maxBytes - this.totalBytes >= 65536)
1147
- ? 65536
1148
- : Math.max(length, curLen * 2, 64);
1149
- 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
+ }
1150
1542
  allocation = this.allocate(capacity, syscall, path);
1151
1543
  }
1152
1544
  try {
1153
1545
  const storage = allocation.data;
1154
- if (allocation !== node.allocation)
1155
- storage.set(node.view ?? (curLen === node.allocation.data.byteLength ? node.allocation.data : node.allocation.data.subarray(0, curLen)));
1546
+ if (allocation !== node.allocation && curLen > 0) {
1547
+ storage.set((node.view ?? node.allocation.data).subarray(0, curLen));
1548
+ }
1156
1549
  if (position > curLen)
1157
1550
  storage.fill(0, curLen, position);
1158
- storage.set(data, position);
1551
+ if (dataLen === data.byteLength) {
1552
+ storage.set(data, position);
1553
+ }
1554
+ else if (typeof data.copy === "function") {
1555
+ data.copy(storage, position, 0, dataLen);
1556
+ }
1557
+ else {
1558
+ for (let i = 0; i < dataLen; i++)
1559
+ storage[position + i] = data[i];
1560
+ }
1159
1561
  }
1160
1562
  catch (error) {
1161
1563
  if (allocation !== node.allocation)
@@ -1169,7 +1571,7 @@ export class MemoryFileSystem {
1169
1571
  node.byteLength = length;
1170
1572
  node.view = length === allocation.data.byteLength ? allocation.data : undefined;
1171
1573
  }
1172
- this.changed(node);
1574
+ this.changed(node, now);
1173
1575
  }
1174
1576
  writeMemoryFileFast(path, data, append, mode) {
1175
1577
  const cache = memoryCaches.get(this.ledger);
@@ -1220,7 +1622,7 @@ export class MemoryFileSystem {
1220
1622
  }
1221
1623
  break;
1222
1624
  }
1223
- const seg = path.slice(start, slash);
1625
+ const seg = slicePathSegment(path, start, slash);
1224
1626
  if (exceedsComponentByteLimit(seg))
1225
1627
  this.fail("ENAMETOOLONG", syscall, path);
1226
1628
  const next = parent.entries.get(seg);
@@ -1232,7 +1634,7 @@ export class MemoryFileSystem {
1232
1634
  start = slash + 1;
1233
1635
  }
1234
1636
  }
1235
- const existing = parent.entries.get(name);
1637
+ const existing = parent.entries.getForWrite ? parent.entries.getForWrite(name) : parent.entries.get(name);
1236
1638
  if (existing) {
1237
1639
  if (existing.type !== "file")
1238
1640
  this.fail("EISDIR", syscall, path);
@@ -1255,7 +1657,7 @@ export class MemoryFileSystem {
1255
1657
  return;
1256
1658
  }
1257
1659
  if (!current) {
1258
- 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;
1259
1661
  const allocation = this.allocate(capacity, syscall, path);
1260
1662
  try {
1261
1663
  allocation.data.set(data);
@@ -1264,7 +1666,14 @@ export class MemoryFileSystem {
1264
1666
  const now = Date.now();
1265
1667
  const fileMode = typeModes.file | mode;
1266
1668
  const view = capacity === length ? allocation.data : undefined;
1267
- let node = sharedFileNodePool.pop() ?? cache.files.pop();
1669
+ let node;
1670
+ if (sharedFileNodePoolLen > 0) {
1671
+ node = sharedFileNodePool[--sharedFileNodePoolLen];
1672
+ sharedFileNodePool[sharedFileNodePoolLen] = DUMMY_POOL_FILE_NODE;
1673
+ }
1674
+ else {
1675
+ node = cache.files.pop();
1676
+ }
1268
1677
  if (node) {
1269
1678
  node.mode = fileMode;
1270
1679
  node.ino = this.nextInode++;
@@ -1278,9 +1687,10 @@ export class MemoryFileSystem {
1278
1687
  node.byteLength = length;
1279
1688
  node.view = view;
1280
1689
  node.allocation = allocation;
1690
+ node.sourceRef = data;
1281
1691
  }
1282
1692
  else {
1283
- node = new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view);
1693
+ node = new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view, data);
1284
1694
  }
1285
1695
  const prevNlink = parent.cachedNlinkRev === parent.revision ? parent.cachedNlink : (parent.entries.size === 0 ? 2 : undefined);
1286
1696
  parent.entries.set(name, node);
@@ -1318,8 +1728,133 @@ export class MemoryFileSystem {
1318
1728
  throw error;
1319
1729
  }
1320
1730
  }
1321
- writeMemoryFileInDirFast(dirPrefix, name, data, append, mode) {
1322
- 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
+ }
1323
1858
  const syscall = append ? "appendFile" : "writeFile";
1324
1859
  try {
1325
1860
  if (append &&
@@ -1354,7 +1889,7 @@ export class MemoryFileSystem {
1354
1889
  while (start < dirPrefix.length) {
1355
1890
  this.permission(parent, 1, syscall, name);
1356
1891
  const slash = dirPrefix.indexOf("/", start);
1357
- 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);
1358
1893
  if (exceedsComponentByteLimit(seg))
1359
1894
  this.fail("ENAMETOOLONG", syscall, name);
1360
1895
  const next = parent.entries.get(seg);
@@ -1375,7 +1910,7 @@ export class MemoryFileSystem {
1375
1910
  cache.lastFastDirNode = parent;
1376
1911
  }
1377
1912
  }
1378
- const existing = parent.entries.get(name);
1913
+ const existing = parent.entries.getForWrite ? parent.entries.getForWrite(name) : parent.entries.get(name);
1379
1914
  if (existing) {
1380
1915
  if (existing.type !== "file")
1381
1916
  this.fail("EISDIR", syscall, name);
@@ -1398,22 +1933,34 @@ export class MemoryFileSystem {
1398
1933
  return;
1399
1934
  }
1400
1935
  if (!current) {
1401
- const capacity = length > 0 && length < 64 && this.ledger.hasInfiniteRetained && this.ledger.limits.maxFileBytes === Infinity ? 64 : length;
1402
- 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);
1403
1939
  try {
1404
- allocation.data.set(data);
1940
+ if (!zeroCopyConst)
1941
+ allocation.data.set(data);
1405
1942
  this.ledger.reserve(nameBytes, 2, syscall, name);
1406
1943
  try {
1407
- const now = Date.now();
1944
+ const now = fastWriteCachedNow;
1408
1945
  const fileMode = typeModes.file | mode;
1409
- const view = capacity === length ? allocation.data : undefined;
1410
- let node = sharedFileNodePool.pop() ?? cache.files.pop();
1946
+ const view = zeroCopyConst ? data : (capacity === length ? allocation.data : undefined);
1947
+ let node;
1948
+ if (sharedFileNodePoolLen > 0) {
1949
+ node = sharedFileNodePool[--sharedFileNodePoolLen];
1950
+ sharedFileNodePool[sharedFileNodePoolLen] = DUMMY_POOL_FILE_NODE;
1951
+ }
1952
+ else {
1953
+ node = cache.files.pop();
1954
+ }
1411
1955
  if (node) {
1412
1956
  node.mode = fileMode;
1413
1957
  node.ino = this.nextInode++;
1414
- node.nlink = 1;
1415
- node.references = 0;
1416
- node.revision = 0;
1958
+ if (node.nlink !== 1)
1959
+ node.nlink = 1;
1960
+ if (node.references !== 0)
1961
+ node.references = 0;
1962
+ if (node.revision !== 0)
1963
+ node.revision = 0;
1417
1964
  node.atimeMs = now;
1418
1965
  node.mtimeMs = now;
1419
1966
  node.ctimeMs = now;
@@ -1421,9 +1968,10 @@ export class MemoryFileSystem {
1421
1968
  node.byteLength = length;
1422
1969
  node.view = view;
1423
1970
  node.allocation = allocation;
1971
+ node.sourceRef = data;
1424
1972
  }
1425
1973
  else {
1426
- node = new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view);
1974
+ node = new MemoryFileNode(fileMode, this.nextInode++, now, length, allocation, view, data);
1427
1975
  }
1428
1976
  const prevNlink = parent.cachedNlinkRev === parent.revision ? parent.cachedNlink : (parent.entries.size === 0 ? 2 : undefined);
1429
1977
  parent.entries.set(name, node);
@@ -1436,8 +1984,12 @@ export class MemoryFileSystem {
1436
1984
  cache.lastFastFilePath = "";
1437
1985
  cache.lastFastFileName = name;
1438
1986
  cache.lastFastFileNode = node;
1987
+ lastFastMapMiss.map = undefined;
1988
+ lastFastMapMiss.key = "";
1439
1989
  }
1440
1990
  catch (error) {
1991
+ lastFastMapMiss.map = undefined;
1992
+ lastFastMapMiss.key = "";
1441
1993
  this.ledger.release(nameBytes, 2);
1442
1994
  throw error;
1443
1995
  }
@@ -1915,6 +2467,45 @@ export class MemoryFileSystem {
1915
2467
  }
1916
2468
  return result.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
1917
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
+ }
1918
2509
  mkdir(path, options = {}) {
1919
2510
  try {
1920
2511
  options.signal?.throwIfAborted();
@@ -1991,7 +2582,7 @@ export class MemoryFileSystem {
1991
2582
  return Promise.reject(error);
1992
2583
  }
1993
2584
  }
1994
- removeLocation(location, path, syscall, recursive) {
2585
+ removeLocation(location, path, syscall, recursive, now = Date.now()) {
1995
2586
  const node = location.node;
1996
2587
  if (this.terminalDot(path))
1997
2588
  this.fail("EINVAL", syscall, path);
@@ -2003,12 +2594,12 @@ export class MemoryFileSystem {
2003
2594
  const cache = memoryCaches.get(this.ledger);
2004
2595
  cache.clearWrites();
2005
2596
  const removed = sharedRemovedScratch;
2006
- removed.length = 0;
2007
- removed.push(node);
2597
+ sharedRemovedLen = 1;
2598
+ removed[0] = node;
2008
2599
  sharedRemovedKeyChars = 0;
2009
2600
  sharedRemovedKeyEntries = 0;
2010
2601
  try {
2011
- for (let index = 0; index < removed.length; index++) {
2602
+ for (let index = 0; index < sharedRemovedLen; index++) {
2012
2603
  const entry = removed[index];
2013
2604
  if (entry.type === "directory" && entry.entries.size > 0) {
2014
2605
  this.permission(entry, 7, syscall, path);
@@ -2017,9 +2608,10 @@ export class MemoryFileSystem {
2017
2608
  }
2018
2609
  location.parent.entries.delete(location.name);
2019
2610
  this.ledger.release((location.name.length + sharedRemovedKeyChars) * 2, 1 + sharedRemovedKeyEntries);
2020
- const now = Date.now();
2021
- for (let i = 0; i < removed.length; i++) {
2611
+ const totalRemoved = sharedRemovedLen;
2612
+ for (let i = 0; i < totalRemoved; i++) {
2022
2613
  const entry = removed[i];
2614
+ removed[i] = DUMMY_POOL_FILE_NODE;
2023
2615
  if (entry.type === "directory") {
2024
2616
  entry.entries.clear();
2025
2617
  }
@@ -2032,9 +2624,11 @@ export class MemoryFileSystem {
2032
2624
  }
2033
2625
  }
2034
2626
  finally {
2035
- removed.length = 0;
2627
+ for (let i = 0; i < sharedRemovedLen; i++)
2628
+ removed[i] = DUMMY_POOL_FILE_NODE;
2629
+ sharedRemovedLen = 0;
2036
2630
  }
2037
- this.changed(location.parent);
2631
+ this.changed(location.parent, now);
2038
2632
  }
2039
2633
  unlink(path, options = {}) {
2040
2634
  // Do not allow an untyped recursive option to widen final-entry removal.
@@ -2490,65 +3084,225 @@ export class MemoryRedirectHandle {
2490
3084
  this.position = 0;
2491
3085
  this.closed = false;
2492
3086
  }
2493
- writeSync(chunk, signal) {
3087
+ reset(fs, path, node, append) {
3088
+ this.fs = fs;
3089
+ this.path = path;
3090
+ this.node = node;
3091
+ this.append = append;
3092
+ this.position = 0;
3093
+ this.closed = false;
3094
+ }
3095
+ writeRangeSync(src, len, signal) {
2494
3096
  signal?.throwIfAborted();
2495
3097
  if (this.closed)
2496
3098
  throw new FsError("EBADF", { syscall: "write", path: this.path });
2497
- if (chunk.byteLength === 0)
3099
+ if (len === 0)
2498
3100
  return;
2499
3101
  const pos = this.append ? this.node.byteLength : this.position;
2500
- this.fs.writeAt(this.node, chunk, pos, "write", this.path);
3102
+ const now = Date.now === defaultDateNow ? fastWriteCachedNow : Date.now();
3103
+ this.fs.writeAtRange(this.node, src, len, pos, "write", this.path, now);
2501
3104
  if (!this.append)
2502
- this.position = pos + chunk.byteLength;
3105
+ this.position = pos + len;
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
+ }
3133
+ writeSync(chunk, signal) {
3134
+ this.writeRangeSync(chunk, chunk.byteLength, signal);
2503
3135
  }
2504
3136
  close() {
2505
3137
  if (this.closed)
2506
3138
  return;
2507
3139
  this.closed = true;
2508
- this.fs.releaseReference(this.node, this.path);
3140
+ const fs = this.fs;
3141
+ const node = this.node;
3142
+ const path = this.path;
3143
+ this.fs = undefined;
3144
+ this.node = DUMMY_POOL_FILE_NODE;
3145
+ this.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
+ }
3153
+ if (redirectHandlePool.handle === undefined) {
3154
+ redirectHandlePool.handle = this;
3155
+ }
2509
3156
  }
2510
3157
  }
3158
+ const redirectHandlePool = { handle: undefined };
3159
+ let _lastRedirectSlice1Path = "";
3160
+ let _lastRedirectSlice1Name = "";
3161
+ let _lastFileSlice1Path = "";
3162
+ let _lastFileSlice1Name = "";
2511
3163
  export function tryOpenMemoryRedirectHandleSync(filesystem, path, append, mode, signal) {
2512
3164
  const mem = filesystem;
2513
- const owner = ownedStores.get(mem);
3165
+ const owner = mem._owner ?? ownedStores.get(mem);
2514
3166
  if (!owner ||
2515
3167
  mem.symlinkCount !== 0 ||
2516
3168
  mem.capabilities !== owner.capabilities ||
2517
- activeConditionalMutations.has(mem.identityScope) ||
3169
+ (activeConditionalMutations.size > 0 && activeConditionalMutations.has(mem.identityScope)) ||
2518
3170
  !isStockMemoryMethods(mem, openRedirectFastMethodNames, false) ||
2519
3171
  !isCleanAbsolutePath(path) ||
3172
+ path === "/" ||
2520
3173
  path === "/dev" ||
2521
3174
  path.startsWith("/dev/")) {
2522
3175
  return undefined;
2523
3176
  }
2524
3177
  signal?.throwIfAborted();
2525
- const validMode = mem.mode(mode, 0o666, "open", path);
2526
- const location = mem.resolve(path, "open", {
2527
- followFinal: true,
2528
- allowMissing: true,
2529
- });
2530
- let node = location.node;
2531
- if (node) {
2532
- if (node.type !== "file")
2533
- mem.fail("EISDIR", "open", path);
2534
- mem.permission(node, 2, "open", path);
2535
- }
2536
- else {
2537
- mem.permission(location.parent, 3, "open", path);
3178
+ const validMode = (mode & 0o777) === mode
3179
+ ? mode
3180
+ : mem.mode(mode, 0o666, "open", path);
3181
+ const memInternal = mem;
3182
+ let parent = memInternal.root;
3183
+ let start = 1;
3184
+ let name = "";
3185
+ while (true) {
3186
+ memInternal.permission(parent, 1, "open", path);
3187
+ const slash = path.indexOf("/", start);
3188
+ if (slash === -1) {
3189
+ name = start === 1
3190
+ ? (path === _lastRedirectSlice1Path ? _lastRedirectSlice1Name : (_lastRedirectSlice1Path = path, _lastRedirectSlice1Name = path.slice(1)))
3191
+ : path.slice(start);
3192
+ if (exceedsComponentByteLimit(name))
3193
+ memInternal.fail("ENAMETOOLONG", "open", path);
3194
+ break;
3195
+ }
3196
+ const seg = slicePathSegment(path, start, slash);
3197
+ if (exceedsComponentByteLimit(seg))
3198
+ memInternal.fail("ENAMETOOLONG", "open", path);
3199
+ const next = parent.entries.get(seg);
3200
+ if (!next)
3201
+ memInternal.fail("ENOENT", "open", path);
3202
+ if (next.type !== "directory")
3203
+ memInternal.fail("ENOTDIR", "open", path);
3204
+ parent = next;
3205
+ start = slash + 1;
2538
3206
  }
2539
- const ledger = mem.ledger;
2540
- ledger.reserve(path.length * 2, 1, "open", path);
2541
- try {
2542
- node ??= mem.openWrite(path, {}, "open", { location, mode: validMode, append: false });
3207
+ const existing = parent.entries.getForWrite(name);
3208
+ const ledger = memInternal.ledger;
3209
+ let node;
3210
+ if (existing !== undefined) {
3211
+ if (existing.type !== "file")
3212
+ memInternal.fail("EISDIR", "open", path);
3213
+ memInternal.permission(existing, 2, "open", path);
3214
+ ledger.reserve(path.length * 2, 1, "open", path);
3215
+ node = existing;
2543
3216
  if (!append) {
2544
- mem.resizeNode(node, 0, "open", path);
3217
+ try {
3218
+ memInternal.resizeNode(node, 0, "open", path);
3219
+ }
3220
+ catch (error) {
3221
+ ledger.release(path.length * 2, 1);
3222
+ throw error;
3223
+ }
3224
+ }
3225
+ node.references++;
3226
+ }
3227
+ else {
3228
+ memInternal.permission(parent, 3, "open", path);
3229
+ const refBytes = path.length * 2;
3230
+ const nameBytes = name.length * 2;
3231
+ if (ledger.hasInfiniteRetained && ledger.hasInfiniteFileBytes) {
3232
+ ledger.reserve(refBytes + nameBytes, 3, "open", path);
3233
+ }
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
+ }
3244
+ }
3245
+ const cache = mem._cache;
3246
+ cache.clearWrites();
3247
+ const now = Date.now === defaultDateNow ? ((++fastWriteNowTick & 63) === 0 ? (fastWriteCachedNow = Date.now()) : fastWriteCachedNow) : Date.now();
3248
+ const fileMode = typeModes.file | validMode;
3249
+ let newNode;
3250
+ if (sharedFileNodePoolLen > 0) {
3251
+ newNode = sharedFileNodePool[--sharedFileNodePoolLen];
3252
+ sharedFileNodePool[sharedFileNodePoolLen] = DUMMY_POOL_FILE_NODE;
3253
+ }
3254
+ else {
3255
+ newNode = cache.files.pop();
3256
+ }
3257
+ if (newNode) {
3258
+ newNode.mode = fileMode;
3259
+ newNode.ino = memInternal.nextInode++;
3260
+ if (newNode.nlink !== 1)
3261
+ newNode.nlink = 1;
3262
+ newNode.references = 1;
3263
+ if (newNode.revision !== 0)
3264
+ newNode.revision = 0;
3265
+ newNode.atimeMs = now;
3266
+ newNode.mtimeMs = now;
3267
+ newNode.ctimeMs = now;
3268
+ newNode.birthtimeMs = now;
3269
+ newNode.byteLength = 0;
3270
+ newNode.view = EMPTY_ALLOC_BYTES;
3271
+ newNode.allocation = DUMMY_POOL_ALLOCATION;
3272
+ }
3273
+ else {
3274
+ newNode = new MemoryFileNode(fileMode, memInternal.nextInode++, now, 0, DUMMY_POOL_ALLOCATION, EMPTY_ALLOC_BYTES);
3275
+ newNode.references = 1;
3276
+ }
3277
+ const prevNlink = parent.cachedNlinkRev === parent.revision ? parent.cachedNlink : (parent.entries.size === 0 ? 2 : undefined);
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 = "";
2545
3287
  }
3288
+ else {
3289
+ entries.set(name, newNode);
3290
+ }
3291
+ mem.mutationTick = (mem.mutationTick + 1) | 0;
3292
+ parent.revision = parent.revision < 1073741823 ? (parent.revision + 1) | 0 : Math.min(Number.MAX_SAFE_INTEGER + 1, parent.revision + 1);
3293
+ parent.mtimeMs = parent.ctimeMs = now;
3294
+ if (prevNlink !== undefined) {
3295
+ parent.cachedNlink = prevNlink;
3296
+ parent.cachedNlinkRev = parent.revision;
3297
+ }
3298
+ node = newNode;
2546
3299
  }
2547
- catch (error) {
2548
- ledger.release(path.length * 2, 1);
2549
- throw error;
3300
+ const handle = redirectHandlePool.handle;
3301
+ if (handle !== undefined) {
3302
+ redirectHandlePool.handle = undefined;
3303
+ handle.reset(mem, path, node, append);
3304
+ return handle;
2550
3305
  }
2551
- node.references++;
2552
3306
  return new MemoryRedirectHandle(mem, path, node, append);
2553
3307
  }
2554
3308
  export function utf8ByteLength(value) {
@@ -2614,14 +3368,14 @@ export function tryResolveMemoryDevicePath(filesystem, path, resizeCreate) {
2614
3368
  return undefined;
2615
3369
  const slash = path.indexOf("/", start);
2616
3370
  if (slash === -1) {
2617
- const name = path.slice(start);
3371
+ const name = slicePathSegment(path, start, path.length);
2618
3372
  if (exceedsComponentByteLimit(name))
2619
3373
  return undefined;
2620
3374
  if (resizeCreate === false && !current.entries.has(name))
2621
3375
  return undefined;
2622
3376
  return path;
2623
3377
  }
2624
- const name = path.slice(start, slash);
3378
+ const name = slicePathSegment(path, start, slash);
2625
3379
  if (exceedsComponentByteLimit(name))
2626
3380
  return undefined;
2627
3381
  const next = current.entries.get(name);
@@ -2631,7 +3385,7 @@ export function tryResolveMemoryDevicePath(filesystem, path, resizeCreate) {
2631
3385
  let remStart = slash + 1;
2632
3386
  while (true) {
2633
3387
  const nextSlash = path.indexOf("/", remStart);
2634
- 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);
2635
3389
  if (exceedsComponentByteLimit(seg))
2636
3390
  return undefined;
2637
3391
  if (nextSlash === -1)
@@ -2645,9 +3399,19 @@ export function tryResolveMemoryDevicePath(filesystem, path, resizeCreate) {
2645
3399
  start = slash + 1;
2646
3400
  }
2647
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;
2648
3412
  function isStockMemoryMethods(mem, names, checkRootAccessor = true) {
2649
- const owner = ownedStores.get(mem);
2650
- 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)
2651
3415
  return false;
2652
3416
  if (checkRootAccessor) {
2653
3417
  const rootDesc = Object.getOwnPropertyDescriptor(mem, "root");
@@ -2657,36 +3421,124 @@ function isStockMemoryMethods(mem, names, checkRootAccessor = true) {
2657
3421
  else if (mem.root !== owner.root) {
2658
3422
  return false;
2659
3423
  }
3424
+ const vals = getExpectedMethodValues(names);
3425
+ const proto = MemoryFileSystem.prototype;
2660
3426
  for (let i = 0; i < names.length; i++) {
2661
3427
  const name = names[i];
2662
- if (Object.prototype.hasOwnProperty.call(mem, name)) {
3428
+ const expected = vals[i];
3429
+ if (hasOwn.call(mem, name)) {
2663
3430
  const descriptor = Object.getOwnPropertyDescriptor(mem, name);
2664
- if (!descriptor || !("value" in descriptor) || descriptor.value !== memoryImplementation[name]?.value)
3431
+ if (!descriptor || !("value" in descriptor) || descriptor.value !== expected)
2665
3432
  return false;
2666
3433
  }
2667
- else if (MemoryFileSystem.prototype[name] !== memoryImplementation[name]?.value) {
3434
+ else if (proto[name] !== expected) {
2668
3435
  return false;
2669
3436
  }
2670
3437
  }
2671
3438
  return true;
2672
3439
  }
2673
- 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) {
2674
3449
  const mem = filesystem;
2675
- if (!ownedStores.has(mem) || !isStockMemoryMethods(mem, readFileFastMethodNames, false))
3450
+ if (mem._owner === undefined || !isStockMemoryMethods(mem, readFileFastMethodNames, false))
2676
3451
  return undefined;
2677
3452
  signal?.throwIfAborted();
2678
3453
  if (maxBytes !== undefined)
2679
3454
  mem.integer(maxBytes, "readFile", path);
2680
3455
  const node = mem.file(path, "readFile");
2681
3456
  mem.permission(node, 4, "readFile", path);
2682
- if (maxBytes !== undefined && node.data.byteLength > maxBytes)
3457
+ const data = node.data;
3458
+ if (maxBytes !== undefined && data.byteLength > maxBytes)
2683
3459
  mem.fail("EFBIG", "readFile", path);
2684
- node.atimeMs = Date.now();
2685
- 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);
2686
3537
  }
2687
3538
  export function tryGetMemoryDirectoryEntryNamesSync(filesystem, path) {
2688
3539
  const mem = filesystem;
2689
- if (!ownedStores.has(mem) || mem.symlinkCount !== 0 || !isStockMemoryMethods(mem, readFileFastMethodNames, false))
3540
+ const owner = ownedStores.get(mem);
3541
+ if (!owner || mem.symlinkCount !== 0 || !isStockMemoryMethods(mem, readFileFastMethodNames, false))
2690
3542
  return undefined;
2691
3543
  const root = mem.root;
2692
3544
  if (path === "/") {
@@ -2696,6 +3548,16 @@ export function tryGetMemoryDirectoryEntryNamesSync(filesystem, path) {
2696
3548
  }
2697
3549
  if (!isCleanAbsolutePath(path) || path === "/dev" || path.startsWith("/dev/"))
2698
3550
  return undefined;
3551
+ const cache = memoryCaches.get(owner.ledger);
3552
+ const fastDir = cache?.lastFastDirNode;
3553
+ const fastPrefix = cache?.lastFastDirPrefix;
3554
+ if (fastDir !== undefined &&
3555
+ fastDir.nlink !== 0 &&
3556
+ ((fastDir.mode >> 6) & 4) === 4 &&
3557
+ fastPrefix !== undefined &&
3558
+ (fastPrefix === path || (fastPrefix.length === path.length + 1 && fastPrefix.charCodeAt(path.length) === 47 && fastPrefix.startsWith(path)))) {
3559
+ return fastDir.entries;
3560
+ }
2699
3561
  let current = root;
2700
3562
  let start = 1;
2701
3563
  while (true) {
@@ -2703,12 +3565,12 @@ export function tryGetMemoryDirectoryEntryNamesSync(filesystem, path) {
2703
3565
  return undefined;
2704
3566
  const slash = path.indexOf("/", start);
2705
3567
  if (slash === -1) {
2706
- const next = current.entries.get(path.slice(start));
3568
+ const next = current.entries.getSubstr(path, start, path.length);
2707
3569
  if (!next || next.type !== "directory" || ((next.mode >> 6) & 4) !== 4)
2708
3570
  return undefined;
2709
3571
  return next.entries;
2710
3572
  }
2711
- const next = current.entries.get(path.slice(start, slash));
3573
+ const next = current.entries.getSubstr(path, start, slash);
2712
3574
  if (!next || next.type !== "directory")
2713
3575
  return undefined;
2714
3576
  current = next;
@@ -2731,7 +3593,7 @@ export function tryWriteMemoryFileSync(filesystem, path, data, append, mode, sig
2731
3593
  mem.writeMemoryFileFast(path, data, append, mode);
2732
3594
  return true;
2733
3595
  }
2734
- export function tryMkdirMemorySync(filesystem, path, recursive, mode, signal) {
3596
+ export function tryMkdirMemorySync(filesystem, path, recursive, mode, signal, dirPrefixHint) {
2735
3597
  const mem = filesystem;
2736
3598
  const owner = ownedStores.get(mem);
2737
3599
  if (!owner ||
@@ -2746,16 +3608,39 @@ export function tryMkdirMemorySync(filesystem, path, recursive, mode, signal) {
2746
3608
  signal?.throwIfAborted();
2747
3609
  const validMode = mem.mode(mode, 0o777, "mkdir", path);
2748
3610
  if (recursive) {
2749
- const node = mem.resolve(path, "mkdir", { createDirectories: validMode }).node;
2750
- if (node.type !== "directory")
2751
- mem.fail("EEXIST", "mkdir", path);
3611
+ let current = owner.root;
3612
+ let start = 1;
3613
+ while (true) {
3614
+ mem.permission(current, 1, "mkdir", path);
3615
+ const slash = path.indexOf("/", start);
3616
+ const seg = slash === -1 ? slicePathSegment(path, start, path.length) : slicePathSegment(path, start, slash);
3617
+ if (exceedsComponentByteLimit(seg))
3618
+ mem.fail("ENAMETOOLONG", "mkdir", path);
3619
+ let next = current.entries.get(seg);
3620
+ if (!next) {
3621
+ mem.permission(current, 3, "mkdir", path);
3622
+ next = mem.addDirectoryNode(current, seg, validMode, "mkdir", path, fastWriteCachedNow);
3623
+ }
3624
+ else if (next.type !== "directory") {
3625
+ mem.fail(slash === -1 ? "EEXIST" : "ENOTDIR", "mkdir", path);
3626
+ }
3627
+ if (slash === -1) {
3628
+ if (dirPrefixHint !== undefined) {
3629
+ mem._cache.lastFastDirPrefix = dirPrefixHint;
3630
+ mem._cache.lastFastDirNode = next;
3631
+ }
3632
+ break;
3633
+ }
3634
+ current = next;
3635
+ start = slash + 1;
3636
+ }
2752
3637
  return true;
2753
3638
  }
2754
- const location = mem.resolve(path, "mkdir", { allowMissing: true, followFinal: false });
3639
+ const location = mem.resolve(path, "mkdir", sharedAllowMissingNoFollowOptions);
2755
3640
  if (location.node)
2756
3641
  mem.fail("EEXIST", "mkdir", path);
2757
3642
  mem.permission(location.parent, 3, "mkdir", path);
2758
- mem.addDirectoryNode(location.parent, location.name, validMode, "mkdir", path);
3643
+ mem.addDirectoryNode(location.parent, location.name, validMode, "mkdir", path, fastWriteCachedNow);
2759
3644
  return true;
2760
3645
  }
2761
3646
  export function tryRmRfMemorySync(filesystem, path, signal) {
@@ -2772,16 +3657,46 @@ export function tryRmRfMemorySync(filesystem, path, signal) {
2772
3657
  return false;
2773
3658
  }
2774
3659
  signal?.throwIfAborted();
2775
- let location;
3660
+ let current = owner.root;
3661
+ let start = 1;
3662
+ let targetNode;
3663
+ let targetName = "";
3664
+ while (true) {
3665
+ mem.permission(current, 1, "rm", path);
3666
+ const slash = path.indexOf("/", start);
3667
+ if (slash === -1) {
3668
+ targetName = slicePathSegment(path, start, path.length);
3669
+ if (exceedsComponentByteLimit(targetName))
3670
+ mem.fail("ENAMETOOLONG", "rm", path);
3671
+ targetNode = current.entries.get(targetName);
3672
+ if (!targetNode)
3673
+ return true;
3674
+ break;
3675
+ }
3676
+ const seg = slicePathSegment(path, start, slash);
3677
+ if (exceedsComponentByteLimit(seg))
3678
+ mem.fail("ENAMETOOLONG", "rm", path);
3679
+ const next = current.entries.get(seg);
3680
+ if (!next)
3681
+ return true;
3682
+ if (next.type !== "directory")
3683
+ mem.fail("ENOTDIR", "rm", path);
3684
+ current = next;
3685
+ start = slash + 1;
3686
+ }
3687
+ sharedFastLocation.node = targetNode;
3688
+ sharedFastLocation.parent = current;
3689
+ sharedFastLocation.name = targetName;
3690
+ sharedFastLocation.path = path;
2776
3691
  try {
2777
- location = mem.resolve(path, "rm", noFollowResolveOptions);
3692
+ mem.removeLocation(sharedFastLocation, path, "rm", true, fastWriteCachedNow);
2778
3693
  }
2779
- catch (error) {
2780
- if (error instanceof FsError && error.code === "ENOENT")
2781
- return true;
2782
- throw error;
3694
+ finally {
3695
+ sharedFastLocation.node = undefined;
3696
+ sharedFastLocation.parent = DUMMY_POOL_DIR_NODE;
3697
+ sharedFastLocation.name = "";
3698
+ sharedFastLocation.path = "";
2783
3699
  }
2784
- mem.removeLocation(location, path, "rm", true);
2785
3700
  return true;
2786
3701
  }
2787
3702
  export function tryWriteMemoryFileInDirSync(filesystem, dirPrefix, name, data, append, mode, signal) {