zlib-streams 1.0.7 → 1.1.1

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.
Files changed (108) hide show
  1. package/Makefile +23 -8
  2. package/dist/zlib-streams-dev.wasm +0 -0
  3. package/dist/zlib-streams.js +145 -75
  4. package/dist/zlib-streams.wasm +0 -0
  5. package/package.json +2 -2
  6. package/src/README.md +61 -1
  7. package/src/chunkcopy.h +565 -0
  8. package/src/compare256.h +56 -0
  9. package/src/cpu_features.h +22 -0
  10. package/src/deflate.c +2349 -0
  11. package/src/deflate.h +420 -0
  12. package/src/deflate_crc32.c +22 -0
  13. package/src/inffast.h +26 -0
  14. package/src/{inffast.c → inffast_chunk.c} +127 -71
  15. package/src/inffast_chunk.h +42 -0
  16. package/src/inflate.c +69 -33
  17. package/src/insert_string.h +80 -0
  18. package/src/wasm/api/zlib-streams.js +145 -75
  19. package/src/wasm/wasm_stream_common.c +8 -4
  20. package/build/debug/src/inffast.d +0 -8
  21. package/build/debug/src/inffast.o +0 -0
  22. package/build/debug/src/inflate.d +0 -8
  23. package/build/debug/src/inflate.o +0 -0
  24. package/build/debug/src/inftrees.d +0 -6
  25. package/build/debug/src/inftrees.o +0 -0
  26. package/build/debug/src/zlib/adler32.d +0 -5
  27. package/build/debug/src/zlib/adler32.o +0 -0
  28. package/build/debug/src/zlib/contrib/infback9/infback9.d +0 -13
  29. package/build/debug/src/zlib/contrib/infback9/infback9.o +0 -0
  30. package/build/debug/src/zlib/contrib/infback9/inftree9.d +0 -7
  31. package/build/debug/src/zlib/contrib/infback9/inftree9.o +0 -0
  32. package/build/debug/src/zlib/crc32.d +0 -6
  33. package/build/debug/src/zlib/crc32.o +0 -0
  34. package/build/debug/src/zlib/trees.d +0 -7
  35. package/build/debug/src/zlib/trees.o +0 -0
  36. package/build/debug/src/zlib/zutil.d +0 -6
  37. package/build/debug/src/zlib/zutil.o +0 -0
  38. package/tmp/all_runs/payload_decompress_nowindow_debug__100k_lines.deflate64.log +0 -34
  39. package/tmp/all_runs/payload_decompress_nowindow_debug__100k_lines.deflate64.out +0 -100000
  40. package/tmp/all_runs/payload_decompress_nowindow_debug__10k_lines.deflate64.log +0 -2
  41. package/tmp/all_runs/payload_decompress_nowindow_debug__10k_lines.deflate64.out +0 -10000
  42. package/tmp/all_runs/payload_decompress_nowindow_debug__payload_63k.deflate64.log +0 -1
  43. package/tmp/all_runs/payload_decompress_nowindow_debug__payload_63k.deflate64.out +0 -0
  44. package/tmp/all_runs/payload_decompress_nowindow_debug__payload_64k.deflate64.log +0 -1
  45. package/tmp/all_runs/payload_decompress_nowindow_debug__payload_64k.deflate64.out +0 -0
  46. package/tmp/all_runs/payload_decompress_nowindow_debug__payload_65k.deflate64.log +0 -2
  47. package/tmp/all_runs/payload_decompress_nowindow_debug__payload_65k.deflate64.out +0 -0
  48. package/tmp/all_runs/payload_decompress_nowindow_debug__rand_block_gap.deflate64.log +0 -1
  49. package/tmp/all_runs/payload_decompress_nowindow_debug__rand_block_gap.deflate64.out +0 -0
  50. package/tmp/all_runs/payload_decompress_nowindow_debug__repeat_63k.deflate64.log +0 -1
  51. package/tmp/all_runs/payload_decompress_nowindow_debug__repeat_63k.deflate64.out +0 -0
  52. package/tmp/all_runs/payload_decompress_nowindow_debug__repeat_64k.deflate64.log +0 -1
  53. package/tmp/all_runs/payload_decompress_nowindow_debug__repeat_64k.deflate64.out +0 -0
  54. package/tmp/all_runs/payload_decompress_nowindow_debug__repeat_65k.deflate64.log +0 -2
  55. package/tmp/all_runs/payload_decompress_nowindow_debug__repeat_65k.deflate64.out +0 -0
  56. package/tmp/all_runs/payload_decompress_nowindow_debug__zeros_100k.deflate64.log +0 -2
  57. package/tmp/all_runs/payload_decompress_nowindow_debug__zeros_100k.deflate64.out +0 -0
  58. package/tmp/all_runs/payload_decompress_ref_debug__100k_lines.deflate64.log +0 -1
  59. package/tmp/all_runs/payload_decompress_ref_debug__100k_lines.deflate64.out +0 -100000
  60. package/tmp/all_runs/payload_decompress_ref_debug__10k_lines.deflate64.log +0 -1
  61. package/tmp/all_runs/payload_decompress_ref_debug__10k_lines.deflate64.out +0 -10000
  62. package/tmp/all_runs/payload_decompress_ref_debug__payload_63k.deflate64.log +0 -1
  63. package/tmp/all_runs/payload_decompress_ref_debug__payload_63k.deflate64.out +0 -0
  64. package/tmp/all_runs/payload_decompress_ref_debug__payload_64k.deflate64.log +0 -1
  65. package/tmp/all_runs/payload_decompress_ref_debug__payload_64k.deflate64.out +0 -0
  66. package/tmp/all_runs/payload_decompress_ref_debug__payload_65k.deflate64.log +0 -1
  67. package/tmp/all_runs/payload_decompress_ref_debug__payload_65k.deflate64.out +0 -0
  68. package/tmp/all_runs/payload_decompress_ref_debug__rand_block_gap.deflate64.log +0 -1
  69. package/tmp/all_runs/payload_decompress_ref_debug__rand_block_gap.deflate64.out +0 -0
  70. package/tmp/all_runs/payload_decompress_ref_debug__repeat_63k.deflate64.log +0 -1
  71. package/tmp/all_runs/payload_decompress_ref_debug__repeat_63k.deflate64.out +0 -0
  72. package/tmp/all_runs/payload_decompress_ref_debug__repeat_64k.deflate64.log +0 -1
  73. package/tmp/all_runs/payload_decompress_ref_debug__repeat_64k.deflate64.out +0 -0
  74. package/tmp/all_runs/payload_decompress_ref_debug__repeat_65k.deflate64.log +0 -1
  75. package/tmp/all_runs/payload_decompress_ref_debug__repeat_65k.deflate64.out +0 -0
  76. package/tmp/all_runs/payload_decompress_ref_debug__zeros_100k.deflate64.log +0 -1
  77. package/tmp/all_runs/payload_decompress_ref_debug__zeros_100k.deflate64.out +0 -0
  78. package/tmp/all_runs/payload_decompress_test_debug__100k_lines.deflate64.log +0 -34
  79. package/tmp/all_runs/payload_decompress_test_debug__100k_lines.deflate64.out +0 -100000
  80. package/tmp/all_runs/payload_decompress_test_debug__10k_lines.deflate64.log +0 -2
  81. package/tmp/all_runs/payload_decompress_test_debug__10k_lines.deflate64.out +0 -10000
  82. package/tmp/all_runs/payload_decompress_test_debug__payload_63k.deflate64.log +0 -1
  83. package/tmp/all_runs/payload_decompress_test_debug__payload_63k.deflate64.out +0 -0
  84. package/tmp/all_runs/payload_decompress_test_debug__payload_64k.deflate64.log +0 -1
  85. package/tmp/all_runs/payload_decompress_test_debug__payload_64k.deflate64.out +0 -0
  86. package/tmp/all_runs/payload_decompress_test_debug__payload_65k.deflate64.log +0 -2
  87. package/tmp/all_runs/payload_decompress_test_debug__payload_65k.deflate64.out +0 -0
  88. package/tmp/all_runs/payload_decompress_test_debug__rand_block_gap.deflate64.log +0 -1
  89. package/tmp/all_runs/payload_decompress_test_debug__rand_block_gap.deflate64.out +0 -0
  90. package/tmp/all_runs/payload_decompress_test_debug__repeat_63k.deflate64.log +0 -1
  91. package/tmp/all_runs/payload_decompress_test_debug__repeat_63k.deflate64.out +0 -0
  92. package/tmp/all_runs/payload_decompress_test_debug__repeat_64k.deflate64.log +0 -1
  93. package/tmp/all_runs/payload_decompress_test_debug__repeat_64k.deflate64.out +0 -0
  94. package/tmp/all_runs/payload_decompress_test_debug__repeat_65k.deflate64.log +0 -2
  95. package/tmp/all_runs/payload_decompress_test_debug__repeat_65k.deflate64.out +0 -0
  96. package/tmp/all_runs/payload_decompress_test_debug__zeros_100k.deflate64.log +0 -2
  97. package/tmp/all_runs/payload_decompress_test_debug__zeros_100k.deflate64.out +0 -0
  98. package/tmp/all_runs/wasm__100k_lines.deflate64.out +0 -100000
  99. package/tmp/all_runs/wasm__10k_lines.deflate64.out +0 -10000
  100. package/tmp/all_runs/wasm__payload_63k.deflate64.out +0 -0
  101. package/tmp/all_runs/wasm__payload_64k.deflate64.out +0 -0
  102. package/tmp/all_runs/wasm__payload_65k.deflate64.out +0 -0
  103. package/tmp/all_runs/wasm__rand_block_gap.deflate64.out +0 -0
  104. package/tmp/all_runs/wasm__repeat_63k.deflate64.out +0 -0
  105. package/tmp/all_runs/wasm__repeat_64k.deflate64.out +0 -0
  106. package/tmp/all_runs/wasm__repeat_65k.deflate64.out +0 -0
  107. package/tmp/all_runs/wasm__zeros_100k.deflate64.out +0 -0
  108. package/tmp/diagnose/diagnose_stream_mem.json +0 -4202
