The reader used miniz_oxide, which accepts a dynamic block whose lone distance
code is longer than one bit. zlib's inflate, and so pack_verify.py, refuses
that block. A golden resealed with such a still verified in wai-pack and was
refused by pack_verify. Both readers now refuse incomplete code sets exactly as
zlib does: the only incomplete literal/length or distance code allowed is one
whose longest code is one bit.
The reader now uses its own inflater (png::inflate_zlib), written from RFC
1950/1951 to zlib's inflate rules:
- the header's check bits, method, window and preset-dictionary flag;
- the stored-block complement;
- the symbol counts;
- a complete code-length code;
- repeats with a length before them and within the code lengths;
- a code for the end of the block;
- over-subscribed and incomplete codes;
- a code with no symbol, or a distance past the bytes written, refused where it
is read;
- the Adler-32, with nothing after it.
miniz_oxide is kept only as a dev-dependency: the writer's round-trip test reads
its stills back with an inflater independent of the reader's.
packaging §5.7 now states these rules instead of "any PNG reader", and says
that a still the verifier does not read is prefix-digest.
stills/ gains the same dynamic block twice: a lone one-bit distance code
(read) and a lone two-bit one (refused, still-incomplete-dist).
png_diff.py drives wai-pack's reader (a new stage-json op, read-png) against
pack_verify.py's. The inputs are:
- zlib's own streams at every level, strategy and window;
- the packager's own stills;
- dynamic blocks built on each code-set rule (22 kinds, each checked against
the verdict the rules give it);
- mutations of all of these.
wai-pack.yml runs it in debug and release.
Co-Authored-By: Claude Opus 5.5 <[email protected]>