@addmaple/brotli 0.1.0 → 0.2.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/dist/core.d.ts CHANGED
@@ -9,4 +9,8 @@ export function free(ptr: number, len: number): void;
9
9
  export function compress_level_1(input: WasmInput): Promise<Uint8Array>;
10
10
  export function compress_level_4(input: WasmInput): Promise<Uint8Array>;
11
11
  export function compress_level_6(input: WasmInput): Promise<Uint8Array>;
12
- export function compress_level_9(input: WasmInput): Promise<Uint8Array>;
12
+ export function compress_level_9(input: WasmInput): Promise<Uint8Array>;
13
+ export function decompress_brotli(input: WasmInput): Promise<Uint8Array>;
14
+ export function create_brotli_compressor(input: WasmInput): Promise<number>;
15
+ export function compress_brotli_chunk(input: WasmInput): Promise<Uint8Array>;
16
+ export function destroy_brotli_compressor(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();
@@ -42,7 +42,42 @@ function toBytes(input) {
42
42
  if (input instanceof Uint8Array) return input;
43
43
  if (ArrayBuffer.isView(input)) return new Uint8Array(input.buffer, input.byteOffset, input.byteLength);
44
44
  if (input instanceof ArrayBuffer) return new Uint8Array(input);
45
- throw new TypeError("Expected a TypedArray or ArrayBuffer");
45
+ if (typeof input === 'string') return new TextEncoder().encode(input);
46
+ throw new TypeError("Expected a TypedArray, ArrayBuffer, or string");
47
+ }
48
+
49
+ function scalarSize(type) {
50
+ switch (type) {
51
+ case "f64": return 8;
52
+ case "f32":
53
+ case "i32":
54
+ case "u32": return 4;
55
+ case "i16":
56
+ case "u16": return 2;
57
+ case "i8":
58
+ case "u8": return 1;
59
+ case "u32_array":
60
+ case "i32_array":
61
+ case "f32_array": return 1024 * 1024;
62
+ default: return 0;
63
+ }
64
+ }
65
+
66
+ function decodeReturn(view, type) {
67
+ switch (type) {
68
+ case "f32": return view.getFloat32(0, true);
69
+ case "f64": return view.getFloat64(0, true);
70
+ case "i32": return view.getInt32(0, true);
71
+ case "u32": return view.getUint32(0, true);
72
+ case "i16": return view.getInt16(0, true);
73
+ case "u16": return view.getUint16(0, true);
74
+ case "i8": return view.getInt8(0);
75
+ case "u8": return view.getUint8(0);
76
+ case "u32_array": return new Uint32Array(view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength));
77
+ case "i32_array": return new Int32Array(view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength));
78
+ case "f32_array": return new Float32Array(view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength));
79
+ default: return null;
80
+ }
46
81
  }
47
82
 
