lzf_rust/lib.rs
1// SPDX-License-Identifier: ISC
2//! Pure Rust LZF compression and decompression.
3//!
4//! # Overview
5//!
6//! This crate provides:
7//!
8//! - Raw LZF token encode/decode (`compress`/`decompress`).
9//! - `lzf` block framing support (`ZV\0`/`ZV\1`) via `encode_blocks`/`decode_blocks`.
10//! - Streaming adapters (`LzfReader`, `LzfWriter`) for framed streams.
11//! - `no_std`-compatible I/O traits (`LzfRead`, `LzfWrite`).
12//!
13//! Raw token compatibility matches `liblzf` (`lzf_compress`, `lzf_compress_best`,
14//! and `lzf_decompress` behavior for valid inputs).
15//!
16//! # Features
17//!
18//! - `std` (default): integrates with `std::io::{Read, Write}`.
19//! - `encoder` (default): enables compression APIs and `LzfWriter`.
20//!
21//! # no_std
22//!
23//! Disable default features to use in `no_std + alloc` environments:
24//!
25//! ```toml
26//! [dependencies]
27//! lzf-rust = { version = "0.1", default-features = false, features = ["encoder"] }
28//! ```
29//!
30//! In this mode, use crate-level `LzfRead`/`LzfWrite` traits.
31//!
32//! # Examples
33//!
34//! Raw token roundtrip:
35//!
36//! ```
37//! use lzf_rust::{compress, decompress, max_compressed_size};
38//!
39//! let input = b"hello hello hello hello";
40//! let mut compressed = vec![0u8; max_compressed_size(input.len())];
41//! let n = compress(input, &mut compressed).unwrap();
42//! compressed.truncate(n);
43//!
44//! let mut out = vec![0u8; input.len()];
45//! let m = decompress(&compressed, &mut out).unwrap();
46//! assert_eq!(m, input.len());
47//! assert_eq!(&out, input);
48//! ```
49//!
50//! Framed block roundtrip:
51//!
52//! ```
53//! use lzf_rust::{decode_blocks, encode_blocks};
54//!
55//! let input = b"framed lzf data";
56//! let framed = encode_blocks(input, 32 * 1024).unwrap();
57//! let decoded = decode_blocks(&framed).unwrap();
58//! assert_eq!(decoded, input);
59//! ```
60//!
61//! # Safety
62//!
63//! This crate forbids `unsafe` code.
64//!
65//! # License
66//!
67//! This repository uses file-level licensing:
68//!
69//! - `src/raw/encoder.rs`: `BSD-2-Clause` (derived from liblzf encoder logic).
70//! - Remaining from-scratch Rust sources: `ISC`.
71
72#![warn(missing_docs)]
73#![forbid(unsafe_code)]
74#![cfg_attr(docsrs, feature(doc_cfg))]
75#![cfg_attr(not(feature = "std"), no_std)]
76
77extern crate alloc;
78
79mod error;
80mod framed;
81mod io;
82mod raw;
83mod stream;
84
85/// Crate error and result types.
86pub use error::{Error, Result};
87/// Decodes `lzf` framed block streams (`ZV\0`/`ZV\1`).
88pub use framed::decode_blocks;
89#[cfg(feature = "encoder")]
90#[cfg_attr(docsrs, doc(cfg(feature = "encoder")))]
91/// Encodes bytes into `lzf` framed block streams (`ZV\0`/`ZV\1`).
92pub use framed::encode_blocks;
93#[cfg(feature = "encoder")]
94#[cfg_attr(docsrs, doc(cfg(feature = "encoder")))]
95/// Encodes bytes into framed block streams with an explicit compression mode.
96pub use framed::encode_blocks_with_mode;
97/// `no_std`-compatible read/write traits used by streaming APIs.
98pub use io::{Read, Write};
99/// Alias for `Read` to mirror naming used by related compression crates.
100pub use io::{Read as LzfRead, Write as LzfWrite};
101#[cfg(feature = "encoder")]
102#[cfg_attr(docsrs, doc(cfg(feature = "encoder")))]
103/// Raw LZF encoder APIs.
104pub use raw::{CompressionMode, compress, compress_best, compress_with_mode};
105/// Raw LZF decoder APIs.
106pub use raw::{decompress, decompress_into_vec};
107/// Framed LZF stream reader.
108pub use stream::LzfReader;
109#[cfg(feature = "encoder")]
110#[cfg_attr(docsrs, doc(cfg(feature = "encoder")))]
111/// Framed LZF stream writer.
112pub use stream::LzfWriter;
113
114/// Maximum literal run size in the LZF format.
115pub const MAX_LITERAL_LEN: usize = 1 << 5;
116
117/// Maximum backwards offset in the LZF format.
118pub const MAX_OFFSET: usize = 1 << 13;
119
120/// Maximum match length in the LZF format.
121pub const MAX_MATCH_LEN: usize = (1 << 8) + (1 << 3);
122
123/// Computes a guaranteed upper bound for compressed output size.
124#[inline]
125pub const fn max_compressed_size(input_len: usize) -> usize {
126 ((input_len * 33) >> 5) + 1
127}
128
129/// Internal trait used by [`AutoFinisher`] to finalize streams on drop.
130#[doc(hidden)]
131pub trait AutoFinish {
132 /// Finalizes the wrapped stream and ignores any returned error.
133 fn finish_ignore_error(self);
134}
135
136/// Wrapper that attempts to finish the wrapped writer on drop.
137///
138/// This is useful when you want best-effort stream finalization even when
139/// early returns or panics bypass an explicit `finish()` call.
140pub struct AutoFinisher<T: AutoFinish>(pub(crate) Option<T>);
141
142impl<T: AutoFinish> Drop for AutoFinisher<T> {
143 fn drop(&mut self) {
144 if let Some(inner) = self.0.take() {
145 inner.finish_ignore_error();
146 }
147 }
148}
149
150impl<T: AutoFinish> core::ops::Deref for AutoFinisher<T> {
151 type Target = T;
152
153 fn deref(&self) -> &Self::Target {
154 self.0.as_ref().expect("AutoFinisher: inner value missing")
155 }
156}
157
158impl<T: AutoFinish> core::ops::DerefMut for AutoFinisher<T> {
159 fn deref_mut(&mut self) -> &mut Self::Target {
160 self.0.as_mut().expect("AutoFinisher: inner value missing")
161 }
162}
163
164impl<T: AutoFinish + Write> Write for AutoFinisher<T> {
165 fn write(&mut self, buf: &[u8]) -> Result<usize> {
166 self.0.as_mut().expect("AutoFinisher: inner value missing").write(buf)
167 }
168
169 fn flush(&mut self) -> Result<()> {
170 self.0.as_mut().expect("AutoFinisher: inner value missing").flush()
171 }
172}