Skip to content

Commit 9110edd

Browse files
authored
Implement -s SINGLE_FILE_BINARY_ENCODE=1 option (emscripten-core#25599)
Implement -s SINGLE_FILE_BINARY_ENCODE=1 option to embed Wasm binary as binary-encoded form instead of base64 form in SINGLE_FILE mode. Continuation of emscripten-core#21478. For comparison of code size, see emscripten-core#21426 (comment).
1 parent 23c2cfe commit 9110edd

16 files changed

+146
-27
lines changed

eslint.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export default [{
2727
'**/test/',
2828
'src/polyfill/',
2929
'src/lib/',
30+
'src/binaryDecode.js',
3031
'src/minimum_runtime_check.js',
3132
'src/runtime_*.js',
3233
'src/shell*.js',

site/source/docs/tools_reference/settings_reference.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2823,6 +2823,20 @@ then you can safely ignore this warning.
28232823

28242824
Default value: false
28252825

2826+
.. _single_file_binary_encode:
2827+
2828+
SINGLE_FILE_BINARY_ENCODE
2829+
=========================
2830+
2831+
If true, binary Wasm content is encoded using a custom UTF-8 embedding
2832+
instead of base64. This generates a smaller binary that compresses well.
2833+
Set this to false to revert back to earlier base64 encoding if you run into
2834+
issues with the binary encoding. (and please let us know of any such issues)
2835+
If no issues arise, this option will permanently become the default in the
2836+
future.
2837+
2838+
Default value: true
2839+
28262840
.. _auto_js_libraries:
28272841

28282842
AUTO_JS_LIBRARIES

src/binaryDecode.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Prevent Closure from minifying the binaryDecode() function, or otherwise
2+
// Closure may analyze through the WASM_BINARY_DATA placeholder string into this
3+
// function, leading into incorrect results.
4+
/** @noinline */
5+
function binaryDecode(bin) {
6+
for (var i = 0, l = bin.length, o = new Uint8Array(l); i < l; ++i) {
7+
o[i] = bin.charCodeAt(i) - 1;
8+
}
9+
return o;
10+
}

src/postamble_minimal.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,9 @@ function initRuntime(wasmExports) {
149149

150150
// Initialize wasm (asynchronous)
151151

152-
#if SINGLE_FILE && WASM == 1 && !WASM2JS
152+
#if SINGLE_FILE && SINGLE_FILE_BINARY_ENCODE && !WASM2JS
153+
Module['wasm'] = binaryDecode("<<< WASM_BINARY_DATA >>>");
154+
#elif SINGLE_FILE && WASM == 1 && !WASM2JS
153155
Module['wasm'] = base64Decode('<<< WASM_BINARY_DATA >>>');
154156
#endif
155157

src/preamble.js

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -423,27 +423,34 @@ function getWasmBinary(file) {}
423423
#else
424424

425425
function findWasmBinary() {
426-
#if SINGLE_FILE
426+
#if SINGLE_FILE && SINGLE_FILE_BINARY_ENCODE && !WASM2JS
427+
return binaryDecode("<<< WASM_BINARY_DATA >>>");
428+
#elif SINGLE_FILE
427429
return base64Decode('<<< WASM_BINARY_DATA >>>');
430+
#elif AUDIO_WORKLET || !EXPORT_ES6 // For an Audio Worklet, we cannot use `new URL()`.
431+
return locateFile('{{{ WASM_BINARY_FILE }}}');
428432
#else
429-
#if EXPORT_ES6 && !AUDIO_WORKLET
430-
if (Module['locateFile']) {
431-
#endif
432-
return locateFile('{{{ WASM_BINARY_FILE }}}');
433-
#if EXPORT_ES6 && !AUDIO_WORKLET // For an Audio Worklet, we cannot use `new URL()`.
434-
}
433+
435434
#if ENVIRONMENT_MAY_BE_SHELL
436435
if (ENVIRONMENT_IS_SHELL) {
437436
return '{{{ WASM_BINARY_FILE }}}';
438437
}
439438
#endif
439+
440+
if (Module['locateFile']) {
441+
return locateFile('{{{ WASM_BINARY_FILE }}}');
442+
}
443+
440444
// Use bundler-friendly `new URL(..., import.meta.url)` pattern; works in browsers too.
441445
return new URL('{{{ WASM_BINARY_FILE }}}', import.meta.url).href;
442-
#endif
446+
443447
#endif
444448
}
445449

446450
function getBinarySync(file) {
451+
#if SINGLE_FILE && SINGLE_FILE_BINARY_ENCODE
452+
return file;
453+
#else
447454
#if SINGLE_FILE
448455
if (ArrayBuffer.isView(file)) {
449456
return file;
@@ -464,6 +471,7 @@ function getBinarySync(file) {
464471
#else
465472
throw 'sync fetching of the wasm failed: you can preload it to Module["wasmBinary"] manually, or emcc.py will do that for you when generating HTML (but not JS)';
466473
#endif
474+
#endif
467475
}
468476

469477
async function getWasmBinary(binaryFile) {

src/proxyClient.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,18 @@ var frameId = 0;
132132

133133
// Worker
134134

135-
var filename = '<<< filename >>>';
135+
var filename = "<<< filename >>>";
136+
137+
#if SINGLE_FILE && SINGLE_FILE_BINARY_ENCODE
138+
#include "binaryDecode.js"
139+
140+
#if ENVIRONMENT_MAY_BE_NODE
141+
if (ENVIRONMENT_IS_NODE) filename = "data:text/javascript;base64," + Buffer.from(binaryDecode(filename)).toString('base64');
142+
else
143+
#endif
144+
filename = URL.createObjectURL(new Blob([binaryDecode(filename)], {type: 'application/javascript'}));
145+
146+
#endif
136147

137148
var worker = new Worker(filename);
138149

src/runtime_common.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ function growMemViews() {
2727
#include "runtime_asan.js"
2828
#endif
2929

30+
#if SINGLE_FILE && SINGLE_FILE_BINARY_ENCODE && !WASM2JS
31+
#include "binaryDecode.js"
32+
#endif
33+
3034
#if MODULARIZE
3135
var readyPromiseResolve, readyPromiseReject;
3236
#endif

src/settings.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1853,6 +1853,15 @@ var WASMFS = false;
18531853
// [link]
18541854
var SINGLE_FILE = false;
18551855

1856+
// If true, binary Wasm content is encoded using a custom UTF-8 embedding
1857+
// instead of base64. This generates a smaller binary that compresses well.
1858+
// Set this to false to revert back to earlier base64 encoding if you run into
1859+
// issues with the binary encoding. (and please let us know of any such issues)
1860+
// If no issues arise, this option will permanently become the default in the
1861+
// future.
1862+
// [link]
1863+
var SINGLE_FILE_BINARY_ENCODE = true;
1864+
18561865
// If set to 1, all JS libraries will be automatically available at link time.
18571866
// This gets set to 0 in STRICT mode (or with MINIMAL_RUNTIME) which mean you
18581867
// need to explicitly specify -lfoo.js in at link time in order to access

test/codesize/test_codesize_hello_single_file.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"a.out.js": 6497,
3-
"a.out.js.gz": 3579,
2+
"a.out.js": 5394,
3+
"a.out.js.gz": 2992,
44
"sent": [
55
"a (fd_write)"
66
]

test/codesize/test_codesize_minimal_O0.expected.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ function createExportWrapper(name, nargs) {
585585
var wasmBinaryFile;
586586

587587
function findWasmBinary() {
588-
return locateFile('a.out.wasm');
588+
return locateFile('a.out.wasm');
589589
}
590590

591591
function getBinarySync(file) {

0 commit comments

Comments
 (0)