use crate::error::{CodecError, Result};
use crate::types::{CodecType, SampleRate};
pub fn validate_samples(samples: &[i16]) -> Result<()> {
if samples.is_empty() {
return Err(CodecError::invalid_format("Input samples cannot be empty"));
}
Ok(())
}
pub fn validate_encoded_data(data: &[u8]) -> Result<()> {
if data.is_empty() {
return Err(CodecError::invalid_format("Encoded data cannot be empty"));
}
if data.len() > 1024 * 1024 {
return Err(CodecError::invalid_format(format!(
"Encoded data too large: {} bytes",
data.len()
)));
}
Ok(())
}
pub fn validate_frame_size(codec_type: CodecType, frame_size: usize) -> Result<()> {
let expected_sizes = match codec_type {
CodecType::G711Pcmu | CodecType::G711Pcma => {
vec![80, 160, 240, 320]
}
CodecType::G729 | CodecType::G729A | CodecType::G729BA => {
vec![80]
}
CodecType::Opus => {
vec![120, 240, 480, 960, 1920, 2880]
}
};
if !expected_sizes.contains(&frame_size) {
return Err(CodecError::InvalidFrameSize {
expected: expected_sizes[0],
actual: frame_size,
});
}
Ok(())
}
pub fn validate_sample_rate(codec_type: CodecType, sample_rate: SampleRate) -> Result<()> {
let supported_rates = codec_type.supported_sample_rates();
let rate_hz = sample_rate.hz();
if !supported_rates.contains(&rate_hz) {
return Err(CodecError::InvalidSampleRate {
rate: rate_hz,
supported: supported_rates.to_vec(),
});
}
Ok(())
}
pub fn validate_channels(codec_type: CodecType, channels: u8) -> Result<()> {
let supported_channels = codec_type.supported_channels();
if !supported_channels.contains(&channels) {
return Err(CodecError::InvalidChannelCount {
channels,
supported: supported_channels.to_vec(),
});
}
Ok(())
}
pub fn validate_bitrate(codec_type: CodecType, bitrate: u32) -> Result<()> {
let (min_bitrate, max_bitrate) = codec_type.bitrate_range();
if bitrate < min_bitrate || bitrate > max_bitrate {
return Err(CodecError::InvalidBitrate {
bitrate,
min: min_bitrate,
max: max_bitrate,
});
}
Ok(())
}
pub fn validate_buffer_sizes(
input_size: usize,
output_size: usize,
expected_ratio: f32,
) -> Result<()> {
let expected_output_size = (input_size as f32 * expected_ratio) as usize;
if output_size < expected_output_size {
return Err(CodecError::BufferTooSmall {
needed: expected_output_size,
actual: output_size,
});
}
Ok(())
}
pub fn validate_channel_alignment(samples: &[i16], channels: u8) -> Result<()> {
if samples.len() % channels as usize != 0 {
return Err(CodecError::invalid_format(format!(
"Sample count {} not divisible by channel count {}",
samples.len(),
channels
)));
}
Ok(())
}
pub fn validate_g711_frame(samples: &[i16], expected_frame_size: usize) -> Result<()> {
validate_samples(samples)?;
if samples.len() != expected_frame_size {
return Err(CodecError::InvalidFrameSize {
expected: expected_frame_size,
actual: samples.len(),
});
}
Ok(())
}
pub fn validate_g722_frame(samples: &[i16], expected_frame_size: usize) -> Result<()> {
validate_samples(samples)?;
if samples.len() != expected_frame_size {
return Err(CodecError::InvalidFrameSize {
expected: expected_frame_size,
actual: samples.len(),
});
}
if samples.len() % 2 != 0 {
return Err(CodecError::invalid_format(
"G.722 requires even number of samples for QMF processing",
));
}
Ok(())
}
pub fn validate_g729_frame(samples: &[i16]) -> Result<()> {
validate_samples(samples)?;
if samples.len() != 80 {
return Err(CodecError::InvalidFrameSize {
expected: 80,
actual: samples.len(),
});
}
Ok(())
}
pub fn validate_opus_frame(samples: &[i16], sample_rate: SampleRate) -> Result<()> {
validate_samples(samples)?;
let rate_hz = sample_rate.hz();
let frame_size = samples.len();
let valid_frame_sizes = match rate_hz {
8000 => vec![20, 40, 80, 160, 320, 480],
12000 => vec![30, 60, 120, 240, 480, 720],
16000 => vec![40, 80, 160, 320, 640, 960],
24000 => vec![60, 120, 240, 480, 960, 1440],
48000 => vec![120, 240, 480, 960, 1920, 2880],
_ => {
return Err(CodecError::InvalidSampleRate {
rate: rate_hz,
supported: vec![8000, 12000, 16000, 24000, 48000],
})
}
};
if !valid_frame_sizes.contains(&frame_size) {
return Err(CodecError::InvalidFrameSize {
expected: valid_frame_sizes[0],
actual: frame_size,
});
}
Ok(())
}
pub fn validate_buffer_compatibility(
input: &[i16],
output: &[u8],
compression_ratio: f32,
) -> Result<()> {
let expected_output_size = (input.len() as f32 * compression_ratio) as usize;
if output.len() < expected_output_size {
return Err(CodecError::BufferTooSmall {
needed: expected_output_size,
actual: output.len(),
});
}
Ok(())
}
pub fn validate_simd_alignment(data: &[i16]) -> Result<()> {
let ptr = data.as_ptr() as usize;
if ptr % 16 != 0 {
tracing::debug!("Data not aligned for SIMD operations, falling back to scalar");
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::SampleRate;
#[test]
fn test_validate_samples() {
let valid_samples = vec![0, 1000, -1000, 16000, -16000];
assert!(validate_samples(&valid_samples).is_ok());
let empty_samples: Vec<i16> = vec![];
assert!(validate_samples(&empty_samples).is_err());
}
#[test]
fn test_validate_encoded_data() {
let valid_data = vec![0u8, 127, 255, 64, 192];
assert!(validate_encoded_data(&valid_data).is_ok());
let empty_data: Vec<u8> = vec![];
assert!(validate_encoded_data(&empty_data).is_err());
let too_large_data = vec![0u8; 2 * 1024 * 1024]; assert!(validate_encoded_data(&too_large_data).is_err());
}
#[test]
fn test_validate_frame_size() {
assert!(validate_frame_size(CodecType::G711Pcmu, 160).is_ok());
assert!(validate_frame_size(CodecType::G711Pcmu, 123).is_err());
assert!(validate_frame_size(CodecType::G729, 80).is_ok());
assert!(validate_frame_size(CodecType::G729, 160).is_err());
}
#[test]
fn test_validate_sample_rate() {
assert!(validate_sample_rate(CodecType::G711Pcmu, SampleRate::Rate8000).is_ok());
assert!(validate_sample_rate(CodecType::G711Pcmu, SampleRate::Rate48000).is_err());
assert!(validate_sample_rate(CodecType::Opus, SampleRate::Rate8000).is_ok());
assert!(validate_sample_rate(CodecType::Opus, SampleRate::Rate48000).is_ok());
}
#[test]
fn test_validate_channels() {
assert!(validate_channels(CodecType::G711Pcmu, 1).is_ok());
assert!(validate_channels(CodecType::G711Pcmu, 2).is_err());
assert!(validate_channels(CodecType::Opus, 1).is_ok());
assert!(validate_channels(CodecType::Opus, 2).is_ok());
assert!(validate_channels(CodecType::Opus, 3).is_err());
}
#[test]
fn test_validate_bitrate() {
assert!(validate_bitrate(CodecType::G711Pcmu, 64000).is_ok());
assert!(validate_bitrate(CodecType::G711Pcmu, 128000).is_err());
assert!(validate_bitrate(CodecType::Opus, 32000).is_ok());
assert!(validate_bitrate(CodecType::Opus, 600000).is_err());
}
#[test]
fn test_validate_buffer_sizes() {
assert!(validate_buffer_sizes(160, 80, 0.5).is_ok());
assert!(validate_buffer_sizes(160, 40, 0.5).is_err());
}
#[test]
fn test_validate_channel_alignment() {
let mono_samples = vec![0, 1, 2, 3, 4]; assert!(validate_channel_alignment(&mono_samples, 1).is_ok());
assert!(validate_channel_alignment(&mono_samples, 2).is_err());
let stereo_samples = vec![0, 1, 2, 3]; assert!(validate_channel_alignment(&stereo_samples, 2).is_ok());
}
#[test]
fn test_codec_specific_validation() {
let g711_frame = vec![0i16; 160];
assert!(validate_g711_frame(&g711_frame, 160).is_ok());
assert!(validate_g711_frame(&g711_frame, 80).is_err());
let g729_frame = vec![0i16; 80];
assert!(validate_g729_frame(&g729_frame).is_ok());
let wrong_g729_frame = vec![0i16; 160];
assert!(validate_g729_frame(&wrong_g729_frame).is_err());
}
#[test]
fn test_buffer_compatibility() {
let input = vec![0i16; 160];
let output = vec![0u8; 80];
assert!(validate_buffer_compatibility(&input, &output, 0.5).is_ok());
let small_output = vec![0u8; 40];
assert!(validate_buffer_compatibility(&input, &small_output, 0.5).is_err());
}
}