par2_rs/lib.rs
1//! General-purpose PAR2 verification and repair engine.
2//!
3//! A pure-Rust implementation of PAR2 (Parity Archive Volume Set v2.0): load a
4//! set, find out what is damaged, and repair it from the recovery data.
5//!
6//! # Verifying a set
7//!
8//! A PAR2 set is usually spread across several `.par2` files. Packets from all
9//! of them aggregate into one [`Par2FileSet`], and verification runs against
10//! that.
11//!
12//! ```no_run
13//! use par2_rs::{DiskFileAccess, Par2FileSet, Repairability, scan_packets_from_path, verify_all};
14//!
15//! # fn main() -> par2_rs::Result<()> {
16//! let packets = scan_packets_from_path(std::path::Path::new("release.par2"))?
17//! .into_iter()
18//! .map(|(packet, _offset)| packet)
19//! .collect();
20//! let set = Par2FileSet::from_packets(packets)?;
21//!
22//! let access = DiskFileAccess::new("/downloads/release".into(), &set);
23//! let result = verify_all(&set, &access);
24//!
25//! println!("{} recovery blocks available", result.recovery_blocks_available);
26//! match result.repairable {
27//! Repairability::NotNeeded => println!("everything verified clean"),
28//! Repairability::Repairable { blocks_needed, .. } => {
29//! println!("repairable: {blocks_needed} blocks to rebuild")
30//! }
31//! Repairability::Insufficient { deficit, .. } => {
32//! println!("not enough recovery data: {deficit} blocks short")
33//! }
34//! other => println!("{other:?}"),
35//! }
36//! # Ok(())
37//! # }
38//! ```
39//!
40//! Verification is **slice-level**, using the CRC32 + MD5 pairs in IFSC packets,
41//! so damage is localised to the slices that are actually wrong rather than
42//! condemning the whole file. Sets carrying no IFSC data fall back to full-file
43//! MD5, and [`quick_check_16k`] identifies a candidate file cheaply before
44//! either.
45//!
46//! # Verifying bytes that are not files
47//!
48//! [`verify_all`] reads through the [`FileAccess`] trait, not the filesystem.
49//! [`DiskFileAccess`] is the ordinary implementation; supply your own and a set
50//! can be verified against bytes still arriving over a network, or assembled
51//! from somewhere that has no paths at all. [`MemoryFileAccess`] is useful in
52//! tests.
53//!
54//! # Repair
55//!
56//! [`Par2Repairer`] drives the whole sequence — scan, verify, solve, repair,
57//! then verify again. Repair is placement-aware: files that were renamed or
58//! moved are matched by content rather than by name, so a set still repairs
59//! after its files have been reorganised.
60//!
61//! # Repairing across a whole download
62//!
63//! [`Par2RepairSession`] is the retained form: one session accumulates
64//! evidence — per-slice verdicts, whole-file proofs — while the data is still
65//! arriving, so assessment is incremental and repair runs from what is already
66//! known instead of a fresh walk. Its sources may be files under a base
67//! directory, or bytes served through a [`FileAccess`] handle
68//! ([`Par2RepairSessionOptions::with_source_access`]) for sets that never
69//! became files — and where the `.par2` volumes themselves never became files
70//! either, [`Par2RepairSessionOptions::from_set`] takes the parsed set
71//! directly. Repair *output* is always real files either way.
72//!
73//! # Damaged PAR2 files
74//!
75//! A malformed or truncated packet does not fail the set. The scanner skips
76//! forward to the next valid packet, because the recovery data that survived is
77//! usually still enough — which is the entire point of parity.
78//!
79//! # Feature flags
80//!
81//! - `crypto-aws-lc` *(default)*: AWS-LC-backed MD5. Needs a C toolchain to
82//! build `aws-lc-sys`, and is the configuration the published performance
83//! figures were measured with.
84//! - `crypto-rust`: the portable RustCrypto (`md-5`) MD5 backend, for builds
85//! that must not carry a C/assembly dependency and for `wasm`, where AWS-LC
86//! is unavailable. Select it with
87//! `default-features = false, features = ["crypto-rust"]`. Expect slower
88//! hashing; nothing else changes.
89//! - `native-crypto`: back-compat alias for `crypto-aws-lc`.
90//!
91//! Exactly one backend is active: on a native target AWS-LC wins whenever
92//! `crypto-aws-lc` is on, and enabling neither backend is a compile error.
93//! - `metal` / `wgpu`: GPU-accelerated repair through [`reedsolomon_rs`], with
94//! repair fallback to CPU when no suitable device or driver is present. The
95//! `metal` feature also enables policy-driven creation on native Apple
96//! Silicon through [`CreationBackend`]. `CreationBackend::Auto` keeps
97//! creation work below 16 GiB (slice size × source-slice count × recovery-
98//! slice count) on CPU; on supported native Apple Silicon at or above that
99//! threshold it preflights Metal and falls back to CPU when unavailable.
100//!
101//! # Benchmarks
102//!
103//! Heavy PAR2 repair against `par2cmdline-turbo 1.4.0`, from the deterministic
104//! 43-case `rarpar-bench` corpus. Each figure is the geometric mean of
105//! `reference wall time / rarpar wall time` over `par2-heavy-damage-28` and
106//! `par2-heavy-damage-250`, so `2.0x` means half the time.
107//!
108//! | CPU | Arch | Instruction set | par2 (heavy) |
109//! |---|---|---|---:|
110//! | AMD EPYC 9R14 (Zen 4) | x86-64 | GFNI + AVX-512 | 1.8x |
111//! | Intel Xeon Platinum 8488C (Sapphire Rapids) | x86-64 | GFNI + AVX-512 | 1.7x |
112//! | Intel Core i5-1240P (Alder Lake) | x86-64 | GFNI + AVX2 | 1.9x |
113//! | AMD Ryzen 5 3600 (Zen 2) | x86-64 | AVX2 | 1.5x |
114//! | Intel Atom C3538 (Denverton) | x86-64 | SSSE3 (no AVX) | 1.3x |
115//! | Apple M5 Max | arm64 | NEON | 7.1x |
116//! | Arm Cortex-A72 | arm64 | NEON | 1.2x |
117//! | Arm Neoverse N1 | arm64 | NEON | 1.4x |
118//! | Arm Neoverse V2 | arm64 | NEON | 1.5x |
119//!
120//! The Apple row is the CPU lane, and is measured against upstream's published
121//! macOS arm64 reference binary, which is much slower than the same version's
122//! Linux and Windows builds; that lifts every macOS PAR2 figure.
123//!
124//! Per-case charts for every machine, the full methodology, and the versions
125//! these numbers were measured with are in
126//! [rarpar benchmarks](https://github.com/scryer-media/rarpar/blob/main/docs/benchmark.md).
127//!
128//! The format is specified in the [Parity Volume Set Specification 2.0](https://parchive.sourceforge.net/docs/specifications/parity-volume-spec/article-spec.html).
129
130#[cfg(all(
131 feature = "crypto-aws-lc",
132 not(any(
133 all(target_arch = "x86_64", target_os = "macos"),
134 all(target_arch = "aarch64", target_os = "macos"),
135 all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"),
136 all(target_arch = "aarch64", target_os = "linux", target_env = "gnu"),
137 all(target_arch = "x86_64", target_os = "linux", target_env = "musl"),
138 all(target_arch = "aarch64", target_os = "linux", target_env = "musl"),
139 all(target_arch = "x86_64", target_os = "windows", target_env = "msvc"),
140 all(target_arch = "aarch64", target_os = "windows", target_env = "msvc")
141 ))
142))]
143compile_error!(
144 "par2-rs crypto-aws-lc only supports x86_64/aarch64 on macOS, Linux GNU/musl, and Windows MSVC"
145);
146
147// A native build must name a backend rather than silently taking one. On wasm
148// the AWS-LC dependency is target-gated away, so `crypto-rust` is the only
149// possibility and the crate selects it without being asked.
150#[cfg(all(
151 not(target_family = "wasm"),
152 not(feature = "crypto-aws-lc"),
153 not(feature = "crypto-rust")
154))]
155compile_error!(
156 "par2-rs needs a crypto backend: enable feature `crypto-aws-lc` (default) \
157 or `crypto-rust` (portable, no C toolchain)."
158);
159
160pub mod checksum;
161mod cpu_repair_controller;
162mod crc_simd;
163pub mod create;
164pub mod disk;
165pub mod error;
166pub mod evidence;
167mod file_cache;
168pub mod matrix;
169pub mod md5_simd;
170pub mod packet;
171pub mod par2_set;
172pub mod path;
173pub mod placement;
174pub mod rename;
175pub mod repair;
176pub mod repair_session;
177pub mod repair_transform;
178pub mod repairer;
179pub mod session;
180pub mod types;
181pub mod verify;
182
183// Re-export key types for convenience.
184pub use checksum::{FileHashState, SliceChecksumState};
185pub use create::{
186 BlockSizing, CreationBackend, CreationSource, ForwardKernel, Par2CreateOutcome, Par2CreatePlan,
187 Par2Creator, Par2CreatorOptions, Par2MemoryPlan, RecoveryAmount, RecoveryVolumePlan,
188 VolumeScheme,
189};
190pub use disk::{DiskFileAccess, MultiDirectoryFileAccess, PlacementFileAccess};
191pub use error::{Par2Error, Result};
192pub use evidence::{CommittedFileEvidence, ContiguousAssemblyProof, FileStatFingerprint};
193pub use file_cache::CacheEvictionDeferral;
194pub use gf::{add as gf_add, input_slice_constants, inv as gf_inv, mul as gf_mul, pow as gf_pow};
195pub use gf_simd::{FactorDst, mul_acc_multi_region, mul_acc_region};
196pub use matrix::{Matrix, build_decode_matrix};
197pub use packet::{
198 CreatorPacket, DEFAULT_MAX_EXAMINED_PACKETS, DEFAULT_MAX_RETAINED_METADATA_BYTES,
199 DEFAULT_MAX_RETAINED_PACKETS, FileDescriptionPacket, IfscPacket, MAX_RECOVERY_EXPONENT,
200 MainPacket, Packet, PacketHeader, PacketScanBudget, PacketScanLimits, PacketSink, PacketType,
201 RECOVERY_EXPONENT_DOMAIN, RecoverySliceData, RecoverySlicePacket, ScannedPacket, parse_packet,
202 scan_packets, scan_packets_bounded, scan_packets_from_path, scan_packets_from_path_bounded,
203 scan_packets_from_path_with_set_ids, scan_packets_from_path_with_set_ids_limited,
204 scan_packets_with_limits,
205};
206pub use par2_set::{
207 FileDescription, MergeResult, Par2Diagnostic, Par2FileSet, Par2ParseResult, RecoverySlice,
208};
209pub use path::{translate_par2_name_to_local_path, translate_par2_name_to_relative};
210pub use placement::{PlacementEntry, PlacementPlan, apply_placement_plan, scan_placement};
211pub use reedsolomon_rs::{gf, gf_pmul, gf_simd, matrix_tiled};
212pub use rename::{
213 MatchType, RenameSuggestion, SplitFileGroup, detect_split_files, identify_par2_files,
214 scan_for_renames,
215};
216pub use repair::{
217 NativeRepairSolver, RepairOptions, RepairPlan, RepairProblem, RepairSolver, SolverError,
218 execute_repair, execute_repair_with_options, execute_repair_with_solver, plan_repair,
219 plan_repair_with_memory_limit, prepare_recovery_buffers, reconstruct_and_write, xor_out_slice,
220};
221pub use repair_session::{
222 DEFAULT_RETAINED_STATE_LIMIT, Par2RepairSession, Par2RepairSessionDiagnostics,
223 Par2RepairSessionOptions, Par2SessionError,
224};
225pub use repair_transform::{
226 TransformArm, TransformArmStats, set_transform_arm_override, transform_arm_override,
227 transform_arm_stats,
228};
229pub use repairer::{
230 BlockLocation, BlockLocationKind, CarryDiagnostics, CarryRetryReason, ExternalCarryError,
231 PacketDiagnostics, PacketInventory, Par2RepairOutcome, Par2RepairStatus, Par2Repairer,
232 Par2RepairerOptions, ScanCarry, ScanDiagnostics, SourceBlock, SourceFileEntry, SourceLocation,
233};
234pub use session::{
235 FeedDisposition, FeedOutcome, InStreamCrc32Proof, InStreamCrc32ProofError, SettleRead,
236 SliceEvidence, SliceEvidenceStrength, VerificationMemoryBudget, VerificationSession,
237 VerificationSessionOptions,
238};
239pub use types::{
240 CancellationToken, ProgressCallback, ProgressPhase, ProgressStage, ProgressUpdate,
241};
242pub use types::{FileId, RecoveryExponent, RecoverySetId, SliceChecksum, SliceIndex};
243pub use verify::{
244 FileAccess, FileStatus, FileVerification, MemoryFileAccess, Repairability, VerificationResult,
245 VerifyOptions, quick_check_16k, verify_all, verify_all_with_options, verify_full_hash,
246 verify_selected_file_ids, verify_selected_file_ids_with_options, verify_slices,
247 verify_slices_from_crcs,
248};