use crate::compressor::QuantizationScale;
use crate::error::BiolepticError;
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
pub enum CompressionMethod {
Cdf53,
Cdf97,
Db4,
Sym4,
}
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
pub enum DataType {
Float32,
}
const CDF53: u32 = u32::from_le_bytes(*b"cf53");
const CDF97: u32 = u32::from_le_bytes(*b"cf97");
const DB4: u32 = u32::from_le_bytes(*b"db04");
const SYM4: u32 = u32::from_le_bytes(*b"sym4");
impl TryFrom<u32> for CompressionMethod {
type Error = BiolepticError;
fn try_from(value: u32) -> Result<Self, BiolepticError> {
match value {
CDF97 => Ok(CompressionMethod::Cdf97),
CDF53 => Ok(CompressionMethod::Cdf53),
DB4 => Ok(CompressionMethod::Db4),
SYM4 => Ok(CompressionMethod::Sym4),
_ => Err(BiolepticError::InvalidCompressionMethod(
value.to_ne_bytes(),
)),
}
}
}
impl From<CompressionMethod> for u32 {
fn from(val: CompressionMethod) -> Self {
match val {
CompressionMethod::Cdf53 => CDF53,
CompressionMethod::Cdf97 => CDF97,
CompressionMethod::Db4 => DB4,
CompressionMethod::Sym4 => SYM4,
}
}
}
const FLOAT_32: u16 = u16::from_le_bytes(*b"f3");
impl TryFrom<u16> for DataType {
type Error = BiolepticError;
fn try_from(value: u16) -> Result<Self, BiolepticError> {
match value {
FLOAT_32 => Ok(DataType::Float32),
_ => Err(BiolepticError::InvalidDataType(value.to_ne_bytes())),
}
}
}
impl From<DataType> for u16 {
fn from(val: DataType) -> Self {
match val {
DataType::Float32 => FLOAT_32,
}
}
}
pub const BIOLEPTIC_MAGIC: [u8; 4] = *b"BILP";
pub const BIOLEPTIC_VERSION: u16 = u16::from_le_bytes([1, 0]);
pub const BIOLEPTIC_HEADER_SIZE: usize = size_of::<BiolepticHeader>();
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Hash)]
pub enum EntropyCoder {
Deflate = 0,
Arans = 1,
}
impl TryFrom<u8> for EntropyCoder {
type Error = BiolepticError;
fn try_from(value: u8) -> Result<Self, BiolepticError> {
match value {
0 => Ok(EntropyCoder::Deflate),
1 => Ok(EntropyCoder::Arans),
_ => Err(BiolepticError::InvalidEntropyCoder(value)),
}
}
}
impl From<EntropyCoder> for u8 {
fn from(val: EntropyCoder) -> Self {
val as u8
}
}
#[repr(C, packed)]
pub struct BiolepticHeader {
pub magic: [u8; 4],
pub version: u16,
pub data_type: u16,
pub compression_method: [u8; 4],
pub levels: u8,
pub scale: u8,
pub entropy_coder: u8,
pub reserved0: [u8; 1],
pub signal_length: u32,
pub min: u32,
pub max: u32,
pub mean: u32,
pub compressed_size: u32,
pub reserved1: [u8; 16],
}
impl BiolepticHeader {
#[allow(clippy::too_many_arguments)]
pub fn new(
data_type: DataType,
compression_method: CompressionMethod,
levels: u8,
scale: QuantizationScale,
signal_length: u32,
min: f32,
max: f32,
mean: f32,
compressed_size: u32,
entropy_coder: EntropyCoder,
) -> Self {
let compression_method_impl: u32 = compression_method.into();
Self {
magic: BIOLEPTIC_MAGIC,
version: BIOLEPTIC_VERSION,
data_type: data_type.into(),
compression_method: compression_method_impl.to_le_bytes(),
levels,
scale: scale.as_u8(),
entropy_coder: entropy_coder.into(),
reserved0: [0; 1],
signal_length,
min: min.to_bits(),
max: max.to_bits(),
mean: mean.to_bits(),
reserved1: [0; 16],
compressed_size,
}
}
pub fn to_bytes(&self) -> [u8; BIOLEPTIC_HEADER_SIZE] {
let mut buf = [0u8; BIOLEPTIC_HEADER_SIZE];
buf[0..4].copy_from_slice(&self.magic);
buf[4..6].copy_from_slice(&self.version.to_le_bytes());
buf[6..8].copy_from_slice(&self.data_type.to_le_bytes());
buf[8..12].copy_from_slice(&self.compression_method);
buf[12] = self.levels;
buf[13] = self.scale;
buf[14] = self.entropy_coder;
buf[15] = self.reserved0[0];
buf[16..20].copy_from_slice(&self.signal_length.to_le_bytes());
buf[20..24].copy_from_slice(&self.min.to_le_bytes());
buf[24..28].copy_from_slice(&self.max.to_le_bytes());
buf[28..32].copy_from_slice(&self.mean.to_le_bytes());
buf[32..36].copy_from_slice(&self.compressed_size.to_le_bytes());
buf[36..52].copy_from_slice(&self.reserved1);
buf
}
pub fn from_bytes(buf: &[u8]) -> Result<Self, BiolepticError> {
if buf.len() < BIOLEPTIC_HEADER_SIZE {
return Err(BiolepticError::InvalidHeader);
}
let buf = &buf[..BIOLEPTIC_HEADER_SIZE];
let magic: [u8; 4] = buf[0..4].try_into().unwrap();
if magic != BIOLEPTIC_MAGIC {
return Err(BiolepticError::InvalidMagic(magic));
}
let version = u16::from_le_bytes(buf[4..6].try_into().unwrap());
if version != BIOLEPTIC_VERSION {
return Err(BiolepticError::InvalidVersion(version.to_ne_bytes()));
}
let data_type = u16::from_le_bytes(buf[6..8].try_into().unwrap());
let compression_method = u32::from_le_bytes(buf[8..12].try_into().unwrap());
let v_data_type = DataType::try_from(data_type)?;
let _ = CompressionMethod::try_from(compression_method)?;
let _ = QuantizationScale::try_from(buf[13]);
let _ = EntropyCoder::try_from(buf[14])?;
let f_min = u32::from_le_bytes(buf[20..24].try_into().unwrap());
let f_max = u32::from_le_bytes(buf[24..28].try_into().unwrap());
let f_mean = u32::from_le_bytes(buf[28..32].try_into().unwrap());
match v_data_type {
DataType::Float32 => {
if !f32::from_bits(f_min).is_finite() {
return Err(BiolepticError::InvalidHeader);
}
if !f32::from_bits(f_max).is_finite() {
return Err(BiolepticError::InvalidHeader);
}
if !f32::from_bits(f_mean).is_finite() {
return Err(BiolepticError::InvalidHeader);
}
}
}
Ok(Self {
magic,
version,
data_type,
compression_method: buf[8..12].try_into().unwrap(),
levels: buf[12],
scale: buf[13],
entropy_coder: buf[14],
reserved0: buf[15..16].try_into().unwrap(),
signal_length: u32::from_le_bytes(buf[16..20].try_into().unwrap()),
min: f_min,
max: f_max,
mean: f_mean,
compressed_size: u32::from_le_bytes(buf[32..36].try_into().unwrap()),
reserved1: buf[36..52].try_into().unwrap(),
})
}
pub fn min_f32(&self) -> f32 {
f32::from_bits(self.min)
}
pub fn max_f32(&self) -> f32 {
f32::from_bits(self.max)
}
pub fn mean_f32(&self) -> f32 {
f32::from_bits(self.mean)
}
pub fn compression_method(&self) -> Result<CompressionMethod, BiolepticError> {
CompressionMethod::try_from(u32::from_le_bytes(self.compression_method))
}
pub fn data_type(&self) -> Result<DataType, BiolepticError> {
DataType::try_from(self.data_type)
}
}
impl std::fmt::Debug for BiolepticHeader {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BiolepticHeader")
.field("magic", &std::str::from_utf8(&self.magic).unwrap_or("????"))
.field("version", &{ self.version })
.field("data_type", &self.data_type())
.field("compression_method", &self.compression_method())
.field("levels", &self.levels)
.field("scale", &self.scale)
.field("signal_length", &{ self.signal_length })
.field("min", &self.min_f32())
.field("max", &self.max_f32())
.field("mean", &self.mean_f32())
.finish()
}
}