Skip to main content

zstd/bulk/
decompressor.rs

1use crate::map_error_code;
2
3use std::convert::TryInto;
4use std::io;
5use zstd_safe;
6
7/// Allows to decompress independently multiple blocks of data.
8///
9/// This reduces memory usage compared to calling `decompress` multiple times.
10#[derive(Default)]
11pub struct Decompressor<'a> {
12    context: zstd_safe::DCtx<'a>,
13}
14
15impl Decompressor<'static> {
16    /// Creates a new zstd decompressor.
17    pub fn new() -> io::Result<Self> {
18        Self::with_dictionary(&[])
19    }
20
21    /// Creates a new zstd decompressor, using the given dictionary.
22    pub fn with_dictionary(dictionary: &[u8]) -> io::Result<Self> {
23        let mut decompressor = Self::default();
24
25        decompressor.set_dictionary(dictionary)?;
26
27        Ok(decompressor)
28    }
29}
30
31impl<'a> Decompressor<'a> {
32    /// Creates a new decompressor using an existing `DecoderDictionary`.
33    ///
34    /// Note that using a dictionary means that compression will need to use
35    /// the same dictionary.
36    pub fn with_prepared_dictionary<'b>(
37        dictionary: &'a crate::dict::DecoderDictionary<'b>,
38    ) -> io::Result<Self>
39    where
40        'b: 'a,
41    {
42        let mut decompressor = Self::default();
43
44        decompressor.set_prepared_dictionary(dictionary)?;
45
46        Ok(decompressor)
47    }
48
49    /// Changes the dictionary used by this decompressor.
50    ///
51    /// Will affect future compression jobs.
52    ///
53    /// Note that using a dictionary means that compression will need to use
54    /// the same dictionary.
55    pub fn set_dictionary(&mut self, dictionary: &[u8]) -> io::Result<()> {
56        self.context
57            .load_dictionary(dictionary)
58            .map_err(map_error_code)?;
59
60        Ok(())
61    }
62
63    /// Changes the dictionary used by this decompressor.
64    ///
65    /// Note that using a dictionary means that compression will need to use
66    /// the same dictionary.
67    pub fn set_prepared_dictionary<'b>(
68        &mut self,
69        dictionary: &'a crate::dict::DecoderDictionary<'b>,
70    ) -> io::Result<()>
71    where
72        'b: 'a,
73    {
74        self.context
75            .ref_ddict(dictionary.as_ddict())
76            .map_err(map_error_code)?;
77
78        Ok(())
79    }
80
81    /// Deompress a single block of data to the given destination buffer.
82    ///
83    /// Returns the number of bytes written, or an error if something happened
84    /// (for instance if the destination buffer was too small).
85    /// [`crate::decompressed_size()`] says how big the result will be, when
86    /// the frame records it - there is no need to keep the size alongside the
87    /// data yourself. For a `Vec` it is the *capacity* that bounds the output,
88    /// so `reserve` what you need rather than `resize`.
89    pub fn decompress_to_buffer<C: zstd_safe::WriteBuf + ?Sized>(
90        &mut self,
91        source: &[u8],
92        destination: &mut C,
93    ) -> io::Result<usize> {
94        self.context
95            .decompress(destination, source)
96            .map_err(map_error_code)
97    }
98
99    /// Decompress a block of data, and return the result in a `Vec<u8>`.
100    ///
101    /// The decompressed data should be at most `capacity` bytes,
102    /// or an error will be returned.
103    pub fn decompress(
104        &mut self,
105        data: &[u8],
106        capacity: usize,
107    ) -> io::Result<Vec<u8>> {
108        // When every frame records its size, we know exactly how much this
109        // produces, so a smaller capacity cannot work. Say so with both
110        // numbers rather than letting zstd report a bare "destination buffer
111        // is too small" after trying.
112        //
113        // It has to be the exact figure: `upper_bound` may hand back an
114        // over-estimate for a frame that records nothing, and refusing on
115        // that would reject input which decompresses perfectly well.
116        let exact = total_decompressed_size(data);
117
118        let capacity = match exact {
119            Some(needed) if needed > capacity as u64 => {
120                return Err(io::Error::new(
121                    io::ErrorKind::Other,
122                    format!(
123                        "Destination buffer is too small: {} bytes needed, \
124                         {} given",
125                        needed, capacity
126                    ),
127                ))
128            }
129            // Known to fit, since it is not greater than `capacity`.
130            Some(needed) => needed as usize,
131            // The walk has already told us there is no total to be had, so
132            // go straight to zstd rather than back through `upper_bound`,
133            // which would only repeat it.
134            None => bound_from_zstd(data).unwrap_or(capacity).min(capacity),
135        };
136
137        let mut buffer = Vec::with_capacity(capacity);
138        self.decompress_to_buffer(data, &mut buffer)?;
139        Ok(buffer)
140    }
141
142    /// Sets a decompression parameter for this decompressor.
143    pub fn set_parameter(
144        &mut self,
145        parameter: zstd_safe::DParameter,
146    ) -> io::Result<()> {
147        self.context
148            .set_parameter(parameter)
149            .map_err(map_error_code)?;
150        Ok(())
151    }
152
153    crate::decoder_parameters!();
154
155    /// Get an upper bound on the decompressed size of data, if available
156    ///
157    /// This can be used to pre-allocate enough capacity for `decompress_to_buffer`
158    /// and is used by `decompress` to ensure that it does not over-allocate if
159    /// you supply a large `capacity`.
160    ///
161    /// Returns `None` if the size cannot be determined, or does not fit a
162    /// `usize`. That happens when the frame does not record its decompressed
163    /// size - which is normal for anything a streaming compressor produced
164    /// without being told the length up front.
165    ///
166    /// With the `experimental` feature this sums every frame in `data`.
167    /// Without it, the answer is limited to a single frame, since the function
168    /// that adds them up is part of zstd's experimental API.
169    pub fn upper_bound(data: &[u8]) -> Option<usize> {
170        // Walking the frames ourselves gives the exact total, and needs
171        // nothing experimental. Failing that, zstd can bound a frame that
172        // records no size, but only through its experimental API.
173        total_decompressed_size(data)
174            .and_then(|size| size.try_into().ok())
175            .or_else(|| bound_from_zstd(data))
176    }
177}
178
179/// What zstd can say about frames that do not record their decompressed size.
180///
181/// It bounds each one from its block count and window size, which needs
182/// walking the block headers - so this is the one part we do not reimplement,
183/// and without the experimental feature there is nothing to offer.
184fn bound_from_zstd(_data: &[u8]) -> Option<usize> {
185    #[cfg(feature = "experimental")]
186    if let Ok(bound) = zstd_safe::decompress_bound(_data) {
187        return bound.try_into().ok();
188    }
189
190    None
191}
192
193/// Adds up the decompressed size of every frame in `data`.
194///
195/// This is what `ZSTD_findDecompressedSize()` does, except that one is part of
196/// zstd's experimental API - and we have the whole input in hand anyway, so
197/// walking it is a few lines.
198///
199/// Returns `None` if `data` is not a clean sequence of frames, or if any of
200/// them does not record its decompressed size.
201///
202/// Skippable frames are handled: zstd reports their full length and a content
203/// size of zero, so they are stepped over without affecting the total.
204fn total_decompressed_size(mut data: &[u8]) -> Option<u64> {
205    let mut total = 0u64;
206
207    while !data.is_empty() {
208        let frame_len = zstd_safe::find_frame_compressed_size(data).ok()?;
209        let content = zstd_safe::get_frame_content_size(data).ok()??;
210
211        total = total.checked_add(content)?;
212        data = data.get(frame_len..)?;
213    }
214
215    Some(total)
216}
217
218fn _assert_traits() {
219    fn _assert_send<T: Send>(_: T) {}
220
221    _assert_send(Decompressor::new());
222}