@componentor/fs 3.0.4 → 3.0.6
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/README.md +21 -0
- package/dist/index.js +125 -44
- package/dist/index.js.map +1 -1
- package/dist/workers/server.worker.js +66 -4
- package/dist/workers/server.worker.js.map +1 -1
- package/dist/workers/service.worker.js +1 -2
- package/dist/workers/service.worker.js.map +1 -1
- package/dist/workers/sync-relay.worker.js +67 -5
- package/dist/workers/sync-relay.worker.js.map +1 -1
- package/package.json +1 -1
|
@@ -291,6 +291,10 @@ var VFSEngine = class {
|
|
|
291
291
|
if (hi > this.bitmapDirtyHi) this.bitmapDirtyHi = hi;
|
|
292
292
|
}
|
|
293
293
|
commitPending() {
|
|
294
|
+
if (this.blocksFreedsinceTrim) {
|
|
295
|
+
this.trimTrailingBlocks();
|
|
296
|
+
this.blocksFreedsinceTrim = false;
|
|
297
|
+
}
|
|
294
298
|
if (this.bitmapDirtyHi >= 0) {
|
|
295
299
|
const lo = this.bitmapDirtyLo;
|
|
296
300
|
const hi = this.bitmapDirtyHi;
|
|
@@ -303,13 +307,69 @@ var VFSEngine = class {
|
|
|
303
307
|
this.superblockDirty = false;
|
|
304
308
|
}
|
|
305
309
|
}
|
|
306
|
-
/**
|
|
310
|
+
/** Shrink the OPFS file by removing trailing free blocks from the data region.
|
|
311
|
+
* Scans bitmap from end to find the last used block, then truncates. */
|
|
312
|
+
trimTrailingBlocks() {
|
|
313
|
+
const bitmap = this.bitmap;
|
|
314
|
+
let lastUsed = -1;
|
|
315
|
+
for (let byteIdx = Math.ceil(this.totalBlocks / 8) - 1; byteIdx >= 0; byteIdx--) {
|
|
316
|
+
if (bitmap[byteIdx] !== 0) {
|
|
317
|
+
for (let bit = 7; bit >= 0; bit--) {
|
|
318
|
+
const blockIdx = byteIdx * 8 + bit;
|
|
319
|
+
if (blockIdx < this.totalBlocks && bitmap[byteIdx] & 1 << bit) {
|
|
320
|
+
lastUsed = blockIdx;
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
const newTotal = Math.max(lastUsed + 1, INITIAL_DATA_BLOCKS);
|
|
328
|
+
if (newTotal >= this.totalBlocks) return;
|
|
329
|
+
this.handle.truncate(this.dataOffset + newTotal * this.blockSize);
|
|
330
|
+
const newBitmapSize = Math.ceil(newTotal / 8);
|
|
331
|
+
this.bitmap = bitmap.slice(0, newBitmapSize);
|
|
332
|
+
const trimmed = this.totalBlocks - newTotal;
|
|
333
|
+
this.freeBlocks -= trimmed;
|
|
334
|
+
this.totalBlocks = newTotal;
|
|
335
|
+
this.superblockDirty = true;
|
|
336
|
+
this.bitmapDirtyLo = 0;
|
|
337
|
+
this.bitmapDirtyHi = newBitmapSize - 1;
|
|
338
|
+
}
|
|
339
|
+
/** Rebuild in-memory path→inode index from disk.
|
|
340
|
+
* Bulk-reads the entire inode table + path table in 2 I/O calls,
|
|
341
|
+
* then parses in memory (avoids 10k+ individual reads). */
|
|
307
342
|
rebuildIndex() {
|
|
308
343
|
this.pathIndex.clear();
|
|
344
|
+
this.inodeCache.clear();
|
|
345
|
+
const inodeTableSize = this.inodeCount * INODE_SIZE;
|
|
346
|
+
const inodeBuf = new Uint8Array(inodeTableSize);
|
|
347
|
+
this.handle.read(inodeBuf, { at: this.inodeTableOffset });
|
|
348
|
+
const inodeView = new DataView(inodeBuf.buffer);
|
|
349
|
+
const pathBuf = this.pathTableUsed > 0 ? new Uint8Array(this.pathTableUsed) : null;
|
|
350
|
+
if (pathBuf) {
|
|
351
|
+
this.handle.read(pathBuf, { at: this.pathTableOffset });
|
|
352
|
+
}
|
|
309
353
|
for (let i = 0; i < this.inodeCount; i++) {
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
354
|
+
const off = i * INODE_SIZE;
|
|
355
|
+
const type = inodeView.getUint8(off + INODE.TYPE);
|
|
356
|
+
if (type === INODE_TYPE.FREE) continue;
|
|
357
|
+
const inode = {
|
|
358
|
+
type,
|
|
359
|
+
pathOffset: inodeView.getUint32(off + INODE.PATH_OFFSET, true),
|
|
360
|
+
pathLength: inodeView.getUint16(off + INODE.PATH_LENGTH, true),
|
|
361
|
+
mode: inodeView.getUint32(off + INODE.MODE, true),
|
|
362
|
+
size: inodeView.getFloat64(off + INODE.SIZE, true),
|
|
363
|
+
firstBlock: inodeView.getUint32(off + INODE.FIRST_BLOCK, true),
|
|
364
|
+
blockCount: inodeView.getUint32(off + INODE.BLOCK_COUNT, true),
|
|
365
|
+
mtime: inodeView.getFloat64(off + INODE.MTIME, true),
|
|
366
|
+
ctime: inodeView.getFloat64(off + INODE.CTIME, true),
|
|
367
|
+
atime: inodeView.getFloat64(off + INODE.ATIME, true),
|
|
368
|
+
uid: inodeView.getUint32(off + INODE.UID, true),
|
|
369
|
+
gid: inodeView.getUint32(off + INODE.GID, true)
|
|
370
|
+
};
|
|
371
|
+
this.inodeCache.set(i, inode);
|
|
372
|
+
const path = pathBuf ? decoder.decode(pathBuf.subarray(inode.pathOffset, inode.pathOffset + inode.pathLength)) : this.readPath(inode.pathOffset, inode.pathLength);
|
|
313
373
|
this.pathIndex.set(path, i);
|
|
314
374
|
}
|
|
315
375
|
}
|
|
@@ -450,6 +510,7 @@ var VFSEngine = class {
|
|
|
450
510
|
this.superblockDirty = true;
|
|
451
511
|
return start;
|
|
452
512
|
}
|
|
513
|
+
blocksFreedsinceTrim = false;
|
|
453
514
|
freeBlockRange(start, count) {
|
|
454
515
|
if (count === 0) return;
|
|
455
516
|
const bitmap = this.bitmap;
|
|
@@ -461,6 +522,7 @@ var VFSEngine = class {
|
|
|
461
522
|
this.markBitmapDirty(start >>> 3, start + count - 1 >>> 3);
|
|
462
523
|
this.freeBlocks += count;
|
|
463
524
|
this.superblockDirty = true;
|
|
525
|
+
this.blocksFreedsinceTrim = true;
|
|
464
526
|
}
|
|
465
527
|
// updateSuperblockFreeBlocks is no longer needed — superblock writes are coalesced via commitPending()
|
|
466
528
|
// ========== Inode allocation ==========
|