async_compression_issue_150_workaround/codec/gzip/
encoder.rs1use 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}