@poe-platform/safe-fs 0.1.731 → 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.
- package/dist/safe-fs/contracts/virtual-path.d.ts +1 -0
- package/dist/safe-fs/contracts/virtual-path.js +68 -8
- package/dist/safe-fs/fs/devices/index.js +11 -2
- package/dist/safe-fs/fs/memory/index.d.ts +42 -4
- package/dist/safe-fs/fs/memory/index.js +572 -75
- package/dist/safe-fs/fs/memory/ledger.d.ts +2 -0
- package/dist/safe-fs/fs/memory/ledger.js +6 -0
- package/package.json +1 -1
|
@@ -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.
|
|
635
|
+
if (this.ledger.hasInfiniteRetained && this.ledger.hasInfiniteFileBytes) {
|
|
536
636
|
replenishSharedMemoryPools();
|
|
537
637
|
}
|
|
538
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
1566
|
-
const
|
|
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.
|
|
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
|
|
1646
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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.
|
|
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.
|
|
2857
|
-
|
|
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
|
-
|
|
2862
|
-
ledger.
|
|
2863
|
-
|
|
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 =
|
|
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
|
|
3260
|
+
if (newNode.nlink !== 1)
|
|
3261
|
+
newNode.nlink = 1;
|
|
2881
3262
|
newNode.references = 1;
|
|
2882
|
-
newNode.revision
|
|
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
|
|
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.
|
|
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
|
|
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.
|
|
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
|
-
|
|
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 !==
|
|
3431
|
+
if (!descriptor || !("value" in descriptor) || descriptor.value !== expected)
|
|
3024
3432
|
return false;
|
|
3025
3433
|
}
|
|
3026
|
-
else if (
|
|
3434
|
+
else if (proto[name] !== expected) {
|
|
3027
3435
|
return false;
|
|
3028
3436
|
}
|
|
3029
3437
|
}
|
|
3030
3438
|
return true;
|
|
3031
3439
|
}
|
|
3032
|
-
|
|
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 (
|
|
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
|
-
|
|
3457
|
+
const data = node.data;
|
|
3458
|
+
if (maxBytes !== undefined && data.byteLength > maxBytes)
|
|
3042
3459
|
mem.fail("EFBIG", "readFile", path);
|
|
3043
|
-
|
|
3044
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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);
|