Skip to content

Commit bdfa745

Browse files
committed
⚡ use brotli-11 + z85 wasm encoding
1 parent e67760b commit bdfa745

2 files changed

Lines changed: 44 additions & 12 deletions

File tree

deno.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
"include": ["*.ts"],
2626
"exclude": ["!wasm.ts"]
2727
},
28+
"compilerOptions": {
29+
"types": ["npm:@types/node"]
30+
},
2831
"fmt": {
2932
"exclude": ["clay", "build"]
3033
},

tasks/bundle-wasm.ts

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,52 @@
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+
}
225

326
const wasm = await Deno.readFile("clayterm.wasm");
427

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+
}));
935

10-
const base64 = encodeBase64(new Uint8Array(compressed));
36+
const z85 = encodeZ85(compressed);
1137

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));
1645
`;
1746

1847
await Deno.writeTextFile("wasm.ts", source);
1948
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)
2251
}%)`,
2352
);

0 commit comments

Comments
 (0)