1use rust_openzl_sys as sys;
116use std::ffi::CStr;
117
118#[derive(Debug, thiserror::Error)]
119pub enum Error {
120 #[error("OpenZL error: {code} ({name}){context}")]
121 Report {
122 code: i32,
123 name: String,
124 context: String,
125 },
126}
127
128fn compress_bound(src_size: usize) -> usize {
134 unsafe { sys::openzl_compress_bound(src_size) }
135}
136
137fn report_to_error(r: sys::ZL_Report) -> Error {
139 let code = sys::report_code(r);
140 let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
141 .to_string_lossy()
142 .into_owned();
143 Error::Report {
144 code,
145 name,
146 context: String::new(),
147 }
148}
149
150#[derive(Debug, Clone)]
152pub struct Warning {
153 pub code: i32,
154 pub name: String,
155}
156
157#[derive(Debug, Copy, Clone)]
159pub struct GraphId(sys::ZL_GraphID);
160
161impl GraphId {
162 pub fn is_valid(&self) -> bool {
164 unsafe { sys::ZL_GraphID_isValid(self.0) != 0 }
165 }
166
167 #[allow(dead_code)] pub(crate) fn as_raw(&self) -> sys::ZL_GraphID {
169 self.0
170 }
171
172 #[allow(dead_code)] pub(crate) fn from_raw(id: sys::ZL_GraphID) -> Self {
174 GraphId(id)
175 }
176}
177
178impl PartialEq for GraphId {
179 fn eq(&self, other: &Self) -> bool {
180 self.0.gid == other.0.gid
181 }
182}
183
184impl Eq for GraphId {}
185
186pub mod graphs {
188 use super::*;
189
190 const fn make_graph_id(id: u32) -> GraphId {
191 GraphId(sys::ZL_GraphID { gid: id })
192 }
193
194 pub const STORE: GraphId = make_graph_id(sys::ZL_StandardGraphID::ZL_StandardGraphID_store as u32);
196
197 pub const ZSTD: GraphId = make_graph_id(sys::ZL_StandardGraphID::ZL_StandardGraphID_zstd as u32);
199
200 pub const NUMERIC: GraphId = make_graph_id(sys::ZL_StandardGraphID::ZL_StandardGraphID_select_numeric as u32);
202
203 pub const FIELD_LZ: GraphId = make_graph_id(sys::ZL_StandardGraphID::ZL_StandardGraphID_field_lz as u32);
205
206 pub const FSE: GraphId = make_graph_id(sys::ZL_StandardGraphID::ZL_StandardGraphID_fse as u32);
208
209 pub const HUFFMAN: GraphId = make_graph_id(sys::ZL_StandardGraphID::ZL_StandardGraphID_huffman as u32);
211
212 pub const ENTROPY: GraphId = make_graph_id(sys::ZL_StandardGraphID::ZL_StandardGraphID_entropy as u32);
214
215 pub const BITPACK: GraphId = make_graph_id(sys::ZL_StandardGraphID::ZL_StandardGraphID_bitpack as u32);
217
218 pub const CONSTANT: GraphId = make_graph_id(sys::ZL_StandardGraphID::ZL_StandardGraphID_constant as u32);
220}
221
222pub struct Compressor(*mut sys::ZL_Compressor);
227
228impl Compressor {
229 pub fn new() -> Self {
231 let ptr = unsafe { sys::ZL_Compressor_create() };
232 assert!(!ptr.is_null(), "ZL_Compressor_create returned null");
233 Compressor(ptr)
234 }
235
236 pub fn set_parameter(&mut self, param: sys::ZL_CParam, value: i32) -> Result<(), Error> {
238 let r = unsafe { sys::ZL_Compressor_setParameter(self.0, param, value) };
239 if sys::report_is_error(r) {
240 let code = sys::report_code(r);
241 let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
242 .to_string_lossy()
243 .into_owned();
244 return Err(Error::Report {
245 code,
246 name,
247 context: String::new(),
248 });
249 }
250 Ok(())
251 }
252
253 pub fn warnings(&self) -> Vec<Warning> {
255 let arr = unsafe { sys::ZL_Compressor_getWarnings(self.0) };
256 let slice = unsafe { std::slice::from_raw_parts(arr.errors, arr.size) };
257 slice
258 .iter()
259 .map(|e| {
260 let code = unsafe { sys::openzl_error_get_code(e) };
261 let name = unsafe { CStr::from_ptr(sys::openzl_error_get_name(e)) }
262 .to_string_lossy()
263 .into_owned();
264 Warning { code, name }
265 })
266 .collect()
267 }
268
269 pub(crate) fn as_ptr(&self) -> *const sys::ZL_Compressor {
270 self.0 as *const _
271 }
272
273 pub(crate) fn as_mut_ptr(&mut self) -> *mut sys::ZL_Compressor {
274 self.0
275 }
276}
277
278impl Drop for Compressor {
279 fn drop(&mut self) {
280 unsafe { sys::ZL_Compressor_free(self.0) }
281 }
282}
283
284impl Default for Compressor {
285 fn default() -> Self {
286 Self::new()
287 }
288}
289
290pub trait GraphFn {
299 fn build_graph(&self, compressor: &mut Compressor) -> GraphId;
303}
304
305pub struct ZstdGraph;
307
308impl GraphFn for ZstdGraph {
309 fn build_graph(&self, _compressor: &mut Compressor) -> GraphId {
310 graphs::ZSTD
311 }
312}
313
314pub struct NumericGraph;
316
317impl GraphFn for NumericGraph {
318 fn build_graph(&self, _compressor: &mut Compressor) -> GraphId {
319 graphs::NUMERIC
320 }
321}
322
323pub struct StoreGraph;
325
326impl GraphFn for StoreGraph {
327 fn build_graph(&self, _compressor: &mut Compressor) -> GraphId {
328 graphs::STORE
329 }
330}
331
332pub struct FieldLzGraph;
334
335impl GraphFn for FieldLzGraph {
336 fn build_graph(&self, _compressor: &mut Compressor) -> GraphId {
337 graphs::FIELD_LZ
338 }
339}
340
341#[allow(dead_code)]
343unsafe extern "C" fn graph_fn_trampoline(_compressor: *mut sys::ZL_Compressor) -> sys::ZL_GraphID {
344 graphs::ZSTD.0
348}
349
350pub fn compress_with_graph<G: GraphFn>(src: &[u8], graph: &G) -> Result<Vec<u8>, Error> {
355 let max_size = compress_bound(src.len());
357 let mut dst = vec![0u8; max_size];
358
359 let mut compressor = Compressor::new();
361 let graph_id = graph.build_graph(&mut compressor);
362
363 let graph_fn = make_graph_selector_fn(graph_id);
366
367 let r = unsafe {
368 sys::ZL_compress_usingGraphFn(
369 dst.as_mut_ptr() as *mut _,
370 dst.len(),
371 src.as_ptr() as *const _,
372 src.len(),
373 graph_fn,
374 )
375 };
376
377 if sys::report_is_error(r) {
378 return Err(report_to_error(r));
379 }
380
381 let compressed_size = sys::report_value(r);
382 dst.truncate(compressed_size);
383 Ok(dst)
384}
385
386fn make_graph_selector_fn(graph_id: GraphId) -> sys::ZL_GraphFn {
388 match graph_id.0.gid {
390 id if id == graphs::ZSTD.0.gid => Some(zstd_graph_callback),
391 id if id == graphs::NUMERIC.0.gid => Some(numeric_graph_callback),
392 id if id == graphs::STORE.0.gid => Some(store_graph_callback),
393 id if id == graphs::FIELD_LZ.0.gid => Some(field_lz_graph_callback),
394 _ => {
395 Some(zstd_graph_callback)
398 }
399 }
400}
401
402unsafe extern "C" fn zstd_graph_callback(compressor: *mut sys::ZL_Compressor) -> sys::ZL_GraphID {
404 sys::ZL_Compressor_setParameter(compressor, sys::ZL_CParam::ZL_CParam_formatVersion, 21);
406 graphs::ZSTD.0
407}
408
409unsafe extern "C" fn numeric_graph_callback(compressor: *mut sys::ZL_Compressor) -> sys::ZL_GraphID {
410 sys::ZL_Compressor_setParameter(compressor, sys::ZL_CParam::ZL_CParam_formatVersion, 21);
412 graphs::NUMERIC.0
413}
414
415unsafe extern "C" fn store_graph_callback(compressor: *mut sys::ZL_Compressor) -> sys::ZL_GraphID {
416 sys::ZL_Compressor_setParameter(compressor, sys::ZL_CParam::ZL_CParam_formatVersion, 21);
418 graphs::STORE.0
419}
420
421unsafe extern "C" fn field_lz_graph_callback(compressor: *mut sys::ZL_Compressor) -> sys::ZL_GraphID {
422 sys::ZL_Compressor_setParameter(compressor, sys::ZL_CParam::ZL_CParam_formatVersion, 21);
424 graphs::FIELD_LZ.0
425}
426
427pub struct TypedRef<'a> {
437 ptr: *mut sys::ZL_TypedRef,
438 _marker: std::marker::PhantomData<&'a [u8]>,
439}
440
441impl<'a> TypedRef<'a> {
442 pub fn serial(data: &'a [u8]) -> Self {
444 let ptr = unsafe {
445 sys::ZL_TypedRef_createSerial(data.as_ptr() as *const _, data.len())
446 };
447 assert!(!ptr.is_null(), "ZL_TypedRef_createSerial returned null");
448 TypedRef {
449 ptr,
450 _marker: std::marker::PhantomData,
451 }
452 }
453
454 pub fn numeric<T: Copy>(data: &'a [T]) -> Result<Self, Error> {
458 let width = std::mem::size_of::<T>();
459 if !matches!(width, 1 | 2 | 4 | 8) {
460 return Err(Error::Report {
461 code: -1,
462 name: "Invalid numeric type".into(),
463 context: format!("\nElement size must be 1, 2, 4, or 8 bytes, got {width}"),
464 });
465 }
466 let ptr = unsafe {
467 sys::ZL_TypedRef_createNumeric(
468 data.as_ptr() as *const _,
469 width,
470 data.len(),
471 )
472 };
473 assert!(!ptr.is_null(), "ZL_TypedRef_createNumeric returned null");
474 Ok(TypedRef {
475 ptr,
476 _marker: std::marker::PhantomData,
477 })
478 }
479
480 pub fn strings(flat: &'a [u8], lens: &'a [u32]) -> Self {
485 let ptr = unsafe {
486 sys::ZL_TypedRef_createString(
487 flat.as_ptr() as *const _,
488 flat.len(),
489 lens.as_ptr(),
490 lens.len(),
491 )
492 };
493 assert!(!ptr.is_null(), "ZL_TypedRef_createString returned null");
494 TypedRef {
495 ptr,
496 _marker: std::marker::PhantomData,
497 }
498 }
499
500 pub fn structs(bytes: &'a [u8], width: usize, count: usize) -> Result<Self, Error> {
506 if width == 0 || count == 0 {
507 return Err(Error::Report {
508 code: -1,
509 name: "Invalid struct parameters".into(),
510 context: "\nWidth and count must be non-zero".into(),
511 });
512 }
513 if bytes.len() != width * count {
514 return Err(Error::Report {
515 code: -1,
516 name: "Invalid struct buffer size".into(),
517 context: format!("\nExpected {} bytes (width={width} * count={count}), got {}", width * count, bytes.len()),
518 });
519 }
520 let ptr = unsafe {
521 sys::ZL_TypedRef_createStruct(
522 bytes.as_ptr() as *const _,
523 width,
524 count,
525 )
526 };
527 assert!(!ptr.is_null(), "ZL_TypedRef_createStruct returned null");
528 Ok(TypedRef {
529 ptr,
530 _marker: std::marker::PhantomData,
531 })
532 }
533
534 pub(crate) fn as_ptr(&self) -> *const sys::ZL_TypedRef {
535 self.ptr as *const _
536 }
537}
538
539impl Drop for TypedRef<'_> {
540 fn drop(&mut self) {
541 unsafe { sys::ZL_TypedRef_free(self.ptr) }
542 }
543}
544
545pub struct TypedBuffer {
549 ptr: *mut sys::ZL_TypedBuffer,
550}
551
552impl TypedBuffer {
553 pub fn new() -> Self {
555 let ptr = unsafe { sys::ZL_TypedBuffer_create() };
556 assert!(!ptr.is_null(), "ZL_TypedBuffer_create returned null");
557 TypedBuffer { ptr }
558 }
559
560 pub fn data_type(&self) -> sys::ZL_Type {
562 unsafe { sys::ZL_TypedBuffer_type(self.ptr) }
563 }
564
565 pub fn byte_size(&self) -> usize {
567 unsafe { sys::ZL_TypedBuffer_byteSize(self.ptr) }
568 }
569
570 pub fn num_elts(&self) -> usize {
572 unsafe { sys::ZL_TypedBuffer_numElts(self.ptr) }
573 }
574
575 pub fn elt_width(&self) -> usize {
577 unsafe { sys::ZL_TypedBuffer_eltWidth(self.ptr) }
578 }
579
580 pub fn as_bytes(&self) -> &[u8] {
582 let ptr = unsafe { sys::ZL_TypedBuffer_rPtr(self.ptr) };
583 let len = self.byte_size();
584 if ptr.is_null() || len == 0 {
585 &[]
586 } else {
587 unsafe { std::slice::from_raw_parts(ptr as *const u8, len) }
588 }
589 }
590
591 pub fn as_numeric<T: Copy>(&self) -> Option<&[T]> {
598 if self.data_type() != sys::ZL_Type::ZL_Type_numeric {
600 return None;
601 }
602
603 let width = std::mem::size_of::<T>();
604 if self.elt_width() != width {
605 return None;
606 }
607
608 let ptr = unsafe { sys::ZL_TypedBuffer_rPtr(self.ptr) };
609 if ptr.is_null() {
610 return None;
611 }
612
613 if (ptr as usize) % std::mem::align_of::<T>() != 0 {
615 return None;
616 }
617
618 let len = self.num_elts();
619 if len == 0 {
620 return Some(&[]);
621 }
622
623 Some(unsafe { std::slice::from_raw_parts(ptr as *const T, len) })
624 }
625
626 pub fn string_lens(&self) -> Option<&[u32]> {
630 if self.data_type() != sys::ZL_Type::ZL_Type_string {
631 return None;
632 }
633
634 let ptr = unsafe { sys::ZL_TypedBuffer_rStringLens(self.ptr) };
635 if ptr.is_null() {
636 return None;
637 }
638
639 let len = self.num_elts();
640 if len == 0 {
641 return Some(&[]);
642 }
643
644 Some(unsafe { std::slice::from_raw_parts(ptr, len) })
645 }
646
647 pub(crate) fn as_mut_ptr(&mut self) -> *mut sys::ZL_TypedBuffer {
648 self.ptr
649 }
650}
651
652impl Drop for TypedBuffer {
653 fn drop(&mut self) {
654 unsafe { sys::ZL_TypedBuffer_free(self.ptr) }
655 }
656}
657
658impl Default for TypedBuffer {
659 fn default() -> Self {
660 Self::new()
661 }
662}
663
664fn error_from_report_with_ctx(code: i32, name: String, ctx_str: Option<&CStr>) -> Error {
665 let context = match ctx_str.and_then(|s| s.to_str().ok()) {
666 Some(s) if !s.is_empty() => format!("\n{s}"),
667 _ => String::new(),
668 };
669 Error::Report { code, name, context }
670}
671
672pub struct CCtx(*mut sys::ZL_CCtx);
673impl CCtx {
674 pub fn new() -> Self {
675 let ptr = unsafe { sys::ZL_CCtx_create() };
676 assert!(!ptr.is_null(), "ZL_CCtx_create returned null");
677 CCtx(ptr)
678 }
679 pub fn set_parameter(&mut self, p: sys::ZL_CParam, v: i32) -> Result<(), Error> {
680 let r = unsafe { sys::ZL_CCtx_setParameter(self.0, p, v) };
681 if sys::report_is_error(r) {
682 let code = sys::report_code(r);
683 let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
684 .to_string_lossy()
685 .into_owned();
686 let ctx = unsafe { CStr::from_ptr(sys::openzl_cctx_error_context(self.0, r)) };
687 return Err(error_from_report_with_ctx(code, name, Some(&ctx)));
688 }
689 Ok(())
690 }
691
692 pub fn ref_compressor(&mut self, compressor: &Compressor) -> Result<(), Error> {
700 let r = unsafe { sys::ZL_CCtx_refCompressor(self.0, compressor.as_ptr()) };
701 if sys::report_is_error(r) {
702 let code = sys::report_code(r);
703 let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
704 .to_string_lossy()
705 .into_owned();
706 let ctx = unsafe { CStr::from_ptr(sys::openzl_cctx_error_context(self.0, r)) };
707 return Err(error_from_report_with_ctx(code, name, Some(&ctx)));
708 }
709 Ok(())
710 }
711
712 pub fn warnings(&self) -> Vec<Warning> {
714 let arr = unsafe { sys::openzl_cctx_get_warnings(self.0) };
715 let slice = unsafe { std::slice::from_raw_parts(arr.errors, arr.size) };
716 slice
717 .iter()
718 .map(|e| {
719 let code = unsafe { sys::openzl_error_get_code(e) };
720 let name = unsafe { CStr::from_ptr(sys::openzl_error_get_name(e)) }
721 .to_string_lossy()
722 .into_owned();
723 Warning { code, name }
724 })
725 .collect()
726 }
727
728 pub fn compress_typed_ref(&mut self, input: &TypedRef, dst: &mut [u8]) -> Result<usize, Error> {
730 let r = unsafe {
731 sys::ZL_CCtx_compressTypedRef(
732 self.0,
733 dst.as_mut_ptr() as *mut _,
734 dst.len(),
735 input.as_ptr(),
736 )
737 };
738 if sys::report_is_error(r) {
739 let code = sys::report_code(r);
740 let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
741 .to_string_lossy()
742 .into_owned();
743 let ctx = unsafe { CStr::from_ptr(sys::openzl_cctx_error_context(self.0, r)) };
744 return Err(error_from_report_with_ctx(code, name, Some(&ctx)));
745 }
746 Ok(sys::report_value(r))
747 }
748
749 pub fn compress_multi_typed_ref(&mut self, inputs: &[&TypedRef], dst: &mut [u8]) -> Result<usize, Error> {
751 let mut ptrs: Vec<*const sys::ZL_TypedRef> = inputs.iter().map(|tr| tr.as_ptr()).collect();
752 let r = unsafe {
753 sys::ZL_CCtx_compressMultiTypedRef(
754 self.0,
755 dst.as_mut_ptr() as *mut _,
756 dst.len(),
757 ptrs.as_mut_ptr() as *mut _,
758 ptrs.len(),
759 )
760 };
761 if sys::report_is_error(r) {
762 let code = sys::report_code(r);
763 let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
764 .to_string_lossy()
765 .into_owned();
766 let ctx = unsafe { CStr::from_ptr(sys::openzl_cctx_error_context(self.0, r)) };
767 return Err(error_from_report_with_ctx(code, name, Some(&ctx)));
768 }
769 Ok(sys::report_value(r))
770 }
771}
772impl Drop for CCtx { fn drop(&mut self) { unsafe { sys::ZL_CCtx_free(self.0) } } }
773
774pub struct DCtx(*mut sys::ZL_DCtx);
775impl DCtx {
776 pub fn new() -> Self {
777 let p = unsafe { sys::ZL_DCtx_create() };
778 assert!(!p.is_null());
779 DCtx(p)
780 }
781
782 pub fn warnings(&self) -> Vec<Warning> {
784 let arr = unsafe { sys::openzl_dctx_get_warnings(self.0) };
785 let slice = unsafe { std::slice::from_raw_parts(arr.errors, arr.size) };
786 slice
787 .iter()
788 .map(|e| {
789 let code = unsafe { sys::openzl_error_get_code(e) };
790 let name = unsafe { CStr::from_ptr(sys::openzl_error_get_name(e)) }
791 .to_string_lossy()
792 .into_owned();
793 Warning { code, name }
794 })
795 .collect()
796 }
797
798 pub fn decompress_typed_buffer(&mut self, compressed: &[u8], output: &mut TypedBuffer) -> Result<usize, Error> {
800 let r = unsafe {
801 sys::ZL_DCtx_decompressTBuffer(
802 self.0,
803 output.as_mut_ptr(),
804 compressed.as_ptr() as *const _,
805 compressed.len(),
806 )
807 };
808 if sys::report_is_error(r) {
809 let code = sys::report_code(r);
810 let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
811 .to_string_lossy()
812 .into_owned();
813 let ctx = unsafe { CStr::from_ptr(sys::openzl_dctx_error_context(self.0, r)) };
814 return Err(error_from_report_with_ctx(code, name, Some(&ctx)));
815 }
816 Ok(sys::report_value(r))
817 }
818
819 pub fn decompress_multi_typed_buffer(&mut self, compressed: &[u8], outputs: &mut [&mut TypedBuffer]) -> Result<usize, Error> {
821 let mut ptrs: Vec<*mut sys::ZL_TypedBuffer> = outputs.iter_mut().map(|tb| tb.as_mut_ptr()).collect();
822 let r = unsafe {
823 sys::ZL_DCtx_decompressMultiTBuffer(
824 self.0,
825 ptrs.as_mut_ptr(),
826 ptrs.len(),
827 compressed.as_ptr() as *const _,
828 compressed.len(),
829 )
830 };
831 if sys::report_is_error(r) {
832 let code = sys::report_code(r);
833 let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
834 .to_string_lossy()
835 .into_owned();
836 let ctx = unsafe { CStr::from_ptr(sys::openzl_dctx_error_context(self.0, r)) };
837 return Err(error_from_report_with_ctx(code, name, Some(&ctx)));
838 }
839 Ok(sys::report_value(r))
840 }
841}
842impl Drop for DCtx { fn drop(&mut self) { unsafe { sys::ZL_DCtx_free(self.0) } } }
843
844pub fn compress_serial(src: &[u8]) -> Result<Vec<u8>, Error> {
845 let mut cctx = CCtx::new();
846 cctx.set_parameter(sys::ZL_CParam::ZL_CParam_formatVersion, 21)?;
848 let cap = unsafe { sys::openzl_compress_bound(src.len()) };
850 let mut dst = vec![0u8; cap];
851 let r = unsafe {
852 sys::ZL_CCtx_compress(
853 cctx.0,
854 dst.as_mut_ptr() as *mut _,
855 dst.len(),
856 src.as_ptr() as *const _,
857 src.len(),
858 )
859 };
860 if sys::report_is_error(r) {
861 let code = sys::report_code(r);
862 let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
863 .to_string_lossy()
864 .into_owned();
865 let ctx = unsafe { CStr::from_ptr(sys::openzl_cctx_error_context(cctx.0, r)) };
866 return Err(error_from_report_with_ctx(code, name, Some(&ctx)));
867 }
868 let n = sys::report_value(r) as usize;
869 dst.truncate(n);
870 Ok(dst)
871}
872
873pub fn compress_typed_ref(input: &TypedRef) -> Result<Vec<u8>, Error> {
879 let mut cctx = CCtx::new();
880 cctx.set_parameter(sys::ZL_CParam::ZL_CParam_formatVersion, 21)?;
882
883 let mut compressor = Compressor::new();
885 compressor.set_parameter(sys::ZL_CParam::ZL_CParam_formatVersion, 21)?;
886
887 let r = unsafe {
889 sys::ZL_Compressor_initUsingGraphFn(compressor.as_mut_ptr(), Some(zstd_graph_callback))
890 };
891 if sys::report_is_error(r) {
892 return Err(report_to_error(r));
893 }
894
895 cctx.ref_compressor(&compressor)?;
897
898 let cap = 1024 * 1024; let mut dst = vec![0u8; cap];
902
903 let n = cctx.compress_typed_ref(input, &mut dst)?;
904 dst.truncate(n);
905 Ok(dst)
906}
907
908pub fn compress_multi_typed_ref(inputs: &[&TypedRef]) -> Result<Vec<u8>, Error> {
913 let mut cctx = CCtx::new();
914 cctx.set_parameter(sys::ZL_CParam::ZL_CParam_formatVersion, 21)?;
916
917 let mut compressor = Compressor::new();
919 compressor.set_parameter(sys::ZL_CParam::ZL_CParam_formatVersion, 21)?;
920
921 let r = unsafe {
922 sys::ZL_Compressor_initUsingGraphFn(compressor.as_mut_ptr(), Some(zstd_graph_callback))
923 };
924 if sys::report_is_error(r) {
925 return Err(report_to_error(r));
926 }
927
928 cctx.ref_compressor(&compressor)?;
929
930 let cap = 1024 * 1024; let mut dst = vec![0u8; cap];
933
934 let n = cctx.compress_multi_typed_ref(inputs, &mut dst)?;
935 dst.truncate(n);
936 Ok(dst)
937}
938
939pub fn decompress_serial(src: &[u8]) -> Result<Vec<u8>, Error> {
940 let rsize = unsafe { sys::ZL_getDecompressedSize(src.as_ptr() as *const _, src.len()) };
942 if sys::report_is_error(rsize) {
943 let code = sys::report_code(rsize);
944 let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
945 .to_string_lossy()
946 .into_owned();
947 return Err(error_from_report_with_ctx(code, name, None));
948 }
949 let mut dst = vec![0u8; sys::report_value(rsize) as usize];
950 let r = unsafe { sys::ZL_decompress(dst.as_mut_ptr() as *mut _, dst.len(), src.as_ptr() as *const _, src.len()) };
951 if sys::report_is_error(r) {
952 let code = sys::report_code(r);
953 let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
954 .to_string_lossy()
955 .into_owned();
956 return Err(error_from_report_with_ctx(code, name, None));
958 }
959 let n = sys::report_value(r) as usize;
960 dst.truncate(n);
961 Ok(dst)
962}
963
964pub fn decompress_typed_buffer(compressed: &[u8]) -> Result<TypedBuffer, Error> {
966 let mut dctx = DCtx::new();
967 let mut output = TypedBuffer::new();
968 dctx.decompress_typed_buffer(compressed, &mut output)?;
969 Ok(output)
970}
971
972pub fn compress_numeric<T: Copy>(data: &[T]) -> Result<Vec<u8>, Error> {
987 let width = std::mem::size_of::<T>();
989 if !matches!(width, 1 | 2 | 4 | 8) {
990 return Err(Error::Report {
991 code: -1,
992 name: "Invalid numeric type".into(),
993 context: format!("\nElement size must be 1, 2, 4, or 8 bytes, got {width}"),
994 });
995 }
996
997 let tref = TypedRef::numeric(data)?;
999
1000 let mut cctx = CCtx::new();
1002 cctx.set_parameter(sys::ZL_CParam::ZL_CParam_formatVersion, 21)?;
1003
1004 let mut compressor = Compressor::new();
1005 compressor.set_parameter(sys::ZL_CParam::ZL_CParam_formatVersion, 21)?;
1006
1007 let r = unsafe {
1009 sys::ZL_Compressor_initUsingGraphFn(compressor.as_mut_ptr(), Some(numeric_graph_callback))
1010 };
1011 if sys::report_is_error(r) {
1012 return Err(report_to_error(r));
1013 }
1014
1015 cctx.ref_compressor(&compressor)?;
1016
1017 let cap = compress_bound(data.len() * width);
1019 let mut dst = vec![0u8; cap];
1020
1021 let n = cctx.compress_typed_ref(&tref, &mut dst)?;
1022 dst.truncate(n);
1023 Ok(dst)
1024}
1025
1026pub fn decompress_numeric<T: Copy>(compressed: &[u8]) -> Result<Vec<T>, Error> {
1039 let width = std::mem::size_of::<T>();
1040 if !matches!(width, 1 | 2 | 4 | 8) {
1041 return Err(Error::Report {
1042 code: -1,
1043 name: "Invalid numeric type".into(),
1044 context: format!("\nElement size must be 1, 2, 4, or 8 bytes, got {width}"),
1045 });
1046 }
1047
1048 let tbuf = decompress_typed_buffer(compressed)?;
1050
1051 if tbuf.data_type() != sys::ZL_Type::ZL_Type_numeric {
1053 return Err(Error::Report {
1054 code: -1,
1055 name: "Type mismatch".into(),
1056 context: format!("\nExpected numeric type, got {:?}", tbuf.data_type()),
1057 });
1058 }
1059
1060 if tbuf.elt_width() != width {
1062 return Err(Error::Report {
1063 code: -1,
1064 name: "Width mismatch".into(),
1065 context: format!(
1066 "\nExpected element width {}, got {}",
1067 width,
1068 tbuf.elt_width()
1069 ),
1070 });
1071 }
1072
1073 let slice = tbuf.as_numeric::<T>().ok_or_else(|| Error::Report {
1075 code: -1,
1076 name: "Failed to extract numeric data".into(),
1077 context: "\nAlignment or type mismatch".into(),
1078 })?;
1079
1080 Ok(slice.to_vec())
1081}