mzstd
Zstandard (RFC 8878) compression and decompression in safe Rust.
- Byte-identical encoder. For the same input and parameters,
single-threaded compression writes exactly the bytes of zstd 1.5.7:
one-shot calls match
ZSTD_compress2, and sessions matchZSTD_compressStream2driven with the same chunks, output capacities and flushes. This holds for levels -131072 to 22, the explicit compression parameters, long distance matching, checksums, content sizes, pledged sizes and size hints. - Independent decoder written from the format specification. It decodes every frame zstd writes apart from dictionary frames, with bounded window, input and output limits.
- Parallel compression with tasks you run on your own executor. Frames are valid Zstandard frames and are identical for any task count, but they are not byte-identical to zstd's multithreaded mode.
- No
unsafe, no dependencies on zstd. It runs withno_std+alloc, and there is a C ABI inmzstd-ffi.
[]
= "0.1"
Usage
use ;
let mut encoder = new?;
let compressed = encoder.compress?;
let restored = default.decompress?;
assert_eq!;
# Ok::
| I/O shape | Compression | Decompression |
|---|---|---|
New Vec<u8> |
Compressor::compress |
Decompressor::decompress |
Append to a Vec |
compress_into |
decompress_into |
| Caller-owned slice | compress_to_slice, compress_to_uninit |
decompress_to_slice |
| Incremental | start → EncoderSession |
start → DecoderSession |
| Incremental, owning the codec | into_session → EncoderSessionOwned |
into_session → DecoderSessionOwned |
std::io |
Compressor::reader / writer |
Decompressor::reader / writer |
| Across threads | compressor::parallel::ParallelCompressor |
— |
Explicit parameters override the level just as ZSTD_CCtx_setParameter does:
use ;
let config = default
.with_level
.with_checksum
.with_params
.with_long_distance_matching;
let mut encoder = new?;
# Ok::
Features
| Feature | Default | Enables |
|---|---|---|
compression |
yes | mzstd::compressor |
decompression |
yes | mzstd::decompressor |
std |
yes | std::error::Error, mzstd::io, parallel compression |
no_std |
no | alloc-only build; overrides std |
hotpath, hotpath-cpu, hotpath-alloc |
no | profiling anchors |
Performance
Speed was not a goal of this first version: both codecs are scalar ports
without SIMD. As a reference point, here is cargo bench --bench compress
and --bench decompress on an Apple M5 Pro with rustc 1.99 (Criterion
medians; both sides write and read identical frames):
| Case | mzstd | zstd 1.5.7 |
|---|---|---|
| compress text 1 MiB, level 1 | 398 MiB/s | 694 MiB/s |
| compress text 1 MiB, level 3 | 418 MiB/s | 775 MiB/s |
| compress text 1 MiB, level 9 | 54.5 MiB/s | 105 MiB/s |
| compress text 1 MiB, level 19 | 3.8 MiB/s | 5.5 MiB/s |
| compress binary 1 MiB, level 3 | 197 MiB/s | 350 MiB/s |
| decompress text 4 MiB (level 3 frame) | 716 MiB/s | 2.19 GiB/s |
| decompress binary 4 MiB (level 3 frame) | 270 MiB/s | 1.22 GiB/s |
Verification
- Differential tests against zstd 1.5.7 (built from the vendored submodule by
zstd-ffi). They cover every level on a standard corpus, thousands of random advanced-parameter configurations, streaming schedules with flushes and wrapped windows, LDM, short destinations, and a build mode that forces index overflow correction on both sides. - Decoder tests: reference frames through every entry point, upstream's golden vectors, truncation and bit-flip corruption, limits.
- AFL++ targets in
fuzz/afl. Encoder targets: differential, output capacity, sessions, lifecycle, parallel and parameter parsing. Decoder targets: raw input against the reference decoder, chunked sessions, and reference-written frames. - 100% function coverage, checked with
cargo llvm-cov.
See architecture/ for how the code is organized
and verified.
Not supported
- Dictionaries
- Magicless frames
targetCBlockSize- External sequence producers
- Legacy (v0.x) frames
- Byte identity with zstd's multithreaded mode
License
MIT. The encoder is a port of zstd, used under its BSD license; see NOTICE.