fast-base64
Version:
Fastest possible base64 encoding/decoding using WebAssembly
79 lines (65 loc) • 2.61 kB
JavaScript
export {toBytes, toBase64};
const encoder = new TextEncoder();
const decoder = new TextDecoder();
const bytesPerPage = 65536;
let wasm64 =
'AGFzbQEAAAABCgJgAX8AYAJ/fwACEwEHaW1wb3J0cwZtZW1vcnkCAAADAwIAAQcfAgxiYXNlNjQyYnl0' +
'ZXMAAAxieXRlczJiYXNlNjQAAQqzBAKUAgEDfwNAIAMgAkEDai0AACIBQQRqIAFBwABLQcUAbGsgAUHg' +
'AEtBBmxrIAFBK0ZBD2xqIAFBL0ZBDGxqIAItAAAiAUEEaiABQcAAS0HFAGxrIAFB4ABLQQZsayABQStG' +
'QQ9saiABQS9GQQxsakESdCACQQFqLQAAIgFBBGogAUHAAEtBxQBsayABQeAAS0EGbGsgAUErRkEPbGog' +
'AUEvRkEMbGpBDHRyIAJBAmotAAAiAUEEaiABQcAAS0HFAGxrIAFB4ABLQQZsayABQStGQQ9saiABQS9G' +
'QQxsakEGdHJyIgFBEHY6AAAgA0EBaiABQQh2OgAAIANBAmogAToAACADQQNqIQMgAkEEaiICIABJDQAL' +
'C5oCAQN/A0AgASADQQJqLQAAIAMtAABBEHQgA0EBai0AAEEIdHJyIgRBEnYiAkHBAGogAkEZS0EGbGog' +
'AkEzS0HLAGxrIAJBPkZBD2xrIAJBP0ZBDGxrOgAAIAFBAWogBEEMdkE/cSICQcEAaiACQRlLQQZsaiAC' +
'QTNLQcsAbGsgAkE+RkEPbGsgAkE/RkEMbGs6AAAgAUECaiAEQQZ2QT9xIgJBwQBqIAJBGUtBBmxqIAJB' +
'M0tBywBsayACQT5GQQ9sayACQT9GQQxsazoAACABQQNqIARBP3EiAkHBAGogAkEZS0EGbGogAkEzS0HL' +
'AGxrIAJBPkZBD2xrIAJBP0ZBDGxrOgAAIAFBBGohASADQQNqIgMgAEkNAAsL';
// meta: base64-decode the base64 decoder
// this takes ~0.2ms
let wasmStr = atob(wasm64);
let wasmBytes = new Uint8Array(
[...wasmStr].map((_, i) => wasmStr.charCodeAt(i))
);
// wasm takes ~1-2ms to compile
let wasmModule = WebAssembly.compile(wasmBytes);
async function toBytes(base64) {
base64 = base64.replace(/=/g, '');
let n = base64.length;
let rem = n % 4;
let k = rem && rem - 1;
let m = (n >> 2) * 3 + k;
let [{base642bytes}, memory] = await instantiate(n);
let encoded = new Uint8Array(memory.buffer, 0, n);
encoder.encodeInto(base64, encoded);
base642bytes(n);
return new Uint8Array(memory.buffer, 0, m);
}
async function toBase64(bytes) {
let m = bytes.length;
let k = m % 3;
let n = Math.floor(m / 3) * 4 + (k && k + 1);
let M = m + 2;
let N = Math.ceil(m / 3) * 4;
let [{bytes2base64}, memory] = await instantiate(M + N);
let decoded = new Uint8Array(memory.buffer, 0, M);
decoded.set(bytes);
decoded[m] = 0;
decoded[m + 1] = 0;
bytes2base64(m, M);
let encoded = new Uint8Array(memory.buffer, M, n);
let base64 = decoder.decode(encoded);
if (k === 1) base64 += '==';
if (k === 2) base64 += '=';
return base64;
}
// this takes ~0.1-0.5 ms after initial compile
async function instantiate(memSize) {
let memory = new WebAssembly.Memory({
initial: Math.ceil(memSize / bytesPerPage),
});
let compiled = await wasmModule;
let instance = await WebAssembly.instantiate(compiled, {
imports: {memory},
});
return [instance.exports, memory];
}