package/Makefile CHANGED
@@ -1,6 +1,11 @@
1
1
  CC=gcc
2
- CFLAGS=-I./src/zlib -I./src -I./test -I./src/zlib/contrib/infback9 -O2 -w -MMD -MP
3
- DEBUG_CFLAGS=-I./src/zlib -I./src -I./test -I./src/zlib/contrib/infback9 -O0 -g3 -ggdb3 -fno-inline -fno-omit-frame-pointer -w -MMD -MP
2
+ # INFLATE_CHUNK_GENERIC selects the portable chunk copies in chunkcopy.h, which has
3
+ # no wasm SIMD path and would #error without one of its three arch macros.
4
+ # INFLATE_CHUNK_READ_64LE buffers the input bits in a uint64_t inside inffast_chunk.c
5
+ # only; state->hold stays unsigned long. Both are needed wherever inflate.c is built.
6
+ INFLATE_CHUNK_CFLAGS = -DINFLATE_CHUNK_GENERIC -DINFLATE_CHUNK_READ_64LE
7
+ CFLAGS=-I./src/zlib -I./src -I./test -I./src/zlib/contrib/infback9 -O2 -w -MMD -MP $(INFLATE_CHUNK_CFLAGS)
8
+ DEBUG_CFLAGS=-I./src/zlib -I./src -I./test -I./src/zlib/contrib/infback9 -O0 -g3 -ggdb3 -fno-inline -fno-omit-frame-pointer -w -MMD -MP $(INFLATE_CHUNK_CFLAGS)
4
9
  ## By default we don't enable the INF9 tracing macros so debug binaries
