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}