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 /// Dictionary the decoder was constructed with, if any. Retained so the
52 /// `read_to_end` paths can re-initialise FOLLOWING concatenated frames with
53 /// the same forced dictionary (a plain re-init resolves dictionaries by
54 /// frame id only and would lose a forced dict for frames omitting the id).
55 /// Cheap to hold: `DictionaryHandle` is an `Arc`/`Rc` handle.
56 dict: Option<DictionaryHandle>,
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 dict: None,
69 })
70 }
71}
72
73impl<READ: Read> StreamingDecoder<READ, FrameDecoder> {
74 pub fn new(
75 mut source: READ,
76 ) -> Result<StreamingDecoder<READ, FrameDecoder>, FrameDecoderError> {
77 let mut decoder = FrameDecoder::new();
78 decoder.init(&mut source)?;
79 Ok(StreamingDecoder {
80 decoder,
81 source,
82 dict: None,
83 })
84 }
85
86 /// Create a streaming decoder using a pre-parsed dictionary handle.
87 ///
88 /// # Warning
89 ///
90 /// This constructor initializes the underlying [`FrameDecoder`] with
91 /// `dict`, even if a frame header omits the optional dictionary ID.
92 /// Callers must only use it when they already know the stream was encoded
93 /// with this dictionary; otherwise decoded output can be silently
94 /// corrupted.
95 pub fn new_with_dictionary_handle(
96 mut source: READ,
97 dict: &DictionaryHandle,
98 ) -> Result<StreamingDecoder<READ, FrameDecoder>, FrameDecoderError> {
99 let mut decoder = FrameDecoder::new();
100 decoder.init_with_dict_handle(&mut source, dict)?;
101 Ok(StreamingDecoder {
102 decoder,
103 source,
104 dict: Some(dict.clone()),
105 })
106 }
107
108 /// Create a streaming decoder using a serialized dictionary blob.
109 ///
110 /// # Warning
111 ///
112 /// This API forwards to [`StreamingDecoder::new_with_dictionary_handle`]
113 /// and therefore applies the decoded dictionary to frames whose headers may
114 /// omit the optional dictionary ID. Only use it when the stream is known to
115 /// be encoded with that dictionary.
116 pub fn new_with_dictionary_bytes(
117 source: READ,
118 raw_dictionary: &[u8],
119 ) -> Result<StreamingDecoder<READ, FrameDecoder>, FrameDecoderError> {
120 let dict = DictionaryHandle::decode_dict(raw_dictionary)?;
121 Self::new_with_dictionary_handle(source, &dict)
122 }
123}
124
125impl<READ: Read, DEC: BorrowMut<FrameDecoder>> StreamingDecoder<READ, DEC> {
126 /// Gets a reference to the underlying reader.
127 pub fn get_ref(&self) -> &READ {
128 &self.source
129 }
130
131 /// Gets a mutable reference to the underlying reader.
132 ///
133 /// It is inadvisable to directly read from the underlying reader.
134 pub fn get_mut(&mut self) -> &mut READ {
135 &mut self.source
136 }
137
138 /// Gets a mutable reference to the frame decoder driving this stream.
139 ///
140 /// Exposed for settings that are read as decoding proceeds rather than at
141 /// construction — [`FrameDecoder::set_content_checksum`] above all, which a
142 /// caller that wants mismatches to fail (rather than merely be computed)
143 /// has to reach after the constructor has chosen and initialised the
144 /// decoder, including on the dictionary paths.
145 pub fn decoder_mut(&mut self) -> &mut FrameDecoder {
146 self.decoder.borrow_mut()
147 }
148
149 /// Destructures this object into the inner reader.
150 pub fn into_inner(self) -> READ
151 where
152 READ: Sized,
153 {
154 self.source
155 }
156
157 /// Destructures this object into both the inner reader and [FrameDecoder].
158 pub fn into_parts(self) -> (READ, DEC)
159 where
160 READ: Sized,
161 {
162 (self.source, self.decoder)
163 }
164
165 /// Destructures this object into the inner [FrameDecoder].
166 pub fn into_frame_decoder(self) -> DEC {
167 self.decoder
168 }
169}
170
171impl<READ: Read, DEC: BorrowMut<FrameDecoder>> Read for StreamingDecoder<READ, DEC> {
172 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
173 let decoder = self.decoder.borrow_mut();
174 if decoder.is_finished() && decoder.can_collect() == 0 {
175 // Frame fully decoded and fully drained: the running XXH64 digest
176 // is final, so a `Verify`-mode decoder validates the content
177 // checksum at this finish point. No-op in other modes.
178 #[cfg(feature = "hash")]
179 if let Err(e) = decoder.verify_content_checksum() {
180 #[cfg(feature = "std")]
181 return Err(Error::other(e));
182 #[cfg(not(feature = "std"))]
183 return Err(Error::new(ErrorKind::Other, alloc::boxed::Box::new(e)));
184 }
185 //No more bytes can ever be decoded
186 return Ok(0);
187 }
188
189 // Interleave bounded decode with draining so the decode window
190 // (`RingBuffer`) stays near `window_size` instead of accumulating the
191 // whole request before a single end-of-call drain. `read_to_end` hands
192 // ever-larger buffers; decoding `buf.len()` worth into the ring up
193 // front grew it far past the window (repeated `reserve_amortized`
194 // alloc+copy). Decode at most one block worth per step, then drain
195 // what is now collectable into `buf`, mirroring upstream zstd's
196 // window-bounded flush loop.
197 let mut written = 0;
198 while written < buf.len() {
199 // Drain whatever is collectable now (retaining `window_size` until
200 // the frame finishes). Reclaims the ring promptly so the next
201 // decode step reuses the same capacity.
202 written += decoder.read(&mut buf[written..])?;
203 if written == buf.len() || decoder.is_finished() {
204 break;
205 }
206 // Decode one bounded chunk. `UptoBytes` may overshoot a little but
207 // is capped to one block, so the ring's live region stays within
208 // `window_size + MAX_BLOCK_SIZE`.
209 let step = (buf.len() - written).min(MAX_BLOCK_SIZE as usize);
210 if let Err(e) =
211 decoder.decode_blocks(&mut self.source, BlockDecodingStrategy::UptoBytes(step))
212 {
213 #[cfg(feature = "std")]
214 {
215 return Err(Error::other(e));
216 }
217 #[cfg(not(feature = "std"))]
218 {
219 return Err(Error::new(ErrorKind::Other, alloc::boxed::Box::new(e)));
220 }
221 }
222 }
223
224 // The loop can finish AND fully drain a frame within this same call
225 // (decode last block, then drain it into `buf`). Validate here too when
226 // the frame is finished and nothing is left to collect, but ONLY when
227 // this call wrote no bytes: the `Read` contract forbids returning `Err`
228 // after bytes were delivered, so when `written > 0` the verify is
229 // deferred to the next call, where the top early-return runs it and
230 // returns `Err` on the zero-byte path. Idempotent with that top check.
231 #[cfg(feature = "hash")]
232 if written == 0
233 && decoder.is_finished()
234 && decoder.can_collect() == 0
235 && let Err(e) = decoder.verify_content_checksum()
236 {
237 #[cfg(feature = "std")]
238 return Err(Error::other(e));
239 #[cfg(not(feature = "std"))]
240 return Err(Error::new(ErrorKind::Other, alloc::boxed::Box::new(e)));
241 }
242
243 Ok(written)
244 }
245
246 /// Decode-in-place fast path for whole-frame consumption. Instead of the
247 /// generic `read` loop (decode block -> `RingBuffer` -> copy into the
248 /// caller buffer), buffer the (compressed, hence small) source and decode
249 /// STRAIGHT into `output`'s spare capacity via the single-copy direct path,
250 /// pre-sized from the frame's declared content size. Only taken when the
251 /// decoder is at a frame boundary (nothing partially decoded / undrained);
252 /// otherwise it falls back to the generic grow-and-`read` loop so a caller
253 /// that mixed `read` with `read_to_end` still gets correct output.
254 ///
255 /// Per the `Read::read_to_end` contract this consumes the source to EOF: if
256 /// the stream holds several concatenated frames they are ALL decoded (and
257 /// skippable frames skipped). To recover bytes that follow a single frame,
258 /// use `read` plus the
259 /// [`SkipFrame`](crate::decoding::errors::ReadFrameHeaderError::SkipFrame)
260 /// recreate-the-decoder pattern instead.
261 #[cfg(feature = "std")]
262 fn read_to_end(&mut self, output: &mut alloc::vec::Vec<u8>) -> Result<usize, Error> {
263 let start_total = output.len();
264 // `new()` already read the frame header, so the fast path applies when
265 // the decoder sits at the start of that frame with nothing decoded yet.
266 let at_start = {
267 let d = self.decoder.borrow_mut();
268 d.is_at_frame_start() && d.can_collect() == 0
269 };
270 // Clone the (cheap Arc/Rc) dict handle out so the `decoder` borrow below
271 // does not conflict with borrowing `self.dict`.
272 let dict = self.dict.clone();
273 if at_start {
274 let mut compressed = alloc::vec::Vec::new();
275 self.source.read_to_end(&mut compressed)?;
276 self.decoder
277 .borrow_mut()
278 .decode_current_frame_to_vec(&compressed, output, dict.as_ref())
279 .map_err(Error::other)?;
280 return Ok(output.len() - start_total);
281 }
282 // Mid-frame fallback: drain the partially-read CURRENT frame through the
283 // generic path, then decode any FOLLOWING concatenated frames so
284 // read_to_end still consumes the source to true EOF.
285 loop {
286 let start = output.len();
287 output.resize(start + MAX_BLOCK_SIZE as usize, 0);
288 // On error, drop the just-grown (zeroed) tail before propagating so
289 // the caller never observes bytes that were never decoded.
290 let n = match self.read(&mut output[start..]) {
291 Ok(n) => n,
292 Err(e) => {
293 output.truncate(start);
294 return Err(e);
295 }
296 };
297 output.truncate(start + n);
298 if n == 0 {
299 break;
300 }
301 }
302 // Current frame fully drained; `source` is positioned at the next frame.
303 let mut rest = alloc::vec::Vec::new();
304 self.source.read_to_end(&mut rest)?;
305 if !rest.is_empty() {
306 let mut input = rest.as_slice();
307 self.decoder
308 .borrow_mut()
309 .decode_concatenated_frames_to_vec(&mut input, output, dict.as_ref())
310 .map_err(Error::other)?;
311 }
312 Ok(output.len() - start_total)
313 }
314
315 /// no_std counterpart of the decode-in-place `read_to_end` fast path above
316 /// (the no_std `Read::read_to_end` returns `()` instead of the byte count).
317 #[cfg(not(feature = "std"))]
318 fn read_to_end(&mut self, output: &mut alloc::vec::Vec<u8>) -> Result<(), Error> {
319 let at_start = {
320 let d = self.decoder.borrow_mut();
321 d.is_at_frame_start() && d.can_collect() == 0
322 };
323 // Cheap Arc/Rc clone so the `decoder` borrow does not conflict with
324 // borrowing `self.dict`.
325 let dict = self.dict.clone();
326 if at_start {
327 let mut compressed = alloc::vec::Vec::new();
328 self.source.read_to_end(&mut compressed)?;
329 self.decoder
330 .borrow_mut()
331 .decode_current_frame_to_vec(&compressed, output, dict.as_ref())
332 .map_err(|e| Error::new(ErrorKind::Other, alloc::boxed::Box::new(e)))?;
333 return Ok(());
334 }
335 // Mid-frame fallback: drain the partial CURRENT frame, then decode the
336 // FOLLOWING concatenated frames so the source is consumed to true EOF.
337 loop {
338 let start = output.len();
339 output.resize(start + MAX_BLOCK_SIZE as usize, 0);
340 // On error, drop the just-grown (zeroed) tail before propagating so
341 // the caller never observes bytes that were never decoded.
342 let n = match self.read(&mut output[start..]) {
343 Ok(n) => n,
344 Err(e) => {
345 output.truncate(start);
346 return Err(e);
347 }
348 };
349 output.truncate(start + n);
350 if n == 0 {
351 break;
352 }
353 }
354 let mut rest = alloc::vec::Vec::new();
355 self.source.read_to_end(&mut rest)?;
356 if !rest.is_empty() {
357 let mut input = rest.as_slice();
358 self.decoder
359 .borrow_mut()
360 .decode_concatenated_frames_to_vec(&mut input, output, dict.as_ref())
361 .map_err(|e| Error::new(ErrorKind::Other, alloc::boxed::Box::new(e)))?;
362 }
363 Ok(())
364 }
365}
366
367#[cfg(test)]
368mod tests;