tiny-lz4-decoder-sys 1.0.1

zero dependency, tiny lz4 decompression wrapper built for lodpm
Documentation
use crate::ffi::{
    LZ4FDecompressionContext, LZ4F_createDecompressionContext, LZ4F_decompress,
    LZ4F_freeDecompressionContext, LZ4_VERSION_NUMBER,
};

use std::io::{Error, ErrorKind, Read};

const BUFFER_SIZE: usize = 32 * 1024;

struct DecoderContext {
    c: LZ4FDecompressionContext,
}

pub struct Decoder<R> {
    c: DecoderContext,
    r: R,
    buf: Box<[u8]>,
    pos: usize,
    len: usize,
    next: usize,
}

impl<R: Read> Decoder<R> {
    pub fn new(r: R) -> Result<Decoder<R>, Error> {
        Ok(Decoder {
            r,
            c: DecoderContext::new()?,
            buf: vec![0; BUFFER_SIZE].into_boxed_slice(),
            pos: BUFFER_SIZE,
            len: BUFFER_SIZE,
            next: 11,
        })
    }

    pub fn reader(&self) -> &R {
        &self.r
    }

    pub fn finish(self) -> (R, Result<(), Error>) {
        (
            self.r,
            match self.next {
                0 => Ok(()),
                _ => Err(Error::new(
                    ErrorKind::Interrupted,
                    "`fn finish` can't be called before `fn reader`",
                )),
            },
        )
    }
}

impl<R: Read> Read for Decoder<R> {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
        if self.next == 0 || buf.is_empty() {
            return Ok(0);
        }
        let mut dst_offset: usize = 0;
        while dst_offset == 0 {
            if self.pos >= self.len {
                let need = if self.buf.len() < self.next {
                    self.buf.len()
                } else {
                    self.next
                };
                self.len = self.r.read(&mut self.buf[0..need])?;
                if self.len == 0 {
                    break;
                }
                self.pos = 0;
                self.next -= self.len;
            }
            while (dst_offset < buf.len()) && (self.pos < self.len) {
                let mut src_size = self.len - self.pos;
                let mut dst_size = buf.len() - dst_offset;

                let len = crate::ehandle::handle_error(unsafe {
                    LZ4F_decompress(
                        self.c.c,
                        buf[dst_offset..].as_mut_ptr(),
                        &mut dst_size,
                        self.buf[self.pos..].as_ptr(),
                        &mut src_size,
                        std::ptr::null(),
                    )
                })?;

                self.pos += src_size;
                dst_offset += dst_size;

                if len == 0 {
                    self.next = 0;
                    return Ok(dst_offset);
                } else if self.next < len {
                    self.next = len;
                }
            }
        }

        Ok(dst_offset)
    }
}

impl DecoderContext {
    fn new() -> Result<DecoderContext, Error> {
        let mut context = LZ4FDecompressionContext(std::ptr::null_mut());
        crate::ehandle::handle_error(unsafe {
            LZ4F_createDecompressionContext(&mut context, LZ4_VERSION_NUMBER)
        })?;
        Ok(DecoderContext { c: context })
    }
}

impl Drop for DecoderContext {
    fn drop(&mut self) {
        unsafe { LZ4F_freeDecompressionContext(self.c) };
    }
}