ps_ecc/reed_solomon/methods/
correct_detached.rs1use ps_buffer::Buffer;
2
3use crate::cow::Cow;
4use crate::{Codeword, RSDecodeError, ReedSolomon};
5
6impl ReedSolomon {
7 pub fn correct_detached<'lt>(
20 parity: &[u8],
21 data: &'lt [u8],
22 ) -> Result<Codeword<'lt>, RSDecodeError> {
23 Self::check_detached_parity(parity)?;
27
28 let parity_bytes = parity.len();
29 let num_parity = u8::try_from(parity_bytes >> 1)?;
30 let length = u8::try_from(parity_bytes + data.len())?;
31 let rs = Self::new(num_parity)?;
32
33 let syndromes = Self::compute_syndromes_detached(parity, data)?;
34
35 let Some(errors) = Self::compute_errors_detached(num_parity, length, &syndromes)? else {
36 return Ok(data.into());
37 };
38
39 let mut corrected = Buffer::with_capacity(parity.len() + data.len())?;
41
42 corrected.extend_from_slice(parity)?;
43 corrected.extend_from_slice(data)?;
44
45 Self::apply_corrections(&mut corrected, errors.first_n_coefficients(length.into()));
46
47 if rs.validate(&corrected).is_some() {
48 return Err(RSDecodeError::TooManyErrors);
49 }
50
51 let range = parity.len()..corrected.len();
52 let codeword = Cow::Owned(corrected.share());
53 let codeword = Codeword { codeword, range };
54
55 Ok(codeword)
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use ps_buffer::{Buffer, ToBuffer};
62
63 use crate::{RSComputeErrorsError, RSConstructorError, RSDecodeError, ReedSolomon};
64
65 type TestError = Box<dyn std::error::Error>;
66
67 #[test]
68 fn test_correct_detached_no_errors() -> Result<(), TestError> {
69 let rs = ReedSolomon::new(2)?;
70 let message = b"DetachOk".to_buffer()?;
71 let parity = rs.generate_parity(&message)?;
72
73 let corrected = ReedSolomon::correct_detached(&parity, &message)?;
74
75 assert_eq!(&corrected[..], &message[..]);
76
77 Ok(())
78 }
79
80 #[test]
81 fn test_correct_detached_one_error() -> Result<(), TestError> {
82 let rs = ReedSolomon::new(3)?;
83 let message = b"DetachErr".to_buffer()?;
84 let parity = rs.generate_parity(&message)?;
85
86 let mut corrupted = message.clone()?;
87
88 corrupted[1] ^= 64;
89
90 let corrected = ReedSolomon::correct_detached(&parity, &corrupted)?;
91
92 assert_eq!(&corrected[..], &message[..]);
93
94 Ok(())
95 }
96
97 #[test]
98 fn test_correct_detached_too_many_errors() -> Result<(), TestError> {
99 let rs = ReedSolomon::new(1)?;
100 let message = b"DetachMany".to_buffer()?;
101 let parity = rs.generate_parity(&message)?;
102
103 let mut corrupted = message.clone()?;
104
105 corrupted[0] ^= 1;
106 corrupted[2] ^= 2;
107
108 assert_eq!(
109 ReedSolomon::correct_detached(&parity, &corrupted),
110 Err(RSDecodeError::RSComputeErrorsError(
111 RSComputeErrorsError::TooManyErrors
112 ))
113 );
114
115 Ok(())
116 }
117
118 #[test]
119 fn test_correct_detached_rejects_oversized_parity() {
120 let parity = [0u8; 127];
124
125 assert_eq!(
126 ReedSolomon::correct_detached(&parity, &[]),
127 Err(RSDecodeError::RSConstructorError(
128 RSConstructorError::ParityTooHigh
129 ))
130 );
131 }
132
133 #[test]
134 fn test_correct_detached_parity_checks_precede_length_conversion() {
135 let parity = [0u8; 1];
138 let data = [0u8; 255];
139
140 assert_eq!(
141 ReedSolomon::correct_detached(&parity, &data),
142 Err(RSDecodeError::RSConstructorError(
143 RSConstructorError::OddParityLength(1)
144 ))
145 );
146 }
147
148 #[test]
149 fn test_correct_detached_rejects_odd_parity_length() {
150 let parity = [0u8; 5];
153
154 assert_eq!(
155 ReedSolomon::correct_detached(&parity, b"data"),
156 Err(RSDecodeError::RSConstructorError(
157 RSConstructorError::OddParityLength(5)
158 ))
159 );
160 }
161
162 #[test]
163 fn test_correct_detached_with_errors_in_parity_only() -> Result<(), TestError> {
164 let rs = ReedSolomon::new(3)?;
165 let message = b"ParityOnly".to_buffer()?;
166 let parity_poly = rs.generate_parity(&message)?;
167 let mut parity = Buffer::from_slice(parity_poly)?;
168
169 parity[0] ^= 2;
170 parity[2] ^= 4;
171
172 let corrected = ReedSolomon::correct_detached(&parity, &message)?;
173
174 assert_eq!(&corrected[..], &message[..]);
175
176 Ok(())
177 }
178}