1use ps_buffer::{Buffer, BufferError, ByteIteratorIntoBuffer, ToBuffer};
2
3use crate::cow::Cow;
4use crate::error::{
5 PolynomialError, RSConstructorError, RSDecodeError, RSEncodeError, RSGenerateParityError,
6};
7use crate::finite_field::{div, inv, mul, ANTILOG_TABLE};
8use crate::polynomial::{
9 poly_div, poly_eval, poly_eval_deriv, poly_eval_detached, poly_mul, poly_rem, poly_sub,
10};
11use crate::{Codeword, RSComputeErrorsError, RSEuclideanError, RSValidationError};
12
13pub struct ReedSolomon {
14 parity: u8,
15}
16
17impl ReedSolomon {
18 pub const fn new(parity: u8) -> Result<Self, RSConstructorError> {
22 use RSConstructorError::ParityTooHigh;
23
24 if parity > 127 {
25 return Err(ParityTooHigh);
26 }
27
28 let codec = Self { parity };
29
30 Ok(codec)
31 }
32
33 #[inline]
34 #[must_use]
35 pub const fn parity(&self) -> u8 {
36 self.parity
37 }
38
39 #[inline]
40 #[must_use]
41 pub const fn parity_bytes(&self) -> u8 {
42 self.parity() << 1
43 }
44
45 pub fn generate_parity(&self, message: &[u8]) -> Result<Buffer, RSGenerateParityError> {
50 let num_parity = usize::from(self.parity_bytes());
51 let g = generate_generator_poly(num_parity)?;
52 let dividend = vec![0u8; num_parity]
53 .into_iter()
54 .chain(message.iter().copied())
55 .into_buffer()?;
56 let mut r = poly_rem(dividend, &g)?;
57 if r.len() != num_parity {
58 r.resize(num_parity, 0)?;
59 }
60 Ok(r)
61 }
62
63 pub fn encode(&self, message: &[u8]) -> Result<Buffer, RSEncodeError> {
68 let mut buffer = Buffer::with_capacity(message.len() + usize::from(self.parity_bytes()))?;
69
70 buffer.extend_from_slice(&self.generate_parity(message)?)?;
71 buffer.extend_from_slice(message)?;
72
73 Ok(buffer)
74 }
75
76 pub fn compute_syndromes(
80 num_parity_bytes: impl Into<usize>,
81 received: &[u8],
82 ) -> Result<Buffer, BufferError> {
83 (0..num_parity_bytes.into())
84 .map(|i| poly_eval(received, ANTILOG_TABLE[i + 1].get()))
85 .into_buffer()
86 }
87
88 pub fn compute_syndromes_detached(parity: &[u8], data: &[u8]) -> Result<Buffer, BufferError> {
92 (0..parity.len())
93 .map(|i| poly_eval_detached(parity, data, ANTILOG_TABLE[i + 1].get()))
94 .into_buffer()
95 }
96
97 pub fn validate(&self, received: &[u8]) -> Result<Option<Buffer>, RSValidationError> {
101 let syndromes = Self::compute_syndromes(self.parity_bytes(), received)?;
102
103 if syndromes.iter().all(|&s| s == 0) {
104 Ok(None)
105 } else {
106 Ok(Some(syndromes))
107 }
108 }
109
110 #[allow(clippy::cast_possible_truncation)]
114 pub fn validate_detached(
115 parity: &[u8],
116 data: &[u8],
117 ) -> Result<Option<Buffer>, RSValidationError> {
118 let syndromes = Self::compute_syndromes_detached(parity, data)?;
119
120 if syndromes.iter().all(|&s| s == 0) {
121 Ok(None)
122 } else {
123 Ok(Some(syndromes))
124 }
125 }
126
127 #[inline]
128 pub fn compute_errors(
132 &self,
133 length: usize,
134 syndromes: &[u8],
135 ) -> Result<Option<Buffer>, RSComputeErrorsError> {
136 Self::compute_errors_detached(self.parity(), length, syndromes)
137 }
138
139 fn compute_errors_detached(
150 parity: impl Into<usize>,
151 length: usize,
152 syndromes: &[u8],
153 ) -> Result<Option<Buffer>, RSComputeErrorsError> {
154 if syndromes.iter().all(|&syndrome| syndrome == 0) {
155 return Ok(None);
156 }
157
158 let parity = parity.into();
159
160 let (mut sigma, mut omega) = euclidean_for_rs(syndromes, parity)?;
162
163 let scale = inv(sigma[0])?.get();
165 sigma.iter_mut().for_each(|x| *x = mul(*x, scale));
166 omega.iter_mut().for_each(|x| *x = mul(*x, scale));
167
168 let error_positions: Vec<usize> = (0..length)
170 .filter(|&m| {
171 let x = if m == 0 {
172 1
173 } else {
174 ANTILOG_TABLE[(255 - m) % 255].get()
175 };
176 poly_eval(&sigma, x) == 0
177 })
178 .collect();
179
180 if error_positions.len() > parity || error_positions.is_empty() {
181 return Err(RSComputeErrorsError::TooManyErrors);
182 }
183
184 let mut errors = Buffer::alloc(length)?;
186 for &j in &error_positions {
187 let x = if j == 0 {
188 1
189 } else {
190 ANTILOG_TABLE[(255 - j) % 255].get()
191 };
192 let omega_x = poly_eval(&omega, x);
193 let sigma_deriv_x = poly_eval_deriv(&sigma, x);
194 if sigma_deriv_x == 0 {
195 return Err(RSComputeErrorsError::ZeroErrorLocatorDerivative);
196 }
197 errors[j] = div(omega_x, sigma_deriv_x)?;
198 }
199
200 Ok(Some(errors))
201 }
202
203 pub fn correct<'lt>(&self, received: &'lt [u8]) -> Result<Cow<'lt>, RSDecodeError> {
208 let syndromes = Self::compute_syndromes(self.parity_bytes(), received)?;
209
210 let errors = match self.compute_errors(received.len(), &syndromes)? {
211 None => return Ok(Cow::Borrowed(received)),
212 Some(errors) => errors,
213 };
214
215 let mut corrected = Buffer::from_slice(received)?;
217
218 Self::apply_corrections(&mut corrected, &errors);
219
220 match Self::validate(self, &corrected)? {
221 None => Ok(corrected.into()),
222 Some(_) => Err(RSDecodeError::TooManyErrors),
223 }
224 }
225 pub fn correct_in_place(&self, received: &mut [u8]) -> Result<(), RSDecodeError> {
231 let syndromes = Self::compute_syndromes(self.parity_bytes(), received)?;
232
233 let errors = match self.compute_errors(received.len(), &syndromes)? {
234 None => return Ok(()),
235 Some(errors) => errors,
236 };
237
238 Self::apply_corrections(received, errors);
239
240 match Self::validate(self, received)? {
241 None => Ok(()),
242 Some(_) => Err(RSDecodeError::TooManyErrors),
243 }
244 }
245
246 pub fn decode<'lt>(&self, received: &'lt [u8]) -> Result<Codeword<'lt>, RSDecodeError> {
251 let corrected = self.correct(received)?;
252 let codeword = Codeword {
253 codeword: corrected,
254 range: self.parity_bytes().into()..received.len(),
255 };
256
257 Ok(codeword)
258 }
259
260 pub fn correct_detached<'lt>(
265 parity: &[u8],
266 data: &'lt [u8],
267 ) -> Result<Codeword<'lt>, RSDecodeError> {
268 let parity_bytes = parity.len();
269 let num_parity = parity_bytes >> 1;
270 let length = parity_bytes + data.len();
271 let rs = Self::new(num_parity.try_into()?)?;
272
273 let syndromes = Self::compute_syndromes_detached(parity, data)?;
274
275 let errors = match Self::compute_errors_detached(num_parity, length, &syndromes)? {
276 None => return Ok(data.into()),
277 Some(errors) => errors,
278 };
279
280 let mut corrected = Buffer::with_capacity(parity.len() + data.len())?;
282
283 corrected.extend_from_slice(parity)?;
284 corrected.extend_from_slice(data)?;
285
286 Self::apply_corrections(&mut corrected, &errors);
287
288 if let Some(_syndromes) = rs.validate(&corrected)? {
289 return Err(RSDecodeError::TooManyErrors);
290 };
291
292 let range = parity.len()..corrected.len();
293 let codeword = Cow::Owned(corrected);
294 let codeword = Codeword { codeword, range };
295
296 Ok(codeword)
297 }
298
299 #[inline]
303 pub fn correct_detached_data_in_place(
304 parity: &[u8],
305 data: &mut [u8],
306 ) -> Result<(), RSDecodeError> {
307 Self::correct_detached_in_place(&mut parity.to_buffer()?, data)
308 }
309
310 pub fn correct_detached_in_place(
315 parity: &mut [u8],
316 data: &mut [u8],
317 ) -> Result<(), RSDecodeError> {
318 let parity_bytes = parity.len();
319 let num_parity = parity_bytes >> 1;
320 let length = parity_bytes + data.len();
321
322 let syndromes = Self::compute_syndromes_detached(parity, data)?;
323
324 let errors = match Self::compute_errors_detached(num_parity, length, &syndromes)? {
325 None => return Ok(()),
326 Some(errors) => errors,
327 };
328
329 Self::apply_corrections_detached(parity, data, &errors);
330
331 match Self::validate_detached(parity, data)? {
332 None => Ok(()),
333 Some(_) => Err(RSDecodeError::TooManyErrors),
334 }
335 }
336
337 pub fn apply_corrections(target: &mut [u8], corrections: impl AsRef<[u8]>) {
338 target
339 .iter_mut()
340 .zip(corrections.as_ref().iter())
341 .for_each(|(target, correction)| *target ^= *correction);
342 }
343
344 pub fn apply_corrections_detached(
345 parity: &mut [u8],
346 data: &mut [u8],
347 corrections: impl AsRef<[u8]>,
348 ) {
349 let corrections = corrections.as_ref();
350 Self::apply_corrections(parity, &corrections[..parity.len()]);
351 Self::apply_corrections(data, &corrections[parity.len()..]);
352 }
353}
354
355#[allow(clippy::needless_range_loop)]
357fn generate_generator_poly(num_roots: usize) -> Result<Buffer, PolynomialError> {
358 let mut g = Buffer::from_slice([1])?;
359 for i in 1..=num_roots {
360 let root = ANTILOG_TABLE[i].get();
361 g = poly_mul(&g, &[root, 1])?; }
363 Ok(g)
364}
365
366fn euclidean_for_rs(s: &[u8], t: usize) -> Result<(Buffer, Buffer), RSEuclideanError> {
368 let mut r0 = Buffer::alloc(2 * t + 1)?;
369 r0[2 * t] = 1; let mut r1 = s.to_buffer()?;
371 let mut t0 = Buffer::from_slice([0])?;
372 let mut t1 = Buffer::from_slice([1])?;
373 while degree(&r1).unwrap_or(0) >= t {
374 let (q, r) = poly_div(&r0, &r1)?;
375 let new_t1 = poly_sub(&t0, &poly_mul(&q, &t1)?)?;
376 t0 = t1;
377 t1 = new_t1;
378 r0 = r1;
379 r1 = r;
380 }
381 Ok((t1, r1))
382}
383
384fn degree(poly: &[u8]) -> Option<usize> {
385 poly.iter().rposition(|&x| x != 0)
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391 use ps_buffer::ToBuffer;
392 use thiserror::Error;
393
394 #[derive(Error, Debug)]
395 enum TestError {
396 #[error(transparent)]
397 Buffer(#[from] ps_buffer::BufferError),
398 #[error(transparent)]
399 Polynomial(#[from] PolynomialError),
400 #[error(transparent)]
401 RSConstructor(#[from] RSConstructorError),
402 #[error(transparent)]
403 RSEncode(#[from] RSEncodeError),
404 #[error(transparent)]
405 RSDecode(#[from] RSDecodeError),
406 #[error(transparent)]
407 RSGenerateParity(#[from] RSGenerateParityError),
408 #[error(transparent)]
409 RSValidation(#[from] RSValidationError),
410 }
411
412 #[test]
413 fn test_encode_decode() -> Result<(), TestError> {
414 let rs = ReedSolomon::new(4)?;
415 let message = b"Hello, World!".to_buffer()?;
416 let encoded = rs.encode(&message)?;
417
418 let mut corrupted = Buffer::with_capacity(encoded.len())?;
419 corrupted.extend_from_slice(&encoded)?;
420 corrupted[2] ^= 1;
421
422 let decoded = rs.decode(&corrupted)?;
423 assert_eq!(&decoded[..], &message[..]);
424
425 Ok(())
426 }
427
428 #[test]
429 fn test_too_many_errors() -> Result<(), TestError> {
430 let rs = ReedSolomon::new(2)?;
431 let message = b"Hello, World!".to_buffer()?;
432 let encoded = rs.encode(&message)?;
433
434 let mut corrupted = Buffer::with_capacity(encoded.len())?;
435 corrupted.extend_from_slice(&encoded)?;
436 corrupted[5] ^= 113;
437 corrupted[6] ^= 59;
438 corrupted[7] ^= 3;
439
440 assert_eq!(
441 rs.decode(&corrupted),
442 Err(RSDecodeError::RSComputeErrorsError(
443 RSComputeErrorsError::TooManyErrors
444 ))
445 );
446
447 Ok(())
448 }
449
450 #[test]
451 fn test_validate() -> Result<(), TestError> {
452 let rs = ReedSolomon::new(4)?;
453 let message = b"Hello, World!".to_buffer()?;
454 let encoded = rs.encode(&message)?;
455
456 assert!(rs.validate(&encoded)?.is_none());
457
458 let mut corrupted = Buffer::with_capacity(encoded.len())?;
459 corrupted.extend_from_slice(&encoded)?;
460 corrupted[2] ^= 1;
461
462 assert!(rs.validate(&corrupted)?.is_some());
463
464 Ok(())
465 }
466
467 #[test]
468 fn test_validate_detached() -> Result<(), TestError> {
469 let rs = ReedSolomon::new(4)?;
470 let message = b"Hello, World!".to_buffer()?;
471 let parity = rs.generate_parity(&message)?;
472
473 assert!(ReedSolomon::validate_detached(&parity, &message)?.is_none());
474
475 let mut corrupted = Buffer::with_capacity(message.len())?;
476 corrupted.extend_from_slice(&message)?;
477 corrupted[2] ^= 1;
478
479 assert!(ReedSolomon::validate_detached(&parity, &corrupted)?.is_some());
480
481 Ok(())
482 }
483
484 #[test]
485 fn test_correct_both_detached_in_place() -> Result<(), TestError> {
486 let rs = ReedSolomon::new(4)?;
487 let message = b"Hello, World!".to_buffer()?;
488 let mut data = Buffer::with_capacity(message.len())?;
489 data.extend_from_slice(&message)?;
490 let mut parity = rs.generate_parity(&message)?;
491
492 data[2] ^= 1;
493
494 ReedSolomon::correct_detached_in_place(&mut parity, &mut data)?;
495
496 assert_eq!(data.as_slice(), message.as_slice());
497 assert_eq!(parity, rs.generate_parity(&message)?);
498
499 Ok(())
500 }
501
502 #[test]
503 fn test_new() {
504 assert!(ReedSolomon::new(0).is_ok());
505 assert!(ReedSolomon::new(10).is_ok());
506 assert!(ReedSolomon::new(127).is_ok());
507 assert!(ReedSolomon::new(128).is_err());
508 }
509
510 #[test]
511 fn test_parity() -> Result<(), RSConstructorError> {
512 let rs = ReedSolomon::new(8)?;
513 assert_eq!(rs.parity(), 8);
514 Ok(())
515 }
516
517 #[test]
518 fn test_parity_bytes() -> Result<(), RSConstructorError> {
519 let rs = ReedSolomon::new(8)?;
520 assert_eq!(rs.parity_bytes(), 16);
521 Ok(())
522 }
523
524 #[test]
525 fn test_generate_parity_no_errors() -> Result<(), TestError> {
526 let rs = ReedSolomon::new(4)?;
527 let message = b"Test".to_buffer()?;
528 let parity = rs.generate_parity(&message)?;
529 assert_eq!(parity.len(), 8); Ok(())
531 }
532
533 #[test]
534 fn test_generate_parity_empty_message() -> Result<(), TestError> {
535 let rs = ReedSolomon::new(2)?;
536 let message = b"".to_buffer()?;
537 let parity = rs.generate_parity(&message)?;
538 assert_eq!(parity.len(), 4);
539 assert_eq!(parity.as_slice(), &[0, 0, 0, 0]);
540 Ok(())
541 }
542
543 #[test]
544 fn test_encode_correct_no_errors() -> Result<(), TestError> {
545 let rs = ReedSolomon::new(2)?;
546 let message = b"Data".to_buffer()?;
547 let encoded = rs.encode(&message)?;
548 let decoded = rs.decode(&encoded)?;
549 assert_eq!(&decoded[..], &message[..]);
550 Ok(())
551 }
552
553 #[test]
554 fn test_encode_correct_one_error() -> Result<(), TestError> {
555 let rs = ReedSolomon::new(3)?;
556 let message = b"Example".to_buffer()?;
557 let encoded = rs.encode(&message)?;
558 let mut corrupted = encoded.clone()?;
559 corrupted[1] ^= 0b1010_1010;
560 let decoded = rs.decode(&corrupted)?;
561 assert_eq!(&decoded[..], &message[..]);
562 Ok(())
563 }
564
565 #[test]
566 fn test_encode_correct_multiple_recoverable_errors() -> Result<(), TestError> {
567 let rs = ReedSolomon::new(4)?;
568 let message = b"Multiple".to_buffer()?;
569 let encoded = rs.encode(&message)?;
570 let mut corrupted = encoded.clone()?;
571 corrupted[3] ^= 0b0011_0011;
572 corrupted[7] ^= 0b1100_1100;
573 let decoded = rs.decode(&corrupted)?;
574 assert_eq!(&decoded[..], &message[..]);
575 Ok(())
576 }
577
578 #[test]
579 fn test_compute_syndromes_no_errors() -> Result<(), TestError> {
580 let rs = ReedSolomon::new(2)?;
581 let message = b"Syndrome".to_buffer()?;
582 let encoded = rs.encode(&message)?;
583 let syndromes = ReedSolomon::compute_syndromes(rs.parity_bytes(), &encoded)?;
584 assert!(syndromes.iter().all(|&s| s == 0));
585 Ok(())
586 }
587
588 #[test]
589 fn test_compute_syndromes_with_errors() -> Result<(), TestError> {
590 let rs = ReedSolomon::new(2)?;
591 let message = b"Syndrome".to_buffer()?;
592 let encoded = rs.encode(&message)?;
593 let mut corrupted = encoded.clone()?;
594 corrupted[0] ^= 1;
595 let syndromes = ReedSolomon::compute_syndromes(rs.parity_bytes(), &corrupted)?;
596 assert!(syndromes.iter().any(|&s| s != 0));
597 Ok(())
598 }
599
600 #[test]
601 fn test_compute_syndromes_detached_no_errors() -> Result<(), TestError> {
602 let rs = ReedSolomon::new(3)?;
603 let message = b"Detached".to_buffer()?;
604 let parity = rs.generate_parity(&message)?;
605 let syndromes = ReedSolomon::compute_syndromes_detached(&parity, &message)?;
606 assert!(syndromes.iter().all(|&s| s == 0));
607 Ok(())
608 }
609
610 #[test]
611 fn test_compute_syndromes_detached_with_errors() -> Result<(), TestError> {
612 let rs = ReedSolomon::new(3)?;
613 let message = b"Detached".to_buffer()?;
614 let parity = rs.generate_parity(&message)?;
615 let mut corrupted = message.clone()?;
616 corrupted[2] ^= 2;
617 let syndromes = ReedSolomon::compute_syndromes_detached(&parity, &corrupted)?;
618 assert!(syndromes.iter().any(|&s| s != 0));
619 Ok(())
620 }
621
622 #[test]
623 fn test_validate_no_errors() -> Result<(), TestError> {
624 let rs = ReedSolomon::new(1)?;
625 let message = b"Valid".to_buffer()?;
626 let encoded = rs.encode(&message)?;
627 assert!(rs.validate(&encoded)?.is_none());
628 Ok(())
629 }
630
631 #[test]
632 fn test_validate_with_errors() -> Result<(), TestError> {
633 let rs = ReedSolomon::new(1)?;
634 let message = b"Valid".to_buffer()?;
635 let encoded = rs.encode(&message)?;
636 let mut corrupted = encoded.clone()?;
637 corrupted[0] ^= 4;
638 assert!(rs.validate(&corrupted)?.is_some());
639 Ok(())
640 }
641
642 #[test]
643 fn test_validate_detached_no_errors_case_2() -> Result<(), TestError> {
644 let rs = ReedSolomon::new(2)?;
645 let message = b"Detached2".to_buffer()?;
646 let parity = rs.generate_parity(&message)?;
647 assert!(ReedSolomon::validate_detached(&parity, &message)?.is_none());
648 Ok(())
649 }
650
651 #[test]
652 fn test_validate_detached_with_errors_case_2() -> Result<(), TestError> {
653 let rs = ReedSolomon::new(2)?;
654 let message = b"Detached2".to_buffer()?;
655 let parity = rs.generate_parity(&message)?;
656 let mut corrupted = message.clone()?;
657 corrupted[1] ^= 8;
658 assert!(ReedSolomon::validate_detached(&parity, &corrupted)?.is_some());
659 Ok(())
660 }
661
662 #[test]
663 fn test_correct_in_place_no_errors() -> Result<(), TestError> {
664 let rs = ReedSolomon::new(2)?;
665 let message = b"InPlace".to_buffer()?;
666 let mut encoded = rs.encode(&message)?;
667 rs.correct_in_place(&mut encoded)?;
668 assert_eq!(encoded.slice(4..), message.as_slice());
669 Ok(())
670 }
671
672 #[test]
673 fn test_correct_in_place_one_error() -> Result<(), TestError> {
674 let rs = ReedSolomon::new(3)?;
675 let message = b"InPlace1".to_buffer()?;
676 let mut encoded = rs.encode(&message)?;
677 encoded[4] ^= 16;
678 rs.correct_in_place(&mut encoded)?;
679 assert_eq!(encoded.slice(6..), message.as_slice());
680 Ok(())
681 }
682
683 #[test]
684 fn test_correct_in_place_too_many_errors() -> Result<(), TestError> {
685 let rs = ReedSolomon::new(1)?;
686 let message = b"TooMany".to_buffer()?;
687 let mut encoded = rs.encode(&message)?;
688 encoded[0] ^= 1;
689 encoded[1] ^= 2;
690 assert_eq!(
691 rs.correct_in_place(&mut encoded),
692 Err(RSDecodeError::RSComputeErrorsError(
693 RSComputeErrorsError::TooManyErrors
694 ))
695 );
696 Ok(())
697 }
698
699 #[test]
700 fn test_decode_no_errors() -> Result<(), TestError> {
701 let rs = ReedSolomon::new(2)?;
702 let message = b"DecodeOk".to_buffer()?;
703 let encoded = rs.encode(&message)?;
704 let decoded = rs.decode(&encoded)?;
705 assert_eq!(&decoded[..], &message[..]);
706 Ok(())
707 }
708
709 #[test]
710 fn test_decode_one_error() -> Result<(), TestError> {
711 let rs = ReedSolomon::new(3)?;
712 let message = b"DecodeErr".to_buffer()?;
713 let encoded = rs.encode(&message)?;
714 let mut corrupted = encoded.clone()?;
715 corrupted[5] ^= 32;
716 let decoded = rs.decode(&corrupted)?;
717 assert_eq!(&decoded[..], &message[..]);
718 Ok(())
719 }
720
721 #[test]
722 fn test_decode_too_many_errors() -> Result<(), TestError> {
723 let rs = ReedSolomon::new(1)?;
724 let message = b"DecodeMany".to_buffer()?;
725 let encoded = rs.encode(&message)?;
726 let mut corrupted = encoded.clone()?;
727 corrupted[0] ^= 1;
728 corrupted[2] ^= 2;
729 assert_eq!(
730 rs.decode(&corrupted),
731 Err(RSDecodeError::RSComputeErrorsError(
732 RSComputeErrorsError::TooManyErrors
733 ))
734 );
735 Ok(())
736 }
737
738 #[test]
739 fn test_correct_detached_no_errors() -> Result<(), TestError> {
740 let rs = ReedSolomon::new(2)?;
741 let message = b"DetachOk".to_buffer()?;
742 let parity = rs.generate_parity(&message)?;
743 let corrected = ReedSolomon::correct_detached(&parity, &message)?;
744 assert_eq!(&corrected[..], &message[..]);
745 Ok(())
746 }
747
748 #[test]
749 fn test_correct_detached_one_error() -> Result<(), TestError> {
750 let rs = ReedSolomon::new(3)?;
751 let message = b"DetachErr".to_buffer()?;
752 let parity = rs.generate_parity(&message)?;
753 let mut corrupted = message.clone()?;
754 corrupted[1] ^= 64;
755 let corrected = ReedSolomon::correct_detached(&parity, &corrupted)?;
756 assert_eq!(&corrected[..], &message[..]);
757 Ok(())
758 }
759
760 #[test]
761 fn test_correct_detached_too_many_errors() -> Result<(), TestError> {
762 let rs = ReedSolomon::new(1)?;
763 let message = b"DetachMany".to_buffer()?;
764 let parity = rs.generate_parity(&message)?;
765 let mut corrupted = message.clone()?;
766 corrupted[0] ^= 1;
767 corrupted[2] ^= 2;
768 assert_eq!(
769 ReedSolomon::correct_detached(&parity, &corrupted),
770 Err(RSDecodeError::RSComputeErrorsError(
771 RSComputeErrorsError::TooManyErrors
772 ))
773 );
774 Ok(())
775 }
776
777 #[test]
778 fn test_correct_detached_data_in_place_no_errors() -> Result<(), TestError> {
779 let rs = ReedSolomon::new(2)?;
780 let message = b"DataInPlaceOk".to_buffer()?;
781 let parity = rs.generate_parity(&message)?;
782 let mut data = message.clone()?;
783 ReedSolomon::correct_detached_data_in_place(&parity, &mut data)?;
784 assert_eq!(data.as_slice(), message.as_slice());
785 Ok(())
786 }
787
788 #[test]
789 fn test_correct_detached_data_in_place_one_error() -> Result<(), TestError> {
790 let rs = ReedSolomon::new(3)?;
791 let message = b"DataInPlaceErr".to_buffer()?;
792 let parity = rs.generate_parity(&message)?;
793 let mut data = message.clone()?;
794 data[3] ^= 128;
795 ReedSolomon::correct_detached_data_in_place(&parity, &mut data)?;
796 assert_eq!(data.as_slice(), message.as_slice());
797 Ok(())
798 }
799
800 #[test]
801 fn test_correct_detached_data_in_place_too_many_errors() -> Result<(), TestError> {
802 let rs = ReedSolomon::new(1)?;
803 let message = b"DataInPlaceMany".to_buffer()?;
804 let parity = rs.generate_parity(&message)?;
805 let mut data = message.clone()?;
806 data[0] ^= 1;
807 data[2] ^= 2;
808 assert_eq!(
809 ReedSolomon::correct_detached_data_in_place(&parity, &mut data),
810 Err(RSDecodeError::RSComputeErrorsError(
811 RSComputeErrorsError::TooManyErrors
812 ))
813 );
814 Ok(())
815 }
816
817 #[test]
818 fn test_apply_corrections() -> Result<(), TestError> {
819 let mut target = Buffer::from_slice([1, 2, 3, 4])?;
820 let corrections = [0, 3, 0, 5];
821 ReedSolomon::apply_corrections(&mut target, corrections);
822 assert_eq!(target.as_slice(), &[1, 2 ^ 3, 3, 4 ^ 5]);
823 Ok(())
824 }
825
826 #[test]
827 fn test_apply_corrections_detached() -> Result<(), TestError> {
828 let mut parity = Buffer::from_slice([10, 20])?;
829 let mut data = Buffer::from_slice([30, 40, 50])?;
830 let corrections = [1, 2, 3, 4, 5];
831 ReedSolomon::apply_corrections_detached(&mut parity, &mut data, corrections);
832 assert_eq!(parity.as_slice(), &[10 ^ 1, 20 ^ 2]);
833 assert_eq!(data.as_slice(), &[30 ^ 3, 40 ^ 4, 50 ^ 5]);
834 Ok(())
835 }
836
837 #[test]
838 fn test_correct_both_detached_in_place_with_parity_error() -> Result<(), TestError> {
839 let rs = ReedSolomon::new(4)?;
840 let message = b"HelloAgain!".to_buffer()?;
841 let mut data = Buffer::with_capacity(message.len())?;
842 data.extend_from_slice(&message)?;
843 let mut parity = rs.generate_parity(&message)?;
844
845 parity[1] ^= 8;
846
847 ReedSolomon::correct_detached_in_place(&mut parity, &mut data)?;
848
849 assert_eq!(data.as_slice(), message.as_slice());
850 assert_eq!(parity, rs.generate_parity(&message)?);
851
852 Ok(())
853 }
854
855 #[test]
856 fn test_correct_both_detached_in_place_with_both_errors() -> Result<(), TestError> {
857 let rs = ReedSolomon::new(4)?;
858 let message = b"BothErrors".to_buffer()?;
859 let mut data = Buffer::with_capacity(message.len())?;
860 data.extend_from_slice(&message)?;
861 let mut parity = rs.generate_parity(&message)?;
862
863 data[3] ^= 16;
864 parity[0] ^= 4;
865
866 ReedSolomon::correct_detached_in_place(&mut parity, &mut data)?;
867
868 assert_eq!(data.as_slice(), message.as_slice());
869 assert_eq!(parity, rs.generate_parity(&message)?);
870
871 Ok(())
872 }
873
874 #[test]
875 fn test_correct_both_detached_in_place_too_many_errors() -> Result<(), TestError> {
876 let rs = ReedSolomon::new(2)?;
877 let message = b"TooManyBoth".to_buffer()?;
878 let mut data = Buffer::with_capacity(message.len())?;
879 data.extend_from_slice(&message)?;
880 let mut parity = rs.generate_parity(&message)?;
881
882 data[0] ^= 1;
883 data[2] ^= 2;
884 parity[1] ^= 4;
885 parity[3] ^= 8;
886
887 assert_eq!(
888 ReedSolomon::correct_detached_in_place(&mut parity, &mut data),
889 Err(RSDecodeError::RSComputeErrorsError(
890 RSComputeErrorsError::TooManyErrors
891 ))
892 );
893
894 Ok(())
895 }
896
897 #[test]
898 fn test_generate_parity_large_message() -> Result<(), TestError> {
899 let rs = ReedSolomon::new(8)?;
900 let message = vec![42; 200].to_buffer()?;
901 let parity = rs.generate_parity(&message)?;
902 assert_eq!(parity.len(), 16); let encoded = rs.encode(&message)?;
904 assert_eq!(&encoded[..16], parity.as_slice());
905 Ok(())
906 }
907
908 #[test]
909 fn test_encode_empty_message() -> Result<(), TestError> {
910 let rs = ReedSolomon::new(2)?;
911 let message = b"".to_buffer()?;
912 let encoded = rs.encode(&message)?;
913 assert_eq!(encoded.len(), 4); assert!(rs.validate(&encoded)?.is_none());
915 Ok(())
916 }
917
918 #[test]
919 fn test_correct_in_place_multiple_errors_at_boundaries() -> Result<(), TestError> {
920 let rs = ReedSolomon::new(4)?;
921 let message = b"Boundary".to_buffer()?;
922 let mut encoded = rs.encode(&message)?;
923 let len = encoded.len();
924 encoded[0] ^= 1; encoded[len - 1] ^= 2; rs.correct_in_place(&mut encoded)?;
927 assert_eq!(encoded.slice(8..), message.as_slice());
928 Ok(())
929 }
930
931 #[test]
932 fn test_decode_with_errors_in_parity_only() -> Result<(), TestError> {
933 let rs = ReedSolomon::new(3)?;
934 let message = b"ParityErr".to_buffer()?;
935 let encoded = rs.encode(&message)?;
936 let mut corrupted = encoded.clone()?;
937 corrupted[1] ^= 4; corrupted[3] ^= 8; let decoded = rs.decode(&corrupted)?;
940 assert_eq!(&decoded[..], &message[..]);
941 Ok(())
942 }
943
944 #[test]
945 fn test_correct_detached_data_in_place_multiple_errors() -> Result<(), TestError> {
946 let rs = ReedSolomon::new(4)?;
947 let message = b"MultiErr".to_buffer()?;
948 let parity = rs.generate_parity(&message)?;
949 let mut data = message.clone()?;
950 data[0] ^= 1;
951 data[4] ^= 16;
952 ReedSolomon::correct_detached_data_in_place(&parity, &mut data)?;
953 assert_eq!(data.as_slice(), message.as_slice());
954 Ok(())
955 }
956
957 #[test]
958 fn test_compute_syndromes_empty_codeword() -> Result<(), TestError> {
959 let syndromes = ReedSolomon::compute_syndromes(4u8, &[])?;
960 assert_eq!(syndromes.len(), 4);
961 assert!(syndromes.iter().all(|&s| s == 0));
962 Ok(())
963 }
964
965 #[test]
966 fn test_compute_syndromes_detached_empty_data() -> Result<(), TestError> {
967 let rs = ReedSolomon::new(2)?;
968 let parity = rs.generate_parity(&[])?;
969 let syndromes = ReedSolomon::compute_syndromes_detached(&parity, &[])?;
970 assert_eq!(syndromes.len(), 4);
971 assert!(syndromes.iter().all(|&s| s == 0));
972 Ok(())
973 }
974
975 #[test]
976 fn test_correct_maximum_correctable_errors() -> Result<(), TestError> {
977 let rs = ReedSolomon::new(4)?;
978 let message = b"MaxErrors".to_buffer()?;
979 let encoded = rs.encode(&message)?;
980 let mut corrupted = encoded.clone()?;
981 corrupted[0] ^= 1;
982 corrupted[2] ^= 2;
983 corrupted[4] ^= 4;
984 corrupted[6] ^= 8;
985 let decoded = rs.decode(&corrupted)?;
986 assert_eq!(&decoded[..], &message[..]);
987 Ok(())
988 }
989
990 #[test]
991 fn test_correct_detached_with_errors_in_parity_only() -> Result<(), TestError> {
992 let rs = ReedSolomon::new(3)?;
993 let message = b"ParityOnly".to_buffer()?;
994 let mut parity = rs.generate_parity(&message)?;
995 parity[0] ^= 2;
996 parity[2] ^= 4;
997 let corrected = ReedSolomon::correct_detached(&parity, &message)?;
998 assert_eq!(&corrected[..], &message[..]);
999 Ok(())
1000 }
1001
1002 #[test]
1003 fn test_apply_corrections_empty_target() -> Result<(), TestError> {
1004 let mut target = Buffer::from_slice([])?;
1005 let corrections = [];
1006 ReedSolomon::apply_corrections(&mut target, corrections);
1007 assert_eq!(target.as_slice(), &[]);
1008 Ok(())
1009 }
1010
1011 #[test]
1012 fn test_apply_corrections_detached_empty() -> Result<(), TestError> {
1013 let mut parity = Buffer::from_slice([])?;
1014 let mut data = Buffer::from_slice([])?;
1015 let corrections = [];
1016 ReedSolomon::apply_corrections_detached(&mut parity, &mut data, corrections);
1017 assert_eq!(parity.as_slice(), &[]);
1018 assert_eq!(data.as_slice(), &[]);
1019 Ok(())
1020 }
1021
1022 #[test]
1023 fn test_validate_large_parity() -> Result<(), TestError> {
1024 let rs = ReedSolomon::new(16)?;
1025 let message = b"LargeParity".to_buffer()?;
1026 let encoded = rs.encode(&message)?;
1027 assert!(rs.validate(&encoded)?.is_none());
1028 let mut corrupted = encoded.clone()?;
1029 corrupted[10] ^= 1;
1030 assert!(rs.validate(&corrupted)?.is_some());
1031 Ok(())
1032 }
1033
1034 #[test]
1035 fn test_correct_in_place_all_zeros() -> Result<(), TestError> {
1036 let rs = ReedSolomon::new(2)?;
1037 let message = vec![0; 10].to_buffer()?;
1038 let mut encoded = rs.encode(&message)?;
1039 encoded[2] ^= 1;
1040 rs.correct_in_place(&mut encoded)?;
1041 assert_eq!(encoded.slice(4..), message.as_slice());
1042 Ok(())
1043 }
1044}