compression_codecs/lz4/
decoder.rs

1use crate::DecodeV2;
2use compression_core::{
3    unshared::Unshared,
4    util::{PartialBuffer, WriteBuffer},
5};
6use lz4::liblz4::{
7    check_error, LZ4FDecompressionContext, LZ4F_createDecompressionContext, LZ4F_decompress,
8    LZ4F_freeDecompressionContext, LZ4F_resetDecompressionContext, LZ4F_VERSION,
9};
10use std::io::Result;
11
12#[derive(Debug)]
13struct DecoderContext {
14    ctx: LZ4FDecompressionContext,
15}
16
17#[derive(Debug)]
18pub struct Lz4Decoder {
19    ctx: Unshared<DecoderContext>,
20    stream_ended: bool,
21}
22
23impl DecoderContext {
24    fn new() -> Result<Self> {
25        let mut context = LZ4FDecompressionContext(core::ptr::null_mut());
26        check_error(unsafe { LZ4F_createDecompressionContext(&mut context, LZ4F_VERSION) })?;
27        Ok(Self { ctx: context })
28    }
29}
30
31impl Drop for DecoderContext {
32    fn drop(&mut self) {
33        unsafe { LZ4F_freeDecompressionContext(self.ctx) };
34    }
35}
36
37impl Default for Lz4Decoder {
38    fn default() -> Self {
39        Self {
40            ctx: Unshared::new(DecoderContext::new().unwrap()),
41            stream_ended: false,
42        }
43    }
44}
45
46impl Lz4Decoder {
47    pub fn new() -> Self {
48        Self::default()
49    }
50}
51
52impl DecodeV2 for Lz4Decoder {
53    fn reinit(&mut self) -> Result<()> {
54        unsafe { LZ4F_resetDecompressionContext(self.ctx.get_mut().ctx) };
55        self.stream_ended = false;
56        Ok(())
57    }
58
59    fn decode(
60        &mut self,
61        input: &mut PartialBuffer<&[u8]>,
62        output: &mut WriteBuffer<'_>,
63    ) -> Result<bool> {
64        let mut input_size = input.unwritten().len();
65
66        // Safety: We **trust** lz4 bytes to properly function as expected,
67        // only write decompressed, initialized data into the buffer properly.
68        let result = unsafe {
69            let out_buf = output.unwritten_mut();
70
71            let mut output_size = out_buf.len();
72
73            let result = check_error(LZ4F_decompress(
74                self.ctx.get_mut().ctx,
75                out_buf.as_mut_ptr() as *mut _,
76                &mut output_size,
77                input.unwritten().as_ptr(),
78                &mut input_size,
79                core::ptr::null(),
80            ));
81            output.assume_init_and_advance(output_size);
82
83            result
84        };
85        input.advance(input_size);
86
87        let finished = result? == 0;
88        if finished {
89            self.stream_ended = true;
90        }
91        Ok(finished)
92    }
93
94    fn flush(&mut self, output: &mut WriteBuffer<'_>) -> Result<bool> {
95        self.decode(&mut PartialBuffer::new(&[][..]), output)?;
96
97        loop {
98            let old_len = output.written_len();
99            self.decode(&mut PartialBuffer::new(&[][..]), output)?;
100            if output.written_len() == old_len {
101                break;
102            }
103        }
104
105        Ok(!output.has_no_spare_space())
106    }
107
108    fn finish(&mut self, output: &mut WriteBuffer<'_>) -> Result<bool> {
109        self.flush(output)?;
110
111        if self.stream_ended {
112            Ok(true)
113        } else {
114            Err(std::io::Error::new(
115                std::io::ErrorKind::UnexpectedEof,
116                "lz4 stream did not finish",
117            ))
118        }
119    }
120}