Skip to content

Commit 92d0bb5

Browse files
authored
Move base64 decoding into JS library. NFC (#21441)
The optimized base64Decode was only being using by minimal runtime. After this change it used in the regular runtime too which should speed up decoding.
1 parent 9aa7d44 commit 92d0bb5

File tree

8 files changed

+84
-94
lines changed

8 files changed

+84
-94
lines changed

eslint.config.mjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ export default [{
5555
'src/growableHeap.js',
5656
'src/emrun_prejs.js',
5757
'src/deterministic.js',
58-
'src/base64Decode.js',
5958
'src/proxyWorker.js',
6059
'src/proxyClient.js',
6160
'src/IDBStore.js',

src/base64Decode.js

Lines changed: 0 additions & 80 deletions
This file was deleted.

src/lib/libbase64.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* @license
3+
* Copyright 2020 The Emscripten Authors
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
addToLibrary({
8+
// Decodes a _known valid_ base64 string (without validation) and returns it as a new Uint8Array.
9+
// Benchmarked to be around 5x faster compared to a simple
10+
// "Uint8Array.from(atob(b64), c => c.charCodeAt(0))" (TODO: perhaps use this form in -Oz builds?)
11+
$base64Decode__postset: `
12+
// Precreate a reverse lookup table from chars
13+
// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" back to
14+
// bytes to make decoding fast.
15+
for (var base64ReverseLookup = new Uint8Array(123/*'z'+1*/), i = 25; i >= 0; --i) {
16+
base64ReverseLookup[48+i] = 52+i; // '0-9'
17+
base64ReverseLookup[65+i] = i; // 'A-Z'
18+
base64ReverseLookup[97+i] = 26+i; // 'a-z'
19+
}
20+
base64ReverseLookup[43] = 62; // '+'
21+
base64ReverseLookup[47] = 63; // '/'
22+
`,
23+
$base64Decode__docs: '/** @noinline */',
24+
$base64Decode: (b64) => {
25+
#if ENVIRONMENT_MAY_BE_NODE
26+
if (ENVIRONMENT_IS_NODE) {
27+
var buf = Buffer.from(b64, 'base64');
28+
return new Uint8Array(buf.buffer, buf.byteOffset, buf.length);
29+
}
30+
#endif
31+
32+
#if ASSERTIONS
33+
assert(b64.length % 4 == 0);
34+
#endif
35+
var b1, b2, i = 0, j = 0, bLength = b64.length;
36+
var output = new Uint8Array((bLength*3>>2) - (b64[bLength-2] == '=') - (b64[bLength-1] == '='));
37+
for (; i < bLength; i += 4, j += 3) {
38+
b1 = base64ReverseLookup[b64.charCodeAt(i+1)];
39+
b2 = base64ReverseLookup[b64.charCodeAt(i+2)];
40+
output[j] = base64ReverseLookup[b64.charCodeAt(i)] << 2 | b1 >> 4;
41+
output[j+1] = b1 << 4 | b2 >> 2;
42+
output[j+2] = b2 << 6 | base64ReverseLookup[b64.charCodeAt(i+3)];
43+
}
44+
return output;
45+
},
46+
47+
// Prefix of data URIs emitted by SINGLE_FILE and related options.
48+
// Double quotes here needed, othersise jsifier will not include any in the
49+
// output.
50+
$dataURIPrefix: "'data:application/octet-stream;base64,'",
51+
52+
/**
53+
* Indicates whether filename is a base64 data URI.
54+
*/
55+
$isDataURI: (filename) => filename.startsWith(dataURIPrefix),
56+
57+
// If filename is a base64 data URI, parses and returns data (Buffer on node,
58+
// Uint8Array otherwise). If filename is not a base64 data URI, returns
59+
// undefined.
60+
$tryParseAsDataURI__deps: ['$base64Decode', '$isDataURI', '$dataURIPrefix'],
61+
$tryParseAsDataURI: (filename) => {
62+
if (isDataURI(filename)) {
63+
return base64Decode(filename.slice(dataURIPrefix.length));
64+
}
65+
},
66+
});

src/modules.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ export const LibraryManager = {
102102
libraries.push('libmemoryprofiler.js');
103103
}
104104

105+
if (SUPPORT_BASE64_EMBEDDING) {
106+
libraries.push('libbase64.js');
107+
}
108+
105109
if (AUTODEBUG) {
106110
libraries.push('libautodebug.js');
107111
}

src/postamble_minimal.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ function initRuntime(wasmExports) {
8282

8383
// Initialize wasm (asynchronous)
8484

85+
#if SINGLE_FILE && WASM == 1 && !WASM2JS
86+
Module['wasm'] = base64Decode('<<< WASM_BINARY_DATA >>>');
87+
#endif
88+
8589
// In non-fastcomp non-asm.js builds, grab wasm exports to outer scope
8690
// for emscripten_get_exported_function() to be able to access them.
8791
#if LibraryManager.has('libexports.js')
@@ -106,10 +110,6 @@ var imports = {
106110
#endif // MINIFY_WASM_IMPORTED_MODULES
107111
};
108112

109-
#if SINGLE_FILE && WASM == 1 && !WASM2JS
110-
Module['wasm'] = base64Decode('<<< WASM_BINARY_DATA >>>');
111-
#endif
112-
113113
#if MINIMAL_RUNTIME_STREAMING_WASM_INSTANTIATION
114114
// https://caniuse.com/#feat=wasm and https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiateStreaming
115115
// Firefox 52 added Wasm support, but only Firefox 58 added instantiateStreaming.

src/runtime_shared.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@
1717
#include "growableHeap.js"
1818
#endif
1919

20-
#if SUPPORT_BASE64_EMBEDDING
21-
#include "base64Decode.js"
22-
#endif
23-
2420
#if USE_ASAN
2521
#include "runtime_asan.js"
2622
#endif
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"a.html": 12449,
3-
"a.html.gz": 6808,
4-
"total": 12449,
5-
"total_gz": 6808
2+
"a.html": 12438,
3+
"a.html.gz": 6795,
4+
"total": 12438,
5+
"total_gz": 6795
66
}

tools/link.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1938,7 +1938,12 @@ def phase_emscript(in_wasm, wasm_target, js_syms, base_metadata):
19381938

19391939
# No need to support base64 embedding in wasm2js mode since
19401940
# the module is already in JS format.
1941-
settings.SUPPORT_BASE64_EMBEDDING = settings.SINGLE_FILE and not settings.WASM2JS
1941+
if settings.SINGLE_FILE and not settings.WASM2JS:
1942+
settings.SUPPORT_BASE64_EMBEDDING = 1
1943+
if settings.MINIMAL_RUNTIME:
1944+
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.append('$base64Decode')
1945+
else:
1946+
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.append('$tryParseAsDataURI')
19421947

19431948
if shared.SKIP_SUBPROCS:
19441949
return

0 commit comments

Comments
 (0)