use rust_openzl_sys as sys;
use std::ffi::CStr;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("OpenZL error: {code} ({name}){context}")]
Report {
code: i32,
name: String,
context: String,
},
}
fn compress_bound(src_size: usize) -> usize {
unsafe { sys::openzl_compress_bound(src_size) }
}
fn report_to_error(r: sys::ZL_Report) -> Error {
let code = sys::report_code(r);
let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
.to_string_lossy()
.into_owned();
Error::Report {
code,
name,
context: String::new(),
}
}
#[derive(Debug, Clone)]
pub struct Warning {
pub code: i32,
pub name: String,
}
#[derive(Debug, Copy, Clone)]
pub struct GraphId(sys::ZL_GraphID);
impl GraphId {
pub fn is_valid(&self) -> bool {
unsafe { sys::ZL_GraphID_isValid(self.0) != 0 }
}
#[allow(dead_code)] pub(crate) fn as_raw(&self) -> sys::ZL_GraphID {
self.0
}
#[allow(dead_code)] pub(crate) fn from_raw(id: sys::ZL_GraphID) -> Self {
GraphId(id)
}
}
impl PartialEq for GraphId {
fn eq(&self, other: &Self) -> bool {
self.0.gid == other.0.gid
}
}
impl Eq for GraphId {}
pub mod graphs {
use super::*;
const fn make_graph_id(id: u32) -> GraphId {
GraphId(sys::ZL_GraphID { gid: id })
}
pub const STORE: GraphId = make_graph_id(sys::ZL_StandardGraphID::ZL_StandardGraphID_store as u32);
pub const ZSTD: GraphId = make_graph_id(sys::ZL_StandardGraphID::ZL_StandardGraphID_zstd as u32);
pub const NUMERIC: GraphId = make_graph_id(sys::ZL_StandardGraphID::ZL_StandardGraphID_select_numeric as u32);
pub const FIELD_LZ: GraphId = make_graph_id(sys::ZL_StandardGraphID::ZL_StandardGraphID_field_lz as u32);
pub const FSE: GraphId = make_graph_id(sys::ZL_StandardGraphID::ZL_StandardGraphID_fse as u32);
pub const HUFFMAN: GraphId = make_graph_id(sys::ZL_StandardGraphID::ZL_StandardGraphID_huffman as u32);
pub const ENTROPY: GraphId = make_graph_id(sys::ZL_StandardGraphID::ZL_StandardGraphID_entropy as u32);
pub const BITPACK: GraphId = make_graph_id(sys::ZL_StandardGraphID::ZL_StandardGraphID_bitpack as u32);
pub const CONSTANT: GraphId = make_graph_id(sys::ZL_StandardGraphID::ZL_StandardGraphID_constant as u32);
}
pub struct Compressor(*mut sys::ZL_Compressor);
impl Compressor {
pub fn new() -> Self {
let ptr = unsafe { sys::ZL_Compressor_create() };
assert!(!ptr.is_null(), "ZL_Compressor_create returned null");
Compressor(ptr)
}
pub fn set_parameter(&mut self, param: sys::ZL_CParam, value: i32) -> Result<(), Error> {
let r = unsafe { sys::ZL_Compressor_setParameter(self.0, param, value) };
if sys::report_is_error(r) {
let code = sys::report_code(r);
let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
.to_string_lossy()
.into_owned();
return Err(Error::Report {
code,
name,
context: String::new(),
});
}
Ok(())
}
pub fn warnings(&self) -> Vec<Warning> {
let arr = unsafe { sys::ZL_Compressor_getWarnings(self.0) };
let slice = unsafe { std::slice::from_raw_parts(arr.errors, arr.size) };
slice
.iter()
.map(|e| {
let code = unsafe { sys::openzl_error_get_code(e) };
let name = unsafe { CStr::from_ptr(sys::openzl_error_get_name(e)) }
.to_string_lossy()
.into_owned();
Warning { code, name }
})
.collect()
}
pub(crate) fn as_ptr(&self) -> *const sys::ZL_Compressor {
self.0 as *const _
}
pub(crate) fn as_mut_ptr(&mut self) -> *mut sys::ZL_Compressor {
self.0
}
}
impl Drop for Compressor {
fn drop(&mut self) {
unsafe { sys::ZL_Compressor_free(self.0) }
}
}
impl Default for Compressor {
fn default() -> Self {
Self::new()
}
}
pub trait GraphFn {
fn build_graph(&self, compressor: &mut Compressor) -> GraphId;
}
pub struct ZstdGraph;
impl GraphFn for ZstdGraph {
fn build_graph(&self, _compressor: &mut Compressor) -> GraphId {
graphs::ZSTD
}
}
pub struct NumericGraph;
impl GraphFn for NumericGraph {
fn build_graph(&self, _compressor: &mut Compressor) -> GraphId {
graphs::NUMERIC
}
}
pub struct StoreGraph;
impl GraphFn for StoreGraph {
fn build_graph(&self, _compressor: &mut Compressor) -> GraphId {
graphs::STORE
}
}
pub struct FieldLzGraph;
impl GraphFn for FieldLzGraph {
fn build_graph(&self, _compressor: &mut Compressor) -> GraphId {
graphs::FIELD_LZ
}
}
#[allow(dead_code)]
unsafe extern "C" fn graph_fn_trampoline(_compressor: *mut sys::ZL_Compressor) -> sys::ZL_GraphID {
graphs::ZSTD.0
}
pub fn compress_with_graph<G: GraphFn>(src: &[u8], graph: &G) -> Result<Vec<u8>, Error> {
let max_size = compress_bound(src.len());
let mut dst = vec![0u8; max_size];
let mut compressor = Compressor::new();
let graph_id = graph.build_graph(&mut compressor);
let graph_fn = make_graph_selector_fn(graph_id);
let r = unsafe {
sys::ZL_compress_usingGraphFn(
dst.as_mut_ptr() as *mut _,
dst.len(),
src.as_ptr() as *const _,
src.len(),
graph_fn,
)
};
if sys::report_is_error(r) {
return Err(report_to_error(r));
}
let compressed_size = sys::report_value(r);
dst.truncate(compressed_size);
Ok(dst)
}
fn make_graph_selector_fn(graph_id: GraphId) -> sys::ZL_GraphFn {
match graph_id.0.gid {
id if id == graphs::ZSTD.0.gid => Some(zstd_graph_callback),
id if id == graphs::NUMERIC.0.gid => Some(numeric_graph_callback),
id if id == graphs::STORE.0.gid => Some(store_graph_callback),
id if id == graphs::FIELD_LZ.0.gid => Some(field_lz_graph_callback),
_ => {
Some(zstd_graph_callback)
}
}
}
unsafe extern "C" fn zstd_graph_callback(compressor: *mut sys::ZL_Compressor) -> sys::ZL_GraphID {
sys::ZL_Compressor_setParameter(compressor, sys::ZL_CParam::ZL_CParam_formatVersion, 21);
graphs::ZSTD.0
}
unsafe extern "C" fn numeric_graph_callback(compressor: *mut sys::ZL_Compressor) -> sys::ZL_GraphID {
sys::ZL_Compressor_setParameter(compressor, sys::ZL_CParam::ZL_CParam_formatVersion, 21);
graphs::NUMERIC.0
}
unsafe extern "C" fn store_graph_callback(compressor: *mut sys::ZL_Compressor) -> sys::ZL_GraphID {
sys::ZL_Compressor_setParameter(compressor, sys::ZL_CParam::ZL_CParam_formatVersion, 21);
graphs::STORE.0
}
unsafe extern "C" fn field_lz_graph_callback(compressor: *mut sys::ZL_Compressor) -> sys::ZL_GraphID {
sys::ZL_Compressor_setParameter(compressor, sys::ZL_CParam::ZL_CParam_formatVersion, 21);
graphs::FIELD_LZ.0
}
pub struct TypedRef<'a> {
ptr: *mut sys::ZL_TypedRef,
_marker: std::marker::PhantomData<&'a [u8]>,
}
impl<'a> TypedRef<'a> {
pub fn serial(data: &'a [u8]) -> Self {
let ptr = unsafe {
sys::ZL_TypedRef_createSerial(data.as_ptr() as *const _, data.len())
};
assert!(!ptr.is_null(), "ZL_TypedRef_createSerial returned null");
TypedRef {
ptr,
_marker: std::marker::PhantomData,
}
}
pub fn numeric<T: Copy>(data: &'a [T]) -> Result<Self, Error> {
let width = std::mem::size_of::<T>();
if !matches!(width, 1 | 2 | 4 | 8) {
return Err(Error::Report {
code: -1,
name: "Invalid numeric type".into(),
context: format!("\nElement size must be 1, 2, 4, or 8 bytes, got {width}"),
});
}
let ptr = unsafe {
sys::ZL_TypedRef_createNumeric(
data.as_ptr() as *const _,
width,
data.len(),
)
};
assert!(!ptr.is_null(), "ZL_TypedRef_createNumeric returned null");
Ok(TypedRef {
ptr,
_marker: std::marker::PhantomData,
})
}
pub fn strings(flat: &'a [u8], lens: &'a [u32]) -> Self {
let ptr = unsafe {
sys::ZL_TypedRef_createString(
flat.as_ptr() as *const _,
flat.len(),
lens.as_ptr(),
lens.len(),
)
};
assert!(!ptr.is_null(), "ZL_TypedRef_createString returned null");
TypedRef {
ptr,
_marker: std::marker::PhantomData,
}
}
pub fn structs(bytes: &'a [u8], width: usize, count: usize) -> Result<Self, Error> {
if width == 0 || count == 0 {
return Err(Error::Report {
code: -1,
name: "Invalid struct parameters".into(),
context: "\nWidth and count must be non-zero".into(),
});
}
if bytes.len() != width * count {
return Err(Error::Report {
code: -1,
name: "Invalid struct buffer size".into(),
context: format!("\nExpected {} bytes (width={width} * count={count}), got {}", width * count, bytes.len()),
});
}
let ptr = unsafe {
sys::ZL_TypedRef_createStruct(
bytes.as_ptr() as *const _,
width,
count,
)
};
assert!(!ptr.is_null(), "ZL_TypedRef_createStruct returned null");
Ok(TypedRef {
ptr,
_marker: std::marker::PhantomData,
})
}
pub(crate) fn as_ptr(&self) -> *const sys::ZL_TypedRef {
self.ptr as *const _
}
}
impl Drop for TypedRef<'_> {
fn drop(&mut self) {
unsafe { sys::ZL_TypedRef_free(self.ptr) }
}
}
pub struct TypedBuffer {
ptr: *mut sys::ZL_TypedBuffer,
}
impl TypedBuffer {
pub fn new() -> Self {
let ptr = unsafe { sys::ZL_TypedBuffer_create() };
assert!(!ptr.is_null(), "ZL_TypedBuffer_create returned null");
TypedBuffer { ptr }
}
pub fn data_type(&self) -> sys::ZL_Type {
unsafe { sys::ZL_TypedBuffer_type(self.ptr) }
}
pub fn byte_size(&self) -> usize {
unsafe { sys::ZL_TypedBuffer_byteSize(self.ptr) }
}
pub fn num_elts(&self) -> usize {
unsafe { sys::ZL_TypedBuffer_numElts(self.ptr) }
}
pub fn elt_width(&self) -> usize {
unsafe { sys::ZL_TypedBuffer_eltWidth(self.ptr) }
}
pub fn as_bytes(&self) -> &[u8] {
let ptr = unsafe { sys::ZL_TypedBuffer_rPtr(self.ptr) };
let len = self.byte_size();
if ptr.is_null() || len == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts(ptr as *const u8, len) }
}
}
pub fn as_numeric<T: Copy>(&self) -> Option<&[T]> {
if self.data_type() != sys::ZL_Type::ZL_Type_numeric {
return None;
}
let width = std::mem::size_of::<T>();
if self.elt_width() != width {
return None;
}
let ptr = unsafe { sys::ZL_TypedBuffer_rPtr(self.ptr) };
if ptr.is_null() {
return None;
}
if (ptr as usize) % std::mem::align_of::<T>() != 0 {
return None;
}
let len = self.num_elts();
if len == 0 {
return Some(&[]);
}
Some(unsafe { std::slice::from_raw_parts(ptr as *const T, len) })
}
pub fn string_lens(&self) -> Option<&[u32]> {
if self.data_type() != sys::ZL_Type::ZL_Type_string {
return None;
}
let ptr = unsafe { sys::ZL_TypedBuffer_rStringLens(self.ptr) };
if ptr.is_null() {
return None;
}
let len = self.num_elts();
if len == 0 {
return Some(&[]);
}
Some(unsafe { std::slice::from_raw_parts(ptr, len) })
}
pub(crate) fn as_mut_ptr(&mut self) -> *mut sys::ZL_TypedBuffer {
self.ptr
}
}
impl Drop for TypedBuffer {
fn drop(&mut self) {
unsafe { sys::ZL_TypedBuffer_free(self.ptr) }
}
}
impl Default for TypedBuffer {
fn default() -> Self {
Self::new()
}
}
fn error_from_report_with_ctx(code: i32, name: String, ctx_str: Option<&CStr>) -> Error {
let context = match ctx_str.and_then(|s| s.to_str().ok()) {
Some(s) if !s.is_empty() => format!("\n{s}"),
_ => String::new(),
};
Error::Report { code, name, context }
}
pub struct CCtx(*mut sys::ZL_CCtx);
impl CCtx {
pub fn new() -> Self {
let ptr = unsafe { sys::ZL_CCtx_create() };
assert!(!ptr.is_null(), "ZL_CCtx_create returned null");
CCtx(ptr)
}
pub fn set_parameter(&mut self, p: sys::ZL_CParam, v: i32) -> Result<(), Error> {
let r = unsafe { sys::ZL_CCtx_setParameter(self.0, p, v) };
if sys::report_is_error(r) {
let code = sys::report_code(r);
let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
.to_string_lossy()
.into_owned();
let ctx = unsafe { CStr::from_ptr(sys::openzl_cctx_error_context(self.0, r)) };
return Err(error_from_report_with_ctx(code, name, Some(&ctx)));
}
Ok(())
}
pub fn ref_compressor(&mut self, compressor: &Compressor) -> Result<(), Error> {
let r = unsafe { sys::ZL_CCtx_refCompressor(self.0, compressor.as_ptr()) };
if sys::report_is_error(r) {
let code = sys::report_code(r);
let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
.to_string_lossy()
.into_owned();
let ctx = unsafe { CStr::from_ptr(sys::openzl_cctx_error_context(self.0, r)) };
return Err(error_from_report_with_ctx(code, name, Some(&ctx)));
}
Ok(())
}
pub fn warnings(&self) -> Vec<Warning> {
let arr = unsafe { sys::openzl_cctx_get_warnings(self.0) };
let slice = unsafe { std::slice::from_raw_parts(arr.errors, arr.size) };
slice
.iter()
.map(|e| {
let code = unsafe { sys::openzl_error_get_code(e) };
let name = unsafe { CStr::from_ptr(sys::openzl_error_get_name(e)) }
.to_string_lossy()
.into_owned();
Warning { code, name }
})
.collect()
}
pub fn compress_typed_ref(&mut self, input: &TypedRef, dst: &mut [u8]) -> Result<usize, Error> {
let r = unsafe {
sys::ZL_CCtx_compressTypedRef(
self.0,
dst.as_mut_ptr() as *mut _,
dst.len(),
input.as_ptr(),
)
};
if sys::report_is_error(r) {
let code = sys::report_code(r);
let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
.to_string_lossy()
.into_owned();
let ctx = unsafe { CStr::from_ptr(sys::openzl_cctx_error_context(self.0, r)) };
return Err(error_from_report_with_ctx(code, name, Some(&ctx)));
}
Ok(sys::report_value(r))
}
pub fn compress_multi_typed_ref(&mut self, inputs: &[&TypedRef], dst: &mut [u8]) -> Result<usize, Error> {
let mut ptrs: Vec<*const sys::ZL_TypedRef> = inputs.iter().map(|tr| tr.as_ptr()).collect();
let r = unsafe {
sys::ZL_CCtx_compressMultiTypedRef(
self.0,
dst.as_mut_ptr() as *mut _,
dst.len(),
ptrs.as_mut_ptr() as *mut _,
ptrs.len(),
)
};
if sys::report_is_error(r) {
let code = sys::report_code(r);
let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
.to_string_lossy()
.into_owned();
let ctx = unsafe { CStr::from_ptr(sys::openzl_cctx_error_context(self.0, r)) };
return Err(error_from_report_with_ctx(code, name, Some(&ctx)));
}
Ok(sys::report_value(r))
}
}
impl Drop for CCtx { fn drop(&mut self) { unsafe { sys::ZL_CCtx_free(self.0) } } }
pub struct DCtx(*mut sys::ZL_DCtx);
impl DCtx {
pub fn new() -> Self {
let p = unsafe { sys::ZL_DCtx_create() };
assert!(!p.is_null());
DCtx(p)
}
pub fn warnings(&self) -> Vec<Warning> {
let arr = unsafe { sys::openzl_dctx_get_warnings(self.0) };
let slice = unsafe { std::slice::from_raw_parts(arr.errors, arr.size) };
slice
.iter()
.map(|e| {
let code = unsafe { sys::openzl_error_get_code(e) };
let name = unsafe { CStr::from_ptr(sys::openzl_error_get_name(e)) }
.to_string_lossy()
.into_owned();
Warning { code, name }
})
.collect()
}
pub fn decompress_typed_buffer(&mut self, compressed: &[u8], output: &mut TypedBuffer) -> Result<usize, Error> {
let r = unsafe {
sys::ZL_DCtx_decompressTBuffer(
self.0,
output.as_mut_ptr(),
compressed.as_ptr() as *const _,
compressed.len(),
)
};
if sys::report_is_error(r) {
let code = sys::report_code(r);
let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
.to_string_lossy()
.into_owned();
let ctx = unsafe { CStr::from_ptr(sys::openzl_dctx_error_context(self.0, r)) };
return Err(error_from_report_with_ctx(code, name, Some(&ctx)));
}
Ok(sys::report_value(r))
}
pub fn decompress_multi_typed_buffer(&mut self, compressed: &[u8], outputs: &mut [&mut TypedBuffer]) -> Result<usize, Error> {
let mut ptrs: Vec<*mut sys::ZL_TypedBuffer> = outputs.iter_mut().map(|tb| tb.as_mut_ptr()).collect();
let r = unsafe {
sys::ZL_DCtx_decompressMultiTBuffer(
self.0,
ptrs.as_mut_ptr(),
ptrs.len(),
compressed.as_ptr() as *const _,
compressed.len(),
)
};
if sys::report_is_error(r) {
let code = sys::report_code(r);
let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
.to_string_lossy()
.into_owned();
let ctx = unsafe { CStr::from_ptr(sys::openzl_dctx_error_context(self.0, r)) };
return Err(error_from_report_with_ctx(code, name, Some(&ctx)));
}
Ok(sys::report_value(r))
}
}
impl Drop for DCtx { fn drop(&mut self) { unsafe { sys::ZL_DCtx_free(self.0) } } }
pub fn compress_serial(src: &[u8]) -> Result<Vec<u8>, Error> {
let mut cctx = CCtx::new();
cctx.set_parameter(sys::ZL_CParam::ZL_CParam_formatVersion, 21)?;
let cap = unsafe { sys::openzl_compress_bound(src.len()) };
let mut dst = vec![0u8; cap];
let r = unsafe {
sys::ZL_CCtx_compress(
cctx.0,
dst.as_mut_ptr() as *mut _,
dst.len(),
src.as_ptr() as *const _,
src.len(),
)
};
if sys::report_is_error(r) {
let code = sys::report_code(r);
let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
.to_string_lossy()
.into_owned();
let ctx = unsafe { CStr::from_ptr(sys::openzl_cctx_error_context(cctx.0, r)) };
return Err(error_from_report_with_ctx(code, name, Some(&ctx)));
}
let n = sys::report_value(r) as usize;
dst.truncate(n);
Ok(dst)
}
pub fn compress_typed_ref(input: &TypedRef) -> Result<Vec<u8>, Error> {
let mut cctx = CCtx::new();
cctx.set_parameter(sys::ZL_CParam::ZL_CParam_formatVersion, 21)?;
let mut compressor = Compressor::new();
compressor.set_parameter(sys::ZL_CParam::ZL_CParam_formatVersion, 21)?;
let r = unsafe {
sys::ZL_Compressor_initUsingGraphFn(compressor.as_mut_ptr(), Some(zstd_graph_callback))
};
if sys::report_is_error(r) {
return Err(report_to_error(r));
}
cctx.ref_compressor(&compressor)?;
let cap = 1024 * 1024; let mut dst = vec![0u8; cap];
let n = cctx.compress_typed_ref(input, &mut dst)?;
dst.truncate(n);
Ok(dst)
}
pub fn compress_multi_typed_ref(inputs: &[&TypedRef]) -> Result<Vec<u8>, Error> {
let mut cctx = CCtx::new();
cctx.set_parameter(sys::ZL_CParam::ZL_CParam_formatVersion, 21)?;
let mut compressor = Compressor::new();
compressor.set_parameter(sys::ZL_CParam::ZL_CParam_formatVersion, 21)?;
let r = unsafe {
sys::ZL_Compressor_initUsingGraphFn(compressor.as_mut_ptr(), Some(zstd_graph_callback))
};
if sys::report_is_error(r) {
return Err(report_to_error(r));
}
cctx.ref_compressor(&compressor)?;
let cap = 1024 * 1024; let mut dst = vec![0u8; cap];
let n = cctx.compress_multi_typed_ref(inputs, &mut dst)?;
dst.truncate(n);
Ok(dst)
}
pub fn decompress_serial(src: &[u8]) -> Result<Vec<u8>, Error> {
let rsize = unsafe { sys::ZL_getDecompressedSize(src.as_ptr() as *const _, src.len()) };
if sys::report_is_error(rsize) {
let code = sys::report_code(rsize);
let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
.to_string_lossy()
.into_owned();
return Err(error_from_report_with_ctx(code, name, None));
}
let mut dst = vec![0u8; sys::report_value(rsize) as usize];
let r = unsafe { sys::ZL_decompress(dst.as_mut_ptr() as *mut _, dst.len(), src.as_ptr() as *const _, src.len()) };
if sys::report_is_error(r) {
let code = sys::report_code(r);
let name = unsafe { CStr::from_ptr(sys::openzl_error_code_to_string(code)) }
.to_string_lossy()
.into_owned();
return Err(error_from_report_with_ctx(code, name, None));
}
let n = sys::report_value(r) as usize;
dst.truncate(n);
Ok(dst)
}
pub fn decompress_typed_buffer(compressed: &[u8]) -> Result<TypedBuffer, Error> {
let mut dctx = DCtx::new();
let mut output = TypedBuffer::new();
dctx.decompress_typed_buffer(compressed, &mut output)?;
Ok(output)
}
pub fn compress_numeric<T: Copy>(data: &[T]) -> Result<Vec<u8>, Error> {
let width = std::mem::size_of::<T>();
if !matches!(width, 1 | 2 | 4 | 8) {
return Err(Error::Report {
code: -1,
name: "Invalid numeric type".into(),
context: format!("\nElement size must be 1, 2, 4, or 8 bytes, got {width}"),
});
}
let tref = TypedRef::numeric(data)?;
let mut cctx = CCtx::new();
cctx.set_parameter(sys::ZL_CParam::ZL_CParam_formatVersion, 21)?;
let mut compressor = Compressor::new();
compressor.set_parameter(sys::ZL_CParam::ZL_CParam_formatVersion, 21)?;
let r = unsafe {
sys::ZL_Compressor_initUsingGraphFn(compressor.as_mut_ptr(), Some(numeric_graph_callback))
};
if sys::report_is_error(r) {
return Err(report_to_error(r));
}
cctx.ref_compressor(&compressor)?;
let cap = compress_bound(data.len() * width);
let mut dst = vec![0u8; cap];
let n = cctx.compress_typed_ref(&tref, &mut dst)?;
dst.truncate(n);
Ok(dst)
}
pub fn decompress_numeric<T: Copy>(compressed: &[u8]) -> Result<Vec<T>, Error> {
let width = std::mem::size_of::<T>();
if !matches!(width, 1 | 2 | 4 | 8) {
return Err(Error::Report {
code: -1,
name: "Invalid numeric type".into(),
context: format!("\nElement size must be 1, 2, 4, or 8 bytes, got {width}"),
});
}
let tbuf = decompress_typed_buffer(compressed)?;
if tbuf.data_type() != sys::ZL_Type::ZL_Type_numeric {
return Err(Error::Report {
code: -1,
name: "Type mismatch".into(),
context: format!("\nExpected numeric type, got {:?}", tbuf.data_type()),
});
}
if tbuf.elt_width() != width {
return Err(Error::Report {
code: -1,
name: "Width mismatch".into(),
context: format!(
"\nExpected element width {}, got {}",
width,
tbuf.elt_width()
),
});
}
let slice = tbuf.as_numeric::<T>().ok_or_else(|| Error::Report {
code: -1,
name: "Failed to extract numeric data".into(),
context: "\nAlignment or type mismatch".into(),
})?;
Ok(slice.to_vec())
}