48
83
  function callWasm(abi, input, outLen, reuse) {
@@ -138,3 +173,64 @@ async function compress_level_9(input) {
138
173
  return result;
139
174
  }
140
175
  export { compress_level_9 };
176
+
177
+ async function decompress_brotli(input) {
178
+ await ensureReady();
179
+ const view = toBytes(input);
180
+ const len = view.byteLength;
181
+ const outLen = len * 100;
182
+ const { outPtr, written, inPtr } = callWasm("decompress_brotli", 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 { decompress_brotli };
191
+
192
+ async function create_brotli_compressor(input) {
193
+ await ensureReady();
194
+ const view = toBytes(input);
195
+ const len = view.byteLength;
196
+ const outLen = (scalarSize('u32') || 4);
197
+ const { outPtr, written, inPtr } = callWasm("create_brotli_compressor", view, outLen, null);
198
+
199
+ const retView = new DataView(memoryU8().buffer, outPtr, written);
200
+ const result = decodeReturn(retView, "u32");
201
+
202
+ free(inPtr, len);
203
+ free(outPtr, outLen);
204
+ return result;
205
+ }
206
+ export { create_brotli_compressor };
207
+
208
+ async function compress_brotli_chunk(input) {
209
+ await ensureReady();
210
+ const view = toBytes(input);
211
+ const len = view.byteLength;
212
+ const outLen = len + 1024;
213
+ const { outPtr, written, inPtr } = callWasm("compress_brotli_chunk", view, outLen, null);
214
+
215
+ const result = memoryU8().slice(outPtr, outPtr + written);
216
+
217
+ free(inPtr, len);
218
+ free(outPtr, outLen);
219
+ return result;
220
+ }
221
+ export { compress_brotli_chunk };
222
+
223
+ async function destroy_brotli_compressor(input) {
224
+ await ensureReady();
225
+ const view = toBytes(input);
226
+ const len = view.byteLength;
227
+ const outLen = Math.max(len, 4);
228
+ const { outPtr, written, inPtr } = callWasm("destroy_brotli_compressor", view, outLen, null);
229
+
230
+ const result = memoryU8().slice(outPtr, outPtr + written);
231
+
232
+ free(inPtr, len);
233
+ free(outPtr, outLen);
234
+ return result;
235
+ }
236
+ export { destroy_brotli_compressor };
package/dist/custom.js CHANGED
@@ -1,4 +1,30 @@
1
- import { compress_level_1, compress_level_4, compress_level_6, compress_level_9, wasmExports } from './core.js';
1
+ import {
2
+ compress_level_1,
3
+ compress_level_4,
4
+ compress_level_6,
5
+ compress_level_9,
6
+ decompress_brotli,
7
+ create_brotli_compressor,
8
+ compress_brotli_chunk,
9
+ destroy_brotli_compressor,
10
+ wasmExports,
11
+ alloc,
12
+ free,
13
+ memoryU8,
14
+ ensureReady
15
+ } from './core.js';
16
+
17
+ function toBytes(input) {
18
+ if (input instanceof Uint8Array) return input;
19
+ if (ArrayBuffer.isView(input)) return new Uint8Array(input.buffer, input.byteOffset, input.byteLength);
20
+ if (input instanceof ArrayBuffer) return new Uint8Array(input);
21
+ if (typeof input === 'string') return new TextEncoder().encode(input);
22
+ throw new TypeError("Expected a TypedArray, ArrayBuffer, or string");
23
+ }
24
+
25
+ // ============================================================================
26
+ // One-shot Compression API
27
+ // ============================================================================
2
28
 
3
29
  export async function compress(input, options = {}) {
4
30
  const level = options.level ?? 9;
@@ -13,8 +39,143 @@ export async function compress(input, options = {}) {
13
39
  }
14
40
  }
15
41
 
42
+ // ============================================================================
43
+ // One-shot Decompression API
44
+ // ============================================================================
45
+
46
+ export async function decompress(input) {
47
+ await ensureReady();
48
+
49
+ const view = toBytes(input);
50
+ const len = view.byteLength;
51
+
52
+ // Start with a reasonable estimate (100x compressed size)
53
+ let outLen = Math.max(len * 100, 65536);
54
+
55
+ const inPtr = alloc(len);
56
+ let outPtr = alloc(outLen);
57
+
58
+ try {
59
+ memoryU8().set(view, inPtr);
60
+ let written = wasmExports().decompress_brotli(inPtr, len, outPtr, outLen);
61
+
62
+ // Negative value means we need more space
63
+ if (written < 0) {
64
+ const neededLen = -written;
65
+ free(outPtr, outLen);
66
+ outLen = neededLen;
67
+ outPtr = alloc(outLen);
68
+
69
+ // Retry with correct size
70
+ written = wasmExports().decompress_brotli(inPtr, len, outPtr, outLen);
71
+
72
+ if (written < 0) {
73
+ throw new Error('Decompression failed after resize');
74
+ }
75
+ }
76
+
77
+ const result = memoryU8().slice(outPtr, outPtr + written);
78
+ free(inPtr, len);
79
+ free(outPtr, outLen);
80
+ return result;
81
+ } catch (error) {
82
+ free(inPtr, len);
83
+ free(outPtr, outLen);
84
+ throw new Error(`Decompression failed: ${error.message}`);
85
+ }
86
+ }
87
+
88
+ // ============================================================================
89
+ // Streaming Compression API
90
+ // ============================================================================
91
+
92
+ export class StreamingCompressor {
93
+ constructor(options = {}) {
94
+ this._initPromise = ensureReady();
95
+ this.level = options.level ?? 6;
96
+ this.handle = null;
97
+ }
98
+
99
+ async _ensureInit() {
100
+ await this._initPromise;
101
+ if (this.handle === null) {
102
+ this.handle = wasmExports().create_brotli_compressor(this.level);
103
+ if (this.handle === 0) {
104
+ throw new Error('Failed to create compressor');
105
+ }
106
+ }
107
+ }
108
+
109
+ async compressChunk(input, finish = false) {
110
+ await this._ensureInit();
111
+ if (this.handle === 0) {
112
+ throw new Error('Compressor already destroyed');
113
+ }
114
+
115
+ const view = toBytes(input);
116
+ const len = view.byteLength;
117
+ const outLen = len + 1024;
118
+
119
+ const inPtr = alloc(len);
120
+ const outPtr = alloc(outLen);
121
+
122
+ try {
123
+ memoryU8().set(view, inPtr);
124
+ const written = wasmExports().compress_brotli_chunk(this.handle, inPtr, len, outPtr, outLen, finish ? 1 : 0);
125
+
126
+ if (written < 0) {
127
+ if (written === -1) {
128
+ throw new Error('Compression failed');
129
+ } else {
130
+ // Negative value indicates needed buffer size
131
+ free(outPtr, outLen);
132
+ const neededLen = -written;
133
+ const newOutPtr = alloc(neededLen);
134
+ memoryU8().set(view, inPtr);
135
+ const retryWritten = wasmExports().compress_brotli_chunk(this.handle, inPtr, len, newOutPtr, neededLen, finish ? 1 : 0);
136
+ if (retryWritten < 0) {
137
+ free(newOutPtr, neededLen);
138
+ throw new Error('Compression failed after retry');
139
+ }
140
+ const result = memoryU8().slice(newOutPtr, newOutPtr + retryWritten);
141
+ free(newOutPtr, neededLen);
142
+ free(inPtr, len);
143
+ if (finish) this.handle = 0;
144
+ return result;
145
+ }
146
+ }
147
+
148
+ if (written === 0) {
149
+ free(outPtr, outLen);
150
+ free(inPtr, len);
151
+ return new Uint8Array(0);
152
+ }
153
+
154
+ const result = memoryU8().slice(outPtr, outPtr + written);
155
+ free(outPtr, outLen);
156
+ free(inPtr, len);
157
+
158
+ if (finish) this.handle = 0;
159
+ return result;
160
+ } catch (error) {
161
+ free(outPtr, outLen);
162
+ free(inPtr, len);
163
+ throw new Error(`Compression failed: ${error.message}`);
164
+ }
165
+ }
166
+
167
+ async destroy() {
168
+ await this._initPromise;
169
+ if (this.handle !== 0 && this.handle !== null) {
170
+ wasmExports().destroy_brotli_compressor(this.handle);
171
+ this.handle = 0;
172
+ }
173
+ }
174
+ }
175
+
16
176
  export function getLoadedVariant() {
17
177
  return 'lite';
18
178
  }
19
179
 
20
180
  export { wasmExports };
181
+
Binary file