use crate::exceptions::{CompressionError, DecompressionError};
use crate::io::{AsBytes, RustyBuffer};
use crate::{to_py_err, BytesType};
use pyo3::prelude::*;
use pyo3::wrap_pyfunction;
use pyo3::PyResult;
use std::io::Cursor;
const DEFAULT_COMPRESSION_LEVEL: u32 = 4;
pub(crate) fn init_py_module(m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(compress, m)?)?;
m.add_function(wrap_pyfunction!(decompress, m)?)?;
m.add_function(wrap_pyfunction!(compress_block, m)?)?;
m.add_function(wrap_pyfunction!(decompress_block, m)?)?;
m.add_function(wrap_pyfunction!(compress_into, m)?)?;
m.add_function(wrap_pyfunction!(decompress_into, m)?)?;
m.add_class::<Compressor>()?;
Ok(())
}
#[pyfunction]
pub fn decompress(data: BytesType, output_len: Option<usize>) -> PyResult<RustyBuffer> {
crate::generic!(decompress(data), output_len = output_len)
}
#[pyfunction]
pub fn compress(mut data: BytesType, level: Option<u32>, output_len: Option<usize>) -> PyResult<RustyBuffer> {
crate::generic!(compress(&mut data), output_len = output_len, level = level)
}
#[pyfunction]
pub fn compress_into(mut input: BytesType, mut output: BytesType, level: Option<u32>) -> PyResult<usize> {
let r = internal::compress(&mut input, &mut output, level)?;
Ok(r)
}
#[pyfunction]
pub fn decompress_into(input: BytesType, mut output: BytesType) -> PyResult<usize> {
let r = internal::decompress(input, &mut output)?;
Ok(r)
}
#[pyfunction]
pub fn decompress_block(data: BytesType, output_len: Option<usize>) -> PyResult<RustyBuffer> {
use lz4::block;
let out = to_py_err!(DecompressionError -> block::decompress(data.as_bytes(), output_len.map(|v| v as i32)))?;
Ok(RustyBuffer::from(out))
}
#[pyfunction]
#[allow(unused_variables)]
pub fn compress_block(
data: BytesType,
output_len: Option<usize>,
mode: Option<&str>,
acceleration: Option<i32>,
compression: Option<i32>,
store_size: Option<bool>,
) -> PyResult<RustyBuffer> {
use lz4::{block, block::CompressionMode};
let store_size = store_size.unwrap_or(true);
let mode = match mode {
Some(m) => match m {
"default" => CompressionMode::DEFAULT,
"fast" => CompressionMode::FAST(acceleration.unwrap_or(1)),
"high_compression" => CompressionMode::HIGHCOMPRESSION(compression.unwrap_or(9)),
_ => return Err(DecompressionError::new_err(format!("Unrecognized mode '{}'", m))),
},
None => CompressionMode::DEFAULT,
};
let out = to_py_err!(CompressionError -> block::compress(data.as_bytes(), Some(mode), store_size))?;
Ok(RustyBuffer::from(out))
}
#[pyclass]
pub struct Compressor {
inner: Option<lz4::Encoder<Cursor<Vec<u8>>>>,
}
#[pymethods]
impl Compressor {
#[new]
pub fn __init__(level: Option<u32>) -> PyResult<Self> {
let inner = lz4::EncoderBuilder::new()
.auto_flush(true)
.level(level.unwrap_or_else(|| DEFAULT_COMPRESSION_LEVEL))
.build(Cursor::new(vec![]))?;
Ok(Self { inner: Some(inner) })
}
pub fn compress(&mut self, input: &[u8]) -> PyResult<usize> {
crate::io::stream_compress(&mut self.inner, input)
}
#[allow(mutable_transmutes)] pub fn flush(&mut self) -> PyResult<RustyBuffer> {
crate::io::stream_flush(&mut self.inner, |e| {
let writer = e.writer();
unsafe { std::mem::transmute::<&Cursor<Vec<u8>>, &mut Cursor<Vec<u8>>>(writer) }
})
}
pub fn finish(&mut self) -> PyResult<RustyBuffer> {
crate::io::stream_finish(&mut self.inner, |inner| {
let (cursor, result) = inner.finish();
result.map(|_| cursor.into_inner())
})
}
}
pub(crate) mod internal {
use crate::lz4::DEFAULT_COMPRESSION_LEVEL;
use lz4::{Decoder, EncoderBuilder};
use std::io::{Error, Read, Seek, SeekFrom, Write};
pub fn decompress<W: Write + ?Sized, R: Read>(input: R, output: &mut W) -> Result<usize, Error> {
let mut decoder = Decoder::new(input)?;
let n_bytes = std::io::copy(&mut decoder, output)?;
decoder.finish().1?;
Ok(n_bytes as usize)
}
pub fn compress<W: Write + ?Sized + Seek, R: Read>(
input: &mut R,
output: &mut W,
level: Option<u32>,
) -> Result<usize, Error> {
let start_pos = output.seek(SeekFrom::Current(0))?;
let mut encoder = EncoderBuilder::new()
.auto_flush(true)
.level(level.unwrap_or_else(|| DEFAULT_COMPRESSION_LEVEL))
.build(output)?;
std::io::copy(input, &mut encoder)?;
let (w, r) = encoder.finish();
r?;
let ending_pos = w.seek(SeekFrom::Current(0))?;
Ok((ending_pos - start_pos) as usize)
}
}