@componentor/fs 3.0.44 → 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.
@@ -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 existing = inode.size > 0 ? this.readData(inode.firstBlock, inode.blockCount, inode.size) : new Uint8Array(0);
853
- const combined = new Uint8Array(existing.byteLength + data.byteLength);
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.writeData(newFirst, combined);
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 = combined.byteLength;
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 newData = new Uint8Array(len);
1129
- newData.set(oldData);
1130
- this.writeData(newFirst, newData);
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
- const data = srcInode.size > 0 ? this.readData(srcInode.firstBlock, srcInode.blockCount, srcInode.size) : new Uint8Array(0);
1153
- return this.write(destPath, data);
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 newBuf = new Uint8Array(endPos);
1321
- newBuf.set(oldData);
1322
- newBuf.set(data, pos);
1323
- this.writeData(newFirst, newBuf);
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
  }