use crate::error::GribberishError;
use libaec_sys::*;
use std::ffi::c_int;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AecError {
#[error("Configuration error: {0}")]
Config(String),
#[error("Stream error: {0}")]
Stream(String),
#[error("Data error: {0}")]
Data(String),
#[error("Memory error: {0}")]
Memory(String),
#[error("Unknown error: {0}")]
Unknown(c_int),
}
impl From<c_int> for AecError {
fn from(code: c_int) -> Self {
match code {
AEC_CONF_ERROR => AecError::Config("Invalid configuration".to_string()),
AEC_STREAM_ERROR => AecError::Stream("Stream processing error".to_string()),
AEC_DATA_ERROR => AecError::Data("Invalid input data".to_string()),
AEC_MEM_ERROR => AecError::Memory("Memory allocation error".to_string()),
_ => AecError::Unknown(code),
}
}
}
pub struct Decoder {
stream: aec_stream,
}
impl Decoder {
pub fn new(
bits_per_sample: u32,
block_size: u32,
reference_sample_interval: u32,
compression_options_mask: u8,
) -> Result<Self, AecError> {
let mut stream = unsafe { std::mem::zeroed::<aec_stream>() };
stream.bits_per_sample = bits_per_sample;
stream.block_size = block_size;
stream.rsi = reference_sample_interval;
let mut flags = 0u32;
if (compression_options_mask & 0x01) != 0 {
flags |= AEC_DATA_SIGNED;
}
if (compression_options_mask & 0x02) != 0 {
flags |= AEC_DATA_PREPROCESS;
}
if (compression_options_mask & 0x04) != 0 {
flags |= AEC_DATA_MSB;
}
if (compression_options_mask & 0x08) != 0 {
flags |= AEC_RESTRICTED;
}
if (compression_options_mask & 0x10) != 0 {
flags |= AEC_PAD_RSI;
}
stream.flags = flags;
let result = unsafe { aec_decode_init(&mut stream) };
if result != AEC_OK as c_int {
return Err(AecError::from(result));
}
Ok(Self { stream })
}
pub fn decode(&mut self, input: &[u8], output_size: usize) -> Result<Vec<u8>, AecError> {
let mut output = vec![0u8; output_size];
self.stream.next_in = input.as_ptr();
self.stream.avail_in = input.len();
self.stream.next_out = output.as_mut_ptr();
self.stream.avail_out = output.len();
let result = unsafe { aec_decode(&mut self.stream, AEC_FLUSH as i32) };
if result as u32 != AEC_OK {
return Err(AecError::from(result));
}
Ok(output)
}
}
impl Drop for Decoder {
fn drop(&mut self) {
unsafe {
aec_decode_end(&mut self.stream);
}
}
}
pub fn extract_ccsds_data(
data: Vec<u8>,
block_len: u8,
compression_options_mask: u8,
avail_out: usize,
reference_sample_interval: u16,
bits_per_sample: usize,
) -> Result<Vec<f32>, GribberishError> {
if data.is_empty() {
return Err(GribberishError::MessageError(
"Empty input data".to_string(),
));
}
if bits_per_sample == 0 {
return Ok(vec![]);
}
if bits_per_sample > 32 {
return Err(GribberishError::MessageError(
"bits_per_sample cannot exceed 32".to_string(),
));
}
let big_endian = (compression_options_mask & 0x04) != 0;
let mut decoder = Decoder::new(
bits_per_sample as u32,
block_len as u32,
reference_sample_interval as u32,
compression_options_mask,
)
.map_err(|e| GribberishError::MessageError(format!("Failed to create decoder: {}", e)))?;
let bytes_per_sample = bits_per_sample.div_ceil(8);
let num_samples = avail_out / bytes_per_sample;
if !avail_out.is_multiple_of(bytes_per_sample) {
return Err(GribberishError::MessageError(format!(
"avail_out ({}) is not a multiple of bytes_per_sample ({})",
avail_out, bytes_per_sample
)));
}
let decompressed_bytes = decoder.decode(&data, avail_out).map_err(|e| match e {
AecError::Config(msg) => {
GribberishError::MessageError(format!("Configuration error: {}", msg))
}
AecError::Stream(msg) => GribberishError::MessageError(format!("Stream error: {}", msg)),
AecError::Data(msg) => GribberishError::MessageError(format!("Data error: {}", msg)),
AecError::Memory(msg) => GribberishError::MessageError(format!("Memory error: {}", msg)),
AecError::Unknown(code) => {
GribberishError::MessageError(format!("Unknown error code: {}", code))
}
})?;
let f32_values = bytes_to_f32_values(
&decompressed_bytes,
bits_per_sample,
num_samples,
big_endian,
)?;
Ok(f32_values)
}
fn bytes_to_f32_values(
bytes: &[u8],
bits_per_sample: usize,
num_samples: usize,
big_endian: bool,
) -> Result<Vec<f32>, GribberishError> {
let storage_size = if bits_per_sample <= 8 {
1
} else if bits_per_sample <= 16 {
2
} else {
4
};
let expected_byte_count = num_samples * storage_size;
if bytes.len() < expected_byte_count {
return Err(GribberishError::MessageError(format!(
"Insufficient decompressed data: got {} bytes, expected {}",
bytes.len(),
expected_byte_count
)));
}
let mut values = Vec::with_capacity(num_samples);
for i in 0..num_samples {
let start_idx = i * storage_size;
let value = match storage_size {
1 => bytes[start_idx] as u32,
2 => {
if big_endian {
u16::from_be_bytes([bytes[start_idx], bytes[start_idx + 1]]) as u32
} else {
u16::from_le_bytes([bytes[start_idx], bytes[start_idx + 1]]) as u32
}
}
4 => {
if big_endian {
u32::from_be_bytes([
bytes[start_idx],
bytes[start_idx + 1],
bytes[start_idx + 2],
bytes[start_idx + 3],
])
} else {
u32::from_le_bytes([
bytes[start_idx],
bytes[start_idx + 1],
bytes[start_idx + 2],
bytes[start_idx + 3],
])
}
}
_ => unreachable!(),
};
let masked_value = if bits_per_sample < storage_size * 8 {
let mask = (1u32 << bits_per_sample) - 1;
value & mask
} else {
value
};
values.push(masked_value as f32);
}
Ok(values)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_decoder_creation() {
let decoder = Decoder::new(16, 32, 128, 0);
assert!(decoder.is_ok());
}
#[test]
fn test_invalid_parameters() {
let decoder = Decoder::new(0, 32, 128, 0);
assert!(decoder.is_err());
}
#[test]
fn test_extract_ccsds_data_empty_input() {
let result = extract_ccsds_data(vec![], 16, 0, 100, 128, 8);
assert!(result.is_err());
}
#[test]
fn test_extract_ccsds_data_zero_bits() {
let result = extract_ccsds_data(vec![1, 2, 3, 4], 16, 0, 100, 128, 0);
assert!(result.is_ok());
assert_eq!(result.unwrap(), vec![]);
}
#[test]
fn test_extract_ccsds_data_invalid_bits() {
let result = extract_ccsds_data(vec![1, 2, 3, 4], 16, 0, 100, 128, 33);
assert!(result.is_err());
}
#[test]
fn test_bytes_to_f32_values_8bit() {
let bytes = vec![0x10, 0x20, 0x30, 0x40];
let result = bytes_to_f32_values(&bytes, 8, 4, true).unwrap();
assert_eq!(result, vec![16.0, 32.0, 48.0, 64.0]);
}
#[test]
fn test_bytes_to_f32_values_16bit() {
let bytes = vec![0x12, 0x34, 0x56, 0x78]; let result = bytes_to_f32_values(&bytes, 16, 2, true).unwrap();
assert_eq!(result, vec![4660.0, 22136.0]);
}
#[test]
fn test_bytes_to_f32_values_32bit() {
let bytes = vec![0x12, 0x34, 0x56, 0x78, 0x56, 0x78, 0x9A, 0xBC];
let result = bytes_to_f32_values(&bytes, 32, 2, true).unwrap();
assert_eq!(result, vec![305419896.0, 1450744508.0]);
}
#[test]
fn test_bytes_to_f32_values_insufficient_data() {
let bytes = vec![0x10, 0x20]; let result = bytes_to_f32_values(&bytes, 16, 2, true);
assert!(result.is_err());
}
#[test]
fn test_bytes_to_f32_values_bit_masking() {
let bytes = vec![0xFF]; let result = bytes_to_f32_values(&bytes, 4, 1, true).unwrap(); assert_eq!(result, vec![15.0]); }
#[test]
fn test_bytes_to_f32_values_little_endian() {
let bytes = vec![0x12, 0x34, 0x56, 0x78]; let result = bytes_to_f32_values(&bytes, 16, 2, false).unwrap();
assert_eq!(result, vec![13330.0, 30806.0]);
}
#[test]
fn test_extract_ccsds_data_invalid_avail_out() {
let result = extract_ccsds_data(vec![1, 2, 3, 4], 16, 0, 5, 128, 16);
assert!(result.is_err());
let error_msg = format!("{}", result.unwrap_err());
assert!(error_msg.contains("not a multiple of bytes_per_sample"));
}
}