55 */
66
77addToLibrary ( {
8+ $wasmFS$JSMemoryRanges : { } ,
9+
810 // Fetch backend: On first access of the file (either a read or a getSize), it
911 // will fetch() the data from the network asynchronously. Otherwise, after
1012 // that fetch it behaves just like JSFile (and it reuses the code from there).
1113
1214 _wasmfs_create_fetch_backend_js__deps : [
1315 '$wasmFS$backends' ,
14- '$wasmFS$JSMemoryFiles ' ,
15- '_wasmfs_create_js_file_backend_js ' ,
16- '_wasmfs_fetch_get_file_path ' ,
16+ '$wasmFS$JSMemoryRanges ' ,
17+ '_wasmfs_fetch_get_file_url ' ,
18+ '_wasmfs_fetch_get_chunk_size ' ,
1719 ] ,
1820 _wasmfs_create_fetch_backend_js : async function ( backend ) {
1921 // Get a promise that fetches the data and stores it in JS memory (if it has
2022 // not already been fetched).
21- async function getFile ( file ) {
22- if ( wasmFS$JSMemoryFiles [ file ] ) {
23- // The data is already here, so nothing to do before we continue on to
24- // the actual read below.
25- return Promise . resolve ( ) ;
26- }
27- // This is the first time we want the file's data.
23+ async function getFileRange ( file , offset , len ) {
2824 var url = '' ;
29- var fileUrl_p = __wasmfs_fetch_get_file_path ( file ) ;
25+ var fileUrl_p = __wasmfs_fetch_get_file_url ( file ) ;
3026 var fileUrl = UTF8ToString ( fileUrl_p ) ;
3127 var isAbs = fileUrl . indexOf ( '://' ) !== - 1 ;
3228 if ( isAbs ) {
@@ -38,31 +34,68 @@ addToLibrary({
3834 } catch ( e ) {
3935 }
4036 }
41- var response = await fetch ( url ) ;
37+ var chunkSize = __wasmfs_fetch_get_chunk_size ( ) ;
38+ offset = offset || 0 ;
39+ len = len || chunkSize ;
40+ var firstChunk = ( offset / chunkSize ) | 0 ;
41+ var lastChunk = ( ( offset + len ) / chunkSize ) | 0 ;
42+ if ( ! ( file in wasmFS$JSMemoryRanges ) ) {
43+ var fileInfo = await fetch ( url , { method :"HEAD" , headers :{ "Range" :"bytes=0-1" } } ) ;
44+ if ( fileInfo . ok && fileInfo . headers . has ( "Content-Length" ) && fileInfo . headers . get ( "Accept-Ranges" ) == "bytes" && ( parseInt ( fileInfo . headers . get ( "Content-Length" ) ) > chunkSize * 2 ) ) {
45+ wasmFS$JSMemoryRanges [ file ] = { size :parseInt ( fileInfo . headers . get ( "Content-Length" ) ) , chunks :[ ] , chunkSize :chunkSize } ;
46+ } else {
47+ // may as well/forced to download the whole file
48+ var wholeFileReq = await fetch ( url ) ;
49+ if ( ! wholeFileReq . ok ) {
50+ throw wholeFileReq ;
51+ }
52+ var wholeFileData = new Uint8Array ( await wholeFileReq . arrayBuffer ( ) ) ;
53+ var text = new TextDecoder ( ) . decode ( wholeFileData ) ;
54+ wasmFS$JSMemoryRanges [ file ] = { size :wholeFileData . byteLength , chunks :[ wholeFileData ] , chunkSize :wholeFileData . byteLength } ;
55+ return Promise . resolve ( ) ;
56+ }
57+ }
58+ var allPresent = true ;
59+ var i ;
60+ if ( lastChunk * chunkSize < offset + len ) {
61+ lastChunk += 1 ;
62+ }
63+ for ( i = firstChunk ; i < lastChunk ; i ++ ) {
64+ if ( ! wasmFS$JSMemoryRanges [ file ] . chunks [ i ] ) {
65+ allPresent = false ;
66+ break ;
67+ }
68+ }
69+ if ( allPresent ) {
70+ // The data is already here, so nothing to do before we continue on to
71+ // the actual read.
72+ return Promise . resolve ( ) ;
73+ }
74+ // This is the first time we want the chunk's data.
75+ var start = firstChunk * chunkSize ;
76+ var end = lastChunk * chunkSize ;
77+ var response = await fetch ( url , { headers :{ "Range" : `bytes=${ start } -${ end } ` } } ) ;
4278 if ( response . ok ) {
43- var buffer = await response [ 'arrayBuffer' ] ( ) ;
44- wasmFS$JSMemoryFiles [ file ] = new Uint8Array ( buffer ) ;
79+ var bytes = new Uint8Array ( await response [ 'arrayBuffer' ] ( ) ) ;
80+ for ( i = firstChunk ; i < lastChunk ; i ++ ) {
81+ wasmFS$JSMemoryRanges [ file ] . chunks [ i ] = bytes . slice ( i * chunkSize , ( i + 1 ) * chunkSize ) ;
82+ }
4583 } else {
4684 throw response ;
4785 }
86+ return Promise . resolve ( ) ;
4887 }
4988
50- // Start with the normal JSFile operations. This sets
51- // wasmFS$backends[backend]
52- // which we will then augment.
53- __wasmfs_create_js_file_backend_js ( backend ) ;
54-
55- // Add the async operations on top.
56- var jsFileOps = wasmFS$backends [ backend ] ;
5789 wasmFS$backends [ backend ] = {
5890 // alloc/free operations are not actually async. Just forward to the
5991 // parent class, but we must return a Promise as the caller expects.
6092 allocFile : async ( file ) => {
61- jsFileOps . allocFile ( file ) ;
93+ // nop
6294 return Promise . resolve ( ) ;
6395 } ,
6496 freeFile : async ( file ) => {
65- jsFileOps . freeFile ( file ) ;
97+ // free memory
98+ wasmFS$JSMemoryRanges [ file ] = undefined ;
6699 return Promise . resolve ( ) ;
67100 } ,
68101
@@ -73,17 +106,39 @@ addToLibrary({
73106 // read/getSize fetch the data, then forward to the parent class.
74107 read : async ( file , buffer , length , offset ) => {
75108 try {
76- await getFile ( file ) ;
109+ await getFileRange ( file , offset || 0 , length ) ;
77110 } catch ( response ) {
78111 return response . status === 404 ? - { { { cDefs . ENOENT } } } : - { { { cDefs. EBADF } } } ;
79112 }
80- return jsFileOps . read ( file, buffer, length, offset) ;
113+ var fileInfo = wasmFS$JSMemoryRanges [ file ] ;
114+ var fileData = fileInfo . chunks ;
115+ var chunkSize = fileInfo . chunkSize ;
116+ var firstChunk = ( offset / chunkSize ) | 0 ;
117+ var lastChunk = ( ( offset + length ) / chunkSize ) | 0 ;
118+ if ( offset + length > lastChunk * chunkSize ) {
119+ lastChunk += 1 ;
120+ }
121+ var readLength = 0 ;
122+ for ( var i = firstChunk ; i < lastChunk ; i ++ ) {
123+ var chunk = fileData [ i ] ;
124+ var start = Math . max ( i * chunkSize , offset ) ;
125+ if ( ! chunk ) {
126+ throw [ fileData . length , firstChunk , lastChunk , i ] ;
127+ }
128+ var end = Math . min ( start + chunk . byteLength , offset + length ) ;
129+ var chunkStart = i * chunkSize ;
130+ HEAPU8 . set ( chunk . subarray ( start - chunkStart , end - chunkStart ) , buffer + ( start - offset ) ) ;
131+ readLength = end - offset ;
132+ }
133+ return readLength ;
81134 } ,
82135 getSize : async ( file ) => {
83136 try {
84- await getFile ( file ) ;
85- } catch ( response ) { }
86- return jsFileOps . getSize ( file ) ;
137+ await getFileRange ( file , 0 , 0 ) ;
138+ } catch ( response ) {
139+ return 0 ;
140+ }
141+ return wasmFS$JSMemoryRanges [ file ] . size ;
87142 } ,
88143 } ;
89144 } ,
0 commit comments