Skip to main content

structured_zstd/decoding/
streaming_decoder.rs

1//! The [StreamingDecoder] wraps a [FrameDecoder] and provides a Read impl that decodes data when necessary
2
3use core::borrow::BorrowMut;
4
5use crate::common::MAX_BLOCK_SIZE;
6use crate::decoding::errors::FrameDecoderError;
7use crate::decoding::{BlockDecodingStrategy, DictionaryHandle, FrameDecoder};
8#[cfg(not(feature = "std"))]
9use crate::io::ErrorKind;
10use crate::io::{Error, Read};
11
12/// High level Zstandard frame decoder that can be used to decompress a given Zstandard frame.
13///
14/// This decoder implements `io::Read`, so you can interact with it by calling
15/// `io::Read::read_to_end` / `io::Read::read_exact` or passing this to another library / module as a source for the decoded content
16///
17/// If you need more control over how decompression takes place, you can use
18/// the lower level [FrameDecoder], which allows for greater control over how
19/// decompression takes place but the implementor must call
20/// [FrameDecoder::decode_blocks] repeatedly to decode the entire frame.
21///
22/// ## Caveat
23/// Plain `read` / `read_exact` operate on the single frame this decoder was
24/// initialised with: they do not advance into following frames. `read_to_end`,
25/// by contrast, is specialised to consume a finite source to EOF, decoding
26/// concatenated frames and skipping skippable frames along the way.
27///
28/// To recover the bytes that follow one frame WITHOUT consuming the rest of the
29/// source, recreate the decoder manually and handle
30/// [crate::decoding::errors::ReadFrameHeaderError::SkipFrame]
31/// errors by skipping forward the `length` amount of bytes, see <https://github.com/KillingSpark/zstd-rs/issues/57>
32///
33/// ```no_run
34/// // `File` is std-only; `read_to_end` itself is available under no_std too.
35/// #[cfg(feature = "std")]
36/// {
37///     use std::fs::File;
38///     use std::io::Read;
39///     use structured_zstd::decoding::StreamingDecoder;
40///
41///     // Read a Zstandard archive from the filesystem then decompress it into a vec.
42///     let mut f: File = todo!("Read a .zstd archive from somewhere");
43///     let mut decoder = StreamingDecoder::new(f).unwrap();
44///     let mut result = Vec::new();
45///     Read::read_to_end(&mut decoder, &mut result).unwrap();
46/// }
47/// ```
48pub struct StreamingDecoder<READ: Read, DEC: BorrowMut<FrameDecoder>> {
49    pub decoder: DEC,
50    source: READ,
51    /// Whether the decoder was constructed with a dictionary it applies to
52    /// every frame. The `read_to_end` paths re-initialise FOLLOWING
53    /// concatenated frames with it (a plain re-init resolves dictionaries by
54    /// frame id only and would lose it for frames omitting the id); the
55    /// decoder already holds its handle, so this one keeps none of its own.
56    forced_dictionary: bool,
57}
58
59impl<READ: Read, DEC: BorrowMut<FrameDecoder>> StreamingDecoder<READ, DEC> {
60    pub fn new_with_decoder(
61        mut source: READ,
62        mut decoder: DEC,
63    ) -> Result<StreamingDecoder<READ, DEC>, FrameDecoderError> {
64        decoder.borrow_mut().init(&mut source)?;
65        Ok(StreamingDecoder {
66            decoder,
67            source,
68            forced_dictionary: false,
69        })
70    }
71
72    /// [`new_with_decoder`](Self::new_with_decoder) with `dict` applied to
73    /// the frame even when its header omits the dictionary ID, as
74    /// [`new_with_dictionary_handle`](StreamingDecoder::new_with_dictionary_handle)
75    /// applies it (same warning). A decoder reused frame after frame keeps its
76    /// buffers and, for the same dictionary, the handle it already holds, so
77    /// after the first frame it allocates nothing and touches no reference
78    /// count.
79    ///
80    /// # Examples
81    /// ```
82    /// use std::io::Read;
83    /// use structured_zstd::decoding::{Dictionary, DictionaryHandle, FrameDecoder, StreamingDecoder};
84    /// use structured_zstd::encoding::{FrameCompressor, CompressionLevel};
85    ///
86    /// let content = b"a dictionary of words the frames reuse".to_vec();
87    /// let dictionary = DictionaryHandle::from_dictionary(
88    ///     Dictionary::from_raw_content(1, content.clone()).unwrap(),
89    /// );
90    /// let mut compressor: FrameCompressor = FrameCompressor::new(CompressionLevel::Default);
91    /// compressor.set_dictionary(Dictionary::from_raw_content(1, content).unwrap()).unwrap();
92    /// let frame = compressor.compress_independent_frame(b"the frames reuse words");
93    ///
94    /// let mut decoder = FrameDecoder::new();
95    /// for _ in 0..2 {
96    ///     let mut stream =
97    ///         StreamingDecoder::new_with_decoder_and_dictionary_handle(&frame[..], &mut decoder, &dictionary)
98    ///             .unwrap();
99    ///     let mut decoded = Vec::new();
100    ///     stream.read_to_end(&mut decoded).unwrap();
101    ///     assert_eq!(decoded, b"the frames reuse words");
102    /// }
103    /// ```
104    pub fn new_with_decoder_and_dictionary_handle(
105        mut source: READ,
106        mut decoder: DEC,
107        dict: &DictionaryHandle,
108    ) -> Result<StreamingDecoder<READ, DEC>, FrameDecoderError> {
109        decoder
110            .borrow_mut()
111            .init_with_dict_handle(&mut source, dict)?;
112        Ok(StreamingDecoder {
113            decoder,
114            source,
115            forced_dictionary: true,
116        })
117    }
118}
119
120impl<READ: Read> StreamingDecoder<READ, FrameDecoder> {
121    pub fn new(
122        mut source: READ,
123    ) -> Result<StreamingDecoder<READ, FrameDecoder>, FrameDecoderError> {
124        let mut decoder = FrameDecoder::new();
125        decoder.init(&mut source)?;
126        Ok(StreamingDecoder {
127            decoder,
128            source,
129            forced_dictionary: false,
130        })
131    }
132
133    /// Create a streaming decoder using a pre-parsed dictionary handle.
134    ///
135    /// # Warning
136    ///
137    /// This constructor initializes the underlying [`FrameDecoder`] with
138    /// `dict`, even if a frame header omits the optional dictionary ID.
139    /// Callers must only use it when they already know the stream was encoded
140    /// with this dictionary; otherwise decoded output can be silently
141    /// corrupted.
142    pub fn new_with_dictionary_handle(
143        source: READ,
144        dict: &DictionaryHandle,
145    ) -> Result<StreamingDecoder<READ, FrameDecoder>, FrameDecoderError> {
146        Self::new_with_decoder_and_dictionary_handle(source, FrameDecoder::new(), dict)
147    }
148
149    /// Create a streaming decoder using a serialized dictionary blob.
150    ///
151    /// # Warning
152    ///
153    /// This API forwards to [`StreamingDecoder::new_with_dictionary_handle`]
154    /// and therefore applies the decoded dictionary to frames whose headers may
155    /// omit the optional dictionary ID. Only use it when the stream is known to
156    /// be encoded with that dictionary.
157    pub fn new_with_dictionary_bytes(
158        source: READ,
159        raw_dictionary: &[u8],
160    ) -> Result<StreamingDecoder<READ, FrameDecoder>, FrameDecoderError> {
161        let dict = DictionaryHandle::decode_dict(raw_dictionary)?;
162        Self::new_with_dictionary_handle(source, &dict)
163    }
164}
165
166impl<READ: Read, DEC: BorrowMut<FrameDecoder>> StreamingDecoder<READ, DEC> {
167    /// Gets a reference to the underlying reader.
168    pub fn get_ref(&self) -> &READ {
169        &self.source
170    }
171
172    /// Gets a mutable reference to the underlying reader.
173    ///
174    /// It is inadvisable to directly read from the underlying reader.
175    pub fn get_mut(&mut self) -> &mut READ {
176        &mut self.source
177    }
178
179    /// Gets a mutable reference to the frame decoder driving this stream.
180    ///
181    /// Exposed for settings that are read as decoding proceeds rather than at
182    /// construction — [`FrameDecoder::set_content_checksum`] above all, which a
183    /// caller that wants mismatches to fail (rather than merely be computed)
184    /// has to reach after the constructor has chosen and initialised the
185    /// decoder, including on the dictionary paths.
186    pub fn decoder_mut(&mut self) -> &mut FrameDecoder {
187        self.decoder.borrow_mut()
188    }
189
190    /// Destructures this object into the inner reader.
191    pub fn into_inner(self) -> READ
192    where
193        READ: Sized,
194    {
195        self.source
196    }
197
198    /// Destructures this object into both the inner reader and [FrameDecoder].
199    pub fn into_parts(self) -> (READ, DEC)
200    where
201        READ: Sized,
202    {
203        (self.source, self.decoder)
204    }
205
206    /// Destructures this object into the inner [FrameDecoder].
207    pub fn into_frame_decoder(self) -> DEC {
208        self.decoder
209    }
210}
211
212impl<READ: Read, DEC: BorrowMut<FrameDecoder>> Read for StreamingDecoder<READ, DEC> {
213    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
214        let decoder = self.decoder.borrow_mut();
215        if decoder.is_finished() && decoder.can_collect() == 0 {
216            // Frame fully decoded and fully drained: the running XXH64 digest
217            // is final, so a `Verify`-mode decoder validates the content
218            // checksum at this finish point. No-op in other modes.
219            #[cfg(feature = "hash")]
220            if let Err(e) = decoder.verify_content_checksum() {
221                #[cfg(feature = "std")]
222                return Err(Error::other(e));
223                #[cfg(not(feature = "std"))]
224                return Err(Error::new(ErrorKind::Other, alloc::boxed::Box::new(e)));
225            }
226            //No more bytes can ever be decoded
227            return Ok(0);
228        }
229
230        // Interleave bounded decode with draining so the decode window
231        // (`RingBuffer`) stays near `window_size` instead of accumulating the
232        // whole request before a single end-of-call drain. `read_to_end` hands
233        // ever-larger buffers; decoding `buf.len()` worth into the ring up
234        // front grew it far past the window (repeated `reserve_amortized`
235        // alloc+copy). Decode at most one block worth per step, then drain
236        // what is now collectable into `buf`, mirroring upstream zstd's
237        // window-bounded flush loop.
238        let mut written = 0;
239        while written < buf.len() {
240            // Drain whatever is collectable now (retaining `window_size` until
241            // the frame finishes). Reclaims the ring promptly so the next
242            // decode step reuses the same capacity.
243            written += decoder.read(&mut buf[written..])?;
244            if written == buf.len() || decoder.is_finished() {
245                break;
246            }
247            // Decode one bounded chunk. `UptoBytes` may overshoot a little but
248            // is capped to one block, so the ring's live region stays within
249            // `window_size + MAX_BLOCK_SIZE`.
250            let step = (buf.len() - written).min(MAX_BLOCK_SIZE as usize);
251            if let Err(e) =
252                decoder.decode_blocks(&mut self.source, BlockDecodingStrategy::UptoBytes(step))
253            {
254                #[cfg(feature = "std")]
255                {
256                    return Err(Error::other(e));
257                }
258                #[cfg(not(feature = "std"))]
259                {
260                    return Err(Error::new(ErrorKind::Other, alloc::boxed::Box::new(e)));
261                }
262            }
263        }
264
265        // The loop can finish AND fully drain a frame within this same call
266        // (decode last block, then drain it into `buf`). Validate here too when
267        // the frame is finished and nothing is left to collect, but ONLY when
268        // this call wrote no bytes: the `Read` contract forbids returning `Err`
269        // after bytes were delivered, so when `written > 0` the verify is
270        // deferred to the next call, where the top early-return runs it and
271        // returns `Err` on the zero-byte path. Idempotent with that top check.
272        #[cfg(feature = "hash")]
273        if written == 0
274            && decoder.is_finished()
275            && decoder.can_collect() == 0
276            && let Err(e) = decoder.verify_content_checksum()
277        {
278            #[cfg(feature = "std")]
279            return Err(Error::other(e));
280            #[cfg(not(feature = "std"))]
281            return Err(Error::new(ErrorKind::Other, alloc::boxed::Box::new(e)));
282        }
283
284        Ok(written)
285    }
286
287    /// Decode-in-place fast path for whole-frame consumption. Instead of the
288    /// generic `read` loop (decode block -> `RingBuffer` -> copy into the
289    /// caller buffer), buffer the (compressed, hence small) source and decode
290    /// STRAIGHT into `output`'s spare capacity via the single-copy direct path,
291    /// pre-sized from the frame's declared content size. Only taken when the
292    /// decoder is at a frame boundary (nothing partially decoded / undrained);
293    /// otherwise it falls back to the generic grow-and-`read` loop so a caller
294    /// that mixed `read` with `read_to_end` still gets correct output.
295    ///
296    /// Per the `Read::read_to_end` contract this consumes the source to EOF: if
297    /// the stream holds several concatenated frames they are ALL decoded (and
298    /// skippable frames skipped). To recover bytes that follow a single frame,
299    /// use `read` plus the
300    /// [`SkipFrame`](crate::decoding::errors::ReadFrameHeaderError::SkipFrame)
301    /// recreate-the-decoder pattern instead.
302    #[cfg(feature = "std")]
303    fn read_to_end(&mut self, output: &mut alloc::vec::Vec<u8>) -> Result<usize, Error> {
304        let start_total = output.len();
305        // `new()` already read the frame header, so the fast path applies when
306        // the decoder sits at the start of that frame with nothing decoded yet.
307        let at_start = {
308            let d = self.decoder.borrow_mut();
309            d.is_at_frame_start() && d.can_collect() == 0
310        };
311        // A forced dictionary is the one the decoder already holds; following
312        // frames are re-initialised with it in place, touching no reference
313        // count.
314        let keep_dictionary = self.forced_dictionary;
315        if at_start {
316            let mut compressed = alloc::vec::Vec::new();
317            self.source.read_to_end(&mut compressed)?;
318            self.decoder
319                .borrow_mut()
320                .decode_current_frame_to_vec(&compressed, output, keep_dictionary)
321                .map_err(Error::other)?;
322            return Ok(output.len() - start_total);
323        }
324        // Mid-frame fallback: drain the partially-read CURRENT frame through the
325        // generic path, then decode any FOLLOWING concatenated frames so
326        // read_to_end still consumes the source to true EOF.
327        loop {
328            let start = output.len();
329            output.resize(start + MAX_BLOCK_SIZE as usize, 0);
330            // On error, drop the just-grown (zeroed) tail before propagating so
331            // the caller never observes bytes that were never decoded.
332            let n = match self.read(&mut output[start..]) {
333                Ok(n) => n,
334                Err(e) => {
335                    output.truncate(start);
336                    return Err(e);
337                }
338            };
339            output.truncate(start + n);
340            if n == 0 {
341                break;
342            }
343        }
344        // Current frame fully drained; `source` is positioned at the next frame.
345        let mut rest = alloc::vec::Vec::new();
346        self.source.read_to_end(&mut rest)?;
347        if !rest.is_empty() {
348            let mut input = rest.as_slice();
349            self.decoder
350                .borrow_mut()
351                .decode_concatenated_frames_to_vec(&mut input, output, keep_dictionary)
352                .map_err(Error::other)?;
353        }
354        Ok(output.len() - start_total)
355    }
356
357    /// no_std counterpart of the decode-in-place `read_to_end` fast path above
358    /// (the no_std `Read::read_to_end` returns `()` instead of the byte count).
359    #[cfg(not(feature = "std"))]
360    fn read_to_end(&mut self, output: &mut alloc::vec::Vec<u8>) -> Result<(), Error> {
361        let at_start = {
362            let d = self.decoder.borrow_mut();
363            d.is_at_frame_start() && d.can_collect() == 0
364        };
365        // As in the std path: the decoder's own dictionary, reused in place.
366        let keep_dictionary = self.forced_dictionary;
367        if at_start {
368            let mut compressed = alloc::vec::Vec::new();
369            self.source.read_to_end(&mut compressed)?;
370            self.decoder
371                .borrow_mut()
372                .decode_current_frame_to_vec(&compressed, output, keep_dictionary)
373                .map_err(|e| Error::new(ErrorKind::Other, alloc::boxed::Box::new(e)))?;
374            return Ok(());
375        }
376        // Mid-frame fallback: drain the partial CURRENT frame, then decode the
377        // FOLLOWING concatenated frames so the source is consumed to true EOF.
378        loop {
379            let start = output.len();
380            output.resize(start + MAX_BLOCK_SIZE as usize, 0);
381            // On error, drop the just-grown (zeroed) tail before propagating so
382            // the caller never observes bytes that were never decoded.
383            let n = match self.read(&mut output[start..]) {
384                Ok(n) => n,
385                Err(e) => {
386                    output.truncate(start);
387                    return Err(e);
388                }
389            };
390            output.truncate(start + n);
391            if n == 0 {
392                break;
393            }
394        }
395        let mut rest = alloc::vec::Vec::new();
396        self.source.read_to_end(&mut rest)?;
397        if !rest.is_empty() {
398            let mut input = rest.as_slice();
399            self.decoder
400                .borrow_mut()
401                .decode_concatenated_frames_to_vec(&mut input, output, keep_dictionary)
402                .map_err(|e| Error::new(ErrorKind::Other, alloc::boxed::Box::new(e)))?;
403        }
404        Ok(())
405    }
406}
407
408#[cfg(test)]
409mod tests;