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