Skip to content

Commit ebaf397

Browse files
authored
Merge pull request #50 from bombshell-dev/ref/brotli-z85
🧼 compress bundled wasm with brotli + z85 encoding
2 parents 02754a2 + 960d426 commit ebaf397

1 file changed

Lines changed: 52 additions & 7 deletions

File tree

tasks/bundle-wasm.ts

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,58 @@
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 = src[i] * 16777216 +
16+
src[i + 1] * 65536 +
17+
src[i + 2] * 256 +
18+
src[i + 3];
19+
out.push(
20+
Z85[Math.floor(v / 52200625)],
21+
Z85[Math.floor(v / 614125) % 85],
22+
Z85[Math.floor(v / 7225) % 85],
23+
Z85[Math.floor(v / 85) % 85],
24+
Z85[v % 85],
25+
);
26+
}
27+
return out.join("");
28+
}
229

330
const wasm = await Deno.readFile("clayterm.wasm");
4-
const base64 = encodeBase64(wasm);
531

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);
32+
const compressed = new Uint8Array(
33+
brotliCompressSync(wasm, {
34+
params: {
35+
[constants.BROTLI_PARAM_QUALITY]: 11,
36+
[constants.BROTLI_PARAM_SIZE_HINT]: wasm.length,
37+
[constants.BROTLI_PARAM_LGWIN]: 24,
38+
},
39+
}),
40+
);
41+
42+
const z85 = encodeZ85(compressed);
43+
44+
// Decoder uses division instead of >>> to avoid 32-bit truncation on values near 0xFFFFFFFF.
45+
const source = `import{brotliDecompressSync}from"node:zlib";
46+
const Z="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#";
47+
const T=new Uint8Array(128);for(let i=0;i<85;i++)T[Z.charCodeAt(i)]=i;
48+
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;}
49+
const compressed=d(${JSON.stringify(z85)},${compressed.byteLength});
50+
export const compiled=await WebAssembly.compile(new Uint8Array(brotliDecompressSync(compressed)));
1051
`;
1152

1253
await Deno.writeTextFile("wasm.ts", source);
13-
console.log(`wrote wasm.ts (${wasm.length} bytes encoded)`);
54+
console.log(
55+
`wrote wasm.ts (${wasm.length}${compressed.byteLength} bytes compressed, ${z85.length} bytes z85, ${
56+
Math.round(z85.length / wasm.length * 100)
57+
}%)`,
58+
);

0 commit comments

Comments
 (0)