zlib-streams 1.0.6 → 1.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.
@@ -1,7 +1,39 @@
1
- /* eslint-disable no-unused-vars */
2
- /* global Buffer, process, TransformStream */
1
+ /*
2
+ Copyright (c) 2025 Gildas Lormeau. All rights reserved.
3
3
 
4
- let wasm, malloc, free, memory;
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above copyright
11
+ notice, this list of conditions and the following disclaimer in
12
+ the documentation and/or other materials provided with the distribution.
13
+
14
+ 3. The names of the authors may not be used to endorse or promote products
15
+ derived from this software without specific prior written permission.
16
+
17
+ THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
18
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
19
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
20
+ INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
21
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23
+ OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+ */
28
+
29
+ /* global TransformStream */
30
+
31
+ const FORMAT_DEFLATE = "deflate";
32
+ const FORMAT_DEFLATE_RAW = "deflate-raw";
33
+ const FORMAT_DEFLATE64_RAW = "deflate64-raw";
34
+ const FORMAT_GZIP = "gzip";
35
+
36
+ let wasm, malloc, free, memory, initError;
5
37
 
6
38
  export function setWasmExports(wasmAPI) {
7
39
  wasm = wasmAPI;
@@ -12,53 +44,74 @@ export function setWasmExports(wasmAPI) {
12
44
  }
13
45
  }
14
46
 
