Expand description
The zstd family (zstd, unzstd, zstdcat, zstdmt): stuffr
behaving as zstd 1.5.7’s command line tool.
Ported from zstd 1.5.7 programs/ by Yann Collet and the zstd
contributors (Meta Platforms, Inc.; BSD-3-Clause, the licence ships as
LICENSE-zstd), one module per C file: cli.rs (zstdcli.c’s argument
parsing, help texts and parameter printers), dibio.rs (dibio.c,
--train*), util.rs (util.c),
display.rs (fileio_common.h’s display macros) and this module
(zstdcli.c’s main after the arguments). Every string is in
messages.rs. libzstd itself is reached through
stuffr::zstd_opts.
Names (spec section 1): the four map to this family, and the
presets come from the invoked name by zstdcli.c’s exeNameMatch (the
name, then the end or a .): unzstd decompresses, zstdcat
decompresses to standard output with pass-through at display level 1,
zstdmt compresses with -T0. zstdcli.c’s gzip, xz, lzma and lz4
presets are ported too, though dispatch never reaches them (those names
belong to other families).
The machine (R3): the real process counts its cores as util.c does
(util::Cores::detect); a sandboxed run ([run_in_counted]) sees 8
logical and 4 physical cores.
Known deviations (the source of truth; the README and the spec mirror this list):
- The banner names stuffr wherever zstd prints
WELCOME_MESSAGE(-V,-vV,-vvV, the first line of-H, and-v’s welcome line on standard error):*** Zstandard CLI (stuffr X.Y.Z; zstd 1.5.7 compatible) (64-bit) v1.5.7, by Yann Collet ***.-qVprints exactly1.5.7, and-vV’s “supports:” line is zstd’s.-vvVprints zstd’s POSIX lines for the platform, but not its three library-version lines (the zlib, lz4 and lzma zstd links), which describe zstd’s own build. - The
-vsummary’s timing fields vary from run to run, as they do between two runs of zstd. - An input that is also the output is never lost.
- Closed standard streams and a parent that ignores SIGPIPE: the Rust
runtime reopens a closed standard descriptor on
/dev/nulland resets SIGPIPE beforemain, so neither is seen. - Out of scope, refused as a zstd built with
ZSTD_NOBENCHandZSTD_NOTRACErefuses them:-b,-e#,-i#(“Incorrect parameter: -b”, exit 1) and--trace(“Incorrect parameter: –trace”);-Homits their lines.-B#,-Sand-p#keep the reference’s meaning.--[no-]asynciois accepted and has no visible effect.--trace-file-statis accepted and prints no trace.-lon two malformed skippable frames (ruling T7-a): a size field of0xFFFFFFF8makes zstd loop forever, and stuffr answers exit 1 withcould not find end of skippable frame; a skippable frame cut after 5 to 7 bytes makes zstd read uninitialised stack, and stuffr reads a deterministic header. Neither upstream defect is reproduced.- At display level 5 and above,
EXM_THROW’sError defined at <file>, line <n>line is not printed (ruling T4-b): it names the C implementation’s source lines.
--train-cover(ruling T1-a): libzstd 1.5.7’s cover sort breaks ties by comparing addresses (cover.c:300,:316), so a cover dictionary depends on heap layout, in zstd itself too. It is promised valid and usable by zstd, not byte for byte.--train,--train-fastcoverand--train-legacywrite zstd’s bytes.--adaptchanges the level by timing, so its frames are compatible, not identical.
Spec deviation 7, the pure tier (zstd-pure without zstd-c), as far
as compressing and decompressing go (fileio.rs):
- Every level compresses with
ruzstd’sFastest, so the sizes and ratios in the summaries and in-vvvvvv’s trace are the tier’s own; the reference decodes every frame. The compression parameters are range-checked as libzstd checks them, then ignored:-T,--auto-threadsandzstdmtrun single-threaded, and the LDM,--rsyncable, block-size and overlap options have no effect. - Content size and window: our frames never record a content size, so
--content-sizehas no effect and-lon our own output says the decompressed size is unknown. The window is 128 KiB whatever--longasks. - Each input is compressed as one frame built in memory: memory use grows with the input’s size (a piped standard input is unbounded), and a run that exhausts memory ends outside zstd’s exit codes.
- Compressing with
-Dor--patch-fromis refused, after everything zstd prints first:zstd: error 1 : dictionary compression is not available in this build (stuffr pure tier). Decompressing with either works (a--patch-fromprefix through a hand-built raw-content dictionary, Task 1’s spike S1). --train*answers as a zstd built withZSTD_NODICT:--trainprintstraining mode not available, exit 1, and--train-cover,--train-fastcoverand--train-legacyare no options there (Incorrect parameter).-Hstill lists the dictionary builder.--adaptis accepted and the level never moves (the encoder has no jobs to measure).- A legacy frame (v0.5 to v0.7) is
unsupported format, what a zstd built without legacy support says. - A corrupt frame whose blocks overflow the output libzstd sized from
its header can be named
Data corruption detectedwhere libzstd saysDestination buffer is too small. --format=gzip|xz|lzmawrite flate2’s andxz_opts’ pure encoders’ bytes, which the reference decodes (compatible, not identical; the summaries’ sizes are the tier’s own).- A gzip member’s deflate data goes through flate2 (miniz_oxide), which
accepts some streams zlib refuses (a distance reaching before the
output, a code with no end of block): where the reference says
inflate error -3and writes nothing, the pure tier may write bytes and then fail (premature gz end), or, when the trailer’s CRC and size match what miniz_oxide decoded, succeed with exit 0 and that output. Withzlib-cit is zlib’s owninflate. - An xz or lzma frame that fails to decode (the xz family’s P6 and P9):
what is written before the error can be a strict prefix of what
liblzma writes, and corruption in the last bytes of a
.lzmareads as the input ending there (premature lzma end, the jobs before it written) where liblzma sayslzma_code decoding error 9.
The foreign formats on both tiers (formats.rs; spec “Promises”):
--format=gzip is zlib’s bytes with zlib-c, --format=xz|lzma
liblzma’s with xz-c; --format=lz4 is compatible only on every tier:
lz4_flex’s fast compressor at every level, inside liblz4’s frame
descriptor (64 KiB linked blocks, content size, content checksum as
fileio.c sets them).
Functions§
- run
- Entry point for the four names;
nameis the canonical invoked name,argv0the program as invoked (its last component is zstd’sprogramName) andargsthe arguments after it.