Skip to main content

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 {}