Coverage Report

Created: 2026-09-03 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/zstd-0.11.2+zstd.1.5.2/src/dict.rs
Line
Count
Source
1
//! Train a dictionary from various sources.
2
//!
3
//! A dictionary can help improve the compression of small files.
4
//! The dictionary must be present during decompression,
5
//! but can be shared accross multiple "similar" files.
6
//!
7
//! Creating a dictionary using the `zstd` C library,
8
//! using the `zstd` command-line interface, using this library,
9
//! or using the `train` binary provided, should give the same result,
10
//! and are therefore completely compatible.
11
//!
12
//! To use, see [`Encoder::with_dictionary`] or [`Decoder::with_dictionary`].
13
//!
14
//! [`Encoder::with_dictionary`]: ../struct.Encoder.html#method.with_dictionary
15
//! [`Decoder::with_dictionary`]: ../struct.Decoder.html#method.with_dictionary
16
17
#[cfg(feature = "zdict_builder")]
18
use std::io::{self, Read};
19
20
pub use zstd_safe::{CDict, DDict};
21
22
/// Prepared dictionary for compression
23
///
24
/// A dictionary can include its own copy of the data (if it is `'static`), or it can merely point
25
/// to a separate buffer (if it has another lifetime).
26
pub struct EncoderDictionary<'a> {
27
    cdict: CDict<'a>,
28
}
29
30
impl EncoderDictionary<'static> {
31
    /// Creates a prepared dictionary for compression.
32
    ///
33
    /// This will copy the dictionary internally.
34
0
    pub fn copy(dictionary: &[u8], level: i32) -> Self {
35
0
        Self {
36
0
            cdict: zstd_safe::create_cdict(dictionary, level),
37
0
        }
38
0
    }
39
}
40
41
impl<'a> EncoderDictionary<'a> {
42
    #[cfg(feature = "experimental")]
43
    #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
44
    /// Create prepared dictionary for compression
45
    ///
46
    /// A level of `0` uses zstd's default (currently `3`).
47
    ///
48
    /// Only available with the `experimental` feature. Use `EncoderDictionary::copy` otherwise.
49
    pub fn new(dictionary: &'a [u8], level: i32) -> Self {
50
        Self {
51
            cdict: zstd_safe::create_cdict_by_reference(dictionary, level),
52
        }
53
    }
54
55
    /// Returns reference to `CDict` inner object
56
0
    pub fn as_cdict(&self) -> &CDict<'a> {
57
0
        &self.cdict
58
0
    }
59
}
60
61
/// Prepared dictionary for decompression
62
pub struct DecoderDictionary<'a> {
63
    ddict: DDict<'a>,
64
}
65
66
impl DecoderDictionary<'static> {
67
    /// Create a prepared dictionary for decompression.
68
    ///
69
    /// This will copy the dictionary internally.
70
0
    pub fn copy(dictionary: &[u8]) -> Self {
71
0
        Self {
72
0
            ddict: zstd_safe::DDict::create(dictionary),
73
0
        }
74
0
    }
75
}
76
77
impl<'a> DecoderDictionary<'a> {
78
    #[cfg(feature = "experimental")]
79
    #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "experimental")))]
80
    /// Create prepared dictionary for decompression
81
    ///
82
    /// Only available with the `experimental` feature. Use `DecoderDictionary::copy` otherwise.
83
    pub fn new(dict: &'a [u8]) -> Self {
84
        Self {
85
            ddict: zstd_safe::create_ddict_by_reference(dict),
86
        }
87
    }
88
89
    /// Returns reference to `DDict` inner object
90
0
    pub fn as_ddict(&self) -> &DDict<'a> {
91
0
        &self.ddict
92
0
    }
