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