compression/gzip/
encoder.rs

1//! rust-compression
2//!
3//! # Licensing
4//! This Source Code is subject to the terms of the Mozilla Public License
5//! version 2.0 (the "License"). You can obtain a copy of the License at
6//! <http://mozilla.org/MPL/2.0/>.
7
8use crate::action::Action;
9use crate::core::borrow::BorrowMut;
10use crate::core::hash::{BuildHasher, Hasher};
11use crate::core::marker::PhantomData;
12use crate::core::mem;
13use crate::crc32::{BuiltinDigest, IEEE_REVERSE};
14use crate::deflate::encoder::Inflater;
15use crate::error::CompressionError;
16use crate::traits::encoder::Encoder;
17
18struct ScanIterator<I: Iterator, BI: BorrowMut<I>, F: FnMut(&I::Item) -> ()> {
19    phantom: PhantomData<I>,
20    inner: BI,
21    closure: F,
22}
23
24impl<I: Iterator, BI: BorrowMut<I>, F: FnMut(&I::Item) -> ()> Iterator
25    for ScanIterator<I, BI, F>
26{
27    type Item = I::Item;
28    fn next(&mut self) -> Option<I::Item> {
29        let ret = self.inner.borrow_mut().next();
30        if let Some(ref s) = ret {
31            (self.closure)(s);
32        }
33        ret
34    }
35}
36
37impl<I: Iterator, BI: BorrowMut<I>, F: FnMut(&I::Item) -> ()>
38    ScanIterator<I, BI, F>
39{
40    pub(crate) fn new(inner: BI, closure: F) -> Self {
41        Self {
42            inner,
43            closure,
44            phantom: PhantomData,
45        }
46    }
47}
48
49#[derive(Debug)]
50pub struct GZipEncoder {
51    inflater: Inflater,
52    crc32: Option<BuiltinDigest>,
53    header_len: u8,
54    header: [u8; 10],
55    hash: Option<u32>,
56    hashlen: u8,
57    i_size_len: u8,
58    i_size: u32,
59}
60
61impl Default for GZipEncoder {
62    fn default() -> Self {
63        Self::new()
64    }
65}
66
67impl GZipEncoder {
68    pub fn new() -> Self {
69        Self {
70            inflater: Inflater::new(),
71            crc32: Some(IEEE_REVERSE.build_hasher()),
72            header: [
73                0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
74            ],
75            header_len: 10,
76            hash: None,
77            hashlen: 3,
78            i_size: 0,
79            i_size_len: 4,
80        }
81    }
82}
83
84impl Encoder for GZipEncoder {
85    type Error = CompressionError;
86    type In = u8;
87    type Out = u8;
88    fn next<I: Iterator<Item = u8>>(
89        &mut self,
90        iter: &mut I,
91        action: Action,
92    ) -> Option<Result<u8, CompressionError>> {
93        let hlen = self.header_len;
94        if hlen > 0 {
95            let hlen_all = self.header.len();
96            self.header_len = hlen - 1;
97            Some(Ok(self.header[hlen_all - hlen as usize]))
98        } else if let Some(hash) = self.hash {
99            if self.hashlen == 0 {
100                if self.i_size_len == 0 {
101                    None
102                } else {
103                    self.i_size_len -= 1;
104                    let ret = self.i_size as u8;
105                    self.i_size >>= 8;
106                    Some(Ok(ret))
107                }
108            } else {
109                self.hashlen -= 1;
110                self.hash = Some(hash >> 8);
111                Some(Ok(hash as u8))
112            }
113        } else {
114            let mut crc32 = mem::replace(&mut self.crc32, None);
115            let mut i_size = self.i_size;
116            let ret = self.inflater.next(
117                &mut ScanIterator::<I, _, _>::new(iter, |x: &u8| {
118                    crc32.as_mut().unwrap().write_u8(*x);
119                    i_size += 1;
120                }),
121                action,
122            );
123            self.i_size = i_size;
124            let _ = mem::replace(&mut self.crc32, crc32);
125            if ret.is_none() {
126                let hash = self.crc32.as_mut().unwrap().finish() as u32;
127                let ret = hash as u8;
128                self.hash = Some(hash >> 8);
129                Some(Ok(ret))
130            } else {
131                ret
132            }
133        }
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140    use crate::traits::encoder::EncodeExt;
141    #[cfg(not(feature = "std"))]
142    #[allow(unused_imports)]
143    use alloc::vec;
144    #[cfg(not(feature = "std"))]
145    use alloc::vec::Vec;
146
147    #[test]
148    fn test_unit() {
149        let mut encoder = GZipEncoder::new();
150        let ret = b"a"
151            .iter()
152            .cloned()
153            .encode(&mut encoder, Action::Finish)
154            .collect::<Result<Vec<_>, _>>();
155
156        assert_eq!(
157            ret,
158            Ok(vec![
159                0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
160                0x4b, 0x04, 0x00, 0x43, 0xbe, 0xb7, 0xe8, 0x01, 0x00, 0x00,
161                0x00,
162            ])
163        );
164    }
165}