Gunnar

nordisk/znippy-zoomies

.nornir/lgz-design.md

# lgz — design

> **Class:** `design` · **Function:** `lgz` (multi-member gzip decode). Strict: the
> shape we ship, every claim carrying a real `file:symbol`. How to run it in
> [`lgz-guide.md`](lgz-guide.md); why gzip is only *partly* saturable in
> [`lgz-reasoning.md`](lgz-reasoning.md). The family view is
> [`decompressors-design.md`](decompressors-design.md); the engine is
> [`gatling-design.md`](gatling-design.md).

![lgz exact dataflow](assets/lgz_flow.svg)

## What it is

A pure-Rust parallel decoder for the *stock* `.gz` format — anything `gzip`/`pigz`
wrote, decoded across every core. The CLI is `lgz/src/bin/lgz.rs`; the library twin
is `lgz::decompress_tar_gz_filter` (`lgz/src/lib.rs:63`) / `_capped`
(`lgz/src/lib.rs:76`) and the in-memory `decompress_gz` (`lgz/src/parallel.rs:108`).

gzip has **two** kinds of parallelism, and lgz picks the one the input actually
offers, per invocation:

## Path 1 — concatenated members (the fast, fully-parallel path)

`try_multimember_fastpath` (`lgz/src/bin/lgz.rs:200`). The common `.gz` from log
rotation, `cat *.gz`, `pigz --independent`, or a member-per-file `.tar.gz` is a run
of **fully independent** gzip members. lgz `mmap`s the file and scans for every
member start with `lgz::speculative::find_gzip_members_parallel`
(`lgz/src/speculative.rs:471`) — itself a `gatling_for_each` fan-out over the box
(header-shape + inflate probe per candidate). `< 2` members ⇒ not this path.

Each member's uncompressed length is its own 4-byte **ISIZE** trailer, so the exact
output byte-layout is known *without decoding*. From there the two output shapes
diverge:

- **`-o file`** → pre-size the output (`set_len(total)`), then
  `gatling::parwrite::write_segments_positional` (`gatling/src/parwrite.rs:191`) fans
  the members across the pool: each worker decodes one member into a **reused**
  scratch buffer and immediately `pwrite`s it at its prefix-sum offset. No collector,
  no serial concat — in-flight memory ≈ `workers × member_size`, never O(output).
  The decode validates its length against ISIZE and fails the segment on mismatch.
- **stdout / pipe** (cannot seek) → `gatling::gatling::ordered::run_ordered_sink`
  (`gatling/src/gatling_ordered.rs:113`) streams members to a worker pool; the
  ordered sink writes each the instant it is next-in-order **while the other workers
  keep decoding**. A `Mutex<Vec<Vec<u8>>>` **recycled buffer pool** (`lgz.rs:350`)
  hands each drained, warm, ISIZE-presized buffer back to the next member — zero
  steady-state allocation and no per-member page-fault storm (the old
  `Vec::with_capacity`+drop churned 130 K+ minor faults on the kernel `mmap_lock`,
  capping saturation at ~8.5/12).

## Path 2 — single member with flush boundaries (streaming)

`main`'s streaming pipeline (`lgz/src/bin/lgz.rs:420`). A single-member stream can
still be split *iff* the encoder inserted `Z_FULL_FLUSH` points (pigz does). A single
**dispatch thread** is the producer: it reads 200 MB chunks (`CHUNK_SIZE`), prepends
the undecoded **carry** into headroom, strips the gzip header (first chunk) and 8-byte
trailer (last chunk), and calls `split_chunk` (`lgz/src/chunk.rs:29`) to cut at flush
boundaries. It distinguishes the two flush kinds by the bytes after the boundary
(`lgz.rs:562`):

- **full-flush** (`00 00 00 FF FF` — empty stored block resets the LZ77 window) ⇒ the
  segments are independent, queued one at a time to `run_ordered_sink`; N map workers
  run `decode_segment_into_hint` (`lgz/src/chunk.rs:79`); the `WriteSink`
  (`lgz.rs:62`) writes in strict stream order.
- **sync-flush** (back-references cross the boundary) ⇒ segments are *not*
  independent, so the whole chunk decodes as **one** segment (still overlapping I/O
  with the producer).

## Path 3 — no boundary at all (serial fallback, honest)

A plain single-member gzip (`gzip foo`) has **no** interior split point — one
continuous LZ77 window. When the producer finds no boundary in the whole file it sets
`fallback_flag` and `main` (`lgz.rs:666`) tries, in order:
`decode_concatenated_members` → `speculative_decode` (DEFLATE block-boundary guess,
**trailer-verified before a byte is emitted**, `lgz.rs:161`) → single-threaded
`flate2`. This path is **serial-by-format**, not a saturation bug (see
[`lgz-reasoning.md`](lgz-reasoning.md)).

## Correctness

Every emitted byte is checksum-verified: the multi-member `-o` path checks each
member's decoded length against ISIZE; the in-memory `decompress_gz` re-checks the
whole-output CRC32 + ISIZE (`lgz/src/parallel.rs:131`); the speculative path is
discarded unless `gzip_trailer_ok` passes.

## Whereabouts

| Thing | file:symbol |
|---|---|
| CLI entry / arg routing | `lgz/src/bin/lgz.rs:420` (`main`) |
| multi-member fast path | `lgz/src/bin/lgz.rs:200` (`try_multimember_fastpath`) |
| member scan (parallel) | `lgz/src/speculative.rs:471` (`find_gzip_members_parallel`) |
| flush split | `lgz/src/chunk.rs:29` (`split_chunk`) |
| per-segment decode | `lgz/src/chunk.rs:79` (`decode_segment_into_hint`) |
| ordered streaming sink | `gatling/src/gatling_ordered.rs:113` (`run_ordered_sink`) |
| positional pwrite | `gatling/src/parwrite.rs:191` (`write_segments_positional`) |
| speculative + trailer check | `lgz/src/speculative.rs:256`; `lgz/src/bin/lgz.rs:161` |
| library API | `lgz/src/lib.rs:63` / `:76`; `lgz/src/parallel.rs:108` |

## Saturation state (just-landed)

Multi-member `-o` decode reaches **~7.8/12 cores** on the quiet box (stdout can't be
offset-written, so it uses the ordered-sink path); single-member gzip is
serial-by-format and exempt. Honest bench numbers land in the
[`README.md`](README.md) A/B/C mashup on the next heavy-bench wave — this doc records
no fabricated `cores_busy`.