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 )  { 
@@ -35,55 +31,127 @@ addToLibrary({
3531        try  { 
3632          var  u  =  new  URL ( fileUrl ,  self . location . origin ) ; 
3733          url  =  u . toString ( ) ; 
38-         }  catch  ( e )  { 
34+         }  catch  ( _e )  { 
35+           throw  { status : 404 } ; 
3936        } 
4037      } 
41-       var  response  =  await  fetch ( url ) ; 
42-       if  ( response . ok )  { 
43-         var  buffer  =  await  response [ 'arrayBuffer' ] ( ) ; 
44-         wasmFS$JSMemoryFiles [ file ]  =  new  Uint8Array ( buffer ) ; 
45-       }  else  { 
38+       var  chunkSize  =  __wasmfs_fetch_get_chunk_size ( file ) ; 
39+       offset  ??=  0 ; 
40+       len  ??=  chunkSize ; 
41+       // In which chunk does the seeked range start?  E.g., 5-14 with chunksize 8 will start in chunk 0. 
42+       var  firstChunk  =  ( offset  /  chunkSize )  |  0 ; 
43+       // In which chunk does the seeked range end?  E.g., 5-14 with chunksize 8 will end in chunk 1, as will 5-16 (since byte 16 isn't requested). 
44+       // This will always give us a chunk >= firstChunk since len > 0. 
45+       var  lastChunk  =  ( ( offset + len - 1 )  /  chunkSize )  |  0 ; 
46+       if  ( ! ( file  in  wasmFS$JSMemoryRanges ) )  { 
47+         var  fileInfo  =  await  fetch ( url ,  { method :'HEAD' ,  headers :{ 'Range' : 'bytes=0-' } } ) ; 
48+         if  ( fileInfo . ok  && 
49+             fileInfo . headers . has ( 'Content-Length' )  && 
50+             fileInfo . headers . get ( 'Accept-Ranges' )  ==  'bytes'  && 
51+             ( parseInt ( fileInfo . headers . get ( 'Content-Length' ) ,  10 )  >  chunkSize * 2 ) )  { 
52+           wasmFS$JSMemoryRanges [ file ]  =  { 
53+             size : parseInt ( fileInfo . headers . get ( 'Content-Length' ) ,  10 ) , 
54+             chunks : [ ] , 
55+             chunkSize : chunkSize 
56+           } ; 
57+         }  else  { 
58+           // may as well/forced to download the whole file 
59+           var  wholeFileReq  =  await  fetch ( url ) ; 
60+           if  ( ! wholeFileReq . ok )  { 
61+             throw  wholeFileReq ; 
62+           } 
63+           var  wholeFileData  =  new  Uint8Array ( await  wholeFileReq . arrayBuffer ( ) ) ; 
64+           var  text  =  new  TextDecoder ( ) . decode ( wholeFileData ) ; 
65+           wasmFS$JSMemoryRanges [ file ]  =  { 
66+             size : wholeFileData . byteLength , 
67+             chunks : [ wholeFileData ] , 
68+             chunkSize : wholeFileData . byteLength 
69+           } ; 
70+           return  Promise . resolve ( ) ; 
71+         } 
72+       } 
73+       var  allPresent  =  true ; 
74+       var  i ; 
75+       // Do we have all the chunks already?  If so, we don't need to do any fetches. 
76+       for  ( i  =  firstChunk ;  i  <=  lastChunk ;  i ++ )  { 
77+         if  ( ! wasmFS$JSMemoryRanges [ file ] . chunks [ i ] )  { 
78+           allPresent  =  false ; 
79+           break ; 
80+         } 
81+       } 
82+       if  ( allPresent )  { 
83+         // The data is already here, so nothing to do before we continue on to 
84+         // the actual read. 
85+         return  Promise . resolve ( ) ; 
86+       } 
87+       // This is the first time we want the chunks' data.  We'll make 
88+       // one request for all the chunks we need, rather than one 
89+       // request per chunk. 
90+       var  start  =  firstChunk  *  chunkSize ; 
91+       // We must fetch *up to* the last byte of the last chunk. 
92+       var  end  =  ( lastChunk + 1 )  *  chunkSize ; 
93+       var  response  =  await  fetch ( url ,  { headers :{ 'Range' : `bytes=${ start }  -${ end - 1 }  ` } } ) ; 
94+       if  ( ! response . ok )  { 
4695        throw  response ; 
4796      } 
97+       var  bytes  =  await  response [ 'bytes' ] ( ) ; 
98+       for  ( i  =  firstChunk ;  i  <=  lastChunk ;  i ++ )  { 
99+         wasmFS$JSMemoryRanges [ file ] . chunks [ i ]  =  bytes . slice ( i * chunkSize - start , ( i + 1 ) * chunkSize - start ) ; 
100+       } 
101+       return  Promise . resolve ( ) ; 
48102    } 
49103
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 ] ; 
57104    wasmFS$backends [ backend ]  =  { 
58105      // alloc/free operations are not actually async. Just forward to the 
59106      // parent class, but we must return a Promise as the caller expects. 
60107      allocFile : async  ( file )  =>  { 
61-         jsFileOps . allocFile ( file ) ; 
108+         // nop 
62109        return  Promise . resolve ( ) ; 
63110      } , 
64111      freeFile : async  ( file )  =>  { 
65-         jsFileOps . freeFile ( file ) ; 
112+         // free memory 
113+         wasmFS$JSMemoryRanges [ file ]  =  undefined ; 
66114        return  Promise . resolve ( ) ; 
67115      } , 
68116
69117      write : async  ( file ,  buffer ,  length ,  offset )  =>  { 
70-         abort ( " TODO: file writing in fetch backend? read-only for now" ) ; 
118+         console . error ( ' TODO: file writing in fetch backend? read-only for now' ) ; 
71119      } , 
72120
73121      // read/getSize fetch the data, then forward to the parent class. 
74122      read : async  ( file ,  buffer ,  length ,  offset )  =>  { 
123+         if  ( length  ==  0 )  { 
124+           return  0 ; 
125+         } 
75126        try  { 
76-           await  getFile ( file ) ; 
77-         }  catch  ( response )  { 
78-           return  response . status  ===  404  ? - { { {  cDefs . ENOENT  } } }  : - { { {  cDefs. EBADF  } } } ; 
127+           await  getFileRange ( file ,   offset   ||   0 ,   length ) ; 
128+         }  catch  ( failedResponse )  { 
129+           return  failedResponse . status  ===  404  ? - { { {  cDefs . ENOENT  } } }  : - { { {  cDefs. EBADF  } } } ; 
79130        } 
80-         return  jsFileOps . read ( file,  buffer,  length,  offset) ; 
131+         var  fileInfo  =  wasmFS$JSMemoryRanges [ file ] ; 
132+         var  chunks  =  fileInfo . chunks ; 
133+         var  chunkSize  =  fileInfo . chunkSize ; 
134+         var  firstChunk  =  ( offset  /  chunkSize )  |  0 ; 
135+         // See comments in getFileRange. 
136+         var  lastChunk  =  ( ( offset + length - 1 )  /  chunkSize )  |  0 ; 
137+         var  readLength  =  0 ; 
138+         for  ( var  i  =  firstChunk ;  i  <=  lastChunk ;  i ++ )  { 
139+           var  chunk =  chunks [ i ] ; 
140+           var  start  =  Math . max ( i * chunkSize ,  offset ) ; 
141+           var  chunkStart  =  i * chunkSize ; 
142+           var  end  =  Math . min ( chunkStart + chunkSize ,  offset + length ) ; 
143+           HEAPU8 . set ( chunk . subarray ( start - chunkStart ,  end - chunkStart ) ,  buffer + ( start - offset ) ) ; 
144+           readLength  =  end  -  offset ; 
145+         } 
146+         return  readLength ; 
81147      } , 
82148      getSize : async  ( file )  =>  { 
83149        try  { 
84-           await  getFile ( file ) ; 
85-         }  catch  ( response )  { } 
86-         return  jsFileOps . getSize ( file ) ; 
150+           await  getFileRange ( file ,  0 ,  0 ) ; 
151+         }  catch  ( failedResponse )  { 
152+           return  0 ; 
153+         } 
154+         return  wasmFS$JSMemoryRanges [ file ] . size ; 
87155      } , 
88156    } ; 
89157  } , 
0 commit comments