@zip.js/zip.js 2.8.28 → 2.8.30
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/BENCHMARKS.md +203 -0
- package/deno.json +1 -1
- package/dist/zip-core.js +590 -53
- package/dist/zip-core.min.js +1 -1
- package/dist/zip-fs-core.js +476 -53
- package/dist/zip-fs-core.min.js +1 -1
- package/dist/zip-fs-native.js +652 -57
- package/dist/zip-fs-native.min.js +1 -1
- package/dist/zip-fs.js +655 -55
- package/dist/zip-fs.min.js +1 -1
- package/dist/zip-legacy.js +591 -54
- package/dist/zip-legacy.min.js +1 -1
- package/dist/zip-module.wasm +0 -0
- package/dist/zip-native.js +594 -57
- package/dist/zip-native.min.js +1 -1
- package/dist/zip-web-worker-native.js +1 -1
- package/dist/zip-web-worker.js +1 -1
- package/dist/zip.js +597 -55
- package/dist/zip.min.js +1 -1
- package/index-native.cjs +652 -57
- package/index-native.min.js +1 -1
- package/index.cjs +655 -55
- package/index.d.ts +126 -1
- package/index.min.js +1 -1
- package/lib/core/codec-worker.js +10 -1
- package/lib/core/options.js +11 -1
- package/lib/core/streams/codecs/crc32.js +39 -11
- package/lib/core/streams/zip-entry-stream.js +139 -9
- package/lib/core/streams/zlib-js/zlib-streams.min.js +1 -1
- package/lib/core/streams/zlib-wasm/zlib-streams.js +6 -1
- package/lib/core/streams/zlib-wasm/zlib-streams.wasm +0 -0
- package/lib/core/util/opfs-temp-stream.js +175 -0
- package/lib/core/web-worker-inline-native.js +1 -1
- package/lib/core/web-worker-inline-wasm.js +1 -1
- package/lib/core/zip-fs.js +58 -0
- package/lib/core/zip-reader.js +219 -31
- package/lib/core/zip-writer.js +11 -0
- package/lib/core/zlib-streams-inline.js +1 -1
- package/lib/zip-core-base.js +4 -1
- package/package.json +1 -1
package/BENCHMARKS.md
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# Benchmarks
|
|
2
|
+
|
|
3
|
+
A fair, reproducible comparison of **@zip.js/zip.js** against
|
|
4
|
+
[jszip](https://github.com/Stuk/jszip), [fflate](https://github.com/101arrowz/fflate)
|
|
5
|
+
and [archiver](https://github.com/archiverjs/node-archiver) on a set of realistic
|
|
6
|
+
workloads.
|
|
7
|
+
|
|
8
|
+
The numbers below are honest: zip.js wins clearly on some workloads and loses on
|
|
9
|
+
others. The goal is to show *where* each library is the right tool, and to give you a
|
|
10
|
+
harness you can re-run on your own hardware — the results are machine-specific and you
|
|
11
|
+
should not trust anyone's benchmark (including this one) without reproducing it.
|
|
12
|
+
|
|
13
|
+
> **TL;DR** — For compressing large or multiple entries, zip.js is the fastest option
|
|
14
|
+
> in the field, and it is the only one that parallelizes compression across CPU cores
|
|
15
|
+
> **without spawning a single Web Worker** — it lets the platform's native
|
|
16
|
+
> `CompressionStream` run on the threadpool while you simply issue concurrent `add()`
|
|
17
|
+
> calls. It also streams arbitrarily large files at a flat, low memory ceiling. For
|
|
18
|
+
> deflating thousands of tiny buffers in one shot, **fflate** remains the throughput
|
|
19
|
+
> and footprint champion.
|
|
20
|
+
|
|
21
|
+
## Environment
|
|
22
|
+
|
|
23
|
+
| | |
|
|
24
|
+
|---|---|
|
|
25
|
+
| Machine | Apple M2, 8 cores (4 performance + 4 efficiency), 16 GB RAM |
|
|
26
|
+
| OS | macOS 26.5.1 (arm64) |
|
|
27
|
+
| Runtime | Node.js v24.12.0 |
|
|
28
|
+
| zip.js | 2.8.29 |
|
|
29
|
+
| jszip | 3.10.1 |
|
|
30
|
+
| fflate | 0.8.3 |
|
|
31
|
+
| archiver | 8.0.0 |
|
|
32
|
+
|
|
33
|
+
## Method
|
|
34
|
+
|
|
35
|
+
- **Isolation.** Each `(library, operation, workload)` combination runs in its own
|
|
36
|
+
freshly-spawned Node process under `/usr/bin/time -l`, so there is no cross-library
|
|
37
|
+
GC or heap contamination and peak memory is a true per-library figure.
|
|
38
|
+
- **Timing.** `performance.now()` around the measured operation only. Every combination
|
|
39
|
+
runs **3 times**; the table reports the **median**.
|
|
40
|
+
- **Memory.** Peak resident set size (RSS) reported by `/usr/bin/time -l`. The "peak"
|
|
41
|
+
column is the delta over an empty-process baseline (~42 MB), i.e. the memory
|
|
42
|
+
attributable to the work.
|
|
43
|
+
- **Fair work.** All libraries compress at **DEFLATE level 6**. Output sizes are shown
|
|
44
|
+
so you can confirm each library did equivalent work. The corpus is generated from a
|
|
45
|
+
seeded PRNG, so every library sees byte-for-byte identical input.
|
|
46
|
+
- **zip.js modes.** zip.js is measured both **single-threaded** (apples-to-apples with
|
|
47
|
+
the single-threaded libraries) and with its **Web Worker** pool, so the worker
|
|
48
|
+
overhead is never hidden.
|
|
49
|
+
|
|
50
|
+
## Compression — single process, single thread
|
|
51
|
+
|
|
52
|
+
Level-6 DEFLATE, one entry (or one batch) compressed in the main thread. This is the
|
|
53
|
+
apples-to-apples comparison against the single-threaded libraries.
|
|
54
|
+
|
|
55
|
+
| Workload | @zip.js/zip.js | jszip | fflate | archiver |
|
|
56
|
+
|---|--:|--:|--:|--:|
|
|
57
|
+
| Compressible text (20 MB) | **795 ms** | 1770 ms | 953 ms | 720 ms |
|
|
58
|
+
| Incompressible data (20 MB) | 446 ms | 883 ms | **306 ms** | 366 ms |
|
|
59
|
+
| Already-compressed media (20 MB) | 452 ms | 886 ms | **298 ms** | 358 ms |
|
|
60
|
+
| 5,000 files × ~2 KB | 845 ms | 887 ms | **287 ms** | 419 ms |
|
|
61
|
+
|
|
62
|
+
Peak memory for the same runs (Δ over baseline):
|
|
63
|
+
|
|
64
|
+
| Workload | @zip.js/zip.js | jszip | fflate | archiver |
|
|
65
|
+
|---|--:|--:|--:|--:|
|
|
66
|
+
| Compressible text (20 MB) | 92 MB | 65 MB | **60 MB** | 82 MB |
|
|
67
|
+
| Incompressible data (20 MB) | 141 MB | 93 MB | 106 MB | **83 MB** |
|
|
68
|
+
| Already-compressed media (20 MB) | 142 MB | 94 MB | 106 MB | **83 MB** |
|
|
69
|
+
| 5,000 files × ~2 KB | 266 MB | 270 MB | **102 MB** | 118 MB |
|
|
70
|
+
|
|
71
|
+
zip.js is fastest on compressible text and competitive with archiver elsewhere, at
|
|
72
|
+
roughly comparable compressed sizes. **fflate** is the clear winner on raw throughput
|
|
73
|
+
and footprint for incompressible data and for large numbers of tiny files — if that is
|
|
74
|
+
your workload, use fflate.
|
|
75
|
+
|
|
76
|
+
## Parallelism & codec backends — 8 files × 8 MB, single process
|
|
77
|
+
|
|
78
|
+
This is the headline. The same 64 MB of compressible entries, compressed several ways
|
|
79
|
+
in a **plain Node process** (no worker threads unless noted). zip.js can select its
|
|
80
|
+
codec backend at runtime — native `CompressionStream`, the bundled WebAssembly zlib, or
|
|
81
|
+
a pure-JavaScript zlib port — and it can issue `add()` calls concurrently.
|
|
82
|
+
|
|
83
|
+
| Configuration | Median time | vs jszip |
|
|
84
|
+
|---|--:|--:|
|
|
85
|
+
| **zip.js — `CompressionStream`, concurrent `add()`** | **657 ms** | **8.8×** |
|
|
86
|
+
| fflate — async (its own worker pool) | 729 ms | 7.9× |
|
|
87
|
+
| archiver — Node zlib (libuv threadpool) | 2266 ms | 2.5× |
|
|
88
|
+
| zip.js — `CompressionStream`, sequential | 2453 ms | 2.3× |
|
|
89
|
+
| fflate — `zipSync` (single thread) | 3103 ms | 1.9× |
|
|
90
|
+
| zip.js — WASM zlib | 4176 ms | 1.4× |
|
|
91
|
+
| zip.js — pure-JS zlib | 4709 ms | 1.2× |
|
|
92
|
+
| jszip (pako, single thread) | 5758 ms | 1.0× |
|
|
93
|
+
|
|
94
|
+
**The point:** zip.js with the native `CompressionStream` goes from **2453 ms
|
|
95
|
+
sequential to 657 ms with concurrent `add()` — a 3.7× speedup — using no Web Workers at
|
|
96
|
+
all.** The native codec runs on the platform's threadpool, so independent entries
|
|
97
|
+
compress on multiple cores while your code stays on the main thread. That makes it the
|
|
98
|
+
fastest configuration measured, narrowly ahead of fflate's dedicated worker pool.
|
|
99
|
+
|
|
100
|
+
One honest caveat, visible in the table: **only the native `CompressionStream` backend
|
|
101
|
+
parallelizes this way.** The WASM and pure-JS backends run synchronously on the main
|
|
102
|
+
thread, so concurrent `add()` does not speed them up (4176 ms and 4709 ms whether
|
|
103
|
+
sequential or "parallel"). Use those backends when a native `CompressionStream` is
|
|
104
|
+
unavailable or when you need byte-identical zlib output; use the native backend when you
|
|
105
|
+
want this parallelism.
|
|
106
|
+
|
|
107
|
+
There is a second way to land on the WASM backend by accident: because
|
|
108
|
+
`CompressionStream` exposes no level control, requesting any non-default compression
|
|
109
|
+
level (e.g. `{ level: 5 }`) makes zip.js fall back to the WASM zlib codec — which, per
|
|
110
|
+
the table, does not parallelize via concurrent `add()`. Keep the default level to keep
|
|
111
|
+
the native-backend parallelism, or pair a custom level with Web Workers (below).
|
|
112
|
+
|
|
113
|
+
### Parallelism is runtime-dependent
|
|
114
|
+
|
|
115
|
+
The table above is measured on **Node**, and its "no Web Workers needed" result does
|
|
116
|
+
**not** hold on every runtime: concurrent `add()` only spreads across cores if the
|
|
117
|
+
runtime runs `CompressionStream` off the main thread. Same 8 × 8 MB workload, level 6,
|
|
118
|
+
median of 3:
|
|
119
|
+
|
|
120
|
+
| Runtime | sequential | concurrent `add()` | concurrent `add()` + `useWebWorkers` |
|
|
121
|
+
|---|--:|--:|--:|
|
|
122
|
+
| Node.js | 2.94 s | **0.74 s** | 0.74 s |
|
|
123
|
+
| Bun | 1.80 s | **0.36 s** | 0.46 s |
|
|
124
|
+
| Deno | 1.84 s | 1.82 s | **0.47 s** |
|
|
125
|
+
|
|
126
|
+
- **Node and Bun** back `CompressionStream` with a threadpool, so concurrent `add()`
|
|
127
|
+
alone parallelizes — no Web Workers needed (Bun is fastest here).
|
|
128
|
+
- **Deno** runs `CompressionStream` on the isolate thread, so concurrent `add()` alone
|
|
129
|
+
gives no speedup (1.82 s ≈ its 1.84 s sequential). Set `useWebWorkers: true` and it
|
|
130
|
+
parallelizes properly (0.47 s), landing right beside the others.
|
|
131
|
+
- **Browsers vary by engine.** Safari/WebKit runs `CompressionStream` on the main thread
|
|
132
|
+
(serial, like Deno), so use `useWebWorkers: true` there. Chromium implements it
|
|
133
|
+
separately and may behave differently — check a given browser by compressing several
|
|
134
|
+
large buffers through `CompressionStream` sequentially versus concurrently and comparing
|
|
135
|
+
the wall time.
|
|
136
|
+
|
|
137
|
+
**Rule of thumb:** on Node and Bun, concurrent `add()` is enough; on Deno and
|
|
138
|
+
Safari/WebKit, also set `useWebWorkers: true`. Web Workers are the portable way to get
|
|
139
|
+
this parallelism on any runtime — and the only way once you use a non-default level
|
|
140
|
+
(which switches to the WASM codec).
|
|
141
|
+
|
|
142
|
+
## Decompression
|
|
143
|
+
|
|
144
|
+
Level-6 archives, read back and fully materialized. archiver has no unzip API, so it is
|
|
145
|
+
excluded.
|
|
146
|
+
|
|
147
|
+
| Workload | @zip.js/zip.js | jszip | fflate |
|
|
148
|
+
|---|--:|--:|--:|
|
|
149
|
+
| Compressible text (20 MB) | **65 ms** | 143 ms | 82 ms |
|
|
150
|
+
| 5,000 files × ~2 KB | 565 ms | 453 ms | **89 ms** |
|
|
151
|
+
|
|
152
|
+
zip.js has the fastest large-stream decompression. On thousands of tiny entries the
|
|
153
|
+
per-entry setup cost dominates and **fflate is dramatically faster and lighter** — again
|
|
154
|
+
the right tool when you are unpacking many small files.
|
|
155
|
+
|
|
156
|
+
## Streaming a large file — 256 MB, disk → zip → disk
|
|
157
|
+
|
|
158
|
+
The input is streamed from disk and the archive is streamed back to disk; neither is
|
|
159
|
+
ever fully held in memory (for the libraries that support it).
|
|
160
|
+
|
|
161
|
+
| Library | Median time | Peak memory |
|
|
162
|
+
|---|--:|--:|
|
|
163
|
+
| archiver | 9183 ms | 111 MB |
|
|
164
|
+
| zip.js (workers) | 9893 ms | **99 MB** |
|
|
165
|
+
| zip.js (1 thread) | 10110 ms | **99 MB** |
|
|
166
|
+
| fflate | 10613 ms | 87 MB |
|
|
167
|
+
| jszip | 22358 ms | 527 MB |
|
|
168
|
+
|
|
169
|
+
zip.js, fflate and archiver all hold memory **flat** while streaming — zip.js peaks at
|
|
170
|
+
~99 MB regardless of the 256 MB input, thanks to real backpressure through the
|
|
171
|
+
compression pipeline. **jszip buffers the entire file** and needs ~527 MB, at more than
|
|
172
|
+
twice the wall-clock time. If you process files that do not fit comfortably in memory,
|
|
173
|
+
avoid jszip.
|
|
174
|
+
|
|
175
|
+
## When to pick which
|
|
176
|
+
|
|
177
|
+
- **Choose zip.js** for the fastest compression of large or multiple entries
|
|
178
|
+
(parallelism with no Web Workers), the fastest large-stream decompression, flat
|
|
179
|
+
low-memory streaming of huge files, and the broadest ZIP feature set in one library —
|
|
180
|
+
AES & ZipCrypto encryption, Zip64, split/multi-volume archives, and an optional Web
|
|
181
|
+
Worker pool.
|
|
182
|
+
- **Choose fflate** when you deflate thousands of tiny buffers in a single call and want
|
|
183
|
+
the smallest memory footprint and the highest raw synchronous throughput.
|
|
184
|
+
- **archiver** is a solid streaming compressor on Node but cannot read archives.
|
|
185
|
+
- **jszip** is convenient but the slowest here and buffers whole files in memory.
|
|
186
|
+
|
|
187
|
+
## Reproduce
|
|
188
|
+
|
|
189
|
+
The harness lives in [`benchmarks/`](benchmarks/). It has no ties to the machine above;
|
|
190
|
+
run it on yours.
|
|
191
|
+
|
|
192
|
+
```sh
|
|
193
|
+
cd benchmarks
|
|
194
|
+
npm install # jszip, fflate, archiver (zip.js is used from the repo)
|
|
195
|
+
npm run corpus # generate the deterministic datasets under .corpus/
|
|
196
|
+
node bench.js # the head-to-head tables (compress / decompress / disk streaming)
|
|
197
|
+
node bench-backends.js # the parallelism & codec-backend matrix
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Both scripts write JSON and a human-readable log to `benchmarks/results/`. Set
|
|
201
|
+
`RUNS=<n>` to change the number of repetitions (default 3). The datasets are generated
|
|
202
|
+
from a seeded PRNG (`benchmarks/lib/corpus.js`), so every run — and every library —
|
|
203
|
+
sees identical bytes.
|