zlib-streams 1.0.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/.clang-format +3 -0
- package/.gitmodules +3 -0
- package/.prettierignore +5 -0
- package/Makefile +441 -0
- package/README.md +35 -0
- package/dist/zlib-streams.js +157 -0
- package/dist/zlib-streams.wasm +0 -0
- package/package.json +31 -0
- package/src/inflate9.c +725 -0
- package/src/inflate9.h +16 -0
- package/src/wasm/api/zlib-streams.js +157 -0
- package/src/wasm/deflate_stream_wasm.c +103 -0
- package/src/wasm/inflate9_stream_wasm.c +89 -0
- package/src/wasm/inflate_stream_wasm.c +99 -0
package/.clang-format
ADDED
package/.gitmodules
ADDED
package/.prettierignore
ADDED
package/Makefile
ADDED
|
@@ -0,0 +1,441 @@
|
|
|
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
|
|
4
|
+
## By default we don't enable the INF9 tracing macros so debug binaries
|
|
5
|
+
## don't embed large diagnostic strings. To enable tracing for local
|
|
6
|
+
## debugging, uncomment and set DEBUG_DEFINES accordingly.
|
|
7
|
+
# Enable zlib's Tracev/Tracevv debug output and inf9 trace hooks for debug builds
|
|
8
|
+
# For traced wasm runs we enable INF9_TRACE via DEBUG_DEFINES_TRACED (used by the traced-wasm target)
|
|
9
|
+
## Debug defines are disabled by default for a clean commit-ready Makefile.
|
|
10
|
+
DEBUG_DEFINES=
|
|
11
|
+
DEBUG_DEFINES_TRACED=
|
|
12
|
+
SRC=src/inflate9.c src/zlib/contrib/infback9/inftree9.c src/zlib/zutil.c src/zlib/inflate.c src/zlib/inftrees.c
|
|
13
|
+
TESTSRC=zlib/crc32.c
|
|
14
|
+
LIBS=-lz
|
|
15
|
+
|
|
16
|
+
## Build the payload decompressor test binary used for verification (deduplicated sources)
|
|
17
|
+
PD_SRCS = test/payload_decompress.c \
|
|
18
|
+
src/inflate9.c src/zlib/inffast.c src/zlib/inftrees.c src/zlib/zutil.c src/zlib/inflate.c src/zlib/infback.c \
|
|
19
|
+
src/zlib/contrib/infback9/infback9.c src/zlib/contrib/infback9/inftree9.c src/zlib/crc32.c src/zlib/adler32.c src/zlib/trees.c
|
|
20
|
+
|
|
21
|
+
PD_NOWINDOW_SRCS = test/payload_decompress_nowindow.c \
|
|
22
|
+
src/inflate9.c src/zlib/inffast.c src/zlib/inftrees.c src/zlib/zutil.c src/zlib/inflate.c src/zlib/infback.c \
|
|
23
|
+
src/zlib/contrib/infback9/infback9.c src/zlib/contrib/infback9/inftree9.c src/zlib/crc32.c src/zlib/adler32.c src/zlib/trees.c
|
|
24
|
+
|
|
25
|
+
PD_NOWINDOW_DEBUG_OBJS = $(PD_NOWINDOW_SRCS:%.c=build/debug/%.o)
|
|
26
|
+
|
|
27
|
+
# Dedicated reference test that uses inflateBack9 (infback9.c) directly
|
|
28
|
+
PD_REF_SRCS = test/payload_decompress_ref.c \
|
|
29
|
+
src/zlib/contrib/infback9/infback9.c src/zlib/contrib/infback9/inftree9.c src/zlib/crc32.c src/zlib/adler32.c src/zlib/trees.c src/zlib/zutil.c
|
|
30
|
+
|
|
31
|
+
PD_REF_OBJS = $(PD_REF_SRCS:%.c=build/debug/%.o)
|
|
32
|
+
PD_REF_OBJS_RELEASE = $(PD_REF_SRCS:%.c=build/%.o)
|
|
33
|
+
|
|
34
|
+
# object paths for release and debug builds
|
|
35
|
+
PD_OBJS = $(PD_SRCS:%.c=build/%.o)
|
|
36
|
+
PD_DEBUG_OBJS = $(PD_SRCS:%.c=build/debug/%.o)
|
|
37
|
+
|
|
38
|
+
all: test_all
|
|
39
|
+
|
|
40
|
+
clean:
|
|
41
|
+
@echo "Cleaning build artifacts, dist, tmp, and generated files"
|
|
42
|
+
rm -rf ./test/payload_decompress_test_debug ./test/payload_decompress_ref_debug ./test/payload_decompress_test_debug.* ./test/payload_decompress_ref_debug.* build tmp *.d dist/*.wasm tmp/all_runs tmp/run_all_verify.log
|
|
43
|
+
# remove node generated artifacts if present
|
|
44
|
+
rm -f src/wasm/tests/*.out || true
|
|
45
|
+
|
|
46
|
+
test/payload_decompress_test: $(PD_OBJS)
|
|
47
|
+
mkdir -p test
|
|
48
|
+
$(CC) $(CFLAGS) $(PD_OBJS) -o $@
|
|
49
|
+
|
|
50
|
+
# Debug variant of the payload decompressor (with debug symbols)
|
|
51
|
+
test_all: test/payload_decompress_test test/payload_decompress_test_debug test/payload_decompress_ref test/payload_decompress_ref_debug
|
|
52
|
+
@echo "Built test binaries: test/payload_decompress_test test/payload_decompress_test_debug test/payload_decompress_ref test/payload_decompress_ref_debug"
|
|
53
|
+
|
|
54
|
+
# Build-only debug target used by IDEs (compile only, do not run)
|
|
55
|
+
test/payload_decompress_test_debug: $(PD_DEBUG_OBJS)
|
|
56
|
+
mkdir -p test
|
|
57
|
+
$(CC) $(DEBUG_DEFINES) $(DEBUG_CFLAGS) $(PD_DEBUG_OBJS) -o $@
|
|
58
|
+
@echo "Debug binary built: $@"
|
|
59
|
+
|
|
60
|
+
# Build a true reference binary that uses inflateBack9 (infback9.c)
|
|
61
|
+
test/payload_decompress_ref_debug: $(PD_REF_OBJS)
|
|
62
|
+
mkdir -p test
|
|
63
|
+
$(CC) $(DEBUG_DEFINES) $(DEBUG_CFLAGS) $(PD_REF_OBJS) -o $@
|
|
64
|
+
|
|
65
|
+
# Build nowindow debug variant
|
|
66
|
+
test/payload_decompress_nowindow_debug: $(PD_NOWINDOW_DEBUG_OBJS)
|
|
67
|
+
mkdir -p test
|
|
68
|
+
$(CC) $(DEBUG_DEFINES) $(DEBUG_CFLAGS) $(PD_NOWINDOW_DEBUG_OBJS) -o $@
|
|
69
|
+
|
|
70
|
+
test/payload_decompress_ref: $(PD_REF_OBJS_RELEASE)
|
|
71
|
+
mkdir -p test
|
|
72
|
+
$(CC) $(CFLAGS) $(PD_REF_OBJS_RELEASE) -o $@
|
|
73
|
+
|
|
74
|
+
# pattern rules to compile sources into build object dirs
|
|
75
|
+
build/%.o: %.c
|
|
76
|
+
@echo "CC $< -> $@"
|
|
77
|
+
@mkdir -p $(dir $@)
|
|
78
|
+
$(CC) $(CFLAGS) -c $< -o $@
|
|
79
|
+
|
|
80
|
+
build/debug/%.o: %.c
|
|
81
|
+
@echo "CC (debug) $< -> $@"
|
|
82
|
+
@mkdir -p $(dir $@)
|
|
83
|
+
$(CC) $(DEBUG_DEFINES) $(DEBUG_CFLAGS) -c $< -o $@
|
|
84
|
+
|
|
85
|
+
run_ref_test:
|
|
86
|
+
@mkdir -p tmp
|
|
87
|
+
@sh -c '\
|
|
88
|
+
./test/payload_decompress_ref_debug ./test/ref-data/10k_lines.deflate64 tmp/out_ref.bin 2> tmp/trace_ref.log; \
|
|
89
|
+
rc=$$?; \
|
|
90
|
+
if [ $$rc -eq 0 ] && [ -s tmp/out_ref.bin ]; then \
|
|
91
|
+
echo OK >> tmp/trace_ref.log; \
|
|
92
|
+
else \
|
|
93
|
+
echo KO >> tmp/trace_ref.log; \
|
|
94
|
+
fi'
|
|
95
|
+
|
|
96
|
+
.PHONY: test_all ref_test run_ref_test
|
|
97
|
+
|
|
98
|
+
.PHONY: run_all_payloads_verify_ci
|
|
99
|
+
run_all_payloads_verify_ci:
|
|
100
|
+
@echo "Building quiet debug harnesses (no INF9_TRACE) and running full verification..."
|
|
101
|
+
@$(MAKE) DEBUG_DEFINES= test/payload_decompress_test_debug test/payload_decompress_ref_debug test/payload_decompress_nowindow_debug >/dev/null 2>&1 || true
|
|
102
|
+
@mkdir -p tmp/all_runs
|
|
103
|
+
@./test/run_all_payloads_and_verify.sh | tee tmp/all_runs/run_all_verify.log
|
|
104
|
+
@echo "wrote tmp/all_runs/run_all_verify.log"
|
|
105
|
+
|
|
106
|
+
.PHONY: ci
|
|
107
|
+
|
|
108
|
+
ci:
|
|
109
|
+
@mkdir -p tmp
|
|
110
|
+
@echo "CI START: $$(date)" > tmp/ci_run.log
|
|
111
|
+
@echo "Running make test (build)" >> tmp/ci_run.log
|
|
112
|
+
@$(MAKE) test_all >> tmp/ci_run.log 2>&1 || true
|
|
113
|
+
@for target run_ref_test; do \
|
|
114
|
+
echo "=== $$target ===" >> tmp/ci_run.log; \
|
|
115
|
+
$(MAKE) $$target >> tmp/ci_run.log 2>&1 || true; \
|
|
116
|
+
done
|
|
117
|
+
@echo "CI DONE: $$(date)" >> tmp/ci_run.log
|
|
118
|
+
@echo "Summary generated at $$(date)" > tmp/ci_summary.txt
|
|
119
|
+
@for out in tmp/out_*.bin; do [ -f "$$out" ] && stat -f"%N %z" "$$out" >> tmp/ci_summary.txt || true; done
|
|
120
|
+
@echo "" >> tmp/ci_summary.txt
|
|
121
|
+
@echo "-- last 10 lines of traces --" >> tmp/ci_summary.txt
|
|
122
|
+
@for trace in tmp/trace_*.log; do echo "--- $$trace ---" >> tmp/ci_summary.txt; tail -n 10 "$$trace" >> tmp/ci_summary.txt 2>/dev/null || true; echo "" >> tmp/ci_summary.txt; done
|
|
123
|
+
|
|
124
|
+
# Include generated dependency files (if present)
|
|
125
|
+
-include $(PD_OBJS:.o=.d) $(PD_DEBUG_OBJS:.o=.d)
|
|
126
|
+
|
|
127
|
+
# -----------------------------------------------------------------------------
|
|
128
|
+
# WASM build target (convenience target to produce dist/zlib-streams-dev.wasm)
|
|
129
|
+
# -----------------------------------------------------------------------------
|
|
130
|
+
EMCC ?= emsdk/upstream/emscripten/emcc
|
|
131
|
+
|
|
132
|
+
WASM_SRCS = src/wasm/inflate9_stream_wasm.c src/wasm/inflate_stream_wasm.c src/wasm/deflate_stream_wasm.c src/inflate9.c \
|
|
133
|
+
src/zlib/contrib/infback9/inftree9.c \
|
|
134
|
+
src/zlib/inffast.c src/zlib/inflate.c src/zlib/inftrees.c src/zlib/zutil.c \
|
|
135
|
+
src/zlib/crc32.c src/zlib/adler32.c src/zlib/trees.c src/zlib/deflate.c
|
|
136
|
+
WASM_CFLAGS = -Isrc -Isrc/zlib -Isrc/zlib/contrib/infback9 -O2 -flto -DDYNAMIC_CRC_TABLE
|
|
137
|
+
|
|
138
|
+
.PHONY: wasm
|
|
139
|
+
wasm: dist/zlib-streams-dev.wasm
|
|
140
|
+
|
|
141
|
+
.PHONY: wasm_traced
|
|
142
|
+
wasm_traced: dist/zlib-streams_traced.wasm
|
|
143
|
+
|
|
144
|
+
dist/zlib-streams_traced.wasm: $(WASM_SRCS)
|
|
145
|
+
@echo "Building traced $@ using $(EMCC)"
|
|
146
|
+
@mkdir -p dist
|
|
147
|
+
$(EMCC) $(WASM_SRCS) $(WASM_CFLAGS) $(DEBUG_DEFINES_TRACED) -s WASM=1 -s STANDALONE_WASM=1 --no-entry \
|
|
148
|
+
-s EXPORTED_FUNCTIONS='["_inflate9_new","_inflate9_init","_inflate9_init_raw","_inflate9_process","_inflate9_end","_inflate9_last_consumed","_inflate_new","_inflate_init","_inflate_init_raw","_inflate_init_gzip","_inflate_process","_inflate_end","_inflate_last_consumed","_deflate_new","_deflate_init","_deflate_init_raw","_deflate_init_gzip","_deflate_process","_deflate_end","_deflate_last_consumed","_malloc","_free"]' \
|
|
149
|
+
-o $@
|
|
150
|
+
|
|
151
|
+
# Run reference C and WASM test suites over payloads in test/ref-data
|
|
152
|
+
.PHONY: run_ref_c_tests
|
|
153
|
+
run_ref_c_tests: test/payload_decompress_test_debug test/payload_decompress_ref_debug test/payload_decompress_nowindow_debug
|
|
154
|
+
@echo "Running C reference harnesses over deflate64 payloads"
|
|
155
|
+
@mkdir -p tmp/all_runs
|
|
156
|
+
@./test/run_deflate64_suite.sh
|
|
157
|
+
|
|
158
|
+
.PHONY: run_ref_wasm_tests
|
|
159
|
+
run_ref_wasm_tests: dist/zlib-streams-dev.wasm
|
|
160
|
+
@echo "Running WASM runner over deflate64 payloads (sequential by default)"
|
|
161
|
+
@mkdir -p tmp/all_runs
|
|
162
|
+
@$(MAKE) run_ref_wasm_seq
|
|
163
|
+
@$(MAKE) run_wasm_roundtrip_mem
|
|
164
|
+
|
|
165
|
+
.PHONY: run_ref_wasm_seq
|
|
166
|
+
run_ref_wasm_seq: dist/zlib-streams-dev.wasm
|
|
167
|
+
@echo "Running WASM runner sequentially per payload (safe, non-blocking)"
|
|
168
|
+
@mkdir -p tmp/all_runs
|
|
169
|
+
@sh -c '\
|
|
170
|
+
for p in test/ref-data/*deflate64*; do \
|
|
171
|
+
[ -f "$$p" ] || continue; \
|
|
172
|
+
f=$$(basename "$$p"); \
|
|
173
|
+
printf "\nPayload: %s\n" "$$f"; \
|
|
174
|
+
rm -f tmp/all_runs/wasm__$$f.out; \
|
|
175
|
+
node src/wasm/tests/test_inflate9_stream.js dist/zlib-streams-dev.wasm "$$p" tmp/all_runs/wasm__$$f.out || printf " NODE_RC:%s for %s\n" "$$?" "$$f"; \
|
|
176
|
+
if [ -f tmp/all_runs/wasm__$$f.out ]; then \
|
|
177
|
+
printf " wrote tmp/all_runs/wasm__%s.out (size=%s)\n" "$$f" "$(stat -f%z tmp/all_runs/wasm__$$f.out 2>/dev/null || echo unknown)"; \
|
|
178
|
+
ref=tmp/all_runs/payload_decompress_ref_debug__$$f.out; \
|
|
179
|
+
if [ -f "$$ref" ]; then \
|
|
180
|
+
h1=`shasum -a 256 tmp/all_runs/wasm__$$f.out | cut -d\ -f1`; \
|
|
181
|
+
h2=`shasum -a 256 "$$ref" | cut -d\ -f1`; \
|
|
182
|
+
if [ "$$h1" = "$$h2" ]; then \
|
|
183
|
+
printf " SHA256 MATCH: %s\n" "$$h1"; \
|
|
184
|
+
else \
|
|
185
|
+
printf " SHA256 MISMATCH: wasm=%s ref=%s\n" "$$h1" "$$h2"; \
|
|
186
|
+
fi; \
|
|
187
|
+
else \
|
|
188
|
+
printf " ref missing: %s\n" "$$ref"; \
|
|
189
|
+
fi; \
|
|
190
|
+
fi; \
|
|
191
|
+
done'
|
|
192
|
+
|
|
193
|
+
.PHONY: run_ref_tests
|
|
194
|
+
run_ref_tests: run_ref_c_tests run_ref_wasm_tests
|
|
195
|
+
@echo "Completed C+WASM reference test suites"
|
|
196
|
+
|
|
197
|
+
.PHONY: run_wasm_roundtrip
|
|
198
|
+
run_wasm_roundtrip: dist/zlib-streams-dev.wasm
|
|
199
|
+
@echo "Running wasm roundtrip tests over tmp/all_runs/roundtrip_input*"
|
|
200
|
+
@mkdir -p tmp/all_runs
|
|
201
|
+
# populate tmp/all_runs with canonical roundtrip inputs
|
|
202
|
+
@cp -f test/ref-data/roundtrip_inputs/roundtrip_input.txt tmp/all_runs/roundtrip_input.txt
|
|
203
|
+
@cp -f test/ref-data/roundtrip_inputs/roundtrip_input1.txt tmp/all_runs/roundtrip_input1.txt
|
|
204
|
+
@cp -f test/ref-data/roundtrip_inputs/roundtrip_input3.bin tmp/all_runs/roundtrip_input3.bin
|
|
205
|
+
@sh -c '\
|
|
206
|
+
for p in tmp/all_runs/roundtrip_input*; do \
|
|
207
|
+
[ -f "$$p" ] || continue; \
|
|
208
|
+
f=$$(basename "$$p"); \
|
|
209
|
+
printf "\nInput: %s\n" "$$f"; \
|
|
210
|
+
rm -f tmp/all_runs/roundtrip_out__$$f; \
|
|
211
|
+
node src/wasm/tests/test_round_trip_stream.js dist/zlib-streams-dev.wasm "$$p" tmp/all_runs/roundtrip_out__$$f 2>&1 | sed -n '1,200p'; \
|
|
212
|
+
rc=$$?; \
|
|
213
|
+
if [ $$rc -eq 0 ]; then \
|
|
214
|
+
printf " OK: %s\n" "$$f"; \
|
|
215
|
+
else \
|
|
216
|
+
printf " FAIL (rc=%s): %s\n" "$$rc" "$$f"; \
|
|
217
|
+
fi; \
|
|
218
|
+
done'
|
|
219
|
+
|
|
220
|
+
.PHONY: run_wasm_roundtrip_mem
|
|
221
|
+
run_wasm_roundtrip_mem: dist/zlib-streams-dev.wasm
|
|
222
|
+
@echo "Running wasm roundtrip (in-memory) tests over tmp/all_runs/roundtrip_input*"
|
|
223
|
+
@mkdir -p tmp/all_runs
|
|
224
|
+
@sh -c '\
|
|
225
|
+
for p in tmp/all_runs/roundtrip_input*; do \
|
|
226
|
+
[ -f "$$p" ] || continue; \
|
|
227
|
+
f=$$(basename "$$p"); \
|
|
228
|
+
printf "\nInput: %s\n" "$$f"; \
|
|
229
|
+
node src/wasm/tests/test_round_trip_stream_mem.js dist/zlib-streams-dev.wasm "$$p" 2>&1 | sed -n "1,200p"; \
|
|
230
|
+
rc=$$?; \
|
|
231
|
+
if [ $$rc -eq 0 ]; then \
|
|
232
|
+
printf " OK: %s\n" "$$f"; \
|
|
233
|
+
else \
|
|
234
|
+
printf " FAIL (rc=%s): %s\n" "$$rc" "$$f"; \
|
|
235
|
+
fi; \
|
|
236
|
+
done'
|
|
237
|
+
|
|
238
|
+
.PHONY: run_transform_roundtrip
|
|
239
|
+
run_transform_roundtrip: dist/zlib-streams-dev.wasm
|
|
240
|
+
@echo "Running TransformStream roundtrip test"
|
|
241
|
+
@node src/wasm/tests/test_transform_roundtrip.js dist/zlib-streams-dev.wasm
|
|
242
|
+
|
|
243
|
+
.PHONY: deno_run_tests
|
|
244
|
+
deno_run_tests:
|
|
245
|
+
@echo "Running Deno test suite (deno/run_all_tests.sh)"
|
|
246
|
+
@chmod +x deno/run_all_tests.sh || true
|
|
247
|
+
@./deno/run_all_tests.sh dist/zlib-streams.wasm
|
|
248
|
+
|
|
249
|
+
.PHONY: run_inflate9_roundtrip_all
|
|
250
|
+
run_inflate9_roundtrip_all: dist/zlib-streams-dev.wasm
|
|
251
|
+
@echo "Running inflate9 roundtrip over deflate64 payloads"
|
|
252
|
+
@node src/wasm/tests/test_inflate9_roundtrip_all.js dist/zlib-streams-dev.wasm
|
|
253
|
+
|
|
254
|
+
.PHONY: test_decompressionstream_inflate9
|
|
255
|
+
test_decompressionstream_inflate9: dist/zlib-streams-dev.wasm
|
|
256
|
+
@echo "Testing DecompressionStreamZlib against native inflate9 for deflate64 payloads"
|
|
257
|
+
@node src/wasm/tests/test_decompressionstream_inflate9.js dist/zlib-streams-dev.wasm
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
# Single target to run the main wasm/TransformStream tests used during development
|
|
261
|
+
.PHONY: run_all_tests
|
|
262
|
+
run_all_tests: dist/zlib-streams-dev.wasm
|
|
263
|
+
@echo "Running all wasm TransformStream tests"
|
|
264
|
+
@$(MAKE) run_ref_wasm_tests
|
|
265
|
+
@$(MAKE) run_wasm_roundtrip
|
|
266
|
+
# Additional roundtrip tests not covered by the generic runners
|
|
267
|
+
@node src/wasm/tests/test_round_trip_stream_deflate.js dist/zlib-streams-dev.wasm
|
|
268
|
+
@node src/wasm/tests/test_round_trip_stream_gzip.js dist/zlib-streams-dev.wasm
|
|
269
|
+
@mkdir -p tmp/all_runs
|
|
270
|
+
# Ensure a raw-deflate test input exists (create from a repeated pattern)
|
|
271
|
+
@node -e "const fs=require('fs'), z=require('zlib'); fs.writeFileSync('tmp/all_runs/roundtrip_input3.bin', z.deflateRawSync(Buffer.alloc(24000, 'x')));"
|
|
272
|
+
@node src/wasm/tests/test_inflate_stream.js dist/zlib-streams-dev.wasm tmp/all_runs/roundtrip_input3.bin tmp/all_runs/inflate_stream_out.bin
|
|
273
|
+
# CLI helper run (small sample) for regressions/throughput checks
|
|
274
|
+
@node src/wasm/tests/run_roundtrip_cli.js deflate 24000 dist/zlib-streams-dev.wasm
|
|
275
|
+
@$(MAKE) run_transform_roundtrip
|
|
276
|
+
@$(MAKE) test_decompressionstream_inflate9
|
|
277
|
+
@echo "Completed run_all_tests"
|
|
278
|
+
|
|
279
|
+
dist/zlib-streams-dev.wasm: $(WASM_SRCS)
|
|
280
|
+
@echo "Building $@ using $(EMCC)"
|
|
281
|
+
@mkdir -p dist
|
|
282
|
+
$(EMCC) $(WASM_SRCS) $(WASM_CFLAGS) -s WASM=1 -s STANDALONE_WASM=1 --no-entry \
|
|
283
|
+
-s EXPORTED_FUNCTIONS='["_inflate9_new","_inflate9_init","_inflate9_init_raw","_inflate9_process","_inflate9_end","_inflate9_last_consumed","_inflate_new","_inflate_init","_inflate_init_raw","_inflate_init_gzip","_inflate_process","_inflate_end","_inflate_last_consumed","_deflate_new","_deflate_init","_deflate_init_raw","_deflate_init_gzip","_deflate_process","_deflate_end","_deflate_last_consumed","_malloc","_free"]' \
|
|
284
|
+
-o $@
|
|
285
|
+
cp src/wasm/api/zlib-streams.js dist/zlib-streams.js
|
|
286
|
+
|
|
287
|
+
# Production-optimized wasm: smaller build with -Oz and no extra runtime methods.
|
|
288
|
+
.PHONY: wasm_prod
|
|
289
|
+
wasm_prod: dist/zlib-streams.wasm
|
|
290
|
+
|
|
291
|
+
dist/zlib-streams.wasm: $(WASM_SRCS)
|
|
292
|
+
@echo "Building production wasm $@ using $(EMCC)"
|
|
293
|
+
@mkdir -p dist
|
|
294
|
+
$(EMCC) $(WASM_SRCS) $(WASM_CFLAGS) -Oz -flto -s WASM=1 -s STANDALONE_WASM=1 --no-entry \
|
|
295
|
+
-s FILESYSTEM=0 -s DISABLE_EXCEPTION_CATCHING=1 \
|
|
296
|
+
-s EXPORTED_FUNCTIONS='["_inflate9_new","_inflate9_init","_inflate9_init_raw","_inflate9_process","_inflate9_end","_inflate9_last_consumed","_inflate_new","_inflate_init","_inflate_init_raw","_inflate_init_gzip","_inflate_process","_inflate_end","_inflate_last_consumed","_deflate_new","_deflate_init","_deflate_init_raw","_deflate_init_gzip","_deflate_process","_deflate_end","_deflate_last_consumed","_malloc","_free"]' \
|
|
297
|
+
-o $@
|
|
298
|
+
cp src/wasm/api/zlib-streams.js dist/zlib-streams.js
|
|
299
|
+
@which wasm-opt >/dev/null 2>&1 && { echo "Running wasm-opt -Oz --enable-bulk-memory-opt"; wasm-opt -Oz --enable-bulk-memory-opt -o $@ $@ || true; } || true
|
|
300
|
+
|
|
301
|
+
# -----------------------------------------------------------------------------
|
|
302
|
+
# Optional generator: build a C++ tool that creates Deflate64 ZIPs using the
|
|
303
|
+
# 7-Zip SDK. This target is guarded: it only compiles when the SDK is present
|
|
304
|
+
# at src/7zip. The generator is not built by default.
|
|
305
|
+
# -----------------------------------------------------------------------------
|
|
306
|
+
CXX ?= g++
|
|
307
|
+
CXXFLAGS = -std=c++17 -I./src -I./src/7zip -O2
|
|
308
|
+
|
|
309
|
+
SDK_ROOT = src/7zip/CPP/7zip
|
|
310
|
+
# Guard building the SDK-backed generator. Set ENABLE_SDK_GENERATOR=1 to
|
|
311
|
+
# attempt compilation. By default, we skip to avoid platform-specific SDK
|
|
312
|
+
# build errors on non-Windows hosts.
|
|
313
|
+
ENABLE_SDK_GENERATOR ?= 0
|
|
314
|
+
|
|
315
|
+
# Only require the heavy SDK archive when we actually plan to build the
|
|
316
|
+
# SDK-backed generator. This avoids building the SDK object archive when
|
|
317
|
+
# the generator is being built in CLI-only mode.
|
|
318
|
+
SDK_PREREQS := $(if $(filter 1,$(ENABLE_SDK_GENERATOR)),build/lib7zip.a,)
|
|
319
|
+
|
|
320
|
+
# Minimal curated list of SDK sources required to build a Zip creator that
|
|
321
|
+
# uses compress/deflate/deflate64. This avoids compiling UI/Explorer/Windows
|
|
322
|
+
# specific code which fails on non-Windows hosts. Add more files here if
|
|
323
|
+
# the linker reports missing symbols.
|
|
324
|
+
SDK_FAST_SRCS = \
|
|
325
|
+
$(SDK_ROOT)/Archive/Zip/ZipAddCommon.cpp \
|
|
326
|
+
$(SDK_ROOT)/Archive/Zip/ZipHandler.cpp \
|
|
327
|
+
$(SDK_ROOT)/Archive/Zip/ZipHandlerOut.cpp \
|
|
328
|
+
$(SDK_ROOT)/Archive/Zip/ZipIn.cpp \
|
|
329
|
+
$(SDK_ROOT)/Archive/Zip/ZipItem.cpp \
|
|
330
|
+
$(SDK_ROOT)/Archive/Zip/ZipOut.cpp \
|
|
331
|
+
$(SDK_ROOT)/Archive/Zip/ZipRegister.cpp \
|
|
332
|
+
$(SDK_ROOT)/Archive/Zip/ZipUpdate.cpp \
|
|
333
|
+
$(SDK_ROOT)/Common/CreateCoder.cpp \
|
|
334
|
+
$(SDK_ROOT)/../Common/MyString.cpp \
|
|
335
|
+
$(SDK_ROOT)/../Common/StringConvert.cpp \
|
|
336
|
+
$(SDK_ROOT)/../Common/UTFConvert.cpp \
|
|
337
|
+
$(SDK_ROOT)/../Common/IntToString.cpp \
|
|
338
|
+
$(SDK_ROOT)/../Common/MyWindows.cpp \
|
|
339
|
+
$(SDK_ROOT)/Common/StreamObjects.cpp \
|
|
340
|
+
$(SDK_ROOT)/Common/OutMemStream.cpp \
|
|
341
|
+
$(SDK_ROOT)/Common/OutBuffer.cpp \
|
|
342
|
+
$(SDK_ROOT)/Common/InBuffer.cpp \
|
|
343
|
+
$(SDK_ROOT)/Common/InOutTempBuffer.cpp \
|
|
344
|
+
$(SDK_ROOT)/Common/StreamUtils.cpp \
|
|
345
|
+
$(SDK_ROOT)/Common/OffsetStream.cpp \
|
|
346
|
+
$(SDK_ROOT)/Common/FileStreams.cpp \
|
|
347
|
+
$(SDK_ROOT)/Common/CWrappers.cpp \
|
|
348
|
+
$(SDK_ROOT)/Common/FilterCoder.cpp \
|
|
349
|
+
$(SDK_ROOT)/Compress/CopyCoder.cpp \
|
|
350
|
+
$(SDK_ROOT)/Compress/DeflateRegister.cpp \
|
|
351
|
+
$(SDK_ROOT)/Compress/DeflateEncoder.cpp \
|
|
352
|
+
$(SDK_ROOT)/Compress/DeflateDecoder.cpp \
|
|
353
|
+
$(SDK_ROOT)/Compress/LzOutWindow.cpp \
|
|
354
|
+
$(SDK_ROOT)/Compress/Deflate64Register.cpp \
|
|
355
|
+
$(SDK_ROOT)/../Windows/FileIO.cpp \
|
|
356
|
+
$(SDK_ROOT)/../Windows/FileFind.cpp \
|
|
357
|
+
$(SDK_ROOT)/../Windows/FileName.cpp \
|
|
358
|
+
$(SDK_ROOT)/../Windows/FileMapping.cpp \
|
|
359
|
+
$(SDK_ROOT)/../Windows/System.cpp \
|
|
360
|
+
$(SDK_ROOT)/../Windows/TimeUtils.cpp \
|
|
361
|
+
$(SDK_ROOT)/../Windows/PropVariant.cpp \
|
|
362
|
+
$(SDK_ROOT)/../Windows/PropVariantConv.cpp \
|
|
363
|
+
$(SDK_ROOT)/../Windows/MemoryGlobal.cpp \
|
|
364
|
+
$(SDK_ROOT)/../Windows/MemoryLock.cpp \
|
|
365
|
+
$(SDK_ROOT)/../Windows/SystemInfo.cpp
|
|
366
|
+
|
|
367
|
+
SDK_C_SRCS := $(wildcard src/7zip/C/*.c)
|
|
368
|
+
|
|
369
|
+
SDK_C_OBJS := $(patsubst src/7zip/C/%.c,build/7zip/C/%.o,$(SDK_C_SRCS))
|
|
370
|
+
|
|
371
|
+
SDK_CPP := $(shell [ -d src/7zip ] && [ "$(ENABLE_SDK_GENERATOR)" -eq "1" ] && echo $(SDK_FAST_SRCS) || echo)
|
|
372
|
+
SDK_OBJS := $(patsubst $(SDK_ROOT)/%.cpp,build/7zip/%.o,$(SDK_CPP))
|
|
373
|
+
|
|
374
|
+
# Any extra non-SDK .cpp sources that should be compiled into the SDK archive
|
|
375
|
+
SDK_EXTRA_SRCS := src/generator/sdk_deflate64.cpp \
|
|
376
|
+
src/generator/win_compat.cpp \
|
|
377
|
+
src/generator/com_shims.cpp \
|
|
378
|
+
src/generator/sdk_shims.cpp
|
|
379
|
+
SDK_EXTRA_OBJS := $(patsubst src/%.cpp,build/7zip/%.o,$(SDK_EXTRA_SRCS))
|
|
380
|
+
|
|
381
|
+
build/lib7zip.a: $(SDK_OBJS) $(SDK_C_OBJS) $(SDK_EXTRA_OBJS)
|
|
382
|
+
@echo "Archiving SDK objects -> $@"
|
|
383
|
+
@mkdir -p $(dir $@)
|
|
384
|
+
@ar rcs $@ $(SDK_OBJS) $(SDK_C_OBJS) $(SDK_EXTRA_OBJS)
|
|
385
|
+
|
|
386
|
+
# Rule to build SDK sources (in-tree)
|
|
387
|
+
build/7zip/%.o: $(SDK_ROOT)/%.cpp
|
|
388
|
+
@echo "CXX sdk $< -> $@"
|
|
389
|
+
@mkdir -p $(dir $@)
|
|
390
|
+
$(CXX) $(CXXFLAGS) -I./src/7zip/CPP -I./src/7zip/CPP/7zip -I./src/generator -include src/generator/compat_prefix.h -c $< -o $@
|
|
391
|
+
|
|
392
|
+
# Rule to build extra local cpp sources into the SDK object tree
|
|
393
|
+
build/7zip/%.o: %.cpp
|
|
394
|
+
@echo "CXX local $< -> $@"
|
|
395
|
+
@mkdir -p $(dir $@)
|
|
396
|
+
$(CXX) $(CXXFLAGS) -I./src/7zip/CPP -I./src/7zip/CPP/7zip -c $< -o $@
|
|
397
|
+
|
|
398
|
+
# Rule to build C sources from src/7zip/C into the SDK object tree
|
|
399
|
+
build/7zip/C/%.o: $(SDK_ROOT)/../../C/%.c
|
|
400
|
+
@echo "CC sdk C $< -> $@"
|
|
401
|
+
@mkdir -p $(dir $@)
|
|
402
|
+
$(CC) $(CFLAGS) -I./src/7zip/C -I./src/7zip/CPP -I./src/7zip/CPP/7zip -c $< -o $@
|
|
403
|
+
|
|
404
|
+
# specific rule for sources under src/generator
|
|
405
|
+
build/7zip/generator/%.o: src/generator/%.cpp
|
|
406
|
+
@echo "CXX gen $< -> $@"
|
|
407
|
+
@mkdir -p $(dir $@)
|
|
408
|
+
$(CXX) $(CXXFLAGS) -I./src/7zip/CPP -I./src/7zip/CPP/7zip -c $< -o $@
|
|
409
|
+
|
|
410
|
+
generator: $(SDK_PREREQS)
|
|
411
|
+
@if [ "$(ENABLE_SDK_GENERATOR)" -ne "1" ]; then \
|
|
412
|
+
mkdir -p bin; \
|
|
413
|
+
echo "Building CLI-only generator (requires '7z' in PATH)"; \
|
|
414
|
+
$(CXX) $(CXXFLAGS) -o bin/create_deflate64 src/generator/create_deflate64.cpp || (echo "generator build failed"; exit 1); \
|
|
415
|
+
echo "built bin/create_deflate64"; \
|
|
416
|
+
else \
|
|
417
|
+
if [ -d src/7zip ]; then \
|
|
418
|
+
mkdir -p bin; \
|
|
419
|
+
echo "Building SDK-backed generator and linking with SDK library"; \
|
|
420
|
+
$(CXX) $(CXXFLAGS) -DUSE_7ZIP_SDK -I./src/7zip/CPP -I./src/7zip/CPP/7zip -o bin/create_deflate64 src/generator/create_deflate64.cpp build/7zip/Compress/Deflate64Register.o build/lib7zip.a || (echo "generator build failed"; exit 1); \
|
|
421
|
+
echo "built bin/create_deflate64"; \
|
|
422
|
+
else \
|
|
423
|
+
echo "skipping generator build: src/7zip not present (run 'make src/7zip' to fetch)"; \
|
|
424
|
+
fi; \
|
|
425
|
+
fi
|
|
426
|
+
|
|
427
|
+
# Helper: download and extract 7-Zip SDK into src/7zip
|
|
428
|
+
.PHONY: src/7zip
|
|
429
|
+
src/7zip:
|
|
430
|
+
@echo "Downloading 7-Zip LZMA SDK and extracting into src/7zip";
|
|
431
|
+
@mkdir -p tmp
|
|
432
|
+
@test -f tmp/lzma2501.7z || curl -sSL -o tmp/lzma2501.7z https://7-zip.org/a/lzma2501.7z
|
|
433
|
+
@mkdir -p src
|
|
434
|
+
@rm -rf src/7zip
|
|
435
|
+
@echo "Extracting tmp/lzma2501.7z -> src/7zip";
|
|
436
|
+
@mkdir -p src/7zip
|
|
437
|
+
@command -v 7z >/dev/null 2>&1 || (echo "7z not found: install p7zip to extract the SDK or extract tmp/lzma2501.7z manually"; exit 1)
|
|
438
|
+
@7z x -y -otmp tmp/lzma2501.7z >/dev/null 2>&1 || (echo "failed to extract SDK with 7z"; exit 1)
|
|
439
|
+
@# The archive contains a 7zip folder; move it to src/7zip/CPP
|
|
440
|
+
@if [ -d tmp/CPP ]; then mv tmp/CPP src/7zip/CPP; elif [ -d tmp/7z ]; then mv tmp/7z src/7zip/CPP; else mv tmp/* src/7zip/CPP 2>/dev/null || true; fi
|
|
441
|
+
@echo "SDK extracted to src/7zip/CPP"
|
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# zlib-streams Project: WASM-based Compression Streams API implementation using zlib, with support for deflate64 decompression
|
|
2
|
+
|
|
3
|
+
The zlib source code is included as a submodule in `src/zlib`.
|
|
4
|
+
|
|
5
|
+
## Directory Structure
|
|
6
|
+
|
|
7
|
+
- `src/` — C source code, including inflate9, zlib, wasm bindings
|
|
8
|
+
- `dist/` — WASM build outputs
|
|
9
|
+
- `test/` — C test harnesses, payloads, and shell scripts for verification
|
|
10
|
+
- `deno/` — Deno/Node.js test scripts and WASM runners
|
|
11
|
+
|
|
12
|
+
## Getting Started
|
|
13
|
+
|
|
14
|
+
### 1. Clone the repository and initialize submodules
|
|
15
|
+
```sh
|
|
16
|
+
git clone https://github.com/gildas-lormeau/zlib-streams.git
|
|
17
|
+
cd zlib-streams
|
|
18
|
+
git submodule update --init --recursive
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### 2. Build WASM module
|
|
22
|
+
- Developmeent
|
|
23
|
+
```sh
|
|
24
|
+
make wasm
|
|
25
|
+
```
|
|
26
|
+
Output: `dist/zlib-streams-dev.wasm` and `dist/zlib-streams.js`
|
|
27
|
+
|
|
28
|
+
- Production
|
|
29
|
+
```sh
|
|
30
|
+
make wasm_prod
|
|
31
|
+
```
|
|
32
|
+
Output: `dist/zlib-streams.wasm` and `dist/zlib-streams.js`
|
|
33
|
+
|
|
34
|
+
## License
|
|
35
|
+
See LICENSE for details.
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
/* global Buffer, process, TransformStream */
|
|
3
|
+
|
|
4
|
+
let wasm, malloc, free, memory;
|
|
5
|
+
|
|
6
|
+
export function setWasmExports(wasmAPI) {
|
|
7
|
+
wasm = wasmAPI;
|
|
8
|
+
({ malloc, free, memory } = wasm);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function _make(isCompress, type, options = {}) {
|
|
12
|
+
const level = (typeof options.level === "number") ? options.level : -1;
|
|
13
|
+
const outBufferSize = (typeof options.outBuffer === "number") ? options.outBuffer : 64 * 1024;
|
|
14
|
+
const inBufferSize = (typeof options.inBufferSize === "number") ? options.inBufferSize : 64 * 1024;
|
|
15
|
+
|
|
16
|
+
return new TransformStream({
|
|
17
|
+
start() {
|
|
18
|
+
let result;
|
|
19
|
+
this.out = malloc(outBufferSize);
|
|
20
|
+
this.in = malloc(inBufferSize);
|
|
21
|
+
this.inBufferSize = inBufferSize;
|
|
22
|
+
this._scratch = new Uint8Array(outBufferSize);
|
|
23
|
+
if (isCompress) {
|
|
24
|
+
this._process = wasm.deflate_process;
|
|
25
|
+
this._last_consumed = wasm.deflate_last_consumed;
|
|
26
|
+
this._end = wasm.deflate_end;
|
|
27
|
+
this.streamHandle = wasm.deflate_new();
|
|
28
|
+
if (type === "gzip") {
|
|
29
|
+
result = wasm.deflate_init_gzip(this.streamHandle, level);
|
|
30
|
+
} else if (type === "deflate-raw") {
|
|
31
|
+
result = wasm.deflate_init_raw(this.streamHandle, level);
|
|
32
|
+
} else {
|
|
33
|
+
result = wasm.deflate_init(this.streamHandle, level);
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
if (type === "deflate64-raw") {
|
|
37
|
+
this._process = wasm.inflate9_process;
|
|
38
|
+
this._last_consumed = wasm.inflate9_last_consumed;
|
|
39
|
+
this._end = wasm.inflate9_end;
|
|
40
|
+
this.streamHandle = wasm.inflate9_new();
|
|
41
|
+
result = wasm.inflate9_init_raw(this.streamHandle);
|
|
42
|
+
} else {
|
|
43
|
+
this._process = wasm.inflate_process;
|
|
44
|
+
this._last_consumed = wasm.inflate_last_consumed;
|
|
45
|
+
this._end = wasm.inflate_end;
|
|
46
|
+
this.streamHandle = wasm.inflate_new();
|
|
47
|
+
if (type === "deflate-raw") {
|
|
48
|
+
result = wasm.inflate_init_raw(this.streamHandle);
|
|
49
|
+
} else if (type === "gzip") {
|
|
50
|
+
result = wasm.inflate_init_gzip(this.streamHandle);
|
|
51
|
+
} else {
|
|
52
|
+
result = wasm.inflate_init(this.streamHandle);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (result !== 0) {
|
|
57
|
+
throw new Error("init failed:" + result);
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
transform(chunk, controller) {
|
|
61
|
+
try {
|
|
62
|
+
const buffer = chunk;
|
|
63
|
+
const heap = new Uint8Array(memory.buffer);
|
|
64
|
+
const process = this._process;
|
|
65
|
+
const last_consumed = this._last_consumed;
|
|
66
|
+
const out = this.out;
|
|
67
|
+
const scratch = this._scratch;
|
|
68
|
+
let offset = 0;
|
|
69
|
+
while (offset < buffer.length) {
|
|
70
|
+
const toRead = Math.min(buffer.length - offset, 32 * 1024);
|
|
71
|
+
if (!this.in || this.inBufferSize < toRead) {
|
|
72
|
+
if (this.in && free) {
|
|
73
|
+
free(this.in);
|
|
74
|
+
}
|
|
75
|
+
this.in = malloc(toRead);
|
|
76
|
+
this.inBufferSize = toRead;
|
|
77
|
+
}
|
|
78
|
+
heap.set(buffer.subarray(offset, offset + toRead), this.in);
|
|
79
|
+
const result = process(this.streamHandle, this.in, toRead, out, outBufferSize, 0);
|
|
80
|
+
if (!isCompress && result < 0) {
|
|
81
|
+
throw new Error("process error:" + result);
|
|
82
|
+
}
|
|
83
|
+
const prod = result & 0x00ffffff;
|
|
84
|
+
if (prod) {
|
|
85
|
+
scratch.set(heap.subarray(out, out + prod), 0);
|
|
86
|
+
controller.enqueue(scratch.slice(0, prod));
|
|
87
|
+
}
|
|
88
|
+
const consumed = last_consumed(this.streamHandle);
|
|
89
|
+
if (consumed === 0) {
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
offset += consumed;
|
|
93
|
+
}
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (this._end && this.streamHandle) {
|
|
96
|
+
this._end(this.streamHandle);
|
|
97
|
+
}
|
|
98
|
+
if (this.in && free) {
|
|
99
|
+
free(this.in);
|
|
100
|
+
}
|
|
101
|
+
if (this.out && free) {
|
|
102
|
+
free(this.out);
|
|
103
|
+
}
|
|
104
|
+
controller.error(error);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
flush(controller) {
|
|
108
|
+
try {
|
|
109
|
+
const heap = new Uint8Array(memory.buffer);
|
|
110
|
+
const process = this._process;
|
|
111
|
+
const out = this.out;
|
|
112
|
+
const scratch = this._scratch;
|
|
113
|
+
while (true) {
|
|
114
|
+
const result = process(this.streamHandle, 0, 0, out, outBufferSize, 4);
|
|
115
|
+
if (!isCompress && result < 0) {
|
|
116
|
+
throw new Error("process error:" + result);
|
|
117
|
+
}
|
|
118
|
+
const produced = result & 0x00ffffff;
|
|
119
|
+
const code = (result >> 24) & 0xff;
|
|
120
|
+
if (produced) {
|
|
121
|
+
scratch.set(heap.subarray(out, out + produced), 0);
|
|
122
|
+
controller.enqueue(scratch.slice(0, produced));
|
|
123
|
+
}
|
|
124
|
+
if (code === 1 || produced === 0) {
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
} catch (error) {
|
|
129
|
+
controller.error(error);
|
|
130
|
+
} finally {
|
|
131
|
+
if (this._end && this.streamHandle) {
|
|
132
|
+
const result = this._end(this.streamHandle);
|
|
133
|
+
if (result !== 0) {
|
|
134
|
+
controller.error(new Error("end error:" + result));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (this.in && free) {
|
|
138
|
+
free(this.in);
|
|
139
|
+
}
|
|
140
|
+
if (this.out && free) {
|
|
141
|
+
free(this.out);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export class CompressionStreamZlib {
|
|
149
|
+
constructor(type = "deflate", options) {
|
|
150
|
+
return _make(true, type, options);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
export class DecompressionStreamZlib {
|
|
154
|
+
constructor(type = "deflate", options) {
|
|
155
|
+
return _make(false, type, options);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
Binary file
|