Skip to main content

ppmd_rust/
encoder_8.rs

1use std::io::Write;
2
3use crate::{
4    internal::ppmd8::{PPMd8, RangeEncoder},
5    Error, RestoreMethod, PPMD8_MAX_MEM_SIZE, PPMD8_MAX_ORDER, PPMD8_MIN_MEM_SIZE, PPMD8_MIN_ORDER,
6    SYM_END,
7};
8
9/// A encoder to compress data using PPMd8 (PPMdI rev.1).
10pub struct Ppmd8Encoder<W: Write> {
11    ppmd: PPMd8<RangeEncoder<W>>,
12}
13
14impl<W: Write> Ppmd8Encoder<W> {
15    /// Creates a new [`Ppmd8Encoder`] which provides a writer over the compressed data.
16    ///
17    /// The given `order` must be between [`PPMD8_MIN_ORDER`] and [`PPMD8_MAX_ORDER`] (inclusive).
18    /// The given `mem_size` must be between [`PPMD8_MIN_MEM_SIZE`] and [`PPMD8_MAX_MEM_SIZE`] (inclusive).
19    pub fn new(
20        writer: W,
21        order: u32,
22        mem_size: u32,
23        restore_method: RestoreMethod,
24    ) -> crate::Result<Self> {
25        if !(PPMD8_MIN_ORDER..=PPMD8_MAX_ORDER).contains(&order)
26            || !(PPMD8_MIN_MEM_SIZE..=PPMD8_MAX_MEM_SIZE).contains(&mem_size)
27        {
28            return Err(Error::InvalidParameter);
29        }
30
31        let ppmd = PPMd8::new_encoder(writer, order, mem_size, restore_method)?;
32
33        Ok(Self { ppmd })
34    }
35
36    /// Gets a reference to the underlying writer.
37    pub fn get_ref(&self) -> &W {
38        self.ppmd.get_ref()
39    }
40
41    /// Gets a mutable reference to the underlying writer.
42    ///
43    /// Note that mutating the output/input state of the stream may corrupt
44    /// this object, so care must be taken when using this method.
45    pub fn get_mut(&mut self) -> &mut W {
46        self.ppmd.get_mut()
47    }
48
49    /// Returns the inner writer.
50    pub fn into_inner(self) -> W {
51        self.ppmd.into_inner()
52    }
53
54    /// Finishes the encoding process.
55    ///
56    /// Adds an end marker to the data if `with_end_marker` is set to `true`.
57    pub fn finish(mut self, with_end_marker: bool) -> std::io::Result<W> {
58        if with_end_marker {
59            self.ppmd.encode_symbol(SYM_END)?;
60        }
61        self.ppmd.flush_range_encoder()?;
62        Ok(self.into_inner())
63    }
64}
65
66impl<W: Write> Write for Ppmd8Encoder<W> {
67    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
68        if buf.is_empty() {
69            return Ok(0);
70        }
71
72        for &byte in buf.iter() {
73            self.ppmd.encode_symbol(byte as i32)?;
74        }
75
76        Ok(buf.len())
77    }
78
79    /// Flushes the inner writer. The range coder writes its final bytes in
80    /// [`finish`](Self::finish), so the stream continues after a flush.
81    fn flush(&mut self) -> std::io::Result<()> {
82        self.ppmd.get_mut().flush()
83    }
84}
85
86#[cfg(test)]
87mod test {
88    use std::io::{Read, Write};
89
90    use super::{super::decoder_8::Ppmd8Decoder, Ppmd8Encoder, RestoreMethod};
91
92    const ORDER: u32 = 8;
93    const MEM_SIZE: u32 = 262144;
94    const RESTORE_METHOD: RestoreMethod = RestoreMethod::Restart;
95
96    #[test]
97    fn ppmd8encoder_without_end_marker() {
98        let test_data = include_str!("../tests/fixtures/text/apache2.txt");
99
100        let mut writer = Vec::new();
101        {
102            let mut encoder =
103                Ppmd8Encoder::new(&mut writer, ORDER, MEM_SIZE, RESTORE_METHOD).unwrap();
104            encoder.write_all(test_data.as_bytes()).unwrap();
105            encoder.finish(false).unwrap();
106        }
107
108        let mut decoder =
109            Ppmd8Decoder::new(writer.as_slice(), ORDER, MEM_SIZE, RESTORE_METHOD).unwrap();
110
111        let mut decoded = vec![0; test_data.len()];
112        decoder.read_exact(&mut decoded).unwrap();
113
114        let decoded_data = String::from_utf8(decoded).unwrap();
115        assert_eq!(decoded_data, test_data);
116    }
117
118    #[test]
119    fn ppmd8encoder_with_end_marker() {
120        let test_data = include_str!("../tests/fixtures/text/apache2.txt");
121
122        let mut writer = Vec::new();
123        {
124            let mut encoder =
125                Ppmd8Encoder::new(&mut writer, ORDER, MEM_SIZE, RESTORE_METHOD).unwrap();
126            encoder.write_all(test_data.as_bytes()).unwrap();
127            encoder.finish(true).unwrap();
128        }
129
130        let mut decoder =
131            Ppmd8Decoder::new(writer.as_slice(), ORDER, MEM_SIZE, RESTORE_METHOD).unwrap();
132
133        let mut decoded = Vec::new();
134        decoder.read_to_end(&mut decoded).unwrap();
135
136        let decoded_data = String::from_utf8(decoded).unwrap();
137        assert_eq!(decoded_data, test_data);
138    }
139
140    #[test]
141    fn ppmd8encoder_flush_between_writes() {
142        let test_data = include_str!("../tests/fixtures/text/apache2.txt");
143        let (first, second) = test_data.as_bytes().split_at(test_data.len() / 2);
144
145        let encode = |flush: bool| {
146            let mut data = Vec::new();
147            {
148                let mut encoder =
149                    Ppmd8Encoder::new(&mut data, ORDER, MEM_SIZE, RESTORE_METHOD).unwrap();
150                encoder.write_all(first).unwrap();
151                if flush {
152                    encoder.flush().unwrap();
153                }
154                encoder.write_all(second).unwrap();
155                encoder.finish(true).unwrap();
156            }
157            data
158        };
159
160        let data = encode(true);
161        assert_eq!(data, encode(false));
162
163        let mut decoder =
164            Ppmd8Decoder::new(data.as_slice(), ORDER, MEM_SIZE, RESTORE_METHOD).unwrap();
165
166        let mut decoded = Vec::new();
167        decoder.read_to_end(&mut decoded).unwrap();
168
169        let decoded_data = String::from_utf8(decoded).unwrap();
170        assert_eq!(decoded_data, test_data);
171    }
172}