Skip to main content

zstd/stream/
functions.rs

1use std::io;
2use std::io::BufRead;
3
4use super::{Decoder, Encoder};
5
6/// How much `decode_all` will reserve up front, at most.
7///
8/// The size comes out of the frame header, which is whatever the data claims
9/// it is - so it is capped, or a few bytes of input could ask us for an
10/// arbitrary amount of memory. Past this the output just grows as it goes,
11/// which is what the whole function used to do; the reserve is only worth
12/// anything for the first few megabytes anyway.
13const MAX_RESERVED: usize = 32 * 1024 * 1024;
14
15/// Reads the decompressed size out of the start of a frame, if it says.
16///
17/// Returns `None` if `data` does not begin with a zstd frame header, or if the
18/// frame does not record its content size - which is normal for anything
19/// produced by a streaming compressor that was not told the size in advance.
20///
21/// This is the size to reserve when decompressing into your own buffer:
22///
23/// ```
24/// # fn main() -> std::io::Result<()> {
25/// let frame = zstd::encode_all(&b"some data"[..], 3)?;
26///
27/// let mut output = match zstd::decompressed_size(&frame) {
28///     Some(size) => Vec::with_capacity(size as usize),
29///     None => Vec::new(),
30/// };
31/// zstd::stream::copy_decode(&frame[..], &mut output)?;
32/// # assert_eq!(output, b"some data");
33/// # Ok(())
34/// # }
35/// ```
36pub fn decompressed_size(data: &[u8]) -> Option<u64> {
37    zstd_safe::get_frame_content_size(data).ok().flatten()
38}
39
40/// Decompress from the given source as if using a `Decoder`.
41///
42/// The input data must be in the zstd frame format.
43///
44/// When the frame records its decompressed size, the returned `Vec` is sized
45/// for it up front. That is worth about a factor of two over letting it grow,
46/// so prefer this to driving a `Decoder` into a `Vec::new()` yourself. If you
47/// are decompressing into a buffer you own, [`decompressed_size()`] gives you
48/// the same number.
49pub fn decode_all<R: io::Read>(source: R) -> io::Result<Vec<u8>> {
50    // Peek at the frame header - `fill_buf` does not consume it - so the
51    // output can be sized before we start.
52    let mut source =
53        io::BufReader::with_capacity(zstd_safe::DCtx::in_size(), source);
54    let reserve = decompressed_size(source.fill_buf()?)
55        .unwrap_or(0)
56        .min(MAX_RESERVED as u64) as usize;
57
58    let mut result = Vec::new();
59    // A failed reservation is not fatal: decoding still works, just without
60    // the head start. This also keeps a huge claimed size from aborting.
61    let _ = result.try_reserve(reserve);
62
63    let mut decoder = Decoder::with_buffer(source)?;
64    io::copy(&mut decoder, &mut result)?;
65    Ok(result)
66}
67
68/// Decompress from the given source as if using a `Decoder`.
69///
70/// Decompressed data will be appended to `destination`.
71pub fn copy_decode<R, W>(source: R, mut destination: W) -> io::Result<()>
72where
73    R: io::Read,
74    W: io::Write,
75{
76    let mut decoder = Decoder::new(source)?;
77    io::copy(&mut decoder, &mut destination)?;
78    Ok(())
79}
80
81/// Compress all data from the given source as if using an `Encoder`.
82///
83/// Result will be in the zstd frame format.
84///
85/// A level of `0` uses zstd's default (currently `3`).
86///
87/// Because the source is read as a stream, its length is not known in advance,
88/// so the frame this produces does not record its decompressed size. Whoever
89/// decompresses it cannot size their buffer up front, which costs them about a
90/// factor of two - see [`decompressed_size()`]. Use [`crate::bulk::compress()`]
91/// for a slice you already hold, [`compress_file()`] for a file, or
92/// [`compress_sized()`] when you know the length some other way; all three
93/// record it.
94pub fn encode_all<R: io::Read>(source: R, level: i32) -> io::Result<Vec<u8>> {
95    let mut result = Vec::<u8>::new();
96    copy_encode(source, &mut result, level)?;
97    Ok(result)
98}
99
100/// Compress everything from `source`, whose length you already know.
101///
102/// The length is recorded in the frame header, which is what lets whoever
103/// decompresses it size their buffer up front - worth about a factor of two to
104/// them. [`encode_all()`] cannot do this, because it has no way to know how
105/// much is coming.
106///
107/// `size` must be exactly how many bytes `source` yields; zstd fails the frame
108/// if it turns out to be wrong.
109///
110/// For data already in a slice, use [`crate::bulk::compress()`], which knows
111/// the length without being told. For a file, [`compress_file()`].
112///
113/// ```
114/// # fn main() -> std::io::Result<()> {
115/// let data = b"the length of this is known up front";
116///
117/// let frame = zstd::compress_sized(&data[..], 3, data.len() as u64)?;
118///
119/// assert_eq!(zstd::decompressed_size(&frame), Some(data.len() as u64));
120/// # assert_eq!(zstd::decode_all(&frame[..])?, data);
121/// # Ok(())
122/// # }
123/// ```
124pub fn compress_sized<R: io::Read>(
125    source: R,
126    level: i32,
127    size: u64,
128) -> io::Result<Vec<u8>> {
129    let mut result = Vec::new();
130    compress_sized_into(source, &mut result, level, size)?;
131    Ok(result)
132}
133
134/// Compress everything from `source`, whose length you already know, into
135/// `destination`.
136///
137/// Like [`copy_encode()`], but the length is recorded in the frame header, so
138/// whoever decompresses it can size their buffer up front - worth about a
139/// factor of two to them.
140///
141/// Nothing larger than the compressor's own buffers is held in memory, so this
142/// is the one to reach for when the data is too big to want a `Vec` of it.
143///
144/// `size` must be exactly how many bytes `source` yields; zstd fails the frame
145/// if it turns out to be wrong.
146///
147/// ```no_run
148/// # fn main() -> std::io::Result<()> {
149/// let input = std::fs::File::open("input.txt")?;
150/// let size = input.metadata()?.len();
151/// let output = std::fs::File::create("input.txt.zst")?;
152///
153/// zstd::compress_sized_into(input, output, 3, size)?;
154/// # Ok(())
155/// # }
156/// ```
157pub fn compress_sized_into<R, W>(
158    mut source: R,
159    destination: W,
160    level: i32,
161    size: u64,
162) -> io::Result<()>
163where
164    R: io::Read,
165    W: io::Write,
166{
167    let mut encoder = Encoder::new(destination, level)?;
168    encoder.set_pledged_src_size(Some(size))?;
169    io::copy(&mut source, &mut encoder)?;
170    encoder.finish()?;
171    Ok(())
172}
173
174/// Compress the contents of a file.
175///
176/// The file's length comes from its metadata and is recorded in the frame, so
177/// whoever decompresses it can size their buffer up front. The contents are
178/// streamed through the compressor rather than read into memory first, so only
179/// the compressed result is held.
180///
181/// That result is still a `Vec`, so for a file large enough that you would
182/// rather not have one, use [`compress_file_into()`] instead.
183///
184/// ```no_run
185/// # fn main() -> std::io::Result<()> {
186/// let frame = zstd::compress_file("input.txt", 3)?;
187/// # Ok(())
188/// # }
189/// ```
190pub fn compress_file<P: AsRef<std::path::Path>>(
191    path: P,
192    level: i32,
193) -> io::Result<Vec<u8>> {
194    let mut result = Vec::new();
195    compress_file_into(path, &mut result, level)?;
196    Ok(result)
197}
198
199/// Compress the contents of a file into `destination`.
200///
201/// Like [`compress_file()`], but writing rather than handing back a
202/// `Vec` - so nothing beyond the compressor's own buffers is held, whatever
203/// the size of the file.
204///
205/// ```no_run
206/// # fn main() -> std::io::Result<()> {
207/// let output = std::fs::File::create("input.txt.zst")?;
208///
209/// zstd::compress_file_into("input.txt", output, 3)?;
210/// # Ok(())
211/// # }
212/// ```
213pub fn compress_file_into<P, W>(
214    path: P,
215    destination: W,
216    level: i32,
217) -> io::Result<()>
218where
219    P: AsRef<std::path::Path>,
220    W: io::Write,
221{
222    let file = std::fs::File::open(path)?;
223    let size = file.metadata()?.len();
224
225    // Read in chunks the compressor is happy with, rather than leaving
226    // `io::copy` to pull from the file 8 KiB at a time.
227    let source =
228        io::BufReader::with_capacity(zstd_safe::CCtx::in_size(), file);
229
230    compress_sized_into(source, destination, level, size)
231}
232
233/// Compress all data from the given source as if using an `Encoder`.
234///
235/// Compressed data will be appended to `destination`.
236///
237/// A level of `0` uses zstd's default (currently `3`).
238pub fn copy_encode<R, W>(
239    mut source: R,
240    destination: W,
241    level: i32,
242) -> io::Result<()>
243where
244    R: io::Read,
245    W: io::Write,
246{
247    let mut encoder = Encoder::new(destination, level)?;
248    io::copy(&mut source, &mut encoder)?;
249    encoder.finish()?;
250    Ok(())
251}
252
253#[cfg(test)]
254mod tests {}