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