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