rapidgzip_core/lib.rs
1//! Parallel decoding of gzip, zlib, and raw DEFLATE.
2//!
3//! `rapidgzip-core` decodes single-member and concatenated gzip, BGZF, zlib,
4//! and raw DEFLATE. It follows rapidgzip's marker/window algorithm for parallel
5//! decoding and uses zlib-rs as its inflate backend. Encoding is outside this
6//! crate's current scope. Index construction, persistence, and decoded-output
7//! seeking are available through explicit opt-in APIs.
8//!
9//! # Formats
10//!
11//! Strict [`Format::Gzip`] is the default. [`DecoderBuilder::format`] selects
12//! zlib or raw DEFLATE explicitly; [`DecoderBuilder::auto_detect_format`]
13//! recognizes gzip or zlib without consuming their prefix. Raw DEFLATE has no
14//! identifying header and is never guessed.
15//!
16//! Gzip checks every member's CRC32 and ISIZE. Zlib validates CMF/FLG, enforces
17//! its declared history window, and checks Adler-32. Raw DEFLATE has no
18//! checksum, so success establishes structural validity and exact source
19//! consumption. [`DecoderBuilder::expected_uncompressed_size`] can require an
20//! exact decoded size for any format.
21//!
22//! # Output interfaces
23//!
24//! - [`Decoder::decode`] is the lower-overhead push interface, and
25//! [`Decoder::decode_path`] adds automatic regular/non-regular path routing.
26//! Both write on the calling thread, so [`std::io::Write`] need not be [`Send`].
27//! - [`Decoder::reader`] and [`Decoder::open`] return an owned [`DecoderReader`]
28//! implementing [`std::io::Read`] + [`Send`]. This is suitable for parsers
29//! that take `Box<dyn Read + Send>`, including `paraseq`.
30//! - [`Decoder::decode_stream`] and [`Decoder::stream_reader`] are the same two
31//! interfaces for non-seekable input; see below.
32//!
33//! # Example
34//!
35//! ```no_run
36//! use rapidgzip_core::Decoder;
37//! use std::io;
38//!
39//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
40//! let decoder = Decoder::builder().decoder_threads(8).build()?;
41//! let mut reader = decoder.open("reads.fastq.gz")?;
42//! let control = reader.handle();
43//! control.set_worker_limit(4)?;
44//! io::copy(&mut reader, &mut io::sink())?;
45//! let report = reader.finish()?;
46//! assert!(report.member_count >= 1);
47//! # Ok(())
48//! # }
49//! ```
50//!
51//! # Verification and errors
52//!
53//! Reaching reader EOF or receiving a successful [`DecodeReport`] means the
54//! complete compressed input passed every check carried by its selected
55//! container. Dropping a [`DecoderReader`] before EOF cancels the unread work;
56//! call [`DecoderReader::finish`] when decoded bytes are no longer needed but
57//! complete validation is.
58//!
59//! Decoding can emit a verified prefix before discovering later corruption or
60//! an I/O failure. Previously written or read bytes are not rolled back.
61//!
62//! # Input and concurrency
63//!
64//! Compressed input implements [`ReadAt`], allowing bounded worker tasks to use
65//! positional reads without a shared cursor. Implementations are supplied for
66//! files on Unix and Windows, in-memory byte storage, [`std::sync::Arc`], and
67//! [`Box`]. The source length and contents must remain stable during decoding.
68//!
69//! # Non-seekable input
70//!
71//! [`Decoder::decode_stream`] and [`Decoder::stream_reader`] accept any
72//! [`std::io::Read`], so standard input, a FIFO, a process substitution, or a
73//! socket can be decoded. [`Decoder::open`] routes non-regular paths accepted by
74//! [`std::fs::File::open`], such as FIFOs and character devices, to the same
75//! sequential engine.
76//!
77//! Validation is identical: such a source runs the same sequential zlib-rs
78//! path that the parallel paths use as their authoritative fallback, sharing
79//! framing, trailer checks, trailing-data detection, and output bounds. It is
80//! not decoded in parallel, because every parallel path needs positional reads.
81//! Telemetry retains the builder's configured worker budget while
82//! reporting an effective target of one and zero spawned decoder/auxiliary
83//! threads. Nothing is spooled: input memory is one
84//! [`DecoderBuilder::input_page_size`] window. [`DecoderReader`] advances the
85//! streaming inflater synchronously from [`std::io::Read::read`], so dropping it
86//! immediately drops the source and cannot strand a thread blocked on input.
87//!
88//! [`DecoderBuilder::decoder_threads`] sets a maximum worker budget rather than
89//! eagerly creating that many threads. Parallel paths grow an elastic worker
90//! population from an affinity- and budget-aware bootstrap. A cloned
91//! [`DecoderHandle`] provides lock-free telemetry and can change the runtime
92//! ceiling after a [`DecoderReader`] moves into another component. Excess
93//! workers finish their current task and retire; sustained reader backpressure
94//! also reduces admission automatically.
95//!
96//! # Structural analysis
97//!
98//! [`Decoder::analyze`] and [`Decoder::analyze_stream`] verify the complete
99//! input while returning an [`Analysis`] of container streams, DEFLATE blocks,
100//! dynamic Huffman alphabets, symbol composition, and predecessor-window use.
101//! [`AnalyzeOptions`] bounds retained streams, blocks, optional gzip metadata,
102//! and individual back-references. Exact aggregate reference statistics remain
103//! available when detail retention is disabled or exhausted. The causal walk
104//! is single-threaded and keeps only one 32 KiB decoded history window.
105//!
106//! # Random access
107//!
108//! [`Decoder::decode_with_index`] and [`Decoder::reader_with_index`] collect a
109//! [`DeflateIndex`] only when requested, leaving [`DecodeReport`] small and
110//! [`Copy`]. The streaming counterparts collect a coarser member-boundary index
111//! while reading a forward-only source. The native format represents every
112//! supported container; GZIDX, htslib BGZF `.gzi`, and gztool are gzip-family
113//! formats and reject incompatible export.
114//!
115//! [`Decoder::decode_from_index`] and [`Decoder::reader_from_index`] reuse an
116//! existing index for strict parallel full-stream decoding. Every worker must
117//! reach the next checkpoint's exact compressed bit and decompressed byte
118//! offsets; invalid or source-mismatched indexes never trigger an ordinary
119//! fallback. The reader remains [`std::io::Read`] + [`Send`] and exposes the
120//! usual runtime worker controls. Concatenated and empty gzip members and BGZF
121//! `.gzi` indexes are supported without weakening whole-stream verification.
122//!
123//! [`IndexedReader`] implements [`std::io::Read`] + [`std::io::Seek`] over a
124//! stable [`ReadAt`] source. Framing-start checkpoints permit complete gzip or
125//! zlib verification; an interior checkpoint cannot authenticate bytes skipped
126//! earlier because indexes do not store prefix checksum state. Raw DEFLATE has
127//! no checksum to authenticate.
128//!
129//! # Line counting and seeking
130//!
131//! [`DecoderBuilder::count_lines`] optionally counts newline bytes on final
132//! ordered output. The scalar result is returned in
133//! [`DecodeReport::line_count`], so [`DecodeReport`] remains [`Copy`]. When the
134//! same operation explicitly builds an index, each retained checkpoint and the
135//! index total receive exact line metadata. [`IndexedReader::seek_to_line`]
136//! then seeks to a zero-based line without scanning from the source origin.
137//! Counting is disabled by default.
138#![deny(unsafe_op_in_unsafe_fn)]
139#![deny(missing_docs)]
140
141mod analyze;
142mod backend;
143mod config;
144mod crc32;
145mod error;
146mod format;
147mod gzip;
148mod indexed;
149mod indexed_parallel;
150mod inflate;
151mod line;
152mod read_at;
153mod reader;
154mod runtime;
155mod zlib;
156
157pub mod index;
158pub mod parallel;
159
160pub use analyze::{
161 AlphabetShape, Analysis, AnalyzeOptions, Backreference, BlockAnalysis, BlockType,
162 GzipHeaderFields, StreamAnalysis, StreamFooter, StreamHeader, ZlibHeaderFields,
163};
164pub use config::{ConfigError, Decoder, DecoderBuilder};
165pub use error::{
166 AnalysisCounter, AnalysisErrorKind, AnalysisResource, DecodeError, DecodeReport,
167 DeflateErrorKind, GzipErrorKind, IndexDecodeError, IndexedDecodeReport, IndexingError,
168 ZlibErrorKind,
169};
170pub use format::Format;
171pub use index::{
172 Checkpoint, CheckpointKind, DeflateIndex, IndexError, IndexKind, IndexOptions,
173 IndexReadOptions, StoredWindow, WindowMap, WindowStorage,
174};
175pub use indexed::{IndexedReader, IndexedReaderError};
176pub use read_at::ReadAt;
177pub use reader::{DecoderReader, IndexingDecoderReader};
178pub use runtime::{DecoderHandle, DecoderPath, DecoderPressure, DecoderStats, WorkerLimitError};