22
33const {
44 DateNow,
5+ MathMax,
56 MathMin,
67 Symbol,
78} = primordials ;
@@ -253,6 +254,7 @@ class VirtualFileHandle {
253254 */
254255class MemoryFileHandle extends VirtualFileHandle {
255256 #content;
257+ #size;
256258 #entry;
257259 #getStats;
258260
@@ -273,6 +275,7 @@ class MemoryFileHandle extends VirtualFileHandle {
273275 constructor ( path , flags , mode , content , entry , getStats ) {
274276 super ( path , flags , mode ) ;
275277 this . #content = content ;
278+ this . #size = content . length ;
276279 this . #entry = entry ;
277280 this . #getStats = getStats ;
278281
@@ -281,13 +284,14 @@ class MemoryFileHandle extends VirtualFileHandle {
281284 flags === 'wx' || flags === 'wx+' ) {
282285 // Write mode: truncate
283286 this . #content = Buffer . alloc ( 0 ) ;
287+ this . #size = 0 ;
284288 if ( entry ) {
285289 entry . content = this . #content;
286290 }
287291 } else if ( flags === 'a' || flags === 'a+' ||
288292 flags === 'ax' || flags === 'ax+' ) {
289293 // Append mode: position at end
290- this . position = this . #content . length ;
294+ this . position = this . #size ;
291295 }
292296 }
293297
@@ -329,7 +333,7 @@ class MemoryFileHandle extends VirtualFileHandle {
329333 if ( this . #entry?. isDynamic && this . #entry. isDynamic ( ) ) {
330334 return this . #entry. getContentSync ( ) ;
331335 }
332- return this . #content;
336+ return this . #content. subarray ( 0 , this . #size ) ;
333337 }
334338
335339 /**
@@ -404,23 +408,30 @@ class MemoryFileHandle extends VirtualFileHandle {
404408
405409 // In append mode, always write at the end
406410 const writePos = this . #isAppend( ) ?
407- this . #content . length :
411+ this . #size :
408412 ( position !== null && position !== undefined ? position : this . position ) ;
409413 const data = buffer . subarray ( offset , offset + length ) ;
410414
411- // Expand content if needed
412- if ( writePos + length > this . #content. length ) {
413- const newContent = Buffer . alloc ( writePos + length ) ;
414- this . #content. copy ( newContent , 0 , 0 , this . #content. length ) ;
415+ // Expand buffer if needed (geometric doubling for amortized O(1) appends)
416+ const neededSize = writePos + length ;
417+ if ( neededSize > this . #content. length ) {
418+ const newCapacity = MathMax ( neededSize , this . #content. length * 2 ) ;
419+ const newContent = Buffer . alloc ( newCapacity ) ;
420+ this . #content. copy ( newContent , 0 , 0 , this . #size) ;
415421 this . #content = newContent ;
416422 }
417423
418424 // Write the data
419425 data . copy ( this . #content, writePos ) ;
420426
427+ // Update actual content size
428+ if ( neededSize > this . #size) {
429+ this . #size = neededSize ;
430+ }
431+
421432 // Update the entry's content and mtime
422433 if ( this . #entry) {
423- this . #entry. content = this . #content;
434+ this . #entry. content = this . #content. subarray ( 0 , this . #size ) ;
424435 this . #entry. mtime = DateNow ( ) ;
425436 }
426437
@@ -495,21 +506,27 @@ class MemoryFileHandle extends VirtualFileHandle {
495506
496507 // In append mode, append to existing content
497508 if ( this . flags === 'a' || this . flags === 'a+' ) {
498- const newContent = Buffer . alloc ( this . #content. length + buffer . length ) ;
499- this . #content. copy ( newContent , 0 ) ;
500- buffer . copy ( newContent , this . #content. length ) ;
501- this . #content = newContent ;
509+ const neededSize = this . #size + buffer . length ;
510+ if ( neededSize > this . #content. length ) {
511+ const newCapacity = MathMax ( neededSize , this . #content. length * 2 ) ;
512+ const newContent = Buffer . alloc ( newCapacity ) ;
513+ this . #content. copy ( newContent , 0 , 0 , this . #size) ;
514+ this . #content = newContent ;
515+ }
516+ buffer . copy ( this . #content, this . #size) ;
517+ this . #size = neededSize ;
502518 } else {
503519 this . #content = Buffer . from ( buffer ) ;
520+ this . #size = buffer . length ;
504521 }
505522
506523 // Update the entry's content and mtime
507524 if ( this . #entry) {
508- this . #entry. content = this . #content;
525+ this . #entry. content = this . #content. subarray ( 0 , this . #size ) ;
509526 this . #entry. mtime = DateNow ( ) ;
510527 }
511528
512- this . position = this . #content . length ;
529+ this . position = this . #size ;
513530 }
514531
515532 /**
@@ -530,7 +547,7 @@ class MemoryFileHandle extends VirtualFileHandle {
530547 statSync ( options ) {
531548 this . #checkClosed( ) ;
532549 if ( this . #getStats) {
533- return this . #getStats( this . #content . length ) ;
550+ return this . #getStats( this . #size ) ;
534551 }
535552 throw new ERR_INVALID_STATE ( 'stats not available' ) ;
536553 }
@@ -552,17 +569,25 @@ class MemoryFileHandle extends VirtualFileHandle {
552569 this . #checkClosed( ) ;
553570 this . #checkWritable( ) ;
554571
555- if ( len < this . #content. length ) {
556- this . #content = this . #content. subarray ( 0 , len ) ;
557- } else if ( len > this . #content. length ) {
558- const newContent = Buffer . alloc ( len ) ;
559- this . #content. copy ( newContent , 0 , 0 , this . #content. length ) ;
560- this . #content = newContent ;
572+ if ( len < this . #size) {
573+ // Zero out truncated region to avoid stale data
574+ this . #content. fill ( 0 , len , this . #size) ;
575+ this . #size = len ;
576+ } else if ( len > this . #size) {
577+ if ( len > this . #content. length ) {
578+ const newContent = Buffer . alloc ( len ) ;
579+ this . #content. copy ( newContent , 0 , 0 , this . #size) ;
580+ this . #content = newContent ;
581+ } else {
582+ // Buffer has enough capacity, just zero-fill the extension
583+ this . #content. fill ( 0 , this . #size, len ) ;
584+ }
585+ this . #size = len ;
561586 }
562587
563588 // Update the entry's content and mtime
564589 if ( this . #entry) {
565- this . #entry. content = this . #content;
590+ this . #entry. content = this . #content. subarray ( 0 , this . #size ) ;
566591 this . #entry. mtime = DateNow ( ) ;
567592 }
568593 }
0 commit comments