compression_codecs/brotli/
decoder.rs

1use crate::Decode;
2use brotli::{enc::StandardAlloc, BrotliDecompressStream, BrotliResult, BrotliState};
3use compression_core::util::PartialBuffer;
4use std::{fmt, io};
5
6pub struct BrotliDecoder {
7    // `BrotliState` is very large (over 2kb) which is why we're boxing it.
8    state: Box<BrotliState<StandardAlloc, StandardAlloc, StandardAlloc>>,
9}
10
11impl Default for BrotliDecoder {
12    fn default() -> Self {
13        Self {
14            state: Box::new(BrotliState::new(
15                StandardAlloc::default(),
16                StandardAlloc::default(),
17                StandardAlloc::default(),
18            )),
19        }
20    }
21}
22
23impl BrotliDecoder {
24    pub fn new() -> Self {
25        Self::default()
26    }
27
28    fn decode(
29        &mut self,
30        input: &mut PartialBuffer<impl AsRef<[u8]>>,
31        output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
32    ) -> io::Result<BrotliResult> {
33        let in_buf = input.unwritten();
34        let out_buf = output.unwritten_mut();
35
36        let mut input_len = 0;
37        let mut output_len = 0;
38
39        let status = match BrotliDecompressStream(
40            &mut in_buf.len(),
41            &mut input_len,
42            in_buf,
43            &mut out_buf.len(),
44            &mut output_len,
45            out_buf,
46            &mut 0,
47            &mut self.state,
48        ) {
49            BrotliResult::ResultFailure => return Err(io::Error::other("brotli error")),
50            status => status,
51        };
52
53        input.advance(input_len);
54        output.advance(output_len);
55
56        Ok(status)
57    }
58}
59
60impl Decode for BrotliDecoder {
61    fn reinit(&mut self) -> io::Result<()> {
62        self.state = Box::new(BrotliState::new(
63            StandardAlloc::default(),
64            StandardAlloc::default(),
65            StandardAlloc::default(),
66        ));
67        Ok(())
68    }
69
70    fn decode(
71        &mut self,
72        input: &mut PartialBuffer<impl AsRef<[u8]>>,
73        output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
74    ) -> io::Result<bool> {
75        match self.decode(input, output)? {
76            BrotliResult::ResultSuccess => Ok(true),
77            BrotliResult::NeedsMoreOutput | BrotliResult::NeedsMoreInput => Ok(false),
78            BrotliResult::ResultFailure => unreachable!(),
79        }
80    }
81
82    fn flush(
83        &mut self,
84        output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
85    ) -> io::Result<bool> {
86        match self.decode(&mut PartialBuffer::new(&[][..]), output)? {
87            BrotliResult::ResultSuccess | BrotliResult::NeedsMoreInput => Ok(true),
88            BrotliResult::NeedsMoreOutput => Ok(false),
89            BrotliResult::ResultFailure => unreachable!(),
90        }
91    }
92
93    fn finish(
94        &mut self,
95        output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
96    ) -> io::Result<bool> {
97        match self.decode(&mut PartialBuffer::new(&[][..]), output)? {
98            BrotliResult::ResultSuccess => Ok(true),
99            BrotliResult::NeedsMoreOutput => Ok(false),
100            BrotliResult::NeedsMoreInput => Err(io::Error::new(
101                io::ErrorKind::UnexpectedEof,
102                "reached unexpected EOF",
103            )),
104            BrotliResult::ResultFailure => unreachable!(),
105        }
106    }
107}
108
109impl fmt::Debug for BrotliDecoder {
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        f.debug_struct("BrotliDecoder")
112            .field("decompress", &"<no debug>")
113            .finish()
114    }
115}