5
10
  ## don't embed large diagnostic strings. To enable tracing for local
6
11
  ## debugging, uncomment and set DEBUG_DEFINES accordingly.
@@ -9,17 +14,17 @@ DEBUG_CFLAGS=-I./src/zlib -I./src -I./test -I./src/zlib/contrib/infback9 -O0 -g3
9
14
  ## Debug defines are disabled by default for a clean commit-ready Makefile.
10
15
  DEBUG_DEFINES=
11
16
  DEBUG_DEFINES_TRACED=
12
- SRC=src/inflate.c src/inffast.c src/inftrees.c src/zlib/zutil.c
17
+ SRC=src/inflate.c src/inffast_chunk.c src/inftrees.c src/zlib/zutil.c
13
18
  TESTSRC=zlib/crc32.c
14
19
  LIBS=-lz
15
20
 
16
21
  ## Build the payload decompressor test binary used for verification (deduplicated sources)
17
22
  PD_SRCS = test/payload_decompress.c \
18
- src/inflate.c src/inffast.c src/inftrees.c src/zlib/zutil.c \
23
+ src/inflate.c src/inffast_chunk.c src/inftrees.c src/zlib/zutil.c \
19
24
  src/zlib/crc32.c src/zlib/adler32.c
20
25
 
21
26
  PD_NOWINDOW_SRCS = test/payload_decompress_nowindow.c \
