Skip to main content

compression_codecs/mbrotli/
decoder.rs

1use crate::DecodeV2;
2use compression_core::util::{PartialBuffer, WriteBuffer};
3use mbrotli::{
4    DecodeOperation, DecodeStreamConfig, DecoderConfig, DecoderSessionOwned, DecoderStatus,
5    Decompressor, WindowLimit,
6};
7use std::io;
8
9/// Only accept RFC 7932 windows, as the `brotli` backend does. Large windows (RFC 9841) are not
10/// valid in the `br` content coding.
11const DECODER_CONFIG: DecoderConfig = DecoderConfig::new().with_window_limit(
12    match WindowLimit::standard(mbrotli::Window::MAX_STANDARD_BITS) {
13        Ok(limit) => limit,
14        Err(_) => panic!("the standard window limit is valid"),
15    },
16);
17
18#[derive(Debug)]
19pub struct MbrotliDecoder {
20    session: DecoderSessionOwned,
21}
22
23impl Default for MbrotliDecoder {
24    fn default() -> Self {
25        let decompressor = Decompressor::new(DECODER_CONFIG).unwrap();
26        // A fresh decompressor without an exact output size has no way to refuse a session.
27        let session = decompressor
28            .into_session(DecodeStreamConfig::default())
29            .unwrap();
30        Self { session }
31    }
32}
33
34impl MbrotliDecoder {
35    pub fn new() -> Self {
36        Self::default()
37    }
38}
39
40/// Feeds `input` to the session, writing straight into the uninitialized tail of `output`.
41///
42/// Returns how much input was consumed and what the session needs next.
43fn process(
44    session: &mut DecoderSessionOwned,
45    input: &[u8],
46    output: &mut WriteBuffer<'_>,
47    operation: DecodeOperation,
48) -> (usize, io::Result<DecoderStatus>) {
49    // SAFETY: `process_uninit` never de-initializes bytes of `output`.
50    let result = session.process_uninit(input, unsafe { output.unwritten_mut() }, operation);
51    // `process_uninit` initializes exactly `produced` leading bytes of `output`, both when it
52    // succeeds and when it fails.
53    match result {
54        Ok(progress) => {
55            // SAFETY: see above.
56            unsafe { output.assume_init_and_advance(progress.produced) };
57            (progress.consumed, Ok(progress.status))
58        }
59        Err(failure) => {
60            // SAFETY: see above.
61            unsafe { output.assume_init_and_advance(failure.produced) };
62            (failure.consumed, Err(failure.into_error().into()))
63        }
64    }
65}
66
67impl DecodeV2 for MbrotliDecoder {
68    fn reinit(&mut self) -> io::Result<()> {
69        self.session.reinit(DecodeStreamConfig::default())?;
70        Ok(())
71    }
72
73    fn decode(
74        &mut self,
75        input: &mut PartialBuffer<&[u8]>,
76        output: &mut WriteBuffer<'_>,
77    ) -> io::Result<bool> {
78        let (consumed, status) = process(
79            &mut self.session,
80            input.unwritten(),
81            output,
82            DecodeOperation::Process,
83        );
84        input.advance(consumed);
85
86        match status? {
87            DecoderStatus::Finished => Ok(true),
88            DecoderStatus::NeedsInput | DecoderStatus::NeedsOutput => Ok(false),
89        }
90    }
91
92    fn flush(&mut self, output: &mut WriteBuffer<'_>) -> io::Result<bool> {
93        // Empty input with `Process` delivers pending output without declaring EOF.
94        let (_, status) = process(&mut self.session, &[], output, DecodeOperation::Process);
95
96        match status? {
97            DecoderStatus::Finished | DecoderStatus::NeedsInput => Ok(true),
98            DecoderStatus::NeedsOutput => Ok(false),
99        }
100    }
101
102    fn finish(&mut self, output: &mut WriteBuffer<'_>) -> io::Result<bool> {
103        // Declaring EOF on a member that is still open fails with `UnexpectedEof`.
104        let (_, status) = process(&mut self.session, &[], output, DecodeOperation::Finish);
105
106        match status? {
107            DecoderStatus::Finished => Ok(true),
108            DecoderStatus::NeedsInput | DecoderStatus::NeedsOutput => Ok(false),
109        }
110    }
111}