Skip to content
REFERENCE / ZLIB × LEAN

Format model in Lean

Source snapshot · 389491f870d55a07 · Source at the matching commit ↗

Documentation home · Data format · API guide

The format model describes how compressed bytes determine uncompressed bytes. It has no heap or pointers. It is a specification to refine, not an implementation of the streaming API. Read the format guide for the rules and the API model for calls, buffers and return values. Accepted results and remaining work live in progress.

The umbrella module is ZlibF/Spec/Format.lean. The token extension is imported separately. Definitions below are in namespace ZlibF.Spec, except the local token relations in ZlibF.Spec.Token.

Module map

Module Vocabulary
Bits.lean Byte-to-bit order, numeric readers, big- and little-endian byte encodings
Checksum.lean Bytewise Adler-32 and CRC-32, update functions and append laws
Huffman.lean Canonical codes, accepted length sets, fixed codes, length and distance tables
Deflate.lean Symbol and block relations, raw prefix and exact-stream relations
Wrapper.lean Zlib and gzip headers, trailers and exact-stream relations
Token.lean One-token decoding and rejection, with obligations connecting them to the block grammar
Examples.lean Concrete acceptance and rejection examples

From bytes to output

flowchart TD
    B["bytesToBits"] --> K["Blocks: header and body"]
    K --> S["Body.stored: aligned bytes"]
    K --> F["Body.fixed: fixed codes"]
    K --> D["Body.dynamic: lengths and codes"]
    D --> C["CodeLens: expand code-length symbols"]
    F --> Y["Symbols: literals, copies, end"]
    D --> Y
    S --> P["Deflate.Prefix: final padding and byte tail"]
    Y --> P
    P --> R["Deflate.Decodes: empty tail"]
    P --> Z["Zlib.Decodes: Adler-32 tail"]
    P --> G["Gzip.Decodes: CRC-32 and size tail"]

Readers such as readBits, readBytes and decodeSym return either none or a value paired with the remaining bits. Relation constructors require the appropriate reader equations. There is no constructor for an invalid block type or reserved length/distance symbol.

Symbols L D dict h inp rest out starts with history h, uses literal/length lengths L and distance lengths D, and ends at an end-of-block symbol. It consumes inp down to rest and leaves output out. copyBack appends each copied byte before reading the next one, so overlapping matches have their ordinary bytewise meaning.

Blocks dict h inp rest out composes bodies until a block with BFINAL = 1. The block grammar uses bits derived from whole input bytes. Under that entry condition, inp.length % 8 is the number of bits left before the next byte boundary; stored bodies drop that many bits.

The public format relations differ in what they require after the final block:

Relation Meaning
Deflate.Prefix dict stream tail out Decode blocks from the beginning of stream; ignore final byte padding; require the remaining bytes to equal tail
Deflate.Decodes dict stream out The same relation with tail = []
Zlib.Decodes dict stream out Exactly one zlib stream, with a checked header, optional dictionary ID and Adler-32 trailer
Gzip.Decodes stream out Exactly one gzip member, with its optional header CRC and CRC-32/size trailer

The wrappers use Deflate.Prefix with the required trailer bytes as its tail. They still describe exact streams: bytes after the trailer are excluded. An API theorem about input left after Z_STREAM_END needs a separate consumed-prefix statement.

Rule map

The F-* rules are defined in the format guide. This table locates their formal counterparts without making an implementation-correctness claim.

