1use crate::error::{CodecError, Result};
4use crate::types::{CodecType, SampleRate};
5
6pub fn validate_samples(samples: &[i16]) -> Result<()> {
12 if samples.is_empty() {
13 return Err(CodecError::invalid_format("Input samples cannot be empty"));
14 }
15 Ok(())
18}
19
20pub fn validate_encoded_data(data: &[u8]) -> Result<()> {
26 if data.is_empty() {
27 return Err(CodecError::invalid_format("Encoded data cannot be empty"));
28 }
29
30 if data.len() > 1024 * 1024 {
32 return Err(CodecError::invalid_format(format!(
33 "Encoded data too large: {} bytes",
34 data.len()
35 )));
36 }
37
38 Ok(())
39}
40
41pub fn validate_frame_size(codec_type: CodecType, frame_size: usize) -> Result<()> {
47 let expected_sizes = match codec_type {
48 CodecType::G711Pcmu | CodecType::G711Pcma => {
49 vec![80, 160, 240, 320]
51 }
52
53 CodecType::G729 | CodecType::G729A | CodecType::G729BA => {
54 vec![80]
56 }
57 CodecType::Opus => {
58 vec![120, 240, 480, 960, 1920, 2880]
60 }
61 CodecType::AmrNb => vec![160],
63 CodecType::AmrWb => vec![320],
64 };
65
66 if !expected_sizes.contains(&frame_size) {
67 return Err(CodecError::InvalidFrameSize {
68 expected: expected_sizes[0],
69 actual: frame_size,
70 });
71 }
72
73 Ok(())
74}
75
76pub fn validate_sample_rate(codec_type: CodecType, sample_rate: SampleRate) -> Result<()> {
82 let supported_rates = codec_type.supported_sample_rates();
83 let rate_hz = sample_rate.hz();
84
85 if !supported_rates.contains(&rate_hz) {
86 return Err(CodecError::InvalidSampleRate {
87 rate: rate_hz,
88 supported: supported_rates.to_vec(),
89 });
90 }
91
92 Ok(())
93}
94
95pub fn validate_channels(codec_type: CodecType, channels: u8) -> Result<()> {
101 let supported_channels = codec_type.supported_channels();
102
103 if !supported_channels.contains(&channels) {
104 return Err(CodecError::InvalidChannelCount {
105 channels,
106 supported: supported_channels.to_vec(),
107 });
108 }
109
110 Ok(())
111}
112
113pub const fn validate_bitrate(codec_type: CodecType, bitrate: u32) -> Result<()> {
119 let (min_bitrate, max_bitrate) = codec_type.bitrate_range();
120
121 if bitrate < min_bitrate || bitrate > max_bitrate {
122 return Err(CodecError::InvalidBitrate {
123 bitrate,
124 min: min_bitrate,
125 max: max_bitrate,
126 });
127 }
128
129 Ok(())
130}
131
132#[allow(
139 clippy::cast_possible_truncation,
140 clippy::cast_precision_loss,
141 clippy::cast_sign_loss
142)]
143pub fn validate_buffer_sizes(
144 input_size: usize,
145 output_size: usize,
146 expected_ratio: f32,
147) -> Result<()> {
148 let expected_output_size = (input_size as f32 * expected_ratio) as usize;
149
150 if output_size < expected_output_size {
151 return Err(CodecError::BufferTooSmall {
152 needed: expected_output_size,
153 actual: output_size,
154 });
155 }
156
157 Ok(())
158}
159
160#[allow(clippy::manual_is_multiple_of)]
166pub fn validate_channel_alignment(samples: &[i16], channels: u8) -> Result<()> {
167 if samples.len() % usize::from(channels) != 0 {
168 return Err(CodecError::invalid_format(format!(
169 "Sample count {} not divisible by channel count {}",
170 samples.len(),
171 channels
172 )));
173 }
174
175 Ok(())
176}
177
178pub fn validate_g711_frame(samples: &[i16], expected_frame_size: usize) -> Result<()> {
184 validate_samples(samples)?;
185
186 if samples.len() != expected_frame_size {
187 return Err(CodecError::InvalidFrameSize {
188 expected: expected_frame_size,
189 actual: samples.len(),
190 });
191 }
192
193 Ok(())
194}
195
196pub fn validate_g722_frame(samples: &[i16], expected_frame_size: usize) -> Result<()> {
203 validate_samples(samples)?;
204
205 if samples.len() != expected_frame_size {
206 return Err(CodecError::InvalidFrameSize {
207 expected: expected_frame_size,
208 actual: samples.len(),
209 });
210 }
211
212 if !samples.len().is_multiple_of(2) {
214 return Err(CodecError::invalid_format(
215 "G.722 requires even number of samples for QMF processing",
216 ));
217 }
218
219 Ok(())
220}
221
222pub fn validate_g729_frame(samples: &[i16]) -> Result<()> {
228 validate_samples(samples)?;
229
230 if samples.len() != 80 {
232 return Err(CodecError::InvalidFrameSize {
233 expected: 80,
234 actual: samples.len(),
235 });
236 }
237
238 Ok(())
239}
240
241pub fn validate_opus_frame(samples: &[i16], sample_rate: SampleRate) -> Result<()> {
248 validate_samples(samples)?;
249
250 let rate_hz = sample_rate.hz();
251 let frame_size = samples.len();
252
253 let valid_frame_sizes = match rate_hz {
255 8000 => vec![20, 40, 80, 160, 320, 480],
256 12000 => vec![30, 60, 120, 240, 480, 720],
257 16000 => vec![40, 80, 160, 320, 640, 960],
258 24000 => vec![60, 120, 240, 480, 960, 1440],
259 48000 => vec![120, 240, 480, 960, 1920, 2880],
260 _ => {
261 return Err(CodecError::InvalidSampleRate {
262 rate: rate_hz,
263 supported: vec![8000, 12000, 16000, 24000, 48000],
264 });
265 }
266 };
267
268 if !valid_frame_sizes.contains(&frame_size) {
269 return Err(CodecError::InvalidFrameSize {
270 expected: valid_frame_sizes[0],
271 actual: frame_size,
272 });
273 }
274
275 Ok(())
276}
277
278#[allow(
285 clippy::cast_possible_truncation,
286 clippy::cast_precision_loss,
287 clippy::cast_sign_loss
288)]
289pub fn validate_buffer_compatibility(
290 input: &[i16],
291 output: &[u8],
292 compression_ratio: f32,
293) -> Result<()> {
294 let expected_output_size = (input.len() as f32 * compression_ratio) as usize;
295
296 if output.len() < expected_output_size {
297 return Err(CodecError::BufferTooSmall {
298 needed: expected_output_size,
299 actual: output.len(),
300 });
301 }
302
303 Ok(())
304}
305
306pub fn validate_simd_alignment(data: &[i16]) -> Result<()> {
313 let ptr = data.as_ptr() as usize;
314
315 if !ptr.is_multiple_of(16) {
317 tracing::debug!("Data not aligned for SIMD operations, falling back to scalar");
318 }
319
320 Ok(())
321}
322
323#[cfg(test)]
324mod tests {
325 use super::*;
326 use crate::types::SampleRate;
327
328 #[test]
329 fn test_validate_samples() {
330 let valid_samples = vec![0, 1000, -1000, 16000, -16000];
331 assert!(validate_samples(&valid_samples).is_ok());
332
333 let empty_samples: Vec<i16> = vec![];
334 assert!(validate_samples(&empty_samples).is_err());
335 }
336
337 #[test]
338 fn test_validate_encoded_data() {
339 let valid_data = vec![0u8, 127, 255, 64, 192];
340 assert!(validate_encoded_data(&valid_data).is_ok());
341
342 let empty_data: Vec<u8> = vec![];
343 assert!(validate_encoded_data(&empty_data).is_err());
344
345 let too_large_data = vec![0u8; 2 * 1024 * 1024]; assert!(validate_encoded_data(&too_large_data).is_err());
347 }
348
349 #[test]
350 fn test_validate_frame_size() {
351 assert!(validate_frame_size(CodecType::G711Pcmu, 160).is_ok());
353 assert!(validate_frame_size(CodecType::G711Pcmu, 123).is_err());
354
355 assert!(validate_frame_size(CodecType::G729, 80).is_ok());
357 assert!(validate_frame_size(CodecType::G729, 160).is_err());
358 }
359
360 #[test]
361 fn test_validate_sample_rate() {
362 assert!(validate_sample_rate(CodecType::G711Pcmu, SampleRate::Rate8000).is_ok());
364 assert!(validate_sample_rate(CodecType::G711Pcmu, SampleRate::Rate48000).is_err());
365
366 assert!(validate_sample_rate(CodecType::Opus, SampleRate::Rate8000).is_ok());
368 assert!(validate_sample_rate(CodecType::Opus, SampleRate::Rate48000).is_ok());
369 }
370
371 #[test]
372 fn test_validate_channels() {
373 assert!(validate_channels(CodecType::G711Pcmu, 1).is_ok());
375 assert!(validate_channels(CodecType::G711Pcmu, 2).is_err());
376
377 assert!(validate_channels(CodecType::Opus, 1).is_ok());
379 assert!(validate_channels(CodecType::Opus, 2).is_ok());
380 assert!(validate_channels(CodecType::Opus, 3).is_err());
381 }
382
383 #[test]
384 fn test_validate_bitrate() {
385 assert!(validate_bitrate(CodecType::G711Pcmu, 64_000).is_ok());
387 assert!(validate_bitrate(CodecType::G711Pcmu, 128_000).is_err());
388
389 assert!(validate_bitrate(CodecType::Opus, 32_000).is_ok());
391 assert!(validate_bitrate(CodecType::Opus, 600_000).is_err());
392 }
393
394 #[test]
395 fn test_validate_buffer_sizes() {
396 assert!(validate_buffer_sizes(160, 80, 0.5).is_ok());
398 assert!(validate_buffer_sizes(160, 40, 0.5).is_err());
399 }
400
401 #[test]
402 fn test_validate_channel_alignment() {
403 let mono_samples = vec![0, 1, 2, 3, 4]; assert!(validate_channel_alignment(&mono_samples, 1).is_ok());
405 assert!(validate_channel_alignment(&mono_samples, 2).is_err());
406
407 let stereo_samples = vec![0, 1, 2, 3]; assert!(validate_channel_alignment(&stereo_samples, 2).is_ok());
409 }
410
411 #[test]
412 fn test_codec_specific_validation() {
413 let g711_frame = vec![0i16; 160];
415 assert!(validate_g711_frame(&g711_frame, 160).is_ok());
416 assert!(validate_g711_frame(&g711_frame, 80).is_err());
417
418 let g729_frame = vec![0i16; 80];
420 assert!(validate_g729_frame(&g729_frame).is_ok());
421
422 let wrong_g729_frame = vec![0i16; 160];
423 assert!(validate_g729_frame(&wrong_g729_frame).is_err());
424 }
425
426 #[test]
427 fn test_buffer_compatibility() {
428 let input = vec![0i16; 160];
429 let output = vec![0u8; 80];
430
431 assert!(validate_buffer_compatibility(&input, &output, 0.5).is_ok());
433
434 let small_output = vec![0u8; 40];
435 assert!(validate_buffer_compatibility(&input, &small_output, 0.5).is_err());
436 }
437}