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,71 @@ addToLibrary({
3834 } catch ( e ) {
3935 }
4036 }
41- var response = await fetch ( url ) ;
37+ var chunkSize = __wasmfs_fetch_get_chunk_size ( file ) ;
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-" } } ) ;
44+ if ( fileInfo . ok &&
45+ fileInfo . headers . has ( "Content-Length" ) &&
46+ fileInfo . headers . get ( "Accept-Ranges" ) == "bytes" &&
47+ ( parseInt ( fileInfo . headers . get ( "Content-Length" ) ) > chunkSize * 2 ) ) {
48+ wasmFS$JSMemoryRanges [ file ] = { size :parseInt ( fileInfo . headers . get ( "Content-Length" ) ) , chunks :[ ] , chunkSize :chunkSize } ;
49+ } else {
50+ // may as well/forced to download the whole file
51+ var wholeFileReq = await fetch ( url ) ;
52+ if ( ! wholeFileReq . ok ) {
53+ throw wholeFileReq ;
54+ }
55+ var wholeFileData = new Uint8Array ( await wholeFileReq . arrayBuffer ( ) ) ;
56+ var text = new TextDecoder ( ) . decode ( wholeFileData ) ;
57+ wasmFS$JSMemoryRanges [ file ] = { size :wholeFileData . byteLength , chunks :[ wholeFileData ] , chunkSize :wholeFileData . byteLength } ;
58+ return Promise . resolve ( ) ;
59+ }
60+ }
61+ var allPresent = true ;
62+ var i ;
63+ if ( lastChunk * chunkSize < offset + len ) {
64+ lastChunk += 1 ;
65+ }
66+ for ( i = firstChunk ; i < lastChunk ; i ++ ) {
67+ if ( ! wasmFS$JSMemoryRanges [ file ] . chunks [ i ] ) {
68+ allPresent = false ;
69+ break ;
70+ }
71+ }
72+ if ( allPresent ) {
73+ // The data is already here, so nothing to do before we continue on to
74+ // the actual read.
75+ return Promise . resolve ( ) ;
76+ }
77+ // This is the first time we want the chunk's data.
78+ var start = firstChunk * chunkSize ;
79+ var end = lastChunk * chunkSize ;
80+ var response = await fetch ( url , { headers :{ "Range" : `bytes=${ start } -${ end - 1 } ` } } ) ;
4281 if ( response . ok ) {
43- var buffer = await response [ 'arrayBuffer' ] ( ) ;
44- wasmFS$JSMemoryFiles [ file ] = new Uint8Array ( buffer ) ;
82+ var bytes = new Uint8Array ( await response [ 'arrayBuffer' ] ( ) ) ;
83+ for ( i = firstChunk ; i < lastChunk ; i ++ ) {
84+ wasmFS$JSMemoryRanges [ file ] . chunks [ i ] = bytes . slice ( i * chunkSize - start , ( i + 1 ) * chunkSize - start ) ;
85+ }
4586 } else {
4687 throw response ;
4788 }
89+ return Promise . resolve ( ) ;
4890 }
4991
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 ] ;
5792 wasmFS$backends [ backend ] = {
5893 // alloc/free operations are not actually async. Just forward to the
5994 // parent class, but we must return a Promise as the caller expects.
6095 allocFile : async ( file ) => {
61- jsFileOps . allocFile ( file ) ;
96+ // nop
6297 return Promise . resolve ( ) ;
6398 } ,
6499 freeFile : async ( file ) => {
65- jsFileOps . freeFile ( file ) ;
100+ // free memory
101+ wasmFS$JSMemoryRanges [ file ] = undefined ;
66102 return Promise . resolve ( ) ;
67103 } ,
68104
@@ -73,17 +109,39 @@ addToLibrary({
73109 // read/getSize fetch the data, then forward to the parent class.
74110 read : async ( file , buffer , length , offset ) => {
75111 try {
76- await getFile ( file ) ;
112+ await getFileRange ( file , offset || 0 , length ) ;
77113 } catch ( response ) {
78114 return response . status === 404 ? - { { { cDefs . ENOENT } } } : - { { { cDefs. EBADF } } } ;
79115 }
80- return jsFileOps . read ( file, buffer, length, offset) ;
116+ var fileInfo = wasmFS$JSMemoryRanges [ file ] ;
117+ var fileData = fileInfo . chunks ;
118+ var chunkSize = fileInfo . chunkSize ;
119+ var firstChunk = ( offset / chunkSize ) | 0 ;
120+ var lastChunk = ( ( offset + length ) / chunkSize ) | 0 ;
121+ if ( offset + length > lastChunk * chunkSize ) {
122+ lastChunk += 1 ;
123+ }
124+ var readLength = 0 ;
125+ for ( var i = firstChunk ; i < lastChunk ; i ++ ) {
126+ var chunk = fileData [ i ] ;
127+ var start = Math . max ( i * chunkSize , offset ) ;
128+ if ( ! chunk ) {
129+ throw [ fileData . length , firstChunk , lastChunk , i ] ;
130+ }
131+ var chunkStart = i * chunkSize ;
132+ var end = Math . min ( chunkStart + chunkSize , offset + length ) ;
133+ HEAPU8 . set ( chunk . subarray ( start - chunkStart , end - chunkStart ) , buffer + ( start - offset ) ) ;
134+ readLength = end - offset ;
135+ }
136+ return readLength ;
81137 } ,
82138 getSize : async ( file ) => {
83139 try {
84- await getFile ( file ) ;
85- } catch ( response ) { }
86- return jsFileOps . getSize ( file ) ;
140+ await getFileRange ( file , 0 , 0 ) ;
141+ } catch ( response ) {
142+ return 0 ;
143+ }
144+ return wasmFS$JSMemoryRanges [ file ] . size ;
87145 } ,
88146 } ;
89147 } ,
0 commit comments