1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#![cfg_attr(not(any(test, feature = "std")), no_std)]
#![cfg_attr(feature = "safe", forbid(unsafe_code))]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![warn(missing_docs)]
#![warn(clippy::pedantic)]
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::cast_lossless)]
#![allow(clippy::cast_sign_loss)]
#![allow(clippy::inline_always)]
#![allow(clippy::match_same_arms)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::missing_panics_doc)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::manual_assert)]

//! # Lempel–Ziv–Storer–Szymanski de-/compression
//!
//! `lzss` is a lossless data compression algorithm in pure Rust.
//! This crate is built for embedded systems:
//!
//! * Small code size
//! * Uses little RAM and CPU
//! * `no_std` feature
//! * All parameters can be compile-time only
//!
//! # Generic vs. dynamic
//!
//! This crate comes in two flavors: generic ([`Lzss`](crate::Lzss)) and dynamic ([`LzssDyn`](crate::LzssDyn)).
//!
//! The dynamic one has one compress function and all parameters are passed to
//! it at runtime, making it very adaptive.
//!
//! The generic one has compile-time parameters will produce a function for each
//! different sets of parameters. This function will be more optimized by the
//! compiler than the dynamic one, the downside is that multiple functions are
//! generated when multiple parameter sets are used.
//!
//! (The same applies for decompress and other functions, only used function will
//! be in the generated program.)
//!
//! # Lack of a header
//!
//! This algorithm has by design no header at all. Please be aware that it is not
//! possible to check if the contents is correct, or even the length matches.
//! It is recommended to add a header based on the requirements.
//!
//! # Origin
//! This code is based on the [LZSS encoder-decoder by Haruhiko Okumura, public domain](https://oku.edu.mie-u.ac.jp/~okumura/compression/lzss.c).
//!
//! In order to create an encoder-decoder which is compatible to the program above
//! the following is required: `C = 0x20` in this library and `P = (1+EI+EJ) / 9` in Okumuras program.
//!
//! # Features
//! * `alloc`       - Allows de-/compression with buffer on the heap and the [`VecWriter`](crate::VecWriter).
//! * `safe`        - Only use safe code (see Safety below).
//! * `std`         - Enables `alloc` and additional [`IOSimpleReader`](crate::IOSimpleReader), [`IOSimpleWriter`](crate::IOSimpleWriter),
//!                   and the [`Error`](::std::error::Error) instance for [`LzssError`](crate::LzssError) and [`LzssDynError`](crate::LzssDynError).
//!
//! `std` and `safe` are enabled by default.
//!
//! ## Usage
//! With defaults (`std` and `safe`):
//! ```toml
//! [dependencies]
//! lzss = "0.9"
//! ```
//!
//! With `no_std` (and without `safe`):
//! ```toml
//! [dependencies]
//! lzss = { version = "0.9", default-features = false }
//! ```
//!
//! # Example
//! ```rust
//! # use lzss::{Lzss, SliceReader, SliceWriter};
//! type MyLzss = Lzss<10, 4, 0x20, { 1 << 10 }, { 2 << 10 }>;
//! let input = b"Example Data";
//! let mut output = [0; 30];
//! let result = MyLzss::compress(
//!   SliceReader::new(input),
//!   SliceWriter::new(&mut output),
//! );
//! assert_eq!(result, Ok(14)); // there was no overflow and the output is 14 bytes long
//! ```
//!
//! # Safety
//!
//! With the `safe` feature the code is not using any unsafe code (`forbid(unsafe_code)`), but at
//! the cost of performance and size - though on modern systems that is not to mention.
//!
//! But on smaller systems (like microcontrollers, where `no_std` is needed) it may be noticeable.
//! Which is the reason wht it can be switched on/off.

#[cfg(feature = "alloc")]
#[macro_use]
extern crate alloc;

pub use crate::dynamic::{LzssDyn, LzssDynError};
pub use crate::error::LzssError;
pub use crate::generic::Lzss;
#[cfg(any(test, feature = "std"))]
pub use crate::io_simple::{IOSimpleReader, IOSimpleWriter};
pub use crate::read_write::{Read, Write};
pub use crate::slice::{SliceReader, SliceWriteError, SliceWriter, SliceWriterExact};
#[cfg(any(test, feature = "alloc"))]
pub use crate::vec::VecWriter;
pub use crate::void::{
    ResultLzssErrorVoidExt, ResultLzssErrorVoidReadExt, ResultLzssErrorVoidWriteExt,
};

mod bits;
mod dynamic;
mod error;
mod generic;
#[cfg(any(test, feature = "std"))]
mod io_simple;
mod macros;
mod read_write;
#[cfg_attr(feature = "safe", path = "slice_safe.rs")]
mod slice;
#[cfg(any(test, feature = "alloc"))]
mod vec;
mod void;