ps_ecc/reed_solomon/methods/
correct_in_place.rs1use crate::{RSDecodeError, ReedSolomon};
2
3impl ReedSolomon {
4 pub fn correct_in_place(&self, received: &mut [u8]) -> Result<(), RSDecodeError> {
14 let parity_bytes = self.parity_bytes();
15
16 if received.len() < usize::from(parity_bytes) {
17 return Err(RSDecodeError::InsufficientLength {
18 parity_bytes,
19 received: received.len(),
20 });
21 }
22
23 let received_len = u8::try_from(received.len())?;
24 let syndromes = Self::compute_syndromes(parity_bytes, received);
25
26 let Some(errors) = self.compute_errors(received_len, &syndromes)? else {
27 return Ok(());
28 };
29
30 Self::apply_corrections(received, errors.first_n_coefficients(received_len.into()));
31
32 match self.validate(received) {
33 None => Ok(()),
34 Some(_) => Err(RSDecodeError::TooManyErrors),
35 }
36 }
37}
38
39#[cfg(test)]
40mod tests {
41 use ps_buffer::ToBuffer;
42
43 use crate::{RSComputeErrorsError, RSDecodeError, ReedSolomon};
44
45 type TestError = Box<dyn std::error::Error>;
46
47 #[test]
48 fn test_correct_in_place_no_errors() -> Result<(), TestError> {
49 let rs = ReedSolomon::new(2)?;
50 let message = b"InPlace".to_buffer()?;
51 let mut encoded = rs.encode(&message)?;
52
53 rs.correct_in_place(&mut encoded)?;
54
55 assert_eq!(encoded.slice(4..), message.as_slice());
56
57 Ok(())
58 }
59
60 #[test]
61 fn test_correct_in_place_one_error() -> Result<(), TestError> {
62 let rs = ReedSolomon::new(3)?;
63 let message = b"InPlace1".to_buffer()?;
64 let mut encoded = rs.encode(&message)?;
65
66 encoded[4] ^= 16;
67
68 rs.correct_in_place(&mut encoded)?;
69
70 assert_eq!(encoded.slice(6..), message.as_slice());
71
72 Ok(())
73 }
74
75 #[test]
76 fn test_correct_in_place_too_many_errors() -> Result<(), TestError> {
77 let rs = ReedSolomon::new(1)?;
78 let message = b"TooMany".to_buffer()?;
79 let mut encoded = rs.encode(&message)?;
80
81 encoded[0] ^= 1;
82 encoded[1] ^= 2;
83
84 assert_eq!(
85 rs.correct_in_place(&mut encoded),
86 Err(RSDecodeError::RSComputeErrorsError(
87 RSComputeErrorsError::TooManyErrors
88 ))
89 );
90
91 Ok(())
92 }
93
94 #[test]
95 fn test_correct_in_place_rejects_input_shorter_than_parity() -> Result<(), TestError> {
96 let rs = ReedSolomon::new(4)?;
99
100 let mut received = [0u8; 7];
101
102 assert_eq!(
103 rs.correct_in_place(&mut received),
104 Err(RSDecodeError::InsufficientLength {
105 parity_bytes: 8,
106 received: 7,
107 })
108 );
109
110 Ok(())
111 }
112
113 #[test]
114 fn test_correct_in_place_accepts_parity_only_codeword() -> Result<(), TestError> {
115 let rs = ReedSolomon::new(4)?;
118
119 let mut received = [0u8; 8];
120
121 rs.correct_in_place(&mut received)?;
122
123 assert_eq!(received, [0u8; 8]);
124
125 Ok(())
126 }
127
128 #[test]
129 fn test_correct_in_place_multiple_errors_at_boundaries() -> Result<(), TestError> {
130 let rs = ReedSolomon::new(4)?;
131 let message = b"Boundary".to_buffer()?;
132 let mut encoded = rs.encode(&message)?;
133 let len = encoded.len();
134
135 encoded[0] ^= 1; encoded[len - 1] ^= 2; rs.correct_in_place(&mut encoded)?;
139
140 assert_eq!(encoded.slice(8..), message.as_slice());
141
142 Ok(())
143 }
144
145 #[test]
151 fn test_correct_in_place_with_trailing_zero_syndrome() -> Result<(), TestError> {
152 let rs = ReedSolomon::new(2)?;
153 let message = b"TestMessage!".to_buffer()?;
154 let encoded = rs.encode(&message)?;
155
156 'search: for pos1 in 0..encoded.len() {
160 for pos2 in (pos1 + 1)..encoded.len() {
161 for xor1 in 1u8..=255 {
162 for xor2 in 1u8..=255 {
163 let mut corrupted = encoded.clone()?;
164
165 corrupted[pos1] ^= xor1;
166 corrupted[pos2] ^= xor2;
167
168 let syndromes =
169 ReedSolomon::compute_syndromes(rs.parity_bytes(), &corrupted);
170 let coeffs = syndromes.first_n_coefficients(rs.parity_bytes().into());
171
172 if coeffs.last() == Some(&0) && coeffs.iter().any(|&s| s != 0) {
174 let mut to_correct = corrupted.clone()?;
177
178 rs.correct_in_place(&mut to_correct)?;
179 assert_eq!(to_correct.as_slice(), encoded.as_slice());
180 break 'search;
181 }
182 }
183 }
184 }
185 }
186
187 Ok(())
188 }
189
190 #[test]
195 fn test_correct_in_place_trailing_zero_syndrome_trimmed() -> Result<(), TestError> {
196 let rs = ReedSolomon::new(2)?;
197 let message = b"TestMessage!".to_buffer()?;
198 let encoded = rs.encode(&message)?;
199
200 for pos1 in 0..encoded.len() {
202 for pos2 in (pos1 + 1)..encoded.len() {
203 for xor1 in 1u8..=255 {
204 for xor2 in 1u8..=255 {
205 let mut corrupted = encoded.clone()?;
206
207 corrupted[pos1] ^= xor1;
208 corrupted[pos2] ^= xor2;
209
210 let syndromes =
211 ReedSolomon::compute_syndromes(rs.parity_bytes(), &corrupted);
212 let full = syndromes.first_n_coefficients(rs.parity_bytes().into());
213
214 if full.last() == Some(&0) && full.iter().any(|&s| s != 0) {
215 assert!(syndromes.coefficients().len() < rs.parity_bytes().into());
218
219 let mut to_correct = corrupted.clone()?;
221
222 rs.correct_in_place(&mut to_correct)?;
223 assert_eq!(to_correct.as_slice(), encoded.as_slice());
224
225 return Ok(());
226 }
227 }
228 }
229 }
230 }
231
232 panic!("No trailing zero syndrome pattern found");
233 }
234
235 #[test]
236 fn test_correct_in_place_all_zeros() -> Result<(), TestError> {
237 let rs = ReedSolomon::new(2)?;
238 let message = vec![0; 10].to_buffer()?;
239 let mut encoded = rs.encode(&message)?;
240
241 encoded[2] ^= 1;
242
243 rs.correct_in_place(&mut encoded)?;
244
245 assert_eq!(encoded.slice(4..), message.as_slice());
246
247 Ok(())
248 }
249}