ppmd_rust/lib.rs
1//! PPMd compression / decompression. It's a port of the PPMd C-code from 7-Zip to Rust.
2//!
3//! The following variants are provided:
4//!
5//! - The PPMd7 (PPMdH) as used by the 7z archive format
6//! - The PPMd7 (PPMdH) with the carryless range coder (PPMd7a) as used by the `.pmd` files of the
7//! original PPMd program
8//! - The PPMd8 (PPMdI rev.1) as used by the zip archive format
9//!
10//! ## Notice
11//!
12//! There are two ways to properly bound the uncompressed data:
13//! 1. Save the uncompressed size along the compressed data and use `std::io::Read::read_exact()`
14//! to read the data (this is what is used in the 7z archive format).
15//! 2. Encode an end marker by calling `finish(true)` on the encoder when finishing the encoder
16//! process. You can then use `std::io::Read::read_to_end()` to read the data. You are of course
17//! free to also use the `std::io::Read::read_exact()` if you have stored the uncompressed data
18//! size (this is what is used in the zip archive format).
19//!
20//! Failing to do so will result in garbage symbols at the end of the actual data.
21//!
22//! ## Acknowledgement
23//!
24//! This port is based on the 7zip version of PPMd by Igor Pavlov, which in turn was based on the
25//! PPMd var.H (2001) / PPMd var.I (2002) code by Dmitry Shkarin. The carryless range coder of
26//! PPMd7a and PPMd8 was originally written by Dmitry Subbotin (1999). These authors put the
27//! original code into the public domain.
28//!
29//! ## License
30//!
31//! Licensed under [CC-0](https://spdx.org/licenses/CC0-1.0) or
32//! [MIT-0](https://spdx.org/licenses/MIT-0), at your option.
33
34#![forbid(unsafe_code)]
35
36mod internal;
37
38mod decoder_7;
39mod decoder_7a;
40mod decoder_8;
41mod encoder_7;
42mod encoder_7a;
43mod encoder_8;
44
45pub use decoder_7::Ppmd7Decoder;
46pub use decoder_7a::Ppmd7aDecoder;
47pub use decoder_8::Ppmd8Decoder;
48pub use encoder_7::Ppmd7Encoder;
49pub use encoder_7a::Ppmd7aEncoder;
50pub use encoder_8::Ppmd8Encoder;
51
52/// The minimal order PPMd7 supports.
53pub const PPMD7_MIN_ORDER: u32 = 2;
54
55/// The maximal order PPMd7 supports.
56pub const PPMD7_MAX_ORDER: u32 = 64;
57
58/// The minimal memory that PPMd7 supports.
59pub const PPMD7_MIN_MEM_SIZE: u32 = 2048;
60
61/// The maximal memory that PPMd7 supports (~4 GiB).
62pub const PPMD7_MAX_MEM_SIZE: u32 = u32::MAX - 36;
63
64/// The minimal order PPMd8 supports.
65pub const PPMD8_MIN_ORDER: u32 = 2;
66
67/// The maximal order PPMd8 supports.
68pub const PPMD8_MAX_ORDER: u32 = 16;
69
70/// The minimal memory that PPMd8 supports.
71///
72/// 7-Zip defines no minimum memory size for PPMd8. Its ZIP and `.pmd` codecs specify
73/// the memory size in MiB and always use at least 1 MiB. The model restoration never
74/// terminates for a memory size below 2064 bytes.
75pub const PPMD8_MIN_MEM_SIZE: u32 = 2064;
76
77/// The maximal memory that PPMd8 supports (4 GiB).
78pub const PPMD8_MAX_MEM_SIZE: u32 = u32::MAX;
79
80const SYM_END: i32 = -1;
81const SYM_ERROR: i32 = -2;
82
83/// Error type of the crate.
84pub type Result<T> = core::result::Result<T, Error>;
85
86/// The restore method used in PPMd8.
87#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
88pub enum RestoreMethod {
89 Restart = 0 as _,
90 CutOff = 1 as _,
91 Unsupported = 2 as _,
92}
93
94macro_rules! impl_from_int_for_restore_method {
95 ($($int_type:ty),+ $(,)?) => {
96 $(
97 impl From<$int_type> for RestoreMethod {
98 fn from(value: $int_type) -> Self {
99 match value {
100 0 => RestoreMethod::Restart,
101 1 => RestoreMethod::CutOff,
102 _ => RestoreMethod::Unsupported,
103 }
104 }
105 }
106 )+
107 };
108}
109
110impl_from_int_for_restore_method!(u8, u16, u32, u64, u128, usize);
111impl_from_int_for_restore_method!(i8, i16, i32, i64, i128, isize);
112
113/// Crate error type.
114pub enum Error {
115 /// The range decoder could not be properly initialized.
116 RangeDecoderInitialization,
117 /// Invalid parameters provided.
118 InvalidParameter,
119 /// General IO error.
120 IoError(std::io::Error),
121 /// The needed memory could not be allocated.
122 MemoryAllocation,
123}
124
125impl std::fmt::Debug for Error {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 match self {
128 Error::RangeDecoderInitialization => {
129 write!(f, "Could not initialize the range decoder")
130 }
131 Error::InvalidParameter => write!(f, "Wrong PPMd parameter"),
132 Error::IoError(err) => write!(f, "Io error: {err}"),
133 Error::MemoryAllocation => write!(f, "Memory allocation error (out of memory?)"),
134 }
135 }
136}
137
138impl std::fmt::Display for Error {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 std::fmt::Debug::fmt(self, f)
141 }
142}
143
144impl std::error::Error for Error {}