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
34mod internal;
35
36mod decoder_7;
37mod decoder_7a;
38mod decoder_8;
39mod encoder_7;
40mod encoder_7a;
41mod encoder_8;
42
43pub use decoder_7::Ppmd7Decoder;
44pub use decoder_7a::Ppmd7aDecoder;
45pub use decoder_8::Ppmd8Decoder;
46pub use encoder_7::Ppmd7Encoder;
47pub use encoder_7a::Ppmd7aEncoder;
48pub use encoder_8::Ppmd8Encoder;
49
50/// The minimal order PPMd7 supports.
51pub const PPMD7_MIN_ORDER: u32 = 2;
52
53/// The maximal order PPMd7 supports.
54pub const PPMD7_MAX_ORDER: u32 = 64;
55
56/// The minimal memory that PPMd7 supports.
57pub const PPMD7_MIN_MEM_SIZE: u32 = 2048;
58
59#[cfg(feature = "unstable-tagged-offsets")]
60/// The maximal memory that PPMd7 supports (512 MiB).
61pub const PPMD7_MAX_MEM_SIZE: u32 = 1 << 29;
62#[cfg(not(feature = "unstable-tagged-offsets"))]
63/// The maximal memory that PPMd7 supports (~4 GiB).
64pub const PPMD7_MAX_MEM_SIZE: u32 = u32::MAX - 36;
65
66/// The minimal order PPMd8 supports.
67pub const PPMD8_MIN_ORDER: u32 = 2;
68
69/// The maximal order PPMd8 supports.
70pub const PPMD8_MAX_ORDER: u32 = 16;
71
72/// The minimal memory that PPMd8 supports.
73pub const PPMD8_MIN_MEM_SIZE: u32 = 2048;
74
75#[cfg(feature = "unstable-tagged-offsets")]
76/// The maximal memory that PPMd8 supports (512 MiB).
77pub const PPMD8_MAX_MEM_SIZE: u32 = 1 << 29;
78#[cfg(not(feature = "unstable-tagged-offsets"))]
79/// The maximal memory that PPMd8 supports (4 GiB).
80pub const PPMD8_MAX_MEM_SIZE: u32 = u32::MAX;
81
82const SYM_END: i32 = -1;
83const SYM_ERROR: i32 = -2;
84
85/// Error type of the crate.
86pub type Result<T> = core::result::Result<T, Error>;
87
88/// The restore method used in PPMd8.
89#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
90pub enum RestoreMethod {
91    Restart = 0 as _,
92    CutOff = 1 as _,
93    Unsupported = 2 as _,
94}
95
96macro_rules! impl_from_int_for_restore_method {
97    ($($int_type:ty),+ $(,)?) => {
98        $(
99            impl From<$int_type> for RestoreMethod {
100                fn from(value: $int_type) -> Self {
101                    match value {
102                        0 => RestoreMethod::Restart,
103                        1 => RestoreMethod::CutOff,
104                        _ => RestoreMethod::Unsupported,
105                    }
106                }
107            }
108        )+
109    };
110}
111
112impl_from_int_for_restore_method!(u8, u16, u32, u64, u128, usize);
113impl_from_int_for_restore_method!(i8, i16, i32, i64, i128, isize);
114
115/// Crate error type.
116pub enum Error {
117    /// The range decoder could not be properly initialized.
118    RangeDecoderInitialization,
119    /// Invalid parameters provided.
120    InvalidParameter,
121    /// General IO error.
122    IoError(std::io::Error),
123    /// The needed memory could not be allocated.
124    MemoryAllocation,
125}
126
127impl std::fmt::Debug for Error {
128    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129        match self {
130            Error::RangeDecoderInitialization => {
131                write!(f, "Could not initialize the range decoder")
132            }
133            Error::InvalidParameter => write!(f, "Wrong PPMd parameter"),
134            Error::IoError(err) => write!(f, "Io error: {err}"),
135            Error::MemoryAllocation => write!(f, "Memory allocation error (out of memory?)"),
136        }
137    }
138}
139
140impl std::fmt::Display for Error {
141    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142        std::fmt::Debug::fmt(self, f)
143    }
144}
145
146impl std::error::Error for Error {}