zlib-streams 1.2.1 → 1.2.3

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/Makefile CHANGED
@@ -296,6 +296,8 @@ run_all_tests: dist/zlib-streams-dev.wasm
296
296
  @node src/wasm/tests/test_output_buffer_boundary.js dist/zlib-streams-dev.wasm
297
297
  # Error codes packed the same way as a success, on both directions
298
298
  @node src/wasm/tests/test_process_error_encoding.js dist/zlib-streams-dev.wasm
299
+ # Buffers and zlib state released whichever end ends the stream, with no transformer hook
300
+ @node src/wasm/tests/test_stream_abort_releases_memory.js dist/zlib-streams-dev.wasm
299
301
  @echo "Completed run_all_tests"
300
302
 
301
303
  dist/zlib-streams-dev.wasm: $(WASM_SRCS)
@@ -26,7 +26,7 @@
26
26
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
28
 
29
- /* global TransformStream */
29
+ /* global ReadableStream, WritableStream */
30
30
 
31
31
  const FORMAT_DEFLATE = "deflate";
32
32
  const FORMAT_DEFLATE_RAW = "deflate-raw";
@@ -61,142 +61,195 @@ function _make(isCompress, type, options = {}) {
61
61
  const level = (typeof options.level === "number") ? options.level : -1;
62
62
  const outBufferSize = (typeof options.outBuffer === "number") ? options.outBuffer : 64 * 1024;
63
63
  const inBufferSize = (typeof options.inBufferSize === "number") ? options.inBufferSize : 64 * 1024;
64
-
65
- return new TransformStream({
66
- start() {
64
+ const state = { out: 0, in: 0, inBufferSize: 0, streamHandle: 0 };
65
+ let readableController, writableController, resolveWrite, cancelReason;
66
+ let backpressure = true;
67
+ let cancelled = false;
68
+ initStream();
69
+ const readable = new ReadableStream({
70
+ start(controller) {
71
+ readableController = controller;
72
+ },
73
+ pull() {
74
+ backpressure = false;
75
+ resumeWrite();
76
+ },
77
+ cancel(reason) {
78
+ cancelled = true;
79
+ cancelReason = reason;
80
+ disposeStream(state);
81
+ writableController.error(reason);
82
+ resumeWrite();
83
+ }
84
+ }, { highWaterMark: 0 });
85
+ const writable = new WritableStream({
86
+ start(controller) {
87
+ writableController = controller;
88
+ },
89
+ async write(chunk) {
90
+ if (backpressure) {
91
+ await new Promise(resolve => resolveWrite = resolve);
92
+ }
93
+ if (cancelled) {
94
+ throw cancelReason;
95
+ }
67
96
  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");
74
- }
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
- }
88
- } else {
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);
95
- } else {
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
- }
107
- }
108
- }
109
- if (result !== 0) {
110
- throw new Error("init failed:" + result);
111
- }
97
+ processChunk(chunk);
112
98
  } catch (error) {
113
- disposeStream(this);
99
+ disposeStream(state);
100
+ readableController.error(error);
114
101
  throw error;
115
102
  }
116
103
  },
