Skip to main content

ps_ecc/reed_solomon/methods/
correct.rs

1use ps_buffer::Buffer;
2
3use crate::cow::Cow;
4use crate::{RSDecodeError, ReedSolomon};
5
6impl ReedSolomon {
7    /// Corrects a received codeword, returning the corrected codeword.
8    /// # Errors
9    /// - [`RSDecodeError::InsufficientLength`] is returned if `received` holds
10    ///   fewer bytes than [`ReedSolomon::parity_bytes`].
11    /// - [`std::num::TryFromIntError`] is returned if `received` holds more
12    ///   than 255 bytes.
13    /// - [`ps_buffer::BufferError`] is returned if memory allocation fails.
14    /// - [`RSComputeErrorsError`](crate::RSComputeErrorsError) is propagated
15    ///   from [`ReedSolomon::compute_errors`].
16    /// - [`RSDecodeError::TooManyErrors`] is returned if the corrected bytes
17    ///   fail validation.
18    pub fn correct<'lt>(&self, received: &'lt [u8]) -> Result<Cow<'lt>, RSDecodeError> {
19        let parity_bytes = self.parity_bytes();
20
21        if received.len() < usize::from(parity_bytes) {
22            return Err(RSDecodeError::InsufficientLength {
23                parity_bytes,
24                received: received.len(),
25            });
26        }
27
28        let received_len = u8::try_from(received.len())?;
29        let syndromes = Self::compute_syndromes(parity_bytes, received);
30
31        let Some(errors) = self.compute_errors(received_len, &syndromes)? else {
32            return Ok(Cow::Borrowed(received));
33        };
34
35        // Correct the received codeword
36        let mut corrected = Buffer::from_slice(received)?;
37
38        Self::apply_corrections(
39            &mut corrected,
40            errors.first_n_coefficients(received_len.into()),
41        );
42
43        match self.validate(&corrected) {
44            None => Ok(corrected.into()),
45            Some(_) => Err(RSDecodeError::TooManyErrors),
46        }
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use crate::{RSDecodeError, ReedSolomon};
53
54    type TestError = Box<dyn std::error::Error>;
55
56    #[test]
57    fn test_correct_rejects_input_shorter_than_parity() -> Result<(), TestError> {
58        // An all-zero truncated slice yields zero syndromes; without the
59        // length check it was returned unchanged as a pristine codeword.
60        let rs = ReedSolomon::new(4)?;
61
62        for received in [&[][..], &[0u8; 7][..]] {
63            assert_eq!(
64                rs.correct(received),
65                Err(RSDecodeError::InsufficientLength {
66                    parity_bytes: 8,
67                    received: received.len(),
68                })
69            );
70        }
71
72        Ok(())
73    }
74
75    #[test]
76    fn test_correct_accepts_parity_only_codeword() -> Result<(), TestError> {
77        let rs = ReedSolomon::new(4)?;
78        let encoded = rs.encode(&[])?;
79        let corrected = rs.correct(&encoded)?;
80
81        assert_eq!(&corrected[..], &encoded[..]);
82
83        Ok(())
84    }
85}