93
}
94
95
/// Train a dictionary from a big continuous chunk of data.
96
///
97
/// This is the most efficient way to train a dictionary,
98
/// since this is directly fed into `zstd`.
99
#[cfg(feature = "zdict_builder")]
100
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "zdict_builder")))]
101
0
pub fn from_continuous(
102
0
    sample_data: &[u8],
103
0
    sample_sizes: &[usize],
104
0
    max_size: usize,
105
0
) -> io::Result<Vec<u8>> {
106
    use crate::map_error_code;
107
108
    // Complain if the lengths don't add up to the entire data.
109
0
    if sample_sizes.iter().sum::<usize>() != sample_data.len() {
110
0
        return Err(io::Error::new(
111
0
            io::ErrorKind::Other,
112
0
            "sample sizes don't add up".to_string(),
113
0
        ));
114
0
    }
115
116
0
    let mut result = Vec::with_capacity(max_size);
117
0
    zstd_safe::train_from_buffer(&mut result, sample_data, sample_sizes)
118
0
        .map_err(map_error_code)?;
119
0
    Ok(result)
120
0
}
121
122
/// Train a dictionary from multiple samples.
123
///
124
/// The samples will internaly be copied to a single continuous buffer,
125
/// so make sure you have enough memory available.
126
///
127
/// If you need to stretch your system's limits,
128
/// [`from_continuous`] directly uses the given slice.
129
///
130
/// [`from_continuous`]: ./fn.from_continuous.html
131
#[cfg(feature = "zdict_builder")]
132
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "zdict_builder")))]
133
0
pub fn from_samples<S: AsRef<[u8]>>(
134
0
    samples: &[S],
135
0
    max_size: usize,
136
0
) -> io::Result<Vec<u8>> {
137
    // Copy every sample to a big chunk of memory
138
0
    let data: Vec<_> =
139
0
        samples.iter().flat_map(|s| s.as_ref()).cloned().collect();
140
0
    let sizes: Vec<_> = samples.iter().map(|s| s.as_ref().len()).collect();
141
142
0
    from_continuous(&data, &sizes, max_size)
143
0
}
144
145
/// Train a dict from a list of files.
146
#[cfg(feature = "zdict_builder")]
147
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "zdict_builder")))]
148
0
pub fn from_files<I, P>(filenames: I, max_size: usize) -> io::Result<Vec<u8>>
149
0
where
150
0
    P: AsRef<std::path::Path>,
151
0
    I: IntoIterator<Item = P>,
152
{
153
    use std::fs;
154
155
0
    let mut buffer = Vec::new();
156
0
    let mut sizes = Vec::new();
157
158
0
    for filename in filenames {
159
0
        let mut file = fs::File::open(filename)?;
160
0
        let len = file.read_to_end(&mut buffer)?;
161
0
        sizes.push(len);
162
    }
163
164
0
    from_continuous(&buffer, &sizes, max_size)
165
0
}
166
167
#[cfg(test)]
168
#[cfg(feature = "zdict_builder")]
169
mod tests {
170
    use std::fs;
171
    use std::io;
172
    use std::io::Read;
173
174
    use walkdir;
175
176
    #[test]
177
    fn test_dict_training() {
178
        // Train a dictionary
179
        let paths: Vec<_> = walkdir::WalkDir::new("src")
180
            .into_iter()
181
            .map(|entry| entry.unwrap())
182
            .map(|entry| entry.into_path())
183
            .filter(|path| path.to_str().unwrap().ends_with(".rs"))
184
            .collect();
185
186
        let dict = super::from_files(&paths, 4000).unwrap();
187
188
        for path in paths {
189
            let mut buffer = Vec::new();
190
            let mut file = fs::File::open(path).unwrap();
191
            let mut content = Vec::new();
192
            file.read_to_end(&mut content).unwrap();
193
            io::copy(
194
                &mut &content[..],
195
                &mut crate::stream::Encoder::with_dictionary(
196
                    &mut buffer,
197
                    1,
198
                    &dict,
199
                )
200
                .unwrap()
201
                .auto_finish(),
202
            )
203
            .unwrap();
204
205
            let mut result = Vec::new();
206
            io::copy(
207
                &mut crate::stream::Decoder::with_dictionary(
208
                    &buffer[..],
209
                    &dict[..],
210
                )
211
                .unwrap(),
212
                &mut result,
213
            )
214
            .unwrap();
215
216
            assert_eq!(&content, &result);
217
        }
218
    }
219
}