mod delta;
mod standard;
use core::{ptr::copy_nonoverlapping, slice::from_raw_parts_mut};
use std::ptr::read_unaligned;
pub use delta::{decode_delta, decode_delta_slice};
pub use standard::{decode_standard, decode_standard_slice};
use crate::{
constants::{EXC_COUNT_LEN, EXC_COUNT_LEN_U32},
error::{Error, Result},
float::AlpFloat,
header::{ParsedHeader, read_header},
};
pub fn decompress_into_slice<F: AlpFloat>(src: &[u8], dst: &mut [F]) -> Result<usize> {
let ParsedHeader {
type_byte,
count,
params,
cursor,
..
} = read_header(src)?;
if count == 0 {
return Ok(0);
}
if dst.len() < count {
return Err(Error::BufferTooSmall {
needed: count,
available: dst.len(),
});
}
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.as_mut_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 {
decode_delta_slice::<F>(payload, count, alp_params, &mut dst[..count])?;
} else {
decode_standard_slice::<F>(payload, count, alp_params, &mut dst[..count])?;
}
Ok(count)
}
pub fn decompress_into<F: AlpFloat>(src: &[u8], dst: &mut Vec<F>) -> Result<()> {
let ParsedHeader { count, .. } = read_header(src)?;
if count == 0 {
return Ok(());
}
let old_len = dst.len();
dst.reserve(count);
let slice = unsafe { from_raw_parts_mut(dst.as_mut_ptr().add(old_len), count) };
let written = decompress_into_slice(src, slice)?;
unsafe {
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) fn patch_exceptions<F: AlpFloat>(src: &[u8], count: usize, dst: &mut [F]) -> Result<()> {
if src.is_empty() {
return Ok(());
}
debug_assert!(
dst.len() >= count,
"destination buffer too small for exceptions"
);
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(),
});
}
if is_large {
for _ in 0..exc_count {
let (pos, val) = F::read_exception_u32(&src[cursor..cursor + entry_size]);
cursor += entry_size;
if pos >= count {
return Err(Error::CorruptedData { index: pos, count });
}
unsafe {
*dst.get_unchecked_mut(pos) = val;
}
}
} else {
for _ in 0..exc_count {
let (pos, val) = F::read_exception(&src[cursor..cursor + entry_size]);
cursor += entry_size;
if pos >= count {
return Err(Error::CorruptedData { index: pos, count });
}
unsafe {
*dst.get_unchecked_mut(pos) = val;
}
}
}
Ok(())
}