Skip to content
REFERENCE / ZLIB × LEAN

Fast decoding: margins, history and source bits

Source snapshot · 1e744b9c0df86908 · Source at the matching commit ↗

Source: zlib/inffast.c; translation: ZlibF/Zlib/inffast.c.lean. The contract vocabulary is I-if-* in internal contracts. Table predicates come from table construction. Window invariant W is explained in stream decoding.

This is an internal function. A theorem under I-if-pre-* does not prove that all legal inflate calls establish those premises. A source-bit preservation result also needs a bridge to the independent format semantics. See progress for the current scope of those results.

Entry views and loop invariant

I-if-pre-room gives mode LEN, at least six input bytes and 258 output bytes, and a readable earlier-output prefix of the outer inflate call in the same output object. Input and output are disjoint. I-if-pre-bits gives bits < 8 and identifies hold with exactly those low buffered bits. I-if-pre-tables supplies initialized, meaningful root/subtable entries. I-if-pre-window supplies the valid history and the default sane-distance mode.

The source forms

last = in + (avail_in - 5)
end  = out + (avail_out - 257)
beg  = out - (start - avail_out)

Here beg starts the outer call's output, not necessarily this fast call's output. The precondition must justify its pointer subtraction for the actual buffer view. Write in0, out0 for the fast-call cursors.

At each body entry, use these named invariants:

Name Content
I1 in0 ≤ in ≤ in0 + avail_in; the loop margin gives six readable bytes at the cursor.
I2 out0 ≤ out; the loop margin gives 258 writable bytes at the cursor.
I3 hold < 2^bits, with body bit counts at most 30 and entry bits below 8.
I4 beg ≤ out; the range [beg, out) has been produced by the outer call.

The first body is reached by a do loop, so entry premises must supply its margins directly. Later bodies use in < last && out < end.

Bit counts and the six-byte bound

There are four refill sites for a length/distance pair.

Site Guard and read count Useful bound
R1, before length lookup bits < 15: read two bytes Refill leaves 16 to 30 bits.
R2, length extras bits < op, op ≤ 5: read one byte Refill leaves at most 12 bits.
R3, before distance lookup bits < 15: read two bytes Refill leaves 16 to 30 bits.
R4, distance extras bits < op, op ≤ 13: up to two bytes The guarded refills keep the count at most 28.

If R1 or R3 is skipped, at least 15 bits are already available. Table meanings bound a complete code, including its possible subtable lookup, by 15 bits. R2 provides enough for at most five extras; R4 provides enough for at most 13. Thus each bits -= op removes only available bits. Refills preserve I3, and the associated shifts stay within their machine widths.

The independent maxima would allow seven input bytes. That combination is impossible. Suppose R1, R2, R3 and R4 read 2, 1, 2 and 2 bytes. After R1 and a length code of at most 15 bits, at least one bit remains. After R2 and at most five length extras, at least 1 + 8 - 5 = 4 remain. After R3 and at most 15 distance-code bits, at least 4 + 16 - 15 = 5 remain. A second R4 byte requires fewer than five before its first refill, because even that refill left fewer than op ≤ 13. Contradiction.

Each body therefore reads at most six bytes. This is the reason for last's five-byte subtraction. A literal writes one byte; a match writes at most 258, from the length table's base/extra interpretation. This justifies end's 257-byte subtraction. The room bounds depend on table semantics, not merely on allocated table capacity.

Table reads

A root index hold & ((1 << rootbits) - 1) is below 2^rootbits. T2 supplies its initialized entry. A link supplies a bounded subtable interval; the masked subindex lies inside that interval. The table predicate must also establish the entry's operation, code length and base/extra ranges and that a second-level entry is not another link. The goto dolen/dodist paths then terminate after at most one linked lookup.

Reserved symbols reach the correct error exit. End-of-block reaches the mode transition prescribed by the caller contract. These exits still require exact cursor, bit-buffer and state effects.

Match copies: F3 and F4

Let done = out - beg, dist ≥ 1 be the decoded distance and 3 ≤ len ≤ 258 the match length.

If dist ≤ done, the source starts at out - dist ≥ beg. The copy is sequential: at step k its source is the already produced byte at relative position k - dist. That byte precedes the destination even when source and destination ranges overlap. This is the DEFLATE repetition rule; replacing the loop by a disjoint-copy argument would lose it.

