@addmaple/lz4 0.1.0 → 0.1.2

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 CHANGED
@@ -37,11 +37,12 @@ const compressed = await compress(input);
37
37
  ### `init()`
38
38
  Initialize the WASM module.
39
39
 
40
- ### `compress(input, options?)`
40
+ ### `compress(input)`
41
41
  - `input`: `Uint8Array`
42
- - `options.level`: 1 or 9 (default: 1)
43
42
  - Returns: `Promise<Uint8Array>`
44
43
 
44
+ Note: LZ4 is a single-speed algorithm optimized for maximum throughput. Unlike Brotli/Gzip, it doesn't have compression levels.
45
+
45
46
  ## Sponsor
46
47
 
47
48
  Development of this module was sponsored by [addmaple.com](https://addmaple.com) — a modern data analysis platform.
@@ -1,2 +1,5 @@
1
- export function init(imports?: WebAssembly.Imports): Promise<void>;
1
+ export interface InitOptions {
2
+ backend?: 'auto' | 'simd' | 'base';
3
+ }
4
+ export function init(imports?: WebAssembly.Imports, opts?: InitOptions): Promise<void>;
2
5
  export * from "./custom.js";
@@ -1,19 +1,26 @@
1
1
  import { setInstance, registerInit } from "./core.js";
2
- import { instantiateWithFallback } from "./util.js";
2
+ import { instantiateWithBackend } from "./util.js";
3
3
 
4
- import { wasmBytes as simdBytes } from "./wasm-inline/lz4.simd.wasm.js";
5
- import { wasmBytes as baseBytes } from "./wasm-inline/lz4.base.wasm.js";
4
+ import { wasmBytes as _simdBytes } from "./wasm-inline/lz4.simd.wasm.js";
5
+ import { wasmBytes as _baseBytes } from "./wasm-inline/lz4.base.wasm.js";
6
6
 
7
- async function getWasmBytes() {
8
- return { simdBytes, baseBytes };
7
+ async function getSimdBytes() {
8
+ return _simdBytes;
9
+ }
10
+
11
+ async function getBaseBytes() {
12
+ return _baseBytes;
9
13
  }
10
14
 
11
15
 
12
16
  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
+ let _backend = null;
18
+ export function init(imports = {}, opts = {}) {
19
+ const backend = opts.backend || 'auto';
20
+ if (_ready && _backend === backend) return _ready;
21
+ _backend = backend;
22
+ return (_ready = (async () => {
23
+ const { instance } = await instantiateWithBackend({ getSimdBytes, getBaseBytes, imports, backend });
17
24
  setInstance(instance);
18
25
  })());
19
26
  }
package/dist/browser.d.ts CHANGED
@@ -1,2 +1,5 @@
1
- export function init(imports?: WebAssembly.Imports): Promise<void>;
1
+ export interface InitOptions {
2
+ backend?: 'auto' | 'simd' | 'base';
3
+ }
4
+ export function init(imports?: WebAssembly.Imports, opts?: InitOptions): Promise<void>;
2
5
  export * from "./custom.js";
package/dist/browser.js CHANGED
@@ -1,21 +1,28 @@
1
1
  import { setInstance, registerInit } from "./core.js";
2
- import { instantiateWithFallback } from "./util.js";
2
+ import { instantiateWithBackend } from "./util.js";
3
3
 
4
4
  const simdUrl = new URL("./wasm/lz4.simd.wasm", import.meta.url);
5
5
  const baseUrl = new URL("./wasm/lz4.base.wasm", import.meta.url);
6
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 };
7
+ async function getSimdBytes() {
8
+ const res = await fetch(simdUrl);
9
+ return res.arrayBuffer();
10
+ }
11
+
12
+ async function getBaseBytes() {
13
+ const res = await fetch(baseUrl);
14
+ return res.arrayBuffer();
11
15
  }
12
16
 
13
17
 
14
18
  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
+ let _backend = null;
20
+ export function init(imports = {}, opts = {}) {
21
+ const backend = opts.backend || 'auto';
22
+ if (_ready && _backend === backend) return _ready;
23
+ _backend = backend;
24
+ return (_ready = (async () => {
25
+ const { instance } = await instantiateWithBackend({ getSimdBytes, getBaseBytes, imports, backend });
19
26
  setInstance(instance);
20
27
  })());
21
28
  }
package/dist/core.d.ts CHANGED
@@ -6,5 +6,13 @@ export function memoryU8(): Uint8Array;
6
6
  export function alloc(len: number): number;
7
7
  export function free(ptr: number, len: number): void;
8
8
 
9
- export function compress_level_1(input: WasmInput): Promise<Uint8Array>;
10
- export function compress_level_9(input: WasmInput): Promise<Uint8Array>;
9
+ export function compress_lz4_block(input: WasmInput): Promise<Uint8Array>;
10
+ export function decompress_lz4_block(input: WasmInput): Promise<Uint8Array>;
11
+ export function compress_lz4(input: WasmInput): Promise<Uint8Array>;
12
+ export function create_compressor(input: WasmInput): Promise<number>;
13
+ export function compress_chunk(input: WasmInput): Promise<Uint8Array>;
14
+ export function destroy_compressor(input: WasmInput): Promise<Uint8Array>;
15
+ export function decompress_lz4(input: WasmInput): Promise<Uint8Array>;
16
+ export function create_decompressor(input: WasmInput): Promise<number>;
17
+ export function decompress_chunk(input: WasmInput): Promise<Uint8Array>;
18
+ export function destroy_decompressor(input: WasmInput): Promise<Uint8Array>;
package/dist/core.js CHANGED
@@ -18,7 +18,7 @@ export function wasmExports() {
18
18
  let _ready = null;
19
19
  export function registerInit(fn) { _initFn = fn; }
20
20
 
21
- async function ensureReady() {
21
+ export async function ensureReady() {
22
22
  if (_ready) return _ready;
23
23
  if (!_initFn) throw new Error("init not registered");
24
24
  _ready = _initFn();
@@ -45,6 +45,40 @@ function toBytes(input) {
45
45
  throw new TypeError("Expected a TypedArray or ArrayBuffer");
46
46
  }
47
47
 
48
+ function scalarSize(type) {
49
+ switch (type) {
50
+ case "f64": return 8;
51
+ case "f32":
52
+ case "i32":
53
+ case "u32": return 4;
54
+ case "i16":
55
+ case "u16": return 2;
56
+ case "i8":
57
+ case "u8": return 1;
58
+ case "u32_array":
59
+ case "i32_array":
60
+ case "f32_array": return 1024 * 1024;
61
+ default: return 0;
62
+ }
63
+ }
64
+
65
+ function decodeReturn(view, type) {
66
+ switch (type) {
67
+ case "f32": return view.getFloat32(0, true);
68
+ case "f64": return view.getFloat64(0, true);
69
+ case "i32": return view.getInt32(0, true);
70
+ case "u32": return view.getUint32(0, true);
71
+ case "i16": return view.getInt16(0, true);
72
+ case "u16": return view.getUint16(0, true);
73
+ case "i8": return view.getInt8(0);
74
+ case "u8": return view.getUint8(0);
75
+ case "u32_array": return new Uint32Array(view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength));
76
+ case "i32_array": return new Int32Array(view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength));
77
+ case "f32_array": return new Float32Array(view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength));
78
+ default: return null;
79
+ }
80
+ }
81
+
48
82
  function callWasm(abi, input, outLen, reuse) {
49
83
  if (!_inst) throw new Error("WASM instance not initialized");
50
84
  const view = toBytes(input);
@@ -79,12 +113,27 @@ function callWasm(abi, input, outLen, reuse) {
79
113
  return { inPtr, outPtr, len, outLen, written };
80
114
  }
81
115
 
82
- async function compress_level_1(input) {
116
+ async function compress_lz4_block(input) {
83
117
  await ensureReady();
84
118
  const view = toBytes(input);
85
119
  const len = view.byteLength;
86
120
  const outLen = len + 1024;
87
- const { outPtr, written, inPtr } = callWasm("compress_lz4_level_1", view, outLen, null);
121
+ const { outPtr, written, inPtr } = callWasm("compress_lz4_block", view, outLen, null);
122
+
123
+ const result = memoryU8().slice(outPtr, outPtr + written);
124
+
125
+ free(inPtr, len);
126
+ free(outPtr, outLen);
127
+ return result;
128
+ }
129
+ export { compress_lz4_block };
130
+
131
+ async function decompress_lz4_block(input) {
132
+ await ensureReady();
133
+ const view = toBytes(input);
134
+ const len = view.byteLength;
135
+ const outLen = outLen;
136
+ const { outPtr, written, inPtr } = callWasm("decompress_lz4_block", view, outLen, null);
88
137
 
89
138
  const result = memoryU8().slice(outPtr, outPtr + written);
90
139
 
@@ -92,14 +141,121 @@ async function compress_level_1(input) {
92
141
  free(outPtr, outLen);
93
142
  return result;
94
143
  }
95
- export { compress_level_1 };
144
+ export { decompress_lz4_block };
96
145
 
97
- async function compress_level_9(input) {
146
+ async function compress_lz4(input) {
98
147
  await ensureReady();
99
148
  const view = toBytes(input);
100
149
  const len = view.byteLength;
101
150
  const outLen = len + 1024;
102
- const { outPtr, written, inPtr } = callWasm("compress_lz4_level_9", view, outLen, null);
151
+ const { outPtr, written, inPtr } = callWasm("compress_lz4", view, outLen, null);
152
+
153
+ const result = memoryU8().slice(outPtr, outPtr + written);
154
+
155
+ free(inPtr, len);
156
+ free(outPtr, outLen);
157
+ return result;
158
+ }
159
+ export { compress_lz4 };
160
+
161
+ async function create_compressor(input) {
162
+ await ensureReady();
163
+ const view = toBytes(input);
164
+ const len = view.byteLength;
165
+ const outLen = (scalarSize('u32') || 4);
166
+ const { outPtr, written, inPtr } = callWasm("create_compressor", view, outLen, null);
167
+
168
+ const retView = new DataView(memoryU8().buffer, outPtr, written);
169
+ const result = decodeReturn(retView, "u32");
170
+
171
+ free(inPtr, len);
172
+ free(outPtr, outLen);
173
+ return result;
174
+ }
175
+ export { create_compressor };
176
+
177
+ async function compress_chunk(input) {
178
+ await ensureReady();
179
+ const view = toBytes(input);
180
+ const len = view.byteLength;
181
+ const outLen = len + 1024;
182
+ const { outPtr, written, inPtr } = callWasm("compress_chunk", view, outLen, null);
183
+
184
+ const result = memoryU8().slice(outPtr, outPtr + written);
185
+
186
+ free(inPtr, len);
187
+ free(outPtr, outLen);
188
+ return result;
189
+ }
190
+ export { compress_chunk };
191
+
192
+ async function destroy_compressor(input) {
193
+ await ensureReady();
194
+ const view = toBytes(input);
195
+ const len = view.byteLength;
196
+ const outLen = Math.max(len, 4);
197
+ const { outPtr, written, inPtr } = callWasm("destroy_compressor", view, outLen, null);
198
+
199
+ const result = memoryU8().slice(outPtr, outPtr + written);
200
+
201
+ free(inPtr, len);
202
+ free(outPtr, outLen);
203
+ return result;
204
+ }
205
+ export { destroy_compressor };
206
+
207
+ async function decompress_lz4(input) {
208
+ await ensureReady();
209
+ const view = toBytes(input);
210
+ const len = view.byteLength;
211
+ const outLen = len * 10;
212
+ const { outPtr, written, inPtr } = callWasm("decompress_lz4", view, outLen, null);
213
+
214
+ const result = memoryU8().slice(outPtr, outPtr + written);
215
+
216
+ free(inPtr, len);
217
+ free(outPtr, outLen);
218
+ return result;
219
+ }
220
+ export { decompress_lz4 };
221
+
222
+ async function create_decompressor(input) {
223
+ await ensureReady();
224
+ const view = toBytes(input);
225
+ const len = view.byteLength;
226
+ const outLen = (scalarSize('u32') || 4);
227
+ const { outPtr, written, inPtr } = callWasm("create_decompressor", view, outLen, null);
228
+
229
+ const retView = new DataView(memoryU8().buffer, outPtr, written);
230
+ const result = decodeReturn(retView, "u32");
231
+
232
+ free(inPtr, len);
233
+ free(outPtr, outLen);
234
+ return result;
235
+ }
236
+ export { create_decompressor };
237
+
238
+ async function decompress_chunk(input) {
239
+ await ensureReady();
240
+ const view = toBytes(input);
241
+ const len = view.byteLength;
242
+ const outLen = len * 4;
243
+ const { outPtr, written, inPtr } = callWasm("decompress_chunk", view, outLen, null);
244
+
245
+ const result = memoryU8().slice(outPtr, outPtr + written);
246
+
247
+ free(inPtr, len);
248
+ free(outPtr, outLen);
249
+ return result;
250
+ }
251
+ export { decompress_chunk };
252
+
253
+ async function destroy_decompressor(input) {
254
+ await ensureReady();
255
+ const view = toBytes(input);
256
+ const len = view.byteLength;
257
+ const outLen = Math.max(len, 4);
258
+ const { outPtr, written, inPtr } = callWasm("destroy_decompressor", view, outLen, null);
103
259
 
104
260
  const result = memoryU8().slice(outPtr, outPtr + written);
105
261
 
@@ -107,4 +263,4 @@ async function compress_level_9(input) {
107
263
  free(outPtr, outLen);
108
264
  return result;
109
265
  }
110
- export { compress_level_9 };
266
+ export { destroy_decompressor };
package/dist/custom.js CHANGED
@@ -1,14 +1,339 @@
1
- import { compress_level_1, compress_level_9, wasmExports } from './core.js';
1
+ import {
2
+ compress_lz4,
3
+ compress_lz4_block,
4
+ decompress_lz4,
5
+ decompress_lz4_block,
6
+ wasmExports,
7
+ alloc,
8
+ free,
9
+ memoryU8,
10
+ ensureReady
11
+ } from './core.js';
2
12
 
3
- export async function compress(input, options = {}) {
4
- const level = options.level ?? 1;
13
+ function toBytes(input) {
14
+ if (input instanceof Uint8Array) return input;
15
+ if (ArrayBuffer.isView(input)) return new Uint8Array(input.buffer, input.byteOffset, input.byteLength);
16
+ if (input instanceof ArrayBuffer) return new Uint8Array(input);
17
+ throw new TypeError("Expected a TypedArray or ArrayBuffer");
18
+ }
19
+
20
+ // ============================================================================
21
+ // Block API - Maximum speed, no frame overhead
22
+ // Output is NOT compatible with standard LZ4 tools (lz4 CLI, etc.)
23
+ // Use when you control both compression and decompression
24
+ // ============================================================================
25
+
26
+ /**
27
+ * Compress using raw LZ4 block format (maximum speed)
28
+ * ~5x faster than frame API due to no checksumming overhead
29
+ * @param {Uint8Array} input - Data to compress
30
+ * @returns {Promise<Uint8Array>} Compressed data (raw block format)
31
+ */
32
+ export async function compressBlock(input) {
33
+ try {
34
+ return compress_lz4_block(input);
35
+ } catch (error) {
36
+ throw new Error(`Block compression failed: ${error.message}`);
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Decompress raw LZ4 block format
42
+ * @param {Uint8Array} input - Compressed data (raw block format)
43
+ * @param {number} originalSize - Original uncompressed size (REQUIRED)
44
+ * @returns {Promise<Uint8Array>} Decompressed data
45
+ */
46
+ export async function decompressBlock(input, originalSize) {
47
+ if (typeof originalSize !== 'number' || originalSize <= 0) {
48
+ throw new Error('decompressBlock requires originalSize parameter');
49
+ }
50
+ await ensureReady();
51
+
52
+ const view = toBytes(input);
53
+ const len = view.byteLength;
5
54
 
55
+ const inPtr = alloc(len);
56
+ const outPtr = alloc(originalSize);
57
+
58
+ try {
59
+ memoryU8().set(view, inPtr);
60
+ const written = wasmExports().decompress_lz4_block(inPtr, len, outPtr, originalSize);
61
+
62
+ if (written < 0) {
63
+ throw new Error('Block decompression failed');
64
+ }
65
+
66
+ const result = memoryU8().slice(outPtr, outPtr + written);
67
+ return result;
68
+ } finally {
69
+ free(inPtr, len);
70
+ free(outPtr, originalSize);
71
+ }
72
+ }
73
+
74
+ // ============================================================================
75
+ // Packed Block API - Block format with size prefix (self-contained)
76
+ // Perfect for network transfer when you control both client and server
77
+ // Format: [4 bytes: original size (little-endian)] + [compressed data]
78
+ // ============================================================================
79
+
80
+ /**
81
+ * Compress with size prefix - ready for network transfer
82
+ * Output includes original size, so decompression doesn't need it separately
83
+ * @param {Uint8Array} input - Data to compress
84
+ * @returns {Promise<Uint8Array>} [4-byte size prefix] + [compressed block]
85
+ */
86
+ export async function compressPacked(input) {
87
+ const view = toBytes(input);
88
+ const compressed = await compressBlock(view);
89
+
90
+ // Prepend 4-byte little-endian size
91
+ const result = new Uint8Array(4 + compressed.length);
92
+ const dataView = new DataView(result.buffer);
93
+ dataView.setUint32(0, view.length, true); // little-endian
94
+ result.set(compressed, 4);
95
+
96
+ return result;
97
+ }
98
+
99
+ /**
100
+ * Decompress packed format (with size prefix)
101
+ * @param {Uint8Array} input - Packed compressed data (from compressPacked)
102
+ * @returns {Promise<Uint8Array>} Decompressed data
103
+ */
104
+ export async function decompressPacked(input) {
105
+ const view = toBytes(input);
106
+ if (view.length < 4) {
107
+ throw new Error('Invalid packed data: too short');
108
+ }
109
+
110
+ const dataView = new DataView(view.buffer, view.byteOffset, view.byteLength);
111
+ const originalSize = dataView.getUint32(0, true); // little-endian
112
+ const compressed = view.subarray(4);
113
+
114
+ return decompressBlock(compressed, originalSize);
115
+ }
116
+
117
+ // ============================================================================
118
+ // Frame API - Standard LZ4 frame format (compatible with lz4 CLI tools)
119
+ // Includes headers and checksums for integrity
120
+ // ============================================================================
121
+
122
+ /**
123
+ * Compress using standard LZ4 frame format
124
+ * Compatible with lz4 CLI and other standard tools
125
+ * @param {Uint8Array} input - Data to compress
126
+ * @returns {Promise<Uint8Array>} Compressed data (LZ4 frame format)
127
+ */
128
+ export async function compress(input) {
6
129
  try {
7
- if (level <= 1) return compress_level_1(input);
8
- return compress_level_9(input);
130
+ return compress_lz4(input);
9
131
  } catch (error) {
10
132
  throw new Error(`Compression failed: ${error.message}`);
11
133
  }
12
134
  }
13
135
 
136
+ // Streaming compression API
137
+ //
138
+ // Note: We don't use wasm-bindgen-lite's createTransformStream() helper here because
139
+ // compression/decompression requires stateful streaming:
140
+ // - Compression: Must accumulate input chunks until finish() is called
141
+ // - Decompression: Must handle partial frames and buffer incomplete data
142
+ //
143
+ // createTransformStream() is designed for stateless transformations where each chunk
144
+ // is processed independently. Instead, we use a manual handle-based approach that:
145
+ // 1. Creates compressor/decompressor handles in Rust (stateful)
146
+ // 2. Maintains state between chunks via handles
147
+ // 3. Manually manages WASM memory allocation/freeing
148
+ export class StreamingCompressor {
149
+ constructor() {
150
+ this._initPromise = ensureReady();
151
+ this.handle = null;
152
+ }
153
+
154
+ async _ensureInit() {
155
+ await this._initPromise;
156
+ if (this.handle === null) {
157
+ this.handle = wasmExports().create_compressor();
158
+ if (this.handle === 0) {
159
+ throw new Error('Failed to create compressor');
160
+ }
161
+ }
162
+ }
163
+
164
+ async compressChunk(input, finish = false) {
165
+ await this._ensureInit();
166
+ if (this.handle === 0) {
167
+ throw new Error('Compressor already destroyed');
168
+ }
169
+
170
+ const view = toBytes(input);
171
+ const len = view.byteLength;
172
+ const outLen = len + 1024; // LZ4 compression typically produces smaller output
173
+
174
+ const inPtr = alloc(len);
175
+ const outPtr = alloc(outLen);
176
+
177
+ try {
178
+ memoryU8().set(view, inPtr);
179
+ const written = wasmExports().compress_chunk(this.handle, inPtr, len, outPtr, outLen, finish ? 1 : 0);
180
+
181
+ if (written < 0) {
182
+ if (written === -1) {
183
+ throw new Error('Compression failed');
184
+ } else {
185
+ // Negative value indicates needed buffer size
186
+ free(outPtr, outLen);
187
+ const neededLen = -written;
188
+ const newOutPtr = alloc(neededLen);
189
+ memoryU8().set(view, inPtr);
190
+ const retryWritten = wasmExports().compress_chunk(this.handle, inPtr, len, newOutPtr, neededLen, finish ? 1 : 0);
191
+ if (retryWritten < 0) {
192
+ free(newOutPtr, neededLen);
193
+ throw new Error('Compression failed after retry');
194
+ }
195
+ const result = memoryU8().slice(newOutPtr, newOutPtr + retryWritten);
196
+ free(newOutPtr, neededLen);
197
+ free(inPtr, len);
198
+ if (finish) {
199
+ this.handle = 0;
200
+ }
201
+ return result;
202
+ }
203
+ }
204
+
205
+ if (written === 0) {
206
+ // No output yet (buffering)
207
+ free(outPtr, outLen);
208
+ free(inPtr, len);
209
+ return new Uint8Array(0);
210
+ }
211
+
212
+ const result = memoryU8().slice(outPtr, outPtr + written);
213
+ free(outPtr, outLen);
214
+ free(inPtr, len);
215
+
216
+ if (finish) {
217
+ this.handle = 0;
218
+ }
219
+
220
+ return result;
221
+ } catch (error) {
222
+ free(outPtr, outLen);
223
+ free(inPtr, len);
224
+ throw new Error(`Compression failed: ${error.message}`);
225
+ }
226
+ }
227
+
228
+ async destroy() {
229
+ await this._ensureInit();
230
+ if (this.handle !== 0 && this.handle !== null) {
231
+ wasmExports().destroy_compressor(this.handle);
232
+ this.handle = 0;
233
+ }
234
+ }
235
+ }
236
+
237
+ // Streaming decompression API
238
+ //
239
+ // See note above about why we use manual handles instead of createTransformStream()
240
+ export class StreamingDecompressor {
241
+ constructor() {
242
+ this._initPromise = ensureReady();
243
+ this.handle = null;
244
+ }
245
+
246
+ async _ensureInit() {
247
+ await this._initPromise;
248
+ if (this.handle === null) {
249
+ this.handle = wasmExports().create_decompressor();
250
+ if (this.handle === 0) {
251
+ throw new Error('Failed to create decompressor');
252
+ }
253
+ }
254
+ }
255
+
256
+ async decompressChunk(input, finish = false) {
257
+ await this._ensureInit();
258
+ if (this.handle === 0) {
259
+ throw new Error('Decompressor already destroyed');
260
+ }
261
+
262
+ const view = toBytes(input);
263
+ const len = view.byteLength;
264
+ const outLen = len * 4; // Decompressed data is typically larger
265
+
266
+ const inPtr = alloc(len);
267
+ const outPtr = alloc(outLen);
268
+
269
+ try {
270
+ memoryU8().set(view, inPtr);
271
+ const written = wasmExports().decompress_chunk(this.handle, inPtr, len, outPtr, outLen, finish ? 1 : 0);
272
+
273
+ if (written < 0) {
274
+ if (written === -1) {
275
+ throw new Error('Decompression failed');
276
+ } else {
277
+ // Negative value indicates needed buffer size
278
+ free(outPtr, outLen);
279
+ const neededLen = -written;
280
+ const newOutPtr = alloc(neededLen);
281
+ memoryU8().set(view, inPtr);
282
+ const retryWritten = wasmExports().decompress_chunk(this.handle, inPtr, len, newOutPtr, neededLen, finish ? 1 : 0);
283
+ if (retryWritten < 0) {
284
+ free(newOutPtr, neededLen);
285
+ throw new Error('Decompression failed after retry');
286
+ }
287
+ const result = memoryU8().slice(newOutPtr, newOutPtr + retryWritten);
288
+ free(newOutPtr, neededLen);
289
+ free(inPtr, len);
290
+ if (finish) {
291
+ this.handle = 0;
292
+ }
293
+ return result;
294
+ }
295
+ }
296
+
297
+ if (written === 0) {
298
+ // No output yet (buffering)
299
+ free(outPtr, outLen);
300
+ free(inPtr, len);
301
+ return new Uint8Array(0);
302
+ }
303
+
304
+ const result = memoryU8().slice(outPtr, outPtr + written);
305
+ free(outPtr, outLen);
306
+ free(inPtr, len);
307
+
308
+ if (finish) {
309
+ this.handle = 0;
310
+ }
311
+
312
+ return result;
313
+ } catch (error) {
314
+ free(outPtr, outLen);
315
+ free(inPtr, len);
316
+ throw new Error(`Decompression failed: ${error.message}`);
317
+ }
318
+ }
319
+
320
+ async destroy() {
321
+ await this._ensureInit();
322
+ if (this.handle !== 0 && this.handle !== null) {
323
+ wasmExports().destroy_decompressor(this.handle);
324
+ this.handle = 0;
325
+ }
326
+ }
327
+ }
328
+
329
+ // One-shot decompression
330
+ export async function decompress(input) {
331
+ try {
332
+ return decompress_lz4(input);
333
+ } catch (error) {
334
+ throw new Error(`Decompression failed: ${error.message}`);
335
+ }
336
+ }
337
+
14
338
  export { wasmExports };
339
+
@@ -1,2 +1,5 @@
1
- export function init(imports?: WebAssembly.Imports): Promise<void>;
1
+ export interface InitOptions {
2
+ backend?: 'auto' | 'simd' | 'base';
3
+ }
4
+ export function init(imports?: WebAssembly.Imports, opts?: InitOptions): Promise<void>;
2
5
  export * from "./custom.js";