compress_lib/lib.rs
1//! # compress_lib
2//!
3//! DEFLATE (RFC 1951), zlib (RFC 1950), and gzip (RFC 1952) for Rust
4//! services, with the per-message rules of WebSocket permessage-deflate
5//! (RFC 7692).
6//!
7//! The engine is sans-IO: the caller pushes input and pulls output through
8//! buffers it owns. Memory is fixed when an engine is built and never grows
9//! with input, and every decoder enforces an absolute output cap while it
10//! decodes, so a decompression bomb costs at most the cap. There is no
11//! `unsafe` code and no third-party runtime dependency.
12//!
13//! ## Tier 1
14//!
15//! Add it with Cargo's rename, `compress = { package = "compress-lib", version
16//! = "0.6" }`, and code reads `compress::`. One call each way; decoding is
17//! capped at 64 MiB of output by default:
18//!
19//! ```
20//! use compress_lib as compress;
21//!
22//! let packed = compress::gzip::compress(b"hello, hello, hello");
23//! let body = compress::gzip::decompress(&packed)?;
24//! assert_eq!(body, b"hello, hello, hello");
25//!
26//! let z = compress::zlib::compress(b"zlib, which HTTP calls deflate");
27//! let raw = compress::deflate::compress_with(b"raw DEFLATE", compress::Level::FAST);
28//! assert_eq!(compress::decompress(&z)?, b"zlib, which HTTP calls deflate"); // gzip or zlib, detected
29//!
30//! let small = compress::Limits::new().with_max_output(1 << 20);
31//! assert_eq!(compress::deflate::decompress_with(&raw, small)?, b"raw DEFLATE");
32//! # Ok::<(), compress_lib::Error>(())
33//! ```
34//!
35//! ## WebSocket permessage-deflate
36//!
37//! [`message`] has the RFC 7692 rules: the sync-flush tail removed and put
38//! back, context takeover on or off, window bits 8 to 15, and the output
39//! budget checked while inflating.
40//!
41//! ```
42//! use compress_lib::message::{Compressor, Decompressor, MessageConfig};
43//!
44//! let config = MessageConfig::new().with_window_bits(10);
45//! let mut tx = Compressor::new(config)?;
46//! let mut rx = Decompressor::new(config)?;
47//! let mut wire = Vec::new();
48//! tx.compress_message(b"{\"op\":\"ping\"}", &mut wire)?;
49//! let mut text = Vec::new();
50//! rx.decompress_message(&wire, &mut text, 1 << 20)?;
51//! assert_eq!(text, b"{\"op\":\"ping\"}");
52//! # Ok::<(), compress_lib::Error>(())
53//! ```
54//!
55//! ## Streaming
56//!
57//! [`deflate::Inflater`] and [`deflate::Deflater`] take buffers you own and
58//! say where they stopped ([`Status`]); memory is fixed when they are built,
59//! and every decoder limit is checked before output is written.
60//!
61//! ```
62//! use compress_lib::deflate::{Deflater, Flush, Inflater, Status};
63//!
64//! let mut deflater = Deflater::default();
65//! let mut packed = [0u8; 256];
66//! let p = deflater.compress(b"streamed", &mut packed, Flush::Finish)?;
67//! assert_eq!(p.status, Status::Done);
68//!
69//! let mut inflater = Inflater::new().with_window_bits(12)?;
70//! let mut out = [0u8; 64];
71//! let q = inflater.finish(&packed[..p.produced], &mut out)?;
72//! assert_eq!(&out[..q.produced], b"streamed");
73//! # Ok::<(), compress_lib::Error>(())
74//! ```
75//!
76//! [`Decoder`] and [`Encoder`] take a [`Format`] chosen at run time (an HTTP
77//! layer picking by `Content-Encoding`); with the `std` feature, [`io`] wraps
78//! any of them around `Read` and `Write`.
79//!
80//! Every failure is an [`Error`] with a matchable [`ErrorKind`] and a stable
81//! code for logs and metrics. [`checksum`] has CRC-32 and Adler-32.
82//!
83//! ## Status
84//!
85//! `0.6.0`, optimisation: measured against miniz_oxide, zlib-rs, and flate2
86//! (`docs/PERFORMANCE.md` in the
87//! [repository](https://github.com/jamesgober/compress-lib)). Hardening and
88//! the 1.0 audit follow; the complete planned API, each item with its
89//! version, is `docs/API.md` there.
90
91#![doc(html_root_url = "https://docs.rs/compress-lib")]
92#![cfg_attr(docsrs, feature(doc_cfg))]
93#![cfg_attr(not(feature = "std"), no_std)]
94#![forbid(unsafe_code)]
95#![deny(missing_docs)]
96#![cfg_attr(
97 not(test),
98 deny(
99 clippy::unwrap_used,
100 clippy::expect_used,
101 clippy::panic,
102 clippy::todo,
103 clippy::unimplemented,
104 clippy::unreachable,
105 clippy::dbg_macro
106 )
107)]
108
109extern crate alloc;
110
111/// Crate version string, populated by Cargo at build time.
112pub const VERSION: &str = env!("CARGO_PKG_VERSION");
113
114pub mod checksum;
115pub mod deflate;
116pub mod gzip;
117#[cfg(feature = "std")]
118#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
119pub mod io;
120pub mod message;
121pub mod zlib;
122
123mod error;
124mod format;
125mod inflate;
126mod level;
127mod limits;
128mod stream;
129
130pub use error::{Error, ErrorKind};
131pub use format::{Decoder, Encoder, Format, decompress, decompress_with};
132pub use level::Level;
133pub use limits::Limits;
134pub use stream::{Progress, Status};
135
136#[cfg(test)]
137mod tests {
138 use super::VERSION;
139
140 #[test]
141 fn test_version_matches_manifest() {
142 assert_eq!(VERSION, env!("CARGO_PKG_VERSION"));
143 let core = VERSION.split(['-', '+']).next().unwrap_or_default();
144 assert_eq!(core.split('.').count(), 3);
145 assert!(core.split('.').all(|part| part.parse::<u64>().is_ok()));
146 }
147}