aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2014-06-16 14:40:45 -0700
committerAlon Zakai <alonzakai@gmail.com>2014-06-16 14:40:45 -0700
commitba387574f23867d889a04f1586e6dd9597e8bad8 (patch)
tree6934000fdd9c173c552a382676b5046f58877b26
parent7acb48826165890ebc9d3794d8d7473aa62b762e (diff)
parent7c26bbdb7d3c40d68777cc93f54ebfa5355a48bc (diff)
Merge branch 'incoming' into proxyGL
-rw-r--r--AUTHORS2
-rw-r--r--ChangeLog141
-rwxr-xr-xemcc4
-rw-r--r--emscripten-version.txt2
-rw-r--r--src/deps_info.json1
-rw-r--r--src/deterministic.js (renamed from src/determinstic.js)7
-rw-r--r--src/jsifier.js5
-rw-r--r--src/library.js13
-rw-r--r--src/library_browser.js33
-rw-r--r--src/library_fs.js13
-rw-r--r--src/library_glfw.js7
-rw-r--r--src/library_idbfs.js3
-rw-r--r--src/library_memfs.js168
-rw-r--r--src/library_sdl.js57
-rw-r--r--src/modules.js25
-rw-r--r--src/settings.js7
-rw-r--r--system/include/SDL/SDL_events.h8
-rw-r--r--system/include/emscripten/emscripten.h20
-rw-r--r--system/local/include/README.txt2
-rw-r--r--tests/fs/test_append.c24
-rw-r--r--tests/glfw.c4
-rw-r--r--tests/sdl_image.c10
-rw-r--r--tests/sdl_key.c93
-rw-r--r--tests/sqlite/speedtest1.c1393
-rw-r--r--tests/test_browser.py56
-rw-r--r--tests/test_core.py70
-rw-r--r--tests/test_other.py54
-rwxr-xr-xtools/ffdb.py602
-rw-r--r--tools/js-optimizer.js5
-rw-r--r--tools/shared.py9
-rw-r--r--tools/system_libs.py8
31 files changed, 2538 insertions, 308 deletions
diff --git a/AUTHORS b/AUTHORS
index d356a05b..5930c012 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -144,4 +144,4 @@ a license to everyone to use it as detailed in LICENSE.)
* Jason Green <jason@transgaming.com> (copyright owned by TransGaming, Inc.)
* Ningxin Hu <ningxin.hu@intel.com> (copyright owned by Intel)
* Nicolas Guillemot <nlguillemot@gmail.com>
-
+* Sathyanarayanan Gunasekaran <gsathya.ceg@gmail.com> (copyright owned by Mozilla Foundation)
diff --git a/ChangeLog b/ChangeLog
index 18640add..cb9f57df 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -10,9 +10,144 @@ Not all changes are documented here. In particular, new features, user-oriented
Current trunk code
------------------
- To see a list of commits in the active development branch 'incoming', which have not yet been packaged in a release, see
- - Emscripten: https://github.com/kripken/emscripten/compare/1.16.0...incoming
- - Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.16.0...incoming
- - Emscripten-Clang: https://github.com/kripken/emscripten-fastcomp-clang/compare/1.16.0...incoming
+ - Emscripten: https://github.com/kripken/emscripten/compare/1.20.0...incoming
+ - Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.20.0...incoming
+ - Emscripten-Clang: https://github.com/kripken/emscripten-fastcomp-clang/compare/1.20.0...incoming
+
+v1.20.0: 6/13/2014
+------------------
+ - Optimize in-memory virtual filesystem performance when serialized to an IndexedDB.
+ - Fixed memcpy regression with ta0 and ta1 modes.
+ - Fixed an issue with line numbers being messed up when generating source maps (#2410)
+ - Fixed an ffdb logging bug that could cause it to drop messages if they were being received too fast. Added support getting memory and system descriptions with ffdb.
+ - Added a new extension to SDL "emscripten_SDL_SetEventHandler()" which enabled application to perform SDL event handling inside a JS event handler to overcome browser security restrictions. (#2417)
+ - Full list of changes:
+ - Emscripten: https://github.com/kripken/emscripten/compare/1.19.2...1.20.0
+ - Emscripten-LLVM: no changes.
+ - Emscripten-Clang: no changes.
+
+v1.19.2: 6/9/2014
+------------------
+ - Updated CMake support for response file handling.
+ - Fixed issues with glfwGetProcAddress and glfwSetWindowSizeCallback.
+ - Fixed an issue with regexes that caused issues on IE11 runtime (#2400)
+ - Added a new functions emscripten_get_preloaded_image_data() and emscripten_get_preloaded_image_data_from_FILE() to obtain pixel data of preloaded images.
+ - Greatly improved ffdb capabilities to operate a FFOS device.
+ - Fixed a Windows-specific bug where the user temp directory was littered with temporary .rsp files that did not get cleaned up.
+ - Improved SIMD support.
+ - Full list of changes:
+ - Emscripten: https://github.com/kripken/emscripten/compare/1.19.1...1.19.2
+ - Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.19.1...1.19.2
+ - Emscripten-Clang: no changes.
+
+v1.19.1: 6/3/2014
+------------------
+ - Migrate to using musl sscanf and sprintf and the family that writes to memory, and not directly to the filesystem.
+ - Improve the error messages from -s SAFE_HEAP_ACCESS=1 runtime checks.
+ - Added new linker flag -s NO_DYNAMIC_EXECUTION=1 which removes the use of eval() and new Function() in the generated output. For more information, see "Eval and related functions are disabled" in https://developer.chrome.com/extensions/contentSecurityPolicy .
+ - Fixed a compiler issue when very large double constants are present. (#2392)
+ - Full list of changes:
+ - Emscripten: https://github.com/kripken/emscripten/compare/1.19.0...1.19.1
+ - Emscripten-LLVM: no changes.
+ - Emscripten-Clang: no changes.
+
+v1.19.0: 5/29/2014
+------------------
+ - Added an error message to signal that linkable modules are not supported in fastcomp.
+ - Fixed a miscompilation issue that resulted in an error "SyntaxError: invalid increment operand" and a statement +(+0) being generated (#2314)
+ - Make optimized compiler output smaller by running the shell code through uglify when not using closure.
+ - Fixed a crash in SDL audio loading code introduced in v1.18.3
+ - Fixed an issue where glTex(Sub)Image2D might throw an exception on error, instead of setting glGetError().
+ - Added new typedefs emscripten_align1_short, emscripten_align{1/2}_int, emscripten_align{1/2}_float and emscripten_align{1/2/4}_double to ease signaling the compiler that unaligned data is present. (#2378)
+ - Fixed an embind issue with refcount tracking on smart pointers.
+ - Full list of changes:
+ - Emscripten: https://github.com/kripken/emscripten/compare/1.18.4...1.19.0
+ - Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.18.4...1.19.0
+ - Emscripten-Clang: no changes.
+
+v1.18.4: 5/27/2014
+------------------
+ - Fixed error message on unsupported linking options (#2365)
+ - Updated embind to latest version from IMVU upstream.
+ - Fixed an issue where source maps did not load properly in Firefox.
+ - Added a more descriptive error message to fastcomp when MAX_SETJMPS limit is violated. (#2379)
+ - Full list of changes:
+ - Emscripten: https://github.com/kripken/emscripten/compare/1.18.3...1.18.4
+ - Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.18.3...1.18.4
+ - Emscripten-Clang: no changes.
+
+v1.18.3: 5/21/2014
+------------------
+ - Added support to emcc command line for "archive groups": -Wl,--start-group and -Wl,--end-group
+ - Greatly optimized ccall and cwrap implementations.
+ - Added new support for SDL_Mix backend to use WebAudio to play back audio clips.
+ - Fixed a registerizeHarder issue with elimination of conditional expressions.
+ - Migrated single-character standard C functions (islower, tolower, and the family) to use musl implementations.
+ - Updated relooper to not optimize out breaks if it causes excessive nesting.
+ - Full list of changes:
+ - Emscripten: https://github.com/kripken/emscripten/compare/1.18.2...1.18.3
+ - Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.18.2...1.18.3
+ - Emscripten-Clang: no changes.
+
+v1.18.2: 5/19/2014
+------------------
+ - Fixed a problem which blocked user applications from handling WebGL context loss events themselves.
+ - Added a new HTML5 api function emscripten_is_webgl_context_lost() which allows polling for context loss in addition to receiving events.
+ - Improved async wget progress events to work better across browsers.
+ - Improved WebIDL binder support.
+ - Added new typeof() function to emscripten::val.
+ - Added support for SDL window events SDL_WINDOWEVENT_FOCUS_GAINED, SDL_WINDOWEVENT_FOCUS_LOST, SDL_WINDOWEVENT_SHOWN, SDL_WINDOWEVENT_HIDDEN.
+ - Fixed a compiler miscompilation on unsigned i1 bitcasts (#2350)
+ - Fixed a compiler bug where doubles in varargs might not get 8-byte aligned (#2358)
+ - Full list of changes:
+ - Emscripten: https://github.com/kripken/emscripten/compare/1.18.1...1.18.2
+ - Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.18.1...1.18.2
+ - Emscripten-Clang: no changes.
+
+v1.18.1: 5/12/2014
+------------------
+ - Fixed an issue where the mouse wheel scroll did not work with SDL.
+ - Fixed an issue with emscripten_async_wget, which undesirably expected that the string pointer passed to it stayed alive for the duration of the operation (#2349)
+ - Emscripten now issues a warning message when the EXPORTED_FUNCTIONS list contains invalid symbol names (#2338)
+ - Full list of changes:
+ - Emscripten: https://github.com/kripken/emscripten/compare/1.18.0...1.18.1
+ - Emscripten-LLVM: no changes.
+ - Emscripten-Clang: no changes.
+
+v1.18.0: 5/10/2014
+------------------
+ - Enable support for low-level C<->JS interop to marshall 64 bit integers from C to JS.
+ - Fixed an issue that caused some programs to immediately run out of memory "(cannot enlarge memory arrays)" at startup. (#2334)
+ - Fixed a crash issue with generated touch events that didn't correspond to a real touch.
+ - Full list of changes:
+ - Emscripten: https://github.com/kripken/emscripten/compare/1.17.0...1.18.0
+ - Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.17.0...1.18.0
+ - Emscripten-Clang: no changes.
+
+v1.17.0: 5/6/2014
+------------------
+ - Enabled asm.js compilation and -s PRECISE_F32 support when using embind.
+ - Improved relooper to emit switches in many-entried blocks.
+ - Fixed a GLFW bug where mouse wheel direction was reversed.
+ - Fixed glfwGetKey to work even when no callback is registered with glfwGetKeyCallback (#1320)
+ - Added a new tool 'webidl_binder' that generates C <-> JS interop code from WebIDL descriptions.
+ - Fix emscripten compilation to work on pages that don't contain a HTML canvas.
+ - Added a new error message to default shell when an uncaught exception is thrown.
+ - Improved error diagnostics reported by -s SAFE_HEAP=1.
+ - Added support for registering callbacks hook to VFS file open, write, move, close and delete.
+ - Added embind support to std::basic_string<unsigned char>
+ - By default, the C runtime will no longer exit after returning from main() when safeSetTimeout() or safeSetInterval() is used.
+ - Fixed an issue with sscanf formatting (#2322)
+ - Fixed an issue where precompiled headers were given a wrong output filename (#2320)
+ - Enabled registerizeHarder optimization pass to work when outlining is enabled.
+ - Fixed an issue with strptime month handling (#2324)
+ - Added an initial implementation of a new tool 'ffdb' which can be used to operate a Firefox OS phone from the command line.
+ - Fixed a compiler crash on assertion failure '!contains(BranchesOut, Target)' (emscripten-fastcomp #32)
+ - Added a new ABI to Clang that targets Emscripten specifically. Stop aligning member functions to save some space in the function table array.
+ - Full list of changes:
+ - Emscripten: https://github.com/kripken/emscripten/compare/1.16.0...1.17.0
+ - Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.16.0...1.17.0
+ - Emscripten-Clang: https://github.com/kripken/emscripten-fastcomp-clang/compare/1.16.0...1.17.0
v1.16.0: 4/16/2014
------------------
diff --git a/emcc b/emcc
index 9f3eb154..0f453f4b 100755
--- a/emcc
+++ b/emcc
@@ -1376,6 +1376,8 @@ try:
if shared.Settings.ASM_JS and shared.Settings.DLOPEN_SUPPORT:
assert shared.Settings.DISABLE_EXCEPTION_CATCHING, 'no exceptions support with dlopen in asm yet'
+ assert not (bind and shared.Settings.NO_DYNAMIC_EXECUTION), 'NO_DYNAMIC_EXECUTION disallows embind'
+
if proxy_to_worker:
shared.Settings.PROXY_TO_WORKER = 1
@@ -1692,7 +1694,7 @@ try:
logging.debug('wrote memory initialization to %s', memfile)
else:
logging.debug('did not see memory initialization')
- elif shared.Settings.USE_TYPED_ARRAYS == 2 and not shared.Settings.MAIN_MODULE and not shared.Settings.SIDE_MODULE:
+ elif shared.Settings.USE_TYPED_ARRAYS == 2 and not shared.Settings.MAIN_MODULE and not shared.Settings.SIDE_MODULE and debug_level < 4:
# not writing a binary init, but we can at least optimize them by splitting them up
src = open(final).read()
src = shared.JS.optimize_initializer(src)
diff --git a/emscripten-version.txt b/emscripten-version.txt
index de59d0c6..59751e94 100644
--- a/emscripten-version.txt
+++ b/emscripten-version.txt
@@ -1,2 +1,2 @@
-1.19.1
+1.20.0
diff --git a/src/deps_info.json b/src/deps_info.json
index 029a20e1..e0983064 100644
--- a/src/deps_info.json
+++ b/src/deps_info.json
@@ -3,6 +3,7 @@
"SDL_Init": ["malloc", "free"],
"SDL_GL_GetProcAddress": ["emscripten_GetProcAddress"],
"eglGetProcAddress": ["emscripten_GetProcAddress"],
+ "glfwGetProcAddress": ["emscripten_GetProcAddress"],
"emscripten_GetProcAddress": ["strstr"]
}
diff --git a/src/determinstic.js b/src/deterministic.js
index 1ec0bbfe..4e9508f3 100644
--- a/src/determinstic.js
+++ b/src/deterministic.js
@@ -8,13 +8,14 @@ var TIME = 10000;
Date.now = function() {
return TIME++;
};
-performance.now = Date.now;
+if (typeof performance === 'object') performance.now = Date.now;
function hashMemory(id) {
var ret = 0;
- for (var i = 0; i < HEAPU8.length; i++) {
+ var len = Math.max(DYNAMICTOP, STATICTOP);
+ for (var i = 0; i < len; i++) {
ret = (ret*17 + HEAPU8[i])|0;
}
- print(id + ':' + ret);
+ printErr(id + ':' + ret);
}
diff --git a/src/jsifier.js b/src/jsifier.js
index e35fef1a..1f6440dd 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -370,7 +370,7 @@ function JSify(data, functionsOnly) {
// name the function; overwrite if it's already named
snippet = snippet.replace(/function(?:\s+([^(]+))?\s*\(/, 'function _' + ident + '(');
- if (LIBRARY_DEBUG) {
+ if (LIBRARY_DEBUG && !LibraryManager.library[ident + '__asm']) {
snippet = snippet.replace('{', '{ var ret = (function() { if (Runtime.debug) Module.printErr("[library call:' + ident + ': " + Array.prototype.slice.call(arguments).map(Runtime.prettyPrint) + "]"); ');
snippet = snippet.substr(0, snippet.length-1) + '}).apply(this, arguments); if (Runtime.debug && typeof ret !== "undefined") Module.printErr(" [ return:" + Runtime.prettyPrint(ret)); return ret; \n}';
}
@@ -1902,6 +1902,9 @@ function JSify(data, functionsOnly) {
print(read('webGLWorker.js'));
print(read('proxyWorker.js'));
}
+ if (DETERMINISTIC) {
+ print(read('deterministic.js'));
+ }
if (RUNTIME_TYPE_INFO) {
Types.cleanForRuntime();
print('Runtime.typeInfo = ' + JSON.stringify(Types.types));
diff --git a/src/library.js b/src/library.js
index c17952b3..1f7fd37d 100644
--- a/src/library.js
+++ b/src/library.js
@@ -3166,7 +3166,7 @@ LibraryManager.library = {
#endif
environ: 'allocate(1, "i32*", ALLOC_STATIC)',
__environ__deps: ['environ'],
- __environ: '_environ',
+ __environ: 'environ',
__buildEnvironment__deps: ['__environ'],
__buildEnvironment: function(env) {
// WARNING: Arbitrary limit!
@@ -3388,9 +3388,20 @@ LibraryManager.library = {
memcpy__sig: 'iiii',
memcpy__deps: ['emscripten_memcpy_big'],
memcpy: function(dest, src, num) {
+#if USE_TYPED_ARRAYS == 0
+ {{{ makeCopyValues('dest', 'src', 'num', 'null') }}};
+ return num;
+#endif
+#if USE_TYPED_ARRAYS == 1
+ {{{ makeCopyValues('dest', 'src', 'num', 'null') }}};
+ return num;
+#endif
+
dest = dest|0; src = src|0; num = num|0;
var ret = 0;
+#if USE_TYPED_ARRAYS
if ((num|0) >= 4096) return _emscripten_memcpy_big(dest|0, src|0, num|0)|0;
+#endif
ret = dest|0;
if ((dest&3) == (src&3)) {
while (dest & 3) {
diff --git a/src/library_browser.js b/src/library_browser.js
index 8509c9d7..8e86371e 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -1175,6 +1175,39 @@ mergeInto(LibraryManager.library, {
var info = Browser.workers[id];
if (!info) return -1;
return info.awaited;
+ },
+
+ emscripten_get_preloaded_image_data: function(path, w, h) {
+ if (typeof path === "number") {
+ path = Pointer_stringify(path);
+ }
+
+ path = PATH.resolve(path);
+
+ var canvas = Module["preloadedImages"][path];
+ if (canvas) {
+ var ctx = canvas.getContext("2d");
+ var image = ctx.getImageData(0, 0, canvas.width, canvas.height);
+ var buf = _malloc(canvas.width * canvas.height * 4);
+
+ HEAPU8.set(image.data, buf);
+
+ {{{ makeSetValue('w', '0', 'canvas.width', 'i32') }}};
+ {{{ makeSetValue('h', '0', 'canvas.height', 'i32') }}};
+ return buf;
+ }
+
+ return 0;
+ },
+
+ emscripten_get_preloaded_image_data_from_FILE__deps: ['emscripten_get_preloaded_image_data'],
+ emscripten_get_preloaded_image_data_from_FILE: function(file, w, h) {
+ var stream = FS.getStreamFromPtr(file);
+ if (stream) {
+ return _emscripten_get_preloaded_image_data(stream.path, w, h);
+ }
+
+ return 0;
}
});
diff --git a/src/library_fs.js b/src/library_fs.js
index 5f7f1dea..d825892c 100644
--- a/src/library_fs.js
+++ b/src/library_fs.js
@@ -1077,6 +1077,10 @@ mergeInto(LibraryManager.library, {
if (!stream.stream_ops.write) {
throw new FS.ErrnoError(ERRNO_CODES.EINVAL);
}
+ if (stream.flags & {{{ cDefine('O_APPEND') }}}) {
+ // seek to the end before writing in append mode
+ FS.llseek(stream, 0, {{{ cDefine('SEEK_END') }}});
+ }
var seeking = true;
if (typeof position === 'undefined') {
position = stream.position;
@@ -1084,10 +1088,6 @@ mergeInto(LibraryManager.library, {
} else if (!stream.seekable) {
throw new FS.ErrnoError(ERRNO_CODES.ESPIPE);
}
- if (stream.flags & {{{ cDefine('O_APPEND') }}}) {
- // seek to the end before writing in append mode
- FS.llseek(stream, 0, {{{ cDefine('SEEK_END') }}});
- }
var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn);
if (!seeking) stream.position += bytesWritten;
try {
@@ -1480,6 +1480,7 @@ mergeInto(LibraryManager.library, {
// WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as
// read() will try to parse UTF8.
obj.contents = intArrayFromString(Module['read'](obj.url), true);
+ obj.usedBytes = obj.contents.length;
} catch (e) {
success = false;
}
@@ -1601,6 +1602,10 @@ mergeInto(LibraryManager.library, {
node.contents = null;
node.url = properties.url;
}
+ // Add a function that defers querying the file size until it is asked the first time.
+ Object.defineProperty(node, "usedBytes", {
+ get: function() { return this.contents.length; }
+ });
// override each stream op with one that tries to force load the lazy file first
var stream_ops = {};
var keys = Object.keys(node.stream_ops);
diff --git a/src/library_glfw.js b/src/library_glfw.js
index 6d539326..6dfea101 100644
--- a/src/library_glfw.js
+++ b/src/library_glfw.js
@@ -417,6 +417,9 @@ var LibraryGLFW = {
glfwSetWindowSizeCallback: function(cbfun) {
GLFW.resizeFunc = cbfun;
+ if (GLFW.resizeFunc) {
+ Runtime.dynCall('vii', GLFW.resizeFunc, [Module['canvas'].width, Module['canvas'].height]);
+ }
},
glfwSetWindowCloseCallback: function(cbfun) {
@@ -507,9 +510,9 @@ var LibraryGLFW = {
return Module.ctx.getSupportedExtensions().indexOf(Pointer_stringify(extension)) > -1;
},
- glfwGetProcAddress__deps: ['glfwGetProcAddress'],
+ glfwGetProcAddress__deps: ['emscripten_GetProcAddress'],
glfwGetProcAddress: function(procname) {
- return _getProcAddress(procname);
+ return _emscripten_GetProcAddress(procname);
},
glfwGetGLVersion: function(major, minor, rev) {
diff --git a/src/library_idbfs.js b/src/library_idbfs.js
index 91015e77..8082c196 100644
--- a/src/library_idbfs.js
+++ b/src/library_idbfs.js
@@ -135,6 +135,9 @@ mergeInto(LibraryManager.library, {
if (FS.isDir(stat.mode)) {
return callback(null, { timestamp: stat.mtime, mode: stat.mode });
} else if (FS.isFile(stat.mode)) {
+ // Performance consideration: storing a normal JavaScript array to a IndexedDB is much slower than storing a typed array.
+ // Therefore always convert the file contents to a typed array first before writing the data to IndexedDB.
+ node.contents = MEMFS.getFileDataAsTypedArray(node);
return callback(null, { timestamp: stat.mtime, mode: stat.mode, contents: node.contents });
} else {
return callback(new Error('node type not supported'));
diff --git a/src/library_memfs.js b/src/library_memfs.js
index 95c3ae65..72ecdcfe 100644
--- a/src/library_memfs.js
+++ b/src/library_memfs.js
@@ -2,11 +2,6 @@ mergeInto(LibraryManager.library, {
$MEMFS__deps: ['$FS'],
$MEMFS: {
ops_table: null,
-
- // content modes
- CONTENT_OWNING: 1, // contains a subarray into the heap, and we own it, without copying (note: someone else needs to free() it, if that is necessary)
- CONTENT_FLEXIBLE: 2, // has been modified or never set to anything, and is a flexible js array that can grow/shrink
- CONTENT_FIXED: 3, // contains some fixed-size content written into it, in a typed array
mount: function(mount) {
return MEMFS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 511 /* 0777 */, 0);
},
@@ -71,8 +66,11 @@ mergeInto(LibraryManager.library, {
} else if (FS.isFile(node.mode)) {
node.node_ops = MEMFS.ops_table.file.node;
node.stream_ops = MEMFS.ops_table.file.stream;
- node.contents = [];
- node.contentMode = MEMFS.CONTENT_FLEXIBLE;
+ node.usedBytes = 0; // The actual number of bytes used in the typed array, as opposed to contents.buffer.byteLength which gives the whole capacity.
+ // When the byte data of the file is populated, this will point to either a typed array, or a normal JS array. Typed arrays are preferred
+ // for performance, and used by default. However, typed arrays are not resizable like normal JS arrays are, so there is a small disk size
+ // penalty involved for appending file writes that continuously grow a file similar to std::vector capacity vs used -scheme.
+ node.contents = null;
} else if (FS.isLink(node.mode)) {
node.node_ops = MEMFS.ops_table.link.node;
node.stream_ops = MEMFS.ops_table.link.stream;
@@ -87,13 +85,88 @@ mergeInto(LibraryManager.library, {
}
return node;
},
- ensureFlexible: function(node) {
- if (node.contentMode !== MEMFS.CONTENT_FLEXIBLE) {
- var contents = node.contents;
- node.contents = Array.prototype.slice.call(contents);
- node.contentMode = MEMFS.CONTENT_FLEXIBLE;
+
+ // Given a file node, returns its file data converted to a regular JS array. You should treat this as read-only.
+ getFileDataAsRegularArray: function(node) {
+#if USE_TYPED_ARRAYS == 2
+ if (node.contents && node.contents.subarray) {
+ var arr = [];
+ for (var i = 0; i < node.usedBytes; ++i) arr.push(node.contents[i]);
+ return arr; // Returns a copy of the original data.
+ }
+#endif
+ return node.contents; // No-op, the file contents are already in a JS array. Return as-is.
+ },
+
+#if USE_TYPED_ARRAYS == 2
+ // Given a file node, returns its file data converted to a typed array.
+ getFileDataAsTypedArray: function(node) {
+ if (node.contents && node.contents.subarray) return node.contents.subarray(0, node.usedBytes); // Make sure to not return excess unused bytes.
+ return new Uint8Array(node.contents);
+ },
+#endif
+
+ // Allocates a new backing store for the given node so that it can fit at least newSize amount of bytes.
+ // May allocate more, to provide automatic geometric increase and amortized linear performance appending writes.
+ // Never shrinks the storage.
+ expandFileStorage: function(node, newCapacity) {
+#if USE_TYPED_ARRAYS == 2
+
+#if !MEMFS_APPEND_TO_TYPED_ARRAYS
+ // If we are asked to expand the size of a file that already exists, revert to using a standard JS array to store the file
+ // instead of a typed array. This makes resizing the array more flexible because we can just .push() elements at the back to
+ // increase the size.
+ if (node.contents && node.contents.subarray && newCapacity > node.contents.length) {
+ node.contents = MEMFS.getFileDataAsRegularArray(node);
+ node.usedBytes = node.contents.length; // We might be writing to a lazy-loaded file which had overridden this property, so force-reset it.
+ }
+#endif
+
+ if (!node.contents || node.contents.subarray) { // Keep using a typed array if creating a new storage, or if old one was a typed array as well.
+ var prevCapacity = node.contents ? node.contents.buffer.byteLength : 0;
+ if (prevCapacity >= newCapacity) return; // No need to expand, the storage was already large enough.
+ // Don't expand strictly to the given requested limit if it's only a very small increase, but instead geometrically grow capacity.
+ // For small filesizes (<1MB), perform size*2 geometric increase, but for large sizes, do a much more conservative size*1.125 increase to
+ // avoid overshooting the allocation cap by a very large margin.
+ var CAPACITY_DOUBLING_MAX = 1024 * 1024;
+ newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2.0 : 1.125)) | 0);
+ if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding.
+ var oldContents = node.contents;
+ node.contents = new Uint8Array(newCapacity); // Allocate new storage.
+ if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0); // Copy old data over to the new storage.
+ return;
+ }
+#endif
+ // Not using a typed array to back the file storage. Use a standard JS array instead.
+ if (!node.contents && newCapacity > 0) node.contents = [];
+ while (node.contents.length < newCapacity) node.contents.push(0);
+ },
+
+ // Performs an exact resize of the backing file storage to the given size, if the size is not exactly this, the storage is fully reallocated.
+ resizeFileStorage: function(node, newSize) {
+ if (node.usedBytes == newSize) return;
+ if (newSize == 0) {
+ node.contents = null; // Fully decommit when requesting a resize to zero.
+ node.usedBytes = 0;
+ return;
}
+
+#if USE_TYPED_ARRAYS == 2
+ if (!node.contents || node.contents.subarray) { // Resize a typed array if that is being used as the backing store.
+ var oldContents = node.contents;
+ node.contents = new Uint8Array(new ArrayBuffer(newSize)); // Allocate new storage.
+ node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); // Copy old data over to the new storage.
+ node.usedBytes = newSize;
+ return;
+ }
+#endif
+ // Backing with a JS array.
+ if (!node.contents) node.contents = [];
+ if (node.contents.length > newSize) node.contents.length = newSize;
+ else while (node.contents.length < newSize) node.contents.push(0);
+ node.usedBytes = newSize;
},
+
node_ops: {
getattr: function(node) {
var attr = {};
@@ -108,7 +181,7 @@ mergeInto(LibraryManager.library, {
if (FS.isDir(node.mode)) {
attr.size = 4096;
} else if (FS.isFile(node.mode)) {
- attr.size = node.contents.length;
+ attr.size = node.usedBytes;
} else if (FS.isLink(node.mode)) {
attr.size = node.link.length;
} else {
@@ -131,10 +204,7 @@ mergeInto(LibraryManager.library, {
node.timestamp = attr.timestamp;
}
if (attr.size !== undefined) {
- MEMFS.ensureFlexible(node);
- var contents = node.contents;
- if (attr.size < contents.length) contents.length = attr.size;
- else while (attr.size > contents.length) contents.push(0);
+ MEMFS.resizeFileStorage(node, attr.size);
}
},
lookup: function(parent, name) {
@@ -198,9 +268,8 @@ mergeInto(LibraryManager.library, {
stream_ops: {
read: function(stream, buffer, offset, length, position) {
var contents = stream.node.contents;
- if (position >= contents.length)
- return 0;
- var size = Math.min(contents.length - position, length);
+ if (position >= stream.node.usedBytes) return 0;
+ var size = Math.min(stream.node.usedBytes - position, length);
assert(size >= 0);
#if USE_TYPED_ARRAYS == 2
if (size > 8 && contents.subarray) { // non-trivial, and typed array
@@ -208,47 +277,56 @@ mergeInto(LibraryManager.library, {
} else
#endif
{
- for (var i = 0; i < size; i++) {
- buffer[offset + i] = contents[position + i];
- }
+ for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i];
}
return size;
},
+
+ // Writes the byte range (buffer[offset], buffer[offset+length]) to offset 'position' into the file pointed by 'stream'
write: function(stream, buffer, offset, length, position, canOwn) {
+ if (!length) return 0;
var node = stream.node;
node.timestamp = Date.now();
- var contents = node.contents;
+
#if USE_TYPED_ARRAYS == 2
- if (length && contents.length === 0 && position === 0 && buffer.subarray) {
- // just replace it with the new data
+ if (buffer.subarray && (!node.contents || node.contents.subarray)) { // This write is from a typed array to a typed array?
+ if (canOwn) { // Can we just reuse the buffer we are given?
#if ASSERTIONS
- assert(buffer.length);
+ assert(position === 0, 'canOwn must imply no weird position inside the file');
#endif
- if (canOwn && offset === 0) {
- node.contents = buffer; // this could be a subarray of Emscripten HEAP, or allocated from some other source.
- node.contentMode = (buffer.buffer === HEAP8.buffer) ? MEMFS.CONTENT_OWNING : MEMFS.CONTENT_FIXED;
- } else {
- node.contents = new Uint8Array(buffer.subarray(offset, offset+length));
- node.contentMode = MEMFS.CONTENT_FIXED;
+ node.contents = buffer.subarray(offset, offset + length);
+ node.usedBytes = length;
+ return length;
+ } else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data.
+ node.contents = new Uint8Array(buffer.subarray(offset, offset + length));
+ node.usedBytes = length;
+ return length;
+ } else if (position + length <= node.usedBytes) { // Writing to an already allocated and used subrange of the file?
+ node.contents.set(buffer.subarray(offset, offset + length), position);
+ return length;
}