ps_ecc/methods/
validate.rs1use crate::{long, ReedSolomon};
2
3#[must_use]
13pub fn validate(received: &[u8], parity: u8) -> bool {
14 if let Ok(length) = u8::try_from(received.len()) {
15 if parity > length >> 1 {
16 return false;
17 }
18
19 let Ok(rs) = ReedSolomon::new(parity) else {
20 return false;
21 };
22
23 rs.validate(received).is_none()
24 } else {
25 matches!(long::fast_validate(received), Ok((_, true)))
26 }
27}
28
29#[cfg(test)]
30mod tests {
31 use crate::{
32 long, validate, LongEccDecodeError, LongEccEncodeError, RSEncodeError, ReedSolomon,
33 };
34
35 use ps_buffer::ToBuffer;
36
37 #[derive(thiserror::Error, Debug)]
38 enum TestError {
39 #[error(transparent)]
40 LongEccEncode(#[from] LongEccEncodeError),
41 #[error(transparent)]
42 LongEccDecode(#[from] LongEccDecodeError),
43 #[error(transparent)]
44 Buffer(#[from] ps_buffer::BufferError),
45 #[error(transparent)]
46 RSConstructorError(#[from] crate::RSConstructorError),
47 #[error(transparent)]
48 RSEncodeError(#[from] RSEncodeError),
49 }
50
51 #[test]
52 fn test_validate_short_data_valid_no_errors() -> Result<(), TestError> {
53 let data = b"test";
54 let parity = 2;
55 let rs = ReedSolomon::new(parity)?;
56 let codeword = rs.encode(data)?;
57
58 assert!(validate(&codeword, parity));
59
60 Ok(())
61 }
62
63 #[test]
64 fn test_validate_short_data_invalid_with_errors() -> Result<(), TestError> {
65 let data = b"test";
66 let parity = 2;
67 let rs = ReedSolomon::new(parity)?;
68 let mut codeword = rs.encode(data)?;
69
70 codeword[0] ^= 1;
72 codeword[1] ^= 1;
73 codeword[2] ^= 1;
74
75 assert!(!validate(&codeword, parity));
76
77 Ok(())
78 }
79
80 #[test]
81 fn test_validate_short_data_parity_too_large() {
82 let data = b"test"; let parity = 3; assert!(!validate(data, parity));
87 }
88
89 #[test]
90 fn test_validate_short_data_rs_constructor_error() {
91 let data = b"test";
92 let parity = 255; assert!(!validate(data, parity));
96 }
97
98 #[test]
99 fn test_validate_short_data_correctable_errors() -> Result<(), TestError> {
100 let data = b"test";
101 let parity = 2;
102 let rs = ReedSolomon::new(parity)?;
103 let mut codeword = rs.encode(data)?;
104
105 codeword[0] ^= 1;
107
108 assert!(!validate(&codeword, parity));
110
111 Ok(())
112 }
113
114 #[test]
115 fn test_validate_long_data_valid_no_errors() -> Result<(), TestError> {
116 let message = b"This is a longer message that will use long ECC".repeat(7);
117 let parity = 2;
118
119 let encoded = long::encode(&message, parity, long::OverlapFactor::Simple)?;
120
121 assert!(validate(&encoded, parity));
123
124 Ok(())
125 }
126
127 #[test]
128 fn test_validate_long_data_invalid_with_errors() -> Result<(), TestError> {
129 let message = b"This is a longer message that will use long ECC".to_buffer()?;
130 let parity = 2;
131
132 let mut encoded = long::encode(&message, parity, long::OverlapFactor::Simple)?;
133
134 encoded[32] ^= 1;
136 encoded[37] ^= 1;
137
138 assert!(!validate(&encoded, parity));
140
141 Ok(())
142 }
143
144 #[test]
145 fn test_validate_long_data_fast_path_valid() -> Result<(), TestError> {
146 let message = b"Fast path validation test".repeat(12);
147 let parity = 1;
148
149 let encoded = long::encode(&message, parity, long::OverlapFactor::Simple)?;
150
151 assert!(validate(&encoded, parity));
153
154 Ok(())
155 }
156
157 #[test]
158 fn test_validate_empty_data() {
159 let data = b"";
160 let parity = 0;
161
162 assert!(validate(data, parity));
164 }
165
166 #[test]
167 fn test_validate_single_byte() -> Result<(), TestError> {
168 let data = b"A";
169 let parity = 1;
170 let rs = ReedSolomon::new(parity)?;
171 let codeword = rs.encode(data)?;
172
173 assert!(validate(&codeword, parity));
174
175 Ok(())
176 }
177
178 #[test]
179 fn test_validate_large_short_data() -> Result<(), TestError> {
180 let data = b"This is exactly 32 bytes of test data!!";
181 let parity = 4;
182 let rs = ReedSolomon::new(parity)?;
183 let codeword = rs.encode(data)?;
184
185 assert!(validate(&codeword, parity));
186
187 Ok(())
188 }
189
190 #[test]
191 fn test_validate_edge_case_parity_equals_length_div_2() -> Result<(), TestError> {
192 let data = b"test"; let parity = 2; let rs = ReedSolomon::new(parity)?;
196 let codeword = rs.encode(data)?;
197
198 assert!(validate(&codeword, parity));
199
200 Ok(())
201 }
202
203 #[test]
204 fn test_validate_edge_case_parity_just_over_length_div_2() {
205 let data = b"test"; let parity = 3; assert!(!validate(data, parity));
210 }
211
212 #[test]
213 fn test_validate_long_data_with_zero_parity() -> Result<(), TestError> {
214 let message = b"Zero parity test".to_buffer()?;
215 let parity = 0;
216
217 let encoded = long::encode(&message, parity, long::OverlapFactor::Simple)?;
218
219 assert!(validate(&encoded, parity));
221
222 Ok(())
223 }
224
225 #[test]
226 fn test_validate_long_data_header_corrupted() -> Result<(), TestError> {
227 let message = b"Header corruption test".to_buffer()?;
228 let parity = 2;
229
230 let mut encoded = long::encode(&message, parity, long::OverlapFactor::Simple)?;
231
232 encoded[0] ^= 1;
234 encoded[5] ^= 1;
235
236 assert!(!validate(&encoded, parity));
238
239 Ok(())
240 }
241
242 #[test]
243 fn test_validate_short_data_length_conversion_error() {
244 let data: Vec<u8> = vec![0x42; 300]; let parity = 2;
246
247 assert!(!validate(&data, parity));
249 }
250
251 #[test]
252 fn test_validate_short_data_unrecoverable_errors() -> Result<(), TestError> {
253 let data = b"test data";
254 let parity = 1;
255 let rs = ReedSolomon::new(parity)?;
256 let mut codeword = rs.encode(data)?;
257
258 codeword[0] ^= 1;
260 codeword[1] ^= 1; assert!(!validate(&codeword, parity));
263
264 Ok(())
265 }
266
267 #[test]
268 fn test_validate_long_data_corrupted_parity() -> Result<(), TestError> {
269 let message = b"Corrupted parity test".repeat(13).to_buffer()?;
271 let parity = 2;
272
273 let mut encoded = long::encode(&message, parity, long::OverlapFactor::Simple)?;
274
275 let parity_start = 32 + message.len();
277
278 if parity_start < encoded.len() {
279 encoded[parity_start] ^= 1;
280 }
281
282 assert!(!validate(&encoded, parity));
285
286 Ok(())
287 }
288}