22
- src/inflate.c src/inffast.c src/inftrees.c src/zlib/zutil.c \
27
+ src/inflate.c src/inffast_chunk.c src/inftrees.c src/zlib/zutil.c \
23
28
  src/zlib/crc32.c src/zlib/adler32.c
24
29
 
25
30
  PD_NOWINDOW_DEBUG_OBJS = $(PD_NOWINDOW_SRCS:%.c=build/debug/%.o)
@@ -131,14 +136,19 @@ EMCC ?= emsdk/upstream/emscripten/emcc
131
136
  WASM_OPT ?= emsdk/upstream/bin/wasm-opt
132
137
 
133
138
  WASM_SRCS = src/wasm/inflate9_stream_wasm.c src/wasm/inflate_stream_wasm.c src/wasm/deflate_stream_wasm.c src/wasm/wasm_stream_common.c src/wasm/allocator.c \
134
- src/inflate.c src/inffast.c src/inftrees.c src/zlib/zutil.c \
135
- src/zlib/crc32.c src/zlib/adler32.c src/trees.c src/zlib/deflate.c
139
+ src/inflate.c src/inffast_chunk.c src/inftrees.c src/zlib/zutil.c \
140
+ src/zlib/crc32.c src/zlib/adler32.c src/trees.c src/deflate.c src/deflate_crc32.c
136
141
  # CRC-32: -DZ_SOLO suppresses zlib's Z_U4/Z_U8 word types, which makes crc32.c's braid
137
142
  # path (#elif defined(Z_U4)) fall back to a byte-at-a-time loop. Restore the types and
138
143
  # force the 8-byte braid (Z_TESTW=8) so the slicing/braided CRC compiles for wasm:
139
144
  # ~12x faster CRC (342 -> ~4100 MB/s), bit-identical output. wasm32 has native i64.
140
145
  WASM_CRC_CFLAGS = -DZ_U4=unsigned -DZ_U8='unsigned long long' -DZ_TESTW=8
141
- WASM_CFLAGS = -Isrc -Isrc/zlib -Isrc/zlib/contrib/infback9 -O2 -flto -DDYNAMIC_CRC_TABLE -DBUILDFIXED -DZ_SOLO $(WASM_CRC_CFLAGS)
146
+ # DEFLATE_COMPARE256_64LE extends matches 8 bytes at a time in longest_match, which is
147
+ # the same match length computed faster: ~7% at every level, byte-identical output.
148
+ # USE_ZLIB_RABIN_KARP_ROLLING_HASH turns Chromium's own hash OFF so the deflate stream
149
+ # stays bit-for-bit what madler's deflate.c produces.
150
+ WASM_DEFLATE_CFLAGS = -DDEFLATE_COMPARE256_64LE -DUSE_ZLIB_RABIN_KARP_ROLLING_HASH
151
+ WASM_CFLAGS = -Isrc -Isrc/zlib -Isrc/zlib/contrib/infback9 -O2 -flto -DDYNAMIC_CRC_TABLE -DBUILDFIXED -DZ_SOLO $(WASM_CRC_CFLAGS) $(WASM_DEFLATE_CFLAGS) $(INFLATE_CHUNK_CFLAGS)
142
152
 
143
153
  .PHONY: wasm
144
154
  wasm: dist/zlib-streams-dev.wasm
@@ -279,6 +289,11 @@ run_all_tests: dist/zlib-streams-dev.wasm
279
289
  @node src/wasm/tests/run_roundtrip_cli.js deflate 24000 dist/zlib-streams-dev.wasm
280
290
  @$(MAKE) run_transform_roundtrip
281
291
  @$(MAKE) test_decompressionstream_inflate9
292
+ # Output buffer filling before any input is consumed, including a deflate64
293
+ # match longer than the buffer, which no encoder we have can produce
294
+ @node src/wasm/tests/test_output_buffer_boundary.js dist/zlib-streams-dev.wasm
295
+ # Error codes packed the same way as a success, on both directions
296
+ @node src/wasm/tests/test_process_error_encoding.js dist/zlib-streams-dev.wasm
282
297
  @echo "Completed run_all_tests"
283
298
 
284
299
  dist/zlib-streams-dev.wasm: $(WASM_SRCS)
Binary file
@@ -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,40 +128,35 @@ 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);
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
+ }
84
147
  const prod = result & 0x00ffffff;
85
148
  if (prod) {
86
149
  scratch.set(heap.subarray(out, out + prod), 0);
87
150
  controller.enqueue(scratch.slice(0, prod));
88
151
  }
