ppmd_rust/
encoder_8.rs

1use crate::byte_writer::ByteWriter;
2use crate::memory::Memory;
3use crate::{Error, RestoreMethod};
4use ppmd_sys::{
5    CPpmd8, PPMD8_MAX_ORDER, PPMD8_MIN_ORDER, Ppmd8_Alloc, Ppmd8_Construct, Ppmd8_EncodeSymbol,
6    Ppmd8_Flush_RangeEnc, Ppmd8_Init,
7};
8use std::io::Write;
9
10/// A encoder to encode PPMd8 (PPMdI) compressed data.
11pub struct Ppmd8Encoder<W: Write> {
12    ppmd: CPpmd8,
13    writer: ByteWriter<W>,
14    _memory: Memory,
15}
16
17impl<W: Write> Ppmd8Encoder<W> {
18    /// Creates a new [`Ppmd8Encoder`].
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            return Err(Error::InvalidParameter);
27        }
28
29        let mut ppmd = unsafe { std::mem::zeroed::<CPpmd8>() };
30        unsafe { Ppmd8_Construct(&mut ppmd) };
31
32        let mut memory = Memory::new(mem_size);
33
34        let success = unsafe { Ppmd8_Alloc(&mut ppmd, mem_size, memory.allocation()) };
35
36        if success == 0 {
37            return Err(Error::InternalError("Failed to allocate memory"));
38        }
39
40        let mut writer = ByteWriter::new(writer);
41        ppmd.Stream.Out = writer.byte_out_ptr();
42
43        // #define Ppmd8_Init_RangeEnc(p) { (p)->Low = 0; (p)->Range = 0xFFFFFFFF; }
44        ppmd.Low = 0;
45        ppmd.Range = 0xFFFFFFFF;
46
47        unsafe { Ppmd8_Init(&mut ppmd, order, restore_method as _) };
48
49        Ok(Self {
50            ppmd,
51            writer,
52            _memory: memory,
53        })
54    }
55
56    fn inner_flush(&mut self) {
57        unsafe { Ppmd8_Flush_RangeEnc(&mut self.ppmd) };
58        self.writer.flush();
59    }
60}
61
62impl<W: Write> Write for Ppmd8Encoder<W> {
63    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
64        if buf.is_empty() {
65            return Ok(0);
66        }
67
68        buf.iter()
69            .for_each(|byte| unsafe { Ppmd8_EncodeSymbol(&mut self.ppmd as *mut _, *byte as _) });
70
71        Ok(buf.len())
72    }
73
74    fn flush(&mut self) -> std::io::Result<()> {
75        self.inner_flush();
76        Ok(())
77    }
78}
79
80#[cfg(test)]
81mod test {
82    use super::Ppmd8Encoder;
83    use crate::{Ppmd8Decoder, RestoreMethod};
84    use std::io::{Read, Write};
85
86    const ORDER: u32 = 8;
87    const MEM_SIZE: u32 = 262144;
88    const RESTORE_METHOD: RestoreMethod = RestoreMethod::Restart;
89
90    #[test]
91    fn ppmd8encoder_init_drop() {
92        let writer = Vec::new();
93        let encoder = Ppmd8Encoder::new(writer, ORDER, MEM_SIZE, RESTORE_METHOD).unwrap();
94        assert!(!encoder.ppmd.Base.is_null());
95    }
96
97    #[test]
98    fn ppmd8encoder_encode_decode() {
99        let test_data = "Lorem ipsum dolor sit amet. ";
100
101        let mut writer = Vec::new();
102        {
103            let mut encoder =
104                Ppmd8Encoder::new(&mut writer, ORDER, MEM_SIZE, RESTORE_METHOD).unwrap();
105            encoder.write_all(test_data.as_bytes()).unwrap();
106            encoder.flush().unwrap();
107        }
108
109        let mut decoder =
110            Ppmd8Decoder::new(writer.as_slice(), ORDER, MEM_SIZE, RESTORE_METHOD).unwrap();
111
112        let mut decoded = vec![0; test_data.len()];
113        decoder.read_exact(&mut decoded).unwrap();
114
115        assert_eq!(decoded.as_slice(), test_data.as_bytes());
116
117        let decoded_data = String::from_utf8(decoded).unwrap();
118
119        assert_eq!(decoded_data, test_data);
120    }
121}