@@ -2,6 +2,46 @@ import { debug } from "./debug.js";
22import * as wasi from "./wasi_defs.js" ;
33import { Fd , Inode } from "./fd.js" ;
44
5+ function dataResize ( data : Uint8Array , newDataSize : number ) : Uint8Array {
6+ // reuse same data if not actually resizing
7+ if ( data . byteLength === newDataSize ) {
8+ return data ;
9+ }
10+
11+ // prefer using
12+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/resize
13+ // when applicable; can be used to shrink/grow
14+ if (
15+ data . buffer instanceof ArrayBuffer &&
16+ data . buffer . resizable &&
17+ newDataSize <= data . buffer . maxByteLength
18+ ) {
19+ data . buffer . resize ( newDataSize ) ;
20+ return data ;
21+ }
22+
23+ // shrinking: create a new resizable ArrayBuffer and copy a subset
24+ // of old data onto it
25+ if ( data . byteLength > newDataSize ) {
26+ const newBuffer = new ArrayBuffer ( newDataSize , {
27+ maxByteLength : newDataSize ,
28+ } ) ,
29+ newData = new Uint8Array ( newBuffer ) ;
30+ newData . set ( new Uint8Array ( data . buffer , 0 , newDataSize ) ) ;
31+ return newData ;
32+ }
33+
34+ // growing: create a new resizable ArrayBuffer with exponential
35+ // growth of maxByteLength, to avoid O(n^2) overhead of repeatedly
36+ // concatenating buffers when doing a lot of small writes at the end
37+ const newBuffer = new ArrayBuffer ( newDataSize , {
38+ maxByteLength : Math . max ( newDataSize , data . buffer . maxByteLength * 2 ) ,
39+ } ) ,
40+ newData = new Uint8Array ( newBuffer ) ;
41+ newData . set ( data ) ;
42+ return newData ;
43+ }
44+
545export class OpenFile extends Fd {
646 file : File ;
747 file_pos : bigint = 0n ;
@@ -12,13 +52,11 @@ export class OpenFile extends Fd {
1252 }
1353
1454 fd_allocate ( offset : bigint , len : bigint ) : number {
15- if ( this . file . size > offset + len ) {
55+ if ( this . file . size >= offset + len ) {
1656 // already big enough
1757 } else {
1858 // extend
19- const new_data = new Uint8Array ( Number ( offset + len ) ) ;
20- new_data . set ( this . file . data , 0 ) ;
21- this . file . data = new_data ;
59+ this . file . data = dataResize ( this . file . data , Number ( offset + len ) ) ;
2260 }
2361 return wasi . ERRNO_SUCCESS ;
2462 }
@@ -28,17 +66,7 @@ export class OpenFile extends Fd {
2866 }
2967
3068 fd_filestat_set_size ( size : bigint ) : number {
31- if ( this . file . size > size ) {
32- // truncate
33- this . file . data = new Uint8Array (
34- this . file . data . buffer . slice ( 0 , Number ( size ) ) ,
35- ) ;
36- } else {
37- // extend
38- const new_data = new Uint8Array ( Number ( size ) ) ;
39- new_data . set ( this . file . data , 0 ) ;
40- this . file . data = new_data ;
41- }
69+ this . file . data = dataResize ( this . file . data , Number ( size ) ) ;
4270 return wasi . ERRNO_SUCCESS ;
4371 }
4472
@@ -91,11 +119,10 @@ export class OpenFile extends Fd {
91119 if ( this . file . readonly ) return { ret : wasi . ERRNO_BADF , nwritten : 0 } ;
92120
93121 if ( this . file_pos + BigInt ( data . byteLength ) > this . file . size ) {
94- const old = this . file . data ;
95- this . file . data = new Uint8Array (
122+ this . file . data = dataResize (
123+ this . file . data ,
96124 Number ( this . file_pos + BigInt ( data . byteLength ) ) ,
97125 ) ;
98- this . file . data . set ( old ) ;
99126 }
100127
101128 this . file . data . set ( data , Number ( this . file_pos ) ) ;
@@ -107,9 +134,10 @@ export class OpenFile extends Fd {
107134 if ( this . file . readonly ) return { ret : wasi . ERRNO_BADF , nwritten : 0 } ;
108135
109136 if ( offset + BigInt ( data . byteLength ) > this . file . size ) {
110- const old = this . file . data ;
111- this . file . data = new Uint8Array ( Number ( offset + BigInt ( data . byteLength ) ) ) ;
112- this . file . data . set ( old ) ;
137+ this . file . data = dataResize (
138+ this . file . data ,
139+ Number ( offset + BigInt ( data . byteLength ) ) ,
140+ ) ;
113141 }
114142
115143 this . file . data . set ( data , Number ( offset ) ) ;
0 commit comments