ZINFInflate wraps output on fill, never signals NOROOM
ZINFInflate (dog/git/ZINF.c) mishandles a too-small into slice: when avail_out hits 0 it WRAPS next_out back to into[0] and keeps inflating (corrupting already-produced bytes), then the trailing u8sFed(into, total_out) — all-or-nothing — no-ops because total_out exceeds capacity, so the head never advances and the call returns OK with produced 0. A genuine empty result also yields 0, so overflow is undetectable by the caller. Surfaced by JS-035 (zip.inflate grow-and-retry), which had to add an XOR head-sentinel hack to guess at overflow. Method: Issues.
avail_out == 0 reset block, dog/git/ZINF.c:31-34, inside the
inflate loop (lines 27-35); resets next_out/avail_out instead of erroring.
into to the object's known uncompressed length
(keeper/UNPK.c:336,618, dog/git/PACK.c:177), so the wrap is LATENT there; it only bites a caller that doesn't know the size up front.
js/zip.cpp works around it with a head sentinel (`into0 ^=0xa5`, re-checked after the call) — that hack is deletable once this lands.
into holds, return a NOROOM-style error
(new ZINFROOM) so the caller can grow and retry — never wrap or corrupt.
OK, total_out equals the bytes actually present in into; the successpath stays byte-identical for the pre-sized native callers.
dog/git libdog, ABC style; new code RON60-encoded, ZINF-prefixed
(abc/ok64), declared in ZINF.h beside ZINFFAIL/ZINFINIT/ZINFTOOBIG.
OK).inflate returns Z_OK with avail_out == 0
(stream not ended, no room left) → inflateEnd(&zs); return ZINFROOM;.
u8csUsed/u8sFed only on the Z_STREAM_END success path.keeper/test/ZINF.c case inflating a known stream into an
undersized into expects ZINFROOM (today: OK + produced 0 + corruption).
ZINFROOM to ZINF.h; fix the loop in ZINF.c; ctest green across
ZINF/PACK/UNPK (the pre-sized callers must stay green).
zip.cpp:46-55): drop the XOR hack, grow directly on a ZINFROOM throw.
(ZINF.c avail_out==0 reset) and ZINF.h declares no ZINFROOM.