@componentor/fs 3.0.43 → 3.0.45
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/index.js +125 -25
- package/dist/index.js.map +1 -1
- package/dist/workers/opfs-sync.worker.js +50 -8
- package/dist/workers/opfs-sync.worker.js.map +1 -1
- package/dist/workers/repair.worker.js +125 -25
- package/dist/workers/repair.worker.js.map +1 -1
- package/dist/workers/server.worker.js +125 -25
- package/dist/workers/server.worker.js.map +1 -1
- package/dist/workers/sync-relay.worker.js +206 -64
- package/dist/workers/sync-relay.worker.js.map +1 -1
- package/package.json +1 -1
- package/readme.md +26 -0
|
@@ -510,14 +510,23 @@ var VFSEngine = class {
|
|
|
510
510
|
growPathTable(needed) {
|
|
511
511
|
const newSize = Math.max(this.pathTableSize * 2, needed + INITIAL_PATH_TABLE_SIZE);
|
|
512
512
|
const growth = newSize - this.pathTableSize;
|
|
513
|
-
const dataSize = this.totalBlocks * this.blockSize;
|
|
514
|
-
const dataBuf = new Uint8Array(dataSize);
|
|
515
|
-
this.handle.read(dataBuf, { at: this.dataOffset });
|
|
516
513
|
const newTotalSize = this.handle.getSize() + growth;
|
|
517
514
|
this.handle.truncate(newTotalSize);
|
|
515
|
+
const dataSize = this.totalBlocks * this.blockSize;
|
|
516
|
+
const CHUNK = 4 * 1024 * 1024;
|
|
517
|
+
const scratch = new Uint8Array(Math.min(CHUNK, Math.max(dataSize, 1)));
|
|
518
|
+
let remaining = dataSize;
|
|
519
|
+
while (remaining > 0) {
|
|
520
|
+
const chunk = Math.min(remaining, CHUNK);
|
|
521
|
+
const srcAt = this.dataOffset + (remaining - chunk);
|
|
522
|
+
const dstAt = this.dataOffset + growth + (remaining - chunk);
|
|
523
|
+
const slice = chunk < scratch.length ? scratch.subarray(0, chunk) : scratch;
|
|
524
|
+
this.handle.read(slice, { at: srcAt });
|
|
525
|
+
this.handle.write(slice, { at: dstAt });
|
|
526
|
+
remaining -= chunk;
|
|
527
|
+
}
|
|
518
528
|
const newBitmapOffset = this.bitmapOffset + growth;
|
|
519
529
|
const newDataOffset = this.dataOffset + growth;
|
|
520
|
-
this.handle.write(dataBuf, { at: newDataOffset });
|
|
521
530
|
this.handle.write(this.bitmap, { at: newBitmapOffset });
|
|
522
531
|
this.pathTableSize = newSize;
|
|
523
532
|
this.bitmapOffset = newBitmapOffset;
|
|
@@ -525,6 +534,23 @@ var VFSEngine = class {
|
|
|
525
534
|
this.superblockDirty = true;
|
|
526
535
|
}
|
|
527
536
|
// ========== Bitmap I/O ==========
|
|
537
|
+
// Write `length` zero bytes at absolute file offset `at` via a small
|
|
538
|
+
// reusable scratch buffer. Used to materialize POSIX "holes" when a
|
|
539
|
+
// write starts past the current file size — those bytes must read as
|
|
540
|
+
// zeros rather than whatever stale data happened to live in the
|
|
541
|
+
// underlying storage blocks.
|
|
542
|
+
zeroFileRange(at, length) {
|
|
543
|
+
if (length <= 0) return;
|
|
544
|
+
const CHUNK = 4 * 1024 * 1024;
|
|
545
|
+
const zeros = new Uint8Array(Math.min(length, CHUNK));
|
|
546
|
+
let written = 0;
|
|
547
|
+
while (written < length) {
|
|
548
|
+
const n = Math.min(CHUNK, length - written);
|
|
549
|
+
const slice = n < zeros.length ? zeros.subarray(0, n) : zeros;
|
|
550
|
+
this.handle.write(slice, { at: at + written });
|
|
551
|
+
written += n;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
528
554
|
allocateBlocks(count) {
|
|
529
555
|
if (count === 0) return 0;
|
|
530
556
|
const bitmap = this.bitmap;
|
|
@@ -849,17 +875,28 @@ var VFSEngine = class {
|
|
|
849
875
|
}
|
|
850
876
|
const inode = this.readInode(existingIdx);
|
|
851
877
|
if (inode.type === INODE_TYPE.DIRECTORY) return { status: CODE_TO_STATUS.EISDIR };
|
|
852
|
-
const
|
|
853
|
-
const
|
|
854
|
-
combined.set(existing);
|
|
855
|
-
combined.set(data, existing.byteLength);
|
|
856
|
-
const neededBlocks = Math.ceil(combined.byteLength / this.blockSize);
|
|
857
|
-
this.freeBlockRange(inode.firstBlock, inode.blockCount);
|
|
878
|
+
const combinedSize = inode.size + data.byteLength;
|
|
879
|
+
const neededBlocks = Math.ceil(combinedSize / this.blockSize);
|
|
858
880
|
const newFirst = this.allocateBlocks(neededBlocks);
|
|
859
|
-
this.
|
|
881
|
+
const newBase = this.dataOffset + newFirst * this.blockSize;
|
|
882
|
+
if (inode.size > 0) {
|
|
883
|
+
const oldBase = this.dataOffset + inode.firstBlock * this.blockSize;
|
|
884
|
+
const CHUNK = 4 * 1024 * 1024;
|
|
885
|
+
const scratch = new Uint8Array(Math.min(CHUNK, inode.size));
|
|
886
|
+
let copied = 0;
|
|
887
|
+
while (copied < inode.size) {
|
|
888
|
+
const n = Math.min(CHUNK, inode.size - copied);
|
|
889
|
+
const slice = n < scratch.length ? scratch.subarray(0, n) : scratch;
|
|
890
|
+
this.handle.read(slice, { at: oldBase + copied });
|
|
891
|
+
this.handle.write(slice, { at: newBase + copied });
|
|
892
|
+
copied += n;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
this.freeBlockRange(inode.firstBlock, inode.blockCount);
|
|
896
|
+
this.handle.write(data, { at: newBase + inode.size });
|
|
860
897
|
inode.firstBlock = newFirst;
|
|
861
898
|
inode.blockCount = neededBlocks;
|
|
862
|
-
inode.size =
|
|
899
|
+
inode.size = combinedSize;
|
|
863
900
|
inode.mtime = Date.now();
|
|
864
901
|
this.writeInode(existingIdx, inode);
|
|
865
902
|
this.commitPending();
|
|
@@ -1122,13 +1159,29 @@ var VFSEngine = class {
|
|
|
1122
1159
|
} else if (len > inode.size) {
|
|
1123
1160
|
const neededBlocks = Math.ceil(len / this.blockSize);
|
|
1124
1161
|
if (neededBlocks > inode.blockCount) {
|
|
1125
|
-
const oldData = this.readData(inode.firstBlock, inode.blockCount, inode.size);
|
|
1126
|
-
this.freeBlockRange(inode.firstBlock, inode.blockCount);
|
|
1127
1162
|
const newFirst = this.allocateBlocks(neededBlocks);
|
|
1128
|
-
const
|
|
1129
|
-
|
|
1130
|
-
|
|
1163
|
+
const newBase = this.dataOffset + newFirst * this.blockSize;
|
|
1164
|
+
if (inode.size > 0) {
|
|
1165
|
+
const oldBase = this.dataOffset + inode.firstBlock * this.blockSize;
|
|
1166
|
+
const CHUNK = 4 * 1024 * 1024;
|
|
1167
|
+
const scratch = new Uint8Array(Math.min(CHUNK, inode.size));
|
|
1168
|
+
let copied = 0;
|
|
1169
|
+
while (copied < inode.size) {
|
|
1170
|
+
const n = Math.min(CHUNK, inode.size - copied);
|
|
1171
|
+
const slice = n < scratch.length ? scratch.subarray(0, n) : scratch;
|
|
1172
|
+
this.handle.read(slice, { at: oldBase + copied });
|
|
1173
|
+
this.handle.write(slice, { at: newBase + copied });
|
|
1174
|
+
copied += n;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
this.freeBlockRange(inode.firstBlock, inode.blockCount);
|
|
1178
|
+
this.zeroFileRange(newBase + inode.size, len - inode.size);
|
|
1131
1179
|
inode.firstBlock = newFirst;
|
|
1180
|
+
} else {
|
|
1181
|
+
this.zeroFileRange(
|
|
1182
|
+
this.dataOffset + inode.firstBlock * this.blockSize + inode.size,
|
|
1183
|
+
len - inode.size
|
|
1184
|
+
);
|
|
1132
1185
|
}
|
|
1133
1186
|
inode.blockCount = neededBlocks;
|
|
1134
1187
|
inode.size = len;
|
|
@@ -1149,8 +1202,36 @@ var VFSEngine = class {
|
|
|
1149
1202
|
if (flags & 1 && this.pathIndex.has(destPath)) {
|
|
1150
1203
|
return { status: CODE_TO_STATUS.EEXIST };
|
|
1151
1204
|
}
|
|
1152
|
-
|
|
1153
|
-
|
|
1205
|
+
if (srcPath === destPath) return { status: 0 };
|
|
1206
|
+
const srcSize = srcInode.size;
|
|
1207
|
+
const srcFirstBlock = srcInode.firstBlock;
|
|
1208
|
+
const emptyStatus = this.write(destPath, new Uint8Array(0));
|
|
1209
|
+
if (emptyStatus.status !== 0) return emptyStatus;
|
|
1210
|
+
if (srcSize === 0) return { status: 0 };
|
|
1211
|
+
const destIdx = this.resolvePathComponents(destPath, true);
|
|
1212
|
+
if (destIdx === void 0) return { status: CODE_TO_STATUS.EIO };
|
|
1213
|
+
const destInode = this.readInode(destIdx);
|
|
1214
|
+
const neededBlocks = Math.ceil(srcSize / this.blockSize);
|
|
1215
|
+
const newFirst = this.allocateBlocks(neededBlocks);
|
|
1216
|
+
const newBase = this.dataOffset + newFirst * this.blockSize;
|
|
1217
|
+
const srcBase = this.dataOffset + srcFirstBlock * this.blockSize;
|
|
1218
|
+
const CHUNK = 4 * 1024 * 1024;
|
|
1219
|
+
const scratch = new Uint8Array(Math.min(CHUNK, srcSize));
|
|
1220
|
+
let copied = 0;
|
|
1221
|
+
while (copied < srcSize) {
|
|
1222
|
+
const n = Math.min(CHUNK, srcSize - copied);
|
|
1223
|
+
const slice = n < scratch.length ? scratch.subarray(0, n) : scratch;
|
|
1224
|
+
this.handle.read(slice, { at: srcBase + copied });
|
|
1225
|
+
this.handle.write(slice, { at: newBase + copied });
|
|
1226
|
+
copied += n;
|
|
1227
|
+
}
|
|
1228
|
+
destInode.firstBlock = newFirst;
|
|
1229
|
+
destInode.blockCount = neededBlocks;
|
|
1230
|
+
destInode.size = srcSize;
|
|
1231
|
+
destInode.mtime = Date.now();
|
|
1232
|
+
this.writeInode(destIdx, destInode);
|
|
1233
|
+
this.commitPending();
|
|
1234
|
+
return { status: 0 };
|
|
1154
1235
|
}
|
|
1155
1236
|
// ---- ACCESS ----
|
|
1156
1237
|
access(path, mode = 0) {
|
|
@@ -1314,16 +1395,35 @@ var VFSEngine = class {
|
|
|
1314
1395
|
if (endPos > inode.size) {
|
|
1315
1396
|
const neededBlocks = Math.ceil(endPos / this.blockSize);
|
|
1316
1397
|
if (neededBlocks > inode.blockCount) {
|
|
1317
|
-
const oldData = inode.size > 0 ? this.readData(inode.firstBlock, inode.blockCount, inode.size) : new Uint8Array(0);
|
|
1318
|
-
this.freeBlockRange(inode.firstBlock, inode.blockCount);
|
|
1319
1398
|
const newFirst = this.allocateBlocks(neededBlocks);
|
|
1320
|
-
const
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1399
|
+
const newBase = this.dataOffset + newFirst * this.blockSize;
|
|
1400
|
+
const oldBase = this.dataOffset + inode.firstBlock * this.blockSize;
|
|
1401
|
+
if (inode.size > 0) {
|
|
1402
|
+
const CHUNK = 4 * 1024 * 1024;
|
|
1403
|
+
const scratch = new Uint8Array(Math.min(CHUNK, inode.size));
|
|
1404
|
+
let copied = 0;
|
|
1405
|
+
while (copied < inode.size) {
|
|
1406
|
+
const n = Math.min(CHUNK, inode.size - copied);
|
|
1407
|
+
const slice = n < scratch.length ? scratch.subarray(0, n) : scratch;
|
|
1408
|
+
this.handle.read(slice, { at: oldBase + copied });
|
|
1409
|
+
this.handle.write(slice, { at: newBase + copied });
|
|
1410
|
+
copied += n;
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
this.freeBlockRange(inode.firstBlock, inode.blockCount);
|
|
1414
|
+
if (pos > inode.size) {
|
|
1415
|
+
this.zeroFileRange(newBase + inode.size, pos - inode.size);
|
|
1416
|
+
}
|
|
1417
|
+
this.handle.write(data, { at: newBase + pos });
|
|
1324
1418
|
inode.firstBlock = newFirst;
|
|
1325
1419
|
inode.blockCount = neededBlocks;
|
|
1326
1420
|
} else {
|
|
1421
|
+
if (pos > inode.size) {
|
|
1422
|
+
this.zeroFileRange(
|
|
1423
|
+
this.dataOffset + inode.firstBlock * this.blockSize + inode.size,
|
|
1424
|
+
pos - inode.size
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1327
1427
|
const dataOffset = this.dataOffset + inode.firstBlock * this.blockSize + pos;
|
|
1328
1428
|
this.handle.write(data, { at: dataOffset });
|
|
1329
1429
|
}
|