use libc::c_void;
use std::ffi::{c_char, CString};
use std::io::Cursor;
use std::io::Write;
use std::slice;
#[cfg(feature = "brotli")]
use crate::brotli;
#[cfg(feature = "bzip2")]
use crate::bzip2;
#[cfg(feature = "deflate")]
use crate::deflate;
#[cfg(feature = "gzip")]
use crate::gzip;
#[cfg(feature = "lz4")]
use crate::lz4;
#[cfg(feature = "snappy")]
use crate::snappy;
#[cfg(feature = "zstd")]
use crate::zstd;
#[repr(C)]
pub struct Buffer {
data: *const u8,
len: usize,
owned: bool,
}
impl Buffer {
pub fn empty() -> Self {
Buffer {
data: std::ptr::null(),
len: 0,
owned: false,
}
}
}
impl From<&Vec<u8>> for Buffer {
fn from(v: &Vec<u8>) -> Self {
Buffer {
data: v.as_ptr(),
len: v.len(),
owned: false,
}
}
}
impl From<Vec<u8>> for Buffer {
fn from(mut v: Vec<u8>) -> Self {
v.shrink_to_fit();
let buffer = Buffer {
data: v.as_ptr(),
len: v.len(),
owned: true,
};
std::mem::forget(v);
buffer
}
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub enum Codec {
#[cfg(feature = "snappy")]
#[allow(dead_code)]
Snappy,
#[cfg(feature = "snappy")]
#[allow(dead_code)]
SnappyRaw,
#[cfg(feature = "bzip2")]
#[allow(dead_code)]
Bzip2,
#[cfg(feature = "lz4")]
#[allow(dead_code)]
Lz4,
#[cfg(feature = "lz4")]
#[allow(dead_code)]
Lz4Block,
#[cfg(feature = "zstd")]
#[allow(dead_code)]
Zstd,
#[cfg(feature = "gzip")]
#[allow(dead_code)]
Gzip,
#[cfg(feature = "brotli")]
#[allow(dead_code)]
Brotli,
}
#[derive(Debug)]
#[repr(C)]
pub enum StreamingCodec {
#[cfg(feature = "bzip2")]
#[allow(dead_code)]
StreamingBzip2,
#[cfg(feature = "snappy")]
#[allow(dead_code)]
StreamingSnappy,
#[cfg(feature = "lz4")]
#[allow(dead_code)]
StreamingLz4,
#[cfg(feature = "zstd")]
#[allow(dead_code)]
StreamingZstd,
#[cfg(feature = "gzip")]
#[allow(dead_code)]
StreamingGzip,
#[cfg(feature = "brotli")]
#[allow(dead_code)]
StreamingBrotli,
}
#[cfg(feature = "snappy")]
type SnappyFrameCompressor = snappy::snap::write::FrameEncoder<Vec<u8>>;
#[cfg(feature = "bzip2")]
type Bzip2Compressor = bzip2::bzip2::write::BzEncoder<Vec<u8>>;
#[cfg(feature = "lz4")]
type Lz4Compressor = crate::lz4::lz4::Encoder<Vec<u8>>;
#[cfg(feature = "gzip")]
type GzipCompressor = crate::gzip::flate2::write::GzEncoder<Vec<u8>>;
#[cfg(feature = "brotli")]
type BrotliCompressor = brotli::brotli::CompressorWriter<Vec<u8>>;
#[cfg(feature = "zstd")]
type ZstdCompressor<'a> = crate::zstd::zstd::Encoder<'a, Vec<u8>>;
type Decompressor = Cursor<Vec<u8>>;
#[inline(always)]
fn error_to_ptr(err: impl ToString, ptr: &mut *mut c_char) {
let err_msg = CString::new(err.to_string()).unwrap();
*ptr = err_msg.into_raw();
}
#[no_mangle]
pub extern "C" fn free_string(ptr: *mut c_char) {
if !ptr.is_null() {
let _ = unsafe { CString::from_raw(ptr) };
}
}
#[no_mangle]
pub extern "C" fn free_buffer(buf: Buffer) {
if !buf.data.is_null() && buf.owned {
let _ = unsafe { Vec::from_raw_parts(buf.data as *mut u8, buf.len, buf.len) };
}
}
#[no_mangle]
pub extern "C" fn decompress(
codec: Codec,
input: *const u8,
input_len: usize,
nbytes_read: &mut usize,
nbytes_written: &mut usize,
error: &mut *mut c_char,
) -> Buffer {
let mut decompressed = Cursor::new(vec![]);
let mut compressed = Cursor::new(unsafe { std::slice::from_raw_parts(input, input_len) });
let ret: Result<usize, std::io::Error> = match codec {
#[cfg(feature = "snappy")]
Codec::Snappy => snappy::decompress(&mut compressed, &mut decompressed),
#[cfg(feature = "snappy")]
Codec::SnappyRaw => snappy::raw::decompress_vec(compressed.get_ref()).map(|v| {
let len = v.len();
*decompressed.get_mut() = v;
decompressed.set_position(len as _);
compressed.set_position(input_len as _); len
}),
#[cfg(feature = "bzip2")]
Codec::Bzip2 => bzip2::decompress(&mut compressed, &mut decompressed),
#[cfg(feature = "brotli")]
Codec::Brotli => brotli::decompress(&mut compressed, &mut decompressed),
#[cfg(feature = "gzip")]
Codec::Gzip => gzip::decompress(&mut compressed, &mut decompressed),
#[cfg(feature = "zstd")]
Codec::Zstd => zstd::decompress(&mut compressed, &mut decompressed),
#[cfg(feature = "lz4")]
Codec::Lz4 => lz4::decompress(&mut compressed, &mut decompressed),
#[cfg(feature = "lz4")]
Codec::Lz4Block => lz4::block::decompress_vec(compressed.get_ref()).map(|v| {
let len = v.len();
*decompressed.get_mut() = v;
decompressed.set_position(len as _);
compressed.set_position(input_len as _); len
}),
};
match ret {
Ok(n) => {
*nbytes_read = compressed.position() as usize;
*nbytes_written = n;
match decompressed.flush() {
Ok(_) => Buffer::from(decompressed.into_inner()),
Err(err) => {
error_to_ptr(err, error);
Buffer::empty()
}
}
}
Err(err) => {
error_to_ptr(err, error);
Buffer::empty()
}
}
}
#[no_mangle]
pub extern "C" fn compress(
codec: Codec,
level: i32,
input: *const u8,
input_len: usize,
nbytes_read: &mut usize,
nbytes_written: &mut usize,
error: &mut *mut c_char,
) -> Buffer {
if level < 0 {
error_to_ptr("Requires compression >= 0", error);
return Buffer::empty();
}
let level = Some(level);
let mut compressed = Cursor::new(vec![]);
let mut decompressed = Cursor::new(unsafe { std::slice::from_raw_parts(input, input_len) });
let ret: Result<usize, std::io::Error> = match codec {
#[cfg(feature = "snappy")]
Codec::Snappy => snappy::compress(&mut decompressed, &mut compressed),
#[cfg(feature = "snappy")]
Codec::SnappyRaw => snappy::raw::compress_vec(decompressed.get_ref()).map(|v| {
let len = v.len();
*compressed.get_mut() = v;
compressed.set_position(len as _);
decompressed.set_position(input_len as _);
len
}),
#[cfg(feature = "bzip2")]
Codec::Bzip2 => bzip2::compress(&mut decompressed, &mut compressed, level.map(|v| v as _)),
#[cfg(feature = "brotli")]
Codec::Brotli => {
brotli::compress(&mut decompressed, &mut compressed, level.map(|v| v as _))
}
#[cfg(feature = "gzip")]
Codec::Gzip => gzip::compress(&mut decompressed, &mut compressed, level.map(|v| v as _)),
#[cfg(feature = "zstd")]
Codec::Zstd => zstd::compress(
&mut decompressed,
&mut compressed,
level.map(|v: i32| v as i32),
Some(input_len),
),
#[cfg(feature = "lz4")]
Codec::Lz4 => lz4::compress(&mut decompressed, &mut compressed, level.map(|v| v as _)),
#[cfg(feature = "lz4")]
Codec::Lz4Block => lz4::block::compress_vec(
decompressed.get_ref(),
level.map(|v| v as _),
None,
Some(true),
)
.map(|v| {
let len = v.len();
*compressed.get_mut() = v;
compressed.set_position(len as _);
decompressed.set_position(input_len as _);
len
}), };
match ret {
Ok(n) => {
*nbytes_read = decompressed.get_ref().len();
*nbytes_written = n;
match compressed.flush() {
Ok(_) => Buffer::from(compressed.into_inner()),
Err(err) => {
error_to_ptr(err, error);
Buffer::empty()
}
}
}
Err(err) => {
error_to_ptr(err, error);
Buffer::empty()
}
}
}
#[no_mangle]
pub extern "C" fn decompress_into(
codec: Codec,
input: *const u8,
input_len: usize,
output: *mut u8,
output_len: usize,
nbytes_read: &mut usize,
nbytes_written: &mut usize,
error: &mut *mut c_char,
) {
let mut compressed = Cursor::new(unsafe { std::slice::from_raw_parts(input, input_len) });
let mut decompressed =
Cursor::new(unsafe { std::slice::from_raw_parts_mut(output, output_len) });
let ret: Result<usize, std::io::Error> = match codec {
#[cfg(feature = "snappy")]
Codec::Snappy => snappy::decompress(&mut compressed, &mut decompressed),
#[cfg(feature = "snappy")]
Codec::SnappyRaw => snappy::raw::decompress(compressed.get_ref(), decompressed.get_mut()),
#[cfg(feature = "bzip2")]
Codec::Bzip2 => bzip2::decompress(&mut compressed, &mut decompressed),
#[cfg(feature = "brotli")]
Codec::Brotli => brotli::decompress(&mut compressed, &mut decompressed),
#[cfg(feature = "gzip")]
Codec::Gzip => gzip::decompress(&mut compressed, &mut decompressed),
#[cfg(feature = "zstd")]
Codec::Zstd => zstd::decompress(&mut compressed, &mut decompressed),
#[cfg(feature = "lz4")]
Codec::Lz4 => lz4::decompress(&mut compressed, &mut decompressed),
#[cfg(feature = "lz4")]
Codec::Lz4Block => {
lz4::block::decompress_into(&compressed.get_ref(), decompressed.get_mut(), None)
}
};
match ret {
Ok(n) => {
*nbytes_written = n;
*nbytes_read = compressed.get_ref().len();
}
Err(err) => {
error_to_ptr(err, error);
*nbytes_written = 0;
*nbytes_read = 0;
}
}
}
#[no_mangle]
pub extern "C" fn compress_into(
codec: Codec,
level: i32,
input: *const u8,
input_len: usize,
output: *mut u8,
output_len: usize,
nbytes_read: &mut usize,
nbytes_written: &mut usize,
error: &mut *mut c_char,
) {
let mut decompressed = unsafe { std::slice::from_raw_parts(input, input_len) };
let mut compressed = unsafe { std::slice::from_raw_parts_mut(output, output_len) };
if level < 0 {
error_to_ptr("Requires compression >= 0", error);
return;
}
let level = Some(level);
let ret: Result<usize, std::io::Error> = match codec {
#[cfg(feature = "snappy")]
Codec::Snappy => snappy::compress(&mut decompressed, &mut compressed),
#[cfg(feature = "snappy")]
Codec::SnappyRaw => snappy::raw::compress(decompressed, &mut compressed),
#[cfg(feature = "bzip2")]
Codec::Bzip2 => bzip2::compress(&mut decompressed, &mut compressed, level.map(|v| v as _)),
#[cfg(feature = "brotli")]
Codec::Brotli => {
brotli::compress(&mut decompressed, &mut compressed, level.map(|v| v as _))
}
#[cfg(feature = "gzip")]
Codec::Gzip => gzip::compress(&mut decompressed, &mut compressed, level.map(|v| v as _)),
#[cfg(feature = "zstd")]
Codec::Zstd => zstd::compress(
&mut decompressed,
&mut compressed,
level.map(|v: i32| v as i32),
Some(input_len),
),
#[cfg(feature = "lz4")]
Codec::Lz4 => lz4::compress(&mut decompressed, &mut compressed, level.map(|v| v as _)),
#[cfg(feature = "lz4")]
Codec::Lz4Block => lz4::block::compress_into(
decompressed,
compressed,
level.map(|v| v as _),
None,
Some(true),
),
};
match ret {
Ok(n) => {
*nbytes_written = n;
*nbytes_read = decompressed.len();
}
Err(err) => {
error_to_ptr(err, error);
*nbytes_written = 0;
*nbytes_read = 0;
}
}
}
#[no_mangle]
#[allow(unused_variables)]
pub extern "C" fn compressor_init(
codec: StreamingCodec,
level: i32,
error: &mut *mut c_char,
) -> *mut c_void {
match codec {
#[cfg(feature = "bzip2")]
StreamingCodec::StreamingBzip2 => {
if level < 0 {
error_to_ptr("Bzip2 requires compression level >= 0", error);
return std::ptr::null_mut();
}
let compressor = bzip2::bzip2::write::BzEncoder::new(
vec![],
bzip2::bzip2::Compression::new(level as _),
);
Box::into_raw(Box::new(compressor)) as _
}
#[cfg(feature = "brotli")]
StreamingCodec::StreamingBrotli => {
if level < 0 {
error_to_ptr("Brotli requires compression level >= 0", error);
return std::ptr::null_mut();
}
let compressor = brotli::make_write_compressor(vec![], Some(level as _));
Box::into_raw(Box::new(compressor)) as _
}
#[cfg(feature = "gzip")]
StreamingCodec::StreamingGzip => {
if level < 1 {
error_to_ptr("Gzip requires compression level >= 1", error);
return std::ptr::null_mut();
}
let compressor = gzip::flate2::write::GzEncoder::new(
vec![],
gzip::flate2::Compression::new(level as _),
);
Box::into_raw(Box::new(compressor)) as _
}
#[cfg(feature = "zstd")]
StreamingCodec::StreamingZstd => {
let compressor = zstd::zstd::Encoder::new(vec![], level);
Box::into_raw(Box::new(compressor)) as _
}
#[cfg(feature = "snappy")]
StreamingCodec::StreamingSnappy => {
let compressor = snappy::snap::write::FrameEncoder::new(vec![]);
Box::into_raw(Box::new(compressor)) as _
}
#[cfg(feature = "lz4")]
StreamingCodec::StreamingLz4 => {
if level < 0 {
error_to_ptr("Lz4 requires compression level >= 0", error);
return std::ptr::null_mut();
}
let compressor = lz4::make_write_compressor(vec![], Some(level as _));
Box::into_raw(Box::new(compressor)) as _
}
}
}
#[no_mangle]
pub extern "C" fn free_compressor(codec: StreamingCodec, compressor_ptr: &mut *mut c_void) {
if !(*compressor_ptr).is_null() {
{
match codec {
#[cfg(feature = "bzip2")]
StreamingCodec::StreamingBzip2 => {
let _ = unsafe { Box::from_raw(*compressor_ptr as *mut Bzip2Compressor) };
}
#[cfg(feature = "brotli")]
StreamingCodec::StreamingBrotli => {
let _ = unsafe { Box::from_raw(*compressor_ptr as *mut BrotliCompressor) };
}
#[cfg(feature = "gzip")]
StreamingCodec::StreamingGzip => {
let _ = unsafe { Box::from_raw(*compressor_ptr as *mut GzipCompressor) };
}
#[cfg(feature = "zstd")]
StreamingCodec::StreamingZstd => {
let _ = unsafe { Box::from_raw(*compressor_ptr as *mut ZstdCompressor) };
}
#[cfg(feature = "snappy")]
StreamingCodec::StreamingSnappy => {
let _ = unsafe { Box::from_raw(*compressor_ptr as *mut SnappyFrameCompressor) };
}
#[cfg(feature = "lz4")]
StreamingCodec::StreamingLz4 => {
let _ = unsafe { Box::from_raw(*compressor_ptr as *mut Lz4Compressor) };
}
}
}
*compressor_ptr = std::ptr::null_mut();
}
}
#[no_mangle]
pub extern "C" fn compressor_inner(
codec: StreamingCodec,
compressor_ptr: &mut *mut c_void,
) -> Buffer {
match codec {
#[cfg(feature = "bzip2")]
StreamingCodec::StreamingBzip2 => {
let compressor = unsafe { Box::from_raw(*compressor_ptr as *mut Bzip2Compressor) };
let buffer = Buffer::from(compressor.get_ref());
*compressor_ptr = Box::into_raw(compressor) as _;
buffer
}
#[cfg(feature = "brotli")]
StreamingCodec::StreamingBrotli => {
let compressor = unsafe { Box::from_raw(*compressor_ptr as *mut BrotliCompressor) };
let buffer = Buffer::from(compressor.get_ref());
*compressor_ptr = Box::into_raw(compressor) as _;
buffer
}
#[cfg(feature = "gzip")]
StreamingCodec::StreamingGzip => {
let compressor = unsafe { Box::from_raw(*compressor_ptr as *mut GzipCompressor) };
let buffer = Buffer::from(compressor.get_ref());
*compressor_ptr = Box::into_raw(compressor) as _;
buffer
}
#[cfg(feature = "zstd")]
StreamingCodec::StreamingZstd => {
let compressor = unsafe { Box::from_raw(*compressor_ptr as *mut ZstdCompressor) };
let buffer = Buffer::from(compressor.get_ref());
*compressor_ptr = Box::into_raw(compressor) as _;
buffer
}
#[cfg(feature = "snappy")]
StreamingCodec::StreamingSnappy => {
let compressor =
unsafe { Box::from_raw(*compressor_ptr as *mut SnappyFrameCompressor) };
let buffer = Buffer::from(compressor.get_ref());
*compressor_ptr = Box::into_raw(compressor) as _;
buffer
}
#[cfg(feature = "lz4")]
StreamingCodec::StreamingLz4 => {
let compressor = unsafe { Box::from_raw(*compressor_ptr as *mut Lz4Compressor) };
let buffer = Buffer::from(compressor.writer());
*compressor_ptr = Box::into_raw(compressor) as _;
buffer
}
}
}
#[no_mangle]
pub extern "C" fn compressor_finish(
codec: StreamingCodec,
compressor_ptr: &mut *mut c_void,
error: &mut *mut c_char,
) -> Buffer {
let buf = match codec {
#[cfg(feature = "bzip2")]
StreamingCodec::StreamingBzip2 => {
let compressor = unsafe { Box::from_raw(*compressor_ptr as *mut Bzip2Compressor) };
match compressor.finish() {
Ok(buf) => Buffer::from(buf),
Err(err) => {
error_to_ptr(err, error);
Buffer::empty()
}
}
}
#[cfg(feature = "brotli")]
StreamingCodec::StreamingBrotli => {
let mut compressor = unsafe { Box::from_raw(*compressor_ptr as *mut BrotliCompressor) };
if let Err(err) = compressor.flush() {
error_to_ptr(err, error);
return Buffer::empty();
}
Buffer::from(compressor.into_inner())
}
#[cfg(feature = "gzip")]
StreamingCodec::StreamingGzip => {
let compressor = unsafe { Box::from_raw(*compressor_ptr as *mut GzipCompressor) };
match compressor.finish() {
Ok(buf) => Buffer::from(buf),
Err(err) => {
error_to_ptr(err, error);
Buffer::empty()
}
}
}
#[cfg(feature = "zstd")]
StreamingCodec::StreamingZstd => {
let compressor = unsafe { Box::from_raw(*compressor_ptr as *mut ZstdCompressor) };
match compressor.finish() {
Ok(buf) => Buffer::from(buf),
Err(err) => {
error_to_ptr(err, error);
Buffer::empty()
}
}
}
#[cfg(feature = "snappy")]
StreamingCodec::StreamingSnappy => {
let compressor =
unsafe { Box::from_raw(*compressor_ptr as *mut SnappyFrameCompressor) };
match compressor.into_inner() {
Ok(buf) => Buffer::from(buf),
Err(err) => {
error_to_ptr(err, error);
Buffer::empty()
}
}
}
#[cfg(feature = "lz4")]
StreamingCodec::StreamingLz4 => {
let compressor = unsafe { Box::from_raw(*compressor_ptr as *mut Lz4Compressor) };
let (w, ret) = compressor.finish();
match ret {
Ok(_) => Buffer::from(w),
Err(err) => {
error_to_ptr(err, error);
Buffer::empty()
}
}
}
};
*compressor_ptr = std::ptr::null_mut();
buf
}
#[no_mangle]
pub extern "C" fn compressor_flush(
codec: StreamingCodec,
compressor_ptr: &mut *mut c_void,
error: &mut *mut c_char,
) {
match codec {
#[cfg(feature = "bzip2")]
StreamingCodec::StreamingBzip2 => {
let mut compressor = unsafe { Box::from_raw(*compressor_ptr as *mut Bzip2Compressor) };
if let Err(err) = compressor.flush() {
error_to_ptr(err, error);
}
*compressor_ptr = Box::into_raw(compressor) as _;
}
#[cfg(feature = "brotli")]
StreamingCodec::StreamingBrotli => {
let mut compressor = unsafe { Box::from_raw(*compressor_ptr as *mut BrotliCompressor) };
if let Err(err) = compressor.flush() {
error_to_ptr(err, error);
}
*compressor_ptr = Box::into_raw(compressor) as _;
}
#[cfg(feature = "gzip")]
StreamingCodec::StreamingGzip => {
let mut compressor = unsafe { Box::from_raw(*compressor_ptr as *mut GzipCompressor) };
if let Err(err) = compressor.flush() {
error_to_ptr(err, error);
}
*compressor_ptr = Box::into_raw(compressor) as _;
}
#[cfg(feature = "zstd")]
StreamingCodec::StreamingZstd => {
let mut compressor = unsafe { Box::from_raw(*compressor_ptr as *mut ZstdCompressor) };
if let Err(err) = compressor.flush() {
error_to_ptr(err, error);
}
*compressor_ptr = Box::into_raw(compressor) as _;
}
#[cfg(feature = "snappy")]
StreamingCodec::StreamingSnappy => {
let mut compressor =
unsafe { Box::from_raw(*compressor_ptr as *mut SnappyFrameCompressor) };
if let Err(err) = compressor.flush() {
error_to_ptr(err, error);
}
*compressor_ptr = Box::into_raw(compressor) as _;
}
#[cfg(feature = "lz4")]
StreamingCodec::StreamingLz4 => {
let mut compressor = unsafe { Box::from_raw(*compressor_ptr as *mut Lz4Compressor) };
if let Err(err) = compressor.flush() {
error_to_ptr(err, error);
}
*compressor_ptr = Box::into_raw(compressor) as _;
}
}
}
#[no_mangle]
pub extern "C" fn compressor_compress(
codec: StreamingCodec,
compressor_ptr: &mut *mut c_void,
input: *const u8,
input_len: usize,
nbytes_read: &mut usize,
nbytes_written: &mut usize,
error: &mut *mut c_char,
) {
let mut decompressed = Cursor::new(unsafe { slice::from_raw_parts(input, input_len) });
match codec {
#[cfg(feature = "bzip2")]
StreamingCodec::StreamingBzip2 => {
let mut compressor = unsafe { Box::from_raw(*compressor_ptr as *mut Bzip2Compressor) };
match std::io::copy(&mut decompressed, &mut compressor) {
Ok(n) => {
*nbytes_written = n as _;
*nbytes_read = decompressed.position() as _;
}
Err(err) => {
error_to_ptr(err, error);
}
}
*compressor_ptr = Box::into_raw(compressor) as _;
}
#[cfg(feature = "brotli")]
StreamingCodec::StreamingBrotli => {
let mut compressor = unsafe { Box::from_raw(*compressor_ptr as *mut BrotliCompressor) };
match std::io::copy(&mut decompressed, &mut compressor) {
Ok(n) => {
*nbytes_written = n as _;
*nbytes_read = decompressed.position() as _;
}
Err(err) => {
error_to_ptr(err, error);
}
}
*compressor_ptr = Box::into_raw(compressor) as _;
}
#[cfg(feature = "gzip")]
StreamingCodec::StreamingGzip => {
let mut compressor = unsafe { Box::from_raw(*compressor_ptr as *mut GzipCompressor) };
match std::io::copy(&mut decompressed, &mut compressor) {
Ok(n) => {
*nbytes_written = n as _;
*nbytes_read = decompressed.position() as _;
}
Err(err) => {
error_to_ptr(err, error);
}
}
*compressor_ptr = Box::into_raw(compressor) as _;
}
#[cfg(feature = "zstd")]
StreamingCodec::StreamingZstd => {
let mut compressor = unsafe { Box::from_raw(*compressor_ptr as *mut ZstdCompressor) };
match std::io::copy(&mut decompressed, &mut compressor) {
Ok(n) => {
*nbytes_written = n as _;
*nbytes_read = decompressed.position() as _;
}
Err(err) => {
error_to_ptr(err, error);
}
}
*compressor_ptr = Box::into_raw(compressor) as _;
}
#[cfg(feature = "snappy")]
StreamingCodec::StreamingSnappy => {
let mut compressor =
unsafe { Box::from_raw(*compressor_ptr as *mut SnappyFrameCompressor) };
match std::io::copy(&mut decompressed, &mut compressor) {
Ok(n) => {
*nbytes_written = n as _;
*nbytes_read = decompressed.position() as _;
}
Err(err) => {
error_to_ptr(err, error);
}
}
*compressor_ptr = Box::into_raw(compressor) as _;
}
#[cfg(feature = "lz4")]
StreamingCodec::StreamingLz4 => {
let mut compressor = unsafe { Box::from_raw(*compressor_ptr as *mut Lz4Compressor) };
match std::io::copy(&mut decompressed, &mut compressor) {
Ok(n) => {
*nbytes_written = n as _;
*nbytes_read = decompressed.position() as _;
}
Err(err) => {
error_to_ptr(err, error);
}
}
*compressor_ptr = Box::into_raw(compressor) as _;
}
}
}
#[no_mangle]
#[allow(unused_variables)]
pub extern "C" fn decompressor_init(codec: StreamingCodec) -> *mut c_void {
let buf: Vec<u8> = vec![];
Box::into_raw(Box::new(Cursor::new(buf))) as _
}
#[no_mangle]
#[allow(unused_variables)]
pub extern "C" fn free_decompressor(codec: StreamingCodec, decompressor_ptr: &mut *mut c_void) {
if !(*decompressor_ptr).is_null() {
{
let _ = unsafe { Box::from_raw(*decompressor_ptr as *mut Decompressor) };
}
*decompressor_ptr = std::ptr::null_mut();
}
}
#[no_mangle]
#[allow(unused_variables)]
pub extern "C" fn decompressor_inner(
codec: StreamingCodec,
decompressor_ptr: &mut *mut c_void,
) -> Buffer {
let decompressor = unsafe { Box::from_raw(*decompressor_ptr as *mut Decompressor) };
let buf = Buffer::from(decompressor.get_ref());
*decompressor_ptr = Box::into_raw(decompressor) as _;
buf
}
#[no_mangle]
#[allow(unused_variables)]
pub extern "C" fn decompressor_finish(
codec: StreamingCodec,
decompressor_ptr: &mut *mut c_void,
error: &mut *mut c_char,
) -> Buffer {
let mut cursor = unsafe { Box::from_raw(*decompressor_ptr as *mut Decompressor) };
if let Err(err) = cursor.flush() {
error_to_ptr(err, error);
return Buffer::empty();
};
*decompressor_ptr = std::ptr::null_mut();
Buffer::from(cursor.into_inner())
}
#[no_mangle]
#[allow(unused_variables)]
pub extern "C" fn decompressor_flush(
codec: StreamingCodec,
decompressor_ptr: &mut *mut c_void,
error: &mut *mut c_char,
) {
let mut cursor = unsafe { Box::from_raw(*decompressor_ptr as *mut Decompressor) };
if let Err(err) = cursor.flush() {
error_to_ptr(err, error);
}
*decompressor_ptr = Box::into_raw(cursor) as _;
}
#[no_mangle]
pub extern "C" fn decompressor_decompress(
codec: StreamingCodec,
decompressor_ptr: &mut *mut c_void,
input: *const u8,
input_len: usize,
nbytes_read: &mut usize,
nbytes_written: &mut usize,
error: &mut *mut c_char,
) {
let mut decompressed = unsafe { Box::from_raw(*decompressor_ptr as *mut Decompressor) };
let start_pos = decompressed.position();
let mut compressed = Cursor::new(unsafe { std::slice::from_raw_parts(input, input_len) });
let ret: Result<usize, std::io::Error> = match codec {
#[cfg(feature = "bzip2")]
StreamingCodec::StreamingBzip2 => bzip2::decompress(&mut compressed, &mut decompressed),
#[cfg(feature = "gzip")]
StreamingCodec::StreamingGzip => gzip::decompress(&mut compressed, &mut decompressed),
#[cfg(feature = "brotli")]
StreamingCodec::StreamingBrotli => brotli::decompress(&mut compressed, &mut decompressed),
#[cfg(feature = "zstd")]
StreamingCodec::StreamingZstd => zstd::decompress(&mut compressed, &mut decompressed),
#[cfg(feature = "snappy")]
StreamingCodec::StreamingSnappy => snappy::decompress(&mut compressed, &mut decompressed),
#[cfg(feature = "lz4")]
StreamingCodec::StreamingLz4 => lz4::decompress(&mut compressed, &mut decompressed),
};
match ret {
Ok(_) => {
*nbytes_read = compressed.position() as _;
*nbytes_written = (decompressed.position() - start_pos) as _;
}
Err(err) => {
error_to_ptr(err, error);
}
};
*decompressor_ptr = Box::into_raw(decompressed) as _;
}
#[cfg(feature = "lz4")]
#[no_mangle]
pub extern "C" fn lz4_frame_max_compression_level() -> usize {
lz4::LZ4_ACCELERATION_MAX as _
}
#[cfg(feature = "lz4")]
#[no_mangle]
pub extern "C" fn lz4_frame_max_compressed_len(input_len: usize, compression_level: i32) -> usize {
lz4::compress_bound(input_len, Some(compression_level as _))
}
#[cfg(feature = "lz4")]
#[no_mangle]
#[allow(unused_variables)]
pub extern "C" fn lz4_block_max_compressed_len(input_len: usize, error: &mut *mut c_char) -> usize {
lz4::block::compress_bound(input_len, Some(true))
}
#[cfg(feature = "deflate")]
#[no_mangle]
pub extern "C" fn deflate_max_compressed_len(input_len: usize) -> usize {
deflate::compress_bound(input_len)
}
#[cfg(feature = "gzip")]
#[no_mangle]
pub extern "C" fn gzip_max_compressed_len(input_len: usize) -> usize {
gzip::compress_bound(input_len)
}
#[cfg(feature = "zstd")]
#[no_mangle]
pub extern "C" fn zstd_max_compressed_len(input_len: usize) -> usize {
zstd::compress_bound(input_len)
}
#[cfg(feature = "snappy")]
#[no_mangle]
pub extern "C" fn snappy_raw_max_compressed_len(input_len: usize) -> usize {
snap::raw::max_compress_len(input_len)
}
#[cfg(feature = "brotli")]
#[no_mangle]
pub extern "C" fn brotli_max_compressed_len(input_len: usize) -> usize {
brotli::compress_bound(input_len)
}
#[cfg(feature = "snappy")]
#[no_mangle]
pub extern "C" fn snappy_raw_decompressed_len(
input: *const u8,
input_len: usize,
error: &mut *mut c_char,
) -> isize {
let input = unsafe { slice::from_raw_parts(input, input_len) };
match snap::raw::decompress_len(input) {
Ok(n) => n as _,
Err(err) => {
error_to_ptr(err, error);
-1
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const DATA: &[u8; 5] = b"bytes";
#[cfg(feature = "lz4")]
#[test]
fn test_lz4_frame_max_compressed_len() {
let len = lz4_frame_max_compressed_len(25, 4);
assert_eq!(len, 65544);
}
#[cfg(feature = "lz4")]
#[test]
fn test_lz4_block_max_compressed_len() {
let mut error: *mut c_char = std::ptr::null_mut();
let len = lz4_block_max_compressed_len(10, &mut error);
assert!(error.is_null());
assert_eq!(len, 30);
}
#[cfg(feature = "snappy")]
#[test]
fn test_snappy_raw_max_compressed_len() {
let len = snappy_raw_max_compressed_len(10);
assert_eq!(len, 43);
}
#[cfg(feature = "snappy")]
#[test]
fn test_snappy_raw_decompressed_len() {
let uncompressed = b"bytes";
let mut compressed = vec![0; snappy_raw_max_compressed_len(uncompressed.len())];
let nbytes_written = snappy::raw::compress(uncompressed, &mut compressed).unwrap();
let mut error: *mut c_char = std::ptr::null_mut();
let len = snappy_raw_decompressed_len(compressed.as_ptr(), nbytes_written, &mut error);
assert!(error.is_null());
assert_eq!(len as usize, uncompressed.len());
}
#[cfg(feature = "snappy")]
#[test]
fn test_snappy_roundtrip() {
let mut expected = vec![];
snappy::compress(Cursor::new(DATA), &mut expected).unwrap();
roundtrip(Codec::Snappy, &expected, 0);
}
#[cfg(feature = "snappy")]
#[test]
fn test_snappy_raw_roundtrip() {
let expected = snappy::raw::compress_vec(DATA).unwrap();
roundtrip(Codec::SnappyRaw, &expected, 0);
}
#[cfg(feature = "lz4")]
#[test]
fn test_lz4_roundtrip() {
let mut expected = Cursor::new(vec![]);
lz4::compress(Cursor::new(DATA), &mut expected, Some(6)).unwrap();
let expected = expected.into_inner();
roundtrip(Codec::Lz4, &expected, 6);
}
#[cfg(feature = "lz4")]
#[test]
fn test_lz4_block_roundtrip() {
let expected = lz4::block::compress_vec(DATA, Some(6), Some(1), Some(true)).unwrap();
roundtrip(Codec::Lz4Block, &expected, 6);
}
#[cfg(feature = "bzip2")]
#[test]
fn test_bzip2_roundtrip() {
let mut expected = Cursor::new(vec![]);
bzip2::compress(Cursor::new(DATA), &mut expected, Some(6)).unwrap();
let expected = expected.into_inner();
roundtrip(Codec::Bzip2, &expected, 6);
}
#[cfg(feature = "brotli")]
#[test]
fn test_brotli_roundtrip() {
let mut expected = Cursor::new(vec![]);
brotli::compress(Cursor::new(DATA), &mut expected, Some(6)).unwrap();
let expected = expected.into_inner();
roundtrip(Codec::Brotli, &expected, 6);
}
#[cfg(feature = "zstd")]
#[test]
fn test_zstd_roundtrip() {
let mut expected = Cursor::new(vec![]);
zstd::compress(Cursor::new(DATA), &mut expected, Some(6), Some(DATA.len())).unwrap();
let expected = expected.into_inner();
roundtrip(Codec::Zstd, &expected, 6);
}
fn roundtrip(codec: Codec, expected: &[u8], level: i32) {
let mut nbytes_read = 0;
let mut nbytes_written = 0;
let mut error = std::ptr::null_mut();
let buffer = compress(
codec,
level,
DATA.as_ptr(),
DATA.len(),
&mut nbytes_read,
&mut nbytes_written,
&mut error,
);
if !error.is_null() {
let error = unsafe { CString::from_raw(error) };
panic!("Failed: {}", error.to_str().unwrap());
}
assert_eq!(nbytes_read, DATA.len());
assert_eq!(nbytes_written, buffer.len);
assert!(buffer.owned);
let compressed =
unsafe { Vec::from_raw_parts(buffer.data as *mut u8, buffer.len, buffer.len) };
assert_eq!(&compressed, expected);
nbytes_read = 0;
nbytes_written = 0;
let buffer = decompress(
codec,
compressed.as_ptr(),
compressed.len(),
&mut nbytes_read,
&mut nbytes_written,
&mut error,
);
if !error.is_null() {
let error = unsafe { CString::from_raw(error) };
panic!("Failed: {}", error.to_str().unwrap());
}
assert_eq!(nbytes_read, compressed.len());
assert_eq!(nbytes_written, buffer.len);
assert_eq!(nbytes_written, DATA.len());
assert!(buffer.owned);
let decompressed =
unsafe { Vec::from_raw_parts(buffer.data as *mut u8, buffer.len, buffer.len) };
assert_eq!(DATA.as_slice(), &decompressed);
}
}