zstd/bulk/
decompressor.rs

1use crate::map_error_code;
2
3#[cfg(feature = "experimental")]
4use std::convert::TryInto;
5use std::io;
6use zstd_safe;
7
8/// Allows to decompress independently multiple blocks of data.
9///
10/// This reduces memory usage compared to calling `decompress` multiple times.
11#[derive(Default)]
12pub struct Decompressor<'a> {
13    context: zstd_safe::DCtx<'a>,
14}
15
16impl Decompressor<'static> {
17    /// Creates a new zstd decompressor.
18    pub fn new() -> io::Result<Self> {
19        Self::with_dictionary(&[])
20    }
21
22    /// Creates a new zstd decompressor, using the given dictionary.
23    pub fn with_dictionary(dictionary: &[u8]) -> io::Result<Self> {
24        let mut decompressor = Self::default();
25
26        decompressor.set_dictionary(dictionary)?;
27
28        Ok(decompressor)
29    }
30}
31
32impl<'a> Decompressor<'a> {
33    /// Creates a new decompressor using an existing `DecoderDictionary`.
34    ///
35    /// Note that using a dictionary means that compression will need to use
36    /// the same dictionary.
37    pub fn with_prepared_dictionary<'b>(
38        dictionary: &'a crate::dict::DecoderDictionary<'b>,
39    ) -> io::Result<Self>
40    where
41        'b: 'a,
42    {
43        let mut decompressor = Self::default();
44
45        decompressor.set_prepared_dictionary(dictionary)?;
46
47        Ok(decompressor)
48    }
49
50    /// Changes the dictionary used by this decompressor.
51    ///
52    /// Will affect future compression jobs.
53    ///
54    /// Note that using a dictionary means that compression will need to use
55    /// the same dictionary.
56    pub fn set_dictionary(&mut self, dictionary: &[u8]) -> io::Result<()> {
57        self.context
58            .load_dictionary(dictionary)
59            .map_err(map_error_code)?;
60
61        Ok(())
62    }
63
64    /// Changes the dictionary used by this decompressor.
65    ///
66    /// Note that using a dictionary means that compression will need to use
67    /// the same dictionary.
68    pub fn set_prepared_dictionary<'b>(
69        &mut self,
70        dictionary: &'a crate::dict::DecoderDictionary<'b>,
71    ) -> io::Result<()>
72    where
73        'b: 'a,
74    {
75        self.context
76            .ref_ddict(dictionary.as_ddict())
77            .map_err(map_error_code)?;
78
79        Ok(())
80    }
81
82    /// Deompress a single block of data to the given destination buffer.
83    ///
84    /// Returns the number of bytes written, or an error if something happened
85    /// (for instance if the destination buffer was too small).
86    pub fn decompress_to_buffer<C: zstd_safe::WriteBuf + ?Sized>(
87        &mut self,
88        source: &[u8],
89        destination: &mut C,
90    ) -> io::Result<usize> {
91        self.context
92            .decompress(destination, source)
93            .map_err(map_error_code)
94    }
95
96    /// Decompress a block of data, and return the result in a `Vec<u8>`.
97    ///
98    /// The decompressed data should be less than `capacity` bytes,
99    /// or an error will be returned.
100    pub fn decompress(
101        &mut self,
102        data: &[u8],
103        capacity: usize,
104    ) -> io::Result<Vec<u8>> {
105        let capacity =
106            Self::upper_bound(data).unwrap_or(capacity).min(capacity);
107        let mut buffer = Vec::with_capacity(capacity);
108        self.decompress_to_buffer(data, &mut buffer)?;
109        Ok(buffer)
110    }
111
112    /// Sets a decompression parameter for this decompressor.
113    pub fn set_parameter(
114        &mut self,
115        parameter: zstd_safe::DParameter,
116    ) -> io::Result<()> {
117        self.context
118            .set_parameter(parameter)
119            .map_err(map_error_code)?;
120        Ok(())
121    }
122
123    crate::decoder_parameters!();
124
125    /// Get an upper bound on the decompressed size of data, if available
126    ///
127    /// This can be used to pre-allocate enough capacity for `decompress_to_buffer`
128    /// and is used by `decompress` to ensure that it does not over-allocate if
129    /// you supply a large `capacity`.
130    ///
131    /// Will return `None` if the upper bound cannot be determined or is larger than `usize::MAX`
132    ///
133    /// Note that unless the `experimental` feature is enabled, this will always return `None`.
134    pub fn upper_bound(_data: &[u8]) -> Option<usize> {
135        #[cfg(feature = "experimental")]
136        {
137            let bound = zstd_safe::decompress_bound(_data).ok()?;
138            bound.try_into().ok()
139        }
140        #[cfg(not(feature = "experimental"))]
141        {
142            None
143        }
144    }
145}
146
147fn _assert_traits() {
148    fn _assert_send<T: Send>(_: T) {}
149
150    _assert_send(Decompressor::new());
151}