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}