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.4" }`, 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::deflate::compress(b"hello, hello, hello");
23//! let data = compress::deflate::decompress(&packed)?;
24//! assert_eq!(data, b"hello, hello, hello");
25//!
26//! let small = compress::Limits::new().with_max_output(1 << 20);
27//! let fast = compress::deflate::compress_with(b"data", compress::Level::FAST);
28//! assert_eq!(compress::deflate::decompress_with(&fast, small)?, b"data");
29//! # Ok::<(), compress_lib::Error>(())
30//! ```
31//!
32//! ## WebSocket permessage-deflate
33//!
34//! [`message`] has the RFC 7692 rules: the sync-flush tail removed and put
35//! back, context takeover on or off, window bits 8 to 15, and the output
36//! budget checked while inflating.
37//!
38//! ```
39//! use compress_lib::message::{Compressor, Decompressor, MessageConfig};
40//!
41//! let config = MessageConfig::new().with_window_bits(10);
42//! let mut tx = Compressor::new(config)?;
43//! let mut rx = Decompressor::new(config)?;
44//! let mut wire = Vec::new();
45//! tx.compress_message(b"{\"op\":\"ping\"}", &mut wire)?;
46//! let mut text = Vec::new();
47//! rx.decompress_message(&wire, &mut text, 1 << 20)?;
48//! assert_eq!(text, b"{\"op\":\"ping\"}");
49//! # Ok::<(), compress_lib::Error>(())
50//! ```
51//!
52//! ## Streaming
53//!
54//! [`deflate::Inflater`] and [`deflate::Deflater`] take buffers you own and
55//! say where they stopped ([`Status`]); memory is fixed when they are built,
56//! and every decoder limit is checked before output is written.
57//!
58//! ```
59//! use compress_lib::deflate::{Deflater, Flush, Inflater, Status};
60//!
61//! let mut deflater = Deflater::default();
62//! let mut packed = [0u8; 256];
63//! let p = deflater.compress(b"streamed", &mut packed, Flush::Finish)?;
64//! assert_eq!(p.status, Status::Done);
65//!
66//! let mut inflater = Inflater::new().with_window_bits(12)?;
67//! let mut out = [0u8; 64];
68//! let q = inflater.finish(&packed[..p.produced], &mut out)?;
69//! assert_eq!(&out[..q.produced], b"streamed");
70//! # Ok::<(), compress_lib::Error>(())
71//! ```
72//!
73//! Every failure is an [`Error`] with a matchable [`ErrorKind`] and a stable
74//! code for logs and metrics. [`checksum`] has CRC-32 and Adler-32.
75//!
76//! ## Status
77//!
78//! `0.4.0`: raw DEFLATE both ways and the RFC 7692 helpers. zlib and gzip
79//! framing arrive in `0.5`; the complete planned API, each item with its
80//! version, is `docs/API.md` in the
81//! [repository](https://github.com/jamesgober/compress-lib).
82
83#![doc(html_root_url = "https://docs.rs/compress-lib")]
84#![cfg_attr(docsrs, feature(doc_cfg))]
85#![cfg_attr(not(feature = "std"), no_std)]
86#![forbid(unsafe_code)]
87#![deny(missing_docs)]
88#![cfg_attr(
89 not(test),
90 deny(
91 clippy::unwrap_used,
92 clippy::expect_used,
93 clippy::panic,
94 clippy::todo,
95 clippy::unimplemented,
96 clippy::unreachable,
97 clippy::dbg_macro
98 )
99)]
100
101extern crate alloc;
102
103/// Crate version string, populated by Cargo at build time.
104pub const VERSION: &str = env!("CARGO_PKG_VERSION");
105
106pub mod checksum;
107pub mod deflate;
108pub mod message;
109
110mod error;
111mod inflate;
112mod level;
113mod limits;
114mod stream;
115
116pub use error::{Error, ErrorKind};
117pub use level::Level;
118pub use limits::Limits;
119pub use stream::{Progress, Status};
120
121#[cfg(test)]
122mod tests {
123 use super::VERSION;
124
125 #[test]
126 fn test_version_matches_manifest() {
127 assert_eq!(VERSION, env!("CARGO_PKG_VERSION"));
128 let core = VERSION.split(['-', '+']).next().unwrap_or_default();
129 assert_eq!(core.split('.').count(), 3);
130 assert!(core.split('.').all(|part| part.parse::<u64>().is_ok()));
131 }
132}