Skip to main content

sevenz_turbo/
lib.rs

1//! This project is a 7z compressor/decompressor written in pure Rust.
2//!
3//! `sevenz-turbo` is a fork of [sevenz-rust2](https://github.com/hasenbanck/sevenz-rust2)
4//! (itself a fork of the unmaintained `sevenz-rust`). It differs from upstream
5//! in two ways, and the module paths and public API are otherwise upstream's:
6//!
7//! 1. LZMA and LZMA2 decode - and, with `compress`, encode - through
8//!    [`lzma-turbo`](https://github.com/scryer-media/lzma-turbo), a port of the
9//!    7-Zip reference coders, instead of upstream's pure-Rust coders.
10//! 2. It adds the container API a streaming consumer needs: memory limits
11//!    enforced before allocation, per-member CRCs, folder-to-pack-stream byte
12//!    ranges, a borrowing reader, typed corruption errors carrying a block
13//!    index and packed offset, and a per-block completion hook.
14//!
15//! PPMd also goes through a different engine,
16//! [`ppmd-turbo`](https://github.com/scryer-media/ppmd-turbo), and the default
17//! thread count, the cryptography backends and the limits on hostile archives
18//! differ; the README lists them.
19//!
20//! The `CHANGELOG.md` section "Fork" is the exhaustive divergence list.
21//!
22//! ## Supported Codecs & filters
23//!
24//! | Codec          | Decompression | Compression |
25//! |----------------|---------------|-------------|
26//! | COPY           | ✓             | ✓           |
27//! | LZMA           | ✓             | ✓           |
28//! | LZMA2          | ✓             | ✓           |
29//! | BROTLI (*)     | ✓             | ✓           |
30//! | BZIP2          | ✓             | ✓           |
31//! | DEFLATE (*)    | ✓             | ✓           |
32//! | PPMD           | ✓             | ✓           |
33//! | LZ4 (*)        | ✓             | ✓           |
34//! | ZSTD (*)       | ✓             | ✓           |
35//!
36//! (*) Require optional cargo feature.
37//!
38//! | Filter        | Decompression | Compression |
39//! |---------------|---------------|-------------|
40//! | BCJ X86       | ✓             | ✓           |
41//! | BCJ ARM       | ✓             | ✓           |
42//! | BCJ ARM64     | ✓             | ✓           |
43//! | BCJ ARM_THUMB | ✓             | ✓           |
44//! | BCJ RISC_V    | ✓             | ✓           |
45//! | BCJ PPC       | ✓             | ✓           |
46//! | BCJ SPARC     | ✓             | ✓           |
47//! | BCJ IA64      | ✓             | ✓           |
48//! | BCJ2          | ✓             | ✓           |
49//! | DELTA         | ✓             | ✓           |
50#![cfg_attr(docsrs, feature(doc_cfg))]
51#![warn(missing_docs)]
52
53#[cfg(target_arch = "wasm32")]
54extern crate wasm_bindgen;
55
56pub mod container;
57#[cfg(feature = "aes256")]
58mod crypto_backend;
59#[cfg(feature = "compress")]
60mod encoder;
61/// Encoding options when compressing.
62#[cfg(feature = "compress")]
63pub mod encoder_options;
64mod encryption;
65
66/// Names the cryptography backend this build of the crate selected for the 7z
67/// `aes256` coder: `"aws-lc"` or `"rustcrypto"`.
68///
69/// Which cryptography ends up in a binary is decided by Cargo features that a
70/// dependency can turn on without the top-level crate noticing, so this is
71/// here to be asserted on in a consumer's own tests. See the `aws-lc-crypto`
72/// and `native-crypto` features.
73#[cfg(feature = "aes256")]
74#[must_use]
75pub fn crypto_backend() -> &'static str {
76    crypto_backend::BACKEND
77}
78
79/// The SHA-256 of `data`, computed by the backend [`crypto_backend`] names.
80///
81/// For a consumer that wants one digest routine in its binary and wants it to
82/// be the one this crate's `aes256` coder uses, rather than linking a second
83/// cryptography library (or a second configuration of the same one) beside it.
84#[cfg(feature = "aes256")]
85#[must_use]
86pub fn sha256(data: &[u8]) -> [u8; 32] {
87    let mut sha = crypto_backend::Sha256::new();
88    sha.update(data);
89    sha.finalize()
90}
91
92/// Names the LZMA and LZMA2 encoder this crate writes archives with:
93/// `"lzma-turbo"`.
94#[cfg(feature = "compress")]
95#[must_use]
96pub fn lzma_encoder() -> &'static str {
97    "lzma-turbo"
98}
99
100/// Embedder-supplied delegation hooks for the bulk AES-256-CBC decrypt, the
101/// key derivation's SHA-256 and the 7z CRC-32 (the `crypto-host` and
102/// `crc-host` features). See the module documentation for the contract a host
103/// must satisfy.
104#[cfg(any(feature = "crc-host", feature = "crypto-host"))]
105pub mod hooks;
106
107mod error;
108#[cfg(not(target_arch = "wasm32"))]
109mod ordered;
110mod positional;
111mod reader;
112
113#[cfg(feature = "compress")]
114mod writer;
115
116pub(crate) mod archive;
117pub(crate) mod bitset;
118pub(crate) mod block;
119pub(crate) mod codec;
120pub(crate) mod decoder;
121pub(crate) mod pipeline;
122
123mod time;
124#[cfg(feature = "util")]
125mod util;
126
127use std::{
128    io::{Read, Write},
129    ops::{Deref, DerefMut},
130};
131
132pub use archive::*;
133pub use block::*;
134pub use codec::lzma_turbo::{
135    Lzma2Handle, Lzma2Ledger, Lzma2Progress, Lzma2RunCompletion, Lzma2RunCrc,
136    PositionalLzma2Folder, PositionalLzma2Run, PositionalLzma2Scanner,
137};
138pub use container::{
139    ArchiveLimits, BlockCompletion, CrcFolder, PackStreamRange, SubStream, SubStreamCompletion,
140    UnsizedCoder, coder_memory_estimate, crc32_combine,
141};
142pub use encryption::Password;
143#[cfg(feature = "aes256")]
144pub use encryption::{AesKey, AesProperties};
145pub use error::{BlockErrorKind, Error, Limit};
146pub use positional::{ReadAt, ReadAtCursor, SerialReadAt};
147pub use reader::{ArchiveReader, BlockDecoder, EntryRead, PositionalBlockDecoder};
148pub use time::NtTime;
149#[cfg(all(feature = "compress", feature = "util", not(target_arch = "wasm32")))]
150pub use util::compress::*;
151#[cfg(all(feature = "util", not(target_arch = "wasm32")))]
152pub use util::decompress::*;
153#[cfg(all(feature = "util", target_arch = "wasm32"))]
154pub use util::wasm::*;
155#[cfg(feature = "compress")]
156pub use writer::*;
157
158trait ByteReader {
159    fn read_u8(&mut self) -> std::io::Result<u8>;
160
161    #[cfg(feature = "brotli")]
162    fn read_u16(&mut self) -> std::io::Result<u16>;
163
164    fn read_u32(&mut self) -> std::io::Result<u32>;
165
166    fn read_u64(&mut self) -> std::io::Result<u64>;
167}
168
169trait ByteWriter {
170    #[cfg(feature = "compress")]
171    fn write_u8(&mut self, value: u8) -> std::io::Result<()>;
172
173    fn write_u16(&mut self, value: u16) -> std::io::Result<()>;
174
175    #[cfg(feature = "compress")]
176    fn write_u32(&mut self, value: u32) -> std::io::Result<()>;
177
178    #[cfg(feature = "compress")]
179    fn write_u64(&mut self, value: u64) -> std::io::Result<()>;
180}
181
182impl<T: Read> ByteReader for T {
183    #[inline(always)]
184    fn read_u8(&mut self) -> std::io::Result<u8> {
185        let mut buf = [0; 1];
186        self.read_exact(&mut buf)?;
187        Ok(buf[0])
188    }
189
190    #[cfg(feature = "brotli")]
191    #[inline(always)]
192    fn read_u16(&mut self) -> std::io::Result<u16> {
193        let mut buf = [0; 2];
194        self.read_exact(buf.as_mut())?;
195        Ok(u16::from_le_bytes(buf))
196    }
197
198    #[inline(always)]
199    fn read_u32(&mut self) -> std::io::Result<u32> {
200        let mut buf = [0; 4];
201        self.read_exact(buf.as_mut())?;
202        Ok(u32::from_le_bytes(buf))
203    }
204
205    #[inline(always)]
206    fn read_u64(&mut self) -> std::io::Result<u64> {
207        let mut buf = [0; 8];
208        self.read_exact(buf.as_mut())?;
209        Ok(u64::from_le_bytes(buf))
210    }
211}
212
213impl<T: Write> ByteWriter for T {
214    #[cfg(feature = "compress")]
215    #[inline(always)]
216    fn write_u8(&mut self, value: u8) -> std::io::Result<()> {
217        self.write_all(&[value])
218    }
219
220    #[inline(always)]
221    fn write_u16(&mut self, value: u16) -> std::io::Result<()> {
222        self.write_all(&value.to_le_bytes())
223    }
224
225    #[cfg(feature = "compress")]
226    #[inline(always)]
227    fn write_u32(&mut self, value: u32) -> std::io::Result<()> {
228        self.write_all(&value.to_le_bytes())
229    }
230
231    #[cfg(feature = "compress")]
232    #[inline(always)]
233    fn write_u64(&mut self, value: u64) -> std::io::Result<()> {
234        self.write_all(&value.to_le_bytes())
235    }
236}
237
238/// A trait for writers that finishes the stream on drop.
239trait AutoFinish {
240    /// Finish writing the stream without error handling.
241    fn finish_ignore_error(self);
242}
243
244/// A wrapper around a writer that finishes the stream on drop.
245#[allow(private_bounds)]
246pub struct AutoFinisher<T: AutoFinish>(Option<T>);
247
248impl<T: AutoFinish> Drop for AutoFinisher<T> {
249    fn drop(&mut self) {
250        if let Some(writer) = self.0.take() {
251            writer.finish_ignore_error();
252        }
253    }
254}
255
256impl<T: AutoFinish> Deref for AutoFinisher<T> {
257    type Target = T;
258
259    fn deref(&self) -> &Self::Target {
260        self.0.as_ref().unwrap()
261    }
262}
263
264impl<T: AutoFinish> DerefMut for AutoFinisher<T> {
265    fn deref_mut(&mut self) -> &mut Self::Target {
266        self.0.as_mut().unwrap()
267    }
268}