Rule Definition or obligation
F-bit-order byteBits, bytesToBits, readBits, natToBitsMSB
F-block-type-3 Body has no constructor for type 3
F-stored-complement Body.stored: nlen = 65535 - len
F-code-fits count, codeStart, codeOf, Fits
F-code-complete Complete, CodeKind, Acceptable
F-fixed-lengths fixedLitLens, fixedDistLens, Body.fixed; Symbols.copy restricts length and distance symbols
F-dynamic-limits DynamicCountsOK, required by Body.dynamic
F-dynamic-repeat CodeLens.rep16, .rep17, .rep18; previous-entry and total-count premises
F-dynamic-eob HasEndOfBlock, required by Body.dynamic
F-dynamic-valid Acceptable .codeLength, .litLen, .dist, required by Body.dynamic
Dynamic header order Body.dynamic, clOrder, readCodeLenLens
F-length-table, F-length-284 lengthBases, lengthExtras, Symbols.copy; base 227 plus extra value 31 remains 258
F-distance-table distBases, distExtras, Symbols.copy
F-match-copy copyBack
F-distance-bound Symbols.copy requires distance at most (dict ++ h).length
Block order and finality Blocks.more, Blocks.last
F-zlib-header Zlib.HeaderOK, Zlib.windowSize, Zlib.Decodes
F-zlib-check Zlib.Decodes requires tail be32 (adler32 out).toNat
Gzip header Gzip.header, header constraints in Gzip.Decodes
F-gzip-check Gzip.Decodes requires tail le32 (crc32 out).toNat ++ le32 (out.length % 2^32)
F-adler-incremental adler32Update, adler32Update_append
F-crc-incremental crc32Update, crc32Update_append
F-deflate-valid Deflate.Valid, Zlib.Valid, Zlib.ValidWith, Gzip.Valid
F-deflate-matches Compressor obligation; no extra restriction in the format grammar
F-deflate-flush Stream-sequence obligation; a completed flush is not a complete final stream

The Valid predicates reverse the emphasis of Decodes: the compressed argument must decode to the supplied input. They do not select one encoding. Zlib.ValidWith supplies the preset dictionary; the other listed Valid predicates use no dictionary.

Model choices and boundaries

History and small windows. Symbols.copy uses the full dict ++ h history, not an explicit saved-window state. The distance table caps a reference at 32768. The zlib header's window field limits the initial dictionary suffix, but does not add a distance premise to Symbols.copy. This keeps the existing model choice explicit: default decoding can use current-call output beyond a small saved window, while a later call may lack those bytes. The C also has a separate INFLATE_STRICT check. Refinement for all configured windows and output-buffer boundaries is unfinished. The declarative relation alone does not establish operational acceptance for every split.

Dictionaries. With FDICT, Zlib.Decodes dict checks the Adler-32 of the entire supplied dictionary but passes only its last Zlib.windowSize cmf bytes to the DEFLATE relation. Without FDICT, it passes [] and ignores dict. A dictionary with Adler-32 1 may be empty. Raw Deflate.Decodes takes its dictionary history directly. Gzip starts with empty history.

Table acceptance. Acceptable permits all-zero length sets for all three code kinds. Such a set gives no decodable symbol. The single used symbol of length 1 is allowed only for literal/length and distance kinds. HasEndOfBlock separately rules out an empty dynamic literal/length code. Neither table acceptance nor a local successful token implies a whole valid block.

Padding. Stored-block alignment and final-byte padding discard bits without requiring them to be zero. Extra complete bytes are handled by the explicit tail, rather than padding.

Gzip metadata. Gzip.Decodes requires four MTIME bytes, zero reserved flag bits, an extra-field length below 2^16, and no embedded zero in the chosen name or comment. FTEXT, MTIME, XFL and OS otherwise remain unconstrained. Witnesses for optional fields whose flags are clear do not contribute bytes; empty witnesses can satisfy their constraints. One relation covers one member, not a whole concatenated gzip file.

Local decoding. Token.Step describes one literal, match or end-of-block without assuming that later input is valid. Token.Rejected describes irreversible invalid-code or unavailable-history cases. The goals step_composes_symbols, symbols_exposes_step and rejected_has_no_symbols connect these local relations to the original Symbols grammar. This boundary prevents a valid future completion from serving as an assumption for output already returned by an API call.

What belongs outside the format relation

Concern Specification boundary
Error codes, diagnostic strings, Z_NEED_DICT and dictionary-call order Inflate API
Buffer sizes, partial input, automatic wrapper detection, priming and recovery Stream API and API model
Compression match choices, flush completion and output bounds Deflate API
Concatenated members, transparent reads and file errors Gzip file API
Checksum combination entry points Checksum API
Decode-table allocation and ENOUGH capacity bounds Table proof guide
Memory safety, ownership and termination of the translated implementation Safety and proof method

Examples.lean supplies concrete positive and negative checks of the specification. Its proofs use kernel evaluation and relation constructors or inversion. Such examples help review the definitions; they do not establish their correspondence to every RFC case or to the translated implementation. Differential tests likewise provide evidence of fidelity, not proof. See progress for the acceptance status of implementation refinements.

ZLIB × LEAN

Global contents

READING NOWFormat model in Lean