compression_codecs/bzip2/
decoder.rs

1use crate::DecodeV2;
2use bzip2::{Decompress, Status};
3use compression_core::util::{PartialBuffer, WriteBuffer};
4use std::{fmt, io};
5
6pub struct BzDecoder {
7    decompress: Decompress,
8    stream_ended: bool,
9}
10
11impl fmt::Debug for BzDecoder {
12    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
13        write!(
14            f,
15            "BzDecoder {{total_in: {}, total_out: {}}}",
16            self.decompress.total_in(),
17            self.decompress.total_out()
18        )
19    }
20}
21
22impl Default for BzDecoder {
23    fn default() -> Self {
24        Self {
25            decompress: Decompress::new(false),
26            stream_ended: false,
27        }
28    }
29}
30
31impl BzDecoder {
32    pub fn new() -> Self {
33        Self::default()
34    }
35
36    fn decode(
37        &mut self,
38        input: &mut PartialBuffer<&[u8]>,
39        output: &mut WriteBuffer<'_>,
40    ) -> io::Result<Status> {
41        let prior_in = self.decompress.total_in();
42        let prior_out = self.decompress.total_out();
43
44        let result = self
45            .decompress
46            // Safety: We **trust** bzip2 to only write initialized data to it
47            .decompress_uninit(input.unwritten(), unsafe { output.unwritten_mut() })
48            .map_err(io::Error::other);
49
50        input.advance((self.decompress.total_in() - prior_in) as usize);
51        // Safety: We **trust** bzip2 to write bytes properly
52        unsafe {
53            output.assume_init_and_advance((self.decompress.total_out() - prior_out) as usize)
54        };
55
56        // Track when stream has properly ended
57        if matches!(result, Ok(Status::StreamEnd)) {
58            self.stream_ended = true;
59        }
60
61        result
62    }
63}
64
65impl DecodeV2 for BzDecoder {
66    fn reinit(&mut self) -> io::Result<()> {
67        self.decompress = Decompress::new(false);
68        self.stream_ended = false;
69        Ok(())
70    }
71
72    fn decode(
73        &mut self,
74        input: &mut PartialBuffer<&[u8]>,
75        output: &mut WriteBuffer<'_>,
76    ) -> io::Result<bool> {
77        match self.decode(input, output)? {
78            // Decompression went fine, nothing much to report.
79            Status::Ok => Ok(false),
80
81            // The Flush action on a compression went ok.
82            Status::FlushOk => unreachable!(),
83
84            // THe Run action on compression went ok.
85            Status::RunOk => unreachable!(),
86
87            // The Finish action on compression went ok.
88            Status::FinishOk => unreachable!(),
89
90            // The stream's end has been met, meaning that no more data can be input.
91            Status::StreamEnd => Ok(true),
92
93            // There was insufficient memory in the input or output buffer to complete
94            // the request, but otherwise everything went normally.
95            Status::MemNeeded => Err(io::ErrorKind::OutOfMemory.into()),
96        }
97    }
98
99    fn flush(&mut self, output: &mut WriteBuffer<'_>) -> io::Result<bool> {
100        self.decode(&mut PartialBuffer::new(&[][..]), output)?;
101
102        loop {
103            let old_len = output.written_len();
104            self.decode(&mut PartialBuffer::new(&[][..]), output)?;
105            if output.written_len() == old_len {
106                break;
107            }
108        }
109
110        Ok(!output.has_no_spare_space())
111    }
112
113    fn finish(&mut self, _output: &mut WriteBuffer<'_>) -> io::Result<bool> {
114        if self.stream_ended {
115            Ok(true)
116        } else {
117            Err(io::Error::new(
118                io::ErrorKind::UnexpectedEof,
119                "bzip2 stream did not finish",
120            ))
121        }
122    }
123}