async_compression/
lib.rs

1//! Adaptors between compression crates and Rust's modern asynchronous IO types.
2//!
3
4//! # Feature Organization
5//!
6//! This crate is divided up along two axes, which can each be individually selected via Cargo
7//! features.
8//!
9//! All features are disabled by default, you should enable just the ones you need from the lists
10//! below.
11//!
12//! If you want to pull in everything there are three group features defined:
13//!
14
15//!  Feature | Does
16//! ---------|------
17//!  `all`   | Activates all implementations and algorithms.
18//!  `all-implementations` | Activates all implementations, needs to be paired with a selection of algorithms
19//!  `all-algorithms` | Activates all algorithms, needs to be paired with a selection of implementations
20//!
21
22//! ## IO implementation
23//!
24//! The first division is which underlying asynchronous IO trait will be wrapped, these are
25//! available as separate features that have corresponding top-level modules:
26//!
27
28//!  Feature | Type
29//! ---------|------
30// TODO: Kill rustfmt on this section, `#![rustfmt::skip::attributes(cfg_attr)]` should do it, but
31// that's unstable
32#![allow(unexpected_cfgs)]
33#![cfg_attr(
34    feature = "futures-io",
35    doc = "[`futures-io`] | [`futures::io::AsyncBufRead`](futures_io::AsyncBufRead), [`futures::io::AsyncWrite`](futures_io::AsyncWrite)"
36)]
37#![cfg_attr(
38    not(feature = "futures-io"),
39    doc = "`futures-io` (*inactive*) | `futures::io::AsyncBufRead`, `futures::io::AsyncWrite`"
40)]
41#![cfg_attr(
42    feature = "tokio",
43    doc = "[`tokio`] | [`tokio::io::AsyncBufRead`](::tokio::io::AsyncBufRead), [`tokio::io::AsyncWrite`](::tokio::io::AsyncWrite)"
44)]
45#![cfg_attr(
46    not(feature = "tokio"),
47    doc = "`tokio` (*inactive*) | `tokio::io::AsyncBufRead`, `tokio::io::AsyncWrite`"
48)]
49//!
50
51//! ## Compression algorithm
52//!
53//! The second division is which compression schemes to support, there are currently a few
54//! available choices, these determine which types will be available inside the above modules:
55//!
56
57//!  Feature | Types
58//! ---------|------
59#![cfg_attr(
60    feature = "brotli",
61    doc = "`brotli` | [`BrotliEncoder`](?search=BrotliEncoder), [`BrotliDecoder`](?search=BrotliDecoder)"
62)]
63#![cfg_attr(
64    not(feature = "brotli"),
65    doc = "`brotli` (*inactive*) | `BrotliEncoder`, `BrotliDecoder`"
66)]
67#![cfg_attr(
68    feature = "bzip2",
69    doc = "`bzip2` | [`BzEncoder`](?search=BzEncoder), [`BzDecoder`](?search=BzDecoder)"
70)]
71#![cfg_attr(
72    not(feature = "bzip2"),
73    doc = "`bzip2` (*inactive*) | `BzEncoder`, `BzDecoder`"
74)]
75#![cfg_attr(
76    feature = "deflate",
77    doc = "`deflate` | [`DeflateEncoder`](?search=DeflateEncoder), [`DeflateDecoder`](?search=DeflateDecoder)"
78)]
79#![cfg_attr(
80    not(feature = "deflate"),
81    doc = "`deflate` (*inactive*) | `DeflateEncoder`, `DeflateDecoder`"
82)]
83#![cfg_attr(
84    feature = "gzip",
85    doc = "`gzip` | [`GzipEncoder`](?search=GzipEncoder), [`GzipDecoder`](?search=GzipDecoder)"
86)]
87#![cfg_attr(
88    not(feature = "gzip"),
89    doc = "`gzip` (*inactive*) | `GzipEncoder`, `GzipDecoder`"
90)]
91#![cfg_attr(
92    feature = "lz4",
93    doc = "`lz4` | [`Lz4Encoder`](?search=Lz4Encoder), [`Lz4Decoder`](?search=Lz4Decoder)"
94)]
95#![cfg_attr(
96    not(feature = "lz4"),
97    doc = "`lz4` (*inactive*) | `Lz4Encoder`, `Lz4Decoder`"
98)]
99#![cfg_attr(
100    feature = "lzma",
101    doc = "`lzma` | [`LzmaEncoder`](?search=LzmaEncoder), [`LzmaDecoder`](?search=LzmaDecoder)"
102)]
103#![cfg_attr(
104    not(feature = "lzma"),
105    doc = "`lzma` (*inactive*) | `LzmaEncoder`, `LzmaDecoder`"
106)]
107#![cfg_attr(
108    feature = "xz",
109    doc = "`xz` | [`XzEncoder`](?search=XzEncoder), [`XzDecoder`](?search=XzDecoder)"
110)]
111#![cfg_attr(
112    not(feature = "xz"),
113    doc = "`xz` (*inactive*) | `XzEncoder`, `XzDecoder`"
114)]
115#![cfg_attr(
116    feature = "zlib",
117    doc = "`zlib` | [`ZlibEncoder`](?search=ZlibEncoder), [`ZlibDecoder`](?search=ZlibDecoder)"
118)]
119#![cfg_attr(
120    not(feature = "zlib"),
121    doc = "`zlib` (*inactive*) | `ZlibEncoder`, `ZlibDecoder`"
122)]
123#![cfg_attr(
124    feature = "zstd",
125    doc = "`zstd` | [`ZstdEncoder`](?search=ZstdEncoder), [`ZstdDecoder`](?search=ZstdDecoder)"
126)]
127#![cfg_attr(
128    not(feature = "zstd"),
129    doc = "`zstd` (*inactive*) | `ZstdEncoder`, `ZstdDecoder`"
130)]
131#![cfg_attr(
132    feature = "deflate64",
133    doc = "`deflate64` | (encoder not implemented), [`Deflate64Decoder`](?search=Deflate64Decoder)"
134)]
135#![cfg_attr(
136    not(feature = "deflate64"),
137    doc = "`deflate64` (*inactive*) | (encoder not implemented), `Deflate64Decoder`"
138)]
139//!
140
141//! ## Multi-thread support
142//! The `xz` compression algorithm supports multi-threaded compression and decompression.
143//! Enable the `xz-parallel` feature to enable multi-threading support.
144//!
145
146#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg))]
147#![warn(
148    missing_docs,
149    rust_2018_idioms,
150    missing_copy_implementations,
151    missing_debug_implementations
152)]
153#![cfg_attr(not(all), allow(unused))]
154
155#[cfg(any(
156    feature = "bzip2",
157    feature = "flate2",
158    feature = "lzma",
159    feature = "lz4"
160))]
161use std::convert::TryInto;
162
163#[macro_use]
164mod macros;
165mod codec;
166
167#[cfg(feature = "futures-io")]
168pub mod futures;
169#[cfg(feature = "tokio")]
170pub mod tokio;
171
172mod unshared;
173mod util;
174
175#[cfg(feature = "brotli")]
176pub mod brotli;
177#[cfg(feature = "lz4")]
178pub mod lz4;
179#[cfg(feature = "zstd")]
180pub mod zstd;
181
182/// Level of compression data should be compressed with.
183#[non_exhaustive]
184#[derive(Clone, Copy, Debug)]
185pub enum Level {
186    /// Fastest quality of compression, usually produces bigger size.
187    Fastest,
188
189    /// Best quality of compression, usually produces the smallest size.
190    Best,
191
192    /// Default quality of compression defined by the selected compression algorithm.
193    Default,
194
195    /// Precise quality based on the underlying compression algorithms' qualities. The
196    /// interpretation of this depends on the algorithm chosen and the specific implementation
197    /// backing it. Qualities are implicitly clamped to the algorithm's maximum.
198    Precise(i32),
199}
200
201impl Level {
202    #[cfg(feature = "brotli")]
203    fn into_brotli(
204        self,
205        mut params: ::brotli::enc::backward_references::BrotliEncoderParams,
206    ) -> ::brotli::enc::backward_references::BrotliEncoderParams {
207        match self {
208            Self::Fastest => params.quality = 0,
209            Self::Best => params.quality = 11,
210            Self::Precise(quality) => params.quality = quality.clamp(0, 11),
211            Self::Default => (),
212        }
213
214        params
215    }
216
217    #[cfg(feature = "bzip2")]
218    fn into_bzip2(self) -> bzip2::Compression {
219        let fastest = bzip2::Compression::fast();
220        let best = bzip2::Compression::best();
221
222        match self {
223            Self::Fastest => fastest,
224            Self::Best => best,
225            Self::Precise(quality) => bzip2::Compression::new(
226                quality
227                    .try_into()
228                    .unwrap_or(0)
229                    .clamp(fastest.level(), best.level()),
230            ),
231            Self::Default => bzip2::Compression::default(),
232        }
233    }
234
235    #[cfg(feature = "flate2")]
236    fn into_flate2(self) -> flate2::Compression {
237        let fastest = flate2::Compression::fast();
238        let best = flate2::Compression::best();
239        let none = flate2::Compression::none();
240
241        match self {
242            Self::Fastest => fastest,
243            Self::Best => best,
244            Self::Precise(quality) => flate2::Compression::new(
245                quality
246                    .try_into()
247                    .unwrap_or(0)
248                    .clamp(none.level(), best.level()),
249            ),
250            Self::Default => flate2::Compression::default(),
251        }
252    }
253
254    #[cfg(feature = "zstd")]
255    fn into_zstd(self) -> i32 {
256        let (fastest, best) = libzstd::compression_level_range().into_inner();
257
258        // NOTE: zstd's "fastest" level is -131072 which can create outputs larger than inputs.
259        // This library chooses a "fastest" level which has a more-or-less equivalent compression
260        // ratio to gzip's fastest mode. We still allow precise levels to go negative.
261        // See discussion in https://github.com/Nullus157/async-compression/issues/352
262        const OUR_FASTEST: i32 = 1;
263
264        match self {
265            Self::Fastest => OUR_FASTEST,
266            Self::Best => best,
267            Self::Precise(quality) => quality.clamp(fastest, best),
268            Self::Default => libzstd::DEFAULT_COMPRESSION_LEVEL,
269        }
270    }
271
272    #[cfg(feature = "lzma")]
273    fn into_xz2(self) -> u32 {
274        match self {
275            Self::Fastest => 0,
276            Self::Best => 9,
277            Self::Precise(quality) => quality.try_into().unwrap_or(0).min(9),
278            Self::Default => 5,
279        }
280    }
281
282    #[cfg(feature = "lz4")]
283    fn into_lz4(
284        self,
285        mut preferences: ::lz4::liblz4::LZ4FPreferences,
286    ) -> ::lz4::liblz4::LZ4FPreferences {
287        let level = match self {
288            Self::Fastest => 0,
289            Self::Best => 12,
290            Self::Precise(quality) => quality.try_into().unwrap_or(0).min(12),
291            Self::Default => 0,
292        };
293
294        preferences.compression_level = level;
295        preferences
296    }
297}