compression_codecs/gzip/
encoder.rs

1use crate::{flate::params::FlateEncoderParams, EncodeV2, FlateEncoder};
2use compression_core::util::{PartialBuffer, WriteBuffer};
3use flate2::{Compression, Crc};
4use std::io;
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: 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 fn new(level: FlateEncoderParams) -> Self {
35        Self {
36            inner: FlateEncoder::new(level.clone(), false),
37            crc: Crc::new(),
38            state: State::Header(header(Compression::from(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 EncodeV2 for GzipEncoder {
53    fn encode(
54        &mut self,
55        input: &mut PartialBuffer<&[u8]>,
56        output: &mut WriteBuffer<'_>,
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.has_no_spare_space() {
80                return Ok(());
81            }
82        }
83    }
84
85    fn flush(&mut self, output: &mut WriteBuffer<'_>) -> io::Result<bool> {
86        loop {
87            let done = match &mut self.state {
88                State::Header(header) => {
89                    output.copy_unwritten_from(&mut *header);
90
91                    if header.unwritten().is_empty() {
92                        self.state = State::Encoding;
93                    }
94                    false
95                }
96
97                State::Encoding => self.inner.flush(output)?,
98
99                State::Footer(footer) => {
100                    output.copy_unwritten_from(&mut *footer);
101
102                    if footer.unwritten().is_empty() {
103                        self.state = State::Done;
104                        true
105                    } else {
106                        false
107                    }
108                }
109
110                State::Done => true,
111            };
112
113            if done {
114                return Ok(true);
115            }
116
117            if output.has_no_spare_space() {
118                return Ok(false);
119            }
120        }
121    }
122
123    fn finish(&mut self, output: &mut WriteBuffer<'_>) -> io::Result<bool> {
124        loop {
125            match &mut self.state {
126                State::Header(header) => {
127                    output.copy_unwritten_from(&mut *header);
128
129                    if header.unwritten().is_empty() {
130                        self.state = State::Encoding;
131                    }
132                }
133
134                State::Encoding => {
135                    if self.inner.finish(output)? {
136                        self.state = State::Footer(self.footer().into());
137                    }
138                }
139
140                State::Footer(footer) => {
141                    output.copy_unwritten_from(&mut *footer);
142
143                    if footer.unwritten().is_empty() {
144                        self.state = State::Done;
145                    }
146                }
147
148                State::Done => {}
149            };
150
151            if let State::Done = self.state {
152                return Ok(true);
153            }
154
155            if output.has_no_spare_space() {
156                return Ok(false);
157            }
158        }
159    }
160}