@@ -70,11 +70,14 @@ addToLibrary({
7070 } else if ( FS . isFile ( node . mode ) ) {
7171 node . node_ops = MEMFS . ops_table . file . node ;
7272 node . stream_ops = MEMFS . ops_table . file . stream ;
73- node . usedBytes = 0 ; // The actual number of bytes used in the typed array, as opposed to contents.length which gives the whole capacity.
74- // When the byte data of the file is populated, this will point to either a typed array, or a normal JS array. Typed arrays are preferred
75- // for performance, and used by default. However, typed arrays are not resizable like normal JS arrays are, so there is a small disk size
76- // penalty involved for appending file writes that continuously grow a file similar to std::vector capacity vs used -scheme.
77- node . contents = null ;
73+ // The actual number of bytes used in the typed array, as opposed to
74+ // contents.length which gives the whole capacity.
75+ node . usedBytes = 0 ;
76+ // The byte data of the file is stored in a typed array.
77+ // Note: typed arrays are not resizable like normal JS arrays are, so
78+ // there is a small penalty involved for appending file writes that
79+ // continuously grow a file similar to std::vector capacity vs used.
80+ node . contents = MEMFS . emptyFileContents ??= new Uint8Array ( 0 ) ;
7881 } else if ( FS . isLink ( node . mode ) ) {
7982 node . node_ops = MEMFS . ops_table . link . node ;
8083 node . stream_ops = MEMFS . ops_table . link . stream ;
@@ -93,42 +96,41 @@ addToLibrary({
9396
9497 // Given a file node, returns its file data converted to a typed array.
9598 getFileDataAsTypedArray ( node ) {
96- if ( ! node . contents ) return new Uint8Array ( 0 ) ;
97- if ( node . contents . subarray ) return node . contents . subarray ( 0 , node . usedBytes ) ; // Make sure to not return excess unused bytes.
98- return new Uint8Array ( node . contents ) ;
99+ #if ASSERTIONS
100+ assert ( FS . isFile ( node . mode ) , 'getFileDataAsTypedArray called on non-file' ) ;
101+ #endif
102+ return node . contents . subarray ( 0 , node . usedBytes ) ; // Make sure to not return excess unused bytes.
99103 } ,
100104
101- // Allocates a new backing store for the given node so that it can fit at least newSize amount of bytes.
102- // May allocate more, to provide automatic geometric increase and amortized linear performance appending writes.
105+ // Allocates a new backing store for the given node so that it can fit at
106+ // least newSize amount of bytes.
107+ // May allocate more, to provide automatic geometric increase and amortized
108+ // linear performance appending writes.
103109 // Never shrinks the storage.
104110 expandFileStorage ( node , newCapacity ) {
105- var prevCapacity = node . contents ? node . contents . length : 0 ;
111+ var prevCapacity = node . contents . length ;
106112 if ( prevCapacity >= newCapacity ) return ; // No need to expand, the storage was already large enough.
107- // Don't expand strictly to the given requested limit if it's only a very small increase, but instead geometrically grow capacity.
108- // For small filesizes (<1MB), perform size*2 geometric increase, but for large sizes, do a much more conservative size*1.125 increase to
109- // avoid overshooting the allocation cap by a very large margin.
113+ // Don't expand strictly to the given requested limit if it's only a very
114+ // small increase, but instead geometrically grow capacity.
115+ // For small filesizes (<1MB), perform size*2 geometric increase, but for
116+ // large sizes, do a much more conservative size*1.125 increase to avoid
117+ // overshooting the allocation cap by a very large margin.
110118 var CAPACITY_DOUBLING_MAX = 1024 * 1024 ;
111119 newCapacity = Math . max ( newCapacity , ( prevCapacity * ( prevCapacity < CAPACITY_DOUBLING_MAX ? 2.0 : 1.125 ) ) >>> 0 ) ;
112- if ( prevCapacity != 0 ) newCapacity = Math . max ( newCapacity , 256 ) ; // At minimum allocate 256b for each file when expanding.
113- var oldContents = node . contents ;
120+ if ( prevCapacity ) newCapacity = Math . max ( newCapacity , 256 ) ; // At minimum allocate 256b for each file when expanding.
121+ var oldContents = MEMFS . getFileDataAsTypedArray ( node ) ;
114122 node . contents = new Uint8Array ( newCapacity ) ; // Allocate new storage.
115- if ( node . usedBytes > 0 ) node . contents . set ( oldContents . subarray ( 0 , node . usedBytes ) , 0 ) ; // Copy old data over to the new storage.
123+ node . contents . set ( oldContents ) ;
116124 } ,
117125
118- // Performs an exact resize of the backing file storage to the given size, if the size is not exactly this, the storage is fully reallocated.
126+ // Performs an exact resize of the backing file storage to the given size,
127+ // if the size is not exactly this, the storage is fully reallocated.
119128 resizeFileStorage ( node , newSize ) {
120129 if ( node . usedBytes == newSize ) return ;
121- if ( newSize == 0 ) {
122- node . contents = null ; // Fully decommit when requesting a resize to zero.
123- node . usedBytes = 0 ;
124- } else {
125- var oldContents = node . contents ;
126- node . contents = new Uint8Array ( newSize ) ; // Allocate new storage.
127- if ( oldContents ) {
128- node . contents . set ( oldContents . subarray ( 0 , Math . min ( newSize , node . usedBytes ) ) ) ; // Copy old data over to the new storage.
129- }
130- node . usedBytes = newSize ;
131- }
130+ var oldContents = node . contents ;
131+ node . contents = new Uint8Array ( newSize ) ; // Allocate new storage.
132+ node . contents . set ( oldContents . subarray ( 0 , Math . min ( newSize , node . usedBytes ) ) ) ; // Copy old data over to the new storage.
133+ node . usedBytes = newSize ;
132134 } ,
133135
134136 node_ops: {
@@ -242,11 +244,7 @@ addToLibrary({
242244#if ASSERTIONS
243245 assert ( size >= 0 ) ;
244246#endif
245- if ( size > 8 && contents . subarray ) { // non-trivial, and typed array
246- buffer . set ( contents . subarray ( position , position + size ) , offset ) ;
247- } else {
248- for ( var i = 0 ; i < size ; i ++ ) buffer [ offset + i ] = contents [ position + i ] ;
249- }
247+ buffer . set ( contents . subarray ( position , position + size ) , offset ) ;
250248 return size ;
251249 } ,
252250
@@ -275,7 +273,7 @@ addToLibrary({
275273 var node = stream . node ;
276274 node . mtime = node . ctime = Date . now ( ) ;
277275
278- if ( buffer . subarray && ( ! node . contents || node . contents . subarray ) ) { // This write is from a typed array to a typed array?
276+ if ( buffer . subarray ) { // This write is from a typed array to a typed array?
279277 if ( canOwn ) {
280278#if ASSERTIONS
281279 assert ( position === 0 , 'canOwn must imply no weird position inside the file' ) ;
@@ -295,12 +293,12 @@ addToLibrary({
295293
296294 // Appending to an existing file and we need to reallocate, or source data did not come as a typed array.
297295 MEMFS . expandFileStorage ( node , position + length ) ;
298- if ( node . contents . subarray && buffer . subarray ) {
296+ if ( buffer . subarray ) {
299297 // Use typed array write which is available.
300298 node . contents . set ( buffer . subarray ( offset , offset + length ) , position ) ;
301299 } else {
302300 for ( var i = 0 ; i < length ; i ++ ) {
303- node . contents [ position + i ] = buffer [ offset + i ] ; // Or fall back to manual write if not.
301+ node . contents [ position + i ] = buffer [ offset + i ] ; // Or fall back to manual write if not.
304302 }
305303 }
306304 node . usedBytes = Math . max ( node . usedBytes , position + length ) ;
@@ -329,7 +327,7 @@ addToLibrary({
329327 var allocated ;
330328 var contents = stream . node . contents ;
331329 // Only make a new copy when MAP_PRIVATE is specified.
332- if ( ! ( flags & { { { cDefs . MAP_PRIVATE } } } ) && contents && contents . buffer === HEAP8 . buffer ) {
330+ if ( ! ( flags & { { { cDefs . MAP_PRIVATE } } } ) && contents . buffer === HEAP8 . buffer ) {
333331 // We can't emulate MAP_SHARED when the file is not backed by the
334332 // buffer we're mapping to (e.g. the HEAP buffer).
335333 allocated = false ;
0 commit comments