117
- transform(chunk, controller) {
104
+ close() {
105
+ if (cancelled) {
106
+ return;
107
+ }
118
108
  try {
119
- const buffer = chunk;
120
- const heap = new Uint8Array(memory.buffer);
121
- const process = this._process;
122
- const last_consumed = this._last_consumed;
123
- const out = this.out;
124
- const scratch = this._scratch;
125
- let offset = 0;
126
- while (offset < buffer.length) {
127
- const toRead = Math.min(buffer.length - offset, 32 * 1024);
128
- if (!this.in || this.inBufferSize < toRead) {
129
- if (this.in && free) {
130
- free(this.in);
131
- this.in = 0;
132
- }
133
- this.in = malloc(toRead);
134
- this.inBufferSize = toRead;
135
- if (!this.in) {
136
- throw new Error("allocation failed");
137
- }
138
- }
139
- heap.set(buffer.subarray(offset, offset + toRead), this.in);
140
- const result = process(this.streamHandle, this.in, toRead, out, outBufferSize, 0);
141
- // checked before the byte count is used, so a status code can never be read as one
142
- const code = (result >> 24) & 0xff;
143
- const signedCode = (code & 0x80) ? code - 256 : code;
144
- if (signedCode < 0) {
145
- throw new Error("process error:" + signedCode);
146
- }
147
- const prod = result & 0x00ffffff;
148
- if (prod) {
149
- scratch.set(heap.subarray(out, out + prod), 0);
150
- controller.enqueue(scratch.slice(0, prod));
151
- }
152
- const consumed = last_consumed(this.streamHandle);
153
- if (consumed === 0 && prod === 0) {
154
- break;
155
- }
156
- offset += consumed;
157
- }
109
+ flushStream();
158
110
  } catch (error) {
159
- disposeStream(this);
160
- controller.error(error);
111
+ disposeStream(state);
112
+ readableController.error(error);
113
+ throw error;
161
114
  }
115
+ const result = disposeStream(state);
116
+ if (result !== 0) {
117
+ const error = new Error("end error:" + result);
118
+ readableController.error(error);
119
+ throw error;
120
+ }
121
+ readableController.close();
162
122
  },
163
- flush(controller) {
164
- try {
165
- const heap = new Uint8Array(memory.buffer);
166
- const process = this._process;
167
- const out = this.out;
168
- const scratch = this._scratch;
169
- while (true) {
170
- const result = process(this.streamHandle, 0, 0, out, outBufferSize, 4);
171
- const code = (result >> 24) & 0xff;
172
- const signedCode = (code & 0x80) ? code - 256 : code;
173
- if (signedCode < 0) {
174
- throw new Error("process error:" + signedCode);
175
- }
176
- const produced = result & 0x00ffffff;
177
- if (produced) {
178
- scratch.set(heap.subarray(out, out + produced), 0);
179
- controller.enqueue(scratch.slice(0, produced));
180
- }
181
- if (code === 1 || produced === 0) {
182
- break;
123
+ abort(reason) {
124
+ disposeStream(state);
125
+ readableController.error(reason);
126
+ }
127
+ }, { highWaterMark: 1 });
128
+ return { readable, writable };
129
+
130
+ function initStream() {
131
+ try {
132
+ let result;
133
+ state.out = malloc(outBufferSize);
134
+ state.in = malloc(inBufferSize);
135
+ state.inBufferSize = inBufferSize;
136
+ if (!state.out || !state.in) {
137
+ throw new Error("allocation failed");
138
+ }
139
+ if (isCompress) {
140
+ state._process = wasm.deflate_process;
141
+ state._last_consumed = wasm.deflate_last_consumed;
142
+ state._end = wasm.deflate_end;
143
+ state.streamHandle = wasm.deflate_new();
144
+ if (type === FORMAT_GZIP) {
145
+ result = wasm.deflate_init_gzip(state.streamHandle, level);
146
+ } else if (type === FORMAT_DEFLATE_RAW) {
147
+ result = wasm.deflate_init_raw(state.streamHandle, level);
148
+ } else {
149
+ result = wasm.deflate_init(state.streamHandle, level);
150
+ }
151
+ } else {
152
+ if (type === FORMAT_DEFLATE64_RAW) {
153
+ state._process = wasm.inflate9_process;
154
+ state._last_consumed = wasm.inflate9_last_consumed;
155
+ state._end = wasm.inflate9_end;
156
+ state.streamHandle = wasm.inflate9_new();
157
+ result = wasm.inflate9_init_raw(state.streamHandle);
158
+ } else {
159
+ state._process = wasm.inflate_process;
160
+ state._last_consumed = wasm.inflate_last_consumed;
161
+ state._end = wasm.inflate_end;
162
+ state.streamHandle = wasm.inflate_new();
163
+ if (type === FORMAT_DEFLATE_RAW) {
164
+ result = wasm.inflate_init_raw(state.streamHandle);
165
+ } else if (type === FORMAT_GZIP) {
166
+ result = wasm.inflate_init_gzip(state.streamHandle);
167
+ } else {
168
+ result = wasm.inflate_init(state.streamHandle);
183
169
  }
184
170
  }
185
- } catch (error) {
186
- controller.error(error);
187
- } finally {
188
- const result = disposeStream(this);
189
- if (result !== 0) {
190
- controller.error(new Error("end error:" + result));
171
+ }
172
+ if (result !== 0) {
173
+ throw new Error("init failed:" + result);
174
+ }
175
+ } catch (error) {
176
+ disposeStream(state);
177
+ throw error;
178
+ }
179
+ }
180
+
181
+ function processChunk(chunk) {
182
+ const heap = new Uint8Array(memory.buffer);
183
+ const process = state._process;
184
+ const last_consumed = state._last_consumed;
185
+ const out = state.out;
186
+ let offset = 0;
187
+ while (offset < chunk.length) {
188
+ const toRead = Math.min(chunk.length - offset, 32 * 1024);
189
+ if (!state.in || state.inBufferSize < toRead) {
190
+ if (state.in && free) {
191
+ free(state.in);
192
+ state.in = 0;
193
+ }
194
+ state.in = malloc(toRead);
195
+ state.inBufferSize = toRead;
196
+ if (!state.in) {
197
+ throw new Error("allocation failed");
191
198
  }
192
199
  }
193
- },
194
- cancel() {
195
- // release the stream handle and buffers when the pipeline is aborted,
196
- // they would be leaked in the process-lifetime wasm heap otherwise
197
- disposeStream(this);
200
+ heap.set(chunk.subarray(offset, offset + toRead), state.in);
201
+ const result = process(state.streamHandle, state.in, toRead, out, outBufferSize, 0);
202
+ // checked before the byte count is used, so a status code can never be read as one
203
+ const code = (result >> 24) & 0xff;
204
+ const signedCode = (code & 0x80) ? code - 256 : code;
205
+ if (signedCode < 0) {
206
+ throw new Error("process error:" + signedCode);
207
+ }
208
+ const prod = result & 0x00ffffff;
209
+ if (prod) {
210
+ enqueue(heap.slice(out, out + prod));
211
+ }
212
+ const consumed = last_consumed(state.streamHandle);
213
+ if (consumed === 0 && prod === 0) {
214
+ break;
215
+ }
216
+ offset += consumed;
217
+ }
218
+ }
219
+
220
+ function flushStream() {
221
+ const heap = new Uint8Array(memory.buffer);
222
+ const process = state._process;
223
+ const out = state.out;
224
+ while (true) {
225
+ const result = process(state.streamHandle, 0, 0, out, outBufferSize, 4);
226
+ const code = (result >> 24) & 0xff;
227
+ const signedCode = (code & 0x80) ? code - 256 : code;
228
+ if (signedCode < 0) {
229
+ throw new Error("process error:" + signedCode);
230
+ }
231
+ const produced = result & 0x00ffffff;
232
+ if (produced) {
233
+ enqueue(heap.slice(out, out + produced));
234
+ }
235
+ if (code === 1 || produced === 0) {
236
+ break;
237
+ }
198
238
  }
199
- });
239
+ }
240
+
241
+ function enqueue(chunk) {
242
+ backpressure = true;
243
+ readableController.enqueue(chunk);
244
+ }
245
+
246
+ function resumeWrite() {
247
+ if (resolveWrite) {
248
+ const resolve = resolveWrite;
249
+ resolveWrite = null;
250
+ resolve();
251
+ }
252
+ }
200
253
 
201
254
  function disposeStream(state) {
202
255
  let endResult = 0;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "WASM-based Compression Streams API implementation using zlib, with support for deflate64 decompression.",
4
4
  "author": "Gildas Lormeau",
5
5
  "license": "BSD-3-Clause",
6
- "version": "1.2.1",
6
+ "version": "1.2.3",
7
7
  "type": "module",
8
8
  "keywords": [
9
9
  "deflate",
@@ -26,7 +26,7 @@
26
26
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
28
 
29
- /* global TransformStream */
29
+ /* global ReadableStream, WritableStream */
30
30
 
31
31
  const FORMAT_DEFLATE = "deflate";
32
32
  const FORMAT_DEFLATE_RAW = "deflate-raw";
@@ -61,142 +61,195 @@ function _make(isCompress, type, options = {}) {
61
61
  const level = (typeof options.level === "number") ? options.level : -1;
62
62
  const outBufferSize = (typeof options.outBuffer === "number") ? options.outBuffer : 64 * 1024;
63
63
  const inBufferSize = (typeof options.inBufferSize === "number") ? options.inBufferSize : 64 * 1024;
64
-
65
- return new TransformStream({
66
- start() {
64
+ const state = { out: 0, in: 0, inBufferSize: 0, streamHandle: 0 };
65
+ let readableController, writableController, resolveWrite, cancelReason;
66
+ let backpressure = true;
67
+ let cancelled = false;
68
+ initStream();
69
+ const readable = new ReadableStream({
70
+ start(controller) {
71
+ readableController = controller;
72
+ },
73
+ pull() {
74
+ backpressure = false;
75
+ resumeWrite();
76
+ },
77
+ cancel(reason) {
78
+ cancelled = true;
79
+ cancelReason = reason;
80
+ disposeStream(state);
81
+ writableController.error(reason);
82
+ resumeWrite();
83
+ }
84
+ }, { highWaterMark: 0 });
85
+ const writable = new WritableStream({
86
+ start(controller) {
87
+ writableController = controller;
88
+ },
89
+ async write(chunk) {
90
+ if (backpressure) {
91
+ await new Promise(resolve => resolveWrite = resolve);
92
+ }
93
+ if (cancelled) {
94
+ throw cancelReason;
95
+ }
67
96
  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");
74
- }
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
- }
88
- } else {
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);
95
- } else {
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
- }
107
- }
108
- }
109
- if (result !== 0) {
110
- throw new Error("init failed:" + result);
111
- }
97
+ processChunk(chunk);
112
98
  } catch (error) {
113
- disposeStream(this);
99
+ disposeStream(state);
100
+ readableController.error(error);
114
101
  throw error;
115
102
  }
116
103
  },
117
- transform(chunk, controller) {
104
+ close() {
105
+ if (cancelled) {
106
+ return;
107
+ }
118
108
  try {
119
- const buffer = chunk;
120
- const heap = new Uint8Array(memory.buffer);
121
- const process = this._process;
122
- const last_consumed = this._last_consumed;
123
- const out = this.out;
124
- const scratch = this._scratch;
125
- let offset = 0;
126
- while (offset < buffer.length) {
127
- const toRead = Math.min(buffer.length - offset, 32 * 1024);
128
- if (!this.in || this.inBufferSize < toRead) {
129
- if (this.in && free) {
130
- free(this.in);
131
- this.in = 0;
132
- }
133
- this.in = malloc(toRead);
134
- this.inBufferSize = toRead;
135
- if (!this.in) {
136
- throw new Error("allocation failed");
137
- }
138
- }
139
- heap.set(buffer.subarray(offset, offset + toRead), this.in);
140
- const result = process(this.streamHandle, this.in, toRead, out, outBufferSize, 0);
141
- // checked before the byte count is used, so a status code can never be read as one
142
- const code = (result >> 24) & 0xff;
143
- const signedCode = (code & 0x80) ? code - 256 : code;
144
- if (signedCode < 0) {
145
- throw new Error("process error:" + signedCode);
146
- }
147
- const prod = result & 0x00ffffff;
148
- if (prod) {
149
- scratch.set(heap.subarray(out, out + prod), 0);
150
- controller.enqueue(scratch.slice(0, prod));
151
- }
152
- const consumed = last_consumed(this.streamHandle);
153
- if (consumed === 0 && prod === 0) {
154
- break;
155
- }
156
- offset += consumed;
157
- }
109
+ flushStream();
158
110
  } catch (error) {
159
- disposeStream(this);
160
- controller.error(error);
111
+ disposeStream(state);
112
+ readableController.error(error);
113
+ throw error;
161
114
  }
115
+ const result = disposeStream(state);
116
+ if (result !== 0) {
117
+ const error = new Error("end error:" + result);
118
+ readableController.error(error);
119
+ throw error;
120
+ }
121
+ readableController.close();
162
122
  },
163
- flush(controller) {
164
- try {
165
- const heap = new Uint8Array(memory.buffer);
166
- const process = this._process;
167
- const out = this.out;
168
- const scratch = this._scratch;
169
- while (true) {
170
- const result = process(this.streamHandle, 0, 0, out, outBufferSize, 4);
171
- const code = (result >> 24) & 0xff;
172
- const signedCode = (code & 0x80) ? code - 256 : code;
173
- if (signedCode < 0) {
174
- throw new Error("process error:" + signedCode);
175
- }
176
- const produced = result & 0x00ffffff;
177
- if (produced) {
178
- scratch.set(heap.subarray(out, out + produced), 0);
179
- controller.enqueue(scratch.slice(0, produced));
180
- }
181
- if (code === 1 || produced === 0) {
182
- break;
123
+ abort(reason) {
124
+ disposeStream(state);
125
+ readableController.error(reason);
126
+ }
127
+ }, { highWaterMark: 1 });
128
+ return { readable, writable };
129
+
130
+ function initStream() {
131
+ try {
132
+ let result;
133
+ state.out = malloc(outBufferSize);
134
+ state.in = malloc(inBufferSize);
135
+ state.inBufferSize = inBufferSize;
136
+ if (!state.out || !state.in) {
137
+ throw new Error("allocation failed");
138
+ }
139
+ if (isCompress) {
140
+ state._process = wasm.deflate_process;
141
+ state._last_consumed = wasm.deflate_last_consumed;
142
+ state._end = wasm.deflate_end;
143
+ state.streamHandle = wasm.deflate_new();
144
+ if (type === FORMAT_GZIP) {
145
+ result = wasm.deflate_init_gzip(state.streamHandle, level);
146
+ } else if (type === FORMAT_DEFLATE_RAW) {
147
+ result = wasm.deflate_init_raw(state.streamHandle, level);
148
+ } else {
149
+ result = wasm.deflate_init(state.streamHandle, level);
150
+ }
151
+ } else {
152
+ if (type === FORMAT_DEFLATE64_RAW) {
153
+ state._process = wasm.inflate9_process;
154
+ state._last_consumed = wasm.inflate9_last_consumed;
155
+ state._end = wasm.inflate9_end;
156
+ state.streamHandle = wasm.inflate9_new();
157
+ result = wasm.inflate9_init_raw(state.streamHandle);
158
+ } else {
159
+ state._process = wasm.inflate_process;
160
+ state._last_consumed = wasm.inflate_last_consumed;
161
+ state._end = wasm.inflate_end;
162
+ state.streamHandle = wasm.inflate_new();
163
+ if (type === FORMAT_DEFLATE_RAW) {
164
+ result = wasm.inflate_init_raw(state.streamHandle);
165
+ } else if (type === FORMAT_GZIP) {
166
+ result = wasm.inflate_init_gzip(state.streamHandle);
167
+ } else {
168
+ result = wasm.inflate_init(state.streamHandle);
183
169
  }
184
170
  }
185
- } catch (error) {
186
- controller.error(error);
187
- } finally {
188
- const result = disposeStream(this);
189
- if (result !== 0) {
190
- controller.error(new Error("end error:" + result));
171
+ }
172
+ if (result !== 0) {
173
+ throw new Error("init failed:" + result);
174
+ }
175
+ } catch (error) {
176
+ disposeStream(state);
177
+ throw error;
178
+ }
179
+ }
180
+
181
+ function processChunk(chunk) {
182
+ const heap = new Uint8Array(memory.buffer);
183
+ const process = state._process;
184
+ const last_consumed = state._last_consumed;
185
+ const out = state.out;
186
+ let offset = 0;
187
+ while (offset < chunk.length) {
188
+ const toRead = Math.min(chunk.length - offset, 32 * 1024);
189
+ if (!state.in || state.inBufferSize < toRead) {
190
+ if (state.in && free) {
191
+ free(state.in);
192
+ state.in = 0;
193
+ }
194
+ state.in = malloc(toRead);
195
+ state.inBufferSize = toRead;
196
+ if (!state.in) {
197
+ throw new Error("allocation failed");
191
198
  }
192
199
  }
193
- },
194
- cancel() {
195
- // release the stream handle and buffers when the pipeline is aborted,
196
- // they would be leaked in the process-lifetime wasm heap otherwise
197
- disposeStream(this);
200
+ heap.set(chunk.subarray(offset, offset + toRead), state.in);
201
+ const result = process(state.streamHandle, state.in, toRead, out, outBufferSize, 0);
202
+ // checked before the byte count is used, so a status code can never be read as one
203
+ const code = (result >> 24) & 0xff;
204
+ const signedCode = (code & 0x80) ? code - 256 : code;
205
+ if (signedCode < 0) {
206
+ throw new Error("process error:" + signedCode);
207
+ }
208
+ const prod = result & 0x00ffffff;
209
+ if (prod) {
210
+ enqueue(heap.slice(out, out + prod));
211
+ }
212
+ const consumed = last_consumed(state.streamHandle);
213
+ if (consumed === 0 && prod === 0) {
214
+ break;
215
+ }
216
+ offset += consumed;
217
+ }
218
+ }
219
+
220
+ function flushStream() {
221
+ const heap = new Uint8Array(memory.buffer);
222
+ const process = state._process;
223
+ const out = state.out;
224
+ while (true) {
225
+ const result = process(state.streamHandle, 0, 0, out, outBufferSize, 4);
226
+ const code = (result >> 24) & 0xff;
227
+ const signedCode = (code & 0x80) ? code - 256 : code;
228
+ if (signedCode < 0) {
229
+ throw new Error("process error:" + signedCode);
230
+ }
231
+ const produced = result & 0x00ffffff;
232
+ if (produced) {
233
+ enqueue(heap.slice(out, out + produced));
234
+ }
235
+ if (code === 1 || produced === 0) {
236
+ break;
237
+ }
198
238
  }
199
- });
239
+ }
240
+
241
+ function enqueue(chunk) {
242
+ backpressure = true;
243
+ readableController.enqueue(chunk);
244
+ }
245
+
246
+ function resumeWrite() {
247
+ if (resolveWrite) {
248
+ const resolve = resolveWrite;
249
+ resolveWrite = null;
250
+ resolve();
251
+ }
252
+ }
200
253
 
201
254
  function disposeStream(state) {
202
255
  let endResult = 0;