async_compression_issue_150_workaround/codec/gzip/
encoder.rs

1use crate::{codec::Encode, util::PartialBuffer};
2use std::io::Result;
3
4use flate2::{Compression, Crc};
5
6#[derive(Debug)]
7enum State {
8    Header(PartialBuffer<Vec<u8>>),
9    Encoding,
10    Footer(PartialBuffer<Vec<u8>>),
11    Done,
12}
13
14#[derive(Debug)]
15pub struct GzipEncoder {
16    inner: crate::codec::FlateEncoder,
17    crc: Crc,
18    state: State,
19}
20
21fn header(level: Compression) -> Vec<u8> {
22    let level_byte = if level.level() >= Compression::best().level() {
23        0x02
24    } else if level.level() <= Compression::fast().level() {
25        0x04
26    } else {
27        0x00
28    };
29
30    vec![0x1f, 0x8b, 0x08, 0, 0, 0, 0, 0, level_byte, 0xff]
31}
32
33impl GzipEncoder {
34    pub(crate) fn new(level: Compression) -> Self {
35        Self {
36            inner: crate::codec::FlateEncoder::new(level, false),
37            crc: Crc::new(),
38            state: State::Header(header(level).into()),
39        }
40    }
41
42    fn footer(&mut self) -> Vec<u8> {
43        let mut output = Vec::with_capacity(8);
44
45        output.extend(&self.crc.sum().to_le_bytes());
46        output.extend(&self.crc.amount().to_le_bytes());
47
48        output
49    }
50}
51
52impl Encode for GzipEncoder {
53    fn encode(
54        &mut self,
55        input: &mut PartialBuffer<impl AsRef<[u8]>>,
56        output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
57    ) -> Result<()> {
58        loop {
59            match &mut self.state {
60                State::Header(header) => {
61                    output.copy_unwritten_from(&mut *header);
62
63                    if header.unwritten().is_empty() {
64                        self.state = State::Encoding;
65                    }
66                }
67
68                State::Encoding => {
69                    let prior_written = input.written().len();
70                    self.inner.encode(input, output)?;
71                    self.crc.update(&input.written()[prior_written..]);
72                }
73
74                State::Footer(_) | State::Done => panic!("encode after complete"),
75            };
76
77            if input.unwritten().is_empty() || output.unwritten().is_empty() {
78                return Ok(());
79            }
80        }
81    }
82
83    fn flush(
84        &mut self,
85        output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
86    ) -> Result<bool> {
87        loop {
88            let done = match &mut self.state {
89                State::Header(header) => {
90                    output.copy_unwritten_from(&mut *header);
91
92                    if header.unwritten().is_empty() {
93                        self.state = State::Encoding;
94                    }
95                    false
96                }
97
98                State::Encoding => self.inner.flush(output)?,
99
100                State::Footer(footer) => {
101                    output.copy_unwritten_from(&mut *footer);
102
103                    if footer.unwritten().is_empty() {
104                        self.state = State::Done;
105                        true
106                    } else {
107                        false
108                    }
109                }
110
111                State::Done => true,
112            };
113
114            if done {
115                return Ok(true);
116            }
117
118            if output.unwritten().is_empty() {
119                return Ok(false);
120            }
121        }
122    }
123
124    fn finish(
125        &mut self,
126        output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
127    ) -> Result<bool> {
128        loop {
129            match &mut self.state {
130                State::Header(header) => {
131                    output.copy_unwritten_from(&mut *header);
132
133                    if header.unwritten().is_empty() {
134                        self.state = State::Encoding;
135                    }
136                }
137
138                State::Encoding => {
139                    if self.inner.finish(output)? {
140                        self.state = State::Footer(self.footer().into());
141                    }
142                }
143
144                State::Footer(footer) => {
145                    output.copy_unwritten_from(&mut *footer);
146
147                    if footer.unwritten().is_empty() {
148                        self.state = State::Done;
149                    }
150                }
151
152                State::Done => {}
153            };
154
155            if let State::Done = self.state {
156                return Ok(true);
157            }
158
159            if output.unwritten().is_empty() {
160                return Ok(false);
161            }
162        }
163    }
164}