async_compression/codec/gzip/
encoder.rs
1use crate::{codec::Encode, util::PartialBuffer};
2use std::io;
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 ) -> io::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 => {
75 return Err(io::Error::new(
76 io::ErrorKind::Other,
77 "encode after complete",
78 ));
79 }
80 };
81
82 if input.unwritten().is_empty() || output.unwritten().is_empty() {
83 return Ok(());
84 }
85 }
86 }
87
88 fn flush(
89 &mut self,
90 output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
91 ) -> io::Result<bool> {
92 loop {
93 let done = match &mut self.state {
94 State::Header(header) => {
95 output.copy_unwritten_from(&mut *header);
96
97 if header.unwritten().is_empty() {
98 self.state = State::Encoding;
99 }
100 false
101 }
102
103 State::Encoding => self.inner.flush(output)?,
104
105 State::Footer(footer) => {
106 output.copy_unwritten_from(&mut *footer);
107
108 if footer.unwritten().is_empty() {
109 self.state = State::Done;
110 true
111 } else {
112 false
113 }
114 }
115
116 State::Done => true,
117 };
118
119 if done {
120 return Ok(true);
121 }
122
123 if output.unwritten().is_empty() {
124 return Ok(false);
125 }
126 }
127 }
128
129 fn finish(
130 &mut self,
131 output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
132 ) -> io::Result<bool> {
133 loop {
134 match &mut self.state {
135 State::Header(header) => {
136 output.copy_unwritten_from(&mut *header);
137
138 if header.unwritten().is_empty() {
139 self.state = State::Encoding;
140 }
141 }
142
143 State::Encoding => {
144 if self.inner.finish(output)? {
145 self.state = State::Footer(self.footer().into());
146 }
147 }
148
149 State::Footer(footer) => {
150 output.copy_unwritten_from(&mut *footer);
151
152 if footer.unwritten().is_empty() {
153 self.state = State::Done;
154 }
155 }
156
157 State::Done => {}
158 };
159
160 if let State::Done = self.state {
161 return Ok(true);
162 }
163
164 if output.unwritten().is_empty() {
165 return Ok(false);
166 }
167 }
168 }
169}