Skip to main content

ps_ecc/reed_solomon/methods/
compute_syndromes_detached.rs

1use crate::finite_field::ANTILOG_TABLE;
2use crate::{Polynomial, RSConstructorError, ReedSolomon};
3
4impl ReedSolomon {
5    /// Computes the syndromes of a given detached codeword.
6    ///
7    /// An empty `parity` slice yields zero syndromes, so a pair carrying no
8    /// parity always validates as pristine.
9    /// # Errors
10    /// - [`RSConstructorError::ParityTooHigh`] is returned if `parity` holds
11    ///   more than [`MAX_PARITY_BYTES`](crate::MAX_PARITY_BYTES) bytes.
12    /// - [`RSConstructorError::OddParityLength`] is returned if `parity`
13    ///   holds an odd number of bytes; parity always comprises two bytes
14    ///   per correctable error.
15    pub fn compute_syndromes_detached(
16        parity: &[u8],
17        data: &[u8],
18    ) -> Result<Polynomial, RSConstructorError> {
19        Self::check_detached_parity(parity)?;
20
21        let syndromes = (0..parity.len())
22            .map(|i| {
23                Polynomial::eval_coefficient_slices_at(&[parity, data], ANTILOG_TABLE[i + 1].get())
24            })
25            .collect();
26
27        Ok(syndromes)
28    }
29}
30
31#[cfg(test)]
32mod tests {
33    use ps_buffer::ToBuffer;
34
35    use crate::{RSConstructorError, ReedSolomon, MAX_PARITY_BYTES};
36
37    type TestError = Box<dyn std::error::Error>;
38
39    #[test]
40    fn test_compute_syndromes_detached_no_errors() -> Result<(), TestError> {
41        let rs = ReedSolomon::new(3)?;
42        let message = b"Detached".to_buffer()?;
43        let parity = rs.generate_parity(&message)?;
44
45        let syndromes = ReedSolomon::compute_syndromes_detached(&parity, &message)?;
46
47        assert!(syndromes.is_zero());
48
49        Ok(())
50    }
51
52    #[test]
53    fn test_compute_syndromes_detached_with_errors() -> Result<(), TestError> {
54        let rs = ReedSolomon::new(3)?;
55        let message = b"Detached".to_buffer()?;
56        let parity = rs.generate_parity(&message)?;
57
58        let mut corrupted = message.clone()?;
59
60        corrupted[2] ^= 2;
61
62        let syndromes = ReedSolomon::compute_syndromes_detached(&parity, &corrupted)?;
63
64        assert!(syndromes.iter().any(|&s| s != 0));
65
66        Ok(())
67    }
68
69    #[test]
70    fn test_compute_syndromes_detached_empty_data() -> Result<(), TestError> {
71        let rs = ReedSolomon::new(2)?;
72        let parity = rs.generate_parity(&[])?;
73
74        let syndromes = ReedSolomon::compute_syndromes_detached(&parity, &[])?;
75
76        assert_eq!(syndromes.degree(), 0);
77        assert!(syndromes.is_zero());
78
79        Ok(())
80    }
81
82    #[test]
83    fn test_compute_syndromes_detached_max_parity_bytes() -> Result<(), TestError> {
84        let parity = [0u8; MAX_PARITY_BYTES as usize];
85
86        let syndromes = ReedSolomon::compute_syndromes_detached(&parity, &[])?;
87
88        assert!(syndromes.is_zero());
89
90        Ok(())
91    }
92
93    #[test]
94    fn test_compute_syndromes_detached_rejects_odd_parity_length() {
95        // num_parity = parity.len() >> 1 silently dropped the odd byte
96        // while the syndromes covered the full slice; odd lengths are now
97        // rejected because generate_parity always produces two parity
98        // bytes per correctable error.
99        for parity_len in [1usize, 3, 125] {
100            let parity = vec![0u8; parity_len];
101
102            assert_eq!(
103                ReedSolomon::compute_syndromes_detached(&parity, &[]),
104                Err(RSConstructorError::OddParityLength(parity_len))
105            );
106        }
107    }
108
109    #[test]
110    fn test_compute_syndromes_detached_oversized_wins_over_odd() {
111        // 127 is both oversized and odd; the length bound is checked first.
112        let parity = [0u8; 127];
113
114        assert_eq!(
115            ReedSolomon::compute_syndromes_detached(&parity, &[]),
116            Err(RSConstructorError::ParityTooHigh)
117        );
118    }
119
120    #[test]
121    fn test_compute_syndromes_detached_rejects_oversized_parity() {
122        // Without the length bound, an oversized parity slice was silently
123        // accepted; collection into Polynomial consumes at most 255
124        // coefficients, so the syndromes were silently truncated.
125        for parity_len in [127usize, 256, 300] {
126            let parity = vec![0u8; parity_len];
127
128            assert_eq!(
129                ReedSolomon::compute_syndromes_detached(&parity, &[]),
130                Err(RSConstructorError::ParityTooHigh)
131            );
132        }
133    }
134}