UNPKG

fast-base64

Version:

Fastest possible base64 encoding/decoding using WebAssembly

104 lines (99 loc) 4.54 kB
// inline-worker:__inline-worker function inlineWorker(scriptText) { let blob = new Blob([scriptText], { type: "text/javascript" }); let url = URL.createObjectURL(blob); let worker = new Worker(url); URL.revokeObjectURL(url); return worker; } // js.worker.js function Worker2() { return inlineWorker('function u(t){postMessage([-1,!0,Object.keys(t)]),addEventListener("message",async({data:[e,r,i]})=>{try{let l=await t[r](...i),o=w([l]);postMessage([e,!0,l],o)}catch(l){postMessage([e,!1,""+l])}})}function w(t){let e=[];for(let r of t)r instanceof ArrayBuffer||r instanceof MessagePort||globalThis.ImageBitmap&&r instanceof ImageBitmap||globalThis.OffscreenCanvas&&r instanceof OffscreenCanvas?e.push(r):ArrayBuffer.isView(r)&&r.buffer instanceof ArrayBuffer&&e.push(r.buffer);return e}var b=new TextEncoder,g=new TextDecoder;function p(t){t=t.replace(/=/g,"");let e=t.length,r=e%4,i=r&&r-1,l=(e>>2)*3+i,o=new Uint8Array(e+3);b.encodeInto(t+"===",o);for(let n=0,s=0;n<e;n+=4,s+=3){let a=(f[o[n]]<<18)+(f[o[n+1]]<<12)+(f[o[n+2]]<<6)+f[o[n+3]];o[s]=a>>16,o[s+1]=a>>8&255,o[s+2]=a&255}return new Uint8Array(o.buffer,0,l)}function d(t){let e=t.length,r=e%3,i=Math.floor(e/3)*4+(r&&r+1),l=Math.ceil(e/3)*4,o=new Uint8Array(l);for(let s=0,a=0;a<e;s+=4,a+=3){let c=(t[a]<<16)+(t[a+1]<<8)+(t[a+2]|0);o[s]=m[c>>18],o[s+1]=m[c>>12&63],o[s+2]=m[c>>6&63],o[s+3]=m[c&63]}let n=g.decode(new Uint8Array(o.buffer,0,i));return r===1&&(n+="=="),r===2&&(n+="="),n}var h="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",f=Object.fromEntries(Array.from(h).map((t,e)=>[t.charCodeAt(0),e]));f["=".charCodeAt(0)]=0;f["-".charCodeAt(0)]=62;f["_".charCodeAt(0)]=63;var m=Object.fromEntries(Array.from(h).map((t,e)=>[e,t.charCodeAt(0)]));u({toBytes:p,toBase64:d});\n'); } // util.js function concat(...arrays) { if (!arrays.length) return null; let totalLength = arrays.reduce((acc, value) => acc + value.length, 0); let result = new Uint8Array(totalLength); let length = 0; for (let array of arrays) { result.set(array, length); length += array.length; } return result; } // worker-tools.js async function workerImport(worker, knownImports) { let id = 0; let promises = {}; promises[-1] = !knownImports && Resolvable(); worker.addEventListener("message", ({ data: [id2, isSuccess, result] }) => { if (isSuccess) promises[id2]?.resolve(result); else promises[id2]?.reject(result); delete promises[id2]; }); let imports = knownImports ?? await promises[-1].promise; const caller = (funcName) => (...args) => { let thisId = id++; promises[thisId] = Resolvable(); let transfer = getTransferables(args); worker.postMessage([thisId, funcName, args], transfer); return promises[thisId].promise; }; return Object.fromEntries(imports.map((name) => [name, caller(name)])); } function Resolvable() { let resolvable = {}; resolvable.promise = new Promise((resolve, reject) => { resolvable.resolve = resolve; resolvable.reject = reject; }); return resolvable; } function getTransferables(args) { let transfer = []; for (let x of args) { if (x instanceof ArrayBuffer || x instanceof MessagePort || globalThis.ImageBitmap && x instanceof ImageBitmap || globalThis.OffscreenCanvas && x instanceof OffscreenCanvas) { transfer.push(x); } else if (ArrayBuffer.isView(x) && x.buffer instanceof ArrayBuffer) { transfer.push(x.buffer); } } return transfer; } // js-threads.js var nWorkers = 4; var workerImports = Promise.all(new Array(nWorkers).fill(0).map(() => workerImport(Worker2()))); async function toBytes(base64) { let n = base64.length; let k = Math.floor(n / nWorkers); k -= k % 4; let slices = new Array(nWorkers); for (let i = 0; i < nWorkers - 1; i++) { slices[i] = [i * k, (i + 1) * k]; } slices[nWorkers - 1] = [(nWorkers - 1) * k, n]; let workers = await workerImports; let parts = await Promise.all(slices.map(([j0, j1], i) => workers[i].toBytes(base64.slice(j0, j1)))); return concat(...parts); } async function toBase64(bytes) { let n = bytes.length; let k = Math.floor(n / nWorkers); k -= k % 3; let slices = new Array(nWorkers); for (let i = 0; i < nWorkers - 1; i++) { slices[i] = [i * k, (i + 1) * k]; } slices[nWorkers - 1] = [(nWorkers - 1) * k, n]; let workers = await workerImports; let parts = await Promise.all(slices.map(([j0, j1], i) => workers[i].toBase64(bytes.slice(j0, j1)))); return parts.join(""); } export { toBase64, toBytes };