If dist > done, set op = dist - done. The sane-distance check rejects op > whave. After the check, 1 ≤ op ≤ whave ≤ wsize, so the allocated window contains the required earlier history. Its three source shapes are:

Condition Initial window index Boundary argument
wnext == 0 wsize - op Copy through the end of the ring.
0 < wnext < op wsize + wnext - op Copy op - wnext bytes through the end, then up to wnext from index 0.
wnext ≥ op wnext - op Copy before the current ring cursor.

Each piece copies only the smaller of its history count and the remaining match length. If the match outlasts the window contribution, its remaining source is in output already produced by this call, where the sequential-copy argument applies. Prove the exact unrolled loop counts, including the positive initial count for every do … while (--op).

F4 says whave < wsize implies wnext = whave. In a partial ring, only the third shape is possible; its source lies in initialized history. In a full ring, all bytes are history and the wrap shapes are valid. The stronger functional invariant must identify those bytes with the suffix of the logical output and dictionary history.

Rollback and final writeback

The exit returns speculative whole input bytes:

len = bits >> 3
in -= len
bits -= len << 3
hold &= (1U << bits) - 1

If B bytes were read by this fast call, the accounting invariant gives bits ≤ bits_entry + 8B < 8 + 8B. Hence len ≤ B and rollback never precedes in0. The result has fewer than eight buffered bits, so the final mask shift is valid. The function then writes exact remaining input/output counts and cursors, hold, bits and the reached mode back to the stream.

bits_entry < 8 is essential. Without it rollback could return bytes that this fast call never read. unfinished: the main decoder establishes that premise on every actual transition/call into this fast path, including resumed calls and fixed/dynamic table installation. A general observation about refill/drop macros is insufficient without checking those paths and buffer views.

From safety to decoding

The functional loop invariant must identify the consumed low bits with the original source stream and relate emitted literals/matches to independent format symbols. Preserve that relation through length/distance selection, extra-bit reads, all copy shapes, linked tables, end and error exits, rollback and final writeback. The source relation must survive whole-body and whole-call composition, rather than only a successful local token.

Internal source-origin lemmas can establish exact selected bits for their stated domains. Connecting them to init-established state, installed tables, all legal caller views and independent output semantics is a separate refinement obligation. Speculative lookahead is not automatically justified by the bytes supporting a decoded symbol; the causal distinction is explained in inflate.

For termination, a body writes at least one byte or exits on end/error. The finite output range bounds the number of continuing bodies. Copy loops and linked lookups also have bounded measures. Translate those bounds into the actual fuel budget and preserve the frame on every exit. No allocation occurs here; ownership of the existing state/window remains unchanged.

C correspondence checks

Fast decoder: margins, copies and rollback

inflate_fast enters in LEN, with at least six input bytes, 258 output bytes, start ≥ avail_out, and fewer than eight buffered bits. inflate checks all these margins. inflateBack checks the byte margins but relies on its mode transitions for bits < 8: unfinished, including dynamic tables and stored blocks.

last = in + (avail_in - 5) and end = out + (avail_out - 257) use unsigned subtraction. Their safety depends on the entry margins. Each iteration reads at most six bytes and writes at most 258. beg = out - (start - avail_out) names the beginning of this call's output. The two callers use start equal to the original output room or the callback window size.

here is a pointer to a table entry here; the slow decoder copies an entry value. dolen and dodist preserve the backward jumps for links. A valid table bounds link depth; fuel in the translation does not itself prove termination. Error and end-of-block breaks skip the loop condition. Pointer comparisons in that condition and in final accounting must compare pointers in the same object.

Copies from the window and current output retain the source's unrolled loops and read/write order. Overlapping output matches are intentional: a newly written byte can be read by a later copy step. They cannot become memcpy or a snapshot of the initial source range. The minimum match length is three. Every pre-decrement copy loop needs a positive count.

On every exit, including invalid symbols and too-far distances, the epilogue returns whole unused bytes to the input, retains only bits mod 8 low bits, and writes back pointers and remaining counts. A semantic proof must track the exact source-bit prefix through this rollback, not just the number of consumed bytes.

ZLIB × LEAN

Global contents

READING NOWFast decoding: margins, history and source bits