47
+ export function setInitError(error) {
48
+ initError = error;
49
+ }
50
+
51
+ export function resetWasmExports() {
52
+ wasm = malloc = free = memory = initError = null;
53
+ }
54
+
15
55
  function _make(isCompress, type, options = {}) {
56
+ if (!wasm) {
57
+ const error = new Error("WASM module not loaded");
58
+ error.cause = initError;
59
+ throw error;
60
+ }
16
61
  const level = (typeof options.level === "number") ? options.level : -1;
17
62
  const outBufferSize = (typeof options.outBuffer === "number") ? options.outBuffer : 64 * 1024;
18
63
  const inBufferSize = (typeof options.inBufferSize === "number") ? options.inBufferSize : 64 * 1024;
19
64
 
20
65
  return new TransformStream({
21
66
  start() {
22
- let result;
23
- this.out = malloc(outBufferSize);
24
- this.in = malloc(inBufferSize);
25
- this.inBufferSize = inBufferSize;
26
- this._scratch = new Uint8Array(outBufferSize);
27
- if (isCompress) {
28
- this._process = wasm.deflate_process;
29
- this._last_consumed = wasm.deflate_last_consumed;
30
- this._end = wasm.deflate_end;
31
- this.streamHandle = wasm.deflate_new();
32
- if (type === "gzip") {
33
- result = wasm.deflate_init_gzip(this.streamHandle, level);
34
- } else if (type === "deflate-raw") {
35
- result = wasm.deflate_init_raw(this.streamHandle, level);
36
- } else {
37
- result = wasm.deflate_init(this.streamHandle, level);
67
+ try {
68
+ let result;
69
+ this.out = malloc(outBufferSize);
70
+ this.in = malloc(inBufferSize);
71
+ this.inBufferSize = inBufferSize;
72
+ if (!this.out || !this.in) {
73
+ throw new Error("allocation failed");
38
74
  }
39
- } else {
40
- if (type === "deflate64-raw") {
41
- this._process = wasm.inflate9_process;
42
- this._last_consumed = wasm.inflate9_last_consumed;
43
- this._end = wasm.inflate9_end;
44
- this.streamHandle = wasm.inflate9_new();
45
- result = wasm.inflate9_init_raw(this.streamHandle);
75
+ this._scratch = new Uint8Array(outBufferSize);
76
+ if (isCompress) {
77
+ this._process = wasm.deflate_process;
78
+ this._last_consumed = wasm.deflate_last_consumed;
79
+ this._end = wasm.deflate_end;
80
+ this.streamHandle = wasm.deflate_new();
81
+ if (type === FORMAT_GZIP) {
82
+ result = wasm.deflate_init_gzip(this.streamHandle, level);
83
+ } else if (type === FORMAT_DEFLATE_RAW) {
84
+ result = wasm.deflate_init_raw(this.streamHandle, level);
85
+ } else {
86
+ result = wasm.deflate_init(this.streamHandle, level);
87
+ }
46
88
  } else {
47
- this._process = wasm.inflate_process;
48
- this._last_consumed = wasm.inflate_last_consumed;
49
- this._end = wasm.inflate_end;
50
- this.streamHandle = wasm.inflate_new();
51
- if (type === "deflate-raw") {
52
- result = wasm.inflate_init_raw(this.streamHandle);
53
- } else if (type === "gzip") {
54
- result = wasm.inflate_init_gzip(this.streamHandle);
89
+ if (type === FORMAT_DEFLATE64_RAW) {
90
+ this._process = wasm.inflate9_process;
91
+ this._last_consumed = wasm.inflate9_last_consumed;
92
+ this._end = wasm.inflate9_end;
93
+ this.streamHandle = wasm.inflate9_new();
94
+ result = wasm.inflate9_init_raw(this.streamHandle);
55
95
  } else {
56
- result = wasm.inflate_init(this.streamHandle);
96
+ this._process = wasm.inflate_process;
97
+ this._last_consumed = wasm.inflate_last_consumed;
98
+ this._end = wasm.inflate_end;
99
+ this.streamHandle = wasm.inflate_new();
100
+ if (type === FORMAT_DEFLATE_RAW) {
101
+ result = wasm.inflate_init_raw(this.streamHandle);
102
+ } else if (type === FORMAT_GZIP) {
103
+ result = wasm.inflate_init_gzip(this.streamHandle);
104
+ } else {
105
+ result = wasm.inflate_init(this.streamHandle);
106
+ }
57
107
  }
58
108
  }
59
- }
60
- if (result !== 0) {
61
- throw new Error("init failed:" + result);
109
+ if (result !== 0) {
110
+ throw new Error("init failed:" + result);
111
+ }
112
+ } catch (error) {
113
+ disposeStream(this);
114
+ throw error;
62
115
  }
63
116
  },
64
117
  transform(chunk, controller) {
@@ -75,9 +128,13 @@ function _make(isCompress, type, options = {}) {
75
128
  if (!this.in || this.inBufferSize < toRead) {
76
129
  if (this.in && free) {
77
130
  free(this.in);
131
+ this.in = 0;
78
132
  }
79
133
  this.in = malloc(toRead);
80
134
  this.inBufferSize = toRead;
135
+ if (!this.in) {
136
+ throw new Error("allocation failed");
137
+ }
81
138
  }
82
139
  heap.set(buffer.subarray(offset, offset + toRead), this.in);
83
140
  const result = process(this.streamHandle, this.in, toRead, out, outBufferSize, 0);
@@ -94,21 +151,13 @@ function _make(isCompress, type, options = {}) {
94
151
  }
95
152
  }
96
153
  const consumed = last_consumed(this.streamHandle);
97
- if (consumed === 0) {
154
+ if (consumed === 0 && prod === 0) {
98
155
  break;
99
156
  }
100
157
  offset += consumed;
101
158
  }
102
159
  } catch (error) {
103
- if (this._end && this.streamHandle) {
104
- this._end(this.streamHandle);
105
- }
106
- if (this.in && free) {
107
- free(this.in);
108
- }
109
- if (this.out && free) {
110
- free(this.out);
111
- }
160
+ disposeStream(this);
112
161
  controller.error(error);
113
162
  }
114
163
  },
@@ -139,30 +188,54 @@ function _make(isCompress, type, options = {}) {
139
188
  } catch (error) {
140
189
  controller.error(error);
141
190
  } finally {
142
- if (this._end && this.streamHandle) {
143
- const result = this._end(this.streamHandle);
144
- if (result !== 0) {
145
- controller.error(new Error("end error:" + result));
146
- }
147
- }
148
- if (this.in && free) {
149
- free(this.in);
150
- }
151
- if (this.out && free) {
152
- free(this.out);
191
+ const result = disposeStream(this);
192
+ if (result !== 0) {
193
+ controller.error(new Error("end error:" + result));
153
194
  }
154
195
  }
196
+ },
197
+ cancel() {
198
+ // release the stream handle and buffers when the pipeline is aborted,
199
+ // they would be leaked in the process-lifetime wasm heap otherwise
200
+ disposeStream(this);
155
201
  }
156
202
  });
203
+
204
+ function disposeStream(state) {
205
+ let endResult = 0;
206
+ if (state.streamHandle && state._end) {
207
+ endResult = state._end(state.streamHandle);
208
+ }
209
+ state.streamHandle = 0;
210
+ if (state.in && free) {
211
+ free(state.in);
212
+ }
213
+ state.in = 0;
214
+ if (state.out && free) {
215
+ free(state.out);
216
+ }
217
+ state.out = 0;
218
+ return endResult;
219
+ }
157
220
  }
158
221
 
159
222
  export class CompressionStreamZlib {
160
- constructor(type = "deflate", options) {
223
+ constructor(type = FORMAT_DEFLATE, options) {
161
224
  return _make(true, type, options);
162
225
  }
163
226
  }
164
227
  export class DecompressionStreamZlib {
165
- constructor(type = "deflate", options) {
228
+ constructor(type = FORMAT_DEFLATE, options) {
166
229
  return _make(false, type, options);
167
230
  }
168
- }
231
+ }
232
+ // These codecs are backed by the WASM module; they are unusable until setWasmExports() has run.
233
+ // The worker uses this flag to know it must fall back to the native CompressionStream when the
234
+ // module fails to load, rather than discarding a self-contained codec supplied through config.
235
+ CompressionStreamZlib.requiresModule = true;
236
+ DecompressionStreamZlib.requiresModule = true;
237
+ // Constructing these classes before the module is loaded throws, so capability probes cannot rely
238
+ // on trying the constructor; the formats are declared instead, next to the branches implementing
239
+ // them in _make().
240
+ CompressionStreamZlib.supportedFormats = [FORMAT_DEFLATE, FORMAT_DEFLATE_RAW, FORMAT_GZIP];
241
+ DecompressionStreamZlib.supportedFormats = [FORMAT_DEFLATE, FORMAT_DEFLATE_RAW, FORMAT_GZIP, FORMAT_DEFLATE64_RAW];
@@ -2,15 +2,9 @@
2
2
  #include <string.h>
3
3
  #include <stdint.h>
4
4
  #include "zlib.h"
5
- #include "inflate9.h"
6
5
  #include "wasm_stream_common.h"
7
- #ifndef DEFLATE64_WBITS
8
- #if defined(MAX_WBITS) && (MAX_WBITS >= 16)
9
- #define DEFLATE64_WBITS MAX_WBITS
10
- #else
6
+
11
7
  #define DEFLATE64_WBITS 16
12
- #endif
13
- #endif
14
8
 
15
9
  struct wasm_inflate9_ctx {
16
10
  WASM_STREAM_COMMON_FIELDS;
@@ -18,28 +12,22 @@ struct wasm_inflate9_ctx {
18
12
 
19
13
  unsigned inflate9_new(void) { return wasm_stream_new(); }
20
14
 
21
- int inflate9_init(unsigned zptr) {
22
- struct wasm_inflate9_ctx *c = (struct wasm_inflate9_ctx *)(uintptr_t)zptr;
23
- if (!c)
24
- return Z_STREAM_ERROR;
25
- return inflate9Init(&c->strm);
26
- }
27
-
28
15
  int inflate9_init_raw(unsigned zptr) {
29
16
  struct wasm_inflate9_ctx *c = (struct wasm_inflate9_ctx *)(uintptr_t)zptr;
30
17
  if (!c)
31
18
  return Z_STREAM_ERROR;
32
- return inflate9Init2_(&c->strm, -DEFLATE64_WBITS, ZLIB_VERSION,
33
- (int)sizeof(z_stream));
19
+ return inflateInit2(&c->strm, -DEFLATE64_WBITS);
34
20
  }
35
21
 
22
+ int inflate9_init(unsigned zptr) { return inflate9_init_raw(zptr); }
23
+
36
24
  int inflate9_process(unsigned zptr, unsigned in_ptr, unsigned in_len,
37
25
  unsigned out_ptr, unsigned out_len, int flush) {
38
26
  return wasm_stream_process_common(zptr, in_ptr, in_len, out_ptr, out_len,
39
- flush, inflate9);
27
+ flush, inflate);
40
28
  }
41
29
 
42
- int inflate9_end(unsigned zptr) { return wasm_stream_end(zptr, inflate9End); }
30
+ int inflate9_end(unsigned zptr) { return wasm_stream_end(zptr, inflateEnd); }
43
31
 
44
32
  unsigned inflate9_last_consumed(unsigned zptr) {
45
33
  return wasm_stream_last_consumed(zptr);