use std::cell::RefCell;
use std::mem::MaybeUninit;
use crate::chunk::Decoder;
use crate::error::ErrorCode;
use crate::util::{validate_compressed_buf_and_get_sizes, CowVec};
use crate::{DParams, Error};
pub struct Chunk<'a> {
pub(crate) buffer: CowVec<'a, u8>,
pub(crate) nbytes: usize,
pub(crate) typesize: usize,
pub(crate) decoder: RefCell<Option<Decoder>>,
}
impl<'a> Chunk<'a> {
pub fn from_compressed(bytes: CowVec<'a, u8>) -> Result<Self, Error> {
let (nbytes, _cbytes, _blocksize) =
validate_compressed_buf_and_get_sizes(bytes.as_slice())?;
let mut typesize = MaybeUninit::uninit();
let mut flags = MaybeUninit::uninit();
unsafe {
blosc2_sys::blosc1_cbuffer_metainfo(
bytes.as_slice().as_ptr().cast(),
typesize.as_mut_ptr(),
flags.as_mut_ptr(),
);
}
let typesize = unsafe { typesize.assume_init() };
if typesize == 0 {
return Err(Error::Failure);
}
Ok(unsafe { Self::from_compressed_unchecked(bytes, nbytes as usize, typesize) })
}
pub(crate) unsafe fn from_compressed_unchecked(
bytes: CowVec<'a, u8>,
nbytes: usize,
typesize: usize,
) -> Self {
Self {
buffer: bytes,
nbytes,
typesize,
decoder: RefCell::new(None),
}
}
pub fn as_bytes(&self) -> &[u8] {
self.buffer.as_slice()
}
pub fn into_bytes(self) -> CowVec<'a, u8> {
self.buffer
}
fn decoder_mut(&self) -> Result<std::cell::RefMut<'_, Decoder>, Error> {
let mut decoder = self.decoder.borrow_mut();
if decoder.is_none() {
*decoder = Some(Decoder::new(Default::default())?);
}
Ok(std::cell::RefMut::map(decoder, |d| d.as_mut().unwrap()))
}
pub fn get_dparams(&self) -> DParams {
self.decoder
.borrow()
.as_ref()
.map(|decoder| decoder.params())
.unwrap_or_default()
}
pub fn set_dparams(&self, params: DParams) -> Result<(), Error> {
*self.decoder.borrow_mut() = Some(Decoder::new(params)?);
Ok(())
}
pub fn decompress(&self) -> Result<Vec<u8>, Error> {
self.decoder_mut()?.decompress(self.buffer.as_slice())
}
pub fn nbytes(&self) -> usize {
self.nbytes
}
pub fn typesize(&self) -> usize {
self.typesize
}
pub fn items_num(&self) -> usize {
self.nbytes / self.typesize
}
pub fn item(&self, idx: usize) -> Result<Vec<u8>, Error> {
self.items(idx..idx + 1)
}
pub fn item_into(&self, idx: usize, dst: &mut [MaybeUninit<u8>]) -> Result<usize, Error> {
self.items_into(idx..idx + 1, dst)
}
pub fn items(&self, idx: std::ops::Range<usize>) -> Result<Vec<u8>, Error> {
let mut dst = Vec::<MaybeUninit<u8>>::with_capacity(self.typesize * idx.len());
unsafe { dst.set_len(self.typesize * idx.len()) };
let len = self.items_into(idx, &mut dst)?;
assert!(len <= dst.len());
unsafe { dst.set_len(len) };
Ok(unsafe { std::mem::transmute::<Vec<MaybeUninit<u8>>, Vec<u8>>(dst) })
}
pub fn items_into(
&self,
idx: std::ops::Range<usize>,
dst: &mut [MaybeUninit<u8>],
) -> Result<usize, Error> {
Ok(unsafe {
blosc2_sys::blosc2_getitem_ctx(
self.decoder_mut()?.ctx_ptr(),
self.buffer.as_slice().as_ptr().cast(),
self.buffer.as_slice().len() as _,
idx.start as _,
idx.len() as _,
dst.as_mut_ptr().cast(),
dst.len() as _,
)
.into_result()? as usize
})
}
pub fn shallow_clone(&self) -> Chunk<'_> {
Chunk {
buffer: CowVec::Borrowed(self.buffer.as_slice()),
nbytes: self.nbytes,
typesize: self.typesize,
decoder: RefCell::new(self.copy_decoder()),
}
}
fn copy_decoder(&self) -> Option<Decoder> {
self.decoder
.borrow()
.as_ref()
.and_then(|decoder| Decoder::new(decoder.params()).ok( ))
}
}
impl Clone for Chunk<'_> {
fn clone(&self) -> Self {
Self {
buffer: self.buffer.clone(),
nbytes: self.nbytes,
typesize: self.typesize,
decoder: RefCell::new(self.copy_decoder()),
}
}
}
#[cfg(test)]
pub(crate) mod tests {
use rand::prelude::*;
use crate::chunk::{Chunk, Encoder};
use crate::util::tests::{rand_cparams, rand_dparams, rand_src_len};
use crate::CParams;
#[test]
fn get_item() {
let mut rand = StdRng::seed_from_u64(0xb47b5287627f3d57);
for _ in 0..30 {
let cparams = rand_cparams(&mut rand);
let dparams = rand_dparams(&mut rand);
let len = rand_src_len(cparams.get_typesize(), &mut rand);
let data = rand_chunk_data(len, &mut rand);
let chunk = Encoder::new(cparams.clone())
.unwrap()
.compress(&data)
.unwrap();
chunk.set_dparams(dparams).unwrap();
assert_eq!(cparams.get_typesize(), chunk.typesize());
assert_eq!(len, chunk.nbytes());
assert_eq!(len / chunk.typesize(), chunk.items_num());
if chunk.items_num() > 0 {
for _ in 0..10 {
let idx = rand.random_range(0..chunk.items_num());
let item = chunk.item(idx).unwrap();
assert_eq!(
item,
data[idx * chunk.typesize()..(idx + 1) * chunk.typesize()]
);
}
for _ in 0..10 {
let start = rand.random_range(0..chunk.items_num());
let end = rand.random_range(start..chunk.items_num());
let items = chunk.items(start..end).unwrap();
assert_eq!(
items,
data[start * chunk.typesize()..end * chunk.typesize()]
);
}
}
}
}
#[test]
fn chunk_covariant() {
#[allow(unused)]
fn assert_covariant<'a, 'b: 'a>(x: Chunk<'b>) -> Chunk<'a> {
x
}
}
pub(crate) fn rand_chunk(size: usize, params: CParams, rand: &mut impl Rng) -> Chunk<'static> {
let data = rand_chunk_data(size, rand);
Encoder::new(params).unwrap().compress(&data).unwrap()
}
pub(crate) fn rand_chunk_data(size: usize, rand: &mut impl Rng) -> Vec<u8> {
rand.random_iter().take(size).collect()
}
}