znippy-zoomies — design
Class:
design· Function axis:f0(zoomies as one system) + the parts:linflatecore · the decoder family · the candidate-offset output model (B6) · thegatlingengine ·stree. Strict: this file states only the shape we run. Every why, every alternative, lives inzoomies-reasoning.md; the how-to inzoomies-guide.md; the measured numbers inbenchmarks.md.
f0 — zoomies as one system (the whole)
znippy-zoomies is a sibling repo consumed as path deps: pure-Rust parallel decoders and fast primitives that znippy leans on. One invariant runs through everything: a decoder takes a borrowed &[u8] and never copies its input. That single rule is what lets a 164 GB planet file stream through on one read-copy — bytes are read into a slot once, sliced, decoded in place, and stitched, with no intermediate owning buffer.
The workspace is tokio-free except the iceberg crate (and the async gatling::io sibling). The pieces, and what each uses:
| part | crate/file | role |
| linflate | linflate/ |
the shared SIMD DEFLATE core — every decoder inflates through it |
| decoder family | ljar lgz lbzip2 lzip-parallel |
format front-ends (JAR / gzip / bzip2 / ZIP), all on linflate |
| candidate-offset model (B6) | ljar/src/chunk.rs, ljar/src/bin/ljar.rs |
the zero-copy parallel decode layout |
| gatling | src/gatling.rs, src/gatling_io.rs |
generic no-barrier streaming engine (sync + async) |
| stree | src/stree.rs |
static search tree for id→position lookup, re-exported into osm-katana |
Consumed by: znippy proper (decompress path), and osm-katana (OSM→GeoParquet/PBF), whose xml→pbf path rides gatling and whose node store uses stree (re-exported as stree64).
linflate — the shared inflate core
linflate/ is a pure-Rust, full-buffer, SIMD DEFLATE decoder: the libdeflate full-buffer model (no streaming window) fused with zlib-ng branchless tricks. Hot working set ≈ 13 KB — it fits in L1-D. Files:
| file | holds |
bitreader.rs |
64-bit shift register; branchless refill guaranteeing ≥56 bits after refill, one refill per symbol |
tables.rs |
packed-u32 Huffman; 11-bit litlen first-level table; thread-local table pool |
fixed.rs |
the static fixed-Huffman tables |
copy.rs |
const-generic copy::<N> SIMD match copy → vmovdqu |
fastloop.rs |
the hot loop: preload-before-copy, 3-literal unroll, #[cold] slow paths |
lib.rs |
the API |
API (linflate/src/lib.rs):
| fn / const | signature | note |
OVERWRITE_HEADROOM |
pub const = copy::CHUNK_SIZE + 258 (:43) |
part of the API contract — every output region must include it so SIMD copies never scribble into a neighbour; undersized ⇒ OutputOverflow, not UB |
inflate_into |
(data, &mut [u8]) → Result (:81) |
decode into a caller buffer |
inflate_segment |
(…) (:93) |
decode a slice that need not end on BFINAL=1 |
inflate_segment_with_prefix[_limited] |
(:108, :120) |
segment decode with a back-reference prefix window |
inflate_to_vec |
(data, expected) → Vec<u8> (:206) |
allocates expected + OVERWRITE_HEADROOM, set_len(expected) |
| error | InflateError::OutputOverflow (:53) |
undersized output |
inflate_segment decoding a slice that does not require BFINAL=1 is the linchpin of parallel decode: mid-stream segments — split at Z_FULL_FLUSH boundaries by deflate_scan — are each independently decodable.
the decoder family
Four format front-ends, one shared core. Each takes a borrowed &[u8]:
| crate | format | parallelism |
ljar |
JAR | parallel (per-entry + per-chunk) |
lgz |
gzip | parallel (flush-marked, concatenated members, and arbitrary markerless via speculative resync) |
lbzip2 |
bzip2 | parallel over BWT blocks |
lzip-parallel |
ZIP | parallel over entries |
All four inflate through linflate; none owns a second DEFLATE implementation.
the candidate-offset output model (B6)
The zero-copy parallel decode layout. One pre-allocated buffer; the flow:
- Candidate offset per segment = prefix sum of generous caps (not true sizes).
- Decode all segments in parallel, in place, each at its candidate offset — zero per-segment allocation (
ljar/src/chunk.rs:220,output.resize(acc, 0)pre-allocates the whole candidate region once). - Forward-seek compaction (
chunk.rs:267): walk segments in order, moving each down to its true cumulative offset. Becauseactual ≤ cap, every move is leftward, so nothing not-yet-moved is ever clobbered. Thenset_len(total). - Spill path (
chunk.rs:258): a segment that outgrows its cap spills to an ownedVec(rare) → sequential rebuild. It never corrupts; output is byte-identical.
Two implementations of this model:
| impl | where | shape |
| library | ljar::chunk::decode_chunk_into (ljar/src/chunk.rs:173) |
rayon fork-join per chunk |
| binary | ljar/src/bin/ljar.rs |
woven into the no-barrier 5-thread pipeline Reader → Main(carry+split) → N Workers → Collector → Writer (232 MB ring slots) |
the gatling engine
src/gatling.rs — a generic, no-barrier streaming engine. A Read source is split into segments in a bounded zero-copy slot ring; N worker threads transform each segment with no cross-worker barrier; an ordered collector reassembles in stream order and hands to a Sink. Two modes:
| mode | driver | workers produce | collector |
| byte | run + Codec (:68) / Sink (:83) |
Vec<u8> |
concatenate + safe_end cut |
| typed | run_typed + TypedCodec (:99) / TypedSink<T> (:127) |
arbitrary Output |
forward each in order, no reassembly (split pre-cuts logical segments) |
Config (:156): chunk_size, carry_headroom, ring_slots, initial_carry, slot_fill (slot size = carry_headroom + chunk_size). SlotFill (:141): Incremental grows a slot to the bytes read (small/partial inputs); Big sizes once and reuses without re-zeroing (planet-scale).
Async sibling src/gatling_io.rs (gatling::io) — same invariants on tokio: run / run_ordered, bound by max_in_flight; payloads are bytes::Bytes moved, never cloned. For I/O-bound work (e.g. S3 multi-PUT).
The 1→32→1 principle: 1 reader only reads (a cheap O(n_workers) candidate-offset split, no CPU work); N workers carry a unit through all heavy stages — boundary-find, decode, parse, encode, sort — with no barrier and self-dispatch; 1 writer only stitches in order. Heavy work on a 1-end is the serial ceiling.
stree
src/stree.rs — a static search tree for id→position lookup under the OSM node store, re-exported into osm-katana as stree64. A cache-line S⁺-tree with branchless SIMD compare and prefetched batch traversal.
| type / fn | signature | note |
STree64 |
pub struct (:55) |
in-memory tree |
STree64Mmap |
pub struct (:175) |
internal nodes only; mmap is the leaf layer |
find_exact |
(q) → Option<usize> (:123) |
serial lookup |
batch_stream::<P> |
(&[i64]) → Vec<Option<usize>> (:773) |
software-pipelined + prefetched |
lookup_batch_pipeline::<16> |
(&[i64], mmap) → Vec<…> (:387) |
mmap-backed batch |
count_lt |
AVX2, dispatched once per query/batch (count_lt_avx2) |
branchless node compare |
advise_hugepage |
(&[T]) (:487) |
THP hint for the RAM-bound regime |
Derived from Ragnar Groot Koerkamp's static-search-tree (MIT, © 2025) — acknowledged in stree.rs:3. Our contribution at i64 width: B=16 (two cache lines) internal nodes.