|
1 | | -import { encodeBase64 } from "@std/encoding/base64"; |
| 1 | +import { brotliCompressSync, constants } from "node:zlib"; |
| 2 | + |
| 3 | +const Z85 = |
| 4 | + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#"; |
| 5 | + |
| 6 | +function encodeZ85(data: Uint8Array): string { |
| 7 | + let padLen = (4 - (data.length % 4)) % 4; |
| 8 | + let src = data; |
| 9 | + if (padLen > 0) { |
| 10 | + src = new Uint8Array(data.length + padLen); |
| 11 | + src.set(data); |
| 12 | + } |
| 13 | + let out: string[] = []; |
| 14 | + for (let i = 0; i < src.length; i += 4) { |
| 15 | + let v = |
| 16 | + src[i] * 16777216 + |
| 17 | + src[i + 1] * 65536 + |
| 18 | + src[i + 2] * 256 + |
| 19 | + src[i + 3]; |
| 20 | + out.push( |
| 21 | + Z85[Math.floor(v / 52200625)], |
| 22 | + Z85[Math.floor(v / 614125) % 85], |
| 23 | + Z85[Math.floor(v / 7225) % 85], |
| 24 | + Z85[Math.floor(v / 85) % 85], |
| 25 | + Z85[v % 85], |
| 26 | + ); |
| 27 | + } |
| 28 | + return out.join(""); |
| 29 | +} |
2 | 30 |
|
3 | 31 | const wasm = await Deno.readFile("clayterm.wasm"); |
4 | | -const base64 = encodeBase64(wasm); |
5 | 32 |
|
6 | | -const source = `const bin = atob("${base64}"); |
7 | | -const bytes = new Uint8Array(bin.length); |
8 | | -for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i); |
9 | | -export const compiled = await WebAssembly.compile(bytes); |
| 33 | +const compressed = new Uint8Array( |
| 34 | + brotliCompressSync(wasm, { |
| 35 | + params: { |
| 36 | + [constants.BROTLI_PARAM_QUALITY]: 11, |
| 37 | + [constants.BROTLI_PARAM_SIZE_HINT]: wasm.length, |
| 38 | + [constants.BROTLI_PARAM_LGWIN]: 24, |
| 39 | + }, |
| 40 | + }), |
| 41 | +); |
| 42 | + |
| 43 | +const z85 = encodeZ85(compressed); |
| 44 | + |
| 45 | +// Decoder uses division instead of >>> to avoid 32-bit truncation on values near 0xFFFFFFFF. |
| 46 | +const source = `import{brotliDecompressSync}from"node:zlib"; |
| 47 | +const Z="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#"; |
| 48 | +const T=new Uint8Array(128);for(let i=0;i<85;i++)T[Z.charCodeAt(i)]=i; |
| 49 | +function d(s:string,n:number){const b=new Uint8Array(n);let o=0;for(let i=0;i<s.length&&o<n;i+=5){const v=T[s.charCodeAt(i)]*52200625+T[s.charCodeAt(i+1)]*614125+T[s.charCodeAt(i+2)]*7225+T[s.charCodeAt(i+3)]*85+T[s.charCodeAt(i+4)];if(o<n)b[o++]=Math.floor(v/16777216);if(o<n)b[o++]=Math.floor(v/65536)%256;if(o<n)b[o++]=Math.floor(v/256)%256;if(o<n)b[o++]=v%256;}return b;} |
| 50 | +const compressed=d(${JSON.stringify(z85)},${compressed.byteLength}); |
| 51 | +export const compiled=await WebAssembly.compile(new Uint8Array(brotliDecompressSync(compressed))); |
10 | 52 | `; |
11 | 53 |
|
12 | 54 | await Deno.writeTextFile("wasm.ts", source); |
13 | | -console.log(`wrote wasm.ts (${wasm.length} bytes encoded)`); |
| 55 | +console.log( |
| 56 | + `wrote wasm.ts (${wasm.length} → ${compressed.byteLength} bytes compressed, ${z85.length} bytes z85, ${ |
| 57 | + Math.round(z85.length / wasm.length * 100) |
| 58 | + }%)`, |
| 59 | +); |
0 commit comments