@addmaple/gzip 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -0
- package/dist/browser-inline.d.ts +2 -0
- package/dist/browser-inline.js +22 -0
- package/dist/browser.d.ts +2 -0
- package/dist/browser.js +24 -0
- package/dist/core.d.ts +11 -0
- package/dist/core.js +125 -0
- package/dist/custom.js +15 -0
- package/dist/node-inline.d.ts +2 -0
- package/dist/node-inline.js +22 -0
- package/dist/node.d.ts +2 -0
- package/dist/node.js +29 -0
- package/dist/util.js +14 -0
- package/dist/wasm/gzip.base.wasm +0 -0
- package/dist/wasm/gzip.simd.wasm +0 -0
- package/dist/wasm-inline/gzip.base.wasm.js +2 -0
- package/dist/wasm-inline/gzip.simd.wasm.js +2 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# @addmaple/gzip
|
|
2
|
+
|
|
3
|
+
Fast Gzip compression in the browser and Node.js using Rust + WASM.
|
|
4
|
+
|
|
5
|
+
**3.5x-5.5x faster** than `pako`.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @addmaple/gzip
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
import { init, compress } from '@addmaple/gzip';
|
|
17
|
+
|
|
18
|
+
await init();
|
|
19
|
+
|
|
20
|
+
const input = new TextEncoder().encode('hello world');
|
|
21
|
+
const compressed = await compress(input, { level: 9 });
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Inline (Zero-latency)
|
|
25
|
+
|
|
26
|
+
WASM bytes embedded directly in JS — no separate file fetching:
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
import { init, compress } from '@addmaple/gzip/inline';
|
|
30
|
+
|
|
31
|
+
await init();
|
|
32
|
+
const compressed = await compress(input);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## API
|
|
36
|
+
|
|
37
|
+
### `init()`
|
|
38
|
+
Initialize the WASM module.
|
|
39
|
+
|
|
40
|
+
### `compress(input, options?)`
|
|
41
|
+
- `input`: `Uint8Array`
|
|
42
|
+
- `options.level`: 1-9 (default: 6)
|
|
43
|
+
- Returns: `Promise<Uint8Array>`
|
|
44
|
+
|
|
45
|
+
## Sponsor
|
|
46
|
+
|
|
47
|
+
Development of this module was sponsored by [addmaple.com](https://addmaple.com) — a modern data analysis platform.
|
|
48
|
+
|
|
49
|
+
## License
|
|
50
|
+
|
|
51
|
+
MIT
|
|
52
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { setInstance, registerInit } from "./core.js";
|
|
2
|
+
import { instantiateWithFallback } from "./util.js";
|
|
3
|
+
|
|
4
|
+
import { wasmBytes as simdBytes } from "./wasm-inline/gzip.simd.wasm.js";
|
|
5
|
+
import { wasmBytes as baseBytes } from "./wasm-inline/gzip.base.wasm.js";
|
|
6
|
+
|
|
7
|
+
async function getWasmBytes() {
|
|
8
|
+
return { simdBytes, baseBytes };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
let _ready = null;
|
|
13
|
+
export function init(imports = {}) {
|
|
14
|
+
return (_ready ??= (async () => {
|
|
15
|
+
const { simdBytes, baseBytes } = await getWasmBytes();
|
|
16
|
+
const { instance } = await instantiateWithFallback(simdBytes, baseBytes, imports);
|
|
17
|
+
setInstance(instance);
|
|
18
|
+
})());
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
registerInit(init);
|
|
22
|
+
export * from "./custom.js";
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { setInstance, registerInit } from "./core.js";
|
|
2
|
+
import { instantiateWithFallback } from "./util.js";
|
|
3
|
+
|
|
4
|
+
const simdUrl = new URL("./wasm/gzip.simd.wasm", import.meta.url);
|
|
5
|
+
const baseUrl = new URL("./wasm/gzip.base.wasm", import.meta.url);
|
|
6
|
+
|
|
7
|
+
async function getWasmBytes() {
|
|
8
|
+
const [simdRes, baseRes] = await Promise.all([fetch(simdUrl), fetch(baseUrl)]);
|
|
9
|
+
const [simdBytes, baseBytes] = await Promise.all([simdRes.arrayBuffer(), baseRes.arrayBuffer()]);
|
|
10
|
+
return { simdBytes, baseBytes };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
let _ready = null;
|
|
15
|
+
export function init(imports = {}) {
|
|
16
|
+
return (_ready ??= (async () => {
|
|
17
|
+
const { simdBytes, baseBytes } = await getWasmBytes();
|
|
18
|
+
const { instance } = await instantiateWithFallback(simdBytes, baseBytes, imports);
|
|
19
|
+
setInstance(instance);
|
|
20
|
+
})());
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
registerInit(init);
|
|
24
|
+
export * from "./custom.js";
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type WasmInput = Uint8Array | ArrayBufferView | ArrayBuffer;
|
|
2
|
+
|
|
3
|
+
export function setInstance(instance: WebAssembly.Instance): void;
|
|
4
|
+
export function wasmExports(): WebAssembly.Exports;
|
|
5
|
+
export function memoryU8(): Uint8Array;
|
|
6
|
+
export function alloc(len: number): number;
|
|
7
|
+
export function free(ptr: number, len: number): void;
|
|
8
|
+
|
|
9
|
+
export function compress_level_1(input: WasmInput): Promise<Uint8Array>;
|
|
10
|
+
export function compress_level_6(input: WasmInput): Promise<Uint8Array>;
|
|
11
|
+
export function compress_level_9(input: WasmInput): Promise<Uint8Array>;
|
package/dist/core.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
let _inst = null;
|
|
2
|
+
let _memU8 = null;
|
|
3
|
+
let _initFn = null;
|
|
4
|
+
|
|
5
|
+
function refreshViews() {
|
|
6
|
+
_memU8 = new Uint8Array(_inst.exports.memory.buffer);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function setInstance(instance) {
|
|
10
|
+
_inst = instance;
|
|
11
|
+
refreshViews();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function wasmExports() {
|
|
15
|
+
return _inst.exports;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let _ready = null;
|
|
19
|
+
export function registerInit(fn) { _initFn = fn; }
|
|
20
|
+
|
|
21
|
+
async function ensureReady() {
|
|
22
|
+
if (_ready) return _ready;
|
|
23
|
+
if (!_initFn) throw new Error("init not registered");
|
|
24
|
+
_ready = _initFn();
|
|
25
|
+
return _ready;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function memoryU8() {
|
|
29
|
+
if (_memU8 && _memU8.buffer !== _inst.exports.memory.buffer) refreshViews();
|
|
30
|
+
return _memU8;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function alloc(len) {
|
|
34
|
+
return _inst.exports.alloc_bytes(len) >>> 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function free(ptr, len) {
|
|
38
|
+
_inst.exports.free_bytes(ptr >>> 0, len >>> 0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function toBytes(input) {
|
|
42
|
+
if (input instanceof Uint8Array) return input;
|
|
43
|
+
if (ArrayBuffer.isView(input)) return new Uint8Array(input.buffer, input.byteOffset, input.byteLength);
|
|
44
|
+
if (input instanceof ArrayBuffer) return new Uint8Array(input);
|
|
45
|
+
throw new TypeError("Expected a TypedArray or ArrayBuffer");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function callWasm(abi, input, outLen, reuse) {
|
|
49
|
+
if (!_inst) throw new Error("WASM instance not initialized");
|
|
50
|
+
const view = toBytes(input);
|
|
51
|
+
const len = view.byteLength;
|
|
52
|
+
|
|
53
|
+
let inPtr, outPtr;
|
|
54
|
+
if (reuse) {
|
|
55
|
+
if (reuse.in.len < len) {
|
|
56
|
+
if (reuse.in.ptr) free(reuse.in.ptr, reuse.in.len);
|
|
57
|
+
reuse.in.ptr = alloc(len);
|
|
58
|
+
reuse.in.len = len;
|
|
59
|
+
}
|
|
60
|
+
if (reuse.out.len < outLen) {
|
|
61
|
+
if (reuse.out.ptr) free(reuse.out.ptr, reuse.out.len);
|
|
62
|
+
reuse.out.ptr = alloc(outLen);
|
|
63
|
+
reuse.out.len = outLen;
|
|
64
|
+
}
|
|
65
|
+
inPtr = reuse.in.ptr;
|
|
66
|
+
outPtr = reuse.out.ptr;
|
|
67
|
+
} else {
|
|
68
|
+
inPtr = alloc(len);
|
|
69
|
+
outPtr = alloc(outLen);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
memoryU8().set(view, inPtr);
|
|
73
|
+
const written = _inst.exports[abi](inPtr, len, outPtr, outLen);
|
|
74
|
+
if (written < 0) {
|
|
75
|
+
if (!reuse) { free(inPtr, len); free(outPtr, outLen); }
|
|
76
|
+
throw new Error(abi + " failed: " + written);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return { inPtr, outPtr, len, outLen, written };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function compress_level_1(input) {
|
|
83
|
+
await ensureReady();
|
|
84
|
+
const view = toBytes(input);
|
|
85
|
+
const len = view.byteLength;
|
|
86
|
+
const outLen = len + 1024;
|
|
87
|
+
const { outPtr, written, inPtr } = callWasm("compress_gzip_level_1", view, outLen, null);
|
|
88
|
+
|
|
89
|
+
const result = memoryU8().slice(outPtr, outPtr + written);
|
|
90
|
+
|
|
91
|
+
free(inPtr, len);
|
|
92
|
+
free(outPtr, outLen);
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
export { compress_level_1 };
|
|
96
|
+
|
|
97
|
+
async function compress_level_6(input) {
|
|
98
|
+
await ensureReady();
|
|
99
|
+
const view = toBytes(input);
|
|
100
|
+
const len = view.byteLength;
|
|
101
|
+
const outLen = len + 1024;
|
|
102
|
+
const { outPtr, written, inPtr } = callWasm("compress_gzip_level_6", view, outLen, null);
|
|
103
|
+
|
|
104
|
+
const result = memoryU8().slice(outPtr, outPtr + written);
|
|
105
|
+
|
|
106
|
+
free(inPtr, len);
|
|
107
|
+
free(outPtr, outLen);
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
export { compress_level_6 };
|
|
111
|
+
|
|
112
|
+
async function compress_level_9(input) {
|
|
113
|
+
await ensureReady();
|
|
114
|
+
const view = toBytes(input);
|
|
115
|
+
const len = view.byteLength;
|
|
116
|
+
const outLen = len + 1024;
|
|
117
|
+
const { outPtr, written, inPtr } = callWasm("compress_gzip_level_9", view, outLen, null);
|
|
118
|
+
|
|
119
|
+
const result = memoryU8().slice(outPtr, outPtr + written);
|
|
120
|
+
|
|
121
|
+
free(inPtr, len);
|
|
122
|
+
free(outPtr, outLen);
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
export { compress_level_9 };
|
package/dist/custom.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { compress_level_1, compress_level_6, compress_level_9, wasmExports } from './core.js';
|
|
2
|
+
|
|
3
|
+
export async function compress(input, options = {}) {
|
|
4
|
+
const level = options.level ?? 6;
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
if (level <= 1) return compress_level_1(input);
|
|
8
|
+
if (level <= 6) return compress_level_6(input);
|
|
9
|
+
return compress_level_9(input);
|
|
10
|
+
} catch (error) {
|
|
11
|
+
throw new Error(`Compression failed: ${error.message}`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { wasmExports };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { setInstance, registerInit } from "./core.js";
|
|
2
|
+
import { instantiateWithFallback } from "./util.js";
|
|
3
|
+
|
|
4
|
+
import { wasmBytes as simdBytes } from "./wasm-inline/gzip.simd.wasm.js";
|
|
5
|
+
import { wasmBytes as baseBytes } from "./wasm-inline/gzip.base.wasm.js";
|
|
6
|
+
|
|
7
|
+
async function getWasmBytes() {
|
|
8
|
+
return { simdBytes, baseBytes };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
let _ready = null;
|
|
13
|
+
export function init(imports = {}) {
|
|
14
|
+
return (_ready ??= (async () => {
|
|
15
|
+
const { simdBytes, baseBytes } = await getWasmBytes();
|
|
16
|
+
const { instance } = await instantiateWithFallback(simdBytes, baseBytes, imports);
|
|
17
|
+
setInstance(instance);
|
|
18
|
+
})());
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
registerInit(init);
|
|
22
|
+
export * from "./custom.js";
|
package/dist/node.d.ts
ADDED
package/dist/node.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { setInstance, registerInit } from "./core.js";
|
|
2
|
+
import { instantiateWithFallback } from "./util.js";
|
|
3
|
+
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const simdPath = fileURLToPath(new URL("./wasm/gzip.simd.wasm", import.meta.url));
|
|
8
|
+
const basePath = fileURLToPath(new URL("./wasm/gzip.base.wasm", import.meta.url));
|
|
9
|
+
|
|
10
|
+
async function getWasmBytes() {
|
|
11
|
+
const [simdBytes, baseBytes] = await Promise.all([
|
|
12
|
+
readFile(simdPath).catch(() => null),
|
|
13
|
+
readFile(basePath).catch(() => null)
|
|
14
|
+
]);
|
|
15
|
+
return { simdBytes, baseBytes };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
let _ready = null;
|
|
20
|
+
export function init(imports = {}) {
|
|
21
|
+
return (_ready ??= (async () => {
|
|
22
|
+
const { simdBytes, baseBytes } = await getWasmBytes();
|
|
23
|
+
const { instance } = await instantiateWithFallback(simdBytes, baseBytes, imports);
|
|
24
|
+
setInstance(instance);
|
|
25
|
+
})());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
registerInit(init);
|
|
29
|
+
export * from "./custom.js";
|
package/dist/util.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export async function instantiateWithFallback(
|
|
2
|
+
trySimdBytes,
|
|
3
|
+
baseBytes,
|
|
4
|
+
imports
|
|
5
|
+
) {
|
|
6
|
+
try {
|
|
7
|
+
const { instance } = await WebAssembly.instantiate(trySimdBytes, imports)
|
|
8
|
+
return { instance, backend: 'wasm-simd' }
|
|
9
|
+
} catch {
|
|
10
|
+
// If SIMD fails (not supported), try baseline
|
|
11
|
+
const { instance } = await WebAssembly.instantiate(baseBytes, imports)
|
|
12
|
+
return { instance, backend: 'wasm' }
|
|
13
|
+
}
|
|
14
|
+
}
|
|
Binary file
|
|
Binary file
|