macro_rules! dispatch_decoder {
($params:expr, $base:expr, $F:ty, $decoder:ident => $body:expr) => {{
let (exp_factor, fac_int, frac_flt) = $params.factors::<$F>();
if $params.use_div {
let $decoder = $crate::bitpack::AlpDivDecoder {
base: $base,
exp_factor,
};
$body
} else if fac_int == 1 {
let $decoder = $crate::bitpack::AlpFac1Decoder {
base: $base,
frac_flt,
};
$body
} else {
let $decoder = $crate::bitpack::AlpMulDecoder {
base: $base,
fac_int,
frac_flt,
};
$body
}
}};
}
mod delta;
mod standard;
use core::ptr::copy_nonoverlapping;
use std::ptr::read_unaligned;
use delta::decode_delta_raw;
use standard::decode_standard_raw;
use crate::{
constants::{EXC_COUNT_LEN, EXC_COUNT_LEN_U32},
error::{Error, Result},
float::AlpFloat,
header::{ParsedHeader, read_count, read_header},
};
pub unsafe fn decompress_into_raw<F: AlpFloat>(
src: &[u8],
dst_ptr: *mut F,
dst_cap: usize,
) -> Result<usize> {
let ParsedHeader {
type_byte,
count,
params,
cursor,
..
} = read_header(src)?;
if count == 0 {
return Ok(0);
}
if dst_cap < count {
return Err(Error::BufferTooSmall {
needed: count,
available: dst_cap,
});
}
if type_byte == F::TYPE_RAW_BYTE {
let raw_bytes_needed = count
.checked_mul(size_of::<F>())
.ok_or(Error::InvalidHeader)?;
if src.len() < cursor + raw_bytes_needed {
return Err(Error::UnexpectedEof {
needed: cursor + raw_bytes_needed,
available: src.len(),
});
}
unsafe {
copy_nonoverlapping(
src.as_ptr().add(cursor),
dst_ptr.cast::<u8>(),
raw_bytes_needed,
);
}
return Ok(count);
}
let is_delta = type_byte == F::TYPE_DELTA_BYTE || type_byte == F::TYPE_DEC_DELTA_BYTE;
let is_standard = type_byte == F::TYPE_BYTE || type_byte == F::TYPE_DEC_BYTE;
if !is_standard && !is_delta {
return Err(Error::InvalidHeader);
}
let alp_params = match params {
Some(p) => p,
None => return Err(Error::InvalidHeader),
};
if !alp_params.validate::<F>() {
return Err(Error::UnsupportedParams {
exp: alp_params.exp,
fac: alp_params.fac,
bit_width: alp_params.bit_width,
});
}
let payload = &src[cursor..];
if is_delta {
unsafe {
decode_delta_raw::<F>(payload, count, alp_params, dst_ptr)?;
}
} else {
unsafe {
decode_standard_raw::<F>(payload, count, alp_params, dst_ptr)?;
}
}
Ok(count)
}
#[inline(always)]
pub fn decompress_into_slice<F: AlpFloat>(src: &[u8], dst: &mut [F]) -> Result<usize> {
unsafe { decompress_into_raw(src, dst.as_mut_ptr(), dst.len()) }
}
pub fn decompress_into<F: AlpFloat>(src: &[u8], dst: &mut Vec<F>) -> Result<()> {
let count = read_count(src)?;
if count == 0 {
return Ok(());
}
let old_len = dst.len();
dst.reserve(count);
unsafe {
let written = decompress_into_raw(src, dst.as_mut_ptr().add(old_len), count)?;
dst.set_len(old_len + written);
}
Ok(())
}
#[inline]
pub fn decompress<F: AlpFloat>(src: &[u8]) -> Result<Vec<F>> {
let mut dst = Vec::new();
decompress_into(src, &mut dst)?;
Ok(dst)
}
#[inline]
pub(crate) unsafe fn patch_exceptions<F: AlpFloat>(
src: &[u8],
count: usize,
dst_ptr: *mut F,
) -> Result<()> {
if src.is_empty() {
return Ok(());
}
let mut cursor = 0;
let is_large = count > u16::MAX as usize;
let (exc_count, exc_count_len) = if is_large {
if src.len() < cursor + EXC_COUNT_LEN_U32 {
return Err(Error::UnexpectedEof {
needed: cursor + EXC_COUNT_LEN_U32,
available: src.len(),
});
}
let c =
unsafe { u32::from_le(read_unaligned(src.as_ptr().add(cursor).cast::<u32>())) } as usize;
(c, EXC_COUNT_LEN_U32)
} else {
if src.len() < cursor + EXC_COUNT_LEN {
return Err(Error::UnexpectedEof {
needed: cursor + EXC_COUNT_LEN,
available: src.len(),
});
}
let c =
unsafe { u16::from_le(read_unaligned(src.as_ptr().add(cursor).cast::<u16>())) } as usize;
(c, EXC_COUNT_LEN)
};
cursor += exc_count_len;
let entry_size = if is_large {
F::EXC_ENTRY_SIZE_U32
} else {
F::EXC_ENTRY_SIZE
};
let exc_bytes_needed = exc_count
.checked_mul(entry_size)
.ok_or(Error::InvalidHeader)?;
if src.len() < cursor + exc_bytes_needed {
return Err(Error::UnexpectedEof {
needed: cursor + exc_bytes_needed,
available: src.len(),
});
}
let exc_slice = &src[cursor..cursor + exc_bytes_needed];
if is_large {
for chunk in exc_slice.chunks_exact(entry_size) {
let (pos, val) = F::read_exception_u32(chunk);
if pos >= count {
return Err(Error::CorruptedData { index: pos, count });
}
unsafe {
*dst_ptr.add(pos) = val;
}
}
} else {
for chunk in exc_slice.chunks_exact(entry_size) {
let (pos, val) = F::read_exception(chunk);
if pos >= count {
return Err(Error::CorruptedData { index: pos, count });
}
unsafe {
*dst_ptr.add(pos) = val;
}
}
}
Ok(())
}