flate2/ffi/
rust.rs

1//! Implementation for `miniz_oxide` rust backend.
2
3use std::convert::TryInto;
4use std::fmt;
5
6use miniz_oxide::deflate::core::CompressorOxide;
7use miniz_oxide::inflate::stream::InflateState;
8pub use miniz_oxide::*;
9
10pub const MZ_NO_FLUSH: isize = MZFlush::None as isize;
11pub const MZ_PARTIAL_FLUSH: isize = MZFlush::Partial as isize;
12pub const MZ_SYNC_FLUSH: isize = MZFlush::Sync as isize;
13pub const MZ_FULL_FLUSH: isize = MZFlush::Full as isize;
14pub const MZ_FINISH: isize = MZFlush::Finish as isize;
15
16use super::*;
17use crate::mem;
18
19// miniz_oxide doesn't provide any error messages (yet?)
20#[derive(Clone, Default)]
21pub struct ErrorMessage;
22
23impl ErrorMessage {
24    pub fn get(&self) -> Option<&str> {
25        None
26    }
27}
28
29fn format_from_bool(zlib_header: bool) -> DataFormat {
30    if zlib_header {
31        DataFormat::Zlib
32    } else {
33        DataFormat::Raw
34    }
35}
36
37pub struct Inflate {
38    inner: Box<InflateState>,
39    total_in: u64,
40    total_out: u64,
41}
42
43impl fmt::Debug for Inflate {
44    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
45        write!(
46            f,
47            "miniz_oxide inflate internal state. total_in: {}, total_out: {}",
48            self.total_in, self.total_out,
49        )
50    }
51}
52
53impl From<FlushDecompress> for MZFlush {
54    fn from(value: FlushDecompress) -> Self {
55        match value {
56            FlushDecompress::None => Self::None,
57            FlushDecompress::Sync => Self::Sync,
58            FlushDecompress::Finish => Self::Finish,
59        }
60    }
61}
62
63impl InflateBackend for Inflate {
64    fn make(zlib_header: bool, _window_bits: u8) -> Self {
65        let format = format_from_bool(zlib_header);
66
67        Inflate {
68            inner: InflateState::new_boxed(format),
69            total_in: 0,
70            total_out: 0,
71        }
72    }
73
74    fn decompress(
75        &mut self,
76        input: &[u8],
77        output: &mut [u8],
78        flush: FlushDecompress,
79    ) -> Result<Status, DecompressError> {
80        let mz_flush = flush.into();
81        let res = inflate::stream::inflate(&mut self.inner, input, output, mz_flush);
82        self.total_in += res.bytes_consumed as u64;
83        self.total_out += res.bytes_written as u64;
84
85        match res.status {
86            Ok(status) => match status {
87                MZStatus::Ok => Ok(Status::Ok),
88                MZStatus::StreamEnd => Ok(Status::StreamEnd),
89                MZStatus::NeedDict => {
90                    mem::decompress_need_dict(self.inner.decompressor().adler32().unwrap_or(0))
91                }
92            },
93            Err(status) => match status {
94                MZError::Buf => Ok(Status::BufError),
95                _ => mem::decompress_failed(ErrorMessage),
96            },
97        }
98    }
99
100    fn reset(&mut self, zlib_header: bool) {
101        self.inner.reset(format_from_bool(zlib_header));
102        self.total_in = 0;
103        self.total_out = 0;
104    }
105}
106
107impl Backend for Inflate {
108    #[inline]
109    fn total_in(&self) -> u64 {
110        self.total_in
111    }
112
113    #[inline]
114    fn total_out(&self) -> u64 {
115        self.total_out
116    }
117}
118
119pub struct Deflate {
120    inner: Box<CompressorOxide>,
121    total_in: u64,
122    total_out: u64,
123}
124
125impl fmt::Debug for Deflate {
126    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
127        write!(
128            f,
129            "miniz_oxide deflate internal state. total_in: {}, total_out: {}",
130            self.total_in, self.total_out,
131        )
132    }
133}
134
135impl From<FlushCompress> for MZFlush {
136    fn from(value: FlushCompress) -> Self {
137        match value {
138            FlushCompress::None => Self::None,
139            FlushCompress::Partial => Self::Partial,
140            FlushCompress::Sync => Self::Sync,
141            FlushCompress::Full => Self::Full,
142            FlushCompress::Finish => Self::Finish,
143        }
144    }
145}
146
147impl DeflateBackend for Deflate {
148    fn make(level: Compression, zlib_header: bool, _window_bits: u8) -> Self {
149        // Check in case the integer value changes at some point.
150        debug_assert!(level.level() <= 10);
151
152        let mut inner: Box<CompressorOxide> = Box::default();
153        let format = format_from_bool(zlib_header);
154        inner.set_format_and_level(format, level.level().try_into().unwrap_or(1));
155
156        Deflate {
157            inner,
158            total_in: 0,
159            total_out: 0,
160        }
161    }
162
163    fn compress(
164        &mut self,
165        input: &[u8],
166        output: &mut [u8],
167        flush: FlushCompress,
168    ) -> Result<Status, CompressError> {
169        let mz_flush = flush.into();
170        let res = deflate::stream::deflate(&mut self.inner, input, output, mz_flush);
171        self.total_in += res.bytes_consumed as u64;
172        self.total_out += res.bytes_written as u64;
173
174        match res.status {
175            Ok(status) => match status {
176                MZStatus::Ok => Ok(Status::Ok),
177                MZStatus::StreamEnd => Ok(Status::StreamEnd),
178                MZStatus::NeedDict => mem::compress_failed(ErrorMessage),
179            },
180            Err(status) => match status {
181                MZError::Buf => Ok(Status::BufError),
182                _ => mem::compress_failed(ErrorMessage),
183            },
184        }
185    }
186
187    fn reset(&mut self) {
188        self.total_in = 0;
189        self.total_out = 0;
190        self.inner.reset();
191    }
192}
193
194impl Backend for Deflate {
195    #[inline]
196    fn total_in(&self) -> u64 {
197        self.total_in
198    }
199
200    #[inline]
201    fn total_out(&self) -> u64 {
202        self.total_out
203    }
204}