Expand description
The lz4 family (lz4, unlz4, lz4cat, lz4c): stuffr behaving as
the LZ4 command line interface v1.10.0.
A clean-room implementation (spec decision 3): lz4’s programs/ is
GPL-2.0-or-later and was never read. Every rule comes from the man page
(lz4.1), NEWS, README.md, lz4 -h/-H and probes of the 1.10.0
binary, and carries its source in a comment; every string is in
messages.rs. liblz4 itself (BSD-2-Clause, LICENSE-lz4) is reached
through stuffr::lz4_opts. cli.rs holds the command line and the
decision table for where output goes.
Names (spec section 1, R12): dispatch registers the four exact
names. The presets come from the invoked name’s last path component
(after / or \), matched as lz4 1.10.0 matches it, probed through
exec -a: the name itself, or the name followed by . and anything
(unlz4.exe, lz4cat.1), case-sensitively; unlz4-1, xunlz4 and
UNLZ4 match nothing. unlz4 decompresses; lz4cat is -dcfm at
display level 1; lz4c reads the legacy arguments (-c0, -c1,
-c2, -hc, -y) and lists them in its help.
The machine (R4): the automatic thread count is the logical cores
less an eighth, less one (cli::resolve_threads); the real process
counts its cores (host::cores), a sandboxed run
([run_in_counted]) sees 8 logical and 4 physical.
Signals (ruling T2-a): lz4 installs no handler; SIGINT, SIGTERM and
SIGHUP kill it and the partial output stays under its name (probed).
So nothing is armed here, and outputs are written directly to their
names (io.rs), never to a temporary file: an interrupt leaves what lz4
leaves. run restores SIGPIPE’s default action, as the earlier families
do (deviation 4).
Files (io.rs, decode.rs): compression, decompression and test
over files and streams, with lz4’s order of opening, its overwrite
question, -m/-r, --rm, sparse output and its display level 2
(ruling P3).
Known deviations (the source of truth; the README and the spec mirror this list):
- The banner names stuffr wherever lz4 prints it (
-V, the first line of-h/-H,-v):*** lz4 v1.10.0 64-bit multithread (stuffr X.Y.Z), by Yann Collet ***. The prefix*** lz4 v1.10.0is unchanged. - Timing and host fields vary, as between two runs of lz4: the
Done inline’s seconds, speed and cpu load, the progress refreshes, and theUsing N threadscount where two programs count the host’s cores differently. Refreshes are drawn at most every 200 ms (the first one always; at display level 4 every chunk), whichdisplay.rspins from recordings; under a sandbox the clock is frozen at zero. While compressing, a refresh that both draw shows the same figures (each job’s refresh is over the input read before that job,inffor the first, and a one-shot frame’s over all of it; the pure tier’s ratio is of its own bytes, deviation 6). While decoding, stuffr offers one refresh per MiB decoded, while lz4’s count and figures vary from run to run with its writer thread (probed: the same legacy file drew14, 14or8, 14MiB, a-B4frame4, 7, 8), so those are timing too. - An input that is also the output is never lost. Where the output
named is the input file itself (the same device and inode:
lz4 -f a a,lz4 -df s.lz4 s.lz4, a hard link, standard input redirected from it), lz4 truncates it before reading it, and the data is gone; stuffr opens nothing, printsNAME is the input file; not overwritten (stuffr)and counts that input as failed (exit 1, or one more in-m’s count), whatever-fsays. - Closed standard streams and a parent that ignores SIGPIPE behave as
in the earlier families: a closed pipe kills stuffr by SIGPIPE, as it
kills lz4 under the default disposition; where the parent ignores
SIGPIPE, lz4 answers
Error 70(decoding) orError 38(compressing) and stuffr still dies of the signal (the Rust runtime ignores SIGPIPE beforemain, so the inherited disposition cannot be told apart). A closed standard input or output is reopened on/dev/nullby the runtime beforemain, solz4 -c <&-(lz4:Error 40) compresses an empty input. - Out of scope and refused:
-b#printsBenchmark mode is not available in this build (stuffr)and exits 1, once the whole command line has parsed (a usage error,-hor-Vstill answers first).-e#and-i#, the benchmark’s own settings, are accepted and have no effect without it, as in lz4.-hand-Hstill list all three (R8). The hidden-S(a benchmark switch with no effect elsewhere) is accepted and has no effect, as in lz4. - The pure tier (
lz4_flex): levels 3 to 12,--bestand--favor-decSpeedcompress with its single level; a corrupt frame’sError Ntext may differ, and output written before it may be a strict prefix of lz4’s. The summary’s compressed size and ratio are those of its own bytes. - A decode that fails keeps everything decoded before the fault in its output (a file or standard output). lz4 writes on another thread and exits from the failing one, so it leaves nothing or all of that, run to run (probed: 0 or 8800 bytes of 8800 for the same input).
--listof a file that ends inside a block-size word: lz4 completes the word from an uninitialised buffer, which only the-vsize of that last frame shows. On macOS that is garbage once a frame or skippable frame came before (probed: the same run to run, about 4.4 million, for a given shape) and zero before; on Linux it is garbage even in the first frame, different on every run (probed: the tarball build,lz4 --list -vof a header and one byte gave sizes from 1.0 to 2.1 billion; CI run 39166896170). stuffr completes it from the frame’s previous block-size word, as lz4 does for a second block (zero for the first).--listof a regular file that cannot be opened (mode 000): lz4 aborts on an assertion (exit 134); stuffr names the error (NAME: Permission denied) and exits 1.
Functions§
- run
- Entry point for the four names;
nameis the canonical invoked name,argv0the program as invoked (its last component is the program name the usage texts print, and picks the presets) andargsthe arguments after it.