diff options
-rw-r--r-- | src/library.js | 100 | ||||
-rw-r--r-- | src/shell.js | 2 |
2 files changed, 80 insertions, 22 deletions
diff --git a/src/library.js b/src/library.js index 26358bd6..18c2196a 100644 --- a/src/library.js +++ b/src/library.js @@ -289,7 +289,82 @@ LibraryManager.library = { // XHR, which is not possible in browsers except in a web worker! Use preloading, // either --preload-file in emcc or FS.createPreloadedFile createLazyFile: function(parent, name, url, canRead, canWrite) { - var properties = {isDevice: false, url: url}; + + if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc'; + + // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse. + var LazyUint8Array = function(CHUNK_SIZE, length) { + this.length = length; + this.CHUNK_SIZE = CHUNK_SIZE; + this.chunks = []; // Loaded chunks. Index is the chunk number + } + LazyUint8Array.prototype.get = function(idx) { + if (idx > this.length-1 || idx < 0) { + //Module["print"]("outside range!"); + return undefined; + } + var chunkOffset = idx % CHUNK_SIZE; + var chunkNum = Math.floor(idx / CHUNK_SIZE) | 0; + //Module["print"]("idx: " + idx); + //Module["print"]("offset: " + chunkOffset); + //Module["print"]("chunknum: " + chunkNum); + return this.getter(chunkNum).get(chunkOffset); + } + LazyUint8Array.prototype.setDataGetter = function(getter) { + this.getter = getter; + } + + // Find length + var xhr = new XMLHttpRequest(); + xhr.open('HEAD', url, false); + xhr.send(null); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); + var datalength = Number(xhr.getResponseHeader("Content-length")); + + // Function to get a range from the remote URL. Uses "url" and "datalength" from this scope. + var doXHR = (function(from, to) { + if (from >= to) throw new Error("invalid range or no bytes requested!"); + if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!"); + + // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + xhr.setRequestHeader("Range", "bytes=" + from + "-" + to + ""); + + // Some hints to the browser that we want binary data. + if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer'; + if (xhr.overrideMimeType) { + xhr.overrideMimeType('text/plain; charset=x-user-defined'); + } + + xhr.send(null); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); + if (xhr.response !== undefined) { + return new Uint8Array(xhr.response || []); + } else { + return intArrayFromString(xhr.responseText || '', true); + } + }); + + var CHUNK_SIZE = 1024*1024; // Chunk size in bytes + + var lazyArray = new LazyUint8Array(CHUNK_SIZE, datalength); + lazyArray.setDataGetter(function(chunkNum) { + var start = chunkNum * lazyArray.CHUNK_SIZE; + var end = (chunkNum+1) * lazyArray.CHUNK_SIZE - 1; // including this byte + end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block + //Module["print"]("start: " + start); + //Module["print"]("end: " + end); + if (typeof(lazyArray.chunks[chunkNum]) === "undefined") { + lazyArray.chunks[chunkNum] = doXHR(start, end); + //Module["print"]("loaded chunks: " + lazyArray.chunks.length); + } + if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!"); + return lazyArray.chunks[chunkNum]; + }); + + var properties = {isDevice: false, contents: lazyArray}; + return FS.createFile(parent, name, properties, canRead, canWrite); }, // Preloads a file asynchronously. You can call this before run, for example in @@ -358,26 +433,7 @@ LibraryManager.library = { if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; var success = true; if (typeof XMLHttpRequest !== 'undefined') { - // Browser. - if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc'; - - // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. - var xhr = new XMLHttpRequest(); - xhr.open('GET', obj.url, false); - - // Some hints to the browser that we want binary data. - if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer'; - if (xhr.overrideMimeType) { - xhr.overrideMimeType('text/plain; charset=x-user-defined'); - } - - xhr.send(null); - if (xhr.status != 200 && xhr.status != 0) success = false; - if (xhr.response !== undefined) { - obj.contents = new Uint8Array(xhr.response || []); - } else { - obj.contents = intArrayFromString(xhr.responseText || '', true); - } + throw new Error("Lazy loading should have been performed (contents set) in createLazyFile. Programmer error."); } else if (Module['read']) { // Command-line. try { @@ -1648,7 +1704,7 @@ LibraryManager.library = { var contents = stream.object.contents; var size = Math.min(contents.length - offset, nbyte); for (var i = 0; i < size; i++) { - {{{ makeSetValue('buf', 'i', 'contents[offset + i]', 'i8') }}} + {{{ makeSetValue('buf', 'i', 'contents.get(offset + i)', 'i8') }}} } bytesRead += size; return bytesRead; diff --git a/src/shell.js b/src/shell.js index 065462e1..83057f6f 100644 --- a/src/shell.js +++ b/src/shell.js @@ -81,6 +81,8 @@ if (ENVIRONMENT_IS_WEB) { } if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { + Uint8Array.prototype.get = function(idx) { return this[idx]; }; + Module['read'] = function(url) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); |