Skip to main content

libzstd_bitexact_rs/
stream.rs

1//! Streaming decompression (`ZSTD_decompressStream` semantics).
2//!
3//! [`StreamDecoder`] wraps any [`Read`] source of compressed data and itself
4//! implements [`Read`], producing the decompressed bytes. Unlike the one-shot
5//! [`decompress`](crate::decompress), it does not hold the whole compressed
6//! input or the whole output in memory: it buffers just enough compressed
7//! bytes to decode the next block, and keeps a **sliding window** of recent
8//! output (at most the frame's `windowSize`, plus the block in flight) so that
9//! matches can reach back while older output is evicted as soon as it passes
10//! out of window range. Memory use is therefore bounded by the frame's window,
11//! not its content.
12//!
13//! The decode itself reuses the same block decoder as the one-shot path
14//! ([`crate::block::decode_compressed_block`]); the only difference is the
15//! windowed output buffer. A dictionary's content is seeded into that window
16//! as initial history (so dictionary-relative matches are ordinary in-window
17//! copies), and its entropy tables seed the first block's context exactly as
18//! in the one-shot path.
19
20use std::io::{self, Read};
21
22use crate::block::{self, BLOCK_SIZE_MAX, FrameContext};
23use crate::decompress::{DecodeOptions, SKIPPABLE_MAGIC, SKIPPABLE_MAGIC_MASK, ZSTD_MAGIC};
24use crate::error::Error;
25use crate::frame;
26use crate::xxhash::Xxh64;
27
28/// The most bytes a frame header can occupy after the magic number
29/// (descriptor + window descriptor + 4-byte dict ID + 8-byte content size).
30const MAX_FRAME_HEADER: usize = 14;
31
32/// Map a decoder error into the `io::Error` that [`Read`] must report.
33fn invalid(e: Error) -> io::Error {
34    io::Error::new(io::ErrorKind::InvalidData, e)
35}
36
37/// Per-frame decoding state, live while inside a frame.
38struct FrameState {
39    ctx: FrameContext,
40    /// Sliding window: recent output plus any seeded dictionary history.
41    win: Vec<u8>,
42    /// Window size in bytes; the window is trimmed to its last `window` bytes
43    /// after each block.
44    window: usize,
45    block_size_max: usize,
46    has_checksum: bool,
47    content_size: Option<u64>,
48    xxh: Xxh64,
49    /// Frame output produced so far (excludes any seeded dictionary prefix).
50    produced: u64,
51}
52
53enum Phase {
54    /// Between frames: expecting a frame or skippable magic, or clean EOF.
55    FrameStart,
56    /// Inside a skippable frame, with this many payload bytes left to drop.
57    Skippable(usize),
58    /// Decoding the block sequence of the current frame.
59    Blocks,
60    /// Block sequence finished; expecting the optional checksum.
61    Footer,
62    /// The whole input has been consumed.
63    Done,
64}
65
66/// A streaming Zstandard decompressor over a [`Read`] source.
67///
68/// ```no_run
69/// use std::io::Read;
70/// use libzstd_bitexact_rs::StreamDecoder;
71///
72/// # fn main() -> std::io::Result<()> {
73/// let file = std::fs::File::open("data.zst")?;
74/// let mut decoder = StreamDecoder::new(file);
75/// let mut out = Vec::new();
76/// decoder.read_to_end(&mut out)?;
77/// # Ok(())
78/// # }
79/// ```
80pub struct StreamDecoder<'d, R: Read> {
81    reader: R,
82    options: DecodeOptions<'d>,
83
84    /// Compressed input buffered from `reader`; `in_pos` marks how much has
85    /// been consumed.
86    in_buf: Vec<u8>,
87    in_pos: usize,
88    eof: bool,
89
90    phase: Phase,
91    frame: Option<FrameState>,
92
93    /// The most recently decoded block's output, drained to callers across
94    /// `read` calls; `out_pos` marks how much has been handed out.
95    out_ready: Vec<u8>,
96    out_pos: usize,
97
98    /// Total output produced across all frames, for the output-size limit.
99    total: u64,
100}
101
102impl<R: Read> StreamDecoder<'static, R> {
103    /// Wrap `reader` with default options (no output limit, window log up to
104    /// the format maximum, no dictionary).
105    pub fn new(reader: R) -> Self {
106        Self::with_options(reader, DecodeOptions::new())
107    }
108}
109
110impl<'d, R: Read> StreamDecoder<'d, R> {
111    /// Wrap `reader`, taking the output limit, maximum window log, and optional
112    /// dictionary from `options`.
113    pub fn with_options(reader: R, options: DecodeOptions<'d>) -> Self {
114        StreamDecoder {
115            reader,
116            options,
117            in_buf: Vec::new(),
118            in_pos: 0,
119            eof: false,
120            phase: Phase::FrameStart,
121            frame: None,
122            out_ready: Vec::new(),
123            out_pos: 0,
124            total: 0,
125        }
126    }
127
128    /// Drop already-consumed input so the buffer does not grow without bound.
129    fn compact(&mut self) {
130        if self.in_pos > 0 {
131            self.in_buf.drain(..self.in_pos);
132            self.in_pos = 0;
133        }
134    }
135
136    fn avail(&self) -> usize {
137        self.in_buf.len() - self.in_pos
138    }
139
140    /// Pull from `reader` until at least `n` bytes are buffered, or EOF.
141    /// Returns whether `n` bytes are now available.
142    fn fill(&mut self, n: usize) -> io::Result<bool> {
143        let mut tmp = [0u8; 1 << 16];
144        while self.avail() < n && !self.eof {
145            let got = self.reader.read(&mut tmp)?;
146            if got == 0 {
147                self.eof = true;
148                break;
149            }
150            self.in_buf.extend_from_slice(&tmp[..got]);
151        }
152        Ok(self.avail() >= n)
153    }
154
155    /// Produce the next block's output into `out_ready`. Returns `Ok(false)`
156    /// only at a clean end of input; otherwise `Ok(true)` once `out_ready` has
157    /// fresh bytes (it may loop over empty blocks, footers, and frame headers
158    /// first).
159    fn decode_more(&mut self) -> io::Result<bool> {
160        loop {
161            self.compact();
162            match self.phase {
163                Phase::Done => return Ok(false),
164                Phase::FrameStart => {
165                    if !self.fill(4)? {
166                        // EOF: clean only if no partial frame is buffered.
167                        if self.avail() == 0 {
168                            self.phase = Phase::Done;
169                            return Ok(false);
170                        }
171                        return Err(invalid(Error::SrcSizeWrong));
172                    }
173                    let magic = u32::from_le_bytes(
174                        self.in_buf[self.in_pos..self.in_pos + 4]
175                            .try_into()
176                            .unwrap(),
177                    );
178                    if magic == ZSTD_MAGIC {
179                        self.in_pos += 4;
180                        self.start_frame()?;
181                    } else if magic & SKIPPABLE_MAGIC_MASK == SKIPPABLE_MAGIC {
182                        if !self.fill(8)? {
183                            return Err(invalid(Error::SrcSizeWrong));
184                        }
185                        let size = u32::from_le_bytes(
186                            self.in_buf[self.in_pos + 4..self.in_pos + 8]
187                                .try_into()
188                                .unwrap(),
189                        ) as usize;
190                        self.in_pos += 8;
191                        self.phase = Phase::Skippable(size);
192                    } else {
193                        return Err(invalid(Error::UnknownMagic(magic)));
194                    }
195                }
196                Phase::Skippable(0) => self.phase = Phase::FrameStart,
197                Phase::Skippable(left) => {
198                    if self.avail() == 0 && !self.fill(1)? {
199                        return Err(invalid(Error::SrcSizeWrong));
200                    }
201                    let take = left.min(self.avail());
202                    self.in_pos += take;
203                    self.phase = Phase::Skippable(left - take);
204                }
205                Phase::Blocks => {
206                    if self.decode_block()? {
207                        return Ok(true);
208                    }
209                }
210                Phase::Footer => self.finish_frame()?,
211            }
212        }
213    }
214
215    /// Parse the current frame's header and set up its window and context.
216    fn start_frame(&mut self) -> io::Result<()> {
217        let _ = self.fill(MAX_FRAME_HEADER)?; // best effort; headers are short
218        let header = match frame::parse(
219            &self.in_buf[self.in_pos..],
220            self.options.window_log_max_value(),
221        ) {
222            Ok(h) => h,
223            // Having filled all we could, a still-short header is truncation.
224            Err(Error::SrcSizeWrong) => return Err(invalid(Error::SrcSizeWrong)),
225            Err(e) => return Err(invalid(e)),
226        };
227
228        // Resolve the dictionary against the frame's declared ID, exactly as
229        // the one-shot path does.
230        let dict = match (self.options.dictionary_value(), header.dict_id) {
231            (_, 0) => self.options.dictionary_value(),
232            (Some(d), id) if d.id() == id => Some(d),
233            (Some(d), id) => {
234                return Err(invalid(Error::DictionaryWrong {
235                    expected: id,
236                    actual: d.id(),
237                }));
238            }
239            (None, id) => return Err(invalid(Error::DictionaryRequired(id))),
240        };
241
242        self.in_pos += header.header_len;
243
244        let mut win = Vec::new();
245        if let Some(d) = dict {
246            win.extend_from_slice(d.content());
247        }
248        let window = header.window_size.min(usize::MAX as u64) as usize;
249        self.frame = Some(FrameState {
250            ctx: FrameContext::with_dictionary(dict),
251            win,
252            window,
253            block_size_max: header.window_size.min(BLOCK_SIZE_MAX as u64) as usize,
254            has_checksum: header.has_checksum,
255            content_size: header.content_size,
256            xxh: Xxh64::new(0),
257            produced: 0,
258        });
259        self.phase = Phase::Blocks;
260        Ok(())
261    }
262
263    /// Decode one block into the window. Returns whether it produced output;
264    /// sets the phase to `Footer` after the last block.
265    fn decode_block(&mut self) -> io::Result<bool> {
266        if !self.fill(3)? {
267            return Err(invalid(Error::SrcSizeWrong));
268        }
269        let p = self.in_pos;
270        let raw = u32::from(self.in_buf[p])
271            | u32::from(self.in_buf[p + 1]) << 8
272            | u32::from(self.in_buf[p + 2]) << 16;
273        let last = raw & 1 != 0;
274        let block_type = (raw >> 1) & 3;
275        let size = (raw >> 3) as usize;
276        self.in_pos += 3;
277
278        let body_needed = match block_type {
279            1 => 1,        // RLE: a single byte
280            0 | 2 => size, // Raw / Compressed: the whole block
281            _ => return Err(invalid(Error::BlockTypeInvalid)),
282        };
283        if !self.fill(body_needed)? {
284            return Err(invalid(Error::SrcSizeWrong));
285        }
286
287        let block_size_max = self.frame.as_ref().unwrap().block_size_max;
288        if size > block_size_max {
289            return Err(invalid(Error::Corrupted(
290                "block size exceeds block size limit",
291            )));
292        }
293
294        let body_start = self.in_pos;
295        let fr = self.frame.as_mut().unwrap();
296        let before = fr.win.len();
297        match block_type {
298            // Raw_Block.
299            0 => {
300                fr.win
301                    .extend_from_slice(&self.in_buf[body_start..body_start + size]);
302                self.in_pos += size;
303            }
304            // RLE_Block.
305            1 => {
306                let byte = self.in_buf[body_start];
307                fr.win.resize(fr.win.len() + size, byte);
308                self.in_pos += 1;
309            }
310            // Compressed_Block: the window is the output buffer, with all prior
311            // history (including any dictionary prefix) already in it, so the
312            // block decoder needs no separate dictionary slice.
313            _ => {
314                let body = &self.in_buf[body_start..body_start + size];
315                block::decode_compressed_block(
316                    &mut fr.ctx,
317                    body,
318                    &mut fr.win,
319                    0,
320                    &[],
321                    block_size_max,
322                    usize::MAX,
323                )
324                .map_err(invalid)?;
325                self.in_pos += size;
326            }
327        }
328
329        // Hand the freshly decoded bytes to the output queue and feed them to
330        // the running checksum before any of them can be evicted.
331        let new = &fr.win[before..];
332        self.total += new.len() as u64;
333        if self.total > self.options.limit_value() as u64 {
334            return Err(invalid(Error::OutputTooLarge));
335        }
336        self.out_ready.extend_from_slice(new);
337        fr.xxh.update(new);
338        fr.produced += new.len() as u64;
339
340        // Trim the window back to its last `window` bytes; everything older is
341        // now out of match range.
342        if fr.win.len() > fr.window {
343            let drop = fr.win.len() - fr.window;
344            fr.win.drain(..drop);
345        }
346
347        if last {
348            self.phase = Phase::Footer;
349        }
350        Ok(!self.out_ready.is_empty())
351    }
352
353    /// Verify the frame's content size and checksum, then arm the next frame.
354    fn finish_frame(&mut self) -> io::Result<()> {
355        let fr = self.frame.take().expect("frame state present in Footer");
356
357        if let Some(fcs) = fr.content_size {
358            if fr.produced != fcs {
359                return Err(invalid(Error::FrameContentSizeMismatch));
360            }
361        }
362
363        if fr.has_checksum {
364            if !self.fill(4)? {
365                return Err(invalid(Error::SrcSizeWrong));
366            }
367            let stored = u32::from_le_bytes(
368                self.in_buf[self.in_pos..self.in_pos + 4]
369                    .try_into()
370                    .unwrap(),
371            );
372            self.in_pos += 4;
373            let actual = fr.xxh.digest() as u32;
374            if stored != actual {
375                return Err(invalid(Error::ChecksumMismatch {
376                    expected: stored,
377                    actual,
378                }));
379            }
380        }
381
382        self.phase = Phase::FrameStart;
383        Ok(())
384    }
385}
386
387impl<R: Read> Read for StreamDecoder<'_, R> {
388    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
389        if buf.is_empty() {
390            return Ok(0);
391        }
392        loop {
393            if self.out_pos < self.out_ready.len() {
394                let n = (self.out_ready.len() - self.out_pos).min(buf.len());
395                buf[..n].copy_from_slice(&self.out_ready[self.out_pos..self.out_pos + n]);
396                self.out_pos += n;
397                return Ok(n);
398            }
399            self.out_ready.clear();
400            self.out_pos = 0;
401            if !self.decode_more()? {
402                return Ok(0);
403            }
404        }
405    }
406}