89
- if (!isCompress) {
90
- const code = (result >> 24) & 0xff;
91
- const signedCode = (code & 0x80) ? code - 256 : code;
92
- if (signedCode < 0) {
93
- throw new Error("process error:" + signedCode);
94
- }
95
- }
96
152
  const consumed = last_consumed(this.streamHandle);
97
- if (consumed === 0) {
153
+ if (consumed === 0 && prod === 0) {
98
154
  break;
99
155
  }
100
156
  offset += consumed;
101
157
  }
102
158
  } 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
- }
159
+ disposeStream(this);
112
160
  controller.error(error);
113
161
  }
114
162
  },
@@ -120,14 +168,12 @@ function _make(isCompress, type, options = {}) {
120
168
  const scratch = this._scratch;
121
169
  while (true) {
122
170
  const result = process(this.streamHandle, 0, 0, out, outBufferSize, 4);
123
- const produced = result & 0x00ffffff;
124
171
  const code = (result >> 24) & 0xff;
125
- if (!isCompress) {
126
- const signedCode = (code & 0x80) ? code - 256 : code;
127
- if (signedCode < 0) {
128
- throw new Error("process error:" + signedCode);
129
- }
172
+ const signedCode = (code & 0x80) ? code - 256 : code;
173
+ if (signedCode < 0) {
174
+ throw new Error("process error:" + signedCode);
130
175
  }
176
+ const produced = result & 0x00ffffff;
131
177
  if (produced) {
132
178
  scratch.set(heap.subarray(out, out + produced), 0);
133
179
  controller.enqueue(scratch.slice(0, produced));
@@ -139,30 +185,54 @@ function _make(isCompress, type, options = {}) {
139
185
  } catch (error) {
140
186
  controller.error(error);
141
187
  } 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);
188
+ const result = disposeStream(this);
189
+ if (result !== 0) {
190
+ controller.error(new Error("end error:" + result));
153
191
  }
154
192
  }
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);
155
198
  }
156
199
  });
200
+
201
+ function disposeStream(state) {
202
+ let endResult = 0;
203
+ if (state.streamHandle && state._end) {
204
+ endResult = state._end(state.streamHandle);
205
+ }
206
+ state.streamHandle = 0;
207
+ if (state.in && free) {
208
+ free(state.in);
209
+ }
210
+ state.in = 0;
211
+ if (state.out && free) {
212
+ free(state.out);
213
+ }
214
+ state.out = 0;
215
+ return endResult;
216
+ }
157
217
  }
158
218
 
