Skip to content

WI-144: in-flight PAR2 slice verifier core (nzb-decode) - #141

Closed
thedancingdeveloper wants to merge 1 commit into
feat/wi-143-damage-ledgerfrom
feat/wi-144-slice-verifier
Closed

WI-144: in-flight PAR2 slice verifier core (nzb-decode)#141
thedancingdeveloper wants to merge 1 commit into
feat/wi-143-damage-ledgerfrom
feat/wi-144-slice-verifier

Conversation

@thedancingdeveloper

Copy link
Copy Markdown
Collaborator

Second code payload of the parity-in-flight initiative (Vogt WI-144). Stacked on #140 (WI-143) — base is feat/wi-143-damage-ledger, so this diff is only the slice verifier. Retarget to main once #140 merges.

What

The computational core of in-flight PAR2 verification: SliceVerifier in crates/nzb-decode/src/slice_map.rs turns article writes into a per-slice Verified / Damaged / Unknown map, so a clean job can skip the post-download verify pass (WI-147) and damage can be weighed against recovery blocks during the download (WI-145).

Why not incremental hashing: articles land out of order at arbitrary offsets, and MD5 is not associative — a slice can't be hashed from fragments. The verifier tracks per-slice filled bytes; once a slice's range is complete the caller reads it back (page-cache-hot) and calls verify(), which zero-pads the final partial slice to slice_size (matching PAR2 IFSC) and compares MD5 + CRC32.

Dependency-light: the verifier holds only plain geometry (SliceLayout = slice_size + per-file size + IFSC digests), so nzb-decode gains no PAR2-parser dependency — only md-5 (crc32fast was already present). The download engine will build the layout from a parsed rust-par2 file set when it's wired in (follow-up).

API: new / mark_written (→ completed global indices) / slice_range / verify / mark_damaged / states / damaged_count / total_slices / is_complete.

Tests

5 deterministic unit tests: in-order writes, an article spanning two slices with out-of-order fill, corruption → Damaged, partial fill stays Unknown, direct mark_damaged. Full nzb-decode suite 35 passed. cargo fmt --check + cargo clippy -D warnings clean. Additive public surface.

Notable finding

rust-par2 0.1.3 (the pinned version) already exposes what WI-144 needsparse_par2_reader, Par2FileSet.slice_size / file_order / recovery_block_count, and per-file Par2File.slices: Vec<SliceChecksum{md5,crc32}>. So WI-144 does not need the unpublished rust-par2 0.2 (WI-152/153) first.

Follow-up (WI-144 integration, not in this PR)

Download-engine PAR2-index parse + layout build, the 16 KiB MD5 file-matcher move (nzbdav-pipeline → nzb-core), the assembler hook (mark_written/verify), emitting the slice map to WI-145/147, and benchnzb. Needs WI-151 generated PAR2 fixtures for the end-to-end "agrees with rust-par2 verify" acceptance.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TpoFnScdkRq7qxQLQZe1we

Add the computational core of in-flight PAR2 verification: a slice
verifier that turns article writes into a per-slice Verified / Damaged /
Unknown map, so a clean job can skip the post-download verify pass
(WI-147) and damage can be weighed against recovery blocks during the
download (WI-145).

Articles land out of order at arbitrary offsets, and MD5 is not
associative, so a slice cannot be hashed incrementally. Instead each
slice tracks how many of its bytes are filled; once its range is
complete the caller reads it back (page-cache-hot) and calls verify(),
which zero-pads the final partial slice to the slice size — matching
PAR2 IFSC — and compares MD5 and CRC32.

The verifier holds only plain PAR2 geometry (SliceLayout), so nzb-decode
gains no PAR2-parser dependency; the download engine will build the
layout from a parsed rust-par2 file set and the NZB-file mapping when it
is wired in (follow-up). Adds md-5 alongside the existing crc32fast.

Six-path unit coverage: in-order writes, an article spanning two slices
with out-of-order fill, corruption -> Damaged, partial fill stays
Unknown, and direct mark_damaged. Full nzb-decode suite 35 passed; fmt
and clippy -D warnings clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TpoFnScdkRq7qxQLQZe1we
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant