use blosc_sys::*;
use std::{
convert::Into,
hash::{Hash, Hasher},
marker::PhantomData,
os::raw::{c_char, c_int, c_void},
{mem, ptr},
};
use thiserror::Error;
#[derive(Clone, Copy, Debug, Error, Eq, PartialEq)]
pub enum BloscError {
#[error("Compressor not supported by this build of c-Blosc")]
CompressorNotSupported,
#[error("Not a valid Blosc buffer")]
ValidationError,
#[error("unspecified error from c-Blosc")]
Unspecified,
}
pub type Result<T> = std::result::Result<T, BloscError>;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)]
#[repr(i32)]
pub enum Clevel {
None = 0,
L1 = 1,
L2 = 2,
L3 = 3,
L4 = 4,
L5 = 5,
L6 = 6,
L7 = 7,
L8 = 8,
L9 = 9,
}
const BLOSC_INVALID_COMPNAME: &[u8; 8usize] = b"invalid\0";
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[allow(clippy::manual_non_exhaustive)]
pub enum Compressor {
BloscLZ,
LZ4,
LZ4HC,
Snappy,
Zlib,
Zstd,
#[doc(hidden)]
Invalid,
}
impl From<Compressor> for *const c_char {
fn from(compressor: Compressor) -> Self {
let compref = match compressor {
Compressor::BloscLZ => BLOSC_BLOSCLZ_COMPNAME.as_ptr(),
Compressor::LZ4 => BLOSC_LZ4_COMPNAME.as_ptr(),
Compressor::LZ4HC => BLOSC_LZ4HC_COMPNAME.as_ptr(),
Compressor::Snappy => BLOSC_SNAPPY_COMPNAME.as_ptr(),
Compressor::Zlib => BLOSC_ZLIB_COMPNAME.as_ptr(),
Compressor::Zstd => BLOSC_ZSTD_COMPNAME.as_ptr(),
Compressor::Invalid => BLOSC_INVALID_COMPNAME.as_ptr(),
};
compref as *const c_char
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(i32)]
pub enum ShuffleMode {
None = BLOSC_NOSHUFFLE as i32,
Byte = BLOSC_SHUFFLE as i32,
Bit = BLOSC_BITSHUFFLE as i32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Context {
blocksize: usize,
clevel: Clevel,
compressor: Compressor,
shuffle_mode: ShuffleMode,
typesize: Option<usize>,
}
pub struct Buffer<T> {
data: Vec<u8>,
phantom: PhantomData<T>,
}
impl<T> Buffer<T> {
fn from_vec(src: Vec<u8>) -> Self {
Buffer {
data: src,
phantom: PhantomData,
}
}
pub fn size(&self) -> usize {
self.data.len()
}
}
impl<T> AsRef<[u8]> for Buffer<T> {
fn as_ref(&self) -> &[u8] {
self.data.as_ref()
}
}
impl<T> Hash for Buffer<T> {
fn hash<H: Hasher>(&self, hasher: &mut H) {
hasher.write(self.as_ref());
}
}
impl<T> From<Buffer<T>> for Vec<u8> {
fn from(buf: Buffer<T>) -> Self {
buf.data
}
}
impl Context {
pub fn blocksize(mut self, blocksize: Option<usize>) -> Self {
self.blocksize = blocksize.unwrap_or(0);
self
}
pub const fn clevel(mut self, clevel: Clevel) -> Self {
self.clevel = clevel;
self
}
pub fn compressor(mut self, compressor: Compressor) -> Result<Self> {
let comp_ptr: *const c_char = compressor.into();
let support = unsafe { blosc_get_complib_info(comp_ptr, ptr::null_mut(), ptr::null_mut()) };
if support >= 0 {
self.compressor = compressor;
Ok(self)
} else {
Err(BloscError::CompressorNotSupported)
}
}
pub fn compress<T>(&self, src: &[T]) -> Buffer<T> {
let typesize = self.typesize.unwrap_or(mem::size_of::<T>());
let src_size = mem::size_of_val(src);
let dest_size = src_size + BLOSC_MAX_OVERHEAD as usize;
let mut dest: Vec<u8> = Vec::with_capacity(dest_size);
let rsize = unsafe {
blosc_compress_ctx(
self.clevel as c_int,
self.shuffle_mode as c_int,
typesize,
src_size,
src.as_ptr() as *const c_void,
dest.as_mut_ptr() as *mut c_void,
dest_size,
self.compressor.into(),
self.blocksize,
1,
)
};
assert!(
rsize >= 0,
"C-Blosc internal error with Context={:?}, typesize={:?} nbytes={:?} and destsize={:?}",
self,
typesize,
src_size,
dest_size
);
unsafe {
dest.set_len(rsize as usize);
}
dest.shrink_to_fit();
Buffer::from_vec(dest)
}
pub const fn new() -> Self {
Context {
blocksize: 0, clevel: Clevel::L2, compressor: Compressor::BloscLZ, shuffle_mode: ShuffleMode::None, typesize: None, }
}
pub const fn shuffle(mut self, shuffle_mode: ShuffleMode) -> Self {
self.shuffle_mode = shuffle_mode;
self
}
pub const fn typesize(mut self, typesize: Option<usize>) -> Self {
self.typesize = typesize;
self
}
}
impl Default for Context {
fn default() -> Self {
Self::new()
}
}
pub fn decompress<T: Copy>(src: &Buffer<T>) -> Result<Vec<T>> {
unsafe { decompress_bytes(&src.data[..]) }
}
pub unsafe fn decompress_bytes<T: Copy>(src: &[u8]) -> Result<Vec<T>> {
let typesize = mem::size_of::<T>();
let mut nbytes: usize = 0;
let mut _cbytes: usize = 0;
let mut _blocksize: usize = 0;
blosc_cbuffer_sizes(
src.as_ptr() as *const c_void,
&mut nbytes as *mut usize,
&mut _cbytes as *mut usize,
&mut _blocksize as *mut usize,
);
let dest_size = nbytes / typesize;
let mut dest: Vec<T> = Vec::with_capacity(dest_size);
let rsize = blosc_decompress_ctx(
src.as_ptr() as *const c_void,
dest.as_mut_ptr() as *mut c_void,
nbytes,
1,
);
if rsize > 0 {
dest.set_len(rsize as usize / typesize);
dest.shrink_to_fit();
Ok(dest)
} else {
Err(BloscError::Unspecified)
}
}
pub fn validate(src: &[u8]) -> Result<usize> {
let mut len: usize = 0;
let r = unsafe {
blosc_cbuffer_validate(
src.as_ptr() as *const c_void,
src.len(),
&mut len as *mut usize,
)
};
if r == 0 {
Ok(len)
} else {
Err(BloscError::ValidationError)
}
}
#[test]
fn test_buffer_into() {
let v0 = vec![0u8, 1, 2, 3, 4, 5];
let v1 = v0.clone();
let buf = Buffer::<u16>::from_vec(v0);
let v2: Vec<u8> = buf.into();
assert_eq!(v1, v2);
}