1use blosc2_sys as ffi;
4use parking_lot::{Mutex, RwLock};
5use std::ffi::{c_void, CStr, CString};
6use std::sync::{Arc, OnceLock};
7use std::{io, mem};
8
9pub const BLOSC2_VERSION_DATE: &'static str =
10 unsafe { std::str::from_utf8_unchecked(blosc2_sys::BLOSC2_VERSION_DATE) };
11pub const BLOSC2_VERSION_STRING: &'static str =
12 unsafe { std::str::from_utf8_unchecked(blosc2_sys::BLOSC2_VERSION_STRING) };
13pub use blosc2_sys::{
14 BLOSC2_MAX_DIM, BLOSC2_VERSION_MAJOR, BLOSC2_VERSION_MINOR, BLOSC2_VERSION_RELEASE,
15};
16
17const BLOSC2_GUARD: OnceLock<Arc<Blosc2Guard>> = OnceLock::new();
18const BLOSC2_INIT_FLAG: OnceLock<Arc<Mutex<bool>>> = OnceLock::new();
19
20pub type Result<T> = std::result::Result<T, Error>;
22
23#[derive(Debug)]
24pub enum Error {
25 Blosc2(Blosc2Error),
28 Other(String),
30}
31impl From<Blosc2Error> for Error {
32 fn from(err: Blosc2Error) -> Self {
33 Error::Blosc2(err)
34 }
35}
36impl From<String> for Error {
37 fn from(err: String) -> Self {
38 Error::Other(err)
39 }
40}
41impl<'a> From<&'a str> for Error {
42 fn from(err: &'a str) -> Self {
43 Error::Other(err.to_string())
44 }
45}
46
47impl From<Error> for io::Error {
48 fn from(err: Error) -> io::Error {
49 io::Error::new(io::ErrorKind::Other, err.to_string())
50 }
51}
52impl std::error::Error for Error {}
53
54impl std::fmt::Display for Error {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 write!(f, "{:?}", self)
57 }
58}
59
60#[derive(Debug)]
62#[repr(i32)]
63pub enum Blosc2Error {
64 Failure = ffi::BLOSC2_ERROR_FAILURE,
66 Stream = ffi::BLOSC2_ERROR_STREAM,
68 Data = ffi::BLOSC2_ERROR_DATA,
70 MemoryAlloc = ffi::BLOSC2_ERROR_MEMORY_ALLOC,
72 ReadBuffer = ffi::BLOSC2_ERROR_READ_BUFFER,
74 WriteBuffer = ffi::BLOSC2_ERROR_WRITE_BUFFER,
76 CodecSupport = ffi::BLOSC2_ERROR_CODEC_SUPPORT,
78 CodecParam = ffi::BLOSC2_ERROR_CODEC_PARAM,
80 CodecDict = ffi::BLOSC2_ERROR_CODEC_DICT,
82 VersionSupport = ffi::BLOSC2_ERROR_VERSION_SUPPORT,
84 InvalidHeader = ffi::BLOSC2_ERROR_INVALID_HEADER,
86 InvalidParam = ffi::BLOSC2_ERROR_INVALID_PARAM,
88 FileRead = ffi::BLOSC2_ERROR_FILE_READ,
90 FileWrite = ffi::BLOSC2_ERROR_FILE_WRITE,
92 FileOpen = ffi::BLOSC2_ERROR_FILE_OPEN,
94 NotFound = ffi::BLOSC2_ERROR_NOT_FOUND,
96 RunLength = ffi::BLOSC2_ERROR_RUN_LENGTH,
98 FilterPipeline = ffi::BLOSC2_ERROR_FILTER_PIPELINE,
100 ChunkInsert = ffi::BLOSC2_ERROR_CHUNK_INSERT,
102 ChunkAppend = ffi::BLOSC2_ERROR_CHUNK_APPEND,
104 ChunkUpdate = ffi::BLOSC2_ERROR_CHUNK_UPDATE,
106 TwoGBLimit = ffi::BLOSC2_ERROR_2GB_LIMIT,
108 SchunkCopy = ffi::BLOSC2_ERROR_SCHUNK_COPY,
110 FrameType = ffi::BLOSC2_ERROR_FRAME_TYPE,
112 FileTruncate = ffi::BLOSC2_ERROR_FILE_TRUNCATE,
114 ThreadCreate = ffi::BLOSC2_ERROR_THREAD_CREATE,
116 PostFilter = ffi::BLOSC2_ERROR_POSTFILTER,
118 FrameSpecial = ffi::BLOSC2_ERROR_FRAME_SPECIAL,
120 SchunkSpecial = ffi::BLOSC2_ERROR_SCHUNK_SPECIAL,
122 PluginIO = ffi::BLOSC2_ERROR_PLUGIN_IO,
124 FileRemove = ffi::BLOSC2_ERROR_FILE_REMOVE,
126 NullPointer = ffi::BLOSC2_ERROR_NULL_POINTER,
128 InvalidIndex = ffi::BLOSC2_ERROR_INVALID_INDEX,
130 MetalayerNotFound = ffi::BLOSC2_ERROR_METALAYER_NOT_FOUND,
132}
133
134impl std::fmt::Display for Blosc2Error {
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 write!(f, "{:?}", self)
137 }
138}
139
140impl From<i32> for Blosc2Error {
141 fn from(code: i32) -> Self {
142 match code {
143 ffi::BLOSC2_ERROR_FAILURE => Blosc2Error::Failure,
144 ffi::BLOSC2_ERROR_STREAM => Blosc2Error::Stream,
145 ffi::BLOSC2_ERROR_DATA => Blosc2Error::Data,
146 ffi::BLOSC2_ERROR_MEMORY_ALLOC => Blosc2Error::MemoryAlloc,
147 ffi::BLOSC2_ERROR_READ_BUFFER => Blosc2Error::ReadBuffer,
148 ffi::BLOSC2_ERROR_WRITE_BUFFER => Blosc2Error::WriteBuffer,
149 ffi::BLOSC2_ERROR_CODEC_SUPPORT => Blosc2Error::CodecSupport,
150 ffi::BLOSC2_ERROR_CODEC_PARAM => Blosc2Error::CodecParam,
151 ffi::BLOSC2_ERROR_CODEC_DICT => Blosc2Error::CodecDict,
152 ffi::BLOSC2_ERROR_VERSION_SUPPORT => Blosc2Error::VersionSupport,
153 ffi::BLOSC2_ERROR_INVALID_HEADER => Blosc2Error::InvalidHeader,
154 ffi::BLOSC2_ERROR_INVALID_PARAM => Blosc2Error::InvalidParam,
155 ffi::BLOSC2_ERROR_FILE_READ => Blosc2Error::FileRead,
156 ffi::BLOSC2_ERROR_FILE_WRITE => Blosc2Error::FileWrite,
157 ffi::BLOSC2_ERROR_FILE_OPEN => Blosc2Error::FileOpen,
158 ffi::BLOSC2_ERROR_NOT_FOUND => Blosc2Error::NotFound,
159 ffi::BLOSC2_ERROR_RUN_LENGTH => Blosc2Error::RunLength,
160 ffi::BLOSC2_ERROR_FILTER_PIPELINE => Blosc2Error::FilterPipeline,
161 ffi::BLOSC2_ERROR_CHUNK_INSERT => Blosc2Error::ChunkInsert,
162 ffi::BLOSC2_ERROR_CHUNK_APPEND => Blosc2Error::ChunkAppend,
163 ffi::BLOSC2_ERROR_CHUNK_UPDATE => Blosc2Error::ChunkUpdate,
164 ffi::BLOSC2_ERROR_2GB_LIMIT => Blosc2Error::TwoGBLimit,
165 ffi::BLOSC2_ERROR_SCHUNK_COPY => Blosc2Error::SchunkCopy,
166 ffi::BLOSC2_ERROR_FRAME_TYPE => Blosc2Error::FrameType,
167 ffi::BLOSC2_ERROR_FILE_TRUNCATE => Blosc2Error::FileTruncate,
168 ffi::BLOSC2_ERROR_THREAD_CREATE => Blosc2Error::ThreadCreate,
169 ffi::BLOSC2_ERROR_POSTFILTER => Blosc2Error::PostFilter,
170 ffi::BLOSC2_ERROR_FRAME_SPECIAL => Blosc2Error::FrameSpecial,
171 ffi::BLOSC2_ERROR_SCHUNK_SPECIAL => Blosc2Error::SchunkSpecial,
172 ffi::BLOSC2_ERROR_PLUGIN_IO => Blosc2Error::PluginIO,
173 ffi::BLOSC2_ERROR_FILE_REMOVE => Blosc2Error::FileRemove,
174 ffi::BLOSC2_ERROR_NULL_POINTER => Blosc2Error::NullPointer,
175 ffi::BLOSC2_ERROR_INVALID_INDEX => Blosc2Error::InvalidIndex,
176 ffi::BLOSC2_ERROR_METALAYER_NOT_FOUND => Blosc2Error::MetalayerNotFound,
177 _ => panic!("Error code {} not matched in existing Error codes", code),
178 }
179 }
180}
181
182pub const BUFSIZE: usize = 8196_usize;
184
185#[derive(Copy, Clone)]
187pub enum Filter {
188 NoFilter = ffi::BLOSC_NOFILTER as _,
189 Shuffle = ffi::BLOSC_SHUFFLE as _,
190 BitShuffle = ffi::BLOSC_BITSHUFFLE as _,
191 Delta = ffi::BLOSC_DELTA as _,
192 TruncPrec = ffi::BLOSC_TRUNC_PREC as _,
193 LastFilter = ffi::BLOSC_LAST_FILTER as _,
194 LastRegisteredFilter = ffi::BLOSC_LAST_REGISTERED_FILTER as _,
195}
196
197impl Default for Filter {
198 fn default() -> Self {
199 Filter::Shuffle
200 }
201}
202
203impl ToString for Filter {
204 fn to_string(&self) -> String {
205 match self {
206 Self::NoFilter => "nofilter",
207 Self::Shuffle => "shuffle",
208 Self::BitShuffle => "bitshuffle",
209 Self::Delta => "delta",
210 Self::TruncPrec => "truncprec",
211 Self::LastFilter => "lastfilter",
212 Self::LastRegisteredFilter => "lastregisteredfilter",
213 }
214 .to_string()
215 }
216}
217
218impl<'a> TryFrom<&'a str> for Filter {
219 type Error = Error;
220
221 fn try_from(val: &'a str) -> Result<Self> {
222 match val.to_lowercase().as_str() {
223 "nofilter" => Ok(Filter::NoFilter),
224 "shuffle" => Ok(Filter::Shuffle),
225 "bitshuffle" => Ok(Filter::BitShuffle),
226 "delta" => Ok(Filter::Delta),
227 "trunctprec" => Ok(Filter::TruncPrec),
228 "lastfilter" => Ok(Filter::LastFilter),
229 "lastregisteredfilter" => Ok(Filter::LastRegisteredFilter),
230 _ => Err(Error::from(format!("No matching filter for '{}'", val))),
231 }
232 }
233}
234
235#[derive(Debug, Copy, Clone, PartialEq, Eq)]
237pub enum Codec {
238 BloscLz = ffi::BLOSC_BLOSCLZ as _,
239 LZ4 = ffi::BLOSC_LZ4 as _,
240 LZ4HC = ffi::BLOSC_LZ4HC as _,
241 ZLIB = ffi::BLOSC_ZLIB as _,
242 ZSTD = ffi::BLOSC_ZSTD as _,
243 LastCodec = ffi::BLOSC_LAST_CODEC as _,
244 LastRegisteredCodec = ffi::BLOSC_LAST_REGISTERED_CODEC as _,
245}
246
247impl Codec {
248 #[allow(dead_code)]
249 fn to_name(&self) -> Result<String> {
250 (*self).try_into()
251 }
252 fn to_name_cstring(&self) -> Result<CString> {
253 let mut compname = std::ptr::null();
254 let rc = unsafe { ffi::blosc2_compcode_to_compname(*self as _, &mut compname) };
255 if rc == -1 {
256 return Err(Error::Other(format!("Unsupported Codec {:?}", self)));
257 }
258 unsafe { Ok(CStr::from_ptr(compname as _).to_owned()) }
259 }
260}
261
262impl TryInto<String> for Codec {
263 type Error = Error;
264 fn try_into(self) -> Result<String> {
265 self.clone()
266 .to_name_cstring()?
267 .into_string()
268 .map_err(|e| Error::Other(e.to_string()))
269 }
270}
271impl<'a> TryFrom<&'a str> for Codec {
272 type Error = Error;
273
274 fn try_from(value: &'a str) -> Result<Self> {
275 let compname = CString::new(value).map_err(|e| Error::Other(e.to_string()))?;
276 let compcode = unsafe { ffi::blosc2_compname_to_compcode(compname.as_ptr()) };
277 if compcode == -1 {
278 return Err(Error::from(format!("Compcode {} not recognized", value)));
279 }
280 Codec::try_from(compcode)
281 }
282}
283impl TryFrom<i32> for Codec {
284 type Error = Error;
285
286 fn try_from(compcode: i32) -> Result<Self> {
287 match compcode as _ {
288 ffi::BLOSC_BLOSCLZ => Ok(Codec::BloscLz),
289 ffi::BLOSC_LZ4 => Ok(Codec::LZ4),
290 ffi::BLOSC_LZ4HC => Ok(Codec::LZ4HC),
291 ffi::BLOSC_ZLIB => Ok(Codec::ZLIB),
292 ffi::BLOSC_ZSTD => Ok(Codec::ZSTD),
293 ffi::BLOSC_LAST_CODEC => Ok(Codec::LastCodec),
294 ffi::BLOSC_LAST_REGISTERED_CODEC => Ok(Codec::LastRegisteredCodec),
295 _ => Err(format!("Not match for compcode {}", compcode).into()),
296 }
297 }
298}
299
300impl Default for Codec {
301 fn default() -> Self {
302 Codec::BloscLz
303 }
304}
305
306#[repr(u8)]
308pub enum CLevel {
309 Zero = 0,
310 One = 1,
311 Two = 2,
312 Three = 3,
313 Four = 4,
314 Five = 5,
315 Six = 6,
316 Seven = 7,
317 Eight = 8,
318 Nine = 9,
319}
320
321impl Default for CLevel {
322 fn default() -> Self {
323 CLevel::Nine }
325}
326
327impl TryFrom<usize> for CLevel {
328 type Error = Error;
329
330 fn try_from(val: usize) -> Result<Self> {
331 match val {
332 0 => Ok(CLevel::Zero),
333 1 => Ok(CLevel::One),
334 2 => Ok(CLevel::Two),
335 3 => Ok(CLevel::Three),
336 4 => Ok(CLevel::Four),
337 5 => Ok(CLevel::Five),
338 6 => Ok(CLevel::Six),
339 7 => Ok(CLevel::Seven),
340 8 => Ok(CLevel::Eight),
341 9 => Ok(CLevel::Nine),
342 _ => Err(Error::from(format!(
343 "Compression level must be 0-9, got {}",
344 val
345 ))),
346 }
347 }
348}
349
350pub mod schunk {
351 use std::ffi::CStr;
354 use std::io;
355 use std::path::{Path, PathBuf};
356
357 use super::*;
358
359 pub struct Storage {
371 inner: ffi::blosc2_storage,
372 cparams: CParams,
373 dparams: DParams,
374 }
375
376 impl Default for Storage {
377 fn default() -> Self {
378 let storage = unsafe { ffi::blosc2_get_blosc2_storage_defaults() };
379 Storage {
380 inner: storage,
381 cparams: CParams::default(),
382 dparams: DParams::default(),
383 }
384 }
385 }
386
387 impl Storage {
388 pub fn set_urlpath<S: AsRef<Path>>(mut self, urlpath: S) -> Result<Self> {
391 self.inner.urlpath = CString::new(urlpath.as_ref().to_string_lossy().to_string())
392 .map_err(|e| Error::Other(e.to_string()))?
393 .into_raw();
394 Ok(self)
395 }
396 pub fn get_urlpath(&self) -> Result<Option<&str>> {
407 if self.inner.urlpath.is_null() {
408 return Ok(None);
409 }
410 unsafe {
411 CStr::from_ptr(self.inner.urlpath)
412 .to_str()
413 .map(|v| Some(v))
414 .map_err(|e| Error::Other(e.to_string()))
415 }
416 }
417 pub fn set_contiguous(mut self, contiguous: bool) -> Self {
419 self.inner.contiguous = contiguous;
420 self
421 }
422 pub fn set_cparams(mut self, cparams: CParams) -> Self {
424 self.cparams = cparams;
425 self.inner.cparams = &mut self.cparams.0;
426 self
427 }
428 pub fn get_cparams(&self) -> &CParams {
430 &self.cparams
431 }
432 pub fn set_dparams(mut self, dparams: DParams) -> Self {
434 self.dparams = dparams;
435 self.inner.dparams = &mut self.dparams.0;
436 self
437 }
438 }
439
440 #[derive(Clone)]
468 pub struct Chunk {
469 pub(crate) chunk: Arc<RwLock<*mut u8>>,
470 pub(crate) needs_free: bool,
471 }
472
473 unsafe impl Sync for Chunk {}
474 unsafe impl Send for Chunk {}
475
476 impl TryFrom<Vec<u8>> for Chunk {
477 type Error = Error;
478 #[inline]
479 fn try_from(v: Vec<u8>) -> Result<Self> {
480 Self::from_vec(v)
481 }
482 }
483
484 impl Chunk {
485 pub fn new(chunk: *mut u8, needs_free: bool) -> Self {
488 Self {
489 chunk: Arc::new(RwLock::new(chunk)),
490 needs_free,
491 }
492 }
493
494 #[inline]
506 pub fn from_vec(v: Vec<u8>) -> Result<Self> {
507 let mut v = v;
508 v.shrink_to_fit();
509 if let Err(_) = CompressedBufferInfo::try_from(v.as_slice()) {
510 return Err("Appears this buffer is not a valid blosc2 chunk".into());
511 }
512 let ptr = v.as_mut_ptr();
513 mem::forget(v);
514 Ok(Self::new(ptr as _, true))
515 }
516
517 pub fn into_vec(self) -> Result<Vec<u8>> {
520 let info = self.info()?;
521 let buf =
522 unsafe { Vec::from_raw_parts(*self.chunk.write(), info.cbytes(), info.cbytes()) };
523 if !self.needs_free {
524 return Ok(buf.clone());
525 }
526 Ok(buf)
527 }
528
529 pub fn as_slice(&self) -> Result<&[u8]> {
531 let info = CompressedBufferInfo::try_from(*self.chunk.read() as *const c_void)?;
532 let slice = unsafe { std::slice::from_raw_parts(*self.chunk.read(), info.cbytes()) };
533 Ok(slice)
534 }
535
536 #[inline]
550 pub fn len<T>(&self) -> Result<usize> {
551 CompressedBufferInfo::try_from(*self.chunk.read() as *const c_void)
552 .map(|info| info.nbytes() / mem::size_of::<T>())
553 }
554
555 #[inline]
556 pub fn getitems<T>(&self, offset: usize, n_items: usize) -> Result<Vec<T>> {
557 if self.len::<T>()? < offset + n_items {
558 return Err(format!(
559 "Would be out of bounds. Chunk contains {} elements",
560 self.len::<T>()?
561 )
562 .into());
563 }
564 getitems(self.as_slice()?, offset, n_items)
565 }
566
567 pub fn uninit<T>(cparams: CParams, len: usize) -> Result<Self> {
579 let mut cparams = cparams;
580 if mem::size_of::<T>() != cparams.0.typesize as usize {
581 cparams.0.typesize = mem::size_of::<T>() as _;
582 }
583 let mut dst = Vec::with_capacity(
584 (len * cparams.0.typesize as usize) + ffi::BLOSC_EXTENDED_HEADER_LENGTH as usize,
585 );
586 let nbytes = unsafe {
587 ffi::blosc2_chunk_uninit(
588 cparams.0,
589 len as i32 * cparams.0.typesize,
590 dst.as_mut_ptr() as *mut c_void,
591 dst.capacity() as i32,
592 )
593 };
594 if nbytes < 0 {
595 return Err("Failed to create uninitialized chunk".into());
596 }
597 unsafe { dst.set_len(nbytes as _) };
598 Self::from_vec(dst)
599 }
600
601 pub fn repeatval<T>(cparams: CParams, value: T, len: usize) -> Result<Self> {
614 let mut cparams = cparams;
615 if mem::size_of::<T>() != cparams.0.typesize as usize {
616 cparams.0.typesize = mem::size_of::<T>() as _;
617 }
618 let mut dst = Vec::with_capacity(
619 (len * cparams.0.typesize as usize) + ffi::BLOSC_EXTENDED_HEADER_LENGTH as usize,
620 );
621 let nbytes = unsafe {
622 ffi::blosc2_chunk_repeatval(
623 cparams.0,
624 len as i32 * cparams.0.typesize,
625 dst.as_mut_ptr() as _,
626 dst.capacity() as _,
627 &value as *const T as _,
628 )
629 };
630 if nbytes < 0 {
631 return Err("Failed to create chunk".into());
632 }
633 unsafe { dst.set_len(nbytes as _) };
634 Self::from_vec(dst)
635 }
636
637 pub fn zeros<T>(cparams: CParams, len: usize) -> Result<Self> {
650 let mut cparams = cparams;
651 if mem::size_of::<T>() != cparams.0.typesize as usize {
652 cparams.0.typesize = mem::size_of::<T>() as _;
653 }
654 let mut dst = Vec::with_capacity(
655 (len * cparams.0.typesize as usize) + ffi::BLOSC_EXTENDED_HEADER_LENGTH as usize,
656 );
657
658 let nbytes = unsafe {
659 ffi::blosc2_chunk_zeros(
660 cparams.0,
661 len as i32 * cparams.0.typesize,
662 dst.as_mut_ptr() as _,
663 dst.capacity() as i32,
664 )
665 };
666 if nbytes < 0 {
667 return Err(Error::Blosc2(Blosc2Error::from(nbytes)));
668 }
669 unsafe { dst.set_len(nbytes as usize) };
670 Self::from_vec(dst)
671 }
672
673 pub fn compression_ratio(&self) -> Result<f32> {
685 let info = self.info()?;
686 if info.cbytes() == 0 {
687 return Ok(0f32);
688 }
689 Ok(info.nbytes() as f32 / info.cbytes() as f32)
690 }
691
692 #[inline]
702 pub fn compress<T: 'static>(
703 src: &[T],
704 typesize: Option<usize>,
705 clevel: Option<CLevel>,
706 filter: Option<Filter>,
707 codec: Option<Codec>,
708 ) -> Result<Self> {
709 crate::compress(src, typesize, clevel, filter, codec).map(Self::from_vec)?
710 }
711
712 pub fn decompress<T>(&self) -> Result<Vec<T>> {
729 let slice =
730 unsafe { std::slice::from_raw_parts(*self.chunk.read(), self.info()?.cbytes) };
731 crate::decompress(slice)
732 }
733
734 #[inline]
736 pub fn from_schunk(schunk: &mut SChunk, nchunk: usize) -> Result<Self> {
737 let mut chunk: *mut u8 = std::ptr::null_mut();
738 let mut needs_free: bool = false;
739 let rc = unsafe {
740 ffi::blosc2_schunk_get_chunk(
741 *schunk.0.read(),
742 nchunk as _,
743 &mut chunk as _,
744 &mut needs_free,
745 )
746 };
747 if rc < 0 {
748 return Err(Error::Blosc2(Blosc2Error::from(rc as i32)));
749 }
750 Ok(Self {
751 chunk: Arc::new(RwLock::new(chunk)),
752 needs_free,
753 })
754 }
755
756 pub fn nbytes(&self) -> Result<usize> {
758 self.info().map(|info| info.nbytes)
759 }
760
761 pub fn cbytes(&self) -> Result<usize> {
763 self.info().map(|info| info.cbytes)
764 }
765
766 #[inline]
768 pub fn info(&self) -> Result<CompressedBufferInfo> {
769 let mut nbytes = 0;
770 let mut cbytes = 0;
771 let mut blocksize = 0;
772 let rc = unsafe {
773 ffi::blosc2_cbuffer_sizes(
774 *self.chunk.read() as _,
775 &mut nbytes,
776 &mut cbytes,
777 &mut blocksize,
778 )
779 };
780 if rc < 0 {
781 return Err(Blosc2Error::from(rc).into());
782 }
783 Ok(CompressedBufferInfo {
784 nbytes: nbytes as _,
785 cbytes: cbytes as _,
786 blocksize: blocksize as _,
787 })
788 }
789 }
790
791 impl Drop for Chunk {
792 fn drop(&mut self) {
793 if self.needs_free && Arc::strong_count(&self.chunk) == 1 {
795 unsafe { blosc2_sys::libc::free(*self.chunk.write() as _) };
796 }
797 }
798 }
799
800 #[derive(Clone)]
804 pub struct SChunk(pub(crate) Arc<RwLock<*mut ffi::blosc2_schunk>>);
805
806 unsafe impl Send for SChunk {}
807
808 impl SChunk {
810 pub fn new(storage: Storage) -> Self {
811 let mut storage = storage;
812 let schunk = unsafe { ffi::blosc2_schunk_new(&mut storage.inner) };
813 Self(Arc::new(RwLock::new(schunk)))
814 }
815
816 pub fn copy(&self) -> Self {
817 let schunk =
818 unsafe { ffi::blosc2_schunk_copy(*self.0.read(), (**self.0.read()).storage) };
819 Self(Arc::new(RwLock::new(schunk)))
820 }
821
822 pub fn frame(&self) -> Result<&[u8]> {
823 unsafe {
824 if (**self.0.read()).frame.is_null() {
825 return Err(Error::from("schunk frame is null"));
826 }
827 let len = ffi::blosc2_schunk_frame_len(*self.0.read()) as usize;
828 let buf = std::slice::from_raw_parts((**self.0.read()).frame as _, len);
829 Ok(buf)
830 }
831 }
832
833 #[inline]
834 pub(crate) fn inner(&self) -> &ffi::blosc2_schunk {
835 unsafe { &(**self.0.read()) }
836 }
837
838 #[inline]
839 #[allow(dead_code)]
840 pub(crate) fn inner_mut(&mut self) -> &mut ffi::blosc2_schunk {
841 unsafe { &mut (**self.0.write()) }
842 }
843
844 #[inline]
846 pub fn append_buffer<T>(&mut self, data: &[T]) -> Result<usize> {
847 if data.is_empty() {
848 return Ok(self.inner().nchunks as usize);
849 }
850 let nbytes = mem::size_of::<T>() * data.len();
851 let typesize = self.typesize();
852 if nbytes % self.typesize() != 0 {
853 let msg = format!("Buffer ({nbytes}) not evenly divisible by typesize: {typesize}");
854 return Err(Error::Other(msg));
855 }
856 let nchunks = unsafe {
857 ffi::blosc2_schunk_append_buffer(*self.0.read(), data.as_ptr() as _, nbytes as _)
858 };
859 if nchunks < 0 {
860 return Err(Blosc2Error::from(nchunks as i32).into());
861 }
862 Ok(nchunks as _)
863 }
864
865 #[inline]
867 pub fn decompress_chunk<T>(&mut self, nchunk: usize, dst: &mut [T]) -> Result<usize> {
868 let chunk = Chunk::from_schunk(self, nchunk)?;
869 let info = chunk.info()?;
870 if dst.len() * mem::size_of::<T>() < info.nbytes as usize {
871 let msg = format!(
872 "Not large enough, need {} bytes but got buffer w/ {} bytes of storage",
873 info.nbytes,
874 dst.len() * mem::size_of::<T>()
875 );
876 return Err(msg.into());
877 }
878
879 let ptr = dst.as_mut_ptr() as _;
880 let size = unsafe {
881 ffi::blosc2_schunk_decompress_chunk(
882 *self.0.read(),
883 nchunk as _,
884 ptr,
885 info.nbytes as _,
886 )
887 };
888
889 if size < 0 {
890 return Err(Blosc2Error::from(size).into());
891 } else if size == 0 {
892 let msg = format!("Non-initialized error decompressing chunk '{}'", nchunk);
893 return Err(msg.into());
894 } else {
895 Ok((size / mem::size_of::<T>() as i32) as _)
896 }
897 }
898
899 #[inline]
900 pub fn decompress_chunk_vec<T>(&mut self, nchunk: usize) -> Result<Vec<T>> {
901 let chunk = Chunk::from_schunk(self, nchunk)?;
902 chunk.decompress()
903 }
904
905 pub fn set_slice_buffer(&self, start: usize, stop: usize, buf: &[u8]) -> Result<()> {
907 if stop > self.len() {
908 return Err(Error::from(format!(
909 "`stop`: {} must be less than len: {}",
910 stop,
911 self.len(),
912 )));
913 }
914
915 if buf.len() % self.typesize() != 0 {
916 return Err(Error::from(
917 "Buffer is not evenly divisible by SChunk typesize",
918 ));
919 }
920
921 let rc = unsafe {
922 ffi::blosc2_schunk_set_slice_buffer(
923 *self.0.write(),
924 start as _,
925 stop as _,
926 buf.as_ptr() as *const _ as *mut _,
927 )
928 };
929 if rc != 0 {
930 return Err(Blosc2Error::from(rc).into());
931 }
932 Ok(())
933 }
934
935 pub fn get_slice_buffer(&self, start: usize, stop: usize) -> Result<Vec<u8>> {
939 if stop > self.len() {
940 return Err(Error::from(format!(
941 "Out of bounds. `stop`={}, is more than length={}",
942 stop,
943 self.len()
944 )));
945 }
946 if stop <= start {
947 return Err(Error::from("start must be less than stop"));
948 }
949 let nbytes = (stop - start) * self.typesize();
950 let mut buf = vec![0u8; nbytes];
951 let rc = unsafe {
952 ffi::blosc2_schunk_get_slice_buffer(
953 *self.0.read(),
954 start as _,
955 stop as _,
956 buf.as_mut_ptr() as _,
957 )
958 };
959 if rc != 0 {
960 return Err(Blosc2Error::from(rc).into());
961 }
962 Ok(buf)
963 }
964
965 pub fn get_slice_buffer_as_type<T>(&self, start: usize, stop: usize) -> Result<Vec<T>> {
968 if mem::size_of::<T>() != self.typesize() {
969 return Err(Error::from("Size of T does not match schunk typesize"));
970 }
971 let buf = self.get_slice_buffer(start, stop)?;
972 Ok(unsafe { mem::transmute(buf) })
973 }
974
975 pub fn get_chunk(&self, nchunk: usize) -> Result<Chunk> {
976 let mut needs_free = true;
977 let mut chunk = std::ptr::null_mut();
978 let nbytes = unsafe {
979 ffi::blosc2_schunk_get_chunk(
980 *self.0.read(),
981 nchunk as _,
982 &mut chunk as _,
983 &mut needs_free,
984 )
985 };
986 if nbytes < 0 {
987 Err(Blosc2Error::from(nbytes).into())
988 } else {
989 Ok(Chunk::new(chunk, needs_free))
990 }
991 }
992
993 pub fn into_vec(self) -> Result<Vec<u8>> {
995 if self.is_empty() {
996 return Ok(vec![]);
997 }
998
999 unsafe { ffi::blosc2_schunk_avoid_cframe_free(*self.0.read(), true) };
1000
1001 let mut needs_free = true;
1002 let mut ptr: *mut u8 = std::ptr::null_mut();
1003 let len =
1004 unsafe { ffi::blosc2_schunk_to_buffer(*self.0.read(), &mut ptr, &mut needs_free) };
1005 if len < 0 {
1006 return Err(Blosc2Error::from(len as i32).into());
1007 }
1008
1009 let mut buf = unsafe { Vec::from_raw_parts(ptr, len as _, len as _) };
1010 if !needs_free {
1011 buf = buf.clone(); }
1013 Ok(buf)
1014 }
1015
1016 pub fn from_vec(buf: Vec<u8>) -> Result<Self> {
1019 let mut buf = buf;
1020 let schunk =
1021 unsafe { ffi::blosc2_schunk_from_buffer(buf.as_mut_ptr(), buf.len() as _, false) };
1022 if schunk.is_null() {
1023 return Err(Error::from(
1024 "Failed to get schunk from buffer; might not be valid buffer for schunk",
1025 ));
1026 }
1027 unsafe { ffi::blosc2_schunk_avoid_cframe_free(schunk, true) };
1028 mem::forget(buf); Ok(Self(Arc::new(RwLock::new(schunk))))
1030 }
1031
1032 pub fn is_contiguous(&self) -> bool {
1036 unsafe { (*(self.inner()).storage).contiguous }
1037 }
1038
1039 pub fn typesize(&self) -> usize {
1041 self.inner().typesize as _
1042 }
1043
1044 pub fn compression_ratio(&self) -> f32 {
1046 if self.inner().cbytes == 0 {
1047 return 0f32;
1048 }
1049 self.inner().nbytes as f32 / self.inner().cbytes as f32
1050 }
1051
1052 pub fn n_chunks(&self) -> usize {
1054 self.inner().nchunks as _
1055 }
1056
1057 pub fn chunk_shape(&self) -> usize {
1059 (self.inner().chunksize / self.inner().typesize) as _
1060 }
1061
1062 pub fn blocksize(&self) -> usize {
1064 self.inner().blocksize as _
1065 }
1066
1067 pub fn nbytes(&self) -> usize {
1069 self.inner().nbytes as _
1070 }
1071
1072 pub fn cbytes(&self) -> usize {
1074 self.inner().cbytes as _
1075 }
1076
1077 pub fn path(&self) -> Option<std::path::PathBuf> {
1079 let urlpath_ptr = unsafe { (*(self.inner().storage)).urlpath };
1080 if urlpath_ptr.is_null() {
1081 return None;
1082 }
1083 let urlpath = unsafe { CStr::from_ptr(urlpath_ptr) };
1084 urlpath.to_str().map(PathBuf::from).ok()
1085 }
1086
1087 pub fn len(&self) -> usize {
1089 (self.inner().nbytes / self.inner().typesize as i64) as usize
1090 }
1091
1092 pub fn is_empty(&self) -> bool {
1093 self.len() == 0
1094 }
1095 }
1096
1097 impl Drop for SChunk {
1098 fn drop(&mut self) {
1099 if Arc::strong_count(&self.0) == 1 && !(*self.0.read()).is_null() {
1101 unsafe { ffi::blosc2_schunk_free(*self.0.write()) };
1102 }
1103 }
1104 }
1105
1106 impl io::Write for SChunk {
1107 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1108 self.append_buffer(buf)?;
1109 Ok(buf.len())
1110 }
1111 fn flush(&mut self) -> io::Result<()> {
1112 Ok(())
1113 }
1114 }
1115
1116 pub struct SChunkDecoder<'schunk> {
1125 pub(crate) schunk: &'schunk mut SChunk,
1126 pub(crate) buf: io::Cursor<Vec<u8>>,
1127 pub(crate) nchunk: usize,
1128 }
1129 impl<'schunk> SChunkDecoder<'schunk> {
1130 pub fn new(schunk: &'schunk mut SChunk) -> Self {
1131 Self {
1132 schunk,
1133 buf: io::Cursor::new(vec![]),
1134 nchunk: 0,
1135 }
1136 }
1137 }
1138
1139 impl<'schunk> io::Read for SChunkDecoder<'schunk> {
1140 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
1141 if self.buf.position() as usize == self.buf.get_ref().len() {
1143 if self.nchunk >= self.schunk.n_chunks() {
1144 return Ok(0);
1145 }
1146 self.buf.get_mut().truncate(0);
1147 self.buf.set_position(0);
1148
1149 let chunk = Chunk::from_schunk(self.schunk, self.nchunk)?;
1151 let nbytes = chunk.info()?.nbytes();
1152
1153 if nbytes <= buf.len() {
1154 self.schunk.decompress_chunk(self.nchunk, buf)?;
1155 self.nchunk += 1;
1156 return Ok(nbytes);
1157 } else {
1158 self.buf.get_mut().resize(nbytes as _, 0u8);
1159 let nbytes_written: usize = self
1160 .schunk
1161 .decompress_chunk(self.nchunk, self.buf.get_mut().as_mut_slice())?;
1162
1163 debug_assert_eq!(nbytes_written, nbytes);
1166 }
1167 self.nchunk += 1;
1168 }
1169 self.buf.read(buf)
1170 }
1171 }
1172}
1173
1174pub struct CParams(ffi::blosc2_cparams);
1190
1191impl CParams {
1192 pub fn new<T>() -> Self {
1193 Self::default().set_typesize::<T>()
1194 }
1195 pub fn from_typesize(typesize: usize) -> Self {
1196 let mut cparams = Self::default();
1197 cparams.0.typesize = typesize as _;
1198 cparams
1199 }
1200 pub(crate) fn into_inner(self) -> ffi::blosc2_cparams {
1201 self.0
1202 }
1203 #[allow(dead_code)]
1204 pub(crate) fn inner_ref_mut(&mut self) -> &mut ffi::blosc2_cparams {
1205 &mut self.0
1206 }
1207 pub fn set_codec(mut self, codec: Codec) -> Self {
1211 self.0.compcode = codec as _;
1212 self
1213 }
1214 pub fn set_clevel(mut self, clevel: CLevel) -> Self {
1218 self.0.clevel = clevel as _;
1219 self
1220 }
1221 pub fn set_filter(mut self, filter: Filter) -> Self {
1225 self.0.filters[ffi::BLOSC2_MAX_FILTERS as usize - 1] = filter as _;
1226 self
1227 }
1228 pub fn set_nthreads(mut self, n: usize) -> Self {
1230 self.0.nthreads = n as _;
1231 self
1232 }
1233 pub fn get_nthreads(&self) -> usize {
1235 self.0.nthreads as _
1236 }
1237 pub fn set_typesize<T>(mut self) -> Self {
1239 self.0.typesize = mem::size_of::<T>() as _;
1240 self
1241 }
1242 pub fn get_typesize(&self) -> usize {
1243 self.0.typesize as _
1244 }
1245}
1246
1247impl Default for CParams {
1248 #[inline]
1249 fn default() -> Self {
1250 let cparams = unsafe { ffi::blosc2_get_blosc2_cparams_defaults() };
1251 Self(cparams)
1252 }
1253}
1254
1255impl<T> From<&T> for CParams {
1257 fn from(_: &T) -> Self {
1258 let mut cparams = CParams::default();
1259 cparams.0.typesize = mem::size_of::<T>() as _;
1260 cparams
1261 }
1262}
1263
1264pub struct DParams(pub(crate) ffi::blosc2_dparams);
1277
1278impl DParams {
1279 pub fn set_nthreads(mut self, n: usize) -> Self {
1281 self.0.nthreads = n as _;
1282 self
1283 }
1284 pub fn get_nthreads(&self) -> usize {
1285 self.0.nthreads as _
1286 }
1287}
1288
1289impl Default for DParams {
1290 #[inline]
1291 fn default() -> Self {
1292 let dparams = unsafe { ffi::blosc2_get_blosc2_dparams_defaults() };
1293 Self(dparams)
1294 }
1295}
1296
1297#[derive(Clone)]
1302pub struct Context(pub(crate) *mut ffi::blosc2_context);
1303
1304impl Context {
1305 pub fn get_cparams(&self) -> Result<CParams> {
1316 if self.0.is_null() {
1317 return Err("Context pointer is null".into());
1318 }
1319 let mut cparams = CParams::default();
1320 let rc = unsafe { ffi::blosc2_ctx_get_cparams(self.0, &mut cparams.0) };
1321 if rc < 0 {
1322 return Err(Blosc2Error::from(rc).into());
1323 }
1324 Ok(cparams)
1325 }
1326 pub fn get_dparams(&self) -> Result<DParams> {
1337 if self.0.is_null() {
1338 return Err("Context pointer is null".into());
1339 }
1340 let mut dparams = DParams::default();
1341 let rc = unsafe { ffi::blosc2_ctx_get_dparams(self.0, &mut dparams.0) };
1342 if rc < 0 {
1343 return Err(Blosc2Error::from(rc).into());
1344 }
1345 Ok(dparams)
1346 }
1347}
1348
1349impl From<DParams> for Context {
1350 fn from(dparams: DParams) -> Self {
1351 Self(unsafe { ffi::blosc2_create_dctx(dparams.0) })
1352 }
1353}
1354impl From<CParams> for Context {
1355 fn from(cparams: CParams) -> Self {
1356 Self(unsafe { ffi::blosc2_create_cctx(cparams.0) })
1357 }
1358}
1359
1360impl Default for Context {
1361 fn default() -> Self {
1362 let ctx = unsafe { ffi::blosc2_create_cctx(CParams::default().into_inner()) };
1363 Self(ctx)
1364 }
1365}
1366
1367impl Drop for Context {
1368 fn drop(&mut self) {
1369 if !self.0.is_null() {
1370 unsafe { ffi::blosc2_free_ctx(self.0) };
1371 }
1372 }
1373}
1374
1375#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1378pub struct CompressedBufferInfo {
1379 nbytes: usize,
1381 cbytes: usize,
1383 blocksize: usize,
1386}
1387
1388impl CompressedBufferInfo {
1389 pub fn nbytes(&self) -> usize {
1390 self.nbytes
1391 }
1392 pub fn cbytes(&self) -> usize {
1393 self.cbytes
1394 }
1395 pub fn blocksize(&self) -> usize {
1396 self.blocksize
1397 }
1398}
1399
1400impl<T> TryFrom<&[T]> for CompressedBufferInfo {
1401 type Error = Error;
1402
1403 #[inline]
1404 fn try_from(buf: &[T]) -> Result<Self> {
1405 let mut nbytes = 0i32;
1406 let mut cbytes = 0i32;
1407 let mut blocksize = 0i32;
1408 let code = unsafe {
1409 ffi::blosc2_cbuffer_sizes(
1410 buf.as_ptr() as *const c_void,
1411 &mut nbytes as *mut _,
1412 &mut cbytes as *mut _,
1413 &mut blocksize as *mut _,
1414 )
1415 };
1416 if code < 0 {
1417 return Err(Blosc2Error::from(code).into());
1418 }
1419 Ok(CompressedBufferInfo {
1420 nbytes: nbytes as _,
1421 cbytes: cbytes as _,
1422 blocksize: blocksize as _,
1423 })
1424 }
1425}
1426impl TryFrom<*const c_void> for CompressedBufferInfo {
1427 type Error = Error;
1428
1429 #[inline]
1430 fn try_from(ptr: *const c_void) -> Result<Self> {
1431 let mut nbytes = 0i32;
1432 let mut cbytes = 0i32;
1433 let mut blocksize = 0i32;
1434 let code = unsafe {
1435 ffi::blosc2_cbuffer_sizes(
1436 ptr,
1437 &mut nbytes as *mut _,
1438 &mut cbytes as *mut _,
1439 &mut blocksize as *mut _,
1440 )
1441 };
1442 if code < 0 {
1443 return Err(Blosc2Error::from(code).into());
1444 }
1445 Ok(CompressedBufferInfo {
1446 nbytes: nbytes as _,
1447 cbytes: cbytes as _,
1448 blocksize: blocksize as _,
1449 })
1450 }
1451}
1452
1453#[inline]
1468pub fn getitems<T>(src: &[u8], offset: usize, n_items: usize) -> Result<Vec<T>> {
1469 let mut dst = Vec::with_capacity(n_items);
1470 let nbytes = unsafe {
1471 ffi::blosc2_getitem(
1472 src.as_ptr() as *const c_void,
1473 src.len() as _,
1474 offset as _,
1475 n_items as _,
1476 dst.as_mut_ptr() as *mut c_void,
1477 (n_items * mem::size_of::<T>()) as i32,
1478 )
1479 };
1480 if nbytes < 0 {
1481 return Err(Blosc2Error::from(nbytes).into());
1482 }
1483 unsafe { dst.set_len(nbytes as usize / mem::size_of::<T>()) };
1484 Ok(dst)
1485}
1486
1487#[inline]
1489pub fn list_compressors() -> Result<Vec<Codec>> {
1490 let names = unsafe {
1491 let ptr = ffi::blosc2_list_compressors();
1492 CStr::from_ptr(ptr)
1493 .to_str()
1494 .map(ToString::to_string)
1495 .map_err(|e| Error::Other(e.to_string()))
1496 }?;
1497
1498 let mut compressors = vec![];
1499 for name in names.split(',') {
1500 compressors.push(Codec::try_from(name)?);
1501 }
1502 Ok(compressors)
1503}
1504
1505#[inline]
1508pub fn compress_ctx<T>(src: &[T], ctx: &mut Context) -> Result<Vec<u8>> {
1509 if src.is_empty() {
1510 return Ok(vec![]);
1511 }
1512 let mut dst =
1513 vec![0u8; max_compress_len(src, ctx.get_cparams().ok().map(|c| c.get_typesize()))];
1514 let size = compress_into_ctx(src, &mut dst, ctx)?;
1515 if dst.len() > size {
1516 dst.truncate(size as _);
1517 }
1518 Ok(dst)
1519}
1520
1521#[inline]
1522pub fn compress_into_ctx<T>(src: &[T], dst: &mut [u8], ctx: &mut Context) -> Result<usize> {
1523 if src.is_empty() {
1524 return Ok(0);
1525 }
1526 let size = unsafe {
1527 ffi::blosc2_compress_ctx(
1528 ctx.0,
1529 src.as_ptr() as *const c_void,
1530 (src.len() * ctx.get_cparams()?.get_typesize()) as _,
1531 dst.as_mut_ptr() as *mut c_void,
1532 dst.len() as _,
1533 )
1534 };
1535
1536 if size == 0 {
1537 return Err(format!("Buffer is incompressible").into());
1538 } else if size < 0 {
1539 return Err(Blosc2Error::from(size).into());
1540 }
1541 Ok(size as _)
1542}
1543
1544#[inline(always)]
1546pub fn max_compress_len<T>(src: &[T], typesize: Option<usize>) -> usize {
1547 (src.len() * typesize.unwrap_or_else(|| mem::size_of::<T>()))
1548 + ffi::BLOSC2_MAX_OVERHEAD as usize
1549}
1550
1551pub fn max_compress_len_bytes(len_bytes: usize) -> usize {
1552 len_bytes + ffi::BLOSC2_MAX_OVERHEAD as usize
1553}
1554
1555#[inline]
1556pub fn compress<T: 'static>(
1557 src: &[T],
1558 typesize: Option<usize>,
1559 clevel: Option<CLevel>,
1560 filter: Option<Filter>,
1561 codec: Option<Codec>,
1562) -> Result<Vec<u8>> {
1563 if src.is_empty() {
1564 return Ok(vec![]);
1565 }
1566 let mut dst = Vec::with_capacity(max_compress_len(src, typesize));
1567 let typesize = typesize.unwrap_or_else(|| mem::size_of::<T>());
1568 set_compressor(codec.unwrap_or_default())?;
1569
1570 let multiplier = (&src[0] as &dyn std::any::Any)
1573 .downcast_ref::<u8>()
1574 .map(|_| 1)
1575 .unwrap_or(typesize);
1576
1577 let n_bytes = unsafe {
1578 ffi::blosc2_compress(
1579 clevel.unwrap_or_default() as _,
1580 filter.unwrap_or_default() as _,
1581 typesize as _,
1582 src.as_ptr() as *const c_void,
1583 (src.len() * multiplier) as _,
1584 dst.as_mut_ptr() as *mut c_void,
1585 dst.capacity() as _,
1586 )
1587 };
1588 if n_bytes < 0 {
1589 return Err(Blosc2Error::from(n_bytes).into());
1590 } else if n_bytes == 0 {
1591 return Err("Data is not compressable.".into());
1592 }
1593 unsafe { dst.set_len(n_bytes as _) };
1594 Ok(dst)
1595}
1596
1597#[inline]
1598pub fn compress_into<T: 'static>(
1599 src: &[T],
1600 dst: &mut [u8],
1601 typesize: Option<usize>,
1602 clevel: Option<CLevel>,
1603 filter: Option<Filter>,
1604 codec: Option<Codec>,
1605) -> Result<usize> {
1606 if src.is_empty() {
1607 return Ok(0);
1608 }
1609 let typesize = typesize.unwrap_or_else(|| mem::size_of::<T>());
1610 set_compressor(codec.unwrap_or_default())?;
1611
1612 let multiplier = (&src[0] as &dyn std::any::Any)
1615 .downcast_ref::<u8>()
1616 .map(|_| 1)
1617 .unwrap_or(typesize);
1618
1619 let n_bytes = unsafe {
1620 ffi::blosc2_compress(
1621 clevel.unwrap_or_default() as _,
1622 filter.unwrap_or_default() as _,
1623 typesize as _,
1624 src.as_ptr() as *const c_void,
1625 (src.len() * multiplier) as _,
1626 dst.as_mut_ptr() as *mut c_void,
1627 dst.len() as _,
1628 )
1629 };
1630 if n_bytes < 0 {
1631 return Err(Blosc2Error::from(n_bytes).into());
1632 } else if n_bytes == 0 {
1633 return Err("Data is not compressable.".into());
1634 }
1635
1636 Ok(n_bytes as _)
1637}
1638
1639#[inline]
1640pub fn decompress_ctx<T>(src: &[u8], ctx: &mut Context) -> Result<Vec<T>> {
1641 if src.is_empty() {
1642 return Ok(vec![]);
1643 }
1644 let info = CompressedBufferInfo::try_from(src)?;
1645 let n_elements = info.nbytes as usize / mem::size_of::<T>();
1646 let mut dst = Vec::with_capacity(n_elements);
1647
1648 let n_bytes = unsafe {
1649 ffi::blosc2_decompress_ctx(
1650 ctx.0,
1651 src.as_ptr() as *const c_void,
1652 src.len() as i32,
1653 dst.as_mut_ptr() as *mut c_void,
1654 info.nbytes as _,
1655 )
1656 };
1657
1658 if n_bytes < 0 {
1659 return Err(Blosc2Error::from(n_bytes).into());
1660 }
1661 debug_assert_eq!(n_bytes as usize, info.nbytes);
1662 unsafe { dst.set_len(n_elements) };
1663 Ok(dst)
1664}
1665
1666#[inline]
1667pub fn decompress_into_ctx<T>(src: &[T], dst: &mut [T], ctx: &mut Context) -> Result<usize> {
1668 if src.is_empty() {
1669 return Ok(0);
1670 }
1671 let info = CompressedBufferInfo::try_from(src)?;
1672 let n_bytes = unsafe {
1673 ffi::blosc2_decompress_ctx(
1674 ctx.0,
1675 src.as_ptr() as *const c_void,
1676 src.len() as i32,
1677 dst.as_mut_ptr() as *mut c_void,
1678 info.nbytes as _,
1679 )
1680 };
1681
1682 if n_bytes < 0 {
1683 return Err(Blosc2Error::from(n_bytes).into());
1684 }
1685 debug_assert_eq!(n_bytes as usize, info.nbytes);
1686 Ok(n_bytes as _)
1687}
1688
1689#[inline]
1691pub fn len<T>(src: &[u8]) -> Result<usize> {
1692 let info = CompressedBufferInfo::try_from(src)?;
1693 Ok(info.nbytes() / mem::size_of::<T>())
1694}
1695
1696#[inline]
1697pub fn decompress<T>(src: &[u8]) -> Result<Vec<T>> {
1698 if src.is_empty() {
1699 return Ok(vec![]);
1700 }
1701
1702 let info = CompressedBufferInfo::try_from(src)?;
1705 let n_elements = info.nbytes as usize / mem::size_of::<T>();
1706 let mut dst = Vec::with_capacity(n_elements);
1707
1708 let n_bytes = unsafe {
1709 ffi::blosc2_decompress(
1710 src.as_ptr() as *const c_void,
1711 src.len() as i32,
1712 dst.as_mut_ptr() as *mut c_void,
1713 info.nbytes as _,
1714 )
1715 };
1716
1717 if n_bytes < 0 {
1718 return Err(Blosc2Error::from(n_bytes).into());
1719 }
1720
1721 debug_assert_eq!(n_bytes as usize, info.nbytes);
1722 unsafe { dst.set_len(n_elements) };
1723
1724 Ok(dst)
1725}
1726
1727#[inline]
1728pub fn decompress_into<T>(src: &[u8], dst: &mut [T]) -> Result<usize> {
1729 if src.is_empty() {
1730 return Ok(0);
1731 }
1732 let info = CompressedBufferInfo::try_from(src)?;
1733 let n_bytes = unsafe {
1734 ffi::blosc2_decompress(
1735 src.as_ptr() as *const c_void,
1736 src.len() as i32,
1737 dst.as_mut_ptr() as *mut c_void,
1738 info.nbytes as _,
1739 )
1740 };
1741
1742 if n_bytes < 0 {
1743 return Err(Blosc2Error::from(n_bytes).into());
1744 }
1745 debug_assert_eq!(n_bytes as usize, info.nbytes);
1746 Ok(n_bytes as _)
1747}
1748
1749#[inline]
1750pub fn set_compressor(codec: Codec) -> Result<()> {
1751 let codec_name = codec.to_name_cstring()?;
1752 let rc = unsafe { ffi::blosc1_set_compressor(codec_name.as_ptr()) };
1753 if rc < 0 {
1754 Err(Blosc2Error::from(rc).into())
1755 } else {
1756 Ok(())
1757 }
1758}
1759
1760pub fn set_nthreads(nthreads: usize) -> usize {
1761 let n = unsafe { ffi::blosc2_set_nthreads(nthreads as _) };
1762 n as _
1763}
1764pub fn get_nthreads() -> usize {
1765 unsafe { ffi::blosc2_get_nthreads() as _ }
1766}
1767
1768pub fn get_complib_info(codec: Codec) -> Result<String> {
1770 let mut compname = std::ptr::null();
1771 let mut complib = std::ptr::null_mut();
1772 let mut version = std::ptr::null_mut();
1773
1774 if unsafe { ffi::blosc2_compcode_to_compname(codec as i32, &mut compname) } == -1 {
1775 return Err("Codec not recognized".into());
1776 }
1777 if unsafe { ffi::blosc2_get_complib_info(compname as _, &mut complib, &mut version) } == -1 {
1778 return Err("Codec not supported".into());
1779 }
1780
1781 let info = unsafe {
1782 format!(
1783 "{}: {}",
1784 CString::from_raw(complib)
1785 .into_string()
1786 .map_err(|e| e.to_string())?,
1787 CString::from_raw(version)
1788 .into_string()
1789 .map_err(|e| e.to_string())?
1790 )
1791 };
1792 Ok(info)
1793}
1794
1795pub fn get_version_string() -> Result<String> {
1797 CStr::from_bytes_with_nul(ffi::BLOSC2_VERSION_STRING)
1798 .map_err(|e| e.to_string())?
1799 .to_str()
1800 .map_err(|e| e.to_string().into())
1801 .map(|s| s.to_string())
1802}
1803
1804pub fn init() {
1808 let initd = BLOSC2_INIT_FLAG
1809 .get_or_init(|| Arc::new(Mutex::new(false)))
1810 .clone();
1811 let mut guard = initd.lock();
1812 if !*guard {
1813 unsafe { init_unsafe() };
1814 *guard = true;
1815 }
1816}
1817
1818pub unsafe fn init_unsafe() {
1821 unsafe { ffi::blosc2_init() }
1822}
1823
1824pub fn destroy() {
1828 let initd = BLOSC2_INIT_FLAG
1829 .get_or_init(|| Arc::new(Mutex::new(false)))
1830 .clone();
1831 let mut guard = initd.lock();
1832 if *guard {
1833 unsafe { destroy_unsafe() };
1834 *guard = false;
1835 }
1836}
1837
1838pub unsafe fn destroy_unsafe() {
1841 unsafe { ffi::blosc2_destroy() };
1842}
1843
1844pub struct Blosc2Guard;
1847
1848impl Blosc2Guard {
1849 pub fn get_or_init() -> Arc<Self> {
1852 init();
1853 BLOSC2_GUARD.get_or_init(|| Arc::new(Self {})).clone()
1854 }
1855}
1856
1857impl Drop for Blosc2Guard {
1858 fn drop(&mut self) {
1859 destroy();
1860 }
1861}
1862
1863pub fn free_resources() -> Result<()> {
1868 let ret = unsafe { ffi::blosc2_free_resources() };
1869 if ret != 0 {
1870 Err(Error::Blosc2(Blosc2Error::from(ret)))
1871 } else {
1872 Ok(())
1873 }
1874}
1875
1876#[cfg(test)]
1877mod tests {
1878 use ctor::{ctor, dtor};
1879 use std::io::Cursor;
1880
1881 use super::*;
1882
1883 #[ctor]
1884 fn blosc2_init() {
1885 init();
1886 }
1887
1888 #[dtor]
1889 fn blosc2_destory() {
1890 destroy();
1891 }
1892
1893 #[test]
1894 fn test_compress_ctx() -> Result<()> {
1895 let input = b"some data";
1896 let compressed = compress_ctx(input, &mut Context::from(CParams::from(&input[0])))?;
1897 let decompressed = decompress(&compressed)?;
1898 assert_eq!(input, decompressed.as_slice());
1899 Ok(())
1900 }
1901
1902 #[test]
1903 fn test_compress_into_ctx() -> Result<()> {
1904 let input = b"some data";
1905 let mut compressed = vec![0u8; 100];
1906 let n_bytes = compress_into_ctx(
1907 input,
1908 &mut compressed,
1909 &mut Context::from(CParams::from(&input[0])),
1910 )?;
1911 let decompressed = decompress(&compressed[..n_bytes])?;
1912 assert_eq!(input, decompressed.as_slice());
1913 Ok(())
1914 }
1915
1916 #[test]
1917 fn test_decompress_ctx() -> Result<()> {
1918 let input = b"some data";
1919 let compressed = compress(input, None, None, None, None)?;
1920 let decompressed = decompress_ctx(&compressed, &mut Context::from(DParams::default()))?;
1921 assert_eq!(input, decompressed.as_slice());
1922 Ok(())
1923 }
1924
1925 #[test]
1926 fn test_decompress_into_ctx() -> Result<()> {
1927 let input = b"some data";
1928 let compressed = compress(input, None, None, None, None)?;
1929 let mut decompressed = vec![0u8; input.len()];
1930 let n_bytes = decompress_into_ctx(
1931 &compressed,
1932 &mut decompressed,
1933 &mut Context::from(DParams::default()),
1934 )?;
1935 assert_eq!(n_bytes, input.len());
1936 assert_eq!(input, decompressed.as_slice());
1937 Ok(())
1938 }
1939
1940 #[test]
1941 fn test_basic_roundtrip() -> Result<()> {
1942 let input = b"some data";
1943 let compressed = compress(input, None, None, None, None)?;
1944 let decompressed = decompress(&compressed)?;
1945 assert_eq!(input, decompressed.as_slice());
1946 Ok(())
1947 }
1948
1949 #[test]
1950 fn test_basic_roundtrip_into() -> Result<()> {
1951 let input = b"some data";
1952 let mut compressed = vec![0u8; 100];
1953 let n_bytes = compress_into(input, &mut compressed, None, None, None, None)?;
1954
1955 let mut decompressed = vec![0u8; input.len()];
1956 let n_out = decompress_into(&compressed[..n_bytes], &mut decompressed)?;
1957
1958 assert_eq!(n_out, input.len());
1959 assert_eq!(input, decompressed.as_slice());
1960 Ok(())
1961 }
1962
1963 #[test]
1964 fn test_schunk_basic() -> Result<()> {
1965 let input = b"some data";
1966 let storage = schunk::Storage::default()
1967 .set_contiguous(true)
1968 .set_cparams(CParams::from(&input[0]))
1969 .set_dparams(DParams::default());
1970 let mut schunk = schunk::SChunk::new(storage);
1971
1972 assert!(schunk.is_contiguous());
1973 assert_eq!(schunk.typesize(), 1);
1974 assert!(schunk.path().is_none());
1975
1976 let mut decompressed = vec![0u8; input.len()];
1977
1978 let n = schunk.append_buffer(input)?;
1979 schunk.decompress_chunk(n - 1, &mut decompressed)?;
1980 assert_eq!(input, decompressed.as_slice());
1981
1982 {
1983 let _cloned = schunk.clone();
1985 }
1986 assert_eq!(schunk.n_chunks(), 1);
1987
1988 let v = schunk.into_vec()?;
1990 schunk = schunk::SChunk::from_vec(v)?;
1991 assert_eq!(schunk.n_chunks(), 1);
1992
1993 Ok(())
1994 }
1995
1996 #[test]
1997 fn test_schunk_thread_shared() -> Result<()> {
1998 let input = b"some data";
1999 let storage = schunk::Storage::default()
2000 .set_contiguous(true)
2001 .set_cparams(CParams::from(&input[0]))
2002 .set_dparams(DParams::default());
2003 let mut schunk = schunk::SChunk::new(storage);
2004
2005 schunk.append_buffer(input)?;
2006
2007 let mut schunk2 = schunk.clone();
2008 std::thread::spawn(move || {
2009 assert_eq!(schunk2.n_chunks(), 1);
2010 schunk2.append_buffer(b"more data").unwrap();
2011 })
2012 .join()
2013 .unwrap();
2014
2015 assert_eq!(schunk.n_chunks(), 2);
2016 assert_eq!(
2017 b"some data",
2018 schunk.decompress_chunk_vec(0).unwrap().as_slice()
2019 );
2020 assert_eq!(
2021 b"more data",
2022 schunk.decompress_chunk_vec(1).unwrap().as_slice()
2023 );
2024
2025 Ok(())
2026 }
2027
2028 #[cfg(not(target_os = "windows"))]
2029 #[test]
2030 fn test_schunk_write() -> Result<()> {
2031 let input = std::iter::repeat(b"some data")
2032 .take(BUFSIZE)
2033 .flat_map(|v| v.to_vec())
2034 .collect::<Vec<u8>>();
2035 let storage = schunk::Storage::default()
2036 .set_contiguous(true)
2037 .set_cparams(CParams::from(&input[0]))
2038 .set_dparams(DParams::default());
2039 let mut schunk = schunk::SChunk::new(storage);
2040
2041 let nbytes = std::io::copy(&mut Cursor::new(input.clone()), &mut schunk)
2042 .map_err(|e| Error::Other(e.to_string()))?;
2043 assert_eq!(nbytes as usize, input.len());
2044
2045 let ratio = schunk.compression_ratio();
2046 assert!(84. < ratio);
2047 assert!(86. > ratio);
2048
2049 let mut uncompressed = vec![];
2050 let mut decoder = schunk::SChunkDecoder::new(&mut schunk);
2051 let n = std::io::copy(&mut decoder, &mut uncompressed).unwrap();
2052 assert_eq!(input, uncompressed.as_slice());
2053 assert_eq!(n as usize, input.len());
2054
2055 Ok(())
2056 }
2057
2058 #[cfg(not(target_os = "windows"))]
2059 #[test]
2060 fn test_get_version_string() -> Result<()> {
2061 let version = get_version_string()?;
2062 assert_eq!(&version, "2.15.1");
2063 Ok(())
2064 }
2065
2066 #[cfg(not(target_os = "windows"))]
2067 #[test]
2068 fn test_get_complib_version_string() -> Result<()> {
2069 let info = get_complib_info(Codec::BloscLz)?;
2070 assert_eq!(&info, "BloscLZ: 2.5.3");
2071 Ok(())
2072 }
2073
2074 #[test]
2075 fn test_list_compressors() {
2076 let compressors = list_compressors().unwrap();
2077 for compressor in &[
2078 Codec::BloscLz,
2079 Codec::LZ4,
2080 Codec::LZ4HC,
2081 Codec::ZLIB,
2082 Codec::ZSTD,
2083 ] {
2084 assert!(compressors.contains(compressor));
2085 }
2086 }
2087}