codec_core/utils/
validation.rs

1//! Input validation utilities for codec operations
2
3use crate::error::{CodecError, Result};
4use crate::types::{CodecType, SampleRate};
5
6/// Validate audio samples for codec processing
7pub fn validate_samples(samples: &[i16]) -> Result<()> {
8    if samples.is_empty() {
9        return Err(CodecError::invalid_format("Input samples cannot be empty"));
10    }
11    
12    // Check for reasonable sample range
13    for (i, &sample) in samples.iter().enumerate() {
14        if sample.abs() > 32767 {
15            return Err(CodecError::invalid_format(format!(
16                "Sample at index {} out of range: {}",
17                i, sample
18            )));
19        }
20    }
21    
22    Ok(())
23}
24
25/// Validate encoded data for codec processing
26pub fn validate_encoded_data(data: &[u8]) -> Result<()> {
27    if data.is_empty() {
28        return Err(CodecError::invalid_format("Encoded data cannot be empty"));
29    }
30    
31    // Check for reasonable data size (not too large)
32    if data.len() > 1024 * 1024 {
33        return Err(CodecError::invalid_format(format!(
34            "Encoded data too large: {} bytes",
35            data.len()
36        )));
37    }
38    
39    Ok(())
40}
41
42/// Validate frame size for a specific codec
43pub fn validate_frame_size(codec_type: CodecType, frame_size: usize) -> Result<()> {
44    let expected_sizes = match codec_type {
45        CodecType::G711Pcmu | CodecType::G711Pcma => {
46            // G.711 commonly uses 10ms or 20ms frames at 8kHz
47            vec![80, 160, 240, 320]
48        }
49
50        CodecType::G729 | CodecType::G729A | CodecType::G729BA => {
51            // G.729/G.729A/G.729BA use fixed 10ms frames at 8kHz
52            vec![80]
53        }
54        CodecType::Opus => {
55            // Opus supports various frame sizes
56            vec![120, 240, 480, 960, 1920, 2880]
57        }
58    };
59    
60    if !expected_sizes.contains(&frame_size) {
61        return Err(CodecError::InvalidFrameSize {
62            expected: expected_sizes[0],
63            actual: frame_size,
64        });
65    }
66    
67    Ok(())
68}
69
70/// Validate sample rate for a specific codec
71pub fn validate_sample_rate(codec_type: CodecType, sample_rate: SampleRate) -> Result<()> {
72    let supported_rates = codec_type.supported_sample_rates();
73    let rate_hz = sample_rate.hz();
74    
75    if !supported_rates.contains(&rate_hz) {
76        return Err(CodecError::InvalidSampleRate {
77            rate: rate_hz,
78            supported: supported_rates.to_vec(),
79        });
80    }
81    
82    Ok(())
83}
84
85/// Validate channel count for a specific codec
86pub fn validate_channels(codec_type: CodecType, channels: u8) -> Result<()> {
87    let supported_channels = codec_type.supported_channels();
88    
89    if !supported_channels.contains(&channels) {
90        return Err(CodecError::InvalidChannelCount {
91            channels,
92            supported: supported_channels.to_vec(),
93        });
94    }
95    
96    Ok(())
97}
98
99/// Validate bitrate for a specific codec
100pub fn validate_bitrate(codec_type: CodecType, bitrate: u32) -> Result<()> {
101    let (min_bitrate, max_bitrate) = codec_type.bitrate_range();
102    
103    if bitrate < min_bitrate || bitrate > max_bitrate {
104        return Err(CodecError::InvalidBitrate {
105            bitrate,
106            min: min_bitrate,
107            max: max_bitrate,
108        });
109    }
110    
111    Ok(())
112}
113
114/// Validate buffer sizes for encoding/decoding operations
115pub fn validate_buffer_sizes(
116    input_size: usize,
117    output_size: usize,
118    expected_ratio: f32,
119) -> Result<()> {
120    let expected_output_size = (input_size as f32 * expected_ratio) as usize;
121    
122    if output_size < expected_output_size {
123        return Err(CodecError::BufferTooSmall {
124            needed: expected_output_size,
125            actual: output_size,
126        });
127    }
128    
129    Ok(())
130}
131
132/// Validate that frame samples are properly aligned for multi-channel audio
133pub fn validate_channel_alignment(samples: &[i16], channels: u8) -> Result<()> {
134    if samples.len() % channels as usize != 0 {
135        return Err(CodecError::invalid_format(format!(
136            "Sample count {} not divisible by channel count {}",
137            samples.len(),
138            channels
139        )));
140    }
141    
142    Ok(())
143}
144
145/// Validate G.711 specific parameters
146pub fn validate_g711_frame(samples: &[i16], expected_frame_size: usize) -> Result<()> {
147    validate_samples(samples)?;
148    
149    if samples.len() != expected_frame_size {
150        return Err(CodecError::InvalidFrameSize {
151            expected: expected_frame_size,
152            actual: samples.len(),
153        });
154    }
155    
156    Ok(())
157}
158
159/// Validate G.722 specific parameters
160pub fn validate_g722_frame(samples: &[i16], expected_frame_size: usize) -> Result<()> {
161    validate_samples(samples)?;
162    
163    if samples.len() != expected_frame_size {
164        return Err(CodecError::InvalidFrameSize {
165            expected: expected_frame_size,
166            actual: samples.len(),
167        });
168    }
169    
170    // G.722 requires even number of samples for QMF processing
171    if samples.len() % 2 != 0 {
172        return Err(CodecError::invalid_format(
173            "G.722 requires even number of samples for QMF processing",
174        ));
175    }
176    
177    Ok(())
178}
179
180/// Validate G.729 specific parameters
181pub fn validate_g729_frame(samples: &[i16]) -> Result<()> {
182    validate_samples(samples)?;
183    
184    // G.729 uses fixed 80-sample frames (10ms at 8kHz)
185    if samples.len() != 80 {
186        return Err(CodecError::InvalidFrameSize {
187            expected: 80,
188            actual: samples.len(),
189        });
190    }
191    
192    Ok(())
193}
194
195/// Validate Opus specific parameters
196pub fn validate_opus_frame(samples: &[i16], sample_rate: SampleRate) -> Result<()> {
197    validate_samples(samples)?;
198    
199    let rate_hz = sample_rate.hz();
200    let frame_size = samples.len();
201    
202    // Opus supports specific frame sizes based on sample rate
203    let valid_frame_sizes = match rate_hz {
204        8000 => vec![20, 40, 80, 160, 320, 480],
205        12000 => vec![30, 60, 120, 240, 480, 720],
206        16000 => vec![40, 80, 160, 320, 640, 960],
207        24000 => vec![60, 120, 240, 480, 960, 1440],
208        48000 => vec![120, 240, 480, 960, 1920, 2880],
209        _ => return Err(CodecError::InvalidSampleRate {
210            rate: rate_hz,
211            supported: vec![8000, 12000, 16000, 24000, 48000],
212        }),
213    };
214    
215    if !valid_frame_sizes.contains(&frame_size) {
216        return Err(CodecError::InvalidFrameSize {
217            expected: valid_frame_sizes[0],
218            actual: frame_size,
219        });
220    }
221    
222    Ok(())
223}
224
225/// Validate that two buffers have compatible sizes for processing
226pub fn validate_buffer_compatibility(
227    input: &[i16],
228    output: &[u8],
229    compression_ratio: f32,
230) -> Result<()> {
231    let expected_output_size = (input.len() as f32 * compression_ratio) as usize;
232    
233    if output.len() < expected_output_size {
234        return Err(CodecError::BufferTooSmall {
235            needed: expected_output_size,
236            actual: output.len(),
237        });
238    }
239    
240    Ok(())
241}
242
243/// Validate memory alignment for SIMD operations
244pub fn validate_simd_alignment(data: &[i16]) -> Result<()> {
245    let ptr = data.as_ptr() as usize;
246    
247    // Check for 16-byte alignment (required for SSE)
248    if ptr % 16 != 0 {
249        tracing::debug!("Data not aligned for SIMD operations, falling back to scalar");
250    }
251    
252    Ok(())
253}
254
255#[cfg(test)]
256mod tests {
257    use super::*;
258    use crate::types::SampleRate;
259
260    #[test]
261    fn test_validate_samples() {
262        let valid_samples = vec![0, 1000, -1000, 16000, -16000];
263        assert!(validate_samples(&valid_samples).is_ok());
264        
265        let empty_samples: Vec<i16> = vec![];
266        assert!(validate_samples(&empty_samples).is_err());
267    }
268
269    #[test]
270    fn test_validate_encoded_data() {
271        let valid_data = vec![0u8, 127, 255, 64, 192];
272        assert!(validate_encoded_data(&valid_data).is_ok());
273        
274        let empty_data: Vec<u8> = vec![];
275        assert!(validate_encoded_data(&empty_data).is_err());
276        
277        let too_large_data = vec![0u8; 2 * 1024 * 1024]; // 2MB
278        assert!(validate_encoded_data(&too_large_data).is_err());
279    }
280
281    #[test]
282    fn test_validate_frame_size() {
283        // G.711 valid frame sizes
284        assert!(validate_frame_size(CodecType::G711Pcmu, 160).is_ok());
285        assert!(validate_frame_size(CodecType::G711Pcmu, 123).is_err());
286        
287        // G.729 fixed frame size
288        assert!(validate_frame_size(CodecType::G729, 80).is_ok());
289        assert!(validate_frame_size(CodecType::G729, 160).is_err());
290    }
291
292    #[test]
293    fn test_validate_sample_rate() {
294        // G.711 supports only 8kHz
295        assert!(validate_sample_rate(CodecType::G711Pcmu, SampleRate::Rate8000).is_ok());
296        assert!(validate_sample_rate(CodecType::G711Pcmu, SampleRate::Rate48000).is_err());
297        
298        // Opus supports multiple rates
299        assert!(validate_sample_rate(CodecType::Opus, SampleRate::Rate8000).is_ok());
300        assert!(validate_sample_rate(CodecType::Opus, SampleRate::Rate48000).is_ok());
301    }
302
303    #[test]
304    fn test_validate_channels() {
305        // G.711 supports only mono
306        assert!(validate_channels(CodecType::G711Pcmu, 1).is_ok());
307        assert!(validate_channels(CodecType::G711Pcmu, 2).is_err());
308        
309        // Opus supports mono and stereo
310        assert!(validate_channels(CodecType::Opus, 1).is_ok());
311        assert!(validate_channels(CodecType::Opus, 2).is_ok());
312        assert!(validate_channels(CodecType::Opus, 3).is_err());
313    }
314
315    #[test]
316    fn test_validate_bitrate() {
317        // G.711 has fixed bitrate
318        assert!(validate_bitrate(CodecType::G711Pcmu, 64000).is_ok());
319        assert!(validate_bitrate(CodecType::G711Pcmu, 128000).is_err());
320        
321        // Opus has variable bitrate
322        assert!(validate_bitrate(CodecType::Opus, 32000).is_ok());
323        assert!(validate_bitrate(CodecType::Opus, 600000).is_err());
324    }
325
326    #[test]
327    fn test_validate_buffer_sizes() {
328        // G.711 has 1:1 compression ratio (16-bit to 8-bit)
329        assert!(validate_buffer_sizes(160, 80, 0.5).is_ok());
330        assert!(validate_buffer_sizes(160, 40, 0.5).is_err());
331    }
332
333    #[test]
334    fn test_validate_channel_alignment() {
335        let mono_samples = vec![0, 1, 2, 3, 4]; // 5 samples
336        assert!(validate_channel_alignment(&mono_samples, 1).is_ok());
337        assert!(validate_channel_alignment(&mono_samples, 2).is_err());
338        
339        let stereo_samples = vec![0, 1, 2, 3]; // 4 samples = 2 stereo pairs
340        assert!(validate_channel_alignment(&stereo_samples, 2).is_ok());
341    }
342
343    #[test]
344    fn test_codec_specific_validation() {
345        // G.711 frame validation
346        let g711_frame = vec![0i16; 160];
347        assert!(validate_g711_frame(&g711_frame, 160).is_ok());
348        assert!(validate_g711_frame(&g711_frame, 80).is_err());
349        
350        // G.729 frame validation
351        let g729_frame = vec![0i16; 80];
352        assert!(validate_g729_frame(&g729_frame).is_ok());
353        
354        let wrong_g729_frame = vec![0i16; 160];
355        assert!(validate_g729_frame(&wrong_g729_frame).is_err());
356    }
357
358    #[test]
359    fn test_buffer_compatibility() {
360        let input = vec![0i16; 160];
361        let output = vec![0u8; 80];
362        
363        // G.711 compression ratio: 0.5 (16-bit to 8-bit)
364        assert!(validate_buffer_compatibility(&input, &output, 0.5).is_ok());
365        
366        let small_output = vec![0u8; 40];
367        assert!(validate_buffer_compatibility(&input, &small_output, 0.5).is_err());
368    }
369}