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}