feat(base64): Polyfill on XSnap
MODIFY
packages/base64/NEWS.md
packages/base64/NEWS.md
User-visible changes in base64:
+# Next release
+
+- Use native `Base64.encode` or `Base64.decode` if available in global scope.
+ This addresses a performance problem on XS by taking advantage of native code
+ implementations.
+ This makes `base64` a "ponyfill".
+
# 0.2.1 (2021-06-05)
- Packaging fixes.

MODIFY
packages/base64/src/decode.js
packages/base64/src/decode.js
// @ts-check
/* eslint no-bitwise: ["off"] */
+/* global globalThis */
import { monodu64, padding } from './common.js';
@@ -7,12 +8,17 @@ import { monodu64, padding } from './common.js';
* Decodes a Base64 string into bytes, as specified in
* https://tools.ietf.org/html/rfc4648#section-4
*
+ * XSnap is a JavaScript engine based on Moddable/XS.
+ * The algorithm below is orders of magnitude too slow on this VM, but it
+ * arranges a native binding on the global object.
+ * We use that if it is available instead.
+ *
* @param {string} string Base64-encoded string
* @param {string} [name] The name of the string as it will appear in error
* messages.
* @returns {Uint8Array} decoded bytes
*/
-export function decodeBase64(string, name = '<unknown>') {
+const jsDecodeBase64 = (string, name = '<unknown>') => {
const data = new Uint8Array(Math.ceil((string.length * 4) / 3));
let register = 0;
let quantum = 0;
@@ -52,4 +58,7 @@ export function decodeBase64(string, name = '<unknown>') {
}
return data.subarray(0, j);
-}
+};
+
+export const decodeBase64 =
+ globalThis.Base64 !== undefined ? globalThis.Base64.decode : jsDecodeBase64;

MODIFY
packages/base64/src/encode.js
packages/base64/src/encode.js
// @ts-check
/* eslint no-bitwise: ["off"] */
+/* global globalThis */
import { alphabet64, padding } from './common.js';
@@ -7,10 +8,15 @@ import { alphabet64, padding } from './common.js';
* Encodes bytes into a Base64 string, as specified in
* https://tools.ietf.org/html/rfc4648#section-4
*
+ * XSnap is a JavaScript engine based on Moddable/XS.
+ * The algorithm below is orders of magnitude too slow on this VM, but it
+ * arranges a native binding on the global object.
+ * We use that if it is available instead.
+ *
* @param {Uint8Array} data
* @returns {string} base64 encoding
*/
-export function encodeBase64(data) {
+const jsEncodeBase64 = data => {
// A cursory benchmark shows that string concatenation is about 25% faster
// than building an array and joining it in v8, in 2020, for strings of about
// 100 long.
@@ -54,4 +60,7 @@ export function encodeBase64(data) {
throw Error(`internal: bad quantum ${quantum}`);
}
return string;
-}
+};
+
+export const encodeBase64 =
+ globalThis.Base64 !== undefined ? globalThis.Base64.encode : jsEncodeBase64;

feat(base64): Polyfill on XSnap (#889)