159
219
  export class CompressionStreamZlib {
160
- constructor(type = "deflate", options) {
220
+ constructor(type = FORMAT_DEFLATE, options) {
161
221
  return _make(true, type, options);
162
222
  }
163
223
  }
164
224
  export class DecompressionStreamZlib {
165
- constructor(type = "deflate", options) {
225
+ constructor(type = FORMAT_DEFLATE, options) {
166
226
  return _make(false, type, options);
167
227
  }
168
- }
228
+ }
229
+ // These codecs are backed by the WASM module; they are unusable until setWasmExports() has run.
230
+ // The worker uses this flag to know it must fall back to the native CompressionStream when the
231
+ // module fails to load, rather than discarding a self-contained codec supplied through config.
232
+ CompressionStreamZlib.requiresModule = true;
233
+ DecompressionStreamZlib.requiresModule = true;
234
+ // Constructing these classes before the module is loaded throws, so capability probes cannot rely
235
+ // on trying the constructor; the formats are declared instead, next to the branches implementing
236
+ // them in _make().
237
+ CompressionStreamZlib.supportedFormats = [FORMAT_DEFLATE, FORMAT_DEFLATE_RAW, FORMAT_GZIP];
238
+ DecompressionStreamZlib.supportedFormats = [FORMAT_DEFLATE, FORMAT_DEFLATE_RAW, FORMAT_GZIP, FORMAT_DEFLATE64_RAW];
Binary file
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.0.7",
6
+ "version": "1.1.1",
7
7
  "type": "module",
8
8
  "keywords": [
9
9
  "deflate",
@@ -28,4 +28,4 @@
28
28
  "publishConfig": {
29
29
  "access": "public"
30
30
  }
31
- }
31
+ }
package/src/README.md CHANGED
@@ -1 +1,61 @@
1
- See https://github.com/gildas-lormeau/inflate9.c
1
+ # Sources
2
+
3
+ `src/zlib` is a submodule of [madler/zlib](https://github.com/madler/zlib) and is never edited. The
4
+ files directly under `src/` are forked copies, compiled instead of their submodule counterparts. The
5
+ deflate64 work is described in <https://github.com/gildas-lormeau/inflate9.c>.
6
+
7
+ ## Forked from madler/zlib
8
+
9
+ | file | why it is forked |
10
+ |---|---|
11
+ | `inftrees.c`, `inftrees.h` | deflate64 length code 285 and distance codes 30 and 31 |
12
+ | `trees.c` | builds the static deflate trees at run time instead of embedding them |
13
+
14
+ ## Forked from Chromium's zlib
15
+
16
+ `deflate.c`, `deflate.h`, `compare256.h`, `insert_string.h`, `cpu_features.h`, `inflate.c`,
17
+ `inffast.h`, `inffast_chunk.c`, `inffast_chunk.h` and `chunkcopy.h` come from
18
+ <https://chromium.googlesource.com/chromium/src/third_party/zlib>, revision
19
+ `285e94b8fa95ad3b7d16b80798ec8dce6febb8c8`. They are BSD-licensed, see the headers in the files.
20
+ `inflate.c`, `inffast_chunk.*` and `chunkcopy.h` are their `contrib/optimizations` versions.
21
+
22
+ `inflate.c` still carries the deflate64 support on top. That merge is small because the two are
23
+ orthogonal: deflate64 never enters `inflate_fast`, so the chunked fast path does not know about it.
24
+
25
+ ### Build options
26
+
27
+ Deflate, in `WASM_DEFLATE_CFLAGS`:
28
+
29
+ - `DEFLATE_COMPARE256_64LE` extends matches 8 bytes at a time in `longest_match`. Same match length,
30
+ computed faster. About 7% at every level.
31
+ - `USE_ZLIB_RABIN_KARP_ROLLING_HASH` turns Chromium's own hash off, so the deflate stream stays
32
+ bit-for-bit what madler's `deflate.c` produces. Dropping this flag enables the faster ANZAC hash,
33
+ which is valid DEFLATE but different bytes.
34
+
35
+ Inflate, in `INFLATE_CHUNK_CFLAGS`, needed by every target that builds `inflate.c`, not only the
36
+ wasm one:
37
+
38
+ - `INFLATE_CHUNK_GENERIC` selects the portable chunk copies. `chunkcopy.h` has no wasm SIMD path and
39
+ `#error`s without one of its three arch macros. `-msimd128` was measured and buys nothing.
40
+ - `INFLATE_CHUNK_READ_64LE` buffers the input bits in a `uint64_t` inside `inffast_chunk.c` only.
41
+ `state->hold` stays `unsigned long`. Together they are worth about 20%.
42
+
43
+ ### Deliberate edits, to re-apply when the Chromium files are updated
44
+
45
+ 1. `deflate.c`, `inflate.c` and `inffast_chunk.c` include `"insert_string.h"`, `"inffast_chunk.h"`
46
+ and `"chunkcopy.h"` directly, not through `contrib/optimizations/`, because this repo keeps
47
+ forked zlib sources flat.
48
+ 2. `deflate.h` leaves `LIT_MEM` off, as madler does. Chromium turns it on, which costs 16 KB of
49
+ `pending_buf` per deflate stream for about 1.5% of speed. The wasm heap is fixed at 16.9 MB, so
50
+ that is 62 concurrent deflate streams instead of 59.
51
+ 3. `deflate.h` casts the arguments of `zmemcpy` and `zmemzero`. Chromium's `zutil.h` declares them
52
+ taking `void *`, madler's takes `Bytef *`, and this build keeps madler's.
53
+ 4. `inflate.c` keeps the 9-bit and 6-bit root tables instead of Chromium's 10 and 9. Theirs need
54
+ `ENOUGH_LENS` raised from 852 to 1332, which is 1920 more bytes per inflate state and a fifth of
55
+ the concurrent inflate streams, for about 1% of speed. Without that change to `inftrees.h` they
56
+ fail every dynamic block with "invalid literal/lengths set".
57
+ 5. `chunkcopy.h` includes `<stddef.h>` for `ptrdiff_t`, which `-DZ_SOLO` keeps `zutil.h` from
58
+ pulling in.
59
+
60
+ `deflate_crc32.c` supplies `crc_reset`, `crc_finalize` and `copy_with_crc`. Chromium's `crc32.c` owns
61
+ them and `deflate.c` calls them on the gzip path, but this build keeps madler's `crc32.c`.